apex-code 0.0.6 → 0.1.0
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.
- package/CHANGELOG.md +26 -0
- package/README.md +7 -8
- package/dist/cli/args.d.ts +0 -12
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -32
- package/dist/cli/args.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -130
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +0 -8
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/delegation/agents.d.ts.map +1 -1
- package/dist/core/delegation/agents.js +2 -1
- package/dist/core/delegation/agents.js.map +1 -1
- package/dist/core/delegation/runtime.d.ts +6 -14
- package/dist/core/delegation/runtime.d.ts.map +1 -1
- package/dist/core/delegation/runtime.js +1 -2
- package/dist/core/delegation/runtime.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +3 -4
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/source-runtime.d.ts +4 -5
- package/dist/core/extensions/source-runtime.d.ts.map +1 -1
- package/dist/core/extensions/source-runtime.js +4 -5
- package/dist/core/extensions/source-runtime.js.map +1 -1
- package/dist/core/formatter-lifecycle.d.ts +2 -1
- package/dist/core/formatter-lifecycle.d.ts.map +1 -1
- package/dist/core/formatter-lifecycle.js +2 -1
- package/dist/core/formatter-lifecycle.js.map +1 -1
- package/dist/core/hooks/types.d.ts +2 -3
- package/dist/core/hooks/types.d.ts.map +1 -1
- package/dist/core/hooks/types.js +2 -3
- package/dist/core/hooks/types.js.map +1 -1
- package/dist/core/http-idle-timeout.d.ts +3 -5
- package/dist/core/http-idle-timeout.d.ts.map +1 -1
- package/dist/core/http-idle-timeout.js +3 -5
- package/dist/core/http-idle-timeout.js.map +1 -1
- package/dist/core/mcp/config.d.ts +18 -1
- package/dist/core/mcp/config.d.ts.map +1 -1
- package/dist/core/mcp/config.js +24 -7
- package/dist/core/mcp/config.js.map +1 -1
- package/dist/core/mcp/connector.d.ts +2 -3
- package/dist/core/mcp/connector.d.ts.map +1 -1
- package/dist/core/mcp/connector.js.map +1 -1
- package/dist/core/mcp/oauth/authorize.d.ts +2 -0
- package/dist/core/mcp/oauth/authorize.d.ts.map +1 -1
- package/dist/core/mcp/oauth/authorize.js +5 -2
- package/dist/core/mcp/oauth/authorize.js.map +1 -1
- package/dist/core/mcp/oauth/mcp-token.d.ts +4 -6
- package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -1
- package/dist/core/mcp/oauth/mcp-token.js +5 -7
- package/dist/core/mcp/oauth/mcp-token.js.map +1 -1
- package/dist/core/mcp/runtime.d.ts +1 -0
- package/dist/core/mcp/runtime.d.ts.map +1 -1
- package/dist/core/mcp/runtime.js +2 -1
- package/dist/core/mcp/runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +0 -15
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions/store.d.ts +6 -11
- package/dist/core/permissions/store.d.ts.map +1 -1
- package/dist/core/permissions/store.js +23 -51
- package/dist/core/permissions/store.js.map +1 -1
- package/dist/core/project-resources.d.ts +62 -0
- package/dist/core/project-resources.d.ts.map +1 -0
- package/dist/core/project-resources.js +42 -0
- package/dist/core/project-resources.js.map +1 -0
- package/dist/core/sdk.d.ts +1 -9
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -35
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-share.d.ts +6 -8
- package/dist/core/session-share.d.ts.map +1 -1
- package/dist/core/session-share.js +9 -12
- package/dist/core/session-share.js.map +1 -1
- package/dist/core/settings-manager.d.ts +0 -21
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -12
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +0 -47
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/web-fetch.d.ts.map +1 -1
- package/dist/core/tools/web-fetch.js +4 -8
- package/dist/core/tools/web-fetch.js.map +1 -1
- package/dist/core/tools/web-search-exa.d.ts +3 -6
- package/dist/core/tools/web-search-exa.d.ts.map +1 -1
- package/dist/core/tools/web-search-exa.js +5 -8
- package/dist/core/tools/web-search-exa.js.map +1 -1
- package/dist/core/trust-manager.d.ts +6 -5
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +42 -19
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/web-search-provider.d.ts +0 -10
- package/dist/core/web-search-provider.d.ts.map +1 -1
- package/dist/core/web-search-provider.js +2 -16
- package/dist/core/web-search-provider.js.map +1 -1
- package/dist/main.d.ts +0 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -3
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +0 -29
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +0 -25
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +3 -52
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +0 -2
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +0 -2
- package/dist/utils/version-check.js.map +1 -1
- package/docs/settings.md +2 -7
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -2
- package/dist/core/sandbox/bwrap-arguments.d.ts +0 -41
- package/dist/core/sandbox/bwrap-arguments.d.ts.map +0 -1
- package/dist/core/sandbox/bwrap-arguments.js +0 -136
- package/dist/core/sandbox/bwrap-arguments.js.map +0 -1
- package/dist/core/sandbox/child-entry.d.ts +0 -2
- package/dist/core/sandbox/child-entry.d.ts.map +0 -1
- package/dist/core/sandbox/child-entry.js +0 -24
- package/dist/core/sandbox/child-entry.js.map +0 -1
- package/dist/core/sandbox/cli-launch.d.ts +0 -162
- package/dist/core/sandbox/cli-launch.d.ts.map +0 -1
- package/dist/core/sandbox/cli-launch.js +0 -366
- package/dist/core/sandbox/cli-launch.js.map +0 -1
- package/dist/core/sandbox/cli-supervisor.d.ts +0 -31
- package/dist/core/sandbox/cli-supervisor.d.ts.map +0 -1
- package/dist/core/sandbox/cli-supervisor.js +0 -147
- package/dist/core/sandbox/cli-supervisor.js.map +0 -1
- package/dist/core/sandbox/default-hosts.d.ts +0 -28
- package/dist/core/sandbox/default-hosts.d.ts.map +0 -1
- package/dist/core/sandbox/default-hosts.js +0 -69
- package/dist/core/sandbox/default-hosts.js.map +0 -1
- package/dist/core/sandbox/full-access.d.ts +0 -28
- package/dist/core/sandbox/full-access.d.ts.map +0 -1
- package/dist/core/sandbox/full-access.js +0 -63
- package/dist/core/sandbox/full-access.js.map +0 -1
- package/dist/core/sandbox/git-identity.d.ts +0 -57
- package/dist/core/sandbox/git-identity.d.ts.map +0 -1
- package/dist/core/sandbox/git-identity.js +0 -78
- package/dist/core/sandbox/git-identity.js.map +0 -1
- package/dist/core/sandbox/host-approval.d.ts +0 -42
- package/dist/core/sandbox/host-approval.d.ts.map +0 -1
- package/dist/core/sandbox/host-approval.js +0 -99
- package/dist/core/sandbox/host-approval.js.map +0 -1
- package/dist/core/sandbox/linux-backend.d.ts +0 -48
- package/dist/core/sandbox/linux-backend.d.ts.map +0 -1
- package/dist/core/sandbox/linux-backend.js +0 -328
- package/dist/core/sandbox/linux-backend.js.map +0 -1
- package/dist/core/sandbox/macos-backend.d.ts +0 -30
- package/dist/core/sandbox/macos-backend.d.ts.map +0 -1
- package/dist/core/sandbox/macos-backend.js +0 -385
- package/dist/core/sandbox/macos-backend.js.map +0 -1
- package/dist/core/sandbox/network-proxy.d.ts +0 -38
- package/dist/core/sandbox/network-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/network-proxy.js +0 -153
- package/dist/core/sandbox/network-proxy.js.map +0 -1
- package/dist/core/sandbox/network-refusal.d.ts +0 -24
- package/dist/core/sandbox/network-refusal.d.ts.map +0 -1
- package/dist/core/sandbox/network-refusal.js +0 -72
- package/dist/core/sandbox/network-refusal.js.map +0 -1
- package/dist/core/sandbox/policy.d.ts +0 -40
- package/dist/core/sandbox/policy.d.ts.map +0 -1
- package/dist/core/sandbox/policy.js +0 -55
- package/dist/core/sandbox/policy.js.map +0 -1
- package/dist/core/sandbox/profiles.d.ts +0 -25
- package/dist/core/sandbox/profiles.d.ts.map +0 -1
- package/dist/core/sandbox/profiles.js +0 -23
- package/dist/core/sandbox/profiles.js.map +0 -1
- package/dist/core/sandbox/rpc/command-client.d.ts +0 -28
- package/dist/core/sandbox/rpc/command-client.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/command-client.js +0 -86
- package/dist/core/sandbox/rpc/command-client.js.map +0 -1
- package/dist/core/sandbox/rpc/command-proxy.d.ts +0 -48
- package/dist/core/sandbox/rpc/command-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/command-proxy.js +0 -120
- package/dist/core/sandbox/rpc/command-proxy.js.map +0 -1
- package/dist/core/sandbox/rpc/credential-client.d.ts +0 -20
- package/dist/core/sandbox/rpc/credential-client.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/credential-client.js +0 -170
- package/dist/core/sandbox/rpc/credential-client.js.map +0 -1
- package/dist/core/sandbox/rpc/credential-proxy.d.ts +0 -44
- package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/credential-proxy.js +0 -287
- package/dist/core/sandbox/rpc/credential-proxy.js.map +0 -1
- package/dist/core/sandbox/rpc/framing.d.ts +0 -35
- package/dist/core/sandbox/rpc/framing.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/framing.js +0 -114
- package/dist/core/sandbox/rpc/framing.js.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts +0 -47
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-helper.js +0 -222
- package/dist/core/sandbox/rpc/git-credential-helper.js.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +0 -66
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-proxy.js +0 -209
- package/dist/core/sandbox/rpc/git-credential-proxy.js.map +0 -1
- package/dist/core/sandbox/supervisor-temp.d.ts +0 -18
- package/dist/core/sandbox/supervisor-temp.d.ts.map +0 -1
- package/dist/core/sandbox/supervisor-temp.js +0 -21
- package/dist/core/sandbox/supervisor-temp.js.map +0 -1
- package/dist/core/sandbox/supervisor.d.ts +0 -62
- package/dist/core/sandbox/supervisor.d.ts.map +0 -1
- package/dist/core/sandbox/supervisor.js +0 -32
- package/dist/core/sandbox/supervisor.js.map +0 -1
- package/dist/core/sandbox/terminal-handoff.d.ts +0 -68
- package/dist/core/sandbox/terminal-handoff.d.ts.map +0 -1
- package/dist/core/sandbox/terminal-handoff.js +0 -221
- package/dist/core/sandbox/terminal-handoff.js.map +0 -1
- package/dist/core/sandbox/terminal-size.d.ts +0 -53
- package/dist/core/sandbox/terminal-size.d.ts.map +0 -1
- package/dist/core/sandbox/terminal-size.js +0 -126
- package/dist/core/sandbox/terminal-size.js.map +0 -1
- package/dist/core/sandbox/violations.d.ts +0 -23
- package/dist/core/sandbox/violations.d.ts.map +0 -1
- package/dist/core/sandbox/violations.js +0 -30
- package/dist/core/sandbox/violations.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-launch.js","sourceRoot":"","sources":["../../../src/core/sandbox/cli-launch.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tchmodSync,\n\texistsSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\treadFileSync,\n\trealpathSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { join, sep } from \"node:path\";\nimport type { ParsedCliCommand } from \"../../cli/args.ts\";\nimport type { HostToolBinary } from \"../../utils/tools-manager.ts\";\nimport { SettingsManager } from \"../settings-manager.ts\";\nimport { resolveWebSearchHost } from \"../web-search-provider.ts\";\nimport { resolveDefaultAllowedHosts } from \"./default-hosts.ts\";\nimport type { SandboxLaunch } from \"./supervisor.ts\";\nimport { supervisorTempDirectory } from \"./supervisor-temp.ts\";\n\n/**\n * OS containment is the normal startup path for every parsed command that can\n * construct an agent session. Classification consumes the same typed parse result\n * as execution, so option values and text after `--` cannot impersonate metadata.\n */\nexport function requiresSandboxedChild(command: ParsedCliCommand): boolean {\n\treturn command.kind === \"session\";\n}\n\nexport interface SandboxedCliLaunch extends SandboxLaunch {\n\treadonly environment: NodeJS.ProcessEnv;\n\t/**\n\t * The supervisor's own 0700 state directory, outside every writable root the child\n\t * has.\n\t */\n\treadonly supervisorStateDirectory: string;\n}\n\nexport function getSandboxSessionDirectory(workspace: string): string {\n\treturn join(workspace, \".apex-code\", \"sandbox-sessions\");\n}\n\n/**\n * Env var naming the file that carries the host's `policy` and `user` permission scopes\n * into the child (PS.3).\n *\n * Deliberately absent from `SAFE_CHILD_ENVIRONMENT_KEYS`: only the supervisor's own\n * resolution may name it, never the invoking shell and never a project file (ADR 0016).\n */\nexport const POLICY_SNAPSHOT_PATH_VARIABLE = \"APEX_CODE_POLICY_SNAPSHOT_PATH\";\n\n/**\n * The marker asserting that an OS boundary is actually enforcing this process (PS.5).\n *\n * Set only on the supervisor's own launch environment for a child it is about to contain.\n * It is not in the child-environment allowlist, so a value of this name in the invoking\n * shell never survives into a child; the supervisor's own value is the only one that can.\n * `core/sdk.ts` reads it for `sandbox: \"required\"` and keeps the same two literals -- a\n * marker spelled two ways would be a drift the SDK could not detect at runtime, so\n * `test/sdk-sandbox-contract.test.ts` pins them to each other.\n */\nexport const SANDBOX_ENFORCEMENT_MARKER_VARIABLE = \"APEX_CODE_SANDBOX_ENFORCED\";\nexport const SANDBOX_ENFORCEMENT_MARKER_VALUE = \"1\";\n\nexport interface SupervisorStateDirectory {\n\t/** A 0700 directory under the supervisor temp root, outside every child-writable root. */\n\treadonly path: string;\n\treadonly dispose: () => void;\n}\n\n/**\n * Allocate the directory the supervisor keeps its own launch state in.\n *\n * Everything the supervisor writes for a launch -- the terminal handoff latch, the\n * published terminal size, the network relay, the git credential helper, the permission\n * snapshot -- used to live in `workspace/.apex-code/sandbox-state`, which is inside the\n * one writable bind the child has. A child that planted a symlink there redirected the\n * *next* launch's supervisor writes onto arbitrary host paths, and could rewrite the\n * scripts the supervisor was about to run. Under the supervisor temp root the child sees\n * these files through the read-only root bind (Linux) or Seatbelt's write denial outside\n * the workspace (macOS), so it can neither replace them nor plant a link where one goes.\n */\nexport function createSupervisorStateDirectory(): SupervisorStateDirectory {\n\tconst path = mkdtempSync(join(supervisorTempDirectory(), `apex-supervisor-${process.pid}-`), { encoding: \"utf8\" });\n\tchmodSync(path, 0o700);\n\treturn {\n\t\tpath,\n\t\tdispose: () => {\n\t\t\ttry {\n\t\t\t\trmSync(path, { force: true, recursive: true });\n\t\t\t} catch {\n\t\t\t\t// A leftover directory under the supervisor's own temp root is not worth\n\t\t\t\t// failing a session teardown over.\n\t\t\t}\n\t\t},\n\t};\n}\n\n/**\n * The managed policy file this host resolves, read before the child's environment exists.\n *\n * `cli-launch`'s child-environment allowlist filters `APEX_CODE_POLICY_PATH` out, so a\n * host that configured a custom managed policy had it silently disappear at the boundary\n * and the child fell back to the system path -- the config-projection defect PS.3 closes.\n * Resolution here mirrors `core/permissions/store.ts`'s own default exactly.\n */\nexport function resolveSupervisorPolicyPath(environment: NodeJS.ProcessEnv = process.env): string {\n\tif (environment.APEX_CODE_POLICY_PATH) return environment.APEX_CODE_POLICY_PATH;\n\treturn process.platform === \"win32\"\n\t\t? join(environment.ProgramData ?? \"C:\\\\ProgramData\", \"apex-code\", \"policy.json\")\n\t\t: \"/etc/apex-code/policy.json\";\n}\n\n/**\n * The two host-owned permission scopes, captured verbatim.\n *\n * Raw file text rather than parsed rules, so the child's store applies exactly the same\n * validation -- and reports exactly the same malformed-file error -- as it would have on\n * the host. A snapshot that pre-parsed would have to decide what a malformed host policy\n * means, and the only safe answer to that lives in the store.\n */\ninterface SupervisorPolicySnapshotFile {\n\treadonly version: 1;\n\treadonly policy: string | null;\n\treadonly user: string | null;\n}\n\nfunction readHostScopeText(path: string): string | null {\n\ttry {\n\t\treturn readFileSync(path, \"utf8\");\n\t} catch {\n\t\t// Absent is the ordinary case for both files; an unreadable one projects as absent\n\t\t// rather than as authority the child cannot see the shape of.\n\t\treturn null;\n\t}\n}\n\n/**\n * Capture the host `policy` and `user` permission scopes into the supervisor's private\n * directory and return the file's path.\n *\n * Called before `buildSandboxedCliLaunch` repoints `APEX_CODE_CODING_AGENT_DIR` at a\n * workspace directory the child can write. Without it the child's `user` scope came from a\n * file the child itself owns, and its `policy` scope from a path the environment allowlist\n * had already removed.\n */\nexport function writeSupervisorPolicySnapshot(options: {\n\tdirectory: string;\n\t/** The *host* agent directory, before the launch repoints it. */\n\tagentDir: string;\n\tpolicyPath?: string;\n\tenvironment?: NodeJS.ProcessEnv;\n}): string {\n\tconst directory = join(options.directory, \"policy\");\n\tmkdirSync(directory, { recursive: true, mode: 0o700 });\n\tconst snapshot: SupervisorPolicySnapshotFile = {\n\t\tversion: 1,\n\t\tpolicy: readHostScopeText(options.policyPath ?? resolveSupervisorPolicyPath(options.environment)),\n\t\tuser: readHostScopeText(join(options.agentDir, \"permissions.json\")),\n\t};\n\tconst path = join(directory, \"permission-snapshot.json\");\n\twriteFileSync(path, JSON.stringify(snapshot), { mode: 0o600 });\n\treturn path;\n}\n\n/**\n * Read supervisor policy only from global settings; project settings are untrusted here.\n *\n * The built-in provider hosts are added unless explicitly refused, because a deny-all\n * default made a fresh install unable to reach any model while giving the user no way to\n * learn which host to permit. Configured hosts are additive on top, and\n * `allowDefaultHosts: false` restores the strict behaviour for anyone who wants it.\n */\nexport function resolveSupervisorAllowedHosts(cwd: string, agentDir: string): readonly string[] | undefined {\n\tconst settings = SettingsManager.create(cwd, agentDir, { projectTrusted: false });\n\tconst network = settings.getNetworkSettings();\n\tconst configured = network?.allowedHosts ?? [];\n\tif (network?.allowDefaultHosts === false) return configured;\n\t// The configured `web_search` backend's host, added only when a credential for it\n\t// is present. Without this the tool would be configured and still unreachable,\n\t// which reads as a broken search rather than a missing allowlist entry. Nothing is\n\t// opened for a user who never set the key.\n\tconst webSearchHost = resolveWebSearchHost(settings.getWebSearchSettings());\n\treturn [...new Set([...resolveDefaultAllowedHosts(), ...configured, ...(webSearchHost ? [webSearchHost] : [])])];\n}\n\n/**\n * The host's two user-scope skill roots, each present only when it exists on the\n * host. Kept as two named slots rather than one list because\n * `core/package-manager.ts` discovers them in different modes -- root `.md` files\n * count as skills under `agentSkills` (\"pi\" mode) and are ignored under\n * `agentsHomeSkills` (\"agents\" mode), per `docs/skills.md` -- and a flat list of 0-2\n * paths cannot tell the child which root a lone survivor was.\n */\nexport interface HostSkillPaths {\n\t/** Host `<agentDir>/skills`. */\n\treadonly agentSkills?: string;\n\t/** Host `<home>/.agents/skills`. */\n\treadonly agentsHomeSkills?: string;\n}\n\n/** A candidate skill root that exists but was excluded, and why. */\nexport interface HostSkillPathRefusal {\n\treadonly root: keyof HostSkillPaths;\n\treadonly path: string;\n\treadonly reason: string;\n}\n\nexport interface ResolvedHostSkillPaths {\n\treadonly paths: HostSkillPaths;\n\treadonly refusals: readonly HostSkillPathRefusal[];\n}\n\n/**\n * True when `candidate` is the host home directory itself, or an ancestor of it.\n * Both paths must already be resolved (`realpathSync`), so a symlink can't disguise\n * either side. Mounting such a candidate read-only would re-expose the entire home\n * tree the sandbox's `--tmpfs /home` (Linux) / `(deny file-read* USER_HOME)` (macOS)\n * deliberately hides -- SSH keys, cloud credentials, other projects -- not just a\n * skills subtree.\n */\nfunction isHomeOrAncestorOfHome(candidate: string, home: string): boolean {\n\tif (candidate === home) return true;\n\tconst prefix = candidate.endsWith(sep) ? candidate : candidate + sep;\n\treturn home.startsWith(prefix);\n}\n\n/** Resolve one candidate root: absent, refused (symlinked onto the host home), or usable. */\nfunction resolveHostSkillRoot(\n\troot: keyof HostSkillPaths,\n\tcandidate: string,\n\thomeDir: string,\n): { path?: string; refusal?: HostSkillPathRefusal } {\n\tif (!existsSync(candidate)) return {};\n\tlet realCandidate: string;\n\tlet realHome: string;\n\ttry {\n\t\trealCandidate = realpathSync(candidate);\n\t\trealHome = realpathSync(homeDir);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\treturn { refusal: { root, path: candidate, reason: `could not resolve real path: ${message}` } };\n\t}\n\tif (isHomeOrAncestorOfHome(realCandidate, realHome)) {\n\t\treturn {\n\t\t\trefusal: {\n\t\t\t\troot,\n\t\t\t\tpath: candidate,\n\t\t\t\treason: \"resolves to the host home directory or an ancestor of it\",\n\t\t\t},\n\t\t};\n\t}\n\treturn { path: candidate };\n}\n\n/**\n * Resolve the host's user-scope skill directories, before the sandbox exists to hide\n * them. Read only from the runtime environment and the host agent directory --\n * never from project files -- matching ADR 0016's rule that supervisor policy is\n * trust-first. Mirrors `core/package-manager.ts`'s own user-scope roots so the two\n * sides agree on where a skill lives. A candidate that resolves (directly or via a\n * symlink) onto the host home or an ancestor of it is refused rather than mounted --\n * see `isHomeOrAncestorOfHome` -- and reported so the caller can surface a startup\n * diagnostic instead of silently mounting or silently skipping it.\n */\nexport function resolveHostSkillPaths(agentDir: string, homeDir: string): ResolvedHostSkillPaths {\n\tconst agentSkills = resolveHostSkillRoot(\"agentSkills\", join(agentDir, \"skills\"), homeDir);\n\tconst agentsHomeSkills = resolveHostSkillRoot(\"agentsHomeSkills\", join(homeDir, \".agents\", \"skills\"), homeDir);\n\treturn {\n\t\tpaths: {\n\t\t\t...(agentSkills.path ? { agentSkills: agentSkills.path } : {}),\n\t\t\t...(agentsHomeSkills.path ? { agentsHomeSkills: agentsHomeSkills.path } : {}),\n\t\t},\n\t\trefusals: [agentSkills.refusal, agentsHomeSkills.refusal].filter(\n\t\t\t(refusal): refusal is HostSkillPathRefusal => refusal !== undefined,\n\t\t),\n\t};\n}\n\nconst SAFE_CHILD_ENVIRONMENT_KEYS = new Set([\n\t\"PATH\",\n\t\"LANG\",\n\t\"LC_ALL\",\n\t\"LC_CTYPE\",\n\t\"TERM\",\n\t\"COLORTERM\",\n\t\"NO_COLOR\",\n\t\"FORCE_COLOR\",\n\t\"TSX_TSCONFIG_PATH\",\n\t\"APEX_CODE_OFFLINE\",\n\t\"APEX_CODE_SKIP_VERSION_CHECK\",\n\t\"APEX_CODE_EXPERIMENTAL\",\n\t\"APEX_CODE_STARTUP_BENCHMARK\",\n\t\"APEX_CODE_TIMING\",\n\t\"APEX_CODE_CLEAR_ON_SHRINK\",\n\t\"APEX_CODE_HARDWARE_CURSOR\",\n\t\"APEX_CODE_MODEL_CATALOG_URL\",\n\t\"APEX_CODE_SHARE_VIEWER_URL\",\n\t\"VISUAL\",\n\t\"EDITOR\",\n]);\n\n// Provider API keys are explicit credential inputs, unlike arbitrary ambient variables.\n// Keep this list in sync with the documented provider environment-variable reference.\nconst SAFE_PROVIDER_CREDENTIAL_KEYS = new Set([\n\t\"ANTHROPIC_API_KEY\",\n\t\"ANTHROPIC_AUTH_TOKEN\",\n\t\"ANTHROPIC_OAUTH_TOKEN\",\n\t\"ANT_LING_API_KEY\",\n\t\"OPENAI_API_KEY\",\n\t\"AZURE_OPENAI_API_KEY\",\n\t\"AZURE_OPENAI_BASE_URL\",\n\t\"AZURE_OPENAI_RESOURCE_NAME\",\n\t\"AZURE_OPENAI_API_VERSION\",\n\t\"AZURE_OPENAI_DEPLOYMENT_NAME_MAP\",\n\t\"DEEPSEEK_API_KEY\",\n\t\"NVIDIA_API_KEY\",\n\t\"GEMINI_API_KEY\",\n\t\"GROQ_API_KEY\",\n\t\"CEREBRAS_API_KEY\",\n\t\"XAI_API_KEY\",\n\t\"FIREWORKS_API_KEY\",\n\t\"TOGETHER_API_KEY\",\n\t\"BASETEN_API_KEY\",\n\t\"OPENROUTER_API_KEY\",\n\t\"AI_GATEWAY_API_KEY\",\n\t\"ZAI_API_KEY\",\n\t\"ZAI_CODING_CN_API_KEY\",\n\t\"MISTRAL_API_KEY\",\n\t\"MINIMAX_API_KEY\",\n\t\"MOONSHOT_API_KEY\",\n\t\"OPENCODE_API_KEY\",\n\t\"KIMI_API_KEY\",\n\t\"CLOUDFLARE_API_KEY\",\n\t\"CLOUDFLARE_ACCOUNT_ID\",\n\t\"CLOUDFLARE_GATEWAY_ID\",\n\t\"QWEN_TOKEN_PLAN_API_KEY\",\n\t\"QWEN_TOKEN_PLAN_CN_API_KEY\",\n\t\"XIAOMI_API_KEY\",\n\t\"XIAOMI_TOKEN_PLAN_CN_API_KEY\",\n\t\"XIAOMI_TOKEN_PLAN_AMS_API_KEY\",\n\t\"XIAOMI_TOKEN_PLAN_SGP_API_KEY\",\n\t\"AWS_PROFILE\",\n\t\"AWS_ACCESS_KEY_ID\",\n\t\"AWS_SECRET_ACCESS_KEY\",\n\t\"AWS_BEARER_TOKEN_BEDROCK\",\n\t\"AWS_REGION\",\n]);\n\n// Credentials for tools rather than model providers. Separate from the provider set\n// above so the provider list stays a mirror of the documented provider reference.\nconst SAFE_TOOL_CREDENTIAL_KEYS = new Set([\"EXA_API_KEY\"]);\n\nfunction buildChildEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n\treturn Object.fromEntries(\n\t\tObject.entries(environment).filter(\n\t\t\t([key]) =>\n\t\t\t\tSAFE_CHILD_ENVIRONMENT_KEYS.has(key) ||\n\t\t\t\tSAFE_PROVIDER_CREDENTIAL_KEYS.has(key) ||\n\t\t\t\tSAFE_TOOL_CREDENTIAL_KEYS.has(key),\n\t\t),\n\t);\n}\n\n/**\n * Drop empty files left in the child's tools directory by a previous launch.\n *\n * A projected tool is bind-mounted over a file there, and bwrap materialises that\n * mountpoint as an empty file on the host which outlives the namespace. If the host\n * tool later disappears, nothing is projected over the stub and the child would\n * otherwise find a 0-byte file where its binary should be. A real downloaded binary\n * is never empty, so size is a safe discriminator.\n */\nfunction clearStaleToolMountpoints(toolsDirectory: string): void {\n\tfor (const entry of readdirSync(toolsDirectory, { withFileTypes: true })) {\n\t\tif (!entry.isFile()) continue;\n\t\tconst entryPath = join(toolsDirectory, entry.name);\n\t\tif (statSync(entryPath).size === 0) {\n\t\t\trmSync(entryPath, { force: true });\n\t\t}\n\t}\n}\n\n/**\n * Allocate agent-owned state under the sole writable workspace mount. The child does\n * not inherit a host home or a host session/config directory, preventing the sandbox\n * from presenting a write boundary while its own state quietly escapes it.\n */\nexport function buildSandboxedCliLaunch(options: {\n\tworkspace: string;\n\tcommand: string;\n\targs: readonly string[];\n\tenvironment: NodeJS.ProcessEnv;\n\tallowedHosts?: readonly string[];\n\tadditionalWritableRoots?: readonly string[];\n\treadOnlyPaths?: readonly string[];\n\tsessionPath?: string;\n\tauthPath?: string;\n\t/**\n\t * A synthesized two-key git config, projected read-only so the child can author a\n\t * commit as the user. Absent when the host had no global identity to resolve.\n\t */\n\tgitConfigPath?: string;\n\ttoolBinaries?: readonly HostToolBinary[];\n\t/**\n\t * Host user-scope skill directories, pre-filtered by the caller to those that\n\t * exist and pass the host-home escape check (SKILL.4). Mounted read-only at their\n\t * original host location -- Seatbelt cannot remap a path, so this must hold for\n\t * both backends -- and named to the child via `APEX_CODE_SKILL_PATH_*` so its\n\t * discovery can find them under the sandbox's own repointed `HOME`/agent dir.\n\t */\n\tskillPaths?: HostSkillPaths;\n\t/** The supervisor-owned credential write channel, when one was opened. */\n\tcredentialChannel?: SandboxLaunch[\"credentialChannel\"];\n\t/** The supervisor's private 0700 state directory for this launch. */\n\tsupervisorStateDirectory?: string;\n\t/** The captured host permission scopes, named to the child read-only. */\n\tpolicySnapshotPath?: string;\n}): SandboxedCliLaunch {\n\tconst supervisorStateDirectory = options.supervisorStateDirectory ?? createSupervisorStateDirectory().path;\n\tconst stateDirectory = join(options.workspace, \".apex-code\", \"sandbox-state\");\n\tconst agentDirectory = join(options.workspace, \".apex-code\", \"sandbox-agent\");\n\tconst sessionDirectory = getSandboxSessionDirectory(options.workspace);\n\t// Mirrors getBinDir() as the child will compute it from APEX_CODE_CODING_AGENT_DIR,\n\t// so a projected tool lands exactly where the child's own lookup already checks.\n\tconst toolsDirectory = join(agentDirectory, \"bin\");\n\tconst xdgDirectories = {\n\t\tXDG_CONFIG_HOME: join(stateDirectory, \"config\"),\n\t\tXDG_CACHE_HOME: join(stateDirectory, \"cache\"),\n\t\tXDG_DATA_HOME: join(stateDirectory, \"data\"),\n\t\tXDG_STATE_HOME: join(stateDirectory, \"state\"),\n\t};\n\tfor (const directory of [\n\t\tstateDirectory,\n\t\tagentDirectory,\n\t\tsessionDirectory,\n\t\ttoolsDirectory,\n\t\t...Object.values(xdgDirectories),\n\t]) {\n\t\tmkdirSync(directory, { recursive: true });\n\t}\n\tclearStaleToolMountpoints(toolsDirectory);\n\tconst childEnvironment = buildChildEnvironment(options.environment);\n\tconst { agentSkills, agentsHomeSkills } = options.skillPaths ?? {};\n\tconst skillMountPaths = [agentSkills, agentsHomeSkills].filter((path): path is string => path !== undefined);\n\tconst readOnlyPaths = [...(options.readOnlyPaths ?? []), ...skillMountPaths];\n\tconst readOnlyFiles = [options.sessionPath, options.authPath, options.gitConfigPath].filter(\n\t\t(path): path is string => path !== undefined,\n\t);\n\tconst readOnlyBinaries = (options.toolBinaries ?? []).map((binary) => ({\n\t\tsource: binary.path,\n\t\tdestination: join(toolsDirectory, binary.name),\n\t}));\n\treturn {\n\t\tcommand: options.command,\n\t\targs: [...options.args],\n\t\tpolicy: {\n\t\t\tworkspace: options.workspace,\n\t\t\tallowedHosts: options.allowedHosts ?? [],\n\t\t\tadditionalWritableRoots: options.additionalWritableRoots ?? [],\n\t\t},\n\t\treadOnlyPaths,\n\t\treadOnlyFiles,\n\t\treadOnlyBinaries,\n\t\t// Travels on the launch contract so the platform backend projects the socket;\n\t\t// the environment entry above is what tells the child where to find it.\n\t\t...(options.credentialChannel ? { credentialChannel: options.credentialChannel } : {}),\n\t\tsupervisorStateDirectory,\n\t\tenvironment: {\n\t\t\t...childEnvironment,\n\t\t\t...(options.authPath ? { APEX_CODE_AUTH_PATH: options.authPath } : {}),\n\t\t\t...(options.credentialChannel\n\t\t\t\t? { APEX_CREDENTIAL_PROXY_PATH: options.credentialChannel.childSocketPath }\n\t\t\t\t: {}),\n\t\t\t// After childEnvironment: these come only from the supervisor's own\n\t\t\t// resolution, never from the invoking shell, so their value always wins over\n\t\t\t// anything of the same name that childEnvironment's allowlist let through.\n\t\t\t...(options.gitConfigPath ? { GIT_CONFIG_GLOBAL: options.gitConfigPath } : {}),\n\t\t\t...(agentSkills ? { APEX_CODE_SKILL_PATH_AGENT: agentSkills } : {}),\n\t\t\t...(agentsHomeSkills ? { APEX_CODE_SKILL_PATH_AGENTS_HOME: agentsHomeSkills } : {}),\n\t\t\t// The host's own permission scopes, and the assertion that a boundary is\n\t\t\t// enforcing this process. Both are supervisor-resolved for exactly the reason\n\t\t\t// `agentDirectory` below is: the child's copy is writable by the child.\n\t\t\t...(options.policySnapshotPath ? { [POLICY_SNAPSHOT_PATH_VARIABLE]: options.policySnapshotPath } : {}),\n\t\t\t[SANDBOX_ENFORCEMENT_MARKER_VARIABLE]: SANDBOX_ENFORCEMENT_MARKER_VALUE,\n\t\t\tAPEX_CODE_CODING_AGENT_DIR: agentDirectory,\n\t\t\tAPEX_CODE_CODING_AGENT_SESSION_DIR: sessionDirectory,\n\t\t\tHOME: stateDirectory,\n\t\t\tTMPDIR: stateDirectory,\n\t\t\t...xdgDirectories,\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { HostToolBinary } from "../../utils/tools-manager.ts";
|
|
2
|
-
import { type HostSkillPaths } from "./cli-launch.ts";
|
|
3
|
-
import { type SandboxBackend } from "./supervisor.ts";
|
|
4
|
-
import { SandboxViolationStore } from "./violations.ts";
|
|
5
|
-
export interface CliSandboxDependencies {
|
|
6
|
-
createBackend: (options: {
|
|
7
|
-
violationStore: SandboxViolationStore;
|
|
8
|
-
}) => SandboxBackend;
|
|
9
|
-
stderr: {
|
|
10
|
-
write(message: string): boolean;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Start a normal CLI runtime beneath the whole-process boundary. The caller returns
|
|
15
|
-
* the result directly so the outer process never continues into `main()` afterward.
|
|
16
|
-
*/
|
|
17
|
-
export declare function launchSandboxedCli(options: {
|
|
18
|
-
command: string;
|
|
19
|
-
args: readonly string[];
|
|
20
|
-
environment: NodeJS.ProcessEnv;
|
|
21
|
-
workspace: string;
|
|
22
|
-
agentDir?: string;
|
|
23
|
-
allowedHosts?: readonly string[];
|
|
24
|
-
additionalWritableRoots?: readonly string[];
|
|
25
|
-
readOnlyPaths?: readonly string[];
|
|
26
|
-
authPath?: string;
|
|
27
|
-
toolBinaries?: readonly HostToolBinary[];
|
|
28
|
-
skillPaths?: HostSkillPaths;
|
|
29
|
-
dependencies?: Partial<CliSandboxDependencies>;
|
|
30
|
-
}): Promise<number>;
|
|
31
|
-
//# sourceMappingURL=cli-supervisor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-supervisor.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/cli-supervisor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,OAAO,EAIN,KAAK,cAAc,EAGnB,MAAM,iBAAiB,CAAC;AAOzB,OAAO,EAA2B,KAAK,cAAc,EAAsB,MAAM,iBAAiB,CAAC;AACnG,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,CAAC,OAAO,EAAE;QAAE,cAAc,EAAE,qBAAqB,CAAA;KAAE,KAAK,cAAc,CAAC;IACtF,MAAM,EAAE;QAAE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CAC5C;AAeD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,uBAAuB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACzC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAC/C,GAAG,OAAO,CAAC,MAAM,CAAC,CAuHlB","sourcesContent":["import { rmSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { parseArgs } from \"../../cli/args.ts\";\nimport { reportConcurrentSessionRefusal } from \"../../cli/concurrent-session.ts\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { HostToolBinary } from \"../../utils/tools-manager.ts\";\nimport { acquireSessionLease, readLiveSessionLeases } from \"../session-lease.ts\";\nimport {\n\tbuildSandboxedCliLaunch,\n\tcreateSupervisorStateDirectory,\n\tgetSandboxSessionDirectory,\n\ttype HostSkillPaths,\n\tresolveSupervisorPolicyPath,\n\twriteSupervisorPolicySnapshot,\n} from \"./cli-launch.ts\";\nimport { createProjectedGitConfig, resolveHostGitIdentity } from \"./git-identity.ts\";\nimport { createLinuxSandboxBackend } from \"./linux-backend.ts\";\nimport { createMacosSandboxBackend } from \"./macos-backend.ts\";\nimport { createSandboxPolicy } from \"./policy.ts\";\nimport { createCredentialProxy, resolveCredentialChannelPaths } from \"./rpc/credential-proxy.ts\";\nimport { gitCredentialHelperCommand } from \"./rpc/git-credential-helper.ts\";\nimport { createSandboxSupervisor, type SandboxBackend, type SandboxLaunch } from \"./supervisor.ts\";\nimport { SandboxViolationStore } from \"./violations.ts\";\n\nexport interface CliSandboxDependencies {\n\tcreateBackend: (options: { violationStore: SandboxViolationStore }) => SandboxBackend;\n\tstderr: { write(message: string): boolean };\n}\n\n/** Routes to the platform adapter for the running OS; each adapter self-reports\n * `unavailable` on any other platform, so this only needs to pick the one whose\n * enforcement is actually reachable here. */\nfunction createDefaultSandboxBackend(options: { violationStore: SandboxViolationStore }): SandboxBackend {\n\tif (process.platform === \"darwin\") return createMacosSandboxBackend(options);\n\treturn createLinuxSandboxBackend(options);\n}\n\nconst defaultDependencies: CliSandboxDependencies = {\n\tcreateBackend: createDefaultSandboxBackend,\n\tstderr: process.stderr,\n};\n\n/**\n * Start a normal CLI runtime beneath the whole-process boundary. The caller returns\n * the result directly so the outer process never continues into `main()` afterward.\n */\nexport async function launchSandboxedCli(options: {\n\tcommand: string;\n\targs: readonly string[];\n\tenvironment: NodeJS.ProcessEnv;\n\tworkspace: string;\n\tagentDir?: string;\n\tallowedHosts?: readonly string[];\n\tadditionalWritableRoots?: readonly string[];\n\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\ttoolBinaries?: readonly HostToolBinary[];\n\tskillPaths?: HostSkillPaths;\n\tdependencies?: Partial<CliSandboxDependencies>;\n}): Promise<number> {\n\tconst dependencies = { ...defaultDependencies, ...options.dependencies };\n\tconst policyResult = createSandboxPolicy({\n\t\tworkspace: options.workspace,\n\t\tallowedHosts: options.allowedHosts,\n\t\tadditionalWritableRoots: options.additionalWritableRoots,\n\t});\n\tif (policyResult.kind === \"invalid\") {\n\t\tdependencies.stderr.write(`Error: OS sandbox is not enforcing this agent session: ${policyResult.reason}\\n`);\n\t\treturn 1;\n\t}\n\tconst violationStore = new SandboxViolationStore();\n\tconst backend = dependencies.createBackend({ violationStore });\n\tconst agentDir = options.agentDir ?? getAgentDir();\n\tconst supervisorState = createSupervisorStateDirectory();\n\n\tconst supervisor = createSandboxSupervisor({ backend, policy: policyResult.policy });\n\tlet credentialProxy: Awaited<ReturnType<typeof createCredentialProxy>> | undefined;\n\tlet lease: ReturnType<typeof acquireSessionLease> | undefined;\n\tlet projectedGitConfig: ReturnType<typeof createProjectedGitConfig> | undefined;\n\ttry {\n\t\tconst parsed = parseArgs(options.args);\n\t\tconst resolvedSessionPath =\n\t\t\tparsed.session &&\n\t\t\t(parsed.session.includes(\"/\") || parsed.session.includes(\"\\\\\") || parsed.session.endsWith(\".jsonl\"))\n\t\t\t\t? resolve(options.workspace, parsed.session)\n\t\t\t\t: undefined;\n\t\tconst sessionRelativePath = resolvedSessionPath ? relative(options.workspace, resolvedSessionPath) : undefined;\n\t\tconst externalSessionPath =\n\t\t\tresolvedSessionPath &&\n\t\t\tsessionRelativePath !== undefined &&\n\t\t\tsessionRelativePath !== \"\" &&\n\t\t\t(sessionRelativePath === \"..\" || sessionRelativePath.startsWith(`..${sep}`) || isAbsolute(sessionRelativePath))\n\t\t\t\t? resolvedSessionPath\n\t\t\t\t: undefined;\n\t\tconst wantsPersistentSession = !parsed.noSession && !parsed.help && parsed.listModels === undefined;\n\t\tconst sessionDirectory = getSandboxSessionDirectory(policyResult.policy.workspace);\n\t\tif (wantsPersistentSession && !parsed.allowConcurrent) {\n\t\t\tconst liveSessions = readLiveSessionLeases(sessionDirectory, policyResult.policy.workspace);\n\t\t\tif (liveSessions.length > 0) {\n\t\t\t\treportConcurrentSessionRefusal(liveSessions, policyResult.policy.workspace);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\tlease = wantsPersistentSession\n\t\t\t? acquireSessionLease(sessionDirectory, policyResult.policy.workspace, `supervisor-${process.pid}`)\n\t\t\t: undefined;\n\n\t\t// Open the host-owned credential channel only after startup checks that can\n\t\t// return early. AuthStorage creates a missing canonical file as 0600, so a\n\t\t// first-run credential writes get the same channel and read-only projection as later runs.\n\t\tlet credentialChannel: SandboxLaunch[\"credentialChannel\"];\n\t\tif (options.authPath && backend.status.kind === \"enforced\") {\n\t\t\tconst paths = resolveCredentialChannelPaths();\n\t\t\tcredentialProxy = await createCredentialProxy({\n\t\t\t\tauthPath: options.authPath,\n\t\t\t\tviolationStore,\n\t\t\t\tsocketPath: paths.hostSocketPath,\n\t\t\t\tcleanupDirectory: paths.hostSocketDirectory,\n\t\t\t});\n\t\t\tcredentialChannel = paths;\n\t\t}\n\n\t\t// Resolved here rather than in the caller because this function owns the teardown\n\t\t// that removes the directory again. The supervisor is unsandboxed, so the host home\n\t\t// is still visible at this point; inside the child it is not.\n\t\tconst identity = resolveHostGitIdentity({ environment: options.environment });\n\t\tconst snapshotPath = writeSupervisorPolicySnapshot({\n\t\t\tdirectory: supervisorState.path,\n\t\t\tagentDir,\n\t\t\tpolicyPath: resolveSupervisorPolicyPath(options.environment),\n\t\t\tenvironment: options.environment,\n\t\t});\n\t\t// The helper command is derived from the state directory rather than handed back by\n\t\t// the backend, because the config has to name it before any backend exists. Both\n\t\t// sides derive the same path, so the config never names a file nothing created.\n\t\tprojectedGitConfig = identity\n\t\t\t? createProjectedGitConfig(identity, {\n\t\t\t\t\tcredentialHelper: gitCredentialHelperCommand(supervisorState.path),\n\t\t\t\t})\n\t\t\t: undefined;\n\n\t\tconst launch = buildSandboxedCliLaunch({\n\t\t\tworkspace: policyResult.policy.workspace,\n\t\t\tcommand: options.command,\n\t\t\targs: options.args,\n\t\t\tenvironment: options.environment,\n\t\t\tallowedHosts: options.allowedHosts,\n\t\t\treadOnlyPaths: options.readOnlyPaths,\n\t\t\tsessionPath: externalSessionPath,\n\t\t\tauthPath: options.authPath,\n\t\t\tgitConfigPath: projectedGitConfig?.path,\n\t\t\ttoolBinaries: options.toolBinaries,\n\t\t\tskillPaths: options.skillPaths,\n\t\t\tcredentialChannel,\n\t\t\tsupervisorStateDirectory: supervisorState.path,\n\t\t\tpolicySnapshotPath: snapshotPath,\n\t\t});\n\t\treturn await supervisor.launch(launch);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : \"Failed to start OS sandbox.\";\n\t\tdependencies.stderr.write(`Error: ${message}\\n`);\n\t\treturn 1;\n\t} finally {\n\t\tlease?.release();\n\t\tif (projectedGitConfig) rmSync(projectedGitConfig.directory, { force: true, recursive: true });\n\t\tconst cleanupResults = await Promise.allSettled([supervisor.close(), credentialProxy?.close()]);\n\t\tsupervisorState.dispose();\n\t\tfor (const violation of violationStore.list()) {\n\t\t\tdependencies.stderr.write(\n\t\t\t\t`Sandbox violation (${violation.kind}): ${violation.command} — ${violation.detail}\\n`,\n\t\t\t);\n\t\t}\n\t\tfor (const result of cleanupResults) {\n\t\t\tif (result.status === \"rejected\") {\n\t\t\t\tdependencies.stderr.write(\"Warning: sandbox cleanup did not complete cleanly.\\n\");\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { rmSync } from "node:fs";
|
|
2
|
-
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
-
import { parseArgs } from "../../cli/args.js";
|
|
4
|
-
import { reportConcurrentSessionRefusal } from "../../cli/concurrent-session.js";
|
|
5
|
-
import { getAgentDir } from "../../config.js";
|
|
6
|
-
import { acquireSessionLease, readLiveSessionLeases } from "../session-lease.js";
|
|
7
|
-
import { buildSandboxedCliLaunch, createSupervisorStateDirectory, getSandboxSessionDirectory, resolveSupervisorPolicyPath, writeSupervisorPolicySnapshot, } from "./cli-launch.js";
|
|
8
|
-
import { createProjectedGitConfig, resolveHostGitIdentity } from "./git-identity.js";
|
|
9
|
-
import { createLinuxSandboxBackend } from "./linux-backend.js";
|
|
10
|
-
import { createMacosSandboxBackend } from "./macos-backend.js";
|
|
11
|
-
import { createSandboxPolicy } from "./policy.js";
|
|
12
|
-
import { createCredentialProxy, resolveCredentialChannelPaths } from "./rpc/credential-proxy.js";
|
|
13
|
-
import { gitCredentialHelperCommand } from "./rpc/git-credential-helper.js";
|
|
14
|
-
import { createSandboxSupervisor } from "./supervisor.js";
|
|
15
|
-
import { SandboxViolationStore } from "./violations.js";
|
|
16
|
-
/** Routes to the platform adapter for the running OS; each adapter self-reports
|
|
17
|
-
* `unavailable` on any other platform, so this only needs to pick the one whose
|
|
18
|
-
* enforcement is actually reachable here. */
|
|
19
|
-
function createDefaultSandboxBackend(options) {
|
|
20
|
-
if (process.platform === "darwin")
|
|
21
|
-
return createMacosSandboxBackend(options);
|
|
22
|
-
return createLinuxSandboxBackend(options);
|
|
23
|
-
}
|
|
24
|
-
const defaultDependencies = {
|
|
25
|
-
createBackend: createDefaultSandboxBackend,
|
|
26
|
-
stderr: process.stderr,
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Start a normal CLI runtime beneath the whole-process boundary. The caller returns
|
|
30
|
-
* the result directly so the outer process never continues into `main()` afterward.
|
|
31
|
-
*/
|
|
32
|
-
export async function launchSandboxedCli(options) {
|
|
33
|
-
const dependencies = { ...defaultDependencies, ...options.dependencies };
|
|
34
|
-
const policyResult = createSandboxPolicy({
|
|
35
|
-
workspace: options.workspace,
|
|
36
|
-
allowedHosts: options.allowedHosts,
|
|
37
|
-
additionalWritableRoots: options.additionalWritableRoots,
|
|
38
|
-
});
|
|
39
|
-
if (policyResult.kind === "invalid") {
|
|
40
|
-
dependencies.stderr.write(`Error: OS sandbox is not enforcing this agent session: ${policyResult.reason}\n`);
|
|
41
|
-
return 1;
|
|
42
|
-
}
|
|
43
|
-
const violationStore = new SandboxViolationStore();
|
|
44
|
-
const backend = dependencies.createBackend({ violationStore });
|
|
45
|
-
const agentDir = options.agentDir ?? getAgentDir();
|
|
46
|
-
const supervisorState = createSupervisorStateDirectory();
|
|
47
|
-
const supervisor = createSandboxSupervisor({ backend, policy: policyResult.policy });
|
|
48
|
-
let credentialProxy;
|
|
49
|
-
let lease;
|
|
50
|
-
let projectedGitConfig;
|
|
51
|
-
try {
|
|
52
|
-
const parsed = parseArgs(options.args);
|
|
53
|
-
const resolvedSessionPath = parsed.session &&
|
|
54
|
-
(parsed.session.includes("/") || parsed.session.includes("\\") || parsed.session.endsWith(".jsonl"))
|
|
55
|
-
? resolve(options.workspace, parsed.session)
|
|
56
|
-
: undefined;
|
|
57
|
-
const sessionRelativePath = resolvedSessionPath ? relative(options.workspace, resolvedSessionPath) : undefined;
|
|
58
|
-
const externalSessionPath = resolvedSessionPath &&
|
|
59
|
-
sessionRelativePath !== undefined &&
|
|
60
|
-
sessionRelativePath !== "" &&
|
|
61
|
-
(sessionRelativePath === ".." || sessionRelativePath.startsWith(`..${sep}`) || isAbsolute(sessionRelativePath))
|
|
62
|
-
? resolvedSessionPath
|
|
63
|
-
: undefined;
|
|
64
|
-
const wantsPersistentSession = !parsed.noSession && !parsed.help && parsed.listModels === undefined;
|
|
65
|
-
const sessionDirectory = getSandboxSessionDirectory(policyResult.policy.workspace);
|
|
66
|
-
if (wantsPersistentSession && !parsed.allowConcurrent) {
|
|
67
|
-
const liveSessions = readLiveSessionLeases(sessionDirectory, policyResult.policy.workspace);
|
|
68
|
-
if (liveSessions.length > 0) {
|
|
69
|
-
reportConcurrentSessionRefusal(liveSessions, policyResult.policy.workspace);
|
|
70
|
-
return 1;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
lease = wantsPersistentSession
|
|
74
|
-
? acquireSessionLease(sessionDirectory, policyResult.policy.workspace, `supervisor-${process.pid}`)
|
|
75
|
-
: undefined;
|
|
76
|
-
// Open the host-owned credential channel only after startup checks that can
|
|
77
|
-
// return early. AuthStorage creates a missing canonical file as 0600, so a
|
|
78
|
-
// first-run credential writes get the same channel and read-only projection as later runs.
|
|
79
|
-
let credentialChannel;
|
|
80
|
-
if (options.authPath && backend.status.kind === "enforced") {
|
|
81
|
-
const paths = resolveCredentialChannelPaths();
|
|
82
|
-
credentialProxy = await createCredentialProxy({
|
|
83
|
-
authPath: options.authPath,
|
|
84
|
-
violationStore,
|
|
85
|
-
socketPath: paths.hostSocketPath,
|
|
86
|
-
cleanupDirectory: paths.hostSocketDirectory,
|
|
87
|
-
});
|
|
88
|
-
credentialChannel = paths;
|
|
89
|
-
}
|
|
90
|
-
// Resolved here rather than in the caller because this function owns the teardown
|
|
91
|
-
// that removes the directory again. The supervisor is unsandboxed, so the host home
|
|
92
|
-
// is still visible at this point; inside the child it is not.
|
|
93
|
-
const identity = resolveHostGitIdentity({ environment: options.environment });
|
|
94
|
-
const snapshotPath = writeSupervisorPolicySnapshot({
|
|
95
|
-
directory: supervisorState.path,
|
|
96
|
-
agentDir,
|
|
97
|
-
policyPath: resolveSupervisorPolicyPath(options.environment),
|
|
98
|
-
environment: options.environment,
|
|
99
|
-
});
|
|
100
|
-
// The helper command is derived from the state directory rather than handed back by
|
|
101
|
-
// the backend, because the config has to name it before any backend exists. Both
|
|
102
|
-
// sides derive the same path, so the config never names a file nothing created.
|
|
103
|
-
projectedGitConfig = identity
|
|
104
|
-
? createProjectedGitConfig(identity, {
|
|
105
|
-
credentialHelper: gitCredentialHelperCommand(supervisorState.path),
|
|
106
|
-
})
|
|
107
|
-
: undefined;
|
|
108
|
-
const launch = buildSandboxedCliLaunch({
|
|
109
|
-
workspace: policyResult.policy.workspace,
|
|
110
|
-
command: options.command,
|
|
111
|
-
args: options.args,
|
|
112
|
-
environment: options.environment,
|
|
113
|
-
allowedHosts: options.allowedHosts,
|
|
114
|
-
readOnlyPaths: options.readOnlyPaths,
|
|
115
|
-
sessionPath: externalSessionPath,
|
|
116
|
-
authPath: options.authPath,
|
|
117
|
-
gitConfigPath: projectedGitConfig?.path,
|
|
118
|
-
toolBinaries: options.toolBinaries,
|
|
119
|
-
skillPaths: options.skillPaths,
|
|
120
|
-
credentialChannel,
|
|
121
|
-
supervisorStateDirectory: supervisorState.path,
|
|
122
|
-
policySnapshotPath: snapshotPath,
|
|
123
|
-
});
|
|
124
|
-
return await supervisor.launch(launch);
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
const message = error instanceof Error ? error.message : "Failed to start OS sandbox.";
|
|
128
|
-
dependencies.stderr.write(`Error: ${message}\n`);
|
|
129
|
-
return 1;
|
|
130
|
-
}
|
|
131
|
-
finally {
|
|
132
|
-
lease?.release();
|
|
133
|
-
if (projectedGitConfig)
|
|
134
|
-
rmSync(projectedGitConfig.directory, { force: true, recursive: true });
|
|
135
|
-
const cleanupResults = await Promise.allSettled([supervisor.close(), credentialProxy?.close()]);
|
|
136
|
-
supervisorState.dispose();
|
|
137
|
-
for (const violation of violationStore.list()) {
|
|
138
|
-
dependencies.stderr.write(`Sandbox violation (${violation.kind}): ${violation.command} — ${violation.detail}\n`);
|
|
139
|
-
}
|
|
140
|
-
for (const result of cleanupResults) {
|
|
141
|
-
if (result.status === "rejected") {
|
|
142
|
-
dependencies.stderr.write("Warning: sandbox cleanup did not complete cleanly.\n");
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
//# sourceMappingURL=cli-supervisor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-supervisor.js","sourceRoot":"","sources":["../../../src/core/sandbox/cli-supervisor.ts"],"names":[],"mappings":"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","sourcesContent":["import { rmSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { parseArgs } from \"../../cli/args.ts\";\nimport { reportConcurrentSessionRefusal } from \"../../cli/concurrent-session.ts\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { HostToolBinary } from \"../../utils/tools-manager.ts\";\nimport { acquireSessionLease, readLiveSessionLeases } from \"../session-lease.ts\";\nimport {\n\tbuildSandboxedCliLaunch,\n\tcreateSupervisorStateDirectory,\n\tgetSandboxSessionDirectory,\n\ttype HostSkillPaths,\n\tresolveSupervisorPolicyPath,\n\twriteSupervisorPolicySnapshot,\n} from \"./cli-launch.ts\";\nimport { createProjectedGitConfig, resolveHostGitIdentity } from \"./git-identity.ts\";\nimport { createLinuxSandboxBackend } from \"./linux-backend.ts\";\nimport { createMacosSandboxBackend } from \"./macos-backend.ts\";\nimport { createSandboxPolicy } from \"./policy.ts\";\nimport { createCredentialProxy, resolveCredentialChannelPaths } from \"./rpc/credential-proxy.ts\";\nimport { gitCredentialHelperCommand } from \"./rpc/git-credential-helper.ts\";\nimport { createSandboxSupervisor, type SandboxBackend, type SandboxLaunch } from \"./supervisor.ts\";\nimport { SandboxViolationStore } from \"./violations.ts\";\n\nexport interface CliSandboxDependencies {\n\tcreateBackend: (options: { violationStore: SandboxViolationStore }) => SandboxBackend;\n\tstderr: { write(message: string): boolean };\n}\n\n/** Routes to the platform adapter for the running OS; each adapter self-reports\n * `unavailable` on any other platform, so this only needs to pick the one whose\n * enforcement is actually reachable here. */\nfunction createDefaultSandboxBackend(options: { violationStore: SandboxViolationStore }): SandboxBackend {\n\tif (process.platform === \"darwin\") return createMacosSandboxBackend(options);\n\treturn createLinuxSandboxBackend(options);\n}\n\nconst defaultDependencies: CliSandboxDependencies = {\n\tcreateBackend: createDefaultSandboxBackend,\n\tstderr: process.stderr,\n};\n\n/**\n * Start a normal CLI runtime beneath the whole-process boundary. The caller returns\n * the result directly so the outer process never continues into `main()` afterward.\n */\nexport async function launchSandboxedCli(options: {\n\tcommand: string;\n\targs: readonly string[];\n\tenvironment: NodeJS.ProcessEnv;\n\tworkspace: string;\n\tagentDir?: string;\n\tallowedHosts?: readonly string[];\n\tadditionalWritableRoots?: readonly string[];\n\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\ttoolBinaries?: readonly HostToolBinary[];\n\tskillPaths?: HostSkillPaths;\n\tdependencies?: Partial<CliSandboxDependencies>;\n}): Promise<number> {\n\tconst dependencies = { ...defaultDependencies, ...options.dependencies };\n\tconst policyResult = createSandboxPolicy({\n\t\tworkspace: options.workspace,\n\t\tallowedHosts: options.allowedHosts,\n\t\tadditionalWritableRoots: options.additionalWritableRoots,\n\t});\n\tif (policyResult.kind === \"invalid\") {\n\t\tdependencies.stderr.write(`Error: OS sandbox is not enforcing this agent session: ${policyResult.reason}\\n`);\n\t\treturn 1;\n\t}\n\tconst violationStore = new SandboxViolationStore();\n\tconst backend = dependencies.createBackend({ violationStore });\n\tconst agentDir = options.agentDir ?? getAgentDir();\n\tconst supervisorState = createSupervisorStateDirectory();\n\n\tconst supervisor = createSandboxSupervisor({ backend, policy: policyResult.policy });\n\tlet credentialProxy: Awaited<ReturnType<typeof createCredentialProxy>> | undefined;\n\tlet lease: ReturnType<typeof acquireSessionLease> | undefined;\n\tlet projectedGitConfig: ReturnType<typeof createProjectedGitConfig> | undefined;\n\ttry {\n\t\tconst parsed = parseArgs(options.args);\n\t\tconst resolvedSessionPath =\n\t\t\tparsed.session &&\n\t\t\t(parsed.session.includes(\"/\") || parsed.session.includes(\"\\\\\") || parsed.session.endsWith(\".jsonl\"))\n\t\t\t\t? resolve(options.workspace, parsed.session)\n\t\t\t\t: undefined;\n\t\tconst sessionRelativePath = resolvedSessionPath ? relative(options.workspace, resolvedSessionPath) : undefined;\n\t\tconst externalSessionPath =\n\t\t\tresolvedSessionPath &&\n\t\t\tsessionRelativePath !== undefined &&\n\t\t\tsessionRelativePath !== \"\" &&\n\t\t\t(sessionRelativePath === \"..\" || sessionRelativePath.startsWith(`..${sep}`) || isAbsolute(sessionRelativePath))\n\t\t\t\t? resolvedSessionPath\n\t\t\t\t: undefined;\n\t\tconst wantsPersistentSession = !parsed.noSession && !parsed.help && parsed.listModels === undefined;\n\t\tconst sessionDirectory = getSandboxSessionDirectory(policyResult.policy.workspace);\n\t\tif (wantsPersistentSession && !parsed.allowConcurrent) {\n\t\t\tconst liveSessions = readLiveSessionLeases(sessionDirectory, policyResult.policy.workspace);\n\t\t\tif (liveSessions.length > 0) {\n\t\t\t\treportConcurrentSessionRefusal(liveSessions, policyResult.policy.workspace);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\tlease = wantsPersistentSession\n\t\t\t? acquireSessionLease(sessionDirectory, policyResult.policy.workspace, `supervisor-${process.pid}`)\n\t\t\t: undefined;\n\n\t\t// Open the host-owned credential channel only after startup checks that can\n\t\t// return early. AuthStorage creates a missing canonical file as 0600, so a\n\t\t// first-run credential writes get the same channel and read-only projection as later runs.\n\t\tlet credentialChannel: SandboxLaunch[\"credentialChannel\"];\n\t\tif (options.authPath && backend.status.kind === \"enforced\") {\n\t\t\tconst paths = resolveCredentialChannelPaths();\n\t\t\tcredentialProxy = await createCredentialProxy({\n\t\t\t\tauthPath: options.authPath,\n\t\t\t\tviolationStore,\n\t\t\t\tsocketPath: paths.hostSocketPath,\n\t\t\t\tcleanupDirectory: paths.hostSocketDirectory,\n\t\t\t});\n\t\t\tcredentialChannel = paths;\n\t\t}\n\n\t\t// Resolved here rather than in the caller because this function owns the teardown\n\t\t// that removes the directory again. The supervisor is unsandboxed, so the host home\n\t\t// is still visible at this point; inside the child it is not.\n\t\tconst identity = resolveHostGitIdentity({ environment: options.environment });\n\t\tconst snapshotPath = writeSupervisorPolicySnapshot({\n\t\t\tdirectory: supervisorState.path,\n\t\t\tagentDir,\n\t\t\tpolicyPath: resolveSupervisorPolicyPath(options.environment),\n\t\t\tenvironment: options.environment,\n\t\t});\n\t\t// The helper command is derived from the state directory rather than handed back by\n\t\t// the backend, because the config has to name it before any backend exists. Both\n\t\t// sides derive the same path, so the config never names a file nothing created.\n\t\tprojectedGitConfig = identity\n\t\t\t? createProjectedGitConfig(identity, {\n\t\t\t\t\tcredentialHelper: gitCredentialHelperCommand(supervisorState.path),\n\t\t\t\t})\n\t\t\t: undefined;\n\n\t\tconst launch = buildSandboxedCliLaunch({\n\t\t\tworkspace: policyResult.policy.workspace,\n\t\t\tcommand: options.command,\n\t\t\targs: options.args,\n\t\t\tenvironment: options.environment,\n\t\t\tallowedHosts: options.allowedHosts,\n\t\t\treadOnlyPaths: options.readOnlyPaths,\n\t\t\tsessionPath: externalSessionPath,\n\t\t\tauthPath: options.authPath,\n\t\t\tgitConfigPath: projectedGitConfig?.path,\n\t\t\ttoolBinaries: options.toolBinaries,\n\t\t\tskillPaths: options.skillPaths,\n\t\t\tcredentialChannel,\n\t\t\tsupervisorStateDirectory: supervisorState.path,\n\t\t\tpolicySnapshotPath: snapshotPath,\n\t\t});\n\t\treturn await supervisor.launch(launch);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : \"Failed to start OS sandbox.\";\n\t\tdependencies.stderr.write(`Error: ${message}\\n`);\n\t\treturn 1;\n\t} finally {\n\t\tlease?.release();\n\t\tif (projectedGitConfig) rmSync(projectedGitConfig.directory, { force: true, recursive: true });\n\t\tconst cleanupResults = await Promise.allSettled([supervisor.close(), credentialProxy?.close()]);\n\t\tsupervisorState.dispose();\n\t\tfor (const violation of violationStore.list()) {\n\t\t\tdependencies.stderr.write(\n\t\t\t\t`Sandbox violation (${violation.kind}): ${violation.command} — ${violation.detail}\\n`,\n\t\t\t);\n\t\t}\n\t\tfor (const result of cleanupResults) {\n\t\t\tif (result.status === \"rejected\") {\n\t\t\t\tdependencies.stderr.write(\"Warning: sandbox cleanup did not complete cleanly.\\n\");\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Model-provider API hosts permitted by default inside the sandbox.
|
|
3
|
-
*
|
|
4
|
-
* The boundary shipped denying every host, which meant a fresh install could not reach
|
|
5
|
-
* any model at all: the first request failed, and the user had no way to discover that
|
|
6
|
-
* `generativelanguage.googleapis.com` was the missing entry — the endpoint is an
|
|
7
|
-
* implementation detail of the provider, not something a user knows. Denying by default
|
|
8
|
-
* only protects anyone if the working configuration is reachable without it.
|
|
9
|
-
*
|
|
10
|
-
* This list is a materialised copy of what `builtinProviders()` declares. It is not
|
|
11
|
-
* derived at runtime because importing the provider registry costs roughly 190ms, and
|
|
12
|
-
* the supervisor pays that on every launch before the child even starts.
|
|
13
|
-
* `test/sandbox/default-hosts.test.ts` recomputes the set from the registry and fails if
|
|
14
|
-
* this copy drifts, so adding a provider upstream cannot silently leave it unreachable.
|
|
15
|
-
*
|
|
16
|
-
* Providers whose `baseUrl` is absent or templated — Bedrock, Azure, Cloudflare, Vertex,
|
|
17
|
-
* and the self-hosted ones — resolve their endpoint from account or environment
|
|
18
|
-
* configuration the supervisor cannot see, so they are not here. Those still require an
|
|
19
|
-
* explicit `network.allowedHosts` entry, and the refusal message now names the host.
|
|
20
|
-
*/
|
|
21
|
-
export declare const DEFAULT_PROVIDER_HOSTS: readonly string[];
|
|
22
|
-
/**
|
|
23
|
-
* Hosts allowed before any user configuration: every statically known model provider,
|
|
24
|
-
* plus the update check, which is Apex Code's own outbound request and otherwise records
|
|
25
|
-
* a policy violation on every single run.
|
|
26
|
-
*/
|
|
27
|
-
export declare function resolveDefaultAllowedHosts(): string[];
|
|
28
|
-
//# sourceMappingURL=default-hosts.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"default-hosts.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/default-hosts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAgCnD,CAAC;AAUF;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,EAAE,CAErD","sourcesContent":["/**\n * Model-provider API hosts permitted by default inside the sandbox.\n *\n * The boundary shipped denying every host, which meant a fresh install could not reach\n * any model at all: the first request failed, and the user had no way to discover that\n * `generativelanguage.googleapis.com` was the missing entry — the endpoint is an\n * implementation detail of the provider, not something a user knows. Denying by default\n * only protects anyone if the working configuration is reachable without it.\n *\n * This list is a materialised copy of what `builtinProviders()` declares. It is not\n * derived at runtime because importing the provider registry costs roughly 190ms, and\n * the supervisor pays that on every launch before the child even starts.\n * `test/sandbox/default-hosts.test.ts` recomputes the set from the registry and fails if\n * this copy drifts, so adding a provider upstream cannot silently leave it unreachable.\n *\n * Providers whose `baseUrl` is absent or templated — Bedrock, Azure, Cloudflare, Vertex,\n * and the self-hosted ones — resolve their endpoint from account or environment\n * configuration the supervisor cannot see, so they are not here. Those still require an\n * explicit `network.allowedHosts` entry, and the refusal message now names the host.\n */\nexport const DEFAULT_PROVIDER_HOSTS: readonly string[] = [\n\t\"ai-gateway.vercel.sh\",\n\t\"api.ant-ling.com\",\n\t\"api.anthropic.com\",\n\t\"api.cerebras.ai\",\n\t\"api.deepseek.com\",\n\t\"api.fireworks.ai\",\n\t\"api.groq.com\",\n\t\"api.individual.githubcopilot.com\",\n\t\"api.kimi.com\",\n\t\"api.minimax.io\",\n\t\"api.minimaxi.com\",\n\t\"api.mistral.ai\",\n\t\"api.moonshot.ai\",\n\t\"api.moonshot.cn\",\n\t\"api.openai.com\",\n\t\"api.together.ai\",\n\t\"api.x.ai\",\n\t\"api.xiaomimimo.com\",\n\t\"api.z.ai\",\n\t\"chatgpt.com\",\n\t\"generativelanguage.googleapis.com\",\n\t\"inference.baseten.co\",\n\t\"integrate.api.nvidia.com\",\n\t\"open.bigmodel.cn\",\n\t\"openrouter.ai\",\n\t\"router.huggingface.co\",\n\t\"token-plan-ams.xiaomimimo.com\",\n\t\"token-plan-cn.xiaomimimo.com\",\n\t\"token-plan-sgp.xiaomimimo.com\",\n\t\"token-plan.ap-southeast-1.maas.aliyuncs.com\",\n\t\"token-plan.cn-beijing.maas.aliyuncs.com\",\n];\n\n/**\n * Host of Apex Code's own update check. Duplicated from `version-check.ts` rather than\n * imported: this module is loaded by the supervisor on every launch, and pulling in that\n * module's dependency chain costs roughly 40ms to obtain a single string.\n * `test/sandbox/default-hosts.test.ts` asserts the two agree.\n */\nconst UPDATE_CHECK_HOST = \"registry.npmjs.org\";\n\n/**\n * Hosts allowed before any user configuration: every statically known model provider,\n * plus the update check, which is Apex Code's own outbound request and otherwise records\n * a policy violation on every single run.\n */\nexport function resolveDefaultAllowedHosts(): string[] {\n\treturn [...new Set([...DEFAULT_PROVIDER_HOSTS, UPDATE_CHECK_HOST])];\n}\n"]}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Model-provider API hosts permitted by default inside the sandbox.
|
|
3
|
-
*
|
|
4
|
-
* The boundary shipped denying every host, which meant a fresh install could not reach
|
|
5
|
-
* any model at all: the first request failed, and the user had no way to discover that
|
|
6
|
-
* `generativelanguage.googleapis.com` was the missing entry — the endpoint is an
|
|
7
|
-
* implementation detail of the provider, not something a user knows. Denying by default
|
|
8
|
-
* only protects anyone if the working configuration is reachable without it.
|
|
9
|
-
*
|
|
10
|
-
* This list is a materialised copy of what `builtinProviders()` declares. It is not
|
|
11
|
-
* derived at runtime because importing the provider registry costs roughly 190ms, and
|
|
12
|
-
* the supervisor pays that on every launch before the child even starts.
|
|
13
|
-
* `test/sandbox/default-hosts.test.ts` recomputes the set from the registry and fails if
|
|
14
|
-
* this copy drifts, so adding a provider upstream cannot silently leave it unreachable.
|
|
15
|
-
*
|
|
16
|
-
* Providers whose `baseUrl` is absent or templated — Bedrock, Azure, Cloudflare, Vertex,
|
|
17
|
-
* and the self-hosted ones — resolve their endpoint from account or environment
|
|
18
|
-
* configuration the supervisor cannot see, so they are not here. Those still require an
|
|
19
|
-
* explicit `network.allowedHosts` entry, and the refusal message now names the host.
|
|
20
|
-
*/
|
|
21
|
-
export const DEFAULT_PROVIDER_HOSTS = [
|
|
22
|
-
"ai-gateway.vercel.sh",
|
|
23
|
-
"api.ant-ling.com",
|
|
24
|
-
"api.anthropic.com",
|
|
25
|
-
"api.cerebras.ai",
|
|
26
|
-
"api.deepseek.com",
|
|
27
|
-
"api.fireworks.ai",
|
|
28
|
-
"api.groq.com",
|
|
29
|
-
"api.individual.githubcopilot.com",
|
|
30
|
-
"api.kimi.com",
|
|
31
|
-
"api.minimax.io",
|
|
32
|
-
"api.minimaxi.com",
|
|
33
|
-
"api.mistral.ai",
|
|
34
|
-
"api.moonshot.ai",
|
|
35
|
-
"api.moonshot.cn",
|
|
36
|
-
"api.openai.com",
|
|
37
|
-
"api.together.ai",
|
|
38
|
-
"api.x.ai",
|
|
39
|
-
"api.xiaomimimo.com",
|
|
40
|
-
"api.z.ai",
|
|
41
|
-
"chatgpt.com",
|
|
42
|
-
"generativelanguage.googleapis.com",
|
|
43
|
-
"inference.baseten.co",
|
|
44
|
-
"integrate.api.nvidia.com",
|
|
45
|
-
"open.bigmodel.cn",
|
|
46
|
-
"openrouter.ai",
|
|
47
|
-
"router.huggingface.co",
|
|
48
|
-
"token-plan-ams.xiaomimimo.com",
|
|
49
|
-
"token-plan-cn.xiaomimimo.com",
|
|
50
|
-
"token-plan-sgp.xiaomimimo.com",
|
|
51
|
-
"token-plan.ap-southeast-1.maas.aliyuncs.com",
|
|
52
|
-
"token-plan.cn-beijing.maas.aliyuncs.com",
|
|
53
|
-
];
|
|
54
|
-
/**
|
|
55
|
-
* Host of Apex Code's own update check. Duplicated from `version-check.ts` rather than
|
|
56
|
-
* imported: this module is loaded by the supervisor on every launch, and pulling in that
|
|
57
|
-
* module's dependency chain costs roughly 40ms to obtain a single string.
|
|
58
|
-
* `test/sandbox/default-hosts.test.ts` asserts the two agree.
|
|
59
|
-
*/
|
|
60
|
-
const UPDATE_CHECK_HOST = "registry.npmjs.org";
|
|
61
|
-
/**
|
|
62
|
-
* Hosts allowed before any user configuration: every statically known model provider,
|
|
63
|
-
* plus the update check, which is Apex Code's own outbound request and otherwise records
|
|
64
|
-
* a policy violation on every single run.
|
|
65
|
-
*/
|
|
66
|
-
export function resolveDefaultAllowedHosts() {
|
|
67
|
-
return [...new Set([...DEFAULT_PROVIDER_HOSTS, UPDATE_CHECK_HOST])];
|
|
68
|
-
}
|
|
69
|
-
//# sourceMappingURL=default-hosts.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"default-hosts.js","sourceRoot":"","sources":["../../../src/core/sandbox/default-hosts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACxD,sBAAsB;IACtB,kBAAkB;IAClB,mBAAmB;IACnB,iBAAiB;IACjB,kBAAkB;IAClB,kBAAkB;IAClB,cAAc;IACd,kCAAkC;IAClC,cAAc;IACd,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,gBAAgB;IAChB,iBAAiB;IACjB,UAAU;IACV,oBAAoB;IACpB,UAAU;IACV,aAAa;IACb,mCAAmC;IACnC,sBAAsB;IACtB,0BAA0B;IAC1B,kBAAkB;IAClB,eAAe;IACf,uBAAuB;IACvB,+BAA+B;IAC/B,8BAA8B;IAC9B,+BAA+B;IAC/B,6CAA6C;IAC7C,yCAAyC;CACzC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AAE/C;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,GAAa;IACtD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,sBAAsB,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAAA,CACpE","sourcesContent":["/**\n * Model-provider API hosts permitted by default inside the sandbox.\n *\n * The boundary shipped denying every host, which meant a fresh install could not reach\n * any model at all: the first request failed, and the user had no way to discover that\n * `generativelanguage.googleapis.com` was the missing entry — the endpoint is an\n * implementation detail of the provider, not something a user knows. Denying by default\n * only protects anyone if the working configuration is reachable without it.\n *\n * This list is a materialised copy of what `builtinProviders()` declares. It is not\n * derived at runtime because importing the provider registry costs roughly 190ms, and\n * the supervisor pays that on every launch before the child even starts.\n * `test/sandbox/default-hosts.test.ts` recomputes the set from the registry and fails if\n * this copy drifts, so adding a provider upstream cannot silently leave it unreachable.\n *\n * Providers whose `baseUrl` is absent or templated — Bedrock, Azure, Cloudflare, Vertex,\n * and the self-hosted ones — resolve their endpoint from account or environment\n * configuration the supervisor cannot see, so they are not here. Those still require an\n * explicit `network.allowedHosts` entry, and the refusal message now names the host.\n */\nexport const DEFAULT_PROVIDER_HOSTS: readonly string[] = [\n\t\"ai-gateway.vercel.sh\",\n\t\"api.ant-ling.com\",\n\t\"api.anthropic.com\",\n\t\"api.cerebras.ai\",\n\t\"api.deepseek.com\",\n\t\"api.fireworks.ai\",\n\t\"api.groq.com\",\n\t\"api.individual.githubcopilot.com\",\n\t\"api.kimi.com\",\n\t\"api.minimax.io\",\n\t\"api.minimaxi.com\",\n\t\"api.mistral.ai\",\n\t\"api.moonshot.ai\",\n\t\"api.moonshot.cn\",\n\t\"api.openai.com\",\n\t\"api.together.ai\",\n\t\"api.x.ai\",\n\t\"api.xiaomimimo.com\",\n\t\"api.z.ai\",\n\t\"chatgpt.com\",\n\t\"generativelanguage.googleapis.com\",\n\t\"inference.baseten.co\",\n\t\"integrate.api.nvidia.com\",\n\t\"open.bigmodel.cn\",\n\t\"openrouter.ai\",\n\t\"router.huggingface.co\",\n\t\"token-plan-ams.xiaomimimo.com\",\n\t\"token-plan-cn.xiaomimimo.com\",\n\t\"token-plan-sgp.xiaomimimo.com\",\n\t\"token-plan.ap-southeast-1.maas.aliyuncs.com\",\n\t\"token-plan.cn-beijing.maas.aliyuncs.com\",\n];\n\n/**\n * Host of Apex Code's own update check. Duplicated from `version-check.ts` rather than\n * imported: this module is loaded by the supervisor on every launch, and pulling in that\n * module's dependency chain costs roughly 40ms to obtain a single string.\n * `test/sandbox/default-hosts.test.ts` asserts the two agree.\n */\nconst UPDATE_CHECK_HOST = \"registry.npmjs.org\";\n\n/**\n * Hosts allowed before any user configuration: every statically known model provider,\n * plus the update check, which is Apex Code's own outbound request and otherwise records\n * a policy violation on every single run.\n */\nexport function resolveDefaultAllowedHosts(): string[] {\n\treturn [...new Set([...DEFAULT_PROVIDER_HOSTS, UPDATE_CHECK_HOST])];\n}\n"]}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The explicit opt-out from OS containment.
|
|
3
|
-
*
|
|
4
|
-
* ADR 0005 shipped without one on purpose, and its 2026-08-28 amendment records why that
|
|
5
|
-
* is now a flag rather than an absence: the delegation work removed the reasons people
|
|
6
|
-
* were reaching for an escape hatch, so the hatch can exist without becoming the default
|
|
7
|
-
* route around a boundary that could not otherwise be worked with.
|
|
8
|
-
*
|
|
9
|
-
* Two rules keep it honest. It is spelled out in full on the command line, never inferred
|
|
10
|
-
* from settings, so no repository and no saved configuration can turn the boundary off.
|
|
11
|
-
* And it always announces itself, because a session that silently ran unconfined is
|
|
12
|
-
* indistinguishable afterwards from one that did not.
|
|
13
|
-
*/
|
|
14
|
-
export declare function writeFullAccessBanner(stderr: {
|
|
15
|
-
write(message: string): boolean;
|
|
16
|
-
}): void;
|
|
17
|
-
/**
|
|
18
|
-
* Confirm the opt-out interactively, or accept it unprompted where no human could answer.
|
|
19
|
-
*
|
|
20
|
-
* A non-interactive session is the case the flag legitimately exists for -- CI that is
|
|
21
|
-
* already externally sandboxed -- and there is nobody there to type. Prompting anyway
|
|
22
|
-
* would hang the run rather than protect anything, so the banner carries the weight.
|
|
23
|
-
*/
|
|
24
|
-
export declare function confirmFullAccess(options: {
|
|
25
|
-
input?: NodeJS.ReadStream;
|
|
26
|
-
output?: NodeJS.WriteStream;
|
|
27
|
-
}): Promise<boolean>;
|
|
28
|
-
//# sourceMappingURL=full-access.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"full-access.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/full-access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAAE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GAAG,IAAI,CAEvF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;CAC5B,GAAG,OAAO,CAAC,OAAO,CAAC,CA4BnB","sourcesContent":["/**\n * The explicit opt-out from OS containment.\n *\n * ADR 0005 shipped without one on purpose, and its 2026-08-28 amendment records why that\n * is now a flag rather than an absence: the delegation work removed the reasons people\n * were reaching for an escape hatch, so the hatch can exist without becoming the default\n * route around a boundary that could not otherwise be worked with.\n *\n * Two rules keep it honest. It is spelled out in full on the command line, never inferred\n * from settings, so no repository and no saved configuration can turn the boundary off.\n * And it always announces itself, because a session that silently ran unconfined is\n * indistinguishable afterwards from one that did not.\n */\n\nconst BANNER = [\n\t\"\",\n\t\" ! OS sandbox disabled for this session (--sandbox danger-full-access).\",\n\t\"\",\n\t\" Tool calls and every process they spawn run with your full account authority:\",\n\t\" your home directory, your credentials, and unrestricted network access.\",\n\t\" The permission gate still applies. It is not a containment boundary.\",\n\t\"\",\n].join(\"\\n\");\n\nexport function writeFullAccessBanner(stderr: { write(message: string): boolean }): void {\n\tstderr.write(`${BANNER}\\n`);\n}\n\n/**\n * Confirm the opt-out interactively, or accept it unprompted where no human could answer.\n *\n * A non-interactive session is the case the flag legitimately exists for -- CI that is\n * already externally sandboxed -- and there is nobody there to type. Prompting anyway\n * would hang the run rather than protect anything, so the banner carries the weight.\n */\nexport function confirmFullAccess(options: {\n\tinput?: NodeJS.ReadStream;\n\toutput?: NodeJS.WriteStream;\n}): Promise<boolean> {\n\tconst input = options.input ?? process.stdin;\n\tconst output = options.output ?? process.stdout;\n\tif (!input.isTTY || !output.isTTY) return Promise.resolve(true);\n\n\toutput.write(\"Run this session with no OS sandbox? [y/N] \");\n\treturn new Promise((resolve) => {\n\t\tlet buffer = \"\";\n\t\tconst finish = (value: string) => {\n\t\t\tinput.off(\"data\", onData);\n\t\t\tinput.off(\"error\", onError);\n\t\t\tinput.pause();\n\t\t\tconst answer = value.trim().toLowerCase();\n\t\t\toutput.write(\"\\n\");\n\t\t\tresolve(answer === \"y\" || answer === \"yes\");\n\t\t};\n\t\tconst onData = (chunk: Buffer | string) => {\n\t\t\tbuffer += chunk.toString();\n\t\t\tconst newline = buffer.indexOf(\"\\n\");\n\t\t\tif (newline >= 0) finish(buffer.slice(0, newline));\n\t\t};\n\t\t// Anything unreadable declines, because an unanswerable prompt must not be the way\n\t\t// containment gets switched off.\n\t\tconst onError = () => finish(\"\");\n\t\tinput.on(\"data\", onData);\n\t\tinput.on(\"error\", onError);\n\t\tinput.resume();\n\t});\n}\n"]}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The explicit opt-out from OS containment.
|
|
3
|
-
*
|
|
4
|
-
* ADR 0005 shipped without one on purpose, and its 2026-08-28 amendment records why that
|
|
5
|
-
* is now a flag rather than an absence: the delegation work removed the reasons people
|
|
6
|
-
* were reaching for an escape hatch, so the hatch can exist without becoming the default
|
|
7
|
-
* route around a boundary that could not otherwise be worked with.
|
|
8
|
-
*
|
|
9
|
-
* Two rules keep it honest. It is spelled out in full on the command line, never inferred
|
|
10
|
-
* from settings, so no repository and no saved configuration can turn the boundary off.
|
|
11
|
-
* And it always announces itself, because a session that silently ran unconfined is
|
|
12
|
-
* indistinguishable afterwards from one that did not.
|
|
13
|
-
*/
|
|
14
|
-
const BANNER = [
|
|
15
|
-
"",
|
|
16
|
-
" ! OS sandbox disabled for this session (--sandbox danger-full-access).",
|
|
17
|
-
"",
|
|
18
|
-
" Tool calls and every process they spawn run with your full account authority:",
|
|
19
|
-
" your home directory, your credentials, and unrestricted network access.",
|
|
20
|
-
" The permission gate still applies. It is not a containment boundary.",
|
|
21
|
-
"",
|
|
22
|
-
].join("\n");
|
|
23
|
-
export function writeFullAccessBanner(stderr) {
|
|
24
|
-
stderr.write(`${BANNER}\n`);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Confirm the opt-out interactively, or accept it unprompted where no human could answer.
|
|
28
|
-
*
|
|
29
|
-
* A non-interactive session is the case the flag legitimately exists for -- CI that is
|
|
30
|
-
* already externally sandboxed -- and there is nobody there to type. Prompting anyway
|
|
31
|
-
* would hang the run rather than protect anything, so the banner carries the weight.
|
|
32
|
-
*/
|
|
33
|
-
export function confirmFullAccess(options) {
|
|
34
|
-
const input = options.input ?? process.stdin;
|
|
35
|
-
const output = options.output ?? process.stdout;
|
|
36
|
-
if (!input.isTTY || !output.isTTY)
|
|
37
|
-
return Promise.resolve(true);
|
|
38
|
-
output.write("Run this session with no OS sandbox? [y/N] ");
|
|
39
|
-
return new Promise((resolve) => {
|
|
40
|
-
let buffer = "";
|
|
41
|
-
const finish = (value) => {
|
|
42
|
-
input.off("data", onData);
|
|
43
|
-
input.off("error", onError);
|
|
44
|
-
input.pause();
|
|
45
|
-
const answer = value.trim().toLowerCase();
|
|
46
|
-
output.write("\n");
|
|
47
|
-
resolve(answer === "y" || answer === "yes");
|
|
48
|
-
};
|
|
49
|
-
const onData = (chunk) => {
|
|
50
|
-
buffer += chunk.toString();
|
|
51
|
-
const newline = buffer.indexOf("\n");
|
|
52
|
-
if (newline >= 0)
|
|
53
|
-
finish(buffer.slice(0, newline));
|
|
54
|
-
};
|
|
55
|
-
// Anything unreadable declines, because an unanswerable prompt must not be the way
|
|
56
|
-
// containment gets switched off.
|
|
57
|
-
const onError = () => finish("");
|
|
58
|
-
input.on("data", onData);
|
|
59
|
-
input.on("error", onError);
|
|
60
|
-
input.resume();
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
//# sourceMappingURL=full-access.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"full-access.js","sourceRoot":"","sources":["../../../src/core/sandbox/full-access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,GAAG;IACd,EAAE;IACF,0EAA0E;IAC1E,EAAE;IACF,mFAAmF;IACnF,6EAA6E;IAC7E,0EAA0E;IAC1E,EAAE;CACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,UAAU,qBAAqB,CAAC,MAA2C,EAAQ;IACxF,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC;AAAA,CAC5B;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAGjC,EAAoB;IACpB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAChD,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAC5D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/B,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC;YACjC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC1B,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnB,OAAO,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,KAAK,CAAC,CAAC;QAAA,CAC5C,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAE,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,OAAO,IAAI,CAAC;gBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAAA,CACnD,CAAC;QACF,mFAAmF;QACnF,iCAAiC;QACjC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC3B,KAAK,CAAC,MAAM,EAAE,CAAC;IAAA,CACf,CAAC,CAAC;AAAA,CACH","sourcesContent":["/**\n * The explicit opt-out from OS containment.\n *\n * ADR 0005 shipped without one on purpose, and its 2026-08-28 amendment records why that\n * is now a flag rather than an absence: the delegation work removed the reasons people\n * were reaching for an escape hatch, so the hatch can exist without becoming the default\n * route around a boundary that could not otherwise be worked with.\n *\n * Two rules keep it honest. It is spelled out in full on the command line, never inferred\n * from settings, so no repository and no saved configuration can turn the boundary off.\n * And it always announces itself, because a session that silently ran unconfined is\n * indistinguishable afterwards from one that did not.\n */\n\nconst BANNER = [\n\t\"\",\n\t\" ! OS sandbox disabled for this session (--sandbox danger-full-access).\",\n\t\"\",\n\t\" Tool calls and every process they spawn run with your full account authority:\",\n\t\" your home directory, your credentials, and unrestricted network access.\",\n\t\" The permission gate still applies. It is not a containment boundary.\",\n\t\"\",\n].join(\"\\n\");\n\nexport function writeFullAccessBanner(stderr: { write(message: string): boolean }): void {\n\tstderr.write(`${BANNER}\\n`);\n}\n\n/**\n * Confirm the opt-out interactively, or accept it unprompted where no human could answer.\n *\n * A non-interactive session is the case the flag legitimately exists for -- CI that is\n * already externally sandboxed -- and there is nobody there to type. Prompting anyway\n * would hang the run rather than protect anything, so the banner carries the weight.\n */\nexport function confirmFullAccess(options: {\n\tinput?: NodeJS.ReadStream;\n\toutput?: NodeJS.WriteStream;\n}): Promise<boolean> {\n\tconst input = options.input ?? process.stdin;\n\tconst output = options.output ?? process.stdout;\n\tif (!input.isTTY || !output.isTTY) return Promise.resolve(true);\n\n\toutput.write(\"Run this session with no OS sandbox? [y/N] \");\n\treturn new Promise((resolve) => {\n\t\tlet buffer = \"\";\n\t\tconst finish = (value: string) => {\n\t\t\tinput.off(\"data\", onData);\n\t\t\tinput.off(\"error\", onError);\n\t\t\tinput.pause();\n\t\t\tconst answer = value.trim().toLowerCase();\n\t\t\toutput.write(\"\\n\");\n\t\t\tresolve(answer === \"y\" || answer === \"yes\");\n\t\t};\n\t\tconst onData = (chunk: Buffer | string) => {\n\t\t\tbuffer += chunk.toString();\n\t\t\tconst newline = buffer.indexOf(\"\\n\");\n\t\t\tif (newline >= 0) finish(buffer.slice(0, newline));\n\t\t};\n\t\t// Anything unreadable declines, because an unanswerable prompt must not be the way\n\t\t// containment gets switched off.\n\t\tconst onError = () => finish(\"\");\n\t\tinput.on(\"data\", onData);\n\t\tinput.on(\"error\", onError);\n\t\tinput.resume();\n\t});\n}\n"]}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export interface HostGitIdentity {
|
|
2
|
-
readonly name: string;
|
|
3
|
-
readonly email: string;
|
|
4
|
-
}
|
|
5
|
-
export interface ProjectedGitConfig {
|
|
6
|
-
readonly directory: string;
|
|
7
|
-
readonly path: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Read the host's git identity before the sandbox exists to hide it.
|
|
11
|
-
*
|
|
12
|
-
* `--global` rather than a plain lookup, because the scope flag is what excludes the
|
|
13
|
-
* workspace's own `.git/config` by construction rather than by choosing a lucky working
|
|
14
|
-
* directory. A repository must not be able to decide what the supervisor projects, the
|
|
15
|
-
* same rule ADR 0016 applies to the network allowlist. Repository scope still works
|
|
16
|
-
* normally inside the child: the workspace is bind-mounted read-write, so git reads
|
|
17
|
-
* `.git/config` there itself and it wins over this projection, exactly as it would on the
|
|
18
|
-
* host.
|
|
19
|
-
*
|
|
20
|
-
* System scope (`/etc/gitconfig`) is deliberately not consulted. A machine-wide commit
|
|
21
|
-
* identity is not a real configuration, and a second `spawnSync` to find one would cost
|
|
22
|
-
* every launch what it saves nobody.
|
|
23
|
-
*
|
|
24
|
-
* Returns undefined unless both keys are present. A name without an email still fails to
|
|
25
|
-
* author a commit, so projecting half an identity would replace one confusing error with
|
|
26
|
-
* another.
|
|
27
|
-
*/
|
|
28
|
-
export declare function resolveHostGitIdentity(options?: {
|
|
29
|
-
/** Injectable only for tests; production uses the supervisor's own environment. */
|
|
30
|
-
environment?: NodeJS.ProcessEnv;
|
|
31
|
-
/**
|
|
32
|
-
* Injectable only for tests, so one can prove repository scope stays excluded even
|
|
33
|
-
* when the lookup runs from inside a repository.
|
|
34
|
-
*/
|
|
35
|
-
cwd?: string;
|
|
36
|
-
}): HostGitIdentity | undefined;
|
|
37
|
-
/**
|
|
38
|
-
* Write a two-key config the child can be pointed at through `GIT_CONFIG_GLOBAL`.
|
|
39
|
-
*
|
|
40
|
-
* Synthesized rather than copied. A real `~/.gitconfig` can carry a `credential.helper`
|
|
41
|
-
* that runs a command, or an `insteadOf` rule rewriting a remote to one carrying a token;
|
|
42
|
-
* projecting the host file would hand both to the child along with the name we wanted.
|
|
43
|
-
* Building the file from a resolved identity means it cannot contain anything else.
|
|
44
|
-
*
|
|
45
|
-
* The directory is its own, not shared with the credential projection, and `0700` for the
|
|
46
|
-
* same reason `resolveCredentialChannelPaths` uses one. TMPDIR is deliberately not
|
|
47
|
-
* honoured: it can point inside the workspace on macOS, which the sandbox may write.
|
|
48
|
-
*/
|
|
49
|
-
export declare function createProjectedGitConfig(identity: HostGitIdentity, options?: {
|
|
50
|
-
/**
|
|
51
|
-
* A `credential.helper` value for the child. It names the supervisor-mediated
|
|
52
|
-
* helper, never a host one: the point is that the child asks over the channel
|
|
53
|
-
* rather than reading any store itself.
|
|
54
|
-
*/
|
|
55
|
-
credentialHelper?: string;
|
|
56
|
-
}): ProjectedGitConfig;
|
|
57
|
-
//# sourceMappingURL=git-identity.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"git-identity.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/git-identity.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE;IAChD,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,GAAG,eAAe,GAAG,SAAS,CAoB9B;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACvC,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE;IACT;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B,GACC,kBAAkB,CAUpB","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { chmodSync, mkdtempSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { supervisorTempDirectory } from \"./supervisor-temp.ts\";\n\nexport interface HostGitIdentity {\n\treadonly name: string;\n\treadonly email: string;\n}\n\nexport interface ProjectedGitConfig {\n\treadonly directory: string;\n\treadonly path: string;\n}\n\n/** Long enough for a cold `git` on a slow disk, short enough not to stall every launch. */\nconst RESOLUTION_TIMEOUT_MILLISECONDS = 2_000;\n\n/**\n * Read the host's git identity before the sandbox exists to hide it.\n *\n * `--global` rather than a plain lookup, because the scope flag is what excludes the\n * workspace's own `.git/config` by construction rather than by choosing a lucky working\n * directory. A repository must not be able to decide what the supervisor projects, the\n * same rule ADR 0016 applies to the network allowlist. Repository scope still works\n * normally inside the child: the workspace is bind-mounted read-write, so git reads\n * `.git/config` there itself and it wins over this projection, exactly as it would on the\n * host.\n *\n * System scope (`/etc/gitconfig`) is deliberately not consulted. A machine-wide commit\n * identity is not a real configuration, and a second `spawnSync` to find one would cost\n * every launch what it saves nobody.\n *\n * Returns undefined unless both keys are present. A name without an email still fails to\n * author a commit, so projecting half an identity would replace one confusing error with\n * another.\n */\nexport function resolveHostGitIdentity(options?: {\n\t/** Injectable only for tests; production uses the supervisor's own environment. */\n\tenvironment?: NodeJS.ProcessEnv;\n\t/**\n\t * Injectable only for tests, so one can prove repository scope stays excluded even\n\t * when the lookup runs from inside a repository.\n\t */\n\tcwd?: string;\n}): HostGitIdentity | undefined {\n\tconst result = spawnSync(\"git\", [\"config\", \"--global\", \"--get-regexp\", \"^user\\\\.(name|email)$\"], {\n\t\tcwd: options?.cwd,\n\t\tencoding: \"utf8\",\n\t\tenv: options?.environment ?? process.env,\n\t\ttimeout: RESOLUTION_TIMEOUT_MILLISECONDS,\n\t});\n\tif (result.status !== 0 || typeof result.stdout !== \"string\") return undefined;\n\n\tlet name: string | undefined;\n\tlet email: string | undefined;\n\tfor (const line of result.stdout.split(\"\\n\")) {\n\t\tconst separator = line.indexOf(\" \");\n\t\tif (separator < 0) continue;\n\t\tconst value = line.slice(separator + 1).trim();\n\t\tif (value.length === 0) continue;\n\t\tif (line.slice(0, separator) === \"user.name\") name = value;\n\t\tif (line.slice(0, separator) === \"user.email\") email = value;\n\t}\n\treturn name && email ? { name, email } : undefined;\n}\n\n/** Escape a value for git's config syntax, which reads a quoted string with backslash escapes. */\nfunction quote(value: string): string {\n\treturn `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/**\n * Write a two-key config the child can be pointed at through `GIT_CONFIG_GLOBAL`.\n *\n * Synthesized rather than copied. A real `~/.gitconfig` can carry a `credential.helper`\n * that runs a command, or an `insteadOf` rule rewriting a remote to one carrying a token;\n * projecting the host file would hand both to the child along with the name we wanted.\n * Building the file from a resolved identity means it cannot contain anything else.\n *\n * The directory is its own, not shared with the credential projection, and `0700` for the\n * same reason `resolveCredentialChannelPaths` uses one. TMPDIR is deliberately not\n * honoured: it can point inside the workspace on macOS, which the sandbox may write.\n */\nexport function createProjectedGitConfig(\n\tidentity: HostGitIdentity,\n\toptions?: {\n\t\t/**\n\t\t * A `credential.helper` value for the child. It names the supervisor-mediated\n\t\t * helper, never a host one: the point is that the child asks over the channel\n\t\t * rather than reading any store itself.\n\t\t */\n\t\tcredentialHelper?: string;\n\t},\n): ProjectedGitConfig {\n\tconst directory = mkdtempSync(join(supervisorTempDirectory(), `apex-git-${process.pid}-`), { encoding: \"utf8\" });\n\tchmodSync(directory, 0o700);\n\tconst path = join(directory, \"config\");\n\tconst sections = [`[user]\\n\\tname = ${quote(identity.name)}\\n\\temail = ${quote(identity.email)}\\n`];\n\tif (options?.credentialHelper) {\n\t\tsections.push(`[credential]\\n\\thelper = ${quote(options.credentialHelper)}\\n`);\n\t}\n\twriteFileSync(path, sections.join(\"\"), { mode: 0o600 });\n\treturn { directory, path };\n}\n"]}
|