@agfpd/iapeer 0.4.116 → 0.4.117

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": "@agfpd/iapeer",
3
- "version": "0.4.116",
3
+ "version": "0.4.117",
4
4
  "description": "Run Claude Code and Codex CLI as local background agents that message each other, wake on demand, and share one memory.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "agfpd",
package/src/cli/index.ts CHANGED
@@ -2735,7 +2735,10 @@ export async function runCli(argv: string[], env: NodeJS.ProcessEnv = process.en
2735
2735
  ` docs: ${docs.copied ? docs.dest : `skipped (${docs.reason})`}\n` +
2736
2736
  trayLine +
2737
2737
  ` daemon plist ${plistChanged ? 'written' : 'unchanged (byte-identical — no write, no BTM notification)'}: ${plist}\n` +
2738
- ` (NOT loaded — a live daemon migration is a separate step: launchctl bootstrap gui/$(id -u) ${plist})\n`,
2738
+ (plistChanged
2739
+ ? ` (install does not load it: \`iapeer update\` re-registers the daemon itself (bootout → bootstrap) right after this step; ` +
2740
+ `a standalone install loads it with \`iapeer onboard\` or launchctl bootstrap gui/$(id -u) ${plist})\n`
2741
+ : ''),
2739
2742
  )
2740
2743
  return 0
2741
2744
  }
@@ -605,6 +605,14 @@ export interface InstallDaemonPlistResult {
605
605
  * com.agfpd.* is the foundation-exclusive daemon namespace, so this never
606
606
  * touches the com.iapeer.* persistent-peer fleet.
607
607
  */
608
+ /** The EnvironmentVariables.PATH value of a rendered plist (XML-unescaped), or undefined. */
609
+ export function plistEnvPath(xml: string): string | undefined {
610
+ const m = /<key>EnvironmentVariables<\/key>\s*<dict>([\s\S]*?)<\/dict>/.exec(xml)
611
+ const v = m ? /<key>PATH<\/key>\s*<string>([\s\S]*?)<\/string>/.exec(m[1]!) : null
612
+ if (!v) return undefined
613
+ return v[1]!.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&')
614
+ }
615
+
608
616
  export function installDaemonPlist(opts: InstallDaemonPlistOptions = {}): InstallDaemonPlistResult {
609
617
  const env = opts.env ?? process.env
610
618
  const path = daemonPlistPath(env)
@@ -614,7 +622,19 @@ export function installDaemonPlist(opts: InstallDaemonPlistOptions = {}): Instal
614
622
  `(no ownership sentinel) — another manager owns it`,
615
623
  )
616
624
  }
617
- const rendered = renderLaunchdPlist(buildDaemonPlistSpec(opts))
625
+ // PATH STABILITY: buildDaemonPlistSpec defaults PATH to the INVOKING process's env.PATH, so every
626
+ // `iapeer update` run from a different shell rewrote the plist (a BTM notification each time, and a
627
+ // daemon PATH that silently followed whoever deployed last). The first install fixes the PATH; later
628
+ // installs keep the one on disk unless a caller passes `path` explicitly.
629
+ let keepPath: string | undefined
630
+ if (opts.path === undefined) {
631
+ try {
632
+ keepPath = plistEnvPath(readFileSync(path, 'utf8'))
633
+ } catch {
634
+ keepPath = undefined
635
+ }
636
+ }
637
+ const rendered = renderLaunchdPlist(buildDaemonPlistSpec(keepPath !== undefined ? { ...opts, path: keepPath } : opts))
618
638
  mkdirSync(launchAgentsDir(env), { recursive: true })
619
639
  mkdirSync(pluginLogsDir('iapeer', { env }), { recursive: true, mode: 0o700 })
620
640
  // Write-if-changed: a byte-identical rewrite is a pure no-op for the daemon but