zidane 9.1.1 → 9.1.4

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 (279) hide show
  1. package/dist/{acp-Di9WvMDF.js → acp-Cl4gCZN6.js} +7 -7
  2. package/dist/{acp-Di9WvMDF.js.map → acp-Cl4gCZN6.js.map} +1 -1
  3. package/dist/acp-cli.js +5 -5
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-DJhpTS-B.js → agent-Dh_XZ7fj.js} +74 -19
  7. package/dist/agent-Dh_XZ7fj.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{auth-store-port-DFgf22oo.js → auth-store-port-DBl2JUC6.js} +2 -2
  11. package/dist/{auth-store-port-DFgf22oo.js.map → auth-store-port-DBl2JUC6.js.map} +1 -1
  12. package/dist/{auto-update-restart-BFB7Ek87.js → auto-update-restart-CF_kjcJd.js} +277 -41
  13. package/dist/auto-update-restart-CF_kjcJd.js.map +1 -0
  14. package/dist/{browser-BcoCCB_n.js → browser-BkdeKnxO.js} +56 -2
  15. package/dist/browser-BkdeKnxO.js.map +1 -0
  16. package/dist/{browser-entry-ypi8CyE-.d.ts → browser-entry-BCwruqNa.d.ts} +10 -10
  17. package/dist/{browser-entry-ypi8CyE-.d.ts.map → browser-entry-BCwruqNa.d.ts.map} +1 -1
  18. package/dist/channels.d.ts +5 -5
  19. package/dist/chat/browser.d.ts +4 -4
  20. package/dist/chat/browser.js +6 -6
  21. package/dist/chat/pure.d.ts +5 -5
  22. package/dist/chat/pure.js +8 -8
  23. package/dist/chat.d.ts +5 -5
  24. package/dist/chat.js +55 -55
  25. package/dist/chat.js.map +1 -1
  26. package/dist/{cli-shared-CabruH8Z.js → cli-shared-DIV9c5Nr.js} +4 -4
  27. package/dist/{cli-shared-CabruH8Z.js.map → cli-shared-DIV9c5Nr.js.map} +1 -1
  28. package/dist/cli-style-DBEm6CyT.js +105 -0
  29. package/dist/cli-style-DBEm6CyT.js.map +1 -0
  30. package/dist/{client-core-CIixwznM.d.ts → client-core-C_4m_oL6.d.ts} +4 -4
  31. package/dist/{client-core-CIixwznM.d.ts.map → client-core-C_4m_oL6.d.ts.map} +1 -1
  32. package/dist/{client-CCaIXWue.d.ts → client-ygTASGis.d.ts} +2 -2
  33. package/dist/{client-CCaIXWue.d.ts.map → client-ygTASGis.d.ts.map} +1 -1
  34. package/dist/codemode-worker.js +8 -5
  35. package/dist/codemode-worker.js.map +1 -1
  36. package/dist/codemode.d.ts +16 -0
  37. package/dist/codemode.d.ts.map +1 -1
  38. package/dist/codemode.js +2 -2
  39. package/dist/{connection-loop-QmWL-fJf.d.ts → connection-loop-D_pRONCr.d.ts} +4 -4
  40. package/dist/{connection-loop-QmWL-fJf.d.ts.map → connection-loop-D_pRONCr.d.ts.map} +1 -1
  41. package/dist/contexts/daytona.d.ts +3 -3
  42. package/dist/contexts/docker.d.ts +1 -1
  43. package/dist/contexts/e2b.d.ts +2 -2
  44. package/dist/contexts/sandbox.d.ts +1 -1
  45. package/dist/{contexts-C72b0mMa.js → contexts-DLZTSRHU.js} +17 -106
  46. package/dist/contexts-DLZTSRHU.js.map +1 -0
  47. package/dist/contexts.d.ts +4 -4
  48. package/dist/contexts.js +2 -2
  49. package/dist/{credentials-wwETW8rI.js → credentials-D71duF64.js} +2 -2
  50. package/dist/{credentials-wwETW8rI.js.map → credentials-D71duF64.js.map} +1 -1
  51. package/dist/{cwd-picker-CcekODCt.js → cwd-picker-BsHA6HKv.js} +4 -4
  52. package/dist/{cwd-picker-CcekODCt.js.map → cwd-picker-BsHA6HKv.js.map} +1 -1
  53. package/dist/daemon-cli.js +4 -3
  54. package/dist/daemon-cli.js.map +1 -1
  55. package/dist/{decisions-CXmK0nz9.js → decisions-hsw5XS2g.js} +3 -1
  56. package/dist/decisions-hsw5XS2g.js.map +1 -0
  57. package/dist/{discovery-DycXFxdA.js → discovery-DD4wPWKS.js} +4 -4
  58. package/dist/{discovery-DycXFxdA.js.map → discovery-DD4wPWKS.js.map} +1 -1
  59. package/dist/durable/sqlite.d.ts +2 -2
  60. package/dist/durable.d.ts +1 -1
  61. package/dist/{engine-VQ86XGdG.js → engine-BDmtgr2O.js} +6 -6
  62. package/dist/{engine-VQ86XGdG.js.map → engine-BDmtgr2O.js.map} +1 -1
  63. package/dist/{eval-Bod0I1W_.js → eval-CfGUDAkN.js} +3 -3
  64. package/dist/{eval-Bod0I1W_.js.map → eval-CfGUDAkN.js.map} +1 -1
  65. package/dist/eval.d.ts +1 -1
  66. package/dist/eval.js +1 -1
  67. package/dist/{executor-C7RQrkEq.js → executor-EWOhLyd9.js} +4 -4
  68. package/dist/{executor-C7RQrkEq.js.map → executor-EWOhLyd9.js.map} +1 -1
  69. package/dist/{extension-mark-BqKdEZwF.js → extension-mark-_rhkDWL5.js} +9 -6
  70. package/dist/extension-mark-_rhkDWL5.js.map +1 -0
  71. package/dist/extensions/testing.d.ts +1 -1
  72. package/dist/extensions/testing.js +2 -2
  73. package/dist/{extensions-BCx4QuGB.js → extensions-DklXPjiq.js} +8 -8
  74. package/dist/{extensions-BCx4QuGB.js.map → extensions-DklXPjiq.js.map} +1 -1
  75. package/dist/{extensions-client-D8tEAcMw.d.ts → extensions-client-D4Q2XRKV.d.ts} +31 -5
  76. package/dist/extensions-client-D4Q2XRKV.d.ts.map +1 -0
  77. package/dist/extensions.d.ts +1 -1
  78. package/dist/extensions.js +6 -6
  79. package/dist/{fold-rules-DvLK8pbf.d.ts → fold-rules-1DpDwG9h.d.ts} +2 -2
  80. package/dist/{fold-rules-DvLK8pbf.d.ts.map → fold-rules-1DpDwG9h.d.ts.map} +1 -1
  81. package/dist/{google-Crva32sk.js → google-BtMW9KzL.js} +2 -2
  82. package/dist/{google-Crva32sk.js.map → google-BtMW9KzL.js.map} +1 -1
  83. package/dist/{headless-CCm-jLLT.js → headless-CQm19BL0.js} +4 -4
  84. package/dist/{headless-CCm-jLLT.js.map → headless-CQm19BL0.js.map} +1 -1
  85. package/dist/headless.d.ts +1 -1
  86. package/dist/headless.js +1 -1
  87. package/dist/history-worker.js +1 -1
  88. package/dist/{host-BHMs9jsN.js → host-DEoEzQal.js} +13 -4
  89. package/dist/host-DEoEzQal.js.map +1 -0
  90. package/dist/{host-scoped-settings-CrbcqOkP.js → host-scoped-settings-WBYaCork.js} +2 -2
  91. package/dist/{host-scoped-settings-CrbcqOkP.js.map → host-scoped-settings-WBYaCork.js.map} +1 -1
  92. package/dist/{host-signals-Jsjd_-4g.js → host-signals-91zyko_i.js} +5 -5
  93. package/dist/{host-signals-Jsjd_-4g.js.map → host-signals-91zyko_i.js.map} +1 -1
  94. package/dist/{host-wO_6GW0y.js → host-uP3t08w8.js} +276 -103
  95. package/dist/host-uP3t08w8.js.map +1 -0
  96. package/dist/{host-version-CNAr9pov.js → host-version-_RHlAB29.js} +2 -2
  97. package/dist/{host-version-CNAr9pov.js.map → host-version-_RHlAB29.js.map} +1 -1
  98. package/dist/{index-C6nnKu32.d.ts → index-B0PLo9jn.d.ts} +4 -4
  99. package/dist/{index-C6nnKu32.d.ts.map → index-B0PLo9jn.d.ts.map} +1 -1
  100. package/dist/{index-Djnc8Peh.d.ts → index-De4x-s76.d.ts} +14 -14
  101. package/dist/{index-Djnc8Peh.d.ts.map → index-De4x-s76.d.ts.map} +1 -1
  102. package/dist/{index-CGbOWa7y.d.ts → index-WlE93Q6d.d.ts} +411 -3
  103. package/dist/index-WlE93Q6d.d.ts.map +1 -0
  104. package/dist/{index-CCf_WeTQ.d.ts → index-f-XWIoYo.d.ts} +2 -2
  105. package/dist/{index-CCf_WeTQ.d.ts.map → index-f-XWIoYo.d.ts.map} +1 -1
  106. package/dist/index.d.ts +5 -5
  107. package/dist/index.js +17 -17
  108. package/dist/ipc/browser-types.d.ts +304 -7
  109. package/dist/ipc/browser-types.d.ts.map +1 -1
  110. package/dist/ipc/browser.d.ts +4 -4
  111. package/dist/ipc/browser.js +2 -2
  112. package/dist/ipc/remote.d.ts +6 -6
  113. package/dist/ipc/remote.js +3 -3
  114. package/dist/{ipc-BzWhIybr.js → ipc-CatnsVEd.js} +4 -4
  115. package/dist/{ipc-BzWhIybr.js.map → ipc-CatnsVEd.js.map} +1 -1
  116. package/dist/ipc.d.ts +10 -10
  117. package/dist/ipc.js +8 -8
  118. package/dist/{json-Dj-YATkZ.js → json-BjClr43H.js} +2 -20
  119. package/dist/json-BjClr43H.js.map +1 -0
  120. package/dist/{links-CJG_rFvf.js → links-a8C9eby4.js} +9 -9
  121. package/dist/{links-CJG_rFvf.js.map → links-a8C9eby4.js.map} +1 -1
  122. package/dist/{loaded-entry-DTURJo_R.js → loaded-entry-D6jzt5HQ.js} +2 -2
  123. package/dist/{loaded-entry-DTURJo_R.js.map → loaded-entry-D6jzt5HQ.js.map} +1 -1
  124. package/dist/mcp.d.ts +1 -1
  125. package/dist/{media-type-BtA0cbfI.d.ts → media-type-ZSmAGHvg.d.ts} +188 -7
  126. package/dist/media-type-ZSmAGHvg.d.ts.map +1 -0
  127. package/dist/{memory-DuVGrzbl.js → memory-CJy52DNi.js} +132 -31
  128. package/dist/memory-CJy52DNi.js.map +1 -0
  129. package/dist/model-test-CAtJ76kM.js.map +1 -1
  130. package/dist/{move-session-kylExJ5H.js → move-session-D7ILECs-.js} +4 -4
  131. package/dist/{move-session-kylExJ5H.js.map → move-session-D7ILECs-.js.map} +1 -1
  132. package/dist/{nested-call-record-DlzPeTB7.js → nested-call-record-OayszZBQ.js} +4 -3
  133. package/dist/nested-call-record-OayszZBQ.js.map +1 -0
  134. package/dist/output/stream-json.d.ts +1 -1
  135. package/dist/output/terminal.d.ts +1 -1
  136. package/dist/{path-display-COVV71Yu.js → path-display-CAWz-sMq.js} +3 -3
  137. package/dist/{path-display-COVV71Yu.js.map → path-display-CAWz-sMq.js.map} +1 -1
  138. package/dist/plugins.d.ts +1 -1
  139. package/dist/{prelude-CVKoUzV8.js → prelude-BwiSR1uA.js} +16 -5
  140. package/dist/prelude-BwiSR1uA.js.map +1 -0
  141. package/dist/{presets-CBt6rFfS.js → presets-CMY43nUP.js} +3 -3
  142. package/dist/{presets-CBt6rFfS.js.map → presets-CMY43nUP.js.map} +1 -1
  143. package/dist/presets.d.ts +1 -1
  144. package/dist/presets.js +1 -1
  145. package/dist/{protocol-CEVqBO-q.d.ts → protocol-C2lqKuMd.d.ts} +50 -7
  146. package/dist/protocol-C2lqKuMd.d.ts.map +1 -0
  147. package/dist/protocol-DOGyHgLQ.js.map +1 -1
  148. package/dist/protocol-NSllElLw.js.map +1 -1
  149. package/dist/providers/anthropic.d.ts +1 -1
  150. package/dist/providers/arcee.d.ts +1 -1
  151. package/dist/providers/baseten.d.ts +1 -1
  152. package/dist/providers/cerebras.d.ts +1 -1
  153. package/dist/providers/google.d.ts +1 -1
  154. package/dist/providers/google.js +1 -1
  155. package/dist/providers/local.d.ts +1 -1
  156. package/dist/providers/neuralwatt.d.ts +1 -1
  157. package/dist/providers/openai-compat.d.ts +1 -1
  158. package/dist/providers/openai.d.ts +1 -1
  159. package/dist/providers/openrouter.d.ts +1 -1
  160. package/dist/providers/xai.d.ts +1 -1
  161. package/dist/{providers-Z0TaBp1O.js → providers-zVRxiJDv.js} +5 -5
  162. package/dist/providers-zVRxiJDv.js.map +1 -0
  163. package/dist/providers.d.ts +1 -1
  164. package/dist/providers.js +1 -1
  165. package/dist/{readonly-db-D7Z-k0pQ.js → readonly-db-DrbT8tX6.js} +2 -2
  166. package/dist/{readonly-db-D7Z-k0pQ.js.map → readonly-db-DrbT8tX6.js.map} +1 -1
  167. package/dist/{registry-BKjeBnoy.js → registry-CBoYDIND.js} +2 -2
  168. package/dist/{registry-BKjeBnoy.js.map → registry-CBoYDIND.js.map} +1 -1
  169. package/dist/{registry-B6Ww76iW.js → registry-CQOU5jvF.js} +2 -2
  170. package/dist/{registry-B6Ww76iW.js.map → registry-CQOU5jvF.js.map} +1 -1
  171. package/dist/{registry-B9INJwJR.js → registry-YUUqbYGr.js} +324 -39
  172. package/dist/registry-YUUqbYGr.js.map +1 -0
  173. package/dist/{remote-hosts-BMWoHCWO.js → remote-hosts-CT8Q95dR.js} +4 -4
  174. package/dist/{remote-hosts-BMWoHCWO.js.map → remote-hosts-CT8Q95dR.js.map} +1 -1
  175. package/dist/{repo-url-D4Dr8NfZ.js → repo-url-DLjjuVk_.js} +169 -2
  176. package/dist/repo-url-DLjjuVk_.js.map +1 -0
  177. package/dist/restate.d.ts +2 -2
  178. package/dist/{sandbox-BwiPGKF7.d.ts → sandbox-B4OW6oNx.d.ts} +2 -2
  179. package/dist/{sandbox-BwiPGKF7.d.ts.map → sandbox-B4OW6oNx.d.ts.map} +1 -1
  180. package/dist/script-progress-CrZ507Qq.js +900 -0
  181. package/dist/script-progress-CrZ507Qq.js.map +1 -0
  182. package/dist/session/sqlite.d.ts +1 -1
  183. package/dist/session.d.ts +1 -1
  184. package/dist/{setting-BQBSyUq0.js → setting-_jh2OtiL.js} +6 -6
  185. package/dist/{setting-BQBSyUq0.js.map → setting-_jh2OtiL.js.map} +1 -1
  186. package/dist/{settings-catalog-4IU2i_jG.js → settings-catalog-DEZlXP51.js} +14 -2
  187. package/dist/settings-catalog-DEZlXP51.js.map +1 -0
  188. package/dist/{settings-keys-iI_Y-7MC.js → settings-keys-BT0_oKjO.js} +3 -3
  189. package/dist/{settings-keys-iI_Y-7MC.js.map → settings-keys-BT0_oKjO.js.map} +1 -1
  190. package/dist/{skills-CFXRItJ6.js → skills-CEbosUlM.js} +2 -2
  191. package/dist/{skills-CFXRItJ6.js.map → skills-CEbosUlM.js.map} +1 -1
  192. package/dist/skills.d.ts +1 -1
  193. package/dist/skills.js +1 -1
  194. package/dist/{source-Bl1Z501r.js → source-3ZnEPOSa.js} +3 -2
  195. package/dist/source-3ZnEPOSa.js.map +1 -0
  196. package/dist/{sqlite-runtime-BZ2L4RaG.d.ts → sqlite-runtime-CS_LBaAN.d.ts} +2 -2
  197. package/dist/sqlite-runtime-CS_LBaAN.d.ts.map +1 -0
  198. package/dist/{src-BY_ND2xn.js → src-BmTXDta8.js} +2 -2
  199. package/dist/{src-BY_ND2xn.js.map → src-BmTXDta8.js.map} +1 -1
  200. package/dist/{store-C6gI7FfZ.js → store-ByCDaKgJ.js} +14 -3
  201. package/dist/store-ByCDaKgJ.js.map +1 -0
  202. package/dist/{system-freeze-m5o1sGIM.js → system-freeze-DxJ-lRhu.js} +3 -3
  203. package/dist/{system-freeze-m5o1sGIM.js.map → system-freeze-DxJ-lRhu.js.map} +1 -1
  204. package/dist/{todo-edits-CbukbjyV.js → todo-edits-Dyj32ds6.js} +4 -3
  205. package/dist/todo-edits-Dyj32ds6.js.map +1 -0
  206. package/dist/tools/fetch-url.d.ts +1 -1
  207. package/dist/tools/web-search.d.ts +1 -1
  208. package/dist/{tools-CVTGhggj.js → tools-BsrGQ8K4.js} +2 -2
  209. package/dist/{tools-CVTGhggj.js.map → tools-BsrGQ8K4.js.map} +1 -1
  210. package/dist/tools.d.ts +1 -1
  211. package/dist/tools.js +3 -3
  212. package/dist/{transcript-anchors-CiSi_UBn.js → transcript-anchors-DMjvyUjk.js} +3 -3
  213. package/dist/{transcript-anchors-CiSi_UBn.js.map → transcript-anchors-DMjvyUjk.js.map} +1 -1
  214. package/dist/{transcript-reconcile-jbSBjLq2.js → transcript-reconcile-DpQO85wl.js} +3 -3
  215. package/dist/{transcript-reconcile-jbSBjLq2.js.map → transcript-reconcile-DpQO85wl.js.map} +1 -1
  216. package/dist/tui.d.ts +9 -9
  217. package/dist/tui.d.ts.map +1 -1
  218. package/dist/tui.js +289 -38
  219. package/dist/tui.js.map +1 -1
  220. package/dist/{types-bDQagoiL.d.ts → types-2HrgolQz.d.ts} +20 -8
  221. package/dist/types-2HrgolQz.d.ts.map +1 -0
  222. package/dist/types-4q1bFG53.js +122 -0
  223. package/dist/types-4q1bFG53.js.map +1 -0
  224. package/dist/{types-DnRd9_cA.d.ts → types-SXqk6hLD.d.ts} +3 -3
  225. package/dist/{types-DnRd9_cA.d.ts.map → types-SXqk6hLD.d.ts.map} +1 -1
  226. package/dist/types-myxheQkw.js.map +1 -1
  227. package/dist/types.d.ts +3 -3
  228. package/dist/{usage-summary-BfcXjiSM.js → usage-summary-BM7B3JdI.js} +2 -2
  229. package/dist/{usage-summary-BfcXjiSM.js.map → usage-summary-BM7B3JdI.js.map} +1 -1
  230. package/dist/{version-DqqqvHDP.js → version-DwQI12WA.js} +2 -2
  231. package/dist/{version-DqqqvHDP.js.map → version-DwQI12WA.js.map} +1 -1
  232. package/dist/{wait-task-BX4NcOOm.js → wait-task-BiI5UaZQ.js} +13 -3
  233. package/dist/wait-task-BiI5UaZQ.js.map +1 -0
  234. package/dist/{wire-CLm2rMK0.js → wire-Dxwxb7QQ.js} +2 -2
  235. package/dist/{wire-CLm2rMK0.js.map → wire-Dxwxb7QQ.js.map} +1 -1
  236. package/dist/{write-file-0E6TUJ9A.js → write-file-Dquu7MzB.js} +5 -3
  237. package/dist/write-file-Dquu7MzB.js.map +1 -0
  238. package/dist/{ws-DfGGYQ4W.js → ws-CA3o2nie.js} +4 -4
  239. package/dist/{ws-DfGGYQ4W.js.map → ws-CA3o2nie.js.map} +1 -1
  240. package/dist/{ws-client-CFMbhsNq.js → ws-client-C0iPAAzc.js} +2 -2
  241. package/dist/{ws-client-CFMbhsNq.js.map → ws-client-C0iPAAzc.js.map} +1 -1
  242. package/docs/CHAT.md +55 -0
  243. package/docs/CODEMODE.md +37 -0
  244. package/docs/EXTENSIONS.md +8 -6
  245. package/docs/RELEASE-NOTES-NEXT.md +48 -1
  246. package/docs/RELEASE.md +9 -1
  247. package/package.json +2 -3
  248. package/dist/agent-DJhpTS-B.js.map +0 -1
  249. package/dist/auto-update-restart-BFB7Ek87.js.map +0 -1
  250. package/dist/browser-BcoCCB_n.js.map +0 -1
  251. package/dist/contexts-C72b0mMa.js.map +0 -1
  252. package/dist/decisions-CXmK0nz9.js.map +0 -1
  253. package/dist/extension-mark-BqKdEZwF.js.map +0 -1
  254. package/dist/extensions-client-D8tEAcMw.d.ts.map +0 -1
  255. package/dist/host-BHMs9jsN.js.map +0 -1
  256. package/dist/host-wO_6GW0y.js.map +0 -1
  257. package/dist/index-CGbOWa7y.d.ts.map +0 -1
  258. package/dist/json-Dj-YATkZ.js.map +0 -1
  259. package/dist/media-type-BtA0cbfI.d.ts.map +0 -1
  260. package/dist/memory-DuVGrzbl.js.map +0 -1
  261. package/dist/nested-call-events-BYk8XvGX.js +0 -182
  262. package/dist/nested-call-events-BYk8XvGX.js.map +0 -1
  263. package/dist/nested-call-record-DlzPeTB7.js.map +0 -1
  264. package/dist/prelude-CVKoUzV8.js.map +0 -1
  265. package/dist/protocol-CEVqBO-q.d.ts.map +0 -1
  266. package/dist/providers-Z0TaBp1O.js.map +0 -1
  267. package/dist/registry-B9INJwJR.js.map +0 -1
  268. package/dist/repo-url-D4Dr8NfZ.js.map +0 -1
  269. package/dist/settings-catalog-4IU2i_jG.js.map +0 -1
  270. package/dist/source-Bl1Z501r.js.map +0 -1
  271. package/dist/sqlite-runtime-BZ2L4RaG.d.ts.map +0 -1
  272. package/dist/store-C6gI7FfZ.js.map +0 -1
  273. package/dist/todo-edits-CbukbjyV.js.map +0 -1
  274. package/dist/types-DTMEhKJO.js +0 -14
  275. package/dist/types-DTMEhKJO.js.map +0 -1
  276. package/dist/types-bDQagoiL.d.ts.map +0 -1
  277. package/dist/wait-task-BX4NcOOm.js.map +0 -1
  278. package/dist/write-file-0E6TUJ9A.js.map +0 -1
  279. package/scripts/eval.ts +0 -179
@@ -1 +1 @@
1
- {"version":3,"file":"registry-BKjeBnoy.js","names":[],"sources":["../src/extensions/integrations/oauth.ts","../src/extensions/integrations/connect.ts","../src/extensions/integrations/connections.ts","../src/extensions/integrations/refresh.ts","../src/extensions/integrations/registry.ts"],"sourcesContent":["/**\n * The OAuth flows of an integration's `oauth` method: authorization code\n * with PKCE (RFC 7636) and the device authorization grant (RFC 8628), plus\n * the refresh-token grant.\n *\n * PKCE runs on the `zidane/oauth` kit (`src/oauth-entry.ts`): the loopback\n * callback server and page, `generatePkce`, and the redirect-or-paste race\n * (`awaitAuthorizationCode`), so a remote sign-in (`callbackServer: false`)\n * works as it does for provider sign-ins. The kit is imported lazily:\n * `node:http` stays off the graph until a flow runs.\n *\n * Every network call goes through {@link OAuthIo.fetch} and every wait\n * through {@link OAuthIo.wait}, so tests drive a fake server and clock.\n */\n\nimport type { OAuthLoginCallbacks } from '@earendil-works/pi-ai/oauth'\nimport type { Credential, IntegrationMethod } from '../api/sessions'\n\n/** An `oauth` integration method. */\nexport type OAuthIntegrationMethod = Extract<IntegrationMethod, { type: 'oauth' }>\n\n/** An OAuth credential. */\nexport type OAuthCredential = Extract<Credential, { type: 'oauth' }>\n\n/** The I/O an OAuth flow uses. */\nexport interface OAuthIo {\n /** Sends an HTTP request (the global `fetch` by default). */\n fetch: (url: string, init: RequestInit) => Promise<Response>\n /** Resolves after `ms`, or rejects when `signal` aborts. Device polling waits through it. */\n wait: (ms: number, signal: AbortSignal) => Promise<void>\n /** The current time in ms; expiry times are computed from it. */\n now: () => number\n}\n\n/** What a flow or a refresh yields. */\nexport interface OAuthGrant {\n /** The credential to hand out. */\n credential: OAuthCredential\n /** The refresh token, when the server issued one. */\n refreshToken?: string\n}\n\n/** A token endpoint refused a request. */\nexport class OAuthTokenError extends Error {\n /**\n * @param code - The OAuth `error` code (`invalid_grant`, `authorization_pending`, …), or `http_<status>`.\n * @param description - The server's `error_description`, when given.\n * @param status - The HTTP status.\n */\n constructor(readonly code: string, readonly description: string | undefined, readonly status: number) {\n super(`OAuth token request failed (${status}): ${[code, description].filter(Boolean).join(': ')}`)\n this.name = 'OAuthTokenError'\n }\n}\n\n/** A hung token endpoint must not hold a resolve forever. */\nconst TOKEN_REQUEST_TIMEOUT_MS = 30_000\n/** RFC 8628 §3.2: default polling interval. */\nconst DEFAULT_POLL_INTERVAL_S = 5\n/** RFC 8628 §3.5: `slow_down` adds 5 seconds. */\nconst SLOW_DOWN_STEP_S = 5\n/** When the device endpoint omits `expires_in`. */\nconst DEFAULT_DEVICE_EXPIRY_S = 900\n\n/** Default {@link OAuthIo}: real fetch, timers and clock. */\nexport const defaultOAuthIo: OAuthIo = {\n fetch: (url, init) => fetch(url, init),\n wait: (ms, signal) => new Promise<void>((resolve, reject) => {\n if (signal.aborted)\n return reject(signal.reason)\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n function onAbort(): void {\n clearTimeout(timer)\n reject(signal.reason)\n }\n signal.addEventListener('abort', onAbort, { once: true })\n }),\n now: () => Date.now(),\n}\n\ninterface TokenResponse {\n access_token?: unknown\n refresh_token?: unknown\n expires_in?: unknown\n scope?: unknown\n error?: unknown\n error_description?: unknown\n}\n\n/** Reads a token or device response: JSON, or a form-encoded body (GitHub without `Accept`). */\nfunction parseBody(text: string): Record<string, unknown> {\n try {\n const parsed: unknown = JSON.parse(text)\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))\n return parsed as Record<string, unknown>\n }\n catch { /* not JSON */ }\n return Object.fromEntries(new URLSearchParams(text))\n}\n\nasync function postForm(io: OAuthIo, url: string, body: Record<string, string>, signal?: AbortSignal): Promise<Record<string, unknown>> {\n const timeout = AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)\n const response = await io.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams(body).toString(),\n signal: signal ? AbortSignal.any([signal, timeout]) : timeout,\n })\n const parsed = parseBody(await response.text())\n if (!response.ok || typeof parsed.error === 'string') {\n const code = typeof parsed.error === 'string' ? parsed.error : `http_${response.status}`\n throw new OAuthTokenError(code, typeof parsed.error_description === 'string' ? parsed.error_description : undefined, response.status)\n }\n return parsed\n}\n\nfunction clientFields(method: OAuthIntegrationMethod): Record<string, string> {\n return { client_id: method.clientId, ...(method.clientSecret ? { client_secret: method.clientSecret } : {}) }\n}\n\n/** The grant a token response carries. */\nfunction toGrant(tokens: TokenResponse, method: OAuthIntegrationMethod, io: OAuthIo, previousRefresh?: string): OAuthGrant {\n if (typeof tokens.access_token !== 'string' || !tokens.access_token)\n throw new Error('The token endpoint returned no access token.')\n const expiresIn = Number(tokens.expires_in)\n const scopes = typeof tokens.scope === 'string' && tokens.scope.trim()\n ? tokens.scope.trim().split(/[\\s,]+/)\n : method.scopes ? [...method.scopes] : undefined\n const refreshToken = typeof tokens.refresh_token === 'string' && tokens.refresh_token ? tokens.refresh_token : previousRefresh\n return {\n credential: {\n type: 'oauth',\n accessToken: tokens.access_token,\n ...(Number.isFinite(expiresIn) && expiresIn > 0 ? { expiresAt: io.now() + expiresIn * 1000 } : {}),\n ...(scopes ? { scopes } : {}),\n },\n ...(refreshToken ? { refreshToken } : {}),\n }\n}\n\nfunction randomHex(bytes: number): string {\n return [...crypto.getRandomValues(new Uint8Array(bytes))].map(b => b.toString(16).padStart(2, '0')).join('')\n}\n\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted)\n return Promise.reject(signal.reason)\n return new Promise<T>((resolve, reject) => {\n const onAbort = (): void => reject(signal.reason)\n signal.addEventListener('abort', onAbort, { once: true })\n promise.then(resolve, reject).finally(() => signal.removeEventListener('abort', onAbort))\n })\n}\n\n/**\n * The loopback port and path of a PKCE method's `redirectUri`. Throws for a\n * URI the callback server can't serve.\n *\n * @param redirectUri - The method's `redirectUri`.\n */\nexport function parseLoopbackRedirect(redirectUri: string | undefined): { port: number, path: string } {\n let url: URL\n try {\n url = new URL(redirectUri ?? '')\n }\n catch {\n throw new TypeError(`redirectUri must be a loopback URL with a port, like http://localhost:51121/callback (got ${JSON.stringify(redirectUri)})`)\n }\n const port = Number(url.port)\n if (url.protocol !== 'http:' || !['localhost', '127.0.0.1'].includes(url.hostname) || !Number.isInteger(port) || port <= 0)\n throw new TypeError(`redirectUri must be http://localhost:<port>/<path> or http://127.0.0.1:<port>/<path> (got ${redirectUri})`)\n return { port, path: url.pathname || '/' }\n}\n\n/**\n * Runs the authorization code flow with PKCE: opens the authorize URL\n * (`callbacks.onAuth`), takes the code from the loopback redirect or a\n * pasted URL, and exchanges it at the token endpoint.\n *\n * @param method - The `oauth` method (`flow: 'pkce'`).\n * @param name - The integration's name, shown on the callback page.\n * @param callbacks - The host's login callbacks.\n * @param signal - Aborts the flow.\n * @param io - Network and clock.\n */\nexport async function loginWithPkce(method: OAuthIntegrationMethod, name: string, callbacks: OAuthLoginCallbacks, signal: AbortSignal, io: OAuthIo = defaultOAuthIo): Promise<OAuthGrant> {\n const { port, path } = parseLoopbackRedirect(method.redirectUri)\n const redirectUri = method.redirectUri as string\n const kit = await import('../../oauth-entry')\n const { verifier, challenge } = await kit.generatePkce()\n const state = randomHex(16)\n const server = await kit.startCallbackServer({\n port,\n path,\n expectedState: state,\n providerName: name,\n renderPage: kit.renderDefaultCallbackPage,\n successMessage: 'You can close this tab.',\n onListenError: 'reject',\n ...(kit.listensForCallback(callbacks) ? {} : { listen: false }),\n }).catch((err: unknown) => {\n if ((err as { code?: unknown } | null)?.code === 'EADDRINUSE')\n throw new Error(`Port ${port} is busy, likely another sign-in in progress. Finish or close it, then try again.`)\n throw err\n })\n try {\n const url = new URL(method.authorizeUrl)\n url.searchParams.set('client_id', method.clientId)\n url.searchParams.set('redirect_uri', redirectUri)\n url.searchParams.set('response_type', 'code')\n if (method.scopes?.length)\n url.searchParams.set('scope', method.scopes.join(' '))\n url.searchParams.set('code_challenge', challenge)\n url.searchParams.set('code_challenge_method', 'S256')\n url.searchParams.set('state', state)\n for (const [key, value] of Object.entries(method.params ?? {}))\n url.searchParams.set(key, value)\n callbacks.onAuth({\n url: url.toString(),\n instructions: `Sign in to ${name} in your browser. If the browser is on another machine, paste the final redirect URL here.`,\n })\n const pending = kit.awaitAuthorizationCode(server, state, callbacks)\n pending.catch(() => {})\n const code = await abortable(pending, signal)\n callbacks.onProgress?.('Exchanging authorization code for tokens...')\n const tokens = await postForm(io, method.tokenUrl, {\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n ...clientFields(method),\n }, signal)\n return toGrant(tokens, method, io)\n }\n finally {\n server.close()\n }\n}\n\n/**\n * Runs the device authorization grant: asks the device endpoint for a code,\n * shows it (`callbacks.onDeviceCode`), then polls the token endpoint until\n * the user approves, refuses, or the code expires.\n *\n * @param method - The `oauth` method (`flow: 'device'`).\n * @param callbacks - The host's login callbacks.\n * @param signal - Aborts the flow, polling included.\n * @param io - Network, waits and clock.\n */\nexport async function loginWithDeviceCode(method: OAuthIntegrationMethod, callbacks: OAuthLoginCallbacks, signal: AbortSignal, io: OAuthIo = defaultOAuthIo): Promise<OAuthGrant> {\n if (!method.deviceUrl)\n throw new TypeError('A device flow needs a deviceUrl')\n const start = await postForm(io, method.deviceUrl, {\n ...clientFields(method),\n ...(method.scopes?.length ? { scope: method.scopes.join(' ') } : {}),\n }, signal)\n const deviceCode = start.device_code\n const userCode = start.user_code\n const verificationUri = start.verification_uri ?? start.verification_url\n if (typeof deviceCode !== 'string' || typeof userCode !== 'string' || typeof verificationUri !== 'string')\n throw new Error('The device endpoint returned no device_code, user_code or verification_uri.')\n let intervalS = Number(start.interval) > 0 ? Number(start.interval) : DEFAULT_POLL_INTERVAL_S\n const expiresInS = Number(start.expires_in) > 0 ? Number(start.expires_in) : DEFAULT_DEVICE_EXPIRY_S\n callbacks.onDeviceCode({\n userCode,\n verificationUri: typeof start.verification_uri_complete === 'string' ? start.verification_uri_complete : verificationUri,\n intervalSeconds: intervalS,\n expiresInSeconds: expiresInS,\n })\n // Count waited time rather than reading the clock: the code expires after\n // `expires_in` of polling, and a driven clock in tests never advances.\n let waitedMs = 0\n while (waitedMs < expiresInS * 1000) {\n await io.wait(intervalS * 1000, signal)\n waitedMs += intervalS * 1000\n try {\n const tokens = await postForm(io, method.tokenUrl, {\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n ...clientFields(method),\n }, signal)\n return toGrant(tokens, method, io)\n }\n catch (err) {\n if (!(err instanceof OAuthTokenError))\n throw err\n if (err.code === 'authorization_pending')\n continue\n if (err.code === 'slow_down') {\n intervalS += SLOW_DOWN_STEP_S\n continue\n }\n if (err.code === 'access_denied')\n throw new Error('The sign-in was refused.', { cause: err })\n if (err.code === 'expired_token')\n break\n throw err\n }\n }\n throw new Error('The device code expired before the sign-in finished. Try again.')\n}\n\n/**\n * Exchanges a refresh token for a new access token. Keeps the old refresh\n * token when the server does not rotate it.\n *\n * @param method - The `oauth` method the grant came from.\n * @param refreshToken - The stored refresh token.\n * @param io - Network and clock.\n * @param signal - Aborts the request.\n */\nexport async function refreshOAuthGrant(method: OAuthIntegrationMethod, refreshToken: string, io: OAuthIo = defaultOAuthIo, signal?: AbortSignal): Promise<OAuthGrant> {\n const tokens = await postForm(io, method.tokenUrl, {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n ...clientFields(method),\n }, signal)\n return toGrant(tokens, method, io, refreshToken)\n}\n","/**\n * The checks around connecting: spec validation, and the extension\n * callbacks (`beforeConnect`, `verify`, `account`, `parseCommandOutput`)\n * run through the registering extension's guard. A callback's throw or\n * returned message becomes the connect's error; a timeout refuses too.\n * A `custom` method's `connect` runs here too, on the host's flow UI\n * ({@link customFlowIo}).\n */\n\nimport type { OAuthLoginCallbacks } from '@earendil-works/pi-ai/oauth'\nimport type { Credential, IntegrationCustomMethod, IntegrationCustomResult, IntegrationFlowIO, IntegrationMethod, IntegrationSpec } from '../api/sessions'\nimport type { Guard } from '../runtime/deps'\nimport { errorMessage } from '../../errors'\nimport { parseLoopbackRedirect } from './oauth'\n\n/** What a host shows while a connect flow runs. */\nexport interface IntegrationFlowRequest {\n /** The integration being connected. */\n integrationId: string\n /** Its display name. */\n name: string\n /** The method the user picked. */\n method: IntegrationMethod\n /** Aborts when the flow is cancelled. */\n signal: AbortSignal\n}\n\n/** The outcome of a `command` method's process. */\nexport interface IntegrationCommandResult {\n /** Standard output; the credential is read from it. */\n stdout: string\n /** Standard error, quoted when the command fails. */\n stderr?: string\n /** Exit code; anything but 0 fails the connect. */\n exitCode: number\n}\n\n/**\n * What a host provides for connect flows. The TUI, desktop and daemon each\n * implement it once; Settings and `ctx.integrations.connect.start` both use it.\n */\nexport interface IntegrationFlowPort {\n /**\n * The login callbacks for an OAuth flow: open `onAuth`'s URL, show\n * `onDeviceCode`'s code, read a pasted redirect (`onManualCodeInput`),\n * show progress. The same callbacks hosts already build for provider\n * sign-in (`runOAuthLogin` in `src/chat/oauth.ts`); set\n * `callbackServer: false` for a client of a remote host.\n */\n oauthCallbacks: (request: IntegrationFlowRequest) => OAuthLoginCallbacks & { callbackServer?: false }\n /** Runs a `command` method's argv (no shell) and returns its output. */\n runCommand: (command: readonly string[], opts: { signal: AbortSignal }) => Promise<IntegrationCommandResult>\n}\n\n/** Integration ids become credential-file keys: no `#` (reserved for provider slots), no spaces. */\nconst ID_PATTERN = /^[\\w.:-]+$/\n\n/**\n * Throws a `TypeError` naming the first problem of a spec.\n *\n * @param spec - The spec an extension registers.\n */\nexport function validateSpec(spec: IntegrationSpec): void {\n if (!spec || typeof spec.id !== 'string' || !ID_PATTERN.test(spec.id))\n throw new TypeError(`integrations.register: id must match ${ID_PATTERN} (got ${JSON.stringify(spec?.id)})`)\n if (typeof spec.name !== 'string' || !spec.name)\n throw new TypeError(`integrations.register(${spec.id}): name is required`)\n if (!Array.isArray(spec.methods) || spec.methods.length === 0)\n throw new TypeError(`integrations.register(${spec.id}): at least one method is required`)\n const ids = new Set<string>()\n for (const method of spec.methods) {\n const where = `integrations.register(${spec.id}): method ${method.id}`\n if (!method.id || ids.has(method.id))\n throw new TypeError(`integrations.register(${spec.id}): method ids must be unique and non-empty (got ${JSON.stringify(method.id)})`)\n ids.add(method.id)\n if (method.type === 'oauth' && method.flow === 'pkce') {\n try {\n parseLoopbackRedirect(method.redirectUri)\n }\n catch (err) {\n throw new TypeError(`${where}: ${errorMessage(err)}`)\n }\n }\n if (method.type === 'oauth' && method.flow === 'device' && !method.deviceUrl)\n throw new TypeError(`${where}: a device flow needs a deviceUrl`)\n if (method.type === 'command' && (!Array.isArray(method.command) || method.command.length === 0))\n throw new TypeError(`${where}: command is empty`)\n if (method.type === 'custom' && typeof method.connect !== 'function')\n throw new TypeError(`${where}: a custom method needs a connect function`)\n if (method.type === 'custom' && method.refresh !== undefined && typeof method.refresh !== 'function')\n throw new TypeError(`${where}: refresh must be a function`)\n }\n if (spec.beforeConnect !== undefined && typeof spec.beforeConnect !== 'function')\n throw new TypeError(`integrations.register(${spec.id}): beforeConnect must be a function`)\n if (spec.legacyCredentialKey !== undefined && (typeof spec.legacyCredentialKey !== 'string' || !spec.legacyCredentialKey || spec.legacyCredentialKey.includes('#')))\n throw new TypeError(`integrations.register(${spec.id}): legacyCredentialKey must be a credentials-file key without \"#\"`)\n}\n\n/**\n * Runs the spec's `beforeConnect` for `method`. Throws its message when it\n * refuses (or throws); a timeout refuses too.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param method - The method the user picked.\n */\nexport async function checkBeforeConnect(spec: IntegrationSpec, guard: Guard, method: IntegrationMethod): Promise<void> {\n const check = spec.beforeConnect\n if (!check)\n return\n const outcome = await guard.call(`integrations.beforeConnect(${spec.id})`, async () => {\n try {\n return { refusal: await check(method) }\n }\n catch (err) {\n return { refusal: errorMessage(err) || `${spec.name} refused to connect.` }\n }\n }, { fallback: { refusal: `${spec.name}: the connect check did not answer.` } })\n if (typeof outcome?.refusal === 'string' && outcome.refusal)\n throw new Error(outcome.refusal)\n}\n\n/**\n * The {@link IntegrationFlowIO} of a `custom` method, on the host's OAuth\n * login callbacks: links (`onAuth`), codes (`onDeviceCode`), questions\n * (`onPrompt`, `onSelect`) and progress lines (`onProgress`).\n *\n * @param callbacks - The host's login callbacks for this flow.\n * @param signal - Aborts the flow.\n */\nexport function customFlowIo(callbacks: OAuthLoginCallbacks, signal: AbortSignal): IntegrationFlowIO {\n return {\n openUrl: url => callbacks.onAuth({ url }),\n showCode: (code, url) => callbacks.onDeviceCode({ userCode: code, verificationUri: url ?? '' }),\n prompt: async (opts) => {\n try {\n if (opts.options)\n return await callbacks.onSelect({ message: opts.message, options: opts.options.map(o => ({ id: o.id, label: o.label })) })\n return await callbacks.onPrompt({ message: opts.message, ...(opts.placeholder ? { placeholder: opts.placeholder } : {}), ...(opts.allowEmpty ? { allowEmpty: true } : {}) })\n }\n catch (err) {\n // A host rejects a dismissed question; the flow reads that as a cancel unless the whole flow was aborted.\n if (signal.aborted)\n throw err\n return undefined\n }\n },\n signal,\n log: message => callbacks.onProgress?.(message),\n }\n}\n\n/**\n * Runs a `custom` method's `connect` and checks what it returns. Its\n * errors become the connect's (prefixed with the integration's name).\n *\n * @param spec - The integration.\n * @param method - The `custom` method.\n * @param io - The flow UI.\n */\nexport async function customCredential(spec: IntegrationSpec, method: IntegrationCustomMethod, io: IntegrationFlowIO): Promise<IntegrationCustomResult> {\n let result: IntegrationCustomResult\n try {\n result = await method.connect(io)\n }\n catch (err) {\n io.signal.throwIfAborted()\n throw new Error(`${spec.name}: ${errorMessage(err) || 'the sign-in failed'}`, { cause: err })\n }\n io.signal.throwIfAborted()\n const credential = validCredential(result?.credential)\n if (!credential)\n throw new Error(`${spec.name}: the sign-in returned no usable credential.`)\n const text = (v: unknown): string | undefined => (typeof v === 'string' && v.trim() ? v.trim() : undefined)\n const account = text(result.account)\n const label = text(result.label)\n return { credential, ...(account ? { account } : {}), ...(label ? { label } : {}) }\n}\n\n/**\n * The credential an extension built, checked; `undefined` when it is not one.\n *\n * @param value - What `parseCommandOutput` returned.\n */\nexport function validCredential(value: unknown): Credential | undefined {\n const c = value as Partial<Record<string, unknown>> | null\n if (!c || typeof c !== 'object')\n return undefined\n if (c.type === 'apiKey' && typeof c.key === 'string' && c.key)\n return { type: 'apiKey', key: c.key }\n if (c.type === 'token' && typeof c.token === 'string' && c.token)\n return { type: 'token', token: c.token }\n if (c.type === 'oauth' && typeof c.accessToken === 'string' && c.accessToken) {\n return {\n type: 'oauth',\n accessToken: c.accessToken,\n ...(typeof c.expiresAt === 'number' ? { expiresAt: c.expiresAt } : {}),\n ...(Array.isArray(c.scopes) ? { scopes: c.scopes.filter((s): s is string => typeof s === 'string') } : {}),\n ...(typeof c.refreshToken === 'string' && c.refreshToken ? { refreshToken: c.refreshToken } : {}),\n ...(c.extra && typeof c.extra === 'object' && !Array.isArray(c.extra) ? { extra: structuredClone(c.extra as Record<string, unknown>) } : {}),\n }\n }\n return undefined\n}\n\n/**\n * Runs the spec's `verify`. Throws when it refuses, throws or times out.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param credential - The credential to check.\n */\nexport async function verifyCredential(spec: IntegrationSpec, guard: Guard, credential: Credential): Promise<void> {\n const verify = spec.verify\n if (!verify)\n return\n const outcome = await guard.call(`integrations.verify(${spec.id})`, async () => {\n try {\n return { error: await verify(credential) }\n }\n catch (err) {\n return { error: errorMessage(err) || 'verification failed' }\n }\n }, { fallback: { error: 'verification did not answer' } })\n if (outcome?.error)\n throw new Error(`${spec.name}: ${outcome.error}`)\n}\n\n/**\n * The spec's `account` label for a credential. `undefined` when it has none, throws or times out.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param credential - The new credential.\n */\nexport async function accountLabel(spec: IntegrationSpec, guard: Guard, credential: Credential): Promise<string | undefined> {\n const account = spec.account\n if (!account)\n return undefined\n const label = await guard.call(`integrations.account(${spec.id})`, () => account(credential))\n return typeof label === 'string' && label ? label : undefined\n}\n\n/**\n * Runs a `command` method through the host and reads its credential: the\n * spec's `parseCommandOutput`, or by default the trimmed stdout as a token.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param method - The `command` method.\n * @param port - The host's command runner.\n * @param signal - Aborts the command.\n */\nexport async function commandCredential(spec: IntegrationSpec, guard: Guard, method: Extract<IntegrationMethod, { type: 'command' }>, port: IntegrationFlowPort, signal: AbortSignal): Promise<Credential> {\n const shown = method.command.join(' ')\n const result = await port.runCommand(method.command, { signal })\n if (result.exitCode !== 0) {\n const detail = (result.stderr ?? '').trim().split('\\n')[0]\n throw new Error(`${shown} failed (exit ${result.exitCode})${detail ? `: ${detail}` : ''}`)\n }\n const parse = spec.parseCommandOutput\n if (!parse) {\n const token = result.stdout.trim()\n if (!token)\n throw new Error(`${shown} printed nothing.`)\n return { type: 'token', token }\n }\n const parsed = guard.callSync(`integrations.parseCommandOutput(${spec.id})`, () => {\n try {\n return { credential: parse(result.stdout) as unknown }\n }\n catch (err) {\n return { error: errorMessage(err) }\n }\n })\n if (parsed && 'error' in parsed)\n throw new Error(`${spec.name}: could not read the command output: ${parsed.error}`)\n const credential = validCredential(parsed?.credential)\n if (!credential)\n throw new Error(`${spec.name}: parseCommandOutput returned no usable credential.`)\n return credential\n}\n","/**\n * The registry's view of stored connections: a cache over the\n * {@link AuthStorePort} (one list per integration, the default first),\n * change detection and notification (`onChange` listeners per integration,\n * Settings subscribers), and the adoption of a legacy provider sign-in\n * (`IntegrationSpec.legacyCredentialKey`).\n */\n\nimport type { Logger } from '../../logger'\nimport type { Connection, IntegrationSpec } from '../api/sessions'\nimport type { AuthStorePort, StoredIntegration } from './store'\nimport { errorMessage } from '../../errors'\nimport { DEFAULT_ACCOUNT } from './auth-store-port'\n\n/** An integration's stored connections, the default first. */\nexport type Stored = StoredIntegration[]\n\n/** What the cache reads through. */\nexport interface ConnectionCacheDeps {\n store: AuthStorePort\n logger?: Logger\n /** The clock adopted connections are stamped with. */\n now: () => number\n /** The registered spec of an integration (for its `legacyCredentialKey`). */\n spec: (integrationId: string) => IntegrationSpec | undefined\n}\n\n/** The connections a store record holds, as `connections()` lists them (`isDefault` on the first). */\nexport function view(list: Stored | undefined | null): Connection[] {\n return (list ?? []).map((s, i) => ({ ...s.connection, ...(i === 0 ? { isDefault: true } : {}) }))\n}\n\nfunction connectionsKey(list: Stored | undefined | null): string {\n return JSON.stringify((list ?? []).map(s => s.connection))\n}\n\n/**\n * Makes the cache.\n *\n * @param deps - Store, logger, clock and spec lookup.\n */\nexport function createConnectionCache(deps: ConnectionCacheDeps) {\n const { store, logger } = deps\n /** Stored records read so far; `null` is \"read, none stored\". */\n const cache = new Map<string, Stored | null>()\n const listeners = new Map<string, Set<(connection: Connection | undefined) => void>>()\n const subscribers = new Set<() => void>()\n\n const safely = (what: string, fn: () => void, attrs?: Record<string, unknown>): void => {\n try {\n fn()\n }\n catch (err) {\n logger?.warn(`integrations: ${what} threw`, { ...attrs, error: errorMessage(err) })\n }\n }\n\n /** Adopts the legacy sign-in a spec names, when the integration has no connection. */\n function adoptLegacy(id: string): Stored | undefined {\n const spec = deps.spec(id)\n const key = spec?.legacyCredentialKey\n if (!spec || !key || !store.readLegacy)\n return undefined\n const grant = store.readLegacy(key)\n if (!grant)\n return undefined\n const methods = spec.methods\n const method = grant.credential.type === 'oauth'\n ? methods.find(m => m.type === 'oauth' || m.type === 'custom')\n : methods.find(m => m.type === 'apiKey') ?? methods.find(m => m.type === 'command')\n const account = grant.account ?? DEFAULT_ACCOUNT\n const record: StoredIntegration = {\n connection: {\n integrationId: id,\n methodId: (method ?? methods[0]!).id,\n account,\n ...(grant.label && grant.label !== account ? { label: grant.label } : {}),\n connectedAt: deps.now(),\n },\n credential: grant.credential,\n ...(grant.refreshToken ? { refreshToken: grant.refreshToken } : {}),\n }\n store.write(id, [record])\n store.markLegacy?.(key, id)\n logger?.info(`integrations: adopted the legacy sign-in under \"${key}\" as a connection of ${id}`)\n return [record]\n }\n\n /** Reads one record fresh from the store (adopting a legacy sign-in when it is empty). */\n function read(id: string): Stored | undefined {\n let value: Stored | undefined\n try {\n // A port written before several connections returns one record: read it as the only one.\n const raw = store.read(id) as Stored | StoredIntegration | undefined\n value = raw === undefined || Array.isArray(raw) ? raw : [raw]\n if (!value?.length)\n value = adoptLegacy(id)\n }\n catch (err) {\n logger?.warn('integrations: could not read the stored connections', { integration: id, error: errorMessage(err) })\n }\n const list = value?.length ? value : undefined\n cache.set(id, list ?? null)\n return list\n }\n\n function cached(id: string): Stored | undefined {\n const value = cache.get(id)\n return value === undefined ? read(id) : value ?? undefined\n }\n\n function notify(): void {\n for (const listener of [...subscribers])\n safely('a subscriber', listener)\n }\n\n function emit(id: string, list: Stored | undefined): void {\n const first = view(list)[0]\n for (const listener of [...listeners.get(id) ?? []])\n safely('an onChange listener', () => listener(first ? { ...first } : undefined), { integration: id })\n notify()\n }\n\n /** Re-reads one record and emits when its connections differ from what was cached. */\n function refresh(id: string): Stored | undefined {\n const before = cache.get(id)\n const after = read(id)\n if (before !== undefined && connectionsKey(before) !== connectionsKey(after))\n emit(id, after)\n return after\n }\n\n /** Writes a record (or deletes it) and emits when the connections changed. */\n function persist(id: string, value: Stored | undefined): void {\n const before = cached(id)\n const list = value?.length ? value : undefined\n store.write(id, list)\n cache.set(id, list ? structuredClone(list) : null)\n if (connectionsKey(before) !== connectionsKey(list))\n emit(id, list)\n }\n\n /** Replaces the connection of the same account, or adds it last. */\n function put(id: string, record: StoredIntegration): void {\n // Fresh from the store: another process may have added an account since the last read.\n const list = [...(refresh(id) ?? [])]\n const at = list.findIndex(s => s.connection.account === record.connection.account)\n if (at >= 0)\n list[at] = record\n else\n list.push(record)\n persist(id, list)\n }\n\n return {\n read,\n cached,\n refresh,\n persist,\n put,\n notify,\n /** Forgets a \"none stored\" read, so the next read may adopt a legacy sign-in. */\n forgetEmpty: (id: string): void => {\n if (cache.get(id) === null)\n cache.delete(id)\n },\n /** Integrations read so far. */\n known: (): string[] => [...cache.keys()],\n onChange(id: string, listener: (connection: Connection | undefined) => void): () => void {\n let set = listeners.get(id)\n if (!set)\n listeners.set(id, set = new Set())\n set.add(listener)\n return () => {\n set.delete(listener)\n if (set.size === 0 && listeners.get(id) === set)\n listeners.delete(id)\n }\n },\n subscribe(listener: () => void): () => void {\n subscribers.add(listener)\n return () => {\n subscribers.delete(listener)\n }\n },\n }\n}\n","/**\n * Refresh of expiring OAuth connections, on `resolveOAuthApiKey`\n * (`src/providers/oauth.ts`), the code provider sign-ins refresh through:\n * concurrent resolves of one connection share one refresh (keyed by the\n * stored grant and these functions' identities, hence one stable binding\n * per connection), and the refreshed grant is written only when the stored\n * one did not change meanwhile.\n *\n * An `oauth` method refreshes with the refresh-token grant; a `custom`\n * method with its own `refresh`. Each refresh is reported to `onRefresh`\n * (the `oauth:refresh` observation).\n */\n\nimport type { OAuthCredentials } from '@earendil-works/pi-ai/oauth'\nimport type { OAuthRefreshHookContext } from '../../types'\nimport type { Credential, IntegrationMethod } from '../api/sessions'\nimport type { OAuthIo } from './oauth'\nimport type { StoredIntegration } from './store'\nimport { errorMessage } from '../../errors'\nimport { resolveOAuthApiKey } from '../../providers/oauth'\nimport { validCredential } from './connect'\nimport { refreshOAuthGrant } from './oauth'\n\n/** Stands for \"no expiry\" in the `expires` field `resolveOAuthApiKey` requires; never stored. */\nconst NO_EXPIRY = Number.MAX_SAFE_INTEGER\n\n/** What the refresher reads and writes through (the registry's cached store). */\nexport interface RefresherDeps {\n /** OAuth network and clock. */\n io: OAuthIo\n /** Refresh this long before expiry. */\n skewMs: number\n /** Reads one connection fresh from the store. */\n read: (integrationId: string, account: string) => StoredIntegration | undefined\n /** Replaces one connection (the registry emits changes). */\n write: (integrationId: string, account: string, value: StoredIntegration) => void\n /** The method of a registered integration, `undefined` when unregistered. */\n method: (integrationId: string, methodId: string) => IntegrationMethod | undefined\n /** Told of each refresh that rotated a grant. */\n onRefresh?: (event: OAuthRefreshHookContext) => void | Promise<void>\n}\n\n/** Refreshes stored OAuth connections. */\nexport interface Refresher {\n /** Whether a stored credential expires within the skew. */\n due: (stored: StoredIntegration) => boolean\n /** The record after refreshing it (or as stored, when it can't refresh and has not expired). */\n refresh: (integrationId: string, name: string, stored: StoredIntegration) => Promise<StoredIntegration>\n}\n\ninterface Binding {\n read: () => Record<string, OAuthCredentials | undefined>\n write: (patch: Record<string, OAuthCredentials | undefined>) => void\n exchange: (providerId: string, credentials: Record<string, OAuthCredentials>, options?: { force?: boolean }) => Promise<{ newCredentials: OAuthCredentials, apiKey: string } | null>\n}\n\n/** The key a binding reads and writes its one connection under (never stored). */\nfunction slotOf(id: string, account: string): string {\n return `${id}#${account}`\n}\n\nfunction toOAuthCredentials(stored: StoredIntegration): OAuthCredentials | undefined {\n const credential = stored.credential\n if (credential.type !== 'oauth' || credential.expiresAt === undefined)\n return undefined\n return {\n access: credential.accessToken,\n refresh: stored.refreshToken ?? '',\n expires: credential.expiresAt,\n ...(credential.scopes ? { scopes: [...credential.scopes] } : {}),\n ...(credential.extra ? { extra: structuredClone(credential.extra) } : {}),\n }\n}\n\nfunction fromOAuthCredentials(next: OAuthCredentials): { credential: Credential, refreshToken?: string } {\n const scopes = Array.isArray(next.scopes) ? next.scopes as string[] : undefined\n const extra = next.extra && typeof next.extra === 'object' ? next.extra as Record<string, unknown> : undefined\n return {\n credential: {\n type: 'oauth',\n accessToken: next.access,\n ...(next.expires < NO_EXPIRY ? { expiresAt: next.expires } : {}),\n ...(scopes ? { scopes } : {}),\n ...(extra ? { extra } : {}),\n },\n ...(next.refresh ? { refreshToken: next.refresh } : {}),\n }\n}\n\n/**\n * Makes a refresher.\n *\n * @param deps - Store access, method lookup, I/O, skew and the refresh observer.\n */\nexport function createRefresher(deps: RefresherDeps): Refresher {\n const { io, skewMs } = deps\n const bindings = new Map<string, Binding>()\n\n async function renew(id: string, account: string, creds: OAuthCredentials): Promise<OAuthCredentials> {\n const stored = deps.read(id, account)\n const method = stored ? deps.method(id, stored.connection.methodId) : undefined\n if (method?.type === 'custom') {\n if (!method.refresh)\n throw new Error('the sign-in can\\'t be renewed')\n const current = fromOAuthCredentials(creds)\n const credential = { ...current.credential, ...(current.refreshToken ? { refreshToken: current.refreshToken } : {}) } as Credential\n const result = validCredential(await method.refresh(structuredClone(credential)))\n if (result?.type !== 'oauth')\n throw new Error('the refresh returned no OAuth credential')\n return {\n access: result.accessToken,\n refresh: result.refreshToken ?? creds.refresh,\n expires: result.expiresAt ?? NO_EXPIRY,\n ...(result.scopes ? { scopes: [...result.scopes] } : {}),\n ...(result.extra ? { extra: result.extra } : creds.extra ? { extra: creds.extra } : {}),\n }\n }\n if (method?.type !== 'oauth')\n throw new Error('the integration that issued it is not registered')\n const grant = await refreshOAuthGrant(method, creds.refresh, io)\n return {\n ...creds,\n access: grant.credential.accessToken,\n refresh: grant.refreshToken ?? creds.refresh,\n // A refreshed token without an expiry never needs another refresh by time.\n expires: grant.credential.expiresAt ?? NO_EXPIRY,\n ...(grant.credential.scopes ? { scopes: [...grant.credential.scopes] } : {}),\n }\n }\n\n /** Display names, for the refresh event (a spec's name can change). */\n const names = new Map<string, string>()\n\n function bindingFor(id: string, account: string): Binding {\n const slot = slotOf(id, account)\n const existing = bindings.get(slot)\n if (existing)\n return existing\n const binding: Binding = {\n read: () => {\n const stored = deps.read(id, account)\n return { [slot]: stored ? toOAuthCredentials(stored) : undefined }\n },\n write: (patch) => {\n const next = patch[slot]\n const current = deps.read(id, account)\n if (!next || !current)\n return\n deps.write(id, account, { connection: current.connection, ...fromOAuthCredentials(next) })\n // Once per rotation, here rather than per resolve: concurrent resolves share one refresh.\n const previous = toOAuthCredentials(current)\n if (deps.onRefresh && previous) {\n // Observers are any extension's: the refresh token stays in the store.\n const event: OAuthRefreshHookContext = { provider: names.get(id) ?? id, providerId: id, source: 'file', previousCredentials: { ...previous, refresh: '' }, credentials: { ...structuredClone(next), refresh: '' } }\n void Promise.resolve().then(() => deps.onRefresh?.(event)).catch(() => {})\n }\n },\n exchange: async (providerId, credentials, opts) => {\n const creds = credentials[providerId]\n if (!creds)\n return null\n if (!opts?.force && creds.expires - skewMs > io.now())\n return { newCredentials: creds, apiKey: creds.access }\n const next = await renew(id, account, creds)\n return { newCredentials: next, apiKey: next.access }\n },\n }\n bindings.set(slot, binding)\n return binding\n }\n\n function canRenew(id: string, stored: StoredIntegration): boolean {\n if (stored.refreshToken)\n return true\n const method = deps.method(id, stored.connection.methodId)\n return method?.type === 'custom' && typeof method.refresh === 'function'\n }\n\n return {\n due: (stored) => {\n const credential = stored.credential\n return credential.type === 'oauth' && credential.expiresAt !== undefined && credential.expiresAt - skewMs <= io.now()\n },\n\n async refresh(id, name, stored) {\n const { account } = stored.connection\n if (!canRenew(id, stored)) {\n if (stored.credential.type === 'oauth' && (stored.credential.expiresAt ?? Infinity) <= io.now())\n throw new Error(`${name}: the sign-in expired. Connect again.`)\n return stored\n }\n names.set(id, name)\n const binding = bindingFor(id, account)\n // Another writer (a second window) may rotate the grant between our\n // read and our write: `resolveOAuthApiKey` refuses that write, and one\n // more pass reads the grant it stored.\n for (let attempt = 0; ; attempt++) {\n try {\n await resolveOAuthApiKey({\n provider: name,\n providerId: id,\n credentialKey: slotOf(id, account),\n readCredentials: binding.read,\n writeCredentials: binding.write,\n getOAuthApiKey: binding.exchange,\n missingError: `${name} is not connected.`,\n refreshError: reason => `${name}: could not refresh the sign-in (${reason}). Connect again if it keeps failing.`,\n })\n break\n }\n catch (err) {\n if (attempt > 0 || !/changed during refresh/.test(errorMessage(err)))\n throw err\n }\n }\n const after = deps.read(id, account)\n if (!after)\n throw new Error(`${name} is not connected.`)\n return after\n },\n }\n}\n","/**\n * The process-wide integration registry: the specs extensions register\n * (first id wins), their connections (through an {@link AuthStorePort}),\n * the connect flows, and credential resolution with OAuth refresh\n * (`./refresh`).\n *\n * An integration holds several connections, one per account; the first is\n * the default. Connecting an account already connected replaces it; the\n * first connection becomes the default. A spec's `legacyCredentialKey`\n * adopts a legacy provider sign-in the first time the integration is read with\n * no connection.\n *\n * Each extension's `ctx.integrations` (`./domain`) is a view on one\n * registry; Settings reads the same registry to draw Connect / Disconnect.\n * Extension callbacks run through the registering extension's guard\n * (`./connect`).\n */\n\nimport type { OAuthLoginCallbacks } from '@earendil-works/pi-ai/oauth'\nimport type { Logger } from '../../logger'\nimport type { OAuthRefreshHookContext } from '../../types'\nimport type { Disposable } from '../api/core'\nimport type { Connection, Credential, IntegrationMethod, IntegrationSpec, IntegrationSpecPatch } from '../api/sessions'\nimport type { Guard } from '../runtime/deps'\nimport type { IntegrationFlowPort } from './connect'\nimport type { OAuthGrant, OAuthIo } from './oauth'\nimport type { AuthStorePort } from './store'\nimport { DEFAULT_ACCOUNT } from './auth-store-port'\nimport { accountLabel, checkBeforeConnect, commandCredential, customCredential, customFlowIo, validateSpec, verifyCredential } from './connect'\nimport { createConnectionCache, view } from './connections'\nimport { defaultOAuthIo, loginWithDeviceCode, loginWithPkce } from './oauth'\nimport { createRefresher } from './refresh'\n\n/** Who registers a spec: its callbacks run through this guard. */\nexport interface IntegrationRegistrant {\n /** The registering extension. */\n extensionId: string\n /** That extension's guard. */\n guard: Guard\n}\n\n/** A registered integration, as Settings lists it. */\nexport interface IntegrationEntry {\n /** The spec. */\n spec: IntegrationSpec\n /** The extension that registered it. */\n extensionId: string\n /** Its default connection, if connected. */\n connection: Connection | undefined\n /** Every connection, the default first. */\n connections: Connection[]\n}\n\n/** A registration in the registry. */\nexport interface IntegrationRegistration extends Disposable {\n /** Replaces fields of the spec (validated; throws a `TypeError` and keeps the spec when invalid). No-op once disposed. */\n update: (patch: IntegrationSpecPatch) => void\n}\n\n/** What a registry is built on. */\nexport interface IntegrationRegistryOptions {\n /** Where connections persist. */\n store: AuthStorePort\n /** Network, waits and clock of the OAuth flows. Defaults to the real ones. */\n io?: Partial<OAuthIo>\n /** Refresh OAuth tokens this long before they expire. Default 60 s. */\n refreshSkewMs?: number\n /** Logs store failures, throwing listeners and refused registrations. */\n logger?: Logger\n /** Told of each refresh that rotated a grant (hosts fire the `oauth:refresh` observation with it). */\n onRefresh?: (event: OAuthRefreshHookContext) => void | Promise<void>\n}\n\n/** The process-wide integration registry. */\nexport interface IntegrationRegistry {\n /**\n * Registers a spec. Returns `undefined` when the id is taken: the first\n * registration wins. Throws a `TypeError` for an invalid spec.\n */\n register: (spec: IntegrationSpec, by: IntegrationRegistrant) => IntegrationRegistration | undefined\n /** A registered integration. */\n get: (integrationId: string) => IntegrationEntry | undefined\n /** Every registered integration, in registration order. */\n list: () => IntegrationEntry[]\n /** Every stored connection of an integration, registered or not, the default first. */\n connections: (integrationId: string) => Connection[]\n /** The default stored connection of an integration, or the one of `account`. */\n connection: (integrationId: string, account?: string) => Connection | undefined\n /** The credential of a connection (an id means its default), refreshed when it expires soon. Rejects when not connected. */\n resolve: (connection: Connection | string) => Promise<Credential>\n /** Connects with an API key: check, verify, then persist. Uses the first `apiKey` method unless `methodId` names one. */\n connectKey: (integrationId: string, key: string, methodId?: string) => Promise<Connection>\n /** Runs an `oauth`, `command` or `custom` method, then verifies and persists. */\n start: (integrationId: string, methodId: string, port: IntegrationFlowPort, opts?: { signal?: AbortSignal }) => Promise<Connection>\n /** Makes the connection of `account` the default. Rejects when there is none. */\n setDefault: (integrationId: string, account: string) => Promise<void>\n /** Deletes the connection of `account`, or all of them. */\n disconnect: (integrationId: string, account?: string) => Promise<void>\n /** Calls `listener` with the default connection when any connection of an integration changes. Returns the unsubscribe. */\n onChange: (integrationId: string, listener: (connection: Connection | undefined) => void) => () => void\n /** Calls `listener` after any spec or connection change (for Settings). Returns the unsubscribe. */\n subscribe: (listener: () => void) => () => void\n /** Re-reads stored connections (all read so far, or one) after the store changed outside the registry; emits what changed. */\n reload: (integrationId?: string) => void\n}\n\ninterface Entry {\n spec: IntegrationSpec\n by: IntegrationRegistrant\n}\n\n/**\n * Makes a registry. Hosts make one per process and install it with\n * {@link installIntegrationRegistry}; tests make their own.\n *\n * @param options - The store, OAuth I/O, refresh skew, logger and refresh observer.\n */\nexport function createIntegrationRegistry(options: IntegrationRegistryOptions): IntegrationRegistry {\n const { logger } = options\n const io: OAuthIo = { ...defaultOAuthIo, ...options.io }\n const entries = new Map<string, Entry>()\n // Assigned below; registrations join the process-wide live set under it.\n\n let registry: IntegrationRegistry\n const store = createConnectionCache({ store: options.store, ...(logger ? { logger } : {}), now: io.now, spec: id => entries.get(id)?.spec })\n\n const refresher = createRefresher({\n io,\n skewMs: options.refreshSkewMs ?? 60_000,\n read: (id, account) => store.read(id)?.find(s => s.connection.account === account),\n write: (id, _account, value) => store.put(id, value),\n method: (id, methodId) => entries.get(id)?.spec.methods.find(m => m.id === methodId),\n ...(options.onRefresh ? { onRefresh: options.onRefresh } : {}),\n })\n\n function entryOf(id: string): Entry {\n const entry = entries.get(id)\n if (!entry)\n throw new Error(`No integration \"${id}\" is registered.`)\n return entry\n }\n\n function methodOf(entry: Entry, methodId: string): IntegrationMethod {\n const method = entry.spec.methods.find(m => m.id === methodId)\n if (!method)\n throw new Error(`${entry.spec.name} has no connect method \"${methodId}\".`)\n return method\n }\n\n async function finish(entry: Entry, method: IntegrationMethod, issued: Credential, refreshToken?: string, named: { account?: string, label?: string } = {}): Promise<Connection> {\n // A custom method hands its refresh token in the credential: the store keeps it beside.\n const { refreshToken: carried, ...credential } = issued as Credential & { refreshToken?: string }\n const refresh = refreshToken ?? carried\n await verifyCredential(entry.spec, entry.by.guard, credential as Credential)\n const account = named.account ?? await accountLabel(entry.spec, entry.by.guard, credential as Credential) ?? DEFAULT_ACCOUNT\n const label = named.label\n const connection: Connection = {\n integrationId: entry.spec.id,\n methodId: method.id,\n account,\n ...(label && label !== account ? { label } : {}),\n connectedAt: io.now(),\n }\n store.put(entry.spec.id, { connection, credential: credential as Credential, ...(refresh ? { refreshToken: refresh } : {}) })\n return view(store.cached(entry.spec.id)).find(c => c.account === account) ?? { ...connection }\n }\n\n async function runFlow(entry: Entry, method: Exclude<IntegrationMethod, { type: 'apiKey' }>, port: IntegrationFlowPort, signal: AbortSignal): Promise<Connection> {\n if (method.type === 'command')\n return finish(entry, method, await commandCredential(entry.spec, entry.by.guard, method, port, signal))\n const callbacks = port.oauthCallbacks({ integrationId: entry.spec.id, name: entry.spec.name, method, signal })\n if (method.type === 'custom') {\n const result = await customCredential(entry.spec, method, customFlowIo(callbacks, signal))\n return finish(entry, method, result.credential, undefined, result)\n }\n const grant: OAuthGrant = method.flow === 'device'\n ? await loginWithDeviceCode(method, callbacks, signal, io)\n : await loginWithPkce(method, entry.spec.name, callbacks, signal, io)\n signal.throwIfAborted()\n return finish(entry, method, grant.credential, grant.refreshToken)\n }\n\n function register(spec: IntegrationSpec, by: IntegrationRegistrant): IntegrationRegistration | undefined {\n validateSpec(spec)\n const holder = entries.get(spec.id)\n if (holder) {\n logger?.warn(`integrations.register: \"${spec.id}\" is already registered by ${holder.by.extensionId}; the first registration wins`, { extension: by.extensionId })\n return undefined\n }\n const entry: Entry = { spec, by }\n entries.set(spec.id, entry)\n live().add(registry)\n // Adopt a legacy sign-in now (read before as \"none stored\" included), so\n // pickers reading the credentials file see it signed in at once.\n if (spec.legacyCredentialKey) {\n store.forgetEmpty(spec.id)\n store.cached(spec.id)\n }\n store.notify()\n const current = (): boolean => entries.get(spec.id) === entry\n return {\n update: (patch) => {\n if (!current())\n return\n const next: IntegrationSpec = { ...entry.spec, ...patch, id: entry.spec.id }\n validateSpec(next)\n entry.spec = next\n store.notify()\n },\n dispose: () => {\n if (!current())\n return\n entries.delete(spec.id)\n if (entries.size === 0)\n live().delete(registry)\n store.notify()\n },\n }\n }\n\n registry = {\n register,\n\n get(id) {\n const entry = entries.get(id)\n if (!entry)\n return undefined\n const connections = view(store.cached(id))\n return { spec: entry.spec, extensionId: entry.by.extensionId, connection: connections[0], connections }\n },\n\n list: () => [...entries.keys()].map(id => registry.get(id) as IntegrationEntry),\n\n connections: id => view(store.cached(id)),\n\n connection(id, account) {\n const all = view(store.cached(id))\n return account === undefined ? all[0] : all.find(c => c.account === account)\n },\n\n async resolve(connection) {\n const id = typeof connection === 'string' ? connection : connection.integrationId\n const account = typeof connection === 'string' ? undefined : connection.account\n const name = entries.get(id)?.spec.name ?? id\n // Fresh from the store: another process may have connected, rotated or disconnected.\n const list = store.refresh(id)\n let stored = account === undefined ? list?.[0] : list?.find(s => s.connection.account === account)\n if (!stored)\n throw new Error(account === undefined || !list?.length ? `${name} is not connected.` : `${name}: the account \"${account}\" is not connected.`)\n if (refresher.due(stored))\n stored = await refresher.refresh(id, name, stored)\n return structuredClone(stored.credential)\n },\n\n async connectKey(id, key, methodId) {\n const entry = entryOf(id)\n const method = methodId ? methodOf(entry, methodId) : entry.spec.methods.find(m => m.type === 'apiKey')\n if (method?.type !== 'apiKey')\n throw new Error(`${entry.spec.name} does not connect with an API key.`)\n const trimmed = typeof key === 'string' ? key.trim() : ''\n if (!trimmed)\n throw new Error(`${entry.spec.name}: the key is empty.`)\n await checkBeforeConnect(entry.spec, entry.by.guard, method)\n return finish(entry, method, { type: 'apiKey', key: trimmed })\n },\n\n async start(id, methodId, port, opts = {}) {\n const entry = entryOf(id)\n const method = methodOf(entry, methodId)\n const signal = opts.signal ?? new AbortController().signal\n signal.throwIfAborted()\n if (method.type === 'apiKey')\n throw new Error(`${entry.spec.name}: \"${method.id}\" takes an API key; use connect.key.`)\n await checkBeforeConnect(entry.spec, entry.by.guard, method)\n signal.throwIfAborted()\n return runFlow(entry, method, port, signal)\n },\n\n async setDefault(id, account) {\n const list = [...(store.refresh(id) ?? [])]\n const at = list.findIndex(s => s.connection.account === account)\n if (at < 0)\n throw new Error(`${entries.get(id)?.spec.name ?? id}: the account \"${account}\" is not connected.`)\n if (at === 0)\n return\n const [record] = list.splice(at, 1)\n store.persist(id, [record!, ...list])\n },\n\n async disconnect(id, account) {\n const list = store.refresh(id)\n if (!list)\n return\n if (account === undefined)\n return store.persist(id, undefined)\n const rest = list.filter(s => s.connection.account !== account)\n if (rest.length !== list.length)\n store.persist(id, rest)\n },\n\n onChange: (id, listener) => store.onChange(id, listener),\n\n subscribe: listener => store.subscribe(listener),\n\n reload(id) {\n for (const each of id === undefined ? store.known() : [id])\n store.refresh(each)\n },\n }\n return registry\n}\n\n/** Process-wide set of the registries holding at least one registration (every runtime has its own). */\nconst LIVE_SLOT = Symbol.for('zidane.integration-registries')\n\nfunction live(): Set<IntegrationRegistry> {\n const slots = globalThis as { [LIVE_SLOT]?: Set<IntegrationRegistry> }\n slots[LIVE_SLOT] ??= new Set()\n return slots[LIVE_SLOT]\n}\n\n/**\n * The registry where `integrationId` is registered: the installed one first,\n * else any runtime's (they share the credentials file, so any will do).\n * `undefined` when no extension registers it in this process.\n *\n * @param integrationId - The integration id.\n */\nexport function findIntegrationRegistry(integrationId: string): IntegrationRegistry | undefined {\n const installed = integrationRegistry()\n if (installed?.get(integrationId))\n return installed\n for (const registry of live()) {\n if (registry.get(integrationId))\n return registry\n }\n return undefined\n}\n\n/**\n * Connects an integration from a provider's \"Sign in\" (`ProviderDescriptor.integration`):\n * runs one of its flow methods (`oauth` or `custom`) on the host's provider\n * sign-in callbacks, asking which through `onSelect` when there are several.\n * The connection is stored by the registry; nothing is returned to persist.\n *\n * @param integrationId - The integration the provider signs in with.\n * @param callbacks - The sign-in UI's login callbacks (`callbackServer: false` for a remote client).\n * @param opts - Aborts the flow.\n * @param opts.signal - Aborts the flow.\n */\nexport async function signInWithIntegration(integrationId: string, callbacks: OAuthLoginCallbacks & { callbackServer?: false }, opts: { signal?: AbortSignal } = {}): Promise<Connection> {\n const registry = findIntegrationRegistry(integrationId)\n const entry = registry?.get(integrationId)\n if (!registry || !entry)\n throw new Error(`No extension offers the \"${integrationId}\" integration here. Enable the extension that provides it, then sign in again.`)\n const flows = entry.spec.methods.filter(m => m.type === 'oauth' || m.type === 'custom')\n if (flows.length === 0)\n throw new Error(`${entry.spec.name} has no browser sign-in. Connect it from Settings › Integrations.`)\n const methodId = flows.length === 1\n ? flows[0]!.id\n : await callbacks.onSelect({ message: `Sign in to ${entry.spec.name} with`, options: flows.map(m => ({ id: m.id, label: m.label })) })\n if (!methodId)\n throw new Error('Sign-in cancelled.')\n return registry.start(integrationId, methodId, {\n oauthCallbacks: () => callbacks,\n runCommand: async () => { throw new Error('A provider sign-in runs no command.') },\n }, opts.signal ? { signal: opts.signal } : {})\n}\n\n/** Process-wide slot, so every zidane copy in the process (src and dist) shares one registry. */\nconst REGISTRY_SLOT = Symbol.for('zidane.integration-registry')\n\nfunction slot(): { [REGISTRY_SLOT]?: IntegrationRegistry } {\n return globalThis as { [REGISTRY_SLOT]?: IntegrationRegistry }\n}\n\n/**\n * Makes `registry` the process's registry, the one `ctx.integrations` uses\n * by default. Disposing restores the previous one, if `registry` is still\n * the installed one.\n *\n * @param registry - The registry to install.\n */\nexport function installIntegrationRegistry(registry: IntegrationRegistry): Disposable {\n const previous = slot()[REGISTRY_SLOT]\n slot()[REGISTRY_SLOT] = registry\n return {\n dispose: () => {\n if (slot()[REGISTRY_SLOT] !== registry)\n return\n if (previous)\n slot()[REGISTRY_SLOT] = previous\n else\n delete slot()[REGISTRY_SLOT]\n },\n }\n}\n\n/** The process's registry, when a host installed one. */\nexport function integrationRegistry(): IntegrationRegistry | undefined {\n return slot()[REGISTRY_SLOT]\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"registry-CBoYDIND.js","names":[],"sources":["../src/extensions/integrations/oauth.ts","../src/extensions/integrations/connect.ts","../src/extensions/integrations/connections.ts","../src/extensions/integrations/refresh.ts","../src/extensions/integrations/registry.ts"],"sourcesContent":["/**\n * The OAuth flows of an integration's `oauth` method: authorization code\n * with PKCE (RFC 7636) and the device authorization grant (RFC 8628), plus\n * the refresh-token grant.\n *\n * PKCE runs on the `zidane/oauth` kit (`src/oauth-entry.ts`): the loopback\n * callback server and page, `generatePkce`, and the redirect-or-paste race\n * (`awaitAuthorizationCode`), so a remote sign-in (`callbackServer: false`)\n * works as it does for provider sign-ins. The kit is imported lazily:\n * `node:http` stays off the graph until a flow runs.\n *\n * Every network call goes through {@link OAuthIo.fetch} and every wait\n * through {@link OAuthIo.wait}, so tests drive a fake server and clock.\n */\n\nimport type { OAuthLoginCallbacks } from '@earendil-works/pi-ai/oauth'\nimport type { Credential, IntegrationMethod } from '../api/sessions'\n\n/** An `oauth` integration method. */\nexport type OAuthIntegrationMethod = Extract<IntegrationMethod, { type: 'oauth' }>\n\n/** An OAuth credential. */\nexport type OAuthCredential = Extract<Credential, { type: 'oauth' }>\n\n/** The I/O an OAuth flow uses. */\nexport interface OAuthIo {\n /** Sends an HTTP request (the global `fetch` by default). */\n fetch: (url: string, init: RequestInit) => Promise<Response>\n /** Resolves after `ms`, or rejects when `signal` aborts. Device polling waits through it. */\n wait: (ms: number, signal: AbortSignal) => Promise<void>\n /** The current time in ms; expiry times are computed from it. */\n now: () => number\n}\n\n/** What a flow or a refresh yields. */\nexport interface OAuthGrant {\n /** The credential to hand out. */\n credential: OAuthCredential\n /** The refresh token, when the server issued one. */\n refreshToken?: string\n}\n\n/** A token endpoint refused a request. */\nexport class OAuthTokenError extends Error {\n /**\n * @param code - The OAuth `error` code (`invalid_grant`, `authorization_pending`, …), or `http_<status>`.\n * @param description - The server's `error_description`, when given.\n * @param status - The HTTP status.\n */\n constructor(readonly code: string, readonly description: string | undefined, readonly status: number) {\n super(`OAuth token request failed (${status}): ${[code, description].filter(Boolean).join(': ')}`)\n this.name = 'OAuthTokenError'\n }\n}\n\n/** A hung token endpoint must not hold a resolve forever. */\nconst TOKEN_REQUEST_TIMEOUT_MS = 30_000\n/** RFC 8628 §3.2: default polling interval. */\nconst DEFAULT_POLL_INTERVAL_S = 5\n/** RFC 8628 §3.5: `slow_down` adds 5 seconds. */\nconst SLOW_DOWN_STEP_S = 5\n/** When the device endpoint omits `expires_in`. */\nconst DEFAULT_DEVICE_EXPIRY_S = 900\n\n/** Default {@link OAuthIo}: real fetch, timers and clock. */\nexport const defaultOAuthIo: OAuthIo = {\n fetch: (url, init) => fetch(url, init),\n wait: (ms, signal) => new Promise<void>((resolve, reject) => {\n if (signal.aborted)\n return reject(signal.reason)\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n function onAbort(): void {\n clearTimeout(timer)\n reject(signal.reason)\n }\n signal.addEventListener('abort', onAbort, { once: true })\n }),\n now: () => Date.now(),\n}\n\ninterface TokenResponse {\n access_token?: unknown\n refresh_token?: unknown\n expires_in?: unknown\n scope?: unknown\n error?: unknown\n error_description?: unknown\n}\n\n/** Reads a token or device response: JSON, or a form-encoded body (GitHub without `Accept`). */\nfunction parseBody(text: string): Record<string, unknown> {\n try {\n const parsed: unknown = JSON.parse(text)\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))\n return parsed as Record<string, unknown>\n }\n catch { /* not JSON */ }\n return Object.fromEntries(new URLSearchParams(text))\n}\n\nasync function postForm(io: OAuthIo, url: string, body: Record<string, string>, signal?: AbortSignal): Promise<Record<string, unknown>> {\n const timeout = AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS)\n const response = await io.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams(body).toString(),\n signal: signal ? AbortSignal.any([signal, timeout]) : timeout,\n })\n const parsed = parseBody(await response.text())\n if (!response.ok || typeof parsed.error === 'string') {\n const code = typeof parsed.error === 'string' ? parsed.error : `http_${response.status}`\n throw new OAuthTokenError(code, typeof parsed.error_description === 'string' ? parsed.error_description : undefined, response.status)\n }\n return parsed\n}\n\nfunction clientFields(method: OAuthIntegrationMethod): Record<string, string> {\n return { client_id: method.clientId, ...(method.clientSecret ? { client_secret: method.clientSecret } : {}) }\n}\n\n/** The grant a token response carries. */\nfunction toGrant(tokens: TokenResponse, method: OAuthIntegrationMethod, io: OAuthIo, previousRefresh?: string): OAuthGrant {\n if (typeof tokens.access_token !== 'string' || !tokens.access_token)\n throw new Error('The token endpoint returned no access token.')\n const expiresIn = Number(tokens.expires_in)\n const scopes = typeof tokens.scope === 'string' && tokens.scope.trim()\n ? tokens.scope.trim().split(/[\\s,]+/)\n : method.scopes ? [...method.scopes] : undefined\n const refreshToken = typeof tokens.refresh_token === 'string' && tokens.refresh_token ? tokens.refresh_token : previousRefresh\n return {\n credential: {\n type: 'oauth',\n accessToken: tokens.access_token,\n ...(Number.isFinite(expiresIn) && expiresIn > 0 ? { expiresAt: io.now() + expiresIn * 1000 } : {}),\n ...(scopes ? { scopes } : {}),\n },\n ...(refreshToken ? { refreshToken } : {}),\n }\n}\n\nfunction randomHex(bytes: number): string {\n return [...crypto.getRandomValues(new Uint8Array(bytes))].map(b => b.toString(16).padStart(2, '0')).join('')\n}\n\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted)\n return Promise.reject(signal.reason)\n return new Promise<T>((resolve, reject) => {\n const onAbort = (): void => reject(signal.reason)\n signal.addEventListener('abort', onAbort, { once: true })\n promise.then(resolve, reject).finally(() => signal.removeEventListener('abort', onAbort))\n })\n}\n\n/**\n * The loopback port and path of a PKCE method's `redirectUri`. Throws for a\n * URI the callback server can't serve.\n *\n * @param redirectUri - The method's `redirectUri`.\n */\nexport function parseLoopbackRedirect(redirectUri: string | undefined): { port: number, path: string } {\n let url: URL\n try {\n url = new URL(redirectUri ?? '')\n }\n catch {\n throw new TypeError(`redirectUri must be a loopback URL with a port, like http://localhost:51121/callback (got ${JSON.stringify(redirectUri)})`)\n }\n const port = Number(url.port)\n if (url.protocol !== 'http:' || !['localhost', '127.0.0.1'].includes(url.hostname) || !Number.isInteger(port) || port <= 0)\n throw new TypeError(`redirectUri must be http://localhost:<port>/<path> or http://127.0.0.1:<port>/<path> (got ${redirectUri})`)\n return { port, path: url.pathname || '/' }\n}\n\n/**\n * Runs the authorization code flow with PKCE: opens the authorize URL\n * (`callbacks.onAuth`), takes the code from the loopback redirect or a\n * pasted URL, and exchanges it at the token endpoint.\n *\n * @param method - The `oauth` method (`flow: 'pkce'`).\n * @param name - The integration's name, shown on the callback page.\n * @param callbacks - The host's login callbacks.\n * @param signal - Aborts the flow.\n * @param io - Network and clock.\n */\nexport async function loginWithPkce(method: OAuthIntegrationMethod, name: string, callbacks: OAuthLoginCallbacks, signal: AbortSignal, io: OAuthIo = defaultOAuthIo): Promise<OAuthGrant> {\n const { port, path } = parseLoopbackRedirect(method.redirectUri)\n const redirectUri = method.redirectUri as string\n const kit = await import('../../oauth-entry')\n const { verifier, challenge } = await kit.generatePkce()\n const state = randomHex(16)\n const server = await kit.startCallbackServer({\n port,\n path,\n expectedState: state,\n providerName: name,\n renderPage: kit.renderDefaultCallbackPage,\n successMessage: 'You can close this tab.',\n onListenError: 'reject',\n ...(kit.listensForCallback(callbacks) ? {} : { listen: false }),\n }).catch((err: unknown) => {\n if ((err as { code?: unknown } | null)?.code === 'EADDRINUSE')\n throw new Error(`Port ${port} is busy, likely another sign-in in progress. Finish or close it, then try again.`)\n throw err\n })\n try {\n const url = new URL(method.authorizeUrl)\n url.searchParams.set('client_id', method.clientId)\n url.searchParams.set('redirect_uri', redirectUri)\n url.searchParams.set('response_type', 'code')\n if (method.scopes?.length)\n url.searchParams.set('scope', method.scopes.join(' '))\n url.searchParams.set('code_challenge', challenge)\n url.searchParams.set('code_challenge_method', 'S256')\n url.searchParams.set('state', state)\n for (const [key, value] of Object.entries(method.params ?? {}))\n url.searchParams.set(key, value)\n callbacks.onAuth({\n url: url.toString(),\n instructions: `Sign in to ${name} in your browser. If the browser is on another machine, paste the final redirect URL here.`,\n })\n const pending = kit.awaitAuthorizationCode(server, state, callbacks)\n pending.catch(() => {})\n const code = await abortable(pending, signal)\n callbacks.onProgress?.('Exchanging authorization code for tokens...')\n const tokens = await postForm(io, method.tokenUrl, {\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n ...clientFields(method),\n }, signal)\n return toGrant(tokens, method, io)\n }\n finally {\n server.close()\n }\n}\n\n/**\n * Runs the device authorization grant: asks the device endpoint for a code,\n * shows it (`callbacks.onDeviceCode`), then polls the token endpoint until\n * the user approves, refuses, or the code expires.\n *\n * @param method - The `oauth` method (`flow: 'device'`).\n * @param callbacks - The host's login callbacks.\n * @param signal - Aborts the flow, polling included.\n * @param io - Network, waits and clock.\n */\nexport async function loginWithDeviceCode(method: OAuthIntegrationMethod, callbacks: OAuthLoginCallbacks, signal: AbortSignal, io: OAuthIo = defaultOAuthIo): Promise<OAuthGrant> {\n if (!method.deviceUrl)\n throw new TypeError('A device flow needs a deviceUrl')\n const start = await postForm(io, method.deviceUrl, {\n ...clientFields(method),\n ...(method.scopes?.length ? { scope: method.scopes.join(' ') } : {}),\n }, signal)\n const deviceCode = start.device_code\n const userCode = start.user_code\n const verificationUri = start.verification_uri ?? start.verification_url\n if (typeof deviceCode !== 'string' || typeof userCode !== 'string' || typeof verificationUri !== 'string')\n throw new Error('The device endpoint returned no device_code, user_code or verification_uri.')\n let intervalS = Number(start.interval) > 0 ? Number(start.interval) : DEFAULT_POLL_INTERVAL_S\n const expiresInS = Number(start.expires_in) > 0 ? Number(start.expires_in) : DEFAULT_DEVICE_EXPIRY_S\n callbacks.onDeviceCode({\n userCode,\n verificationUri: typeof start.verification_uri_complete === 'string' ? start.verification_uri_complete : verificationUri,\n intervalSeconds: intervalS,\n expiresInSeconds: expiresInS,\n })\n // Count waited time rather than reading the clock: the code expires after\n // `expires_in` of polling, and a driven clock in tests never advances.\n let waitedMs = 0\n while (waitedMs < expiresInS * 1000) {\n await io.wait(intervalS * 1000, signal)\n waitedMs += intervalS * 1000\n try {\n const tokens = await postForm(io, method.tokenUrl, {\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n ...clientFields(method),\n }, signal)\n return toGrant(tokens, method, io)\n }\n catch (err) {\n if (!(err instanceof OAuthTokenError))\n throw err\n if (err.code === 'authorization_pending')\n continue\n if (err.code === 'slow_down') {\n intervalS += SLOW_DOWN_STEP_S\n continue\n }\n if (err.code === 'access_denied')\n throw new Error('The sign-in was refused.', { cause: err })\n if (err.code === 'expired_token')\n break\n throw err\n }\n }\n throw new Error('The device code expired before the sign-in finished. Try again.')\n}\n\n/**\n * Exchanges a refresh token for a new access token. Keeps the old refresh\n * token when the server does not rotate it.\n *\n * @param method - The `oauth` method the grant came from.\n * @param refreshToken - The stored refresh token.\n * @param io - Network and clock.\n * @param signal - Aborts the request.\n */\nexport async function refreshOAuthGrant(method: OAuthIntegrationMethod, refreshToken: string, io: OAuthIo = defaultOAuthIo, signal?: AbortSignal): Promise<OAuthGrant> {\n const tokens = await postForm(io, method.tokenUrl, {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n ...clientFields(method),\n }, signal)\n return toGrant(tokens, method, io, refreshToken)\n}\n","/**\n * The checks around connecting: spec validation, and the extension\n * callbacks (`beforeConnect`, `verify`, `account`, `parseCommandOutput`)\n * run through the registering extension's guard. A callback's throw or\n * returned message becomes the connect's error; a timeout refuses too.\n * A `custom` method's `connect` runs here too, on the host's flow UI\n * ({@link customFlowIo}).\n */\n\nimport type { OAuthLoginCallbacks } from '@earendil-works/pi-ai/oauth'\nimport type { Credential, IntegrationCustomMethod, IntegrationCustomResult, IntegrationFlowIO, IntegrationMethod, IntegrationSpec } from '../api/sessions'\nimport type { Guard } from '../runtime/deps'\nimport { errorMessage } from '../../errors'\nimport { parseLoopbackRedirect } from './oauth'\n\n/** What a host shows while a connect flow runs. */\nexport interface IntegrationFlowRequest {\n /** The integration being connected. */\n integrationId: string\n /** Its display name. */\n name: string\n /** The method the user picked. */\n method: IntegrationMethod\n /** Aborts when the flow is cancelled. */\n signal: AbortSignal\n}\n\n/** The outcome of a `command` method's process. */\nexport interface IntegrationCommandResult {\n /** Standard output; the credential is read from it. */\n stdout: string\n /** Standard error, quoted when the command fails. */\n stderr?: string\n /** Exit code; anything but 0 fails the connect. */\n exitCode: number\n}\n\n/**\n * What a host provides for connect flows. The TUI, desktop and daemon each\n * implement it once; Settings and `ctx.integrations.connect.start` both use it.\n */\nexport interface IntegrationFlowPort {\n /**\n * The login callbacks for an OAuth flow: open `onAuth`'s URL, show\n * `onDeviceCode`'s code, read a pasted redirect (`onManualCodeInput`),\n * show progress. The same callbacks hosts already build for provider\n * sign-in (`runOAuthLogin` in `src/chat/oauth.ts`); set\n * `callbackServer: false` for a client of a remote host.\n */\n oauthCallbacks: (request: IntegrationFlowRequest) => OAuthLoginCallbacks & { callbackServer?: false }\n /** Runs a `command` method's argv (no shell) and returns its output. */\n runCommand: (command: readonly string[], opts: { signal: AbortSignal }) => Promise<IntegrationCommandResult>\n}\n\n/** Integration ids become credential-file keys: no `#` (reserved for provider slots), no spaces. */\nconst ID_PATTERN = /^[\\w.:-]+$/\n\n/**\n * Throws a `TypeError` naming the first problem of a spec.\n *\n * @param spec - The spec an extension registers.\n */\nexport function validateSpec(spec: IntegrationSpec): void {\n if (!spec || typeof spec.id !== 'string' || !ID_PATTERN.test(spec.id))\n throw new TypeError(`integrations.register: id must match ${ID_PATTERN} (got ${JSON.stringify(spec?.id)})`)\n if (typeof spec.name !== 'string' || !spec.name)\n throw new TypeError(`integrations.register(${spec.id}): name is required`)\n if (!Array.isArray(spec.methods) || spec.methods.length === 0)\n throw new TypeError(`integrations.register(${spec.id}): at least one method is required`)\n const ids = new Set<string>()\n for (const method of spec.methods) {\n const where = `integrations.register(${spec.id}): method ${method.id}`\n if (!method.id || ids.has(method.id))\n throw new TypeError(`integrations.register(${spec.id}): method ids must be unique and non-empty (got ${JSON.stringify(method.id)})`)\n ids.add(method.id)\n if (method.type === 'oauth' && method.flow === 'pkce') {\n try {\n parseLoopbackRedirect(method.redirectUri)\n }\n catch (err) {\n throw new TypeError(`${where}: ${errorMessage(err)}`)\n }\n }\n if (method.type === 'oauth' && method.flow === 'device' && !method.deviceUrl)\n throw new TypeError(`${where}: a device flow needs a deviceUrl`)\n if (method.type === 'command' && (!Array.isArray(method.command) || method.command.length === 0))\n throw new TypeError(`${where}: command is empty`)\n if (method.type === 'custom' && typeof method.connect !== 'function')\n throw new TypeError(`${where}: a custom method needs a connect function`)\n if (method.type === 'custom' && method.refresh !== undefined && typeof method.refresh !== 'function')\n throw new TypeError(`${where}: refresh must be a function`)\n }\n if (spec.beforeConnect !== undefined && typeof spec.beforeConnect !== 'function')\n throw new TypeError(`integrations.register(${spec.id}): beforeConnect must be a function`)\n if (spec.legacyCredentialKey !== undefined && (typeof spec.legacyCredentialKey !== 'string' || !spec.legacyCredentialKey || spec.legacyCredentialKey.includes('#')))\n throw new TypeError(`integrations.register(${spec.id}): legacyCredentialKey must be a credentials-file key without \"#\"`)\n}\n\n/**\n * Runs the spec's `beforeConnect` for `method`. Throws its message when it\n * refuses (or throws); a timeout refuses too.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param method - The method the user picked.\n */\nexport async function checkBeforeConnect(spec: IntegrationSpec, guard: Guard, method: IntegrationMethod): Promise<void> {\n const check = spec.beforeConnect\n if (!check)\n return\n const outcome = await guard.call(`integrations.beforeConnect(${spec.id})`, async () => {\n try {\n return { refusal: await check(method) }\n }\n catch (err) {\n return { refusal: errorMessage(err) || `${spec.name} refused to connect.` }\n }\n }, { fallback: { refusal: `${spec.name}: the connect check did not answer.` } })\n if (typeof outcome?.refusal === 'string' && outcome.refusal)\n throw new Error(outcome.refusal)\n}\n\n/**\n * The {@link IntegrationFlowIO} of a `custom` method, on the host's OAuth\n * login callbacks: links (`onAuth`), codes (`onDeviceCode`), questions\n * (`onPrompt`, `onSelect`) and progress lines (`onProgress`).\n *\n * @param callbacks - The host's login callbacks for this flow.\n * @param signal - Aborts the flow.\n */\nexport function customFlowIo(callbacks: OAuthLoginCallbacks, signal: AbortSignal): IntegrationFlowIO {\n return {\n openUrl: url => callbacks.onAuth({ url }),\n showCode: (code, url) => callbacks.onDeviceCode({ userCode: code, verificationUri: url ?? '' }),\n prompt: async (opts) => {\n try {\n if (opts.options)\n return await callbacks.onSelect({ message: opts.message, options: opts.options.map(o => ({ id: o.id, label: o.label })) })\n return await callbacks.onPrompt({ message: opts.message, ...(opts.placeholder ? { placeholder: opts.placeholder } : {}), ...(opts.allowEmpty ? { allowEmpty: true } : {}) })\n }\n catch (err) {\n // A host rejects a dismissed question; the flow reads that as a cancel unless the whole flow was aborted.\n if (signal.aborted)\n throw err\n return undefined\n }\n },\n signal,\n log: message => callbacks.onProgress?.(message),\n }\n}\n\n/**\n * Runs a `custom` method's `connect` and checks what it returns. Its\n * errors become the connect's (prefixed with the integration's name).\n *\n * @param spec - The integration.\n * @param method - The `custom` method.\n * @param io - The flow UI.\n */\nexport async function customCredential(spec: IntegrationSpec, method: IntegrationCustomMethod, io: IntegrationFlowIO): Promise<IntegrationCustomResult> {\n let result: IntegrationCustomResult\n try {\n result = await method.connect(io)\n }\n catch (err) {\n io.signal.throwIfAborted()\n throw new Error(`${spec.name}: ${errorMessage(err) || 'the sign-in failed'}`, { cause: err })\n }\n io.signal.throwIfAborted()\n const credential = validCredential(result?.credential)\n if (!credential)\n throw new Error(`${spec.name}: the sign-in returned no usable credential.`)\n const text = (v: unknown): string | undefined => (typeof v === 'string' && v.trim() ? v.trim() : undefined)\n const account = text(result.account)\n const label = text(result.label)\n return { credential, ...(account ? { account } : {}), ...(label ? { label } : {}) }\n}\n\n/**\n * The credential an extension built, checked; `undefined` when it is not one.\n *\n * @param value - What `parseCommandOutput` returned.\n */\nexport function validCredential(value: unknown): Credential | undefined {\n const c = value as Partial<Record<string, unknown>> | null\n if (!c || typeof c !== 'object')\n return undefined\n if (c.type === 'apiKey' && typeof c.key === 'string' && c.key)\n return { type: 'apiKey', key: c.key }\n if (c.type === 'token' && typeof c.token === 'string' && c.token)\n return { type: 'token', token: c.token }\n if (c.type === 'oauth' && typeof c.accessToken === 'string' && c.accessToken) {\n return {\n type: 'oauth',\n accessToken: c.accessToken,\n ...(typeof c.expiresAt === 'number' ? { expiresAt: c.expiresAt } : {}),\n ...(Array.isArray(c.scopes) ? { scopes: c.scopes.filter((s): s is string => typeof s === 'string') } : {}),\n ...(typeof c.refreshToken === 'string' && c.refreshToken ? { refreshToken: c.refreshToken } : {}),\n ...(c.extra && typeof c.extra === 'object' && !Array.isArray(c.extra) ? { extra: structuredClone(c.extra as Record<string, unknown>) } : {}),\n }\n }\n return undefined\n}\n\n/**\n * Runs the spec's `verify`. Throws when it refuses, throws or times out.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param credential - The credential to check.\n */\nexport async function verifyCredential(spec: IntegrationSpec, guard: Guard, credential: Credential): Promise<void> {\n const verify = spec.verify\n if (!verify)\n return\n const outcome = await guard.call(`integrations.verify(${spec.id})`, async () => {\n try {\n return { error: await verify(credential) }\n }\n catch (err) {\n return { error: errorMessage(err) || 'verification failed' }\n }\n }, { fallback: { error: 'verification did not answer' } })\n if (outcome?.error)\n throw new Error(`${spec.name}: ${outcome.error}`)\n}\n\n/**\n * The spec's `account` label for a credential. `undefined` when it has none, throws or times out.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param credential - The new credential.\n */\nexport async function accountLabel(spec: IntegrationSpec, guard: Guard, credential: Credential): Promise<string | undefined> {\n const account = spec.account\n if (!account)\n return undefined\n const label = await guard.call(`integrations.account(${spec.id})`, () => account(credential))\n return typeof label === 'string' && label ? label : undefined\n}\n\n/**\n * Runs a `command` method through the host and reads its credential: the\n * spec's `parseCommandOutput`, or by default the trimmed stdout as a token.\n *\n * @param spec - The integration.\n * @param guard - The registering extension's guard.\n * @param method - The `command` method.\n * @param port - The host's command runner.\n * @param signal - Aborts the command.\n */\nexport async function commandCredential(spec: IntegrationSpec, guard: Guard, method: Extract<IntegrationMethod, { type: 'command' }>, port: IntegrationFlowPort, signal: AbortSignal): Promise<Credential> {\n const shown = method.command.join(' ')\n const result = await port.runCommand(method.command, { signal })\n if (result.exitCode !== 0) {\n const detail = (result.stderr ?? '').trim().split('\\n')[0]\n throw new Error(`${shown} failed (exit ${result.exitCode})${detail ? `: ${detail}` : ''}`)\n }\n const parse = spec.parseCommandOutput\n if (!parse) {\n const token = result.stdout.trim()\n if (!token)\n throw new Error(`${shown} printed nothing.`)\n return { type: 'token', token }\n }\n const parsed = guard.callSync(`integrations.parseCommandOutput(${spec.id})`, () => {\n try {\n return { credential: parse(result.stdout) as unknown }\n }\n catch (err) {\n return { error: errorMessage(err) }\n }\n })\n if (parsed && 'error' in parsed)\n throw new Error(`${spec.name}: could not read the command output: ${parsed.error}`)\n const credential = validCredential(parsed?.credential)\n if (!credential)\n throw new Error(`${spec.name}: parseCommandOutput returned no usable credential.`)\n return credential\n}\n","/**\n * The registry's view of stored connections: a cache over the\n * {@link AuthStorePort} (one list per integration, the default first),\n * change detection and notification (`onChange` listeners per integration,\n * Settings subscribers), and the adoption of a legacy provider sign-in\n * (`IntegrationSpec.legacyCredentialKey`).\n */\n\nimport type { Logger } from '../../logger'\nimport type { Connection, IntegrationSpec } from '../api/sessions'\nimport type { AuthStorePort, StoredIntegration } from './store'\nimport { errorMessage } from '../../errors'\nimport { DEFAULT_ACCOUNT } from './auth-store-port'\n\n/** An integration's stored connections, the default first. */\nexport type Stored = StoredIntegration[]\n\n/** What the cache reads through. */\nexport interface ConnectionCacheDeps {\n store: AuthStorePort\n logger?: Logger\n /** The clock adopted connections are stamped with. */\n now: () => number\n /** The registered spec of an integration (for its `legacyCredentialKey`). */\n spec: (integrationId: string) => IntegrationSpec | undefined\n}\n\n/** The connections a store record holds, as `connections()` lists them (`isDefault` on the first). */\nexport function view(list: Stored | undefined | null): Connection[] {\n return (list ?? []).map((s, i) => ({ ...s.connection, ...(i === 0 ? { isDefault: true } : {}) }))\n}\n\nfunction connectionsKey(list: Stored | undefined | null): string {\n return JSON.stringify((list ?? []).map(s => s.connection))\n}\n\n/**\n * Makes the cache.\n *\n * @param deps - Store, logger, clock and spec lookup.\n */\nexport function createConnectionCache(deps: ConnectionCacheDeps) {\n const { store, logger } = deps\n /** Stored records read so far; `null` is \"read, none stored\". */\n const cache = new Map<string, Stored | null>()\n const listeners = new Map<string, Set<(connection: Connection | undefined) => void>>()\n const subscribers = new Set<() => void>()\n\n const safely = (what: string, fn: () => void, attrs?: Record<string, unknown>): void => {\n try {\n fn()\n }\n catch (err) {\n logger?.warn(`integrations: ${what} threw`, { ...attrs, error: errorMessage(err) })\n }\n }\n\n /** Adopts the legacy sign-in a spec names, when the integration has no connection. */\n function adoptLegacy(id: string): Stored | undefined {\n const spec = deps.spec(id)\n const key = spec?.legacyCredentialKey\n if (!spec || !key || !store.readLegacy)\n return undefined\n const grant = store.readLegacy(key)\n if (!grant)\n return undefined\n const methods = spec.methods\n const method = grant.credential.type === 'oauth'\n ? methods.find(m => m.type === 'oauth' || m.type === 'custom')\n : methods.find(m => m.type === 'apiKey') ?? methods.find(m => m.type === 'command')\n const account = grant.account ?? DEFAULT_ACCOUNT\n const record: StoredIntegration = {\n connection: {\n integrationId: id,\n methodId: (method ?? methods[0]!).id,\n account,\n ...(grant.label && grant.label !== account ? { label: grant.label } : {}),\n connectedAt: deps.now(),\n },\n credential: grant.credential,\n ...(grant.refreshToken ? { refreshToken: grant.refreshToken } : {}),\n }\n store.write(id, [record])\n store.markLegacy?.(key, id)\n logger?.info(`integrations: adopted the legacy sign-in under \"${key}\" as a connection of ${id}`)\n return [record]\n }\n\n /** Reads one record fresh from the store (adopting a legacy sign-in when it is empty). */\n function read(id: string): Stored | undefined {\n let value: Stored | undefined\n try {\n // A port written before several connections returns one record: read it as the only one.\n const raw = store.read(id) as Stored | StoredIntegration | undefined\n value = raw === undefined || Array.isArray(raw) ? raw : [raw]\n if (!value?.length)\n value = adoptLegacy(id)\n }\n catch (err) {\n logger?.warn('integrations: could not read the stored connections', { integration: id, error: errorMessage(err) })\n }\n const list = value?.length ? value : undefined\n cache.set(id, list ?? null)\n return list\n }\n\n function cached(id: string): Stored | undefined {\n const value = cache.get(id)\n return value === undefined ? read(id) : value ?? undefined\n }\n\n function notify(): void {\n for (const listener of [...subscribers])\n safely('a subscriber', listener)\n }\n\n function emit(id: string, list: Stored | undefined): void {\n const first = view(list)[0]\n for (const listener of [...listeners.get(id) ?? []])\n safely('an onChange listener', () => listener(first ? { ...first } : undefined), { integration: id })\n notify()\n }\n\n /** Re-reads one record and emits when its connections differ from what was cached. */\n function refresh(id: string): Stored | undefined {\n const before = cache.get(id)\n const after = read(id)\n if (before !== undefined && connectionsKey(before) !== connectionsKey(after))\n emit(id, after)\n return after\n }\n\n /** Writes a record (or deletes it) and emits when the connections changed. */\n function persist(id: string, value: Stored | undefined): void {\n const before = cached(id)\n const list = value?.length ? value : undefined\n store.write(id, list)\n cache.set(id, list ? structuredClone(list) : null)\n if (connectionsKey(before) !== connectionsKey(list))\n emit(id, list)\n }\n\n /** Replaces the connection of the same account, or adds it last. */\n function put(id: string, record: StoredIntegration): void {\n // Fresh from the store: another process may have added an account since the last read.\n const list = [...(refresh(id) ?? [])]\n const at = list.findIndex(s => s.connection.account === record.connection.account)\n if (at >= 0)\n list[at] = record\n else\n list.push(record)\n persist(id, list)\n }\n\n return {\n read,\n cached,\n refresh,\n persist,\n put,\n notify,\n /** Forgets a \"none stored\" read, so the next read may adopt a legacy sign-in. */\n forgetEmpty: (id: string): void => {\n if (cache.get(id) === null)\n cache.delete(id)\n },\n /** Integrations read so far. */\n known: (): string[] => [...cache.keys()],\n onChange(id: string, listener: (connection: Connection | undefined) => void): () => void {\n let set = listeners.get(id)\n if (!set)\n listeners.set(id, set = new Set())\n set.add(listener)\n return () => {\n set.delete(listener)\n if (set.size === 0 && listeners.get(id) === set)\n listeners.delete(id)\n }\n },\n subscribe(listener: () => void): () => void {\n subscribers.add(listener)\n return () => {\n subscribers.delete(listener)\n }\n },\n }\n}\n","/**\n * Refresh of expiring OAuth connections, on `resolveOAuthApiKey`\n * (`src/providers/oauth.ts`), the code provider sign-ins refresh through:\n * concurrent resolves of one connection share one refresh (keyed by the\n * stored grant and these functions' identities, hence one stable binding\n * per connection), and the refreshed grant is written only when the stored\n * one did not change meanwhile.\n *\n * An `oauth` method refreshes with the refresh-token grant; a `custom`\n * method with its own `refresh`. Each refresh is reported to `onRefresh`\n * (the `oauth:refresh` observation).\n */\n\nimport type { OAuthCredentials } from '@earendil-works/pi-ai/oauth'\nimport type { OAuthRefreshHookContext } from '../../types'\nimport type { Credential, IntegrationMethod } from '../api/sessions'\nimport type { OAuthIo } from './oauth'\nimport type { StoredIntegration } from './store'\nimport { errorMessage } from '../../errors'\nimport { resolveOAuthApiKey } from '../../providers/oauth'\nimport { validCredential } from './connect'\nimport { refreshOAuthGrant } from './oauth'\n\n/** Stands for \"no expiry\" in the `expires` field `resolveOAuthApiKey` requires; never stored. */\nconst NO_EXPIRY = Number.MAX_SAFE_INTEGER\n\n/** What the refresher reads and writes through (the registry's cached store). */\nexport interface RefresherDeps {\n /** OAuth network and clock. */\n io: OAuthIo\n /** Refresh this long before expiry. */\n skewMs: number\n /** Reads one connection fresh from the store. */\n read: (integrationId: string, account: string) => StoredIntegration | undefined\n /** Replaces one connection (the registry emits changes). */\n write: (integrationId: string, account: string, value: StoredIntegration) => void\n /** The method of a registered integration, `undefined` when unregistered. */\n method: (integrationId: string, methodId: string) => IntegrationMethod | undefined\n /** Told of each refresh that rotated a grant. */\n onRefresh?: (event: OAuthRefreshHookContext) => void | Promise<void>\n}\n\n/** Refreshes stored OAuth connections. */\nexport interface Refresher {\n /** Whether a stored credential expires within the skew. */\n due: (stored: StoredIntegration) => boolean\n /** The record after refreshing it (or as stored, when it can't refresh and has not expired). */\n refresh: (integrationId: string, name: string, stored: StoredIntegration) => Promise<StoredIntegration>\n}\n\ninterface Binding {\n read: () => Record<string, OAuthCredentials | undefined>\n write: (patch: Record<string, OAuthCredentials | undefined>) => void\n exchange: (providerId: string, credentials: Record<string, OAuthCredentials>, options?: { force?: boolean }) => Promise<{ newCredentials: OAuthCredentials, apiKey: string } | null>\n}\n\n/** The key a binding reads and writes its one connection under (never stored). */\nfunction slotOf(id: string, account: string): string {\n return `${id}#${account}`\n}\n\nfunction toOAuthCredentials(stored: StoredIntegration): OAuthCredentials | undefined {\n const credential = stored.credential\n if (credential.type !== 'oauth' || credential.expiresAt === undefined)\n return undefined\n return {\n access: credential.accessToken,\n refresh: stored.refreshToken ?? '',\n expires: credential.expiresAt,\n ...(credential.scopes ? { scopes: [...credential.scopes] } : {}),\n ...(credential.extra ? { extra: structuredClone(credential.extra) } : {}),\n }\n}\n\nfunction fromOAuthCredentials(next: OAuthCredentials): { credential: Credential, refreshToken?: string } {\n const scopes = Array.isArray(next.scopes) ? next.scopes as string[] : undefined\n const extra = next.extra && typeof next.extra === 'object' ? next.extra as Record<string, unknown> : undefined\n return {\n credential: {\n type: 'oauth',\n accessToken: next.access,\n ...(next.expires < NO_EXPIRY ? { expiresAt: next.expires } : {}),\n ...(scopes ? { scopes } : {}),\n ...(extra ? { extra } : {}),\n },\n ...(next.refresh ? { refreshToken: next.refresh } : {}),\n }\n}\n\n/**\n * Makes a refresher.\n *\n * @param deps - Store access, method lookup, I/O, skew and the refresh observer.\n */\nexport function createRefresher(deps: RefresherDeps): Refresher {\n const { io, skewMs } = deps\n const bindings = new Map<string, Binding>()\n\n async function renew(id: string, account: string, creds: OAuthCredentials): Promise<OAuthCredentials> {\n const stored = deps.read(id, account)\n const method = stored ? deps.method(id, stored.connection.methodId) : undefined\n if (method?.type === 'custom') {\n if (!method.refresh)\n throw new Error('the sign-in can\\'t be renewed')\n const current = fromOAuthCredentials(creds)\n const credential = { ...current.credential, ...(current.refreshToken ? { refreshToken: current.refreshToken } : {}) } as Credential\n const result = validCredential(await method.refresh(structuredClone(credential)))\n if (result?.type !== 'oauth')\n throw new Error('the refresh returned no OAuth credential')\n return {\n access: result.accessToken,\n refresh: result.refreshToken ?? creds.refresh,\n expires: result.expiresAt ?? NO_EXPIRY,\n ...(result.scopes ? { scopes: [...result.scopes] } : {}),\n ...(result.extra ? { extra: result.extra } : creds.extra ? { extra: creds.extra } : {}),\n }\n }\n if (method?.type !== 'oauth')\n throw new Error('the integration that issued it is not registered')\n const grant = await refreshOAuthGrant(method, creds.refresh, io)\n return {\n ...creds,\n access: grant.credential.accessToken,\n refresh: grant.refreshToken ?? creds.refresh,\n // A refreshed token without an expiry never needs another refresh by time.\n expires: grant.credential.expiresAt ?? NO_EXPIRY,\n ...(grant.credential.scopes ? { scopes: [...grant.credential.scopes] } : {}),\n }\n }\n\n /** Display names, for the refresh event (a spec's name can change). */\n const names = new Map<string, string>()\n\n function bindingFor(id: string, account: string): Binding {\n const slot = slotOf(id, account)\n const existing = bindings.get(slot)\n if (existing)\n return existing\n const binding: Binding = {\n read: () => {\n const stored = deps.read(id, account)\n return { [slot]: stored ? toOAuthCredentials(stored) : undefined }\n },\n write: (patch) => {\n const next = patch[slot]\n const current = deps.read(id, account)\n if (!next || !current)\n return\n deps.write(id, account, { connection: current.connection, ...fromOAuthCredentials(next) })\n // Once per rotation, here rather than per resolve: concurrent resolves share one refresh.\n const previous = toOAuthCredentials(current)\n if (deps.onRefresh && previous) {\n // Observers are any extension's: the refresh token stays in the store.\n const event: OAuthRefreshHookContext = { provider: names.get(id) ?? id, providerId: id, source: 'file', previousCredentials: { ...previous, refresh: '' }, credentials: { ...structuredClone(next), refresh: '' } }\n void Promise.resolve().then(() => deps.onRefresh?.(event)).catch(() => {})\n }\n },\n exchange: async (providerId, credentials, opts) => {\n const creds = credentials[providerId]\n if (!creds)\n return null\n if (!opts?.force && creds.expires - skewMs > io.now())\n return { newCredentials: creds, apiKey: creds.access }\n const next = await renew(id, account, creds)\n return { newCredentials: next, apiKey: next.access }\n },\n }\n bindings.set(slot, binding)\n return binding\n }\n\n function canRenew(id: string, stored: StoredIntegration): boolean {\n if (stored.refreshToken)\n return true\n const method = deps.method(id, stored.connection.methodId)\n return method?.type === 'custom' && typeof method.refresh === 'function'\n }\n\n return {\n due: (stored) => {\n const credential = stored.credential\n return credential.type === 'oauth' && credential.expiresAt !== undefined && credential.expiresAt - skewMs <= io.now()\n },\n\n async refresh(id, name, stored) {\n const { account } = stored.connection\n if (!canRenew(id, stored)) {\n if (stored.credential.type === 'oauth' && (stored.credential.expiresAt ?? Infinity) <= io.now())\n throw new Error(`${name}: the sign-in expired. Connect again.`)\n return stored\n }\n names.set(id, name)\n const binding = bindingFor(id, account)\n // Another writer (a second window) may rotate the grant between our\n // read and our write: `resolveOAuthApiKey` refuses that write, and one\n // more pass reads the grant it stored.\n for (let attempt = 0; ; attempt++) {\n try {\n await resolveOAuthApiKey({\n provider: name,\n providerId: id,\n credentialKey: slotOf(id, account),\n readCredentials: binding.read,\n writeCredentials: binding.write,\n getOAuthApiKey: binding.exchange,\n missingError: `${name} is not connected.`,\n refreshError: reason => `${name}: could not refresh the sign-in (${reason}). Connect again if it keeps failing.`,\n })\n break\n }\n catch (err) {\n if (attempt > 0 || !/changed during refresh/.test(errorMessage(err)))\n throw err\n }\n }\n const after = deps.read(id, account)\n if (!after)\n throw new Error(`${name} is not connected.`)\n return after\n },\n }\n}\n","/**\n * The process-wide integration registry: the specs extensions register\n * (first id wins), their connections (through an {@link AuthStorePort}),\n * the connect flows, and credential resolution with OAuth refresh\n * (`./refresh`).\n *\n * An integration holds several connections, one per account; the first is\n * the default. Connecting an account already connected replaces it; the\n * first connection becomes the default. A spec's `legacyCredentialKey`\n * adopts a legacy provider sign-in the first time the integration is read with\n * no connection.\n *\n * Each extension's `ctx.integrations` (`./domain`) is a view on one\n * registry; Settings reads the same registry to draw Connect / Disconnect.\n * Extension callbacks run through the registering extension's guard\n * (`./connect`).\n */\n\nimport type { OAuthLoginCallbacks } from '@earendil-works/pi-ai/oauth'\nimport type { Logger } from '../../logger'\nimport type { OAuthRefreshHookContext } from '../../types'\nimport type { Disposable } from '../api/core'\nimport type { Connection, Credential, IntegrationMethod, IntegrationSpec, IntegrationSpecPatch } from '../api/sessions'\nimport type { Guard } from '../runtime/deps'\nimport type { IntegrationFlowPort } from './connect'\nimport type { OAuthGrant, OAuthIo } from './oauth'\nimport type { AuthStorePort } from './store'\nimport { DEFAULT_ACCOUNT } from './auth-store-port'\nimport { accountLabel, checkBeforeConnect, commandCredential, customCredential, customFlowIo, validateSpec, verifyCredential } from './connect'\nimport { createConnectionCache, view } from './connections'\nimport { defaultOAuthIo, loginWithDeviceCode, loginWithPkce } from './oauth'\nimport { createRefresher } from './refresh'\n\n/** Who registers a spec: its callbacks run through this guard. */\nexport interface IntegrationRegistrant {\n /** The registering extension. */\n extensionId: string\n /** That extension's guard. */\n guard: Guard\n}\n\n/** A registered integration, as Settings lists it. */\nexport interface IntegrationEntry {\n /** The spec. */\n spec: IntegrationSpec\n /** The extension that registered it. */\n extensionId: string\n /** Its default connection, if connected. */\n connection: Connection | undefined\n /** Every connection, the default first. */\n connections: Connection[]\n}\n\n/** A registration in the registry. */\nexport interface IntegrationRegistration extends Disposable {\n /** Replaces fields of the spec (validated; throws a `TypeError` and keeps the spec when invalid). No-op once disposed. */\n update: (patch: IntegrationSpecPatch) => void\n}\n\n/** What a registry is built on. */\nexport interface IntegrationRegistryOptions {\n /** Where connections persist. */\n store: AuthStorePort\n /** Network, waits and clock of the OAuth flows. Defaults to the real ones. */\n io?: Partial<OAuthIo>\n /** Refresh OAuth tokens this long before they expire. Default 60 s. */\n refreshSkewMs?: number\n /** Logs store failures, throwing listeners and refused registrations. */\n logger?: Logger\n /** Told of each refresh that rotated a grant (hosts fire the `oauth:refresh` observation with it). */\n onRefresh?: (event: OAuthRefreshHookContext) => void | Promise<void>\n}\n\n/** The process-wide integration registry. */\nexport interface IntegrationRegistry {\n /**\n * Registers a spec. Returns `undefined` when the id is taken: the first\n * registration wins. Throws a `TypeError` for an invalid spec.\n */\n register: (spec: IntegrationSpec, by: IntegrationRegistrant) => IntegrationRegistration | undefined\n /** A registered integration. */\n get: (integrationId: string) => IntegrationEntry | undefined\n /** Every registered integration, in registration order. */\n list: () => IntegrationEntry[]\n /** Every stored connection of an integration, registered or not, the default first. */\n connections: (integrationId: string) => Connection[]\n /** The default stored connection of an integration, or the one of `account`. */\n connection: (integrationId: string, account?: string) => Connection | undefined\n /** The credential of a connection (an id means its default), refreshed when it expires soon. Rejects when not connected. */\n resolve: (connection: Connection | string) => Promise<Credential>\n /** Connects with an API key: check, verify, then persist. Uses the first `apiKey` method unless `methodId` names one. */\n connectKey: (integrationId: string, key: string, methodId?: string) => Promise<Connection>\n /** Runs an `oauth`, `command` or `custom` method, then verifies and persists. */\n start: (integrationId: string, methodId: string, port: IntegrationFlowPort, opts?: { signal?: AbortSignal }) => Promise<Connection>\n /** Makes the connection of `account` the default. Rejects when there is none. */\n setDefault: (integrationId: string, account: string) => Promise<void>\n /** Deletes the connection of `account`, or all of them. */\n disconnect: (integrationId: string, account?: string) => Promise<void>\n /** Calls `listener` with the default connection when any connection of an integration changes. Returns the unsubscribe. */\n onChange: (integrationId: string, listener: (connection: Connection | undefined) => void) => () => void\n /** Calls `listener` after any spec or connection change (for Settings). Returns the unsubscribe. */\n subscribe: (listener: () => void) => () => void\n /** Re-reads stored connections (all read so far, or one) after the store changed outside the registry; emits what changed. */\n reload: (integrationId?: string) => void\n}\n\ninterface Entry {\n spec: IntegrationSpec\n by: IntegrationRegistrant\n}\n\n/**\n * Makes a registry. Hosts make one per process and install it with\n * {@link installIntegrationRegistry}; tests make their own.\n *\n * @param options - The store, OAuth I/O, refresh skew, logger and refresh observer.\n */\nexport function createIntegrationRegistry(options: IntegrationRegistryOptions): IntegrationRegistry {\n const { logger } = options\n const io: OAuthIo = { ...defaultOAuthIo, ...options.io }\n const entries = new Map<string, Entry>()\n // Assigned below; registrations join the process-wide live set under it.\n\n let registry: IntegrationRegistry\n const store = createConnectionCache({ store: options.store, ...(logger ? { logger } : {}), now: io.now, spec: id => entries.get(id)?.spec })\n\n const refresher = createRefresher({\n io,\n skewMs: options.refreshSkewMs ?? 60_000,\n read: (id, account) => store.read(id)?.find(s => s.connection.account === account),\n write: (id, _account, value) => store.put(id, value),\n method: (id, methodId) => entries.get(id)?.spec.methods.find(m => m.id === methodId),\n ...(options.onRefresh ? { onRefresh: options.onRefresh } : {}),\n })\n\n function entryOf(id: string): Entry {\n const entry = entries.get(id)\n if (!entry)\n throw new Error(`No integration \"${id}\" is registered.`)\n return entry\n }\n\n function methodOf(entry: Entry, methodId: string): IntegrationMethod {\n const method = entry.spec.methods.find(m => m.id === methodId)\n if (!method)\n throw new Error(`${entry.spec.name} has no connect method \"${methodId}\".`)\n return method\n }\n\n async function finish(entry: Entry, method: IntegrationMethod, issued: Credential, refreshToken?: string, named: { account?: string, label?: string } = {}): Promise<Connection> {\n // A custom method hands its refresh token in the credential: the store keeps it beside.\n const { refreshToken: carried, ...credential } = issued as Credential & { refreshToken?: string }\n const refresh = refreshToken ?? carried\n await verifyCredential(entry.spec, entry.by.guard, credential as Credential)\n const account = named.account ?? await accountLabel(entry.spec, entry.by.guard, credential as Credential) ?? DEFAULT_ACCOUNT\n const label = named.label\n const connection: Connection = {\n integrationId: entry.spec.id,\n methodId: method.id,\n account,\n ...(label && label !== account ? { label } : {}),\n connectedAt: io.now(),\n }\n store.put(entry.spec.id, { connection, credential: credential as Credential, ...(refresh ? { refreshToken: refresh } : {}) })\n return view(store.cached(entry.spec.id)).find(c => c.account === account) ?? { ...connection }\n }\n\n async function runFlow(entry: Entry, method: Exclude<IntegrationMethod, { type: 'apiKey' }>, port: IntegrationFlowPort, signal: AbortSignal): Promise<Connection> {\n if (method.type === 'command')\n return finish(entry, method, await commandCredential(entry.spec, entry.by.guard, method, port, signal))\n const callbacks = port.oauthCallbacks({ integrationId: entry.spec.id, name: entry.spec.name, method, signal })\n if (method.type === 'custom') {\n const result = await customCredential(entry.spec, method, customFlowIo(callbacks, signal))\n return finish(entry, method, result.credential, undefined, result)\n }\n const grant: OAuthGrant = method.flow === 'device'\n ? await loginWithDeviceCode(method, callbacks, signal, io)\n : await loginWithPkce(method, entry.spec.name, callbacks, signal, io)\n signal.throwIfAborted()\n return finish(entry, method, grant.credential, grant.refreshToken)\n }\n\n function register(spec: IntegrationSpec, by: IntegrationRegistrant): IntegrationRegistration | undefined {\n validateSpec(spec)\n const holder = entries.get(spec.id)\n if (holder) {\n logger?.warn(`integrations.register: \"${spec.id}\" is already registered by ${holder.by.extensionId}; the first registration wins`, { extension: by.extensionId })\n return undefined\n }\n const entry: Entry = { spec, by }\n entries.set(spec.id, entry)\n live().add(registry)\n // Adopt a legacy sign-in now (read before as \"none stored\" included), so\n // pickers reading the credentials file see it signed in at once.\n if (spec.legacyCredentialKey) {\n store.forgetEmpty(spec.id)\n store.cached(spec.id)\n }\n store.notify()\n const current = (): boolean => entries.get(spec.id) === entry\n return {\n update: (patch) => {\n if (!current())\n return\n const next: IntegrationSpec = { ...entry.spec, ...patch, id: entry.spec.id }\n validateSpec(next)\n entry.spec = next\n store.notify()\n },\n dispose: () => {\n if (!current())\n return\n entries.delete(spec.id)\n if (entries.size === 0)\n live().delete(registry)\n store.notify()\n },\n }\n }\n\n registry = {\n register,\n\n get(id) {\n const entry = entries.get(id)\n if (!entry)\n return undefined\n const connections = view(store.cached(id))\n return { spec: entry.spec, extensionId: entry.by.extensionId, connection: connections[0], connections }\n },\n\n list: () => [...entries.keys()].map(id => registry.get(id) as IntegrationEntry),\n\n connections: id => view(store.cached(id)),\n\n connection(id, account) {\n const all = view(store.cached(id))\n return account === undefined ? all[0] : all.find(c => c.account === account)\n },\n\n async resolve(connection) {\n const id = typeof connection === 'string' ? connection : connection.integrationId\n const account = typeof connection === 'string' ? undefined : connection.account\n const name = entries.get(id)?.spec.name ?? id\n // Fresh from the store: another process may have connected, rotated or disconnected.\n const list = store.refresh(id)\n let stored = account === undefined ? list?.[0] : list?.find(s => s.connection.account === account)\n if (!stored)\n throw new Error(account === undefined || !list?.length ? `${name} is not connected.` : `${name}: the account \"${account}\" is not connected.`)\n if (refresher.due(stored))\n stored = await refresher.refresh(id, name, stored)\n return structuredClone(stored.credential)\n },\n\n async connectKey(id, key, methodId) {\n const entry = entryOf(id)\n const method = methodId ? methodOf(entry, methodId) : entry.spec.methods.find(m => m.type === 'apiKey')\n if (method?.type !== 'apiKey')\n throw new Error(`${entry.spec.name} does not connect with an API key.`)\n const trimmed = typeof key === 'string' ? key.trim() : ''\n if (!trimmed)\n throw new Error(`${entry.spec.name}: the key is empty.`)\n await checkBeforeConnect(entry.spec, entry.by.guard, method)\n return finish(entry, method, { type: 'apiKey', key: trimmed })\n },\n\n async start(id, methodId, port, opts = {}) {\n const entry = entryOf(id)\n const method = methodOf(entry, methodId)\n const signal = opts.signal ?? new AbortController().signal\n signal.throwIfAborted()\n if (method.type === 'apiKey')\n throw new Error(`${entry.spec.name}: \"${method.id}\" takes an API key; use connect.key.`)\n await checkBeforeConnect(entry.spec, entry.by.guard, method)\n signal.throwIfAborted()\n return runFlow(entry, method, port, signal)\n },\n\n async setDefault(id, account) {\n const list = [...(store.refresh(id) ?? [])]\n const at = list.findIndex(s => s.connection.account === account)\n if (at < 0)\n throw new Error(`${entries.get(id)?.spec.name ?? id}: the account \"${account}\" is not connected.`)\n if (at === 0)\n return\n const [record] = list.splice(at, 1)\n store.persist(id, [record!, ...list])\n },\n\n async disconnect(id, account) {\n const list = store.refresh(id)\n if (!list)\n return\n if (account === undefined)\n return store.persist(id, undefined)\n const rest = list.filter(s => s.connection.account !== account)\n if (rest.length !== list.length)\n store.persist(id, rest)\n },\n\n onChange: (id, listener) => store.onChange(id, listener),\n\n subscribe: listener => store.subscribe(listener),\n\n reload(id) {\n for (const each of id === undefined ? store.known() : [id])\n store.refresh(each)\n },\n }\n return registry\n}\n\n/** Process-wide set of the registries holding at least one registration (every runtime has its own). */\nconst LIVE_SLOT = Symbol.for('zidane.integration-registries')\n\nfunction live(): Set<IntegrationRegistry> {\n const slots = globalThis as { [LIVE_SLOT]?: Set<IntegrationRegistry> }\n slots[LIVE_SLOT] ??= new Set()\n return slots[LIVE_SLOT]\n}\n\n/**\n * The registry where `integrationId` is registered: the installed one first,\n * else any runtime's (they share the credentials file, so any will do).\n * `undefined` when no extension registers it in this process.\n *\n * @param integrationId - The integration id.\n */\nexport function findIntegrationRegistry(integrationId: string): IntegrationRegistry | undefined {\n const installed = integrationRegistry()\n if (installed?.get(integrationId))\n return installed\n for (const registry of live()) {\n if (registry.get(integrationId))\n return registry\n }\n return undefined\n}\n\n/**\n * Connects an integration from a provider's \"Sign in\" (`ProviderDescriptor.integration`):\n * runs one of its flow methods (`oauth` or `custom`) on the host's provider\n * sign-in callbacks, asking which through `onSelect` when there are several.\n * The connection is stored by the registry; nothing is returned to persist.\n *\n * @param integrationId - The integration the provider signs in with.\n * @param callbacks - The sign-in UI's login callbacks (`callbackServer: false` for a remote client).\n * @param opts - Aborts the flow.\n * @param opts.signal - Aborts the flow.\n */\nexport async function signInWithIntegration(integrationId: string, callbacks: OAuthLoginCallbacks & { callbackServer?: false }, opts: { signal?: AbortSignal } = {}): Promise<Connection> {\n const registry = findIntegrationRegistry(integrationId)\n const entry = registry?.get(integrationId)\n if (!registry || !entry)\n throw new Error(`No extension offers the \"${integrationId}\" integration here. Enable the extension that provides it, then sign in again.`)\n const flows = entry.spec.methods.filter(m => m.type === 'oauth' || m.type === 'custom')\n if (flows.length === 0)\n throw new Error(`${entry.spec.name} has no browser sign-in. Connect it from Settings › Integrations.`)\n const methodId = flows.length === 1\n ? flows[0]!.id\n : await callbacks.onSelect({ message: `Sign in to ${entry.spec.name} with`, options: flows.map(m => ({ id: m.id, label: m.label })) })\n if (!methodId)\n throw new Error('Sign-in cancelled.')\n return registry.start(integrationId, methodId, {\n oauthCallbacks: () => callbacks,\n runCommand: async () => { throw new Error('A provider sign-in runs no command.') },\n }, opts.signal ? { signal: opts.signal } : {})\n}\n\n/** Process-wide slot, so every zidane copy in the process (src and dist) shares one registry. */\nconst REGISTRY_SLOT = Symbol.for('zidane.integration-registry')\n\nfunction slot(): { [REGISTRY_SLOT]?: IntegrationRegistry } {\n return globalThis as { [REGISTRY_SLOT]?: IntegrationRegistry }\n}\n\n/**\n * Makes `registry` the process's registry, the one `ctx.integrations` uses\n * by default. Disposing restores the previous one, if `registry` is still\n * the installed one.\n *\n * @param registry - The registry to install.\n */\nexport function installIntegrationRegistry(registry: IntegrationRegistry): Disposable {\n const previous = slot()[REGISTRY_SLOT]\n slot()[REGISTRY_SLOT] = registry\n return {\n dispose: () => {\n if (slot()[REGISTRY_SLOT] !== registry)\n return\n if (previous)\n slot()[REGISTRY_SLOT] = previous\n else\n delete slot()[REGISTRY_SLOT]\n },\n }\n}\n\n/** The process's registry, when a host installed one. */\nexport function integrationRegistry(): IntegrationRegistry | undefined {\n return slot()[REGISTRY_SLOT]\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { i as writeJsonFileAtomic, t as OWNER_ONLY } from "./atomic-write-BewQOXlD.js";
2
- import { S as writeCredentialEntry, u as readCredentialEntry } from "./credentials-wwETW8rI.js";
2
+ import { S as writeCredentialEntry, u as readCredentialEntry } from "./credentials-D71duF64.js";
3
3
  import { t as HostRegistryError } from "./types-CiUp8oom.js";
4
4
  import { i as urlCarriesSecrets, n as displayHostUrl, r as parseRemoteHostUrl, t as RemoteHostError } from "./url-znC3tmKE.js";
5
5
  import { resolve } from "node:path";
@@ -240,4 +240,4 @@ function createHostRegistry(options) {
240
240
  //#endregion
241
241
  export { probeUnsavedHost as i, hostCredentialKey as n, hostsFilePath as r, createHostRegistry as t };
242
242
 
243
- //# sourceMappingURL=registry-B6Ww76iW.js.map
243
+ //# sourceMappingURL=registry-CQOU5jvF.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry-B6Ww76iW.js","names":[],"sources":["../src/chat/hosts/registry.ts"],"sourcesContent":["/**\n * The client's saved hosts: `<dataDir>/hosts.json` (0600, atomic writes) for\n * the entries, the credentials store (`credentials.json`, entry\n * `hosts:<id>`) for their tokens, so tokens sit with every other secret and\n * never in the listable file.\n *\n * Every call reads the files fresh: TUI and GUI windows on one machine share\n * them. Connecting is injected (`probe`): `src/chat` cannot reach the\n * WebSocket client, `createWsHostRegistry` (`zidane/ipc`) wires it.\n */\nimport type { HostEntry, HostProbe, HostProbeResult, HostTestResult } from './types'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { OWNER_ONLY, writeJsonFileAtomic } from '../../atomic-write'\nimport { readCredentialEntry, writeCredentialEntry } from '../credentials'\nimport { HostRegistryError } from './types'\nimport { displayHostUrl, parseRemoteHostUrl, RemoteHostError, urlCarriesSecrets } from './url'\n\nconst FILE_VERSION = 1\n\ninterface HostsFile {\n version: typeof FILE_VERSION\n hosts: HostEntry[]\n}\n\n/**\n * Returns the path of the file that stores the saved hosts.\n *\n * @param dataDir - Application data directory.\n */\nexport function hostsFilePath(dataDir: string): string {\n return resolve(dataDir, 'hosts.json')\n}\n\n/**\n * Where a host's token lives in the credentials store.\n *\n * @param id - Host id.\n */\nexport function hostCredentialKey(id: string): string {\n return `hosts:${id}`\n}\n\n/** Saved remote hosts and their tokens, persisted under the data directory. */\nexport interface HostRegistry {\n /** Returns every saved host. */\n list: () => HostEntry[]\n /** Returns the saved host with this id, or `undefined`. */\n get: (id: string) => HostEntry | undefined\n /**\n * Verify the host (it must answer as a zidane-server with this token),\n * then save it and its token. Adding a host already saved (same server\n * id) updates its URL and token in place, keeping its name unless one is\n * given. Throws {@link HostRegistryError}.\n */\n add: (input: { url: string, token: string, name?: string }) => Promise<HostEntry>\n /** Forget the entry and its token; `false` when it wasn't saved. */\n remove: (id: string) => boolean\n /** Renames a saved host. Throws {@link HostRegistryError} for an empty name, or when no saved host has this id. */\n rename: (id: string, name: string) => HostEntry\n /**\n * Move a saved host to a new URL, with its saved token: saved only once\n * the same server (same id) answers there. Name and token are kept.\n * `lastConnectedAt` and the server's name and version are refreshed.\n * Throws {@link HostRegistryError} (`identity` when another server answers).\n */\n setUrl: (id: string, url: string) => Promise<HostEntry>\n /** Connect with the saved token and say who answered; refreshes `lastConnectedAt` on success. */\n test: (id: string) => Promise<HostTestResult>\n /** The saved token, for transports. Never display it. */\n token: (id: string) => string | undefined\n}\n\nfunction isEntry(value: unknown): value is HostEntry {\n const e = value as Partial<HostEntry> | null\n return !!e && typeof e === 'object' && typeof e.id === 'string' && typeof e.name === 'string' && typeof e.url === 'string' && typeof e.addedAt === 'number'\n}\n\n/** An entry saved before URLs were checked for credentials: read without them (it fails auth, the user re-adds it). */\nfunction withoutUrlSecrets(entry: HostEntry): HostEntry {\n const raw = entry.url\n return urlCarriesSecrets(raw) ? { ...entry, url: displayHostUrl(raw) } : entry\n}\n\nfunction readHosts(dataDir: string): HostEntry[] {\n const path = hostsFilePath(dataDir)\n if (!existsSync(path))\n return []\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as Partial<HostsFile>\n return Array.isArray(parsed.hosts) ? parsed.hosts.filter(isEntry).map(withoutUrlSecrets) : []\n }\n catch {\n return []\n }\n}\n\nfunction writeHosts(dataDir: string, hosts: HostEntry[]): void {\n const file: HostsFile = { version: FILE_VERSION, hosts }\n writeJsonFileAtomic(hostsFilePath(dataDir), file, OWNER_ONLY)\n}\n\nfunction cleanName(name: string): string {\n const trimmed = name.trim()\n if (!trimmed)\n throw new HostRegistryError('invalid-name', 'A host needs a name.')\n return trimmed\n}\n\nfunction checkedUrl(raw: string): string {\n try {\n return parseRemoteHostUrl(raw)\n }\n catch (err) {\n throw new HostRegistryError('invalid-url', err instanceof RemoteHostError ? err.message : String(err))\n }\n}\n\n/** The URL and token an `add` (or an unsaved test) takes, checked; throws {@link HostRegistryError}. */\nexport function checkHostInput(input: { url: string, token: string }): { url: string, token: string } {\n const url = checkedUrl(input.url)\n const token = input.token.trim()\n if (!token)\n throw new HostRegistryError('invalid-token', 'A host needs a token (zidane server token create --name <device>).')\n return { url, token }\n}\n\n/** A probe's answer, refused unless a zidane-server answered. */\nfunction asServer(url: string, probed: HostProbeResult): HostProbeResult {\n if (probed.ok && probed.hostRef.kind !== 'server')\n return { ok: false, error: 'protocol', message: `${url} is a ${probed.hostRef.kind} host, not a zidane-server.` }\n return probed\n}\n\n/** The add form's \"test\": `add`'s checks and probe, saving nothing. A refused input or a failed probe comes back as an error result, not a throw. */\nexport async function probeUnsavedHost(probe: HostProbe, input: { url: string, token: string }): Promise<HostProbeResult> {\n let checked: { url: string, token: string }\n try {\n checked = checkHostInput(input)\n }\n catch (err) {\n if (err instanceof HostRegistryError)\n return { ok: false, error: err.code, message: err.message }\n throw err\n }\n return asServer(checked.url, await probe(checked))\n}\n\n/**\n * Creates a host registry backed by `hosts.json` and the credentials store in `options.dataDir`.\n *\n * @param options - Where to store hosts (`dataDir`), how to reach a host (`probe`), and an optional clock (`now`, default `Date.now`).\n * @example\n * ```ts\n * import { createHostRegistry } from 'zidane/chat'\n *\n * const hosts = createHostRegistry({\n * dataDir: '/home/me/.zidane',\n * probe: async () => ({ ok: false, error: 'unreachable', message: 'No transport wired.' }),\n * })\n * console.log(hosts.list())\n * ```\n */\nexport function createHostRegistry(options: { dataDir: string, probe: HostProbe, now?: () => number }): HostRegistry {\n const { dataDir, probe } = options\n const now = options.now ?? Date.now\n const list = (): HostEntry[] => readHosts(dataDir)\n const get = (id: string): HostEntry | undefined => list().find(h => h.id === id)\n const token = (id: string): string | undefined => {\n const cred = readCredentialEntry(dataDir, hostCredentialKey(id))\n return cred?.kind === 'apikey' && cred.value ? cred.value : undefined\n }\n const update = (id: string, patch: (entry: HostEntry) => HostEntry): HostEntry | undefined => {\n const hosts = list()\n const i = hosts.findIndex(h => h.id === id)\n if (i < 0)\n return undefined\n hosts[i] = patch(hosts[i]!)\n writeHosts(dataDir, hosts)\n return hosts[i]\n }\n\n return {\n list,\n get,\n token,\n async add(input) {\n const { url, token: secret } = checkHostInput(input)\n const name = input.name === undefined ? undefined : cleanName(input.name)\n const probed = asServer(url, await probe({ url, token: secret }))\n if (!probed.ok)\n throw new HostRegistryError(probed.error, probed.message)\n const at = now()\n const fresh: Pick<HostEntry, 'url' | 'lastConnectedAt' | 'hostKind' | 'serverName' | 'serverVersion'> = {\n url,\n lastConnectedAt: at,\n hostKind: 'server',\n serverName: probed.hostRef.name,\n ...(probed.serverVersion !== undefined ? { serverVersion: probed.serverVersion } : {}),\n }\n const id = probed.hostRef.id\n // Token first: an entry is never saved without its token.\n writeCredentialEntry(dataDir, hostCredentialKey(id), { kind: 'apikey', value: secret })\n const hosts = list()\n const existing = hosts.find(h => h.id === id)\n const entry: HostEntry = existing\n ? { ...existing, ...fresh, ...(name !== undefined ? { name } : {}) }\n : { id, name: name ?? probed.hostRef.name, addedAt: at, ...fresh }\n writeHosts(dataDir, existing ? hosts.map(h => (h.id === id ? entry : h)) : [...hosts, entry])\n return entry\n },\n remove(id) {\n const hosts = list()\n const kept = hosts.filter(h => h.id !== id)\n const had = kept.length !== hosts.length\n if (had)\n writeHosts(dataDir, kept)\n const hadToken = token(id) !== undefined\n writeCredentialEntry(dataDir, hostCredentialKey(id), undefined)\n return had || hadToken\n },\n rename(id, name) {\n const clean = cleanName(name)\n const entry = update(id, e => ({ ...e, name: clean }))\n if (!entry)\n throw new HostRegistryError('unknown-host', `No host \"${id}\".`)\n return entry\n },\n async setUrl(id, raw) {\n const url = checkedUrl(raw)\n const entry = get(id)\n const secret = entry ? token(id) : undefined\n if (!entry)\n throw new HostRegistryError('unknown-host', `No host \"${id}\".`)\n if (secret === undefined)\n throw new HostRegistryError('unknown-host', `No token saved for \"${entry.name}\": add it again.`)\n const probed = asServer(url, await probe({ url, token: secret }))\n if (!probed.ok)\n throw new HostRegistryError(probed.error, probed.message)\n if (probed.hostRef.id !== entry.id)\n throw new HostRegistryError('identity', `A different host (\"${probed.hostRef.name}\") answers at ${displayHostUrl(url)}, not \"${entry.name}\".`)\n const moved = update(id, e => ({\n ...e,\n url,\n lastConnectedAt: now(),\n serverName: probed.hostRef.name,\n ...(probed.serverVersion !== undefined ? { serverVersion: probed.serverVersion } : {}),\n }))\n if (!moved)\n throw new HostRegistryError('unknown-host', `No host \"${id}\".`)\n return moved\n },\n async test(id) {\n const entry = get(id)\n const secret = entry ? token(id) : undefined\n if (!entry || secret === undefined)\n return { ok: false, error: 'unknown-host', message: entry ? `No token saved for \"${entry.name}\": add it again.` : `No host \"${id}\".` }\n const probed = await probe({ url: entry.url, token: secret })\n if (!probed.ok)\n return probed\n if (probed.hostRef.id !== entry.id)\n return { ok: false, error: 'identity', message: `A different host (\"${probed.hostRef.name}\") answers at ${entry.url}. Remove \"${entry.name}\" and add it again if it was reinstalled.` }\n update(id, e => ({\n ...e,\n lastConnectedAt: now(),\n serverName: probed.hostRef.name,\n ...(probed.serverVersion !== undefined ? { serverVersion: probed.serverVersion } : {}),\n }))\n return probed\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"registry-CQOU5jvF.js","names":[],"sources":["../src/chat/hosts/registry.ts"],"sourcesContent":["/**\n * The client's saved hosts: `<dataDir>/hosts.json` (0600, atomic writes) for\n * the entries, the credentials store (`credentials.json`, entry\n * `hosts:<id>`) for their tokens, so tokens sit with every other secret and\n * never in the listable file.\n *\n * Every call reads the files fresh: TUI and GUI windows on one machine share\n * them. Connecting is injected (`probe`): `src/chat` cannot reach the\n * WebSocket client, `createWsHostRegistry` (`zidane/ipc`) wires it.\n */\nimport type { HostEntry, HostProbe, HostProbeResult, HostTestResult } from './types'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { OWNER_ONLY, writeJsonFileAtomic } from '../../atomic-write'\nimport { readCredentialEntry, writeCredentialEntry } from '../credentials'\nimport { HostRegistryError } from './types'\nimport { displayHostUrl, parseRemoteHostUrl, RemoteHostError, urlCarriesSecrets } from './url'\n\nconst FILE_VERSION = 1\n\ninterface HostsFile {\n version: typeof FILE_VERSION\n hosts: HostEntry[]\n}\n\n/**\n * Returns the path of the file that stores the saved hosts.\n *\n * @param dataDir - Application data directory.\n */\nexport function hostsFilePath(dataDir: string): string {\n return resolve(dataDir, 'hosts.json')\n}\n\n/**\n * Where a host's token lives in the credentials store.\n *\n * @param id - Host id.\n */\nexport function hostCredentialKey(id: string): string {\n return `hosts:${id}`\n}\n\n/** Saved remote hosts and their tokens, persisted under the data directory. */\nexport interface HostRegistry {\n /** Returns every saved host. */\n list: () => HostEntry[]\n /** Returns the saved host with this id, or `undefined`. */\n get: (id: string) => HostEntry | undefined\n /**\n * Verify the host (it must answer as a zidane-server with this token),\n * then save it and its token. Adding a host already saved (same server\n * id) updates its URL and token in place, keeping its name unless one is\n * given. Throws {@link HostRegistryError}.\n */\n add: (input: { url: string, token: string, name?: string }) => Promise<HostEntry>\n /** Forget the entry and its token; `false` when it wasn't saved. */\n remove: (id: string) => boolean\n /** Renames a saved host. Throws {@link HostRegistryError} for an empty name, or when no saved host has this id. */\n rename: (id: string, name: string) => HostEntry\n /**\n * Move a saved host to a new URL, with its saved token: saved only once\n * the same server (same id) answers there. Name and token are kept.\n * `lastConnectedAt` and the server's name and version are refreshed.\n * Throws {@link HostRegistryError} (`identity` when another server answers).\n */\n setUrl: (id: string, url: string) => Promise<HostEntry>\n /** Connect with the saved token and say who answered; refreshes `lastConnectedAt` on success. */\n test: (id: string) => Promise<HostTestResult>\n /** The saved token, for transports. Never display it. */\n token: (id: string) => string | undefined\n}\n\nfunction isEntry(value: unknown): value is HostEntry {\n const e = value as Partial<HostEntry> | null\n return !!e && typeof e === 'object' && typeof e.id === 'string' && typeof e.name === 'string' && typeof e.url === 'string' && typeof e.addedAt === 'number'\n}\n\n/** An entry saved before URLs were checked for credentials: read without them (it fails auth, the user re-adds it). */\nfunction withoutUrlSecrets(entry: HostEntry): HostEntry {\n const raw = entry.url\n return urlCarriesSecrets(raw) ? { ...entry, url: displayHostUrl(raw) } : entry\n}\n\nfunction readHosts(dataDir: string): HostEntry[] {\n const path = hostsFilePath(dataDir)\n if (!existsSync(path))\n return []\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as Partial<HostsFile>\n return Array.isArray(parsed.hosts) ? parsed.hosts.filter(isEntry).map(withoutUrlSecrets) : []\n }\n catch {\n return []\n }\n}\n\nfunction writeHosts(dataDir: string, hosts: HostEntry[]): void {\n const file: HostsFile = { version: FILE_VERSION, hosts }\n writeJsonFileAtomic(hostsFilePath(dataDir), file, OWNER_ONLY)\n}\n\nfunction cleanName(name: string): string {\n const trimmed = name.trim()\n if (!trimmed)\n throw new HostRegistryError('invalid-name', 'A host needs a name.')\n return trimmed\n}\n\nfunction checkedUrl(raw: string): string {\n try {\n return parseRemoteHostUrl(raw)\n }\n catch (err) {\n throw new HostRegistryError('invalid-url', err instanceof RemoteHostError ? err.message : String(err))\n }\n}\n\n/** The URL and token an `add` (or an unsaved test) takes, checked; throws {@link HostRegistryError}. */\nexport function checkHostInput(input: { url: string, token: string }): { url: string, token: string } {\n const url = checkedUrl(input.url)\n const token = input.token.trim()\n if (!token)\n throw new HostRegistryError('invalid-token', 'A host needs a token (zidane server token create --name <device>).')\n return { url, token }\n}\n\n/** A probe's answer, refused unless a zidane-server answered. */\nfunction asServer(url: string, probed: HostProbeResult): HostProbeResult {\n if (probed.ok && probed.hostRef.kind !== 'server')\n return { ok: false, error: 'protocol', message: `${url} is a ${probed.hostRef.kind} host, not a zidane-server.` }\n return probed\n}\n\n/** The add form's \"test\": `add`'s checks and probe, saving nothing. A refused input or a failed probe comes back as an error result, not a throw. */\nexport async function probeUnsavedHost(probe: HostProbe, input: { url: string, token: string }): Promise<HostProbeResult> {\n let checked: { url: string, token: string }\n try {\n checked = checkHostInput(input)\n }\n catch (err) {\n if (err instanceof HostRegistryError)\n return { ok: false, error: err.code, message: err.message }\n throw err\n }\n return asServer(checked.url, await probe(checked))\n}\n\n/**\n * Creates a host registry backed by `hosts.json` and the credentials store in `options.dataDir`.\n *\n * @param options - Where to store hosts (`dataDir`), how to reach a host (`probe`), and an optional clock (`now`, default `Date.now`).\n * @example\n * ```ts\n * import { createHostRegistry } from 'zidane/chat'\n *\n * const hosts = createHostRegistry({\n * dataDir: '/home/me/.zidane',\n * probe: async () => ({ ok: false, error: 'unreachable', message: 'No transport wired.' }),\n * })\n * console.log(hosts.list())\n * ```\n */\nexport function createHostRegistry(options: { dataDir: string, probe: HostProbe, now?: () => number }): HostRegistry {\n const { dataDir, probe } = options\n const now = options.now ?? Date.now\n const list = (): HostEntry[] => readHosts(dataDir)\n const get = (id: string): HostEntry | undefined => list().find(h => h.id === id)\n const token = (id: string): string | undefined => {\n const cred = readCredentialEntry(dataDir, hostCredentialKey(id))\n return cred?.kind === 'apikey' && cred.value ? cred.value : undefined\n }\n const update = (id: string, patch: (entry: HostEntry) => HostEntry): HostEntry | undefined => {\n const hosts = list()\n const i = hosts.findIndex(h => h.id === id)\n if (i < 0)\n return undefined\n hosts[i] = patch(hosts[i]!)\n writeHosts(dataDir, hosts)\n return hosts[i]\n }\n\n return {\n list,\n get,\n token,\n async add(input) {\n const { url, token: secret } = checkHostInput(input)\n const name = input.name === undefined ? undefined : cleanName(input.name)\n const probed = asServer(url, await probe({ url, token: secret }))\n if (!probed.ok)\n throw new HostRegistryError(probed.error, probed.message)\n const at = now()\n const fresh: Pick<HostEntry, 'url' | 'lastConnectedAt' | 'hostKind' | 'serverName' | 'serverVersion'> = {\n url,\n lastConnectedAt: at,\n hostKind: 'server',\n serverName: probed.hostRef.name,\n ...(probed.serverVersion !== undefined ? { serverVersion: probed.serverVersion } : {}),\n }\n const id = probed.hostRef.id\n // Token first: an entry is never saved without its token.\n writeCredentialEntry(dataDir, hostCredentialKey(id), { kind: 'apikey', value: secret })\n const hosts = list()\n const existing = hosts.find(h => h.id === id)\n const entry: HostEntry = existing\n ? { ...existing, ...fresh, ...(name !== undefined ? { name } : {}) }\n : { id, name: name ?? probed.hostRef.name, addedAt: at, ...fresh }\n writeHosts(dataDir, existing ? hosts.map(h => (h.id === id ? entry : h)) : [...hosts, entry])\n return entry\n },\n remove(id) {\n const hosts = list()\n const kept = hosts.filter(h => h.id !== id)\n const had = kept.length !== hosts.length\n if (had)\n writeHosts(dataDir, kept)\n const hadToken = token(id) !== undefined\n writeCredentialEntry(dataDir, hostCredentialKey(id), undefined)\n return had || hadToken\n },\n rename(id, name) {\n const clean = cleanName(name)\n const entry = update(id, e => ({ ...e, name: clean }))\n if (!entry)\n throw new HostRegistryError('unknown-host', `No host \"${id}\".`)\n return entry\n },\n async setUrl(id, raw) {\n const url = checkedUrl(raw)\n const entry = get(id)\n const secret = entry ? token(id) : undefined\n if (!entry)\n throw new HostRegistryError('unknown-host', `No host \"${id}\".`)\n if (secret === undefined)\n throw new HostRegistryError('unknown-host', `No token saved for \"${entry.name}\": add it again.`)\n const probed = asServer(url, await probe({ url, token: secret }))\n if (!probed.ok)\n throw new HostRegistryError(probed.error, probed.message)\n if (probed.hostRef.id !== entry.id)\n throw new HostRegistryError('identity', `A different host (\"${probed.hostRef.name}\") answers at ${displayHostUrl(url)}, not \"${entry.name}\".`)\n const moved = update(id, e => ({\n ...e,\n url,\n lastConnectedAt: now(),\n serverName: probed.hostRef.name,\n ...(probed.serverVersion !== undefined ? { serverVersion: probed.serverVersion } : {}),\n }))\n if (!moved)\n throw new HostRegistryError('unknown-host', `No host \"${id}\".`)\n return moved\n },\n async test(id) {\n const entry = get(id)\n const secret = entry ? token(id) : undefined\n if (!entry || secret === undefined)\n return { ok: false, error: 'unknown-host', message: entry ? `No token saved for \"${entry.name}\": add it again.` : `No host \"${id}\".` }\n const probed = await probe({ url: entry.url, token: secret })\n if (!probed.ok)\n return probed\n if (probed.hostRef.id !== entry.id)\n return { ok: false, error: 'identity', message: `A different host (\"${probed.hostRef.name}\") answers at ${entry.url}. Remove \"${entry.name}\" and add it again if it was reinstalled.` }\n update(id, e => ({\n ...e,\n lastConnectedAt: now(),\n serverName: probed.hostRef.name,\n ...(probed.serverVersion !== undefined ? { serverVersion: probed.serverVersion } : {}),\n }))\n return probed\n },\n }\n}\n"],"mappings":"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"}