@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/.nebutra/debug/code-execution.jsonl +30 -3
- package/.turbo/turbo-build.log +19 -19
- package/.turbo/turbo-test.log +7 -7
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +41 -0
- package/LICENSE +21 -676
- package/README.md +74 -13
- package/dist/{chunk-26GNZJ27.js → chunk-LLPO5OG2.js} +30 -56
- package/dist/chunk-LLPO5OG2.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +4 -7
- package/dist/index.js +1 -1
- package/package.json +7 -5
- package/src/index.ts +35 -74
- package/tsup.config.ts +7 -1
- package/dist/chunk-26GNZJ27.js.map +0 -1
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
|
|
@@ -1,64 +1,23 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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
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 =
|
|
4
|
-
|
|
5
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebutra/code-execution",
|
|
3
|
-
"version": "0.1.
|
|
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/
|
|
32
|
-
"@nebutra/event-log": "0.1.
|
|
33
|
-
"@nebutra/
|
|
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": "^
|
|
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 {
|
|
2
|
-
import {
|
|
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 =
|
|
12
|
-
|
|
13
|
-
export
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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: [
|
|
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":[]}
|