copilot-hub 0.1.12 → 0.1.13

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": "copilot-hub",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Copilot Hub CLI and runtime bundle",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -11,6 +11,7 @@ const runtimeDir = path.join(repoRoot, ".copilot-hub");
11
11
  const pidsDir = path.join(runtimeDir, "pids");
12
12
  const logsDir = path.join(repoRoot, "logs");
13
13
  const daemonStatePath = path.join(pidsDir, "daemon.json");
14
+ const lastStartupErrorPath = path.join(runtimeDir, "last-startup-error.json");
14
15
  const daemonLogPath = path.join(logsDir, "service-daemon.log");
15
16
  const controlPlaneLogPath = path.join(logsDir, "control-plane.log");
16
17
  const agentEngineLogPath = path.join(logsDir, "agent-engine.log");
@@ -106,6 +107,7 @@ async function runDaemonLoop() {
106
107
  while (!state.stopping) {
107
108
  const ensureResult = runSupervisor("ensure", { allowFailure: true });
108
109
  if (ensureResult.ok) {
110
+ clearLastStartupError();
109
111
  if (failureCount > 0) {
110
112
  console.log("[daemon] workers recovered.");
111
113
  }
@@ -115,6 +117,7 @@ async function runDaemonLoop() {
115
117
  }
116
118
  const fatal = detectFatalStartupError(ensureResult);
117
119
  if (fatal) {
120
+ writeLastStartupError(fatal);
118
121
  console.error(`[daemon] fatal startup error: ${fatal.reason}`);
119
122
  console.error(`[daemon] action required: ${fatal.action}`);
120
123
  state.stopping = true;
@@ -158,6 +161,7 @@ function showDaemonStatus() {
158
161
  console.log(`running: ${running ? "yes" : "no"}`);
159
162
  console.log(`pid: ${running ? String(pid) : "-"}`);
160
163
  console.log(`logFile: ${daemonLogPath}`);
164
+ printLastStartupError();
161
165
  if (!fs.existsSync(supervisorScriptPath)) {
162
166
  console.log("\n(worker status unavailable: supervisor script missing)");
163
167
  return;
@@ -395,20 +399,18 @@ function getErrorMessage(error) {
395
399
  return String(error ?? "Unknown error.");
396
400
  }
397
401
  function detectFatalStartupError(ensureResult) {
398
- const evidence = [
402
+ const evidenceChunks = [
399
403
  String(ensureResult?.combinedOutput ?? ""),
400
404
  readLogTail(controlPlaneLogPath, 120),
401
405
  readLogTail(agentEngineLogPath, 120),
402
- ]
403
- .map((chunk) => String(chunk ?? "").trim())
404
- .filter(Boolean)
405
- .join("\n")
406
- .toLowerCase();
407
- const missingHubToken = evidence.includes("hub telegram token is missing") && evidence.includes("hub_telegram_token");
406
+ ].map((chunk) => String(chunk ?? "").trim());
407
+ const missingHubTokenLine = findLineContaining(evidenceChunks, (line) => line.includes("hub telegram token is missing") && line.includes("hub_telegram_token"));
408
+ const missingHubToken = Boolean(missingHubTokenLine);
408
409
  if (missingHubToken) {
409
410
  return {
410
- reason: "Hub Telegram token is missing (HUB_TELEGRAM_TOKEN).",
411
- action: "Run 'copilot-hub configure', set the token, then run 'copilot-hub start'.",
411
+ reason: missingHubTokenLine || "Hub Telegram token is missing (HUB_TELEGRAM_TOKEN).",
412
+ action: "Run 'copilot-hub start' in a terminal (it will guide setup), then retry service.",
413
+ detectedAt: new Date().toISOString(),
412
414
  };
413
415
  }
414
416
  return null;
@@ -426,6 +428,68 @@ function readLogTail(filePath, maxLines = 120) {
426
428
  return "";
427
429
  }
428
430
  }
431
+ function findLineContaining(chunks, predicate) {
432
+ const lines = chunks
433
+ .flatMap((chunk) => String(chunk ?? "").split(/\r?\n/))
434
+ .map((line) => String(line ?? "").trim())
435
+ .filter(Boolean);
436
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
437
+ const line = lines[index];
438
+ if (predicate(line.toLowerCase())) {
439
+ return line;
440
+ }
441
+ }
442
+ return "";
443
+ }
444
+ function writeLastStartupError(value) {
445
+ try {
446
+ fs.mkdirSync(runtimeDir, { recursive: true });
447
+ fs.writeFileSync(lastStartupErrorPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
448
+ }
449
+ catch {
450
+ // Best effort only.
451
+ }
452
+ }
453
+ function readLastStartupError() {
454
+ if (!fs.existsSync(lastStartupErrorPath)) {
455
+ return null;
456
+ }
457
+ try {
458
+ const raw = fs.readFileSync(lastStartupErrorPath, "utf8");
459
+ const parsed = JSON.parse(raw);
460
+ return parsed && typeof parsed === "object" ? parsed : null;
461
+ }
462
+ catch {
463
+ return null;
464
+ }
465
+ }
466
+ function clearLastStartupError() {
467
+ if (!fs.existsSync(lastStartupErrorPath)) {
468
+ return;
469
+ }
470
+ try {
471
+ fs.rmSync(lastStartupErrorPath, { force: true });
472
+ }
473
+ catch {
474
+ // Best effort only.
475
+ }
476
+ }
477
+ function printLastStartupError() {
478
+ const issue = readLastStartupError();
479
+ if (!issue) {
480
+ return;
481
+ }
482
+ console.log("\n=== last startup error ===");
483
+ if (issue.detectedAt) {
484
+ console.log(`detectedAt: ${String(issue.detectedAt)}`);
485
+ }
486
+ if (issue.reason) {
487
+ console.log(`reason: ${String(issue.reason)}`);
488
+ }
489
+ if (issue.action) {
490
+ console.log(`action: ${String(issue.action)}`);
491
+ }
492
+ }
429
493
  function printUsage() {
430
494
  console.log("Usage: node scripts/dist/daemon.mjs <start|run|stop|status|help>");
431
495
  }
@@ -14,6 +14,7 @@ const pidsDir = path.join(runtimeDir, "pids");
14
14
  const logsDir = path.join(repoRoot, "logs");
15
15
 
16
16
  const daemonStatePath = path.join(pidsDir, "daemon.json");
17
+ const lastStartupErrorPath = path.join(runtimeDir, "last-startup-error.json");
17
18
  const daemonLogPath = path.join(logsDir, "service-daemon.log");
18
19
  const controlPlaneLogPath = path.join(logsDir, "control-plane.log");
19
20
  const agentEngineLogPath = path.join(logsDir, "agent-engine.log");
@@ -125,6 +126,7 @@ async function runDaemonLoop() {
125
126
  while (!state.stopping) {
126
127
  const ensureResult = runSupervisor("ensure", { allowFailure: true });
127
128
  if (ensureResult.ok) {
129
+ clearLastStartupError();
128
130
  if (failureCount > 0) {
129
131
  console.log("[daemon] workers recovered.");
130
132
  }
@@ -135,6 +137,7 @@ async function runDaemonLoop() {
135
137
 
136
138
  const fatal = detectFatalStartupError(ensureResult);
137
139
  if (fatal) {
140
+ writeLastStartupError(fatal);
138
141
  console.error(`[daemon] fatal startup error: ${fatal.reason}`);
139
142
  console.error(`[daemon] action required: ${fatal.action}`);
140
143
  state.stopping = true;
@@ -191,6 +194,7 @@ function showDaemonStatus() {
191
194
  console.log(`running: ${running ? "yes" : "no"}`);
192
195
  console.log(`pid: ${running ? String(pid) : "-"}`);
193
196
  console.log(`logFile: ${daemonLogPath}`);
197
+ printLastStartupError();
194
198
 
195
199
  if (!fs.existsSync(supervisorScriptPath)) {
196
200
  console.log("\n(worker status unavailable: supervisor script missing)");
@@ -466,22 +470,22 @@ function getErrorMessage(error) {
466
470
  }
467
471
 
468
472
  function detectFatalStartupError(ensureResult) {
469
- const evidence = [
473
+ const evidenceChunks = [
470
474
  String(ensureResult?.combinedOutput ?? ""),
471
475
  readLogTail(controlPlaneLogPath, 120),
472
476
  readLogTail(agentEngineLogPath, 120),
473
- ]
474
- .map((chunk) => String(chunk ?? "").trim())
475
- .filter(Boolean)
476
- .join("\n")
477
- .toLowerCase();
478
-
479
- const missingHubToken =
480
- evidence.includes("hub telegram token is missing") && evidence.includes("hub_telegram_token");
477
+ ].map((chunk) => String(chunk ?? "").trim());
478
+
479
+ const missingHubTokenLine = findLineContaining(
480
+ evidenceChunks,
481
+ (line) => line.includes("hub telegram token is missing") && line.includes("hub_telegram_token"),
482
+ );
483
+ const missingHubToken = Boolean(missingHubTokenLine);
481
484
  if (missingHubToken) {
482
485
  return {
483
- reason: "Hub Telegram token is missing (HUB_TELEGRAM_TOKEN).",
484
- action: "Run 'copilot-hub configure', set the token, then run 'copilot-hub start'.",
486
+ reason: missingHubTokenLine || "Hub Telegram token is missing (HUB_TELEGRAM_TOKEN).",
487
+ action: "Run 'copilot-hub start' in a terminal (it will guide setup), then retry service.",
488
+ detectedAt: new Date().toISOString(),
485
489
  };
486
490
  }
487
491
 
@@ -502,6 +506,71 @@ function readLogTail(filePath, maxLines = 120) {
502
506
  }
503
507
  }
504
508
 
509
+ function findLineContaining(chunks, predicate) {
510
+ const lines = chunks
511
+ .flatMap((chunk) => String(chunk ?? "").split(/\r?\n/))
512
+ .map((line) => String(line ?? "").trim())
513
+ .filter(Boolean);
514
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
515
+ const line = lines[index];
516
+ if (predicate(line.toLowerCase())) {
517
+ return line;
518
+ }
519
+ }
520
+ return "";
521
+ }
522
+
523
+ function writeLastStartupError(value) {
524
+ try {
525
+ fs.mkdirSync(runtimeDir, { recursive: true });
526
+ fs.writeFileSync(lastStartupErrorPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
527
+ } catch {
528
+ // Best effort only.
529
+ }
530
+ }
531
+
532
+ function readLastStartupError() {
533
+ if (!fs.existsSync(lastStartupErrorPath)) {
534
+ return null;
535
+ }
536
+ try {
537
+ const raw = fs.readFileSync(lastStartupErrorPath, "utf8");
538
+ const parsed = JSON.parse(raw);
539
+ return parsed && typeof parsed === "object" ? parsed : null;
540
+ } catch {
541
+ return null;
542
+ }
543
+ }
544
+
545
+ function clearLastStartupError() {
546
+ if (!fs.existsSync(lastStartupErrorPath)) {
547
+ return;
548
+ }
549
+ try {
550
+ fs.rmSync(lastStartupErrorPath, { force: true });
551
+ } catch {
552
+ // Best effort only.
553
+ }
554
+ }
555
+
556
+ function printLastStartupError() {
557
+ const issue = readLastStartupError();
558
+ if (!issue) {
559
+ return;
560
+ }
561
+
562
+ console.log("\n=== last startup error ===");
563
+ if (issue.detectedAt) {
564
+ console.log(`detectedAt: ${String(issue.detectedAt)}`);
565
+ }
566
+ if (issue.reason) {
567
+ console.log(`reason: ${String(issue.reason)}`);
568
+ }
569
+ if (issue.action) {
570
+ console.log(`action: ${String(issue.action)}`);
571
+ }
572
+ }
573
+
505
574
  function printUsage() {
506
575
  console.log("Usage: node scripts/dist/daemon.mjs <start|run|stop|status|help>");
507
576
  }