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

Function startServer

apps/local/src/serve.ts:298–492  ·  view source on GitHub ↗
(opts: StartServerOptions = {})

Source from the content-addressed store, hash-verified

296};
297
298export async function startServer(opts: StartServerOptions = {}): Promise<ServerInstance> {
299 const port = opts.port ?? parseInt(process.env.PORT ?? "4788", 10);
300 const hostname = opts.hostname ?? "127.0.0.1";
301 // ONE credential, always present: an explicit override or the stable token
302 // from auth.json (minted on first run). Auth is unconditionally on — loopback
303 // is no longer a free pass, since Executor runs arbitrary code that any local
304 // process could otherwise drive.
305 const authToken = normalizeCredential(opts.authToken) ?? loadOrMintLocalAuthToken();
306 const isAuthorized = makeIsAuthorized(authToken);
307 // CORS-only origin allowlist (no Host gate — the bearer is the boundary).
308 const corsAllowedHosts = new Set<string>([
309 ...DEFAULT_ALLOWED_HOSTS,
310 ...(opts.allowedHosts ?? []),
311 ]);
312 const clientDir = opts.clientDir ?? resolve(import.meta.dirname, "../dist");
313
314 startIntegrationsRefresh();
315
316 const ownsHandlers = opts.handlers === undefined;
317 const handlers = opts.handlers ?? (await getServerHandlers(authToken));
318 let viteChild: ViteChild | null = null;
319
320 const disposeOwnedResources = async (): Promise<void> => {
321 setOAuthCompletionListener(null);
322 if (ownsHandlers) {
323 await disposeServerHandlers();
324 } else {
325 await closeProvidedHandlers(handlers);
326 }
327 if (viteChild) await viteChild.stop();
328 };
329
330 // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: after handlers boot, failed static/dev/Bun startup must release DB ownership before surfacing the startup error
331 try {
332 // Mirror every OAuth callback completion into the local in-memory result
333 // store. The Electron desktop renderer polls /api/oauth/await/:sessionId
334 // for these when the user runs the flow in their system browser (no
335 // shared origin → no postMessage). Cloud doesn't register a listener;
336 // its same-origin web SPA receives results via postMessage directly.
337 setOAuthCompletionListener((result) => publishOAuthResult(result));
338
339 // Build static routes from either embedded assets, disk, or a spawned
340 // vite dev child (EXECUTOR_DEV=1). Vite mode takes precedence and
341 // disables the file-extension 404 short-circuit since vite serves
342 // hashed asset paths directly.
343 let staticRoutes: Record<string, StaticHandler> = {};
344 let serveIndex: StaticHandler;
345
346 const devMode = process.env.EXECUTOR_DEV === "1" && !opts.embeddedWebUI;
347 if (devMode) {
348 console.log("[executor] EXECUTOR_DEV=1 — spawning vite dev child for live UI");
349 viteChild = await startViteChild();
350 // Diagnostic only — this is the internal vite port the daemon proxies to.
351 // It must NOT read as a destination: the URL to open is the `Open:` line the
352 // CLI prints (the daemon port, with ?_token). Hitting the vite port directly
353 // skips that bootstrap and lands on the auth gate.
354 console.log(`[executor] (internal) vite dev child at ${viteChild.url} — don't open this`);
355 serveIndex = () =>

Callers 5

server.tsFile · 0.90
startTestServerFunction · 0.90
serve.test.tsFile · 0.90
serve.tsFile · 0.70

Calls 14

normalizeCredentialFunction · 0.90
loadOrMintLocalAuthTokenFunction · 0.90
makeIsAuthorizedFunction · 0.90
startIntegrationsRefreshFunction · 0.90
getServerHandlersFunction · 0.90
publishOAuthResultFunction · 0.90
startViteChildFunction · 0.85
embeddedToStaticRoutesFunction · 0.85
htmlResponseFunction · 0.85
collectStaticRoutesFunction · 0.85
ignoreCleanupFailureFunction · 0.85

Tested by 1

startTestServerFunction · 0.72