apex-code 0.0.1-alpha.2 → 0.0.1-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +47 -1
  2. package/README.md +7 -3
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +9 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/sandbox/child-entry.d.ts.map +1 -1
  7. package/dist/core/sandbox/child-entry.js +4 -0
  8. package/dist/core/sandbox/child-entry.js.map +1 -1
  9. package/dist/core/sandbox/cli-launch.d.ts +10 -1
  10. package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
  11. package/dist/core/sandbox/cli-launch.js +50 -4
  12. package/dist/core/sandbox/cli-launch.js.map +1 -1
  13. package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
  14. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
  15. package/dist/core/sandbox/cli-supervisor.js +1 -0
  16. package/dist/core/sandbox/cli-supervisor.js.map +1 -1
  17. package/dist/core/sandbox/default-hosts.d.ts +28 -0
  18. package/dist/core/sandbox/default-hosts.d.ts.map +1 -0
  19. package/dist/core/sandbox/default-hosts.js +69 -0
  20. package/dist/core/sandbox/default-hosts.js.map +1 -0
  21. package/dist/core/sandbox/linux-backend.d.ts +16 -0
  22. package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
  23. package/dist/core/sandbox/linux-backend.js +60 -11
  24. package/dist/core/sandbox/linux-backend.js.map +1 -1
  25. package/dist/core/sandbox/macos-backend.d.ts +7 -0
  26. package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
  27. package/dist/core/sandbox/macos-backend.js +17 -9
  28. package/dist/core/sandbox/macos-backend.js.map +1 -1
  29. package/dist/core/sandbox/network-refusal.d.ts +24 -0
  30. package/dist/core/sandbox/network-refusal.d.ts.map +1 -0
  31. package/dist/core/sandbox/network-refusal.js +72 -0
  32. package/dist/core/sandbox/network-refusal.js.map +1 -0
  33. package/dist/core/sandbox/supervisor.d.ts +9 -0
  34. package/dist/core/sandbox/supervisor.d.ts.map +1 -1
  35. package/dist/core/sandbox/supervisor.js.map +1 -1
  36. package/dist/core/session-share.d.ts +15 -0
  37. package/dist/core/session-share.d.ts.map +1 -1
  38. package/dist/core/session-share.js +23 -0
  39. package/dist/core/session-share.js.map +1 -1
  40. package/dist/core/settings-manager.d.ts +5 -0
  41. package/dist/core/settings-manager.d.ts.map +1 -1
  42. package/dist/core/settings-manager.js.map +1 -1
  43. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  44. package/dist/modes/interactive/interactive-mode.js +3 -3
  45. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  46. package/dist/utils/tools-manager.d.ts +28 -1
  47. package/dist/utils/tools-manager.d.ts.map +1 -1
  48. package/dist/utils/tools-manager.js +68 -5
  49. package/dist/utils/tools-manager.js.map +1 -1
  50. package/dist/utils/version-check.d.ts +2 -0
  51. package/dist/utils/version-check.d.ts.map +1 -1
  52. package/dist/utils/version-check.js +2 -0
  53. package/dist/utils/version-check.js.map +1 -1
  54. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  55. package/npm-shrinkwrap.json +5 -5
  56. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,6 +1,52 @@
1
1
  # Apex Code changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [0.0.1-alpha.4] - 2026-08-18
4
+
5
+ - Fixed `fd not found. Downloading...` / `Failed to download fd: fetch failed` (and the same
6
+ pair for ripgrep) on every sandboxed startup. The OS sandbox replaces `/home` with an empty
7
+ tmpfs, so the child could not see host installations of `fd` and `rg` even though its `PATH`
8
+ still named them, and `--unshare-net` plus an empty `network.allowedHosts` meant the fallback
9
+ download could not reach GitHub either. The supervisor now resolves both tools on the host —
10
+ installing them there once if missing, where the network is still reachable — and projects
11
+ each one read-only into the child at the path its own lookup already checks. One host-side
12
+ install now serves every workspace, and no network access is required at startup.
13
+ - The OS sandbox no longer reports `Sandbox violation (unknown)` when a sandboxed process
14
+ simply failed on its own, on either Linux or macOS. An invalid API key, a failing test command, or a script exiting 1
15
+ are not boundary refusals, and reporting them as such sent users looking for a refusal that
16
+ never happened. Genuine filesystem and network refusals are still recorded.
17
+ - The OS sandbox now permits the built-in model-provider hosts and the update check by
18
+ default, so a fresh install can reach its provider without first discovering
19
+ `network.allowedHosts` and the provider's endpoint. Configured hosts are added on top;
20
+ `network.allowDefaultHosts: false` restores the previous deny-all behaviour. Providers whose
21
+ endpoint depends on account or environment configuration — Bedrock, Azure, Cloudflare,
22
+ Vertex — still need an explicit entry.
23
+ - A request refused by the sandbox network allowlist now says which host was refused and how to
24
+ permit it, instead of surfacing undici's bare `fetch failed`. The refusal is also attempted
25
+ once rather than retried three times, since an allowlist decision cannot change on retry.
26
+ - Fixed the OS sandbox failing to start with `Error: listen EINVAL: invalid argument` in any
27
+ workspace with a moderately long path. The allowlist proxy's Unix socket lived under the
28
+ workspace, and AF_UNIX caps socket paths at 108 bytes, leaving only ~72 characters for the
29
+ project's own directory layout — a deep-but-ordinary checkout could not launch at all. The
30
+ socket now uses a short path in the system temp directory, bind-mounted into the child, and
31
+ is cleaned up on exit.
32
+ - Fixed `/share` reporting `GitHub CLI is not logged in. Run 'gh auth login' first.` inside the
33
+ OS sandbox, where that diagnosis is wrong and the suggested fix does not help. The sandbox
34
+ cannot reach the host's gh credentials — `~/.config/gh` sits under the tmpfs that replaces
35
+ `/home`, `XDG_CONFIG_HOME` is redirected, and the token usually lives in a system keyring.
36
+ The message now points at the workflow that works: `/export` inside the session, then
37
+ `gh gist create --public=false <file>` outside the sandbox.
38
+ - Hardened the managed-tool lookup against an unusable leftover file. A projected tool is
39
+ bind-mounted over a file in the child's tools directory, and Bubblewrap materialises that
40
+ mountpoint as an empty file that outlives the namespace. `getToolPath()` now requires an
41
+ executable file rather than merely an existing one, and the supervisor clears stale
42
+ zero-byte mountpoints, so a host tool that later disappears can no longer leave the child
43
+ treating a 0-byte stub as its binary.
44
+
45
+ ## [0.0.1-alpha.3] - 2026-08-17
46
+
47
+ - Fixed the Linux OS sandbox crashing with `ReferenceError: require is not defined in ES module
48
+ scope` when launched inside a workspace whose `package.json` declares `"type": "module"`; the
49
+ sandbox's network relay script is now written as `relay.cjs` instead of `relay.js`.
4
50
 
5
51
  ## [0.0.1-alpha.2] - 2026-08-17
6
52
 
package/README.md CHANGED
@@ -41,9 +41,13 @@ Sessions, settings, credentials, extensions, prompts, and other state live under
41
41
 
42
42
  ## Safety and capabilities
43
43
 
44
- Every tool has a declared contract and passes through the permission gate. Linux and
45
- macOS tool execution can additionally run inside the supported OS sandbox. Windows is
46
- a required build/test portability target, but its sandbox backend remains unsupported.
44
+ Every tool has a declared contract and passes through the permission gate. On Linux and
45
+ macOS, every command that can start a session runs inside the OS sandbox: the workspace is
46
+ the only writable location, the invoking account's home directory is hidden, and egress
47
+ goes through an allowlist proxy that permits the built-in model-provider hosts by default
48
+ and reports anything it refuses. Add hosts with `network.allowedHosts` in global
49
+ `settings.json`. Windows is a required build/test portability target, but its sandbox
50
+ backend remains unsupported.
47
51
  Built-in capabilities include file/search tools, shell execution, web tools, user
48
52
  questions, planning, and bounded subagent delegation.
49
53
 
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n/** The public CLI either supervises a sandbox child or starts an ordinary runtime. */\nimport { fileURLToPath } from \"node:url\";\nimport { APP_NAME, getAgentDir, getPackageDir } from \"./config.ts\";\nimport { setApexEnvironment } from \"./core/environment.ts\";\nimport { configureHttpDispatcher } from \"./core/http-dispatcher.ts\";\nimport { requiresSandboxedChild, resolveSupervisorAllowedHosts } from \"./core/sandbox/cli-launch.ts\";\nimport { launchSandboxedCli } from \"./core/sandbox/cli-supervisor.ts\";\n\nasync function run(): Promise<void> {\n\tconst args = process.argv.slice(2);\n\t// Validate session IDs before entering the sandbox. Invalid metadata must fail fast\n\t// without starting a child process or touching network/sandbox setup.\n\tconst sessionIdIndex = args.indexOf(\"--session-id\");\n\tconst sessionId = sessionIdIndex >= 0 ? args[sessionIdIndex + 1] : undefined;\n\tif (sessionId !== undefined && !/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(sessionId)) {\n\t\tconsole.error(\n\t\t\t\"Error: Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tif (requiresSandboxedChild(args)) {\n\t\tconst allowedHosts = resolveSupervisorAllowedHosts(process.cwd(), getAgentDir());\n\t\tconst authPath = join(getAgentDir(), \"auth.json\");\n\n\t\tprocess.exitCode = await launchSandboxedCli({\n\t\t\tcommand: process.execPath,\n\t\t\targs: [\n\t\t\t\tfileURLToPath(\n\t\t\t\t\tnew URL(`./core/sandbox/child-entry${import.meta.url.endsWith(\".ts\") ? \".ts\" : \".js\"}`, import.meta.url),\n\t\t\t\t),\n\t\t\t\t...args,\n\t\t\t],\n\t\t\tenvironment: process.env,\n\t\t\tworkspace: process.cwd(),\n\t\t\tallowedHosts,\n\t\t\tauthPath: existsSync(authPath) ? authPath : undefined,\n\t\t\treadOnlyPaths: [getPackageDir(), dirname(getPackageDir())],\n\t\t});\n\t\treturn;\n\t}\n\n\tprocess.title = APP_NAME;\n\tsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\n\tprocess.env.AI_AGENT = \"apex-code\";\n\tprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\tconfigureHttpDispatcher();\n\tconst { main } = await import(\"./main.ts\");\n\tawait main(args);\n}\n\nawait run();\n"]}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n/** The public CLI either supervises a sandbox child or starts an ordinary runtime. */\nimport { fileURLToPath } from \"node:url\";\nimport { APP_NAME, getAgentDir, getPackageDir } from \"./config.ts\";\nimport { setApexEnvironment } from \"./core/environment.ts\";\nimport { configureHttpDispatcher } from \"./core/http-dispatcher.ts\";\nimport { requiresSandboxedChild, resolveSupervisorAllowedHosts } from \"./core/sandbox/cli-launch.ts\";\nimport { launchSandboxedCli } from \"./core/sandbox/cli-supervisor.ts\";\nimport { prepareHostToolBinaries } from \"./utils/tools-manager.ts\";\n\nasync function run(): Promise<void> {\n\tconst args = process.argv.slice(2);\n\t// Validate session IDs before entering the sandbox. Invalid metadata must fail fast\n\t// without starting a child process or touching network/sandbox setup.\n\tconst sessionIdIndex = args.indexOf(\"--session-id\");\n\tconst sessionId = sessionIdIndex >= 0 ? args[sessionIdIndex + 1] : undefined;\n\tif (sessionId !== undefined && !/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(sessionId)) {\n\t\tconsole.error(\n\t\t\t\"Error: Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tif (requiresSandboxedChild(args)) {\n\t\tconst allowedHosts = resolveSupervisorAllowedHosts(process.cwd(), getAgentDir());\n\t\tconst authPath = join(getAgentDir(), \"auth.json\");\n\t\t// Resolve fd and rg out here, where the host home and the network are both still\n\t\t// reachable. The child's own PATH still names host directories the sandbox has\n\t\t// replaced, so without this projection it finds nothing and cannot download either.\n\t\t// Silent because this runs ahead of every mode, including --print and --mode rpc,\n\t\t// whose stdout carries machine-readable output; the child still reports a genuinely\n\t\t// missing tool through its own startup path.\n\t\tconst toolBinaries = await prepareHostToolBinaries(true);\n\n\t\tprocess.exitCode = await launchSandboxedCli({\n\t\t\tcommand: process.execPath,\n\t\t\targs: [\n\t\t\t\tfileURLToPath(\n\t\t\t\t\tnew URL(`./core/sandbox/child-entry${import.meta.url.endsWith(\".ts\") ? \".ts\" : \".js\"}`, import.meta.url),\n\t\t\t\t),\n\t\t\t\t...args,\n\t\t\t],\n\t\t\tenvironment: process.env,\n\t\t\tworkspace: process.cwd(),\n\t\t\tallowedHosts,\n\t\t\tauthPath: existsSync(authPath) ? authPath : undefined,\n\t\t\treadOnlyPaths: [getPackageDir(), dirname(getPackageDir())],\n\t\t\ttoolBinaries,\n\t\t});\n\t\treturn;\n\t}\n\n\tprocess.title = APP_NAME;\n\tsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\n\tprocess.env.AI_AGENT = \"apex-code\";\n\tprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\tconfigureHttpDispatcher();\n\tconst { main } = await import(\"./main.ts\");\n\tawait main(args);\n}\n\nawait run();\n"]}
package/dist/cli.js CHANGED
@@ -8,6 +8,7 @@ import { setApexEnvironment } from "./core/environment.js";
8
8
  import { configureHttpDispatcher } from "./core/http-dispatcher.js";
9
9
  import { requiresSandboxedChild, resolveSupervisorAllowedHosts } from "./core/sandbox/cli-launch.js";
10
10
  import { launchSandboxedCli } from "./core/sandbox/cli-supervisor.js";
11
+ import { prepareHostToolBinaries } from "./utils/tools-manager.js";
11
12
  async function run() {
12
13
  const args = process.argv.slice(2);
13
14
  // Validate session IDs before entering the sandbox. Invalid metadata must fail fast
@@ -22,6 +23,13 @@ async function run() {
22
23
  if (requiresSandboxedChild(args)) {
23
24
  const allowedHosts = resolveSupervisorAllowedHosts(process.cwd(), getAgentDir());
24
25
  const authPath = join(getAgentDir(), "auth.json");
26
+ // Resolve fd and rg out here, where the host home and the network are both still
27
+ // reachable. The child's own PATH still names host directories the sandbox has
28
+ // replaced, so without this projection it finds nothing and cannot download either.
29
+ // Silent because this runs ahead of every mode, including --print and --mode rpc,
30
+ // whose stdout carries machine-readable output; the child still reports a genuinely
31
+ // missing tool through its own startup path.
32
+ const toolBinaries = await prepareHostToolBinaries(true);
25
33
  process.exitCode = await launchSandboxedCli({
26
34
  command: process.execPath,
27
35
  args: [
@@ -33,6 +41,7 @@ async function run() {
33
41
  allowedHosts,
34
42
  authPath: existsSync(authPath) ? authPath : undefined,
35
43
  readOnlyPaths: [getPackageDir(), dirname(getPackageDir())],
44
+ toolBinaries,
36
45
  });
37
46
  return;
38
47
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,sFAAsF;AACtF,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AACrG,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE,KAAK,UAAU,GAAG,GAAkB;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,oFAAoF;IACpF,sEAAsE;IACtE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,cAAc,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,8CAA8C,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChG,OAAO,CAAC,KAAK,CACZ,gJAAgJ,CAChJ,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACR,CAAC;IACD,IAAI,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,YAAY,GAAG,6BAA6B,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QACjF,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,CAAC;QAElD,OAAO,CAAC,QAAQ,GAAG,MAAM,kBAAkB,CAAC;YAC3C,OAAO,EAAE,OAAO,CAAC,QAAQ;YACzB,IAAI,EAAE;gBACL,aAAa,CACZ,IAAI,GAAG,CAAC,6BAA6B,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CACxG;gBACD,GAAG,IAAI;aACP;YACD,WAAW,EAAE,OAAO,CAAC,GAAG;YACxB,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE;YACxB,YAAY;YACZ,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;YACrD,aAAa,EAAE,CAAC,aAAa,EAAE,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;SAC1D,CAAC,CAAC;QACH,OAAO;IACR,CAAC;IAED,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;IACzB,kBAAkB,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,WAAW,CAAC;IACnC,OAAO,CAAC,WAAW,GAAG,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAA+B,CAAC;IAC/D,uBAAuB,EAAE,CAAC;IAC1B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACjB;AAED,MAAM,GAAG,EAAE,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n/** The public CLI either supervises a sandbox child or starts an ordinary runtime. */\nimport { fileURLToPath } from \"node:url\";\nimport { APP_NAME, getAgentDir, getPackageDir } from \"./config.ts\";\nimport { setApexEnvironment } from \"./core/environment.ts\";\nimport { configureHttpDispatcher } from \"./core/http-dispatcher.ts\";\nimport { requiresSandboxedChild, resolveSupervisorAllowedHosts } from \"./core/sandbox/cli-launch.ts\";\nimport { launchSandboxedCli } from \"./core/sandbox/cli-supervisor.ts\";\n\nasync function run(): Promise<void> {\n\tconst args = process.argv.slice(2);\n\t// Validate session IDs before entering the sandbox. Invalid metadata must fail fast\n\t// without starting a child process or touching network/sandbox setup.\n\tconst sessionIdIndex = args.indexOf(\"--session-id\");\n\tconst sessionId = sessionIdIndex >= 0 ? args[sessionIdIndex + 1] : undefined;\n\tif (sessionId !== undefined && !/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(sessionId)) {\n\t\tconsole.error(\n\t\t\t\"Error: Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tif (requiresSandboxedChild(args)) {\n\t\tconst allowedHosts = resolveSupervisorAllowedHosts(process.cwd(), getAgentDir());\n\t\tconst authPath = join(getAgentDir(), \"auth.json\");\n\n\t\tprocess.exitCode = await launchSandboxedCli({\n\t\t\tcommand: process.execPath,\n\t\t\targs: [\n\t\t\t\tfileURLToPath(\n\t\t\t\t\tnew URL(`./core/sandbox/child-entry${import.meta.url.endsWith(\".ts\") ? \".ts\" : \".js\"}`, import.meta.url),\n\t\t\t\t),\n\t\t\t\t...args,\n\t\t\t],\n\t\t\tenvironment: process.env,\n\t\t\tworkspace: process.cwd(),\n\t\t\tallowedHosts,\n\t\t\tauthPath: existsSync(authPath) ? authPath : undefined,\n\t\t\treadOnlyPaths: [getPackageDir(), dirname(getPackageDir())],\n\t\t});\n\t\treturn;\n\t}\n\n\tprocess.title = APP_NAME;\n\tsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\n\tprocess.env.AI_AGENT = \"apex-code\";\n\tprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\tconfigureHttpDispatcher();\n\tconst { main } = await import(\"./main.ts\");\n\tawait main(args);\n}\n\nawait run();\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n/** The public CLI either supervises a sandbox child or starts an ordinary runtime. */\nimport { fileURLToPath } from \"node:url\";\nimport { APP_NAME, getAgentDir, getPackageDir } from \"./config.ts\";\nimport { setApexEnvironment } from \"./core/environment.ts\";\nimport { configureHttpDispatcher } from \"./core/http-dispatcher.ts\";\nimport { requiresSandboxedChild, resolveSupervisorAllowedHosts } from \"./core/sandbox/cli-launch.ts\";\nimport { launchSandboxedCli } from \"./core/sandbox/cli-supervisor.ts\";\nimport { prepareHostToolBinaries } from \"./utils/tools-manager.ts\";\n\nasync function run(): Promise<void> {\n\tconst args = process.argv.slice(2);\n\t// Validate session IDs before entering the sandbox. Invalid metadata must fail fast\n\t// without starting a child process or touching network/sandbox setup.\n\tconst sessionIdIndex = args.indexOf(\"--session-id\");\n\tconst sessionId = sessionIdIndex >= 0 ? args[sessionIdIndex + 1] : undefined;\n\tif (sessionId !== undefined && !/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(sessionId)) {\n\t\tconsole.error(\n\t\t\t\"Error: Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tif (requiresSandboxedChild(args)) {\n\t\tconst allowedHosts = resolveSupervisorAllowedHosts(process.cwd(), getAgentDir());\n\t\tconst authPath = join(getAgentDir(), \"auth.json\");\n\t\t// Resolve fd and rg out here, where the host home and the network are both still\n\t\t// reachable. The child's own PATH still names host directories the sandbox has\n\t\t// replaced, so without this projection it finds nothing and cannot download either.\n\t\t// Silent because this runs ahead of every mode, including --print and --mode rpc,\n\t\t// whose stdout carries machine-readable output; the child still reports a genuinely\n\t\t// missing tool through its own startup path.\n\t\tconst toolBinaries = await prepareHostToolBinaries(true);\n\n\t\tprocess.exitCode = await launchSandboxedCli({\n\t\t\tcommand: process.execPath,\n\t\t\targs: [\n\t\t\t\tfileURLToPath(\n\t\t\t\t\tnew URL(`./core/sandbox/child-entry${import.meta.url.endsWith(\".ts\") ? \".ts\" : \".js\"}`, import.meta.url),\n\t\t\t\t),\n\t\t\t\t...args,\n\t\t\t],\n\t\t\tenvironment: process.env,\n\t\t\tworkspace: process.cwd(),\n\t\t\tallowedHosts,\n\t\t\tauthPath: existsSync(authPath) ? authPath : undefined,\n\t\t\treadOnlyPaths: [getPackageDir(), dirname(getPackageDir())],\n\t\t\ttoolBinaries,\n\t\t});\n\t\treturn;\n\t}\n\n\tprocess.title = APP_NAME;\n\tsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\n\tprocess.env.AI_AGENT = \"apex-code\";\n\tprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\tconfigureHttpDispatcher();\n\tconst { main } = await import(\"./main.ts\");\n\tawait main(args);\n}\n\nawait run();\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"child-entry.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/child-entry.ts"],"names":[],"mappings":"","sourcesContent":["import { APP_NAME } from \"../../config.ts\";\nimport { main } from \"../../main.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { configureHttpDispatcher } from \"../http-dispatcher.ts\";\n\nprocess.title = APP_NAME;\nsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\nprocess.env.AI_AGENT = \"apex-code\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\nconfigureHttpDispatcher();\n\nawait main(process.argv.slice(2));\n"]}
1
+ {"version":3,"file":"child-entry.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/child-entry.ts"],"names":[],"mappings":"","sourcesContent":["import { APP_NAME } from \"../../config.ts\";\nimport { main } from \"../../main.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { configureHttpDispatcher } from \"../http-dispatcher.ts\";\nimport { installSandboxNetworkRefusalMessages } from \"./network-refusal.ts\";\n\nprocess.title = APP_NAME;\nsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\nprocess.env.AI_AGENT = \"apex-code\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\nconfigureHttpDispatcher();\n// Only the sandboxed child installs this, which is what makes attributing a refused\n// request to the sandbox allowlist accurate rather than a guess about whose proxy replied.\ninstallSandboxNetworkRefusalMessages();\n\nawait main(process.argv.slice(2));\n"]}
@@ -2,10 +2,14 @@ import { APP_NAME } from "../../config.js";
2
2
  import { main } from "../../main.js";
3
3
  import { setApexEnvironment } from "../environment.js";
4
4
  import { configureHttpDispatcher } from "../http-dispatcher.js";
5
+ import { installSandboxNetworkRefusalMessages } from "./network-refusal.js";
5
6
  process.title = APP_NAME;
6
7
  setApexEnvironment("APEX_CODE_CODING_AGENT", "true");
7
8
  process.env.AI_AGENT = "apex-code";
8
9
  process.emitWarning = (() => { });
9
10
  configureHttpDispatcher();
11
+ // Only the sandboxed child installs this, which is what makes attributing a refused
12
+ // request to the sandbox allowlist accurate rather than a guess about whose proxy replied.
13
+ installSandboxNetworkRefusalMessages();
10
14
  await main(process.argv.slice(2));
11
15
  //# sourceMappingURL=child-entry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"child-entry.js","sourceRoot":"","sources":["../../../src/core/sandbox/child-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;AACzB,kBAAkB,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;AACrD,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,WAAW,CAAC;AACnC,OAAO,CAAC,WAAW,GAAG,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAA+B,CAAC;AAC/D,uBAAuB,EAAE,CAAC;AAE1B,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC","sourcesContent":["import { APP_NAME } from \"../../config.ts\";\nimport { main } from \"../../main.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { configureHttpDispatcher } from \"../http-dispatcher.ts\";\n\nprocess.title = APP_NAME;\nsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\nprocess.env.AI_AGENT = \"apex-code\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\nconfigureHttpDispatcher();\n\nawait main(process.argv.slice(2));\n"]}
1
+ {"version":3,"file":"child-entry.js","sourceRoot":"","sources":["../../../src/core/sandbox/child-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;AACzB,kBAAkB,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;AACrD,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,WAAW,CAAC;AACnC,OAAO,CAAC,WAAW,GAAG,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAA+B,CAAC;AAC/D,uBAAuB,EAAE,CAAC;AAC1B,oFAAoF;AACpF,2FAA2F;AAC3F,oCAAoC,EAAE,CAAC;AAEvC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC","sourcesContent":["import { APP_NAME } from \"../../config.ts\";\nimport { main } from \"../../main.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { configureHttpDispatcher } from \"../http-dispatcher.ts\";\nimport { installSandboxNetworkRefusalMessages } from \"./network-refusal.ts\";\n\nprocess.title = APP_NAME;\nsetApexEnvironment(\"APEX_CODE_CODING_AGENT\", \"true\");\nprocess.env.AI_AGENT = \"apex-code\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\nconfigureHttpDispatcher();\n// Only the sandboxed child installs this, which is what makes attributing a refused\n// request to the sandbox allowlist accurate rather than a guess about whose proxy replied.\ninstallSandboxNetworkRefusalMessages();\n\nawait main(process.argv.slice(2));\n"]}
@@ -1,3 +1,4 @@
1
+ import type { HostToolBinary } from "../../utils/tools-manager.ts";
1
2
  import type { SandboxLaunch } from "./supervisor.ts";
2
3
  /**
3
4
  * OS containment is the normal startup path for every command that can construct an
@@ -8,7 +9,14 @@ export declare function requiresSandboxedChild(args: readonly string[]): boolean
8
9
  export interface SandboxedCliLaunch extends SandboxLaunch {
9
10
  readonly environment: NodeJS.ProcessEnv;
10
11
  }
11
- /** Read supervisor policy only from global settings; project settings are untrusted here. */
12
+ /**
13
+ * Read supervisor policy only from global settings; project settings are untrusted here.
14
+ *
15
+ * The built-in provider hosts are added unless explicitly refused, because a deny-all
16
+ * default made a fresh install unable to reach any model while giving the user no way to
17
+ * learn which host to permit. Configured hosts are additive on top, and
18
+ * `allowDefaultHosts: false` restores the strict behaviour for anyone who wants it.
19
+ */
12
20
  export declare function resolveSupervisorAllowedHosts(cwd: string, agentDir: string): readonly string[] | undefined;
13
21
  /**
14
22
  * Allocate agent-owned state under the sole writable workspace mount. The child does
@@ -23,5 +31,6 @@ export declare function buildSandboxedCliLaunch(options: {
23
31
  allowedHosts?: readonly string[];
24
32
  readOnlyPaths?: readonly string[];
25
33
  authPath?: string;
34
+ toolBinaries?: readonly HostToolBinary[];
26
35
  }): SandboxedCliLaunch;
27
36
  //# sourceMappingURL=cli-launch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-launch.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/cli-launch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKrD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAKvE;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;CACxC;AAED,6FAA6F;AAC7F,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAE1G;AAgFD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,kBAAkB,CAgCrB","sourcesContent":["import { mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { SettingsManager } from \"../settings-manager.ts\";\nimport type { SandboxLaunch } from \"./supervisor.ts\";\n\nconst NON_SESSION_COMMANDS = new Set([\"auth\", \"config\", \"install\", \"remove\", \"uninstall\", \"update\", \"list\"]);\nconst METADATA_FLAGS = new Set([\"--version\", \"-v\", \"--export\", \"--list-models\"]);\n\n/**\n * OS containment is the normal startup path for every command that can construct an\n * agent session. Commands that only inspect or maintain host configuration do not\n * create a runtime and therefore remain outside this child boundary.\n */\nexport function requiresSandboxedChild(args: readonly string[]): boolean {\n\tif (NON_SESSION_COMMANDS.has(args[0] ?? \"\")) return false;\n\tif (args.some((argument) => METADATA_FLAGS.has(argument))) return false;\n\tif (args.includes(\"--help\") || args.includes(\"-h\")) return false;\n\treturn true;\n}\n\nexport interface SandboxedCliLaunch extends SandboxLaunch {\n\treadonly environment: NodeJS.ProcessEnv;\n}\n\n/** Read supervisor policy only from global settings; project settings are untrusted here. */\nexport function resolveSupervisorAllowedHosts(cwd: string, agentDir: string): readonly string[] | undefined {\n\treturn SettingsManager.create(cwd, agentDir, { projectTrusted: false }).getNetworkSettings()?.allowedHosts;\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\nfunction buildChildEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n\treturn Object.fromEntries(\n\t\tObject.entries(environment).filter(\n\t\t\t([key]) => SAFE_CHILD_ENVIRONMENT_KEYS.has(key) || SAFE_PROVIDER_CREDENTIAL_KEYS.has(key),\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\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n}): SandboxedCliLaunch {\n\tconst stateDirectory = join(options.workspace, \".apex-code\", \"sandbox-state\");\n\tconst agentDirectory = join(options.workspace, \".apex-code\", \"sandbox-agent\");\n\tconst sessionDirectory = join(options.workspace, \".apex-code\", \"sandbox-sessions\");\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 [stateDirectory, agentDirectory, sessionDirectory, ...Object.values(xdgDirectories)]) {\n\t\tmkdirSync(directory, { recursive: true });\n\t}\n\tconst childEnvironment = buildChildEnvironment(options.environment);\n\tconst readOnlyPaths = [...(options.readOnlyPaths ?? [])];\n\tconst readOnlyFiles = options.authPath ? [options.authPath] : [];\n\treturn {\n\t\tcommand: options.command,\n\t\targs: [...options.args],\n\t\tpolicy: { workspace: options.workspace, allowedHosts: options.allowedHosts ?? [] },\n\t\treadOnlyPaths,\n\t\treadOnlyFiles,\n\t\tenvironment: {\n\t\t\t...childEnvironment,\n\t\t\t...(options.authPath ? { APEX_CODE_AUTH_PATH: options.authPath } : {}),\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
+ {"version":3,"file":"cli-launch.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/cli-launch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAGnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKrD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAKvE;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;CACxC;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAK1G;AAmGD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACzC,GAAG,kBAAkB,CA+CrB","sourcesContent":["import { mkdirSync, readdirSync, rmSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { HostToolBinary } from \"../../utils/tools-manager.ts\";\nimport { SettingsManager } from \"../settings-manager.ts\";\nimport { resolveDefaultAllowedHosts } from \"./default-hosts.ts\";\nimport type { SandboxLaunch } from \"./supervisor.ts\";\n\nconst NON_SESSION_COMMANDS = new Set([\"auth\", \"config\", \"install\", \"remove\", \"uninstall\", \"update\", \"list\"]);\nconst METADATA_FLAGS = new Set([\"--version\", \"-v\", \"--export\", \"--list-models\"]);\n\n/**\n * OS containment is the normal startup path for every command that can construct an\n * agent session. Commands that only inspect or maintain host configuration do not\n * create a runtime and therefore remain outside this child boundary.\n */\nexport function requiresSandboxedChild(args: readonly string[]): boolean {\n\tif (NON_SESSION_COMMANDS.has(args[0] ?? \"\")) return false;\n\tif (args.some((argument) => METADATA_FLAGS.has(argument))) return false;\n\tif (args.includes(\"--help\") || args.includes(\"-h\")) return false;\n\treturn true;\n}\n\nexport interface SandboxedCliLaunch extends SandboxLaunch {\n\treadonly environment: NodeJS.ProcessEnv;\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 network = SettingsManager.create(cwd, agentDir, { projectTrusted: false }).getNetworkSettings();\n\tconst configured = network?.allowedHosts ?? [];\n\tif (network?.allowDefaultHosts === false) return configured;\n\treturn [...new Set([...resolveDefaultAllowedHosts(), ...configured])];\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\nfunction buildChildEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n\treturn Object.fromEntries(\n\t\tObject.entries(environment).filter(\n\t\t\t([key]) => SAFE_CHILD_ENVIRONMENT_KEYS.has(key) || SAFE_PROVIDER_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\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\ttoolBinaries?: readonly HostToolBinary[];\n}): SandboxedCliLaunch {\n\tconst stateDirectory = join(options.workspace, \".apex-code\", \"sandbox-state\");\n\tconst agentDirectory = join(options.workspace, \".apex-code\", \"sandbox-agent\");\n\tconst sessionDirectory = join(options.workspace, \".apex-code\", \"sandbox-sessions\");\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 readOnlyPaths = [...(options.readOnlyPaths ?? [])];\n\tconst readOnlyFiles = options.authPath ? [options.authPath] : [];\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: { workspace: options.workspace, allowedHosts: options.allowedHosts ?? [] },\n\t\treadOnlyPaths,\n\t\treadOnlyFiles,\n\t\treadOnlyBinaries,\n\t\tenvironment: {\n\t\t\t...childEnvironment,\n\t\t\t...(options.authPath ? { APEX_CODE_AUTH_PATH: options.authPath } : {}),\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,6 +1,7 @@
1
- import { mkdirSync } from "node:fs";
1
+ import { mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { SettingsManager } from "../settings-manager.js";
4
+ import { resolveDefaultAllowedHosts } from "./default-hosts.js";
4
5
  const NON_SESSION_COMMANDS = new Set(["auth", "config", "install", "remove", "uninstall", "update", "list"]);
5
6
  const METADATA_FLAGS = new Set(["--version", "-v", "--export", "--list-models"]);
6
7
  /**
@@ -17,9 +18,20 @@ export function requiresSandboxedChild(args) {
17
18
  return false;
18
19
  return true;
19
20
  }
20
- /** Read supervisor policy only from global settings; project settings are untrusted here. */
21
+ /**
22
+ * Read supervisor policy only from global settings; project settings are untrusted here.
23
+ *
24
+ * The built-in provider hosts are added unless explicitly refused, because a deny-all
25
+ * default made a fresh install unable to reach any model while giving the user no way to
26
+ * learn which host to permit. Configured hosts are additive on top, and
27
+ * `allowDefaultHosts: false` restores the strict behaviour for anyone who wants it.
28
+ */
21
29
  export function resolveSupervisorAllowedHosts(cwd, agentDir) {
22
- return SettingsManager.create(cwd, agentDir, { projectTrusted: false }).getNetworkSettings()?.allowedHosts;
30
+ const network = SettingsManager.create(cwd, agentDir, { projectTrusted: false }).getNetworkSettings();
31
+ const configured = network?.allowedHosts ?? [];
32
+ if (network?.allowDefaultHosts === false)
33
+ return configured;
34
+ return [...new Set([...resolveDefaultAllowedHosts(), ...configured])];
23
35
  }
24
36
  const SAFE_CHILD_ENVIRONMENT_KEYS = new Set([
25
37
  "PATH",
@@ -92,6 +104,25 @@ const SAFE_PROVIDER_CREDENTIAL_KEYS = new Set([
92
104
  function buildChildEnvironment(environment) {
93
105
  return Object.fromEntries(Object.entries(environment).filter(([key]) => SAFE_CHILD_ENVIRONMENT_KEYS.has(key) || SAFE_PROVIDER_CREDENTIAL_KEYS.has(key)));
94
106
  }
107
+ /**
108
+ * Drop empty files left in the child's tools directory by a previous launch.
109
+ *
110
+ * A projected tool is bind-mounted over a file there, and bwrap materialises that
111
+ * mountpoint as an empty file on the host which outlives the namespace. If the host
112
+ * tool later disappears, nothing is projected over the stub and the child would
113
+ * otherwise find a 0-byte file where its binary should be. A real downloaded binary
114
+ * is never empty, so size is a safe discriminator.
115
+ */
116
+ function clearStaleToolMountpoints(toolsDirectory) {
117
+ for (const entry of readdirSync(toolsDirectory, { withFileTypes: true })) {
118
+ if (!entry.isFile())
119
+ continue;
120
+ const entryPath = join(toolsDirectory, entry.name);
121
+ if (statSync(entryPath).size === 0) {
122
+ rmSync(entryPath, { force: true });
123
+ }
124
+ }
125
+ }
95
126
  /**
96
127
  * Allocate agent-owned state under the sole writable workspace mount. The child does
97
128
  * not inherit a host home or a host session/config directory, preventing the sandbox
@@ -101,24 +132,39 @@ export function buildSandboxedCliLaunch(options) {
101
132
  const stateDirectory = join(options.workspace, ".apex-code", "sandbox-state");
102
133
  const agentDirectory = join(options.workspace, ".apex-code", "sandbox-agent");
103
134
  const sessionDirectory = join(options.workspace, ".apex-code", "sandbox-sessions");
135
+ // Mirrors getBinDir() as the child will compute it from APEX_CODE_CODING_AGENT_DIR,
136
+ // so a projected tool lands exactly where the child's own lookup already checks.
137
+ const toolsDirectory = join(agentDirectory, "bin");
104
138
  const xdgDirectories = {
105
139
  XDG_CONFIG_HOME: join(stateDirectory, "config"),
106
140
  XDG_CACHE_HOME: join(stateDirectory, "cache"),
107
141
  XDG_DATA_HOME: join(stateDirectory, "data"),
108
142
  XDG_STATE_HOME: join(stateDirectory, "state"),
109
143
  };
110
- for (const directory of [stateDirectory, agentDirectory, sessionDirectory, ...Object.values(xdgDirectories)]) {
144
+ for (const directory of [
145
+ stateDirectory,
146
+ agentDirectory,
147
+ sessionDirectory,
148
+ toolsDirectory,
149
+ ...Object.values(xdgDirectories),
150
+ ]) {
111
151
  mkdirSync(directory, { recursive: true });
112
152
  }
153
+ clearStaleToolMountpoints(toolsDirectory);
113
154
  const childEnvironment = buildChildEnvironment(options.environment);
114
155
  const readOnlyPaths = [...(options.readOnlyPaths ?? [])];
115
156
  const readOnlyFiles = options.authPath ? [options.authPath] : [];
157
+ const readOnlyBinaries = (options.toolBinaries ?? []).map((binary) => ({
158
+ source: binary.path,
159
+ destination: join(toolsDirectory, binary.name),
160
+ }));
116
161
  return {
117
162
  command: options.command,
118
163
  args: [...options.args],
119
164
  policy: { workspace: options.workspace, allowedHosts: options.allowedHosts ?? [] },
120
165
  readOnlyPaths,
121
166
  readOnlyFiles,
167
+ readOnlyBinaries,
122
168
  environment: {
123
169
  ...childEnvironment,
124
170
  ...(options.authPath ? { APEX_CODE_AUTH_PATH: options.authPath } : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"cli-launch.js","sourceRoot":"","sources":["../../../src/core/sandbox/cli-launch.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { SettingsManager } from \"../settings-manager.ts\";\nimport type { SandboxLaunch } from \"./supervisor.ts\";\n\nconst NON_SESSION_COMMANDS = new Set([\"auth\", \"config\", \"install\", \"remove\", \"uninstall\", \"update\", \"list\"]);\nconst METADATA_FLAGS = new Set([\"--version\", \"-v\", \"--export\", \"--list-models\"]);\n\n/**\n * OS containment is the normal startup path for every command that can construct an\n * agent session. Commands that only inspect or maintain host configuration do not\n * create a runtime and therefore remain outside this child boundary.\n */\nexport function requiresSandboxedChild(args: readonly string[]): boolean {\n\tif (NON_SESSION_COMMANDS.has(args[0] ?? \"\")) return false;\n\tif (args.some((argument) => METADATA_FLAGS.has(argument))) return false;\n\tif (args.includes(\"--help\") || args.includes(\"-h\")) return false;\n\treturn true;\n}\n\nexport interface SandboxedCliLaunch extends SandboxLaunch {\n\treadonly environment: NodeJS.ProcessEnv;\n}\n\n/** Read supervisor policy only from global settings; project settings are untrusted here. */\nexport function resolveSupervisorAllowedHosts(cwd: string, agentDir: string): readonly string[] | undefined {\n\treturn SettingsManager.create(cwd, agentDir, { projectTrusted: false }).getNetworkSettings()?.allowedHosts;\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\nfunction buildChildEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n\treturn Object.fromEntries(\n\t\tObject.entries(environment).filter(\n\t\t\t([key]) => SAFE_CHILD_ENVIRONMENT_KEYS.has(key) || SAFE_PROVIDER_CREDENTIAL_KEYS.has(key),\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\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n}): SandboxedCliLaunch {\n\tconst stateDirectory = join(options.workspace, \".apex-code\", \"sandbox-state\");\n\tconst agentDirectory = join(options.workspace, \".apex-code\", \"sandbox-agent\");\n\tconst sessionDirectory = join(options.workspace, \".apex-code\", \"sandbox-sessions\");\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 [stateDirectory, agentDirectory, sessionDirectory, ...Object.values(xdgDirectories)]) {\n\t\tmkdirSync(directory, { recursive: true });\n\t}\n\tconst childEnvironment = buildChildEnvironment(options.environment);\n\tconst readOnlyPaths = [...(options.readOnlyPaths ?? [])];\n\tconst readOnlyFiles = options.authPath ? [options.authPath] : [];\n\treturn {\n\t\tcommand: options.command,\n\t\targs: [...options.args],\n\t\tpolicy: { workspace: options.workspace, allowedHosts: options.allowedHosts ?? [] },\n\t\treadOnlyPaths,\n\t\treadOnlyFiles,\n\t\tenvironment: {\n\t\t\t...childEnvironment,\n\t\t\t...(options.authPath ? { APEX_CODE_AUTH_PATH: options.authPath } : {}),\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
+ {"version":3,"file":"cli-launch.js","sourceRoot":"","sources":["../../../src/core/sandbox/cli-launch.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync, readdirSync, rmSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { HostToolBinary } from \"../../utils/tools-manager.ts\";\nimport { SettingsManager } from \"../settings-manager.ts\";\nimport { resolveDefaultAllowedHosts } from \"./default-hosts.ts\";\nimport type { SandboxLaunch } from \"./supervisor.ts\";\n\nconst NON_SESSION_COMMANDS = new Set([\"auth\", \"config\", \"install\", \"remove\", \"uninstall\", \"update\", \"list\"]);\nconst METADATA_FLAGS = new Set([\"--version\", \"-v\", \"--export\", \"--list-models\"]);\n\n/**\n * OS containment is the normal startup path for every command that can construct an\n * agent session. Commands that only inspect or maintain host configuration do not\n * create a runtime and therefore remain outside this child boundary.\n */\nexport function requiresSandboxedChild(args: readonly string[]): boolean {\n\tif (NON_SESSION_COMMANDS.has(args[0] ?? \"\")) return false;\n\tif (args.some((argument) => METADATA_FLAGS.has(argument))) return false;\n\tif (args.includes(\"--help\") || args.includes(\"-h\")) return false;\n\treturn true;\n}\n\nexport interface SandboxedCliLaunch extends SandboxLaunch {\n\treadonly environment: NodeJS.ProcessEnv;\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 network = SettingsManager.create(cwd, agentDir, { projectTrusted: false }).getNetworkSettings();\n\tconst configured = network?.allowedHosts ?? [];\n\tif (network?.allowDefaultHosts === false) return configured;\n\treturn [...new Set([...resolveDefaultAllowedHosts(), ...configured])];\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\nfunction buildChildEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n\treturn Object.fromEntries(\n\t\tObject.entries(environment).filter(\n\t\t\t([key]) => SAFE_CHILD_ENVIRONMENT_KEYS.has(key) || SAFE_PROVIDER_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\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\ttoolBinaries?: readonly HostToolBinary[];\n}): SandboxedCliLaunch {\n\tconst stateDirectory = join(options.workspace, \".apex-code\", \"sandbox-state\");\n\tconst agentDirectory = join(options.workspace, \".apex-code\", \"sandbox-agent\");\n\tconst sessionDirectory = join(options.workspace, \".apex-code\", \"sandbox-sessions\");\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 readOnlyPaths = [...(options.readOnlyPaths ?? [])];\n\tconst readOnlyFiles = options.authPath ? [options.authPath] : [];\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: { workspace: options.workspace, allowedHosts: options.allowedHosts ?? [] },\n\t\treadOnlyPaths,\n\t\treadOnlyFiles,\n\t\treadOnlyBinaries,\n\t\tenvironment: {\n\t\t\t...childEnvironment,\n\t\t\t...(options.authPath ? { APEX_CODE_AUTH_PATH: options.authPath } : {}),\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,3 +1,4 @@
1
+ import type { HostToolBinary } from "../../utils/tools-manager.ts";
1
2
  import { type SandboxBackend } from "./supervisor.ts";
2
3
  import { SandboxViolationStore } from "./violations.ts";
3
4
  export interface CliSandboxDependencies {
@@ -20,6 +21,7 @@ export declare function launchSandboxedCli(options: {
20
21
  allowedHosts?: readonly string[];
21
22
  readOnlyPaths?: readonly string[];
22
23
  authPath?: string;
24
+ toolBinaries?: readonly HostToolBinary[];
23
25
  dependencies?: Partial<CliSandboxDependencies>;
24
26
  }): Promise<number>;
25
27
  //# sourceMappingURL=cli-supervisor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-supervisor.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/cli-supervisor.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/E,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,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAC/C,GAAG,OAAO,CAAC,MAAM,CAAC,CAiClB","sourcesContent":["import { buildSandboxedCliLaunch } from \"./cli-launch.ts\";\nimport { createLinuxSandboxBackend } from \"./linux-backend.ts\";\nimport { createMacosSandboxBackend } from \"./macos-backend.ts\";\nimport { createSandboxPolicy } from \"./policy.ts\";\nimport { createSandboxSupervisor, type SandboxBackend } 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\tallowedHosts?: readonly string[];\n\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\tdependencies?: Partial<CliSandboxDependencies>;\n}): Promise<number> {\n\tconst dependencies = { ...defaultDependencies, ...options.dependencies };\n\tconst policyResult = createSandboxPolicy({ workspace: options.workspace, allowedHosts: options.allowedHosts });\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 supervisor = createSandboxSupervisor({ backend, policy: policyResult.policy });\n\tconst launch = buildSandboxedCliLaunch({\n\t\tworkspace: policyResult.policy.workspace,\n\t\tcommand: options.command,\n\t\targs: options.args,\n\t\tenvironment: options.environment,\n\t\tallowedHosts: options.allowedHosts,\n\t\treadOnlyPaths: options.readOnlyPaths,\n\t\tauthPath: options.authPath,\n\t});\n\ttry {\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\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\tawait supervisor.close();\n\t}\n}\n"]}
1
+ {"version":3,"file":"cli-supervisor.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/cli-supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAKnE,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/E,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,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACzC,YAAY,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAC/C,GAAG,OAAO,CAAC,MAAM,CAAC,CAkClB","sourcesContent":["import type { HostToolBinary } from \"../../utils/tools-manager.ts\";\nimport { buildSandboxedCliLaunch } from \"./cli-launch.ts\";\nimport { createLinuxSandboxBackend } from \"./linux-backend.ts\";\nimport { createMacosSandboxBackend } from \"./macos-backend.ts\";\nimport { createSandboxPolicy } from \"./policy.ts\";\nimport { createSandboxSupervisor, type SandboxBackend } 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\tallowedHosts?: readonly string[];\n\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\ttoolBinaries?: readonly HostToolBinary[];\n\tdependencies?: Partial<CliSandboxDependencies>;\n}): Promise<number> {\n\tconst dependencies = { ...defaultDependencies, ...options.dependencies };\n\tconst policyResult = createSandboxPolicy({ workspace: options.workspace, allowedHosts: options.allowedHosts });\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 supervisor = createSandboxSupervisor({ backend, policy: policyResult.policy });\n\tconst launch = buildSandboxedCliLaunch({\n\t\tworkspace: policyResult.policy.workspace,\n\t\tcommand: options.command,\n\t\targs: options.args,\n\t\tenvironment: options.environment,\n\t\tallowedHosts: options.allowedHosts,\n\t\treadOnlyPaths: options.readOnlyPaths,\n\t\tauthPath: options.authPath,\n\t\ttoolBinaries: options.toolBinaries,\n\t});\n\ttry {\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\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\tawait supervisor.close();\n\t}\n}\n"]}
@@ -38,6 +38,7 @@ export async function launchSandboxedCli(options) {
38
38
  allowedHosts: options.allowedHosts,
39
39
  readOnlyPaths: options.readOnlyPaths,
40
40
  authPath: options.authPath,
41
+ toolBinaries: options.toolBinaries,
41
42
  });
42
43
  try {
43
44
  return await supervisor.launch(launch);
@@ -1 +1 @@
1
- {"version":3,"file":"cli-supervisor.js","sourceRoot":"","sources":["../../../src/core/sandbox/cli-supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAuB,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAOxD;;6CAE6C;AAC7C,SAAS,2BAA2B,CAAC,OAAkD,EAAkB;IACxG,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC7E,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;AAAA,CAC1C;AAED,MAAM,mBAAmB,GAA2B;IACnD,aAAa,EAAE,2BAA2B;IAC1C,MAAM,EAAE,OAAO,CAAC,MAAM;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OASxC,EAAmB;IACnB,MAAM,YAAY,GAAG,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IACzE,MAAM,YAAY,GAAG,mBAAmB,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/G,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACrC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,0DAA0D,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;QAC7G,OAAO,CAAC,CAAC;IACV,CAAC;IACD,MAAM,cAAc,GAAG,IAAI,qBAAqB,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,YAAY,CAAC,aAAa,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,uBAAuB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,uBAAuB,CAAC;QACtC,SAAS,EAAE,YAAY,CAAC,MAAM,CAAC,SAAS;QACxC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC1B,CAAC,CAAC;IACH,IAAI,CAAC;QACJ,OAAO,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;QACvF,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;QACjD,OAAO,CAAC,CAAC;IACV,CAAC;YAAS,CAAC;QACV,KAAK,MAAM,SAAS,IAAI,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/C,YAAY,CAAC,MAAM,CAAC,KAAK,CACxB,sBAAsB,SAAS,CAAC,IAAI,MAAM,SAAS,CAAC,OAAO,QAAM,SAAS,CAAC,MAAM,IAAI,CACrF,CAAC;QACH,CAAC;QACD,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;AAAA,CACD","sourcesContent":["import { buildSandboxedCliLaunch } from \"./cli-launch.ts\";\nimport { createLinuxSandboxBackend } from \"./linux-backend.ts\";\nimport { createMacosSandboxBackend } from \"./macos-backend.ts\";\nimport { createSandboxPolicy } from \"./policy.ts\";\nimport { createSandboxSupervisor, type SandboxBackend } 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\tallowedHosts?: readonly string[];\n\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\tdependencies?: Partial<CliSandboxDependencies>;\n}): Promise<number> {\n\tconst dependencies = { ...defaultDependencies, ...options.dependencies };\n\tconst policyResult = createSandboxPolicy({ workspace: options.workspace, allowedHosts: options.allowedHosts });\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 supervisor = createSandboxSupervisor({ backend, policy: policyResult.policy });\n\tconst launch = buildSandboxedCliLaunch({\n\t\tworkspace: policyResult.policy.workspace,\n\t\tcommand: options.command,\n\t\targs: options.args,\n\t\tenvironment: options.environment,\n\t\tallowedHosts: options.allowedHosts,\n\t\treadOnlyPaths: options.readOnlyPaths,\n\t\tauthPath: options.authPath,\n\t});\n\ttry {\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\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\tawait supervisor.close();\n\t}\n}\n"]}
1
+ {"version":3,"file":"cli-supervisor.js","sourceRoot":"","sources":["../../../src/core/sandbox/cli-supervisor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAuB,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAOxD;;6CAE6C;AAC7C,SAAS,2BAA2B,CAAC,OAAkD,EAAkB;IACxG,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC7E,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;AAAA,CAC1C;AAED,MAAM,mBAAmB,GAA2B;IACnD,aAAa,EAAE,2BAA2B;IAC1C,MAAM,EAAE,OAAO,CAAC,MAAM;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAUxC,EAAmB;IACnB,MAAM,YAAY,GAAG,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IACzE,MAAM,YAAY,GAAG,mBAAmB,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/G,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACrC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,0DAA0D,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;QAC7G,OAAO,CAAC,CAAC;IACV,CAAC;IACD,MAAM,cAAc,GAAG,IAAI,qBAAqB,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,YAAY,CAAC,aAAa,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,uBAAuB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,uBAAuB,CAAC;QACtC,SAAS,EAAE,YAAY,CAAC,MAAM,CAAC,SAAS;QACxC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;KAClC,CAAC,CAAC;IACH,IAAI,CAAC;QACJ,OAAO,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;QACvF,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;QACjD,OAAO,CAAC,CAAC;IACV,CAAC;YAAS,CAAC;QACV,KAAK,MAAM,SAAS,IAAI,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/C,YAAY,CAAC,MAAM,CAAC,KAAK,CACxB,sBAAsB,SAAS,CAAC,IAAI,MAAM,SAAS,CAAC,OAAO,QAAM,SAAS,CAAC,MAAM,IAAI,CACrF,CAAC;QACH,CAAC;QACD,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;AAAA,CACD","sourcesContent":["import type { HostToolBinary } from \"../../utils/tools-manager.ts\";\nimport { buildSandboxedCliLaunch } from \"./cli-launch.ts\";\nimport { createLinuxSandboxBackend } from \"./linux-backend.ts\";\nimport { createMacosSandboxBackend } from \"./macos-backend.ts\";\nimport { createSandboxPolicy } from \"./policy.ts\";\nimport { createSandboxSupervisor, type SandboxBackend } 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\tallowedHosts?: readonly string[];\n\treadOnlyPaths?: readonly string[];\n\tauthPath?: string;\n\ttoolBinaries?: readonly HostToolBinary[];\n\tdependencies?: Partial<CliSandboxDependencies>;\n}): Promise<number> {\n\tconst dependencies = { ...defaultDependencies, ...options.dependencies };\n\tconst policyResult = createSandboxPolicy({ workspace: options.workspace, allowedHosts: options.allowedHosts });\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 supervisor = createSandboxSupervisor({ backend, policy: policyResult.policy });\n\tconst launch = buildSandboxedCliLaunch({\n\t\tworkspace: policyResult.policy.workspace,\n\t\tcommand: options.command,\n\t\targs: options.args,\n\t\tenvironment: options.environment,\n\t\tallowedHosts: options.allowedHosts,\n\t\treadOnlyPaths: options.readOnlyPaths,\n\t\tauthPath: options.authPath,\n\t\ttoolBinaries: options.toolBinaries,\n\t});\n\ttry {\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\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\tawait supervisor.close();\n\t}\n}\n"]}
@@ -0,0 +1,28 @@
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
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,69 @@
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
@@ -0,0 +1 @@
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"]}