ework-daemon 0.4.28 β 0.4.29
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/src/index.ts +7 -0
- package/src/opencode.ts +48 -18
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -88,6 +88,13 @@ async function boot() {
|
|
|
88
88
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
89
89
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
90
90
|
|
|
91
|
+
process.on("unhandledRejection", (reason) => {
|
|
92
|
+
log.error("unhandledRejection (continuing):", reason);
|
|
93
|
+
});
|
|
94
|
+
process.on("uncaughtException", (err) => {
|
|
95
|
+
log.error("uncaughtException (continuing):", err);
|
|
96
|
+
});
|
|
97
|
+
|
|
91
98
|
const activeCount = (await store.listActiveIssues()).length;
|
|
92
99
|
log.info(`\n${isTest ? "π§ͺ" : "β
"} ework-daemon ready at http://${server.hostname}:${server.port}/webhook`);
|
|
93
100
|
log.info(` Configure Gitea webhook to POST to /webhook/gitea`);
|
package/src/opencode.ts
CHANGED
|
@@ -562,8 +562,8 @@ export class Engine {
|
|
|
562
562
|
const tracker = this.getTracker(ref.trackerType);
|
|
563
563
|
const scopeKey = tracker.formatScopeKey(ref.scope);
|
|
564
564
|
|
|
565
|
-
if (this.paused && event.type === "issue_opened") {
|
|
566
|
-
log.info(`engine: paused β skipping
|
|
565
|
+
if (this.paused && (event.type === "issue_opened" || event.type === "comment_created")) {
|
|
566
|
+
log.info(`engine: paused β skipping ${event.type} for ${ref.trackerType}:${scopeKey}#${ref.issueId}`);
|
|
567
567
|
return;
|
|
568
568
|
}
|
|
569
569
|
|
|
@@ -796,13 +796,11 @@ export class Engine {
|
|
|
796
796
|
|
|
797
797
|
// Kill all running processes for this issue's sessions
|
|
798
798
|
const sessions = await this.store.getSessionsForIssue(issue.id);
|
|
799
|
+
let killedCount = 0;
|
|
799
800
|
for (const session of sessions) {
|
|
800
801
|
const k = this.sessionKey(session, issue);
|
|
801
|
-
const
|
|
802
|
-
if (
|
|
803
|
-
this.stopping.add(k);
|
|
804
|
-
try { this.killProcessTree(proc.pid, "SIGTERM"); } catch { /* already dead */ }
|
|
805
|
-
}
|
|
802
|
+
const killed = await this.killSessionProcess(session, k);
|
|
803
|
+
if (killed) killedCount++;
|
|
806
804
|
this.clearRuntimeState(k);
|
|
807
805
|
const msgs = await this.store.getMessagesForSession(session.id);
|
|
808
806
|
for (const msg of msgs) {
|
|
@@ -829,7 +827,7 @@ export class Engine {
|
|
|
829
827
|
this.cloneUrls.delete(gcKey);
|
|
830
828
|
this.senders.delete(gcKey);
|
|
831
829
|
|
|
832
|
-
log.info(`engine: issue closed, ${sessions.length} sessions
|
|
830
|
+
log.info(`engine: issue closed, ${killedCount}/${sessions.length} sessions killed for ${scopeKey}#${ref.issueId}`);
|
|
833
831
|
void tracker.updateStatus(ref, "completed");
|
|
834
832
|
}
|
|
835
833
|
|
|
@@ -842,13 +840,11 @@ export class Engine {
|
|
|
842
840
|
if (!issue) return;
|
|
843
841
|
this.stopObserver(issue.id);
|
|
844
842
|
const sessions = await this.store.getSessionsForIssue(issue.id);
|
|
843
|
+
let killedCount = 0;
|
|
845
844
|
for (const session of sessions) {
|
|
846
845
|
const k = this.sessionKey(session, issue);
|
|
847
|
-
const
|
|
848
|
-
if (
|
|
849
|
-
this.stopping.add(k);
|
|
850
|
-
try { this.killProcessTree(proc.pid, "SIGTERM"); } catch { /* already dead */ }
|
|
851
|
-
}
|
|
846
|
+
const killed = await this.killSessionProcess(session, k);
|
|
847
|
+
if (killed) killedCount++;
|
|
852
848
|
this.clearRuntimeState(k);
|
|
853
849
|
const msgs = await this.store.getMessagesForSession(session.id);
|
|
854
850
|
for (const msg of msgs) {
|
|
@@ -858,7 +854,7 @@ export class Engine {
|
|
|
858
854
|
}
|
|
859
855
|
await this.store.updateSession(session.id, { state: "idle", opencodePid: undefined });
|
|
860
856
|
}
|
|
861
|
-
log.info(`engine: issue halted, ${sessions.length} sessions killed for ${scopeKey}#${ref.issueId}`);
|
|
857
|
+
log.info(`engine: issue halted, ${killedCount}/${sessions.length} sessions killed for ${scopeKey}#${ref.issueId}`);
|
|
862
858
|
try { await tracker.createComment(ref, "[system] βΈοΈ AI processing halted by user."); } catch { /* tracker unavailable */ }
|
|
863
859
|
}
|
|
864
860
|
|
|
@@ -878,6 +874,36 @@ export class Engine {
|
|
|
878
874
|
this.generation.delete(k);
|
|
879
875
|
}
|
|
880
876
|
|
|
877
|
+
private async killSessionProcess(session: OpSession, k: string): Promise<boolean> {
|
|
878
|
+
const handle = this.processes.get(k);
|
|
879
|
+
if (handle) {
|
|
880
|
+
this.stopping.add(k);
|
|
881
|
+
try { this.killProcessTree(handle.pid, "SIGTERM"); } catch { /* already dead */ }
|
|
882
|
+
this.processes.delete(k);
|
|
883
|
+
return true;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const pid = session.opencodePid;
|
|
887
|
+
if (!pid) return false;
|
|
888
|
+
try {
|
|
889
|
+
process.kill(pid, 0);
|
|
890
|
+
} catch {
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
log.info(`engine: killing orphaned pid=${pid} for ${k} (cross-restart)`);
|
|
895
|
+
this.stopping.add(k);
|
|
896
|
+
try {
|
|
897
|
+
this.killProcessTree(pid, "SIGTERM");
|
|
898
|
+
for (let i = 0; i < 30; i++) {
|
|
899
|
+
await new Promise(r => setTimeout(r, 100));
|
|
900
|
+
try { process.kill(pid, 0); } catch { break; }
|
|
901
|
+
}
|
|
902
|
+
try { process.kill(pid, "SIGKILL"); } catch { /* dead */ }
|
|
903
|
+
} catch { /* already dead */ }
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
|
|
881
907
|
// βββ Preemptive Scheduler βββ
|
|
882
908
|
|
|
883
909
|
private async enqueueOrRun(session: OpSession, issue: Issue, prompt: string, sourceCommentId?: string, model?: string) {
|
|
@@ -1393,16 +1419,20 @@ export class Engine {
|
|
|
1393
1419
|
}
|
|
1394
1420
|
|
|
1395
1421
|
private async runObserverCycle() {
|
|
1396
|
-
// Multi-machine: periodically release stale owners so we can adopt their
|
|
1397
|
-
// work, and only iterate issues/sessions this daemon owns.
|
|
1398
1422
|
try {
|
|
1399
1423
|
await this.store.releaseDeadOwners(this.cfg.work.leaseTtlMs);
|
|
1400
1424
|
} catch (err) {
|
|
1401
1425
|
log.error("engine: releaseDeadOwners failed:", (err as Error).message);
|
|
1402
1426
|
}
|
|
1403
1427
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1428
|
+
let ownedIssues;
|
|
1429
|
+
try {
|
|
1430
|
+
ownedIssues = (await this.store.listOwnedIssues(this.daemonId))
|
|
1431
|
+
.filter((i) => this.observedIssues.has(i.id));
|
|
1432
|
+
} catch (err) {
|
|
1433
|
+
log.error("engine: listOwnedIssues failed:", (err as Error).message);
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1406
1436
|
|
|
1407
1437
|
for (const issue of ownedIssues) {
|
|
1408
1438
|
try {
|