Implements a complete dynamic tool loading system that allows the playground to discover and load tools from the TPMJS registry at runtime. **Architecture:** - Search tool package (@tpmjs/search-registry) - Searches registry for tools - Search API endpoint (/api/tools/search) - Text-based search with scoring - Pre-flight tool loading - Automatically searches and loads tools on every message - Railway executor service (Deno) - Loads tools from esm.sh via HTTP imports - Dynamic tool loader - Calls Railway to load and execute tools remotely **Key Components:** 1. Railway Executor (apps/railway-executor/) - Deno-based service that natively supports HTTP imports - Endpoints: /load-and-describe, /execute-tool, /cache/stats, /cache/clear - Deploys to Railway with deno run --allow-net --allow-env server.ts 2. Search Tool Package (packages/tools/search-registry/) - AI SDK v6 tool for searching TPMJS registry - Uses jsonSchema + inputSchema pattern - Searches /api/tools/search endpoint 3. Search API (apps/web/src/app/api/tools/search/) - Text-based search with composite scoring - Scores: text relevance + quality boost + download boost - Returns tool metadata with importUrl for dynamic loading 4. Dynamic Tool Loader (apps/playground/src/lib/dynamic-tool-loader.ts) - Calls Railway service to load tools from esm.sh - Creates tool wrappers that execute remotely - Process-level module cache + per-conversation tracking 5. Pre-flight Loading (apps/playground/src/app/api/chat/route.ts) - Automatically searches for tools on every user message - Loads top 5 matching tools before agent processes request - Merges with static tools for seamless experience **Technical Decisions:** - Deno over Node.js: Native HTTP import support without flags - Remote execution: Tools run in Railway sandbox, not Vercel - Pre-flight loading: Better UX than two-turn search pattern - Text search: BM25 had dependency issues, simple scoring works well **Environment Variables:** - RAILWAY_SERVICE_URL: https://endearing-commitment-production.up.railway.app 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
98 lines
2.9 KiB
Markdown
98 lines
2.9 KiB
Markdown
# Railway Executor - Deployment Status
|
|
|
|
## Issue Discovered
|
|
|
|
Node.js does not support HTTP(S) imports by default, even with `--experimental-network-imports` flag (that flag doesn't exist in current Node versions).
|
|
|
|
## Solutions Considered
|
|
|
|
1. **Custom ESM Loader** - Complex, requires Node.js 18.19+ with `--loader` flag
|
|
2. **fetch + eval** - Security concerns, doesn't handle ES modules properly
|
|
3. **Bundler approach** - Would defeat the purpose of dynamic imports
|
|
4. **Deno** - Supports HTTP imports natively, but different ecosystem
|
|
|
|
## Recommended Solution
|
|
|
|
Since the core issue is that we need truly dynamic runtime imports from HTTP URLs, and Node.js doesn't support this, we have **two viable paths**:
|
|
|
|
### Option A: Use Deno on Railway (RECOMMENDED)
|
|
|
|
Deno supports HTTP imports natively:
|
|
|
|
```typescript
|
|
// server.ts (Deno)
|
|
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
|
|
|
|
const moduleCache = new Map();
|
|
|
|
async function loadTool(url: string, exportName: string) {
|
|
if (moduleCache.has(url)) {
|
|
return moduleCache.get(url);
|
|
}
|
|
|
|
// Deno supports this natively!
|
|
const module = await import(url);
|
|
const tool = module[exportName];
|
|
moduleCache.set(url, tool);
|
|
return tool;
|
|
}
|
|
|
|
serve(async (req) => {
|
|
// ... handle requests
|
|
}, { port: 3002 });
|
|
```
|
|
|
|
**Deploy to Railway:**
|
|
```bash
|
|
# In Railway dashboard:
|
|
# - Set Start Command: deno run --allow-net --allow-env server.ts
|
|
# - Or use railway.json with deno runtime
|
|
```
|
|
|
|
### Option B: Pre-build Bundle Approach
|
|
|
|
Instead of truly dynamic imports, pre-fetch and cache tools:
|
|
|
|
1. Playground searches for tools
|
|
2. Backend fetches tool code once and caches it
|
|
3. Use `vm2` or similar to execute in sandbox
|
|
4. Not truly "dynamic" but works with Node.js
|
|
|
|
## Current Status
|
|
|
|
The Railway executor service is **created** but **not deployed** because Node.js doesn't support the required HTTP imports.
|
|
|
|
**Files created:**
|
|
- `apps/railway-executor/package.json`
|
|
- `apps/railway-executor/server.js` (incomplete - needs Deno or vm2 approach)
|
|
- `apps/railway-executor/README.md`
|
|
|
|
## Next Steps
|
|
|
|
**If using Deno (recommended):**
|
|
1. Rewrite server.js as server.ts for Deno
|
|
2. Deploy to Railway with Deno runtime
|
|
3. Test HTTP imports work
|
|
4. Update playground to use Railway URL
|
|
|
|
**If sticking with Node.js:**
|
|
1. Install `vm2` package for sandboxed execution
|
|
2. Implement fetch + vm2 approach
|
|
3. Deploy to Railway
|
|
4. Accept limitations (less dynamic, more complex)
|
|
|
|
## Alternative: Skip Railway, Use Different Architecture
|
|
|
|
Since the original issue is Next.js bundler limitations, consider:
|
|
|
|
**Web Workers in Browser** - Load tools client-side using native `import()`
|
|
- Pros: No server needed, truly dynamic
|
|
- Cons: Exposes API keys, security concerns
|
|
|
|
**Serverless Functions with Pre-installed Tools** - Deploy each tool as separate function
|
|
- Pros: Works with Vercel/Next.js
|
|
- Cons: Not truly dynamic, requires redeployment for new tools
|
|
|
|
---
|
|
|
|
**Recommendation**: Use Deno on Railway. It's designed for exactly this use case.
|