feat(executors): add Railway executor template and documentation
- Add Railway executor template with one-click deploy support - Create Railway documentation page at /docs/executors/railway - Update main executors page with Railway as official recommendation - Add Railway to platform comparison table with new columns - Update inter-page navigation for Railway → Unsandbox → Vercel flow - Update FAQ to recommend Railway for most use cases Railway executor features: - Zero-dependency Node.js HTTP server - Docker support via included Dockerfile - Health check endpoint at /health - Tool execution at /execute-tool - API key authentication support - Auto-restart on failure via railway.json
This commit is contained in:
parent
3f62228c56
commit
ffc6ddcdbb
9 changed files with 1251 additions and 19 deletions
4
templates/railway-executor/.gitignore
vendored
Normal file
4
templates/railway-executor/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
node_modules/
|
||||
.env
|
||||
.env.local
|
||||
*.log
|
||||
22
templates/railway-executor/Dockerfile
Normal file
22
templates/railway-executor/Dockerfile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
FROM node:20-slim
|
||||
|
||||
# Install npm for tool installation during execution
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy executor files
|
||||
COPY package.json index.js ./
|
||||
|
||||
# Create temp directory for tool executions
|
||||
RUN mkdir -p /tmp
|
||||
|
||||
# Set environment
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "index.js"]
|
||||
260
templates/railway-executor/README.md
Normal file
260
templates/railway-executor/README.md
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
# TPMJS Executor for Railway
|
||||
|
||||
Deploy your own TPMJS tool executor on **Railway** for reliable, always-on execution.
|
||||
|
||||
## Features
|
||||
|
||||
- **One-Click Deploy**: Deploy to Railway in seconds
|
||||
- **Always-On**: No cold starts, instant tool execution
|
||||
- **Full Control**: Your infrastructure, your environment variables
|
||||
- **Privacy**: No data passes through TPMJS servers
|
||||
- **Auto-Scaling**: Railway handles scaling automatically
|
||||
- **Free Tier**: $5/month free credit included
|
||||
|
||||
## One-Click Deploy
|
||||
|
||||
[](https://railway.app/template/tpmjs-executor?referralCode=tpmjs)
|
||||
|
||||
Or deploy manually:
|
||||
|
||||
```bash
|
||||
# Clone this template
|
||||
git clone https://github.com/tpmjs/tpmjs.git
|
||||
cd tpmjs/templates/railway-executor
|
||||
|
||||
# Create a new Railway project
|
||||
railway init
|
||||
|
||||
# Deploy
|
||||
railway up
|
||||
```
|
||||
|
||||
Your executor will be available at: `https://your-project.up.railway.app`
|
||||
|
||||
## How It Works
|
||||
|
||||
This executor runs as an always-on Node.js service on Railway:
|
||||
|
||||
1. Receives tool execution requests via HTTP
|
||||
2. Installs the npm package in an isolated directory
|
||||
3. Executes the tool with your parameters
|
||||
4. Returns the result and cleans up
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### GET /health
|
||||
|
||||
Check executor health status.
|
||||
|
||||
```bash
|
||||
curl https://your-executor.up.railway.app/health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "1.0.0",
|
||||
"info": {
|
||||
"runtime": "railway",
|
||||
"timestamp": "2024-01-01T00:00:00.000Z",
|
||||
"region": "us-west1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /execute-tool
|
||||
|
||||
Execute a TPMJS tool.
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-executor.up.railway.app/execute-tool \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-d '{
|
||||
"packageName": "@tpmjs/hello",
|
||||
"name": "helloWorldTool",
|
||||
"version": "latest",
|
||||
"params": { "includeTimestamp": true }
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"output": {
|
||||
"message": "Hello, World!",
|
||||
"timestamp": "2024-01-01T00:00:00.000Z"
|
||||
},
|
||||
"executionTimeMs": 2345
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `EXECUTOR_API_KEY` | No | API key for authentication. If set, requests must include `Authorization: Bearer <key>` header. |
|
||||
|
||||
### Setting Up API Key Authentication
|
||||
|
||||
1. Go to your Railway project dashboard
|
||||
2. Click on your service
|
||||
3. Go to "Variables" tab
|
||||
4. Add `EXECUTOR_API_KEY` with a secure random value
|
||||
5. The service will automatically redeploy
|
||||
|
||||
### Adding Tool Environment Variables
|
||||
|
||||
Pass environment variables that your tools need:
|
||||
|
||||
1. In Railway dashboard, go to "Variables"
|
||||
2. Add your variables (e.g., `OPENAI_API_KEY`, `DATABASE_URL`)
|
||||
3. These will be available during tool execution
|
||||
|
||||
Or use the Railway CLI:
|
||||
|
||||
```bash
|
||||
railway variables set EXECUTOR_API_KEY=your-key
|
||||
railway variables set OPENAI_API_KEY=sk-xxx
|
||||
railway variables set DATABASE_URL=postgres://...
|
||||
```
|
||||
|
||||
## Connecting to TPMJS
|
||||
|
||||
1. Go to your TPMJS collection or agent settings
|
||||
2. In "Executor Configuration", select "Custom Executor"
|
||||
3. Enter your executor URL: `https://your-project.up.railway.app`
|
||||
4. Enter your API key (if configured)
|
||||
5. Click "Verify Connection" to test
|
||||
|
||||
## Local Development
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/tpmjs/tpmjs.git
|
||||
cd tpmjs/templates/railway-executor
|
||||
|
||||
# Run locally
|
||||
PORT=3000 node index.js
|
||||
|
||||
# Or with an API key
|
||||
EXECUTOR_API_KEY=test-key PORT=3000 node index.js
|
||||
|
||||
# Test health endpoint
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# Test tool execution
|
||||
curl -X POST http://localhost:3000/execute-tool \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"packageName": "@tpmjs/hello",
|
||||
"name": "helloWorldTool",
|
||||
"params": {}
|
||||
}'
|
||||
```
|
||||
|
||||
## Managing Your Service
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
railway logs
|
||||
```
|
||||
|
||||
Or view in the Railway dashboard under "Deployments" → select deployment → "Logs"
|
||||
|
||||
### Redeploy
|
||||
|
||||
```bash
|
||||
railway up
|
||||
```
|
||||
|
||||
Or push to your connected GitHub repository for automatic deployments.
|
||||
|
||||
### Scale Resources
|
||||
|
||||
1. Go to Railway dashboard
|
||||
2. Click on your service
|
||||
3. Go to "Settings" tab
|
||||
4. Adjust CPU and memory limits
|
||||
|
||||
### Custom Domains
|
||||
|
||||
1. Go to Railway dashboard
|
||||
2. Click on your service
|
||||
3. Go to "Settings" tab
|
||||
4. Under "Domains", click "Generate Domain" or add a custom domain
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
If you prefer Docker:
|
||||
|
||||
```bash
|
||||
# Build the image
|
||||
docker build -t tpmjs-executor .
|
||||
|
||||
# Run locally
|
||||
docker run -p 3000:3000 -e EXECUTOR_API_KEY=your-key tpmjs-executor
|
||||
```
|
||||
|
||||
Railway will automatically detect and use the Dockerfile if present.
|
||||
|
||||
## Security
|
||||
|
||||
- Set `EXECUTOR_API_KEY` to require authentication for all requests
|
||||
- Tools run in isolated temporary directories
|
||||
- Each execution uses a fresh npm install
|
||||
- Environment variables are stored encrypted by Railway
|
||||
- Network traffic is encrypted via HTTPS
|
||||
|
||||
## Pricing
|
||||
|
||||
Railway pricing is usage-based with a generous free tier:
|
||||
|
||||
- **Free Tier**: $5/month credit (enough for light usage)
|
||||
- **Pay-as-you-go**: ~$0.000463/min for 0.5 vCPU, 512MB RAM
|
||||
|
||||
See [Railway Pricing](https://railway.app/pricing) for current rates.
|
||||
|
||||
**Cost Optimization Tips:**
|
||||
- Use the "Sleep" feature for dev environments
|
||||
- Set memory limits appropriate for your tools
|
||||
- Monitor usage in Railway dashboard
|
||||
|
||||
## Comparison: Railway vs Other Platforms
|
||||
|
||||
| Feature | Railway | Vercel | Unsandbox |
|
||||
|---------|---------|--------|-----------|
|
||||
| Deploy method | One-click / CLI | One-click | CLI |
|
||||
| Cold starts | None (always-on) | Yes (serverless) | None |
|
||||
| Max runtime | Unlimited | 45min / 5hr | Unlimited |
|
||||
| Free tier | $5/month credit | Limited | None |
|
||||
| Pricing | Per usage | Per compute | Per uptime |
|
||||
| Docker support | Yes | No | Yes |
|
||||
| Auto-scaling | Yes | Yes | Manual |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Connection refused" errors
|
||||
- Check that your service is running in Railway dashboard
|
||||
- Verify the URL is correct (check "Domains" in settings)
|
||||
- Ensure `EXECUTOR_API_KEY` matches if authentication is enabled
|
||||
|
||||
### Tool installation failures
|
||||
- Check Railway logs for npm errors
|
||||
- Verify the package name and version are correct
|
||||
- Some packages may need additional system dependencies
|
||||
|
||||
### Timeout errors
|
||||
- Railway has no timeout limit, but individual tool executions timeout at 2 minutes
|
||||
- For longer-running tools, consider increasing the timeout in the executor code
|
||||
|
||||
## Support
|
||||
|
||||
- [TPMJS Custom Executors Documentation](https://tpmjs.com/docs/executors)
|
||||
- [Railway Documentation](https://docs.railway.app)
|
||||
- [GitHub Issues](https://github.com/tpmjs/tpmjs/issues)
|
||||
398
templates/railway-executor/index.js
Normal file
398
templates/railway-executor/index.js
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* TPMJS Executor for Railway
|
||||
*
|
||||
* A lightweight HTTP server that executes TPMJS tools.
|
||||
* Designed for deployment on Railway with zero dependencies.
|
||||
*
|
||||
* API-compatible with the Vercel and Unsandbox executors.
|
||||
*/
|
||||
|
||||
const http = require('node:http');
|
||||
const { execSync, spawn } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
const API_KEY = process.env.EXECUTOR_API_KEY || null;
|
||||
|
||||
// CORS headers for cross-origin requests
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
};
|
||||
|
||||
/**
|
||||
* Send JSON response with proper headers
|
||||
*/
|
||||
function jsonResponse(res, statusCode, data) {
|
||||
res.writeHead(statusCode, {
|
||||
'Content-Type': 'application/json',
|
||||
...corsHeaders,
|
||||
});
|
||||
res.end(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate API key if configured
|
||||
*/
|
||||
function checkAuth(req) {
|
||||
if (!API_KEY) return true;
|
||||
const authHeader = req.headers.authorization;
|
||||
return authHeader === `Bearer ${API_KEY}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON request body
|
||||
*/
|
||||
function parseBody(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let body = '';
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk;
|
||||
});
|
||||
req.on('end', () => {
|
||||
try {
|
||||
resolve(body ? JSON.parse(body) : {});
|
||||
} catch (_e) {
|
||||
reject(new Error('Invalid JSON'));
|
||||
}
|
||||
});
|
||||
req.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /health - Health check endpoint
|
||||
*/
|
||||
function handleHealth(_req, res) {
|
||||
jsonResponse(res, 200, {
|
||||
status: 'ok',
|
||||
version: '1.0.0',
|
||||
info: {
|
||||
runtime: 'railway',
|
||||
timestamp: new Date().toISOString(),
|
||||
region: process.env.RAILWAY_REGION || 'unknown',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create isolated work directory and package.json
|
||||
*/
|
||||
function createWorkDir() {
|
||||
const workDir = `/tmp/tpmjs-exec-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
fs.mkdirSync(workDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(workDir, 'package.json'),
|
||||
JSON.stringify({
|
||||
name: 'tpmjs-execution',
|
||||
private: true,
|
||||
type: 'commonjs',
|
||||
})
|
||||
);
|
||||
return workDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install npm package in work directory
|
||||
*/
|
||||
function installPackage(workDir, packageSpec) {
|
||||
execSync(`npm install --no-save --omit=dev --no-audit --no-fund ${packageSpec}`, {
|
||||
cwd: workDir,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 60000, // 60s timeout for install
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the tool execution script
|
||||
*/
|
||||
function generateExecutionScript(packageName, name, params, env) {
|
||||
const envSetup = env
|
||||
? Object.entries(env)
|
||||
.map(([key, value]) => `process.env[${JSON.stringify(key)}] = ${JSON.stringify(value)};`)
|
||||
.join('\n')
|
||||
: '';
|
||||
|
||||
return `
|
||||
${envSetup}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const pkg = require(${JSON.stringify(packageName)});
|
||||
|
||||
// Find the tool export - check named export, default.name, or default
|
||||
let tool = pkg[${JSON.stringify(name)}] || pkg.default?.[${JSON.stringify(name)}] || pkg.default;
|
||||
|
||||
if (!tool) {
|
||||
throw new Error(\`Tool "${name}" not found in package "${packageName}"\`);
|
||||
}
|
||||
|
||||
// Handle factory functions (tools that need to be instantiated)
|
||||
if (typeof tool === 'function' && !tool.execute) {
|
||||
const envVars = ${env ? JSON.stringify(env) : 'null'};
|
||||
|
||||
// Try no-arg call first
|
||||
try {
|
||||
const result = tool();
|
||||
if (result && typeof result.execute === 'function') {
|
||||
tool = result;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// Try with env config if still a function
|
||||
if (typeof tool === 'function' && envVars) {
|
||||
try {
|
||||
const result = tool(envVars);
|
||||
if (result && typeof result.execute === 'function') {
|
||||
tool = result;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tool || typeof tool.execute !== 'function') {
|
||||
throw new Error(\`Tool "${name}" does not have an execute() function\`);
|
||||
}
|
||||
|
||||
// Execute the tool
|
||||
const result = await tool.execute(${JSON.stringify(params)});
|
||||
process.stdout.write(JSON.stringify({ __tpmjs_result__: result }));
|
||||
} catch (err) {
|
||||
process.stderr.write(JSON.stringify({ __tpmjs_error__: err.message || String(err) }));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
})();
|
||||
`.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run execution script and return results
|
||||
*/
|
||||
function runScript(workDir, env) {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn('node', ['execute.cjs'], {
|
||||
cwd: workDir,
|
||||
env: { ...process.env, ...env },
|
||||
timeout: 120000, // 2 minute timeout
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
child.stdout.on('data', (data) => {
|
||||
stdout += data;
|
||||
});
|
||||
child.stderr.on('data', (data) => {
|
||||
stderr += data;
|
||||
});
|
||||
|
||||
child.on('close', (code) => {
|
||||
resolve({ exitCode: code, stdout, stderr });
|
||||
});
|
||||
|
||||
child.on('error', (err) => {
|
||||
resolve({ exitCode: 1, stdout: '', stderr: err.message });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up work directory
|
||||
*/
|
||||
function cleanup(workDir) {
|
||||
try {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
} catch (_e) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse execution result and determine response
|
||||
*/
|
||||
function parseExecutionResult(result, startTime) {
|
||||
// Handle execution failure
|
||||
if (result.exitCode !== 0) {
|
||||
// Try to parse structured error from stderr
|
||||
try {
|
||||
const errorObj = JSON.parse(result.stderr);
|
||||
if (errorObj.__tpmjs_error__) {
|
||||
return {
|
||||
success: false,
|
||||
error: errorObj.__tpmjs_error__,
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
};
|
||||
}
|
||||
} catch (_e) {
|
||||
// Not structured error
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: result.stderr || `Script exited with code ${result.exitCode}`,
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
};
|
||||
}
|
||||
|
||||
// Parse the result
|
||||
try {
|
||||
const parsed = JSON.parse(result.stdout);
|
||||
if (parsed.__tpmjs_result__ !== undefined) {
|
||||
return {
|
||||
success: true,
|
||||
output: parsed.__tpmjs_result__,
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
};
|
||||
}
|
||||
} catch (_e) {
|
||||
// Not structured result
|
||||
}
|
||||
|
||||
// Return raw output
|
||||
return {
|
||||
success: true,
|
||||
output: result.stdout || null,
|
||||
stderr: result.stderr || undefined,
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /execute-tool - Execute a TPMJS tool
|
||||
*/
|
||||
async function handleExecuteTool(req, res) {
|
||||
const startTime = Date.now();
|
||||
|
||||
// Check authorization
|
||||
if (!checkAuth(req)) {
|
||||
return jsonResponse(res, 401, {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
|
||||
// Parse request body
|
||||
let body;
|
||||
try {
|
||||
body = await parseBody(req);
|
||||
} catch (_e) {
|
||||
return jsonResponse(res, 400, {
|
||||
success: false,
|
||||
error: 'Invalid JSON body',
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
|
||||
const { packageName, name, version = 'latest', params = {}, env } = body;
|
||||
|
||||
// Validate required fields
|
||||
if (!packageName || !name) {
|
||||
return jsonResponse(res, 400, {
|
||||
success: false,
|
||||
error: 'Missing required fields: packageName, name',
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
|
||||
const packageSpec = `${packageName}@${version}`;
|
||||
const workDir = createWorkDir();
|
||||
|
||||
try {
|
||||
// Install the npm package
|
||||
console.log(`[executor] Installing ${packageSpec}...`);
|
||||
const installStart = Date.now();
|
||||
|
||||
try {
|
||||
installPackage(workDir, packageSpec);
|
||||
} catch (installError) {
|
||||
console.error(`[executor] npm install failed:`, installError.message);
|
||||
cleanup(workDir);
|
||||
return jsonResponse(res, 500, {
|
||||
success: false,
|
||||
error: `npm install failed: ${installError.message}`,
|
||||
stderr: installError.stderr?.toString(),
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[executor] npm install completed in ${Date.now() - installStart}ms`);
|
||||
|
||||
// Generate and write execution script
|
||||
const script = generateExecutionScript(packageName, name, params, env);
|
||||
fs.writeFileSync(path.join(workDir, 'execute.cjs'), script);
|
||||
|
||||
// Run the execution script
|
||||
console.log(`[executor] Running tool ${packageName}/${name}...`);
|
||||
const runStart = Date.now();
|
||||
const result = await runScript(workDir, env);
|
||||
console.log(
|
||||
`[executor] Tool execution completed in ${Date.now() - runStart}ms (exit: ${result.exitCode})`
|
||||
);
|
||||
|
||||
// Cleanup and return result
|
||||
cleanup(workDir);
|
||||
return jsonResponse(res, 200, parseExecutionResult(result, startTime));
|
||||
} catch (error) {
|
||||
cleanup(workDir);
|
||||
return jsonResponse(res, 500, {
|
||||
success: false,
|
||||
error: error.message || String(error),
|
||||
executionTimeMs: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main HTTP server
|
||||
*/
|
||||
const server = http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url, `http://localhost:${PORT}`);
|
||||
const pathname = url.pathname;
|
||||
|
||||
// Handle CORS preflight
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(200, corsHeaders);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
// Route requests (support both /api/path and /path)
|
||||
if ((pathname === '/api/health' || pathname === '/health') && req.method === 'GET') {
|
||||
return handleHealth(req, res);
|
||||
}
|
||||
|
||||
if ((pathname === '/api/execute-tool' || pathname === '/execute-tool') && req.method === 'POST') {
|
||||
return handleExecuteTool(req, res);
|
||||
}
|
||||
|
||||
// Root path - simple info
|
||||
if (pathname === '/' && req.method === 'GET') {
|
||||
return jsonResponse(res, 200, {
|
||||
name: 'TPMJS Executor',
|
||||
version: '1.0.0',
|
||||
runtime: 'railway',
|
||||
endpoints: {
|
||||
health: 'GET /health',
|
||||
execute: 'POST /execute-tool',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 404 for unknown routes
|
||||
jsonResponse(res, 404, { error: 'Not found' });
|
||||
});
|
||||
|
||||
// Start server
|
||||
server.listen(PORT, () => {
|
||||
console.log(`TPMJS Executor running on port ${PORT}`);
|
||||
console.log(`Health: http://localhost:${PORT}/health`);
|
||||
console.log(`Execute: POST http://localhost:${PORT}/execute-tool`);
|
||||
if (API_KEY) {
|
||||
console.log(`Authentication: Required (EXECUTOR_API_KEY is set)`);
|
||||
} else {
|
||||
console.log(`Authentication: None (set EXECUTOR_API_KEY to enable)`);
|
||||
}
|
||||
});
|
||||
20
templates/railway-executor/package.json
Normal file
20
templates/railway-executor/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "tpmjs-executor",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "TPMJS Tool Executor for Railway - Deploy your own executor on Railway",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"dev": "node index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"keywords": [
|
||||
"tpmjs",
|
||||
"executor",
|
||||
"railway"
|
||||
]
|
||||
}
|
||||
13
templates/railway-executor/railway.json
Normal file
13
templates/railway-executor/railway.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"$schema": "https://railway.app/railway.schema.json",
|
||||
"build": {
|
||||
"builder": "NIXPACKS"
|
||||
},
|
||||
"deploy": {
|
||||
"startCommand": "node index.js",
|
||||
"healthcheckPath": "/health",
|
||||
"healthcheckTimeout": 30,
|
||||
"restartPolicyType": "ON_FAILURE",
|
||||
"restartPolicyMaxRetries": 3
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue