sandboxedjs 0.1.19 → 0.1.21

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/index.js CHANGED
@@ -8583,10 +8583,6 @@ async function inflate(data) {
8583
8583
  if (isNode) return new Uint8Array((await nodeZlib()).inflateSync(data));
8584
8584
  return throughStream(data, new DecompressionStream("deflate"));
8585
8585
  }
8586
- async function inflateRaw(data) {
8587
- if (isNode) return new Uint8Array((await nodeZlib()).inflateRawSync(data));
8588
- return throughStream(data, new DecompressionStream("deflate-raw"));
8589
- }
8590
8586
  var cryptoPromise = null;
8591
8587
  function nodeCrypto() {
8592
8588
  cryptoPromise ??= nodeBuiltin("crypto");
@@ -10926,7 +10922,7 @@ fi
10926
10922
  `;
10927
10923
  var MOTD = `Welcome to SandboxedJS \u2014 a Linux-like container running inside Node.js.
10928
10924
 
10929
- * Node.js, npm and MicroPython are preinstalled.
10925
+ * Node.js, npm and CPython (Pyodide) are preinstalled.
10930
10926
  * The filesystem is virtual: nothing here touches your host.
10931
10927
  * Run 'help' for the list of built-in commands.
10932
10928
 
@@ -21899,6 +21895,9 @@ function nodeCommands() {
21899
21895
  return [node, nodeVersionFile];
21900
21896
  }
21901
21897
 
21898
+ // src/runtime/python.ts
21899
+ init_path();
21900
+
21902
21901
  // src/runtime/emscripten-fs.ts
21903
21902
  init_errno();
21904
21903
  init_path();
@@ -22269,12 +22268,13 @@ function mountContainerDirs(py, ctx) {
22269
22268
  function bootstrap(py, ctx, argv, scriptDir) {
22270
22269
  const paths = [
22271
22270
  ...scriptDir ? [scriptDir] : [""],
22271
+ "/workspace",
22272
22272
  "/usr/lib/python3",
22273
22273
  "/usr/lib/python3/site-packages",
22274
22274
  "/usr/local/lib/python3/site-packages"
22275
22275
  ];
22276
22276
  py.runPython(`
22277
- import sys, os
22277
+ import sys, os, importlib
22278
22278
  sys.argv[:] = ${JSON.stringify(argv)}
22279
22279
  for __p in reversed(${JSON.stringify(paths)}):
22280
22280
  if __p and __p not in sys.path:
@@ -22287,6 +22287,7 @@ except NameError:
22287
22287
  pass
22288
22288
  os.environ.clear()
22289
22289
  os.environ.update(${JSON.stringify(ctx.env)})
22290
+ importlib.invalidate_caches()
22290
22291
  `);
22291
22292
  try {
22292
22293
  py.FS.chdir(ctx.cwd);
@@ -22376,22 +22377,92 @@ async function runCPythonProgram(ctx, source, argv, scriptDir, stdinText) {
22376
22377
  }
22377
22378
  }
22378
22379
  }
22380
+ async function cpythonVersion(ctx) {
22381
+ try {
22382
+ const py = await interpreterFor(ctx);
22383
+ return String(py.runPython("import sys; sys.version.split()[0]"));
22384
+ } catch {
22385
+ return null;
22386
+ }
22387
+ }
22388
+ async function runCPythonRepl(ctx) {
22389
+ let py;
22390
+ try {
22391
+ py = await interpreterFor(ctx);
22392
+ } catch (error) {
22393
+ ctx.stderr.write(`python3: Pyodide is unavailable (${error instanceof Error ? error.message : String(error)})
22394
+ `);
22395
+ return 127;
22396
+ }
22397
+ mountContainerDirs(py, ctx);
22398
+ bootstrap(py, ctx, [""], null);
22399
+ const decoder7 = new TextDecoder();
22400
+ py.setStdout({ write: (data) => (ctx.write(decoder7.decode(data)), data.length) });
22401
+ py.setStderr({ write: (data) => (ctx.stderr.write(decoder7.decode(data)), data.length) });
22402
+ ctx.line(`Python ${String(py.runPython("import sys; sys.version.split()[0]"))} (Pyodide)`);
22403
+ ctx.line('Type "help()" for more information.');
22404
+ let source = "";
22405
+ for (; ; ) {
22406
+ ctx.write(source ? "... " : ">>> ");
22407
+ const line = await ctx.stdin.readLine();
22408
+ if (line === null) {
22409
+ ctx.line("");
22410
+ break;
22411
+ }
22412
+ if (!source && ["exit()", "quit()"].includes(line.trim())) break;
22413
+ source += `${source ? "\n" : ""}${line}`;
22414
+ if (/[:\\]\s*$/.test(line) || source.includes("\n") && line.trim() !== "" && /^\s+/.test(line)) continue;
22415
+ try {
22416
+ await py.runPythonAsync(source);
22417
+ } catch (error) {
22418
+ reportError(ctx, error);
22419
+ }
22420
+ source = "";
22421
+ }
22422
+ return 0;
22423
+ }
22379
22424
  var micropip = defineCommand({
22380
22425
  name: "micropip",
22381
22426
  path: "/usr/bin/micropip",
22427
+ aliases: ["pip", "pip3"],
22382
22428
  summary: "install Python packages into the running interpreter",
22383
22429
  usage: "micropip install <package>...",
22384
22430
  async run(ctx) {
22385
- const [action, ...packages] = ctx.args;
22386
- if (action !== "install" || packages.length === 0) {
22387
- ctx.line("usage: micropip install <package>...");
22431
+ const [action, ...args] = ctx.args;
22432
+ if (action === "--version" || action === "-V") {
22433
+ ctx.line("pip (micropip, Pyodide)");
22434
+ return 0;
22435
+ }
22436
+ if (action !== "install") {
22437
+ ctx.line("usage: pip install [-r requirements.txt] <package>...");
22388
22438
  return action === void 0 ? 1 : 0;
22389
22439
  }
22440
+ const packages = args.filter((arg) => !arg.startsWith("-"));
22441
+ const requirementIndex = args.findIndex((arg) => arg === "-r" || arg === "--requirement");
22442
+ if (requirementIndex >= 0) {
22443
+ const file3 = args[requirementIndex + 1];
22444
+ if (!file3) {
22445
+ ctx.stderr.write("pip: option -r requires a file\n");
22446
+ return 2;
22447
+ }
22448
+ try {
22449
+ packages.splice(packages.indexOf(file3), 1);
22450
+ packages.push(...ctx.vfs.readText(ctx.path(file3), ctx.cred).split(/\r?\n/).map((line) => line.replace(/#.*$/, "").trim()).filter(Boolean));
22451
+ } catch {
22452
+ ctx.stderr.write(`pip: could not open requirements file '${file3}'
22453
+ `);
22454
+ return 1;
22455
+ }
22456
+ }
22457
+ if (packages.length === 0) {
22458
+ ctx.stderr.write("pip: no packages specified\n");
22459
+ return 1;
22460
+ }
22390
22461
  let py;
22391
22462
  try {
22392
22463
  py = await interpreterFor(ctx);
22393
22464
  } catch {
22394
- ctx.stderr.write("micropip: CPython is unavailable in this host\n");
22465
+ ctx.stderr.write("pip: Pyodide is unavailable in this host\n");
22395
22466
  return 127;
22396
22467
  }
22397
22468
  try {
@@ -22405,7 +22476,7 @@ var micropip = defineCommand({
22405
22476
  return 0;
22406
22477
  } catch (error) {
22407
22478
  ctx.stderr.write(
22408
- `micropip: ${error instanceof Error ? error.message : String(error)}
22479
+ `Package installation failed: ${error instanceof Error ? error.message : String(error)}
22409
22480
  `
22410
22481
  );
22411
22482
  return 1;
@@ -22414,138 +22485,25 @@ var micropip = defineCommand({
22414
22485
  });
22415
22486
 
22416
22487
  // src/runtime/python.ts
22417
- init_path();
22418
- var PYTHON_VERSION = "3.4.0";
22419
- var MICROPYTHON_BANNER = "MicroPython v1.28.0 on 2026-04-06; SandboxedJS with Emscripten";
22420
- var loaderPromise = null;
22421
- var wasmUrl;
22422
- var engine = "auto";
22423
- var cpythonUsable = null;
22424
- async function useCPython() {
22425
- if (engine === "micropython") return false;
22426
- if (engine === "cpython") return true;
22427
- cpythonUsable ??= isCPythonAvailable();
22428
- return cpythonUsable;
22429
- }
22488
+ var PYTHON_VERSION = "3.13";
22430
22489
  function configurePython(options = {}) {
22431
- if (options.engine !== void 0) {
22432
- engine = options.engine;
22433
- cpythonUsable = null;
22434
- }
22435
- if (options.indexURL !== void 0) configureCPython({ indexURL: options.indexURL });
22436
- if (options.pyodideURL !== void 0) configureCPython({ moduleURL: options.pyodideURL });
22437
- if (options.wasmUrl !== void 0) wasmUrl = options.wasmUrl;
22438
- }
22439
- async function getLoader() {
22440
- if (!loaderPromise) {
22441
- loaderPromise = import('@micropython/micropython-webassembly-pyscript/micropython.mjs').then(
22442
- (m) => m.loadMicroPython
22443
- );
22444
- }
22445
- return loaderPromise;
22446
- }
22447
- async function isPythonAvailable() {
22448
- try {
22449
- await getLoader();
22450
- return true;
22451
- } catch {
22452
- return false;
22453
- }
22454
- }
22455
- async function createInterpreter(ctx, opts) {
22456
- const loadMicroPython = await getLoader();
22457
- const mp = await loadMicroPython({
22458
- stdout: opts.stdout,
22459
- stderr: opts.stderr,
22460
- ...opts.stdin ? { stdin: opts.stdin } : {},
22461
- ...wasmUrl ? { url: wasmUrl } : {},
22462
- linebuffer: false,
22463
- heapsize: opts.heapsize ?? 64 * 1024 * 1024
22464
- });
22465
- mountContainerFs(mp.FS, { vfs: ctx.vfs, cred: ctx.cred });
22466
- try {
22467
- mp.FS.chdir(ctx.cwd);
22468
- } catch {
22469
- }
22470
- return mp;
22471
- }
22472
- function bootstrapPython(mp, ctx, argv, scriptDir) {
22473
- const envEntries = Object.entries(ctx.env).map(([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`).join(", ");
22474
- const paths = [
22475
- ...scriptDir ? [scriptDir] : [""],
22476
- "/usr/lib/python3",
22477
- "/usr/lib/python3/site-packages",
22478
- "/usr/local/lib/python3/site-packages"
22479
- ];
22480
- mp.runPython(`
22481
- import sys, os
22482
- sys.argv[:] = ${JSON.stringify(argv)}
22483
- for __p in reversed(${JSON.stringify(paths)}):
22484
- if __p not in sys.path:
22485
- sys.path.insert(0, __p)
22486
- del __p
22487
- os.environ = {${envEntries}}
22488
- os.getenv = lambda k, d=None: os.environ.get(k, d)
22489
- os.putenv = lambda k, v: os.environ.__setitem__(k, v)
22490
- os.unsetenv = lambda k: os.environ.pop(k, None)
22491
- os.sep = '/'
22492
- os.linesep = '\\n'
22493
- os.name = 'posix'
22494
- `);
22495
- }
22496
- function reportPythonError(ctx, e) {
22497
- const message = e instanceof Error ? e.message : String(e);
22498
- const systemExit = /SystemExit:?\s*(-?\d+)?/.exec(message);
22499
- if (systemExit && /SystemExit/.test(message)) {
22500
- return systemExit[1] !== void 0 ? Number(systemExit[1]) & 255 : 0;
22501
- }
22502
- if (/KeyboardInterrupt/.test(message)) {
22503
- ctx.stderr.write("KeyboardInterrupt\n");
22504
- return 130;
22505
- }
22506
- const text = message.replace(/^PythonError:\s*/, "");
22507
- ctx.stderr.write(text.endsWith("\n") ? text : text + "\n");
22508
- return 1;
22509
- }
22510
- async function runProgram(ctx, source, argv, scriptDir, stdinText) {
22511
- if (await useCPython()) {
22512
- return runCPythonProgram(ctx, source, argv, scriptDir, stdinText);
22513
- }
22514
- let stdinOffset = 0;
22515
- const stdinBytes = stdinText === null ? new Uint8Array(0) : new TextEncoder().encode(stdinText);
22516
- const mp = await createInterpreter(ctx, {
22517
- stdout: (chunk) => ctx.write(chunk),
22518
- stderr: (chunk) => ctx.stderr.write(chunk),
22519
- stdin: stdinText === null ? void 0 : () => stdinOffset < stdinBytes.length ? stdinBytes[stdinOffset++] : null
22520
- });
22521
- try {
22522
- bootstrapPython(mp, ctx, argv, scriptDir);
22523
- } catch (e) {
22524
- return { exitCode: reportPythonError(ctx, e) };
22525
- }
22526
- try {
22527
- await mp.runPythonAsync(source);
22528
- return { exitCode: 0 };
22529
- } catch (e) {
22530
- return { exitCode: reportPythonError(ctx, e) };
22531
- }
22490
+ configureCPython({ indexURL: options.indexURL, moduleURL: options.pyodideURL });
22532
22491
  }
22492
+ var isPythonAvailable = isCPythonAvailable;
22533
22493
  async function readStdin(ctx) {
22534
22494
  if (ctx.stdin.isTTY || ctx.stdin.interactive) return null;
22535
22495
  const bytes = await ctx.stdin.readAll();
22536
- if (bytes.length === 0) return null;
22537
- return new TextDecoder().decode(bytes);
22496
+ return bytes.length ? new TextDecoder().decode(bytes) : null;
22538
22497
  }
22539
22498
  var python = defineCommand({
22540
22499
  name: "python3",
22541
22500
  path: "/usr/bin/python3",
22542
- aliases: ["python", "micropython"],
22543
- summary: "run a Python program (MicroPython)",
22501
+ aliases: ["python"],
22502
+ summary: "run Python using CPython (Pyodide)",
22544
22503
  usage: "python3 [-c command | -m module | script.py] [arguments]",
22545
- manual: `Python is provided by MicroPython compiled to WebAssembly. The
22546
- standard library subset includes json, re, os, sys, math, random, hashlib,
22547
- binascii, struct, time, collections, itertools, functools, asyncio and more.
22548
- Scripts see the container's filesystem, so open('/etc/passwd') works.`,
22504
+ manual: `Python is CPython compiled to WebAssembly by Pyodide. Scripts use
22505
+ the container's virtual filesystem, modules in /workspace are importable, and
22506
+ compatible packages can be installed with pip (micropip).`,
22549
22507
  async run(ctx) {
22550
22508
  const argv = ctx.args;
22551
22509
  let i = 0;
@@ -22555,7 +22513,7 @@ Scripts see the container's filesystem, so open('/etc/passwd') works.`,
22555
22513
  for (; i < argv.length; i++) {
22556
22514
  const arg = argv[i];
22557
22515
  if (arg === "-V" || arg === "--version") {
22558
- ctx.line(`Python ${PYTHON_VERSION} (MicroPython v1.28.0)`);
22516
+ ctx.line(`Python ${await cpythonVersion(ctx) ?? PYTHON_VERSION}`);
22559
22517
  return 0;
22560
22518
  }
22561
22519
  if (arg === "-h" || arg === "--help") {
@@ -22577,7 +22535,7 @@ Scripts see the container's filesystem, so open('/etc/passwd') works.`,
22577
22535
  i++;
22578
22536
  break;
22579
22537
  }
22580
- if (arg === "-i" || arg === "-u" || arg === "-B" || arg === "-E" || arg === "-s" || arg === "-S" || arg === "-O") continue;
22538
+ if (["-i", "-u", "-B", "-E", "-s", "-S", "-O"].includes(arg)) continue;
22581
22539
  if (arg.startsWith("-")) continue;
22582
22540
  script = arg;
22583
22541
  i++;
@@ -22585,38 +22543,28 @@ Scripts see the container's filesystem, so open('/etc/passwd') works.`,
22585
22543
  }
22586
22544
  const rest = argv.slice(i);
22587
22545
  if (command !== void 0) {
22588
- const stdin2 = await readStdin(ctx);
22589
- const outcome2 = await runProgram(ctx, command, ["-c", ...rest], null, stdin2);
22590
- return outcome2.exitCode;
22546
+ return (await runCPythonProgram(ctx, command, ["-c", ...rest], null, await readStdin(ctx))).exitCode;
22591
22547
  }
22592
22548
  if (moduleName !== void 0) {
22593
- const stdin2 = await readStdin(ctx);
22594
22549
  const program = `
22595
- import sys
22550
+ import runpy, sys
22596
22551
  sys.argv = ${JSON.stringify([moduleName, ...rest])}
22597
22552
  try:
22598
- __mod = __import__(${JSON.stringify(moduleName)})
22599
- except ImportError as exc:
22600
- print("${"/usr/bin/python3"}: No module named " + ${JSON.stringify(moduleName)}, file=sys.stderr)
22553
+ runpy.run_module(${JSON.stringify(moduleName)}, run_name="__main__", alter_sys=True)
22554
+ except ImportError:
22555
+ print("/usr/bin/python3: No module named " + ${JSON.stringify(moduleName)}, file=sys.stderr)
22601
22556
  raise SystemExit(1)
22602
- _main = getattr(__mod, "main", None)
22603
- if callable(_main):
22604
- _main()
22605
22557
  `;
22606
- const outcome2 = await runProgram(ctx, program, [moduleName, ...rest], null, stdin2);
22607
- return outcome2.exitCode;
22558
+ return (await runCPythonProgram(ctx, program, [moduleName, ...rest], null, await readStdin(ctx))).exitCode;
22608
22559
  }
22609
22560
  if (script === void 0) {
22610
- if (ctx.stdin.isTTY) return runRepl2(ctx);
22561
+ if (ctx.stdin.isTTY) return runCPythonRepl(ctx);
22611
22562
  const source2 = new TextDecoder().decode(await ctx.stdin.readAll());
22612
- if (source2.trim() === "") return 0;
22613
- const outcome2 = await runProgram(ctx, source2, ["", ...rest], null, null);
22614
- return outcome2.exitCode;
22563
+ return source2.trim() ? (await runCPythonProgram(ctx, source2, ["", ...rest], null, null)).exitCode : 0;
22615
22564
  }
22616
22565
  if (script === "-") {
22617
22566
  const source2 = new TextDecoder().decode(await ctx.stdin.readAll());
22618
- const outcome2 = await runProgram(ctx, source2, ["-", ...rest], null, null);
22619
- return outcome2.exitCode;
22567
+ return (await runCPythonProgram(ctx, source2, ["-", ...rest], null, null)).exitCode;
22620
22568
  }
22621
22569
  const abs = ctx.path(script);
22622
22570
  let source;
@@ -22627,213 +22575,17 @@ if callable(_main):
22627
22575
  `);
22628
22576
  return 2;
22629
22577
  }
22630
- const stdin = await readStdin(ctx);
22631
- const outcome = await runProgram(ctx, source, [abs, ...rest], dirname(abs), stdin);
22632
- return outcome.exitCode;
22578
+ return (await runCPythonProgram(ctx, source, [abs, ...rest], dirname(abs), await readStdin(ctx))).exitCode;
22633
22579
  }
22634
22580
  });
22635
- async function runRepl2(ctx) {
22636
- ctx.line(MICROPYTHON_BANNER);
22637
- ctx.line('Type "help()" for more information.');
22638
- const mp = await createInterpreter(ctx, {
22639
- stdout: (chunk) => ctx.write(chunk),
22640
- stderr: (chunk) => ctx.stderr.write(chunk)
22641
- });
22642
- bootstrapPython(mp, ctx, [""], null);
22643
- let buffer = "";
22644
- for (; ; ) {
22645
- ctx.write(buffer === "" ? ">>> " : "... ");
22646
- const line = await ctx.stdin.readLine();
22647
- if (line === null) {
22648
- ctx.line("");
22649
- break;
22650
- }
22651
- if (buffer === "" && (line.trim() === "exit()" || line.trim() === "quit()")) break;
22652
- buffer += (buffer === "" ? "" : "\n") + line;
22653
- if (/[:\\]\s*$/.test(line) || buffer !== "" && line.trim() !== "" && /^\s+/.test(line)) continue;
22654
- try {
22655
- const trimmed = buffer.trim();
22656
- const isExpression = !/^(import|from|def|class|if|for|while|try|with|return|raise|pass|del|global|nonlocal|assert|@)\b/.test(trimmed) && !trimmed.includes("\n") && !/^[A-Za-z_][A-Za-z0-9_]*\s*(=[^=]|[+\-*/|&^]=)/.test(trimmed);
22657
- if (isExpression) {
22658
- mp.runPython(`__sbx_repl = (${trimmed})
22659
- if __sbx_repl is not None: print(repr(__sbx_repl))`);
22660
- } else {
22661
- mp.runPython(buffer);
22662
- }
22663
- } catch (e) {
22664
- const message = e instanceof Error ? e.message : String(e);
22665
- if (/SystemExit/.test(message)) break;
22666
- try {
22667
- mp.runPython(buffer);
22668
- } catch (inner) {
22669
- const text = (inner instanceof Error ? inner.message : String(inner)).replace(/^PythonError:\s*/, "");
22670
- ctx.stderr.write(text.endsWith("\n") ? text : text + "\n");
22671
- }
22672
- }
22673
- buffer = "";
22674
- }
22675
- return 0;
22676
- }
22677
22581
  function printHelp2(ctx) {
22678
22582
  ctx.line("usage: python3 [option] ... [-c cmd | -m mod | file | -] [arg] ...");
22679
- ctx.line("Options:");
22680
22583
  ctx.line("-c cmd : program passed in as string");
22681
22584
  ctx.line("-m mod : run library module as a script");
22682
- ctx.line("-V : print the Python version number and exit");
22683
- ctx.line("file : program read from script file");
22684
- ctx.line("- : program read from stdin");
22685
- }
22686
- var pip = defineCommand({
22687
- name: "pip",
22688
- path: "/usr/bin/pip",
22689
- aliases: ["pip3"],
22690
- summary: "install Python packages",
22691
- usage: "pip install PACKAGE...",
22692
- async run(ctx) {
22693
- const [subcommand, ...rest] = ctx.args;
22694
- const siteDir = "/usr/lib/python3/site-packages";
22695
- if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h") {
22696
- ctx.line("Usage: pip <command> [options]");
22697
- ctx.line("");
22698
- ctx.line("Commands:");
22699
- ctx.line(" install Install packages");
22700
- ctx.line(" list List installed packages");
22701
- ctx.line(" show Show information about installed packages");
22702
- ctx.line(" uninstall Uninstall packages");
22703
- return 0;
22704
- }
22705
- if (subcommand === "--version" || subcommand === "-V") {
22706
- ctx.line(`pip 24.0 from ${siteDir}/pip (python ${PYTHON_VERSION})`);
22707
- return 0;
22708
- }
22709
- if (subcommand === "list") {
22710
- if (!ctx.vfs.lexists(siteDir)) {
22711
- ctx.line("Package Version");
22712
- ctx.line("---------- -------");
22713
- return 0;
22714
- }
22715
- ctx.line("Package Version");
22716
- ctx.line("---------- -------");
22717
- for (const name of ctx.vfs.readdir(siteDir, ctx.cred)) {
22718
- if (name.endsWith(".dist-info")) {
22719
- const [pkg, version] = name.replace(".dist-info", "").split("-");
22720
- ctx.line(`${(pkg ?? name).padEnd(10)} ${version ?? "0.0.0"}`);
22721
- }
22722
- }
22723
- return 0;
22724
- }
22725
- if (subcommand === "uninstall") {
22726
- let status2 = 0;
22727
- for (const name of rest.filter((a) => !a.startsWith("-"))) {
22728
- const target = join(siteDir, name);
22729
- if (ctx.vfs.lexists(target)) {
22730
- ctx.vfs.rmrf(target, ctx.cred);
22731
- ctx.line(`Successfully uninstalled ${name}`);
22732
- } else {
22733
- ctx.warn(`WARNING: Skipping ${name} as it is not installed.`);
22734
- status2 = 1;
22735
- }
22736
- }
22737
- return status2;
22738
- }
22739
- if (subcommand !== "install") {
22740
- ctx.warn(`ERROR: unknown command "${subcommand}"`);
22741
- return 1;
22742
- }
22743
- const packages = rest.filter((a) => !a.startsWith("-"));
22744
- if (packages.length === 0) {
22745
- ctx.warn("ERROR: You must give at least one requirement to install");
22746
- return 1;
22747
- }
22748
- if (!ctx.kernel.net.options.allowOutbound) {
22749
- ctx.warn("ERROR: Could not find a version that satisfies the requirement");
22750
- ctx.warn("Outbound network access is disabled for this container.");
22751
- ctx.warn("Enable it with createContainer({ network: { allowOutbound: true } }).");
22752
- return 1;
22753
- }
22754
- ctx.vfs.mkdir(siteDir, { recursive: true, cred: ctx.cred, mode: 493 });
22755
- let status = 0;
22756
- for (const spec of packages) {
22757
- const [name, version] = spec.split(/[=<>~!]+/);
22758
- const url = `https://pypi.org/pypi/${name}/json`;
22759
- if (!ctx.kernel.net.outboundAllowed(url)) {
22760
- ctx.warn(`ERROR: host pypi.org is not in the allowed list`);
22761
- status = 1;
22762
- continue;
22763
- }
22764
- ctx.line(`Collecting ${spec}`);
22765
- try {
22766
- const response = await fetch(url);
22767
- if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
22768
- const meta = await response.json();
22769
- const chosen = version && meta.releases[version] ? version : meta.info.version;
22770
- const files = meta.releases[chosen] ?? [];
22771
- const wheel = files.find((f) => f.packagetype === "bdist_wheel" && f.filename.includes("py3-none-any"));
22772
- if (!wheel) {
22773
- ctx.warn(`ERROR: no pure-Python wheel available for ${name} ${chosen}`);
22774
- status = 1;
22775
- continue;
22776
- }
22777
- const data = new Uint8Array(await (await fetch(wheel.url)).arrayBuffer());
22778
- ctx.line(` Downloading ${wheel.filename} (${Math.round(data.length / 1024)} kB)`);
22779
- await installWheel(ctx, siteDir, data, name, chosen);
22780
- ctx.line(`Successfully installed ${name}-${chosen}`);
22781
- } catch (e) {
22782
- ctx.warn(`ERROR: Could not install ${spec}: ${e instanceof Error ? e.message : String(e)}`);
22783
- status = 1;
22784
- }
22785
- }
22786
- return status;
22787
- }
22788
- });
22789
- async function installWheel(ctx, siteDir, data, name, version) {
22790
- const entries = await readZip(data);
22791
- for (const entry of entries) {
22792
- if (entry.name.endsWith("/")) continue;
22793
- const target = join(siteDir, entry.name);
22794
- const parent = dirname(target);
22795
- if (!ctx.vfs.lexists(parent)) ctx.vfs.mkdir(parent, { recursive: true, cred: ctx.cred, mode: 493 });
22796
- ctx.vfs.writeFile(target, entry.data, { cred: ctx.cred, mode: 420 });
22797
- }
22798
- const distInfo = join(siteDir, `${name}-${version}.dist-info`);
22799
- if (!ctx.vfs.lexists(distInfo)) ctx.vfs.mkdir(distInfo, { recursive: true, cred: ctx.cred, mode: 493 });
22800
- ctx.vfs.writeFile(join(distInfo, "METADATA"), `Name: ${name}
22801
- Version: ${version}
22802
- `, { cred: ctx.cred });
22803
- }
22804
- async function readZip(data) {
22805
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
22806
- const entries = [];
22807
- let eocd = -1;
22808
- for (let i = data.length - 22; i >= 0 && i > data.length - 65558; i--) {
22809
- if (view.getUint32(i, true) === 101010256) {
22810
- eocd = i;
22811
- break;
22812
- }
22813
- }
22814
- if (eocd < 0) return entries;
22815
- const count = view.getUint16(eocd + 10, true);
22816
- let offset = view.getUint32(eocd + 16, true);
22817
- for (let i = 0; i < count; i++) {
22818
- if (view.getUint32(offset, true) !== 33639248) break;
22819
- const method = view.getUint16(offset + 10, true);
22820
- const compressedSize = view.getUint32(offset + 20, true);
22821
- const nameLength = view.getUint16(offset + 28, true);
22822
- const extraLength = view.getUint16(offset + 30, true);
22823
- const commentLength = view.getUint16(offset + 32, true);
22824
- const localOffset = view.getUint32(offset + 42, true);
22825
- const name = new TextDecoder().decode(data.subarray(offset + 46, offset + 46 + nameLength));
22826
- const localNameLength = view.getUint16(localOffset + 26, true);
22827
- const localExtraLength = view.getUint16(localOffset + 28, true);
22828
- const dataStart = localOffset + 30 + localNameLength + localExtraLength;
22829
- const raw = data.subarray(dataStart, dataStart + compressedSize);
22830
- entries.push({ name, data: method === 0 ? raw.slice() : await inflateRaw(raw) });
22831
- offset += 46 + nameLength + extraLength + commentLength;
22832
- }
22833
- return entries;
22585
+ ctx.line("-V : print the Python version and exit");
22834
22586
  }
22835
22587
  function pythonCommands() {
22836
- return [python, pip, micropip];
22588
+ return [python, micropip];
22837
22589
  }
22838
22590
 
22839
22591
  // src/runtime/ffmpeg.ts
@@ -23647,7 +23399,7 @@ var apt = defineCommand({
23647
23399
  const provided = {
23648
23400
  nodejs: { version: NODE_VERSION.replace(/^v/, ""), description: "Node.js event-based server-side JavaScript engine" },
23649
23401
  npm: { version: NPM_VERSION, description: "package manager for Node.js" },
23650
- python3: { version: "3.4.0-micropython", description: "interactive high-level object-oriented language" },
23402
+ python3: { version: PYTHON_VERSION, description: "CPython interpreter powered by Pyodide" },
23651
23403
  "python3-pip": { version: "24.0", description: "Python package installer" },
23652
23404
  coreutils: { version: "9.4", description: "GNU core utilities" },
23653
23405
  grep: { version: "3.11", description: "GNU grep, egrep and fgrep" },
@@ -23751,7 +23503,7 @@ var dpkg = defineCommand({
23751
23503
  ctx.line("||/ Name Version Architecture Description");
23752
23504
  ctx.line("+++-==============-============-============-=================================");
23753
23505
  ctx.line(`ii nodejs ${NODE_VERSION.replace(/^v/, "").padEnd(12)} amd64 Node.js JavaScript runtime`);
23754
- ctx.line(`ii python3 3.4.0 amd64 MicroPython interpreter`);
23506
+ ctx.line(`ii python3 ${PYTHON_VERSION.padEnd(12)} wasm32 CPython (Pyodide) interpreter`);
23755
23507
  return 0;
23756
23508
  }
23757
23509
  ctx.line("dpkg 1.22.6 (amd64)");
@@ -24602,7 +24354,12 @@ function isNodeRuntime() {
24602
24354
  return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
24603
24355
  }
24604
24356
  async function bootBrowserPod(opts) {
24605
- const { Nodepod } = await import('@scelar/nodepod');
24357
+ const { Nodepod, createBrowserHost, getRuntimeHost, setRuntimeHost } = await import('@scelar/nodepod');
24358
+ try {
24359
+ getRuntimeHost();
24360
+ } catch {
24361
+ setRuntimeHost(createBrowserHost());
24362
+ }
24606
24363
  return Nodepod.boot({
24607
24364
  env: opts.env ?? {},
24608
24365
  workdir: opts.cwd ?? "/",