herdr-remote 0.2.39 → 0.3.32
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/README.md +4 -2
- package/README.zh-CN.md +2 -2
- package/dist/chunks/3FQAXVGC.js +3 -0
- package/dist/chunks/42KARAL4.js +31 -0
- package/dist/chunks/44IGVPMA.js +5 -0
- package/dist/chunks/5LZFJ6UZ.js +3 -0
- package/dist/chunks/7SMZ43LI.js +9 -0
- package/dist/chunks/AX4EPJCP.js +4 -0
- package/dist/chunks/B76DFI4H.js +16 -0
- package/dist/chunks/BYGDPZZD.js +3 -0
- package/dist/chunks/C2UUZY6J.js +2 -0
- package/dist/chunks/CVJ4HBMP.js +3 -0
- package/dist/chunks/DGUZCGQN.js +3 -0
- package/dist/chunks/FONUO5O5.js +4 -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/K5RFPHSF.js +5 -0
- package/dist/chunks/KGOQKOHH.js +5 -0
- package/dist/chunks/KRVF37OW.js +3 -0
- package/dist/chunks/KUDCC7ZO.js +6 -0
- package/dist/chunks/LPR662JH.js +77 -0
- package/dist/chunks/LY2ZGCXN.js +4 -0
- package/dist/chunks/M52LJEES.js +6 -0
- package/dist/chunks/MCUVFSSK.js +3 -0
- package/dist/chunks/MEFDV375.js +10 -0
- package/dist/chunks/MKAEXKXG.js +10 -0
- package/dist/chunks/O2LNVFUT.js +3 -0
- package/dist/chunks/PBMBFWLQ.js +3 -0
- package/dist/chunks/PBRQ3HEG.js +3 -0
- package/dist/chunks/QCSILMFB.js +3 -0
- package/dist/chunks/QRGNEKME.js +5 -0
- package/dist/chunks/RMHH3IAX.js +63 -0
- package/dist/chunks/TNLCNVA2.js +3 -0
- package/dist/chunks/U6QAYG3E.js +3 -0
- package/dist/chunks/VD54DF7R.js +45 -0
- package/dist/chunks/WY7WHEJ4.js +3 -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/supervisor.js
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import { loadConfig } from './config.js';
|
|
4
|
-
import { logPath, stateDir } from './paths.js';
|
|
5
|
-
import { ensureDir } from 'herdr-remote-relay/state';
|
|
6
|
-
import { ensureRuntime, managedPids, recordManagedPid, updateRuntime, } from './runtime.js';
|
|
7
|
-
import { baseEnvironment, serviceSpecs } from './service.js';
|
|
8
|
-
import { EXIT_REPLACED, EXIT_AUTH_FAILED } from './exit-codes.js';
|
|
9
|
-
import { pidAlive, stillRunning } from './lib/process.js';
|
|
10
|
-
const MIN_BACKOFF_MS = 500;
|
|
11
|
-
const MAX_BACKOFF_MS = 30_000;
|
|
12
|
-
// A child that stayed up this long is considered healthy, so the next crash
|
|
13
|
-
// starts backing off from scratch instead of inheriting an old penalty.
|
|
14
|
-
const HEALTHY_UPTIME_MS = 30_000;
|
|
15
|
-
/**
|
|
16
|
-
* Runs the relay and host connector as managed children and restarts them when
|
|
17
|
-
* they die.
|
|
18
|
-
*
|
|
19
|
-
* This is the process a service manager supervises: `herdr-remote run` stays in
|
|
20
|
-
* the foreground so systemd/launchd can track it, and the same class backs the
|
|
21
|
-
* fallback daemon on systems where neither is available.
|
|
22
|
-
*/
|
|
23
|
-
class Supervisor {
|
|
24
|
-
config;
|
|
25
|
-
state;
|
|
26
|
-
logToFiles;
|
|
27
|
-
onEvent;
|
|
28
|
-
/** What to run; the relay and host connector unless a test says otherwise. */
|
|
29
|
-
specs;
|
|
30
|
-
children = new Map();
|
|
31
|
-
stopping = false;
|
|
32
|
-
constructor({ config = loadConfig(), state = ensureRuntime(), logToFiles = false, onEvent = null, specs = null, } = {}) {
|
|
33
|
-
this.config = config;
|
|
34
|
-
this.state = state;
|
|
35
|
-
this.logToFiles = logToFiles;
|
|
36
|
-
this.onEvent = onEvent;
|
|
37
|
-
this.specs = specs;
|
|
38
|
-
}
|
|
39
|
-
emit(event) {
|
|
40
|
-
if (this.onEvent)
|
|
41
|
-
this.onEvent(event);
|
|
42
|
-
const line = `[${new Date().toISOString()}] herdr-remote supervisor: ${event.message}\n`;
|
|
43
|
-
process.stdout.write(line);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Stop anything a previous, unmanaged start left behind.
|
|
47
|
-
*
|
|
48
|
-
* Taking over without this is what produced the runaway: a detached relay
|
|
49
|
-
* from `herdr-remote start` kept port 8787, so every relay this supervisor
|
|
50
|
-
* spawned died with EADDRINUSE and retried forever, while the orphaned host
|
|
51
|
-
* connector and ours both claimed the same host id on the relay and kicked
|
|
52
|
-
* each other off — which is what browsers saw as an endless reconnect.
|
|
53
|
-
*
|
|
54
|
-
* Only ledger entries whose start token still matches are touched: the
|
|
55
|
-
* ledger survives a reboot, and its pids then belong to whatever the new
|
|
56
|
-
* boot started.
|
|
57
|
-
*/
|
|
58
|
-
async reclaimStrays({ timeoutMs = 5000 } = {}) {
|
|
59
|
-
const strays = managedPids().filter((entry) => entry.pid !== process.pid);
|
|
60
|
-
if (strays.length === 0)
|
|
61
|
-
return strays;
|
|
62
|
-
for (const { pid, name } of strays) {
|
|
63
|
-
this.emit({
|
|
64
|
-
type: 'reclaim',
|
|
65
|
-
name,
|
|
66
|
-
pid,
|
|
67
|
-
message: `stopping stray ${name} from an earlier start (pid ${pid})`,
|
|
68
|
-
});
|
|
69
|
-
try {
|
|
70
|
-
process.kill(pid, 'SIGTERM');
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
// Already gone, or not ours to signal; the liveness check below decides.
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// Wait for them to actually go: the relay only releases its port on exit,
|
|
77
|
-
// and spawning ours before then just reproduces the EADDRINUSE loop.
|
|
78
|
-
const deadline = Date.now() + timeoutMs;
|
|
79
|
-
while (Date.now() < deadline) {
|
|
80
|
-
if (strays.every(({ pid }) => !pidAlive(pid)))
|
|
81
|
-
return strays;
|
|
82
|
-
await new Promise((resolve) => {
|
|
83
|
-
setTimeout(resolve, 100);
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
// Checked again before the harder signal, in case the number moved on meanwhile.
|
|
87
|
-
for (const { pid, name } of stillRunning(strays)) {
|
|
88
|
-
this.emit({
|
|
89
|
-
type: 'reclaim',
|
|
90
|
-
name,
|
|
91
|
-
pid,
|
|
92
|
-
message: `stray ${name} (pid ${pid}) ignored SIGTERM, killing`,
|
|
93
|
-
});
|
|
94
|
-
try {
|
|
95
|
-
process.kill(pid, 'SIGKILL');
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
// Already gone; SIGKILL was the last resort anyway.
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return strays;
|
|
102
|
-
}
|
|
103
|
-
async start() {
|
|
104
|
-
this.stopping = false;
|
|
105
|
-
ensureDir(stateDir());
|
|
106
|
-
await this.reclaimStrays();
|
|
107
|
-
if (this.stopping)
|
|
108
|
-
return;
|
|
109
|
-
for (const spec of this.specs ?? serviceSpecs(this.config, this.state)) {
|
|
110
|
-
this.children.set(spec.name, {
|
|
111
|
-
spec,
|
|
112
|
-
child: null,
|
|
113
|
-
pid: null,
|
|
114
|
-
restarts: 0,
|
|
115
|
-
backoffMs: MIN_BACKOFF_MS,
|
|
116
|
-
timer: null,
|
|
117
|
-
});
|
|
118
|
-
this.spawnChild(spec.name);
|
|
119
|
-
}
|
|
120
|
-
this.persistPids();
|
|
121
|
-
}
|
|
122
|
-
spawnChild(name) {
|
|
123
|
-
if (this.stopping)
|
|
124
|
-
return;
|
|
125
|
-
const entry = this.children.get(name);
|
|
126
|
-
if (!entry || entry.child)
|
|
127
|
-
return;
|
|
128
|
-
let stdio = ['ignore', 'inherit', 'inherit'];
|
|
129
|
-
let fd = null;
|
|
130
|
-
if (this.logToFiles) {
|
|
131
|
-
fd = fs.openSync(logPath(name), 'a');
|
|
132
|
-
stdio = ['ignore', fd, fd];
|
|
133
|
-
}
|
|
134
|
-
let child;
|
|
135
|
-
try {
|
|
136
|
-
child = spawn(entry.spec.command, entry.spec.args, {
|
|
137
|
-
cwd: entry.spec.cwd,
|
|
138
|
-
env: { ...baseEnvironment(), ...entry.spec.env },
|
|
139
|
-
windowsHide: true,
|
|
140
|
-
stdio,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
this.emit({
|
|
145
|
-
type: 'spawn_failed',
|
|
146
|
-
name,
|
|
147
|
-
message: `could not start ${name}: ${error.message}`,
|
|
148
|
-
});
|
|
149
|
-
this.scheduleRestart(name);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
finally {
|
|
153
|
-
// The child inherited the descriptor during spawn, so this copy is done.
|
|
154
|
-
if (fd !== null) {
|
|
155
|
-
try {
|
|
156
|
-
fs.closeSync(fd);
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// The child holds its own copy; failing to close ours leaks one descriptor at worst.
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
entry.child = child;
|
|
164
|
-
entry.pid = child.pid;
|
|
165
|
-
entry.startedAt = Date.now();
|
|
166
|
-
this.emit({
|
|
167
|
-
type: 'started',
|
|
168
|
-
name,
|
|
169
|
-
pid: child.pid,
|
|
170
|
-
message: `${name} started (pid ${child.pid})`,
|
|
171
|
-
});
|
|
172
|
-
this.persistPids();
|
|
173
|
-
child.on('exit', (code, signal) => {
|
|
174
|
-
const uptimeMs = Date.now() - (entry.startedAt ?? Date.now());
|
|
175
|
-
entry.child = null;
|
|
176
|
-
entry.pid = null;
|
|
177
|
-
this.persistPids();
|
|
178
|
-
if (this.stopping)
|
|
179
|
-
return;
|
|
180
|
-
if (code === EXIT_REPLACED) {
|
|
181
|
-
this.emit({
|
|
182
|
-
type: 'replaced',
|
|
183
|
-
name,
|
|
184
|
-
message: `${name} stood down: another instance owns this workstation. Not restarting it.`,
|
|
185
|
-
});
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (code === EXIT_AUTH_FAILED) {
|
|
189
|
-
this.emit({
|
|
190
|
-
type: 'fatal',
|
|
191
|
-
name,
|
|
192
|
-
message: `${name} stopped: relay authentication failed. Update credentials before restarting it.`,
|
|
193
|
-
});
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
if (uptimeMs >= HEALTHY_UPTIME_MS)
|
|
197
|
-
entry.backoffMs = MIN_BACKOFF_MS;
|
|
198
|
-
this.emit({
|
|
199
|
-
type: 'exited',
|
|
200
|
-
name,
|
|
201
|
-
code,
|
|
202
|
-
signal,
|
|
203
|
-
message: `${name} exited (${signal || `code ${code}`}) after ${Math.round(uptimeMs / 1000)}s, restarting in ${entry.backoffMs}ms`,
|
|
204
|
-
});
|
|
205
|
-
this.scheduleRestart(name);
|
|
206
|
-
});
|
|
207
|
-
child.on('error', (error) => {
|
|
208
|
-
this.emit({ type: 'error', name, message: `${name} error: ${error.message}` });
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
scheduleRestart(name) {
|
|
212
|
-
if (this.stopping)
|
|
213
|
-
return;
|
|
214
|
-
const entry = this.children.get(name);
|
|
215
|
-
if (!entry || entry.timer)
|
|
216
|
-
return;
|
|
217
|
-
const delay = entry.backoffMs;
|
|
218
|
-
entry.restarts += 1;
|
|
219
|
-
entry.backoffMs = Math.min(MAX_BACKOFF_MS, Math.round(entry.backoffMs * 2));
|
|
220
|
-
const timer = setTimeout(() => {
|
|
221
|
-
entry.timer = null;
|
|
222
|
-
this.spawnChild(name);
|
|
223
|
-
}, delay);
|
|
224
|
-
entry.timer = timer;
|
|
225
|
-
timer.unref?.();
|
|
226
|
-
}
|
|
227
|
-
persistPids() {
|
|
228
|
-
try {
|
|
229
|
-
updateRuntime((current) => {
|
|
230
|
-
current.supervisorPid = process.pid;
|
|
231
|
-
current.relayPid = this.children.get('relay')?.pid || null;
|
|
232
|
-
current.hostPid = this.children.get('host')?.pid || null;
|
|
233
|
-
current.startedAt = current.startedAt || new Date().toISOString();
|
|
234
|
-
current.mode = this.config.relay.mode;
|
|
235
|
-
// Merge into the ledger rather than replacing it, so a pid recorded by
|
|
236
|
-
// another writer is never dropped and left running with nobody tracking
|
|
237
|
-
// it.
|
|
238
|
-
recordManagedPid(current, 'supervisor', process.pid);
|
|
239
|
-
for (const [name, entry] of this.children) {
|
|
240
|
-
if (entry.pid)
|
|
241
|
-
recordManagedPid(current, name, entry.pid);
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
process.stderr.write(`herdr-remote supervisor: could not persist pids: ${error.message}\n`);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
async stop({ graceMs = 5000 } = {}) {
|
|
250
|
-
this.stopping = true;
|
|
251
|
-
const pending = [];
|
|
252
|
-
for (const entry of this.children.values()) {
|
|
253
|
-
if (entry.timer) {
|
|
254
|
-
clearTimeout(entry.timer);
|
|
255
|
-
entry.timer = null;
|
|
256
|
-
}
|
|
257
|
-
const child = entry.child;
|
|
258
|
-
if (!child)
|
|
259
|
-
continue;
|
|
260
|
-
pending.push(new Promise((resolve) => {
|
|
261
|
-
const killTimer = setTimeout(() => {
|
|
262
|
-
try {
|
|
263
|
-
child.kill('SIGKILL');
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
// Already exited between the timer and the kill.
|
|
267
|
-
}
|
|
268
|
-
resolve();
|
|
269
|
-
}, graceMs);
|
|
270
|
-
killTimer.unref?.();
|
|
271
|
-
child.once('exit', () => {
|
|
272
|
-
clearTimeout(killTimer);
|
|
273
|
-
resolve();
|
|
274
|
-
});
|
|
275
|
-
try {
|
|
276
|
-
child.kill('SIGTERM');
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
resolve();
|
|
280
|
-
}
|
|
281
|
-
}));
|
|
282
|
-
}
|
|
283
|
-
await Promise.all(pending);
|
|
284
|
-
try {
|
|
285
|
-
updateRuntime((current) => {
|
|
286
|
-
current.supervisorPid = null;
|
|
287
|
-
current.relayPid = null;
|
|
288
|
-
current.hostPid = null;
|
|
289
|
-
current.startedAt = null;
|
|
290
|
-
current.managedPids = stillRunning((current.managedPids || []).filter(Boolean));
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
catch {
|
|
294
|
-
// The runtime file is advisory; stopping must finish even if it cannot be written.
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
/** Entry point for `herdr-remote run`: supervise in the foreground until told to stop. */
|
|
299
|
-
async function runForeground({ logToFiles = false } = {}) {
|
|
300
|
-
const supervisor = new Supervisor({ logToFiles });
|
|
301
|
-
await supervisor.start();
|
|
302
|
-
return new Promise((resolve) => {
|
|
303
|
-
let shuttingDown = false;
|
|
304
|
-
const shutdown = async (signal) => {
|
|
305
|
-
if (shuttingDown)
|
|
306
|
-
return;
|
|
307
|
-
shuttingDown = true;
|
|
308
|
-
supervisor.emit({ type: 'shutdown', message: `${signal} received, stopping services` });
|
|
309
|
-
await supervisor.stop();
|
|
310
|
-
resolve(0);
|
|
311
|
-
};
|
|
312
|
-
process.on('SIGINT', () => {
|
|
313
|
-
shutdown('SIGINT');
|
|
314
|
-
});
|
|
315
|
-
process.on('SIGTERM', () => {
|
|
316
|
-
shutdown('SIGTERM');
|
|
317
|
-
});
|
|
318
|
-
process.on('SIGHUP', () => {
|
|
319
|
-
shutdown('SIGHUP');
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
export { MIN_BACKOFF_MS, runForeground, Supervisor };
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// What font detection reads the world through, injectable for tests, and the
|
|
2
|
-
// unit conversions every reader shares.
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import os from 'node:os';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
/** Terminals size fonts in points; CSS pixels are 1/96 inch. */
|
|
8
|
-
const PX_PER_PT = 96 / 72;
|
|
9
|
-
const COMMAND_TIMEOUT_MS = 3000;
|
|
10
|
-
/** Names a terminal accepts in place of a real family. */
|
|
11
|
-
export const GENERIC_FAMILIES = new Set([
|
|
12
|
-
'monospace',
|
|
13
|
-
'mono',
|
|
14
|
-
'sans',
|
|
15
|
-
'sans-serif',
|
|
16
|
-
'serif',
|
|
17
|
-
'system-ui',
|
|
18
|
-
'ui-monospace',
|
|
19
|
-
]);
|
|
20
|
-
export function defaultRun(command, args) {
|
|
21
|
-
try {
|
|
22
|
-
const result = spawnSync(command, args, {
|
|
23
|
-
encoding: 'utf8',
|
|
24
|
-
timeout: COMMAND_TIMEOUT_MS,
|
|
25
|
-
windowsHide: true,
|
|
26
|
-
});
|
|
27
|
-
return { status: result.status, stdout: result.stdout || '' };
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return { status: null, stdout: '' };
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function defaultReadFile(filePath) {
|
|
34
|
-
try {
|
|
35
|
-
return fs.readFileSync(filePath, 'utf8');
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function defaultReadBuffer(filePath) {
|
|
42
|
-
try {
|
|
43
|
-
return fs.readFileSync(filePath);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
export function withDefaults(deps = {}) {
|
|
50
|
-
const env = deps.env || process.env;
|
|
51
|
-
return {
|
|
52
|
-
env,
|
|
53
|
-
platform: deps.platform || process.platform,
|
|
54
|
-
home: deps.home || env.HOME || os.homedir(),
|
|
55
|
-
run: deps.run || defaultRun,
|
|
56
|
-
readFile: deps.readFile || defaultReadFile,
|
|
57
|
-
readBuffer: deps.readBuffer || defaultReadBuffer,
|
|
58
|
-
terminalArgs: deps.terminalArgs || [],
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
export function configHome(deps) {
|
|
62
|
-
return deps.env.XDG_CONFIG_HOME || path.join(deps.home, '.config');
|
|
63
|
-
}
|
|
64
|
-
export function dataHome(deps) {
|
|
65
|
-
return deps.env.XDG_DATA_HOME || path.join(deps.home, '.local', 'share');
|
|
66
|
-
}
|
|
67
|
-
export function localAppData(deps) {
|
|
68
|
-
return deps.env.LOCALAPPDATA || path.join(deps.home, 'AppData', 'Local');
|
|
69
|
-
}
|
|
70
|
-
export function roamingAppData(deps) {
|
|
71
|
-
return deps.env.APPDATA || path.join(deps.home, 'AppData', 'Roaming');
|
|
72
|
-
}
|
|
73
|
-
export function expandHome(filePath, deps) {
|
|
74
|
-
return filePath.startsWith('~/') ? path.join(deps.home, filePath.slice(2)) : filePath;
|
|
75
|
-
}
|
|
76
|
-
export function pointsToPx(points) {
|
|
77
|
-
const value = Number(points);
|
|
78
|
-
return Number.isFinite(value) && value > 0 ? Math.round(value * PX_PER_PT * 10) / 10 : undefined;
|
|
79
|
-
}
|
|
80
|
-
export function positiveNumber(value) {
|
|
81
|
-
const number = Number(value);
|
|
82
|
-
return Number.isFinite(number) && number > 0 ? number : undefined;
|
|
83
|
-
}
|