@oh-my-pi/pi-utils 14.5.10 → 14.5.12

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,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-utils",
4
- "version": "14.5.10",
4
+ "version": "14.5.12",
5
5
  "description": "Shared utilities for pi packages",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/bun": "^1.3",
41
- "@oh-my-pi/pi-natives": "14.5.10"
41
+ "@oh-my-pi/pi-natives": "14.5.12"
42
42
  },
43
43
  "engines": {
44
44
  "bun": ">=1.3.7"
package/src/index.ts CHANGED
@@ -15,7 +15,6 @@ export * from "./mime";
15
15
  export * from "./peek-file";
16
16
  export * as postmortem from "./postmortem";
17
17
  export * as procmgr from "./procmgr";
18
- export { setNativeKillTree } from "./procmgr";
19
18
  export * as prompt from "./prompt";
20
19
  export * as ptree from "./ptree";
21
20
  export { AbortError, ChildProcess, Exception, NonZeroExitError } from "./ptree";
package/src/procmgr.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
- import path from "node:path";
3
- import * as timers from "node:timers";
2
+ import * as path from "node:path";
3
+ import { Process, ProcessStatus } from "@oh-my-pi/pi-natives";
4
4
  import type { Subprocess } from "bun";
5
5
  import { $env } from "./env";
6
6
  import { $which } from "./which";
@@ -14,9 +14,6 @@ export interface ShellConfig {
14
14
 
15
15
  let cachedShellConfig: ShellConfig | null = null;
16
16
 
17
- const IS_WINDOWS = process.platform === "win32";
18
- const TERM_SIGNAL = IS_WINDOWS ? undefined : "SIGTERM";
19
-
20
17
  /**
21
18
  * Check if a shell binary is executable.
22
19
  */
@@ -174,64 +171,20 @@ export function getShellConfig(customShellPath?: string): ShellConfig {
174
171
  return cachedShellConfig;
175
172
  }
176
173
 
177
- /**
178
- * Function signature for native process tree killing.
179
- * Returns the number of processes killed.
180
- */
181
- export type KillTreeFn = (pid: number, signal: number) => number;
182
-
183
- /**
184
- * Global native kill tree function, injected by pi-natives when loaded.
185
- * Falls back to platform-specific behavior if not set.
186
- */
187
- export let nativeKillTree: KillTreeFn | undefined;
188
-
189
- /**
190
- * Set the native kill tree function. Called by pi-natives on load.
191
- */
192
- export function setNativeKillTree(fn: KillTreeFn): void {
193
- nativeKillTree = fn;
194
- }
195
-
196
- /**
197
- * Options for terminating a process and all its descendants.
198
- */
199
- export interface TerminateOptions {
200
- /** The process to terminate */
201
- target: Subprocess | number;
202
- /** Whether to terminate the process tree (all descendants) */
203
- group?: boolean;
204
- /** Timeout in milliseconds */
205
- timeout?: number;
206
- /** Abort signal */
207
- signal?: AbortSignal;
208
- }
209
-
210
174
  /**
211
175
  * Check if a process is running.
212
176
  */
213
177
  export function isPidRunning(pid: number | Subprocess): boolean {
214
- try {
215
- if (typeof pid === "number") {
216
- process.kill(pid, 0);
217
- } else {
218
- if (pid.killed) return false;
219
- if (pid.exitCode !== null) return false;
220
- }
178
+ if (typeof pid !== "number") {
179
+ if (pid.killed) return false;
180
+ if (pid.exitCode !== null) return false;
221
181
  return true;
222
- } catch {
223
- return false;
224
182
  }
225
- }
226
183
 
227
- function joinSignals(...sigs: (AbortSignal | null | undefined)[]): AbortSignal | undefined {
228
- const nn = sigs.filter(Boolean) as AbortSignal[];
229
- if (nn.length === 0) return undefined;
230
- if (nn.length === 1) return nn[0];
231
- return AbortSignal.any(nn);
184
+ return Process.fromPid(pid)?.status() === ProcessStatus.Running;
232
185
  }
233
186
 
234
- export function onProcessExit(proc: Subprocess | number, abortSignal?: AbortSignal): Promise<boolean> {
187
+ export async function onProcessExit(proc: Subprocess | number, abortSignal?: AbortSignal): Promise<boolean> {
235
188
  if (typeof proc !== "number") {
236
189
  return proc.exited.then(
237
190
  () => true,
@@ -239,88 +192,5 @@ export function onProcessExit(proc: Subprocess | number, abortSignal?: AbortSign
239
192
  );
240
193
  }
241
194
 
242
- if (!isPidRunning(proc)) {
243
- return Promise.resolve(true);
244
- }
245
-
246
- const { promise, resolve, reject } = Promise.withResolvers<boolean>();
247
- const localAbortController = new AbortController();
248
-
249
- const timer = timers.promises.setInterval(300, null, {
250
- signal: joinSignals(abortSignal, localAbortController.signal),
251
- });
252
- void (async () => {
253
- try {
254
- for await (const _ of timer) {
255
- if (!isPidRunning(proc)) {
256
- resolve(true);
257
- break;
258
- }
259
- }
260
- } catch (error) {
261
- return reject(error);
262
- } finally {
263
- localAbortController.abort();
264
- }
265
- resolve(false);
266
- })();
267
-
268
- return promise;
269
- }
270
-
271
- /**
272
- * Terminate a process and all its descendants.
273
- */
274
- export async function terminate(options: TerminateOptions): Promise<boolean> {
275
- const { target, group = false, timeout = 5000, signal } = options;
276
-
277
- const abortController = new AbortController();
278
- try {
279
- const abortSignal = joinSignals(signal, abortController.signal);
280
-
281
- // Determine PID
282
- let pid: number | undefined;
283
- const exitPromise = onProcessExit(target, abortSignal);
284
- if (typeof target === "number") {
285
- pid = target;
286
- } else {
287
- pid = target.pid;
288
- if (target.killed) return true;
289
- }
290
-
291
- // Give it a moment to exit gracefully first.
292
- try {
293
- if (typeof target === "number") {
294
- process.kill(target, TERM_SIGNAL);
295
- } else {
296
- target.kill(TERM_SIGNAL);
297
- }
298
-
299
- if (exitPromise) {
300
- const exited = await Promise.race([Bun.sleep(1000).then(() => false), exitPromise]);
301
- if (exited) return true;
302
- }
303
- } catch {}
304
-
305
- if (nativeKillTree) {
306
- nativeKillTree(pid, 9);
307
- } else {
308
- if (group && !IS_WINDOWS) {
309
- try {
310
- process.kill(-pid, "SIGKILL");
311
- } catch {}
312
- }
313
- try {
314
- if (typeof target === "number") {
315
- process.kill(target, "SIGKILL");
316
- } else {
317
- target.kill("SIGKILL");
318
- }
319
- } catch {}
320
- }
321
-
322
- return await Promise.race([Bun.sleep(timeout).then(() => false), exitPromise]);
323
- } finally {
324
- abortController.abort();
325
- }
195
+ return (await Process.fromPid(proc)?.waitForExit({ signal: abortSignal })) ?? true;
326
196
  }
package/src/ptree.ts CHANGED
@@ -6,8 +6,9 @@
6
6
  * - Cross-platform tree kill for process groups (Windows taskkill, Unix -pid).
7
7
  * - Convenience helpers: captureText / execText, AbortSignal, timeouts.
8
8
  */
9
+
10
+ import { Process } from "@oh-my-pi/pi-natives";
9
11
  import type { Spawn, Subprocess } from "bun";
10
- import { terminate } from "./procmgr";
11
12
 
12
13
  type InMask = "pipe" | "ignore" | Buffer | Uint8Array | null;
13
14
 
@@ -215,7 +216,10 @@ export class ChildProcess<In extends InMask = InMask> {
215
216
 
216
217
  kill(reason?: Exception) {
217
218
  if (reason && !this.#exitReasonPending) this.#exitReasonPending = reason;
218
- if (!this.proc.killed) void terminate({ target: this.proc });
219
+ if (!this.proc.killed)
220
+ void Process.fromPid(this.proc.pid)
221
+ ?.terminate()
222
+ ?.catch(e => void e);
219
223
  }
220
224
 
221
225
  // ── Output helpers ───────────────────────────────────────────────────