copilot-reverse 0.5.1 → 0.5.2
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/dist/shared/config.js +1 -1
- package/dist/supervisor/index.js +4 -0
- package/dist/supervisor/monitor.js +8 -1
- package/dist/version.js +1 -1
- package/dist/worker/index.js +22 -3
- package/package.json +1 -1
package/dist/shared/config.js
CHANGED
|
@@ -3,7 +3,7 @@ export function defaultConfig() {
|
|
|
3
3
|
bindHost: "127.0.0.1",
|
|
4
4
|
supervisorPort: 7890,
|
|
5
5
|
workerPort: 7891,
|
|
6
|
-
restart: { maxCrashes: 5, windowMs: 60_000, baseBackoffMs: 500, maxBackoffMs: 8_000 },
|
|
6
|
+
restart: { maxCrashes: 5, windowMs: 60_000, baseBackoffMs: 500, maxBackoffMs: 8_000, unhealthyCooldownMs: 30_000 },
|
|
7
7
|
// Empty = pass the requested model straight through to Copilot. Add entries (or "*") to remap.
|
|
8
8
|
modelMap: {},
|
|
9
9
|
// Set MAESTRO_REPORT_REPO=owner/repo to override where /report files diagnostics issues.
|
package/dist/supervisor/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { dataDir, dbPath } from "../shared/paths.js";
|
|
|
10
10
|
import { readGhToken } from "../shared/creds.js";
|
|
11
11
|
import { probeGithubAuth } from "../providers/copilot/token.js";
|
|
12
12
|
import { GithubHeartbeat, SIGNED_OUT_DETAIL } from "./github-heartbeat.js";
|
|
13
|
+
import { appendCrashLog } from "../shared/crash-log.js";
|
|
13
14
|
export function startSupervisor() {
|
|
14
15
|
const config = defaultConfig();
|
|
15
16
|
mkdirSync(dataDir(), { recursive: true });
|
|
@@ -21,6 +22,9 @@ export function startSupervisor() {
|
|
|
21
22
|
onStateChange: (s) => { state = s; bus.emit("state", { state: s }); },
|
|
22
23
|
onCrash: (d, exitCode, stderrTail) => {
|
|
23
24
|
recordRestart(db, { ts: Date.now(), reason: d.markedUnhealthy ? "unhealthy" : "crash", exitCode, stderrTail, backoffMs: d.backoffMs, markedUnhealthy: d.markedUnhealthy ? 1 : 0 });
|
|
25
|
+
// Also persist to crash.log so a worker crash is diagnosable post-mortem — the DB stderrTail can
|
|
26
|
+
// be empty if the worker died before flushing; this keeps whatever it did emit.
|
|
27
|
+
appendCrashLog("worker-crash", `exit=${exitCode} unhealthy=${d.markedUnhealthy} backoff=${d.backoffMs}ms\n${stderrTail || "(no stderr captured)"}`);
|
|
24
28
|
bus.emit("crash", { exitCode, ...d });
|
|
25
29
|
},
|
|
26
30
|
onWorkerMessage: (m) => {
|
|
@@ -16,7 +16,7 @@ export class RestartController {
|
|
|
16
16
|
const backoffMs = Math.min(this.policy.baseBackoffMs * 2 ** (this.consecutive - 1), this.policy.maxBackoffMs);
|
|
17
17
|
return { backoffMs, markedUnhealthy: this.crashTimes.length >= this.policy.maxCrashes, crashesInWindow: this.crashTimes.length };
|
|
18
18
|
}
|
|
19
|
-
reset() { this.consecutive = 0; }
|
|
19
|
+
reset() { this.consecutive = 0; this.crashTimes = []; }
|
|
20
20
|
}
|
|
21
21
|
export class WorkerMonitor {
|
|
22
22
|
config;
|
|
@@ -58,7 +58,14 @@ export class WorkerMonitor {
|
|
|
58
58
|
const d = this.controller.onCrash();
|
|
59
59
|
this.hooks.onCrash(d, code, this.stderrTail);
|
|
60
60
|
if (d.markedUnhealthy) {
|
|
61
|
+
// Don't give up forever: a transient crash burst (token rotation, a flaky upstream) shouldn't
|
|
62
|
+
// leave the daemon permanently dead. Mark unhealthy, then after a cooldown reset the window and
|
|
63
|
+
// try once more — recovering on its own if the cause has passed.
|
|
61
64
|
this.set("unhealthy");
|
|
65
|
+
setTimeout(() => { if (!this.stopped) {
|
|
66
|
+
this.controller.reset();
|
|
67
|
+
this.spawn();
|
|
68
|
+
} }, this.config.restart.unhealthyCooldownMs);
|
|
62
69
|
return;
|
|
63
70
|
}
|
|
64
71
|
this.set("crashed");
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// AUTO-GENERATED by scripts/gen-version.mjs from package.json — do not edit.
|
|
2
|
-
export const APP_VERSION = "0.5.
|
|
2
|
+
export const APP_VERSION = "0.5.2";
|
package/dist/worker/index.js
CHANGED
|
@@ -9,8 +9,13 @@ import { makeGatewayRunner } from "../core/server-tools.js";
|
|
|
9
9
|
import { borrowSearch } from "../providers/copilot/borrow-search.js";
|
|
10
10
|
import { dataDir } from "../shared/paths.js";
|
|
11
11
|
import { defaultConfig } from "../shared/config.js";
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// Sending after the parent tore down the IPC channel throws ERR_IPC_CHANNEL_CLOSED; guard it so a
|
|
13
|
+
// crash-time report can't itself become a second, masking crash.
|
|
14
|
+
function send(msg) { try {
|
|
15
|
+
if (process.connected)
|
|
16
|
+
process.send?.(msg);
|
|
17
|
+
}
|
|
18
|
+
catch { /* channel gone */ } }
|
|
14
19
|
const cfg = defaultConfig();
|
|
15
20
|
const port = Number(process.env.WORKER_PORT ?? cfg.workerPort);
|
|
16
21
|
const host = process.env.BIND_HOST ?? cfg.bindHost;
|
|
@@ -47,4 +52,18 @@ process.on("message", (m) => { if (m?.type === "shutdown") {
|
|
|
47
52
|
clearInterval(hb);
|
|
48
53
|
server.close(() => process.exit(0));
|
|
49
54
|
} });
|
|
50
|
-
|
|
55
|
+
// Crash diagnostics: write to STDERR FIRST so the supervisor's stderr capture (and crash.log) records
|
|
56
|
+
// the reason even when the IPC channel is already gone; the IPC send is a best-effort extra. Without
|
|
57
|
+
// the unhandledRejection handler, a stray floating rejection silently kills the worker on Node ≥15 —
|
|
58
|
+
// the exact "exit 1, empty stderr" crash loop we kept seeing.
|
|
59
|
+
function fatal(kind, e) {
|
|
60
|
+
const detail = e instanceof Error ? `${e.message}\n${e.stack ?? ""}` : String(e);
|
|
61
|
+
try {
|
|
62
|
+
process.stderr.write(`worker ${kind}: ${detail}\n`);
|
|
63
|
+
}
|
|
64
|
+
catch { /* nothing more we can do */ }
|
|
65
|
+
send({ type: "error", message: e instanceof Error ? e.message : String(e), stack: e instanceof Error ? e.stack : undefined });
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
process.on("uncaughtException", (e) => fatal("uncaughtException", e));
|
|
69
|
+
process.on("unhandledRejection", (e) => fatal("unhandledRejection", e));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copilot-reverse",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Interactive terminal app that exposes your GitHub Copilot subscription as local OpenAI- and Anthropic-compatible endpoints, with a self-healing daemon and a built-in assistant.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|