@agentprojectcontext/apx 1.40.0 → 1.40.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentprojectcontext/apx",
3
- "version": "1.40.0",
3
+ "version": "1.40.1",
4
4
  "description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -277,6 +277,32 @@ export async function cmdDesktopStop(_args = {}) {
277
277
  }
278
278
  }
279
279
 
280
+ // True when the desktop Electron process is alive. Used by `apx restart` so it
281
+ // only re-launches the desktop if the user already had it running.
282
+ export function desktopRunning() {
283
+ return pidAlive(readPid());
284
+ }
285
+
286
+ // Stop (if running) then start — the only way the desktop picks up new
287
+ // renderer/main.js code after a pull, since the Electron process holds the
288
+ // old bundle in memory. Token re-sync after a daemon restart is handled
289
+ // automatically by the WS reconnect (it re-reads daemon.token), so this is
290
+ // purely about refreshing stale desktop CODE.
291
+ export async function cmdDesktopRestart(args = {}) {
292
+ const pid = readPid();
293
+ if (pidAlive(pid)) {
294
+ try { process.kill(pid, "SIGTERM"); } catch {}
295
+ clearPid();
296
+ // Give Electron a moment to release the tray/shortcut before relaunch.
297
+ await new Promise((r) => setTimeout(r, 600));
298
+ process.stderr.write("apx: restarting desktop...\n");
299
+ } else {
300
+ clearPid();
301
+ process.stderr.write("apx: desktop not running — starting...\n");
302
+ }
303
+ await cmdDesktopStart(args);
304
+ }
305
+
280
306
  export async function cmdDesktopStatus(_args = {}) {
281
307
  const pid = readPid();
282
308
  const alive = pidAlive(pid);
@@ -99,7 +99,7 @@ import {
99
99
  cmdPermission,
100
100
  } from "./commands/config.js";
101
101
  import { cmdPluginsList, cmdPluginStatus } from "./commands/plugins.js";
102
- import { cmdDesktopStart, cmdDesktopStop, cmdDesktopStatus, cmdDesktopInstall, cmdDesktopUninstall } from "./commands/desktop.js";
102
+ import { cmdDesktopStart, cmdDesktopStop, cmdDesktopRestart, cmdDesktopStatus, cmdDesktopInstall, cmdDesktopUninstall, desktopRunning } from "./commands/desktop.js";
103
103
  import { cmdVoiceSay, cmdVoiceListen, cmdVoiceProviders } from "./commands/voice.js";
104
104
  import { cmdSkillsAdd, cmdSkillsList, cmdSkillsStatus, cmdSkillsSync, cmdSkillsIndex, cmdSkillsInspect, cmdSkillsInspector } from "./commands/skills.js";
105
105
  import { cmdIdentity } from "./commands/identity.js";
@@ -2615,6 +2615,18 @@ async function dispatch(cmd, rest) {
2615
2615
  await cmdUpdate(parseArgs(rest), VERSION);
2616
2616
  return; // skip checkForUpdate after an update
2617
2617
 
2618
+ // Refresh everything held in memory after a code change (e.g. a `git
2619
+ // pull` in a dev checkout): restart the daemon, and restart the desktop
2620
+ // too if it was running. The daemon picks up new code/prompts; the
2621
+ // desktop picks up its new renderer/main.js. Token re-sync is automatic
2622
+ // (the desktop WS re-reads daemon.token on reconnect).
2623
+ case "restart": {
2624
+ const a = parseArgs(rest);
2625
+ await cmdDaemonRestart(a);
2626
+ if (desktopRunning()) await cmdDesktopRestart(a);
2627
+ return;
2628
+ }
2629
+
2618
2630
  case "overlay":
2619
2631
  console.error(" apx overlay has been renamed to apx desktop — forwarding.");
2620
2632
  /* falls through */
@@ -2623,10 +2635,11 @@ async function dispatch(cmd, rest) {
2623
2635
  const oArgs = parseArgs(oRest);
2624
2636
  if (!sub || sub === "start") { await cmdDesktopStart(oArgs); return; }
2625
2637
  if (sub === "stop") { await cmdDesktopStop(oArgs); return; }
2638
+ if (sub === "restart") { await cmdDesktopRestart(oArgs); return; }
2626
2639
  if (sub === "status") { await cmdDesktopStatus(oArgs);return; }
2627
2640
  if (sub === "install") { await cmdDesktopInstall(oArgs); return; }
2628
2641
  if (sub === "uninstall") { await cmdDesktopUninstall(oArgs);return; }
2629
- die(`unknown desktop sub-command: ${sub}\nUsage: apx desktop <start|stop|status|install|uninstall>`);
2642
+ die(`unknown desktop sub-command: ${sub}\nUsage: apx desktop <start|stop|restart|status|install|uninstall>`);
2630
2643
  return;
2631
2644
  }
2632
2645
 
@@ -2656,7 +2669,7 @@ const [topCmd, ...topRest] = argv;
2656
2669
  // of the compact line.
2657
2670
  // Suppress everything with APX_QUIET=1 / APX_NO_BANNER=1 (see branding.js).
2658
2671
  const SELF_BRANDED = new Set([
2659
- "status", "setup", "install", "daemon", "update", "upgrade", "help",
2672
+ "status", "setup", "install", "daemon", "update", "upgrade", "help", "restart",
2660
2673
  ]);
2661
2674
  const BANNERED = new Set(["init"]);
2662
2675
 
@@ -561,10 +561,16 @@ function connectDaemon() {
561
561
  return;
562
562
  }
563
563
 
564
- const token = readToken();
565
564
  const url = `ws://${DAEMON_HOST}:${DAEMON_PORT}/desktop/ws`;
566
565
 
567
566
  function connect() {
567
+ // Re-read the token on EVERY attempt — the daemon regenerates
568
+ // ~/.apx/daemon.token on each restart, so a token captured once at
569
+ // startup goes stale the moment the daemon is restarted (e.g. after a
570
+ // pull / `apx daemon restart`) and every reconnect 401s forever. Reading
571
+ // it fresh here lets the desktop self-heal: the next retry picks up the
572
+ // new token and reconnects on its own.
573
+ const token = readToken();
568
574
  try {
569
575
  wsConn = new WS(url, {
570
576
  headers: token ? { Authorization: `Bearer ${token}` } : {},