ework-daemon 0.4.21 → 0.4.22

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-daemon",
3
- "version": "0.4.21",
3
+ "version": "0.4.22",
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/config.ts CHANGED
@@ -64,6 +64,7 @@ export const configSchema = z.object({
64
64
  maxLines: z.coerce.number().int().positive().default(2000),
65
65
  maxBytes: z.coerce.number().int().positive().default(524288),
66
66
  }).default({ roots: [], maxLines: 2000, maxBytes: 524288 }),
67
+ childEnvDeny: z.array(z.string()).default([]),
67
68
  });
68
69
 
69
70
  export type Config = z.infer<typeof configSchema>;
@@ -145,6 +146,7 @@ export function loadConfig(): Config {
145
146
  thresholdMs: Number(process.env.DAEMON_STUCK_THRESHOLD_MS) || 30 * 60 * 1000,
146
147
  maxNudges: Number(process.env.DAEMON_MAX_STUCK_NUDGES) || 1,
147
148
  } : undefined,
149
+ childEnvDeny: (process.env.WORK_CHILD_ENV_DENY ?? "").split(",").map((s) => s.trim()).filter(Boolean),
148
150
  });
149
151
  }
150
152
 
@@ -188,5 +190,6 @@ export function loadConfig(): Config {
188
190
  maxLines: Number(process.env.WORK_FILE_MAX_LINES) || 2000,
189
191
  maxBytes: Number(process.env.WORK_FILE_MAX_BYTES) || 524288,
190
192
  },
193
+ childEnvDeny: (process.env.WORK_CHILD_ENV_DENY ?? "").split(",").map((s) => s.trim()).filter(Boolean),
191
194
  });
192
195
  }
package/src/opencode.ts CHANGED
@@ -308,6 +308,7 @@ export class Engine {
308
308
  private processExitNudgeRounds = new Map<string, number>();
309
309
  private stuckNudgeRounds = new Map<string, number>();
310
310
  private currentPrompt = new Map<string, string>();
311
+ private paused = false;
311
312
 
312
313
  // Generation counter per session key — incremented on every execProcess call.
313
314
  // finishRun captures the generation at start and checks it after each await.
@@ -372,6 +373,22 @@ export class Engine {
372
373
  return this.daemonId;
373
374
  }
374
375
 
376
+ async pause(): Promise<void> {
377
+ this.paused = true;
378
+ await this.store.markDaemonStatus(this.daemonId, "drained");
379
+ log.info(`engine: daemon ${this.daemonId} paused (drained) — new issues rejected, existing sessions continue`);
380
+ }
381
+
382
+ async resume(): Promise<void> {
383
+ this.paused = false;
384
+ await this.store.markDaemonStatus(this.daemonId, "active");
385
+ log.info(`engine: daemon ${this.daemonId} resumed (active)`);
386
+ }
387
+
388
+ isPaused(): boolean {
389
+ return this.paused;
390
+ }
391
+
375
392
  /**
376
393
  * Ensure this engine owns the issue before doing work on it. Returns true
377
394
  * if we own it (either already, or just claimed). Returns false if another
@@ -539,6 +556,11 @@ export class Engine {
539
556
  const tracker = this.getTracker(ref.trackerType);
540
557
  const scopeKey = tracker.formatScopeKey(ref.scope);
541
558
 
559
+ if (this.paused && event.type === "issue_opened") {
560
+ log.info(`engine: paused — skipping issue_opened for ${ref.trackerType}:${scopeKey}#${ref.issueId}`);
561
+ return;
562
+ }
563
+
542
564
  const issueMapKey = `${ref.trackerType}:${scopeKey}#${ref.issueId}`;
543
565
  if (groupConfig) {
544
566
  this.groupConfigs.set(issueMapKey, groupConfig);
@@ -941,14 +963,13 @@ export class Engine {
941
963
  // (the operator's explicit config) instead of arbitrary env pollution.
942
964
  // Provider keys / Gitea vars are preserved (opencode + its plugin need
943
965
  // them); only the explicit model-override var is neutralized.
944
- const childEnv = { ...process.env };
966
+ const childEnv = { ...process.env, ...this.hookEnvFor(issue, session, workdir) };
945
967
  if (!model) delete childEnv.OPENCODE_MODEL;
946
- // Strip parent-session env vars so the child opencode reads its own
947
- // opencode.json instead of inheriting a parent session's model/provider.
948
968
  delete childEnv.OPENCODE;
949
969
  delete childEnv.OPENCODE_PID;
950
970
  delete childEnv.OPENCODE_RUN_ID;
951
971
  delete childEnv.OPENCODE_PROCESS_ROLE;
972
+ for (const k of this.cfg.childEnvDeny) delete childEnv[k];
952
973
 
953
974
  try {
954
975
  const proc = spawn({
package/src/server.ts CHANGED
@@ -205,6 +205,15 @@ export function createServer(
205
205
  }
206
206
  }
207
207
 
208
+ if (pathname === "/api/admin/pause" && req.method === "POST") {
209
+ await engine.pause();
210
+ return json({ ok: true, paused: true });
211
+ }
212
+ if (pathname === "/api/admin/resume" && req.method === "POST") {
213
+ await engine.resume();
214
+ return json({ ok: true, paused: false });
215
+ }
216
+
208
217
  return json({ error: "not found" }, 404);
209
218
  }
210
219