@ouro.bot/cli 0.1.0-alpha.550 → 0.1.0-alpha.551

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/changelog.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.551",
6
+ "changes": [
7
+ "`ouro up` now waits for SIGTERMed orphan daemon processes to settle before opening the replacement Unix socket, preventing the previous daemon from unlinking the new daemon's command socket during orphan cleanup."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.550",
6
12
  "changes": [
@@ -37,6 +37,7 @@ exports.OuroDaemon = void 0;
37
37
  exports.parseOrphanPidsFromPs = parseOrphanPidsFromPs;
38
38
  exports.filterPidfilePidsToActualOrphans = filterPidfilePidsToActualOrphans;
39
39
  exports.mergeUniqueOrphanPids = mergeUniqueOrphanPids;
40
+ exports.waitForOrphanProcessesToSettle = waitForOrphanProcessesToSettle;
40
41
  exports.killOrphanProcesses = killOrphanProcesses;
41
42
  exports.writePidfile = writePidfile;
42
43
  exports.handleAgentSenseTurn = handleAgentSenseTurn;
@@ -176,6 +177,40 @@ function mergeUniqueOrphanPids(...sources) {
176
177
  }
177
178
  return merged;
178
179
  }
180
+ const ORPHAN_CLEANUP_SETTLE_TIMEOUT_MS = 5_000;
181
+ const ORPHAN_CLEANUP_SETTLE_POLL_INTERVAL_MS = 50;
182
+ /* v8 ignore start -- process liveness probe; pure wait behavior covered via injected deps @preserve */
183
+ function defaultIsPidAlive(pid) {
184
+ try {
185
+ process.kill(pid, 0);
186
+ return true;
187
+ }
188
+ catch (error) {
189
+ return error.code === "EPERM";
190
+ }
191
+ }
192
+ /* v8 ignore stop */
193
+ /* v8 ignore start -- real timer wiring; wait behavior covered via injected sleep @preserve */
194
+ async function defaultSettleSleep(ms) {
195
+ await new Promise((resolve) => setTimeout(resolve, ms));
196
+ }
197
+ /* v8 ignore stop */
198
+ async function waitForOrphanProcessesToSettle(pids, deps = {}) {
199
+ if (pids.length === 0)
200
+ return [];
201
+ const isPidAlive = deps.isPidAlive ?? defaultIsPidAlive;
202
+ const now = deps.now ?? Date.now;
203
+ const sleep = deps.sleep ?? defaultSettleSleep;
204
+ const timeoutMs = deps.timeoutMs ?? ORPHAN_CLEANUP_SETTLE_TIMEOUT_MS;
205
+ const pollIntervalMs = deps.pollIntervalMs ?? ORPHAN_CLEANUP_SETTLE_POLL_INTERVAL_MS;
206
+ const deadline = now() + timeoutMs;
207
+ let survivors = pids.filter(isPidAlive);
208
+ while (survivors.length > 0 && now() < deadline) {
209
+ await sleep(pollIntervalMs);
210
+ survivors = pids.filter(isPidAlive);
211
+ }
212
+ return survivors;
213
+ }
179
214
  /* v8 ignore start -- shells out to ps; covered by filterPidfilePidsToActualOrphans unit tests via injected runner @preserve */
180
215
  function runPsCheck(pids) {
181
216
  try {
@@ -217,7 +252,7 @@ function killOrphanProcesses(socketPath = socket_client_1.DEFAULT_DAEMON_SOCKET_
217
252
  message: "blocked orphan cleanup for non-production daemon socket",
218
253
  meta: { socketPath, pidfilePath: PIDFILE_PATH },
219
254
  });
220
- return;
255
+ return [];
221
256
  }
222
257
  if (isVitestProcess()) {
223
258
  (0, runtime_1.emitNervesEvent)({
@@ -227,7 +262,7 @@ function killOrphanProcesses(socketPath = socket_client_1.DEFAULT_DAEMON_SOCKET_
227
262
  message: "blocked killOrphanProcesses from touching real pidfile under vitest",
228
263
  meta: { pidfilePath: PIDFILE_PATH },
229
264
  });
230
- return;
265
+ return [];
231
266
  }
232
267
  try {
233
268
  let pidfileOrphans = [];
@@ -269,6 +304,7 @@ function killOrphanProcesses(socketPath = socket_client_1.DEFAULT_DAEMON_SOCKET_
269
304
  meta: { pids: pidsToKill },
270
305
  });
271
306
  }
307
+ return pidsToKill;
272
308
  }
273
309
  catch (error) {
274
310
  (0, runtime_1.emitNervesEvent)({
@@ -278,6 +314,7 @@ function killOrphanProcesses(socketPath = socket_client_1.DEFAULT_DAEMON_SOCKET_
278
314
  message: "failed to clean up orphaned ouro processes",
279
315
  meta: { error: error instanceof Error ? error.message : String(error) },
280
316
  });
317
+ return [];
281
318
  }
282
319
  }
283
320
  /**
@@ -594,7 +631,8 @@ class OuroDaemon {
594
631
  // MCP connections are lazily initialized per-agent during senseTurn
595
632
  // (daemon manages multiple agents; agent identity must be set before loading MCP config)
596
633
  /* v8 ignore start -- orphan cleanup + pidfile: calls process management functions @preserve */
597
- killOrphanProcesses(this.socketPath);
634
+ const killedOrphanPids = killOrphanProcesses(this.socketPath);
635
+ await waitForOrphanProcessesToSettle(killedOrphanPids);
598
636
  /* v8 ignore stop */
599
637
  await this.openCommandSocket();
600
638
  this.triggerAutoStartAgents();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.550",
3
+ "version": "0.1.0-alpha.551",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",