// MUST be first: publishes the colocated libSQL/keyring native `.node` paths// before any import (e.g. `@executor-js/local` → libSQL) eagerly loads them.import "./native-bindings"; import { execFile } from "node:child_process";import { randomUUID } from "node:crypto";import { existsSync, realpathSync } from "node:fs";import { dirname, join, resolve } from "node:path";// Make sibling binaries (if any are added later) discoverable on $PATH so// child processes spawned without an absolute path still find them.const execDir = dirname(process.execPath);if (process.env.PATH && !process.env.PATH.includes(execDir)) { process.env.PATH = `${execDir}:${process.env.PATH}`;} // Pre-load QuickJS WASM for compiled binaries — must run before server importsconst wasmOnDisk = join(execDir, "emscripten-module.wasm");if (typeof Bun !== "undefined" && (await Bun.file(wasmOnDisk).exists())) { const { setQuickJSModule } = await import("@executor-js/runtime-quickjs"); const { newQuickJSWASMModule } = await import("quickjs-emscripten"); type QuickJSSyncVariant = import("quickjs-emscripten").QuickJSSyncVariant; const wasmBinary = await Bun.file(wasmOnDisk).arrayBuffer(); const importFFI: QuickJSSyncVariant["importFFI"] = () => import("@jitl/quickjs-wasmfile-release-sync/ffi").then((m) => m.QuickJSFFI); const importModuleLoader: QuickJSSyncVariant["importModuleLoader"] = async () => { const { default: original } = await import("@jitl/quickjs-wasmfile-release-sync/emscripten-module"); return (moduleArg = {}) => original({ ...moduleArg, wasmBinary }); }; const variant: QuickJSSyncVariant = { type: "sync" as const, importFFI, importModuleLoader, }; const mod = await newQuickJSWASMModule(variant); setQuickJSModule(mod);} import { Argument as Args, Command, Flag as Options } from "effect/unstable/cli";import { BunRuntime, BunServices } from "@effect/platform-bun";import { HttpApiClient } from "effect/unstable/httpapi";import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http";import { FileSystem, Path as PlatformPath } from "effect";import type { PlatformError } from "effect/PlatformError";import * as Effect from "effect/Effect";import * as Option from "effect/Option";import * as Cause from "effect/Cause"; import { ExecutorApi } from "@executor-js/api";import { getExecutorServerAuthorizationHeader, normalizeExecutorServerConnection, type ExecutorLocalServerKind, type ExecutorLocalServerManifest, type ExecutorServerConnection, type ExecutorServerConnectionInput,} from "@executor-js/sdk/shared";import { startServer, runMcpStdioServer, getExecutor, rotateLocalAuthToken, localAuthTokenPath,} from "@executor-js/local";import { makeQuickJsExecutor } from "@executor-js/runtime-quickjs";import { fetchIntegrations } from "./integrations";import { buildDaemonSpawnSpec, chooseDaemonPort, canAutoStartLocalDaemonForHost, isExecutorServerReachable, isDevCliEntrypoint, parseDaemonBaseUrl, spawnDetached, waitForReachable, waitForUnreachable,} from "./daemon";import { acquireDaemonStartLock, canonicalDaemonHost, currentDaemonScopeId, isPidAlive, readDaemonPointer, readDaemonRecord, releaseDaemonStartLock, removeDaemonPointer, removeDaemonRecord, terminatePid, writeDaemonPointer, writeDaemonRecord,} from "./daemon-state";import { canAutoStartCliServerConnection, chooseCliServerConnectionWithActiveLocal, parseCliExecutorServerConnection, type CliServerConnectionSource, withCliServerAuthFallback,} from "./server-connection";import { acquireLocalServerStartLock, readLocalServerManifest, releaseLocalServerStartLock, removeLocalServerManifestIfOwnedBy, resolveExecutorDataDir, writeLocalServerManifest,} from "./local-server-manifest";import { defaultCliServerConnectionProfile, findCliServerConnectionProfile, readCliServerConnectionStore, removeCliServerConnectionProfile, setDefaultCliServerConnectionProfile, upsertCliServerConnectionProfile, validateCliServerConnectionProfileName,} from "./server-profile";import { buildResumeContentTemplate, buildToolPath, buildDescribeToolCode, filterToolPathChildren, buildInvokeToolCode, buildListSourcesCode, buildSearchToolsCode, extractExecutionId, extractPausedInteraction, extractExecutionResult, inspectToolPath, normalizeCliErrorText, parseJsonObjectInput, sanitizeCliOutputText, shellQuoteArg,} from "./tooling"; // Embedded web UI — baked into compiled binaries via `with { type: "file" }`import embeddedWebUI from "./embedded-web-ui.gen"; // ---------------------------------------------------------------------------// Constants// --------------------------------------------------------------------------- const { version: CLI_VERSION } = await import("../package.json");const DEFAULT_PORT = 4788;const DEFAULT_BASE_URL = `http://localhost:${DEFAULT_PORT}`;const DAEMON_BOOT_TIMEOUT_MS = 15_000;const DAEMON_BOOT_POLL_MS = 150;const DAEMON_STOP_TIMEOUT_MS = 10_000; // ---------------------------------------------------------------------------// Helpers// --------------------------------------------------------------------------- const waitForShutdownSignal = () => Effect.callback<void, never>((resume) => { const shutdown = () => resume(Effect.void); process.once("SIGINT", shutdown); process.once("SIGTERM", shutdown); return Effect.sync(() => { process.off("SIGINT", shutdown); process.off("SIGTERM", shutdown); }); }); // ---------------------------------------------------------------------------// Background server management// --------------------------------------------------------------------------- const isServerReachable = (baseUrl: string): Effect.Effect<boolean> => isExecutorServerReachable({ baseUrl }); const readActiveLocalServerManifest = (): Effect.Effect< ExecutorLocalServerManifest | null, Error, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const manifest = yield* readLocalServerManifest(); if (!manifest) return null; if (!isPidAlive(manifest.pid)) { yield* removeLocalServerManifestIfOwnedBy({ pid: manifest.pid }).pipe(Effect.ignore); return null; } if (yield* isServerReachable(manifest.connection.origin)) { return manifest; } return yield* Effect.fail( new Error( [ `A local Executor ${manifest.kind} is registered at ${manifest.connection.origin} (pid ${manifest.pid}) but is not reachable.`, "Refusing to start another local server against the same data directory.", "Stop the existing process or remove the stale server-control manifest after verifying the process is not using the database.", ].join("\n"), ), ); }); const normalizeDaemonScopeDir = (dir: string): string => { const resolved = resolve(dir); return existsSync(resolved) ? realpathSync.native(resolved) : resolved;}; const currentScopeDirForManifest = (): string | null => process.env.EXECUTOR_SCOPE_DIR ? normalizeDaemonScopeDir(process.env.EXECUTOR_SCOPE_DIR) : null; const script = process.argv[1];const isDevMode = isDevCliEntrypoint(script);const cliPrefix = isDevMode ? `bun run ${script}` : "executor"; const toError = (cause: unknown): Error => cause instanceof Error ? cause : new Error(String(cause)); interface ServerTarget { readonly baseUrl?: string; readonly serverName?: string;} interface RequestedExecutorServerConnection { readonly connection: ExecutorServerConnection; readonly source: CliServerConnectionSource;} interface ExecuteCodeResult { readonly connection: ExecutorServerConnection; readonly outcome: ExecuteCodeOutcome;} const parseDaemonUrl = (baseUrl: string) => Effect.try({ try: () => parseDaemonBaseUrl(baseUrl, DEFAULT_PORT), catch: (cause) => cause instanceof Error ? cause : new Error(`Invalid base URL: ${String(cause)}`), }); const parseExecutorServerConnection = (baseUrl: string) => Effect.try({ try: () => parseCliExecutorServerConnection(baseUrl), catch: (cause) => cause instanceof Error ? cause : new Error(`Invalid server URL: ${String(cause)}`), }); const daemonBaseUrl = (hostname: string, port: number): string => `http://${canonicalDaemonHost(hostname)}:${port}`; const makeLocalServerManifest = (input: { readonly kind: ExecutorLocalServerKind; readonly connection: ExecutorServerConnection;}): Effect.Effect<ExecutorLocalServerManifest, never, PlatformPath.Path> => Effect.gen(function* () { const path = yield* PlatformPath.Path; return { version: 1, kind: input.kind, pid: process.pid, startedAt: new Date().toISOString(), dataDir: resolveExecutorDataDir(path), scopeDir: currentScopeDirForManifest(), connection: input.connection, owner: { client: "cli", version: CLI_VERSION, executablePath: isDevMode ? (script ?? null) : process.execPath, }, }; }); const assertNoOtherActiveLocalServer = (): Effect.Effect< void, Error, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const active = yield* readActiveLocalServerManifest(); if (!active || active.pid === process.pid) return; return yield* Effect.fail( new Error( [ `A local Executor ${active.kind} is already running at ${active.connection.origin} (pid ${active.pid}).`, `It owns the current data directory: ${active.dataDir}`, "Stop it before starting another local server.", ].join("\n"), ), ); }); const publishLocalServerManifest = (input: { readonly kind: ExecutorLocalServerKind; readonly connection: ExecutorServerConnection;}): Effect.Effect<void, PlatformError, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const manifest = yield* makeLocalServerManifest(input); yield* writeLocalServerManifest(manifest); }); const installDefaultExecutorWebBaseUrl = (baseUrl: string): (() => void) => { if (process.env.EXECUTOR_WEB_BASE_URL !== undefined) { return () => {}; } process.env.EXECUTOR_WEB_BASE_URL = baseUrl; return () => { delete process.env.EXECUTOR_WEB_BASE_URL; };}; const cleanupPointer = (input: { hostname: string; scopeId: string; port: number }) => Effect.gen(function* () { yield* removeDaemonPointer({ hostname: input.hostname, scopeId: input.scopeId }).pipe( Effect.ignore, ); yield* removeDaemonRecord({ hostname: input.hostname, port: input.port }).pipe(Effect.ignore); }); const resolveDaemonTarget = (baseUrl: string) => Effect.gen(function* () { const parsed = yield* parseDaemonUrl(baseUrl); const host = canonicalDaemonHost(parsed.hostname); const scopeId = currentDaemonScopeId(); const pointer = yield* readDaemonPointer({ hostname: host, scopeId }); if (pointer) { const pointerUrl = daemonBaseUrl(pointer.hostname, pointer.port); if (isPidAlive(pointer.pid) && (yield* isServerReachable(pointerUrl))) { return { baseUrl: pointerUrl, hostname: pointer.hostname, port: pointer.port, scopeId, fromPointer: true, }; } yield* cleanupPointer({ hostname: pointer.hostname, scopeId, port: pointer.port }); } return { baseUrl: daemonBaseUrl(host, parsed.port), hostname: host, port: parsed.port, scopeId, fromPointer: false, }; }); // Serialize daemon startup behind a filesystem lock so concurrent CLI invocations don't// each spawn their own daemon. The post-lock pointer recheck catches the case where// another invocation finished bootstrapping while we were waiting for the lock.const spawnAndWaitForDaemon = (input: { host: string; scopeId: string; preferredPort: number; allowedHosts: ReadonlyArray<string>;}): Effect.Effect<string, Error, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const lock = yield* acquireDaemonStartLock({ hostname: input.host, scopeId: input.scopeId }); try { const existing = yield* readDaemonPointer({ hostname: input.host, scopeId: input.scopeId }); if (existing && isPidAlive(existing.pid)) { const existingUrl = daemonBaseUrl(existing.hostname, existing.port); if (yield* isServerReachable(existingUrl)) { return existingUrl; } } const selectedPort = yield* chooseDaemonPort({ preferredPort: input.preferredPort, hostname: input.host, }); if (selectedPort !== input.preferredPort) { console.error( `Port ${input.preferredPort} is in use. Starting daemon on available port ${selectedPort} instead.`, ); } const spec = yield* Effect.try({ try: () => buildDaemonSpawnSpec({ port: selectedPort, hostname: input.host, isDevMode, scriptPath: script, executablePath: process.execPath, allowedHosts: input.allowedHosts, }), catch: (cause) => cause instanceof Error ? cause : new Error(`Failed to build daemon command: ${String(cause)}`), }); const startBaseUrl = daemonBaseUrl(input.host, selectedPort); console.error(`Starting daemon on ${input.host}:${selectedPort}...`); yield* spawnDetached({ command: spec.command, args: spec.args, env: process.env, }); const ready = yield* waitForReachable({ check: isServerReachable(startBaseUrl), timeoutMs: DAEMON_BOOT_TIMEOUT_MS, intervalMs: DAEMON_BOOT_POLL_MS, }); if (!ready) { return yield* Effect.fail( new Error( [ `Daemon did not become reachable at ${startBaseUrl} within ${DAEMON_BOOT_TIMEOUT_MS}ms.`, `Run in foreground to inspect logs: ${cliPrefix} daemon run --foreground --port ${selectedPort} --hostname ${input.host}`, ].join("\n"), ), ); } return startBaseUrl; } finally { yield* releaseDaemonStartLock(lock).pipe(Effect.ignore); } }); // Auto-start a local daemon on demand so commands like `executor call` work without the// user having to run `daemon run` first. Refuses non-local hosts because spawning a// daemon process on the user's behalf only makes sense when "the user's machine" is// also where the request will land.const ensureDaemon = ( baseUrl: string,): Effect.Effect<string, Error, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const resolvedTarget = yield* resolveDaemonTarget(baseUrl); if (resolvedTarget.fromPointer && (yield* isServerReachable(resolvedTarget.baseUrl))) { return resolvedTarget.baseUrl; } const active = yield* readActiveLocalServerManifest(); const activeOrigin = active ? normalizeExecutorServerConnection({ origin: active.connection.origin }).origin : null; const targetOrigin = normalizeExecutorServerConnection({ origin: resolvedTarget.baseUrl, }).origin; if (activeOrigin === targetOrigin) { return resolvedTarget.baseUrl; } if (active && activeOrigin !== targetOrigin) { return yield* Effect.fail( new Error( [ `A local Executor ${active.kind} is already running at ${active.connection.origin} (pid ${active.pid}).`, `It owns the current data directory: ${active.dataDir}`, "Refusing to start another local daemon against the same database.", ].join("\n"), ), ); } const parsed = yield* parseDaemonUrl(baseUrl); const host = canonicalDaemonHost(parsed.hostname); if (!canAutoStartLocalDaemonForHost(host)) { return yield* Effect.fail( new Error( [ `Executor daemon is not reachable at ${baseUrl}.`, "Auto-start is only supported for local hosts.", `Start it manually: ${cliPrefix} daemon run --port ${parsed.port} --hostname ${host}`, ].join("\n"), ), ); } return yield* spawnAndWaitForDaemon({ host, scopeId: resolvedTarget.scopeId, preferredPort: parsed.port, allowedHosts: [], }); }).pipe(Effect.mapError(toError)); const resolveRequestedExecutorServerConnection = ( target: ServerTarget,): Effect.Effect< RequestedExecutorServerConnection, Error, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { if (target.baseUrl && target.serverName) { return yield* Effect.fail(new Error("Use either --server or --base-url, not both.")); } if (target.serverName) { const store = yield* readCliServerConnectionStore(); const profile = findCliServerConnectionProfile(store, target.serverName); if (!profile) { return yield* Effect.fail(new Error(`No server profile named "${target.serverName}".`));Lines 1–500 of 2223. Load more
// MUST be first: publishes the colocated libSQL/keyring native `.node` paths // before any import (e.g. `@executor-js/local` → libSQL) eagerly loads them. import "./native-bindings"; import { execFile } from "node:child_process"; import { randomUUID } from "node:crypto"; import { existsSync, realpathSync } from "node:fs"; import { dirname, join, resolve } from "node:path"; // Make sibling binaries (if any are added later) discoverable on $PATH so // child processes spawned without an absolute path still find them. const execDir = dirname(process.execPath); if (process.env.PATH && !process.env.PATH.includes(execDir)) { process.env.PATH = `${execDir}:${process.env.PATH}`; } // Pre-load QuickJS WASM for compiled binaries — must run before server imports const wasmOnDisk = join(execDir, "emscripten-module.wasm"); if (typeof Bun !== "undefined" && (await Bun.file(wasmOnDisk).exists())) { const { setQuickJSModule } = await import("@executor-js/runtime-quickjs"); const { newQuickJSWASMModule } = await import("quickjs-emscripten"); type QuickJSSyncVariant = import("quickjs-emscripten").QuickJSSyncVariant; const wasmBinary = await Bun.file(wasmOnDisk).arrayBuffer(); const importFFI: QuickJSSyncVariant["importFFI"] = () => import("@jitl/quickjs-wasmfile-release-sync/ffi").then((m) => m.QuickJSFFI); const importModuleLoader: QuickJSSyncVariant["importModuleLoader"] = async () => { const { default: original } = await import("@jitl/quickjs-wasmfile-release-sync/emscripten-module"); return (moduleArg = {}) => original({ ...moduleArg, wasmBinary }); }; const variant: QuickJSSyncVariant = { type: "sync" as const, importFFI, importModuleLoader, }; const mod = await newQuickJSWASMModule(variant); setQuickJSModule(mod); } import { Argument as Args, Command, Flag as Options } from "effect/unstable/cli"; import { BunRuntime, BunServices } from "@effect/platform-bun"; import { HttpApiClient } from "effect/unstable/httpapi"; import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"; import { FileSystem, Path as PlatformPath } from "effect"; import type { PlatformError } from "effect/PlatformError"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Cause from "effect/Cause"; import { ExecutorApi } from "@executor-js/api"; import { getExecutorServerAuthorizationHeader, normalizeExecutorServerConnection, type ExecutorLocalServerKind, type ExecutorLocalServerManifest, type ExecutorServerConnection, type ExecutorServerConnectionInput, } from "@executor-js/sdk/shared"; import { startServer, runMcpStdioServer, getExecutor, rotateLocalAuthToken, localAuthTokenPath, } from "@executor-js/local"; import { makeQuickJsExecutor } from "@executor-js/runtime-quickjs"; import { fetchIntegrations } from "./integrations"; import { buildDaemonSpawnSpec, chooseDaemonPort, canAutoStartLocalDaemonForHost, isExecutorServerReachable, isDevCliEntrypoint, parseDaemonBaseUrl, spawnDetached, waitForReachable, waitForUnreachable, } from "./daemon"; import { acquireDaemonStartLock, canonicalDaemonHost, currentDaemonScopeId, isPidAlive, readDaemonPointer, readDaemonRecord, releaseDaemonStartLock, removeDaemonPointer, removeDaemonRecord, terminatePid, writeDaemonPointer, writeDaemonRecord, } from "./daemon-state"; import { canAutoStartCliServerConnection, chooseCliServerConnectionWithActiveLocal, parseCliExecutorServerConnection, type CliServerConnectionSource, withCliServerAuthFallback, } from "./server-connection"; import { acquireLocalServerStartLock, readLocalServerManifest, releaseLocalServerStartLock, removeLocalServerManifestIfOwnedBy, resolveExecutorDataDir, writeLocalServerManifest, } from "./local-server-manifest"; import { defaultCliServerConnectionProfile, findCliServerConnectionProfile, readCliServerConnectionStore, removeCliServerConnectionProfile, setDefaultCliServerConnectionProfile, upsertCliServerConnectionProfile, validateCliServerConnectionProfileName, } from "./server-profile"; import { buildResumeContentTemplate, buildToolPath, buildDescribeToolCode, filterToolPathChildren, buildInvokeToolCode, buildListSourcesCode, buildSearchToolsCode, extractExecutionId, extractPausedInteraction, extractExecutionResult, inspectToolPath, normalizeCliErrorText, parseJsonObjectInput, sanitizeCliOutputText, shellQuoteArg, } from "./tooling"; // Embedded web UI — baked into compiled binaries via `with { type: "file" }` import embeddedWebUI from "./embedded-web-ui.gen"; // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const { version: CLI_VERSION } = await import("../package.json"); const DEFAULT_PORT = 4788; const DEFAULT_BASE_URL = `http://localhost:${DEFAULT_PORT}`; const DAEMON_BOOT_TIMEOUT_MS = 15_000; const DAEMON_BOOT_POLL_MS = 150; const DAEMON_STOP_TIMEOUT_MS = 10_000; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- const waitForShutdownSignal = () => Effect.callback<void, never>((resume) => { const shutdown = () => resume(Effect.void); process.once("SIGINT", shutdown); process.once("SIGTERM", shutdown); return Effect.sync(() => { process.off("SIGINT", shutdown); process.off("SIGTERM", shutdown); }); }); // --------------------------------------------------------------------------- // Background server management // --------------------------------------------------------------------------- const isServerReachable = (baseUrl: string): Effect.Effect<boolean> => isExecutorServerReachable({ baseUrl }); const readActiveLocalServerManifest = (): Effect.Effect< ExecutorLocalServerManifest | null, Error, FileSystem.FileSystem | PlatformPath.Path > => Effect.gen(function* () { const manifest = yield* readLocalServerManifest(); if (!manifest) return null; if (!isPidAlive(manifest.pid)) { yield* removeLocalServerManifestIfOwnedBy({ pid: manifest.pid }).pipe(Effect.ignore); return null; } if (yield* isServerReachable(manifest.connection.origin)) { return manifest; } return yield* Effect.fail( new Error( [ `A local Executor ${manifest.kind} is registered at ${manifest.connection.origin} (pid ${manifest.pid}) but is not reachable.`, "Refusing to start another local server against the same data directory.", "Stop the existing process or remove the stale server-control manifest after verifying the process is not using the database.", ].join("\n"), ), ); }); const normalizeDaemonScopeDir = (dir: string): string => { const resolved = resolve(dir); return existsSync(resolved) ? realpathSync.native(resolved) : resolved; }; const currentScopeDirForManifest = (): string | null => process.env.EXECUTOR_SCOPE_DIR ? normalizeDaemonScopeDir(process.env.EXECUTOR_SCOPE_DIR) : null; const script = process.argv[1]; const isDevMode = isDevCliEntrypoint(script); const cliPrefix = isDevMode ? `bun run ${script}` : "executor"; const toError = (cause: unknown): Error => cause instanceof Error ? cause : new Error(String(cause)); interface ServerTarget { readonly baseUrl?: string; readonly serverName?: string; } interface RequestedExecutorServerConnection { readonly connection: ExecutorServerConnection; readonly source: CliServerConnectionSource; } interface ExecuteCodeResult { readonly connection: ExecutorServerConnection; readonly outcome: ExecuteCodeOutcome; } const parseDaemonUrl = (baseUrl: string) => Effect.try({ try: () => parseDaemonBaseUrl(baseUrl, DEFAULT_PORT), catch: (cause) => cause instanceof Error ? cause : new Error(`Invalid base URL: ${String(cause)}`), }); const parseExecutorServerConnection = (baseUrl: string) => Effect.try({ try: () => parseCliExecutorServerConnection(baseUrl), catch: (cause) => cause instanceof Error ? cause : new Error(`Invalid server URL: ${String(cause)}`), }); const daemonBaseUrl = (hostname: string, port: number): string => `http://${canonicalDaemonHost(hostname)}:${port}`; const makeLocalServerManifest = (input: { readonly kind: ExecutorLocalServerKind; readonly connection: ExecutorServerConnection; }): Effect.Effect<ExecutorLocalServerManifest, never, PlatformPath.Path> => Effect.gen(function* () { const path = yield* PlatformPath.Path; return { version: 1, kind: input.kind, pid: process.pid, startedAt: new Date().toISOString(), dataDir: resolveExecutorDataDir(path), scopeDir: currentScopeDirForManifest(), connection: input.connection, owner: { client: "cli", version: CLI_VERSION, executablePath: isDevMode ? (script ?? null) : process.execPath, }, }; }); const assertNoOtherActiveLocalServer = (): Effect.Effect< void, Error, FileSystem.FileSystem | PlatformPath.Path > => Effect.gen(function* () { const active = yield* readActiveLocalServerManifest(); if (!active || active.pid === process.pid) return; return yield* Effect.fail( new Error( [ `A local Executor ${active.kind} is already running at ${active.connection.origin} (pid ${active.pid}).`, `It owns the current data directory: ${active.dataDir}`, "Stop it before starting another local server.", ].join("\n"), ), ); }); const publishLocalServerManifest = (input: { readonly kind: ExecutorLocalServerKind; readonly connection: ExecutorServerConnection; }): Effect.Effect<void, PlatformError, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const manifest = yield* makeLocalServerManifest(input); yield* writeLocalServerManifest(manifest); }); const installDefaultExecutorWebBaseUrl = (baseUrl: string): (() => void) => { if (process.env.EXECUTOR_WEB_BASE_URL !== undefined) { return () => {}; } process.env.EXECUTOR_WEB_BASE_URL = baseUrl; return () => { delete process.env.EXECUTOR_WEB_BASE_URL; }; }; const cleanupPointer = (input: { hostname: string; scopeId: string; port: number }) => Effect.gen(function* () { yield* removeDaemonPointer({ hostname: input.hostname, scopeId: input.scopeId }).pipe( Effect.ignore, ); yield* removeDaemonRecord({ hostname: input.hostname, port: input.port }).pipe(Effect.ignore); }); const resolveDaemonTarget = (baseUrl: string) => Effect.gen(function* () { const parsed = yield* parseDaemonUrl(baseUrl); const host = canonicalDaemonHost(parsed.hostname); const scopeId = currentDaemonScopeId(); const pointer = yield* readDaemonPointer({ hostname: host, scopeId }); if (pointer) { const pointerUrl = daemonBaseUrl(pointer.hostname, pointer.port); if (isPidAlive(pointer.pid) && (yield* isServerReachable(pointerUrl))) { return { baseUrl: pointerUrl, hostname: pointer.hostname, port: pointer.port, scopeId, fromPointer: true, }; } yield* cleanupPointer({ hostname: pointer.hostname, scopeId, port: pointer.port }); } return { baseUrl: daemonBaseUrl(host, parsed.port), hostname: host, port: parsed.port, scopeId, fromPointer: false, }; }); // Serialize daemon startup behind a filesystem lock so concurrent CLI invocations don't // each spawn their own daemon. The post-lock pointer recheck catches the case where // another invocation finished bootstrapping while we were waiting for the lock. const spawnAndWaitForDaemon = (input: { host: string; scopeId: string; preferredPort: number; allowedHosts: ReadonlyArray<string>; }): Effect.Effect<string, Error, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const lock = yield* acquireDaemonStartLock({ hostname: input.host, scopeId: input.scopeId }); try { const existing = yield* readDaemonPointer({ hostname: input.host, scopeId: input.scopeId }); if (existing && isPidAlive(existing.pid)) { const existingUrl = daemonBaseUrl(existing.hostname, existing.port); if (yield* isServerReachable(existingUrl)) { return existingUrl; } } const selectedPort = yield* chooseDaemonPort({ preferredPort: input.preferredPort, hostname: input.host, }); if (selectedPort !== input.preferredPort) { console.error( `Port ${input.preferredPort} is in use. Starting daemon on available port ${selectedPort} instead.`, ); } const spec = yield* Effect.try({ try: () => buildDaemonSpawnSpec({ port: selectedPort, hostname: input.host, isDevMode, scriptPath: script, executablePath: process.execPath, allowedHosts: input.allowedHosts, }), catch: (cause) => cause instanceof Error ? cause : new Error(`Failed to build daemon command: ${String(cause)}`), }); const startBaseUrl = daemonBaseUrl(input.host, selectedPort); console.error(`Starting daemon on ${input.host}:${selectedPort}...`); yield* spawnDetached({ command: spec.command, args: spec.args, env: process.env, }); const ready = yield* waitForReachable({ check: isServerReachable(startBaseUrl), timeoutMs: DAEMON_BOOT_TIMEOUT_MS, intervalMs: DAEMON_BOOT_POLL_MS, }); if (!ready) { return yield* Effect.fail( new Error( [ `Daemon did not become reachable at ${startBaseUrl} within ${DAEMON_BOOT_TIMEOUT_MS}ms.`, `Run in foreground to inspect logs: ${cliPrefix} daemon run --foreground --port ${selectedPort} --hostname ${input.host}`, ].join("\n"), ), ); } return startBaseUrl; } finally { yield* releaseDaemonStartLock(lock).pipe(Effect.ignore); } }); // Auto-start a local daemon on demand so commands like `executor call` work without the // user having to run `daemon run` first. Refuses non-local hosts because spawning a // daemon process on the user's behalf only makes sense when "the user's machine" is // also where the request will land. const ensureDaemon = ( baseUrl: string, ): Effect.Effect<string, Error, FileSystem.FileSystem | PlatformPath.Path> => Effect.gen(function* () { const resolvedTarget = yield* resolveDaemonTarget(baseUrl); if (resolvedTarget.fromPointer && (yield* isServerReachable(resolvedTarget.baseUrl))) { return resolvedTarget.baseUrl; } const active = yield* readActiveLocalServerManifest(); const activeOrigin = active ? normalizeExecutorServerConnection({ origin: active.connection.origin }).origin : null; const targetOrigin = normalizeExecutorServerConnection({ origin: resolvedTarget.baseUrl, }).origin; if (activeOrigin === targetOrigin) { return resolvedTarget.baseUrl; } if (active && activeOrigin !== targetOrigin) { return yield* Effect.fail( new Error( [ `A local Executor ${active.kind} is already running at ${active.connection.origin} (pid ${active.pid}).`, `It owns the current data directory: ${active.dataDir}`, "Refusing to start another local daemon against the same database.", ].join("\n"), ), ); } const parsed = yield* parseDaemonUrl(baseUrl); const host = canonicalDaemonHost(parsed.hostname); if (!canAutoStartLocalDaemonForHost(host)) { return yield* Effect.fail( new Error( [ `Executor daemon is not reachable at ${baseUrl}.`, "Auto-start is only supported for local hosts.", `Start it manually: ${cliPrefix} daemon run --port ${parsed.port} --hostname ${host}`, ].join("\n"), ), ); } return yield* spawnAndWaitForDaemon({ host, scopeId: resolvedTarget.scopeId, preferredPort: parsed.port, allowedHosts: [], }); }).pipe(Effect.mapError(toError)); const resolveRequestedExecutorServerConnection = ( target: ServerTarget, ): Effect.Effect< RequestedExecutorServerConnection, Error, FileSystem.FileSystem | PlatformPath.Path > => Effect.gen(function* () { if (target.baseUrl && target.serverName) { return yield* Effect.fail(new Error("Use either --server or --base-url, not both.")); } if (target.serverName) { const store = yield* readCliServerConnectionStore(); const profile = findCliServerConnectionProfile(store, target.serverName); if (!profile) { return yield* Effect.fail(new Error(`No server profile named "${target.serverName}".`));