@phnx-labs/agents-cli 1.22.83 → 1.22.85

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 (291) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +80 -53
  3. package/dist/bootstrap.d.ts +7 -6
  4. package/dist/bootstrap.js +20 -27
  5. package/dist/cli/command-registry.js +5 -1
  6. package/dist/commands/accounts.d.ts +6 -13
  7. package/dist/commands/accounts.js +330 -143
  8. package/dist/commands/apply.js +1 -1
  9. package/dist/commands/auth-mint.d.ts +7 -3
  10. package/dist/commands/auth-mint.js +21 -9
  11. package/dist/commands/auth.js +2 -3
  12. package/dist/commands/browser.js +10 -10
  13. package/dist/commands/daemon.d.ts +7 -3
  14. package/dist/commands/daemon.js +40 -25
  15. package/dist/commands/doctor.js +28 -2
  16. package/dist/commands/exec.js +87 -39
  17. package/dist/commands/fleet-capture.js +21 -2
  18. package/dist/commands/harness-wizard.js +2 -2
  19. package/dist/commands/lease.js +7 -12
  20. package/dist/commands/profiles.d.ts +2 -2
  21. package/dist/commands/profiles.js +20 -13
  22. package/dist/commands/repo.js +3 -3
  23. package/dist/commands/run-account-picker.d.ts +3 -3
  24. package/dist/commands/run-account-picker.js +29 -30
  25. package/dist/commands/secrets-passthrough.d.ts +20 -0
  26. package/dist/commands/secrets-passthrough.js +43 -0
  27. package/dist/commands/sessions-trace.d.ts +11 -0
  28. package/dist/commands/sessions-trace.js +71 -0
  29. package/dist/commands/setup-accounts.d.ts +3 -2
  30. package/dist/commands/setup-accounts.js +4 -4
  31. package/dist/commands/setup-secrets.d.ts +16 -21
  32. package/dist/commands/setup-secrets.js +43 -224
  33. package/dist/commands/ssh.js +2 -2
  34. package/dist/commands/sync.js +1 -1
  35. package/dist/commands/update.d.ts +10 -0
  36. package/dist/commands/update.js +43 -40
  37. package/dist/commands/versions.d.ts +11 -11
  38. package/dist/commands/versions.js +131 -83
  39. package/dist/commands/view.d.ts +1 -9
  40. package/dist/commands/view.js +41 -55
  41. package/dist/commands/webhook.js +5 -5
  42. package/dist/commands/workflows.js +2 -1
  43. package/dist/index.d.ts +12 -12
  44. package/dist/index.js +13 -42
  45. package/dist/lib/account-capabilities.d.ts +1 -1
  46. package/dist/lib/account-capabilities.js +1 -1
  47. package/dist/lib/account-catalog.d.ts +121 -6
  48. package/dist/lib/account-catalog.js +421 -7
  49. package/dist/lib/account-registry.d.ts +8 -30
  50. package/dist/lib/account-registry.js +36 -79
  51. package/dist/lib/account-schema.d.ts +1 -1
  52. package/dist/lib/account-schema.js +1 -1
  53. package/dist/lib/accounting/account-pool-collect.js +16 -4
  54. package/dist/lib/accounting/rotate.d.ts +20 -7
  55. package/dist/lib/accounting/rotate.js +89 -13
  56. package/dist/lib/accounting/usage.js +11 -16
  57. package/dist/lib/accounts/add.d.ts +138 -0
  58. package/dist/lib/accounts/add.js +651 -0
  59. package/dist/lib/accounts/migrate.d.ts +117 -0
  60. package/dist/lib/accounts/migrate.js +536 -0
  61. package/dist/lib/accounts/slots.d.ts +13 -0
  62. package/dist/lib/accounts/slots.js +100 -0
  63. package/dist/lib/agent-spec/agents.d.ts +16 -0
  64. package/dist/lib/agent-spec/agents.js +32 -4
  65. package/dist/lib/app-bundle-install.js +5 -4
  66. package/dist/lib/auth-health.d.ts +3 -0
  67. package/dist/lib/auth-health.js +11 -0
  68. package/dist/lib/auth-mint.d.ts +52 -16
  69. package/dist/lib/auth-mint.js +117 -39
  70. package/dist/lib/browser/chrome.d.ts +1 -1
  71. package/dist/lib/browser/chrome.js +5 -5
  72. package/dist/lib/byok-usage.js +3 -3
  73. package/dist/lib/claude-account-token.d.ts +47 -3
  74. package/dist/lib/claude-account-token.js +172 -50
  75. package/dist/lib/cloud/antigravity.js +4 -4
  76. package/dist/lib/cloud/cursor.js +4 -4
  77. package/dist/lib/crabbox/cli.d.ts +1 -1
  78. package/dist/lib/crabbox/cli.js +6 -6
  79. package/dist/lib/crabbox/runtimes.d.ts +3 -3
  80. package/dist/lib/crabbox/runtimes.js +5 -5
  81. package/dist/lib/daemon/account-state-daemon-service.d.ts +37 -1
  82. package/dist/lib/daemon/account-state-daemon-service.js +199 -3
  83. package/dist/lib/daemon/auth-sync-service.js +28 -1
  84. package/dist/lib/daemon/daemon.d.ts +6 -16
  85. package/dist/lib/daemon/daemon.js +52 -97
  86. package/dist/lib/daemon/daemon.test-fixture.d.ts +7 -9
  87. package/dist/lib/daemon/daemon.test-fixture.js +22 -40
  88. package/dist/lib/daemon/harness-update-service.d.ts +1 -1
  89. package/dist/lib/daemon/harness-update-service.js +1 -1
  90. package/dist/lib/daemon/runner.d.ts +15 -3
  91. package/dist/lib/daemon/runner.js +49 -24
  92. package/dist/lib/daemon-health.d.ts +1 -2
  93. package/dist/lib/daemon-health.js +7 -6
  94. package/dist/lib/daemon-services.d.ts +1 -1
  95. package/dist/lib/daemon-services.js +1 -11
  96. package/dist/lib/daemon-ticks.js +23 -2
  97. package/dist/lib/daemon-webhooks.d.ts +8 -7
  98. package/dist/lib/daemon-webhooks.js +10 -9
  99. package/dist/lib/device-config.js +1 -1
  100. package/dist/lib/devices/doctor-findings.d.ts +1 -1
  101. package/dist/lib/devices/harness-inventory.d.ts +39 -0
  102. package/dist/lib/devices/harness-inventory.js +126 -4
  103. package/dist/lib/doctor-diff.js +2 -1
  104. package/dist/lib/exec-account-home.d.ts +38 -0
  105. package/dist/lib/exec-account-home.js +164 -0
  106. package/dist/lib/exec.d.ts +30 -0
  107. package/dist/lib/exec.js +101 -31
  108. package/dist/lib/feed/activity-stream.d.ts +60 -0
  109. package/dist/lib/feed/activity-stream.js +271 -0
  110. package/dist/lib/feed/activity.d.ts +7 -0
  111. package/dist/lib/feed/activity.js +10 -4
  112. package/dist/lib/feed/watch.d.ts +9 -3
  113. package/dist/lib/feed/watch.js +72 -36
  114. package/dist/lib/fleet/apply.d.ts +2 -2
  115. package/dist/lib/fleet/apply.js +4 -4
  116. package/dist/lib/fleet/auth-sync.js +4 -3
  117. package/dist/lib/fleet-shared-repo-sync.js +6 -9
  118. package/dist/lib/fleet-shared-state.d.ts +6 -0
  119. package/dist/lib/harness/adapter.d.ts +7 -7
  120. package/dist/lib/harness/adapter.js +10 -6
  121. package/dist/lib/harness/adapters/grok.js +8 -3
  122. package/dist/lib/harness/adapters/muse.js +1 -1
  123. package/dist/lib/harness/adapters/opencode.js +12 -3
  124. package/dist/lib/harness-auth-capabilities.d.ts +61 -0
  125. package/dist/lib/harness-auth-capabilities.js +52 -0
  126. package/dist/lib/helper-versions.d.ts +6 -4
  127. package/dist/lib/helper-versions.js +5 -4
  128. package/dist/lib/hosts/credential-transport.d.ts +10 -0
  129. package/dist/lib/hosts/credential-transport.js +37 -0
  130. package/dist/lib/hosts/dispatch.d.ts +7 -0
  131. package/dist/lib/hosts/dispatch.js +13 -6
  132. package/dist/lib/identity/client.d.ts +3 -3
  133. package/dist/lib/identity/client.js +3 -3
  134. package/dist/lib/installations/index.d.ts +1 -1
  135. package/dist/lib/installations/index.js +1 -1
  136. package/dist/lib/installations/migrate.js +9 -0
  137. package/dist/lib/installations/resolve.js +1 -1
  138. package/dist/lib/installations/shims.d.ts +15 -0
  139. package/dist/lib/installations/shims.js +69 -0
  140. package/dist/lib/installations/store.d.ts +43 -0
  141. package/dist/lib/installations/store.js +75 -7
  142. package/dist/lib/installations/versions.js +1 -1
  143. package/dist/lib/menubar/install-menubar.d.ts +3 -3
  144. package/dist/lib/menubar/install-menubar.js +3 -3
  145. package/dist/lib/native-accounts.d.ts +35 -0
  146. package/dist/lib/native-accounts.js +41 -0
  147. package/dist/lib/net-close.d.ts +11 -0
  148. package/dist/lib/net-close.js +25 -0
  149. package/dist/lib/openclaw-keychain.js +27 -1
  150. package/dist/lib/profiles.js +22 -9
  151. package/dist/lib/project-resources.js +1 -1
  152. package/dist/lib/reserved-stores.d.ts +53 -0
  153. package/dist/lib/reserved-stores.js +120 -0
  154. package/dist/lib/secrets-client.d.ts +191 -0
  155. package/dist/lib/secrets-client.js +710 -0
  156. package/dist/lib/secrets-policy.d.ts +222 -0
  157. package/dist/lib/secrets-policy.js +483 -0
  158. package/dist/lib/secrets-types.d.ts +188 -0
  159. package/dist/lib/secrets-types.js +24 -0
  160. package/dist/lib/service-manifest.js +3 -4
  161. package/dist/lib/session/active.d.ts +38 -4
  162. package/dist/lib/session/active.js +36 -4
  163. package/dist/lib/session/bash-command.js +10 -0
  164. package/dist/lib/session/db.d.ts +65 -2
  165. package/dist/lib/session/db.js +178 -18
  166. package/dist/lib/session/mirror.d.ts +5 -0
  167. package/dist/lib/session/mirror.js +166 -0
  168. package/dist/lib/session/parse.d.ts +49 -0
  169. package/dist/lib/session/parse.js +324 -30
  170. package/dist/lib/session/prompt.d.ts +93 -2
  171. package/dist/lib/session/prompt.js +266 -15
  172. package/dist/lib/session/remote/peer-stream.d.ts +47 -0
  173. package/dist/lib/session/remote/peer-stream.js +142 -0
  174. package/dist/lib/session/remote/watch.d.ts +1 -1
  175. package/dist/lib/session/remote/watch.js +41 -42
  176. package/dist/lib/session/session-cache.d.ts +9 -0
  177. package/dist/lib/session/session-cache.js +40 -2
  178. package/dist/lib/session/sync/config.js +2 -2
  179. package/dist/lib/session/timeline-pass.d.ts +129 -0
  180. package/dist/lib/session/timeline-pass.js +323 -0
  181. package/dist/lib/session/timeline.d.ts +182 -0
  182. package/dist/lib/session/timeline.js +636 -0
  183. package/dist/lib/session/types.d.ts +155 -1
  184. package/dist/lib/sha256-asset.d.ts +11 -16
  185. package/dist/lib/sha256-asset.js +11 -16
  186. package/dist/lib/share/config.js +14 -5
  187. package/dist/lib/signin-badge.d.ts +26 -0
  188. package/dist/lib/signin-badge.js +42 -0
  189. package/dist/lib/staleness/detectors/workflows.js +3 -105
  190. package/dist/lib/staleness/writers/workflows.js +2 -1
  191. package/dist/lib/state.d.ts +7 -4
  192. package/dist/lib/state.js +18 -7
  193. package/dist/lib/summarizer/pass.d.ts +2 -0
  194. package/dist/lib/summarizer/pass.js +14 -1
  195. package/dist/lib/summarizer/summarize.d.ts +7 -0
  196. package/dist/lib/summarizer/summarize.js +3 -0
  197. package/dist/lib/sync-umbrella.js +2 -2
  198. package/dist/lib/teams/agents.js +1 -1
  199. package/dist/lib/types.d.ts +74 -33
  200. package/dist/lib/view-types.d.ts +6 -3
  201. package/dist/lib/workflows-registry.d.ts +71 -0
  202. package/dist/lib/workflows-registry.js +280 -0
  203. package/dist/lib/workflows.d.ts +21 -21
  204. package/dist/lib/workflows.js +12 -327
  205. package/package.json +2 -3
  206. package/scripts/postinstall.js +4 -29
  207. package/dist/commands/secrets-import.d.ts +0 -18
  208. package/dist/commands/secrets-import.js +0 -74
  209. package/dist/commands/secrets-migrate.d.ts +0 -25
  210. package/dist/commands/secrets-migrate.js +0 -334
  211. package/dist/commands/secrets-rotate-passphrase.d.ts +0 -17
  212. package/dist/commands/secrets-rotate-passphrase.js +0 -96
  213. package/dist/commands/secrets-sync.d.ts +0 -11
  214. package/dist/commands/secrets-sync.js +0 -153
  215. package/dist/commands/secrets-vault.d.ts +0 -10
  216. package/dist/commands/secrets-vault.js +0 -130
  217. package/dist/commands/secrets.d.ts +0 -212
  218. package/dist/commands/secrets.js +0 -3030
  219. package/dist/lib/accounts/connect.d.ts +0 -170
  220. package/dist/lib/accounts/connect.js +0 -383
  221. package/dist/lib/daemon/keychain-reap-service.d.ts +0 -17
  222. package/dist/lib/daemon/keychain-reap-service.js +0 -32
  223. package/dist/lib/daemon/secrets-broker-service.d.ts +0 -21
  224. package/dist/lib/daemon/secrets-broker-service.js +0 -51
  225. package/dist/lib/secrets/agent.d.ts +0 -358
  226. package/dist/lib/secrets/agent.js +0 -1291
  227. package/dist/lib/secrets/audit.d.ts +0 -46
  228. package/dist/lib/secrets/audit.js +0 -101
  229. package/dist/lib/secrets/bundles.d.ts +0 -290
  230. package/dist/lib/secrets/bundles.js +0 -1547
  231. package/dist/lib/secrets/download-keychain.d.ts +0 -47
  232. package/dist/lib/secrets/download-keychain.js +0 -70
  233. package/dist/lib/secrets/drivers/rush.d.ts +0 -14
  234. package/dist/lib/secrets/drivers/rush.js +0 -90
  235. package/dist/lib/secrets/fallback.d.ts +0 -48
  236. package/dist/lib/secrets/fallback.js +0 -48
  237. package/dist/lib/secrets/filestore.d.ts +0 -222
  238. package/dist/lib/secrets/filestore.js +0 -1099
  239. package/dist/lib/secrets/headless.d.ts +0 -43
  240. package/dist/lib/secrets/headless.js +0 -56
  241. package/dist/lib/secrets/icloud-import.d.ts +0 -79
  242. package/dist/lib/secrets/icloud-import.js +0 -206
  243. package/dist/lib/secrets/index.d.ts +0 -451
  244. package/dist/lib/secrets/index.js +0 -1568
  245. package/dist/lib/secrets/install-helper.d.ts +0 -72
  246. package/dist/lib/secrets/install-helper.js +0 -245
  247. package/dist/lib/secrets/lease.d.ts +0 -25
  248. package/dist/lib/secrets/lease.js +0 -44
  249. package/dist/lib/secrets/linux.d.ts +0 -77
  250. package/dist/lib/secrets/linux.js +0 -393
  251. package/dist/lib/secrets/list-filter.d.ts +0 -109
  252. package/dist/lib/secrets/list-filter.js +0 -261
  253. package/dist/lib/secrets/mcp.d.ts +0 -93
  254. package/dist/lib/secrets/mcp.js +0 -211
  255. package/dist/lib/secrets/profiles.d.ts +0 -10
  256. package/dist/lib/secrets/profiles.js +0 -13
  257. package/dist/lib/secrets/push.d.ts +0 -133
  258. package/dist/lib/secrets/push.js +0 -273
  259. package/dist/lib/secrets/rc-hygiene.d.ts +0 -63
  260. package/dist/lib/secrets/rc-hygiene.js +0 -143
  261. package/dist/lib/secrets/read-backoff.d.ts +0 -27
  262. package/dist/lib/secrets/read-backoff.js +0 -64
  263. package/dist/lib/secrets/reaper.d.ts +0 -90
  264. package/dist/lib/secrets/reaper.js +0 -243
  265. package/dist/lib/secrets/remote.d.ts +0 -152
  266. package/dist/lib/secrets/remote.js +0 -344
  267. package/dist/lib/secrets/reserved-sync.d.ts +0 -66
  268. package/dist/lib/secrets/reserved-sync.js +0 -147
  269. package/dist/lib/secrets/scope.d.ts +0 -26
  270. package/dist/lib/secrets/scope.js +0 -29
  271. package/dist/lib/secrets/session-store.d.ts +0 -107
  272. package/dist/lib/secrets/session-store.js +0 -342
  273. package/dist/lib/secrets/sync-backend.d.ts +0 -48
  274. package/dist/lib/secrets/sync-backend.js +0 -13
  275. package/dist/lib/secrets/sync-commands.d.ts +0 -21
  276. package/dist/lib/secrets/sync-commands.js +0 -21
  277. package/dist/lib/secrets/sync.d.ts +0 -48
  278. package/dist/lib/secrets/sync.js +0 -237
  279. package/dist/lib/secrets/unlock-hints.d.ts +0 -27
  280. package/dist/lib/secrets/unlock-hints.js +0 -36
  281. package/dist/lib/secrets/usage-db.d.ts +0 -46
  282. package/dist/lib/secrets/usage-db.js +0 -96
  283. package/dist/lib/secrets/vault-age-helper.d.ts +0 -1
  284. package/dist/lib/secrets/vault-age-helper.js +0 -34
  285. package/dist/lib/secrets/vault.d.ts +0 -49
  286. package/dist/lib/secrets/vault.js +0 -399
  287. package/dist/lib/secrets/windows.d.ts +0 -81
  288. package/dist/lib/secrets/windows.js +0 -556
  289. package/scripts/install-helper.js +0 -97
  290. /package/dist/lib/{secrets/sync-passphrase.d.ts → sync-passphrase.d.ts} +0 -0
  291. /package/dist/lib/{secrets/sync-passphrase.js → sync-passphrase.js} +0 -0
@@ -1,170 +0,0 @@
1
- import type { AgentId, Meta } from '../types.js';
2
- import { type NativeAccount } from '../account-registry.js';
3
- /** Which harnesses `connect` supports, and how their native login is launched. */
4
- export interface LoginInvocation {
5
- /** argv passed to the installed binary to start the native login. */
6
- args: string[];
7
- /** Flag that pre-fills the login email (appended as `[emailFlag, email]`), when supported. */
8
- emailFlag?: string;
9
- /** One-line hint shown before the login flow takes over. */
10
- hint?: string;
11
- }
12
- /** Whether `connect` can drive this harness (isolation + a real native login). */
13
- export declare function connectSupported(agent: AgentId): boolean;
14
- /**
15
- * Named reason connect refuses a harness, or null when supported. Distinguishes
16
- * "cannot isolate/name this login" (capability) from "no native login command
17
- * wired yet" so the message is honest about which limit was hit.
18
- */
19
- export declare function connectRefusal(agent: AgentId): string | null;
20
- export declare function assertConnectSupported(agent: AgentId): void;
21
- export declare function loginInvocation(agent: AgentId): LoginInvocation;
22
- /**
23
- * Mint a fresh, opaque installation slot. `acct-<hex>` — alnum + hyphen only, so
24
- * it satisfies VERSION_RE, and is visibly an account slot, not a release. It is
25
- * RANDOM, never derived from the account name or identity: a name-derived slot
26
- * recomputes an already-occupied home after a rename and lets a new connect
27
- * overwrite another account's login (PHNX-3940 security fix). Retry-idempotency
28
- * is instead provided by the device-scoped pending-connect map, and collision
29
- * safety by allocating around occupied slots — see `allocateConnectSlot`.
30
- */
31
- export declare function mintConnectLabel(): string;
32
- export type ConnectMode = 'new' | 'reconnect';
33
- export interface ConnectPlan {
34
- mode: ConnectMode;
35
- agent: AgentId;
36
- /** The installation label whose isolated home hosts this account's login. */
37
- label: string;
38
- /** The human account name to register (new) or already registered (reconnect). */
39
- name?: string;
40
- /** For reconnect: the existing account being re-authenticated. */
41
- existing?: NativeAccount;
42
- /**
43
- * For reconnect: the label was ADOPTED (freshly minted) because the existing
44
- * account carried no recorded home and none was discoverable — so its identity
45
- * must still be re-verified against the login, but there is no prior home to
46
- * reuse. `false` when an existing home is being reused.
47
- */
48
- adoptedHome?: boolean;
49
- }
50
- /**
51
- * Resolve the home to reuse for reconnecting an existing account: THIS box's
52
- * recorded connect home first (device-scoped, survives an expired credential),
53
- * else a currently-signed-in local home carrying its identity. Returns null when
54
- * neither is known — a legacy account with no recorded or live home, for which
55
- * connect adopts a fresh home.
56
- */
57
- export declare function resolveExistingHomeLabel(existing: NativeAccount, deviceHome: string | null, signedInHomes: Array<{
58
- agent: AgentId;
59
- identityKey: string;
60
- label: string;
61
- }>): string | null;
62
- /**
63
- * Decide the release-independent connect plan (pure). `existing` is the native
64
- * account the name resolves to for THIS harness (or null for a new connect);
65
- * `existingHomeLabel` is its reusable home from {@link resolveExistingHomeLabel};
66
- * `freshSlot` is a safely-allocated opaque slot (see `allocateConnectSlot`) used
67
- * for a new connect or an adopted reconnect home — NEVER a name-derived label.
68
- */
69
- export declare function planConnect(input: {
70
- agent: AgentId;
71
- name?: string;
72
- existing: NativeAccount | null;
73
- existingHomeLabel: string | null;
74
- freshSlot: string;
75
- }): ConnectPlan;
76
- /**
77
- * Safely allocate the opaque slot for a NEW connect or an ADOPTED reconnect home
78
- * (PHNX-3940 security fix). Never reuses an identity-bearing slot:
79
- *
80
- * - `occupied` is every slot already owned by an account's home OR currently
81
- * signed in — an allocated slot is guaranteed disjoint from it, so a new
82
- * connect can never land on another account's home (the rename-collision flaw).
83
- * - A named connect first reuses its device-scoped PENDING slot (a prior
84
- * failed/cancelled attempt) IF that slot is not occupied, so a retry lands in
85
- * the same fresh home instead of orphaning a new one.
86
- * - Otherwise it mints random slots until one is neither occupied nor installed.
87
- */
88
- export declare function allocateConnectSlot(input: {
89
- agent: AgentId;
90
- name?: string;
91
- existing: NativeAccount | null;
92
- occupied: ReadonlySet<string>;
93
- installedLabels: ReadonlySet<string>;
94
- pending: string | null;
95
- mint?: () => string;
96
- }): string;
97
- /**
98
- * Fail-closed identity check after the login completes (pure).
99
- *
100
- * - Not signed in (no live credential) → the login did not complete; a metadata
101
- * identity key alone is NOT proof, so `signedIn` is required.
102
- * - Reconnect whose completed identity differs from the account's → REFUSE
103
- * registering the binding. The native credential in that home DID change (the
104
- * user signed in), so the message says the account BINDING is left unchanged —
105
- * it does not claim nothing happened.
106
- * A new connect accepts whatever identity signed in (that is the account being
107
- * created); the caller registers it.
108
- */
109
- export declare function verifyConnectedIdentity(plan: ConnectPlan, observed: Pick<ObservedIdentity, 'identityKey' | 'signedIn'>): void;
110
- /** Resolve the existing native account a connect `name` refers to for a harness. */
111
- export declare function findConnectAccount(agent: AgentId, name: string | undefined, meta: Pick<Meta, 'accounts' | 'deviceAccounts'>): NativeAccount | null;
112
- /** The observed identity of a completed login. */
113
- export interface ObservedIdentity {
114
- identityKey: string | null;
115
- email: string | null;
116
- releaseVersion: string | null;
117
- /** Live credential presence — proof of sign-in, not just a metadata identity claim. */
118
- signedIn: boolean;
119
- }
120
- /**
121
- * Side-effecting operations connect needs, injected so the planning/verification
122
- * path is unit-testable and the parent can own the real E2E login+verify.
123
- */
124
- export interface ConnectRunners {
125
- installedLabels(agent: AgentId): string[];
126
- /** Install the CURRENT release into `label`'s isolated home (opaque label). */
127
- install(agent: AgentId, label: string, onProgress?: (m: string) => void): Promise<{
128
- success: boolean;
129
- error?: string;
130
- }>;
131
- /** Launch the harness's native login under `label`'s home; resolves on exit. */
132
- launchLogin(agent: AgentId, label: string, invocation: LoginInvocation, email?: string, signal?: AbortSignal): Promise<{
133
- code: number | null;
134
- }>;
135
- observeIdentity(agent: AgentId, label: string): Promise<ObservedIdentity>;
136
- signedInHomes(): Promise<Array<{
137
- agent: AgentId;
138
- identityKey: string;
139
- label: string;
140
- }>>;
141
- }
142
- export interface ConnectResult {
143
- mode: ConnectMode;
144
- agent: AgentId;
145
- label: string;
146
- name?: string;
147
- identityKey: string;
148
- email: string | null;
149
- releaseVersion: string | null;
150
- /** True when this connect became the harness's default (only set if none was configured). */
151
- becameDefault: boolean;
152
- /** For an UNNAMED connect: the command to name the login (no name was forced). */
153
- nameHint?: string;
154
- }
155
- /**
156
- * Drive one `agents accounts connect`. Pure decisions (plan, fail-closed verify)
157
- * come from the helpers above; the install + login + identity read are injected.
158
- *
159
- * The default runners are loaded via dynamic import so this module — reachable
160
- * from `account-registry` consumers — never statically pulls in the install
161
- * engine or exec path and closes an import cycle (the intended pattern here).
162
- *
163
- * `stateDir` is only for tests: override the runtime-state dir used by the
164
- * per-harness auth-operation mutex (defaults to the real runtime state dir).
165
- */
166
- export declare function runConnect(agent: AgentId, name: string | undefined, opts: {
167
- meta: Pick<Meta, 'accounts' | 'deviceAccounts'>;
168
- onProgress?: (m: string) => void;
169
- stateDir?: string;
170
- }, runners?: ConnectRunners): Promise<ConnectResult>;
@@ -1,383 +0,0 @@
1
- import * as crypto from 'node:crypto';
2
- import { nativeAccountCapability, nativeAccountNamingRefusal } from '../account-capabilities.js';
3
- import { listNativeAccounts } from '../account-registry.js';
4
- import { readMeta } from '../state.js';
5
- import { acquireAuthOperationLock } from './auth-operation-lock.js';
6
- /**
7
- * `agents accounts connect <harness> [name]` — the stable-account front door
8
- * (PHNX-3940).
9
- *
10
- * An account is stable INDEPENDENT of releases: connecting a NEW account mints a
11
- * fresh opaque installation label, installs the current release into that
12
- * label's isolated home (even when the same release is already installed under
13
- * another label), and launches the harness's own native login there — no
14
- * credential is copied or refreshed, the OAuth login stays owned by the harness.
15
- * Reconnecting a NAMED EXISTING account reuses that account's existing home and
16
- * fails CLOSED if the completed login is a different identity, so a reconnect can
17
- * never overwrite some other account's home.
18
- *
19
- * This module owns the release-independent decision (plan), the opaque label,
20
- * and the fail-closed identity check. The actual install + login spawn is
21
- * injected (`ConnectRunners`) so the pure decision path is testable without a
22
- * network install or a TTY, and so the parent can own the real E2E login+verify.
23
- */
24
- /**
25
- * Ambient provider-credential env vars stripped before a native login spawn, so
26
- * the OAuth flow authenticates as the human's identity rather than being
27
- * short-circuited by an injected API key / setup-token that would impersonate a
28
- * different account into the fresh home.
29
- */
30
- const PROVIDER_AUTH_ENV_KEYS = [
31
- 'ANTHROPIC_API_KEY',
32
- 'CLAUDE_CODE_OAUTH_TOKEN',
33
- 'ANTHROPIC_AUTH_TOKEN',
34
- 'OPENAI_API_KEY',
35
- 'CODEX_API_KEY',
36
- ];
37
- /**
38
- * Native-login invocation per harness. Only harnesses with a REAL, finite login
39
- * COMMAND are listed — connect fails clearly for anything else rather than faking
40
- * a flow that never signs the user in. Verified against the installed CLIs
41
- * (PHNX-3940): `claude auth login --help` → "Sign in to your Anthropic account"
42
- * with `--email`; `codex login` drives the OAuth flow. A harness is added here
43
- * only once its isolated login command is verified.
44
- */
45
- const LOGIN_INVOCATIONS = {
46
- claude: { args: ['auth', 'login'], emailFlag: '--email', hint: 'Complete the Claude sign-in in your browser.' },
47
- codex: { args: ['login'] },
48
- };
49
- /** Whether `connect` can drive this harness (isolation + a real native login). */
50
- export function connectSupported(agent) {
51
- const cap = nativeAccountCapability(agent);
52
- return cap.scope === 'version' && !!LOGIN_INVOCATIONS[agent];
53
- }
54
- /**
55
- * Named reason connect refuses a harness, or null when supported. Distinguishes
56
- * "cannot isolate/name this login" (capability) from "no native login command
57
- * wired yet" so the message is honest about which limit was hit.
58
- */
59
- export function connectRefusal(agent) {
60
- const capabilityRefusal = nativeAccountNamingRefusal(agent);
61
- if (capabilityRefusal)
62
- return capabilityRefusal;
63
- const cap = nativeAccountCapability(agent);
64
- if (cap.scope !== 'version') {
65
- return `${agent} authentication is ${cap.scope}-scoped; connect creates per-account isolated homes, which needs a version-scoped login.`;
66
- }
67
- if (!LOGIN_INVOCATIONS[agent]) {
68
- const supported = Object.keys(LOGIN_INVOCATIONS).sort().join(', ');
69
- return `connect does not yet drive ${agent}'s native login. Supported: ${supported}.`;
70
- }
71
- return null;
72
- }
73
- export function assertConnectSupported(agent) {
74
- const reason = connectRefusal(agent);
75
- if (reason)
76
- throw new Error(reason);
77
- }
78
- export function loginInvocation(agent) {
79
- const invocation = LOGIN_INVOCATIONS[agent];
80
- if (!invocation)
81
- throw new Error(`No native login command is wired for ${agent}.`);
82
- return invocation;
83
- }
84
- /**
85
- * Mint a fresh, opaque installation slot. `acct-<hex>` — alnum + hyphen only, so
86
- * it satisfies VERSION_RE, and is visibly an account slot, not a release. It is
87
- * RANDOM, never derived from the account name or identity: a name-derived slot
88
- * recomputes an already-occupied home after a rename and lets a new connect
89
- * overwrite another account's login (PHNX-3940 security fix). Retry-idempotency
90
- * is instead provided by the device-scoped pending-connect map, and collision
91
- * safety by allocating around occupied slots — see `allocateConnectSlot`.
92
- */
93
- export function mintConnectLabel() {
94
- return `acct-${crypto.randomBytes(6).toString('hex')}`;
95
- }
96
- /**
97
- * Resolve the home to reuse for reconnecting an existing account: THIS box's
98
- * recorded connect home first (device-scoped, survives an expired credential),
99
- * else a currently-signed-in local home carrying its identity. Returns null when
100
- * neither is known — a legacy account with no recorded or live home, for which
101
- * connect adopts a fresh home.
102
- */
103
- export function resolveExistingHomeLabel(existing, deviceHome, signedInHomes) {
104
- if (deviceHome)
105
- return deviceHome;
106
- const match = signedInHomes.find(h => h.agent === existing.agent && h.identityKey === existing.identityKey);
107
- return match?.label ?? null;
108
- }
109
- /**
110
- * Decide the release-independent connect plan (pure). `existing` is the native
111
- * account the name resolves to for THIS harness (or null for a new connect);
112
- * `existingHomeLabel` is its reusable home from {@link resolveExistingHomeLabel};
113
- * `freshSlot` is a safely-allocated opaque slot (see `allocateConnectSlot`) used
114
- * for a new connect or an adopted reconnect home — NEVER a name-derived label.
115
- */
116
- export function planConnect(input) {
117
- if (input.existing) {
118
- const reuse = input.existingHomeLabel;
119
- return {
120
- mode: 'reconnect',
121
- agent: input.agent,
122
- label: reuse ?? input.freshSlot,
123
- name: input.existing.name,
124
- existing: input.existing,
125
- adoptedHome: !reuse,
126
- };
127
- }
128
- return { mode: 'new', agent: input.agent, label: input.freshSlot, name: input.name };
129
- }
130
- /**
131
- * Safely allocate the opaque slot for a NEW connect or an ADOPTED reconnect home
132
- * (PHNX-3940 security fix). Never reuses an identity-bearing slot:
133
- *
134
- * - `occupied` is every slot already owned by an account's home OR currently
135
- * signed in — an allocated slot is guaranteed disjoint from it, so a new
136
- * connect can never land on another account's home (the rename-collision flaw).
137
- * - A named connect first reuses its device-scoped PENDING slot (a prior
138
- * failed/cancelled attempt) IF that slot is not occupied, so a retry lands in
139
- * the same fresh home instead of orphaning a new one.
140
- * - Otherwise it mints random slots until one is neither occupied nor installed.
141
- */
142
- export function allocateConnectSlot(input) {
143
- const mint = input.mint ?? mintConnectLabel;
144
- // Retry reuse is only for a NEW named connect (pending is keyed by name); a
145
- // pending slot that has since become occupied is abandoned, never overwritten.
146
- if (!input.existing && input.name && input.pending && !input.occupied.has(input.pending)) {
147
- return input.pending;
148
- }
149
- let slot = mint();
150
- while (input.occupied.has(slot) || input.installedLabels.has(slot))
151
- slot = mint();
152
- return slot;
153
- }
154
- /**
155
- * Fail-closed identity check after the login completes (pure).
156
- *
157
- * - Not signed in (no live credential) → the login did not complete; a metadata
158
- * identity key alone is NOT proof, so `signedIn` is required.
159
- * - Reconnect whose completed identity differs from the account's → REFUSE
160
- * registering the binding. The native credential in that home DID change (the
161
- * user signed in), so the message says the account BINDING is left unchanged —
162
- * it does not claim nothing happened.
163
- * A new connect accepts whatever identity signed in (that is the account being
164
- * created); the caller registers it.
165
- */
166
- export function verifyConnectedIdentity(plan, observed) {
167
- if (!observed.signedIn || !observed.identityKey) {
168
- throw new Error(`No ${plan.agent} login completed in ${plan.agent}@${plan.label} (no live credential). Nothing was connected.`);
169
- }
170
- if (plan.mode === 'reconnect' && plan.existing && observed.identityKey !== plan.existing.identityKey) {
171
- throw new Error(`The ${plan.agent} login that just completed in ${plan.agent}@${plan.label} is a different identity `
172
- + `(${observed.identityKey}) than account '${plan.existing.name}' (${plan.existing.identityKey}). `
173
- + `Account '${plan.existing.name}' still points at its original identity (binding unchanged); `
174
- + `connect a NEW account for this login instead.`);
175
- }
176
- }
177
- /** Resolve the existing native account a connect `name` refers to for a harness. */
178
- export function findConnectAccount(agent, name, meta) {
179
- if (!name)
180
- return null;
181
- const needle = name.toLowerCase();
182
- return listNativeAccounts(meta).find(a => a.agent === agent && (a.id === name || a.name.toLowerCase() === needle || a.identityLabel?.toLowerCase() === needle)) ?? null;
183
- }
184
- /**
185
- * Drive one `agents accounts connect`. Pure decisions (plan, fail-closed verify)
186
- * come from the helpers above; the install + login + identity read are injected.
187
- *
188
- * The default runners are loaded via dynamic import so this module — reachable
189
- * from `account-registry` consumers — never statically pulls in the install
190
- * engine or exec path and closes an import cycle (the intended pattern here).
191
- *
192
- * `stateDir` is only for tests: override the runtime-state dir used by the
193
- * per-harness auth-operation mutex (defaults to the real runtime state dir).
194
- */
195
- export async function runConnect(agent, name, opts, runners) {
196
- assertConnectSupported(agent);
197
- // Acquire the per-harness auth-operation mutex BEFORE any meta read, slot
198
- // allocation, or name validation — two parallel connects for the same harness
199
- // would otherwise both see "name available" and "slot free", then race to
200
- // register the same slot or the same name, overwriting one account's home.
201
- const lock = acquireAuthOperationLock(agent, opts.stateDir);
202
- try {
203
- return await _runConnectLocked(agent, name, opts, lock, runners);
204
- }
205
- finally {
206
- lock.release();
207
- }
208
- }
209
- async function _runConnectLocked(agent, name, opts, lock, runners) {
210
- const run = runners ?? await defaultConnectRunners();
211
- const registry = await import('../account-registry.js');
212
- lock.assertHeld();
213
- // Re-read meta AFTER acquiring the lock so we see any writes a concurrent
214
- // connect committed before we were serialized past it. `opts.meta` was a
215
- // snapshot taken before the lock was available and must not be used here.
216
- const meta = readMeta();
217
- const existing = findConnectAccount(agent, name, meta);
218
- // Validate the requested NAME and its collisions BEFORE any install or login,
219
- // so a bad name never mints an orphan home and drives a login that can't be
220
- // recorded. Only a genuinely NEW named connect needs this (a reconnect names an
221
- // account that already passed the check).
222
- if (!existing && name)
223
- registry.assertNativeAccountNameAvailable(name, agent);
224
- const signedIn = await run.signedInHomes();
225
- lock.assertHeld();
226
- const existingHomeLabel = existing
227
- ? resolveExistingHomeLabel(existing, registry.nativeAccountHome(existing.id, meta), signedIn)
228
- : null;
229
- // SECURITY (PHNX-3940): allocate a fresh slot that is DISJOINT from every
230
- // occupied home — a slot already owned by an account OR currently signed in.
231
- // This is what stops a NEW connect (e.g. `connect work` after `work` was
232
- // renamed `personal`) from landing on and overwriting another account's login.
233
- const occupied = new Set([
234
- ...registry.ownedConnectHomeLabels(meta),
235
- ...signedIn.filter(h => h.agent === agent).map(h => h.label),
236
- ]);
237
- const freshSlot = allocateConnectSlot({
238
- agent,
239
- name,
240
- existing,
241
- occupied,
242
- installedLabels: new Set(run.installedLabels(agent)),
243
- pending: (!existing && name) ? registry.pendingConnectSlot(agent, name, meta) : null,
244
- });
245
- // Record the in-flight slot for a named connect so a failed retry reuses it.
246
- if (!existing && name && freshSlot !== registry.pendingConnectSlot(agent, name, meta)) {
247
- registry.setPendingConnectSlot(agent, name, freshSlot);
248
- }
249
- const plan = planConnect({ agent, name, existing, existingHomeLabel, freshSlot });
250
- // Pre-launch guard (both modes): if the chosen home is already installed AND
251
- // signed in, refuse to launch a login that would OVERWRITE its identity —
252
- // unless it is the SAME identity we are (re)connecting. For a NEW connect any
253
- // signed-in identity is a stranger; for a reconnect only a DIFFERENT one is.
254
- if (run.installedLabels(agent).includes(plan.label)) {
255
- const current = await run.observeIdentity(agent, plan.label);
256
- lock.assertHeld();
257
- const intended = existing?.identityKey;
258
- if (current.signedIn && current.identityKey && current.identityKey !== intended) {
259
- throw new Error(`${agent}@${plan.label} is currently signed in as ${current.identityKey}`
260
- + `${existing ? ` (not account '${existing.name}')` : ''}. `
261
- + `Refusing to launch a login that would overwrite it.`);
262
- }
263
- }
264
- // Install the current release into the account's home. A reconnect that reuses
265
- // an already-installed home skips the install (reuse, don't re-mint); a new
266
- // account (or an adopted reconnect home) installs the current release under its
267
- // opaque label even when the same release is already installed elsewhere. A
268
- // retried named connect reuses the same pending slot rather than orphaning.
269
- const alreadyInstalled = run.installedLabels(agent).includes(plan.label);
270
- lock.assertHeld();
271
- if (!(alreadyInstalled && (plan.mode === 'reconnect' || !!name))) {
272
- opts.onProgress?.(`Installing ${agent} into ${plan.label}…`);
273
- const install = await run.install(agent, plan.label, opts.onProgress);
274
- lock.assertHeld();
275
- if (!install.success)
276
- throw new Error(`Could not install ${agent} for the account home: ${install.error ?? 'unknown error'}`);
277
- }
278
- const invocation = loginInvocation(agent);
279
- if (invocation.hint)
280
- opts.onProgress?.(invocation.hint);
281
- const loginEmail = existing?.identityLabel;
282
- lock.assertHeld();
283
- const login = await run.launchLogin(agent, plan.label, invocation, loginEmail, lock.signal);
284
- lock.assertHeld();
285
- // A cancelled or failed login exits non-zero — do NOT read stale metadata and
286
- // report success. A NAMED connect can retry the same home (its slot is pending);
287
- // an UNNAMED connect has no pending slot, so a re-run allocates a new home.
288
- if (login.code !== 0) {
289
- const retry = name ? 're-run to retry the same home' : 're-run to try again (a new home is allocated)';
290
- throw new Error(`${agent} login did not complete (exit ${login.code ?? 'null'}) in ${agent}@${plan.label}. Nothing was recorded; ${retry}.`);
291
- }
292
- const observed = await run.observeIdentity(agent, plan.label);
293
- lock.assertHeld();
294
- verifyConnectedIdentity(plan, observed);
295
- const identityKey = observed.identityKey;
296
- // Persist THIS box's account⇄home binding (device-scoped) so a later reconnect
297
- // reuses this exact home even after the credential expires, and clear the
298
- // in-flight pending slot now that it is a real account home.
299
- let becameDefault = false;
300
- let nameHint;
301
- if (plan.mode === 'new') {
302
- if (name) {
303
- const account = registry.addNativeAccount(name, agent, identityKey, observed.email ?? undefined, 'version');
304
- registry.setNativeAccountHome(account.id, plan.label);
305
- registry.clearPendingConnectSlot(agent, name);
306
- // Select this account as the harness default ONLY if none is configured —
307
- // never override an existing choice, and never force a duplicate name.
308
- becameDefault = registry.setDefaultAccountIfAbsent(agent, account.name);
309
- }
310
- else {
311
- // No name was given: do not invent/force one. Point the user at the command
312
- // that names this login so it becomes selectable and default-eligible.
313
- nameHint = `agents accounts label ${agent} <name>`;
314
- }
315
- }
316
- else if (existing) {
317
- registry.setNativeAccountHome(existing.id, plan.label);
318
- }
319
- return {
320
- mode: plan.mode,
321
- agent,
322
- label: plan.label,
323
- name: plan.name ?? name,
324
- identityKey,
325
- email: observed.email,
326
- releaseVersion: observed.releaseVersion,
327
- becameDefault,
328
- nameHint,
329
- };
330
- }
331
- /** Real runners: the install engine, an inherited-stdio login spawn, and identity read. */
332
- async function defaultConnectRunners() {
333
- const [{ installVersion, getVersionHomePath, listInstalledVersions }, store, agentsMod, execMod] = await Promise.all([
334
- import('../installations/versions.js'),
335
- import('../installations/store.js'),
336
- import('../agents.js'),
337
- import('../exec.js'),
338
- ]);
339
- const { readInstallation } = store;
340
- const { getAccountInfo } = agentsMod;
341
- const { buildExecEnv } = execMod;
342
- const { nativeAccountCapability, nativeIdentityKey } = await import('../account-capabilities.js');
343
- const { runNativeAccountCommand } = await import('../installations/native-command.js');
344
- return {
345
- installedLabels: (agent) => listInstalledVersions(agent),
346
- install: async (agent, label, onProgress) => {
347
- const result = await installVersion(agent, 'latest', onProgress, { installationLabel: label });
348
- return { success: result.success, error: result.error };
349
- },
350
- launchLogin: async (agent, label, invocation, email, signal) => {
351
- // Pin the harness's own config-dir env (CLAUDE_CONFIG_DIR / CODEX_HOME) to
352
- // this label's isolated home so the native login lands there — no
353
- // credential is copied; the login writes into the home it authenticates.
354
- const env = buildExecEnv({ agent, version: label, configVersion: label, interactive: true, mode: 'auto', effort: 'auto', cwd: process.cwd() });
355
- // Strip any ambient provider API-key / setup-token env so the native login
356
- // authenticates as the human's OAuth identity, not an injected credential
357
- // that would impersonate a different account into this home.
358
- for (const key of PROVIDER_AUTH_ENV_KEYS)
359
- delete env[key];
360
- const args = email && invocation.emailFlag ? [...invocation.args, invocation.emailFlag, email] : invocation.args;
361
- return runNativeAccountCommand(agent, label, args, env, signal);
362
- },
363
- observeIdentity: async (agent, label) => {
364
- const home = getVersionHomePath(agent, label);
365
- const info = await getAccountInfo(agent, home);
366
- const { isLaunchableSignedIn } = await import('../account-catalog.js');
367
- return {
368
- identityKey: nativeIdentityKey(info, nativeAccountCapability(agent)),
369
- email: info.email,
370
- releaseVersion: readInstallation(agent, label)?.releaseVersion ?? null,
371
- // Strict: a live credential in THIS home, not a bare metadata identity.
372
- signedIn: isLaunchableSignedIn(agent, home, info),
373
- };
374
- },
375
- signedInHomes: async () => {
376
- const { collectNativeHomeRows } = await import('../account-catalog.js');
377
- const rows = await collectNativeHomeRows();
378
- return rows
379
- .filter(r => r.signedIn && (r.accountKey ?? r.email))
380
- .map(r => ({ agent: r.agent, identityKey: (r.accountKey ?? r.email.toLowerCase()), label: r.label }));
381
- },
382
- };
383
- }
@@ -1,17 +0,0 @@
1
- /**
2
- * Keychain orphan reaper tick as a `PeriodicService` (RUSH-3193 P3).
3
- *
4
- * Kills stuck keychain helper and `agents` processes whose keychain call
5
- * never returned (RUSH-2232). Single-executor: only the daemon runs this, so
6
- * no cross-device race.
7
- */
8
- import { BasePeriodicService, type DaemonContext } from './service.js';
9
- import type { DaemonServiceId } from '../daemon-services.js';
10
- export declare class KeychainReapService extends BasePeriodicService {
11
- readonly id: DaemonServiceId;
12
- readonly intervalMs: number;
13
- readonly deadlineMs = 30000;
14
- protected onStart(_ctx: DaemonContext): Promise<void>;
15
- protected onStop(): Promise<void>;
16
- protected onTick(ctx: DaemonContext): Promise<void>;
17
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Keychain orphan reaper tick as a `PeriodicService` (RUSH-3193 P3).
3
- *
4
- * Kills stuck keychain helper and `agents` processes whose keychain call
5
- * never returned (RUSH-2232). Single-executor: only the daemon runs this, so
6
- * no cross-device race.
7
- */
8
- import { BasePeriodicService } from './service.js';
9
- /** Matches the historical inline interval (daemon.ts KEYCHAIN_REAP_TICK_MS). */
10
- const KEYCHAIN_REAP_TICK_MS = 5 * 60_000;
11
- /** Hard cap per tick — a process-table scan + targeted kills, short enough a hang never freezes the service for long. */
12
- const KEYCHAIN_REAP_DEADLINE_MS = 30_000;
13
- export class KeychainReapService extends BasePeriodicService {
14
- id = 'keychain-reap';
15
- intervalMs = KEYCHAIN_REAP_TICK_MS;
16
- deadlineMs = KEYCHAIN_REAP_DEADLINE_MS;
17
- async onStart(_ctx) {
18
- // No connections/handles to open — each tick performs a fresh process scan.
19
- }
20
- async onStop() {
21
- // Nothing to release — the supervisor's timer teardown is the only cleanup needed.
22
- }
23
- async onTick(ctx) {
24
- const { reapOrphanedKeychainProcesses } = await import('../secrets/reaper.js');
25
- const result = await reapOrphanedKeychainProcesses();
26
- if (result.reaped > 0) {
27
- ctx.log('WARN', `Reaped ${result.reaped} keychain orphan/stuck process(es)`);
28
- for (const d of result.details)
29
- ctx.log('WARN', ` ${d}`);
30
- }
31
- }
32
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * Secrets-broker lifecycle as a `DaemonService` (RUSH-3193 P2).
3
- *
4
- * Wraps the daemon's secrets-broker hosting under the `ServiceSupervisor`
5
- * contract so the supervisor owns start/stop and reports health through the
6
- * uniform `daemon-health.ts` path. The broker self-heal (RUSH-1817) is
7
- * encapsulated here: a background interval re-probes the socket and takes
8
- * over hosting whenever the daemon is NOT already hosting AND no healthy
9
- * standalone broker answers a ping.
10
- */
11
- import { BasePeriodicService, type DaemonContext } from './service.js';
12
- import type { DaemonServiceId } from '../daemon-services.js';
13
- export declare class SecretsBrokerService extends BasePeriodicService {
14
- readonly id: DaemonServiceId;
15
- readonly intervalMs = 60000;
16
- readonly deadlineMs = 30000;
17
- private hostedBroker;
18
- protected onStart(ctx: DaemonContext): Promise<void>;
19
- protected onStop(): Promise<void>;
20
- protected onTick(ctx: DaemonContext): Promise<void>;
21
- }