ework-daemon 0.4.45 → 0.4.47
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/opencode.ts +31 -8
package/package.json
CHANGED
package/src/opencode.ts
CHANGED
|
@@ -207,13 +207,20 @@ export class RecloneStrategy implements TakeoverStrategy {
|
|
|
207
207
|
const gitArgs = ["git"];
|
|
208
208
|
if (credHelper) gitArgs.push("-c", `credential.helper=${credHelper}`);
|
|
209
209
|
gitArgs.push("clone", url, dir);
|
|
210
|
+
// ssh without these can sleep forever: no TCP keepalive on a blackholed
|
|
211
|
+
// connection, and host-key/passphrase prompts block a headless daemon.
|
|
212
|
+
const sshCmd = process.env.WORK_GIT_SSH_COMMAND
|
|
213
|
+
?? "ssh -o ConnectTimeout=10 -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o BatchMode=yes";
|
|
210
214
|
let r: { exitCode: number | null; stderr?: Uint8Array | undefined };
|
|
211
215
|
try {
|
|
212
|
-
// async spawn: a slow/hung remote
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
const proc = Bun.spawn({
|
|
216
|
-
|
|
216
|
+
// async spawn: a slow/hung remote must never block the daemon event
|
|
217
|
+
// loop — spawnSync froze healthz+heartbeats for the whole clone
|
|
218
|
+
// duration. Capped at 10 minutes.
|
|
219
|
+
const proc = Bun.spawn({
|
|
220
|
+
cmd: gitArgs, stdout: "ignore", stderr: "pipe",
|
|
221
|
+
env: { ...process.env, ...env, GIT_SSH_COMMAND: sshCmd },
|
|
222
|
+
});
|
|
223
|
+
const killTimer = setTimeout(() => { try { proc.kill("SIGKILL"); } catch { /* already dead */ } }, 10 * 60_000);
|
|
217
224
|
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).arrayBuffer()]);
|
|
218
225
|
clearTimeout(killTimer);
|
|
219
226
|
r = { exitCode, stderr: new Uint8Array(stderr) };
|
|
@@ -328,6 +335,7 @@ export class Engine {
|
|
|
328
335
|
private lastOutputAt = new Map<string, number>();
|
|
329
336
|
private startedAt = new Map<string, number>();
|
|
330
337
|
private progressCommentId = new Map<string, string>();
|
|
338
|
+
private pickupCommentId = new Map<string, string>();
|
|
331
339
|
|
|
332
340
|
private nudgeRounds = new Map<string, number>();
|
|
333
341
|
private emptyResponseRounds = new Map<string, number>();
|
|
@@ -795,11 +803,18 @@ export class Engine {
|
|
|
795
803
|
}
|
|
796
804
|
|
|
797
805
|
const k = this.sessionKey(session, issue);
|
|
798
|
-
const workdir = await this.resolveWorkdir(session, issue);
|
|
799
|
-
log.info(`engine: session "${session.name}" created for ${k}, workdir=${workdir}`);
|
|
800
806
|
|
|
801
|
-
|
|
807
|
+
// Ack before resolving the workdir: a first-touch clone can take minutes
|
|
808
|
+
// (or hit the 10-min cap), and the user must see pickup feedback immediately.
|
|
809
|
+
// The session link is a placeholder until the backend reports its session id;
|
|
810
|
+
// the onSessionId callback rewrites this comment with the real reference.
|
|
811
|
+
await tracker.createComment(ref, `[system] 🔄 **${session.name}** picked up this issue — preparing workspace…`).then(
|
|
812
|
+
(c) => this.pickupCommentId.set(k, c.id),
|
|
813
|
+
() => {},
|
|
814
|
+
);
|
|
802
815
|
void tracker.updateStatus(ref, "processing");
|
|
816
|
+
const workdir = await this.resolveWorkdir(session, issue);
|
|
817
|
+
log.info(`engine: session "${session.name}" created for ${k}, workdir=${workdir}`);
|
|
803
818
|
|
|
804
819
|
const instructions = tracker.getTrackerInstructions(ref);
|
|
805
820
|
const prompt = this.buildInitialPrompt(
|
|
@@ -1201,6 +1216,13 @@ export class Engine {
|
|
|
1201
1216
|
await this.store.updateSession(session.id, { opencodeSessionId: id });
|
|
1202
1217
|
session.opencodeSessionId = id;
|
|
1203
1218
|
log.info(`engine: captured sessionID=${id.slice(0, 8)} for ${k} (early persist)`);
|
|
1219
|
+
const pickupId = this.pickupCommentId.get(k);
|
|
1220
|
+
if (pickupId) {
|
|
1221
|
+
this.pickupCommentId.delete(k);
|
|
1222
|
+
await tracker
|
|
1223
|
+
.editComment(ref, pickupId, `[system] 🔄 **${session.name}** picked up this issue.\n> session: ${this.sessionRef(session)} | workdir: ${this.workdirLink(workdir)}`)
|
|
1224
|
+
.catch((err) => log.error(`engine: failed to rewrite pickup comment for ${k}:`, (err as Error).message));
|
|
1225
|
+
}
|
|
1204
1226
|
}
|
|
1205
1227
|
},
|
|
1206
1228
|
},
|
|
@@ -1297,6 +1319,7 @@ export class Engine {
|
|
|
1297
1319
|
return;
|
|
1298
1320
|
}
|
|
1299
1321
|
this.progressCommentId.delete(k);
|
|
1322
|
+
this.pickupCommentId.delete(k);
|
|
1300
1323
|
this.currentPrompt.delete(k);
|
|
1301
1324
|
await this.persistRuntimeState(session.id);
|
|
1302
1325
|
|