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/service.js
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
// The processes that make up a running herdr-remote — the local relay and the
|
|
2
|
-
// host connector — and starting, stopping and describing them.
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import { createRequire } from 'node:module';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { ensureDir } from 'herdr-remote-relay/state';
|
|
8
|
-
import { loadConfig } from './config.js';
|
|
9
|
-
import { findHerdrCommand, herdrVersion, MIN_HERDR_VERSION, resolveHerdrCommand, } from './herdr-command.js';
|
|
10
|
-
import { preferredLanAddress } from './net-interfaces.js';
|
|
11
|
-
import { logPath, PACKAGE_ROOT, stateDir } from './paths.js';
|
|
12
|
-
import { healthUrl, hostHeaders, requestJson, waitForHost, waitForRelay } from './relay-client.js';
|
|
13
|
-
import { bindAddress, resolveAdminOrigin, resolveHostRelayUrl, resolvePublicUrl, runsLocalRelay, } from './relay-urls.js';
|
|
14
|
-
import { ensureRuntime, managedPids, readRuntime, recordManagedPid, updateRuntime, writeRuntime, } from './runtime.js';
|
|
15
|
-
import { resolveSocketPath } from './socket-discovery.js';
|
|
16
|
-
import { resolveHostFont } from './terminal-font/index.js';
|
|
17
|
-
import { hostTerminalPalette } from './terminal-palette.js';
|
|
18
|
-
export function relayBinPath() {
|
|
19
|
-
// Resolved rather than hard-coded: the relay is a separate npm package, so
|
|
20
|
-
// its location depends on how npm hoisted it.
|
|
21
|
-
const manifest = createRequire(import.meta.url).resolve('herdr-remote-relay/package.json');
|
|
22
|
-
return path.join(path.dirname(manifest), 'bin', 'herdr-remote-relay.js');
|
|
23
|
-
}
|
|
24
|
-
function relayAuthStatePath() {
|
|
25
|
-
return path.join(stateDir(), 'relay-auth.json');
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* The processes that make up a running herdr-remote, as declarative specs.
|
|
29
|
-
*
|
|
30
|
-
* Both the detached starter and the foreground supervisor build their children
|
|
31
|
-
* from this one list so the two paths cannot drift in how they configure the
|
|
32
|
-
* relay or the host connector.
|
|
33
|
-
*/
|
|
34
|
-
export function serviceSpecs(config = loadConfig(), state = ensureRuntime()) {
|
|
35
|
-
const specs = [];
|
|
36
|
-
const publicUrl = resolvePublicUrl(config, preferredLanAddress());
|
|
37
|
-
if (runsLocalRelay(config)) {
|
|
38
|
-
specs.push({
|
|
39
|
-
name: 'relay',
|
|
40
|
-
command: process.execPath,
|
|
41
|
-
args: [relayBinPath()],
|
|
42
|
-
cwd: stateDir(),
|
|
43
|
-
env: {
|
|
44
|
-
// Let the shared web UI tell a private workstation relay apart from
|
|
45
|
-
// the operator-facing self-hosted relay. Standalone relay installs
|
|
46
|
-
// default to remote mode.
|
|
47
|
-
RELAY_DEPLOYMENT_MODE: 'local',
|
|
48
|
-
RELAY_BIND: bindAddress(config),
|
|
49
|
-
RELAY_PORT: String(config.relay.port),
|
|
50
|
-
RELAY_PUBLIC_URL: publicUrl,
|
|
51
|
-
// A relay we start ourselves is closed to everything but this
|
|
52
|
-
// workstation: reusing the host token as its password costs nothing and
|
|
53
|
-
// stops another machine on the LAN from enrolling into it.
|
|
54
|
-
RELAY_PASSWORD: state.hostToken,
|
|
55
|
-
RELAY_AUTH_STATE_FILE: relayAuthStatePath(),
|
|
56
|
-
RELAY_ALLOWED_ORIGINS: (config.relay.allowedOrigins || []).join(','),
|
|
57
|
-
RELAY_MAX_CLIENTS_PER_HOST: String(config.relay.maxClientsPerHost),
|
|
58
|
-
RELAY_MAX_HOSTS: String(config.relay.maxHosts),
|
|
59
|
-
RELAY_MAX_PENDING_HANDSHAKES: String(config.relay.maxPendingHandshakes),
|
|
60
|
-
RELAY_MAX_BUFFERED_BYTES_PER_CLIENT: String(config.relay.maxBufferedBytesPerClient),
|
|
61
|
-
RELAY_HOST_RECONNECT_GRACE_MS: String(config.relay.hostReconnectGraceMs),
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
const terminalPalette = hostTerminalPalette();
|
|
66
|
-
// What this command detected at its entry point, else what an earlier start
|
|
67
|
-
// with a terminal wrote down. Only the family, size and terminal travel here;
|
|
68
|
-
// the connector finds the files itself, on the machine that has them.
|
|
69
|
-
const terminalFont = resolveHostFont();
|
|
70
|
-
specs.push({
|
|
71
|
-
name: 'host',
|
|
72
|
-
command: process.execPath,
|
|
73
|
-
args: [path.join(PACKAGE_ROOT, 'dist', 'connector', 'main.js')],
|
|
74
|
-
cwd: stateDir(),
|
|
75
|
-
env: {
|
|
76
|
-
// Captured here, where a terminal may still be attached, because the
|
|
77
|
-
// connector itself usually runs detached with no terminal to ask.
|
|
78
|
-
...(terminalPalette ? { HERDR_TERM_PALETTE_JSON: JSON.stringify(terminalPalette) } : {}),
|
|
79
|
-
...(terminalFont ? { HERDR_TERM_FONT_JSON: JSON.stringify(terminalFont) } : {}),
|
|
80
|
-
RELAY_URL: resolveHostRelayUrl(config),
|
|
81
|
-
RELAY_HOST_ID: state.hostId,
|
|
82
|
-
RELAY_HOST_TOKEN: state.hostToken,
|
|
83
|
-
RELAY_PASSWORD: runsLocalRelay(config) ? state.hostToken : state.relayPassword || '',
|
|
84
|
-
HERDR_SOCKET_PATH: resolveSocketPath(config.herdr.socketPath),
|
|
85
|
-
HERDR_ARGS_JSON: JSON.stringify(config.herdr.args),
|
|
86
|
-
HERDR_CWD: config.herdr.cwd,
|
|
87
|
-
HERDR_BIN_PATH: resolveHerdrCommand(),
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
return specs;
|
|
91
|
-
}
|
|
92
|
-
export function baseEnvironment() {
|
|
93
|
-
return { ...process.env, HERDR_REMOTE_SERVICE: '1' };
|
|
94
|
-
}
|
|
95
|
-
function spawnDetached(spec) {
|
|
96
|
-
ensureDir(stateDir());
|
|
97
|
-
const logFd = fs.openSync(logPath(spec.name), 'a');
|
|
98
|
-
try {
|
|
99
|
-
const child = spawn(spec.command, spec.args, {
|
|
100
|
-
cwd: spec.cwd,
|
|
101
|
-
env: { ...baseEnvironment(), ...spec.env },
|
|
102
|
-
detached: true,
|
|
103
|
-
// A detached console program can open a visible window from a console-less Windows parent.
|
|
104
|
-
windowsHide: true,
|
|
105
|
-
stdio: ['ignore', logFd, logFd],
|
|
106
|
-
});
|
|
107
|
-
child.unref();
|
|
108
|
-
return child.pid;
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
fs.closeSync(logFd);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/** Where each service's pid is kept in runtime.json. */
|
|
115
|
-
const PID_FIELDS = { relay: 'relayPid', host: 'hostPid' };
|
|
116
|
-
export function startServices() {
|
|
117
|
-
const config = loadConfig();
|
|
118
|
-
const state = ensureRuntime();
|
|
119
|
-
const next = { ...state };
|
|
120
|
-
const specs = serviceSpecs(config, state);
|
|
121
|
-
const running = managedPids(next);
|
|
122
|
-
const isRunning = (pid) => running.some((entry) => entry.pid === pid);
|
|
123
|
-
for (const spec of specs) {
|
|
124
|
-
const field = PID_FIELDS[spec.name];
|
|
125
|
-
if (isRunning(next[field]))
|
|
126
|
-
continue;
|
|
127
|
-
next[field] = spawnDetached(spec) ?? null;
|
|
128
|
-
recordManagedPid(next, spec.name, next[field]);
|
|
129
|
-
}
|
|
130
|
-
// A relay that is no longer part of the plan (switched to remote mode) must
|
|
131
|
-
// not be left running on the old port.
|
|
132
|
-
if (!specs.some((spec) => spec.name === 'relay') && isRunning(next.relayPid)) {
|
|
133
|
-
try {
|
|
134
|
-
process.kill(next.relayPid, 'SIGTERM');
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
// Exited between the check and the signal: the port is free either way.
|
|
138
|
-
}
|
|
139
|
-
next.relayPid = null;
|
|
140
|
-
}
|
|
141
|
-
next.startedAt = next.startedAt || new Date().toISOString();
|
|
142
|
-
next.mode = config.relay.mode;
|
|
143
|
-
writeRuntime(next);
|
|
144
|
-
const live = managedPids(next);
|
|
145
|
-
return {
|
|
146
|
-
ok: true,
|
|
147
|
-
mode: config.relay.mode,
|
|
148
|
-
relay: {
|
|
149
|
-
local: runsLocalRelay(config),
|
|
150
|
-
pid: next.relayPid || null,
|
|
151
|
-
alive: live.some(({ pid }) => pid === next.relayPid),
|
|
152
|
-
bind: runsLocalRelay(config) ? bindAddress(config) : null,
|
|
153
|
-
port: config.relay.port,
|
|
154
|
-
remoteUrl: config.relay.remoteUrl || null,
|
|
155
|
-
},
|
|
156
|
-
host: {
|
|
157
|
-
pid: next.hostPid || null,
|
|
158
|
-
alive: live.some(({ pid }) => pid === next.hostPid),
|
|
159
|
-
socketPath: resolveSocketPath(config.herdr.socketPath),
|
|
160
|
-
},
|
|
161
|
-
publicUrl: resolvePublicUrl(config, preferredLanAddress()),
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
export function stopServices() {
|
|
165
|
-
const stopped = [];
|
|
166
|
-
ensureDir(stateDir());
|
|
167
|
-
updateRuntime((state) => {
|
|
168
|
-
// Supervisor first: it would otherwise see its children die and restart
|
|
169
|
-
// them faster than we can kill them.
|
|
170
|
-
const targets = managedPids(state).sort((a, b) => (a.name === 'supervisor' ? 0 : 1) - (b.name === 'supervisor' ? 0 : 1));
|
|
171
|
-
for (const { pid, name } of targets) {
|
|
172
|
-
try {
|
|
173
|
-
process.kill(pid, 'SIGTERM');
|
|
174
|
-
stopped.push({ name, pid });
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
process.stderr.write(`herdr-remote: could not stop ${name} (pid ${pid}): ${error.message}\n`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
state.hostPid = null;
|
|
181
|
-
state.relayPid = null;
|
|
182
|
-
state.supervisorPid = null;
|
|
183
|
-
state.managedPids = [];
|
|
184
|
-
state.startedAt = null;
|
|
185
|
-
});
|
|
186
|
-
return { ok: true, stopped };
|
|
187
|
-
}
|
|
188
|
-
export function restartServices() {
|
|
189
|
-
stopServices();
|
|
190
|
-
return startServices();
|
|
191
|
-
}
|
|
192
|
-
export async function statusServices() {
|
|
193
|
-
const config = loadConfig();
|
|
194
|
-
const state = readRuntime();
|
|
195
|
-
const live = managedPids(state);
|
|
196
|
-
const lanAddress = preferredLanAddress();
|
|
197
|
-
let health;
|
|
198
|
-
try {
|
|
199
|
-
health = await requestJson(healthUrl(config), { timeout: 1200 });
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
health = { ok: false, message: error.message };
|
|
203
|
-
}
|
|
204
|
-
const socketPath = resolveSocketPath(config.herdr.socketPath);
|
|
205
|
-
// Where Herdr was found, and whether it was found at all: under a service
|
|
206
|
-
// manager this is the first thing to check when sessions will not start.
|
|
207
|
-
const herdr = findHerdrCommand();
|
|
208
|
-
// Only worth spawning when there is something to spawn; an unresolved command
|
|
209
|
-
// has already answered the question the version would.
|
|
210
|
-
const installed = herdr.found
|
|
211
|
-
? herdrVersion({ command: herdr.command })
|
|
212
|
-
: { version: null, supported: true };
|
|
213
|
-
return {
|
|
214
|
-
ok: true,
|
|
215
|
-
mode: config.relay.mode,
|
|
216
|
-
relay: {
|
|
217
|
-
local: runsLocalRelay(config),
|
|
218
|
-
pid: state.relayPid || null,
|
|
219
|
-
alive: runsLocalRelay(config) ? live.some(({ pid }) => pid === state.relayPid) : null,
|
|
220
|
-
bind: runsLocalRelay(config) ? bindAddress(config) : null,
|
|
221
|
-
port: config.relay.port,
|
|
222
|
-
remoteUrl: config.relay.remoteUrl || null,
|
|
223
|
-
health,
|
|
224
|
-
},
|
|
225
|
-
host: {
|
|
226
|
-
pid: state.hostPid || null,
|
|
227
|
-
alive: live.some(({ pid }) => pid === state.hostPid),
|
|
228
|
-
hostId: state.hostId || null,
|
|
229
|
-
socketPath,
|
|
230
|
-
socketExists: Boolean(socketPath) && fs.existsSync(socketPath),
|
|
231
|
-
herdrCommand: herdr.command,
|
|
232
|
-
herdrCommandFound: herdr.found,
|
|
233
|
-
herdrCommandSource: herdr.source,
|
|
234
|
-
herdrVersion: installed.version,
|
|
235
|
-
herdrVersionSupported: installed.supported,
|
|
236
|
-
herdrVersionMinimum: MIN_HERDR_VERSION,
|
|
237
|
-
},
|
|
238
|
-
publicUrl: resolvePublicUrl(config, lanAddress),
|
|
239
|
-
startedAt: state.startedAt || null,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
/** Start what pairing needs, then ask the relay for a code. */
|
|
243
|
-
export async function pair() {
|
|
244
|
-
const config = loadConfig();
|
|
245
|
-
if (runsLocalRelay(config))
|
|
246
|
-
startServices();
|
|
247
|
-
await waitForRelay(config);
|
|
248
|
-
await waitForHost(config);
|
|
249
|
-
// Authenticated with this workstation's own host token: on a shared relay
|
|
250
|
-
// that is what proves the pairing code is being minted for our terminal and
|
|
251
|
-
// nobody else's.
|
|
252
|
-
return requestJson(`${resolveAdminOrigin(config)}/api/pair/start`, {
|
|
253
|
-
method: 'POST',
|
|
254
|
-
headers: hostHeaders(),
|
|
255
|
-
timeout: 5000,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
export function readLogTail(name, lines = 40) {
|
|
259
|
-
try {
|
|
260
|
-
const content = fs.readFileSync(logPath(name), 'utf8');
|
|
261
|
-
return content.split('\n').filter(Boolean).slice(-lines);
|
|
262
|
-
}
|
|
263
|
-
catch {
|
|
264
|
-
// No log yet: the service has never run.
|
|
265
|
-
return [];
|
|
266
|
-
}
|
|
267
|
-
}
|
package/dist/settings-model.js
DELETED
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
// Editable settings, independent of how they are rendered.
|
|
2
|
-
//
|
|
3
|
-
// The TUI holds a *draft* copy of the config, mutates it through these pure
|
|
4
|
-
// functions and writes it out on save. Keeping the rules here (rather than in
|
|
5
|
-
// the view) means they can be tested without a terminal, and the same
|
|
6
|
-
// validation applies to the first-run wizard and the settings screen.
|
|
7
|
-
import { ACCESS_MODES, KEEPALIVE_MANAGERS, LANGUAGES, OFFICIAL_RELAY_URL, loadConfig, } from './config.js';
|
|
8
|
-
import { configDir, configPath } from './paths.js';
|
|
9
|
-
import { isLoopbackHost, isUnspecifiedAddress, isUnspecifiedHost, resolvePublicUrl, } from './relay-urls.js';
|
|
10
|
-
import { ensureDir, readJson, writeJsonAtomic } from 'herdr-remote-relay/state';
|
|
11
|
-
import { preferredLanAddress } from './net-interfaces.js';
|
|
12
|
-
function isOneOf(list, value) {
|
|
13
|
-
return list.includes(value);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Field metadata. `kind` drives the editor the TUI shows; `visibleFor` limits a
|
|
17
|
-
* field to the access modes where it means anything.
|
|
18
|
-
*/
|
|
19
|
-
const FIELDS = [
|
|
20
|
-
{ id: 'mode', kind: 'choice', choices: ACCESS_MODES, labelKey: 'field.mode' },
|
|
21
|
-
{ id: 'port', kind: 'text', labelKey: 'field.port', visibleFor: ['local', 'lan'] },
|
|
22
|
-
{ id: 'lanHost', kind: 'address', labelKey: 'field.lanHost', visibleFor: ['lan'] },
|
|
23
|
-
{ id: 'remoteUrl', kind: 'text', labelKey: 'field.remoteUrl', visibleFor: ['remote'] },
|
|
24
|
-
{ id: 'publicUrl', kind: 'text', labelKey: 'field.publicUrl' },
|
|
25
|
-
{ id: 'socketPath', kind: 'text', labelKey: 'field.socketPath' },
|
|
26
|
-
{ id: 'herdrArgs', kind: 'text', labelKey: 'field.herdrArgs' },
|
|
27
|
-
{ id: 'herdrAutoStart', kind: 'toggle', labelKey: 'field.herdrAutoStart' },
|
|
28
|
-
{ id: 'language', kind: 'choice', choices: LANGUAGES, labelKey: 'field.language' },
|
|
29
|
-
{
|
|
30
|
-
id: 'keepaliveManager',
|
|
31
|
-
kind: 'choice',
|
|
32
|
-
choices: KEEPALIVE_MANAGERS,
|
|
33
|
-
labelKey: 'field.keepalive',
|
|
34
|
-
},
|
|
35
|
-
];
|
|
36
|
-
const EMPTY = '';
|
|
37
|
-
/**
|
|
38
|
-
* The access modes a *person* chooses between.
|
|
39
|
-
*
|
|
40
|
-
* The official relay is stored as `remote` with a known URL, because that is
|
|
41
|
-
* exactly what it is and nothing downstream should have to learn a fourth mode.
|
|
42
|
-
* It is still a separate answer to "how do I reach this workstation", though:
|
|
43
|
-
* one option needs no server and no credentials, the other needs both. Keeping
|
|
44
|
-
* that distinction here — rather than in whichever screen happens to draw it —
|
|
45
|
-
* is what stops the official relay from being displayed as "self-hosted relay"
|
|
46
|
-
* that merely happens to hold our address.
|
|
47
|
-
*/
|
|
48
|
-
const SELECTABLE_MODES = ['local', 'lan', 'official', 'remote'];
|
|
49
|
-
/** Which of `SELECTABLE_MODES` this draft represents. */
|
|
50
|
-
function selectedMode(draft) {
|
|
51
|
-
if (draft.relay.mode === 'remote' && draft.relay.remoteUrl === OFFICIAL_RELAY_URL)
|
|
52
|
-
return 'official';
|
|
53
|
-
return draft.relay.mode;
|
|
54
|
-
}
|
|
55
|
-
/** True when the relay is the one we run, so its address and password are ours. */
|
|
56
|
-
function isOfficialRelay(draft) {
|
|
57
|
-
return selectedMode(draft) === 'official';
|
|
58
|
-
}
|
|
59
|
-
function clone(value) {
|
|
60
|
-
return JSON.parse(JSON.stringify(value));
|
|
61
|
-
}
|
|
62
|
-
function createDraft(config = loadConfig()) {
|
|
63
|
-
return clone(config);
|
|
64
|
-
}
|
|
65
|
-
function fieldsForMode(mode) {
|
|
66
|
-
return FIELDS.filter((field) => !field.visibleFor || field.visibleFor.includes(mode));
|
|
67
|
-
}
|
|
68
|
-
/** Current value of a field as an editable string. */
|
|
69
|
-
function getField(draft, id) {
|
|
70
|
-
switch (id) {
|
|
71
|
-
case 'mode':
|
|
72
|
-
return draft.relay.mode;
|
|
73
|
-
case 'port':
|
|
74
|
-
return String(draft.relay.port);
|
|
75
|
-
case 'lanHost':
|
|
76
|
-
return draft.relay.lanHost || EMPTY;
|
|
77
|
-
case 'remoteUrl':
|
|
78
|
-
return draft.relay.remoteUrl || EMPTY;
|
|
79
|
-
case 'publicUrl':
|
|
80
|
-
return draft.relay.publicUrl || EMPTY;
|
|
81
|
-
case 'socketPath':
|
|
82
|
-
return draft.herdr.socketPath || EMPTY;
|
|
83
|
-
case 'herdrArgs':
|
|
84
|
-
return (draft.herdr.args || []).join(' ');
|
|
85
|
-
case 'herdrAutoStart':
|
|
86
|
-
return draft.herdr.autoStart ? 'on' : 'off';
|
|
87
|
-
case 'language':
|
|
88
|
-
return draft.ui.language;
|
|
89
|
-
case 'keepaliveManager':
|
|
90
|
-
return draft.keepalive.manager;
|
|
91
|
-
default:
|
|
92
|
-
return EMPTY;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* What the field shows when it is empty: the value that will actually be used.
|
|
97
|
-
* Displaying the derived value beats an empty box the user cannot interpret.
|
|
98
|
-
*/
|
|
99
|
-
function getFieldPlaceholder(draft, id) {
|
|
100
|
-
switch (id) {
|
|
101
|
-
case 'lanHost':
|
|
102
|
-
return preferredLanAddress() || '0.0.0.0';
|
|
103
|
-
case 'publicUrl':
|
|
104
|
-
return resolvePublicUrl(draft, preferredLanAddress());
|
|
105
|
-
case 'socketPath':
|
|
106
|
-
return 'placeholder.autoDiscovered';
|
|
107
|
-
case 'herdrArgs':
|
|
108
|
-
return 'placeholder.none';
|
|
109
|
-
case 'remoteUrl':
|
|
110
|
-
return 'wss://relay.example.com';
|
|
111
|
-
default:
|
|
112
|
-
return EMPTY;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function isValidUrl(value, protocols) {
|
|
116
|
-
try {
|
|
117
|
-
const url = new URL(value);
|
|
118
|
-
return protocols.includes(url.protocol);
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Apply a value to a copy of the draft.
|
|
126
|
-
*
|
|
127
|
-
* Returns `{ draft, errorKey }`. On a validation failure the draft comes back
|
|
128
|
-
* unchanged and `errorKey` names a translatable message, so callers never have
|
|
129
|
-
* to guess whether the edit landed.
|
|
130
|
-
*/
|
|
131
|
-
function setField(draft, id, rawValue) {
|
|
132
|
-
const next = clone(draft);
|
|
133
|
-
const value = typeof rawValue === 'string' ? rawValue.trim() : rawValue;
|
|
134
|
-
// Every field but the toggle is edited as text.
|
|
135
|
-
const text = value;
|
|
136
|
-
switch (id) {
|
|
137
|
-
case 'mode': {
|
|
138
|
-
if (!isOneOf(SELECTABLE_MODES, value))
|
|
139
|
-
return { draft, errorKey: 'error.invalidMode' };
|
|
140
|
-
// Picking the official relay fills in its address in the same edit: a
|
|
141
|
-
// remote mode with no URL is not a valid state, and asking for the
|
|
142
|
-
// address we already know would be asking the user to do our filing.
|
|
143
|
-
if (value === 'official') {
|
|
144
|
-
next.relay.mode = 'remote';
|
|
145
|
-
next.relay.remoteUrl = OFFICIAL_RELAY_URL;
|
|
146
|
-
next.relay.publicUrl = EMPTY;
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
// Leaving the official relay clears its address, so the self-hosted URL
|
|
150
|
-
// field is empty and asking to be filled in rather than pre-loaded with
|
|
151
|
-
// an address that belongs to somebody else's server.
|
|
152
|
-
if (value === 'remote' && next.relay.remoteUrl === OFFICIAL_RELAY_URL) {
|
|
153
|
-
next.relay.remoteUrl = EMPTY;
|
|
154
|
-
}
|
|
155
|
-
next.relay.mode = value;
|
|
156
|
-
if (value === 'lan' &&
|
|
157
|
-
(isLoopbackHost(next.relay.lanHost) || isUnspecifiedAddress(next.relay.lanHost))) {
|
|
158
|
-
next.relay.lanHost = EMPTY;
|
|
159
|
-
}
|
|
160
|
-
// A public URL pinned for one mode is wrong for the next one; clearing it
|
|
161
|
-
// lets derivation produce the right address again.
|
|
162
|
-
next.relay.publicUrl = EMPTY;
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
case 'port': {
|
|
166
|
-
const port = Number.parseInt(text, 10);
|
|
167
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
168
|
-
return { draft, errorKey: 'error.invalidPort' };
|
|
169
|
-
next.relay.port = port;
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
case 'lanHost': {
|
|
173
|
-
if (text && (isLoopbackHost(text) || isUnspecifiedAddress(text))) {
|
|
174
|
-
return { draft, errorKey: 'error.invalidLanHost' };
|
|
175
|
-
}
|
|
176
|
-
next.relay.lanHost = text || EMPTY;
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
case 'remoteUrl': {
|
|
180
|
-
if (text && !isValidUrl(text, ['ws:', 'wss:', 'http:', 'https:'])) {
|
|
181
|
-
return { draft, errorKey: 'error.invalidRelayUrl' };
|
|
182
|
-
}
|
|
183
|
-
next.relay.remoteUrl = text.replace(/\/+$/, '');
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
case 'publicUrl': {
|
|
187
|
-
if (text && (!isValidUrl(text, ['http:', 'https:']) || isUnspecifiedHost(text))) {
|
|
188
|
-
return { draft, errorKey: 'error.invalidPublicUrl' };
|
|
189
|
-
}
|
|
190
|
-
next.relay.publicUrl = text.replace(/\/+$/, '');
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
case 'socketPath': {
|
|
194
|
-
next.herdr.socketPath = text || null;
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
case 'herdrArgs': {
|
|
198
|
-
const args = text ? text.split(/\s+/).filter(Boolean) : [];
|
|
199
|
-
if (args.includes('--no-session')) {
|
|
200
|
-
return { draft, errorKey: 'error.removedHerdrArg' };
|
|
201
|
-
}
|
|
202
|
-
next.herdr.args = args;
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
case 'herdrAutoStart': {
|
|
206
|
-
if (value !== true && value !== false && value !== 'on' && value !== 'off') {
|
|
207
|
-
return { draft, errorKey: 'error.unknownField' };
|
|
208
|
-
}
|
|
209
|
-
next.herdr.autoStart = value === true || value === 'on';
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
case 'language': {
|
|
213
|
-
if (!isOneOf(LANGUAGES, value))
|
|
214
|
-
return { draft, errorKey: 'error.invalidLanguage' };
|
|
215
|
-
next.ui.language = value;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
case 'keepaliveManager': {
|
|
219
|
-
if (!isOneOf(KEEPALIVE_MANAGERS, value))
|
|
220
|
-
return { draft, errorKey: 'error.invalidKeepalive' };
|
|
221
|
-
next.keepalive.manager = value;
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
default:
|
|
225
|
-
return { draft, errorKey: 'error.unknownField' };
|
|
226
|
-
}
|
|
227
|
-
return { draft: next, errorKey: null };
|
|
228
|
-
}
|
|
229
|
-
/** Problems that should block saving, as translatable keys. */
|
|
230
|
-
function validateDraft(draft) {
|
|
231
|
-
const problems = [];
|
|
232
|
-
if (draft.relay.mode === 'remote' && !draft.relay.remoteUrl)
|
|
233
|
-
problems.push('error.remoteUrlRequired');
|
|
234
|
-
return problems;
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Persist a draft, merging into whatever is already on disk so keys this
|
|
238
|
-
* version does not know about survive the round trip.
|
|
239
|
-
*/
|
|
240
|
-
function saveDraft(draft) {
|
|
241
|
-
const problems = validateDraft(draft);
|
|
242
|
-
if (problems.length > 0) {
|
|
243
|
-
throw Object.assign(new Error(`configuration is incomplete: ${problems.join(', ')}`), {
|
|
244
|
-
problems,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
const current = (readJson(configPath(), {}) || {});
|
|
248
|
-
const merged = { ...current };
|
|
249
|
-
merged.ui = { ...(current.ui || {}), language: draft.ui.language };
|
|
250
|
-
const relay = {
|
|
251
|
-
...(current.relay || {}),
|
|
252
|
-
mode: draft.relay.mode,
|
|
253
|
-
port: draft.relay.port,
|
|
254
|
-
lanHost: draft.relay.mode === 'lan' &&
|
|
255
|
-
(isLoopbackHost(draft.relay.lanHost) || isUnspecifiedAddress(draft.relay.lanHost))
|
|
256
|
-
? EMPTY
|
|
257
|
-
: draft.relay.lanHost,
|
|
258
|
-
publicUrl: draft.relay.publicUrl,
|
|
259
|
-
remoteUrl: draft.relay.remoteUrl,
|
|
260
|
-
};
|
|
261
|
-
merged.relay = relay;
|
|
262
|
-
// Fields from the 0.1 schema would otherwise keep overriding the new ones on
|
|
263
|
-
// the next load.
|
|
264
|
-
delete relay.local;
|
|
265
|
-
delete relay.host;
|
|
266
|
-
delete relay.url;
|
|
267
|
-
// The optional Herdr source patch is gone. Nothing reads this section any
|
|
268
|
-
// more, so the next save is where a config written by an older build sheds
|
|
269
|
-
// it rather than carrying stale checksums and paths forever.
|
|
270
|
-
delete merged.patch;
|
|
271
|
-
const herdr = {
|
|
272
|
-
...(current.herdr || {}),
|
|
273
|
-
socketPath: draft.herdr.socketPath,
|
|
274
|
-
args: draft.herdr.args,
|
|
275
|
-
autoStart: draft.herdr.autoStart === true,
|
|
276
|
-
};
|
|
277
|
-
merged.herdr = herdr;
|
|
278
|
-
if (!herdr.socketPath)
|
|
279
|
-
delete herdr.socketPath;
|
|
280
|
-
merged.keepalive = { ...(current.keepalive || {}), manager: draft.keepalive.manager };
|
|
281
|
-
ensureDir(configDir());
|
|
282
|
-
writeJsonAtomic(configPath(), merged);
|
|
283
|
-
return { ok: true, path: configPath() };
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Settings that only take effect after the services restart. Used to show a
|
|
287
|
-
* "restart required" hint rather than silently doing nothing.
|
|
288
|
-
*/
|
|
289
|
-
function requiresRestart(before, after) {
|
|
290
|
-
return (before.relay.mode !== after.relay.mode ||
|
|
291
|
-
before.relay.port !== after.relay.port ||
|
|
292
|
-
before.relay.lanHost !== after.relay.lanHost ||
|
|
293
|
-
before.relay.remoteUrl !== after.relay.remoteUrl ||
|
|
294
|
-
before.relay.publicUrl !== after.relay.publicUrl ||
|
|
295
|
-
before.herdr.socketPath !== after.herdr.socketPath ||
|
|
296
|
-
before.herdr.args.join(' ') !== after.herdr.args.join(' '));
|
|
297
|
-
}
|
|
298
|
-
export { createDraft, fieldsForMode, isOfficialRelay, selectedMode, getField, getFieldPlaceholder, setField, validateDraft, saveDraft, requiresRestart, };
|
package/dist/socket-discovery.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
/**
|
|
5
|
-
* Herdr's own config directory, resolved as Herdr does (`XDG_CONFIG_HOME`
|
|
6
|
-
* first on every platform, then the platform's home for it), so the socket
|
|
7
|
-
* and `config.toml` are found wherever Herdr put them.
|
|
8
|
-
*/
|
|
9
|
-
function herdrConfigDir(env = process.env, platform = process.platform) {
|
|
10
|
-
if (platform === 'win32') {
|
|
11
|
-
const configHome = env.XDG_CONFIG_HOME ||
|
|
12
|
-
env.APPDATA ||
|
|
13
|
-
(env.USERPROFILE && path.join(env.USERPROFILE, 'AppData', 'Roaming')) ||
|
|
14
|
-
path.join(os.homedir(), 'AppData', 'Roaming');
|
|
15
|
-
return path.join(configHome, 'herdr');
|
|
16
|
-
}
|
|
17
|
-
const configHome = env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
|
|
18
|
-
return path.join(configHome, 'herdr');
|
|
19
|
-
}
|
|
20
|
-
function defaultSocketPath(env = process.env, platform = process.platform) {
|
|
21
|
-
return env.HERDR_SOCKET_PATH || path.join(herdrConfigDir(env, platform), 'herdr.sock');
|
|
22
|
-
}
|
|
23
|
-
/** The `config.toml` Herdr reads; `HERDR_CONFIG_PATH` names it outright. */
|
|
24
|
-
function herdrConfigPath(env = process.env, platform = process.platform) {
|
|
25
|
-
return env.HERDR_CONFIG_PATH || path.join(herdrConfigDir(env, platform), 'config.toml');
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* On Windows, Herdr uses this path itself as the GenericNamespaced pipe name;
|
|
29
|
-
* the file only contains the `pid:nanos` marker.
|
|
30
|
-
*/
|
|
31
|
-
function herdrEndpoint(socketPath, platform = process.platform) {
|
|
32
|
-
return platform === 'win32' ? `\\\\.\\pipe\\${socketPath}` : socketPath;
|
|
33
|
-
}
|
|
34
|
-
function resolveSocketPath(configuredPath = null, env = process.env) {
|
|
35
|
-
return configuredPath || env.HERDR_SOCKET_PATH || defaultSocketPath(env);
|
|
36
|
-
}
|
|
37
|
-
function inspectSocket(socketPath, options = {}) {
|
|
38
|
-
if (typeof socketPath !== 'string' || socketPath.length === 0) {
|
|
39
|
-
return { ok: false, reason: 'socket path is empty', path: socketPath };
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
const stat = fs.statSync(socketPath);
|
|
43
|
-
// Herdr leaves an ordinary marker file on Windows and connects through the pipe named by its path.
|
|
44
|
-
if (process.platform !== 'win32' && !stat.isSocket()) {
|
|
45
|
-
return { ok: false, reason: 'path is not a Unix socket', path: socketPath };
|
|
46
|
-
}
|
|
47
|
-
// Windows has no per-process uid to compare, so ownership checks naturally skip there.
|
|
48
|
-
if (options.requireOwner !== false &&
|
|
49
|
-
typeof process.getuid === 'function' &&
|
|
50
|
-
stat.uid !== process.getuid()) {
|
|
51
|
-
return {
|
|
52
|
-
ok: false,
|
|
53
|
-
reason: 'socket is not owned by the current user',
|
|
54
|
-
path: socketPath,
|
|
55
|
-
uid: stat.uid,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return { ok: true, path: socketPath, uid: stat.uid, mode: stat.mode };
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
const { code, message } = error;
|
|
62
|
-
return {
|
|
63
|
-
ok: false,
|
|
64
|
-
missing: code === 'ENOENT',
|
|
65
|
-
reason: code === 'ENOENT' ? 'socket does not exist' : message,
|
|
66
|
-
path: socketPath,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
export { defaultSocketPath, herdrConfigPath, herdrEndpoint, resolveSocketPath, inspectSocket };
|