@nebutra/code-execution 0.1.0 → 0.1.2

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
@@ -1,64 +1,23 @@
1
1
  // src/index.ts
2
- import { mkdir, readFile, writeFile } from "fs/promises";
3
- import { dirname, join, normalize } from "path";
2
+ import { readFile, writeFile } from "fs/promises";
3
+ import { join, normalize } from "path";
4
+ import { requireCapabilityTenant } from "@nebutra/capability-kit";
5
+ import { appendCapabilityDebug, readCapabilityDebug } from "@nebutra/capability-kit/debug";
4
6
  import { CapabilityError } from "@nebutra/errors";
7
+ import {
8
+ DEFAULT_SHELL_APPROVAL_RULES,
9
+ shellApprovalRequired
10
+ } from "@nebutra/execution-policy";
5
11
  import {
6
12
  SandboxRuntime
7
13
  } from "@nebutra/sandbox-runtime";
8
- var DefaultPolicy = [
9
- { match: /^rm\s+-rf\b/, requireApproval: "always", reason: "destructive recursive removal" },
10
- {
11
- match: /\b(format|mkfs)\b/,
12
- requireApproval: "always",
13
- reason: "destructive filesystem operation"
14
- },
15
- {
16
- match: /\bDROP\s+(DATABASE|SCHEMA|TABLE)\b/i,
17
- requireApproval: "always",
18
- reason: "destructive database operation"
19
- },
20
- {
21
- match: /\b(npm|pnpm|yarn)\s+publish\b/,
22
- requireApproval: "always",
23
- reason: "package publishing"
24
- },
25
- { match: /^git\s+push\b/, requireApproval: "once_per_session", reason: "remote git mutation" }
26
- ];
27
- function debugPath(root = process.cwd()) {
28
- return join(root, ".nebutra", "debug", "code-execution.jsonl");
29
- }
30
- async function appendDebug(root, entry) {
31
- const path = debugPath(root);
32
- await mkdir(dirname(path), { recursive: true });
33
- await writeFile(path, `${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry })}
34
- `, {
35
- flag: "a"
36
- });
37
- }
14
+ var DefaultPolicy = DEFAULT_SHELL_APPROVAL_RULES;
38
15
  async function readExecutionDebug(root = process.cwd(), limit = 20) {
39
- try {
40
- const raw = await readFile(debugPath(root), "utf8");
41
- return raw.trim().split("\n").filter(Boolean).slice(-limit).map((line) => JSON.parse(line));
42
- } catch {
43
- return [];
44
- }
45
- }
46
- function requireTenant(explicit, fallback) {
47
- const tenantId = explicit ?? fallback;
48
- if (!tenantId) {
49
- throw new CapabilityError("code-execution", "Code execution requires tenant context", {
50
- suggestion: "Pass tenantId on the action or construct CodeExecutor with a tenantId default.",
51
- statusCode: 400
52
- });
53
- }
54
- return tenantId;
16
+ return readCapabilityDebug("code-execution", { root, limit });
55
17
  }
56
18
  function actionIdFrom(action) {
57
19
  return action.actionId ?? `${action.type}_${Date.now().toString(36)}`;
58
20
  }
59
- function matchesRule(command, rule) {
60
- return typeof rule.match === "string" ? command.startsWith(rule.match) : rule.match.test(command);
61
- }
62
21
  function safeWorkspacePath(root, path) {
63
22
  const normalized = normalize(path);
64
23
  if (normalized.startsWith("/") || normalized.includes("..")) {
@@ -86,7 +45,14 @@ var CodeExecutor = class {
86
45
  this.#policy = options.policy ?? DefaultPolicy;
87
46
  }
88
47
  async run(action) {
89
- const tenantId = requireTenant(action.tenantId, this.#tenantId);
48
+ const tenantId = requireCapabilityTenant({
49
+ explicit: action.tenantId,
50
+ fallback: this.#tenantId,
51
+ onMissing: () => new CapabilityError("code-execution", "Code execution requires tenant context", {
52
+ suggestion: "Pass tenantId on the action or construct CodeExecutor with a tenantId default.",
53
+ statusCode: 400
54
+ })
55
+ });
90
56
  const actionId = actionIdFrom(action);
91
57
  const started = Date.now();
92
58
  let observation;
@@ -95,12 +61,20 @@ var CodeExecutor = class {
95
61
  } catch (cause) {
96
62
  observation = errorObservation(actionId, cause);
97
63
  }
98
- await appendDebug(this.#debugRoot, { type: "action", tenantId, action, observation });
64
+ await appendCapabilityDebug(
65
+ "code-execution",
66
+ { type: "action", tenantId, action, observation },
67
+ { root: this.#debugRoot }
68
+ );
99
69
  return observation;
100
70
  }
101
71
  async doctor() {
102
72
  const sandbox = await this.#sandboxRuntime.doctor();
103
- await appendDebug(this.#debugRoot, { type: "doctor", sandbox });
73
+ await appendCapabilityDebug(
74
+ "code-execution",
75
+ { type: "doctor", sandbox },
76
+ { root: this.#debugRoot }
77
+ );
104
78
  return { capability: "code-execution", sandbox, policies: this.#policy.length };
105
79
  }
106
80
  policy() {
@@ -222,7 +196,7 @@ var CodeExecutor = class {
222
196
  }
223
197
  }
224
198
  #approvalRequired(command) {
225
- return this.#policy.find((rule) => rule.requireApproval !== "never" && matchesRule(command, rule)) ?? null;
199
+ return shellApprovalRequired(command, this.#policy);
226
200
  }
227
201
  };
228
202
  function errorObservation(actionId, cause, override = {}) {
@@ -297,4 +271,4 @@ export {
297
271
  CodeExecutor,
298
272
  applyUnifiedDiff
299
273
  };
300
- //# sourceMappingURL=chunk-26GNZJ27.js.map
274
+ //# sourceMappingURL=chunk-LLPO5OG2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { join, normalize } from \"node:path\";\nimport { requireCapabilityTenant } from \"@nebutra/capability-kit\";\nimport { appendCapabilityDebug, readCapabilityDebug } from \"@nebutra/capability-kit/debug\";\nimport { CapabilityError } from \"@nebutra/errors\";\nimport {\n DEFAULT_SHELL_APPROVAL_RULES,\n type ShellApprovalMode,\n type ShellApprovalRule,\n shellApprovalRequired,\n} from \"@nebutra/execution-policy\";\nimport {\n type ExecRequest,\n type ExecResponse,\n type SandboxHealth,\n SandboxRuntime,\n} from \"@nebutra/sandbox-runtime\";\n\nexport type ApprovalMode = ShellApprovalMode;\nexport type PolicyRule = ShellApprovalRule;\nexport const DefaultPolicy = DEFAULT_SHELL_APPROVAL_RULES;\n\nexport interface BaseAction {\n readonly tenantId?: string;\n readonly threadId?: string;\n readonly actionId?: string;\n readonly approved?: boolean;\n}\n\nexport interface ShellAction extends BaseAction {\n readonly type: \"shell\";\n readonly cmd: string;\n readonly cwd: string;\n readonly timeoutS?: number;\n}\n\nexport interface ReadAction extends BaseAction {\n readonly type: \"read\";\n readonly path: string;\n readonly lineRange?: readonly [number, number];\n}\n\nexport interface EditAction extends BaseAction {\n readonly type: \"edit\";\n readonly path: string;\n readonly diff: string;\n readonly lineRange?: readonly [number, number];\n}\n\nexport interface IPythonAction extends BaseAction {\n readonly type: \"ipython\";\n readonly code: string;\n readonly kernelId: string;\n}\n\nexport interface GitAction extends BaseAction {\n readonly type: \"git\";\n readonly op: \"status\" | \"log\" | \"push\";\n readonly args?: readonly string[];\n}\n\nexport type Action = ShellAction | ReadAction | EditAction | IPythonAction | GitAction;\n\nexport interface ShellOutputObservation {\n readonly type: \"shell_output\";\n readonly actionId: string;\n readonly stdout: string;\n readonly stderr: string;\n readonly exitCode: number;\n readonly durationMs: number;\n readonly executedOn: string;\n}\n\nexport interface FileContentObservation {\n readonly type: \"file_content\";\n readonly actionId: string;\n readonly path: string;\n readonly content: string;\n readonly lines: readonly [number, number];\n}\n\nexport interface EditAppliedObservation {\n readonly type: \"edit_applied\";\n readonly actionId: string;\n readonly path: string;\n readonly diff: string;\n readonly conflicts: readonly string[];\n}\n\nexport interface ErrorObservation {\n readonly type: \"error\";\n readonly actionId: string;\n readonly kind: string;\n readonly message: string;\n readonly suggestion: string;\n}\n\nexport type Observation =\n | ShellOutputObservation\n | FileContentObservation\n | EditAppliedObservation\n | ErrorObservation;\n\nexport interface SandboxRuntimeLike {\n exec(request: ExecRequest): Promise<ExecResponse>;\n doctor(): Promise<readonly SandboxHealth[]>;\n}\n\nexport interface EventLogLike {\n commit(event: {\n traceId: string;\n kind: \"tool_call\" | \"content_write\" | \"sandbox_exec\";\n affected: readonly string[];\n parent: string | null;\n snapshot?: Record<string, string>;\n }): Promise<string>;\n}\n\nexport interface CodeExecutorOptions {\n readonly tenantId?: string;\n readonly workspaceRoot?: string;\n readonly debugRoot?: string;\n readonly sandboxRuntime?: SandboxRuntimeLike;\n readonly eventLog?: EventLogLike;\n readonly policy?: readonly PolicyRule[];\n}\n\nexport async function readExecutionDebug(root = process.cwd(), limit = 20): Promise<unknown[]> {\n return readCapabilityDebug(\"code-execution\", { root, limit });\n}\n\nfunction actionIdFrom(action: Action): string {\n return action.actionId ?? `${action.type}_${Date.now().toString(36)}`;\n}\n\nfunction safeWorkspacePath(root: string, path: string): string {\n const normalized = normalize(path);\n if (normalized.startsWith(\"/\") || normalized.includes(\"..\")) {\n throw new CapabilityError(\"code-execution\", \"Unsafe workspace path rejected\", {\n suggestion: \"Use a path relative to the configured workspace root.\",\n metadata: { path },\n statusCode: 400,\n });\n }\n return join(root, normalized);\n}\n\nexport class CodeExecutor {\n readonly #tenantId: string | undefined;\n readonly #workspaceRoot: string;\n readonly #debugRoot: string;\n readonly #sandboxRuntime: SandboxRuntimeLike;\n readonly #eventLog: EventLogLike | undefined;\n readonly #policy: readonly PolicyRule[];\n\n constructor(options: CodeExecutorOptions = {}) {\n this.#tenantId = options.tenantId;\n this.#workspaceRoot = options.workspaceRoot ?? process.cwd();\n this.#debugRoot = options.debugRoot ?? process.cwd();\n this.#sandboxRuntime = options.sandboxRuntime ?? SandboxRuntime.fromConfig();\n this.#eventLog = options.eventLog;\n this.#policy = options.policy ?? DefaultPolicy;\n }\n\n async run(action: Action): Promise<Observation> {\n const tenantId = requireCapabilityTenant({\n explicit: action.tenantId,\n fallback: this.#tenantId,\n onMissing: () =>\n new CapabilityError(\"code-execution\", \"Code execution requires tenant context\", {\n suggestion:\n \"Pass tenantId on the action or construct CodeExecutor with a tenantId default.\",\n statusCode: 400,\n }),\n });\n const actionId = actionIdFrom(action);\n const started = Date.now();\n let observation: Observation;\n\n try {\n observation = await this.#runWithTenant({ ...action, tenantId }, actionId, started);\n } catch (cause) {\n observation = errorObservation(actionId, cause);\n }\n\n await appendCapabilityDebug(\n \"code-execution\",\n { type: \"action\", tenantId, action, observation },\n { root: this.#debugRoot },\n );\n return observation;\n }\n\n async doctor(): Promise<{\n readonly capability: \"code-execution\";\n readonly sandbox: readonly SandboxHealth[];\n readonly policies: number;\n }> {\n const sandbox = await this.#sandboxRuntime.doctor();\n await appendCapabilityDebug(\n \"code-execution\",\n { type: \"doctor\", sandbox },\n { root: this.#debugRoot },\n );\n return { capability: \"code-execution\", sandbox, policies: this.#policy.length };\n }\n\n policy(): readonly PolicyRule[] {\n return this.#policy;\n }\n\n async #runWithTenant(\n action: Action & { readonly tenantId: string },\n actionId: string,\n started: number,\n ): Promise<Observation> {\n switch (action.type) {\n case \"shell\":\n return this.#runShell(action, actionId, started);\n case \"read\":\n return this.#readFile(action, actionId);\n case \"edit\":\n return this.#editFile(action, actionId);\n case \"git\":\n return this.#runGit(action, actionId, started);\n case \"ipython\":\n return {\n type: \"error\",\n actionId,\n kind: \"AdapterUnavailable\",\n message: \"IPython kernel execution is not configured\",\n suggestion: \"Configure a notebook kernel sidecar and route it through sandbox-runtime.\",\n };\n }\n }\n\n async #runShell(\n action: ShellAction & { readonly tenantId: string },\n actionId: string,\n started: number,\n ): Promise<Observation> {\n const approval = this.#approvalRequired(action.cmd);\n if (approval && !action.approved) {\n return {\n type: \"error\",\n actionId,\n kind: \"ApprovalRequired\",\n message: `Command requires approval: ${approval.reason}`,\n suggestion:\n \"Ask the user for explicit approval, then retry the action with approved: true.\",\n };\n }\n\n const result = await this.#sandboxRuntime.exec({\n cmd: action.cmd,\n tenantId: action.tenantId,\n ...(action.threadId ? { threadId: action.threadId } : {}),\n ...(action.timeoutS !== undefined ? { hints: { durationS: action.timeoutS } } : {}),\n });\n await this.#eventLog?.commit({\n traceId: action.threadId ?? actionId,\n kind: \"sandbox_exec\",\n affected: [],\n parent: null,\n });\n return {\n type: \"shell_output\",\n actionId,\n stdout: result.aggregatedOutput,\n stderr: \"\",\n exitCode: result.exitCode,\n durationMs: Date.now() - started,\n executedOn: result.executedOn,\n };\n }\n\n async #runGit(\n action: GitAction & { readonly tenantId: string },\n actionId: string,\n started: number,\n ): Promise<Observation> {\n const command = `git ${action.op}${action.args?.length ? ` ${action.args.join(\" \")}` : \"\"}`;\n return this.#runShell(\n {\n type: \"shell\",\n cmd: command,\n cwd: this.#workspaceRoot,\n tenantId: action.tenantId,\n ...(action.threadId ? { threadId: action.threadId } : {}),\n ...(action.approved !== undefined ? { approved: action.approved } : {}),\n },\n actionId,\n started,\n );\n }\n\n async #readFile(\n action: ReadAction & { readonly tenantId: string },\n actionId: string,\n ): Promise<Observation> {\n try {\n const content = await readFile(safeWorkspacePath(this.#workspaceRoot, action.path), \"utf8\");\n const lines = content.split(\"\\n\");\n const start = action.lineRange?.[0] ?? 1;\n const end = action.lineRange?.[1] ?? lines.length;\n return {\n type: \"file_content\",\n actionId,\n path: action.path,\n content: lines.slice(start - 1, end).join(\"\\n\"),\n lines: [start, end],\n };\n } catch (cause) {\n return errorObservation(actionId, cause, {\n kind: \"FileReadFailed\",\n suggestion: \"Check the path with a read/list action before asking the executor to edit it.\",\n });\n }\n }\n\n async #editFile(\n action: EditAction & { readonly tenantId: string },\n actionId: string,\n ): Promise<Observation> {\n const full = safeWorkspacePath(this.#workspaceRoot, action.path);\n try {\n const before = await readFile(full, \"utf8\");\n const after = applyUnifiedDiff(before, action.diff);\n await writeFile(full, after, \"utf8\");\n await this.#eventLog?.commit({\n traceId: action.threadId ?? actionId,\n kind: \"content_write\",\n affected: [action.path],\n parent: null,\n snapshot: { [action.path]: after },\n });\n return {\n type: \"edit_applied\",\n actionId,\n path: action.path,\n diff: action.diff,\n conflicts: [],\n };\n } catch (cause) {\n return errorObservation(actionId, cause, {\n kind: \"EditFailed\",\n suggestion:\n \"Refresh the file, regenerate a unified diff against the current contents, and retry without overwrite semantics.\",\n });\n }\n }\n\n #approvalRequired(command: string): PolicyRule | null {\n return shellApprovalRequired(command, this.#policy);\n }\n}\n\nfunction errorObservation(\n actionId: string,\n cause: unknown,\n override: { readonly kind?: string; readonly suggestion?: string } = {},\n): ErrorObservation {\n const message = cause instanceof Error ? cause.message : String(cause);\n return {\n type: \"error\",\n actionId,\n kind:\n override.kind ?? (cause instanceof CapabilityError ? \"CapabilityError\" : \"ExecutionError\"),\n message,\n suggestion:\n override.suggestion ??\n (cause instanceof CapabilityError && cause.suggestion\n ? cause.suggestion\n : \"Inspect the action, sandbox route, and tenant context, then retry with a narrower command.\"),\n };\n}\n\nexport function applyUnifiedDiff(original: string, diff: string): string {\n const originalHadTrailingNewline = original.endsWith(\"\\n\");\n let lines = originalHadTrailingNewline ? original.slice(0, -1).split(\"\\n\") : original.split(\"\\n\");\n if (original === \"\") lines = [];\n\n const diffLines = diff.split(\"\\n\");\n let index = 0;\n while (index < diffLines.length) {\n const line = diffLines[index] ?? \"\";\n if (!line.startsWith(\"@@\")) {\n index += 1;\n continue;\n }\n index += 1;\n const oldLines: string[] = [];\n const newLines: string[] = [];\n while (index < diffLines.length && !(diffLines[index] ?? \"\").startsWith(\"@@\")) {\n const current = diffLines[index] ?? \"\";\n if (current.startsWith(\"\\\\ No newline\")) {\n index += 1;\n continue;\n }\n if (current.startsWith(\"-\")) oldLines.push(current.slice(1));\n else if (current.startsWith(\"+\")) newLines.push(current.slice(1));\n else if (current.startsWith(\" \")) {\n oldLines.push(current.slice(1));\n newLines.push(current.slice(1));\n }\n index += 1;\n }\n const position = findSubsequence(lines, oldLines);\n if (position < 0) {\n throw new CapabilityError(\"code-execution\", \"Diff hunk does not match current file\", {\n suggestion:\n \"Re-read the file and generate a fresh unified diff against the latest content.\",\n metadata: { oldLines },\n statusCode: 409,\n });\n }\n lines.splice(position, oldLines.length, ...newLines);\n }\n\n return `${lines.join(\"\\n\")}${originalHadTrailingNewline ? \"\\n\" : \"\"}`;\n}\n\nfunction findSubsequence(haystack: readonly string[], needle: readonly string[]): number {\n if (needle.length === 0) return 0;\n for (let start = 0; start <= haystack.length - needle.length; start += 1) {\n let matched = true;\n for (let offset = 0; offset < needle.length; offset += 1) {\n if (haystack[start + offset] !== needle[offset]) {\n matched = false;\n break;\n }\n }\n if (matched) return start;\n }\n return -1;\n}\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CodeExecutor,
3
3
  readExecutionDebug
4
- } from "./chunk-26GNZJ27.js";
4
+ } from "./chunk-LLPO5OG2.js";
5
5
 
6
6
  // src/cli.ts
7
7
  var command = process.argv[2] ?? "doctor";
package/dist/index.d.ts CHANGED
@@ -1,12 +1,9 @@
1
+ import { ShellApprovalMode, ShellApprovalRule } from '@nebutra/execution-policy';
1
2
  import { ExecRequest, ExecResponse, SandboxHealth } from '@nebutra/sandbox-runtime';
2
3
 
3
- type ApprovalMode = "always" | "once_per_session" | "never";
4
- interface PolicyRule {
5
- readonly match: string | RegExp;
6
- readonly requireApproval: ApprovalMode;
7
- readonly reason: string;
8
- }
9
- declare const DefaultPolicy: readonly PolicyRule[];
4
+ type ApprovalMode = ShellApprovalMode;
5
+ type PolicyRule = ShellApprovalRule;
6
+ declare const DefaultPolicy: readonly ShellApprovalRule[];
10
7
  interface BaseAction {
11
8
  readonly tenantId?: string;
12
9
  readonly threadId?: string;
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DefaultPolicy,
4
4
  applyUnifiedDiff,
5
5
  readExecutionDebug
6
- } from "./chunk-26GNZJ27.js";
6
+ } from "./chunk-LLPO5OG2.js";
7
7
  export {
8
8
  CodeExecutor,
9
9
  DefaultPolicy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/code-execution",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Action and observation execution layer backed by sandbox-runtime",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -28,12 +28,14 @@
28
28
  ".": "./src/index.ts"
29
29
  },
30
30
  "dependencies": {
31
- "@nebutra/errors": "0.1.0",
32
- "@nebutra/event-log": "0.1.0",
33
- "@nebutra/sandbox-runtime": "0.1.0"
31
+ "@nebutra/capability-kit": "0.2.2",
32
+ "@nebutra/event-log": "0.1.2",
33
+ "@nebutra/execution-policy": "0.1.2",
34
+ "@nebutra/sandbox-runtime": "0.1.2",
35
+ "@nebutra/errors": "0.1.2"
34
36
  },
35
37
  "devDependencies": {
36
- "@types/node": "^22.19.15",
38
+ "@types/node": "^25.9.1",
37
39
  "tsup": "^8.5.1",
38
40
  "typescript": "^5.9.3",
39
41
  "vitest": "^4.1.4"
package/src/index.ts CHANGED
@@ -1,6 +1,14 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { dirname, join, normalize } from "node:path";
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { join, normalize } from "node:path";
3
+ import { requireCapabilityTenant } from "@nebutra/capability-kit";
4
+ import { appendCapabilityDebug, readCapabilityDebug } from "@nebutra/capability-kit/debug";
3
5
  import { CapabilityError } from "@nebutra/errors";
6
+ import {
7
+ DEFAULT_SHELL_APPROVAL_RULES,
8
+ type ShellApprovalMode,
9
+ type ShellApprovalRule,
10
+ shellApprovalRequired,
11
+ } from "@nebutra/execution-policy";
4
12
  import {
5
13
  type ExecRequest,
6
14
  type ExecResponse,
@@ -8,33 +16,9 @@ import {
8
16
  SandboxRuntime,
9
17
  } from "@nebutra/sandbox-runtime";
10
18
 
11
- export type ApprovalMode = "always" | "once_per_session" | "never";
12
-
13
- export interface PolicyRule {
14
- readonly match: string | RegExp;
15
- readonly requireApproval: ApprovalMode;
16
- readonly reason: string;
17
- }
18
-
19
- export const DefaultPolicy: readonly PolicyRule[] = [
20
- { match: /^rm\s+-rf\b/, requireApproval: "always", reason: "destructive recursive removal" },
21
- {
22
- match: /\b(format|mkfs)\b/,
23
- requireApproval: "always",
24
- reason: "destructive filesystem operation",
25
- },
26
- {
27
- match: /\bDROP\s+(DATABASE|SCHEMA|TABLE)\b/i,
28
- requireApproval: "always",
29
- reason: "destructive database operation",
30
- },
31
- {
32
- match: /\b(npm|pnpm|yarn)\s+publish\b/,
33
- requireApproval: "always",
34
- reason: "package publishing",
35
- },
36
- { match: /^git\s+push\b/, requireApproval: "once_per_session", reason: "remote git mutation" },
37
- ];
19
+ export type ApprovalMode = ShellApprovalMode;
20
+ export type PolicyRule = ShellApprovalRule;
21
+ export const DefaultPolicy = DEFAULT_SHELL_APPROVAL_RULES;
38
22
 
39
23
  export interface BaseAction {
40
24
  readonly tenantId?: string;
@@ -141,51 +125,14 @@ export interface CodeExecutorOptions {
141
125
  readonly policy?: readonly PolicyRule[];
142
126
  }
143
127
 
144
- function debugPath(root = process.cwd()): string {
145
- return join(root, ".nebutra", "debug", "code-execution.jsonl");
146
- }
147
-
148
- async function appendDebug(root: string, entry: Record<string, unknown>): Promise<void> {
149
- const path = debugPath(root);
150
- await mkdir(dirname(path), { recursive: true });
151
- await writeFile(path, `${JSON.stringify({ at: new Date().toISOString(), ...entry })}\n`, {
152
- flag: "a",
153
- });
154
- }
155
-
156
128
  export async function readExecutionDebug(root = process.cwd(), limit = 20): Promise<unknown[]> {
157
- try {
158
- const raw = await readFile(debugPath(root), "utf8");
159
- return raw
160
- .trim()
161
- .split("\n")
162
- .filter(Boolean)
163
- .slice(-limit)
164
- .map((line) => JSON.parse(line) as unknown);
165
- } catch {
166
- return [];
167
- }
168
- }
169
-
170
- function requireTenant(explicit: string | undefined, fallback: string | undefined): string {
171
- const tenantId = explicit ?? fallback;
172
- if (!tenantId) {
173
- throw new CapabilityError("code-execution", "Code execution requires tenant context", {
174
- suggestion: "Pass tenantId on the action or construct CodeExecutor with a tenantId default.",
175
- statusCode: 400,
176
- });
177
- }
178
- return tenantId;
129
+ return readCapabilityDebug("code-execution", { root, limit });
179
130
  }
180
131
 
181
132
  function actionIdFrom(action: Action): string {
182
133
  return action.actionId ?? `${action.type}_${Date.now().toString(36)}`;
183
134
  }
184
135
 
185
- function matchesRule(command: string, rule: PolicyRule): boolean {
186
- return typeof rule.match === "string" ? command.startsWith(rule.match) : rule.match.test(command);
187
- }
188
-
189
136
  function safeWorkspacePath(root: string, path: string): string {
190
137
  const normalized = normalize(path);
191
138
  if (normalized.startsWith("/") || normalized.includes("..")) {
@@ -216,7 +163,16 @@ export class CodeExecutor {
216
163
  }
217
164
 
218
165
  async run(action: Action): Promise<Observation> {
219
- const tenantId = requireTenant(action.tenantId, this.#tenantId);
166
+ const tenantId = requireCapabilityTenant({
167
+ explicit: action.tenantId,
168
+ fallback: this.#tenantId,
169
+ onMissing: () =>
170
+ new CapabilityError("code-execution", "Code execution requires tenant context", {
171
+ suggestion:
172
+ "Pass tenantId on the action or construct CodeExecutor with a tenantId default.",
173
+ statusCode: 400,
174
+ }),
175
+ });
220
176
  const actionId = actionIdFrom(action);
221
177
  const started = Date.now();
222
178
  let observation: Observation;
@@ -227,7 +183,11 @@ export class CodeExecutor {
227
183
  observation = errorObservation(actionId, cause);
228
184
  }
229
185
 
230
- await appendDebug(this.#debugRoot, { type: "action", tenantId, action, observation });
186
+ await appendCapabilityDebug(
187
+ "code-execution",
188
+ { type: "action", tenantId, action, observation },
189
+ { root: this.#debugRoot },
190
+ );
231
191
  return observation;
232
192
  }
233
193
 
@@ -237,7 +197,11 @@ export class CodeExecutor {
237
197
  readonly policies: number;
238
198
  }> {
239
199
  const sandbox = await this.#sandboxRuntime.doctor();
240
- await appendDebug(this.#debugRoot, { type: "doctor", sandbox });
200
+ await appendCapabilityDebug(
201
+ "code-execution",
202
+ { type: "doctor", sandbox },
203
+ { root: this.#debugRoot },
204
+ );
241
205
  return { capability: "code-execution", sandbox, policies: this.#policy.length };
242
206
  }
243
207
 
@@ -387,10 +351,7 @@ export class CodeExecutor {
387
351
  }
388
352
 
389
353
  #approvalRequired(command: string): PolicyRule | null {
390
- return (
391
- this.#policy.find((rule) => rule.requireApproval !== "never" && matchesRule(command, rule)) ??
392
- null
393
- );
354
+ return shellApprovalRequired(command, this.#policy);
394
355
  }
395
356
  }
396
357
 
package/tsup.config.ts CHANGED
@@ -7,5 +7,11 @@ export default defineConfig({
7
7
  sourcemap: true,
8
8
  clean: true,
9
9
  target: "es2022",
10
- external: ["@nebutra/errors", "@nebutra/event-log", "@nebutra/sandbox-runtime"],
10
+ external: [
11
+ "@nebutra/capability-kit/debug",
12
+ "@nebutra/errors",
13
+ "@nebutra/event-log",
14
+ "@nebutra/execution-policy",
15
+ "@nebutra/sandbox-runtime",
16
+ ],
11
17
  });
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, normalize } from \"node:path\";\nimport { CapabilityError } from \"@nebutra/errors\";\nimport {\n type ExecRequest,\n type ExecResponse,\n type SandboxHealth,\n SandboxRuntime,\n} from \"@nebutra/sandbox-runtime\";\n\nexport type ApprovalMode = \"always\" | \"once_per_session\" | \"never\";\n\nexport interface PolicyRule {\n readonly match: string | RegExp;\n readonly requireApproval: ApprovalMode;\n readonly reason: string;\n}\n\nexport const DefaultPolicy: readonly PolicyRule[] = [\n { match: /^rm\\s+-rf\\b/, requireApproval: \"always\", reason: \"destructive recursive removal\" },\n {\n match: /\\b(format|mkfs)\\b/,\n requireApproval: \"always\",\n reason: \"destructive filesystem operation\",\n },\n {\n match: /\\bDROP\\s+(DATABASE|SCHEMA|TABLE)\\b/i,\n requireApproval: \"always\",\n reason: \"destructive database operation\",\n },\n {\n match: /\\b(npm|pnpm|yarn)\\s+publish\\b/,\n requireApproval: \"always\",\n reason: \"package publishing\",\n },\n { match: /^git\\s+push\\b/, requireApproval: \"once_per_session\", reason: \"remote git mutation\" },\n];\n\nexport interface BaseAction {\n readonly tenantId?: string;\n readonly threadId?: string;\n readonly actionId?: string;\n readonly approved?: boolean;\n}\n\nexport interface ShellAction extends BaseAction {\n readonly type: \"shell\";\n readonly cmd: string;\n readonly cwd: string;\n readonly timeoutS?: number;\n}\n\nexport interface ReadAction extends BaseAction {\n readonly type: \"read\";\n readonly path: string;\n readonly lineRange?: readonly [number, number];\n}\n\nexport interface EditAction extends BaseAction {\n readonly type: \"edit\";\n readonly path: string;\n readonly diff: string;\n readonly lineRange?: readonly [number, number];\n}\n\nexport interface IPythonAction extends BaseAction {\n readonly type: \"ipython\";\n readonly code: string;\n readonly kernelId: string;\n}\n\nexport interface GitAction extends BaseAction {\n readonly type: \"git\";\n readonly op: \"status\" | \"log\" | \"push\";\n readonly args?: readonly string[];\n}\n\nexport type Action = ShellAction | ReadAction | EditAction | IPythonAction | GitAction;\n\nexport interface ShellOutputObservation {\n readonly type: \"shell_output\";\n readonly actionId: string;\n readonly stdout: string;\n readonly stderr: string;\n readonly exitCode: number;\n readonly durationMs: number;\n readonly executedOn: string;\n}\n\nexport interface FileContentObservation {\n readonly type: \"file_content\";\n readonly actionId: string;\n readonly path: string;\n readonly content: string;\n readonly lines: readonly [number, number];\n}\n\nexport interface EditAppliedObservation {\n readonly type: \"edit_applied\";\n readonly actionId: string;\n readonly path: string;\n readonly diff: string;\n readonly conflicts: readonly string[];\n}\n\nexport interface ErrorObservation {\n readonly type: \"error\";\n readonly actionId: string;\n readonly kind: string;\n readonly message: string;\n readonly suggestion: string;\n}\n\nexport type Observation =\n | ShellOutputObservation\n | FileContentObservation\n | EditAppliedObservation\n | ErrorObservation;\n\nexport interface SandboxRuntimeLike {\n exec(request: ExecRequest): Promise<ExecResponse>;\n doctor(): Promise<readonly SandboxHealth[]>;\n}\n\nexport interface EventLogLike {\n commit(event: {\n traceId: string;\n kind: \"tool_call\" | \"content_write\" | \"sandbox_exec\";\n affected: readonly string[];\n parent: string | null;\n snapshot?: Record<string, string>;\n }): Promise<string>;\n}\n\nexport interface CodeExecutorOptions {\n readonly tenantId?: string;\n readonly workspaceRoot?: string;\n readonly debugRoot?: string;\n readonly sandboxRuntime?: SandboxRuntimeLike;\n readonly eventLog?: EventLogLike;\n readonly policy?: readonly PolicyRule[];\n}\n\nfunction debugPath(root = process.cwd()): string {\n return join(root, \".nebutra\", \"debug\", \"code-execution.jsonl\");\n}\n\nasync function appendDebug(root: string, entry: Record<string, unknown>): Promise<void> {\n const path = debugPath(root);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify({ at: new Date().toISOString(), ...entry })}\\n`, {\n flag: \"a\",\n });\n}\n\nexport async function readExecutionDebug(root = process.cwd(), limit = 20): Promise<unknown[]> {\n try {\n const raw = await readFile(debugPath(root), \"utf8\");\n return raw\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .slice(-limit)\n .map((line) => JSON.parse(line) as unknown);\n } catch {\n return [];\n }\n}\n\nfunction requireTenant(explicit: string | undefined, fallback: string | undefined): string {\n const tenantId = explicit ?? fallback;\n if (!tenantId) {\n throw new CapabilityError(\"code-execution\", \"Code execution requires tenant context\", {\n suggestion: \"Pass tenantId on the action or construct CodeExecutor with a tenantId default.\",\n statusCode: 400,\n });\n }\n return tenantId;\n}\n\nfunction actionIdFrom(action: Action): string {\n return action.actionId ?? `${action.type}_${Date.now().toString(36)}`;\n}\n\nfunction matchesRule(command: string, rule: PolicyRule): boolean {\n return typeof rule.match === \"string\" ? command.startsWith(rule.match) : rule.match.test(command);\n}\n\nfunction safeWorkspacePath(root: string, path: string): string {\n const normalized = normalize(path);\n if (normalized.startsWith(\"/\") || normalized.includes(\"..\")) {\n throw new CapabilityError(\"code-execution\", \"Unsafe workspace path rejected\", {\n suggestion: \"Use a path relative to the configured workspace root.\",\n metadata: { path },\n statusCode: 400,\n });\n }\n return join(root, normalized);\n}\n\nexport class CodeExecutor {\n readonly #tenantId: string | undefined;\n readonly #workspaceRoot: string;\n readonly #debugRoot: string;\n readonly #sandboxRuntime: SandboxRuntimeLike;\n readonly #eventLog: EventLogLike | undefined;\n readonly #policy: readonly PolicyRule[];\n\n constructor(options: CodeExecutorOptions = {}) {\n this.#tenantId = options.tenantId;\n this.#workspaceRoot = options.workspaceRoot ?? process.cwd();\n this.#debugRoot = options.debugRoot ?? process.cwd();\n this.#sandboxRuntime = options.sandboxRuntime ?? SandboxRuntime.fromConfig();\n this.#eventLog = options.eventLog;\n this.#policy = options.policy ?? DefaultPolicy;\n }\n\n async run(action: Action): Promise<Observation> {\n const tenantId = requireTenant(action.tenantId, this.#tenantId);\n const actionId = actionIdFrom(action);\n const started = Date.now();\n let observation: Observation;\n\n try {\n observation = await this.#runWithTenant({ ...action, tenantId }, actionId, started);\n } catch (cause) {\n observation = errorObservation(actionId, cause);\n }\n\n await appendDebug(this.#debugRoot, { type: \"action\", tenantId, action, observation });\n return observation;\n }\n\n async doctor(): Promise<{\n readonly capability: \"code-execution\";\n readonly sandbox: readonly SandboxHealth[];\n readonly policies: number;\n }> {\n const sandbox = await this.#sandboxRuntime.doctor();\n await appendDebug(this.#debugRoot, { type: \"doctor\", sandbox });\n return { capability: \"code-execution\", sandbox, policies: this.#policy.length };\n }\n\n policy(): readonly PolicyRule[] {\n return this.#policy;\n }\n\n async #runWithTenant(\n action: Action & { readonly tenantId: string },\n actionId: string,\n started: number,\n ): Promise<Observation> {\n switch (action.type) {\n case \"shell\":\n return this.#runShell(action, actionId, started);\n case \"read\":\n return this.#readFile(action, actionId);\n case \"edit\":\n return this.#editFile(action, actionId);\n case \"git\":\n return this.#runGit(action, actionId, started);\n case \"ipython\":\n return {\n type: \"error\",\n actionId,\n kind: \"AdapterUnavailable\",\n message: \"IPython kernel execution is not configured\",\n suggestion: \"Configure a notebook kernel sidecar and route it through sandbox-runtime.\",\n };\n }\n }\n\n async #runShell(\n action: ShellAction & { readonly tenantId: string },\n actionId: string,\n started: number,\n ): Promise<Observation> {\n const approval = this.#approvalRequired(action.cmd);\n if (approval && !action.approved) {\n return {\n type: \"error\",\n actionId,\n kind: \"ApprovalRequired\",\n message: `Command requires approval: ${approval.reason}`,\n suggestion:\n \"Ask the user for explicit approval, then retry the action with approved: true.\",\n };\n }\n\n const result = await this.#sandboxRuntime.exec({\n cmd: action.cmd,\n tenantId: action.tenantId,\n ...(action.threadId ? { threadId: action.threadId } : {}),\n ...(action.timeoutS !== undefined ? { hints: { durationS: action.timeoutS } } : {}),\n });\n await this.#eventLog?.commit({\n traceId: action.threadId ?? actionId,\n kind: \"sandbox_exec\",\n affected: [],\n parent: null,\n });\n return {\n type: \"shell_output\",\n actionId,\n stdout: result.aggregatedOutput,\n stderr: \"\",\n exitCode: result.exitCode,\n durationMs: Date.now() - started,\n executedOn: result.executedOn,\n };\n }\n\n async #runGit(\n action: GitAction & { readonly tenantId: string },\n actionId: string,\n started: number,\n ): Promise<Observation> {\n const command = `git ${action.op}${action.args?.length ? ` ${action.args.join(\" \")}` : \"\"}`;\n return this.#runShell(\n {\n type: \"shell\",\n cmd: command,\n cwd: this.#workspaceRoot,\n tenantId: action.tenantId,\n ...(action.threadId ? { threadId: action.threadId } : {}),\n ...(action.approved !== undefined ? { approved: action.approved } : {}),\n },\n actionId,\n started,\n );\n }\n\n async #readFile(\n action: ReadAction & { readonly tenantId: string },\n actionId: string,\n ): Promise<Observation> {\n try {\n const content = await readFile(safeWorkspacePath(this.#workspaceRoot, action.path), \"utf8\");\n const lines = content.split(\"\\n\");\n const start = action.lineRange?.[0] ?? 1;\n const end = action.lineRange?.[1] ?? lines.length;\n return {\n type: \"file_content\",\n actionId,\n path: action.path,\n content: lines.slice(start - 1, end).join(\"\\n\"),\n lines: [start, end],\n };\n } catch (cause) {\n return errorObservation(actionId, cause, {\n kind: \"FileReadFailed\",\n suggestion: \"Check the path with a read/list action before asking the executor to edit it.\",\n });\n }\n }\n\n async #editFile(\n action: EditAction & { readonly tenantId: string },\n actionId: string,\n ): Promise<Observation> {\n const full = safeWorkspacePath(this.#workspaceRoot, action.path);\n try {\n const before = await readFile(full, \"utf8\");\n const after = applyUnifiedDiff(before, action.diff);\n await writeFile(full, after, \"utf8\");\n await this.#eventLog?.commit({\n traceId: action.threadId ?? actionId,\n kind: \"content_write\",\n affected: [action.path],\n parent: null,\n snapshot: { [action.path]: after },\n });\n return {\n type: \"edit_applied\",\n actionId,\n path: action.path,\n diff: action.diff,\n conflicts: [],\n };\n } catch (cause) {\n return errorObservation(actionId, cause, {\n kind: \"EditFailed\",\n suggestion:\n \"Refresh the file, regenerate a unified diff against the current contents, and retry without overwrite semantics.\",\n });\n }\n }\n\n #approvalRequired(command: string): PolicyRule | null {\n return (\n this.#policy.find((rule) => rule.requireApproval !== \"never\" && matchesRule(command, rule)) ??\n null\n );\n }\n}\n\nfunction errorObservation(\n actionId: string,\n cause: unknown,\n override: { readonly kind?: string; readonly suggestion?: string } = {},\n): ErrorObservation {\n const message = cause instanceof Error ? cause.message : String(cause);\n return {\n type: \"error\",\n actionId,\n kind:\n override.kind ?? (cause instanceof CapabilityError ? \"CapabilityError\" : \"ExecutionError\"),\n message,\n suggestion:\n override.suggestion ??\n (cause instanceof CapabilityError && cause.suggestion\n ? cause.suggestion\n : \"Inspect the action, sandbox route, and tenant context, then retry with a narrower command.\"),\n };\n}\n\nexport function applyUnifiedDiff(original: string, diff: string): string {\n const originalHadTrailingNewline = original.endsWith(\"\\n\");\n let lines = originalHadTrailingNewline ? original.slice(0, -1).split(\"\\n\") : original.split(\"\\n\");\n if (original === \"\") lines = [];\n\n const diffLines = diff.split(\"\\n\");\n let index = 0;\n while (index < diffLines.length) {\n const line = diffLines[index] ?? \"\";\n if (!line.startsWith(\"@@\")) {\n index += 1;\n continue;\n }\n index += 1;\n const oldLines: string[] = [];\n const newLines: string[] = [];\n while (index < diffLines.length && !(diffLines[index] ?? \"\").startsWith(\"@@\")) {\n const current = diffLines[index] ?? \"\";\n if (current.startsWith(\"\\\\ No newline\")) {\n index += 1;\n continue;\n }\n if (current.startsWith(\"-\")) oldLines.push(current.slice(1));\n else if (current.startsWith(\"+\")) newLines.push(current.slice(1));\n else if (current.startsWith(\" \")) {\n oldLines.push(current.slice(1));\n newLines.push(current.slice(1));\n }\n index += 1;\n }\n const position = findSubsequence(lines, oldLines);\n if (position < 0) {\n throw new CapabilityError(\"code-execution\", \"Diff hunk does not match current file\", {\n suggestion:\n \"Re-read the file and generate a fresh unified diff against the latest content.\",\n metadata: { oldLines },\n statusCode: 409,\n });\n }\n lines.splice(position, oldLines.length, ...newLines);\n }\n\n return `${lines.join(\"\\n\")}${originalHadTrailingNewline ? \"\\n\" : \"\"}`;\n}\n\nfunction findSubsequence(haystack: readonly string[], needle: readonly string[]): number {\n if (needle.length === 0) return 0;\n for (let start = 0; start <= haystack.length - needle.length; start += 1) {\n let matched = true;\n for (let offset = 0; offset < needle.length; offset += 1) {\n if (haystack[start + offset] !== needle[offset]) {\n matched = false;\n break;\n }\n }\n if (matched) return start;\n }\n return -1;\n}\n"],"mappings":"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","names":[]}