@vantran-se/openchamber-web 1.23.1 → 1.24.3

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-BjJHrzmU.js} +1 -1
  5. package/dist/assets/DiffView-COVQhWGd.js +4 -0
  6. package/dist/assets/FilesView-B1V5JxFX.js +13 -0
  7. package/dist/assets/GitView-CDOJy5Ay.js +6 -0
  8. package/dist/assets/GuestAttachDialog-D3S0Rjic.js +1 -0
  9. package/dist/assets/{JsonTreeView-nd_E8eVM.js → JsonTreeView-D0C9nU7K.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-D7FKg1XJ.js +1 -0
  70. package/dist/assets/{MarkdownImageGallery-CVYds3sQ.js → MarkdownImageGallery-BrFjAVEf.js} +1 -1
  71. package/dist/assets/MarkdownRendererImpl-DWepTafh.js +12 -0
  72. package/dist/assets/{OnboardingScreen-Dd2IbPTn.js → OnboardingScreen-Dyxxiqdt.js} +1 -1
  73. package/dist/assets/{PatchDiff-nUu5i4bG.js → PatchDiff-DnEhjXcC.js} +1 -1
  74. package/dist/assets/PierreDiffViewer-DyxZVVVR.js +210 -0
  75. package/dist/assets/PlanView-LW2z_EAP.js +5 -0
  76. package/dist/assets/PluginPane-B4tKno2h.js +3 -0
  77. package/dist/assets/SettingsView-DEaMulN9.js +29 -0
  78. package/dist/assets/{SettingsWindow-BccxY81J.js → SettingsWindow-FLDJXOgQ.js} +1 -1
  79. package/dist/assets/SubmoduleDiffSummary-CvVwD-Vi.js +1 -0
  80. package/dist/assets/ToolOutputDialog-BiP0x2Rw.js +15 -0
  81. package/dist/assets/{ToolPartDiffPreview-BFVgk0UK.js → ToolPartDiffPreview-CkR4msKP.js} +2 -2
  82. package/dist/assets/{VirtualizedFileDiff-z3_H74UR.js → VirtualizedFileDiff-0pyr_mHY.js} +3 -3
  83. package/dist/assets/{WalkthroughView-BqM9GFbJ.js → WalkthroughView-yNj4nOP5.js} +2 -2
  84. package/dist/assets/appThemeRegistry-BuUKhbYQ.js +1 -0
  85. package/dist/assets/commitAndPush-DvJKa3Ks.js +1 -0
  86. package/dist/assets/{computeVirtualFileMetrics-Bdazp9Qo.js → computeVirtualFileMetrics-DxTBMuu-.js} +1 -1
  87. package/dist/assets/de-DgxgFyGG.js +10 -0
  88. package/dist/assets/es-BFsZj5F_.js +16 -0
  89. package/dist/assets/fr-jdZ5KKQi.js +14 -0
  90. package/dist/assets/{index-eo1-D_7z.js → index-CKHjPKbq.js} +1 -1
  91. package/dist/assets/index-Cbci4i1W.js +40 -0
  92. package/dist/assets/index-DqaMWNrC.css +1 -0
  93. package/dist/assets/{isDiffPlainText-BvKH712-.js → isDiffPlainText-D7FkBMP6.js} +1 -1
  94. package/dist/assets/{isFilePlainText-BdVVPukf.js → isFilePlainText-CL2vsrIZ.js} +1 -1
  95. package/dist/assets/ja-C5kKrhO4.js +16 -0
  96. package/dist/assets/ko-DXDTT_0O.js +16 -0
  97. package/dist/assets/main-CZALtVek.js +896 -0
  98. package/dist/assets/main-CgXMBNvG.js +2 -0
  99. package/dist/assets/{markdown-shiki.worker-D5fBU-lw.js → markdown-shiki.worker-CahLvHIq.js} +5 -5
  100. package/dist/assets/markdownCore-Dh7WEsdM.js +5 -0
  101. package/dist/assets/miniChat-CwTmDixi.js +2 -0
  102. package/dist/assets/mobile-CLf2vCtO.js +2 -0
  103. package/dist/assets/{patchFileDiff-B7LjppSS.js → patchFileDiff-Bsba8KFp.js} +3 -3
  104. package/dist/assets/pl-D-7x4Y8Q.js +16 -0
  105. package/dist/assets/pt-BR-CSBZrHa8.js +16 -0
  106. package/dist/assets/renderElectronMiniChatApp-C5gAFBk0.js +2 -0
  107. package/dist/assets/renderMobileApp-V4hYd32H.js +25 -0
  108. package/dist/assets/runtimeEndpointReset-DhlHCn-2.js +25 -0
  109. package/dist/assets/{scheduledTasksApi-hceovDei.js → scheduledTasksApi-DzjbLd8P.js} +1 -1
  110. package/dist/assets/shikiHighlight-8_3csmr6.js +1 -0
  111. package/dist/assets/textMateThemeFromAppTheme-CHo2fCgW.js +1 -0
  112. package/dist/assets/tr-Dr-oJ22L.js +7 -0
  113. package/dist/assets/uk-9Nx83UcI.js +16 -0
  114. package/dist/assets/useAppFontEffects-khZXpFL_.js +7333 -0
  115. package/dist/assets/useInlineCommentController-DkQ_gqpn.js +2 -0
  116. package/dist/assets/useRootScrollLock-DdvHf9Pt.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-CdzUmUyZ.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-B8IcVQAA.js} +2 -2
  128. package/dist/assets/zh-CN-Bxy_Hj3r.js +16 -0
  129. package/dist/assets/zh-TW-0BB4gpHM.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,53 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import {
4
+ injectGuestAssetTokens,
5
+ listRelativeGuestScriptHrefs,
6
+ parseGuestUrlToken,
7
+ resolveGuestHtmlRelativePath,
8
+ } from './html-tokens.js';
9
+
10
+ describe('parseGuestUrlToken', () => {
11
+ test('reads a string or the first array entry', () => {
12
+ expect(parseGuestUrlToken('abc')).toBe('abc');
13
+ expect(parseGuestUrlToken(['abc', 'def'])).toBe('abc');
14
+ expect(parseGuestUrlToken(undefined)).toBe('');
15
+ expect(parseGuestUrlToken('')).toBe('');
16
+ });
17
+ });
18
+
19
+ describe('listRelativeGuestScriptHrefs', () => {
20
+ test('keeps relative scripts and drops remote ones', () => {
21
+ expect(listRelativeGuestScriptHrefs(
22
+ '<script src="./main.js"></script><script src="https://cdn.example/a.js"></script>',
23
+ )).toEqual(['./main.js']);
24
+ expect(listRelativeGuestScriptHrefs('')).toEqual([]);
25
+ });
26
+ });
27
+
28
+ describe('resolveGuestHtmlRelativePath', () => {
29
+ test('resolves against the entry file and blocks escape', () => {
30
+ expect(resolveGuestHtmlRelativePath('panel/index.html', './main.js')).toBe('panel/main.js');
31
+ expect(resolveGuestHtmlRelativePath('panel/index.html', 'main.js?oc_url_token=x')).toBe('panel/main.js');
32
+ expect(resolveGuestHtmlRelativePath('panel/index.html', '../secret.js')).toBe('secret.js');
33
+ expect(resolveGuestHtmlRelativePath('panel/index.html', '../../outside.js')).toBeNull();
34
+ expect(resolveGuestHtmlRelativePath('index.html', './panel/main.js')).toBe('panel/main.js');
35
+ });
36
+ });
37
+
38
+ describe('injectGuestAssetTokens', () => {
39
+ test('adds the token to a relative script src', () => {
40
+ expect(injectGuestAssetTokens('<script src="./main.js"></script>', 'tok/en'))
41
+ .toBe('<script src="./main.js?oc_url_token=tok%2Fen"></script>');
42
+ });
43
+
44
+ test('leaves absolute and data urls alone', () => {
45
+ const html = '<script src="https://cdn.example/a.js"></script><img src="data:image/svg+xml,x">';
46
+ expect(injectGuestAssetTokens(html, 'tok')).toBe(html);
47
+ });
48
+
49
+ test('does not duplicate an existing token', () => {
50
+ expect(injectGuestAssetTokens('<script src="./main.js?oc_url_token=old"></script>', 'new'))
51
+ .toBe('<script src="./main.js?oc_url_token=old"></script>');
52
+ });
53
+ });
@@ -0,0 +1,406 @@
1
+ import fs from 'node:fs/promises';
2
+ import { removeGuestStorage } from './storage.js';
3
+ import https from 'node:https';
4
+ import path from 'node:path';
5
+ import { z } from 'zod';
6
+
7
+ import {
8
+ inspectGuestPackage,
9
+ listInstalledGuests,
10
+ resolveGuestPackageRoot,
11
+ toPublicGuest,
12
+ } from './catalog.js';
13
+ import { stopGuestService } from './service.js';
14
+ import { cloneGitRepository, isHttpsZipUrl, isPublicHostname, parseGitInstallUrl, publicAddressesOf } from './clone.js';
15
+ import { isReservedBuiltInId } from './builtins.js';
16
+ import { extractZipBuffer, unwrapGuestRoot } from './extract-zip.js';
17
+ import {
18
+ guestCopiesDir,
19
+ isCopiedGuestRoot,
20
+ readExtensionStore,
21
+ updateExtensionStore,
22
+ } from './persist.js';
23
+
24
+ const MAX_ZIP_BYTES = 20 * 1024 * 1024;
25
+ const DOWNLOAD_TIMEOUT_MS = 60_000;
26
+
27
+ const installBodySchema = z.object({
28
+ path: z.string().trim().min(1).optional(),
29
+ url: z.string().trim().min(1).optional(),
30
+ gitIdentityId: z.string().trim().min(1).max(128).optional(),
31
+ replace: z.boolean().optional(),
32
+ }).refine((value) => Boolean(value.path) !== Boolean(value.url));
33
+
34
+ export const parseInstallRequest = (body) => {
35
+ const parsed = installBodySchema.safeParse(body);
36
+ return parsed.success ? parsed.data : null;
37
+ };
38
+
39
+ const persistGuest = async (guest, root, source, persistPath, { replace = false, origin = null } = {}) => {
40
+ if (isReservedBuiltInId(guest.id)) return { ok: false, code: 'reserved-id' };
41
+ const stored = await readExtensionStore(persistPath);
42
+ const storedRoots = await Promise.all(stored.paths.map((entry) => resolveGuestPackageRoot(entry)));
43
+ if (storedRoots.some((entry) => entry === root)) {
44
+ if (!replace) {
45
+ return { ok: false, code: 'already-installed', id: guest.id };
46
+ }
47
+ return {
48
+ ok: true,
49
+ replaced: true,
50
+ guest: toPublicGuest({
51
+ ...guest,
52
+ source,
53
+ path: root,
54
+ capabilityGrants: stored.capabilityGrants?.[guest.id] ?? [],
55
+ enabled: !stored.disabledGuests?.[guest.id],
56
+ }),
57
+ };
58
+ }
59
+ const existing = await listInstalledGuests({ persistPath });
60
+ const clash = existing.find((entry) => entry.id === guest.id);
61
+ if (clash) {
62
+ if (!replace) {
63
+ return { ok: false, code: 'id-taken', id: guest.id };
64
+ }
65
+ const removed = await uninstallGuest(guest.id, persistPath);
66
+ if (!removed.ok) {
67
+ return removed;
68
+ }
69
+ }
70
+ await updateExtensionStore(persistPath, (after) => ({
71
+ ...after,
72
+ paths: after.paths.includes(root) ? after.paths : [...after.paths, root],
73
+ sources: { ...after.sources, [root]: source },
74
+ gitOrigins: origin ? { ...after.gitOrigins, [root]: origin } : after.gitOrigins,
75
+ }));
76
+ return {
77
+ ok: true,
78
+ replaced: Boolean(clash),
79
+ guest: toPublicGuest({ ...guest, source, path: root, capabilityGrants: [], enabled: true }),
80
+ };
81
+ };
82
+
83
+ const removeDir = async (dir) => {
84
+ await fs.rm(dir, { recursive: true, force: true });
85
+ };
86
+
87
+ const installCopiedGuest = async ({ source, prepare, persistPath, openchamberVersion, replace = false, origin = null }) => {
88
+ const copies = guestCopiesDir(persistPath);
89
+ await fs.mkdir(copies, { recursive: true });
90
+ const staging = path.join(copies, `.tmp-${process.pid}-${Date.now()}`);
91
+ try {
92
+ const prepared = await prepare(staging);
93
+ if (!prepared.ok) {
94
+ await removeDir(staging);
95
+ return prepared;
96
+ }
97
+ const packageRoot = await unwrapGuestRoot(prepared.root ?? staging);
98
+ const inspected = await inspectGuestPackage(packageRoot, { openchamberVersion });
99
+ if (!inspected.ok) {
100
+ await removeDir(staging);
101
+ return inspected;
102
+ }
103
+ if (isReservedBuiltInId(inspected.guest.id)) {
104
+ await removeDir(staging);
105
+ return { ok: false, code: 'reserved-id' };
106
+ }
107
+ const dest = path.join(copies, inspected.guest.id);
108
+ const store = await readExtensionStore(persistPath);
109
+ // The store holds the copy's realpath (on macOS /tmp is a symlink, so
110
+ // that differs from `dest` as spelled); either spelling is the same
111
+ // package already installed, not another folder claiming the id.
112
+ const destReal = await fs.realpath(dest).catch(() => dest);
113
+ const registered = store.paths.some((entry) => path.resolve(entry) === dest || path.resolve(entry) === destReal);
114
+ if (registered) {
115
+ if (!replace) {
116
+ await removeDir(staging);
117
+ return { ok: false, code: 'already-installed', id: inspected.guest.id };
118
+ }
119
+ const removed = await uninstallGuest(inspected.guest.id, persistPath);
120
+ if (!removed.ok) {
121
+ await removeDir(staging);
122
+ return removed;
123
+ }
124
+ }
125
+ // A copy on disk that the store does not know about is a leftover from an
126
+ // install that died between the move and the persist. It would otherwise
127
+ // block this id forever, so it is replaced rather than reported.
128
+ await removeDir(dest);
129
+ await fs.rename(packageRoot, dest);
130
+ if (packageRoot !== staging) {
131
+ await removeDir(staging);
132
+ }
133
+ const root = await fs.realpath(dest);
134
+ const persisted = await persistGuest(inspected.guest, root, source, persistPath, { replace, origin });
135
+ if (!persisted.ok) {
136
+ await removeDir(dest);
137
+ return persisted;
138
+ }
139
+ // A registered copy was uninstalled above before this one took its place.
140
+ return registered ? { ...persisted, replaced: true } : persisted;
141
+ } catch {
142
+ await removeDir(staging);
143
+ return { ok: false, code: source === 'git' ? 'clone-failed' : 'extract-failed' };
144
+ }
145
+ };
146
+
147
+ // A redirect hop may land on a signed download URL without a .zip suffix; it
148
+ // still has to be https on a public host.
149
+ const isHttpsRedirectUrl = (value) => {
150
+ try {
151
+ const parsed = new URL(value);
152
+ return parsed.protocol === 'https:' && parsed.username === '' && parsed.password === '' && isPublicHostname(parsed.hostname);
153
+ } catch {
154
+ return false;
155
+ }
156
+ };
157
+
158
+ const readLocalZip = async (filePath) => {
159
+ try {
160
+ const stat = await fs.stat(filePath);
161
+ if (!stat.isFile() || stat.size > MAX_ZIP_BYTES) {
162
+ return null;
163
+ }
164
+ return await fs.readFile(filePath);
165
+ } catch {
166
+ return null;
167
+ }
168
+ };
169
+
170
+ const MAX_ZIP_REDIRECTS = 5;
171
+
172
+ /**
173
+ * One https request pinned to an address the caller already checked. The
174
+ * socket goes to `address`; TLS still verifies the certificate against the
175
+ * URL's hostname. Redirects are not followed: the status and `location` come
176
+ * back for the caller to check. The body is capped while it streams.
177
+ * @param {string} url
178
+ * @param {{ address: string, family: 4 | 6 }} target
179
+ * @param {number} maxBytes
180
+ * @returns {Promise<{ status: number, location: string | null, body: Buffer | null }>}
181
+ */
182
+ const requestHttpsPinned = (url, target, maxBytes) => new Promise((resolve, reject) => {
183
+ const parsed = new URL(url);
184
+ const request = https.request(parsed, {
185
+ method: 'GET',
186
+ // Node's connector asks for `all` addresses when it tries families in
187
+ // parallel; either way it only ever gets the one that was checked.
188
+ lookup: (_hostname, options, callback) => (
189
+ options?.all
190
+ ? callback(null, [{ address: target.address, family: target.family }])
191
+ : callback(null, target.address, target.family)
192
+ ),
193
+ autoSelectFamily: false,
194
+ headers: { 'User-Agent': 'openchamber' },
195
+ timeout: DOWNLOAD_TIMEOUT_MS,
196
+ }, (response) => {
197
+ const status = response.statusCode ?? 0;
198
+ const location = typeof response.headers.location === 'string' ? response.headers.location : null;
199
+ if (status >= 300 && status < 400) {
200
+ response.resume();
201
+ resolve({ status, location, body: null });
202
+ return;
203
+ }
204
+ const declared = Number(response.headers['content-length']);
205
+ if (Number.isFinite(declared) && declared > maxBytes) {
206
+ response.destroy();
207
+ resolve({ status, location, body: null });
208
+ return;
209
+ }
210
+ /** @type {Buffer[]} */
211
+ const chunks = [];
212
+ let received = 0;
213
+ response.on('data', (chunk) => {
214
+ received += chunk.length;
215
+ if (received > maxBytes) {
216
+ response.destroy();
217
+ resolve({ status, location, body: null });
218
+ return;
219
+ }
220
+ chunks.push(chunk);
221
+ });
222
+ response.on('end', () => resolve({ status, location, body: Buffer.concat(chunks) }));
223
+ response.on('error', reject);
224
+ });
225
+ request.on('timeout', () => request.destroy(new Error('download timed out')));
226
+ request.on('error', reject);
227
+ request.end();
228
+ });
229
+
230
+ /**
231
+ * Fetch the archive by hand, one hop at a time. A public URL can redirect to
232
+ * a private one, so every hop is checked (https, public host, public DNS
233
+ * answer) before the request is made, and the request goes to the address
234
+ * that was checked, not to whatever a second lookup would say.
235
+ * @param {string} url
236
+ * @param {{ request?: typeof requestHttpsPinned, lookup?: Parameters<typeof publicAddressesOf>[1] }} [options]
237
+ */
238
+ export const downloadZip = async (url, { request = requestHttpsPinned, lookup } = {}) => {
239
+ let current = url;
240
+ for (let hop = 0; hop <= MAX_ZIP_REDIRECTS; hop += 1) {
241
+ if (!isHttpsZipUrl(current) && !isHttpsRedirectUrl(current)) {
242
+ return null;
243
+ }
244
+ const addresses = await publicAddressesOf(new URL(current).hostname, lookup);
245
+ if (!addresses) {
246
+ return null;
247
+ }
248
+ const response = await request(current, addresses[0], MAX_ZIP_BYTES);
249
+ if (response.status >= 300 && response.status < 400) {
250
+ if (!response.location) {
251
+ return null;
252
+ }
253
+ current = new URL(response.location, current).href;
254
+ continue;
255
+ }
256
+ if (response.status < 200 || response.status >= 300 || !response.body) {
257
+ return null;
258
+ }
259
+ return response.body;
260
+ }
261
+ return null;
262
+ };
263
+
264
+ /**
265
+ * Install from zip bytes already in memory. Local `.zip` paths, https zip
266
+ * URLs, and browser uploads (`POST /api/guests/upload`) all land here, so the
267
+ * archive limits, unwrap, inspection, and store write are one path. The
268
+ * archive size cap is the caller's job: path and URL installs stop at
269
+ * `MAX_ZIP_BYTES`, the upload route at its own configured limit.
270
+ */
271
+ export const installGuestFromZipBuffer = async (buffer, persistPath, { openchamberVersion, replace = false } = {}) => (
272
+ installCopiedGuest({
273
+ source: 'zip',
274
+ persistPath,
275
+ openchamberVersion,
276
+ replace,
277
+ prepare: async (staging) => {
278
+ const extracted = await extractZipBuffer(buffer, staging);
279
+ return extracted.ok ? { ok: true, root: staging } : extracted;
280
+ },
281
+ })
282
+ );
283
+
284
+ export const installGuestFromPath = async (rawPath, persistPath, { openchamberVersion, replace = false } = {}) => {
285
+ if (!path.isAbsolute(rawPath)) {
286
+ return { ok: false, code: 'invalid-path' };
287
+ }
288
+ try {
289
+ const stat = await fs.stat(rawPath);
290
+ if (stat.isFile() && rawPath.toLowerCase().endsWith('.zip')) {
291
+ const buffer = await readLocalZip(rawPath);
292
+ if (!buffer) {
293
+ return { ok: false, code: 'not-found' };
294
+ }
295
+ return installGuestFromZipBuffer(buffer, persistPath, { openchamberVersion, replace });
296
+ }
297
+ } catch {
298
+ return { ok: false, code: 'not-found' };
299
+ }
300
+
301
+ const root = await resolveGuestPackageRoot(rawPath);
302
+ if (!root) {
303
+ return { ok: false, code: 'not-found' };
304
+ }
305
+ const inspected = await inspectGuestPackage(root, { openchamberVersion });
306
+ if (!inspected.ok) {
307
+ return inspected;
308
+ }
309
+ return persistGuest(inspected.guest, root, 'path', persistPath, { replace });
310
+ };
311
+
312
+ export const installGuestFromUrl = async (rawUrl, persistPath, { openchamberVersion, replace = false, gitBinary, gitIdentityId } = {}) => {
313
+ if (isHttpsZipUrl(rawUrl)) {
314
+ try {
315
+ const buffer = await downloadZip(rawUrl);
316
+ if (!buffer) {
317
+ return { ok: false, code: 'extract-failed' };
318
+ }
319
+ return installGuestFromZipBuffer(buffer, persistPath, { openchamberVersion, replace });
320
+ } catch {
321
+ return { ok: false, code: 'extract-failed' };
322
+ }
323
+ }
324
+ const gitSource = parseGitInstallUrl(rawUrl);
325
+ if (!gitSource) {
326
+ return { ok: false, code: 'invalid-url' };
327
+ }
328
+ return installGuestFromGitSource(gitSource.url, persistPath, { openchamberVersion, replace, gitBinary, ref: gitSource.ref, gitIdentityId });
329
+ };
330
+
331
+ /**
332
+ * `source` is the clone URL without its `#ref` fragment; `ref` is the branch
333
+ * or tag to pin (omitted means the remote default branch). Both are stored
334
+ * as the guest's origin so Settings → Extensions can check for updates later.
335
+ */
336
+ export const installGuestFromGitSource = async (source, persistPath, { openchamberVersion, replace = false, gitBinary, ref, gitIdentityId, lookup } = {}) => {
337
+ const origin = { url: source };
338
+ if (ref) origin.ref = ref;
339
+ if (gitIdentityId) origin.gitIdentityId = gitIdentityId;
340
+ return installCopiedGuest({
341
+ source: 'git',
342
+ persistPath,
343
+ openchamberVersion,
344
+ replace,
345
+ origin,
346
+ prepare: async (staging) => {
347
+ const cloned = await cloneGitRepository(source, staging, { gitBinary, ref, gitIdentityId, lookup });
348
+ return cloned.ok ? { ok: true, root: staging } : cloned;
349
+ },
350
+ });
351
+ };
352
+
353
+ export const installGuest = async (request, persistPath, { openchamberVersion, gitBinary } = {}) => {
354
+ const replace = Boolean(request.replace);
355
+ if (request.url) {
356
+ return installGuestFromUrl(request.url, persistPath, { openchamberVersion, replace, gitBinary, gitIdentityId: request.gitIdentityId });
357
+ }
358
+ return installGuestFromPath(request.path, persistPath, { openchamberVersion, replace });
359
+ };
360
+
361
+ export const uninstallGuest = async (id, persistPath) => {
362
+ const existing = await listInstalledGuests({ persistPath });
363
+ const guest = existing.find((entry) => entry.id === id);
364
+ if (!guest) {
365
+ return { ok: false, code: 'not-found' };
366
+ }
367
+ if (guest.source === 'bundled') {
368
+ return { ok: false, code: 'bundled' };
369
+ }
370
+
371
+ let removedRoot = null;
372
+ await updateExtensionStore(persistPath, async (stored) => {
373
+ const kept = [];
374
+ const sources = {};
375
+ const gitOrigins = {};
376
+ for (const entry of stored.paths) {
377
+ const root = await resolveGuestPackageRoot(entry);
378
+ if (root === guest.packageRoot) {
379
+ removedRoot = root;
380
+ continue;
381
+ }
382
+ kept.push(entry);
383
+ if (stored.sources[entry]) {
384
+ sources[entry] = stored.sources[entry];
385
+ }
386
+ if (stored.gitOrigins[entry]) {
387
+ gitOrigins[entry] = stored.gitOrigins[entry];
388
+ }
389
+ }
390
+ const capabilityGrants = { ...(stored.capabilityGrants ?? {}) };
391
+ delete capabilityGrants[id];
392
+ const capabilityScopes = { ...(stored.capabilityScopes ?? {}) };
393
+ delete capabilityScopes[id];
394
+ const disabledGuests = { ...(stored.disabledGuests ?? {}) };
395
+ delete disabledGuests[id];
396
+ const serviceSocketOverrides = { ...(stored.serviceSocketOverrides ?? {}) };
397
+ delete serviceSocketOverrides[id];
398
+ return { paths: kept, sources, gitOrigins, capabilityGrants, capabilityScopes, disabledGuests, serviceSocketOverrides };
399
+ });
400
+ await stopGuestService(id);
401
+ await removeGuestStorage(persistPath, id);
402
+ if (removedRoot && isCopiedGuestRoot(removedRoot, persistPath)) {
403
+ await fs.rm(removedRoot, { recursive: true, force: true });
404
+ }
405
+ return { ok: true };
406
+ };