diff --git a/packages/server/scripts/daemon-runner.ts b/packages/server/scripts/daemon-runner.ts index e978334e4..db6d660e2 100644 --- a/packages/server/scripts/daemon-runner.ts +++ b/packages/server/scripts/daemon-runner.ts @@ -1,5 +1,6 @@ import { fileURLToPath } from "url"; import { existsSync } from "node:fs"; +import path from "node:path"; import { loadConfig } from "../src/server/config.js"; import { acquirePidLock, PidLockError, releasePidLock } from "../src/server/pid-lock.js"; import { resolvePaseoHome } from "../src/server/paseo-home.js"; @@ -55,13 +56,30 @@ function resolveWorkerExecArgv(workerEntry: string): string[] { return workerEntry.endsWith(".ts") ? ["--import", "tsx"] : []; } +function resolvePackagedNodeEntrypointRunnerPath(currentScriptPath: string): string | null { + const packageMarker = `${path.sep}node_modules${path.sep}@getpaseo${path.sep}server${path.sep}`; + const markerIndex = currentScriptPath.lastIndexOf(packageMarker); + if (markerIndex === -1) { + return null; + } + + const appRoot = currentScriptPath.slice(0, markerIndex); + const runnerPath = path.join(appRoot, "dist", "daemon", "node-entrypoint-runner.js"); + return existsSync(runnerPath) ? runnerPath : null; +} + async function main(): Promise { const config = parseConfig(process.argv.slice(2)); const workerEntry = config.devMode ? resolveDevWorkerEntry() : resolveWorkerEntry(); + const workerExecArgv = resolveWorkerExecArgv(workerEntry); const workerEnv: NodeJS.ProcessEnv = { ...process.env, PASEO_PID_LOCK_MODE: "external", }; + const packagedNodeEntrypointRunner = + process.env.ELECTRON_RUN_AS_NODE === "1" + ? resolvePackagedNodeEntrypointRunnerPath(fileURLToPath(import.meta.url)) + : null; applySherpaLoaderEnv(workerEnv); @@ -100,7 +118,22 @@ async function main(): Promise { resolveWorkerEntry: () => workerEntry, workerArgs: config.workerArgs, workerEnv, - workerExecArgv: resolveWorkerExecArgv(workerEntry), + workerExecArgv, + resolveWorkerSpawnSpec: packagedNodeEntrypointRunner + ? (resolvedWorkerEntry) => ({ + command: process.execPath, + args: [ + packagedNodeEntrypointRunner, + "node-script", + resolvedWorkerEntry, + ...config.workerArgs, + ], + env: { + ...workerEnv, + ELECTRON_RUN_AS_NODE: "1", + }, + }) + : undefined, restartOnCrash: config.devMode, onSupervisorExit: releaseLock, }); diff --git a/packages/server/scripts/supervisor.ts b/packages/server/scripts/supervisor.ts index 4b191005a..b661371ab 100644 --- a/packages/server/scripts/supervisor.ts +++ b/packages/server/scripts/supervisor.ts @@ -1,4 +1,4 @@ -import { fork, type ChildProcess } from "child_process"; +import { fork, spawn, type ChildProcess } from "child_process"; type WorkerLifecycleMessage = | { @@ -16,6 +16,11 @@ type SupervisorOptions = { workerArgs?: string[]; workerEnv?: NodeJS.ProcessEnv; workerExecArgv?: string[]; + resolveWorkerSpawnSpec?: (workerEntry: string) => { + command: string; + args: string[]; + env?: NodeJS.ProcessEnv; + } | null; restartOnCrash?: boolean; onSupervisorExit?: () => Promise | void; }; @@ -47,6 +52,7 @@ export function runSupervisor(options: SupervisorOptions): void { const workerArgs = options.workerArgs ?? process.argv.slice(2); const workerEnv = options.workerEnv ?? process.env; const workerExecArgv = options.workerExecArgv ?? ["--import", "tsx"]; + const resolveWorkerSpawnSpec = options.resolveWorkerSpawnSpec; let child: ChildProcess | null = null; let restarting = false; @@ -84,11 +90,19 @@ export function runSupervisor(options: SupervisorOptions): void { return; } - child = fork(workerEntry, workerArgs, { - stdio: "inherit", - env: workerEnv, - execArgv: workerExecArgv, - }); + const spawnSpec = resolveWorkerSpawnSpec?.(workerEntry) ?? null; + if (spawnSpec) { + child = spawn(spawnSpec.command, spawnSpec.args, { + stdio: ["inherit", "inherit", "inherit", "ipc"], + env: spawnSpec.env ?? workerEnv, + }); + } else { + child = fork(workerEntry, workerArgs, { + stdio: "inherit", + env: workerEnv, + execArgv: workerExecArgv, + }); + } child.on("message", (msg: unknown) => { const lifecycleMessage = parseLifecycleMessage(msg);