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/README.md +43 -12
- package/dist/cli.js +272 -113
- 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/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-
|
|
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-
|
|
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 = "/
|
|
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
|
|
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 = "/
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
22501
|
+
var onerror = function onerror(err) {
|
|
22502
22502
|
callback.call(stream, err);
|
|
22503
22503
|
};
|
|
22504
|
-
var onclose = function
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
25542
|
+
let bufoff = copy && dstStart || 0;
|
|
25543
25543
|
let start = off[1];
|
|
25544
25544
|
if (srcStart === 0 && srcEnd === this.length) {
|
|
25545
|
-
if (!
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
35497
|
+
var isAbsolute = path.isAbsolute = function isAbsolute(path2) {
|
|
35498
35498
|
return /^(?:\/|\w+:)/.test(path2);
|
|
35499
35499
|
};
|
|
35500
|
-
var normalize = path.normalize = function
|
|
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
|
|
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 = "/
|
|
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 = "/
|
|
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 = "/
|
|
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
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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.
|
|
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=
|
|
62664
|
+
//# debugId=AB0B8F37F6C568CA64756E2164756E21
|