@agimon-ai/doompi-sandbox 0.0.1-alpha.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +224 -0
  3. package/dist/apiContracts.cjs +17 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +13 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/brokerHost-BeGtmnK8.mjs +302 -0
  10. package/dist/brokerHost-BeGtmnK8.mjs.map +1 -0
  11. package/dist/brokerHost-BzffXeb2.cjs +340 -0
  12. package/dist/brokerHost-BzffXeb2.cjs.map +1 -0
  13. package/dist/cockpitHarness.cjs +298 -0
  14. package/dist/cockpitHarness.cjs.map +1 -0
  15. package/dist/cockpitHarness.d.mts +26 -0
  16. package/dist/cockpitHarness.d.mts.map +1 -0
  17. package/dist/cockpitHarness.mjs +295 -0
  18. package/dist/cockpitHarness.mjs.map +1 -0
  19. package/dist/extensionService-BnIZjdJN.cjs +25 -0
  20. package/dist/extensionService-BnIZjdJN.cjs.map +1 -0
  21. package/dist/extensionService-DiVpVLn9.mjs +20 -0
  22. package/dist/extensionService-DiVpVLn9.mjs.map +1 -0
  23. package/dist/extensions/mcp.cjs +26 -0
  24. package/dist/extensions/mcp.cjs.map +1 -0
  25. package/dist/extensions/mcp.d.mts +5 -0
  26. package/dist/extensions/mcp.d.mts.map +1 -0
  27. package/dist/extensions/mcp.mjs +21 -0
  28. package/dist/extensions/mcp.mjs.map +1 -0
  29. package/dist/extensions/pi.cjs +85 -0
  30. package/dist/extensions/pi.cjs.map +1 -0
  31. package/dist/extensions/pi.d.mts +6 -0
  32. package/dist/extensions/pi.d.mts.map +1 -0
  33. package/dist/extensions/pi.mjs +80 -0
  34. package/dist/extensions/pi.mjs.map +1 -0
  35. package/dist/extensions/server.cjs +92 -0
  36. package/dist/extensions/server.cjs.map +1 -0
  37. package/dist/extensions/server.d.mts +5 -0
  38. package/dist/extensions/server.d.mts.map +1 -0
  39. package/dist/extensions/server.mjs +87 -0
  40. package/dist/extensions/server.mjs.map +1 -0
  41. package/dist/harness-CEuhOTr2.mjs +716 -0
  42. package/dist/harness-CEuhOTr2.mjs.map +1 -0
  43. package/dist/harness-fK8CSaU1.cjs +785 -0
  44. package/dist/harness-fK8CSaU1.cjs.map +1 -0
  45. package/dist/index-CBt-PlVG.d.mts +22 -0
  46. package/dist/index-CBt-PlVG.d.mts.map +1 -0
  47. package/dist/index.cjs +3 -0
  48. package/dist/index.d.mts +2 -0
  49. package/dist/index.mjs +2 -0
  50. package/dist/sandboxBridge-B3FRZkC2.cjs +131 -0
  51. package/dist/sandboxBridge-B3FRZkC2.cjs.map +1 -0
  52. package/dist/sandboxBridge-D6RAncmy.mjs +72 -0
  53. package/dist/sandboxBridge-D6RAncmy.mjs.map +1 -0
  54. package/dist/sandboxHarness-BIvuRMio.d.mts +38 -0
  55. package/dist/sandboxHarness-BIvuRMio.d.mts.map +1 -0
  56. package/dist/sandboxHarness.cjs +4 -0
  57. package/dist/sandboxHarness.d.mts +38 -0
  58. package/dist/sandboxHarness.d.mts.map +1 -0
  59. package/dist/sandboxHarness.mjs +2 -0
  60. package/dist/sandboxResources-5pvXyBW9.mjs +79 -0
  61. package/dist/sandboxResources-5pvXyBW9.mjs.map +1 -0
  62. package/dist/sandboxResources-Djxj5l1V.cjs +120 -0
  63. package/dist/sandboxResources-Djxj5l1V.cjs.map +1 -0
  64. package/llms.txt +11 -0
  65. package/package.json +132 -0
  66. package/src/prompts/doompi-use-sandbox/SKILL.md +14 -0
@@ -0,0 +1 @@
1
+ {"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":";;;;;;;;;;;;;;;;AAOA,MAAa,4BAA+C,CAAC,mCAAmC,oBAAoB;AAEpH,MAAa,4BAA4B;AACzC,MAAa,2BAA2B;;AAoBxC,SAAgB,mBAAmB,SAA0C;CAC3E,OAAO;EACL;EACA;EACA,QAAQ;EACR;EACA;EACA,GAAI,QAAQ,mBAAmB,QAAQ,eACnC,CAAC,WAAW,oBAAoB,QAAQ,gBAAgB,UAAU,QAAQ,cAAc,IACxF,CAAC;CACP;AACF;;;;;;;AAQA,SAAgB,oBAAoB,QAAsC;CACxE,IAAI;CAGJ,KAAK,MAAM,QAAQ,OAAO,MAAM,IAAI,GAAG;EACrC,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,QAAQ,WAAW,GAAG,GAAG;EAC9B,IAAI;EACJ,IAAI;GACF,SAAS,KAAK,MAAM,OAAO;EAC7B,QAAQ;GACN;EACF;EACA,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,aAAa,QAChE,UAAU;CAEd;CACA,IAAI,CAAC,SAAS,OAAO,EAAE,OAAO,6CAA6C;CAC3E,IAAI,QAAQ,YAAY,WACtB,OAAO,EAAE,OAAO,QAAQ,eAAe,QAAQ,WAAW,gCAAgC,QAAQ,QAAQ,GAAG;CAE/G,IAAI,CAAC,QAAQ,aAAa,OAAO,EAAE,OAAO,0DAA0D;CACpG,OAAO;EAAE,aAAa,QAAQ;EAAa,uBAAuB,QAAQ;CAAsB;AAClG;;;;;;;;AASA,SAAgB,uBAAuB,UAAkB,KAAa,uBAAuC;CAE3G,MAAM,SAAS,KAAK,SAAS,UAAU,GAAG;CAC1C,MAAM,WAAW,WAAW,MAAM,OAAO,WAAW,IAAI,KAAK,KAAK,WAAW,MAAM,IAAI,KAAK;CAC5F,OAAO,WAAW,GAAG,sBAAsB,GAAG,SAAS,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AACnF;;;;;;;;AASA,SAAgB,iBAAiB,SAA2B;CAC1D,OAAO;EACL;EACA;EACA,mNAEsC,QAAQ;CAChD;AACF;;;;;;;;;;AAWA,SAAgB,eAAe,MAAyB,UAAkB,uBAAyC;CACjH,OAAO,KAAK,KAAK,aACf,aAAa,YAAY,SAAS,WAAW,GAAG,SAAS,EAAE,IACvD,uBAAuB,UAAU,UAAU,qBAAqB,IAChE,QACN;AACF;;;;;;;AAgBA,SAAgB,qBAAqB,SAA4C;CAC/E,MAAM,mBAAmB,OAAO,QAAQ,QAAQ,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,KAAK,CAAC;CAChH,OAAO;EACL;EACA;EACA,GAAI,QAAQ,SAAS,CAAC,IAAI,IAAI,CAAC;EAC/B;EACA,QAAQ;EACR,GAAG,iBAAiB,SAAS,CAAC,MAAM,WAAW,CAAC,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;EACzE,QAAQ;EACR,GAAG,QAAQ;CACb;AACF;;;;;;;;;;;AC5IA,MAAa,uBAA0C;CACrD;CACA;CACA;AACF;AAEA,MAAa,0BAA0B;;;;;;;;;AAUvC,MAAa,uBAAuB;;AAGpC,SAAgB,iBAAiB,OAAoC;CACnE,OAAO,MAAM,SAAS,SAAS,CAAC,MAAM,aAAa,KAAK,GAAG,MAAM,CAAC;AACpE;;;AC7BA,MAAM,8BAAc,IAAI,IAAI;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,gBAAgB,CAAC,KAAK;AAC5B,MAAM,gBAAgB;CAAC;CAAY;CAAe;AAAW;;AAG7D,SAAgB,oBAAoB,MAAuB;CACzD,OAAO,cAAc,MAAM,WAAW,KAAK,SAAS,MAAM,CAAC;AAC7D;AAEA,SAAS,QAAQ,MAAuB;CACtC,OACE,YAAY,IAAI,IAAI,KACpB,cAAc,MAAM,WAAW,KAAK,WAAW,MAAM,CAAC,KACtD,cAAc,MAAM,WAAW,KAAK,SAAS,MAAM,CAAC;AAExD;;;;;;;;;AAUA,SAAgB,yBACd,aACwB;CACxB,MAAM,WAAmC,CAAC;CAC1C,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,WAAW,GACpD,IAAI,UAAU,KAAA,KAAa,QAAQ,IAAI,GAAG,SAAS,QAAQ;CAE7D,OAAO;AACT;;;AC9BA,MAAM,iBAAiB;AACvB,MAAM,mBAAmB;AAGzB,MAAMA,oBAAkB;AACxB,MAAM,cAAc;AACpB,MAAM,iBAAiB;AACvB,MAAM,UAAU;AAChB,MAAMC,2BAAyB;;;;;;;;AAwC/B,SAAS,oBAAoB,UAAkC,QAAmD;CAChH,MAAM,cAAsC,CAAC;CAC7C,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,GACjD,IAAI,CAAC,oBAAoB,IAAI,GAAG,YAAY,QAAQ;CAEtD,KAAK,MAAM,QAAQ,OAAO,aAAa,YAAY,QAAQ,OAAO;CAClE,IAAI,OAAO,SAAS,cAAc,QAAQ;EACxC,YAAY,qBAAqB;EAEjC,YAAY,mBAAmB,OAAO,qBAAqB;CAC7D,OACE,YAAY,sBAAsB,GAAG,oBAAoB,GAAG,OAAO,SAAS;CAE9E,YAAY,wBAAwB,OAAO,UAAU,KAAK,GAAG;CAC7D,OAAO;AACT;;;;;;;;AASA,SAAS,iBAAiB,QAAiD;CACzE,IAAI,CAAC,QAAQ,OAAO,CAAC;CACrB,IAAI,OAAO,SAAS,cAAc,QAChC,OAAO,CAAC,MAAM,GAAG,OAAO,SAAS,gBAAgB,GAAGA,0BAAwB;CAE9E,OAAO,CAAC,cAAc,GAAG,oBAAoB,cAAc;AAC7D;;;;;;;;;;;;;;;AAgBA,SAAgB,qBACd,aACA,QACA,aAAgC,CAAC,GACT;CACxB,MAAM,WAAW,yBAAyB,WAAW;CACrD,MAAM,WAAW,SAAS,oBAAoB,UAAU,MAAM,IAAI;CAClE,SAAS,sBAAsB;CAC/B,IAAI,WAAW,SAAS,GAAG,SAAS,2BAA2B;CAC/D,OAAO;AACT;AAEA,SAAgB,iBAAiB,OAAsC;CACrE,MAAM,EAAE,UAAU,KAAK,QAAQ,MAAM,QAAQ,aAAa;CAC1D,MAAM,aAAa,MAAM,cAAc,CAAC;CACxC,MAAM,mBAAmB,OAAO,QAAQ,qBAAqB,MAAM,aAAa,QAAQ,UAAU,CAAC,CAAC,CAAC,MAClG,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,KAAK,CAC/C;CAwCA,OAAO;EAAE;EAAU,SAAA;GArCjB;GACA;GACA;GACA,GAAI,KAAK,SAAS,CAAC,IAAI,IAAI,CAAC;GAC5B;GACA,GAAG,iBAAiB,GAAG;GACvB;GACA,GAAG,SAAS,GAAG;GACf;GACA,GAAG,KAAK,QAAQ,QAAQ;GACxB;GACA,GAAG,KAAK,QAAQ,MAAM,SAAS;GAC/B,GAAG,iBAAiB,MAAM;GAC1B,GAAG,iBAAiB,UAAU;GAC9B;GACA;GACA;GACA,QAAQ;GACR,GAAG,iBAAiB,SAAS,CAAC,MAAM,WAAW,CAAC,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;GACzE,GAAI,KAAK,aAAa,kBAAkB,KAAK,WAAW,KAAA,IACpD;IACE;IACA,GAAG,KAAK,OAAO,GAAG,KAAK,WAAW,KAAK;IACvC,GAAI,WAAW,WAAW,CAAC,kBAAkB,IAAI,CAAC;GACpD,IACA,CAAC;GAGL,GAAI,MAAM,YAAY,CAAC;GACvB;GAGA,GAAI,QAAQ,SAAS,cAAc,SAAS,CAAC,aAAa,qBAAqB,IAAI,CAAC;GACpFD;GACA,GAAG,MAAM;EAGc;CAAE;AAC7B;;;ACpKA,MAAM,UAAU;AAChB,MAAM,OAAO;AACb,MAAM,SAAS;AACf,MAAM,oBAAsC;CAAC;CAAU;CAAW;AAAQ;;AAG1E,IAAa,2BAAb,MAAqE;CACnE,IAAI,SAAiB,MAAgB,UAA4B,CAAC,GAAoB;EACpF,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,QAAQ,MAAM,SAAS,MAAM,EACjC,OAAO,QAAQ,UAAU,KAAA,IAAY,UAAU;IAAC;IAAM;IAAS;GAAO,EACxE,CAAC;GACD,IAAI,QAAQ,UAAU,KAAA,GAAW,MAAM,OAAO,IAAI,QAAQ,KAAK;GAI/D,MAAM,aAAa,kBAAkB,KAAK,WAAW;IACnD,MAAM,gBAAsB;KAC1B,MAAM,KAAK,MAAM;IACnB;IACA,QAAQ,GAAG,QAAQ,OAAO;IAC1B,OAAO;KAAE;KAAQ;IAAQ;GAC3B,CAAC;GACD,MAAM,gBAAsB;IAC1B,KAAK,MAAM,EAAE,QAAQ,aAAa,YAAY,QAAQ,IAAI,QAAQ,OAAO;GAC3E;GAEA,MAAM,KAAK,UAAU,UAAU;IAC7B,QAAQ;IACR,OAAO,KAAK;GACd,CAAC;GACD,MAAM,KAAK,SAAS,MAAM,WAAW;IACnC,QAAQ;IACR,QAAQ,SAAS,SAAS,IAAI,EAAE;GAClC,CAAC;EACH,CAAC;CACH;CAEA,QAAQ,SAAiB,MAA0D;EACjF,OAAO,IAAI,SAAS,YAAY;GAC9B,MAAM,QAAQ,MAAM,SAAS,MAAM,EAAE,OAAO;IAAC;IAAQ;IAAM;GAAM,EAAE,CAAC;GACpE,IAAI,SAAS;GACb,MAAM,QAAQ,GAAG,SAAS,UAA2B;IACnD,UAAU,MAAM,SAAS;GAC3B,CAAC;GACD,MAAM,KAAK,eAAe;IACxB,QAAQ,KAAA,CAAS;GACnB,CAAC;GACD,MAAM,KAAK,SAAS,SAAS;IAC3B,QAAQ;KAAE,UAAU,QAAQ;KAAG;IAAO,CAAC;GACzC,CAAC;EACH,CAAC;CACH;AACF;;;AC1CA,MAAM,sBAAsB;AAC5B,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,yBAAyB;;AAG/B,SAAgB,uBAAuB,UAAsC;CAC3E,KAAK,MAAM,YAAY,2BAA2B;EAChD,MAAM,YAAY,KAAK,KAAK,UAAU,QAAQ;EAC9C,IAAI,GAAG,WAAW,SAAS,GAAG,OAAO;CACvC;AAEF;;;;;;;AAQA,eAAsB,uBACpB,QACgD;CAEhD,KAAI,MADgB,OAAO,QAAQ,qBAAqB,CAAC,WAAW,CAAC,EAAA,EAC1D,aAAa,GAAG,OAAO;EAAE,SAAS;EAAqB,QAAQ,CAAC;CAAE;CAC7E,OAAO;EAAE,SAAS;EAAY,QAAQ,CAAC,MAAM,wBAAwB;CAAE;AACzE;;;;;;;;;AAwBA,eAAsB,uBAAuB,SAAsD;CACjG,MAAM,MAAM,MAAM,uBAAuB,QAAQ,MAAM;CACvD,QAAQ,aAAa,sCAAsC;CAE3D,MAAM,SAAS,mBAAmB;EAChC,UAAU,QAAQ;EAClB,iBAAiB,QAAQ;EACzB,cAAc,QAAQ,kBAAkB,yBAAyB,KAAA;CACnE,CAAC;CACD,MAAM,KAAK,MAAM,QAAQ,OAAO,QAAQ,IAAI,SAAS,CAAC,GAAG,IAAI,QAAQ,GAAG,MAAM,CAAC;CAC/E,IAAI,CAAC,IACH,MAAM,IAAI,MAAM,iGAAiG;CAEnH,MAAM,EAAE,aAAa,uBAAuB,UAAU,oBAAoB,GAAG,MAAM;CACnF,IAAI,CAAC,aAAa,MAAM,IAAI,MAAM,oCAAoC,SAAS,QAAQ,GAAG,YAAY;CAEtG,MAAM,YAAY,MAAM,QAAQ,OAAO,IAAI,QAAQ,QAAQ;EACzD;EACA;EACA,GAAG,iBAAiB,QAAQ,OAAO;CACrC,CAAC;CACD,IAAI,cAAc,GAChB,MAAM,IAAI,MACR,4DAA4D,UAAU,iGAExE;CAGF,QAAQ,aAAa,8BAA8B,YAAY,MAAM,GAAG,EAAE,GAAG;CAC7E,OAAO,QAAQ,OAAO,IACpB,QAAQ,QACR,qBAAqB;EACnB;EACA,KAAK,wBACD,uBAAuB,QAAQ,UAAU,QAAQ,KAAK,qBAAqB,IAC3E,QAAQ;EACZ,aAAa,QAAQ;EACrB,QAAQ,QAAQ;EAChB,SAAS,CACP,iBACA,GAAI,wBACA,eAAe,QAAQ,aAAa,QAAQ,UAAU,qBAAqB,IAC3E,QAAQ,WACd;CACF,CAAC,CACH;AACF;;;AC3GA,MAAM,WAAW;AAEjB,SAAS,WAAW,MAAgC;CAClD,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,QAAQ,IAAI,aAAa;EAC/B,MAAM,KAAK,eAAe,QAAQ,KAAK,CAAC;EACxC,MAAM,OAAO,MAAM,gBAAgB;GACjC,MAAM,YAAY,QAAQ,IAAI,CAAC;EACjC,CAAC;CACH,CAAC;AACH;;;;;;;;AASA,eAAsB,oBAAoB,QAA2B,sBAAyC;CAC5G,MAAM,OAAiB,CAAC;CACxB,KAAK,MAAM,QAAQ,OACjB,IAAI,MAAM,WAAW,IAAI,GAAG,KAAK,KAAK,IAAI;CAE5C,OAAO;AACT;;;;;;;;;;ACtBA,SAAgB,cAAc,YAA0C;CACtE,QAAQ,cAAc,GAAA,CAAI,MAAM,KAAK,CAAC,CAAC,QAAQ,SAAS,KAAK,SAAS,CAAC;AACzE;AAEA,MAAM,cAAc;;;;;;;;;AAUpB,SAAgB,eAAe,OAA0B,YAA0B;CACjF,MAAM,UAAU,MAAM,MAAM,SAAS,CAAC,YAAY,KAAK,IAAI,CAAC;CAC5D,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,MACR,GAAG,WAAW,2DAA2D,QAAQ,gFAEnF;AAEJ;;;AC3BA,MAAM,mBAAmB;AACzB,MAAM,WAAW;;;;;;;;AASjB,SAAgB,eAAe,SAAiB,QAAwB;CACtE,OAAO,GAAG,iBAAiB,IAAI,QAAQ,WAAW,UAAU,GAAG,EAAE,GAAG;AACtE;;;;;;;;;;;;;;AAeA,SAAgB,oBAA4B;CAC1C,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ,iBAAiB,GAAG;EAC5B;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;;;;;;;;;;AAcA,SAAgB,oBAA4B;CAC1C,OAAO;EACL;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;ACzEA,MAAM,gBAAgB;;AAGtB,SAAgB,gBAAgB,SAAyB;CAMvD,OAAO,eAAe,SALP,WAAW,QAAQ,CAAC,CAChC,OAAO,kBAAkB,CAAC,CAAC,CAC3B,OAAO,oBAAoB,CAAC,CAAC,CAC7B,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,aACmB,CAAM;AACvC;;;;;;;AAQA,SAAgB,gBAAgB,SAAyB;CACvD,MAAM,SAAS,WAAW,QAAQ,CAAC,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,aAAa;CACpG,OAAO,eAAe,GAAG,QAAQ,WAAW,MAAM;AACpD;;;ACLA,MAAM,aAAa;AACnB,MAAM,sBAAsB;AAC5B,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AAEvB,MAAM,UAA2B;CAAC;CAAU;CAAU;CAAW;AAAO;AACxE,MAAM,kBAAkB;AACxB,MAAM,aAAa,KAAK,KAAK,SAAS,YAAY;AAClD,MAAM,wBAAwB;AAC9B,MAAM,kBAAkB;AAIxB,MAAM,UAAU,cAAc,YAAY,GAAG;AAC7C,MAAM,gBAAgB;AAgBtB,SAAS,sBAA8B;CACrC,OAAQ,QAAQ,aAAa,CAAC,CAAyB;AACzD;;AAGA,SAAS,gBAAgB,UAA0B;CACjD,OAAO,KAAK,KAAK,GAAG,OAAO,GAAG,GAAG,QAAQ,QAAQ,EAAE,QAAQ;AAC7D;AAEA,SAAS,QAAQ,UAA0B;CACzC,OAAO,kBAAkB,WAAW,QAAQ,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,eAAe;AACvG;AAEA,eAAsB,aACpB,QACA,aACwB;CACxB,MAAM,aAAa,YAAY,WAAW,EAAE,KAAK;CACjD,IAAI,YAAY;EACd,IAAK,QAAqB,SAAS,UAAU,GAAG,OAAO;EACvD,MAAM,IAAI,MAAM,GAAG,WAAW,mBAAmB,QAAQ,KAAK,IAAI,EAAE,EAAE;CACxE;CACA,KAAK,MAAM,UAAU,SAEnB,KAAI,MADgB,OAAO,QAAQ,QAAQ,CAAC,WAAW,CAAC,EAAA,EAC7C,aAAa,GAAG,OAAO;CAEpC,MAAM,IAAI,MAAM,6CAA6C,QAAQ,KAAK,IAAI,EAAE,WAAW,WAAW,EAAE;AAC1G;;;;;;;;;AAUA,eAAsB,YACpB,QACA,QACA,SACA,YACA,YACiB;CACjB,MAAM,MAAM,WAAW;CAEvB,KAAI,MADoB,OAAO,QAAQ,QAAQ;EAAC;EAAS;EAAW;CAAG,CAAC,EAAA,EACzD,aAAa,GAAG,OAAO;CAEtC,aAAa,YAAY,IAAI,qCAAqC;CAClE,MAAM,UAAU,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,uBAAuB,CAAC;CAC9E,IAAI;EACF,GAAG,cAAc,KAAK,KAAK,SAAS,eAAe,GAAG,WAAW,UAAU;EAC3E,KAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,WAAW,SAAS,CAAC,CAAC,GAClE,GAAG,cAAc,KAAK,KAAK,SAAS,IAAI,GAAG,QAAQ;EAErD,MAAM,WAAW,MAAM,OAAO,IAAI,QAAQ;GACxC;GACA;GACA;GACA;GACA,kBAAkB;GAClB;EACF,CAAC;EACD,IAAI,aAAa,GAAG,MAAM,IAAI,MAAM,gBAAgB,IAAI,+BAA+B,SAAS,EAAE;CACpG,UAAU;EACR,GAAG,OAAO,SAAS;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACrD;CACA,OAAO;AACT;AAEA,SAAS,uBAAuB,UAAkB,YAA2D;CAC3G,IAAI;CACJ,IAAI;EACF,QAAQ,GAAG,aAAa,KAAK,KAAK,UAAU,UAAU,GAAG,MAAM;CACjE,QAAQ;EACN;CACF;CACA,IAAI,sBAAsB,KAAK,KAAK,GAClC,aACE,gJAEF;AAEJ;;AAGA,SAAgB,sBAAsB,eAA4C,CAAC,GAAG;CACpF,MAAM,SAAS,aAAa,UAAU,IAAI,yBAAyB;CACnE,MAAM,cAAc,aAAa,eAAe;CAChD,MAAM,oBAAoB,aAAa,cAAc;CACrD,OAAO,EACL,MAAM,cAAc,SAAgD;EAClE,MAAM,SAAS,MAAM,aAAa,QAAQ,QAAQ,WAAW;EAC7D,MAAM,WAAW,cAAc,QAAQ,YAAY,cAAc;EACjE,eAAe,UAAU,aAAa;EACtC,QAAQ,aAAa,SAAS,SAAS,SAAS,SAAS,IAAI,SAAS,SAAS,KAAK,GAAG,MAAM,IAAI;EAEjG,MAAM,UAAU,aAAa,WAAW,oBAAoB;EAI5D,MAAM,eACJ,QAAQ,YAAA,mCAA2C,iBAC/C,KAAA,IACA,uBAAuB,QAAQ,QAAQ;EAC7C,MAAM,WAAW,eACb,KAAA,IACA,MAAM,YAAY,QAAQ,QAAQ,SAAS,QAAQ,YAAY;GAC7D,KAAK,gBAAgB,OAAO;GAC5B,YAAY,kBAAkB;GAC9B,OAAO,GAAG,mBAAmB,oBAAoB,EAAE;EACrD,CAAC;EACL,IAAI,cAAc;GAChB,QAAQ,aAAa,0CAA0C,KAAK,SAAS,QAAQ,UAAU,YAAY,GAAG;GAC9G,QAAQ,aACN,2FACF;EACF,OACE,uBAAuB,QAAQ,UAAU,QAAQ,UAAU;EAG7D,MAAM,SACJ,QAAQ,YAAY,yBAAyB,iBACzC,KAAA,IACA,MAAM,YAAY;GAChB,aAAa,QAAQ;GACrB,WAAW,WAAW,QAAQ,aAAa,0BAA0B,QAAQ;GAG7E,GAAI,eAAe,EAAE,iBAAiB,gBAAgB,QAAQ,QAAQ,EAAE,IAAI,CAAC;EAC/E,CAAC;EACP,IAAI,QACF,QAAQ,aAAa,aAAa,OAAO,UAAU,KAAK,IAAI,EAAE,iCAAiC;EAGjG,MAAM,aAAa,eAAe,CAAC,IAAI,MAAM,kBAAkB;EAC/D,IAAI,CAAC,gBAAgB,WAAW,SAAS,qBAAqB,QAC5D,QAAQ,aACN,0FACF;EAGF,IAAI;GACF,MAAM,SAAS,QAAQ,MAAM,UAAU,QAAQ,QAAQ,OAAO,UAAU;GACxE,IAAI,cACF,OAAO,MAAM,uBAAuB;IAClC,UAAU,QAAQ;IAClB,KAAK,QAAQ;IACb,aAAa,QAAQ;IACrB,aAAa,qBAAqB,QAAQ,aAAa,MAAM;IAC7D;IACA;IACA;IACA;IACA,GAAI,QAAQ,SAAS,cAAc,SAAS,EAAE,iBAAiB,OAAO,SAAS,gBAAgB,IAAI,CAAC;IACpG,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;GACjE,CAAC;GAGH,MAAM,OAAyB;IAC7B;IACA,UAAU,QAAQ;IAClB,QAAQ,QAAQ,SAAS;IACzB,SAAS,QAAQ,SAAS;IAC1B,SAAS,QAAQ,QAAQ,QAAQ;IACjC;IACA,GAAG,aAAa;GAClB;GACA,MAAM,OAAO,iBAAiB;IAC5B,UAAU,QAAQ;IAClB,KAAK,QAAQ;IACb,aAAa,QAAQ;IACrB,aAAa,QAAQ;IACrB;IACA;IACU;IACV;IACA;IACA;GACF,CAAC;GACD,QAAQ,aAAa,iCAAiC,UAAU;GAChE,OAAO,MAAM,OAAO,IAAI,QAAQ,KAAK,OAAO;EAC9C,UAAU;GACR,MAAM,QAAQ,KAAK;EACrB;CACF,EACF;AACF;;AAGA,SAAgB,cAAc,SAAgD;CAC5E,OAAO,sBAAsB,CAAC,CAAC,cAAc,OAAO;AACtD"}