@nordbyte/nordrelay 0.4.0 → 0.4.1

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.
@@ -49,8 +49,8 @@ function parseArgs(argv) {
49
49
  home: process.env.NORDRELAY_HOME || DEFAULT_HOME,
50
50
  dropPendingUpdates: !envFlag("NORDRELAY_KEEP_PENDING_UPDATES"),
51
51
  force: false,
52
- host: process.env.NORDRELAY_DASHBOARD_HOST || "127.0.0.1",
53
- port: Number.parseInt(process.env.NORDRELAY_DASHBOARD_PORT || "31878", 10),
52
+ host: undefined,
53
+ port: undefined,
54
54
  };
55
55
 
56
56
  for (let i = 0; i < copy.length; i += 1) {
@@ -175,9 +175,31 @@ async function readPid(pidFile) {
175
175
  }
176
176
  }
177
177
 
178
- async function commandStart(options) {
178
+ function resolveDashboardEndpoint(options, settings = {}) {
179
+ const host = options.host || process.env.NORDRELAY_DASHBOARD_HOST || "127.0.0.1";
180
+ const rawPort = options.port ?? Number.parseInt(process.env.NORDRELAY_DASHBOARD_PORT || "31878", 10);
181
+ if (!Number.isFinite(rawPort) || rawPort <= 0) {
182
+ if (settings.strict) {
183
+ throw new Error("Dashboard port must be a positive number.");
184
+ }
185
+ return { host, port: 31878 };
186
+ }
187
+ const port = rawPort;
188
+ return { host, port };
189
+ }
190
+
191
+ function formatDashboardUrl(endpoint) {
192
+ const host = endpoint.host || "127.0.0.1";
193
+ const displayHost = host === "0.0.0.0" || host === "" ? "127.0.0.1" : host === "::" ? "::1" : host;
194
+ const formattedHost = displayHost.includes(":") && !displayHost.startsWith("[") ? `[${displayHost}]` : displayHost;
195
+ const bindHint = displayHost === host ? "" : ` (binds ${host || "all interfaces"})`;
196
+ return `http://${formattedHost}:${endpoint.port}/${bindHint}`;
197
+ }
198
+
199
+ async function commandStart(options, settings = {}) {
179
200
  await mkdirp(options.home);
180
201
  loadEnvFiles(options.home);
202
+ const dashboard = resolveDashboardEndpoint(options);
181
203
 
182
204
  const currentPid = await readPid(options.pidFile);
183
205
  if (isProcessRunning(currentPid)) {
@@ -210,6 +232,9 @@ async function commandStart(options) {
210
232
  console.log(`Started ${APP_NAME} ${VERSION} with PID ${child.pid}`);
211
233
  console.log(`Workspace: ${state.workspace || "-"}`);
212
234
  console.log(`Mode: ${state.sessionMode || "per Telegram context"}`);
235
+ if (!settings.skipWebHint) {
236
+ console.log(`WebUI: ${formatDashboardUrl(dashboard)} (run \`nordrelay web\` to start it)`);
237
+ }
213
238
  console.log(`Log: ${options.logFile}`);
214
239
  return;
215
240
  }
@@ -225,9 +250,27 @@ async function commandStart(options) {
225
250
  }
226
251
 
227
252
  console.log(`Started ${APP_NAME} ${VERSION} with PID ${child.pid}`);
253
+ if (!settings.skipWebHint) {
254
+ console.log(`WebUI: ${formatDashboardUrl(dashboard)} (run \`nordrelay web\` to start it)`);
255
+ }
228
256
  console.log(`Startup is still in progress. Log: ${options.logFile}`);
229
257
  }
230
258
 
259
+ async function ensureConnectorStartedForWeb(options) {
260
+ const currentPid = await readPid(options.pidFile);
261
+ if (isProcessRunning(currentPid)) {
262
+ console.log(`NordRelay connector already running with PID ${currentPid}.`);
263
+ return;
264
+ }
265
+
266
+ console.log("Starting NordRelay connector...");
267
+ const previousExitCode = process.exitCode;
268
+ await commandStart(options, { skipWebHint: true });
269
+ if (process.exitCode && process.exitCode !== previousExitCode) {
270
+ throw new Error(`NordRelay connector failed to start. See ${options.logFile}.`);
271
+ }
272
+ }
273
+
231
274
  async function waitForState(stateFile, pid, timeoutMs) {
232
275
  const deadline = Date.now() + timeoutMs;
233
276
  while (Date.now() < deadline) {
@@ -268,6 +311,7 @@ async function commandStop(options) {
268
311
 
269
312
  async function commandStatus(options) {
270
313
  loadEnvFiles(options.home);
314
+ const dashboard = resolveDashboardEndpoint(options);
271
315
  const pid = await readPid(options.pidFile);
272
316
  const state = await readJson(options.stateFile, {});
273
317
  const running = isProcessRunning(pid);
@@ -282,6 +326,7 @@ async function commandStatus(options) {
282
326
  console.log(`OpenClaw CLI: ${state.openClawCli || "-"}`);
283
327
  console.log(`Claude Code CLI: ${state.claudeCodeCli || "-"}`);
284
328
  console.log(`OpenClaw Gateway: ${state.openClawGateway || process.env.OPENCLAW_GATEWAY_URL || "-"}`);
329
+ console.log(`WebUI: ${formatDashboardUrl(dashboard)}`);
285
330
  console.log(`Log: ${options.logFile}`);
286
331
  if (state.error) console.log(`Error: ${state.error}`);
287
332
  }
@@ -472,8 +517,8 @@ async function checkOpenClawGateway() {
472
517
  async function commandWeb(options) {
473
518
  await mkdirp(options.home);
474
519
  loadEnvFiles(options.home);
475
- const host = options.host || "127.0.0.1";
476
- const port = Number.isFinite(options.port) ? options.port : 31878;
520
+ const { host, port } = resolveDashboardEndpoint(options, { strict: true });
521
+ await ensureConnectorStartedForWeb(options);
477
522
  const entry = await resolveWebRuntimeEntry();
478
523
  if (!entry) {
479
524
  throw new Error(`Missing dashboard runtime. Run \`npm install\` and \`npm run build\` in ${RUNTIME_ROOT}.`);
@@ -564,21 +609,23 @@ async function commandForeground(options) {
564
609
  });
565
610
 
566
611
  const previousState = await readJson(options.stateFile, {});
612
+ const stoppedBySignal = exit.signal === "SIGTERM" || exit.signal === "SIGINT";
613
+ const stopped = exit.code === 0 || stoppedBySignal;
567
614
  await writeJsonAtomic(options.stateFile, {
568
- status: exit.code === 0 ? "stopped" : "error",
615
+ status: stopped ? "stopped" : "error",
569
616
  pid: process.pid,
570
617
  updatedAt: nowIso(),
571
618
  exitCode: exit.code,
572
619
  signal: exit.signal,
573
- error: exit.code === 0 ? undefined : previousState.error,
620
+ error: stopped ? undefined : previousState.error,
574
621
  logFile: options.logFile,
575
622
  });
576
623
 
577
- if (exit.signal) {
624
+ if (exit.signal && !stoppedBySignal) {
578
625
  process.kill(process.pid, exit.signal);
579
626
  return;
580
627
  }
581
- process.exit(exit.code ?? 0);
628
+ process.exit(stopped ? 0 : exit.code ?? 1);
582
629
  }
583
630
 
584
631
  async function resolveRuntimeEntry() {