@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
@@ -1,43 +0,0 @@
1
- /**
2
- * The headless-context detector shared by every secrets read path that could
3
- * raise a macOS Touch ID sheet: bundle resolution (bundles.ts) and raw item
4
- * reads (index.ts). It lives in its own module so index.ts can use it without
5
- * importing bundles.ts (which already imports index.ts).
6
- */
7
- /**
8
- * True when the current process was structurally launched by an agent runtime
9
- * that must NEVER raise a Keychain biometry prompt on the user's screen:
10
- * - `AGENTS_RUNTIME` is `headless`, `teams`, or `terminal` — i.e. ANY agent
11
- * launch, interactive included, and inherited by everything spawned beneath
12
- * one (set on the child env by `agents run --headless`, scheduled routines,
13
- * teammates, and interactive runs — see exec.ts:430, runner.ts,
14
- * teams/agents.ts).
15
- *
16
- * Only **macOS keychain** reads pop an interactive Touch ID sheet — the secrets
17
- * broker itself is a no-op off darwin (see agent.ts), and libsecret (Linux) /
18
- * the Windows credential store resolve without any prompt. So off-darwin this
19
- * ALWAYS returns false: forcing broker-only there would break every headless
20
- * Linux/Windows read (CI, `agents run --headless`, routines, the Linux-driven
21
- * release flow) for no benefit — there is no prompt to suppress.
22
- *
23
- * A read in a macOS headless context resolves broker-only (agentOnly) and fails
24
- * fast with an actionable error instead of hijacking Touch ID. This generalizes
25
- * the per-caller broker-only pattern used across the headless secrets readers.
26
- */
27
- export declare function isHeadlessSecretsContext(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
28
- /**
29
- * True when this process runs structurally inside a coding-agent session: any
30
- * `agents run` launch (AGENTS_RUNTIME / AGENT_SESSION_ID ride the child env —
31
- * exec.ts buildExecEnv — and are inherited by every shell the agent spawns), or
32
- * a harness's own tool shell launched outside agents-cli (Claude Code stamps
33
- * CLAUDECODE on its Bash tool).
34
- *
35
- * Distinct from isHeadlessSecretsContext above on both axes: it is
36
- * platform-independent (no biometry involved), and a TTY does not clear it (an
37
- * agent inside tmux has one). Its job is the materialization boundary, not the
38
- * prompt boundary: a command that would PRINT a plaintext value refuses under
39
- * an agent, because anything printed lands in the agent's context and session
40
- * transcript. The agent path to secret values is injection — `secrets exec`,
41
- * `run --secrets` — which places them only in a child process env.
42
- */
43
- export declare function isAgentInvocationContext(env?: NodeJS.ProcessEnv): boolean;
@@ -1,56 +0,0 @@
1
- /**
2
- * The headless-context detector shared by every secrets read path that could
3
- * raise a macOS Touch ID sheet: bundle resolution (bundles.ts) and raw item
4
- * reads (index.ts). It lives in its own module so index.ts can use it without
5
- * importing bundles.ts (which already imports index.ts).
6
- */
7
- /**
8
- * True when the current process was structurally launched by an agent runtime
9
- * that must NEVER raise a Keychain biometry prompt on the user's screen:
10
- * - `AGENTS_RUNTIME` is `headless`, `teams`, or `terminal` — i.e. ANY agent
11
- * launch, interactive included, and inherited by everything spawned beneath
12
- * one (set on the child env by `agents run --headless`, scheduled routines,
13
- * teammates, and interactive runs — see exec.ts:430, runner.ts,
14
- * teams/agents.ts).
15
- *
16
- * Only **macOS keychain** reads pop an interactive Touch ID sheet — the secrets
17
- * broker itself is a no-op off darwin (see agent.ts), and libsecret (Linux) /
18
- * the Windows credential store resolve without any prompt. So off-darwin this
19
- * ALWAYS returns false: forcing broker-only there would break every headless
20
- * Linux/Windows read (CI, `agents run --headless`, routines, the Linux-driven
21
- * release flow) for no benefit — there is no prompt to suppress.
22
- *
23
- * A read in a macOS headless context resolves broker-only (agentOnly) and fails
24
- * fast with an actionable error instead of hijacking Touch ID. This generalizes
25
- * the per-caller broker-only pattern used across the headless secrets readers.
26
- */
27
- export function isHeadlessSecretsContext(env = process.env, platform = process.platform) {
28
- if (platform !== 'darwin')
29
- return false; // no biometry prompt to suppress off-darwin
30
- // Every agent-launch runtime resolves broker-only, interactive included.
31
- const runtime = env.AGENTS_RUNTIME;
32
- if (runtime === 'headless' || runtime === 'teams' || runtime === 'terminal')
33
- return true;
34
- return false;
35
- }
36
- /**
37
- * True when this process runs structurally inside a coding-agent session: any
38
- * `agents run` launch (AGENTS_RUNTIME / AGENT_SESSION_ID ride the child env —
39
- * exec.ts buildExecEnv — and are inherited by every shell the agent spawns), or
40
- * a harness's own tool shell launched outside agents-cli (Claude Code stamps
41
- * CLAUDECODE on its Bash tool).
42
- *
43
- * Distinct from isHeadlessSecretsContext above on both axes: it is
44
- * platform-independent (no biometry involved), and a TTY does not clear it (an
45
- * agent inside tmux has one). Its job is the materialization boundary, not the
46
- * prompt boundary: a command that would PRINT a plaintext value refuses under
47
- * an agent, because anything printed lands in the agent's context and session
48
- * transcript. The agent path to secret values is injection — `secrets exec`,
49
- * `run --secrets` — which places them only in a child process env.
50
- */
51
- export function isAgentInvocationContext(env = process.env) {
52
- return Boolean(env.AGENTS_RUNTIME ||
53
- env.AGENT_SESSION_ID ||
54
- env.AGENTS_SESSION_ID ||
55
- env.CLAUDECODE);
56
- }
@@ -1,79 +0,0 @@
1
- /**
2
- * Recovery for LEGACY SYNCHRONIZABLE (iCloud Keychain) bundles.
3
- *
4
- * The pre-biometry helper era defaulted bundles to iCloud Keychain sync. The
5
- * device-local cutover (biometry ACL + kSecAttrSynchronizable false on every
6
- * query) orphaned those items: they still sync back via iCloud Keychain, but
7
- * neither `secrets list` nor `migrate-acl` can see them. This module powers
8
- * `agents secrets import --from icloud` — discover the orphaned bundles,
9
- * re-import them as normal device-local bundles, and optionally purge the
10
- * iCloud copies.
11
- *
12
- * The item-name scheme is the same one the modern store uses (see bundles.ts):
13
- * metadata under `agents-cli.bundles.<name>`, one value per key under
14
- * `agents-cli.secrets.<bundle>.<key>`. Legacy env keys never contained a dot,
15
- * so metadata-less synced secret services split at their LAST dot to recover
16
- * the bundle/key boundary even for dotted bundle names like `hetzner.com`.
17
- */
18
- import { type SecretsBackend } from './bundles.js';
19
- /** One orphaned iCloud bundle, as discovered from the synced item names. */
20
- export interface SyncedBundleCandidate {
21
- /** Bundle name derived from the iCloud service names. */
22
- name: string;
23
- /** Env keys that have a per-key secret item in the iCloud keychain. */
24
- keys: string[];
25
- /** True when an `agents-cli.bundles.<name>` metadata item exists in iCloud. */
26
- hasMeta: boolean;
27
- /** Every iCloud service name belonging to this candidate (the purge set). */
28
- services: string[];
29
- }
30
- /**
31
- * Group raw synced service names into per-bundle candidates. Pure — separated
32
- * from discovery so the parsing rules are unit-testable without a keychain.
33
- *
34
- * A bundle can surface as metadata only (`agents-cli.bundles.<name>`), as
35
- * secret items only (`agents-cli.secrets.<bundle>.<KEY>` — metadata never
36
- * synced), or both; all three shapes appear in real iCloud strays, so every
37
- * one becomes a candidate.
38
- */
39
- export declare function groupSyncedServices(services: string[]): SyncedBundleCandidate[];
40
- /** Enumerate the iCloud keychain and return every orphaned bundle candidate. */
41
- export declare function discoverSyncedBundles(): SyncedBundleCandidate[];
42
- export interface ImportSyncedOptions {
43
- /** Overwrite keys that already exist in the local bundle. */
44
- force?: boolean;
45
- /** Store imported values as literals in the bundle metadata (no keychain items). */
46
- allPlaintext?: boolean;
47
- /** Backend for a newly created bundle (existing bundles keep theirs). */
48
- backend?: SecretsBackend;
49
- /** Delete the iCloud copies of successfully-read items after import. */
50
- purge?: boolean;
51
- }
52
- export interface ImportSyncedResult {
53
- name: string;
54
- added: number;
55
- skipped: number;
56
- /** Keys whose iCloud value could not be read (left in place, never purged). */
57
- missing: string[];
58
- /**
59
- * Keys the modern store refuses by policy (reserved env names like USER,
60
- * loader/interpreter vars like DYLD_*) — the pre-cutover store accepted
61
- * them. Left in iCloud, never purged: the iCloud item is the only copy.
62
- */
63
- unimportable: string[];
64
- purged: number;
65
- }
66
- /**
67
- * Import one discovered iCloud bundle into the local (device-local) store.
68
- *
69
- * Values come from the synced secret items; the synced metadata item, when
70
- * present, contributes the description, literal vars, and non-keychain refs
71
- * (env:/file:/exec: refs carry no stored secret, so copying the ref preserves
72
- * them exactly). Existing local keys are skipped unless `force`. Keys the
73
- * modern store refuses by policy (reserved / loader env names the pre-cutover
74
- * store accepted) are reported as `unimportable` instead of aborting the whole
75
- * bundle. With `purge`, only services whose value provably lives locally
76
- * (imported now, or skipped-because-present) are deleted from iCloud — an
77
- * unreadable or unimportable item is never destroyed.
78
- */
79
- export declare function importSyncedBundle(candidate: SyncedBundleCandidate, opts?: ImportSyncedOptions): ImportSyncedResult;
@@ -1,206 +0,0 @@
1
- /**
2
- * Recovery for LEGACY SYNCHRONIZABLE (iCloud Keychain) bundles.
3
- *
4
- * The pre-biometry helper era defaulted bundles to iCloud Keychain sync. The
5
- * device-local cutover (biometry ACL + kSecAttrSynchronizable false on every
6
- * query) orphaned those items: they still sync back via iCloud Keychain, but
7
- * neither `secrets list` nor `migrate-acl` can see them. This module powers
8
- * `agents secrets import --from icloud` — discover the orphaned bundles,
9
- * re-import them as normal device-local bundles, and optionally purge the
10
- * iCloud copies.
11
- *
12
- * The item-name scheme is the same one the modern store uses (see bundles.ts):
13
- * metadata under `agents-cli.bundles.<name>`, one value per key under
14
- * `agents-cli.secrets.<bundle>.<key>`. Legacy env keys never contained a dot,
15
- * so metadata-less synced secret services split at their LAST dot to recover
16
- * the bundle/key boundary even for dotted bundle names like `hetzner.com`.
17
- */
18
- import { deleteSyncedKeychainItem, getSyncedKeychainTokens, listSyncedKeychainItems, parseBundleValue, secretsKeychainItem, serializeRef, SECRETS_ITEM_PREFIX, } from './index.js';
19
- import { BUNDLE_META_PREFIX, BUNDLE_NAME_PATTERN, BUNDLE_KEY_PATTERN, ENV_KEY_PATTERN, bundleKeyToEnvKey, bundleExists, bundleItemStore, bundlePolicy, isLoaderOrInterpreterEnv, isReservedEnvName, keychainRef, readBundle, writeBundle, } from './bundles.js';
20
- /**
21
- * Group raw synced service names into per-bundle candidates. Pure — separated
22
- * from discovery so the parsing rules are unit-testable without a keychain.
23
- *
24
- * A bundle can surface as metadata only (`agents-cli.bundles.<name>`), as
25
- * secret items only (`agents-cli.secrets.<bundle>.<KEY>` — metadata never
26
- * synced), or both; all three shapes appear in real iCloud strays, so every
27
- * one becomes a candidate.
28
- */
29
- export function groupSyncedServices(services) {
30
- const byName = new Map();
31
- const claim = (name) => {
32
- let c = byName.get(name);
33
- if (!c) {
34
- c = { name, keys: [], hasMeta: false, services: [] };
35
- byName.set(name, c);
36
- }
37
- return c;
38
- };
39
- for (const svc of services) {
40
- if (svc.startsWith(BUNDLE_META_PREFIX)) {
41
- const name = svc.slice(BUNDLE_META_PREFIX.length);
42
- if (!BUNDLE_NAME_PATTERN.test(name))
43
- continue;
44
- const c = claim(name);
45
- c.hasMeta = true;
46
- c.services.push(svc);
47
- }
48
- else if (svc.startsWith(SECRETS_ITEM_PREFIX)) {
49
- const rest = svc.slice(SECRETS_ITEM_PREFIX.length);
50
- const cut = rest.lastIndexOf('.');
51
- if (cut <= 0)
52
- continue;
53
- const name = rest.slice(0, cut);
54
- const key = rest.slice(cut + 1);
55
- if (!BUNDLE_NAME_PATTERN.test(name) || !ENV_KEY_PATTERN.test(key))
56
- continue;
57
- const c = claim(name);
58
- if (!c.keys.includes(key))
59
- c.keys.push(key);
60
- c.services.push(svc);
61
- }
62
- }
63
- return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
64
- }
65
- /** Enumerate the iCloud keychain and return every orphaned bundle candidate. */
66
- export function discoverSyncedBundles() {
67
- return groupSyncedServices(listSyncedKeychainItems('agents-cli.'));
68
- }
69
- /**
70
- * Import one discovered iCloud bundle into the local (device-local) store.
71
- *
72
- * Values come from the synced secret items; the synced metadata item, when
73
- * present, contributes the description, literal vars, and non-keychain refs
74
- * (env:/file:/exec: refs carry no stored secret, so copying the ref preserves
75
- * them exactly). Existing local keys are skipped unless `force`. Keys the
76
- * modern store refuses by policy (reserved / loader env names the pre-cutover
77
- * store accepted) are reported as `unimportable` instead of aborting the whole
78
- * bundle. With `purge`, only services whose value provably lives locally
79
- * (imported now, or skipped-because-present) are deleted from iCloud — an
80
- * unreadable or unimportable item is never destroyed.
81
- */
82
- export function importSyncedBundle(candidate, opts = {}) {
83
- const values = getSyncedKeychainTokens(candidate.services);
84
- let bundle;
85
- if (bundleExists(candidate.name)) {
86
- bundle = readBundle(candidate.name);
87
- }
88
- else {
89
- bundle = {
90
- name: candidate.name,
91
- backend: opts.backend === 'keychain' ? undefined : opts.backend,
92
- vars: {},
93
- };
94
- }
95
- const metaService = BUNDLE_META_PREFIX + candidate.name;
96
- let metaVars = {};
97
- const metaJson = values.get(metaService);
98
- if (metaJson !== undefined) {
99
- try {
100
- const parsed = JSON.parse(metaJson);
101
- if (parsed && typeof parsed === 'object') {
102
- if (typeof parsed.description === 'string' && !bundle.description) {
103
- bundle.description = parsed.description;
104
- }
105
- if (parsed.vars && typeof parsed.vars === 'object') {
106
- metaVars = parsed.vars;
107
- }
108
- }
109
- }
110
- catch {
111
- // Corrupt legacy metadata — the per-key secret items still import.
112
- }
113
- }
114
- const store = bundleItemStore(bundle.backend, { noAcl: bundlePolicy(bundle) === 'never' });
115
- let added = 0;
116
- let skipped = 0;
117
- const missing = [];
118
- const unimportable = [];
119
- // Services whose value now provably lives in the local store — imported this
120
- // run, or skipped because the local bundle already carries the key. Only
121
- // these may be purged; a missing or unimportable key's iCloud item is its
122
- // only copy and must survive.
123
- const purgeable = new Set();
124
- const rejectedByPolicy = (key) => {
125
- const envKey = bundleKeyToEnvKey(key);
126
- return isLoaderOrInterpreterEnv(envKey) || isReservedEnvName(envKey);
127
- };
128
- // Keys with a synced secret item: re-store the value device-locally.
129
- for (const key of candidate.keys) {
130
- // The pre-cutover store accepted keys the modern one refuses (writeBundle
131
- // throws) — skip them instead of aborting the whole bundle.
132
- if (rejectedByPolicy(key)) {
133
- unimportable.push(key);
134
- continue;
135
- }
136
- const value = values.get(secretsKeychainItem(candidate.name, key));
137
- if (value === undefined) {
138
- missing.push(key);
139
- continue;
140
- }
141
- if (!opts.force && key in bundle.vars) {
142
- skipped++;
143
- purgeable.add(secretsKeychainItem(candidate.name, key));
144
- continue;
145
- }
146
- if (opts.allPlaintext) {
147
- bundle.vars[key] = { value };
148
- }
149
- else {
150
- store.set(secretsKeychainItem(candidate.name, key), value);
151
- bundle.vars[key] = keychainRef(key);
152
- }
153
- purgeable.add(secretsKeychainItem(candidate.name, key));
154
- added++;
155
- }
156
- // Vars declared only in the synced metadata: literals and non-keychain refs
157
- // carry everything they need; a keychain ref without its synced item is
158
- // unrecoverable.
159
- for (const [key, raw] of Object.entries(metaVars)) {
160
- if (!BUNDLE_KEY_PATTERN.test(key))
161
- continue;
162
- if (candidate.keys.includes(key))
163
- continue; // the secret item already covered it
164
- if (rejectedByPolicy(key)) {
165
- if (!unimportable.includes(key))
166
- unimportable.push(key);
167
- continue;
168
- }
169
- let parsed;
170
- try {
171
- parsed = parseBundleValue(raw);
172
- }
173
- catch {
174
- continue; // malformed legacy entry
175
- }
176
- if ('ref' in parsed && parsed.ref.provider === 'keychain') {
177
- if (!missing.includes(key))
178
- missing.push(key);
179
- continue;
180
- }
181
- if (!opts.force && key in bundle.vars) {
182
- skipped++;
183
- continue;
184
- }
185
- bundle.vars[key] = 'literal' in parsed ? { value: parsed.literal } : serializeRef(parsed.ref);
186
- added++;
187
- }
188
- writeBundle(bundle);
189
- // The metadata item is only a projection of what was just written locally —
190
- // safe to purge once writeBundle has succeeded, UNLESS it still names keys
191
- // that never made it over (missing/unimportable): then it stays as the only
192
- // record of them.
193
- if (metaJson !== undefined && missing.length === 0 && unimportable.length === 0) {
194
- purgeable.add(metaService);
195
- }
196
- let purged = 0;
197
- if (opts.purge) {
198
- for (const svc of candidate.services) {
199
- if (!purgeable.has(svc))
200
- continue;
201
- if (deleteSyncedKeychainItem(svc))
202
- purged++;
203
- }
204
- }
205
- return { name: candidate.name, added, skipped, missing, unimportable, purged };
206
- }