ework-daemon 0.4.32 → 0.4.34

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.32",
3
+ "version": "0.4.34",
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
@@ -25,6 +25,7 @@ export const configSchema = z.object({
25
25
  port: z.coerce.number().default(3101),
26
26
  host: z.string().default("0.0.0.0"),
27
27
  endpoint: z.string().default(""),
28
+ nonWakingAuthors: z.array(z.string()).default([]),
28
29
  }),
29
30
  opencode: z.object({
30
31
  binary: z.string().default("opencode"),
@@ -45,6 +46,7 @@ export const configSchema = z.object({
45
46
  work: z.object({
46
47
  capacity: z.coerce.number().int().positive().default(4),
47
48
  maxConcurrent: z.coerce.number().int().positive().default(4),
49
+ maxConcurrentExplicit: z.boolean().default(false),
48
50
  heartbeatMs: z.coerce.number().int().positive().default(10_000),
49
51
  leaseTtlMs: z.coerce.number().int().positive().default(60_000),
50
52
  }),
@@ -95,9 +97,11 @@ const TEST_DEFAULTS = {
95
97
 
96
98
  function readWorkSection() {
97
99
  const capacity = process.env.WORK_DAEMON_CAPACITY ? Number(process.env.WORK_DAEMON_CAPACITY) : 4;
100
+ const maxConcurrentExplicit = process.env.WORK_MAX_CONCURRENT != null;
98
101
  return {
99
102
  capacity,
100
- maxConcurrent: process.env.WORK_MAX_CONCURRENT ? Number(process.env.WORK_MAX_CONCURRENT) : capacity,
103
+ maxConcurrent: maxConcurrentExplicit ? Number(process.env.WORK_MAX_CONCURRENT) : capacity,
104
+ maxConcurrentExplicit,
101
105
  heartbeatMs: process.env.WORK_DAEMON_HEARTBEAT_MS ? Number(process.env.WORK_DAEMON_HEARTBEAT_MS) : 10_000,
102
106
  leaseTtlMs: process.env.WORK_DAEMON_LEASE_TTL_MS ? Number(process.env.WORK_DAEMON_LEASE_TTL_MS) : 60_000,
103
107
  };
@@ -139,6 +143,7 @@ export function loadConfig(): Config {
139
143
  port: process.env.DAEMON_PORT ?? TEST_DEFAULTS.daemon.port,
140
144
  host: process.env.DAEMON_HOST ?? TEST_DEFAULTS.daemon.host,
141
145
  endpoint: process.env.DAEMON_ENDPOINT ?? "",
146
+ nonWakingAuthors: (process.env.WORK_NON_WAKING_AUTHORS ?? "").split(",").map((s) => s.trim()).filter(Boolean),
142
147
  },
143
148
  opencode: {
144
149
  binary: process.env.OPENCODE_BINARY ?? TEST_DEFAULTS.opencode.binary,
package/src/opencode.ts CHANGED
@@ -301,6 +301,7 @@ export class Engine {
301
301
  private readonly backend: RuntimeBackend;
302
302
  private heartbeatTimer?: ReturnType<typeof setInterval>;
303
303
  private maxConcurrent: number;
304
+ private maxConcurrentExplicit: boolean;
304
305
 
305
306
  // Runtime state keyed by session key (trackerType:scopeKey#issueId@sessionName)
306
307
  private processes = new Map<string, RuntimeHandle>();
@@ -350,6 +351,7 @@ export class Engine {
350
351
  this.takeover = opts.takeover ?? new RecloneStrategy(cfg);
351
352
  this.backend = opts.backend ?? createDefaultBackend(cfg);
352
353
  this.maxConcurrent = cfg.work.maxConcurrent;
354
+ this.maxConcurrentExplicit = cfg.work.maxConcurrentExplicit;
353
355
  this.startGlobalObserver();
354
356
  void this.recover();
355
357
  }
@@ -376,6 +378,7 @@ export class Engine {
376
378
  }
377
379
 
378
380
  private async syncMaxConcurrent(): Promise<void> {
381
+ if (this.maxConcurrentExplicit) return;
379
382
  try {
380
383
  const cap = await this.store.getDaemonCapacity(this.daemonId);
381
384
  if (cap != null && cap > 0 && cap !== this.maxConcurrent) {
@@ -388,7 +391,8 @@ export class Engine {
388
391
  setMaxConcurrent(n: number): void {
389
392
  if (!Number.isFinite(n) || n < 1) return;
390
393
  this.maxConcurrent = Math.floor(n);
391
- log.info(`engine: maxConcurrent set to ${this.maxConcurrent}`);
394
+ this.maxConcurrentExplicit = true;
395
+ log.info(`engine: maxConcurrent set to ${this.maxConcurrent} (explicit — DB sync disabled)`);
392
396
  }
393
397
 
394
398
  getMaxConcurrent(): number {
@@ -595,6 +599,14 @@ export class Engine {
595
599
  return;
596
600
  }
597
601
 
602
+ if (event.type === "comment_created" && event.comment?.author) {
603
+ const author = event.comment.author;
604
+ if (this.cfg.daemon.nonWakingAuthors.includes(author)) {
605
+ log.info(`engine: non-waking author ${author} — skipping comment_created for ${ref.trackerType}:${scopeKey}#${ref.issueId}`);
606
+ return;
607
+ }
608
+ }
609
+
598
610
  const issueMapKey = `${ref.trackerType}:${scopeKey}#${ref.issueId}`;
599
611
  if (groupConfig) {
600
612
  this.groupConfigs.set(issueMapKey, groupConfig);
@@ -1727,7 +1739,9 @@ export class Engine {
1727
1739
 
1728
1740
  try {
1729
1741
  const cmdline = readFileSync(`/proc/${s.opencodePid}/cmdline`, "utf8");
1730
- if (!cmdline.includes(binaryName) && !cmdline.includes("opencode") && !cmdline.includes("pi")) continue;
1742
+ const exe = cmdline.split("\0")[0] ?? "";
1743
+ const base = exe.split("/").pop() ?? "";
1744
+ if (base !== binaryName && base !== "opencode" && base !== "pi") continue;
1731
1745
  } catch { continue; }
1732
1746
 
1733
1747
  let ppid = -1;
package/src/server.ts CHANGED
@@ -81,6 +81,7 @@ export function createServer(
81
81
  running: status.runningCount,
82
82
  pending: status.pendingCount,
83
83
  processes: status.processCount,
84
+ maxConcurrent: engine.getMaxConcurrent(),
84
85
  observedIssues: status.observedIssues,
85
86
  issues: (await store.listAllIssues()).length,
86
87
  sessions: (await store.listAllSessions()).length,