@nimbus-sh/worker 0.9.0 → 0.11.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 (286) hide show
  1. package/NOTICE.md +5 -0
  2. package/README.md +10 -0
  3. package/dist/_shared/session-router.d.ts +7 -0
  4. package/dist/_shared/session-router.d.ts.map +1 -1
  5. package/dist/_shared/session-router.js +7 -0
  6. package/dist/bindings/body.d.ts +6 -0
  7. package/dist/bindings/body.d.ts.map +1 -0
  8. package/dist/bindings/body.js +37 -0
  9. package/dist/bindings/kv.d.ts +0 -2
  10. package/dist/bindings/kv.d.ts.map +1 -1
  11. package/dist/bindings/kv.js +3 -42
  12. package/dist/bindings/r2.d.ts +0 -2
  13. package/dist/bindings/r2.d.ts.map +1 -1
  14. package/dist/bindings/r2.js +4 -44
  15. package/dist/esbuild-cli-artifact.generated.d.ts +18 -0
  16. package/dist/esbuild-cli-artifact.generated.d.ts.map +1 -0
  17. package/dist/esbuild-cli-artifact.generated.js +17 -0
  18. package/dist/esbuild-wasm-bundle.generated.d.ts +24 -24
  19. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
  20. package/dist/esbuild-wasm-bundle.generated.js +24 -24
  21. package/dist/facets/cirrus-real.d.ts.map +1 -1
  22. package/dist/facets/cirrus-real.js +11 -6
  23. package/dist/facets/compose.d.ts +11 -7
  24. package/dist/facets/compose.d.ts.map +1 -1
  25. package/dist/facets/compose.js +8 -31
  26. package/dist/facets/data-plan.d.ts +79 -0
  27. package/dist/facets/data-plan.d.ts.map +1 -0
  28. package/dist/facets/data-plan.js +477 -0
  29. package/dist/facets/esbuild-bundle-pool.d.ts +1 -1
  30. package/dist/facets/esbuild-bundle-pool.js +3 -3
  31. package/dist/facets/esbuild-transform.d.ts +38 -8
  32. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  33. package/dist/facets/esbuild-transform.js +219 -19
  34. package/dist/facets/exec-telemetry.d.ts +7 -3
  35. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  36. package/dist/facets/manager.d.ts +198 -69
  37. package/dist/facets/manager.d.ts.map +1 -1
  38. package/dist/facets/manager.js +1276 -652
  39. package/dist/facets/opencode-staging.d.ts +0 -2
  40. package/dist/facets/opencode-staging.d.ts.map +1 -1
  41. package/dist/facets/opencode-staging.js +5 -11
  42. package/dist/facets/process.d.ts +4 -0
  43. package/dist/facets/process.d.ts.map +1 -1
  44. package/dist/facets/process.js +3 -0
  45. package/dist/facets/read-profile.d.ts +149 -0
  46. package/dist/facets/read-profile.d.ts.map +1 -0
  47. package/dist/facets/read-profile.js +483 -0
  48. package/dist/facets/real-vite-hmr.d.ts +2 -0
  49. package/dist/facets/real-vite-hmr.d.ts.map +1 -1
  50. package/dist/facets/real-vite-hmr.js +3 -2
  51. package/dist/facets/resident-identity.d.ts +0 -1
  52. package/dist/facets/resident-identity.d.ts.map +1 -1
  53. package/dist/facets/resident-identity.js +0 -3
  54. package/dist/facets/wasm-swap-registry.d.ts +34 -25
  55. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  56. package/dist/facets/wasm-swap-registry.js +62 -56
  57. package/dist/facets/ws-terminal.d.ts +4 -4
  58. package/dist/facets/ws-terminal.d.ts.map +1 -1
  59. package/dist/facets/ws-terminal.js +4 -5
  60. package/dist/git/commands.d.ts +33 -10
  61. package/dist/git/commands.d.ts.map +1 -1
  62. package/dist/git/commands.js +1891 -197
  63. package/dist/git/network-facet.d.ts +12 -2
  64. package/dist/git/network-facet.d.ts.map +1 -1
  65. package/dist/git/network-facet.js +69 -18
  66. package/dist/git/unified-diff.d.ts +74 -0
  67. package/dist/git/unified-diff.d.ts.map +1 -0
  68. package/dist/git/unified-diff.js +676 -0
  69. package/dist/git-bundle.generated.d.ts +7 -11
  70. package/dist/git-bundle.generated.d.ts.map +1 -1
  71. package/dist/git-bundle.generated.js +7 -11
  72. package/dist/hosted/commands.d.ts.map +1 -1
  73. package/dist/hosted/commands.js +64 -245
  74. package/dist/hosted/runtime.d.ts +22 -15
  75. package/dist/hosted/runtime.d.ts.map +1 -1
  76. package/dist/hosted/runtime.js +72 -14
  77. package/dist/hosted/services.d.ts +8 -1
  78. package/dist/hosted/services.d.ts.map +1 -1
  79. package/dist/hosted/services.js +12 -13
  80. package/dist/hosted/session.d.ts +165 -0
  81. package/dist/hosted/session.d.ts.map +1 -0
  82. package/dist/hosted/session.js +132 -0
  83. package/dist/loaders/generated-workers.d.ts +6 -3
  84. package/dist/loaders/generated-workers.d.ts.map +1 -1
  85. package/dist/loaders/generated-workers.js +8 -5
  86. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  87. package/dist/loaders/npm-resolve-preamble.js +0 -27
  88. package/dist/loaders/pre-bundle-preamble.d.ts +6 -3
  89. package/dist/loaders/pre-bundle-preamble.d.ts.map +1 -1
  90. package/dist/loaders/pre-bundle-preamble.js +13 -8
  91. package/dist/node-shims-artifact.generated.d.ts +13 -4
  92. package/dist/node-shims-artifact.generated.d.ts.map +1 -1
  93. package/dist/node-shims-artifact.generated.js +16 -7
  94. package/dist/npm/bin-links.d.ts +3 -1
  95. package/dist/npm/bin-links.d.ts.map +1 -1
  96. package/dist/npm/bin-links.js +32 -8
  97. package/dist/npm/cache.d.ts +11 -1
  98. package/dist/npm/cache.d.ts.map +1 -1
  99. package/dist/npm/cache.js +21 -7
  100. package/dist/npm/install-batch-facet.d.ts +4 -2
  101. package/dist/npm/install-batch-facet.d.ts.map +1 -1
  102. package/dist/npm/install-batch-facet.js +32 -18
  103. package/dist/npm/installer.d.ts +23 -3
  104. package/dist/npm/installer.d.ts.map +1 -1
  105. package/dist/npm/installer.js +330 -131
  106. package/dist/npm/npx-install.d.ts +3 -1
  107. package/dist/npm/npx-install.d.ts.map +1 -1
  108. package/dist/npm/npx-install.js +4 -1
  109. package/dist/npm/package-lock.d.ts +32 -0
  110. package/dist/npm/package-lock.d.ts.map +1 -0
  111. package/dist/npm/package-lock.js +65 -0
  112. package/dist/npm/placement.d.ts +16 -0
  113. package/dist/npm/placement.d.ts.map +1 -0
  114. package/dist/npm/placement.js +33 -0
  115. package/dist/npm/pre-bundle-facet.d.ts +1 -30
  116. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  117. package/dist/npm/pre-bundle-facet.js +3 -5
  118. package/dist/npm/r2-cache.d.ts +12 -6
  119. package/dist/npm/r2-cache.d.ts.map +1 -1
  120. package/dist/npm/r2-cache.js +34 -16
  121. package/dist/npm/resolve-one-facet.d.ts +3 -0
  122. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  123. package/dist/npm/resolve-one-facet.js +15 -3
  124. package/dist/npm/resolver.d.ts +11 -13
  125. package/dist/npm/resolver.d.ts.map +1 -1
  126. package/dist/npm/resolver.js +12 -25
  127. package/dist/opencode-artifact.generated.js +2 -2
  128. package/dist/replica/routing.d.ts +2 -38
  129. package/dist/replica/routing.d.ts.map +1 -1
  130. package/dist/replica/routing.js +3 -55
  131. package/dist/router/index.d.ts.map +1 -1
  132. package/dist/router/index.js +15 -2
  133. package/dist/router/public-directory.d.ts +18 -0
  134. package/dist/router/public-directory.d.ts.map +1 -1
  135. package/dist/router/public-directory.js +1 -1
  136. package/dist/router/remote-api.d.ts +3 -1
  137. package/dist/router/remote-api.d.ts.map +1 -1
  138. package/dist/router/remote-api.js +42 -79
  139. package/dist/runtime/bash-repl.js +2 -2
  140. package/dist/runtime/bun-repl.d.ts.map +1 -1
  141. package/dist/runtime/bun-repl.js +2 -13
  142. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  143. package/dist/runtime/cpython-resident.js +2 -10
  144. package/dist/runtime/esbuild-wasm-bytes.d.ts +37 -18
  145. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  146. package/dist/runtime/esbuild-wasm-bytes.js +66 -92
  147. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  148. package/dist/runtime/facet-loader-host.js +0 -7
  149. package/dist/runtime/git-bundle-artifact.d.ts +17 -0
  150. package/dist/runtime/git-bundle-artifact.d.ts.map +1 -0
  151. package/dist/runtime/git-bundle-artifact.js +25 -0
  152. package/dist/runtime/node-repl.d.ts.map +1 -1
  153. package/dist/runtime/node-repl.js +2 -13
  154. package/dist/runtime/node-shims-artifact.d.ts +22 -23
  155. package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
  156. package/dist/runtime/node-shims-artifact.js +48 -78
  157. package/dist/runtime/node-shims.d.ts.map +1 -1
  158. package/dist/runtime/node-shims.js +2358 -996
  159. package/dist/runtime/opencode-artifact.d.ts.map +1 -1
  160. package/dist/runtime/opencode-artifact.js +17 -53
  161. package/dist/runtime/opencode-facet-runner.d.ts +3 -6
  162. package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
  163. package/dist/runtime/opencode-facet-runner.js +45 -10
  164. package/dist/runtime/opentui-wasm-bytes.d.ts.map +1 -1
  165. package/dist/runtime/opentui-wasm-bytes.js +16 -49
  166. package/dist/runtime/package-manager.d.ts +0 -2
  167. package/dist/runtime/package-manager.d.ts.map +1 -1
  168. package/dist/runtime/package-manager.js +0 -3
  169. package/dist/runtime/python-repl.d.ts.map +1 -1
  170. package/dist/runtime/python-repl.js +4 -35
  171. package/dist/runtime/repl-session.d.ts +10 -0
  172. package/dist/runtime/repl-session.d.ts.map +1 -1
  173. package/dist/runtime/repl-session.js +7 -0
  174. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  175. package/dist/runtime/ruby-repl.js +2 -2
  176. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  177. package/dist/runtime/ruby-resident.js +41 -51
  178. package/dist/runtime/runtime-catalog.d.ts +10 -3
  179. package/dist/runtime/runtime-catalog.d.ts.map +1 -1
  180. package/dist/runtime/runtime-catalog.js +125 -15
  181. package/dist/runtime/sqlite-wasm-bytes.d.ts +4 -4
  182. package/dist/runtime/sqlite-wasm-bytes.d.ts.map +1 -1
  183. package/dist/runtime/sqlite-wasm-bytes.js +21 -57
  184. package/dist/runtime/staged-source.d.ts +67 -0
  185. package/dist/runtime/staged-source.d.ts.map +1 -0
  186. package/dist/runtime/staged-source.js +113 -0
  187. package/dist/runtime/wasi/preamble.d.ts +0 -22
  188. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  189. package/dist/runtime/wasi/preamble.js +98 -1425
  190. package/dist/runtime-catalog.generated.js +1 -1
  191. package/dist/session/agent.d.ts +14 -2
  192. package/dist/session/agent.d.ts.map +1 -1
  193. package/dist/session/agent.js +57 -19
  194. package/dist/session/ai.d.ts +0 -11
  195. package/dist/session/ai.d.ts.map +1 -1
  196. package/dist/session/ai.js +0 -4
  197. package/dist/session/fs-watch.d.ts +0 -11
  198. package/dist/session/fs-watch.d.ts.map +1 -1
  199. package/dist/session/fs-watch.js +0 -21
  200. package/dist/session/hibernation.d.ts +68 -2
  201. package/dist/session/hibernation.d.ts.map +1 -1
  202. package/dist/session/hibernation.js +95 -1
  203. package/dist/session/init-phases.d.ts +1 -2
  204. package/dist/session/init-phases.d.ts.map +1 -1
  205. package/dist/session/init-phases.js +1 -2
  206. package/dist/session/init.d.ts.map +1 -1
  207. package/dist/session/init.js +7 -20
  208. package/dist/session/keys.d.ts +0 -3
  209. package/dist/session/keys.d.ts.map +1 -1
  210. package/dist/session/keys.js +0 -3
  211. package/dist/session/legacy-reset.d.ts +11 -0
  212. package/dist/session/legacy-reset.d.ts.map +1 -0
  213. package/dist/session/legacy-reset.js +16 -0
  214. package/dist/session/nimbus-session.d.ts +50 -16
  215. package/dist/session/nimbus-session.d.ts.map +1 -1
  216. package/dist/session/nimbus-session.js +77 -71
  217. package/dist/session/npm-install-port.d.ts.map +1 -1
  218. package/dist/session/npm-install-port.js +2 -0
  219. package/dist/session/programmatic.d.ts +13 -18
  220. package/dist/session/programmatic.d.ts.map +1 -1
  221. package/dist/session/programmatic.js +53 -39
  222. package/dist/session/replica-routes.d.ts +2 -3
  223. package/dist/session/replica-routes.d.ts.map +1 -1
  224. package/dist/session/replica-routes.js +3 -10
  225. package/dist/session/routes.d.ts.map +1 -1
  226. package/dist/session/routes.js +11 -12
  227. package/dist/session/rpc.d.ts +108 -21
  228. package/dist/session/rpc.d.ts.map +1 -1
  229. package/dist/session/rpc.js +194 -39
  230. package/dist/session/start-real-vite.js +2 -2
  231. package/dist/session/state-store.d.ts +1 -17
  232. package/dist/session/state-store.d.ts.map +1 -1
  233. package/dist/session/state-store.js +3 -53
  234. package/dist/session/supervisor-op.d.ts +7 -0
  235. package/dist/session/supervisor-op.d.ts.map +1 -1
  236. package/dist/session/supervisor-op.js +6 -1
  237. package/dist/session/supervisor-rpc.d.ts +116 -19
  238. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  239. package/dist/session/supervisor-rpc.js +194 -84
  240. package/dist/session/vite-command.d.ts.map +1 -1
  241. package/dist/session/vite-command.js +17 -14
  242. package/dist/session/ws.d.ts.map +1 -1
  243. package/dist/session/ws.js +20 -8
  244. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  245. package/dist/shell/npm-bin-entrypoints.js +30 -5
  246. package/dist/vfs/facet-resident-limits.d.ts +33 -0
  247. package/dist/vfs/facet-resident-limits.d.ts.map +1 -0
  248. package/dist/vfs/facet-resident-limits.js +33 -0
  249. package/dist/vfs/facet-resident-store.d.ts +2 -10
  250. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  251. package/dist/vfs/facet-resident-store.js +1594 -219
  252. package/dist/workspace-host.d.ts +3 -1
  253. package/dist/workspace-host.d.ts.map +1 -1
  254. package/dist/workspace-host.js +2 -0
  255. package/dist/wrangler/nimbus-wrangler.d.ts +0 -2
  256. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  257. package/dist/wrangler/nimbus-wrangler.js +8 -22
  258. package/package.json +34 -7
  259. package/public/_assets/esbuild-0.24.2.js +2380 -0
  260. package/public/_assets/opencode/1.16.2/index-attach.js +2 -2
  261. package/public/_assets/runtime/esbuild-cli-c4fda380d52befc1.js +6892 -0
  262. package/public/_assets/runtime/git-de3e60c9f3ff006a.js +103 -0
  263. package/public/_assets/runtime/{node-shims-acaa037e6d26ec0d.js → node-shims-06565b121555c194.js} +2879 -992
  264. package/public/_assets/runtime/resident-store-6ffb1939c53d982f.js +2264 -0
  265. package/public/_assets/runtime/vfs-write-ledger-e501e8fc793bc2c1.js +889 -0
  266. package/scripts/build-opencode-attach-entry.mjs +51 -1
  267. package/scripts/bundle-esbuild-wasm.mjs +55 -39
  268. package/scripts/bundle-facet-workers.mjs +159 -10
  269. package/scripts/bundle-git.mjs +88 -27
  270. package/scripts/bundle-node-shims.mjs +72 -46
  271. package/scripts/bundle-runtime.mjs +18 -421
  272. package/scripts/cf-git-patch.mjs +137 -0
  273. package/scripts/patch-install-deps.mjs +49 -96
  274. package/scripts/runtime-specs.mjs +435 -0
  275. package/vendor/git-http.generated.d.ts +82 -0
  276. package/vendor/git-upstream.generated.d.ts +4378 -0
  277. package/vendor/git.LICENSE +7 -0
  278. package/vendor/git.generated.d.mts +21 -0
  279. package/vendor/git.generated.mjs +103 -0
  280. package/dist/replica/suspension.d.ts +0 -36
  281. package/dist/replica/suspension.d.ts.map +0 -1
  282. package/dist/replica/suspension.js +0 -49
  283. package/dist/runtime/static-server.d.ts +0 -3
  284. package/dist/runtime/static-server.d.ts.map +0 -1
  285. package/dist/runtime/static-server.js +0 -133
  286. package/scripts/clean-dist.mjs +0 -7
@@ -4,10 +4,15 @@
4
4
  * Exported from index.ts. Facets receive `env.SUPERVISOR` service binding
5
5
  * pointing to this class via ctx.exports loopback binding.
6
6
  *
7
- * Props: { doId: string, pid: number, writerId: string }
7
+ * Props: { doId: string, pid: number, writerId: string, route: HostRoute, hostIncarnation?: string }
8
8
  * doId — the supervisor DO's durable object ID (for routing)
9
9
  * pid — the process ID (for stdout/stderr routing)
10
10
  * writerId — the active append-writer incarnation for this process
11
+ * route — the host namespace and dispatch method, minted with the binding
12
+ * in the host's isolate; this entrypoint may answer from another
13
+ * hostIncarnation — the host instance that minted the binding, present
14
+ * when that host applies delivered mutations once
15
+ * (@nimbus-sh/core/workspace/supervisor-delivery.js)
11
16
  *
12
17
  * Methods callable by facets via RPC:
13
18
  * readFile(path) → string | null
@@ -20,16 +25,29 @@
20
25
  * fsOpen/fsRead/fsWrite/fsClose/readlink/symlink/rename/rmdir/fsRevision
21
26
  * fsReadRange/fsWriteRange/fsAppend/fsAppendAck/fsTruncate
22
27
  * → shared RuntimeFsBridge operations
23
- * fsReadBatch(requests) → per-range results (many reads, one round trip)
28
+ * fsReadBatch(requests) → per-request results (many reads and lstats, one round trip)
24
29
  * fsList(after, limit) → one page of what EXISTS, with per-path revisions
25
30
  * writeBatch(payload) → { inodes, chunks } (bulk atomic write)
26
31
  * stdout(data) → void (pushed to WebSocket + ring buffer)
27
32
  * stderr(data) → void
28
33
  * reportExit(code, tail?) → void (called from facet's finally block)
29
34
  * prefetch(cwd, entryCode) → Record<string, string>
35
+ *
36
+ * Delivery: every call reaches the session over a Durable Object stub the
37
+ * platform can drop ("Network connection lost.", `retryable`). Reads are
38
+ * re-sent on a fresh stub, and hedged: one unanswered after
39
+ * SUPERVISOR_READ_HEDGE_AFTER_MS is sent again while it stays in flight.
40
+ * Filesystem mutations, on a binding that names its
41
+ * host's incarnation, are re-sent under one delivery id that host applies at
42
+ * most once (`_fsMutation`); on any other binding they are sent once.
43
+ * Appends are re-sent under the append ledger's identity. Everything else is
44
+ * sent once and a drop surfaces.
30
45
  */
31
46
  import { WorkerEntrypoint } from 'cloudflare:workers';
32
- import { hostNamespace, hostDispatchMethod } from '@nimbus-sh/platform/composition.js';
47
+ import { hostNamespaceBinding, hostOpDispatch } from '@nimbus-sh/fabric/host-dispatch.js';
48
+ import { idempotent } from '@nimbus-sh/fabric/do-calls.js';
49
+ import { SUPERVISOR_DELIVER_OP, } from '@nimbus-sh/core/workspace/supervisor-delivery.js';
50
+ import { VFS_DELIVERY_RETRY_WINDOW_MS } from '@nimbus-sh/core/constants.js';
33
51
  // W5: OOM discriminator — record last-known RPC frame on writeBatch entry
34
52
  import { setLastRpcFrame } from '@nimbus-sh/platform/oom-discriminator.js';
35
53
  // Phase 2 A'.2 — supervisor in-flight RPC payload byte tracking.
@@ -37,6 +55,7 @@ import { rpcPayloadStart, rpcPayloadEnd } from '@nimbus-sh/platform/diag-counter
37
55
  // W4: R2 cross-tenant npm cache (tarballs + packuments)
38
56
  import { R2CacheClient, MAX_R2_TARBALL_BYTES } from '../npm/r2-cache.js';
39
57
  import { useRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
58
+ import { fsReadBatchRequestBytes } from './rpc.js';
40
59
  import { W7_MAX_RECORD_BYTES } from '@nimbus-sh/platform/w7-frame.js';
41
60
  /**
42
61
  * Drain the per-call event list captured by R2CacheClient during this
@@ -78,39 +97,104 @@ function _estimateWriteBatchBytes(payload) {
78
97
  // The fabric mints `env.SUPERVISOR` bindings for the programs it hosts; the
79
98
  // worker's composition root (src/index.ts) names this class to the fabric
80
99
  // with composeFabric.
100
+ // A process's filesystem read (SupervisorRPC → session) still unanswered
101
+ // after this long is sent again on a fresh stub, the first left running and
102
+ // the first answer taken (fabric do-calls `hedgeAfterMs`). Measured on a
103
+ // throwaway under three concurrent sessions, 2026-09-28: none of the 521
104
+ // read batches that answered took more than 5 s at the facet's side, the
105
+ // session served each read it received without waiting on I/O, and the 11
106
+ // attempts that stalled — none of which reached the session — were still
107
+ // pending 110–560 s later. So an attempt past 5 s is one that is not coming
108
+ // back, and a hedge then costs a duplicate read only when that measurement
109
+ // was wrong.
110
+ export const SUPERVISOR_READ_HEDGE_AFTER_MS = 5_000;
81
111
  export class SupervisorRPC extends WorkerEntrypoint {
82
- /** Resolve the composed host anew for each WorkerEntrypoint invocation. */
83
- _dispatch() {
112
+ /**
113
+ * A fresh stub for the host, by the route the binding carries, per call.
114
+ * The platform serves this entrypoint from whichever isolate it likes; the
115
+ * props were minted in the host's.
116
+ */
117
+ _host() {
84
118
  const doId = this.ctx.props?.doId;
85
119
  if (typeof doId !== 'string' || doId.length === 0) {
86
120
  throw new Error('SupervisorRPC: missing doId in props');
87
121
  }
88
- const binding = hostNamespace();
89
- const namespace = Reflect.get(this.env, binding);
90
- if (!namespace || typeof namespace.idFromString !== 'function') {
91
- throw new Error(`SupervisorRPC: env.${binding} is not a Durable Object namespace; `
92
- + "a workspace host names its own with composeFabric({ hostNamespace: '<binding>' })");
93
- }
94
- const stub = namespace.get(namespace.idFromString(doId));
95
- const method = hostDispatchMethod();
96
- const dispatch = Reflect.get(stub, method);
97
- if (typeof dispatch !== 'function') {
98
- throw new Error(`SupervisorRPC: the workspace host mounts no ${method}(); `
99
- + 'a host forwards one method to workspace.supervisorOp(op)');
100
- }
101
- // Never `.call(stub, …)` on an RpcStub: the proxy treats `call` as a
102
- // property get, which becomes an RPC to a method literally named "call".
103
- // `Reflect.apply` reaches the proxied member's apply trap instead, and on
104
- // a plain-object host it still invokes the method with the stub as `this`.
105
- return (envelope) => Reflect.apply(dispatch, stub, [envelope]);
122
+ const namespace = hostNamespaceBinding(this.env, 'SupervisorRPC', this._route());
123
+ return namespace.get(namespace.idFromString(doId));
124
+ }
125
+ _route() {
126
+ return this.ctx.props?.route;
106
127
  }
107
128
  _op(op, args = [], extra = {}) {
108
- return this._dispatch()({ op, args, ...extra });
129
+ return hostOpDispatch(this._host(), 'SupervisorRPC', this._route())({ op, args, ...extra });
109
130
  }
110
131
  /** Stamp filesystem credentials from the binding, not the supplied arguments. */
111
132
  _fsOp(op, args = []) {
112
133
  return this._op(op, args, { pid: this._pid() });
113
134
  }
135
+ /**
136
+ * A filesystem read changes nothing on the host, so a call the platform
137
+ * dropped on the way to it is repeated on a fresh stub. Measured: the
138
+ * host's `stat` failing with "Network connection lost." (`retryable`) is
139
+ * what failed CPython's start in about one fresh session in twenty.
140
+ *
141
+ * A read can also never answer: under concurrent sessions a burst of reads
142
+ * from one facet left some attempts pending for minutes without reaching
143
+ * the host, and the program waiting on them never exited
144
+ * (preview/new/lucide-barrel-cache-widens). So a read still unanswered
145
+ * after SUPERVISOR_READ_HEDGE_AFTER_MS is hedged: sent again on a fresh
146
+ * stub, the first attempt left running, the first answer taken.
147
+ *
148
+ * A read can equally be slow at the session — queued behind the read
149
+ * budget, a lazy import, a busy input gate — and a hedge must not make it
150
+ * read again there. So every attempt carries the one read id minted here,
151
+ * and the session joins a repeat to the read it is still serving
152
+ * (`SupervisorDeliveries.joinRead`): a hedge that did arrive costs the
153
+ * session nothing but a second copy of the answer. Mutations are not
154
+ * hedged: their repeats stay bounded by the delivery retry window.
155
+ */
156
+ _fsRead(op, args = []) {
157
+ return this._resent({ op, args, pid: this._pid(), readId: crypto.randomUUID() }, { hedgeAfterMs: SUPERVISOR_READ_HEDGE_AFTER_MS });
158
+ }
159
+ /**
160
+ * A filesystem mutation, delivered exactly once. The platform drops this
161
+ * hop too — measured: pip's `fsWrite` of a wheel member and a FileHandle
162
+ * write loop, each failing "Network connection lost." (`retryable`) as
163
+ * EIO — and a dropped mutation may or may not have run.
164
+ *
165
+ * So on a binding whose host names its incarnation, every attempt carries
166
+ * the one delivery id minted here, under SUPERVISOR_DELIVER_OP, and that
167
+ * host instance applies the id at most once: a repeat of a mutation that
168
+ * ran is answered from its receipt and never applied again, even over
169
+ * another writer's newer write; one that never arrived applies on the
170
+ * repeat. Any other instance, and any host that predates delivery, refuses
171
+ * the envelope outright, and that refusal is never repeated. Repeats stop
172
+ * VFS_DELIVERY_RETRY_WINDOW_MS after the first attempt, inside the host's
173
+ * receipt retention.
174
+ *
175
+ * A binding minted by a host that dedupes nothing names no incarnation,
176
+ * and its mutations are sent once.
177
+ *
178
+ * The id is random because this entrypoint keeps nothing between calls and
179
+ * may answer from any isolate; the pid still comes from the binding.
180
+ */
181
+ _fsMutation(op, args) {
182
+ const hostIncarnation = this._hostIncarnation();
183
+ if (hostIncarnation === undefined)
184
+ return this._fsOp(op, args);
185
+ return this._resent({ op: SUPERVISOR_DELIVER_OP, args, pid: this._pid(), delivery: { op, id: crypto.randomUUID(), hostIncarnation } }, { retryWindowMs: VFS_DELIVERY_RETRY_WINDOW_MS });
186
+ }
187
+ /** `envelope`, re-sent as it is on a fresh stub while the platform drops it retryably. */
188
+ _resent(envelope, policy) {
189
+ return idempotent(envelope.delivery?.op ?? envelope.op, () => this._host(), (host) => hostOpDispatch(host, 'SupervisorRPC', this._route())(envelope), policy);
190
+ }
191
+ _hostIncarnation() {
192
+ const props = this.ctx.props;
193
+ if (typeof props !== 'object' || props === null || !('hostIncarnation' in props))
194
+ return undefined;
195
+ const incarnation = props.hostIncarnation;
196
+ return typeof incarnation === 'string' && incarnation.length > 0 ? incarnation : undefined;
197
+ }
114
198
  _reportingPid() {
115
199
  const pid = this.ctx.props?.pid;
116
200
  return typeof pid === 'number' && Number.isInteger(pid) && pid > 0 ? pid : 0;
@@ -134,14 +218,14 @@ export class SupervisorRPC extends WorkerEntrypoint {
134
218
  }
135
219
  // ── Filesystem RPC ────────────────────────────────────────────────────
136
220
  async readFile(path) {
137
- return this._call(this._fsOp('readFile', [path]));
221
+ return this._call(this._fsRead('readFile', [path]));
138
222
  }
139
223
  /**
140
224
  * Read a file as raw bytes. Used by the git network facet for binary
141
225
  * object/pack files where the text readFile would corrupt content.
142
226
  */
143
227
  async readFileBytes(path) {
144
- return this._call(this._fsOp('readFileBytes', [path]));
228
+ return this._call(this._fsRead('readFileBytes', [path]));
145
229
  }
146
230
  async writeFile(path, content) {
147
231
  // binary-fs wave: accept Uint8Array natively. Pre-fix this RPC was
@@ -149,55 +233,55 @@ export class SupervisorRPC extends WorkerEntrypoint {
149
233
  // decode every Uint8Array write — mangling bytes ≥ 0x80 to U+FFFD
150
234
  // and corrupting binary content. RPC structured-clone handles
151
235
  // Uint8Array transparently; downstream _rpcWriteFile also accepts
152
- return this._call(this._fsOp('writeFile', [path, content]));
236
+ return this._call(this._fsMutation('writeFile', [path, content]));
153
237
  }
154
238
  async stat(path, options) {
155
- return this._call(this._fsOp('stat', [path, options]));
239
+ return this._call(this._fsRead('stat', [path, options]));
156
240
  }
157
241
  async lstat(path) {
158
- return this._call(this._fsOp('lstat', [path]));
242
+ return this._call(this._fsRead('lstat', [path]));
159
243
  }
160
244
  async hasLegacySymlinkUnder(path) {
161
- return this._call(this._fsOp('hasLegacySymlinkUnder', [path]));
245
+ return this._call(this._fsRead('hasLegacySymlinkUnder', [path]));
162
246
  }
163
247
  async utimes(path, atimeMs, mtimeMs) {
164
- return this._call(this._fsOp('utimes', [path, atimeMs, mtimeMs]));
248
+ return this._call(this._fsMutation('utimes', [path, atimeMs, mtimeMs]));
165
249
  }
166
250
  async chmod(path, mode) {
167
- return this._call(this._fsOp('chmod', [path, mode]));
251
+ return this._call(this._fsMutation('chmod', [path, mode]));
168
252
  }
169
253
  async access(path, mode) {
170
- return this._call(this._fsOp('access', [path, mode]));
254
+ return this._call(this._fsRead('access', [path, mode]));
171
255
  }
172
256
  async chown(path, uid, gid, options) {
173
- return this._call(this._fsOp('chown', [path, uid, gid, options]));
257
+ return this._call(this._fsMutation('chown', [path, uid, gid, options]));
174
258
  }
175
259
  async setUmask(mask) {
176
260
  return this._call(this._fsOp('setUmask', [mask]));
177
261
  }
178
262
  async readdir(path) {
179
- return this._call(this._fsOp('readdir', [path]));
263
+ return this._call(this._fsRead('readdir', [path]));
180
264
  }
181
265
  async exists(path) {
182
- return this._call(this._fsOp('exists', [path]));
266
+ return this._call(this._fsRead('exists', [path]));
183
267
  }
184
268
  async mkdir(path, options) {
185
- return this._call(this._fsOp('mkdir', [path, options]));
269
+ return this._call(this._fsMutation('mkdir', [path, options]));
186
270
  }
187
271
  async rmdir(path) {
188
- return this._call(this._fsOp('rmdir', [path]));
272
+ return this._call(this._fsMutation('rmdir', [path]));
189
273
  }
190
274
  async rename(from, to) {
191
- return this._call(this._fsOp('rename', [from, to]));
275
+ return this._call(this._fsMutation('rename', [from, to]));
192
276
  }
193
277
  async unlink(path) {
194
- return this._call(this._fsOp('unlink', [path]));
278
+ return this._call(this._fsMutation('unlink', [path]));
195
279
  }
196
280
  async readlink(path) {
197
- return this._call(this._fsOp('readlink', [path]));
281
+ return this._call(this._fsRead('readlink', [path]));
198
282
  }
199
283
  async symlink(target, path) {
200
- return this._call(this._fsOp('symlink', [target, path]));
284
+ return this._call(this._fsMutation('symlink', [target, path]));
201
285
  }
202
286
  /**
203
287
  * ACQUIRE: the paths mutated since the facet's cursor, plus a fresh
@@ -208,13 +292,14 @@ export class SupervisorRPC extends WorkerEntrypoint {
208
292
  * SupervisorRPC runs in a different isolate from the DO that owns the
209
293
  * revision clock, so stamping here would cost its own round trip anyway,
210
294
  * and enveloping the existing returns would break `useRpcResource`
211
- * disposal, which targets the returned value.
295
+ * disposal, which targets the returned value. It only reads the
296
+ * invalidation log, so a dropped call is repeated like any other read.
212
297
  */
213
- async fsAcquire(epoch, cursor) {
214
- return this._call(this._fsOp('fsAcquire', [epoch, cursor]));
298
+ async fsAcquire(epoch, cursor, options) {
299
+ return this._call(this._fsRead('fsAcquire', options === undefined ? [epoch, cursor] : [epoch, cursor, options]));
215
300
  }
216
301
  async fsRevision(path) {
217
- return this._call(this._fsOp('fsRevision', [path]));
302
+ return this._call(this._fsRead('fsRevision', [path]));
218
303
  }
219
304
  /**
220
305
  * Enumerate the session filesystem, one bounded page at a time.
@@ -233,8 +318,12 @@ export class SupervisorRPC extends WorkerEntrypoint {
233
318
  * enumerating. `next === null` marks the final page, so a short page is
234
319
  * never mistaken for a complete listing.
235
320
  */
321
+ /** N18: `bytes` more for this process's facet store, reporting what it measures. */
322
+ async fsStorageGrant(facet, bytes, databaseSize) {
323
+ return this._call(this._fsOp('fsStorageGrant', [facet, bytes, databaseSize]));
324
+ }
236
325
  async fsList(after, limit) {
237
- return this._call(this._fsOp('fsList', [after ?? null, limit ?? null]));
326
+ return this._call(this._fsRead('fsList', [after ?? null, limit ?? null]));
238
327
  }
239
328
  /**
240
329
  * WebSocket relay. A facet does not open its own sockets: the supervisor
@@ -257,61 +346,65 @@ export class SupervisorRPC extends WorkerEntrypoint {
257
346
  return this._call(this._fsOp('wsClose', [id, code, reason]));
258
347
  }
259
348
  async fsOpen(path, flags) {
260
- return this._call(this._fsOp('fsOpen', [path, flags]));
349
+ return this._call(this._fsMutation('fsOpen', [path, flags]));
261
350
  }
262
351
  async fsRead(handleId, offset, length) {
263
352
  return this._call(this._fsOp('fsRead', [handleId, offset, length]));
264
353
  }
265
354
  async fsWrite(handleId, offset, bytes) {
266
- return this._call(this._fsOp('fsWrite', [handleId, offset, bytes]));
355
+ return this._call(this._fsMutation('fsWrite', [handleId, offset, bytes]));
267
356
  }
357
+ /** A descriptor's stat and its directory listing move nothing: re-sent like any read. */
268
358
  async fsFstat(...args) {
269
- return this._call(this._fsOp('fsFstat', args));
359
+ return this._call(this._fsRead('fsFstat', args));
270
360
  }
271
361
  async fsDup(...args) {
272
- return this._call(this._fsOp('fsDup', args));
362
+ return this._call(this._fsMutation('fsDup', args));
273
363
  }
274
364
  async fsSeek(...args) {
275
- return this._call(this._fsOp('fsSeek', args));
365
+ return this._call(this._fsMutation('fsSeek', args));
276
366
  }
277
367
  async fsSetStatus(...args) {
278
- return this._call(this._fsOp('fsSetStatus', args));
368
+ return this._call(this._fsMutation('fsSetStatus', args));
279
369
  }
280
370
  async fsReaddirHandle(...args) {
281
- return this._call(this._fsOp('fsReaddirHandle', args));
371
+ return this._call(this._fsRead('fsReaddirHandle', args));
282
372
  }
283
373
  async fsFtruncate(...args) {
284
- return this._call(this._fsOp('fsFtruncate', args));
374
+ return this._call(this._fsMutation('fsFtruncate', args));
285
375
  }
286
376
  async fsFchmod(...args) {
287
- return this._call(this._fsOp('fsFchmod', args));
377
+ return this._call(this._fsMutation('fsFchmod', args));
288
378
  }
289
379
  async fsFchown(...args) {
290
- return this._call(this._fsOp('fsFchown', args));
380
+ return this._call(this._fsMutation('fsFchown', args));
291
381
  }
292
382
  async fsFutimes(...args) {
293
- return this._call(this._fsOp('fsFutimes', args));
383
+ return this._call(this._fsMutation('fsFutimes', args));
294
384
  }
295
385
  async fsSync(...args) {
296
- return this._call(this._fsOp('fsSync', args));
386
+ return this._call(this._fsMutation('fsSync', args));
297
387
  }
298
388
  async fsRealpath(...args) {
299
- return this._call(this._fsOp('fsRealpath', args));
389
+ return this._call(this._fsRead('fsRealpath', args));
300
390
  }
301
391
  async fsRemove(...args) {
302
- return this._call(this._fsOp('fsRemove', args));
392
+ return this._call(this._fsMutation('fsRemove', args));
303
393
  }
304
394
  async fsCopyFile(...args) {
305
- return this._call(this._fsOp('fsCopyFile', args));
395
+ return this._call(this._fsMutation('fsCopyFile', args));
396
+ }
397
+ async fsCopyTree(...args) {
398
+ return this._call(this._fsMutation('fsCopyTree', args));
306
399
  }
307
400
  async fsAcquireExclusiveMutation(...args) {
308
- return this._call(this._fsOp('fsAcquireExclusiveMutation', args));
401
+ return this._call(this._fsMutation('fsAcquireExclusiveMutation', args));
309
402
  }
310
403
  async fsReleaseExclusiveMutation(...args) {
311
- return this._call(this._fsOp('fsReleaseExclusiveMutation', args));
404
+ return this._call(this._fsMutation('fsReleaseExclusiveMutation', args));
312
405
  }
313
406
  async fsClose(handleId) {
314
- return this._call(this._fsOp('fsClose', [handleId]));
407
+ return this._call(this._fsMutation('fsClose', [handleId]));
315
408
  }
316
409
  /**
317
410
  * Stateless ranged ops. Unlike fsOpen/fsRead/fsWrite they carry no
@@ -319,7 +412,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
319
412
  * hibernation and never rewrite whole files for partial updates.
320
413
  */
321
414
  async fsReadRange(path, offset, length) {
322
- return this._call(this._fsOp('fsReadRange', [path, offset, length]));
415
+ return this._call(this._fsRead('fsReadRange', [path, offset, length]));
323
416
  }
324
417
  /**
325
418
  * The same read with the session's content cache bypassed, for a boot spec's
@@ -328,7 +421,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
328
421
  * session's heap for the rest of its life.
329
422
  */
330
423
  async fsReadRangeUncached(path, offset, length) {
331
- return this._call(this._fsOp('fsReadRangeUncached', [path, offset, length]));
424
+ return this._call(this._fsRead('fsReadRangeUncached', [path, offset, length]));
332
425
  }
333
426
  /**
334
427
  * Read many ranges in ONE round trip — the read-side counterpart to
@@ -337,8 +430,11 @@ export class SupervisorRPC extends WorkerEntrypoint {
337
430
  * A file the caller knows is small is one entry; a large one is a run of
338
431
  * entries over the same path.
339
432
  *
433
+ * A request may instead ask for a path's lstat, so a process learning the
434
+ * metadata of many paths pays one round trip, not one per path.
435
+ *
340
436
  * Entries come back positionally, each carrying exactly what the
341
- * equivalent fsReadRange would have returned. The batch is bounded by
437
+ * equivalent fsReadRange or lstat would have returned. The batch is bounded by
342
438
  * FS_READ_BATCH_PATH_LIMIT paths and FS_READ_BATCH_REQUEST_BYTES of
343
439
  * requested range, and the supervisor rejects anything past either — never
344
440
  * a short result, which a caller could mistake for a short file.
@@ -348,27 +444,33 @@ export class SupervisorRPC extends WorkerEntrypoint {
348
444
  // returns at most the range asked for. Counting it keeps the
349
445
  // supervisor's heap estimate honest for the duration of the await, the
350
446
  // same accounting writeBatch does for its inbound payload.
351
- const payloadBytes = requests.reduce((total, request) => total + request.length, 0);
447
+ const payloadBytes = requests.reduce((total, request) => total + fsReadBatchRequestBytes(request), 0);
352
448
  setLastRpcFrame('fsReadBatch', payloadBytes);
353
449
  rpcPayloadStart(payloadBytes);
354
450
  try {
355
- return await this._call(this._fsOp('fsReadBatch', [requests]));
451
+ return await this._call(this._fsRead('fsReadBatch', [requests]));
356
452
  }
357
453
  finally {
358
454
  rpcPayloadEnd(payloadBytes);
359
455
  }
360
456
  }
361
457
  async fsWriteRange(path, offset, bytes) {
362
- return this._call(this._fsOp('fsWriteRange', [path, offset, bytes]));
458
+ return this._call(this._fsMutation('fsWriteRange', [path, offset, bytes]));
363
459
  }
460
+ /**
461
+ * An append and its acknowledgement carry the append ledger's own identity
462
+ * (writer, module incarnation, operation sequence), whose receipt the host
463
+ * keeps until the acknowledgement: a repeat of either applies nothing twice,
464
+ * so a dropped one is simply re-sent.
465
+ */
364
466
  async fsAppend(path, moduleId, operationId, bytes) {
365
- return this._call(this._op('fsAppend', [path, moduleId, operationId, bytes], { pid: this._pid(), writerId: this._writerId() }));
467
+ return this._call(this._resent({ op: 'fsAppend', args: [path, moduleId, operationId, bytes], pid: this._pid(), writerId: this._writerId() }));
366
468
  }
367
469
  async fsAppendAck(moduleId, operationId) {
368
- return this._call(this._op('fsAppendAck', [moduleId, operationId], { pid: this._pid(), writerId: this._writerId() }));
470
+ return this._call(this._resent({ op: 'fsAppendAck', args: [moduleId, operationId], pid: this._pid(), writerId: this._writerId() }));
369
471
  }
370
472
  async fsTruncate(path, size) {
371
- return this._call(this._fsOp('fsTruncate', [path, size]));
473
+ return this._call(this._fsMutation('fsTruncate', [path, size]));
372
474
  }
373
475
  /**
374
476
  * Bulk-write all inodes + chunks in ONE transactionSync on the supervisor.
@@ -391,7 +493,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
391
493
  setLastRpcFrame('writeBatch', payloadBytes);
392
494
  rpcPayloadStart(payloadBytes);
393
495
  try {
394
- return await this._call(this._fsOp('writeBatch', [payload]));
496
+ return await this._call(this._fsMutation('writeBatch', [payload]));
395
497
  }
396
498
  finally {
397
499
  rpcPayloadEnd(payloadBytes);
@@ -534,7 +636,8 @@ export class SupervisorRPC extends WorkerEntrypoint {
534
636
  * it, so a caller-supplied `put` would be a cross-tenant
535
637
  * code-execution primitive for anyone holding a supervisor stub. No
536
638
  * such RPC exists: the only bytes that reach `pc/<name>.json` are the
537
- * ones registry.npmjs.org served for that exact name.
639
+ * ones registry.npmjs.org served for that exact name; another registry
640
+ * (`options.registry`, the install's `NPM_REGISTRY`) has its own keys.
538
641
  */
539
642
  async getPackument(name, options) {
540
643
  const r2 = this._r2();
@@ -561,8 +664,8 @@ export class SupervisorRPC extends WorkerEntrypoint {
561
664
  * has error state it couldn't stream in-band (rare; main path drains
562
665
  * via __pendingIO first).
563
666
  */
564
- async reportExit(code, tail) {
565
- return this._call(this._op('reportExit', [code, tail || ''], { pid: this._reportingPid() }));
667
+ async reportExit(code, tail, residencyMisses, profileUnread) {
668
+ return this._call(this._op('reportExit', [code, tail || '', residencyMisses ?? [], profileUnread ?? null], { pid: this._reportingPid() }));
566
669
  }
567
670
  // ── Prefetch ──────────────────────────────────────────────────────────
568
671
  async prefetch(cwd, entryCode) {
@@ -609,11 +712,18 @@ export class SupervisorRPC extends WorkerEntrypoint {
609
712
  async cpStdinEnd(childPid) {
610
713
  return this._call(this._op('cpStdinEnd', [childPid]));
611
714
  }
612
- async cpReadStdin(childPid, waitMs) {
613
- return this._call(this._op('cpReadStdin', [childPid, waitMs]));
715
+ /**
716
+ * The three long polls that deliver to a process — its stdin, and a
717
+ * child's output and exit — carry the process's ACQUIRE arguments, and a
718
+ * reply that delivers anything carries the answer for them (`acquired`,
719
+ * session/rpc.ts `_acquireOnDelivery`), so the process applies it without
720
+ * asking. The caller's pid names whose credential answers it.
721
+ */
722
+ async cpReadStdin(childPid, waitMs, acquire) {
723
+ return this._call(this._op('cpReadStdin', [childPid, waitMs, acquire ?? null], { pid: this._reportingPid() }));
614
724
  }
615
- async cpReadOutput(childPid, fd, sinceSeq, waitMs) {
616
- return this._call(this._op('cpReadOutput', [childPid, fd, sinceSeq, waitMs]));
725
+ async cpReadOutput(childPid, fd, sinceSeq, waitMs, acquire) {
726
+ return this._call(this._op('cpReadOutput', [childPid, fd, sinceSeq, waitMs, acquire ?? null], { pid: this._reportingPid() }));
617
727
  }
618
728
  async cpDrainOutput(childPid) {
619
729
  return this._call(this._op('cpDrainOutput', [childPid]));
@@ -621,8 +731,8 @@ export class SupervisorRPC extends WorkerEntrypoint {
621
731
  async cpKill(childPid, signal) {
622
732
  return this._call(this._op('cpKill', [childPid, signal]));
623
733
  }
624
- async cpWait(childPid, waitMs) {
625
- return this._call(this._op('cpWait', [childPid, waitMs]));
734
+ async cpWait(childPid, waitMs, acquire) {
735
+ return this._call(this._op('cpWait', [childPid, waitMs, acquire ?? null], { pid: this._reportingPid() }));
626
736
  }
627
737
  /**
628
738
  * child-process isolation gap #1: dispatch a single cp.spawn request inline using
@@ -1 +1 @@
1
- {"version":3,"file":"vite-command.d.ts","sourceRoot":"","sources":["../../src/session/vite-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyBH,KAAK,QAAQ,GAAG,GAAG,CAAC;AAEpB,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,IAChC,KAAK,GAAG,KAAG,OAAO,CAAC,MAAM,CAAC,CAmlBzC"}
1
+ {"version":3,"file":"vite-command.d.ts","sourceRoot":"","sources":["../../src/session/vite-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyBH,KAAK,QAAQ,GAAG,GAAG,CAAC;AAEpB,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,IAChC,KAAK,GAAG,KAAG,OAAO,CAAC,MAAM,CAAC,CAslBzC"}
@@ -15,9 +15,9 @@
15
15
  import { normalizeVfsPath, parentVfsPath, resolveVfsPath, stripLeadingSlashes } from '@nimbus-sh/core/vfs/path.js';
16
16
  import { errorText } from '@nimbus-sh/core/_shared/error-text.js';
17
17
  import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
18
- import { parseViteConfigSource, viteBuildBlockingPlugins, unhandledVitePlugins } from '@nimbus-sh/core/runtime/vite-config-parser.js';
18
+ import { parseViteConfigSource, parseViteConfigTypeScript, viteBuildBlockingPlugins, unhandledVitePlugins } from '@nimbus-sh/core/runtime/vite-config-parser.js';
19
19
  import { findHtmlScriptEntrypoint, rewriteViteBuildHtml } from '../runtime/html-entrypoint.js';
20
- import { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
20
+ import { supervisorEsbuildService } from '../facets/esbuild-transform.js';
21
21
  import { ViteDevServer } from '../facets/vite-dev-server.js';
22
22
  import { shouldUseRealVite } from '../facets/cirrus-real.js';
23
23
  import { makeLongRunningPortStub, resolveLongRunningPort, expandArgvShellDefaults, } from '@nimbus-sh/core/runtime/long-running-handle.js';
@@ -49,15 +49,18 @@ export function createViteCommand(self) {
49
49
  const cfgPath = cwd + '/' + cfgName;
50
50
  if (kernelFs.exists(cfgPath)) {
51
51
  try {
52
- let cfgCode = kernelFs.readFileString(cfgPath);
53
- // Transform TS to JS
54
- if (cfgName.endsWith('.ts')) {
55
- if (!self.esbuildService)
56
- self.esbuildService = new EsbuildService(kernelFs);
57
- const t = await self.esbuildService.transform(cfgCode, { loader: 'ts', format: 'esm' });
58
- cfgCode = t.code;
59
- }
60
- Object.assign(viteConfig, parseViteConfigSource(cfgCode));
52
+ const cfgCode = kernelFs.readFileString(cfgPath);
53
+ // A .ts config needs esbuild only when it holds type syntax
54
+ // (parseViteConfigTypeScript); a plain one is read as is, so a
55
+ // fresh session's `vite` does not wait on the esbuild facet.
56
+ const parsed = cfgName.endsWith('.ts')
57
+ ? await parseViteConfigTypeScript(cfgCode, async (source) => {
58
+ if (!self.esbuildService)
59
+ self.esbuildService = supervisorEsbuildService(self.ctx, self.env, self.getFilesystemAuthority().namespaceFs(CRED_KERNEL));
60
+ return (await self.esbuildService.transform(source, { loader: 'ts', format: 'esm' })).code;
61
+ })
62
+ : parseViteConfigSource(cfgCode);
63
+ Object.assign(viteConfig, parsed);
61
64
  }
62
65
  catch (e) {
63
66
  ctx.stderr.write(`Warning: could not parse ${cfgName}: ${e?.message}\n`);
@@ -89,7 +92,7 @@ export function createViteCommand(self) {
89
92
  ' (' + buildSkippedPlugins.join(', ') + '); output is the plain-Vite bundle.\n');
90
93
  }
91
94
  if (!self.esbuildService)
92
- self.esbuildService = new EsbuildService(kernelFs);
95
+ self.esbuildService = supervisorEsbuildService(self.ctx, self.env, self.getFilesystemAuthority().namespaceFs(CRED_KERNEL));
93
96
  const htmlPath = cwd + '/index.html';
94
97
  let entryPoint = cwd + '/src/main.tsx';
95
98
  let origHtml = '';
@@ -295,7 +298,7 @@ export function createViteCommand(self) {
295
298
  }
296
299
  // Start vite on the dist directory
297
300
  if (!self.esbuildService)
298
- self.esbuildService = new EsbuildService(kernelFs);
301
+ self.esbuildService = supervisorEsbuildService(self.ctx, self.env, self.getFilesystemAuthority().namespaceFs(CRED_KERNEL));
299
302
  if (self.viteDevServer?.isRunning)
300
303
  self.viteDevServer.stop();
301
304
  const previewBasePath = self.viteBasePath;
@@ -502,7 +505,7 @@ export function createViteCommand(self) {
502
505
  ' (' + devSkipped.join(', ') + '); serving the plain-Vite app.\n');
503
506
  }
504
507
  if (!self.esbuildService)
505
- self.esbuildService = new EsbuildService(kernelFs);
508
+ self.esbuildService = supervisorEsbuildService(self.ctx, self.env, self.getFilesystemAuthority().namespaceFs(CRED_KERNEL));
506
509
  const previewBasePath = self.viteBasePath;
507
510
  const viteDefine = viteConfig.define;
508
511
  // Vite dev servers are represented as long-running process-table
@@ -1 +1 @@
1
- {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../src/session/ws.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../src/session/ws.ts"],"names":[],"mappings":"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"}
@@ -316,15 +316,27 @@ export async function wsMessage(self, ws, message) {
316
316
  return;
317
317
  }
318
318
  const terminalMessage = TerminalMessageSchema.safeParse(value);
319
- if (self.terminal && terminalMessage.success)
320
- self.terminal.handleMessage(terminalMessage.data);
319
+ if (self.terminal && terminalMessage.success) {
320
+ const terminal = self.terminal;
321
+ const completion = terminal.handleMessage(terminalMessage.data);
322
+ if (completion) {
323
+ const task = completion.catch((error) => {
324
+ console.error('[nimbus] terminal input failed:', errorText(error));
325
+ terminal.writeln(`terminal input failed: ${errorText(error)}`);
326
+ }).finally(() => snapshotShellState(self));
327
+ const ctx = self.ctx;
328
+ // Hand the command's completion to waitUntil so its failure is observed and
329
+ // the final snapshot runs, without holding the WebSocket event open or
330
+ // serializing later input. DurableObjectState.waitUntil does not extend the
331
+ // object's lifetime (Cloudflare docs, api/state/#waituntil).
332
+ if (ctx?.waitUntil)
333
+ ctx.waitUntil(task);
334
+ else
335
+ await task;
336
+ }
337
+ }
321
338
  // ── B'.1 snapshot ───────────────────────────────────────────────
322
- // Persist Shell state to DO SQLite after the terminal has handled
323
- // the user's keystroke. The Shell builtin `cd` mutates this.cwd
324
- // synchronously inside executeLine, so by the time we reach this
325
- // line a `cd example-app\r` has already taken effect and we capture the
326
- // new cwd. Cheap when nothing has changed; SESSION_ENV_MAX_BYTES
327
- // is the only failure mode and is logged, not thrown.
339
+ // Capture immediate keystroke changes; async input completion takes a final snapshot above.
328
340
  snapshotShellState(self);
329
341
  }
330
342
  catch (e) {