isol8 0.10.1 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -12
- package/dist/cli.js +238 -79
- package/dist/index.js +231 -77
- package/dist/src/engine/docker.d.ts +3 -0
- package/dist/src/engine/docker.d.ts.map +1 -1
- package/dist/src/engine/pool.d.ts +48 -13
- package/dist/src/engine/pool.d.ts.map +1 -1
- package/dist/src/types.d.ts +17 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -183,11 +183,42 @@ const result = await isol8.execute({
|
|
|
183
183
|
|
|
184
184
|
console.log(result.stdout); // "Hello from isol8!"
|
|
185
185
|
console.log(result.exitCode); // 0
|
|
186
|
-
console.log(result.durationMs); // ~
|
|
186
|
+
console.log(result.durationMs); // ~120-140ms (warm pool)
|
|
187
187
|
|
|
188
188
|
await isol8.stop();
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
### Pool Strategy
|
|
192
|
+
|
|
193
|
+
isol8 supports two pool strategies for container reuse:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Fast mode (default) - best performance
|
|
197
|
+
// Uses dual-pool system: clean pool + dirty pool
|
|
198
|
+
// Instant acquire from clean pool, immediate cleanup on acquire if needed
|
|
199
|
+
// Background cleanup runs every 5 seconds
|
|
200
|
+
const fastEngine = new DockerIsol8({
|
|
201
|
+
network: "none",
|
|
202
|
+
poolStrategy: "fast",
|
|
203
|
+
poolSize: { clean: 2, dirty: 2 }, // 2 ready, 2 being cleaned
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Secure mode - cleanup in acquire
|
|
207
|
+
// Slower but ensures container is always clean before use
|
|
208
|
+
const secureEngine = new DockerIsol8({
|
|
209
|
+
network: "none",
|
|
210
|
+
poolStrategy: "secure",
|
|
211
|
+
poolSize: 2, // 2 warm containers
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Fast mode details:**
|
|
216
|
+
- Maintains two pools: `clean` (ready to use) and `dirty` (need cleanup)
|
|
217
|
+
- `acquire()` returns instantly from clean pool if available
|
|
218
|
+
- If clean pool is empty but dirty has containers, tries immediate cleanup
|
|
219
|
+
- Background cleanup runs every 5 seconds to process dirty containers
|
|
220
|
+
- Best performance with minimal memory overhead
|
|
221
|
+
|
|
191
222
|
### Persistent Sessions
|
|
192
223
|
|
|
193
224
|
```typescript
|
|
@@ -328,7 +359,7 @@ Full schema: [`schema/isol8.config.schema.json`](./schema/isol8.config.schema.js
|
|
|
328
359
|
|
|
329
360
|
## Benchmarks
|
|
330
361
|
|
|
331
|
-
Execution latency for a "hello world" script per runtime. Measured on Apple Silicon (
|
|
362
|
+
Execution latency for a "hello world" script per runtime. Measured on Apple Silicon (OrbStack), averaged across multiple runs. Results will vary by machine.
|
|
332
363
|
|
|
333
364
|
### Cold Start (fresh engine per run)
|
|
334
365
|
|
|
@@ -336,11 +367,11 @@ Each run creates a new `DockerIsol8` instance, executes, and tears down.
|
|
|
336
367
|
|
|
337
368
|
| Runtime | Min | Median | Max | Avg |
|
|
338
369
|
|---------|-----|--------|-----|-----|
|
|
339
|
-
| Python |
|
|
340
|
-
| Node.js |
|
|
341
|
-
| Bun |
|
|
342
|
-
| Deno |
|
|
343
|
-
| Bash |
|
|
370
|
+
| Python | 220ms | 280ms | 350ms | 280ms |
|
|
371
|
+
| Node.js | 200ms | 250ms | 320ms | 260ms |
|
|
372
|
+
| Bun | 180ms | 230ms | 300ms | 230ms |
|
|
373
|
+
| Deno | 210ms | 270ms | 340ms | 270ms |
|
|
374
|
+
| Bash | 180ms | 220ms | 280ms | 220ms |
|
|
344
375
|
|
|
345
376
|
### Warm Pool (reused engine)
|
|
346
377
|
|
|
@@ -348,11 +379,11 @@ A single `DockerIsol8` instance reused across 5 runs. The first run is cold (poo
|
|
|
348
379
|
|
|
349
380
|
| Runtime | Cold | Warm Avg | Warm Min | Speedup |
|
|
350
381
|
|---------|------|----------|----------|---------|
|
|
351
|
-
| Python |
|
|
352
|
-
| Node.js |
|
|
353
|
-
| Bun |
|
|
354
|
-
| Deno |
|
|
355
|
-
| Bash |
|
|
382
|
+
| Python | 300ms | 160ms | 130ms | 2.3x |
|
|
383
|
+
| Node.js | 280ms | 170ms | 140ms | 2.0x |
|
|
384
|
+
| Bun | 250ms | 155ms | 130ms | 1.9x |
|
|
385
|
+
| Deno | 270ms | 160ms | 140ms | 1.9x |
|
|
386
|
+
| Bash | 230ms | 145ms | 125ms | 1.8x |
|
|
356
387
|
|
|
357
388
|
### Execution Phase Breakdown
|
|
358
389
|
|
package/dist/cli.js
CHANGED
|
@@ -55213,47 +55213,129 @@ class Semaphore {
|
|
|
55213
55213
|
// src/engine/pool.ts
|
|
55214
55214
|
class ContainerPool {
|
|
55215
55215
|
docker;
|
|
55216
|
-
|
|
55216
|
+
poolStrategy;
|
|
55217
|
+
cleanPoolSize;
|
|
55218
|
+
dirtyPoolSize;
|
|
55217
55219
|
createOptions;
|
|
55220
|
+
networkMode;
|
|
55221
|
+
securityMode;
|
|
55218
55222
|
pools = new Map;
|
|
55219
55223
|
replenishing = new Set;
|
|
55220
55224
|
pendingReplenishments = new Set;
|
|
55225
|
+
cleaningInterval = null;
|
|
55221
55226
|
constructor(options) {
|
|
55222
55227
|
this.docker = options.docker;
|
|
55223
|
-
this.
|
|
55228
|
+
this.poolStrategy = options.poolStrategy ?? "fast";
|
|
55224
55229
|
this.createOptions = options.createOptions;
|
|
55230
|
+
this.networkMode = options.networkMode;
|
|
55231
|
+
this.securityMode = options.securityMode;
|
|
55232
|
+
if (typeof options.poolSize === "number") {
|
|
55233
|
+
this.cleanPoolSize = options.poolSize;
|
|
55234
|
+
this.dirtyPoolSize = options.poolSize;
|
|
55235
|
+
} else if (options.poolSize) {
|
|
55236
|
+
this.cleanPoolSize = options.poolSize.clean ?? 1;
|
|
55237
|
+
this.dirtyPoolSize = options.poolSize.dirty ?? 1;
|
|
55238
|
+
} else {
|
|
55239
|
+
this.cleanPoolSize = 1;
|
|
55240
|
+
this.dirtyPoolSize = 1;
|
|
55241
|
+
}
|
|
55242
|
+
if (this.poolStrategy === "fast") {
|
|
55243
|
+
this.startBackgroundCleaning();
|
|
55244
|
+
}
|
|
55225
55245
|
}
|
|
55226
55246
|
async acquire(image) {
|
|
55227
|
-
const pool = this.pools.get(image);
|
|
55228
|
-
if (
|
|
55229
|
-
|
|
55247
|
+
const pool = this.pools.get(image) ?? { clean: [], dirty: [] };
|
|
55248
|
+
if (this.poolStrategy === "fast") {
|
|
55249
|
+
if (pool.clean.length > 0) {
|
|
55250
|
+
const entry = pool.clean.shift();
|
|
55251
|
+
this.pools.set(image, pool);
|
|
55252
|
+
this.replenish(image);
|
|
55253
|
+
return entry.container;
|
|
55254
|
+
}
|
|
55255
|
+
if (pool.dirty.length > 0 && pool.clean.length < this.cleanPoolSize) {
|
|
55256
|
+
await this.cleanDirtyImmediate(image);
|
|
55257
|
+
const updatedPool = this.pools.get(image);
|
|
55258
|
+
if (updatedPool && updatedPool.clean.length > 0) {
|
|
55259
|
+
const entry = updatedPool.clean.shift();
|
|
55260
|
+
this.pools.set(image, updatedPool);
|
|
55261
|
+
this.replenish(image);
|
|
55262
|
+
return entry.container;
|
|
55263
|
+
}
|
|
55264
|
+
}
|
|
55265
|
+
return this.createContainer(image);
|
|
55266
|
+
}
|
|
55267
|
+
if (pool.clean && pool.clean.length > 0) {
|
|
55268
|
+
const entry = pool.clean.shift();
|
|
55269
|
+
this.pools.set(image, { clean: pool.clean, dirty: [] });
|
|
55270
|
+
await this.cleanupContainer(entry.container);
|
|
55230
55271
|
this.replenish(image);
|
|
55231
55272
|
return entry.container;
|
|
55232
55273
|
}
|
|
55233
55274
|
return this.createContainer(image);
|
|
55234
55275
|
}
|
|
55235
55276
|
async release(container, image) {
|
|
55236
|
-
|
|
55237
|
-
if (pool
|
|
55238
|
-
|
|
55277
|
+
let pool = this.pools.get(image);
|
|
55278
|
+
if (!pool) {
|
|
55279
|
+
pool = { clean: [], dirty: [] };
|
|
55280
|
+
this.pools.set(image, pool);
|
|
55281
|
+
}
|
|
55282
|
+
if (this.poolStrategy === "fast") {
|
|
55283
|
+
if (pool.dirty.length >= this.dirtyPoolSize) {
|
|
55284
|
+
await container.remove({ force: true }).catch(() => {});
|
|
55285
|
+
return;
|
|
55286
|
+
}
|
|
55287
|
+
pool.dirty.push({ container, createdAt: Date.now() });
|
|
55288
|
+
} else {
|
|
55289
|
+
if (pool.clean.length >= this.cleanPoolSize) {
|
|
55290
|
+
await container.remove({ force: true }).catch(() => {});
|
|
55291
|
+
return;
|
|
55292
|
+
}
|
|
55293
|
+
if (!pool.clean) {
|
|
55294
|
+
pool.clean = [];
|
|
55295
|
+
}
|
|
55296
|
+
pool.clean.push({ container, createdAt: Date.now() });
|
|
55297
|
+
}
|
|
55298
|
+
}
|
|
55299
|
+
startBackgroundCleaning() {
|
|
55300
|
+
this.cleaningInterval = setInterval(async () => {
|
|
55301
|
+
for (const [_image, pool] of this.pools) {
|
|
55302
|
+
for (let i = 0;i < this.dirtyPoolSize; i++) {
|
|
55303
|
+
if (pool.dirty.length > 0 && pool.clean.length < this.cleanPoolSize) {
|
|
55304
|
+
const entry = pool.dirty.shift();
|
|
55305
|
+
try {
|
|
55306
|
+
await this.cleanupContainer(entry.container);
|
|
55307
|
+
pool.clean.push(entry);
|
|
55308
|
+
} catch {
|
|
55309
|
+
entry.container.remove({ force: true }).catch(() => {});
|
|
55310
|
+
}
|
|
55311
|
+
}
|
|
55312
|
+
}
|
|
55313
|
+
}
|
|
55314
|
+
}, 5000);
|
|
55315
|
+
}
|
|
55316
|
+
async cleanDirtyImmediate(image) {
|
|
55317
|
+
const pool = this.pools.get(image);
|
|
55318
|
+
if (!pool || pool.dirty.length === 0 || pool.clean.length >= this.cleanPoolSize) {
|
|
55239
55319
|
return;
|
|
55240
55320
|
}
|
|
55321
|
+
const entry = pool.dirty.shift();
|
|
55241
55322
|
try {
|
|
55242
|
-
|
|
55243
|
-
|
|
55244
|
-
|
|
55245
|
-
|
|
55246
|
-
|
|
55247
|
-
|
|
55248
|
-
|
|
55249
|
-
|
|
55250
|
-
|
|
55251
|
-
|
|
55252
|
-
|
|
55253
|
-
|
|
55254
|
-
|
|
55323
|
+
await this.cleanupContainer(entry.container);
|
|
55324
|
+
pool.clean.push(entry);
|
|
55325
|
+
} catch {
|
|
55326
|
+
entry.container.remove({ force: true }).catch(() => {});
|
|
55327
|
+
}
|
|
55328
|
+
}
|
|
55329
|
+
async cleanupContainer(container) {
|
|
55330
|
+
const needsCleanup = this.securityMode === "strict";
|
|
55331
|
+
const needsIptables = this.networkMode === "filtered" && needsCleanup;
|
|
55332
|
+
if (!needsCleanup) {
|
|
55333
|
+
return;
|
|
55334
|
+
}
|
|
55335
|
+
try {
|
|
55336
|
+
const cleanupCmd = needsIptables ? "pkill -9 -u sandbox 2>/dev/null; /usr/sbin/iptables -F OUTPUT 2>/dev/null; rm -rf /sandbox/* /sandbox/.[!.]* 2>/dev/null; true" : "pkill -9 -u sandbox 2>/dev/null; rm -rf /sandbox/* /sandbox/.[!.]* 2>/dev/null; true";
|
|
55255
55337
|
const cleanExec = await container.exec({
|
|
55256
|
-
Cmd: ["sh", "-c",
|
|
55338
|
+
Cmd: ["sh", "-c", cleanupCmd]
|
|
55257
55339
|
});
|
|
55258
55340
|
await cleanExec.start({ Detach: true });
|
|
55259
55341
|
let info2 = await cleanExec.inspect();
|
|
@@ -55261,29 +55343,45 @@ class ContainerPool {
|
|
|
55261
55343
|
await new Promise((r) => setTimeout(r, 5));
|
|
55262
55344
|
info2 = await cleanExec.inspect();
|
|
55263
55345
|
}
|
|
55264
|
-
|
|
55265
|
-
this.pools.set(image, pool);
|
|
55266
|
-
} catch {
|
|
55267
|
-
await container.remove({ force: true }).catch(() => {});
|
|
55268
|
-
}
|
|
55346
|
+
} catch {}
|
|
55269
55347
|
}
|
|
55270
55348
|
async warm(image) {
|
|
55271
|
-
const pool = this.pools.get(image) ?? [];
|
|
55349
|
+
const pool = this.pools.get(image) ?? { clean: [], dirty: [] };
|
|
55272
55350
|
this.pools.set(image, pool);
|
|
55273
|
-
const needed = this.
|
|
55351
|
+
const needed = this.poolStrategy === "fast" ? this.cleanPoolSize - pool.clean.length : this.cleanPoolSize - (pool.clean?.length ?? 0);
|
|
55352
|
+
if (needed <= 0) {
|
|
55353
|
+
return;
|
|
55354
|
+
}
|
|
55274
55355
|
const promises = [];
|
|
55275
55356
|
for (let i = 0;i < needed; i++) {
|
|
55276
55357
|
promises.push(this.createContainer(image).then((container) => {
|
|
55277
|
-
|
|
55358
|
+
if (this.poolStrategy === "fast") {
|
|
55359
|
+
pool.clean.push({ container, createdAt: Date.now() });
|
|
55360
|
+
} else {
|
|
55361
|
+
if (!pool.clean) {
|
|
55362
|
+
pool.clean = [];
|
|
55363
|
+
}
|
|
55364
|
+
pool.clean.push({ container, createdAt: Date.now() });
|
|
55365
|
+
}
|
|
55278
55366
|
}));
|
|
55279
55367
|
}
|
|
55280
55368
|
await Promise.all(promises);
|
|
55281
55369
|
}
|
|
55370
|
+
async stop() {
|
|
55371
|
+
return this.drain();
|
|
55372
|
+
}
|
|
55282
55373
|
async drain() {
|
|
55374
|
+
if (this.cleaningInterval) {
|
|
55375
|
+
clearInterval(this.cleaningInterval);
|
|
55376
|
+
this.cleaningInterval = null;
|
|
55377
|
+
}
|
|
55283
55378
|
await Promise.all(this.pendingReplenishments);
|
|
55284
55379
|
const promises = [];
|
|
55285
55380
|
for (const [, pool] of this.pools) {
|
|
55286
|
-
for (const entry of pool) {
|
|
55381
|
+
for (const entry of pool.clean ?? []) {
|
|
55382
|
+
promises.push(entry.container.remove({ force: true }).catch(() => {}));
|
|
55383
|
+
}
|
|
55384
|
+
for (const entry of pool.dirty) {
|
|
55287
55385
|
promises.push(entry.container.remove({ force: true }).catch(() => {}));
|
|
55288
55386
|
}
|
|
55289
55387
|
}
|
|
@@ -55302,30 +55400,46 @@ class ContainerPool {
|
|
|
55302
55400
|
}
|
|
55303
55401
|
replenish(image) {
|
|
55304
55402
|
if (this.replenishing.has(image)) {
|
|
55305
|
-
|
|
55306
|
-
|
|
55307
|
-
}
|
|
55308
|
-
this.replenishing.add(image);
|
|
55309
|
-
logger.debug(`[Pool] Starting background replenishment for image: ${image}`);
|
|
55310
|
-
const promise = this.createContainer(image).then((container) => {
|
|
55311
|
-
const pool = this.pools.get(image) ?? [];
|
|
55312
|
-
if (pool.length < this.poolSize) {
|
|
55313
|
-
pool.push({ container, createdAt: Date.now() });
|
|
55314
|
-
this.pools.set(image, pool);
|
|
55315
|
-
logger.debug(`[Pool] Replenished container ${container.id} added to pool for ${image}. Pool size: ${pool.length}`);
|
|
55316
|
-
} else {
|
|
55317
|
-
logger.debug(`[Pool] Replenished container ${container.id} not needed (pool for ${image} is full), destroying`);
|
|
55318
|
-
container.remove({ force: true }).catch((err) => {
|
|
55319
|
-
logger.error(`[Pool] Error destroying unneeded replenished container ${container.id}:`, err);
|
|
55320
|
-
});
|
|
55403
|
+
if (this.replenishing.has(image)) {
|
|
55404
|
+
return;
|
|
55321
55405
|
}
|
|
55322
|
-
|
|
55323
|
-
|
|
55324
|
-
|
|
55325
|
-
|
|
55326
|
-
|
|
55327
|
-
|
|
55328
|
-
|
|
55406
|
+
const pool = this.pools.get(image);
|
|
55407
|
+
const currentSize = pool ? this.poolStrategy === "fast" ? pool.clean.length : pool.clean?.length ?? 0 : 0;
|
|
55408
|
+
const targetSize = this.poolStrategy === "fast" ? this.cleanPoolSize : this.cleanPoolSize;
|
|
55409
|
+
if (currentSize >= targetSize) {
|
|
55410
|
+
return;
|
|
55411
|
+
}
|
|
55412
|
+
this.replenishing.add(image);
|
|
55413
|
+
const promise = this.createContainer(image).then((container) => {
|
|
55414
|
+
const p = this.pools.get(image);
|
|
55415
|
+
if (!p) {
|
|
55416
|
+
container.remove({ force: true }).catch(() => {});
|
|
55417
|
+
return;
|
|
55418
|
+
}
|
|
55419
|
+
if (this.poolStrategy === "fast") {
|
|
55420
|
+
if (p.clean.length < this.cleanPoolSize) {
|
|
55421
|
+
p.clean.push({ container, createdAt: Date.now() });
|
|
55422
|
+
} else {
|
|
55423
|
+
container.remove({ force: true }).catch(() => {});
|
|
55424
|
+
}
|
|
55425
|
+
} else {
|
|
55426
|
+
if (!p.clean) {
|
|
55427
|
+
p.clean = [];
|
|
55428
|
+
}
|
|
55429
|
+
if (p.clean.length < this.cleanPoolSize) {
|
|
55430
|
+
p.clean.push({ container, createdAt: Date.now() });
|
|
55431
|
+
} else {
|
|
55432
|
+
container.remove({ force: true }).catch(() => {});
|
|
55433
|
+
}
|
|
55434
|
+
}
|
|
55435
|
+
}).catch((err) => {
|
|
55436
|
+
logger.error(`[Pool] Error during replenishment for ${image}:`, err);
|
|
55437
|
+
}).finally(() => {
|
|
55438
|
+
this.replenishing.delete(image);
|
|
55439
|
+
this.pendingReplenishments.delete(promise);
|
|
55440
|
+
});
|
|
55441
|
+
this.pendingReplenishments.add(promise);
|
|
55442
|
+
}
|
|
55329
55443
|
}
|
|
55330
55444
|
}
|
|
55331
55445
|
var init_pool = __esm(() => {
|
|
@@ -55487,31 +55601,52 @@ var exports_docker = {};
|
|
|
55487
55601
|
__export(exports_docker, {
|
|
55488
55602
|
DockerIsol8: () => DockerIsol8
|
|
55489
55603
|
});
|
|
55490
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
55491
55604
|
import { randomUUID } from "node:crypto";
|
|
55492
55605
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
55493
55606
|
import { PassThrough } from "node:stream";
|
|
55494
55607
|
async function writeFileViaExec(container, filePath, content) {
|
|
55495
55608
|
const data = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
55496
|
-
|
|
55497
|
-
|
|
55498
|
-
|
|
55499
|
-
|
|
55500
|
-
|
|
55501
|
-
reject(new Error(`Failed to spawn docker exec: ${err.message}`));
|
|
55609
|
+
const b64 = data.toString("base64");
|
|
55610
|
+
if (b64.length < 20000) {
|
|
55611
|
+
const exec = await container.exec({
|
|
55612
|
+
Cmd: ["sh", "-c", `printf '%s' '${b64}' | base64 -d > ${filePath}`],
|
|
55613
|
+
User: "sandbox"
|
|
55502
55614
|
});
|
|
55503
|
-
|
|
55504
|
-
|
|
55505
|
-
|
|
55506
|
-
|
|
55507
|
-
|
|
55508
|
-
|
|
55509
|
-
|
|
55510
|
-
}
|
|
55511
|
-
|
|
55512
|
-
|
|
55615
|
+
await exec.start({ Detach: true });
|
|
55616
|
+
let info3 = await exec.inspect();
|
|
55617
|
+
while (info3.Running) {
|
|
55618
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
55619
|
+
info3 = await exec.inspect();
|
|
55620
|
+
}
|
|
55621
|
+
if (info3.ExitCode !== 0) {
|
|
55622
|
+
throw new Error(`Failed to write file ${filePath} in container (exit code ${info3.ExitCode})`);
|
|
55623
|
+
}
|
|
55624
|
+
return;
|
|
55625
|
+
}
|
|
55626
|
+
const tempPath = `/tmp/b64_${Date.now()}.tmp`;
|
|
55627
|
+
const chunkSize = 8000;
|
|
55628
|
+
for (let i = 0;i < b64.length; i += chunkSize) {
|
|
55629
|
+
const chunk = b64.slice(i, i + chunkSize);
|
|
55630
|
+
const exec = await container.exec({
|
|
55631
|
+
Cmd: ["sh", "-c", `printf '%s' '${chunk}' >> ${tempPath}`],
|
|
55632
|
+
User: "sandbox"
|
|
55513
55633
|
});
|
|
55634
|
+
await exec.start({ Detach: true });
|
|
55635
|
+
await exec.inspect();
|
|
55636
|
+
}
|
|
55637
|
+
const decodeExec = await container.exec({
|
|
55638
|
+
Cmd: ["sh", "-c", `base64 -d ${tempPath} > ${filePath} && rm ${tempPath}`],
|
|
55639
|
+
User: "sandbox"
|
|
55514
55640
|
});
|
|
55641
|
+
await decodeExec.start({ Detach: true });
|
|
55642
|
+
let info2 = await decodeExec.inspect();
|
|
55643
|
+
while (info2.Running) {
|
|
55644
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
55645
|
+
info2 = await decodeExec.inspect();
|
|
55646
|
+
}
|
|
55647
|
+
if (info2.ExitCode !== 0) {
|
|
55648
|
+
throw new Error(`Failed to write file ${filePath} in container (exit code ${info2.ExitCode})`);
|
|
55649
|
+
}
|
|
55515
55650
|
}
|
|
55516
55651
|
async function readFileViaExec(container, filePath) {
|
|
55517
55652
|
const exec = await container.exec({
|
|
@@ -55681,10 +55816,13 @@ class DockerIsol8 {
|
|
|
55681
55816
|
security;
|
|
55682
55817
|
persist;
|
|
55683
55818
|
logNetwork;
|
|
55819
|
+
poolStrategy;
|
|
55820
|
+
poolSize;
|
|
55684
55821
|
auditLogger;
|
|
55685
55822
|
container = null;
|
|
55686
55823
|
persistentRuntime = null;
|
|
55687
55824
|
pool = null;
|
|
55825
|
+
imageCache = new Map;
|
|
55688
55826
|
constructor(options = {}, maxConcurrent = 10) {
|
|
55689
55827
|
this.docker = options.docker ?? new import_dockerode.default;
|
|
55690
55828
|
this.mode = options.mode ?? "ephemeral";
|
|
@@ -55704,6 +55842,8 @@ class DockerIsol8 {
|
|
|
55704
55842
|
this.persist = options.persist ?? false;
|
|
55705
55843
|
this.security = options.security ?? { seccomp: "strict" };
|
|
55706
55844
|
this.logNetwork = options.logNetwork ?? false;
|
|
55845
|
+
this.poolStrategy = options.poolStrategy ?? "fast";
|
|
55846
|
+
this.poolSize = options.poolSize ?? { clean: 1, dirty: 1 };
|
|
55707
55847
|
if (options.audit) {
|
|
55708
55848
|
this.auditLogger = new AuditLogger(options.audit);
|
|
55709
55849
|
}
|
|
@@ -55953,13 +56093,21 @@ class DockerIsol8 {
|
|
|
55953
56093
|
if (this.overrideImage) {
|
|
55954
56094
|
return this.overrideImage;
|
|
55955
56095
|
}
|
|
56096
|
+
const cacheKey = adapter.image;
|
|
56097
|
+
const cached = this.imageCache.get(cacheKey);
|
|
56098
|
+
if (cached) {
|
|
56099
|
+
return cached;
|
|
56100
|
+
}
|
|
55956
56101
|
const customTag = `${adapter.image}-custom`;
|
|
56102
|
+
let resolvedImage;
|
|
55957
56103
|
try {
|
|
55958
56104
|
await this.docker.getImage(customTag).inspect();
|
|
55959
|
-
|
|
56105
|
+
resolvedImage = customTag;
|
|
55960
56106
|
} catch {
|
|
55961
|
-
|
|
56107
|
+
resolvedImage = adapter.image;
|
|
55962
56108
|
}
|
|
56109
|
+
this.imageCache.set(cacheKey, resolvedImage);
|
|
56110
|
+
return resolvedImage;
|
|
55963
56111
|
}
|
|
55964
56112
|
async executeEphemeral(req, startTime) {
|
|
55965
56113
|
const adapter = this.getAdapter(req.runtime);
|
|
@@ -55968,7 +56116,10 @@ class DockerIsol8 {
|
|
|
55968
56116
|
if (!this.pool) {
|
|
55969
56117
|
this.pool = new ContainerPool({
|
|
55970
56118
|
docker: this.docker,
|
|
55971
|
-
|
|
56119
|
+
poolStrategy: this.poolStrategy,
|
|
56120
|
+
poolSize: this.poolSize,
|
|
56121
|
+
networkMode: this.network,
|
|
56122
|
+
securityMode: this.security.seccomp ?? "strict",
|
|
55972
56123
|
createOptions: {
|
|
55973
56124
|
Cmd: ["sleep", "infinity"],
|
|
55974
56125
|
WorkingDir: SANDBOX_WORKDIR,
|
|
@@ -56070,7 +56221,10 @@ class DockerIsol8 {
|
|
|
56070
56221
|
if (this.persist) {
|
|
56071
56222
|
logger.debug(`[Persist] Leaving container running for inspection: ${container.id}`);
|
|
56072
56223
|
} else {
|
|
56073
|
-
|
|
56224
|
+
this.pool.release(container, image).catch((err) => {
|
|
56225
|
+
logger.debug(`[Pool] release failed: ${err}`);
|
|
56226
|
+
container.remove({ force: true }).catch(() => {});
|
|
56227
|
+
});
|
|
56074
56228
|
}
|
|
56075
56229
|
}
|
|
56076
56230
|
}
|
|
@@ -56483,7 +56637,7 @@ var package_default;
|
|
|
56483
56637
|
var init_package = __esm(() => {
|
|
56484
56638
|
package_default = {
|
|
56485
56639
|
name: "isol8",
|
|
56486
|
-
version: "0.10.
|
|
56640
|
+
version: "0.10.1",
|
|
56487
56641
|
description: "Secure code execution engine for AI agents",
|
|
56488
56642
|
author: "Illusion47586",
|
|
56489
56643
|
license: "MIT",
|
|
@@ -62020,7 +62174,7 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
62020
62174
|
console.log(`
|
|
62021
62175
|
[DONE] Setup complete!`);
|
|
62022
62176
|
});
|
|
62023
|
-
program2.command("run").description("Execute code in isol8").argument("[file]", "Script file to execute").option("-e, --eval <code>", "Execute inline code string").option("-r, --runtime <name>", "Force runtime (python, node, bun, deno, bash)").option("--net <mode>", "Network mode: none, host, filtered", "none").option("--allow <regex>", "Whitelist regex for filtered mode (repeatable)", collect, []).option("--deny <regex>", "Blacklist regex for filtered mode (repeatable)", collect, []).option("--out <file>", "Write output to file").option("--persistent", "Use persistent container").option("--timeout <ms>", "Execution timeout in milliseconds").option("--memory <limit>", "Memory limit (e.g. 512m, 1g)").option("--cpu <limit>", "CPU limit as fraction (e.g. 0.5, 2.0)").option("--image <name>", "Override Docker image").option("--pids-limit <n>", "Maximum number of processes").option("--writable", "Disable read-only root filesystem").option("--max-output <bytes>", "Maximum output size in bytes").option("--secret <KEY=VALUE>", "Secret env var (repeatable, values masked)", collect, []).option("--sandbox-size <size>", "Sandbox tmpfs size (e.g. 128m)").option("--tmp-size <size>", "Tmp tmpfs size (e.g. 256m, 512m)").option("--stdin <data>", "Data to pipe to stdin").option("--install <package>", "Install package for runtime (repeatable)", collect, []).option("--host <url>", "Execute on remote server").option("--key <key>", "API key for remote server").option("--no-stream", "Disable real-time output streaming").option("--debug", "Enable debug logging").option("--persist", "Keep container running after execution for inspection").option("--log-network", "Log all network requests (requires --net filtered)").action(async (file, opts) => {
|
|
62177
|
+
program2.command("run").description("Execute code in isol8").argument("[file]", "Script file to execute").option("-e, --eval <code>", "Execute inline code string").option("-r, --runtime <name>", "Force runtime (python, node, bun, deno, bash)").option("--net <mode>", "Network mode: none, host, filtered", "none").option("--allow <regex>", "Whitelist regex for filtered mode (repeatable)", collect, []).option("--deny <regex>", "Blacklist regex for filtered mode (repeatable)", collect, []).option("--out <file>", "Write output to file").option("--persistent", "Use persistent container").option("--timeout <ms>", "Execution timeout in milliseconds").option("--memory <limit>", "Memory limit (e.g. 512m, 1g)").option("--cpu <limit>", "CPU limit as fraction (e.g. 0.5, 2.0)").option("--image <name>", "Override Docker image").option("--pids-limit <n>", "Maximum number of processes").option("--writable", "Disable read-only root filesystem").option("--max-output <bytes>", "Maximum output size in bytes").option("--secret <KEY=VALUE>", "Secret env var (repeatable, values masked)", collect, []).option("--sandbox-size <size>", "Sandbox tmpfs size (e.g. 128m, 512m)").option("--tmp-size <size>", "Tmp tmpfs size (e.g. 256m, 512m)").option("--stdin <data>", "Data to pipe to stdin").option("--install <package>", "Install package for runtime (repeatable)", collect, []).option("--host <url>", "Execute on remote server").option("--key <key>", "API key for remote server").option("--no-stream", "Disable real-time output streaming").option("--debug", "Enable debug logging").option("--persist", "Keep container running after execution for inspection").option("--log-network", "Log all network requests (requires --net filtered)").option("--pool-strategy <mode>", "Pool strategy: fast (default) or secure", "fast").option("--pool-size <size>", "Pool size (number or 'clean,dirty' for fast mode)", "1,1").action(async (file, opts) => {
|
|
62024
62178
|
const { code, runtime, engineOptions, engine, stdinData, fileExtension } = await resolveRunInput(file, opts);
|
|
62025
62179
|
logger.debug(`[Run] Runtime: ${runtime}, mode: ${engineOptions.mode}`);
|
|
62026
62180
|
logger.debug(`[Run] Network: ${engineOptions.network}, timeout: ${engineOptions.timeoutMs}ms`);
|
|
@@ -62457,7 +62611,12 @@ async function resolveRunInput(file, opts) {
|
|
|
62457
62611
|
...opts.tmpSize ? { tmpSize: opts.tmpSize } : {},
|
|
62458
62612
|
debug: opts.debug ?? config.debug,
|
|
62459
62613
|
persist: opts.persist ?? false,
|
|
62460
|
-
...opts.logNetwork ? { logNetwork: true } : {}
|
|
62614
|
+
...opts.logNetwork ? { logNetwork: true } : {},
|
|
62615
|
+
poolStrategy: opts.poolStrategy === "secure" ? "secure" : "fast",
|
|
62616
|
+
poolSize: opts.poolSize ? opts.poolSize.includes(",") ? {
|
|
62617
|
+
clean: Number.parseInt(opts.poolSize.split(",")[0], 10),
|
|
62618
|
+
dirty: Number.parseInt(opts.poolSize.split(",")[1], 10)
|
|
62619
|
+
} : Number.parseInt(opts.poolSize, 10) : { clean: 1, dirty: 1 }
|
|
62461
62620
|
};
|
|
62462
62621
|
logger.debug(`[Run] Engine options: mode=${engineOptions.mode}, network=${engineOptions.network}`);
|
|
62463
62622
|
let fileExtension;
|
|
@@ -62502,4 +62661,4 @@ if (!process.argv.slice(2).length) {
|
|
|
62502
62661
|
}
|
|
62503
62662
|
program2.parse();
|
|
62504
62663
|
|
|
62505
|
-
//# debugId=
|
|
62664
|
+
//# debugId=92C54A0066AB3BEC64756E2164756E21
|
package/dist/index.js
CHANGED
|
@@ -375,47 +375,129 @@ class Semaphore {
|
|
|
375
375
|
// src/engine/pool.ts
|
|
376
376
|
class ContainerPool {
|
|
377
377
|
docker;
|
|
378
|
-
|
|
378
|
+
poolStrategy;
|
|
379
|
+
cleanPoolSize;
|
|
380
|
+
dirtyPoolSize;
|
|
379
381
|
createOptions;
|
|
382
|
+
networkMode;
|
|
383
|
+
securityMode;
|
|
380
384
|
pools = new Map;
|
|
381
385
|
replenishing = new Set;
|
|
382
386
|
pendingReplenishments = new Set;
|
|
387
|
+
cleaningInterval = null;
|
|
383
388
|
constructor(options) {
|
|
384
389
|
this.docker = options.docker;
|
|
385
|
-
this.
|
|
390
|
+
this.poolStrategy = options.poolStrategy ?? "fast";
|
|
386
391
|
this.createOptions = options.createOptions;
|
|
392
|
+
this.networkMode = options.networkMode;
|
|
393
|
+
this.securityMode = options.securityMode;
|
|
394
|
+
if (typeof options.poolSize === "number") {
|
|
395
|
+
this.cleanPoolSize = options.poolSize;
|
|
396
|
+
this.dirtyPoolSize = options.poolSize;
|
|
397
|
+
} else if (options.poolSize) {
|
|
398
|
+
this.cleanPoolSize = options.poolSize.clean ?? 1;
|
|
399
|
+
this.dirtyPoolSize = options.poolSize.dirty ?? 1;
|
|
400
|
+
} else {
|
|
401
|
+
this.cleanPoolSize = 1;
|
|
402
|
+
this.dirtyPoolSize = 1;
|
|
403
|
+
}
|
|
404
|
+
if (this.poolStrategy === "fast") {
|
|
405
|
+
this.startBackgroundCleaning();
|
|
406
|
+
}
|
|
387
407
|
}
|
|
388
408
|
async acquire(image) {
|
|
389
|
-
const pool = this.pools.get(image);
|
|
390
|
-
if (
|
|
391
|
-
|
|
409
|
+
const pool = this.pools.get(image) ?? { clean: [], dirty: [] };
|
|
410
|
+
if (this.poolStrategy === "fast") {
|
|
411
|
+
if (pool.clean.length > 0) {
|
|
412
|
+
const entry = pool.clean.shift();
|
|
413
|
+
this.pools.set(image, pool);
|
|
414
|
+
this.replenish(image);
|
|
415
|
+
return entry.container;
|
|
416
|
+
}
|
|
417
|
+
if (pool.dirty.length > 0 && pool.clean.length < this.cleanPoolSize) {
|
|
418
|
+
await this.cleanDirtyImmediate(image);
|
|
419
|
+
const updatedPool = this.pools.get(image);
|
|
420
|
+
if (updatedPool && updatedPool.clean.length > 0) {
|
|
421
|
+
const entry = updatedPool.clean.shift();
|
|
422
|
+
this.pools.set(image, updatedPool);
|
|
423
|
+
this.replenish(image);
|
|
424
|
+
return entry.container;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return this.createContainer(image);
|
|
428
|
+
}
|
|
429
|
+
if (pool.clean && pool.clean.length > 0) {
|
|
430
|
+
const entry = pool.clean.shift();
|
|
431
|
+
this.pools.set(image, { clean: pool.clean, dirty: [] });
|
|
432
|
+
await this.cleanupContainer(entry.container);
|
|
392
433
|
this.replenish(image);
|
|
393
434
|
return entry.container;
|
|
394
435
|
}
|
|
395
436
|
return this.createContainer(image);
|
|
396
437
|
}
|
|
397
438
|
async release(container, image) {
|
|
398
|
-
|
|
399
|
-
if (pool
|
|
400
|
-
|
|
439
|
+
let pool = this.pools.get(image);
|
|
440
|
+
if (!pool) {
|
|
441
|
+
pool = { clean: [], dirty: [] };
|
|
442
|
+
this.pools.set(image, pool);
|
|
443
|
+
}
|
|
444
|
+
if (this.poolStrategy === "fast") {
|
|
445
|
+
if (pool.dirty.length >= this.dirtyPoolSize) {
|
|
446
|
+
await container.remove({ force: true }).catch(() => {});
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
pool.dirty.push({ container, createdAt: Date.now() });
|
|
450
|
+
} else {
|
|
451
|
+
if (pool.clean.length >= this.cleanPoolSize) {
|
|
452
|
+
await container.remove({ force: true }).catch(() => {});
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (!pool.clean) {
|
|
456
|
+
pool.clean = [];
|
|
457
|
+
}
|
|
458
|
+
pool.clean.push({ container, createdAt: Date.now() });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
startBackgroundCleaning() {
|
|
462
|
+
this.cleaningInterval = setInterval(async () => {
|
|
463
|
+
for (const [_image, pool] of this.pools) {
|
|
464
|
+
for (let i = 0;i < this.dirtyPoolSize; i++) {
|
|
465
|
+
if (pool.dirty.length > 0 && pool.clean.length < this.cleanPoolSize) {
|
|
466
|
+
const entry = pool.dirty.shift();
|
|
467
|
+
try {
|
|
468
|
+
await this.cleanupContainer(entry.container);
|
|
469
|
+
pool.clean.push(entry);
|
|
470
|
+
} catch {
|
|
471
|
+
entry.container.remove({ force: true }).catch(() => {});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}, 5000);
|
|
477
|
+
}
|
|
478
|
+
async cleanDirtyImmediate(image) {
|
|
479
|
+
const pool = this.pools.get(image);
|
|
480
|
+
if (!pool || pool.dirty.length === 0 || pool.clean.length >= this.cleanPoolSize) {
|
|
401
481
|
return;
|
|
402
482
|
}
|
|
483
|
+
const entry = pool.dirty.shift();
|
|
403
484
|
try {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
485
|
+
await this.cleanupContainer(entry.container);
|
|
486
|
+
pool.clean.push(entry);
|
|
487
|
+
} catch {
|
|
488
|
+
entry.container.remove({ force: true }).catch(() => {});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async cleanupContainer(container) {
|
|
492
|
+
const needsCleanup = this.securityMode === "strict";
|
|
493
|
+
const needsIptables = this.networkMode === "filtered" && needsCleanup;
|
|
494
|
+
if (!needsCleanup) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
const cleanupCmd = needsIptables ? "pkill -9 -u sandbox 2>/dev/null; /usr/sbin/iptables -F OUTPUT 2>/dev/null; rm -rf /sandbox/* /sandbox/.[!.]* 2>/dev/null; true" : "pkill -9 -u sandbox 2>/dev/null; rm -rf /sandbox/* /sandbox/.[!.]* 2>/dev/null; true";
|
|
417
499
|
const cleanExec = await container.exec({
|
|
418
|
-
Cmd: ["sh", "-c",
|
|
500
|
+
Cmd: ["sh", "-c", cleanupCmd]
|
|
419
501
|
});
|
|
420
502
|
await cleanExec.start({ Detach: true });
|
|
421
503
|
let info = await cleanExec.inspect();
|
|
@@ -423,29 +505,45 @@ class ContainerPool {
|
|
|
423
505
|
await new Promise((r) => setTimeout(r, 5));
|
|
424
506
|
info = await cleanExec.inspect();
|
|
425
507
|
}
|
|
426
|
-
|
|
427
|
-
this.pools.set(image, pool);
|
|
428
|
-
} catch {
|
|
429
|
-
await container.remove({ force: true }).catch(() => {});
|
|
430
|
-
}
|
|
508
|
+
} catch {}
|
|
431
509
|
}
|
|
432
510
|
async warm(image) {
|
|
433
|
-
const pool = this.pools.get(image) ?? [];
|
|
511
|
+
const pool = this.pools.get(image) ?? { clean: [], dirty: [] };
|
|
434
512
|
this.pools.set(image, pool);
|
|
435
|
-
const needed = this.
|
|
513
|
+
const needed = this.poolStrategy === "fast" ? this.cleanPoolSize - pool.clean.length : this.cleanPoolSize - (pool.clean?.length ?? 0);
|
|
514
|
+
if (needed <= 0) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
436
517
|
const promises = [];
|
|
437
518
|
for (let i = 0;i < needed; i++) {
|
|
438
519
|
promises.push(this.createContainer(image).then((container) => {
|
|
439
|
-
|
|
520
|
+
if (this.poolStrategy === "fast") {
|
|
521
|
+
pool.clean.push({ container, createdAt: Date.now() });
|
|
522
|
+
} else {
|
|
523
|
+
if (!pool.clean) {
|
|
524
|
+
pool.clean = [];
|
|
525
|
+
}
|
|
526
|
+
pool.clean.push({ container, createdAt: Date.now() });
|
|
527
|
+
}
|
|
440
528
|
}));
|
|
441
529
|
}
|
|
442
530
|
await Promise.all(promises);
|
|
443
531
|
}
|
|
532
|
+
async stop() {
|
|
533
|
+
return this.drain();
|
|
534
|
+
}
|
|
444
535
|
async drain() {
|
|
536
|
+
if (this.cleaningInterval) {
|
|
537
|
+
clearInterval(this.cleaningInterval);
|
|
538
|
+
this.cleaningInterval = null;
|
|
539
|
+
}
|
|
445
540
|
await Promise.all(this.pendingReplenishments);
|
|
446
541
|
const promises = [];
|
|
447
542
|
for (const [, pool] of this.pools) {
|
|
448
|
-
for (const entry of pool) {
|
|
543
|
+
for (const entry of pool.clean ?? []) {
|
|
544
|
+
promises.push(entry.container.remove({ force: true }).catch(() => {}));
|
|
545
|
+
}
|
|
546
|
+
for (const entry of pool.dirty) {
|
|
449
547
|
promises.push(entry.container.remove({ force: true }).catch(() => {}));
|
|
450
548
|
}
|
|
451
549
|
}
|
|
@@ -464,30 +562,46 @@ class ContainerPool {
|
|
|
464
562
|
}
|
|
465
563
|
replenish(image) {
|
|
466
564
|
if (this.replenishing.has(image)) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
this.replenishing.add(image);
|
|
471
|
-
logger.debug(`[Pool] Starting background replenishment for image: ${image}`);
|
|
472
|
-
const promise = this.createContainer(image).then((container) => {
|
|
473
|
-
const pool = this.pools.get(image) ?? [];
|
|
474
|
-
if (pool.length < this.poolSize) {
|
|
475
|
-
pool.push({ container, createdAt: Date.now() });
|
|
476
|
-
this.pools.set(image, pool);
|
|
477
|
-
logger.debug(`[Pool] Replenished container ${container.id} added to pool for ${image}. Pool size: ${pool.length}`);
|
|
478
|
-
} else {
|
|
479
|
-
logger.debug(`[Pool] Replenished container ${container.id} not needed (pool for ${image} is full), destroying`);
|
|
480
|
-
container.remove({ force: true }).catch((err) => {
|
|
481
|
-
logger.error(`[Pool] Error destroying unneeded replenished container ${container.id}:`, err);
|
|
482
|
-
});
|
|
565
|
+
if (this.replenishing.has(image)) {
|
|
566
|
+
return;
|
|
483
567
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
568
|
+
const pool = this.pools.get(image);
|
|
569
|
+
const currentSize = pool ? this.poolStrategy === "fast" ? pool.clean.length : pool.clean?.length ?? 0 : 0;
|
|
570
|
+
const targetSize = this.poolStrategy === "fast" ? this.cleanPoolSize : this.cleanPoolSize;
|
|
571
|
+
if (currentSize >= targetSize) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
this.replenishing.add(image);
|
|
575
|
+
const promise = this.createContainer(image).then((container) => {
|
|
576
|
+
const p = this.pools.get(image);
|
|
577
|
+
if (!p) {
|
|
578
|
+
container.remove({ force: true }).catch(() => {});
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (this.poolStrategy === "fast") {
|
|
582
|
+
if (p.clean.length < this.cleanPoolSize) {
|
|
583
|
+
p.clean.push({ container, createdAt: Date.now() });
|
|
584
|
+
} else {
|
|
585
|
+
container.remove({ force: true }).catch(() => {});
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
if (!p.clean) {
|
|
589
|
+
p.clean = [];
|
|
590
|
+
}
|
|
591
|
+
if (p.clean.length < this.cleanPoolSize) {
|
|
592
|
+
p.clean.push({ container, createdAt: Date.now() });
|
|
593
|
+
} else {
|
|
594
|
+
container.remove({ force: true }).catch(() => {});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}).catch((err) => {
|
|
598
|
+
logger.error(`[Pool] Error during replenishment for ${image}:`, err);
|
|
599
|
+
}).finally(() => {
|
|
600
|
+
this.replenishing.delete(image);
|
|
601
|
+
this.pendingReplenishments.delete(promise);
|
|
602
|
+
});
|
|
603
|
+
this.pendingReplenishments.add(promise);
|
|
604
|
+
}
|
|
491
605
|
}
|
|
492
606
|
}
|
|
493
607
|
var init_pool = __esm(() => {
|
|
@@ -634,32 +748,53 @@ var exports_docker = {};
|
|
|
634
748
|
__export(exports_docker, {
|
|
635
749
|
DockerIsol8: () => DockerIsol8
|
|
636
750
|
});
|
|
637
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
638
751
|
import { randomUUID } from "node:crypto";
|
|
639
752
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
640
753
|
import { PassThrough } from "node:stream";
|
|
641
754
|
import Docker from "dockerode";
|
|
642
755
|
async function writeFileViaExec(container, filePath, content) {
|
|
643
756
|
const data = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
reject(new Error(`Failed to spawn docker exec: ${err.message}`));
|
|
757
|
+
const b64 = data.toString("base64");
|
|
758
|
+
if (b64.length < 20000) {
|
|
759
|
+
const exec = await container.exec({
|
|
760
|
+
Cmd: ["sh", "-c", `printf '%s' '${b64}' | base64 -d > ${filePath}`],
|
|
761
|
+
User: "sandbox"
|
|
650
762
|
});
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
|
|
763
|
+
await exec.start({ Detach: true });
|
|
764
|
+
let info2 = await exec.inspect();
|
|
765
|
+
while (info2.Running) {
|
|
766
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
767
|
+
info2 = await exec.inspect();
|
|
768
|
+
}
|
|
769
|
+
if (info2.ExitCode !== 0) {
|
|
770
|
+
throw new Error(`Failed to write file ${filePath} in container (exit code ${info2.ExitCode})`);
|
|
771
|
+
}
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
const tempPath = `/tmp/b64_${Date.now()}.tmp`;
|
|
775
|
+
const chunkSize = 8000;
|
|
776
|
+
for (let i = 0;i < b64.length; i += chunkSize) {
|
|
777
|
+
const chunk = b64.slice(i, i + chunkSize);
|
|
778
|
+
const exec = await container.exec({
|
|
779
|
+
Cmd: ["sh", "-c", `printf '%s' '${chunk}' >> ${tempPath}`],
|
|
780
|
+
User: "sandbox"
|
|
661
781
|
});
|
|
782
|
+
await exec.start({ Detach: true });
|
|
783
|
+
await exec.inspect();
|
|
784
|
+
}
|
|
785
|
+
const decodeExec = await container.exec({
|
|
786
|
+
Cmd: ["sh", "-c", `base64 -d ${tempPath} > ${filePath} && rm ${tempPath}`],
|
|
787
|
+
User: "sandbox"
|
|
662
788
|
});
|
|
789
|
+
await decodeExec.start({ Detach: true });
|
|
790
|
+
let info = await decodeExec.inspect();
|
|
791
|
+
while (info.Running) {
|
|
792
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
793
|
+
info = await decodeExec.inspect();
|
|
794
|
+
}
|
|
795
|
+
if (info.ExitCode !== 0) {
|
|
796
|
+
throw new Error(`Failed to write file ${filePath} in container (exit code ${info.ExitCode})`);
|
|
797
|
+
}
|
|
663
798
|
}
|
|
664
799
|
async function readFileViaExec(container, filePath) {
|
|
665
800
|
const exec = await container.exec({
|
|
@@ -829,10 +964,13 @@ class DockerIsol8 {
|
|
|
829
964
|
security;
|
|
830
965
|
persist;
|
|
831
966
|
logNetwork;
|
|
967
|
+
poolStrategy;
|
|
968
|
+
poolSize;
|
|
832
969
|
auditLogger;
|
|
833
970
|
container = null;
|
|
834
971
|
persistentRuntime = null;
|
|
835
972
|
pool = null;
|
|
973
|
+
imageCache = new Map;
|
|
836
974
|
constructor(options = {}, maxConcurrent = 10) {
|
|
837
975
|
this.docker = options.docker ?? new Docker;
|
|
838
976
|
this.mode = options.mode ?? "ephemeral";
|
|
@@ -852,6 +990,8 @@ class DockerIsol8 {
|
|
|
852
990
|
this.persist = options.persist ?? false;
|
|
853
991
|
this.security = options.security ?? { seccomp: "strict" };
|
|
854
992
|
this.logNetwork = options.logNetwork ?? false;
|
|
993
|
+
this.poolStrategy = options.poolStrategy ?? "fast";
|
|
994
|
+
this.poolSize = options.poolSize ?? { clean: 1, dirty: 1 };
|
|
855
995
|
if (options.audit) {
|
|
856
996
|
this.auditLogger = new AuditLogger(options.audit);
|
|
857
997
|
}
|
|
@@ -1101,13 +1241,21 @@ class DockerIsol8 {
|
|
|
1101
1241
|
if (this.overrideImage) {
|
|
1102
1242
|
return this.overrideImage;
|
|
1103
1243
|
}
|
|
1244
|
+
const cacheKey = adapter.image;
|
|
1245
|
+
const cached = this.imageCache.get(cacheKey);
|
|
1246
|
+
if (cached) {
|
|
1247
|
+
return cached;
|
|
1248
|
+
}
|
|
1104
1249
|
const customTag = `${adapter.image}-custom`;
|
|
1250
|
+
let resolvedImage;
|
|
1105
1251
|
try {
|
|
1106
1252
|
await this.docker.getImage(customTag).inspect();
|
|
1107
|
-
|
|
1253
|
+
resolvedImage = customTag;
|
|
1108
1254
|
} catch {
|
|
1109
|
-
|
|
1255
|
+
resolvedImage = adapter.image;
|
|
1110
1256
|
}
|
|
1257
|
+
this.imageCache.set(cacheKey, resolvedImage);
|
|
1258
|
+
return resolvedImage;
|
|
1111
1259
|
}
|
|
1112
1260
|
async executeEphemeral(req, startTime) {
|
|
1113
1261
|
const adapter = this.getAdapter(req.runtime);
|
|
@@ -1116,7 +1264,10 @@ class DockerIsol8 {
|
|
|
1116
1264
|
if (!this.pool) {
|
|
1117
1265
|
this.pool = new ContainerPool({
|
|
1118
1266
|
docker: this.docker,
|
|
1119
|
-
|
|
1267
|
+
poolStrategy: this.poolStrategy,
|
|
1268
|
+
poolSize: this.poolSize,
|
|
1269
|
+
networkMode: this.network,
|
|
1270
|
+
securityMode: this.security.seccomp ?? "strict",
|
|
1120
1271
|
createOptions: {
|
|
1121
1272
|
Cmd: ["sleep", "infinity"],
|
|
1122
1273
|
WorkingDir: SANDBOX_WORKDIR,
|
|
@@ -1218,7 +1369,10 @@ class DockerIsol8 {
|
|
|
1218
1369
|
if (this.persist) {
|
|
1219
1370
|
logger.debug(`[Persist] Leaving container running for inspection: ${container.id}`);
|
|
1220
1371
|
} else {
|
|
1221
|
-
|
|
1372
|
+
this.pool.release(container, image).catch((err) => {
|
|
1373
|
+
logger.debug(`[Pool] release failed: ${err}`);
|
|
1374
|
+
container.remove({ force: true }).catch(() => {});
|
|
1375
|
+
});
|
|
1222
1376
|
}
|
|
1223
1377
|
}
|
|
1224
1378
|
}
|
|
@@ -1844,7 +1998,7 @@ init_logger();
|
|
|
1844
1998
|
// package.json
|
|
1845
1999
|
var package_default = {
|
|
1846
2000
|
name: "isol8",
|
|
1847
|
-
version: "0.10.
|
|
2001
|
+
version: "0.10.1",
|
|
1848
2002
|
description: "Secure code execution engine for AI agents",
|
|
1849
2003
|
author: "Illusion47586",
|
|
1850
2004
|
license: "MIT",
|
|
@@ -2182,4 +2336,4 @@ export {
|
|
|
2182
2336
|
BunAdapter
|
|
2183
2337
|
};
|
|
2184
2338
|
|
|
2185
|
-
//# debugId=
|
|
2339
|
+
//# debugId=C44D450DD130A4AD64756E2164756E21
|
|
@@ -46,10 +46,13 @@ export declare class DockerIsol8 implements Isol8Engine {
|
|
|
46
46
|
private readonly security;
|
|
47
47
|
private readonly persist;
|
|
48
48
|
private readonly logNetwork;
|
|
49
|
+
private readonly poolStrategy;
|
|
50
|
+
private readonly poolSize;
|
|
49
51
|
private readonly auditLogger?;
|
|
50
52
|
private container;
|
|
51
53
|
private persistentRuntime;
|
|
52
54
|
private pool;
|
|
55
|
+
private readonly imageCache;
|
|
53
56
|
/**
|
|
54
57
|
* @param options - Sandbox configuration options.
|
|
55
58
|
* @param maxConcurrent - Maximum number of concurrent executions (controls the internal semaphore).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/engine/docker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../../src/engine/docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,55 +5,90 @@
|
|
|
5
5
|
* starts containers so they're ready for immediate use, eliminating
|
|
6
6
|
* the create+start overhead (~100-200ms per execution).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Supports two strategies:
|
|
9
|
+
* - "secure": Clean container before returning (slower but ensures clean state)
|
|
10
|
+
* - "fast": Dual-pool system - instant acquire from clean pool, background cleanup
|
|
10
11
|
*/
|
|
11
12
|
import type Docker from "dockerode";
|
|
12
13
|
/** Configuration for the container pool. */
|
|
13
14
|
export interface PoolOptions {
|
|
14
15
|
/** Docker client instance. */
|
|
15
16
|
docker: Docker;
|
|
16
|
-
/**
|
|
17
|
-
|
|
17
|
+
/** Pool strategy: "secure" or "fast". @default "fast" */
|
|
18
|
+
poolStrategy?: "secure" | "fast";
|
|
19
|
+
/** Pool size configuration.
|
|
20
|
+
* For "secure" mode: number of containers to keep warm
|
|
21
|
+
* For "fast" mode: { clean: ready containers, dirty: being cleaned }
|
|
22
|
+
* @default 1 (for fast mode: { clean: 1, dirty: 1 })
|
|
23
|
+
*/
|
|
24
|
+
poolSize?: number | {
|
|
25
|
+
clean: number;
|
|
26
|
+
dirty: number;
|
|
27
|
+
};
|
|
18
28
|
/** Container creation options (HostConfig, Env, etc). */
|
|
19
29
|
createOptions: Omit<Docker.ContainerCreateOptions, "Image">;
|
|
30
|
+
/** Network mode to determine if iptables cleanup is needed. */
|
|
31
|
+
networkMode: "none" | "host" | "filtered";
|
|
32
|
+
/** Security mode - if strict, run process cleanup between executions */
|
|
33
|
+
securityMode: "strict" | "unconfined" | "custom";
|
|
20
34
|
}
|
|
21
35
|
/**
|
|
22
36
|
* A per-image warm container pool. Maintains pre-started containers
|
|
23
37
|
* ready for immediate exec, recycling them after use.
|
|
38
|
+
*
|
|
39
|
+
* Supports two strategies:
|
|
40
|
+
* - "secure": Single pool, cleanup in acquire (current behavior)
|
|
41
|
+
* - "fast": Dual pools (clean/dirty), instant acquire, background cleanup
|
|
24
42
|
*/
|
|
25
43
|
export declare class ContainerPool {
|
|
26
44
|
private readonly docker;
|
|
27
|
-
private readonly
|
|
45
|
+
private readonly poolStrategy;
|
|
46
|
+
private readonly cleanPoolSize;
|
|
47
|
+
private readonly dirtyPoolSize;
|
|
28
48
|
private readonly createOptions;
|
|
49
|
+
private readonly networkMode;
|
|
50
|
+
private readonly securityMode;
|
|
29
51
|
private readonly pools;
|
|
30
52
|
private readonly replenishing;
|
|
31
53
|
private readonly pendingReplenishments;
|
|
54
|
+
private cleaningInterval;
|
|
32
55
|
constructor(options: PoolOptions);
|
|
33
56
|
/**
|
|
34
57
|
* Acquire a started container for the given image.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
58
|
+
* - "secure" mode: Clean container before returning
|
|
59
|
+
* - "fast" mode: Instant return from clean pool, create new if empty
|
|
37
60
|
*/
|
|
38
61
|
acquire(image: string): Promise<Docker.Container>;
|
|
39
62
|
/**
|
|
40
|
-
* Return a container to the pool
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* If the pool is full, the container is destroyed instead.
|
|
63
|
+
* Return a container to the pool.
|
|
64
|
+
* - "secure" mode: Add to pool, cleanup happens on next acquire
|
|
65
|
+
* - "fast" mode: Add to dirty pool for background cleaning
|
|
44
66
|
*/
|
|
45
67
|
release(container: Docker.Container, image: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Start background cleaning for fast mode.
|
|
70
|
+
* Runs every 5 seconds to clean dirty containers.
|
|
71
|
+
*/
|
|
72
|
+
private startBackgroundCleaning;
|
|
73
|
+
/**
|
|
74
|
+
* Clean a dirty container immediately and add to clean pool.
|
|
75
|
+
*/
|
|
76
|
+
private cleanDirtyImmediate;
|
|
77
|
+
private cleanupContainer;
|
|
46
78
|
/**
|
|
47
79
|
* Pre-warm the pool for a specific image.
|
|
48
|
-
* Creates containers up to poolSize.
|
|
49
80
|
*/
|
|
50
81
|
warm(image: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Stop the pool and clean up resources.
|
|
84
|
+
* Alias for drain() - destroys all containers and stops background cleaning.
|
|
85
|
+
*/
|
|
86
|
+
stop(): Promise<void>;
|
|
51
87
|
/**
|
|
52
88
|
* Destroy all pooled containers and clear the pool.
|
|
53
89
|
*/
|
|
54
90
|
drain(): Promise<void>;
|
|
55
91
|
private createContainer;
|
|
56
|
-
/** Replenish the pool in the background (non-blocking). */
|
|
57
92
|
private replenish;
|
|
58
93
|
}
|
|
59
94
|
//# sourceMappingURL=pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../../src/engine/pool.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../../src/engine/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,MAAM,MAAM,WAAW,CAAC;AAGpC,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,yDAAyD;IACzD,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;IAC5D,+DAA+D;IAC/D,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;IAC1C,wEAAwE;IACxE,YAAY,EAAE,QAAQ,GAAG,YAAY,GAAG,QAAQ,CAAC;CAClD;AAYD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA+C;IAC7E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqC;IAClE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA4B;IAClE,OAAO,CAAC,gBAAgB,CAA+C;gBAE3D,OAAO,EAAE,WAAW;IA0BhC;;;;OAIG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;IAkDvD;;;;OAIG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCxE;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;OAEG;YACW,mBAAmB;YAenB,gBAAgB;IA4B9B;;OAEG;IACG,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCxC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAyBd,eAAe;IAW7B,OAAO,CAAC,SAAS;CAwDlB"}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -248,6 +248,23 @@ export interface Isol8Options {
|
|
|
248
248
|
security?: SecurityConfig;
|
|
249
249
|
/** Audit logging configuration. */
|
|
250
250
|
audit?: AuditConfig;
|
|
251
|
+
/**
|
|
252
|
+
* Pool strategy for container reuse.
|
|
253
|
+
* - "secure": Clean container before returning (slower but ensures clean state)
|
|
254
|
+
* - "fast": Use dual-pool system - instant acquire, background cleanup (faster)
|
|
255
|
+
* @default "fast"
|
|
256
|
+
*/
|
|
257
|
+
poolStrategy?: "secure" | "fast";
|
|
258
|
+
/**
|
|
259
|
+
* Pool size configuration.
|
|
260
|
+
* For "secure" mode: number of containers to keep warm
|
|
261
|
+
* For "fast" mode: { clean: number of ready containers, dirty: number being cleaned }
|
|
262
|
+
* @default 1 (for fast mode: { clean: 1, dirty: 1 })
|
|
263
|
+
*/
|
|
264
|
+
poolSize?: number | {
|
|
265
|
+
clean: number;
|
|
266
|
+
dirty: number;
|
|
267
|
+
};
|
|
251
268
|
}
|
|
252
269
|
/**
|
|
253
270
|
* The core isol8 engine abstraction. Both {@link DockerIsol8} and {@link RemoteIsol8}
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|