@volter/supercode-terminal 0.2.38 → 0.2.40
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 +53 -3
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFile, spawn, spawnSync } from 'node:child_process';
|
|
2
2
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
3
|
+
import { cpSync, existsSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
4
5
|
import { connect as netConnect } from 'node:net';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
import { createServer as defaultCreateServer } from 'node:http';
|
|
@@ -810,6 +811,8 @@ export class PtyTerminalHost {
|
|
|
810
811
|
// The launch directory: another process's current directory is not readable here as tmux reads it.
|
|
811
812
|
cwd: entry.cwd,
|
|
812
813
|
dead: entry.exit !== null,
|
|
814
|
+
// How a dead pane's program ended (a failed one is kept so its last screen can be read).
|
|
815
|
+
exitCode: entry.exit ? entry.exit.exitCode ?? null : null,
|
|
813
816
|
id: entry.id,
|
|
814
817
|
label: entry.name,
|
|
815
818
|
owned: true,
|
|
@@ -902,6 +905,20 @@ export class PtyTerminalHost {
|
|
|
902
905
|
* connection; the panes and their programs go on, and the next daemon finds them there. The server starts on first
|
|
903
906
|
* use, detached, and leaves a minute after it holds no pane and no client.
|
|
904
907
|
*/
|
|
908
|
+
/** What the pane server loads beyond this package (PtyTerminalHost's dynamic imports). */
|
|
909
|
+
const PANE_RUNTIME_PACKAGES = ['@homebridge/node-pty-prebuilt-multiarch', '@xterm/headless', '@xterm/addon-serialize'];
|
|
910
|
+
|
|
911
|
+
/** A dependency's package directory: the nearest one above its entry named for it (ESM resolution, as it is loaded). */
|
|
912
|
+
function packageDir(name) {
|
|
913
|
+
let dir = dirname(fileURLToPath(import.meta.resolve(name)));
|
|
914
|
+
for (;;) {
|
|
915
|
+
try { if (JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).name === name) return dir; } catch { /* not a package root */ }
|
|
916
|
+
const up = dirname(dir);
|
|
917
|
+
if (up === dir) throw new Error(`no package directory for ${name}`);
|
|
918
|
+
dir = up;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
905
922
|
export class PaneServerHost {
|
|
906
923
|
#connection = null;
|
|
907
924
|
#connecting = null;
|
|
@@ -910,11 +927,44 @@ export class PaneServerHost {
|
|
|
910
927
|
#streams = new Map();
|
|
911
928
|
#pipe;
|
|
912
929
|
#serverScript;
|
|
930
|
+
#runtimeDir;
|
|
913
931
|
|
|
932
|
+
/**
|
|
933
|
+
* `runtimeDir`: where the pane server runs from, a copy of this package and its runtime dependencies per version
|
|
934
|
+
* (`<runtimeDir>/<version>`). A running pane server holds node-pty's native module open, and Windows cannot replace
|
|
935
|
+
* a file in use, so a server started from the installed package would stop the next install of it; started from its
|
|
936
|
+
* own copy, it leaves the installed package free to change under it. Without one, it runs from the package.
|
|
937
|
+
*/
|
|
914
938
|
constructor(options = {}) {
|
|
915
939
|
if (!options.pipe) throw new TypeError('PaneServerHost requires a pipe name');
|
|
916
940
|
this.#pipe = options.pipe;
|
|
917
|
-
this.#serverScript = options.serverScript ??
|
|
941
|
+
this.#serverScript = options.serverScript ?? null;
|
|
942
|
+
this.#runtimeDir = options.runtimeDir ?? null;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/** The pane server's script: its own versioned copy under runtimeDir, made once, else the package's. */
|
|
946
|
+
#script() {
|
|
947
|
+
if (this.#serverScript) return this.#serverScript;
|
|
948
|
+
const installed = fileURLToPath(new URL('./substrate/pane-server.mjs', import.meta.url));
|
|
949
|
+
if (!this.#runtimeDir) return installed;
|
|
950
|
+
const root = fileURLToPath(new URL('.', import.meta.url));
|
|
951
|
+
const version = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version;
|
|
952
|
+
const target = join(this.#runtimeDir, version);
|
|
953
|
+
const script = join(target, 'node_modules', '@volter', 'supercode-terminal', 'substrate', 'pane-server.mjs');
|
|
954
|
+
if (existsSync(join(target, '.complete'))) return script;
|
|
955
|
+
try {
|
|
956
|
+
const staging = `${target}.${process.pid}.staging`;
|
|
957
|
+
rmSync(staging, { recursive: true, force: true });
|
|
958
|
+
cpSync(root, join(staging, 'node_modules', '@volter', 'supercode-terminal'), { recursive: true, filter: (path) => !path.slice(root.length).split(/[\\/]/).includes('node_modules') });
|
|
959
|
+
for (const name of PANE_RUNTIME_PACKAGES) cpSync(packageDir(name), join(staging, 'node_modules', ...name.split('/')), { recursive: true });
|
|
960
|
+
writeFileSync(join(staging, '.complete'), `${version}\n`);
|
|
961
|
+
rmSync(target, { recursive: true, force: true });
|
|
962
|
+
renameSync(staging, target);
|
|
963
|
+
return script;
|
|
964
|
+
} catch {
|
|
965
|
+
// A copy that cannot be made (a full disk, a racing daemon) leaves the package's own script, as before.
|
|
966
|
+
return existsSync(join(target, '.complete')) ? script : installed;
|
|
967
|
+
}
|
|
918
968
|
}
|
|
919
969
|
|
|
920
970
|
listSessions() { return this.#request('listSessions', []); }
|
|
@@ -996,7 +1046,7 @@ export class PaneServerHost {
|
|
|
996
1046
|
} catch (error) {
|
|
997
1047
|
// No server on the pipe yet: start one, detached from this daemon, and try again as it comes up.
|
|
998
1048
|
if (attempt === 0 && (error.code === 'ENOENT' || error.code === 'ECONNREFUSED')) {
|
|
999
|
-
const child = spawn(process.execPath, [this.#
|
|
1049
|
+
const child = spawn(process.execPath, [this.#script(), this.#pipe], { detached: true, stdio: 'ignore', windowsHide: true, env: process.env });
|
|
1000
1050
|
child.on('error', () => {});
|
|
1001
1051
|
child.unref();
|
|
1002
1052
|
}
|