@nimbus-sh/worker 0.3.0 → 0.5.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 (136) hide show
  1. package/README.md +46 -43
  2. package/dist/_shared/preview-host.d.ts +14 -1
  3. package/dist/_shared/preview-host.d.ts.map +1 -1
  4. package/dist/_shared/preview-host.js +28 -3
  5. package/dist/_shared/session-router.d.ts +43 -0
  6. package/dist/_shared/session-router.d.ts.map +1 -1
  7. package/dist/_shared/session-router.js +59 -12
  8. package/dist/esbuild-wasm-bundle.generated.d.ts +1 -1
  9. package/dist/esbuild-wasm-bundle.generated.js +1 -1
  10. package/dist/facets/cirrus-real.js +1 -1
  11. package/dist/facets/durable-images.d.ts +50 -0
  12. package/dist/facets/durable-images.d.ts.map +1 -0
  13. package/dist/facets/durable-images.js +75 -0
  14. package/dist/facets/durable-slots.d.ts +45 -0
  15. package/dist/facets/durable-slots.d.ts.map +1 -0
  16. package/dist/facets/durable-slots.js +94 -0
  17. package/dist/facets/esbuild-transform.d.ts +10 -0
  18. package/dist/facets/esbuild-transform.d.ts.map +1 -0
  19. package/dist/facets/esbuild-transform.js +36 -0
  20. package/dist/facets/manager.d.ts +167 -12
  21. package/dist/facets/manager.d.ts.map +1 -1
  22. package/dist/facets/manager.js +575 -113
  23. package/dist/facets/opencode-staging.d.ts.map +1 -1
  24. package/dist/facets/opencode-staging.js +3 -4
  25. package/dist/facets/real-vite-hmr.js +1 -1
  26. package/dist/facets/vite-dev-server.d.ts +4 -4
  27. package/dist/facets/vite-dev-server.js +5 -5
  28. package/dist/git/commands.d.ts +22 -0
  29. package/dist/git/commands.d.ts.map +1 -1
  30. package/dist/git/commands.js +431 -424
  31. package/dist/git/network-facet.d.ts.map +1 -1
  32. package/dist/git/network-facet.js +6 -5
  33. package/dist/git-bundle.generated.d.ts +2 -2
  34. package/dist/git-bundle.generated.d.ts.map +1 -1
  35. package/dist/git-bundle.generated.js +3 -3
  36. package/dist/index.d.ts +2 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +11 -2
  39. package/dist/loaders/child-process/spawn-facet.js +1 -1
  40. package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
  41. package/dist/loaders/child-process/spawn-pool.js +2 -2
  42. package/dist/loaders/generated-workers.d.ts +2 -2
  43. package/dist/loaders/generated-workers.d.ts.map +1 -1
  44. package/dist/loaders/generated-workers.js +3 -3
  45. package/dist/loaders/npm-resolve-preamble.d.ts +3 -3
  46. package/dist/loaders/npm-resolve-preamble.js +3 -3
  47. package/dist/loaders/pre-bundle-preamble.d.ts +5 -5
  48. package/dist/loaders/pre-bundle-preamble.js +6 -6
  49. package/dist/node-shims-artifact.generated.js +3 -3
  50. package/dist/npm/install-batch-facet.d.ts +1 -1
  51. package/dist/npm/install-batch-facet.js +1 -1
  52. package/dist/npm/installer.d.ts +6 -6
  53. package/dist/npm/installer.d.ts.map +1 -1
  54. package/dist/npm/installer.js +28 -27
  55. package/dist/npm/pre-bundle-facet.d.ts +2 -2
  56. package/dist/npm/pre-bundle-facet.js +5 -5
  57. package/dist/npm/r2-cache.d.ts +1 -1
  58. package/dist/npm/resolve-facet.d.ts +1 -1
  59. package/dist/npm/resolve-facet.js +1 -1
  60. package/dist/npm/resolve-one-facet.d.ts +5 -5
  61. package/dist/npm/resolve-one-facet.js +5 -5
  62. package/dist/router/index.d.ts.map +1 -1
  63. package/dist/router/index.js +61 -4
  64. package/dist/router/public-directory-do.d.ts +17 -0
  65. package/dist/router/public-directory-do.d.ts.map +1 -0
  66. package/dist/router/public-directory-do.js +16 -0
  67. package/dist/router/public-directory.d.ts +75 -0
  68. package/dist/router/public-directory.d.ts.map +1 -0
  69. package/dist/router/public-directory.js +115 -0
  70. package/dist/router/remote-api.d.ts +9 -1
  71. package/dist/router/remote-api.d.ts.map +1 -1
  72. package/dist/router/remote-api.js +20 -3
  73. package/dist/runtime/bun-repl.d.ts +1 -1
  74. package/dist/runtime/bun-repl.js +3 -3
  75. package/dist/runtime/esbuild-wasm-bytes.js +1 -1
  76. package/dist/runtime/facet-loader-host.d.ts +2 -2
  77. package/dist/runtime/facet-loader-host.js +3 -3
  78. package/dist/runtime/node-repl.js +2 -2
  79. package/dist/runtime/node-shims-artifact.js +1 -1
  80. package/dist/runtime/node-shims.d.ts +4 -1
  81. package/dist/runtime/node-shims.d.ts.map +1 -1
  82. package/dist/runtime/node-shims.js +326 -7
  83. package/dist/runtime/opencode-artifact.js +1 -1
  84. package/dist/runtime/opentui-wasm-bytes.js +1 -1
  85. package/dist/runtime/python-repl.js +3 -3
  86. package/dist/runtime/ruby-repl.js +2 -2
  87. package/dist/runtime/sqlite-wasm-bytes.js +1 -1
  88. package/dist/session/agent.d.ts +11 -0
  89. package/dist/session/agent.d.ts.map +1 -1
  90. package/dist/session/diag.js +1 -1
  91. package/dist/session/helpers.js +1 -1
  92. package/dist/session/hibernation.d.ts +8 -8
  93. package/dist/session/hibernation.d.ts.map +1 -1
  94. package/dist/session/hibernation.js +15 -15
  95. package/dist/session/init-phases.d.ts +19 -26
  96. package/dist/session/init-phases.d.ts.map +1 -1
  97. package/dist/session/init-phases.js +20 -43
  98. package/dist/session/init.d.ts.map +1 -1
  99. package/dist/session/init.js +24 -19
  100. package/dist/session/keys.d.ts +8 -0
  101. package/dist/session/keys.d.ts.map +1 -1
  102. package/dist/session/keys.js +12 -3
  103. package/dist/session/nimbus-session.d.ts +33 -19
  104. package/dist/session/nimbus-session.d.ts.map +1 -1
  105. package/dist/session/nimbus-session.js +78 -37
  106. package/dist/session/port-capability.d.ts +95 -8
  107. package/dist/session/port-capability.d.ts.map +1 -1
  108. package/dist/session/port-capability.js +165 -8
  109. package/dist/session/programmatic.d.ts +50 -4
  110. package/dist/session/programmatic.d.ts.map +1 -1
  111. package/dist/session/programmatic.js +112 -15
  112. package/dist/session/routes.d.ts +17 -0
  113. package/dist/session/routes.d.ts.map +1 -1
  114. package/dist/session/routes.js +111 -43
  115. package/dist/session/rpc.d.ts +7 -7
  116. package/dist/session/rpc.d.ts.map +1 -1
  117. package/dist/session/rpc.js +34 -23
  118. package/dist/session/shell-socket.d.ts +76 -0
  119. package/dist/session/shell-socket.d.ts.map +1 -0
  120. package/dist/session/shell-socket.js +172 -0
  121. package/dist/session/start-real-vite.js +2 -2
  122. package/dist/session/supervisor-op.d.ts +5 -0
  123. package/dist/session/supervisor-op.d.ts.map +1 -0
  124. package/dist/session/supervisor-op.js +74 -0
  125. package/dist/session/supervisor-rpc.d.ts +6 -10
  126. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  127. package/dist/session/supervisor-rpc.js +98 -79
  128. package/dist/session/ws.d.ts +1 -3
  129. package/dist/session/ws.d.ts.map +1 -1
  130. package/dist/session/ws.js +23 -7
  131. package/package.json +4 -3
  132. package/public/_assets/runtime/{node-shims-fea906843f6d594e.js → node-shims-9e1380d9ee430fcc.js} +320 -5
  133. package/public/s/index.html +101 -5
  134. package/scripts/bundle-esbuild-wasm.mjs +2 -2
  135. package/scripts/bundle-facet-workers.mjs +5 -7
  136. package/scripts/bundle-sqlite-wasm.mjs +1 -1
@@ -22,10 +22,10 @@
22
22
  * reason dispatcher with this session's handlers registered.
23
23
  * - flushOnClose(host) — synchronous flush on ws close.
24
24
  *
25
- * The alarm multiplexer itself (reason map, scheduleAlarm, the per-instance
26
- * chain) and maybeBumpIsolateGen are fabric machinery —
27
- * `@nimbus-sh/fabric/alarms.js`; this module registers the session's reasons
28
- * ('w9-flush' | 'log-janitor' | 'resident-launch') on top of it.
25
+ * The timer multiplexer itself (reason map, schedule, the per-instance
26
+ * chain) is fabric machinery — `@nimbus-sh/fabric/timers.js`; this module
27
+ * registers the session's reasons ('w9-flush' | 'log-janitor' |
28
+ * 'resident-launch') on top of it.
29
29
  *
30
30
  * **`ctx` taken as a separate arg from `host`** because the parent
31
31
  * `CloudflareDurableObject` class declares `ctx` as `protected`, which
@@ -39,7 +39,7 @@
39
39
  * wireProcessLogPersist again.
40
40
  */
41
41
  import { configureWsHibernation } from '@nimbus-sh/fabric/ws-hibernation-config.js';
42
- import { dispatchAlarm as dispatchAlarmReasons, scheduleAlarm, } from '@nimbus-sh/fabric/alarms.js';
42
+ import { timers } from '@nimbus-sh/fabric/timers.js';
43
43
  import { SESSION_DESTROYED_KEY, W9_FLUSH_DEBOUNCE_MS } from './keys.js';
44
44
  /**
45
45
  * Run at DO ctor time. Returns the result for the class to assign to
@@ -216,11 +216,11 @@ export function ensureLogJanitor(host, ctx) {
216
216
  if (host._w1SessionDestroyed)
217
217
  return;
218
218
  // Optimistic flag (dedupes same-tick appends), CONFIRMED by the schedule
219
- // outcome: scheduleAlarm swallows storage errors, and a failure with the
219
+ // outcome: timers.schedule swallows storage errors, and a failure with the
220
220
  // flag left set would mean no alarm AND nothing ever re-arming until the
221
221
  // instance recycles.
222
222
  host._w1JanitorArmed = true;
223
- void scheduleAlarm(host, ctx, 'log-janitor', Date.now() + 60_000).then((ok) => {
223
+ void timers(host, ctx).schedule('log-janitor', Date.now() + 60_000).then((ok) => {
224
224
  if (!ok)
225
225
  host._w1JanitorArmed = false;
226
226
  });
@@ -249,7 +249,7 @@ export function ensureHibSchema(host, ctx) {
249
249
  /**
250
250
  * W9: ensure the alarm is set for the next flush window. Cheap to
251
251
  * call repeatedly — we only schedule the in-isolate flush timer if
252
- * it isn't already set. The persistent alarm goes through scheduleAlarm
252
+ * it isn't already set. The persistent alarm goes through timers.schedule
253
253
  * so it coordinates with W1's log-janitor sweep.
254
254
  */
255
255
  export function scheduleHibFlush(host, ctx) {
@@ -271,10 +271,10 @@ export function scheduleHibFlush(host, ctx) {
271
271
  }, W9_FLUSH_DEBOUNCE_MS);
272
272
  // Persistent alarm: fires post-hibernation if the in-isolate timer
273
273
  // didn't get a chance to run (DO evicted under memory pressure
274
- // before the 250ms debounce expired). Coordinated via scheduleAlarm
274
+ // before the 250ms debounce expired). Coordinated via timers.schedule
275
275
  // so W1's log-janitor doesn't clobber it (or vice-versa).
276
- // Fire-and-forget — scheduleAlarm is fail-soft.
277
- void scheduleAlarm(host, ctx, 'w9-flush', Date.now() + W9_FLUSH_DEBOUNCE_MS * 4);
276
+ // Fire-and-forget — timers.schedule is fail-soft.
277
+ void timers(host, ctx).schedule('w9-flush', Date.now() + W9_FLUSH_DEBOUNCE_MS * 4);
278
278
  }
279
279
  /**
280
280
  * W1: this session's alarm() handler body — the fabric's multi-reason
@@ -288,8 +288,8 @@ export function scheduleHibFlush(host, ctx) {
288
288
  * caller (typically `(pid) => !host.processes.get(pid)`). Decoupled
289
289
  * so HibHost doesn't need to import ProcessTable.
290
290
  */
291
- export function dispatchAlarm(host, ctx, janitorOrphanCheck, pumpResidentLaunches) {
292
- return dispatchAlarmReasons(host, ctx, {
291
+ export function dispatchAlarm(host, ctx, janitorOrphanCheck, pumpResidentLaunches, alarmInfo) {
292
+ return timers(host, ctx).dispatch({
293
293
  'w9-flush': () => {
294
294
  host.processes.flushLogs();
295
295
  },
@@ -322,14 +322,14 @@ export function dispatchAlarm(host, ctx, janitorOrphanCheck, pumpResidentLaunche
322
322
  // Legacy path: pre-W1 deploys had no map. dispatchAlarm was called
323
323
  // with no map and unconditionally ran the log flush. Preserve
324
324
  // that on a missing map (one-time post-deploy, then the map is
325
- // populated by the next scheduleHibFlush / scheduleAlarm call).
325
+ // populated by the next scheduleHibFlush / timers.schedule call).
326
326
  try {
327
327
  host.processes.flushLogs();
328
328
  }
329
329
  catch (e) {
330
330
  console.warn('[nimbus/W9] legacy flush threw:', e?.message);
331
331
  }
332
- });
332
+ }, alarmInfo);
333
333
  }
334
334
  /**
335
335
  * W9: synchronous flush of the process-log ring on session close.
@@ -32,7 +32,8 @@
32
32
  * preserved — old probes still see it. New phase entries are
33
33
  * additional ring entries, not replacements.
34
34
  */
35
- import { type SessionState } from '@nimbus-sh/core/observability/oom-discriminator.js';
35
+ import { type SessionState } from '@nimbus-sh/platform/oom-discriminator.js';
36
+ import { type AutoResponseHost } from './shell-socket.js';
36
37
  /**
37
38
  * Set the current phase + record a transition. Fail-soft on the
38
39
  * observability call; the phase update on `self` is direct and
@@ -40,39 +41,32 @@ import { type SessionState } from '@nimbus-sh/core/observability/oom-discriminat
40
41
  */
41
42
  export declare function setPhase(self: {
42
43
  _b4Phase: SessionState | null;
43
- _isolateGen?: number;
44
+ ctx?: unknown;
44
45
  }, toState: SessionState, trigger: string): void;
46
+ type WsUpgradeDecision = 'warm-join' | 'conflict' | 'cold';
45
47
  /**
46
- * [B'.5] Identify the original phase-based warm-rejoin case. The /ws
47
- * upgrade classifier below also recognizes headless sessions whose
48
- * lifecycle phase does not describe a real terminal attachment.
48
+ * Decide what a /ws upgrade should do with the session it lands on.
49
49
  *
50
- * Conditions for warm rejoin:
51
- * 1. Phase = 'drained' (a wsClose / wsError fired since last init).
52
- * 2. Kernel + Shell + Terminal are still alive in-memory (the
53
- * [B'.5] change to wsClose stopped nulling them).
54
- * 3. Same isolate (no DO eviction since the close).
50
+ * - cold: no session to join. Either nothing was ever built, or a
51
+ * previous teardown left the object half-built. A Shell
52
+ * without its Kernel or its Terminal serves nobody, and
53
+ * nothing else rebuilds one, so the upgrade does.
54
+ * - conflict: another browser terminal holds this session. The caller
55
+ * answers 409, which is what stops two tabs from driving
56
+ * one shell. See `shell-socket.ts` for what proves that a
57
+ * socket still has a peer on it.
58
+ * - warm-join: the session is built and unattended. Skip the rebuild
59
+ * and hand the live Shell to the incoming socket.
55
60
  *
56
- */
57
- export declare function isWarmRejoin(self: {
58
- _b4Phase: SessionState | null;
59
- shell: any;
60
- terminal: any;
61
- kernel: any;
62
- }): boolean;
63
- type WsUpgradeDecision = 'warm-join' | 'conflict' | 'cold';
64
- /**
65
- * Classify a shell WebSocket upgrade from the actual attachment state.
66
- * A non-null Shell can belong to a headless programmatic boot, so only an
67
- * open socket tagged as `shell` proves that another browser terminal is
68
- * currently attached.
61
+ * The caller classifies BEFORE it accepts the incoming socket, so the
62
+ * upgrade cannot count itself as the incumbent.
69
63
  */
70
64
  export declare function classifyWsUpgrade(self: {
71
- _b4Phase: SessionState | null;
72
65
  shell: unknown;
73
66
  terminal: unknown;
74
67
  kernel: unknown;
75
- }, sockets: readonly WebSocket[]): WsUpgradeDecision;
68
+ ctx?: AutoResponseHost;
69
+ }, sockets: readonly WebSocket[], now?: number): WsUpgradeDecision;
76
70
  /**
77
71
  * [B'.5] Run the warm-rejoin path. Skips Phase B (kernel + shell are
78
72
  * already built and alive in-memory). Phase R loads any state that
@@ -93,7 +87,6 @@ export declare function joinExistingSession(self: {
93
87
  write(s: string): void;
94
88
  };
95
89
  _b4Phase: SessionState | null;
96
- _isolateGen?: number;
97
90
  _b4WarmJoinCount: number;
98
91
  }, ws: WebSocket, appendScrollback: (ctx: any, data: string, atMs: number) => void, loadScrollback: (ctx: any) => string): void;
99
92
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"init-phases.d.ts","sourceRoot":"","sources":["../../src/session/init-phases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oDAAoD,CAAC;AAE5G;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAC7D,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,GACd,IAAI,CAcN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;CACxC,GAAG,OAAO,CAKV;AAED,KAAK,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,iBAAiB,CAmBnD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE;IACJ,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE;QAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpG,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,EACD,EAAE,EAAE,SAAS,EACb,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,EAChE,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,GACnC,IAAI,CAsCN"}
1
+ {"version":3,"file":"init-phases.d.ts","sourceRoot":"","sources":["../../src/session/init-phases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAElG,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE7E;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,EACtD,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,GACd,IAAI,CAcN;AAED,KAAK,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,gBAAgB,CAAC;CACxB,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,EAAE,GAAG,GAAE,MAAmB,GAAG,iBAAiB,CAG7E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE;IACJ,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE;QAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpG,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,EACD,EAAE,EAAE,SAAS,EACb,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,EAChE,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,GACnC,IAAI,CAsCN"}
@@ -32,7 +32,9 @@
32
32
  * preserved — old probes still see it. New phase entries are
33
33
  * additional ring entries, not replacements.
34
34
  */
35
- import { recordRecoveryEvent } from '@nimbus-sh/core/observability/oom-discriminator.js';
35
+ import { recordRecoveryEvent } from '@nimbus-sh/platform/oom-discriminator.js';
36
+ import { generation } from '@nimbus-sh/fabric/generation.js';
37
+ import { hasLiveShellOwner } from './shell-socket.js';
36
38
  /**
37
39
  * Set the current phase + record a transition. Fail-soft on the
38
40
  * observability call; the phase update on `self` is direct and
@@ -47,7 +49,7 @@ export function setPhase(self, toState, trigger) {
47
49
  fromState,
48
50
  toState,
49
51
  trigger,
50
- isolateGen: self._isolateGen ?? 0,
52
+ isolateGen: generation(self.ctx),
51
53
  dataLoss: false,
52
54
  snapshotKeysRehydrated: 0,
53
55
  });
@@ -55,51 +57,26 @@ export function setPhase(self, toState, trigger) {
55
57
  catch { /* observability is non-critical */ }
56
58
  }
57
59
  /**
58
- * [B'.5] Identify the original phase-based warm-rejoin case. The /ws
59
- * upgrade classifier below also recognizes headless sessions whose
60
- * lifecycle phase does not describe a real terminal attachment.
60
+ * Decide what a /ws upgrade should do with the session it lands on.
61
61
  *
62
- * Conditions for warm rejoin:
63
- * 1. Phase = 'drained' (a wsClose / wsError fired since last init).
64
- * 2. Kernel + Shell + Terminal are still alive in-memory (the
65
- * [B'.5] change to wsClose stopped nulling them).
66
- * 3. Same isolate (no DO eviction since the close).
62
+ * - cold: no session to join. Either nothing was ever built, or a
63
+ * previous teardown left the object half-built. A Shell
64
+ * without its Kernel or its Terminal serves nobody, and
65
+ * nothing else rebuilds one, so the upgrade does.
66
+ * - conflict: another browser terminal holds this session. The caller
67
+ * answers 409, which is what stops two tabs from driving
68
+ * one shell. See `shell-socket.ts` for what proves that a
69
+ * socket still has a peer on it.
70
+ * - warm-join: the session is built and unattended. Skip the rebuild
71
+ * and hand the live Shell to the incoming socket.
67
72
  *
73
+ * The caller classifies BEFORE it accepts the incoming socket, so the
74
+ * upgrade cannot count itself as the incumbent.
68
75
  */
69
- export function isWarmRejoin(self) {
70
- return self._b4Phase === 'drained'
71
- && self.shell != null
72
- && self.terminal != null
73
- && self.kernel != null;
74
- }
75
- /**
76
- * Classify a shell WebSocket upgrade from the actual attachment state.
77
- * A non-null Shell can belong to a headless programmatic boot, so only an
78
- * open socket tagged as `shell` proves that another browser terminal is
79
- * currently attached.
80
- */
81
- export function classifyWsUpgrade(self, sockets) {
82
- if (isWarmRejoin(self))
83
- return 'warm-join';
84
- if (self.shell == null)
76
+ export function classifyWsUpgrade(self, sockets, now = Date.now()) {
77
+ if (self.shell == null || self.terminal == null || self.kernel == null)
85
78
  return 'cold';
86
- if (self.terminal == null || self.kernel == null)
87
- return 'conflict';
88
- const hasOpenShellSocket = sockets.some((socket) => {
89
- if (socket.readyState !== WebSocket.OPEN)
90
- return false;
91
- try {
92
- const attachment = socket.deserializeAttachment();
93
- return typeof attachment === 'object'
94
- && attachment !== null
95
- && 'kind' in attachment
96
- && attachment.kind === 'shell';
97
- }
98
- catch {
99
- return false;
100
- }
101
- });
102
- return hasOpenShellSocket ? 'conflict' : 'warm-join';
79
+ return hasLiveShellOwner(self.ctx, sockets, now) ? 'conflict' : 'warm-join';
103
80
  }
104
81
  /**
105
82
  * [B'.5] Run the warm-rejoin path. Skips Phase B (kernel + shell are
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/session/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA8EH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;;;GAQG;AACH,KAAK,QAAQ,GAAG,eAAe,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;CAAE,CAAC;AAa3E,wBAAsB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAosF9E"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/session/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA8EH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;;;GAQG;AACH,KAAK,QAAQ,GAAG,eAAe,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;CAAE,CAAC;AAa3E,wBAAsB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAysF9E"}
@@ -58,7 +58,6 @@ import { installNpmBinFallbackResolver } from '../shell/npm-bin-entrypoints.js';
58
58
  import { parseNpmInstallInvocation } from '../npm/install-args.js';
59
59
  import { npmLogEnabled } from '../npm/npm-log.js';
60
60
  import { materializeNpmBinShims } from '../npm/bin-links.js';
61
- import { registerGitCommands } from '../git/commands.js';
62
61
  import { makeNimbusVerbHandler, createRuntimeCommandHintResolver, } from '../runtime/package-manager.js';
63
62
  import { listInstalledRuntimes, rehydrateInstalledRuntimes, registerRunnerFactory, } from '@nimbus-sh/core/runtime/installed-runtimes.js';
64
63
  // Runtime factories (clang/python/ruby/bash/wasm) are imported lazily at
@@ -68,10 +67,11 @@ import { listInstalledRuntimes, rehydrateInstalledRuntimes, registerRunnerFactor
68
67
  // one-time Worker Startup Time paid on every fresh-isolate cold run.
69
68
  import { hasSeededProject, SEED_PROJECT_DIR } from '@nimbus-sh/core/vfs/seed-project.js';
70
69
  import { notifyTerminalEvent } from '../runtime/process-logs-api.js';
70
+ import { generation } from '@nimbus-sh/fabric/generation.js';
71
71
  import { stripAnsi } from '@nimbus-sh/core/runtime/process-logs.js';
72
72
  import { DEFAULT_MOUNT_POINTS, NODE_VERSION, } from '@nimbus-sh/core/constants.js';
73
73
  import { ensureSessionStateSchema, loadShellState, stampHydratedAt, countSessionStateKeys, loadKernelMounts, persistKernelMounts, appendScrollback, loadScrollback, } from './state-store.js';
74
- import { recordRecoveryEvent } from '@nimbus-sh/core/observability/oom-discriminator.js';
74
+ import { recordRecoveryEvent } from '@nimbus-sh/platform/oom-discriminator.js';
75
75
  import { sessionAiEnv } from './ai.js';
76
76
  import { routeSessionLoopback } from './loopback.js';
77
77
  import { setPhase } from './init-phases.js';
@@ -166,7 +166,7 @@ export async function initSession(self, ws) {
166
166
  + 'running was lost with the old one — re-run it if it had not finished.\x1b[0m\r\n');
167
167
  }
168
168
  // A reset that killed a resident launch left its journal row behind
169
- // (fabric's ResidentLaunchJournal.recoverInterrupted). The alarm the dying
169
+ // (fabric's FencedWork.recoverInterrupted). The alarm the dying
170
170
  // instance was using for launch turns is NOT a trigger recovery can rely
171
171
  // on — measured live, a launch killed early in its first chunks rolls the
172
172
  // alarm-map put back with the rest of the dying turn, so the replacement
@@ -453,8 +453,13 @@ export async function initSession(self, ws) {
453
453
  }));
454
454
  // ── Git integration (isomorphic-git) ──
455
455
  // ctx + env are passed for clone/fetch/pull which run in a facet to avoid
456
- // exhausting the supervisor DO's CPU budget on large repos.
457
- registerGitCommands(registry, sqliteFs, self.ctx, self.env);
456
+ // exhausting the supervisor DO's CPU budget on large repos. The command
457
+ // module (+ its ~106 KB network-facet dependency) is loaded lazily on the
458
+ // first `git` invocation so it stays out of the cold script-eval graph.
459
+ registry.register('git', async (ctx) => {
460
+ const { runGitCommand } = await import('../git/commands.js');
461
+ return runGitCommand(ctx, sqliteFs, self.ctx, self.env);
462
+ });
458
463
  // ── runtime package manager: `nimbus install` package manager + runner registry.
459
464
  //
460
465
  // 1. Register the clang-runner factory FIRST so the rehydration step
@@ -481,7 +486,7 @@ export async function initSession(self, ws) {
481
486
  registerRunnerFactory('cpython-runner', (manifest, installRoot, binName, binKind) => async function pythonReplOrOneShot(ctx) {
482
487
  const argv = ctx.args || [];
483
488
  // No args at all → REPL session. Hand off to runPythonRepl
484
- // which builds its own LoaderPool (separate from the
489
+ // which builds its own IsolatePool (separate from the
485
490
  // one-shot dispatch's pool) and drives a ReplSession.
486
491
  if (argv.length === 0 && self.terminal) {
487
492
  const { runPythonRepl } = await import('../runtime/python-repl.js');
@@ -688,7 +693,7 @@ export async function initSession(self, ws) {
688
693
  getEsbuild: () => {
689
694
  if (!self.esbuildService) {
690
695
  self.ensureSqliteFs();
691
- self.esbuildService = new EsbuildService(self.sqliteFs);
696
+ self.esbuildService = new EsbuildService(kernelFs);
692
697
  }
693
698
  return self.esbuildService;
694
699
  },
@@ -837,7 +842,7 @@ export async function initSession(self, ws) {
837
842
  getEsbuild: () => {
838
843
  if (!self.esbuildService) {
839
844
  self.ensureSqliteFs();
840
- self.esbuildService = new EsbuildService(self.sqliteFs);
845
+ self.esbuildService = new EsbuildService(kernelFs);
841
846
  }
842
847
  return self.esbuildService;
843
848
  },
@@ -885,7 +890,7 @@ export async function initSession(self, ws) {
885
890
  getEsbuild: () => {
886
891
  if (!self.esbuildService) {
887
892
  self.ensureSqliteFs();
888
- self.esbuildService = new EsbuildService(self.sqliteFs);
893
+ self.esbuildService = new EsbuildService(kernelFs);
889
894
  }
890
895
  return self.esbuildService;
891
896
  },
@@ -955,7 +960,7 @@ export async function initSession(self, ws) {
955
960
  // Lazy-init esbuild service
956
961
  if (!self.esbuildService) {
957
962
  self.ensureSqliteFs();
958
- self.esbuildService = new EsbuildService(self.sqliteFs);
963
+ self.esbuildService = new EsbuildService(kernelFs);
959
964
  }
960
965
  // Parse flags
961
966
  const flags = {};
@@ -1090,7 +1095,7 @@ export async function initSession(self, ws) {
1090
1095
  // Transform TS to JS
1091
1096
  if (cfgName.endsWith('.ts')) {
1092
1097
  if (!self.esbuildService)
1093
- self.esbuildService = new EsbuildService(self.sqliteFs);
1098
+ self.esbuildService = new EsbuildService(kernelFs);
1094
1099
  const t = await self.esbuildService.transform(cfgCode, { loader: 'ts', format: 'esm' });
1095
1100
  cfgCode = t.code;
1096
1101
  }
@@ -1105,7 +1110,7 @@ export async function initSession(self, ws) {
1105
1110
  // ── vite build ──
1106
1111
  if (args[0] === 'build') {
1107
1112
  if (!self.esbuildService)
1108
- self.esbuildService = new EsbuildService(self.sqliteFs);
1113
+ self.esbuildService = new EsbuildService(kernelFs);
1109
1114
  const htmlPath = cwd + '/index.html';
1110
1115
  let entryPoint = cwd + '/src/main.tsx';
1111
1116
  let origHtml = '';
@@ -1221,7 +1226,7 @@ export async function initSession(self, ws) {
1221
1226
  }
1222
1227
  // Start vite on the dist directory
1223
1228
  if (!self.esbuildService)
1224
- self.esbuildService = new EsbuildService(self.sqliteFs);
1229
+ self.esbuildService = new EsbuildService(kernelFs);
1225
1230
  if (self.viteDevServer?.isRunning)
1226
1231
  self.viteDevServer.stop();
1227
1232
  const previewBasePath = self.viteBasePath;
@@ -1394,7 +1399,7 @@ export async function initSession(self, ws) {
1394
1399
  }
1395
1400
  }
1396
1401
  if (!self.esbuildService)
1397
- self.esbuildService = new EsbuildService(self.sqliteFs);
1402
+ self.esbuildService = new EsbuildService(kernelFs);
1398
1403
  const previewBasePath = self.viteBasePath;
1399
1404
  const viteDefine = viteConfig.define;
1400
1405
  // Vite dev servers are represented as long-running process-table
@@ -1562,7 +1567,7 @@ export async function initSession(self, ws) {
1562
1567
  // Lazy-init esbuild
1563
1568
  if (!self.esbuildService) {
1564
1569
  self.ensureSqliteFs();
1565
- self.esbuildService = new EsbuildService(self.sqliteFs);
1570
+ self.esbuildService = new EsbuildService(kernelFs);
1566
1571
  }
1567
1572
  // Parse --root flag; default to the shell cwd so `npm run dev` from
1568
1573
  // a project directory picks up that project's wrangler.jsonc.
@@ -1665,7 +1670,7 @@ export async function initSession(self, ws) {
1665
1670
  kernelFs.writeFile(pkgJsonPath, '{"name":"project","version":"1.0.0","dependencies":{}}\n');
1666
1671
  }
1667
1672
  ctx.stdout.write('\x1b[36mNimbus npm v2 (batched writes)\x1b[0m\n');
1668
- self.ensureNpmInstaller((msg) => {
1673
+ await self.ensureNpmInstaller((msg) => {
1669
1674
  ctx.stdout.write('[npm] ' + msg + '\n');
1670
1675
  });
1671
1676
  const result = await self.npmInstaller.install(cwd, { packages, pid: ctx.pid });
@@ -2163,7 +2168,7 @@ export async function initSession(self, ws) {
2163
2168
  ? `${explicitPkgs.length} packages`
2164
2169
  : 'dependencies from package.json';
2165
2170
  ctx.stdout.write(`\x1b[36mInstalling ${pkgLabel} (npm v2 — batched writes)...\x1b[0m\n`);
2166
- self.ensureNpmInstaller((msg) => {
2171
+ await self.ensureNpmInstaller((msg) => {
2167
2172
  ctx.stdout.write('[npm] ' + msg + '\n');
2168
2173
  });
2169
2174
  // `--loglevel` selects npm's own log protocol on stderr, where npm
@@ -2332,7 +2337,7 @@ export async function initSession(self, ws) {
2332
2337
  }
2333
2338
  // Nimbus-native npx install + run path. Routes package installation
2334
2339
  // through NpmInstaller's full-packument resolver.
2335
- self.ensureNpmInstaller((msg) => ctx.stdout.write('[npm] ' + msg + '\n'));
2340
+ await self.ensureNpmInstaller((msg) => ctx.stdout.write('[npm] ' + msg + '\n'));
2336
2341
  self.ensureSqliteFs();
2337
2342
  const installer = self.npmInstaller;
2338
2343
  const resolveResult = await resolveNpxBinary(installer, self.sqliteFs.as(requireVfsCred('cred' in ctx ? ctx.cred : undefined, 'npx')), ctx.cwd || '/home/user', npxArgs, (msg) => ctx.stdout.write(msg + '\n'), ctx.pid);
@@ -2705,7 +2710,7 @@ export async function initSession(self, ws) {
2705
2710
  fromState: fromState,
2706
2711
  toState: 'hydrated',
2707
2712
  trigger: 'init-session',
2708
- isolateGen: self._isolateGen,
2713
+ isolateGen: generation(self.ctx),
2709
2714
  dataLoss: false,
2710
2715
  snapshotKeysRehydrated: snapshotKeys,
2711
2716
  });
@@ -51,6 +51,14 @@ export declare const ATTACH_BOOTSTRAP_JTI_KEY_PREFIX = "attach-bootstrap-jti:";
51
51
  * these rows.
52
52
  */
53
53
  export declare const PORT_CAPABILITY_KEY_PREFIX = "nimbus_preview_capability:";
54
+ /**
55
+ * Prefix for a durable application's facet-slot row, keyed by owner.
56
+ * `durable-slot:next` and `durable-slot:free` under the same prefix are the
57
+ * allocator's counter and free list; `durable-slot:<owner>` pins the owner to
58
+ * its `app-slot-<n>` facet name for the application's whole life. Owned by
59
+ * `facets/durable-slots.ts`, the only module that reads or writes these rows.
60
+ */
61
+ export declare const DURABLE_SLOT_KEY_PREFIX = "durable-slot:";
54
62
  /**
55
63
  * Prefix for a named programmatic shell's durable cwd+env, keyed by shell id.
56
64
  * One row per id the embedder has actually used; bounded by the embedder's own
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/session/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH;2EAC2E;AAC3E,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;yDAEyD;AACzD,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD;0DAC0D;AAC1D,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;uBAEuB;AACvB,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;;4EAG4E;AAC5E,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAEvE;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AAEvE;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/session/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH;2EAC2E;AAC3E,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;yDAEyD;AACzD,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD;0DAC0D;AAC1D,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;uBAEuB;AACvB,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;;4EAG4E;AAC5E,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAEvE;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AAEvE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC"}
@@ -17,9 +17,10 @@
17
17
  * These keys persist DO state across deploys. Renaming any key is a
18
18
  * storage migration — never do it without an explicit migration plan.
19
19
  */
20
- // The isolate-generation counter key (ISOLATE_GEN_KEY) and the multi-reason
21
- // alarm map key (ALARM_REASONS_KEY) live with their machinery in
22
- // @nimbus-sh/fabric/alarms.js; their persisted VALUES are unchanged.
20
+ // The isolate-generation counter key (GENERATION_KEY) and the multi-reason
21
+ // timer map key (TIMER_REASONS_KEY) live with their machinery in
22
+ // @nimbus-sh/fabric/generation.js and @nimbus-sh/fabric/timers.js; their
23
+ // persisted VALUES are unchanged.
23
24
  /** W9: debounce window in ms before flushing pending process-log writes
24
25
  * to SQL. Hot path (every append schedules a flush via this debounce). */
25
26
  export const W9_FLUSH_DEBOUNCE_MS = 250;
@@ -54,6 +55,14 @@ export const ATTACH_BOOTSTRAP_JTI_KEY_PREFIX = 'attach-bootstrap-jti:';
54
55
  * these rows.
55
56
  */
56
57
  export const PORT_CAPABILITY_KEY_PREFIX = 'nimbus_preview_capability:';
58
+ /**
59
+ * Prefix for a durable application's facet-slot row, keyed by owner.
60
+ * `durable-slot:next` and `durable-slot:free` under the same prefix are the
61
+ * allocator's counter and free list; `durable-slot:<owner>` pins the owner to
62
+ * its `app-slot-<n>` facet name for the application's whole life. Owned by
63
+ * `facets/durable-slots.ts`, the only module that reads or writes these rows.
64
+ */
65
+ export const DURABLE_SLOT_KEY_PREFIX = 'durable-slot:';
57
66
  /**
58
67
  * Prefix for a named programmatic shell's durable cwd+env, keyed by shell id.
59
68
  * One row per id the embedder has actually used; bounded by the embedder's own
@@ -15,13 +15,14 @@ import { SqliteRuntimeFsBridge } from '@nimbus-sh/core/runtime/sqlite-runtime-fs
15
15
  import { type VfsAcquireResult, type VfsListPage } from '@nimbus-sh/core/runtime/os-contracts.js';
16
16
  import type { WsHibernationConfigResult } from './hibernation.js';
17
17
  import { PortRegistry } from '@nimbus-sh/core/runtime/port-registry.js';
18
- import { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
19
18
  import { ViteDevServer } from '../facets/vite-dev-server.js';
20
19
  import { CirrusReal } from '../facets/cirrus-real.js';
20
+ import { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
21
21
  import { NimbusWrangler } from '../wrangler/nimbus-wrangler.js';
22
- import { NpmInstaller } from '../npm/installer.js';
22
+ import type { NpmInstaller } from '../npm/installer.js';
23
23
  import { type TryEnableReplicasResult as _W12EnableResult } from '../replica/routing.js';
24
24
  import * as _rpc from './rpc.js';
25
+ import type { SupervisorOpEnvelope } from '@nimbus-sh/core/workspace/supervisor-op.js';
25
26
  import type { HostedHttpRequest, HostedHttpResponse } from '@nimbus-sh/fabric/process-host.js';
26
27
  import { WebSocketRelay } from './ws-relay.js';
27
28
  import * as _programmatic from './programmatic.js';
@@ -120,17 +121,8 @@ export declare class NimbusSession extends CloudflareDurableObject {
120
121
  * catch-all).
121
122
  */
122
123
  _w9WsConfig: WsHibernationConfigResult | null;
123
- /**
124
- * Monotonic isolate generation counter. Each fresh isolate (cold start
125
- * or post-hibernation wake) increments this and persists to storage.
126
- * Lets `/api/_diag/memory` confirm whether a wake actually happened
127
- * between two probe calls.
128
- */
129
- _isolateGen: number;
130
- /** True once we've persisted the bumped gen counter to storage. */
131
- _isolateGenPersisted: boolean;
132
- /** W1: serializes every alarm-map read-modify-write (fabric alarms.ts). */
133
- _alarmChain?: Promise<unknown>;
124
+ /** W1: serializes every timer-map read-modify-write (fabric timers.ts). */
125
+ _timerChain?: Promise<unknown>;
134
126
  /** SQL DDL — idempotent; run on first fetch. */
135
127
  _w9SchemaInit: boolean;
136
128
  /** Have we wired the persist adapter into ProcessLogStore yet? */
@@ -207,7 +199,7 @@ export declare class NimbusSession extends CloudflareDurableObject {
207
199
  * 'log-janitor' (dropOlderThan sweep). The janitor body needs an
208
200
  * orphan-pid predicate so we close over the process supervisor here.
209
201
  */
210
- alarm(): Promise<void>;
202
+ alarm(alarmInfo?: AlarmInvocationInfo): Promise<void>;
211
203
  /**
212
204
  * Grant the fresh turn a suspended launch asked for.
213
205
  *
@@ -234,8 +226,6 @@ export declare class NimbusSession extends CloudflareDurableObject {
234
226
  * budget rather than the one the launch has already been spending.
235
227
  */
236
228
  private _scheduleLaunchTurn;
237
- /** W9: increment + persist isolate-gen counter once per fresh isolate. */
238
- _w9MaybeBumpIsolateGen(): Promise<void>;
239
229
  /**
240
230
  * Convenience: the full URL prefix for the Vite dev server inside this
241
231
  * session (e.g. `/s/nimble-otter-4271/preview`). Falls back to the
@@ -260,6 +250,7 @@ export declare class NimbusSession extends CloudflareDurableObject {
260
250
  * get/put, so two concurrent exchanges cannot both observe "absent".
261
251
  */
262
252
  _rpcConsumeAttachBootstrap(jti: string): Promise<boolean>;
253
+ supervisorOp(envelope: SupervisorOpEnvelope): Promise<unknown>;
263
254
  _rpcReadFile(path: string, pid?: number): Promise<string | null>;
264
255
  _rpcReadFileBytes(path: string, pid?: number): Promise<Uint8Array | null>;
265
256
  _rpcInnerDoFetch(req: any): Promise<any>;
@@ -448,12 +439,29 @@ export declare class NimbusSession extends CloudflareDurableObject {
448
439
  exit: import("@nimbus-sh/core/runtime/process-logs.js").ProcessExitInfo | null;
449
440
  }>;
450
441
  _rpcListPorts(): Promise<_programmatic.SerializedPort[]>;
451
- _rpcExposePort(port: number): Promise<{
442
+ _rpcExposePort(port: number, options?: {
443
+ visibility?: 'scoped' | 'public';
444
+ }): Promise<{
452
445
  port: number;
453
446
  listening: boolean;
454
447
  pid: number | null;
455
448
  registeredAt: number | null;
456
449
  capability: string | null;
450
+ visibility: "scoped" | "public";
451
+ }>;
452
+ _rpcEnsureDurableApp(input: {
453
+ owner: string;
454
+ preferredPort?: number;
455
+ visibility?: 'scoped' | 'public';
456
+ }): Promise<{
457
+ port: number;
458
+ capability: string | null;
459
+ visibility: "scoped" | "public";
460
+ }>;
461
+ _rpcRemoveDurableApp(owner: string): Promise<{
462
+ owner: string;
463
+ removed: boolean;
464
+ port: number | null;
457
465
  }>;
458
466
  _rpcUnexposePort(port: number): Promise<{
459
467
  port: number;
@@ -461,6 +469,12 @@ export declare class NimbusSession extends CloudflareDurableObject {
461
469
  }>;
462
470
  /** Capability-authenticated port route, for an embedder holding the token. */
463
471
  _rpcRouteCapabilityPort(port: number, capability: string, request: Request, innerPath: string): Promise<Response>;
472
+ /**
473
+ * The port route's recovery seam: a durable application journalled but
474
+ * dead is re-driven on demand rather than left for the alarm pump. The
475
+ * manager decides 'started' | 'absent' | 'failed'; the route maps them.
476
+ */
477
+ ensureDurableAppOnPort(port: number): Promise<'started' | 'absent' | 'failed'>;
464
478
  _rpcDeleteFile(path: string, options?: {
465
479
  recursive?: boolean;
466
480
  }): Promise<void>;
@@ -517,7 +531,7 @@ export declare class NimbusSession extends CloudflareDurableObject {
517
531
  _w5LastPersistRingSize: number;
518
532
  /** B'.4 — live initSession phase. Surfaced via
519
533
  * /api/_diag/session.phase. null pre-first-init. */
520
- _b4Phase: import('@nimbus-sh/core/observability/oom-discriminator.js').SessionState | null;
534
+ _b4Phase: import('@nimbus-sh/platform/oom-discriminator.js').SessionState | null;
521
535
  /** B'.5 — count of warm-rejoin /ws upgrades. Increments each
522
536
  * time the join path is taken (Phase B skipped). 0 means no
523
537
  * warm rejoins yet. Surfaced via /api/_diag/session.warmJoinCount. */
@@ -557,7 +571,7 @@ export declare class NimbusSession extends CloudflareDurableObject {
557
571
  * All concurrent fetches share ONE worker — no port exhaustion.
558
572
  */
559
573
  buildFetchFn(log?: (msg: string) => void): ((url: string, init?: RequestInit) => Promise<Response>) | undefined;
560
- ensureNpmInstaller(onProgress?: (msg: string) => void): void;
574
+ ensureNpmInstaller(onProgress?: (msg: string) => void): Promise<void>;
561
575
  /**
562
576
  * Read an environment flag with default-on semantics. Mirrors the
563
577
  * shouldUseFacetPool / shouldUseFacetResolver / shouldUseBatchFacet