@vantran-se/openchamber-web 1.23.1 → 1.24.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/bin/cli.test.js +29 -6
  2. package/bin/lib/cli-http.js +5 -1
  3. package/bin/lib/commands-status.js +5 -0
  4. package/dist/assets/{CodeMirrorCommentWidgets-BOpqXpFU.js → CodeMirrorCommentWidgets-B4a0JkUz.js} +1 -1
  5. package/dist/assets/DiffView-B2CD9wC8.js +4 -0
  6. package/dist/assets/FilesView-C4vMFclc.js +13 -0
  7. package/dist/assets/GitView-Byu2DY7s.js +6 -0
  8. package/dist/assets/GuestAttachDialog-B808E0-d.js +1 -0
  9. package/dist/assets/{JsonTreeView-nd_E8eVM.js → JsonTreeView-VW_7czo3.js} +1 -1
  10. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  11. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  12. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  13. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  14. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  15. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  16. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  17. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  18. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  19. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  20. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  21. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  22. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  23. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  24. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  25. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  26. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  28. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  29. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  30. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  31. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  32. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  33. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  34. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  35. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  36. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  37. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  38. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  39. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  40. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  41. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  42. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  45. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  48. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  49. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  50. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  51. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  52. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  53. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  54. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  55. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  56. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  57. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  58. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  59. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  60. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  61. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  62. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  63. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  64. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  65. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  66. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  67. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  68. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  69. package/dist/assets/LinearIssuesView-EzGNCGrd.js +1 -0
  70. package/dist/assets/{MarkdownImageGallery-CVYds3sQ.js → MarkdownImageGallery-B2YUDFaB.js} +1 -1
  71. package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +12 -0
  72. package/dist/assets/{OnboardingScreen-Dd2IbPTn.js → OnboardingScreen-CQHBSTEi.js} +1 -1
  73. package/dist/assets/{PatchDiff-nUu5i4bG.js → PatchDiff-DCjGhlYV.js} +1 -1
  74. package/dist/assets/PierreDiffViewer-CEDZNkU2.js +210 -0
  75. package/dist/assets/PlanView-D3x-pTkZ.js +5 -0
  76. package/dist/assets/PluginPane-DEcgDQ9h.js +3 -0
  77. package/dist/assets/SettingsView-CSinuH-1.js +29 -0
  78. package/dist/assets/{SettingsWindow-BccxY81J.js → SettingsWindow-B5CHoJFZ.js} +1 -1
  79. package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +1 -0
  80. package/dist/assets/ToolOutputDialog-tLNWmfcW.js +15 -0
  81. package/dist/assets/{ToolPartDiffPreview-BFVgk0UK.js → ToolPartDiffPreview-CflzmK-x.js} +2 -2
  82. package/dist/assets/{VirtualizedFileDiff-z3_H74UR.js → VirtualizedFileDiff-NU0FHFhT.js} +3 -3
  83. package/dist/assets/{WalkthroughView-BqM9GFbJ.js → WalkthroughView-BZyjhqHv.js} +2 -2
  84. package/dist/assets/appThemeRegistry-DLicw4dH.js +1 -0
  85. package/dist/assets/commitAndPush-BDftrjr6.js +1 -0
  86. package/dist/assets/{computeVirtualFileMetrics-Bdazp9Qo.js → computeVirtualFileMetrics-DlWlEM3x.js} +1 -1
  87. package/dist/assets/de-Bro6_zyl.js +10 -0
  88. package/dist/assets/es-Bb9XQZvd.js +16 -0
  89. package/dist/assets/fr-Dgfzn-IL.js +14 -0
  90. package/dist/assets/{index-eo1-D_7z.js → index-CtQNmqUL.js} +1 -1
  91. package/dist/assets/index-DqaMWNrC.css +1 -0
  92. package/dist/assets/index-Dqc6USl3.js +40 -0
  93. package/dist/assets/{isDiffPlainText-BvKH712-.js → isDiffPlainText-CO4e5J_S.js} +1 -1
  94. package/dist/assets/{isFilePlainText-BdVVPukf.js → isFilePlainText-ue6kCou7.js} +1 -1
  95. package/dist/assets/ja-BYsakGFh.js +16 -0
  96. package/dist/assets/ko-BSaQz9aN.js +16 -0
  97. package/dist/assets/main-B62_pjL5.js +2 -0
  98. package/dist/assets/main-BEdcOI2P.js +896 -0
  99. package/dist/assets/{markdown-shiki.worker-D5fBU-lw.js → markdown-shiki.worker-CahLvHIq.js} +5 -5
  100. package/dist/assets/markdownCore-CK48LG0z.js +5 -0
  101. package/dist/assets/miniChat-LT8Rqofr.js +2 -0
  102. package/dist/assets/mobile-CqarEynh.js +2 -0
  103. package/dist/assets/{patchFileDiff-B7LjppSS.js → patchFileDiff-D7QVlovD.js} +3 -3
  104. package/dist/assets/pl-OIviPdoj.js +16 -0
  105. package/dist/assets/pt-BR-Cltg-E6s.js +16 -0
  106. package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +2 -0
  107. package/dist/assets/renderMobileApp-BC1MHg6g.js +25 -0
  108. package/dist/assets/runtimeEndpointReset-DdrazOVH.js +25 -0
  109. package/dist/assets/{scheduledTasksApi-hceovDei.js → scheduledTasksApi-B2SWMGEU.js} +1 -1
  110. package/dist/assets/shikiHighlight-BPu3JiQs.js +1 -0
  111. package/dist/assets/textMateThemeFromAppTheme-DWqRPo9A.js +1 -0
  112. package/dist/assets/tr-CQI-KPre.js +7 -0
  113. package/dist/assets/uk-CqW7R4DA.js +16 -0
  114. package/dist/assets/useAppFontEffects-ne84JBVL.js +7333 -0
  115. package/dist/assets/useInlineCommentController-CgS0fnw_.js +2 -0
  116. package/dist/assets/useRootScrollLock-Cpf1qoGp.js +2 -0
  117. package/dist/assets/{vendor-base-ui-BY_5qla_.js → vendor-base-ui-CsXYnR5c.js} +1 -1
  118. package/dist/assets/{vendor-codemirror-language-data-DWA1bm1F.js → vendor-codemirror-language-data-Bx2EHc1O.js} +2 -2
  119. package/dist/assets/vendor-framer-motion-Bq3iTZ6r.js +1 -0
  120. package/dist/assets/vendor-katex-Cp579UoJ.css +1 -0
  121. package/dist/assets/{vendor-motion-dom-D_o3BTI3.js → vendor-motion-dom-DcCgeG69.js} +1 -1
  122. package/dist/assets/{vendor-remixicon-react-Dil0zKKT.js → vendor-remixicon-react-BDmGmFCA.js} +1 -1
  123. package/dist/assets/vendor-strip-json-comments-5dJlOkP1.js +4 -0
  124. package/dist/assets/{vendor-tanstack-react-virtual-g05Cx3z9.js → vendor-tanstack-react-virtual-ClYN8CzB.js} +1 -1
  125. package/dist/assets/{vendor-tanstack-virtual-core-Dubg10g5.js → vendor-tanstack-virtual-core-BewVQryB.js} +1 -1
  126. package/dist/assets/{vendor-zod-B6551dwG.js → vendor-zod-CgAdXWZO.js} +4 -4
  127. package/dist/assets/{web-components-B_Z64So5.js → web-components-DxX8bZ1o.js} +2 -2
  128. package/dist/assets/zh-CN-CVZG_o_J.js +16 -0
  129. package/dist/assets/zh-TW-D5ZxzCZp.js +16 -0
  130. package/dist/index.html +6 -4
  131. package/dist/mini-chat.html +6 -4
  132. package/dist/mobile.html +6 -4
  133. package/package.json +12 -9
  134. package/server/built-in-extensions/registry.json +4 -0
  135. package/server/index.d.ts +2 -0
  136. package/server/index.js +44 -10
  137. package/server/lib/agent-tool/DOCUMENTATION.md +17 -4
  138. package/server/lib/agent-tool/runtime.js +62 -15
  139. package/server/lib/agent-tool/runtime.test.js +88 -2
  140. package/server/lib/changelog/update-notes.js +2 -1
  141. package/server/lib/changelog/update-notes.test.js +19 -0
  142. package/server/lib/event-stream/DOCUMENTATION.md +23 -0
  143. package/server/lib/event-stream/delta-coalescer.js +0 -0
  144. package/server/lib/event-stream/delta-coalescer.test.js +292 -0
  145. package/server/lib/event-stream/global-hub.js +53 -23
  146. package/server/lib/event-stream/global-hub.test.js +160 -0
  147. package/server/lib/event-stream/global-ws-bridge.js +1 -0
  148. package/server/lib/event-stream/index.js +4 -0
  149. package/server/lib/fs/DOCUMENTATION.md +10 -1
  150. package/server/lib/fs/git-process.test.js +77 -0
  151. package/server/lib/fs/routes.js +58 -5
  152. package/server/lib/fs/routes.test.js +116 -0
  153. package/server/lib/fs/search.js +2 -1
  154. package/server/lib/git/DOCUMENTATION.md +20 -5
  155. package/server/lib/git/routes.js +16 -3
  156. package/server/lib/git/serial-refresh.js +93 -0
  157. package/server/lib/git/serial-refresh.test.js +112 -0
  158. package/server/lib/git/service.js +429 -70
  159. package/server/lib/git/service.test.js +418 -0
  160. package/server/lib/github/pr-status.js +5 -5
  161. package/server/lib/github/pr-status.test.js +1 -1
  162. package/server/lib/github/routes.js +4 -4
  163. package/server/lib/guests/DOCUMENTATION.md +68 -0
  164. package/server/lib/guests/auth-store.js +184 -0
  165. package/server/lib/guests/auth-store.test.js +112 -0
  166. package/server/lib/guests/background.test.js +83 -0
  167. package/server/lib/guests/builtins.js +34 -0
  168. package/server/lib/guests/builtins.test.js +174 -0
  169. package/server/lib/guests/catalog.js +477 -0
  170. package/server/lib/guests/catalog.test.js +390 -0
  171. package/server/lib/guests/clone.js +297 -0
  172. package/server/lib/guests/extract-zip.js +136 -0
  173. package/server/lib/guests/extract-zip.test.js +109 -0
  174. package/server/lib/guests/files.js +308 -0
  175. package/server/lib/guests/files.test.js +262 -0
  176. package/server/lib/guests/grant-scope.js +81 -0
  177. package/server/lib/guests/grant-scope.test.js +75 -0
  178. package/server/lib/guests/host-session.js +73 -0
  179. package/server/lib/guests/host-session.test.js +30 -0
  180. package/server/lib/guests/html-styles.js +6 -0
  181. package/server/lib/guests/html-styles.test.js +45 -0
  182. package/server/lib/guests/html-tokens.js +73 -0
  183. package/server/lib/guests/html-tokens.test.js +53 -0
  184. package/server/lib/guests/install.js +406 -0
  185. package/server/lib/guests/install.test.js +338 -0
  186. package/server/lib/guests/oauth.js +501 -0
  187. package/server/lib/guests/oauth.test.js +481 -0
  188. package/server/lib/guests/persist.js +336 -0
  189. package/server/lib/guests/persist.test.js +288 -0
  190. package/server/lib/guests/request.js +112 -0
  191. package/server/lib/guests/request.test.js +206 -0
  192. package/server/lib/guests/routes.js +838 -0
  193. package/server/lib/guests/service.js +538 -0
  194. package/server/lib/guests/service.test.js +277 -0
  195. package/server/lib/guests/sockets.js +129 -0
  196. package/server/lib/guests/sockets.test.js +110 -0
  197. package/server/lib/guests/ssh-install.test.js +188 -0
  198. package/server/lib/guests/storage.js +68 -0
  199. package/server/lib/guests/storage.test.js +59 -0
  200. package/server/lib/guests/updates.js +294 -0
  201. package/server/lib/guests/updates.test.js +334 -0
  202. package/server/lib/guests/upload.js +50 -0
  203. package/server/lib/guests/upload.test.js +131 -0
  204. package/server/lib/linear/DOCUMENTATION.md +1 -1
  205. package/server/lib/message-queue/DOCUMENTATION.md +10 -1
  206. package/server/lib/message-queue/runtime.js +21 -1
  207. package/server/lib/message-queue/runtime.test.js +52 -5
  208. package/server/lib/notifications/routes.js +5 -0
  209. package/server/lib/openchamber-control/DOCUMENTATION.md +6 -0
  210. package/server/lib/openchamber-sessions/routes.js +62 -29
  211. package/server/lib/openchamber-sessions/routes.test.js +96 -0
  212. package/server/lib/opencode/DOCUMENTATION.md +55 -1
  213. package/server/lib/opencode/bootstrap-runtime.js +1 -0
  214. package/server/lib/opencode/core-routes.js +39 -1
  215. package/server/lib/opencode/feature-routes-runtime.js +15 -0
  216. package/server/lib/opencode/lifecycle-process.test.js +84 -0
  217. package/server/lib/opencode/lifecycle.js +54 -67
  218. package/server/lib/opencode/openchamber-routes.js +72 -14
  219. package/server/lib/opencode/openchamber-routes.test.js +71 -3
  220. package/server/lib/opencode/session-runtime.js +68 -0
  221. package/server/lib/opencode/session-runtime.test.js +43 -0
  222. package/server/lib/opencode/settings-helpers.js +8 -0
  223. package/server/lib/opencode/settings-helpers.test.js +13 -1
  224. package/server/lib/opencode/settings-normalization-runtime.js +2 -0
  225. package/server/lib/opencode/settings-normalization-runtime.test.js +13 -0
  226. package/server/lib/opencode/settings-registry.json +15 -0
  227. package/server/lib/opencode/shutdown-runtime.js +3 -0
  228. package/server/lib/opencode/shutdown-runtime.test.js +24 -0
  229. package/server/lib/opencode/theme-archive.js +53 -0
  230. package/server/lib/opencode/theme-catalog.js +171 -0
  231. package/server/lib/opencode/theme-catalog.test.js +125 -0
  232. package/server/lib/opencode/theme-import.test.js +144 -0
  233. package/server/lib/opencode/theme-runtime.js +100 -28
  234. package/server/lib/opencode/theme-runtime.test.js +15 -0
  235. package/server/lib/permission-auto-accept/DOCUMENTATION.md +8 -0
  236. package/server/lib/permission-auto-accept/runtime.js +13 -0
  237. package/server/lib/permission-auto-accept/runtime.test.js +37 -1
  238. package/server/lib/projects/project-config.js +2 -2
  239. package/server/lib/quota/DOCUMENTATION.md +0 -1
  240. package/server/lib/quota/providers/index.js +0 -7
  241. package/server/lib/realtime-proxy-terminal.test.js +150 -0
  242. package/server/lib/realtime-proxy.js +2 -0
  243. package/server/lib/routing/DOCUMENTATION.md +83 -0
  244. package/server/lib/routing/defaults.js +98 -0
  245. package/server/lib/routing/feature-flag.js +11 -0
  246. package/server/lib/routing/history.js +34 -0
  247. package/server/lib/routing/jev.js +98 -0
  248. package/server/lib/routing/routes.http.test.js +100 -0
  249. package/server/lib/routing/routes.js +81 -0
  250. package/server/lib/routing/runtime.js +217 -0
  251. package/server/lib/routing/runtime.test.js +188 -0
  252. package/server/lib/routing/store.js +249 -0
  253. package/server/lib/routing/store.test.js +96 -0
  254. package/server/lib/scheduled-tasks/runtime.js +2 -2
  255. package/server/lib/scheduled-tasks/runtime.test.js +14 -0
  256. package/server/lib/security/request-security.js +9 -5
  257. package/server/lib/security/request-security.test.js +37 -0
  258. package/server/lib/small-model/DOCUMENTATION.md +14 -2
  259. package/server/lib/small-model/call.js +37 -6
  260. package/server/lib/small-model/call.test.js +182 -0
  261. package/server/lib/terminal/DOCUMENTATION.md +23 -2
  262. package/server/lib/terminal/runtime.js +27 -5
  263. package/server/lib/terminal/runtime.test.js +67 -0
  264. package/server/lib/terminal/shutdown.js +106 -0
  265. package/server/lib/terminal/shutdown.test.js +84 -0
  266. package/server/lib/tts/service.test.js +78 -0
  267. package/server/lib/ui-auth/DOCUMENTATION.md +8 -0
  268. package/server/lib/ui-auth/session-cookie.js +51 -0
  269. package/server/lib/ui-auth/session-cookie.test.js +46 -0
  270. package/server/lib/ui-auth/ui-auth.js +56 -17
  271. package/server/lib/ui-auth/ui-auth.test.js +96 -0
  272. package/dist/assets/DiffView-CyLmb-wF.js +0 -4
  273. package/dist/assets/DirtyBranchSwitchDialog-pu5kcoir.js +0 -1
  274. package/dist/assets/FilesView-WtJB0y_l.js +0 -13
  275. package/dist/assets/GitView-DkNrV0BK.js +0 -6
  276. package/dist/assets/LinearIssuesView-B0H0Otii.js +0 -1
  277. package/dist/assets/MarkdownRendererImpl-DSqVN2d8.js +0 -12
  278. package/dist/assets/PierreDiffViewer-Bsq9z-sw.js +0 -161
  279. package/dist/assets/PlanView-CAFziKH5.js +0 -5
  280. package/dist/assets/SettingsView-BRittavZ.js +0 -28
  281. package/dist/assets/ToolOutputDialog-B0uYaJio.js +0 -15
  282. package/dist/assets/appThemeRegistry-BK0dHKa6.js +0 -1
  283. package/dist/assets/de-c7CYLCAW.js +0 -10
  284. package/dist/assets/es-BKAVPQmQ.js +0 -16
  285. package/dist/assets/fr-Bj1glqfV.js +0 -14
  286. package/dist/assets/index-BYFyOu_D.css +0 -1
  287. package/dist/assets/index-BfwRClDe.js +0 -40
  288. package/dist/assets/ja-5jwSIbK0.js +0 -16
  289. package/dist/assets/ko-BaDbg-Eb.js +0 -16
  290. package/dist/assets/main-CGG2_uHq.js +0 -2
  291. package/dist/assets/main-CcrSaL4J.js +0 -930
  292. package/dist/assets/markdownCore-DsmQr3bt.js +0 -5
  293. package/dist/assets/miniChat-DEaAhP4b.js +0 -2
  294. package/dist/assets/mobile-BC3rsvv0.js +0 -2
  295. package/dist/assets/pl-BFKVBDFu.js +0 -16
  296. package/dist/assets/pt-BR-qKfHvkDL.js +0 -16
  297. package/dist/assets/registerCustomTheme-BBXUCRnG.js +0 -1
  298. package/dist/assets/renderElectronMiniChatApp-CR9d1Mr0.js +0 -2
  299. package/dist/assets/renderMobileApp-Da5dwobZ.js +0 -25
  300. package/dist/assets/runtimeEndpointReset-CLYVRw2H.js +0 -41
  301. package/dist/assets/shikiHighlight-D1Z9G58W.js +0 -1
  302. package/dist/assets/tr-DYXPoEDG.js +0 -7
  303. package/dist/assets/uk-CwAoloR8.js +0 -16
  304. package/dist/assets/useAppFontEffects-DeStGcUT.js +0 -7282
  305. package/dist/assets/useGitComparison-CpxioduU.js +0 -1
  306. package/dist/assets/useInlineCommentController-gmUnJtrb.js +0 -2
  307. package/dist/assets/useRootScrollLock--a3cPz8s.js +0 -1
  308. package/dist/assets/vendor-framer-motion-livSKyyV.js +0 -1
  309. package/dist/assets/zh-CN-D9MG0lqF.js +0 -16
  310. package/dist/assets/zh-TW-1HNK7shh.js +0 -16
  311. package/server/lib/quota/providers/crof.js +0 -96
  312. package/server/lib/quota/providers/crof.test.js +0 -98
  313. /package/dist/assets/{vendor-katex-k3Ppq1Ha.js → vendor-katex-CHD9BdDO.js} +0 -0
@@ -0,0 +1,481 @@
1
+ import { afterEach, describe, expect, test } from 'bun:test';
2
+ import fs from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+
6
+ import { getGuestAuth, guestAuthPersistPath, patchGuestAuth } from './auth-store.js';
7
+ import {
8
+ clearGuestPendingForTests,
9
+ consumeGuestAuthorization,
10
+ credentialTarget,
11
+ createPkcePair,
12
+ encodeBasicCredential,
13
+ guestAuthorizationHeader,
14
+ guestRedirectUri,
15
+ saveGuestAccessToken,
16
+ saveGuestOAuthClient,
17
+ refreshGuestAccessToken,
18
+ startGuestAuthorization,
19
+ storedTokensUsable,
20
+ toPublicGuestAuth,
21
+ } from './oauth.js';
22
+
23
+ const clickupGuest = {
24
+ id: 'clickup',
25
+ integration: {
26
+ name: 'ClickUp',
27
+ description: 'Tasks',
28
+ oauth: {
29
+ authorizeUrl: 'https://app.clickup.com/api',
30
+ tokenUrl: 'https://api.clickup.com/api/v2/oauth/token',
31
+ apiOrigin: 'https://api.clickup.com',
32
+ account: { path: '/api/v2/user', name: 'user.username' },
33
+ },
34
+ },
35
+ };
36
+
37
+ afterEach(() => {
38
+ clearGuestPendingForTests();
39
+ });
40
+
41
+ describe('guestRedirectUri', () => {
42
+ test('stays on the server origin', () => {
43
+ expect(guestRedirectUri('http://127.0.0.1:4096', 'clickup'))
44
+ .toBe('http://127.0.0.1:4096/api/guests/clickup/oauth/callback');
45
+ });
46
+ });
47
+
48
+ describe('toPublicGuestAuth', () => {
49
+ test('never forwards tokens', () => {
50
+ expect(toPublicGuestAuth({
51
+ clientId: 'id',
52
+ clientSecret: 'secret',
53
+ accessToken: 'tok',
54
+ account: 'ada',
55
+ settings: { 'list-id': '1' },
56
+ })).toEqual({
57
+ connected: true,
58
+ account: 'ada',
59
+ hasClient: true,
60
+ settings: { 'list-id': '1' },
61
+ });
62
+ });
63
+ });
64
+
65
+ describe('startGuestAuthorization', () => {
66
+ test('builds a PKCE authorize URL after a client id is saved', async () => {
67
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
68
+ const persistPath = guestAuthPersistPath(dir);
69
+ await patchGuestAuth('clickup', { clientId: 'app-id', clientSecret: 'app-secret', clientTarget: credentialTarget(clickupGuest.integration) }, persistPath);
70
+ const started = await startGuestAuthorization({
71
+ guest: clickupGuest,
72
+ persistPath,
73
+ origin: 'http://127.0.0.1:4096',
74
+ });
75
+ const url = new URL(started.authorizationUrl);
76
+ expect(url.origin + url.pathname).toBe('https://app.clickup.com/api');
77
+ expect(url.searchParams.get('client_id')).toBe('app-id');
78
+ expect(url.searchParams.get('redirect_uri')).toBe('http://127.0.0.1:4096/api/guests/clickup/oauth/callback');
79
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
80
+ expect(url.searchParams.get('state')).toBeTruthy();
81
+ await fs.rm(dir, { recursive: true, force: true });
82
+ });
83
+
84
+ test('refuses start without a client id', async () => {
85
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
86
+ const persistPath = guestAuthPersistPath(dir);
87
+ try {
88
+ await startGuestAuthorization({
89
+ guest: clickupGuest,
90
+ persistPath,
91
+ origin: 'http://127.0.0.1:4096',
92
+ });
93
+ throw new Error('should have thrown');
94
+ } catch (error) {
95
+ expect(error.code).toBe('CLIENT_MISSING');
96
+ }
97
+ await fs.rm(dir, { recursive: true, force: true });
98
+ });
99
+ });
100
+
101
+ describe('consumeGuestAuthorization', () => {
102
+ test('stores tokens from the token endpoint and never returns them', async () => {
103
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
104
+ const persistPath = guestAuthPersistPath(dir);
105
+ await patchGuestAuth('clickup', { clientId: 'app-id', clientSecret: 'app-secret', clientTarget: credentialTarget(clickupGuest.integration) }, persistPath);
106
+ const started = await startGuestAuthorization({
107
+ guest: clickupGuest,
108
+ persistPath,
109
+ origin: 'http://127.0.0.1:4096',
110
+ });
111
+ const state = new URL(started.authorizationUrl).searchParams.get('state');
112
+ const originalFetch = globalThis.fetch;
113
+ globalThis.fetch = async (url) => {
114
+ const target = String(url);
115
+ if (target === 'https://api.clickup.com/api/v2/oauth/token') {
116
+ return new Response(JSON.stringify({
117
+ access_token: 'access-1',
118
+ refresh_token: 'refresh-1',
119
+ token_type: 'Bearer',
120
+ expires_in: 3600,
121
+ }), { status: 200, headers: { 'Content-Type': 'application/json' } });
122
+ }
123
+ if (target === 'https://api.clickup.com/api/v2/user') {
124
+ return new Response(JSON.stringify({ user: { username: 'ada' } }), {
125
+ status: 200,
126
+ headers: { 'Content-Type': 'application/json' },
127
+ });
128
+ }
129
+ throw new Error(`unexpected fetch ${target}`);
130
+ };
131
+ try {
132
+ const result = await consumeGuestAuthorization({
133
+ guest: clickupGuest,
134
+ persistPath,
135
+ code: 'auth-code',
136
+ state,
137
+ });
138
+ expect(result).toEqual({ connected: true, account: 'ada' });
139
+ const stored = await getGuestAuth('clickup', persistPath);
140
+ expect(stored.accessToken).toBe('access-1');
141
+ expect(toPublicGuestAuth(stored).accessToken).toBeUndefined();
142
+ } finally {
143
+ globalThis.fetch = originalFetch;
144
+ await fs.rm(dir, { recursive: true, force: true });
145
+ }
146
+ });
147
+
148
+ test('rejects a callback without state even when this guest has one pending exchange', async () => {
149
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
150
+ const persistPath = guestAuthPersistPath(dir);
151
+ await patchGuestAuth('clickup', { clientId: 'app-id', clientSecret: 'app-secret', clientTarget: credentialTarget(clickupGuest.integration) }, persistPath);
152
+ await startGuestAuthorization({
153
+ guest: clickupGuest,
154
+ persistPath,
155
+ origin: 'http://127.0.0.1:4096',
156
+ });
157
+ const originalFetch = globalThis.fetch;
158
+ globalThis.fetch = async (url) => {
159
+ const target = String(url);
160
+ if (target === 'https://api.clickup.com/api/v2/oauth/token') {
161
+ return new Response(JSON.stringify({ access_token: 'access-2' }), {
162
+ status: 200,
163
+ headers: { 'Content-Type': 'application/json' },
164
+ });
165
+ }
166
+ if (target === 'https://api.clickup.com/api/v2/user') {
167
+ return new Response(JSON.stringify({ user: { username: 'ada' } }), {
168
+ status: 200,
169
+ headers: { 'Content-Type': 'application/json' },
170
+ });
171
+ }
172
+ throw new Error(`unexpected fetch ${target}`);
173
+ };
174
+ try {
175
+ await expect(consumeGuestAuthorization({
176
+ guest: clickupGuest,
177
+ persistPath,
178
+ code: 'auth-code',
179
+ state: '',
180
+ })).rejects.toThrow();
181
+ } finally {
182
+ globalThis.fetch = originalFetch;
183
+ await fs.rm(dir, { recursive: true, force: true });
184
+ }
185
+ });
186
+
187
+ test('falls back to a JSON token body when form exchange fails', async () => {
188
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
189
+ const persistPath = guestAuthPersistPath(dir);
190
+ await patchGuestAuth('clickup', { clientId: 'app-id', clientSecret: 'app-secret', clientTarget: credentialTarget(clickupGuest.integration) }, persistPath);
191
+ const started = await startGuestAuthorization({
192
+ guest: clickupGuest,
193
+ persistPath,
194
+ origin: 'http://127.0.0.1:4096',
195
+ });
196
+ const state = new URL(started.authorizationUrl).searchParams.get('state');
197
+ const originalFetch = globalThis.fetch;
198
+ const seen = [];
199
+ globalThis.fetch = async (url, init) => {
200
+ const target = String(url);
201
+ if (target === 'https://api.clickup.com/api/v2/oauth/token') {
202
+ seen.push(init.headers['Content-Type']);
203
+ if (init.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
204
+ return new Response(JSON.stringify({ error: 'invalid_request' }), { status: 400 });
205
+ }
206
+ const body = JSON.parse(init.body);
207
+ expect(body).toEqual({
208
+ client_id: 'app-id',
209
+ client_secret: 'app-secret',
210
+ code: 'auth-code',
211
+ });
212
+ return new Response(JSON.stringify({ access_token: 'access-json' }), { status: 200 });
213
+ }
214
+ if (target === 'https://api.clickup.com/api/v2/user') {
215
+ return new Response(JSON.stringify({ user: { username: 'ada' } }), { status: 200 });
216
+ }
217
+ throw new Error(`unexpected fetch ${target}`);
218
+ };
219
+ try {
220
+ const result = await consumeGuestAuthorization({
221
+ guest: clickupGuest,
222
+ persistPath,
223
+ code: 'auth-code',
224
+ state,
225
+ });
226
+ expect(result).toEqual({ connected: true, account: 'ada' });
227
+ expect(seen).toEqual([
228
+ 'application/x-www-form-urlencoded',
229
+ 'application/json',
230
+ ]);
231
+ } finally {
232
+ globalThis.fetch = originalFetch;
233
+ await fs.rm(dir, { recursive: true, force: true });
234
+ }
235
+ });
236
+
237
+ test('rejects an unknown state', async () => {
238
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
239
+ const persistPath = guestAuthPersistPath(dir);
240
+ try {
241
+ await consumeGuestAuthorization({
242
+ guest: clickupGuest,
243
+ persistPath,
244
+ code: 'auth-code',
245
+ state: 'nope',
246
+ });
247
+ throw new Error('should have thrown');
248
+ } catch (error) {
249
+ expect(error.code).toBe('STATE_MISMATCH');
250
+ }
251
+ await fs.rm(dir, { recursive: true, force: true });
252
+ });
253
+ });
254
+
255
+ describe('createPkcePair', () => {
256
+ test('returns a verifier and challenge', () => {
257
+ const pair = createPkcePair();
258
+ expect(pair.verifier.length).toBeGreaterThan(20);
259
+ expect(pair.challenge.length).toBeGreaterThan(20);
260
+ expect(pair.verifier).not.toBe(pair.challenge);
261
+ });
262
+ });
263
+
264
+ describe('saveGuestAccessToken with a basic scheme', () => {
265
+ const jiraGuest = {
266
+ id: 'jira',
267
+ integration: {
268
+ name: 'Jira',
269
+ description: 'Issues',
270
+ token: {
271
+ apiOrigin: 'https://acme.atlassian.net',
272
+ scheme: 'basic',
273
+ account: { path: '/rest/api/3/myself', name: 'displayName' },
274
+ },
275
+ },
276
+ };
277
+
278
+ test('encodes username:token, probes with a Basic header, and stores only the pair', async () => {
279
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-basic-'));
280
+ const persistPath = guestAuthPersistPath(dir);
281
+ const originalFetch = globalThis.fetch;
282
+ let seenAuthorization = '';
283
+ globalThis.fetch = async (url, init) => {
284
+ expect(String(url)).toBe('https://acme.atlassian.net/rest/api/3/myself');
285
+ seenAuthorization = init.headers.Authorization;
286
+ return new Response(JSON.stringify({ displayName: 'Ada Lovelace' }), {
287
+ status: 200,
288
+ headers: { 'Content-Type': 'application/json' },
289
+ });
290
+ };
291
+ try {
292
+ const result = await saveGuestAccessToken({ guest: jiraGuest, persistPath, token: 'api-token', username: 'ada@acme.example' });
293
+ expect(result).toEqual({ connected: true, account: 'Ada Lovelace' });
294
+ const expected = encodeBasicCredential('ada@acme.example', 'api-token');
295
+ expect(seenAuthorization).toBe(`Basic ${expected}`);
296
+ const stored = await getGuestAuth('jira', persistPath);
297
+ expect(stored.accessToken).toBe(expected);
298
+ expect(stored.tokenType).toBe('basic');
299
+ expect(guestAuthorizationHeader(stored.accessToken, 'basic')).toBe(`Basic ${expected}`);
300
+ } finally {
301
+ globalThis.fetch = originalFetch;
302
+ await fs.rm(dir, { recursive: true, force: true });
303
+ }
304
+ });
305
+
306
+ test('refuses a basic integration without a username', async () => {
307
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-basic-'));
308
+ const persistPath = guestAuthPersistPath(dir);
309
+ try {
310
+ await expect(saveGuestAccessToken({ guest: jiraGuest, persistPath, token: 'api-token' }))
311
+ .rejects.toMatchObject({ code: 'USERNAME_MISSING' });
312
+ } finally {
313
+ await fs.rm(dir, { recursive: true, force: true });
314
+ }
315
+ });
316
+
317
+ test('uses the username as the account label when the manifest declares no account probe', async () => {
318
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-basic-'));
319
+ const persistPath = guestAuthPersistPath(dir);
320
+ const guest = { ...jiraGuest, integration: { ...jiraGuest.integration, token: { apiOrigin: 'https://acme.atlassian.net', scheme: 'basic' } } };
321
+ try {
322
+ const result = await saveGuestAccessToken({ guest, persistPath, token: 'api-token', username: 'ada@acme.example' });
323
+ expect(result).toEqual({ connected: true, account: 'ada@acme.example' });
324
+ } finally {
325
+ await fs.rm(dir, { recursive: true, force: true });
326
+ }
327
+ });
328
+ });
329
+
330
+ describe('a package that changes its endpoints mid sign-in', () => {
331
+ test('the callback refuses the exchange and nothing reaches the new endpoint', async () => {
332
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
333
+ const persistPath = guestAuthPersistPath(dir);
334
+ await patchGuestAuth('clickup', { clientId: 'app-id', clientSecret: 'app-secret', clientTarget: credentialTarget(clickupGuest.integration) }, persistPath);
335
+ const started = await startGuestAuthorization({ guest: clickupGuest, persistPath, origin: 'http://127.0.0.1:4096' });
336
+ const state = new URL(started.authorizationUrl).searchParams.get('state');
337
+ const moved = {
338
+ ...clickupGuest,
339
+ integration: { ...clickupGuest.integration, oauth: { ...clickupGuest.integration.oauth, tokenUrl: 'https://evil.example/token' } },
340
+ };
341
+ const originalFetch = globalThis.fetch;
342
+ const seen = [];
343
+ globalThis.fetch = async (url) => {
344
+ seen.push(String(url));
345
+ return new Response('{}', { status: 200 });
346
+ };
347
+ try {
348
+ await expect(consumeGuestAuthorization({ guest: moved, persistPath, code: 'auth-code', state }))
349
+ .rejects.toMatchObject({ code: 'TARGET_CHANGED' });
350
+ expect(seen).toEqual([]);
351
+ expect((await getGuestAuth('clickup', persistPath))?.accessToken).toBeUndefined();
352
+ } finally {
353
+ globalThis.fetch = originalFetch;
354
+ await fs.rm(dir, { recursive: true, force: true });
355
+ }
356
+ });
357
+
358
+ test('stored tokens record the target and stop counting once it moves', async () => {
359
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
360
+ const persistPath = guestAuthPersistPath(dir);
361
+ await patchGuestAuth('clickup', { clientId: 'app-id', clientSecret: 'app-secret', clientTarget: credentialTarget(clickupGuest.integration) }, persistPath);
362
+ const started = await startGuestAuthorization({ guest: clickupGuest, persistPath, origin: 'http://127.0.0.1:4096' });
363
+ const state = new URL(started.authorizationUrl).searchParams.get('state');
364
+ const originalFetch = globalThis.fetch;
365
+ globalThis.fetch = async (url) => {
366
+ if (String(url) === 'https://api.clickup.com/api/v2/oauth/token') {
367
+ return new Response(JSON.stringify({ access_token: 'access-1', refresh_token: 'refresh-1', token_type: 'Bearer', expires_in: 3600 }), { status: 200, headers: { 'Content-Type': 'application/json' } });
368
+ }
369
+ return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
370
+ };
371
+ try {
372
+ await consumeGuestAuthorization({ guest: clickupGuest, persistPath, code: 'auth-code', state });
373
+ const stored = await getGuestAuth('clickup', persistPath);
374
+ expect(stored.target).toEqual({
375
+ apiOrigin: 'https://api.clickup.com',
376
+ authorizeUrl: 'https://app.clickup.com/api',
377
+ tokenUrl: 'https://api.clickup.com/api/v2/oauth/token',
378
+ });
379
+ expect(storedTokensUsable(stored, clickupGuest.integration)).toBe(true);
380
+ const moved = { ...clickupGuest.integration, oauth: { ...clickupGuest.integration.oauth, apiOrigin: 'https://api.other.example' } };
381
+ expect(storedTokensUsable(stored, moved)).toBe(false);
382
+ } finally {
383
+ globalThis.fetch = originalFetch;
384
+ await fs.rm(dir, { recursive: true, force: true });
385
+ }
386
+ });
387
+ });
388
+
389
+ describe('client credentials after the package moved its endpoints', () => {
390
+ const moved = {
391
+ ...clickupGuest,
392
+ integration: { ...clickupGuest.integration, oauth: { ...clickupGuest.integration.oauth, tokenUrl: 'https://evil.example/token' } },
393
+ };
394
+
395
+ test('a new Connect refuses to reuse the old client id and secret', async () => {
396
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
397
+ const persistPath = guestAuthPersistPath(dir);
398
+ await patchGuestAuth('clickup', { clientId: 'app-id', clientSecret: 'app-secret', clientTarget: credentialTarget(clickupGuest.integration) }, persistPath);
399
+ try {
400
+ await expect(startGuestAuthorization({ guest: moved, persistPath, origin: 'http://127.0.0.1:4096' }))
401
+ .rejects.toMatchObject({ code: 'CLIENT_MISSING' });
402
+ expect(toPublicGuestAuth(await getGuestAuth('clickup', persistPath), moved.integration).hasClient).toBe(false);
403
+ expect(toPublicGuestAuth(await getGuestAuth('clickup', persistPath), clickupGuest.integration).hasClient).toBe(true);
404
+ } finally {
405
+ await fs.rm(dir, { recursive: true, force: true });
406
+ }
407
+ });
408
+
409
+ test('a refresh that finished after a new Connect for other endpoints is dropped', async () => {
410
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
411
+ const persistPath = guestAuthPersistPath(dir);
412
+ await patchGuestAuth('clickup', {
413
+ clientId: 'app-id',
414
+ clientSecret: 'app-secret',
415
+ clientTarget: credentialTarget(clickupGuest.integration),
416
+ accessToken: 'access-a',
417
+ refreshToken: 'refresh-a',
418
+ target: credentialTarget(clickupGuest.integration),
419
+ }, persistPath);
420
+ const originalFetch = globalThis.fetch;
421
+ let release;
422
+ const gate = new Promise((resolve) => { release = resolve; });
423
+ globalThis.fetch = async () => {
424
+ await gate;
425
+ return new Response(JSON.stringify({ access_token: 'access-a2', refresh_token: 'refresh-a2', token_type: 'Bearer', expires_in: 3600 }), { status: 200, headers: { 'Content-Type': 'application/json' } });
426
+ };
427
+ try {
428
+ const refreshing = refreshGuestAccessToken({ guest: clickupGuest, persistPath });
429
+ // Meanwhile the user connected the moved package: new client, new tokens, new target.
430
+ await patchGuestAuth('clickup', {
431
+ clientId: 'app-id-b',
432
+ clientSecret: 'app-secret-b',
433
+ clientTarget: credentialTarget(moved.integration),
434
+ accessToken: 'access-b',
435
+ refreshToken: 'refresh-b',
436
+ target: credentialTarget(moved.integration),
437
+ }, persistPath);
438
+ release();
439
+ expect(await refreshing).toBeNull();
440
+ const stored = await getGuestAuth('clickup', persistPath);
441
+ expect(stored.accessToken).toBe('access-b');
442
+ expect(stored.refreshToken).toBe('refresh-b');
443
+ expect(stored.target).toEqual(credentialTarget(moved.integration));
444
+ } finally {
445
+ globalThis.fetch = originalFetch;
446
+ await fs.rm(dir, { recursive: true, force: true });
447
+ }
448
+ });
449
+ });
450
+
451
+ describe('saveGuestOAuthClient', () => {
452
+ const moved = {
453
+ ...clickupGuest,
454
+ integration: { ...clickupGuest.integration, oauth: { ...clickupGuest.integration.oauth, tokenUrl: 'https://evil.example/token' } },
455
+ };
456
+
457
+ test('an empty secret keeps the stored one only for the same client and endpoints', async () => {
458
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-guest-oauth-'));
459
+ const persistPath = guestAuthPersistPath(dir);
460
+ try {
461
+ await saveGuestOAuthClient({ guest: clickupGuest, persistPath, clientId: 'app-id', clientSecret: 'app-secret' });
462
+ await saveGuestOAuthClient({ guest: clickupGuest, persistPath, clientId: 'app-id' });
463
+ expect((await getGuestAuth('clickup', persistPath)).clientSecret).toBe('app-secret');
464
+
465
+ // Same endpoints, new client id: the old secret does not belong to it.
466
+ await saveGuestOAuthClient({ guest: clickupGuest, persistPath, clientId: 'app-id-2' });
467
+ expect((await getGuestAuth('clickup', persistPath)).clientSecret).toBeUndefined();
468
+
469
+ // Same client id, moved endpoints: the old secret must not follow.
470
+ await saveGuestOAuthClient({ guest: clickupGuest, persistPath, clientId: 'app-id-2', clientSecret: 'secret-2' });
471
+ await saveGuestOAuthClient({ guest: moved, persistPath, clientId: 'app-id-2' });
472
+ const stored = await getGuestAuth('clickup', persistPath);
473
+ expect(stored.clientSecret).toBeUndefined();
474
+ expect(stored.clientTarget).toEqual(credentialTarget(moved.integration));
475
+ await expect(startGuestAuthorization({ guest: moved, persistPath, origin: 'http://127.0.0.1:4096' }))
476
+ .resolves.toMatchObject({ authorizationUrl: expect.stringContaining('client_id=app-id-2') });
477
+ } finally {
478
+ await fs.rm(dir, { recursive: true, force: true });
479
+ }
480
+ });
481
+ });