@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/src/index.ts
DELETED
|
@@ -1,436 +0,0 @@
|
|
|
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";
|
|
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";
|
|
12
|
-
import {
|
|
13
|
-
type ExecRequest,
|
|
14
|
-
type ExecResponse,
|
|
15
|
-
type SandboxHealth,
|
|
16
|
-
SandboxRuntime,
|
|
17
|
-
} from "@nebutra/sandbox-runtime";
|
|
18
|
-
|
|
19
|
-
export type ApprovalMode = ShellApprovalMode;
|
|
20
|
-
export type PolicyRule = ShellApprovalRule;
|
|
21
|
-
export const DefaultPolicy = DEFAULT_SHELL_APPROVAL_RULES;
|
|
22
|
-
|
|
23
|
-
export interface BaseAction {
|
|
24
|
-
readonly tenantId?: string;
|
|
25
|
-
readonly threadId?: string;
|
|
26
|
-
readonly actionId?: string;
|
|
27
|
-
readonly approved?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ShellAction extends BaseAction {
|
|
31
|
-
readonly type: "shell";
|
|
32
|
-
readonly cmd: string;
|
|
33
|
-
readonly cwd: string;
|
|
34
|
-
readonly timeoutS?: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface ReadAction extends BaseAction {
|
|
38
|
-
readonly type: "read";
|
|
39
|
-
readonly path: string;
|
|
40
|
-
readonly lineRange?: readonly [number, number];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface EditAction extends BaseAction {
|
|
44
|
-
readonly type: "edit";
|
|
45
|
-
readonly path: string;
|
|
46
|
-
readonly diff: string;
|
|
47
|
-
readonly lineRange?: readonly [number, number];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface IPythonAction extends BaseAction {
|
|
51
|
-
readonly type: "ipython";
|
|
52
|
-
readonly code: string;
|
|
53
|
-
readonly kernelId: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface GitAction extends BaseAction {
|
|
57
|
-
readonly type: "git";
|
|
58
|
-
readonly op: "status" | "log" | "push";
|
|
59
|
-
readonly args?: readonly string[];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export type Action = ShellAction | ReadAction | EditAction | IPythonAction | GitAction;
|
|
63
|
-
|
|
64
|
-
export interface ShellOutputObservation {
|
|
65
|
-
readonly type: "shell_output";
|
|
66
|
-
readonly actionId: string;
|
|
67
|
-
readonly stdout: string;
|
|
68
|
-
readonly stderr: string;
|
|
69
|
-
readonly exitCode: number;
|
|
70
|
-
readonly durationMs: number;
|
|
71
|
-
readonly executedOn: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface FileContentObservation {
|
|
75
|
-
readonly type: "file_content";
|
|
76
|
-
readonly actionId: string;
|
|
77
|
-
readonly path: string;
|
|
78
|
-
readonly content: string;
|
|
79
|
-
readonly lines: readonly [number, number];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface EditAppliedObservation {
|
|
83
|
-
readonly type: "edit_applied";
|
|
84
|
-
readonly actionId: string;
|
|
85
|
-
readonly path: string;
|
|
86
|
-
readonly diff: string;
|
|
87
|
-
readonly conflicts: readonly string[];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface ErrorObservation {
|
|
91
|
-
readonly type: "error";
|
|
92
|
-
readonly actionId: string;
|
|
93
|
-
readonly kind: string;
|
|
94
|
-
readonly message: string;
|
|
95
|
-
readonly suggestion: string;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export type Observation =
|
|
99
|
-
| ShellOutputObservation
|
|
100
|
-
| FileContentObservation
|
|
101
|
-
| EditAppliedObservation
|
|
102
|
-
| ErrorObservation;
|
|
103
|
-
|
|
104
|
-
export interface SandboxRuntimeLike {
|
|
105
|
-
exec(request: ExecRequest): Promise<ExecResponse>;
|
|
106
|
-
doctor(): Promise<readonly SandboxHealth[]>;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface EventLogLike {
|
|
110
|
-
commit(event: {
|
|
111
|
-
traceId: string;
|
|
112
|
-
kind: "tool_call" | "content_write" | "sandbox_exec";
|
|
113
|
-
affected: readonly string[];
|
|
114
|
-
parent: string | null;
|
|
115
|
-
snapshot?: Record<string, string>;
|
|
116
|
-
}): Promise<string>;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export interface CodeExecutorOptions {
|
|
120
|
-
readonly tenantId?: string;
|
|
121
|
-
readonly workspaceRoot?: string;
|
|
122
|
-
readonly debugRoot?: string;
|
|
123
|
-
readonly sandboxRuntime?: SandboxRuntimeLike;
|
|
124
|
-
readonly eventLog?: EventLogLike;
|
|
125
|
-
readonly policy?: readonly PolicyRule[];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export async function readExecutionDebug(root = process.cwd(), limit = 20): Promise<unknown[]> {
|
|
129
|
-
return readCapabilityDebug("code-execution", { root, limit });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function actionIdFrom(action: Action): string {
|
|
133
|
-
return action.actionId ?? `${action.type}_${Date.now().toString(36)}`;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function safeWorkspacePath(root: string, path: string): string {
|
|
137
|
-
const normalized = normalize(path);
|
|
138
|
-
if (normalized.startsWith("/") || normalized.includes("..")) {
|
|
139
|
-
throw new CapabilityError("code-execution", "Unsafe workspace path rejected", {
|
|
140
|
-
suggestion: "Use a path relative to the configured workspace root.",
|
|
141
|
-
metadata: { path },
|
|
142
|
-
statusCode: 400,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
return join(root, normalized);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export class CodeExecutor {
|
|
149
|
-
readonly #tenantId: string | undefined;
|
|
150
|
-
readonly #workspaceRoot: string;
|
|
151
|
-
readonly #debugRoot: string;
|
|
152
|
-
readonly #sandboxRuntime: SandboxRuntimeLike;
|
|
153
|
-
readonly #eventLog: EventLogLike | undefined;
|
|
154
|
-
readonly #policy: readonly PolicyRule[];
|
|
155
|
-
|
|
156
|
-
constructor(options: CodeExecutorOptions = {}) {
|
|
157
|
-
this.#tenantId = options.tenantId;
|
|
158
|
-
this.#workspaceRoot = options.workspaceRoot ?? process.cwd();
|
|
159
|
-
this.#debugRoot = options.debugRoot ?? process.cwd();
|
|
160
|
-
this.#sandboxRuntime = options.sandboxRuntime ?? SandboxRuntime.fromConfig();
|
|
161
|
-
this.#eventLog = options.eventLog;
|
|
162
|
-
this.#policy = options.policy ?? DefaultPolicy;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async run(action: Action): Promise<Observation> {
|
|
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
|
-
});
|
|
176
|
-
const actionId = actionIdFrom(action);
|
|
177
|
-
const started = Date.now();
|
|
178
|
-
let observation: Observation;
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
observation = await this.#runWithTenant({ ...action, tenantId }, actionId, started);
|
|
182
|
-
} catch (cause) {
|
|
183
|
-
observation = errorObservation(actionId, cause);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
await appendCapabilityDebug(
|
|
187
|
-
"code-execution",
|
|
188
|
-
{ type: "action", tenantId, action, observation },
|
|
189
|
-
{ root: this.#debugRoot },
|
|
190
|
-
);
|
|
191
|
-
return observation;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async doctor(): Promise<{
|
|
195
|
-
readonly capability: "code-execution";
|
|
196
|
-
readonly sandbox: readonly SandboxHealth[];
|
|
197
|
-
readonly policies: number;
|
|
198
|
-
}> {
|
|
199
|
-
const sandbox = await this.#sandboxRuntime.doctor();
|
|
200
|
-
await appendCapabilityDebug(
|
|
201
|
-
"code-execution",
|
|
202
|
-
{ type: "doctor", sandbox },
|
|
203
|
-
{ root: this.#debugRoot },
|
|
204
|
-
);
|
|
205
|
-
return { capability: "code-execution", sandbox, policies: this.#policy.length };
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
policy(): readonly PolicyRule[] {
|
|
209
|
-
return this.#policy;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async #runWithTenant(
|
|
213
|
-
action: Action & { readonly tenantId: string },
|
|
214
|
-
actionId: string,
|
|
215
|
-
started: number,
|
|
216
|
-
): Promise<Observation> {
|
|
217
|
-
switch (action.type) {
|
|
218
|
-
case "shell":
|
|
219
|
-
return this.#runShell(action, actionId, started);
|
|
220
|
-
case "read":
|
|
221
|
-
return this.#readFile(action, actionId);
|
|
222
|
-
case "edit":
|
|
223
|
-
return this.#editFile(action, actionId);
|
|
224
|
-
case "git":
|
|
225
|
-
return this.#runGit(action, actionId, started);
|
|
226
|
-
case "ipython":
|
|
227
|
-
return {
|
|
228
|
-
type: "error",
|
|
229
|
-
actionId,
|
|
230
|
-
kind: "AdapterUnavailable",
|
|
231
|
-
message: "IPython kernel execution is not configured",
|
|
232
|
-
suggestion: "Configure a notebook kernel sidecar and route it through sandbox-runtime.",
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async #runShell(
|
|
238
|
-
action: ShellAction & { readonly tenantId: string },
|
|
239
|
-
actionId: string,
|
|
240
|
-
started: number,
|
|
241
|
-
): Promise<Observation> {
|
|
242
|
-
const approval = this.#approvalRequired(action.cmd);
|
|
243
|
-
if (approval && !action.approved) {
|
|
244
|
-
return {
|
|
245
|
-
type: "error",
|
|
246
|
-
actionId,
|
|
247
|
-
kind: "ApprovalRequired",
|
|
248
|
-
message: `Command requires approval: ${approval.reason}`,
|
|
249
|
-
suggestion:
|
|
250
|
-
"Ask the user for explicit approval, then retry the action with approved: true.",
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const result = await this.#sandboxRuntime.exec({
|
|
255
|
-
cmd: action.cmd,
|
|
256
|
-
tenantId: action.tenantId,
|
|
257
|
-
...(action.threadId ? { threadId: action.threadId } : {}),
|
|
258
|
-
...(action.timeoutS !== undefined ? { hints: { durationS: action.timeoutS } } : {}),
|
|
259
|
-
});
|
|
260
|
-
await this.#eventLog?.commit({
|
|
261
|
-
traceId: action.threadId ?? actionId,
|
|
262
|
-
kind: "sandbox_exec",
|
|
263
|
-
affected: [],
|
|
264
|
-
parent: null,
|
|
265
|
-
});
|
|
266
|
-
return {
|
|
267
|
-
type: "shell_output",
|
|
268
|
-
actionId,
|
|
269
|
-
stdout: result.aggregatedOutput,
|
|
270
|
-
stderr: "",
|
|
271
|
-
exitCode: result.exitCode,
|
|
272
|
-
durationMs: Date.now() - started,
|
|
273
|
-
executedOn: result.executedOn,
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
async #runGit(
|
|
278
|
-
action: GitAction & { readonly tenantId: string },
|
|
279
|
-
actionId: string,
|
|
280
|
-
started: number,
|
|
281
|
-
): Promise<Observation> {
|
|
282
|
-
const command = `git ${action.op}${action.args?.length ? ` ${action.args.join(" ")}` : ""}`;
|
|
283
|
-
return this.#runShell(
|
|
284
|
-
{
|
|
285
|
-
type: "shell",
|
|
286
|
-
cmd: command,
|
|
287
|
-
cwd: this.#workspaceRoot,
|
|
288
|
-
tenantId: action.tenantId,
|
|
289
|
-
...(action.threadId ? { threadId: action.threadId } : {}),
|
|
290
|
-
...(action.approved !== undefined ? { approved: action.approved } : {}),
|
|
291
|
-
},
|
|
292
|
-
actionId,
|
|
293
|
-
started,
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async #readFile(
|
|
298
|
-
action: ReadAction & { readonly tenantId: string },
|
|
299
|
-
actionId: string,
|
|
300
|
-
): Promise<Observation> {
|
|
301
|
-
try {
|
|
302
|
-
const content = await readFile(safeWorkspacePath(this.#workspaceRoot, action.path), "utf8");
|
|
303
|
-
const lines = content.split("\n");
|
|
304
|
-
const start = action.lineRange?.[0] ?? 1;
|
|
305
|
-
const end = action.lineRange?.[1] ?? lines.length;
|
|
306
|
-
return {
|
|
307
|
-
type: "file_content",
|
|
308
|
-
actionId,
|
|
309
|
-
path: action.path,
|
|
310
|
-
content: lines.slice(start - 1, end).join("\n"),
|
|
311
|
-
lines: [start, end],
|
|
312
|
-
};
|
|
313
|
-
} catch (cause) {
|
|
314
|
-
return errorObservation(actionId, cause, {
|
|
315
|
-
kind: "FileReadFailed",
|
|
316
|
-
suggestion: "Check the path with a read/list action before asking the executor to edit it.",
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
async #editFile(
|
|
322
|
-
action: EditAction & { readonly tenantId: string },
|
|
323
|
-
actionId: string,
|
|
324
|
-
): Promise<Observation> {
|
|
325
|
-
const full = safeWorkspacePath(this.#workspaceRoot, action.path);
|
|
326
|
-
try {
|
|
327
|
-
const before = await readFile(full, "utf8");
|
|
328
|
-
const after = applyUnifiedDiff(before, action.diff);
|
|
329
|
-
await writeFile(full, after, "utf8");
|
|
330
|
-
await this.#eventLog?.commit({
|
|
331
|
-
traceId: action.threadId ?? actionId,
|
|
332
|
-
kind: "content_write",
|
|
333
|
-
affected: [action.path],
|
|
334
|
-
parent: null,
|
|
335
|
-
snapshot: { [action.path]: after },
|
|
336
|
-
});
|
|
337
|
-
return {
|
|
338
|
-
type: "edit_applied",
|
|
339
|
-
actionId,
|
|
340
|
-
path: action.path,
|
|
341
|
-
diff: action.diff,
|
|
342
|
-
conflicts: [],
|
|
343
|
-
};
|
|
344
|
-
} catch (cause) {
|
|
345
|
-
return errorObservation(actionId, cause, {
|
|
346
|
-
kind: "EditFailed",
|
|
347
|
-
suggestion:
|
|
348
|
-
"Refresh the file, regenerate a unified diff against the current contents, and retry without overwrite semantics.",
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
#approvalRequired(command: string): PolicyRule | null {
|
|
354
|
-
return shellApprovalRequired(command, this.#policy);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function errorObservation(
|
|
359
|
-
actionId: string,
|
|
360
|
-
cause: unknown,
|
|
361
|
-
override: { readonly kind?: string; readonly suggestion?: string } = {},
|
|
362
|
-
): ErrorObservation {
|
|
363
|
-
const message = cause instanceof Error ? cause.message : String(cause);
|
|
364
|
-
return {
|
|
365
|
-
type: "error",
|
|
366
|
-
actionId,
|
|
367
|
-
kind:
|
|
368
|
-
override.kind ?? (cause instanceof CapabilityError ? "CapabilityError" : "ExecutionError"),
|
|
369
|
-
message,
|
|
370
|
-
suggestion:
|
|
371
|
-
override.suggestion ??
|
|
372
|
-
(cause instanceof CapabilityError && cause.suggestion
|
|
373
|
-
? cause.suggestion
|
|
374
|
-
: "Inspect the action, sandbox route, and tenant context, then retry with a narrower command."),
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
export function applyUnifiedDiff(original: string, diff: string): string {
|
|
379
|
-
const originalHadTrailingNewline = original.endsWith("\n");
|
|
380
|
-
let lines = originalHadTrailingNewline ? original.slice(0, -1).split("\n") : original.split("\n");
|
|
381
|
-
if (original === "") lines = [];
|
|
382
|
-
|
|
383
|
-
const diffLines = diff.split("\n");
|
|
384
|
-
let index = 0;
|
|
385
|
-
while (index < diffLines.length) {
|
|
386
|
-
const line = diffLines[index] ?? "";
|
|
387
|
-
if (!line.startsWith("@@")) {
|
|
388
|
-
index += 1;
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
index += 1;
|
|
392
|
-
const oldLines: string[] = [];
|
|
393
|
-
const newLines: string[] = [];
|
|
394
|
-
while (index < diffLines.length && !(diffLines[index] ?? "").startsWith("@@")) {
|
|
395
|
-
const current = diffLines[index] ?? "";
|
|
396
|
-
if (current.startsWith("\\ No newline")) {
|
|
397
|
-
index += 1;
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
if (current.startsWith("-")) oldLines.push(current.slice(1));
|
|
401
|
-
else if (current.startsWith("+")) newLines.push(current.slice(1));
|
|
402
|
-
else if (current.startsWith(" ")) {
|
|
403
|
-
oldLines.push(current.slice(1));
|
|
404
|
-
newLines.push(current.slice(1));
|
|
405
|
-
}
|
|
406
|
-
index += 1;
|
|
407
|
-
}
|
|
408
|
-
const position = findSubsequence(lines, oldLines);
|
|
409
|
-
if (position < 0) {
|
|
410
|
-
throw new CapabilityError("code-execution", "Diff hunk does not match current file", {
|
|
411
|
-
suggestion:
|
|
412
|
-
"Re-read the file and generate a fresh unified diff against the latest content.",
|
|
413
|
-
metadata: { oldLines },
|
|
414
|
-
statusCode: 409,
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
lines.splice(position, oldLines.length, ...newLines);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return `${lines.join("\n")}${originalHadTrailingNewline ? "\n" : ""}`;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function findSubsequence(haystack: readonly string[], needle: readonly string[]): number {
|
|
424
|
-
if (needle.length === 0) return 0;
|
|
425
|
-
for (let start = 0; start <= haystack.length - needle.length; start += 1) {
|
|
426
|
-
let matched = true;
|
|
427
|
-
for (let offset = 0; offset < needle.length; offset += 1) {
|
|
428
|
-
if (haystack[start + offset] !== needle[offset]) {
|
|
429
|
-
matched = false;
|
|
430
|
-
break;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
if (matched) return start;
|
|
434
|
-
}
|
|
435
|
-
return -1;
|
|
436
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"target": "esnext",
|
|
7
|
-
"types": ["node"],
|
|
8
|
-
"incremental": false
|
|
9
|
-
},
|
|
10
|
-
"include": ["src", "examples"],
|
|
11
|
-
"exclude": ["node_modules", "dist"]
|
|
12
|
-
}
|
package/tsup.config.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "tsup";
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ["src/index.ts", "src/cli.ts"],
|
|
5
|
-
format: ["esm"],
|
|
6
|
-
dts: true,
|
|
7
|
-
sourcemap: true,
|
|
8
|
-
clean: true,
|
|
9
|
-
target: "es2022",
|
|
10
|
-
external: [
|
|
11
|
-
"@nebutra/capability-kit/debug",
|
|
12
|
-
"@nebutra/errors",
|
|
13
|
-
"@nebutra/event-log",
|
|
14
|
-
"@nebutra/execution-policy",
|
|
15
|
-
"@nebutra/sandbox-runtime",
|
|
16
|
-
],
|
|
17
|
-
});
|