@hienlh/ppm 0.13.30 → 0.13.31

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
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.13.31] - 2026-04-25
4
+
5
+ ### Fixed
6
+ - **Windows restart fails with SIGUSR2 error**: `ppm restart` on Windows threw `TypeError [ERR_UNKNOWN_SIGNAL]: Unknown signal: SIGUSR2` — Unix signals don't exist on Windows. Now writes command file that supervisor polls every 1s (same mechanism as upgrade). Also fixed `ppm start` sending SIGUSR1/SIGUSR2 directly on Windows for resume/upgrade paths
7
+
3
8
  ## [0.13.30] - 2026-04-25
4
9
 
5
10
  ### Fixed
@@ -71,4 +71,4 @@ This skill covers the `ppm` CLI, its HTTP API, and its config DB. It does **not*
71
71
  - Third-party extensions (inspect via `ppm ext list`).
72
72
  - The Claude Agent SDK internals (separate skill).
73
73
 
74
- <!-- Generated for PPM v0.13.30 at build time. Re-run `ppm export skill --install` to refresh. -->
74
+ <!-- Generated for PPM v0.13.31 at build time. Re-run `ppm export skill --install` to refresh. -->
@@ -201,4 +201,4 @@ _Base URL: `http://localhost:8080` (default; override via `ppm config set port <
201
201
  - `ws://<host>/ws/terminal` — PTY terminal multiplexer
202
202
  - `ws://<host>/ws/extensions` — extension host channel
203
203
 
204
- <!-- Generated from src/server/routes/ for PPM v0.13.30 -->
204
+ <!-- Generated from src/server/routes/ for PPM v0.13.31 -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.13.30",
3
+ "version": "0.13.31",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -74,9 +74,14 @@ export async function restartServer(options: { force?: boolean }) {
74
74
  console.log("\n Restarting PPM server via supervisor...");
75
75
  console.log(" If you're using PPM terminal, wait a few seconds for auto-reconnect.\n");
76
76
 
77
- try { process.kill(supervisorPid, "SIGUSR2"); } catch (e) {
78
- console.error(` ✗ Failed to signal supervisor: ${e}`);
79
- process.exit(1);
77
+ if (process.platform === "win32") {
78
+ const cmdFile = resolve(getPpmDir(), ".supervisor-cmd");
79
+ writeFileSync(cmdFile, JSON.stringify({ action: "restart" }));
80
+ } else {
81
+ try { process.kill(supervisorPid, "SIGUSR2"); } catch (e) {
82
+ console.error(` ✗ Failed to signal supervisor: ${e}`);
83
+ process.exit(1);
84
+ }
80
85
  }
81
86
 
82
87
  // Wait for new server PID to appear in status.json (up to 15s)
@@ -269,15 +269,26 @@ export async function startServer(options: {
269
269
  const state = status.state as string;
270
270
  const runningVersion = status.serverVersion as string;
271
271
 
272
+ // Helper: send signal (Unix) or write command file (Windows)
273
+ const signalSupervisor = (signal: "SIGUSR1" | "SIGUSR2", cmdAction?: string) => {
274
+ if (process.platform === "win32") {
275
+ const cmdFile = resolve(ppmDir, ".supervisor-cmd");
276
+ const action = cmdAction ?? (signal === "SIGUSR1" ? "upgrade" : "restart");
277
+ writeFileSync(cmdFile, JSON.stringify({ action }));
278
+ } else {
279
+ process.kill(supervisorPid, signal);
280
+ }
281
+ };
282
+
272
283
  if (state === "stopped") {
273
284
  console.log(" Supervisor is alive (stopped state). Resuming server...");
274
285
  if (runningVersion !== VERSION) {
275
286
  console.log(` Upgrading: ${runningVersion} -> ${VERSION}`);
276
- process.kill(supervisorPid, "SIGUSR1");
287
+ signalSupervisor("SIGUSR1");
277
288
  await waitForNewSupervisor(statusFile, supervisorPid);
278
289
  } else {
279
290
  writeCmd("resume");
280
- process.kill(supervisorPid, "SIGUSR2");
291
+ signalSupervisor("SIGUSR2", "resume");
281
292
  await waitForServerReady(statusFile, port);
282
293
  }
283
294
  return;
@@ -286,7 +297,7 @@ export async function startServer(options: {
286
297
  if (state === "running") {
287
298
  if (runningVersion !== VERSION) {
288
299
  console.log(` Supervisor running (v${runningVersion}). Upgrading to v${VERSION}...`);
289
- process.kill(supervisorPid, "SIGUSR1");
300
+ signalSupervisor("SIGUSR1");
290
301
  await waitForNewSupervisor(statusFile, supervisorPid);
291
302
  } else {
292
303
  console.log(`\n PPM is already running (PID: ${supervisorPid}).`);
@@ -299,7 +310,7 @@ export async function startServer(options: {
299
310
  if (state === "paused") {
300
311
  console.log(" Supervisor is paused (max restarts). Sending resume...");
301
312
  writeCmd("resume");
302
- process.kill(supervisorPid, "SIGUSR2");
313
+ signalSupervisor("SIGUSR2", "resume");
303
314
  await waitForServerReady(statusFile, port);
304
315
  return;
305
316
  }
@@ -71,7 +71,7 @@ export function writeStatus(data: Record<string, unknown>) {
71
71
  }
72
72
 
73
73
  // ─── Command file protocol ─────────────────────────────────────────────
74
- export type CmdAction = "soft_stop" | "resume";
74
+ export type CmdAction = "soft_stop" | "resume" | "restart";
75
75
 
76
76
  /** Atomically claim + read command file (rename to .claimed, read, delete) */
77
77
  export function readAndDeleteCmd(): { action: CmdAction } | null {
@@ -985,6 +985,15 @@ export async function runSupervisor(opts: {
985
985
  else if (cmd.action === "resume") {
986
986
  if (getState() === "stopped" || getState() === "paused") triggerResume();
987
987
  }
988
+ else if (cmd.action === "restart") {
989
+ log("INFO", "Windows command: restart server only");
990
+ if (getState() === "paused" || getState() === "stopped") {
991
+ triggerResume();
992
+ } else if (serverChild) {
993
+ serverRestartRequested = true;
994
+ try { serverChild.kill(); } catch {}
995
+ }
996
+ }
988
997
  else if (cmd.action === "upgrade") {
989
998
  log("INFO", "Windows command: upgrade, starting self-replace");
990
999
  selfReplace().then((result) => {