ework-daemon 0.4.30 → 0.4.31
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/op.ts +7 -0
- package/src/opencode.ts +30 -1
package/package.json
CHANGED
package/src/op.ts
CHANGED
|
@@ -260,6 +260,13 @@ export class Store {
|
|
|
260
260
|
return rows.map(rowToSession);
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
async listSessionsWithPid(): Promise<Array<{ id: string; opencodePid: number }>> {
|
|
264
|
+
const rows = await getDB().all<{ uid: string; opencode_pid: number }>(
|
|
265
|
+
"SELECT uid, opencode_pid FROM {{op_sessions}} WHERE opencode_pid IS NOT NULL"
|
|
266
|
+
);
|
|
267
|
+
return rows.map((r) => ({ id: r.uid, opencodePid: r.opencode_pid }));
|
|
268
|
+
}
|
|
269
|
+
|
|
263
270
|
async listNonIdleSessions(): Promise<OpSession[]> {
|
|
264
271
|
const rows = await getDB().all<SessionRow>("SELECT * FROM {{op_sessions}} WHERE state != 'idle'");
|
|
265
272
|
return rows.map(rowToSession);
|
package/src/opencode.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
-
import { mkdirSync, writeFileSync, readdirSync, existsSync } from "fs";
|
|
2
|
+
import { mkdirSync, writeFileSync, readdirSync, existsSync, readFileSync } from "fs";
|
|
3
3
|
import { join, resolve, isAbsolute } from "path";
|
|
4
4
|
import { homedir } from "os";
|
|
5
5
|
import { log } from "./logger";
|
|
@@ -1617,6 +1617,8 @@ export class Engine {
|
|
|
1617
1617
|
log.error("engine: releaseDeadOwners at boot failed:", (err as Error).message);
|
|
1618
1618
|
}
|
|
1619
1619
|
|
|
1620
|
+
await this.cleanupGlobalOrphans();
|
|
1621
|
+
|
|
1620
1622
|
// Multi-machine: recover ONLY this daemon's sessions. Other daemons own
|
|
1621
1623
|
// the rest; touching their state would race them.
|
|
1622
1624
|
const ownedSessions = await this.store.listOwnedSessions(this.daemonId);
|
|
@@ -1711,6 +1713,33 @@ export class Engine {
|
|
|
1711
1713
|
}
|
|
1712
1714
|
}
|
|
1713
1715
|
|
|
1716
|
+
private async cleanupGlobalOrphans(): Promise<void> {
|
|
1717
|
+
let sessions: Array<{ id: string; opencodePid: number }>;
|
|
1718
|
+
try {
|
|
1719
|
+
sessions = await this.store.listSessionsWithPid();
|
|
1720
|
+
} catch { return; }
|
|
1721
|
+
let killed = 0;
|
|
1722
|
+
for (const s of sessions) {
|
|
1723
|
+
let alive = false;
|
|
1724
|
+
try { process.kill(s.opencodePid, 0); alive = true; } catch { /* dead */ }
|
|
1725
|
+
if (!alive) continue;
|
|
1726
|
+
|
|
1727
|
+
let ppid = -1;
|
|
1728
|
+
try {
|
|
1729
|
+
const stat = readFileSync(`/proc/${s.opencodePid}/stat`, "utf8");
|
|
1730
|
+
const m = stat.match(/\)\s+\S+\s+(\d+)/);
|
|
1731
|
+
ppid = m ? Number(m[1]) : -1;
|
|
1732
|
+
} catch { continue; }
|
|
1733
|
+
|
|
1734
|
+
if (ppid === 1) {
|
|
1735
|
+
log.info(`engine: killing global orphan pid=${s.opencodePid} (PPID=1, session ${s.id})`);
|
|
1736
|
+
try { this.killProcessTree(s.opencodePid); } catch { /* dead */ }
|
|
1737
|
+
killed++;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
if (killed > 0) log.info(`engine: cleaned up ${killed} orphaned processes (PPID=1)`);
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1714
1743
|
// ─── API Methods ───
|
|
1715
1744
|
|
|
1716
1745
|
async retryMessage(messageId: string): Promise<boolean> {
|