@anvia/sandbox 0.2.2 → 0.3.0
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/dist/index.d.ts +82 -2
- package/dist/index.js +353 -107
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,18 @@ import { AnyTool } from '@anvia/core/tool';
|
|
|
2
2
|
|
|
3
3
|
type SandboxFileType = "file" | "directory" | "symlink" | "other";
|
|
4
4
|
type SandboxNetworkMode = boolean | "none" | "host" | string;
|
|
5
|
+
type SandboxWorkspaceOptions = {
|
|
6
|
+
mode?: "ephemeral";
|
|
7
|
+
} | {
|
|
8
|
+
mode: "persistent";
|
|
9
|
+
id: string;
|
|
10
|
+
destroyOnSessionDestroy?: boolean;
|
|
11
|
+
};
|
|
12
|
+
interface SandboxLifecycleOptions {
|
|
13
|
+
ttlMs?: number;
|
|
14
|
+
idleTimeoutMs?: number;
|
|
15
|
+
autoDestroy?: boolean;
|
|
16
|
+
}
|
|
5
17
|
interface Sandbox {
|
|
6
18
|
readonly provider: string;
|
|
7
19
|
createSession(options?: SandboxCreateSessionOptions): Promise<SandboxSession>;
|
|
@@ -11,6 +23,7 @@ interface SandboxSession {
|
|
|
11
23
|
readonly provider: string;
|
|
12
24
|
readonly workdir: string;
|
|
13
25
|
exec(options: SandboxExecOptions): Promise<SandboxExecResult>;
|
|
26
|
+
execStream(options: SandboxExecOptions): AsyncIterable<SandboxExecStreamEvent>;
|
|
14
27
|
readFile(path: string): Promise<Uint8Array>;
|
|
15
28
|
readTextFile(path: string): Promise<string>;
|
|
16
29
|
writeFile(path: string, data: string | Uint8Array): Promise<void>;
|
|
@@ -20,6 +33,7 @@ interface SandboxSession {
|
|
|
20
33
|
}
|
|
21
34
|
interface SandboxCreateSessionOptions {
|
|
22
35
|
id?: string;
|
|
36
|
+
workspace?: SandboxWorkspaceOptions;
|
|
23
37
|
manifest?: SandboxManifest;
|
|
24
38
|
metadata?: Record<string, string>;
|
|
25
39
|
}
|
|
@@ -31,6 +45,7 @@ interface SandboxManifest {
|
|
|
31
45
|
interface SandboxLimits {
|
|
32
46
|
timeoutMs?: number;
|
|
33
47
|
maxOutputBytes?: number;
|
|
48
|
+
maxFileBytes?: number;
|
|
34
49
|
memoryMb?: number;
|
|
35
50
|
cpus?: number;
|
|
36
51
|
pidsLimit?: number;
|
|
@@ -56,6 +71,18 @@ interface SandboxExecResult {
|
|
|
56
71
|
stdoutTruncated: boolean;
|
|
57
72
|
stderrTruncated: boolean;
|
|
58
73
|
}
|
|
74
|
+
type SandboxExecStreamEvent = {
|
|
75
|
+
type: "stdout";
|
|
76
|
+
chunk: Uint8Array;
|
|
77
|
+
text: string;
|
|
78
|
+
} | {
|
|
79
|
+
type: "stderr";
|
|
80
|
+
chunk: Uint8Array;
|
|
81
|
+
text: string;
|
|
82
|
+
} | {
|
|
83
|
+
type: "exit";
|
|
84
|
+
result: SandboxExecResult;
|
|
85
|
+
};
|
|
59
86
|
interface SandboxFileEntry {
|
|
60
87
|
path: string;
|
|
61
88
|
type: SandboxFileType;
|
|
@@ -66,22 +93,65 @@ interface DockerSandboxSecurityOptions {
|
|
|
66
93
|
noNewPrivileges?: boolean;
|
|
67
94
|
dropCapabilities?: string[];
|
|
68
95
|
}
|
|
96
|
+
interface DockerSandboxNetworkOptions {
|
|
97
|
+
mode: SandboxNetworkMode;
|
|
98
|
+
}
|
|
99
|
+
interface SandboxSessionEvent {
|
|
100
|
+
sessionId: string;
|
|
101
|
+
provider: string;
|
|
102
|
+
workdir: string;
|
|
103
|
+
}
|
|
104
|
+
interface SandboxExecEvent extends SandboxSessionEvent {
|
|
105
|
+
command: string;
|
|
106
|
+
args: string[];
|
|
107
|
+
cwd?: string;
|
|
108
|
+
}
|
|
109
|
+
interface SandboxExecEndEvent extends SandboxExecEvent {
|
|
110
|
+
result: SandboxExecResult;
|
|
111
|
+
}
|
|
112
|
+
interface SandboxFileWriteEvent extends SandboxSessionEvent {
|
|
113
|
+
path: string;
|
|
114
|
+
size: number;
|
|
115
|
+
}
|
|
116
|
+
interface SandboxHooks {
|
|
117
|
+
onSessionCreate?: (event: SandboxSessionEvent) => void | Promise<void>;
|
|
118
|
+
onExecStart?: (event: SandboxExecEvent) => void | Promise<void>;
|
|
119
|
+
onExecEnd?: (event: SandboxExecEndEvent) => void | Promise<void>;
|
|
120
|
+
onFileWrite?: (event: SandboxFileWriteEvent) => void | Promise<void>;
|
|
121
|
+
onDestroy?: (event: SandboxSessionEvent) => void | Promise<void>;
|
|
122
|
+
}
|
|
69
123
|
interface DockerSandboxOptions {
|
|
70
124
|
image?: string;
|
|
71
125
|
pull?: "missing" | "always" | "never";
|
|
72
126
|
workdir?: string;
|
|
73
|
-
|
|
127
|
+
workspace?: SandboxWorkspaceOptions;
|
|
128
|
+
lifecycle?: SandboxLifecycleOptions;
|
|
129
|
+
network?: SandboxNetworkMode | DockerSandboxNetworkOptions;
|
|
74
130
|
user?: string;
|
|
75
131
|
dockerPath?: string;
|
|
76
132
|
labels?: Record<string, string>;
|
|
77
133
|
limits?: SandboxLimits;
|
|
78
134
|
security?: DockerSandboxSecurityOptions;
|
|
135
|
+
hooks?: SandboxHooks;
|
|
79
136
|
}
|
|
80
137
|
interface SandboxToolsOptions {
|
|
138
|
+
allow?: SandboxToolName[];
|
|
81
139
|
include?: SandboxToolName[];
|
|
82
140
|
execTimeoutMs?: number;
|
|
141
|
+
exec?: SandboxExecToolPolicy;
|
|
142
|
+
readFile?: SandboxFileToolPolicy;
|
|
143
|
+
writeFile?: SandboxFileToolPolicy;
|
|
83
144
|
}
|
|
84
145
|
type SandboxToolName = "exec_command" | "read_file" | "write_file" | "list_files";
|
|
146
|
+
interface SandboxExecToolPolicy {
|
|
147
|
+
allowedCommands?: string[];
|
|
148
|
+
blockedCommands?: string[];
|
|
149
|
+
defaultTimeoutMs?: number;
|
|
150
|
+
maxTimeoutMs?: number;
|
|
151
|
+
}
|
|
152
|
+
interface SandboxFileToolPolicy {
|
|
153
|
+
maxBytes?: number;
|
|
154
|
+
}
|
|
85
155
|
type SandboxToolsFactory = (session: SandboxSession, options?: SandboxToolsOptions) => AnyTool[];
|
|
86
156
|
|
|
87
157
|
declare class DockerSandbox implements Sandbox {
|
|
@@ -89,13 +159,19 @@ declare class DockerSandbox implements Sandbox {
|
|
|
89
159
|
private readonly image;
|
|
90
160
|
private readonly pull;
|
|
91
161
|
private readonly workdir;
|
|
162
|
+
private readonly workspace;
|
|
163
|
+
private readonly lifecycle;
|
|
92
164
|
private readonly network;
|
|
93
165
|
private readonly dockerPath;
|
|
94
166
|
private readonly labels;
|
|
95
167
|
private readonly limits;
|
|
96
168
|
private readonly security;
|
|
169
|
+
private readonly hooks;
|
|
97
170
|
private readonly user;
|
|
98
171
|
constructor(options?: DockerSandboxOptions);
|
|
172
|
+
static node(options?: DockerSandboxOptions): DockerSandbox;
|
|
173
|
+
static python(options?: DockerSandboxOptions): DockerSandbox;
|
|
174
|
+
static deno(options?: DockerSandboxOptions): DockerSandbox;
|
|
99
175
|
createSession(options?: SandboxCreateSessionOptions): Promise<SandboxSession>;
|
|
100
176
|
private ensureImage;
|
|
101
177
|
private createRunArgs;
|
|
@@ -130,7 +206,11 @@ declare class SandboxPathError extends SandboxError {
|
|
|
130
206
|
}
|
|
131
207
|
declare class SandboxTimeoutError extends SandboxError {
|
|
132
208
|
}
|
|
209
|
+
declare class SandboxFileSizeError extends SandboxError {
|
|
210
|
+
}
|
|
211
|
+
declare class SandboxToolPolicyError extends SandboxError {
|
|
212
|
+
}
|
|
133
213
|
|
|
134
214
|
declare function createSandboxTools(session: SandboxSession, options?: SandboxToolsOptions): AnyTool[];
|
|
135
215
|
|
|
136
|
-
export { DockerSandbox, type DockerSandboxOptions, type DockerSandboxSecurityOptions, type Sandbox, type SandboxCreateSessionOptions, SandboxDockerCommandError, SandboxDockerUnavailableError, SandboxError, type SandboxExecOptions, type SandboxExecResult, type SandboxFileEntry, type SandboxFileType, type SandboxLimits, type SandboxManifest, type SandboxNetworkMode, SandboxPathError, type SandboxSession, SandboxSessionDestroyedError, SandboxTimeoutError, type SandboxToolName, type SandboxToolsFactory, type SandboxToolsOptions, createSandboxTools };
|
|
216
|
+
export { DockerSandbox, type DockerSandboxNetworkOptions, type DockerSandboxOptions, type DockerSandboxSecurityOptions, type Sandbox, type SandboxCreateSessionOptions, SandboxDockerCommandError, SandboxDockerUnavailableError, SandboxError, type SandboxExecEndEvent, type SandboxExecEvent, type SandboxExecOptions, type SandboxExecResult, type SandboxExecStreamEvent, type SandboxExecToolPolicy, type SandboxFileEntry, SandboxFileSizeError, type SandboxFileToolPolicy, type SandboxFileType, type SandboxFileWriteEvent, type SandboxHooks, type SandboxLifecycleOptions, type SandboxLimits, type SandboxManifest, type SandboxNetworkMode, SandboxPathError, type SandboxSession, SandboxSessionDestroyedError, type SandboxSessionEvent, SandboxTimeoutError, type SandboxToolName, SandboxToolPolicyError, type SandboxToolsFactory, type SandboxToolsOptions, type SandboxWorkspaceOptions, createSandboxTools };
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,10 @@ var SandboxPathError = class extends SandboxError {
|
|
|
31
31
|
};
|
|
32
32
|
var SandboxTimeoutError = class extends SandboxError {
|
|
33
33
|
};
|
|
34
|
+
var SandboxFileSizeError = class extends SandboxError {
|
|
35
|
+
};
|
|
36
|
+
var SandboxToolPolicyError = class extends SandboxError {
|
|
37
|
+
};
|
|
34
38
|
|
|
35
39
|
// src/docker-cli.ts
|
|
36
40
|
var defaultMaxOutputBytes = 1024 * 1024;
|
|
@@ -173,21 +177,30 @@ var defaultImage = "node:22-bookworm";
|
|
|
173
177
|
var defaultWorkdir = "/workspace";
|
|
174
178
|
var defaultTimeoutMs = 3e4;
|
|
175
179
|
var defaultMaxOutputBytes2 = 1024 * 1024;
|
|
176
|
-
var DockerSandbox = class {
|
|
180
|
+
var DockerSandbox = class _DockerSandbox {
|
|
177
181
|
provider = "docker";
|
|
178
182
|
image;
|
|
179
183
|
pull;
|
|
180
184
|
workdir;
|
|
185
|
+
workspace;
|
|
186
|
+
lifecycle;
|
|
181
187
|
network;
|
|
182
188
|
dockerPath;
|
|
183
189
|
labels;
|
|
184
190
|
limits;
|
|
185
191
|
security;
|
|
192
|
+
hooks;
|
|
186
193
|
user;
|
|
187
194
|
constructor(options = {}) {
|
|
188
195
|
this.image = options.image ?? defaultImage;
|
|
189
196
|
this.pull = options.pull ?? "missing";
|
|
190
197
|
this.workdir = options.workdir ?? defaultWorkdir;
|
|
198
|
+
this.workspace = options.workspace ?? { mode: "ephemeral" };
|
|
199
|
+
this.lifecycle = {
|
|
200
|
+
autoDestroy: options.lifecycle?.autoDestroy ?? true,
|
|
201
|
+
...options.lifecycle?.ttlMs === void 0 ? {} : { ttlMs: options.lifecycle.ttlMs },
|
|
202
|
+
...options.lifecycle?.idleTimeoutMs === void 0 ? {} : { idleTimeoutMs: options.lifecycle.idleTimeoutMs }
|
|
203
|
+
};
|
|
191
204
|
this.network = options.network ?? false;
|
|
192
205
|
this.dockerPath = options.dockerPath ?? "docker";
|
|
193
206
|
this.labels = options.labels ?? {};
|
|
@@ -197,19 +210,35 @@ var DockerSandbox = class {
|
|
|
197
210
|
noNewPrivileges: options.security?.noNewPrivileges ?? true,
|
|
198
211
|
dropCapabilities: options.security?.dropCapabilities ?? ["ALL"]
|
|
199
212
|
};
|
|
213
|
+
this.hooks = options.hooks ?? {};
|
|
200
214
|
this.user = options.user;
|
|
201
215
|
}
|
|
216
|
+
static node(options = {}) {
|
|
217
|
+
return new _DockerSandbox({ ...options, image: options.image ?? "node:22-bookworm" });
|
|
218
|
+
}
|
|
219
|
+
static python(options = {}) {
|
|
220
|
+
return new _DockerSandbox({ ...options, image: options.image ?? "python:3.13-bookworm" });
|
|
221
|
+
}
|
|
222
|
+
static deno(options = {}) {
|
|
223
|
+
return new _DockerSandbox({ ...options, image: options.image ?? "denoland/deno:debian" });
|
|
224
|
+
}
|
|
202
225
|
async createSession(options = {}) {
|
|
203
226
|
await this.ensureImage();
|
|
204
227
|
const id = sanitizeResourceId(options.id ?? randomUUID());
|
|
228
|
+
const workspace = options.workspace ?? this.workspace;
|
|
229
|
+
const workspaceId = getWorkspaceId(workspace, id);
|
|
205
230
|
const containerName = `anvia-sandbox-${id}`;
|
|
206
|
-
const volumeName = `anvia-sandbox-${
|
|
231
|
+
const volumeName = `anvia-sandbox-${workspaceId}-workspace`;
|
|
232
|
+
const removeVolumeOnDestroy = shouldDestroyWorkspace(workspace);
|
|
207
233
|
await assertDockerCli(["volume", "create", volumeName], this.cliOptions());
|
|
208
234
|
try {
|
|
209
|
-
await assertDockerCli(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
235
|
+
await assertDockerCli(
|
|
236
|
+
this.createRunArgs(containerName, volumeName, workspace, options.metadata),
|
|
237
|
+
{
|
|
238
|
+
...this.cliOptions(),
|
|
239
|
+
timeoutMs: this.limits.timeoutMs ?? defaultTimeoutMs
|
|
240
|
+
}
|
|
241
|
+
);
|
|
213
242
|
const session = new DockerSandboxSession({
|
|
214
243
|
id,
|
|
215
244
|
containerName,
|
|
@@ -217,12 +246,16 @@ var DockerSandbox = class {
|
|
|
217
246
|
workdir: this.workdir,
|
|
218
247
|
dockerPath: this.dockerPath,
|
|
219
248
|
limits: this.limits,
|
|
220
|
-
|
|
249
|
+
lifecycle: this.lifecycle,
|
|
250
|
+
removeVolumeOnDestroy,
|
|
251
|
+
env: options.manifest?.env ?? {},
|
|
252
|
+
hooks: this.hooks
|
|
221
253
|
});
|
|
222
254
|
await session.applyManifest(options.manifest);
|
|
255
|
+
await this.hooks.onSessionCreate?.(session.event());
|
|
223
256
|
return session;
|
|
224
257
|
} catch (error) {
|
|
225
|
-
await this.cleanup(containerName, volumeName);
|
|
258
|
+
await this.cleanup(containerName, removeVolumeOnDestroy ? volumeName : void 0);
|
|
226
259
|
throw error;
|
|
227
260
|
}
|
|
228
261
|
}
|
|
@@ -238,7 +271,7 @@ var DockerSandbox = class {
|
|
|
238
271
|
}
|
|
239
272
|
}
|
|
240
273
|
}
|
|
241
|
-
createRunArgs(containerName, volumeName, metadata) {
|
|
274
|
+
createRunArgs(containerName, volumeName, workspace, metadata) {
|
|
242
275
|
const args = [
|
|
243
276
|
"run",
|
|
244
277
|
"-d",
|
|
@@ -249,7 +282,11 @@ var DockerSandbox = class {
|
|
|
249
282
|
"-w",
|
|
250
283
|
this.workdir,
|
|
251
284
|
"--label",
|
|
252
|
-
"anvia.sandbox=true"
|
|
285
|
+
"anvia.sandbox=true",
|
|
286
|
+
"--label",
|
|
287
|
+
`anvia.sandbox.workspace.mode=${workspace.mode ?? "ephemeral"}`,
|
|
288
|
+
"--label",
|
|
289
|
+
`anvia.sandbox.workspace.volume=${volumeName}`
|
|
253
290
|
];
|
|
254
291
|
for (const [key, value] of Object.entries(this.labels)) {
|
|
255
292
|
args.push("--label", `${key}=${value}`);
|
|
@@ -274,12 +311,13 @@ var DockerSandbox = class {
|
|
|
274
311
|
return args;
|
|
275
312
|
}
|
|
276
313
|
appendNetworkArgs(args) {
|
|
277
|
-
|
|
314
|
+
const mode = typeof this.network === "object" ? this.network.mode : this.network;
|
|
315
|
+
if (mode === false || mode === "none") {
|
|
278
316
|
args.push("--network", "none");
|
|
279
317
|
return;
|
|
280
318
|
}
|
|
281
|
-
if (
|
|
282
|
-
args.push("--network",
|
|
319
|
+
if (mode !== true) {
|
|
320
|
+
args.push("--network", mode);
|
|
283
321
|
}
|
|
284
322
|
}
|
|
285
323
|
appendLimitArgs(args) {
|
|
@@ -312,9 +350,11 @@ var DockerSandbox = class {
|
|
|
312
350
|
}
|
|
313
351
|
async cleanup(containerName, volumeName) {
|
|
314
352
|
await runDockerCli(["rm", "-f", containerName], this.cliOptions()).catch(() => void 0);
|
|
315
|
-
|
|
316
|
-
(
|
|
317
|
-
|
|
353
|
+
if (volumeName !== void 0) {
|
|
354
|
+
await runDockerCli(["volume", "rm", "-f", volumeName], this.cliOptions()).catch(
|
|
355
|
+
() => void 0
|
|
356
|
+
);
|
|
357
|
+
}
|
|
318
358
|
}
|
|
319
359
|
};
|
|
320
360
|
var DockerSandboxSession = class {
|
|
@@ -325,7 +365,13 @@ var DockerSandboxSession = class {
|
|
|
325
365
|
volumeName;
|
|
326
366
|
dockerPath;
|
|
327
367
|
limits;
|
|
368
|
+
lifecycle;
|
|
369
|
+
removeVolumeOnDestroy;
|
|
328
370
|
env;
|
|
371
|
+
hooks;
|
|
372
|
+
ttlTimer;
|
|
373
|
+
idleTimer;
|
|
374
|
+
activeOperations = 0;
|
|
329
375
|
destroyed = false;
|
|
330
376
|
constructor(options) {
|
|
331
377
|
this.id = options.id;
|
|
@@ -334,122 +380,169 @@ var DockerSandboxSession = class {
|
|
|
334
380
|
this.workdir = options.workdir;
|
|
335
381
|
this.dockerPath = options.dockerPath;
|
|
336
382
|
this.limits = options.limits;
|
|
383
|
+
this.lifecycle = options.lifecycle;
|
|
384
|
+
this.removeVolumeOnDestroy = options.removeVolumeOnDestroy;
|
|
337
385
|
this.env = options.env;
|
|
386
|
+
this.hooks = options.hooks;
|
|
387
|
+
this.startLifecycleTimers();
|
|
338
388
|
}
|
|
339
389
|
async applyManifest(manifest) {
|
|
340
|
-
this.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
390
|
+
await this.runOperation(async () => {
|
|
391
|
+
for (const directory of manifest?.directories ?? []) {
|
|
392
|
+
await this.mkdir(directory);
|
|
393
|
+
}
|
|
394
|
+
for (const [filePath, content] of Object.entries(manifest?.files ?? {})) {
|
|
395
|
+
await this.writeFile(filePath, content);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
347
398
|
}
|
|
348
399
|
async exec(options) {
|
|
349
|
-
this.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
400
|
+
return this.runOperation(async () => {
|
|
401
|
+
await this.hooks.onExecStart?.({
|
|
402
|
+
...this.event(),
|
|
403
|
+
command: options.command,
|
|
404
|
+
args: options.args ?? [],
|
|
405
|
+
...options.cwd === void 0 ? {} : { cwd: options.cwd }
|
|
355
406
|
});
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
args.push(this.containerName, options.command, ...options.args ?? []);
|
|
367
|
-
const cliOptions = {
|
|
368
|
-
dockerPath: this.dockerPath,
|
|
369
|
-
timeoutMs: options.timeoutMs ?? this.limits.timeoutMs ?? defaultTimeoutMs,
|
|
370
|
-
maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes2,
|
|
371
|
-
...options.input === void 0 ? {} : { input: options.input },
|
|
372
|
-
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
373
|
-
...options.onStdout === void 0 ? {} : { onStdout: options.onStdout },
|
|
374
|
-
...options.onStderr === void 0 ? {} : { onStderr: options.onStderr }
|
|
375
|
-
};
|
|
376
|
-
const result = await runDockerCli(args, cliOptions);
|
|
377
|
-
if (result.timedOut) {
|
|
378
|
-
return {
|
|
379
|
-
...result,
|
|
380
|
-
exitCode: result.exitCode === 0 ? 124 : result.exitCode
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
return result;
|
|
407
|
+
const normalizedResult = await this.execCommand(options);
|
|
408
|
+
await this.hooks.onExecEnd?.({
|
|
409
|
+
...this.event(),
|
|
410
|
+
command: options.command,
|
|
411
|
+
args: options.args ?? [],
|
|
412
|
+
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
413
|
+
result: normalizedResult
|
|
414
|
+
});
|
|
415
|
+
return normalizedResult;
|
|
416
|
+
});
|
|
384
417
|
}
|
|
385
|
-
async
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
418
|
+
async *execStream(options) {
|
|
419
|
+
const events = [];
|
|
420
|
+
let notify;
|
|
421
|
+
let done = false;
|
|
422
|
+
let error;
|
|
423
|
+
const push = (event) => {
|
|
424
|
+
events.push(event);
|
|
425
|
+
notify?.();
|
|
426
|
+
notify = void 0;
|
|
427
|
+
};
|
|
428
|
+
const wait = () => new Promise((resolve) => {
|
|
429
|
+
notify = resolve;
|
|
430
|
+
});
|
|
431
|
+
const run = this.exec({
|
|
432
|
+
...options,
|
|
433
|
+
onStdout: (chunk) => {
|
|
434
|
+
options.onStdout?.(chunk);
|
|
435
|
+
push({ type: "stdout", chunk, text: Buffer.from(chunk).toString("utf8") });
|
|
436
|
+
},
|
|
437
|
+
onStderr: (chunk) => {
|
|
438
|
+
options.onStderr?.(chunk);
|
|
439
|
+
push({ type: "stderr", chunk, text: Buffer.from(chunk).toString("utf8") });
|
|
440
|
+
}
|
|
441
|
+
}).then((result) => {
|
|
442
|
+
push({ type: "exit", result });
|
|
443
|
+
}).catch((caught) => {
|
|
444
|
+
error = caught;
|
|
445
|
+
}).finally(() => {
|
|
446
|
+
done = true;
|
|
447
|
+
notify?.();
|
|
448
|
+
notify = void 0;
|
|
449
|
+
});
|
|
390
450
|
try {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
451
|
+
while (!done || events.length > 0) {
|
|
452
|
+
const event = events.shift();
|
|
453
|
+
if (event !== void 0) {
|
|
454
|
+
yield event;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
await wait();
|
|
458
|
+
}
|
|
459
|
+
if (error !== void 0) {
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
397
462
|
} finally {
|
|
398
|
-
await
|
|
463
|
+
await run;
|
|
399
464
|
}
|
|
400
465
|
}
|
|
466
|
+
async readFile(filePath) {
|
|
467
|
+
return this.runOperation(async () => {
|
|
468
|
+
const normalized = normalizeSandboxPath(filePath);
|
|
469
|
+
const tempDir = await mkdtemp(path2.join(os.tmpdir(), "anvia-sandbox-read-"));
|
|
470
|
+
const target = path2.join(tempDir, path2.basename(normalized));
|
|
471
|
+
try {
|
|
472
|
+
await assertDockerCli(
|
|
473
|
+
["cp", `${this.containerName}:${containerPath(this.workdir, normalized)}`, target],
|
|
474
|
+
this.cliOptions()
|
|
475
|
+
);
|
|
476
|
+
const { readFile } = await import("fs/promises");
|
|
477
|
+
const bytes = await readFile(target);
|
|
478
|
+
this.assertFileSize(bytes.byteLength, filePath);
|
|
479
|
+
return bytes;
|
|
480
|
+
} finally {
|
|
481
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
}
|
|
401
485
|
async readTextFile(filePath) {
|
|
402
486
|
const bytes = await this.readFile(filePath);
|
|
403
487
|
return new TextDecoder().decode(bytes);
|
|
404
488
|
}
|
|
405
489
|
async writeFile(filePath, data) {
|
|
406
|
-
this.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
490
|
+
await this.runOperation(async () => {
|
|
491
|
+
const size = byteLength(data);
|
|
492
|
+
this.assertFileSize(size, filePath);
|
|
493
|
+
const normalized = normalizeSandboxPath(filePath);
|
|
494
|
+
await this.mkdir(parentSandboxPath(normalized));
|
|
495
|
+
const tempDir = await mkdtemp(path2.join(os.tmpdir(), "anvia-sandbox-write-"));
|
|
496
|
+
const source = path2.join(tempDir, path2.basename(normalized));
|
|
497
|
+
try {
|
|
498
|
+
await writeFile(source, data);
|
|
499
|
+
await assertDockerCli(
|
|
500
|
+
["cp", source, `${this.containerName}:${containerPath(this.workdir, normalized)}`],
|
|
501
|
+
this.cliOptions()
|
|
502
|
+
);
|
|
503
|
+
await this.hooks.onFileWrite?.({ ...this.event(), path: normalized, size });
|
|
504
|
+
} finally {
|
|
505
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
506
|
+
}
|
|
507
|
+
});
|
|
420
508
|
}
|
|
421
509
|
async writeTextFile(filePath, content) {
|
|
422
510
|
await this.writeFile(filePath, content);
|
|
423
511
|
}
|
|
424
512
|
async listFiles(filePath = ".") {
|
|
425
|
-
this.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
513
|
+
return this.runOperation(async () => {
|
|
514
|
+
const normalized = normalizeSandboxPath(filePath, { allowRoot: true });
|
|
515
|
+
const target = containerPath(this.workdir, normalized);
|
|
516
|
+
const result = await this.execCommand({
|
|
517
|
+
command: "find",
|
|
518
|
+
args: [target, "-mindepth", "1", "-maxdepth", "1", "-printf", "%p %y %s\n"]
|
|
519
|
+
});
|
|
520
|
+
if (result.timedOut) {
|
|
521
|
+
throw new SandboxTimeoutError(`Listing files timed out for ${filePath}.`);
|
|
522
|
+
}
|
|
523
|
+
if (result.exitCode !== 0) {
|
|
524
|
+
throw new SandboxDockerCommandError(`Unable to list sandbox path: ${filePath}`, result);
|
|
525
|
+
}
|
|
526
|
+
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => this.parseFindEntry(line));
|
|
431
527
|
});
|
|
432
|
-
if (result.timedOut) {
|
|
433
|
-
throw new SandboxTimeoutError(`Listing files timed out for ${filePath}.`);
|
|
434
|
-
}
|
|
435
|
-
if (result.exitCode !== 0) {
|
|
436
|
-
throw new SandboxDockerCommandError(`Unable to list sandbox path: ${filePath}`, result);
|
|
437
|
-
}
|
|
438
|
-
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => this.parseFindEntry(line));
|
|
439
528
|
}
|
|
440
529
|
async destroy() {
|
|
441
530
|
if (this.destroyed) {
|
|
442
531
|
return;
|
|
443
532
|
}
|
|
444
533
|
this.destroyed = true;
|
|
534
|
+
this.clearLifecycleTimers();
|
|
445
535
|
await runDockerCli(["rm", "-f", this.containerName], this.cliOptions()).catch(() => void 0);
|
|
446
|
-
|
|
447
|
-
(
|
|
448
|
-
|
|
536
|
+
if (this.removeVolumeOnDestroy) {
|
|
537
|
+
await runDockerCli(["volume", "rm", "-f", this.volumeName], this.cliOptions()).catch(
|
|
538
|
+
() => void 0
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
await this.hooks.onDestroy?.(this.event());
|
|
449
542
|
}
|
|
450
543
|
async mkdir(directoryPath) {
|
|
451
544
|
const normalized = normalizeSandboxPath(directoryPath, { allowRoot: true });
|
|
452
|
-
const result = await this.
|
|
545
|
+
const result = await this.execCommand({
|
|
453
546
|
command: "mkdir",
|
|
454
547
|
args: ["-p", containerPath(this.workdir, normalized)]
|
|
455
548
|
});
|
|
@@ -480,16 +573,130 @@ var DockerSandboxSession = class {
|
|
|
480
573
|
maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes2
|
|
481
574
|
};
|
|
482
575
|
}
|
|
576
|
+
async execCommand(options) {
|
|
577
|
+
if (options.command.trim().length === 0) {
|
|
578
|
+
throw new SandboxDockerCommandError("Sandbox command cannot be empty.", {
|
|
579
|
+
stdout: "",
|
|
580
|
+
stderr: "",
|
|
581
|
+
exitCode: 1
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
const args = ["exec"];
|
|
585
|
+
if (options.input !== void 0) {
|
|
586
|
+
args.push("-i");
|
|
587
|
+
}
|
|
588
|
+
const cwd = containerPath(this.workdir, options.cwd ?? ".");
|
|
589
|
+
args.push("-w", cwd);
|
|
590
|
+
for (const [key, value] of Object.entries({ ...this.env, ...options.env })) {
|
|
591
|
+
args.push("-e", `${key}=${value}`);
|
|
592
|
+
}
|
|
593
|
+
args.push(this.containerName, options.command, ...options.args ?? []);
|
|
594
|
+
const cliOptions = {
|
|
595
|
+
dockerPath: this.dockerPath,
|
|
596
|
+
timeoutMs: options.timeoutMs ?? this.limits.timeoutMs ?? defaultTimeoutMs,
|
|
597
|
+
maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes2,
|
|
598
|
+
...options.input === void 0 ? {} : { input: options.input },
|
|
599
|
+
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
600
|
+
...options.onStdout === void 0 ? {} : { onStdout: options.onStdout },
|
|
601
|
+
...options.onStderr === void 0 ? {} : { onStderr: options.onStderr }
|
|
602
|
+
};
|
|
603
|
+
const result = await runDockerCli(args, cliOptions);
|
|
604
|
+
if (result.timedOut) {
|
|
605
|
+
return {
|
|
606
|
+
...result,
|
|
607
|
+
exitCode: result.exitCode === 0 ? 124 : result.exitCode
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
return result;
|
|
611
|
+
}
|
|
483
612
|
assertActive() {
|
|
484
613
|
if (this.destroyed) {
|
|
485
614
|
throw new SandboxSessionDestroyedError(`Sandbox session ${this.id} has been destroyed.`);
|
|
486
615
|
}
|
|
487
616
|
}
|
|
617
|
+
event() {
|
|
618
|
+
return {
|
|
619
|
+
sessionId: this.id,
|
|
620
|
+
provider: this.provider,
|
|
621
|
+
workdir: this.workdir
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
async runOperation(operation) {
|
|
625
|
+
this.assertActive();
|
|
626
|
+
this.activeOperations += 1;
|
|
627
|
+
this.clearIdleTimer();
|
|
628
|
+
try {
|
|
629
|
+
return await operation();
|
|
630
|
+
} finally {
|
|
631
|
+
this.activeOperations -= 1;
|
|
632
|
+
this.scheduleIdleTimer();
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
assertFileSize(size, filePath) {
|
|
636
|
+
if (this.limits.maxFileBytes !== void 0 && size > this.limits.maxFileBytes) {
|
|
637
|
+
throw new SandboxFileSizeError(
|
|
638
|
+
`Sandbox file exceeds maxFileBytes (${size} > ${this.limits.maxFileBytes}): ${filePath}`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
startLifecycleTimers() {
|
|
643
|
+
if (!this.lifecycle.autoDestroy) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (this.lifecycle.ttlMs !== void 0) {
|
|
647
|
+
this.ttlTimer = setTimeout(() => {
|
|
648
|
+
void this.destroy().catch(() => void 0);
|
|
649
|
+
}, this.lifecycle.ttlMs);
|
|
650
|
+
this.ttlTimer.unref?.();
|
|
651
|
+
}
|
|
652
|
+
this.scheduleIdleTimer();
|
|
653
|
+
}
|
|
654
|
+
scheduleIdleTimer() {
|
|
655
|
+
if (!this.lifecycle.autoDestroy || this.lifecycle.idleTimeoutMs === void 0) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (this.destroyed || this.activeOperations > 0) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
this.clearIdleTimer();
|
|
662
|
+
this.idleTimer = setTimeout(() => {
|
|
663
|
+
void this.destroy().catch(() => void 0);
|
|
664
|
+
}, this.lifecycle.idleTimeoutMs);
|
|
665
|
+
this.idleTimer.unref?.();
|
|
666
|
+
}
|
|
667
|
+
clearLifecycleTimers() {
|
|
668
|
+
if (this.ttlTimer !== void 0) {
|
|
669
|
+
clearTimeout(this.ttlTimer);
|
|
670
|
+
this.ttlTimer = void 0;
|
|
671
|
+
}
|
|
672
|
+
this.clearIdleTimer();
|
|
673
|
+
}
|
|
674
|
+
clearIdleTimer() {
|
|
675
|
+
if (this.idleTimer !== void 0) {
|
|
676
|
+
clearTimeout(this.idleTimer);
|
|
677
|
+
this.idleTimer = void 0;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
488
680
|
};
|
|
681
|
+
function getWorkspaceId(workspace, sessionId) {
|
|
682
|
+
if (workspace.mode === "persistent") {
|
|
683
|
+
return sanitizeResourceId(workspace.id);
|
|
684
|
+
}
|
|
685
|
+
return sessionId;
|
|
686
|
+
}
|
|
687
|
+
function shouldDestroyWorkspace(workspace) {
|
|
688
|
+
if (workspace.mode === "persistent") {
|
|
689
|
+
return workspace.destroyOnSessionDestroy ?? false;
|
|
690
|
+
}
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
489
693
|
function sanitizeResourceId(id) {
|
|
490
694
|
const sanitized = id.toLowerCase().replaceAll(/[^a-z0-9_.-]/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
491
695
|
return sanitized.length > 0 ? sanitized : randomUUID();
|
|
492
696
|
}
|
|
697
|
+
function byteLength(data) {
|
|
698
|
+
return typeof data === "string" ? Buffer.byteLength(data) : data.byteLength;
|
|
699
|
+
}
|
|
493
700
|
function mapFindType(type) {
|
|
494
701
|
if (type === "f") {
|
|
495
702
|
return "file";
|
|
@@ -527,17 +734,17 @@ var listFilesInput = z.object({
|
|
|
527
734
|
var textOutput = z.string();
|
|
528
735
|
function createSandboxTools(session, options = {}) {
|
|
529
736
|
const include = new Set(
|
|
530
|
-
options.include ?? ["exec_command", "read_file", "write_file", "list_files"]
|
|
737
|
+
options.allow ?? options.include ?? ["exec_command", "read_file", "write_file", "list_files"]
|
|
531
738
|
);
|
|
532
739
|
const tools = [];
|
|
533
740
|
if (include.has("exec_command")) {
|
|
534
741
|
tools.push(createExecCommandTool(session, options));
|
|
535
742
|
}
|
|
536
743
|
if (include.has("read_file")) {
|
|
537
|
-
tools.push(createReadFileTool(session));
|
|
744
|
+
tools.push(createReadFileTool(session, options));
|
|
538
745
|
}
|
|
539
746
|
if (include.has("write_file")) {
|
|
540
|
-
tools.push(createWriteFileTool(session));
|
|
747
|
+
tools.push(createWriteFileTool(session, options));
|
|
541
748
|
}
|
|
542
749
|
if (include.has("list_files")) {
|
|
543
750
|
tools.push(createListFilesTool(session));
|
|
@@ -545,12 +752,14 @@ function createSandboxTools(session, options = {}) {
|
|
|
545
752
|
return tools;
|
|
546
753
|
}
|
|
547
754
|
function createExecCommandTool(session, options) {
|
|
755
|
+
const policy = options.exec ?? {};
|
|
548
756
|
return createTool({
|
|
549
757
|
name: "exec_command",
|
|
550
758
|
description: "Run a command inside the sandbox workspace. Use structured args instead of shell quoting.",
|
|
551
759
|
input: execCommandInput,
|
|
552
760
|
output: textOutput,
|
|
553
761
|
execute: async ({ command, args, cwd, env, timeoutMs, input }) => {
|
|
762
|
+
assertCommandAllowed(command, options);
|
|
554
763
|
const execOptions = {
|
|
555
764
|
command
|
|
556
765
|
};
|
|
@@ -563,8 +772,9 @@ function createExecCommandTool(session, options) {
|
|
|
563
772
|
if (env !== void 0) {
|
|
564
773
|
execOptions.env = env;
|
|
565
774
|
}
|
|
566
|
-
const effectiveTimeoutMs = timeoutMs ?? options.execTimeoutMs;
|
|
775
|
+
const effectiveTimeoutMs = timeoutMs ?? policy.defaultTimeoutMs ?? options.execTimeoutMs;
|
|
567
776
|
if (effectiveTimeoutMs !== void 0) {
|
|
777
|
+
assertTimeoutAllowed(effectiveTimeoutMs, options);
|
|
568
778
|
execOptions.timeoutMs = effectiveTimeoutMs;
|
|
569
779
|
}
|
|
570
780
|
if (input !== void 0) {
|
|
@@ -575,22 +785,27 @@ function createExecCommandTool(session, options) {
|
|
|
575
785
|
}
|
|
576
786
|
});
|
|
577
787
|
}
|
|
578
|
-
function createReadFileTool(session) {
|
|
788
|
+
function createReadFileTool(session, options) {
|
|
579
789
|
return createTool({
|
|
580
790
|
name: "read_file",
|
|
581
791
|
description: "Read a text file from the sandbox workspace.",
|
|
582
792
|
input: readFileInput,
|
|
583
793
|
output: textOutput,
|
|
584
|
-
execute: async ({ path: path3 }) =>
|
|
794
|
+
execute: async ({ path: path3 }) => {
|
|
795
|
+
const content = await session.readTextFile(path3);
|
|
796
|
+
assertReadAllowed(content, options);
|
|
797
|
+
return content;
|
|
798
|
+
}
|
|
585
799
|
});
|
|
586
800
|
}
|
|
587
|
-
function createWriteFileTool(session) {
|
|
801
|
+
function createWriteFileTool(session, options) {
|
|
588
802
|
return createTool({
|
|
589
803
|
name: "write_file",
|
|
590
804
|
description: "Write a text file inside the sandbox workspace. Creates parent directories.",
|
|
591
805
|
input: writeFileInput,
|
|
592
806
|
output: textOutput,
|
|
593
807
|
execute: async ({ path: path3, content }) => {
|
|
808
|
+
assertContentAllowed(content, options);
|
|
594
809
|
await session.writeTextFile(path3, content);
|
|
595
810
|
return `Wrote ${path3}`;
|
|
596
811
|
}
|
|
@@ -635,14 +850,45 @@ ${result.stderr.trimEnd()}`);
|
|
|
635
850
|
}
|
|
636
851
|
return parts.join("\n\n");
|
|
637
852
|
}
|
|
853
|
+
function assertCommandAllowed(command, options) {
|
|
854
|
+
const policy = options.exec;
|
|
855
|
+
if (policy?.blockedCommands?.includes(command)) {
|
|
856
|
+
throw new SandboxToolPolicyError(`Command is blocked by sandbox tool policy: ${command}`);
|
|
857
|
+
}
|
|
858
|
+
if (policy?.allowedCommands !== void 0 && !policy.allowedCommands.includes(command)) {
|
|
859
|
+
throw new SandboxToolPolicyError(`Command is not allowed by sandbox tool policy: ${command}`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
function assertTimeoutAllowed(timeoutMs, options) {
|
|
863
|
+
const maxTimeoutMs = options.exec?.maxTimeoutMs;
|
|
864
|
+
if (maxTimeoutMs !== void 0 && timeoutMs > maxTimeoutMs) {
|
|
865
|
+
throw new SandboxToolPolicyError(
|
|
866
|
+
`Command timeout exceeds sandbox tool policy (${timeoutMs} > ${maxTimeoutMs}).`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function assertContentAllowed(content, options) {
|
|
871
|
+
const maxBytes = options.writeFile?.maxBytes;
|
|
872
|
+
if (maxBytes !== void 0 && Buffer.byteLength(content) > maxBytes) {
|
|
873
|
+
throw new SandboxToolPolicyError("File content exceeds sandbox tool policy.");
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function assertReadAllowed(content, options) {
|
|
877
|
+
const maxBytes = options.readFile?.maxBytes;
|
|
878
|
+
if (maxBytes !== void 0 && Buffer.byteLength(content) > maxBytes) {
|
|
879
|
+
throw new SandboxToolPolicyError("File content exceeds sandbox tool policy.");
|
|
880
|
+
}
|
|
881
|
+
}
|
|
638
882
|
export {
|
|
639
883
|
DockerSandbox,
|
|
640
884
|
SandboxDockerCommandError,
|
|
641
885
|
SandboxDockerUnavailableError,
|
|
642
886
|
SandboxError,
|
|
887
|
+
SandboxFileSizeError,
|
|
643
888
|
SandboxPathError,
|
|
644
889
|
SandboxSessionDestroyedError,
|
|
645
890
|
SandboxTimeoutError,
|
|
891
|
+
SandboxToolPolicyError,
|
|
646
892
|
createSandboxTools
|
|
647
893
|
};
|
|
648
894
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/docker-sandbox.ts","../src/docker-cli.ts","../src/errors.ts","../src/path.ts","../src/tools.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { assertDockerCli, runDockerCli } from \"./docker-cli\";\nimport {\n SandboxDockerCommandError,\n SandboxSessionDestroyedError,\n SandboxTimeoutError,\n} from \"./errors\";\nimport { containerPath, normalizeSandboxPath, parentSandboxPath } from \"./path\";\nimport type {\n DockerSandboxOptions,\n Sandbox,\n SandboxCreateSessionOptions,\n SandboxExecOptions,\n SandboxExecResult,\n SandboxFileEntry,\n SandboxFileType,\n SandboxLimits,\n SandboxManifest,\n SandboxNetworkMode,\n SandboxSession,\n} from \"./types\";\n\nconst defaultImage = \"node:22-bookworm\";\nconst defaultWorkdir = \"/workspace\";\nconst defaultTimeoutMs = 30_000;\nconst defaultMaxOutputBytes = 1024 * 1024;\n\nexport class DockerSandbox implements Sandbox {\n readonly provider = \"docker\";\n\n private readonly image: string;\n private readonly pull: \"missing\" | \"always\" | \"never\";\n private readonly workdir: string;\n private readonly network: SandboxNetworkMode;\n private readonly dockerPath: string;\n private readonly labels: Record<string, string>;\n private readonly limits: SandboxLimits;\n private readonly security: Required<NonNullable<DockerSandboxOptions[\"security\"]>>;\n private readonly user: string | undefined;\n\n constructor(options: DockerSandboxOptions = {}) {\n this.image = options.image ?? defaultImage;\n this.pull = options.pull ?? \"missing\";\n this.workdir = options.workdir ?? defaultWorkdir;\n this.network = options.network ?? false;\n this.dockerPath = options.dockerPath ?? \"docker\";\n this.labels = options.labels ?? {};\n this.limits = options.limits ?? {};\n this.security = {\n readonlyRootfs: options.security?.readonlyRootfs ?? false,\n noNewPrivileges: options.security?.noNewPrivileges ?? true,\n dropCapabilities: options.security?.dropCapabilities ?? [\"ALL\"],\n };\n this.user = options.user;\n }\n\n async createSession(options: SandboxCreateSessionOptions = {}): Promise<SandboxSession> {\n await this.ensureImage();\n\n const id = sanitizeResourceId(options.id ?? randomUUID());\n const containerName = `anvia-sandbox-${id}`;\n const volumeName = `anvia-sandbox-${id}-workspace`;\n\n await assertDockerCli([\"volume\", \"create\", volumeName], this.cliOptions());\n\n try {\n await assertDockerCli(this.createRunArgs(containerName, volumeName, options.metadata), {\n ...this.cliOptions(),\n timeoutMs: this.limits.timeoutMs ?? defaultTimeoutMs,\n });\n\n const session = new DockerSandboxSession({\n id,\n containerName,\n volumeName,\n workdir: this.workdir,\n dockerPath: this.dockerPath,\n limits: this.limits,\n env: options.manifest?.env ?? {},\n });\n\n await session.applyManifest(options.manifest);\n return session;\n } catch (error) {\n await this.cleanup(containerName, volumeName);\n throw error;\n }\n }\n\n private async ensureImage(): Promise<void> {\n if (this.pull === \"always\") {\n await assertDockerCli([\"pull\", this.image], this.cliOptions());\n return;\n }\n\n if (this.pull === \"missing\") {\n const inspect = await runDockerCli([\"image\", \"inspect\", this.image], this.cliOptions());\n if (inspect.exitCode !== 0) {\n await assertDockerCli([\"pull\", this.image], this.cliOptions());\n }\n }\n }\n\n private createRunArgs(\n containerName: string,\n volumeName: string,\n metadata: Record<string, string> | undefined,\n ): string[] {\n const args = [\n \"run\",\n \"-d\",\n \"--name\",\n containerName,\n \"-v\",\n `${volumeName}:${this.workdir}`,\n \"-w\",\n this.workdir,\n \"--label\",\n \"anvia.sandbox=true\",\n ];\n\n for (const [key, value] of Object.entries(this.labels)) {\n args.push(\"--label\", `${key}=${value}`);\n }\n\n if (metadata !== undefined) {\n for (const [key, value] of Object.entries(metadata)) {\n args.push(\"--label\", `anvia.sandbox.metadata.${key}=${value}`);\n }\n }\n\n this.appendNetworkArgs(args);\n this.appendLimitArgs(args);\n this.appendSecurityArgs(args);\n\n if (this.user !== undefined) {\n args.push(\"-u\", this.user);\n }\n\n args.push(\n this.image,\n \"sh\",\n \"-c\",\n \"trap 'exit 0' TERM INT; while :; do sleep 3600 & wait $!; done\",\n );\n return args;\n }\n\n private appendNetworkArgs(args: string[]): void {\n if (this.network === false || this.network === \"none\") {\n args.push(\"--network\", \"none\");\n return;\n }\n\n if (this.network !== true) {\n args.push(\"--network\", this.network);\n }\n }\n\n private appendLimitArgs(args: string[]): void {\n if (this.limits.memoryMb !== undefined) {\n args.push(\"--memory\", `${this.limits.memoryMb}m`);\n }\n\n if (this.limits.cpus !== undefined) {\n args.push(\"--cpus\", String(this.limits.cpus));\n }\n\n if (this.limits.pidsLimit !== undefined) {\n args.push(\"--pids-limit\", String(this.limits.pidsLimit));\n }\n }\n\n private appendSecurityArgs(args: string[]): void {\n if (this.security.readonlyRootfs) {\n args.push(\"--read-only\");\n }\n\n if (this.security.noNewPrivileges) {\n args.push(\"--security-opt\", \"no-new-privileges\");\n }\n\n for (const capability of this.security.dropCapabilities) {\n args.push(\"--cap-drop\", capability);\n }\n }\n\n private cliOptions() {\n return {\n dockerPath: this.dockerPath,\n maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes,\n };\n }\n\n private async cleanup(containerName: string, volumeName: string): Promise<void> {\n await runDockerCli([\"rm\", \"-f\", containerName], this.cliOptions()).catch(() => undefined);\n await runDockerCli([\"volume\", \"rm\", \"-f\", volumeName], this.cliOptions()).catch(\n () => undefined,\n );\n }\n}\n\nclass DockerSandboxSession implements SandboxSession {\n readonly provider = \"docker\";\n readonly id: string;\n readonly workdir: string;\n\n private readonly containerName: string;\n private readonly volumeName: string;\n private readonly dockerPath: string;\n private readonly limits: SandboxLimits;\n private readonly env: Record<string, string>;\n private destroyed = false;\n\n constructor(options: {\n id: string;\n containerName: string;\n volumeName: string;\n workdir: string;\n dockerPath: string;\n limits: SandboxLimits;\n env: Record<string, string>;\n }) {\n this.id = options.id;\n this.containerName = options.containerName;\n this.volumeName = options.volumeName;\n this.workdir = options.workdir;\n this.dockerPath = options.dockerPath;\n this.limits = options.limits;\n this.env = options.env;\n }\n\n async applyManifest(manifest: SandboxManifest | undefined): Promise<void> {\n this.assertActive();\n\n for (const directory of manifest?.directories ?? []) {\n await this.mkdir(directory);\n }\n\n for (const [filePath, content] of Object.entries(manifest?.files ?? {})) {\n await this.writeFile(filePath, content);\n }\n }\n\n async exec(options: SandboxExecOptions): Promise<SandboxExecResult> {\n this.assertActive();\n\n if (options.command.trim().length === 0) {\n throw new SandboxDockerCommandError(\"Sandbox command cannot be empty.\", {\n stdout: \"\",\n stderr: \"\",\n exitCode: 1,\n });\n }\n\n const args = [\"exec\"];\n\n if (options.input !== undefined) {\n args.push(\"-i\");\n }\n\n const cwd = containerPath(this.workdir, options.cwd ?? \".\");\n args.push(\"-w\", cwd);\n\n for (const [key, value] of Object.entries({ ...this.env, ...options.env })) {\n args.push(\"-e\", `${key}=${value}`);\n }\n\n args.push(this.containerName, options.command, ...(options.args ?? []));\n\n const cliOptions = {\n dockerPath: this.dockerPath,\n timeoutMs: options.timeoutMs ?? this.limits.timeoutMs ?? defaultTimeoutMs,\n maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes,\n ...(options.input === undefined ? {} : { input: options.input }),\n ...(options.signal === undefined ? {} : { signal: options.signal }),\n ...(options.onStdout === undefined ? {} : { onStdout: options.onStdout }),\n ...(options.onStderr === undefined ? {} : { onStderr: options.onStderr }),\n };\n\n const result = await runDockerCli(args, cliOptions);\n\n if (result.timedOut) {\n return {\n ...result,\n exitCode: result.exitCode === 0 ? 124 : result.exitCode,\n };\n }\n\n return result;\n }\n\n async readFile(filePath: string): Promise<Uint8Array> {\n this.assertActive();\n const normalized = normalizeSandboxPath(filePath);\n const tempDir = await mkdtemp(path.join(os.tmpdir(), \"anvia-sandbox-read-\"));\n const target = path.join(tempDir, path.basename(normalized));\n\n try {\n await assertDockerCli(\n [\"cp\", `${this.containerName}:${containerPath(this.workdir, normalized)}`, target],\n this.cliOptions(),\n );\n const { readFile } = await import(\"node:fs/promises\");\n return await readFile(target);\n } finally {\n await rm(tempDir, { recursive: true, force: true });\n }\n }\n\n async readTextFile(filePath: string): Promise<string> {\n const bytes = await this.readFile(filePath);\n return new TextDecoder().decode(bytes);\n }\n\n async writeFile(filePath: string, data: string | Uint8Array): Promise<void> {\n this.assertActive();\n const normalized = normalizeSandboxPath(filePath);\n await this.mkdir(parentSandboxPath(normalized));\n\n const tempDir = await mkdtemp(path.join(os.tmpdir(), \"anvia-sandbox-write-\"));\n const source = path.join(tempDir, path.basename(normalized));\n\n try {\n await writeFile(source, data);\n await assertDockerCli(\n [\"cp\", source, `${this.containerName}:${containerPath(this.workdir, normalized)}`],\n this.cliOptions(),\n );\n } finally {\n await rm(tempDir, { recursive: true, force: true });\n }\n }\n\n async writeTextFile(filePath: string, content: string): Promise<void> {\n await this.writeFile(filePath, content);\n }\n\n async listFiles(filePath = \".\"): Promise<SandboxFileEntry[]> {\n this.assertActive();\n const normalized = normalizeSandboxPath(filePath, { allowRoot: true });\n const target = containerPath(this.workdir, normalized);\n const result = await this.exec({\n command: \"find\",\n args: [target, \"-mindepth\", \"1\", \"-maxdepth\", \"1\", \"-printf\", \"%p\\t%y\\t%s\\n\"],\n });\n\n if (result.timedOut) {\n throw new SandboxTimeoutError(`Listing files timed out for ${filePath}.`);\n }\n\n if (result.exitCode !== 0) {\n throw new SandboxDockerCommandError(`Unable to list sandbox path: ${filePath}`, result);\n }\n\n return result.stdout\n .split(\"\\n\")\n .filter((line) => line.trim().length > 0)\n .map((line) => this.parseFindEntry(line));\n }\n\n async destroy(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n\n this.destroyed = true;\n await runDockerCli([\"rm\", \"-f\", this.containerName], this.cliOptions()).catch(() => undefined);\n await runDockerCli([\"volume\", \"rm\", \"-f\", this.volumeName], this.cliOptions()).catch(\n () => undefined,\n );\n }\n\n private async mkdir(directoryPath: string): Promise<void> {\n const normalized = normalizeSandboxPath(directoryPath, { allowRoot: true });\n const result = await this.exec({\n command: \"mkdir\",\n args: [\"-p\", containerPath(this.workdir, normalized)],\n });\n\n if (result.exitCode !== 0) {\n throw new SandboxDockerCommandError(\n `Unable to create sandbox directory: ${directoryPath}`,\n result,\n );\n }\n }\n\n private parseFindEntry(line: string): SandboxFileEntry {\n const [rawPath, rawType, rawSize] = line.split(\"\\t\");\n const absolutePath = rawPath ?? \"\";\n const relativePath = absolutePath.startsWith(`${this.workdir}/`)\n ? absolutePath.slice(this.workdir.length + 1)\n : absolutePath;\n const size = rawSize === undefined ? undefined : Number(rawSize);\n const entry: SandboxFileEntry = {\n path: relativePath,\n type: mapFindType(rawType),\n };\n\n if (size !== undefined && Number.isFinite(size)) {\n entry.size = size;\n }\n\n return entry;\n }\n\n private cliOptions() {\n return {\n dockerPath: this.dockerPath,\n maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes,\n };\n }\n\n private assertActive(): void {\n if (this.destroyed) {\n throw new SandboxSessionDestroyedError(`Sandbox session ${this.id} has been destroyed.`);\n }\n }\n}\n\nfunction sanitizeResourceId(id: string): string {\n const sanitized = id\n .toLowerCase()\n .replaceAll(/[^a-z0-9_.-]/g, \"-\")\n .replaceAll(/^-+|-+$/g, \"\");\n return sanitized.length > 0 ? sanitized : randomUUID();\n}\n\nfunction mapFindType(type: string | undefined): SandboxFileType {\n if (type === \"f\") {\n return \"file\";\n }\n if (type === \"d\") {\n return \"directory\";\n }\n if (type === \"l\") {\n return \"symlink\";\n }\n return \"other\";\n}\n","import { spawn } from \"node:child_process\";\nimport { SandboxDockerCommandError, SandboxDockerUnavailableError } from \"./errors\";\n\nexport interface DockerCliResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n durationMs: number;\n timedOut: boolean;\n aborted: boolean;\n stdoutTruncated: boolean;\n stderrTruncated: boolean;\n}\n\nexport interface DockerCliOptions {\n dockerPath: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n input?: string | Uint8Array;\n signal?: AbortSignal;\n onStdout?: (chunk: Uint8Array) => void;\n onStderr?: (chunk: Uint8Array) => void;\n}\n\nconst defaultMaxOutputBytes = 1024 * 1024;\n\nexport async function runDockerCli(\n args: string[],\n options: DockerCliOptions,\n): Promise<DockerCliResult> {\n const startedAt = Date.now();\n const maxOutputBytes = options.maxOutputBytes ?? defaultMaxOutputBytes;\n const stdout = createOutputCollector(maxOutputBytes, options.onStdout);\n const stderr = createOutputCollector(maxOutputBytes, options.onStderr);\n\n return new Promise((resolve, reject) => {\n const child = spawn(options.dockerPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let timedOut = false;\n let aborted = false;\n let settled = false;\n\n const timeout =\n options.timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGKILL\");\n }, options.timeoutMs);\n\n const abort = () => {\n aborted = true;\n child.kill(\"SIGKILL\");\n };\n\n if (options.signal?.aborted === true) {\n abort();\n } else {\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n }\n\n child.stdout.on(\"data\", (chunk: Buffer) => stdout.accept(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => stderr.accept(chunk));\n\n child.on(\"error\", (error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", abort);\n\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n reject(new SandboxDockerUnavailableError(\"Docker CLI was not found.\", error));\n return;\n }\n\n reject(error);\n });\n\n child.on(\"close\", (code) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", abort);\n\n resolve({\n stdout: stdout.text(),\n stderr: stderr.text(),\n exitCode: code ?? 1,\n durationMs: Date.now() - startedAt,\n timedOut,\n aborted,\n stdoutTruncated: stdout.truncated,\n stderrTruncated: stderr.truncated,\n });\n });\n\n if (options.input !== undefined) {\n child.stdin.end(options.input);\n } else {\n child.stdin.end();\n }\n });\n}\n\nexport async function assertDockerCli(args: string[], options: DockerCliOptions): Promise<string> {\n const result = await runDockerCli(args, options);\n\n if (result.exitCode !== 0) {\n throw new SandboxDockerCommandError(`Docker command failed: docker ${args.join(\" \")}`, result);\n }\n\n return result.stdout.trim();\n}\n\nfunction createOutputCollector(maxBytes: number, onChunk?: (chunk: Uint8Array) => void) {\n const chunks: Buffer[] = [];\n let length = 0;\n let truncated = false;\n\n return {\n get truncated() {\n return truncated;\n },\n accept(chunk: Buffer) {\n onChunk?.(chunk);\n\n if (length >= maxBytes) {\n truncated = true;\n return;\n }\n\n const remaining = maxBytes - length;\n const next = chunk.length > remaining ? chunk.subarray(0, remaining) : chunk;\n chunks.push(next);\n length += next.length;\n\n if (next.length < chunk.length) {\n truncated = true;\n }\n },\n text() {\n return Buffer.concat(chunks, length).toString(\"utf8\");\n },\n };\n}\n","export class SandboxError extends Error {\n constructor(\n message: string,\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class SandboxDockerUnavailableError extends SandboxError {}\n\nexport class SandboxDockerCommandError extends SandboxError {\n constructor(\n message: string,\n readonly result: {\n stdout: string;\n stderr: string;\n exitCode: number;\n },\n ) {\n super(message);\n }\n}\n\nexport class SandboxSessionDestroyedError extends SandboxError {}\n\nexport class SandboxPathError extends SandboxError {}\n\nexport class SandboxTimeoutError extends SandboxError {}\n","import path from \"node:path\";\nimport { SandboxPathError } from \"./errors\";\n\nexport function normalizeSandboxPath(input: string, options: { allowRoot?: boolean } = {}): string {\n if (input.length === 0) {\n throw new SandboxPathError(\"Sandbox path cannot be empty.\");\n }\n\n if (input.includes(\"\\0\")) {\n throw new SandboxPathError(\"Sandbox path cannot contain null bytes.\");\n }\n\n const normalized = path.posix.normalize(input.replaceAll(\"\\\\\", \"/\"));\n\n if (path.posix.isAbsolute(normalized)) {\n throw new SandboxPathError(`Sandbox path must be relative: ${input}`);\n }\n\n if (normalized === \"..\" || normalized.startsWith(\"../\")) {\n throw new SandboxPathError(`Sandbox path cannot leave the workspace: ${input}`);\n }\n\n if (normalized === \".\" && options.allowRoot !== true) {\n throw new SandboxPathError(\n \"Sandbox path must refer to a file or directory inside the workspace.\",\n );\n }\n\n return normalized;\n}\n\nexport function containerPath(workdir: string, relativePath: string): string {\n const normalizedWorkdir = path.posix.normalize(workdir);\n const normalizedPath = normalizeSandboxPath(relativePath, { allowRoot: true });\n return normalizedPath === \".\"\n ? normalizedWorkdir\n : path.posix.join(normalizedWorkdir, normalizedPath);\n}\n\nexport function parentSandboxPath(relativePath: string): string {\n const normalized = normalizeSandboxPath(relativePath);\n const parent = path.posix.dirname(normalized);\n return parent === \".\" ? \".\" : parent;\n}\n","import { type AnyTool, createTool } from \"@anvia/core/tool\";\nimport { z } from \"zod\";\nimport type {\n SandboxExecOptions,\n SandboxExecResult,\n SandboxSession,\n SandboxToolName,\n SandboxToolsOptions,\n} from \"./types\";\n\nconst execCommandInput = z.object({\n command: z.string().min(1).describe(\"Executable to run inside the sandbox workspace.\"),\n args: z.array(z.string()).optional().describe(\"Command arguments.\"),\n cwd: z.string().optional().describe(\"Relative working directory inside the sandbox.\"),\n env: z\n .record(z.string(), z.string())\n .optional()\n .describe(\"Environment variables for this command.\"),\n timeoutMs: z\n .number()\n .int()\n .positive()\n .max(300_000)\n .optional()\n .describe(\"Optional command timeout in milliseconds.\"),\n input: z.string().optional().describe(\"Optional stdin text to pass to the command.\"),\n});\n\nconst readFileInput = z.object({\n path: z.string().min(1).describe(\"Relative file path inside the sandbox.\"),\n});\n\nconst writeFileInput = z.object({\n path: z.string().min(1).describe(\"Relative file path inside the sandbox.\"),\n content: z.string().describe(\"Complete text content to write.\"),\n});\n\nconst listFilesInput = z.object({\n path: z\n .string()\n .optional()\n .describe(\"Relative directory path inside the sandbox. Defaults to root.\"),\n});\n\nconst textOutput = z.string();\n\nexport function createSandboxTools(\n session: SandboxSession,\n options: SandboxToolsOptions = {},\n): AnyTool[] {\n const include = new Set<SandboxToolName>(\n options.include ?? [\"exec_command\", \"read_file\", \"write_file\", \"list_files\"],\n );\n const tools: AnyTool[] = [];\n\n if (include.has(\"exec_command\")) {\n tools.push(createExecCommandTool(session, options));\n }\n\n if (include.has(\"read_file\")) {\n tools.push(createReadFileTool(session));\n }\n\n if (include.has(\"write_file\")) {\n tools.push(createWriteFileTool(session));\n }\n\n if (include.has(\"list_files\")) {\n tools.push(createListFilesTool(session));\n }\n\n return tools;\n}\n\nfunction createExecCommandTool(session: SandboxSession, options: SandboxToolsOptions): AnyTool {\n return createTool({\n name: \"exec_command\",\n description:\n \"Run a command inside the sandbox workspace. Use structured args instead of shell quoting.\",\n input: execCommandInput,\n output: textOutput,\n execute: async ({ command, args, cwd, env, timeoutMs, input }) => {\n const execOptions: SandboxExecOptions = {\n command,\n };\n\n if (args !== undefined) {\n execOptions.args = args;\n }\n if (cwd !== undefined) {\n execOptions.cwd = cwd;\n }\n if (env !== undefined) {\n execOptions.env = env;\n }\n const effectiveTimeoutMs = timeoutMs ?? options.execTimeoutMs;\n if (effectiveTimeoutMs !== undefined) {\n execOptions.timeoutMs = effectiveTimeoutMs;\n }\n if (input !== undefined) {\n execOptions.input = input;\n }\n\n const result = await session.exec(execOptions);\n\n return formatExecResult(result);\n },\n });\n}\n\nfunction createReadFileTool(session: SandboxSession): AnyTool {\n return createTool({\n name: \"read_file\",\n description: \"Read a text file from the sandbox workspace.\",\n input: readFileInput,\n output: textOutput,\n execute: async ({ path }) => session.readTextFile(path),\n });\n}\n\nfunction createWriteFileTool(session: SandboxSession): AnyTool {\n return createTool({\n name: \"write_file\",\n description: \"Write a text file inside the sandbox workspace. Creates parent directories.\",\n input: writeFileInput,\n output: textOutput,\n execute: async ({ path, content }) => {\n await session.writeTextFile(path, content);\n return `Wrote ${path}`;\n },\n });\n}\n\nfunction createListFilesTool(session: SandboxSession): AnyTool {\n return createTool({\n name: \"list_files\",\n description: \"List files and directories inside the sandbox workspace.\",\n input: listFilesInput,\n output: textOutput,\n execute: async ({ path }) => {\n const entries = await session.listFiles(path);\n\n if (entries.length === 0) {\n return \"No files found.\";\n }\n\n return entries\n .map((entry) => {\n const size = entry.size === undefined ? \"\" : ` ${entry.size}b`;\n return `${entry.type}${size}\\t${entry.path}`;\n })\n .join(\"\\n\");\n },\n });\n}\n\nfunction formatExecResult(result: SandboxExecResult): string {\n const parts = [`exit_code: ${result.exitCode}`];\n\n if (result.timedOut) {\n parts.push(\"timed_out: true\");\n }\n\n if (result.aborted) {\n parts.push(\"aborted: true\");\n }\n\n if (result.stdout.length > 0) {\n parts.push(`stdout:\\n${result.stdout.trimEnd()}`);\n }\n\n if (result.stderr.length > 0) {\n parts.push(`stderr:\\n${result.stderr.trimEnd()}`);\n }\n\n if (result.stdoutTruncated || result.stderrTruncated) {\n parts.push(\"output_truncated: true\");\n }\n\n return parts.join(\"\\n\\n\");\n}\n"],"mappings":"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","names":["path","defaultMaxOutputBytes","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/docker-sandbox.ts","../src/docker-cli.ts","../src/errors.ts","../src/path.ts","../src/tools.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { assertDockerCli, runDockerCli } from \"./docker-cli\";\nimport {\n SandboxDockerCommandError,\n SandboxFileSizeError,\n SandboxSessionDestroyedError,\n SandboxTimeoutError,\n} from \"./errors\";\nimport { containerPath, normalizeSandboxPath, parentSandboxPath } from \"./path\";\nimport type {\n DockerSandboxOptions,\n Sandbox,\n SandboxCreateSessionOptions,\n SandboxExecOptions,\n SandboxExecResult,\n SandboxExecStreamEvent,\n SandboxFileEntry,\n SandboxFileType,\n SandboxHooks,\n SandboxLifecycleOptions,\n SandboxLimits,\n SandboxManifest,\n SandboxSession,\n SandboxSessionEvent,\n SandboxWorkspaceOptions,\n} from \"./types\";\n\nconst defaultImage = \"node:22-bookworm\";\nconst defaultWorkdir = \"/workspace\";\nconst defaultTimeoutMs = 30_000;\nconst defaultMaxOutputBytes = 1024 * 1024;\n\nexport class DockerSandbox implements Sandbox {\n readonly provider = \"docker\";\n\n private readonly image: string;\n private readonly pull: \"missing\" | \"always\" | \"never\";\n private readonly workdir: string;\n private readonly workspace: SandboxWorkspaceOptions;\n private readonly lifecycle: Required<Pick<SandboxLifecycleOptions, \"autoDestroy\">> &\n Omit<SandboxLifecycleOptions, \"autoDestroy\">;\n private readonly network: NonNullable<DockerSandboxOptions[\"network\"]>;\n private readonly dockerPath: string;\n private readonly labels: Record<string, string>;\n private readonly limits: SandboxLimits;\n private readonly security: Required<NonNullable<DockerSandboxOptions[\"security\"]>>;\n private readonly hooks: SandboxHooks;\n private readonly user: string | undefined;\n\n constructor(options: DockerSandboxOptions = {}) {\n this.image = options.image ?? defaultImage;\n this.pull = options.pull ?? \"missing\";\n this.workdir = options.workdir ?? defaultWorkdir;\n this.workspace = options.workspace ?? { mode: \"ephemeral\" };\n this.lifecycle = {\n autoDestroy: options.lifecycle?.autoDestroy ?? true,\n ...(options.lifecycle?.ttlMs === undefined ? {} : { ttlMs: options.lifecycle.ttlMs }),\n ...(options.lifecycle?.idleTimeoutMs === undefined\n ? {}\n : { idleTimeoutMs: options.lifecycle.idleTimeoutMs }),\n };\n this.network = options.network ?? false;\n this.dockerPath = options.dockerPath ?? \"docker\";\n this.labels = options.labels ?? {};\n this.limits = options.limits ?? {};\n this.security = {\n readonlyRootfs: options.security?.readonlyRootfs ?? false,\n noNewPrivileges: options.security?.noNewPrivileges ?? true,\n dropCapabilities: options.security?.dropCapabilities ?? [\"ALL\"],\n };\n this.hooks = options.hooks ?? {};\n this.user = options.user;\n }\n\n static node(options: DockerSandboxOptions = {}): DockerSandbox {\n return new DockerSandbox({ ...options, image: options.image ?? \"node:22-bookworm\" });\n }\n\n static python(options: DockerSandboxOptions = {}): DockerSandbox {\n return new DockerSandbox({ ...options, image: options.image ?? \"python:3.13-bookworm\" });\n }\n\n static deno(options: DockerSandboxOptions = {}): DockerSandbox {\n return new DockerSandbox({ ...options, image: options.image ?? \"denoland/deno:debian\" });\n }\n\n async createSession(options: SandboxCreateSessionOptions = {}): Promise<SandboxSession> {\n await this.ensureImage();\n\n const id = sanitizeResourceId(options.id ?? randomUUID());\n const workspace = options.workspace ?? this.workspace;\n const workspaceId = getWorkspaceId(workspace, id);\n const containerName = `anvia-sandbox-${id}`;\n const volumeName = `anvia-sandbox-${workspaceId}-workspace`;\n const removeVolumeOnDestroy = shouldDestroyWorkspace(workspace);\n\n await assertDockerCli([\"volume\", \"create\", volumeName], this.cliOptions());\n\n try {\n await assertDockerCli(\n this.createRunArgs(containerName, volumeName, workspace, options.metadata),\n {\n ...this.cliOptions(),\n timeoutMs: this.limits.timeoutMs ?? defaultTimeoutMs,\n },\n );\n\n const session = new DockerSandboxSession({\n id,\n containerName,\n volumeName,\n workdir: this.workdir,\n dockerPath: this.dockerPath,\n limits: this.limits,\n lifecycle: this.lifecycle,\n removeVolumeOnDestroy,\n env: options.manifest?.env ?? {},\n hooks: this.hooks,\n });\n\n await session.applyManifest(options.manifest);\n await this.hooks.onSessionCreate?.(session.event());\n return session;\n } catch (error) {\n await this.cleanup(containerName, removeVolumeOnDestroy ? volumeName : undefined);\n throw error;\n }\n }\n\n private async ensureImage(): Promise<void> {\n if (this.pull === \"always\") {\n await assertDockerCli([\"pull\", this.image], this.cliOptions());\n return;\n }\n\n if (this.pull === \"missing\") {\n const inspect = await runDockerCli([\"image\", \"inspect\", this.image], this.cliOptions());\n if (inspect.exitCode !== 0) {\n await assertDockerCli([\"pull\", this.image], this.cliOptions());\n }\n }\n }\n\n private createRunArgs(\n containerName: string,\n volumeName: string,\n workspace: SandboxWorkspaceOptions,\n metadata: Record<string, string> | undefined,\n ): string[] {\n const args = [\n \"run\",\n \"-d\",\n \"--name\",\n containerName,\n \"-v\",\n `${volumeName}:${this.workdir}`,\n \"-w\",\n this.workdir,\n \"--label\",\n \"anvia.sandbox=true\",\n \"--label\",\n `anvia.sandbox.workspace.mode=${workspace.mode ?? \"ephemeral\"}`,\n \"--label\",\n `anvia.sandbox.workspace.volume=${volumeName}`,\n ];\n\n for (const [key, value] of Object.entries(this.labels)) {\n args.push(\"--label\", `${key}=${value}`);\n }\n\n if (metadata !== undefined) {\n for (const [key, value] of Object.entries(metadata)) {\n args.push(\"--label\", `anvia.sandbox.metadata.${key}=${value}`);\n }\n }\n\n this.appendNetworkArgs(args);\n this.appendLimitArgs(args);\n this.appendSecurityArgs(args);\n\n if (this.user !== undefined) {\n args.push(\"-u\", this.user);\n }\n\n args.push(\n this.image,\n \"sh\",\n \"-c\",\n \"trap 'exit 0' TERM INT; while :; do sleep 3600 & wait $!; done\",\n );\n return args;\n }\n\n private appendNetworkArgs(args: string[]): void {\n const mode = typeof this.network === \"object\" ? this.network.mode : this.network;\n\n if (mode === false || mode === \"none\") {\n args.push(\"--network\", \"none\");\n return;\n }\n\n if (mode !== true) {\n args.push(\"--network\", mode);\n }\n }\n\n private appendLimitArgs(args: string[]): void {\n if (this.limits.memoryMb !== undefined) {\n args.push(\"--memory\", `${this.limits.memoryMb}m`);\n }\n\n if (this.limits.cpus !== undefined) {\n args.push(\"--cpus\", String(this.limits.cpus));\n }\n\n if (this.limits.pidsLimit !== undefined) {\n args.push(\"--pids-limit\", String(this.limits.pidsLimit));\n }\n }\n\n private appendSecurityArgs(args: string[]): void {\n if (this.security.readonlyRootfs) {\n args.push(\"--read-only\");\n }\n\n if (this.security.noNewPrivileges) {\n args.push(\"--security-opt\", \"no-new-privileges\");\n }\n\n for (const capability of this.security.dropCapabilities) {\n args.push(\"--cap-drop\", capability);\n }\n }\n\n private cliOptions() {\n return {\n dockerPath: this.dockerPath,\n maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes,\n };\n }\n\n private async cleanup(containerName: string, volumeName: string | undefined): Promise<void> {\n await runDockerCli([\"rm\", \"-f\", containerName], this.cliOptions()).catch(() => undefined);\n\n if (volumeName !== undefined) {\n await runDockerCli([\"volume\", \"rm\", \"-f\", volumeName], this.cliOptions()).catch(\n () => undefined,\n );\n }\n }\n}\n\nclass DockerSandboxSession implements SandboxSession {\n readonly provider = \"docker\";\n readonly id: string;\n readonly workdir: string;\n\n private readonly containerName: string;\n private readonly volumeName: string;\n private readonly dockerPath: string;\n private readonly limits: SandboxLimits;\n private readonly lifecycle: Required<Pick<SandboxLifecycleOptions, \"autoDestroy\">> &\n Omit<SandboxLifecycleOptions, \"autoDestroy\">;\n private readonly removeVolumeOnDestroy: boolean;\n private readonly env: Record<string, string>;\n private readonly hooks: SandboxHooks;\n private ttlTimer: ReturnType<typeof setTimeout> | undefined;\n private idleTimer: ReturnType<typeof setTimeout> | undefined;\n private activeOperations = 0;\n private destroyed = false;\n\n constructor(options: {\n id: string;\n containerName: string;\n volumeName: string;\n workdir: string;\n dockerPath: string;\n limits: SandboxLimits;\n lifecycle: Required<Pick<SandboxLifecycleOptions, \"autoDestroy\">> &\n Omit<SandboxLifecycleOptions, \"autoDestroy\">;\n removeVolumeOnDestroy: boolean;\n env: Record<string, string>;\n hooks: SandboxHooks;\n }) {\n this.id = options.id;\n this.containerName = options.containerName;\n this.volumeName = options.volumeName;\n this.workdir = options.workdir;\n this.dockerPath = options.dockerPath;\n this.limits = options.limits;\n this.lifecycle = options.lifecycle;\n this.removeVolumeOnDestroy = options.removeVolumeOnDestroy;\n this.env = options.env;\n this.hooks = options.hooks;\n this.startLifecycleTimers();\n }\n\n async applyManifest(manifest: SandboxManifest | undefined): Promise<void> {\n await this.runOperation(async () => {\n for (const directory of manifest?.directories ?? []) {\n await this.mkdir(directory);\n }\n\n for (const [filePath, content] of Object.entries(manifest?.files ?? {})) {\n await this.writeFile(filePath, content);\n }\n });\n }\n\n async exec(options: SandboxExecOptions): Promise<SandboxExecResult> {\n return this.runOperation(async () => {\n await this.hooks.onExecStart?.({\n ...this.event(),\n command: options.command,\n args: options.args ?? [],\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n });\n\n const normalizedResult = await this.execCommand(options);\n\n await this.hooks.onExecEnd?.({\n ...this.event(),\n command: options.command,\n args: options.args ?? [],\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n result: normalizedResult,\n });\n\n return normalizedResult;\n });\n }\n\n async *execStream(options: SandboxExecOptions): AsyncIterable<SandboxExecStreamEvent> {\n const events: SandboxExecStreamEvent[] = [];\n let notify: (() => void) | undefined;\n let done = false;\n let error: unknown;\n\n const push = (event: SandboxExecStreamEvent) => {\n events.push(event);\n notify?.();\n notify = undefined;\n };\n\n const wait = () =>\n new Promise<void>((resolve) => {\n notify = resolve;\n });\n\n const run = this.exec({\n ...options,\n onStdout: (chunk) => {\n options.onStdout?.(chunk);\n push({ type: \"stdout\", chunk, text: Buffer.from(chunk).toString(\"utf8\") });\n },\n onStderr: (chunk) => {\n options.onStderr?.(chunk);\n push({ type: \"stderr\", chunk, text: Buffer.from(chunk).toString(\"utf8\") });\n },\n })\n .then((result) => {\n push({ type: \"exit\", result });\n })\n .catch((caught) => {\n error = caught;\n })\n .finally(() => {\n done = true;\n notify?.();\n notify = undefined;\n });\n\n try {\n while (!done || events.length > 0) {\n const event = events.shift();\n if (event !== undefined) {\n yield event;\n continue;\n }\n\n await wait();\n }\n\n if (error !== undefined) {\n throw error;\n }\n } finally {\n await run;\n }\n }\n\n async readFile(filePath: string): Promise<Uint8Array> {\n return this.runOperation(async () => {\n const normalized = normalizeSandboxPath(filePath);\n const tempDir = await mkdtemp(path.join(os.tmpdir(), \"anvia-sandbox-read-\"));\n const target = path.join(tempDir, path.basename(normalized));\n\n try {\n await assertDockerCli(\n [\"cp\", `${this.containerName}:${containerPath(this.workdir, normalized)}`, target],\n this.cliOptions(),\n );\n const { readFile } = await import(\"node:fs/promises\");\n const bytes = await readFile(target);\n this.assertFileSize(bytes.byteLength, filePath);\n return bytes;\n } finally {\n await rm(tempDir, { recursive: true, force: true });\n }\n });\n }\n\n async readTextFile(filePath: string): Promise<string> {\n const bytes = await this.readFile(filePath);\n return new TextDecoder().decode(bytes);\n }\n\n async writeFile(filePath: string, data: string | Uint8Array): Promise<void> {\n await this.runOperation(async () => {\n const size = byteLength(data);\n this.assertFileSize(size, filePath);\n const normalized = normalizeSandboxPath(filePath);\n await this.mkdir(parentSandboxPath(normalized));\n\n const tempDir = await mkdtemp(path.join(os.tmpdir(), \"anvia-sandbox-write-\"));\n const source = path.join(tempDir, path.basename(normalized));\n\n try {\n await writeFile(source, data);\n await assertDockerCli(\n [\"cp\", source, `${this.containerName}:${containerPath(this.workdir, normalized)}`],\n this.cliOptions(),\n );\n await this.hooks.onFileWrite?.({ ...this.event(), path: normalized, size });\n } finally {\n await rm(tempDir, { recursive: true, force: true });\n }\n });\n }\n\n async writeTextFile(filePath: string, content: string): Promise<void> {\n await this.writeFile(filePath, content);\n }\n\n async listFiles(filePath = \".\"): Promise<SandboxFileEntry[]> {\n return this.runOperation(async () => {\n const normalized = normalizeSandboxPath(filePath, { allowRoot: true });\n const target = containerPath(this.workdir, normalized);\n const result = await this.execCommand({\n command: \"find\",\n args: [target, \"-mindepth\", \"1\", \"-maxdepth\", \"1\", \"-printf\", \"%p\\t%y\\t%s\\n\"],\n });\n\n if (result.timedOut) {\n throw new SandboxTimeoutError(`Listing files timed out for ${filePath}.`);\n }\n\n if (result.exitCode !== 0) {\n throw new SandboxDockerCommandError(`Unable to list sandbox path: ${filePath}`, result);\n }\n\n return result.stdout\n .split(\"\\n\")\n .filter((line) => line.trim().length > 0)\n .map((line) => this.parseFindEntry(line));\n });\n }\n\n async destroy(): Promise<void> {\n if (this.destroyed) {\n return;\n }\n\n this.destroyed = true;\n this.clearLifecycleTimers();\n await runDockerCli([\"rm\", \"-f\", this.containerName], this.cliOptions()).catch(() => undefined);\n\n if (this.removeVolumeOnDestroy) {\n await runDockerCli([\"volume\", \"rm\", \"-f\", this.volumeName], this.cliOptions()).catch(\n () => undefined,\n );\n }\n\n await this.hooks.onDestroy?.(this.event());\n }\n\n private async mkdir(directoryPath: string): Promise<void> {\n const normalized = normalizeSandboxPath(directoryPath, { allowRoot: true });\n const result = await this.execCommand({\n command: \"mkdir\",\n args: [\"-p\", containerPath(this.workdir, normalized)],\n });\n\n if (result.exitCode !== 0) {\n throw new SandboxDockerCommandError(\n `Unable to create sandbox directory: ${directoryPath}`,\n result,\n );\n }\n }\n\n private parseFindEntry(line: string): SandboxFileEntry {\n const [rawPath, rawType, rawSize] = line.split(\"\\t\");\n const absolutePath = rawPath ?? \"\";\n const relativePath = absolutePath.startsWith(`${this.workdir}/`)\n ? absolutePath.slice(this.workdir.length + 1)\n : absolutePath;\n const size = rawSize === undefined ? undefined : Number(rawSize);\n const entry: SandboxFileEntry = {\n path: relativePath,\n type: mapFindType(rawType),\n };\n\n if (size !== undefined && Number.isFinite(size)) {\n entry.size = size;\n }\n\n return entry;\n }\n\n private cliOptions() {\n return {\n dockerPath: this.dockerPath,\n maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes,\n };\n }\n\n private async execCommand(options: SandboxExecOptions): Promise<SandboxExecResult> {\n if (options.command.trim().length === 0) {\n throw new SandboxDockerCommandError(\"Sandbox command cannot be empty.\", {\n stdout: \"\",\n stderr: \"\",\n exitCode: 1,\n });\n }\n\n const args = [\"exec\"];\n\n if (options.input !== undefined) {\n args.push(\"-i\");\n }\n\n const cwd = containerPath(this.workdir, options.cwd ?? \".\");\n args.push(\"-w\", cwd);\n\n for (const [key, value] of Object.entries({ ...this.env, ...options.env })) {\n args.push(\"-e\", `${key}=${value}`);\n }\n\n args.push(this.containerName, options.command, ...(options.args ?? []));\n\n const cliOptions = {\n dockerPath: this.dockerPath,\n timeoutMs: options.timeoutMs ?? this.limits.timeoutMs ?? defaultTimeoutMs,\n maxOutputBytes: this.limits.maxOutputBytes ?? defaultMaxOutputBytes,\n ...(options.input === undefined ? {} : { input: options.input }),\n ...(options.signal === undefined ? {} : { signal: options.signal }),\n ...(options.onStdout === undefined ? {} : { onStdout: options.onStdout }),\n ...(options.onStderr === undefined ? {} : { onStderr: options.onStderr }),\n };\n\n const result = await runDockerCli(args, cliOptions);\n\n if (result.timedOut) {\n return {\n ...result,\n exitCode: result.exitCode === 0 ? 124 : result.exitCode,\n };\n }\n\n return result;\n }\n\n private assertActive(): void {\n if (this.destroyed) {\n throw new SandboxSessionDestroyedError(`Sandbox session ${this.id} has been destroyed.`);\n }\n }\n\n event(): SandboxSessionEvent {\n return {\n sessionId: this.id,\n provider: this.provider,\n workdir: this.workdir,\n };\n }\n\n private async runOperation<T>(operation: () => Promise<T>): Promise<T> {\n this.assertActive();\n this.activeOperations += 1;\n this.clearIdleTimer();\n\n try {\n return await operation();\n } finally {\n this.activeOperations -= 1;\n this.scheduleIdleTimer();\n }\n }\n\n private assertFileSize(size: number, filePath: string): void {\n if (this.limits.maxFileBytes !== undefined && size > this.limits.maxFileBytes) {\n throw new SandboxFileSizeError(\n `Sandbox file exceeds maxFileBytes (${size} > ${this.limits.maxFileBytes}): ${filePath}`,\n );\n }\n }\n\n private startLifecycleTimers(): void {\n if (!this.lifecycle.autoDestroy) {\n return;\n }\n\n if (this.lifecycle.ttlMs !== undefined) {\n this.ttlTimer = setTimeout(() => {\n void this.destroy().catch(() => undefined);\n }, this.lifecycle.ttlMs);\n this.ttlTimer.unref?.();\n }\n\n this.scheduleIdleTimer();\n }\n\n private scheduleIdleTimer(): void {\n if (!this.lifecycle.autoDestroy || this.lifecycle.idleTimeoutMs === undefined) {\n return;\n }\n\n if (this.destroyed || this.activeOperations > 0) {\n return;\n }\n\n this.clearIdleTimer();\n this.idleTimer = setTimeout(() => {\n void this.destroy().catch(() => undefined);\n }, this.lifecycle.idleTimeoutMs);\n this.idleTimer.unref?.();\n }\n\n private clearLifecycleTimers(): void {\n if (this.ttlTimer !== undefined) {\n clearTimeout(this.ttlTimer);\n this.ttlTimer = undefined;\n }\n this.clearIdleTimer();\n }\n\n private clearIdleTimer(): void {\n if (this.idleTimer !== undefined) {\n clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n }\n }\n}\n\nfunction getWorkspaceId(workspace: SandboxWorkspaceOptions, sessionId: string): string {\n if (workspace.mode === \"persistent\") {\n return sanitizeResourceId(workspace.id);\n }\n\n return sessionId;\n}\n\nfunction shouldDestroyWorkspace(workspace: SandboxWorkspaceOptions): boolean {\n if (workspace.mode === \"persistent\") {\n return workspace.destroyOnSessionDestroy ?? false;\n }\n\n return true;\n}\n\nfunction sanitizeResourceId(id: string): string {\n const sanitized = id\n .toLowerCase()\n .replaceAll(/[^a-z0-9_.-]/g, \"-\")\n .replaceAll(/^-+|-+$/g, \"\");\n return sanitized.length > 0 ? sanitized : randomUUID();\n}\n\nfunction byteLength(data: string | Uint8Array): number {\n return typeof data === \"string\" ? Buffer.byteLength(data) : data.byteLength;\n}\n\nfunction mapFindType(type: string | undefined): SandboxFileType {\n if (type === \"f\") {\n return \"file\";\n }\n if (type === \"d\") {\n return \"directory\";\n }\n if (type === \"l\") {\n return \"symlink\";\n }\n return \"other\";\n}\n","import { spawn } from \"node:child_process\";\nimport { SandboxDockerCommandError, SandboxDockerUnavailableError } from \"./errors\";\n\nexport interface DockerCliResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n durationMs: number;\n timedOut: boolean;\n aborted: boolean;\n stdoutTruncated: boolean;\n stderrTruncated: boolean;\n}\n\nexport interface DockerCliOptions {\n dockerPath: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n input?: string | Uint8Array;\n signal?: AbortSignal;\n onStdout?: (chunk: Uint8Array) => void;\n onStderr?: (chunk: Uint8Array) => void;\n}\n\nconst defaultMaxOutputBytes = 1024 * 1024;\n\nexport async function runDockerCli(\n args: string[],\n options: DockerCliOptions,\n): Promise<DockerCliResult> {\n const startedAt = Date.now();\n const maxOutputBytes = options.maxOutputBytes ?? defaultMaxOutputBytes;\n const stdout = createOutputCollector(maxOutputBytes, options.onStdout);\n const stderr = createOutputCollector(maxOutputBytes, options.onStderr);\n\n return new Promise((resolve, reject) => {\n const child = spawn(options.dockerPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let timedOut = false;\n let aborted = false;\n let settled = false;\n\n const timeout =\n options.timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGKILL\");\n }, options.timeoutMs);\n\n const abort = () => {\n aborted = true;\n child.kill(\"SIGKILL\");\n };\n\n if (options.signal?.aborted === true) {\n abort();\n } else {\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n }\n\n child.stdout.on(\"data\", (chunk: Buffer) => stdout.accept(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => stderr.accept(chunk));\n\n child.on(\"error\", (error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", abort);\n\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n reject(new SandboxDockerUnavailableError(\"Docker CLI was not found.\", error));\n return;\n }\n\n reject(error);\n });\n\n child.on(\"close\", (code) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", abort);\n\n resolve({\n stdout: stdout.text(),\n stderr: stderr.text(),\n exitCode: code ?? 1,\n durationMs: Date.now() - startedAt,\n timedOut,\n aborted,\n stdoutTruncated: stdout.truncated,\n stderrTruncated: stderr.truncated,\n });\n });\n\n if (options.input !== undefined) {\n child.stdin.end(options.input);\n } else {\n child.stdin.end();\n }\n });\n}\n\nexport async function assertDockerCli(args: string[], options: DockerCliOptions): Promise<string> {\n const result = await runDockerCli(args, options);\n\n if (result.exitCode !== 0) {\n throw new SandboxDockerCommandError(`Docker command failed: docker ${args.join(\" \")}`, result);\n }\n\n return result.stdout.trim();\n}\n\nfunction createOutputCollector(maxBytes: number, onChunk?: (chunk: Uint8Array) => void) {\n const chunks: Buffer[] = [];\n let length = 0;\n let truncated = false;\n\n return {\n get truncated() {\n return truncated;\n },\n accept(chunk: Buffer) {\n onChunk?.(chunk);\n\n if (length >= maxBytes) {\n truncated = true;\n return;\n }\n\n const remaining = maxBytes - length;\n const next = chunk.length > remaining ? chunk.subarray(0, remaining) : chunk;\n chunks.push(next);\n length += next.length;\n\n if (next.length < chunk.length) {\n truncated = true;\n }\n },\n text() {\n return Buffer.concat(chunks, length).toString(\"utf8\");\n },\n };\n}\n","export class SandboxError extends Error {\n constructor(\n message: string,\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class SandboxDockerUnavailableError extends SandboxError {}\n\nexport class SandboxDockerCommandError extends SandboxError {\n constructor(\n message: string,\n readonly result: {\n stdout: string;\n stderr: string;\n exitCode: number;\n },\n ) {\n super(message);\n }\n}\n\nexport class SandboxSessionDestroyedError extends SandboxError {}\n\nexport class SandboxPathError extends SandboxError {}\n\nexport class SandboxTimeoutError extends SandboxError {}\n\nexport class SandboxFileSizeError extends SandboxError {}\n\nexport class SandboxToolPolicyError extends SandboxError {}\n","import path from \"node:path\";\nimport { SandboxPathError } from \"./errors\";\n\nexport function normalizeSandboxPath(input: string, options: { allowRoot?: boolean } = {}): string {\n if (input.length === 0) {\n throw new SandboxPathError(\"Sandbox path cannot be empty.\");\n }\n\n if (input.includes(\"\\0\")) {\n throw new SandboxPathError(\"Sandbox path cannot contain null bytes.\");\n }\n\n const normalized = path.posix.normalize(input.replaceAll(\"\\\\\", \"/\"));\n\n if (path.posix.isAbsolute(normalized)) {\n throw new SandboxPathError(`Sandbox path must be relative: ${input}`);\n }\n\n if (normalized === \"..\" || normalized.startsWith(\"../\")) {\n throw new SandboxPathError(`Sandbox path cannot leave the workspace: ${input}`);\n }\n\n if (normalized === \".\" && options.allowRoot !== true) {\n throw new SandboxPathError(\n \"Sandbox path must refer to a file or directory inside the workspace.\",\n );\n }\n\n return normalized;\n}\n\nexport function containerPath(workdir: string, relativePath: string): string {\n const normalizedWorkdir = path.posix.normalize(workdir);\n const normalizedPath = normalizeSandboxPath(relativePath, { allowRoot: true });\n return normalizedPath === \".\"\n ? normalizedWorkdir\n : path.posix.join(normalizedWorkdir, normalizedPath);\n}\n\nexport function parentSandboxPath(relativePath: string): string {\n const normalized = normalizeSandboxPath(relativePath);\n const parent = path.posix.dirname(normalized);\n return parent === \".\" ? \".\" : parent;\n}\n","import { type AnyTool, createTool } from \"@anvia/core/tool\";\nimport { z } from \"zod\";\nimport { SandboxToolPolicyError } from \"./errors\";\nimport type {\n SandboxExecOptions,\n SandboxExecResult,\n SandboxSession,\n SandboxToolName,\n SandboxToolsOptions,\n} from \"./types\";\n\nconst execCommandInput = z.object({\n command: z.string().min(1).describe(\"Executable to run inside the sandbox workspace.\"),\n args: z.array(z.string()).optional().describe(\"Command arguments.\"),\n cwd: z.string().optional().describe(\"Relative working directory inside the sandbox.\"),\n env: z\n .record(z.string(), z.string())\n .optional()\n .describe(\"Environment variables for this command.\"),\n timeoutMs: z\n .number()\n .int()\n .positive()\n .max(300_000)\n .optional()\n .describe(\"Optional command timeout in milliseconds.\"),\n input: z.string().optional().describe(\"Optional stdin text to pass to the command.\"),\n});\n\nconst readFileInput = z.object({\n path: z.string().min(1).describe(\"Relative file path inside the sandbox.\"),\n});\n\nconst writeFileInput = z.object({\n path: z.string().min(1).describe(\"Relative file path inside the sandbox.\"),\n content: z.string().describe(\"Complete text content to write.\"),\n});\n\nconst listFilesInput = z.object({\n path: z\n .string()\n .optional()\n .describe(\"Relative directory path inside the sandbox. Defaults to root.\"),\n});\n\nconst textOutput = z.string();\n\nexport function createSandboxTools(\n session: SandboxSession,\n options: SandboxToolsOptions = {},\n): AnyTool[] {\n const include = new Set<SandboxToolName>(\n options.allow ?? options.include ?? [\"exec_command\", \"read_file\", \"write_file\", \"list_files\"],\n );\n const tools: AnyTool[] = [];\n\n if (include.has(\"exec_command\")) {\n tools.push(createExecCommandTool(session, options));\n }\n\n if (include.has(\"read_file\")) {\n tools.push(createReadFileTool(session, options));\n }\n\n if (include.has(\"write_file\")) {\n tools.push(createWriteFileTool(session, options));\n }\n\n if (include.has(\"list_files\")) {\n tools.push(createListFilesTool(session));\n }\n\n return tools;\n}\n\nfunction createExecCommandTool(session: SandboxSession, options: SandboxToolsOptions): AnyTool {\n const policy = options.exec ?? {};\n\n return createTool({\n name: \"exec_command\",\n description:\n \"Run a command inside the sandbox workspace. Use structured args instead of shell quoting.\",\n input: execCommandInput,\n output: textOutput,\n execute: async ({ command, args, cwd, env, timeoutMs, input }) => {\n assertCommandAllowed(command, options);\n\n const execOptions: SandboxExecOptions = {\n command,\n };\n\n if (args !== undefined) {\n execOptions.args = args;\n }\n if (cwd !== undefined) {\n execOptions.cwd = cwd;\n }\n if (env !== undefined) {\n execOptions.env = env;\n }\n const effectiveTimeoutMs = timeoutMs ?? policy.defaultTimeoutMs ?? options.execTimeoutMs;\n if (effectiveTimeoutMs !== undefined) {\n assertTimeoutAllowed(effectiveTimeoutMs, options);\n execOptions.timeoutMs = effectiveTimeoutMs;\n }\n if (input !== undefined) {\n execOptions.input = input;\n }\n\n const result = await session.exec(execOptions);\n\n return formatExecResult(result);\n },\n });\n}\n\nfunction createReadFileTool(session: SandboxSession, options: SandboxToolsOptions): AnyTool {\n return createTool({\n name: \"read_file\",\n description: \"Read a text file from the sandbox workspace.\",\n input: readFileInput,\n output: textOutput,\n execute: async ({ path }) => {\n const content = await session.readTextFile(path);\n assertReadAllowed(content, options);\n return content;\n },\n });\n}\n\nfunction createWriteFileTool(session: SandboxSession, options: SandboxToolsOptions): AnyTool {\n return createTool({\n name: \"write_file\",\n description: \"Write a text file inside the sandbox workspace. Creates parent directories.\",\n input: writeFileInput,\n output: textOutput,\n execute: async ({ path, content }) => {\n assertContentAllowed(content, options);\n await session.writeTextFile(path, content);\n return `Wrote ${path}`;\n },\n });\n}\n\nfunction createListFilesTool(session: SandboxSession): AnyTool {\n return createTool({\n name: \"list_files\",\n description: \"List files and directories inside the sandbox workspace.\",\n input: listFilesInput,\n output: textOutput,\n execute: async ({ path }) => {\n const entries = await session.listFiles(path);\n\n if (entries.length === 0) {\n return \"No files found.\";\n }\n\n return entries\n .map((entry) => {\n const size = entry.size === undefined ? \"\" : ` ${entry.size}b`;\n return `${entry.type}${size}\\t${entry.path}`;\n })\n .join(\"\\n\");\n },\n });\n}\n\nfunction formatExecResult(result: SandboxExecResult): string {\n const parts = [`exit_code: ${result.exitCode}`];\n\n if (result.timedOut) {\n parts.push(\"timed_out: true\");\n }\n\n if (result.aborted) {\n parts.push(\"aborted: true\");\n }\n\n if (result.stdout.length > 0) {\n parts.push(`stdout:\\n${result.stdout.trimEnd()}`);\n }\n\n if (result.stderr.length > 0) {\n parts.push(`stderr:\\n${result.stderr.trimEnd()}`);\n }\n\n if (result.stdoutTruncated || result.stderrTruncated) {\n parts.push(\"output_truncated: true\");\n }\n\n return parts.join(\"\\n\\n\");\n}\n\nfunction assertCommandAllowed(command: string, options: SandboxToolsOptions): void {\n const policy = options.exec;\n\n if (policy?.blockedCommands?.includes(command)) {\n throw new SandboxToolPolicyError(`Command is blocked by sandbox tool policy: ${command}`);\n }\n\n if (policy?.allowedCommands !== undefined && !policy.allowedCommands.includes(command)) {\n throw new SandboxToolPolicyError(`Command is not allowed by sandbox tool policy: ${command}`);\n }\n}\n\nfunction assertTimeoutAllowed(timeoutMs: number, options: SandboxToolsOptions): void {\n const maxTimeoutMs = options.exec?.maxTimeoutMs;\n\n if (maxTimeoutMs !== undefined && timeoutMs > maxTimeoutMs) {\n throw new SandboxToolPolicyError(\n `Command timeout exceeds sandbox tool policy (${timeoutMs} > ${maxTimeoutMs}).`,\n );\n }\n}\n\nfunction assertContentAllowed(content: string, options: SandboxToolsOptions): void {\n const maxBytes = options.writeFile?.maxBytes;\n\n if (maxBytes !== undefined && Buffer.byteLength(content) > maxBytes) {\n throw new SandboxToolPolicyError(\"File content exceeds sandbox tool policy.\");\n }\n}\n\nfunction assertReadAllowed(content: string, options: SandboxToolsOptions): void {\n const maxBytes = options.readFile?.maxBytes;\n\n if (maxBytes !== undefined && Buffer.byteLength(content) > maxBytes) {\n throw new SandboxToolPolicyError(\"File content exceeds sandbox tool policy.\");\n }\n}\n"],"mappings":"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","names":["path","defaultMaxOutputBytes","path","path"]}
|