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.
Files changed (203) hide show
  1. package/dist/chunks/353DMT35.js +3 -0
  2. package/dist/chunks/42KARAL4.js +31 -0
  3. package/dist/chunks/7AUQHB3O.js +10 -0
  4. package/dist/chunks/7SMZ43LI.js +9 -0
  5. package/dist/chunks/AM3OEMFK.js +16 -0
  6. package/dist/chunks/ANNJESOL.js +10 -0
  7. package/dist/chunks/AWB4A65P.js +63 -0
  8. package/dist/chunks/C2UUZY6J.js +2 -0
  9. package/dist/chunks/DGUZCGQN.js +3 -0
  10. package/dist/chunks/H24ZP5D5.js +16 -0
  11. package/dist/chunks/I6NRCYLI.js +3 -0
  12. package/dist/chunks/JJGEV4EZ.js +3 -0
  13. package/dist/chunks/KRSURB5L.js +5 -0
  14. package/dist/chunks/KRVF37OW.js +3 -0
  15. package/dist/chunks/KUDCC7ZO.js +6 -0
  16. package/dist/chunks/M3NRPJK2.js +77 -0
  17. package/dist/chunks/M4AFZHKH.js +3 -0
  18. package/dist/chunks/M52LJEES.js +6 -0
  19. package/dist/chunks/O2LNVFUT.js +3 -0
  20. package/dist/chunks/OJPBI7KB.js +3 -0
  21. package/dist/chunks/OMN6C7J6.js +3 -0
  22. package/dist/chunks/PBMBFWLQ.js +3 -0
  23. package/dist/chunks/PBRQ3HEG.js +3 -0
  24. package/dist/chunks/PFFVGFPL.js +45 -0
  25. package/dist/chunks/QRGNEKME.js +5 -0
  26. package/dist/chunks/TNLCNVA2.js +3 -0
  27. package/dist/chunks/U27JJKJ6.js +3 -0
  28. package/dist/chunks/U6QAYG3E.js +3 -0
  29. package/dist/chunks/UEOQE77N.js +4 -0
  30. package/dist/chunks/WSACSE45.js +4 -0
  31. package/dist/chunks/WY7WHEJ4.js +3 -0
  32. package/dist/chunks/XJKWLK43.js +4 -0
  33. package/dist/chunks/XN6D5S55.js +3 -0
  34. package/dist/chunks/YD3MIP7B.js +5 -0
  35. package/dist/chunks/YXDQ62BB.js +4 -0
  36. package/dist/cli.js +21 -294
  37. package/dist/connector/admin-broker.js +3 -303
  38. package/dist/connector/main.js +20 -18
  39. package/dist/harfbuzz-subset.wasm +0 -0
  40. package/dist/postinstall.js +17 -0
  41. package/herdr-plugin.toml +1 -1
  42. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  43. package/node_modules/herdr-remote-relay/package.json +3 -46
  44. package/package.json +8 -27
  45. package/dist/agent-status.js +0 -130
  46. package/dist/binary-plist.js +0 -157
  47. package/dist/config.js +0 -278
  48. package/dist/connector/admin-broker-setup.js +0 -40
  49. package/dist/connector/admin-shell.js +0 -67
  50. package/dist/connector/admin-tab.js +0 -90
  51. package/dist/connector/adminBrokerEndpoint.js +0 -68
  52. package/dist/connector/agent-watch.js +0 -123
  53. package/dist/connector/elevatedPty.js +0 -141
  54. package/dist/connector/font-server.js +0 -165
  55. package/dist/connector/herdr-prefix-keys.js +0 -28
  56. package/dist/connector/host-connector.js +0 -433
  57. package/dist/connector/host-hello.js +0 -32
  58. package/dist/connector/lock.js +0 -73
  59. package/dist/connector/sessions.js +0 -381
  60. package/dist/connector/update-report.js +0 -70
  61. package/dist/connector/wire.js +0 -42
  62. package/dist/exit-codes.js +0 -13
  63. package/dist/font-subset.js +0 -116
  64. package/dist/herdr-api.js +0 -254
  65. package/dist/herdr-command.js +0 -245
  66. package/dist/herdr-plugin.js +0 -120
  67. package/dist/herdr-server.js +0 -190
  68. package/dist/i18n/en.js +0 -257
  69. package/dist/i18n/index.js +0 -48
  70. package/dist/i18n/zh.js +0 -256
  71. package/dist/keepalive/detached.js +0 -97
  72. package/dist/keepalive/environment.js +0 -44
  73. package/dist/keepalive/index.js +0 -129
  74. package/dist/keepalive/launchd.js +0 -117
  75. package/dist/keepalive/systemd.js +0 -112
  76. package/dist/keepalive/types.js +0 -1
  77. package/dist/keepalive/windows-service-setup.js +0 -138
  78. package/dist/keepalive/windows-service.js +0 -272
  79. package/dist/keepalive/windows-task.js +0 -191
  80. package/dist/lib/process.js +0 -132
  81. package/dist/lifecycle.js +0 -79
  82. package/dist/net-interfaces.js +0 -82
  83. package/dist/node-pty-loader.js +0 -80
  84. package/dist/pasted-files.js +0 -115
  85. package/dist/paths.js +0 -74
  86. package/dist/pty-session.js +0 -133
  87. package/dist/relay-client.js +0 -106
  88. package/dist/relay-urls.js +0 -99
  89. package/dist/runtime.js +0 -107
  90. package/dist/service.js +0 -267
  91. package/dist/settings-model.js +0 -298
  92. package/dist/socket-discovery.js +0 -70
  93. package/dist/supervisor.js +0 -323
  94. package/dist/terminal-font/deps.js +0 -83
  95. package/dist/terminal-font/files.js +0 -357
  96. package/dist/terminal-font/formats.js +0 -355
  97. package/dist/terminal-font/index.js +0 -187
  98. package/dist/terminal-font/readers.js +0 -373
  99. package/dist/terminal-font/terminals.js +0 -147
  100. package/dist/terminal-palette.js +0 -399
  101. package/dist/tui/App.js +0 -199
  102. package/dist/tui/api.js +0 -91
  103. package/dist/tui/components/TextField.js +0 -50
  104. package/dist/tui/components/common.js +0 -80
  105. package/dist/tui/index.js +0 -36
  106. package/dist/tui/mouse/index.js +0 -164
  107. package/dist/tui/mouse/protocol.js +0 -96
  108. package/dist/tui/mouse/source.js +0 -79
  109. package/dist/tui/screens/About.js +0 -172
  110. package/dist/tui/screens/Herdr.js +0 -75
  111. package/dist/tui/screens/Keepalive.js +0 -141
  112. package/dist/tui/screens/Overview.js +0 -36
  113. package/dist/tui/screens/Pair.js +0 -74
  114. package/dist/tui/screens/Relay.js +0 -243
  115. package/dist/tui/screens/Services.js +0 -63
  116. package/dist/tui/screens/Wizard.js +0 -241
  117. package/dist/tui/terminal.js +0 -39
  118. package/dist/tui/theme.js +0 -33
  119. package/dist/update-command.js +0 -118
  120. package/dist/updater/in-place.js +0 -83
  121. package/dist/updater/npm.js +0 -62
  122. package/dist/updater.js +0 -401
  123. package/node_modules/herdr-remote-relay/README.md +0 -76
  124. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  125. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  126. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  127. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  128. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  129. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  130. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  131. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  132. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  133. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  134. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  135. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  136. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  137. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  138. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  139. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  140. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  141. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  142. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  143. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  144. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  145. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  146. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  147. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  148. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  149. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  150. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  151. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  152. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  153. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  154. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  155. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  156. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  157. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  158. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  159. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  160. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  161. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  162. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  163. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  164. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  165. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  166. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  167. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  168. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  169. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  170. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  171. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  172. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  173. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  174. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  175. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  176. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  177. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  178. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  179. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  180. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  181. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  182. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  183. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  184. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  185. package/node_modules/ws/LICENSE +0 -20
  186. package/node_modules/ws/README.md +0 -548
  187. package/node_modules/ws/browser.js +0 -8
  188. package/node_modules/ws/index.js +0 -22
  189. package/node_modules/ws/lib/buffer-util.js +0 -131
  190. package/node_modules/ws/lib/constants.js +0 -19
  191. package/node_modules/ws/lib/event-target.js +0 -292
  192. package/node_modules/ws/lib/extension.js +0 -203
  193. package/node_modules/ws/lib/limiter.js +0 -55
  194. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  195. package/node_modules/ws/lib/receiver.js +0 -743
  196. package/node_modules/ws/lib/sender.js +0 -607
  197. package/node_modules/ws/lib/stream.js +0 -161
  198. package/node_modules/ws/lib/subprotocol.js +0 -62
  199. package/node_modules/ws/lib/validation.js +0 -152
  200. package/node_modules/ws/lib/websocket-server.js +0 -562
  201. package/node_modules/ws/lib/websocket.js +0 -1407
  202. package/node_modules/ws/package.json +0 -74
  203. 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, };
@@ -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 };
@@ -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
- }
@@ -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
- }