@phnx-labs/agents-cli 1.20.23 → 1.20.24

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.
@@ -25,6 +25,17 @@
25
25
  import type { SecretsBundle } from './bundles.js';
26
26
  /** Default lifetime of an unlocked bundle when `--ttl` is not given. */
27
27
  export declare const DEFAULT_TTL_MS: number;
28
+ /**
29
+ * Decide whether a persistent broker should self-heal onto freshly-installed
30
+ * code (exit so launchd relaunches it). Only when the store is EMPTY: exiting
31
+ * with bundles still unlocked wipes them from memory, so the next reader falls
32
+ * back to a direct keychain read and re-prompts for Touch ID. Deferring the
33
+ * restart until the cache is idle (TTL-expired / screen-locked) means an
34
+ * in-place `npm i -g` never wipes a hot cache — the new code is adopted at the
35
+ * next quiet moment instead. See #435: rapid repeated upgrades wiped a hot
36
+ * cache on every bump and produced a recurring Touch ID storm.
37
+ */
38
+ export declare function shouldSelfHealForUpgrade(persistent: boolean, storeSize: number, runningVersion: string, onDiskVersion: string): boolean;
28
39
  export interface StoredBundle {
29
40
  bundle: SecretsBundle;
30
41
  env: Record<string, string>;
@@ -41,6 +41,25 @@ export const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000; // 24h
41
41
  const IDLE_EXIT_MS = 5 * 60 * 1000; // 5m
42
42
  /** How often the broker sweeps expired entries. */
43
43
  const SWEEP_INTERVAL_MS = 30 * 1000;
44
+ /**
45
+ * Decide whether a persistent broker should self-heal onto freshly-installed
46
+ * code (exit so launchd relaunches it). Only when the store is EMPTY: exiting
47
+ * with bundles still unlocked wipes them from memory, so the next reader falls
48
+ * back to a direct keychain read and re-prompts for Touch ID. Deferring the
49
+ * restart until the cache is idle (TTL-expired / screen-locked) means an
50
+ * in-place `npm i -g` never wipes a hot cache — the new code is adopted at the
51
+ * next quiet moment instead. See #435: rapid repeated upgrades wiped a hot
52
+ * cache on every bump and produced a recurring Touch ID storm.
53
+ */
54
+ export function shouldSelfHealForUpgrade(persistent, storeSize, runningVersion, onDiskVersion) {
55
+ if (!persistent)
56
+ return false;
57
+ if (storeSize > 0)
58
+ return false; // hot cache — defer rather than wipe unlocks
59
+ if (runningVersion === 'unknown' || onDiskVersion === 'unknown')
60
+ return false;
61
+ return onDiskVersion !== runningVersion;
62
+ }
44
63
  function onDarwin() {
45
64
  return process.platform === 'darwin';
46
65
  }
@@ -307,15 +326,15 @@ export async function runSecretsAgent(opts = {}) {
307
326
  for (const [name, e] of store)
308
327
  if (now >= e.expiresAt)
309
328
  store.delete(name);
310
- // Self-heal: a newer version was installed in place exit so launchd
311
- // relaunches us on the new code. Only meaningful when launchd will restart
312
- // us (persistent); a one-off broker just keeps serving until idle.
313
- if (persistent) {
314
- const onDisk = getCliVersionFresh();
315
- if (onDisk !== 'unknown' && runningVersion !== 'unknown' && onDisk !== runningVersion) {
316
- shutdown(0); // KeepAlive relaunches on the new code
317
- return;
318
- }
329
+ // Self-heal onto a newer in-place install but ONLY while the store is
330
+ // empty, so we never wipe live unlocks and force a re-prompt (#435). The
331
+ // `store.size === 0` short-circuit also keeps getCliVersionFresh (a disk
332
+ // read) off the hot path. A pending upgrade is adopted at the next idle
333
+ // sweep instead of immediately.
334
+ if (store.size === 0 &&
335
+ shouldSelfHealForUpgrade(persistent, store.size, runningVersion, getCliVersionFresh())) {
336
+ shutdown(0); // KeepAlive relaunches on the new code
337
+ return;
319
338
  }
320
339
  if (store.size === 0) {
321
340
  if (!persistent && now - emptySince >= IDLE_EXIT_MS)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.20.23",
3
+ "version": "1.20.24",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",