isol8 0.10.3 → 0.11.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 +16 -0
- package/dist/cli.js +465 -48
- package/dist/index.js +256 -17
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/engine/code-fetcher.d.ts +21 -0
- package/dist/src/engine/code-fetcher.d.ts.map +1 -0
- package/dist/src/engine/docker.d.ts +2 -0
- package/dist/src/engine/docker.d.ts.map +1 -1
- package/dist/src/engine/image-builder.d.ts +12 -2
- package/dist/src/engine/image-builder.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +44 -2
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/schema/isol8.config.schema.json +59 -0
package/dist/cli.js
CHANGED
|
@@ -54803,6 +54803,13 @@ function mergeConfig(defaults, overrides) {
|
|
|
54803
54803
|
seccomp: overrides.security?.seccomp ?? defaults.security.seccomp,
|
|
54804
54804
|
customProfilePath: overrides.security?.customProfilePath ?? defaults.security.customProfilePath
|
|
54805
54805
|
},
|
|
54806
|
+
remoteCode: {
|
|
54807
|
+
...defaults.remoteCode,
|
|
54808
|
+
...overrides.remoteCode,
|
|
54809
|
+
allowedSchemes: overrides.remoteCode?.allowedSchemes ?? defaults.remoteCode.allowedSchemes,
|
|
54810
|
+
allowedHosts: overrides.remoteCode?.allowedHosts ?? defaults.remoteCode.allowedHosts,
|
|
54811
|
+
blockedHosts: overrides.remoteCode?.blockedHosts ?? defaults.remoteCode.blockedHosts
|
|
54812
|
+
},
|
|
54806
54813
|
audit: {
|
|
54807
54814
|
...defaults.audit,
|
|
54808
54815
|
...overrides.audit
|
|
@@ -54834,6 +54841,28 @@ var init_config = __esm(() => {
|
|
|
54834
54841
|
security: {
|
|
54835
54842
|
seccomp: "strict"
|
|
54836
54843
|
},
|
|
54844
|
+
remoteCode: {
|
|
54845
|
+
enabled: false,
|
|
54846
|
+
allowedSchemes: ["https"],
|
|
54847
|
+
allowedHosts: [],
|
|
54848
|
+
blockedHosts: [
|
|
54849
|
+
"^localhost$",
|
|
54850
|
+
"^127(?:\\.[0-9]{1,3}){3}$",
|
|
54851
|
+
"^\\[::1\\]$",
|
|
54852
|
+
"^::1$",
|
|
54853
|
+
"^10(?:\\.[0-9]{1,3}){3}$",
|
|
54854
|
+
"^172\\.(?:1[6-9]|2[0-9]|3[0-1])(?:\\.[0-9]{1,3}){2}$",
|
|
54855
|
+
"^192\\.168(?:\\.[0-9]{1,3}){2}$",
|
|
54856
|
+
"^169\\.254(?:\\.[0-9]{1,3}){2}$",
|
|
54857
|
+
"^metadata\\.google\\.internal$",
|
|
54858
|
+
"^169\\.254\\.169\\.254$"
|
|
54859
|
+
],
|
|
54860
|
+
maxCodeSize: 10 * 1024 * 1024,
|
|
54861
|
+
fetchTimeoutMs: 30000,
|
|
54862
|
+
requireHash: false,
|
|
54863
|
+
enableCache: true,
|
|
54864
|
+
cacheTtl: 3600
|
|
54865
|
+
},
|
|
54837
54866
|
audit: {
|
|
54838
54867
|
enabled: false,
|
|
54839
54868
|
destination: "filesystem",
|
|
@@ -55174,6 +55203,180 @@ var init_audit = __esm(() => {
|
|
|
55174
55203
|
init_logger();
|
|
55175
55204
|
});
|
|
55176
55205
|
|
|
55206
|
+
// src/engine/code-fetcher.ts
|
|
55207
|
+
import { createHash } from "node:crypto";
|
|
55208
|
+
import { lookup as dnsLookup } from "node:dns/promises";
|
|
55209
|
+
import { isIP } from "node:net";
|
|
55210
|
+
function sha256Hex(input) {
|
|
55211
|
+
return createHash("sha256").update(input, "utf-8").digest("hex");
|
|
55212
|
+
}
|
|
55213
|
+
function normalizeScheme(url) {
|
|
55214
|
+
return url.protocol.replace(/:$/, "").toLowerCase();
|
|
55215
|
+
}
|
|
55216
|
+
function isBlockedByPattern(host, patterns) {
|
|
55217
|
+
return patterns.some((pattern) => new RegExp(pattern, "i").test(host));
|
|
55218
|
+
}
|
|
55219
|
+
function isAllowedByPattern(host, patterns) {
|
|
55220
|
+
if (patterns.length === 0) {
|
|
55221
|
+
return true;
|
|
55222
|
+
}
|
|
55223
|
+
return patterns.some((pattern) => new RegExp(pattern, "i").test(host));
|
|
55224
|
+
}
|
|
55225
|
+
function isPrivateIpv4(ip) {
|
|
55226
|
+
const parts = ip.split(IPV4_SEPARATOR).map((v) => Number.parseInt(v, 10));
|
|
55227
|
+
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p))) {
|
|
55228
|
+
return false;
|
|
55229
|
+
}
|
|
55230
|
+
const a = parts[0];
|
|
55231
|
+
const b = parts[1];
|
|
55232
|
+
if (a === 10 || a === 127 || a === 0) {
|
|
55233
|
+
return true;
|
|
55234
|
+
}
|
|
55235
|
+
if (a === 169 && b === 254) {
|
|
55236
|
+
return true;
|
|
55237
|
+
}
|
|
55238
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
55239
|
+
return true;
|
|
55240
|
+
}
|
|
55241
|
+
if (a === 192 && b === 168) {
|
|
55242
|
+
return true;
|
|
55243
|
+
}
|
|
55244
|
+
if (a === 100 && b >= 64 && b <= 127) {
|
|
55245
|
+
return true;
|
|
55246
|
+
}
|
|
55247
|
+
return false;
|
|
55248
|
+
}
|
|
55249
|
+
function isPrivateIpv6(ip) {
|
|
55250
|
+
const normalized = ip.toLowerCase();
|
|
55251
|
+
if (normalized === IPV6_LOOPBACK) {
|
|
55252
|
+
return true;
|
|
55253
|
+
}
|
|
55254
|
+
return normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe8") || normalized.startsWith("fe9") || normalized.startsWith("fea") || normalized.startsWith("feb");
|
|
55255
|
+
}
|
|
55256
|
+
function isPrivateIp(ip) {
|
|
55257
|
+
const family = isIP(ip);
|
|
55258
|
+
if (family === 4) {
|
|
55259
|
+
return isPrivateIpv4(ip);
|
|
55260
|
+
}
|
|
55261
|
+
if (family === 6) {
|
|
55262
|
+
return isPrivateIpv6(ip);
|
|
55263
|
+
}
|
|
55264
|
+
return false;
|
|
55265
|
+
}
|
|
55266
|
+
async function assertHostResolvesPublic(host, lookupFn) {
|
|
55267
|
+
if (isIP(host) && isPrivateIp(host)) {
|
|
55268
|
+
throw new Error(`Blocked code URL host: ${host}`);
|
|
55269
|
+
}
|
|
55270
|
+
try {
|
|
55271
|
+
const records = await lookupFn(host);
|
|
55272
|
+
for (const record of records) {
|
|
55273
|
+
if (isPrivateIp(record.address)) {
|
|
55274
|
+
throw new Error(`Blocked code URL host: ${host}`);
|
|
55275
|
+
}
|
|
55276
|
+
}
|
|
55277
|
+
} catch (err) {
|
|
55278
|
+
if (err instanceof Error && err.message.startsWith("Blocked code URL host:")) {
|
|
55279
|
+
throw err;
|
|
55280
|
+
}
|
|
55281
|
+
throw new Error(`Failed to resolve code URL host: ${host}`);
|
|
55282
|
+
}
|
|
55283
|
+
}
|
|
55284
|
+
function decodeUtf8(content) {
|
|
55285
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
55286
|
+
const text = decoder.decode(content);
|
|
55287
|
+
if (text.includes("\x00")) {
|
|
55288
|
+
throw new Error("Fetched code appears to be binary content");
|
|
55289
|
+
}
|
|
55290
|
+
return text;
|
|
55291
|
+
}
|
|
55292
|
+
async function fetchRemoteCode(request, policy, deps = {}) {
|
|
55293
|
+
if (!policy.enabled) {
|
|
55294
|
+
throw new Error("Remote code fetching is disabled. Set remoteCode.enabled=true to allow it.");
|
|
55295
|
+
}
|
|
55296
|
+
const fetchFn = deps.fetchFn ?? globalThis.fetch;
|
|
55297
|
+
const lookupFn = deps.lookupFn ?? (async (hostname) => {
|
|
55298
|
+
const records = await dnsLookup(hostname, { all: true, verbatim: true });
|
|
55299
|
+
return records;
|
|
55300
|
+
});
|
|
55301
|
+
if (!request.codeUrl) {
|
|
55302
|
+
throw new Error("codeUrl is required for remote code fetching");
|
|
55303
|
+
}
|
|
55304
|
+
const url = new URL(request.codeUrl);
|
|
55305
|
+
const scheme = normalizeScheme(url);
|
|
55306
|
+
if (scheme === "http" && !request.allowInsecureCodeUrl) {
|
|
55307
|
+
throw new Error("Insecure code URL blocked. Use allowInsecureCodeUrl=true to allow HTTP.");
|
|
55308
|
+
}
|
|
55309
|
+
if (!policy.allowedSchemes.map((s) => s.toLowerCase()).includes(scheme)) {
|
|
55310
|
+
throw new Error(`URL scheme not allowed: ${scheme}`);
|
|
55311
|
+
}
|
|
55312
|
+
const host = url.hostname.toLowerCase();
|
|
55313
|
+
if (!isAllowedByPattern(host, policy.allowedHosts) || isBlockedByPattern(host, policy.blockedHosts)) {
|
|
55314
|
+
throw new Error(`Blocked code URL host: ${host}`);
|
|
55315
|
+
}
|
|
55316
|
+
await assertHostResolvesPublic(host, lookupFn);
|
|
55317
|
+
if (policy.requireHash && !request.codeHash) {
|
|
55318
|
+
throw new Error("Hash verification required: provide codeHash for remote code execution.");
|
|
55319
|
+
}
|
|
55320
|
+
const controller = new AbortController;
|
|
55321
|
+
const timeout = setTimeout(() => controller.abort(), policy.fetchTimeoutMs);
|
|
55322
|
+
let response;
|
|
55323
|
+
try {
|
|
55324
|
+
response = await fetchFn(url.toString(), {
|
|
55325
|
+
method: "GET",
|
|
55326
|
+
redirect: "follow",
|
|
55327
|
+
signal: controller.signal
|
|
55328
|
+
});
|
|
55329
|
+
} catch (err) {
|
|
55330
|
+
throw new Error(err instanceof Error && err.name === "AbortError" ? `Remote code fetch timed out after ${policy.fetchTimeoutMs}ms` : `Failed to fetch remote code: ${err instanceof Error ? err.message : String(err)}`);
|
|
55331
|
+
} finally {
|
|
55332
|
+
clearTimeout(timeout);
|
|
55333
|
+
}
|
|
55334
|
+
if (!response.ok) {
|
|
55335
|
+
throw new Error(`Failed to fetch remote code: HTTP ${response.status}`);
|
|
55336
|
+
}
|
|
55337
|
+
const contentLengthHeader = response.headers.get("content-length");
|
|
55338
|
+
if (contentLengthHeader) {
|
|
55339
|
+
const parsedLength = Number.parseInt(contentLengthHeader, 10);
|
|
55340
|
+
if (!Number.isNaN(parsedLength) && parsedLength > policy.maxCodeSize) {
|
|
55341
|
+
throw new Error(`Remote code exceeds maxCodeSize (${policy.maxCodeSize} bytes): ${parsedLength} bytes`);
|
|
55342
|
+
}
|
|
55343
|
+
}
|
|
55344
|
+
if (!response.body) {
|
|
55345
|
+
throw new Error("Remote code response body is empty");
|
|
55346
|
+
}
|
|
55347
|
+
const reader = response.body.getReader();
|
|
55348
|
+
const chunks = [];
|
|
55349
|
+
let totalBytes = 0;
|
|
55350
|
+
while (true) {
|
|
55351
|
+
const { done, value } = await reader.read();
|
|
55352
|
+
if (done) {
|
|
55353
|
+
break;
|
|
55354
|
+
}
|
|
55355
|
+
if (!value) {
|
|
55356
|
+
continue;
|
|
55357
|
+
}
|
|
55358
|
+
totalBytes += value.byteLength;
|
|
55359
|
+
if (totalBytes > policy.maxCodeSize) {
|
|
55360
|
+
throw new Error(`Remote code exceeds maxCodeSize (${policy.maxCodeSize} bytes)`);
|
|
55361
|
+
}
|
|
55362
|
+
chunks.push(value);
|
|
55363
|
+
}
|
|
55364
|
+
const buffer = new Uint8Array(totalBytes);
|
|
55365
|
+
let offset = 0;
|
|
55366
|
+
for (const chunk of chunks) {
|
|
55367
|
+
buffer.set(chunk, offset);
|
|
55368
|
+
offset += chunk.byteLength;
|
|
55369
|
+
}
|
|
55370
|
+
const code = decodeUtf8(buffer);
|
|
55371
|
+
const hash = sha256Hex(code);
|
|
55372
|
+
if (request.codeHash && hash.toLowerCase() !== request.codeHash.toLowerCase()) {
|
|
55373
|
+
throw new Error("Remote code hash mismatch");
|
|
55374
|
+
}
|
|
55375
|
+
return { code, url: url.toString(), hash };
|
|
55376
|
+
}
|
|
55377
|
+
var IPV4_SEPARATOR = ".", IPV6_LOOPBACK = "::1";
|
|
55378
|
+
var init_code_fetcher = () => {};
|
|
55379
|
+
|
|
55177
55380
|
// src/engine/concurrency.ts
|
|
55178
55381
|
class Semaphore {
|
|
55179
55382
|
max;
|
|
@@ -55819,10 +56022,30 @@ class DockerIsol8 {
|
|
|
55819
56022
|
poolStrategy;
|
|
55820
56023
|
poolSize;
|
|
55821
56024
|
auditLogger;
|
|
56025
|
+
remoteCodePolicy;
|
|
55822
56026
|
container = null;
|
|
55823
56027
|
persistentRuntime = null;
|
|
55824
56028
|
pool = null;
|
|
55825
56029
|
imageCache = new Map;
|
|
56030
|
+
async resolveExecutionRequest(req) {
|
|
56031
|
+
const inlineCode = req.code?.trim();
|
|
56032
|
+
const codeUrl = req.codeUrl?.trim();
|
|
56033
|
+
if (inlineCode && codeUrl) {
|
|
56034
|
+
throw new Error("ExecutionRequest.code and ExecutionRequest.codeUrl are mutually exclusive.");
|
|
56035
|
+
}
|
|
56036
|
+
if (!(inlineCode || codeUrl)) {
|
|
56037
|
+
throw new Error("ExecutionRequest must include either code or codeUrl.");
|
|
56038
|
+
}
|
|
56039
|
+
if (inlineCode) {
|
|
56040
|
+
return { ...req, code: req.code };
|
|
56041
|
+
}
|
|
56042
|
+
const fetched = await fetchRemoteCode({
|
|
56043
|
+
codeUrl,
|
|
56044
|
+
codeHash: req.codeHash,
|
|
56045
|
+
allowInsecureCodeUrl: req.allowInsecureCodeUrl
|
|
56046
|
+
}, this.remoteCodePolicy);
|
|
56047
|
+
return { ...req, code: fetched.code };
|
|
56048
|
+
}
|
|
55826
56049
|
constructor(options = {}, maxConcurrent = 10) {
|
|
55827
56050
|
this.docker = options.docker ?? new import_dockerode.default;
|
|
55828
56051
|
this.mode = options.mode ?? "ephemeral";
|
|
@@ -55844,6 +56067,17 @@ class DockerIsol8 {
|
|
|
55844
56067
|
this.logNetwork = options.logNetwork ?? false;
|
|
55845
56068
|
this.poolStrategy = options.poolStrategy ?? "fast";
|
|
55846
56069
|
this.poolSize = options.poolSize ?? { clean: 1, dirty: 1 };
|
|
56070
|
+
this.remoteCodePolicy = options.remoteCode ?? {
|
|
56071
|
+
enabled: false,
|
|
56072
|
+
allowedSchemes: ["https"],
|
|
56073
|
+
allowedHosts: [],
|
|
56074
|
+
blockedHosts: [],
|
|
56075
|
+
maxCodeSize: 10 * 1024 * 1024,
|
|
56076
|
+
fetchTimeoutMs: 30000,
|
|
56077
|
+
requireHash: false,
|
|
56078
|
+
enableCache: true,
|
|
56079
|
+
cacheTtl: 3600
|
|
56080
|
+
};
|
|
55847
56081
|
if (options.audit) {
|
|
55848
56082
|
this.auditLogger = new AuditLogger(options.audit);
|
|
55849
56083
|
}
|
|
@@ -55872,7 +56106,8 @@ class DockerIsol8 {
|
|
|
55872
56106
|
await this.semaphore.acquire();
|
|
55873
56107
|
const startTime = Date.now();
|
|
55874
56108
|
try {
|
|
55875
|
-
const
|
|
56109
|
+
const request = await this.resolveExecutionRequest(req);
|
|
56110
|
+
const result = this.mode === "persistent" ? await this.executePersistent(request, startTime) : await this.executeEphemeral(request, startTime);
|
|
55876
56111
|
return result;
|
|
55877
56112
|
} finally {
|
|
55878
56113
|
this.semaphore.release();
|
|
@@ -56026,8 +56261,9 @@ class DockerIsol8 {
|
|
|
56026
56261
|
async* executeStream(req) {
|
|
56027
56262
|
await this.semaphore.acquire();
|
|
56028
56263
|
try {
|
|
56029
|
-
const
|
|
56030
|
-
const
|
|
56264
|
+
const request = await this.resolveExecutionRequest(req);
|
|
56265
|
+
const adapter = this.getAdapter(request.runtime);
|
|
56266
|
+
const timeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;
|
|
56031
56267
|
const image = await this.resolveImage(adapter);
|
|
56032
56268
|
const container = await this.docker.createContainer({
|
|
56033
56269
|
Image: image,
|
|
@@ -56044,23 +56280,23 @@ class DockerIsol8 {
|
|
|
56044
56280
|
await startProxy(container, this.networkFilter);
|
|
56045
56281
|
await setupIptables(container);
|
|
56046
56282
|
}
|
|
56047
|
-
const ext =
|
|
56283
|
+
const ext = request.fileExtension ?? adapter.getFileExtension();
|
|
56048
56284
|
const filePath = `${SANDBOX_WORKDIR}/main${ext}`;
|
|
56049
|
-
await writeFileViaExec(container, filePath,
|
|
56050
|
-
if (
|
|
56051
|
-
await installPackages(container,
|
|
56285
|
+
await writeFileViaExec(container, filePath, request.code);
|
|
56286
|
+
if (request.installPackages?.length) {
|
|
56287
|
+
await installPackages(container, request.runtime, request.installPackages);
|
|
56052
56288
|
}
|
|
56053
|
-
if (
|
|
56054
|
-
for (const [fPath, fContent] of Object.entries(
|
|
56289
|
+
if (request.files) {
|
|
56290
|
+
for (const [fPath, fContent] of Object.entries(request.files)) {
|
|
56055
56291
|
await writeFileViaExec(container, fPath, fContent);
|
|
56056
56292
|
}
|
|
56057
56293
|
}
|
|
56058
|
-
const rawCmd = adapter.getCommand(
|
|
56294
|
+
const rawCmd = adapter.getCommand(request.code, filePath);
|
|
56059
56295
|
const timeoutSec = Math.ceil(timeoutMs / 1000);
|
|
56060
56296
|
let cmd;
|
|
56061
|
-
if (
|
|
56297
|
+
if (request.stdin) {
|
|
56062
56298
|
const stdinPath = `${SANDBOX_WORKDIR}/_stdin`;
|
|
56063
|
-
await writeFileViaExec(container, stdinPath,
|
|
56299
|
+
await writeFileViaExec(container, stdinPath, request.stdin);
|
|
56064
56300
|
const cmdStr = rawCmd.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
|
|
56065
56301
|
cmd = wrapWithTimeout(["sh", "-c", `cat ${stdinPath} | ${cmdStr}`], timeoutSec);
|
|
56066
56302
|
} else {
|
|
@@ -56068,7 +56304,7 @@ class DockerIsol8 {
|
|
|
56068
56304
|
}
|
|
56069
56305
|
const exec = await container.exec({
|
|
56070
56306
|
Cmd: cmd,
|
|
56071
|
-
Env: this.buildEnv(
|
|
56307
|
+
Env: this.buildEnv(request.env),
|
|
56072
56308
|
AttachStdout: true,
|
|
56073
56309
|
AttachStderr: true,
|
|
56074
56310
|
WorkingDir: SANDBOX_WORKDIR,
|
|
@@ -56627,6 +56863,7 @@ var init_docker = __esm(() => {
|
|
|
56627
56863
|
init_runtime();
|
|
56628
56864
|
init_logger();
|
|
56629
56865
|
init_audit();
|
|
56866
|
+
init_code_fetcher();
|
|
56630
56867
|
init_pool();
|
|
56631
56868
|
import_dockerode = __toESM(require_docker(), 1);
|
|
56632
56869
|
MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
@@ -56637,7 +56874,7 @@ var package_default;
|
|
|
56637
56874
|
var init_package = __esm(() => {
|
|
56638
56875
|
package_default = {
|
|
56639
56876
|
name: "isol8",
|
|
56640
|
-
version: "0.10.
|
|
56877
|
+
version: "0.10.3",
|
|
56641
56878
|
description: "Secure code execution engine for AI agents",
|
|
56642
56879
|
author: "Illusion47586",
|
|
56643
56880
|
license: "MIT",
|
|
@@ -58388,7 +58625,7 @@ async function createServer(options) {
|
|
|
58388
58625
|
app.post("/execute", async (c) => {
|
|
58389
58626
|
const body = await c.req.json();
|
|
58390
58627
|
logger.debug(`[Server] POST /execute runtime=${body.request.runtime} sessionId=${body.sessionId ?? "ephemeral"}`);
|
|
58391
|
-
logger.debug(`[Server] Code
|
|
58628
|
+
logger.debug(`[Server] Code source: ${body.request.codeUrl ? `url=${body.request.codeUrl}` : `inline (${body.request.code?.length ?? 0} chars)`}`);
|
|
58392
58629
|
const engineOptions = {
|
|
58393
58630
|
network: config.defaults.network,
|
|
58394
58631
|
memoryLimit: config.defaults.memoryLimit,
|
|
@@ -58396,6 +58633,7 @@ async function createServer(options) {
|
|
|
58396
58633
|
timeoutMs: config.defaults.timeoutMs,
|
|
58397
58634
|
sandboxSize: config.defaults.sandboxSize,
|
|
58398
58635
|
tmpSize: config.defaults.tmpSize,
|
|
58636
|
+
remoteCode: config.remoteCode,
|
|
58399
58637
|
...body.options,
|
|
58400
58638
|
mode: body.sessionId ? "persistent" : "ephemeral",
|
|
58401
58639
|
audit: config.audit
|
|
@@ -58449,7 +58687,7 @@ async function createServer(options) {
|
|
|
58449
58687
|
app.post("/execute/stream", async (c) => {
|
|
58450
58688
|
const body = await c.req.json();
|
|
58451
58689
|
logger.debug(`[Server] POST /execute/stream runtime=${body.request.runtime}`);
|
|
58452
|
-
logger.debug(`[Server] Code
|
|
58690
|
+
logger.debug(`[Server] Code source: ${body.request.codeUrl ? `url=${body.request.codeUrl}` : `inline (${body.request.code?.length ?? 0} chars)`}`);
|
|
58453
58691
|
const engineOptions = {
|
|
58454
58692
|
network: config.defaults.network,
|
|
58455
58693
|
memoryLimit: config.defaults.memoryLimit,
|
|
@@ -58457,6 +58695,7 @@ async function createServer(options) {
|
|
|
58457
58695
|
timeoutMs: config.defaults.timeoutMs,
|
|
58458
58696
|
sandboxSize: config.defaults.sandboxSize,
|
|
58459
58697
|
tmpSize: config.defaults.tmpSize,
|
|
58698
|
+
remoteCode: config.remoteCode,
|
|
58460
58699
|
...body.options,
|
|
58461
58700
|
mode: "ephemeral"
|
|
58462
58701
|
};
|
|
@@ -58581,13 +58820,13 @@ import {
|
|
|
58581
58820
|
chmodSync,
|
|
58582
58821
|
existsSync as existsSync5,
|
|
58583
58822
|
mkdirSync as mkdirSync2,
|
|
58584
|
-
readFileSync as
|
|
58823
|
+
readFileSync as readFileSync4,
|
|
58585
58824
|
renameSync,
|
|
58586
58825
|
unlinkSync as unlinkSync2,
|
|
58587
58826
|
writeFileSync
|
|
58588
58827
|
} from "node:fs";
|
|
58589
58828
|
import { arch, homedir as homedir2, platform } from "node:os";
|
|
58590
|
-
import { join as
|
|
58829
|
+
import { join as join4, resolve as resolve2 } from "node:path";
|
|
58591
58830
|
|
|
58592
58831
|
// node_modules/commander/esm.mjs
|
|
58593
58832
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -61981,7 +62220,10 @@ init_docker();
|
|
|
61981
62220
|
|
|
61982
62221
|
// src/engine/image-builder.ts
|
|
61983
62222
|
init_runtime();
|
|
61984
|
-
|
|
62223
|
+
init_logger();
|
|
62224
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
62225
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
62226
|
+
import { join as join3 } from "node:path";
|
|
61985
62227
|
function resolveDockerDir() {
|
|
61986
62228
|
const fromBundled = new URL("./docker", import.meta.url).pathname;
|
|
61987
62229
|
if (existsSync4(fromBundled)) {
|
|
@@ -61990,16 +62232,82 @@ function resolveDockerDir() {
|
|
|
61990
62232
|
return new URL("../../docker", import.meta.url).pathname;
|
|
61991
62233
|
}
|
|
61992
62234
|
var DOCKERFILE_DIR = resolveDockerDir();
|
|
61993
|
-
|
|
62235
|
+
var LABELS = {
|
|
62236
|
+
dockerHash: "org.isol8.build.hash",
|
|
62237
|
+
depsHash: "org.isol8.deps.hash"
|
|
62238
|
+
};
|
|
62239
|
+
var DOCKER_BUILD_FILES = ["Dockerfile", "proxy.sh", "proxy-handler.sh"];
|
|
62240
|
+
function computeDockerDirHash() {
|
|
62241
|
+
const hash = createHash2("sha256");
|
|
62242
|
+
const files = [...DOCKER_BUILD_FILES].sort();
|
|
62243
|
+
for (const file of files) {
|
|
62244
|
+
const filePath = join3(DOCKERFILE_DIR, file);
|
|
62245
|
+
if (existsSync4(filePath)) {
|
|
62246
|
+
const content = readFileSync3(filePath);
|
|
62247
|
+
hash.update(file);
|
|
62248
|
+
hash.update(content);
|
|
62249
|
+
}
|
|
62250
|
+
}
|
|
62251
|
+
return hash.digest("hex");
|
|
62252
|
+
}
|
|
62253
|
+
function computeDepsHash(runtime, packages) {
|
|
62254
|
+
const hash = createHash2("sha256");
|
|
62255
|
+
hash.update(runtime);
|
|
62256
|
+
for (const pkg of [...packages].sort()) {
|
|
62257
|
+
hash.update(pkg);
|
|
62258
|
+
}
|
|
62259
|
+
return hash.digest("hex");
|
|
62260
|
+
}
|
|
62261
|
+
async function getImageLabels(docker, imageName) {
|
|
62262
|
+
try {
|
|
62263
|
+
const image = docker.getImage(imageName);
|
|
62264
|
+
const inspect = await image.inspect();
|
|
62265
|
+
return inspect.Config?.Labels ?? {};
|
|
62266
|
+
} catch {
|
|
62267
|
+
return null;
|
|
62268
|
+
}
|
|
62269
|
+
}
|
|
62270
|
+
async function removeImage(docker, imageId) {
|
|
62271
|
+
try {
|
|
62272
|
+
const image = docker.getImage(imageId);
|
|
62273
|
+
await image.remove();
|
|
62274
|
+
logger.debug(`[ImageBuilder] Removed old image: ${imageId.slice(0, 12)}`);
|
|
62275
|
+
} catch (err) {
|
|
62276
|
+
logger.debug(`[ImageBuilder] Could not remove image ${imageId.slice(0, 12)}: ${err}`);
|
|
62277
|
+
}
|
|
62278
|
+
}
|
|
62279
|
+
async function buildBaseImages(docker, onProgress, force = false) {
|
|
61994
62280
|
const runtimes = RuntimeRegistry.list();
|
|
62281
|
+
const dockerHash = computeDockerDirHash();
|
|
62282
|
+
logger.debug(`[ImageBuilder] Docker directory hash: ${dockerHash.slice(0, 16)}...`);
|
|
61995
62283
|
for (const adapter of runtimes) {
|
|
61996
62284
|
const target = adapter.name;
|
|
62285
|
+
const imageName = adapter.image;
|
|
62286
|
+
if (!force) {
|
|
62287
|
+
const labels = await getImageLabels(docker, imageName);
|
|
62288
|
+
if (labels && labels[LABELS.dockerHash] === dockerHash) {
|
|
62289
|
+
logger.debug(`[ImageBuilder] Base image ${target} is up to date, skipping build`);
|
|
62290
|
+
onProgress?.({ runtime: target, status: "done", message: "Up to date" });
|
|
62291
|
+
continue;
|
|
62292
|
+
}
|
|
62293
|
+
}
|
|
62294
|
+
let oldImageId = null;
|
|
62295
|
+
try {
|
|
62296
|
+
const oldImage = await docker.getImage(imageName).inspect();
|
|
62297
|
+
oldImageId = oldImage.Id;
|
|
62298
|
+
logger.debug(`[ImageBuilder] Existing image ${target} ID: ${oldImageId.slice(0, 12)}`);
|
|
62299
|
+
} catch {
|
|
62300
|
+
logger.debug(`[ImageBuilder] No existing image for ${target}`);
|
|
62301
|
+
}
|
|
61997
62302
|
onProgress?.({ runtime: target, status: "building" });
|
|
61998
62303
|
try {
|
|
61999
|
-
const stream = await docker.buildImage({ context: DOCKERFILE_DIR, src:
|
|
62000
|
-
t:
|
|
62304
|
+
const stream = await docker.buildImage({ context: DOCKERFILE_DIR, src: DOCKER_BUILD_FILES }, {
|
|
62305
|
+
t: imageName,
|
|
62001
62306
|
target,
|
|
62002
|
-
dockerfile: "Dockerfile"
|
|
62307
|
+
dockerfile: "Dockerfile",
|
|
62308
|
+
labels: {
|
|
62309
|
+
[LABELS.dockerHash]: dockerHash
|
|
62310
|
+
}
|
|
62003
62311
|
});
|
|
62004
62312
|
await new Promise((resolve2, reject) => {
|
|
62005
62313
|
docker.modem.followProgress(stream, (err) => {
|
|
@@ -62010,6 +62318,9 @@ async function buildBaseImages(docker, onProgress) {
|
|
|
62010
62318
|
}
|
|
62011
62319
|
});
|
|
62012
62320
|
});
|
|
62321
|
+
if (oldImageId) {
|
|
62322
|
+
await removeImage(docker, oldImageId);
|
|
62323
|
+
}
|
|
62013
62324
|
onProgress?.({ runtime: target, status: "done" });
|
|
62014
62325
|
} catch (err) {
|
|
62015
62326
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -62018,26 +62329,44 @@ async function buildBaseImages(docker, onProgress) {
|
|
|
62018
62329
|
}
|
|
62019
62330
|
}
|
|
62020
62331
|
}
|
|
62021
|
-
async function buildCustomImages(docker, config, onProgress) {
|
|
62332
|
+
async function buildCustomImages(docker, config, onProgress, force = false) {
|
|
62022
62333
|
const deps = config.dependencies;
|
|
62023
62334
|
if (deps.python?.length) {
|
|
62024
|
-
await buildCustomImage(docker, "python", deps.python, onProgress);
|
|
62335
|
+
await buildCustomImage(docker, "python", deps.python, onProgress, force);
|
|
62025
62336
|
}
|
|
62026
62337
|
if (deps.node?.length) {
|
|
62027
|
-
await buildCustomImage(docker, "node", deps.node, onProgress);
|
|
62338
|
+
await buildCustomImage(docker, "node", deps.node, onProgress, force);
|
|
62028
62339
|
}
|
|
62029
62340
|
if (deps.bun?.length) {
|
|
62030
|
-
await buildCustomImage(docker, "bun", deps.bun, onProgress);
|
|
62341
|
+
await buildCustomImage(docker, "bun", deps.bun, onProgress, force);
|
|
62031
62342
|
}
|
|
62032
62343
|
if (deps.deno?.length) {
|
|
62033
|
-
await buildCustomImage(docker, "deno", deps.deno, onProgress);
|
|
62344
|
+
await buildCustomImage(docker, "deno", deps.deno, onProgress, force);
|
|
62034
62345
|
}
|
|
62035
62346
|
if (deps.bash?.length) {
|
|
62036
|
-
await buildCustomImage(docker, "bash", deps.bash, onProgress);
|
|
62347
|
+
await buildCustomImage(docker, "bash", deps.bash, onProgress, force);
|
|
62037
62348
|
}
|
|
62038
62349
|
}
|
|
62039
|
-
async function buildCustomImage(docker, runtime, packages, onProgress) {
|
|
62350
|
+
async function buildCustomImage(docker, runtime, packages, onProgress, force = false) {
|
|
62040
62351
|
const tag = `isol8:${runtime}-custom`;
|
|
62352
|
+
const depsHash = computeDepsHash(runtime, packages);
|
|
62353
|
+
logger.debug(`[ImageBuilder] ${runtime} custom deps hash: ${depsHash.slice(0, 16)}...`);
|
|
62354
|
+
if (!force) {
|
|
62355
|
+
const labels = await getImageLabels(docker, tag);
|
|
62356
|
+
if (labels && labels[LABELS.depsHash] === depsHash) {
|
|
62357
|
+
logger.debug(`[ImageBuilder] Custom image ${runtime} is up to date, skipping build`);
|
|
62358
|
+
onProgress?.({ runtime, status: "done", message: "Up to date" });
|
|
62359
|
+
return;
|
|
62360
|
+
}
|
|
62361
|
+
}
|
|
62362
|
+
let oldImageId = null;
|
|
62363
|
+
try {
|
|
62364
|
+
const oldImage = await docker.getImage(tag).inspect();
|
|
62365
|
+
oldImageId = oldImage.Id;
|
|
62366
|
+
logger.debug(`[ImageBuilder] Existing custom image ${runtime} ID: ${oldImageId.slice(0, 12)}`);
|
|
62367
|
+
} catch {
|
|
62368
|
+
logger.debug(`[ImageBuilder] No existing custom image for ${runtime}`);
|
|
62369
|
+
}
|
|
62041
62370
|
onProgress?.({ runtime, status: "building", message: `Custom: ${packages.join(", ")}` });
|
|
62042
62371
|
let installCmd;
|
|
62043
62372
|
switch (runtime) {
|
|
@@ -62069,7 +62398,10 @@ ${installCmd}
|
|
|
62069
62398
|
const tarBuffer = createTarBuffer2("Dockerfile", dockerfileContent);
|
|
62070
62399
|
const stream = await docker.buildImage(Readable.from(tarBuffer), {
|
|
62071
62400
|
t: tag,
|
|
62072
|
-
dockerfile: "Dockerfile"
|
|
62401
|
+
dockerfile: "Dockerfile",
|
|
62402
|
+
labels: {
|
|
62403
|
+
[LABELS.depsHash]: depsHash
|
|
62404
|
+
}
|
|
62073
62405
|
});
|
|
62074
62406
|
await new Promise((resolve2, reject) => {
|
|
62075
62407
|
docker.modem.followProgress(stream, (err) => {
|
|
@@ -62080,6 +62412,9 @@ ${installCmd}
|
|
|
62080
62412
|
}
|
|
62081
62413
|
});
|
|
62082
62414
|
});
|
|
62415
|
+
if (oldImageId) {
|
|
62416
|
+
await removeImage(docker, oldImageId);
|
|
62417
|
+
}
|
|
62083
62418
|
onProgress?.({ runtime, status: "done" });
|
|
62084
62419
|
}
|
|
62085
62420
|
|
|
@@ -62097,7 +62432,7 @@ program2.name("isol8").description("Secure code execution engine").version(VERSI
|
|
|
62097
62432
|
logger.debug(`[CLI] Version: ${VERSION}`);
|
|
62098
62433
|
logger.debug(`[CLI] Platform: ${platform()} ${arch()}`);
|
|
62099
62434
|
});
|
|
62100
|
-
program2.command("setup").description("Check Docker and build isol8 images").option("--python <packages>", "Additional Python packages (comma-separated)").option("--node <packages>", "Additional Node.js packages (comma-separated)").option("--bun <packages>", "Additional Bun packages (comma-separated)").option("--deno <packages>", "Additional Deno packages (comma-separated)").option("--bash <packages>", "Additional Bash packages (comma-separated)").action(async (opts) => {
|
|
62435
|
+
program2.command("setup").description("Check Docker and build isol8 images").option("--python <packages>", "Additional Python packages (comma-separated)").option("--node <packages>", "Additional Node.js packages (comma-separated)").option("--bun <packages>", "Additional Bun packages (comma-separated)").option("--deno <packages>", "Additional Deno packages (comma-separated)").option("--bash <packages>", "Additional Bash packages (comma-separated)").option("--force", "Force rebuild even if images are up to date").action(async (opts) => {
|
|
62101
62436
|
const docker = new import_dockerode2.default;
|
|
62102
62437
|
logger.debug("[Setup] Connecting to Docker daemon");
|
|
62103
62438
|
const spinner = ora("Checking Docker...").start();
|
|
@@ -62112,7 +62447,7 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
62112
62447
|
process.exit(1);
|
|
62113
62448
|
}
|
|
62114
62449
|
spinner.start("Building isol8 images...");
|
|
62115
|
-
logger.debug(
|
|
62450
|
+
logger.debug(`[Setup] Building base images (force=${opts.force ?? false})`);
|
|
62116
62451
|
await buildBaseImages(docker, (progress) => {
|
|
62117
62452
|
const status = progress.status === "error" ? "[ERR]" : progress.status === "done" ? "[OK]" : "[..]";
|
|
62118
62453
|
if (progress.status === "building") {
|
|
@@ -62128,7 +62463,7 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
62128
62463
|
spinner.start();
|
|
62129
62464
|
}
|
|
62130
62465
|
}
|
|
62131
|
-
});
|
|
62466
|
+
}, opts.force ?? false);
|
|
62132
62467
|
if (spinner.isSpinning) {
|
|
62133
62468
|
spinner.stop();
|
|
62134
62469
|
}
|
|
@@ -62176,7 +62511,7 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
62176
62511
|
spinner.start();
|
|
62177
62512
|
}
|
|
62178
62513
|
}
|
|
62179
|
-
});
|
|
62514
|
+
}, opts.force ?? false);
|
|
62180
62515
|
if (spinner.isSpinning) {
|
|
62181
62516
|
spinner.stop();
|
|
62182
62517
|
}
|
|
@@ -62184,12 +62519,25 @@ program2.command("setup").description("Check Docker and build isol8 images").opt
|
|
|
62184
62519
|
console.log(`
|
|
62185
62520
|
[DONE] Setup complete!`);
|
|
62186
62521
|
});
|
|
62187
|
-
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) => {
|
|
62188
|
-
const {
|
|
62522
|
+
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("--url <url>", "Fetch code from URL").option("--github <path>", "GitHub shorthand: owner/repo/ref/path/to/file").option("--gist <path>", "Gist shorthand: gistId/file.ext").option("--hash <sha256>", "Expected SHA-256 hash of fetched code").option("--allow-insecure-code-url", "Allow insecure HTTP code URLs").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) => {
|
|
62523
|
+
const {
|
|
62524
|
+
code,
|
|
62525
|
+
codeUrl,
|
|
62526
|
+
codeHash,
|
|
62527
|
+
allowInsecureCodeUrl,
|
|
62528
|
+
runtime,
|
|
62529
|
+
engineOptions,
|
|
62530
|
+
engine,
|
|
62531
|
+
stdinData,
|
|
62532
|
+
fileExtension
|
|
62533
|
+
} = await resolveRunInput(file, opts);
|
|
62189
62534
|
logger.debug(`[Run] Runtime: ${runtime}, mode: ${engineOptions.mode}`);
|
|
62190
62535
|
logger.debug(`[Run] Network: ${engineOptions.network}, timeout: ${engineOptions.timeoutMs}ms`);
|
|
62191
62536
|
logger.debug(`[Run] Memory: ${engineOptions.memoryLimit}, CPU: ${engineOptions.cpuLimit}`);
|
|
62192
|
-
logger.debug(`[Run] Code
|
|
62537
|
+
logger.debug(`[Run] Code source: ${codeUrl ? `url=${codeUrl}` : "inline/file/stdin"}`);
|
|
62538
|
+
if (code) {
|
|
62539
|
+
logger.debug(`[Run] Code length: ${code.length} chars`);
|
|
62540
|
+
}
|
|
62193
62541
|
if (stdinData) {
|
|
62194
62542
|
logger.debug(`[Run] Stdin data provided (${stdinData.length} chars)`);
|
|
62195
62543
|
}
|
|
@@ -62215,9 +62563,12 @@ program2.command("run").description("Execute code in isol8").argument("[file]",
|
|
|
62215
62563
|
logger.debug("[Run] Engine started");
|
|
62216
62564
|
spinner.text = "Running code...";
|
|
62217
62565
|
const req = {
|
|
62218
|
-
code,
|
|
62219
62566
|
runtime,
|
|
62220
62567
|
timeoutMs: engineOptions.timeoutMs,
|
|
62568
|
+
...code ? { code } : {},
|
|
62569
|
+
...codeUrl ? { codeUrl } : {},
|
|
62570
|
+
...codeHash ? { codeHash } : {},
|
|
62571
|
+
...allowInsecureCodeUrl ? { allowInsecureCodeUrl } : {},
|
|
62221
62572
|
...stdinData ? { stdin: stdinData } : {},
|
|
62222
62573
|
...opts.install.length > 0 ? { installPackages: opts.install } : {},
|
|
62223
62574
|
fileExtension
|
|
@@ -62377,7 +62728,7 @@ async function downloadServerBinary(binaryPath) {
|
|
|
62377
62728
|
}
|
|
62378
62729
|
process.exit(1);
|
|
62379
62730
|
}
|
|
62380
|
-
const binDir =
|
|
62731
|
+
const binDir = join4(homedir2(), ".isol8", "bin");
|
|
62381
62732
|
mkdirSync2(binDir, { recursive: true });
|
|
62382
62733
|
const tmpPath = `${binaryPath}.tmp`;
|
|
62383
62734
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
@@ -62409,8 +62760,8 @@ async function promptYesNo(question) {
|
|
|
62409
62760
|
return normalized === "" || normalized === "y" || normalized === "yes";
|
|
62410
62761
|
}
|
|
62411
62762
|
async function ensureServerBinary(forceUpdate) {
|
|
62412
|
-
const binDir =
|
|
62413
|
-
const binaryPath =
|
|
62763
|
+
const binDir = join4(homedir2(), ".isol8", "bin");
|
|
62764
|
+
const binaryPath = join4(binDir, "isol8-server");
|
|
62414
62765
|
logger.debug(`[Serve] Binary path: ${binaryPath}, forceUpdate: ${forceUpdate}`);
|
|
62415
62766
|
if (forceUpdate) {
|
|
62416
62767
|
logger.debug("[Serve] Force update requested");
|
|
@@ -62440,8 +62791,8 @@ async function ensureServerBinary(forceUpdate) {
|
|
|
62440
62791
|
program2.command("config").description("Show the resolved isol8 configuration").option("--json", "Output as raw JSON").action((opts) => {
|
|
62441
62792
|
const config = loadConfig();
|
|
62442
62793
|
const searchPaths = [
|
|
62443
|
-
|
|
62444
|
-
|
|
62794
|
+
join4(resolve2(process.cwd()), "isol8.config.json"),
|
|
62795
|
+
join4(homedir2(), ".isol8", "config.json")
|
|
62445
62796
|
];
|
|
62446
62797
|
const loadedFrom = searchPaths.find((p) => existsSync5(p));
|
|
62447
62798
|
logger.debug(`[Config] Config source: ${loadedFrom ?? "defaults"}`);
|
|
@@ -62476,6 +62827,13 @@ Isol8 Configuration
|
|
|
62476
62827
|
} else {
|
|
62477
62828
|
console.log(" Whitelist: (none)");
|
|
62478
62829
|
}
|
|
62830
|
+
console.log("");
|
|
62831
|
+
console.log(" ── Remote Code ──");
|
|
62832
|
+
console.log(` Enabled: ${config.remoteCode.enabled ? "yes" : "no"}`);
|
|
62833
|
+
console.log(` Schemes: ${config.remoteCode.allowedSchemes.join(", ")}`);
|
|
62834
|
+
console.log(` Max code size: ${config.remoteCode.maxCodeSize} bytes`);
|
|
62835
|
+
console.log(` Fetch timeout: ${config.remoteCode.fetchTimeoutMs}ms`);
|
|
62836
|
+
console.log(` Require hash: ${config.remoteCode.requireHash ? "yes" : "no"}`);
|
|
62479
62837
|
if (config.network.blacklist.length > 0) {
|
|
62480
62838
|
console.log(` Blacklist: ${config.network.blacklist.join(", ")}`);
|
|
62481
62839
|
} else {
|
|
@@ -62570,8 +62928,25 @@ async function resolveRunInput(file, opts) {
|
|
|
62570
62928
|
const config = loadConfig();
|
|
62571
62929
|
logger.debug("[Run] Config loaded");
|
|
62572
62930
|
let code;
|
|
62931
|
+
let codeUrl;
|
|
62932
|
+
let codeHash;
|
|
62933
|
+
let allowInsecureCodeUrl = false;
|
|
62573
62934
|
let runtime;
|
|
62574
|
-
if (opts.
|
|
62935
|
+
if (opts.url || opts.github || opts.gist) {
|
|
62936
|
+
if (file || opts.eval) {
|
|
62937
|
+
console.error("[ERR] --url/--github/--gist cannot be used with file input or --eval.");
|
|
62938
|
+
process.exit(1);
|
|
62939
|
+
}
|
|
62940
|
+
codeUrl = resolveCodeUrl(opts);
|
|
62941
|
+
codeHash = opts.hash ?? undefined;
|
|
62942
|
+
allowInsecureCodeUrl = opts.allowInsecureCodeUrl ?? false;
|
|
62943
|
+
runtime = opts.runtime ?? detectRuntimeFromPath(new URL(codeUrl).pathname);
|
|
62944
|
+
if (!runtime) {
|
|
62945
|
+
console.error("[ERR] Cannot detect runtime from URL path. Use --runtime to specify.");
|
|
62946
|
+
process.exit(1);
|
|
62947
|
+
}
|
|
62948
|
+
logger.debug(`[Run] Remote code URL: ${codeUrl}`);
|
|
62949
|
+
} else if (opts.eval) {
|
|
62575
62950
|
code = opts.eval;
|
|
62576
62951
|
runtime = opts.runtime ?? "python";
|
|
62577
62952
|
logger.debug(`[Run] Inline eval, runtime: ${runtime}`);
|
|
@@ -62582,7 +62957,7 @@ async function resolveRunInput(file, opts) {
|
|
|
62582
62957
|
console.error(`[ERR] File not found: ${file}`);
|
|
62583
62958
|
process.exit(1);
|
|
62584
62959
|
}
|
|
62585
|
-
code =
|
|
62960
|
+
code = readFileSync4(filePath, "utf-8");
|
|
62586
62961
|
if (opts.runtime) {
|
|
62587
62962
|
runtime = opts.runtime;
|
|
62588
62963
|
logger.debug(`[Run] Runtime specified: ${runtime}`);
|
|
@@ -62622,6 +62997,7 @@ async function resolveRunInput(file, opts) {
|
|
|
62622
62997
|
debug: opts.debug ?? config.debug,
|
|
62623
62998
|
persist: opts.persist ?? false,
|
|
62624
62999
|
...opts.logNetwork ? { logNetwork: true } : {},
|
|
63000
|
+
remoteCode: config.remoteCode,
|
|
62625
63001
|
poolStrategy: opts.poolStrategy === "secure" ? "secure" : "fast",
|
|
62626
63002
|
poolSize: opts.poolSize ? opts.poolSize.includes(",") ? {
|
|
62627
63003
|
clean: Number.parseInt(opts.poolSize.split(",")[0], 10),
|
|
@@ -62660,7 +63036,48 @@ async function resolveRunInput(file, opts) {
|
|
|
62660
63036
|
logger.debug("[Run] Using local Docker engine");
|
|
62661
63037
|
engine = new DockerIsol8(engineOptions, config.maxConcurrent);
|
|
62662
63038
|
}
|
|
62663
|
-
return {
|
|
63039
|
+
return {
|
|
63040
|
+
code,
|
|
63041
|
+
codeUrl,
|
|
63042
|
+
codeHash,
|
|
63043
|
+
allowInsecureCodeUrl,
|
|
63044
|
+
runtime,
|
|
63045
|
+
engineOptions,
|
|
63046
|
+
engine,
|
|
63047
|
+
stdinData,
|
|
63048
|
+
fileExtension
|
|
63049
|
+
};
|
|
63050
|
+
}
|
|
63051
|
+
function resolveCodeUrl(opts) {
|
|
63052
|
+
if (typeof opts.url === "string") {
|
|
63053
|
+
return opts.url;
|
|
63054
|
+
}
|
|
63055
|
+
if (typeof opts.github === "string") {
|
|
63056
|
+
const parts = opts.github.split("/");
|
|
63057
|
+
if (parts.length < 4) {
|
|
63058
|
+
console.error("[ERR] --github format must be owner/repo/ref/path/to/file");
|
|
63059
|
+
process.exit(1);
|
|
63060
|
+
}
|
|
63061
|
+
const [owner, repo, ref, ...pathParts] = parts;
|
|
63062
|
+
return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${pathParts.join("/")}`;
|
|
63063
|
+
}
|
|
63064
|
+
if (typeof opts.gist === "string") {
|
|
63065
|
+
const [gistId, ...fileParts] = opts.gist.split("/");
|
|
63066
|
+
if (!gistId || fileParts.length === 0) {
|
|
63067
|
+
console.error("[ERR] --gist format must be gistId/file.ext");
|
|
63068
|
+
process.exit(1);
|
|
63069
|
+
}
|
|
63070
|
+
return `https://gist.githubusercontent.com/${gistId}/raw/${fileParts.join("/")}`;
|
|
63071
|
+
}
|
|
63072
|
+
console.error("[ERR] Missing code URL source.");
|
|
63073
|
+
process.exit(1);
|
|
63074
|
+
}
|
|
63075
|
+
function detectRuntimeFromPath(pathValue) {
|
|
63076
|
+
try {
|
|
63077
|
+
return RuntimeRegistry.detect(pathValue).name;
|
|
63078
|
+
} catch {
|
|
63079
|
+
return;
|
|
63080
|
+
}
|
|
62664
63081
|
}
|
|
62665
63082
|
function collect(value, previous) {
|
|
62666
63083
|
return previous.concat([value]);
|
|
@@ -62671,4 +63088,4 @@ if (!process.argv.slice(2).length) {
|
|
|
62671
63088
|
}
|
|
62672
63089
|
program2.parse();
|
|
62673
63090
|
|
|
62674
|
-
//# debugId=
|
|
63091
|
+
//# debugId=280ED4C71DBDC32964756E2164756E21
|