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.
- package/CHANGELOG.md +47 -1
- package/README.md +7 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/sandbox/child-entry.d.ts.map +1 -1
- package/dist/core/sandbox/child-entry.js +4 -0
- package/dist/core/sandbox/child-entry.js.map +1 -1
- package/dist/core/sandbox/cli-launch.d.ts +10 -1
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
- package/dist/core/sandbox/cli-launch.js +50 -4
- package/dist/core/sandbox/cli-launch.js.map +1 -1
- package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/cli-supervisor.js +1 -0
- package/dist/core/sandbox/cli-supervisor.js.map +1 -1
- package/dist/core/sandbox/default-hosts.d.ts +28 -0
- package/dist/core/sandbox/default-hosts.d.ts.map +1 -0
- package/dist/core/sandbox/default-hosts.js +69 -0
- package/dist/core/sandbox/default-hosts.js.map +1 -0
- package/dist/core/sandbox/linux-backend.d.ts +16 -0
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
- package/dist/core/sandbox/linux-backend.js +60 -11
- package/dist/core/sandbox/linux-backend.js.map +1 -1
- package/dist/core/sandbox/macos-backend.d.ts +7 -0
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
- package/dist/core/sandbox/macos-backend.js +17 -9
- package/dist/core/sandbox/macos-backend.js.map +1 -1
- package/dist/core/sandbox/network-refusal.d.ts +24 -0
- package/dist/core/sandbox/network-refusal.d.ts.map +1 -0
- package/dist/core/sandbox/network-refusal.js +72 -0
- package/dist/core/sandbox/network-refusal.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +9 -0
- package/dist/core/sandbox/supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/supervisor.js.map +1 -1
- package/dist/core/session-share.d.ts +15 -0
- package/dist/core/session-share.d.ts.map +1 -1
- package/dist/core/session-share.js +23 -0
- package/dist/core/session-share.js.map +1 -1
- package/dist/core/settings-manager.d.ts +5 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +3 -3
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +28 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +68 -5
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +2 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +2 -0
- package/dist/utils/version-check.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
# Apex Code changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
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
|
|
46
|
-
|
|
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;
|
|
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;
|
|
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
|
-
/**
|
|
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":"
|
|
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
|
-
/**
|
|
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
|
-
|
|
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 [
|
|
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":"
|
|
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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-supervisor.js","sourceRoot":"","sources":["../../../src/core/sandbox/cli-supervisor.ts"],"names":[],"mappings":"
|
|
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"]}
|