isol8 0.10.0 → 0.10.2-alpha.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/cli.js CHANGED
@@ -6318,7 +6318,7 @@ var require_bcrypt_pbkdf = __commonJS((exports, module) => {
6318
6318
 
6319
6319
  // node_modules/cpu-features/build/Release/cpufeatures.node
6320
6320
  var require_cpufeatures = __commonJS((exports, module) => {
6321
- module.exports = __require("./cpufeatures-tjjrgpt7.node");
6321
+ module.exports = __require("./cpufeatures-8g73ch7n.node");
6322
6322
  });
6323
6323
 
6324
6324
  // node_modules/cpu-features/lib/index.js
@@ -6931,12 +6931,12 @@ var require_utils2 = __commonJS((exports, module) => {
6931
6931
 
6932
6932
  // node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
6933
6933
  var require_sshcrypto = __commonJS((exports, module) => {
6934
- module.exports = __require("./sshcrypto-0209sx47.node");
6934
+ module.exports = __require("./sshcrypto-f6atjna1.node");
6935
6935
  });
6936
6936
 
6937
6937
  // node_modules/ssh2/lib/protocol/crypto/poly1305.js
6938
6938
  var require_poly1305 = __commonJS((exports, module) => {
6939
- var __dirname = "/home/runner/work/isol8/isol8/node_modules/ssh2/lib/protocol/crypto", __filename = "/home/runner/work/isol8/isol8/node_modules/ssh2/lib/protocol/crypto/poly1305.js";
6939
+ var __dirname = "/Users/dhruv/Developer/Projects/isol8/node_modules/ssh2/lib/protocol/crypto", __filename = "/Users/dhruv/Developer/Projects/isol8/node_modules/ssh2/lib/protocol/crypto/poly1305.js";
6940
6940
  var createPoly1305 = function() {
6941
6941
  var _scriptDir = typeof document !== "undefined" && document.currentScript ? document.currentScript.src : undefined;
6942
6942
  if (typeof __filename !== "undefined")
@@ -8681,7 +8681,7 @@ ${formatted}-----END ${type} KEY-----`;
8681
8681
  }
8682
8682
  return Buffer.from(hex, "hex");
8683
8683
  }
8684
- return function genOpenSSLRSAPriv2(n, e, d, iqmp, p, q) {
8684
+ return function genOpenSSLRSAPriv(n, e, d, iqmp, p, q) {
8685
8685
  const bn_d = bigIntFromBuffer(d);
8686
8686
  const dmp1 = bigIntToBuffer(bn_d % (bigIntFromBuffer(p) - 1n));
8687
8687
  const dmq1 = bigIntToBuffer(bn_d % (bigIntFromBuffer(q) - 1n));
@@ -9709,7 +9709,7 @@ ${formatted}-----END ${type} KEY-----`;
9709
9709
 
9710
9710
  // node_modules/ssh2/lib/agent.js
9711
9711
  var require_agent = __commonJS((exports, module) => {
9712
- var __dirname = "/home/runner/work/isol8/isol8/node_modules/ssh2/lib";
9712
+ var __dirname = "/Users/dhruv/Developer/Projects/isol8/node_modules/ssh2/lib";
9713
9713
  var { Socket } = __require("net");
9714
9714
  var { Duplex } = __require("stream");
9715
9715
  var { resolve } = __require("path");
@@ -21682,7 +21682,7 @@ var require__stream_writable = __commonJS((exports, module) => {
21682
21682
  }
21683
21683
  });
21684
21684
  } else {
21685
- realHasInstance = function realHasInstance2(object) {
21685
+ realHasInstance = function realHasInstance(object) {
21686
21686
  return object instanceof this;
21687
21687
  };
21688
21688
  }
@@ -22480,28 +22480,28 @@ var require_end_of_stream = __commonJS((exports, module) => {
22480
22480
  callback = once(callback || noop);
22481
22481
  var readable = opts.readable || opts.readable !== false && stream.readable;
22482
22482
  var writable = opts.writable || opts.writable !== false && stream.writable;
22483
- var onlegacyfinish = function onlegacyfinish2() {
22483
+ var onlegacyfinish = function onlegacyfinish() {
22484
22484
  if (!stream.writable)
22485
22485
  onfinish();
22486
22486
  };
22487
22487
  var writableEnded = stream._writableState && stream._writableState.finished;
22488
- var onfinish = function onfinish2() {
22488
+ var onfinish = function onfinish() {
22489
22489
  writable = false;
22490
22490
  writableEnded = true;
22491
22491
  if (!readable)
22492
22492
  callback.call(stream);
22493
22493
  };
22494
22494
  var readableEnded = stream._readableState && stream._readableState.endEmitted;
22495
- var onend = function onend2() {
22495
+ var onend = function onend() {
22496
22496
  readable = false;
22497
22497
  readableEnded = true;
22498
22498
  if (!writable)
22499
22499
  callback.call(stream);
22500
22500
  };
22501
- var onerror = function onerror2(err) {
22501
+ var onerror = function onerror(err) {
22502
22502
  callback.call(stream, err);
22503
22503
  };
22504
- var onclose = function onclose2() {
22504
+ var onclose = function onclose() {
22505
22505
  var err;
22506
22506
  if (readable && !readableEnded) {
22507
22507
  if (!stream._readableState || !stream._readableState.ended)
@@ -22514,7 +22514,7 @@ var require_end_of_stream = __commonJS((exports, module) => {
22514
22514
  return callback.call(stream, err);
22515
22515
  }
22516
22516
  };
22517
- var onrequest = function onrequest2() {
22517
+ var onrequest = function onrequest() {
22518
22518
  stream.req.on("finish", onfinish);
22519
22519
  };
22520
22520
  if (isRequest(stream)) {
@@ -22671,7 +22671,7 @@ var require_async_iterator = __commonJS((exports, module) => {
22671
22671
  });
22672
22672
  });
22673
22673
  }), _Object$setPrototypeO), AsyncIteratorPrototype);
22674
- var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator2(stream) {
22674
+ var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) {
22675
22675
  var _Object$create;
22676
22676
  var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, {
22677
22677
  value: stream,
@@ -22860,7 +22860,7 @@ var require__stream_readable = __commonJS((exports, module) => {
22860
22860
  var Duplex;
22861
22861
  Readable.ReadableState = ReadableState;
22862
22862
  var EE = __require("events").EventEmitter;
22863
- var EElistenerCount = function EElistenerCount2(emitter, type) {
22863
+ var EElistenerCount = function EElistenerCount(emitter, type) {
22864
22864
  return emitter.listeners(type).length;
22865
22865
  };
22866
22866
  var Stream = __require("stream");
@@ -22877,7 +22877,7 @@ var require__stream_readable = __commonJS((exports, module) => {
22877
22877
  if (debugUtil && debugUtil.debuglog) {
22878
22878
  debug = debugUtil.debuglog("stream");
22879
22879
  } else {
22880
- debug = function debug2() {};
22880
+ debug = function debug() {};
22881
22881
  }
22882
22882
  var BufferList = require_buffer_list();
22883
22883
  var destroyImpl = require_destroy();
@@ -25535,14 +25535,14 @@ var require_BufferList = __commonJS((exports, module) => {
25535
25535
  if (srcEnd <= 0) {
25536
25536
  return dst || Buffer2.alloc(0);
25537
25537
  }
25538
- const copy2 = !!dst;
25538
+ const copy = !!dst;
25539
25539
  const off = this._offset(srcStart);
25540
25540
  const len = srcEnd - srcStart;
25541
25541
  let bytes = len;
25542
- let bufoff = copy2 && dstStart || 0;
25542
+ let bufoff = copy && dstStart || 0;
25543
25543
  let start = off[1];
25544
25544
  if (srcStart === 0 && srcEnd === this.length) {
25545
- if (!copy2) {
25545
+ if (!copy) {
25546
25546
  return this._bufs.length === 1 ? this._bufs[0] : Buffer2.concat(this._bufs, this.length);
25547
25547
  }
25548
25548
  for (let i = 0;i < this._bufs.length; i++) {
@@ -25552,9 +25552,9 @@ var require_BufferList = __commonJS((exports, module) => {
25552
25552
  return dst;
25553
25553
  }
25554
25554
  if (bytes <= this._bufs[off[0]].length - start) {
25555
- return copy2 ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes) : this._bufs[off[0]].slice(start, start + bytes);
25555
+ return copy ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes) : this._bufs[off[0]].slice(start, start + bytes);
25556
25556
  }
25557
- if (!copy2) {
25557
+ if (!copy) {
25558
25558
  dst = Buffer2.allocUnsafe(len);
25559
25559
  }
25560
25560
  for (let i = off[0];i < this._bufs.length; i++) {
@@ -25776,7 +25776,7 @@ var require_bl = __commonJS((exports, module) => {
25776
25776
  }
25777
25777
  if (typeof callback === "function") {
25778
25778
  this._callback = callback;
25779
- const piper = function piper2(err) {
25779
+ const piper = function piper(err) {
25780
25780
  if (this._callback) {
25781
25781
  this._callback(err);
25782
25782
  this._callback = null;
@@ -34839,7 +34839,7 @@ var require_writer2 = __commonJS((exports, module) => {
34839
34839
  this.tail = this.head;
34840
34840
  this.states = null;
34841
34841
  }
34842
- var create = function create2() {
34842
+ var create = function create() {
34843
34843
  return util.Buffer ? function create_buffer_setup() {
34844
34844
  return (Writer.create = function create_buffer() {
34845
34845
  return new BufferWriter;
@@ -35063,12 +35063,12 @@ var require_reader2 = __commonJS((exports, module) => {
35063
35063
  if (buffer instanceof Uint8Array || Array.isArray(buffer))
35064
35064
  return new Reader(buffer);
35065
35065
  throw Error("illegal buffer");
35066
- } : function create_array2(buffer) {
35066
+ } : function create_array(buffer) {
35067
35067
  if (Array.isArray(buffer))
35068
35068
  return new Reader(buffer);
35069
35069
  throw Error("illegal buffer");
35070
35070
  };
35071
- var create = function create2() {
35071
+ var create = function create() {
35072
35072
  return util.Buffer ? function create_buffer_setup(buffer) {
35073
35073
  return (Reader.create = function create_buffer(buffer2) {
35074
35074
  return util.Buffer.isBuffer(buffer2) ? new BufferReader(buffer2) : create_array(buffer2);
@@ -35494,10 +35494,10 @@ var require_fetch = __commonJS((exports, module) => {
35494
35494
  // node_modules/@protobufjs/path/index.js
35495
35495
  var require_path = __commonJS((exports) => {
35496
35496
  var path = exports;
35497
- var isAbsolute = path.isAbsolute = function isAbsolute2(path2) {
35497
+ var isAbsolute = path.isAbsolute = function isAbsolute(path2) {
35498
35498
  return /^(?:\/|\w+:)/.test(path2);
35499
35499
  };
35500
- var normalize = path.normalize = function normalize2(path2) {
35500
+ var normalize = path.normalize = function normalize(path2) {
35501
35501
  path2 = path2.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
35502
35502
  var parts = path2.split("/"), absolute = isAbsolute(path2), prefix = "";
35503
35503
  if (absolute)
@@ -35662,7 +35662,7 @@ var require_namespace = __commonJS((exports, module) => {
35662
35662
  object.onRemove(this);
35663
35663
  return clearCache(this);
35664
35664
  };
35665
- Namespace.prototype.define = function define2(path, json) {
35665
+ Namespace.prototype.define = function define(path, json) {
35666
35666
  if (util.isString(path))
35667
35667
  path = path.split(".");
35668
35668
  else if (!Array.isArray(path))
@@ -42478,7 +42478,7 @@ var require_src3 = __commonJS((exports) => {
42478
42478
 
42479
42479
  // node_modules/@grpc/grpc-js/build/src/channelz.js
42480
42480
  var require_channelz = __commonJS((exports) => {
42481
- var __dirname = "/home/runner/work/isol8/isol8/node_modules/@grpc/grpc-js/build/src";
42481
+ var __dirname = "/Users/dhruv/Developer/Projects/isol8/node_modules/@grpc/grpc-js/build/src";
42482
42482
  Object.defineProperty(exports, "__esModule", { value: true });
42483
42483
  exports.registerChannelzSocket = exports.registerChannelzServer = exports.registerChannelzSubchannel = exports.registerChannelzChannel = exports.ChannelzCallTrackerStub = exports.ChannelzCallTracker = exports.ChannelzChildrenTrackerStub = exports.ChannelzChildrenTracker = exports.ChannelzTrace = exports.ChannelzTraceStub = undefined;
42484
42484
  exports.unregisterChannelzRef = unregisterChannelzRef;
@@ -47881,7 +47881,7 @@ var require_duration = __commonJS((exports) => {
47881
47881
 
47882
47882
  // node_modules/@grpc/grpc-js/build/src/orca.js
47883
47883
  var require_orca = __commonJS((exports) => {
47884
- var __dirname = "/home/runner/work/isol8/isol8/node_modules/@grpc/grpc-js/build/src";
47884
+ var __dirname = "/Users/dhruv/Developer/Projects/isol8/node_modules/@grpc/grpc-js/build/src";
47885
47885
  Object.defineProperty(exports, "__esModule", { value: true });
47886
47886
  exports.OrcaOobMetricsSubchannelWrapper = exports.GRPC_METRICS_HEADER = exports.ServerMetricRecorder = exports.PerRequestMetricRecorder = undefined;
47887
47887
  exports.createOrcaClient = createOrcaClient;
@@ -53270,7 +53270,7 @@ var require_dist = __commonJS((exports) => {
53270
53270
 
53271
53271
  // node_modules/dockerode/lib/session.js
53272
53272
  var require_session = __commonJS((exports, module) => {
53273
- var __dirname = "/home/runner/work/isol8/isol8/node_modules/dockerode/lib";
53273
+ var __dirname = "/Users/dhruv/Developer/Projects/isol8/node_modules/dockerode/lib";
53274
53274
  var grpc = require_src4();
53275
53275
  var protoLoader = require_src5();
53276
53276
  var path = __require("path");
@@ -54496,7 +54496,7 @@ var require_docker = __commonJS((exports, module) => {
54496
54496
  stream: true,
54497
54497
  stdout: true,
54498
54498
  stderr: true
54499
- }, function handler2(err2, stream) {
54499
+ }, function handler(err2, stream) {
54500
54500
  if (err2)
54501
54501
  return callback(err2, null, container);
54502
54502
  hub.emit("stream", stream);
@@ -55213,47 +55213,129 @@ class Semaphore {
55213
55213
  // src/engine/pool.ts
55214
55214
  class ContainerPool {
55215
55215
  docker;
55216
- poolSize;
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.poolSize = options.poolSize ?? 2;
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 (pool && pool.length > 0) {
55229
- const entry = pool.shift();
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
- const pool = this.pools.get(image) ?? [];
55237
- if (pool.length >= this.poolSize) {
55238
- await container.remove({ force: true }).catch(() => {});
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
- const killExec = await container.exec({
55243
- Cmd: [
55244
- "sh",
55245
- "-c",
55246
- "pkill -9 -u sandbox 2>/dev/null; /usr/sbin/iptables -F OUTPUT 2>/dev/null; true"
55247
- ]
55248
- });
55249
- await killExec.start({ Detach: true });
55250
- let killInfo = await killExec.inspect();
55251
- while (killInfo.Running) {
55252
- await new Promise((r) => setTimeout(r, 5));
55253
- killInfo = await killExec.inspect();
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", "rm -rf /sandbox/* /sandbox/.[!.]* 2>/dev/null; true"]
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
- pool.push({ container, createdAt: Date.now() });
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.poolSize - pool.length;
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
- pool.push({ container, createdAt: Date.now() });
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
- logger.debug(`[Pool] Replenishment for ${image} already in progress`);
55306
- return;
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
- }).catch((err) => {
55323
- logger.error(`[Pool] Error during replenishment for ${image}:`, err);
55324
- }).finally(() => {
55325
- this.replenishing.delete(image);
55326
- this.pendingReplenishments.delete(promise);
55327
- });
55328
- this.pendingReplenishments.add(promise);
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
- return new Promise((resolve2, reject) => {
55497
- const child = spawn2("docker", ["exec", "-i", "-u", "sandbox", container.id, "sh", "-c", `cat > ${filePath}`], {
55498
- stdio: ["pipe", "ignore", "pipe"]
55499
- });
55500
- child.on("error", (err) => {
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
- let stderr = "";
55504
- child.stderr.on("data", (chunk) => stderr += chunk.toString());
55505
- child.stdin.write(data);
55506
- child.stdin.end();
55507
- child.on("close", (code) => {
55508
- if (code !== 0) {
55509
- reject(new Error(`Failed to write file ${filePath}: ${stderr} (exit code ${code})`));
55510
- } else {
55511
- resolve2();
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
- return customTag;
56105
+ resolvedImage = customTag;
55960
56106
  } catch {
55961
- return adapter.image;
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
- poolSize: 2,
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
- await this.pool.release(container, image);
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
  }
@@ -56470,11 +56624,11 @@ class DockerIsol8 {
56470
56624
  }
56471
56625
  var import_dockerode, SANDBOX_WORKDIR = "/sandbox", MAX_OUTPUT_BYTES, PROXY_PORT = 8118, PROXY_STARTUP_TIMEOUT_MS = 5000, PROXY_POLL_INTERVAL_MS = 100;
56472
56626
  var init_docker = __esm(() => {
56627
+ import_dockerode = __toESM(require_docker(), 1);
56473
56628
  init_runtime();
56474
56629
  init_logger();
56475
56630
  init_audit();
56476
56631
  init_pool();
56477
- import_dockerode = __toESM(require_docker(), 1);
56478
56632
  MAX_OUTPUT_BYTES = 1024 * 1024;
56479
56633
  });
56480
56634
 
@@ -56483,7 +56637,7 @@ var package_default;
56483
56637
  var init_package = __esm(() => {
56484
56638
  package_default = {
56485
56639
  name: "isol8",
56486
- version: "0.9.0",
56640
+ version: "0.10.2-alpha.0",
56487
56641
  description: "Secure code execution engine for AI agents",
56488
56642
  author: "Illusion47586",
56489
56643
  license: "MIT",
@@ -59020,7 +59174,7 @@ onetime.callCount = (function_) => {
59020
59174
  };
59021
59175
  var onetime_default = onetime;
59022
59176
 
59023
- // node_modules/signal-exit/dist/mjs/signals.js
59177
+ // node_modules/restore-cursor/node_modules/signal-exit/dist/mjs/signals.js
59024
59178
  var signals = [];
59025
59179
  signals.push("SIGHUP", "SIGINT", "SIGTERM");
59026
59180
  if (process.platform !== "win32") {
@@ -59030,7 +59184,7 @@ if (process.platform === "linux") {
59030
59184
  signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT");
59031
59185
  }
59032
59186
 
59033
- // node_modules/signal-exit/dist/mjs/index.js
59187
+ // node_modules/restore-cursor/node_modules/signal-exit/dist/mjs/index.js
59034
59188
  var processOk = (process3) => !!process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
59035
59189
  var kExitEmitter = Symbol.for("signal-exit emitter");
59036
59190
  var global2 = globalThis;
@@ -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=6CABAE4F2846DBE164756E2164756E21
62664
+ //# debugId=AB0B8F37F6C568CA64756E2164756E21