copilot-hub 0.1.11 → 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 +106 -0
- package/scripts/src/daemon.mts +117 -0
package/package.json
CHANGED
package/scripts/dist/daemon.mjs
CHANGED
|
@@ -11,7 +11,10 @@ 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");
|
|
16
|
+
const controlPlaneLogPath = path.join(logsDir, "control-plane.log");
|
|
17
|
+
const agentEngineLogPath = path.join(logsDir, "agent-engine.log");
|
|
15
18
|
const daemonScriptPath = path.join(repoRoot, "scripts", "dist", "daemon.mjs");
|
|
16
19
|
const supervisorScriptPath = path.join(repoRoot, "scripts", "dist", "supervisor.mjs");
|
|
17
20
|
const nodeBin = process.execPath;
|
|
@@ -104,6 +107,7 @@ async function runDaemonLoop() {
|
|
|
104
107
|
while (!state.stopping) {
|
|
105
108
|
const ensureResult = runSupervisor("ensure", { allowFailure: true });
|
|
106
109
|
if (ensureResult.ok) {
|
|
110
|
+
clearLastStartupError();
|
|
107
111
|
if (failureCount > 0) {
|
|
108
112
|
console.log("[daemon] workers recovered.");
|
|
109
113
|
}
|
|
@@ -111,6 +115,15 @@ async function runDaemonLoop() {
|
|
|
111
115
|
await sleepInterruptible(BASE_CHECK_MS, () => state.stopping);
|
|
112
116
|
continue;
|
|
113
117
|
}
|
|
118
|
+
const fatal = detectFatalStartupError(ensureResult);
|
|
119
|
+
if (fatal) {
|
|
120
|
+
writeLastStartupError(fatal);
|
|
121
|
+
console.error(`[daemon] fatal startup error: ${fatal.reason}`);
|
|
122
|
+
console.error(`[daemon] action required: ${fatal.action}`);
|
|
123
|
+
state.stopping = true;
|
|
124
|
+
await shutdownDaemon(state, { reason: "fatal-configuration", exitCode: 1 });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
114
127
|
failureCount += 1;
|
|
115
128
|
const delay = computeBackoffDelay(failureCount);
|
|
116
129
|
const reason = firstLine(ensureResult.combinedOutput) ||
|
|
@@ -148,6 +161,7 @@ function showDaemonStatus() {
|
|
|
148
161
|
console.log(`running: ${running ? "yes" : "no"}`);
|
|
149
162
|
console.log(`pid: ${running ? String(pid) : "-"}`);
|
|
150
163
|
console.log(`logFile: ${daemonLogPath}`);
|
|
164
|
+
printLastStartupError();
|
|
151
165
|
if (!fs.existsSync(supervisorScriptPath)) {
|
|
152
166
|
console.log("\n(worker status unavailable: supervisor script missing)");
|
|
153
167
|
return;
|
|
@@ -384,6 +398,98 @@ function getErrorMessage(error) {
|
|
|
384
398
|
}
|
|
385
399
|
return String(error ?? "Unknown error.");
|
|
386
400
|
}
|
|
401
|
+
function detectFatalStartupError(ensureResult) {
|
|
402
|
+
const evidenceChunks = [
|
|
403
|
+
String(ensureResult?.combinedOutput ?? ""),
|
|
404
|
+
readLogTail(controlPlaneLogPath, 120),
|
|
405
|
+
readLogTail(agentEngineLogPath, 120),
|
|
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);
|
|
409
|
+
if (missingHubToken) {
|
|
410
|
+
return {
|
|
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(),
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
function readLogTail(filePath, maxLines = 120) {
|
|
419
|
+
try {
|
|
420
|
+
if (!fs.existsSync(filePath)) {
|
|
421
|
+
return "";
|
|
422
|
+
}
|
|
423
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
424
|
+
const lines = content.split(/\r?\n/);
|
|
425
|
+
return lines.slice(-maxLines).join("\n").trim();
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
return "";
|
|
429
|
+
}
|
|
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
|
+
}
|
|
387
493
|
function printUsage() {
|
|
388
494
|
console.log("Usage: node scripts/dist/daemon.mjs <start|run|stop|status|help>");
|
|
389
495
|
}
|
package/scripts/src/daemon.mts
CHANGED
|
@@ -14,7 +14,10 @@ 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");
|
|
19
|
+
const controlPlaneLogPath = path.join(logsDir, "control-plane.log");
|
|
20
|
+
const agentEngineLogPath = path.join(logsDir, "agent-engine.log");
|
|
18
21
|
const daemonScriptPath = path.join(repoRoot, "scripts", "dist", "daemon.mjs");
|
|
19
22
|
const supervisorScriptPath = path.join(repoRoot, "scripts", "dist", "supervisor.mjs");
|
|
20
23
|
const nodeBin = process.execPath;
|
|
@@ -123,6 +126,7 @@ async function runDaemonLoop() {
|
|
|
123
126
|
while (!state.stopping) {
|
|
124
127
|
const ensureResult = runSupervisor("ensure", { allowFailure: true });
|
|
125
128
|
if (ensureResult.ok) {
|
|
129
|
+
clearLastStartupError();
|
|
126
130
|
if (failureCount > 0) {
|
|
127
131
|
console.log("[daemon] workers recovered.");
|
|
128
132
|
}
|
|
@@ -131,6 +135,16 @@ async function runDaemonLoop() {
|
|
|
131
135
|
continue;
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
const fatal = detectFatalStartupError(ensureResult);
|
|
139
|
+
if (fatal) {
|
|
140
|
+
writeLastStartupError(fatal);
|
|
141
|
+
console.error(`[daemon] fatal startup error: ${fatal.reason}`);
|
|
142
|
+
console.error(`[daemon] action required: ${fatal.action}`);
|
|
143
|
+
state.stopping = true;
|
|
144
|
+
await shutdownDaemon(state, { reason: "fatal-configuration", exitCode: 1 });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
134
148
|
failureCount += 1;
|
|
135
149
|
const delay = computeBackoffDelay(failureCount);
|
|
136
150
|
const reason =
|
|
@@ -180,6 +194,7 @@ function showDaemonStatus() {
|
|
|
180
194
|
console.log(`running: ${running ? "yes" : "no"}`);
|
|
181
195
|
console.log(`pid: ${running ? String(pid) : "-"}`);
|
|
182
196
|
console.log(`logFile: ${daemonLogPath}`);
|
|
197
|
+
printLastStartupError();
|
|
183
198
|
|
|
184
199
|
if (!fs.existsSync(supervisorScriptPath)) {
|
|
185
200
|
console.log("\n(worker status unavailable: supervisor script missing)");
|
|
@@ -454,6 +469,108 @@ function getErrorMessage(error) {
|
|
|
454
469
|
return String(error ?? "Unknown error.");
|
|
455
470
|
}
|
|
456
471
|
|
|
472
|
+
function detectFatalStartupError(ensureResult) {
|
|
473
|
+
const evidenceChunks = [
|
|
474
|
+
String(ensureResult?.combinedOutput ?? ""),
|
|
475
|
+
readLogTail(controlPlaneLogPath, 120),
|
|
476
|
+
readLogTail(agentEngineLogPath, 120),
|
|
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);
|
|
484
|
+
if (missingHubToken) {
|
|
485
|
+
return {
|
|
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(),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function readLogTail(filePath, maxLines = 120) {
|
|
496
|
+
try {
|
|
497
|
+
if (!fs.existsSync(filePath)) {
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
502
|
+
const lines = content.split(/\r?\n/);
|
|
503
|
+
return lines.slice(-maxLines).join("\n").trim();
|
|
504
|
+
} catch {
|
|
505
|
+
return "";
|
|
506
|
+
}
|
|
507
|
+
}
|
|
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
|
+
|
|
457
574
|
function printUsage() {
|
|
458
575
|
console.log("Usage: node scripts/dist/daemon.mjs <start|run|stop|status|help>");
|
|
459
576
|
}
|