@cleocode/core 2026.9.23 → 2026.9.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (651) hide show
  1. package/dist/admin/import-tasks.d.ts.map +1 -1
  2. package/dist/admin/import-tasks.js +35 -6
  3. package/dist/admin/import-tasks.js.map +1 -1
  4. package/dist/admin/import.d.ts.map +1 -1
  5. package/dist/admin/import.js +25 -6
  6. package/dist/admin/import.js.map +1 -1
  7. package/dist/build-provenance.json +2 -2
  8. package/dist/cloud/http.d.ts +8 -0
  9. package/dist/cloud/http.d.ts.map +1 -1
  10. package/dist/cloud/http.js +8 -1
  11. package/dist/cloud/http.js.map +1 -1
  12. package/dist/cloud/index.d.ts +6 -1
  13. package/dist/cloud/index.d.ts.map +1 -1
  14. package/dist/cloud/index.js +6 -1
  15. package/dist/cloud/index.js.map +1 -1
  16. package/dist/cloud/nexus-auth.d.ts +169 -0
  17. package/dist/cloud/nexus-auth.d.ts.map +1 -0
  18. package/dist/cloud/nexus-auth.js +372 -0
  19. package/dist/cloud/nexus-auth.js.map +1 -0
  20. package/dist/cloud/nexus-credentials.d.ts +188 -0
  21. package/dist/cloud/nexus-credentials.d.ts.map +1 -0
  22. package/dist/cloud/nexus-credentials.js +323 -0
  23. package/dist/cloud/nexus-credentials.js.map +1 -0
  24. package/dist/cloud/nexus-link.d.ts +85 -0
  25. package/dist/cloud/nexus-link.d.ts.map +1 -0
  26. package/dist/cloud/nexus-link.js +226 -0
  27. package/dist/cloud/nexus-link.js.map +1 -0
  28. package/dist/cloud/presence.d.ts +88 -0
  29. package/dist/cloud/presence.d.ts.map +1 -0
  30. package/dist/cloud/presence.js +137 -0
  31. package/dist/cloud/presence.js.map +1 -0
  32. package/dist/cloud/projects.d.ts +3 -1
  33. package/dist/cloud/projects.d.ts.map +1 -1
  34. package/dist/cloud/projects.js +4 -2
  35. package/dist/cloud/projects.js.map +1 -1
  36. package/dist/crypto/credentials.d.ts +14 -4
  37. package/dist/crypto/credentials.d.ts.map +1 -1
  38. package/dist/crypto/credentials.js +15 -1
  39. package/dist/crypto/credentials.js.map +1 -1
  40. package/dist/decide/bench/dataset.d.ts +125 -0
  41. package/dist/decide/bench/dataset.d.ts.map +1 -0
  42. package/dist/decide/bench/dataset.js +457 -0
  43. package/dist/decide/bench/dataset.js.map +1 -0
  44. package/dist/decide/bench/index.d.ts +23 -0
  45. package/dist/decide/bench/index.d.ts.map +1 -0
  46. package/dist/decide/bench/index.js +23 -0
  47. package/dist/decide/bench/index.js.map +1 -0
  48. package/dist/decide/bench/metrics.d.ts +55 -0
  49. package/dist/decide/bench/metrics.d.ts.map +1 -0
  50. package/dist/decide/bench/metrics.js +100 -0
  51. package/dist/decide/bench/metrics.js.map +1 -0
  52. package/dist/decide/bench/operation.d.ts +119 -0
  53. package/dist/decide/bench/operation.d.ts.map +1 -0
  54. package/dist/decide/bench/operation.js +171 -0
  55. package/dist/decide/bench/operation.js.map +1 -0
  56. package/dist/decide/bench/profiles.d.ts +74 -0
  57. package/dist/decide/bench/profiles.d.ts.map +1 -0
  58. package/dist/decide/bench/profiles.js +131 -0
  59. package/dist/decide/bench/profiles.js.map +1 -0
  60. package/dist/decide/bench/questions.d.ts +40 -0
  61. package/dist/decide/bench/questions.d.ts.map +1 -0
  62. package/dist/decide/bench/questions.js +107 -0
  63. package/dist/decide/bench/questions.js.map +1 -0
  64. package/dist/decide/bench/report.d.ts +21 -0
  65. package/dist/decide/bench/report.d.ts.map +1 -0
  66. package/dist/decide/bench/report.js +120 -0
  67. package/dist/decide/bench/report.js.map +1 -0
  68. package/dist/decide/bench/runner.d.ts +203 -0
  69. package/dist/decide/bench/runner.d.ts.map +1 -0
  70. package/dist/decide/bench/runner.js +335 -0
  71. package/dist/decide/bench/runner.js.map +1 -0
  72. package/dist/decide/bench/source.d.ts +21 -0
  73. package/dist/decide/bench/source.d.ts.map +1 -0
  74. package/dist/decide/bench/source.js +67 -0
  75. package/dist/decide/bench/source.js.map +1 -0
  76. package/dist/decide/bench/spot-check.d.ts +117 -0
  77. package/dist/decide/bench/spot-check.d.ts.map +1 -0
  78. package/dist/decide/bench/spot-check.js +221 -0
  79. package/dist/decide/bench/spot-check.js.map +1 -0
  80. package/dist/decide/bench/stats.d.ts +57 -0
  81. package/dist/decide/bench/stats.d.ts.map +1 -0
  82. package/dist/decide/bench/stats.js +82 -0
  83. package/dist/decide/bench/stats.js.map +1 -0
  84. package/dist/decide/bench/types.d.ts +216 -0
  85. package/dist/decide/bench/types.d.ts.map +1 -0
  86. package/dist/decide/bench/types.js +104 -0
  87. package/dist/decide/bench/types.js.map +1 -0
  88. package/dist/decide/client.d.ts +35 -1
  89. package/dist/decide/client.d.ts.map +1 -1
  90. package/dist/decide/client.js +124 -30
  91. package/dist/decide/client.js.map +1 -1
  92. package/dist/decide/credentials.d.ts +177 -17
  93. package/dist/decide/credentials.d.ts.map +1 -1
  94. package/dist/decide/credentials.js +505 -41
  95. package/dist/decide/credentials.js.map +1 -1
  96. package/dist/decide/index.d.ts +8 -8
  97. package/dist/decide/index.d.ts.map +1 -1
  98. package/dist/decide/index.js +8 -8
  99. package/dist/decide/index.js.map +1 -1
  100. package/dist/decide/jev-wire.d.ts +25 -19
  101. package/dist/decide/jev-wire.d.ts.map +1 -1
  102. package/dist/decide/jev-wire.js +56 -31
  103. package/dist/decide/jev-wire.js.map +1 -1
  104. package/dist/decide/operations.d.ts +63 -12
  105. package/dist/decide/operations.d.ts.map +1 -1
  106. package/dist/decide/operations.js +116 -58
  107. package/dist/decide/operations.js.map +1 -1
  108. package/dist/decide/provider-state.d.ts +85 -12
  109. package/dist/decide/provider-state.d.ts.map +1 -1
  110. package/dist/decide/provider-state.js +154 -21
  111. package/dist/decide/provider-state.js.map +1 -1
  112. package/dist/decide/provider.d.ts +5 -0
  113. package/dist/decide/provider.d.ts.map +1 -1
  114. package/dist/decide/provider.js.map +1 -1
  115. package/dist/decide/providers.d.ts +15 -2
  116. package/dist/decide/providers.d.ts.map +1 -1
  117. package/dist/decide/providers.js +22 -6
  118. package/dist/decide/providers.js.map +1 -1
  119. package/dist/decide/sites/registry.d.ts +22 -0
  120. package/dist/decide/sites/registry.d.ts.map +1 -1
  121. package/dist/decide/sites/registry.js +23 -0
  122. package/dist/decide/sites/registry.js.map +1 -1
  123. package/dist/decide/wizard.d.ts +12 -4
  124. package/dist/decide/wizard.d.ts.map +1 -1
  125. package/dist/decide/wizard.js +79 -26
  126. package/dist/decide/wizard.js.map +1 -1
  127. package/dist/dispatch/mvi-projection.d.ts +15 -2
  128. package/dist/dispatch/mvi-projection.d.ts.map +1 -1
  129. package/dist/dispatch/mvi-projection.js +73 -12
  130. package/dist/dispatch/mvi-projection.js.map +1 -1
  131. package/dist/docs/adr-allocator.d.ts +2 -2
  132. package/dist/docs/adr-allocator.d.ts.map +1 -1
  133. package/dist/docs/adr-allocator.js +17 -4
  134. package/dist/docs/adr-allocator.js.map +1 -1
  135. package/dist/docs/blob-consolidation.js +1 -1
  136. package/dist/docs/blob-consolidation.js.map +1 -1
  137. package/dist/docs/display-alias.d.ts +1 -1
  138. package/dist/docs/display-alias.d.ts.map +1 -1
  139. package/dist/docs/display-alias.js +7 -4
  140. package/dist/docs/display-alias.js.map +1 -1
  141. package/dist/docs/docs-inconsistency-detector.js +1 -1
  142. package/dist/docs/docs-inconsistency-detector.js.map +1 -1
  143. package/dist/docs/docs-update.d.ts.map +1 -1
  144. package/dist/docs/docs-update.js +6 -1
  145. package/dist/docs/docs-update.js.map +1 -1
  146. package/dist/docs/export-document.js +2928 -1053
  147. package/dist/docs/export-document.js.map +3 -3
  148. package/dist/docs/numbering.js +1 -1
  149. package/dist/docs/numbering.js.map +1 -1
  150. package/dist/docs/supersede.d.ts.map +1 -1
  151. package/dist/docs/supersede.js +6 -3
  152. package/dist/docs/supersede.js.map +1 -1
  153. package/dist/docs/version-ssot.js +1 -1
  154. package/dist/docs/version-ssot.js.map +1 -1
  155. package/dist/doctor/db-substrate.d.ts.map +1 -1
  156. package/dist/doctor/db-substrate.js +39 -24
  157. package/dist/doctor/db-substrate.js.map +1 -1
  158. package/dist/doctor/knowledge-summary.d.ts +10 -3
  159. package/dist/doctor/knowledge-summary.d.ts.map +1 -1
  160. package/dist/doctor/knowledge-summary.js +10 -3
  161. package/dist/doctor/knowledge-summary.js.map +1 -1
  162. package/dist/doctor/project-identity.d.ts +128 -37
  163. package/dist/doctor/project-identity.d.ts.map +1 -1
  164. package/dist/doctor/project-identity.js +476 -63
  165. package/dist/doctor/project-identity.js.map +1 -1
  166. package/dist/doctor/twin-collapse.d.ts +52 -2
  167. package/dist/doctor/twin-collapse.d.ts.map +1 -1
  168. package/dist/doctor/twin-collapse.js +108 -4
  169. package/dist/doctor/twin-collapse.js.map +1 -1
  170. package/dist/error-catalog.js +2 -2
  171. package/dist/error-catalog.js.map +1 -1
  172. package/dist/error-registry.js +1 -1
  173. package/dist/error-registry.js.map +1 -1
  174. package/dist/gateway-client/generated/types.gen.d.ts +22 -2
  175. package/dist/gateway-client/generated/types.gen.d.ts.map +1 -1
  176. package/dist/index.d.ts +1 -1
  177. package/dist/index.d.ts.map +1 -1
  178. package/dist/index.js +1 -1
  179. package/dist/index.js.map +1 -1
  180. package/dist/init.d.ts.map +1 -1
  181. package/dist/init.js +20 -11
  182. package/dist/init.js.map +1 -1
  183. package/dist/inject/index.d.ts.map +1 -1
  184. package/dist/inject/index.js +2 -3
  185. package/dist/inject/index.js.map +1 -1
  186. package/dist/internal.d.ts +5 -4
  187. package/dist/internal.d.ts.map +1 -1
  188. package/dist/internal.js +6 -4
  189. package/dist/internal.js.map +1 -1
  190. package/dist/llm/credential-remove-entry.d.ts +59 -0
  191. package/dist/llm/credential-remove-entry.d.ts.map +1 -0
  192. package/dist/llm/credential-remove-entry.js +101 -0
  193. package/dist/llm/credential-remove-entry.js.map +1 -0
  194. package/dist/llm/oauth/device-code.d.ts +26 -6
  195. package/dist/llm/oauth/device-code.d.ts.map +1 -1
  196. package/dist/llm/oauth/device-code.js +44 -12
  197. package/dist/llm/oauth/device-code.js.map +1 -1
  198. package/dist/llm/plugin-facade.js +3017 -1142
  199. package/dist/llm/plugin-facade.js.map +3 -3
  200. package/dist/memory/attention.d.ts.map +1 -1
  201. package/dist/memory/attention.js +5 -3
  202. package/dist/memory/attention.js.map +1 -1
  203. package/dist/memory/decisions.d.ts +48 -3
  204. package/dist/memory/decisions.d.ts.map +1 -1
  205. package/dist/memory/decisions.js +98 -14
  206. package/dist/memory/decisions.js.map +1 -1
  207. package/dist/memory/learnings.d.ts +6 -6
  208. package/dist/memory/patterns.d.ts +4 -4
  209. package/dist/migration/manual/T10165-backfill-adr-index.d.ts.map +1 -1
  210. package/dist/migration/manual/T10165-backfill-adr-index.js +5 -1
  211. package/dist/migration/manual/T10165-backfill-adr-index.js.map +1 -1
  212. package/dist/nexus/fleet-status.d.ts +83 -0
  213. package/dist/nexus/fleet-status.d.ts.map +1 -0
  214. package/dist/nexus/fleet-status.js +414 -0
  215. package/dist/nexus/fleet-status.js.map +1 -0
  216. package/dist/nexus/git-state.d.ts +15 -0
  217. package/dist/nexus/git-state.d.ts.map +1 -1
  218. package/dist/nexus/git-state.js +67 -2
  219. package/dist/nexus/git-state.js.map +1 -1
  220. package/dist/nexus/identity.d.ts.map +1 -1
  221. package/dist/nexus/identity.js +4 -0
  222. package/dist/nexus/identity.js.map +1 -1
  223. package/dist/nexus/index.d.ts +4 -1
  224. package/dist/nexus/index.d.ts.map +1 -1
  225. package/dist/nexus/index.js +7 -1
  226. package/dist/nexus/index.js.map +1 -1
  227. package/dist/nexus/migrate-json-to-sqlite.d.ts +3 -2
  228. package/dist/nexus/migrate-json-to-sqlite.d.ts.map +1 -1
  229. package/dist/nexus/migrate-json-to-sqlite.js +9 -17
  230. package/dist/nexus/migrate-json-to-sqlite.js.map +1 -1
  231. package/dist/nexus/ops.d.ts +1 -0
  232. package/dist/nexus/ops.d.ts.map +1 -1
  233. package/dist/nexus/project-activity.d.ts +104 -0
  234. package/dist/nexus/project-activity.d.ts.map +1 -0
  235. package/dist/nexus/project-activity.js +150 -0
  236. package/dist/nexus/project-activity.js.map +1 -0
  237. package/dist/nexus/query.d.ts +2 -2
  238. package/dist/nexus/query.d.ts.map +1 -1
  239. package/dist/nexus/query.js +4 -19
  240. package/dist/nexus/query.js.map +1 -1
  241. package/dist/nexus/registry-errors.d.ts +61 -0
  242. package/dist/nexus/registry-errors.d.ts.map +1 -0
  243. package/dist/nexus/registry-errors.js +85 -0
  244. package/dist/nexus/registry-errors.js.map +1 -0
  245. package/dist/nexus/registry.d.ts +30 -5
  246. package/dist/nexus/registry.d.ts.map +1 -1
  247. package/dist/nexus/registry.js +115 -70
  248. package/dist/nexus/registry.js.map +1 -1
  249. package/dist/orchestrate/pivot.d.ts +4 -0
  250. package/dist/orchestrate/pivot.d.ts.map +1 -1
  251. package/dist/orchestrate/pivot.js +75 -12
  252. package/dist/orchestrate/pivot.js.map +1 -1
  253. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  254. package/dist/orchestrate/query-ops.js +29 -27
  255. package/dist/orchestrate/query-ops.js.map +1 -1
  256. package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
  257. package/dist/orchestrate/spawn-ops.js +29 -3
  258. package/dist/orchestrate/spawn-ops.js.map +1 -1
  259. package/dist/orchestration/bootstrap.d.ts.map +1 -1
  260. package/dist/orchestration/bootstrap.js +20 -14
  261. package/dist/orchestration/bootstrap.js.map +1 -1
  262. package/dist/orchestration/index.d.ts +15 -1
  263. package/dist/orchestration/index.d.ts.map +1 -1
  264. package/dist/orchestration/index.js +25 -6
  265. package/dist/orchestration/index.js.map +1 -1
  266. package/dist/orchestration/lead-rollup.d.ts +7 -1
  267. package/dist/orchestration/lead-rollup.d.ts.map +1 -1
  268. package/dist/orchestration/lead-rollup.js +6 -3
  269. package/dist/orchestration/lead-rollup.js.map +1 -1
  270. package/dist/orchestration/spawn-prompt.js +1 -1
  271. package/dist/orchestration/spawn-prompt.js.map +1 -1
  272. package/dist/orchestration/waves.d.ts +21 -10
  273. package/dist/orchestration/waves.d.ts.map +1 -1
  274. package/dist/orchestration/waves.js +38 -35
  275. package/dist/orchestration/waves.js.map +1 -1
  276. package/dist/paths.d.ts +9 -2
  277. package/dist/paths.d.ts.map +1 -1
  278. package/dist/paths.js +30 -13
  279. package/dist/paths.js.map +1 -1
  280. package/dist/phases/index.d.ts +1 -1
  281. package/dist/phases/index.d.ts.map +1 -1
  282. package/dist/phases/index.js +3 -4
  283. package/dist/phases/index.js.map +1 -1
  284. package/dist/project-info.d.ts +40 -3
  285. package/dist/project-info.d.ts.map +1 -1
  286. package/dist/project-info.js +74 -6
  287. package/dist/project-info.js.map +1 -1
  288. package/dist/project-lifecycle.d.ts +28 -7
  289. package/dist/project-lifecycle.d.ts.map +1 -1
  290. package/dist/project-lifecycle.js +114 -47
  291. package/dist/project-lifecycle.js.map +1 -1
  292. package/dist/project-scope.d.ts +70 -3
  293. package/dist/project-scope.d.ts.map +1 -1
  294. package/dist/project-scope.js +154 -15
  295. package/dist/project-scope.js.map +1 -1
  296. package/dist/release/open.d.ts +12 -2
  297. package/dist/release/open.d.ts.map +1 -1
  298. package/dist/release/open.js +32 -3
  299. package/dist/release/open.js.map +1 -1
  300. package/dist/release/preflight-skip.d.ts +73 -0
  301. package/dist/release/preflight-skip.d.ts.map +1 -0
  302. package/dist/release/preflight-skip.js +254 -0
  303. package/dist/release/preflight-skip.js.map +1 -0
  304. package/dist/render/decide/index.d.ts +51 -0
  305. package/dist/render/decide/index.d.ts.map +1 -0
  306. package/dist/render/decide/index.js +289 -0
  307. package/dist/render/decide/index.js.map +1 -0
  308. package/dist/render/index.d.ts +1 -0
  309. package/dist/render/index.d.ts.map +1 -1
  310. package/dist/render/index.js +1 -0
  311. package/dist/render/index.js.map +1 -1
  312. package/dist/render/nexus/graph/index.d.ts +5 -0
  313. package/dist/render/nexus/graph/index.d.ts.map +1 -1
  314. package/dist/render/nexus/graph/index.js +53 -0
  315. package/dist/render/nexus/graph/index.js.map +1 -1
  316. package/dist/scaffold/ensure-config.d.ts +14 -6
  317. package/dist/scaffold/ensure-config.d.ts.map +1 -1
  318. package/dist/scaffold/ensure-config.js +39 -17
  319. package/dist/scaffold/ensure-config.js.map +1 -1
  320. package/dist/scaffold/project-identity.d.ts +115 -23
  321. package/dist/scaffold/project-identity.d.ts.map +1 -1
  322. package/dist/scaffold/project-identity.js +196 -44
  323. package/dist/scaffold/project-identity.js.map +1 -1
  324. package/dist/selfimprove/fix-gen.js +3027 -1152
  325. package/dist/selfimprove/fix-gen.js.map +3 -3
  326. package/dist/sentient/cross-project-hygiene.d.ts +12 -6
  327. package/dist/sentient/cross-project-hygiene.d.ts.map +1 -1
  328. package/dist/sentient/cross-project-hygiene.js +15 -10
  329. package/dist/sentient/cross-project-hygiene.js.map +1 -1
  330. package/dist/session/engine-ops.d.ts +13 -4
  331. package/dist/session/engine-ops.d.ts.map +1 -1
  332. package/dist/session/engine-ops.js +38 -45
  333. package/dist/session/engine-ops.js.map +1 -1
  334. package/dist/sessions/briefing.d.ts +7 -3
  335. package/dist/sessions/briefing.d.ts.map +1 -1
  336. package/dist/sessions/briefing.js +10 -10
  337. package/dist/sessions/briefing.js.map +1 -1
  338. package/dist/sessions/drift-watchdog.d.ts.map +1 -1
  339. package/dist/sessions/drift-watchdog.js +11 -19
  340. package/dist/sessions/drift-watchdog.js.map +1 -1
  341. package/dist/sessions/focus-state-store.d.ts +103 -26
  342. package/dist/sessions/focus-state-store.d.ts.map +1 -1
  343. package/dist/sessions/focus-state-store.js +166 -37
  344. package/dist/sessions/focus-state-store.js.map +1 -1
  345. package/dist/sessions/handoff.d.ts.map +1 -1
  346. package/dist/sessions/handoff.js +9 -18
  347. package/dist/sessions/handoff.js.map +1 -1
  348. package/dist/sessions/index.d.ts +4 -2
  349. package/dist/sessions/index.d.ts.map +1 -1
  350. package/dist/sessions/index.js +35 -6
  351. package/dist/sessions/index.js.map +1 -1
  352. package/dist/sessions/session-drift.d.ts.map +1 -1
  353. package/dist/sessions/session-drift.js +4 -2
  354. package/dist/sessions/session-drift.js.map +1 -1
  355. package/dist/sessions/session-view.d.ts +8 -1
  356. package/dist/sessions/session-view.d.ts.map +1 -1
  357. package/dist/sessions/session-view.js +9 -2
  358. package/dist/sessions/session-view.js.map +1 -1
  359. package/dist/setup/config-json-merge.d.ts.map +1 -1
  360. package/dist/setup/config-json-merge.js +1 -0
  361. package/dist/setup/config-json-merge.js.map +1 -1
  362. package/dist/setup/index.d.ts +4 -2
  363. package/dist/setup/index.d.ts.map +1 -1
  364. package/dist/setup/index.js +6 -2
  365. package/dist/setup/index.js.map +1 -1
  366. package/dist/setup/sections/nexus-account.d.ts +32 -0
  367. package/dist/setup/sections/nexus-account.d.ts.map +1 -0
  368. package/dist/setup/sections/nexus-account.js +64 -0
  369. package/dist/setup/sections/nexus-account.js.map +1 -0
  370. package/dist/setup/wizard.d.ts +1 -1
  371. package/dist/setup/wizard.d.ts.map +1 -1
  372. package/dist/setup/wizard.js.map +1 -1
  373. package/dist/skills/orchestrator/startup.d.ts.map +1 -1
  374. package/dist/skills/orchestrator/startup.js +18 -31
  375. package/dist/skills/orchestrator/startup.js.map +1 -1
  376. package/dist/snapshot/index.d.ts.map +1 -1
  377. package/dist/snapshot/index.js +60 -53
  378. package/dist/snapshot/index.js.map +1 -1
  379. package/dist/spawn/agent-identity.d.ts +33 -1
  380. package/dist/spawn/agent-identity.d.ts.map +1 -1
  381. package/dist/spawn/agent-identity.js +53 -1
  382. package/dist/spawn/agent-identity.js.map +1 -1
  383. package/dist/spawn/index.d.ts +1 -1
  384. package/dist/spawn/index.d.ts.map +1 -1
  385. package/dist/spawn/index.js +1 -1
  386. package/dist/spawn/index.js.map +1 -1
  387. package/dist/stats/index.d.ts.map +1 -1
  388. package/dist/stats/index.js +3 -4
  389. package/dist/stats/index.js.map +1 -1
  390. package/dist/status/index.d.ts +12 -3
  391. package/dist/status/index.d.ts.map +1 -1
  392. package/dist/status/index.js +47 -5
  393. package/dist/status/index.js.map +1 -1
  394. package/dist/store/attachment-repair.d.ts.map +1 -1
  395. package/dist/store/attachment-repair.js +42 -3
  396. package/dist/store/attachment-repair.js.map +1 -1
  397. package/dist/store/attachment-store.d.ts.map +1 -1
  398. package/dist/store/attachment-store.js +30 -46
  399. package/dist/store/attachment-store.js.map +1 -1
  400. package/dist/store/backup-recover-brain.d.ts +4 -0
  401. package/dist/store/backup-recover-brain.d.ts.map +1 -1
  402. package/dist/store/backup-recover-brain.js +2 -0
  403. package/dist/store/backup-recover-brain.js.map +1 -1
  404. package/dist/store/backup-recover.d.ts +9 -0
  405. package/dist/store/backup-recover.d.ts.map +1 -1
  406. package/dist/store/backup-recover.js +28 -1
  407. package/dist/store/backup-recover.js.map +1 -1
  408. package/dist/store/backup-sidecar.d.ts +30 -0
  409. package/dist/store/backup-sidecar.d.ts.map +1 -1
  410. package/dist/store/backup-sidecar.js +90 -2
  411. package/dist/store/backup-sidecar.js.map +1 -1
  412. package/dist/store/blob-keep.d.ts +135 -0
  413. package/dist/store/blob-keep.d.ts.map +1 -0
  414. package/dist/store/blob-keep.js +283 -0
  415. package/dist/store/blob-keep.js.map +1 -0
  416. package/dist/store/converters.d.ts.map +1 -1
  417. package/dist/store/converters.js +11 -0
  418. package/dist/store/converters.js.map +1 -1
  419. package/dist/store/credential-transfer.d.ts +37 -5
  420. package/dist/store/credential-transfer.d.ts.map +1 -1
  421. package/dist/store/credential-transfer.js +73 -18
  422. package/dist/store/credential-transfer.js.map +1 -1
  423. package/dist/store/data-accessor.d.ts +1 -1
  424. package/dist/store/data-accessor.d.ts.map +1 -1
  425. package/dist/store/data-accessor.js.map +1 -1
  426. package/dist/store/db-helpers.d.ts +18 -0
  427. package/dist/store/db-helpers.d.ts.map +1 -1
  428. package/dist/store/db-helpers.js +46 -0
  429. package/dist/store/db-helpers.js.map +1 -1
  430. package/dist/store/exodus/on-open.d.ts.map +1 -1
  431. package/dist/store/exodus/on-open.js +35 -0
  432. package/dist/store/exodus/on-open.js.map +1 -1
  433. package/dist/store/exodus/runtime-targets.d.ts +3 -3
  434. package/dist/store/exodus/runtime-targets.js +3 -3
  435. package/dist/store/import-remap.d.ts +12 -1
  436. package/dist/store/import-remap.d.ts.map +1 -1
  437. package/dist/store/import-remap.js +14 -0
  438. package/dist/store/import-remap.js.map +1 -1
  439. package/dist/store/legacy-tasks-lineage.d.ts +3 -1
  440. package/dist/store/legacy-tasks-lineage.d.ts.map +1 -1
  441. package/dist/store/legacy-tasks-lineage.js +3 -1
  442. package/dist/store/legacy-tasks-lineage.js.map +1 -1
  443. package/dist/store/memory-accessor.js +1 -1
  444. package/dist/store/memory-accessor.js.map +1 -1
  445. package/dist/store/migration-manager.d.ts +61 -0
  446. package/dist/store/migration-manager.d.ts.map +1 -1
  447. package/dist/store/migration-manager.js +102 -4
  448. package/dist/store/migration-manager.js.map +1 -1
  449. package/dist/store/nexus-validation-schemas.d.ts +64 -0
  450. package/dist/store/nexus-validation-schemas.d.ts.map +1 -1
  451. package/dist/store/portable-bundle-import.d.ts +8 -0
  452. package/dist/store/portable-bundle-import.d.ts.map +1 -1
  453. package/dist/store/portable-bundle-import.js +10 -0
  454. package/dist/store/portable-bundle-import.js.map +1 -1
  455. package/dist/store/portable-bundle-scan.d.ts.map +1 -1
  456. package/dist/store/portable-bundle-scan.js +1 -0
  457. package/dist/store/portable-bundle-scan.js.map +1 -1
  458. package/dist/store/portable-bundle.d.ts.map +1 -1
  459. package/dist/store/portable-bundle.js +15 -14
  460. package/dist/store/portable-bundle.js.map +1 -1
  461. package/dist/store/pre-repair-snapshot.d.ts +4 -1
  462. package/dist/store/pre-repair-snapshot.d.ts.map +1 -1
  463. package/dist/store/pre-repair-snapshot.js +5 -1
  464. package/dist/store/pre-repair-snapshot.js.map +1 -1
  465. package/dist/store/regenerators.d.ts +4 -2
  466. package/dist/store/regenerators.d.ts.map +1 -1
  467. package/dist/store/regenerators.js +7 -3
  468. package/dist/store/regenerators.js.map +1 -1
  469. package/dist/store/repair-malformed-dbs.d.ts +4 -0
  470. package/dist/store/repair-malformed-dbs.d.ts.map +1 -1
  471. package/dist/store/repair-malformed-dbs.js +3 -0
  472. package/dist/store/repair-malformed-dbs.js.map +1 -1
  473. package/dist/store/safety-data-accessor.d.ts +8 -3
  474. package/dist/store/safety-data-accessor.d.ts.map +1 -1
  475. package/dist/store/safety-data-accessor.js +11 -4
  476. package/dist/store/safety-data-accessor.js.map +1 -1
  477. package/dist/store/schema/cleo-global/nexus.d.ts +48 -0
  478. package/dist/store/schema/cleo-global/nexus.d.ts.map +1 -1
  479. package/dist/store/schema/cleo-global/nexus.js +17 -0
  480. package/dist/store/schema/cleo-global/nexus.js.map +1 -1
  481. package/dist/store/schema/cleo-project/docs.d.ts +16 -0
  482. package/dist/store/schema/cleo-project/docs.d.ts.map +1 -1
  483. package/dist/store/schema/cleo-project/docs.js +10 -0
  484. package/dist/store/schema/cleo-project/docs.js.map +1 -1
  485. package/dist/store/schema/cleo-project/tasks-core.d.ts +80 -0
  486. package/dist/store/schema/cleo-project/tasks-core.d.ts.map +1 -1
  487. package/dist/store/schema/cleo-project/tasks-core.js +25 -1
  488. package/dist/store/schema/cleo-project/tasks-core.js.map +1 -1
  489. package/dist/store/schema/cleo-shared/brain.d.ts +1 -1
  490. package/dist/store/schema/memory-schema.d.ts +1 -1
  491. package/dist/store/schema/nexus-schema.d.ts +48 -0
  492. package/dist/store/schema/nexus-schema.d.ts.map +1 -1
  493. package/dist/store/schema/nexus-schema.js +28 -7
  494. package/dist/store/schema/nexus-schema.js.map +1 -1
  495. package/dist/store/session-store.d.ts +65 -0
  496. package/dist/store/session-store.d.ts.map +1 -1
  497. package/dist/store/session-store.js +108 -7
  498. package/dist/store/session-store.js.map +1 -1
  499. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  500. package/dist/store/sqlite-data-accessor.js +245 -47
  501. package/dist/store/sqlite-data-accessor.js.map +1 -1
  502. package/dist/store/sqlite.d.ts +15 -3
  503. package/dist/store/sqlite.d.ts.map +1 -1
  504. package/dist/store/sqlite.js +52 -20
  505. package/dist/store/sqlite.js.map +1 -1
  506. package/dist/store/store-write-guard.d.ts +9 -3
  507. package/dist/store/store-write-guard.d.ts.map +1 -1
  508. package/dist/store/store-write-guard.js +25 -4
  509. package/dist/store/store-write-guard.js.map +1 -1
  510. package/dist/store/table-classification.d.ts +1 -1
  511. package/dist/store/table-classification.d.ts.map +1 -1
  512. package/dist/store/table-classification.js +34 -8
  513. package/dist/store/table-classification.js.map +1 -1
  514. package/dist/store/task-claim.d.ts +144 -0
  515. package/dist/store/task-claim.d.ts.map +1 -0
  516. package/dist/store/task-claim.js +195 -0
  517. package/dist/store/task-claim.js.map +1 -0
  518. package/dist/store/task-version.d.ts +43 -5
  519. package/dist/store/task-version.d.ts.map +1 -1
  520. package/dist/store/task-version.js +113 -17
  521. package/dist/store/task-version.js.map +1 -1
  522. package/dist/store/tasks-schema.d.ts +2 -0
  523. package/dist/store/tasks-schema.d.ts.map +1 -1
  524. package/dist/store/tasks-schema.js +1 -0
  525. package/dist/store/tasks-schema.js.map +1 -1
  526. package/dist/store/tasks-sqlite.js +1 -1
  527. package/dist/store/tasks-sqlite.js.map +1 -1
  528. package/dist/store/twin-collapse.d.ts +339 -22
  529. package/dist/store/twin-collapse.d.ts.map +1 -1
  530. package/dist/store/twin-collapse.js +1323 -48
  531. package/dist/store/twin-collapse.js.map +1 -1
  532. package/dist/store/umbrella-data-accessor.d.ts +8 -3
  533. package/dist/store/umbrella-data-accessor.d.ts.map +1 -1
  534. package/dist/store/umbrella-data-accessor.js +10 -4
  535. package/dist/store/umbrella-data-accessor.js.map +1 -1
  536. package/dist/store/validation-schemas.d.ts +161 -1
  537. package/dist/store/validation-schemas.d.ts.map +1 -1
  538. package/dist/store/with-retry.d.ts +36 -0
  539. package/dist/store/with-retry.d.ts.map +1 -1
  540. package/dist/store/with-retry.js +51 -0
  541. package/dist/store/with-retry.js.map +1 -1
  542. package/dist/store/worktree-build-guard.d.ts +13 -4
  543. package/dist/store/worktree-build-guard.d.ts.map +1 -1
  544. package/dist/store/worktree-build-guard.js +21 -10
  545. package/dist/store/worktree-build-guard.js.map +1 -1
  546. package/dist/store/worktree-isolation-guard.d.ts +121 -10
  547. package/dist/store/worktree-isolation-guard.d.ts.map +1 -1
  548. package/dist/store/worktree-isolation-guard.js +155 -34
  549. package/dist/store/worktree-isolation-guard.js.map +1 -1
  550. package/dist/system/backup.d.ts +7 -0
  551. package/dist/system/backup.d.ts.map +1 -1
  552. package/dist/system/backup.js +5 -0
  553. package/dist/system/backup.js.map +1 -1
  554. package/dist/system/health.d.ts.map +1 -1
  555. package/dist/system/health.js +3 -1
  556. package/dist/system/health.js.map +1 -1
  557. package/dist/system/inject-generate.d.ts.map +1 -1
  558. package/dist/system/inject-generate.js +23 -10
  559. package/dist/system/inject-generate.js.map +1 -1
  560. package/dist/system/project-health.d.ts +8 -0
  561. package/dist/system/project-health.d.ts.map +1 -1
  562. package/dist/system/project-health.js +13 -5
  563. package/dist/system/project-health.js.map +1 -1
  564. package/dist/task-tools/index.d.ts +1 -1
  565. package/dist/task-tools/index.d.ts.map +1 -1
  566. package/dist/task-tools/index.js +1 -1
  567. package/dist/task-tools/index.js.map +1 -1
  568. package/dist/task-tools/score-task-priority.d.ts +38 -0
  569. package/dist/task-tools/score-task-priority.d.ts.map +1 -1
  570. package/dist/task-tools/score-task-priority.js +51 -0
  571. package/dist/task-tools/score-task-priority.js.map +1 -1
  572. package/dist/task-work/claims.d.ts +215 -0
  573. package/dist/task-work/claims.d.ts.map +1 -0
  574. package/dist/task-work/claims.js +272 -0
  575. package/dist/task-work/claims.js.map +1 -0
  576. package/dist/task-work/index.d.ts +47 -3
  577. package/dist/task-work/index.d.ts.map +1 -1
  578. package/dist/task-work/index.js +74 -41
  579. package/dist/task-work/index.js.map +1 -1
  580. package/dist/tasks/add.d.ts.map +1 -1
  581. package/dist/tasks/add.js +3 -2
  582. package/dist/tasks/add.js.map +1 -1
  583. package/dist/tasks/analyze.d.ts.map +1 -1
  584. package/dist/tasks/analyze.js +3 -4
  585. package/dist/tasks/analyze.js.map +1 -1
  586. package/dist/tasks/change-set.d.ts +5 -0
  587. package/dist/tasks/change-set.d.ts.map +1 -1
  588. package/dist/tasks/change-set.js +59 -3
  589. package/dist/tasks/change-set.js.map +1 -1
  590. package/dist/tasks/complete.d.ts +16 -0
  591. package/dist/tasks/complete.d.ts.map +1 -1
  592. package/dist/tasks/complete.js +27 -7
  593. package/dist/tasks/complete.js.map +1 -1
  594. package/dist/tasks/component-pr.d.ts +162 -1
  595. package/dist/tasks/component-pr.d.ts.map +1 -1
  596. package/dist/tasks/component-pr.js +560 -59
  597. package/dist/tasks/component-pr.js.map +1 -1
  598. package/dist/tasks/done-record.d.ts +14 -1
  599. package/dist/tasks/done-record.d.ts.map +1 -1
  600. package/dist/tasks/done-record.js +37 -0
  601. package/dist/tasks/done-record.js.map +1 -1
  602. package/dist/tasks/duplicate-detector.d.ts +20 -1
  603. package/dist/tasks/duplicate-detector.d.ts.map +1 -1
  604. package/dist/tasks/duplicate-detector.js +10 -2
  605. package/dist/tasks/duplicate-detector.js.map +1 -1
  606. package/dist/tasks/engine-wrap-ops.d.ts +19 -10
  607. package/dist/tasks/engine-wrap-ops.d.ts.map +1 -1
  608. package/dist/tasks/engine-wrap-ops.js +38 -10
  609. package/dist/tasks/engine-wrap-ops.js.map +1 -1
  610. package/dist/tasks/graph-ops.d.ts +9 -2
  611. package/dist/tasks/graph-ops.d.ts.map +1 -1
  612. package/dist/tasks/graph-ops.js +24 -32
  613. package/dist/tasks/graph-ops.js.map +1 -1
  614. package/dist/tasks/plan.d.ts.map +1 -1
  615. package/dist/tasks/plan.js +16 -60
  616. package/dist/tasks/plan.js.map +1 -1
  617. package/dist/tasks/task-context.d.ts.map +1 -1
  618. package/dist/tasks/task-context.js +13 -0
  619. package/dist/tasks/task-context.js.map +1 -1
  620. package/dist/tasks/task-import.d.ts.map +1 -1
  621. package/dist/tasks/task-import.js +21 -12
  622. package/dist/tasks/task-import.js.map +1 -1
  623. package/dist/tasks/task-next.d.ts +25 -2
  624. package/dist/tasks/task-next.d.ts.map +1 -1
  625. package/dist/tasks/task-next.js +32 -13
  626. package/dist/tasks/task-next.js.map +1 -1
  627. package/dist/tasks/update.d.ts.map +1 -1
  628. package/dist/tasks/update.js +7 -5
  629. package/dist/tasks/update.js.map +1 -1
  630. package/dist/upgrade.d.ts +1 -1
  631. package/dist/upgrade.d.ts.map +1 -1
  632. package/dist/upgrade.js +48 -9
  633. package/dist/upgrade.js.map +1 -1
  634. package/dist/validation/doctor/checks.d.ts +20 -6
  635. package/dist/validation/doctor/checks.d.ts.map +1 -1
  636. package/dist/validation/doctor/checks.js +51 -8
  637. package/dist/validation/doctor/checks.js.map +1 -1
  638. package/dist/validation/validate-ops.d.ts.map +1 -1
  639. package/dist/validation/validate-ops.js +2 -3
  640. package/dist/validation/validate-ops.js.map +1 -1
  641. package/migrations/drizzle-cleo-global/20260929010000_t12512-registry-probed-opened/migration.sql +35 -0
  642. package/migrations/drizzle-cleo-global/20260929020000_t12721-git-state-head-committed-at/migration.sql +25 -0
  643. package/migrations/drizzle-cleo-project/20260929000000_t12535-docs-attachments-union-shape/migration.sql +21 -0
  644. package/migrations/drizzle-cleo-project/20260929120000_t12502-task-claim-leases/migration.sql +77 -0
  645. package/migrations/drizzle-cleo-project/20260929130000_t12736-claim-lease-iso-repair/migration.sql +37 -0
  646. package/package.json +23 -13
  647. package/schemas/project-info.schema.json +10 -4
  648. package/templates/CLEO-INJECTION.md +1 -1
  649. package/templates/CLEO-REFERENCE.md +3 -3
  650. package/templates/cleo-gitignore +11 -0
  651. package/templates/workflows/release-prepare.yml.tmpl +156 -10
@@ -29,19 +29,56 @@
29
29
  * audit: nothing here logs, and every summary this module returns carries the
30
30
  * masked preview only.
31
31
  *
32
+ * ## Profiles (T12733)
33
+ *
34
+ * The store holds any number of profiles, several per provider (accounts,
35
+ * each with its own key), addressed as `<provider>/<name>` (e.g.
36
+ * `layahost/work`, `layahost/personal`, `jev/team`), and exactly one ACTIVE
37
+ * profile, which everyday decisions use. The format stays additive on
38
+ * version 1:
39
+ *
40
+ * ```json
41
+ * { "version": 1,
42
+ * "provider": "layahost", "baseUrl": "https://layahost.com", "apiKey": "…",
43
+ * "model": "laya-auto", "updatedAt": "…",
44
+ * "active": "layahost/work",
45
+ * "profiles": {
46
+ * "layahost/work": { "provider": "layahost", "name": "work", "baseUrl": "default", "apiKey": "…", "model": "laya-auto" },
47
+ * "jev/team": { "provider": "jev", "name": "team", "baseUrl": "https://jev.internal", "apiKey": "…", "model": "…" } } }
48
+ * ```
49
+ *
50
+ * - A profile's `baseUrl` is the literal `default` (its provider's preset
51
+ * URL, resolved at call time, so a changed preset flows through) or an
52
+ * override URL.
53
+ * - The top-level single-config fields ARE the active profile with its URL
54
+ * resolved, so a CLEO that predates profiles (9.23) reads the active profile
55
+ * as its one config. Every write keeps them in sync.
56
+ * - A file without `profiles` (written before T12733) is one active profile,
57
+ * `<provider>/default`.
58
+ * - An older CLEO that rewrites the file drops `profiles` and `active` (its
59
+ * zod object strips unknown keys and its save replaces the file). When the
60
+ * top-level settings then differ from the active profile, the TOP-LEVEL
61
+ * settings are authoritative: they replace the active profile (or, when the
62
+ * provider changed, become `<new provider>/<active name>`), and
63
+ * {@link listDecideProfiles} reports `reconciled: true`. Profiles dropped
64
+ * by such a rewrite cannot be recovered and must be added again. Empty
65
+ * top-level settings beside a stored active profile mean the older CLEO
66
+ * cleared them: no profile is active.
67
+ *
32
68
  * @task T12491
33
69
  * @task T12713
70
+ * @task T12733
34
71
  * @epic T12486
35
72
  */
36
73
  import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from 'node:fs';
37
74
  import { dirname, join } from 'node:path';
38
75
  import { inspect } from 'node:util';
39
- import { decisionProviderConfigSchema, decisionProviderKindSchema, } from '@cleocode/contracts';
76
+ import { DECIDE_DEFAULT_PROFILE_NAME, DECIDE_PROFILE_DEFAULT_URL, DECIDE_PROFILE_NAME_PATTERN, DECISION_PROVIDER_KINDS, decisionProviderConfigSchema, decisionProviderKindSchema, } from '@cleocode/contracts';
40
77
  import { getCleoHome } from '@cleocode/paths';
41
78
  import { z } from 'zod';
42
79
  import { withLock } from '../store/file-utils.js';
43
80
  import { isValidDecisionModelName } from './jev-wire.js';
44
- import { inferDecisionProviderKind } from './providers.js';
81
+ import { inferDecisionProviderKind, presetBaseUrl } from './providers.js';
45
82
  /** File name of the store, directly under the CLEO home. */
46
83
  export const DECIDE_CREDENTIALS_FILE = 'decide-credentials.json';
47
84
  /**
@@ -50,7 +87,21 @@ export const DECIDE_CREDENTIALS_FILE = 'decide-credentials.json';
50
87
  * silently disable System One after a downgrade.
51
88
  */
52
89
  export const DECIDE_CREDENTIALS_VERSION = 1;
53
- /** On-disk shape of the store. `null` settings mean "not configured". */
90
+ /** On-disk shape of one profile (T12733). `baseUrl` is `default` or an override URL. */
91
+ const profileSchema = z.object({
92
+ provider: decisionProviderKindSchema,
93
+ name: z.string(),
94
+ baseUrl: z.string(),
95
+ apiKey: z.string(),
96
+ model: z.string().nullable().optional(),
97
+ updatedAt: z.string().optional(),
98
+ });
99
+ /**
100
+ * On-disk shape of the store. `null` settings mean "not configured". The
101
+ * top-level settings are the active profile with its URL resolved (T12733);
102
+ * `profiles` and `active` are additive, and a malformed value for either is
103
+ * ignored rather than failing the whole file.
104
+ */
54
105
  const storeSchema = z.object({
55
106
  version: z.literal(DECIDE_CREDENTIALS_VERSION),
56
107
  /** T12713, optional: absent in files written before provider kinds existed. */
@@ -59,6 +110,10 @@ const storeSchema = z.object({
59
110
  apiKey: z.string().nullable(),
60
111
  model: z.string().nullable().optional(),
61
112
  updatedAt: z.string().optional(),
113
+ /** T12733, optional: every profile, keyed by `<provider>/<name>`. Entries are validated one by one. */
114
+ profiles: z.record(z.string(), z.json()).optional().catch(undefined),
115
+ /** T12733, optional: the id of the active profile. */
116
+ active: z.string().nullable().optional().catch(undefined),
62
117
  });
63
118
  const EMPTY_STORE = {
64
119
  version: DECIDE_CREDENTIALS_VERSION,
@@ -75,6 +130,155 @@ function parseStore(value) {
75
130
  return store;
76
131
  return { ...store, provider: inferDecisionProviderKind(store.baseUrl) };
77
132
  }
133
+ /**
134
+ * Whether `name` is a valid profile name: 1-32 characters of `a-z`, `0-9`
135
+ * and `-`, starting and ending with a letter or digit.
136
+ *
137
+ * @param name - Candidate name (the part after `<provider>/`).
138
+ * @returns True when the name may be stored.
139
+ */
140
+ export function isValidDecideProfileName(name) {
141
+ return DECIDE_PROFILE_NAME_PATTERN.test(name);
142
+ }
143
+ /**
144
+ * Build a profile id.
145
+ *
146
+ * @param provider - Provider kind.
147
+ * @param name - Profile name.
148
+ * @returns `<provider>/<name>`.
149
+ */
150
+ export function decideProfileId(provider, name) {
151
+ return `${provider}/${name}`;
152
+ }
153
+ /**
154
+ * Parse a profile reference: `<provider>/<name>` (e.g. `layahost/work`), or a
155
+ * bare provider kind meaning `<provider>/default`.
156
+ *
157
+ * @param ref - Reference as typed by the user.
158
+ * @returns The parsed reference, or `null` when it names no valid profile.
159
+ */
160
+ export function parseDecideProfileRef(ref) {
161
+ const parts = ref.trim().split('/');
162
+ const provider = DECISION_PROVIDER_KINDS.find((kind) => kind === parts[0]);
163
+ if (!provider || parts.length > 2)
164
+ return null;
165
+ const name = parts.length === 2 ? (parts[1] ?? '') : DECIDE_DEFAULT_PROFILE_NAME;
166
+ if (!isValidDecideProfileName(name))
167
+ return null;
168
+ return { provider, name, id: decideProfileId(provider, name) };
169
+ }
170
+ /** A profile's URL resolved: its override, or its provider's preset for `default`. */
171
+ function resolveProfileUrl(profile) {
172
+ return profile.baseUrl === DECIDE_PROFILE_DEFAULT_URL
173
+ ? presetBaseUrl(profile.provider)
174
+ : profile.baseUrl;
175
+ }
176
+ /** Strip trailing slashes for URL comparison. */
177
+ function normalizeUrl(url) {
178
+ return url.trim().replace(/\/+$/, '');
179
+ }
180
+ /** The stored form of a URL: `default` when it is the provider's preset URL, else the URL. */
181
+ function urlSetting(provider, url) {
182
+ const preset = presetBaseUrl(provider);
183
+ return preset && normalizeUrl(url) === normalizeUrl(preset) ? DECIDE_PROFILE_DEFAULT_URL : url;
184
+ }
185
+ /** The top-level settings, or `null` when no URL + key is stored. */
186
+ function topLevelSettings(store) {
187
+ const baseUrl = store.baseUrl?.trim();
188
+ const apiKey = store.apiKey?.trim();
189
+ if (!baseUrl || !apiKey)
190
+ return null;
191
+ return {
192
+ provider: store.provider ?? inferDecisionProviderKind(baseUrl),
193
+ baseUrl,
194
+ apiKey,
195
+ model: store.model?.trim() || null,
196
+ ...(store.updatedAt ? { updatedAt: store.updatedAt } : {}),
197
+ };
198
+ }
199
+ /** Whether a profile holds the same settings as the top level (timestamps ignored). */
200
+ function matchesTopLevel(profile, top) {
201
+ const url = resolveProfileUrl(profile);
202
+ return (profile.provider === top.provider &&
203
+ url !== undefined &&
204
+ normalizeUrl(url) === normalizeUrl(top.baseUrl) &&
205
+ profile.apiKey.trim() === top.apiKey &&
206
+ (profile.model?.trim() || null) === top.model);
207
+ }
208
+ /** Keep only well-formed profiles whose key matches their provider and name. */
209
+ function validProfiles(raw) {
210
+ const profiles = {};
211
+ for (const [id, value] of Object.entries(raw ?? {})) {
212
+ const parsed = profileSchema.safeParse(value);
213
+ if (!parsed.success)
214
+ continue;
215
+ const ref = parseDecideProfileRef(id);
216
+ if (ref?.id !== id || ref.provider !== parsed.data.provider || ref.name !== parsed.data.name) {
217
+ continue;
218
+ }
219
+ profiles[id] = parsed.data;
220
+ }
221
+ return profiles;
222
+ }
223
+ /**
224
+ * View the parsed store as profiles. The top-level settings are
225
+ * authoritative for the active profile (see the module doc): when they
226
+ * differ from it, they replace it (same provider) or become
227
+ * `<their provider>/<active name>` (an older CLEO switched provider).
228
+ */
229
+ function toProfileStore(store) {
230
+ const profiles = validProfiles(store.profiles);
231
+ const hadProfiles = Object.keys(profiles).length > 0;
232
+ const activeRef = store.active ? parseDecideProfileRef(store.active) : null;
233
+ const top = topLevelSettings(store);
234
+ if (!top) {
235
+ // An older CLEO cleared the settings (or nothing was ever configured).
236
+ return { profiles, active: null, reconciled: activeRef !== null && activeRef.id in profiles };
237
+ }
238
+ const name = activeRef?.name ?? DECIDE_DEFAULT_PROFILE_NAME;
239
+ const id = activeRef?.provider === top.provider ? activeRef.id : decideProfileId(top.provider, name);
240
+ const existing = profiles[id];
241
+ const inSync = existing !== undefined && matchesTopLevel(existing, top);
242
+ if (!inSync) {
243
+ profiles[id] = {
244
+ provider: top.provider,
245
+ name: id.slice(top.provider.length + 1),
246
+ baseUrl: urlSetting(top.provider, top.baseUrl),
247
+ apiKey: top.apiKey,
248
+ model: top.model,
249
+ ...(top.updatedAt ? { updatedAt: top.updatedAt } : {}),
250
+ };
251
+ }
252
+ const reconciled = (activeRef !== null || hadProfiles) && !(inSync && id === activeRef?.id);
253
+ return { profiles, active: id, reconciled };
254
+ }
255
+ /** The empty store as written. */
256
+ const EMPTY_WRITTEN = {
257
+ version: DECIDE_CREDENTIALS_VERSION,
258
+ baseUrl: null,
259
+ apiKey: null,
260
+ };
261
+ /** Render profiles back to the on-disk shape (top-level = active profile, URL resolved). */
262
+ function fromProfileStore(ps) {
263
+ const ids = Object.keys(ps.profiles).sort();
264
+ if (ids.length === 0)
265
+ return EMPTY_WRITTEN;
266
+ const active = ps.active ? ps.profiles[ps.active] : undefined;
267
+ const activeUrl = active ? resolveProfileUrl(active) : undefined;
268
+ return {
269
+ version: DECIDE_CREDENTIALS_VERSION,
270
+ ...(active && activeUrl ? { provider: active.provider } : {}),
271
+ baseUrl: active && activeUrl ? activeUrl : null,
272
+ apiKey: active && activeUrl ? active.apiKey : null,
273
+ model: (active && activeUrl ? active.model : null) ?? null,
274
+ updatedAt: new Date().toISOString(),
275
+ ...(ps.active && activeUrl ? { active: ps.active } : {}),
276
+ profiles: Object.fromEntries(ids.flatMap((id) => {
277
+ const profile = ps.profiles[id];
278
+ return profile ? [[id, profile]] : [];
279
+ })),
280
+ };
281
+ }
78
282
  /**
79
283
  * Absolute path of the decision-credential store.
80
284
  *
@@ -106,31 +310,37 @@ export class SealedDecideConnection {
106
310
  model;
107
311
  /** Masked key preview (`…abcd`). */
108
312
  keyPreview;
313
+ /** Profile id (`<provider>/<name>`, T12733), when resolved from a profile. */
314
+ profile;
109
315
  #apiKey;
110
316
  /**
111
317
  * @param baseUrl - Provider base URL.
112
318
  * @param apiKey - Plaintext key; captured privately.
113
319
  * @param model - Optional default model.
114
320
  * @param provider - Provider kind. Default `jev`.
321
+ * @param profile - Profile id, when known.
115
322
  */
116
- constructor(baseUrl, apiKey, model, provider = 'jev') {
323
+ constructor(baseUrl, apiKey, model, provider = 'jev', profile) {
117
324
  this.provider = provider;
118
325
  this.baseUrl = baseUrl;
119
326
  this.#apiKey = apiKey;
120
327
  this.keyPreview = maskApiKey(apiKey);
121
328
  if (model)
122
329
  this.model = model;
330
+ if (profile)
331
+ this.profile = profile;
123
332
  }
124
333
  /**
125
334
  * Materialise the wire connection. Call only where the request is built.
126
335
  *
127
- * @returns Base URL, plaintext key and optional model.
336
+ * @returns Base URL, plaintext key, optional model and the profile id when known.
128
337
  */
129
338
  connection() {
130
339
  return {
131
340
  baseUrl: this.baseUrl,
132
341
  apiKey: this.#apiKey,
133
342
  ...(this.model ? { model: this.model } : {}),
343
+ ...(this.profile ? { profile: this.profile } : {}),
134
344
  };
135
345
  }
136
346
  /** @returns The secret-free JSON form. */
@@ -140,6 +350,7 @@ export class SealedDecideConnection {
140
350
  baseUrl: this.baseUrl,
141
351
  ...(this.model ? { model: this.model } : {}),
142
352
  keyPreview: this.keyPreview,
353
+ ...(this.profile ? { profile: this.profile } : {}),
143
354
  };
144
355
  }
145
356
  /** @returns A secret-free string form. */
@@ -223,24 +434,142 @@ export function sameDecideHost(a, b) {
223
434
  return false;
224
435
  }
225
436
  }
437
+ /** Seal settings, or `null` when the URL or key is unusable. */
438
+ function sealSettings(provider, baseUrl, apiKey, model, profile) {
439
+ const url = baseUrl?.trim();
440
+ const key = apiKey.trim();
441
+ if (!url || !key || !isAllowedDecideBaseUrl(url))
442
+ return null;
443
+ const m = model?.trim();
444
+ return new SealedDecideConnection(url, key, m && isValidDecisionModelName(m) ? m : undefined, provider, profile ?? undefined);
445
+ }
446
+ /** Seal a stored profile (URL resolved), or `null` when unusable. */
447
+ function sealProfile(profile) {
448
+ if (!profile)
449
+ return null;
450
+ return sealSettings(profile.provider, resolveProfileUrl(profile), profile.apiKey, profile.model, decideProfileId(profile.provider, profile.name));
451
+ }
452
+ /** Read the store as profiles. Never throws. */
453
+ function readProfileStoreSync(path = decideCredentialsPath()) {
454
+ return toProfileStore(readStoreSync(path));
455
+ }
456
+ /** Human rule for profile references, used in error messages. */
457
+ const PROFILE_REF_RULE = `(use <provider>/<name>: provider ${DECISION_PROVIDER_KINDS.join('|')}, name of 1-32 lowercase letters, digits or -, e.g. layahost/work)`;
458
+ /** ` (profiles: a, b)`, or ` (no profiles are stored)`. */
459
+ function knownIds(ps) {
460
+ const ids = Object.keys(ps.profiles).sort();
461
+ return ids.length ? ` (profiles: ${ids.join(', ')})` : ' (no profiles are stored)';
462
+ }
463
+ /**
464
+ * Parse a profile reference or throw.
465
+ *
466
+ * @throws {DecideCredentialsError} When the reference names no valid profile.
467
+ */
468
+ function requireProfileRef(ref) {
469
+ const parsed = parseDecideProfileRef(ref);
470
+ if (!parsed)
471
+ throw new DecideCredentialsError(`invalid profile '${ref}' ${PROFILE_REF_RULE}`);
472
+ return parsed;
473
+ }
226
474
  /**
227
- * Load the stored connection. Synchronous and total: missing, malformed or
228
- * incomplete settings return `null` (the decision client then falls back to
229
- * heuristics).
475
+ * Load the ACTIVE profile's connection: what everyday decisions use. Reads
476
+ * the top-level settings, which are the active profile with its URL
477
+ * resolved. Synchronous and total: missing, malformed or incomplete
478
+ * settings return `null` (the decision client then falls back to heuristics).
230
479
  *
231
480
  * @returns The sealed connection, or `null` when unconfigured.
232
481
  */
233
482
  export function loadDecideConnection() {
234
483
  const store = readStoreSync();
235
- const baseUrl = store.baseUrl?.trim();
236
- const apiKey = store.apiKey?.trim();
237
- if (!baseUrl || !apiKey || !isAllowedDecideBaseUrl(baseUrl))
484
+ const top = topLevelSettings(store);
485
+ if (!top)
238
486
  return null;
239
- const model = store.model?.trim();
240
- return new SealedDecideConnection(baseUrl, apiKey, model && isValidDecisionModelName(model) ? model : undefined, store.provider ?? 'jev');
487
+ return sealSettings(top.provider, top.baseUrl, top.apiKey, top.model, toProfileStore(store).active);
241
488
  }
242
489
  /**
243
- * Describe the stored settings without the key.
490
+ * Load a profile's connection without changing the active profile. Total:
491
+ * an invalid or unknown reference, or unusable settings, return `null`.
492
+ *
493
+ * @param ref - `<provider>/<name>`, or a bare provider for `<provider>/default`.
494
+ * @returns The sealed connection, or `null`.
495
+ */
496
+ export function loadDecideProfile(ref) {
497
+ const parsed = parseDecideProfileRef(ref);
498
+ return parsed ? sealProfile(readProfileStoreSync().profiles[parsed.id]) : null;
499
+ }
500
+ /**
501
+ * Resolve a profile to its connection, for callers (the System One
502
+ * benchmark) that address a profile by name without switching the active
503
+ * one. A `default` URL is resolved to the provider's current preset. The
504
+ * result CONTAINS THE PLAINTEXT KEY: pass it to the wire, never log or emit
505
+ * it. For a secret-safe handle use {@link loadDecideProfile}.
506
+ *
507
+ * @param ref - `<provider>/<name>` (e.g. `layahost/work`), or a bare provider for `<provider>/default`.
508
+ * @returns `{ name, profile, provider, baseUrl, apiKey, model }`, `name` and `profile` being the id.
509
+ * @throws {DecideCredentialsError} When the reference is invalid or unknown,
510
+ * or the profile's settings are unusable. The message lists the stored ids.
511
+ */
512
+ export function resolveDecideProfile(ref) {
513
+ const { id } = requireProfileRef(ref);
514
+ const ps = readProfileStoreSync();
515
+ const profile = ps.profiles[id];
516
+ if (!profile) {
517
+ throw new DecideCredentialsError(`no System One profile '${id}'${knownIds(ps)}`);
518
+ }
519
+ const sealed = sealProfile(profile);
520
+ if (!sealed) {
521
+ throw new DecideCredentialsError(`System One profile '${id}' has an unusable URL or key; store it again with: cleo decide config --profile ${id}`);
522
+ }
523
+ const wire = sealed.connection();
524
+ return {
525
+ name: id,
526
+ profile: id,
527
+ provider: sealed.provider,
528
+ baseUrl: wire.baseUrl,
529
+ apiKey: wire.apiKey,
530
+ ...(wire.model ? { model: wire.model } : {}),
531
+ };
532
+ }
533
+ /** Secret-free summary of one stored profile. */
534
+ function summarizeProfile(id, profile, active) {
535
+ const model = profile.model?.trim();
536
+ return {
537
+ id,
538
+ name: profile.name,
539
+ active: id === active,
540
+ configured: sealProfile(profile) !== null,
541
+ provider: profile.provider,
542
+ baseUrl: withoutUserinfo(resolveProfileUrl(profile) ?? ''),
543
+ urlSource: profile.baseUrl === DECIDE_PROFILE_DEFAULT_URL ? 'default' : 'override',
544
+ ...(model ? { model } : {}),
545
+ keyPreview: maskApiKey(profile.apiKey),
546
+ ...(profile.updatedAt ? { updatedAt: profile.updatedAt } : {}),
547
+ };
548
+ }
549
+ /**
550
+ * List every stored profile, the active one marked, keys masked. The
551
+ * benchmark uses it to enumerate the profiles it can call by id.
552
+ *
553
+ * @returns The profiles sorted by id, the active id, and whether the
554
+ * top-level settings overrode a stale active entry (an older CLEO rewrote the file).
555
+ */
556
+ export function listDecideProfiles() {
557
+ const path = decideCredentialsPath();
558
+ const ps = readProfileStoreSync(path);
559
+ return {
560
+ path,
561
+ active: ps.active,
562
+ profiles: Object.keys(ps.profiles)
563
+ .sort()
564
+ .flatMap((id) => {
565
+ const profile = ps.profiles[id];
566
+ return profile ? [summarizeProfile(id, profile, ps.active)] : [];
567
+ }),
568
+ reconciled: ps.reconciled,
569
+ };
570
+ }
571
+ /**
572
+ * Describe the ACTIVE profile's settings without the key.
244
573
  *
245
574
  * @returns A secret-free summary.
246
575
  */
@@ -248,6 +577,7 @@ export function describeDecideCredentials() {
248
577
  const path = decideCredentialsPath();
249
578
  const store = readStoreSync(path);
250
579
  const sealed = loadDecideConnection();
580
+ const active = toProfileStore(store).active;
251
581
  return {
252
582
  configured: sealed !== null,
253
583
  path,
@@ -256,6 +586,35 @@ export function describeDecideCredentials() {
256
586
  ...(store.model ? { model: store.model } : {}),
257
587
  ...(store.apiKey ? { keyPreview: maskApiKey(store.apiKey) } : {}),
258
588
  ...(store.updatedAt ? { updatedAt: store.updatedAt } : {}),
589
+ ...(active ? { profile: active, activeProfile: active } : {}),
590
+ };
591
+ }
592
+ /**
593
+ * Describe one profile's settings without the key.
594
+ *
595
+ * @param ref - `<provider>/<name>`, or a bare provider for `<provider>/default`.
596
+ * @returns A secret-free summary; `configured: false` when the profile is unknown.
597
+ */
598
+ export function describeDecideProfile(ref) {
599
+ const path = decideCredentialsPath();
600
+ const ps = readProfileStoreSync(path);
601
+ const parsed = parseDecideProfileRef(ref);
602
+ const profile = parsed ? ps.profiles[parsed.id] : undefined;
603
+ const activeProfile = ps.active ? { activeProfile: ps.active } : {};
604
+ if (!parsed || !profile)
605
+ return { configured: false, path, ...activeProfile };
606
+ const summary = summarizeProfile(parsed.id, profile, ps.active);
607
+ return {
608
+ configured: summary.configured,
609
+ path,
610
+ provider: summary.provider,
611
+ baseUrl: summary.baseUrl,
612
+ ...(summary.model ? { model: summary.model } : {}),
613
+ keyPreview: summary.keyPreview,
614
+ ...(summary.updatedAt ? { updatedAt: summary.updatedAt } : {}),
615
+ profile: parsed.id,
616
+ urlSource: summary.urlSource,
617
+ ...activeProfile,
259
618
  };
260
619
  }
261
620
  /** Error thrown by {@link saveDecideCredentials} for invalid input. The message never contains the key. */
@@ -340,23 +699,72 @@ function ensureStoreFile(path) {
340
699
  writeFileSync(path, `${JSON.stringify(EMPTY_STORE)}\n`, { encoding: 'utf-8', mode: 0o600 });
341
700
  }
342
701
  /**
343
- * Store the provider kind, base URL, API key and optional model (0600, locked,
344
- * atomic). Writes schema version 1 with the optional `provider` field.
702
+ * Apply `change` to the profiles under the store lock and write the result
703
+ * (0600, atomic), keeping the top-level settings equal to the active
704
+ * profile. `change` may throw a {@link DecideCredentialsError} to abort
705
+ * without writing.
706
+ */
707
+ async function mutateProfiles(change) {
708
+ const path = decideCredentialsPath();
709
+ guardWrite(path);
710
+ ensureStoreFile(path);
711
+ await withLock(path, (current) => fromProfileStore(change(toProfileStore(parseStore(current) ?? EMPTY_STORE))), { mode: 0o600 });
712
+ }
713
+ /**
714
+ * The profile a save targets: `input.profile` as `<provider>/<name>` or a
715
+ * bare name, with the provider from `input.provider`, the reference, or the
716
+ * URL.
717
+ */
718
+ function saveTarget(input, baseUrl) {
719
+ const raw = input.profile?.trim();
720
+ if (raw?.includes('/')) {
721
+ const ref = requireProfileRef(raw);
722
+ if (input.provider && input.provider !== ref.provider) {
723
+ throw new DecideCredentialsError(`profile '${ref.id}' belongs to ${ref.provider}, not ${input.provider}`);
724
+ }
725
+ return ref;
726
+ }
727
+ const provider = input.provider ??
728
+ (baseUrl === DECIDE_PROFILE_DEFAULT_URL ? 'layahost' : inferDecisionProviderKind(baseUrl));
729
+ const name = raw || DECIDE_DEFAULT_PROFILE_NAME;
730
+ if (!isValidDecideProfileName(name)) {
731
+ throw new DecideCredentialsError(`invalid profile name '${name}' ${PROFILE_REF_RULE}`);
732
+ }
733
+ return { provider, name, id: decideProfileId(provider, name) };
734
+ }
735
+ /**
736
+ * Store a profile: provider kind, base URL (`default` or an override), API
737
+ * key and optional model (0600, locked, atomic). Adds the profile or
738
+ * replaces that profile's settings; every other profile is kept. A URL equal
739
+ * to the provider's preset is stored as `default`, so a changed preset flows
740
+ * through. Writes schema version 1 with the active profile mirrored at the
741
+ * top level, URL resolved.
345
742
  *
346
- * @param input - Settings to store; replaces any previous settings.
347
- * @returns Secret-free summary of what is now stored.
348
- * @throws {DecideCredentialsError} When the URL is not https (or loopback http), the key is
349
- * blank, the model name is invalid, or the store or its backups are symlinks.
743
+ * @param input - Settings to store, the profile and whether to activate it.
744
+ * @returns Secret-free summary of the profile now stored.
745
+ * @throws {DecideCredentialsError} When the URL is not https (or loopback http), `default`
746
+ * names a provider without a preset URL, the key is blank, the model or profile is
747
+ * invalid, or the store or its backups are symlinks.
350
748
  */
351
749
  export async function saveDecideCredentials(input) {
352
- const baseUrl = input.baseUrl.trim();
750
+ const rawUrl = input.baseUrl.trim();
353
751
  const apiKey = input.apiKey.trim();
354
752
  const model = input.model?.trim();
355
- if (hasUserinfo(baseUrl)) {
356
- throw new DecideCredentialsError('base URL must not contain a username or password (user:pass@host); supply only the URL and the API key');
753
+ const target = saveTarget(input, rawUrl);
754
+ let baseUrl = DECIDE_PROFILE_DEFAULT_URL;
755
+ if (rawUrl === DECIDE_PROFILE_DEFAULT_URL) {
756
+ if (!presetBaseUrl(target.provider)) {
757
+ throw new DecideCredentialsError(`the ${target.provider} provider has no default URL; pass --url https://your-provider.example`);
758
+ }
357
759
  }
358
- if (!isAllowedDecideBaseUrl(baseUrl)) {
359
- throw new DecideCredentialsError('base URL must be an absolute https:// URL (plain http:// is allowed only for localhost, 127.0.0.1 and ::1)');
760
+ else {
761
+ if (hasUserinfo(rawUrl)) {
762
+ throw new DecideCredentialsError('base URL must not contain a username or password (user:pass@host); supply only the URL and the API key');
763
+ }
764
+ if (!isAllowedDecideBaseUrl(rawUrl)) {
765
+ throw new DecideCredentialsError('base URL must be an absolute https:// URL (plain http:// is allowed only for localhost, 127.0.0.1 and ::1)');
766
+ }
767
+ baseUrl = urlSetting(target.provider, rawUrl);
360
768
  }
361
769
  if (model && !isValidDecisionModelName(model)) {
362
770
  throw new DecideCredentialsError('model name may contain only letters, digits and . _ : / @ - (1-128 characters)');
@@ -365,33 +773,89 @@ export async function saveDecideCredentials(input) {
365
773
  throw new DecideCredentialsError('API key must not be empty');
366
774
  if (/\s/.test(apiKey))
367
775
  throw new DecideCredentialsError('API key must not contain whitespace');
368
- const path = decideCredentialsPath();
369
- guardWrite(path);
370
- ensureStoreFile(path);
371
- await withLock(path, () => ({
372
- version: DECIDE_CREDENTIALS_VERSION,
373
- provider: input.provider ?? 'jev',
374
- baseUrl,
375
- apiKey,
376
- model: model || null,
377
- updatedAt: new Date().toISOString(),
378
- }), { mode: 0o600 });
379
- return describeDecideCredentials();
776
+ await mutateProfiles((ps) => {
777
+ const activate = input.activate ?? (ps.active === null || ps.active === target.id);
778
+ const profile = {
779
+ provider: target.provider,
780
+ name: target.name,
781
+ baseUrl,
782
+ apiKey,
783
+ model: model || null,
784
+ updatedAt: new Date().toISOString(),
785
+ };
786
+ return {
787
+ profiles: { ...ps.profiles, [target.id]: profile },
788
+ active: activate ? target.id : ps.active,
789
+ reconciled: false,
790
+ };
791
+ });
792
+ return describeDecideProfile(target.id);
793
+ }
794
+ /**
795
+ * Make a profile the active one: everyday decisions use it from now on.
796
+ *
797
+ * @param ref - `<provider>/<name>`, or a bare provider for `<provider>/default`.
798
+ * @returns The profile list after the switch.
799
+ * @throws {DecideCredentialsError} When the reference is invalid or unknown.
800
+ */
801
+ export async function useDecideProfile(ref) {
802
+ const { id } = requireProfileRef(ref);
803
+ await mutateProfiles((ps) => {
804
+ if (!ps.profiles[id]) {
805
+ throw new DecideCredentialsError(`no System One profile '${id}'${knownIds(ps)}`);
806
+ }
807
+ return { ...ps, active: id, reconciled: false };
808
+ });
809
+ return listDecideProfiles();
810
+ }
811
+ /**
812
+ * Remove a profile, then delete every rotated backup of the store (a backup
813
+ * may hold the removed key). The active profile is removed only when `use`
814
+ * names another profile to activate in its place.
815
+ *
816
+ * @param ref - Profile to remove.
817
+ * @param use - Profile to activate instead; required when `ref` is active.
818
+ * @returns The profile list after the removal.
819
+ * @throws {DecideCredentialsError} When a reference is invalid or unknown,
820
+ * or `ref` is active and `use` is missing or names it.
821
+ */
822
+ export async function removeDecideProfile(ref, use) {
823
+ const { id } = requireProfileRef(ref);
824
+ const useId = use === undefined ? undefined : requireProfileRef(use).id;
825
+ await mutateProfiles((ps) => {
826
+ if (!ps.profiles[id]) {
827
+ throw new DecideCredentialsError(`no System One profile '${id}'${knownIds(ps)}`);
828
+ }
829
+ let active = ps.active;
830
+ if (useId !== undefined) {
831
+ if (useId === id || !ps.profiles[useId]) {
832
+ throw new DecideCredentialsError(`--use must name another stored profile${knownIds(ps)}`);
833
+ }
834
+ active = useId;
835
+ }
836
+ if (active === id) {
837
+ throw new DecideCredentialsError(`'${id}' is the active profile: name the profile to activate instead with --use <provider>/<name>, or remove every profile with --clear`);
838
+ }
839
+ const profiles = Object.fromEntries(Object.entries(ps.profiles).filter(([profileId]) => profileId !== id));
840
+ return { profiles, active, reconciled: false };
841
+ });
842
+ purgeBackups(decideCredentialsPath());
843
+ return listDecideProfiles();
380
844
  }
381
845
  /**
382
- * Remove the stored settings: the store is overwritten with empty settings
383
- * and every rotated backup of it (which may hold an old key) is deleted.
846
+ * Remove every profile: the store is overwritten with empty settings and
847
+ * every rotated backup of it (which may hold an old key) is deleted.
384
848
  *
385
849
  * @returns True when settings were present before the call.
386
850
  */
387
851
  export async function clearDecideCredentials() {
388
852
  const path = decideCredentialsPath();
389
853
  const before = readStoreSync(path);
390
- const hadSettings = Boolean(before.baseUrl || before.apiKey || before.model);
854
+ const hadSettings = Boolean(before.baseUrl || before.apiKey || before.model || Object.keys(before.profiles ?? {}).length);
391
855
  guardWrite(path);
392
856
  if (existsSync(path)) {
393
857
  ensureStoreFile(path);
394
- await withLock(path, () => EMPTY_STORE, { mode: 0o600 });
858
+ await withLock(path, () => EMPTY_WRITTEN, { mode: 0o600 });
395
859
  }
396
860
  purgeBackups(path);
397
861
  return hadSettings;