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

Function startServer

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

Source from the content-addressed store, hash-verified

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