ework-daemon 0.4.43 → 0.4.45

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/opencode.ts +17 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-daemon",
3
- "version": "0.4.43",
3
+ "version": "0.4.45",
4
4
  "description": "Issue-driven AI development daemon. Spawns opencode subprocesses to resolve Gitea issues.",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
package/src/opencode.ts CHANGED
@@ -207,15 +207,28 @@ 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
- const r = Bun.spawnSync({ cmd: gitArgs, stdout: "ignore", stderr: "pipe", env: { ...process.env, ...env } });
211
- if (r.exitCode !== 0) {
210
+ let r: { exitCode: number | null; stderr?: Uint8Array | undefined };
211
+ try {
212
+ // async spawn: a slow/hung remote (e.g. SSH host-key prompt) must never
213
+ // block the daemon event loop — spawnSync froze healthz+heartbeats for
214
+ // the whole clone duration. Capped at 10 minutes.
215
+ const proc = Bun.spawn({ cmd: gitArgs, stdout: "ignore", stderr: "pipe", env: { ...process.env, ...env } });
216
+ const killTimer = setTimeout(() => proc.kill("SIGKILL"), 10 * 60_000);
217
+ const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).arrayBuffer()]);
218
+ clearTimeout(killTimer);
219
+ r = { exitCode, stderr: new Uint8Array(stderr) };
220
+ } catch {
221
+ r = { exitCode: -1 };
222
+ }
223
+ const exitCode = r.exitCode ?? -1;
224
+ if (exitCode !== 0) {
212
225
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
213
226
  if (existsSync(dir) && readdirSync(dir).length === 0) {
214
227
  Bun.spawnSync({ cmd: ["git", "init", dir], stdout: "ignore", stderr: "ignore" });
215
228
  }
216
229
  const stderrBuf = r.stderr as Uint8Array | undefined;
217
230
  const stderrText = stderrBuf ? new TextDecoder().decode(stderrBuf).slice(0, 500) : "";
218
- log.warn(`acquireWorkdir: git clone failed (exit ${r.exitCode}) for ${url}${stderrText ? `: ${stderrText}` : ""}; fell back to empty workdir`);
231
+ log.warn(`acquireWorkdir: git clone failed (exit ${exitCode}) for ${url}${stderrText ? `: ${stderrText}` : ""}; fell back to empty workdir`);
219
232
  }
220
233
  }
221
234
  } catch {
@@ -1372,8 +1385,7 @@ export class Engine {
1372
1385
  try {
1373
1386
  await tracker.setReaction(ref, lastMsg.sourceCommentId, "eyes", true);
1374
1387
  const reaction = exitCode === 0 ? "+1" : "-1";
1375
- const botReply = hasRecent ? this.lastBotReply(commentsNow, tracker) : undefined;
1376
- const targetId = botReply?.id ?? lastMsg.sourceCommentId;
1388
+ const targetId = lastMsg.sourceCommentId;
1377
1389
  await tracker.setReaction(ref, targetId, reaction);
1378
1390
  } catch { /* non-critical */ }
1379
1391
  }