@isol8/core 0.19.0 → 0.20.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/docker/Dockerfile +1 -1
- package/dist/engine/docker.d.ts +17 -2
- package/dist/engine/docker.d.ts.map +1 -1
- package/dist/engine/managers/execution-manager.d.ts +1 -1
- package/dist/engine/managers/execution-manager.d.ts.map +1 -1
- package/dist/index.js +321 -125
- package/dist/index.js.map +4 -4
- package/dist/types.d.ts +18 -2
- package/dist/types.d.ts.map +1 -1
- package/docker/Dockerfile +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1491,7 +1491,7 @@ class ExecutionManager {
|
|
|
1491
1491
|
stream.on("error", reject);
|
|
1492
1492
|
});
|
|
1493
1493
|
}
|
|
1494
|
-
async runSetupScript(container, script, timeoutMs, volumeManager) {
|
|
1494
|
+
async* runSetupScript(container, script, timeoutMs, volumeManager) {
|
|
1495
1495
|
const scriptPath = "/sandbox/.isol8-setup.sh";
|
|
1496
1496
|
await volumeManager.writeFileViaExec(container, scriptPath, script);
|
|
1497
1497
|
const chmodExec = await container.exec({
|
|
@@ -1519,34 +1519,67 @@ class ExecutionManager {
|
|
|
1519
1519
|
User: "sandbox"
|
|
1520
1520
|
});
|
|
1521
1521
|
const stream = await exec.start({ Detach: false, Tty: false });
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1522
|
+
const queue = [];
|
|
1523
|
+
let notify = null;
|
|
1524
|
+
let done = false;
|
|
1525
|
+
const push = (event) => {
|
|
1526
|
+
queue.push(event);
|
|
1527
|
+
if (notify) {
|
|
1528
|
+
notify();
|
|
1529
|
+
notify = null;
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
const timer = setTimeout(() => {
|
|
1533
|
+
push({ type: "error", data: "SETUP SCRIPT TIMED OUT", phase: "setup" });
|
|
1534
|
+
push({ type: "exit", data: "137", phase: "setup" });
|
|
1535
|
+
done = true;
|
|
1536
|
+
}, timeoutMs);
|
|
1537
|
+
const stdoutStream = new PassThrough;
|
|
1538
|
+
const stderrStream = new PassThrough;
|
|
1539
|
+
container.modem.demuxStream(stream, stdoutStream, stderrStream);
|
|
1540
|
+
stdoutStream.on("data", (chunk) => {
|
|
1541
|
+
const text = chunk.toString("utf-8");
|
|
1542
|
+
logger.debug(`[setup:stdout] ${text.trimEnd()}`);
|
|
1543
|
+
push({ type: "stdout", data: text, phase: "setup" });
|
|
1544
|
+
});
|
|
1545
|
+
stderrStream.on("data", (chunk) => {
|
|
1546
|
+
const text = chunk.toString("utf-8");
|
|
1547
|
+
logger.debug(`[setup:stderr] ${text.trimEnd()}`);
|
|
1548
|
+
push({ type: "stderr", data: text, phase: "setup" });
|
|
1549
|
+
});
|
|
1550
|
+
stream.on("end", async () => {
|
|
1551
|
+
clearTimeout(timer);
|
|
1552
|
+
try {
|
|
1553
|
+
const info = await exec.inspect();
|
|
1554
|
+
const exitCode = info.ExitCode ?? 0;
|
|
1555
|
+
if (exitCode !== 0) {
|
|
1556
|
+
push({
|
|
1557
|
+
type: "error",
|
|
1558
|
+
data: `Setup script failed (exit code ${exitCode})`,
|
|
1559
|
+
phase: "setup"
|
|
1560
|
+
});
|
|
1546
1561
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1562
|
+
push({ type: "exit", data: exitCode.toString(), phase: "setup" });
|
|
1563
|
+
} catch {
|
|
1564
|
+
push({ type: "exit", data: "1", phase: "setup" });
|
|
1565
|
+
}
|
|
1566
|
+
done = true;
|
|
1549
1567
|
});
|
|
1568
|
+
stream.on("error", (err) => {
|
|
1569
|
+
clearTimeout(timer);
|
|
1570
|
+
push({ type: "error", data: err.message, phase: "setup" });
|
|
1571
|
+
push({ type: "exit", data: "1", phase: "setup" });
|
|
1572
|
+
done = true;
|
|
1573
|
+
});
|
|
1574
|
+
while (!done || queue.length > 0) {
|
|
1575
|
+
if (queue.length > 0) {
|
|
1576
|
+
yield queue.shift();
|
|
1577
|
+
} else {
|
|
1578
|
+
await new Promise((r) => {
|
|
1579
|
+
notify = r;
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1550
1583
|
}
|
|
1551
1584
|
async* streamExecOutput(stream, exec, container, timeoutMs) {
|
|
1552
1585
|
const queue = [];
|
|
@@ -1560,8 +1593,8 @@ class ExecutionManager {
|
|
|
1560
1593
|
}
|
|
1561
1594
|
};
|
|
1562
1595
|
const timer = setTimeout(() => {
|
|
1563
|
-
push({ type: "error", data: "EXECUTION TIMED OUT" });
|
|
1564
|
-
push({ type: "exit", data: "137" });
|
|
1596
|
+
push({ type: "error", data: "EXECUTION TIMED OUT", phase: "code" });
|
|
1597
|
+
push({ type: "exit", data: "137", phase: "code" });
|
|
1565
1598
|
done = true;
|
|
1566
1599
|
}, timeoutMs);
|
|
1567
1600
|
const stdoutStream = new PassThrough;
|
|
@@ -1572,29 +1605,29 @@ class ExecutionManager {
|
|
|
1572
1605
|
if (Object.keys(this.secrets).length > 0) {
|
|
1573
1606
|
text = maskSecrets(text, this.secrets);
|
|
1574
1607
|
}
|
|
1575
|
-
push({ type: "stdout", data: text });
|
|
1608
|
+
push({ type: "stdout", data: text, phase: "code" });
|
|
1576
1609
|
});
|
|
1577
1610
|
stderrStream.on("data", (chunk) => {
|
|
1578
1611
|
let text = chunk.toString("utf-8");
|
|
1579
1612
|
if (Object.keys(this.secrets).length > 0) {
|
|
1580
1613
|
text = maskSecrets(text, this.secrets);
|
|
1581
1614
|
}
|
|
1582
|
-
push({ type: "stderr", data: text });
|
|
1615
|
+
push({ type: "stderr", data: text, phase: "code" });
|
|
1583
1616
|
});
|
|
1584
1617
|
stream.on("end", async () => {
|
|
1585
1618
|
clearTimeout(timer);
|
|
1586
1619
|
try {
|
|
1587
1620
|
const info = await exec.inspect();
|
|
1588
|
-
push({ type: "exit", data: (info.ExitCode ?? 0).toString() });
|
|
1621
|
+
push({ type: "exit", data: (info.ExitCode ?? 0).toString(), phase: "code" });
|
|
1589
1622
|
} catch {
|
|
1590
|
-
push({ type: "exit", data: "1" });
|
|
1623
|
+
push({ type: "exit", data: "1", phase: "code" });
|
|
1591
1624
|
}
|
|
1592
1625
|
done = true;
|
|
1593
1626
|
});
|
|
1594
1627
|
stream.on("error", (err) => {
|
|
1595
1628
|
clearTimeout(timer);
|
|
1596
|
-
push({ type: "error", data: err.message });
|
|
1597
|
-
push({ type: "exit", data: "1" });
|
|
1629
|
+
push({ type: "error", data: err.message, phase: "code" });
|
|
1630
|
+
push({ type: "exit", data: "1", phase: "code" });
|
|
1598
1631
|
done = true;
|
|
1599
1632
|
});
|
|
1600
1633
|
while (!done || queue.length > 0) {
|
|
@@ -2232,11 +2265,16 @@ class DockerIsol8 {
|
|
|
2232
2265
|
async resolveExecutionRequest(req) {
|
|
2233
2266
|
const inlineCode = req.code?.trim();
|
|
2234
2267
|
const codeUrl = req.codeUrl?.trim();
|
|
2235
|
-
|
|
2236
|
-
|
|
2268
|
+
const cmd = req.cmd?.trim();
|
|
2269
|
+
const sourceCount = [inlineCode, codeUrl, cmd].filter(Boolean).length;
|
|
2270
|
+
if (sourceCount > 1) {
|
|
2271
|
+
throw new Error("ExecutionRequest.code, ExecutionRequest.codeUrl, and ExecutionRequest.cmd are mutually exclusive — provide exactly one.");
|
|
2237
2272
|
}
|
|
2238
|
-
if (
|
|
2239
|
-
throw new Error("ExecutionRequest must include
|
|
2273
|
+
if (sourceCount === 0) {
|
|
2274
|
+
throw new Error("ExecutionRequest must include exactly one of: code, codeUrl, or cmd.");
|
|
2275
|
+
}
|
|
2276
|
+
if (cmd) {
|
|
2277
|
+
return { ...req, cmd: req.cmd };
|
|
2240
2278
|
}
|
|
2241
2279
|
if (inlineCode) {
|
|
2242
2280
|
return { ...req, code: req.code };
|
|
@@ -2358,7 +2396,7 @@ class DockerIsol8 {
|
|
|
2358
2396
|
async recordAudit(req, result, startTime, container) {
|
|
2359
2397
|
try {
|
|
2360
2398
|
const enc = new TextEncoder;
|
|
2361
|
-
const data = enc.encode(req.code);
|
|
2399
|
+
const data = enc.encode(req.code ?? req.cmd ?? "");
|
|
2362
2400
|
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
2363
2401
|
const codeHash = Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2364
2402
|
let securityEvents;
|
|
@@ -2491,73 +2529,171 @@ class DockerIsol8 {
|
|
|
2491
2529
|
try {
|
|
2492
2530
|
this.validateAgentRuntime(req);
|
|
2493
2531
|
const request = await this.resolveExecutionRequest(req);
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2532
|
+
if (this.mode === "persistent") {
|
|
2533
|
+
yield* this.executeStreamPersistent(request);
|
|
2534
|
+
} else {
|
|
2535
|
+
yield* this.executeStreamEphemeral(request);
|
|
2536
|
+
}
|
|
2537
|
+
} finally {
|
|
2538
|
+
this.semaphore.release();
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
async* executeStreamPersistent(request) {
|
|
2542
|
+
const adapter = this.getAdapter(request.runtime);
|
|
2543
|
+
const timeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;
|
|
2544
|
+
const execWorkdir = request.workdir ? resolveWorkdir(request.workdir) : SANDBOX_WORKDIR;
|
|
2545
|
+
let remainingPackages = request.installPackages ?? [];
|
|
2546
|
+
let imageSetupScript;
|
|
2547
|
+
if (!this.container) {
|
|
2548
|
+
const started = await this.startPersistentContainer(adapter, request.installPackages);
|
|
2549
|
+
remainingPackages = started.remainingPackages;
|
|
2550
|
+
imageSetupScript = started.imageSetupScript;
|
|
2551
|
+
} else if (this.persistentRuntime?.name !== adapter.name) {
|
|
2552
|
+
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.`);
|
|
2553
|
+
}
|
|
2554
|
+
let rawCmd;
|
|
2555
|
+
if (request.cmd) {
|
|
2556
|
+
rawCmd = ["bash", "-c", request.cmd];
|
|
2557
|
+
} else {
|
|
2558
|
+
const ext = request.fileExtension ?? adapter.getFileExtension();
|
|
2559
|
+
const filePath = `${SANDBOX_WORKDIR}/exec_${Date.now()}${ext}`;
|
|
2560
|
+
await this.volumeManager.putFile(this.container, filePath, request.code);
|
|
2561
|
+
rawCmd = this.buildAdapterCommand(adapter, request, filePath);
|
|
2562
|
+
}
|
|
2563
|
+
if (request.files) {
|
|
2564
|
+
for (const [fPath, fContent] of Object.entries(request.files)) {
|
|
2565
|
+
await this.volumeManager.putFile(this.container, fPath, fContent);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
if (remainingPackages.length > 0) {
|
|
2569
|
+
await this.executionManager.installPackages(this.container, request.runtime, remainingPackages, timeoutMs);
|
|
2570
|
+
}
|
|
2571
|
+
if (imageSetupScript) {
|
|
2572
|
+
let setupExitCode = 0;
|
|
2573
|
+
for await (const event of this.executionManager.runSetupScript(this.container, imageSetupScript, timeoutMs, this.volumeManager)) {
|
|
2574
|
+
yield event;
|
|
2575
|
+
if (event.type === "exit") {
|
|
2576
|
+
setupExitCode = Number(event.data);
|
|
2517
2577
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2578
|
+
}
|
|
2579
|
+
if (setupExitCode !== 0) {
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
if (request.setupScript) {
|
|
2584
|
+
let setupExitCode = 0;
|
|
2585
|
+
for await (const event of this.executionManager.runSetupScript(this.container, request.setupScript, timeoutMs, this.volumeManager)) {
|
|
2586
|
+
yield event;
|
|
2587
|
+
if (event.type === "exit") {
|
|
2588
|
+
setupExitCode = Number(event.data);
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
if (setupExitCode !== 0) {
|
|
2592
|
+
return;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
2596
|
+
let cmd;
|
|
2597
|
+
if (request.stdin) {
|
|
2598
|
+
const stdinPath = `${SANDBOX_WORKDIR}/_stdin_${Date.now()}`;
|
|
2599
|
+
await this.volumeManager.writeFileViaExec(this.container, stdinPath, request.stdin);
|
|
2600
|
+
const cmdStr = rawCmd.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
|
|
2601
|
+
cmd = this.executionManager.wrapWithTimeout(["sh", "-c", `cat ${stdinPath} | ${cmdStr}`], timeoutSec);
|
|
2602
|
+
} else {
|
|
2603
|
+
cmd = this.executionManager.wrapWithTimeout(rawCmd, timeoutSec);
|
|
2604
|
+
}
|
|
2605
|
+
const exec = await this.container.exec({
|
|
2606
|
+
Cmd: cmd,
|
|
2607
|
+
Env: this.executionManager.buildEnv(request.env, this.networkManager.proxyPort, this.network, this.networkFilter),
|
|
2608
|
+
AttachStdout: true,
|
|
2609
|
+
AttachStderr: true,
|
|
2610
|
+
WorkingDir: execWorkdir,
|
|
2611
|
+
User: "sandbox"
|
|
2612
|
+
});
|
|
2613
|
+
const execStream = await exec.start({ Tty: false });
|
|
2614
|
+
yield* this.executionManager.streamExecOutput(execStream, exec, this.container, timeoutMs);
|
|
2615
|
+
}
|
|
2616
|
+
async* executeStreamEphemeral(request) {
|
|
2617
|
+
const adapter = this.getAdapter(request.runtime);
|
|
2618
|
+
const timeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;
|
|
2619
|
+
const resolved = await this.resolveImage(adapter, request.installPackages);
|
|
2620
|
+
const image = resolved.image;
|
|
2621
|
+
const execWorkdir = request.workdir ? resolveWorkdir(request.workdir) : SANDBOX_WORKDIR;
|
|
2622
|
+
const pool = this.ensurePool();
|
|
2623
|
+
const container = await pool.acquire(image);
|
|
2624
|
+
try {
|
|
2625
|
+
await this.networkManager.startProxy(container);
|
|
2626
|
+
await this.networkManager.setupIptables(container);
|
|
2627
|
+
if (resolved.remainingPackages.length > 0) {
|
|
2628
|
+
await this.executionManager.installPackages(container, request.runtime, resolved.remainingPackages, timeoutMs);
|
|
2629
|
+
}
|
|
2630
|
+
if (resolved.imageSetupScript) {
|
|
2631
|
+
let setupExitCode = 0;
|
|
2632
|
+
for await (const event of this.executionManager.runSetupScript(container, resolved.imageSetupScript, timeoutMs, this.volumeManager)) {
|
|
2633
|
+
yield event;
|
|
2634
|
+
if (event.type === "exit") {
|
|
2635
|
+
setupExitCode = Number(event.data);
|
|
2636
|
+
}
|
|
2520
2637
|
}
|
|
2521
|
-
if (
|
|
2522
|
-
|
|
2638
|
+
if (setupExitCode !== 0) {
|
|
2639
|
+
return;
|
|
2523
2640
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2641
|
+
}
|
|
2642
|
+
if (request.setupScript) {
|
|
2643
|
+
let setupExitCode = 0;
|
|
2644
|
+
for await (const event of this.executionManager.runSetupScript(container, request.setupScript, timeoutMs, this.volumeManager)) {
|
|
2645
|
+
yield event;
|
|
2646
|
+
if (event.type === "exit") {
|
|
2647
|
+
setupExitCode = Number(event.data);
|
|
2527
2648
|
}
|
|
2528
2649
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
let cmd;
|
|
2532
|
-
if (request.stdin) {
|
|
2533
|
-
const stdinPath = `${SANDBOX_WORKDIR}/_stdin`;
|
|
2534
|
-
await this.volumeManager.writeFileViaExec(container, stdinPath, request.stdin);
|
|
2535
|
-
const cmdStr = rawCmd.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
|
|
2536
|
-
cmd = this.executionManager.wrapWithTimeout(["sh", "-c", `cat ${stdinPath} | ${cmdStr}`], timeoutSec);
|
|
2537
|
-
} else {
|
|
2538
|
-
cmd = this.executionManager.wrapWithTimeout(rawCmd, timeoutSec);
|
|
2650
|
+
if (setupExitCode !== 0) {
|
|
2651
|
+
return;
|
|
2539
2652
|
}
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
AttachStderr: true,
|
|
2545
|
-
WorkingDir: execWorkdir,
|
|
2546
|
-
User: "sandbox"
|
|
2547
|
-
});
|
|
2548
|
-
const execStream = await exec.start({ Tty: false });
|
|
2549
|
-
yield* this.executionManager.streamExecOutput(execStream, exec, container, timeoutMs);
|
|
2550
|
-
} finally {
|
|
2551
|
-
if (this.persist) {
|
|
2552
|
-
logger.debug(`[Persist] Leaving container running for inspection: ${container.id}`);
|
|
2553
|
-
} else {
|
|
2554
|
-
try {
|
|
2555
|
-
await container.remove({ force: true });
|
|
2556
|
-
} catch {}
|
|
2653
|
+
}
|
|
2654
|
+
if (request.files) {
|
|
2655
|
+
for (const [fPath, fContent] of Object.entries(request.files)) {
|
|
2656
|
+
await this.volumeManager.writeFileViaExec(container, fPath, fContent);
|
|
2557
2657
|
}
|
|
2558
2658
|
}
|
|
2659
|
+
let rawCmd;
|
|
2660
|
+
if (request.cmd) {
|
|
2661
|
+
rawCmd = ["bash", "-c", request.cmd];
|
|
2662
|
+
} else {
|
|
2663
|
+
const ext = request.fileExtension ?? adapter.getFileExtension();
|
|
2664
|
+
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
2665
|
+
await this.volumeManager.writeFileViaExec(container, filePath, request.code);
|
|
2666
|
+
rawCmd = this.buildAdapterCommand(adapter, request, filePath);
|
|
2667
|
+
}
|
|
2668
|
+
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
2669
|
+
let cmd;
|
|
2670
|
+
if (request.stdin) {
|
|
2671
|
+
const stdinPath = `${SANDBOX_WORKDIR}/_stdin`;
|
|
2672
|
+
await this.volumeManager.writeFileViaExec(container, stdinPath, request.stdin);
|
|
2673
|
+
const cmdStr = rawCmd.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
|
|
2674
|
+
cmd = this.executionManager.wrapWithTimeout(["sh", "-c", `cat ${stdinPath} | ${cmdStr}`], timeoutSec);
|
|
2675
|
+
} else {
|
|
2676
|
+
cmd = this.executionManager.wrapWithTimeout(rawCmd, timeoutSec);
|
|
2677
|
+
}
|
|
2678
|
+
const exec = await container.exec({
|
|
2679
|
+
Cmd: cmd,
|
|
2680
|
+
Env: this.executionManager.buildEnv(request.env, this.networkManager.proxyPort, this.network, this.networkFilter),
|
|
2681
|
+
AttachStdout: true,
|
|
2682
|
+
AttachStderr: true,
|
|
2683
|
+
WorkingDir: execWorkdir,
|
|
2684
|
+
User: "sandbox"
|
|
2685
|
+
});
|
|
2686
|
+
const execStream = await exec.start({ Tty: false });
|
|
2687
|
+
yield* this.executionManager.streamExecOutput(execStream, exec, container, timeoutMs);
|
|
2559
2688
|
} finally {
|
|
2560
|
-
this.
|
|
2689
|
+
if (this.persist) {
|
|
2690
|
+
logger.debug(`[Persist] Leaving container running for inspection: ${container.id}`);
|
|
2691
|
+
} else {
|
|
2692
|
+
pool.release(container, image).catch((err) => {
|
|
2693
|
+
logger.debug(`[Pool] release failed: ${err}`);
|
|
2694
|
+
container.remove({ force: true }).catch(() => {});
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2561
2697
|
}
|
|
2562
2698
|
}
|
|
2563
2699
|
async resolveImage(adapter, requestedPackages) {
|
|
@@ -2684,31 +2820,59 @@ class DockerIsol8 {
|
|
|
2684
2820
|
try {
|
|
2685
2821
|
await this.networkManager.startProxy(container);
|
|
2686
2822
|
await this.networkManager.setupIptables(container);
|
|
2687
|
-
const canUseInline = !(req.stdin || req.files || req.outputPaths) && (!req.installPackages || req.installPackages.length === 0);
|
|
2688
2823
|
let rawCmd;
|
|
2689
|
-
if (
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2824
|
+
if (req.cmd) {
|
|
2825
|
+
rawCmd = ["bash", "-c", req.cmd];
|
|
2826
|
+
} else {
|
|
2827
|
+
const canUseInline = !(req.stdin || req.files || req.outputPaths) && (!req.installPackages || req.installPackages.length === 0);
|
|
2828
|
+
if (canUseInline) {
|
|
2829
|
+
try {
|
|
2830
|
+
rawCmd = this.buildAdapterCommand(adapter, req);
|
|
2831
|
+
} catch {
|
|
2832
|
+
const ext = req.fileExtension ?? adapter.getFileExtension();
|
|
2833
|
+
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
2834
|
+
await this.volumeManager.writeFileViaExec(container, filePath, req.code);
|
|
2835
|
+
rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2836
|
+
}
|
|
2837
|
+
} else {
|
|
2693
2838
|
const ext = req.fileExtension ?? adapter.getFileExtension();
|
|
2694
2839
|
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
2695
2840
|
await this.volumeManager.writeFileViaExec(container, filePath, req.code);
|
|
2696
2841
|
rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2697
2842
|
}
|
|
2698
|
-
} else {
|
|
2699
|
-
const ext = req.fileExtension ?? adapter.getFileExtension();
|
|
2700
|
-
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
2701
|
-
await this.volumeManager.writeFileViaExec(container, filePath, req.code);
|
|
2702
|
-
rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2703
2843
|
}
|
|
2704
2844
|
if (resolved.remainingPackages.length > 0) {
|
|
2705
2845
|
await this.executionManager.installPackages(container, req.runtime, resolved.remainingPackages, timeoutMs);
|
|
2706
2846
|
}
|
|
2707
2847
|
if (resolved.imageSetupScript) {
|
|
2708
|
-
|
|
2848
|
+
let stderr2 = "";
|
|
2849
|
+
let exitCode = 0;
|
|
2850
|
+
for await (const event of this.executionManager.runSetupScript(container, resolved.imageSetupScript, timeoutMs, this.volumeManager)) {
|
|
2851
|
+
if (event.type === "stderr") {
|
|
2852
|
+
stderr2 += event.data;
|
|
2853
|
+
}
|
|
2854
|
+
if (event.type === "exit") {
|
|
2855
|
+
exitCode = Number(event.data);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
if (exitCode !== 0) {
|
|
2859
|
+
throw new Error(`Setup script failed (exit code ${exitCode}): ${stderr2}`);
|
|
2860
|
+
}
|
|
2709
2861
|
}
|
|
2710
2862
|
if (req.setupScript) {
|
|
2711
|
-
|
|
2863
|
+
let stderr2 = "";
|
|
2864
|
+
let exitCode = 0;
|
|
2865
|
+
for await (const event of this.executionManager.runSetupScript(container, req.setupScript, timeoutMs, this.volumeManager)) {
|
|
2866
|
+
if (event.type === "stderr") {
|
|
2867
|
+
stderr2 += event.data;
|
|
2868
|
+
}
|
|
2869
|
+
if (event.type === "exit") {
|
|
2870
|
+
exitCode = Number(event.data);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
if (exitCode !== 0) {
|
|
2874
|
+
throw new Error(`Setup script failed (exit code ${exitCode}): ${stderr2}`);
|
|
2875
|
+
}
|
|
2712
2876
|
}
|
|
2713
2877
|
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
2714
2878
|
let cmd;
|
|
@@ -2800,24 +2964,53 @@ class DockerIsol8 {
|
|
|
2800
2964
|
} else if (this.persistentRuntime?.name !== adapter.name) {
|
|
2801
2965
|
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.`);
|
|
2802
2966
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2967
|
+
let rawCmd;
|
|
2968
|
+
if (req.cmd) {
|
|
2969
|
+
rawCmd = ["bash", "-c", req.cmd];
|
|
2970
|
+
} else {
|
|
2971
|
+
const ext = req.fileExtension ?? adapter.getFileExtension();
|
|
2972
|
+
const filePath = `${SANDBOX_WORKDIR}/exec_${Date.now()}${ext}`;
|
|
2973
|
+
await this.volumeManager.putFile(this.container, filePath, req.code);
|
|
2974
|
+
rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2975
|
+
}
|
|
2806
2976
|
if (req.files) {
|
|
2807
2977
|
for (const [fPath, fContent] of Object.entries(req.files)) {
|
|
2808
2978
|
await this.volumeManager.putFile(this.container, fPath, fContent);
|
|
2809
2979
|
}
|
|
2810
2980
|
}
|
|
2811
|
-
const rawCmd = this.buildAdapterCommand(adapter, req, filePath);
|
|
2812
2981
|
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
2813
2982
|
if (remainingPackages.length > 0) {
|
|
2814
2983
|
await this.executionManager.installPackages(this.container, req.runtime, remainingPackages, timeoutMs);
|
|
2815
2984
|
}
|
|
2816
2985
|
if (imageSetupScript) {
|
|
2817
|
-
|
|
2986
|
+
let stderr2 = "";
|
|
2987
|
+
let exitCode = 0;
|
|
2988
|
+
for await (const event of this.executionManager.runSetupScript(this.container, imageSetupScript, timeoutMs, this.volumeManager)) {
|
|
2989
|
+
if (event.type === "stderr") {
|
|
2990
|
+
stderr2 += event.data;
|
|
2991
|
+
}
|
|
2992
|
+
if (event.type === "exit") {
|
|
2993
|
+
exitCode = Number(event.data);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
if (exitCode !== 0) {
|
|
2997
|
+
throw new Error(`Setup script failed (exit code ${exitCode}): ${stderr2}`);
|
|
2998
|
+
}
|
|
2818
2999
|
}
|
|
2819
3000
|
if (req.setupScript) {
|
|
2820
|
-
|
|
3001
|
+
let stderr2 = "";
|
|
3002
|
+
let exitCode = 0;
|
|
3003
|
+
for await (const event of this.executionManager.runSetupScript(this.container, req.setupScript, timeoutMs, this.volumeManager)) {
|
|
3004
|
+
if (event.type === "stderr") {
|
|
3005
|
+
stderr2 += event.data;
|
|
3006
|
+
}
|
|
3007
|
+
if (event.type === "exit") {
|
|
3008
|
+
exitCode = Number(event.data);
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
if (exitCode !== 0) {
|
|
3012
|
+
throw new Error(`Setup script failed (exit code ${exitCode}): ${stderr2}`);
|
|
3013
|
+
}
|
|
2821
3014
|
}
|
|
2822
3015
|
let cmd;
|
|
2823
3016
|
if (req.stdin) {
|
|
@@ -2920,22 +3113,25 @@ class DockerIsol8 {
|
|
|
2920
3113
|
if (req.runtime !== "agent") {
|
|
2921
3114
|
return;
|
|
2922
3115
|
}
|
|
2923
|
-
if (this.network
|
|
2924
|
-
throw new Error(`Agent runtime requires network
|
|
3116
|
+
if (this.network === "none") {
|
|
3117
|
+
throw new Error(`Agent runtime requires network access. The AI coding agent needs to reach its LLM provider API. Use --net host, or --net filtered --allow "api.anthropic.com" (or your provider's domain).`);
|
|
2925
3118
|
}
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
3119
|
+
if (this.network === "filtered") {
|
|
3120
|
+
const whitelist = this.networkFilter?.whitelist ?? [];
|
|
3121
|
+
if (whitelist.length === 0) {
|
|
3122
|
+
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).`);
|
|
3123
|
+
}
|
|
2929
3124
|
}
|
|
2930
3125
|
}
|
|
2931
3126
|
buildAdapterCommand(adapter, req, filePath) {
|
|
3127
|
+
const code = req.code ?? "";
|
|
2932
3128
|
if (adapter.getCommandWithOptions) {
|
|
2933
|
-
return adapter.getCommandWithOptions(
|
|
3129
|
+
return adapter.getCommandWithOptions(code, {
|
|
2934
3130
|
filePath,
|
|
2935
3131
|
agentFlags: req.agentFlags
|
|
2936
3132
|
});
|
|
2937
3133
|
}
|
|
2938
|
-
return adapter.getCommand(
|
|
3134
|
+
return adapter.getCommand(code, filePath);
|
|
2939
3135
|
}
|
|
2940
3136
|
buildHostConfig() {
|
|
2941
3137
|
const config = {
|
|
@@ -3045,7 +3241,7 @@ init_logger();
|
|
|
3045
3241
|
// package.json
|
|
3046
3242
|
var package_default = {
|
|
3047
3243
|
name: "@isol8/core",
|
|
3048
|
-
version: "0.
|
|
3244
|
+
version: "0.20.0",
|
|
3049
3245
|
description: "Sandboxed code execution engine for AI agents and apps (Docker, runtime and network controls)",
|
|
3050
3246
|
author: "Illusion47586",
|
|
3051
3247
|
license: "MIT",
|
|
@@ -3130,4 +3326,4 @@ export {
|
|
|
3130
3326
|
AgentAdapter
|
|
3131
3327
|
};
|
|
3132
3328
|
|
|
3133
|
-
//# debugId=
|
|
3329
|
+
//# debugId=767B2279906CCA3764756E2164756E21
|