@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.
Files changed (179) hide show
  1. package/.env.example +9 -0
  2. package/README.md +84 -1205
  3. package/dist/{access-control.js → access/access-control.js} +1 -1
  4. package/dist/{audit-log.js → access/audit-log.js} +32 -15
  5. package/dist/{session-locks.js → access/session-locks.js} +1 -1
  6. package/dist/{user-management.js → access/user-management.js} +1 -1
  7. package/dist/{claude-code-cli.js → agents/claude-code/claude-code-cli.js} +2 -2
  8. package/dist/{claude-code-session.js → agents/claude-code/claude-code-session.js} +1 -1
  9. package/dist/{codex-cli.js → agents/codex/codex-cli.js} +14 -5
  10. package/dist/{codex-session.js → agents/codex/codex-session.js} +2 -4
  11. package/dist/{hermes-cli.js → agents/hermes/hermes-cli.js} +2 -2
  12. package/dist/{hermes-launch.js → agents/hermes/hermes-launch.js} +1 -1
  13. package/dist/{hermes-session.js → agents/hermes/hermes-session.js} +1 -1
  14. package/dist/{openclaw-cli.js → agents/openclaw/openclaw-cli.js} +2 -2
  15. package/dist/{openclaw-launch.js → agents/openclaw/openclaw-launch.js} +1 -1
  16. package/dist/{openclaw-session.js → agents/openclaw/openclaw-session.js} +1 -1
  17. package/dist/{pi-cli.js → agents/pi/pi-cli.js} +2 -2
  18. package/dist/{pi-launch.js → agents/pi/pi-launch.js} +1 -1
  19. package/dist/{pi-session.js → agents/pi/pi-session.js} +1 -1
  20. package/dist/{adapter-conformance.js → agents/shared/adapter-conformance.js} +2 -2
  21. package/dist/{agent-activity.js → agents/shared/agent-activity.js} +5 -5
  22. package/dist/agents/shared/agent-auth-commands.js +30 -0
  23. package/dist/{agent-factory.js → agents/shared/agent-factory.js} +5 -5
  24. package/dist/{agent-feature-matrix.js → agents/shared/agent-feature-matrix.js} +2 -2
  25. package/dist/{agent-updates.js → agents/shared/agent-updates.js} +7 -7
  26. package/dist/{discord-artifacts.js → channels/discord/discord-artifacts.js} +4 -4
  27. package/dist/{discord-bot.js → channels/discord/discord-bot.js} +176 -451
  28. package/dist/{discord-channel-runtime.js → channels/discord/discord-channel-runtime.js} +2 -2
  29. package/dist/{discord-command-surface.js → channels/discord/discord-command-surface.js} +3 -3
  30. package/dist/{bot-rendering.js → channels/shared/bot-rendering.js} +6 -6
  31. package/dist/{channel-actions.js → channels/shared/channel-actions.js} +4 -4
  32. package/dist/channels/shared/channel-bridge-controller.js +69 -0
  33. package/dist/channels/shared/channel-cli-artifacts.js +51 -0
  34. package/dist/{channel-command-service.js → channels/shared/channel-command-service.js} +51 -28
  35. package/dist/channels/shared/channel-external-mirror-controller.js +193 -0
  36. package/dist/channels/shared/channel-external-monitor.js +52 -0
  37. package/dist/{channel-mirror-registry.js → channels/shared/channel-mirror-registry.js} +14 -6
  38. package/dist/{channel-peer-prompt.js → channels/shared/channel-peer-prompt.js} +3 -3
  39. package/dist/channels/shared/channel-prompt-queue.js +37 -0
  40. package/dist/{channel-turn-service.js → channels/shared/channel-turn-service.js} +25 -11
  41. package/dist/{context-key.js → channels/shared/context-key.js} +1 -1
  42. package/dist/{session-format.js → channels/shared/session-format.js} +2 -2
  43. package/dist/{slack-artifacts.js → channels/slack/slack-artifacts.js} +4 -4
  44. package/dist/{slack-bot.js → channels/slack/slack-bot.js} +171 -309
  45. package/dist/{slack-channel-runtime.js → channels/slack/slack-channel-runtime.js} +2 -2
  46. package/dist/{slack-command-surface.js → channels/slack/slack-command-surface.js} +2 -2
  47. package/dist/{slack-diagnostics.js → channels/slack/slack-diagnostics.js} +2 -2
  48. package/dist/{bot-ui.js → channels/telegram/bot-ui.js} +1 -1
  49. package/dist/{bot.js → channels/telegram/bot.js} +195 -430
  50. package/dist/{telegram-access-commands.js → channels/telegram/telegram-access-commands.js} +3 -3
  51. package/dist/{telegram-access-middleware.js → channels/telegram/telegram-access-middleware.js} +4 -4
  52. package/dist/{telegram-agent-commands.js → channels/telegram/telegram-agent-commands.js} +9 -9
  53. package/dist/{telegram-artifact-commands.js → channels/telegram/telegram-artifact-commands.js} +4 -4
  54. package/dist/{telegram-channel-runtime.js → channels/telegram/telegram-channel-runtime.js} +2 -2
  55. package/dist/{telegram-command-menu.js → channels/telegram/telegram-command-menu.js} +1 -1
  56. package/dist/{telegram-diagnostics-command.js → channels/telegram/telegram-diagnostics-command.js} +7 -7
  57. package/dist/{telegram-general-commands.js → channels/telegram/telegram-general-commands.js} +4 -4
  58. package/dist/{telegram-operational-commands.js → channels/telegram/telegram-operational-commands.js} +5 -5
  59. package/dist/{telegram-output.js → channels/telegram/telegram-output.js} +2 -2
  60. package/dist/{telegram-preference-commands.js → channels/telegram/telegram-preference-commands.js} +3 -3
  61. package/dist/{telegram-queue-commands.js → channels/telegram/telegram-queue-commands.js} +6 -6
  62. package/dist/{telegram-support-command.js → channels/telegram/telegram-support-command.js} +4 -4
  63. package/dist/{telegram-update-commands.js → channels/telegram/telegram-update-commands.js} +5 -5
  64. package/dist/{config-metadata.js → core/config-metadata.js} +8 -0
  65. package/dist/{config.js → core/config.js} +11 -3
  66. package/dist/core/pagination.js +22 -0
  67. package/dist/index.js +27 -23
  68. package/dist/peers/peer-discovery-jobs.js +206 -0
  69. package/dist/peers/peer-discovery.js +223 -0
  70. package/dist/peers/peer-health-monitor.js +49 -0
  71. package/dist/{peer-identity.js → peers/peer-identity.js} +50 -1
  72. package/dist/{peer-runtime-service.js → peers/peer-runtime-service.js} +29 -7
  73. package/dist/{peer-server.js → peers/peer-server.js} +3 -2
  74. package/dist/{peer-store.js → peers/peer-store.js} +96 -9
  75. package/dist/{peer-types.js → peers/peer-types.js} +28 -0
  76. package/dist/peers/peer-web-proxy-contract.js +129 -0
  77. package/dist/{metrics.js → runtime/metrics.js} +5 -3
  78. package/dist/{relay-artifact-service.js → runtime/relay-artifact-service.js} +1 -1
  79. package/dist/runtime/relay-auth-service.js +63 -0
  80. package/dist/runtime/relay-dashboard-service.js +139 -0
  81. package/dist/{relay-external-activity-monitor.js → runtime/relay-external-activity-monitor.js} +155 -53
  82. package/dist/{relay-queue-service.js → runtime/relay-queue-service.js} +1 -0
  83. package/dist/runtime/relay-runtime-active-sessions.js +387 -0
  84. package/dist/runtime/relay-runtime-dashboard.js +204 -0
  85. package/dist/{relay-runtime-helpers.js → runtime/relay-runtime-helpers.js} +3 -0
  86. package/dist/runtime/relay-runtime-prompt-queue-artifacts.js +311 -0
  87. package/dist/runtime/relay-runtime-sessions.js +631 -0
  88. package/dist/runtime/relay-runtime-trace.js +92 -0
  89. package/dist/runtime/relay-runtime-types.js +1 -0
  90. package/dist/runtime/relay-runtime-updates-jobs.js +366 -0
  91. package/dist/runtime/relay-runtime.js +461 -0
  92. package/dist/runtime/runtime-cache.js +117 -0
  93. package/dist/{prompt-store.js → state/prompt-store.js} +13 -1
  94. package/dist/{session-registry.js → state/session-registry.js} +3 -3
  95. package/dist/{operations.js → support/operations.js} +7 -7
  96. package/dist/{support-bundle.js → support/support-bundle.js} +1 -1
  97. package/dist/{web-api-contract.js → web/web-api-contract.js} +19 -3
  98. package/dist/web/web-api-types.js +1 -0
  99. package/dist/{web-dashboard-access-routes.js → web/web-dashboard-access-routes.js} +17 -14
  100. package/dist/{web-dashboard-artifact-routes.js → web/web-dashboard-artifact-routes.js} +6 -2
  101. package/dist/{web-dashboard-assets.js → web/web-dashboard-assets.js} +25 -2
  102. package/dist/{web-dashboard-http.js → web/web-dashboard-http.js} +41 -5
  103. package/dist/{web-dashboard-pages.js → web/web-dashboard-pages.js} +95 -30
  104. package/dist/{web-dashboard-peer-routes.js → web/web-dashboard-peer-routes.js} +121 -7
  105. package/dist/{web-dashboard-runtime-routes.js → web/web-dashboard-runtime-routes.js} +8 -1
  106. package/dist/web/web-dashboard-security.js +14 -0
  107. package/dist/{web-dashboard-session-routes.js → web/web-dashboard-session-routes.js} +29 -13
  108. package/dist/web/web-dashboard-ui.js +56 -0
  109. package/dist/{web-dashboard.js → web/web-dashboard.js} +132 -48
  110. package/dist/web/web-performance.js +62 -0
  111. package/dist/web/web-rate-limit.js +19 -0
  112. package/dist/{web-state.js → web/web-state.js} +107 -9
  113. package/dist/webui-assets/dashboard.css +398 -49
  114. package/dist/webui-assets/dashboard.js +1239 -103
  115. package/dist/webui-assets/favicon.ico +0 -0
  116. package/dist/webui-assets/favicon.png +0 -0
  117. package/dist/webui-assets/logo.png +0 -0
  118. package/package.json +6 -3
  119. package/plugins/nordrelay/scripts/nordrelay.mjs +346 -12
  120. package/plugins/nordrelay/scripts/service-installer.mjs +183 -0
  121. package/{launchd/start.sh → scripts/launchd-start.sh} +1 -1
  122. package/scripts/postinstall.mjs +122 -0
  123. package/dist/relay-runtime.js +0 -1916
  124. package/dist/runtime-cache.js +0 -57
  125. package/dist/web-dashboard-ui.js +0 -20
  126. /package/dist/{user-management-crypto.js → access/user-management-crypto.js} +0 -0
  127. /package/dist/{user-management-normalize.js → access/user-management-normalize.js} +0 -0
  128. /package/dist/{user-management-types.js → access/user-management-types.js} +0 -0
  129. /package/dist/{claude-code-auth.js → agents/claude-code/claude-code-auth.js} +0 -0
  130. /package/dist/{claude-code-launch.js → agents/claude-code/claude-code-launch.js} +0 -0
  131. /package/dist/{claude-code-state.js → agents/claude-code/claude-code-state.js} +0 -0
  132. /package/dist/{codex-auth.js → agents/codex/codex-auth.js} +0 -0
  133. /package/dist/{codex-config.js → agents/codex/codex-config.js} +0 -0
  134. /package/dist/{codex-launch.js → agents/codex/codex-launch.js} +0 -0
  135. /package/dist/{codex-state.js → agents/codex/codex-state.js} +0 -0
  136. /package/dist/{hermes-api.js → agents/hermes/hermes-api.js} +0 -0
  137. /package/dist/{hermes-auth.js → agents/hermes/hermes-auth.js} +0 -0
  138. /package/dist/{hermes-state.js → agents/hermes/hermes-state.js} +0 -0
  139. /package/dist/{openclaw-auth.js → agents/openclaw/openclaw-auth.js} +0 -0
  140. /package/dist/{openclaw-gateway.js → agents/openclaw/openclaw-gateway.js} +0 -0
  141. /package/dist/{openclaw-state.js → agents/openclaw/openclaw-state.js} +0 -0
  142. /package/dist/{pi-auth.js → agents/pi/pi-auth.js} +0 -0
  143. /package/dist/{pi-rpc.js → agents/pi/pi-rpc.js} +0 -0
  144. /package/dist/{pi-state.js → agents/pi/pi-state.js} +0 -0
  145. /package/dist/{agent-adapter.js → agents/shared/agent-adapter.js} +0 -0
  146. /package/dist/{agent.js → agents/shared/agent.js} +0 -0
  147. /package/dist/{artifacts.js → artifacts/artifacts.js} +0 -0
  148. /package/dist/{attachments.js → artifacts/attachments.js} +0 -0
  149. /package/dist/{voice.js → artifacts/voice.js} +0 -0
  150. /package/dist/{discord-rate-limit.js → channels/discord/discord-rate-limit.js} +0 -0
  151. /package/dist/{channel-adapter.js → channels/shared/channel-adapter.js} +0 -0
  152. /package/dist/{relay-runtime-types.js → channels/shared/channel-bridge-state.js} +0 -0
  153. /package/dist/{channel-command-catalog.js → channels/shared/channel-command-catalog.js} +0 -0
  154. /package/dist/{channel-command-core.js → channels/shared/channel-command-core.js} +0 -0
  155. /package/dist/{channel-prompt-engine.js → channels/shared/channel-prompt-engine.js} +0 -0
  156. /package/dist/{channel-runtime.js → channels/shared/channel-runtime.js} +0 -0
  157. /package/dist/{channel-turn-lifecycle.js → channels/shared/channel-turn-lifecycle.js} +0 -0
  158. /package/dist/{slack-rate-limit.js → channels/slack/slack-rate-limit.js} +0 -0
  159. /package/dist/{telegram-command-types.js → channels/telegram/telegram-command-types.js} +0 -0
  160. /package/dist/{telegram-rate-limit.js → channels/telegram/telegram-rate-limit.js} +0 -0
  161. /package/dist/{activity-events.js → core/activity-events.js} +0 -0
  162. /package/dist/{error-messages.js → core/error-messages.js} +0 -0
  163. /package/dist/{format.js → core/format.js} +0 -0
  164. /package/dist/{logger.js → core/logger.js} +0 -0
  165. /package/dist/{redaction.js → core/redaction.js} +0 -0
  166. /package/dist/{settings-service.js → core/settings-service.js} +0 -0
  167. /package/dist/{settings-wizard-test.js → core/settings-wizard-test.js} +0 -0
  168. /package/dist/{workspace-policy.js → core/workspace-policy.js} +0 -0
  169. /package/dist/{peer-auth.js → peers/peer-auth.js} +0 -0
  170. /package/dist/{peer-client.js → peers/peer-client.js} +0 -0
  171. /package/dist/{peer-context.js → peers/peer-context.js} +0 -0
  172. /package/dist/{peer-readiness.js → peers/peer-readiness.js} +0 -0
  173. /package/dist/{web-api-types.js → runtime/relay-runtime-delegate.js} +0 -0
  174. /package/dist/{remote-prompt.js → runtime/remote-prompt.js} +0 -0
  175. /package/dist/{bot-preferences.js → state/bot-preferences.js} +0 -0
  176. /package/dist/{job-store.js → state/job-store.js} +0 -0
  177. /package/dist/{persistence.js → state/persistence.js} +0 -0
  178. /package/dist/{state-backend.js → state/state-backend.js} +0 -0
  179. /package/dist/{zip-writer.js → support/zip-writer.js} +0 -0
@@ -0,0 +1,183 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const DEFAULT_SCRIPT_PATH = fileURLToPath(new URL("./nordrelay.mjs", import.meta.url));
7
+
8
+ function requireValue(argv, index, flag) {
9
+ const value = argv[index];
10
+ if (!value || value.startsWith("-")) {
11
+ throw new Error(`${flag} requires a value`);
12
+ }
13
+ return value;
14
+ }
15
+
16
+ function parseServiceFlags(argv) {
17
+ const copy = [...argv];
18
+ const subcommand = copy[0] && !copy[0].startsWith("-") ? copy.shift() : "status";
19
+ const flags = {
20
+ subcommand,
21
+ start: true,
22
+ name: process.platform === "win32" ? "NordRelay" : "nordrelay",
23
+ label: "io.nordbyte.nordrelay",
24
+ dryRun: false,
25
+ platform: process.platform,
26
+ };
27
+ for (let i = 0; i < copy.length; i += 1) {
28
+ const arg = copy[i];
29
+ if (arg === "--no-start") flags.start = false;
30
+ else if (arg === "--dry-run") flags.dryRun = true;
31
+ else if (arg === "--platform") flags.platform = requireValue(copy, ++i, arg);
32
+ else if (arg === "--name") flags.name = requireValue(copy, ++i, arg);
33
+ else if (arg === "--label") flags.label = requireValue(copy, ++i, arg);
34
+ }
35
+ return flags;
36
+ }
37
+
38
+ function serviceRunArgs(options) {
39
+ const args = ["service-run", "--home", options.home];
40
+ if (options.host) args.push("--host", options.host);
41
+ if (options.port) args.push("--port", String(options.port));
42
+ return args;
43
+ }
44
+
45
+ function buildSystemdUserServiceSpec(options, flags) {
46
+ const scriptPath = options.scriptPath || DEFAULT_SCRIPT_PATH;
47
+ const unitPath = path.join(os.homedir(), ".config", "systemd", "user", `${flags.name}.service`);
48
+ const execStart = [process.execPath, scriptPath, ...serviceRunArgs(options)].map(systemdQuote).join(" ");
49
+ const content = [
50
+ "[Unit]",
51
+ "Description=NordRelay connector and WebUI",
52
+ "After=network-online.target",
53
+ "",
54
+ "[Service]",
55
+ "Type=simple",
56
+ `ExecStart=${execStart}`,
57
+ "Restart=on-failure",
58
+ "RestartSec=5",
59
+ `Environment=NORDRELAY_HOME=${systemdQuote(options.home)}`,
60
+ "",
61
+ "[Install]",
62
+ "WantedBy=default.target",
63
+ "",
64
+ ].join("\n");
65
+ const action = flags.start ? "enable --now" : "enable";
66
+ return {
67
+ platform: "linux",
68
+ path: unitPath,
69
+ content,
70
+ commands: [
71
+ { command: "systemctl", args: ["--user", "daemon-reload"], label: "Reload systemd user units" },
72
+ { command: "systemctl", args: ["--user", "enable", flags.start ? "--now" : "", `${flags.name}.service`].filter(Boolean), label: `systemctl --user ${action} ${flags.name}.service` },
73
+ ],
74
+ };
75
+ }
76
+
77
+ function buildLaunchdServiceSpec(options, flags) {
78
+ const scriptPath = options.scriptPath || DEFAULT_SCRIPT_PATH;
79
+ const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", `${flags.label}.plist`);
80
+ const domain = launchdDomain();
81
+ const commands = [
82
+ { command: "launchctl", args: ["bootout", domain, plistPath], label: `Unload existing ${flags.label}`, settings: { allowFailure: true } },
83
+ ];
84
+ if (flags.start) {
85
+ commands.push(
86
+ { command: "launchctl", args: ["bootstrap", domain, plistPath], label: `Load ${flags.label}` },
87
+ { command: "launchctl", args: ["enable", `${domain}/${flags.label}`], label: `Enable ${flags.label}`, settings: { allowFailure: true } },
88
+ { command: "launchctl", args: ["kickstart", "-k", `${domain}/${flags.label}`], label: `Start ${flags.label}`, settings: { allowFailure: true } },
89
+ );
90
+ }
91
+ return {
92
+ platform: "darwin",
93
+ path: plistPath,
94
+ content: launchdPlist(flags.label, process.execPath, [scriptPath, ...serviceRunArgs(options)], options.home),
95
+ commands,
96
+ };
97
+ }
98
+
99
+ function buildWindowsTaskServiceSpec(options, flags) {
100
+ const scriptPath = options.scriptPath || DEFAULT_SCRIPT_PATH;
101
+ const taskCommand = windowsTaskCommand(process.execPath, [scriptPath, ...serviceRunArgs(options)]);
102
+ const commands = [
103
+ { command: "schtasks", args: ["/Create", "/F", "/SC", "ONLOGON", "/TN", flags.name, "/TR", taskCommand], label: `Create Windows task ${flags.name}` },
104
+ ];
105
+ if (flags.start) {
106
+ commands.push({ command: "schtasks", args: ["/Run", "/TN", flags.name], label: `Start Windows task ${flags.name}`, settings: { allowFailure: true } });
107
+ }
108
+ return {
109
+ platform: "win32",
110
+ path: flags.name,
111
+ content: "",
112
+ commands,
113
+ };
114
+ }
115
+
116
+ function serviceInstallSpec(options, flags) {
117
+ if (flags.platform === "darwin") return buildLaunchdServiceSpec(options, flags);
118
+ if (flags.platform === "win32") return buildWindowsTaskServiceSpec(options, flags);
119
+ return buildSystemdUserServiceSpec(options, flags);
120
+ }
121
+
122
+ function systemdQuote(value) {
123
+ return `"${String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
124
+ }
125
+
126
+ function launchdDomain() {
127
+ return `gui/${process.getuid?.() ?? ""}`;
128
+ }
129
+
130
+ function launchdPlist(label, command, args, home) {
131
+ const programArguments = [command, ...args]
132
+ .map((value) => ` <string>${xmlEscape(value)}</string>`)
133
+ .join("\n");
134
+ return [
135
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
136
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">",
137
+ "<plist version=\"1.0\">",
138
+ "<dict>",
139
+ " <key>Label</key>",
140
+ ` <string>${xmlEscape(label)}</string>`,
141
+ " <key>ProgramArguments</key>",
142
+ " <array>",
143
+ programArguments,
144
+ " </array>",
145
+ " <key>EnvironmentVariables</key>",
146
+ " <dict>",
147
+ " <key>NORDRELAY_HOME</key>",
148
+ ` <string>${xmlEscape(home)}</string>`,
149
+ " </dict>",
150
+ " <key>RunAtLoad</key>",
151
+ " <true/>",
152
+ " <key>KeepAlive</key>",
153
+ " <true/>",
154
+ " <key>StandardOutPath</key>",
155
+ ` <string>${xmlEscape(path.join(home, "service.log"))}</string>`,
156
+ " <key>StandardErrorPath</key>",
157
+ ` <string>${xmlEscape(path.join(home, "service.log"))}</string>`,
158
+ "</dict>",
159
+ "</plist>",
160
+ "",
161
+ ].join("\n");
162
+ }
163
+
164
+ function windowsTaskCommand(command, args) {
165
+ return [command, ...args].map((part) => `"${String(part).replace(/"/g, '""')}"`).join(" ");
166
+ }
167
+
168
+ function xmlEscape(value) {
169
+ return String(value)
170
+ .replace(/&/g, "&amp;")
171
+ .replace(/</g, "&lt;")
172
+ .replace(/>/g, "&gt;")
173
+ .replace(/"/g, "&quot;")
174
+ .replace(/'/g, "&apos;");
175
+ }
176
+
177
+ export {
178
+ buildLaunchdServiceSpec,
179
+ buildSystemdUserServiceSpec,
180
+ buildWindowsTaskServiceSpec,
181
+ parseServiceFlags,
182
+ serviceInstallSpec,
183
+ };
@@ -5,4 +5,4 @@ export PATH="${PATH:-/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/s
5
5
  : "${HOME:?Set HOME before starting nordrelay}"
6
6
 
7
7
  cd "${NORDRELAY_SOURCE_ROOT:-$HOME/projects/nordrelay}"
8
- exec node dist/index.js
8
+ exec node plugins/nordrelay/scripts/nordrelay.mjs foreground
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
3
+ import path from "node:path";
4
+
5
+ const commandName = process.platform === "win32" ? "nordrelay.cmd" : "nordrelay";
6
+
7
+ function isGlobalInstall() {
8
+ return process.env.NORDRELAY_POSTINSTALL_CHECK === "1"
9
+ || process.env.npm_config_global === "true"
10
+ || process.env.npm_config_location === "global";
11
+ }
12
+
13
+ function npmPrefix() {
14
+ const configured = process.env.npm_config_prefix;
15
+ if (configured) {
16
+ return configured;
17
+ }
18
+ const npmExec = process.env.npm_execpath || "npm";
19
+ try {
20
+ return execFileSync(npmExec, ["prefix", "-g"], {
21
+ encoding: "utf8",
22
+ shell: process.platform === "win32",
23
+ stdio: ["ignore", "pipe", "ignore"],
24
+ }).trim();
25
+ } catch {
26
+ return "";
27
+ }
28
+ }
29
+
30
+ function globalBinDir(prefix, platform = process.platform) {
31
+ if (!prefix) {
32
+ return "";
33
+ }
34
+ return platform === "win32" ? prefix : path.join(prefix, "bin");
35
+ }
36
+
37
+ function normalizePathEntry(entry, platform = process.platform) {
38
+ const resolved = path.resolve(entry);
39
+ return platform === "win32" || platform === "darwin" ? resolved.toLowerCase() : resolved;
40
+ }
41
+
42
+ function pathContains(dir, envPath = process.env.PATH || "", platform = process.platform) {
43
+ if (!dir) {
44
+ return false;
45
+ }
46
+ const wanted = normalizePathEntry(dir, platform);
47
+ return envPath
48
+ .split(path.delimiter)
49
+ .filter(Boolean)
50
+ .map((entry) => normalizePathEntry(entry, platform))
51
+ .includes(wanted);
52
+ }
53
+
54
+ function shellProfile(platform = process.platform, shell = process.env.SHELL || "") {
55
+ if (platform === "win32") {
56
+ return "";
57
+ }
58
+ if (platform === "darwin") {
59
+ return shell.endsWith("/bash") ? "~/.bash_profile" : "~/.zshrc";
60
+ }
61
+ return shell.endsWith("/zsh") ? "~/.zshrc" : "~/.bashrc";
62
+ }
63
+
64
+ function quote(value) {
65
+ return `"${String(value).replaceAll('"', '\\"')}"`;
66
+ }
67
+
68
+ function warning(prefix, binDir, platform = process.env.NORDRELAY_POSTINSTALL_PLATFORM || process.platform) {
69
+ const commandPath = path.join(binDir, commandName);
70
+ if (platform === "win32") {
71
+ return [
72
+ "",
73
+ "NordRelay installed, but the npm global bin directory is not in your PATH.",
74
+ 'The `nordrelay` command may fail with "command not found".',
75
+ "",
76
+ `npm global bin: ${binDir}`,
77
+ "",
78
+ "Add it to your user PATH, then open a new terminal:",
79
+ ` [Environment]::SetEnvironmentVariable("Path", $env:Path + ";${binDir}", "User")`,
80
+ "",
81
+ "Or run NordRelay directly:",
82
+ ` & ${quote(commandPath)} init`,
83
+ "",
84
+ ].join("\n");
85
+ }
86
+
87
+ const profile = shellProfile(platform);
88
+ return [
89
+ "",
90
+ "NordRelay installed, but the npm global bin directory is not in your PATH.",
91
+ 'The `nordrelay` command may fail with "command not found".',
92
+ "",
93
+ `npm global bin: ${binDir}`,
94
+ "",
95
+ `Add this to ${profile}:`,
96
+ ` export PATH=${quote(binDir + ":$PATH")}`,
97
+ "",
98
+ "Then reload your shell and initialize NordRelay:",
99
+ ` source ${profile}`,
100
+ " nordrelay init",
101
+ "",
102
+ "Or run NordRelay directly:",
103
+ ` ${quote(commandPath)} init`,
104
+ "",
105
+ prefix ? "" : "If the npm prefix could not be detected, run `npm prefix -g` and add its bin directory to PATH.",
106
+ ].filter(Boolean).join("\n");
107
+ }
108
+
109
+ function main() {
110
+ if (!isGlobalInstall()) {
111
+ return;
112
+ }
113
+ const prefix = npmPrefix();
114
+ const platform = process.env.NORDRELAY_POSTINSTALL_PLATFORM || process.platform;
115
+ const binDir = globalBinDir(prefix, platform);
116
+ if (!binDir || pathContains(binDir, process.env.PATH || "", platform)) {
117
+ return;
118
+ }
119
+ console.warn(warning(prefix, binDir, platform));
120
+ }
121
+
122
+ main();