MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / executorApiPlugin

Function executorApiPlugin

apps/local/vite.config.ts:80–223  ·  view source on GitHub ↗

* Vite plugin that forwards /api and /mcp requests to the Effect handlers * during development, so you don't need a separate server process.

()

Source from the content-addressed store, hash-verified

78 * during development, so you don't need a separate server process.
79 */
80function executorApiPlugin(): Plugin {
81 let handlers: import("./src/main").ServerHandlers | null = null;
82 // The dev server's in-process /api + /mcp require the bearer (same gate as the
83 // production Bun shell). The browser gets the token the SAME way it does in
84 // production — via the one-time `?_token=` URL printed below — so there is no
85 // dev-only token-injection path to drift.
86 let devToken: string | null = null;
87
88 return {
89 name: "executor-api",
90 configureServer(server) {
91 devToken ??= loadOrMintLocalAuthToken();
92
93 // Print the bootstrap URL when vite is the front (plain `bun run dev`).
94 // When the CLI daemon spawns vite as a child (EXECUTOR_DEV_VITE_PORT set),
95 // the daemon prints its own `?_token=` URL and the app is loaded from the
96 // daemon port, so we stay quiet to avoid two conflicting URLs.
97 if (!process.env.EXECUTOR_DEV_VITE_PORT) {
98 server.httpServer?.once("listening", () => {
99 const address = server.httpServer?.address();
100 const port =
101 typeof address === "object" && address ? address.port : server.config.server.port;
102 server.config.logger.info(
103 `\n Open with auth: http://127.0.0.1:${port}/?_token=${devToken}\n`,
104 );
105 });
106 }
107
108 server.watcher.on("change", (path) => {
109 if (path.includes("/apps/local/src/") || path.endsWith("/executor.config.ts")) {
110 handlers = null;
111 }
112 });
113 server.middlewares.use(async (req, res, next) => {
114 const rawUrl = req.url ?? "/";
115 const pathOnly = rawUrl.split("?")[0] ?? "/";
116 const isApi = pathOnly.startsWith("/api/") || pathOnly === "/api";
117 const isMcp = pathOnly === "/mcp" || pathOnly.startsWith("/mcp/");
118 // App-level routes the Effect app serves at root, outside the `/api`
119 // prefix (e.g. `/v1/app/npm/dist-tags`, the update-check the web shell
120 // fetches). Public, like `/api/health` below.
121 const isV1 = pathOnly === "/v1" || pathOnly.startsWith("/v1/");
122
123 if (!isApi && !isMcp && !isV1) return next();
124
125 // Gate parity with the production Bun shell (serve.ts): the vite server
126 // is reachable by any local process, so /api and /mcp require the bearer
127 // here too — otherwise /mcp would be unauthenticated arbitrary code
128 // execution in dev. Exempt the health probe and the state-gated OAuth
129 // callback. The SPA carries the token from its `?_token`/localStorage
130 // bootstrap, so the UI is unaffected; external MCP clients use the
131 // daemon port.
132 const authExempt =
133 pathOnly === "/api/health" || isV1 || isUnauthenticatedOAuthPath(pathOnly);
134 if (!authExempt) {
135 const presented = req.headers.authorization;
136 const authValue = Array.isArray(presented) ? presented[0] : presented;
137 const probe = new Request(

Callers 1

vite.config.tsFile · 0.70

Calls

no outgoing calls

Tested by

no test coverage detected