@nimbus-sh/worker 0.12.0 → 0.13.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 (198) hide show
  1. package/dist/esbuild-cli-artifact.generated.d.ts +1 -1
  2. package/dist/esbuild-cli-artifact.generated.js +4 -4
  3. package/dist/esbuild-wasm-bundle.generated.d.ts +11 -10
  4. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
  5. package/dist/esbuild-wasm-bundle.generated.js +11 -10
  6. package/dist/facets/build-facet.d.ts +53 -0
  7. package/dist/facets/build-facet.d.ts.map +1 -0
  8. package/dist/facets/build-facet.js +346 -0
  9. package/dist/facets/cirrus-real.d.ts +3 -0
  10. package/dist/facets/cirrus-real.d.ts.map +1 -1
  11. package/dist/facets/cirrus-real.js +1 -2
  12. package/dist/facets/compose.d.ts +1 -0
  13. package/dist/facets/compose.d.ts.map +1 -1
  14. package/dist/facets/compose.js +3 -0
  15. package/dist/facets/data-plan.d.ts +14 -30
  16. package/dist/facets/data-plan.d.ts.map +1 -1
  17. package/dist/facets/data-plan.js +153 -13
  18. package/dist/facets/dev-stylesheet.d.ts +27 -0
  19. package/dist/facets/dev-stylesheet.d.ts.map +1 -0
  20. package/dist/facets/dev-stylesheet.js +65 -0
  21. package/dist/facets/esbuild-transform.d.ts +23 -18
  22. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  23. package/dist/facets/esbuild-transform.js +63 -109
  24. package/dist/facets/manager.d.ts +49 -9
  25. package/dist/facets/manager.d.ts.map +1 -1
  26. package/dist/facets/manager.js +238 -69
  27. package/dist/facets/oxc-transform.d.ts +29 -0
  28. package/dist/facets/oxc-transform.d.ts.map +1 -0
  29. package/dist/facets/oxc-transform.js +198 -0
  30. package/dist/facets/prebundle-pool.d.ts +23 -0
  31. package/dist/facets/prebundle-pool.d.ts.map +1 -0
  32. package/dist/facets/prebundle-pool.js +55 -0
  33. package/dist/facets/process.d.ts +13 -6
  34. package/dist/facets/process.d.ts.map +1 -1
  35. package/dist/facets/process.js +37 -31
  36. package/dist/facets/transform-store.d.ts +4 -4
  37. package/dist/facets/transform-store.js +4 -4
  38. package/dist/facets/vite-dev-server.d.ts +89 -12
  39. package/dist/facets/vite-dev-server.d.ts.map +1 -1
  40. package/dist/facets/vite-dev-server.js +66 -43
  41. package/dist/hosted/commands.d.ts.map +1 -1
  42. package/dist/hosted/commands.js +20 -4
  43. package/dist/hosted/runtime.d.ts.map +1 -1
  44. package/dist/hosted/runtime.js +7 -2
  45. package/dist/hosted/services.d.ts +3 -3
  46. package/dist/hosted/services.d.ts.map +1 -1
  47. package/dist/hosted/services.js +102 -95
  48. package/dist/hosted/session.d.ts +1 -0
  49. package/dist/hosted/session.d.ts.map +1 -1
  50. package/dist/loaders/generated-workers.d.ts +3 -0
  51. package/dist/loaders/generated-workers.d.ts.map +1 -1
  52. package/dist/loaders/generated-workers.js +3 -0
  53. package/dist/napi-wasm-artifacts.generated.js +2 -2
  54. package/dist/node-shims-artifact.generated.d.ts +14 -1
  55. package/dist/node-shims-artifact.generated.d.ts.map +1 -1
  56. package/dist/node-shims-artifact.generated.js +23 -10
  57. package/dist/npm/bin-links.d.ts +7 -4
  58. package/dist/npm/bin-links.d.ts.map +1 -1
  59. package/dist/npm/bin-links.js +14 -55
  60. package/dist/npm/cache.d.ts +6 -0
  61. package/dist/npm/cache.d.ts.map +1 -1
  62. package/dist/npm/cache.js +26 -4
  63. package/dist/npm/installer.d.ts +1 -1
  64. package/dist/npm/installer.d.ts.map +1 -1
  65. package/dist/npm/installer.js +26 -52
  66. package/dist/npm/pre-bundle-facet.d.ts +15 -117
  67. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  68. package/dist/npm/pre-bundle-facet.js +13 -511
  69. package/dist/oxc-facet-artifact.generated.d.ts +18 -0
  70. package/dist/oxc-facet-artifact.generated.d.ts.map +1 -0
  71. package/dist/oxc-facet-artifact.generated.js +17 -0
  72. package/dist/oxc-wasm-artifact.generated.d.ts +20 -0
  73. package/dist/oxc-wasm-artifact.generated.d.ts.map +1 -0
  74. package/dist/oxc-wasm-artifact.generated.js +19 -0
  75. package/dist/rolldown-facet-artifact.generated.d.ts +18 -0
  76. package/dist/rolldown-facet-artifact.generated.d.ts.map +1 -0
  77. package/dist/rolldown-facet-artifact.generated.js +17 -0
  78. package/dist/router/remote-api.d.ts.map +1 -1
  79. package/dist/router/remote-api.js +9 -0
  80. package/dist/runtime/bash-repl.d.ts.map +1 -1
  81. package/dist/runtime/bash-repl.js +1 -4
  82. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  83. package/dist/runtime/cpython-resident.js +1 -0
  84. package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -23
  85. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  86. package/dist/runtime/esbuild-wasm-bytes.js +30 -25
  87. package/dist/runtime/facet-loader-host.d.ts +5 -4
  88. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  89. package/dist/runtime/facet-loader-host.js +5 -4
  90. package/dist/runtime/node-runner.d.ts +2 -0
  91. package/dist/runtime/node-runner.d.ts.map +1 -1
  92. package/dist/runtime/node-runner.js +1 -0
  93. package/dist/runtime/node-shims-artifact.d.ts +8 -1
  94. package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
  95. package/dist/runtime/node-shims-artifact.js +32 -4
  96. package/dist/runtime/node-shims.d.ts.map +1 -1
  97. package/dist/runtime/node-shims.js +145 -109
  98. package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
  99. package/dist/runtime/opencode-facet-runner.js +6 -2
  100. package/dist/runtime/oxc-wasm-bytes.d.ts +19 -0
  101. package/dist/runtime/oxc-wasm-bytes.d.ts.map +1 -0
  102. package/dist/runtime/oxc-wasm-bytes.js +39 -0
  103. package/dist/runtime/package-manager.js +1 -1
  104. package/dist/runtime/process-logs-api.d.ts.map +1 -1
  105. package/dist/runtime/process-logs-api.js +2 -0
  106. package/dist/runtime/project-fs.d.ts +8 -1
  107. package/dist/runtime/project-fs.d.ts.map +1 -1
  108. package/dist/runtime/project-fs.js +15 -1
  109. package/dist/runtime/python-repl.d.ts +40 -1
  110. package/dist/runtime/python-repl.d.ts.map +1 -1
  111. package/dist/runtime/python-repl.js +19 -11
  112. package/dist/runtime/ruby-repl.d.ts +38 -0
  113. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  114. package/dist/runtime/ruby-repl.js +24 -5
  115. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  116. package/dist/runtime/ruby-resident.js +2 -0
  117. package/dist/session/nimbus-session.d.ts +13 -8
  118. package/dist/session/nimbus-session.d.ts.map +1 -1
  119. package/dist/session/nimbus-session.js +11 -5
  120. package/dist/session/programmatic.d.ts +25 -30
  121. package/dist/session/programmatic.d.ts.map +1 -1
  122. package/dist/session/programmatic.js +65 -93
  123. package/dist/session/routes.d.ts.map +1 -1
  124. package/dist/session/routes.js +23 -7
  125. package/dist/session/rpc.d.ts +16 -6
  126. package/dist/session/rpc.d.ts.map +1 -1
  127. package/dist/session/rpc.js +53 -13
  128. package/dist/session/start-real-vite.d.ts +24 -9
  129. package/dist/session/start-real-vite.d.ts.map +1 -1
  130. package/dist/session/start-real-vite.js +40 -4
  131. package/dist/session/supervisor-op.d.ts +14 -0
  132. package/dist/session/supervisor-op.d.ts.map +1 -1
  133. package/dist/session/supervisor-op.js +49 -0
  134. package/dist/session/supervisor-rpc.d.ts +11 -0
  135. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  136. package/dist/session/supervisor-rpc.js +18 -0
  137. package/dist/session/vite-command.d.ts.map +1 -1
  138. package/dist/session/vite-command.js +23 -19
  139. package/dist/session/ws-relay.d.ts.map +1 -1
  140. package/dist/session/ws-relay.js +13 -1
  141. package/dist/shell/npm-bin-entrypoints.d.ts +3 -1
  142. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  143. package/dist/shell/npm-bin-entrypoints.js +53 -25
  144. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  145. package/dist/vfs/facet-resident-store.js +11 -5
  146. package/dist/wrangler/nimbus-wrangler.d.ts +8 -0
  147. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  148. package/dist/wrangler/nimbus-wrangler.js +54 -57
  149. package/package.json +6 -4
  150. package/public/_assets/esbuild-0.24.2.wasm +0 -0
  151. package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/napi-wasm-loader.mjs +4 -4
  152. package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/provenance.json +2 -2
  153. package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.provenance.json +72 -0
  154. package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.wasm +0 -0
  155. package/public/_assets/runtime/esbuild-cli-f13e6a112b03ac07.js +1143 -0
  156. package/public/_assets/runtime/js-interpreter-3c37c48024f0f651.js +23 -0
  157. package/public/_assets/runtime/js-interpreter-ops-2f4dfec6798e70fc.js +290 -0
  158. package/public/_assets/runtime/js-interpreter-primordials-e620a61b7e9800fa.js +1 -0
  159. package/public/_assets/runtime/{node-shims-f92f16c7d7c532e9.js → node-shims-e3adc4576da31e6e.js} +651 -103
  160. package/public/_assets/runtime/{esbuild-cli-b7aecffd4c6a36fe.js → oxc-facet-fd86fefcdf16bf9c.js} +217 -1047
  161. package/public/_assets/runtime/{resident-store-92386265e8b306dc.js → resident-store-009f39539ac989fc.js} +11 -5
  162. package/public/_assets/runtime/rolldown-facet-09983434e2dbe93e.js +15165 -0
  163. package/public/_assets/runtime/{vfs-write-ledger-d33ee0034446c3e0.js → vfs-write-ledger-9e9594591425a9b8.js} +4 -1
  164. package/scripts/acorn-primordials.mjs +637 -0
  165. package/scripts/bundle-esbuild-wasm.mjs +42 -50
  166. package/scripts/bundle-facet-workers.mjs +247 -18
  167. package/scripts/bundle-node-shims.mjs +29 -2
  168. package/scripts/bundle-oxc-wasm.mjs +105 -0
  169. package/scripts/interpreter-bundle.mjs +105 -0
  170. package/scripts/napi-wasm/build.mjs +4 -1
  171. package/scripts/napi-wasm/loader/napi-wasm-loader.ts +38 -3
  172. package/scripts/oxc-wasm/Cargo.lock +1246 -0
  173. package/scripts/oxc-wasm/Cargo.toml +30 -0
  174. package/scripts/oxc-wasm/build.mjs +249 -0
  175. package/scripts/oxc-wasm/examples/memory.rs +51 -0
  176. package/scripts/oxc-wasm/examples/transform.rs +20 -0
  177. package/scripts/oxc-wasm/inputs.mjs +32 -0
  178. package/scripts/oxc-wasm/rust-toolchain.toml +5 -0
  179. package/scripts/oxc-wasm/src/abi.rs +136 -0
  180. package/scripts/oxc-wasm/src/diagnostics.rs +49 -0
  181. package/scripts/oxc-wasm/src/helpers.rs +139 -0
  182. package/scripts/oxc-wasm/src/lib.rs +247 -0
  183. package/scripts/oxc-wasm/src/module.rs +978 -0
  184. package/scripts/oxc-wasm/src/names.rs +175 -0
  185. package/scripts/oxc-wasm/src/options.rs +154 -0
  186. package/scripts/parser-audit.mjs +492 -0
  187. package/scripts/rolldown-facet/entry.mjs +21 -0
  188. package/scripts/rolldown-facet/shims.mjs +64 -0
  189. package/dist/facets/esbuild-bundle-pool.d.ts +0 -43
  190. package/dist/facets/esbuild-bundle-pool.d.ts.map +0 -1
  191. package/dist/facets/esbuild-bundle-pool.js +0 -80
  192. package/dist/loaders/pre-bundle-preamble.d.ts +0 -44
  193. package/dist/loaders/pre-bundle-preamble.d.ts.map +0 -1
  194. package/dist/loaders/pre-bundle-preamble.js +0 -116
  195. package/dist/runtime/host-wasm.d.ts +0 -8
  196. package/dist/runtime/host-wasm.d.ts.map +0 -1
  197. package/dist/runtime/host-wasm.js +0 -33
  198. /package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/wasi-trampoline.wasm +0 -0
@@ -15,23 +15,25 @@
15
15
  * Long-running processes use a dynamic Worker entrypoint that stays
16
16
  * registered in ProcessTable and PortRegistry until exit or kill.
17
17
  */
18
- import { COMMONJS_CELL_IMPORTS, COMMONJS_CELL_RUNTIME_SOURCE, commonJsCellModuleName, commonJsCellReadsBack, commonJsEntryModuleName, declaresWrapperBinding, runtimeCodeModuleName, runtimeFunctionModule, wrapCommonJsCell, } from '@nimbus-sh/core/_shared/commonjs-cell.js';
18
+ import { COMMONJS_CELL_IMPORTS, COMMONJS_CELL_RUNTIME_SOURCE, commonJsCellModuleName, commonJsCellReadsBack, commonJsEntryModuleName, declaresWrapperBinding, RUNTIME_INTERPRETER_MODULE, RUNTIME_INTERPRETER_OPS_MODULE, RUNTIME_INTERPRETER_PRIMORDIALS_MODULE, runtimeCodeModuleName, runtimeExpressionModule, runtimeFunctionModule, wrapCommonJsCell, } from '@nimbus-sh/core/_shared/commonjs-cell.js';
19
19
  import { ReadAheadBudget, STDIN_SYNC_READ_BYTES } from '@nimbus-sh/core/runtime/stdin-read.js';
20
+ import { execIdField } from '@nimbus-sh/core/runtime/process-table.js';
20
21
  import { exitCodeForSignal } from '@nimbus-sh/core/substrate/lifo/shell/signals.js';
21
22
  import { fetchNodeFacetSources } from '../runtime/node-shims-artifact.js';
22
23
  import { generateSqliteFacetPreamble } from '../runtime/sqlite-shim.js';
23
24
  import { getRealNodeImportsCode } from '@nimbus-sh/core/_shared/real-node-imports.js';
24
25
  import { VFS_CURSOR_SEED_SOURCE, serializeFacetVfsCursor, } from '@nimbus-sh/core/_shared/facet-vfs-cursor.js';
25
- import { stripLeadingSlashes, vfsPathExtension } from '@nimbus-sh/core/vfs/path.js';
26
+ import { normalizeVfsPath, stripLeadingSlashes, vfsPathExtension } from '@nimbus-sh/core/vfs/path.js';
27
+ import { direntTypeOf } from '@nimbus-sh/core/vfs/dirent-type.js';
26
28
  import { clearPortCapability, listPortReservations, readPortReservation, readPortReservationByOwner, releasePortReservation, restoreReservedPortCapability, } from '../session/port-capability.js';
27
29
  import { deriveResidentOwner } from './resident-identity.js';
28
30
  import { z } from 'zod/v4';
29
31
  import { RESIDENT_OWNER_KEY_PREFIX, DURABLE_IMAGES_KEY_PREFIX } from '../session/keys.js';
30
32
  import { PORT_CAPABILITY_KEY_PREFIX } from '../session/keys.js';
31
33
  import { sessionIdentity, unbindPublicPortCapability } from '../router/public-directory.js';
32
- import { prefetchForRequire, requireFsOverBridge, ClosureBoundExceededError } from '@nimbus-sh/core/runtime/require-resolver.js';
34
+ import { prefetchForRequire, requireFsOverBridge, resolveDeferredImport, ClosureBoundExceededError, } from '@nimbus-sh/core/runtime/require-resolver.js';
33
35
  import { findStaticFsReferences } from '@nimbus-sh/core/runtime/static-fs-refs.js';
34
- import { planFacetData } from './data-plan.js';
36
+ import { linkTargetOf, packageRootOf, planFacetData } from './data-plan.js';
35
37
  import { principalTag, profilePrincipal, ReadProfile, verifiedEvidence, } from './read-profile.js';
36
38
  /** What the shared read profile may add to one launch: an eighth of its module map's bytes. */
37
39
  const READ_PROFILE_LAUNCH_BYTES = Math.floor(VFS_BUNDLE_MAX_BYTES / 8);
@@ -49,6 +51,7 @@ import { DEFAULT_CJS_CONDITIONS, DEFAULT_ESM_CONDITIONS, resolvePackageEntry, pa
49
51
  import { isExecDiagEnabled, recordExecTelemetry } from './exec-telemetry.js';
50
52
  import { LaunchLearningStore } from './launch-learning-store.js';
51
53
  import { disposeRpcResource, disposeRpcResources } from '@nimbus-sh/platform/rpc-dispose.js';
54
+ import { SUPERVISOR_ANSWERING_SRC } from '@nimbus-sh/core/runtime/supervisor-answering.generated.js';
52
55
  import { sqliteWasmModuleEntry } from './opencode-staging.js';
53
56
  import { FACET_IMAGE_WRITE_SLICE_BYTES, ImageStore, } from '@nimbus-sh/fabric/image-store.js';
54
57
  import { fetchStagedBindingAsset, NAPI_WASM_LOADER, NAPI_WASM_TRAMPOLINE, STAGED_BINDING_LOADER_MODULE, STAGED_BINDING_TRAMPOLINE_MODULE, stagedBinding, stagedBindingsFacetImport, stagedBindingsRequiredBy, } from '../runtime/staged-bindings.js';
@@ -69,6 +72,10 @@ import { supervisorBindingProps } from '@nimbus-sh/fabric/supervisor-props.js';
69
72
  import { streamedWasmImageDigest, wasmImageDigest } from './wasm-image-digest.js';
70
73
  import { prefetchBundleStart, prefetchBundleEnd, setPrefetchCacheBytes, } from '@nimbus-sh/platform/diag-counters.js';
71
74
  const launchAdapters = new WeakMap();
75
+ /** The exact type of `entry` in `dir` on a launch's fs, lstat'ing it where its listing could not type it. */
76
+ function launchEntryType(vfs, dir, entry) {
77
+ return direntTypeOf(entry, () => vfs.stat(`${dir}/${entry.name}`, { followSymlinks: false }));
78
+ }
72
79
  function filesOf(fs) {
73
80
  let files = launchAdapters.get(fs);
74
81
  if (files === undefined) {
@@ -536,6 +543,19 @@ export function findInlineWasmImages(bundle) {
536
543
  const SQLITE_FACET_IMPORT = `import __nimbusSqliteWasmModule from "${SQLITE_WASM_MODULE_NAME}";\n` +
537
544
  `globalThis.__nimbusSqliteWasmModule = __nimbusSqliteWasmModule;\n` +
538
545
  generateSqliteFacetPreamble();
546
+ /**
547
+ * The runtime-code interpreter, its primordials and its host module, in every
548
+ * launch's map: the primordials load at the launch's start, the other two
549
+ * only when the program first produces code no launch staged
550
+ * (core/_shared/commonjs-cell.ts, RUNTIME CODE).
551
+ */
552
+ function interpreterModules(sources) {
553
+ return {
554
+ [RUNTIME_INTERPRETER_PRIMORDIALS_MODULE]: sources.interpreterPrimordials,
555
+ [RUNTIME_INTERPRETER_MODULE]: sources.interpreter,
556
+ [RUNTIME_INTERPRETER_OPS_MODULE]: sources.interpreterOps,
557
+ };
558
+ }
539
559
  /**
540
560
  * The entry code as a module of the map, named for the script it came from so
541
561
  * its stack frames carry that path; `-e` code is `[eval]`. The runtime may
@@ -565,6 +585,9 @@ ${usesSqlite ? SQLITE_FACET_IMPORT : ''}
565
585
  ${stagedBindingsFacetImport(vfsState.stagedBindings)}
566
586
  ${facetWasmImportsSource(wasmImports)}
567
587
  const __NimbusHostResponse = globalThis.Response;
588
+ // The SUPERVISOR binding's filesystem calls answer a refusal as a value
589
+ // (core vfs-supervisor.ts answeringSupervisor).
590
+ ${SUPERVISOR_ANSWERING_SRC}
568
591
 
569
592
  // The process's code: a module per cell, compiled when first required.
570
593
  const __NIMBUS_CODE_CELLS = ${bundleSource.codeCells};
@@ -601,7 +624,7 @@ ${VFS_CURSOR_SEED_SOURCE}
601
624
  // ends it early.
602
625
  const __entryBudgetMs = Infinity;
603
626
  let __drainPasses = 0;
604
- const __supervisor = workerEnv?.SUPERVISOR || null;
627
+ const __supervisor = workerEnv?.SUPERVISOR ? globalThis.__nimbusAnsweringSupervisor(workerEnv.SUPERVISOR) : null;
605
628
  // The same store, namespace and data plan a resident boots on, backed by
606
629
  // this run's heap (runOnce hosts no SQLite; vfs/facet-resident-store.ts).
607
630
  // Declared inside the request, beside the shims, so a loader that reuses
@@ -688,6 +711,7 @@ ${RESIDENCY_MISS_REPORT}
688
711
  } catch {}
689
712
 
690
713
  const mod = { exports: {} };
714
+ Object.defineProperty(mod, "__nimbusImportMeta", { value: __nimbusFileImportMeta(filename || "/home/user/script.js") });
691
715
  // G2 (runtime-pkg wave): see corresponding comment in NodeProcess.run.
692
716
  __require.main = mod;
693
717
  try {
@@ -706,7 +730,6 @@ ${RESIDENCY_MISS_REPORT}
706
730
  } catch (e) {
707
731
  if (e instanceof __ProcessExit) { exitCode = e.code; }
708
732
  else {
709
- globalThis.__nimbusRuntimeCode.noteFailure(e);
710
733
  const trace = (e && e.stack) || (e && e.message) || String(e);
711
734
  stderr += trace + "\\n";
712
735
  exitCode = 1;
@@ -745,14 +768,6 @@ ${RESIDENCY_MISS_REPORT}
745
768
  if (exitCode === 0) exitCode = 1;
746
769
  if (__supervisor && !captureOutput) __queueRpcWrite("stderr", __nimbusOutEnc.encode(__residencyReport));
747
770
  }
748
- // A failed launch stages the Function-constructor text its failure is
749
- // attributable to (commonjs-cell.ts, stageFailedLaunch).
750
- const __stagedCode = exitCode !== 0 ? globalThis.__nimbusRuntimeCode.stageFailedLaunch() : "";
751
- if (__stagedCode) {
752
- stderr += __stagedCode + "\\n";
753
- if (__supervisor && !captureOutput) __queueRpcWrite("stderr", __nimbusOutEnc.encode(__stagedCode + "\\n"));
754
- }
755
-
756
771
  await __drainPendingIO();
757
772
 
758
773
  if (__supervisor) {
@@ -810,7 +825,7 @@ ${RESIDENCY_MISS_REPORT}
810
825
  };
811
826
  `,
812
827
  modules: bundleSource.modules,
813
- codeModules: { ...bundleSource.codeModules, [entry.name]: entry.text },
828
+ codeModules: { ...bundleSource.codeModules, ...interpreterModules(sources), [entry.name]: entry.text },
814
829
  };
815
830
  }
816
831
  /**
@@ -905,6 +920,9 @@ ${stagedBindingsFacetImport(vfsState.stagedBindings)}
905
920
  ${facetWasmImportsSource(opts.wasmImports ?? [])}
906
921
  const __NIMBUS_ARGS = ${safeArgs};
907
922
  const __NimbusHostResponse = globalThis.Response;
923
+ // The SUPERVISOR binding's filesystem calls answer a refusal as a value
924
+ // (core vfs-supervisor.ts answeringSupervisor).
925
+ ${SUPERVISOR_ANSWERING_SRC}
908
926
 
909
927
  // The process's code: a module per cell, compiled when first required. The
910
928
  // only other way a string becomes code in a Worker is \`new Function\` at
@@ -1006,7 +1024,7 @@ async function __nimbusEnsureStarted(workerEnv, workerCtx, __startArgs) {
1006
1024
  // time. Same reason argv/env/pid want to move here.
1007
1025
  const __MODULE_VFS_CURSOR = (__startArgs && __startArgs.vfsCursor) || null;
1008
1026
  ${VFS_CURSOR_SEED_SOURCE}
1009
- const __supervisor = workerEnv?.SUPERVISOR || null;
1027
+ const __supervisor = workerEnv?.SUPERVISOR ? globalThis.__nimbusAnsweringSupervisor(workerEnv.SUPERVISOR) : null;
1010
1028
  __nimbusRuntimeCodeReporter = () => {
1011
1029
  const report = __nimbusFlushRuntimeCode(__supervisor).catch(async (error) => {
1012
1030
  await __nimbusReportLearningFailure(__supervisor, error);
@@ -1138,6 +1156,7 @@ ${RESIDENCY_MISS_REPORT}
1138
1156
  }
1139
1157
 
1140
1158
  const mod = { exports: {} };
1159
+ Object.defineProperty(mod, "__nimbusImportMeta", { value: __nimbusFileImportMeta(filename || "/home/user/script.js") });
1141
1160
  __require.main = mod;
1142
1161
  let __attachedCompletion = null;
1143
1162
  let __attachedExplicitExit = false;
@@ -1174,7 +1193,6 @@ ${RESIDENCY_MISS_REPORT}
1174
1193
  __attachedExplicitExit = true;
1175
1194
  exitCode = e.code;
1176
1195
  } else {
1177
- globalThis.__nimbusRuntimeCode.noteFailure(e);
1178
1196
  const trace = (e && e.stack) || (e && e.message) || String(e);
1179
1197
  stderr += trace + "\\n";
1180
1198
  exitCode = 1;
@@ -1210,13 +1228,6 @@ ${RESIDENCY_MISS_REPORT}
1210
1228
  if (Number(code ?? 0) === 0) code = 1;
1211
1229
  try { await __supervisor.stderr(__nimbusOutEnc.encode(__residencyReport)); } catch {}
1212
1230
  }
1213
- // A failed launch stages the Function-constructor text its failure is
1214
- // attributable to (commonjs-cell.ts, stageFailedLaunch).
1215
- const __stagedCode = Number(code ?? 0) !== 0 ? globalThis.__nimbusRuntimeCode.stageFailedLaunch() : "";
1216
- if (__stagedCode) {
1217
- stderr += __stagedCode + "\\n";
1218
- try { await __supervisor.stderr(__nimbusOutEnc.encode(__stagedCode + "\\n")); } catch {}
1219
- }
1220
1231
  await __supervisor.reportExit(code, reason || "", __nimbusDataReadMisses(), [...(globalThis.__nimbusProfileStaged || [])], __nimbusRuntimeCodeLedger(), __nimbusExecutedModuleMisses());
1221
1232
  __nimbusProcessExitReported = true;
1222
1233
  };
@@ -1336,7 +1347,7 @@ export class NimbusProcess extends DurableObject {
1336
1347
  }
1337
1348
  `,
1338
1349
  modules: bundleSource.modules,
1339
- codeModules: { ...bundleSource.codeModules, [entry.name]: entry.text },
1350
+ codeModules: { ...bundleSource.codeModules, ...interpreterModules(sources), [entry.name]: entry.text },
1340
1351
  };
1341
1352
  }
1342
1353
  /**
@@ -2019,6 +2030,8 @@ export async function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requir
2019
2030
  if (!((await filesOf(vfs).exists(nmDir)) && (await filesOf(vfs).isDirectory(nmDir))))
2020
2031
  return { added, groups };
2021
2032
  const exts = ['', '.js', '.cjs', '.mjs', '/index.js', '/index.cjs'];
2033
+ // What the admitted groups defer with `import()`; a group does not walk them.
2034
+ const deferred = [];
2022
2035
  async function addOne(path) {
2023
2036
  const stripped = path.replace(/^\/+/, '');
2024
2037
  if ((isBundleModuleCandidate(stripped) || stripped.endsWith('.cjs')) && !isNativeBinPath(stripped)) {
@@ -2033,6 +2046,9 @@ export async function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requir
2033
2046
  });
2034
2047
  if ('kind' in closure || closure.bundle[stripped] === undefined)
2035
2048
  return false;
2049
+ for (const deferral of closure.deferred ?? [])
2050
+ if (deferral.alternatives === 1)
2051
+ deferred.push(deferral);
2036
2052
  for (const [member, cell] of Object.entries(closure.bundle)) {
2037
2053
  if (bundle[member] !== undefined)
2038
2054
  continue;
@@ -2190,7 +2206,7 @@ export async function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requir
2190
2206
  {
2191
2207
  const sibs = await (async () => vfs.readdir(entryDir))().catch(() => []);
2192
2208
  for (const sib of sibs) {
2193
- if (sib.type !== 'file')
2209
+ if ((await launchEntryType(vfs, entryDir, sib)) !== 'file')
2194
2210
  continue;
2195
2211
  // Hash-chunk pattern: <name>.<hash>.<cjs|mjs|js>. Hash must
2196
2212
  // be 6+ chars AND look like a hash, not an English word —
@@ -2218,7 +2234,7 @@ export async function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requir
2218
2234
  const sharedStripped = sharedDir.replace(/^\/+/, '');
2219
2235
  if (await (async () => filesOf(vfs).isDirectory(sharedStripped))().catch(() => false)) {
2220
2236
  for (const sh of await (async () => vfs.readdir(sharedDir))().catch(() => [])) {
2221
- if (sh.type !== 'file')
2237
+ if ((await launchEntryType(vfs, sharedDir, sh)) !== 'file')
2222
2238
  continue;
2223
2239
  if (!/\.(cjs|mjs|js)$/.test(sh.name))
2224
2240
  continue;
@@ -2232,6 +2248,28 @@ export async function greedyAddMainEntries(vfs, cwd, bundle, budgetState, requir
2232
2248
  }
2233
2249
  for (const pkgDir of (await speculativePackageDirs(vfs, cwdStripped, bundle)))
2234
2250
  (await addPkgEntry(pkgDir));
2251
+ // Then the one `import()` a guessed module defers, when it defers only one
2252
+ // and it is a file of its own package, as a guess of its own with its static
2253
+ // closure; what that defers joins the queue. That is how a package proxies
2254
+ // or splits its own code, and it loads whenever the deferring code runs:
2255
+ // vinext imports @vitejs/plugin-rsc by a computed URL (a guess here, as a
2256
+ // project dependency); plugin-rsc imports vitefu, whose CommonJS entry loads
2257
+ // its ESM build with `import('./index.js')`. A module that defers several
2258
+ // chooses among them, and a deferral of another package is an optional
2259
+ // dependency (@vercel/og's `import("sharp")`, caught when absent, whose
2260
+ // 9.2 MB wasm image the launch would compile): a guess does not guess those.
2261
+ const followed = new Set();
2262
+ for (let i = 0; i < deferred.length; i++) {
2263
+ if (rawBytes >= bound || budgetState.fileCount >= VFS_BUNDLE_MAX_FILES)
2264
+ break;
2265
+ const target = await resolveDeferredImport(requireFsOverBridge(vfs), deferred[i], options.pacer?.spend.bind(options.pacer));
2266
+ // Its own package: where the deferral lands, not how it is spelled.
2267
+ const own = packageRootOf(deferred[i].fromDir.replace(/^\/+/, '') + '/_');
2268
+ if (target === null || own === null || packageRootOf(target) !== own || followed.has(target))
2269
+ continue;
2270
+ followed.add(target);
2271
+ await addOne(target);
2272
+ }
2235
2273
  return { added, groups };
2236
2274
  }
2237
2275
  /**
@@ -2760,7 +2798,7 @@ export async function addBinTargetSiblings(vfs, scriptPath, bundle, budgetState,
2760
2798
  if (e.name === '.git')
2761
2799
  continue;
2762
2800
  const child = dir + '/' + e.name;
2763
- if (e.type === 'directory') {
2801
+ if ((await launchEntryType(vfs, dir, e)) === 'directory') {
2764
2802
  if (!shouldVisitBinPackageDirectory(pkgRoot, child, bundleProfile))
2765
2803
  continue;
2766
2804
  queue.push(child);
@@ -3120,10 +3158,11 @@ async function addCwdProjectFiles(vfs, cwd, bundle, budgetState) {
3120
3158
  visited++;
3121
3159
  if (e.name === '.' || e.name === '..')
3122
3160
  continue;
3123
- if (e.type === 'directory' && SKIP_DIRS.has(e.name))
3161
+ const isDirectory = (await launchEntryType(vfs, dir, e)) === 'directory';
3162
+ if (isDirectory && SKIP_DIRS.has(e.name))
3124
3163
  continue;
3125
3164
  const child = dir + '/' + e.name;
3126
- if (e.type === 'directory') {
3165
+ if (isDirectory) {
3127
3166
  queue.push(child);
3128
3167
  continue;
3129
3168
  }
@@ -3316,6 +3355,61 @@ async function transformEsmInBundle(bundle, emits, lowered, esbuild, pacer, stor
3316
3355
  lowered.add(path);
3317
3356
  });
3318
3357
  }
3358
+ /** A tool's config file: `<tool>.config.js|ts|mjs|cjs|mts|cts` (vite.config.ts, astro.config.mjs). */
3359
+ const TOOL_CONFIG = /^(.+)\.config\.(?:c|m)?(?:j|t)s$/;
3360
+ /**
3361
+ * The working dir's config files of the tool a launch runs. The tool
3362
+ * executes them (Vite bundles vite.config.ts and imports the result), so
3363
+ * what they import is code that nothing in the program's own graph names,
3364
+ * and a first run would miss it. They are optional roots: a command may
3365
+ * never read its config (`vite --version`), so a config graph past the
3366
+ * map's bound is cut, never a refusal. A config is the tool's when it is
3367
+ * named for the launched package or a package that one depends on (Astro
3368
+ * runs Vite, Vite runs PostCSS); a config of a tool the launch does not run
3369
+ * (eslint.config.js beside Vite) is not. What a config names by a string
3370
+ * (PostCSS's plugins) the tool loads by name: those join the walk's phase 2.
3371
+ */
3372
+ export async function toolConfigRoots(vfs, cwd, scriptPath) {
3373
+ if (scriptPath === undefined)
3374
+ return [];
3375
+ // A bin is usually launched by its node_modules/.bin link.
3376
+ let script;
3377
+ try {
3378
+ script = await vfs.realpath(scriptPath);
3379
+ }
3380
+ catch {
3381
+ return [];
3382
+ }
3383
+ const root = packageRootOf(script.replace(/^\/+/, ''));
3384
+ if (root === null)
3385
+ return [];
3386
+ let manifest;
3387
+ try {
3388
+ manifest = JSON.parse(await filesOf(vfs).readFileString(root + '/package.json'));
3389
+ }
3390
+ catch {
3391
+ return [];
3392
+ }
3393
+ const tools = new Set(typeof manifest.name === 'string' ? [manifest.name] : []);
3394
+ for (const field of ['dependencies', 'peerDependencies', 'optionalDependencies']) {
3395
+ const named = manifest[field];
3396
+ if (named && typeof named === 'object')
3397
+ for (const name of Object.keys(named))
3398
+ tools.add(name);
3399
+ }
3400
+ let entries;
3401
+ try {
3402
+ entries = await vfs.readdir(cwd);
3403
+ }
3404
+ catch {
3405
+ return [];
3406
+ }
3407
+ const dir = cwd.replace(/^\/+/, '').replace(/\/+$/, '');
3408
+ return entries.flatMap((entry) => {
3409
+ const tool = entry.type === 'file' ? TOOL_CONFIG.exec(entry.name)?.[1] : undefined;
3410
+ return tool !== undefined && tools.has(tool) ? [{ path: (dir ? dir + '/' : '') + entry.name, config: true }] : [];
3411
+ });
3412
+ }
3319
3413
  /**
3320
3414
  * W2.6a: build the prefetch bundle for FacetManager.exec.
3321
3415
  *
@@ -3360,8 +3454,10 @@ async function _buildPrefetchBundle(vfs, { scriptPath, cwd, entryCode, esbuild,
3360
3454
  // executable map. A module that arrives with its text (Vite unlinks its
3361
3455
  // temporary config after loading it) is walked from that text. Files runs
3362
3456
  // only READ are observed reads below: data, never roots, whatever their
3363
- // extension (Tailwind scans .js/.ts content files as text).
3364
- const prefetch = (await prefetchForRequire(requireFsOverBridge(vfs), entryCode || '', cwd, scriptPath, maxBundleBytes, pacer?.spend.bind(pacer), undefined, executedModules && executedModules.length > 0 ? executedModules : undefined));
3457
+ // extension (Tailwind scans .js/.ts content files as text). The tool's
3458
+ // configs ride along, as optional roots (RequiredModuleRoot.config).
3459
+ const requiredRoots = [...await toolConfigRoots(vfs, cwd, scriptPath), ...executedModules ?? []];
3460
+ const prefetch = (await prefetchForRequire(requireFsOverBridge(vfs), entryCode || '', cwd, scriptPath, maxBundleBytes, pacer?.spend.bind(pacer), undefined, requiredRoots.length > 0 ? requiredRoots : undefined));
3365
3461
  if ('kind' in prefetch) {
3366
3462
  // A required closure larger than the bound can never launch as a
3367
3463
  // snapshot. Surface it as the process's own failure rather than a
@@ -3772,9 +3868,10 @@ export class FacetManager {
3772
3868
  /** Per resident pid: the shared read profile's entries its launch staged, settled at its exit. */
3773
3869
  residentProfileOffers = new Map();
3774
3870
  /**
3775
- * The esbuild the bundle's ESM→CJS pass transforms with. composeFacetManager
3871
+ * The service the bundle's ESM→CJS pass transforms with. composeFacetManager
3776
3872
  * sets it: the host's own, or one whose transforms run in the session's
3777
- * esbuild facet. Never one of this isolate: esbuild-wasm's heap only grows.
3873
+ * transform facet. Never one of this isolate: a transform engine's wasm
3874
+ * memory only grows.
3778
3875
  */
3779
3876
  esbuild = null;
3780
3877
  /**
@@ -3879,8 +3976,11 @@ export class FacetManager {
3879
3976
  + `"${record.command}" was ${residentLaunchDoing(record)} — restarting it]\x1b[0m\r\n`),
3880
3977
  onAbandoned: (record) => this.hooks.notify?.('\x1b[2m[nimbus: the session restarted again while '
3881
3978
  + `"${record.command}" was ${residentLaunchDoing(record)} — leaving it stopped]\x1b[0m\r\n`),
3882
- onRedriveFailed: (record, e) => this.hooks.notify?.(`\x1b[2m[nimbus: "${record.command}" could not be restarted: `
3883
- + `${errorMessage(e)}]\x1b[0m\r\n`),
3979
+ onRedriveFailed: (record, e) => {
3980
+ console.warn(`[facet-manager] resident pid ${record.pid} ("${record.command}") not re-driven: ${errorMessage(e)}`);
3981
+ this.hooks.notify?.(`\x1b[2m[nimbus: "${record.command}" could not be restarted: `
3982
+ + `${errorMessage(e)}]\x1b[0m\r\n`);
3983
+ },
3884
3984
  });
3885
3985
  this.launchPump = new PacedWork(ctx, {
3886
3986
  requestTurn: hooks.requestLaunchTurn?.bind(hooks),
@@ -4064,7 +4164,7 @@ export class FacetManager {
4064
4164
  * The entry script as the facet compiles it: each dynamic `import()` routed
4065
4165
  * to the process's ESM loader, with the entry's own URL as the parent (Node
4066
4166
  * names `-e` code `<cwd>/[eval]` and stdin `<cwd>/[stdin]`). The parse runs
4067
- * in the esbuild facet like every cell's, and its result is kept by content
4167
+ * in the transform facet like every cell's, and its result is kept by content
4068
4168
  * in the session's transform store. The module-map walk reads the script as
4069
4169
  * written, before this.
4070
4170
  */
@@ -4316,15 +4416,7 @@ export class FacetManager {
4316
4416
  return null;
4317
4417
  }
4318
4418
  },
4319
- // A missing or unreadable component is not a link: the lookup ends there.
4320
- readlink: async (path) => {
4321
- try {
4322
- return await vfs.readlink(path);
4323
- }
4324
- catch {
4325
- return null;
4326
- }
4327
- },
4419
+ linkTarget: (path) => linkTargetOf(vfs, path),
4328
4420
  stat: async (path) => {
4329
4421
  const st = await filesOf(vfs).stat(path).catch(() => null);
4330
4422
  return st && { kind: st.type, size: st.size };
@@ -4349,6 +4441,49 @@ export class FacetManager {
4349
4441
  const storageBytes = Math.ceil(plan.bytes * 1.01) + rows * LEDGER_ROW_BYTES + moduleBytes + 65_536;
4350
4442
  return { paths: plan.paths, storageBytes };
4351
4443
  }
4444
+ /**
4445
+ * A one-shot's data plan: what its closure reads synchronously by a path its
4446
+ * code spells out (static-fs-refs.ts; readFileSync, or a read-only openSync),
4447
+ * any size, through any links on it, that the module map does not hold. It
4448
+ * is data-plan.ts's `static` rule for synchronous reads, which needs the
4449
+ * closure and a stat per path but no listing. The store fetches it at boot,
4450
+ * so it is held beside the module map rather than carried in it: `vite
4451
+ * build` reads lightningcss's 15.8 MB image with readFileSync(new
4452
+ * URL('lightningcss_node.wasm', import.meta.url)), which as a map cell
4453
+ * left the closure no room under the map's bound.
4454
+ */
4455
+ async _staticReadPlan(entry, vfsState, cwd, pacer) {
4456
+ if (!this.filesystem)
4457
+ return [];
4458
+ const vfs = this.filesystem.bind({ pid: entry.pid, cred: entry.cred });
4459
+ const held = new Set(vfsState.bundlePaths ?? []);
4460
+ const dir = stripLeadingSlashes(cwd).replace(/\/+$/, '');
4461
+ const reads = new Set();
4462
+ for (const refs of await this._closureStaticRefs(vfs, vfsState.bundlePaths ?? [], pacer)) {
4463
+ for (const ref of refs.exact)
4464
+ if (ref.sync)
4465
+ reads.add(stripLeadingSlashes(ref.path));
4466
+ for (const ref of refs.cwdRelative)
4467
+ if (ref.sync)
4468
+ reads.add(normalizeVfsPath((dir ? dir + '/' : '') + ref.path));
4469
+ }
4470
+ const plan = new Set();
4471
+ for (const path of reads) {
4472
+ let target;
4473
+ try {
4474
+ target = stripLeadingSlashes(await vfs.realpath('/' + path));
4475
+ }
4476
+ catch {
4477
+ continue;
4478
+ }
4479
+ if (held.has(target))
4480
+ continue;
4481
+ const stat = await filesOf(vfs).stat(target).catch(() => null);
4482
+ if (stat?.type === 'file')
4483
+ plan.add(target);
4484
+ }
4485
+ return [...plan];
4486
+ }
4352
4487
  /**
4353
4488
  * Paths earlier launches of the same build missed in this session. Other
4354
4489
  * sessions' misses (the shared read profile) join the module map instead,
@@ -4652,6 +4787,10 @@ export class FacetManager {
4652
4787
  async _stagedRuntimeCode(learning, pacer) {
4653
4788
  const modules = new Map();
4654
4789
  for (const [codeKey, entry] of learning.code) {
4790
+ if (entry.kind === 'expression') {
4791
+ modules.set(codeKey, runtimeExpressionModule(entry.code));
4792
+ continue;
4793
+ }
4655
4794
  if (entry.kind !== 'module') {
4656
4795
  modules.set(codeKey, runtimeFunctionModule(entry.kind, entry.params, entry.body));
4657
4796
  continue;
@@ -4849,8 +4988,12 @@ export class FacetManager {
4849
4988
  entry = found;
4850
4989
  }
4851
4990
  else {
4852
- this.processes.reap();
4853
- entry = this.processes.spawn(command, opts.argv || [], opts.cwd || '/home/user');
4991
+ await this.processes.reap();
4992
+ // A child of the command that ran it, so under its credential: the
4993
+ // program's syscalls answer under the credential the table holds for
4994
+ // its pid. At the top of the table it ran as the session user whoever
4995
+ // started it.
4996
+ entry = this.processes.spawn(command, opts.argv || [], opts.cwd || '/home/user', { parentPid: opts.invokerPid });
4854
4997
  // Short foreground `node -e ...` helpers are quiet by design — only
4855
4998
  // notify for user-facing `node <file>` invocations, which covers the
4856
4999
  // real user intent (running scripts, wrangler, etc.).
@@ -4928,10 +5071,12 @@ export class FacetManager {
4928
5071
  opts.signal?.addEventListener('abort', onShellAbort, { once: true });
4929
5072
  try {
4930
5073
  // A one-shot holds its module map and what it was seen to read (both in
4931
- // the bundle), and plans nothing beyond them: a data plan is chosen from
4932
- // a listing of the namespace, and a one-shot takes no listing of its own
4933
- // (§2.8: the principal's image is where that comes from).
4934
- const result = await this._execViaLoader(code, opts, entry, vfsState, [], abortController.signal, pacer, diagSink);
5074
+ // the bundle), and plans no listing of the namespace (§2.8: the
5075
+ // principal's image is where that comes from). What its closure reads
5076
+ // synchronously by a path its code spells out needs no listing, and is
5077
+ // its data plan.
5078
+ const dataPlan = await this._staticReadPlan(entry, vfsState, cwd, pacer);
5079
+ const result = await this._execViaLoader(code, opts, entry, vfsState, dataPlan, abortController.signal, pacer, diagSink);
4935
5080
  await this._recordLaunchLearning(vfsState.bundleKey, {
4936
5081
  code: result.runtimeCode, executedModules: result.moduleMisses, dataReads: result.residencyMisses,
4937
5082
  }).catch((error) => this._learningLost(entry.pid, error));
@@ -5252,7 +5397,7 @@ export class FacetManager {
5252
5397
  }
5253
5398
  const command = opts.command || `opencode ${opts.argv.join(' ')}`.trim();
5254
5399
  const attached = mode === 'attached';
5255
- const entry = this.processes.spawn(command, ['opencode', ...opts.argv], opts.cwd);
5400
+ const entry = this.processes.spawn(command, ['opencode', ...opts.argv], opts.cwd, { parentPid: opts.invokerPid });
5256
5401
  const pid = entry.pid;
5257
5402
  // attached TUI + headless serve are resident long-running processes; only the
5258
5403
  // attached TUI grabs the terminal (raw-mode stdin + live geometry).
@@ -5400,6 +5545,7 @@ export class FacetManager {
5400
5545
  env: opts.env,
5401
5546
  cwd: opts.cwd,
5402
5547
  command: `opencode serve --port ${port}`,
5548
+ invokerPid: opts.invokerPid,
5403
5549
  }, 'server');
5404
5550
  const servePid = serveStaged.pid;
5405
5551
  try {
@@ -5425,6 +5571,9 @@ export class FacetManager {
5425
5571
  stdin: '',
5426
5572
  command: opts.command || 'opencode',
5427
5573
  attachedTty: true,
5574
+ // The pair's exec id, from the serve: the command that ran `opencode`
5575
+ // may have ended while the serve booted, and been reaped since.
5576
+ invokerPid: servePid,
5428
5577
  });
5429
5578
  }
5430
5579
  catch (e) {
@@ -5732,14 +5881,20 @@ export class FacetManager {
5732
5881
  }
5733
5882
  /**
5734
5883
  * Re-drive a journalled launch after an instance reset. What the journal
5735
- * row carries is the recipe and nothing else: env and credentials are never
5736
- * written to storage, so a worker launch's are re-resolved by the embedder
5737
- * through `hooks.resolveWorkerLaunch`.
5884
+ * row carries is the recipe, and who the launch ran as (its credential and
5885
+ * exec id); env and secrets are never written to storage, so a worker
5886
+ * launch's are re-resolved by the embedder through
5887
+ * `hooks.resolveWorkerLaunch`.
5738
5888
  */
5739
5889
  async _redrive(record, attempt) {
5740
5890
  const { recipe } = record;
5891
+ if (record.cred === undefined) {
5892
+ // Reported through onRedriveFailed, and the row is superseded.
5893
+ throw new Error('its journal entry predates the credential a re-drive runs under, so it is not started as anyone else');
5894
+ }
5895
+ const identity = { cred: record.cred, ...(record.execId === undefined ? {} : { execId: record.execId }) };
5741
5896
  switch (recipe.kind) {
5742
- case 'node': return this._spawnResident(recipe.code, recipe.opts, attempt);
5897
+ case 'node': return this._spawnResident(recipe.code, recipe.opts, attempt, identity);
5743
5898
  case 'worker': {
5744
5899
  // Which resolver a worker recipe re-drives through is decided by
5745
5900
  // `recipe.resident`, and it is not a flag: it is the interpreter
@@ -5771,7 +5926,7 @@ export class FacetManager {
5771
5926
  vfsTextModules: resolved.vfsTextModules,
5772
5927
  mainModule,
5773
5928
  durable: { owner: residentOwner(record) ?? recipe.owner, image: recipe.image },
5774
- }, attempt);
5929
+ }, attempt, identity);
5775
5930
  }
5776
5931
  }
5777
5932
  }
@@ -5792,10 +5947,12 @@ export class FacetManager {
5792
5947
  /**
5793
5948
  * `attempt` distinguishes the launch the user asked for from the one re-drive
5794
5949
  * an instance reset earns it, and is carried in the journal rather than in
5795
- * the caller's options because no caller has an opinion about it.
5950
+ * the caller's options because no caller has an opinion about it. So is a
5951
+ * re-drive's credential and exec id, from the row: the process that invoked
5952
+ * the launch went with the instance.
5796
5953
  */
5797
- async _spawnResident(code, opts, attempt) {
5798
- this.processes.reap();
5954
+ async _spawnResident(code, opts, attempt, redriven) {
5955
+ await this.processes.reap();
5799
5956
  const command = opts.command || (opts.filename ? `node ${opts.filename}` : 'node <script>');
5800
5957
  const cwd = opts.cwd || '/home/user';
5801
5958
  let entry;
@@ -5807,7 +5964,9 @@ export class FacetManager {
5807
5964
  entry = found;
5808
5965
  }
5809
5966
  else {
5810
- entry = this.processes.spawn(command, opts.argv || [], cwd);
5967
+ // A child of its invoker, under its credential, as exec's. A re-drive has
5968
+ // no invoker (the journal never holds one): it runs as the row says.
5969
+ entry = this.processes.spawn(command, opts.argv || [], cwd, { parentPid: opts.invokerPid, ...redriven });
5811
5970
  }
5812
5971
  this.processes.setLongRunning(entry.pid);
5813
5972
  if (opts.attachedTty)
@@ -5888,10 +6047,16 @@ export class FacetManager {
5888
6047
  owner = declared.owner;
5889
6048
  }
5890
6049
  }
6050
+ // The recipe is the launch's inputs; the invoker is a pid of this
6051
+ // instance, which a re-drive outlives, so the row carries who it ran as.
6052
+ const { invokerPid: _invokerPid, ...inputs } = opts;
6053
+ const recipe = { kind: 'node', code, opts: { ...inputs, skipSpawn: undefined, callerPid: undefined } };
6054
+ const ranAs = { cred: entry.cred, ...(entry.execId === undefined ? {} : { execId: entry.execId }) };
5891
6055
  const initial = {
5892
6056
  pid: entry.pid, command, attempt, phase: 'starting', owner,
5893
- recipe: { kind: 'node', code, opts: { ...opts, skipSpawn: undefined, callerPid: undefined } },
6057
+ recipe,
5894
6058
  restart: residentRestartPolicy(opts.env),
6059
+ ...ranAs,
5895
6060
  };
5896
6061
  let duplicateOf;
5897
6062
  try {
@@ -5930,10 +6095,11 @@ export class FacetManager {
5930
6095
  command,
5931
6096
  attempt,
5932
6097
  phase: 'starting',
5933
- recipe: { kind: 'node', code, opts: { ...opts, skipSpawn: undefined, callerPid: undefined } },
6098
+ recipe,
5934
6099
  owner,
5935
6100
  ...(held !== null ? { port: held.port, injectedPort: held.port } : {}),
5936
6101
  restart: residentRestartPolicy(opts.env),
6102
+ ...ranAs,
5937
6103
  };
5938
6104
  try {
5939
6105
  if (!ephemeral)
@@ -6236,15 +6402,15 @@ export class FacetManager {
6236
6402
  async spawnWorker(workerCode, command, cwd, opts = {}) {
6237
6403
  return this._spawnWorker(workerCode, command, cwd, opts, 0);
6238
6404
  }
6239
- /** `attempt` is the journal's re-drive budget, as `_spawnResident` carries it. */
6240
- async _spawnWorker(workerCode, command, cwd, opts, attempt) {
6405
+ /** `attempt` is the journal's re-drive budget, and `redriven` who a re-drive runs as, as `_spawnResident` carries them. */
6406
+ async _spawnWorker(workerCode, command, cwd, opts, attempt, redriven) {
6241
6407
  if (opts.resident && !opts.durable) {
6242
6408
  opts = { ...opts, durable: { owner: await deriveResidentOwner(cwd, opts.resident.argv) } };
6243
6409
  }
6244
- this.processes.reap();
6410
+ await this.processes.reap();
6245
6411
  // The table entry carries the same argv the identity is derived from, so
6246
6412
  // a runtime resident reads the same way through either path.
6247
- const entry = this.processes.spawn(command, opts.resident?.argv ?? [], cwd);
6413
+ const entry = this.processes.spawn(command, opts.resident?.argv ?? [], cwd, { parentPid: opts.invokerPid, ...redriven });
6248
6414
  // Stamp the process-table entry so /api/processes exposes this as a
6249
6415
  // long-running process.
6250
6416
  this.processes.setLongRunning(entry.pid);
@@ -6334,6 +6500,8 @@ export class FacetManager {
6334
6500
  owner: opts.durable.owner,
6335
6501
  restart: opts.restart ?? 'never',
6336
6502
  ...(opts.port !== undefined && opts.port > 0 ? { port: opts.port } : {}),
6503
+ cred: entry.cred,
6504
+ ...(entry.execId !== undefined ? { execId: entry.execId } : {}),
6337
6505
  };
6338
6506
  // The durable facet name is claimed once, ever, from DO storage — a
6339
6507
  // re-drive after a reset, an eviction's re-attach and a relaunch all
@@ -6674,7 +6842,8 @@ export class FacetManager {
6674
6842
  app.status = 'running';
6675
6843
  apps.set(identity.owner, app);
6676
6844
  }
6677
- return [...apps.values()];
6845
+ // The live pid's exec id, read from its process: the one place it lives.
6846
+ return [...apps.values()].map((app) => (app.pid === null ? app : { ...app, ...execIdField(this.processes.get(app.pid)) }));
6678
6847
  }
6679
6848
  async registerPort(pid, port) {
6680
6849
  if (port > 0 && port < 65536) {