(token: string)
| 73 | }; |
| 74 | |
| 75 | export const createServerHandlers = async (token: string): Promise<ServerHandlers> => { |
| 76 | let apiHandler: ServerHandlers["api"] | null = null; |
| 77 | let mcp: McpRequestHandler | null = null; |
| 78 | |
| 79 | // 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 |
| 80 | try { |
| 81 | // The typed `/api` web-handler comes from `ExecutorApp.make` (./app.ts). The |
| 82 | // boot bearer token is the authoritative `/api` gate (see `identity.ts`). |
| 83 | apiHandler = await makeLocalApiHandler(token); |
| 84 | |
| 85 | // The in-process MCP server runs over the SAME boot executor, with its own |
| 86 | // engine instance (the browser-approval + stdio surface is local-only and not |
| 87 | // part of the shared API). Reuse the shared boot bundle so the MCP executor is |
| 88 | // byte-identical to the one the API serves. |
| 89 | const { executor, webBaseUrl } = await getExecutorBundle(); |
| 90 | // Both engines below serve MCP endpoints, so the wrap binds the "mcp" |
| 91 | // plane structurally; the toolkit-scoped engine additionally marks |
| 92 | // `toolkit` (the slug itself is a user label and never recorded). |
| 93 | const engine = withExecutionAnalytics( |
| 94 | createExecutionEngine({ |
| 95 | executor, |
| 96 | codeExecutor: makeQuickJsExecutor(), |
| 97 | }), |
| 98 | localAnalytics, |
| 99 | { plane: "mcp", toolkit: false }, |
| 100 | ); |
| 101 | // The generative-UI surface, shared by every resource this daemon serves. |
| 102 | // Each toolkit gets its own executor, so `artifacts` is bound per resource |
| 103 | // below rather than hoisted with the rest. |
| 104 | // |
| 105 | // Including the create-time smoke render, on the same terms as every other |
| 106 | // host. This daemon is a dependency of `apps/cli`, whose build does not |
| 107 | // configure `jsx`, and the renderer used to be unusable here for that |
| 108 | // reason — TypeScript resolved its dynamic `import()` of the `.tsx` |
| 109 | // component barrel eagerly and dragged the whole React graph (plus a |
| 110 | // duplicate `@types/react`) into the CLI and desktop trees. It renders |
| 111 | // inside a QuickJS sandbox now, so there is no `.tsx` in its graph and no |
| 112 | // React in this process; the cost is one lazily-loaded string constant. |
| 113 | const appsConfig = { |
| 114 | loadAppShellHtml: loadMcpAppsShellHtml, |
| 115 | smokeRenderArtifact, |
| 116 | artifactUrl: artifactUrlFor(webBaseUrl), |
| 117 | // Artifact operations on this surface come from an agent's MCP tools. |
| 118 | onArtifactUsage: (action: "created" | "viewed" | "updated") => |
| 119 | localAnalytics.record(`artifact_${action}`, { via: "agent" }), |
| 120 | }; |
| 121 | mcp = createMcpRequestHandler({ |
| 122 | defaultConfig: { |
| 123 | engine, |
| 124 | artifacts: executor.artifacts, |
| 125 | connections: executor.connections, |
| 126 | ...appsConfig, |
| 127 | }, |
| 128 | createConfigForResource: async (resource) => { |
| 129 | if (resource.kind === "default") { |
| 130 | return { |
| 131 | config: { |
| 132 | engine, |
no test coverage detected