@nebutra/sandbox-runtime 0.1.2 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/sandbox-runtime",
3
- "version": "0.1.2",
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,21 +11,30 @@
11
11
  "surface": "execution-router",
12
12
  "summary": "Sandbox trait, provider router, doctor, and debug tools"
13
13
  },
14
- "main": "./src/index.ts",
15
- "types": "./src/index.ts",
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
16
  "exports": {
17
- ".": "./src/index.ts"
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
- "p-limit": "^7.3.0",
21
- "@nebutra/errors": "0.1.2",
22
- "@nebutra/capability-kit": "0.2.2"
29
+ "@nebutra/capability-kit": "workspace:*",
30
+ "@nebutra/errors": "workspace:*",
31
+ "p-limit": "^7.3.0"
23
32
  },
24
33
  "devDependencies": {
25
- "@types/node": "^25.9.1",
26
- "tsup": "^8.5.1",
27
- "typescript": "^5.9.3",
28
- "vitest": "^4.1.4"
34
+ "@types/node": "catalog:",
35
+ "tsup": "catalog:",
36
+ "typescript": "catalog:",
37
+ "vitest": "catalog:"
29
38
  },
30
39
  "homepage": "https://github.com/Nebutra/Nebutra-Sailor/tree/main/packages/ai/sandbox-runtime#readme",
31
40
  "repository": {
@@ -39,9 +48,10 @@
39
48
  "publishConfig": {
40
49
  "access": "public"
41
50
  },
42
- "scripts": {
43
- "build": "tsup",
44
- "test": "vitest run",
45
- "typecheck": "tsc --noEmit"
46
- }
47
- }
51
+ "files": [
52
+ "dist",
53
+ "README.md",
54
+ "LICENSE",
55
+ "CHANGELOG.md"
56
+ ]
57
+ }
@@ -1,6 +0,0 @@
1
- {"at":"2026-05-19T05:44:37.177Z","type":"exec","provider":"local_mac","command":"echo sandbox ok","result":{"exitCode":0,"aggregatedOutput":"sandbox ok\n","executedOn":"local_mac"}}
2
- {"at":"2026-05-30T20:41:47.727Z","type":"exec","provider":"local_mac","command":"echo sandbox ok","result":{"exitCode":0,"aggregatedOutput":"sandbox ok\n","executedOn":"local_mac"}}
3
- {"at":"2026-06-05T07:18:37.847Z","type":"exec","provider":"local_mac","command":"echo sandbox ok","result":{"exitCode":0,"aggregatedOutput":"sandbox ok\n","executedOn":"local_mac"}}
4
- {"at":"2026-06-05T07:19:23.017Z","type":"exec","provider":"local_mac","command":"echo sandbox ok","result":{"exitCode":0,"aggregatedOutput":"sandbox ok\n","executedOn":"local_mac"}}
5
- {"at":"2026-06-05T07:23:30.124Z","type":"exec","provider":"local_mac","command":"echo sandbox ok","result":{"exitCode":0,"aggregatedOutput":"sandbox ok\n","executedOn":"local_mac"}}
6
- {"at":"2026-07-01T01:38:32.446Z","type":"exec","provider":"local_mac","command":"echo sandbox ok","result":{"exitCode":0,"aggregatedOutput":"sandbox ok\n","executedOn":"local_mac"}}
@@ -1,22 +0,0 @@
1
-
2
- > @nebutra/sandbox-runtime@0.1.2 build /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/sandbox-runtime
3
- > tsup
4
-
5
- CLI Building entry: src/cli.ts, src/index.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Using tsup config: /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/sandbox-runtime/tsup.config.ts
9
- CLI Target: es2022
10
- CLI Cleaning output folder
11
- ESM Build start
12
- ESM dist/cli.js 882.00 B
13
- ESM dist/index.js 302.00 B
14
- ESM dist/chunk-6UZA3GQP.js 4.80 KB
15
- ESM dist/cli.js.map 1.56 KB
16
- ESM dist/index.js.map 71.00 B
17
- ESM dist/chunk-6UZA3GQP.js.map 9.55 KB
18
- ESM ⚡️ Build success in 11ms
19
- DTS Build start
20
- DTS ⚡️ Build success in 2124ms
21
- DTS dist/cli.d.ts 13.00 B
22
- DTS dist/index.d.ts 1.83 KB
@@ -1,14 +0,0 @@
1
-
2
- > @nebutra/sandbox-runtime@0.1.1 test /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/sandbox-runtime
3
- > vitest run
4
-
5
-
6
- RUN v4.1.4 /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/sandbox-runtime
7
-
8
- ✓ src/index.test.ts (3 tests) 72ms
9
-
10
- Test Files 1 passed (1)
11
- Tests 3 passed (3)
12
- Start at 09:38:32
13
- Duration 332ms (transform 55ms, setup 0ms, import 72ms, tests 72ms, environment 0ms)
14
-
@@ -1,4 +0,0 @@
1
-
2
- > @nebutra/sandbox-runtime@0.1.2 typecheck /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/ai/sandbox-runtime
3
- > tsc --noEmit
4
-
@@ -1,6 +0,0 @@
1
- import { SandboxRuntime } from "../src/index";
2
-
3
- const runtime = SandboxRuntime.fromConfig();
4
- const health = await runtime.doctor();
5
-
6
- process.stdout.write(`${JSON.stringify(health, null, 2)}\n`);
@@ -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`);
@@ -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,69 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { createLocalMacSandbox, type Sandbox, 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
-
42
- it("caps provider doctor concurrency to avoid burst diagnostics", async () => {
43
- let active = 0;
44
- let maxActive = 0;
45
- const providers = Array.from({ length: 12 }, (_, index): Sandbox => {
46
- const id = `sandbox_${index}`;
47
- return {
48
- id,
49
- exec: async () => ({
50
- exitCode: 0,
51
- aggregatedOutput: "ok",
52
- executedOn: id,
53
- }),
54
- doctor: async () => {
55
- active += 1;
56
- maxActive = Math.max(maxActive, active);
57
- await new Promise((resolve) => setTimeout(resolve, 0));
58
- active -= 1;
59
- return { provider: id, ok: true };
60
- },
61
- };
62
- });
63
-
64
- const runtime = SandboxRuntime.fromConfig({ providers });
65
-
66
- await expect(runtime.doctor()).resolves.toHaveLength(12);
67
- expect(maxActive).toBeLessThanOrEqual(4);
68
- });
69
- });
package/src/index.ts DELETED
@@ -1,207 +0,0 @@
1
- import {
2
- appendCapabilityDebug,
3
- capabilityDebugPath,
4
- readCapabilityDebug,
5
- } from "@nebutra/capability-kit/debug";
6
- import { CapabilityError } from "@nebutra/errors";
7
- import pLimit from "p-limit";
8
-
9
- export interface ExecHints {
10
- readonly durationS?: number;
11
- readonly needsGpu?: boolean;
12
- readonly networkAccess?: boolean;
13
- }
14
-
15
- export interface ExecRequest {
16
- readonly cmd: string;
17
- readonly filesIn?: Record<string, string>;
18
- readonly hints?: ExecHints;
19
- readonly tenantId?: string;
20
- readonly threadId?: string;
21
- }
22
-
23
- export interface ExecResponse {
24
- readonly exitCode: number;
25
- readonly aggregatedOutput: string;
26
- readonly executedOn: string;
27
- }
28
-
29
- export interface SandboxHealth {
30
- readonly provider: string;
31
- readonly ok: boolean;
32
- readonly suggestion?: string;
33
- }
34
-
35
- export interface Sandbox {
36
- readonly id: string;
37
- exec(request: ExecRequest): Promise<ExecResponse>;
38
- doctor(): Promise<SandboxHealth>;
39
- }
40
-
41
- export interface RouteRule {
42
- readonly when: Partial<ExecHints>;
43
- readonly provider: string;
44
- }
45
-
46
- export interface SandboxRuntimeConfig {
47
- readonly providers?: readonly Sandbox[];
48
- readonly routes?: readonly RouteRule[];
49
- }
50
-
51
- const SANDBOX_DOCTOR_CONCURRENCY = 4;
52
-
53
- export function sandboxDebugPath(): string {
54
- return capabilityDebugPath("sandbox-runtime");
55
- }
56
-
57
- async function appendSandboxDebug(entry: Record<string, unknown>): Promise<void> {
58
- await appendCapabilityDebug("sandbox-runtime", entry);
59
- }
60
-
61
- export function createLocalMacSandbox(
62
- options: { endpoint?: string; fetch?: typeof fetch } = {},
63
- ): Sandbox {
64
- const endpoint =
65
- options.endpoint ??
66
- process.env.SANDBOX_RUNTIME_LOCAL_URL ??
67
- "http://127.0.0.1:8020/api/v1/sandbox/exec";
68
- const fetchImpl = options.fetch ?? fetch;
69
- return {
70
- id: "local_mac",
71
- async exec(request) {
72
- const response = await fetchImpl(endpoint, {
73
- method: "POST",
74
- headers: { "content-type": "application/json" },
75
- body: JSON.stringify({
76
- tenantId: request.tenantId ?? "local",
77
- threadId: request.threadId ?? "local",
78
- command: request.cmd,
79
- capabilityPolicy: {
80
- kind: "external_sandbox",
81
- network_access: request.hints?.networkAccess ?? false,
82
- },
83
- }),
84
- });
85
- if (!response.ok) {
86
- const detail = await response.text().catch(() => "");
87
- throw new CapabilityError(
88
- "sandbox-runtime",
89
- `Local sandbox refused execution (${response.status})`,
90
- {
91
- suggestion:
92
- "Start the Rust sandbox service or route this task to a configured remote provider.",
93
- metadata: { detail },
94
- statusCode: response.status,
95
- },
96
- );
97
- }
98
- const result = (await response.json()) as ExecResponse;
99
- await appendSandboxDebug({
100
- type: "exec",
101
- provider: "local_mac",
102
- command: request.cmd,
103
- result,
104
- });
105
- return result;
106
- },
107
- async doctor() {
108
- try {
109
- const result = await this.exec({ cmd: "echo sandbox ok" });
110
- return { provider: "local_mac", ok: result.exitCode === 0 };
111
- } catch (error) {
112
- return {
113
- provider: "local_mac",
114
- ok: false,
115
- suggestion:
116
- error instanceof Error ? error.message : "Check local sandbox service reachability.",
117
- };
118
- }
119
- },
120
- };
121
- }
122
-
123
- export function createRemotePlaceholder(
124
- id: "remote_code" | "remote_gpu" | "remote_workspace",
125
- envKey: string,
126
- ): Sandbox {
127
- return {
128
- id,
129
- async exec() {
130
- throw new CapabilityError("sandbox-runtime", `${id} adapter is not configured`, {
131
- suggestion: `Set ${envKey} and wire the provider SDK before routing tasks to ${id}.`,
132
- statusCode: 503,
133
- });
134
- },
135
- async doctor() {
136
- return process.env[envKey]
137
- ? { provider: id, ok: true }
138
- : { provider: id, ok: false, suggestion: `Set ${envKey} to enable ${id}.` };
139
- },
140
- };
141
- }
142
-
143
- export class SandboxRuntime {
144
- readonly #providers = new Map<string, Sandbox>();
145
- readonly #routes: readonly RouteRule[];
146
-
147
- constructor(config: SandboxRuntimeConfig = {}) {
148
- for (const provider of config.providers ?? [
149
- createLocalMacSandbox(),
150
- createRemotePlaceholder("remote_code", "SANDBOX_REMOTE_CODE_TOKEN"),
151
- createRemotePlaceholder("remote_gpu", "SANDBOX_REMOTE_GPU_TOKEN"),
152
- createRemotePlaceholder("remote_workspace", "SANDBOX_REMOTE_WORKSPACE_TOKEN"),
153
- ]) {
154
- this.#providers.set(provider.id, provider);
155
- }
156
- this.#routes = config.routes ?? [
157
- { when: { needsGpu: true }, provider: "remote_gpu" },
158
- { when: { durationS: 30 }, provider: "remote_code" },
159
- { when: { needsGpu: false }, provider: "local_mac" },
160
- ];
161
- }
162
-
163
- static fromConfig(config: SandboxRuntimeConfig = {}): SandboxRuntime {
164
- return new SandboxRuntime(config);
165
- }
166
-
167
- plan(request: ExecRequest): { provider: string; reason: string } {
168
- const hints = request.hints ?? {};
169
- for (const rule of this.#routes) {
170
- const matches = Object.entries(rule.when).every(
171
- ([key, value]) => hints[key as keyof ExecHints] === value,
172
- );
173
- if (matches && this.#providers.has(rule.provider)) {
174
- return { provider: rule.provider, reason: "matched static routing rule" };
175
- }
176
- }
177
- return {
178
- provider: this.#providers.has("local_mac")
179
- ? "local_mac"
180
- : (Array.from(this.#providers.keys())[0] ?? ""),
181
- reason: "default route",
182
- };
183
- }
184
-
185
- async exec(request: ExecRequest): Promise<ExecResponse> {
186
- const plan = this.plan(request);
187
- const provider = this.#providers.get(plan.provider);
188
- if (!provider) {
189
- throw new CapabilityError("sandbox-runtime", "No sandbox provider available", {
190
- suggestion: "Configure at least one sandbox provider before executing commands.",
191
- metadata: plan,
192
- });
193
- }
194
- return provider.exec(request);
195
- }
196
-
197
- async doctor(): Promise<SandboxHealth[]> {
198
- const limit = pLimit(SANDBOX_DOCTOR_CONCURRENCY);
199
- return Promise.all(
200
- Array.from(this.#providers.values()).map((provider) => limit(() => provider.doctor())),
201
- );
202
- }
203
- }
204
-
205
- export async function readSandboxDebug(limit = 10): Promise<unknown[]> {
206
- return readCapabilityDebug("sandbox-runtime", { limit });
207
- }
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
@@ -1,11 +0,0 @@
1
- import { defineConfig } from "tsup";
2
-
3
- export default defineConfig({
4
- entry: ["src/index.ts", "src/cli.ts"],
5
- format: ["esm"],
6
- dts: true,
7
- sourcemap: true,
8
- clean: true,
9
- target: "es2022",
10
- external: ["@nebutra/errors"],
11
- });