@nimbus-sh/core 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 (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +140 -0
  3. package/dist/_shared/error-text.d.ts +15 -0
  4. package/dist/_shared/error-text.d.ts.map +1 -0
  5. package/dist/_shared/error-text.js +21 -0
  6. package/dist/_shared/exports-resolver.d.ts +19 -7
  7. package/dist/_shared/exports-resolver.d.ts.map +1 -1
  8. package/dist/_shared/exports-resolver.js +1 -1
  9. package/dist/_shared/retry.d.ts.map +1 -1
  10. package/dist/_shared/retry.js +3 -1
  11. package/dist/_shared/tarball-stream.d.ts +93 -0
  12. package/dist/_shared/tarball-stream.d.ts.map +1 -0
  13. package/dist/_shared/tarball-stream.js +235 -0
  14. package/dist/_shared/tarball.d.ts +17 -0
  15. package/dist/_shared/tarball.d.ts.map +1 -0
  16. package/dist/_shared/tarball.js +39 -0
  17. package/dist/_shared/w7-frame.js +2 -1
  18. package/dist/constants.d.ts.map +1 -1
  19. package/dist/constants.js +30 -16
  20. package/dist/observability/oom-classify.d.ts +1 -1
  21. package/dist/observability/oom-classify.d.ts.map +1 -1
  22. package/dist/observability/oom-classify.js +7 -0
  23. package/dist/observability/oom-discriminator.d.ts +1 -1
  24. package/dist/runtime/clang-runner.d.ts +38 -0
  25. package/dist/runtime/clang-runner.d.ts.map +1 -0
  26. package/dist/runtime/clang-runner.js +866 -0
  27. package/dist/runtime/esbuild-service.d.ts +4 -3
  28. package/dist/runtime/esbuild-service.d.ts.map +1 -1
  29. package/dist/runtime/esbuild-service.js +9 -4
  30. package/dist/runtime/facet-host.d.ts +12 -0
  31. package/dist/runtime/facet-host.d.ts.map +1 -1
  32. package/dist/runtime/installed-runtimes.d.ts +4 -3
  33. package/dist/runtime/installed-runtimes.d.ts.map +1 -1
  34. package/dist/runtime/local-facet-host.d.ts.map +1 -1
  35. package/dist/runtime/local-facet-host.js +29 -9
  36. package/dist/runtime/long-running-handle.d.ts +3 -3
  37. package/dist/runtime/long-running-handle.d.ts.map +1 -1
  38. package/dist/runtime/os-contracts.d.ts +7 -5
  39. package/dist/runtime/os-contracts.d.ts.map +1 -1
  40. package/dist/runtime/port-registry.d.ts +28 -0
  41. package/dist/runtime/port-registry.d.ts.map +1 -1
  42. package/dist/runtime/port-registry.js +79 -9
  43. package/dist/runtime/process-logs.d.ts +9 -1
  44. package/dist/runtime/process-logs.d.ts.map +1 -1
  45. package/dist/runtime/project-detect.d.ts +6 -1
  46. package/dist/runtime/project-detect.d.ts.map +1 -1
  47. package/dist/runtime/require-resolver.d.ts.map +1 -1
  48. package/dist/runtime/require-resolver.js +3 -4
  49. package/dist/runtime/ruby-gems.d.ts +30 -0
  50. package/dist/runtime/ruby-gems.d.ts.map +1 -0
  51. package/dist/runtime/ruby-gems.js +636 -0
  52. package/dist/runtime/ruby-runner.d.ts +127 -0
  53. package/dist/runtime/ruby-runner.d.ts.map +1 -0
  54. package/dist/runtime/ruby-runner.js +1357 -0
  55. package/dist/runtime/runtime-registry.d.ts +4 -3
  56. package/dist/runtime/runtime-registry.d.ts.map +1 -1
  57. package/dist/runtime/runtime-registry.js +2 -1
  58. package/dist/runtime/session-process-supervisor.d.ts +15 -0
  59. package/dist/runtime/session-process-supervisor.d.ts.map +1 -1
  60. package/dist/runtime/session-process-supervisor.js +30 -0
  61. package/dist/runtime/sqlite-runtime-fs-bridge.js +1 -5
  62. package/dist/runtime/virtual-socket-kernel.d.ts.map +1 -1
  63. package/dist/runtime/virtual-socket-kernel.generated.d.ts +1 -1
  64. package/dist/runtime/virtual-socket-kernel.generated.d.ts.map +1 -1
  65. package/dist/runtime/virtual-socket-kernel.generated.js +2 -2
  66. package/dist/runtime/virtual-socket-kernel.js +10 -5
  67. package/dist/runtime/wasm-runner.d.ts.map +1 -1
  68. package/dist/runtime/wasm-runner.js +22 -14
  69. package/dist/shell/features.d.ts.map +1 -1
  70. package/dist/shell/features.js +5 -4
  71. package/dist/shell/unix-commands.d.ts +14 -1
  72. package/dist/shell/unix-commands.d.ts.map +1 -1
  73. package/dist/shell/unix-commands.js +115 -69
  74. package/dist/substrate/lifo/commands/net/ip.d.ts.map +1 -1
  75. package/dist/substrate/lifo/commands/system/ps.d.ts +2 -1
  76. package/dist/substrate/lifo/commands/system/ps.d.ts.map +1 -1
  77. package/dist/substrate/lifo/kernel/ServiceManager.d.ts.map +1 -1
  78. package/dist/substrate/lifo/kernel/ServiceManager.js +0 -2
  79. package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts +22 -11
  80. package/dist/substrate/lifo/kernel/network/NetworkStack.d.ts.map +1 -1
  81. package/dist/substrate/lifo/kernel/network/NetworkStack.js +0 -2
  82. package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts +2 -1
  83. package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.d.ts.map +1 -1
  84. package/dist/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.js +3 -4
  85. package/dist/substrate/lifo/kernel/vfs/VFS.d.ts +17 -0
  86. package/dist/substrate/lifo/kernel/vfs/VFS.d.ts.map +1 -1
  87. package/dist/substrate/lifo/kernel/vfs/VFS.js +33 -0
  88. package/dist/substrate/lifo/node-compat/esbuild.d.ts.map +1 -1
  89. package/dist/substrate/lifo/node-compat/esbuild.js +2 -4
  90. package/dist/substrate/lifo/node-compat/index.d.ts.map +1 -1
  91. package/dist/substrate/lifo/node-compat/index.js +3 -2
  92. package/dist/substrate/lifo/node-compat/loose-equality.d.ts +19 -0
  93. package/dist/substrate/lifo/node-compat/loose-equality.d.ts.map +1 -0
  94. package/dist/substrate/lifo/node-compat/loose-equality.js +91 -0
  95. package/dist/substrate/lifo/node-compat/process.d.ts.map +1 -1
  96. package/dist/substrate/lifo/node-compat/process.js +6 -3
  97. package/dist/substrate/lifo/shell/Shell.d.ts +19 -0
  98. package/dist/substrate/lifo/shell/Shell.d.ts.map +1 -1
  99. package/dist/substrate/lifo/shell/Shell.js +86 -75
  100. package/dist/substrate/lifo/shell/lexer.d.ts +9 -0
  101. package/dist/substrate/lifo/shell/lexer.d.ts.map +1 -1
  102. package/dist/substrate/lifo/shell/lexer.js +40 -0
  103. package/dist/vfs/seed-project.d.ts.map +1 -1
  104. package/dist/vfs/seed-project.js +8 -2
  105. package/dist/vfs/sqlite-vfs.d.ts +61 -1
  106. package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
  107. package/dist/vfs/sqlite-vfs.js +199 -35
  108. package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
  109. package/dist/workspace/nimbus-workspace.js +9 -2
  110. package/package.json +4 -2
  111. package/src/_shared/error-text.ts +21 -0
  112. package/src/_shared/exports-resolver.ts +24 -4
  113. package/src/_shared/retry.ts +4 -2
  114. package/src/_shared/tarball-stream.ts +263 -0
  115. package/src/_shared/tarball.ts +46 -0
  116. package/src/_shared/w7-frame.ts +2 -1
  117. package/src/constants.ts +30 -16
  118. package/src/observability/oom-classify.ts +7 -0
  119. package/src/observability/oom-discriminator.ts +1 -1
  120. package/src/runtime/clang-runner.ts +924 -0
  121. package/src/runtime/esbuild-service.ts +16 -11
  122. package/src/runtime/facet-host.ts +12 -0
  123. package/src/runtime/installed-runtimes.ts +4 -3
  124. package/src/runtime/local-facet-host.ts +28 -8
  125. package/src/runtime/long-running-handle.ts +3 -3
  126. package/src/runtime/os-contracts.ts +7 -5
  127. package/src/runtime/port-registry.ts +97 -9
  128. package/src/runtime/process-logs.ts +12 -3
  129. package/src/runtime/project-detect.ts +7 -1
  130. package/src/runtime/require-resolver.ts +7 -7
  131. package/src/runtime/ruby-gems.ts +682 -0
  132. package/src/runtime/ruby-runner.ts +1484 -0
  133. package/src/runtime/runtime-registry.ts +9 -7
  134. package/src/runtime/session-process-supervisor.ts +27 -0
  135. package/src/runtime/sqlite-runtime-fs-bridge.ts +9 -6
  136. package/src/runtime/virtual-socket-kernel.generated.ts +2 -2
  137. package/src/runtime/virtual-socket-kernel.ts +10 -5
  138. package/src/runtime/wasm-runner.ts +55 -29
  139. package/src/shell/features.ts +5 -4
  140. package/src/shell/unix-commands.ts +273 -128
  141. package/src/substrate/lifo/commands/net/ip.ts +15 -15
  142. package/src/substrate/lifo/commands/system/ps.ts +2 -1
  143. package/src/substrate/lifo/kernel/ServiceManager.ts +0 -2
  144. package/src/substrate/lifo/kernel/network/NetworkStack.ts +24 -17
  145. package/src/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.ts +37 -12
  146. package/src/substrate/lifo/kernel/vfs/VFS.ts +34 -0
  147. package/src/substrate/lifo/node-compat/esbuild.ts +16 -8
  148. package/src/substrate/lifo/node-compat/index.ts +3 -2
  149. package/src/substrate/lifo/node-compat/loose-equality.ts +81 -0
  150. package/src/substrate/lifo/node-compat/process.ts +5 -2
  151. package/src/substrate/lifo/shell/Shell.ts +92 -70
  152. package/src/substrate/lifo/shell/lexer.ts +38 -0
  153. package/src/vfs/seed-project.ts +10 -3
  154. package/src/vfs/sqlite-vfs.ts +209 -36
  155. package/src/workspace/nimbus-workspace.ts +10 -1
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketTunnel.d.ts","sourceRoot":"","sources":["../../../../../../src/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C;;;;;;;;;;;GAWG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC9C,IAAI,EAAE,KAAK,CAAS;IAEpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAC,CAAM;IAC7B,OAAO,CAAC,cAAc,CAAS;IAG/B,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,gBAAgB,CAAuC;gBAG9D,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,EAC1B,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,SAAS,SAAY,EACrB,WAAW,GAAE,MAAM,GAAG,IAAW;IAgBlC,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;OAEG;IACY,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAUlC;;OAEG;IACY,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBpC;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAc7B;;OAEG;YACW,OAAO;IA4CrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,OAAO,CAAC,aAAa;IAgBrB;;OAEG;YACW,iBAAiB;IA2E/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAK1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAgBpB;;OAEG;IACH,OAAO,CAAC,SAAS;IAgBjB;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;OAEG;IACM,QAAQ,IAAI,MAAM;IAQ3B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,cAAc,IAAI,MAAM,EAAE;CAG1B"}
1
+ {"version":3,"file":"WebSocketTunnel.d.ts","sourceRoot":"","sources":["../../../../../../src/substrate/lifo/kernel/network/tunnel/WebSocketTunnel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AA4B5D;;;;;;;;;;;GAWG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC9C,IAAI,EAAE,KAAK,CAAS;IAEpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,YAAY,CAAC,CAAqC;IAC1D,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAC,CAAgC;IACvD,OAAO,CAAC,cAAc,CAAS;IAG/B,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,gBAAgB,CAAuC;gBAG9D,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,EAC1B,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,EACjD,SAAS,SAAY,EACrB,WAAW,GAAE,MAAM,GAAG,IAAW;IAgBlC,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;OAEG;IACY,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAUlC;;OAEG;IACY,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBpC;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAc7B;;OAEG;YACW,OAAO;IA4CrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,OAAO,CAAC,aAAa;IAgBrB;;OAEG;YACW,iBAAiB;IAyE/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAK1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAgBpB;;OAEG;IACH,OAAO,CAAC,SAAS;IAgBjB;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;OAEG;IACM,QAAQ,IAAI,MAAM;IAQ3B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,cAAc,IAAI,MAAM,EAAE;CAG1B"}
@@ -202,13 +202,12 @@ export class WebSocketTunnel extends BaseTunnel {
202
202
  port = parseInt(match[1], 10);
203
203
  path = match[2] || '/';
204
204
  }
205
- // Check if port exists in registry
206
- if (!this.portRegistry || !this.portRegistry.has(port)) {
205
+ // Check if a server is listening on the port
206
+ const handler = this.portRegistry?.get(port);
207
+ if (!handler) {
207
208
  this.sendError(requestId, 404, `No server listening on port ${port}`);
208
209
  return;
209
210
  }
210
- // Get handler
211
- const handler = this.portRegistry.get(port);
212
211
  // Create virtual request/response
213
212
  const vReq = {
214
213
  method,
@@ -77,6 +77,23 @@ export declare class VFS {
77
77
  private rewriteFile;
78
78
  appendFile(path: string, content: string | Uint8Array): void;
79
79
  exists(path: string): boolean;
80
+ /**
81
+ * Type probes, mount-aware through {@link stat}.
82
+ *
83
+ * `CredentialedVfs` declares these and the durable coreutils call them on
84
+ * whatever `ctx.vfs` is, so a view that omitted them was not merely missing
85
+ * a convenience: `touch` on an existing file died with
86
+ * `targetVfs.isDirectory is not a function`, because `&&` short-circuited
87
+ * past the call whenever the file was absent — which is why creating a file
88
+ * worked and touching one did not.
89
+ *
90
+ * A structural miss answers false, the way `fs.existsSync` does. A denial
91
+ * still throws: traverse-x enforcement must not be maskable into a quiet
92
+ * false, which is the rule `SqliteVFS.probeInode` already follows.
93
+ */
94
+ isDirectory(path: string): boolean;
95
+ isFile(path: string): boolean;
96
+ private probeType;
80
97
  access(path: string, mode: number): void;
81
98
  stat(path: string): Stat;
82
99
  unlink(path: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"VFS.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/kernel/vfs/VFS.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VFS.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/kernel/vfs/VFS.ts"],"names":[],"mappings":"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"}
@@ -384,6 +384,39 @@ export class VFS {
384
384
  return false;
385
385
  }
386
386
  }
387
+ /**
388
+ * Type probes, mount-aware through {@link stat}.
389
+ *
390
+ * `CredentialedVfs` declares these and the durable coreutils call them on
391
+ * whatever `ctx.vfs` is, so a view that omitted them was not merely missing
392
+ * a convenience: `touch` on an existing file died with
393
+ * `targetVfs.isDirectory is not a function`, because `&&` short-circuited
394
+ * past the call whenever the file was absent — which is why creating a file
395
+ * worked and touching one did not.
396
+ *
397
+ * A structural miss answers false, the way `fs.existsSync` does. A denial
398
+ * still throws: traverse-x enforcement must not be maskable into a quiet
399
+ * false, which is the rule `SqliteVFS.probeInode` already follows.
400
+ */
401
+ isDirectory(path) {
402
+ return this.probeType(path) === 'directory';
403
+ }
404
+ isFile(path) {
405
+ return this.probeType(path) === 'file';
406
+ }
407
+ probeType(path) {
408
+ try {
409
+ return this.stat(path).type;
410
+ }
411
+ catch (error) {
412
+ if (error !== null && typeof error === 'object' && 'code' in error) {
413
+ const code = error.code;
414
+ if (code === 'ENOENT' || code === 'ENOTDIR')
415
+ return undefined;
416
+ }
417
+ throw error;
418
+ }
419
+ }
387
420
  access(path, mode) {
388
421
  const vp = this.getProvider(path);
389
422
  if (vp) {
@@ -1 +1 @@
1
- {"version":3,"file":"esbuild.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/node-compat/esbuild.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8BH,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+CvD"}
1
+ {"version":3,"file":"esbuild.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/node-compat/esbuild.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAsCH,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+CvD"}
@@ -9,10 +9,8 @@
9
9
  const ESBUILD_WASM_VERSION = '0.24.2';
10
10
  const ESBUILD_WASM_URL = `https://unpkg.com/esbuild-wasm@${ESBUILD_WASM_VERSION}/esbuild.wasm`;
11
11
  const ESBUILD_ESM_URL = `https://unpkg.com/esbuild-wasm@${ESBUILD_WASM_VERSION}/esm/browser.min.js`;
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
12
  let esbuildModule = null;
14
13
  let initPromise = null;
15
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
14
  async function ensureInitialized() {
17
15
  if (esbuildModule)
18
16
  return esbuildModule;
@@ -25,10 +23,10 @@ async function ensureInitialized() {
25
23
  wasmURL: ESBUILD_WASM_URL,
26
24
  });
27
25
  esbuildModule = mod;
26
+ return mod;
28
27
  })();
29
28
  }
30
- await initPromise;
31
- return esbuildModule;
29
+ return await initPromise;
32
30
  }
33
31
  export function createEsbuild() {
34
32
  const mod = {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/node-compat/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAShE,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAehF,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAClD,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,CAiQ/E;AAED,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/node-compat/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAShE,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAgBhF,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAClD,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,CAiQ/E;AAED,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
@@ -19,6 +19,7 @@ import { createModuleShim } from './module.js';
19
19
  import * as readlineModule from './readline.js';
20
20
  import { createRimraf } from './rimraf.js';
21
21
  import { createEsbuild } from './esbuild.js';
22
+ import { assertEqualHolds } from './loose-equality.js';
22
23
  export function createModuleMap(ctx) {
23
24
  const map = {
24
25
  fs: () => createFs(ctx.vfs, ctx.cwd),
@@ -89,11 +90,11 @@ export function createModuleMap(ctx) {
89
90
  throw new Error(message || 'AssertionError');
90
91
  };
91
92
  assert.ok = assert;
92
- assert.equal = (a, b, msg) => { if (a != b)
93
+ assert.equal = (a, b, msg) => { if (!assertEqualHolds(a, b))
93
94
  throw new Error(msg || `${a} != ${b}`); };
94
95
  assert.strictEqual = (a, b, msg) => { if (a !== b)
95
96
  throw new Error(msg || `${a} !== ${b}`); };
96
- assert.notEqual = (a, b, msg) => { if (a == b)
97
+ assert.notEqual = (a, b, msg) => { if (assertEqualHolds(a, b))
97
98
  throw new Error(msg || `${a} == ${b}`); };
98
99
  assert.notStrictEqual = (a, b, msg) => { if (a === b)
99
100
  throw new Error(msg || `${a} === ${b}`); };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * loose-equality.ts — the `==` coercions, spelled out.
3
+ *
4
+ * `assert.equal`/`assert.notEqual` are specified against the `==` operator —
5
+ * Node's own docs call it "shallow, coercive equality between the actual and
6
+ * expected parameters using the == operator" — and since Node 14 both also
7
+ * treat two NaNs as identical, which `==` on its own does not. Guest code
8
+ * inside the sandbox sees those semantics, so the shim carries them here
9
+ * instead of the strict comparison a reader would expect of new code.
10
+ */
11
+ /** IsLooselyEqual — what `a == b` answers. */
12
+ export declare function looseEqual(a: unknown, b: unknown): boolean;
13
+ /**
14
+ * Whether Node's legacy assertions call these equal: `==`, widened by the
15
+ * both-sides-NaN case Node folded into `assert.equal` in v14. Node reaches
16
+ * for the non-coercing `Number.isNaN`, so a string spelling "NaN" is not it.
17
+ */
18
+ export declare function assertEqualHolds(a: unknown, b: unknown): boolean;
19
+ //# sourceMappingURL=loose-equality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loose-equality.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/node-compat/loose-equality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsCH,8CAA8C;AAC9C,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAoB1D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAKhE"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * loose-equality.ts — the `==` coercions, spelled out.
3
+ *
4
+ * `assert.equal`/`assert.notEqual` are specified against the `==` operator —
5
+ * Node's own docs call it "shallow, coercive equality between the actual and
6
+ * expected parameters using the == operator" — and since Node 14 both also
7
+ * treat two NaNs as identical, which `==` on its own does not. Guest code
8
+ * inside the sandbox sees those semantics, so the shim carries them here
9
+ * instead of the strict comparison a reader would expect of new code.
10
+ */
11
+ /** The spec's Object type: everything callable or keyed, null excluded. */
12
+ function isObject(value) {
13
+ return value !== null && (typeof value === 'object' || typeof value === 'function');
14
+ }
15
+ /** ToPrimitive(value) with the default hint. */
16
+ function toPrimitive(value) {
17
+ if (Symbol.toPrimitive in value) {
18
+ const exotic = value[Symbol.toPrimitive];
19
+ if (typeof exotic === 'function')
20
+ return exotic.call(value, 'default');
21
+ }
22
+ for (const method of ['valueOf', 'toString']) {
23
+ if (!(method in value))
24
+ continue;
25
+ const convert = value[method];
26
+ if (typeof convert !== 'function')
27
+ continue;
28
+ const primitive = convert.call(value);
29
+ if (!isObject(primitive))
30
+ return primitive;
31
+ }
32
+ throw new TypeError('Cannot convert object to primitive value');
33
+ }
34
+ /**
35
+ * A bigint compares against the mathematical value of its counterpart, so a
36
+ * fractional number, a non-finite one, or a string naming no integer can
37
+ * never match one.
38
+ */
39
+ function bigIntLooseEqual(big, other) {
40
+ if (typeof other === 'number')
41
+ return Number.isInteger(other) && BigInt(other) === big;
42
+ if (typeof other !== 'string')
43
+ return false;
44
+ try {
45
+ return BigInt(other) === big;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /** IsLooselyEqual — what `a == b` answers. */
52
+ export function looseEqual(a, b) {
53
+ if (a === null || a === undefined)
54
+ return b === null || b === undefined;
55
+ if (b === null || b === undefined)
56
+ return false;
57
+ // Two objects match only as the same reference; an object against a
58
+ // primitive matches on the object's own primitive value.
59
+ if (isObject(a)) {
60
+ return isObject(b) ? a === b : looseEqual(toPrimitive(a), b);
61
+ }
62
+ if (isObject(b))
63
+ return looseEqual(a, toPrimitive(b));
64
+ const aType = typeof a;
65
+ const bType = typeof b;
66
+ if (aType === bType)
67
+ return a === b;
68
+ if (aType === 'boolean')
69
+ return looseEqual(Number(a), b);
70
+ if (bType === 'boolean')
71
+ return looseEqual(a, Number(b));
72
+ if (aType === 'symbol' || bType === 'symbol')
73
+ return false;
74
+ if (typeof a === 'bigint')
75
+ return bigIntLooseEqual(a, b);
76
+ if (typeof b === 'bigint')
77
+ return bigIntLooseEqual(b, a);
78
+ return Number(a) === Number(b);
79
+ }
80
+ /**
81
+ * Whether Node's legacy assertions call these equal: `==`, widened by the
82
+ * both-sides-NaN case Node folded into `assert.equal` in v14. Node reaches
83
+ * for the non-coercing `Number.isNaN`, so a string spelling "NaN" is not it.
84
+ */
85
+ export function assertEqualHolds(a, b) {
86
+ if (typeof a === 'number' && typeof b === 'number') {
87
+ if (Number.isNaN(a) && Number.isNaN(b))
88
+ return true;
89
+ }
90
+ return looseEqual(a, b);
91
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/node-compat/process.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,EAAE,MAAM,CAAC;gBACL,IAAI,EAAE,MAAM;CAKzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc;;;;;;;kBAShC,MAAM;;;sBAQJ,MAAM;;;;;;;;;sBASN,MAAM;;;;;;;;;;;;;;;;;;cAkBD,MAAM;UACZ,MAAM;;;;;;;;;;qBAWX,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,CAAC,MAAM,EAAE,MAAM,CAAC;sBAa7B,MAAM;;mBAGP,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,WAAW,OAAO,EAAE;;;;;;;;;;;;;uBAgB5C,MAAM;;;;;mBAMV,MAAM;oBAEL,MAAM;wBAGA,MAAM;;;;;;;;;;;;;;;;;;gBAShB,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;yBAK/B,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;kBAG/C,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;iBAOzC,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;4BAM7B,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;iCACnC,MAAM;uBAKhB,MAAM,gBArIqB,OAAO,EAAE,KAAK,IAAI;kBAsIlD,MAAM,WAAW,OAAO,EAAE;2BAMjB,MAAM;;;6BAGJ,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;0BAK3C,MAAM,gBApJkB,OAAO,EAAE,KAAK,IAAI;;;;;;;;;;;;EA4JnE"}
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/node-compat/process.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,EAAE,MAAM,CAAC;gBACL,IAAI,EAAE,MAAM;CAKzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc;;;;;;;kBAShC,MAAM;;;sBAQJ,MAAM;;;;;;;;;sBASN,MAAM;;;;;;;;;;;;;;;;;;cAkBD,MAAM;UACZ,MAAM;;;;;;;;;;qBAWX,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,CAAC,MAAM,EAAE,MAAM,CAAC;sBAa7B,MAAM;;mBAGP,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,WAAW,OAAO,EAAE;;;;;;;;;;;;;uBAgB5C,MAAM;;;;;mBAMV,MAAM;oBAEL,MAAM;wBAGA,MAAM;;;;;;;;;;;;;;;;;;gBAShB,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;yBAK/B,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;kBAG/C,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;iBAOzC,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;4BAM7B,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;iCACnC,MAAM;uBAQhB,MAAM,gBAxIqB,OAAO,EAAE,KAAK,IAAI;kBAyIlD,MAAM,WAAW,OAAO,EAAE;2BAMjB,MAAM;;;6BAGJ,MAAM,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI;0BAK3C,MAAM,gBAvJkB,OAAO,EAAE,KAAK,IAAI;;;;;;;;;;;;EA+JnE"}
@@ -139,10 +139,13 @@ export function createProcess(opts) {
139
139
  },
140
140
  removeListener: (event, fn) => proc.off(event, fn),
141
141
  removeAllListeners: (event) => {
142
- if (event)
142
+ if (event) {
143
143
  delete listeners[event];
144
- else
145
- Object.keys(listeners).forEach((k) => delete listeners[k]);
144
+ }
145
+ else {
146
+ for (const k of Object.keys(listeners))
147
+ delete listeners[k];
148
+ }
146
149
  return proc;
147
150
  },
148
151
  listeners: (event) => listeners[event] ? [...listeners[event]] : [],
@@ -69,6 +69,12 @@ export declare class Shell {
69
69
  private commandIdentity;
70
70
  private tabCount;
71
71
  pasteQueue: string[];
72
+ /**
73
+ * Accepted lines that do not form a complete command yet: an unclosed
74
+ * quote or a trailing `\` keeps the shell reading under PS2, as bash
75
+ * does, instead of executing a truncated command.
76
+ */
77
+ private pendingLine;
72
78
  /**
73
79
  * Keystrokes that arrived while a foreground command owned the terminal and
74
80
  * nothing was reading stdin. A tty buffers type-ahead and hands it to the
@@ -86,6 +92,12 @@ export declare class Shell {
86
92
  setCwd(cwd: string): void;
87
93
  getEnv(): Record<string, string>;
88
94
  getVfs(): VFS;
95
+ /**
96
+ * The `runAs` host this shell re-credentials through. A caller building a
97
+ * second Shell over the same kernel needs it, or its commands lose the
98
+ * identity-transition path `sudo` and `su` are dispatched on.
99
+ */
100
+ getRunAsHost(): CommandRunAsHost | undefined;
89
101
  getRegistry(): CommandRegistry;
90
102
  /**
91
103
  * Programmatic command execution with captured stdout/stderr.
@@ -121,6 +133,13 @@ export declare class Shell {
121
133
  private moveCursorEnd;
122
134
  private historyUp;
123
135
  private historyDown;
136
+ /**
137
+ * A line the user finished with Enter. If it leaves a quote open or ends
138
+ * in a line continuation it is not a command yet: bash buffers it, shows
139
+ * PS2 and keeps reading, and so does this shell. A `\<newline>` join drops
140
+ * both characters; a quoted join keeps the newline in the string.
141
+ */
142
+ private acceptLine;
124
143
  executeLine(line: string): Promise<void>;
125
144
  private builtinCd;
126
145
  private builtinPwd;
@@ -1 +1 @@
1
- {"version":3,"file":"Shell.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/shell/Shell.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Shell.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/shell/Shell.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { resolve } from '../utils/path.js';
2
2
  import { BOLD, GREEN, BLUE, RESET } from '../utils/colors.js';
3
3
  import { VFSError } from '../kernel/vfs/index.js';
4
4
  import { ExitSignal, Interpreter, assignScalar, } from './interpreter.js';
5
- import { lex } from './lexer.js';
5
+ import { continuationState, lex } from './lexer.js';
6
6
  import { TokenKind } from './types.js';
7
7
  import { HistoryManager } from './history.js';
8
8
  import { JobTable } from './jobs.js';
@@ -31,6 +31,8 @@ export function formatShellPrompt(env, cwd) {
31
31
  const { displayPath, user, host } = shellPromptParts(env, cwd);
32
32
  return `${BOLD}${GREEN}${user}@${host}${RESET}:${BOLD}${BLUE}${displayPath}${RESET}$ `;
33
33
  }
34
+ /** PS2 — shown while an accepted line has not closed into a command yet. */
35
+ const CONTINUATION_PROMPT = '> ';
34
36
  export class Shell {
35
37
  terminal;
36
38
  vfs;
@@ -74,6 +76,12 @@ export class Shell {
74
76
  tabCount = 0;
75
77
  // Paste queue for multiline paste support
76
78
  pasteQueue = [];
79
+ /**
80
+ * Accepted lines that do not form a complete command yet: an unclosed
81
+ * quote or a trailing `\` keeps the shell reading under PS2, as bash
82
+ * does, instead of executing a truncated command.
83
+ */
84
+ pendingLine = null;
77
85
  /**
78
86
  * Keystrokes that arrived while a foreground command owned the terminal and
79
87
  * nothing was reading stdin. A tty buffers type-ahead and hands it to the
@@ -181,6 +189,14 @@ export class Shell {
181
189
  getVfs() {
182
190
  return this.vfs;
183
191
  }
192
+ /**
193
+ * The `runAs` host this shell re-credentials through. A caller building a
194
+ * second Shell over the same kernel needs it, or its commands lose the
195
+ * identity-transition path `sudo` and `su` are dispatched on.
196
+ */
197
+ getRunAsHost() {
198
+ return this.commandIdentity.runAs;
199
+ }
184
200
  getRegistry() {
185
201
  return this.registry;
186
202
  }
@@ -341,6 +357,10 @@ export class Shell {
341
357
  }
342
358
  }
343
359
  printPrompt() {
360
+ if (this.pendingLine !== null) {
361
+ this.terminal.write(CONTINUATION_PROMPT);
362
+ return;
363
+ }
344
364
  // Report finished background jobs from JobTable (legacy)
345
365
  const doneJobs = this.jobTable.collectDone();
346
366
  for (const job of doneJobs) {
@@ -357,61 +377,32 @@ export class Shell {
357
377
  this.terminalStdin.feed(data);
358
378
  return;
359
379
  }
360
- // Multiline paste detection: if data contains newlines and has multiple chars,
361
- // split into lines and execute them sequentially via the paste queue
380
+ // Multiline paste detection: if data contains newlines and has multiple
381
+ // chars, split into lines and accept them sequentially via the paste
382
+ // queue. Middle segments are queued even when empty — a blank pasted
383
+ // line is a newline inside an open quote; only the tail after the final
384
+ // newline is dropped when empty, because no Enter followed it.
362
385
  if (!this.running && data.length > 1 && /[\r\n]/.test(data)) {
363
386
  const lines = data.split(/\r\n|\r|\n/);
364
- for (let i = 0; i < lines.length; i++) {
365
- const segment = lines[i];
366
- if (i === 0) {
367
- // First segment: append to current lineBuffer and execute
368
- if (segment) {
369
- this.lineBuffer = this.lineBuffer.slice(0, this.cursorPos) + segment + this.lineBuffer.slice(this.cursorPos);
370
- this.cursorPos += segment.length;
371
- }
372
- if (i < lines.length - 1) {
373
- // There's a newline after this segment, execute it
374
- this.redrawLine();
375
- this.terminal.write('\r\n');
376
- const line = this.lineBuffer.trim();
377
- this.lineBuffer = '';
378
- this.cursorPos = 0;
379
- this.historyIndex = -1;
380
- if (line) {
381
- this.history.push(line);
382
- // Queue remaining lines before executing
383
- for (let j = 1; j < lines.length - 1; j++) {
384
- if (lines[j])
385
- this.pasteQueue.push(lines[j]);
386
- }
387
- // Last segment (after final newline) goes into lineBuffer without executing
388
- const lastSegment = lines[lines.length - 1];
389
- if (lastSegment) {
390
- this.pasteQueue.push(lastSegment);
391
- }
392
- this.executeLine(line);
393
- return;
394
- }
395
- else {
396
- // Empty first line, queue the rest
397
- for (let j = 1; j < lines.length - 1; j++) {
398
- if (lines[j])
399
- this.pasteQueue.push(lines[j]);
400
- }
401
- const lastSegment = lines[lines.length - 1];
402
- if (lastSegment) {
403
- this.pasteQueue.push(lastSegment);
404
- }
405
- this.drainPasteQueue();
406
- return;
407
- }
408
- }
409
- }
387
+ const first = lines[0];
388
+ if (first) {
389
+ this.lineBuffer = this.lineBuffer.slice(0, this.cursorPos) + first + this.lineBuffer.slice(this.cursorPos);
390
+ this.cursorPos += first.length;
410
391
  }
411
- // If we get here, data had newlines but only one segment (shouldn't happen)
412
- // Fall through to normal handling
413
- if (this.lineBuffer)
414
- this.redrawLine();
392
+ this.redrawLine();
393
+ this.terminal.write('\r\n');
394
+ const line = this.lineBuffer;
395
+ this.lineBuffer = '';
396
+ this.cursorPos = 0;
397
+ this.historyIndex = -1;
398
+ for (let j = 1; j < lines.length - 1; j++) {
399
+ this.pasteQueue.push(lines[j]);
400
+ }
401
+ const lastSegment = lines[lines.length - 1];
402
+ if (lastSegment) {
403
+ this.pasteQueue.push(lastSegment);
404
+ }
405
+ this.acceptLine(line);
415
406
  return;
416
407
  }
417
408
  // ESC sequences. Cursor motion and history are line EDITING, so they only
@@ -457,6 +448,7 @@ export class Shell {
457
448
  }
458
449
  else if (signal === 'INT') {
459
450
  this.terminal.write('^C\r\n');
451
+ this.pendingLine = null;
460
452
  this.lineBuffer = '';
461
453
  this.cursorPos = 0;
462
454
  this.screenCursorRow = 0;
@@ -517,18 +509,12 @@ export class Shell {
517
509
  // Enter
518
510
  if (data === '\r') {
519
511
  this.terminal.write('\r\n');
520
- const line = this.lineBuffer.trim();
512
+ const line = this.lineBuffer;
521
513
  this.lineBuffer = '';
522
514
  this.cursorPos = 0;
523
515
  this.screenCursorRow = 0;
524
516
  this.historyIndex = -1;
525
- if (line) {
526
- this.history.push(line);
527
- this.executeLine(line);
528
- }
529
- else {
530
- this.printPrompt();
531
- }
517
+ this.acceptLine(line);
532
518
  return;
533
519
  }
534
520
  // Backspace
@@ -698,6 +684,8 @@ export class Shell {
698
684
  this.redrawLine();
699
685
  }
700
686
  getPromptWidth() {
687
+ if (this.pendingLine !== null)
688
+ return CONTINUATION_PROMPT.length;
701
689
  const { displayPath, user, host } = shellPromptParts(this.env, this.cwd);
702
690
  // "user@host:path$ " — count visible chars only (no ANSI codes)
703
691
  return user.length + 1 + host.length + 1 + displayPath.length + 2;
@@ -714,7 +702,7 @@ export class Shell {
714
702
  // Clear from here to end of screen
715
703
  this.terminal.write('\x1b[J');
716
704
  // Rewrite prompt + buffer
717
- this.terminal.write(formatShellPrompt(this.env, this.cwd));
705
+ this.terminal.write(this.pendingLine === null ? formatShellPrompt(this.env, this.cwd) : CONTINUATION_PROMPT);
718
706
  this.terminal.write(this.lineBuffer);
719
707
  // After writing all content, figure out which row the cursor is on.
720
708
  // If content exactly fills N rows, the terminal auto-wraps cursor to the next row.
@@ -755,19 +743,11 @@ export class Shell {
755
743
  }
756
744
  drainPasteQueue() {
757
745
  const next = this.pasteQueue.shift();
758
- if (next !== undefined) {
759
- const line = next.trim();
760
- if (line) {
761
- this.terminal.write(line);
762
- this.terminal.write('\r\n');
763
- this.history.push(line);
764
- this.executeLine(line);
765
- }
766
- else {
767
- // Skip empty lines, try next
768
- this.drainPasteQueue();
769
- }
770
- }
746
+ if (next === undefined)
747
+ return;
748
+ this.terminal.write(next);
749
+ this.terminal.write('\r\n');
750
+ this.acceptLine(next);
771
751
  }
772
752
  moveCursorLeft() {
773
753
  if (this.cursorPos > 0) {
@@ -825,6 +805,37 @@ export class Shell {
825
805
  this.cursorPos = this.lineBuffer.length;
826
806
  this.redrawLine();
827
807
  }
808
+ /**
809
+ * A line the user finished with Enter. If it leaves a quote open or ends
810
+ * in a line continuation it is not a command yet: bash buffers it, shows
811
+ * PS2 and keeps reading, and so does this shell. A `\<newline>` join drops
812
+ * both characters; a quoted join keeps the newline in the string.
813
+ */
814
+ acceptLine(rawLine) {
815
+ let command;
816
+ if (this.pendingLine === null) {
817
+ command = rawLine.trim();
818
+ if (!command) {
819
+ this.printPrompt();
820
+ this.drainPasteQueue();
821
+ return;
822
+ }
823
+ }
824
+ else {
825
+ command = continuationState(this.pendingLine) === 'backslash'
826
+ ? this.pendingLine.slice(0, -1) + rawLine
827
+ : this.pendingLine + '\n' + rawLine;
828
+ }
829
+ if (continuationState(command) !== null) {
830
+ this.pendingLine = command;
831
+ this.printPrompt();
832
+ this.drainPasteQueue();
833
+ return;
834
+ }
835
+ this.pendingLine = null;
836
+ this.history.push(command);
837
+ this.executeLine(command);
838
+ }
828
839
  async executeLine(line) {
829
840
  // History expansion
830
841
  const expanded = this.historyManager.expand(line);
@@ -1,5 +1,14 @@
1
1
  import { type Token } from './types.js';
2
2
  export declare function lex(input: string): Token[];
3
+ export type ContinuationState = 'single' | 'double' | 'backslash';
4
+ /**
5
+ * How `input` leaves the lexer at end of input: inside an unclosed single or
6
+ * double quote, or ending in a `\` line continuation (which bash also honors
7
+ * inside double quotes, where `\<newline>` is removed). The interactive shell
8
+ * uses this to keep reading (PS2) instead of executing a truncated command.
9
+ * Comments are skipped exactly where lex() treats `#` as one.
10
+ */
11
+ export declare function continuationState(input: string): ContinuationState | null;
3
12
  /**
4
13
  * The full `${...}` span starting at `pos`. Quotes, nested braces and command
5
14
  * substitutions inside it belong to the expansion, not to the surrounding
@@ -1 +1 @@
1
- {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/shell/lexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,KAAK,EAAiB,MAAM,YAAY,CAAC;AAElE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAmE1C;AA8hBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAG9C"}
1
+ {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../../../src/substrate/lifo/shell/lexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,KAAK,EAAiB,MAAM,YAAY,CAAC;AAElE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAmE1C;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CA2BzE;AA8hBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAG9C"}