@wrongstack/cli 0.295.0 → 0.296.2

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 (471) hide show
  1. package/data/providers.json +0 -24
  2. package/dist/acp-A5OSIU2P.js +824 -0
  3. package/dist/acp-A5OSIU2P.js.map +7 -0
  4. package/dist/acp-server-agent.d.ts.map +1 -1
  5. package/dist/arg-parser.d.ts.map +1 -1
  6. package/dist/audit-2ZSFGNIF.js +102 -0
  7. package/dist/audit-2ZSFGNIF.js.map +7 -0
  8. package/dist/auth-CJWQJVRH.js +504 -0
  9. package/dist/auth-CJWQJVRH.js.map +7 -0
  10. package/dist/auth-menu/auth-menu-audit.d.ts +1 -5
  11. package/dist/auth-menu/auth-menu-audit.d.ts.map +1 -1
  12. package/dist/auth-menu/local.d.ts +1 -1
  13. package/dist/auth-menu/local.d.ts.map +1 -1
  14. package/dist/auth-menu/shared.d.ts +1 -1
  15. package/dist/auth-menu/shared.d.ts.map +1 -1
  16. package/dist/bench-7ZDWSNEF.js +278 -0
  17. package/dist/bench-7ZDWSNEF.js.map +7 -0
  18. package/dist/boot/brain-decision-log.d.ts +7 -5
  19. package/dist/boot/brain-decision-log.d.ts.map +1 -1
  20. package/dist/boot/dispatch-webui.d.ts +4 -8
  21. package/dist/boot/dispatch-webui.d.ts.map +1 -1
  22. package/dist/boot/launch-menu.d.ts +1 -1
  23. package/dist/boot/launch-menu.d.ts.map +1 -1
  24. package/dist/boot/system-prompt-builder.d.ts +4 -1
  25. package/dist/boot/system-prompt-builder.d.ts.map +1 -1
  26. package/dist/boot/tui-coordinator-callbacks.d.ts +45 -0
  27. package/dist/boot/tui-coordinator-callbacks.d.ts.map +1 -0
  28. package/dist/boot/tui-coordinator-setup.d.ts +2 -2
  29. package/dist/boot/tui-coordinator-setup.d.ts.map +1 -1
  30. package/dist/boot/tui-project-picker-callback.d.ts +1 -1
  31. package/dist/boot/tui-project-picker-callback.d.ts.map +1 -1
  32. package/dist/boot/tui-project-switch.d.ts +3 -4
  33. package/dist/boot/tui-project-switch.d.ts.map +1 -1
  34. package/dist/boot/tui-runtime-state.d.ts +5 -6
  35. package/dist/boot/tui-runtime-state.d.ts.map +1 -1
  36. package/dist/boot/tui-session-resume.d.ts.map +1 -1
  37. package/dist/boot/tui-settings-adapter.d.ts +2 -3
  38. package/dist/boot/tui-settings-adapter.d.ts.map +1 -1
  39. package/dist/boot-config.d.ts +2 -2
  40. package/dist/boot-config.d.ts.map +1 -1
  41. package/dist/boot.d.ts +1 -1
  42. package/dist/boot.d.ts.map +1 -1
  43. package/dist/brain-menu/panel-service.d.ts.map +1 -1
  44. package/dist/chimera-review-task.d.ts +6 -0
  45. package/dist/chimera-review-task.d.ts.map +1 -0
  46. package/dist/chimera-reviewer-policy.d.ts +34 -0
  47. package/dist/chimera-reviewer-policy.d.ts.map +1 -0
  48. package/dist/chronicle-QQEQFSSE.js +256 -0
  49. package/dist/chronicle-QQEQFSSE.js.map +7 -0
  50. package/dist/chunk-5N34EWJR.js +418 -0
  51. package/dist/chunk-5N34EWJR.js.map +7 -0
  52. package/dist/chunk-66T43Q4Y.js +90 -0
  53. package/dist/chunk-66T43Q4Y.js.map +7 -0
  54. package/dist/chunk-7OCVIDC7.js +11 -0
  55. package/dist/chunk-7OCVIDC7.js.map +7 -0
  56. package/dist/chunk-ABXK4VVY.js +84 -0
  57. package/dist/chunk-ABXK4VVY.js.map +7 -0
  58. package/dist/chunk-AXVZMEWG.js +306 -0
  59. package/dist/chunk-AXVZMEWG.js.map +7 -0
  60. package/dist/chunk-AZESNNIC.js +216 -0
  61. package/dist/chunk-AZESNNIC.js.map +7 -0
  62. package/dist/chunk-BGFM3PD7.js +41 -0
  63. package/dist/chunk-BGFM3PD7.js.map +7 -0
  64. package/dist/chunk-DPJZQCVP.js +13 -0
  65. package/dist/chunk-DPJZQCVP.js.map +7 -0
  66. package/dist/chunk-F4PEGAJZ.js +176 -0
  67. package/dist/chunk-F4PEGAJZ.js.map +7 -0
  68. package/dist/chunk-IHBYBYGA.js +443 -0
  69. package/dist/chunk-IHBYBYGA.js.map +7 -0
  70. package/dist/chunk-IN4GXGYW.js +425 -0
  71. package/dist/chunk-IN4GXGYW.js.map +7 -0
  72. package/dist/chunk-JBSQ7ONN.js +1023 -0
  73. package/dist/chunk-JBSQ7ONN.js.map +7 -0
  74. package/dist/chunk-JBTFWWKV.js +1325 -0
  75. package/dist/chunk-JBTFWWKV.js.map +7 -0
  76. package/dist/chunk-JUHRI6OV.js +100 -0
  77. package/dist/chunk-JUHRI6OV.js.map +7 -0
  78. package/dist/chunk-OP47B3IX.js +142 -0
  79. package/dist/chunk-OP47B3IX.js.map +7 -0
  80. package/dist/chunk-PJHET45A.js +158 -0
  81. package/dist/chunk-PJHET45A.js.map +7 -0
  82. package/dist/chunk-Q5GTM25S.js +17 -0
  83. package/dist/chunk-Q5GTM25S.js.map +7 -0
  84. package/dist/chunk-QD544J2B.js +19 -0
  85. package/dist/chunk-QD544J2B.js.map +7 -0
  86. package/dist/chunk-QUNHS6O3.js +258 -0
  87. package/dist/chunk-QUNHS6O3.js.map +7 -0
  88. package/dist/chunk-TADVX644.js +480 -0
  89. package/dist/chunk-TADVX644.js.map +7 -0
  90. package/dist/chunk-TGDHN4LM.js +81 -0
  91. package/dist/chunk-TGDHN4LM.js.map +7 -0
  92. package/dist/chunk-TYO2OVAD.js +50 -0
  93. package/dist/chunk-TYO2OVAD.js.map +7 -0
  94. package/dist/chunk-VCYZ3WZV.js +14 -0
  95. package/dist/chunk-VCYZ3WZV.js.map +7 -0
  96. package/dist/chunk-VJUEZZYB.js +29 -0
  97. package/dist/chunk-VJUEZZYB.js.map +7 -0
  98. package/dist/chunk-WHOIR577.js +1 -0
  99. package/dist/chunk-WHOIR577.js.map +7 -0
  100. package/dist/chunk-WY7KKOUK.js +460 -0
  101. package/dist/chunk-WY7KKOUK.js.map +7 -0
  102. package/dist/chunk-X5DQTPXT.js +3059 -0
  103. package/dist/chunk-X5DQTPXT.js.map +7 -0
  104. package/dist/chunk-XJXDOF63.js +28 -0
  105. package/dist/chunk-XJXDOF63.js.map +7 -0
  106. package/dist/chunk-XWEGWYDU.js +560 -0
  107. package/dist/chunk-XWEGWYDU.js.map +7 -0
  108. package/dist/chunk-YWUPHRGB.js +346 -0
  109. package/dist/chunk-YWUPHRGB.js.map +7 -0
  110. package/dist/chunk-Z7YGASQP.js +313 -0
  111. package/dist/chunk-Z7YGASQP.js.map +7 -0
  112. package/dist/cli-entry-main.d.ts +2 -0
  113. package/dist/cli-entry-main.d.ts.map +1 -0
  114. package/dist/cli-entry-point.d.ts +3 -3
  115. package/dist/cli-entry-point.d.ts.map +1 -1
  116. package/dist/cli-eternal-flag.d.ts +2 -2
  117. package/dist/cli-eternal-flag.d.ts.map +1 -1
  118. package/dist/cli-main-6AU6E4XZ.js +29126 -0
  119. package/dist/cli-main-6AU6E4XZ.js.map +7 -0
  120. package/dist/cli-main-helpers.d.ts +21 -0
  121. package/dist/cli-main-helpers.d.ts.map +1 -0
  122. package/dist/cli-main.d.ts +25 -1
  123. package/dist/cli-main.d.ts.map +1 -1
  124. package/dist/context-limit.d.ts +48 -0
  125. package/dist/context-limit.d.ts.map +1 -1
  126. package/dist/diag-doctor-OQ3AA63O.js +170 -0
  127. package/dist/diag-doctor-OQ3AA63O.js.map +7 -0
  128. package/dist/execute-deps.d.ts +10 -12
  129. package/dist/execute-deps.d.ts.map +1 -1
  130. package/dist/execution-V26SE52F.js +4656 -0
  131. package/dist/execution-V26SE52F.js.map +7 -0
  132. package/dist/execution-chimera-ask.d.ts +10 -0
  133. package/dist/execution-chimera-ask.d.ts.map +1 -0
  134. package/dist/execution-chimera-cascade.d.ts +15 -0
  135. package/dist/execution-chimera-cascade.d.ts.map +1 -0
  136. package/dist/execution-cleanup.d.ts +23 -0
  137. package/dist/execution-cleanup.d.ts.map +1 -0
  138. package/dist/execution-kanban-dispatch.d.ts +10 -0
  139. package/dist/execution-kanban-dispatch.d.ts.map +1 -0
  140. package/dist/execution-repl-fleet-callbacks.d.ts +11 -0
  141. package/dist/execution-repl-fleet-callbacks.d.ts.map +1 -0
  142. package/dist/execution-storage-observability.d.ts +3 -0
  143. package/dist/execution-storage-observability.d.ts.map +1 -0
  144. package/dist/execution-tui-next-step-callbacks.d.ts +20 -0
  145. package/dist/execution-tui-next-step-callbacks.d.ts.map +1 -0
  146. package/dist/execution.d.ts +2 -139
  147. package/dist/execution.d.ts.map +1 -1
  148. package/dist/export-TPORYVRZ.js +64 -0
  149. package/dist/export-TPORYVRZ.js.map +7 -0
  150. package/dist/fleet/host-acp-runner-cache.d.ts +9 -0
  151. package/dist/fleet/host-acp-runner-cache.d.ts.map +1 -0
  152. package/dist/fleet/host-acp.d.ts +3 -0
  153. package/dist/fleet/host-acp.d.ts.map +1 -0
  154. package/dist/fleet/host-concurrency.d.ts +2 -0
  155. package/dist/fleet/host-concurrency.d.ts.map +1 -0
  156. package/dist/fleet/host-context.d.ts +5 -0
  157. package/dist/fleet/host-context.d.ts.map +1 -0
  158. package/dist/fleet/host-director-builder.d.ts +24 -0
  159. package/dist/fleet/host-director-builder.d.ts.map +1 -0
  160. package/dist/fleet/host-director-event-bridges.d.ts +37 -0
  161. package/dist/fleet/host-director-event-bridges.d.ts.map +1 -0
  162. package/dist/fleet/host-director-services.d.ts +20 -0
  163. package/dist/fleet/host-director-services.d.ts.map +1 -0
  164. package/dist/fleet/host-event-bridge.d.ts +17 -0
  165. package/dist/fleet/host-event-bridge.d.ts.map +1 -0
  166. package/dist/fleet/host-helpers.d.ts +22 -0
  167. package/dist/fleet/host-helpers.d.ts.map +1 -0
  168. package/dist/fleet/host-learning-tracker.d.ts +11 -0
  169. package/dist/fleet/host-learning-tracker.d.ts.map +1 -0
  170. package/dist/fleet/host-learning.d.ts +4 -0
  171. package/dist/fleet/host-learning.d.ts.map +1 -0
  172. package/dist/fleet/host-lifecycle-events.d.ts +4 -0
  173. package/dist/fleet/host-lifecycle-events.d.ts.map +1 -0
  174. package/dist/fleet/host-paths.d.ts +3 -0
  175. package/dist/fleet/host-paths.d.ts.map +1 -0
  176. package/dist/fleet/host-provider.d.ts +22 -0
  177. package/dist/fleet/host-provider.d.ts.map +1 -0
  178. package/dist/fleet/host-session-writer.d.ts +3 -0
  179. package/dist/fleet/host-session-writer.d.ts.map +1 -0
  180. package/dist/fleet/host-shadow-pass.d.ts +21 -0
  181. package/dist/fleet/host-shadow-pass.d.ts.map +1 -0
  182. package/dist/fleet/host-spawn-types.d.ts +25 -0
  183. package/dist/fleet/host-spawn-types.d.ts.map +1 -0
  184. package/dist/fleet/host-status.d.ts +48 -0
  185. package/dist/fleet/host-status.d.ts.map +1 -0
  186. package/dist/fleet/host-subagent-factory.d.ts +16 -0
  187. package/dist/fleet/host-subagent-factory.d.ts.map +1 -0
  188. package/dist/fleet/host-supervisor.d.ts +16 -0
  189. package/dist/fleet/host-supervisor.d.ts.map +1 -0
  190. package/dist/fleet/host-task-result-report.d.ts +11 -0
  191. package/dist/fleet/host-task-result-report.d.ts.map +1 -0
  192. package/dist/fleet/host-types.d.ts +89 -0
  193. package/dist/fleet/host-types.d.ts.map +1 -0
  194. package/dist/fleet/host.d.ts +10 -357
  195. package/dist/fleet/host.d.ts.map +1 -1
  196. package/dist/fleet/routing.d.ts +1 -1
  197. package/dist/fleet/routing.d.ts.map +1 -1
  198. package/dist/fleet/status-broadcast.d.ts.map +1 -1
  199. package/dist/goal-host.d.ts +2 -2
  200. package/dist/goal-host.d.ts.map +1 -1
  201. package/dist/hq-BECGICEZ.js +547 -0
  202. package/dist/hq-BECGICEZ.js.map +7 -0
  203. package/dist/hq-command-controller.d.ts +2 -2
  204. package/dist/hq-command-controller.d.ts.map +1 -1
  205. package/dist/hq-publisher.d.ts +2 -2
  206. package/dist/hq-publisher.d.ts.map +1 -1
  207. package/dist/hq-server/auth-state.d.ts +1 -1
  208. package/dist/hq-server/auth-state.d.ts.map +1 -1
  209. package/dist/hq-server/project-root.d.ts +5 -0
  210. package/dist/hq-server/project-root.d.ts.map +1 -0
  211. package/dist/hq-server/routes/auth-handlers.d.ts +32 -0
  212. package/dist/hq-server/routes/auth-handlers.d.ts.map +1 -0
  213. package/dist/hq-server/routes/session-handlers.d.ts +8 -0
  214. package/dist/hq-server/routes/session-handlers.d.ts.map +1 -0
  215. package/dist/hq-server/routes/system-handlers.d.ts +16 -0
  216. package/dist/hq-server/routes/system-handlers.d.ts.map +1 -0
  217. package/dist/hq-server/routes.d.ts +6 -6
  218. package/dist/hq-server/routes.d.ts.map +1 -1
  219. package/dist/hq-server/ws.d.ts +1 -1
  220. package/dist/hq-server/ws.d.ts.map +1 -1
  221. package/dist/hq-server-FJOVOERO.js +39 -0
  222. package/dist/hq-server-FJOVOERO.js.map +7 -0
  223. package/dist/hq-server.d.ts.map +1 -1
  224. package/dist/hq-ttl-U2FAYFAT.js +8 -0
  225. package/dist/hq-ttl-U2FAYFAT.js.map +7 -0
  226. package/dist/hq-tunnel-YZLRONPV.js +111 -0
  227. package/dist/hq-tunnel-YZLRONPV.js.map +7 -0
  228. package/dist/index.d.ts +2 -1
  229. package/dist/index.d.ts.map +1 -1
  230. package/dist/index.js +2507 -52632
  231. package/dist/index.js.map +4 -4
  232. package/dist/init-E2NDDOHI.js +27 -0
  233. package/dist/init-E2NDDOHI.js.map +7 -0
  234. package/dist/input-reader.d.ts +1 -1
  235. package/dist/input-reader.d.ts.map +1 -1
  236. package/dist/kanban-dispatch-route.d.ts +14 -0
  237. package/dist/kanban-dispatch-route.d.ts.map +1 -0
  238. package/dist/kanban-hq-sync.d.ts +2 -0
  239. package/dist/kanban-hq-sync.d.ts.map +1 -1
  240. package/dist/kanban-run-mirror-EKVNGXWW.js +12 -0
  241. package/dist/kanban-run-mirror-EKVNGXWW.js.map +7 -0
  242. package/dist/live-settings-input.d.ts +69 -0
  243. package/dist/live-settings-input.d.ts.map +1 -0
  244. package/dist/mailbox-serve-2UWHEN3M.js +302 -0
  245. package/dist/mailbox-serve-2UWHEN3M.js.map +7 -0
  246. package/dist/mcp-CYLPUEHC.js +413 -0
  247. package/dist/mcp-CYLPUEHC.js.map +7 -0
  248. package/dist/mcp-serve.d.ts +1 -1
  249. package/dist/mcp-serve.d.ts.map +1 -1
  250. package/dist/modeldiag-HENYJ4N7.js +1290 -0
  251. package/dist/modeldiag-HENYJ4N7.js.map +7 -0
  252. package/dist/next-task-predictor.d.ts +1 -1
  253. package/dist/next-task-predictor.d.ts.map +1 -1
  254. package/dist/permissions-FNYLZIL4.js +115 -0
  255. package/dist/permissions-FNYLZIL4.js.map +7 -0
  256. package/dist/picker-key-state.d.ts +7 -0
  257. package/dist/picker-key-state.d.ts.map +1 -0
  258. package/dist/picker-model-picker.d.ts +6 -0
  259. package/dist/picker-model-picker.d.ts.map +1 -0
  260. package/dist/picker-ui.d.ts +17 -0
  261. package/dist/picker-ui.d.ts.map +1 -0
  262. package/dist/picker.d.ts +4 -37
  263. package/dist/picker.d.ts.map +1 -1
  264. package/dist/plugin-management.d.ts +1 -1
  265. package/dist/plugin-management.d.ts.map +1 -1
  266. package/dist/plugin-usage-OU6X6LM3.js +40 -0
  267. package/dist/plugin-usage-OU6X6LM3.js.map +7 -0
  268. package/dist/plugins-4LJ5IUBI.js +24 -0
  269. package/dist/plugins-4LJ5IUBI.js.map +7 -0
  270. package/dist/pre-launch.d.ts +0 -1
  271. package/dist/pre-launch.d.ts.map +1 -1
  272. package/dist/project-BLQ3ALVL.js +76 -0
  273. package/dist/project-BLQ3ALVL.js.map +7 -0
  274. package/dist/project-manifest-GE25PW4S.js +18 -0
  275. package/dist/project-manifest-GE25PW4S.js.map +7 -0
  276. package/dist/project-picker-BOCWHBXP.js +17 -0
  277. package/dist/project-picker-BOCWHBXP.js.map +7 -0
  278. package/dist/projects-XE76NFNZ.js +38 -0
  279. package/dist/projects-XE76NFNZ.js.map +7 -0
  280. package/dist/provider-helpers.d.ts +22 -6
  281. package/dist/provider-helpers.d.ts.map +1 -1
  282. package/dist/providers-models-GDELBHZK.js +589 -0
  283. package/dist/providers-models-GDELBHZK.js.map +7 -0
  284. package/dist/quick-VIO2BEHS.js +10 -0
  285. package/dist/quick-VIO2BEHS.js.map +7 -0
  286. package/dist/repl-auto-proceed.d.ts +19 -0
  287. package/dist/repl-auto-proceed.d.ts.map +1 -0
  288. package/dist/repl-client-registration.d.ts +7 -0
  289. package/dist/repl-client-registration.d.ts.map +1 -0
  290. package/dist/repl-input.d.ts +11 -0
  291. package/dist/repl-input.d.ts.map +1 -0
  292. package/dist/repl-options.d.ts +54 -0
  293. package/dist/repl-options.d.ts.map +1 -0
  294. package/dist/repl-rendering.d.ts +4 -0
  295. package/dist/repl-rendering.d.ts.map +1 -0
  296. package/dist/repl-suggestions.d.ts +21 -0
  297. package/dist/repl-suggestions.d.ts.map +1 -0
  298. package/dist/repl.d.ts +3 -162
  299. package/dist/repl.d.ts.map +1 -1
  300. package/dist/replay-KCTXNMZQ.js +75 -0
  301. package/dist/replay-KCTXNMZQ.js.map +7 -0
  302. package/dist/rewind-EJMVFVLS.js +185 -0
  303. package/dist/rewind-EJMVFVLS.js.map +7 -0
  304. package/dist/sdd-runtime-IKPROVV2.js +54 -0
  305. package/dist/sdd-runtime-IKPROVV2.js.map +7 -0
  306. package/dist/services/dispatch-classifier.d.ts +1 -1
  307. package/dist/services/dispatch-classifier.d.ts.map +1 -1
  308. package/dist/services/project-facts.d.ts.map +1 -1
  309. package/dist/services/sdd/project-context.d.ts +1 -1
  310. package/dist/services/sdd/project-context.d.ts.map +1 -1
  311. package/dist/services/sdd/task-manager.d.ts +1 -1
  312. package/dist/services/sdd/task-manager.d.ts.map +1 -1
  313. package/dist/services/statusline-config.d.ts +1 -1
  314. package/dist/services/statusline-config.d.ts.map +1 -1
  315. package/dist/session-event-wiring.d.ts +5 -2
  316. package/dist/session-event-wiring.d.ts.map +1 -1
  317. package/dist/sessions-config-NLCMK3BY.js +409 -0
  318. package/dist/sessions-config-NLCMK3BY.js.map +7 -0
  319. package/dist/settings-menu.d.ts +4 -19
  320. package/dist/settings-menu.d.ts.map +1 -1
  321. package/dist/simpleui-dist-JIFGUJO7.js +93 -0
  322. package/dist/simpleui-dist-JIFGUJO7.js.map +7 -0
  323. package/dist/slash-commands/agent-improve.d.ts +6 -5
  324. package/dist/slash-commands/agent-improve.d.ts.map +1 -1
  325. package/dist/slash-commands/brain.d.ts.map +1 -1
  326. package/dist/slash-commands/btw.d.ts +1 -1
  327. package/dist/slash-commands/btw.d.ts.map +1 -1
  328. package/dist/slash-commands/clear.d.ts +1 -1
  329. package/dist/slash-commands/clear.d.ts.map +1 -1
  330. package/dist/slash-commands/fallback.d.ts.map +1 -1
  331. package/dist/slash-commands/goal-refiner.d.ts +2 -2
  332. package/dist/slash-commands/goal-refiner.d.ts.map +1 -1
  333. package/dist/slash-commands/interrupt.d.ts +1 -1
  334. package/dist/slash-commands/interrupt.d.ts.map +1 -1
  335. package/dist/slash-commands/kanban-agent-helpers.d.ts +18 -0
  336. package/dist/slash-commands/kanban-agent-helpers.d.ts.map +1 -0
  337. package/dist/slash-commands/kanban-format.d.ts +16 -0
  338. package/dist/slash-commands/kanban-format.d.ts.map +1 -0
  339. package/dist/slash-commands/kanban-help.d.ts +2 -0
  340. package/dist/slash-commands/kanban-help.d.ts.map +1 -0
  341. package/dist/slash-commands/kanban.d.ts +1 -1
  342. package/dist/slash-commands/kanban.d.ts.map +1 -1
  343. package/dist/slash-commands/mailbox-demo.d.ts.map +1 -1
  344. package/dist/slash-commands/mailbox.d.ts +3 -2
  345. package/dist/slash-commands/mailbox.d.ts.map +1 -1
  346. package/dist/slash-commands/memory-compact.d.ts +5 -0
  347. package/dist/slash-commands/memory-compact.d.ts.map +1 -0
  348. package/dist/slash-commands/memory-flags.d.ts +36 -0
  349. package/dist/slash-commands/memory-flags.d.ts.map +1 -0
  350. package/dist/slash-commands/memory-formatters.d.ts +16 -0
  351. package/dist/slash-commands/memory-formatters.d.ts.map +1 -0
  352. package/dist/slash-commands/memory.d.ts.map +1 -1
  353. package/dist/slash-commands/mouse.d.ts +2 -2
  354. package/dist/slash-commands/refiner.d.ts +1 -1
  355. package/dist/slash-commands/refiner.d.ts.map +1 -1
  356. package/dist/slash-commands/review.d.ts.map +1 -1
  357. package/dist/slash-commands/sdd-command-helpers.d.ts +23 -0
  358. package/dist/slash-commands/sdd-command-helpers.d.ts.map +1 -0
  359. package/dist/slash-commands/sdd-format.d.ts +32 -0
  360. package/dist/slash-commands/sdd-format.d.ts.map +1 -0
  361. package/dist/slash-commands/sdd.d.ts +1 -1
  362. package/dist/slash-commands/sdd.d.ts.map +1 -1
  363. package/dist/slash-commands/tool.d.ts +1 -1
  364. package/dist/slash-commands/tool.d.ts.map +1 -1
  365. package/dist/slash-commands/working-dir.d.ts +1 -1
  366. package/dist/slash-commands/working-dir.d.ts.map +1 -1
  367. package/dist/subcommands/handlers/chronicle.d.ts.map +1 -1
  368. package/dist/subcommands/handlers/mailbox-serve.d.ts.map +1 -1
  369. package/dist/subcommands/handlers/model-smoke-test.d.ts +51 -0
  370. package/dist/subcommands/handlers/model-smoke-test.d.ts.map +1 -0
  371. package/dist/subcommands/handlers/modeldiag-profiles.d.ts +64 -0
  372. package/dist/subcommands/handlers/modeldiag-profiles.d.ts.map +1 -0
  373. package/dist/subcommands/handlers/modeldiag.d.ts.map +1 -1
  374. package/dist/subcommands/handlers/per-subcommand-deep-help-table.d.ts +3 -0
  375. package/dist/subcommands/handlers/per-subcommand-deep-help-table.d.ts.map +1 -0
  376. package/dist/subcommands/handlers/per-subcommand-help-data.d.ts +3 -0
  377. package/dist/subcommands/handlers/per-subcommand-help-data.d.ts.map +1 -0
  378. package/dist/subcommands/handlers/per-subcommand-help-table.d.ts +36 -0
  379. package/dist/subcommands/handlers/per-subcommand-help-table.d.ts.map +1 -0
  380. package/dist/subcommands/handlers/per-subcommand-help-types.d.ts +18 -0
  381. package/dist/subcommands/handlers/per-subcommand-help-types.d.ts.map +1 -0
  382. package/dist/subcommands/handlers/per-subcommand-help.d.ts +2 -80
  383. package/dist/subcommands/handlers/per-subcommand-help.d.ts.map +1 -1
  384. package/dist/subcommands/handlers/rewind.d.ts.map +1 -1
  385. package/dist/subcommands/index.d.ts +7 -0
  386. package/dist/subcommands/index.d.ts.map +1 -1
  387. package/dist/tools-skills-UNKLOB7M.js +29 -0
  388. package/dist/tools-skills-UNKLOB7M.js.map +7 -0
  389. package/dist/update-YC4TYW2Y.js +16 -0
  390. package/dist/update-YC4TYW2Y.js.map +7 -0
  391. package/dist/update-check-UMYTOH7Z.js +18 -0
  392. package/dist/update-check-UMYTOH7Z.js.map +7 -0
  393. package/dist/version-help-DROHZRW6.js +11 -0
  394. package/dist/version-help-DROHZRW6.js.map +7 -0
  395. package/dist/webui-server/connection-handler.d.ts +2 -1
  396. package/dist/webui-server/connection-handler.d.ts.map +1 -1
  397. package/dist/webui-server/kanban-host-adapter.d.ts +9 -5
  398. package/dist/webui-server/kanban-host-adapter.d.ts.map +1 -1
  399. package/dist/webui-server/listen-helpers.d.ts +18 -0
  400. package/dist/webui-server/listen-helpers.d.ts.map +1 -0
  401. package/dist/webui-server/node-pty-loader.d.ts +6 -0
  402. package/dist/webui-server/node-pty-loader.d.ts.map +1 -0
  403. package/dist/webui-server/prefs-seeding.d.ts.map +1 -1
  404. package/dist/webui-server/terminal-log-view.d.ts +30 -0
  405. package/dist/webui-server/terminal-log-view.d.ts.map +1 -0
  406. package/dist/webui-server-G3TISUIY.js +1459 -0
  407. package/dist/webui-server-G3TISUIY.js.map +7 -0
  408. package/dist/webui-server-options.d.ts +189 -0
  409. package/dist/webui-server-options.d.ts.map +1 -0
  410. package/dist/webui-server.d.ts +2 -178
  411. package/dist/webui-server.d.ts.map +1 -1
  412. package/dist/wiring/brain-and-orchestration.d.ts +2 -2
  413. package/dist/wiring/brain-and-orchestration.d.ts.map +1 -1
  414. package/dist/wiring/codebase-index.d.ts.map +1 -1
  415. package/dist/wiring/command-host-adapters.d.ts +2 -2
  416. package/dist/wiring/command-host-adapters.d.ts.map +1 -1
  417. package/dist/wiring/command-host-state.d.ts +7 -7
  418. package/dist/wiring/command-host-state.d.ts.map +1 -1
  419. package/dist/wiring/dep-watcher-bridge.d.ts +23 -0
  420. package/dist/wiring/dep-watcher-bridge.d.ts.map +1 -0
  421. package/dist/wiring/dep-watcher.d.ts +1 -1
  422. package/dist/wiring/dep-watcher.d.ts.map +1 -1
  423. package/dist/wiring/director-setup.d.ts +4 -4
  424. package/dist/wiring/director-setup.d.ts.map +1 -1
  425. package/dist/wiring/eternal-command-handlers.d.ts +5 -5
  426. package/dist/wiring/eternal-command-handlers.d.ts.map +1 -1
  427. package/dist/wiring/fleet-command-handlers.d.ts +2 -2
  428. package/dist/wiring/fleet-command-handlers.d.ts.map +1 -1
  429. package/dist/wiring/hq-telemetry.d.ts +5 -5
  430. package/dist/wiring/hq-telemetry.d.ts.map +1 -1
  431. package/dist/wiring/lifecycle-plugins.d.ts +9 -8
  432. package/dist/wiring/lifecycle-plugins.d.ts.map +1 -1
  433. package/dist/wiring/mailbox-bridge-bootstrap.d.ts +1 -1
  434. package/dist/wiring/management-tools.d.ts +15 -0
  435. package/dist/wiring/management-tools.d.ts.map +1 -0
  436. package/dist/wiring/pipeline.d.ts +4 -3
  437. package/dist/wiring/pipeline.d.ts.map +1 -1
  438. package/dist/wiring/plugins.d.ts +1 -1
  439. package/dist/wiring/plugins.d.ts.map +1 -1
  440. package/dist/wiring/provider-runtime-setup.d.ts +18 -5
  441. package/dist/wiring/provider-runtime-setup.d.ts.map +1 -1
  442. package/dist/wiring/provider-status.d.ts +3 -3
  443. package/dist/wiring/provider-status.d.ts.map +1 -1
  444. package/dist/wiring/provider-utility-tools.d.ts +4 -4
  445. package/dist/wiring/provider-utility-tools.d.ts.map +1 -1
  446. package/dist/wiring/provider.d.ts.map +1 -1
  447. package/dist/wiring/replay.d.ts +2 -2
  448. package/dist/wiring/replay.d.ts.map +1 -1
  449. package/dist/wiring/runtime-controller-deps.d.ts +4 -4
  450. package/dist/wiring/runtime-controller-deps.d.ts.map +1 -1
  451. package/dist/wiring/runtime-dispatch-state.d.ts +5 -5
  452. package/dist/wiring/runtime-dispatch-state.d.ts.map +1 -1
  453. package/dist/wiring/runtime-picker-deps.d.ts +3 -3
  454. package/dist/wiring/runtime-picker-deps.d.ts.map +1 -1
  455. package/dist/wiring/sage.d.ts.map +1 -1
  456. package/dist/wiring/sdd-handlers.d.ts.map +1 -1
  457. package/dist/wiring/session-command-handlers.d.ts +5 -5
  458. package/dist/wiring/session-command-handlers.d.ts.map +1 -1
  459. package/dist/wiring/session-registry.d.ts +38 -0
  460. package/dist/wiring/session-registry.d.ts.map +1 -0
  461. package/dist/wiring/session.d.ts +7 -5
  462. package/dist/wiring/session.d.ts.map +1 -1
  463. package/dist/wiring/tools.d.ts +2 -2
  464. package/dist/wiring/tools.d.ts.map +1 -1
  465. package/package.json +22 -22
  466. package/dist/cli-recovery-prompt.d.ts +0 -19
  467. package/dist/cli-recovery-prompt.d.ts.map +0 -1
  468. package/dist/first-run-auth-gate.d.ts +0 -20
  469. package/dist/first-run-auth-gate.d.ts.map +0 -1
  470. package/dist/pre-launch/indexing-question.d.ts +0 -28
  471. package/dist/pre-launch/indexing-question.d.ts.map +0 -1
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/auth-menu/oauth-menu.ts", "../src/auth-menu/anthropic-oauth.ts", "../src/auth-menu/github-copilot-oauth.ts", "../src/auth-menu/helpers.ts", "../src/auth-menu/shared.ts", "../src/auth-menu/add-provider.ts", "../src/provider-id.ts", "../src/auth-menu/local.ts", "../src/auth-menu/auth-menu-audit.ts"],
4
+ "sourcesContent": ["import { color } from '@wrongstack/core/utils';\nimport { runClaudeOAuthLogin } from './anthropic-oauth.js';\nimport { runCopilotOAuthLogin } from './github-copilot-oauth.js';\nimport { runCodexOAuthLogin } from './openai-codex-oauth.js';\nimport type { AuthMenuDeps } from './types.js';\n\n/** Render subscription OAuth login choices shared by the top menu and add flow. */\nexport function renderOAuthLoginOptions(deps: AuthMenuDeps, indent = ' '): void {\n deps.renderer.write(\n `${indent}${color.bold('OAuth login options')} ${color.dim('(subscription sign-in)')}\\n` +\n `${indent}${color.bold('chatgpt')} ChatGPT Plus/Pro ${color.dim('(\u2192 openai-codex)')}\\n` +\n `${indent}${color.bold('claude')} Claude Pro/Max ${color.dim('(\u2192 anthropic-oauth)')}\\n` +\n `${indent}${color.bold('copilot')} GitHub Copilot ${color.dim('(\u2192 github-copilot)')}\\n`,\n );\n}\n\nexport type OAuthMenuKind = 'chatgpt' | 'claude' | 'copilot';\n\n/**\n * Single source of truth for the OAuth vocabulary. Every surface that accepts\n * a subscription name \u2014 `wstack auth login <x>`, the OAuth submenu, and the\n * catalog-cancel fallback \u2014 resolves through {@link resolveOAuthKind}, so the\n * three never drift into accepting different spellings of the same thing.\n */\nconst OAUTH_ALIASES: Record<OAuthMenuKind, readonly string[]> = {\n chatgpt: ['chatgpt', 'openai', 'codex', 'codex-cli', 'openai-codex', 'chatgpt-plus', 'plus', 'pro'],\n claude: ['claude', 'anthropic', 'claude-pro', 'claude-max', 'anthropic-oauth', 'max'],\n copilot: ['copilot', 'github', 'gh', 'github-copilot'],\n};\n\nconst OAUTH_NUMERIC: Record<string, OAuthMenuKind> = { '1': 'chatgpt', '2': 'claude', '3': 'copilot' };\n\n/**\n * Normalize a user-typed subscription name to an OAuth kind.\n * `allowNumeric` enables the 1/2/3 menu picks (off for free-text prompts,\n * where a bare digit is far more likely to be a typo than a menu choice).\n */\nexport function resolveOAuthKind(\n choice: string,\n opts: { allowNumeric?: boolean } = {},\n): OAuthMenuKind | undefined {\n const pick = choice.trim().toLowerCase();\n if (!pick) return undefined;\n if (opts.allowNumeric !== false && OAUTH_NUMERIC[pick]) return OAUTH_NUMERIC[pick];\n for (const [kind, aliases] of Object.entries(OAUTH_ALIASES)) {\n if (aliases.includes(pick)) return kind as OAuthMenuKind;\n }\n return undefined;\n}\n\n/** Run an OAuth login for a normalized menu choice. Returns true when handled. */\nexport async function runOAuthLoginChoice(\n deps: AuthMenuDeps,\n choice: string,\n opts: { allowNumeric?: boolean } = {},\n): Promise<boolean> {\n const kind = resolveOAuthKind(choice, opts);\n if (!kind) return false;\n await runOAuthLoginKind(deps, kind);\n return true;\n}\n\n/** Run the OAuth login flow for an already-resolved kind. */\nexport async function runOAuthLoginKind(deps: AuthMenuDeps, kind: OAuthMenuKind): Promise<number> {\n if (kind === 'claude') return runClaudeOAuthLogin(deps);\n if (kind === 'copilot') return runCopilotOAuthLogin(deps);\n return runCodexOAuthLogin(deps);\n}\n\n/** Sub-menu: pick a subscription to sign in with (OAuth). */\nexport async function runOAuthLoginMenu(deps: AuthMenuDeps): Promise<void> {\n deps.renderer.write(\n `\\n ${color.bold('Login with OAuth:')}\\n` +\n color.amber(' \u26A0 Subscription tokens used outside official clients may violate provider\\n') +\n color.amber(' Terms \u2014 your account could be rate-limited or banned. An API key is the\\n') +\n color.dim(' sanctioned path for programmatic use.\\n') +\n ` ${color.bold('1')} ChatGPT Plus/Pro ${color.dim('(\u2192 openai-codex)')}\\n` +\n ` ${color.bold('2')} Claude Pro/Max ${color.dim('(\u2192 anthropic-oauth)')}\\n` +\n ` ${color.bold('3')} GitHub Copilot ${color.dim('(\u2192 github-copilot)')}\\n`,\n );\n const pick = await deps.reader.readLine(` ${color.amber('?')} Pick ${color.dim('(or b to go back)')}: `);\n await runOAuthLoginChoice(deps, pick);\n}\n", "/**\n * Anthropic \"Sign in with Claude\" OAuth (Authorization Code + PKCE) \u2014 Claude\n * Pro/Max subscription login. Parallel to the Codex flow but with Anthropic's\n * endpoints and quirks:\n * - authorize at claude.ai/oauth/authorize (params include `code=true`),\n * - the OAuth `state` is the PKCE verifier (Anthropic's convention),\n * - JSON (not form) token exchange at platform.claude.com/v1/oauth/token,\n * - loopback callback on http://localhost:53692/callback.\n *\n * Stored under the canonical `anthropic-oauth` provider (family `anthropic-oauth`)\n * so it never clobbers an API-key `anthropic` provider. The provider adapter\n * adds the Bearer + beta headers and the required Claude Code system block.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport { openBrowser, startLoopbackServer } from './loopback-server.js';\nimport { color } from '@wrongstack/core/utils';\nimport { FetchError, ParseError, type ProviderApiKey, type ProviderConfig } from '@wrongstack/core/types';\nimport {\n mutateConfigProviders,\n normalizeKeys,\n nowIso,\n writeKeysBack,\n} from '../provider-config-utils.js';\nimport type { AuthMenuDeps } from './types.js';\n\nconst CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';\nconst AUTHORIZE_URL = 'https://claude.ai/oauth/authorize';\nconst TOKEN_URL = 'https://platform.claude.com/v1/oauth/token';\nconst REDIRECT_PORT = 53692;\nconst REDIRECT_HOST = '127.0.0.1';\nconst REDIRECT_PATH = '/callback';\nconst REDIRECT_URI = `http://localhost:${REDIRECT_PORT}${REDIRECT_PATH}`;\nconst SCOPES =\n 'org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload';\nexport const CLAUDE_PROVIDER_ID = 'anthropic-oauth';\nconst CLAUDE_BASE_URL = 'https://api.anthropic.com';\n\nexport interface ClaudeTokens {\n access: string;\n refresh: string;\n expires: number;\n}\n\n// \u2500\u2500 PKCE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function generatePkce(): { verifier: string; challenge: string } {\n const verifier = base64url(randomBytes(32));\n const challenge = base64url(createHash('sha256').update(verifier).digest());\n return { verifier, challenge };\n}\n\n/** Build the Claude authorize URL. Anthropic uses the PKCE verifier as `state`. */\nexport function buildAuthorizeUrl(challenge: string, verifier: string): string {\n const params = new URLSearchParams({\n code: 'true',\n client_id: CLIENT_ID,\n response_type: 'code',\n redirect_uri: REDIRECT_URI,\n scope: SCOPES,\n code_challenge: challenge,\n code_challenge_method: 'S256',\n state: verifier,\n });\n return `${AUTHORIZE_URL}?${params.toString()}`;\n}\n\nexport function parseAuthorizationInput(input: string): {\n code?: string | undefined;\n state?: string | undefined;\n} {\n const value = input.trim();\n if (!value) return {};\n try {\n const url = new URL(value);\n return {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n };\n } catch {\n /* not a URL */\n }\n if (value.includes('#')) {\n const [code, state] = value.split('#', 2);\n return { code, state };\n }\n if (value.includes('code=')) {\n const params = new URLSearchParams(value);\n return {\n code: params.get('code') ?? undefined,\n state: params.get('state') ?? undefined,\n };\n }\n return { code: value };\n}\n\n// \u2500\u2500 Token exchange / refresh (JSON) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface TokenJson {\n access_token?: string;\n refresh_token?: string;\n expires_in?: number;\n}\n\nasync function readTokens(res: Response, op: string, url: string): Promise<ClaudeTokens> {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new FetchError({\n message: `Claude token ${op} failed (${res.status}): ${text || res.statusText}`,\n status: res.status,\n context: { provider: 'anthropic-oauth', op, url },\n });\n }\n const json = (await res.json()) as TokenJson | null;\n if (!json?.access_token || !json.refresh_token || typeof json.expires_in !== 'number') {\n throw new ParseError({\n message: `Claude token ${op} response missing fields`,\n source: 'anthropic-oauth-token-response',\n context: { op },\n });\n }\n return {\n access: json.access_token,\n refresh: json.refresh_token,\n expires: Date.now() + json.expires_in * 1000,\n };\n}\n\n/** Exchange an authorization code (+ verifier, reused as state) for tokens. */\nexport async function exchangeAuthorizationCode(\n code: string,\n state: string,\n verifier: string,\n signal?: AbortSignal,\n): Promise<ClaudeTokens> {\n const res = await fetch(TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n state,\n redirect_uri: REDIRECT_URI,\n code_verifier: verifier,\n }),\n signal: signal\n ? AbortSignal.any([signal, AbortSignal.timeout(30_000)])\n : AbortSignal.timeout(30_000),\n });\n return readTokens(res, 'exchange', TOKEN_URL);\n}\n\n/**\n * Fetch the account's available Claude model ids live from\n * `api.anthropic.com/v1/models` using the OAuth token. Best-effort: returns []\n * on any failure so login still succeeds with a sensible fallback list.\n */\nasync function fetchClaudeModels(accessToken: string, signal: AbortSignal): Promise<string[]> {\n try {\n const res = await fetch(`${CLAUDE_BASE_URL}/v1/models?limit=100`, {\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n 'anthropic-version': '2023-06-01',\n 'anthropic-beta': 'claude-code-20250219,oauth-2025-04-20',\n },\n signal: AbortSignal.any([signal, AbortSignal.timeout(8_000)]),\n });\n if (!res.ok) return [];\n const json = (await res.json()) as { data?: Array<{ id?: string }> } | null;\n const ids = (json?.data ?? [])\n .map((m) => m.id)\n .filter((id): id is string => typeof id === 'string' && id.startsWith('claude-'));\n return ids;\n } catch {\n return [];\n }\n}\n\n// \u2500\u2500 Loopback server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// \u2500\u2500 Loopback server, browser opener, and callback HTML are now in\n// `./loopback-server.js` \u2014 imported at the top of this file.\n\n// \u2500\u2500 Main flow \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ClaudeLoginOptions {\n providerId?: string;\n /**\n * External cancellation signal (e.g. the TUI auth panel's Esc). When\n * provided, the flow does NOT install its own SIGINT handler \u2014 the\n * caller owns cancellation.\n */\n signal?: AbortSignal | undefined;\n}\n\nexport async function runClaudeOAuthLogin(\n deps: AuthMenuDeps,\n opts: ClaudeLoginOptions = {},\n): Promise<number> {\n const providerId = opts.providerId ?? CLAUDE_PROVIDER_ID;\n const { verifier, challenge } = generatePkce();\n // Anthropic reuses the PKCE verifier as the OAuth state.\n const state = verifier;\n const authorizeUrl = buildAuthorizeUrl(challenge, verifier);\n // Wrap URL in OSC 8 terminal hyperlink sequences \u2014 makes it a clickable\n // link in modern terminals (Windows Terminal, iTerm2, Kitty, WezTerm, etc.).\n // The hyperlink is region-based: clicking any wrapped segment opens the URL.\n const osc8 = (url: string) => `\\x1b]8;;${url}\\x1b\\\\${url}\\x1b]8;;\\x1b\\\\`;\n\n const ac = new AbortController();\n const onSig = () => ac.abort();\n const external = opts.signal;\n if (external) {\n if (external.aborted) ac.abort();\n else external.addEventListener('abort', () => ac.abort(), { once: true });\n } else {\n process.on('SIGINT', onSig);\n }\n\n const server = await startLoopbackServer(state, [REDIRECT_PORT], ac.signal, REDIRECT_PATH, REDIRECT_HOST);\n\n deps.renderer.write(\n color.bold(`\\n Sign in with Claude \u2014 ${color.cyan(providerId)}\\n`) +\n color.dim(' Uses your Claude Pro/Max subscription (not an API key).\\n') +\n color.amber(' \u26A0 Using a subscription outside the official Claude Code client is against\\n') +\n color.amber(' Anthropic\u2019s Terms \u2014 your account could be rate-limited or banned.\\n') +\n color.dim(' Sanctioned programmatic use = an API key: ') +\n color.bold('wstack auth anthropic') +\n color.dim('\\n\\n') +\n color.bold(` ${'\u2500'.repeat(56)}\\n`) +\n color.bold(' Open this URL in your browser to sign in:\\n') +\n color.cyan(` ${osc8(authorizeUrl)}\\n`) +\n color.bold(` ${'\u2500'.repeat(56)}\\n\\n`),\n );\n\n if (server.bound) {\n openBrowser(authorizeUrl);\n deps.renderer.write(\n color.dim(' A browser window should open. Waiting for you to finish signing in...\\n') +\n color.dim(' (Listening on http://localhost:53692 \u2014 press Ctrl+C to cancel.)\\n'),\n );\n } else {\n deps.renderer.write(\n color.amber(' \u26A0 Could not start the local callback listener (port 53692 in use).\\n') +\n color.dim(' After signing in, paste the full redirect URL (or the code) below.\\n'),\n );\n }\n\n let code: string | undefined;\n try {\n if (server.bound) {\n const got = await server.waitForCode();\n // Cancelled while waiting on the loopback \u2192 return now rather than\n // dropping into the manual-paste prompt (which would block again).\n if (ac.signal.aborted) {\n deps.renderer.write(color.dim(' Cancelled.\\n'));\n return 1;\n }\n if (got) code = got.code;\n }\n\n if (!code) {\n const input = (\n await deps.reader.readLine(\n `\\n ${color.amber('?')} Paste the redirect URL or code ${color.dim('(or q to cancel)')}: `,\n )\n ).trim();\n if (input.toLowerCase() === 'q' || input === '') {\n deps.renderer.write(color.dim(' Cancelled.\\n'));\n return 1;\n }\n const parsed = parseAuthorizationInput(input);\n if (parsed.state && parsed.state !== state) {\n deps.renderer.writeError(' State mismatch \u2014 please restart the login flow.');\n return 1;\n }\n code = parsed.code;\n }\n\n if (!code) {\n deps.renderer.writeError(' No authorization code received.');\n return 1;\n }\n\n deps.renderer.write(color.dim('\\n Exchanging authorization code for tokens...\\n'));\n const tokens = await exchangeAuthorizationCode(code, state, verifier, ac.signal);\n const models = await fetchClaudeModels(tokens.access, ac.signal);\n\n const saved = await saveClaudeTokens(deps, providerId, tokens, models);\n if (!saved) return 1;\n\n deps.renderer.write(color.green('\\n \u2713 Signed in with Claude!\\n'));\n const modelHint = models[0];\n deps.renderer.writeInfo(\n ` Saved as provider ${color.bold(providerId)}${models.length ? ` (${models.length} models)` : ''}.\\n` +\n (modelHint\n ? ` Use: ${color.bold(`wstack --provider ${providerId} --model ${modelHint}`)} \"<task>\"\\n`\n : ` No model list was discovered; choose a model before starting a run.\\n`) +\n color.dim(' Tokens refresh automatically before they expire.\\n'),\n );\n return 0;\n } catch (err) {\n const msg =\n err instanceof DOMException && err.name === 'AbortError'\n ? 'Login cancelled.'\n : (err as Error).message;\n deps.renderer.writeError(` Login failed: ${msg}`);\n return 1;\n } finally {\n server.close();\n if (!external) process.off('SIGINT', onSig);\n }\n}\n\nasync function saveClaudeTokens(\n deps: AuthMenuDeps,\n providerId: string,\n tokens: ClaudeTokens,\n models: string[],\n): Promise<boolean> {\n const entry: ProviderApiKey = {\n label: 'oauth-default',\n apiKey: tokens.access,\n createdAt: nowIso(),\n authMethod: 'oauth',\n expiresAt: new Date(tokens.expires).toISOString(),\n refreshToken: tokens.refresh,\n tokenType: 'bearer',\n scope: SCOPES,\n };\n try {\n await mutateConfigProviders(deps.profileConfigPath, deps.vault, (all) => {\n const existing = all[providerId];\n const p: ProviderConfig = existing ? { ...existing } : { type: providerId };\n p.family = 'anthropic-oauth';\n if (!p.baseUrl) p.baseUrl = CLAUDE_BASE_URL;\n if (models.length > 0) p.models = models;\n const keys = normalizeKeys(p).filter((k) => k.label !== entry.label);\n keys.push(entry);\n writeKeysBack(p, keys);\n p.activeKey = entry.label;\n all[providerId] = p;\n }, deps.profileConfigPath);\n return true;\n } catch (err) {\n deps.renderer.writeError(` Failed to save tokens: ${(err as Error).message}`);\n return false;\n }\n}\n", "/**\n * GitHub Copilot login \u2014 GitHub OAuth **device flow** (no loopback server).\n *\n * 1. POST github.com/login/device/code \u2192 user_code + verification_uri.\n * 2. User opens the URL, enters the code; we poll login/oauth/access_token\n * until GitHub returns a long-lived OAuth token.\n * 3. Exchange that token at api.github.com/copilot_internal/v2/token for a\n * short-lived Copilot token (the chat access token).\n *\n * Stored under the canonical `github-copilot` provider (family `github-copilot`,\n * an OpenAI chat/completions-compatible wire). The GitHub OAuth token is the\n * refresh token; the provider mints fresh Copilot tokens from it transparently.\n */\n\nimport { spawn } from 'node:child_process';\nimport { color } from '@wrongstack/core/utils';\nimport { FetchError, ParseError, type ProviderApiKey, type ProviderConfig } from '@wrongstack/core/types';\nimport { copilotBaseUrlFromToken, refreshCopilotToken } from '@wrongstack/providers';\nimport {\n mutateConfigProviders,\n normalizeKeys,\n nowIso,\n writeKeysBack,\n} from '../provider-config-utils.js';\nimport type { AuthMenuDeps } from './types.js';\n\nconst CLIENT_ID = 'Iv1.b507a08c87ecfe98';\nconst DEVICE_CODE_URL = 'https://github.com/login/device/code';\nconst ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token';\nconst COPILOT_HEADERS: Record<string, string> = {\n 'User-Agent': 'GitHubCopilotChat/0.35.0',\n 'Editor-Version': 'vscode/1.107.0',\n 'Editor-Plugin-Version': 'copilot-chat/0.35.0',\n 'Copilot-Integration-Id': 'vscode-chat',\n};\nconst COPILOT_API_VERSION = '2026-06-01';\nexport const COPILOT_PROVIDER_ID = 'github-copilot';\n\ninterface DeviceCode {\n device_code: string;\n user_code: string;\n verification_uri: string;\n interval: number;\n expires_in: number;\n}\n\nfunction openBrowser(url: string): void {\n try {\n const platform = process.platform;\n const { command, args } =\n platform === 'win32'\n ? { command: 'cmd', args: ['/c', 'start', '', url] }\n : platform === 'darwin'\n ? { command: 'open', args: [url] }\n : { command: 'xdg-open', args: [url] };\n const child = spawn(command, args, { stdio: 'ignore', windowsHide: true });\n child.on('error', () => {});\n child.unref();\n } catch {\n /* best-effort */\n }\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) return reject(new DOMException('Aborted', 'AbortError'));\n const t = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(t);\n reject(new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\n/** Start the GitHub device flow. */\nexport async function startDeviceFlow(signal?: AbortSignal): Promise<DeviceCode> {\n const res = await fetch(DEVICE_CODE_URL, {\n method: 'POST',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/x-www-form-urlencoded',\n 'user-agent': COPILOT_HEADERS['User-Agent']!,\n },\n body: new URLSearchParams({ client_id: CLIENT_ID, scope: 'read:user' }).toString(),\n signal: signal\n ? AbortSignal.any([signal, AbortSignal.timeout(15_000)])\n : AbortSignal.timeout(15_000),\n });\n if (!res.ok) {\n throw new FetchError({\n message: `GitHub device-code request failed (${res.status})`,\n status: res.status,\n context: { provider: 'github-copilot', op: 'device-code', url: DEVICE_CODE_URL },\n });\n }\n const json = (await res.json()) as Partial<DeviceCode> | null;\n if (\n !json?.device_code ||\n !json.user_code ||\n !json.verification_uri ||\n typeof json.expires_in !== 'number'\n ) {\n throw new ParseError({\n message: 'Invalid device-code response',\n source: 'github-copilot-device-code-response',\n });\n }\n return {\n device_code: json.device_code,\n user_code: json.user_code,\n verification_uri: json.verification_uri,\n interval: json.interval ?? 5,\n expires_in: json.expires_in,\n };\n}\n\n/** Poll until the user authorizes; returns the GitHub OAuth token. */\nexport async function pollForGitHubToken(device: DeviceCode, signal: AbortSignal): Promise<string> {\n let intervalMs = device.interval * 1000;\n const expiresAt = Date.now() + device.expires_in * 1000;\n\n while (Date.now() < expiresAt) {\n await sleep(intervalMs, signal);\n const res = await fetch(ACCESS_TOKEN_URL, {\n method: 'POST',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/x-www-form-urlencoded',\n 'user-agent': COPILOT_HEADERS['User-Agent']!,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n device_code: device.device_code,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }).toString(),\n signal: AbortSignal.any([signal, AbortSignal.timeout(15_000)]),\n });\n const json = (await res.json().catch(() => ({}))) as {\n access_token?: string;\n error?: string;\n };\n if (json.access_token) return json.access_token;\n if (json.error === 'authorization_pending') continue;\n if (json.error === 'slow_down') {\n intervalMs += 5_000;\n continue;\n }\n throw new Error(`Device flow failed: ${json.error ?? 'unknown error'}`);\n }\n throw new FetchError({\n message: 'Device code expired \u2014 please restart the login.',\n status: 408, // Request Timeout\n context: { provider: 'github-copilot', op: 'device-code-poll', reason: 'expired' },\n });\n}\n\n/** Shape of the fields we read from a `/models` entry (everything optional). */\ninterface CopilotModelEntry {\n id?: unknown;\n model_picker_enabled?: unknown;\n is_chat_default?: unknown;\n is_chat_fallback?: unknown;\n vendor?: unknown;\n supported_endpoints?: unknown;\n policy?: { state?: unknown } | undefined;\n capabilities?: { type?: unknown; supports?: { tool_calls?: unknown } | undefined } | undefined;\n}\n\n/**\n * Decide whether a Copilot `/models` entry is a chat model we can actually\n * drive over this provider's wire.\n *\n * The provider POSTs to `\u2026/chat/completions`, so the discriminators are:\n * - `capabilities.type === 'chat'` \u2014 drops embeddings and completion-only ids.\n * - `capabilities.supports.tool_calls` \u2014 the agent loop needs tool calling.\n * - `supported_endpoints` (when present) must include `/chat/completions`.\n * This is the key fix: `/responses`-only ids (the `*-picker` router models,\n * `gpt-5.4-mini-free-auto`, `mai-code-1-flash*`) advertise themselves in the\n * model picker but 400 on `/chat/completions`. Absent field \u21D2 legacy chat\n * model (gpt-4o, gpt-4.1) \u21D2 allowed.\n * - `policy.state !== 'disabled'` \u2014 disabled means the user hasn't enabled the\n * model in their GitHub Copilot settings; calling it would 403.\n * - `vendor !== 'Experimental'` \u2014 drops internal previews (trajectory-compaction).\n */\nexport function isUsableCopilotChatModel(item: CopilotModelEntry): boolean {\n if (typeof item.id !== 'string' || item.id.length === 0) return false;\n const cap = item.capabilities;\n if (cap?.type !== 'chat') return false;\n if (cap.supports?.tool_calls !== true) return false;\n const eps = item.supported_endpoints;\n if (Array.isArray(eps) && !eps.includes('/chat/completions')) return false;\n if (item.policy?.state === 'disabled') return false;\n if (item.vendor === 'Experimental') return false;\n return true;\n}\n\n/** Fetch the user's usable Copilot chat-model ids (best-effort, never throws). */\nasync function fetchCopilotModels(copilotToken: string, signal: AbortSignal): Promise<string[]> {\n try {\n const base = copilotBaseUrlFromToken(copilotToken);\n const res = await fetch(`${base}/models`, {\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${copilotToken}`,\n 'X-GitHub-Api-Version': COPILOT_API_VERSION,\n ...COPILOT_HEADERS,\n },\n signal: AbortSignal.any([signal, AbortSignal.timeout(8_000)]),\n });\n if (!res.ok) return [];\n const json = (await res.json()) as { data?: CopilotModelEntry[] } | null;\n const data = json?.data;\n if (!Array.isArray(data)) return [];\n const usable = data.filter(isUsableCopilotChatModel);\n // Float the provider's own default/fallback chat model to the front so the\n // saved list's first entry is a sane `--model` default.\n usable.sort((a, b) => copilotModelRank(a) - copilotModelRank(b));\n return usable.map((m) => m.id as string);\n } catch {\n return [];\n }\n}\n\n/** Lower rank sorts first: chat default, then fallback, then everything else. */\nfunction copilotModelRank(item: CopilotModelEntry): number {\n if (item.is_chat_default === true) return 0;\n if (item.is_chat_fallback === true) return 1;\n return 2;\n}\n\nexport interface CopilotLoginOptions {\n providerId?: string;\n /**\n * External cancellation signal (e.g. the TUI auth panel's Esc). When\n * provided, the flow does NOT install its own SIGINT handler \u2014 the\n * caller owns cancellation.\n */\n signal?: AbortSignal | undefined;\n}\n\nexport async function runCopilotOAuthLogin(\n deps: AuthMenuDeps,\n opts: CopilotLoginOptions = {},\n): Promise<number> {\n const providerId = opts.providerId ?? COPILOT_PROVIDER_ID;\n const ac = new AbortController();\n const onSig = () => ac.abort();\n const external = opts.signal;\n if (external) {\n if (external.aborted) ac.abort();\n else external.addEventListener('abort', () => ac.abort(), { once: true });\n } else {\n process.on('SIGINT', onSig);\n }\n\n try {\n deps.renderer.write(\n color.bold(`\\n Sign in with GitHub Copilot \u2014 ${color.cyan(providerId)}\\n`) +\n color.dim(' Uses your GitHub Copilot subscription (not an API key).\\n') +\n color.amber(' \u26A0 Using Copilot outside its official editor integrations may violate\\n') +\n color.amber(' GitHub\u2019s Terms \u2014 your account could be rate-limited or banned.\\n\\n'),\n );\n\n const device = await startDeviceFlow(ac.signal);\n deps.renderer.write(\n color.bold(` ${'\u2500'.repeat(56)}\\n`) +\n color.bold(' Open this URL and enter the code:\\n') +\n color.cyan(` ${device.verification_uri}\\n`) +\n color.bold(' Code: ') +\n color.green(color.bold(device.user_code)) +\n '\\n' +\n color.bold(` ${'\u2500'.repeat(56)}\\n\\n`),\n );\n openBrowser(device.verification_uri);\n deps.renderer.write(color.dim(' Waiting for you to authorize in the browser...\\n'));\n\n const githubToken = await pollForGitHubToken(device, ac.signal);\n deps.renderer.write(color.dim(' Authorized. Fetching your Copilot token...\\n'));\n const copilot = await refreshCopilotToken(githubToken, ac.signal);\n const models = await fetchCopilotModels(copilot.token, ac.signal);\n\n const saved = await saveCopilotTokens(\n deps,\n providerId,\n copilot.token,\n githubToken,\n copilot.expires,\n models,\n );\n if (!saved) return 1;\n\n deps.renderer.write(color.green('\\n \u2713 Signed in with GitHub Copilot!\\n'));\n const modelHint = models[0] ?? 'gpt-4o';\n deps.renderer.writeInfo(\n ` Saved as provider ${color.bold(providerId)}${models.length ? ` (${models.length} models)` : ''}.\\n` +\n ` Use: ${color.bold(`wstack --provider ${providerId} --model ${modelHint}`)} \"<task>\"\\n` +\n color.dim(' The Copilot token refreshes automatically.\\n'),\n );\n return 0;\n } catch (err) {\n const msg =\n err instanceof DOMException && err.name === 'AbortError'\n ? 'Login cancelled.'\n : (err as Error).message;\n deps.renderer.writeError(` Login failed: ${msg}`);\n return 1;\n } finally {\n if (!external) process.off('SIGINT', onSig);\n }\n}\n\nasync function saveCopilotTokens(\n deps: AuthMenuDeps,\n providerId: string,\n copilotToken: string,\n githubToken: string,\n expires: number,\n models: string[],\n): Promise<boolean> {\n const entry: ProviderApiKey = {\n label: 'oauth-default',\n apiKey: copilotToken,\n createdAt: nowIso(),\n authMethod: 'oauth',\n expiresAt: new Date(expires).toISOString(),\n refreshToken: githubToken,\n tokenType: 'bearer',\n };\n try {\n await mutateConfigProviders(deps.profileConfigPath, deps.vault, (all) => {\n const existing = all[providerId];\n const p: ProviderConfig = existing ? { ...existing } : { type: providerId };\n p.family = 'github-copilot';\n if (!p.baseUrl) p.baseUrl = copilotBaseUrlFromToken(copilotToken);\n if (models.length > 0) p.models = models;\n else if (!p.models || p.models.length === 0) p.models = ['gpt-4o'];\n const keys = normalizeKeys(p).filter((k) => k.label !== entry.label);\n keys.push(entry);\n writeKeysBack(p, keys);\n p.activeKey = entry.label;\n all[providerId] = p;\n }, deps.profileConfigPath);\n return true;\n } catch (err) {\n deps.renderer.writeError(` Failed to save tokens: ${(err as Error).message}`);\n return false;\n }\n}\n", "import type { ProviderConfig } from '@wrongstack/core/types';\nimport { loadConfigProviders } from '../provider-config-utils.js';\nimport type { AuthMenuDeps } from './types.js';\n\n/** Thin wrapper \u2014 delegates to the shared config provider loader. */\nexport async function loadProviders(deps: AuthMenuDeps): Promise<Record<string, ProviderConfig>> {\n return loadConfigProviders(deps.profileConfigPath, deps.vault, {\n warn: (msg: string) => deps.renderer.writeWarning(msg),\n profileConfigPath: deps.profileConfigPath,\n });\n}\n", "import { color } from '@wrongstack/core/utils';\nimport type { ProviderApiKey, ProviderConfig, WireFamily } from '@wrongstack/core/types';\nimport { activeLabel, maskedKey, normalizeKeys } from '../provider-config-utils.js';\nimport type { AuthMenuDeps } from './types.js';\n\n/* ------------------------------------------------------------------ */\n/* Rendering helpers */\n/* ------------------------------------------------------------------ */\n\n/** Render a provider entry line in the top-level listing. */\nfunction renderProviderLine(\n renderer: AuthMenuDeps['renderer'],\n id: string,\n cfg: ProviderConfig,\n idx: number,\n): void {\n const keys = normalizeKeys(cfg);\n const active = activeLabel(cfg, keys);\n const firstKey = keys[0];\n\n let summary: string;\n if (keys.length === 0) {\n summary = color.dim('(no keys)');\n } else if (keys.length === 1) {\n summary = maskedKey(firstKey?.apiKey ?? '');\n } else {\n const activeKeyObj = active != null ? keys.find((k) => k.label === active) : undefined;\n summary =\n `${color.dim(`${keys.length} keys`)} ` +\n `${color.dim('active:')} ${color.bold(active ?? '?')} ` +\n maskedKey(activeKeyObj?.apiKey ?? firstKey?.apiKey ?? '');\n }\n\n const fam = cfg.family ? color.dim(`[${cfg.family}]`) : '';\n const aliasHint = cfg.type && cfg.type !== id ? color.dim(`\u2192 ${cfg.type}`) : '';\n\n renderer.write(\n ` ${color.dim(`${idx}.`.padStart(4))} ${id.padEnd(22)} ${fam} ${aliasHint} ${summary}\\n`,\n );\n}\n\n/** Render the header for the provider management submenu. */\nexport function renderProviderHeader(\n renderer: AuthMenuDeps['renderer'],\n providerId: string,\n cfg: ProviderConfig,\n): void {\n const keys = normalizeKeys(cfg);\n const active = activeLabel(cfg, keys);\n\n renderer.write(\n `\\n${color.bold(providerId)} ` +\n `${cfg.family ? color.dim(`[${cfg.family}]`) : color.amber('[no family]')}\\n`,\n );\n\n const details: string[] = [\n color.dim(` type: ${cfg.type ?? providerId}`),\n color.dim(` family: ${cfg.family ?? '(unset \u2192 resolved from models.dev when type matches)'}`),\n color.dim(` baseUrl: ${cfg.baseUrl ?? '(unset \u2192 catalog default)'}`),\n ];\n\n if (cfg.envVars && cfg.envVars.length > 0) {\n details.push(color.dim(` envVars: ${cfg.envVars.join(', ')}`));\n }\n if (cfg.models && cfg.models.length > 0) {\n details.push(color.dim(` models: ${cfg.models.join(', ')}`));\n }\n\n renderer.write(details.join('\\n') + '\\n');\n\n if (keys.length === 0) {\n renderer.write(color.dim(' (no keys saved)\\n'));\n } else {\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n if (key) renderKeyLine(renderer, key, i + 1, active);\n }\n }\n}\n\n/** Render a single key entry line. */\nfunction renderKeyLine(\n renderer: AuthMenuDeps['renderer'],\n key: ProviderApiKey,\n idx: number,\n active: string | undefined,\n): void {\n const marker = key.label === active ? color.green('\u25CF') : color.dim('\u25CB');\n renderer.write(\n ` ${color.dim(`${idx}.`.padStart(4))} ${marker} ${key.label.padEnd(20)} ` +\n `${maskedKey(key.apiKey)} ${color.dim(key.createdAt)}\\n`,\n );\n}\n\n/** Render action shortcuts. */\nexport function renderActions(renderer: AuthMenuDeps['renderer'], keysLength: number): void {\n renderer.write(`\\n ${color.dim('Actions:')}\\n`);\n renderer.write(` ${color.bold('a')} Add another key\\n`);\n if (keysLength > 0) {\n renderer.write(` ${color.bold('u')} <n> Update key <n>\\n`);\n renderer.write(` ${color.bold('d')} <n> Delete key <n>\\n`);\n renderer.write(` ${color.bold('s')} <n> Set key <n> as active\\n`);\n }\n renderer.write(` ${color.bold('f')} Edit family\\n`);\n renderer.write(` ${color.bold('B')} Edit baseUrl ${color.dim('(capital B \u2014 b is Back)')}\\n`);\n renderer.write(` ${color.bold('m')} Edit visible model list\\n`);\n renderer.write(` ${color.bold('x')} Remove this provider entirely\\n`);\n renderer.write(` ${color.bold('b')} Back\\n`);\n renderer.write(` ${color.bold('q')} Quit\\n`);\n}\n\n/** Render the top-level menu header. */\nexport function renderTopMenu(\n renderer: AuthMenuDeps['renderer'],\n providers: Record<string, ProviderConfig>,\n): void {\n renderer.write(`\\n${color.bold('WrongStack')} ${color.dim('\u2014 API key manager')}\\n\\n`);\n\n const ids = Object.keys(providers).sort();\n if (ids.length === 0) {\n renderer.write(color.dim(' No providers configured yet.\\n'));\n renderer.write(\n color.dim(\n ' Use (a) to add one from the models.dev catalog, (l) for a local server, or (c) for a custom provider.\\n',\n ),\n );\n } else {\n renderer.write(` ${color.dim('Saved providers:')}\\n`);\n let idx = 1;\n for (const id of ids) {\n const cfg = providers[id];\n if (!cfg) continue;\n renderProviderLine(renderer, id, cfg, idx);\n idx++;\n }\n }\n\n renderer.write(`\\n ${color.dim('Actions:')}\\n`);\n renderer.write(` ${color.bold('a')} Add a provider (from catalog)\\n`);\n renderer.write(\n ` ${color.bold('l')} Add a local server ${color.dim('(OmniRoute / Ollama / vLLM / LM Studio)')}\\n`,\n );\n renderer.write(` ${color.bold('c')} Add a custom provider\\n`);\n renderer.write(\n ` ${color.bold('s')} Login with OAuth ${color.dim('(ChatGPT / Claude / Copilot)')}\\n`,\n );\n if (ids.length > 0) {\n renderer.write(\n ` ${color.dim('1-')}${color.dim(String(ids.length))} ${color.bold('Manage a provider')}\\n`,\n );\n }\n renderer.write(` ${color.bold('q')} Quit\\n`);\n}\n\n/* ------------------------------------------------------------------ */\n/* User input helpers */\n/* ------------------------------------------------------------------ */\n\n/** Prompt for a hidden API key. Returns undefined on empty input. */\nexport async function readKeyInput(\n deps: { reader: AuthMenuDeps['reader']; renderer: AuthMenuDeps['renderer'] },\n intent: string,\n): Promise<string | undefined> {\n const key = (\n await deps.reader.readSecret(\n ` ${color.amber('?')} ${intent} ${color.dim('(hidden, paste OK)')}: `,\n )\n ).trim();\n if (!key) {\n deps.renderer.writeError('No key entered.');\n return undefined;\n }\n return key;\n}\n\n/**\n * Ask a yes/no confirmation. Returns:\n * true \u2192 user confirmed (y/yes)\n * false \u2192 user declined (n/no)\n * null \u2192 user cancelled (q)\n */\nexport async function confirm(\n deps: { reader: AuthMenuDeps['reader']; renderer: AuthMenuDeps['renderer'] },\n question: string,\n): Promise<boolean | null> {\n const answer = (\n await deps.reader.readLine(` ${color.amber('?')} ${question} ${color.dim('[y/N/q]')} `)\n )\n .trim()\n .toLowerCase();\n if (answer === 'q' || answer === 'quit') return null;\n return answer === 'y' || answer === 'yes';\n}\n\n/** Suggest a default label that doesn't conflict with existing keys. */\nexport function suggestLabel(usedLabels: Set<string>): string {\n const candidate = 'default';\n if (!usedLabels.has(candidate)) return candidate;\n let n = 2;\n while (usedLabels.has(`key${n}`)) n++;\n return `key${n}`;\n}\n\n/** Validate a wire family string. Returns the WireFamily or null. */\nexport function validateFamily(raw: string): WireFamily | null {\n const valid: WireFamily[] = ['anthropic', 'openai', 'openai-compatible', 'google'];\n return valid.includes(raw as WireFamily) ? (raw as WireFamily) : null;\n}\n", "import type { ProviderConfig, ResolvedProvider, WireFamily } from '@wrongstack/core/types';\nimport { atomicWrite, color } from '@wrongstack/core/utils';\nimport {\n mutateConfigProviders,\n normalizeKeys,\n nowIso,\n writeKeysBack,\n} from '../provider-config-utils.js';\nimport { loadProviders } from './helpers.js';\nimport {\n renderOAuthLoginOptions,\n runOAuthLoginChoice,\n} from './oauth-menu.js';\nimport * as fs from 'node:fs/promises';\nimport { runLiveProviderPicker } from '../picker.js';\nimport { nextCustomProviderId } from '../provider-id.js';\nimport { readKeyInput, suggestLabel, validateFamily } from './shared.js';\nimport type { AuthMenuDeps } from './types.js';\n\n/* ------------------------------------------------------------------ */\n/* Add from catalog \u2014 pick a known provider from models.dev */\n/* ------------------------------------------------------------------ */\n\nexport async function addFromCatalog(deps: AuthMenuDeps): Promise<boolean> {\n let catalog: ResolvedProvider[] = [];\n try {\n catalog = (await deps.modelsRegistry.listProviders()).filter((p) => p.family !== 'unsupported');\n } catch {\n deps.renderer.writeWarning('Catalog unavailable \u2014 falling back to manual entry.\\n');\n }\n\n if (catalog.length === 0) {\n return addManualEntry(deps);\n }\n\n const saved = new Set(Object.keys(await loadProviders(deps)));\n deps.renderer.write(\n color.dim(\n ` Catalog: ${catalog.length} providers. Type to filter, \u2191/\u2193 navigate, Enter to select.\\n`,\n ),\n );\n deps.renderer.write(` ${color.dim('\u25C9 already saved \u25CB no key yet')}\\n\\n`);\n renderOAuthLoginOptions(deps);\n deps.renderer.write('\\n');\n\n const chosen = await runLiveProviderPicker(catalog, saved);\n if (!chosen) {\n // User cancelled \u2014 offer OAuth as an alternative.\n deps.renderer.write(\n `\\n ${color.dim('OAuth login options:')} ${color.dim('chatgpt')}, ${color.dim('claude')}, or ${color.dim('copilot')}?\\n`,\n );\n const answer = (\n await deps.reader.readLine(\n ` ${color.amber('?')} OAuth or q to quit ${color.dim('[chatgpt/claude/copilot]')}: `,\n )\n ).trim();\n if (answer && await runOAuthLoginChoice(deps, answer, { allowNumeric: false })) {\n return true;\n }\n return false;\n }\n\n return addKeyForCatalogProvider(deps, chosen);\n}\n\n/**\n * Prompt for family/baseUrl/alias overrides, then add a key.\n * Returns true if a key was added.\n *\n * Exported for the TUI auth panel: the panel does its own catalog\n * selection (searchable list) and then drives this flow through the\n * prompt bridge for the override/label/key questions.\n */\nexport async function addKeyForCatalogProvider(\n deps: AuthMenuDeps,\n chosen: ResolvedProvider,\n): Promise<boolean> {\n deps.renderer.write(\n color.dim(`\\n Defaults from models.dev \u2014 press Enter to keep, or type overrides.\\n`),\n );\n\n // Family override\n const famRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Family ${color.dim(`[${chosen.family}]`)} ${color.dim('(q to quit)')}: `,\n )\n ).trim();\n if (famRaw === 'q') return false;\n let family: WireFamily = chosen.family as WireFamily;\n if (famRaw) {\n const validated = validateFamily(famRaw);\n if (!validated) {\n deps.renderer.writeError(\n `Invalid family: \"${famRaw}\" (must be: anthropic, openai, openai-compatible, google).`,\n );\n return false;\n }\n family = validated;\n }\n\n // Base URL override\n const baseRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Base URL ${color.dim(`[${chosen.apiBase ?? 'unset'}]`)} ${color.dim('(q to quit)')}: `,\n )\n ).trim();\n if (baseRaw === 'q') return false;\n const baseUrl: string | undefined = baseRaw || chosen.apiBase;\n\n // Alias\n const providersNow = await loadProviders(deps);\n let suggestedAlias = chosen.id;\n if (family !== (chosen.family as WireFamily)) {\n let candidate = `${chosen.id}-${family}`;\n let n = 2;\n while (providersNow[candidate]) {\n candidate = `${chosen.id}-${family}-${n}`;\n n++;\n }\n suggestedAlias = candidate;\n }\n const aliasRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Save as alias ${color.dim(`[${suggestedAlias}]`)} ${color.dim('(used with --provider <alias>)')}: `,\n )\n ).trim();\n const alias = aliasRaw || suggestedAlias;\n\n // Check for conflicting existing entry\n const existing = providersNow[alias];\n if (existing) {\n const sameFamily = (existing.family ?? (chosen.family as WireFamily)) === family;\n const sameBase = (existing.baseUrl ?? chosen.apiBase) === baseUrl;\n if (!sameFamily || !sameBase) {\n deps.renderer.writeError(\n `Alias \"${alias}\" already exists with different family/baseUrl.\\n ` +\n `Existing: family=${existing.family ?? '(unset)'}, baseUrl=${existing.baseUrl ?? '(unset)'}\\n ` +\n `New: family=${family}, baseUrl=${baseUrl ?? '(unset)'}\\n ` +\n `Pick a different alias to keep them separate.`,\n );\n return false;\n }\n }\n\n return addKeyForProvider(alias, deps, {\n type: chosen.id,\n family,\n baseUrl,\n envVars: chosen.envVars,\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Add custom provider \u2014 fully user-defined (bypasses catalog) */\n/* ------------------------------------------------------------------ */\n\nexport async function addCustomProvider(deps: AuthMenuDeps): Promise<boolean> {\n deps.renderer.write(\n `\\n${color.bold('Custom provider')} ${color.dim('\u2014 for local models or proxies not in the catalog.')}\\n`,\n );\n\n const providersNow = await loadProviders(deps);\n const idRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Provider id ${color.dim('(e.g. \"local-llama\", \"my-proxy\"; blank = auto custom-N, q to quit)')}: `,\n )\n ).trim();\n if (idRaw === 'q') return false;\n\n let type = idRaw;\n if (!type) {\n // Blank id \u2192 assign a non-colliding custom-N name instead of rejecting,\n // so a hastily-added provider still gets a stable, unique id that won't\n // clash with (or overwrite) another entry.\n type = nextCustomProviderId(Object.keys(providersNow));\n deps.renderer.write(` ${color.dim('Using generated id:')} ${color.bold(type)}\\n`);\n } else if (providersNow[type]) {\n deps.renderer.writeWarning(`\"${type}\" already exists. Pick it from the main menu to edit.`);\n return false;\n }\n\n const familyRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Wire family ${color.dim('(anthropic | openai | openai-compatible | google)')} ${color.dim('(q to quit)')}: `,\n )\n ).trim();\n if (familyRaw === 'q') return false;\n const family = validateFamily(familyRaw);\n if (!family) {\n deps.renderer.writeError(`Invalid family: \"${familyRaw}\"`);\n return false;\n }\n\n const baseUrl = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Base URL ${color.dim('(e.g. http://localhost:11434/v1, optional)')}: `,\n )\n ).trim();\n\n const modelsRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Model ids ${color.dim('(comma-separated, optional)')}: `,\n )\n ).trim();\n const models = modelsRaw\n ? modelsRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n\n const envVarsRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Env var names ${color.dim('(comma-separated, optional)')}: `,\n )\n ).trim();\n const envVars = envVarsRaw\n ? envVarsRaw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n\n return addKeyForProvider(type, deps, {\n type,\n family,\n ...(baseUrl ? { baseUrl } : {}),\n ...(models ? { models } : {}),\n ...(envVars ? { envVars } : {}),\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Manual entry \u2014 fallback when catalog is unavailable */\n/* ------------------------------------------------------------------ */\n\nasync function addManualEntry(deps: AuthMenuDeps): Promise<boolean> {\n const pid = (\n await deps.reader.readLine(` ${color.amber('?')} Provider id ${color.dim('[q to quit]')}: `)\n ).trim();\n if (!pid || pid === 'q') return false;\n\n const famRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Family ${color.dim('(anthropic/openai/openai-compatible/google)')}: `,\n )\n ).trim();\n const family = validateFamily(famRaw);\n if (!family) {\n deps.renderer.writeError(`Invalid family: \"${famRaw}\"`);\n return false;\n }\n\n const baseUrl = (\n await deps.reader.readLine(` ${color.amber('?')} Base URL ${color.dim('(optional)')}: `)\n ).trim();\n\n return addKeyForProvider(pid, deps, {\n type: pid,\n family,\n ...(baseUrl ? { baseUrl } : {}),\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* Core: add a key to a provider */\n/* ------------------------------------------------------------------ */\n\nexport async function addKeyForProvider(\n providerId: string,\n deps: AuthMenuDeps,\n template: Partial<ProviderConfig>,\n): Promise<boolean> {\n const providers = await loadProviders(deps);\n const existing = providers[providerId];\n const existingKeys = existing ? normalizeKeys(existing) : [];\n const usedLabels = new Set(existingKeys.map((k) => k.label));\n\n const label = await promptForLabel(deps, usedLabels);\n if (!label) return false;\n\n const apiKey = await readKeyInput(deps, `API key for ${providerId}/${label}`);\n if (!apiKey) return false;\n\n await mutateConfigProviders(deps.profileConfigPath, deps.vault, (all) => {\n const existingProv: ProviderConfig = all[providerId] ?? {\n type: providerId,\n ...template,\n };\n if (!existingProv.type) existingProv.type = providerId;\n if (!existingProv.family && template.family) {\n existingProv.family = template.family;\n }\n if (!existingProv.baseUrl && template.baseUrl) {\n existingProv.baseUrl = template.baseUrl;\n }\n if (!existingProv.envVars && template.envVars) {\n existingProv.envVars = template.envVars;\n }\n const list = normalizeKeys(existingProv);\n list.push({ label, apiKey, createdAt: nowIso() });\n writeKeysBack(existingProv, list);\n if (!existingProv.activeKey) existingProv.activeKey = label;\n all[providerId] = existingProv;\n }, deps.profileConfigPath);\n\n deps.renderer.write(\n ` ${color.green('\u2713')} Saved ${color.bold(providerId)}/${color.bold(label)}.\\n`,\n );\n deps.renderer.write(color.dim(` Launch: wstack --provider ${providerId} \"<task>\"\\n`));\n\n // If the user has no active default yet, adopt this provider as the default\n // so the very first provider they add is immediately usable \u2014 the WebUI\n // otherwise shows a \"no model / needs setup\" screen when config.provider or\n // config.model is unset (the TUI tolerates it via its own picker). Never\n // overrides an existing default; best-effort so a miss just defers the pick.\n try {\n await adoptAsDefaultIfUnset(deps, providerId, template);\n } catch {\n /* best-effort \u2014 the user can still pick a default via the picker */\n }\n return true;\n}\n\n/**\n * Resolve a sensible default model id for a just-added provider: an explicit\n * saved model wins, otherwise the first model the catalog knows for it.\n * Returns undefined when neither source yields a model (e.g. a custom provider\n * with no `models` list and no catalog entry) \u2014 the caller then skips adoption.\n */\nasync function resolveDefaultModelId(\n deps: AuthMenuDeps,\n providerId: string,\n template: Partial<ProviderConfig>,\n): Promise<string | undefined> {\n if (template.models && template.models.length > 0) return template.models[0];\n const catalogId = template.type && template.type !== providerId ? template.type : providerId;\n const provider = await deps.modelsRegistry.getProvider(catalogId).catch(() => undefined);\n return provider?.models?.[0]?.id;\n}\n\n/**\n * Adopt `providerId` as the global default provider/model when none is set.\n * Reads the raw config (provider/model are plaintext top-level keys) so we\n * never clobber an existing choice, then persists via {@link saveToGlobalConfig}.\n */\nasync function adoptAsDefaultIfUnset(\n deps: AuthMenuDeps,\n providerId: string,\n template: Partial<ProviderConfig>,\n): Promise<void> {\n const targetPath = deps.profileConfigPath;\n let cfg: Record<string, unknown> = {};\n try {\n cfg = JSON.parse(await fs.readFile(targetPath, 'utf8')) as Record<string, unknown>;\n } catch {\n // Missing/unparsable config \u2014 treat as \"no default set\" and continue.\n }\n if (cfg['provider'] && cfg['model']) return; // already has a usable default\n\n const modelId = await resolveDefaultModelId(deps, providerId, template);\n if (!modelId) return; // nothing concrete to point the default at\n\n // Write straight to the same config path we just saved the provider into.\n // Re-serialising the parsed object preserves the vault-encrypted provider\n // blobs verbatim (we never decrypt here), and scoping the write to\n // the resolved profile target keeps it path-honest \u2014 unlike saveToGlobalConfig, whose\n // backup step targets the real ~/.wrongstack regardless of the path passed.\n cfg['provider'] = providerId;\n cfg['model'] = modelId;\n await atomicWrite(targetPath, JSON.stringify(cfg, null, 2), { mode: 0o600 });\n deps.renderer.write(\n ` ${color.green('\u2713')} Set ${color.bold(providerId)}/${color.bold(modelId)} as the default.\\n`,\n );\n}\n\nasync function promptForLabel(deps: AuthMenuDeps, usedLabels: Set<string>): Promise<string | null> {\n const defaultLabel = suggestLabel(usedLabels);\n const labelRaw = (\n await deps.reader.readLine(\n ` ${color.amber('?')} Label for this key ${color.dim(`[${defaultLabel}]`)}: `,\n )\n ).trim();\n const label = labelRaw || defaultLabel;\n if (usedLabels.has(label)) {\n deps.renderer.writeError(`Label \"${label}\" is already used. Use update (u) instead.`);\n return null;\n }\n return label;\n}\n", "/**\n * Pure helper for generating non-colliding custom provider ids.\n *\n * Shared by the `/auth` custom-provider flow (blank id \u2192 auto name) and the\n * config doctor's blank-id repair, so both surfaces name providers the same\n * way: `custom-1`, `custom-2`, \u2026 skipping any id already in the config.\n */\n\n/**\n * Return the first `custom-N` id (N \u2265 1) not present in `existing`.\n * `existing` is any iterable of the provider ids already taken.\n */\nexport function nextCustomProviderId(existing: Iterable<string>): string {\n const taken = new Set(existing);\n let n = 1;\n while (taken.has(`custom-${n}`)) n++;\n return `custom-${n}`;\n}\n", "/**\n * `wstack auth local` \u2014 quick-add shortcut for local-LLM servers.\n *\n * Three servers are first-class: Ollama, vLLM, LM Studio. They share an\n * OpenAI-compatible wire format at `/v1/chat/completions`, so each entry\n * here just declares the defaults that the matching wire preset\n * (see `@wrongstack/providers/src/presets/local-llm.ts`) expects.\n *\n * Difference that matters at the config layer:\n * - Ollama \u2192 no auth (the server rejects any Authorization header).\n * - vLLM \u2192 optional Bearer auth; key is left blank when auth is\n * disabled on the server, so the prompt is \"Enter to skip\".\n * - LM Studio \u2192 same as vLLM (optional Bearer).\n *\n * Selection is interactive by default \u2014 the user picks `1/2/3` from a\n * numbered list. A direct path is also exposed for scripting via the\n * `--name <ollama|vllm|lmstudio>` flag.\n *\n * Before saving, the shortcut runs a health probe (`GET /v1/models`)\n * to verify the server is actually reachable. The probe is opt-out via\n * `--no-probe`; failures are non-fatal by default \u2014 the user is asked\n * whether to save anyway. Every log line is run through the shared\n * `SecretScrubber` before reaching the renderer, so a Bearer token\n * accidentally captured in a probe log can never echo to the terminal\n * in plaintext.\n */\nimport type { SecretScrubber } from '@wrongstack/core/types';\nimport { color } from '@wrongstack/core/utils';\nimport { DefaultSecretScrubber } from '@wrongstack/core/security';\nimport {\n mutateConfigProviders,\n normalizeKeys,\n nowIso,\n writeKeysBack,\n} from '../provider-config-utils.js';\nimport { loadProviders } from './helpers.js';\nimport { LOCAL_LLM_PRESETS, type LocalLlmPresetEntry } from './local-presets.js';\nimport { suggestLabel } from './shared.js';\nimport type { AuthMenuDeps } from './types.js';\nimport { probeLocalLlm } from '@wrongstack/runtime/probe';\nimport type { ProbeOptions, ProbeResult } from '@wrongstack/runtime/probe';\nimport {\n type AuthAuditLogger,\n type AuthAuditEvent,\n decideAuthLocalEvents,\n NOOP_AUDIT_LOGGER,\n} from './auth-menu-audit.js';\n\n// Re-export from the runtime package so consumers (CLI tests, the\n// top-level CLI barrel) keep a stable import path. The runtime\n// package owns the canonical implementation \u2014 the WebUI server\n// imports from there too, so the probe runs in both contexts without\n// pulling the full CLI into the WebUI dependency graph.\nexport { probeLocalLlm, type ProbeOptions, type ProbeResult };\n\n// Preset data + type live in a dependency-free module so the startup\n// provider picker can import them without pulling in the probe/config\n// machinery this file depends on. Re-exported here to keep the public\n// import path (`@wrongstack/cli` barrel, tests) stable.\nexport { LOCAL_LLM_PRESETS, type LocalLlmPresetEntry };\n\nconst PRESET_BY_ID = new Map(LOCAL_LLM_PRESETS.map((p) => [p.id, p]));\n\nexport interface RunAuthLocalOptions {\n /**\n * Direct pick: skip the interactive menu and configure this preset.\n * `wstack auth local --name ollama` maps to `{ name: 'ollama' }`.\n */\n name?: string | undefined;\n /**\n * Override the default base URL. Empty / undefined falls back to the\n * preset's `defaultBaseUrl`.\n */\n baseUrl?: string | undefined;\n /**\n * Optional key to save. When omitted, the shortcut prompts the user\n * (unless the preset is `noAuth: true`).\n */\n apiKey?: string | undefined;\n /**\n * Optional label for the saved key. Defaults to \"default\" (matching\n * `addKeyForProvider` and `runAuthDirect`).\n */\n label?: string | undefined;\n /**\n * Force-skip the key prompt even for presets that normally accept a\n * key. Useful for scripting `wstack auth local --name ollama --no-key`\n * so no TTY input is required.\n */\n skipKey?: boolean | undefined;\n /**\n * Skip the reachability probe entirely. Use for scripting when the\n * local server may not be running yet (e.g. CI installing Ollama in\n * the background) but the user still wants the config in place.\n */\n noProbe?: boolean | undefined;\n /**\n * Run the probe and report, but don't save. Useful for re-checking a\n * previously-saved local provider.\n */\n probeOnly?: boolean | undefined;\n /**\n * Inject a custom `fetch` (test seam). Defaults to the global one.\n */\n fetchImpl?: typeof fetch | undefined;\n /**\n * Probe timeout override (test seam). Defaults to {@link PROBE_TIMEOUT_MS}.\n */\n probeTimeoutMs?: number | undefined;\n /**\n * Pre-populate `providers[id].models` so the user can launch\n * `wstack --provider <id> --model <picked>` right after the shortcut\n * completes. Four shapes are accepted:\n *\n * - `undefined` (or omitted) \u2192 don't touch `cfg.models`.\n * - `'first'` \u2192 take the first model id from the probe.\n * - `<N>` (a positive integer string) \u2192 take the first N ids.\n * Caps at the available list size.\n * - Anything else \u2192 treated as a literal comma-separated list of\n * model ids. Used verbatim, ignoring the probe result.\n *\n * When the probe didn't run (`noProbe: true`) or failed, only the\n * literal-list shape is meaningful \u2014 the count-based shapes return\n * an empty list and the shortcut proceeds without writing models.\n */\n models?: string | undefined;\n /**\n * Audit logger (test seam). When supplied, the function emits\n * structured `auth.local.*` events for the save lifecycle:\n * `add` / `clear` / `undo` / `probe_skip` / `probe_failed_save`.\n * The default is a no-op logger (production usage in\n * `wstack auth local` doesn't currently wire one \u2014 the WS server\n * is the source of truth for the dedicated `provider.*` message\n * types on the WebUI side; this option exists so the\n * integration test can capture the CLI flow's events for\n * end-to-end verification).\n */\n audit?: AuthAuditLogger | undefined;\n}\n\n/**\n * Quick-add entry point. Returns 0 on success, 1 on a hard error.\n *\n * The exit-code semantics are:\n * - 0 \u2192 provider saved (or `probeOnly` ran successfully / user\n * canceled with no state change / user declined save after\n * probe failure)\n * - 1 \u2192 hard failure: unknown --name, mutate threw, etc.\n */\nexport async function runAuthLocal(\n deps: AuthMenuDeps,\n opts: RunAuthLocalOptions = {},\n): Promise<number> {\n const scrubber = deps.secretScrubber ?? new DefaultSecretScrubber();\n const audit: AuthAuditLogger = opts.audit ?? NOOP_AUDIT_LOGGER;\n\n const preset = opts.name ? PRESET_BY_ID.get(opts.name.toLowerCase()) : undefined;\n if (opts.name && !preset) {\n deps.renderer.writeError(\n `Unknown local server \"${opts.name}\". Valid: ${LOCAL_LLM_PRESETS.map((p) => p.id).join(', ')}.`,\n );\n return 1;\n }\n\n const chosen = preset ?? (await pickLocalPreset(deps));\n if (!chosen) {\n // Picker returns undefined on either user-cancel (`q`) or an\n // unknown-selection. Both cases have already been reported to the\n // user via the renderer; there's no error state to surface.\n return 0;\n }\n\n // Resolve the base URL \u2014 user override, then preset default.\n const baseUrl = opts.baseUrl?.trim() || chosen.defaultBaseUrl;\n\n // Resolve the key \u2014 non-interactive path when caller provided it,\n // else prompt (unless the preset is noAuth and the user didn't opt\n // out of the prompt via --no-key).\n let apiKey: string | undefined;\n if (opts.apiKey !== undefined) {\n apiKey = opts.apiKey.trim() || undefined;\n } else if (chosen.noAuth) {\n apiKey = undefined;\n } else if (opts.skipKey) {\n apiKey = undefined;\n } else {\n apiKey = await promptOptionalKey(deps, chosen);\n }\n\n // \u2500\u2500 Health probe \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Run unless the user explicitly disabled it. On failure, prompt\n // before saving \u2014 local servers being down is a common cause of\n // \"why isn't anything working\" and we'd rather catch it here.\n // The probe result is captured (not just inspected) because the\n // --model flag consumes its modelIds below.\n let probe: ProbeResult | undefined;\n let probeFailedAndSaved = false;\n if (!opts.noProbe) {\n probe = await probeLocalLlm({\n baseUrl,\n apiKey,\n noAuth: chosen.noAuth,\n presetLabel: chosen.label,\n scrubber,\n fetchImpl: opts.fetchImpl,\n timeoutMs: opts.probeTimeoutMs,\n });\n\n renderProbeResult(deps, probe);\n\n if (opts.probeOnly) {\n // Don't save \u2014 just report. Exit 0 is correct: we did exactly\n // what the user asked.\n return 0;\n }\n\n if (!probe.ok) {\n if (chosen.noAuth) {\n // Keyless loopback gateway (OmniRoute / Ollama): there's no\n // credential to validate, and these are meant to be configured\n // ahead of the server being up \u2014 OmniRoute auto-discovers its\n // model list at boot. Skip the \"Save anyway?\" detour entirely and\n // save with an informational note rather than nagging the user.\n deps.renderer.write(\n color.dim(\n ` Saving ${chosen.label} anyway \u2014 keyless local gateway needs no key ` +\n 'and discovers models when it comes up.\\n',\n ),\n );\n probeFailedAndSaved = true;\n } else {\n const saveAnyway = await promptSaveAnyway(deps);\n if (!saveAnyway) {\n deps.renderer.write(color.dim(' Cancelled. Nothing saved.\\n'));\n return 0;\n }\n probeFailedAndSaved = true;\n }\n }\n }\n\n // Resolve the model list once \u2014 passed to the save block below and\n // used for the \"Launch:\" hint so the user can copy-paste a working\n // command after the shortcut completes. `probe` may be undefined\n // (when --no-probe was passed); resolveModelList handles that.\n const resolvedModels = resolveModelList(opts.models, probe, scrubber);\n\n // Resolve the label (used for the key entry \u2014 always present so the\n // saved shape matches the rest of the config, even when apiKey is\n // undefined).\n const label = opts.label?.trim() || 'default';\n\n const providers = await loadProviders(deps);\n const existing = providers[chosen.id];\n // Capture the previous models list BEFORE the mutate, so the\n // audit-log decision (add vs clear vs undo) can compare\n // against the pre-save state. The list is defensive-copied\n // because `mutateConfigProviders` mutates `p.models` in\n // place, and the capture must survive that mutation.\n const previousModels: string[] | undefined = existing?.models\n ? [...existing.models]\n : existing?.models === undefined\n ? undefined\n : [...existing.models];\n const usedLabels = new Set(existing ? normalizeKeys(existing).map((k) => k.label) : []);\n let finalLabel = label;\n if (usedLabels.has(finalLabel)) {\n let n = 2;\n while (usedLabels.has(`${label}-${n}`)) n++;\n finalLabel = `${label}-${n}`;\n deps.renderer.writeInfo(`Label collided; saving as \"${finalLabel}\".`);\n }\n\n try {\n await mutateConfigProviders(deps.profileConfigPath, deps.vault, (all) => {\n const p = all[chosen.id] ?? { type: chosen.id };\n if (!p.type) p.type = chosen.id;\n // Wire family is always openai-compatible for these three.\n if (!p.family) p.family = 'openai-compatible';\n if (!p.baseUrl) p.baseUrl = baseUrl;\n // vLLM and LM Studio both rely on the `openai-compatible` family\n // for the right token-limit param. No env vars to probe \u2014 local\n // servers don't read process env for auth.\n if (!p.envVars) p.envVars = [];\n\n // Pre-populate `models` when the user passed --model. Three\n // shapes from the resolver:\n // - `null` \u2192 flag not passed; don't touch p.models\n // - `[]` \u2192 empty list (user passed --model \"\" to clear)\n // - non-empty \u2192 overwrite with the resolved list\n // The merge is intentionally write-on-write: the user's\n // explicit --model value always wins over any pre-existing\n // models entry. To preserve an existing list, simply omit the\n // flag.\n if (resolvedModels !== null) {\n p.models = [...resolvedModels];\n }\n\n if (apiKey) {\n const list = normalizeKeys(p);\n list.push({ label: finalLabel, apiKey, createdAt: nowIso() });\n writeKeysBack(p, list);\n if (!p.activeKey) p.activeKey = finalLabel;\n }\n all[chosen.id] = p;\n }, deps.profileConfigPath);\n } catch (err) {\n deps.renderer.writeError(`Failed to save ${chosen.id}: ${(err as Error).message}`);\n return 1;\n }\n\n // \u2500\u2500 Audit-log emit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // The `decideAuthLocalEvents` helper is a pure function that\n // maps (previous state, new state, probe outcome) \u2192 event\n // list. The lifecycle events mirror the WebUI's\n // `provider.clear_models` / `provider.undo_clear` message\n // types so the two surfaces share an audit-log vocabulary.\n const events: AuthAuditEvent[] = decideAuthLocalEvents({\n providerId: chosen.id,\n baseUrl,\n previousModels,\n newModels: resolvedModels,\n keyLabel: apiKey ? finalLabel : undefined,\n probeFailedSave: probeFailedAndSaved && probe\n ? {\n status: probe.status,\n ...(probe.detail !== undefined ? { detail: probe.detail } : {}),\n }\n : undefined,\n });\n for (const event of events) {\n audit.emit(event);\n }\n\n if (apiKey) {\n deps.renderer.write(\n ` ${color.green('\u2713')} Saved ${color.bold(chosen.id)}/${color.bold(finalLabel)} ` +\n `\u2192 ${color.cyan(baseUrl)}\\n`,\n );\n } else {\n deps.renderer.write(\n ` ${color.green('\u2713')} Saved ${color.bold(chosen.id)} ` +\n `(no key) \u2192 ${color.cyan(baseUrl)}\\n`,\n );\n }\n\n // When --model resolved to a non-empty list, the user can copy-paste\n // the first id straight into --model. When the resolver returned\n // null (no flag passed) or [] (empty literal), keep the placeholder\n // hint so the UX stays informative.\n const firstModel = resolvedModels && resolvedModels.length > 0 ? resolvedModels[0] : null;\n const modelHint = firstModel\n ? color.cyan(firstModel)\n : color.dim('<model-id>');\n\n deps.renderer.write(\n color.dim(\n ` Launch: wstack --provider ${chosen.id} --model ${modelHint} \"<task>\"\\n`,\n ),\n );\n return 0;\n}\n\n/**\n * Resolve the user-supplied `models` option against the probe result.\n *\n * - `undefined` \u2192 null (don't write `cfg.models`)\n * - `'first'` \u2192 first probe id, or null if probe failed\n * - `<positive int>` \u2192 first N probe ids, or null if probe failed\n * - literal csv \u2192 parsed list, ignoring the probe (the\n * user knows exactly which ids they want)\n *\n * Returns the resolved id list, or null when no `models` should be\n * written. Each entry is scrubbed before return \u2014 the user's literal\n * list could in theory contain a credential if they pasted it from\n * a config dump; better to scrub once here than risk echoing it back.\n */\nexport function resolveModelList(\n models: string | undefined,\n probe: ProbeResult | undefined,\n scrubber: SecretScrubber,\n): string[] | null {\n if (models === undefined) return null;\n\n // The literal-list path: a positive integer is treated as a count\n // when the probe succeeded, otherwise we fall through to the literal\n // parser below. This keeps `'5'` from being interpreted as a model\n // id named \"5\" when a probe result is available.\n const trimmed = models.trim();\n if (trimmed.length === 0) return [];\n\n // The two probe-driven shapes need a successful probe with at least\n // one model id.\n const hasProbeIds = probe?.status === 'ok' && (probe.modelIds?.length ?? 0) > 0;\n const probeIds = hasProbeIds ? (probe?.modelIds ?? []) : [];\n\n if (trimmed === 'first') {\n if (probeIds.length === 0) return null;\n return [scrubber.scrub(probeIds[0]!)];\n }\n if (/^\\d+$/.test(trimmed)) {\n if (probeIds.length === 0) return null;\n const n = Math.min(Number.parseInt(trimmed, 10), probeIds.length);\n return probeIds.slice(0, n).map((id) => scrubber.scrub(id));\n }\n\n // Literal CSV: split, trim, dedupe, scrub each, drop empties.\n const seen = new Set<string>();\n const out: string[] = [];\n for (const raw of trimmed.split(',')) {\n const id = scrubber.scrub(raw.trim());\n if (id.length === 0) continue;\n if (seen.has(id)) continue;\n seen.add(id);\n out.push(id);\n }\n return out;\n}\n\n/**\n * Render the probe result with consistent color/formatting. Always\n * passes the rendered string through the scrubber before writing.\n */\nfunction renderProbeResult(\n deps: AuthMenuDeps,\n probe: ProbeResult,\n opts: { presetLabel?: string } = {},\n): void {\n const label = opts.presetLabel ? ` ${color.bold(opts.presetLabel)}` : '';\n const elapsed = probe.elapsedMs !== undefined ? ` ${color.dim(`(${probe.elapsedMs}ms)`)}` : '';\n\n switch (probe.status) {\n case 'ok': {\n const models =\n probe.modelCount !== undefined\n ? color.dim(`, ${probe.modelCount} model${probe.modelCount === 1 ? '' : 's'}`)\n : '';\n deps.renderer.write(\n ` ${color.green('\u25CF')} ${label} health probe ok${models}${elapsed}\\n`,\n );\n break;\n }\n case 'timeout': {\n deps.renderer.write(\n ` ${color.red('\u25CF')} ${label} health probe timed out${elapsed}\\n` +\n ` ${color.dim('The server did not respond within the timeout.')}\\n` +\n ` ${color.dim('Is the local server running? Check the port and try again.')}\\n`,\n );\n break;\n }\n case 'unreachable': {\n const detail = probe.detail ? ` ${color.dim(`(${probe.detail})`)}` : '';\n deps.renderer.write(\n ` ${color.red('\u25CF')} ${label} server unreachable${detail}\\n` +\n ` ${color.dim('Could not connect. Common causes:')}\\n` +\n ` ${color.dim(' - The local server is not running')}\\n` +\n ` ${color.dim(' - The port is blocked by a firewall')}\\n` +\n ` ${color.dim(' - The base URL is wrong')}\\n`,\n );\n break;\n }\n case 'http_error': {\n const status = probe.httpStatus !== undefined ? ` HTTP ${probe.httpStatus}` : '';\n const detail = probe.detail ? ` ${color.dim(`\u2014 ${probe.detail}`)}` : '';\n deps.renderer.write(\n ` ${color.yellow('\u25CF')} ${label} health probe got${status}${detail}\\n`,\n );\n break;\n }\n case 'invalid_response': {\n deps.renderer.write(\n ` ${color.yellow('\u25CF')} ${label} health probe returned an unexpected shape\\n` +\n (probe.detail ? ` ${color.dim(probe.detail)}\\n` : ''),\n );\n break;\n }\n case 'skipped': {\n // Not currently emitted \u2014 kept for completeness in case we\n // wire a flag-driven skip in the future.\n break;\n }\n }\n}\n\n/**\n * After a failed probe, ask the user whether to save the provider\n * anyway. Default answer is N \u2014 so a misclick or piped input doesn't\n * silently create a broken config.\n */\nasync function promptSaveAnyway(deps: AuthMenuDeps): Promise<boolean> {\n const answer = (\n await deps.reader.readLine(\n `\\n${color.amber('?')} ${color.bold('Save anyway?')} ` +\n `${color.dim('[y/N]')}: `,\n )\n )\n .trim()\n .toLowerCase();\n return answer === 'y' || answer === 'yes';\n}\n\n/* -------------------------------------------------------------------------- */\n/* Interactive picker */\n/* -------------------------------------------------------------------------- */\n\n/** Show the picker, return the chosen preset or undefined on cancel. */\nasync function pickLocalPreset(deps: AuthMenuDeps): Promise<LocalLlmPresetEntry | undefined> {\n deps.renderer.write(\n `\\n${color.bold('Local LLM servers')} ${color.dim('\u2014 pick one to pre-fill the base URL')}\\n\\n`,\n );\n\n let idx = 1;\n for (const entry of LOCAL_LLM_PRESETS) {\n const auth = entry.noAuth ? color.dim('(no auth)') : color.dim('(optional auth)');\n deps.renderer.write(\n ` ${color.dim(`${idx}.`.padStart(4))} ${color.bold(entry.label.padEnd(12))} ` +\n `${color.cyan(entry.defaultBaseUrl.padEnd(34))} ${auth}\\n`,\n );\n deps.renderer.write(\n ` ${color.dim(entry.hint)}\\n`,\n );\n idx++;\n }\n\n const answer = (\n await deps.reader.readLine(\n `\\n${color.amber('?')} Pick ${color.dim(`(1-${LOCAL_LLM_PRESETS.length}, q to quit)`)}: `,\n )\n )\n .trim()\n .toLowerCase();\n\n if (!answer || answer === 'q' || answer === 'quit') return undefined;\n\n const num = Number.parseInt(answer, 10);\n if (!Number.isNaN(num) && num >= 1 && num <= LOCAL_LLM_PRESETS.length) {\n return LOCAL_LLM_PRESETS[num - 1];\n }\n const byId = LOCAL_LLM_PRESETS.find((p) => p.id === answer);\n if (byId) return byId;\n\n deps.renderer.writeError(`Unknown selection: \"${answer}\"`);\n return undefined;\n}\n\n/**\n * Prompt for a Bearer key with the option to skip (Enter on an empty\n * line). vLLM and LM Studio both run fine with auth disabled, so an\n * empty submit is a legitimate choice \u2014 it just means the shortcut\n * saves the provider without a key entry.\n */\nasync function promptOptionalKey(\n deps: AuthMenuDeps,\n preset: LocalLlmPresetEntry,\n): Promise<string | undefined> {\n const answer = (\n await deps.reader.readSecret(\n ` ${color.amber('?')} ${color.bold(preset.label)} API key ` +\n `${color.dim('(hidden \u2014 Enter to skip if auth is disabled on the server)')}: `,\n )\n ).trim();\n if (!answer) return undefined;\n return answer;\n}\n\n// Re-exported for tests.\nexport { suggestLabel };\n", "/**\n * Structured audit logger for the `wstack auth local` lifecycle.\n *\n * Emits JSON-line events to an injectable sink (default: stdout).\n * The event names mirror the WebUI's WS message types\n * (`provider.clear_models` \u2194 `auth.local.clear`,\n * `provider.undo_clear` \u2194 `auth.local.undo`) so a downstream\n * tool that filters on one convention finds the corresponding\n * events on the other surface.\n *\n * Why a dedicated logger (vs. `console.log`)?\n * - **Test seam** \u2014 tests inject an in-memory array sink and\n * assert on the recorded events. `console.log` can't be\n * captured cleanly across the test runner / reporter.\n * - **Schema stability** \u2014 the discriminated union of\n * `AuthAuditEvent` types gives downstream consumers a\n * contract they can switch on. Plain `console.log` strings\n * drift on every commit.\n * - **Redaction built in** \u2014 every event passes through a\n * `SecretScrubber` before reaching the sink, so a Bearer\n * token accidentally captured in a payload can't echo to\n * stdout / the audit file in plaintext.\n *\n * The logger is intentionally minimal: no buffering, no\n * rotation, no transport. The CLI process is short-lived and\n * emits at most a handful of events per invocation. A\n * long-running consumer (file rotation, OTLP export) would\n * belong in a dedicated audit-sink module, not here.\n */\nimport type { SecretScrubber } from '@wrongstack/core/types';\nimport { DefaultSecretScrubber } from '@wrongstack/core/security';\n\n/**\n * Discriminated union of every event the auth-menu local flow\n * can emit. New events slot in here as the lifecycle grows \u2014\n * the `type` field is the stable contract.\n */\nexport type AuthAuditEvent =\n | {\n type: 'auth.local.add';\n providerId: string;\n baseUrl: string;\n /** The resolved model list saved to disk. Empty when the\n * user didn't pass `--model`. */\n models: string[];\n /** Label of the saved key entry, if any. */\n keyLabel?: string | undefined;\n }\n | {\n /**\n * Emitted when the save replaces a previously non-empty\n * `cfg.models` allowlist with `[]`. Pairs with\n * `auth.local.undo` \u2014 a search for both events in\n * sequence surfaces a \"user changed their mind\" pair.\n */\n type: 'auth.local.clear';\n providerId: string;\n baseUrl: string;\n /** The list the user just removed. */\n previousModels: string[];\n }\n | {\n /**\n * Emitted when the save restores a non-empty `cfg.models`\n * allowlist that was previously `[]` or absent. The\n * `restoredModels` field carries the new list verbatim.\n * Pairs with `auth.local.clear`.\n */\n type: 'auth.local.undo';\n providerId: string;\n baseUrl: string;\n /** The list the user just restored. */\n restoredModels: string[];\n }\n | {\n /**\n * Emitted when the `--model` flag was probe-driven\n * (`'first'` / `'<N>'`) but the probe didn't return\n * enough ids. The flag is silently ignored in that case\n * \u2014 this event lets the audit log surface the silent\n * skip.\n */\n type: 'auth.local.probe_skip';\n providerId: string;\n baseUrl: string;\n requestedShape: 'first' | string;\n }\n | {\n /**\n * Emitted when the health probe failed AND the user chose\n * to save anyway. Pairs with the probe result line in\n * the terminal \u2014 the audit event is the durable record\n * that the provider was saved in a known-bad state.\n */\n type: 'auth.local.probe_failed_save';\n providerId: string;\n baseUrl: string;\n probeStatus: string;\n probeDetail?: string | undefined;\n };\n\n/**\n * Sink contract: receives the post-scrub, post-stringify event\n * line. The default implementation writes to stdout; tests\n * inject an in-memory array.\n */\nexport interface AuthAuditSink {\n write(line: string): void;\n}\n\n/** Default sink: one JSON line per event on stdout. */\nfunction defaultStdoutSink(): AuthAuditSink {\n // process.stdout.write is the right primitive here \u2014 it\n // bypasses the test runner's `console.log` capture and\n // never throws. Use \\n explicitly; some terminals buffer\n // until they see one.\n return {\n write(line: string) {\n process.stdout.write(`${line}\\n`);\n },\n };\n}\n\n/** Stderr sink: one JSON line per event on stderr. */\nfunction defaultStderrSink(): AuthAuditSink {\n return {\n write(line: string) {\n process.stderr.write(`${line}\\n`);\n },\n };\n}\n\n/**\n * File sink: appends one JSON line per event to the named\n * path. Uses synchronous append + newline so events are\n * durable on process exit. Wraps any I/O failure as a thrown\n * error so the caller can surface it.\n */\nexport function fileAuditSink(path: string): AuthAuditSink {\n // Lazy require so the test suite (which doesn't touch the\n // file sink) doesn't pay the cost of importing `node:fs`\n // just to read the type.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('node:fs');\n return {\n write(line: string) {\n fs.appendFileSync(path, `${line}\\n`, { encoding: 'utf8' });\n },\n };\n}\n\n/**\n * Resolve the `--audit` flag value (parsed by `parseAuthFlags`)\n * to a concrete `AuthAuditSink`. Returns `undefined` when the\n * caller didn't pass `--audit` (no sink is wired and the\n * audit log is silent).\n *\n * Resolution rules:\n * - `true` (bare `--audit`) \u2192 stdout\n * - `'stdout'` \u2192 stdout\n * - `'stderr'` \u2192 stderr\n * - `'<file-path>'` \u2192 append to the file\n * - `undefined` \u2192 undefined (no sink)\n */\nexport function resolveAuditSink(\n flag: boolean | string | undefined,\n): AuthAuditSink | undefined {\n if (flag === undefined) return undefined;\n if (flag === true || flag === 'stdout') return defaultStdoutSink();\n if (flag === 'stderr') return defaultStderrSink();\n // At this point, `flag` is narrowed to `string` \u2014 the\n // boolean literal `true` was handled above and the\n // `string` literals 'stdout' / 'stderr' are also handled.\n // The explicit `typeof` guard documents the narrowing for\n // the reader and silences a residual `boolean` in the\n // union (TS doesn't always narrow `boolean | string`\n // through `=== literal` chains).\n if (typeof flag !== 'string') return undefined;\n return fileAuditSink(flag);\n}\n\n/** In-memory sink for tests. Records every line verbatim. */\nexport function memAuthAuditSink(): AuthAuditSink & { lines: string[] } {\n const lines: string[] = [];\n return {\n lines,\n write(line: string) {\n lines.push(line);\n },\n };\n}\n\nexport interface AuthAuditLogger {\n /** Emit an event. The payload is JSON-serialized, scrubbed,\n * and written to the sink. No-ops when the sink is\n * `undefined` (tests that don't care about the audit log\n * can pass `audit: undefined`). */\n emit(event: AuthAuditEvent): void;\n}\n\n/** No-op logger for tests that don't need the audit log. */\nexport const NOOP_AUDIT_LOGGER: AuthAuditLogger = {\n emit: () => {},\n};\n\n/**\n * Build the canonical logger. The `scrubber` runs over the\n * serialized event so a Bearer token accidentally included\n * in a payload (e.g. an echo from a misbehaving server) is\n * redacted before reaching the sink.\n */\nexport function createAuthAuditLogger(\n sink: AuthAuditSink | undefined,\n scrubber: SecretScrubber = new DefaultSecretScrubber(),\n): AuthAuditLogger {\n if (!sink) return NOOP_AUDIT_LOGGER;\n return {\n emit(event) {\n // Scrub first, then serialize. Scrubbing the object\n // directly is more reliable than scrubbing the\n // serialized line (a value could contain a quote that\n // looks like a credential when serialized).\n const scrubbed = scrubEvent(event, scrubber);\n sink.write(JSON.stringify(scrubbed));\n },\n };\n}\n\n/**\n * Run the scrubber over every string leaf of the event\n * payload. The `type` discriminator is left intact (it's a\n * contract, not data). Nested arrays (`models`,\n * `previousModels`, `restoredModels`) are scrubbed\n * element-by-element.\n */\nfunction scrubEvent(\n event: AuthAuditEvent,\n scrubber: SecretScrubber,\n): AuthAuditEvent {\n switch (event.type) {\n case 'auth.local.add':\n return {\n type: 'auth.local.add',\n providerId: scrubber.scrub(event.providerId),\n baseUrl: scrubber.scrub(event.baseUrl),\n models: event.models.map((m) => scrubber.scrub(m)),\n ...(event.keyLabel !== undefined\n ? { keyLabel: scrubber.scrub(event.keyLabel) }\n : {}),\n };\n case 'auth.local.clear':\n return {\n type: 'auth.local.clear',\n providerId: scrubber.scrub(event.providerId),\n baseUrl: scrubber.scrub(event.baseUrl),\n previousModels: event.previousModels.map((m) => scrubber.scrub(m)),\n };\n case 'auth.local.undo':\n return {\n type: 'auth.local.undo',\n providerId: scrubber.scrub(event.providerId),\n baseUrl: scrubber.scrub(event.baseUrl),\n restoredModels: event.restoredModels.map((m) => scrubber.scrub(m)),\n };\n case 'auth.local.probe_skip':\n return {\n type: 'auth.local.probe_skip',\n providerId: scrubber.scrub(event.providerId),\n baseUrl: scrubber.scrub(event.baseUrl),\n requestedShape: scrubber.scrub(event.requestedShape),\n };\n case 'auth.local.probe_failed_save':\n return {\n type: 'auth.local.probe_failed_save',\n providerId: scrubber.scrub(event.providerId),\n baseUrl: scrubber.scrub(event.baseUrl),\n probeStatus: scrubber.scrub(event.probeStatus),\n ...(event.probeDetail !== undefined\n ? { probeDetail: scrubber.scrub(event.probeDetail) }\n : {}),\n };\n }\n}\n\n/**\n * Decide which audit event(s) to emit given the previous saved\n * state and the new save's resolved values. Pure function \u2014\n * the caller is responsible for the actual `emit()` call so\n * the same input always produces the same event list (and the\n * test suite can pin it).\n *\n * - `previousModels` is `undefined` when no prior entry\n * existed. `[]` means \"the user previously cleared\".\n * - `newModels` is `null` when the `--model` flag wasn't\n * passed (don't touch `cfg.models`). `[]` means \"the user\n * passed `--model ''` to clear\". A non-empty list is the\n * resolved list (probe-driven or literal).\n */\nexport function decideAuthLocalEvents(opts: {\n providerId: string;\n baseUrl: string;\n previousModels: string[] | undefined;\n newModels: string[] | null;\n keyLabel: string | undefined;\n probeSkip?: { shape: 'first' | string } | undefined;\n probeFailedSave?: { status: string; detail?: string } | undefined;\n}): AuthAuditEvent[] {\n const events: AuthAuditEvent[] = [];\n\n // The probe-skip and probe-failed-save events are emitted\n // unconditionally when they apply (the caller decides based\n // on the runtime probe result).\n if (opts.probeSkip) {\n events.push({\n type: 'auth.local.probe_skip',\n providerId: opts.providerId,\n baseUrl: opts.baseUrl,\n requestedShape: opts.probeSkip.shape,\n });\n }\n if (opts.probeFailedSave) {\n events.push({\n type: 'auth.local.probe_failed_save',\n providerId: opts.providerId,\n baseUrl: opts.baseUrl,\n probeStatus: opts.probeFailedSave.status,\n ...(opts.probeFailedSave.detail !== undefined\n ? { probeDetail: opts.probeFailedSave.detail }\n : {}),\n });\n }\n\n // The add/clear/undo detection runs only when the user\n // actually wrote `models` (i.e. the --model flag was\n // passed). When the flag is omitted we don't change the\n // allowlist, so no lifecycle event.\n if (opts.newModels === null) return events;\n\n const hadPriorAllowlist =\n opts.previousModels !== undefined && opts.previousModels.length > 0;\n\n if (hadPriorAllowlist && opts.newModels.length === 0) {\n // Non-empty \u2192 empty: a clear.\n events.push({\n type: 'auth.local.clear',\n providerId: opts.providerId,\n baseUrl: opts.baseUrl,\n previousModels: opts.previousModels!,\n });\n return events;\n }\n\n if (!hadPriorAllowlist && opts.newModels.length > 0) {\n // No prior allowlist (either undefined or []) \u2192 non-empty\n // list: a fresh add OR an undo (we can't distinguish a\n // \"user is configuring for the first time\" add from a\n // \"user just cleared and is now restoring\" undo from\n // disk state alone \u2014 the only signal is the prior state\n // being literally [] vs. undefined).\n if (opts.previousModels !== undefined) {\n // prior was [] (or some other empty state we\n // collapsed). This is an undo.\n events.push({\n type: 'auth.local.undo',\n providerId: opts.providerId,\n baseUrl: opts.baseUrl,\n restoredModels: [...opts.newModels],\n });\n } else {\n events.push({\n type: 'auth.local.add',\n providerId: opts.providerId,\n baseUrl: opts.baseUrl,\n models: [...opts.newModels],\n ...(opts.keyLabel !== undefined ? { keyLabel: opts.keyLabel } : {}),\n });\n }\n return events;\n }\n\n if (hadPriorAllowlist && opts.newModels.length > 0) {\n // Non-empty \u2192 non-empty: the user is overwriting an\n // existing allowlist. Not strictly a clear+undo \u2014 emit a\n // plain add so the audit log records the new state.\n events.push({\n type: 'auth.local.add',\n providerId: opts.providerId,\n baseUrl: opts.baseUrl,\n models: [...opts.newModels],\n ...(opts.keyLabel !== undefined ? { keyLabel: opts.keyLabel } : {}),\n });\n return events;\n }\n\n // Both empty (no prior allowlist, --model ''): the user\n // explicitly cleared an already-empty allowlist. No-op\n // for the audit log \u2014 no state change.\n return events;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["color", "color", "FetchError", "ParseError", "CLIENT_ID", "openBrowser", "FetchError", "ParseError", "color", "color", "color", "color", "color", "color", "color", "DefaultSecretScrubber", "DefaultSecretScrubber", "color"]
7
+ }
@@ -0,0 +1,100 @@
1
+
2
+ // src/services/project-manifest.ts
3
+ import { createHash } from "node:crypto";
4
+ import * as fs from "node:fs/promises";
5
+ import * as path from "node:path";
6
+ import { ConfigError } from "@wrongstack/core/types";
7
+ import { readProjectIdentity, withFileLock, wstackGlobalRoot } from "@wrongstack/core/utils";
8
+ function projectsJsonPath(globalConfigPath) {
9
+ const base = globalConfigPath ? path.dirname(globalConfigPath) : wstackGlobalRoot();
10
+ return path.join(base, "projects.json");
11
+ }
12
+ function projectsDataDir(globalConfigPath) {
13
+ const base = globalConfigPath ? path.dirname(globalConfigPath) : wstackGlobalRoot();
14
+ return path.join(base, "projects");
15
+ }
16
+ async function loadManifest(globalConfigPath) {
17
+ const file = projectsJsonPath(globalConfigPath);
18
+ try {
19
+ const raw = await fs.readFile(file, "utf8");
20
+ const parsed = JSON.parse(raw);
21
+ return { projects: parsed.projects ?? [] };
22
+ } catch {
23
+ return { projects: [] };
24
+ }
25
+ }
26
+ async function saveManifest(manifest, globalConfigPath) {
27
+ const file = projectsJsonPath(globalConfigPath);
28
+ await fs.mkdir(path.dirname(file), { recursive: true });
29
+ await fs.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
30
+ }
31
+ function generateSlug(root) {
32
+ const base = path.basename(root).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
33
+ const hash = createHash("sha256").update(path.resolve(root)).digest("hex").slice(0, 6);
34
+ return `${base}-${hash}`;
35
+ }
36
+ function findProject(manifest, query) {
37
+ const lower = query.toLowerCase();
38
+ let found = manifest.projects.find((p) => p.slug === lower);
39
+ if (found) return found;
40
+ found = manifest.projects.find((p) => p.name.toLowerCase() === lower);
41
+ if (found) return found;
42
+ found = manifest.projects.find((p) => p.root.toLowerCase().endsWith(lower));
43
+ if (found) return found;
44
+ found = manifest.projects.find((p) => p.root.toLowerCase().includes(lower));
45
+ return found;
46
+ }
47
+ async function ensureProjectDataDir(slug, globalConfigPath) {
48
+ const dir = path.join(projectsDataDir(globalConfigPath), slug);
49
+ await fs.mkdir(dir, { recursive: true });
50
+ return dir;
51
+ }
52
+ async function touchProjectInManifest(opts) {
53
+ const root = path.resolve(opts.projectRoot);
54
+ const projectId = (await readProjectIdentity(root).catch(() => void 0))?.projectId;
55
+ const file = projectsJsonPath(opts.globalConfigPath);
56
+ let entry;
57
+ await withFileLock(file, async () => {
58
+ const manifest = await loadManifest(opts.globalConfigPath);
59
+ const now = (/* @__PURE__ */ new Date()).toISOString();
60
+ entry = manifest.projects.find((p) => path.resolve(p.root) === root);
61
+ if (entry) {
62
+ entry.lastSeen = now;
63
+ if (projectId) entry.projectId = projectId;
64
+ if (opts.workingDir) entry.lastWorkingDir = path.resolve(opts.workingDir);
65
+ } else {
66
+ entry = {
67
+ name: opts.name ?? path.basename(root),
68
+ root,
69
+ slug: generateSlug(root),
70
+ projectId,
71
+ createdAt: now,
72
+ lastSeen: now,
73
+ lastWorkingDir: opts.workingDir ? path.resolve(opts.workingDir) : void 0
74
+ };
75
+ manifest.projects.push(entry);
76
+ }
77
+ await saveManifest(manifest, opts.globalConfigPath);
78
+ });
79
+ await ensureProjectDataDir(expectEntry(entry).slug, opts.globalConfigPath);
80
+ return expectEntry(entry);
81
+ }
82
+ function expectEntry(e) {
83
+ if (!e)
84
+ throw new ConfigError({
85
+ message: "touchProjectInManifest: entry not resolved",
86
+ code: "CONFIG_INVALID",
87
+ context: { phase: "manifest-resolve" }
88
+ });
89
+ return e;
90
+ }
91
+
92
+ export {
93
+ loadManifest,
94
+ saveManifest,
95
+ generateSlug,
96
+ findProject,
97
+ ensureProjectDataDir,
98
+ touchProjectInManifest
99
+ };
100
+ //# sourceMappingURL=chunk-JUHRI6OV.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/services/project-manifest.ts"],
4
+ "sourcesContent": ["import { createHash } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { ConfigError } from '@wrongstack/core/types';\nimport { readProjectIdentity, withFileLock, wstackGlobalRoot } from '@wrongstack/core/utils';\n\n// Shared project-manifest types and persistence.\n\nexport interface ProjectEntry {\n /** User-friendly name (defaults to dirname, can be renamed). */\n name: string;\n /** Absolute path to the project root. */\n root: string;\n /** Stable unique slug (dirname-hash) for per-project data storage. */\n slug: string;\n /** Repo-committed identity shared by clones, worktrees, and machines. */\n projectId?: string | undefined;\n /** ISO timestamp of last use. */\n lastSeen?: string | undefined;\n /** ISO timestamp of when the project was first registered. */\n createdAt?: string | undefined;\n /** Working directory of the most recent session (may differ from root). */\n lastWorkingDir?: string | undefined;\n}\n\nexport interface ProjectsManifest {\n projects: ProjectEntry[];\n}\n\n// \u2500\u2500 Path resolution \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction projectsJsonPath(globalConfigPath?: string | undefined): string {\n const base = globalConfigPath ? path.dirname(globalConfigPath) : wstackGlobalRoot();\n return path.join(base, 'projects.json');\n}\n\nfunction projectsDataDir(globalConfigPath?: string | undefined): string {\n const base = globalConfigPath ? path.dirname(globalConfigPath) : wstackGlobalRoot();\n return path.join(base, 'projects');\n}\n\n// \u2500\u2500 Read / Write manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport async function loadManifest(\n globalConfigPath?: string | undefined,\n): Promise<ProjectsManifest> {\n const file = projectsJsonPath(globalConfigPath);\n try {\n const raw = await fs.readFile(file, 'utf8');\n const parsed = JSON.parse(raw) as ProjectsManifest;\n return { projects: parsed.projects ?? [] };\n } catch {\n return { projects: [] };\n }\n}\n\nexport async function saveManifest(\n manifest: ProjectsManifest,\n globalConfigPath?: string | undefined,\n): Promise<void> {\n const file = projectsJsonPath(globalConfigPath);\n await fs.mkdir(path.dirname(file), { recursive: true });\n await fs.writeFile(file, JSON.stringify(manifest, null, 2), 'utf8');\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Generate a unique slug from a project root path.\n */\nexport function generateSlug(root: string): string {\n const base =\n path\n .basename(root)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40) || 'project';\n const hash = createHash('sha256').update(path.resolve(root)).digest('hex').slice(0, 6);\n return `${base}-${hash}`;\n}\n\n/**\n * Find a project entry by slug, name, or root (partial match).\n */\nexport function findProject(manifest: ProjectsManifest, query: string): ProjectEntry | undefined {\n const lower = query.toLowerCase();\n // Exact slug match first\n let found = manifest.projects.find((p) => p.slug === lower);\n if (found) return found;\n // Name match\n found = manifest.projects.find((p) => p.name.toLowerCase() === lower);\n if (found) return found;\n // Root ends-with match\n found = manifest.projects.find((p) => p.root.toLowerCase().endsWith(lower));\n if (found) return found;\n // Root contains match\n found = manifest.projects.find((p) => p.root.toLowerCase().includes(lower));\n return found;\n}\n\n/**\n * Ensure the per-project data directory exists under ~/.wrongstack/projects/<slug>/.\n */\nexport async function ensureProjectDataDir(\n slug: string,\n globalConfigPath?: string | undefined,\n): Promise<string> {\n const dir = path.join(projectsDataDir(globalConfigPath), slug);\n await fs.mkdir(dir, { recursive: true });\n return dir;\n}\n\n/**\n * Idempotent project registration: ensure `projectRoot` has an entry in\n * projects.json, creating one when missing and refreshing `lastSeen` /\n * `lastWorkingDir` when present. Returns the (created or updated) entry.\n *\n * Every surface that opens a project (CLI/TUI boot, standalone WebUI boot,\n * WebUI projects.select) funnels through this so the manifest is the single\n * source of truth for \"which projects exist\" regardless of entry point.\n * Concurrent processes booting on the same machine serialize via a file\n * lock around the read-modify-write.\n */\nexport async function touchProjectInManifest(opts: {\n projectRoot: string;\n globalConfigPath?: string | undefined;\n /** Working dir of this session when it differs from the root. */\n workingDir?: string | undefined;\n /** Friendly name for a NEWLY created entry (default: basename). */\n name?: string | undefined;\n}): Promise<ProjectEntry> {\n const root = path.resolve(opts.projectRoot);\n const projectId = (await readProjectIdentity(root).catch(() => undefined))?.projectId;\n const file = projectsJsonPath(opts.globalConfigPath);\n let entry: ProjectEntry | undefined;\n await withFileLock(file, async () => {\n const manifest = await loadManifest(opts.globalConfigPath);\n const now = new Date().toISOString();\n entry = manifest.projects.find((p) => path.resolve(p.root) === root);\n if (entry) {\n entry.lastSeen = now;\n if (projectId) entry.projectId = projectId;\n if (opts.workingDir) entry.lastWorkingDir = path.resolve(opts.workingDir);\n } else {\n entry = {\n name: opts.name ?? path.basename(root),\n root,\n slug: generateSlug(root),\n projectId,\n createdAt: now,\n lastSeen: now,\n lastWorkingDir: opts.workingDir ? path.resolve(opts.workingDir) : undefined,\n };\n manifest.projects.push(entry);\n }\n await saveManifest(manifest, opts.globalConfigPath);\n });\n await ensureProjectDataDir(expectEntry(entry).slug, opts.globalConfigPath);\n return expectEntry(entry);\n}\n\nfunction expectEntry(e: ProjectEntry | undefined): ProjectEntry {\n if (!e)\n throw new ConfigError({\n message: 'touchProjectInManifest: entry not resolved',\n code: 'CONFIG_INVALID',\n context: { phase: 'manifest-resolve' },\n });\n return e;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,142 @@
1
+
2
+ // src/hq-command-controller.ts
3
+ function createHqCommandDispatcher(controller) {
4
+ return async (command) => {
5
+ const cmd = command;
6
+ try {
7
+ const result = await dispatch(controller, cmd.commandId, cmd.type, cmd.payload);
8
+ return result;
9
+ } catch (err) {
10
+ return {
11
+ commandId: command.commandId,
12
+ status: "failed",
13
+ message: err instanceof Error ? err.message : String(err)
14
+ };
15
+ }
16
+ };
17
+ }
18
+ async function dispatch(controller, commandId, type, payload) {
19
+ switch (type) {
20
+ case "steer": {
21
+ const mailbox = controller.steerMailbox;
22
+ if (mailbox === void 0) {
23
+ return { commandId, status: "rejected", message: "no mailbox available" };
24
+ }
25
+ const to = typeof payload["to"] === "string" ? payload["to"] : "leader";
26
+ const subject = typeof payload["subject"] === "string" ? payload["subject"] : "HQ steer";
27
+ const body = typeof payload["body"] === "string" ? payload["body"] : "";
28
+ const priority = payload["priority"] === "high" ? "high" : payload["priority"] === "low" ? "low" : "normal";
29
+ const from = `hq@${controller.sessionTag()}`;
30
+ await mailbox.send({ from, to, type: "steer", subject, body, priority });
31
+ return { commandId, status: "accepted", message: `steered ${to}` };
32
+ }
33
+ case "btw": {
34
+ const mailbox = controller.steerMailbox;
35
+ if (mailbox === void 0) {
36
+ return { commandId, status: "rejected", message: "no mailbox available" };
37
+ }
38
+ const to = typeof payload["to"] === "string" ? payload["to"] : "leader";
39
+ const subject = typeof payload["subject"] === "string" ? payload["subject"] : "HQ note";
40
+ const body = typeof payload["body"] === "string" ? payload["body"] : "";
41
+ const priority = payload["priority"] === "high" ? "high" : payload["priority"] === "low" ? "low" : "normal";
42
+ const from = `hq@${controller.sessionTag()}`;
43
+ await mailbox.send({ from, to, type: "btw", subject, body, priority });
44
+ return { commandId, status: "accepted", message: `noted ${to}` };
45
+ }
46
+ case "queue": {
47
+ const mailbox = controller.steerMailbox;
48
+ if (mailbox === void 0) {
49
+ return { commandId, status: "rejected", message: "no mailbox available" };
50
+ }
51
+ const to = typeof payload["to"] === "string" ? payload["to"] : "leader";
52
+ const subject = typeof payload["subject"] === "string" ? payload["subject"] : "HQ prompt";
53
+ const body = typeof payload["body"] === "string" ? payload["body"] : "";
54
+ const priority = payload["priority"] === "high" ? "high" : payload["priority"] === "low" ? "low" : "normal";
55
+ const from = `hq@${controller.sessionTag()}`;
56
+ await mailbox.send({ from, to, type: "note", subject, body, priority });
57
+ return { commandId, status: "accepted", message: `queued for ${to}` };
58
+ }
59
+ case "broadcast": {
60
+ const mailbox = controller.steerMailbox;
61
+ if (mailbox === void 0) {
62
+ return { commandId, status: "rejected", message: "no mailbox available" };
63
+ }
64
+ const subject = typeof payload["subject"] === "string" ? payload["subject"] : "HQ broadcast";
65
+ const body = typeof payload["body"] === "string" ? payload["body"] : "";
66
+ const priority = payload["priority"] === "high" ? "high" : payload["priority"] === "low" ? "low" : "normal";
67
+ const from = `hq@${controller.sessionTag()}`;
68
+ await mailbox.send({ from, to: "all", type: "broadcast", subject, body, priority });
69
+ return { commandId, status: "accepted", message: "broadcast sent" };
70
+ }
71
+ case "abort": {
72
+ const target = typeof payload["target"] === "string" ? payload["target"] : "leader";
73
+ if (target === "leader") {
74
+ const aborted = controller.interruptLeader();
75
+ return {
76
+ commandId,
77
+ status: aborted ? "completed" : "accepted",
78
+ message: aborted ? "leader aborted" : "no active leader run"
79
+ };
80
+ }
81
+ if (target === "fleet") {
82
+ const killed = await controller.killFleet?.() ?? 0;
83
+ return {
84
+ commandId,
85
+ status: "completed",
86
+ message: `fleet stopped (${killed} agents)`
87
+ };
88
+ }
89
+ const terminated = await controller.terminateAgent?.(target) ?? false;
90
+ return {
91
+ commandId,
92
+ status: terminated ? "completed" : "rejected",
93
+ message: terminated ? `agent ${target} terminated` : `agent ${target} not found`
94
+ };
95
+ }
96
+ case "spawn": {
97
+ const role = typeof payload["role"] === "string" ? payload["role"] : "";
98
+ const task = typeof payload["task"] === "string" ? payload["task"] : void 0;
99
+ const maxIterations = typeof payload["maxIterations"] === "number" ? payload["maxIterations"] : void 0;
100
+ if (controller.spawnAgent === void 0) {
101
+ return { commandId, status: "rejected", message: "no director active" };
102
+ }
103
+ const subagentId = await controller.spawnAgent(role, task, maxIterations);
104
+ return { commandId, status: "completed", message: `spawned ${subagentId}` };
105
+ }
106
+ case "run-command": {
107
+ if (!controller.allowRunCommand()) {
108
+ return {
109
+ commandId,
110
+ status: "rejected",
111
+ message: "run-command requires operator opt-in (--hq-allow-exec)"
112
+ };
113
+ }
114
+ const mailbox = controller.steerMailbox;
115
+ const command = typeof payload["command"] === "string" ? payload["command"] : "";
116
+ if (mailbox !== void 0 && command.length > 0) {
117
+ const from = `hq@${controller.sessionTag()}`;
118
+ await mailbox.send({
119
+ from,
120
+ to: "leader",
121
+ type: "steer",
122
+ subject: "Run command (HQ)",
123
+ body: `Run the following shell command:
124
+
125
+ \`\`\`
126
+ ${command}
127
+ \`\`\``,
128
+ priority: "high"
129
+ });
130
+ return { commandId, status: "accepted", message: "run-command routed to leader as steer" };
131
+ }
132
+ return { commandId, status: "rejected", message: "cannot route run-command (no mailbox)" };
133
+ }
134
+ default:
135
+ return { commandId, status: "rejected", message: `unknown command type: ${type}` };
136
+ }
137
+ }
138
+
139
+ export {
140
+ createHqCommandDispatcher
141
+ };
142
+ //# sourceMappingURL=chunk-OP47B3IX.js.map