@nordbyte/nordrelay 0.3.0 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nordbyte/nordrelay",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Remote control plane for coding agents across messaging channels.",
5
5
  "type": "module",
6
6
  "author": "Ricardo",
@@ -16,7 +16,7 @@ const SCRIPT_PATH = fileURLToPath(import.meta.url);
16
16
  const PLUGIN_ROOT = path.resolve(path.dirname(SCRIPT_PATH), "..");
17
17
  const DEFAULT_MARKETPLACE_ROOT = path.resolve(PLUGIN_ROOT, "../..");
18
18
  const RUNTIME_ROOT = findRuntimeRoot();
19
- const DEFAULT_HOME = path.join(os.homedir(), ".codex", "nordrelay");
19
+ const DEFAULT_HOME = path.join(os.homedir(), ".nordrelay");
20
20
 
21
21
  function nowIso() {
22
22
  return new Date().toISOString();
@@ -77,16 +77,11 @@ async function mkdirp(dir) {
77
77
  }
78
78
 
79
79
  function loadEnvFiles(home) {
80
- const files = [
81
- path.join(process.cwd(), ".env"),
82
- path.join(RUNTIME_ROOT, ".env"),
83
- path.join(PLUGIN_ROOT, ".env"),
84
- path.join(home, "nordrelay.env"),
85
- ];
80
+ const envPath = process.env.NORDRELAY_ENV_FILE
81
+ ? path.resolve(process.env.NORDRELAY_ENV_FILE)
82
+ : path.join(home, "nordrelay.env");
86
83
 
87
- for (const envPath of files) {
88
- loadEnvFile(envPath);
89
- }
84
+ loadEnvFile(envPath);
90
85
 
91
86
  normalizeEnvAliases();
92
87
  }
@@ -207,7 +202,7 @@ async function commandStart(options) {
207
202
  await fsp.rm(options.pidFile, { force: true });
208
203
  }
209
204
  console.log(`Startup failed. Log: ${options.logFile}`);
210
- console.log(state.error || "Unknown error");
205
+ console.log(state.error || await readStartupError(options.logFile) || "Unknown error");
211
206
  process.exitCode = 1;
212
207
  return;
213
208
  }
@@ -332,7 +327,8 @@ async function commandDoctor(options) {
332
327
  checks.push(check("Codex CLI", Boolean(findExecutable(process.env.CODEX_CLI_PATH || "codex")), process.env.CODEX_CLI_PATH || findExecutable("codex") || "not found", process.env.NORDRELAY_CODEX_ENABLED === "false" ? "warn" : "fail"));
333
328
  checks.push(check("Pi CLI", Boolean(findExecutable(process.env.PI_CLI_PATH || "pi")), process.env.PI_CLI_PATH || findExecutable("pi") || "not found", process.env.NORDRELAY_PI_ENABLED === "true" ? "fail" : "warn"));
334
329
  checks.push(check("ffmpeg", Boolean(findExecutable("ffmpeg")), findExecutable("ffmpeg") || "not found", "warn"));
335
- checks.push(check("State backend", validateStateBackend(), `NORDRELAY_STATE_BACKEND=${process.env.NORDRELAY_STATE_BACKEND ?? "json"}`));
330
+ const stateBackendCheck = validateStateBackend();
331
+ checks.push(check("State backend", stateBackendCheck.ok, stateBackendCheck.detail));
336
332
  checks.push(check("Runtime entry", Boolean(await resolveRuntimeEntry()), RUNTIME_ROOT));
337
333
 
338
334
  for (const item of checks) {
@@ -439,12 +435,14 @@ async function commandForeground(options) {
439
435
  child.once("exit", (code, signal) => resolve({ code, signal }));
440
436
  });
441
437
 
438
+ const previousState = await readJson(options.stateFile, {});
442
439
  await writeJsonAtomic(options.stateFile, {
443
440
  status: exit.code === 0 ? "stopped" : "error",
444
441
  pid: process.pid,
445
442
  updatedAt: nowIso(),
446
443
  exitCode: exit.code,
447
444
  signal: exit.signal,
445
+ error: exit.code === 0 ? undefined : previousState.error,
448
446
  logFile: options.logFile,
449
447
  });
450
448
 
@@ -550,13 +548,40 @@ function findExecutable(command) {
550
548
 
551
549
  function validateStateBackend() {
552
550
  const backend = process.env.NORDRELAY_STATE_BACKEND || "json";
553
- if (backend === "json") return true;
554
- if (backend !== "sqlite") return false;
551
+ if (backend === "json") return { ok: true, detail: "NORDRELAY_STATE_BACKEND=json" };
552
+ if (backend !== "sqlite") return { ok: false, detail: `Invalid NORDRELAY_STATE_BACKEND=${backend}` };
555
553
  try {
556
- require("better-sqlite3");
557
- return true;
554
+ const Database = require("better-sqlite3");
555
+ const filePath = path.join(process.cwd(), ".nordrelay", "state.sqlite");
556
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
557
+ const db = new Database(filePath);
558
+ db.exec([
559
+ "CREATE TABLE IF NOT EXISTS documents (",
560
+ "key TEXT PRIMARY KEY,",
561
+ "json TEXT NOT NULL,",
562
+ "updated_at TEXT NOT NULL",
563
+ ")",
564
+ ].join(" "));
565
+ db.close?.();
566
+ return { ok: true, detail: `NORDRELAY_STATE_BACKEND=sqlite (${filePath})` };
567
+ } catch (error) {
568
+ return {
569
+ ok: false,
570
+ detail: `NORDRELAY_STATE_BACKEND=sqlite failed: ${error instanceof Error ? error.message : String(error)}`,
571
+ };
572
+ }
573
+ }
574
+
575
+ async function readStartupError(logFile) {
576
+ try {
577
+ const lines = (await fsp.readFile(logFile, "utf8")).split(/\r?\n/).filter(Boolean).slice(-80).reverse();
578
+ const startupLine = lines.find((line) => line.includes("Failed to start NordRelay:"));
579
+ if (startupLine) return startupLine.replace(/^.*Failed to start NordRelay:\s*/, "");
580
+ const errorLine = lines.find((line) => /\bERROR\b/i.test(line));
581
+ if (errorLine) return errorLine;
582
+ return lines[0] || null;
558
583
  } catch {
559
- return false;
584
+ return null;
560
585
  }
561
586
  }
562
587