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

Function startServer

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

Source from the content-addressed store, hash-verified

325};
326
327export async function startServer(opts: StartServerOptions = {}): Promise<ServerInstance> {
328 const port = opts.port ?? parseInt(process.env.PORT ?? "4788", 10);
329 const hostname = opts.hostname ?? "127.0.0.1";
330 // ONE credential, always present: an explicit override or the stable token
331 // from auth.json (minted on first run). Auth is unconditionally on — loopback
332 // is no longer a free pass, since Executor runs arbitrary code that any local
333 // process could otherwise drive.
334 const authToken = normalizeCredential(opts.authToken) ?? loadOrMintLocalAuthToken();
335 const isAuthorized = makeIsAuthorized(authToken);
336 // CORS-only origin allowlist (no Host gate — the bearer is the boundary).
337 const corsAllowedHosts = new Set<string>([
338 ...DEFAULT_ALLOWED_HOSTS,
339 ...(opts.allowedHosts ?? []),
340 ]);
341 const clientDir = opts.clientDir ?? resolve(import.meta.dirname, "../dist");
342
343 startIntegrationsRefresh();
344
345 const ownsHandlers = opts.handlers === undefined;
346 const handlers = opts.handlers ?? (await getServerHandlers(authToken));
347 let viteChild: ViteChild | null = null;
348
349 const disposeOwnedResources = async (): Promise<void> => {
350 setOAuthCompletionListener(null);
351 if (ownsHandlers) {
352 await disposeServerHandlers();
353 } else {
354 await closeProvidedHandlers(handlers);
355 }
356 // Final analytics flush; the layer finalizer drains the buffer.
357 await disposeAnalytics();
358 if (viteChild) await viteChild.stop();
359 };
360
361 // 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
362 try {
363 // Mirror every OAuth callback completion into the local in-memory result
364 // store. The Electron desktop renderer polls /api/oauth/await/:sessionId
365 // for these when the user runs the flow in their system browser (no
366 // shared origin → no postMessage). Cloud doesn't register a listener;
367 // its same-origin web SPA receives results via postMessage directly.
368 setOAuthCompletionListener((result) => publishOAuthResult(result));
369
370 // Build static routes from either embedded assets, disk, or a spawned
371 // vite dev child (EXECUTOR_DEV=1). Vite mode takes precedence and
372 // disables the file-extension 404 short-circuit since vite serves
373 // hashed asset paths directly.
374 let staticRoutes: Record<string, StaticHandler> = {};
375 let serveIndex: StaticHandler;
376
377 const devMode = process.env.EXECUTOR_DEV === "1" && !opts.embeddedWebUI;
378 if (devMode) {
379 console.log("[executor] EXECUTOR_DEV=1 — spawning vite dev child for live UI");
380 viteChild = await startViteChild();
381 // Diagnostic only — this is the internal vite port the daemon proxies to.
382 // It must NOT read as a destination: the URL to open is the `Open:` line the
383 // CLI prints (the daemon port, with ?_token). Hitting the vite port directly
384 // skips that bootstrap and lands on the auth gate.

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