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 +1 -1
- package/scripts/dist/daemon.mjs +73 -9
- package/scripts/src/daemon.mts +80 -11
package/package.json
CHANGED
package/scripts/dist/daemon.mjs
CHANGED
|
@@ -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
|
|
402
|
+
const evidenceChunks = [
|
|
399
403
|
String(ensureResult?.combinedOutput ?? ""),
|
|
400
404
|
readLogTail(controlPlaneLogPath, 120),
|
|
401
405
|
readLogTail(agentEngineLogPath, 120),
|
|
402
|
-
]
|
|
403
|
-
|
|
404
|
-
|
|
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
|
|
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
|
}
|
package/scripts/src/daemon.mts
CHANGED
|
@@ -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
|
|
473
|
+
const evidenceChunks = [
|
|
470
474
|
String(ensureResult?.combinedOutput ?? ""),
|
|
471
475
|
readLogTail(controlPlaneLogPath, 120),
|
|
472
476
|
readLogTail(agentEngineLogPath, 120),
|
|
473
|
-
]
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
.
|
|
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
|
|
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
|
}
|