refactor(executor): switch vercel template from Next.js to Deno runtime

- Replace Next.js app router with pure Deno API functions
- Use vercel-deno@3.0.0 community runtime for native HTTP imports
- Deno natively supports importing from esm.sh URLs
- Simplified template: just api/health.ts and api/execute-tool.ts
- Remove unnecessary React/Next.js dependencies
- Update README with new architecture documentation

This matches the Railway executor's Deno-based approach but runs on
the user's own Vercel account. The Deno runtime enables dynamic
imports from esm.sh without any special setup.
This commit is contained in:
Ajax Davis 2026-01-09 01:15:47 +10:00
parent 346a3f6da0
commit 300083aff9
11 changed files with 357 additions and 357 deletions

View file

@ -1,112 +0,0 @@
/**
* Execute Tool Endpoint
*
* POST /api/execute-tool
* Executes a TPMJS tool with the provided parameters
*/
import { type ExecuteToolRequest, executeTool } from '@/lib/executor';
import { type NextRequest, NextResponse } from 'next/server';
export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';
export const maxDuration = 60; // 60 seconds max
/**
* Verify API key if configured
*/
function verifyApiKey(request: NextRequest): boolean {
const apiKey = process.env.EXECUTOR_API_KEY;
// If no API key is configured, allow all requests
if (!apiKey) {
return true;
}
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return false;
}
// Expect "Bearer <api-key>" format
const [type, token] = authHeader.split(' ');
if (type !== 'Bearer' || token !== apiKey) {
return false;
}
return true;
}
/**
* Validate the request body
*/
function validateRequest(body: unknown): body is ExecuteToolRequest {
if (!body || typeof body !== 'object') {
return false;
}
const req = body as Record<string, unknown>;
return (
typeof req.packageName === 'string' &&
req.packageName.length > 0 &&
typeof req.name === 'string' &&
req.name.length > 0 &&
typeof req.params === 'object' &&
req.params !== null
);
}
export async function POST(request: NextRequest): Promise<NextResponse> {
// Verify API key if configured
if (!verifyApiKey(request)) {
return NextResponse.json(
{ success: false, error: 'Unauthorized', executionTimeMs: 0 },
{ status: 401 }
);
}
try {
const body = await request.json();
// Validate request
if (!validateRequest(body)) {
return NextResponse.json(
{
success: false,
error: 'Invalid request. Required: packageName, name, params',
executionTimeMs: 0,
},
{ status: 400 }
);
}
// Execute the tool
const result = await executeTool(body);
return NextResponse.json(result, {
status: result.success ? 200 : 500,
});
} catch (error) {
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Internal server error',
executionTimeMs: 0,
},
{ status: 500 }
);
}
}
// Handle OPTIONS for CORS preflight
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}

View file

@ -1,40 +0,0 @@
/**
* Health Check Endpoint
*
* GET /api/health
* Returns the health status of the executor
*/
import { NextResponse } from 'next/server';
export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';
interface HealthResponse {
status: 'ok' | 'degraded' | 'error';
version?: string;
info?: Record<string, unknown>;
}
export async function GET(): Promise<NextResponse<HealthResponse>> {
return NextResponse.json({
status: 'ok',
version: '1.0.0',
info: {
runtime: 'vercel-serverless',
timestamp: new Date().toISOString(),
},
});
}
// Handle OPTIONS for CORS preflight
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}

View file

@ -1,11 +0,0 @@
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

View file

@ -1,20 +0,0 @@
export default function Home() {
return (
<div style={{ padding: '2rem', fontFamily: 'system-ui, sans-serif' }}>
<h1>TPMJS Executor</h1>
<p>This is a TPMJS tool executor service.</p>
<h2>Endpoints</h2>
<ul>
<li>
<code>GET /api/health</code> - Health check
</li>
<li>
<code>POST /api/execute-tool</code> - Execute a tool
</li>
</ul>
<p>
<a href="https://tpmjs.com/docs/executors">Documentation</a>
</p>
</div>
);
}