@isol8/core 0.17.0 → 0.19.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/config.d.ts.map +1 -1
- package/dist/docker/Dockerfile +14 -2
- package/dist/engine/docker.d.ts +12 -2
- package/dist/engine/docker.d.ts.map +1 -1
- package/dist/engine/image-builder.d.ts +10 -22
- package/dist/engine/image-builder.d.ts.map +1 -1
- package/dist/engine/managers/execution-manager.d.ts +2 -0
- package/dist/engine/managers/execution-manager.d.ts.map +1 -1
- package/dist/engine/utils.d.ts +21 -1
- package/dist/engine/utils.d.ts.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +319 -133
- package/dist/index.js.map +12 -11
- package/dist/runtime/adapter.d.ts +20 -0
- package/dist/runtime/adapter.d.ts.map +1 -1
- package/dist/runtime/adapters/agent.d.ts +19 -0
- package/dist/runtime/adapters/agent.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/types.d.ts +55 -27
- package/dist/types.d.ts.map +1 -1
- package/docker/Dockerfile +14 -2
- package/package.json +1 -1
- package/schema/isol8.config.schema.json +39 -46
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
2
|
var __returnValue = (v) => v;
|
|
4
3
|
function __exportSetter(name, newValue) {
|
|
@@ -14,7 +13,6 @@ var __export = (target, all) => {
|
|
|
14
13
|
});
|
|
15
14
|
};
|
|
16
15
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
18
16
|
|
|
19
17
|
// src/runtime/adapter.ts
|
|
20
18
|
var adapters, extensionMap, RuntimeRegistry;
|
|
@@ -50,6 +48,37 @@ var init_adapter = __esm(() => {
|
|
|
50
48
|
};
|
|
51
49
|
});
|
|
52
50
|
|
|
51
|
+
// src/runtime/adapters/agent.ts
|
|
52
|
+
function shellQuote(s) {
|
|
53
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
54
|
+
}
|
|
55
|
+
var SANDBOX_SYSTEM_PROMPT, AgentAdapter;
|
|
56
|
+
var init_agent = __esm(() => {
|
|
57
|
+
SANDBOX_SYSTEM_PROMPT = "You are running inside an isol8 sandbox — a Docker container with strict " + "resource limits and controlled network access. isol8 exists to execute " + "untrusted code safely: outbound network is filtered to a whitelist, the " + "filesystem is ephemeral, and some system calls are restricted. Work within " + "these constraints: do not assume open internet access, do not rely on " + "persistent state across runs, and do not attempt to escape the sandbox.";
|
|
58
|
+
AgentAdapter = {
|
|
59
|
+
name: "agent",
|
|
60
|
+
image: "isol8:agent",
|
|
61
|
+
getCommand(code) {
|
|
62
|
+
return [
|
|
63
|
+
"bash",
|
|
64
|
+
"-c",
|
|
65
|
+
`pi --no-session --append-system-prompt ${shellQuote(SANDBOX_SYSTEM_PROMPT)} -p ${shellQuote(code)}`
|
|
66
|
+
];
|
|
67
|
+
},
|
|
68
|
+
getCommandWithOptions(code, options) {
|
|
69
|
+
const flags = options.agentFlags ? `${options.agentFlags} ` : "";
|
|
70
|
+
return [
|
|
71
|
+
"bash",
|
|
72
|
+
"-c",
|
|
73
|
+
`pi --no-session --append-system-prompt ${shellQuote(SANDBOX_SYSTEM_PROMPT)} ${flags}-p ${shellQuote(code)}`
|
|
74
|
+
];
|
|
75
|
+
},
|
|
76
|
+
getFileExtension() {
|
|
77
|
+
return ".txt";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
53
82
|
// src/runtime/adapters/bash.ts
|
|
54
83
|
var bashAdapter;
|
|
55
84
|
var init_bash = __esm(() => {
|
|
@@ -151,12 +180,14 @@ var init_python = __esm(() => {
|
|
|
151
180
|
// src/runtime/index.ts
|
|
152
181
|
var init_runtime = __esm(() => {
|
|
153
182
|
init_adapter();
|
|
183
|
+
init_agent();
|
|
154
184
|
init_bash();
|
|
155
185
|
init_bun();
|
|
156
186
|
init_deno();
|
|
157
187
|
init_node();
|
|
158
188
|
init_python();
|
|
159
189
|
init_adapter();
|
|
190
|
+
init_agent();
|
|
160
191
|
init_bash();
|
|
161
192
|
init_bun();
|
|
162
193
|
init_deno();
|
|
@@ -167,6 +198,7 @@ var init_runtime = __esm(() => {
|
|
|
167
198
|
RuntimeRegistry.register(BunAdapter);
|
|
168
199
|
RuntimeRegistry.register(bashAdapter);
|
|
169
200
|
RuntimeRegistry.register(DenoAdapter);
|
|
201
|
+
RuntimeRegistry.register(AgentAdapter);
|
|
170
202
|
});
|
|
171
203
|
|
|
172
204
|
// src/utils/logger.ts
|
|
@@ -200,11 +232,13 @@ var exports_utils = {};
|
|
|
200
232
|
__export(exports_utils, {
|
|
201
233
|
validatePackageName: () => validatePackageName,
|
|
202
234
|
truncateOutput: () => truncateOutput,
|
|
235
|
+
resolveWorkdir: () => resolveWorkdir,
|
|
203
236
|
parseMemoryLimit: () => parseMemoryLimit,
|
|
204
237
|
maskSecrets: () => maskSecrets,
|
|
205
238
|
extractFromTar: () => extractFromTar,
|
|
206
239
|
createTarBuffer: () => createTarBuffer
|
|
207
240
|
});
|
|
241
|
+
import path from "node:path";
|
|
208
242
|
function parseMemoryLimit(limit) {
|
|
209
243
|
const match = limit.match(/^(\d+(?:\.\d+)?)\s*([kmgt]?)b?$/i);
|
|
210
244
|
if (!match) {
|
|
@@ -299,17 +333,24 @@ function validatePackageName(name) {
|
|
|
299
333
|
}
|
|
300
334
|
return name;
|
|
301
335
|
}
|
|
336
|
+
function resolveWorkdir(workdir, sandboxRoot = "/sandbox") {
|
|
337
|
+
const resolved = path.posix.resolve(sandboxRoot, workdir);
|
|
338
|
+
if (resolved !== sandboxRoot && !resolved.startsWith(`${sandboxRoot}/`)) {
|
|
339
|
+
throw new Error("Working directory must be inside /sandbox");
|
|
340
|
+
}
|
|
341
|
+
return resolved;
|
|
342
|
+
}
|
|
343
|
+
var init_utils = () => {};
|
|
302
344
|
|
|
303
345
|
// src/engine/image-builder.ts
|
|
304
346
|
var exports_image_builder = {};
|
|
305
347
|
__export(exports_image_builder, {
|
|
306
348
|
normalizePackages: () => normalizePackages,
|
|
307
349
|
imageExists: () => imageExists,
|
|
308
|
-
getCustomImageTag: () => getCustomImageTag,
|
|
309
350
|
ensureImages: () => ensureImages,
|
|
310
|
-
buildCustomImages: () => buildCustomImages,
|
|
311
351
|
buildCustomImage: () => buildCustomImage,
|
|
312
|
-
buildBaseImages: () => buildBaseImages
|
|
352
|
+
buildBaseImages: () => buildBaseImages,
|
|
353
|
+
LABELS: () => LABELS
|
|
313
354
|
});
|
|
314
355
|
import { createHash as createHash2 } from "node:crypto";
|
|
315
356
|
import { existsSync as existsSync3, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
@@ -350,23 +391,21 @@ function computeDockerDirHash() {
|
|
|
350
391
|
}
|
|
351
392
|
return hash.digest("hex");
|
|
352
393
|
}
|
|
353
|
-
function computeDepsHash(runtime, packages) {
|
|
394
|
+
function computeDepsHash(runtime, packages, setupScript) {
|
|
354
395
|
const hash = createHash2("sha256");
|
|
355
396
|
hash.update(runtime);
|
|
356
397
|
for (const pkg of [...packages].sort()) {
|
|
357
398
|
hash.update(pkg);
|
|
358
399
|
}
|
|
400
|
+
if (setupScript) {
|
|
401
|
+
hash.update("setup:");
|
|
402
|
+
hash.update(setupScript);
|
|
403
|
+
}
|
|
359
404
|
return hash.digest("hex");
|
|
360
405
|
}
|
|
361
406
|
function normalizePackages(packages) {
|
|
362
407
|
return [...new Set(packages.map((pkg) => pkg.trim()).filter(Boolean))].sort();
|
|
363
408
|
}
|
|
364
|
-
function getCustomImageTag(runtime, packages) {
|
|
365
|
-
const normalizedPackages = normalizePackages(packages);
|
|
366
|
-
const depsHash = computeDepsHash(runtime, normalizedPackages);
|
|
367
|
-
const shortHash = depsHash.slice(0, 12);
|
|
368
|
-
return `isol8:${runtime}-custom-${shortHash}`;
|
|
369
|
-
}
|
|
370
409
|
async function getImageLabels(docker, imageName) {
|
|
371
410
|
try {
|
|
372
411
|
const image = docker.getImage(imageName);
|
|
@@ -439,33 +478,9 @@ async function buildBaseImages(docker, onProgress, force = false, onlyRuntimes)
|
|
|
439
478
|
}
|
|
440
479
|
}
|
|
441
480
|
}
|
|
442
|
-
async function
|
|
443
|
-
const deps = config.dependencies;
|
|
444
|
-
const python = deps.python ? normalizePackages(deps.python) : [];
|
|
445
|
-
const node = deps.node ? normalizePackages(deps.node) : [];
|
|
446
|
-
const bun = deps.bun ? normalizePackages(deps.bun) : [];
|
|
447
|
-
const deno = deps.deno ? normalizePackages(deps.deno) : [];
|
|
448
|
-
const bash = deps.bash ? normalizePackages(deps.bash) : [];
|
|
449
|
-
if (python.length) {
|
|
450
|
-
await buildCustomImage(docker, "python", python, onProgress, force);
|
|
451
|
-
}
|
|
452
|
-
if (node.length) {
|
|
453
|
-
await buildCustomImage(docker, "node", node, onProgress, force);
|
|
454
|
-
}
|
|
455
|
-
if (bun.length) {
|
|
456
|
-
await buildCustomImage(docker, "bun", bun, onProgress, force);
|
|
457
|
-
}
|
|
458
|
-
if (deno.length) {
|
|
459
|
-
await buildCustomImage(docker, "deno", deno, onProgress, force);
|
|
460
|
-
}
|
|
461
|
-
if (bash.length) {
|
|
462
|
-
await buildCustomImage(docker, "bash", bash, onProgress, force);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
async function buildCustomImage(docker, runtime, packages, onProgress, force = false) {
|
|
481
|
+
async function buildCustomImage(docker, runtime, packages, tag, onProgress, force = false, setupScript) {
|
|
466
482
|
const normalizedPackages = normalizePackages(packages);
|
|
467
|
-
const
|
|
468
|
-
const depsHash = computeDepsHash(runtime, normalizedPackages);
|
|
483
|
+
const depsHash = computeDepsHash(runtime, normalizedPackages, setupScript);
|
|
469
484
|
logger.debug(`[ImageBuilder] ${runtime} custom deps hash: ${depsHash.slice(0, 16)}...`);
|
|
470
485
|
if (!force) {
|
|
471
486
|
const labels = await getImageLabels(docker, tag);
|
|
@@ -491,7 +506,7 @@ async function buildCustomImage(docker, runtime, packages, onProgress, force = f
|
|
|
491
506
|
let installCmd;
|
|
492
507
|
switch (runtime) {
|
|
493
508
|
case "python":
|
|
494
|
-
installCmd = `RUN pip install --no-cache-dir ${normalizedPackages.join(" ")}`;
|
|
509
|
+
installCmd = `RUN pip install --break-system-packages --no-cache-dir ${normalizedPackages.join(" ")}`;
|
|
495
510
|
break;
|
|
496
511
|
case "node":
|
|
497
512
|
installCmd = `RUN npm install -g ${normalizedPackages.join(" ")}`;
|
|
@@ -509,27 +524,50 @@ async function buildCustomImage(docker, runtime, packages, onProgress, force = f
|
|
|
509
524
|
default:
|
|
510
525
|
throw new Error(`Unknown runtime: ${runtime}`);
|
|
511
526
|
}
|
|
527
|
+
let setupLines = "";
|
|
528
|
+
if (setupScript) {
|
|
529
|
+
const escaped = setupScript.replace(/\\/g, "\\\\").replace(/'/g, "'\\''");
|
|
530
|
+
setupLines = [
|
|
531
|
+
`RUN printf '%s\\n' '${escaped}' > /sandbox/.isol8-setup.sh`,
|
|
532
|
+
"RUN chmod +x /sandbox/.isol8-setup.sh"
|
|
533
|
+
].join(`
|
|
534
|
+
`);
|
|
535
|
+
}
|
|
512
536
|
const dockerfileContent = `FROM isol8:${runtime}
|
|
513
537
|
${installCmd}
|
|
538
|
+
${setupLines}
|
|
514
539
|
`;
|
|
515
|
-
const { createTarBuffer: createTarBuffer2, validatePackageName: validatePackageName2 } = await Promise.resolve().then(() => exports_utils);
|
|
516
|
-
const { Readable } = await import("node:stream");
|
|
540
|
+
const { createTarBuffer: createTarBuffer2, validatePackageName: validatePackageName2 } = await Promise.resolve().then(() => (init_utils(), exports_utils));
|
|
517
541
|
normalizedPackages.forEach(validatePackageName2);
|
|
518
542
|
const tarBuffer = createTarBuffer2("Dockerfile", dockerfileContent);
|
|
519
|
-
const
|
|
543
|
+
const imageLabels = {
|
|
544
|
+
[LABELS.depsHash]: depsHash,
|
|
545
|
+
[LABELS.runtime]: runtime.toString(),
|
|
546
|
+
[LABELS.dependencies]: normalizedPackages.join(",")
|
|
547
|
+
};
|
|
548
|
+
if (setupScript) {
|
|
549
|
+
imageLabels[LABELS.setupScript] = setupScript;
|
|
550
|
+
}
|
|
551
|
+
const stream = await docker.buildImage(tarBuffer, {
|
|
520
552
|
t: tag,
|
|
521
553
|
dockerfile: "Dockerfile",
|
|
522
|
-
labels:
|
|
523
|
-
[LABELS.depsHash]: depsHash
|
|
524
|
-
}
|
|
554
|
+
labels: imageLabels
|
|
525
555
|
});
|
|
526
556
|
await new Promise((resolve2, reject) => {
|
|
527
|
-
docker.modem.followProgress(stream, (err) => {
|
|
557
|
+
docker.modem.followProgress(stream, (err, res) => {
|
|
528
558
|
if (err) {
|
|
529
559
|
reject(err);
|
|
560
|
+
} else if (res && res.length > 0 && res.at(-1).error) {
|
|
561
|
+
reject(new Error(res.at(-1).error));
|
|
530
562
|
} else {
|
|
531
563
|
resolve2();
|
|
532
564
|
}
|
|
565
|
+
}, (event) => {
|
|
566
|
+
if (event.stream) {
|
|
567
|
+
onProgress?.({ runtime: String(runtime), status: "building", message: event.stream });
|
|
568
|
+
} else if (event.error) {
|
|
569
|
+
onProgress?.({ runtime: String(runtime), status: "error", message: event.error });
|
|
570
|
+
}
|
|
533
571
|
});
|
|
534
572
|
});
|
|
535
573
|
if (oldImageId) {
|
|
@@ -564,7 +602,10 @@ var init_image_builder = __esm(() => {
|
|
|
564
602
|
DOCKERFILE_DIR = resolveDockerDir();
|
|
565
603
|
LABELS = {
|
|
566
604
|
dockerHash: "org.isol8.build.hash",
|
|
567
|
-
depsHash: "org.isol8.deps.hash"
|
|
605
|
+
depsHash: "org.isol8.deps.hash",
|
|
606
|
+
runtime: "org.isol8.runtime",
|
|
607
|
+
dependencies: "org.isol8.dependencies",
|
|
608
|
+
setupScript: "org.isol8.setup"
|
|
568
609
|
};
|
|
569
610
|
DOCKER_BUILD_FILES = ["Dockerfile", "proxy.sh", "proxy-handler.sh"];
|
|
570
611
|
});
|
|
@@ -828,7 +869,6 @@ var DEFAULT_CONFIG = {
|
|
|
828
869
|
},
|
|
829
870
|
poolStrategy: "fast",
|
|
830
871
|
poolSize: { clean: 1, dirty: 1 },
|
|
831
|
-
dependencies: {},
|
|
832
872
|
security: {
|
|
833
873
|
seccomp: "strict"
|
|
834
874
|
},
|
|
@@ -869,6 +909,7 @@ var DEFAULT_CONFIG = {
|
|
|
869
909
|
defaultTtlMs: 86400000,
|
|
870
910
|
cleanupIntervalMs: 3600000
|
|
871
911
|
},
|
|
912
|
+
prebuiltImages: [],
|
|
872
913
|
debug: false
|
|
873
914
|
};
|
|
874
915
|
function loadConfig(cwd) {
|
|
@@ -903,10 +944,6 @@ function mergeConfig(defaults, overrides) {
|
|
|
903
944
|
},
|
|
904
945
|
poolStrategy: overrides.poolStrategy ?? defaults.poolStrategy,
|
|
905
946
|
poolSize: overrides.poolSize ?? defaults.poolSize,
|
|
906
|
-
dependencies: {
|
|
907
|
-
...defaults.dependencies,
|
|
908
|
-
...overrides.dependencies
|
|
909
|
-
},
|
|
910
947
|
security: {
|
|
911
948
|
seccomp: overrides.security?.seccomp ?? defaults.security.seccomp,
|
|
912
949
|
customProfilePath: overrides.security?.customProfilePath ?? defaults.security.customProfilePath
|
|
@@ -926,6 +963,7 @@ function mergeConfig(defaults, overrides) {
|
|
|
926
963
|
...defaults.auth,
|
|
927
964
|
...overrides.auth
|
|
928
965
|
},
|
|
966
|
+
prebuiltImages: overrides.prebuiltImages ?? defaults.prebuiltImages,
|
|
929
967
|
debug: overrides.debug ?? defaults.debug
|
|
930
968
|
};
|
|
931
969
|
}
|
|
@@ -1345,11 +1383,9 @@ var EMBEDDED_DEFAULT_SECCOMP_PROFILE = JSON.stringify({
|
|
|
1345
1383
|
]
|
|
1346
1384
|
});
|
|
1347
1385
|
|
|
1348
|
-
// src/engine/docker.ts
|
|
1349
|
-
init_image_builder();
|
|
1350
|
-
|
|
1351
1386
|
// src/engine/managers/execution-manager.ts
|
|
1352
1387
|
init_logger();
|
|
1388
|
+
init_utils();
|
|
1353
1389
|
import { PassThrough } from "node:stream";
|
|
1354
1390
|
|
|
1355
1391
|
class ExecutionManager {
|
|
@@ -1382,6 +1418,8 @@ class ExecutionManager {
|
|
|
1382
1418
|
return ["npm", "install", "--prefix", "/sandbox", ...packages];
|
|
1383
1419
|
case "bun":
|
|
1384
1420
|
return ["bun", "install", "-g", "--global-dir=/sandbox/.bun-global", ...packages];
|
|
1421
|
+
case "agent":
|
|
1422
|
+
return ["bun", "install", "-g", "--global-dir=/sandbox/.bun-global", ...packages];
|
|
1385
1423
|
case "deno":
|
|
1386
1424
|
return ["sh", "-c", packages.map((p) => `deno cache ${p}`).join(" && ")];
|
|
1387
1425
|
case "bash":
|
|
@@ -1409,7 +1447,7 @@ class ExecutionManager {
|
|
|
1409
1447
|
env.push("npm_config_fetch_retry_mintimeout=1000");
|
|
1410
1448
|
env.push("NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT=2000");
|
|
1411
1449
|
env.push("npm_config_fetch_retry_maxtimeout=2000");
|
|
1412
|
-
} else if (runtime === "bun") {
|
|
1450
|
+
} else if (runtime === "bun" || runtime === "agent") {
|
|
1413
1451
|
env.push("BUN_INSTALL_GLOBAL_DIR=/sandbox/.bun-global");
|
|
1414
1452
|
env.push("BUN_INSTALL_CACHE_DIR=/sandbox/.bun-cache");
|
|
1415
1453
|
env.push("BUN_INSTALL_BIN=/sandbox/.bun-global/bin");
|
|
@@ -1453,6 +1491,63 @@ class ExecutionManager {
|
|
|
1453
1491
|
stream.on("error", reject);
|
|
1454
1492
|
});
|
|
1455
1493
|
}
|
|
1494
|
+
async runSetupScript(container, script, timeoutMs, volumeManager) {
|
|
1495
|
+
const scriptPath = "/sandbox/.isol8-setup.sh";
|
|
1496
|
+
await volumeManager.writeFileViaExec(container, scriptPath, script);
|
|
1497
|
+
const chmodExec = await container.exec({
|
|
1498
|
+
Cmd: ["chmod", "+x", scriptPath],
|
|
1499
|
+
User: "sandbox"
|
|
1500
|
+
});
|
|
1501
|
+
await chmodExec.start({ Detach: true });
|
|
1502
|
+
let chmodInfo = await chmodExec.inspect();
|
|
1503
|
+
while (chmodInfo.Running) {
|
|
1504
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
1505
|
+
chmodInfo = await chmodExec.inspect();
|
|
1506
|
+
}
|
|
1507
|
+
const timeoutSec = Math.max(1, Math.ceil(timeoutMs / 1000));
|
|
1508
|
+
const cmd = this.wrapWithTimeout(["bash", scriptPath], timeoutSec);
|
|
1509
|
+
logger.debug(`Running setup script: ${JSON.stringify(cmd)}`);
|
|
1510
|
+
const env = [
|
|
1511
|
+
"PATH=/sandbox/.local/bin:/sandbox/.npm-global/bin:/sandbox/.bun-global/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"
|
|
1512
|
+
];
|
|
1513
|
+
const exec = await container.exec({
|
|
1514
|
+
Cmd: cmd,
|
|
1515
|
+
AttachStdout: true,
|
|
1516
|
+
AttachStderr: true,
|
|
1517
|
+
Env: env,
|
|
1518
|
+
WorkingDir: "/sandbox",
|
|
1519
|
+
User: "sandbox"
|
|
1520
|
+
});
|
|
1521
|
+
const stream = await exec.start({ Detach: false, Tty: false });
|
|
1522
|
+
return new Promise((resolve2, reject) => {
|
|
1523
|
+
let stderr = "";
|
|
1524
|
+
const stdoutStream = new PassThrough;
|
|
1525
|
+
const stderrStream = new PassThrough;
|
|
1526
|
+
container.modem.demuxStream(stream, stdoutStream, stderrStream);
|
|
1527
|
+
stderrStream.on("data", (chunk) => {
|
|
1528
|
+
const text = chunk.toString();
|
|
1529
|
+
stderr += text;
|
|
1530
|
+
logger.debug(`[setup:stderr] ${text.trimEnd()}`);
|
|
1531
|
+
});
|
|
1532
|
+
stdoutStream.on("data", (chunk) => {
|
|
1533
|
+
const text = chunk.toString();
|
|
1534
|
+
logger.debug(`[setup:stdout] ${text.trimEnd()}`);
|
|
1535
|
+
});
|
|
1536
|
+
stream.on("end", async () => {
|
|
1537
|
+
try {
|
|
1538
|
+
const info = await exec.inspect();
|
|
1539
|
+
if (info.ExitCode !== 0) {
|
|
1540
|
+
reject(new Error(`Setup script failed (exit code ${info.ExitCode}): ${stderr}`));
|
|
1541
|
+
} else {
|
|
1542
|
+
resolve2();
|
|
1543
|
+
}
|
|
1544
|
+
} catch (err) {
|
|
1545
|
+
reject(err);
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
stream.on("error", reject);
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1456
1551
|
async* streamExecOutput(stream, exec, container, timeoutMs) {
|
|
1457
1552
|
const queue = [];
|
|
1458
1553
|
let resolve2 = null;
|
|
@@ -1707,6 +1802,7 @@ class NetworkManager {
|
|
|
1707
1802
|
}
|
|
1708
1803
|
}
|
|
1709
1804
|
// src/engine/managers/volume-manager.ts
|
|
1805
|
+
init_utils();
|
|
1710
1806
|
import { PassThrough as PassThrough2 } from "node:stream";
|
|
1711
1807
|
|
|
1712
1808
|
class VolumeManager {
|
|
@@ -1787,13 +1883,13 @@ class VolumeManager {
|
|
|
1787
1883
|
const b64Output = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1788
1884
|
return Buffer.from(b64Output, "base64");
|
|
1789
1885
|
}
|
|
1790
|
-
async getFileFromContainer(container,
|
|
1791
|
-
const stream = await container.getArchive({ path });
|
|
1886
|
+
async getFileFromContainer(container, path2) {
|
|
1887
|
+
const stream = await container.getArchive({ path: path2 });
|
|
1792
1888
|
const chunks = [];
|
|
1793
1889
|
for await (const chunk of stream) {
|
|
1794
1890
|
chunks.push(chunk);
|
|
1795
1891
|
}
|
|
1796
|
-
return extractFromTar(Buffer.concat(chunks),
|
|
1892
|
+
return extractFromTar(Buffer.concat(chunks), path2);
|
|
1797
1893
|
}
|
|
1798
1894
|
async retrieveFiles(container, paths) {
|
|
1799
1895
|
const files = {};
|
|
@@ -1805,19 +1901,19 @@ class VolumeManager {
|
|
|
1805
1901
|
}
|
|
1806
1902
|
return files;
|
|
1807
1903
|
}
|
|
1808
|
-
async putFile(container,
|
|
1904
|
+
async putFile(container, path2, content) {
|
|
1809
1905
|
if (this.readonlyRootFs) {
|
|
1810
|
-
await this.writeFileViaExec(container,
|
|
1906
|
+
await this.writeFileViaExec(container, path2, content);
|
|
1811
1907
|
} else {
|
|
1812
|
-
const tar = createTarBuffer(
|
|
1908
|
+
const tar = createTarBuffer(path2, content);
|
|
1813
1909
|
await container.putArchive(tar, { path: "/" });
|
|
1814
1910
|
}
|
|
1815
1911
|
}
|
|
1816
|
-
async getFile(container,
|
|
1912
|
+
async getFile(container, path2) {
|
|
1817
1913
|
if (this.readonlyRootFs) {
|
|
1818
|
-
return this.readFileViaExec(container,
|
|
1914
|
+
return this.readFileViaExec(container, path2);
|
|
1819
1915
|
}
|
|
1820
|
-
return this.getFileFromContainer(container,
|
|
1916
|
+
return this.getFileFromContainer(container, path2);
|
|
1821
1917
|
}
|
|
1822
1918
|
}
|
|
1823
1919
|
// src/engine/pool.ts
|
|
@@ -2099,6 +2195,7 @@ function calculateResourceDelta(before, after) {
|
|
|
2099
2195
|
}
|
|
2100
2196
|
|
|
2101
2197
|
// src/engine/docker.ts
|
|
2198
|
+
init_utils();
|
|
2102
2199
|
var SANDBOX_WORKDIR = "/sandbox";
|
|
2103
2200
|
var MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
2104
2201
|
|
|
@@ -2123,7 +2220,6 @@ class DockerIsol8 {
|
|
|
2123
2220
|
logNetwork;
|
|
2124
2221
|
poolStrategy;
|
|
2125
2222
|
poolSize;
|
|
2126
|
-
dependencies;
|
|
2127
2223
|
auditLogger;
|
|
2128
2224
|
remoteCodePolicy;
|
|
2129
2225
|
networkManager;
|
|
@@ -2173,7 +2269,6 @@ class DockerIsol8 {
|
|
|
2173
2269
|
this.logNetwork = options.logNetwork ?? false;
|
|
2174
2270
|
this.poolStrategy = options.poolStrategy ?? "fast";
|
|
2175
2271
|
this.poolSize = options.poolSize ?? { clean: 1, dirty: 1 };
|
|
2176
|
-
this.dependencies = options.dependencies ?? {};
|
|
2177
2272
|
this.remoteCodePolicy = options.remoteCode ?? {
|
|
2178
2273
|
enabled: false,
|
|
2179
2274
|
allowedSchemes: ["https"],
|
|
@@ -2217,7 +2312,8 @@ class DockerIsol8 {
|
|
|
2217
2312
|
const adapters2 = typeof prewarm === "object" && prewarm.runtimes?.length ? prewarm.runtimes.map((runtime) => RuntimeRegistry.get(runtime)) : RuntimeRegistry.list();
|
|
2218
2313
|
for (const adapter of adapters2) {
|
|
2219
2314
|
try {
|
|
2220
|
-
|
|
2315
|
+
const resolved = await this.resolveImage(adapter);
|
|
2316
|
+
images.add(resolved.image);
|
|
2221
2317
|
} catch (err) {
|
|
2222
2318
|
logger.debug(`[Pool] Pre-warm image resolution failed for ${adapter.name}: ${err}`);
|
|
2223
2319
|
}
|
|
@@ -2251,6 +2347,7 @@ class DockerIsol8 {
|
|
|
2251
2347
|
await this.semaphore.acquire();
|
|
2252
2348
|
const startTime = Date.now();
|
|
2253
2349
|
try {
|
|
2350
|
+
this.validateAgentRuntime(req);
|
|
2254
2351
|
const request = await this.resolveExecutionRequest(req);
|
|
2255
2352
|
const result = this.mode === "persistent" ? await this.executePersistent(request, startTime) : await this.executeEphemeral(request, startTime);
|
|
2256
2353
|
return result;
|
|
@@ -2374,17 +2471,17 @@ class DockerIsol8 {
|
|
|
2374
2471
|
} catch {}
|
|
2375
2472
|
return logs;
|
|
2376
2473
|
}
|
|
2377
|
-
async putFile(
|
|
2474
|
+
async putFile(path2, content) {
|
|
2378
2475
|
if (!this.container) {
|
|
2379
2476
|
throw new Error("No active container. Call execute() first in persistent mode.");
|
|
2380
2477
|
}
|
|
2381
|
-
await this.volumeManager.putFile(this.container,
|
|
2478
|
+
await this.volumeManager.putFile(this.container, path2, content);
|
|
2382
2479
|
}
|
|
2383
|
-
async getFile(
|
|
2480
|
+
async getFile(path2) {
|
|
2384
2481
|
if (!this.container) {
|
|
2385
2482
|
throw new Error("No active container. Call execute() first in persistent mode.");
|
|
2386
2483
|
}
|
|
2387
|
-
return this.volumeManager.getFile(this.container,
|
|
2484
|
+
return this.volumeManager.getFile(this.container, path2);
|
|
2388
2485
|
}
|
|
2389
2486
|
get containerId() {
|
|
2390
2487
|
return this.container?.id ?? null;
|
|
@@ -2392,10 +2489,13 @@ class DockerIsol8 {
|
|
|
2392
2489
|
async* executeStream(req) {
|
|
2393
2490
|
await this.semaphore.acquire();
|
|
2394
2491
|
try {
|
|
2492
|
+
this.validateAgentRuntime(req);
|
|
2395
2493
|
const request = await this.resolveExecutionRequest(req);
|
|
2396
2494
|
const adapter = this.getAdapter(request.runtime);
|
|
2397
2495
|
const timeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;
|
|
2398
|
-
const
|
|
2496
|
+
const resolved = await this.resolveImage(adapter, request.installPackages);
|
|
2497
|
+
const image = resolved.image;
|
|
2498
|
+
const execWorkdir = request.workdir ? resolveWorkdir(request.workdir) : SANDBOX_WORKDIR;
|
|
2399
2499
|
const container = await this.docker.createContainer({
|
|
2400
2500
|
Image: image,
|
|
2401
2501
|
Cmd: ["sleep", "infinity"],
|
|
@@ -2412,15 +2512,21 @@ class DockerIsol8 {
|
|
|
2412
2512
|
const ext = request.fileExtension ?? adapter.getFileExtension();
|
|
2413
2513
|
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
2414
2514
|
await this.volumeManager.writeFileViaExec(container, filePath, request.code);
|
|
2415
|
-
if (
|
|
2416
|
-
await this.executionManager.installPackages(container, request.runtime,
|
|
2515
|
+
if (resolved.remainingPackages.length > 0) {
|
|
2516
|
+
await this.executionManager.installPackages(container, request.runtime, resolved.remainingPackages, timeoutMs);
|
|
2517
|
+
}
|
|
2518
|
+
if (resolved.imageSetupScript) {
|
|
2519
|
+
await this.executionManager.runSetupScript(container, resolved.imageSetupScript, timeoutMs, this.volumeManager);
|
|
2520
|
+
}
|
|
2521
|
+
if (request.setupScript) {
|
|
2522
|
+
await this.executionManager.runSetupScript(container, request.setupScript, timeoutMs, this.volumeManager);
|
|
2417
2523
|
}
|
|
2418
2524
|
if (request.files) {
|
|
2419
2525
|
for (const [fPath, fContent] of Object.entries(request.files)) {
|
|
2420
2526
|
await this.volumeManager.writeFileViaExec(container, fPath, fContent);
|
|
2421
2527
|
}
|
|
2422
2528
|
}
|
|
2423
|
-
const rawCmd =
|
|
2529
|
+
const rawCmd = this.buildAdapterCommand(adapter, request, filePath);
|
|
2424
2530
|
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
2425
2531
|
let cmd;
|
|
2426
2532
|
if (request.stdin) {
|
|
@@ -2436,7 +2542,7 @@ class DockerIsol8 {
|
|
|
2436
2542
|
Env: this.executionManager.buildEnv(request.env, this.networkManager.proxyPort, this.network, this.networkFilter),
|
|
2437
2543
|
AttachStdout: true,
|
|
2438
2544
|
AttachStderr: true,
|
|
2439
|
-
WorkingDir:
|
|
2545
|
+
WorkingDir: execWorkdir,
|
|
2440
2546
|
User: "sandbox"
|
|
2441
2547
|
});
|
|
2442
2548
|
const execStream = await exec.start({ Tty: false });
|
|
@@ -2454,55 +2560,90 @@ class DockerIsol8 {
|
|
|
2454
2560
|
this.semaphore.release();
|
|
2455
2561
|
}
|
|
2456
2562
|
}
|
|
2457
|
-
async resolveImage(adapter) {
|
|
2563
|
+
async resolveImage(adapter, requestedPackages) {
|
|
2458
2564
|
if (this.overrideImage) {
|
|
2459
|
-
|
|
2565
|
+
let imageSetupScript2;
|
|
2566
|
+
try {
|
|
2567
|
+
const { LABELS: LABELS2 } = await Promise.resolve().then(() => (init_image_builder(), exports_image_builder));
|
|
2568
|
+
const inspect = await this.docker.getImage(this.overrideImage).inspect();
|
|
2569
|
+
const labels = inspect.Config?.Labels ?? {};
|
|
2570
|
+
imageSetupScript2 = labels[LABELS2.setupScript] || undefined;
|
|
2571
|
+
} catch {}
|
|
2572
|
+
return {
|
|
2573
|
+
image: this.overrideImage,
|
|
2574
|
+
remainingPackages: requestedPackages ?? [],
|
|
2575
|
+
imageSetupScript: imageSetupScript2
|
|
2576
|
+
};
|
|
2460
2577
|
}
|
|
2461
|
-
const cacheKey = adapter.
|
|
2578
|
+
const cacheKey = `${adapter.name}:${(requestedPackages ?? []).join(",")}`;
|
|
2462
2579
|
const cached = this.imageCache.get(cacheKey);
|
|
2463
2580
|
if (cached) {
|
|
2464
|
-
|
|
2465
|
-
}
|
|
2466
|
-
let resolvedImage = adapter.image;
|
|
2467
|
-
const configuredDeps = this.dependencies[adapter.name];
|
|
2468
|
-
const normalizedDeps = configuredDeps ? normalizePackages(configuredDeps) : [];
|
|
2469
|
-
if (normalizedDeps.length > 0) {
|
|
2470
|
-
const hashedCustomTag = getCustomImageTag(adapter.name, normalizedDeps);
|
|
2581
|
+
let imageSetupScript2;
|
|
2471
2582
|
try {
|
|
2472
|
-
await
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2583
|
+
const { LABELS: LABELS2 } = await Promise.resolve().then(() => (init_image_builder(), exports_image_builder));
|
|
2584
|
+
const inspect = await this.docker.getImage(cached).inspect();
|
|
2585
|
+
const labels = inspect.Config?.Labels ?? {};
|
|
2586
|
+
imageSetupScript2 = labels[LABELS2.setupScript] || undefined;
|
|
2587
|
+
} catch {}
|
|
2588
|
+
return { image: cached, remainingPackages: [], imageSetupScript: imageSetupScript2 };
|
|
2589
|
+
}
|
|
2590
|
+
const baseImage = adapter.image;
|
|
2591
|
+
let bestImage = baseImage;
|
|
2592
|
+
let remainingPackages = requestedPackages ?? [];
|
|
2593
|
+
let imageSetupScript;
|
|
2594
|
+
if (requestedPackages && requestedPackages.length > 0) {
|
|
2595
|
+
const { LABELS: LABELS2, normalizePackages: normalizePackages2 } = await Promise.resolve().then(() => (init_image_builder(), exports_image_builder));
|
|
2596
|
+
const normalizedReq = normalizePackages2(requestedPackages);
|
|
2597
|
+
const images = await this.docker.listImages({
|
|
2598
|
+
filters: {
|
|
2599
|
+
label: [`${LABELS2.runtime}=${adapter.name}`]
|
|
2600
|
+
}
|
|
2601
|
+
});
|
|
2602
|
+
for (const img of images) {
|
|
2603
|
+
if (!img.RepoTags || img.RepoTags.length === 0) {
|
|
2604
|
+
continue;
|
|
2605
|
+
}
|
|
2606
|
+
const depsLabel = img.Labels?.[LABELS2.dependencies];
|
|
2607
|
+
if (!depsLabel) {
|
|
2608
|
+
continue;
|
|
2609
|
+
}
|
|
2610
|
+
const imgDeps = depsLabel.split(",");
|
|
2611
|
+
if (img.RepoTags[0] && normalizedReq.length === imgDeps.length && normalizedReq.every((p) => imgDeps.includes(p))) {
|
|
2612
|
+
bestImage = img.RepoTags[0];
|
|
2613
|
+
remainingPackages = [];
|
|
2614
|
+
imageSetupScript = img.Labels?.[LABELS2.setupScript] || undefined;
|
|
2615
|
+
logger.debug(`[Docker] Found exact custom image match: ${bestImage}`);
|
|
2616
|
+
break;
|
|
2617
|
+
}
|
|
2618
|
+
if (img.RepoTags[0] && normalizedReq.every((p) => imgDeps.includes(p))) {
|
|
2619
|
+
bestImage = img.RepoTags[0];
|
|
2620
|
+
remainingPackages = [];
|
|
2621
|
+
imageSetupScript = img.Labels?.[LABELS2.setupScript] || undefined;
|
|
2622
|
+
logger.debug(`[Docker] Found superset custom image match: ${bestImage}`);
|
|
2623
|
+
}
|
|
2476
2624
|
}
|
|
2477
2625
|
}
|
|
2478
|
-
if (
|
|
2479
|
-
const legacyCustomTag = `${adapter.image}-custom`;
|
|
2626
|
+
if (bestImage !== baseImage && imageSetupScript === undefined) {
|
|
2480
2627
|
try {
|
|
2481
|
-
await
|
|
2482
|
-
|
|
2628
|
+
const { LABELS: LABELS2 } = await Promise.resolve().then(() => (init_image_builder(), exports_image_builder));
|
|
2629
|
+
const inspect = await this.docker.getImage(bestImage).inspect();
|
|
2630
|
+
const labels = inspect.Config?.Labels ?? {};
|
|
2631
|
+
imageSetupScript = labels[LABELS2.setupScript] || undefined;
|
|
2483
2632
|
} catch {}
|
|
2484
2633
|
}
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
try {
|
|
2492
|
-
await this.docker.getImage(adapter.image).inspect();
|
|
2493
|
-
} catch {
|
|
2494
|
-
logger.debug(`[ImageBuilder] Base image ${adapter.image} missing. Building...`);
|
|
2495
|
-
await buildBaseImages2(this.docker, undefined, false, [adapter.name]);
|
|
2496
|
-
}
|
|
2497
|
-
logger.debug(`[ImageBuilder] Building custom image for ${adapter.name}...`);
|
|
2498
|
-
await buildCustomImage2(this.docker, adapter.name, normalizedDeps);
|
|
2499
|
-
} else {
|
|
2500
|
-
logger.debug(`[ImageBuilder] Building base image for ${adapter.name}...`);
|
|
2634
|
+
if (bestImage === baseImage) {
|
|
2635
|
+
try {
|
|
2636
|
+
await this.docker.getImage(baseImage).inspect();
|
|
2637
|
+
} catch {
|
|
2638
|
+
logger.debug(`[Docker] Base image ${baseImage} not found. Building...`);
|
|
2639
|
+
const { buildBaseImages: buildBaseImages2 } = await Promise.resolve().then(() => (init_image_builder(), exports_image_builder));
|
|
2501
2640
|
await buildBaseImages2(this.docker, undefined, false, [adapter.name]);
|
|
2502
2641
|
}
|
|
2503
2642
|
}
|
|
2504
|
-
|
|
2505
|
-
|
|
2643
|
+
if (remainingPackages.length === 0) {
|
|
2644
|
+
this.imageCache.set(cacheKey, bestImage);
|
|
2645
|
+
}
|
|
2646
|
+
return { image: bestImage, remainingPackages, imageSetupScript };
|
|
2506
2647
|
}
|
|
2507
2648
|
ensurePool() {
|
|
2508
2649
|
if (!this.pool) {
|
|
@@ -2527,7 +2668,9 @@ class DockerIsol8 {
|
|
|
2527
2668
|
async executeEphemeral(req, startTime) {
|
|
2528
2669
|
const adapter = this.getAdapter(req.runtime);
|
|
2529
2670
|
const timeoutMs = req.timeoutMs ?? this.defaultTimeoutMs;
|
|
2530
|
-
const
|
|
2671
|
+
const resolved = await this.resolveImage(adapter, req.installPackages);
|
|
2672
|
+
const image = resolved.image;
|
|
2673
|
+
const execWorkdir = req.workdir ? resolveWorkdir(req.workdir) : SANDBOX_WORKDIR;
|
|
2531
2674
|
const pool = this.ensurePool();
|
|
2532
2675
|
const container = await pool.acquire(image);
|
|
2533
2676
|
let startStats;
|
|
@@ -2545,21 +2688,27 @@ class DockerIsol8 {
|
|
|
2545
2688
|
let rawCmd;
|
|
2546
2689
|
if (canUseInline) {
|
|
2547
2690
|
try {
|
|
2548
|
-
rawCmd =
|
|
2691
|
+
rawCmd = this.buildAdapterCommand(adapter, req);
|
|
2549
2692
|
} catch {
|
|
2550
2693
|
const ext = req.fileExtension ?? adapter.getFileExtension();
|
|
2551
2694
|
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
2552
2695
|
await this.volumeManager.writeFileViaExec(container, filePath, req.code);
|
|
2553
|
-
rawCmd =
|
|
2696
|
+
rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2554
2697
|
}
|
|
2555
2698
|
} else {
|
|
2556
2699
|
const ext = req.fileExtension ?? adapter.getFileExtension();
|
|
2557
2700
|
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
2558
2701
|
await this.volumeManager.writeFileViaExec(container, filePath, req.code);
|
|
2559
|
-
rawCmd =
|
|
2702
|
+
rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2703
|
+
}
|
|
2704
|
+
if (resolved.remainingPackages.length > 0) {
|
|
2705
|
+
await this.executionManager.installPackages(container, req.runtime, resolved.remainingPackages, timeoutMs);
|
|
2560
2706
|
}
|
|
2561
|
-
if (
|
|
2562
|
-
await this.executionManager.
|
|
2707
|
+
if (resolved.imageSetupScript) {
|
|
2708
|
+
await this.executionManager.runSetupScript(container, resolved.imageSetupScript, timeoutMs, this.volumeManager);
|
|
2709
|
+
}
|
|
2710
|
+
if (req.setupScript) {
|
|
2711
|
+
await this.executionManager.runSetupScript(container, req.setupScript, timeoutMs, this.volumeManager);
|
|
2563
2712
|
}
|
|
2564
2713
|
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
2565
2714
|
let cmd;
|
|
@@ -2581,7 +2730,7 @@ class DockerIsol8 {
|
|
|
2581
2730
|
Env: this.executionManager.buildEnv(req.env, this.networkManager.proxyPort, this.network, this.networkFilter),
|
|
2582
2731
|
AttachStdout: true,
|
|
2583
2732
|
AttachStderr: true,
|
|
2584
|
-
WorkingDir:
|
|
2733
|
+
WorkingDir: execWorkdir,
|
|
2585
2734
|
User: "sandbox"
|
|
2586
2735
|
});
|
|
2587
2736
|
const start = performance.now();
|
|
@@ -2641,8 +2790,13 @@ class DockerIsol8 {
|
|
|
2641
2790
|
async executePersistent(req, startTime) {
|
|
2642
2791
|
const adapter = this.getAdapter(req.runtime);
|
|
2643
2792
|
const timeoutMs = req.timeoutMs ?? this.defaultTimeoutMs;
|
|
2793
|
+
const execWorkdir = req.workdir ? resolveWorkdir(req.workdir) : SANDBOX_WORKDIR;
|
|
2794
|
+
let remainingPackages = req.installPackages ?? [];
|
|
2795
|
+
let imageSetupScript;
|
|
2644
2796
|
if (!this.container) {
|
|
2645
|
-
await this.startPersistentContainer(adapter);
|
|
2797
|
+
const started = await this.startPersistentContainer(adapter, req.installPackages);
|
|
2798
|
+
remainingPackages = started.remainingPackages;
|
|
2799
|
+
imageSetupScript = started.imageSetupScript;
|
|
2646
2800
|
} else if (this.persistentRuntime?.name !== adapter.name) {
|
|
2647
2801
|
throw new Error(`Cannot switch runtime from "${this.persistentRuntime?.name}" to "${adapter.name}". Each persistent container supports a single runtime. Create a new Isol8 instance for a different runtime.`);
|
|
2648
2802
|
}
|
|
@@ -2654,10 +2808,16 @@ class DockerIsol8 {
|
|
|
2654
2808
|
await this.volumeManager.putFile(this.container, fPath, fContent);
|
|
2655
2809
|
}
|
|
2656
2810
|
}
|
|
2657
|
-
const rawCmd =
|
|
2811
|
+
const rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2658
2812
|
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
2659
|
-
if (
|
|
2660
|
-
await this.executionManager.installPackages(this.container, req.runtime,
|
|
2813
|
+
if (remainingPackages.length > 0) {
|
|
2814
|
+
await this.executionManager.installPackages(this.container, req.runtime, remainingPackages, timeoutMs);
|
|
2815
|
+
}
|
|
2816
|
+
if (imageSetupScript) {
|
|
2817
|
+
await this.executionManager.runSetupScript(this.container, imageSetupScript, timeoutMs, this.volumeManager);
|
|
2818
|
+
}
|
|
2819
|
+
if (req.setupScript) {
|
|
2820
|
+
await this.executionManager.runSetupScript(this.container, req.setupScript, timeoutMs, this.volumeManager);
|
|
2661
2821
|
}
|
|
2662
2822
|
let cmd;
|
|
2663
2823
|
if (req.stdin) {
|
|
@@ -2674,7 +2834,7 @@ class DockerIsol8 {
|
|
|
2674
2834
|
Env: execEnv,
|
|
2675
2835
|
AttachStdout: true,
|
|
2676
2836
|
AttachStderr: true,
|
|
2677
|
-
WorkingDir:
|
|
2837
|
+
WorkingDir: execWorkdir,
|
|
2678
2838
|
User: "sandbox"
|
|
2679
2839
|
});
|
|
2680
2840
|
const start = performance.now();
|
|
@@ -2729,10 +2889,10 @@ class DockerIsol8 {
|
|
|
2729
2889
|
async retrieveFiles(container, paths) {
|
|
2730
2890
|
return this.volumeManager.retrieveFiles(container, paths);
|
|
2731
2891
|
}
|
|
2732
|
-
async startPersistentContainer(adapter) {
|
|
2733
|
-
const
|
|
2892
|
+
async startPersistentContainer(adapter, requestedPackages) {
|
|
2893
|
+
const resolved = await this.resolveImage(adapter, requestedPackages);
|
|
2734
2894
|
this.container = await this.docker.createContainer({
|
|
2735
|
-
Image: image,
|
|
2895
|
+
Image: resolved.image,
|
|
2736
2896
|
Cmd: ["sleep", "infinity"],
|
|
2737
2897
|
WorkingDir: SANDBOX_WORKDIR,
|
|
2738
2898
|
Env: this.executionManager.buildEnv(undefined, this.networkManager.proxyPort, this.network, this.networkFilter),
|
|
@@ -2748,10 +2908,35 @@ class DockerIsol8 {
|
|
|
2748
2908
|
await this.networkManager.startProxy(this.container);
|
|
2749
2909
|
await this.networkManager.setupIptables(this.container);
|
|
2750
2910
|
this.persistentRuntime = adapter;
|
|
2911
|
+
return {
|
|
2912
|
+
remainingPackages: resolved.remainingPackages,
|
|
2913
|
+
imageSetupScript: resolved.imageSetupScript
|
|
2914
|
+
};
|
|
2751
2915
|
}
|
|
2752
2916
|
getAdapter(runtime) {
|
|
2753
2917
|
return RuntimeRegistry.get(runtime);
|
|
2754
2918
|
}
|
|
2919
|
+
validateAgentRuntime(req) {
|
|
2920
|
+
if (req.runtime !== "agent") {
|
|
2921
|
+
return;
|
|
2922
|
+
}
|
|
2923
|
+
if (this.network !== "filtered") {
|
|
2924
|
+
throw new Error(`Agent runtime requires network mode "filtered". The AI coding agent needs network access to reach its LLM provider API. Use --net filtered --allow "api.anthropic.com" (or your provider's domain).`);
|
|
2925
|
+
}
|
|
2926
|
+
const whitelist = this.networkFilter?.whitelist ?? [];
|
|
2927
|
+
if (whitelist.length === 0) {
|
|
2928
|
+
throw new Error(`Agent runtime requires at least one network whitelist entry. The AI coding agent needs to reach its LLM provider API. Use --allow "api.anthropic.com" (or your provider's domain).`);
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
buildAdapterCommand(adapter, req, filePath) {
|
|
2932
|
+
if (adapter.getCommandWithOptions) {
|
|
2933
|
+
return adapter.getCommandWithOptions(req.code, {
|
|
2934
|
+
filePath,
|
|
2935
|
+
agentFlags: req.agentFlags
|
|
2936
|
+
});
|
|
2937
|
+
}
|
|
2938
|
+
return adapter.getCommand(req.code, filePath);
|
|
2939
|
+
}
|
|
2755
2940
|
buildHostConfig() {
|
|
2756
2941
|
const config = {
|
|
2757
2942
|
Memory: parseMemoryLimit(this.memoryLimit),
|
|
@@ -2860,7 +3045,7 @@ init_logger();
|
|
|
2860
3045
|
// package.json
|
|
2861
3046
|
var package_default = {
|
|
2862
3047
|
name: "@isol8/core",
|
|
2863
|
-
version: "0.
|
|
3048
|
+
version: "0.19.0",
|
|
2864
3049
|
description: "Sandboxed code execution engine for AI agents and apps (Docker, runtime and network controls)",
|
|
2865
3050
|
author: "Illusion47586",
|
|
2866
3051
|
license: "MIT",
|
|
@@ -2928,8 +3113,7 @@ var VERSION = package_default.version;
|
|
|
2928
3113
|
export {
|
|
2929
3114
|
logger,
|
|
2930
3115
|
loadConfig,
|
|
2931
|
-
|
|
2932
|
-
buildCustomImages,
|
|
3116
|
+
imageExists,
|
|
2933
3117
|
buildCustomImage,
|
|
2934
3118
|
buildBaseImages,
|
|
2935
3119
|
bashAdapter,
|
|
@@ -2939,9 +3123,11 @@ export {
|
|
|
2939
3123
|
RemoteIsol8,
|
|
2940
3124
|
PythonAdapter,
|
|
2941
3125
|
NodeAdapter,
|
|
3126
|
+
LABELS,
|
|
2942
3127
|
DockerIsol8,
|
|
2943
3128
|
DenoAdapter,
|
|
2944
|
-
BunAdapter
|
|
3129
|
+
BunAdapter,
|
|
3130
|
+
AgentAdapter
|
|
2945
3131
|
};
|
|
2946
3132
|
|
|
2947
|
-
//# debugId=
|
|
3133
|
+
//# debugId=944C4599E61DE68D64756E2164756E21
|