@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/CHANGELOG.md +27 -0
- package/LICENSE +21 -676
- package/README.md +74 -13
- package/package.json +20 -10
- package/.nebutra/debug/code-execution.jsonl +0 -3
- package/.turbo/turbo-build.log +0 -22
- package/.turbo/turbo-test.log +0 -14
- package/.turbo/turbo-typecheck.log +0 -4
- package/examples/edit-by-diff.ts +0 -17
- package/examples/policy.ts +0 -10
- package/examples/shell.ts +0 -11
- package/src/cli.ts +0 -44
- package/src/index.test.ts +0 -110
- package/src/index.ts +0 -436
- package/tsconfig.json +0 -12
- package/tsup.config.ts +0 -17
package/README.md
CHANGED
|
@@ -1,15 +1,78 @@
|
|
|
1
1
|
# @nebutra/code-execution
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Action and observation execution layer backed by `@nebutra/sandbox-runtime`.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
86
|
+
Executable examples live under `examples/`.
|
|
24
87
|
|
|
25
|
-
|
|
88
|
+
## License
|
|
26
89
|
|
|
27
|
-
|
|
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.
|
|
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": "./
|
|
26
|
-
"types": "./
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
27
|
"exports": {
|
|
28
|
-
".":
|
|
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/
|
|
32
|
-
"@nebutra/
|
|
33
|
-
"@nebutra/
|
|
34
|
-
"@nebutra/
|
|
35
|
-
"@nebutra/
|
|
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": "^
|
|
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":[]}}
|
package/.turbo/turbo-build.log
DELETED
|
@@ -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
|
-
[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/code-execution/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[32m1.59 KB[39m
|
|
13
|
-
[32mESM[39m [1mdist/index.js [22m[32m232.00 B[39m
|
|
14
|
-
[32mESM[39m [1mdist/chunk-LLPO5OG2.js [22m[32m9.10 KB[39m
|
|
15
|
-
[32mESM[39m [1mdist/cli.js.map [22m[32m2.87 KB[39m
|
|
16
|
-
[32mESM[39m [1mdist/index.js.map [22m[32m71.00 B[39m
|
|
17
|
-
[32mESM[39m [1mdist/chunk-LLPO5OG2.js.map [22m[32m19.58 KB[39m
|
|
18
|
-
[32mESM[39m ⚡️ Build success in 100ms
|
|
19
|
-
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in 12500ms
|
|
21
|
-
[32mDTS[39m [1mdist/cli.d.ts [22m[32m13.00 B[39m
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m3.79 KB[39m
|
package/.turbo/turbo-test.log
DELETED
|
@@ -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
|
-
[1m[30m[46m RUN [49m[39m[22m [36mv4.1.4 [39m[90m/home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/code-execution[39m
|
|
7
|
-
|
|
8
|
-
[32m✓[39m src/index.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 131[2mms[22m[39m
|
|
9
|
-
|
|
10
|
-
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
11
|
-
[2m Tests [22m [1m[32m5 passed[39m[22m[90m (5)[39m
|
|
12
|
-
[2m Start at [22m 08:15:16
|
|
13
|
-
[2m Duration [22m 1.50s[2m (transform 681ms, setup 0ms, import 796ms, tests 131ms, environment 0ms)[22m
|
|
14
|
-
|
package/examples/edit-by-diff.ts
DELETED
|
@@ -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`);
|
package/examples/policy.ts
DELETED
|
@@ -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
|
-
});
|