@nimbus-sh/worker 0.7.0 → 0.9.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 (102) hide show
  1. package/dist/facets/compose.d.ts +3 -0
  2. package/dist/facets/compose.d.ts.map +1 -1
  3. package/dist/facets/compose.js +1 -1
  4. package/dist/facets/manager.d.ts +26 -15
  5. package/dist/facets/manager.d.ts.map +1 -1
  6. package/dist/facets/manager.js +154 -112
  7. package/dist/facets/real-vite-hmr.d.ts +3 -1
  8. package/dist/facets/real-vite-hmr.d.ts.map +1 -1
  9. package/dist/facets/real-vite-hmr.js +18 -8
  10. package/dist/facets/ws-terminal.d.ts +9 -31
  11. package/dist/facets/ws-terminal.d.ts.map +1 -1
  12. package/dist/facets/ws-terminal.js +58 -51
  13. package/dist/hosted/commands.d.ts +12 -0
  14. package/dist/hosted/commands.d.ts.map +1 -0
  15. package/dist/hosted/commands.js +1647 -0
  16. package/dist/hosted/runtime.d.ts +135 -0
  17. package/dist/hosted/runtime.d.ts.map +1 -0
  18. package/dist/hosted/runtime.js +344 -0
  19. package/dist/hosted/services.d.ts +44 -0
  20. package/dist/hosted/services.d.ts.map +1 -0
  21. package/dist/hosted/services.js +522 -0
  22. package/dist/loaders/generated-workers.d.ts +1 -1
  23. package/dist/loaders/generated-workers.d.ts.map +1 -1
  24. package/dist/loaders/generated-workers.js +2 -2
  25. package/dist/node-shims-artifact.generated.js +3 -3
  26. package/dist/runtime/bash-repl.d.ts +6 -4
  27. package/dist/runtime/bash-repl.d.ts.map +1 -1
  28. package/dist/runtime/bash-repl.js +46 -7
  29. package/dist/runtime/bun-runner.js +1 -1
  30. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  31. package/dist/runtime/facet-loader-host.js +1 -3
  32. package/dist/runtime/node-runner.d.ts +1 -1
  33. package/dist/runtime/node-runner.d.ts.map +1 -1
  34. package/dist/runtime/node-runner.js +2 -2
  35. package/dist/runtime/node-shims.d.ts.map +1 -1
  36. package/dist/runtime/node-shims.js +14 -1
  37. package/dist/runtime/opencode-facet-runner.js +14 -14
  38. package/dist/runtime/package-manager.d.ts +35 -122
  39. package/dist/runtime/package-manager.d.ts.map +1 -1
  40. package/dist/runtime/package-manager.js +61 -596
  41. package/dist/runtime/process-logs-api.d.ts +1 -1
  42. package/dist/runtime/process-logs-api.d.ts.map +1 -1
  43. package/dist/runtime/process-logs-api.js +1 -1
  44. package/dist/runtime/python-repl.d.ts +6 -4
  45. package/dist/runtime/python-repl.d.ts.map +1 -1
  46. package/dist/runtime/python-repl.js +92 -67
  47. package/dist/runtime/repl-session.d.ts +9 -47
  48. package/dist/runtime/repl-session.d.ts.map +1 -1
  49. package/dist/runtime/repl-session.js +105 -105
  50. package/dist/runtime/ruby-repl.d.ts +3 -2
  51. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  52. package/dist/runtime/ruby-repl.js +7 -6
  53. package/dist/runtime/runtime-catalog.d.ts +13 -0
  54. package/dist/runtime/runtime-catalog.d.ts.map +1 -1
  55. package/dist/runtime/runtime-catalog.js +88 -0
  56. package/dist/runtime/wasi/preamble.d.ts.map +1 -1
  57. package/dist/runtime/wasi/preamble.js +74 -56
  58. package/dist/runtime-catalog.generated.js +1 -1
  59. package/dist/session/agent.d.ts +1 -21
  60. package/dist/session/agent.d.ts.map +1 -1
  61. package/dist/session/ai.d.ts +5 -1
  62. package/dist/session/ai.d.ts.map +1 -1
  63. package/dist/session/ai.js +7 -0
  64. package/dist/session/hibernation.d.ts +2 -1
  65. package/dist/session/hibernation.d.ts.map +1 -1
  66. package/dist/session/hibernation.js +7 -4
  67. package/dist/session/init.d.ts +1 -36
  68. package/dist/session/init.d.ts.map +1 -1
  69. package/dist/session/init.js +18 -1773
  70. package/dist/session/loopback.d.ts +2 -29
  71. package/dist/session/loopback.d.ts.map +1 -1
  72. package/dist/session/loopback.js +3 -27
  73. package/dist/session/nimbus-session.d.ts +28 -34
  74. package/dist/session/nimbus-session.d.ts.map +1 -1
  75. package/dist/session/nimbus-session.js +78 -518
  76. package/dist/session/npm-install-port.d.ts +3 -1
  77. package/dist/session/npm-install-port.d.ts.map +1 -1
  78. package/dist/session/port-capability.d.ts.map +1 -1
  79. package/dist/session/port-capability.js +11 -1
  80. package/dist/session/programmatic.d.ts +5 -38
  81. package/dist/session/programmatic.d.ts.map +1 -1
  82. package/dist/session/programmatic.js +17 -49
  83. package/dist/session/rpc.d.ts +3 -43
  84. package/dist/session/rpc.d.ts.map +1 -1
  85. package/dist/session/rpc.js +8 -67
  86. package/dist/session/supervisor-op.d.ts +10 -5
  87. package/dist/session/supervisor-op.d.ts.map +1 -1
  88. package/dist/session/supervisor-op.js +21 -48
  89. package/dist/session/supervisor-rpc.d.ts +33 -16
  90. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  91. package/dist/session/supervisor-rpc.js +49 -4
  92. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  93. package/dist/vfs/facet-resident-store.js +4 -2
  94. package/dist/workspace-host.d.ts +7 -0
  95. package/dist/workspace-host.d.ts.map +1 -0
  96. package/dist/workspace-host.js +5 -0
  97. package/dist/workspace.d.ts +2 -0
  98. package/dist/workspace.d.ts.map +1 -0
  99. package/dist/workspace.js +1 -0
  100. package/package.json +17 -12
  101. package/public/_assets/runtime/{node-shims-8f8c64edd2a1f0f5.js → node-shims-acaa037e6d26ec0d.js} +14 -1
  102. package/scripts/bundle-runtime.mjs +35 -8
@@ -1 +1 @@
1
- {"version":3,"file":"process-logs-api.d.ts","sourceRoot":"","sources":["../../src/runtime/process-logs-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uDAAuD,CAAC;AAEtG;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,wBAAwB,CAAC;IACpC;;;;OAIG;IACH,GAAG,EAAE;QACH,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;KACvD,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,SAAS,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAON;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,iBAAiB,GACtB,QAAQ,CA8EV;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,wBAAwB,EACnC,GAAG,EAAE;IAAE,aAAa,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAAA;CAAE,GACjD,IAAI,CA8CN;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,wBAAwB,GAClC,QAAQ,CAsCV;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK7D"}
1
+ {"version":3,"file":"process-logs-api.d.ts","sourceRoot":"","sources":["../../src/runtime/process-logs-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uDAAuD,CAAC;AAEtG;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,wBAAwB,CAAC;IACpC;;;;OAIG;IACH,GAAG,EAAE;QACH,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;KACvD,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,SAAS,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAON;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,iBAAiB,GACtB,QAAQ,CA8EV;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,wBAAwB,EACnC,GAAG,EAAE;IAAE,aAAa,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAAA;CAAE,GACjD,IAAI,CA8CN;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,wBAAwB,GAClC,QAAQ,CAsCV;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK7D"}
@@ -30,7 +30,7 @@ export function notifyTerminalEvent(terminal, event) {
30
30
  if (!terminal)
31
31
  return;
32
32
  try {
33
- terminal.ws.send(JSON.stringify(event));
33
+ terminal.ws?.send(JSON.stringify(event));
34
34
  }
35
35
  catch {
36
36
  /* socket closed or congested — dropping is the right behavior here */
@@ -1,3 +1,4 @@
1
+ import type { Shell } from '@nimbus-sh/core/substrate/lifo/shell/Shell.js';
1
2
  /**
2
3
  * python-repl.ts — the interactive `python` prompt.
3
4
  *
@@ -19,13 +20,14 @@
19
20
  * so `1 + 1` prints `2` and `x = 1` prints nothing, with no wrapper of ours
20
21
  * deciding what counts as a result.
21
22
  */
22
- import type { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
23
23
  import type { FacetManager } from '../facets/manager.js';
24
24
  import type { WebSocketTerminal } from '../facets/ws-terminal.js';
25
25
  import type { RuntimeManifest } from '@nimbus-sh/core/runtime/runtime-manifest.js';
26
+ import { type NimbusFilesystemAuthority } from '@nimbus-sh/core/runtime/os-contracts.js';
26
27
  export interface PythonReplDeps {
27
28
  facetMgr: FacetManager;
28
- vfs: SqliteVFS;
29
+ /** Owns the installed interpreter blobs the prompt is booted from. */
30
+ authority: NimbusFilesystemAuthority;
29
31
  terminal: WebSocketTerminal;
30
32
  /** Per-user-VFS install dir, e.g. 'home/user/.nimbus/runtimes/cpython/3.13.14'. */
31
33
  installRoot: string;
@@ -40,7 +42,7 @@ export interface PythonReplDeps {
40
42
  * ReplSession lets it drain that queue on attach, which is the difference
41
43
  * between the pasted tail arriving and the prompt hanging.
42
44
  */
43
- shell?: unknown;
45
+ shell?: Pick<Shell, 'takeQueuedInput'>;
44
46
  /**
45
47
  * The invoking process's pid.
46
48
  *
@@ -57,5 +59,5 @@ export declare function runPythonRepl(deps: PythonReplDeps): Promise<number>;
57
59
  * Pay the interpreter's boot before the user asks for a prompt. Pushing empty
58
60
  * source compiles to a no-op, so the only thing it does is bring the facet up.
59
61
  */
60
- export declare function warmPythonRepl(deps: Pick<PythonReplDeps, 'facetMgr' | 'vfs' | 'installRoot' | 'manifest'>): Promise<void>;
62
+ export declare function warmPythonRepl(deps: Pick<PythonReplDeps, 'facetMgr' | 'authority' | 'installRoot' | 'manifest'>): Promise<void>;
61
63
  //# sourceMappingURL=python-repl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"python-repl.d.ts","sourceRoot":"","sources":["../../src/runtime/python-repl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAoBnF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA0QD,wBAAsB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAIzE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,KAAK,GAAG,aAAa,GAAG,UAAU,CAAC,GAC1E,OAAO,CAAC,IAAI,CAAC,CAIf"}
1
+ {"version":3,"file":"python-repl.d.ts","sourceRoot":"","sources":["../../src/runtime/python-repl.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+CAA+C,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAMnF,OAAO,EAAe,KAAK,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AActG,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,sEAAsE;IACtE,SAAS,EAAE,yBAAyB,CAAC;IACrC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,mFAAmF;IACnF,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,CAAC;IAC1B;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IACvC;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA2SD,wBAAsB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAIzE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC,GAChF,OAAO,CAAC,IAAI,CAAC,CAIf"}
@@ -1,24 +1,7 @@
1
- /**
2
- * python-repl.ts — the interactive `python` prompt.
3
- *
4
- * A long-lived facet holds one interpreter and each submitted line is run in
5
- * it, so definitions, imports and open files survive between prompts. That is
6
- * the whole reason the interpreter is built as a WASI reactor: a command
7
- * module's _start runs once.
8
- *
9
- * Incompleteness is decided by `codeop.compile_command`, which is what the real
10
- * Python REPL uses — it returns None for source that is syntactically fine so
11
- * far but unfinished (an open bracket, a `def` with no body yet), raises
12
- * SyntaxError for source that can never complete, and otherwise hands back a
13
- * code object. That distinction is not something to re-derive from error
14
- * strings; the previous Pyodide implementation asked PyodideConsole for it,
15
- * which is the same idea reached through a Pyodide-only object.
16
- *
17
- * Compiling in 'single' mode also gets the echo right for free: an expression
18
- * statement goes through sys.displayhook exactly as it does at a real prompt,
19
- * so `1 + 1` prints `2` and `x = 1` prints nothing, with no wrapper of ours
20
- * deciding what counts as a result.
21
- */
1
+ import { IsolatePool } from '@nimbus-sh/fabric/isolate-pool.js';
2
+ import { manifestVfs } from '@nimbus-sh/core/runtime/vfs-manifest.js';
3
+ import { withHostFilesystem } from '@nimbus-sh/core/shell/execution-fs.js';
4
+ import { z } from 'zod/v4';
22
5
  import { ReplSession } from './repl-session.js';
23
6
  import { sessionUsesSciVariant } from '@nimbus-sh/core/runtime/python-pip.js';
24
7
  import { buildCPythonPreamble } from '@nimbus-sh/core/runtime/cpython-runner.js';
@@ -35,6 +18,8 @@ const INCOMPLETE_MARKER = '__NIMBUS_PY_INCOMPLETE__';
35
18
  * ordinary output and never leaves.
36
19
  */
37
20
  const EXIT_MARKER = '__NIMBUS_PY_EXIT__';
21
+ const PythonFacetResult = z.object({ stdout: z.string(), stderr: z.string(), exitCode: z.number().int(), error: z.string().optional() });
22
+ const PythonFacetFailure = z.object({ __nimbusFacetError: z.string() });
38
23
  /** Where cpython-runner's catalog spec stages the interpreter. */
39
24
  const CPYTHON_WASM_REL = 'share/cpython/python.wasm';
40
25
  const CPYTHON_SCI_WASM_REL = 'share/cpython/python-sci.wasm';
@@ -102,13 +87,25 @@ class PythonReplAdapter {
102
87
  return ('Python 3.13.14 (CPython, wasm32-wasi, Nimbus runtime)\r\n' +
103
88
  'Type "exit()" or press Ctrl-D to exit.\r\n');
104
89
  }
105
- async push(source) {
90
+ push(source) {
91
+ const controller = new AbortController();
92
+ const done = this.evaluate(source, controller.signal);
93
+ const active = { controller, done };
94
+ this.active = active;
95
+ const clear = () => { if (this.active === active)
96
+ this.active = null; };
97
+ void done.then(clear, clear);
98
+ return done;
99
+ }
100
+ async evaluate(source, signal) {
106
101
  const trimmed = source.trim();
107
102
  if (trimmed === 'exit' || trimmed === 'quit') {
108
103
  return { kind: 'output', stdout: '', stderr: 'Use exit() or Ctrl-D to exit\n' };
109
104
  }
110
105
  try {
106
+ signal.throwIfAborted();
111
107
  await this.ensurePool();
108
+ signal.throwIfAborted();
112
109
  }
113
110
  catch (e) {
114
111
  const message = e instanceof Error ? e.message : String(e);
@@ -116,7 +113,7 @@ class PythonReplAdapter {
116
113
  }
117
114
  let result;
118
115
  try {
119
- result = await this.submit(buildReplDriver(source));
116
+ result = await this.submit(buildReplDriver(source), signal);
120
117
  }
121
118
  catch (e) {
122
119
  const message = e instanceof Error ? e.message : String(e);
@@ -149,48 +146,53 @@ class PythonReplAdapter {
149
146
  }
150
147
  return { kind: 'output', stdout: result.stdout, stderr: result.stderr };
151
148
  }
152
- async close() {
153
- if (this.pool) {
154
- try {
155
- this.pool.dispose?.();
156
- }
157
- catch { /* fail-soft: the session is ending anyway */ }
158
- this.pool = null;
149
+ close() { return this.interrupt(); }
150
+ resetPool() {
151
+ const pool = this.pool;
152
+ this.pool = null;
153
+ this.wasmBytes = null;
154
+ this.fsSnapshot = null;
155
+ pool?.dispose();
156
+ }
157
+ async interrupt() {
158
+ const active = this.active;
159
+ active?.controller.abort();
160
+ try {
161
+ await active?.done;
162
+ }
163
+ finally {
164
+ this.resetPool();
159
165
  }
160
166
  }
161
167
  async ensurePool() {
162
- const vfsForVariant = this.deps.vfs.as(CRED_KERNEL);
168
+ await withHostFilesystem(this.deps.authority, CRED_KERNEL, (vfs) => this.ensurePoolFrom(vfs));
169
+ }
170
+ async ensurePoolFrom(vfs) {
163
171
  const sciPath = `${this.deps.installRoot}/${CPYTHON_SCI_WASM_REL}`;
164
- const wantsSci = sessionUsesSciVariant(vfsForVariant) && vfsForVariant.exists(sciPath);
172
+ const wantsSci = (await sessionUsesSciVariant(vfs)) && (await vfs.exists(sciPath));
165
173
  // A prompt that was open before `pip install numpy` is holding the
166
174
  // interpreter that does not have it. Dropping the pool rebuilds on the next
167
175
  // statement, which is the facet restart EXTENSIONS.md says this costs.
168
176
  if (this.pool && this.poolUsesSci !== wantsSci) {
169
- try {
170
- this.pool.dispose?.();
171
- }
172
- catch { /* fail-soft: it is being replaced */ }
173
- this.pool = null;
177
+ this.resetPool();
174
178
  }
175
179
  if (this.pool)
176
180
  return;
177
181
  this.poolUsesSci = wantsSci;
178
182
  const { installRoot, facetMgr } = this.deps;
179
- const vfs = vfsForVariant;
180
183
  const wasmPath = wantsSci ? sciPath : `${installRoot}/${CPYTHON_WASM_REL}`;
181
184
  const stdlibPath = `${installRoot}/${CPYTHON_STDLIB_REL}`;
182
- if (!vfs.exists(wasmPath)) {
185
+ if (!(await vfs.exists(wasmPath))) {
183
186
  throw new Error(`python.wasm missing at ${wasmPath} (run 'nimbus install python')`);
184
187
  }
185
- if (!vfs.exists(stdlibPath)) {
188
+ if (!(await vfs.exists(stdlibPath))) {
186
189
  throw new Error(`python313.zip missing at ${stdlibPath} (run 'nimbus install python')`);
187
190
  }
188
- this.wasmBytes = toArrayBuffer(vfs.readFile(wasmPath));
189
- const { manifestVfs } = await import('@nimbus-sh/core/runtime/vfs-manifest.js');
191
+ this.wasmBytes = toArrayBuffer(await vfs.readFile(wasmPath));
190
192
  // The install root is the Python prefix, so the manifest covers lib/ and
191
193
  // etc/ as they are — nothing is aliased into a path the supervisor could
192
194
  // not serve.
193
- const built = manifestVfs(vfs, 'home/user', { extraRoots: [installRoot.replace(/^\/+/, '')] });
195
+ const built = await manifestVfs(vfs.authority, CRED_KERNEL, 'home/user', { extraRoots: [installRoot.replace(/^\/+/, '')] });
194
196
  if ('error' in built)
195
197
  throw new Error(built.error);
196
198
  // Same workaround as cpython-runner, and it belongs to the same open
@@ -198,7 +200,7 @@ class PythonReplAdapter {
198
200
  // demand-load, though the transport delivers it byte-identically. Seeding
199
201
  // it by value is what makes the interpreter start. Remove both together.
200
202
  const snapshot = built.snapshot;
201
- const zipBytes = vfs.readFile(stdlibPath);
203
+ const zipBytes = await vfs.readFile(stdlibPath);
202
204
  let bin = '';
203
205
  const CH = 32768;
204
206
  for (let i = 0; i < zipBytes.length; i += CH) {
@@ -207,7 +209,6 @@ class PythonReplAdapter {
207
209
  snapshot.files[stdlibPath.replace(/^\/+/, '')] = btoa(bin);
208
210
  this.fsSnapshot = snapshot;
209
211
  this.pythonHome = `/${installRoot.replace(/^\/+/, '')}`;
210
- const { IsolatePool } = await import('@nimbus-sh/fabric/isolate-pool.js');
211
212
  const host = getFacetManagerLoaderHost(facetMgr);
212
213
  // A prompt where `open(path, "w")` silently does nothing is worse than one
213
214
  // that refuses to start, so the pid decides which pool this is. Written as
@@ -219,43 +220,65 @@ class PythonReplAdapter {
219
220
  // Distinct from cpython-runner's tag: a REPL facet holds a live
220
221
  // interpreter and must never be handed a one-shot invocation.
221
222
  tag: wantsSci ? 'python-repl:sci' : 'python-repl',
223
+ scope: crypto.randomUUID(),
222
224
  concurrency: 1,
223
225
  preamble: buildCPythonPreamble(),
224
226
  wasmModules: { 'python.wasm': this.wasmBytes },
225
227
  };
226
228
  const pid = this.deps.pid;
227
- this.pool = (typeof pid === 'number' && pid > 0
229
+ this.pool = typeof pid === 'number' && pid > 0
228
230
  ? new IsolatePool(host.env, host.ctx, { ...base, supervisorPid: pid })
229
231
  // The install-time warm-up has no invoking process. It boots the
230
232
  // interpreter and never touches a file, so it asks for no supervisor
231
233
  // rather than binding one it cannot authenticate to.
232
- : new IsolatePool(host.env, host.ctx, { ...base, omitSupervisor: true }));
234
+ : new IsolatePool(host.env, host.ctx, { ...base, omitSupervisor: true });
233
235
  }
234
- async submit(userCode) {
235
- const result = await this.pool.submit(pythonReplStepFacetFn, {
236
- userCode,
237
- pythonHome: this.pythonHome,
238
- pyArgv: ['python'],
239
- userEnv: { HOME: '/home/user', PYTHONUNBUFFERED: '1' },
240
- progName: 'python',
241
- cwd: '/home/user',
242
- fsSnapshot: this.fsSnapshot,
243
- }, { timeoutMs: 60_000 });
244
- return result;
236
+ active = null;
237
+ async submit(userCode, signal) {
238
+ const pool = this.pool;
239
+ if (!pool || !this.fsSnapshot)
240
+ throw new Error('Python REPL is not initialized');
241
+ const response = await pool.submitRequest(pythonReplStepRequestFn, new Request('https://facet.internal/python-repl-step', {
242
+ method: 'POST',
243
+ body: JSON.stringify({
244
+ userCode,
245
+ pythonHome: this.pythonHome,
246
+ pyArgv: ['python'],
247
+ userEnv: { HOME: '/home/user', PYTHONUNBUFFERED: '1' },
248
+ progName: 'python',
249
+ cwd: '/home/user',
250
+ fsSnapshot: this.fsSnapshot,
251
+ }),
252
+ signal,
253
+ }), { timeoutMs: 60_000 });
254
+ if (!response.ok) {
255
+ const failure = PythonFacetFailure.parse(await response.json());
256
+ throw new Error(failure.__nimbusFacetError);
257
+ }
258
+ return PythonFacetResult.parse(await response.json());
245
259
  }
246
260
  }
247
261
  /**
248
- * Facet-side. Serialized with fn.toString(), so it captures nothing and names
249
- * no import: __cpythonReplRun is put on globalThis by the preamble, and unlike
250
- * __cpythonRun it keeps its interpreter between calls.
262
+ * Facet-side, request-shaped: serialized with fn.toString() into the
263
+ * pool's fetch entrypoint, so it captures nothing and names no import —
264
+ * __cpythonReplRun is put on globalThis by the preamble, and unlike
265
+ * __cpythonRun it keeps its interpreter between calls. The request body
266
+ * is the step payload the adapter JSON-encodes; the response is the
267
+ * step result. Request transport because it is the pool's only
268
+ * cancellable dispatch: Ctrl-C aborts the request, workerd stops the
269
+ * interpreter at its suspension point.
251
270
  */
252
- async function pythonReplStepFacetFn(args, facetEnv) {
271
+ async function pythonReplStepRequestFn(request, facetEnv) {
272
+ const args = await request.json();
273
+ if (typeof args !== 'object' || args === null || !('userCode' in args) || typeof args.userCode !== 'string') {
274
+ throw new Error('Python REPL request must contain userCode');
275
+ }
253
276
  const run = Reflect.get(globalThis, '__cpythonReplRun');
254
277
  if (typeof run !== 'function') {
255
- return {
278
+ return Response.json({
256
279
  stdout: '', stderr: '', exitCode: 127,
257
280
  error: 'cpython preamble missing: __cpythonReplRun not in scope',
258
- };
281
+ });
259
282
  }
260
283
  const adopt = Reflect.get(globalThis, '__wasiAdoptSupervisor');
261
284
  const drain = Reflect.get(globalThis, '__wasiDrainPersist');
@@ -266,13 +289,15 @@ async function pythonReplStepFacetFn(args, facetEnv) {
266
289
  // filesystem it could read.
267
290
  if (supervisor)
268
291
  Reflect.set(globalThis, '__nimbusPySupervisor', supervisor);
269
- adopt?.(supervisor);
292
+ if (typeof adopt === 'function')
293
+ Reflect.apply(adopt, undefined, [supervisor ?? null]);
270
294
  try {
271
- return await run(args);
295
+ return Response.json(await run(args));
272
296
  }
273
297
  finally {
274
298
  // A line that wrote a file and then raised still wrote the file.
275
- await drain?.();
299
+ if (typeof drain === 'function')
300
+ await drain();
276
301
  }
277
302
  }
278
303
  export async function runPythonRepl(deps) {
@@ -12,6 +12,7 @@
12
12
  * interface declared below.
13
13
  */
14
14
  import type { WebSocketTerminal } from '../facets/ws-terminal.js';
15
+ import type { Shell } from '@nimbus-sh/core/substrate/lifo/shell/Shell.js';
15
16
  /**
16
17
  * Per-runtime adapter contract. The shell-side ReplSession orchestrates
17
18
  * input handling; the adapter wraps the actual runtime invocation.
@@ -30,6 +31,8 @@ export interface ReplAdapter {
30
31
  /** Called once on session close (exit() / Ctrl-D / shell teardown).
31
32
  * Should free any cached pool / isolate. Idempotent. */
32
33
  close(): Promise<void>;
34
+ /** Abort and settle the active push, resetting its interpreter before resolving. */
35
+ interrupt?(): void | Promise<void>;
33
36
  /** Primary prompt (typically '>>> '). */
34
37
  ps1: string;
35
38
  /** Continuation prompt (typically '... '). */
@@ -50,45 +53,10 @@ export type ReplPushResult = {
50
53
  kind: 'error';
51
54
  stderr: string;
52
55
  };
53
- /**
54
- * Manages an interactive REPL session: stdin buffering with minimal
55
- * readline UX (line-mode), output routing to the WS terminal, prompt
56
- * rendering, and adapter dispatch.
57
- *
58
- * The shell creates a ReplSession when a runtime's `startRepl` hook
59
- * fires; the session installs a `replCallback` on the WebSocketTerminal
60
- * (via attachRepl()) and runs until the adapter signals 'exit' or the
61
- * user presses Ctrl-D on an empty line.
62
- *
63
- * No setTimeout in the read loop — the session is driven entirely by
64
- * keystroke arrival on the WS, with awaits gating the adapter's push().
65
- */
66
56
  export declare class ReplSession {
67
57
  private adapter;
58
+ private detachRepl;
68
59
  private terminal;
69
- private detachReplCb;
70
- /**
71
- * REPL-R7-1 (2026-05-12): optional reference to the Nimbus shell.
72
- *
73
- * Required when the REPL is launched from a multi-line WS frame
74
- * (e.g. user pastes `python\nexit(7)`). The shell input handler
75
- * splits the frame on \r\n and pushes lines AFTER
76
- * the first into `shell.pasteQueue`, which is drained ONLY when
77
- * the shell becomes idle (after executeLine returns). While our
78
- * REPL is running, those pasteQueue lines sit there waiting and
79
- * the REPL itself receives no input → user sees a hung `>>> `
80
- * prompt that never responds.
81
- *
82
- * If `shell` is provided, ReplSession will, immediately after
83
- * attaching its replCallback, drain shell.pasteQueue and feed the
84
- * lines into the callback (suffixed with \r each — matches what
85
- * the WS frame originally would have looked like). This makes the
86
- * paste path work transparently for REPL launches.
87
- *
88
- * The shell reference is optional so existing adapters (bun, node,
89
- * ruby) that haven't been updated keep working with their
90
- * pre-fix behavior.
91
- */
92
60
  private shellRef;
93
61
  /** Current line buffer (chars typed since the last enter). */
94
62
  private lineBuf;
@@ -101,9 +69,11 @@ export declare class ReplSession {
101
69
  /** Per-session history ring (most recent first). Capped at 100. */
102
70
  private history;
103
71
  private historyIdx;
104
- /** Resolves when close() has been called and the session ended. */
105
72
  private closedResolve;
106
73
  private closedPromise;
74
+ private pendingInterrupt;
75
+ private activePush;
76
+ private ending;
107
77
  /** Exit code captured from adapter's last 'exit' return. */
108
78
  private exitCode;
109
79
  /**
@@ -124,23 +94,16 @@ export declare class ReplSession {
124
94
  */
125
95
  private inputQueue;
126
96
  private draining;
127
- constructor(adapter: ReplAdapter, terminal: WebSocketTerminal, shell?: any);
97
+ constructor(adapter: ReplAdapter, terminal: WebSocketTerminal, shell?: Pick<Shell, 'takeQueuedInput'>);
128
98
  /** Run the session: prints banner, installs the input hook, returns
129
99
  * a promise that resolves with the exit code when the session ends. */
130
100
  run(): Promise<number>;
131
- /**
132
- * REPL-A1b: drain the input queue serially. Pulls data off
133
- * inputQueue, runs handleInput, and reads any data that arrived
134
- * during the await. Exits when queue is empty. Only ONE drainInput
135
- * runs at a time (guarded by draining flag set in attachRepl
136
- * callback).
137
- */
138
101
  private drainInput;
139
102
  /** Process an input chunk. May contain multiple characters (paste
140
103
  * or rapid typing) — we iterate char-by-char to handle each
141
104
  * control byte individually. */
142
105
  private handleInput;
143
- /** Submit the current line buffer to the adapter. */
106
+ private interruptBusy;
144
107
  private submitLine;
145
108
  /** Map up-arrow → previous history entry. */
146
109
  private historyUp;
@@ -148,7 +111,6 @@ export declare class ReplSession {
148
111
  private historyDown;
149
112
  /** Erase the current displayed line and replace with `text`. */
150
113
  private replaceCurrentLine;
151
- /** Close the session: detach input hook, free adapter, resolve. */
152
114
  private endSession;
153
115
  }
154
116
  //# sourceMappingURL=repl-session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repl-session.d.ts","sourceRoot":"","sources":["../../src/runtime/repl-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;mCAC+B;IAC/B,MAAM,IAAI,MAAM,CAAC;IACjB;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5C;6DACyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;;;;;GAYG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,YAAY,CAA6B;IACjD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,QAAQ,CAAa;IAE7B,8DAA8D;IAC9D,OAAO,CAAC,OAAO,CAAc;IAC7B,sDAAsD;IACtD,OAAO,CAAC,SAAS,CAAa;IAC9B,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAgB;IAChC,8EAA8E;IAC9E,OAAO,CAAC,IAAI,CAAkB;IAC9B,mEAAmE;IACnE,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,UAAU,CAAc;IAChC,mEAAmE;IACnE,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAgB;IACrC,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAa;IAC7B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,CAAC,EAAE,GAAG;IAU1E;4EACwE;IAClE,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;IAyD5B;;;;;;OAMG;YACW,UAAU;IASxB;;qCAEiC;YACnB,WAAW;IA6EzB,qDAAqD;YACvC,UAAU;IA8ExB,6CAA6C;IAC7C,OAAO,CAAC,SAAS;IAMjB,mDAAmD;IACnD,OAAO,CAAC,WAAW;IAUnB,gEAAgE;IAChE,OAAO,CAAC,kBAAkB;IAW1B,mEAAmE;YACrD,UAAU;CAYzB"}
1
+ {"version":3,"file":"repl-session.d.ts","sourceRoot":"","sources":["../../src/runtime/repl-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+CAA+C,CAAC;AAI3E;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;mCAC+B;IAC/B,MAAM,IAAI,MAAM,CAAC;IACjB;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5C;6DACyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,oFAAoF;IACpF,SAAS,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAiBtC,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,QAAQ,CAA+C;IAE/D,8DAA8D;IAC9D,OAAO,CAAC,OAAO,CAAc;IAC7B,sDAAsD;IACtD,OAAO,CAAC,SAAS,CAAa;IAC9B,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAgB;IAChC,8EAA8E;IAC9E,OAAO,CAAC,IAAI,CAAkB;IAC9B,mEAAmE;IACnE,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,gBAAgB,CAA0C;IAClE,OAAO,CAAC,UAAU,CAAwC;IAC1D,OAAO,CAAC,MAAM,CAA8B;IAE5C,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAa;IAC7B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAUrG;4EACwE;IAClE,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;YAuCd,UAAU;IAiBxB;;qCAEiC;YACnB,WAAW;IA0EzB,OAAO,CAAC,aAAa;YAiBP,UAAU;IA0FxB,6CAA6C;IAC7C,OAAO,CAAC,SAAS;IAMjB,mDAAmD;IACnD,OAAO,CAAC,WAAW;IAUnB,gEAAgE;IAChE,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,UAAU;CAwBnB"}