@nimbus-sh/worker 0.1.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 (408) hide show
  1. package/README.md +219 -0
  2. package/dist/_shared/bytes.d.ts +18 -0
  3. package/dist/_shared/bytes.d.ts.map +1 -0
  4. package/dist/_shared/bytes.js +17 -0
  5. package/dist/_shared/cache-stats.d.ts +146 -0
  6. package/dist/_shared/cache-stats.d.ts.map +1 -0
  7. package/dist/_shared/cache-stats.js +162 -0
  8. package/dist/_shared/exports-resolver.d.ts +76 -0
  9. package/dist/_shared/exports-resolver.d.ts.map +1 -0
  10. package/dist/_shared/exports-resolver.js +284 -0
  11. package/dist/_shared/install-phase.d.ts +47 -0
  12. package/dist/_shared/install-phase.d.ts.map +1 -0
  13. package/dist/_shared/install-phase.js +1 -0
  14. package/dist/_shared/real-node-imports.d.ts +38 -0
  15. package/dist/_shared/real-node-imports.d.ts.map +1 -0
  16. package/dist/_shared/real-node-imports.js +46 -0
  17. package/dist/_shared/retry.d.ts +93 -0
  18. package/dist/_shared/retry.d.ts.map +1 -0
  19. package/dist/_shared/retry.js +161 -0
  20. package/dist/_shared/session-id.d.ts +39 -0
  21. package/dist/_shared/session-id.d.ts.map +1 -0
  22. package/dist/_shared/session-id.js +127 -0
  23. package/dist/_shared/session-router.d.ts +104 -0
  24. package/dist/_shared/session-router.d.ts.map +1 -0
  25. package/dist/_shared/session-router.js +165 -0
  26. package/dist/_shared/w7-frame.d.ts +93 -0
  27. package/dist/_shared/w7-frame.d.ts.map +1 -0
  28. package/dist/_shared/w7-frame.js +402 -0
  29. package/dist/auth/index.d.ts +16 -0
  30. package/dist/auth/index.d.ts.map +1 -0
  31. package/dist/auth/index.js +15 -0
  32. package/dist/auth/middleware.d.ts +85 -0
  33. package/dist/auth/middleware.d.ts.map +1 -0
  34. package/dist/auth/middleware.js +150 -0
  35. package/dist/auth/token.d.ts +96 -0
  36. package/dist/auth/token.d.ts.map +1 -0
  37. package/dist/auth/token.js +226 -0
  38. package/dist/auth/types.d.ts +141 -0
  39. package/dist/auth/types.d.ts.map +1 -0
  40. package/dist/auth/types.js +114 -0
  41. package/dist/bindings/d1.d.ts +102 -0
  42. package/dist/bindings/d1.d.ts.map +1 -0
  43. package/dist/bindings/d1.js +549 -0
  44. package/dist/bindings/kv.d.ts +82 -0
  45. package/dist/bindings/kv.d.ts.map +1 -0
  46. package/dist/bindings/kv.js +290 -0
  47. package/dist/bindings/r2.d.ts +137 -0
  48. package/dist/bindings/r2.d.ts.map +1 -0
  49. package/dist/bindings/r2.js +379 -0
  50. package/dist/cirrus-npm-cjs.generated.d.ts +17 -0
  51. package/dist/cirrus-npm-cjs.generated.d.ts.map +1 -0
  52. package/dist/cirrus-npm-cjs.generated.js +41 -0
  53. package/dist/cirrus-plugin-react.generated.d.ts +34 -0
  54. package/dist/cirrus-plugin-react.generated.d.ts.map +1 -0
  55. package/dist/cirrus-plugin-react.generated.js +33 -0
  56. package/dist/constants.d.ts +34 -0
  57. package/dist/constants.d.ts.map +1 -0
  58. package/dist/constants.js +116 -0
  59. package/dist/esbuild-wasm-bundle.generated.d.ts +41 -0
  60. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -0
  61. package/dist/esbuild-wasm-bundle.generated.js +40 -0
  62. package/dist/facets/cirrus-real.d.ts +147 -0
  63. package/dist/facets/cirrus-real.d.ts.map +1 -0
  64. package/dist/facets/cirrus-real.js +981 -0
  65. package/dist/facets/inner-do-registry.d.ts +41 -0
  66. package/dist/facets/inner-do-registry.d.ts.map +1 -0
  67. package/dist/facets/inner-do-registry.js +51 -0
  68. package/dist/facets/manager.d.ts +314 -0
  69. package/dist/facets/manager.d.ts.map +1 -0
  70. package/dist/facets/manager.js +2681 -0
  71. package/dist/facets/process.d.ts +318 -0
  72. package/dist/facets/process.d.ts.map +1 -0
  73. package/dist/facets/process.js +567 -0
  74. package/dist/facets/real-vite-fs-shim.d.ts +151 -0
  75. package/dist/facets/real-vite-fs-shim.d.ts.map +1 -0
  76. package/dist/facets/real-vite-fs-shim.js +1279 -0
  77. package/dist/facets/real-vite-hmr.d.ts +142 -0
  78. package/dist/facets/real-vite-hmr.d.ts.map +1 -0
  79. package/dist/facets/real-vite-hmr.js +476 -0
  80. package/dist/facets/vite-dev-server.d.ts +293 -0
  81. package/dist/facets/vite-dev-server.d.ts.map +1 -0
  82. package/dist/facets/vite-dev-server.js +2580 -0
  83. package/dist/facets/wasm-swap-registry.d.ts +261 -0
  84. package/dist/facets/wasm-swap-registry.d.ts.map +1 -0
  85. package/dist/facets/wasm-swap-registry.js +551 -0
  86. package/dist/facets/ws-terminal.d.ts +107 -0
  87. package/dist/facets/ws-terminal.d.ts.map +1 -0
  88. package/dist/facets/ws-terminal.js +207 -0
  89. package/dist/git/commands.d.ts +13 -0
  90. package/dist/git/commands.d.ts.map +1 -0
  91. package/dist/git/commands.js +633 -0
  92. package/dist/git/network-facet.d.ts +61 -0
  93. package/dist/git/network-facet.d.ts.map +1 -0
  94. package/dist/git/network-facet.js +700 -0
  95. package/dist/git-bundle.generated.d.ts +16 -0
  96. package/dist/git-bundle.generated.d.ts.map +1 -0
  97. package/dist/git-bundle.generated.js +15 -0
  98. package/dist/index.d.ts +41 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +73 -0
  101. package/dist/loaders/child-process/spawn-facet.d.ts +90 -0
  102. package/dist/loaders/child-process/spawn-facet.d.ts.map +1 -0
  103. package/dist/loaders/child-process/spawn-facet.js +103 -0
  104. package/dist/loaders/child-process/spawn-pool.d.ts +110 -0
  105. package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -0
  106. package/dist/loaders/child-process/spawn-pool.js +161 -0
  107. package/dist/loaders/fanout-pool.d.ts +195 -0
  108. package/dist/loaders/fanout-pool.d.ts.map +1 -0
  109. package/dist/loaders/fanout-pool.js +326 -0
  110. package/dist/loaders/generated-workers.d.ts +26 -0
  111. package/dist/loaders/generated-workers.d.ts.map +1 -0
  112. package/dist/loaders/generated-workers.js +25 -0
  113. package/dist/loaders/index.d.ts +15 -0
  114. package/dist/loaders/index.d.ts.map +1 -0
  115. package/dist/loaders/index.js +13 -0
  116. package/dist/loaders/loader-pool.d.ts +273 -0
  117. package/dist/loaders/loader-pool.d.ts.map +1 -0
  118. package/dist/loaders/loader-pool.js +624 -0
  119. package/dist/loaders/npm-resolve-preamble.d.ts +34 -0
  120. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -0
  121. package/dist/loaders/npm-resolve-preamble.js +237 -0
  122. package/dist/loaders/pre-bundle-preamble.d.ts +41 -0
  123. package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -0
  124. package/dist/loaders/pre-bundle-preamble.js +114 -0
  125. package/dist/loaders/vendor/codegen.d.ts +24 -0
  126. package/dist/loaders/vendor/codegen.d.ts.map +1 -0
  127. package/dist/loaders/vendor/codegen.js +44 -0
  128. package/dist/loaders/vendor/errors.d.ts +24 -0
  129. package/dist/loaders/vendor/errors.d.ts.map +1 -0
  130. package/dist/loaders/vendor/errors.js +46 -0
  131. package/dist/loaders/vendor/index.d.ts +16 -0
  132. package/dist/loaders/vendor/index.d.ts.map +1 -0
  133. package/dist/loaders/vendor/index.js +11 -0
  134. package/dist/loaders/vendor/pool.d.ts +88 -0
  135. package/dist/loaders/vendor/pool.d.ts.map +1 -0
  136. package/dist/loaders/vendor/pool.js +353 -0
  137. package/dist/loaders/vendor/primitives.d.ts +11 -0
  138. package/dist/loaders/vendor/primitives.d.ts.map +1 -0
  139. package/dist/loaders/vendor/primitives.js +33 -0
  140. package/dist/loaders/vendor/serialize.d.ts +3 -0
  141. package/dist/loaders/vendor/serialize.d.ts.map +1 -0
  142. package/dist/loaders/vendor/serialize.js +25 -0
  143. package/dist/loaders/vendor/types.d.ts +60 -0
  144. package/dist/loaders/vendor/types.d.ts.map +1 -0
  145. package/dist/loaders/vendor/types.js +4 -0
  146. package/dist/npm/cache.d.ts +136 -0
  147. package/dist/npm/cache.d.ts.map +1 -0
  148. package/dist/npm/cache.js +429 -0
  149. package/dist/npm/install-batch-facet.d.ts +129 -0
  150. package/dist/npm/install-batch-facet.d.ts.map +1 -0
  151. package/dist/npm/install-batch-facet.js +598 -0
  152. package/dist/npm/install-facet.d.ts +29 -0
  153. package/dist/npm/install-facet.d.ts.map +1 -0
  154. package/dist/npm/install-facet.js +9 -0
  155. package/dist/npm/installer.d.ts +239 -0
  156. package/dist/npm/installer.d.ts.map +1 -0
  157. package/dist/npm/installer.js +2022 -0
  158. package/dist/npm/npx-install.d.ts +74 -0
  159. package/dist/npm/npx-install.d.ts.map +1 -0
  160. package/dist/npm/npx-install.js +302 -0
  161. package/dist/npm/pre-bundle-facet.d.ts +168 -0
  162. package/dist/npm/pre-bundle-facet.d.ts.map +1 -0
  163. package/dist/npm/pre-bundle-facet.js +653 -0
  164. package/dist/npm/r2-cache.d.ts +286 -0
  165. package/dist/npm/r2-cache.d.ts.map +1 -0
  166. package/dist/npm/r2-cache.js +506 -0
  167. package/dist/npm/resolve-facet.d.ts +202 -0
  168. package/dist/npm/resolve-facet.d.ts.map +1 -0
  169. package/dist/npm/resolve-facet.js +833 -0
  170. package/dist/npm/resolve-one-facet.d.ts +204 -0
  171. package/dist/npm/resolve-one-facet.d.ts.map +1 -0
  172. package/dist/npm/resolve-one-facet.js +437 -0
  173. package/dist/npm/resolver.d.ts +109 -0
  174. package/dist/npm/resolver.d.ts.map +1 -0
  175. package/dist/npm/resolver.js +899 -0
  176. package/dist/npm/tarball-stream.d.ts +85 -0
  177. package/dist/npm/tarball-stream.d.ts.map +1 -0
  178. package/dist/npm/tarball-stream.js +205 -0
  179. package/dist/npm/tarball.d.ts +51 -0
  180. package/dist/npm/tarball.d.ts.map +1 -0
  181. package/dist/npm/tarball.js +185 -0
  182. package/dist/observability/diag-counters.d.ts +292 -0
  183. package/dist/observability/diag-counters.d.ts.map +1 -0
  184. package/dist/observability/diag-counters.js +302 -0
  185. package/dist/observability/heap-estimate.d.ts +166 -0
  186. package/dist/observability/heap-estimate.d.ts.map +1 -0
  187. package/dist/observability/heap-estimate.js +192 -0
  188. package/dist/observability/heavy-alloc-coord.d.ts +95 -0
  189. package/dist/observability/heavy-alloc-coord.d.ts.map +1 -0
  190. package/dist/observability/heavy-alloc-coord.js +149 -0
  191. package/dist/observability/oom-classify.d.ts +56 -0
  192. package/dist/observability/oom-classify.d.ts.map +1 -0
  193. package/dist/observability/oom-classify.js +118 -0
  194. package/dist/observability/oom-discriminator.d.ts +192 -0
  195. package/dist/observability/oom-discriminator.d.ts.map +1 -0
  196. package/dist/observability/oom-discriminator.js +233 -0
  197. package/dist/real-vite-bundle.generated.d.ts +46 -0
  198. package/dist/real-vite-bundle.generated.d.ts.map +1 -0
  199. package/dist/real-vite-bundle.generated.js +53 -0
  200. package/dist/replica/routing.d.ts +171 -0
  201. package/dist/replica/routing.d.ts.map +1 -0
  202. package/dist/replica/routing.js +288 -0
  203. package/dist/replica/suspension.d.ts +36 -0
  204. package/dist/replica/suspension.d.ts.map +1 -0
  205. package/dist/replica/suspension.js +49 -0
  206. package/dist/router/index.d.ts +156 -0
  207. package/dist/router/index.d.ts.map +1 -0
  208. package/dist/router/index.js +220 -0
  209. package/dist/runtime/assets-loader.d.ts +71 -0
  210. package/dist/runtime/assets-loader.d.ts.map +1 -0
  211. package/dist/runtime/assets-loader.js +128 -0
  212. package/dist/runtime/barrel-detect.d.ts +46 -0
  213. package/dist/runtime/barrel-detect.d.ts.map +1 -0
  214. package/dist/runtime/barrel-detect.js +73 -0
  215. package/dist/runtime/barrel-synthesizer.d.ts +181 -0
  216. package/dist/runtime/barrel-synthesizer.d.ts.map +1 -0
  217. package/dist/runtime/barrel-synthesizer.js +541 -0
  218. package/dist/runtime/bun-repl.d.ts +46 -0
  219. package/dist/runtime/bun-repl.d.ts.map +1 -0
  220. package/dist/runtime/bun-repl.js +326 -0
  221. package/dist/runtime/bun-runner.d.ts +66 -0
  222. package/dist/runtime/bun-runner.d.ts.map +1 -0
  223. package/dist/runtime/bun-runner.js +219 -0
  224. package/dist/runtime/clang-runner.d.ts +38 -0
  225. package/dist/runtime/clang-runner.d.ts.map +1 -0
  226. package/dist/runtime/clang-runner.js +1033 -0
  227. package/dist/runtime/comment-strip.d.ts +37 -0
  228. package/dist/runtime/comment-strip.d.ts.map +1 -0
  229. package/dist/runtime/comment-strip.js +74 -0
  230. package/dist/runtime/esbuild-service.d.ts +201 -0
  231. package/dist/runtime/esbuild-service.d.ts.map +1 -0
  232. package/dist/runtime/esbuild-service.js +1444 -0
  233. package/dist/runtime/esbuild-wasm-bytes.d.ts +87 -0
  234. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -0
  235. package/dist/runtime/esbuild-wasm-bytes.js +131 -0
  236. package/dist/runtime/framework-detect.d.ts +71 -0
  237. package/dist/runtime/framework-detect.d.ts.map +1 -0
  238. package/dist/runtime/framework-detect.js +178 -0
  239. package/dist/runtime/long-running-handle.d.ts +133 -0
  240. package/dist/runtime/long-running-handle.d.ts.map +1 -0
  241. package/dist/runtime/long-running-handle.js +192 -0
  242. package/dist/runtime/node-repl.d.ts +36 -0
  243. package/dist/runtime/node-repl.d.ts.map +1 -0
  244. package/dist/runtime/node-repl.js +293 -0
  245. package/dist/runtime/node-runner.d.ts +111 -0
  246. package/dist/runtime/node-runner.d.ts.map +1 -0
  247. package/dist/runtime/node-runner.js +194 -0
  248. package/dist/runtime/node-shims.d.ts +24 -0
  249. package/dist/runtime/node-shims.d.ts.map +1 -0
  250. package/dist/runtime/node-shims.js +3019 -0
  251. package/dist/runtime/package-manager.d.ts +118 -0
  252. package/dist/runtime/package-manager.d.ts.map +1 -0
  253. package/dist/runtime/package-manager.js +420 -0
  254. package/dist/runtime/port-registry.d.ts +80 -0
  255. package/dist/runtime/port-registry.d.ts.map +1 -0
  256. package/dist/runtime/port-registry.js +166 -0
  257. package/dist/runtime/process-logs-api.d.ts +102 -0
  258. package/dist/runtime/process-logs-api.d.ts.map +1 -0
  259. package/dist/runtime/process-logs-api.js +253 -0
  260. package/dist/runtime/process-logs.d.ts +311 -0
  261. package/dist/runtime/process-logs.d.ts.map +1 -0
  262. package/dist/runtime/process-logs.js +656 -0
  263. package/dist/runtime/process-table.d.ts +65 -0
  264. package/dist/runtime/process-table.d.ts.map +1 -0
  265. package/dist/runtime/process-table.js +107 -0
  266. package/dist/runtime/project-detect.d.ts +21 -0
  267. package/dist/runtime/project-detect.d.ts.map +1 -0
  268. package/dist/runtime/project-detect.js +46 -0
  269. package/dist/runtime/python-repl.d.ts +66 -0
  270. package/dist/runtime/python-repl.d.ts.map +1 -0
  271. package/dist/runtime/python-repl.js +563 -0
  272. package/dist/runtime/python-runner.d.ts +50 -0
  273. package/dist/runtime/python-runner.d.ts.map +1 -0
  274. package/dist/runtime/python-runner.js +870 -0
  275. package/dist/runtime/repl-session.d.ts +160 -0
  276. package/dist/runtime/repl-session.d.ts.map +1 -0
  277. package/dist/runtime/repl-session.js +393 -0
  278. package/dist/runtime/require-resolver.d.ts +52 -0
  279. package/dist/runtime/require-resolver.d.ts.map +1 -0
  280. package/dist/runtime/require-resolver.js +690 -0
  281. package/dist/runtime/router-basename.d.ts +36 -0
  282. package/dist/runtime/router-basename.d.ts.map +1 -0
  283. package/dist/runtime/router-basename.js +310 -0
  284. package/dist/runtime/ruby-repl.d.ts +43 -0
  285. package/dist/runtime/ruby-repl.d.ts.map +1 -0
  286. package/dist/runtime/ruby-repl.js +229 -0
  287. package/dist/runtime/ruby-runner.d.ts +62 -0
  288. package/dist/runtime/ruby-runner.d.ts.map +1 -0
  289. package/dist/runtime/ruby-runner.js +737 -0
  290. package/dist/runtime/runtime-catalog.d.ts +99 -0
  291. package/dist/runtime/runtime-catalog.d.ts.map +1 -0
  292. package/dist/runtime/runtime-catalog.js +183 -0
  293. package/dist/runtime/runtime-registry.d.ts +136 -0
  294. package/dist/runtime/runtime-registry.d.ts.map +1 -0
  295. package/dist/runtime/runtime-registry.js +318 -0
  296. package/dist/runtime/streams.d.ts +13 -0
  297. package/dist/runtime/streams.d.ts.map +1 -0
  298. package/dist/runtime/streams.js +402 -0
  299. package/dist/runtime/wasi-instance.d.ts +190 -0
  300. package/dist/runtime/wasi-instance.d.ts.map +1 -0
  301. package/dist/runtime/wasi-instance.js +2044 -0
  302. package/dist/runtime/wasm-runner.d.ts +117 -0
  303. package/dist/runtime/wasm-runner.d.ts.map +1 -0
  304. package/dist/runtime/wasm-runner.js +705 -0
  305. package/dist/session/bindings.d.ts +168 -0
  306. package/dist/session/bindings.d.ts.map +1 -0
  307. package/dist/session/bindings.js +524 -0
  308. package/dist/session/ctx-exports.d.ts +16 -0
  309. package/dist/session/ctx-exports.d.ts.map +1 -0
  310. package/dist/session/ctx-exports.js +22 -0
  311. package/dist/session/diag.d.ts +62 -0
  312. package/dist/session/diag.d.ts.map +1 -0
  313. package/dist/session/diag.js +118 -0
  314. package/dist/session/fs-watch.d.ts +132 -0
  315. package/dist/session/fs-watch.d.ts.map +1 -0
  316. package/dist/session/fs-watch.js +255 -0
  317. package/dist/session/helpers.d.ts +150 -0
  318. package/dist/session/helpers.d.ts.map +1 -0
  319. package/dist/session/helpers.js +472 -0
  320. package/dist/session/hibernation.d.ts +145 -0
  321. package/dist/session/hibernation.d.ts.map +1 -0
  322. package/dist/session/hibernation.js +385 -0
  323. package/dist/session/init-phases.d.ts +87 -0
  324. package/dist/session/init-phases.d.ts.map +1 -0
  325. package/dist/session/init-phases.js +137 -0
  326. package/dist/session/init.d.ts +46 -0
  327. package/dist/session/init.d.ts.map +1 -0
  328. package/dist/session/init.js +3579 -0
  329. package/dist/session/keys.d.ts +56 -0
  330. package/dist/session/keys.d.ts.map +1 -0
  331. package/dist/session/keys.js +55 -0
  332. package/dist/session/nimbus-session.d.ts +434 -0
  333. package/dist/session/nimbus-session.d.ts.map +1 -0
  334. package/dist/session/nimbus-session.js +1084 -0
  335. package/dist/session/programmatic.d.ts +105 -0
  336. package/dist/session/programmatic.d.ts.map +1 -0
  337. package/dist/session/programmatic.js +326 -0
  338. package/dist/session/replica-routes.d.ts +68 -0
  339. package/dist/session/replica-routes.d.ts.map +1 -0
  340. package/dist/session/replica-routes.js +77 -0
  341. package/dist/session/routes.d.ts +29 -0
  342. package/dist/session/routes.d.ts.map +1 -0
  343. package/dist/session/routes.js +1331 -0
  344. package/dist/session/rpc.d.ts +323 -0
  345. package/dist/session/rpc.d.ts.map +1 -0
  346. package/dist/session/rpc.js +808 -0
  347. package/dist/session/state-store.d.ts +210 -0
  348. package/dist/session/state-store.d.ts.map +1 -0
  349. package/dist/session/state-store.js +459 -0
  350. package/dist/session/supervisor-rpc.d.ts +269 -0
  351. package/dist/session/supervisor-rpc.d.ts.map +1 -0
  352. package/dist/session/supervisor-rpc.js +437 -0
  353. package/dist/session/ws-hibernation-config.d.ts +64 -0
  354. package/dist/session/ws-hibernation-config.d.ts.map +1 -0
  355. package/dist/session/ws-hibernation-config.js +89 -0
  356. package/dist/session/ws.d.ts +95 -0
  357. package/dist/session/ws.d.ts.map +1 -0
  358. package/dist/session/ws.js +395 -0
  359. package/dist/shell/features.d.ts +410 -0
  360. package/dist/shell/features.d.ts.map +1 -0
  361. package/dist/shell/features.js +1885 -0
  362. package/dist/shell/unix-commands.d.ts +15 -0
  363. package/dist/shell/unix-commands.d.ts.map +1 -0
  364. package/dist/shell/unix-commands.js +3703 -0
  365. package/dist/stubs/lifo-ui.d.ts +10 -0
  366. package/dist/stubs/lifo-ui.d.ts.map +1 -0
  367. package/dist/stubs/lifo-ui.js +1 -0
  368. package/dist/tailwind-play.generated.d.ts +19 -0
  369. package/dist/tailwind-play.generated.d.ts.map +1 -0
  370. package/dist/tailwind-play.generated.js +18 -0
  371. package/dist/vfs/dev-provider.d.ts +64 -0
  372. package/dist/vfs/dev-provider.d.ts.map +1 -0
  373. package/dist/vfs/dev-provider.js +191 -0
  374. package/dist/vfs/events.d.ts +51 -0
  375. package/dist/vfs/events.d.ts.map +1 -0
  376. package/dist/vfs/events.js +127 -0
  377. package/dist/vfs/path.d.ts +30 -0
  378. package/dist/vfs/path.d.ts.map +1 -0
  379. package/dist/vfs/path.js +41 -0
  380. package/dist/vfs/seed-project.d.ts +62 -0
  381. package/dist/vfs/seed-project.d.ts.map +1 -0
  382. package/dist/vfs/seed-project.js +1045 -0
  383. package/dist/vfs/sqlite-vfs.d.ts +435 -0
  384. package/dist/vfs/sqlite-vfs.d.ts.map +1 -0
  385. package/dist/vfs/sqlite-vfs.js +1670 -0
  386. package/dist/vfs/symlink-registry.d.ts +64 -0
  387. package/dist/vfs/symlink-registry.d.ts.map +1 -0
  388. package/dist/vfs/symlink-registry.js +137 -0
  389. package/dist/wrangler/nimbus-wrangler.d.ts +157 -0
  390. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -0
  391. package/dist/wrangler/nimbus-wrangler.js +931 -0
  392. package/package.json +77 -0
  393. package/public/_assets/cirrus-plugin-react.bundle.js +70740 -0
  394. package/public/_assets/esbuild-0.24.2.wasm +0 -0
  395. package/public/_assets/real-vite-bundle.js +101478 -0
  396. package/public/_assets/rollup.wasm +0 -0
  397. package/public/favicon.svg +8 -0
  398. package/public/index.html +565 -0
  399. package/public/s/index.html +2379 -0
  400. package/scripts/bundle-esbuild-wasm.mjs +244 -0
  401. package/scripts/bundle-facet-workers.mjs +137 -0
  402. package/scripts/bundle-git.mjs +118 -0
  403. package/scripts/bundle-npm-cjs.mjs +363 -0
  404. package/scripts/bundle-plugin-react.mjs +480 -0
  405. package/scripts/bundle-real-vite.mjs +959 -0
  406. package/scripts/bundle-runtime.mjs +600 -0
  407. package/scripts/bundle-tailwind-play.mjs +130 -0
  408. package/scripts/patch-lifo-ui.mjs +131 -0
@@ -0,0 +1,870 @@
1
+ /**
2
+ * python-runner.ts — Pyodide v0.29.4 runner (True-OS Wave-3 v2 / Pyodide v1).
3
+ *
4
+ * v1 scope per /workspace/.seal-internal/2026-05-10-pyodide-research/plan.md
5
+ * D1-D7:
6
+ * - `python --version` / `python -c '<code>'` / `python script.py`
7
+ * - stdlib subset (full python_stdlib.zip ships)
8
+ * - stdout/stderr → processLogs (Process tab integration)
9
+ * - exit code via sys.exit(N) or unhandled exception → 1
10
+ * - argv passed through to sys.argv
11
+ *
12
+ * Out of v1 (queued for v2/v3):
13
+ * - REPL mode (`python` with no args)
14
+ * - File I/O beyond reading the entry script
15
+ * - `pip install` / `loadPackage` / native-extension packages
16
+ * - Sync HTTP (urllib3 / requests blocked without JSPI)
17
+ *
18
+ * Architecture: SAME LOADER-modules transport as clang-runner/wasm-
19
+ * runner. The Pyodide wasm bytes ship via the LOADER `modules` map
20
+ * (CSP allows wasm code-gen at module-load time, not at request
21
+ * time). The Pyodide.asm.js + stdlib zip ride via the loader-pool
22
+ * `context` field (JSON-stringified into the inner worker.js at
23
+ * module-load).
24
+ *
25
+ * Per wasm-csp/findings.md §4b: Pyodide.asm.wasm (10.1 MB on disk)
26
+ * compiles in 314 ms via LOADER on PROD. With our v1 deployment of
27
+ * 0.29.4 (8.25 MB asm.wasm), this is well under the empirical
28
+ * ~32 MiB per-call ceiling.
29
+ */
30
+ /**
31
+ * Build the python-runner factory. Called once at session init; the
32
+ * returned factory binds the manifest + install root for each
33
+ * registered entrypoint (`python`, `python3`).
34
+ */
35
+ export function makePythonRunnerFactory(deps) {
36
+ const { facetMgr, vfs } = deps;
37
+ return function pythonRunnerFactory(manifest, installRoot, binName, _binKind) {
38
+ const findFile = (rel) => {
39
+ const entry = manifest.files.find((f) => f.path === rel);
40
+ return entry ? `${installRoot}/${entry.path}` : null;
41
+ };
42
+ const asmWasmVfs = findFile('share/pyodide/pyodide.asm.wasm');
43
+ const asmJsVfs = findFile('share/pyodide/pyodide.asm.js');
44
+ const stdlibVfs = findFile('share/pyodide/python_stdlib.zip');
45
+ return async function pythonBinHandler(ctx) {
46
+ const argv = ctx.args || [];
47
+ const cwd = ctx.cwd || '/home/user';
48
+ // ── --version / --help fast paths (no wasm boot) ─────────────
49
+ if (argv.includes('--version') || argv.includes('-V')) {
50
+ ctx.stdout.write(`Python 3.13.2 (Pyodide 0.29.4, Nimbus runtime)\n`);
51
+ return 0;
52
+ }
53
+ if (argv.includes('--help') || argv.includes('-h')) {
54
+ ctx.stdout.write(`usage: ${binName} [option] ... [-c cmd | -m mod | file | -] [arg] ...\n`);
55
+ ctx.stdout.write(`Nimbus Pyodide 0.29.4 / Python 3.13 runtime.\n`);
56
+ ctx.stdout.write(`Supported v1: -c <code>, <file.py>, stdin via -\n`);
57
+ ctx.stdout.write(`Not supported: REPL (no args), pip, native extensions\n`);
58
+ return 0;
59
+ }
60
+ // ── Resolve install bytes ────────────────────────────────────
61
+ if (!asmWasmVfs || !vfs.exists(asmWasmVfs)) {
62
+ ctx.stderr.write(`${binName}: pyodide.asm.wasm missing (re-run 'nimbus install python')\n`);
63
+ return 127;
64
+ }
65
+ if (!asmJsVfs || !vfs.exists(asmJsVfs)) {
66
+ ctx.stderr.write(`${binName}: pyodide.asm.js missing\n`);
67
+ return 127;
68
+ }
69
+ if (!stdlibVfs || !vfs.exists(stdlibVfs)) {
70
+ ctx.stderr.write(`${binName}: python_stdlib.zip missing\n`);
71
+ return 127;
72
+ }
73
+ const asmWasmBytes = vfs.readFile(asmWasmVfs);
74
+ const asmJsBytes = vfs.readFile(asmJsVfs);
75
+ const stdlibBytes = vfs.readFile(stdlibVfs);
76
+ // ── Parse argv ───────────────────────────────────────────────
77
+ // Supported in v1:
78
+ // python -c '<code>' run inline code, args[i+1..] in sys.argv
79
+ // python <file.py> [args...] run script, args in sys.argv
80
+ // python (no args) → not supported; REPL is v2
81
+ // python - read code from stdin (advanced)
82
+ const parsed = parsePythonArgv(argv);
83
+ if (parsed.error) {
84
+ ctx.stderr.write(`${binName}: ${parsed.error}\n`);
85
+ return parsed.exitCode;
86
+ }
87
+ // For -c mode: code comes from argv. For script-file mode:
88
+ // read from VFS. For stdin mode: we collect stdin upfront.
89
+ let userCode = '';
90
+ let progName = binName;
91
+ let pyArgv = [binName];
92
+ if (parsed.mode === 'inline') {
93
+ userCode = parsed.inlineCode;
94
+ pyArgv = ['-c', ...parsed.scriptArgs];
95
+ }
96
+ else if (parsed.mode === 'script') {
97
+ // Read script from VFS, resolving relative to cwd.
98
+ const absPath = resolveVfsPath(parsed.scriptPath, cwd);
99
+ if (!vfs.exists(absPath)) {
100
+ ctx.stderr.write(`${binName}: ${parsed.scriptPath}: No such file or directory\n`);
101
+ return 2;
102
+ }
103
+ try {
104
+ userCode = new TextDecoder('utf-8').decode(vfs.readFile(absPath));
105
+ }
106
+ catch (e) {
107
+ ctx.stderr.write(`${binName}: ${parsed.scriptPath}: ${e?.message || e}\n`);
108
+ return 1;
109
+ }
110
+ progName = parsed.scriptPath;
111
+ pyArgv = [parsed.scriptPath, ...parsed.scriptArgs];
112
+ }
113
+ else if (parsed.mode === 'stdin') {
114
+ // Read all of stdin from ctx (lifo-sh wires it when the pipe
115
+ // is filled). Pyodide receives it as the program source.
116
+ const stdinReader = ctx.stdin;
117
+ if (stdinReader && typeof stdinReader.read === 'function') {
118
+ // The shell's ctx.stdin is a stream-like with .read() that
119
+ // returns the accumulated buffer up to EOF. Lifo-sh's pipe
120
+ // implementation feeds the upstream's stdout into this.
121
+ userCode = await stdinReader.read();
122
+ }
123
+ else {
124
+ // No piped stdin → empty program.
125
+ userCode = '';
126
+ }
127
+ pyArgv = ['-', ...parsed.scriptArgs];
128
+ }
129
+ // env passed to Python's os.environ. We forward NIMBUS_*,
130
+ // PATH-ish, and a default HOME if not set.
131
+ const userEnv = { ...(ctx.env || {}) };
132
+ if (!userEnv.HOME)
133
+ userEnv.HOME = '/home/pyodide';
134
+ if (!userEnv.PYTHONUNBUFFERED)
135
+ userEnv.PYTHONUNBUFFERED = '1';
136
+ // ── Dispatch the facet ───────────────────────────────────────
137
+ const result = await dispatchPythonFacet(facetMgr, {
138
+ asmWasmBytes,
139
+ asmJsBytes,
140
+ stdlibBytes,
141
+ userCode,
142
+ pyArgv,
143
+ userEnv,
144
+ progName,
145
+ });
146
+ if (result.stdout)
147
+ ctx.stdout.write(result.stdout);
148
+ if (result.stderr)
149
+ ctx.stderr.write(result.stderr);
150
+ if (result.error) {
151
+ ctx.stderr.write(`${binName}: ${result.error}\n`);
152
+ return 1;
153
+ }
154
+ return result.exitCode;
155
+ };
156
+ };
157
+ }
158
+ function parsePythonArgv(argv) {
159
+ // Walk argv left-to-right looking for the first non-flag token OR
160
+ // the -c / -m mode-switches. Python's CLI is more involved (-O,
161
+ // -B, -E, -W, -I, etc.) but for v1 we accept and ignore unknown
162
+ // single-letter flags that don't take args, and error loudly on
163
+ // unsupported ones.
164
+ let i = 0;
165
+ while (i < argv.length) {
166
+ const a = argv[i];
167
+ if (a === '-c') {
168
+ const code = argv[i + 1];
169
+ if (code === undefined) {
170
+ return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
171
+ error: "Argument expected for the -c option" };
172
+ }
173
+ return {
174
+ mode: 'inline',
175
+ inlineCode: code,
176
+ scriptPath: '',
177
+ scriptArgs: argv.slice(i + 2),
178
+ exitCode: 0,
179
+ };
180
+ }
181
+ if (a === '-m') {
182
+ const mod = argv[i + 1];
183
+ if (mod === undefined) {
184
+ return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
185
+ error: "Argument expected for the -m option" };
186
+ }
187
+ // -m <mod> [args...] → runpy.run_module(mod)
188
+ const inlineCode = `import runpy, sys\nsys.argv = ${JSON.stringify([mod, ...argv.slice(i + 2)])}\nrunpy.run_module(${JSON.stringify(mod)}, run_name='__main__', alter_sys=True)\n`;
189
+ return {
190
+ mode: 'inline',
191
+ inlineCode,
192
+ scriptPath: '',
193
+ scriptArgs: argv.slice(i + 2),
194
+ exitCode: 0,
195
+ };
196
+ }
197
+ if (a === '-') {
198
+ return { mode: 'stdin', inlineCode: '', scriptPath: '-', scriptArgs: argv.slice(i + 1), exitCode: 0 };
199
+ }
200
+ if (!a.startsWith('-')) {
201
+ return {
202
+ mode: 'script',
203
+ inlineCode: '',
204
+ scriptPath: a,
205
+ scriptArgs: argv.slice(i + 1),
206
+ exitCode: 0,
207
+ };
208
+ }
209
+ // Unknown flag — for v1, silently skip single-char flags that
210
+ // are commonly harmless (-O, -B, -u, -E, -I). Error on others.
211
+ if (/^-[OBuEItcsx]+$/.test(a)) {
212
+ i++;
213
+ continue;
214
+ }
215
+ return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
216
+ error: `unknown option: ${a}` };
217
+ }
218
+ // No mode argument provided — REPL is not supported in v1.
219
+ return { mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2,
220
+ error: "REPL not supported in v1. Use 'python -c \"code\"' or 'python script.py'." };
221
+ }
222
+ function resolveVfsPath(rel, cwd) {
223
+ const cwdN = cwd.replace(/^\/+/, '').replace(/\/+$/, '');
224
+ if (rel.startsWith('/'))
225
+ return rel.replace(/^\/+/, '');
226
+ if (rel === '.')
227
+ return cwdN;
228
+ return `${cwdN}/${rel}`;
229
+ }
230
+ function uint8ToBase64(u8) {
231
+ const CHUNK = 0x8000;
232
+ let s = '';
233
+ for (let i = 0; i < u8.length; i += CHUNK) {
234
+ s += String.fromCharCode.apply(null, Array.from(u8.subarray(i, Math.min(i + CHUNK, u8.length))));
235
+ }
236
+ return btoa(s);
237
+ }
238
+ async function dispatchPythonFacet(facetMgr, args) {
239
+ const toAB = (u8) => u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
240
+ // pyodide.asm.js is INLINED into the preamble (1 MiB text source).
241
+ // Workerd's CSP allows wasm code-gen AND JS top-level evaluation at
242
+ // module-load time, but BLOCKS `new Function(src)` at request time.
243
+ // The asm.js source declares `var _createPyodideModule = ...` at
244
+ // module-top; spliced into the preamble it becomes a module-scope
245
+ // binding we expose via `globalThis._createPyodideModule = ...`
246
+ // right after.
247
+ //
248
+ // stdlib.zip stays in the context (base64) — it's data not code,
249
+ // and the context blob is evaluated at module-load too.
250
+ let asmJsSrc = new TextDecoder('utf-8').decode(args.asmJsBytes);
251
+ // ── Source-patch the asm.js's "Cannot determine runtime environment" branch ──
252
+ //
253
+ // Even with our globalThis.process / WorkerGlobalScope / self / location
254
+ // shims in place, the asm.js's pyodide_js_init() IIFE's env-detect (via
255
+ // bn → vn) somehow lands d.IN_BROWSER_WEB_WORKER = false at request time
256
+ // on workerd. Our supervisor-side mirror of the same logic returns
257
+ // IN_BROWSER_WEB_WORKER = true with the same shims; the discrepancy
258
+ // remains unidentified after P8-P17 of diagnostic instrumentation.
259
+ //
260
+ // Pragmatic v1 fix: source-patch the if-chain to ALWAYS take the
261
+ // IN_BROWSER_WEB_WORKER branch (regardless of `d`). The branch's body
262
+ // assigns `Fe` to a globalThis.importScripts-based loadScript helper.
263
+ // We don't actually call Fe (Pyodide only invokes loadScript when
264
+ // dynamic-loading a wasm sibling; our instantiateWasm override pre-
265
+ // empts that). So the patch is functionally a no-op other than making
266
+ // the throw unreachable.
267
+ //
268
+ // The patch target string is a literal in the minified asm.js — stable
269
+ // across rebuilds because it's a unique identifier path.
270
+ const PATCH_NEEDLE = 'else throw new Error("Cannot determine runtime environment")';
271
+ const PATCH_REPLACE = '/* nimbus-patch: was: ' + PATCH_NEEDLE + ' */';
272
+ if (asmJsSrc.includes(PATCH_NEEDLE)) {
273
+ asmJsSrc = asmJsSrc.replace(PATCH_NEEDLE, PATCH_REPLACE);
274
+ }
275
+ // Also patch the if-chain head so we ALWAYS set Fe (force-take the
276
+ // first branch — IN_BROWSER_MAIN_THREAD's loadScript path uses
277
+ // `await import(t)` which works in workerd at module-load time).
278
+ // The previous patch alone is insufficient because if NO branch matches,
279
+ // Fe stays undefined → calling it would TypeError later.
280
+ const HEAD_NEEDLE = 'if(d.IN_BROWSER_MAIN_THREAD)';
281
+ const HEAD_REPLACE = 'if(true||d.IN_BROWSER_MAIN_THREAD)';
282
+ if (asmJsSrc.includes(HEAD_NEEDLE)) {
283
+ asmJsSrc = asmJsSrc.replace(HEAD_NEEDLE, HEAD_REPLACE);
284
+ }
285
+ const stdlibB64 = uint8ToBase64(args.stdlibBytes);
286
+ // Build the preamble with stdlib bytes spliced in. The preamble runs
287
+ // the entire Pyodide bootstrap at child-facet module-init time —
288
+ // where workerd's CSP permits `new WebAssembly.Module(rawBytes)` (the
289
+ // convertJsFunctionToWasm code path which is the v1 hang root).
290
+ // Per-call __pyodideRun just executes runPython on the cached
291
+ // Pyodide instance.
292
+ const preamble = buildPyodidePreamble(asmJsSrc, stdlibB64);
293
+ const { NimbusLoaderPool } = await import('../loaders/loader-pool.js');
294
+ const env = facetMgr.env;
295
+ const ctx = facetMgr.ctx;
296
+ const pool = new NimbusLoaderPool(env, ctx, {
297
+ tag: 'python-runner',
298
+ concurrency: 1,
299
+ omitSupervisor: true,
300
+ preamble,
301
+ });
302
+ // v2 simplification: stdlibB64 and asmWasmMod are now embedded in the
303
+ // preamble (stdlib via base64 splice; wasm via __NIMBUS_WASM table set
304
+ // by the loader-pool). The facet fn only needs the per-call inputs.
305
+ const facetFn = async function pythonFacetCall(inArgs) {
306
+ const fn = globalThis.__pyodideRun;
307
+ if (typeof fn !== 'function') {
308
+ return { exitCode: 127, stdout: '', stderr: '',
309
+ error: 'python-runner preamble missing: __pyodideRun not in scope' };
310
+ }
311
+ return await fn({
312
+ userCode: inArgs.userCode,
313
+ pyArgv: inArgs.pyArgv,
314
+ userEnv: inArgs.userEnv,
315
+ progName: inArgs.progName,
316
+ });
317
+ };
318
+ try {
319
+ const result = await pool.submit(facetFn, {
320
+ userCode: args.userCode,
321
+ pyArgv: args.pyArgv,
322
+ userEnv: args.userEnv,
323
+ progName: args.progName,
324
+ }, {
325
+ wasmModules: {
326
+ 'pyodide.asm.wasm': toAB(args.asmWasmBytes),
327
+ },
328
+ timeoutMs: 300_000,
329
+ });
330
+ return {
331
+ exitCode: result.exitCode,
332
+ stdout: result.stdout || '',
333
+ stderr: result.stderr || '',
334
+ error: result.error,
335
+ };
336
+ }
337
+ catch (e) {
338
+ return {
339
+ exitCode: 1,
340
+ stdout: '',
341
+ stderr: '',
342
+ error: `python-runner dispatch failed: ${e?.message || e}`,
343
+ };
344
+ }
345
+ }
346
+ /**
347
+ * Compose the per-call preamble by splicing the pyodide.asm.js source
348
+ * verbatim ahead of the __pyodideRun helper. Workerd compiles this
349
+ * blob as JS at module-load time (where `var` declarations + globals
350
+ * assignment are allowed), then the asm.js's `var _createPyodideModule`
351
+ * is hoisted onto globalThis.
352
+ */
353
+ // REPL-W1: exported so the adjacent src/runtime/python-repl.ts can
354
+ // reuse the canonical preamble verbatim. Additive change only —
355
+ // internal callers are unchanged. The `export` keyword is the sole
356
+ // modification to python-runner.ts in the REPL-W1 wave.
357
+ export function buildPyodidePreamble(asmJsSrc, stdlibB64) {
358
+ return [
359
+ '// ── Pre-asm.js environment shims ───────────────────────────────',
360
+ '// Pyodide.asm.js detects its environment via heuristics. In',
361
+ '// workerd, several detections fire wrong:',
362
+ '// - ENVIRONMENT_IS_NODE: workerd defines process + process.versions.node',
363
+ '// under nodejs_compat → Pyodide tries require("fs"), require("path"),',
364
+ '// require("crypto"), require("ws"), require("child_process"). We',
365
+ '// don\'t want any of those code paths because instantiateWasm is',
366
+ '// overridden anyway.',
367
+ '// - ENVIRONMENT_IS_WORKER (typeof WorkerGlobalScope !== "undefined"):',
368
+ '// true in workerd. Pyodide reads `self.location.href`. workerd has',
369
+ '// `self` (== globalThis) but `self.location` is undefined. Stub it.',
370
+ '// - document.currentScript?.src: workerd has no document; the',
371
+ '// optional-chain on undefined is fine, but we shim it explicitly',
372
+ '// to remove any drift across pyodide versions.',
373
+ '//',
374
+ '// CRITICAL: ENVIRONMENT_IS_NODE is computed inside the async-factory',
375
+ '// returned by the asm.js IIFE — i.e., it\'s evaluated WHEN',
376
+ '// _createPyodideModule(settings) is CALLED, not when the asm.js',
377
+ '// module-init runs. We therefore need to keep globalThis.process =',
378
+ '// undefined across that call, not just the asm.js inline above. The',
379
+ '// __pyodideRun helper below does the save+restore around the call.',
380
+ '// IMPORTANT: env-detection in asm.js happens at the IIFE OUTER scope',
381
+ '// (runs at module-load time, during the asm.js inline below), via',
382
+ '// var f = oe();',
383
+ '// `oe()` reads globalThis.process / typeof WorkerGlobalScope / typeof',
384
+ '// self instanceof WorkerGlobalScope and stores derived flags in `f`',
385
+ '// (captured into closure scope as `d`). Later inside the async factory',
386
+ '// (at request time), loadScript selects on `d.IN_BROWSER_WEB_WORKER`.',
387
+ '//',
388
+ '// So the shims MUST be in place BEFORE the asm.js inline runs.',
389
+ '// __pyodideRun\'s save+restore around _createPyodideModule covers the',
390
+ '// inner async factory call BUT NOT this outer IIFE evaluation. We',
391
+ '// therefore stub at module-load too, save the originals, and restore',
392
+ '// AT THE END OF THE PREAMBLE (after the asm.js inline returns).',
393
+ 'const __nimbusOrigProcess = globalThis.process;',
394
+ 'const __nimbusOrigWGS = globalThis.WorkerGlobalScope;',
395
+ 'try { globalThis.process = undefined; } catch (e) { /* non-writable; fall through */ }',
396
+ '// Defense in depth: if globalThis.process survived the = undefined',
397
+ '// (workerd treats it as non-configurable in some setups), mark it',
398
+ '// browser-like so Pyodide\'s `!process.browser` check fails → IN_NODE = false.',
399
+ 'try {',
400
+ ' if (globalThis.process && typeof globalThis.process === "object") globalThis.process.browser = true;',
401
+ '} catch (e) { /* fail-soft */ }',
402
+ 'globalThis.WorkerGlobalScope = Object;',
403
+ 'if (typeof globalThis.location !== "object" || globalThis.location === null) {',
404
+ ' globalThis.location = { href: "pyodide://nimbus/", origin: "pyodide://nimbus", toString() { return this.href; } };',
405
+ '}',
406
+ 'if (typeof globalThis.document === "undefined") globalThis.document = undefined;',
407
+ 'if (typeof globalThis.self === "undefined") globalThis.self = globalThis;',
408
+ '',
409
+ '// pyodide_js_init() inside the asm.js IIFE constructs a FinalizationRegistry',
410
+ '// to bridge JS-side GC to Python ref-cleanup. workerd does not expose',
411
+ '// FinalizationRegistry by default (it is gated behind the `enable_weak_ref`',
412
+ '// compat flag, on by default after 2025-05-05). For older compat dates',
413
+ '// the constructor is undefined and pyodide_js_init throws ReferenceError.',
414
+ '// Provide a no-op class shim — registered callbacks are simply never',
415
+ '// invoked. This is memory-leaky for long-lived workers (Python objects',
416
+ '// holding JS proxies will be retained beyond their natural lifetime),',
417
+ '// but acceptable for v1 (per-request bootstrap; workerd reaps on isolate',
418
+ '// reuse anyway). Future v3: switch to compat_date >= 2025-05-05 and drop.',
419
+ 'if (typeof globalThis.FinalizationRegistry === "undefined") {',
420
+ ' globalThis.FinalizationRegistry = class FinalizationRegistry {',
421
+ ' constructor(_cleanup) {}',
422
+ ' register(_target, _heldValue, _token) {}',
423
+ ' unregister(_token) {}',
424
+ ' };',
425
+ '}',
426
+ '',
427
+ '// ── BEGIN: pyodide.asm.js (inlined; ~1 MiB) ─────────────────────',
428
+ '// Module-load time evaluation. Declares `var _createPyodideModule`',
429
+ '// at module scope; the next line hoists it onto globalThis so the',
430
+ '// __pyodideRun helper below can reach it across slot reuses.',
431
+ asmJsSrc,
432
+ 'if (typeof _createPyodideModule === \'function\') {',
433
+ ' globalThis._createPyodideModule = _createPyodideModule;',
434
+ '}',
435
+ '// Restore originals after the asm.js IIFE outer captures env flags.',
436
+ '// (The bootstrap promise below re-hides them inside its own async',
437
+ '// scope so the env-detection inside the async factory body sees the',
438
+ '// stubbed values.)',
439
+ 'try { globalThis.process = __nimbusOrigProcess; } catch (e) { /* fall through */ }',
440
+ 'try { if (globalThis.process && globalThis.process.browser === true) delete globalThis.process.browser; } catch (e) {}',
441
+ 'globalThis.WorkerGlobalScope = __nimbusOrigWGS;',
442
+ '// ── END: pyodide.asm.js inline ──────────────────────────────────',
443
+ '',
444
+ buildPreambleTail(stdlibB64),
445
+ ].join('\n');
446
+ }
447
+ // ── Facet preamble ───────────────────────────────────────────────────
448
+ //
449
+ // The preamble runs at facet module-init (workerd compiles it once
450
+ // per slot). It exposes __pyodideRun(args) on globalThis which the
451
+ // dispatched facet fn calls.
452
+ //
453
+ // Strategy mirrors Cloudflare's `python-entrypoint-helper.ts`:
454
+ // 1. Decode pyodide.asm.js bytes from context → JS source string.
455
+ // 2. new Function(asmJsSrc + '; return _createPyodideModule')()
456
+ // — runs the Emscripten loader, returns the factory. workerd
457
+ // allows new Function at module-init time.
458
+ // 3. Build the Emscripten settings object directly (skip the
459
+ // public loadPyodide wrapper — we don't need its file-system
460
+ // probing, lockfile loader, indexURL juggling). Override:
461
+ // - instantiateWasm: feed precompiled module from __NIMBUS_WASM
462
+ // - preRun: write python_stdlib.zip into MEMFS at /lib/python313.zip
463
+ // - print / printErr: capture to stdout/stderr buffers
464
+ // - onExit: capture exit code
465
+ // - stdin: feed user input if any
466
+ // - arguments_: sys.argv
467
+ // - thisProgram: progName (becomes sys.argv[0])
468
+ // - ENV: user env vars
469
+ // 4. Call _createPyodideModule(settings) → Pyodide instance
470
+ // 5. Run user code via Pyodide.runPython
471
+ // 6. Return captured stdout/stderr/exitCode
472
+ // pyodide.asm.js is inlined ABOVE this preamble by
473
+ // buildPyodidePreamble(asmJsSrc). It declares 'var
474
+ // _createPyodideModule = (() => { ... })()' at module-top and we
475
+ // hoist it onto globalThis right after the inline. By the time
476
+ // __pyodideRun is invoked at request time, globalThis._createPyodideModule
477
+ // is already populated; we never invoke `new Function` (CSP-blocked
478
+ // at request time).
479
+ // ── Preamble tail builder ─────────────────────────────────────────────
480
+ //
481
+ // Generates the preamble tail with stdlibB64 spliced as a constant so
482
+ // the bootstrap can run at child-facet module-init time (not request
483
+ // time). Module-init time is where workerd permits
484
+ // `new WebAssembly.Module(rawBytes)`, which Pyodide's
485
+ // convertJsFunctionToWasm uses to build JS-callback-to-wasm shims.
486
+ // Without module-init context, that throws CompileError and the
487
+ // bootstrap promise never resolves → workerd cancels the request as
488
+ // hung. (This v2 redesign was directly motivated by the
489
+ // /tmp/pyodide-smoketest finding — see
490
+ // /workspace/.seal-internal/2026-05-11-pyodide-v2/smoketest-result.md.)
491
+ //
492
+ // Architecture:
493
+ //
494
+ // PREAMBLE (runs at child-facet module-init):
495
+ // ┌─────────────────────────────────────────────────────────────┐
496
+ // │ - env shims (FinalizationRegistry, process, WGS, location) │
497
+ // │ - asm.js eval (defines _createPyodideModule) │
498
+ // │ - stdlibB64 constant + decoded stdlibBytes │
499
+ // │ - sentinel wasm module compiled │
500
+ // │ - globalThis.__pyodideBootstrap = (async () => { ... })() │
501
+ // │ ├─ preRun: installStdlib, setHome, setEnv, │
502
+ // │ │ initializeNativeFS, gateRuntimeInit │
503
+ // │ ├─ _createPyodideModule(settings) called │
504
+ // │ ├─ Awaits at gate (until request time) │
505
+ // │ └─ Resolves with pyodideMod after gate release │
506
+ // └─────────────────────────────────────────────────────────────┘
507
+ //
508
+ // __pyodideRun(args) (runs at child-facet request handler):
509
+ // ┌─────────────────────────────────────────────────────────────┐
510
+ // │ - Release the bootstrap gate (crypto.getRandomValues OK now)│
511
+ // │ - Await __pyodideBootstrap → pyodideMod │
512
+ // │ - finalizeBootstrap → pyodide │
513
+ // │ - runPython(userCode) → stdout/stderr/exitCode │
514
+ // └─────────────────────────────────────────────────────────────┘
515
+ //
516
+ function buildPreambleTail(stdlibB64) {
517
+ return `
518
+ // ── BEGIN: python-runner preamble tail (Pyodide 0.29.4, Nimbus v2) ──
519
+
520
+ // Stdlib bytes spliced into preamble at facet-build time. ~3.2 MiB
521
+ // base64 text — decoded once at module-init. Same content as
522
+ // share/pyodide/python_stdlib.zip in the runtime cache.
523
+ const __NIMBUS_STDLIB_B64 = ${JSON.stringify(stdlibB64)};
524
+
525
+ // Decode base64 → Uint8Array. Run at module-init time (synchronous).
526
+ const __nimbusStdlibBytes = (function decode(b64) {
527
+ const bin = atob(b64);
528
+ const u8 = new Uint8Array(bin.length);
529
+ for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i);
530
+ return u8;
531
+ })(__NIMBUS_STDLIB_B64);
532
+
533
+ // Sentinel-module setup. Pyodide's pyodide.mjs compiles a tiny standalone
534
+ // wasm blob that exports {create_sentinel, is_sentinel} and attaches its
535
+ // exports as imports.sentinel BEFORE instantiating pyodide.asm.wasm. We
536
+ // bypass loadPyodide → must replicate here. The base64 blob below is
537
+ // verbatim from pyodide.mjs (variable \`G\` in v0.29.4).
538
+ const __NIMBUS_SENTINEL_WASM_B64 = 'AGFzbQEAAAABDANfAGAAAW9gAW8BfwMDAgECByECD2NyZWF0ZV9zZW50aW5lbAAAC2lzX3NlbnRpbmVsAAEKEwIHAPsBAPsbCwkAIAD7GvsUAAs=';
539
+ let __nimbusSentinelExports;
540
+ try {
541
+ const bin = atob(__NIMBUS_SENTINEL_WASM_B64);
542
+ const sentinelBytes = new Uint8Array(bin.length);
543
+ for (let i = 0; i < bin.length; i++) sentinelBytes[i] = bin.charCodeAt(i);
544
+ // Synchronous wasm compile at module-init (CSP permits here).
545
+ const sentinelMod = new WebAssembly.Module(sentinelBytes);
546
+ const sentinelInst = new WebAssembly.Instance(sentinelMod);
547
+ __nimbusSentinelExports = sentinelInst.exports;
548
+ } catch (_e) {
549
+ // CSP fallback — pyodide.mjs's K() also has this Symbol-based path.
550
+ const marker = Symbol('sentinel');
551
+ __nimbusSentinelExports = {
552
+ create_sentinel: function() { return marker; },
553
+ is_sentinel: function(v) { return v === marker; },
554
+ };
555
+ }
556
+
557
+ // Module-init stdout/stderr capture buffers. Used by Pyodide's print/
558
+ // printErr (set in settings below). The per-call __pyodideRun grabs a
559
+ // slice from these buffers to isolate output per invocation.
560
+ globalThis.__nimbusPyStdout = globalThis.__nimbusPyStdout || [];
561
+ globalThis.__nimbusPyStderr = globalThis.__nimbusPyStderr || [];
562
+
563
+ // Bootstrap gate release fn — populated by gateRuntimeInit preRun hook
564
+ // during _createPyodideModule's synchronous preRun pass.
565
+ globalThis.__nimbusReleaseGate = null;
566
+
567
+ // ── Bootstrap promise (kicked off at child-facet module-init) ────────
568
+ //
569
+ // CRITICAL: this promise is created at module-init time so the
570
+ // _createPyodideModule call (and its synchronous wasm-module-compile
571
+ // side effects, including convertJsFunctionToWasm) happens in
572
+ // startup-CSP context. The promise body's awaits run as microtasks
573
+ // after module-init returns, but the SYNCHRONOUS portion of
574
+ // _createPyodideModule (wasm instantiate + preRun + reportUndefinedSymbols
575
+ // + convertJsFunctionToWasm) all completes before the first await.
576
+ //
577
+ // We hide process + WGS inside this async function so the env-detection
578
+ // inside Pyodide's async factory body sees the stubbed values, then
579
+ // restore them in the finally block.
580
+ globalThis.__pyodideBootstrap = (async function nimbusBootstrap() {
581
+ if (typeof globalThis._createPyodideModule !== 'function') {
582
+ return { ok: false, error: '_createPyodideModule not installed by inline asm.js' };
583
+ }
584
+ const __origProcess = globalThis.process;
585
+ const __origWGS = globalThis.WorkerGlobalScope;
586
+ try { globalThis.process = undefined; } catch {}
587
+ try {
588
+ if (globalThis.process && typeof globalThis.process === 'object') {
589
+ globalThis.process.browser = true;
590
+ }
591
+ } catch {}
592
+ try { globalThis.WorkerGlobalScope = Object; } catch {}
593
+
594
+ try {
595
+ // Get the wasm module that the loader-pool injected via __NIMBUS_WASM.
596
+ const wasmTable = globalThis.__NIMBUS_WASM || {};
597
+ const asmWasmMod = wasmTable['pyodide.asm.wasm'];
598
+ if (!asmWasmMod) {
599
+ return { ok: false, error: '__NIMBUS_WASM missing pyodide.asm.wasm' };
600
+ }
601
+
602
+ // Initial Pyodide config — note: args, env, progName come from the
603
+ // per-call args at request time. We use sensible defaults here so
604
+ // the bootstrap can complete; per-call __pyodideRun overrides via
605
+ // M.ENV / pyodide.runPython arguments.
606
+ const config = {
607
+ indexURL: '/pyodide/',
608
+ fullStdLib: false,
609
+ jsglobals: globalThis,
610
+ args: [],
611
+ env: { HOME: '/home/pyodide', PYTHONINSPECT: '1' },
612
+ packages: [],
613
+ lockFileContents: '{"packages":{}}',
614
+ packageCacheDir: undefined,
615
+ enableRunUntilComplete: true,
616
+ checkAPIVersion: false,
617
+ _sysExecutable: 'python',
618
+ BUILD_ID: 'nimbus-pyodide-0.29.4',
619
+ };
620
+
621
+ const settings = {
622
+ noInitialRun: false,
623
+ noExitRuntime: true,
624
+ noImageDecoding: true,
625
+ noAudioDecoding: true,
626
+ noWasmDecoding: false,
627
+ print: function(s) { globalThis.__nimbusPyStdout.push(s + '\\n'); },
628
+ printErr: function(s) { globalThis.__nimbusPyStderr.push(s + '\\n'); },
629
+ // onExit captures the SystemExit code from Pyodide via Emscripten's
630
+ // ExitStatus path. The per-call __pyodideRun resets __nimbusExitCode
631
+ // to undefined before runPython and reads it after.
632
+ onExit: function(code) { globalThis.__nimbusExitCode = code | 0; },
633
+ thisProgram: config._sysExecutable,
634
+ arguments: config.args,
635
+ API: { config: config, runtimeEnv: { IN_NODE: false, IN_BROWSER: false, IN_SHELL: false } },
636
+ locateFile: function(path) { return '/pyodide/' + path; },
637
+ instantiateWasm: function(imports, successCallback) {
638
+ // Attach sentinel namespace synchronously — Pyodide expects
639
+ // imports.sentinel = {create_sentinel, is_sentinel}.
640
+ imports.sentinel = __nimbusSentinelExports;
641
+ WebAssembly.instantiate(asmWasmMod, imports).then(function(result) {
642
+ const inst = (result instanceof WebAssembly.Instance ? result : result.instance);
643
+ try {
644
+ successCallback(inst, asmWasmMod);
645
+ } catch (cbErr) {
646
+ globalThis.__nimbusPyStderr.push('[python-runner] receiveInstance threw: ' + (cbErr && cbErr.message) + '\\n');
647
+ throw cbErr;
648
+ }
649
+ }).catch(function(e) {
650
+ globalThis.__nimbusPyStderr.push('[python-runner] wasm instantiate failed: ' + (e && e.message) + '\\n');
651
+ });
652
+ return {};
653
+ },
654
+ };
655
+
656
+ // Faithful replication of pyodide.mjs's
657
+ // getFileSystemInitializationFuncs ordering, plus our request-gate.
658
+ settings.preRun = [
659
+ function installStdlib(M) {
660
+ M.addRunDependency('nimbus-install-stdlib');
661
+ try {
662
+ const verWord = M.HEAPU32[M._Py_Version >>> 2];
663
+ const major = (verWord >>> 24) & 0xff;
664
+ const minor = (verWord >>> 16) & 0xff;
665
+ M.API.pyVersionTuple = [major, minor, (verWord >>> 8) & 0xff];
666
+ M.FS.mkdirTree('/lib');
667
+ M.API.sitePackages = '/lib/python' + major + '.' + minor + '/site-packages';
668
+ M.FS.mkdirTree(M.API.sitePackages);
669
+ M.FS.writeFile('/lib/python' + major + minor + '.zip', __nimbusStdlibBytes);
670
+ } catch (e) {
671
+ globalThis.__nimbusPyStderr.push('[python-runner] installStdlib failed: ' + (e && e.message) + '\\n');
672
+ } finally {
673
+ M.removeRunDependency('nimbus-install-stdlib');
674
+ }
675
+ },
676
+ function setHome(M) {
677
+ let home = config.env.HOME;
678
+ try { M.FS.mkdirTree(home); } catch { home = '/'; }
679
+ try { M.FS.chdir(home); } catch {}
680
+ },
681
+ function setEnv(M) {
682
+ try { Object.assign(M.ENV, config.env); } catch {}
683
+ },
684
+ function initializeNativeFS(M) {
685
+ try {
686
+ M.FS.filesystems.NATIVEFS_ASYNC = {
687
+ mount: function() { throw new Error('NATIVEFS_ASYNC not implemented'); },
688
+ syncfs: function(_m, _p, cb) { cb(); },
689
+ };
690
+ } catch {}
691
+ },
692
+ function gateRuntimeInit(M) {
693
+ // Adds a runDependency that defers Pyodide's doRun (callMain →
694
+ // CPython __wasm_call_ctors → crypto.getRandomValues — blocked
695
+ // at module-init) until our request handler releases it.
696
+ M.addRunDependency('nimbus-request-gate');
697
+ globalThis.__nimbusReleaseGate = function() {
698
+ try { M.removeRunDependency('nimbus-request-gate'); } catch {}
699
+ };
700
+ },
701
+ ];
702
+
703
+ // Stash settings on globalThis so __pyodideRun can mutate config
704
+ // (args/env/progName) at request time before runPython.
705
+ globalThis.__nimbusPyConfig = config;
706
+
707
+ // Kick off _createPyodideModule. Its synchronous portion runs all
708
+ // preRun hooks (registering the gate) and instantiates the wasm
709
+ // module (including convertJsFunctionToWasm in
710
+ // reportUndefinedSymbols — the v1 hang root). The returned promise
711
+ // resolves when the gate is released at request time.
712
+ const modPromise = globalThis._createPyodideModule(settings);
713
+ const pyodideMod = await modPromise;
714
+ return { ok: true, mod: pyodideMod };
715
+ } catch (e) {
716
+ return { ok: false, error: '_createPyodideModule failed: ' + (e && e.message), stack: e && e.stack };
717
+ } finally {
718
+ try { globalThis.process = __origProcess; } catch {}
719
+ try {
720
+ if (globalThis.process && globalThis.process.browser === true) {
721
+ delete globalThis.process.browser;
722
+ }
723
+ } catch {}
724
+ globalThis.WorkerGlobalScope = __origWGS;
725
+ }
726
+ })();
727
+
728
+ // ── Per-call entry point ─────────────────────────────────────────────
729
+ //
730
+ // Invoked from the LOADER child facet's execute() (which calls the
731
+ // serialized facetFn that does globalThis.__pyodideRun(args)).
732
+ //
733
+ // At this point the bootstrap promise is mid-flight at the gate.
734
+ // We release the gate, await the bootstrap completion (which now
735
+ // runs CPython init in request-handler CSP context where
736
+ // crypto.getRandomValues is permitted), then finalizeBootstrap +
737
+ // runPython.
738
+ globalThis.__pyodideRun = async function __pyodideRun(args) {
739
+ // Tracks where in the user's request output begins (the bootstrap
740
+ // may have produced some print() output too, but in practice it
741
+ // doesn't — CPython's Py_Initialize is silent).
742
+ const stdoutStart = globalThis.__nimbusPyStdout.length;
743
+ const stderrStart = globalThis.__nimbusPyStderr.length;
744
+
745
+ // Override config args/env/progName for THIS call. The bootstrap
746
+ // used defaults; the user's actual sys.argv and env are applied here.
747
+ // setEnv preRun ran with the old config — we re-apply onto M.ENV
748
+ // via runPython below.
749
+ if (globalThis.__nimbusPyConfig) {
750
+ globalThis.__nimbusPyConfig.args = args.pyArgv.slice(1);
751
+ globalThis.__nimbusPyConfig.env = Object.assign({}, globalThis.__nimbusPyConfig.env, args.userEnv || {});
752
+ globalThis.__nimbusPyConfig._sysExecutable = args.progName;
753
+ }
754
+
755
+ // Release the bootstrap gate (registered by gateRuntimeInit preRun).
756
+ // Required: workerd blocks crypto.getRandomValues at module-init.
757
+ // Releasing at request time lets Pyodide's doRun → callMain →
758
+ // __wasm_call_ctors → randomFill run in request-handler context.
759
+ if (typeof globalThis.__nimbusReleaseGate === 'function') {
760
+ globalThis.__nimbusReleaseGate();
761
+ // Make idempotent so a re-entrant call doesn't double-release.
762
+ globalThis.__nimbusReleaseGate = function() {};
763
+ }
764
+
765
+ // Await bootstrap completion.
766
+ const boot = await globalThis.__pyodideBootstrap;
767
+ if (!boot.ok || !boot.mod) {
768
+ return {
769
+ exitCode: 1,
770
+ stdout: globalThis.__nimbusPyStdout.slice(stdoutStart).join(''),
771
+ stderr: globalThis.__nimbusPyStderr.slice(stderrStart).join(''),
772
+ error: 'pyodide bootstrap failed: ' + (boot.error || 'unknown'),
773
+ };
774
+ }
775
+ const pyodideMod = boot.mod;
776
+
777
+ // Apply user env to MEMFS now that CPython is up. (The preRun setEnv
778
+ // ran with bootstrap defaults; we layer the user env on top.)
779
+ if (args.userEnv) {
780
+ try { Object.assign(pyodideMod.ENV, args.userEnv); } catch {}
781
+ }
782
+
783
+ // finalizeBootstrap returns the public Pyodide JS API (the one with
784
+ // .runPython, .globals, .registerJsModule). It registers Python-side
785
+ // js-module hooks via register_js_finder; calling it twice on the
786
+ // same pyodideMod throws "JsFinder already registered". So we cache
787
+ // the result globally and reuse it across multiple __pyodideRun
788
+ // invocations (which happen when the child-facet pool reuses the
789
+ // same Pyodide instance for multiple python -c calls in one session).
790
+ let pyodide = globalThis.__nimbusPyodideInstance;
791
+ if (!pyodide) {
792
+ try {
793
+ pyodide = pyodideMod.API.finalizeBootstrap(undefined, undefined);
794
+ globalThis.__nimbusPyodideInstance = pyodide;
795
+ } catch (e) {
796
+ return {
797
+ exitCode: 1,
798
+ stdout: globalThis.__nimbusPyStdout.slice(stdoutStart).join(''),
799
+ stderr: globalThis.__nimbusPyStderr.slice(stderrStart).join(''),
800
+ error: 'finalizeBootstrap failed: ' + (e && e.message),
801
+ };
802
+ }
803
+ }
804
+
805
+ // Update sys.argv to reflect this call's progName + pyArgv. Pyodide's
806
+ // thisProgram is fixed at bootstrap time (when callMain ran); we
807
+ // overwrite sys.argv at request time via a tiny runPython prelude.
808
+ // pyArgv is the user-visible argv (script name first, then args);
809
+ // progName is the executable that prefixes errors.
810
+ try {
811
+ const argv = [args.progName].concat((args.pyArgv || []).slice(0));
812
+ // If pyArgv already includes the script as [0], use it directly.
813
+ // Convention from the python-runner caller: pyArgv = [progName,
814
+ // ...args]. We set sys.argv = pyArgv with [0] = progName.
815
+ const effectiveArgv = (args.pyArgv && args.pyArgv.length > 0) ? args.pyArgv : argv;
816
+ pyodide.runPython(
817
+ 'import sys\\nsys.argv = ' +
818
+ JSON.stringify(effectiveArgv)
819
+ );
820
+ } catch { /* best-effort */ }
821
+
822
+ // Reset onExit capture for this call.
823
+ globalThis.__nimbusExitCode = undefined;
824
+
825
+ // Run the user code.
826
+ let exitCode = 0;
827
+ if (args.userCode) {
828
+ try {
829
+ pyodide.runPython(args.userCode);
830
+ // If runPython returned normally, exit code is 0 unless onExit
831
+ // was invoked (which can happen if user called sys.exit but
832
+ // Pyodide handled it via ExitStatus before throwing).
833
+ if (typeof globalThis.__nimbusExitCode === 'number') {
834
+ exitCode = globalThis.__nimbusExitCode;
835
+ }
836
+ } catch (e) {
837
+ // Check onExit-captured code first (set by Emscripten's ExitStatus
838
+ // path before the throw).
839
+ if (typeof globalThis.__nimbusExitCode === 'number') {
840
+ exitCode = globalThis.__nimbusExitCode;
841
+ } else if (e && typeof e.message === 'string') {
842
+ // Fall back to message parsing. Pyodide surfaces SystemExit as
843
+ // a PythonError with message containing "SystemExit: <code>".
844
+ const m = e.message.match(/SystemExit:\\s*(-?\\d+)/);
845
+ if (m) {
846
+ exitCode = parseInt(m[1], 10);
847
+ } else if (/SystemExit/.test(e.message)) {
848
+ // SystemExit with no numeric arg (e.g., sys.exit() or sys.exit(None))
849
+ exitCode = 0;
850
+ } else {
851
+ globalThis.__nimbusPyStderr.push(e.message + (e.message.endsWith('\\n') ? '' : '\\n'));
852
+ exitCode = 1;
853
+ }
854
+ } else {
855
+ globalThis.__nimbusPyStderr.push('[python-runner] unknown error: ' + e + '\\n');
856
+ exitCode = 1;
857
+ }
858
+ }
859
+ }
860
+
861
+ return {
862
+ exitCode: exitCode,
863
+ stdout: globalThis.__nimbusPyStdout.slice(stdoutStart).join(''),
864
+ stderr: globalThis.__nimbusPyStderr.slice(stderrStart).join(''),
865
+ };
866
+ };
867
+
868
+ // ── END: python-runner preamble ───────────────────────────────────────
869
+ `;
870
+ }