@nordbyte/nordrelay 0.8.1 → 0.8.3
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/.env.example +9 -0
- package/README.md +84 -1205
- package/dist/{access-control.js → access/access-control.js} +1 -1
- package/dist/{audit-log.js → access/audit-log.js} +32 -15
- package/dist/{session-locks.js → access/session-locks.js} +1 -1
- package/dist/{user-management.js → access/user-management.js} +1 -1
- package/dist/{claude-code-cli.js → agents/claude-code/claude-code-cli.js} +2 -2
- package/dist/{claude-code-session.js → agents/claude-code/claude-code-session.js} +1 -1
- package/dist/{codex-cli.js → agents/codex/codex-cli.js} +14 -5
- package/dist/{codex-session.js → agents/codex/codex-session.js} +2 -4
- package/dist/{hermes-cli.js → agents/hermes/hermes-cli.js} +2 -2
- package/dist/{hermes-launch.js → agents/hermes/hermes-launch.js} +1 -1
- package/dist/{hermes-session.js → agents/hermes/hermes-session.js} +1 -1
- package/dist/{openclaw-cli.js → agents/openclaw/openclaw-cli.js} +2 -2
- package/dist/{openclaw-launch.js → agents/openclaw/openclaw-launch.js} +1 -1
- package/dist/{openclaw-session.js → agents/openclaw/openclaw-session.js} +1 -1
- package/dist/{pi-cli.js → agents/pi/pi-cli.js} +2 -2
- package/dist/{pi-launch.js → agents/pi/pi-launch.js} +1 -1
- package/dist/{pi-session.js → agents/pi/pi-session.js} +1 -1
- package/dist/{adapter-conformance.js → agents/shared/adapter-conformance.js} +2 -2
- package/dist/{agent-activity.js → agents/shared/agent-activity.js} +5 -5
- package/dist/agents/shared/agent-auth-commands.js +30 -0
- package/dist/{agent-factory.js → agents/shared/agent-factory.js} +5 -5
- package/dist/{agent-feature-matrix.js → agents/shared/agent-feature-matrix.js} +2 -2
- package/dist/{agent-updates.js → agents/shared/agent-updates.js} +7 -7
- package/dist/{discord-artifacts.js → channels/discord/discord-artifacts.js} +4 -4
- package/dist/{discord-bot.js → channels/discord/discord-bot.js} +176 -451
- package/dist/{discord-channel-runtime.js → channels/discord/discord-channel-runtime.js} +2 -2
- package/dist/{discord-command-surface.js → channels/discord/discord-command-surface.js} +3 -3
- package/dist/{bot-rendering.js → channels/shared/bot-rendering.js} +6 -6
- package/dist/{channel-actions.js → channels/shared/channel-actions.js} +4 -4
- package/dist/channels/shared/channel-bridge-controller.js +69 -0
- package/dist/channels/shared/channel-cli-artifacts.js +51 -0
- package/dist/{channel-command-service.js → channels/shared/channel-command-service.js} +51 -28
- package/dist/channels/shared/channel-external-mirror-controller.js +193 -0
- package/dist/channels/shared/channel-external-monitor.js +52 -0
- package/dist/{channel-mirror-registry.js → channels/shared/channel-mirror-registry.js} +14 -6
- package/dist/{channel-peer-prompt.js → channels/shared/channel-peer-prompt.js} +3 -3
- package/dist/channels/shared/channel-prompt-queue.js +37 -0
- package/dist/{channel-turn-service.js → channels/shared/channel-turn-service.js} +25 -11
- package/dist/{context-key.js → channels/shared/context-key.js} +1 -1
- package/dist/{session-format.js → channels/shared/session-format.js} +2 -2
- package/dist/{slack-artifacts.js → channels/slack/slack-artifacts.js} +4 -4
- package/dist/{slack-bot.js → channels/slack/slack-bot.js} +171 -309
- package/dist/{slack-channel-runtime.js → channels/slack/slack-channel-runtime.js} +2 -2
- package/dist/{slack-command-surface.js → channels/slack/slack-command-surface.js} +2 -2
- package/dist/{slack-diagnostics.js → channels/slack/slack-diagnostics.js} +2 -2
- package/dist/{bot-ui.js → channels/telegram/bot-ui.js} +1 -1
- package/dist/{bot.js → channels/telegram/bot.js} +195 -430
- package/dist/{telegram-access-commands.js → channels/telegram/telegram-access-commands.js} +3 -3
- package/dist/{telegram-access-middleware.js → channels/telegram/telegram-access-middleware.js} +4 -4
- package/dist/{telegram-agent-commands.js → channels/telegram/telegram-agent-commands.js} +9 -9
- package/dist/{telegram-artifact-commands.js → channels/telegram/telegram-artifact-commands.js} +4 -4
- package/dist/{telegram-channel-runtime.js → channels/telegram/telegram-channel-runtime.js} +2 -2
- package/dist/{telegram-command-menu.js → channels/telegram/telegram-command-menu.js} +1 -1
- package/dist/{telegram-diagnostics-command.js → channels/telegram/telegram-diagnostics-command.js} +7 -7
- package/dist/{telegram-general-commands.js → channels/telegram/telegram-general-commands.js} +4 -4
- package/dist/{telegram-operational-commands.js → channels/telegram/telegram-operational-commands.js} +5 -5
- package/dist/{telegram-output.js → channels/telegram/telegram-output.js} +2 -2
- package/dist/{telegram-preference-commands.js → channels/telegram/telegram-preference-commands.js} +3 -3
- package/dist/{telegram-queue-commands.js → channels/telegram/telegram-queue-commands.js} +6 -6
- package/dist/{telegram-support-command.js → channels/telegram/telegram-support-command.js} +4 -4
- package/dist/{telegram-update-commands.js → channels/telegram/telegram-update-commands.js} +5 -5
- package/dist/{config-metadata.js → core/config-metadata.js} +8 -0
- package/dist/{config.js → core/config.js} +11 -3
- package/dist/core/pagination.js +22 -0
- package/dist/index.js +27 -23
- package/dist/peers/peer-discovery-jobs.js +206 -0
- package/dist/peers/peer-discovery.js +223 -0
- package/dist/peers/peer-health-monitor.js +49 -0
- package/dist/{peer-identity.js → peers/peer-identity.js} +50 -1
- package/dist/{peer-runtime-service.js → peers/peer-runtime-service.js} +29 -7
- package/dist/{peer-server.js → peers/peer-server.js} +3 -2
- package/dist/{peer-store.js → peers/peer-store.js} +96 -9
- package/dist/{peer-types.js → peers/peer-types.js} +28 -0
- package/dist/peers/peer-web-proxy-contract.js +129 -0
- package/dist/{metrics.js → runtime/metrics.js} +5 -3
- package/dist/{relay-artifact-service.js → runtime/relay-artifact-service.js} +1 -1
- package/dist/runtime/relay-auth-service.js +63 -0
- package/dist/runtime/relay-dashboard-service.js +139 -0
- package/dist/{relay-external-activity-monitor.js → runtime/relay-external-activity-monitor.js} +155 -53
- package/dist/{relay-queue-service.js → runtime/relay-queue-service.js} +1 -0
- package/dist/runtime/relay-runtime-active-sessions.js +387 -0
- package/dist/runtime/relay-runtime-dashboard.js +204 -0
- package/dist/{relay-runtime-helpers.js → runtime/relay-runtime-helpers.js} +3 -0
- package/dist/runtime/relay-runtime-prompt-queue-artifacts.js +311 -0
- package/dist/runtime/relay-runtime-sessions.js +631 -0
- package/dist/runtime/relay-runtime-trace.js +92 -0
- package/dist/runtime/relay-runtime-types.js +1 -0
- package/dist/runtime/relay-runtime-updates-jobs.js +366 -0
- package/dist/runtime/relay-runtime.js +461 -0
- package/dist/runtime/runtime-cache.js +117 -0
- package/dist/{prompt-store.js → state/prompt-store.js} +13 -1
- package/dist/{session-registry.js → state/session-registry.js} +3 -3
- package/dist/{operations.js → support/operations.js} +7 -7
- package/dist/{support-bundle.js → support/support-bundle.js} +1 -1
- package/dist/{web-api-contract.js → web/web-api-contract.js} +19 -3
- package/dist/web/web-api-types.js +1 -0
- package/dist/{web-dashboard-access-routes.js → web/web-dashboard-access-routes.js} +17 -14
- package/dist/{web-dashboard-artifact-routes.js → web/web-dashboard-artifact-routes.js} +6 -2
- package/dist/{web-dashboard-assets.js → web/web-dashboard-assets.js} +25 -2
- package/dist/{web-dashboard-http.js → web/web-dashboard-http.js} +41 -5
- package/dist/{web-dashboard-pages.js → web/web-dashboard-pages.js} +95 -30
- package/dist/{web-dashboard-peer-routes.js → web/web-dashboard-peer-routes.js} +121 -7
- package/dist/{web-dashboard-runtime-routes.js → web/web-dashboard-runtime-routes.js} +8 -1
- package/dist/web/web-dashboard-security.js +14 -0
- package/dist/{web-dashboard-session-routes.js → web/web-dashboard-session-routes.js} +29 -13
- package/dist/web/web-dashboard-ui.js +56 -0
- package/dist/{web-dashboard.js → web/web-dashboard.js} +132 -48
- package/dist/web/web-performance.js +62 -0
- package/dist/web/web-rate-limit.js +19 -0
- package/dist/{web-state.js → web/web-state.js} +107 -9
- package/dist/webui-assets/dashboard.css +398 -49
- package/dist/webui-assets/dashboard.js +1239 -103
- package/dist/webui-assets/favicon.ico +0 -0
- package/dist/webui-assets/favicon.png +0 -0
- package/dist/webui-assets/logo.png +0 -0
- package/package.json +6 -3
- package/plugins/nordrelay/scripts/nordrelay.mjs +346 -12
- package/plugins/nordrelay/scripts/service-installer.mjs +183 -0
- package/{launchd/start.sh → scripts/launchd-start.sh} +1 -1
- package/scripts/postinstall.mjs +122 -0
- package/dist/relay-runtime.js +0 -1916
- package/dist/runtime-cache.js +0 -57
- package/dist/web-dashboard-ui.js +0 -20
- /package/dist/{user-management-crypto.js → access/user-management-crypto.js} +0 -0
- /package/dist/{user-management-normalize.js → access/user-management-normalize.js} +0 -0
- /package/dist/{user-management-types.js → access/user-management-types.js} +0 -0
- /package/dist/{claude-code-auth.js → agents/claude-code/claude-code-auth.js} +0 -0
- /package/dist/{claude-code-launch.js → agents/claude-code/claude-code-launch.js} +0 -0
- /package/dist/{claude-code-state.js → agents/claude-code/claude-code-state.js} +0 -0
- /package/dist/{codex-auth.js → agents/codex/codex-auth.js} +0 -0
- /package/dist/{codex-config.js → agents/codex/codex-config.js} +0 -0
- /package/dist/{codex-launch.js → agents/codex/codex-launch.js} +0 -0
- /package/dist/{codex-state.js → agents/codex/codex-state.js} +0 -0
- /package/dist/{hermes-api.js → agents/hermes/hermes-api.js} +0 -0
- /package/dist/{hermes-auth.js → agents/hermes/hermes-auth.js} +0 -0
- /package/dist/{hermes-state.js → agents/hermes/hermes-state.js} +0 -0
- /package/dist/{openclaw-auth.js → agents/openclaw/openclaw-auth.js} +0 -0
- /package/dist/{openclaw-gateway.js → agents/openclaw/openclaw-gateway.js} +0 -0
- /package/dist/{openclaw-state.js → agents/openclaw/openclaw-state.js} +0 -0
- /package/dist/{pi-auth.js → agents/pi/pi-auth.js} +0 -0
- /package/dist/{pi-rpc.js → agents/pi/pi-rpc.js} +0 -0
- /package/dist/{pi-state.js → agents/pi/pi-state.js} +0 -0
- /package/dist/{agent-adapter.js → agents/shared/agent-adapter.js} +0 -0
- /package/dist/{agent.js → agents/shared/agent.js} +0 -0
- /package/dist/{artifacts.js → artifacts/artifacts.js} +0 -0
- /package/dist/{attachments.js → artifacts/attachments.js} +0 -0
- /package/dist/{voice.js → artifacts/voice.js} +0 -0
- /package/dist/{discord-rate-limit.js → channels/discord/discord-rate-limit.js} +0 -0
- /package/dist/{channel-adapter.js → channels/shared/channel-adapter.js} +0 -0
- /package/dist/{relay-runtime-types.js → channels/shared/channel-bridge-state.js} +0 -0
- /package/dist/{channel-command-catalog.js → channels/shared/channel-command-catalog.js} +0 -0
- /package/dist/{channel-command-core.js → channels/shared/channel-command-core.js} +0 -0
- /package/dist/{channel-prompt-engine.js → channels/shared/channel-prompt-engine.js} +0 -0
- /package/dist/{channel-runtime.js → channels/shared/channel-runtime.js} +0 -0
- /package/dist/{channel-turn-lifecycle.js → channels/shared/channel-turn-lifecycle.js} +0 -0
- /package/dist/{slack-rate-limit.js → channels/slack/slack-rate-limit.js} +0 -0
- /package/dist/{telegram-command-types.js → channels/telegram/telegram-command-types.js} +0 -0
- /package/dist/{telegram-rate-limit.js → channels/telegram/telegram-rate-limit.js} +0 -0
- /package/dist/{activity-events.js → core/activity-events.js} +0 -0
- /package/dist/{error-messages.js → core/error-messages.js} +0 -0
- /package/dist/{format.js → core/format.js} +0 -0
- /package/dist/{logger.js → core/logger.js} +0 -0
- /package/dist/{redaction.js → core/redaction.js} +0 -0
- /package/dist/{settings-service.js → core/settings-service.js} +0 -0
- /package/dist/{settings-wizard-test.js → core/settings-wizard-test.js} +0 -0
- /package/dist/{workspace-policy.js → core/workspace-policy.js} +0 -0
- /package/dist/{peer-auth.js → peers/peer-auth.js} +0 -0
- /package/dist/{peer-client.js → peers/peer-client.js} +0 -0
- /package/dist/{peer-context.js → peers/peer-context.js} +0 -0
- /package/dist/{peer-readiness.js → peers/peer-readiness.js} +0 -0
- /package/dist/{web-api-types.js → runtime/relay-runtime-delegate.js} +0 -0
- /package/dist/{remote-prompt.js → runtime/remote-prompt.js} +0 -0
- /package/dist/{bot-preferences.js → state/bot-preferences.js} +0 -0
- /package/dist/{job-store.js → state/job-store.js} +0 -0
- /package/dist/{persistence.js → state/persistence.js} +0 -0
- /package/dist/{state-backend.js → state/state-backend.js} +0 -0
- /package/dist/{zip-writer.js → support/zip-writer.js} +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nordbyte/nordrelay",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Remote control plane for coding agents across messaging channels.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Ricardo",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"files": [
|
|
35
35
|
"dist/",
|
|
36
36
|
"plugins/",
|
|
37
|
-
"
|
|
37
|
+
"scripts/postinstall.mjs",
|
|
38
|
+
"scripts/launchd-start.sh",
|
|
38
39
|
".env.example",
|
|
39
40
|
"Dockerfile",
|
|
40
41
|
"docker-compose.yml"
|
|
@@ -43,14 +44,16 @@
|
|
|
43
44
|
"api:check": "node --import tsx scripts/generate-web-api-routes.mjs --check",
|
|
44
45
|
"api:generate": "node --import tsx scripts/generate-web-api-routes.mjs",
|
|
45
46
|
"build": "node scripts/clean-dist.mjs && npm run api:generate && tsc && node scripts/build-web-assets.mjs",
|
|
46
|
-
"check": "node --check plugins/nordrelay/scripts/nordrelay.mjs && npm run api:check && tsc --noEmit && npm run webui:check && node scripts/build-web-assets.mjs --check && node --import tsx scripts/generate-env-example.mjs --check",
|
|
47
|
+
"check": "node --check plugins/nordrelay/scripts/nordrelay.mjs && node --check plugins/nordrelay/scripts/service-installer.mjs && npm run api:check && tsc --noEmit && npm run webui:check && node scripts/build-web-assets.mjs --check && node --import tsx scripts/generate-env-example.mjs --check && npm run size:check",
|
|
47
48
|
"dev": "tsx src/index.ts",
|
|
48
49
|
"env:check": "node --import tsx scripts/generate-env-example.mjs --check",
|
|
49
50
|
"env:generate": "node --import tsx scripts/generate-env-example.mjs",
|
|
50
51
|
"foreground": "node plugins/nordrelay/scripts/nordrelay.mjs foreground",
|
|
52
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
51
53
|
"prepack": "npm run build",
|
|
52
54
|
"prepublishOnly": "npm run check && npm test && npm run build",
|
|
53
55
|
"security:audit": "npm audit --audit-level=high",
|
|
56
|
+
"size:check": "node scripts/check-module-size.mjs",
|
|
54
57
|
"sbom": "npm sbom --json > sbom.json",
|
|
55
58
|
"status": "node plugins/nordrelay/scripts/nordrelay.mjs status",
|
|
56
59
|
"start": "node plugins/nordrelay/scripts/nordrelay.mjs start",
|
|
@@ -6,8 +6,15 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
import readline from "node:readline/promises";
|
|
9
|
-
import { spawn } from "node:child_process";
|
|
9
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
10
10
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
|
+
import {
|
|
12
|
+
buildLaunchdServiceSpec,
|
|
13
|
+
buildSystemdUserServiceSpec,
|
|
14
|
+
buildWindowsTaskServiceSpec,
|
|
15
|
+
parseServiceFlags,
|
|
16
|
+
serviceInstallSpec,
|
|
17
|
+
} from "./service-installer.mjs";
|
|
11
18
|
|
|
12
19
|
const FALLBACK_VERSION = "0.3.1";
|
|
13
20
|
const require = createRequire(import.meta.url);
|
|
@@ -238,6 +245,7 @@ function formatDashboardUrl(endpoint) {
|
|
|
238
245
|
async function commandStart(options, settings = {}) {
|
|
239
246
|
await mkdirp(options.home);
|
|
240
247
|
loadEnvFiles(options.home);
|
|
248
|
+
warnIfCliPathMissing();
|
|
241
249
|
await prepareRuntimeForLaunch(options);
|
|
242
250
|
const dashboard = resolveDashboardEndpoint(options);
|
|
243
251
|
|
|
@@ -431,6 +439,46 @@ async function commandStatus(options) {
|
|
|
431
439
|
if (state.error) console.log(`Error: ${state.error}`);
|
|
432
440
|
}
|
|
433
441
|
|
|
442
|
+
function cliPathDiagnostics() {
|
|
443
|
+
const resolved = findExecutable(APP_NAME);
|
|
444
|
+
const globalBin = resolveNpmGlobalBinDir();
|
|
445
|
+
const candidate = globalBin ? path.join(globalBin, process.platform === "win32" ? `${APP_NAME}.cmd` : APP_NAME) : null;
|
|
446
|
+
const pathContainsGlobalBin = globalBin ? pathListIncludes(globalBin) : false;
|
|
447
|
+
const expected = [candidate, SCRIPT_PATH].filter(Boolean);
|
|
448
|
+
const resolvedKnown = Boolean(resolved && expected.some((item) => pathsEqualOrLinked(resolved, item)));
|
|
449
|
+
const hint = globalBin
|
|
450
|
+
? process.platform === "win32"
|
|
451
|
+
? `Add ${globalBin} to PATH and reopen the terminal.`
|
|
452
|
+
: `Add ${globalBin} to PATH, for example: export PATH="${globalBin}:$PATH"`
|
|
453
|
+
: "Ensure the npm global bin directory is on PATH.";
|
|
454
|
+
return {
|
|
455
|
+
ok: Boolean(resolved),
|
|
456
|
+
resolved,
|
|
457
|
+
globalBin,
|
|
458
|
+
pathContainsGlobalBin,
|
|
459
|
+
expected: candidate,
|
|
460
|
+
resolvedKnown,
|
|
461
|
+
detail: resolved
|
|
462
|
+
? resolvedKnown
|
|
463
|
+
? resolved
|
|
464
|
+
: `${resolved} (different command target; current wrapper: ${SCRIPT_PATH})`
|
|
465
|
+
: `not found on PATH${globalBin ? `; npm global bin: ${globalBin}` : ""}`,
|
|
466
|
+
hint,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function warnIfCliPathMissing() {
|
|
471
|
+
if (envFlag("NORDRELAY_SUPPRESS_PATH_WARNING")) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const diagnostics = cliPathDiagnostics();
|
|
475
|
+
if (diagnostics.ok) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
console.warn(`Warning: \`${APP_NAME}\` is not available on PATH.`);
|
|
479
|
+
console.warn(`Hint: ${diagnostics.hint}`);
|
|
480
|
+
}
|
|
481
|
+
|
|
434
482
|
async function commandUpdate(options) {
|
|
435
483
|
await mkdirp(options.home);
|
|
436
484
|
loadEnvFiles(options.home);
|
|
@@ -671,6 +719,7 @@ function quoteWindowsCmdArg(value) {
|
|
|
671
719
|
|
|
672
720
|
async function commandInit(options) {
|
|
673
721
|
await mkdirp(options.home);
|
|
722
|
+
warnIfCliPathMissing();
|
|
674
723
|
const envPath = path.join(options.home, "nordrelay.env");
|
|
675
724
|
const userStore = await createUserStore(options.home);
|
|
676
725
|
if (fs.existsSync(envPath) && !options.force) {
|
|
@@ -818,7 +867,7 @@ function parsePeerFlags(argv) {
|
|
|
818
867
|
const copy = [...argv];
|
|
819
868
|
const subcommand = copy[0] && !copy[0].startsWith("-") ? copy.shift() : "list";
|
|
820
869
|
const flags = { subcommand, url: undefined };
|
|
821
|
-
if (["add", "test", "check", "revoke"].includes(subcommand) && copy[0] && !copy[0].startsWith("-")) {
|
|
870
|
+
if (["add", "test", "check", "revoke", "trust", "rotate"].includes(subcommand) && copy[0] && !copy[0].startsWith("-")) {
|
|
822
871
|
flags.url = copy.shift();
|
|
823
872
|
flags.id = flags.url;
|
|
824
873
|
}
|
|
@@ -881,6 +930,7 @@ async function commandPeer(options) {
|
|
|
881
930
|
if (peer.lastSeenAt) console.log(` Last seen: ${peer.lastSeenAt}`);
|
|
882
931
|
if (peer.lastLatencyMs !== undefined) console.log(` Latency: ${peer.lastLatencyMs}ms`);
|
|
883
932
|
if (peer.remoteVersion) console.log(` Remote version: ${peer.remoteVersion}`);
|
|
933
|
+
if (peer.trustStatus) console.log(` Trust: ${peer.trustStatus}${peer.trustWarnings?.length ? ` (${peer.trustWarnings.join("; ")})` : ""}`);
|
|
884
934
|
if (peer.lastError) console.log(` Last error: ${peer.lastError}`);
|
|
885
935
|
}
|
|
886
936
|
return;
|
|
@@ -957,7 +1007,187 @@ async function commandPeer(options) {
|
|
|
957
1007
|
return;
|
|
958
1008
|
}
|
|
959
1009
|
|
|
960
|
-
|
|
1010
|
+
if (flags.subcommand === "trust") {
|
|
1011
|
+
const id = flags.id || await ask(null, "Peer id", "");
|
|
1012
|
+
const peer = store.get(id);
|
|
1013
|
+
if (!peer?.url) throw new Error("Peer URL is required before TLS trust can be updated.");
|
|
1014
|
+
const probe = await clientMod.checkPeerIdentityEndpoint(peer.url, { timeoutMs: 5000 });
|
|
1015
|
+
if (!probe.ok || !probe.identity) throw new Error(`Peer identity could not be verified: ${probe.detail}`);
|
|
1016
|
+
if (probe.identity.nodeId !== peer.nodeId || probe.identity.publicKey !== peer.publicKey || probe.identity.fingerprint !== peer.fingerprint) {
|
|
1017
|
+
throw new Error("Peer identity changed. Re-pair this peer instead of trusting the TLS fingerprint.");
|
|
1018
|
+
}
|
|
1019
|
+
const updated = store.updatePeerTlsFingerprint(peer.id, probe.tlsFingerprint);
|
|
1020
|
+
console.log(`Trusted TLS fingerprint for ${updated.name}: ${updated.tlsFingerprint || "-"}`);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
if (flags.subcommand === "rotate") {
|
|
1025
|
+
const id = flags.id || await ask(null, "Peer id", "");
|
|
1026
|
+
const url = process.env.NORDRELAY_PEER_PUBLIC_URL || `${process.env.NORDRELAY_PEER_TLS_ENABLED === "false" ? "http" : "https"}://${process.env.NORDRELAY_PEER_HOST || "127.0.0.1"}:${process.env.NORDRELAY_PEER_PORT || "31979"}`;
|
|
1027
|
+
const created = store.createRotationInvitation(id, { expiresInMs: Number.isFinite(flags.expiresMinutes) ? flags.expiresMinutes * 60 * 1000 : undefined });
|
|
1028
|
+
console.log(`Rotation invite for ${created.peer.name} (${created.peer.id}).`);
|
|
1029
|
+
console.log(`Pairing code: ${created.code}`);
|
|
1030
|
+
console.log(`Expires: ${created.invitation.expiresAt}`);
|
|
1031
|
+
console.log(`Command: nordrelay peer add ${url} --code ${created.code}`);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
throw new Error("Usage: nordrelay peer [identity|list|invite|add|test|check|trust|rotate|revoke]");
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
async function commandService(options) {
|
|
1039
|
+
await mkdirp(options.home);
|
|
1040
|
+
loadEnvFiles(options.home);
|
|
1041
|
+
warnIfCliPathMissing();
|
|
1042
|
+
const flags = parseServiceFlags(options.rawFlags);
|
|
1043
|
+
const specOptions = { ...options, scriptPath: SCRIPT_PATH };
|
|
1044
|
+
|
|
1045
|
+
if (flags.subcommand === "install") {
|
|
1046
|
+
if (flags.dryRun) {
|
|
1047
|
+
printServiceInstallDryRun(specOptions, flags);
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
if (flags.platform === "darwin") {
|
|
1051
|
+
await installLaunchdService(specOptions, flags);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
if (flags.platform === "win32") {
|
|
1055
|
+
await installWindowsTask(specOptions, flags);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
await installSystemdUserService(specOptions, flags);
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (flags.subcommand === "uninstall" || flags.subcommand === "remove") {
|
|
1063
|
+
if (flags.platform === "darwin") {
|
|
1064
|
+
await uninstallLaunchdService(flags);
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (flags.platform === "win32") {
|
|
1068
|
+
await uninstallWindowsTask(flags);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
await uninstallSystemdUserService(flags);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (flags.subcommand === "status") {
|
|
1076
|
+
await commandServiceStatus(flags);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
throw new Error("Usage: nordrelay service [install|uninstall|status] [--no-start] [--name <name>] [--label <label>]");
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
async function installSystemdUserService(options, flags) {
|
|
1084
|
+
const spec = buildSystemdUserServiceSpec(options, flags);
|
|
1085
|
+
const unitDir = path.dirname(spec.path);
|
|
1086
|
+
const unitPath = spec.path;
|
|
1087
|
+
await mkdirp(unitDir);
|
|
1088
|
+
await fsp.writeFile(unitPath, spec.content);
|
|
1089
|
+
console.log(`Installed systemd user service: ${unitPath}`);
|
|
1090
|
+
for (const command of spec.commands) {
|
|
1091
|
+
runPlatformCommand(command.command, command.args, command.label, command.settings);
|
|
1092
|
+
}
|
|
1093
|
+
console.log(`Status: nordrelay service status`);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
async function uninstallSystemdUserService(flags) {
|
|
1097
|
+
runPlatformCommand("systemctl", ["--user", "disable", "--now", `${flags.name}.service`], `Disable ${flags.name}.service`);
|
|
1098
|
+
const unitPath = path.join(os.homedir(), ".config", "systemd", "user", `${flags.name}.service`);
|
|
1099
|
+
await fsp.rm(unitPath, { force: true });
|
|
1100
|
+
runPlatformCommand("systemctl", ["--user", "daemon-reload"], "Reload systemd user units");
|
|
1101
|
+
console.log(`Removed systemd user service: ${unitPath}`);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
async function installLaunchdService(options, flags) {
|
|
1105
|
+
const spec = buildLaunchdServiceSpec(options, flags);
|
|
1106
|
+
const launchAgentsDir = path.dirname(spec.path);
|
|
1107
|
+
const plistPath = spec.path;
|
|
1108
|
+
await mkdirp(launchAgentsDir);
|
|
1109
|
+
await fsp.writeFile(plistPath, spec.content);
|
|
1110
|
+
console.log(`Installed launchd service: ${plistPath}`);
|
|
1111
|
+
for (const command of spec.commands) {
|
|
1112
|
+
runPlatformCommand(command.command, command.args, command.label, command.settings);
|
|
1113
|
+
}
|
|
1114
|
+
if (!flags.start) {
|
|
1115
|
+
const domain = launchdDomain();
|
|
1116
|
+
console.log(`Start later with: launchctl bootstrap ${domain} ${plistPath}`);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
async function uninstallLaunchdService(flags) {
|
|
1121
|
+
const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", `${flags.label}.plist`);
|
|
1122
|
+
const domain = `gui/${process.getuid?.() ?? ""}`;
|
|
1123
|
+
runPlatformCommand("launchctl", ["bootout", domain, plistPath], `Unload ${flags.label}`, { allowFailure: true });
|
|
1124
|
+
await fsp.rm(plistPath, { force: true });
|
|
1125
|
+
console.log(`Removed launchd service: ${plistPath}`);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
async function installWindowsTask(options, flags) {
|
|
1129
|
+
const spec = buildWindowsTaskServiceSpec(options, flags);
|
|
1130
|
+
for (const command of spec.commands) {
|
|
1131
|
+
runPlatformCommand(command.command, command.args, command.label, command.settings);
|
|
1132
|
+
}
|
|
1133
|
+
console.log(`Installed Windows task: ${flags.name}`);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
async function uninstallWindowsTask(flags) {
|
|
1137
|
+
runPlatformCommand("schtasks", ["/Delete", "/F", "/TN", flags.name], `Delete Windows task ${flags.name}`, { allowFailure: true });
|
|
1138
|
+
console.log(`Removed Windows task: ${flags.name}`);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
async function commandServiceStatus(flags) {
|
|
1142
|
+
if (process.platform === "darwin") {
|
|
1143
|
+
const domain = `gui/${process.getuid?.() ?? ""}`;
|
|
1144
|
+
runPlatformCommand("launchctl", ["print", `${domain}/${flags.label}`], `launchd status ${flags.label}`, { allowFailure: true });
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
if (process.platform === "win32") {
|
|
1148
|
+
runPlatformCommand("schtasks", ["/Query", "/TN", flags.name], `Windows task status ${flags.name}`, { allowFailure: true });
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
runPlatformCommand("systemctl", ["--user", "status", `${flags.name}.service`, "--no-pager"], `systemd user status ${flags.name}.service`, { allowFailure: true });
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function printServiceInstallDryRun(options, flags) {
|
|
1155
|
+
const spec = serviceInstallSpec(options, flags);
|
|
1156
|
+
console.log(`Service install dry-run (${spec.platform})`);
|
|
1157
|
+
console.log(`Target: ${spec.path}`);
|
|
1158
|
+
if (spec.content) {
|
|
1159
|
+
console.log("--- file content ---");
|
|
1160
|
+
console.log(spec.content.trimEnd());
|
|
1161
|
+
}
|
|
1162
|
+
console.log("--- commands ---");
|
|
1163
|
+
for (const command of spec.commands) {
|
|
1164
|
+
console.log(formatCommand(command.command, command.args));
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
function launchdDomain() {
|
|
1169
|
+
return `gui/${process.getuid?.() ?? ""}`;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
function runPlatformCommand(command, args, label, settings = {}) {
|
|
1173
|
+
const resolved = findExecutable(command);
|
|
1174
|
+
if (!resolved) {
|
|
1175
|
+
console.log(`${label}: ${command} not found. Run this step manually if this platform service manager is available.`);
|
|
1176
|
+
return false;
|
|
1177
|
+
}
|
|
1178
|
+
const useShell = isWindowsShellScript(resolved);
|
|
1179
|
+
console.log(`${label}: ${formatCommand(resolved, args)}`);
|
|
1180
|
+
const result = spawnSync(useShell ? formatShellCommand(resolved, args) : resolved, useShell ? [] : args, {
|
|
1181
|
+
cwd: RUNTIME_ROOT,
|
|
1182
|
+
env: process.env,
|
|
1183
|
+
shell: useShell,
|
|
1184
|
+
stdio: "inherit",
|
|
1185
|
+
windowsHide: false,
|
|
1186
|
+
});
|
|
1187
|
+
if (result.status !== 0 && !settings.allowFailure) {
|
|
1188
|
+
throw new Error(`${label} failed with exit code ${result.status ?? "unknown"}`);
|
|
1189
|
+
}
|
|
1190
|
+
return result.status === 0;
|
|
961
1191
|
}
|
|
962
1192
|
|
|
963
1193
|
function parseUserFlags(argv) {
|
|
@@ -1092,6 +1322,11 @@ async function commandDoctor(options) {
|
|
|
1092
1322
|
const userSnapshot = userStore?.snapshot();
|
|
1093
1323
|
const checks = [];
|
|
1094
1324
|
checks.push(check("Node.js >= 22", Number.parseInt(process.versions.node.split(".")[0], 10) >= 22, process.version));
|
|
1325
|
+
const cliPath = cliPathDiagnostics();
|
|
1326
|
+
checks.push(check("NordRelay CLI on PATH", cliPath.ok, cliPath.ok ? cliPath.detail : `${cliPath.detail}; ${cliPath.hint}`, "warn"));
|
|
1327
|
+
if (cliPath.globalBin) {
|
|
1328
|
+
checks.push(check("npm global bin on PATH", cliPath.pathContainsGlobalBin, cliPath.globalBin, "warn"));
|
|
1329
|
+
}
|
|
1095
1330
|
const telegramRequested = process.env.TELEGRAM_ENABLED !== "false";
|
|
1096
1331
|
const discordRequested = process.env.DISCORD_ENABLED === "true";
|
|
1097
1332
|
const slackRequested = process.env.SLACK_ENABLED === "true";
|
|
@@ -1245,11 +1480,20 @@ async function checkOpenClawGateway() {
|
|
|
1245
1480
|
async function commandWeb(options) {
|
|
1246
1481
|
await mkdirp(options.home);
|
|
1247
1482
|
loadEnvFiles(options.home);
|
|
1483
|
+
warnIfCliPathMissing();
|
|
1248
1484
|
await prepareRuntimeForLaunch(options);
|
|
1249
1485
|
await ensureConnectorStartedForWeb(options);
|
|
1250
1486
|
await startWebDashboard(options, { detached: false });
|
|
1251
1487
|
}
|
|
1252
1488
|
|
|
1489
|
+
async function commandServiceRun(options) {
|
|
1490
|
+
await mkdirp(options.home);
|
|
1491
|
+
loadEnvFiles(options.home);
|
|
1492
|
+
await prepareRuntimeForLaunch(options);
|
|
1493
|
+
await ensureConnectorStartedForWeb(options);
|
|
1494
|
+
await startWebDashboard(options, { detached: false, stopConnectorOnExit: true });
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1253
1497
|
async function startWebDashboard(options, settings = {}) {
|
|
1254
1498
|
await mkdirp(options.home);
|
|
1255
1499
|
loadEnvFiles(options.home);
|
|
@@ -1335,6 +1579,9 @@ async function startWebDashboard(options, settings = {}) {
|
|
|
1335
1579
|
exitCode: exit.code,
|
|
1336
1580
|
signal: exit.signal,
|
|
1337
1581
|
});
|
|
1582
|
+
if (settings.stopConnectorOnExit) {
|
|
1583
|
+
await commandStop(options, { keepWeb: true });
|
|
1584
|
+
}
|
|
1338
1585
|
if (exit.signal) {
|
|
1339
1586
|
process.kill(process.pid, exit.signal);
|
|
1340
1587
|
return;
|
|
@@ -1486,7 +1733,7 @@ function runtimeBuildStatus() {
|
|
|
1486
1733
|
]);
|
|
1487
1734
|
const distTargets = [
|
|
1488
1735
|
path.join(RUNTIME_ROOT, "dist", "index.js"),
|
|
1489
|
-
path.join(RUNTIME_ROOT, "dist", "web-dashboard.js"),
|
|
1736
|
+
path.join(RUNTIME_ROOT, "dist", "web", "web-dashboard.js"),
|
|
1490
1737
|
path.join(RUNTIME_ROOT, "dist", "webui-assets", "dashboard.js"),
|
|
1491
1738
|
path.join(RUNTIME_ROOT, "dist", "webui-assets", "dashboard.css"),
|
|
1492
1739
|
];
|
|
@@ -1583,12 +1830,12 @@ async function runInteractiveStep(label, command, args, settings = {}) {
|
|
|
1583
1830
|
}
|
|
1584
1831
|
|
|
1585
1832
|
async function resolveWebRuntimeEntry() {
|
|
1586
|
-
const distEntry = path.join(RUNTIME_ROOT, "dist", "web-dashboard.js");
|
|
1833
|
+
const distEntry = path.join(RUNTIME_ROOT, "dist", "web", "web-dashboard.js");
|
|
1587
1834
|
if (fs.existsSync(distEntry)) {
|
|
1588
1835
|
return { command: process.execPath, args: [distEntry] };
|
|
1589
1836
|
}
|
|
1590
1837
|
|
|
1591
|
-
const tsEntry = path.join(RUNTIME_ROOT, "src", "web-dashboard.ts");
|
|
1838
|
+
const tsEntry = path.join(RUNTIME_ROOT, "src", "web", "web-dashboard.ts");
|
|
1592
1839
|
const tsxBin = path.join(RUNTIME_ROOT, "node_modules", ".bin", process.platform === "win32" ? "tsx.cmd" : "tsx");
|
|
1593
1840
|
if (fs.existsSync(tsEntry) && fs.existsSync(tsxBin)) {
|
|
1594
1841
|
return { command: tsxBin, args: [tsEntry] };
|
|
@@ -1712,7 +1959,7 @@ function findExecutable(command, pathValue = process.env.PATH, pathextValue = pr
|
|
|
1712
1959
|
if (command.includes(path.sep) && fs.existsSync(command)) return command;
|
|
1713
1960
|
const paths = (pathValue || "").split(path.delimiter);
|
|
1714
1961
|
const extensions = process.platform === "win32"
|
|
1715
|
-
?
|
|
1962
|
+
? windowsExecutableExtensions(pathextValue)
|
|
1716
1963
|
: [""];
|
|
1717
1964
|
for (const dir of paths) {
|
|
1718
1965
|
for (const extension of extensions) {
|
|
@@ -1723,6 +1970,79 @@ function findExecutable(command, pathValue = process.env.PATH, pathextValue = pr
|
|
|
1723
1970
|
return null;
|
|
1724
1971
|
}
|
|
1725
1972
|
|
|
1973
|
+
function resolveNpmGlobalBinDir(env = process.env) {
|
|
1974
|
+
const prefix = resolveNpmGlobalPrefix(env);
|
|
1975
|
+
if (!prefix) {
|
|
1976
|
+
return null;
|
|
1977
|
+
}
|
|
1978
|
+
return process.platform === "win32" ? prefix : path.join(prefix, "bin");
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
function resolveNpmGlobalPrefix(env = process.env) {
|
|
1982
|
+
if (env.npm_config_prefix) {
|
|
1983
|
+
return path.resolve(env.npm_config_prefix);
|
|
1984
|
+
}
|
|
1985
|
+
const npm = resolveNpmSpawnCommand(env);
|
|
1986
|
+
if (!npm) {
|
|
1987
|
+
return null;
|
|
1988
|
+
}
|
|
1989
|
+
const command = npm.shell
|
|
1990
|
+
? formatShellCommand(npm.command, [...npm.argsPrefix, "prefix", "-g"])
|
|
1991
|
+
: npm.command;
|
|
1992
|
+
const args = npm.shell ? [] : [...npm.argsPrefix, "prefix", "-g"];
|
|
1993
|
+
const result = spawnSync(command, args, {
|
|
1994
|
+
cwd: os.homedir(),
|
|
1995
|
+
env,
|
|
1996
|
+
shell: npm.shell,
|
|
1997
|
+
encoding: "utf8",
|
|
1998
|
+
windowsHide: true,
|
|
1999
|
+
timeout: 5000,
|
|
2000
|
+
});
|
|
2001
|
+
if (result.status !== 0) {
|
|
2002
|
+
return null;
|
|
2003
|
+
}
|
|
2004
|
+
const prefix = String(result.stdout || "").trim().split(/\r?\n/).at(-1)?.trim();
|
|
2005
|
+
return prefix ? path.resolve(prefix) : null;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
function pathListIncludes(directory, pathValue = process.env.PATH) {
|
|
2009
|
+
const normalized = normalizePathForCompare(directory);
|
|
2010
|
+
return (pathValue || "")
|
|
2011
|
+
.split(path.delimiter)
|
|
2012
|
+
.filter(Boolean)
|
|
2013
|
+
.some((entry) => normalizePathForCompare(entry) === normalized);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
function pathsEqualOrLinked(left, right) {
|
|
2017
|
+
if (!left || !right) {
|
|
2018
|
+
return false;
|
|
2019
|
+
}
|
|
2020
|
+
const normalizedLeft = normalizePathForCompare(left);
|
|
2021
|
+
const normalizedRight = normalizePathForCompare(right);
|
|
2022
|
+
if (normalizedLeft === normalizedRight) {
|
|
2023
|
+
return true;
|
|
2024
|
+
}
|
|
2025
|
+
try {
|
|
2026
|
+
return normalizePathForCompare(fs.realpathSync(left)) === normalizePathForCompare(fs.realpathSync(right));
|
|
2027
|
+
} catch {
|
|
2028
|
+
return false;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
function normalizePathForCompare(value) {
|
|
2033
|
+
const resolved = path.resolve(value || "");
|
|
2034
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
function windowsExecutableExtensions(pathextValue) {
|
|
2038
|
+
const pathext = (pathextValue || ".COM;.EXE;.BAT;.CMD")
|
|
2039
|
+
.split(";")
|
|
2040
|
+
.map((extension) => extension.trim())
|
|
2041
|
+
.filter(Boolean)
|
|
2042
|
+
.map((extension) => extension.startsWith(".") ? extension : `.${extension}`);
|
|
2043
|
+
return [...new Set([...pathext, ""])];
|
|
2044
|
+
}
|
|
2045
|
+
|
|
1726
2046
|
function isWindowsShellScript(filePath) {
|
|
1727
2047
|
return process.platform === "win32" && /\.(?:cmd|bat)$/i.test(filePath);
|
|
1728
2048
|
}
|
|
@@ -1783,6 +2103,7 @@ function printHelp() {
|
|
|
1783
2103
|
console.log(" init Create local config and first admin user");
|
|
1784
2104
|
console.log(" user Manage users, groups, and channel links");
|
|
1785
2105
|
console.log(" peer Manage secure NordRelay peer federation");
|
|
2106
|
+
console.log(" service Install, remove, or inspect the OS service");
|
|
1786
2107
|
console.log(" doctor Validate the local setup");
|
|
1787
2108
|
console.log(" web, dashboard Start the WebUI and connector");
|
|
1788
2109
|
console.log(" start Start the connector");
|
|
@@ -1797,6 +2118,7 @@ function printHelp() {
|
|
|
1797
2118
|
console.log(" --home <path> Runtime home directory");
|
|
1798
2119
|
console.log(" --host <host> WebUI bind host");
|
|
1799
2120
|
console.log(" --port <port> WebUI port");
|
|
2121
|
+
console.log(" service install --dry-run [--platform linux|darwin|win32]");
|
|
1800
2122
|
console.log(" --build Build source runtime before start/web/restart");
|
|
1801
2123
|
console.log(" --force Overwrite existing config during init");
|
|
1802
2124
|
console.log(" --help, -h Show this help");
|
|
@@ -1815,6 +2137,7 @@ async function main() {
|
|
|
1815
2137
|
if (options.command === "init") return commandInit(options);
|
|
1816
2138
|
if (options.command === "user") return commandUser(options);
|
|
1817
2139
|
if (options.command === "peer") return commandPeer(options);
|
|
2140
|
+
if (options.command === "service") return commandService(options);
|
|
1818
2141
|
if (options.command === "doctor") return commandDoctor(options);
|
|
1819
2142
|
if (options.command === "update") return commandUpdate(options);
|
|
1820
2143
|
if (options.command === "web" || options.command === "dashboard") return commandWeb(options);
|
|
@@ -1831,18 +2154,29 @@ async function main() {
|
|
|
1831
2154
|
return;
|
|
1832
2155
|
}
|
|
1833
2156
|
if (options.command === "foreground") return commandForeground(options);
|
|
2157
|
+
if (options.command === "service-run") return commandServiceRun(options);
|
|
1834
2158
|
if (options.command === "--version" || options.command === "version") {
|
|
1835
2159
|
console.log(`${APP_NAME} ${VERSION}`);
|
|
1836
2160
|
return;
|
|
1837
2161
|
}
|
|
1838
2162
|
|
|
1839
2163
|
console.error(`Unknown command: ${options.command}`);
|
|
1840
|
-
console.error("Usage: nordrelay [init|user|peer|doctor|web|start|stop|restart|status|update|foreground|version]");
|
|
2164
|
+
console.error("Usage: nordrelay [init|user|peer|service|doctor|web|start|stop|restart|status|update|foreground|version]");
|
|
1841
2165
|
console.error("Run `nordrelay --help` for details.");
|
|
1842
2166
|
process.exitCode = 2;
|
|
1843
2167
|
}
|
|
1844
2168
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
2169
|
+
export {
|
|
2170
|
+
buildLaunchdServiceSpec,
|
|
2171
|
+
buildSystemdUserServiceSpec,
|
|
2172
|
+
buildWindowsTaskServiceSpec,
|
|
2173
|
+
parseServiceFlags,
|
|
2174
|
+
serviceInstallSpec,
|
|
2175
|
+
};
|
|
2176
|
+
|
|
2177
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === SCRIPT_PATH) {
|
|
2178
|
+
main().catch((error) => {
|
|
2179
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
2180
|
+
process.exitCode = 1;
|
|
2181
|
+
});
|
|
2182
|
+
}
|