@phnx-labs/agents-cli 1.22.84 → 1.22.86

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 (259) 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/alias.js +7 -3
  9. package/dist/commands/apply.js +1 -1
  10. package/dist/commands/auth-mint.d.ts +7 -3
  11. package/dist/commands/auth-mint.js +21 -9
  12. package/dist/commands/auth.js +2 -3
  13. package/dist/commands/browser.js +10 -10
  14. package/dist/commands/daemon.d.ts +7 -3
  15. package/dist/commands/daemon.js +40 -25
  16. package/dist/commands/doctor.js +28 -2
  17. package/dist/commands/exec.js +87 -39
  18. package/dist/commands/fleet-capture.js +21 -2
  19. package/dist/commands/harness-wizard.js +2 -2
  20. package/dist/commands/lease.js +7 -12
  21. package/dist/commands/profiles.d.ts +2 -2
  22. package/dist/commands/profiles.js +20 -13
  23. package/dist/commands/repo.js +3 -3
  24. package/dist/commands/run-account-picker.d.ts +3 -3
  25. package/dist/commands/run-account-picker.js +29 -30
  26. package/dist/commands/secrets-passthrough.d.ts +20 -0
  27. package/dist/commands/secrets-passthrough.js +43 -0
  28. package/dist/commands/setup-accounts.d.ts +3 -2
  29. package/dist/commands/setup-accounts.js +4 -4
  30. package/dist/commands/setup-secrets.d.ts +16 -21
  31. package/dist/commands/setup-secrets.js +43 -224
  32. package/dist/commands/ssh.js +2 -2
  33. package/dist/commands/sync.js +1 -1
  34. package/dist/commands/update.d.ts +10 -0
  35. package/dist/commands/update.js +43 -40
  36. package/dist/commands/versions.d.ts +11 -11
  37. package/dist/commands/versions.js +131 -83
  38. package/dist/commands/view.d.ts +1 -9
  39. package/dist/commands/view.js +41 -55
  40. package/dist/commands/webhook.js +5 -5
  41. package/dist/commands/workflows.js +2 -1
  42. package/dist/index.d.ts +12 -12
  43. package/dist/index.js +13 -42
  44. package/dist/lib/account-capabilities.d.ts +1 -1
  45. package/dist/lib/account-capabilities.js +1 -1
  46. package/dist/lib/account-catalog.d.ts +121 -6
  47. package/dist/lib/account-catalog.js +421 -7
  48. package/dist/lib/account-registry.d.ts +8 -30
  49. package/dist/lib/account-registry.js +36 -79
  50. package/dist/lib/account-schema.d.ts +1 -1
  51. package/dist/lib/account-schema.js +1 -1
  52. package/dist/lib/accounting/account-pool-collect.js +16 -4
  53. package/dist/lib/accounting/rotate.d.ts +20 -7
  54. package/dist/lib/accounting/rotate.js +89 -13
  55. package/dist/lib/accounting/usage.js +11 -16
  56. package/dist/lib/accounts/add.d.ts +138 -0
  57. package/dist/lib/accounts/add.js +651 -0
  58. package/dist/lib/accounts/migrate.d.ts +117 -0
  59. package/dist/lib/accounts/migrate.js +536 -0
  60. package/dist/lib/accounts/slots.d.ts +13 -0
  61. package/dist/lib/accounts/slots.js +100 -0
  62. package/dist/lib/agent-spec/agents.d.ts +16 -0
  63. package/dist/lib/agent-spec/agents.js +38 -4
  64. package/dist/lib/app-bundle-install.js +5 -4
  65. package/dist/lib/auth-health.d.ts +3 -0
  66. package/dist/lib/auth-health.js +11 -0
  67. package/dist/lib/auth-mint.d.ts +52 -16
  68. package/dist/lib/auth-mint.js +117 -39
  69. package/dist/lib/browser/chrome.d.ts +1 -1
  70. package/dist/lib/browser/chrome.js +5 -5
  71. package/dist/lib/byok-usage.js +3 -3
  72. package/dist/lib/claude-account-token.d.ts +47 -3
  73. package/dist/lib/claude-account-token.js +172 -50
  74. package/dist/lib/cloud/antigravity.js +4 -4
  75. package/dist/lib/cloud/cursor.js +4 -4
  76. package/dist/lib/crabbox/cli.d.ts +1 -1
  77. package/dist/lib/crabbox/cli.js +6 -6
  78. package/dist/lib/crabbox/runtimes.d.ts +3 -3
  79. package/dist/lib/crabbox/runtimes.js +5 -5
  80. package/dist/lib/daemon/account-state-daemon-service.d.ts +37 -1
  81. package/dist/lib/daemon/account-state-daemon-service.js +199 -3
  82. package/dist/lib/daemon/auth-sync-service.js +32 -1
  83. package/dist/lib/daemon/daemon.d.ts +6 -16
  84. package/dist/lib/daemon/daemon.js +52 -97
  85. package/dist/lib/daemon/daemon.test-fixture.d.ts +7 -9
  86. package/dist/lib/daemon/daemon.test-fixture.js +22 -40
  87. package/dist/lib/daemon/harness-update-service.d.ts +1 -1
  88. package/dist/lib/daemon/harness-update-service.js +1 -1
  89. package/dist/lib/daemon/runner.d.ts +15 -3
  90. package/dist/lib/daemon/runner.js +49 -24
  91. package/dist/lib/daemon-health.d.ts +1 -2
  92. package/dist/lib/daemon-health.js +7 -6
  93. package/dist/lib/daemon-services.d.ts +1 -1
  94. package/dist/lib/daemon-services.js +1 -11
  95. package/dist/lib/daemon-webhooks.d.ts +8 -7
  96. package/dist/lib/daemon-webhooks.js +10 -9
  97. package/dist/lib/device-config.js +1 -1
  98. package/dist/lib/devices/doctor-findings.d.ts +1 -1
  99. package/dist/lib/devices/harness-inventory.d.ts +39 -0
  100. package/dist/lib/devices/harness-inventory.js +126 -4
  101. package/dist/lib/doctor-diff.js +2 -1
  102. package/dist/lib/exec-account-home.d.ts +38 -0
  103. package/dist/lib/exec-account-home.js +164 -0
  104. package/dist/lib/exec.d.ts +30 -0
  105. package/dist/lib/exec.js +101 -31
  106. package/dist/lib/fleet/apply.d.ts +2 -2
  107. package/dist/lib/fleet/apply.js +4 -4
  108. package/dist/lib/fleet/auth-sync.js +4 -3
  109. package/dist/lib/fleet-shared-repo-sync.js +6 -9
  110. package/dist/lib/harness/adapter.d.ts +7 -7
  111. package/dist/lib/harness/adapter.js +10 -6
  112. package/dist/lib/harness/adapters/grok.js +8 -3
  113. package/dist/lib/harness/adapters/muse.js +1 -1
  114. package/dist/lib/harness/adapters/opencode.js +12 -3
  115. package/dist/lib/harness-auth-capabilities.d.ts +61 -0
  116. package/dist/lib/harness-auth-capabilities.js +52 -0
  117. package/dist/lib/helper-versions.d.ts +6 -4
  118. package/dist/lib/helper-versions.js +5 -4
  119. package/dist/lib/hosts/credential-transport.d.ts +10 -0
  120. package/dist/lib/hosts/credential-transport.js +37 -0
  121. package/dist/lib/hosts/dispatch.d.ts +7 -0
  122. package/dist/lib/hosts/dispatch.js +13 -6
  123. package/dist/lib/identity/client.d.ts +3 -3
  124. package/dist/lib/identity/client.js +3 -3
  125. package/dist/lib/installations/index.d.ts +1 -1
  126. package/dist/lib/installations/index.js +1 -1
  127. package/dist/lib/installations/migrate.js +9 -0
  128. package/dist/lib/installations/resolve.js +1 -1
  129. package/dist/lib/installations/shims.d.ts +21 -4
  130. package/dist/lib/installations/shims.js +86 -5
  131. package/dist/lib/installations/store.d.ts +43 -0
  132. package/dist/lib/installations/store.js +75 -7
  133. package/dist/lib/installations/versions.js +1 -1
  134. package/dist/lib/menubar/install-menubar.d.ts +3 -3
  135. package/dist/lib/menubar/install-menubar.js +3 -3
  136. package/dist/lib/native-accounts.d.ts +35 -0
  137. package/dist/lib/native-accounts.js +41 -0
  138. package/dist/lib/net-close.d.ts +11 -0
  139. package/dist/lib/net-close.js +25 -0
  140. package/dist/lib/openclaw-keychain.js +27 -1
  141. package/dist/lib/profiles.js +22 -9
  142. package/dist/lib/project-resources.js +1 -1
  143. package/dist/lib/reserved-stores.d.ts +53 -0
  144. package/dist/lib/reserved-stores.js +120 -0
  145. package/dist/lib/secrets-client.d.ts +199 -0
  146. package/dist/lib/secrets-client.js +718 -0
  147. package/dist/lib/secrets-policy.d.ts +235 -0
  148. package/dist/lib/secrets-policy.js +505 -0
  149. package/dist/lib/secrets-types.d.ts +188 -0
  150. package/dist/lib/secrets-types.js +24 -0
  151. package/dist/lib/self-heal/checks/shims.js +5 -3
  152. package/dist/lib/service-manifest.js +3 -4
  153. package/dist/lib/session/db.d.ts +18 -0
  154. package/dist/lib/session/db.js +47 -14
  155. package/dist/lib/session/sync/config.js +2 -2
  156. package/dist/lib/sha256-asset.d.ts +11 -16
  157. package/dist/lib/sha256-asset.js +11 -16
  158. package/dist/lib/share/config.js +14 -5
  159. package/dist/lib/signin-badge.d.ts +26 -0
  160. package/dist/lib/signin-badge.js +42 -0
  161. package/dist/lib/staleness/detectors/workflows.js +3 -105
  162. package/dist/lib/staleness/writers/workflows.js +2 -1
  163. package/dist/lib/state.d.ts +7 -4
  164. package/dist/lib/state.js +18 -7
  165. package/dist/lib/sync-umbrella.js +2 -2
  166. package/dist/lib/teams/agents.js +1 -1
  167. package/dist/lib/types.d.ts +74 -33
  168. package/dist/lib/view-types.d.ts +6 -3
  169. package/dist/lib/workflows-registry.d.ts +71 -0
  170. package/dist/lib/workflows-registry.js +280 -0
  171. package/dist/lib/workflows.d.ts +21 -21
  172. package/dist/lib/workflows.js +12 -327
  173. package/package.json +2 -3
  174. package/scripts/postinstall.js +35 -30
  175. package/dist/commands/secrets-import.d.ts +0 -18
  176. package/dist/commands/secrets-import.js +0 -74
  177. package/dist/commands/secrets-migrate.d.ts +0 -25
  178. package/dist/commands/secrets-migrate.js +0 -334
  179. package/dist/commands/secrets-rotate-passphrase.d.ts +0 -17
  180. package/dist/commands/secrets-rotate-passphrase.js +0 -96
  181. package/dist/commands/secrets-sync.d.ts +0 -11
  182. package/dist/commands/secrets-sync.js +0 -153
  183. package/dist/commands/secrets-vault.d.ts +0 -10
  184. package/dist/commands/secrets-vault.js +0 -130
  185. package/dist/commands/secrets.d.ts +0 -212
  186. package/dist/commands/secrets.js +0 -3030
  187. package/dist/lib/accounts/connect.d.ts +0 -176
  188. package/dist/lib/accounts/connect.js +0 -453
  189. package/dist/lib/daemon/keychain-reap-service.d.ts +0 -17
  190. package/dist/lib/daemon/keychain-reap-service.js +0 -32
  191. package/dist/lib/daemon/secrets-broker-service.d.ts +0 -21
  192. package/dist/lib/daemon/secrets-broker-service.js +0 -51
  193. package/dist/lib/secrets/agent.d.ts +0 -358
  194. package/dist/lib/secrets/agent.js +0 -1291
  195. package/dist/lib/secrets/audit.d.ts +0 -46
  196. package/dist/lib/secrets/audit.js +0 -101
  197. package/dist/lib/secrets/bundles.d.ts +0 -290
  198. package/dist/lib/secrets/bundles.js +0 -1547
  199. package/dist/lib/secrets/download-keychain.d.ts +0 -47
  200. package/dist/lib/secrets/download-keychain.js +0 -70
  201. package/dist/lib/secrets/drivers/rush.d.ts +0 -14
  202. package/dist/lib/secrets/drivers/rush.js +0 -90
  203. package/dist/lib/secrets/fallback.d.ts +0 -48
  204. package/dist/lib/secrets/fallback.js +0 -48
  205. package/dist/lib/secrets/filestore.d.ts +0 -222
  206. package/dist/lib/secrets/filestore.js +0 -1099
  207. package/dist/lib/secrets/headless.d.ts +0 -43
  208. package/dist/lib/secrets/headless.js +0 -56
  209. package/dist/lib/secrets/icloud-import.d.ts +0 -79
  210. package/dist/lib/secrets/icloud-import.js +0 -206
  211. package/dist/lib/secrets/index.d.ts +0 -451
  212. package/dist/lib/secrets/index.js +0 -1568
  213. package/dist/lib/secrets/install-helper.d.ts +0 -72
  214. package/dist/lib/secrets/install-helper.js +0 -245
  215. package/dist/lib/secrets/lease.d.ts +0 -25
  216. package/dist/lib/secrets/lease.js +0 -44
  217. package/dist/lib/secrets/linux.d.ts +0 -77
  218. package/dist/lib/secrets/linux.js +0 -393
  219. package/dist/lib/secrets/list-filter.d.ts +0 -109
  220. package/dist/lib/secrets/list-filter.js +0 -261
  221. package/dist/lib/secrets/mcp.d.ts +0 -93
  222. package/dist/lib/secrets/mcp.js +0 -211
  223. package/dist/lib/secrets/profiles.d.ts +0 -10
  224. package/dist/lib/secrets/profiles.js +0 -13
  225. package/dist/lib/secrets/push.d.ts +0 -133
  226. package/dist/lib/secrets/push.js +0 -273
  227. package/dist/lib/secrets/rc-hygiene.d.ts +0 -63
  228. package/dist/lib/secrets/rc-hygiene.js +0 -143
  229. package/dist/lib/secrets/read-backoff.d.ts +0 -27
  230. package/dist/lib/secrets/read-backoff.js +0 -64
  231. package/dist/lib/secrets/reaper.d.ts +0 -90
  232. package/dist/lib/secrets/reaper.js +0 -243
  233. package/dist/lib/secrets/remote.d.ts +0 -152
  234. package/dist/lib/secrets/remote.js +0 -344
  235. package/dist/lib/secrets/reserved-sync.d.ts +0 -66
  236. package/dist/lib/secrets/reserved-sync.js +0 -147
  237. package/dist/lib/secrets/scope.d.ts +0 -26
  238. package/dist/lib/secrets/scope.js +0 -29
  239. package/dist/lib/secrets/session-store.d.ts +0 -107
  240. package/dist/lib/secrets/session-store.js +0 -342
  241. package/dist/lib/secrets/sync-backend.d.ts +0 -48
  242. package/dist/lib/secrets/sync-backend.js +0 -13
  243. package/dist/lib/secrets/sync-commands.d.ts +0 -21
  244. package/dist/lib/secrets/sync-commands.js +0 -21
  245. package/dist/lib/secrets/sync.d.ts +0 -48
  246. package/dist/lib/secrets/sync.js +0 -237
  247. package/dist/lib/secrets/unlock-hints.d.ts +0 -27
  248. package/dist/lib/secrets/unlock-hints.js +0 -36
  249. package/dist/lib/secrets/usage-db.d.ts +0 -46
  250. package/dist/lib/secrets/usage-db.js +0 -96
  251. package/dist/lib/secrets/vault-age-helper.d.ts +0 -1
  252. package/dist/lib/secrets/vault-age-helper.js +0 -34
  253. package/dist/lib/secrets/vault.d.ts +0 -49
  254. package/dist/lib/secrets/vault.js +0 -399
  255. package/dist/lib/secrets/windows.d.ts +0 -81
  256. package/dist/lib/secrets/windows.js +0 -556
  257. package/scripts/install-helper.js +0 -97
  258. /package/dist/lib/{secrets/sync-passphrase.d.ts → sync-passphrase.d.ts} +0 -0
  259. /package/dist/lib/{secrets/sync-passphrase.js → sync-passphrase.js} +0 -0
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Wire-shared types for the standalone `secrets` CLI (PHNX-3989).
3
+ *
4
+ * The standalone engine (`@phnx-labs/secrets-cli`, extracted from what used to
5
+ * be `cli/src/lib/secrets/**`) owns every one of these shapes; this module only
6
+ * re-declares them so `secrets-client.ts` and its consumers keep exact static
7
+ * types across the process-client boundary with no runtime dependency on the
8
+ * (now deleted) in-repo engine. Keep byte-for-byte in sync with
9
+ * `secrets-cli/src/schema.ts` / `src/core/bundles.ts` — this is the one seam
10
+ * both sides legitimately re-declare rather than share a package for (see the
11
+ * `secrets-client.ts` docblock).
12
+ */
13
+ /** Supported bundle storage backends. */
14
+ export type SecretsBackend = 'keychain' | 'file' | 'vault';
15
+ /** Supported secret resolution providers for a bundle var. */
16
+ export type SecretProvider = 'keychain' | 'env' | 'file' | 'exec';
17
+ /** A typed reference to a secret: a provider plus a provider-specific value. */
18
+ export interface SecretRef {
19
+ provider: SecretProvider;
20
+ value: string;
21
+ }
22
+ /**
23
+ * A bundle value: either a string (literal or provider-prefixed ref) or an
24
+ * object `{value: string}` used to escape a literal that would otherwise be
25
+ * parsed as a ref (e.g. a URL that happens to start with `env:`).
26
+ */
27
+ export type BundleValue = string | {
28
+ value: string;
29
+ };
30
+ /** Remote (bundle@host) push destination backend. */
31
+ export type RemoteBackend = 'keychain' | 'file';
32
+ export declare const SECRET_TYPES: readonly ["api-key", "token", "password", "url", "database-url", "ssh-key", "certificate", "webhook", "note"];
33
+ export type SecretType = (typeof SECRET_TYPES)[number];
34
+ /** Per-secret metadata; absent fields are omitted at write time. */
35
+ export interface VarMeta {
36
+ type?: SecretType;
37
+ /** Future-dated ISO date ('YYYY-MM-DD'). */
38
+ expires?: string;
39
+ note?: string;
40
+ }
41
+ /**
42
+ * Bundle prompt policy. `hold` (default): one Touch ID per hold window (~7d),
43
+ * then silent via the secrets-agent. `always`: prompt every read. `never`: no
44
+ * biometry ACL — least-safe, automation-only.
45
+ */
46
+ export type SecretsPolicy = 'always' | 'hold' | 'never';
47
+ /** A named set of environment variable definitions backed by secret stores. */
48
+ export interface SecretsBundle {
49
+ name: string;
50
+ description?: string;
51
+ allow_exec?: boolean;
52
+ /** Absent ⇒ `keychain`. */
53
+ backend?: SecretsBackend;
54
+ /** Absent ⇒ configured default (`hold`). */
55
+ policy?: SecretsPolicy;
56
+ created_at?: string;
57
+ updated_at?: string;
58
+ last_used?: string;
59
+ vars: Record<string, BundleValue>;
60
+ meta?: Record<string, VarMeta>;
61
+ }
62
+ /** Per-key kind breakdown of an already-resolved bundle. */
63
+ export interface BundleEntryInfo {
64
+ key: string;
65
+ kind: 'literal' | 'keychain' | 'env' | 'file' | 'exec';
66
+ /** Ref target, or empty for a literal. Never a resolved value. */
67
+ detail: string;
68
+ }
69
+ /** Options for `writeBundle` / `writeBundleWithItems`. */
70
+ export interface WriteBundleOptions {
71
+ /** Skip evicting the broker-held copy (no-op writers such as `stampLastUsed`). */
72
+ skipBrokerEviction?: boolean;
73
+ /** Allow writing a reserved `__<harness>__` store. */
74
+ allowReservedStore?: boolean;
75
+ }
76
+ /** Options for `readAndResolveBundleEnv`. */
77
+ export interface ResolveBundleOptions {
78
+ caller?: string;
79
+ /** Harness type whose unlock may be reused. */
80
+ agent?: string;
81
+ /** Duration shown in the Touch ID prompt. */
82
+ duration?: string;
83
+ /** Allow this call to raise an interactive biometric prompt. */
84
+ interactiveUnlock?: boolean;
85
+ /** Skip the broker fast-path and read from the keychain directly. */
86
+ noAgent?: boolean;
87
+ /** Resolve only from an already-unlocked broker snapshot; fail before prompting. */
88
+ agentOnly?: boolean;
89
+ /** Inject only these keys. Errors if any requested key is absent. */
90
+ keys?: string[];
91
+ /** Skip the per-key expiry gate. */
92
+ allowExpired?: boolean;
93
+ /** `process` projects dotted keys to shell-safe env names; `storage` preserves them. */
94
+ keyMode?: 'process' | 'storage';
95
+ /** Read a reserved `__<harness>__` store (system readers only). */
96
+ allowReservedStore?: boolean;
97
+ }
98
+ /** Options for `renameBundle`. */
99
+ export interface RenameOptions {
100
+ /** Overwrite an existing destination bundle. */
101
+ force?: boolean;
102
+ }
103
+ /** Options for `rotateBundleSecret`. */
104
+ export interface RotateOptions {
105
+ /** New plaintext value to write into the store (replaces the old one). */
106
+ newValue: string;
107
+ /** When true, drop existing meta for this key. Mutually exclusive with `meta`. */
108
+ clearMeta?: boolean;
109
+ /** Patch to merge into existing meta. Undefined fields preserve current values. */
110
+ meta?: Partial<VarMeta>;
111
+ }
112
+ /** Reading a keychain-backed item through the standalone. */
113
+ export interface KeychainReadContext {
114
+ agent?: string;
115
+ bundle?: string;
116
+ sessionId?: string;
117
+ reason?: string;
118
+ duration?: string;
119
+ defaultPolicy?: 'hold' | 'always' | 'never';
120
+ forceDuration?: boolean;
121
+ /**
122
+ * The caller attests the item(s) carry NO biometry ACL — so the read is
123
+ * silent even when no one is at the screen. Never pass this for an
124
+ * ACL-protected item.
125
+ */
126
+ silentNoAcl?: boolean;
127
+ }
128
+ /** One held bundle unlock reported by `agent.agentStatus`. */
129
+ export interface AgentStatusEntry {
130
+ name: string;
131
+ expiresAt: number;
132
+ keyCount: number;
133
+ harness: string;
134
+ leaseId?: string;
135
+ keys?: string[];
136
+ }
137
+ /** Options for `pushBundleToHost` / `pushBundleToHostAsync`. */
138
+ export interface PushBundleOptions {
139
+ remoteBackend: RemoteBackend;
140
+ /** Overwrite a key that already exists on the remote. */
141
+ force?: boolean;
142
+ /**
143
+ * Ignored. File-backend export never forwards the passphrase (PHNX-2371);
144
+ * the remote auto-provisions its own machine-local key. Kept on the options
145
+ * type so existing callers that passed one still compile.
146
+ */
147
+ passphrase?: string;
148
+ /** Label for the audit trail — `export --device` vs `fleet apply`. */
149
+ operation: string;
150
+ /** Preserve an automation account's permanent prompt-free policy remotely. */
151
+ policyNever?: boolean;
152
+ /** Permit a human-invoked push to read locally without requiring the agent broker. */
153
+ agentOnly?: boolean;
154
+ /** Non-secret literals whose bundle value kind must survive the dotenv transport. */
155
+ literalValues?: Record<string, string>;
156
+ /** Per-SSH-operation deadline. Async daemon callers must set this explicitly. */
157
+ timeoutMs?: number;
158
+ }
159
+ export interface PushBundleResult {
160
+ ok: boolean;
161
+ host: string;
162
+ bundle: string;
163
+ keyCount: number;
164
+ /** One line for the caller to render. Never contains a secret value. */
165
+ message: string;
166
+ }
167
+ /** Lightweight listing entry returned by the managed sync transport. */
168
+ export interface RemoteBundleSummary {
169
+ name: string;
170
+ updated_at: string;
171
+ }
172
+ /** Options for `pullBundle` (the `agents sync --secrets` umbrella stage). */
173
+ export interface PullOptions {
174
+ passphrase: string;
175
+ /** When true, overwrite an existing local bundle. */
176
+ force?: boolean;
177
+ }
178
+ /** One credential-shaped shell-rc export finding. */
179
+ export interface RcSecretFinding {
180
+ /** Basename of the rc file, e.g. `.zshenv`. */
181
+ file: string;
182
+ /** 1-based line number of the export. */
183
+ line: number;
184
+ /** The exported variable name. Never the value. */
185
+ name: string;
186
+ /** The file-store master passphrase gets called out separately. */
187
+ isMasterPassphrase: boolean;
188
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Wire-shared types for the standalone `secrets` CLI (PHNX-3989).
3
+ *
4
+ * The standalone engine (`@phnx-labs/secrets-cli`, extracted from what used to
5
+ * be `cli/src/lib/secrets/**`) owns every one of these shapes; this module only
6
+ * re-declares them so `secrets-client.ts` and its consumers keep exact static
7
+ * types across the process-client boundary with no runtime dependency on the
8
+ * (now deleted) in-repo engine. Keep byte-for-byte in sync with
9
+ * `secrets-cli/src/schema.ts` / `src/core/bundles.ts` — this is the one seam
10
+ * both sides legitimately re-declare rather than share a package for (see the
11
+ * `secrets-client.ts` docblock).
12
+ */
13
+ // --- bundle document ---------------------------------------------------
14
+ export const SECRET_TYPES = [
15
+ 'api-key',
16
+ 'token',
17
+ 'password',
18
+ 'url',
19
+ 'database-url',
20
+ 'ssh-key',
21
+ 'certificate',
22
+ 'webhook',
23
+ 'note',
24
+ ];
@@ -46,10 +46,12 @@ export const shimsCheck = {
46
46
  if (!ctx.dryRun && removeLegacyUserShim(agent))
47
47
  fixed.push(`removed legacy ${cmd} shim`);
48
48
  }
49
- // Prune orphaned legacy command shims (browser/secrets/sessions/… left by a
50
- // removed install) whose baked AGENTS_BIN is dead — the current source never
49
+ // Prune orphaned legacy command shims (browser/sessions/… left by a removed
50
+ // install) whose baked AGENTS_BIN is dead — the current source never
51
51
  // regenerates them, and they either die with exit 127 or shadow the real
52
- // package bin on PATH. Only removes shims whose target install is gone.
52
+ // package bin on PATH. Only removes shims whose target install is gone — plus
53
+ // the legacy `secrets` shim regardless: it re-enters `agents secrets`, which is
54
+ // a passthrough to the standalone binary since PHNX-3989, so it can only recurse.
53
55
  if (!ctx.dryRun) {
54
56
  for (const name of listShimFileNames()) {
55
57
  if (pruneOrphanedCommandShim(name))
@@ -27,10 +27,9 @@
27
27
  * module is the single place both rules live, so a new manifest has one obvious
28
28
  * thing to call.
29
29
  *
30
- * It is not yet the only consumer of the identifier rule: `lib/secrets/agent.ts`
31
- * keeps a literal `SERVICE_LABEL` for its `bootout`. That path is retire-only —
32
- * it detects and removes a plist an older version installed and generates none
33
- * of its own — so it has no HOME to bake and was deliberately left alone.
30
+ * A third consumer that used to keep its own literal `SERVICE_LABEL` for a
31
+ * retire-only `bootout` — the in-repo secrets broker's launchd cleanup — moved
32
+ * out of this repo entirely with the standalone `secrets` engine (PHNX-3989).
34
33
  */
35
34
  import * as crypto from 'crypto';
36
35
  import * as os from 'os';
@@ -817,6 +817,22 @@ export declare function buildFtsQuery(input: string): {
817
817
  * so "higher = better" is consistent across all tiers.
818
818
  */
819
819
  export declare function ftsSearch(input: string, limit?: number): FtsHit[];
820
+ /**
821
+ * Rewrite file_path for sessions whose path starts with any of `moves.from`,
822
+ * replacing that prefix with `moves.to` and clearing the matching scan_ledger
823
+ * entries so the next scan re-indexes from the new location.
824
+ *
825
+ * One transaction for the whole batch (PHNX-3940 T7): a migration that moves
826
+ * several homes into slots and several version dirs into trash must not leave
827
+ * the index half-rewritten. Longest `from` wins when prefixes nest, and each
828
+ * session row is rewritten at most once.
829
+ *
830
+ * Returns the number of session rows updated.
831
+ */
832
+ export declare function reindexMovedSessionPaths(moves: ReadonlyArray<{
833
+ from: string;
834
+ to: string;
835
+ }>): number;
820
836
  /**
821
837
  * Rewrite file_path for all sessions whose path starts with oldPrefix, replacing
822
838
  * it with newPrefix + the unchanged suffix. Also clears the matching scan_ledger
@@ -827,6 +843,8 @@ export declare function ftsSearch(input: string, limit?: number): FtsHit[];
827
843
  * Returns the number of session rows updated.
828
844
  */
829
845
  export declare function updateSessionFilePaths(oldPrefix: string, newPrefix: string): number;
846
+ /** Count indexed sessions whose transcript path sits under `prefix`. */
847
+ export declare function countSessionsWithFilePrefix(prefix: string): number;
830
848
  /** Per-kind capture tallies for a browser task. Counts only -- never the bytes. */
831
849
  interface BrowserCaptureCounts {
832
850
  screenshot: number;
@@ -4317,6 +4317,44 @@ export function ftsSearch(input, limit = 200) {
4317
4317
  hits.sort((a, b) => b.score - a.score);
4318
4318
  return hits.slice(0, limit);
4319
4319
  }
4320
+ /**
4321
+ * Rewrite file_path for sessions whose path starts with any of `moves.from`,
4322
+ * replacing that prefix with `moves.to` and clearing the matching scan_ledger
4323
+ * entries so the next scan re-indexes from the new location.
4324
+ *
4325
+ * One transaction for the whole batch (PHNX-3940 T7): a migration that moves
4326
+ * several homes into slots and several version dirs into trash must not leave
4327
+ * the index half-rewritten. Longest `from` wins when prefixes nest, and each
4328
+ * session row is rewritten at most once.
4329
+ *
4330
+ * Returns the number of session rows updated.
4331
+ */
4332
+ export function reindexMovedSessionPaths(moves) {
4333
+ const usable = moves.filter((m) => m.from && m.from !== m.to);
4334
+ if (usable.length === 0)
4335
+ return 0;
4336
+ const db = getDB();
4337
+ const sorted = [...usable].sort((a, b) => b.from.length - a.from.length);
4338
+ return db.transaction(() => {
4339
+ let n = 0;
4340
+ const seen = new Set();
4341
+ const update = db.prepare(`UPDATE sessions SET file_path = ? WHERE id = ?`);
4342
+ const dropLedger = db.prepare(`DELETE FROM scan_ledger WHERE file_path = ?`);
4343
+ const select = db.prepare(`SELECT id, file_path FROM sessions WHERE file_path LIKE ?`);
4344
+ for (const { from, to } of sorted) {
4345
+ const rows = select.all(from + '%');
4346
+ for (const { id, file_path } of rows) {
4347
+ if (seen.has(id))
4348
+ continue;
4349
+ seen.add(id);
4350
+ update.run(to + file_path.slice(from.length), id);
4351
+ dropLedger.run(canonicalLedgerKey(file_path));
4352
+ n++;
4353
+ }
4354
+ }
4355
+ return n;
4356
+ })();
4357
+ }
4320
4358
  /**
4321
4359
  * Rewrite file_path for all sessions whose path starts with oldPrefix, replacing
4322
4360
  * it with newPrefix + the unchanged suffix. Also clears the matching scan_ledger
@@ -4327,21 +4365,16 @@ export function ftsSearch(input, limit = 200) {
4327
4365
  * Returns the number of session rows updated.
4328
4366
  */
4329
4367
  export function updateSessionFilePaths(oldPrefix, newPrefix) {
4330
- const db = getDB();
4331
- const rows = db
4332
- .prepare(`SELECT id, file_path FROM sessions WHERE file_path LIKE ?`)
4333
- .all(oldPrefix + '%');
4334
- if (rows.length === 0)
4368
+ return reindexMovedSessionPaths([{ from: oldPrefix, to: newPrefix }]);
4369
+ }
4370
+ /** Count indexed sessions whose transcript path sits under `prefix`. */
4371
+ export function countSessionsWithFilePrefix(prefix) {
4372
+ if (!prefix)
4335
4373
  return 0;
4336
- const txn = db.transaction(() => {
4337
- for (const { id, file_path } of rows) {
4338
- const newPath = newPrefix + file_path.slice(oldPrefix.length);
4339
- db.prepare(`UPDATE sessions SET file_path = ? WHERE id = ?`).run(newPath, id);
4340
- db.prepare(`DELETE FROM scan_ledger WHERE file_path = ?`).run(canonicalLedgerKey(file_path));
4341
- }
4342
- });
4343
- txn();
4344
- return rows.length;
4374
+ const row = getDB()
4375
+ .prepare(`SELECT COUNT(*) AS c FROM sessions WHERE file_path LIKE ?`)
4376
+ .get(prefix + '%');
4377
+ return row.c;
4345
4378
  }
4346
4379
  /**
4347
4380
  * Upsert one browser task's durable metadata.
@@ -11,7 +11,7 @@
11
11
  * background sync. Credentials come from the `r2.backups` secrets bundle (OS
12
12
  * keychain on macOS, libsecret on Linux) — never from env or disk.
13
13
  */
14
- import { readAndResolveBundleEnv } from '../../secrets/bundles.js';
14
+ import { readAndResolveBundleEnvSync } from '../../secrets-client.js';
15
15
  /** Secrets bundle holding the R2 credentials. */
16
16
  export const SYNC_BUNDLE = 'r2.backups';
17
17
  /**
@@ -30,7 +30,7 @@ function resolveR2Config() {
30
30
  // and degrades to no-transport (sync disabled) with no prompt and no crash —
31
31
  // unlock once (`agents secrets unlock r2.backups`) or set it no-ACL
32
32
  // (`agents secrets policy r2.backups never`) for silent zero-friction sync.
33
- const { env } = readAndResolveBundleEnv(SYNC_BUNDLE, { caller: 'session-transport', agentOnly: true });
33
+ const { env } = readAndResolveBundleEnvSync(SYNC_BUNDLE, { caller: 'session-transport', agentOnly: true });
34
34
  const accountId = env.R2_ACCOUNT_ID?.trim();
35
35
  const bucket = env.R2_BUCKET_NAME?.trim();
36
36
  const accessKeyId = env.R2_ACCESS_KEY_ID?.trim();
@@ -3,22 +3,17 @@
3
3
  *
4
4
  * These live in their own LEAF module — importing only `node:crypto` and
5
5
  * `node:fs` — on purpose. They used to sit in `computer/ssh-tunnel.ts`, whose
6
- * own import graph reaches `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
- * `secrets/*`. `helper-download.ts` needs nothing from that graph but these two
8
- * pure functions, and importing them from there closed a module-initialization
9
- * cycle:
10
- *
11
- * helper-download.ts
12
- * -> computer/ssh-tunnel.ts (evaluated BEFORE `EXPECTED_TEAM_ID`)
13
- * -> browser/drivers/ssh.ts -> browser/chrome.ts
14
- * -> secrets/bundles.ts -> secrets/index.ts -> secrets/install-helper.ts
15
- * -> secrets/download-keychain.ts
16
- * -> helper-download.ts (still evaluating; const not yet bound)
17
- *
18
- * which threw `ReferenceError: Cannot access 'EXPECTED_TEAM_ID' before
19
- * initialization` at `secrets/download-keychain.ts:45` for any entry point that
20
- * reached `helper-download.ts` first (RUSH-3113). Keep this module a leaf: adding
21
- * a local import here can reintroduce that cycle.
6
+ * own import graph reached `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
+ * the in-repo secrets engine's own keychain-helper downloader, which imported
8
+ * back into `helper-download.ts` while it was still evaluating — before
9
+ * `EXPECTED_TEAM_ID` was bound, throwing `ReferenceError: Cannot access
10
+ * 'EXPECTED_TEAM_ID' before initialization` for any entry point that reached
11
+ * `helper-download.ts` first (RUSH-3113). The secrets engine that closed that
12
+ * cycle is gone now (PHNX-3989 — the standalone `secrets` CLI downloads its own
13
+ * helper), but the discipline that fixed it still holds: keep this module a
14
+ * leaf, since `helper-download.ts` needs nothing beyond these two pure
15
+ * functions and adding a local import here can reintroduce a cycle with
16
+ * whatever imports it next.
22
17
  */
23
18
  /** Pull the digest out of a `<sha256> <filename>` release asset. */
24
19
  export declare function parseSha256Asset(text: string): string;
@@ -3,22 +3,17 @@
3
3
  *
4
4
  * These live in their own LEAF module — importing only `node:crypto` and
5
5
  * `node:fs` — on purpose. They used to sit in `computer/ssh-tunnel.ts`, whose
6
- * own import graph reaches `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
- * `secrets/*`. `helper-download.ts` needs nothing from that graph but these two
8
- * pure functions, and importing them from there closed a module-initialization
9
- * cycle:
10
- *
11
- * helper-download.ts
12
- * -> computer/ssh-tunnel.ts (evaluated BEFORE `EXPECTED_TEAM_ID`)
13
- * -> browser/drivers/ssh.ts -> browser/chrome.ts
14
- * -> secrets/bundles.ts -> secrets/index.ts -> secrets/install-helper.ts
15
- * -> secrets/download-keychain.ts
16
- * -> helper-download.ts (still evaluating; const not yet bound)
17
- *
18
- * which threw `ReferenceError: Cannot access 'EXPECTED_TEAM_ID' before
19
- * initialization` at `secrets/download-keychain.ts:45` for any entry point that
20
- * reached `helper-download.ts` first (RUSH-3113). Keep this module a leaf: adding
21
- * a local import here can reintroduce that cycle.
6
+ * own import graph reached `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
+ * the in-repo secrets engine's own keychain-helper downloader, which imported
8
+ * back into `helper-download.ts` while it was still evaluating — before
9
+ * `EXPECTED_TEAM_ID` was bound, throwing `ReferenceError: Cannot access
10
+ * 'EXPECTED_TEAM_ID' before initialization` for any entry point that reached
11
+ * `helper-download.ts` first (RUSH-3113). The secrets engine that closed that
12
+ * cycle is gone now (PHNX-3989 — the standalone `secrets` CLI downloads its own
13
+ * helper), but the discipline that fixed it still holds: keep this module a
14
+ * leaf, since `helper-download.ts` needs nothing beyond these two pure
15
+ * functions and adding a local import here can reintroduce a cycle with
16
+ * whatever imports it next.
22
17
  */
23
18
  import { createHash } from 'node:crypto';
24
19
  import * as fs from 'node:fs';
@@ -9,8 +9,7 @@
9
9
  // `cloudflare` bundle.
10
10
  import { randomBytes } from 'node:crypto';
11
11
  import { readMeta, updateMeta } from '../state.js';
12
- import { bundleExists, bundleItemStore, bundlePolicy, keychainRef, readAndResolveBundleEnv, readBundle, writeBundle, } from '../secrets/bundles.js';
13
- import { secretsKeychainItem } from '../secrets/index.js';
12
+ import { bundleExistsSync as bundleExists, keychainRef, readAndResolveBundleEnvSync as readAndResolveBundleEnv, readBundleSync as readBundle, secretsKeychainItem, writeBundleWithItemsSync as writeBundleWithItems, } from '../secrets-client.js';
14
13
  export const SHARE_BUNDLE = 'share';
15
14
  export const SHARE_TOKEN_KEY = 'WRITE_TOKEN';
16
15
  export const SHARE_TOKEN_ENV_KEY = 'SHARE_WRITE_TOKEN';
@@ -113,10 +112,12 @@ export function storeWriteToken(token) {
113
112
  vars: {},
114
113
  };
115
114
  }
116
- const store = bundleItemStore(bundle.backend, { noAcl: bundlePolicy(bundle) === 'never' });
117
- store.set(secretsKeychainItem(bundle.name, SHARE_TOKEN_KEY), token);
115
+ // writeBundleWithItems decides the ACL (keychain backend) purely from
116
+ // `bundle.policy === 'never'` — the fresh-bundle case above sets it
117
+ // explicitly, and an existing bundle already carries its own explicit tier.
118
118
  bundle.vars[SHARE_TOKEN_KEY] = keychainRef(SHARE_TOKEN_KEY);
119
- writeBundle(bundle);
119
+ const item = secretsKeychainItem(bundle.name, SHARE_TOKEN_KEY);
120
+ writeBundleWithItems(bundle, new Map([[item, token]]));
120
121
  }
121
122
  /** Read the raw write token from the `share` secrets bundle. */
122
123
  export function readWriteTokenFromBundle() {
@@ -178,6 +179,14 @@ export function readCloudflareCreds(bundle = DEFAULT_CF_BUNDLE, override) {
178
179
  if (override?.apiToken) {
179
180
  return { apiToken: override.apiToken, accountId: override.accountId ?? '' };
180
181
  }
182
+ // Check existence first (same guard as readShareToken above): resolving a
183
+ // missing bundle through the process client surfaces an opaque transport code
184
+ // (e.g. LOCKED), never naming the bundle the user typed — so name it here.
185
+ if (!bundleExists(bundle)) {
186
+ throw new Error(`The '${bundle}' bundle does not exist. ` +
187
+ `Pass credentials directly with --token <t> [--account <id>], or create it: ` +
188
+ `agents secrets add ${bundle} CLOUDFLARE_API_TOKEN`);
189
+ }
181
190
  const { env } = readAndResolveBundleEnv(bundle, {
182
191
  caller: 'share',
183
192
  // Setup is still a read; only `agents secrets unlock` may authenticate.
@@ -1,5 +1,8 @@
1
1
  import type { AccountInfo } from './agents.js';
2
+ import type { AuthVerdict } from './auth-health.js';
2
3
  import type { AgentId } from './types.js';
4
+ export type AccountVerdict = Exclude<AuthVerdict, 'unconfigured' | 'error'> | 'missing' | 'per-device' | 'ready';
5
+ export type AccountProvisioning = 'portable' | 'per-device';
3
6
  /**
4
7
  * The exact command that logs a given agent in — for warn banners and nudges.
5
8
  * Driven off the registry `cliCommand` with the per-agent subcommand overrides
@@ -8,6 +11,29 @@ import type { AgentId } from './types.js';
8
11
  * remaining agents (kimi, gemini, …) start their device/oauth flow on launch.
9
12
  */
10
13
  export declare function loginHint(agentId: AgentId): string;
14
+ /**
15
+ * Exact action shown beside a non-live account. Every emitted command exists
16
+ * today — never a planned surface and never a hidden verb:
17
+ * - Per-device harnesses repair per box via `agents devices login`.
18
+ * - Named accounts re-auth through `agents accounts login <harness>#<name>`.
19
+ * A known account with no slot on a headed device is onboarded with
20
+ * `agents accounts add <harness> <name>`. A worker never runs an
21
+ * interactive login — the hint is `add.ts`'s worker refusal (add on the
22
+ * personal device; this box is provisioned from the durable credential).
23
+ * - Unnamed legacy homes use the same version-targeted command shape as
24
+ * doctor, so the hint never logs a different/default home in by accident.
25
+ * - `unverified` emits nothing: the probe could not confirm state (e.g. a
26
+ * worker whose token lacks the usage scope), so there is nothing to repair.
27
+ */
28
+ export declare function fixFor(input: {
29
+ agent: AgentId;
30
+ verdict: AccountVerdict;
31
+ name?: string | null;
32
+ version?: string | null;
33
+ provisioning?: AccountProvisioning;
34
+ /** When false, this named account has no slot on this device yet. */
35
+ hasSlot?: boolean;
36
+ }): string | null;
11
37
  /**
12
38
  * Whether `agents run` should probe login state before launching. True only for
13
39
  * a launch that actually opens the interactive TUI — where discovering a logged-out
@@ -9,7 +9,9 @@
9
9
  * on it (the run preflight) WARN and continue; they never block.
10
10
  */
11
11
  import chalk from 'chalk';
12
+ import { addWorkerRefusal } from './accounts/add.js';
12
13
  import { AGENTS } from './agents.js';
14
+ import { CONFIG_ENV_ISOLATED_AGENTS } from './installations/shims.js';
13
15
  /**
14
16
  * The exact command that logs a given agent in — for warn banners and nudges.
15
17
  * Driven off the registry `cliCommand` with the per-agent subcommand overrides
@@ -34,6 +36,46 @@ export function loginHint(agentId) {
34
36
  return cli;
35
37
  }
36
38
  }
39
+ /**
40
+ * Exact action shown beside a non-live account. Every emitted command exists
41
+ * today — never a planned surface and never a hidden verb:
42
+ * - Per-device harnesses repair per box via `agents devices login`.
43
+ * - Named accounts re-auth through `agents accounts login <harness>#<name>`.
44
+ * A known account with no slot on a headed device is onboarded with
45
+ * `agents accounts add <harness> <name>`. A worker never runs an
46
+ * interactive login — the hint is `add.ts`'s worker refusal (add on the
47
+ * personal device; this box is provisioned from the durable credential).
48
+ * - Unnamed legacy homes use the same version-targeted command shape as
49
+ * doctor, so the hint never logs a different/default home in by accident.
50
+ * - `unverified` emits nothing: the probe could not confirm state (e.g. a
51
+ * worker whose token lacks the usage scope), so there is nothing to repair.
52
+ */
53
+ export function fixFor(input) {
54
+ const { agent, verdict } = input;
55
+ if (verdict === 'live' || verdict === 'rate_limited' || verdict === 'unverified' || verdict === 'ready')
56
+ return null;
57
+ if (input.provisioning === 'per-device' || verdict === 'per-device') {
58
+ return `agents devices login --agents ${agent}`;
59
+ }
60
+ if (input.name) {
61
+ const worker = addWorkerRefusal(agent, input.name);
62
+ if (worker)
63
+ return worker;
64
+ if (input.hasSlot === false)
65
+ return `agents accounts add ${agent} ${input.name}`;
66
+ return `agents accounts login ${agent}#${input.name}`;
67
+ }
68
+ const version = input.version ?? null;
69
+ if (!version || !CONFIG_ENV_ISOLATED_AGENTS.includes(agent))
70
+ return loginHint(agent);
71
+ if (agent === 'claude')
72
+ return `agents run ${agent}@${version}, then /login`;
73
+ if (agent === 'codex' || agent === 'grok')
74
+ return `agents run ${agent}@${version} -- login`;
75
+ if (agent === 'opencode')
76
+ return `agents run ${agent}@${version} -- auth login`;
77
+ return `agents run ${agent}@${version}`;
78
+ }
37
79
  /**
38
80
  * Whether `agents run` should probe login state before launching. True only for
39
81
  * a launch that actually opens the interactive TUI — where discovering a logged-out