docs(executors): add Unsandbox and Vercel deployment guides
Restructure executor documentation to support multiple platforms: - Main /docs/executors page now serves as overview with platform selector - Add dedicated /docs/executors/unsandbox guide with CLI deployment - Add dedicated /docs/executors/vercel guide with one-click deploy - Include platform comparison table and shared API specification
This commit is contained in:
parent
ee066a20ca
commit
3f62228c56
3 changed files with 1096 additions and 109 deletions
|
|
@ -1,4 +1,3 @@
|
||||||
import { Button } from '@tpmjs/ui/Button/Button';
|
|
||||||
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
|
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
|
||||||
import { Icon } from '@tpmjs/ui/Icon/Icon';
|
import { Icon } from '@tpmjs/ui/Icon/Icon';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
@ -10,7 +9,7 @@ import { AppHeader } from '~/components/AppHeader';
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Custom Executors - TPMJS',
|
title: 'Custom Executors - TPMJS',
|
||||||
description:
|
description:
|
||||||
'Learn how to deploy and configure custom executors for running TPMJS tools on your own infrastructure.',
|
'Deploy your own executor to run TPMJS tools on your own infrastructure with full control and privacy.',
|
||||||
};
|
};
|
||||||
|
|
||||||
const executeToolExample = `// POST /execute-tool
|
const executeToolExample = `// POST /execute-tool
|
||||||
|
|
@ -46,69 +45,39 @@ export default function ExecutorsDocsPage(): React.ReactElement {
|
||||||
<div className="mb-12">
|
<div className="mb-12">
|
||||||
<h1 className="text-3xl font-bold text-foreground mb-4">Custom Executors</h1>
|
<h1 className="text-3xl font-bold text-foreground mb-4">Custom Executors</h1>
|
||||||
<p className="text-lg text-foreground-secondary">
|
<p className="text-lg text-foreground-secondary">
|
||||||
Deploy your own executor to run TPMJS tools on your own infrastructure.
|
Deploy your own executor to run TPMJS tools on your infrastructure with full control
|
||||||
|
over environment, secrets, and data.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Start Banner */}
|
{/* What is an Executor */}
|
||||||
<section className="mb-12">
|
|
||||||
<Link
|
|
||||||
href="/docs/tutorials/custom-executor"
|
|
||||||
className="block p-4 bg-gradient-to-r from-primary/10 via-primary/5 to-transparent border border-primary/30 rounded-lg hover:border-primary/50 transition-colors"
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<div className="text-3xl">🚀</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<p className="font-medium text-foreground">
|
|
||||||
New to custom executors? Start with the tutorial
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-foreground-secondary">
|
|
||||||
Deploy your own executor in 10 minutes with our step-by-step guide
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Icon icon="chevronRight" className="w-5 h-5 text-primary" />
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Overview Section */}
|
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-2xl font-semibold text-foreground mb-4">What is an Executor?</h2>
|
<h2 className="text-2xl font-semibold text-foreground mb-4">What is an Executor?</h2>
|
||||||
<p className="text-foreground-secondary mb-4">
|
<p className="text-foreground-secondary mb-4">
|
||||||
An executor is a service that runs TPMJS tools. When you use a collection or agent,
|
An executor is a service that runs TPMJS tools. When you use a collection or agent,
|
||||||
TPMJS sends tool execution requests to an executor, which dynamically loads and runs
|
TPMJS sends tool execution requests to an executor, which dynamically loads the npm
|
||||||
the tool code.
|
package and calls the tool's{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">execute()</code> function.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-foreground-secondary mb-6">
|
<p className="text-foreground-secondary">
|
||||||
By default, TPMJS uses a shared executor. You can deploy your own for:
|
By default, TPMJS uses a shared executor. Deploying your own gives you complete
|
||||||
|
control over the execution environment.
|
||||||
</p>
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Benefits Grid */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">
|
||||||
|
Why Deploy Your Own Executor?
|
||||||
|
</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div className="p-4 bg-surface border border-border rounded-lg">
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Icon icon="folder" className="w-5 h-5 text-primary" />
|
<Icon icon="key" className="w-5 h-5 text-primary" />
|
||||||
<h3 className="font-medium text-foreground">Full Control</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-foreground-secondary">
|
|
||||||
Run tools on your own infrastructure with complete control over the execution
|
|
||||||
environment.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 bg-surface border border-border rounded-lg">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<Icon icon="globe" className="w-5 h-5 text-primary" />
|
|
||||||
<h3 className="font-medium text-foreground">Privacy</h3>
|
<h3 className="font-medium text-foreground">Privacy</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-foreground-secondary">
|
<p className="text-sm text-foreground-secondary">
|
||||||
Keep tool execution data on your own servers. No data leaves your infrastructure.
|
Keep tool execution data on your own servers. No data passes through TPMJS.
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-4 bg-surface border border-border rounded-lg">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<Icon icon="clock" className="w-5 h-5 text-primary" />
|
|
||||||
<h3 className="font-medium text-foreground">Performance</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-foreground-secondary">
|
|
||||||
Deploy in regions closest to your users for lower latency tool execution.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 bg-surface border border-border rounded-lg">
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
|
@ -117,68 +86,206 @@ export default function ExecutorsDocsPage(): React.ReactElement {
|
||||||
<h3 className="font-medium text-foreground">Custom Environment</h3>
|
<h3 className="font-medium text-foreground">Custom Environment</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-foreground-secondary">
|
<p className="text-sm text-foreground-secondary">
|
||||||
Inject your own environment variables, secrets, and configuration into tool
|
Inject your own API keys, database connections, and secrets into tool execution.
|
||||||
execution.
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Icon icon="folder" className="w-5 h-5 text-primary" />
|
||||||
|
<h3 className="font-medium text-foreground">Full Control</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-foreground-secondary">
|
||||||
|
Choose your infrastructure, scale resources, and customize the execution
|
||||||
|
environment.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Icon icon="clock" className="w-5 h-5 text-primary" />
|
||||||
|
<h3 className="font-medium text-foreground">No Timeouts</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-foreground-secondary">
|
||||||
|
Run long-running tools without hitting shared executor time limits.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Deploy Section */}
|
{/* Choose Your Platform */}
|
||||||
<section id="deploy" className="mb-12">
|
<section id="deploy" className="mb-12">
|
||||||
<h2 className="text-2xl font-semibold text-foreground mb-4">
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Choose Your Platform</h2>
|
||||||
Deploy Your Own Executor
|
|
||||||
</h2>
|
|
||||||
<p className="text-foreground-secondary mb-6">
|
<p className="text-foreground-secondary mb-6">
|
||||||
The fastest way to get started is to deploy our template to Vercel with one click:
|
We provide deployment templates for multiple platforms. Choose the one that fits your
|
||||||
|
needs:
|
||||||
</p>
|
</p>
|
||||||
<div className="mb-6">
|
|
||||||
<a
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
href="https://vercel.com/new/clone?repository-url=https://github.com/tpmjs/tpmjs/tree/main/templates/vercel-executor&project-name=tpmjs-executor&repository-name=tpmjs-executor"
|
{/* Unsandbox Card */}
|
||||||
target="_blank"
|
<Link
|
||||||
rel="noopener noreferrer"
|
href="/docs/executors/unsandbox"
|
||||||
|
className="group p-6 bg-surface border border-border rounded-lg hover:border-primary/50 transition-colors"
|
||||||
>
|
>
|
||||||
<Button size="lg">
|
<div className="flex items-start gap-4">
|
||||||
<Icon icon="externalLink" className="w-4 h-4 mr-2" />
|
<div className="w-12 h-12 bg-gradient-to-br from-orange-500 to-red-600 rounded-lg flex items-center justify-center text-white font-bold text-lg">
|
||||||
Deploy to Vercel
|
un
|
||||||
</Button>
|
</div>
|
||||||
</a>
|
<div className="flex-1">
|
||||||
|
<h3 className="font-semibold text-foreground group-hover:text-primary transition-colors">
|
||||||
|
Unsandbox
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-foreground-secondary mt-1">
|
||||||
|
Always-on container execution with automatic HTTPS. Deploy with one CLI
|
||||||
|
command.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-3">
|
||||||
|
<span className="px-2 py-0.5 text-xs bg-success/10 text-success rounded">
|
||||||
|
Recommended
|
||||||
|
</span>
|
||||||
|
<span className="px-2 py-0.5 text-xs bg-surface-secondary rounded text-foreground-tertiary">
|
||||||
|
No cold starts
|
||||||
|
</span>
|
||||||
|
<span className="px-2 py-0.5 text-xs bg-surface-secondary rounded text-foreground-tertiary">
|
||||||
|
Unlimited runtime
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Icon
|
||||||
|
icon="chevronRight"
|
||||||
|
className="w-5 h-5 text-foreground-tertiary group-hover:text-primary transition-colors"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Vercel Card */}
|
||||||
|
<Link
|
||||||
|
href="/docs/executors/vercel"
|
||||||
|
className="group p-6 bg-surface border border-border rounded-lg hover:border-primary/50 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="w-12 h-12 bg-black rounded-lg flex items-center justify-center">
|
||||||
|
<svg
|
||||||
|
className="w-6 h-6 text-white"
|
||||||
|
viewBox="0 0 76 65"
|
||||||
|
fill="currentColor"
|
||||||
|
aria-label="Vercel logo"
|
||||||
|
>
|
||||||
|
<path d="M37.5274 0L75.0548 65H0L37.5274 0Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="font-semibold text-foreground group-hover:text-primary transition-colors">
|
||||||
|
Vercel
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-foreground-secondary mt-1">
|
||||||
|
Serverless execution with VM-level isolation using Vercel Sandbox. One-click
|
||||||
|
deploy.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-3">
|
||||||
|
<span className="px-2 py-0.5 text-xs bg-surface-secondary rounded text-foreground-tertiary">
|
||||||
|
One-click deploy
|
||||||
|
</span>
|
||||||
|
<span className="px-2 py-0.5 text-xs bg-surface-secondary rounded text-foreground-tertiary">
|
||||||
|
Free tier available
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Icon
|
||||||
|
icon="chevronRight"
|
||||||
|
className="w-5 h-5 text-foreground-tertiary group-hover:text-primary transition-colors"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-foreground-tertiary">
|
|
||||||
After deployment, you'll get a URL like{' '}
|
<p className="text-sm text-foreground-tertiary mt-4">
|
||||||
<code className="px-1.5 py-0.5 bg-surface rounded text-foreground-secondary">
|
You can also build your own executor on any platform that runs Node.js. Just implement
|
||||||
https://tpmjs-executor.vercel.app
|
the API specification below.
|
||||||
</code>
|
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Comparison Table */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Platform Comparison</h2>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b border-border">
|
||||||
|
<th className="text-left py-3 pr-4 font-medium text-foreground">Feature</th>
|
||||||
|
<th className="text-left py-3 px-4 font-medium text-foreground">Unsandbox</th>
|
||||||
|
<th className="text-left py-3 pl-4 font-medium text-foreground">Vercel</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-foreground-secondary">
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">Deploy method</td>
|
||||||
|
<td className="py-3 px-4">CLI command</td>
|
||||||
|
<td className="py-3 pl-4">One-click button</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">Isolation</td>
|
||||||
|
<td className="py-3 px-4">Container-level</td>
|
||||||
|
<td className="py-3 pl-4">VM-level (Sandbox)</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">Cold starts</td>
|
||||||
|
<td className="py-3 px-4">
|
||||||
|
<span className="text-success">None (always-on)</span>
|
||||||
|
</td>
|
||||||
|
<td className="py-3 pl-4">Yes (serverless)</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">Max runtime</td>
|
||||||
|
<td className="py-3 px-4">
|
||||||
|
<span className="text-success">Unlimited</span>
|
||||||
|
</td>
|
||||||
|
<td className="py-3 pl-4">45min (Hobby) / 5hr (Pro)</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">Pricing</td>
|
||||||
|
<td className="py-3 px-4">Per uptime</td>
|
||||||
|
<td className="py-3 pl-4">Per compute time</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">Custom domains</td>
|
||||||
|
<td className="py-3 px-4">Yes</td>
|
||||||
|
<td className="py-3 pl-4">Yes</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="py-3 pr-4">Freeze/unfreeze</td>
|
||||||
|
<td className="py-3 px-4">Yes (save costs)</td>
|
||||||
|
<td className="py-3 pl-4">N/A (serverless)</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{/* Configuration Section */}
|
{/* Configuration Section */}
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-2xl font-semibold text-foreground mb-4">Configuration</h2>
|
<h2 className="text-2xl font-semibold text-foreground mb-4">
|
||||||
|
Connecting to Your Executor
|
||||||
|
</h2>
|
||||||
<p className="text-foreground-secondary mb-4">
|
<p className="text-foreground-secondary mb-4">
|
||||||
Once you have your executor deployed, configure your collections or agents to use it:
|
Once deployed, configure your collections or agents to use your executor:
|
||||||
</p>
|
</p>
|
||||||
<ol className="list-decimal list-inside text-foreground-secondary space-y-3 mb-6">
|
<ol className="list-decimal list-inside text-foreground-secondary space-y-3 mb-6">
|
||||||
<li>Go to your collection or agent settings</li>
|
<li>Go to your collection or agent settings</li>
|
||||||
<li>
|
<li>In "Executor Configuration", select "Custom Executor"</li>
|
||||||
In the "Executor Configuration" section, select "Custom
|
|
||||||
Executor"
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
Enter your executor URL (e.g.,{' '}
|
Enter your executor URL (e.g.,{' '}
|
||||||
<code className="px-1.5 py-0.5 bg-surface rounded">
|
<code className="px-1.5 py-0.5 bg-surface rounded">
|
||||||
https://tpmjs-executor.vercel.app
|
https://my-executor.on.unsandbox.com
|
||||||
</code>
|
</code>
|
||||||
)
|
)
|
||||||
</li>
|
</li>
|
||||||
<li>Optionally add an API key if your executor requires authentication</li>
|
<li>Add your API key if authentication is enabled</li>
|
||||||
<li>Click "Verify Connection" to test the configuration</li>
|
<li>Click "Verify Connection" to test</li>
|
||||||
</ol>
|
</ol>
|
||||||
<div className="p-4 bg-warning/10 border border-warning/30 rounded-lg">
|
<div className="p-4 bg-warning/10 border border-warning/30 rounded-lg">
|
||||||
<p className="text-sm text-warning">
|
<p className="text-sm text-warning">
|
||||||
<strong>Security tip:</strong> Set the{' '}
|
<strong>Security:</strong> Always set{' '}
|
||||||
<code className="px-1 bg-warning/20 rounded">EXECUTOR_API_KEY</code> environment
|
<code className="px-1 bg-warning/20 rounded">EXECUTOR_API_KEY</code> to require
|
||||||
variable in your Vercel project to require authentication for all requests.
|
authentication. Without it, anyone can execute tools on your executor.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -188,7 +295,10 @@ export default function ExecutorsDocsPage(): React.ReactElement {
|
||||||
<h2 className="text-2xl font-semibold text-foreground mb-4">
|
<h2 className="text-2xl font-semibold text-foreground mb-4">
|
||||||
Executor API Specification
|
Executor API Specification
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-foreground-secondary mb-6">All executors must implement this API:</p>
|
<p className="text-foreground-secondary mb-6">
|
||||||
|
All executors must implement these endpoints. Use this spec if building a custom
|
||||||
|
executor.
|
||||||
|
</p>
|
||||||
|
|
||||||
{/* POST /execute-tool */}
|
{/* POST /execute-tool */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
|
|
@ -197,7 +307,9 @@ export default function ExecutorsDocsPage(): React.ReactElement {
|
||||||
/execute-tool
|
/execute-tool
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-foreground-secondary mb-4">
|
<p className="text-foreground-secondary mb-4">
|
||||||
Execute a TPMJS tool with the provided parameters.
|
Execute a TPMJS tool. The executor should install the npm package, find the named
|
||||||
|
export, and call its{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">execute(params)</code> function.
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -217,22 +329,33 @@ export default function ExecutorsDocsPage(): React.ReactElement {
|
||||||
<code className="px-2 py-1 bg-success/10 text-success rounded">GET</code> /health
|
<code className="px-2 py-1 bg-success/10 text-success rounded">GET</code> /health
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-foreground-secondary mb-4">
|
<p className="text-foreground-secondary mb-4">
|
||||||
Check executor health status. Used by TPMJS to verify the executor is reachable.
|
Health check endpoint. TPMJS uses this to verify the executor is reachable and
|
||||||
|
working.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-foreground mb-2">Response:</p>
|
<p className="text-sm font-medium text-foreground mb-2">Response:</p>
|
||||||
<CodeBlock language="json" code={healthExample} />
|
<CodeBlock language="json" code={healthExample} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<p className="text-sm text-foreground-secondary">
|
||||||
|
<strong>Note:</strong> Both{' '}
|
||||||
|
<code className="px-1 bg-surface-secondary rounded">/api/health</code> and{' '}
|
||||||
|
<code className="px-1 bg-surface-secondary rounded">/health</code> paths should work
|
||||||
|
(same for <code className="px-1 bg-surface-secondary rounded">/execute-tool</code>).
|
||||||
|
Our templates support both.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Cascade Section */}
|
{/* Executor Cascade */}
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-2xl font-semibold text-foreground mb-4">Executor Cascade</h2>
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Executor Cascade</h2>
|
||||||
<p className="text-foreground-secondary mb-4">
|
<p className="text-foreground-secondary mb-4">
|
||||||
Executor configuration follows a cascade resolution order:
|
When a tool is executed, TPMJS resolves which executor to use in this order:
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-2 text-foreground-secondary mb-4">
|
<div className="flex items-center gap-2 text-foreground-secondary mb-4 flex-wrap">
|
||||||
<span className="px-3 py-1 bg-primary/10 text-primary rounded-full text-sm font-medium">
|
<span className="px-3 py-1 bg-primary/10 text-primary rounded-full text-sm font-medium">
|
||||||
Agent Config
|
Agent Config
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -255,35 +378,47 @@ export default function ExecutorsDocsPage(): React.ReactElement {
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* FAQ Section */}
|
{/* FAQ */}
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-2xl font-semibold text-foreground mb-4">FAQ</h2>
|
<h2 className="text-2xl font-semibold text-foreground mb-4">FAQ</h2>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-foreground mb-2">Can I use any cloud provider?</h3>
|
<h3 className="font-medium text-foreground mb-2">
|
||||||
|
Which platform should I choose?
|
||||||
|
</h3>
|
||||||
<p className="text-foreground-secondary text-sm">
|
<p className="text-foreground-secondary text-sm">
|
||||||
Yes! While we provide a Vercel template, you can deploy an executor anywhere that
|
<strong>Unsandbox</strong> is recommended for most use cases. It has no cold
|
||||||
can run Node.js and expose an HTTP endpoint. The executor just needs to implement
|
starts, unlimited runtime, and simple CLI deployment. Use <strong>Vercel</strong>{' '}
|
||||||
the API specification above.
|
if you're already on Vercel or prefer one-click deployment and pay-per-use
|
||||||
|
pricing.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-foreground mb-2">What about timeouts?</h3>
|
<h3 className="font-medium text-foreground mb-2">Can I use other platforms?</h3>
|
||||||
<p className="text-foreground-secondary text-sm">
|
<p className="text-foreground-secondary text-sm">
|
||||||
The default timeout for tool execution is 30 seconds. On Vercel's free tier,
|
Yes! Any platform that runs Node.js and exposes HTTP endpoints works. AWS Lambda,
|
||||||
you get up to 10 seconds per request. For longer-running tools, consider deploying
|
Google Cloud Run, Railway, Render, Fly.io—just implement the API specification
|
||||||
to a platform with higher timeout limits.
|
above.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-foreground mb-2">How do tools get loaded?</h3>
|
<h3 className="font-medium text-foreground mb-2">How do tools get loaded?</h3>
|
||||||
<p className="text-foreground-secondary text-sm">
|
<p className="text-foreground-secondary text-sm">
|
||||||
Tools are dynamically imported from{' '}
|
The executor runs <code className="px-1 bg-surface rounded">npm install</code> for
|
||||||
<Link href="https://esm.sh" className="text-primary hover:underline">
|
the requested package, then dynamically imports it and calls the tool's{' '}
|
||||||
esm.sh
|
<code className="px-1 bg-surface rounded">execute()</code> function. Each
|
||||||
</Link>
|
execution uses a fresh temporary directory.
|
||||||
, a CDN for npm packages. The executor fetches the package, finds the tool export,
|
</p>
|
||||||
and calls its <code className="px-1 bg-surface rounded">execute()</code> function.
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium text-foreground mb-2">
|
||||||
|
Are environment variables secure?
|
||||||
|
</h3>
|
||||||
|
<p className="text-foreground-secondary text-sm">
|
||||||
|
Yes. Environment variables are stored encrypted by the platform (Vercel/Unsandbox)
|
||||||
|
and only available during execution. You can also pass per-request environment
|
||||||
|
variables in the <code className="px-1 bg-surface rounded">env</code> field of the
|
||||||
|
execute-tool request.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -302,16 +437,22 @@ export default function ExecutorsDocsPage(): React.ReactElement {
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<Button variant="secondary" size="sm">
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center px-3 py-1.5 text-sm font-medium rounded-md bg-surface-secondary hover:bg-surface-tertiary text-foreground transition-colors"
|
||||||
|
>
|
||||||
<Icon icon="github" className="w-4 h-4 mr-2" />
|
<Icon icon="github" className="w-4 h-4 mr-2" />
|
||||||
Open an Issue
|
Open an Issue
|
||||||
</Button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.gg/tpmjs" target="_blank" rel="noopener noreferrer">
|
<a href="https://discord.gg/tpmjs" target="_blank" rel="noopener noreferrer">
|
||||||
<Button variant="ghost" size="sm">
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center px-3 py-1.5 text-sm font-medium rounded-md hover:bg-surface text-foreground-secondary transition-colors"
|
||||||
|
>
|
||||||
<Icon icon="discord" className="w-4 h-4 mr-2" />
|
<Icon icon="discord" className="w-4 h-4 mr-2" />
|
||||||
Join Discord
|
Join Discord
|
||||||
</Button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
420
apps/web/src/app/docs/executors/unsandbox/page.tsx
Normal file
420
apps/web/src/app/docs/executors/unsandbox/page.tsx
Normal file
|
|
@ -0,0 +1,420 @@
|
||||||
|
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
|
||||||
|
import { Icon } from '@tpmjs/ui/Icon/Icon';
|
||||||
|
import type { Metadata } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import { AppFooter } from '~/components/AppFooter';
|
||||||
|
import { AppHeader } from '~/components/AppHeader';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Deploy to Unsandbox - Custom Executors - TPMJS',
|
||||||
|
description:
|
||||||
|
'Deploy a TPMJS executor to Unsandbox with one CLI command. Always-on, no cold starts, unlimited runtime.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const deployCommand = `# Install the Unsandbox CLI
|
||||||
|
curl -fsSL https://unsandbox.com/install.sh | bash
|
||||||
|
|
||||||
|
# Deploy the TPMJS executor
|
||||||
|
un service --name tpmjs-executor --ports 80 -n semitrusted \\
|
||||||
|
--bootstrap "curl -fsSL https://raw.githubusercontent.com/tpmjs/tpmjs/main/templates/unsandbox-executor/bootstrap.sh | bash"`;
|
||||||
|
|
||||||
|
const deployWithApiKey = `un service --name tpmjs-executor --ports 80 -n semitrusted \\
|
||||||
|
-e EXECUTOR_API_KEY=your-secure-random-key \\
|
||||||
|
--bootstrap "curl -fsSL https://raw.githubusercontent.com/tpmjs/tpmjs/main/templates/unsandbox-executor/bootstrap.sh | bash"`;
|
||||||
|
|
||||||
|
const deployWithEnvVars = `un service --name tpmjs-executor --ports 80 -n semitrusted \\
|
||||||
|
-e EXECUTOR_API_KEY=your-key \\
|
||||||
|
-e OPENAI_API_KEY=sk-xxx \\
|
||||||
|
-e DATABASE_URL=postgres://... \\
|
||||||
|
--bootstrap "curl -fsSL https://raw.githubusercontent.com/tpmjs/tpmjs/main/templates/unsandbox-executor/bootstrap.sh | bash"`;
|
||||||
|
|
||||||
|
const deployWithEnvFile = `# Create .env file with your secrets
|
||||||
|
cat > .env << EOF
|
||||||
|
EXECUTOR_API_KEY=your-key
|
||||||
|
OPENAI_API_KEY=sk-xxx
|
||||||
|
DATABASE_URL=postgres://...
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Deploy with env file
|
||||||
|
un service --name tpmjs-executor --ports 80 -n semitrusted \\
|
||||||
|
--env-file .env \\
|
||||||
|
--bootstrap "curl -fsSL https://raw.githubusercontent.com/tpmjs/tpmjs/main/templates/unsandbox-executor/bootstrap.sh | bash"`;
|
||||||
|
|
||||||
|
const healthCheck = `curl https://tpmjs-executor.on.unsandbox.com/api/health`;
|
||||||
|
|
||||||
|
const healthResponse = `{
|
||||||
|
"status": "ok",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"info": {
|
||||||
|
"runtime": "unsandbox",
|
||||||
|
"timestamp": "2024-01-01T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const executeExample = `curl -X POST https://tpmjs-executor.on.unsandbox.com/api/execute-tool \\
|
||||||
|
-H "Content-Type: application/json" \\
|
||||||
|
-H "Authorization: Bearer your-api-key" \\
|
||||||
|
-d '{
|
||||||
|
"packageName": "@tpmjs/hello",
|
||||||
|
"name": "helloWorldTool",
|
||||||
|
"version": "latest",
|
||||||
|
"params": { "includeTimestamp": true }
|
||||||
|
}'`;
|
||||||
|
|
||||||
|
const localDev = `# Clone the repository
|
||||||
|
git clone https://github.com/tpmjs/tpmjs.git
|
||||||
|
cd tpmjs/templates/unsandbox-executor
|
||||||
|
|
||||||
|
# Run locally
|
||||||
|
PORT=3000 node executor.js
|
||||||
|
|
||||||
|
# Test health endpoint
|
||||||
|
curl http://localhost:3000/api/health`;
|
||||||
|
|
||||||
|
const managementCommands = `# View logs
|
||||||
|
un service --logs tpmjs-executor
|
||||||
|
|
||||||
|
# Redeploy (after updating)
|
||||||
|
un service --redeploy tpmjs-executor
|
||||||
|
|
||||||
|
# Freeze when not in use (save costs)
|
||||||
|
un service --freeze tpmjs-executor
|
||||||
|
|
||||||
|
# Unfreeze when needed
|
||||||
|
un service --unfreeze tpmjs-executor
|
||||||
|
|
||||||
|
# Scale resources (4 vCPU, 8GB RAM)
|
||||||
|
un service --resize tpmjs-executor --vcpu 4
|
||||||
|
|
||||||
|
# Destroy service
|
||||||
|
un service --destroy tpmjs-executor`;
|
||||||
|
|
||||||
|
const customDomain = `un service --name tpmjs-executor --ports 80 -n semitrusted \\
|
||||||
|
--domains executor.yourdomain.com \\
|
||||||
|
--bootstrap "curl -fsSL https://raw.githubusercontent.com/tpmjs/tpmjs/main/templates/unsandbox-executor/bootstrap.sh | bash"`;
|
||||||
|
|
||||||
|
export default function UnsandboxExecutorPage(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-background flex flex-col">
|
||||||
|
<AppHeader />
|
||||||
|
|
||||||
|
<main className="flex-1">
|
||||||
|
<div className="max-w-4xl mx-auto px-4 py-12">
|
||||||
|
{/* Breadcrumb */}
|
||||||
|
<nav className="flex items-center gap-2 text-sm text-foreground-secondary mb-8">
|
||||||
|
<Link href="/docs/executors" className="hover:text-foreground transition-colors">
|
||||||
|
Executors
|
||||||
|
</Link>
|
||||||
|
<Icon icon="chevronRight" className="w-4 h-4" />
|
||||||
|
<span className="text-foreground">Unsandbox</span>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="mb-12">
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="w-12 h-12 bg-gradient-to-br from-orange-500 to-red-600 rounded-lg flex items-center justify-center text-white font-bold text-lg">
|
||||||
|
un
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl font-bold text-foreground">Deploy to Unsandbox</h1>
|
||||||
|
<p className="text-foreground-secondary">
|
||||||
|
Always-on execution with one CLI command
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Why Unsandbox */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="text-2xl mb-2">0ms</div>
|
||||||
|
<div className="text-sm text-foreground-secondary">No cold starts</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="text-2xl mb-2">∞</div>
|
||||||
|
<div className="text-sm text-foreground-secondary">Unlimited runtime</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="text-2xl mb-2">1 cmd</div>
|
||||||
|
<div className="text-sm text-foreground-secondary">Deploy in seconds</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Quick Deploy */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Quick Deploy</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Deploy a TPMJS executor with a single command. Your executor will be live at{' '}
|
||||||
|
<code className="px-1.5 py-0.5 bg-surface rounded">
|
||||||
|
https://tpmjs-executor.on.unsandbox.com
|
||||||
|
</code>
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={deployCommand} />
|
||||||
|
<p className="text-sm text-foreground-tertiary mt-4">
|
||||||
|
This creates an always-on service that runs the executor. HTTPS is automatically
|
||||||
|
configured.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Test Your Deployment */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Test Your Deployment</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Verify your executor is running with a health check:
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={healthCheck} />
|
||||||
|
<p className="text-sm text-foreground-secondary mt-4 mb-2">Expected response:</p>
|
||||||
|
<CodeBlock language="json" code={healthResponse} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Authentication */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Add Authentication</h2>
|
||||||
|
<div className="p-4 bg-warning/10 border border-warning/30 rounded-lg mb-4">
|
||||||
|
<p className="text-sm text-warning">
|
||||||
|
<strong>Important:</strong> Without an API key, anyone can execute tools on your
|
||||||
|
executor. Always set{' '}
|
||||||
|
<code className="px-1 bg-warning/20 rounded">EXECUTOR_API_KEY</code> in production.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Deploy with an API key to require authentication:
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={deployWithApiKey} />
|
||||||
|
<p className="text-sm text-foreground-tertiary mt-4">
|
||||||
|
When configured, requests must include{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">Authorization: Bearer your-api-key</code>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Environment Variables */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Environment Variables</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Pass environment variables that your tools need. These are available during tool
|
||||||
|
execution.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 className="text-lg font-medium text-foreground mb-3">Inline Variables</h3>
|
||||||
|
<CodeBlock language="bash" code={deployWithEnvVars} />
|
||||||
|
|
||||||
|
<h3 className="text-lg font-medium text-foreground mt-6 mb-3">Using an Env File</h3>
|
||||||
|
<CodeBlock language="bash" code={deployWithEnvFile} />
|
||||||
|
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg mt-4">
|
||||||
|
<p className="text-sm text-foreground-secondary">
|
||||||
|
All environment variables are stored encrypted and only available to your executor.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Execute a Tool */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Execute a Tool</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Test tool execution with a curl request:
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={executeExample} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Local Development */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Local Development</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Run the executor locally for testing and development:
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={localDev} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Management Commands */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Managing Your Service</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Unsandbox provides commands to manage your executor:
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={managementCommands} />
|
||||||
|
|
||||||
|
<h3 className="text-lg font-medium text-foreground mt-6 mb-3">Cost Optimization</h3>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Freeze your executor when not in use to stop billing:
|
||||||
|
</p>
|
||||||
|
<ul className="text-foreground-secondary text-sm space-y-2">
|
||||||
|
<li>
|
||||||
|
• <code className="px-1 bg-surface rounded">un service --freeze</code> stops the
|
||||||
|
service and billing
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
• <code className="px-1 bg-surface rounded">un service --unfreeze</code> restarts it
|
||||||
|
when needed
|
||||||
|
</li>
|
||||||
|
<li>• Configure auto-unfreeze to wake on HTTP request (incurs cold start)</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Custom Domains */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Custom Domains</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Use your own domain instead of the default{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">*.on.unsandbox.com</code>:
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={customDomain} />
|
||||||
|
<p className="text-foreground-secondary text-sm mt-4">
|
||||||
|
After deploying, add a CNAME record pointing{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">executor.yourdomain.com</code> to your
|
||||||
|
Unsandbox service domain.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* How It Works */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">How It Works</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
The Unsandbox executor runs as an always-on HTTP server:
|
||||||
|
</p>
|
||||||
|
<ol className="text-foreground-secondary space-y-3">
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
1
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Receives tool execution request via HTTP POST to{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">/api/execute-tool</code>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
2
|
||||||
|
</span>
|
||||||
|
<span>Creates an isolated temporary directory for the execution</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
3
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Installs the npm package using{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">npm install</code>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
4
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Loads the tool and calls its{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">execute()</code> function
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
5
|
||||||
|
</span>
|
||||||
|
<span>Returns the result and cleans up the temporary directory</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p className="text-foreground-secondary text-sm mt-4">
|
||||||
|
Since Unsandbox containers are already isolated, no additional sandbox layer is
|
||||||
|
needed. Network access is controlled by Unsandbox's semitrusted mode.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Security */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Security</h2>
|
||||||
|
<ul className="text-foreground-secondary space-y-2">
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>
|
||||||
|
Set <code className="px-1 bg-surface rounded">EXECUTOR_API_KEY</code> to require
|
||||||
|
authentication
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>Tools run in isolated Unsandbox containers</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>Each execution uses a fresh temporary directory</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>Network controlled by semitrusted mode</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>Environment variables stored encrypted</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Pricing */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Pricing</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Unsandbox services are billed based on uptime. See{' '}
|
||||||
|
<a
|
||||||
|
href="https://unsandbox.com/pricing"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:underline"
|
||||||
|
>
|
||||||
|
Unsandbox Pricing
|
||||||
|
</a>{' '}
|
||||||
|
for current rates.
|
||||||
|
</p>
|
||||||
|
<ul className="text-foreground-secondary text-sm space-y-2">
|
||||||
|
<li>
|
||||||
|
• HTTPS included via{' '}
|
||||||
|
<code className="px-1 bg-surface rounded">*.on.unsandbox.com</code>
|
||||||
|
</li>
|
||||||
|
<li>• Freeze when not in use to pause billing</li>
|
||||||
|
<li>• Scale vCPU and RAM as needed</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Connect to TPMJS */}
|
||||||
|
<section className="mb-12 p-6 bg-primary/5 border border-primary/20 rounded-lg">
|
||||||
|
<h2 className="text-lg font-semibold text-foreground mb-4">Connect to TPMJS</h2>
|
||||||
|
<ol className="text-foreground-secondary space-y-2">
|
||||||
|
<li>1. Go to your collection or agent settings on TPMJS</li>
|
||||||
|
<li>2. Select "Custom Executor" in Executor Configuration</li>
|
||||||
|
<li>
|
||||||
|
3. Enter URL:{' '}
|
||||||
|
<code className="px-1.5 py-0.5 bg-surface rounded">
|
||||||
|
https://tpmjs-executor.on.unsandbox.com
|
||||||
|
</code>
|
||||||
|
</li>
|
||||||
|
<li>4. Enter your API key (if configured)</li>
|
||||||
|
<li>5. Click "Verify Connection"</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<div className="flex items-center justify-between pt-8 border-t border-border">
|
||||||
|
<Link
|
||||||
|
href="/docs/executors"
|
||||||
|
className="flex items-center gap-2 text-foreground-secondary hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
<Icon icon="chevronLeft" className="w-4 h-4" />
|
||||||
|
<span>Back to Executors</span>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/docs/executors/vercel"
|
||||||
|
className="flex items-center gap-2 text-foreground-secondary hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
<span>Vercel Guide</span>
|
||||||
|
<Icon icon="chevronRight" className="w-4 h-4" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<AppFooter />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
426
apps/web/src/app/docs/executors/vercel/page.tsx
Normal file
426
apps/web/src/app/docs/executors/vercel/page.tsx
Normal file
|
|
@ -0,0 +1,426 @@
|
||||||
|
import { Button } from '@tpmjs/ui/Button/Button';
|
||||||
|
import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
|
||||||
|
import { Icon } from '@tpmjs/ui/Icon/Icon';
|
||||||
|
import type { Metadata } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import { AppFooter } from '~/components/AppFooter';
|
||||||
|
import { AppHeader } from '~/components/AppHeader';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Deploy to Vercel - Custom Executors - TPMJS',
|
||||||
|
description:
|
||||||
|
'Deploy a TPMJS executor to Vercel with one click. VM-level isolation using Vercel Sandbox.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const healthCheck = `curl https://your-executor.vercel.app/api/health`;
|
||||||
|
|
||||||
|
const healthResponse = `{
|
||||||
|
"status": "ok",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"info": {
|
||||||
|
"runtime": "vercel-sandbox",
|
||||||
|
"region": "iad1",
|
||||||
|
"timestamp": "2024-01-01T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const executeExample = `curl -X POST https://your-executor.vercel.app/api/execute-tool \\
|
||||||
|
-H "Content-Type: application/json" \\
|
||||||
|
-H "Authorization: Bearer your-api-key" \\
|
||||||
|
-d '{
|
||||||
|
"packageName": "@tpmjs/hello",
|
||||||
|
"name": "helloWorld",
|
||||||
|
"version": "latest",
|
||||||
|
"params": { "name": "World" }
|
||||||
|
}'`;
|
||||||
|
|
||||||
|
const localDev = `# Clone and install
|
||||||
|
git clone https://github.com/tpmjs/tpmjs.git
|
||||||
|
cd tpmjs/templates/vercel-executor
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Login to Vercel (required for sandbox)
|
||||||
|
vercel login
|
||||||
|
vercel link
|
||||||
|
|
||||||
|
# Pull environment variables
|
||||||
|
vercel env pull
|
||||||
|
|
||||||
|
# Run development server
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Test health endpoint
|
||||||
|
curl http://localhost:3000/api/health`;
|
||||||
|
|
||||||
|
export default function VercelExecutorPage(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-background flex flex-col">
|
||||||
|
<AppHeader />
|
||||||
|
|
||||||
|
<main className="flex-1">
|
||||||
|
<div className="max-w-4xl mx-auto px-4 py-12">
|
||||||
|
{/* Breadcrumb */}
|
||||||
|
<nav className="flex items-center gap-2 text-sm text-foreground-secondary mb-8">
|
||||||
|
<Link href="/docs/executors" className="hover:text-foreground transition-colors">
|
||||||
|
Executors
|
||||||
|
</Link>
|
||||||
|
<Icon icon="chevronRight" className="w-4 h-4" />
|
||||||
|
<span className="text-foreground">Vercel</span>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="mb-12">
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="w-12 h-12 bg-black rounded-lg flex items-center justify-center">
|
||||||
|
<svg
|
||||||
|
className="w-6 h-6 text-white"
|
||||||
|
viewBox="0 0 76 65"
|
||||||
|
fill="currentColor"
|
||||||
|
aria-label="Vercel logo"
|
||||||
|
>
|
||||||
|
<path d="M37.5274 0L75.0548 65H0L37.5274 0Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl font-bold text-foreground">Deploy to Vercel</h1>
|
||||||
|
<p className="text-foreground-secondary">
|
||||||
|
One-click deploy with VM-level isolation
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Why Vercel */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="text-2xl mb-2">1-click</div>
|
||||||
|
<div className="text-sm text-foreground-secondary">Deploy instantly</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="text-2xl mb-2">VM</div>
|
||||||
|
<div className="text-sm text-foreground-secondary">Sandbox isolation</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg">
|
||||||
|
<div className="text-2xl mb-2">Free</div>
|
||||||
|
<div className="text-sm text-foreground-secondary">Hobby tier available</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* One-Click Deploy */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">One-Click Deploy</h2>
|
||||||
|
<p className="text-foreground-secondary mb-6">
|
||||||
|
Deploy the TPMJS executor template to your Vercel account:
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="https://vercel.com/new/clone?repository-url=https://github.com/tpmjs/tpmjs/tree/main/templates/vercel-executor&project-name=tpmjs-executor&repository-name=tpmjs-executor"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Button size="lg">
|
||||||
|
<svg
|
||||||
|
className="w-4 h-4 mr-2"
|
||||||
|
viewBox="0 0 76 65"
|
||||||
|
fill="currentColor"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M37.5274 0L75.0548 65H0L37.5274 0Z" />
|
||||||
|
</svg>
|
||||||
|
Deploy with Vercel
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
<p className="text-sm text-foreground-tertiary mt-4">
|
||||||
|
After deployment, your executor will be available at{' '}
|
||||||
|
<code className="px-1.5 py-0.5 bg-surface rounded">
|
||||||
|
https://tpmjs-executor.vercel.app
|
||||||
|
</code>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Test Your Deployment */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Test Your Deployment</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">Verify your executor is running:</p>
|
||||||
|
<CodeBlock language="bash" code={healthCheck} />
|
||||||
|
<p className="text-sm text-foreground-secondary mt-4 mb-2">Expected response:</p>
|
||||||
|
<CodeBlock language="json" code={healthResponse} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Authentication */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Add Authentication</h2>
|
||||||
|
<div className="p-4 bg-warning/10 border border-warning/30 rounded-lg mb-4">
|
||||||
|
<p className="text-sm text-warning">
|
||||||
|
<strong>Important:</strong> Without an API key, anyone can execute tools on your
|
||||||
|
executor. Always set{' '}
|
||||||
|
<code className="px-1 bg-warning/20 rounded">EXECUTOR_API_KEY</code> in production.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ol className="text-foreground-secondary space-y-3">
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-surface-secondary text-foreground-secondary text-sm flex items-center justify-center">
|
||||||
|
1
|
||||||
|
</span>
|
||||||
|
<span>Go to your Vercel project settings</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-surface-secondary text-foreground-secondary text-sm flex items-center justify-center">
|
||||||
|
2
|
||||||
|
</span>
|
||||||
|
<span>Navigate to Environment Variables</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-surface-secondary text-foreground-secondary text-sm flex items-center justify-center">
|
||||||
|
3
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Add <code className="px-1 bg-surface rounded">EXECUTOR_API_KEY</code> with a
|
||||||
|
secure random value
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-surface-secondary text-foreground-secondary text-sm flex items-center justify-center">
|
||||||
|
4
|
||||||
|
</span>
|
||||||
|
<span>Redeploy your project to apply the changes</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Environment Variables */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Environment Variables</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Add custom environment variables for your tools in Vercel project settings:
|
||||||
|
</p>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b border-border">
|
||||||
|
<th className="text-left py-3 pr-4 font-medium text-foreground">Variable</th>
|
||||||
|
<th className="text-left py-3 px-4 font-medium text-foreground">Required</th>
|
||||||
|
<th className="text-left py-3 pl-4 font-medium text-foreground">Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-foreground-secondary">
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">
|
||||||
|
<code className="px-1 bg-surface rounded">EXECUTOR_API_KEY</code>
|
||||||
|
</td>
|
||||||
|
<td className="py-3 px-4">No*</td>
|
||||||
|
<td className="py-3 pl-4">
|
||||||
|
API key for authentication. Required for production.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">
|
||||||
|
<code className="px-1 bg-surface rounded">OPENAI_API_KEY</code>
|
||||||
|
</td>
|
||||||
|
<td className="py-3 px-4">No</td>
|
||||||
|
<td className="py-3 pl-4">Example: Pass through to tools that need OpenAI</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="py-3 pr-4">
|
||||||
|
<code className="px-1 bg-surface rounded">DATABASE_URL</code>
|
||||||
|
</td>
|
||||||
|
<td className="py-3 px-4">No</td>
|
||||||
|
<td className="py-3 pl-4">Example: Pass through to tools that need database</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-foreground-tertiary mt-4">
|
||||||
|
* Strongly recommended for production deployments
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Execute a Tool */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Execute a Tool</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Test tool execution with a curl request:
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={executeExample} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Local Development */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Local Development</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Run the executor locally for testing. Note: Vercel Sandbox requires authentication
|
||||||
|
even in development.
|
||||||
|
</p>
|
||||||
|
<CodeBlock language="bash" code={localDev} />
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg mt-4">
|
||||||
|
<p className="text-sm text-foreground-secondary">
|
||||||
|
<strong>Note:</strong> You must run{' '}
|
||||||
|
<code className="px-1 bg-surface-secondary rounded">vercel login</code> and{' '}
|
||||||
|
<code className="px-1 bg-surface-secondary rounded">vercel link</code> before local
|
||||||
|
development. Vercel Sandbox requires authentication to create VMs.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* How It Works */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">How It Works</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
The Vercel executor uses{' '}
|
||||||
|
<a
|
||||||
|
href="https://vercel.com/docs/vercel-sandbox"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:underline"
|
||||||
|
>
|
||||||
|
Vercel Sandbox
|
||||||
|
</a>{' '}
|
||||||
|
for isolated execution:
|
||||||
|
</p>
|
||||||
|
<ol className="text-foreground-secondary space-y-3">
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
1
|
||||||
|
</span>
|
||||||
|
<span>Creates an isolated VM for each tool execution</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
2
|
||||||
|
</span>
|
||||||
|
<span>Installs the npm package in the sandbox</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
3
|
||||||
|
</span>
|
||||||
|
<span>Executes the tool with your parameters</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex gap-3">
|
||||||
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-primary/10 text-primary text-sm flex items-center justify-center">
|
||||||
|
4
|
||||||
|
</span>
|
||||||
|
<span>Returns the result and destroys the sandbox</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p className="text-foreground-secondary text-sm mt-4">
|
||||||
|
This provides VM-level isolation without the limitations of Node.js serverless
|
||||||
|
functions.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Security */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Security</h2>
|
||||||
|
<ul className="text-foreground-secondary space-y-2">
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>
|
||||||
|
Set <code className="px-1 bg-surface rounded">EXECUTOR_API_KEY</code> to require
|
||||||
|
authentication
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>Tools run in isolated VMs with no access to your Vercel project</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>Each execution gets a fresh sandbox instance</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Icon icon="check" className="w-5 h-5 text-success flex-shrink-0 mt-0.5" />
|
||||||
|
<span>Sandboxes are destroyed after execution completes</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Pricing & Limits */}
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-semibold text-foreground mb-4">Pricing & Limits</h2>
|
||||||
|
<p className="text-foreground-secondary mb-4">
|
||||||
|
Vercel Sandbox usage is billed based on compute time. See{' '}
|
||||||
|
<a
|
||||||
|
href="https://vercel.com/docs/vercel-sandbox/pricing"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:underline"
|
||||||
|
>
|
||||||
|
Vercel Sandbox Pricing
|
||||||
|
</a>{' '}
|
||||||
|
for current rates.
|
||||||
|
</p>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b border-border">
|
||||||
|
<th className="text-left py-3 pr-4 font-medium text-foreground">Plan</th>
|
||||||
|
<th className="text-left py-3 px-4 font-medium text-foreground">Max Runtime</th>
|
||||||
|
<th className="text-left py-3 pl-4 font-medium text-foreground">Notes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="text-foreground-secondary">
|
||||||
|
<tr className="border-b border-border/50">
|
||||||
|
<td className="py-3 pr-4">Hobby</td>
|
||||||
|
<td className="py-3 px-4">45 minutes</td>
|
||||||
|
<td className="py-3 pl-4">Free tier</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className="py-3 pr-4">Pro</td>
|
||||||
|
<td className="py-3 px-4">5 hours</td>
|
||||||
|
<td className="py-3 pl-4">For longer-running tools</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-surface border border-border rounded-lg mt-4">
|
||||||
|
<p className="text-sm text-foreground-secondary">
|
||||||
|
<strong>Region:</strong> Vercel Sandbox is currently only available in{' '}
|
||||||
|
<code className="px-1 bg-surface-secondary rounded">iad1</code> (US East).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Connect to TPMJS */}
|
||||||
|
<section className="mb-12 p-6 bg-primary/5 border border-primary/20 rounded-lg">
|
||||||
|
<h2 className="text-lg font-semibold text-foreground mb-4">Connect to TPMJS</h2>
|
||||||
|
<ol className="text-foreground-secondary space-y-2">
|
||||||
|
<li>1. Go to your collection or agent settings on TPMJS</li>
|
||||||
|
<li>2. Select "Custom Executor" in Executor Configuration</li>
|
||||||
|
<li>
|
||||||
|
3. Enter URL:{' '}
|
||||||
|
<code className="px-1.5 py-0.5 bg-surface rounded">
|
||||||
|
https://tpmjs-executor.vercel.app
|
||||||
|
</code>
|
||||||
|
</li>
|
||||||
|
<li>4. Enter your API key (if configured)</li>
|
||||||
|
<li>5. Click "Verify Connection"</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<div className="flex items-center justify-between pt-8 border-t border-border">
|
||||||
|
<Link
|
||||||
|
href="/docs/executors/unsandbox"
|
||||||
|
className="flex items-center gap-2 text-foreground-secondary hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
<Icon icon="chevronLeft" className="w-4 h-4" />
|
||||||
|
<span>Unsandbox Guide</span>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/docs/executors"
|
||||||
|
className="flex items-center gap-2 text-foreground-secondary hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
<span>Back to Executors</span>
|
||||||
|
<Icon icon="chevronRight" className="w-4 h-4" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<AppFooter />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue