@aliou/pi-processes 0.10.0 → 0.10.1

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.
@@ -17,5 +17,5 @@ export const configVersionStampMigration: Migration<ProcessConfig> = {
17
17
  shouldRun: needsConfigVersionStamp,
18
18
  run: (config) => stampConfigVersion(config),
19
19
  message:
20
- "Updated pi-processes settings for v0.10.0. This release rewrites the package into separate process, logs, and dock extensions while preserving your existing settings. Release notes will be published at https://github.com/aliou/pi-processes/releases/tag/v0.10.0.",
20
+ "Updated pi-processes settings for v0.10.0. This release rewrites the package into separate process, logs, and dock extensions while preserving your existing settings. Release notes are available at https://github.com/aliou/pi-processes/releases/tag/v0.10.0.",
21
21
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliou/pi-processes",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "private": false,
@@ -68,12 +68,6 @@
68
68
  "typescript": "^5.9.3",
69
69
  "vitest": "^4.1.5"
70
70
  },
71
- "pnpm": {
72
- "overrides": {
73
- "@earendil-works/pi-ai": "$@earendil-works/pi-coding-agent",
74
- "@earendil-works/pi-tui": "$@earendil-works/pi-coding-agent"
75
- }
76
- },
77
71
  "scripts": {
78
72
  "typecheck": "tsc --noEmit",
79
73
  "lint": "biome check",
@@ -88,7 +82,7 @@
88
82
  "test:e2e": "vitest run --config vitest.e2e.config.ts",
89
83
  "release": "pnpm changeset publish"
90
84
  },
91
- "packageManager": "pnpm@10.26.1",
85
+ "packageManager": "pnpm@11.5.1",
92
86
  "peerDependenciesMeta": {
93
87
  "@earendil-works/pi-ai": {
94
88
  "optional": true
@@ -83,6 +83,11 @@ export class ProcessRuntimeController {
83
83
  this.registry.add(managed);
84
84
 
85
85
  if (!child.pid) {
86
+ // No pid means the process never started. The async spawn `error`
87
+ // event may not have been delivered yet (Node emits it on a later
88
+ // turn of the event loop, and it cannot fire while start() runs),
89
+ // so attach a handler that finalizes the record with the real
90
+ // reason when it arrives.
86
91
  this.logs.appendErrorLine(managed.stderrFile, "Spawn error: missing pid");
87
92
  managed.exitCode = -1;
88
93
  managed.success = false;
@@ -91,6 +96,14 @@ export class ProcessRuntimeController {
91
96
  managed.endTime = Date.now();
92
97
  this.releaseRuntimeHandles(managed);
93
98
  this.transition(managed, "exited");
99
+ child.on("error", (err) => {
100
+ this.logs.appendErrorLine(
101
+ managed.stderrFile,
102
+ `Process error: ${err.message}`,
103
+ );
104
+ managed.endReason = "spawn_error";
105
+ managed.errorMessage = err.message;
106
+ });
94
107
  return managed;
95
108
  }
96
109
 
@@ -361,24 +374,31 @@ export class ProcessRuntimeController {
361
374
  });
362
375
 
363
376
  child.on("error", (err) => {
364
- this.logs.appendErrorLine(
365
- managed.stderrFile,
366
- `Process error: ${err.message}`,
367
- );
368
-
369
- if (!managed.endTime) {
370
- this.releaseRuntimeHandles(managed);
371
- managed.exitCode = -1;
372
- managed.success = false;
373
- managed.endReason = "spawn_error";
374
- managed.errorMessage = err.message;
375
- managed.endTime = Date.now();
376
- this.output.flush(managed);
377
- this.transition(managed, "exited");
378
- }
377
+ this.handleSpawnError(managed, err);
379
378
  });
380
379
  }
381
380
 
381
+ private handleSpawnError(
382
+ managed: ManagedProcessRecord,
383
+ err: NodeJS.ErrnoException,
384
+ ): void {
385
+ this.logs.appendErrorLine(
386
+ managed.stderrFile,
387
+ `Process error: ${err.message}`,
388
+ );
389
+
390
+ if (managed.endTime) return;
391
+
392
+ this.releaseRuntimeHandles(managed);
393
+ managed.exitCode = -1;
394
+ managed.success = false;
395
+ managed.endReason = "spawn_error";
396
+ managed.errorMessage = err.message;
397
+ managed.endTime = Date.now();
398
+ this.output.flush(managed);
399
+ this.transition(managed, "exited");
400
+ }
401
+
382
402
  private ensureWatcherRunning(): void {
383
403
  if (this.watcher) return;
384
404
  if (!this.registry.hasAliveishProcesses()) return;
@@ -3,6 +3,7 @@
3
3
  * Uses signal 0 to test existence without actually sending a signal.
4
4
  */
5
5
  export function isProcessGroupAlive(pgid: number): boolean {
6
+ if (!Number.isInteger(pgid) || pgid <= 0) return false;
6
7
  try {
7
8
  process.kill(-pgid, 0);
8
9
  return true;
@@ -18,5 +19,8 @@ export function isProcessGroupAlive(pgid: number): boolean {
18
19
  * Negative PID targets the process group.
19
20
  */
20
21
  export function killProcessGroup(pgid: number, signal: NodeJS.Signals): void {
22
+ if (!Number.isInteger(pgid) || pgid <= 0) {
23
+ throw new RangeError("Process group ID must be a positive integer");
24
+ }
21
25
  process.kill(-pgid, signal);
22
26
  }