@nimbus-sh/worker 0.10.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 (260) 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/facets/cirrus-real.d.ts.map +1 -1
  19. package/dist/facets/cirrus-real.js +9 -4
  20. package/dist/facets/compose.d.ts +11 -7
  21. package/dist/facets/compose.d.ts.map +1 -1
  22. package/dist/facets/compose.js +8 -38
  23. package/dist/facets/data-plan.d.ts +79 -0
  24. package/dist/facets/data-plan.d.ts.map +1 -0
  25. package/dist/facets/data-plan.js +477 -0
  26. package/dist/facets/esbuild-transform.d.ts +36 -10
  27. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  28. package/dist/facets/esbuild-transform.js +216 -20
  29. package/dist/facets/exec-telemetry.d.ts +7 -3
  30. package/dist/facets/exec-telemetry.d.ts.map +1 -1
  31. package/dist/facets/manager.d.ts +179 -78
  32. package/dist/facets/manager.d.ts.map +1 -1
  33. package/dist/facets/manager.js +1221 -649
  34. package/dist/facets/opencode-staging.d.ts +0 -2
  35. package/dist/facets/opencode-staging.d.ts.map +1 -1
  36. package/dist/facets/opencode-staging.js +5 -11
  37. package/dist/facets/process.d.ts +4 -0
  38. package/dist/facets/process.d.ts.map +1 -1
  39. package/dist/facets/process.js +3 -0
  40. package/dist/facets/read-profile.d.ts +149 -0
  41. package/dist/facets/read-profile.d.ts.map +1 -0
  42. package/dist/facets/read-profile.js +483 -0
  43. package/dist/facets/resident-identity.d.ts +0 -1
  44. package/dist/facets/resident-identity.d.ts.map +1 -1
  45. package/dist/facets/resident-identity.js +0 -3
  46. package/dist/facets/wasm-swap-registry.d.ts +34 -25
  47. package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
  48. package/dist/facets/wasm-swap-registry.js +62 -56
  49. package/dist/facets/ws-terminal.d.ts +4 -4
  50. package/dist/facets/ws-terminal.d.ts.map +1 -1
  51. package/dist/facets/ws-terminal.js +4 -5
  52. package/dist/git/commands.d.ts +16 -9
  53. package/dist/git/commands.d.ts.map +1 -1
  54. package/dist/git/commands.js +1818 -188
  55. package/dist/git/network-facet.d.ts +10 -2
  56. package/dist/git/network-facet.d.ts.map +1 -1
  57. package/dist/git/network-facet.js +62 -16
  58. package/dist/git/unified-diff.d.ts +74 -0
  59. package/dist/git/unified-diff.d.ts.map +1 -0
  60. package/dist/git/unified-diff.js +676 -0
  61. package/dist/git-bundle.generated.d.ts +7 -11
  62. package/dist/git-bundle.generated.d.ts.map +1 -1
  63. package/dist/git-bundle.generated.js +7 -11
  64. package/dist/hosted/commands.d.ts.map +1 -1
  65. package/dist/hosted/commands.js +62 -243
  66. package/dist/hosted/runtime.d.ts +19 -13
  67. package/dist/hosted/runtime.d.ts.map +1 -1
  68. package/dist/hosted/runtime.js +71 -14
  69. package/dist/hosted/services.d.ts +6 -7
  70. package/dist/hosted/services.d.ts.map +1 -1
  71. package/dist/hosted/services.js +10 -14
  72. package/dist/hosted/session.d.ts +165 -0
  73. package/dist/hosted/session.d.ts.map +1 -0
  74. package/dist/hosted/session.js +132 -0
  75. package/dist/loaders/generated-workers.d.ts +5 -2
  76. package/dist/loaders/generated-workers.d.ts.map +1 -1
  77. package/dist/loaders/generated-workers.js +6 -3
  78. package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
  79. package/dist/loaders/npm-resolve-preamble.js +0 -27
  80. package/dist/node-shims-artifact.generated.d.ts +13 -4
  81. package/dist/node-shims-artifact.generated.d.ts.map +1 -1
  82. package/dist/node-shims-artifact.generated.js +16 -7
  83. package/dist/npm/bin-links.d.ts +3 -1
  84. package/dist/npm/bin-links.d.ts.map +1 -1
  85. package/dist/npm/bin-links.js +32 -8
  86. package/dist/npm/cache.d.ts +5 -0
  87. package/dist/npm/cache.d.ts.map +1 -1
  88. package/dist/npm/cache.js +13 -1
  89. package/dist/npm/install-batch-facet.d.ts +2 -2
  90. package/dist/npm/install-batch-facet.js +3 -3
  91. package/dist/npm/installer.d.ts +17 -1
  92. package/dist/npm/installer.d.ts.map +1 -1
  93. package/dist/npm/installer.js +159 -20
  94. package/dist/npm/package-lock.d.ts +32 -0
  95. package/dist/npm/package-lock.d.ts.map +1 -0
  96. package/dist/npm/package-lock.js +65 -0
  97. package/dist/npm/pre-bundle-facet.d.ts +1 -30
  98. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  99. package/dist/npm/pre-bundle-facet.js +3 -5
  100. package/dist/npm/r2-cache.d.ts +2 -0
  101. package/dist/npm/r2-cache.d.ts.map +1 -1
  102. package/dist/npm/r2-cache.js +2 -0
  103. package/dist/npm/resolve-one-facet.d.ts.map +1 -1
  104. package/dist/npm/resolve-one-facet.js +14 -3
  105. package/dist/npm/resolver.d.ts +0 -7
  106. package/dist/npm/resolver.d.ts.map +1 -1
  107. package/dist/npm/resolver.js +0 -22
  108. package/dist/opencode-artifact.generated.js +2 -2
  109. package/dist/replica/routing.d.ts +2 -38
  110. package/dist/replica/routing.d.ts.map +1 -1
  111. package/dist/replica/routing.js +3 -55
  112. package/dist/router/index.d.ts.map +1 -1
  113. package/dist/router/index.js +15 -2
  114. package/dist/router/public-directory.d.ts +18 -0
  115. package/dist/router/public-directory.d.ts.map +1 -1
  116. package/dist/router/public-directory.js +1 -1
  117. package/dist/router/remote-api.d.ts +3 -1
  118. package/dist/router/remote-api.d.ts.map +1 -1
  119. package/dist/router/remote-api.js +42 -79
  120. package/dist/runtime/bash-repl.js +2 -2
  121. package/dist/runtime/bun-repl.d.ts.map +1 -1
  122. package/dist/runtime/bun-repl.js +2 -13
  123. package/dist/runtime/esbuild-wasm-bytes.d.ts +17 -5
  124. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  125. package/dist/runtime/esbuild-wasm-bytes.js +43 -94
  126. package/dist/runtime/git-bundle-artifact.d.ts +17 -0
  127. package/dist/runtime/git-bundle-artifact.d.ts.map +1 -0
  128. package/dist/runtime/git-bundle-artifact.js +25 -0
  129. package/dist/runtime/node-repl.d.ts.map +1 -1
  130. package/dist/runtime/node-repl.js +2 -13
  131. package/dist/runtime/node-shims-artifact.d.ts +22 -23
  132. package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
  133. package/dist/runtime/node-shims-artifact.js +48 -78
  134. package/dist/runtime/node-shims.d.ts.map +1 -1
  135. package/dist/runtime/node-shims.js +1567 -864
  136. package/dist/runtime/opencode-artifact.d.ts.map +1 -1
  137. package/dist/runtime/opencode-artifact.js +17 -53
  138. package/dist/runtime/opencode-facet-runner.d.ts +3 -6
  139. package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
  140. package/dist/runtime/opencode-facet-runner.js +45 -10
  141. package/dist/runtime/opentui-wasm-bytes.d.ts.map +1 -1
  142. package/dist/runtime/opentui-wasm-bytes.js +16 -49
  143. package/dist/runtime/package-manager.d.ts +0 -2
  144. package/dist/runtime/package-manager.d.ts.map +1 -1
  145. package/dist/runtime/package-manager.js +0 -3
  146. package/dist/runtime/python-repl.d.ts.map +1 -1
  147. package/dist/runtime/python-repl.js +2 -2
  148. package/dist/runtime/repl-session.d.ts +10 -0
  149. package/dist/runtime/repl-session.d.ts.map +1 -1
  150. package/dist/runtime/repl-session.js +7 -0
  151. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  152. package/dist/runtime/ruby-repl.js +2 -2
  153. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  154. package/dist/runtime/ruby-resident.js +36 -35
  155. package/dist/runtime/runtime-catalog.d.ts +10 -3
  156. package/dist/runtime/runtime-catalog.d.ts.map +1 -1
  157. package/dist/runtime/runtime-catalog.js +125 -15
  158. package/dist/runtime/sqlite-wasm-bytes.d.ts +4 -4
  159. package/dist/runtime/sqlite-wasm-bytes.d.ts.map +1 -1
  160. package/dist/runtime/sqlite-wasm-bytes.js +21 -57
  161. package/dist/runtime/staged-source.d.ts +67 -0
  162. package/dist/runtime/staged-source.d.ts.map +1 -0
  163. package/dist/runtime/staged-source.js +113 -0
  164. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  165. package/dist/runtime/wasi/preamble.js +0 -24
  166. package/dist/runtime-catalog.generated.js +1 -1
  167. package/dist/session/agent.d.ts +14 -2
  168. package/dist/session/agent.d.ts.map +1 -1
  169. package/dist/session/agent.js +57 -19
  170. package/dist/session/ai.d.ts +0 -11
  171. package/dist/session/ai.d.ts.map +1 -1
  172. package/dist/session/ai.js +0 -4
  173. package/dist/session/fs-watch.d.ts +0 -11
  174. package/dist/session/fs-watch.d.ts.map +1 -1
  175. package/dist/session/fs-watch.js +0 -21
  176. package/dist/session/hibernation.d.ts +34 -11
  177. package/dist/session/hibernation.d.ts.map +1 -1
  178. package/dist/session/hibernation.js +46 -29
  179. package/dist/session/init-phases.d.ts +1 -2
  180. package/dist/session/init-phases.d.ts.map +1 -1
  181. package/dist/session/init-phases.js +1 -2
  182. package/dist/session/init.d.ts.map +1 -1
  183. package/dist/session/init.js +7 -20
  184. package/dist/session/keys.d.ts +0 -3
  185. package/dist/session/keys.d.ts.map +1 -1
  186. package/dist/session/keys.js +0 -3
  187. package/dist/session/legacy-reset.d.ts +11 -0
  188. package/dist/session/legacy-reset.d.ts.map +1 -0
  189. package/dist/session/legacy-reset.js +16 -0
  190. package/dist/session/nimbus-session.d.ts +41 -15
  191. package/dist/session/nimbus-session.d.ts.map +1 -1
  192. package/dist/session/nimbus-session.js +64 -70
  193. package/dist/session/npm-install-port.d.ts.map +1 -1
  194. package/dist/session/npm-install-port.js +1 -0
  195. package/dist/session/programmatic.d.ts +13 -18
  196. package/dist/session/programmatic.d.ts.map +1 -1
  197. package/dist/session/programmatic.js +53 -39
  198. package/dist/session/replica-routes.d.ts +2 -3
  199. package/dist/session/replica-routes.d.ts.map +1 -1
  200. package/dist/session/replica-routes.js +3 -10
  201. package/dist/session/routes.d.ts.map +1 -1
  202. package/dist/session/routes.js +7 -10
  203. package/dist/session/rpc.d.ts +104 -20
  204. package/dist/session/rpc.d.ts.map +1 -1
  205. package/dist/session/rpc.js +184 -37
  206. package/dist/session/start-real-vite.js +2 -2
  207. package/dist/session/state-store.d.ts +1 -17
  208. package/dist/session/state-store.d.ts.map +1 -1
  209. package/dist/session/state-store.js +3 -53
  210. package/dist/session/supervisor-op.d.ts +7 -0
  211. package/dist/session/supervisor-op.d.ts.map +1 -1
  212. package/dist/session/supervisor-op.js +6 -1
  213. package/dist/session/supervisor-rpc.d.ts +104 -15
  214. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  215. package/dist/session/supervisor-rpc.js +188 -71
  216. package/dist/session/vite-command.d.ts.map +1 -1
  217. package/dist/session/vite-command.js +17 -14
  218. package/dist/session/ws.d.ts.map +1 -1
  219. package/dist/session/ws.js +20 -8
  220. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  221. package/dist/shell/npm-bin-entrypoints.js +30 -5
  222. package/dist/vfs/facet-resident-limits.d.ts +33 -0
  223. package/dist/vfs/facet-resident-limits.d.ts.map +1 -0
  224. package/dist/vfs/facet-resident-limits.js +33 -0
  225. package/dist/vfs/facet-resident-store.d.ts +2 -10
  226. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  227. package/dist/vfs/facet-resident-store.js +1594 -219
  228. package/dist/workspace-host.d.ts +2 -0
  229. package/dist/workspace-host.d.ts.map +1 -1
  230. package/dist/workspace-host.js +2 -0
  231. package/dist/wrangler/nimbus-wrangler.d.ts +0 -2
  232. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  233. package/dist/wrangler/nimbus-wrangler.js +5 -22
  234. package/package.json +8 -7
  235. package/public/_assets/opencode/1.16.2/index-attach.js +2 -2
  236. package/public/_assets/runtime/esbuild-cli-c4fda380d52befc1.js +6892 -0
  237. package/public/_assets/runtime/git-de3e60c9f3ff006a.js +103 -0
  238. package/public/_assets/runtime/{node-shims-7b4dd7a76d95d21e.js → node-shims-06565b121555c194.js} +2089 -861
  239. package/public/_assets/runtime/resident-store-6ffb1939c53d982f.js +2264 -0
  240. package/public/_assets/runtime/vfs-write-ledger-e501e8fc793bc2c1.js +889 -0
  241. package/scripts/build-opencode-attach-entry.mjs +51 -1
  242. package/scripts/bundle-facet-workers.mjs +114 -3
  243. package/scripts/bundle-git.mjs +88 -27
  244. package/scripts/bundle-node-shims.mjs +72 -46
  245. package/scripts/bundle-runtime.mjs +18 -421
  246. package/scripts/cf-git-patch.mjs +137 -0
  247. package/scripts/patch-install-deps.mjs +49 -96
  248. package/scripts/runtime-specs.mjs +435 -0
  249. package/vendor/git-http.generated.d.ts +82 -0
  250. package/vendor/git-upstream.generated.d.ts +4378 -0
  251. package/vendor/git.LICENSE +7 -0
  252. package/vendor/git.generated.d.mts +21 -0
  253. package/vendor/git.generated.mjs +103 -0
  254. package/dist/replica/suspension.d.ts +0 -36
  255. package/dist/replica/suspension.d.ts.map +0 -1
  256. package/dist/replica/suspension.js +0 -49
  257. package/dist/runtime/static-server.d.ts +0 -3
  258. package/dist/runtime/static-server.d.ts.map +0 -1
  259. package/dist/runtime/static-server.js +0 -133
  260. package/scripts/clean-dist.mjs +0 -7
@@ -4,12 +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, route: HostRoute }
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
11
  * route — the host namespace and dispatch method, minted with the binding
12
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)
13
16
  *
14
17
  * Methods callable by facets via RPC:
15
18
  * readFile(path) → string | null
@@ -22,16 +25,29 @@
22
25
  * fsOpen/fsRead/fsWrite/fsClose/readlink/symlink/rename/rmdir/fsRevision
23
26
  * fsReadRange/fsWriteRange/fsAppend/fsAppendAck/fsTruncate
24
27
  * → shared RuntimeFsBridge operations
25
- * fsReadBatch(requests) → per-range results (many reads, one round trip)
28
+ * fsReadBatch(requests) → per-request results (many reads and lstats, one round trip)
26
29
  * fsList(after, limit) → one page of what EXISTS, with per-path revisions
27
30
  * writeBatch(payload) → { inodes, chunks } (bulk atomic write)
28
31
  * stdout(data) → void (pushed to WebSocket + ring buffer)
29
32
  * stderr(data) → void
30
33
  * reportExit(code, tail?) → void (called from facet's finally block)
31
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.
32
45
  */
33
46
  import { WorkerEntrypoint } from 'cloudflare:workers';
34
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';
35
51
  // W5: OOM discriminator — record last-known RPC frame on writeBatch entry
36
52
  import { setLastRpcFrame } from '@nimbus-sh/platform/oom-discriminator.js';
37
53
  // Phase 2 A'.2 — supervisor in-flight RPC payload byte tracking.
@@ -39,6 +55,7 @@ import { rpcPayloadStart, rpcPayloadEnd } from '@nimbus-sh/platform/diag-counter
39
55
  // W4: R2 cross-tenant npm cache (tarballs + packuments)
40
56
  import { R2CacheClient, MAX_R2_TARBALL_BYTES } from '../npm/r2-cache.js';
41
57
  import { useRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
58
+ import { fsReadBatchRequestBytes } from './rpc.js';
42
59
  import { W7_MAX_RECORD_BYTES } from '@nimbus-sh/platform/w7-frame.js';
43
60
  /**
44
61
  * Drain the per-call event list captured by R2CacheClient during this
@@ -80,29 +97,104 @@ function _estimateWriteBatchBytes(payload) {
80
97
  // The fabric mints `env.SUPERVISOR` bindings for the programs it hosts; the
81
98
  // worker's composition root (src/index.ts) names this class to the fabric
82
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;
83
111
  export class SupervisorRPC extends WorkerEntrypoint {
84
112
  /**
85
- * Resolve the host anew for each WorkerEntrypoint invocation, by the
86
- * route the binding carries. The platform serves this entrypoint from
87
- * whichever isolate it likes; the props were minted in the host's.
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.
88
116
  */
89
- _dispatch() {
90
- const props = (this.ctx.props ?? {});
91
- const doId = props.doId;
117
+ _host() {
118
+ const doId = this.ctx.props?.doId;
92
119
  if (typeof doId !== 'string' || doId.length === 0) {
93
120
  throw new Error('SupervisorRPC: missing doId in props');
94
121
  }
95
- const namespace = hostNamespaceBinding(this.env, 'SupervisorRPC', props.route);
96
- const stub = namespace.get(namespace.idFromString(doId));
97
- return hostOpDispatch(stub, 'SupervisorRPC', props.route);
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;
98
127
  }
99
128
  _op(op, args = [], extra = {}) {
100
- return this._dispatch()({ op, args, ...extra });
129
+ return hostOpDispatch(this._host(), 'SupervisorRPC', this._route())({ op, args, ...extra });
101
130
  }
102
131
  /** Stamp filesystem credentials from the binding, not the supplied arguments. */
103
132
  _fsOp(op, args = []) {
104
133
  return this._op(op, args, { pid: this._pid() });
105
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
+ }
106
198
  _reportingPid() {
107
199
  const pid = this.ctx.props?.pid;
108
200
  return typeof pid === 'number' && Number.isInteger(pid) && pid > 0 ? pid : 0;
@@ -126,14 +218,14 @@ export class SupervisorRPC extends WorkerEntrypoint {
126
218
  }
127
219
  // ── Filesystem RPC ────────────────────────────────────────────────────
128
220
  async readFile(path) {
129
- return this._call(this._fsOp('readFile', [path]));
221
+ return this._call(this._fsRead('readFile', [path]));
130
222
  }
131
223
  /**
132
224
  * Read a file as raw bytes. Used by the git network facet for binary
133
225
  * object/pack files where the text readFile would corrupt content.
134
226
  */
135
227
  async readFileBytes(path) {
136
- return this._call(this._fsOp('readFileBytes', [path]));
228
+ return this._call(this._fsRead('readFileBytes', [path]));
137
229
  }
138
230
  async writeFile(path, content) {
139
231
  // binary-fs wave: accept Uint8Array natively. Pre-fix this RPC was
@@ -141,55 +233,55 @@ export class SupervisorRPC extends WorkerEntrypoint {
141
233
  // decode every Uint8Array write — mangling bytes ≥ 0x80 to U+FFFD
142
234
  // and corrupting binary content. RPC structured-clone handles
143
235
  // Uint8Array transparently; downstream _rpcWriteFile also accepts
144
- return this._call(this._fsOp('writeFile', [path, content]));
236
+ return this._call(this._fsMutation('writeFile', [path, content]));
145
237
  }
146
238
  async stat(path, options) {
147
- return this._call(this._fsOp('stat', [path, options]));
239
+ return this._call(this._fsRead('stat', [path, options]));
148
240
  }
149
241
  async lstat(path) {
150
- return this._call(this._fsOp('lstat', [path]));
242
+ return this._call(this._fsRead('lstat', [path]));
151
243
  }
152
244
  async hasLegacySymlinkUnder(path) {
153
- return this._call(this._fsOp('hasLegacySymlinkUnder', [path]));
245
+ return this._call(this._fsRead('hasLegacySymlinkUnder', [path]));
154
246
  }
155
247
  async utimes(path, atimeMs, mtimeMs) {
156
- return this._call(this._fsOp('utimes', [path, atimeMs, mtimeMs]));
248
+ return this._call(this._fsMutation('utimes', [path, atimeMs, mtimeMs]));
157
249
  }
158
250
  async chmod(path, mode) {
159
- return this._call(this._fsOp('chmod', [path, mode]));
251
+ return this._call(this._fsMutation('chmod', [path, mode]));
160
252
  }
161
253
  async access(path, mode) {
162
- return this._call(this._fsOp('access', [path, mode]));
254
+ return this._call(this._fsRead('access', [path, mode]));
163
255
  }
164
256
  async chown(path, uid, gid, options) {
165
- return this._call(this._fsOp('chown', [path, uid, gid, options]));
257
+ return this._call(this._fsMutation('chown', [path, uid, gid, options]));
166
258
  }
167
259
  async setUmask(mask) {
168
260
  return this._call(this._fsOp('setUmask', [mask]));
169
261
  }
170
262
  async readdir(path) {
171
- return this._call(this._fsOp('readdir', [path]));
263
+ return this._call(this._fsRead('readdir', [path]));
172
264
  }
173
265
  async exists(path) {
174
- return this._call(this._fsOp('exists', [path]));
266
+ return this._call(this._fsRead('exists', [path]));
175
267
  }
176
268
  async mkdir(path, options) {
177
- return this._call(this._fsOp('mkdir', [path, options]));
269
+ return this._call(this._fsMutation('mkdir', [path, options]));
178
270
  }
179
271
  async rmdir(path) {
180
- return this._call(this._fsOp('rmdir', [path]));
272
+ return this._call(this._fsMutation('rmdir', [path]));
181
273
  }
182
274
  async rename(from, to) {
183
- return this._call(this._fsOp('rename', [from, to]));
275
+ return this._call(this._fsMutation('rename', [from, to]));
184
276
  }
185
277
  async unlink(path) {
186
- return this._call(this._fsOp('unlink', [path]));
278
+ return this._call(this._fsMutation('unlink', [path]));
187
279
  }
188
280
  async readlink(path) {
189
- return this._call(this._fsOp('readlink', [path]));
281
+ return this._call(this._fsRead('readlink', [path]));
190
282
  }
191
283
  async symlink(target, path) {
192
- return this._call(this._fsOp('symlink', [target, path]));
284
+ return this._call(this._fsMutation('symlink', [target, path]));
193
285
  }
194
286
  /**
195
287
  * ACQUIRE: the paths mutated since the facet's cursor, plus a fresh
@@ -200,13 +292,14 @@ export class SupervisorRPC extends WorkerEntrypoint {
200
292
  * SupervisorRPC runs in a different isolate from the DO that owns the
201
293
  * revision clock, so stamping here would cost its own round trip anyway,
202
294
  * and enveloping the existing returns would break `useRpcResource`
203
- * 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.
204
297
  */
205
- async fsAcquire(epoch, cursor) {
206
- 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]));
207
300
  }
208
301
  async fsRevision(path) {
209
- return this._call(this._fsOp('fsRevision', [path]));
302
+ return this._call(this._fsRead('fsRevision', [path]));
210
303
  }
211
304
  /**
212
305
  * Enumerate the session filesystem, one bounded page at a time.
@@ -225,8 +318,12 @@ export class SupervisorRPC extends WorkerEntrypoint {
225
318
  * enumerating. `next === null` marks the final page, so a short page is
226
319
  * never mistaken for a complete listing.
227
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
+ }
228
325
  async fsList(after, limit) {
229
- return this._call(this._fsOp('fsList', [after ?? null, limit ?? null]));
326
+ return this._call(this._fsRead('fsList', [after ?? null, limit ?? null]));
230
327
  }
231
328
  /**
232
329
  * WebSocket relay. A facet does not open its own sockets: the supervisor
@@ -249,61 +346,65 @@ export class SupervisorRPC extends WorkerEntrypoint {
249
346
  return this._call(this._fsOp('wsClose', [id, code, reason]));
250
347
  }
251
348
  async fsOpen(path, flags) {
252
- return this._call(this._fsOp('fsOpen', [path, flags]));
349
+ return this._call(this._fsMutation('fsOpen', [path, flags]));
253
350
  }
254
351
  async fsRead(handleId, offset, length) {
255
352
  return this._call(this._fsOp('fsRead', [handleId, offset, length]));
256
353
  }
257
354
  async fsWrite(handleId, offset, bytes) {
258
- return this._call(this._fsOp('fsWrite', [handleId, offset, bytes]));
355
+ return this._call(this._fsMutation('fsWrite', [handleId, offset, bytes]));
259
356
  }
357
+ /** A descriptor's stat and its directory listing move nothing: re-sent like any read. */
260
358
  async fsFstat(...args) {
261
- return this._call(this._fsOp('fsFstat', args));
359
+ return this._call(this._fsRead('fsFstat', args));
262
360
  }
263
361
  async fsDup(...args) {
264
- return this._call(this._fsOp('fsDup', args));
362
+ return this._call(this._fsMutation('fsDup', args));
265
363
  }
266
364
  async fsSeek(...args) {
267
- return this._call(this._fsOp('fsSeek', args));
365
+ return this._call(this._fsMutation('fsSeek', args));
268
366
  }
269
367
  async fsSetStatus(...args) {
270
- return this._call(this._fsOp('fsSetStatus', args));
368
+ return this._call(this._fsMutation('fsSetStatus', args));
271
369
  }
272
370
  async fsReaddirHandle(...args) {
273
- return this._call(this._fsOp('fsReaddirHandle', args));
371
+ return this._call(this._fsRead('fsReaddirHandle', args));
274
372
  }
275
373
  async fsFtruncate(...args) {
276
- return this._call(this._fsOp('fsFtruncate', args));
374
+ return this._call(this._fsMutation('fsFtruncate', args));
277
375
  }
278
376
  async fsFchmod(...args) {
279
- return this._call(this._fsOp('fsFchmod', args));
377
+ return this._call(this._fsMutation('fsFchmod', args));
280
378
  }
281
379
  async fsFchown(...args) {
282
- return this._call(this._fsOp('fsFchown', args));
380
+ return this._call(this._fsMutation('fsFchown', args));
283
381
  }
284
382
  async fsFutimes(...args) {
285
- return this._call(this._fsOp('fsFutimes', args));
383
+ return this._call(this._fsMutation('fsFutimes', args));
286
384
  }
287
385
  async fsSync(...args) {
288
- return this._call(this._fsOp('fsSync', args));
386
+ return this._call(this._fsMutation('fsSync', args));
289
387
  }
290
388
  async fsRealpath(...args) {
291
- return this._call(this._fsOp('fsRealpath', args));
389
+ return this._call(this._fsRead('fsRealpath', args));
292
390
  }
293
391
  async fsRemove(...args) {
294
- return this._call(this._fsOp('fsRemove', args));
392
+ return this._call(this._fsMutation('fsRemove', args));
295
393
  }
296
394
  async fsCopyFile(...args) {
297
- 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));
298
399
  }
299
400
  async fsAcquireExclusiveMutation(...args) {
300
- return this._call(this._fsOp('fsAcquireExclusiveMutation', args));
401
+ return this._call(this._fsMutation('fsAcquireExclusiveMutation', args));
301
402
  }
302
403
  async fsReleaseExclusiveMutation(...args) {
303
- return this._call(this._fsOp('fsReleaseExclusiveMutation', args));
404
+ return this._call(this._fsMutation('fsReleaseExclusiveMutation', args));
304
405
  }
305
406
  async fsClose(handleId) {
306
- return this._call(this._fsOp('fsClose', [handleId]));
407
+ return this._call(this._fsMutation('fsClose', [handleId]));
307
408
  }
308
409
  /**
309
410
  * Stateless ranged ops. Unlike fsOpen/fsRead/fsWrite they carry no
@@ -311,7 +412,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
311
412
  * hibernation and never rewrite whole files for partial updates.
312
413
  */
313
414
  async fsReadRange(path, offset, length) {
314
- return this._call(this._fsOp('fsReadRange', [path, offset, length]));
415
+ return this._call(this._fsRead('fsReadRange', [path, offset, length]));
315
416
  }
316
417
  /**
317
418
  * The same read with the session's content cache bypassed, for a boot spec's
@@ -320,7 +421,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
320
421
  * session's heap for the rest of its life.
321
422
  */
322
423
  async fsReadRangeUncached(path, offset, length) {
323
- return this._call(this._fsOp('fsReadRangeUncached', [path, offset, length]));
424
+ return this._call(this._fsRead('fsReadRangeUncached', [path, offset, length]));
324
425
  }
325
426
  /**
326
427
  * Read many ranges in ONE round trip — the read-side counterpart to
@@ -329,8 +430,11 @@ export class SupervisorRPC extends WorkerEntrypoint {
329
430
  * A file the caller knows is small is one entry; a large one is a run of
330
431
  * entries over the same path.
331
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
+ *
332
436
  * Entries come back positionally, each carrying exactly what the
333
- * equivalent fsReadRange would have returned. The batch is bounded by
437
+ * equivalent fsReadRange or lstat would have returned. The batch is bounded by
334
438
  * FS_READ_BATCH_PATH_LIMIT paths and FS_READ_BATCH_REQUEST_BYTES of
335
439
  * requested range, and the supervisor rejects anything past either — never
336
440
  * a short result, which a caller could mistake for a short file.
@@ -340,27 +444,33 @@ export class SupervisorRPC extends WorkerEntrypoint {
340
444
  // returns at most the range asked for. Counting it keeps the
341
445
  // supervisor's heap estimate honest for the duration of the await, the
342
446
  // same accounting writeBatch does for its inbound payload.
343
- const payloadBytes = requests.reduce((total, request) => total + request.length, 0);
447
+ const payloadBytes = requests.reduce((total, request) => total + fsReadBatchRequestBytes(request), 0);
344
448
  setLastRpcFrame('fsReadBatch', payloadBytes);
345
449
  rpcPayloadStart(payloadBytes);
346
450
  try {
347
- return await this._call(this._fsOp('fsReadBatch', [requests]));
451
+ return await this._call(this._fsRead('fsReadBatch', [requests]));
348
452
  }
349
453
  finally {
350
454
  rpcPayloadEnd(payloadBytes);
351
455
  }
352
456
  }
353
457
  async fsWriteRange(path, offset, bytes) {
354
- return this._call(this._fsOp('fsWriteRange', [path, offset, bytes]));
458
+ return this._call(this._fsMutation('fsWriteRange', [path, offset, bytes]));
355
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
+ */
356
466
  async fsAppend(path, moduleId, operationId, bytes) {
357
- 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() }));
358
468
  }
359
469
  async fsAppendAck(moduleId, operationId) {
360
- 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() }));
361
471
  }
362
472
  async fsTruncate(path, size) {
363
- return this._call(this._fsOp('fsTruncate', [path, size]));
473
+ return this._call(this._fsMutation('fsTruncate', [path, size]));
364
474
  }
365
475
  /**
366
476
  * Bulk-write all inodes + chunks in ONE transactionSync on the supervisor.
@@ -383,7 +493,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
383
493
  setLastRpcFrame('writeBatch', payloadBytes);
384
494
  rpcPayloadStart(payloadBytes);
385
495
  try {
386
- return await this._call(this._fsOp('writeBatch', [payload]));
496
+ return await this._call(this._fsMutation('writeBatch', [payload]));
387
497
  }
388
498
  finally {
389
499
  rpcPayloadEnd(payloadBytes);
@@ -554,8 +664,8 @@ export class SupervisorRPC extends WorkerEntrypoint {
554
664
  * has error state it couldn't stream in-band (rare; main path drains
555
665
  * via __pendingIO first).
556
666
  */
557
- async reportExit(code, tail) {
558
- 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() }));
559
669
  }
560
670
  // ── Prefetch ──────────────────────────────────────────────────────────
561
671
  async prefetch(cwd, entryCode) {
@@ -602,11 +712,18 @@ export class SupervisorRPC extends WorkerEntrypoint {
602
712
  async cpStdinEnd(childPid) {
603
713
  return this._call(this._op('cpStdinEnd', [childPid]));
604
714
  }
605
- async cpReadStdin(childPid, waitMs) {
606
- 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() }));
607
724
  }
608
- async cpReadOutput(childPid, fd, sinceSeq, waitMs) {
609
- 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() }));
610
727
  }
611
728
  async cpDrainOutput(childPid) {
612
729
  return this._call(this._op('cpDrainOutput', [childPid]));
@@ -614,8 +731,8 @@ export class SupervisorRPC extends WorkerEntrypoint {
614
731
  async cpKill(childPid, signal) {
615
732
  return this._call(this._op('cpKill', [childPid, signal]));
616
733
  }
617
- async cpWait(childPid, waitMs) {
618
- 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() }));
619
736
  }
620
737
  /**
621
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) {
@@ -1 +1 @@
1
- {"version":3,"file":"npm-bin-entrypoints.d.ts","sourceRoot":"","sources":["../../src/shell/npm-bin-entrypoints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,KAAK,EAAE,YAAY,EAA4B,MAAM,sBAAsB,CAAC;AAWnF,KAAK,MAAM,GAAG;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAiB5C,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACnD,CAAC;AAEF,KAAK,kBAAkB,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAezD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE;IACJ,GAAG,EAAE,eAAe,CAAC;IACrB,MAAM,IAAI,MAAM,CAAC;IACjB,SAAS,EAAE,wBAAwB,CAAC;IACpC,eAAe,IAAI,YAAY,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACxJ,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9D,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7F,GACA,IAAI,CAqHN;AAqFD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAEnF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EAAE,GACb,yBAAyB,CAS3B;AA+FD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAS/E"}
1
+ {"version":3,"file":"npm-bin-entrypoints.d.ts","sourceRoot":"","sources":["../../src/shell/npm-bin-entrypoints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,KAAK,EAAE,YAAY,EAA4B,MAAM,sBAAsB,CAAC;AAYnF,KAAK,MAAM,GAAG;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAiB5C,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACnD,CAAC;AAEF,KAAK,kBAAkB,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAezD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE;IACJ,GAAG,EAAE,eAAe,CAAC;IACrB,MAAM,IAAI,MAAM,CAAC;IACjB,SAAS,EAAE,wBAAwB,CAAC;IACpC,eAAe,IAAI,YAAY,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACxJ,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9D,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7F,GACA,IAAI,CAgJN;AAqFD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAEnF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EAAE,GACb,yBAAyB,CAS3B;AAiGD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAS/E"}