@danypops/papyrus 0.45.0 → 0.45.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/client.ts +26 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.45.0",
3
+ "version": "0.45.1",
4
4
  "description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package"],
package/src/client.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { spawn as spawnProcess } from "node:child_process";
2
2
  import { fileURLToPath } from "node:url";
3
- import { connectWithVersionCheck, type ExpectedVersion, spawnDetachedDaemon } from "@danypops/vehicle-client/daemon-client";
3
+ import {
4
+ connectWithVersionCheck,
5
+ type ExpectedVersion,
6
+ type SpawnPlatformOptions,
7
+ spawnDetachedDaemon,
8
+ } from "@danypops/vehicle-client/daemon-client";
4
9
  import { createLiveVersionExpectation } from "@danypops/vehicle-client/version";
5
10
  import { DAEMON_CLIENT_TIMEOUT_MS, DAEMON_DIR_ENV, DAEMON_PROBE_TIMEOUT_MS } from "./constants.ts";
6
11
  import { type DaemonHandle, daemonStateDir, readDaemonHandle } from "./daemon/daemon-state.ts";
@@ -74,6 +79,25 @@ function papyrusCliPath(): string {
74
79
  return fileURLToPath(new URL("cli.ts", import.meta.url));
75
80
  }
76
81
 
82
+ /**
83
+ * spawnDetachedDaemon's injected spawn() callback, factored out for a direct unit test.
84
+ *
85
+ * A spawn() failure (missing binPath, no exec permission, wrong interpreter) surfaces
86
+ * asynchronously as an "error" event on the ChildProcess -- with no listener, Node treats it
87
+ * as an uncaught exception and kills the whole host process, not just this one connect
88
+ * attempt (a real incident: auto-spawning against a since-deleted binPath crashed Pi itself).
89
+ * The listener below turns that into an ordinary logged failure instead: the handle file
90
+ * simply never appears, and connectWithPolicy's own poll-then-timeout already reports that
91
+ * as its usual, catchable fallbackMessage error.
92
+ */
93
+ export function spawnPapyrusDaemonProcess(command: string, args: string[], spawnOptions: SpawnPlatformOptions): void {
94
+ const child = spawnProcess(command, args, spawnOptions);
95
+ child.on("error", (error) => {
96
+ console.error(`Papyrus daemon auto-spawn failed: ${error instanceof Error ? error.message : String(error)}`);
97
+ });
98
+ child.unref();
99
+ }
100
+
77
101
  /** connectWithVersionCheck's killStaleProcess callback, factored out for a direct unit test -- a real spawned daemon can't be made to report a mismatched version without a second build. */
78
102
  export function killStalePapyrusDaemon(handle: Pick<DaemonHandle, "pid">): void {
79
103
  if (handle.pid <= 0) return; // daemon-state.ts's inert "unknown pid" sentinel -- never a real process.
@@ -123,10 +147,7 @@ export async function connectPapyrusClient(
123
147
  binPath: papyrusCliPath(),
124
148
  args: ["serve"],
125
149
  env: { ...(options.env ?? process.env), [DAEMON_DIR_ENV]: dir },
126
- spawn: (command, args, spawnOptions) => {
127
- const child = spawnProcess(command, args, spawnOptions);
128
- child.unref();
129
- },
150
+ spawn: spawnPapyrusDaemonProcess,
130
151
  });
131
152
  },
132
153
  fallbackMessage: "Papyrus daemon failed to start automatically; run `papyrus service install` or `papyrus serve` manually.",