@nebutra/code-execution 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/README.md CHANGED
@@ -1,15 +1,78 @@
1
1
  # @nebutra/code-execution
2
2
 
3
- Status: WIP — Not yet integrated into any production app.
3
+ Action and observation execution layer backed by `@nebutra/sandbox-runtime`.
4
4
 
5
- `@nebutra/code-execution` owns action/observation execution for shell, read,
6
- edit-by-diff, git, policy checks, replay, doctor output, and debug inspection.
7
- It routes process execution through sandbox contracts and returns structured
8
- observations with suggestions.
5
+ This package turns shell, read, edit-by-diff, git, policy, replay, doctor, and
6
+ debug actions into structured observations. It owns execution behavior, not
7
+ thread state, prompts, model calls, sub-agent scheduling, or UI approval flows.
9
8
 
10
- It does not own Thread/Turn/Item state, prompt generation, model calls,
11
- sub-agent scheduling, or approval lifecycle. Approval requests are surfaced as
12
- observations for the runtime or UI to handle.
9
+ ## Status: WIP
10
+
11
+ Experimental public package. The package metadata marks it as not production
12
+ ready yet because:
13
+
14
+ - notebook kernel execution is adapter-gated
15
+ - remote workspace providers depend on sandbox-runtime configuration
16
+ - approval UI is surfaced as observations and is not wired in this package
17
+
18
+ Use it as a capability layer behind an application runtime that owns approval,
19
+ tenant, and UI handoff.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pnpm add @nebutra/code-execution
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```ts
30
+ import { CodeExecutor } from "@nebutra/code-execution";
31
+
32
+ const executor = new CodeExecutor({
33
+ tenantId: "org_123",
34
+ workspaceRoot: process.cwd(),
35
+ });
36
+
37
+ const observation = await executor.run({
38
+ type: "read",
39
+ path: "README.md",
40
+ });
41
+ ```
42
+
43
+ Shell actions route through `@nebutra/sandbox-runtime`:
44
+
45
+ ```ts
46
+ const result = await executor.run({
47
+ type: "shell",
48
+ cmd: "pnpm test",
49
+ cwd: ".",
50
+ timeoutS: 30,
51
+ });
52
+ ```
53
+
54
+ ## Action Types
55
+
56
+ | Type | Purpose |
57
+ | --- | --- |
58
+ | `shell` | Execute a command through the configured sandbox runtime |
59
+ | `read` | Read a workspace-relative file, optionally by line range |
60
+ | `edit` | Apply an edit-by-diff request to a workspace-relative file |
61
+ | `git` | Execute supported git operations through the sandbox path |
62
+ | `ipython` | Return an adapter-unavailable observation until a notebook sidecar is configured |
63
+
64
+ ## Observations
65
+
66
+ The executor returns structured observations instead of raw side effects:
67
+
68
+ - `shell_output`
69
+ - `file_content`
70
+ - `edit_applied`
71
+ - `error`
72
+
73
+ Approval requirements are surfaced as `error` observations with the
74
+ `ApprovalRequired` kind. The caller is responsible for asking the user and
75
+ retrying with `approved: true`.
13
76
 
14
77
  ## Commands
15
78
 
@@ -20,10 +83,8 @@ pnpm exec:replay <action_id>
20
83
  pnpm exec:policy
21
84
  ```
22
85
 
23
- ## Examples
86
+ Executable examples live under `examples/`.
24
87
 
25
- Executable examples live under `examples/`:
88
+ ## License
26
89
 
27
- - `edit-by-diff.ts`
28
- - `policy.ts`
29
- - `shell.ts`
90
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/code-execution",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Action and observation execution layer backed by sandbox-runtime",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -22,20 +22,24 @@
22
22
  "category": "ai",
23
23
  "summary": "Shell, read, edit-by-diff, policy, replay, doctor, and debug tools"
24
24
  },
25
- "main": "./src/index.ts",
26
- "types": "./src/index.ts",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
27
  "exports": {
28
- ".": "./src/index.ts"
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "default": "./dist/index.js"
32
+ }
29
33
  },
30
34
  "dependencies": {
31
- "@nebutra/capability-kit": "0.2.1",
32
- "@nebutra/errors": "0.1.1",
33
- "@nebutra/event-log": "0.1.1",
34
- "@nebutra/execution-policy": "0.1.1",
35
- "@nebutra/sandbox-runtime": "0.1.1"
35
+ "@nebutra/sandbox-runtime": "0.1.4",
36
+ "@nebutra/capability-kit": "0.2.4",
37
+ "@nebutra/errors": "0.1.2",
38
+ "@nebutra/event-log": "0.1.4",
39
+ "@nebutra/execution-policy": "0.1.4"
36
40
  },
37
41
  "devDependencies": {
38
- "@types/node": "^22.19.15",
42
+ "@types/node": "^25.9.1",
39
43
  "tsup": "^8.5.1",
40
44
  "typescript": "^5.9.3",
41
45
  "vitest": "^4.1.4"
@@ -52,6 +56,12 @@
52
56
  "publishConfig": {
53
57
  "access": "public"
54
58
  },
59
+ "files": [
60
+ "dist",
61
+ "README.md",
62
+ "LICENSE",
63
+ "CHANGELOG.md"
64
+ ],
55
65
  "scripts": {
56
66
  "build": "tsup",
57
67
  "test": "vitest run",
@@ -1,3 +0,0 @@
1
- {"at":"2026-05-19T08:15:17.662Z","type":"action","tenantId":"tenant_a","action":{"type":"shell","cmd":"pnpm test","cwd":"/workspace","timeoutS":60,"tenantId":"tenant_a"},"observation":{"type":"shell_output","actionId":"shell_mpccyaa5","stdout":"ran pnpm test for tenant_a","stderr":"","exitCode":0,"durationMs":7,"executedOn":"test_sandbox"}}
2
- {"at":"2026-05-19T08:15:17.690Z","type":"action","tenantId":"tenant_a","action":{"type":"shell","cmd":"rm -rf build","cwd":"/workspace","tenantId":"tenant_a"},"observation":{"type":"error","actionId":"shell_mpccyab5","kind":"ApprovalRequired","message":"Command requires approval: destructive recursive removal","suggestion":"Ask the user for explicit approval, then retry the action with approved: true."}}
3
- {"at":"2026-05-19T08:15:17.717Z","type":"action","tenantId":"tenant_a","action":{"type":"edit","path":"main.ts","diff":"--- a/main.ts\n+++ b/main.ts\n@@ -1,2 +1,2 @@\n-const value = 1;\n+const value = 2;\n console.log(value);\n","tenantId":"tenant_a"},"observation":{"type":"edit_applied","actionId":"edit_mpccyaby","path":"main.ts","diff":"--- a/main.ts\n+++ b/main.ts\n@@ -1,2 +1,2 @@\n-const value = 1;\n+const value = 2;\n console.log(value);\n","conflicts":[]}}
@@ -1,22 +0,0 @@
1
-
2
- > @nebutra/code-execution@0.1.1 build /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/code-execution
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: /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/code-execution/tsup.config.ts
9
- CLI Target: es2022
10
- CLI Cleaning output folder
11
- ESM Build start
12
- ESM dist/cli.js 1.59 KB
13
- ESM dist/index.js 232.00 B
14
- ESM dist/chunk-LLPO5OG2.js 9.10 KB
15
- ESM dist/cli.js.map 2.87 KB
16
- ESM dist/index.js.map 71.00 B
17
- ESM dist/chunk-LLPO5OG2.js.map 19.58 KB
18
- ESM ⚡️ Build success in 100ms
19
- DTS Build start
20
- DTS ⚡️ Build success in 12500ms
21
- DTS dist/cli.d.ts 13.00 B
22
- DTS dist/index.d.ts 3.79 KB
@@ -1,14 +0,0 @@
1
-
2
- > @nebutra/code-execution@0.1.1 test /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/code-execution
3
- > vitest run
4
-
5
-
6
-  RUN  v4.1.4 /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/code-execution
7
-
8
- ✓ src/index.test.ts (5 tests) 131ms
9
-
10
-  Test Files  1 passed (1)
11
-  Tests  5 passed (5)
12
-  Start at  08:15:16
13
-  Duration  1.50s (transform 681ms, setup 0ms, import 796ms, tests 131ms, environment 0ms)
14
-
@@ -1,4 +0,0 @@
1
-
2
- > @nebutra/code-execution@0.1.1 typecheck /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/code-execution
3
- > tsc --noEmit
4
-
@@ -1,17 +0,0 @@
1
- import { CodeExecutor } from "@nebutra/code-execution";
2
-
3
- const exec = new CodeExecutor({ tenantId: "local", workspaceRoot: process.cwd() });
4
- const observation = await exec.run({
5
- type: "edit",
6
- tenantId: "local",
7
- path: "scratch/example.txt",
8
- diff: [
9
- "--- a/scratch/example.txt",
10
- "+++ b/scratch/example.txt",
11
- "@@ -1 +1 @@",
12
- "-old",
13
- "+new",
14
- "",
15
- ].join("\n"),
16
- });
17
- process.stdout.write(`${JSON.stringify(observation, null, 2)}\n`);
@@ -1,10 +0,0 @@
1
- import { CodeExecutor } from "@nebutra/code-execution";
2
-
3
- const exec = new CodeExecutor({ tenantId: "local" });
4
- const observation = await exec.run({
5
- type: "shell",
6
- tenantId: "local",
7
- cwd: process.cwd(),
8
- cmd: "git push origin main",
9
- });
10
- process.stdout.write(`${JSON.stringify(observation, null, 2)}\n`);
package/examples/shell.ts DELETED
@@ -1,11 +0,0 @@
1
- import { CodeExecutor } from "@nebutra/code-execution";
2
-
3
- const exec = new CodeExecutor({ tenantId: "local" });
4
- const observation = await exec.run({
5
- type: "shell",
6
- tenantId: "local",
7
- cwd: process.cwd(),
8
- cmd: "echo sandbox ok",
9
- timeoutS: 5,
10
- });
11
- process.stdout.write(`${JSON.stringify(observation, null, 2)}\n`);
package/src/cli.ts DELETED
@@ -1,44 +0,0 @@
1
- import { CodeExecutor, readExecutionDebug } from "./index";
2
-
3
- const command = process.argv[2] ?? "doctor";
4
- const exec = new CodeExecutor({ tenantId: process.env.NEBUTRA_TENANT_ID ?? "local" });
5
-
6
- if (command === "doctor") {
7
- process.stdout.write(`${JSON.stringify(await exec.doctor(), null, 2)}\n`);
8
- } else if (command === "debug") {
9
- process.stdout.write(
10
- `${JSON.stringify({ capability: "code-execution", entries: await readExecutionDebug() }, null, 2)}\n`,
11
- );
12
- } else if (command === "policy") {
13
- const rules = exec.policy().map((rule) => ({
14
- ...rule,
15
- match: rule.match instanceof RegExp ? rule.match.source : rule.match,
16
- }));
17
- process.stdout.write(`${JSON.stringify({ capability: "code-execution", rules }, null, 2)}\n`);
18
- } else if (command === "replay") {
19
- const actionId = process.argv[3];
20
- const entries = await readExecutionDebug(process.cwd(), 100);
21
- const entry = entries.find((item) => {
22
- if (!item || typeof item !== "object") return false;
23
- const value = item as { action?: { actionId?: string }; observation?: { actionId?: string } };
24
- return value.action?.actionId === actionId || value.observation?.actionId === actionId;
25
- });
26
- process.stdout.write(
27
- `${JSON.stringify(
28
- {
29
- capability: "code-execution",
30
- actionId,
31
- replayable: Boolean(entry),
32
- entry,
33
- suggestion: entry
34
- ? "Use the stored action payload as the deterministic replay input."
35
- : "Run `pnpm exec:debug` and choose an action id from the recent log.",
36
- },
37
- null,
38
- 2,
39
- )}\n`,
40
- );
41
- } else {
42
- process.stderr.write(`Unknown code-execution command: ${command}\n`);
43
- process.exitCode = 1;
44
- }
package/src/index.test.ts DELETED
@@ -1,110 +0,0 @@
1
- import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterEach, describe, expect, it } from "vitest";
5
- import { CodeExecutor, DefaultPolicy, readExecutionDebug } from "./index";
6
-
7
- let root: string | undefined;
8
-
9
- afterEach(async () => {
10
- if (root) await rm(root, { recursive: true, force: true });
11
- root = undefined;
12
- });
13
-
14
- describe("CodeExecutor", () => {
15
- it("runs shell actions through sandbox-runtime with tenant context", async () => {
16
- const exec = new CodeExecutor({
17
- tenantId: "tenant_a",
18
- sandboxRuntime: {
19
- async exec(request) {
20
- return {
21
- exitCode: 0,
22
- aggregatedOutput: `ran ${request.cmd} for ${request.tenantId}`,
23
- executedOn: "test_sandbox",
24
- };
25
- },
26
- async doctor() {
27
- return [{ provider: "test_sandbox", ok: true }];
28
- },
29
- },
30
- });
31
-
32
- await expect(
33
- exec.run({
34
- type: "shell",
35
- cmd: "pnpm test",
36
- cwd: "/workspace",
37
- timeoutS: 60,
38
- tenantId: "tenant_a",
39
- }),
40
- ).resolves.toMatchObject({
41
- type: "shell_output",
42
- stdout: "ran pnpm test for tenant_a",
43
- exitCode: 0,
44
- executedOn: "test_sandbox",
45
- });
46
- });
47
-
48
- it("blocks destructive shell commands until approval is explicit", async () => {
49
- const exec = new CodeExecutor({
50
- tenantId: "tenant_a",
51
- sandboxRuntime: {
52
- async exec() {
53
- throw new Error("should not run");
54
- },
55
- async doctor() {
56
- return [];
57
- },
58
- },
59
- policy: DefaultPolicy,
60
- });
61
-
62
- await expect(
63
- exec.run({ type: "shell", cmd: "rm -rf build", cwd: "/workspace", tenantId: "tenant_a" }),
64
- ).resolves.toMatchObject({
65
- type: "error",
66
- kind: "ApprovalRequired",
67
- suggestion: expect.stringContaining("approval"),
68
- });
69
- });
70
-
71
- it("applies edits by unified diff instead of overwrite", async () => {
72
- root = await mkdtemp(join(tmpdir(), "code-execution-"));
73
- await writeFile(join(root, "main.ts"), "const value = 1;\nconsole.log(value);\n", "utf8");
74
- const exec = new CodeExecutor({ tenantId: "tenant_a", workspaceRoot: root });
75
-
76
- await expect(
77
- exec.run({
78
- type: "edit",
79
- path: "main.ts",
80
- diff: [
81
- "--- a/main.ts",
82
- "+++ b/main.ts",
83
- "@@ -1,2 +1,2 @@",
84
- "-const value = 1;",
85
- "+const value = 2;",
86
- " console.log(value);",
87
- "",
88
- ].join("\n"),
89
- tenantId: "tenant_a",
90
- }),
91
- ).resolves.toMatchObject({ type: "edit_applied", path: "main.ts" });
92
-
93
- await expect(readFile(join(root, "main.ts"), "utf8")).resolves.toContain("value = 2");
94
- });
95
-
96
- it("requires tenant context for every action", async () => {
97
- const exec = new CodeExecutor();
98
- await expect(exec.run({ type: "read", path: "main.ts" })).rejects.toMatchObject({
99
- capability: "code-execution",
100
- suggestion: expect.stringContaining("tenantId"),
101
- });
102
- });
103
-
104
- it("writes replayable debug entries", async () => {
105
- root = await mkdtemp(join(tmpdir(), "code-execution-"));
106
- const exec = new CodeExecutor({ tenantId: "tenant_a", debugRoot: root });
107
- await exec.run({ type: "read", path: "missing.ts", tenantId: "tenant_a" });
108
- expect(await readExecutionDebug(root)).toEqual(expect.any(Array));
109
- });
110
- });