@smithers-orchestrator/driver 0.21.0 → 0.22.0

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": "@smithers-orchestrator/driver",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Workflow execution driver — runs tasks, acts on EngineDecisions, reports results",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -63,11 +63,11 @@
63
63
  "dependencies": {
64
64
  "effect": "^3.21.1",
65
65
  "zod": "^4.3.6",
66
- "@smithers-orchestrator/db": "0.21.0",
67
- "@smithers-orchestrator/graph": "0.21.0",
68
- "@smithers-orchestrator/errors": "0.21.0",
69
- "@smithers-orchestrator/observability": "0.21.0",
70
- "@smithers-orchestrator/scheduler": "0.21.0"
66
+ "@smithers-orchestrator/graph": "0.22.0",
67
+ "@smithers-orchestrator/errors": "0.22.0",
68
+ "@smithers-orchestrator/scheduler": "0.22.0",
69
+ "@smithers-orchestrator/observability": "0.22.0",
70
+ "@smithers-orchestrator/db": "0.22.0"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@types/bun": "latest",
@@ -88,13 +88,24 @@ export function spawnCaptureEffect(command, args, options) {
88
88
  };
89
89
  let totalTimer;
90
90
  let idleTimer;
91
+ let idleGeneration = 0;
92
+ // Bun can deliver child stdout/stderr chunks late on macOS; avoid
93
+ // killing active CLI agents before observable output reaches JS.
94
+ const effectiveIdleTimeoutMs = typeof process.versions.bun === "string" && idleTimeoutMs >= 1000
95
+ ? Math.max(idleTimeoutMs, 5000)
96
+ : idleTimeoutMs;
91
97
  const resetIdle = () => {
92
98
  if (idleTimer)
93
99
  clearTimeout(idleTimer);
94
- if (idleTimeoutMs) {
100
+ if (effectiveIdleTimeoutMs) {
101
+ idleGeneration += 1;
102
+ const generation = idleGeneration;
95
103
  idleTimer = setTimeout(() => {
104
+ if (generation !== idleGeneration) {
105
+ return;
106
+ }
96
107
  kill(`CLI idle timed out after ${idleTimeoutMs}ms`, "PROCESS_IDLE_TIMEOUT");
97
- }, idleTimeoutMs);
108
+ }, effectiveIdleTimeoutMs);
98
109
  }
99
110
  };
100
111
  if (timeoutMs) {
@@ -188,6 +199,12 @@ export function spawnCaptureEffect(command, args, options) {
188
199
  child.on("close", (code) => {
189
200
  finalize({ stdout, stderr, exitCode: code ?? null });
190
201
  });
202
+ child.stdin?.on("error", (error) => {
203
+ logWarning("child process stdin error", {
204
+ ...logAnnotations,
205
+ error: error instanceof Error ? error.message : String(error),
206
+ }, span);
207
+ });
191
208
  if (input) {
192
209
  child.stdin?.write(input);
193
210
  }
package/src/withAbort.js CHANGED
@@ -16,18 +16,23 @@ function throwIfAborted(signal) {
16
16
  }
17
17
  /**
18
18
  * @param {AbortSignal} [signal]
19
- * @returns {Promise<never> | null}
19
+ * @returns {{ promise: Promise<never>, cleanup: () => void } | null}
20
20
  */
21
21
  function abortPromise(signal) {
22
22
  if (!signal)
23
23
  return null;
24
24
  if (signal.aborted)
25
- return Promise.reject(makeAbortError());
26
- return new Promise((_, reject) => {
27
- signal.addEventListener("abort", () => reject(makeAbortError()), {
28
- once: true,
29
- });
25
+ return { promise: Promise.reject(makeAbortError()), cleanup: () => {} };
26
+ /** @type {() => void} */
27
+ let cleanup = () => {};
28
+ const promise = new Promise((_, reject) => {
29
+ function onAbort() {
30
+ reject(makeAbortError());
31
+ }
32
+ signal.addEventListener("abort", onAbort, { once: true });
33
+ cleanup = () => signal.removeEventListener("abort", onAbort);
30
34
  });
35
+ return { promise, cleanup };
31
36
  }
32
37
  /**
33
38
  * @template T
@@ -39,5 +44,12 @@ export async function withAbort(value, signal) {
39
44
  throwIfAborted(signal);
40
45
  const abort = abortPromise(signal);
41
46
  const promise = Promise.resolve(value);
42
- return abort ? Promise.race([promise, abort]) : promise;
47
+ if (!abort)
48
+ return promise;
49
+ try {
50
+ return await Promise.race([promise, abort.promise]);
51
+ }
52
+ finally {
53
+ abort.cleanup();
54
+ }
43
55
  }