@poe-platform/safe-bash 0.1.683 → 0.1.684

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.
Files changed (41) hide show
  1. package/README.md +10 -0
  2. package/dist/safe-bash/chunks/chunk-4MPK7VIM.js +519 -0
  3. package/dist/safe-bash/chunks/chunk-4MPK7VIM.js.map +7 -0
  4. package/dist/safe-bash/chunks/{chunk-6R7IR2MV.js → chunk-BONY6MX5.js} +731 -15
  5. package/dist/safe-bash/chunks/chunk-BONY6MX5.js.map +7 -0
  6. package/dist/safe-bash/commands/python/index.browser.js +12 -2
  7. package/dist/safe-bash/commands/python/index.d.ts +5 -0
  8. package/dist/safe-bash/commands/python/index.d.ts.map +1 -1
  9. package/dist/safe-bash/commands/python/index.js +15 -3
  10. package/dist/safe-bash/commands/python/index.js.map +1 -1
  11. package/dist/safe-bash/commands/python/jspi-assets.d.ts +14 -0
  12. package/dist/safe-bash/commands/python/jspi-assets.d.ts.map +1 -0
  13. package/dist/safe-bash/commands/python/jspi-assets.js +51 -0
  14. package/dist/safe-bash/commands/python/jspi-assets.js.map +1 -0
  15. package/dist/safe-bash/commands/python/jspi-scheduler.d.ts +8 -0
  16. package/dist/safe-bash/commands/python/jspi-scheduler.d.ts.map +1 -0
  17. package/dist/safe-bash/commands/python/jspi-scheduler.js +49 -0
  18. package/dist/safe-bash/commands/python/jspi-scheduler.js.map +1 -0
  19. package/dist/safe-bash/commands/python/jspi-trampoline.d.ts +5 -0
  20. package/dist/safe-bash/commands/python/jspi-trampoline.d.ts.map +1 -0
  21. package/dist/safe-bash/commands/python/jspi-trampoline.js +90 -0
  22. package/dist/safe-bash/commands/python/jspi-trampoline.js.map +1 -0
  23. package/dist/safe-bash/commands/python/jspi.d.ts +22 -0
  24. package/dist/safe-bash/commands/python/jspi.d.ts.map +1 -0
  25. package/dist/safe-bash/commands/python/jspi.js +295 -0
  26. package/dist/safe-bash/commands/python/jspi.js.map +1 -0
  27. package/dist/safe-bash/commands/python/runtime-scripts.d.ts +6 -0
  28. package/dist/safe-bash/commands/python/runtime-scripts.d.ts.map +1 -0
  29. package/dist/safe-bash/commands/python/runtime-scripts.js +168 -0
  30. package/dist/safe-bash/commands/python/runtime-scripts.js.map +1 -0
  31. package/dist/safe-bash/commands/python/worker.browser.js +13 -368
  32. package/dist/safe-bash/commands/python/worker.browser.js.map +3 -3
  33. package/dist/safe-bash/commands/python/worker.d.ts.map +1 -1
  34. package/dist/safe-bash/commands/python/worker.js +6 -168
  35. package/dist/safe-bash/commands/python/worker.js.map +1 -1
  36. package/dist/safe-bash/core.browser.js +12 -2
  37. package/dist/safe-bash/core.browser.js.map +1 -1
  38. package/package.json +2 -2
  39. package/dist/safe-bash/chunks/chunk-3G3EZZ63.js +0 -145
  40. package/dist/safe-bash/chunks/chunk-3G3EZZ63.js.map +0 -7
  41. package/dist/safe-bash/chunks/chunk-6R7IR2MV.js.map +0 -7
@@ -6,8 +6,14 @@ import {
6
6
  PythonInvocationError,
7
7
  isPythonFailureCategory,
8
8
  parsePythonInvocation,
9
+ pythonDirectoryEntries,
10
+ pythonExecution,
11
+ pythonImportMetadata,
12
+ pythonRuntimeRelocation,
13
+ pythonStatProjection,
14
+ pythonTreeCleanup,
9
15
  reportPythonFailure
10
- } from "./chunk-3G3EZZ63.js";
16
+ } from "./chunk-4MPK7VIM.js";
11
17
  import {
12
18
  createOutputOperation
13
19
  } from "./chunk-ZDYA3SLF.js";
@@ -44,21 +50,21 @@ function encodePythonReply(value, maxBytes) {
44
50
  used += bytes;
45
51
  if (used > maxBytes) throw new FsError("EFBIG", { syscall: "python reply" });
46
52
  };
47
- const string = (text) => {
48
- if (text.length + 2 > maxBytes - used) throw new FsError("EFBIG", { syscall: "python reply" });
53
+ const string = (text2) => {
54
+ if (text2.length + 2 > maxBytes - used) throw new FsError("EFBIG", { syscall: "python reply" });
49
55
  reserve(2);
50
- for (let index = 0; index < text.length; index++) {
51
- const code = text.charCodeAt(index);
56
+ for (let index = 0; index < text2.length; index++) {
57
+ const code = text2.charCodeAt(index);
52
58
  if (code < 32) reserve([8, 9, 10, 12, 13].includes(code) ? 2 : 6);
53
59
  else if (code === 34 || code === 92) reserve(2);
54
60
  else if (code < 128) reserve(1);
55
61
  else if (code < 2048) reserve(2);
56
- else if (code >= 55296 && code <= 56319 && text.charCodeAt(index + 1) >= 56320 && text.charCodeAt(index + 1) <= 57343) {
62
+ else if (code >= 55296 && code <= 56319 && text2.charCodeAt(index + 1) >= 56320 && text2.charCodeAt(index + 1) <= 57343) {
57
63
  reserve(4);
58
64
  index++;
59
65
  } else reserve(code >= 55296 && code <= 57343 ? 6 : 3);
60
66
  }
61
- return text;
67
+ return text2;
62
68
  };
63
69
  const own = (input, depth) => {
64
70
  if (depth > 64) throw new FsError("EFBIG", { syscall: "python reply" });
@@ -1131,10 +1137,10 @@ function createPythonPackageEnvironment(options = {}) {
1131
1137
  }
1132
1138
  for (const line of source.split("\n")) {
1133
1139
  const comment = line.split("").findIndex((character, index) => character === "#" && (index === 0 || line[index - 1].trim() === ""));
1134
- const text = (comment < 0 ? line : line.slice(0, comment)).trim();
1135
- if (!text) continue;
1136
- if (text.endsWith("\\") || text.startsWith("-")) throw failure(`Unsupported requirements option or continuation in ${path}: ${text}`);
1137
- requirements.push(normalizeRequirement(text, dirname(path)));
1140
+ const text2 = (comment < 0 ? line : line.slice(0, comment)).trim();
1141
+ if (!text2) continue;
1142
+ if (text2.endsWith("\\") || text2.startsWith("-")) throw failure(`Unsupported requirements option or continuation in ${path}: ${text2}`);
1143
+ requirements.push(normalizeRequirement(text2, dirname(path)));
1138
1144
  }
1139
1145
  }
1140
1146
  context.signal.throwIfAborted();
@@ -1424,6 +1430,706 @@ function createPythonExecutorPool(options) {
1424
1430
  };
1425
1431
  }
1426
1432
 
1433
+ // packages/safe-bash/src/commands/python/jspi.ts
1434
+ init_platform();
1435
+ import { createPythonNativeSyscalls } from "@poe-platform/safe-fs/core";
1436
+
1437
+ // packages/safe-bash/src/commands/python/jspi-trampoline.ts
1438
+ init_platform();
1439
+ var pythonJspiSignatures = Object.freeze({
1440
+ __syscall_chdir: "ip",
1441
+ __syscall_chmod: "ipi",
1442
+ __syscall_dup: "ii",
1443
+ __syscall_faccessat: "iipii",
1444
+ __syscall_fcntl64: "iiip",
1445
+ __syscall_fdatasync: "ii",
1446
+ __syscall_fstat64: "iip",
1447
+ __syscall_ftruncate64: "iij",
1448
+ __syscall_getcwd: "ipp",
1449
+ __syscall_getdents64: "iipp",
1450
+ __syscall_ioctl: "iiip",
1451
+ __syscall_lstat64: "ipp",
1452
+ __syscall_mkdirat: "iipi",
1453
+ __syscall_newfstatat: "iippi",
1454
+ __syscall_openat: "iipip",
1455
+ __syscall_readlinkat: "iippp",
1456
+ __syscall_renameat: "iipip",
1457
+ __syscall_rmdir: "ip",
1458
+ __syscall_stat64: "ipp",
1459
+ __syscall_symlinkat: "ipip",
1460
+ __syscall_truncate64: "ipj",
1461
+ __syscall_unlinkat: "iipi",
1462
+ __syscall_utimensat: "iippi",
1463
+ fd_close: "ii",
1464
+ fd_fdstat_get: "iip",
1465
+ fd_pread: "iippjp",
1466
+ fd_pwrite: "iippjp",
1467
+ fd_read: "iippp",
1468
+ fd_seek: "iijip",
1469
+ fd_sync: "ii",
1470
+ fd_write: "iippp"
1471
+ });
1472
+ var unsigned = (value) => {
1473
+ const bytes = [];
1474
+ do {
1475
+ bytes.push(value & 127 | (value > 127 ? 128 : 0));
1476
+ value >>>= 7;
1477
+ } while (value);
1478
+ return bytes;
1479
+ };
1480
+ var vector = (entries) => [...unsigned(entries.length), ...entries.flat()];
1481
+ var text = (value) => {
1482
+ const bytes = new TextEncoder().encode(value);
1483
+ return [...unsigned(bytes.length), ...bytes];
1484
+ };
1485
+ var section = (type, bytes) => [type, ...unsigned(bytes.length), ...bytes];
1486
+ var signature = (parameters, result) => [96, ...vector(parameters.map((type) => [type])), ...result === void 0 ? [0] : [1, result]];
1487
+ function createPythonJspiTrampoline() {
1488
+ const entries = Object.entries(pythonJspiSignatures);
1489
+ const types = entries.flatMap(([, value]) => {
1490
+ const parameters = [...value.slice(1)].map((type) => type === "j" ? 126 : 127);
1491
+ return [signature(parameters, 127), signature(parameters, 111)];
1492
+ });
1493
+ types.push(signature([111], 127));
1494
+ const imports = entries.flatMap(([name], index) => [
1495
+ [...text("original"), ...text(name), 0, ...unsigned(index * 2)],
1496
+ [...text("request"), ...text(name), 0, ...unsigned(index * 2 + 1)],
1497
+ [...text("shutdown"), ...text(name), 0, ...unsigned(index * 2)]
1498
+ ]);
1499
+ imports.push([...text("control"), ...text("syncify"), 1, 112, 0, 1]);
1500
+ imports.push([...text("control"), ...text("active"), 3, 127, 1]);
1501
+ const functions = entries.map(([, value], index) => {
1502
+ const argumentsBytes = [...value.slice(1)].flatMap((_type, parameter) => [32, ...unsigned(parameter)]);
1503
+ const code = [
1504
+ 0,
1505
+ 35,
1506
+ 0,
1507
+ 4,
1508
+ 127,
1509
+ 35,
1510
+ 0,
1511
+ 65,
1512
+ 2,
1513
+ 70,
1514
+ 4,
1515
+ 127,
1516
+ ...argumentsBytes,
1517
+ 16,
1518
+ ...unsigned(index * 3 + 2),
1519
+ 5,
1520
+ ...argumentsBytes,
1521
+ 16,
1522
+ ...unsigned(index * 3 + 1),
1523
+ 65,
1524
+ 0,
1525
+ 17,
1526
+ ...unsigned(entries.length * 2),
1527
+ 0,
1528
+ 11,
1529
+ 5,
1530
+ ...argumentsBytes,
1531
+ 16,
1532
+ ...unsigned(index * 3),
1533
+ 11,
1534
+ 11
1535
+ ];
1536
+ return [...unsigned(code.length), ...code];
1537
+ });
1538
+ return new Uint8Array([
1539
+ 0,
1540
+ 97,
1541
+ 115,
1542
+ 109,
1543
+ 1,
1544
+ 0,
1545
+ 0,
1546
+ 0,
1547
+ ...section(1, vector(types)),
1548
+ ...section(2, vector(imports)),
1549
+ ...section(3, vector(entries.map((_entry, index) => unsigned(index * 2)))),
1550
+ ...section(7, vector(entries.map(([name], index) => [...text(name), 0, ...unsigned(entries.length * 3 + index)]))),
1551
+ ...section(10, vector(functions))
1552
+ ]);
1553
+ }
1554
+ function createPythonJspiNativeCall() {
1555
+ const types = [
1556
+ signature([127], 127),
1557
+ signature([127]),
1558
+ signature([127], 111),
1559
+ signature([111], 127),
1560
+ signature([127, 127], 127),
1561
+ signature([], 127)
1562
+ ];
1563
+ const imports = [
1564
+ ["python", "utf8", 0],
1565
+ ["python", "unicode", 0],
1566
+ ["python", "free", 1],
1567
+ ["python", "noMemory", 5],
1568
+ ["request", "send", 2],
1569
+ ["shutdown", "send", 0]
1570
+ ];
1571
+ const code = [
1572
+ 1,
1573
+ 2,
1574
+ 127,
1575
+ 32,
1576
+ 1,
1577
+ 16,
1578
+ 0,
1579
+ 34,
1580
+ 2,
1581
+ 69,
1582
+ 4,
1583
+ 127,
1584
+ 65,
1585
+ 0,
1586
+ 5,
1587
+ 35,
1588
+ 0,
1589
+ 65,
1590
+ 2,
1591
+ 70,
1592
+ 4,
1593
+ 127,
1594
+ 32,
1595
+ 2,
1596
+ 16,
1597
+ 5,
1598
+ 5,
1599
+ 32,
1600
+ 2,
1601
+ 16,
1602
+ 4,
1603
+ 65,
1604
+ 0,
1605
+ 17,
1606
+ 3,
1607
+ 0,
1608
+ 11,
1609
+ 34,
1610
+ 2,
1611
+ 69,
1612
+ 4,
1613
+ 127,
1614
+ 16,
1615
+ 3,
1616
+ 5,
1617
+ 32,
1618
+ 2,
1619
+ 16,
1620
+ 1,
1621
+ 33,
1622
+ 3,
1623
+ 32,
1624
+ 2,
1625
+ 16,
1626
+ 2,
1627
+ 32,
1628
+ 3,
1629
+ 11,
1630
+ 11,
1631
+ 11
1632
+ ];
1633
+ return new Uint8Array([
1634
+ 0,
1635
+ 97,
1636
+ 115,
1637
+ 109,
1638
+ 1,
1639
+ 0,
1640
+ 0,
1641
+ 0,
1642
+ ...section(1, vector(types)),
1643
+ ...section(2, vector([
1644
+ ...imports.map(([namespace, name, type]) => [...text(namespace), ...text(name), 0, type]),
1645
+ [...text("control"), ...text("syncify"), 1, 112, 0, 1],
1646
+ [...text("control"), ...text("active"), 3, 127, 1]
1647
+ ])),
1648
+ ...section(3, vector([[4]])),
1649
+ ...section(7, vector([[...text("call"), 0, 6]])),
1650
+ ...section(10, vector([[...unsigned(code.length), ...code]]))
1651
+ ]);
1652
+ }
1653
+ function createPythonJspiStatResult() {
1654
+ const types = [
1655
+ signature([127], 127),
1656
+ signature([127]),
1657
+ signature([127, 127], 127),
1658
+ signature([127, 127, 127]),
1659
+ signature([], 127)
1660
+ ];
1661
+ const imports = [["create", 0], ["size", 0], ["item", 2], ["retain", 1], ["set", 3], ["invalid", 4]];
1662
+ const code = [
1663
+ 1,
1664
+ 3,
1665
+ 127,
1666
+ 32,
1667
+ 1,
1668
+ 16,
1669
+ 1,
1670
+ 35,
1671
+ 0,
1672
+ 71,
1673
+ 4,
1674
+ 64,
1675
+ 16,
1676
+ 5,
1677
+ 15,
1678
+ 11,
1679
+ 32,
1680
+ 0,
1681
+ 16,
1682
+ 0,
1683
+ 34,
1684
+ 2,
1685
+ 69,
1686
+ 4,
1687
+ 127,
1688
+ 65,
1689
+ 0,
1690
+ 5,
1691
+ 2,
1692
+ 64,
1693
+ 3,
1694
+ 64,
1695
+ 32,
1696
+ 3,
1697
+ 35,
1698
+ 0,
1699
+ 79,
1700
+ 13,
1701
+ 1,
1702
+ 32,
1703
+ 1,
1704
+ 32,
1705
+ 3,
1706
+ 16,
1707
+ 2,
1708
+ 34,
1709
+ 4,
1710
+ 16,
1711
+ 3,
1712
+ 32,
1713
+ 2,
1714
+ 32,
1715
+ 3,
1716
+ 32,
1717
+ 4,
1718
+ 16,
1719
+ 4,
1720
+ 32,
1721
+ 3,
1722
+ 65,
1723
+ 1,
1724
+ 106,
1725
+ 33,
1726
+ 3,
1727
+ 12,
1728
+ 0,
1729
+ 11,
1730
+ 11,
1731
+ 32,
1732
+ 2,
1733
+ 11,
1734
+ 11
1735
+ ];
1736
+ return new Uint8Array([
1737
+ 0,
1738
+ 97,
1739
+ 115,
1740
+ 109,
1741
+ 1,
1742
+ 0,
1743
+ 0,
1744
+ 0,
1745
+ ...section(1, vector(types)),
1746
+ ...section(2, vector([
1747
+ ...imports.map(([name, type]) => [...text("python"), ...text(name), 0, type]),
1748
+ [...text("python"), ...text("fields"), 3, 127, 0]
1749
+ ])),
1750
+ ...section(3, vector([[2]])),
1751
+ ...section(7, vector([[...text("construct"), 0, 6]])),
1752
+ ...section(10, vector([[...unsigned(code.length), ...code]]))
1753
+ ]);
1754
+ }
1755
+
1756
+ // packages/safe-bash/src/commands/python/jspi-scheduler.ts
1757
+ init_platform();
1758
+ function createPythonJspiScheduler() {
1759
+ const queued = /* @__PURE__ */ new Map();
1760
+ const active = /* @__PURE__ */ new Set();
1761
+ const failures = [];
1762
+ let closed = false;
1763
+ let retirement;
1764
+ return {
1765
+ scheduleCallback(callback, delay = 0) {
1766
+ if (closed) {
1767
+ callback.destroy?.();
1768
+ throw new Error("Python scheduler is closed");
1769
+ }
1770
+ const timer = setTimeout(() => {
1771
+ queued.delete(timer);
1772
+ const work = Promise.resolve().then(() => callback()).then(() => {
1773
+ }, (error) => {
1774
+ failures.push(error);
1775
+ }).finally(() => {
1776
+ active.delete(work);
1777
+ });
1778
+ active.add(work);
1779
+ }, delay);
1780
+ queued.set(timer, callback);
1781
+ },
1782
+ close() {
1783
+ if (retirement) return retirement;
1784
+ if (!active.size) closed = true;
1785
+ retirement = Promise.resolve().then(async () => {
1786
+ while (active.size) await Promise.all(active);
1787
+ closed = true;
1788
+ for (const [timer, callback] of queued) {
1789
+ clearTimeout(timer);
1790
+ try {
1791
+ callback.destroy?.();
1792
+ } catch (error) {
1793
+ failures.push(error);
1794
+ }
1795
+ }
1796
+ queued.clear();
1797
+ if (failures.length === 1) throw failures[0];
1798
+ if (failures.length) throw new AggregateError(failures, "Python callback retirement failed");
1799
+ });
1800
+ return retirement;
1801
+ }
1802
+ };
1803
+ }
1804
+
1805
+ // packages/safe-bash/src/commands/python/jspi.ts
1806
+ function createPythonJspiExecutor(options) {
1807
+ const engine = globalThis.WebAssembly;
1808
+ const active = new engine.Global({ value: "i32", mutable: true }, 0);
1809
+ const syncify = new engine.Table({ element: "anyfunc", initial: 1 });
1810
+ const controller = new AbortController();
1811
+ const scheduler = createPythonJspiScheduler();
1812
+ let admitted = false;
1813
+ let running;
1814
+ let retirement;
1815
+ let cleanup;
1816
+ let runtime;
1817
+ let native;
1818
+ let instanceBound = false;
1819
+ let importsBound = false;
1820
+ let schedulerBound = false;
1821
+ let methodDefinition = 0;
1822
+ let methodPointer;
1823
+ let statPointer;
1824
+ const originals = {};
1825
+ const execute = async (start) => {
1826
+ const signal = AbortSignal.any([start.signal, controller.signal]);
1827
+ const configuration = parsePythonInvocation(start.invocation.args, start.invocation.env);
1828
+ let qualified = false;
1829
+ let exitCode = 0;
1830
+ let finalized = 0;
1831
+ try {
1832
+ signal.throwIfAborted();
1833
+ if (start.packages?.requirements.length || start.installOnly) throw new PythonFailure("runtime-assets", { cause: new Error("JSPI runtime requires statically qualified packages") });
1834
+ runtime = await options.loadRuntime({
1835
+ jsglobals: /* @__PURE__ */ Object.create(null),
1836
+ args: configuration.startupArgs,
1837
+ env: configuration.env,
1838
+ bindImports(imports) {
1839
+ if (importsBound) throw new PythonFailure("runtime-abi");
1840
+ const namespaces = [...new Set(Object.values(imports))];
1841
+ for (const namespace of namespaces) {
1842
+ for (const [name, value] of Object.entries(namespace)) {
1843
+ if (typeof value !== "function") continue;
1844
+ if (name in pythonJspiSignatures) originals[name] = value;
1845
+ else if (name.startsWith("__syscall_") || name === "_maybe_connect_async") namespace[name] = () => -52;
1846
+ else if (name === "_emscripten_system") namespace[name] = (command) => command === 0 ? 0 : -52;
1847
+ }
1848
+ }
1849
+ if (!originals.fd_read || !originals.fd_write || !originals.__syscall_openat) throw new PythonFailure("runtime-abi");
1850
+ const original = Object.fromEntries(Object.keys(pythonJspiSignatures).map((name) => [name, originals[name] ?? (() => -52)]));
1851
+ const request = Object.fromEntries(Object.keys(pythonJspiSignatures).map((name) => [
1852
+ name,
1853
+ (...args) => native.invoke(name, args)
1854
+ ]));
1855
+ const shutdown = Object.fromEntries(Object.entries(request).map(([name, dispatch]) => [name, new engine.Suspending(dispatch)]));
1856
+ const bridge = new engine.Instance(options.trampoline, { original, request, shutdown, control: { active, syncify } });
1857
+ for (const namespace of namespaces) for (const name of Object.keys(originals)) {
1858
+ if (typeof namespace[name] === "function") namespace[name] = bridge.exports[name];
1859
+ }
1860
+ importsBound = true;
1861
+ },
1862
+ bindInstance(instance) {
1863
+ if (instanceBound || !importsBound || typeof instance.exports.syscall_syncify !== "function") throw new PythonFailure("runtime-abi");
1864
+ syncify.set(0, instance.exports.syscall_syncify);
1865
+ instanceBound = true;
1866
+ },
1867
+ bindScheduler(api) {
1868
+ if (schedulerBound) throw new PythonFailure("runtime-abi");
1869
+ api.scheduleCallback = scheduler.scheduleCallback;
1870
+ schedulerBound = true;
1871
+ }
1872
+ });
1873
+ if (!importsBound || !instanceBound || !schedulerBound || runtime.version !== "314.0.6" || !runtime._module.jspiSupported) throw new PythonFailure("runtime-abi");
1874
+ qualified = true;
1875
+ signal.throwIfAborted();
1876
+ runtime.runPython("import sys, os, json, runpy, traceback, types, warnings, textwrap, io, struct, linecache, importlib.machinery, shutil, stat, pyodide.ffi");
1877
+ runtime.globals.set("_safe_runtime_mount", start.runtimeMount);
1878
+ const filesystem = runtime.FS;
1879
+ const bootstrap = filesystem.root;
1880
+ filesystem.root = null;
1881
+ filesystem.mount(filesystem.filesystems.MEMFS, {}, "/");
1882
+ filesystem.mkdir(start.runtimeMount);
1883
+ filesystem.mount({ mount: () => bootstrap }, {}, start.runtimeMount);
1884
+ runtime.runPython(pythonRuntimeRelocation);
1885
+ filesystem.currentPath = start.invocation.cwd;
1886
+ native = createPythonNativeSyscalls({
1887
+ runtime: runtime._module,
1888
+ cwd: start.invocation.cwd,
1889
+ runtimeMount: start.runtimeMount,
1890
+ maxTransferBytes: start.maxTransferBytes,
1891
+ signal,
1892
+ dispatch: start.dispatch,
1893
+ original: (name, args) => originals[name](...args)
1894
+ });
1895
+ const errno = JSON.parse(runtime.runPython("__import__('json').dumps({name:value for name,value in vars(__import__('errno')).items() if name.startswith('E') and isinstance(value,int)})"));
1896
+ const encode = async (operation) => {
1897
+ try {
1898
+ return JSON.stringify(await operation());
1899
+ } catch (error) {
1900
+ return JSON.stringify({ errno: signal.aborted ? errno.EINTR : error.errno ?? errno[error.code ?? ""] ?? errno.EIO });
1901
+ }
1902
+ };
1903
+ const absolute = (path) => path.startsWith("/") ? path : filesystem.cwd() + "/" + path;
1904
+ const bootstrapPath = (path) => path === start.runtimeMount || path.startsWith(start.runtimeMount + "/");
1905
+ const module = runtime._module;
1906
+ const send = async (pointer) => {
1907
+ let response = await encode(async () => {
1908
+ signal.throwIfAborted();
1909
+ const [operation, path, follow] = JSON.parse(module.UTF8ToString(pointer, 16384));
1910
+ if (operation === "stat") return native.metadata(path, follow);
1911
+ if (typeof path !== "string") throw Object.assign(new Error("Invalid native Python path"), { code: "EINVAL" });
1912
+ const target = absolute(path);
1913
+ if (operation === "directory") {
1914
+ const entries = bootstrapPath(target) ? filesystem.readdir(target).filter((name) => name !== "." && name !== "..") : (await start.dispatch({ op: "readdir", args: [target] })).map((entry) => entry.name);
1915
+ return { entries };
1916
+ }
1917
+ if (operation === "tree") {
1918
+ if (bootstrapPath(target) || !await start.dispatch({ op: "rmtreeSupported", args: [target] })) return { supported: false };
1919
+ try {
1920
+ await start.dispatch({ op: "rmtree", args: [target] });
1921
+ return { supported: true };
1922
+ } catch (error) {
1923
+ return { supported: true, errno: errno[error.code ?? ""] ?? errno.EIO };
1924
+ }
1925
+ }
1926
+ throw Object.assign(new Error("Invalid native Python operation"), { code: "EINVAL" });
1927
+ });
1928
+ if (response.length > 1048576) response = JSON.stringify({ errno: errno.EFBIG });
1929
+ let bytes = new TextEncoder().encode(response);
1930
+ if (bytes.length > 1048576) bytes = new TextEncoder().encode(JSON.stringify({ errno: errno.EFBIG }));
1931
+ const output = module._malloc(bytes.length + 1);
1932
+ if (!output) return 0;
1933
+ module.HEAPU8.set(bytes, output);
1934
+ module.HEAPU8[output + bytes.length] = 0;
1935
+ return output;
1936
+ };
1937
+ const nativeCall = new engine.Instance(options.nativeCall, {
1938
+ python: {
1939
+ utf8: module._PyUnicode_AsUTF8,
1940
+ unicode: module._PyUnicode_FromString,
1941
+ free: module._free,
1942
+ noMemory: module._PyErr_NoMemory
1943
+ },
1944
+ request: { send },
1945
+ shutdown: { send: new engine.Suspending(send) },
1946
+ control: { active, syncify }
1947
+ });
1948
+ methodPointer = module.addFunction(nativeCall.exports.call, "iii");
1949
+ methodDefinition = module._malloc(128);
1950
+ if (!methodDefinition) throw new PythonFailure("runtime-assets");
1951
+ module.HEAPU8.fill(0, methodDefinition, methodDefinition + 128);
1952
+ module.HEAPU8.set(new TextEncoder().encode("request\0_safe_native_fs\0"), methodDefinition + 16);
1953
+ module.HEAPU32.set([methodDefinition + 16, methodPointer, 8, 0], methodDefinition / 4);
1954
+ const extension = module._PyImport_AddModule(methodDefinition + 24);
1955
+ const callable = module._PyCFunction_NewEx(methodDefinition, 0, 0);
1956
+ if (!extension || !callable || module._PyModule_AddObject(extension, methodDefinition + 16, callable) < 0) throw new PythonFailure("runtime-abi");
1957
+ const fields = Number(runtime.runPython("os.stat_result.n_fields"));
1958
+ const statResult = new engine.Instance(options.statResult, { python: {
1959
+ fields: new engine.Global({ value: "i32" }, fields),
1960
+ create: module._PyStructSequence_New,
1961
+ size: module._PyTuple_Size,
1962
+ item: module._PyTuple_GetItem,
1963
+ retain: module._Py_IncRef,
1964
+ set: module._PyStructSequence_SetItem,
1965
+ invalid: module._PyErr_BadArgument
1966
+ } });
1967
+ statPointer = module.addFunction(statResult.exports.construct, "iii");
1968
+ module.HEAPU8.set(new TextEncoder().encode("stat_result\0os\0"), methodDefinition + 80);
1969
+ module.HEAPU32.set([methodDefinition + 80, statPointer, 8, 0], (methodDefinition + 64) / 4);
1970
+ const osModule = module._PyImport_AddModule(methodDefinition + 92);
1971
+ const statType = module._PyObject_GetAttrString(osModule, methodDefinition + 80);
1972
+ const constructStat = module._PyCFunction_NewEx(methodDefinition + 64, statType, 0);
1973
+ module._Py_DecRef(statType);
1974
+ if (!constructStat || module._PyModule_AddObject(extension, methodDefinition + 80, constructStat) < 0) throw new PythonFailure("runtime-abi");
1975
+ runtime.runPython(`
1976
+ from _safe_native_fs import request as _safe_native_request
1977
+ def _safe_stat_projection(path, follow):
1978
+ return _safe_native_request(json.dumps(['stat', path, follow]))
1979
+ def _safe_import_stat(path):
1980
+ return _safe_stat_projection(path, True)
1981
+ def _safe_directory_entries(path):
1982
+ return _safe_native_request(json.dumps(['directory', path]))
1983
+ def _safe_tree_cleanup(path):
1984
+ return _safe_native_request(json.dumps(['tree', path]))
1985
+ `);
1986
+ for (const script of [pythonImportMetadata, pythonDirectoryEntries, pythonStatProjection, pythonTreeCleanup]) runtime.runPython(script);
1987
+ runtime.runPython(`
1988
+ from _safe_native_fs import stat_result as _safe_construct_stat
1989
+ _safe_stat_fields = os.stat_result.n_fields
1990
+ _safe_stat_probe = os.stat_result(tuple(range(_safe_stat_fields)))
1991
+ _safe_stat_indexes = {name: getattr(_safe_stat_probe, name) for name in dir(_safe_stat_probe) if name.startswith('st_')}
1992
+ def _safe_native_stat_type(values, extras):
1993
+ fields = list(values) + [None] * (_safe_stat_fields - len(values))
1994
+ for name, value in extras.items():
1995
+ if name in _safe_stat_indexes:
1996
+ fields[_safe_stat_indexes[name]] = value
1997
+ return _safe_construct_stat(tuple(fields))
1998
+ _safe_stat_type = _safe_native_stat_type
1999
+ `);
2000
+ runtime.globals.set("_safe_invocation_json", JSON.stringify(start.invocation));
2001
+ runtime.globals.set("_safe_execution_code", pythonExecution);
2002
+ runtime.globals.set("_safe_is_cancelled", () => signal.aborted);
2003
+ active.value = 1;
2004
+ start.onReady();
2005
+ exitCode = Number(await runtime.runPythonAsync(`
2006
+ try:
2007
+ exec(_safe_execution_code)
2008
+ except BaseException:
2009
+ if not _safe_is_cancelled():
2010
+ raise
2011
+ _safe_exit = 130
2012
+ _safe_exit
2013
+ `)) & 255;
2014
+ } finally {
2015
+ try {
2016
+ if (qualified) {
2017
+ try {
2018
+ await runtime.runPythonAsync(`
2019
+ import asyncio as _safe_asyncio
2020
+ async def _safe_quiesce_tasks():
2021
+ current = _safe_asyncio.current_task()
2022
+ while True:
2023
+ pending = [task for task in _safe_asyncio.all_tasks() if task is not current]
2024
+ if not pending:
2025
+ break
2026
+ for task in pending:
2027
+ task.cancel()
2028
+ await _safe_asyncio.gather(*pending, return_exceptions=True)
2029
+ loop = _safe_asyncio.get_running_loop()
2030
+ await loop.shutdown_asyncgens()
2031
+ await loop.shutdown_default_executor()
2032
+ await _safe_quiesce_tasks()
2033
+ `);
2034
+ } finally {
2035
+ try {
2036
+ await scheduler.close();
2037
+ } finally {
2038
+ active.value = native ? 2 : 0;
2039
+ finalized = Number(await engine.promising(runtime._module._Py_FinalizeEx)());
2040
+ }
2041
+ }
2042
+ }
2043
+ } finally {
2044
+ active.value = 0;
2045
+ cleanup = scheduler.close().finally(() => native?.close());
2046
+ try {
2047
+ await cleanup;
2048
+ } finally {
2049
+ if (methodDefinition) runtime?._module._free(methodDefinition);
2050
+ if (methodPointer !== void 0) runtime?._module.removeFunction(methodPointer);
2051
+ if (statPointer !== void 0) runtime?._module.removeFunction(statPointer);
2052
+ runtime = void 0;
2053
+ native = void 0;
2054
+ syncify.set(0, null);
2055
+ for (const name of Object.keys(originals)) delete originals[name];
2056
+ }
2057
+ }
2058
+ }
2059
+ signal.throwIfAborted();
2060
+ return finalized < 0 ? 120 : exitCode;
2061
+ };
2062
+ return {
2063
+ run(start) {
2064
+ if (admitted || retirement) return Promise.reject(new PythonFailure("executor-unavailable"));
2065
+ admitted = true;
2066
+ running = execute(start);
2067
+ return running;
2068
+ },
2069
+ terminate() {
2070
+ retirement ??= (async () => {
2071
+ controller.abort();
2072
+ await running?.catch(() => {
2073
+ });
2074
+ await cleanup;
2075
+ })();
2076
+ return retirement;
2077
+ }
2078
+ };
2079
+ }
2080
+
2081
+ // packages/safe-bash/src/commands/python/jspi-assets.ts
2082
+ init_platform();
2083
+ function createPythonJspiAssets(options) {
2084
+ const engine = globalThis.WebAssembly;
2085
+ const main = options.main;
2086
+ const registry = options.modules.map((entry) => ({ module: entry.module, bytes: Uint8Array.from(entry.bytes) }));
2087
+ if (![main, ...registry.map((entry) => entry.module)].every((module) => module instanceof engine.Module)) throw new TypeError("Expected precompiled Python Wasm modules");
2088
+ const stdlib = Uint8Array.from(options.stdlib);
2089
+ const responses = /* @__PURE__ */ new WeakMap();
2090
+ const approved = /* @__PURE__ */ new Set([main, ...registry.map((entry) => entry.module)]);
2091
+ const select = (source) => {
2092
+ const bytes = ArrayBuffer.isView(source) ? new Uint8Array(source.buffer, source.byteOffset, source.byteLength) : new Uint8Array(source);
2093
+ const entry = registry.find((candidate) => candidate.bytes.length === bytes.length && candidate.bytes.every((byte, index) => byte === bytes[index]));
2094
+ if (!entry) throw new engine.CompileError("Unapproved Python runtime Wasm bytes");
2095
+ return entry.module;
2096
+ };
2097
+ const WebAssembly = Object.create(engine);
2098
+ WebAssembly.Module = function(source) {
2099
+ return select(source);
2100
+ };
2101
+ Object.setPrototypeOf(WebAssembly.Module, engine.Module);
2102
+ WebAssembly.Module.prototype = engine.Module.prototype;
2103
+ WebAssembly.compile = async (source) => select(source);
2104
+ WebAssembly.compileStreaming = async () => {
2105
+ throw new engine.CompileError("Unapproved Python streaming compilation");
2106
+ };
2107
+ WebAssembly.instantiate = async (source, imports) => {
2108
+ if (source instanceof engine.Module) {
2109
+ if (!approved.has(source)) throw new engine.CompileError("Unapproved Python Wasm module");
2110
+ return new engine.Instance(source, imports);
2111
+ }
2112
+ const module = select(source);
2113
+ return { module, instance: new engine.Instance(module, imports) };
2114
+ };
2115
+ WebAssembly.instantiateStreaming = async (response, imports) => {
2116
+ const module = responses.get(await response);
2117
+ if (!module) throw new engine.CompileError("Unapproved Python Wasm response");
2118
+ return { module, instance: new engine.Instance(module, imports) };
2119
+ };
2120
+ const location = "https://safe-python.invalid/";
2121
+ return { WebAssembly, location, async fetch(url) {
2122
+ const target = new URL(url, location).href;
2123
+ if (target === location + "python_stdlib.zip") return new Response(stdlib);
2124
+ if (target === location + "pyodide.asm.wasm") {
2125
+ const response = new Response(null, { headers: { "Content-Type": "application/wasm" } });
2126
+ responses.set(response, main);
2127
+ return response;
2128
+ }
2129
+ throw new Error("Unapproved Python runtime asset");
2130
+ } };
2131
+ }
2132
+
1427
2133
  // packages/safe-bash/src/commands/python/index.ts
1428
2134
  var PythonInputChunkError = class extends RangeError {
1429
2135
  };
@@ -1503,7 +2209,8 @@ function createPythonCommands(options) {
1503
2209
  signal.removeEventListener("abort", aborted);
1504
2210
  const subscription = Promise.resolve().then(() => unsubscribe?.());
1505
2211
  const termination = Promise.resolve().then(() => executor ? executor.terminate() : endpoint?.terminate());
1506
- const results = await Promise.allSettled([subscription, termination, service.close(), stdoutOperation?.close(), stderrOperation?.close(), ...pending]);
2212
+ const filesystemRetirement = termination.finally(() => service.close());
2213
+ const results = await Promise.allSettled([subscription, termination, filesystemRetirement, stdoutOperation?.close(), stderrOperation?.close(), ...pending]);
1507
2214
  await input.return?.(void 0);
1508
2215
  fragment = void 0;
1509
2216
  if (packages) environment.finish(packages);
@@ -1544,6 +2251,7 @@ function createPythonCommands(options) {
1544
2251
  }
1545
2252
  };
1546
2253
  const dispatch = async (request) => {
2254
+ if (request.op === "close") return service.dispatch(request);
1547
2255
  signal.throwIfAborted();
1548
2256
  if (request.op.startsWith("package-")) return environment.dispatch(request.op, request.args, { fs: context.fs, cwd: context.cwd, signal });
1549
2257
  if (request.op === "stdin") {
@@ -1620,10 +2328,13 @@ function createPythonCommands(options) {
1620
2328
  }
1621
2329
  },
1622
2330
  async dispatch(request) {
1623
- signal.throwIfAborted();
1624
- if (closed || !running || requesting || !request || typeof request.op !== "string" || !Array.isArray(request.args)) throw reportPythonFailure("transport-unavailable", void 0, options.onDiagnostic);
2331
+ if (!request || typeof request.op !== "string" || !Array.isArray(request.args)) throw reportPythonFailure("transport-unavailable", void 0, options.onDiagnostic);
2332
+ const releasing = request.op === "close";
2333
+ if (!releasing) signal.throwIfAborted();
2334
+ if (closed && !releasing || !running || requesting) throw reportPythonFailure("transport-unavailable", void 0, options.onDiagnostic);
1625
2335
  requesting = true;
1626
2336
  const operation = Promise.resolve().then(() => dispatch(request)).catch(async (error) => {
2337
+ if (releasing) throw error;
1627
2338
  signal.throwIfAborted();
1628
2339
  if (error instanceof PythonInputChunkError) rejectRun?.(error);
1629
2340
  if (request.op.startsWith("package-")) throw Object.assign(reportPythonFailure("runtime-assets", error, options.onDiagnostic), { code: "EPACKAGE" });
@@ -1863,7 +2574,12 @@ export {
1863
2574
  pythonDocumentPackages,
1864
2575
  createPythonPackageEnvironment,
1865
2576
  createPythonExecutorPool,
2577
+ createPythonJspiTrampoline,
2578
+ createPythonJspiNativeCall,
2579
+ createPythonJspiStatResult,
2580
+ createPythonJspiExecutor,
2581
+ createPythonJspiAssets,
1866
2582
  createPythonCommands,
1867
2583
  pythonCommands
1868
2584
  };
1869
- //# sourceMappingURL=chunk-6R7IR2MV.js.map
2585
+ //# sourceMappingURL=chunk-BONY6MX5.js.map