shadow-claw 1.32.0 → 1.33.0

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 (220) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/dist/electron/main.cjs +1 -1
  4. package/dist/lib/components/index.js +26 -26
  5. package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
  6. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  7. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  8. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  9. package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
  10. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  11. package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
  12. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  13. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  14. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  15. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  16. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  17. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  18. package/dist/lib/components/shadow-claw-a2ui.js +1 -1
  19. package/dist/lib/components/shadow-claw-accounts.js +1 -1
  20. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  21. package/dist/lib/components/shadow-claw-channels.js +1 -1
  22. package/dist/lib/components/shadow-claw-chat.js +1 -1
  23. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  24. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  25. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  26. package/dist/lib/components/shadow-claw-files.js +1 -1
  27. package/dist/lib/components/shadow-claw-git.js +1 -1
  28. package/dist/lib/components/shadow-claw-integrations.js +1 -1
  29. package/dist/lib/components/shadow-claw-llm.js +1 -1
  30. package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
  31. package/dist/lib/components/shadow-claw-networking.js +1 -1
  32. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  33. package/dist/lib/components/shadow-claw-page-header.js +1 -1
  34. package/dist/lib/components/shadow-claw-pages.js +1 -1
  35. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  36. package/dist/lib/components/shadow-claw-settings.js +1 -1
  37. package/dist/lib/components/shadow-claw-storage.js +1 -1
  38. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  39. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  40. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  41. package/dist/lib/components/shadow-claw-tools.js +1 -1
  42. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  43. package/dist/lib/components/shadow-claw.js +1 -1
  44. package/dist/lib/{config-DjZ9QWc4.js → config-BcFbkaQv.js} +21 -4
  45. package/dist/lib/config-BcFbkaQv.js.map +1 -0
  46. package/dist/lib/{configurePeerJs-DD3G3Pms.js → configurePeerJs-CBMPHbwa.js} +3 -3
  47. package/dist/lib/{configurePeerJs-DD3G3Pms.js.map → configurePeerJs-CBMPHbwa.js.map} +1 -1
  48. package/dist/lib/{connections-BURqMIUv.js → connections-PZrUDf49.js} +2 -2
  49. package/dist/lib/{connections-BURqMIUv.js.map → connections-PZrUDf49.js.map} +1 -1
  50. package/dist/lib/{downloadGroupFile-B8J4up2a.js → downloadGroupFile-DSRUa9Au.js} +2 -2
  51. package/dist/lib/{downloadGroupFile-B8J4up2a.js.map → downloadGroupFile-DSRUa9Au.js.map} +1 -1
  52. package/dist/lib/{file-viewer-DoHM75Jz.js → file-viewer-11VVOpJv.js} +5 -5
  53. package/dist/lib/{file-viewer-DoHM75Jz.js.map → file-viewer-11VVOpJv.js.map} +1 -1
  54. package/dist/lib/{getGroupDir-QrN2aTo8.js → getGroupDir-BxKMYPPm.js} +3 -3
  55. package/dist/lib/{getGroupDir-QrN2aTo8.js.map → getGroupDir-BxKMYPPm.js.map} +1 -1
  56. package/dist/lib/{git-Brr7JIbs.js → git-BppibnJW.js} +3 -3
  57. package/dist/lib/{git-Brr7JIbs.js.map → git-BppibnJW.js.map} +1 -1
  58. package/dist/lib/{iframe-sanitizer-Bujgq14T.js → iframe-sanitizer-Dp8EYffI.js} +2 -2
  59. package/dist/lib/{iframe-sanitizer-Bujgq14T.js.map → iframe-sanitizer-Dp8EYffI.js.map} +1 -1
  60. package/dist/lib/index.js +27 -27
  61. package/dist/lib/{initChatSplitResize-BjiWoqC_.js → initChatSplitResize-CE_kWaLf.js} +2 -2
  62. package/dist/lib/{initChatSplitResize-BjiWoqC_.js.map → initChatSplitResize-CE_kWaLf.js.map} +1 -1
  63. package/dist/lib/{initControlPlane-3TUdhRBM.js → initControlPlane-BRIIOzoH.js} +18 -17
  64. package/dist/lib/{initControlPlane-3TUdhRBM.js.map → initControlPlane-BRIIOzoH.js.map} +1 -1
  65. package/dist/lib/{markdown-BFUTPQoO.js → markdown-Cq4sVwUT.js} +4 -4
  66. package/dist/lib/{markdown-BFUTPQoO.js.map → markdown-Cq4sVwUT.js.map} +1 -1
  67. package/dist/lib/{mcp-reconnect-BxJ77WSb.js → mcp-reconnect-CoHDMjmY.js} +2 -2
  68. package/dist/lib/{mcp-reconnect-BxJ77WSb.js.map → mcp-reconnect-CoHDMjmY.js.map} +1 -1
  69. package/dist/lib/{memoryStorage-D3FBwO9T.js → memoryStorage-DcFWrn_y.js} +2 -2
  70. package/dist/lib/{memoryStorage-D3FBwO9T.js.map → memoryStorage-DcFWrn_y.js.map} +1 -1
  71. package/dist/lib/{orchestrator-DW5l2bbW.js → orchestrator-C3TgDZvq.js} +81 -18
  72. package/dist/lib/{orchestrator-DW5l2bbW.js.map → orchestrator-C3TgDZvq.js.map} +1 -1
  73. package/dist/lib/{orchestrator-DLnCmeTZ.js → orchestrator-Ce9_lmL2.js} +12 -12
  74. package/dist/lib/{orchestrator-DLnCmeTZ.js.map → orchestrator-Ce9_lmL2.js.map} +1 -1
  75. package/dist/lib/{peerjs-BXlMuIQn.js → peerjs-PTYQ7yEG.js} +6 -6
  76. package/dist/lib/{peerjs-BXlMuIQn.js.map → peerjs-PTYQ7yEG.js.map} +1 -1
  77. package/dist/lib/{push-client-B419nn-i.js → push-client-DDV5Rcg6.js} +3 -3
  78. package/dist/lib/{push-client-B419nn-i.js.map → push-client-DDV5Rcg6.js.map} +1 -1
  79. package/dist/lib/{readGroupFileBytes-CSA7u-nJ.js → readGroupFileBytes-7C8G-yps.js} +2 -2
  80. package/dist/lib/{readGroupFileBytes-CSA7u-nJ.js.map → readGroupFileBytes-7C8G-yps.js.map} +1 -1
  81. package/dist/lib/{shadow-claw-DmMO4vVG.js → shadow-claw-WrWoPqFx.js} +10 -10
  82. package/dist/lib/{shadow-claw-DmMO4vVG.js.map → shadow-claw-WrWoPqFx.js.map} +1 -1
  83. package/dist/lib/{shadow-claw-a2ui-2I5nffIn.js → shadow-claw-a2ui-CcmJNXQP.js} +2 -2
  84. package/dist/lib/{shadow-claw-a2ui-2I5nffIn.js.map → shadow-claw-a2ui-CcmJNXQP.js.map} +1 -1
  85. package/dist/lib/{shadow-claw-accounts-DMb0y4ca.js → shadow-claw-accounts-CcB2429E.js} +3 -3
  86. package/dist/lib/{shadow-claw-accounts-DMb0y4ca.js.map → shadow-claw-accounts-CcB2429E.js.map} +1 -1
  87. package/dist/lib/{shadow-claw-channel-config-BhxHf6wZ.js → shadow-claw-channel-config-C4v0h8oa.js} +3 -3
  88. package/dist/lib/{shadow-claw-channel-config-BhxHf6wZ.js.map → shadow-claw-channel-config-C4v0h8oa.js.map} +1 -1
  89. package/dist/lib/{shadow-claw-channels-B5vzE9Z2.js → shadow-claw-channels-CyV88k9T.js} +3 -3
  90. package/dist/lib/{shadow-claw-channels-B5vzE9Z2.js.map → shadow-claw-channels-CyV88k9T.js.map} +1 -1
  91. package/dist/lib/{shadow-claw-chat-BsYnJ4-G.js → shadow-claw-chat-DSYtuZod.js} +11 -11
  92. package/dist/lib/{shadow-claw-chat-BsYnJ4-G.js.map → shadow-claw-chat-DSYtuZod.js.map} +1 -1
  93. package/dist/lib/{shadow-claw-control-plane-DqcgQsun.js → shadow-claw-control-plane-DBSGuDT1.js} +7 -7
  94. package/dist/lib/{shadow-claw-control-plane-DqcgQsun.js.map → shadow-claw-control-plane-DBSGuDT1.js.map} +1 -1
  95. package/dist/lib/{shadow-claw-conversations-B9Pn58g3.js → shadow-claw-conversations-Cp8pK4PM.js} +5 -5
  96. package/dist/lib/{shadow-claw-conversations-B9Pn58g3.js.map → shadow-claw-conversations-Cp8pK4PM.js.map} +1 -1
  97. package/dist/lib/{shadow-claw-file-viewer-DL6c4oPK.js → shadow-claw-file-viewer-B8G8yo48.js} +8 -8
  98. package/dist/lib/{shadow-claw-file-viewer-DL6c4oPK.js.map → shadow-claw-file-viewer-B8G8yo48.js.map} +1 -1
  99. package/dist/lib/{shadow-claw-files-C_ksPb8W.js → shadow-claw-files-D1COKzuC.js} +9 -9
  100. package/dist/lib/{shadow-claw-files-C_ksPb8W.js.map → shadow-claw-files-D1COKzuC.js.map} +1 -1
  101. package/dist/lib/{shadow-claw-git-jwsY7L1N.js → shadow-claw-git-60tWU4Ee.js} +6 -6
  102. package/dist/lib/{shadow-claw-git-jwsY7L1N.js.map → shadow-claw-git-60tWU4Ee.js.map} +1 -1
  103. package/dist/lib/{shadow-claw-integrations-yT7j07TM.js → shadow-claw-integrations-Bw1cG1ze.js} +4 -4
  104. package/dist/lib/{shadow-claw-integrations-yT7j07TM.js.map → shadow-claw-integrations-Bw1cG1ze.js.map} +1 -1
  105. package/dist/lib/{shadow-claw-llm-CEPJh1Jh.js → shadow-claw-llm-By5CvsFJ.js} +6 -6
  106. package/dist/lib/{shadow-claw-llm-CEPJh1Jh.js.map → shadow-claw-llm-By5CvsFJ.js.map} +1 -1
  107. package/dist/lib/{shadow-claw-mcp-remote-DGDWjByQ.js → shadow-claw-mcp-remote-BaNODd1v.js} +4 -4
  108. package/dist/lib/{shadow-claw-mcp-remote-DGDWjByQ.js.map → shadow-claw-mcp-remote-BaNODd1v.js.map} +1 -1
  109. package/dist/lib/{shadow-claw-networking-Bi4yvc73.js → shadow-claw-networking-DtZ5bsTz.js} +2 -2
  110. package/dist/lib/{shadow-claw-networking-Bi4yvc73.js.map → shadow-claw-networking-DtZ5bsTz.js.map} +1 -1
  111. package/dist/lib/{shadow-claw-notifications-C-JgGqQx.js → shadow-claw-notifications-C3VEba_A.js} +3 -3
  112. package/dist/lib/{shadow-claw-notifications-C-JgGqQx.js.map → shadow-claw-notifications-C3VEba_A.js.map} +1 -1
  113. package/dist/lib/{shadow-claw-page-header-DrAcWCZ9.js → shadow-claw-page-header-BLowJopA.js} +2 -2
  114. package/dist/lib/{shadow-claw-page-header-DrAcWCZ9.js.map → shadow-claw-page-header-BLowJopA.js.map} +1 -1
  115. package/dist/lib/{shadow-claw-pages-OEOClgA8.js → shadow-claw-pages-CpeUV-hw.js} +9 -9
  116. package/dist/lib/{shadow-claw-pages-OEOClgA8.js.map → shadow-claw-pages-CpeUV-hw.js.map} +1 -1
  117. package/dist/lib/{shadow-claw-peerjs-DmC2CV-9.js → shadow-claw-peerjs-DYAzJbTy.js} +3 -3
  118. package/dist/lib/{shadow-claw-peerjs-DmC2CV-9.js.map → shadow-claw-peerjs-DYAzJbTy.js.map} +1 -1
  119. package/dist/lib/{shadow-claw-settings-xAtFog0T.js → shadow-claw-settings-ByxwW3WK.js} +9 -9
  120. package/dist/lib/{shadow-claw-settings-xAtFog0T.js.map → shadow-claw-settings-ByxwW3WK.js.map} +1 -1
  121. package/dist/lib/{shadow-claw-storage-B4UHl-po.js → shadow-claw-storage-CsSPYVDJ.js} +5 -5
  122. package/dist/lib/{shadow-claw-storage-B4UHl-po.js.map → shadow-claw-storage-CsSPYVDJ.js.map} +1 -1
  123. package/dist/lib/{shadow-claw-task-server-B0eWzg57.js → shadow-claw-task-server-De2WCEw4.js} +2 -2
  124. package/dist/lib/{shadow-claw-task-server-B0eWzg57.js.map → shadow-claw-task-server-De2WCEw4.js.map} +1 -1
  125. package/dist/lib/{shadow-claw-tasks-HHU_8Gyg.js → shadow-claw-tasks-zHWT4GaE.js} +7 -7
  126. package/dist/lib/{shadow-claw-tasks-HHU_8Gyg.js.map → shadow-claw-tasks-zHWT4GaE.js.map} +1 -1
  127. package/dist/lib/{shadow-claw-terminal-CdC2G6Zz.js → shadow-claw-terminal-Bpawfv4N.js} +2 -2
  128. package/dist/lib/{shadow-claw-terminal-CdC2G6Zz.js.map → shadow-claw-terminal-Bpawfv4N.js.map} +1 -1
  129. package/dist/lib/{shadow-claw-tools-2AICxVXA.js → shadow-claw-tools-BbKdWcps.js} +7 -7
  130. package/dist/lib/{shadow-claw-tools-2AICxVXA.js.map → shadow-claw-tools-BbKdWcps.js.map} +1 -1
  131. package/dist/lib/{shadow-claw-webvm-Cy-Ia2vs.js → shadow-claw-webvm-DLqK0AxW.js} +3 -3
  132. package/dist/lib/{shadow-claw-webvm-Cy-Ia2vs.js.map → shadow-claw-webvm-DLqK0AxW.js.map} +1 -1
  133. package/dist/lib/subsystems/mcp/webmcp.d.ts +37 -0
  134. package/dist/lib/subsystems/providers/model-registry.d.ts +4 -1
  135. package/dist/lib/subsystems/providers/providers.d.ts +10 -0
  136. package/dist/lib/{syncWebMcpRegistration-CaAPPg2V.js → syncWebMcpRegistration-mlbbvcdm.js} +5 -5
  137. package/dist/lib/{syncWebMcpRegistration-CaAPPg2V.js.map → syncWebMcpRegistration-mlbbvcdm.js.map} +1 -1
  138. package/dist/lib/{tools-f1WPNDSS.js → tools-Bb2ujM2E.js} +4 -4
  139. package/dist/lib/{tools-f1WPNDSS.js.map → tools-Bb2ujM2E.js.map} +1 -1
  140. package/dist/lib/utils/index.js +1 -1
  141. package/dist/lib/utils/utils.js +1 -1
  142. package/dist/lib/{utils-B99XP70b.js → utils-mpsYPfS7.js} +3 -3
  143. package/dist/lib/{utils-B99XP70b.js.map → utils-mpsYPfS7.js.map} +1 -1
  144. package/dist/lib/webmcp-BVR2lBVo.js +1544 -0
  145. package/dist/lib/webmcp-BVR2lBVo.js.map +1 -0
  146. package/dist/public/.well-known/mcp/server-card.json +1 -1
  147. package/dist/public/.well-known/mcp.json +1 -1
  148. package/dist/public/agent.worker.js +44 -44
  149. package/dist/public/config-BqecTktB.js +1 -0
  150. package/dist/public/{configurePeerJs--JGpJVti.js → configurePeerJs-BPKfWuEt.js} +1 -1
  151. package/dist/public/{connections-6575KDGE.js → connections-C_3tYsmX.js} +1 -1
  152. package/dist/public/{custom-element-security-Da_6HQ6X.js → custom-element-security-DXwU3Lae.js} +1 -1
  153. package/dist/public/docs/example/article/index.html +2 -2
  154. package/dist/public/docs/publishing/index.html +2 -2
  155. package/dist/public/docs/skill-creator/index.html +2 -2
  156. package/dist/public/docs/subsystems/tools.md +4 -3
  157. package/dist/public/docs/subsystems/webmcp.md +58 -13
  158. package/dist/public/{downloadGroupFile-Dj-AMNsd.js → downloadGroupFile-DTyf5DIC.js} +1 -1
  159. package/dist/public/{e2e-bridge-CbYqdMp-.js → e2e-bridge-DXwWCQKw.js} +1 -1
  160. package/dist/public/{file-viewer-vHJ5Zsgk.js → file-viewer-ConEyDDG.js} +1 -1
  161. package/dist/public/{getGroupDir-BwprrdrQ.js → getGroupDir-ZfZsiq6d.js} +1 -1
  162. package/dist/public/{git-8F7wvoUB.js → git-BtBaLC1-.js} +2 -2
  163. package/dist/public/{iframe-sanitizer-viROQkRn.js → iframe-sanitizer-Corkvo_d.js} +1 -1
  164. package/dist/public/index.html +2 -2
  165. package/dist/public/index.js +1 -1
  166. package/dist/public/{initChatSplitResize-BA18wLQQ.js → initChatSplitResize-CUc-TEmT.js} +1 -1
  167. package/dist/public/{initControlPlane-C5v0FtJE.js → initControlPlane-B_uYS8a4.js} +1 -1
  168. package/dist/public/main/index.html +2 -2
  169. package/dist/public/main/memory/index.html +2 -2
  170. package/dist/public/{markdown-eFKxxpVP.js → markdown-Cfa6GuJs.js} +1 -1
  171. package/dist/public/{mcp-reconnect-CBHuaYdF.js → mcp-reconnect-CUOkdPEz.js} +1 -1
  172. package/dist/public/{memoryStorage-C1llaYus.js → memoryStorage-Bh8fjmip.js} +1 -1
  173. package/dist/public/{orchestrator-DNr2HDI2.js → orchestrator-BcSzrg6E.js} +2 -2
  174. package/dist/public/{orchestrator-BeJfwtvz.js → orchestrator-Bi5OdVZD.js} +11 -11
  175. package/dist/public/{peerjs-mG30TAJ_.js → peerjs-CzCXG-NW.js} +1 -1
  176. package/dist/public/{push-client-C2zMT3Sf.js → push-client-Cr3Ruahp.js} +1 -1
  177. package/dist/public/service-worker.js +1 -1
  178. package/dist/public/{shadow-claw-Ble6l0Iw.js → shadow-claw-CzUYuuOa.js} +2 -2
  179. package/dist/public/{shadow-claw-accounts-D1y-zbMs.js → shadow-claw-accounts-BgIHB_aP.js} +1 -1
  180. package/dist/public/{shadow-claw-channels-D_xfBoWp.js → shadow-claw-channels-Ce_trJ5D.js} +1 -1
  181. package/dist/public/{shadow-claw-chat-D_Lbi5oH.js → shadow-claw-chat-CdECLLwL.js} +1 -1
  182. package/dist/public/{shadow-claw-control-plane-CrnA5lM0.js → shadow-claw-control-plane-DM62UmOz.js} +2 -2
  183. package/dist/public/{shadow-claw-conversations-BN-hFmN1.js → shadow-claw-conversations-AM5g3C00.js} +1 -1
  184. package/dist/public/{shadow-claw-file-viewer-BKEpzpbx.js → shadow-claw-file-viewer-BP7gWS5g.js} +1 -1
  185. package/dist/public/{shadow-claw-files-DDZMk5AA.js → shadow-claw-files-RPPGszcK.js} +1 -1
  186. package/dist/public/{shadow-claw-git-CqOO97Re.js → shadow-claw-git-BsHEvLrb.js} +2 -2
  187. package/dist/public/{shadow-claw-integrations-CRGL78Fy.js → shadow-claw-integrations-B0vbcOqg.js} +1 -1
  188. package/dist/public/{shadow-claw-llm-DoGxEHQL.js → shadow-claw-llm-FNNamrc_.js} +2 -2
  189. package/dist/public/{shadow-claw-mcp-remote-DyGnK4S_.js → shadow-claw-mcp-remote-7mDgniQu.js} +1 -1
  190. package/dist/public/{shadow-claw-networking-DJeCdb7e.js → shadow-claw-networking-CGsetaLp.js} +1 -1
  191. package/dist/public/{shadow-claw-notifications-hSKJeMXb.js → shadow-claw-notifications-pnwRcR-4.js} +1 -1
  192. package/dist/public/{shadow-claw-page-header-DA-FG08G.js → shadow-claw-page-header-BUy15Poz.js} +1 -1
  193. package/dist/public/{shadow-claw-pages-DTS98Zbi.js → shadow-claw-pages-ILAjGWNk.js} +1 -1
  194. package/dist/public/{shadow-claw-settings-5w0ce8SP.js → shadow-claw-settings-Bx5IgVqN.js} +3 -3
  195. package/dist/public/{shadow-claw-storage-DqlVt27X.js → shadow-claw-storage-PAtNykaF.js} +1 -1
  196. package/dist/public/{shadow-claw-task-server-ByZwKI6S.js → shadow-claw-task-server-Cbbi_2OO.js} +1 -1
  197. package/dist/public/{shadow-claw-tasks-Dc4G4zBu.js → shadow-claw-tasks-B-jKYWZJ.js} +1 -1
  198. package/dist/public/{shadow-claw-terminal-DvusP7wg.js → shadow-claw-terminal-BRejND-Z.js} +1 -1
  199. package/dist/public/{shadow-claw-tools-BEsid8q-.js → shadow-claw-tools-DNQe3oa-.js} +1 -1
  200. package/dist/public/{shadow-claw-webvm-BWlIeK78.js → shadow-claw-webvm-BFuwRifU.js} +1 -1
  201. package/dist/public/{syncWebMcpRegistration-CqVCB6m8.js → syncWebMcpRegistration-FxKAPwaz.js} +1 -1
  202. package/dist/public/theme-init.js +1 -1
  203. package/dist/public/{tools-Cp1gequ8.js → tools-BwQKGsro.js} +1 -1
  204. package/dist/public/transformers-js.worker.js +1 -1
  205. package/dist/public/webmcp-C28PAlfy.js +1 -0
  206. package/dist/server.js +1 -1
  207. package/package.json +3 -3
  208. package/src/core/utils/initControlPlane.test.ts +122 -1
  209. package/src/core/utils/initControlPlane.ts +12 -8
  210. package/src/subsystems/mcp/webmcp.test.ts +253 -1
  211. package/src/subsystems/mcp/webmcp.ts +147 -4
  212. package/src/subsystems/providers/model-registry.test.ts +139 -0
  213. package/src/subsystems/providers/model-registry.ts +54 -2
  214. package/src/subsystems/providers/providers.test.ts +329 -0
  215. package/src/subsystems/providers/providers.ts +135 -11
  216. package/dist/lib/config-DjZ9QWc4.js.map +0 -1
  217. package/dist/lib/webmcp-BC8_WafM.js +0 -1710
  218. package/dist/lib/webmcp-BC8_WafM.js.map +0 -1
  219. package/dist/public/config-Bnwx4EfS.js +0 -1
  220. package/dist/public/webmcp-DBONjQWd.js +0 -2
@@ -1,4 +1,4 @@
1
- import { L as getProviderTokenAuthScheme, N as getGeneralAccountProviderCapabilities, b as OAUTH_PROVIDER_DEFINITIONS, h as GENERAL_ACCOUNT_PROVIDER_CAPABILITIES, r as CONFIG_KEYS } from "./config-DjZ9QWc4.js";
1
+ import { L as getProviderTokenAuthScheme, N as getGeneralAccountProviderCapabilities, b as OAUTH_PROVIDER_DEFINITIONS, h as GENERAL_ACCOUNT_PROVIDER_CAPABILITIES, r as CONFIG_KEYS } from "./config-BcFbkaQv.js";
2
2
  import { t as getConfig } from "./getConfig-BxxXMyuM.js";
3
3
  import { t as setConfig } from "./setConfig-BFED5CXn.js";
4
4
  import { ulid } from "./utils/ulid.js";
@@ -1298,4 +1298,4 @@ async function reconnectMcpOAuth(db, connectionId, options = {}) {
1298
1298
 
1299
1299
  //#endregion
1300
1300
  export { testRemoteMcpConnection as a, getRemoteMcpConnection as c, buildAuthHeaders as d, detectProvider as f, resolveStoredCredentialAuthMode as h, listRemoteMcpTools as i, listRemoteMcpConnections as l, resolveServiceCredentials as m, McpReauthRequiredError as n, bindRemoteMcpCredentialRef as o, resolveGitCredentials as p, callRemoteMcpTool as r, deleteRemoteMcpConnection as s, reconnectMcpOAuth as t, upsertRemoteMcpConnection as u };
1301
- //# sourceMappingURL=mcp-reconnect-BxJ77WSb.js.map
1301
+ //# sourceMappingURL=mcp-reconnect-CoHDMjmY.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-reconnect-BxJ77WSb.js","names":["OAUTH_REFRESH_WINDOW_MS","OAUTH_REFRESH_FAILURE_THRESHOLD","shouldRefreshOAuthToken","tryRefreshOAuthAccount","matchAccountsByHost","pickAccountByAuthMode"],"sources":["../../src/subsystems/accounts/stored-credentials.ts","../../src/subsystems/accounts/service-accounts.ts","../../src/subsystems/git/credentials.ts","../../src/subsystems/mcp/mcp-connections.ts","../../src/subsystems/mcp/mcp-connection-auth.ts","../../src/subsystems/mcp/remote-mcp-client.ts","../../src/subsystems/mcp/mcp-reconnect.ts"],"sourcesContent":["export type StoredCredentialAuthMode = \"token\" | \"basic\" | \"oauth\";\n\nexport interface StoredCredentialBase {\n id: string;\n label: string;\n hostPattern: string;\n token: string;\n basicUsername?: string;\n authMode?: StoredCredentialAuthMode;\n}\n\nexport interface StoredCredentialOAuthFields {\n oauthProviderId?: string;\n oauthClientId?: string;\n oauthClientSecret?: string;\n accessTokenExpiresAt?: number;\n refreshToken?: string;\n scopes?: string[];\n tokenType?: string;\n oauthRefreshFailureCount?: number;\n oauthReauthRequired?: boolean;\n oauthReauthRequiredAt?: number;\n /** Custom authorize URL (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomAuthorizeUrl?: string;\n /** Custom token URL (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomTokenUrl?: string;\n /** Custom PKCE preference (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomUsePkce?: boolean;\n /** Custom redirect URI (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomRedirectUri?: string;\n}\n\nexport interface PendingOAuthResult {\n providerId: string;\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n scope?: string;\n tokenType?: string;\n}\n\ntype OAuthCapableStoredCredential = {\n authMode?: StoredCredentialAuthMode;\n} & StoredCredentialOAuthFields;\n\nexport function resolveStoredCredentialAuthMode(\n credential: OAuthCapableStoredCredential | null | undefined,\n): StoredCredentialAuthMode {\n if (!credential) {\n return \"token\";\n }\n\n // Treat legacy \"pat\" string identically to \"token\" mode\n if (\n credential.authMode === \"token\" ||\n (credential.authMode as string) === \"pat\"\n ) {\n return \"token\";\n }\n\n if (credential.authMode === \"basic\") {\n return \"basic\";\n }\n\n if (credential.authMode === \"oauth\") {\n return \"oauth\";\n }\n\n if (\n credential.oauthProviderId ||\n credential.oauthClientId ||\n credential.oauthClientSecret ||\n credential.refreshToken ||\n credential.accessTokenExpiresAt ||\n credential.tokenType ||\n credential.oauthReauthRequired\n ) {\n return \"oauth\";\n }\n\n return \"token\";\n}\n","import {\n CONFIG_KEYS,\n GENERAL_ACCOUNT_PROVIDER_CAPABILITIES,\n getGeneralAccountProviderCapabilities,\n} from \"../../config/config.js\";\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\nimport { decryptValue, encryptValue } from \"../../security/crypto.js\";\nimport {\n resolveStoredCredentialAuthMode,\n type StoredCredentialAuthMode,\n type StoredCredentialBase,\n type StoredCredentialOAuthFields,\n} from \"./stored-credentials.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\nimport type { GeneralAccountProviderCapabilities } from \"../../config/config.js\";\n\nexport type AccountAuthMode = StoredCredentialAuthMode;\n\nexport interface ServiceAccount\n extends StoredCredentialBase, StoredCredentialOAuthFields {\n service: string;\n}\n\nexport interface ResolvedServiceCredentials {\n token: string;\n service: string;\n hostPattern: string;\n headerName: string; // e.g. \"Authorization\", \"X-Figma-Token\"\n headerValue: string; // e.g. \"Bearer TOKEN\", \"TOKEN\" (no prefix)\n accountId: string;\n authMode: AccountAuthMode;\n reauthRequired?: boolean;\n}\n\nexport interface ResolveServiceCredentialsOptions {\n accountId?: string;\n authMode?: AccountAuthMode;\n forceRefresh?: boolean;\n}\n\nconst OAUTH_REFRESH_WINDOW_MS = 60_000;\nconst OAUTH_REFRESH_FAILURE_THRESHOLD = 3;\n\nfunction resolveAccountAuthMode(account: ServiceAccount): AccountAuthMode {\n return resolveStoredCredentialAuthMode(account);\n}\n\nfunction shouldRefreshOAuthToken(\n account: ServiceAccount,\n options: ResolveServiceCredentialsOptions,\n): boolean {\n if (resolveAccountAuthMode(account) !== \"oauth\") {\n return false;\n }\n\n if (account.oauthReauthRequired) {\n return false;\n }\n\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return false;\n }\n\n if (options.forceRefresh) {\n return true;\n }\n\n if (!account.accessTokenExpiresAt) {\n return false;\n }\n\n return account.accessTokenExpiresAt <= Date.now() + OAUTH_REFRESH_WINDOW_MS;\n}\n\nasync function tryRefreshOAuthAccount(\n db: ShadowClawDatabase,\n accounts: ServiceAccount[],\n account: ServiceAccount,\n): Promise<ServiceAccount> {\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return account;\n }\n\n const decryptedRefreshToken = await decryptValue(account.refreshToken);\n if (!decryptedRefreshToken) {\n return account;\n }\n\n const oauthClientSecret = account.oauthClientSecret\n ? await decryptValue(account.oauthClientSecret)\n : undefined;\n\n const persistAccount = async (updatedAccount: ServiceAccount) => {\n const idx = accounts.findIndex((candidate) => candidate.id === account.id);\n if (idx !== -1) {\n accounts[idx] = updatedAccount;\n await setConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS, accounts);\n }\n };\n\n const markRefreshFailure = async (): Promise<ServiceAccount> => {\n const failureCount = (account.oauthRefreshFailureCount || 0) + 1;\n const reauthRequired = failureCount >= OAUTH_REFRESH_FAILURE_THRESHOLD;\n const updatedAccount: ServiceAccount = {\n ...account,\n oauthRefreshFailureCount: failureCount,\n oauthReauthRequired: reauthRequired,\n oauthReauthRequiredAt: reauthRequired\n ? account.oauthReauthRequiredAt || Date.now()\n : undefined,\n };\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n };\n\n try {\n const response = await fetch(\"/oauth/refresh\", {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n providerId: account.oauthProviderId,\n clientId: account.oauthClientId,\n clientSecret: oauthClientSecret,\n refreshToken: decryptedRefreshToken,\n scope: account.scopes,\n tokenUrl: account.oauthCustomTokenUrl,\n }),\n });\n\n if (!response.ok) {\n return markRefreshFailure();\n }\n\n const payload = (await response.json()) as {\n accessToken?: string;\n refreshToken?: string;\n expiresIn?: number;\n tokenType?: string;\n scope?: string;\n };\n\n if (!payload.accessToken) {\n return markRefreshFailure();\n }\n\n const encryptedAccessToken = await encryptValue(payload.accessToken);\n if (!encryptedAccessToken) {\n return markRefreshFailure();\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (payload.refreshToken) {\n const maybeEncryptedRefreshToken = await encryptValue(\n payload.refreshToken,\n );\n if (maybeEncryptedRefreshToken) {\n encryptedRefreshToken = maybeEncryptedRefreshToken;\n }\n }\n\n const updatedAccount: ServiceAccount = {\n ...account,\n token: encryptedAccessToken,\n refreshToken: encryptedRefreshToken,\n accessTokenExpiresAt: payload.expiresIn\n ? Date.now() + payload.expiresIn * 1000\n : account.accessTokenExpiresAt,\n tokenType: payload.tokenType || account.tokenType,\n scopes: payload.scope\n ? payload.scope\n .split(/[\\s,]+/)\n .map((scope) => scope.trim())\n .filter(Boolean)\n : account.scopes,\n oauthRefreshFailureCount: 0,\n oauthReauthRequired: false,\n oauthReauthRequiredAt: undefined,\n };\n\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n } catch {\n return markRefreshFailure();\n }\n}\n\nfunction resolveProviderCapabilities(\n account: ServiceAccount,\n url?: string,\n): GeneralAccountProviderCapabilities | null {\n if (account.oauthProviderId) {\n const direct = getGeneralAccountProviderCapabilities(\n account.oauthProviderId,\n );\n if (direct) {\n return direct;\n }\n }\n\n const hints = [account.service, account.hostPattern, url]\n .filter((value): value is string => !!value)\n .map((value) => value.toLowerCase());\n\n for (const provider of Object.values(GENERAL_ACCOUNT_PROVIDER_CAPABILITIES)) {\n const aliases = [provider.providerId, ...(provider.aliases || [])].map(\n (alias) => alias.toLowerCase(),\n );\n\n if (aliases.some((alias) => hints.some((hint) => hint.includes(alias)))) {\n return provider;\n }\n }\n\n return null;\n}\n\n/**\n * Resolve auth header format from provider capabilities. Falls back to Bearer.\n */\nfunction getServiceAuthFormat(\n account: ServiceAccount,\n url?: string,\n): {\n headerName: string;\n headerPrefix: string;\n} {\n const authMode = resolveAccountAuthMode(account);\n const provider = resolveProviderCapabilities(account, url);\n if (provider) {\n let scheme;\n if (authMode === \"oauth\") {\n scheme = provider.tokenAuth.oauth;\n } else if (authMode === \"basic\" && provider.tokenAuth.basic) {\n scheme = provider.tokenAuth.basic;\n } else {\n scheme = provider.tokenAuth.token;\n }\n\n return {\n headerName: scheme.headerName,\n headerPrefix: scheme.headerPrefix || \"\",\n };\n }\n\n return {\n headerName: \"Authorization\",\n headerPrefix: authMode === \"basic\" ? \"Basic \" : \"Bearer \",\n };\n}\n\n/**\n * Find the most-specific (longest hostPattern) account that matches a URL.\n */\nfunction matchAccountsByHost(\n accounts: ServiceAccount[],\n url: string,\n): ServiceAccount[] {\n const matches: ServiceAccount[] = [];\n let bestLen = -1;\n const lower = url.toLowerCase();\n\n for (const acct of accounts) {\n if (!acct.hostPattern) {\n continue;\n }\n\n const pattern = acct.hostPattern.toLowerCase();\n if (lower.includes(pattern)) {\n if (pattern.length > bestLen) {\n bestLen = pattern.length;\n matches.length = 0;\n matches.push(acct);\n } else if (pattern.length === bestLen) {\n matches.push(acct);\n }\n }\n }\n\n return matches;\n}\n\nfunction pickAccountByAuthMode(\n accounts: ServiceAccount[],\n authMode?: AccountAuthMode,\n): ServiceAccount | undefined {\n if (!accounts.length) {\n return undefined;\n }\n\n if (!authMode) {\n return accounts[0];\n }\n\n const modeMatch = accounts.find(\n (acct) => resolveAccountAuthMode(acct) === authMode,\n );\n if (modeMatch) {\n return modeMatch;\n }\n\n return accounts[0];\n}\n\n/**\n * Resolve a decrypted service account PAT for the given URL.\n *\n * Resolution order:\n * 1. Longest hostPattern match against the URL\n * 2. SERVICE_DEFAULT_ACCOUNT\n * 3. First account in the list\n *\n * Returns undefined when no accounts are configured.\n */\nexport async function resolveServiceCredentials(\n db: ShadowClawDatabase,\n url?: string,\n options: ResolveServiceCredentialsOptions = {},\n): Promise<ResolvedServiceCredentials | undefined> {\n const raw = await getConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS);\n const accounts: ServiceAccount[] = Array.isArray(raw) ? raw : [];\n\n if (accounts.length === 0) {\n return undefined;\n }\n\n let account: ServiceAccount | undefined;\n\n if (options.accountId) {\n account = accounts.find((a) => a.id === options.accountId);\n }\n\n // 1. Match by URL host pattern\n if (!account && url) {\n const hostMatches = matchAccountsByHost(accounts, url);\n if (options.authMode) {\n account = pickAccountByAuthMode(hostMatches, options.authMode);\n } else {\n const preferredMode = hostMatches[0]\n ? resolveProviderCapabilities(hostMatches[0], url)?.defaultMode\n : undefined;\n account = pickAccountByAuthMode(hostMatches, preferredMode);\n }\n }\n\n // 2. Fall back to stored default\n if (!account) {\n const defaultId = await getConfig(db, CONFIG_KEYS.SERVICE_DEFAULT_ACCOUNT);\n if (defaultId) {\n account = accounts.find((a) => a.id === defaultId);\n }\n }\n\n // 3. Fall back to first account matching requested mode\n if (!account && options.authMode) {\n account = pickAccountByAuthMode(accounts, options.authMode);\n }\n\n // 4. Fall back to first\n if (!account) {\n account = accounts[0];\n }\n\n if (!account) {\n return undefined;\n }\n\n if (shouldRefreshOAuthToken(account, options)) {\n account = await tryRefreshOAuthAccount(db, accounts, account);\n }\n\n if (\n resolveAccountAuthMode(account) === \"oauth\" &&\n account.oauthReauthRequired\n ) {\n return {\n token: \"\",\n service: account.service,\n hostPattern: account.hostPattern,\n headerName: \"Authorization\",\n headerValue: \"\",\n accountId: account.id,\n authMode: \"oauth\",\n reauthRequired: true,\n };\n }\n\n let token = \"\";\n if (account.token) {\n token = (await decryptValue(account.token)) ?? \"\";\n }\n\n const { headerName, headerPrefix } = getServiceAuthFormat(account, url);\n let finalToken = token;\n\n if (resolveAccountAuthMode(account) === \"basic\" && account.basicUsername) {\n finalToken = btoa(`${account.basicUsername}:${token}`);\n }\n\n const headerValue = headerPrefix\n ? `${headerPrefix}${finalToken}`\n : finalToken;\n\n return {\n token,\n service: account.service,\n hostPattern: account.hostPattern,\n headerName,\n headerValue,\n accountId: account.id,\n authMode: resolveAccountAuthMode(account),\n };\n}\n","import {\n CONFIG_KEYS,\n getProviderTokenAuthScheme,\n} from \"../../config/config.js\";\n\nimport { resolveStoredCredentialAuthMode } from \"../accounts/stored-credentials.js\";\nimport { decryptValue, encryptValue } from \"../../security/crypto.js\";\n\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\n\nimport type {\n GitAccount,\n GitAuthMode,\n GitProvider,\n ResolveGitCredentialsOptions,\n ResolvedGitCredentials,\n} from \"./types.js\";\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\n\nconst OAUTH_REFRESH_WINDOW_MS = 60_000;\nconst OAUTH_REFRESH_FAILURE_THRESHOLD = 3;\n\nfunction resolveGitAuthMode(account: GitAccount): GitAuthMode {\n return resolveStoredCredentialAuthMode(account);\n}\n\nfunction mapGitProviderToGeneralProviderId(provider: GitProvider): string {\n switch (provider) {\n case \"azure-devops\":\n return \"azure_devops\";\n default:\n return provider;\n }\n}\n\n/**\n * Detect the git provider type from a hostname or host pattern.\n */\nexport function detectProvider(hostPattern: string): GitProvider {\n if (!hostPattern) {\n return \"generic\";\n }\n\n const lower = hostPattern.toLowerCase();\n if (lower.includes(\"github\")) {\n return \"github\";\n }\n\n if (lower.includes(\"dev.azure.com\") || lower.includes(\"visualstudio.com\")) {\n return \"azure-devops\";\n }\n\n if (lower.includes(\"gitlab\")) {\n return \"gitlab\";\n }\n\n return \"generic\";\n}\n\n/**\n * Build HTTP Authorization headers for a resolved credential set.\n */\nexport function buildAuthHeaders(\n creds: Partial<ResolvedGitCredentials> & {\n hostPattern?: string;\n provider?: GitProvider;\n },\n): Record<string, string> {\n if (creds.reauthRequired) {\n return {};\n }\n\n const provider = creds.provider || detectProvider(creds.hostPattern || \"\");\n const mappedProviderId = mapGitProviderToGeneralProviderId(provider);\n const authMode = creds.authMode || \"token\";\n\n // Some git providers (Azure DevOps) need Basic auth for PAT/OAuth over git HTTP.\n if (\n creds.token &&\n mappedProviderId === \"azure_devops\" &&\n getProviderTokenAuthScheme(mappedProviderId, authMode, \"git_remote\")\n ?.headerPrefix === \"Basic \"\n ) {\n return { Authorization: `Basic ${btoa(\":\" + creds.token)}` };\n }\n\n switch (provider) {\n case \"azure-devops\": {\n if (creds.username && creds.password) {\n return {\n Authorization: `Basic ${btoa(creds.username + \":\" + creds.password)}`,\n };\n }\n\n break;\n }\n\n default:\n break;\n }\n\n if (creds.token) {\n const scheme = getProviderTokenAuthScheme(\n mappedProviderId,\n authMode,\n \"git_remote\",\n );\n\n if (scheme?.headerName) {\n return {\n [scheme.headerName]: `${scheme.headerPrefix || \"\"}${creds.token}`,\n };\n }\n\n return { Authorization: `Bearer ${creds.token}` };\n }\n\n return {};\n}\n\n/**\n * Extract hostname + pathname from a git remote URL.\n * Returns e.g. \"dev.azure.com/exampleOrg2/project/_git/repo\" so that\n * path-based host patterns like \"dev.azure.com/exampleOrg2\" can match.\n */\nfunction extractHostAndPath(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n const path = parsed.pathname.replace(/\\/+$/, \"\");\n\n return path ? parsed.hostname + path : parsed.hostname;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Find the best matching account for a URL by host (and optional path prefix).\n * When multiple accounts match, the longest (most specific) pattern wins.\n */\nfunction matchAccountsByHost(\n accounts: GitAccount[],\n hostAndPath: string,\n): GitAccount[] {\n const lower = hostAndPath.toLowerCase();\n const matches: GitAccount[] = [];\n\n let bestLen = -1;\n for (const a of accounts) {\n if (!a.hostPattern) {\n continue;\n }\n\n const pattern = a.hostPattern.toLowerCase();\n if (lower.includes(pattern)) {\n if (pattern.length > bestLen) {\n bestLen = pattern.length;\n\n matches.length = 0;\n matches.push(a);\n } else if (pattern.length === bestLen) {\n matches.push(a);\n }\n }\n }\n\n return matches;\n}\n\nfunction pickAccountByAuthMode(\n accounts: GitAccount[],\n authMode?: GitAuthMode,\n): GitAccount | undefined {\n if (!accounts.length) {\n return undefined;\n }\n\n if (!authMode) {\n return accounts[0];\n }\n\n const modeMatch = accounts.find(\n (acct) => resolveGitAuthMode(acct) === authMode,\n );\n if (modeMatch) {\n return modeMatch;\n }\n\n // Legacy accounts without explicit mode should behave as PAT accounts.\n if (authMode === \"token\") {\n const legacyPat = accounts.find(\n (acct) => resolveGitAuthMode(acct) === \"token\" && !acct.authMode,\n );\n\n if (legacyPat) {\n return legacyPat;\n }\n }\n\n return accounts[0];\n}\n\n/**\n * Decrypt a stored encrypted value, returning undefined for empty/missing.\n */\nasync function decryptIfPresent(\n encrypted: string | null | undefined,\n): Promise<string | undefined> {\n if (!encrypted) {\n return undefined;\n }\n\n const result = await decryptValue(encrypted);\n\n return result ?? undefined;\n}\n\nfunction shouldRefreshOAuthToken(\n account: GitAccount,\n options: ResolveGitCredentialsOptions,\n): boolean {\n if (resolveGitAuthMode(account) !== \"oauth\") {\n return false;\n }\n\n if (account.oauthReauthRequired) {\n return false;\n }\n\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return false;\n }\n\n if (options.forceRefresh) {\n return true;\n }\n\n if (!account.accessTokenExpiresAt) {\n return false;\n }\n\n return account.accessTokenExpiresAt <= Date.now() + OAUTH_REFRESH_WINDOW_MS;\n}\n\nasync function tryRefreshOAuthAccount(\n db: ShadowClawDatabase,\n accounts: GitAccount[],\n account: GitAccount,\n): Promise<GitAccount> {\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return account;\n }\n\n const decryptedRefreshToken = await decryptIfPresent(account.refreshToken);\n if (!decryptedRefreshToken) {\n return account;\n }\n\n const oauthClientSecret = account.oauthClientSecret\n ? await decryptIfPresent(account.oauthClientSecret)\n : undefined;\n\n const persistAccount = async (updatedAccount: GitAccount) => {\n const idx = accounts.findIndex((candidate) => candidate.id === account.id);\n if (idx !== -1) {\n accounts[idx] = updatedAccount;\n await setConfig(db, CONFIG_KEYS.GIT_ACCOUNTS, accounts);\n }\n };\n\n const markRefreshFailure = async (): Promise<GitAccount> => {\n const failureCount = (account.oauthRefreshFailureCount || 0) + 1;\n const reauthRequired = failureCount >= OAUTH_REFRESH_FAILURE_THRESHOLD;\n const updatedAccount: GitAccount = {\n ...account,\n oauthRefreshFailureCount: failureCount,\n oauthReauthRequired: reauthRequired,\n oauthReauthRequiredAt: reauthRequired\n ? account.oauthReauthRequiredAt || Date.now()\n : undefined,\n };\n\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n };\n\n try {\n const response = await fetch(\"/oauth/refresh\", {\n body: JSON.stringify({\n clientId: account.oauthClientId,\n clientSecret: oauthClientSecret,\n providerId: account.oauthProviderId,\n refreshToken: decryptedRefreshToken,\n scope: account.scopes,\n }),\n headers: {\n \"content-type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n return markRefreshFailure();\n }\n\n const payload = (await response.json()) as {\n accessToken?: string;\n expiresIn?: number;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n };\n\n if (!payload.accessToken) {\n return markRefreshFailure();\n }\n\n const encryptedAccessToken = await encryptValue(payload.accessToken);\n if (!encryptedAccessToken) {\n return markRefreshFailure();\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (payload.refreshToken) {\n const maybeEncryptedRefreshToken = await encryptValue(\n payload.refreshToken,\n );\n\n if (maybeEncryptedRefreshToken) {\n encryptedRefreshToken = maybeEncryptedRefreshToken;\n }\n }\n\n const updatedAccount: GitAccount = {\n ...account,\n token: encryptedAccessToken,\n refreshToken: encryptedRefreshToken,\n accessTokenExpiresAt: payload.expiresIn\n ? Date.now() + payload.expiresIn * 1000\n : account.accessTokenExpiresAt,\n tokenType: payload.tokenType || account.tokenType,\n scopes: payload.scope\n ? payload.scope\n .split(/[\\s,]+/)\n .map((scope) => scope.trim())\n .filter(Boolean)\n : account.scopes,\n oauthRefreshFailureCount: 0,\n oauthReauthRequired: false,\n oauthReauthRequiredAt: undefined,\n };\n\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n } catch {\n return markRefreshFailure();\n }\n}\n\n/**\n * Resolve git credentials for a given operation.\n *\n * Resolution order:\n * 1. If GIT_ACCOUNTS exist, match by URL hostname → default account → first account\n * 2. Fall back to legacy single-key config (GIT_TOKEN, GIT_USERNAME, etc.)\n */\nexport async function resolveGitCredentials(\n db: ShadowClawDatabase,\n url?: string,\n options: ResolveGitCredentialsOptions = {},\n): Promise<ResolvedGitCredentials> {\n const accounts = (await getConfig(db, CONFIG_KEYS.GIT_ACCOUNTS)) as\n | GitAccount[]\n | undefined;\n\n if (Array.isArray(accounts) && accounts.length > 0) {\n return resolveFromAccounts(db, accounts, url, options);\n }\n\n return resolveFromLegacy(db);\n}\n\n/**\n * Resolve credentials from the multi-account list.\n */\nasync function resolveFromAccounts(\n db: ShadowClawDatabase,\n accounts: GitAccount[],\n url?: string,\n options: ResolveGitCredentialsOptions = {},\n): Promise<ResolvedGitCredentials> {\n let account: GitAccount | undefined;\n\n if (options.accountId) {\n account = accounts.find((a) => a.id === options.accountId);\n }\n\n // Match by URL host (+ path prefix when the pattern includes a path)\n if (!account && url) {\n const hostAndPath = extractHostAndPath(url);\n if (hostAndPath) {\n const hostMatches = matchAccountsByHost(accounts, hostAndPath);\n account = pickAccountByAuthMode(hostMatches, options.authMode);\n }\n }\n\n // Fall back to default account (only when no URL was provided, or the\n // default account's host matches the URL). Sending unrelated credentials\n // to a public repo host causes a 401 even though the repo is public.\n if (!account) {\n const defaultId = await getConfig(db, CONFIG_KEYS.GIT_DEFAULT_ACCOUNT);\n if (defaultId) {\n const defaultAccount = accounts.find((a) => a.id === defaultId);\n if (defaultAccount) {\n if (!url) {\n account = defaultAccount;\n } else {\n const hostAndPath = extractHostAndPath(url);\n if (hostAndPath && defaultAccount.hostPattern) {\n const lower = hostAndPath?.toLowerCase();\n const pattern = defaultAccount.hostPattern.toLowerCase();\n if (lower?.includes(pattern)) {\n account = defaultAccount;\n }\n }\n }\n }\n }\n }\n\n // Fall back to first account matching requested mode\n if (!account && !url && options.authMode) {\n account = pickAccountByAuthMode(accounts, options.authMode);\n }\n\n // Fall back to first account\n if (!account && !url) {\n account = accounts[0];\n }\n\n if (!account) {\n return {\n authMode: \"token\",\n authorEmail: undefined,\n authorName: undefined,\n password: undefined,\n provider: \"generic\",\n token: undefined,\n username: undefined,\n };\n }\n\n if (shouldRefreshOAuthToken(account, options)) {\n account = await tryRefreshOAuthAccount(db, accounts, account);\n }\n\n const provider =\n account.provider || detectProvider(account.hostPattern || \"\");\n\n if (resolveGitAuthMode(account) === \"oauth\" && account.oauthReauthRequired) {\n return {\n accountId: account.id,\n authMode: \"oauth\",\n authorEmail: account.authorEmail || undefined,\n authorName: account.authorName || undefined,\n hostPattern: account.hostPattern || undefined,\n password: undefined,\n provider,\n reauthRequired: true,\n token: undefined,\n username: account.username || undefined,\n };\n }\n\n return {\n accountId: account.id,\n authMode: resolveGitAuthMode(account),\n authorEmail: account.authorEmail || undefined,\n authorName: account.authorName || undefined,\n hostPattern: account.hostPattern || undefined,\n password: await decryptIfPresent(account.password),\n provider,\n token: await decryptIfPresent(account.token),\n username: account.username || undefined,\n };\n}\n\n/**\n * Resolve credentials from legacy single-key config.\n */\nasync function resolveFromLegacy(\n db: ShadowClawDatabase,\n): Promise<ResolvedGitCredentials> {\n const encToken = await getConfig(db, CONFIG_KEYS.GIT_TOKEN);\n const encPassword = await getConfig(db, CONFIG_KEYS.GIT_PASSWORD);\n\n return {\n authMode: \"token\",\n authorEmail:\n (await getConfig(db, CONFIG_KEYS.GIT_AUTHOR_EMAIL)) || undefined,\n authorName: (await getConfig(db, CONFIG_KEYS.GIT_AUTHOR_NAME)) || undefined,\n password: encPassword ? await decryptIfPresent(encPassword) : undefined,\n provider: \"generic\" as GitProvider,\n token: encToken ? await decryptIfPresent(encToken) : undefined,\n username: (await getConfig(db, CONFIG_KEYS.GIT_USERNAME)) || undefined,\n };\n}\n","import { CONFIG_KEYS } from \"../../config/config.js\";\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\nimport { ulid } from \"../../utils/ulid.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\nimport type { AuthType, ServiceType } from \"../../config/config.js\";\n\nexport type RemoteMcpTransport = \"streamable_http\" | \"sse\" | \"websocket\";\n\nexport interface RemoteMcpCredentialRef {\n serviceType: ServiceType;\n authType: AuthType;\n providerId?: string;\n accountId?: string;\n gitAccountId?: string;\n headerName?: string;\n encryptedValue?: string;\n}\n\nexport interface RemoteMcpConnectionRecord {\n id: string;\n label: string;\n serviceType: Extract<ServiceType, \"mcp_remote\" | \"webmcp_local\">;\n serverUrl: string;\n transport: RemoteMcpTransport;\n enabled: boolean;\n autoReconnectOAuth: boolean;\n createdAt: number;\n updatedAt: number;\n credentialRef: RemoteMcpCredentialRef | null;\n}\n\nexport interface UpsertRemoteMcpConnectionInput {\n id?: string;\n label: string;\n serviceType?: Extract<ServiceType, \"mcp_remote\" | \"webmcp_local\">;\n serverUrl: string;\n transport: RemoteMcpTransport;\n enabled?: boolean;\n autoReconnectOAuth?: boolean;\n}\n\nfunction isSafeAbsoluteUrl(value: unknown): value is string {\n if (typeof value !== \"string\" || !value.trim()) {\n return false;\n }\n\n try {\n const parsed = new URL(value);\n\n return parsed.protocol === \"https:\" || parsed.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n\nfunction normalizeCredentialRef(\n value: unknown,\n connectionServiceType: RemoteMcpConnectionRecord[\"serviceType\"],\n): RemoteMcpCredentialRef | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const raw = value as RemoteMcpCredentialRef;\n if (!raw.serviceType || !raw.authType) {\n return null;\n }\n\n if (raw.serviceType !== connectionServiceType) {\n throw new Error(\n \"Credential ref serviceType must match connection serviceType\",\n );\n }\n\n return {\n serviceType: raw.serviceType,\n authType: raw.authType,\n providerId: raw.providerId || undefined,\n accountId: raw.accountId || undefined,\n gitAccountId: raw.gitAccountId || undefined,\n headerName: raw.headerName || undefined,\n encryptedValue: raw.encryptedValue || undefined,\n };\n}\n\nfunction normalizeConnection(value: unknown): RemoteMcpConnectionRecord | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const raw = value as Partial<RemoteMcpConnectionRecord>;\n if (\n !raw.id ||\n !raw.label ||\n !raw.serviceType ||\n !raw.transport ||\n typeof raw.enabled !== \"boolean\" ||\n typeof raw.createdAt !== \"number\" ||\n typeof raw.updatedAt !== \"number\" ||\n !isSafeAbsoluteUrl(raw.serverUrl)\n ) {\n return null;\n }\n\n if (raw.serviceType !== \"mcp_remote\" && raw.serviceType !== \"webmcp_local\") {\n return null;\n }\n\n if (\n raw.transport !== \"streamable_http\" &&\n raw.transport !== \"sse\" &&\n raw.transport !== \"websocket\"\n ) {\n return null;\n }\n\n return {\n id: raw.id,\n label: raw.label,\n serviceType: raw.serviceType,\n serverUrl: raw.serverUrl,\n transport: raw.transport,\n enabled: raw.enabled,\n autoReconnectOAuth: raw.autoReconnectOAuth === true,\n createdAt: raw.createdAt,\n updatedAt: raw.updatedAt,\n credentialRef: normalizeCredentialRef(raw.credentialRef, raw.serviceType),\n };\n}\n\nasync function loadConnections(\n db: ShadowClawDatabase,\n): Promise<RemoteMcpConnectionRecord[]> {\n const raw = await getConfig(db, CONFIG_KEYS.REMOTE_MCP_CONNECTIONS);\n if (!Array.isArray(raw)) {\n return [];\n }\n\n const normalized: RemoteMcpConnectionRecord[] = [];\n for (const item of raw) {\n const parsed = normalizeConnection(item);\n if (parsed) {\n normalized.push(parsed);\n }\n }\n\n return normalized;\n}\n\nasync function saveConnections(\n db: ShadowClawDatabase,\n connections: RemoteMcpConnectionRecord[],\n): Promise<void> {\n await setConfig(db, CONFIG_KEYS.REMOTE_MCP_CONNECTIONS, connections);\n}\n\nexport async function listRemoteMcpConnections(\n db: ShadowClawDatabase,\n): Promise<RemoteMcpConnectionRecord[]> {\n return loadConnections(db);\n}\n\nexport async function getRemoteMcpConnection(\n db: ShadowClawDatabase,\n id: string,\n): Promise<RemoteMcpConnectionRecord | null> {\n if (!id) {\n return null;\n }\n\n const all = await loadConnections(db);\n\n // Match by exact ID first, then fall back to case-insensitive label match\n // so agents can reference connections by human-readable name.\n\n return (\n all.find((connection) => connection.id === id) ||\n all.find(\n (connection) => connection.label.toLowerCase() === id.toLowerCase(),\n ) ||\n null\n );\n}\n\nexport async function upsertRemoteMcpConnection(\n db: ShadowClawDatabase,\n input: UpsertRemoteMcpConnectionInput,\n): Promise<RemoteMcpConnectionRecord> {\n if (!input?.label?.trim()) {\n throw new Error(\"Connection label is required\");\n }\n\n if (!isSafeAbsoluteUrl(input.serverUrl)) {\n throw new Error(\"Connection serverUrl must be an absolute http(s) URL\");\n }\n\n const serviceType = (input.serviceType ||\n \"mcp_remote\") as RemoteMcpConnectionRecord[\"serviceType\"];\n if (serviceType !== \"mcp_remote\" && serviceType !== \"webmcp_local\") {\n throw new Error(\"Unsupported serviceType for remote MCP connection\");\n }\n\n const all = await loadConnections(db);\n const now = Date.now();\n\n if (input.id) {\n const existingIndex = all.findIndex((record) => record.id === input.id);\n if (existingIndex !== -1) {\n const existing = all[existingIndex];\n const updated: RemoteMcpConnectionRecord = {\n ...existing,\n label: input.label.trim(),\n serviceType,\n serverUrl: input.serverUrl,\n transport: input.transport,\n enabled: input.enabled ?? existing.enabled,\n autoReconnectOAuth:\n input.autoReconnectOAuth ?? existing.autoReconnectOAuth,\n updatedAt: now,\n };\n all[existingIndex] = updated;\n await saveConnections(db, all);\n\n return updated;\n }\n }\n\n const created: RemoteMcpConnectionRecord = {\n id: input.id || ulid(),\n label: input.label.trim(),\n serviceType,\n serverUrl: input.serverUrl,\n transport: input.transport,\n enabled: input.enabled ?? true,\n autoReconnectOAuth: input.autoReconnectOAuth ?? false,\n createdAt: now,\n updatedAt: now,\n credentialRef: null,\n };\n\n all.push(created);\n await saveConnections(db, all);\n\n return created;\n}\n\nexport async function bindRemoteMcpCredentialRef(\n db: ShadowClawDatabase,\n connectionId: string,\n credentialRef: RemoteMcpCredentialRef | null,\n): Promise<RemoteMcpConnectionRecord | null> {\n if (!connectionId) {\n return null;\n }\n\n const all = await loadConnections(db);\n const idx = all.findIndex((record) => record.id === connectionId);\n if (idx === -1) {\n return null;\n }\n\n const current = all[idx];\n const normalizedRef = credentialRef\n ? normalizeCredentialRef(credentialRef, current.serviceType)\n : null;\n\n const updated: RemoteMcpConnectionRecord = {\n ...current,\n credentialRef: normalizedRef,\n updatedAt: Date.now(),\n };\n\n all[idx] = updated;\n await saveConnections(db, all);\n\n return updated;\n}\n\nexport async function deleteRemoteMcpConnection(\n db: ShadowClawDatabase,\n id: string,\n): Promise<boolean> {\n if (!id) {\n return false;\n }\n\n const all = await loadConnections(db);\n const next = all.filter((record) => record.id !== id);\n if (next.length === all.length) {\n return false;\n }\n\n await saveConnections(db, next);\n\n return true;\n}\n","import { decryptValue } from \"../../security/crypto.js\";\nimport { getRemoteMcpConnection } from \"./mcp-connections.js\";\nimport { resolveServiceCredentials } from \"../accounts/service-accounts.js\";\nimport { buildAuthHeaders, resolveGitCredentials } from \"../git/credentials.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\nimport type { RemoteMcpConnectionRecord } from \"./mcp-connections.js\";\nimport type { AuthType } from \"../../config/config.js\";\n\nexport interface ResolvedRemoteMcpAuth {\n connection: RemoteMcpConnectionRecord;\n authType: AuthType;\n headers: Record<string, string>;\n reauthRequired?: boolean;\n}\n\nexport interface ResolveRemoteMcpAuthOptions {\n forceRefresh?: boolean;\n}\n\nfunction toAccountAuthMode(authType: AuthType): \"token\" | \"oauth\" | \"basic\" {\n return authType === \"oauth\" ? \"oauth\" : \"token\";\n}\n\nexport async function resolveRemoteMcpConnectionAuth(\n db: ShadowClawDatabase,\n connectionId: string,\n options: ResolveRemoteMcpAuthOptions = {},\n): Promise<ResolvedRemoteMcpAuth | null> {\n const connection = await getRemoteMcpConnection(db, connectionId);\n if (!connection) {\n return null;\n }\n\n const ref = connection.credentialRef;\n if (!ref || ref.authType === \"none\") {\n return {\n connection,\n authType: \"none\",\n headers: {},\n };\n }\n\n if (ref.authType === \"custom_header\") {\n if (!ref.headerName || !ref.encryptedValue) {\n return {\n connection,\n authType: \"custom_header\",\n headers: {},\n };\n }\n\n const decrypted = await decryptValue(ref.encryptedValue);\n\n return {\n connection,\n authType: \"custom_header\",\n headers: decrypted ? { [ref.headerName]: decrypted } : {},\n };\n }\n\n const authMode = toAccountAuthMode(ref.authType);\n\n if (ref.accountId) {\n const creds = await resolveServiceCredentials(db, connection.serverUrl, {\n accountId: ref.accountId,\n authMode,\n forceRefresh: options.forceRefresh,\n });\n\n if (!creds) {\n return {\n connection,\n authType: ref.authType,\n headers: {},\n };\n }\n\n if (creds.reauthRequired) {\n return {\n connection,\n authType: ref.authType,\n headers: {},\n reauthRequired: true,\n };\n }\n\n return {\n connection,\n authType: ref.authType,\n headers: creds.headerValue\n ? { [creds.headerName]: creds.headerValue }\n : {},\n };\n }\n\n if (ref.gitAccountId) {\n const creds = await resolveGitCredentials(db, connection.serverUrl, {\n accountId: ref.gitAccountId,\n authMode,\n forceRefresh: options.forceRefresh,\n });\n\n if (creds.reauthRequired) {\n return {\n connection,\n authType: ref.authType,\n headers: {},\n reauthRequired: true,\n };\n }\n\n return {\n connection,\n authType: ref.authType,\n headers: buildAuthHeaders(creds),\n };\n }\n\n return {\n connection,\n authType: ref.authType,\n headers: {},\n };\n}\n","import { resolveRemoteMcpConnectionAuth } from \"./mcp-connection-auth.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\n\ninterface RemoteMcpSession {\n sessionId: string;\n protocolVersion?: string;\n lastUsedAt: number;\n}\n\nconst remoteMcpSessions: Map<string, RemoteMcpSession> = new Map();\n\ninterface JsonRpcSuccess<T> {\n jsonrpc: \"2.0\";\n id: string;\n result: T;\n}\n\ninterface JsonRpcError {\n jsonrpc: \"2.0\";\n id: string;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\ntype JsonRpcResponse<T> = JsonRpcSuccess<T> | JsonRpcError;\n\ninterface JsonRpcServerRequest {\n jsonrpc: \"2.0\";\n id: string | number;\n method: string;\n params?: Record<string, unknown>;\n}\n\nfunction isServerRequest(msg: unknown): msg is JsonRpcServerRequest {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"method\" in msg &&\n \"id\" in msg &&\n !(\"result\" in msg) &&\n !(\"error\" in msg)\n );\n}\n\nfunction buildElicitationDefaults(\n params: Record<string, unknown>,\n): Record<string, unknown> {\n const schema = (params.requestedSchema || {}) as Record<string, unknown>;\n const properties = (schema.properties || {}) as Record<\n string,\n Record<string, unknown>\n >;\n const defaults: Record<string, unknown> = {};\n\n for (const [key, prop] of Object.entries(properties)) {\n if (\"default\" in prop) {\n defaults[key] = prop.default;\n } else if (prop.type === \"boolean\") {\n defaults[key] = true;\n }\n }\n\n return defaults;\n}\n\ninterface ToolListResult {\n tools?: Array<{ name: string; description?: string }>;\n}\n\ninterface ToolCallResult {\n content?: unknown[];\n isError?: boolean;\n}\n\ninterface JsonRpcInitializeResult {\n protocolVersion?: string;\n}\n\nclass InvalidRemoteMcpSessionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidRemoteMcpSessionError\";\n }\n}\n\nclass HttpRemoteMcpError extends Error {\n status: number;\n constructor(status: number, message: string) {\n super(message);\n this.name = \"HttpRemoteMcpError\";\n this.status = status;\n }\n}\n\nexport class McpReauthRequiredError extends Error {\n connectionId: string;\n constructor(connectionId: string) {\n super(\"OAuth reconnect required for remote MCP connection\");\n this.name = \"McpReauthRequiredError\";\n this.connectionId = connectionId;\n }\n}\n\ninterface HttpLikeResponse {\n ok: boolean;\n status: number;\n json: () => Promise<unknown>;\n text?: () => Promise<string>;\n headers?: {\n get?: (name: string) => string | null;\n };\n}\n\nfunction getHeader(response: HttpLikeResponse, name: string): string | null {\n if (!response.headers || typeof response.headers.get !== \"function\") {\n return null;\n }\n\n return response.headers.get(name);\n}\n\nfunction isInvalidSessionMessage(message: string): boolean {\n return /no valid session id provided/i.test(message);\n}\n\nfunction toError(\n response: HttpLikeResponse,\n payload: JsonRpcResponse<unknown> | null,\n): Error {\n const jsonRpcMessage =\n payload && \"error\" in payload\n ? payload.error.message || \"Remote MCP JSON-RPC error\"\n : \"\";\n\n if (isInvalidSessionMessage(jsonRpcMessage)) {\n return new InvalidRemoteMcpSessionError(jsonRpcMessage);\n }\n\n if (!response.ok) {\n return new HttpRemoteMcpError(\n response.status,\n jsonRpcMessage\n ? `Remote MCP HTTP error: ${response.status} (${jsonRpcMessage})`\n : `Remote MCP HTTP error: ${response.status}`,\n );\n }\n\n return new Error(jsonRpcMessage || \"Remote MCP JSON-RPC error\");\n}\n\nfunction parseJsonRpcFromSseText<T>(raw: string): JsonRpcResponse<T> | null {\n if (!raw) {\n return null;\n }\n\n let lastJsonRpc: JsonRpcResponse<T> | null = null;\n const lines = raw.split(/\\r?\\n/);\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data:\")) {\n continue;\n }\n\n const data = trimmed.slice(5).trim();\n if (!data || data === \"[DONE]\") {\n continue;\n }\n\n try {\n const parsed = JSON.parse(data) as JsonRpcResponse<T>;\n if (parsed && typeof parsed === \"object\" && \"jsonrpc\" in parsed) {\n lastJsonRpc = parsed;\n }\n } catch {\n // Ignore malformed SSE data chunks and keep scanning.\n }\n }\n\n return lastJsonRpc;\n}\n\nfunction parseJsonRpcFromText<T>(raw: string): JsonRpcResponse<T> | null {\n if (!raw) {\n return null;\n }\n\n try {\n return JSON.parse(raw) as JsonRpcResponse<T>;\n } catch {\n return null;\n }\n}\n\nasync function postJsonRpc<T>(\n url: string,\n headers: Record<string, string>,\n body: Record<string, unknown>,\n): Promise<{\n response: HttpLikeResponse;\n payload: JsonRpcResponse<T> | null;\n}> {\n const response = (await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json, text/event-stream\",\n ...headers,\n },\n body: JSON.stringify(body),\n })) as HttpLikeResponse;\n\n const contentType = (getHeader(response, \"content-type\") || \"\").toLowerCase();\n let payload: JsonRpcResponse<T> | null = null;\n\n if (typeof response.text === \"function\") {\n const raw = await response.text();\n payload = contentType.includes(\"text/event-stream\")\n ? parseJsonRpcFromSseText<T>(raw)\n : parseJsonRpcFromText<T>(raw);\n } else {\n try {\n payload = (await response.json()) as JsonRpcResponse<T>;\n } catch {\n payload = null;\n }\n }\n\n return { response, payload };\n}\n\nasync function sendInitializedNotification(\n serverUrl: string,\n headers: Record<string, string>,\n sessionId: string,\n): Promise<void> {\n const { response, payload } = await postJsonRpc<unknown>(\n serverUrl,\n {\n ...headers,\n \"mcp-session-id\": sessionId,\n },\n {\n jsonrpc: \"2.0\",\n method: \"notifications/initialized\",\n params: {},\n },\n );\n\n if (!response.ok || (payload && \"error\" in payload)) {\n throw toError(response, payload);\n }\n}\n\nasync function ensureRemoteMcpSession(\n connectionId: string,\n serverUrl: string,\n headers: Record<string, string>,\n): Promise<RemoteMcpSession> {\n const existing = remoteMcpSessions.get(connectionId);\n if (existing?.sessionId) {\n return existing;\n }\n\n const { response, payload } = await postJsonRpc<JsonRpcInitializeResult>(\n serverUrl,\n headers,\n {\n jsonrpc: \"2.0\",\n id: `initialize-${Date.now().toString(36)}`,\n method: \"initialize\",\n params: {\n protocolVersion: \"2025-03-26\",\n capabilities: {\n elicitation: {},\n },\n clientInfo: {\n name: \"shadowclaw-remote-mcp-client\",\n version: \"1.0.0\",\n },\n },\n },\n );\n\n if (!response.ok) {\n throw toError(response, payload);\n }\n\n if (payload && \"error\" in payload) {\n throw toError(response, payload);\n }\n\n const sessionId =\n getHeader(response, \"mcp-session-id\") ||\n `local-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n\n const session: RemoteMcpSession = {\n sessionId,\n protocolVersion:\n getHeader(response, \"mcp-protocol-version\") ||\n (payload as { result?: { protocolVersion?: string } } | null)?.result\n ?.protocolVersion,\n lastUsedAt: Date.now(),\n };\n\n await sendInitializedNotification(serverUrl, headers, sessionId);\n remoteMcpSessions.set(connectionId, session);\n\n return session;\n}\n\nasync function callRemoteMcpWithSession<T>(\n connectionId: string,\n serverUrl: string,\n headers: Record<string, string>,\n method: string,\n params?: Record<string, unknown>,\n): Promise<T> {\n let session = await ensureRemoteMcpSession(connectionId, serverUrl, headers);\n\n const doRequest = async (activeSessionId: string) => {\n const body = {\n jsonrpc: \"2.0\",\n id: Date.now().toString(),\n method,\n params: params || {},\n };\n\n const sessionHeaders = {\n ...headers,\n \"mcp-session-id\": activeSessionId,\n };\n\n let { response, payload } = await postJsonRpc<T>(\n serverUrl,\n sessionHeaders,\n body,\n );\n\n // Handle server-initiated requests (e.g., elicitation/create).\n // The gateway may ask the client to confirm before completing a tool call.\n let elicitationAttempts = 0;\n while (isServerRequest(payload) && elicitationAttempts < 5) {\n elicitationAttempts++;\n\n if (payload.method === \"elicitation/create\") {\n const elicitationParams = (payload.params || {}) as Record<\n string,\n unknown\n >;\n const defaults = buildElicitationDefaults(elicitationParams);\n\n ({ response, payload } = await postJsonRpc<T>(\n serverUrl,\n sessionHeaders,\n {\n jsonrpc: \"2.0\",\n id: payload.id,\n result: {\n action: \"accept\",\n content: defaults,\n },\n },\n ));\n } else {\n break;\n }\n }\n\n if (isServerRequest(payload)) {\n throw new Error(\n `Unsupported server request: ${(payload as JsonRpcServerRequest).method}`,\n );\n }\n\n if (!response.ok || !payload || \"error\" in payload) {\n throw toError(response, payload);\n }\n\n return payload.result;\n };\n\n try {\n const result = await doRequest(session.sessionId);\n session.lastUsedAt = Date.now();\n remoteMcpSessions.set(connectionId, session);\n\n return result;\n } catch (err) {\n if (!(err instanceof InvalidRemoteMcpSessionError)) {\n throw err;\n }\n\n // Session expired or unknown on server side; refresh once and retry.\n remoteMcpSessions.delete(connectionId);\n session = await ensureRemoteMcpSession(connectionId, serverUrl, headers);\n const result = await doRequest(session.sessionId);\n session.lastUsedAt = Date.now();\n remoteMcpSessions.set(connectionId, session);\n\n return result;\n }\n}\n\nexport function clearRemoteMcpSession(connectionId: string): void {\n if (!connectionId) {\n return;\n }\n\n remoteMcpSessions.delete(connectionId);\n}\n\nexport function clearAllRemoteMcpSessions(): void {\n remoteMcpSessions.clear();\n}\n\nasync function callRemoteMcp<T>(\n db: ShadowClawDatabase,\n connectionId: string,\n method: string,\n params?: Record<string, unknown>,\n): Promise<T> {\n const resolved = await resolveRemoteMcpConnectionAuth(db, connectionId);\n if (!resolved) {\n throw new Error(\"Remote MCP connection not found\");\n }\n\n if (resolved.reauthRequired) {\n throw new McpReauthRequiredError(connectionId);\n }\n\n const { connection, authType, headers } = resolved;\n\n if (connection.transport !== \"streamable_http\") {\n throw new Error(\"Unsupported remote MCP transport\");\n }\n\n try {\n return await callRemoteMcpWithSession<T>(\n connectionId,\n connection.serverUrl,\n headers,\n method,\n params,\n );\n } catch (err) {\n if (\n !(err instanceof HttpRemoteMcpError) ||\n err.status !== 401 ||\n authType !== \"oauth\"\n ) {\n throw err;\n }\n\n // 401 with OAuth — force-refresh token and retry once.\n clearRemoteMcpSession(connectionId);\n\n const refreshed = await resolveRemoteMcpConnectionAuth(db, connectionId, {\n forceRefresh: true,\n });\n\n if (!refreshed || refreshed.reauthRequired) {\n throw new McpReauthRequiredError(connectionId);\n }\n\n try {\n return await callRemoteMcpWithSession<T>(\n connectionId,\n refreshed.connection.serverUrl,\n refreshed.headers,\n method,\n params,\n );\n } catch (retryErr) {\n if (retryErr instanceof HttpRemoteMcpError && retryErr.status === 401) {\n throw new McpReauthRequiredError(connectionId);\n }\n\n throw retryErr;\n }\n }\n}\n\nexport async function listRemoteMcpTools(\n db: ShadowClawDatabase,\n connectionId: string,\n): Promise<Array<{ name: string; description?: string }>> {\n const result = await callRemoteMcp<ToolListResult>(\n db,\n connectionId,\n \"tools/list\",\n );\n\n return Array.isArray(result.tools) ? result.tools : [];\n}\n\nexport async function callRemoteMcpTool(\n db: ShadowClawDatabase,\n connectionId: string,\n name: string,\n argumentsInput: Record<string, unknown> = {},\n): Promise<ToolCallResult> {\n return callRemoteMcp<ToolCallResult>(db, connectionId, \"tools/call\", {\n name,\n arguments: argumentsInput,\n });\n}\n\nexport interface McpConnectionTestStep {\n step: string;\n status: \"ok\" | \"error\" | \"skipped\";\n detail?: string;\n}\n\nexport interface McpConnectionTestResult {\n success: boolean;\n error: string | null;\n toolCount: number;\n toolNames: string[];\n steps: McpConnectionTestStep[];\n}\n\nexport async function testRemoteMcpConnection(\n db: ShadowClawDatabase,\n connectionId: string,\n): Promise<McpConnectionTestResult> {\n const steps: McpConnectionTestStep[] = [];\n\n // Step 1: Resolve auth\n let resolved;\n try {\n resolved = await resolveRemoteMcpConnectionAuth(db, connectionId);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n steps.push({\n step: \"Resolve authentication\",\n status: \"error\",\n detail: message,\n });\n\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n if (!resolved) {\n steps.push({\n step: \"Resolve authentication\",\n status: \"error\",\n detail: \"Connection not found\",\n });\n\n return {\n success: false,\n error: \"Connection not found\",\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n if (resolved.reauthRequired) {\n const detail =\n \"OAuth token expired or revoked — re-authenticate in Settings\";\n steps.push({ step: \"Resolve authentication\", status: \"error\", detail });\n\n return {\n success: false,\n error: detail,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n steps.push({\n step: \"Resolve authentication\",\n status: \"ok\",\n detail: `Auth type: ${resolved.authType}`,\n });\n\n let { connection, authType, headers } = resolved;\n\n if (connection.transport !== \"streamable_http\") {\n const detail = `Unsupported transport: ${connection.transport}`;\n steps.push({ step: \"Establish MCP session\", status: \"error\", detail });\n\n return {\n success: false,\n error: detail,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n // Step 2: Establish session (clear any stale session first)\n clearRemoteMcpSession(connectionId);\n\n try {\n await ensureRemoteMcpSession(connectionId, connection.serverUrl, headers);\n steps.push({\n step: \"Establish MCP session\",\n status: \"ok\",\n detail: \"Session established\",\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n steps.push({\n step: \"Establish MCP session\",\n status: \"error\",\n detail: message,\n });\n\n // If 401 with OAuth, try force-refreshing the token and retrying.\n if (\n err instanceof HttpRemoteMcpError &&\n err.status === 401 &&\n authType === \"oauth\"\n ) {\n clearRemoteMcpSession(connectionId);\n\n const refreshed = await resolveRemoteMcpConnectionAuth(db, connectionId, {\n forceRefresh: true,\n });\n\n if (refreshed && !refreshed.reauthRequired) {\n steps.push({\n step: \"Refresh OAuth token\",\n status: \"ok\",\n detail: \"Token refreshed, retrying\",\n });\n\n connection = refreshed.connection;\n headers = refreshed.headers;\n\n try {\n await ensureRemoteMcpSession(\n connectionId,\n connection.serverUrl,\n headers,\n );\n steps.push({\n step: \"Establish MCP session\",\n status: \"ok\",\n detail: \"Session established\",\n });\n } catch (retryErr) {\n const retryMessage =\n retryErr instanceof Error ? retryErr.message : String(retryErr);\n const isStill401 =\n retryErr instanceof HttpRemoteMcpError && retryErr.status === 401;\n const detail = isStill401\n ? `${retryMessage} — re-authorize in Settings → Accounts`\n : retryMessage;\n steps.push({\n step: \"Establish MCP session\",\n status: \"error\",\n detail,\n });\n\n return {\n success: false,\n error: detail,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n } else {\n steps.push({\n step: \"Refresh OAuth token\",\n status: \"error\",\n detail: \"Token refresh failed — re-authenticate in Settings\",\n });\n\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n } else {\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n }\n\n // Step 3: Discover tools\n try {\n const result = await callRemoteMcpWithSession<ToolListResult>(\n connectionId,\n connection.serverUrl,\n headers,\n \"tools/list\",\n );\n\n const tools = Array.isArray(result.tools) ? result.tools : [];\n const toolNames = tools.map((t) => t.name);\n steps.push({\n step: \"Discover tools\",\n status: \"ok\",\n detail: `${tools.length} tool${tools.length === 1 ? \"\" : \"s\"} available`,\n });\n\n return {\n success: true,\n error: null,\n toolCount: tools.length,\n toolNames,\n steps,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n steps.push({ step: \"Discover tools\", status: \"error\", detail: message });\n\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n}\n","import {\n CONFIG_KEYS,\n OAUTH_PROVIDER_DEFINITIONS,\n} from \"../../config/config.js\";\nimport { decryptValue, encryptValue } from \"../../security/crypto.js\";\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\nimport { getRemoteMcpConnection } from \"./mcp-connections.js\";\nimport { clearRemoteMcpSession } from \"./remote-mcp-client.js\";\n\nimport type { ServiceAccount } from \"../accounts/service-accounts.js\";\nimport type { RemoteMcpCredentialRef } from \"./mcp-connections.js\";\nimport type { GitAccount } from \"../git/types.js\";\nimport type {\n OAuthAccountLike,\n ReconnectMcpOAuthOptions,\n ReconnectMcpOAuthResult,\n} from \"./types.js\";\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\n\n/** Helper to build account update fields from OAuth token payload. */\nfunction buildOAuthUpdateFields(\n payload: {\n accessToken: string;\n refreshToken?: string;\n expiresIn?: number;\n tokenType?: string;\n scope?: string;\n },\n existingAccount: OAuthAccountLike,\n encryptedAccessToken: string,\n encryptedRefreshToken: string | undefined,\n) {\n return {\n accessTokenExpiresAt: payload.expiresIn\n ? Date.now() + payload.expiresIn * 1000\n : existingAccount.accessTokenExpiresAt,\n oauthReauthRequired: false,\n oauthReauthRequiredAt: undefined,\n oauthRefreshFailureCount: 0,\n refreshToken: encryptedRefreshToken,\n scopes: payload.scope\n ? payload.scope\n .split(/[\\s,]+/)\n .map((s) => s.trim())\n .filter(Boolean)\n : existingAccount.scopes,\n token: encryptedAccessToken,\n tokenType: payload.tokenType || existingAccount.tokenType,\n };\n}\n\n/**\n * Try a silent OAuth token refresh using the existing refresh token.\n * Returns `true` if the token was successfully refreshed and persisted.\n */\nasync function trySilentRefresh(\n db: ShadowClawDatabase,\n account: OAuthAccountLike,\n accountStore: \"service\" | \"git\",\n serviceAccounts: ServiceAccount[],\n gitAccounts: GitAccount[],\n ref: RemoteMcpCredentialRef,\n connectionId: string,\n): Promise<ReconnectMcpOAuthResult> {\n if (!account.refreshToken) {\n return { success: false, error: \"No refresh token available.\" };\n }\n\n const decryptedRefreshToken = await decryptValue(account.refreshToken);\n if (!decryptedRefreshToken) {\n return { success: false, error: \"Failed to decrypt refresh token.\" };\n }\n\n const clientSecret = account.oauthClientSecret\n ? await decryptValue(account.oauthClientSecret)\n : undefined;\n\n try {\n const response = await fetch(\"/oauth/refresh\", {\n body: JSON.stringify({\n clientId: account.oauthClientId,\n clientSecret: clientSecret || undefined,\n providerId: account.oauthProviderId,\n refreshToken: decryptedRefreshToken,\n scope: account.scopes,\n tokenUrl: account.oauthCustomTokenUrl,\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\",\n });\n\n if (!response.ok) {\n return { success: false, error: \"Token refresh request failed.\" };\n }\n\n const payload = (await response.json()) as {\n accessToken?: string;\n expiresIn?: number;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n };\n\n if (!payload.accessToken) {\n return { success: false, error: \"No access token in refresh response.\" };\n }\n\n const encryptedAccessToken = await encryptValue(payload.accessToken);\n if (!encryptedAccessToken) {\n return { success: false, error: \"Failed to encrypt access token.\" };\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (payload.refreshToken) {\n const maybeEncrypted = await encryptValue(payload.refreshToken);\n if (maybeEncrypted) {\n encryptedRefreshToken = maybeEncrypted;\n }\n }\n\n const updatedFields = buildOAuthUpdateFields(\n {\n accessToken: payload.accessToken,\n expiresIn: payload.expiresIn,\n refreshToken: payload.refreshToken,\n scope: payload.scope,\n tokenType: payload.tokenType,\n },\n account,\n encryptedAccessToken,\n encryptedRefreshToken,\n );\n\n if (accountStore === \"service\" && ref.accountId) {\n const idx = serviceAccounts.findIndex(\n (acct) => acct.id === ref.accountId,\n );\n\n if (idx !== -1) {\n Object.assign(serviceAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS, serviceAccounts);\n }\n } else if (accountStore === \"git\" && ref.gitAccountId) {\n const idx = gitAccounts.findIndex((acct) => acct.id === ref.gitAccountId);\n if (idx !== -1) {\n Object.assign(gitAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.GIT_ACCOUNTS, gitAccounts);\n }\n }\n\n clearRemoteMcpSession(connectionId);\n\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n\n return { success: false, error: `Silent refresh failed: ${message}` };\n }\n}\n\n/**\n * Performs OAuth re-authentication for a remote MCP connection.\n *\n * When `silentOnly` is true, only attempts a token refresh using the\n * existing refresh token — no popup is opened. This is used for\n * automatic reconnection from the orchestrator (no user gesture).\n *\n * When `silentOnly` is false (default), first tries a silent refresh,\n * and if that fails, opens a popup for full re-authorization.\n */\nexport async function reconnectMcpOAuth(\n db: ShadowClawDatabase,\n connectionId: string,\n options: ReconnectMcpOAuthOptions = {},\n): Promise<ReconnectMcpOAuthResult> {\n const connection = await getRemoteMcpConnection(db, connectionId);\n if (!connection?.credentialRef) {\n return { success: false, error: \"Connection not found.\" };\n }\n\n const ref = connection.credentialRef;\n if (ref.authType !== \"oauth\") {\n return {\n success: false,\n error: \"This connection does not use OAuth authentication.\",\n };\n }\n\n // Load accounts from DB\n const serviceRaw = await getConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS);\n const serviceAccounts: ServiceAccount[] = Array.isArray(serviceRaw)\n ? serviceRaw\n : [];\n\n const gitRaw = await getConfig(db, CONFIG_KEYS.GIT_ACCOUNTS);\n const gitAccounts: GitAccount[] = Array.isArray(gitRaw) ? gitRaw : [];\n\n // Find the linked account\n let account: OAuthAccountLike | undefined;\n let accountStore: \"service\" | \"git\" | null = null;\n\n if (ref.accountId) {\n account = serviceAccounts.find((acct) => acct.id === ref.accountId) as\n | OAuthAccountLike\n | undefined;\n\n accountStore = \"service\";\n } else if (ref.gitAccountId) {\n account = gitAccounts.find((acct) => acct.id === ref.gitAccountId) as\n | OAuthAccountLike\n | undefined;\n\n accountStore = \"git\";\n }\n\n if (!account || !accountStore) {\n return {\n error:\n \"Linked account not found. Edit the connection to re-configure authentication.\",\n success: false,\n };\n }\n\n const clientId = account.oauthClientId;\n const providerId = account.oauthProviderId;\n\n if (!providerId || !clientId) {\n return {\n error:\n \"Account is missing OAuth provider or client ID. Edit the account in Settings → Accounts to fix.\",\n success: false,\n };\n }\n\n // ── Silent refresh attempt ───────────────────────────────────────\n // Always try a silent token refresh first. This works without a\n // popup and handles the common case where the refresh token is\n // still valid but the access token expired.\n if (account.refreshToken) {\n const silentResult = await trySilentRefresh(\n db,\n account,\n accountStore,\n serviceAccounts,\n gitAccounts,\n ref,\n connectionId,\n );\n\n if (silentResult.success) {\n return silentResult;\n }\n }\n\n // If silentOnly was requested, don't open a popup.\n if (options.silentOnly) {\n return {\n error:\n \"Silent token refresh failed. Use Settings → Remote MCP → Reconnect OAuth for full re-authorization.\",\n success: false,\n };\n }\n\n const clientSecret = account.oauthClientSecret\n ? await decryptValue(account.oauthClientSecret)\n : undefined;\n\n const scope = account.scopes;\n\n // Resolve OAuth URLs\n const providerDef = OAUTH_PROVIDER_DEFINITIONS[providerId];\n const isCustomMcp = providerId === \"custom_mcp\";\n const redirectUri =\n isCustomMcp && account.oauthCustomRedirectUri\n ? account.oauthCustomRedirectUri\n : providerDef?.redirectUri\n ? providerDef.redirectUri\n : `${globalThis.location?.origin || \"\"}/oauth/callback`;\n\n try {\n const authorizeRes = await fetch(\"/oauth/authorize\", {\n body: JSON.stringify({\n providerId,\n clientId,\n clientSecret: clientSecret || undefined,\n redirectUri,\n scope,\n ...(isCustomMcp && account.oauthCustomAuthorizeUrl\n ? { authorizeUrl: account.oauthCustomAuthorizeUrl }\n : {}),\n ...(isCustomMcp && account.oauthCustomTokenUrl\n ? { tokenUrl: account.oauthCustomTokenUrl }\n : {}),\n ...(isCustomMcp && typeof account.oauthCustomUsePkce === \"boolean\"\n ? { usePkce: account.oauthCustomUsePkce }\n : {}),\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\",\n });\n\n const authorizePayload = (await authorizeRes.json()) as {\n authorizeUrl?: string;\n error?: string;\n state?: string;\n };\n\n if (\n !authorizeRes.ok ||\n !authorizePayload.state ||\n !authorizePayload.authorizeUrl\n ) {\n throw new Error(authorizePayload.error || \"OAuth authorize failed\");\n }\n\n const popup = window.open(\n authorizePayload.authorizeUrl,\n \"shadowclaw-oauth\",\n \"popup=yes,width=540,height=720\",\n );\n\n if (!popup || popup.closed || typeof popup.closed === \"undefined\") {\n throw new Error(\n \"OAuth popup was blocked by the browser. Please enable popups for this site and try again.\",\n );\n }\n\n const state = authorizePayload.state;\n let status = \"pending\";\n for (let attempt = 0; attempt < 60; attempt++) {\n const sessionRes = await fetch(\n `/oauth/session/${encodeURIComponent(state)}`,\n );\n\n const sessionPayload = (await sessionRes.json()) as {\n error?: string;\n status?: string;\n };\n\n if (!sessionRes.ok) {\n throw new Error(sessionPayload.error || \"OAuth session not found\");\n }\n\n status = sessionPayload.status || \"pending\";\n\n if (status === \"authorized\") {\n break;\n }\n\n if (status === \"error\") {\n throw new Error(sessionPayload.error || \"OAuth authorization failed\");\n }\n\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n\n if (status !== \"authorized\") {\n throw new Error(\"OAuth authorization timed out\");\n }\n\n const tokenRes = await fetch(\"/oauth/token\", {\n body: JSON.stringify({ state }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\",\n });\n\n const tokenPayload = (await tokenRes.json()) as {\n accessToken?: string;\n error?: string;\n expiresIn?: number;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n };\n\n if (!tokenRes.ok || !tokenPayload.accessToken) {\n throw new Error(tokenPayload.error || \"OAuth token exchange failed\");\n }\n\n // Update the linked account with new tokens\n const encryptedAccessToken = await encryptValue(tokenPayload.accessToken);\n if (!encryptedAccessToken) {\n throw new Error(\"Failed to encrypt access token\");\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (tokenPayload.refreshToken) {\n const maybeEncrypted = await encryptValue(tokenPayload.refreshToken);\n if (maybeEncrypted) {\n encryptedRefreshToken = maybeEncrypted;\n }\n }\n\n const updatedFields = buildOAuthUpdateFields(\n {\n accessToken: tokenPayload.accessToken,\n expiresIn: tokenPayload.expiresIn,\n refreshToken: tokenPayload.refreshToken,\n scope: tokenPayload.scope,\n tokenType: tokenPayload.tokenType,\n },\n account,\n encryptedAccessToken,\n encryptedRefreshToken,\n );\n\n // Persist to the correct account store\n if (accountStore === \"service\" && ref.accountId) {\n const idx = serviceAccounts.findIndex(\n (acct) => acct.id === ref.accountId,\n );\n if (idx !== -1) {\n Object.assign(serviceAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS, serviceAccounts);\n }\n } else if (accountStore === \"git\" && ref.gitAccountId) {\n const idx = gitAccounts.findIndex((acct) => acct.id === ref.gitAccountId);\n if (idx !== -1) {\n Object.assign(gitAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.GIT_ACCOUNTS, gitAccounts);\n }\n }\n\n // Clear the MCP session so the next call uses fresh credentials\n clearRemoteMcpSession(connectionId);\n\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n\n return { success: false, error: message };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp-reconnect-CoHDMjmY.js","names":["OAUTH_REFRESH_WINDOW_MS","OAUTH_REFRESH_FAILURE_THRESHOLD","shouldRefreshOAuthToken","tryRefreshOAuthAccount","matchAccountsByHost","pickAccountByAuthMode"],"sources":["../../src/subsystems/accounts/stored-credentials.ts","../../src/subsystems/accounts/service-accounts.ts","../../src/subsystems/git/credentials.ts","../../src/subsystems/mcp/mcp-connections.ts","../../src/subsystems/mcp/mcp-connection-auth.ts","../../src/subsystems/mcp/remote-mcp-client.ts","../../src/subsystems/mcp/mcp-reconnect.ts"],"sourcesContent":["export type StoredCredentialAuthMode = \"token\" | \"basic\" | \"oauth\";\n\nexport interface StoredCredentialBase {\n id: string;\n label: string;\n hostPattern: string;\n token: string;\n basicUsername?: string;\n authMode?: StoredCredentialAuthMode;\n}\n\nexport interface StoredCredentialOAuthFields {\n oauthProviderId?: string;\n oauthClientId?: string;\n oauthClientSecret?: string;\n accessTokenExpiresAt?: number;\n refreshToken?: string;\n scopes?: string[];\n tokenType?: string;\n oauthRefreshFailureCount?: number;\n oauthReauthRequired?: boolean;\n oauthReauthRequiredAt?: number;\n /** Custom authorize URL (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomAuthorizeUrl?: string;\n /** Custom token URL (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomTokenUrl?: string;\n /** Custom PKCE preference (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomUsePkce?: boolean;\n /** Custom redirect URI (used when oauthProviderId is \"custom_mcp\"). */\n oauthCustomRedirectUri?: string;\n}\n\nexport interface PendingOAuthResult {\n providerId: string;\n accessToken: string;\n refreshToken?: string;\n expiresAt?: number;\n scope?: string;\n tokenType?: string;\n}\n\ntype OAuthCapableStoredCredential = {\n authMode?: StoredCredentialAuthMode;\n} & StoredCredentialOAuthFields;\n\nexport function resolveStoredCredentialAuthMode(\n credential: OAuthCapableStoredCredential | null | undefined,\n): StoredCredentialAuthMode {\n if (!credential) {\n return \"token\";\n }\n\n // Treat legacy \"pat\" string identically to \"token\" mode\n if (\n credential.authMode === \"token\" ||\n (credential.authMode as string) === \"pat\"\n ) {\n return \"token\";\n }\n\n if (credential.authMode === \"basic\") {\n return \"basic\";\n }\n\n if (credential.authMode === \"oauth\") {\n return \"oauth\";\n }\n\n if (\n credential.oauthProviderId ||\n credential.oauthClientId ||\n credential.oauthClientSecret ||\n credential.refreshToken ||\n credential.accessTokenExpiresAt ||\n credential.tokenType ||\n credential.oauthReauthRequired\n ) {\n return \"oauth\";\n }\n\n return \"token\";\n}\n","import {\n CONFIG_KEYS,\n GENERAL_ACCOUNT_PROVIDER_CAPABILITIES,\n getGeneralAccountProviderCapabilities,\n} from \"../../config/config.js\";\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\nimport { decryptValue, encryptValue } from \"../../security/crypto.js\";\nimport {\n resolveStoredCredentialAuthMode,\n type StoredCredentialAuthMode,\n type StoredCredentialBase,\n type StoredCredentialOAuthFields,\n} from \"./stored-credentials.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\nimport type { GeneralAccountProviderCapabilities } from \"../../config/config.js\";\n\nexport type AccountAuthMode = StoredCredentialAuthMode;\n\nexport interface ServiceAccount\n extends StoredCredentialBase, StoredCredentialOAuthFields {\n service: string;\n}\n\nexport interface ResolvedServiceCredentials {\n token: string;\n service: string;\n hostPattern: string;\n headerName: string; // e.g. \"Authorization\", \"X-Figma-Token\"\n headerValue: string; // e.g. \"Bearer TOKEN\", \"TOKEN\" (no prefix)\n accountId: string;\n authMode: AccountAuthMode;\n reauthRequired?: boolean;\n}\n\nexport interface ResolveServiceCredentialsOptions {\n accountId?: string;\n authMode?: AccountAuthMode;\n forceRefresh?: boolean;\n}\n\nconst OAUTH_REFRESH_WINDOW_MS = 60_000;\nconst OAUTH_REFRESH_FAILURE_THRESHOLD = 3;\n\nfunction resolveAccountAuthMode(account: ServiceAccount): AccountAuthMode {\n return resolveStoredCredentialAuthMode(account);\n}\n\nfunction shouldRefreshOAuthToken(\n account: ServiceAccount,\n options: ResolveServiceCredentialsOptions,\n): boolean {\n if (resolveAccountAuthMode(account) !== \"oauth\") {\n return false;\n }\n\n if (account.oauthReauthRequired) {\n return false;\n }\n\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return false;\n }\n\n if (options.forceRefresh) {\n return true;\n }\n\n if (!account.accessTokenExpiresAt) {\n return false;\n }\n\n return account.accessTokenExpiresAt <= Date.now() + OAUTH_REFRESH_WINDOW_MS;\n}\n\nasync function tryRefreshOAuthAccount(\n db: ShadowClawDatabase,\n accounts: ServiceAccount[],\n account: ServiceAccount,\n): Promise<ServiceAccount> {\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return account;\n }\n\n const decryptedRefreshToken = await decryptValue(account.refreshToken);\n if (!decryptedRefreshToken) {\n return account;\n }\n\n const oauthClientSecret = account.oauthClientSecret\n ? await decryptValue(account.oauthClientSecret)\n : undefined;\n\n const persistAccount = async (updatedAccount: ServiceAccount) => {\n const idx = accounts.findIndex((candidate) => candidate.id === account.id);\n if (idx !== -1) {\n accounts[idx] = updatedAccount;\n await setConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS, accounts);\n }\n };\n\n const markRefreshFailure = async (): Promise<ServiceAccount> => {\n const failureCount = (account.oauthRefreshFailureCount || 0) + 1;\n const reauthRequired = failureCount >= OAUTH_REFRESH_FAILURE_THRESHOLD;\n const updatedAccount: ServiceAccount = {\n ...account,\n oauthRefreshFailureCount: failureCount,\n oauthReauthRequired: reauthRequired,\n oauthReauthRequiredAt: reauthRequired\n ? account.oauthReauthRequiredAt || Date.now()\n : undefined,\n };\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n };\n\n try {\n const response = await fetch(\"/oauth/refresh\", {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n providerId: account.oauthProviderId,\n clientId: account.oauthClientId,\n clientSecret: oauthClientSecret,\n refreshToken: decryptedRefreshToken,\n scope: account.scopes,\n tokenUrl: account.oauthCustomTokenUrl,\n }),\n });\n\n if (!response.ok) {\n return markRefreshFailure();\n }\n\n const payload = (await response.json()) as {\n accessToken?: string;\n refreshToken?: string;\n expiresIn?: number;\n tokenType?: string;\n scope?: string;\n };\n\n if (!payload.accessToken) {\n return markRefreshFailure();\n }\n\n const encryptedAccessToken = await encryptValue(payload.accessToken);\n if (!encryptedAccessToken) {\n return markRefreshFailure();\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (payload.refreshToken) {\n const maybeEncryptedRefreshToken = await encryptValue(\n payload.refreshToken,\n );\n if (maybeEncryptedRefreshToken) {\n encryptedRefreshToken = maybeEncryptedRefreshToken;\n }\n }\n\n const updatedAccount: ServiceAccount = {\n ...account,\n token: encryptedAccessToken,\n refreshToken: encryptedRefreshToken,\n accessTokenExpiresAt: payload.expiresIn\n ? Date.now() + payload.expiresIn * 1000\n : account.accessTokenExpiresAt,\n tokenType: payload.tokenType || account.tokenType,\n scopes: payload.scope\n ? payload.scope\n .split(/[\\s,]+/)\n .map((scope) => scope.trim())\n .filter(Boolean)\n : account.scopes,\n oauthRefreshFailureCount: 0,\n oauthReauthRequired: false,\n oauthReauthRequiredAt: undefined,\n };\n\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n } catch {\n return markRefreshFailure();\n }\n}\n\nfunction resolveProviderCapabilities(\n account: ServiceAccount,\n url?: string,\n): GeneralAccountProviderCapabilities | null {\n if (account.oauthProviderId) {\n const direct = getGeneralAccountProviderCapabilities(\n account.oauthProviderId,\n );\n if (direct) {\n return direct;\n }\n }\n\n const hints = [account.service, account.hostPattern, url]\n .filter((value): value is string => !!value)\n .map((value) => value.toLowerCase());\n\n for (const provider of Object.values(GENERAL_ACCOUNT_PROVIDER_CAPABILITIES)) {\n const aliases = [provider.providerId, ...(provider.aliases || [])].map(\n (alias) => alias.toLowerCase(),\n );\n\n if (aliases.some((alias) => hints.some((hint) => hint.includes(alias)))) {\n return provider;\n }\n }\n\n return null;\n}\n\n/**\n * Resolve auth header format from provider capabilities. Falls back to Bearer.\n */\nfunction getServiceAuthFormat(\n account: ServiceAccount,\n url?: string,\n): {\n headerName: string;\n headerPrefix: string;\n} {\n const authMode = resolveAccountAuthMode(account);\n const provider = resolveProviderCapabilities(account, url);\n if (provider) {\n let scheme;\n if (authMode === \"oauth\") {\n scheme = provider.tokenAuth.oauth;\n } else if (authMode === \"basic\" && provider.tokenAuth.basic) {\n scheme = provider.tokenAuth.basic;\n } else {\n scheme = provider.tokenAuth.token;\n }\n\n return {\n headerName: scheme.headerName,\n headerPrefix: scheme.headerPrefix || \"\",\n };\n }\n\n return {\n headerName: \"Authorization\",\n headerPrefix: authMode === \"basic\" ? \"Basic \" : \"Bearer \",\n };\n}\n\n/**\n * Find the most-specific (longest hostPattern) account that matches a URL.\n */\nfunction matchAccountsByHost(\n accounts: ServiceAccount[],\n url: string,\n): ServiceAccount[] {\n const matches: ServiceAccount[] = [];\n let bestLen = -1;\n const lower = url.toLowerCase();\n\n for (const acct of accounts) {\n if (!acct.hostPattern) {\n continue;\n }\n\n const pattern = acct.hostPattern.toLowerCase();\n if (lower.includes(pattern)) {\n if (pattern.length > bestLen) {\n bestLen = pattern.length;\n matches.length = 0;\n matches.push(acct);\n } else if (pattern.length === bestLen) {\n matches.push(acct);\n }\n }\n }\n\n return matches;\n}\n\nfunction pickAccountByAuthMode(\n accounts: ServiceAccount[],\n authMode?: AccountAuthMode,\n): ServiceAccount | undefined {\n if (!accounts.length) {\n return undefined;\n }\n\n if (!authMode) {\n return accounts[0];\n }\n\n const modeMatch = accounts.find(\n (acct) => resolveAccountAuthMode(acct) === authMode,\n );\n if (modeMatch) {\n return modeMatch;\n }\n\n return accounts[0];\n}\n\n/**\n * Resolve a decrypted service account PAT for the given URL.\n *\n * Resolution order:\n * 1. Longest hostPattern match against the URL\n * 2. SERVICE_DEFAULT_ACCOUNT\n * 3. First account in the list\n *\n * Returns undefined when no accounts are configured.\n */\nexport async function resolveServiceCredentials(\n db: ShadowClawDatabase,\n url?: string,\n options: ResolveServiceCredentialsOptions = {},\n): Promise<ResolvedServiceCredentials | undefined> {\n const raw = await getConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS);\n const accounts: ServiceAccount[] = Array.isArray(raw) ? raw : [];\n\n if (accounts.length === 0) {\n return undefined;\n }\n\n let account: ServiceAccount | undefined;\n\n if (options.accountId) {\n account = accounts.find((a) => a.id === options.accountId);\n }\n\n // 1. Match by URL host pattern\n if (!account && url) {\n const hostMatches = matchAccountsByHost(accounts, url);\n if (options.authMode) {\n account = pickAccountByAuthMode(hostMatches, options.authMode);\n } else {\n const preferredMode = hostMatches[0]\n ? resolveProviderCapabilities(hostMatches[0], url)?.defaultMode\n : undefined;\n account = pickAccountByAuthMode(hostMatches, preferredMode);\n }\n }\n\n // 2. Fall back to stored default\n if (!account) {\n const defaultId = await getConfig(db, CONFIG_KEYS.SERVICE_DEFAULT_ACCOUNT);\n if (defaultId) {\n account = accounts.find((a) => a.id === defaultId);\n }\n }\n\n // 3. Fall back to first account matching requested mode\n if (!account && options.authMode) {\n account = pickAccountByAuthMode(accounts, options.authMode);\n }\n\n // 4. Fall back to first\n if (!account) {\n account = accounts[0];\n }\n\n if (!account) {\n return undefined;\n }\n\n if (shouldRefreshOAuthToken(account, options)) {\n account = await tryRefreshOAuthAccount(db, accounts, account);\n }\n\n if (\n resolveAccountAuthMode(account) === \"oauth\" &&\n account.oauthReauthRequired\n ) {\n return {\n token: \"\",\n service: account.service,\n hostPattern: account.hostPattern,\n headerName: \"Authorization\",\n headerValue: \"\",\n accountId: account.id,\n authMode: \"oauth\",\n reauthRequired: true,\n };\n }\n\n let token = \"\";\n if (account.token) {\n token = (await decryptValue(account.token)) ?? \"\";\n }\n\n const { headerName, headerPrefix } = getServiceAuthFormat(account, url);\n let finalToken = token;\n\n if (resolveAccountAuthMode(account) === \"basic\" && account.basicUsername) {\n finalToken = btoa(`${account.basicUsername}:${token}`);\n }\n\n const headerValue = headerPrefix\n ? `${headerPrefix}${finalToken}`\n : finalToken;\n\n return {\n token,\n service: account.service,\n hostPattern: account.hostPattern,\n headerName,\n headerValue,\n accountId: account.id,\n authMode: resolveAccountAuthMode(account),\n };\n}\n","import {\n CONFIG_KEYS,\n getProviderTokenAuthScheme,\n} from \"../../config/config.js\";\n\nimport { resolveStoredCredentialAuthMode } from \"../accounts/stored-credentials.js\";\nimport { decryptValue, encryptValue } from \"../../security/crypto.js\";\n\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\n\nimport type {\n GitAccount,\n GitAuthMode,\n GitProvider,\n ResolveGitCredentialsOptions,\n ResolvedGitCredentials,\n} from \"./types.js\";\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\n\nconst OAUTH_REFRESH_WINDOW_MS = 60_000;\nconst OAUTH_REFRESH_FAILURE_THRESHOLD = 3;\n\nfunction resolveGitAuthMode(account: GitAccount): GitAuthMode {\n return resolveStoredCredentialAuthMode(account);\n}\n\nfunction mapGitProviderToGeneralProviderId(provider: GitProvider): string {\n switch (provider) {\n case \"azure-devops\":\n return \"azure_devops\";\n default:\n return provider;\n }\n}\n\n/**\n * Detect the git provider type from a hostname or host pattern.\n */\nexport function detectProvider(hostPattern: string): GitProvider {\n if (!hostPattern) {\n return \"generic\";\n }\n\n const lower = hostPattern.toLowerCase();\n if (lower.includes(\"github\")) {\n return \"github\";\n }\n\n if (lower.includes(\"dev.azure.com\") || lower.includes(\"visualstudio.com\")) {\n return \"azure-devops\";\n }\n\n if (lower.includes(\"gitlab\")) {\n return \"gitlab\";\n }\n\n return \"generic\";\n}\n\n/**\n * Build HTTP Authorization headers for a resolved credential set.\n */\nexport function buildAuthHeaders(\n creds: Partial<ResolvedGitCredentials> & {\n hostPattern?: string;\n provider?: GitProvider;\n },\n): Record<string, string> {\n if (creds.reauthRequired) {\n return {};\n }\n\n const provider = creds.provider || detectProvider(creds.hostPattern || \"\");\n const mappedProviderId = mapGitProviderToGeneralProviderId(provider);\n const authMode = creds.authMode || \"token\";\n\n // Some git providers (Azure DevOps) need Basic auth for PAT/OAuth over git HTTP.\n if (\n creds.token &&\n mappedProviderId === \"azure_devops\" &&\n getProviderTokenAuthScheme(mappedProviderId, authMode, \"git_remote\")\n ?.headerPrefix === \"Basic \"\n ) {\n return { Authorization: `Basic ${btoa(\":\" + creds.token)}` };\n }\n\n switch (provider) {\n case \"azure-devops\": {\n if (creds.username && creds.password) {\n return {\n Authorization: `Basic ${btoa(creds.username + \":\" + creds.password)}`,\n };\n }\n\n break;\n }\n\n default:\n break;\n }\n\n if (creds.token) {\n const scheme = getProviderTokenAuthScheme(\n mappedProviderId,\n authMode,\n \"git_remote\",\n );\n\n if (scheme?.headerName) {\n return {\n [scheme.headerName]: `${scheme.headerPrefix || \"\"}${creds.token}`,\n };\n }\n\n return { Authorization: `Bearer ${creds.token}` };\n }\n\n return {};\n}\n\n/**\n * Extract hostname + pathname from a git remote URL.\n * Returns e.g. \"dev.azure.com/exampleOrg2/project/_git/repo\" so that\n * path-based host patterns like \"dev.azure.com/exampleOrg2\" can match.\n */\nfunction extractHostAndPath(url: string): string | undefined {\n try {\n const parsed = new URL(url);\n const path = parsed.pathname.replace(/\\/+$/, \"\");\n\n return path ? parsed.hostname + path : parsed.hostname;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Find the best matching account for a URL by host (and optional path prefix).\n * When multiple accounts match, the longest (most specific) pattern wins.\n */\nfunction matchAccountsByHost(\n accounts: GitAccount[],\n hostAndPath: string,\n): GitAccount[] {\n const lower = hostAndPath.toLowerCase();\n const matches: GitAccount[] = [];\n\n let bestLen = -1;\n for (const a of accounts) {\n if (!a.hostPattern) {\n continue;\n }\n\n const pattern = a.hostPattern.toLowerCase();\n if (lower.includes(pattern)) {\n if (pattern.length > bestLen) {\n bestLen = pattern.length;\n\n matches.length = 0;\n matches.push(a);\n } else if (pattern.length === bestLen) {\n matches.push(a);\n }\n }\n }\n\n return matches;\n}\n\nfunction pickAccountByAuthMode(\n accounts: GitAccount[],\n authMode?: GitAuthMode,\n): GitAccount | undefined {\n if (!accounts.length) {\n return undefined;\n }\n\n if (!authMode) {\n return accounts[0];\n }\n\n const modeMatch = accounts.find(\n (acct) => resolveGitAuthMode(acct) === authMode,\n );\n if (modeMatch) {\n return modeMatch;\n }\n\n // Legacy accounts without explicit mode should behave as PAT accounts.\n if (authMode === \"token\") {\n const legacyPat = accounts.find(\n (acct) => resolveGitAuthMode(acct) === \"token\" && !acct.authMode,\n );\n\n if (legacyPat) {\n return legacyPat;\n }\n }\n\n return accounts[0];\n}\n\n/**\n * Decrypt a stored encrypted value, returning undefined for empty/missing.\n */\nasync function decryptIfPresent(\n encrypted: string | null | undefined,\n): Promise<string | undefined> {\n if (!encrypted) {\n return undefined;\n }\n\n const result = await decryptValue(encrypted);\n\n return result ?? undefined;\n}\n\nfunction shouldRefreshOAuthToken(\n account: GitAccount,\n options: ResolveGitCredentialsOptions,\n): boolean {\n if (resolveGitAuthMode(account) !== \"oauth\") {\n return false;\n }\n\n if (account.oauthReauthRequired) {\n return false;\n }\n\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return false;\n }\n\n if (options.forceRefresh) {\n return true;\n }\n\n if (!account.accessTokenExpiresAt) {\n return false;\n }\n\n return account.accessTokenExpiresAt <= Date.now() + OAUTH_REFRESH_WINDOW_MS;\n}\n\nasync function tryRefreshOAuthAccount(\n db: ShadowClawDatabase,\n accounts: GitAccount[],\n account: GitAccount,\n): Promise<GitAccount> {\n if (\n !account.refreshToken ||\n !account.oauthProviderId ||\n !account.oauthClientId\n ) {\n return account;\n }\n\n const decryptedRefreshToken = await decryptIfPresent(account.refreshToken);\n if (!decryptedRefreshToken) {\n return account;\n }\n\n const oauthClientSecret = account.oauthClientSecret\n ? await decryptIfPresent(account.oauthClientSecret)\n : undefined;\n\n const persistAccount = async (updatedAccount: GitAccount) => {\n const idx = accounts.findIndex((candidate) => candidate.id === account.id);\n if (idx !== -1) {\n accounts[idx] = updatedAccount;\n await setConfig(db, CONFIG_KEYS.GIT_ACCOUNTS, accounts);\n }\n };\n\n const markRefreshFailure = async (): Promise<GitAccount> => {\n const failureCount = (account.oauthRefreshFailureCount || 0) + 1;\n const reauthRequired = failureCount >= OAUTH_REFRESH_FAILURE_THRESHOLD;\n const updatedAccount: GitAccount = {\n ...account,\n oauthRefreshFailureCount: failureCount,\n oauthReauthRequired: reauthRequired,\n oauthReauthRequiredAt: reauthRequired\n ? account.oauthReauthRequiredAt || Date.now()\n : undefined,\n };\n\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n };\n\n try {\n const response = await fetch(\"/oauth/refresh\", {\n body: JSON.stringify({\n clientId: account.oauthClientId,\n clientSecret: oauthClientSecret,\n providerId: account.oauthProviderId,\n refreshToken: decryptedRefreshToken,\n scope: account.scopes,\n }),\n headers: {\n \"content-type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n return markRefreshFailure();\n }\n\n const payload = (await response.json()) as {\n accessToken?: string;\n expiresIn?: number;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n };\n\n if (!payload.accessToken) {\n return markRefreshFailure();\n }\n\n const encryptedAccessToken = await encryptValue(payload.accessToken);\n if (!encryptedAccessToken) {\n return markRefreshFailure();\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (payload.refreshToken) {\n const maybeEncryptedRefreshToken = await encryptValue(\n payload.refreshToken,\n );\n\n if (maybeEncryptedRefreshToken) {\n encryptedRefreshToken = maybeEncryptedRefreshToken;\n }\n }\n\n const updatedAccount: GitAccount = {\n ...account,\n token: encryptedAccessToken,\n refreshToken: encryptedRefreshToken,\n accessTokenExpiresAt: payload.expiresIn\n ? Date.now() + payload.expiresIn * 1000\n : account.accessTokenExpiresAt,\n tokenType: payload.tokenType || account.tokenType,\n scopes: payload.scope\n ? payload.scope\n .split(/[\\s,]+/)\n .map((scope) => scope.trim())\n .filter(Boolean)\n : account.scopes,\n oauthRefreshFailureCount: 0,\n oauthReauthRequired: false,\n oauthReauthRequiredAt: undefined,\n };\n\n await persistAccount(updatedAccount);\n\n return updatedAccount;\n } catch {\n return markRefreshFailure();\n }\n}\n\n/**\n * Resolve git credentials for a given operation.\n *\n * Resolution order:\n * 1. If GIT_ACCOUNTS exist, match by URL hostname → default account → first account\n * 2. Fall back to legacy single-key config (GIT_TOKEN, GIT_USERNAME, etc.)\n */\nexport async function resolveGitCredentials(\n db: ShadowClawDatabase,\n url?: string,\n options: ResolveGitCredentialsOptions = {},\n): Promise<ResolvedGitCredentials> {\n const accounts = (await getConfig(db, CONFIG_KEYS.GIT_ACCOUNTS)) as\n | GitAccount[]\n | undefined;\n\n if (Array.isArray(accounts) && accounts.length > 0) {\n return resolveFromAccounts(db, accounts, url, options);\n }\n\n return resolveFromLegacy(db);\n}\n\n/**\n * Resolve credentials from the multi-account list.\n */\nasync function resolveFromAccounts(\n db: ShadowClawDatabase,\n accounts: GitAccount[],\n url?: string,\n options: ResolveGitCredentialsOptions = {},\n): Promise<ResolvedGitCredentials> {\n let account: GitAccount | undefined;\n\n if (options.accountId) {\n account = accounts.find((a) => a.id === options.accountId);\n }\n\n // Match by URL host (+ path prefix when the pattern includes a path)\n if (!account && url) {\n const hostAndPath = extractHostAndPath(url);\n if (hostAndPath) {\n const hostMatches = matchAccountsByHost(accounts, hostAndPath);\n account = pickAccountByAuthMode(hostMatches, options.authMode);\n }\n }\n\n // Fall back to default account (only when no URL was provided, or the\n // default account's host matches the URL). Sending unrelated credentials\n // to a public repo host causes a 401 even though the repo is public.\n if (!account) {\n const defaultId = await getConfig(db, CONFIG_KEYS.GIT_DEFAULT_ACCOUNT);\n if (defaultId) {\n const defaultAccount = accounts.find((a) => a.id === defaultId);\n if (defaultAccount) {\n if (!url) {\n account = defaultAccount;\n } else {\n const hostAndPath = extractHostAndPath(url);\n if (hostAndPath && defaultAccount.hostPattern) {\n const lower = hostAndPath?.toLowerCase();\n const pattern = defaultAccount.hostPattern.toLowerCase();\n if (lower?.includes(pattern)) {\n account = defaultAccount;\n }\n }\n }\n }\n }\n }\n\n // Fall back to first account matching requested mode\n if (!account && !url && options.authMode) {\n account = pickAccountByAuthMode(accounts, options.authMode);\n }\n\n // Fall back to first account\n if (!account && !url) {\n account = accounts[0];\n }\n\n if (!account) {\n return {\n authMode: \"token\",\n authorEmail: undefined,\n authorName: undefined,\n password: undefined,\n provider: \"generic\",\n token: undefined,\n username: undefined,\n };\n }\n\n if (shouldRefreshOAuthToken(account, options)) {\n account = await tryRefreshOAuthAccount(db, accounts, account);\n }\n\n const provider =\n account.provider || detectProvider(account.hostPattern || \"\");\n\n if (resolveGitAuthMode(account) === \"oauth\" && account.oauthReauthRequired) {\n return {\n accountId: account.id,\n authMode: \"oauth\",\n authorEmail: account.authorEmail || undefined,\n authorName: account.authorName || undefined,\n hostPattern: account.hostPattern || undefined,\n password: undefined,\n provider,\n reauthRequired: true,\n token: undefined,\n username: account.username || undefined,\n };\n }\n\n return {\n accountId: account.id,\n authMode: resolveGitAuthMode(account),\n authorEmail: account.authorEmail || undefined,\n authorName: account.authorName || undefined,\n hostPattern: account.hostPattern || undefined,\n password: await decryptIfPresent(account.password),\n provider,\n token: await decryptIfPresent(account.token),\n username: account.username || undefined,\n };\n}\n\n/**\n * Resolve credentials from legacy single-key config.\n */\nasync function resolveFromLegacy(\n db: ShadowClawDatabase,\n): Promise<ResolvedGitCredentials> {\n const encToken = await getConfig(db, CONFIG_KEYS.GIT_TOKEN);\n const encPassword = await getConfig(db, CONFIG_KEYS.GIT_PASSWORD);\n\n return {\n authMode: \"token\",\n authorEmail:\n (await getConfig(db, CONFIG_KEYS.GIT_AUTHOR_EMAIL)) || undefined,\n authorName: (await getConfig(db, CONFIG_KEYS.GIT_AUTHOR_NAME)) || undefined,\n password: encPassword ? await decryptIfPresent(encPassword) : undefined,\n provider: \"generic\" as GitProvider,\n token: encToken ? await decryptIfPresent(encToken) : undefined,\n username: (await getConfig(db, CONFIG_KEYS.GIT_USERNAME)) || undefined,\n };\n}\n","import { CONFIG_KEYS } from \"../../config/config.js\";\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\nimport { ulid } from \"../../utils/ulid.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\nimport type { AuthType, ServiceType } from \"../../config/config.js\";\n\nexport type RemoteMcpTransport = \"streamable_http\" | \"sse\" | \"websocket\";\n\nexport interface RemoteMcpCredentialRef {\n serviceType: ServiceType;\n authType: AuthType;\n providerId?: string;\n accountId?: string;\n gitAccountId?: string;\n headerName?: string;\n encryptedValue?: string;\n}\n\nexport interface RemoteMcpConnectionRecord {\n id: string;\n label: string;\n serviceType: Extract<ServiceType, \"mcp_remote\" | \"webmcp_local\">;\n serverUrl: string;\n transport: RemoteMcpTransport;\n enabled: boolean;\n autoReconnectOAuth: boolean;\n createdAt: number;\n updatedAt: number;\n credentialRef: RemoteMcpCredentialRef | null;\n}\n\nexport interface UpsertRemoteMcpConnectionInput {\n id?: string;\n label: string;\n serviceType?: Extract<ServiceType, \"mcp_remote\" | \"webmcp_local\">;\n serverUrl: string;\n transport: RemoteMcpTransport;\n enabled?: boolean;\n autoReconnectOAuth?: boolean;\n}\n\nfunction isSafeAbsoluteUrl(value: unknown): value is string {\n if (typeof value !== \"string\" || !value.trim()) {\n return false;\n }\n\n try {\n const parsed = new URL(value);\n\n return parsed.protocol === \"https:\" || parsed.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n\nfunction normalizeCredentialRef(\n value: unknown,\n connectionServiceType: RemoteMcpConnectionRecord[\"serviceType\"],\n): RemoteMcpCredentialRef | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const raw = value as RemoteMcpCredentialRef;\n if (!raw.serviceType || !raw.authType) {\n return null;\n }\n\n if (raw.serviceType !== connectionServiceType) {\n throw new Error(\n \"Credential ref serviceType must match connection serviceType\",\n );\n }\n\n return {\n serviceType: raw.serviceType,\n authType: raw.authType,\n providerId: raw.providerId || undefined,\n accountId: raw.accountId || undefined,\n gitAccountId: raw.gitAccountId || undefined,\n headerName: raw.headerName || undefined,\n encryptedValue: raw.encryptedValue || undefined,\n };\n}\n\nfunction normalizeConnection(value: unknown): RemoteMcpConnectionRecord | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const raw = value as Partial<RemoteMcpConnectionRecord>;\n if (\n !raw.id ||\n !raw.label ||\n !raw.serviceType ||\n !raw.transport ||\n typeof raw.enabled !== \"boolean\" ||\n typeof raw.createdAt !== \"number\" ||\n typeof raw.updatedAt !== \"number\" ||\n !isSafeAbsoluteUrl(raw.serverUrl)\n ) {\n return null;\n }\n\n if (raw.serviceType !== \"mcp_remote\" && raw.serviceType !== \"webmcp_local\") {\n return null;\n }\n\n if (\n raw.transport !== \"streamable_http\" &&\n raw.transport !== \"sse\" &&\n raw.transport !== \"websocket\"\n ) {\n return null;\n }\n\n return {\n id: raw.id,\n label: raw.label,\n serviceType: raw.serviceType,\n serverUrl: raw.serverUrl,\n transport: raw.transport,\n enabled: raw.enabled,\n autoReconnectOAuth: raw.autoReconnectOAuth === true,\n createdAt: raw.createdAt,\n updatedAt: raw.updatedAt,\n credentialRef: normalizeCredentialRef(raw.credentialRef, raw.serviceType),\n };\n}\n\nasync function loadConnections(\n db: ShadowClawDatabase,\n): Promise<RemoteMcpConnectionRecord[]> {\n const raw = await getConfig(db, CONFIG_KEYS.REMOTE_MCP_CONNECTIONS);\n if (!Array.isArray(raw)) {\n return [];\n }\n\n const normalized: RemoteMcpConnectionRecord[] = [];\n for (const item of raw) {\n const parsed = normalizeConnection(item);\n if (parsed) {\n normalized.push(parsed);\n }\n }\n\n return normalized;\n}\n\nasync function saveConnections(\n db: ShadowClawDatabase,\n connections: RemoteMcpConnectionRecord[],\n): Promise<void> {\n await setConfig(db, CONFIG_KEYS.REMOTE_MCP_CONNECTIONS, connections);\n}\n\nexport async function listRemoteMcpConnections(\n db: ShadowClawDatabase,\n): Promise<RemoteMcpConnectionRecord[]> {\n return loadConnections(db);\n}\n\nexport async function getRemoteMcpConnection(\n db: ShadowClawDatabase,\n id: string,\n): Promise<RemoteMcpConnectionRecord | null> {\n if (!id) {\n return null;\n }\n\n const all = await loadConnections(db);\n\n // Match by exact ID first, then fall back to case-insensitive label match\n // so agents can reference connections by human-readable name.\n\n return (\n all.find((connection) => connection.id === id) ||\n all.find(\n (connection) => connection.label.toLowerCase() === id.toLowerCase(),\n ) ||\n null\n );\n}\n\nexport async function upsertRemoteMcpConnection(\n db: ShadowClawDatabase,\n input: UpsertRemoteMcpConnectionInput,\n): Promise<RemoteMcpConnectionRecord> {\n if (!input?.label?.trim()) {\n throw new Error(\"Connection label is required\");\n }\n\n if (!isSafeAbsoluteUrl(input.serverUrl)) {\n throw new Error(\"Connection serverUrl must be an absolute http(s) URL\");\n }\n\n const serviceType = (input.serviceType ||\n \"mcp_remote\") as RemoteMcpConnectionRecord[\"serviceType\"];\n if (serviceType !== \"mcp_remote\" && serviceType !== \"webmcp_local\") {\n throw new Error(\"Unsupported serviceType for remote MCP connection\");\n }\n\n const all = await loadConnections(db);\n const now = Date.now();\n\n if (input.id) {\n const existingIndex = all.findIndex((record) => record.id === input.id);\n if (existingIndex !== -1) {\n const existing = all[existingIndex];\n const updated: RemoteMcpConnectionRecord = {\n ...existing,\n label: input.label.trim(),\n serviceType,\n serverUrl: input.serverUrl,\n transport: input.transport,\n enabled: input.enabled ?? existing.enabled,\n autoReconnectOAuth:\n input.autoReconnectOAuth ?? existing.autoReconnectOAuth,\n updatedAt: now,\n };\n all[existingIndex] = updated;\n await saveConnections(db, all);\n\n return updated;\n }\n }\n\n const created: RemoteMcpConnectionRecord = {\n id: input.id || ulid(),\n label: input.label.trim(),\n serviceType,\n serverUrl: input.serverUrl,\n transport: input.transport,\n enabled: input.enabled ?? true,\n autoReconnectOAuth: input.autoReconnectOAuth ?? false,\n createdAt: now,\n updatedAt: now,\n credentialRef: null,\n };\n\n all.push(created);\n await saveConnections(db, all);\n\n return created;\n}\n\nexport async function bindRemoteMcpCredentialRef(\n db: ShadowClawDatabase,\n connectionId: string,\n credentialRef: RemoteMcpCredentialRef | null,\n): Promise<RemoteMcpConnectionRecord | null> {\n if (!connectionId) {\n return null;\n }\n\n const all = await loadConnections(db);\n const idx = all.findIndex((record) => record.id === connectionId);\n if (idx === -1) {\n return null;\n }\n\n const current = all[idx];\n const normalizedRef = credentialRef\n ? normalizeCredentialRef(credentialRef, current.serviceType)\n : null;\n\n const updated: RemoteMcpConnectionRecord = {\n ...current,\n credentialRef: normalizedRef,\n updatedAt: Date.now(),\n };\n\n all[idx] = updated;\n await saveConnections(db, all);\n\n return updated;\n}\n\nexport async function deleteRemoteMcpConnection(\n db: ShadowClawDatabase,\n id: string,\n): Promise<boolean> {\n if (!id) {\n return false;\n }\n\n const all = await loadConnections(db);\n const next = all.filter((record) => record.id !== id);\n if (next.length === all.length) {\n return false;\n }\n\n await saveConnections(db, next);\n\n return true;\n}\n","import { decryptValue } from \"../../security/crypto.js\";\nimport { getRemoteMcpConnection } from \"./mcp-connections.js\";\nimport { resolveServiceCredentials } from \"../accounts/service-accounts.js\";\nimport { buildAuthHeaders, resolveGitCredentials } from \"../git/credentials.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\nimport type { RemoteMcpConnectionRecord } from \"./mcp-connections.js\";\nimport type { AuthType } from \"../../config/config.js\";\n\nexport interface ResolvedRemoteMcpAuth {\n connection: RemoteMcpConnectionRecord;\n authType: AuthType;\n headers: Record<string, string>;\n reauthRequired?: boolean;\n}\n\nexport interface ResolveRemoteMcpAuthOptions {\n forceRefresh?: boolean;\n}\n\nfunction toAccountAuthMode(authType: AuthType): \"token\" | \"oauth\" | \"basic\" {\n return authType === \"oauth\" ? \"oauth\" : \"token\";\n}\n\nexport async function resolveRemoteMcpConnectionAuth(\n db: ShadowClawDatabase,\n connectionId: string,\n options: ResolveRemoteMcpAuthOptions = {},\n): Promise<ResolvedRemoteMcpAuth | null> {\n const connection = await getRemoteMcpConnection(db, connectionId);\n if (!connection) {\n return null;\n }\n\n const ref = connection.credentialRef;\n if (!ref || ref.authType === \"none\") {\n return {\n connection,\n authType: \"none\",\n headers: {},\n };\n }\n\n if (ref.authType === \"custom_header\") {\n if (!ref.headerName || !ref.encryptedValue) {\n return {\n connection,\n authType: \"custom_header\",\n headers: {},\n };\n }\n\n const decrypted = await decryptValue(ref.encryptedValue);\n\n return {\n connection,\n authType: \"custom_header\",\n headers: decrypted ? { [ref.headerName]: decrypted } : {},\n };\n }\n\n const authMode = toAccountAuthMode(ref.authType);\n\n if (ref.accountId) {\n const creds = await resolveServiceCredentials(db, connection.serverUrl, {\n accountId: ref.accountId,\n authMode,\n forceRefresh: options.forceRefresh,\n });\n\n if (!creds) {\n return {\n connection,\n authType: ref.authType,\n headers: {},\n };\n }\n\n if (creds.reauthRequired) {\n return {\n connection,\n authType: ref.authType,\n headers: {},\n reauthRequired: true,\n };\n }\n\n return {\n connection,\n authType: ref.authType,\n headers: creds.headerValue\n ? { [creds.headerName]: creds.headerValue }\n : {},\n };\n }\n\n if (ref.gitAccountId) {\n const creds = await resolveGitCredentials(db, connection.serverUrl, {\n accountId: ref.gitAccountId,\n authMode,\n forceRefresh: options.forceRefresh,\n });\n\n if (creds.reauthRequired) {\n return {\n connection,\n authType: ref.authType,\n headers: {},\n reauthRequired: true,\n };\n }\n\n return {\n connection,\n authType: ref.authType,\n headers: buildAuthHeaders(creds),\n };\n }\n\n return {\n connection,\n authType: ref.authType,\n headers: {},\n };\n}\n","import { resolveRemoteMcpConnectionAuth } from \"./mcp-connection-auth.js\";\n\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\n\ninterface RemoteMcpSession {\n sessionId: string;\n protocolVersion?: string;\n lastUsedAt: number;\n}\n\nconst remoteMcpSessions: Map<string, RemoteMcpSession> = new Map();\n\ninterface JsonRpcSuccess<T> {\n jsonrpc: \"2.0\";\n id: string;\n result: T;\n}\n\ninterface JsonRpcError {\n jsonrpc: \"2.0\";\n id: string;\n error: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\ntype JsonRpcResponse<T> = JsonRpcSuccess<T> | JsonRpcError;\n\ninterface JsonRpcServerRequest {\n jsonrpc: \"2.0\";\n id: string | number;\n method: string;\n params?: Record<string, unknown>;\n}\n\nfunction isServerRequest(msg: unknown): msg is JsonRpcServerRequest {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"method\" in msg &&\n \"id\" in msg &&\n !(\"result\" in msg) &&\n !(\"error\" in msg)\n );\n}\n\nfunction buildElicitationDefaults(\n params: Record<string, unknown>,\n): Record<string, unknown> {\n const schema = (params.requestedSchema || {}) as Record<string, unknown>;\n const properties = (schema.properties || {}) as Record<\n string,\n Record<string, unknown>\n >;\n const defaults: Record<string, unknown> = {};\n\n for (const [key, prop] of Object.entries(properties)) {\n if (\"default\" in prop) {\n defaults[key] = prop.default;\n } else if (prop.type === \"boolean\") {\n defaults[key] = true;\n }\n }\n\n return defaults;\n}\n\ninterface ToolListResult {\n tools?: Array<{ name: string; description?: string }>;\n}\n\ninterface ToolCallResult {\n content?: unknown[];\n isError?: boolean;\n}\n\ninterface JsonRpcInitializeResult {\n protocolVersion?: string;\n}\n\nclass InvalidRemoteMcpSessionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidRemoteMcpSessionError\";\n }\n}\n\nclass HttpRemoteMcpError extends Error {\n status: number;\n constructor(status: number, message: string) {\n super(message);\n this.name = \"HttpRemoteMcpError\";\n this.status = status;\n }\n}\n\nexport class McpReauthRequiredError extends Error {\n connectionId: string;\n constructor(connectionId: string) {\n super(\"OAuth reconnect required for remote MCP connection\");\n this.name = \"McpReauthRequiredError\";\n this.connectionId = connectionId;\n }\n}\n\ninterface HttpLikeResponse {\n ok: boolean;\n status: number;\n json: () => Promise<unknown>;\n text?: () => Promise<string>;\n headers?: {\n get?: (name: string) => string | null;\n };\n}\n\nfunction getHeader(response: HttpLikeResponse, name: string): string | null {\n if (!response.headers || typeof response.headers.get !== \"function\") {\n return null;\n }\n\n return response.headers.get(name);\n}\n\nfunction isInvalidSessionMessage(message: string): boolean {\n return /no valid session id provided/i.test(message);\n}\n\nfunction toError(\n response: HttpLikeResponse,\n payload: JsonRpcResponse<unknown> | null,\n): Error {\n const jsonRpcMessage =\n payload && \"error\" in payload\n ? payload.error.message || \"Remote MCP JSON-RPC error\"\n : \"\";\n\n if (isInvalidSessionMessage(jsonRpcMessage)) {\n return new InvalidRemoteMcpSessionError(jsonRpcMessage);\n }\n\n if (!response.ok) {\n return new HttpRemoteMcpError(\n response.status,\n jsonRpcMessage\n ? `Remote MCP HTTP error: ${response.status} (${jsonRpcMessage})`\n : `Remote MCP HTTP error: ${response.status}`,\n );\n }\n\n return new Error(jsonRpcMessage || \"Remote MCP JSON-RPC error\");\n}\n\nfunction parseJsonRpcFromSseText<T>(raw: string): JsonRpcResponse<T> | null {\n if (!raw) {\n return null;\n }\n\n let lastJsonRpc: JsonRpcResponse<T> | null = null;\n const lines = raw.split(/\\r?\\n/);\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data:\")) {\n continue;\n }\n\n const data = trimmed.slice(5).trim();\n if (!data || data === \"[DONE]\") {\n continue;\n }\n\n try {\n const parsed = JSON.parse(data) as JsonRpcResponse<T>;\n if (parsed && typeof parsed === \"object\" && \"jsonrpc\" in parsed) {\n lastJsonRpc = parsed;\n }\n } catch {\n // Ignore malformed SSE data chunks and keep scanning.\n }\n }\n\n return lastJsonRpc;\n}\n\nfunction parseJsonRpcFromText<T>(raw: string): JsonRpcResponse<T> | null {\n if (!raw) {\n return null;\n }\n\n try {\n return JSON.parse(raw) as JsonRpcResponse<T>;\n } catch {\n return null;\n }\n}\n\nasync function postJsonRpc<T>(\n url: string,\n headers: Record<string, string>,\n body: Record<string, unknown>,\n): Promise<{\n response: HttpLikeResponse;\n payload: JsonRpcResponse<T> | null;\n}> {\n const response = (await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json, text/event-stream\",\n ...headers,\n },\n body: JSON.stringify(body),\n })) as HttpLikeResponse;\n\n const contentType = (getHeader(response, \"content-type\") || \"\").toLowerCase();\n let payload: JsonRpcResponse<T> | null = null;\n\n if (typeof response.text === \"function\") {\n const raw = await response.text();\n payload = contentType.includes(\"text/event-stream\")\n ? parseJsonRpcFromSseText<T>(raw)\n : parseJsonRpcFromText<T>(raw);\n } else {\n try {\n payload = (await response.json()) as JsonRpcResponse<T>;\n } catch {\n payload = null;\n }\n }\n\n return { response, payload };\n}\n\nasync function sendInitializedNotification(\n serverUrl: string,\n headers: Record<string, string>,\n sessionId: string,\n): Promise<void> {\n const { response, payload } = await postJsonRpc<unknown>(\n serverUrl,\n {\n ...headers,\n \"mcp-session-id\": sessionId,\n },\n {\n jsonrpc: \"2.0\",\n method: \"notifications/initialized\",\n params: {},\n },\n );\n\n if (!response.ok || (payload && \"error\" in payload)) {\n throw toError(response, payload);\n }\n}\n\nasync function ensureRemoteMcpSession(\n connectionId: string,\n serverUrl: string,\n headers: Record<string, string>,\n): Promise<RemoteMcpSession> {\n const existing = remoteMcpSessions.get(connectionId);\n if (existing?.sessionId) {\n return existing;\n }\n\n const { response, payload } = await postJsonRpc<JsonRpcInitializeResult>(\n serverUrl,\n headers,\n {\n jsonrpc: \"2.0\",\n id: `initialize-${Date.now().toString(36)}`,\n method: \"initialize\",\n params: {\n protocolVersion: \"2025-03-26\",\n capabilities: {\n elicitation: {},\n },\n clientInfo: {\n name: \"shadowclaw-remote-mcp-client\",\n version: \"1.0.0\",\n },\n },\n },\n );\n\n if (!response.ok) {\n throw toError(response, payload);\n }\n\n if (payload && \"error\" in payload) {\n throw toError(response, payload);\n }\n\n const sessionId =\n getHeader(response, \"mcp-session-id\") ||\n `local-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n\n const session: RemoteMcpSession = {\n sessionId,\n protocolVersion:\n getHeader(response, \"mcp-protocol-version\") ||\n (payload as { result?: { protocolVersion?: string } } | null)?.result\n ?.protocolVersion,\n lastUsedAt: Date.now(),\n };\n\n await sendInitializedNotification(serverUrl, headers, sessionId);\n remoteMcpSessions.set(connectionId, session);\n\n return session;\n}\n\nasync function callRemoteMcpWithSession<T>(\n connectionId: string,\n serverUrl: string,\n headers: Record<string, string>,\n method: string,\n params?: Record<string, unknown>,\n): Promise<T> {\n let session = await ensureRemoteMcpSession(connectionId, serverUrl, headers);\n\n const doRequest = async (activeSessionId: string) => {\n const body = {\n jsonrpc: \"2.0\",\n id: Date.now().toString(),\n method,\n params: params || {},\n };\n\n const sessionHeaders = {\n ...headers,\n \"mcp-session-id\": activeSessionId,\n };\n\n let { response, payload } = await postJsonRpc<T>(\n serverUrl,\n sessionHeaders,\n body,\n );\n\n // Handle server-initiated requests (e.g., elicitation/create).\n // The gateway may ask the client to confirm before completing a tool call.\n let elicitationAttempts = 0;\n while (isServerRequest(payload) && elicitationAttempts < 5) {\n elicitationAttempts++;\n\n if (payload.method === \"elicitation/create\") {\n const elicitationParams = (payload.params || {}) as Record<\n string,\n unknown\n >;\n const defaults = buildElicitationDefaults(elicitationParams);\n\n ({ response, payload } = await postJsonRpc<T>(\n serverUrl,\n sessionHeaders,\n {\n jsonrpc: \"2.0\",\n id: payload.id,\n result: {\n action: \"accept\",\n content: defaults,\n },\n },\n ));\n } else {\n break;\n }\n }\n\n if (isServerRequest(payload)) {\n throw new Error(\n `Unsupported server request: ${(payload as JsonRpcServerRequest).method}`,\n );\n }\n\n if (!response.ok || !payload || \"error\" in payload) {\n throw toError(response, payload);\n }\n\n return payload.result;\n };\n\n try {\n const result = await doRequest(session.sessionId);\n session.lastUsedAt = Date.now();\n remoteMcpSessions.set(connectionId, session);\n\n return result;\n } catch (err) {\n if (!(err instanceof InvalidRemoteMcpSessionError)) {\n throw err;\n }\n\n // Session expired or unknown on server side; refresh once and retry.\n remoteMcpSessions.delete(connectionId);\n session = await ensureRemoteMcpSession(connectionId, serverUrl, headers);\n const result = await doRequest(session.sessionId);\n session.lastUsedAt = Date.now();\n remoteMcpSessions.set(connectionId, session);\n\n return result;\n }\n}\n\nexport function clearRemoteMcpSession(connectionId: string): void {\n if (!connectionId) {\n return;\n }\n\n remoteMcpSessions.delete(connectionId);\n}\n\nexport function clearAllRemoteMcpSessions(): void {\n remoteMcpSessions.clear();\n}\n\nasync function callRemoteMcp<T>(\n db: ShadowClawDatabase,\n connectionId: string,\n method: string,\n params?: Record<string, unknown>,\n): Promise<T> {\n const resolved = await resolveRemoteMcpConnectionAuth(db, connectionId);\n if (!resolved) {\n throw new Error(\"Remote MCP connection not found\");\n }\n\n if (resolved.reauthRequired) {\n throw new McpReauthRequiredError(connectionId);\n }\n\n const { connection, authType, headers } = resolved;\n\n if (connection.transport !== \"streamable_http\") {\n throw new Error(\"Unsupported remote MCP transport\");\n }\n\n try {\n return await callRemoteMcpWithSession<T>(\n connectionId,\n connection.serverUrl,\n headers,\n method,\n params,\n );\n } catch (err) {\n if (\n !(err instanceof HttpRemoteMcpError) ||\n err.status !== 401 ||\n authType !== \"oauth\"\n ) {\n throw err;\n }\n\n // 401 with OAuth — force-refresh token and retry once.\n clearRemoteMcpSession(connectionId);\n\n const refreshed = await resolveRemoteMcpConnectionAuth(db, connectionId, {\n forceRefresh: true,\n });\n\n if (!refreshed || refreshed.reauthRequired) {\n throw new McpReauthRequiredError(connectionId);\n }\n\n try {\n return await callRemoteMcpWithSession<T>(\n connectionId,\n refreshed.connection.serverUrl,\n refreshed.headers,\n method,\n params,\n );\n } catch (retryErr) {\n if (retryErr instanceof HttpRemoteMcpError && retryErr.status === 401) {\n throw new McpReauthRequiredError(connectionId);\n }\n\n throw retryErr;\n }\n }\n}\n\nexport async function listRemoteMcpTools(\n db: ShadowClawDatabase,\n connectionId: string,\n): Promise<Array<{ name: string; description?: string }>> {\n const result = await callRemoteMcp<ToolListResult>(\n db,\n connectionId,\n \"tools/list\",\n );\n\n return Array.isArray(result.tools) ? result.tools : [];\n}\n\nexport async function callRemoteMcpTool(\n db: ShadowClawDatabase,\n connectionId: string,\n name: string,\n argumentsInput: Record<string, unknown> = {},\n): Promise<ToolCallResult> {\n return callRemoteMcp<ToolCallResult>(db, connectionId, \"tools/call\", {\n name,\n arguments: argumentsInput,\n });\n}\n\nexport interface McpConnectionTestStep {\n step: string;\n status: \"ok\" | \"error\" | \"skipped\";\n detail?: string;\n}\n\nexport interface McpConnectionTestResult {\n success: boolean;\n error: string | null;\n toolCount: number;\n toolNames: string[];\n steps: McpConnectionTestStep[];\n}\n\nexport async function testRemoteMcpConnection(\n db: ShadowClawDatabase,\n connectionId: string,\n): Promise<McpConnectionTestResult> {\n const steps: McpConnectionTestStep[] = [];\n\n // Step 1: Resolve auth\n let resolved;\n try {\n resolved = await resolveRemoteMcpConnectionAuth(db, connectionId);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n steps.push({\n step: \"Resolve authentication\",\n status: \"error\",\n detail: message,\n });\n\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n if (!resolved) {\n steps.push({\n step: \"Resolve authentication\",\n status: \"error\",\n detail: \"Connection not found\",\n });\n\n return {\n success: false,\n error: \"Connection not found\",\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n if (resolved.reauthRequired) {\n const detail =\n \"OAuth token expired or revoked — re-authenticate in Settings\";\n steps.push({ step: \"Resolve authentication\", status: \"error\", detail });\n\n return {\n success: false,\n error: detail,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n steps.push({\n step: \"Resolve authentication\",\n status: \"ok\",\n detail: `Auth type: ${resolved.authType}`,\n });\n\n let { connection, authType, headers } = resolved;\n\n if (connection.transport !== \"streamable_http\") {\n const detail = `Unsupported transport: ${connection.transport}`;\n steps.push({ step: \"Establish MCP session\", status: \"error\", detail });\n\n return {\n success: false,\n error: detail,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n\n // Step 2: Establish session (clear any stale session first)\n clearRemoteMcpSession(connectionId);\n\n try {\n await ensureRemoteMcpSession(connectionId, connection.serverUrl, headers);\n steps.push({\n step: \"Establish MCP session\",\n status: \"ok\",\n detail: \"Session established\",\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n steps.push({\n step: \"Establish MCP session\",\n status: \"error\",\n detail: message,\n });\n\n // If 401 with OAuth, try force-refreshing the token and retrying.\n if (\n err instanceof HttpRemoteMcpError &&\n err.status === 401 &&\n authType === \"oauth\"\n ) {\n clearRemoteMcpSession(connectionId);\n\n const refreshed = await resolveRemoteMcpConnectionAuth(db, connectionId, {\n forceRefresh: true,\n });\n\n if (refreshed && !refreshed.reauthRequired) {\n steps.push({\n step: \"Refresh OAuth token\",\n status: \"ok\",\n detail: \"Token refreshed, retrying\",\n });\n\n connection = refreshed.connection;\n headers = refreshed.headers;\n\n try {\n await ensureRemoteMcpSession(\n connectionId,\n connection.serverUrl,\n headers,\n );\n steps.push({\n step: \"Establish MCP session\",\n status: \"ok\",\n detail: \"Session established\",\n });\n } catch (retryErr) {\n const retryMessage =\n retryErr instanceof Error ? retryErr.message : String(retryErr);\n const isStill401 =\n retryErr instanceof HttpRemoteMcpError && retryErr.status === 401;\n const detail = isStill401\n ? `${retryMessage} — re-authorize in Settings → Accounts`\n : retryMessage;\n steps.push({\n step: \"Establish MCP session\",\n status: \"error\",\n detail,\n });\n\n return {\n success: false,\n error: detail,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n } else {\n steps.push({\n step: \"Refresh OAuth token\",\n status: \"error\",\n detail: \"Token refresh failed — re-authenticate in Settings\",\n });\n\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n } else {\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n }\n\n // Step 3: Discover tools\n try {\n const result = await callRemoteMcpWithSession<ToolListResult>(\n connectionId,\n connection.serverUrl,\n headers,\n \"tools/list\",\n );\n\n const tools = Array.isArray(result.tools) ? result.tools : [];\n const toolNames = tools.map((t) => t.name);\n steps.push({\n step: \"Discover tools\",\n status: \"ok\",\n detail: `${tools.length} tool${tools.length === 1 ? \"\" : \"s\"} available`,\n });\n\n return {\n success: true,\n error: null,\n toolCount: tools.length,\n toolNames,\n steps,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n steps.push({ step: \"Discover tools\", status: \"error\", detail: message });\n\n return {\n success: false,\n error: message,\n toolCount: 0,\n toolNames: [],\n steps,\n };\n }\n}\n","import {\n CONFIG_KEYS,\n OAUTH_PROVIDER_DEFINITIONS,\n} from \"../../config/config.js\";\nimport { decryptValue, encryptValue } from \"../../security/crypto.js\";\nimport { getConfig } from \"../../db/getConfig.js\";\nimport { setConfig } from \"../../db/setConfig.js\";\nimport { getRemoteMcpConnection } from \"./mcp-connections.js\";\nimport { clearRemoteMcpSession } from \"./remote-mcp-client.js\";\n\nimport type { ServiceAccount } from \"../accounts/service-accounts.js\";\nimport type { RemoteMcpCredentialRef } from \"./mcp-connections.js\";\nimport type { GitAccount } from \"../git/types.js\";\nimport type {\n OAuthAccountLike,\n ReconnectMcpOAuthOptions,\n ReconnectMcpOAuthResult,\n} from \"./types.js\";\nimport type { ShadowClawDatabase } from \"../../db/types.js\";\n\n/** Helper to build account update fields from OAuth token payload. */\nfunction buildOAuthUpdateFields(\n payload: {\n accessToken: string;\n refreshToken?: string;\n expiresIn?: number;\n tokenType?: string;\n scope?: string;\n },\n existingAccount: OAuthAccountLike,\n encryptedAccessToken: string,\n encryptedRefreshToken: string | undefined,\n) {\n return {\n accessTokenExpiresAt: payload.expiresIn\n ? Date.now() + payload.expiresIn * 1000\n : existingAccount.accessTokenExpiresAt,\n oauthReauthRequired: false,\n oauthReauthRequiredAt: undefined,\n oauthRefreshFailureCount: 0,\n refreshToken: encryptedRefreshToken,\n scopes: payload.scope\n ? payload.scope\n .split(/[\\s,]+/)\n .map((s) => s.trim())\n .filter(Boolean)\n : existingAccount.scopes,\n token: encryptedAccessToken,\n tokenType: payload.tokenType || existingAccount.tokenType,\n };\n}\n\n/**\n * Try a silent OAuth token refresh using the existing refresh token.\n * Returns `true` if the token was successfully refreshed and persisted.\n */\nasync function trySilentRefresh(\n db: ShadowClawDatabase,\n account: OAuthAccountLike,\n accountStore: \"service\" | \"git\",\n serviceAccounts: ServiceAccount[],\n gitAccounts: GitAccount[],\n ref: RemoteMcpCredentialRef,\n connectionId: string,\n): Promise<ReconnectMcpOAuthResult> {\n if (!account.refreshToken) {\n return { success: false, error: \"No refresh token available.\" };\n }\n\n const decryptedRefreshToken = await decryptValue(account.refreshToken);\n if (!decryptedRefreshToken) {\n return { success: false, error: \"Failed to decrypt refresh token.\" };\n }\n\n const clientSecret = account.oauthClientSecret\n ? await decryptValue(account.oauthClientSecret)\n : undefined;\n\n try {\n const response = await fetch(\"/oauth/refresh\", {\n body: JSON.stringify({\n clientId: account.oauthClientId,\n clientSecret: clientSecret || undefined,\n providerId: account.oauthProviderId,\n refreshToken: decryptedRefreshToken,\n scope: account.scopes,\n tokenUrl: account.oauthCustomTokenUrl,\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\",\n });\n\n if (!response.ok) {\n return { success: false, error: \"Token refresh request failed.\" };\n }\n\n const payload = (await response.json()) as {\n accessToken?: string;\n expiresIn?: number;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n };\n\n if (!payload.accessToken) {\n return { success: false, error: \"No access token in refresh response.\" };\n }\n\n const encryptedAccessToken = await encryptValue(payload.accessToken);\n if (!encryptedAccessToken) {\n return { success: false, error: \"Failed to encrypt access token.\" };\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (payload.refreshToken) {\n const maybeEncrypted = await encryptValue(payload.refreshToken);\n if (maybeEncrypted) {\n encryptedRefreshToken = maybeEncrypted;\n }\n }\n\n const updatedFields = buildOAuthUpdateFields(\n {\n accessToken: payload.accessToken,\n expiresIn: payload.expiresIn,\n refreshToken: payload.refreshToken,\n scope: payload.scope,\n tokenType: payload.tokenType,\n },\n account,\n encryptedAccessToken,\n encryptedRefreshToken,\n );\n\n if (accountStore === \"service\" && ref.accountId) {\n const idx = serviceAccounts.findIndex(\n (acct) => acct.id === ref.accountId,\n );\n\n if (idx !== -1) {\n Object.assign(serviceAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS, serviceAccounts);\n }\n } else if (accountStore === \"git\" && ref.gitAccountId) {\n const idx = gitAccounts.findIndex((acct) => acct.id === ref.gitAccountId);\n if (idx !== -1) {\n Object.assign(gitAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.GIT_ACCOUNTS, gitAccounts);\n }\n }\n\n clearRemoteMcpSession(connectionId);\n\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n\n return { success: false, error: `Silent refresh failed: ${message}` };\n }\n}\n\n/**\n * Performs OAuth re-authentication for a remote MCP connection.\n *\n * When `silentOnly` is true, only attempts a token refresh using the\n * existing refresh token — no popup is opened. This is used for\n * automatic reconnection from the orchestrator (no user gesture).\n *\n * When `silentOnly` is false (default), first tries a silent refresh,\n * and if that fails, opens a popup for full re-authorization.\n */\nexport async function reconnectMcpOAuth(\n db: ShadowClawDatabase,\n connectionId: string,\n options: ReconnectMcpOAuthOptions = {},\n): Promise<ReconnectMcpOAuthResult> {\n const connection = await getRemoteMcpConnection(db, connectionId);\n if (!connection?.credentialRef) {\n return { success: false, error: \"Connection not found.\" };\n }\n\n const ref = connection.credentialRef;\n if (ref.authType !== \"oauth\") {\n return {\n success: false,\n error: \"This connection does not use OAuth authentication.\",\n };\n }\n\n // Load accounts from DB\n const serviceRaw = await getConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS);\n const serviceAccounts: ServiceAccount[] = Array.isArray(serviceRaw)\n ? serviceRaw\n : [];\n\n const gitRaw = await getConfig(db, CONFIG_KEYS.GIT_ACCOUNTS);\n const gitAccounts: GitAccount[] = Array.isArray(gitRaw) ? gitRaw : [];\n\n // Find the linked account\n let account: OAuthAccountLike | undefined;\n let accountStore: \"service\" | \"git\" | null = null;\n\n if (ref.accountId) {\n account = serviceAccounts.find((acct) => acct.id === ref.accountId) as\n | OAuthAccountLike\n | undefined;\n\n accountStore = \"service\";\n } else if (ref.gitAccountId) {\n account = gitAccounts.find((acct) => acct.id === ref.gitAccountId) as\n | OAuthAccountLike\n | undefined;\n\n accountStore = \"git\";\n }\n\n if (!account || !accountStore) {\n return {\n error:\n \"Linked account not found. Edit the connection to re-configure authentication.\",\n success: false,\n };\n }\n\n const clientId = account.oauthClientId;\n const providerId = account.oauthProviderId;\n\n if (!providerId || !clientId) {\n return {\n error:\n \"Account is missing OAuth provider or client ID. Edit the account in Settings → Accounts to fix.\",\n success: false,\n };\n }\n\n // ── Silent refresh attempt ───────────────────────────────────────\n // Always try a silent token refresh first. This works without a\n // popup and handles the common case where the refresh token is\n // still valid but the access token expired.\n if (account.refreshToken) {\n const silentResult = await trySilentRefresh(\n db,\n account,\n accountStore,\n serviceAccounts,\n gitAccounts,\n ref,\n connectionId,\n );\n\n if (silentResult.success) {\n return silentResult;\n }\n }\n\n // If silentOnly was requested, don't open a popup.\n if (options.silentOnly) {\n return {\n error:\n \"Silent token refresh failed. Use Settings → Remote MCP → Reconnect OAuth for full re-authorization.\",\n success: false,\n };\n }\n\n const clientSecret = account.oauthClientSecret\n ? await decryptValue(account.oauthClientSecret)\n : undefined;\n\n const scope = account.scopes;\n\n // Resolve OAuth URLs\n const providerDef = OAUTH_PROVIDER_DEFINITIONS[providerId];\n const isCustomMcp = providerId === \"custom_mcp\";\n const redirectUri =\n isCustomMcp && account.oauthCustomRedirectUri\n ? account.oauthCustomRedirectUri\n : providerDef?.redirectUri\n ? providerDef.redirectUri\n : `${globalThis.location?.origin || \"\"}/oauth/callback`;\n\n try {\n const authorizeRes = await fetch(\"/oauth/authorize\", {\n body: JSON.stringify({\n providerId,\n clientId,\n clientSecret: clientSecret || undefined,\n redirectUri,\n scope,\n ...(isCustomMcp && account.oauthCustomAuthorizeUrl\n ? { authorizeUrl: account.oauthCustomAuthorizeUrl }\n : {}),\n ...(isCustomMcp && account.oauthCustomTokenUrl\n ? { tokenUrl: account.oauthCustomTokenUrl }\n : {}),\n ...(isCustomMcp && typeof account.oauthCustomUsePkce === \"boolean\"\n ? { usePkce: account.oauthCustomUsePkce }\n : {}),\n }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\",\n });\n\n const authorizePayload = (await authorizeRes.json()) as {\n authorizeUrl?: string;\n error?: string;\n state?: string;\n };\n\n if (\n !authorizeRes.ok ||\n !authorizePayload.state ||\n !authorizePayload.authorizeUrl\n ) {\n throw new Error(authorizePayload.error || \"OAuth authorize failed\");\n }\n\n const popup = window.open(\n authorizePayload.authorizeUrl,\n \"shadowclaw-oauth\",\n \"popup=yes,width=540,height=720\",\n );\n\n if (!popup || popup.closed || typeof popup.closed === \"undefined\") {\n throw new Error(\n \"OAuth popup was blocked by the browser. Please enable popups for this site and try again.\",\n );\n }\n\n const state = authorizePayload.state;\n let status = \"pending\";\n for (let attempt = 0; attempt < 60; attempt++) {\n const sessionRes = await fetch(\n `/oauth/session/${encodeURIComponent(state)}`,\n );\n\n const sessionPayload = (await sessionRes.json()) as {\n error?: string;\n status?: string;\n };\n\n if (!sessionRes.ok) {\n throw new Error(sessionPayload.error || \"OAuth session not found\");\n }\n\n status = sessionPayload.status || \"pending\";\n\n if (status === \"authorized\") {\n break;\n }\n\n if (status === \"error\") {\n throw new Error(sessionPayload.error || \"OAuth authorization failed\");\n }\n\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n\n if (status !== \"authorized\") {\n throw new Error(\"OAuth authorization timed out\");\n }\n\n const tokenRes = await fetch(\"/oauth/token\", {\n body: JSON.stringify({ state }),\n headers: { \"content-type\": \"application/json\" },\n method: \"POST\",\n });\n\n const tokenPayload = (await tokenRes.json()) as {\n accessToken?: string;\n error?: string;\n expiresIn?: number;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n };\n\n if (!tokenRes.ok || !tokenPayload.accessToken) {\n throw new Error(tokenPayload.error || \"OAuth token exchange failed\");\n }\n\n // Update the linked account with new tokens\n const encryptedAccessToken = await encryptValue(tokenPayload.accessToken);\n if (!encryptedAccessToken) {\n throw new Error(\"Failed to encrypt access token\");\n }\n\n let encryptedRefreshToken = account.refreshToken;\n if (tokenPayload.refreshToken) {\n const maybeEncrypted = await encryptValue(tokenPayload.refreshToken);\n if (maybeEncrypted) {\n encryptedRefreshToken = maybeEncrypted;\n }\n }\n\n const updatedFields = buildOAuthUpdateFields(\n {\n accessToken: tokenPayload.accessToken,\n expiresIn: tokenPayload.expiresIn,\n refreshToken: tokenPayload.refreshToken,\n scope: tokenPayload.scope,\n tokenType: tokenPayload.tokenType,\n },\n account,\n encryptedAccessToken,\n encryptedRefreshToken,\n );\n\n // Persist to the correct account store\n if (accountStore === \"service\" && ref.accountId) {\n const idx = serviceAccounts.findIndex(\n (acct) => acct.id === ref.accountId,\n );\n if (idx !== -1) {\n Object.assign(serviceAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.SERVICE_ACCOUNTS, serviceAccounts);\n }\n } else if (accountStore === \"git\" && ref.gitAccountId) {\n const idx = gitAccounts.findIndex((acct) => acct.id === ref.gitAccountId);\n if (idx !== -1) {\n Object.assign(gitAccounts[idx], updatedFields);\n\n await setConfig(db, CONFIG_KEYS.GIT_ACCOUNTS, gitAccounts);\n }\n }\n\n // Clear the MCP session so the next call uses fresh credentials\n clearRemoteMcpSession(connectionId);\n\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n\n return { success: false, error: message };\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-DZ8SGSJo.js";
2
- import { x as OPFS_ROOT } from "./config-DjZ9QWc4.js";
2
+ import { x as OPFS_ROOT } from "./config-BcFbkaQv.js";
3
3
 
4
4
  //#region src/storage/memoryStorage.ts
5
5
  var memoryStorage_exports = /* @__PURE__ */ __exportAll({
@@ -349,4 +349,4 @@ function isMemoryStorageFallbackActive() {
349
349
 
350
350
  //#endregion
351
351
  export { memoryStorage_exports as n, getMemoryOpfsRoot as t };
352
- //# sourceMappingURL=memoryStorage-D3FBwO9T.js.map
352
+ //# sourceMappingURL=memoryStorage-DcFWrn_y.js.map