{"version":3,"file":"process.cjs","names":["spawnSync","osConstants","spawn","readJson","path","fs","delay"],"sources":["../../../../../src/cli/commands/compat/providers/process.ts"],"sourcesContent":["import { type ChildProcess, spawnSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport { constants as osConstants } from 'node:os';\nimport path from 'node:path';\nimport { setTimeout as delay } from 'node:timers/promises';\n\nimport { readJson, writeJson } from '@agimon-ai/doompi-core/runtime-json';\nimport spawn from 'cross-spawn';\n\n/**\n * Child process and cross-repository locking primitives.\n *\n * Shared by every compatibility provider and by the Pi launch command, so that\n * signal forwarding behaves the same however the harness starts a child.\n */\n\nconst FORWARDED_SIGNALS: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'];\nconst SIGNAL_EXIT_CODE_OFFSET = 128;\nconst UNKNOWN_SIGNAL_EXIT_CODE = 1;\nconst LOCK_RETRY_MS = 25;\nconst LOCK_STALE_MS = 10_000;\nconst LOCK_TIMEOUT_MS = 30_000;\nconst FILE_EXISTS_ERROR = 'EEXIST';\nconst FILE_NOT_FOUND_ERROR = 'ENOENT';\nconst PROCESS_NOT_FOUND_ERROR = 'ESRCH';\nconst LOCK_OWNER_FILENAME = 'owner.json';\n\n/** Runs a command and returns its trimmed stdout, throwing on a non-zero exit. */\nexport function runCaptured(command: string, args: string[], cwd: string, environment: NodeJS.ProcessEnv): string {\n  const result = spawnSync(command, args, { cwd, encoding: 'utf8', env: environment });\n  if (result.error) throw result.error;\n  if (result.stderr) process.stderr.write(result.stderr);\n  if (result.status !== 0) {\n    throw new Error(`${command} exited with status ${result.status ?? 1}`);\n  }\n  return result.stdout.trim();\n}\n\n/** Runs a command with inherited stdio, throwing on a non-zero exit. */\nexport function runChecked(command: string, args: string[], cwd: string, environment: NodeJS.ProcessEnv): void {\n  const result = spawnSync(command, args, { cwd, env: environment, stdio: 'inherit' });\n  if (result.error) throw result.error;\n  if (result.status !== 0) throw new Error(`${command} exited with status ${result.status ?? 1}`);\n}\n\n/** The conventional 128+n exit code for a child killed by a signal. */\nexport function signalExitCode(signal: NodeJS.Signals | null): number {\n  if (!signal) return UNKNOWN_SIGNAL_EXIT_CODE;\n  const signalNumber = osConstants.signals[signal];\n  return signalNumber ? SIGNAL_EXIT_CODE_OFFSET + signalNumber : UNKNOWN_SIGNAL_EXIT_CODE;\n}\n\n/**\n * Runs an interactive child, forwarding terminal signals for its lifetime.\n *\n * Listeners are registered with `on` rather than `once` so a second Ctrl-C still\n * reaches the child, and removed in a finally so repeated launches in one\n * process do not accumulate handlers.\n */\nexport function forwardSignals(child: ChildProcess): () => void {\n  const forward = (signal: NodeJS.Signals): void => {\n    if (child.exitCode === null && child.signalCode === null) child.kill(signal);\n  };\n  for (const signal of FORWARDED_SIGNALS) process.on(signal, forward);\n  return () => {\n    for (const signal of FORWARDED_SIGNALS) process.removeListener(signal, forward);\n  };\n}\n\n/** Resolves with the child's exit code, or the conventional code for its signal. */\nexport function waitForExit(child: ChildProcess): Promise<number> {\n  return new Promise<number>((resolve, reject) => {\n    child.once('error', reject);\n    child.once('exit', (code, signal) => resolve(code ?? signalExitCode(signal)));\n  });\n}\n\nexport async function runInteractive(\n  command: string,\n  args: string[],\n  cwd: string,\n  environment: NodeJS.ProcessEnv,\n): Promise<number> {\n  const child = spawn(command, args, { cwd, env: environment, stdio: 'inherit' });\n  const stopForwarding = forwardSignals(child);\n  try {\n    return await waitForExit(child);\n  } finally {\n    stopForwarding();\n  }\n}\n\nexport function isFileSystemError(error: unknown): error is NodeJS.ErrnoException {\n  return error instanceof Error && typeof (error as NodeJS.ErrnoException).code === 'string';\n}\n\n/** True when the recorded lock owner is still a live process. */\nexport function lockOwnerIsRunning(lockPath: string): boolean {\n  const owner = readJson(path.join(lockPath, LOCK_OWNER_FILENAME));\n  if (typeof owner.pid !== 'number' || !Number.isSafeInteger(owner.pid) || owner.pid <= 0) return false;\n  try {\n    process.kill(owner.pid, 0);\n    return true;\n  } catch (error) {\n    // Anything other than \"no such process\" means the pid exists but is not\n    // ours to signal, so the lock is still held.\n    return !isFileSystemError(error) || error.code !== PROCESS_NOT_FOUND_ERROR;\n  }\n}\n\n/**\n * Takes an exclusive lock by creating a directory, which is atomic everywhere.\n *\n * A lock older than the stale window whose owner is gone is reclaimed, so a\n * harness killed mid-sync cannot block every other repository forever.\n */\nexport async function acquireDirectoryLock(lockPath: string): Promise<() => Promise<void>> {\n  fs.mkdirSync(path.dirname(lockPath), { recursive: true });\n  const startedAt = Date.now();\n\n  while (true) {\n    try {\n      await fs.promises.mkdir(lockPath);\n      try {\n        writeJson(path.join(lockPath, LOCK_OWNER_FILENAME), { pid: process.pid });\n      } catch (error) {\n        await fs.promises.rm(lockPath, { recursive: true, force: true });\n        throw error;\n      }\n      return () => fs.promises.rm(lockPath, { recursive: true, force: true });\n    } catch (error) {\n      if (!isFileSystemError(error) || error.code !== FILE_EXISTS_ERROR) throw error;\n      try {\n        const stat = await fs.promises.stat(lockPath);\n        if (Date.now() - stat.mtimeMs >= LOCK_STALE_MS && !lockOwnerIsRunning(lockPath)) {\n          await fs.promises.rm(lockPath, { recursive: true, force: true });\n          continue;\n        }\n      } catch (statError) {\n        // The holder released between the failed mkdir and this stat, so retry\n        // immediately rather than waiting out the retry delay.\n        if (isFileSystemError(statError) && statError.code === FILE_NOT_FOUND_ERROR) continue;\n        throw statError;\n      }\n      if (Date.now() - startedAt >= LOCK_TIMEOUT_MS) {\n        throw new Error(`Timed out waiting for Antigravity state lock: ${lockPath}`);\n      }\n      await delay(LOCK_RETRY_MS);\n    }\n  }\n}\n\n/** True when `candidate` is `root` itself or sits underneath it. */\nexport function pathInside(root: string, candidate: string): boolean {\n  const relative = path.relative(root, candidate);\n  return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));\n}\n"],"mappings":"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"}