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

Function startSidecar

apps/desktop/src/main/sidecar.ts:218–396  ·  view source on GitHub ↗
(options: StartOptions = {})

Source from the content-addressed store, hash-verified

216 new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
217
218export async function startSidecar(options: StartOptions = {}): Promise<SidecarConnection> {
219 const hostname = options.hostname ?? "127.0.0.1";
220 const settings = getServerSettings();
221 // data.db and the optional executor.jsonc plugin manifest live under
222 // ~/.executor — the same path the CLI's `executor web` uses. Desktop and CLI
223 // share state on the same machine so sources/secrets/policies set up in one
224 // show up in the other, and user-facing commands like
225 // `executor mcp --scope ~/.executor` stay copy-paste-friendly. Electron's
226 // userData (set in main/index.ts) is still used for electron-store,
227 // electron-log, and window-state — those stay app-scoped to avoid colliding
228 // with anything else under HOME.
229 const scopeDir = join(homedir(), ".executor");
230 const dataDir = scopeDir;
231 mkdirSync(dataDir, { recursive: true });
232
233 // The stable bearer token from auth.json (shared with the CLI). The main
234 // process holds it so it can inject the header into the webview; the child
235 // validates against the same value. Always present — auth is unconditional.
236 const authToken = loadOrMintLocalAuthToken(dataDir);
237 const { command, args, cwd, cliManagedManifest } = resolveSidecarCommand({
238 port: settings.port,
239 hostname,
240 authToken,
241 });
242 const clientDir = cliManagedManifest ? null : resolveClientDir();
243
244 if (!cliManagedManifest && clientDir && !existsSync(clientDir)) {
245 // oxlint-disable-next-line executor/no-error-constructor, executor/no-try-catch-or-throw -- boundary: startup failure is surfaced in the Electron main process
246 throw new Error(
247 `Executor client bundle not found at ${clientDir}. Run \`bun run --filter @executor-js/local build\` before launching desktop.`,
248 );
249 }
250
251 // No process-level startup lock: the dev sidecar child opens the DB through
252 // openOwnedLocalDatabase, whose ownership lock is the real gate. If the child
253 // loses the race, startup fails as before; only the packaged supervised boot
254 // path attaches to an existing daemon.
255 const webBaseUrl = `http://${hostname}:${settings.port}`;
256 const child = spawn(command, args, {
257 cwd,
258 stdio: ["ignore", "pipe", "pipe"],
259 env: {
260 ...process.env,
261 EXECUTOR_PORT: String(settings.port),
262 EXECUTOR_HOST: hostname,
263 EXECUTOR_WEB_BASE_URL: webBaseUrl,
264 PORT: String(settings.port),
265 // The bearer token the child validates and the main process injects into
266 // the webview. Always set — auth is unconditional.
267 EXECUTOR_AUTH_TOKEN: authToken,
268 ...(clientDir ? { EXECUTOR_CLIENT_DIR: clientDir } : {}),
269 EXECUTOR_SCOPE_DIR: scopeDir,
270 EXECUTOR_DATA_DIR: dataDir,
271 EXECUTOR_CLIENT: "desktop",
272 // Crash reporting (desktop builds with a baked-in DSN only). The
273 // CLI's `executor web` never sets these, so the shared server code
274 // stays telemetry-free outside the desktop app.
275 ...sidecarCrashReportingEnv(),

Callers 2

startWithCurrentSettingsFunction · 0.90
bootedSidecarFunction · 0.85

Calls 8

getServerSettingsFunction · 0.90
loadOrMintLocalAuthTokenFunction · 0.90
sidecarCrashReportingEnvFunction · 0.90
resolveSidecarCommandFunction · 0.85
resolveClientDirFunction · 0.85
makeLineSplitterFunction · 0.85
errorMethod · 0.80
infoMethod · 0.65

Tested by 1

bootedSidecarFunction · 0.68