(token: string)
| 68 | }; |
| 69 | |
| 70 | export const createServerHandlers = async (token: string): Promise<ServerHandlers> => { |
| 71 | let apiHandler: ServerHandlers["api"] | null = null; |
| 72 | let mcp: McpRequestHandler | null = null; |
| 73 | |
| 74 | // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: handler boot owns the shared executor; partial startup failure must release it before rethrowing |
| 75 | try { |
| 76 | // The typed `/api` web-handler comes from `ExecutorApp.make` (./app.ts). The |
| 77 | // boot bearer token is the authoritative `/api` gate (see `identity.ts`). |
| 78 | apiHandler = await makeLocalApiHandler(token); |
| 79 | |
| 80 | // The in-process MCP server runs over the SAME boot executor, with its own |
| 81 | // engine instance (the browser-approval + stdio surface is local-only and not |
| 82 | // part of the shared API). Reuse the shared boot bundle so the MCP executor is |
| 83 | // byte-identical to the one the API serves. |
| 84 | const { executor } = await getExecutorBundle(); |
| 85 | const engine = createExecutionEngine({ |
| 86 | executor, |
| 87 | codeExecutor: makeQuickJsExecutor(), |
| 88 | }); |
| 89 | mcp = createMcpRequestHandler({ |
| 90 | defaultConfig: { engine }, |
| 91 | createConfigForResource: async (resource) => { |
| 92 | if (resource.kind === "default") return { config: { engine } }; |
| 93 | const handle = await createExecutorHandle({ |
| 94 | activeToolkitSlug: resource.slug, |
| 95 | }); |
| 96 | const toolkitEngine = createExecutionEngine({ |
| 97 | executor: handle.executor, |
| 98 | codeExecutor: makeQuickJsExecutor(), |
| 99 | }); |
| 100 | return { |
| 101 | config: { engine: toolkitEngine }, |
| 102 | close: handle.dispose, |
| 103 | }; |
| 104 | }, |
| 105 | }); |
| 106 | |
| 107 | return { api: apiHandler, mcp }; |
| 108 | } catch (cause) { |
| 109 | const partialApiHandler = apiHandler; |
| 110 | const partialMcp = mcp; |
| 111 | await Effect.runPromise( |
| 112 | Effect.gen(function* () { |
| 113 | yield* Effect.all( |
| 114 | [ |
| 115 | partialApiHandler |
| 116 | ? ignoreDisposeFailure("api.dispose", () => partialApiHandler.dispose()) |
| 117 | : Effect.void, |
| 118 | partialMcp ? ignoreDisposeFailure("mcp.close", () => partialMcp.close()) : Effect.void, |
| 119 | ], |
| 120 | { concurrency: "unbounded" }, |
| 121 | ); |
| 122 | yield* ignoreDisposeFailure("disposeExecutor", () => disposeExecutor()); |
| 123 | }), |
| 124 | ); |
| 125 | // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: async factory must reject with the original handler boot failure after cleanup |
| 126 | throw cause; |
| 127 | } |
no test coverage detected