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

Function startSidecar

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

Source from the content-addressed store, hash-verified

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

Callers 2

startWithCurrentSettingsFunction · 0.90
bootedSidecarFunction · 0.85

Calls 9

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

Tested by 1

bootedSidecarFunction · 0.68