@oh-my-pi/pi-utils 18.9.2 → 18.10.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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.10.0] - 2026-10-12
6
+
7
+ ### Added
8
+
9
+ - Added `getMnemopiEmbedRuntimeDir()` for the shared mnemopi embedding server endpoints and logs.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed `ChildProcess.kill()` silently leaving the child running when the native process handle could not be resolved or used; it now signals the child directly and escalates to SIGKILL after the grace period.
14
+
5
15
  ## [18.9.2] - 2026-10-11
6
16
 
7
17
  ### Changed
@@ -320,6 +320,8 @@ export declare function getDebugLogPath(agentDir?: string): string;
320
320
  export declare function getSecretPlaceholderKeyPath(): string;
321
321
  /** Directory holding the per-model tiny-worker sockets and logs (~/.omp/run/tiny; XDG default: $XDG_STATE_HOME/omp/run/tiny). */
322
322
  export declare function getTinyWorkerRuntimeDir(): string;
323
+ /** Directory holding the per-model mnemopi embedding server endpoints and logs (~/.omp/run/mnemopi; XDG default: $XDG_STATE_HOME/omp/run/mnemopi). */
324
+ export declare function getMnemopiEmbedRuntimeDir(): string;
323
325
  /** Root directory containing every per-project daemon runtime scope (~/.omp/run/daemons; XDG default: $XDG_STATE_HOME/omp/run/daemons). */
324
326
  export declare function getDaemonRuntimeRoot(): string;
325
327
  /** Get the daemon runtime directory for a project (~/.omp/run/daemons/<hash>; XDG default: $XDG_STATE_HOME/omp/run/daemons/<hash>). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-utils",
3
- "version": "18.9.2",
3
+ "version": "18.10.0",
4
4
  "description": "Shared utilities for pi packages",
5
5
  "keywords": [
6
6
  "cli",
@@ -57,7 +57,7 @@
57
57
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
58
58
  },
59
59
  "dependencies": {
60
- "@oh-my-pi/pi-natives": "18.9.2"
60
+ "@oh-my-pi/pi-natives": "18.10.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/bun": "^1.3.14"
package/src/dirs.ts CHANGED
@@ -1082,6 +1082,11 @@ export function getTinyWorkerRuntimeDir(): string {
1082
1082
  return dirs.rootSubdir(path.join("run", "tiny"), "state");
1083
1083
  }
1084
1084
 
1085
+ /** Directory holding the per-model mnemopi embedding server endpoints and logs (~/.omp/run/mnemopi; XDG default: $XDG_STATE_HOME/omp/run/mnemopi). */
1086
+ export function getMnemopiEmbedRuntimeDir(): string {
1087
+ return dirs.rootSubdir(path.join("run", "mnemopi"), "state");
1088
+ }
1089
+
1085
1090
  /** Root directory containing every per-project daemon runtime scope (~/.omp/run/daemons; XDG default: $XDG_STATE_HOME/omp/run/daemons). */
1086
1091
  export function getDaemonRuntimeRoot(): string {
1087
1092
  return dirs.rootSubdir(path.join("run", "daemons"), "state");
package/src/ptree.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * - Convenience helpers: captureText / execText, AbortSignal, timeouts.
8
8
  */
9
9
 
10
- import { Process } from "@oh-my-pi/pi-natives";
10
+ import { Process, type ProcessTerminateOptions } from "@oh-my-pi/pi-natives";
11
11
  import type { Spawn, Subprocess } from "bun";
12
12
 
13
13
  type InMask = "pipe" | "ignore" | Buffer | Uint8Array | null;
@@ -387,12 +387,40 @@ export class ChildProcess<In extends InMask = InMask> {
387
387
  ? { group: true }
388
388
  : undefined
389
389
  : { gracefulMs, group: this.#terminateGroup };
390
- this.#terminating = (this.#windowsRootProcess ?? Process.fromPid(this.proc.pid))
391
- ?.terminate(options)
392
- ?.catch(e => void e);
390
+ this.#terminating = this.#terminateTree(options);
393
391
  }
394
392
  }
395
393
 
394
+ /**
395
+ * Native tree termination. A native handle that cannot be resolved or used
396
+ * — the PID left the process table, or a `bun test --isolate` realm whose
397
+ * napi classes another realm superseded ("Value is not an instance of class
398
+ * `Process`") — must not leave the child running, so signal it directly.
399
+ */
400
+ #terminateTree(options: ProcessTerminateOptions | undefined): Promise<boolean | void> {
401
+ const gracefulMs = options?.gracefulMs ?? 1000;
402
+ try {
403
+ const root = this.#windowsRootProcess ?? Process.fromPid(this.proc.pid);
404
+ if (root) return root.terminate(options).catch(() => this.#signalChild(gracefulMs));
405
+ } catch {}
406
+ return this.#signalChild(gracefulMs);
407
+ }
408
+
409
+ /** SIGTERM the child (its group when it leads one), then SIGKILL if it outlives `gracefulMs`. */
410
+ async #signalChild(gracefulMs: number): Promise<void> {
411
+ const signal = (name: NodeJS.Signals) => {
412
+ try {
413
+ if (this.#terminateGroup && process.platform !== "win32") process.kill(-this.proc.pid, name);
414
+ else this.proc.kill(name);
415
+ } catch {}
416
+ };
417
+ if (gracefulMs >= 0) {
418
+ signal("SIGTERM");
419
+ await Promise.race([this.proc.exited, Bun.sleep(gracefulMs)]);
420
+ }
421
+ if (this.proc.exitCode === null && this.proc.signalCode === null) signal("SIGKILL");
422
+ }
423
+
396
424
  async #hardKillSubreaperTree(root: Process): Promise<void> {
397
425
  try {
398
426
  const deadline = Date.now() + SUBREAPER_KILL_WINDOW_MS;