@nimbus-sh/worker 0.4.0 → 0.6.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 (95) hide show
  1. package/README.md +46 -43
  2. package/dist/_shared/preview-host.d.ts +33 -2
  3. package/dist/_shared/preview-host.d.ts.map +1 -1
  4. package/dist/_shared/preview-host.js +73 -8
  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/facets/durable-images.d.ts +59 -0
  9. package/dist/facets/durable-images.d.ts.map +1 -0
  10. package/dist/facets/durable-images.js +94 -0
  11. package/dist/facets/durable-slots.d.ts +45 -0
  12. package/dist/facets/durable-slots.d.ts.map +1 -0
  13. package/dist/facets/durable-slots.js +95 -0
  14. package/dist/facets/esbuild-transform.d.ts +10 -0
  15. package/dist/facets/esbuild-transform.d.ts.map +1 -0
  16. package/dist/facets/esbuild-transform.js +36 -0
  17. package/dist/facets/manager.d.ts +244 -6
  18. package/dist/facets/manager.d.ts.map +1 -1
  19. package/dist/facets/manager.js +901 -117
  20. package/dist/facets/resident-identity.d.ts +23 -0
  21. package/dist/facets/resident-identity.d.ts.map +1 -0
  22. package/dist/facets/resident-identity.js +31 -0
  23. package/dist/git/commands.d.ts +22 -1
  24. package/dist/git/commands.d.ts.map +1 -1
  25. package/dist/git/commands.js +429 -425
  26. package/dist/git-bundle.generated.d.ts +2 -2
  27. package/dist/git-bundle.generated.d.ts.map +1 -1
  28. package/dist/git-bundle.generated.js +3 -3
  29. package/dist/index.d.ts +2 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +2 -1
  32. package/dist/node-shims-artifact.generated.js +3 -3
  33. package/dist/router/index.d.ts.map +1 -1
  34. package/dist/router/index.js +97 -3
  35. package/dist/router/public-directory-do.d.ts +17 -0
  36. package/dist/router/public-directory-do.d.ts.map +1 -0
  37. package/dist/router/public-directory-do.js +16 -0
  38. package/dist/router/public-directory.d.ts +79 -0
  39. package/dist/router/public-directory.d.ts.map +1 -0
  40. package/dist/router/public-directory.js +122 -0
  41. package/dist/router/remote-api.d.ts +27 -1
  42. package/dist/router/remote-api.d.ts.map +1 -1
  43. package/dist/router/remote-api.js +48 -2
  44. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  45. package/dist/runtime/cpython-resident.js +3 -1
  46. package/dist/runtime/node-shims.d.ts +4 -1
  47. package/dist/runtime/node-shims.d.ts.map +1 -1
  48. package/dist/runtime/node-shims.js +324 -6
  49. package/dist/runtime/package-manager.d.ts +44 -1
  50. package/dist/runtime/package-manager.d.ts.map +1 -1
  51. package/dist/runtime/package-manager.js +168 -3
  52. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  53. package/dist/runtime/ruby-resident.js +2 -0
  54. package/dist/session/agent.d.ts +11 -0
  55. package/dist/session/agent.d.ts.map +1 -1
  56. package/dist/session/agent.js +28 -1
  57. package/dist/session/helpers.js +1 -1
  58. package/dist/session/init-phases.d.ts +17 -23
  59. package/dist/session/init-phases.d.ts.map +1 -1
  60. package/dist/session/init-phases.js +17 -41
  61. package/dist/session/init.d.ts.map +1 -1
  62. package/dist/session/init.js +28 -15
  63. package/dist/session/keys.d.ts +12 -0
  64. package/dist/session/keys.d.ts.map +1 -1
  65. package/dist/session/keys.js +12 -0
  66. package/dist/session/nimbus-session.d.ts +63 -5
  67. package/dist/session/nimbus-session.d.ts.map +1 -1
  68. package/dist/session/nimbus-session.js +131 -17
  69. package/dist/session/port-capability.d.ts +141 -8
  70. package/dist/session/port-capability.d.ts.map +1 -1
  71. package/dist/session/port-capability.js +265 -8
  72. package/dist/session/programmatic.d.ts +145 -3
  73. package/dist/session/programmatic.d.ts.map +1 -1
  74. package/dist/session/programmatic.js +434 -12
  75. package/dist/session/routes.d.ts +26 -0
  76. package/dist/session/routes.d.ts.map +1 -1
  77. package/dist/session/routes.js +117 -25
  78. package/dist/session/rpc.d.ts +24 -1
  79. package/dist/session/rpc.d.ts.map +1 -1
  80. package/dist/session/rpc.js +65 -98
  81. package/dist/session/shell-socket.d.ts +76 -0
  82. package/dist/session/shell-socket.d.ts.map +1 -0
  83. package/dist/session/shell-socket.js +172 -0
  84. package/dist/session/start-real-vite.js +1 -1
  85. package/dist/session/supervisor-op.d.ts +42 -0
  86. package/dist/session/supervisor-op.d.ts.map +1 -0
  87. package/dist/session/supervisor-op.js +96 -0
  88. package/dist/session/supervisor-rpc.d.ts +6 -10
  89. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  90. package/dist/session/supervisor-rpc.js +91 -71
  91. package/dist/session/ws.d.ts.map +1 -1
  92. package/dist/session/ws.js +19 -4
  93. package/package.json +4 -4
  94. package/public/_assets/runtime/{node-shims-fea906843f6d594e.js → node-shims-9e1380d9ee430fcc.js} +320 -5
  95. package/public/s/index.html +101 -5
@@ -15,6 +15,17 @@ interface AgentStorage {
15
15
  delete(key: string): Promise<void>;
16
16
  deleteAll(): Promise<void>;
17
17
  deleteAlarm(): Promise<void>;
18
+ list<T = unknown>(options: {
19
+ prefix: string;
20
+ }): Promise<Map<string, T>>;
21
+ transaction<T>(body: (txn: {
22
+ get(key: string): Promise<unknown>;
23
+ put(key: string, value: unknown): Promise<void>;
24
+ delete(key: string): Promise<unknown>;
25
+ list<T2 = unknown>(options: {
26
+ prefix: string;
27
+ }): Promise<Map<string, T2>>;
28
+ }) => Promise<T>): Promise<T>;
18
29
  }
19
30
  interface Host extends ProgrammaticHost, SessionAiHost {
20
31
  ctx: {
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/session/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,OAAO,EASL,KAAK,aAAa,EACnB,MAAM,SAAS,CAAC;AACjB,OAAO,EAUL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAiB3B,UAAU,YAAY;IACpB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,UAAU,IAAK,SAAQ,gBAAgB,EAAE,aAAa;IACpD,GAAG,EAAE;QAAE,OAAO,EAAE,YAAY,CAAA;KAAE,CAAC;IAC/B,GAAG,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxD;AAED,UAAU,iBAAiB;IACzB,CAAC,EAAE,CAAC,CAAC;IACL,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AA2BD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2ClG;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,GAAG,IAAI,CAQxF"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/session/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,OAAO,EASL,KAAK,aAAa,EACnB,MAAM,SAAS,CAAC;AACjB,OAAO,EAYL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAiB3B,UAAU,YAAY;IACpB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE;QACzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;KAC3E,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC/B;AAED,UAAU,IAAK,SAAQ,gBAAgB,EAAE,aAAa;IACpD,GAAG,EAAE;QAAE,OAAO,EAAE,YAAY,CAAA;KAAE,CAAC;IAC/B,GAAG,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxD;AAED,UAAU,iBAAiB;IACzB,CAAC,EAAE,CAAC,CAAC;IACL,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AA2BD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2ClG;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,GAAG,IAAI,CAQxF"}
@@ -12,7 +12,7 @@ import { BASE_PATH_HEADER, TENANT_HEADER } from '../_shared/session-router.js';
12
12
  import { decodeJsonBase64Url, encodeJsonBase64Url, pkceChallenge, randomBase64Url, sealJson, unsealJson, } from '@nimbus-sh/core/_shared/crypto.js';
13
13
  import { clearNimbusAgentOAuthCookie, fetchNimbusCloudflareAccounts, isNimbusCloudflareAccountId, isNimbusTenantSegment, NIMBUS_CF_OAUTH_AUTH_URL, readNimbusCookie, readNimbusAgentCookieSecret, readNimbusAgentOAuthConfig, requestNimbusCloudflareOAuthToken, serializeNimbusCookie, } from './agent-oauth.js';
14
14
  import { clearSessionAiCredential, createSessionAiModel, describeSessionAiConnection, readSessionAiConfig, resolveSessionAiCredential, sessionAiAccountIsAvailable, setSessionAiAccount, storeSessionAiCredential, } from './ai.js';
15
- import { ensureProgrammaticReady, rpcExec, rpcEnsureRuntimes, rpcInstallRuntime, rpcKillProcess, rpcListPorts, rpcListProcesses, rpcProcessLogs, rpcStartProcess, } from './programmatic.js';
15
+ import { ensureProgrammaticReady, rpcExec, rpcEnsureRuntimes, rpcInstallRuntime, rpcExposeApp, rpcKillProcess, rpcListApps, rpcListPorts, rpcListProcesses, rpcProcessLogs, rpcStartProcess, } from './programmatic.js';
16
16
  import { resolveVfsPath } from '@nimbus-sh/core/vfs/path.js';
17
17
  import { appendTextPart, interruptRunningTools, textFromParts, upsertStoredMessage, upsertToolPart, } from './agent-contract.js';
18
18
  import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
@@ -645,6 +645,20 @@ function createAiSdkTools(self) {
645
645
  inputSchema: toolSchema({}, []),
646
646
  execute: async (args) => runTool(self, 'list_ports', args),
647
647
  }),
648
+ expose_app: aiTool({
649
+ description: 'Expose a running application by port, pid or name: reserves its port under its identity, optionally names it and makes it public, and returns its URL.',
650
+ inputSchema: toolSchema({
651
+ target: stringProp('Port number, process id, or app name.'),
652
+ visibility: stringProp("'scoped' (default) or 'public' — public mints a shareable capability URL."),
653
+ name: stringProp('Optional DNS-label name for the app, e.g. "api".'),
654
+ }, ['target']),
655
+ execute: async (args) => runTool(self, 'expose_app', args),
656
+ }),
657
+ list_apps: aiTool({
658
+ description: 'List every application the session knows: owner, name, port, pid, status, visibility, restart policy and URL.',
659
+ inputSchema: toolSchema({}, []),
660
+ execute: async (args) => runTool(self, 'list_apps', args),
661
+ }),
648
662
  };
649
663
  }
650
664
  function collectTurnParts(result) {
@@ -872,6 +886,19 @@ async function runTool(self, name, args) {
872
886
  }
873
887
  if (name === 'list_ports')
874
888
  return rpcListPorts(self);
889
+ if (name === 'expose_app') {
890
+ const raw = String(args.target ?? '').trim();
891
+ if (!raw)
892
+ return { error: 'target is required' };
893
+ const visibility = args.visibility === 'public' ? 'public' : args.visibility === 'scoped' ? 'scoped' : undefined;
894
+ const appName = typeof args.name === 'string' && args.name.trim() ? args.name.trim() : undefined;
895
+ return rpcExposeApp(self, /^\d+$/.test(raw) ? Number(raw) : raw, {
896
+ ...(visibility !== undefined ? { visibility } : {}),
897
+ ...(appName !== undefined ? { name: appName } : {}),
898
+ });
899
+ }
900
+ if (name === 'list_apps')
901
+ return rpcListApps(self);
875
902
  return { error: `unknown tool: ${name}` };
876
903
  }
877
904
  catch (e) {
@@ -370,7 +370,7 @@ export const _CP_PURE_BUILTIN = new Set([
370
370
  'sleep', 'date', 'whoami', 'id', 'hostname', 'uname', 'clear',
371
371
  'tree', 'find', 'grep', 'head', 'tail', 'wc', 'sort', 'uniq', 'sed',
372
372
  'awk', 'xargs', 'tee', 'du', 'diff', 'base64', 'seq', 'realpath',
373
- 'basename', 'dirname', 'printf', 'sha256sum', 'file', 'xxd',
373
+ 'basename', 'dirname', 'printf', 'sha256sum', 'file', 'xxd', 'od', 'hexdump',
374
374
  'chmod', 'chown', 'ln', 'test', '[', 'read', 'exit', 'set', 'shopt',
375
375
  'trap', 'umask', 'ulimit', 'which', 'uptime',
376
376
  ]);
@@ -33,6 +33,7 @@
33
33
  * additional ring entries, not replacements.
34
34
  */
35
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
@@ -42,37 +43,30 @@ export declare function setPhase(self: {
42
43
  _b4Phase: SessionState | null;
43
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
@@ -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,0CAA0C,CAAC;AAGlG;;;;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;;;;;;;;;;;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,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"}
@@ -34,6 +34,7 @@
34
34
  */
35
35
  import { recordRecoveryEvent } from '@nimbus-sh/platform/oom-discriminator.js';
36
36
  import { generation } from '@nimbus-sh/fabric/generation.js';
37
+ import { hasLiveShellOwner } from './shell-socket.js';
37
38
  /**
38
39
  * Set the current phase + record a transition. Fail-soft on the
39
40
  * observability call; the phase update on `self` is direct and
@@ -56,51 +57,26 @@ export function setPhase(self, toState, trigger) {
56
57
  catch { /* observability is non-critical */ }
57
58
  }
58
59
  /**
59
- * [B'.5] Identify the original phase-based warm-rejoin case. The /ws
60
- * upgrade classifier below also recognizes headless sessions whose
61
- * lifecycle phase does not describe a real terminal attachment.
60
+ * Decide what a /ws upgrade should do with the session it lands on.
62
61
  *
63
- * Conditions for warm rejoin:
64
- * 1. Phase = 'drained' (a wsClose / wsError fired since last init).
65
- * 2. Kernel + Shell + Terminal are still alive in-memory (the
66
- * [B'.5] change to wsClose stopped nulling them).
67
- * 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.
68
72
  *
73
+ * The caller classifies BEFORE it accepts the incoming socket, so the
74
+ * upgrade cannot count itself as the incumbent.
69
75
  */
70
- export function isWarmRejoin(self) {
71
- return self._b4Phase === 'drained'
72
- && self.shell != null
73
- && self.terminal != null
74
- && self.kernel != null;
75
- }
76
- /**
77
- * Classify a shell WebSocket upgrade from the actual attachment state.
78
- * A non-null Shell can belong to a headless programmatic boot, so only an
79
- * open socket tagged as `shell` proves that another browser terminal is
80
- * currently attached.
81
- */
82
- export function classifyWsUpgrade(self, sockets) {
83
- if (isWarmRejoin(self))
84
- return 'warm-join';
85
- if (self.shell == null)
76
+ export function classifyWsUpgrade(self, sockets, now = Date.now()) {
77
+ if (self.shell == null || self.terminal == null || self.kernel == null)
86
78
  return 'cold';
87
- if (self.terminal == null || self.kernel == null)
88
- return 'conflict';
89
- const hasOpenShellSocket = sockets.some((socket) => {
90
- if (socket.readyState !== WebSocket.OPEN)
91
- return false;
92
- try {
93
- const attachment = socket.deserializeAttachment();
94
- return typeof attachment === 'object'
95
- && attachment !== null
96
- && 'kind' in attachment
97
- && attachment.kind === 'shell';
98
- }
99
- catch {
100
- return false;
101
- }
102
- });
103
- return hasOpenShellSocket ? 'conflict' : 'warm-join';
79
+ return hasLiveShellOwner(self.ctx, sockets, now) ? 'conflict' : 'warm-join';
104
80
  }
105
81
  /**
106
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;AA+EH,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;AA+EH,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,CAitF9E"}
@@ -58,8 +58,8 @@ 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';
62
+ import { rpcExposeApp, rpcListApps, rpcRemoveApp, rpcRotateLink } from './programmatic.js';
63
63
  import { listInstalledRuntimes, rehydrateInstalledRuntimes, registerRunnerFactory, } from '@nimbus-sh/core/runtime/installed-runtimes.js';
64
64
  // Runtime factories (clang/python/ruby/bash/wasm) are imported lazily at
65
65
  // first-use inside their registered handlers — see the registrations below.
@@ -454,8 +454,13 @@ export async function initSession(self, ws) {
454
454
  }));
455
455
  // ── Git integration (isomorphic-git) ──
456
456
  // ctx + env are passed for clone/fetch/pull which run in a facet to avoid
457
- // exhausting the supervisor DO's CPU budget on large repos.
458
- registerGitCommands(registry, sqliteFs, self.ctx, self.env);
457
+ // exhausting the supervisor DO's CPU budget on large repos. The command
458
+ // module (+ its ~106 KB network-facet dependency) is loaded lazily on the
459
+ // first `git` invocation so it stays out of the cold script-eval graph.
460
+ registry.register('git', async (ctx) => {
461
+ const { runGitCommand } = await import('../git/commands.js');
462
+ return runGitCommand(ctx, sqliteFs, self.ctx, self.env);
463
+ });
459
464
  // ── runtime package manager: `nimbus install` package manager + runner registry.
460
465
  //
461
466
  // 1. Register the clang-runner factory FIRST so the rehydration step
@@ -585,6 +590,14 @@ export async function initSession(self, ws) {
585
590
  vfs: sqliteFs,
586
591
  registry: pkgRegistry,
587
592
  getHome: nimbusGetHome,
593
+ // The application verbs are the session's own (programmatic.ts):
594
+ // the shell, the SDK and the Agent all reach the same policy.
595
+ apps: {
596
+ expose: (target, options) => rpcExposeApp(self, target, options),
597
+ list: () => rpcListApps(self),
598
+ rotateLink: (target) => rpcRotateLink(self, target),
599
+ remove: (target) => rpcRemoveApp(self, target),
600
+ },
588
601
  warmRuntime: async (target, ctx) => {
589
602
  // The runtime name, which is what `nimbus install python` installs
590
603
  // — not the name the user typed. Left as 'python' through the
@@ -689,7 +702,7 @@ export async function initSession(self, ws) {
689
702
  getEsbuild: () => {
690
703
  if (!self.esbuildService) {
691
704
  self.ensureSqliteFs();
692
- self.esbuildService = new EsbuildService(self.sqliteFs);
705
+ self.esbuildService = new EsbuildService(kernelFs);
693
706
  }
694
707
  return self.esbuildService;
695
708
  },
@@ -838,7 +851,7 @@ export async function initSession(self, ws) {
838
851
  getEsbuild: () => {
839
852
  if (!self.esbuildService) {
840
853
  self.ensureSqliteFs();
841
- self.esbuildService = new EsbuildService(self.sqliteFs);
854
+ self.esbuildService = new EsbuildService(kernelFs);
842
855
  }
843
856
  return self.esbuildService;
844
857
  },
@@ -886,7 +899,7 @@ export async function initSession(self, ws) {
886
899
  getEsbuild: () => {
887
900
  if (!self.esbuildService) {
888
901
  self.ensureSqliteFs();
889
- self.esbuildService = new EsbuildService(self.sqliteFs);
902
+ self.esbuildService = new EsbuildService(kernelFs);
890
903
  }
891
904
  return self.esbuildService;
892
905
  },
@@ -956,7 +969,7 @@ export async function initSession(self, ws) {
956
969
  // Lazy-init esbuild service
957
970
  if (!self.esbuildService) {
958
971
  self.ensureSqliteFs();
959
- self.esbuildService = new EsbuildService(self.sqliteFs);
972
+ self.esbuildService = new EsbuildService(kernelFs);
960
973
  }
961
974
  // Parse flags
962
975
  const flags = {};
@@ -1091,7 +1104,7 @@ export async function initSession(self, ws) {
1091
1104
  // Transform TS to JS
1092
1105
  if (cfgName.endsWith('.ts')) {
1093
1106
  if (!self.esbuildService)
1094
- self.esbuildService = new EsbuildService(self.sqliteFs);
1107
+ self.esbuildService = new EsbuildService(kernelFs);
1095
1108
  const t = await self.esbuildService.transform(cfgCode, { loader: 'ts', format: 'esm' });
1096
1109
  cfgCode = t.code;
1097
1110
  }
@@ -1106,7 +1119,7 @@ export async function initSession(self, ws) {
1106
1119
  // ── vite build ──
1107
1120
  if (args[0] === 'build') {
1108
1121
  if (!self.esbuildService)
1109
- self.esbuildService = new EsbuildService(self.sqliteFs);
1122
+ self.esbuildService = new EsbuildService(kernelFs);
1110
1123
  const htmlPath = cwd + '/index.html';
1111
1124
  let entryPoint = cwd + '/src/main.tsx';
1112
1125
  let origHtml = '';
@@ -1222,7 +1235,7 @@ export async function initSession(self, ws) {
1222
1235
  }
1223
1236
  // Start vite on the dist directory
1224
1237
  if (!self.esbuildService)
1225
- self.esbuildService = new EsbuildService(self.sqliteFs);
1238
+ self.esbuildService = new EsbuildService(kernelFs);
1226
1239
  if (self.viteDevServer?.isRunning)
1227
1240
  self.viteDevServer.stop();
1228
1241
  const previewBasePath = self.viteBasePath;
@@ -1395,7 +1408,7 @@ export async function initSession(self, ws) {
1395
1408
  }
1396
1409
  }
1397
1410
  if (!self.esbuildService)
1398
- self.esbuildService = new EsbuildService(self.sqliteFs);
1411
+ self.esbuildService = new EsbuildService(kernelFs);
1399
1412
  const previewBasePath = self.viteBasePath;
1400
1413
  const viteDefine = viteConfig.define;
1401
1414
  // Vite dev servers are represented as long-running process-table
@@ -1563,7 +1576,7 @@ export async function initSession(self, ws) {
1563
1576
  // Lazy-init esbuild
1564
1577
  if (!self.esbuildService) {
1565
1578
  self.ensureSqliteFs();
1566
- self.esbuildService = new EsbuildService(self.sqliteFs);
1579
+ self.esbuildService = new EsbuildService(kernelFs);
1567
1580
  }
1568
1581
  // Parse --root flag; default to the shell cwd so `npm run dev` from
1569
1582
  // a project directory picks up that project's wrangler.jsonc.
@@ -1666,7 +1679,7 @@ export async function initSession(self, ws) {
1666
1679
  kernelFs.writeFile(pkgJsonPath, '{"name":"project","version":"1.0.0","dependencies":{}}\n');
1667
1680
  }
1668
1681
  ctx.stdout.write('\x1b[36mNimbus npm v2 (batched writes)\x1b[0m\n');
1669
- self.ensureNpmInstaller((msg) => {
1682
+ await self.ensureNpmInstaller((msg) => {
1670
1683
  ctx.stdout.write('[npm] ' + msg + '\n');
1671
1684
  });
1672
1685
  const result = await self.npmInstaller.install(cwd, { packages, pid: ctx.pid });
@@ -2164,7 +2177,7 @@ export async function initSession(self, ws) {
2164
2177
  ? `${explicitPkgs.length} packages`
2165
2178
  : 'dependencies from package.json';
2166
2179
  ctx.stdout.write(`\x1b[36mInstalling ${pkgLabel} (npm v2 — batched writes)...\x1b[0m\n`);
2167
- self.ensureNpmInstaller((msg) => {
2180
+ await self.ensureNpmInstaller((msg) => {
2168
2181
  ctx.stdout.write('[npm] ' + msg + '\n');
2169
2182
  });
2170
2183
  // `--loglevel` selects npm's own log protocol on stderr, where npm
@@ -2333,7 +2346,7 @@ export async function initSession(self, ws) {
2333
2346
  }
2334
2347
  // Nimbus-native npx install + run path. Routes package installation
2335
2348
  // through NpmInstaller's full-packument resolver.
2336
- self.ensureNpmInstaller((msg) => ctx.stdout.write('[npm] ' + msg + '\n'));
2349
+ await self.ensureNpmInstaller((msg) => ctx.stdout.write('[npm] ' + msg + '\n'));
2337
2350
  self.ensureSqliteFs();
2338
2351
  const installer = self.npmInstaller;
2339
2352
  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);
@@ -51,6 +51,18 @@ 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:";
62
+ /** Atomic resident identity claim: owner → winning pid, released at process termination. */
63
+ export declare const RESIDENT_OWNER_KEY_PREFIX = "resident-owner:";
64
+ /** Owner → retained durable image digests, including images of stopped processes. */
65
+ export declare const DURABLE_IMAGES_KEY_PREFIX = "durable-images:";
54
66
  /**
55
67
  * Prefix for a named programmatic shell's durable cwd+env, keyed by shell id.
56
68
  * 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;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;;;;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,4FAA4F;AAC5F,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AAE3D,qFAAqF;AACrF,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC"}
@@ -55,6 +55,18 @@ export const ATTACH_BOOTSTRAP_JTI_KEY_PREFIX = 'attach-bootstrap-jti:';
55
55
  * these rows.
56
56
  */
57
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:';
66
+ /** Atomic resident identity claim: owner → winning pid, released at process termination. */
67
+ export const RESIDENT_OWNER_KEY_PREFIX = 'resident-owner:';
68
+ /** Owner → retained durable image digests, including images of stopped processes. */
69
+ export const DURABLE_IMAGES_KEY_PREFIX = 'durable-images:';
58
70
  /**
59
71
  * Prefix for a named programmatic shell's durable cwd+env, keyed by shell id.
60
72
  * 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';
@@ -48,7 +49,6 @@ export declare function renderMotdBanner(version: string): string;
48
49
  export declare function renderWelcomeMarkdown(version: string): string;
49
50
  export declare class NimbusSession extends CloudflareDurableObject {
50
51
  sqliteFs: SqliteVFS | null;
51
- runtimeFsBridges: Map<number, SqliteRuntimeFsBridge> | null;
52
52
  kernel: Kernel | null;
53
53
  shell: Shell | null;
54
54
  shellProcessPid: number | null;
@@ -179,6 +179,13 @@ export declare class NimbusSession extends CloudflareDurableObject {
179
179
  sessionBasePath: string;
180
180
  /** Have we attempted to hydrate sessionBasePath from storage yet? */
181
181
  sessionBasePathHydrated: boolean;
182
+ /**
183
+ * The origin this session was last reached at (`https://host`), remembered
184
+ * beside the base path so the session can spell a path-form URL for one
185
+ * of its own applications — `nimbus expose` printing one, `apps.list`
186
+ * carrying one — on a deployment with no preview-host suffix.
187
+ */
188
+ sessionOrigin: string;
182
189
  /**
183
190
  * Has the "wrangler is aliased to nimbus-wrangler" banner been shown
184
191
  * this session? Reset on WebSocket close/reopen so a reconnecting user
@@ -249,6 +256,20 @@ export declare class NimbusSession extends CloudflareDurableObject {
249
256
  * get/put, so two concurrent exchanges cannot both observe "absent".
250
257
  */
251
258
  _rpcConsumeAttachBootstrap(jti: string): Promise<boolean>;
259
+ /**
260
+ * The one supervisor-op handler this session's bindings, loopback stubs and
261
+ * `_rpc*` delegates all dispatch through — native filesystem ops against
262
+ * the shared bridge store, session overrides for the accounting-carrying
263
+ * reads and the output stream, and the canonical route table for the rest.
264
+ * Lazy: sqliteFs exists only after ensureSqliteFs().
265
+ */
266
+ private _supervisorOps;
267
+ private supervisorOps;
268
+ /** The pid-keyed filesystem bridge behind the supervisor ops. */
269
+ supervisorBridge(pid?: number): SqliteRuntimeFsBridge;
270
+ /** Drop a dead pid's supervisor bridge — its credential stops being valid. */
271
+ supervisorForgetBridge(pid: number): void;
272
+ supervisorOp(envelope: SupervisorOpEnvelope): Promise<unknown>;
252
273
  _rpcReadFile(path: string, pid?: number): Promise<string | null>;
253
274
  _rpcReadFileBytes(path: string, pid?: number): Promise<Uint8Array | null>;
254
275
  _rpcInnerDoFetch(req: any): Promise<any>;
@@ -437,12 +458,43 @@ export declare class NimbusSession extends CloudflareDurableObject {
437
458
  exit: import("@nimbus-sh/core/runtime/process-logs.js").ProcessExitInfo | null;
438
459
  }>;
439
460
  _rpcListPorts(): Promise<_programmatic.SerializedPort[]>;
440
- _rpcExposePort(port: number): Promise<{
461
+ _rpcExposePort(port: number, options?: {
462
+ visibility?: 'scoped' | 'public';
463
+ name?: string;
464
+ }): Promise<{
441
465
  port: number;
442
466
  listening: boolean;
443
467
  pid: number | null;
444
468
  registeredAt: number | null;
445
469
  capability: string | null;
470
+ visibility: "scoped" | "public";
471
+ owner: string | null;
472
+ name: string | null;
473
+ }>;
474
+ _rpcExposeApp(target: _programmatic.AppTarget, options?: {
475
+ visibility?: 'scoped' | 'public';
476
+ name?: string;
477
+ }): Promise<_programmatic.ExposedAppResult>;
478
+ _rpcListApps(): Promise<_programmatic.ListedApp[]>;
479
+ _rpcRotateLink(target: _programmatic.AppTarget): Promise<_programmatic.ExposedAppResult>;
480
+ _rpcRemoveApp(target: _programmatic.AppTarget): Promise<{
481
+ owner: string;
482
+ removed: boolean;
483
+ port: number | null;
484
+ }>;
485
+ _rpcEnsureDurableApp(input: {
486
+ owner: string;
487
+ preferredPort?: number;
488
+ visibility?: 'scoped' | 'public';
489
+ }): Promise<{
490
+ port: number;
491
+ capability: string | null;
492
+ visibility: "scoped" | "public";
493
+ }>;
494
+ _rpcRemoveDurableApp(owner: string): Promise<{
495
+ owner: string;
496
+ removed: boolean;
497
+ port: number | null;
446
498
  }>;
447
499
  _rpcUnexposePort(port: number): Promise<{
448
500
  port: number;
@@ -450,6 +502,12 @@ export declare class NimbusSession extends CloudflareDurableObject {
450
502
  }>;
451
503
  /** Capability-authenticated port route, for an embedder holding the token. */
452
504
  _rpcRouteCapabilityPort(port: number, capability: string, request: Request, innerPath: string): Promise<Response>;
505
+ /**
506
+ * The port route's recovery seam: a durable application journalled but
507
+ * dead is re-driven on demand rather than left for the alarm pump. The
508
+ * manager decides 'started' | 'absent' | 'failed'; the route maps them.
509
+ */
510
+ ensureDurableAppOnPort(port: number): Promise<'started' | 'absent' | 'failed'>;
453
511
  _rpcDeleteFile(path: string, options?: {
454
512
  recursive?: boolean;
455
513
  }): Promise<void>;
@@ -546,7 +604,7 @@ export declare class NimbusSession extends CloudflareDurableObject {
546
604
  * All concurrent fetches share ONE worker — no port exhaustion.
547
605
  */
548
606
  buildFetchFn(log?: (msg: string) => void): ((url: string, init?: RequestInit) => Promise<Response>) | undefined;
549
- ensureNpmInstaller(onProgress?: (msg: string) => void): void;
607
+ ensureNpmInstaller(onProgress?: (msg: string) => void): Promise<void>;
550
608
  /**
551
609
  * Read an environment flag with default-on semantics. Mirrors the
552
610
  * shouldUseFacetPool / shouldUseFacetResolver / shouldUseBatchFacet