(request: Request, env: TEnv)
| 113 | ): ExportedHandler<TEnv> { |
| 114 | return { |
| 115 | async fetch(request: Request, env: TEnv): Promise<Response> { |
| 116 | // Preview-port traffic for exposed sandbox ports is routed by hostname; let |
| 117 | // the sandbox runtime claim those requests before our app routes run. Only |
| 118 | // possible when the env actually carries the Sandbox binding. |
| 119 | if (hasSandboxBinding(env)) { |
| 120 | const proxied = await proxyToSandbox(request, env) |
| 121 | if (proxied) return proxied |
| 122 | } |
| 123 | |
| 124 | const url = new URL(request.url) |
| 125 | const parts = url.pathname.split('/').filter(Boolean) |
| 126 | |
| 127 | // POST /runs — trigger a run, return 202 immediately. |
| 128 | if ( |
| 129 | request.method === 'POST' && |
| 130 | parts.length === 1 && |
| 131 | parts[0] === 'runs' |
| 132 | ) { |
| 133 | let body: CreateRunBody |
| 134 | try { |
| 135 | body = parseCreateRunBody(await request.json()) |
| 136 | } catch (error) { |
| 137 | const message = error instanceof Error ? error.message : String(error) |
| 138 | return jsonResponse({ error: message }, 400) |
| 139 | } |
| 140 | const runId = crypto.randomUUID() |
| 141 | const input: StartRunInput = { |
| 142 | runId, |
| 143 | threadId: body.threadId, |
| 144 | messages: body.messages, |
| 145 | // The host this request arrived on. Coordinators derive the container's |
| 146 | // callback hosts from it when `PUBLIC_HOSTNAME`/`PREVIEW_HOSTNAME` are |
| 147 | // unset. On Cloudflare this is safe to trust — the edge only routes |
| 148 | // hostnames you own to your Worker. See `resolveBridgeOrigin` / |
| 149 | // `resolvePreviewHost`. |
| 150 | publicHost: url.host, |
| 151 | // Forwarded verbatim to the app's resolvers (e.g. the chosen harness). |
| 152 | metadata: body.metadata, |
| 153 | } |
| 154 | // RPC into the coordinator. `startRun` registers the run and returns |
| 155 | // immediately under `ctx.waitUntil`; we do NOT await the agent loop. |
| 156 | await resolveCoordinator(env, body.threadId).startRun(input) |
| 157 | return jsonResponse({ runId }, 202) |
| 158 | } |
| 159 | |
| 160 | // Everything else for a run needs the owning coordinator, addressed by the |
| 161 | // `threadId` query the Worker carries so it never reads run state itself. |
| 162 | // The base coordinator routes `/runs/:id` + `/runs/:id/stream`, and a |
| 163 | // subclass routes `/_bridge/:runId` (DO-drives) or `/tool-exec/:runId` |
| 164 | // (co-located) — all reachable through one forward. |
| 165 | const threadId = url.searchParams.get('threadId') |
| 166 | if (threadId !== null) { |
| 167 | return resolveCoordinator(env, threadId).fetch(request) |
| 168 | } |
| 169 | |
| 170 | return jsonResponse({ error: 'threadId query param required' }, 400) |
| 171 | }, |
| 172 | } |
no test coverage detected