herdr-remote 0.2.39 → 0.3.31
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/dist/chunks/353DMT35.js +3 -0
- package/dist/chunks/42KARAL4.js +31 -0
- package/dist/chunks/7AUQHB3O.js +10 -0
- package/dist/chunks/7SMZ43LI.js +9 -0
- package/dist/chunks/AM3OEMFK.js +16 -0
- package/dist/chunks/ANNJESOL.js +10 -0
- package/dist/chunks/AWB4A65P.js +63 -0
- package/dist/chunks/C2UUZY6J.js +2 -0
- package/dist/chunks/DGUZCGQN.js +3 -0
- package/dist/chunks/H24ZP5D5.js +16 -0
- package/dist/chunks/I6NRCYLI.js +3 -0
- package/dist/chunks/JJGEV4EZ.js +3 -0
- package/dist/chunks/KRSURB5L.js +5 -0
- package/dist/chunks/KRVF37OW.js +3 -0
- package/dist/chunks/KUDCC7ZO.js +6 -0
- package/dist/chunks/M3NRPJK2.js +77 -0
- package/dist/chunks/M4AFZHKH.js +3 -0
- package/dist/chunks/M52LJEES.js +6 -0
- package/dist/chunks/O2LNVFUT.js +3 -0
- package/dist/chunks/OJPBI7KB.js +3 -0
- package/dist/chunks/OMN6C7J6.js +3 -0
- package/dist/chunks/PBMBFWLQ.js +3 -0
- package/dist/chunks/PBRQ3HEG.js +3 -0
- package/dist/chunks/PFFVGFPL.js +45 -0
- package/dist/chunks/QRGNEKME.js +5 -0
- package/dist/chunks/TNLCNVA2.js +3 -0
- package/dist/chunks/U27JJKJ6.js +3 -0
- package/dist/chunks/U6QAYG3E.js +3 -0
- package/dist/chunks/UEOQE77N.js +4 -0
- package/dist/chunks/WSACSE45.js +4 -0
- package/dist/chunks/WY7WHEJ4.js +3 -0
- package/dist/chunks/XJKWLK43.js +4 -0
- package/dist/chunks/XN6D5S55.js +3 -0
- package/dist/chunks/YD3MIP7B.js +5 -0
- package/dist/chunks/YXDQ62BB.js +4 -0
- package/dist/cli.js +21 -294
- package/dist/connector/admin-broker.js +3 -303
- package/dist/connector/main.js +20 -18
- package/dist/harfbuzz-subset.wasm +0 -0
- package/dist/postinstall.js +17 -0
- package/herdr-plugin.toml +1 -1
- package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
- package/node_modules/herdr-remote-relay/package.json +3 -46
- package/package.json +8 -27
- package/dist/agent-status.js +0 -130
- package/dist/binary-plist.js +0 -157
- package/dist/config.js +0 -278
- package/dist/connector/admin-broker-setup.js +0 -40
- package/dist/connector/admin-shell.js +0 -67
- package/dist/connector/admin-tab.js +0 -90
- package/dist/connector/adminBrokerEndpoint.js +0 -68
- package/dist/connector/agent-watch.js +0 -123
- package/dist/connector/elevatedPty.js +0 -141
- package/dist/connector/font-server.js +0 -165
- package/dist/connector/herdr-prefix-keys.js +0 -28
- package/dist/connector/host-connector.js +0 -433
- package/dist/connector/host-hello.js +0 -32
- package/dist/connector/lock.js +0 -73
- package/dist/connector/sessions.js +0 -381
- package/dist/connector/update-report.js +0 -70
- package/dist/connector/wire.js +0 -42
- package/dist/exit-codes.js +0 -13
- package/dist/font-subset.js +0 -116
- package/dist/herdr-api.js +0 -254
- package/dist/herdr-command.js +0 -245
- package/dist/herdr-plugin.js +0 -120
- package/dist/herdr-server.js +0 -190
- package/dist/i18n/en.js +0 -257
- package/dist/i18n/index.js +0 -48
- package/dist/i18n/zh.js +0 -256
- package/dist/keepalive/detached.js +0 -97
- package/dist/keepalive/environment.js +0 -44
- package/dist/keepalive/index.js +0 -129
- package/dist/keepalive/launchd.js +0 -117
- package/dist/keepalive/systemd.js +0 -112
- package/dist/keepalive/types.js +0 -1
- package/dist/keepalive/windows-service-setup.js +0 -138
- package/dist/keepalive/windows-service.js +0 -272
- package/dist/keepalive/windows-task.js +0 -191
- package/dist/lib/process.js +0 -132
- package/dist/lifecycle.js +0 -79
- package/dist/net-interfaces.js +0 -82
- package/dist/node-pty-loader.js +0 -80
- package/dist/pasted-files.js +0 -115
- package/dist/paths.js +0 -74
- package/dist/pty-session.js +0 -133
- package/dist/relay-client.js +0 -106
- package/dist/relay-urls.js +0 -99
- package/dist/runtime.js +0 -107
- package/dist/service.js +0 -267
- package/dist/settings-model.js +0 -298
- package/dist/socket-discovery.js +0 -70
- package/dist/supervisor.js +0 -323
- package/dist/terminal-font/deps.js +0 -83
- package/dist/terminal-font/files.js +0 -357
- package/dist/terminal-font/formats.js +0 -355
- package/dist/terminal-font/index.js +0 -187
- package/dist/terminal-font/readers.js +0 -373
- package/dist/terminal-font/terminals.js +0 -147
- package/dist/terminal-palette.js +0 -399
- package/dist/tui/App.js +0 -199
- package/dist/tui/api.js +0 -91
- package/dist/tui/components/TextField.js +0 -50
- package/dist/tui/components/common.js +0 -80
- package/dist/tui/index.js +0 -36
- package/dist/tui/mouse/index.js +0 -164
- package/dist/tui/mouse/protocol.js +0 -96
- package/dist/tui/mouse/source.js +0 -79
- package/dist/tui/screens/About.js +0 -172
- package/dist/tui/screens/Herdr.js +0 -75
- package/dist/tui/screens/Keepalive.js +0 -141
- package/dist/tui/screens/Overview.js +0 -36
- package/dist/tui/screens/Pair.js +0 -74
- package/dist/tui/screens/Relay.js +0 -243
- package/dist/tui/screens/Services.js +0 -63
- package/dist/tui/screens/Wizard.js +0 -241
- package/dist/tui/terminal.js +0 -39
- package/dist/tui/theme.js +0 -33
- package/dist/update-command.js +0 -118
- package/dist/updater/in-place.js +0 -83
- package/dist/updater/npm.js +0 -62
- package/dist/updater.js +0 -401
- package/node_modules/herdr-remote-relay/README.md +0 -76
- package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
- package/node_modules/herdr-remote-relay/config.example.json +0 -18
- package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
- package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
- package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
- package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
- package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
- package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
- package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
- package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
- package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
- package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
- package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
- package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
- package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
- package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
- package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
- package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
- package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
- package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
- package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
- package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
- package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
- package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
- package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
- package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
- package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
- package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
- package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
- package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
- package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
- package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
- package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
- package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
- package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
- package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
- package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
- package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
- package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
- package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
- package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
- package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
- package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
- package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
- package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
- package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
- package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
- package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
- package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
- package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
- package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
- package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
- package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
- package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
- package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
- package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
- package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
- package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
- package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
- package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
- package/node_modules/herdr-remote-relay/dist/state.js +0 -47
- package/node_modules/ws/LICENSE +0 -20
- package/node_modules/ws/README.md +0 -548
- package/node_modules/ws/browser.js +0 -8
- package/node_modules/ws/index.js +0 -22
- package/node_modules/ws/lib/buffer-util.js +0 -131
- package/node_modules/ws/lib/constants.js +0 -19
- package/node_modules/ws/lib/event-target.js +0 -292
- package/node_modules/ws/lib/extension.js +0 -203
- package/node_modules/ws/lib/limiter.js +0 -55
- package/node_modules/ws/lib/permessage-deflate.js +0 -530
- package/node_modules/ws/lib/receiver.js +0 -743
- package/node_modules/ws/lib/sender.js +0 -607
- package/node_modules/ws/lib/stream.js +0 -161
- package/node_modules/ws/lib/subprotocol.js +0 -62
- package/node_modules/ws/lib/validation.js +0 -152
- package/node_modules/ws/lib/websocket-server.js +0 -562
- package/node_modules/ws/lib/websocket.js +0 -1407
- package/node_modules/ws/package.json +0 -74
- package/node_modules/ws/wrapper.mjs +0 -21
package/dist/paths.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// Where herdr-remote keeps its files: the package itself, the configuration
|
|
2
|
-
// directory and the state directory.
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
/**
|
|
7
|
-
* Locate the package directory by walking up to our own package.json.
|
|
8
|
-
*
|
|
9
|
-
* This module runs from src/ under the tests and from dist/ once built.
|
|
10
|
-
* Everything that matters — the plugin manifest, the host connector entry
|
|
11
|
-
* point, the systemd unit's ExecStart — is resolved from this value, so it has
|
|
12
|
-
* to be independent of where the code happens to be loaded from.
|
|
13
|
-
*/
|
|
14
|
-
function findPackageRoot(start) {
|
|
15
|
-
let directory = start;
|
|
16
|
-
for (let depth = 0; depth < 8; depth += 1) {
|
|
17
|
-
const candidate = path.join(directory, 'package.json');
|
|
18
|
-
try {
|
|
19
|
-
if (JSON.parse(fs.readFileSync(candidate, 'utf8')).name === 'herdr-remote')
|
|
20
|
-
return directory;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
// Keep climbing: a missing or unrelated package.json is expected.
|
|
24
|
-
}
|
|
25
|
-
const parent = path.dirname(directory);
|
|
26
|
-
if (parent === directory)
|
|
27
|
-
break;
|
|
28
|
-
directory = parent;
|
|
29
|
-
}
|
|
30
|
-
return path.resolve(start, '..');
|
|
31
|
-
}
|
|
32
|
-
const PACKAGE_ROOT = findPackageRoot(import.meta.dirname);
|
|
33
|
-
/**
|
|
34
|
-
* The canonical configuration directory.
|
|
35
|
-
*
|
|
36
|
-
* Deliberately NOT $HERDR_PLUGIN_CONFIG_DIR: Herdr sets that variable only when
|
|
37
|
-
* it launches the plugin itself, so honouring it would give "herdr-remote" run
|
|
38
|
-
* from a shell and the same tool run from a Herdr pane two different config
|
|
39
|
-
* files. One path, one config; `migrateLegacyConfig()` imports the old one.
|
|
40
|
-
* On Windows, configuration roams under `APPDATA` while state and logs stay
|
|
41
|
-
* local under `LOCALAPPDATA`, following Windows and Herdr's directory layout.
|
|
42
|
-
*/
|
|
43
|
-
function configDir(env = process.env, platform = process.platform) {
|
|
44
|
-
if (env.HERDR_REMOTE_CONFIG_DIR)
|
|
45
|
-
return env.HERDR_REMOTE_CONFIG_DIR;
|
|
46
|
-
if (platform === 'win32') {
|
|
47
|
-
return path.join(env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'herdr-remote');
|
|
48
|
-
}
|
|
49
|
-
return path.join(os.homedir(), '.config', 'herdr-remote');
|
|
50
|
-
}
|
|
51
|
-
function stateDir(env = process.env, platform = process.platform) {
|
|
52
|
-
if (env.HERDR_REMOTE_STATE_DIR)
|
|
53
|
-
return env.HERDR_REMOTE_STATE_DIR;
|
|
54
|
-
if (platform === 'win32') {
|
|
55
|
-
return path.join(env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'herdr-remote');
|
|
56
|
-
}
|
|
57
|
-
return path.join(os.homedir(), '.local', 'state', 'herdr-remote');
|
|
58
|
-
}
|
|
59
|
-
function configPath() {
|
|
60
|
-
return path.join(configDir(), 'config.json');
|
|
61
|
-
}
|
|
62
|
-
function runtimeStatePath() {
|
|
63
|
-
return path.join(stateDir(), 'runtime.json');
|
|
64
|
-
}
|
|
65
|
-
/** Where a service (relay, host, supervisor) writes its output. */
|
|
66
|
-
function logPath(name) {
|
|
67
|
-
return path.join(stateDir(), `${name}.log`);
|
|
68
|
-
}
|
|
69
|
-
function legacyConfigPath() {
|
|
70
|
-
return process.env.HERDR_PLUGIN_CONFIG_DIR
|
|
71
|
-
? path.join(process.env.HERDR_PLUGIN_CONFIG_DIR, 'config.json')
|
|
72
|
-
: null;
|
|
73
|
-
}
|
|
74
|
-
export { PACKAGE_ROOT, configDir, stateDir, configPath, runtimeStatePath, logPath, legacyConfigPath, };
|
package/dist/pty-session.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import { nodePty } from './node-pty-loader.js';
|
|
3
|
-
/**
|
|
4
|
-
* Variables that name the terminal this process was started from. The Herdr
|
|
5
|
-
* client's outer terminal is the browser's xterm.js, and from Herdr 0.9.2 the
|
|
6
|
-
* client reads these to pick image transports: one that believes it runs in
|
|
7
|
-
* Ghostty, kitty or WezTerm hands images over as local file paths no browser
|
|
8
|
-
* can open. Herdr strips the same list from its own panes.
|
|
9
|
-
*/
|
|
10
|
-
const OUTER_TERMINAL_ENV = [
|
|
11
|
-
'TERM_PROGRAM',
|
|
12
|
-
'TERM_PROGRAM_VERSION',
|
|
13
|
-
'ITERM_SESSION_ID',
|
|
14
|
-
'LC_TERMINAL',
|
|
15
|
-
'LC_TERMINAL_VERSION',
|
|
16
|
-
'WEZTERM_PANE',
|
|
17
|
-
'KITTY_WINDOW_ID',
|
|
18
|
-
'WT_SESSION',
|
|
19
|
-
'TMUX',
|
|
20
|
-
'TMUX_PANE',
|
|
21
|
-
'STY',
|
|
22
|
-
'ZELLIJ',
|
|
23
|
-
'ZELLIJ_SESSION_NAME',
|
|
24
|
-
'ZELLIJ_PANE_ID',
|
|
25
|
-
];
|
|
26
|
-
class PtySession {
|
|
27
|
-
static DEFAULT_COLS = 100;
|
|
28
|
-
static DEFAULT_ROWS = 30;
|
|
29
|
-
static MIN_DIMENSION = 2;
|
|
30
|
-
static MAX_DIMENSION = 500;
|
|
31
|
-
command;
|
|
32
|
-
args;
|
|
33
|
-
cwd;
|
|
34
|
-
socketPath;
|
|
35
|
-
platform;
|
|
36
|
-
fastWindowsPty;
|
|
37
|
-
spawnPty;
|
|
38
|
-
terminal;
|
|
39
|
-
startedAt;
|
|
40
|
-
constructor({ command, args = [], cwd = os.homedir(), socketPath, platform = process.platform, spawn: spawnPty = nodePty().spawn, fastWindowsPty = false, } = {}) {
|
|
41
|
-
if (typeof command !== 'string' || command.length === 0)
|
|
42
|
-
throw new TypeError('command must be a non-empty string');
|
|
43
|
-
if (!Array.isArray(args) || !args.every((arg) => typeof arg === 'string'))
|
|
44
|
-
throw new TypeError('args must be strings');
|
|
45
|
-
this.command = command;
|
|
46
|
-
this.args = args;
|
|
47
|
-
this.cwd = cwd;
|
|
48
|
-
this.socketPath = socketPath;
|
|
49
|
-
this.platform = platform;
|
|
50
|
-
this.fastWindowsPty = fastWindowsPty;
|
|
51
|
-
this.spawnPty = spawnPty;
|
|
52
|
-
this.terminal = null;
|
|
53
|
-
this.startedAt = null;
|
|
54
|
-
}
|
|
55
|
-
static childEnv(socketPath, source = process.env) {
|
|
56
|
-
const env = { ...source };
|
|
57
|
-
for (const key of Object.keys(env)) {
|
|
58
|
-
if (key.startsWith('HERDR_'))
|
|
59
|
-
delete env[key];
|
|
60
|
-
}
|
|
61
|
-
for (const key of OUTER_TERMINAL_ENV)
|
|
62
|
-
delete env[key];
|
|
63
|
-
if (socketPath)
|
|
64
|
-
env.HERDR_SOCKET_PATH = socketPath;
|
|
65
|
-
env.TERM = 'xterm-256color';
|
|
66
|
-
env.COLORTERM = 'truecolor';
|
|
67
|
-
return env;
|
|
68
|
-
}
|
|
69
|
-
static clampDimension(value, fallback) {
|
|
70
|
-
const numeric = Number(value);
|
|
71
|
-
if (!Number.isInteger(numeric))
|
|
72
|
-
return fallback;
|
|
73
|
-
return Math.min(PtySession.MAX_DIMENSION, Math.max(PtySession.MIN_DIMENSION, numeric));
|
|
74
|
-
}
|
|
75
|
-
start({ cols, rows, onData, onExit }) {
|
|
76
|
-
if (this.terminal)
|
|
77
|
-
throw new Error('PTY session already started');
|
|
78
|
-
// The Herdr UI needs the bundled ConPTY's Kitty and mouse reporting.
|
|
79
|
-
this.terminal = this.spawnPty(this.command, this.args, {
|
|
80
|
-
name: 'xterm-256color',
|
|
81
|
-
cols: PtySession.clampDimension(cols, PtySession.DEFAULT_COLS),
|
|
82
|
-
rows: PtySession.clampDimension(rows, PtySession.DEFAULT_ROWS),
|
|
83
|
-
cwd: this.cwd,
|
|
84
|
-
env: PtySession.childEnv(this.socketPath),
|
|
85
|
-
...(this.platform === 'win32'
|
|
86
|
-
? this.fastWindowsPty
|
|
87
|
-
? { useConpty: false }
|
|
88
|
-
: { useConptyDll: true }
|
|
89
|
-
: {}),
|
|
90
|
-
});
|
|
91
|
-
this.startedAt = new Date().toISOString();
|
|
92
|
-
const terminal = this.terminal;
|
|
93
|
-
if (onData)
|
|
94
|
-
terminal.onData(onData);
|
|
95
|
-
if (onExit) {
|
|
96
|
-
terminal.onExit((event) => {
|
|
97
|
-
this.terminal = null;
|
|
98
|
-
onExit(event);
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
return this;
|
|
102
|
-
}
|
|
103
|
-
write(data) {
|
|
104
|
-
if (!this.terminal)
|
|
105
|
-
return;
|
|
106
|
-
this.terminal.write(Buffer.isBuffer(data) ? data.toString('utf8') : String(data));
|
|
107
|
-
}
|
|
108
|
-
resize(cols, rows) {
|
|
109
|
-
if (!this.terminal)
|
|
110
|
-
return;
|
|
111
|
-
this.terminal.resize(PtySession.clampDimension(cols, PtySession.DEFAULT_COLS), PtySession.clampDimension(rows, PtySession.DEFAULT_ROWS));
|
|
112
|
-
}
|
|
113
|
-
info() {
|
|
114
|
-
return {
|
|
115
|
-
pid: this.terminal?.pid || null,
|
|
116
|
-
command: this.command,
|
|
117
|
-
cols: this.terminal?.cols || null,
|
|
118
|
-
rows: this.terminal?.rows || null,
|
|
119
|
-
cwd: this.cwd,
|
|
120
|
-
createdAt: this.startedAt,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
kill() {
|
|
124
|
-
try {
|
|
125
|
-
this.terminal?.kill();
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
// The process has already exited.
|
|
129
|
-
}
|
|
130
|
-
this.terminal = null;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
export { PtySession };
|
package/dist/relay-client.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
// Talking to a relay's HTTP API from this workstation: health, status, pairing.
|
|
2
|
-
import http from 'node:http';
|
|
3
|
-
import https from 'node:https';
|
|
4
|
-
import { resolveAdminOrigin } from './relay-urls.js';
|
|
5
|
-
import { ensureRuntime } from './runtime.js';
|
|
6
|
-
/**
|
|
7
|
-
* Minimal JSON client. Picks http or https from the URL: a self-hosted relay is
|
|
8
|
-
* reached over https, while the local relay is plain http on loopback.
|
|
9
|
-
*/
|
|
10
|
-
export function requestJson(urlString, options = {}) {
|
|
11
|
-
return new Promise((resolve, reject) => {
|
|
12
|
-
let url;
|
|
13
|
-
try {
|
|
14
|
-
url = new URL(urlString);
|
|
15
|
-
}
|
|
16
|
-
catch (error) {
|
|
17
|
-
reject(error);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const transport = url.protocol === 'https:' ? https : http;
|
|
21
|
-
const request = transport.request(url, {
|
|
22
|
-
method: options.method || 'GET',
|
|
23
|
-
headers: options.headers || {},
|
|
24
|
-
timeout: options.timeout || 1500,
|
|
25
|
-
}, (response) => {
|
|
26
|
-
const chunks = [];
|
|
27
|
-
response.on('data', (chunk) => chunks.push(chunk));
|
|
28
|
-
response.on('end', () => {
|
|
29
|
-
const text = Buffer.concat(chunks).toString('utf8');
|
|
30
|
-
let body;
|
|
31
|
-
try {
|
|
32
|
-
body = JSON.parse(text);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
body = { raw: text };
|
|
36
|
-
}
|
|
37
|
-
const status = response.statusCode || 0;
|
|
38
|
-
if (status >= 400) {
|
|
39
|
-
const error = new Error(typeof body.message === 'string' ? body.message : `HTTP ${status}`);
|
|
40
|
-
error.statusCode = status;
|
|
41
|
-
error.body = body;
|
|
42
|
-
reject(error);
|
|
43
|
-
}
|
|
44
|
-
else
|
|
45
|
-
resolve(body);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
request.on('timeout', () => request.destroy(new Error('request timed out')));
|
|
49
|
-
request.on('error', reject);
|
|
50
|
-
if (options.body)
|
|
51
|
-
request.write(options.body);
|
|
52
|
-
request.end();
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
export function healthUrl(config) {
|
|
56
|
-
return `${resolveAdminOrigin(config)}/healthz`;
|
|
57
|
-
}
|
|
58
|
-
/** This workstation's own credentials, which scope a relay's answers to it. */
|
|
59
|
-
export function hostHeaders() {
|
|
60
|
-
const state = ensureRuntime();
|
|
61
|
-
return { 'X-Herdr-Host-Id': state.hostId, 'X-Herdr-Host-Token': state.hostToken };
|
|
62
|
-
}
|
|
63
|
-
export async function waitForRelay(config, { attempts = 20, delayMs = 100 } = {}) {
|
|
64
|
-
let lastError;
|
|
65
|
-
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
66
|
-
try {
|
|
67
|
-
return await requestJson(healthUrl(config), { timeout: 800 });
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
lastError = error;
|
|
71
|
-
}
|
|
72
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
73
|
-
}
|
|
74
|
-
throw lastError || new Error('relay did not become ready');
|
|
75
|
-
}
|
|
76
|
-
export async function waitForHost(config, { attempts = 30, delayMs = 100 } = {}) {
|
|
77
|
-
const headers = hostHeaders();
|
|
78
|
-
const hostId = headers['X-Herdr-Host-Id'];
|
|
79
|
-
const statusEndpoint = `${resolveAdminOrigin(config)}/api/status`;
|
|
80
|
-
let lastStatus = null;
|
|
81
|
-
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
82
|
-
try {
|
|
83
|
-
lastStatus = await requestJson(statusEndpoint, {
|
|
84
|
-
timeout: 800,
|
|
85
|
-
headers,
|
|
86
|
-
});
|
|
87
|
-
if (lastStatus.hosts?.some((host) => host.id === hostId))
|
|
88
|
-
return lastStatus;
|
|
89
|
-
}
|
|
90
|
-
catch (error) {
|
|
91
|
-
lastStatus = { ok: false, message: error.message };
|
|
92
|
-
}
|
|
93
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
94
|
-
}
|
|
95
|
-
const error = new Error(lastStatus?.message || 'the Herdr host connector did not register with the relay');
|
|
96
|
-
error.health = lastStatus;
|
|
97
|
-
throw error;
|
|
98
|
-
}
|
|
99
|
-
export function extractPairingCode(response) {
|
|
100
|
-
const answer = response;
|
|
101
|
-
const code = answer?.code || answer?.pairCode;
|
|
102
|
-
if (typeof code !== 'string' || code.length === 0) {
|
|
103
|
-
throw new Error('the relay response did not contain a valid pairing code');
|
|
104
|
-
}
|
|
105
|
-
return code;
|
|
106
|
-
}
|
package/dist/relay-urls.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// The addresses a configuration implies: where the local relay binds, the URL
|
|
2
|
-
// a browser opens, and the relay URLs the host connector and the CLI dial.
|
|
3
|
-
import { WS_HOST_PATH } from 'herdr-remote-relay/protocol';
|
|
4
|
-
function isLoopbackHost(value) {
|
|
5
|
-
return value === '127.0.0.1' || value === 'localhost' || value === '::1' || value === '[::1]';
|
|
6
|
-
}
|
|
7
|
-
function isUnspecifiedAddress(value) {
|
|
8
|
-
const address = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
9
|
-
return address === '0.0.0.0' || address === '::' || address === '[::]';
|
|
10
|
-
}
|
|
11
|
-
/** An unspecified host is valid for a server bind, but not for a browser URL. */
|
|
12
|
-
function isUnspecifiedHost(value) {
|
|
13
|
-
try {
|
|
14
|
-
const hostname = new URL(value).hostname.toLowerCase();
|
|
15
|
-
return hostname === '0.0.0.0' || hostname === '::' || hostname === '[::]';
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function normalizeUrl(value) {
|
|
22
|
-
return typeof value === 'string' ? value.trim().replace(/\/+$/, '') : '';
|
|
23
|
-
}
|
|
24
|
-
/** Whether this machine runs its own relay process. */
|
|
25
|
-
function runsLocalRelay(config) {
|
|
26
|
-
return config.relay.mode !== 'remote';
|
|
27
|
-
}
|
|
28
|
-
/** Address the local relay binds to for the configured mode. */
|
|
29
|
-
function bindAddress(config) {
|
|
30
|
-
return config.relay.mode === 'lan' ? '0.0.0.0' : '127.0.0.1';
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Host name to put into pairing URLs. For "lan" this is the interface address a
|
|
34
|
-
* phone can actually reach; `lanHost` is the user's pick from the TUI, and the
|
|
35
|
-
* fallback keeps things working if that interface disappeared.
|
|
36
|
-
*/
|
|
37
|
-
function advertisedHost(config, fallbackLanHost = null) {
|
|
38
|
-
if (config.relay.mode === 'lan') {
|
|
39
|
-
const configured = config.relay.lanHost;
|
|
40
|
-
if (configured && !isLoopbackHost(configured) && !isUnspecifiedAddress(configured))
|
|
41
|
-
return configured;
|
|
42
|
-
if (fallbackLanHost &&
|
|
43
|
-
!isLoopbackHost(fallbackLanHost) &&
|
|
44
|
-
!isUnspecifiedAddress(fallbackLanHost)) {
|
|
45
|
-
return fallbackLanHost;
|
|
46
|
-
}
|
|
47
|
-
return '127.0.0.1';
|
|
48
|
-
}
|
|
49
|
-
return '127.0.0.1';
|
|
50
|
-
}
|
|
51
|
-
/** The URL a browser opens. */
|
|
52
|
-
function resolvePublicUrl(config, fallbackLanHost = null) {
|
|
53
|
-
if (config.relay.publicUrl)
|
|
54
|
-
return config.relay.publicUrl;
|
|
55
|
-
if (config.relay.mode === 'remote')
|
|
56
|
-
return httpOrigin(config.relay.remoteUrl);
|
|
57
|
-
return `http://${advertisedHost(config, fallbackLanHost)}:${config.relay.port}`;
|
|
58
|
-
}
|
|
59
|
-
/** Base HTTP origin used for relay admin calls (pairing, health). */
|
|
60
|
-
function resolveAdminOrigin(config) {
|
|
61
|
-
if (config.relay.mode === 'remote')
|
|
62
|
-
return httpOrigin(config.relay.remoteUrl);
|
|
63
|
-
return `http://127.0.0.1:${config.relay.port}`;
|
|
64
|
-
}
|
|
65
|
-
/** WebSocket URL the host connector dials. */
|
|
66
|
-
function resolveHostRelayUrl(config) {
|
|
67
|
-
const base = config.relay.mode === 'remote' ? config.relay.remoteUrl : `ws://127.0.0.1:${config.relay.port}`;
|
|
68
|
-
return hostWebSocketUrl(base);
|
|
69
|
-
}
|
|
70
|
-
function httpOrigin(value) {
|
|
71
|
-
try {
|
|
72
|
-
const url = new URL(value);
|
|
73
|
-
if (url.protocol === 'ws:')
|
|
74
|
-
url.protocol = 'http:';
|
|
75
|
-
if (url.protocol === 'wss:')
|
|
76
|
-
url.protocol = 'https:';
|
|
77
|
-
const pathname = url.pathname.replace(/\/+$/, '');
|
|
78
|
-
return `${url.origin}${pathname}`;
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
return normalizeUrl(value);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function hostWebSocketUrl(base) {
|
|
85
|
-
const url = new URL(base);
|
|
86
|
-
if (url.protocol === 'http:')
|
|
87
|
-
url.protocol = 'ws:';
|
|
88
|
-
if (url.protocol === 'https:')
|
|
89
|
-
url.protocol = 'wss:';
|
|
90
|
-
const pathname = url.pathname.replace(/\/+$/, '');
|
|
91
|
-
if (!pathname || pathname === '/') {
|
|
92
|
-
url.pathname = WS_HOST_PATH;
|
|
93
|
-
}
|
|
94
|
-
else if (!pathname.endsWith(WS_HOST_PATH)) {
|
|
95
|
-
url.pathname = `${pathname}${WS_HOST_PATH}`;
|
|
96
|
-
}
|
|
97
|
-
return url.toString();
|
|
98
|
-
}
|
|
99
|
-
export { isLoopbackHost, isUnspecifiedAddress, isUnspecifiedHost, normalizeUrl, runsLocalRelay, bindAddress, advertisedHost, resolvePublicUrl, resolveAdminOrigin, resolveHostRelayUrl, hostWebSocketUrl, };
|
package/dist/runtime.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// runtime.json: this workstation's relay credentials, the processes started
|
|
2
|
-
// for it, and facts other modules remember there (terminal palette and font).
|
|
3
|
-
// Every read and write of that file goes through here.
|
|
4
|
-
import { ensureDir, randomToken, readJson, writeJsonAtomic } from 'herdr-remote-relay/state';
|
|
5
|
-
import { processStart, stillRunning } from './lib/process.js';
|
|
6
|
-
import { configDir, runtimeStatePath, stateDir } from './paths.js';
|
|
7
|
-
const RUNTIME_VERSION = 2;
|
|
8
|
-
export function readRuntime() {
|
|
9
|
-
const state = readJson(runtimeStatePath(), {});
|
|
10
|
-
return state && typeof state === 'object' ? state : {};
|
|
11
|
-
}
|
|
12
|
-
export function writeRuntime(state) {
|
|
13
|
-
writeJsonAtomic(runtimeStatePath(), state);
|
|
14
|
-
}
|
|
15
|
-
/** Read, change and write back in one step, so no field another writer set is lost. */
|
|
16
|
-
export function updateRuntime(change) {
|
|
17
|
-
const state = readRuntime();
|
|
18
|
-
change(state);
|
|
19
|
-
writeRuntime(state);
|
|
20
|
-
return state;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Load (creating if needed) the long-lived identifiers and secrets this
|
|
24
|
-
* workstation uses. These live in the state directory rather than config.json
|
|
25
|
-
* because they are credentials: writeJsonAtomic stores them mode 0600.
|
|
26
|
-
*/
|
|
27
|
-
export function ensureRuntime() {
|
|
28
|
-
ensureDir(configDir());
|
|
29
|
-
ensureDir(stateDir());
|
|
30
|
-
return updateRuntime((state) => {
|
|
31
|
-
if (!state.version)
|
|
32
|
-
state.version = RUNTIME_VERSION;
|
|
33
|
-
if (!state.hostId)
|
|
34
|
-
state.hostId = `host-${randomToken(9)}`;
|
|
35
|
-
if (!state.hostToken)
|
|
36
|
-
state.hostToken = randomToken(32);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Set the password used to join a relay. An empty value means "no password",
|
|
41
|
-
* which is what a public relay expects.
|
|
42
|
-
*
|
|
43
|
-
* It lives in the state file rather than config.json because it is a secret:
|
|
44
|
-
* writeJsonAtomic stores that file mode 0600.
|
|
45
|
-
*/
|
|
46
|
-
export function setRelayPassword(password) {
|
|
47
|
-
const state = ensureRuntime();
|
|
48
|
-
state.relayPassword = typeof password === 'string' ? password.trim() : '';
|
|
49
|
-
writeRuntime(state);
|
|
50
|
-
return state;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Issue this workstation a new identity on the relay.
|
|
54
|
-
*
|
|
55
|
-
* The host token is what proves ownership of this workstation, so replacing it
|
|
56
|
-
* means the relay no longer recognises the old one — useful if it leaked, but
|
|
57
|
-
* it also orphans the previous record on a relay that already enrolled it.
|
|
58
|
-
*/
|
|
59
|
-
export function regenerateHostIdentity() {
|
|
60
|
-
const state = ensureRuntime();
|
|
61
|
-
state.hostId = `host-${randomToken(9)}`;
|
|
62
|
-
state.hostToken = randomToken(32);
|
|
63
|
-
writeRuntime(state);
|
|
64
|
-
return state;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Remember a process we started, in a list that survives being overwritten.
|
|
68
|
-
*
|
|
69
|
-
* `relayPid`/`hostPid` alone were not enough: the supervisor rewrote them with
|
|
70
|
-
* its own children, which orphaned anything an earlier detached start had
|
|
71
|
-
* spawned. Those orphans kept the port and the host session, so the relay could
|
|
72
|
-
* never bind and "stop" had nothing left to kill. The ledger is append-only
|
|
73
|
-
* (minus dead entries) precisely so no writer can lose another writer's pids.
|
|
74
|
-
*/
|
|
75
|
-
export function recordManagedPid(state, name, pid) {
|
|
76
|
-
const existing = Array.isArray(state.managedPids) ? state.managedPids.filter(Boolean) : [];
|
|
77
|
-
const kept = stillRunning(existing.filter((entry) => entry.pid !== pid));
|
|
78
|
-
// Only track something that is actually running, and together with its start
|
|
79
|
-
// token: the ledger outlives a reboot, and a bare pid it kept would then name
|
|
80
|
-
// whichever unrelated process got that number next — which we would signal.
|
|
81
|
-
const start = Number.isInteger(pid) ? processStart(pid) : null;
|
|
82
|
-
if (start) {
|
|
83
|
-
kept.push({
|
|
84
|
-
name,
|
|
85
|
-
pid: pid,
|
|
86
|
-
processStart: start,
|
|
87
|
-
startedAt: new Date().toISOString(),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
state.managedPids = kept;
|
|
91
|
-
return state;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Every process we started that is still running, and still that process.
|
|
95
|
-
*
|
|
96
|
-
* Only the ledger counts. `supervisorPid`, `relayPid` and `hostPid` are bare
|
|
97
|
-
* numbers that survive a reboot, and trusting them would signal whichever
|
|
98
|
-
* unrelated process inherited one.
|
|
99
|
-
*/
|
|
100
|
-
export function managedPids(state = readRuntime()) {
|
|
101
|
-
const unique = new Map();
|
|
102
|
-
for (const entry of state.managedPids || []) {
|
|
103
|
-
if (entry && !unique.has(entry.pid))
|
|
104
|
-
unique.set(entry.pid, entry);
|
|
105
|
-
}
|
|
106
|
-
return stillRunning([...unique.values()]);
|
|
107
|
-
}
|