@nebutra/sandbox-runtime 0.1.1 → 0.1.3
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 +24 -0
- package/LICENSE +21 -676
- package/README.md +78 -0
- package/dist/{chunk-5TXS6ZSQ.js → chunk-6UZA3GQP.js} +7 -2
- package/dist/chunk-6UZA3GQP.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +27 -16
- package/.nebutra/debug/sandbox-runtime.jsonl +0 -1
- package/.turbo/turbo-build.log +0 -22
- package/.turbo/turbo-test.log +0 -14
- package/.turbo/turbo-typecheck.log +0 -4
- package/dist/chunk-5TXS6ZSQ.js.map +0 -1
- package/examples/doctor.ts +0 -6
- package/examples/dry-run-plan.ts +0 -9
- package/examples/local-echo.ts +0 -11
- package/src/cli.ts +0 -22
- package/src/index.test.ts +0 -41
- package/src/index.ts +0 -201
- package/tsconfig.json +0 -12
- package/tsup.config.ts +0 -11
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @nebutra/sandbox-runtime
|
|
2
|
+
|
|
3
|
+
Unified sandbox runtime router for local and remote execution providers.
|
|
4
|
+
|
|
5
|
+
This package defines the domain-neutral sandbox contract used by Nebutra
|
|
6
|
+
execution packages. It routes requests to configured providers, exposes provider
|
|
7
|
+
health checks, and records debug events for execution troubleshooting.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @nebutra/sandbox-runtime
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { SandboxRuntime } from "@nebutra/sandbox-runtime";
|
|
19
|
+
|
|
20
|
+
const runtime = SandboxRuntime.fromConfig();
|
|
21
|
+
|
|
22
|
+
const result = await runtime.exec({
|
|
23
|
+
cmd: "echo sandbox ok",
|
|
24
|
+
tenantId: "org_123",
|
|
25
|
+
threadId: "thread_123",
|
|
26
|
+
hints: {
|
|
27
|
+
networkAccess: false,
|
|
28
|
+
needsGpu: false,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Providers
|
|
34
|
+
|
|
35
|
+
Default providers:
|
|
36
|
+
|
|
37
|
+
| Provider | Behavior |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `local_mac` | Calls the local sandbox service at `SANDBOX_RUNTIME_LOCAL_URL` or `http://127.0.0.1:8020/api/v1/sandbox/exec` |
|
|
40
|
+
| `remote_code` | Placeholder provider gated by `SANDBOX_REMOTE_CODE_TOKEN` |
|
|
41
|
+
| `remote_gpu` | Placeholder provider gated by `SANDBOX_REMOTE_GPU_TOKEN` |
|
|
42
|
+
| `remote_workspace` | Placeholder provider gated by `SANDBOX_REMOTE_WORKSPACE_TOKEN` |
|
|
43
|
+
|
|
44
|
+
Remote placeholders intentionally throw until a provider SDK is configured by
|
|
45
|
+
the host application.
|
|
46
|
+
|
|
47
|
+
## Routing
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const runtime = SandboxRuntime.fromConfig({
|
|
51
|
+
routes: [
|
|
52
|
+
{ when: { needsGpu: true }, provider: "remote_gpu" },
|
|
53
|
+
{ when: { durationS: 30 }, provider: "remote_code" },
|
|
54
|
+
{ when: { needsGpu: false }, provider: "local_mac" },
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const plan = runtime.plan({ cmd: "pnpm test", hints: { needsGpu: false } });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The first matching static route wins. If no route matches, the runtime falls
|
|
62
|
+
back to `local_mac` when it is configured.
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
| Export | Description |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `SandboxRuntime` | Provider registry, planner, executor, and doctor |
|
|
69
|
+
| `createLocalMacSandbox()` | Local sandbox-service adapter |
|
|
70
|
+
| `createRemotePlaceholder(id, envKey)` | Explicit placeholder for remote providers |
|
|
71
|
+
| `sandboxDebugPath()` | Debug path helper |
|
|
72
|
+
| `readSandboxDebug(limit)` | Read sandbox debug events |
|
|
73
|
+
| `ExecRequest` / `ExecResponse` | Execution request and response contracts |
|
|
74
|
+
| `Sandbox` / `SandboxHealth` | Provider interface and health shape |
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
readCapabilityDebug
|
|
6
6
|
} from "@nebutra/capability-kit/debug";
|
|
7
7
|
import { CapabilityError } from "@nebutra/errors";
|
|
8
|
+
import pLimit from "p-limit";
|
|
9
|
+
var SANDBOX_DOCTOR_CONCURRENCY = 4;
|
|
8
10
|
function sandboxDebugPath() {
|
|
9
11
|
return capabilityDebugPath("sandbox-runtime");
|
|
10
12
|
}
|
|
@@ -127,7 +129,10 @@ var SandboxRuntime = class _SandboxRuntime {
|
|
|
127
129
|
return provider.exec(request);
|
|
128
130
|
}
|
|
129
131
|
async doctor() {
|
|
130
|
-
|
|
132
|
+
const limit = pLimit(SANDBOX_DOCTOR_CONCURRENCY);
|
|
133
|
+
return Promise.all(
|
|
134
|
+
Array.from(this.#providers.values()).map((provider) => limit(() => provider.doctor()))
|
|
135
|
+
);
|
|
131
136
|
}
|
|
132
137
|
};
|
|
133
138
|
async function readSandboxDebug(limit = 10) {
|
|
@@ -141,4 +146,4 @@ export {
|
|
|
141
146
|
SandboxRuntime,
|
|
142
147
|
readSandboxDebug
|
|
143
148
|
};
|
|
144
|
-
//# sourceMappingURL=chunk-
|
|
149
|
+
//# sourceMappingURL=chunk-6UZA3GQP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n appendCapabilityDebug,\n capabilityDebugPath,\n readCapabilityDebug,\n} from \"@nebutra/capability-kit/debug\";\nimport { CapabilityError } from \"@nebutra/errors\";\nimport pLimit from \"p-limit\";\n\nexport interface ExecHints {\n readonly durationS?: number;\n readonly needsGpu?: boolean;\n readonly networkAccess?: boolean;\n}\n\nexport interface ExecRequest {\n readonly cmd: string;\n readonly filesIn?: Record<string, string>;\n readonly hints?: ExecHints;\n readonly tenantId?: string;\n readonly threadId?: string;\n}\n\nexport interface ExecResponse {\n readonly exitCode: number;\n readonly aggregatedOutput: string;\n readonly executedOn: string;\n}\n\nexport interface SandboxHealth {\n readonly provider: string;\n readonly ok: boolean;\n readonly suggestion?: string;\n}\n\nexport interface Sandbox {\n readonly id: string;\n exec(request: ExecRequest): Promise<ExecResponse>;\n doctor(): Promise<SandboxHealth>;\n}\n\nexport interface RouteRule {\n readonly when: Partial<ExecHints>;\n readonly provider: string;\n}\n\nexport interface SandboxRuntimeConfig {\n readonly providers?: readonly Sandbox[];\n readonly routes?: readonly RouteRule[];\n}\n\nconst SANDBOX_DOCTOR_CONCURRENCY = 4;\n\nexport function sandboxDebugPath(): string {\n return capabilityDebugPath(\"sandbox-runtime\");\n}\n\nasync function appendSandboxDebug(entry: Record<string, unknown>): Promise<void> {\n await appendCapabilityDebug(\"sandbox-runtime\", entry);\n}\n\nexport function createLocalMacSandbox(\n options: { endpoint?: string; fetch?: typeof fetch } = {},\n): Sandbox {\n const endpoint =\n options.endpoint ??\n process.env.SANDBOX_RUNTIME_LOCAL_URL ??\n \"http://127.0.0.1:8020/api/v1/sandbox/exec\";\n const fetchImpl = options.fetch ?? fetch;\n return {\n id: \"local_mac\",\n async exec(request) {\n const response = await fetchImpl(endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n tenantId: request.tenantId ?? \"local\",\n threadId: request.threadId ?? \"local\",\n command: request.cmd,\n capabilityPolicy: {\n kind: \"external_sandbox\",\n network_access: request.hints?.networkAccess ?? false,\n },\n }),\n });\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new CapabilityError(\n \"sandbox-runtime\",\n `Local sandbox refused execution (${response.status})`,\n {\n suggestion:\n \"Start the Rust sandbox service or route this task to a configured remote provider.\",\n metadata: { detail },\n statusCode: response.status,\n },\n );\n }\n const result = (await response.json()) as ExecResponse;\n await appendSandboxDebug({\n type: \"exec\",\n provider: \"local_mac\",\n command: request.cmd,\n result,\n });\n return result;\n },\n async doctor() {\n try {\n const result = await this.exec({ cmd: \"echo sandbox ok\" });\n return { provider: \"local_mac\", ok: result.exitCode === 0 };\n } catch (error) {\n return {\n provider: \"local_mac\",\n ok: false,\n suggestion:\n error instanceof Error ? error.message : \"Check local sandbox service reachability.\",\n };\n }\n },\n };\n}\n\nexport function createRemotePlaceholder(\n id: \"remote_code\" | \"remote_gpu\" | \"remote_workspace\",\n envKey: string,\n): Sandbox {\n return {\n id,\n async exec() {\n throw new CapabilityError(\"sandbox-runtime\", `${id} adapter is not configured`, {\n suggestion: `Set ${envKey} and wire the provider SDK before routing tasks to ${id}.`,\n statusCode: 503,\n });\n },\n async doctor() {\n return process.env[envKey]\n ? { provider: id, ok: true }\n : { provider: id, ok: false, suggestion: `Set ${envKey} to enable ${id}.` };\n },\n };\n}\n\nexport class SandboxRuntime {\n readonly #providers = new Map<string, Sandbox>();\n readonly #routes: readonly RouteRule[];\n\n constructor(config: SandboxRuntimeConfig = {}) {\n for (const provider of config.providers ?? [\n createLocalMacSandbox(),\n createRemotePlaceholder(\"remote_code\", \"SANDBOX_REMOTE_CODE_TOKEN\"),\n createRemotePlaceholder(\"remote_gpu\", \"SANDBOX_REMOTE_GPU_TOKEN\"),\n createRemotePlaceholder(\"remote_workspace\", \"SANDBOX_REMOTE_WORKSPACE_TOKEN\"),\n ]) {\n this.#providers.set(provider.id, provider);\n }\n this.#routes = config.routes ?? [\n { when: { needsGpu: true }, provider: \"remote_gpu\" },\n { when: { durationS: 30 }, provider: \"remote_code\" },\n { when: { needsGpu: false }, provider: \"local_mac\" },\n ];\n }\n\n static fromConfig(config: SandboxRuntimeConfig = {}): SandboxRuntime {\n return new SandboxRuntime(config);\n }\n\n plan(request: ExecRequest): { provider: string; reason: string } {\n const hints = request.hints ?? {};\n for (const rule of this.#routes) {\n const matches = Object.entries(rule.when).every(\n ([key, value]) => hints[key as keyof ExecHints] === value,\n );\n if (matches && this.#providers.has(rule.provider)) {\n return { provider: rule.provider, reason: \"matched static routing rule\" };\n }\n }\n return {\n provider: this.#providers.has(\"local_mac\")\n ? \"local_mac\"\n : (Array.from(this.#providers.keys())[0] ?? \"\"),\n reason: \"default route\",\n };\n }\n\n async exec(request: ExecRequest): Promise<ExecResponse> {\n const plan = this.plan(request);\n const provider = this.#providers.get(plan.provider);\n if (!provider) {\n throw new CapabilityError(\"sandbox-runtime\", \"No sandbox provider available\", {\n suggestion: \"Configure at least one sandbox provider before executing commands.\",\n metadata: plan,\n });\n }\n return provider.exec(request);\n }\n\n async doctor(): Promise<SandboxHealth[]> {\n const limit = pLimit(SANDBOX_DOCTOR_CONCURRENCY);\n return Promise.all(\n Array.from(this.#providers.values()).map((provider) => limit(() => provider.doctor())),\n );\n }\n}\n\nexport async function readSandboxDebug(limit = 10): Promise<unknown[]> {\n return readCapabilityDebug(\"sandbox-runtime\", { limit });\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebutra/sandbox-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Unified sandbox runtime router for local and remote execution providers",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,20 +11,30 @@
|
|
|
11
11
|
"surface": "execution-router",
|
|
12
12
|
"summary": "Sandbox trait, provider router, doctor, and debug tools"
|
|
13
13
|
},
|
|
14
|
-
"main": "./
|
|
15
|
-
"types": "./
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
16
|
"exports": {
|
|
17
|
-
".":
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"typecheck": "tsc --noEmit"
|
|
18
27
|
},
|
|
19
28
|
"dependencies": {
|
|
20
|
-
"@nebutra/capability-kit": "
|
|
21
|
-
"@nebutra/errors": "
|
|
29
|
+
"@nebutra/capability-kit": "workspace:*",
|
|
30
|
+
"@nebutra/errors": "workspace:*",
|
|
31
|
+
"p-limit": "^7.3.0"
|
|
22
32
|
},
|
|
23
33
|
"devDependencies": {
|
|
24
|
-
"@types/node": "
|
|
25
|
-
"tsup": "
|
|
26
|
-
"typescript": "
|
|
27
|
-
"vitest": "
|
|
34
|
+
"@types/node": "catalog:",
|
|
35
|
+
"tsup": "catalog:",
|
|
36
|
+
"typescript": "catalog:",
|
|
37
|
+
"vitest": "catalog:"
|
|
28
38
|
},
|
|
29
39
|
"homepage": "https://github.com/Nebutra/Nebutra-Sailor/tree/main/packages/ai/sandbox-runtime#readme",
|
|
30
40
|
"repository": {
|
|
@@ -38,9 +48,10 @@
|
|
|
38
48
|
"publishConfig": {
|
|
39
49
|
"access": "public"
|
|
40
50
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
"files": [
|
|
52
|
+
"dist",
|
|
53
|
+
"README.md",
|
|
54
|
+
"LICENSE",
|
|
55
|
+
"CHANGELOG.md"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"at":"2026-05-19T08:15:00.536Z","type":"exec","provider":"local_mac","command":"echo sandbox ok","result":{"exitCode":0,"aggregatedOutput":"sandbox ok\n","executedOn":"local_mac"}}
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @nebutra/sandbox-runtime@0.1.1 build /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/sandbox-runtime
|
|
3
|
-
> tsup
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/sandbox-runtime/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: es2022
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mESM[39m Build start
|
|
12
|
-
[32mESM[39m [1mdist/cli.js [22m[32m882.00 B[39m
|
|
13
|
-
[32mESM[39m [1mdist/index.js [22m[32m302.00 B[39m
|
|
14
|
-
[32mESM[39m [1mdist/chunk-5TXS6ZSQ.js [22m[32m4.66 KB[39m
|
|
15
|
-
[32mESM[39m [1mdist/cli.js.map [22m[32m1.56 KB[39m
|
|
16
|
-
[32mESM[39m [1mdist/index.js.map [22m[32m71.00 B[39m
|
|
17
|
-
[32mESM[39m [1mdist/chunk-5TXS6ZSQ.js.map [22m[32m9.32 KB[39m
|
|
18
|
-
[32mESM[39m ⚡️ Build success in 81ms
|
|
19
|
-
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in 10574ms
|
|
21
|
-
[32mDTS[39m [1mdist/cli.d.ts [22m[32m13.00 B[39m
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.83 KB[39m
|
package/.turbo/turbo-test.log
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @nebutra/sandbox-runtime@0.1.1 test /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/sandbox-runtime
|
|
3
|
-
> vitest run
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
[1m[30m[46m RUN [49m[39m[22m [36mv4.1.4 [39m[90m/home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/sandbox-runtime[39m
|
|
7
|
-
|
|
8
|
-
[32m✓[39m src/index.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 263[2mms[22m[39m
|
|
9
|
-
|
|
10
|
-
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
11
|
-
[2m Tests [22m [1m[32m2 passed[39m[22m[90m (2)[39m
|
|
12
|
-
[2m Start at [22m 08:14:59
|
|
13
|
-
[2m Duration [22m 1.35s[2m (transform 376ms, setup 0ms, import 442ms, tests 263ms, environment 0ms)[22m
|
|
14
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n appendCapabilityDebug,\n capabilityDebugPath,\n readCapabilityDebug,\n} from \"@nebutra/capability-kit/debug\";\nimport { CapabilityError } from \"@nebutra/errors\";\n\nexport interface ExecHints {\n readonly durationS?: number;\n readonly needsGpu?: boolean;\n readonly networkAccess?: boolean;\n}\n\nexport interface ExecRequest {\n readonly cmd: string;\n readonly filesIn?: Record<string, string>;\n readonly hints?: ExecHints;\n readonly tenantId?: string;\n readonly threadId?: string;\n}\n\nexport interface ExecResponse {\n readonly exitCode: number;\n readonly aggregatedOutput: string;\n readonly executedOn: string;\n}\n\nexport interface SandboxHealth {\n readonly provider: string;\n readonly ok: boolean;\n readonly suggestion?: string;\n}\n\nexport interface Sandbox {\n readonly id: string;\n exec(request: ExecRequest): Promise<ExecResponse>;\n doctor(): Promise<SandboxHealth>;\n}\n\nexport interface RouteRule {\n readonly when: Partial<ExecHints>;\n readonly provider: string;\n}\n\nexport interface SandboxRuntimeConfig {\n readonly providers?: readonly Sandbox[];\n readonly routes?: readonly RouteRule[];\n}\n\nexport function sandboxDebugPath(): string {\n return capabilityDebugPath(\"sandbox-runtime\");\n}\n\nasync function appendSandboxDebug(entry: Record<string, unknown>): Promise<void> {\n await appendCapabilityDebug(\"sandbox-runtime\", entry);\n}\n\nexport function createLocalMacSandbox(\n options: { endpoint?: string; fetch?: typeof fetch } = {},\n): Sandbox {\n const endpoint =\n options.endpoint ??\n process.env.SANDBOX_RUNTIME_LOCAL_URL ??\n \"http://127.0.0.1:8020/api/v1/sandbox/exec\";\n const fetchImpl = options.fetch ?? fetch;\n return {\n id: \"local_mac\",\n async exec(request) {\n const response = await fetchImpl(endpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n tenantId: request.tenantId ?? \"local\",\n threadId: request.threadId ?? \"local\",\n command: request.cmd,\n capabilityPolicy: {\n kind: \"external_sandbox\",\n network_access: request.hints?.networkAccess ?? false,\n },\n }),\n });\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new CapabilityError(\n \"sandbox-runtime\",\n `Local sandbox refused execution (${response.status})`,\n {\n suggestion:\n \"Start the Rust sandbox service or route this task to a configured remote provider.\",\n metadata: { detail },\n statusCode: response.status,\n },\n );\n }\n const result = (await response.json()) as ExecResponse;\n await appendSandboxDebug({\n type: \"exec\",\n provider: \"local_mac\",\n command: request.cmd,\n result,\n });\n return result;\n },\n async doctor() {\n try {\n const result = await this.exec({ cmd: \"echo sandbox ok\" });\n return { provider: \"local_mac\", ok: result.exitCode === 0 };\n } catch (error) {\n return {\n provider: \"local_mac\",\n ok: false,\n suggestion:\n error instanceof Error ? error.message : \"Check local sandbox service reachability.\",\n };\n }\n },\n };\n}\n\nexport function createRemotePlaceholder(\n id: \"remote_code\" | \"remote_gpu\" | \"remote_workspace\",\n envKey: string,\n): Sandbox {\n return {\n id,\n async exec() {\n throw new CapabilityError(\"sandbox-runtime\", `${id} adapter is not configured`, {\n suggestion: `Set ${envKey} and wire the provider SDK before routing tasks to ${id}.`,\n statusCode: 503,\n });\n },\n async doctor() {\n return process.env[envKey]\n ? { provider: id, ok: true }\n : { provider: id, ok: false, suggestion: `Set ${envKey} to enable ${id}.` };\n },\n };\n}\n\nexport class SandboxRuntime {\n readonly #providers = new Map<string, Sandbox>();\n readonly #routes: readonly RouteRule[];\n\n constructor(config: SandboxRuntimeConfig = {}) {\n for (const provider of config.providers ?? [\n createLocalMacSandbox(),\n createRemotePlaceholder(\"remote_code\", \"SANDBOX_REMOTE_CODE_TOKEN\"),\n createRemotePlaceholder(\"remote_gpu\", \"SANDBOX_REMOTE_GPU_TOKEN\"),\n createRemotePlaceholder(\"remote_workspace\", \"SANDBOX_REMOTE_WORKSPACE_TOKEN\"),\n ]) {\n this.#providers.set(provider.id, provider);\n }\n this.#routes = config.routes ?? [\n { when: { needsGpu: true }, provider: \"remote_gpu\" },\n { when: { durationS: 30 }, provider: \"remote_code\" },\n { when: { needsGpu: false }, provider: \"local_mac\" },\n ];\n }\n\n static fromConfig(config: SandboxRuntimeConfig = {}): SandboxRuntime {\n return new SandboxRuntime(config);\n }\n\n plan(request: ExecRequest): { provider: string; reason: string } {\n const hints = request.hints ?? {};\n for (const rule of this.#routes) {\n const matches = Object.entries(rule.when).every(\n ([key, value]) => hints[key as keyof ExecHints] === value,\n );\n if (matches && this.#providers.has(rule.provider)) {\n return { provider: rule.provider, reason: \"matched static routing rule\" };\n }\n }\n return {\n provider: this.#providers.has(\"local_mac\")\n ? \"local_mac\"\n : (Array.from(this.#providers.keys())[0] ?? \"\"),\n reason: \"default route\",\n };\n }\n\n async exec(request: ExecRequest): Promise<ExecResponse> {\n const plan = this.plan(request);\n const provider = this.#providers.get(plan.provider);\n if (!provider) {\n throw new CapabilityError(\"sandbox-runtime\", \"No sandbox provider available\", {\n suggestion: \"Configure at least one sandbox provider before executing commands.\",\n metadata: plan,\n });\n }\n return provider.exec(request);\n }\n\n async doctor(): Promise<SandboxHealth[]> {\n return Promise.all(Array.from(this.#providers.values()).map((provider) => provider.doctor()));\n }\n}\n\nexport async function readSandboxDebug(limit = 10): Promise<unknown[]> {\n return readCapabilityDebug(\"sandbox-runtime\", { limit });\n}\n"],"mappings":"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","names":[]}
|
package/examples/doctor.ts
DELETED
package/examples/dry-run-plan.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { SandboxRuntime } from "../src/index";
|
|
2
|
-
|
|
3
|
-
const runtime = SandboxRuntime.fromConfig();
|
|
4
|
-
const plan = runtime.plan({
|
|
5
|
-
cmd: "echo sandbox ok",
|
|
6
|
-
hints: { needsGpu: false, durationS: 1 },
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`);
|
package/examples/local-echo.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { SandboxRuntime } from "../src/index";
|
|
2
|
-
|
|
3
|
-
const runtime = SandboxRuntime.fromConfig();
|
|
4
|
-
const result = await runtime.exec({
|
|
5
|
-
cmd: "echo sandbox ok",
|
|
6
|
-
hints: { needsGpu: false },
|
|
7
|
-
tenantId: "local",
|
|
8
|
-
threadId: "example",
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
package/src/cli.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { readSandboxDebug, SandboxRuntime } from "./index";
|
|
2
|
-
|
|
3
|
-
const command = process.argv[2] ?? "doctor";
|
|
4
|
-
const runtime = SandboxRuntime.fromConfig();
|
|
5
|
-
|
|
6
|
-
if (command === "doctor") {
|
|
7
|
-
process.stdout.write(
|
|
8
|
-
`${JSON.stringify({ capability: "sandbox-runtime", results: await runtime.doctor() }, null, 2)}\n`,
|
|
9
|
-
);
|
|
10
|
-
} else if (command === "debug") {
|
|
11
|
-
process.stdout.write(
|
|
12
|
-
`${JSON.stringify({ capability: "sandbox-runtime", entries: await readSandboxDebug() }, null, 2)}\n`,
|
|
13
|
-
);
|
|
14
|
-
} else if (command === "plan") {
|
|
15
|
-
const task = process.argv.slice(3).join(" ") || "echo sandbox ok";
|
|
16
|
-
process.stdout.write(
|
|
17
|
-
`${JSON.stringify({ capability: "sandbox-runtime", task, plan: runtime.plan({ cmd: task }) }, null, 2)}\n`,
|
|
18
|
-
);
|
|
19
|
-
} else {
|
|
20
|
-
process.stderr.write(`Unknown sandbox-runtime command: ${command}\n`);
|
|
21
|
-
process.exitCode = 1;
|
|
22
|
-
}
|
package/src/index.test.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createLocalMacSandbox, SandboxRuntime } from "./index";
|
|
3
|
-
|
|
4
|
-
describe("SandboxRuntime", () => {
|
|
5
|
-
it("plans deterministic provider routing without asking a model", () => {
|
|
6
|
-
const runtime = SandboxRuntime.fromConfig({
|
|
7
|
-
providers: [createLocalMacSandbox({ fetch: fetch })],
|
|
8
|
-
routes: [{ when: { needsGpu: false }, provider: "local_mac" }],
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
expect(runtime.plan({ cmd: "echo sandbox ok", hints: { needsGpu: false } })).toMatchObject({
|
|
12
|
-
provider: "local_mac",
|
|
13
|
-
reason: expect.stringContaining("rule"),
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("delegates execution to the local isolator endpoint", async () => {
|
|
18
|
-
const runtime = SandboxRuntime.fromConfig({
|
|
19
|
-
providers: [
|
|
20
|
-
createLocalMacSandbox({
|
|
21
|
-
fetch: async () =>
|
|
22
|
-
new Response(
|
|
23
|
-
JSON.stringify({
|
|
24
|
-
exitCode: 0,
|
|
25
|
-
aggregatedOutput: "sandbox ok\n",
|
|
26
|
-
executedOn: "local_mac",
|
|
27
|
-
}),
|
|
28
|
-
{
|
|
29
|
-
status: 200,
|
|
30
|
-
},
|
|
31
|
-
),
|
|
32
|
-
}),
|
|
33
|
-
],
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
await expect(runtime.exec({ cmd: "echo sandbox ok" })).resolves.toMatchObject({
|
|
37
|
-
exitCode: 0,
|
|
38
|
-
executedOn: "local_mac",
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
appendCapabilityDebug,
|
|
3
|
-
capabilityDebugPath,
|
|
4
|
-
readCapabilityDebug,
|
|
5
|
-
} from "@nebutra/capability-kit/debug";
|
|
6
|
-
import { CapabilityError } from "@nebutra/errors";
|
|
7
|
-
|
|
8
|
-
export interface ExecHints {
|
|
9
|
-
readonly durationS?: number;
|
|
10
|
-
readonly needsGpu?: boolean;
|
|
11
|
-
readonly networkAccess?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ExecRequest {
|
|
15
|
-
readonly cmd: string;
|
|
16
|
-
readonly filesIn?: Record<string, string>;
|
|
17
|
-
readonly hints?: ExecHints;
|
|
18
|
-
readonly tenantId?: string;
|
|
19
|
-
readonly threadId?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ExecResponse {
|
|
23
|
-
readonly exitCode: number;
|
|
24
|
-
readonly aggregatedOutput: string;
|
|
25
|
-
readonly executedOn: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface SandboxHealth {
|
|
29
|
-
readonly provider: string;
|
|
30
|
-
readonly ok: boolean;
|
|
31
|
-
readonly suggestion?: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface Sandbox {
|
|
35
|
-
readonly id: string;
|
|
36
|
-
exec(request: ExecRequest): Promise<ExecResponse>;
|
|
37
|
-
doctor(): Promise<SandboxHealth>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface RouteRule {
|
|
41
|
-
readonly when: Partial<ExecHints>;
|
|
42
|
-
readonly provider: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface SandboxRuntimeConfig {
|
|
46
|
-
readonly providers?: readonly Sandbox[];
|
|
47
|
-
readonly routes?: readonly RouteRule[];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function sandboxDebugPath(): string {
|
|
51
|
-
return capabilityDebugPath("sandbox-runtime");
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function appendSandboxDebug(entry: Record<string, unknown>): Promise<void> {
|
|
55
|
-
await appendCapabilityDebug("sandbox-runtime", entry);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function createLocalMacSandbox(
|
|
59
|
-
options: { endpoint?: string; fetch?: typeof fetch } = {},
|
|
60
|
-
): Sandbox {
|
|
61
|
-
const endpoint =
|
|
62
|
-
options.endpoint ??
|
|
63
|
-
process.env.SANDBOX_RUNTIME_LOCAL_URL ??
|
|
64
|
-
"http://127.0.0.1:8020/api/v1/sandbox/exec";
|
|
65
|
-
const fetchImpl = options.fetch ?? fetch;
|
|
66
|
-
return {
|
|
67
|
-
id: "local_mac",
|
|
68
|
-
async exec(request) {
|
|
69
|
-
const response = await fetchImpl(endpoint, {
|
|
70
|
-
method: "POST",
|
|
71
|
-
headers: { "content-type": "application/json" },
|
|
72
|
-
body: JSON.stringify({
|
|
73
|
-
tenantId: request.tenantId ?? "local",
|
|
74
|
-
threadId: request.threadId ?? "local",
|
|
75
|
-
command: request.cmd,
|
|
76
|
-
capabilityPolicy: {
|
|
77
|
-
kind: "external_sandbox",
|
|
78
|
-
network_access: request.hints?.networkAccess ?? false,
|
|
79
|
-
},
|
|
80
|
-
}),
|
|
81
|
-
});
|
|
82
|
-
if (!response.ok) {
|
|
83
|
-
const detail = await response.text().catch(() => "");
|
|
84
|
-
throw new CapabilityError(
|
|
85
|
-
"sandbox-runtime",
|
|
86
|
-
`Local sandbox refused execution (${response.status})`,
|
|
87
|
-
{
|
|
88
|
-
suggestion:
|
|
89
|
-
"Start the Rust sandbox service or route this task to a configured remote provider.",
|
|
90
|
-
metadata: { detail },
|
|
91
|
-
statusCode: response.status,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
const result = (await response.json()) as ExecResponse;
|
|
96
|
-
await appendSandboxDebug({
|
|
97
|
-
type: "exec",
|
|
98
|
-
provider: "local_mac",
|
|
99
|
-
command: request.cmd,
|
|
100
|
-
result,
|
|
101
|
-
});
|
|
102
|
-
return result;
|
|
103
|
-
},
|
|
104
|
-
async doctor() {
|
|
105
|
-
try {
|
|
106
|
-
const result = await this.exec({ cmd: "echo sandbox ok" });
|
|
107
|
-
return { provider: "local_mac", ok: result.exitCode === 0 };
|
|
108
|
-
} catch (error) {
|
|
109
|
-
return {
|
|
110
|
-
provider: "local_mac",
|
|
111
|
-
ok: false,
|
|
112
|
-
suggestion:
|
|
113
|
-
error instanceof Error ? error.message : "Check local sandbox service reachability.",
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function createRemotePlaceholder(
|
|
121
|
-
id: "remote_code" | "remote_gpu" | "remote_workspace",
|
|
122
|
-
envKey: string,
|
|
123
|
-
): Sandbox {
|
|
124
|
-
return {
|
|
125
|
-
id,
|
|
126
|
-
async exec() {
|
|
127
|
-
throw new CapabilityError("sandbox-runtime", `${id} adapter is not configured`, {
|
|
128
|
-
suggestion: `Set ${envKey} and wire the provider SDK before routing tasks to ${id}.`,
|
|
129
|
-
statusCode: 503,
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
async doctor() {
|
|
133
|
-
return process.env[envKey]
|
|
134
|
-
? { provider: id, ok: true }
|
|
135
|
-
: { provider: id, ok: false, suggestion: `Set ${envKey} to enable ${id}.` };
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export class SandboxRuntime {
|
|
141
|
-
readonly #providers = new Map<string, Sandbox>();
|
|
142
|
-
readonly #routes: readonly RouteRule[];
|
|
143
|
-
|
|
144
|
-
constructor(config: SandboxRuntimeConfig = {}) {
|
|
145
|
-
for (const provider of config.providers ?? [
|
|
146
|
-
createLocalMacSandbox(),
|
|
147
|
-
createRemotePlaceholder("remote_code", "SANDBOX_REMOTE_CODE_TOKEN"),
|
|
148
|
-
createRemotePlaceholder("remote_gpu", "SANDBOX_REMOTE_GPU_TOKEN"),
|
|
149
|
-
createRemotePlaceholder("remote_workspace", "SANDBOX_REMOTE_WORKSPACE_TOKEN"),
|
|
150
|
-
]) {
|
|
151
|
-
this.#providers.set(provider.id, provider);
|
|
152
|
-
}
|
|
153
|
-
this.#routes = config.routes ?? [
|
|
154
|
-
{ when: { needsGpu: true }, provider: "remote_gpu" },
|
|
155
|
-
{ when: { durationS: 30 }, provider: "remote_code" },
|
|
156
|
-
{ when: { needsGpu: false }, provider: "local_mac" },
|
|
157
|
-
];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
static fromConfig(config: SandboxRuntimeConfig = {}): SandboxRuntime {
|
|
161
|
-
return new SandboxRuntime(config);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
plan(request: ExecRequest): { provider: string; reason: string } {
|
|
165
|
-
const hints = request.hints ?? {};
|
|
166
|
-
for (const rule of this.#routes) {
|
|
167
|
-
const matches = Object.entries(rule.when).every(
|
|
168
|
-
([key, value]) => hints[key as keyof ExecHints] === value,
|
|
169
|
-
);
|
|
170
|
-
if (matches && this.#providers.has(rule.provider)) {
|
|
171
|
-
return { provider: rule.provider, reason: "matched static routing rule" };
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
provider: this.#providers.has("local_mac")
|
|
176
|
-
? "local_mac"
|
|
177
|
-
: (Array.from(this.#providers.keys())[0] ?? ""),
|
|
178
|
-
reason: "default route",
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async exec(request: ExecRequest): Promise<ExecResponse> {
|
|
183
|
-
const plan = this.plan(request);
|
|
184
|
-
const provider = this.#providers.get(plan.provider);
|
|
185
|
-
if (!provider) {
|
|
186
|
-
throw new CapabilityError("sandbox-runtime", "No sandbox provider available", {
|
|
187
|
-
suggestion: "Configure at least one sandbox provider before executing commands.",
|
|
188
|
-
metadata: plan,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
return provider.exec(request);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async doctor(): Promise<SandboxHealth[]> {
|
|
195
|
-
return Promise.all(Array.from(this.#providers.values()).map((provider) => provider.doctor()));
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export async function readSandboxDebug(limit = 10): Promise<unknown[]> {
|
|
200
|
-
return readCapabilityDebug("sandbox-runtime", { limit });
|
|
201
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"target": "esnext",
|
|
7
|
-
"types": ["node"],
|
|
8
|
-
"incremental": false
|
|
9
|
-
},
|
|
10
|
-
"include": ["src", "examples"],
|
|
11
|
-
"exclude": ["node_modules", "dist"]
|
|
12
|
-
}
|
package/tsup.config.ts
DELETED