{"version":3,"file":"harness-CEuhOTr2.mjs","names":["LAUNCHER_BINARY","BROKER_MOUNT_DIRECTORY"],"sources":["../src/services/devcontainer/index.ts","../src/services/oauthCallback/index.ts","../src/services/sandboxEnvironment/index.ts","../src/services/sandboxPlan/index.ts","../src/services/engineProcess/index.ts","../src/services/devcontainer/runtime.ts","../src/services/loginPorts/index.ts","../src/services/runFlags/index.ts","../src/services/sandboxImage/index.ts","../src/services/sandboxImageTag/index.ts","../src/services/harness/index.ts"],"sourcesContent":["import path from 'node:path';\n\n/**\n * Relative locations the Dev Containers spec looks for a configuration.\n *\n * Ordered as the spec resolves them, so the first hit on disk wins.\n */\nexport const DEVCONTAINER_CONFIG_PATHS: readonly string[] = ['.devcontainer/devcontainer.json', '.devcontainer.json'];\n\nexport const DEVCONTAINER_DISABLED_ENV = 'DOOMPI_SANDBOX_DEVCONTAINER';\nexport const DEVCONTAINER_CLI_PACKAGE = '@devcontainers/cli@0.80.0';\n/** Marks containers this layer brought up, so a stale one can be told apart. */\nexport const DEVCONTAINER_ID_LABEL = 'doompi.sandbox.devcontainer';\n\nexport interface DevcontainerUpResult {\n  containerId?: string;\n  /** Where the CLI mounted the workspace inside the container. */\n  remoteWorkspaceFolder?: string;\n  error?: string;\n}\n\nexport interface DevcontainerUpOptions {\n  repoRoot: string;\n  /** Host directory holding the broker socket, bind-mounted when one is used. */\n  socketDirectory?: string;\n  /** Container path the socket directory is mounted at. */\n  socketTarget?: string;\n}\n\n/** Arguments for bringing the workspace's own dev container up. */\nexport function devcontainerUpArgs(options: DevcontainerUpOptions): string[] {\n  return [\n    'up',\n    '--workspace-folder',\n    options.repoRoot,\n    '--log-format',\n    'json',\n    ...(options.socketDirectory && options.socketTarget\n      ? ['--mount', `type=bind,source=${options.socketDirectory},target=${options.socketTarget}`]\n      : []),\n  ];\n}\n\n/**\n * Reads the container id out of the CLI's JSON log stream.\n *\n * The stream carries progress records too, so the result is the last record\n * that reports an outcome rather than the first line parsed.\n */\nexport function parseDevcontainerUp(stdout: string): DevcontainerUpResult {\n  let outcome:\n    | { outcome?: string; containerId?: string; remoteWorkspaceFolder?: string; message?: string; description?: string }\n    | undefined;\n  for (const line of stdout.split('\\n')) {\n    const trimmed = line.trim();\n    if (!trimmed.startsWith('{')) continue;\n    let parsed: unknown;\n    try {\n      parsed = JSON.parse(trimmed);\n    } catch {\n      continue;\n    }\n    if (typeof parsed === 'object' && parsed !== null && 'outcome' in parsed) {\n      outcome = parsed as typeof outcome;\n    }\n  }\n  if (!outcome) return { error: 'The dev container CLI reported no outcome.' };\n  if (outcome.outcome !== 'success') {\n    return { error: outcome.description || outcome.message || `Dev container start reported ${outcome.outcome}.` };\n  }\n  if (!outcome.containerId) return { error: 'The dev container started but reported no container id.' };\n  return { containerId: outcome.containerId, remoteWorkspaceFolder: outcome.remoteWorkspaceFolder };\n}\n\n/**\n * Maps a host path into the dev container's own workspace.\n *\n * A dev container mounts the workspace where its configuration says, commonly\n * /workspaces/<name>, so the host path the launch was given does not exist\n * inside and would fail the exec before the agent ever starts.\n */\nexport function containerWorkspacePath(repoRoot: string, cwd: string, remoteWorkspaceFolder: string): string {\n  // A plain prefix test would also map a sibling such as <repo>-other into the container.\n  const inside = path.relative(repoRoot, cwd);\n  const relative = inside === '' || inside.startsWith('..') || path.isAbsolute(inside) ? '' : inside;\n  return relative ? `${remoteWorkspaceFolder}/${relative.split('\\\\').join('/')}` : remoteWorkspaceFolder;\n}\n\n/**\n * Installs the distribution inside a dev container that lacks it.\n *\n * The container is the workspace's own, so it carries the project toolchain\n * rather than this layer's, and nothing guarantees DoomPi is present. `up`\n * reuses an existing container, so this is paid once for its lifetime.\n */\nexport function bootstrapCommand(version: string): string[] {\n  return [\n    'sh',\n    '-c',\n    `command -v doompi >/dev/null 2>&1 && exit 0; ` +\n      `command -v npm >/dev/null 2>&1 || { echo \"doompi-sandbox: this dev container has no npm to install DoomPi with\" >&2; exit 127; }; ` +\n      `npm install -g @agimon-ai/doompi@${version} >/dev/null`,\n  ];\n}\n\n/**\n * Rewrites forwarded host paths into the dev container's workspace.\n *\n * The launcher replays its resolved options, and several carry absolute host\n * paths: --cwd always, plugin and additional directories when set. Those\n * resolve in the built-in image, which mounts the repository at the path it\n * has on the host, but a dev container mounts it wherever its configuration\n * says, and the session would fail looking for a repository that is not there.\n */\nexport function mapForwardArgs(args: readonly string[], repoRoot: string, remoteWorkspaceFolder: string): string[] {\n  return args.map((argument) =>\n    argument === repoRoot || argument.startsWith(`${repoRoot}/`)\n      ? containerWorkspacePath(repoRoot, argument, remoteWorkspaceFolder)\n      : argument,\n  );\n}\n\nexport interface DevcontainerExecOptions {\n  containerId: string;\n  cwd: string;\n  environment: Readonly<Record<string, string>>;\n  hasTty: boolean;\n  command: readonly string[];\n}\n\n/**\n * Runs a command in the dev container through the engine rather than the CLI.\n *\n * `devcontainer exec` allocates no terminal, which a full screen TUI needs, so\n * the session is attached to the container the CLI already started.\n */\nexport function devcontainerExecArgs(options: DevcontainerExecOptions): string[] {\n  const environmentPairs = Object.entries(options.environment).sort(([left], [right]) => left.localeCompare(right));\n  return [\n    'exec',\n    '-i',\n    ...(options.hasTty ? ['-t'] : []),\n    '-w',\n    options.cwd,\n    ...environmentPairs.flatMap(([name, value]) => ['-e', `${name}=${value}`]),\n    options.containerId,\n    ...options.command,\n  ];\n}\n","/**\n * Fixed loopback ports Pi's OAuth flows listen on.\n *\n * A provider redirects the browser to `http://localhost:<port>`, so the host\n * port has to match exactly and cannot be remapped. Providers that bind an\n * ephemeral port instead, such as OpenRouter, cannot be published ahead of the\n * flow and are not covered.\n */\nexport const OAUTH_CALLBACK_PORTS: readonly number[] = [\n  1455, // openai-codex\n  1456, // radius\n  53692, // anthropic\n];\n\nexport const OAUTH_CALLBACK_HOST_ENV = 'PI_OAUTH_CALLBACK_HOST';\n\n/**\n * Address the in-container callback server binds.\n *\n * Pi binds loopback by default, which a published port cannot reach: the\n * engine forwards to the container's external interface, not its loopback.\n * Only the bind address changes; the redirect the provider sees stays\n * `localhost`.\n */\nexport const OAUTH_CONTAINER_BIND = '0.0.0.0';\n\n/** Publishes each reachable callback port back onto the host's loopback. */\nexport function oauthPublishArgs(ports: readonly number[]): string[] {\n  return ports.flatMap((port) => ['-p', `127.0.0.1:${port}:${port}`]);\n}\n","const EXACT_NAMES = new Set([\n  'TERM',\n  'COLORTERM',\n  'LANG',\n  'TZ',\n  'DOOMPI_PRESET',\n  'ELICITATION_SESSION_ID',\n  'HTTP_PROXY',\n  'HTTPS_PROXY',\n  'NO_PROXY',\n  'http_proxy',\n  'https_proxy',\n  'no_proxy',\n]);\nconst NAME_PREFIXES = ['LC_'];\nconst NAME_SUFFIXES = ['_API_KEY', '_AUTH_TOKEN', '_BASE_URL'];\n\n/** True for variables shaped like a provider credential or endpoint override. */\nexport function isCredentialEnvName(name: string): boolean {\n  return NAME_SUFFIXES.some((suffix) => name.endsWith(suffix));\n}\n\nfunction allowed(name: string): boolean {\n  return (\n    EXACT_NAMES.has(name) ||\n    NAME_PREFIXES.some((prefix) => name.startsWith(prefix)) ||\n    NAME_SUFFIXES.some((suffix) => name.endsWith(suffix))\n  );\n}\n\n/**\n * Projects the host environment down to what a sandboxed session may see.\n *\n * The list is an allowlist on purpose: everything a terminal accumulates\n * (agents, sockets, tokens, paths) stays on the host unless a rule names it.\n * Provider credentials pass through for now; the credential broker planned for\n * the client-server phase removes even those from the container.\n */\nexport function filterSandboxEnvironment(\n  environment: Readonly<Record<string, string | undefined>>,\n): Record<string, string> {\n  const filtered: Record<string, string> = {};\n  for (const [name, value] of Object.entries(environment)) {\n    if (value !== undefined && allowed(name)) filtered[name] = value;\n  }\n  return filtered;\n}\n","import { DOOMPI_SANDBOX_ENV } from '@agimon-ai/doompi-core/sandboxHarness';\n\nimport type { BrokerEndpoint, SandboxEngine, SandboxHostFacts } from '../../types/sandboxHarness';\nimport { OAUTH_CALLBACK_HOST_ENV, OAUTH_CONTAINER_BIND, oauthPublishArgs } from '../oauthCallback';\nimport {\n  BRIDGE_CONTAINER_PATH,\n  BROKER_ADDRESS_ENV,\n  BROKER_CONTAINER_PORT,\n  BROKER_HOST_GATEWAY,\n  BROKER_PORT_ENV,\n  BROKER_PROVIDERS_ENV,\n  BROKER_SOCKET_CONTAINER_PATH,\n  BROKER_SOCKET_ENV,\n} from '../sandboxBridge';\nimport { filterSandboxEnvironment, isCredentialEnvName } from '../sandboxEnvironment';\n\nconst CONTAINER_HOME = '/doompi-home';\nconst REPOSITORY_LABEL = 'doompi.sandbox.repo';\n// The harness binary, which accepts every forwarded flag; dpi hands its\n// arguments straight to Pi and would reject harness options like --preset.\nconst LAUNCHER_BINARY = 'doompi';\nconst NODE_BINARY = 'node';\nconst LINUX_PLATFORM = 'linux';\nconst ENABLED = '1';\nconst BROKER_MOUNT_DIRECTORY = '/run/doompi';\n\n/** Host broker facts the plan projects into mounts and environment. */\nexport interface SandboxPlanBroker {\n  endpoint: BrokerEndpoint;\n  token: string;\n  providers: readonly string[];\n  /** Credential variables whose value the container receives as the token. */\n  withheldEnv: readonly string[];\n}\n\nexport interface SandboxPlanInput {\n  repoRoot: string;\n  cwd: string;\n  forwardArgs: string[];\n  environment: Readonly<Record<string, string | undefined>>;\n  engine: SandboxEngine;\n  host: SandboxHostFacts;\n  /** Image the launch resolved, which also pins the image definition. */\n  imageTag: string;\n  /** Engine options the operator configured, such as an alternate runtime. */\n  runFlags?: readonly string[];\n  /** OAuth callback ports free on the host, published so a browser can reach them. */\n  loginPorts?: readonly number[];\n  broker?: SandboxPlanBroker;\n}\n\nexport interface SandboxPlan {\n  imageTag: string;\n  /** Full engine argv after the engine binary itself. */\n  runArgs: string[];\n}\n\n/**\n * Replaces every credential in the container with the broker's session token.\n *\n * Dropping all credential-shaped variables first is what makes the promise\n * hold for providers the broker does not carry: an unbrokered key would\n * otherwise stay readable inside the container.\n */\nfunction brokeredEnvironment(filtered: Record<string, string>, broker: SandboxPlanBroker): Record<string, string> {\n  const environment: Record<string, string> = {};\n  for (const [name, value] of Object.entries(filtered)) {\n    if (!isCredentialEnvName(name)) environment[name] = value;\n  }\n  for (const name of broker.withheldEnv) environment[name] = broker.token;\n  if (broker.endpoint.transport === 'unix') {\n    environment[BROKER_SOCKET_ENV] = BROKER_SOCKET_CONTAINER_PATH;\n    // Only the bridge reads this, and only a socket needs the bridge.\n    environment[BROKER_PORT_ENV] = String(BROKER_CONTAINER_PORT);\n  } else {\n    environment[BROKER_ADDRESS_ENV] = `${BROKER_HOST_GATEWAY}:${broker.endpoint.port}`;\n  }\n  environment[BROKER_PROVIDERS_ENV] = broker.providers.join(',');\n  return environment;\n}\n\n/**\n * Grants the container the one route it needs to reach the broker.\n *\n * A mounted socket needs no network at all. TCP needs the engine's host\n * gateway named explicitly, because it is not resolvable by default on every\n * engine.\n */\nfunction brokerAccessArgs(broker: SandboxPlanBroker | undefined): string[] {\n  if (!broker) return [];\n  if (broker.endpoint.transport === 'unix') {\n    return ['-v', `${broker.endpoint.socketDirectory}:${BROKER_MOUNT_DIRECTORY}`];\n  }\n  return ['--add-host', `${BROKER_HOST_GATEWAY}:host-gateway`];\n}\n\n/**\n * Projects one launch into a disposable engine run.\n *\n * The container is ephemeral; persistence lives in two named volumes: the\n * container home (Pi sessions, sync cache) and a shadow over the repository's\n * `.pi` store, which keeps Linux installs from corrupting the host's own\n * platform-specific packages on the shared workspace mount.\n */\n/**\n * The environment a sandboxed session runs with, whichever container hosts it.\n *\n * Shared with the dev container path, which builds no run arguments of its own\n * but must apply the same allowlist and the same credential substitution.\n */\nexport function containerEnvironment(\n  environment: Readonly<Record<string, string | undefined>>,\n  broker?: SandboxPlanBroker,\n  loginPorts: readonly number[] = [],\n): Record<string, string> {\n  const filtered = filterSandboxEnvironment(environment);\n  const resolved = broker ? brokeredEnvironment(filtered, broker) : filtered;\n  resolved[DOOMPI_SANDBOX_ENV] = ENABLED;\n  if (loginPorts.length > 0) resolved[OAUTH_CALLBACK_HOST_ENV] = OAUTH_CONTAINER_BIND;\n  return resolved;\n}\n\nexport function buildSandboxPlan(input: SandboxPlanInput): SandboxPlan {\n  const { repoRoot, cwd, engine, host, broker, imageTag } = input;\n  const loginPorts = input.loginPorts ?? [];\n  const environmentPairs = Object.entries(containerEnvironment(input.environment, broker, loginPorts)).sort(\n    ([left], [right]) => left.localeCompare(right),\n  );\n\n  const runArgs = [\n    'run',\n    '--rm',\n    '-i',\n    ...(host.hasTty ? ['-t'] : []),\n    '--label',\n    `${REPOSITORY_LABEL}=${repoRoot}`,\n    '-v',\n    `${repoRoot}:${repoRoot}`,\n    '-v',\n    `${host.repoKey}-home:${CONTAINER_HOME}`,\n    '-v',\n    `${host.repoKey}-pi:${repoRoot}/.pi`,\n    ...brokerAccessArgs(broker),\n    ...oauthPublishArgs(loginPorts),\n    '-w',\n    cwd,\n    '-e',\n    `HOME=${CONTAINER_HOME}`,\n    ...environmentPairs.flatMap(([name, value]) => ['-e', `${name}=${value}`]),\n    ...(host.platform === LINUX_PLATFORM && host.userId !== undefined\n      ? [\n          '--user',\n          `${host.userId}:${host.groupId ?? host.userId}`,\n          ...(engine === 'podman' ? ['--userns=keep-id'] : []),\n        ]\n      : []),\n    // Last before the image so a configured option wins over the defaults\n    // above it, and cannot be mistaken for the image or its command.\n    ...(input.runFlags ?? []),\n    imageTag,\n    // Only a mounted socket needs the bridge: a broker on a host port is\n    // addressed directly, so the launcher runs unwrapped.\n    ...(broker?.endpoint.transport === 'unix' ? [NODE_BINARY, BRIDGE_CONTAINER_PATH] : []),\n    LAUNCHER_BINARY,\n    ...input.forwardArgs,\n  ];\n\n  return { imageTag, runArgs };\n}\n","import { spawn } from 'node:child_process';\n\nimport type { EngineCaptureResult, EngineProcessRunner, EngineRunOptions } from '../../types/sandboxHarness';\n\nconst INHERIT = 'inherit';\nconst PIPE = 'pipe';\nconst IGNORE = 'ignore';\nconst FORWARDED_SIGNALS: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGHUP'];\n\n/** Spawns the container engine, keeping the session's terminal attached. */\nexport class SpawnEngineProcessRunner implements EngineProcessRunner {\n  run(command: string, args: string[], options: EngineRunOptions = {}): Promise<number> {\n    return new Promise((resolve, reject) => {\n      const child = spawn(command, args, {\n        stdio: options.input === undefined ? INHERIT : [PIPE, INHERIT, INHERIT],\n      });\n      if (options.input !== undefined) child.stdin?.end(options.input);\n\n      // With a shared terminal the engine sees Ctrl-C itself; forwarding covers\n      // detached automation runs, and a duplicate signal is harmless.\n      const forwarders = FORWARDED_SIGNALS.map((signal) => {\n        const forward = (): void => {\n          child.kill(signal);\n        };\n        process.on(signal, forward);\n        return { signal, forward };\n      });\n      const cleanup = (): void => {\n        for (const { signal, forward } of forwarders) process.off(signal, forward);\n      };\n\n      child.once('error', (error) => {\n        cleanup();\n        reject(error);\n      });\n      child.once('exit', (code, signal) => {\n        cleanup();\n        resolve(code ?? (signal ? 1 : 0));\n      });\n    });\n  }\n\n  capture(command: string, args: string[]): Promise<EngineCaptureResult | undefined> {\n    return new Promise((resolve) => {\n      const child = spawn(command, args, { stdio: [IGNORE, PIPE, IGNORE] });\n      let stdout = '';\n      child.stdout?.on('data', (chunk: Buffer | string) => {\n        stdout += chunk.toString();\n      });\n      child.once('error', () => {\n        resolve(undefined);\n      });\n      child.once('exit', (code) => {\n        resolve({ exitCode: code ?? 1, stdout });\n      });\n    });\n  }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n  bootstrapCommand,\n  containerWorkspacePath,\n  mapForwardArgs,\n  DEVCONTAINER_CLI_PACKAGE,\n  DEVCONTAINER_CONFIG_PATHS,\n  devcontainerExecArgs,\n  devcontainerUpArgs,\n  parseDevcontainerUp,\n} from '.';\nimport type { EngineProcessRunner, SandboxEngine } from '../../types/sandboxHarness';\n\nconst DEVCONTAINER_BINARY = 'devcontainer';\nconst NPX_BINARY = 'npx';\nconst LAUNCHER_BINARY = 'doompi';\nconst BROKER_MOUNT_DIRECTORY = '/run/doompi';\n\n/** Locates the workspace's own dev container configuration, if it has one. */\nexport function findDevcontainerConfig(repoRoot: string): string | undefined {\n  for (const relative of DEVCONTAINER_CONFIG_PATHS) {\n    const candidate = path.join(repoRoot, relative);\n    if (fs.existsSync(candidate)) return candidate;\n  }\n  return undefined;\n}\n\n/**\n * Resolves how to invoke the Dev Containers CLI.\n *\n * A local install is preferred so a launch does not depend on the network;\n * npx is the fallback, pinned so the tool cannot change underneath a session.\n */\nexport async function resolveDevcontainerCli(\n  runner: EngineProcessRunner,\n): Promise<{ command: string; prefix: string[] }> {\n  const probe = await runner.capture(DEVCONTAINER_BINARY, ['--version']);\n  if (probe?.exitCode === 0) return { command: DEVCONTAINER_BINARY, prefix: [] };\n  return { command: NPX_BINARY, prefix: ['-y', DEVCONTAINER_CLI_PACKAGE] };\n}\n\nexport interface DevcontainerSessionOptions {\n  repoRoot: string;\n  cwd: string;\n  forwardArgs: readonly string[];\n  environment: Readonly<Record<string, string>>;\n  engine: SandboxEngine;\n  runner: EngineProcessRunner;\n  version: string;\n  hasTty: boolean;\n  /** Host directory holding the broker socket, when the broker uses one. */\n  socketDirectory?: string;\n  onProgress?: (message: string) => void;\n}\n\n/**\n * Runs the session inside the workspace's own dev container.\n *\n * The container is brought up by the Dev Containers CLI, so the file decides\n * the image, features, mounts and lifecycle hooks: the session gets the\n * project's real toolchain, and this layer's isolation guarantees do not\n * apply beyond the environment it passes in.\n */\nexport async function runDevcontainerSession(options: DevcontainerSessionOptions): Promise<number> {\n  const cli = await resolveDevcontainerCli(options.runner);\n  options.onProgress?.('starting the workspace dev container');\n\n  const upArgs = devcontainerUpArgs({\n    repoRoot: options.repoRoot,\n    socketDirectory: options.socketDirectory,\n    socketTarget: options.socketDirectory ? BROKER_MOUNT_DIRECTORY : undefined,\n  });\n  const up = await options.runner.capture(cli.command, [...cli.prefix, ...upArgs]);\n  if (!up) {\n    throw new Error('The Dev Containers CLI could not be started. Install @devcontainers/cli, or make npx available.');\n  }\n  const { containerId, remoteWorkspaceFolder, error } = parseDevcontainerUp(up.stdout);\n  if (!containerId) throw new Error(`The dev container did not start: ${error ?? `exit ${up.exitCode}`}`);\n\n  const bootstrap = await options.runner.run(options.engine, [\n    'exec',\n    containerId,\n    ...bootstrapCommand(options.version),\n  ]);\n  if (bootstrap !== 0) {\n    throw new Error(\n      `DoomPi could not be installed in the dev container (exit ${bootstrap}). ` +\n        'Add it to the dev container, or unset the dev container preference to use the built-in image.',\n    );\n  }\n\n  options.onProgress?.(`attaching to dev container ${containerId.slice(0, 12)}`);\n  return options.runner.run(\n    options.engine,\n    devcontainerExecArgs({\n      containerId,\n      cwd: remoteWorkspaceFolder\n        ? containerWorkspacePath(options.repoRoot, options.cwd, remoteWorkspaceFolder)\n        : options.cwd,\n      environment: options.environment,\n      hasTty: options.hasTty,\n      command: [\n        LAUNCHER_BINARY,\n        ...(remoteWorkspaceFolder\n          ? mapForwardArgs(options.forwardArgs, options.repoRoot, remoteWorkspaceFolder)\n          : options.forwardArgs),\n      ],\n    }),\n  );\n}\n","import net from 'node:net';\n\nimport { OAUTH_CALLBACK_PORTS } from '../oauthCallback';\n\nconst LOOPBACK = '127.0.0.1';\n\nfunction portIsFree(port: number): Promise<boolean> {\n  return new Promise((resolve) => {\n    const probe = net.createServer();\n    probe.once('error', () => resolve(false));\n    probe.listen(port, LOOPBACK, () => {\n      probe.close(() => resolve(true));\n    });\n  });\n}\n\n/**\n * Reports which OAuth callback ports this launch may publish.\n *\n * A taken port means another sandbox already holds it, so publishing would\n * fail the whole launch over a login the session may never attempt. Skipping\n * it costs only the ability to log in from inside this particular container.\n */\nexport async function availableLoginPorts(ports: readonly number[] = OAUTH_CALLBACK_PORTS): Promise<number[]> {\n  const free: number[] = [];\n  for (const port of ports) {\n    if (await portIsFree(port)) free.push(port);\n  }\n  return free;\n}\n","/**\n * Splits configured engine options into argv entries.\n *\n * Whitespace separated rather than shell parsed: the engine is spawned without\n * a shell, so quoting rules would only invent a syntax the caller does not\n * actually get.\n */\nexport function parseRunFlags(configured: string | undefined): string[] {\n  return (configured ?? '').split(/\\s+/).filter((flag) => flag.length > 0);\n}\n\nconst OPTION_FORM = /^--?[^\\s=]+(=.*)?$/s;\n\n/**\n * Requires every configured entry to be a self-contained option.\n *\n * Separated values cannot be told from positional arguments without knowing\n * each option's arity, and a stray positional would silently replace the image\n * or its command. Demanding `--flag=value` removes the ambiguity instead of\n * guessing at it.\n */\nexport function assertRunFlags(flags: readonly string[], optionName: string): void {\n  const invalid = flags.find((flag) => !OPTION_FORM.test(flag));\n  if (invalid !== undefined) {\n    throw new Error(\n      `${optionName} accepts engine options in --flag or --flag=value form; \"${invalid}\" is neither. ` +\n        'Write a value as part of its option, for example --runtime=runsc.',\n    );\n  }\n}\n","import { BRIDGE_CONTAINER_PATH, BRIDGE_FILE_NAME } from '../sandboxBridge';\n\nconst IMAGE_REPOSITORY = 'doompi-sandbox';\nconst TAG_SAFE = /[^A-Za-z0-9_.-]/g;\n\n/**\n * Names one image build: a distribution version plus its image definition.\n *\n * The digest matters as much as the version. Editing the Dockerfile or the\n * bridge without it would silently reuse a cached image that predates the\n * change.\n */\nexport function formatImageTag(version: string, digest: string): string {\n  return `${IMAGE_REPOSITORY}:v${version.replaceAll(TAG_SAFE, '-')}-${digest}`;\n}\n\n/**\n * Self-contained Linux image for sandboxed sessions.\n *\n * nano is present because Pi falls back to it for the external editor when\n * VISUAL and EDITOR are unset, which is always the case here: a host editor\n * variable naming a desktop application would resolve to a binary the\n * container does not have, so it is deliberately not forwarded.\n *\n * The distribution installs from the registry rather than mounting host\n * modules: host installs carry platform-specific binaries a Linux container\n * cannot run. DOOMPI_SANDBOX is baked in so nothing that reaches a shell in\n * the container can present itself as an unsandboxed session.\n */\nexport function sandboxDockerfile(): string {\n  return [\n    'FROM node:22-bookworm-slim',\n    'RUN apt-get update \\\\',\n    '  && apt-get install -y --no-install-recommends ca-certificates curl git nano openssh-client ripgrep \\\\',\n    '  && rm -rf /var/lib/apt/lists/*',\n    'ARG DOOMPI_VERSION',\n    'RUN npm install -g @agimon-ai/doompi@${DOOMPI_VERSION}',\n    `COPY ${BRIDGE_FILE_NAME} ${BRIDGE_CONTAINER_PATH}`,\n    '# World-writable so the launch can map any host user id onto it.',\n    'RUN mkdir -m 0777 /doompi-home',\n    'ENV DOOMPI_SANDBOX=1 HOME=/doompi-home',\n    '',\n  ].join('\\n');\n}\n\n/**\n * Image for the cockpit container, which holds the hub rather than one session.\n *\n * Kept separate from the sandbox image rather than folded into it. The cockpit\n * needs the web package and a tunnel client, and making every interactive\n * `--sandbox` launch pay for an extra apt repository and a larger download to\n * get them would be a poor trade.\n *\n * Installing `@agimon-ai/doompi-web` is enough for all three programs: its\n * `@agimon-ai/doompi` dependency owns both the session server executable and\n * the agent runtime.\n */\nexport function cockpitDockerfile(): string {\n  return [\n    'FROM node:22-bookworm-slim',\n    'RUN apt-get update \\\\',\n    '  && apt-get install -y --no-install-recommends ca-certificates curl git gnupg nano openssh-client ripgrep \\\\',\n    '  && rm -rf /var/lib/apt/lists/*',\n    // The tunnel runs beside the hub rather than on the host, so the cockpit\n    // owns its own remote access and all of that state stays in one place.\n    'RUN curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \\\\',\n    '  -o /usr/share/keyrings/cloudflare-main.gpg \\\\',\n    ' && echo \"deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bookworm main\" \\\\',\n    '  > /etc/apt/sources.list.d/cloudflared.list \\\\',\n    ' && apt-get update && apt-get install -y --no-install-recommends cloudflared \\\\',\n    ' && rm -rf /var/lib/apt/lists/*',\n    'ARG DOOMPI_VERSION',\n    'RUN npm install -g @agimon-ai/doompi-web@${DOOMPI_VERSION}',\n    '# World-writable so the launch can map any host user id onto it.',\n    'RUN mkdir -m 0777 /doompi-home',\n    'ENV DOOMPI_SANDBOX=1 HOME=/doompi-home',\n    '',\n  ].join('\\n');\n}\n","import { createHash } from 'node:crypto';\n\nimport { sandboxBridgeSource } from '../sandboxBridge';\nimport { cockpitDockerfile, formatImageTag, sandboxDockerfile } from '../sandboxImage';\n\nconst DIGEST_LENGTH = 8;\n\n/** Tags the image with the distribution version and a digest of its definition. */\nexport function sandboxImageTag(version: string): string {\n  const digest = createHash('sha256')\n    .update(sandboxDockerfile())\n    .update(sandboxBridgeSource())\n    .digest('hex')\n    .slice(0, DIGEST_LENGTH);\n  return formatImageTag(version, digest);\n}\n\n/**\n * Tags the cockpit image separately from the sandbox one.\n *\n * Same digest scheme, different definition, so editing either Dockerfile\n * invalidates only its own image.\n */\nexport function cockpitImageTag(version: string): string {\n  const digest = createHash('sha256').update(cockpitDockerfile()).digest('hex').slice(0, DIGEST_LENGTH);\n  return formatImageTag(`${version}-cockpit`, digest);\n}\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport type { SandboxLaunchRequest } from '@agimon-ai/doompi-core/sandboxHarness';\n\nimport type { EngineProcessRunner, SandboxEngine, SandboxHostFacts } from '../../types/sandboxHarness';\nimport { startBroker, type RunningBroker } from '../brokerHost';\nimport { DEVCONTAINER_DISABLED_ENV } from '../devcontainer';\nimport { findDevcontainerConfig, runDevcontainerSession } from '../devcontainer/runtime';\nimport { SpawnEngineProcessRunner } from '../engineProcess';\nimport { availableLoginPorts } from '../loginPorts';\nimport { OAUTH_CALLBACK_PORTS } from '../oauthCallback';\nimport { assertRunFlags, parseRunFlags } from '../runFlags';\nimport { BRIDGE_FILE_NAME, sandboxBridgeSource } from '../sandboxBridge';\nimport { sandboxDockerfile } from '../sandboxImage';\nimport { sandboxImageTag } from '../sandboxImageTag';\nimport { buildSandboxPlan, containerEnvironment } from '../sandboxPlan';\n\nconst ENGINE_ENV = 'DOOMPI_SANDBOX_ENGINE';\nconst BROKER_DISABLED_ENV = 'DOOMPI_SANDBOX_BROKER';\nconst RUN_FLAGS_ENV = 'DOOMPI_SANDBOX_RUN_FLAGS';\nconst DISABLED_VALUE = '0';\n// Every entry takes docker's run syntax, which is what the plan emits.\nconst ENGINES: SandboxEngine[] = ['docker', 'podman', 'nerdctl', 'finch'];\nconst DOCKERFILE_NAME = 'Dockerfile';\nconst MODES_FILE = path.join('.doom', 'modes.yaml');\nconst LOCAL_PACKAGE_PATTERN = /^\\s*(?:-|name:)\\s*[\"']?\\.{1,2}\\//m;\nconst REPO_KEY_LENGTH = 12;\n\n// Self-reference instead of a relative path: the file depth differs between\n// running from src and from the built dist tree.\nconst require = createRequire(import.meta.url);\nconst SELF_MANIFEST = '@agimon-ai/doompi-sandbox/package.json';\n\nexport interface SandboxLauncherDependencies {\n  runner?: EngineProcessRunner;\n  /** Seam for tests; defaults to probing the real OAuth callback ports. */\n  loginPorts?: () => Promise<number[]>;\n  /** Seam for tests; defaults to the real host broker. */\n  startBroker?: (options: {\n    environment: Readonly<Record<string, string | undefined>>;\n    onDenied?: (reason: string) => void;\n  }) => Promise<RunningBroker | undefined>;\n  /** Distribution version override; defaults to this package's own version. */\n  version?: string;\n  hostFacts?: Partial<SandboxHostFacts>;\n}\n\nfunction distributionVersion(): string {\n  return (require(SELF_MANIFEST) as { version: string }).version;\n}\n\n/** Fixed per-repository directory a reused dev container can keep mounted. */\nfunction brokerDirectory(repoRoot: string): string {\n  return path.join(os.tmpdir(), `${repoKey(repoRoot)}-broker`);\n}\n\nfunction repoKey(repoRoot: string): string {\n  return `doompi-sandbox-${createHash('sha256').update(repoRoot).digest('hex').slice(0, REPO_KEY_LENGTH)}`;\n}\n\nexport async function detectEngine(\n  runner: EngineProcessRunner,\n  environment: Readonly<Record<string, string | undefined>>,\n): Promise<SandboxEngine> {\n  const configured = environment[ENGINE_ENV]?.trim();\n  if (configured) {\n    if ((ENGINES as string[]).includes(configured)) return configured as SandboxEngine;\n    throw new Error(`${ENGINE_ENV} must be one of: ${ENGINES.join(', ')}.`);\n  }\n  for (const engine of ENGINES) {\n    const probe = await runner.capture(engine, ['--version']);\n    if (probe?.exitCode === 0) return engine;\n  }\n  throw new Error(`No container engine found. Install one of ${ENGINES.join(', ')}, or set ${ENGINE_ENV}.`);\n}\n\n/**\n * Builds the image if the engine does not already have it.\n *\n * Takes its definition rather than assuming one, because the interactive\n * sandbox and the cockpit container are two different images built the same\n * way. The tag already carries a digest of the definition, so a changed\n * Dockerfile misses the inspect and rebuilds.\n */\nexport async function ensureImage(\n  runner: EngineProcessRunner,\n  engine: SandboxEngine,\n  version: string,\n  onProgress: ((message: string) => void) | undefined,\n  definition: { tag: string; dockerfile: string; files?: Readonly<Record<string, string>> },\n): Promise<string> {\n  const tag = definition.tag;\n  const inspected = await runner.capture(engine, ['image', 'inspect', tag]);\n  if (inspected?.exitCode === 0) return tag;\n\n  onProgress?.(`building ${tag} (first run for this DoomPi version)`);\n  const context = fs.mkdtempSync(path.join(os.tmpdir(), 'doompi-sandbox-build-'));\n  try {\n    fs.writeFileSync(path.join(context, DOCKERFILE_NAME), definition.dockerfile);\n    for (const [name, contents] of Object.entries(definition.files ?? {})) {\n      fs.writeFileSync(path.join(context, name), contents);\n    }\n    const exitCode = await runner.run(engine, [\n      'build',\n      '-t',\n      tag,\n      '--build-arg',\n      `DOOMPI_VERSION=${version}`,\n      context,\n    ]);\n    if (exitCode !== 0) throw new Error(`Building the ${tag} image failed with exit code ${exitCode}.`);\n  } finally {\n    fs.rmSync(context, { recursive: true, force: true });\n  }\n  return tag;\n}\n\nfunction warnAboutLocalPackages(repoRoot: string, onProgress: ((message: string) => void) | undefined): void {\n  let modes: string;\n  try {\n    modes = fs.readFileSync(path.join(repoRoot, MODES_FILE), 'utf8');\n  } catch {\n    return;\n  }\n  if (LOCAL_PACKAGE_PATTERN.test(modes)) {\n    onProgress?.(\n      'modes.yaml declares local workspace packages; their platform-specific dependencies ' +\n        'must be installed inside the container before they can load',\n    );\n  }\n}\n\n/** Builds the launcher with injectable seams for tests. */\nexport function createSandboxLauncher(dependencies: SandboxLauncherDependencies = {}) {\n  const runner = dependencies.runner ?? new SpawnEngineProcessRunner();\n  const beginBroker = dependencies.startBroker ?? startBroker;\n  const resolveLoginPorts = dependencies.loginPorts ?? availableLoginPorts;\n  return {\n    async launchSandbox(request: SandboxLaunchRequest): Promise<number> {\n      const engine = await detectEngine(runner, request.environment);\n      const runFlags = parseRunFlags(request.environment[RUN_FLAGS_ENV]);\n      assertRunFlags(runFlags, RUN_FLAGS_ENV);\n      request.onProgress?.(`using ${engine}${runFlags.length > 0 ? ` with ${runFlags.join(' ')}` : ''}`);\n\n      const version = dependencies.version ?? distributionVersion();\n      // A workspace that describes its own container is describing the\n      // toolchain its agent needs, so that container wins over the built-in\n      // image. Nothing here is built or pulled until that choice is made.\n      const devcontainer =\n        request.environment[DEVCONTAINER_DISABLED_ENV] === DISABLED_VALUE\n          ? undefined\n          : findDevcontainerConfig(request.repoRoot);\n      const imageTag = devcontainer\n        ? undefined\n        : await ensureImage(runner, engine, version, request.onProgress, {\n            tag: sandboxImageTag(version),\n            dockerfile: sandboxDockerfile(),\n            files: { [BRIDGE_FILE_NAME]: sandboxBridgeSource() },\n          });\n      if (devcontainer) {\n        request.onProgress?.(`using the workspace dev container from ${path.relative(request.repoRoot, devcontainer)}`);\n        request.onProgress?.(\n          'its configuration owns the mounts and run arguments, so this is not an isolation boundary',\n        );\n      } else {\n        warnAboutLocalPackages(request.repoRoot, request.onProgress);\n      }\n\n      const broker =\n        request.environment[BROKER_DISABLED_ENV] === DISABLED_VALUE\n          ? undefined\n          : await beginBroker({\n              environment: request.environment,\n              onDenied: (reason) => request.onProgress?.(`broker refused a call: ${reason}`),\n              // A reused dev container keeps the mounts it was created with,\n              // so the socket has to be at the same path on every launch.\n              ...(devcontainer ? { socketDirectory: brokerDirectory(request.repoRoot) } : {}),\n            });\n      if (broker) {\n        request.onProgress?.(`brokering ${broker.providers.join(', ')}; provider keys stay on the host`);\n      }\n\n      const loginPorts = devcontainer ? [] : await resolveLoginPorts();\n      if (!devcontainer && loginPorts.length < OAUTH_CALLBACK_PORTS.length) {\n        request.onProgress?.(\n          'some OAuth callback ports are already taken; /login inside this sandbox may not complete',\n        );\n      }\n\n      try {\n        const hasTty = process.stdin.isTTY === true && process.stdout.isTTY === true;\n        if (devcontainer) {\n          return await runDevcontainerSession({\n            repoRoot: request.repoRoot,\n            cwd: request.cwd,\n            forwardArgs: request.forwardArgs,\n            environment: containerEnvironment(request.environment, broker),\n            engine,\n            runner,\n            version,\n            hasTty,\n            ...(broker?.endpoint.transport === 'unix' ? { socketDirectory: broker.endpoint.socketDirectory } : {}),\n            ...(request.onProgress ? { onProgress: request.onProgress } : {}),\n          });\n        }\n\n        const host: SandboxHostFacts = {\n          hasTty,\n          platform: process.platform,\n          userId: process.getuid?.(),\n          groupId: process.getgid?.(),\n          repoKey: repoKey(request.repoRoot),\n          version,\n          ...dependencies.hostFacts,\n        };\n        const plan = buildSandboxPlan({\n          repoRoot: request.repoRoot,\n          cwd: request.cwd,\n          forwardArgs: request.forwardArgs,\n          environment: request.environment,\n          engine,\n          host,\n          imageTag: imageTag as string,\n          runFlags,\n          loginPorts,\n          broker,\n        });\n        request.onProgress?.(`starting contained session in ${imageTag}`);\n        return await runner.run(engine, plan.runArgs);\n      } finally {\n        await broker?.stop();\n      }\n    },\n  };\n}\n\n/** Contract entry the DoomPi harness resolves for `--sandbox`. */\nexport function launchSandbox(request: SandboxLaunchRequest): Promise<number> {\n  return createSandboxLauncher().launchSandbox(request);\n}\n"],"mappings":"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"}