Workspace & AgentRouter
@moon-wave/workspace provides two utilities:
AgentRouterβ serve multiple agents from one Worker with a clean REST APIFileSystemβ R2-backed file storage for agents that need to read/write files
Install
npm install @moon-wave/workspaceAgentRouter
Route HTTP requests to different agents based on a name in the URL path. Useful when you have several agents that share the same Worker but serve different purposes.
Basic usage
import { Agent } from '@moon-wave/core';import { AgentRouter } from '@moon-wave/workspace';
export interface Env { GROQ_API_KEY: string;}
const supportAgent = new Agent({ name: 'support', model: { provider: 'groq', model: 'llama-3.3-70b-versatile' }, systemPrompt: 'You are a helpful customer support agent.', memory: 'none',});
const codingAgent = new Agent({ name: 'coding', model: { provider: 'groq', model: 'llama-3.3-70b-versatile' }, systemPrompt: 'You are a senior software engineer. Help with code reviews and debugging.', memory: 'none',});
const router = new AgentRouter();router.register('support', supportAgent, 'Customer support');router.register('coding', codingAgent, 'Software engineering help');
export default { async fetch(request: Request, env: Env): Promise<Response> { return router.handle(request, env as unknown as Record<string, unknown>); },};Routes
| Method | Path | Description |
|---|---|---|
GET | /agents | List all registered agents |
GET | /agents/:name | Get info about one agent |
POST | /agents/:name | Run an agent |
List agents
curl https://your-worker.workers.dev/agents[ { "name": "support", "description": "Customer support" }, { "name": "coding", "description": "Software engineering help" }]Run an agent
curl -X POST https://your-worker.workers.dev/agents/support \ -H "Content-Type: application/json" \ -d '{ "input": "How do I reset my password?" }'{ "output": "To reset your password, go to the login page and click...", "iterations": 1, "toolCalls": []}Pass sessionId to preserve conversation history across requests (requires memory configured):
curl -X POST .../agents/support \ -H "Content-Type: application/json" \ -d '{ "input": "follow up question", "sessionId": "user-123" }'Combine with dashboard
AgentRouter and @moon-wave/dashboard work well together:
import { AgentRouter } from '@moon-wave/workspace';import { createDashboard } from '@moon-wave/dashboard';
const router = new AgentRouter();router.register('support', supportAgent, 'Customer support');router.register('coding', codingAgent, 'Engineering help');
const dashboard = createDashboard({ agents: { support: supportAgent, coding: codingAgent },});
export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); if (url.pathname.startsWith('/dashboard')) { return dashboard.handle(request, env as unknown as Record<string, unknown>); } return router.handle(request, env as unknown as Record<string, unknown>); },};This gives you:
GET /agentsβ list all agentsPOST /agents/:nameβ call an agentGET /dashboardβ interactive playground UI
API reference
new AgentRouter()
Create a new router instance.
router.register(name, agent, description?)
Register an agent under a URL slug.
| Parameter | Type | Description |
|---|---|---|
name | string | URL-safe slug (e.g. "support") |
agent | Agent | Agent instance |
description | string (optional) | Shown in GET /agents |
router.handle(request, env)
Handle an incoming Request. Returns a Response. Mount in your Workerβs fetch handler.
FileSystem
R2-backed file storage. Useful for agents that need to persist or read files (documents, knowledge bases, generated outputs).
Setup
Add an R2 binding to your wrangler.toml:
[[r2_buckets]]binding = "BUCKET"bucket_name = "my-workspace"Add it to your Env type:
export interface Env { BUCKET: R2Bucket;}Usage
import { FileSystem } from '@moon-wave/workspace';
const fs = new FileSystem(env.BUCKET, 'workspace-id');
// Write a fileawait fs.write('notes.txt', 'hello world', 'text/plain');
// Read it backconst text = await fs.readText('notes.txt');
// List filesconst files = await fs.list();
// Search across all text filesconst results = await fs.grep('hello');// β [{ path: 'notes.txt', line: 'hello world', lineNumber: 1 }]
// Deleteawait fs.delete('notes.txt');API reference
const fs = new FileSystem(r2: R2Bucket, workspaceId: string)
fs.write(path, content, contentType?) β Promise<FileEntry>fs.read(path) β Promise<ArrayBuffer | null>fs.readText(path) β Promise<string | null>fs.list(prefix?) β Promise<FileEntry[]>fs.grep(query, filePrefix?) β Promise<SearchResult[]>fs.delete(path) β Promise<void>