@alisio/core 0.5.1 → 0.6.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.
Files changed (169) hide show
  1. package/dist/analysis/janitor.d.ts +48 -0
  2. package/dist/analysis/janitor.js +170 -18
  3. package/dist/analysis/jobs.d.ts +11 -2
  4. package/dist/analysis/jobs.js +13 -0
  5. package/dist/analysis/oci.d.ts +11 -2
  6. package/dist/analysis/oci.js +20 -4
  7. package/dist/analysis/python/sources.js +2 -1
  8. package/dist/analysis/rerun.d.ts +1 -1
  9. package/dist/analysis/status.d.ts +3 -1
  10. package/dist/analysis/status.js +3 -0
  11. package/dist/application.d.ts +38 -2
  12. package/dist/application.js +213 -4
  13. package/dist/changelog/data.js +64 -0
  14. package/dist/config.d.ts +110 -2
  15. package/dist/config.js +188 -6
  16. package/dist/core/call-internals.d.ts +22 -0
  17. package/dist/core/call-internals.js +9 -0
  18. package/dist/core/contracts.d.ts +2 -2
  19. package/dist/core/runner.js +111 -38
  20. package/dist/forge/broker.d.ts +51 -0
  21. package/dist/forge/broker.js +244 -0
  22. package/dist/forge/budget.d.ts +27 -0
  23. package/dist/forge/budget.js +60 -0
  24. package/dist/forge/catalog-view.d.ts +24 -0
  25. package/dist/forge/catalog-view.js +43 -0
  26. package/dist/forge/errors.d.ts +26 -0
  27. package/dist/forge/errors.js +49 -0
  28. package/dist/forge/guides/javascript.d.ts +25 -0
  29. package/dist/forge/guides/javascript.js +90 -0
  30. package/dist/forge/guides/provider.d.ts +42 -0
  31. package/dist/forge/guides/provider.js +96 -0
  32. package/dist/forge/guides/python-environment.d.ts +15 -0
  33. package/dist/forge/guides/python-environment.js +81 -0
  34. package/dist/forge/guides/python.d.ts +38 -0
  35. package/dist/forge/guides/python.js +170 -0
  36. package/dist/forge/javascript/engine.d.ts +44 -0
  37. package/dist/forge/javascript/engine.js +229 -0
  38. package/dist/forge/javascript/hosts/factory.d.ts +25 -0
  39. package/dist/forge/javascript/hosts/factory.js +21 -0
  40. package/dist/forge/javascript/hosts/framing.d.ts +17 -0
  41. package/dist/forge/javascript/hosts/framing.js +49 -0
  42. package/dist/forge/javascript/hosts/isolate-main.d.ts +9 -0
  43. package/dist/forge/javascript/hosts/isolate-main.js +63 -0
  44. package/dist/forge/javascript/hosts/process-entry.d.ts +1 -0
  45. package/dist/forge/javascript/hosts/process-entry.js +3 -0
  46. package/dist/forge/javascript/hosts/process-host.d.ts +33 -0
  47. package/dist/forge/javascript/hosts/process-host.js +160 -0
  48. package/dist/forge/javascript/hosts/worker-entry.d.ts +1 -0
  49. package/dist/forge/javascript/hosts/worker-entry.js +3 -0
  50. package/dist/forge/javascript/hosts/worker-host.d.ts +18 -0
  51. package/dist/forge/javascript/hosts/worker-host.js +101 -0
  52. package/dist/forge/javascript/prelude.d.ts +2 -0
  53. package/dist/forge/javascript/prelude.js +333 -0
  54. package/dist/forge/javascript/probe.d.ts +2 -0
  55. package/dist/forge/javascript/probe.js +53 -0
  56. package/dist/forge/javascript/runtime-manifest.d.ts +14 -0
  57. package/dist/forge/javascript/runtime-manifest.js +14 -0
  58. package/dist/forge/javascript/vm-runner.d.ts +21 -0
  59. package/dist/forge/javascript/vm-runner.js +301 -0
  60. package/dist/forge/javascript/wasm-asset.d.ts +8 -0
  61. package/dist/forge/javascript/wasm-asset.js +8 -0
  62. package/dist/forge/javascript/wasm.d.ts +15 -0
  63. package/dist/forge/javascript/wasm.js +54 -0
  64. package/dist/forge/journal.d.ts +105 -0
  65. package/dist/forge/journal.js +174 -0
  66. package/dist/forge/kernel.d.ts +76 -0
  67. package/dist/forge/kernel.js +338 -0
  68. package/dist/forge/limits.d.ts +38 -0
  69. package/dist/forge/limits.js +73 -0
  70. package/dist/forge/nested.d.ts +96 -0
  71. package/dist/forge/nested.js +214 -0
  72. package/dist/forge/ports.d.ts +120 -0
  73. package/dist/forge/ports.js +1 -0
  74. package/dist/forge/progress.d.ts +24 -0
  75. package/dist/forge/progress.js +76 -0
  76. package/dist/forge/protocol.d.ts +110 -0
  77. package/dist/forge/protocol.js +171 -0
  78. package/dist/forge/python/backend-selector.d.ts +38 -0
  79. package/dist/forge/python/backend-selector.js +111 -0
  80. package/dist/forge/python/backend.d.ts +89 -0
  81. package/dist/forge/python/backend.js +1 -0
  82. package/dist/forge/python/engine.d.ts +84 -0
  83. package/dist/forge/python/engine.js +279 -0
  84. package/dist/forge/python/frame-parser.d.ts +24 -0
  85. package/dist/forge/python/frame-parser.js +107 -0
  86. package/dist/forge/python/frame-transport.d.ts +40 -0
  87. package/dist/forge/python/frame-transport.js +101 -0
  88. package/dist/forge/python/import-scan.d.ts +22 -0
  89. package/dist/forge/python/import-scan.js +133 -0
  90. package/dist/forge/python/managed-backend.d.ts +30 -0
  91. package/dist/forge/python/managed-backend.js +111 -0
  92. package/dist/forge/python/oci-backend.d.ts +27 -0
  93. package/dist/forge/python/oci-backend.js +84 -0
  94. package/dist/forge/python/pyodide-backend.d.ts +40 -0
  95. package/dist/forge/python/pyodide-backend.js +289 -0
  96. package/dist/forge/python/pyodide-install.d.ts +41 -0
  97. package/dist/forge/python/pyodide-install.js +167 -0
  98. package/dist/forge/python/pyodide-manifest.d.ts +42 -0
  99. package/dist/forge/python/pyodide-manifest.js +376 -0
  100. package/dist/forge/python/pyodide-runner-source.d.ts +2 -0
  101. package/dist/forge/python/pyodide-runner-source.js +3 -0
  102. package/dist/forge/python/rss.d.ts +2 -0
  103. package/dist/forge/python/rss.js +33 -0
  104. package/dist/forge/python/scratch-copy.d.ts +6 -0
  105. package/dist/forge/python/scratch-copy.js +61 -0
  106. package/dist/forge/python/settle.d.ts +6 -0
  107. package/dist/forge/python/settle.js +21 -0
  108. package/dist/forge/python/status.d.ts +8 -0
  109. package/dist/forge/python/status.js +15 -0
  110. package/dist/forge/python/wasm-node.d.ts +59 -0
  111. package/dist/forge/python/wasm-node.js +220 -0
  112. package/dist/forge/python/wiring.d.ts +39 -0
  113. package/dist/forge/python/wiring.js +47 -0
  114. package/dist/forge/registry.d.ts +26 -0
  115. package/dist/forge/registry.js +83 -0
  116. package/dist/forge/result.d.ts +58 -0
  117. package/dist/forge/result.js +86 -0
  118. package/dist/forge/routing-rule.d.ts +19 -0
  119. package/dist/forge/routing-rule.js +31 -0
  120. package/dist/forge/runtime.d.ts +64 -0
  121. package/dist/forge/runtime.js +88 -0
  122. package/dist/forge/scratch-files.d.ts +36 -0
  123. package/dist/forge/scratch-files.js +94 -0
  124. package/dist/forge/scratch-provider.d.ts +41 -0
  125. package/dist/forge/scratch-provider.js +79 -0
  126. package/dist/forge/scratch-quota.d.ts +8 -0
  127. package/dist/forge/scratch-quota.js +25 -0
  128. package/dist/forge/selector.d.ts +35 -0
  129. package/dist/forge/selector.js +51 -0
  130. package/dist/forge/session-dir.d.ts +59 -0
  131. package/dist/forge/session-dir.js +239 -0
  132. package/dist/forge/session-path.d.ts +31 -0
  133. package/dist/forge/session-path.js +61 -0
  134. package/dist/forge/slots.d.ts +16 -0
  135. package/dist/forge/slots.js +47 -0
  136. package/dist/forge/state-delta.d.ts +54 -0
  137. package/dist/forge/state-delta.js +99 -0
  138. package/dist/forge/state-machine.d.ts +20 -0
  139. package/dist/forge/state-machine.js +40 -0
  140. package/dist/forge/state-store.d.ts +25 -0
  141. package/dist/forge/state-store.js +50 -0
  142. package/dist/forge/task-log.d.ts +9 -0
  143. package/dist/forge/task-log.js +42 -0
  144. package/dist/forge/verdict.d.ts +20 -0
  145. package/dist/forge/verdict.js +28 -0
  146. package/dist/forge-isolate.d.ts +6 -0
  147. package/dist/forge-isolate.js +6 -0
  148. package/dist/index.d.ts +7 -0
  149. package/dist/index.js +7 -0
  150. package/dist/permissions/decide.d.ts +42 -0
  151. package/dist/permissions/decide.js +24 -0
  152. package/dist/resources/context.d.ts +2 -1
  153. package/dist/resources/context.js +2 -1
  154. package/dist/runtime/access.d.ts +34 -0
  155. package/dist/runtime/access.js +80 -0
  156. package/dist/runtime/process.d.ts +2 -1
  157. package/dist/runtime/process.js +5 -5
  158. package/dist/runtime/store.d.ts +11 -0
  159. package/dist/runtime/store.js +103 -0
  160. package/dist/tools/analysis.d.ts +25 -2
  161. package/dist/tools/analysis.js +315 -267
  162. package/dist/tools/execute.js +6 -7
  163. package/dist/tools/forge-guide.d.ts +6 -0
  164. package/dist/tools/forge-guide.js +60 -0
  165. package/dist/tools/js-run.d.ts +19 -0
  166. package/dist/tools/js-run.js +126 -0
  167. package/dist/tools/standard.d.ts +7 -1
  168. package/dist/tools/standard.js +44 -43
  169. package/package.json +7 -2
@@ -0,0 +1,376 @@
1
+ /**
2
+ * Pinned Pyodide distribution for the `wasm` Python backend (Python spec §8.1, PD7). A TypeScript
3
+ * constant, not JSON, because `tsc` does not copy JSON into `dist`. Every file carries its
4
+ * SHA-256 and size; `alisio analysis setup --wasm` is the only code that fetches them.
5
+ *
6
+ * - `pyodide` files come from the official jsDelivr mirror of the 314.0.7 release; the hashes of
7
+ * the wheels are the ones of `pyodide-lock.json`, and the core files were checked against the
8
+ * mirror (Phase P0, 2026-10-12).
9
+ * - `pypi` files are pure wheels that the Pyodide lock does not ship; their hashes are the ones
10
+ * PyPI publishes. A raw wheel URL does not resolve dependencies, so `depends` lists them.
11
+ * - `sqlite3` is part of the standard library build and is deliberately not listed.
12
+ */
13
+ export const PYODIDE_VERSION = "314.0.7";
14
+ export const PYTHON_VERSION = "3.14.2";
15
+ /** Files every profile needs (the runtime itself). */
16
+ export const PYODIDE_CORE_FILES = [
17
+ {
18
+ name: "pyodide.mjs",
19
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pyodide.mjs",
20
+ sha256: "6f1d60f7bf529beb300f0f47983c921d3982363640ba20af0e38efdddbc66109",
21
+ size: 17931,
22
+ source: "pyodide",
23
+ },
24
+ {
25
+ name: "pyodide.asm.mjs",
26
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pyodide.asm.mjs",
27
+ sha256: "f7cdc8ece80678ceb712f8e65ebe6d3a83203a180c399865f49612a051693635",
28
+ size: 1250344,
29
+ source: "pyodide",
30
+ },
31
+ {
32
+ name: "pyodide.asm.wasm",
33
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pyodide.asm.wasm",
34
+ sha256: "cc36e3cab04fdfc9a63ff13eb52eae2b911bf46c025cc7b281f394bd3de1d5e6",
35
+ size: 9598218,
36
+ source: "pyodide",
37
+ },
38
+ {
39
+ name: "python_stdlib.zip",
40
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/python_stdlib.zip",
41
+ sha256: "fa1957e5777068fc4f7437f96d860ae2fbe9c19732ba06c84e004ec16dd7dd7a",
42
+ size: 2545637,
43
+ source: "pyodide",
44
+ },
45
+ {
46
+ name: "pyodide-lock.json",
47
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pyodide-lock.json",
48
+ sha256: "5dc2fc119108bc148c7457dc86e7675b5c87e1cafd420b9c34c1eaef7b36c010",
49
+ size: 119077,
50
+ source: "pyodide",
51
+ },
52
+ ];
53
+ /** Wheels by manifest key (the distribution name, normalized with dashes). */
54
+ export const PYODIDE_PACKAGES = {
55
+ numpy: {
56
+ name: "numpy-2.4.6-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
57
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/numpy-2.4.6-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
58
+ sha256: "a292c1f5d7d8a2208cd5e94fc467604c131cabcd2fc14fed6eefde121e7fabdf",
59
+ size: 2960568,
60
+ source: "pyodide",
61
+ depends: [],
62
+ },
63
+ pandas: {
64
+ name: "pandas-3.0.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
65
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pandas-3.0.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
66
+ sha256: "45ff57772cc2f366a8582c7d3097cc4e6676342ecd5a3c08184a43462d5a02ae",
67
+ size: 4177749,
68
+ source: "pyodide",
69
+ depends: ["numpy", "python-dateutil", "pytz"],
70
+ },
71
+ "python-dateutil": {
72
+ name: "python_dateutil-2.9.0.post0-py2.py3-none-any.whl",
73
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/python_dateutil-2.9.0.post0-py2.py3-none-any.whl",
74
+ sha256: "9b13365edf9c188f570baf9c540bbb3029ada2a2dacb9694b3659941693ee9e5",
75
+ size: 229892,
76
+ source: "pyodide",
77
+ depends: ["six"],
78
+ },
79
+ pytz: {
80
+ name: "pytz-2026.1.post1-py2.py3-none-any.whl",
81
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pytz-2026.1.post1-py2.py3-none-any.whl",
82
+ sha256: "b8249d6450146e0b61e6d710dc02ebb35a904796c4c2f97fe87d4ac5872db36a",
83
+ size: 510489,
84
+ source: "pyodide",
85
+ depends: [],
86
+ },
87
+ six: {
88
+ name: "six-1.17.0-py2.py3-none-any.whl",
89
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/six-1.17.0-py2.py3-none-any.whl",
90
+ sha256: "228c50f73aa7addf2c2ccf2979c256802a59ab69cad8152b31b9443cc8140f42",
91
+ size: 11050,
92
+ source: "pyodide",
93
+ depends: [],
94
+ },
95
+ matplotlib: {
96
+ name: "matplotlib-3.10.8-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
97
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/matplotlib-3.10.8-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
98
+ sha256: "722857932f8f62eac64f8439af15c94d427f9fb945f9590cd242805c983d2d54",
99
+ size: 6982033,
100
+ source: "pyodide",
101
+ depends: [
102
+ "contourpy",
103
+ "cycler",
104
+ "fonttools",
105
+ "kiwisolver",
106
+ "numpy",
107
+ "packaging",
108
+ "pillow",
109
+ "pyparsing",
110
+ "python-dateutil",
111
+ "pytz",
112
+ ],
113
+ },
114
+ contourpy: {
115
+ name: "contourpy-1.3.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
116
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/contourpy-1.3.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
117
+ sha256: "0ac15ebf9f820d2d1c3526388aa2818b631cb6c4aae1682efd6b4cc12c1f302c",
118
+ size: 118874,
119
+ source: "pyodide",
120
+ depends: ["numpy"],
121
+ },
122
+ cycler: {
123
+ name: "cycler-0.12.1-py3-none-any.whl",
124
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/cycler-0.12.1-py3-none-any.whl",
125
+ sha256: "8ee450085f15b47f78b034d60059af6a3649e98fa5dde073363437742cc0b4ea",
126
+ size: 8321,
127
+ source: "pyodide",
128
+ depends: ["six"],
129
+ },
130
+ fonttools: {
131
+ name: "fonttools-4.62.1-py3-none-any.whl",
132
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/fonttools-4.62.1-py3-none-any.whl",
133
+ sha256: "0d1516e073fd0a8d8e6d9af46417b6a26209a42518024a18f7085e72d2599605",
134
+ size: 1152647,
135
+ source: "pyodide",
136
+ depends: [],
137
+ },
138
+ kiwisolver: {
139
+ name: "kiwisolver-1.5.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
140
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/kiwisolver-1.5.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
141
+ sha256: "47781998156721147c128a3c74547d5703e0e3cc454d8203e629339368308a93",
142
+ size: 36616,
143
+ source: "pyodide",
144
+ depends: [],
145
+ },
146
+ packaging: {
147
+ name: "packaging-26.1-py3-none-any.whl",
148
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/packaging-26.1-py3-none-any.whl",
149
+ sha256: "565acbbea54da30348b6d68b6a54373e6f777987db9f1db6966b4b3a5060d303",
150
+ size: 95852,
151
+ source: "pyodide",
152
+ depends: [],
153
+ },
154
+ pillow: {
155
+ name: "pillow-12.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
156
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pillow-12.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
157
+ sha256: "e29838b7a756e4ee0f27a9cfa9a387ee0dfa2e9dd44be2dd595130b9f9d93ac3",
158
+ size: 1037806,
159
+ source: "pyodide",
160
+ depends: [],
161
+ },
162
+ pyparsing: {
163
+ name: "pyparsing-3.3.2-py3-none-any.whl",
164
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pyparsing-3.3.2-py3-none-any.whl",
165
+ sha256: "f0dd8225b5f8e945980b400bd465b4b90cb0498a2eede0d9dbea98372f6110c5",
166
+ size: 122781,
167
+ source: "pyodide",
168
+ depends: [],
169
+ },
170
+ jinja2: {
171
+ name: "jinja2-3.1.6-py3-none-any.whl",
172
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/jinja2-3.1.6-py3-none-any.whl",
173
+ sha256: "9330ec9cf2241fb8fc352928ea5e72016bf82de1d3d3b71ffea52a093a3ebab8",
174
+ size: 134899,
175
+ source: "pyodide",
176
+ depends: ["markupsafe"],
177
+ },
178
+ markupsafe: {
179
+ name: "markupsafe-3.0.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
180
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/markupsafe-3.0.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
181
+ sha256: "b44c364dc0d19e4210ce68ab77833c2b5d608f78fe6966263add4fc1fa667d97",
182
+ size: 9986,
183
+ source: "pyodide",
184
+ depends: [],
185
+ },
186
+ lxml: {
187
+ name: "lxml-6.1.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
188
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/lxml-6.1.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
189
+ sha256: "6a0b98f57a538cb6af4868c3499efec2bb52f7ed59fed94b7fbf195d6e40281f",
190
+ size: 2145850,
191
+ source: "pyodide",
192
+ depends: [],
193
+ },
194
+ "typing-extensions": {
195
+ name: "typing_extensions-4.15.0-py3-none-any.whl",
196
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/typing_extensions-4.15.0-py3-none-any.whl",
197
+ sha256: "4fec68770a05408e668a4fa53d6bf4f0f684b9dacf820c7c5d6e97d3ad225e29",
198
+ size: 44613,
199
+ source: "pyodide",
200
+ depends: [],
201
+ },
202
+ scipy: {
203
+ name: "scipy-1.18.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
204
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/scipy-1.18.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
205
+ sha256: "17ee329a957863516d1bb6a6aaa0c60576fac027e9cd3d43de27f58b5b599b50",
206
+ size: 14029750,
207
+ source: "pyodide",
208
+ depends: ["numpy"],
209
+ },
210
+ patsy: {
211
+ name: "patsy-1.0.2-py2.py3-none-any.whl",
212
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/patsy-1.0.2-py2.py3-none-any.whl",
213
+ sha256: "64e931013dc7c23d2661327531b04c4370605cd439cdee4bb1a55846c5f41771",
214
+ size: 121050,
215
+ source: "pyodide",
216
+ depends: ["numpy", "six"],
217
+ },
218
+ statsmodels: {
219
+ name: "statsmodels-0.14.6-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
220
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/statsmodels-0.14.6-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
221
+ sha256: "8ee50091810f8ac540a4b94eeba777028cfe8200a78bc5367ea01ad2d6a3a951",
222
+ size: 8162663,
223
+ source: "pyodide",
224
+ depends: ["numpy", "scipy", "pandas", "patsy", "packaging"],
225
+ },
226
+ joblib: {
227
+ name: "joblib-1.5.3-py3-none-any.whl",
228
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/joblib-1.5.3-py3-none-any.whl",
229
+ sha256: "fb2860ff70d0b6b43a3df5e3ce52d3a938ad2fcacd80a3f2f3f37e930843e607",
230
+ size: 180317,
231
+ source: "pyodide",
232
+ depends: [],
233
+ },
234
+ threadpoolctl: {
235
+ name: "threadpoolctl-3.6.0-py3-none-any.whl",
236
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/threadpoolctl-3.6.0-py3-none-any.whl",
237
+ sha256: "3521783ffb6bf212e03fb67ec1955af206d6ba7ea09ac21f200875077bbbe7c2",
238
+ size: 18638,
239
+ source: "pyodide",
240
+ depends: [],
241
+ },
242
+ "scikit-learn": {
243
+ name: "scikit_learn-1.8.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
244
+ url: "https://cdn.jsdelivr.net/pyodide/v314.0.7/full/scikit_learn-1.8.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
245
+ sha256: "432f560f54c5283d13e9d7c11dddc8bdf2f15aec326cc1d7314f600786154181",
246
+ size: 4449204,
247
+ source: "pyodide",
248
+ depends: ["scipy", "joblib", "threadpoolctl"],
249
+ },
250
+ openpyxl: {
251
+ name: "openpyxl-3.1.5-py2.py3-none-any.whl",
252
+ url: "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl",
253
+ sha256: "5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2",
254
+ size: 250910,
255
+ source: "pypi",
256
+ depends: ["et-xmlfile"],
257
+ },
258
+ "et-xmlfile": {
259
+ name: "et_xmlfile-2.0.0-py3-none-any.whl",
260
+ url: "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl",
261
+ sha256: "7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa",
262
+ size: 18059,
263
+ source: "pypi",
264
+ depends: [],
265
+ },
266
+ "python-docx": {
267
+ name: "python_docx-1.2.0-py3-none-any.whl",
268
+ url: "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl",
269
+ sha256: "3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7",
270
+ size: 252987,
271
+ source: "pypi",
272
+ depends: ["lxml", "typing-extensions"],
273
+ },
274
+ narwhals: {
275
+ name: "narwhals-2.27.1-py3-none-any.whl",
276
+ url: "https://files.pythonhosted.org/packages/d1/89/5d4c86da1130d9059681e5b6cd7645df5c10279a6a079c5c37dcb2cc6f3f/narwhals-2.27.1-py3-none-any.whl",
277
+ sha256: "d057df13f5852b8e157596e82eb5e955fad267425df5e420e0ee9863da483b31",
278
+ size: 483211,
279
+ source: "pypi",
280
+ depends: [],
281
+ },
282
+ plotly: {
283
+ name: "plotly-7.1.0-py3-none-any.whl",
284
+ url: "https://files.pythonhosted.org/packages/e5/7d/905a3a3d51087515719058c94cfbda2ff0fc14417c20d557ae3e82d8b250/plotly-7.1.0-py3-none-any.whl",
285
+ sha256: "dbb7fa18afce40d0a8e80d1bf162eceb3faa0ce5a77fe741ad09a74cf78f53f3",
286
+ size: 9692368,
287
+ source: "pypi",
288
+ depends: ["narwhals", "packaging"],
289
+ },
290
+ "charset-normalizer": {
291
+ name: "charset_normalizer-3.5.2-py3-none-any.whl",
292
+ url: "https://files.pythonhosted.org/packages/fc/ad/d07d7862a62ffa6d79d68074d14823243dd235a77c45262acbf6adeb28bf/charset_normalizer-3.5.2-py3-none-any.whl",
293
+ sha256: "b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685",
294
+ size: 68872,
295
+ source: "pypi",
296
+ depends: [],
297
+ },
298
+ reportlab: {
299
+ name: "reportlab-5.0.1-py3-none-any.whl",
300
+ url: "https://files.pythonhosted.org/packages/db/cb/dacbc268cb68d0428ea2cbd85266195a9ab3e677449589ddae59bd7542ac/reportlab-5.0.1-py3-none-any.whl",
301
+ sha256: "1c36e6bb0e71780c72331eba60da7f602e8d4389a8723825af71342e49d791e8",
302
+ size: 1957258,
303
+ source: "pypi",
304
+ depends: ["pillow", "charset-normalizer"],
305
+ },
306
+ };
307
+ /** Explicit dependency closure per profile, in load order. `science` includes `analysis`. */
308
+ export const PYODIDE_PROFILES = {
309
+ base: [],
310
+ analysis: [
311
+ "numpy",
312
+ "six",
313
+ "python-dateutil",
314
+ "pytz",
315
+ "pandas",
316
+ "contourpy",
317
+ "cycler",
318
+ "fonttools",
319
+ "kiwisolver",
320
+ "packaging",
321
+ "pillow",
322
+ "pyparsing",
323
+ "matplotlib",
324
+ "markupsafe",
325
+ "jinja2",
326
+ "et-xmlfile",
327
+ "openpyxl",
328
+ "lxml",
329
+ "typing-extensions",
330
+ "python-docx",
331
+ "narwhals",
332
+ "plotly",
333
+ "charset-normalizer",
334
+ "reportlab",
335
+ ],
336
+ science: [
337
+ "numpy",
338
+ "six",
339
+ "python-dateutil",
340
+ "pytz",
341
+ "pandas",
342
+ "contourpy",
343
+ "cycler",
344
+ "fonttools",
345
+ "kiwisolver",
346
+ "packaging",
347
+ "pillow",
348
+ "pyparsing",
349
+ "matplotlib",
350
+ "markupsafe",
351
+ "jinja2",
352
+ "et-xmlfile",
353
+ "openpyxl",
354
+ "lxml",
355
+ "typing-extensions",
356
+ "python-docx",
357
+ "narwhals",
358
+ "plotly",
359
+ "charset-normalizer",
360
+ "reportlab",
361
+ "scipy",
362
+ "patsy",
363
+ "statsmodels",
364
+ "joblib",
365
+ "threadpoolctl",
366
+ "scikit-learn",
367
+ ],
368
+ };
369
+ export const PYODIDE_MANIFEST = {
370
+ version: PYODIDE_VERSION,
371
+ python: PYTHON_VERSION,
372
+ core: PYODIDE_CORE_FILES,
373
+ packages: PYODIDE_PACKAGES,
374
+ profiles: PYODIDE_PROFILES,
375
+ };
376
+ export const PYODIDE_PROFILE_ORDER = ["base", "analysis", "science"];
@@ -0,0 +1,2 @@
1
+ /** The plain-JavaScript runner executed by the child `node` of the wasm Python backend. */
2
+ export declare const PYODIDE_RUNNER_SOURCE: string;
@@ -0,0 +1,3 @@
1
+ // Generated by scripts/pyodide-runner-source.ts from pyodide-runner.mjs. Do not edit.
2
+ /** The plain-JavaScript runner executed by the child `node` of the wasm Python backend. */
3
+ export const PYODIDE_RUNNER_SOURCE = '// Pyodide runner of the `wasm` Python backend (Python spec §8.2). Plain JavaScript on purpose: it\n// runs in a child `node` started with --permission, outside the Alisio bundle, and ships inside\n// `pyodide-runner-source.ts` (generated by scripts/pyodide-runner-source.ts) because the npm\n// package and the standalone binary carry no loose files.\n//\n// argv[2] is one JSON object: { dist, wheelsDir?, packages, wheels, job: { script, input, work,\n// out, scratch? } }. Environment: ALISIO_EXECUTION_ID, ALISIO_FORGE_NONCE, ALISIO_FORGE_ENGINE.\n// Exit code: the script\'s own (0 = ok, 1 = it raised), 70 when the runner itself failed.\n// The runner holds no secret: Python reaches this JavaScript through the FFI (verified in P0), so\n// everything it can do is bounded by the Permission Model flags the parent chose.\nimport fs from "node:fs";\nimport path from "node:path";\nimport { pathToFileURL } from "node:url";\n\nconst spec = JSON.parse(process.argv[2] ?? "{}");\nconst SCRATCH_CAP_BYTES = 64 * 1024 * 1024;\nconst started = performance.now();\n\n// 1. No network entry points for the script. Not a boundary (node:http is still reachable through\n// the FFI on Node 22/24); it only closes the obvious doors and keeps Pyodide on node:fs.\nfor (const name of ["fetch", "WebSocket", "XMLHttpRequest", "EventSource", "navigator"]) {\n try {\n delete globalThis[name];\n } catch {\n /* non-configurable in this runtime */\n }\n}\n\n// 2. Emscripten\'s NODEFS calls process.binding("constants"), which --permission denies.\nif (process.permission) {\n process.binding = (name) => {\n if (name === "constants") return { fs: fs.constants };\n throw new Error(`process.binding(${name}) is disabled`);\n };\n}\n\n// 3. process.kill could end the parent or any process of the user; replaced and frozen (D-32).\n// A mitigation, not a boundary: the OS still lets the child signal its own group.\nconst denied = () => {\n throw new Error("process.kill is disabled in the Alisio wasm runner");\n};\nfor (const name of ["kill", "_kill"]) {\n try {\n Object.defineProperty(process, name, {\n value: denied,\n writable: false,\n configurable: false,\n enumerable: true,\n });\n } catch {\n /* already frozen */\n }\n}\n\n// What the runner reports reaches the model: host absolute paths never do. The folders it was\n// given become placeholders; any other absolute path keeps only its last segment.\nconst KNOWN_PATHS = [\n [spec.dist, "<dist>"],\n [spec.job?.script, "<job>/script"],\n [spec.job?.input, "<job>/input"],\n [spec.job?.work, "<job>/work"],\n [spec.job?.out, "<job>/out"],\n [spec.job?.scratch, "<job>/scratch"],\n]\n .filter(([host]) => typeof host === "string" && host.length > 1)\n .sort((a, b) => b[0].length - a[0].length);\nconst redact = (text) => {\n let result = String(text);\n for (const [host, label] of KNOWN_PATHS) {\n result = result.split(pathToFileURL(host).href).join(label).split(host).join(label);\n }\n return result.replace(\n /(?<![\\w:.<>~-])(?:file:\\/\\/)?(?:[A-Za-z]:)?[\\\\/](?:[^\\s:()\'"\\\\/]+[\\\\/])+([^\\s:()\'"\\\\/]+)/g,\n "<host>/$1",\n );\n};\n\nconst fail = (message, code = 70) => {\n process.stderr.write(`alisio wasm runner: ${redact(message)}\\n`, () => process.exit(code));\n};\n\n// An error thrown into a Node event handler (a Python callback that raised, say) would otherwise\n// end the process with Node\'s report, which quotes the Emscripten source: keep it to one line.\nconst brief = (error) =>\n redact(String(error instanceof Error ? error.message : error).split("\\n")[0]);\nprocess.on("uncaughtException", (error) => fail(`uncaught exception: ${brief(error)}`));\nprocess.on("unhandledRejection", (error) => fail(`unhandled rejection: ${brief(error)}`));\n\nconst writeLine = (stream) => (line) => {\n stream.write(`${line}\\n`);\n};\n\ntry {\n const { loadPyodide } = await import(pathToFileURL(path.join(spec.dist, "pyodide.mjs")).href);\n const py = await loadPyodide({\n indexURL: `${spec.dist}/`,\n lockFileURL: path.join(spec.dist, "pyodide-lock.json"),\n packageCacheDir: `${spec.dist}/`,\n env: {\n ALISIO_INPUT_DIR: "/job/input",\n ALISIO_OUTPUT_DIR: "/job/out",\n ALISIO_WORK_DIR: "/job/work",\n ALISIO_EXECUTION_ID: process.env.ALISIO_EXECUTION_ID ?? "",\n ALISIO_FORGE_NONCE: process.env.ALISIO_FORGE_NONCE ?? "",\n ALISIO_FORGE_ENGINE: process.env.ALISIO_FORGE_ENGINE ?? "python-wasm",\n ...(spec.job.scratch ? { ALISIO_SCRATCH_DIR: "/job/scratch" } : {}),\n MPLBACKEND: "Agg",\n HOME: "/job/work",\n TMPDIR: "/job/work",\n PYTHONDONTWRITEBYTECODE: "1",\n },\n stdout: writeLine(process.stdout),\n stderr: writeLine(process.stderr),\n });\n\n // 4. Only what the script needs: lock packages by name (their dependencies resolve from the\n // lock), then the pure wheels from the local folder, in the manifest\'s dependency order.\n const messages = (m) => process.stderr.write(`${m}\\n`);\n if (spec.packages.length)\n await py.loadPackage(spec.packages, { messageCallback: () => {}, errorCallback: messages });\n for (const wheel of spec.wheels)\n await py.loadPackage(pathToFileURL(path.join(spec.dist, wheel)).href, {\n messageCallback: () => {},\n errorCallback: messages,\n });\n\n // 5. The job lives in Pyodide\'s own file system; only these folders are read from the host.\n const copyIn = (hostDir, vDir, budget) => {\n py.FS.mkdirTree(vDir);\n let used = 0;\n const walk = (host, virtual) => {\n for (const entry of fs.readdirSync(host, { withFileTypes: true })) {\n const from = path.join(host, entry.name);\n const to = `${virtual}/${entry.name}`;\n if (entry.isDirectory()) {\n py.FS.mkdirTree(to);\n walk(from, to);\n } else if (entry.isFile()) {\n const size = fs.statSync(from).size;\n if (budget !== undefined && used + size > budget) continue;\n used += size;\n py.FS.writeFile(to, fs.readFileSync(from));\n }\n }\n };\n walk(hostDir, vDir);\n };\n for (const dir of ["/job", "/job/out"]) py.FS.mkdirTree(dir);\n copyIn(spec.job.script, "/job/script");\n copyIn(spec.job.input, "/job/input");\n copyIn(spec.job.work, "/job/work");\n if (spec.job.scratch) copyIn(spec.job.scratch, "/job/scratch", SCRATCH_CAP_BYTES);\n py.FS.chdir("/job/work");\n\n // The parent times the start from this file (not from a line, which would end up in the\n // script\'s output and in the logs). Python can write it too; that only skews a metric.\n fs.mkdirSync(path.join(spec.job.work, ".forge"), { recursive: true });\n fs.writeFileSync(\n path.join(spec.job.work, ".forge", "wasm-ready"),\n String(Math.round(performance.now() - started)),\n );\n\n // 6. Run main.py as __main__ in a real module (dataclasses and pickling look it up in\n // sys.modules), with top-level await allowed; the exit code follows the script (SystemExit\n // included). Pyodide never finalizes the interpreter, so the atexit handlers (Forja\'s final\n // value) run here, after the script and also after a failure, as CPython would.\n py.globals.set("_alisio_main", "/job/script/main.py");\n const code = await py.runPythonAsync(`\nimport atexit, sys, traceback, types\nfrom pyodide.code import eval_code_async\n\nasync def _alisio_run(path):\n sys.argv = [path]\n sys.path.insert(0, "/job/script")\n module = types.ModuleType("__main__")\n module.__file__ = path\n sys.modules["__main__"] = module\n try:\n with open(path, encoding="utf-8") as handle:\n source = handle.read()\n await eval_code_async(source, module.__dict__, filename=path, return_mode="none")\n return 0\n except SystemExit as exit_:\n value = exit_.code\n if value is None:\n return 0\n if isinstance(value, int):\n return value & 255\n print(value, file=sys.stderr)\n return 1\n except BaseException as error:\n tb = error.__traceback__\n while tb is not None and tb.tb_frame.f_code.co_filename != path:\n tb = tb.tb_next\n traceback.print_exception(type(error), error, tb if tb is not None else error.__traceback__)\n return 1\n finally:\n try:\n atexit._run_exitfuncs()\n sys.stdout.flush()\n sys.stderr.flush()\n except BaseException:\n traceback.print_exc()\n\nawait _alisio_run(_alisio_main)\n`);\n\n // 7. Whatever the script left in /job/out goes to the real out folder (symlinks are not followed).\n const copyOut = (virtual, host) => {\n for (const name of py.FS.readdir(virtual)) {\n if (name === "." || name === "..") continue;\n const from = `${virtual}/${name}`;\n const to = path.join(host, name);\n const info = py.FS.lstat(from);\n if (py.FS.isDir(info.mode)) {\n fs.mkdirSync(to, { recursive: true });\n copyOut(from, to);\n } else if (py.FS.isFile(info.mode)) fs.writeFileSync(to, py.FS.readFile(from));\n }\n };\n copyOut("/job/out", spec.job.out);\n\n const exitCode = Number(code) | 0;\n process.stdout.write("", () => process.stderr.write("", () => process.exit(exitCode)));\n} catch (error) {\n fail(error instanceof Error ? (error.stack ?? error.message) : String(error));\n}\n';
@@ -0,0 +1,2 @@
1
+ export type RssReader = (pid: number) => Promise<number | undefined>;
2
+ export declare const readRssMb: RssReader;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Resident memory of a process in MiB, for the `wasm` backend's soft memory limit (PD13). Linux
3
+ * reads `/proc`; macOS and the BSDs ask `ps`; Windows asks `tasklist`. `undefined` when it cannot
4
+ * be read (the limit then is not enforced for that sample).
5
+ */
6
+ import { execFile } from "node:child_process";
7
+ import { readFileSync } from "node:fs";
8
+ function procRss(pid) {
9
+ try {
10
+ const match = /VmRSS:\s+(\d+) kB/.exec(readFileSync(`/proc/${pid}/status`, "utf8"));
11
+ return match ? Number(match[1]) / 1024 : undefined;
12
+ }
13
+ catch {
14
+ return undefined;
15
+ }
16
+ }
17
+ function run(command, args) {
18
+ return new Promise((resolve) => {
19
+ execFile(command, args, { timeout: 2000, windowsHide: true }, (error, out) => resolve(error ? undefined : String(out)));
20
+ });
21
+ }
22
+ export const readRssMb = async (pid) => {
23
+ if (process.platform === "linux")
24
+ return procRss(pid);
25
+ if (process.platform === "win32") {
26
+ const out = await run("tasklist", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"]);
27
+ const kb = out ? /"([\d.,\s]+) K"\s*$/m.exec(out)?.[1]?.replace(/[^\d]/g, "") : undefined;
28
+ return kb ? Number(kb) / 1024 : undefined;
29
+ }
30
+ const out = await run("ps", ["-o", "rss=", "-p", String(pid)]);
31
+ const kb = out ? Number(out.trim()) : Number.NaN;
32
+ return Number.isFinite(kb) ? kb / 1024 : undefined;
33
+ };
@@ -0,0 +1,6 @@
1
+ /** Most bytes copied; larger files that do not fit are left out (the runner used the same cap). */
2
+ export declare const SCRATCH_COPY_CAP_BYTES: number;
3
+ export declare function copyScratchNoFollow(source: string, target: string, cap?: number): Promise<{
4
+ files: number;
5
+ bytes: number;
6
+ }>;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * A private copy of the scratchpad for the `wasm` backend. The Node Permission Model follows
3
+ * links that already exist, and Python reaches `node:fs` through the FFI, so granting the real
4
+ * scratchpad would also grant whatever a symbolic link inside it points at. The copy is made by
5
+ * the parent without ever following a link: links and special files are skipped, regular files
6
+ * are opened with `O_NOFOLLOW` where the platform has it, relative paths are kept.
7
+ */
8
+ import { constants } from "node:fs";
9
+ import { lstat, mkdir, open, readdir } from "node:fs/promises";
10
+ import { join } from "node:path";
11
+ /** Most bytes copied; larger files that do not fit are left out (the runner used the same cap). */
12
+ export const SCRATCH_COPY_CAP_BYTES = 64 * 1024 * 1024;
13
+ /** Deepest directory level copied. */
14
+ const MAX_DEPTH = 32;
15
+ export async function copyScratchNoFollow(source, target, cap = SCRATCH_COPY_CAP_BYTES) {
16
+ await mkdir(target, { recursive: true, mode: 0o700 });
17
+ let files = 0;
18
+ let bytes = 0;
19
+ const walk = async (from, to, depth) => {
20
+ const entries = await readdir(from, { withFileTypes: true }).catch(() => []);
21
+ for (const entry of entries) {
22
+ const src = join(from, entry.name);
23
+ const dst = join(to, entry.name);
24
+ const info = await lstat(src).catch(() => undefined);
25
+ if (!info)
26
+ continue;
27
+ if (info.isDirectory()) {
28
+ if (depth >= MAX_DEPTH)
29
+ continue;
30
+ await mkdir(dst, { mode: 0o700 });
31
+ await walk(src, dst, depth + 1);
32
+ }
33
+ else if (info.isFile() && bytes + info.size <= cap) {
34
+ const handle = await open(src, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0)).catch(() => undefined);
35
+ if (!handle)
36
+ continue;
37
+ try {
38
+ // The entry could have been swapped for something else since the lstat.
39
+ const opened = await handle.stat();
40
+ if (!opened.isFile() || bytes + opened.size > cap)
41
+ continue;
42
+ const contents = await handle.readFile();
43
+ const out = await open(dst, "wx", 0o600);
44
+ try {
45
+ await out.writeFile(contents);
46
+ }
47
+ finally {
48
+ await out.close();
49
+ }
50
+ files++;
51
+ bytes += contents.length;
52
+ }
53
+ finally {
54
+ await handle.close();
55
+ }
56
+ }
57
+ }
58
+ };
59
+ await walk(source, target, 0);
60
+ return { files, bytes };
61
+ }
@@ -0,0 +1,6 @@
1
+ import type { TerminationReport } from "../ports.ts";
2
+ import type { RunningProcess } from "./backend.ts";
3
+ /** How long a stop waits for the process to be reported gone (the kill grace is 5 s). */
4
+ export declare const SETTLE_MS = 12000;
5
+ /** Waits for an aborted process to settle and says whether that was confirmed in time. */
6
+ export declare function settle(process: RunningProcess, method: TerminationReport["method"], started: number): Promise<TerminationReport>;
@@ -0,0 +1,21 @@
1
+ /** How long a stop waits for the process to be reported gone (the kill grace is 5 s). */
2
+ export const SETTLE_MS = 12_000;
3
+ /** Waits for an aborted process to settle and says whether that was confirmed in time. */
4
+ export async function settle(process, method, started) {
5
+ const done = process.result.then(() => true, () => true);
6
+ let timer;
7
+ const confirmed = await Promise.race([
8
+ done,
9
+ new Promise((resolve) => {
10
+ timer = setTimeout(() => resolve(false), SETTLE_MS);
11
+ timer.unref?.();
12
+ }),
13
+ ]);
14
+ clearTimeout(timer);
15
+ return {
16
+ confirmed,
17
+ method,
18
+ elapsedMs: Date.now() - started,
19
+ ...(confirmed ? {} : { settled: done.then(() => undefined) }),
20
+ };
21
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The live state of the Python backends for `alisio analysis status`, `alisio forge status` and the
3
+ * API (Python spec §9.2-9.3). A descriptor says what a backend IS; readiness says whether it can
4
+ * run now, so the two are merged here: `ready` and `reason` come from `check()`.
5
+ */
6
+ import type { EngineDescriptor } from "@alisio/sdk";
7
+ import type { PythonBackend, PythonBackendId } from "./backend.ts";
8
+ export declare function describePythonBackends(backends: Partial<Record<PythonBackendId, PythonBackend>>, signal?: AbortSignal): Promise<EngineDescriptor[]>;
@@ -0,0 +1,15 @@
1
+ /** Reading order: most isolated first, like `auto`. */
2
+ const ORDER = ["oci", "wasm", "managed"];
3
+ export async function describePythonBackends(backends, signal) {
4
+ const found = ORDER.flatMap((id) => {
5
+ const backend = backends[id];
6
+ return backend ? [backend] : [];
7
+ });
8
+ return Promise.all(found.map(async (backend) => {
9
+ const { reason: _own, ...descriptor } = await backend.describe();
10
+ const state = await backend.check(signal);
11
+ return state.ready
12
+ ? { ...descriptor, ready: true }
13
+ : { ...descriptor, ready: false, reason: state.reason };
14
+ }));
15
+ }
@@ -0,0 +1,59 @@
1
+ export declare const MIN_NODE: {
2
+ readonly major: 22;
3
+ readonly minor: 22;
4
+ };
5
+ export type NodeResolution = {
6
+ ok: true;
7
+ path: string;
8
+ version: string;
9
+ } | {
10
+ ok: false;
11
+ reason: string;
12
+ };
13
+ export interface NodeEnvironment {
14
+ /** Running under Bun (the standalone binary): `process.execPath` is not a Node. */
15
+ isBun: boolean;
16
+ execPath: string;
17
+ /** `process.version` of the running Node. */
18
+ version: string;
19
+ /** The `PATH` to search under Bun. */
20
+ pathEnv: string;
21
+ platform: NodeJS.Platform;
22
+ }
23
+ export declare const currentNodeEnvironment: () => NodeEnvironment;
24
+ /** `v22.22.0` -> true: at least the release of its line that fixed CVE-2025-55130. */
25
+ export declare function isSupportedNode(version: string): boolean;
26
+ /** Finds the node to run Pyodide with; only successes are remembered. */
27
+ export declare function resolveNode(configured: string | null | undefined, environment?: NodeEnvironment): Promise<NodeResolution>;
28
+ /** Clears remembered resolutions and probes (tests, and a changed PATH). */
29
+ export declare function resetNodeCaches(): void;
30
+ /**
31
+ * The flags of the restricted child: the Permission Model with ONE flag per path (a comma list is
32
+ * invalid in Node 22), reading only the distribution and the job, writing only `work/` and `out/`.
33
+ * No `--allow-child-process`, `--allow-worker`, `--allow-addons` or `--allow-wasi`; on Node >= 25
34
+ * `--allow-net` is likewise left out.
35
+ */
36
+ export declare function permissionArgs(paths: {
37
+ read: readonly string[];
38
+ write: readonly string[];
39
+ }): string[];
40
+ /** Environment of the child: nothing but the job's `ALISIO_*` variables. */
41
+ export declare function childEnv(vars: Record<string, string>, platform?: NodeJS.Platform): Record<string, string>;
42
+ export interface NetworkProbeResult {
43
+ /**
44
+ * The Permission Model refused the connection (`ERR_ACCESS_DENIED`). `false` also when the probe
45
+ * was not conclusive: any other failure (refused, unreachable, no route) says nothing about it.
46
+ */
47
+ denied: boolean;
48
+ detail: string;
49
+ /** The child gave an answer that settles the question (connected, or refused by the Permission Model). */
50
+ conclusive?: boolean;
51
+ }
52
+ /**
53
+ * Starts `node` with the real flags and tries to reach a port of ours. Remembered per binary and
54
+ * version (the flags do not change what the network flag does). Inconclusive means "not denied".
55
+ */
56
+ export declare function probeNetwork(node: {
57
+ path: string;
58
+ version: string;
59
+ }, args: readonly string[]): Promise<NetworkProbeResult>;