@volter/supercode-terminal 0.2.57 → 0.2.59

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.
package/index.mjs CHANGED
@@ -4,6 +4,7 @@ import { appendFileSync, cpSync, existsSync, mkdirSync, readFileSync, renameSync
4
4
  import { homedir } from 'node:os';
5
5
  import { dirname, join } from 'node:path';
6
6
  import { connect as netConnect } from 'node:net';
7
+ import { createRequire } from 'node:module';
7
8
  import { fileURLToPath } from 'node:url';
8
9
  import { createServer as defaultCreateServer } from 'node:http';
9
10
 
@@ -560,11 +561,18 @@ export class PtyTerminalHost {
560
561
  if ([...this.#sessions.values()].some((entry) => entry.name === name)) {
561
562
  throw new TerminalHostError('session_exists', `A pane named ${name} already exists.`);
562
563
  }
563
- const [{ default: pty }, { default: headless }, { default: serialize }] = await Promise.all([
564
- import('@homebridge/node-pty-prebuilt-multiarch'),
565
- import('@xterm/headless'),
566
- import('@xterm/addon-serialize'),
567
- ]);
564
+ let pty, headless, serialize;
565
+ try {
566
+ [{ default: pty }, { default: headless }, { default: serialize }] = await Promise.all([
567
+ import('@homebridge/node-pty-prebuilt-multiarch'),
568
+ import('@xterm/headless'),
569
+ import('@xterm/addon-serialize'),
570
+ ]);
571
+ } catch (error) {
572
+ // A process that failed this load may keep failing it after the package is installed (Node keeps what it read of
573
+ // a half-installed package): the pane server answers this code by ending itself, and the next one loads afresh.
574
+ throw new TerminalHostError('pane_runtime_missing', `The pane runtime could not load: ${errorMessage(error)}`);
575
+ }
568
576
  const env = { ...process.env, ...(input.env ?? {}) };
569
577
  const { file, args } = windowsLaunch(input.command.program, input.command.arguments ?? [], env);
570
578
  // The watchdog is running before the pane is, and the pane is on its list the moment it exists.
@@ -974,6 +982,10 @@ export class PaneServerHost {
974
982
  rmSync(staging, { recursive: true, force: true });
975
983
  cpSync(root, join(staging, 'node_modules', '@volter', 'supercode-terminal'), { recursive: true, filter: (path) => !path.slice(root.length).split(/[\\/]/).includes('node_modules') });
976
984
  for (const name of PANE_RUNTIME_PACKAGES) cpSync(packageDir(name), join(staging, 'node_modules', ...name.split('/')), { recursive: true });
985
+ // A copy taken of a half-installed package would stay broken under its version: it is complete only when every
986
+ // runtime package resolves to an entry that exists in it.
987
+ const fromCopy = createRequire(join(staging, 'node_modules', '@volter', 'supercode-terminal', 'package.json'));
988
+ for (const name of PANE_RUNTIME_PACKAGES) if (!existsSync(fromCopy.resolve(name))) throw new Error(`${name} has no entry in the copy`);
977
989
  writeFileSync(join(staging, '.complete'), `${version}\n`);
978
990
  rmSync(target, { recursive: true, force: true });
979
991
  renameSync(staging, target);
@@ -1742,7 +1754,8 @@ export function composerText(kind, screen) {
1742
1754
  if (kind !== 'claude-code' && kind !== 'codex') return null;
1743
1755
  const lines = String(screen ?? '').split('\n').map(styledCells);
1744
1756
  const plain = lines.map((cells) => cells.map((cell) => cell.char).join(''));
1745
- const rule = /^\s*[─━]{4,}\s*$/u;
1757
+ // A session started with `--name` labels the rule above its editor (`──── name ─`); the label does not end the rule.
1758
+ const rule = /^\s*[─━]{4,}(?:[^─━]+[─━]+)?\s*$/u;
1746
1759
  const footer = /^\s*(?:.*\btab to queue (?:a )?message\b.*$|\? for shortcuts\b|⚠\s+\d+\s+warnings?\s+·\s+f2 to view\s*$|[^\n]+ · (?:~(?:[\\/]|(?=\s|$))|[\\/]|[A-Z]:[\\/])[^\n]*$|.*← for agents\s*$|.*\bcontext(?:\s+\d+%)?\s+left\b)/iu;
1747
1760
  const caret = kind === 'codex' ? /^\s*›(?:\s|$)/u : /^\s*[❯>](?:\s|$)/u;
1748
1761
  for (let row = plain.length - 1; row >= 0; row -= 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-terminal",
3
- "version": "0.2.57",
3
+ "version": "0.2.59",
4
4
  "type": "module",
5
5
  "description": "Optional leak-safe terminal and tmux host adapter for Volter Harness frontends",
6
6
  "exports": {
@@ -95,8 +95,12 @@
95
95
  "prepack": "npm run build",
96
96
  "build:substrate": "tsc -p tsconfig.substrate.json"
97
97
  },
98
- "peerDependencies": {
98
+ "dependencies": {
99
99
  "@homebridge/node-pty-prebuilt-multiarch": "^0.13.1 || ^0.14.1",
100
+ "@xterm/addon-serialize": "^0.14.0",
101
+ "@xterm/headless": "^6.0.0"
102
+ },
103
+ "peerDependencies": {
100
104
  "@xterm/addon-fit": "^0.11.0",
101
105
  "@xterm/addon-search": "^0.16.0",
102
106
  "@xterm/addon-web-links": "^0.12.0",
@@ -104,14 +108,9 @@
104
108
  "preact": ">=10.19.0 <11",
105
109
  "react": ">=18 <20",
106
110
  "ws": "^8.21.0",
107
- "@volter/supercode-harness-sdk": "^0.3.18",
108
- "@xterm/headless": "^6.0.0",
109
- "@xterm/addon-serialize": "^0.14.0"
111
+ "@volter/supercode-harness-sdk": "^0.3.18"
110
112
  },
111
113
  "peerDependenciesMeta": {
112
- "@homebridge/node-pty-prebuilt-multiarch": {
113
- "optional": true
114
- },
115
114
  "@xterm/addon-fit": {
116
115
  "optional": true
117
116
  },
@@ -135,24 +134,15 @@
135
134
  },
136
135
  "@volter/supercode-harness-sdk": {
137
136
  "optional": true
138
- },
139
- "@xterm/headless": {
140
- "optional": true
141
- },
142
- "@xterm/addon-serialize": {
143
- "optional": true
144
137
  }
145
138
  },
146
139
  "devDependencies": {
147
- "@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
148
140
  "@types/node": "^22.20.1",
149
141
  "@types/ws": "^8.18.1",
150
142
  "@volter/supercode-harness-sdk": "^0.3.18",
151
143
  "@xterm/addon-fit": "^0.11.0",
152
144
  "@xterm/addon-search": "^0.16.0",
153
- "@xterm/addon-serialize": "^0.14.0",
154
145
  "@xterm/addon-web-links": "^0.12.0",
155
- "@xterm/headless": "^6.0.0",
156
146
  "@xterm/xterm": "^6.0.0",
157
147
  "esbuild": "^0.25.0",
158
148
  "preact": "^10.19.0",
@@ -86,7 +86,14 @@ const server = createServer((connection) => {
86
86
  const { id, op, args = [] } = message;
87
87
  if (op === 'attach') { attach(id, args, message.stream); continue; }
88
88
  if (!OPS.has(op)) { refuse(id, { code: 'unsupported', message: `pane server: no operation ${op}` }); continue; }
89
- Promise.resolve().then(() => host[op](...args)).then((result) => answer(id, result), (error) => refuse(id, error));
89
+ Promise.resolve().then(() => host[op](...args)).then((result) => answer(id, result), async (error) => {
90
+ refuse(id, error);
91
+ // The pane runtime failed to load in this process: with no pane to keep, it ends once the answer is out, so
92
+ // the next request starts a pane server that loads the runtime afresh (one installed since this one started).
93
+ if (error?.code === 'pane_runtime_missing' && !(await host.listSessions().catch(() => [])).length) {
94
+ connection.end(() => { host.dispose(); process.exit(0); });
95
+ }
96
+ });
90
97
  }
91
98
  });
92
99
  // A client that goes (its daemon restarting) takes only its viewers with it: the panes stay.