@vantran-se/openchamber-web 2.1.1 → 2.2.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 (442) hide show
  1. package/README.md +32 -24
  2. package/bin/cli.js +1 -3
  3. package/bin/cli.test.js +40 -16
  4. package/bin/lib/DOCUMENTATION.md +6 -5
  5. package/bin/lib/cli-args.js +6 -1
  6. package/bin/lib/cli-args.test.js +34 -0
  7. package/bin/lib/cli-lifecycle.js +0 -1
  8. package/bin/lib/cli-network.js +8 -2
  9. package/bin/lib/cli-process.js +1 -1
  10. package/bin/lib/cli-settings-accessors.test.js +21 -13
  11. package/bin/lib/cli-startup.js +17 -11
  12. package/bin/lib/cli-startup.test.js +92 -2
  13. package/bin/lib/commands-connect-url.js +8 -1
  14. package/bin/lib/commands-schedule.js +8 -2
  15. package/bin/lib/commands-serve.js +15 -21
  16. package/bin/lib/commands-update.js +12 -24
  17. package/bin/lib/commands-update.test.js +14 -46
  18. package/dist/assets/DiffView-Cv9MSXl1.js +4 -0
  19. package/dist/assets/FilePreviewCommentMenu-D4kLseHj.js +4 -0
  20. package/dist/assets/FilesView-BVoD8I2x.js +14 -0
  21. package/dist/assets/{GitView-DzuzhIPx.js → GitView-Bzb8p-ew.js} +4 -4
  22. package/dist/assets/GuestSurfacePane-Crdbvsxo.js +1 -0
  23. package/dist/assets/InProgressOperationBanner-CwMjNQgk.js +2 -0
  24. package/dist/assets/{JsonTreeView-B_EPRjzy.js → JsonTreeView-DRKHZfsn.js} +1 -1
  25. package/dist/assets/MarkdownImageGallery-DZtqT1Zn.js +1 -0
  26. package/dist/assets/MarkdownRendererImpl-CqG7HCNd.js +10 -0
  27. package/dist/assets/OnboardingScreen-DMIxwYK5.js +1 -0
  28. package/dist/assets/{PatchDiff-6yh1csza.js → PatchDiff-CUcCg1jk.js} +1 -1
  29. package/dist/assets/PierreDiffViewer-CnnR-dnD.js +228 -0
  30. package/dist/assets/PlanView-RCBadcY7.js +5 -0
  31. package/dist/assets/SettingsView-CjBjZFRO.js +30 -0
  32. package/dist/assets/SettingsWindow-DyM7QuhI.js +1 -0
  33. package/dist/assets/SubmoduleDiffSummary-BWIfgMED.js +1 -0
  34. package/dist/assets/ToolOutputDialog-Q0P3FuAi.js +15 -0
  35. package/dist/assets/ToolPartDiffPreview-p7qR9FpU.js +8 -0
  36. package/dist/assets/{VirtualizedFileDiff-BTJVRkyo.js → VirtualizedFileDiff-CFpjL8Ns.js} +3 -3
  37. package/dist/assets/{Virtualizer-xCEYz34p.js → Virtualizer-C0IsclZn.js} +1 -1
  38. package/dist/assets/WalkthroughView-DI31zD02.js +4 -0
  39. package/dist/assets/appThemeRegistry-B9gkFq9i.js +1 -0
  40. package/dist/assets/{computeVirtualFileMetrics-CmPe29DM.js → computeVirtualFileMetrics-C2WM6JC2.js} +2 -2
  41. package/dist/assets/de-YKfk5MmC.js +14 -0
  42. package/dist/assets/es-_gwT0kr-.js +20 -0
  43. package/dist/assets/fileTreeStatus-D438yi8M.js +1 -0
  44. package/dist/assets/fr-DqzvXmIm.js +18 -0
  45. package/dist/assets/index-BLaHKdtx.js +1 -0
  46. package/dist/assets/index-BkMXRrVQ.css +1 -0
  47. package/dist/assets/index-DxNa7xYB.js +65 -0
  48. package/dist/assets/initializeSharedPreferences-Bck5EAWP.js +1 -0
  49. package/dist/assets/{isDiffPlainText-CQmDCnEv.js → isDiffPlainText-Dgbw8VKk.js} +1 -1
  50. package/dist/assets/{isFilePlainText-CYBUzW4I.js → isFilePlainText-_ZS5UYRj.js} +1 -1
  51. package/dist/assets/ja-B-GFYhQE.js +20 -0
  52. package/dist/assets/ko-mzksfqDC.js +20 -0
  53. package/dist/assets/main-C5Xx8dn_.js +2 -0
  54. package/dist/assets/main-CfBSQM1H.js +990 -0
  55. package/dist/assets/markdownCore-BmWwW1ll.js +7 -0
  56. package/dist/assets/miniChat-mnKwy4vQ.js +2 -0
  57. package/dist/assets/mobile-DeEQd0wZ.js +2 -0
  58. package/dist/assets/nl-CWQr6NSq.js +20 -0
  59. package/dist/assets/{parseDiffFromFile-Cy0PNxun.js → parseDiffFromFile-BUgn-T9p.js} +2 -2
  60. package/dist/assets/patchFileDiff-Dt8LhgBh.js +13 -0
  61. package/dist/assets/pl-UmRcDUX2.js +20 -0
  62. package/dist/assets/pt-BR-BSLybvVF.js +20 -0
  63. package/dist/assets/relocateRestoredSession-B5E9xb3T.js +1 -0
  64. package/dist/assets/renderElectronMiniChatApp-C0a9A5_J.js +2 -0
  65. package/dist/assets/renderMobileApp-BNncg3Pk.js +25 -0
  66. package/dist/assets/runtimeEndpointReset-CZ5aafpl.js +62 -0
  67. package/dist/assets/{selectionMarkdown-TkYk5h9l.js → selectionMarkdown-CUN3wUld.js} +9 -9
  68. package/dist/assets/{sessionOwnership-Bs1xbRfB.js → sessionOwnership-BSuxCEhb.js} +1 -1
  69. package/dist/assets/shikiHighlight-DHDx-6eM.js +1 -0
  70. package/dist/assets/space-apply-DM_2pRW7.js +1 -0
  71. package/dist/assets/space-close-Bpomt8z0.js +1 -0
  72. package/dist/assets/{surface-B6n7wJtt.js → surface-C-jQaGnL.js} +1 -1
  73. package/dist/assets/{textMateThemeFromAppTheme-CDlKffWz.js → textMateThemeFromAppTheme-DlsTphbY.js} +1 -1
  74. package/dist/assets/tr-gaTrX_HK.js +11 -0
  75. package/dist/assets/uk-CnJF6ykl.js +20 -0
  76. package/dist/assets/{useAppFontEffects-DCqCVGlT.js → useAppFontEffects-DGlvOqP_.js} +194 -233
  77. package/dist/assets/useInlineCommentController-kMN4Wg_D.js +2 -0
  78. package/dist/assets/{useRootScrollLock-BowwS8kq.js → useRootScrollLock-DMPpvpNP.js} +2 -2
  79. package/dist/assets/{vendor-base-ui-DX96QhTP.js → vendor-base-ui-Det7U81h.js} +1 -1
  80. package/dist/assets/{vendor-codemirror-autocomplete-DW1FQz9_.js → vendor-codemirror-autocomplete-BSHQHbKu.js} +1 -1
  81. package/dist/assets/{vendor-codemirror-commands-DpmhalwN.js → vendor-codemirror-commands-C4G_dPWe.js} +1 -1
  82. package/dist/assets/{vendor-codemirror-lang-angular-BqExbN1M.js → vendor-codemirror-lang-angular-B6LbBGHB.js} +1 -1
  83. package/dist/assets/{vendor-codemirror-lang-cpp-DZA36HfS.js → vendor-codemirror-lang-cpp-BWWXBx7W.js} +1 -1
  84. package/dist/assets/{vendor-codemirror-lang-css-B07blnnZ.js → vendor-codemirror-lang-css-Cayfb29q.js} +1 -1
  85. package/dist/assets/{vendor-codemirror-lang-go-BM8K9D1-.js → vendor-codemirror-lang-go-BoKlCNKw.js} +1 -1
  86. package/dist/assets/{vendor-codemirror-lang-html-DaH9adfE.js → vendor-codemirror-lang-html-BQWerHsJ.js} +1 -1
  87. package/dist/assets/{vendor-codemirror-lang-java-CsdC0viz.js → vendor-codemirror-lang-java-DWX7Fq_K.js} +1 -1
  88. package/dist/assets/{vendor-codemirror-lang-javascript-CpGYXGuo.js → vendor-codemirror-lang-javascript-DbpEcZM0.js} +2 -2
  89. package/dist/assets/{vendor-codemirror-lang-jinja-BBeGzXw5.js → vendor-codemirror-lang-jinja-D1JIJRA7.js} +1 -1
  90. package/dist/assets/{vendor-codemirror-lang-json-58T70Hqs.js → vendor-codemirror-lang-json-BzgckzmB.js} +1 -1
  91. package/dist/assets/{vendor-codemirror-lang-less-BI-xCA59.js → vendor-codemirror-lang-less-B9EpDXFi.js} +1 -1
  92. package/dist/assets/{vendor-codemirror-lang-liquid-zEcMa-zy.js → vendor-codemirror-lang-liquid-9U7kRbSf.js} +1 -1
  93. package/dist/assets/{vendor-codemirror-lang-markdown-BLKnlLP4.js → vendor-codemirror-lang-markdown-D2TEBIvZ.js} +1 -1
  94. package/dist/assets/{vendor-codemirror-lang-php-9f35M83-.js → vendor-codemirror-lang-php-BT6sDVzE.js} +1 -1
  95. package/dist/assets/vendor-codemirror-lang-python-jAMtYiA9.js +3 -0
  96. package/dist/assets/{vendor-codemirror-lang-rust-MNDL_jZQ.js → vendor-codemirror-lang-rust-CVO56EiL.js} +1 -1
  97. package/dist/assets/{vendor-codemirror-lang-sass-p9epsSNm.js → vendor-codemirror-lang-sass-DZlOoUWu.js} +1 -1
  98. package/dist/assets/{vendor-codemirror-lang-sql-Bqzh0EXX.js → vendor-codemirror-lang-sql-fZ6t1DPw.js} +1 -1
  99. package/dist/assets/{vendor-codemirror-lang-vue-BnNUWV-p.js → vendor-codemirror-lang-vue-DxbqrLiE.js} +1 -1
  100. package/dist/assets/{vendor-codemirror-lang-wast-C2a-oLmk.js → vendor-codemirror-lang-wast-DyU1Gt-I.js} +1 -1
  101. package/dist/assets/vendor-codemirror-lang-xml-C9a_K5_p.js +1 -0
  102. package/dist/assets/{vendor-codemirror-lang-yaml-C0sjswB0.js → vendor-codemirror-lang-yaml-Cptq8l0e.js} +1 -1
  103. package/dist/assets/{vendor-codemirror-language-ByBn5zIW.js → vendor-codemirror-language-DufXoQvE.js} +2 -2
  104. package/dist/assets/vendor-codemirror-language-data-CtwSWKtX.js +2 -0
  105. package/dist/assets/{vendor-codemirror-search-ib-sHD5W.js → vendor-codemirror-search-CvXM_pZg.js} +1 -1
  106. package/dist/assets/{vendor-codemirror-state-G3UuR-be.js → vendor-codemirror-state-Dek9sMtw.js} +1 -1
  107. package/dist/assets/{vendor-codemirror-view-Bniqp7p5.js → vendor-codemirror-view-1e0FQ6Wn.js} +1 -1
  108. package/dist/assets/vendor-framer-motion-DxDVhOLc.js +9 -0
  109. package/dist/assets/vendor-katex-CuQvj9-L.js +257 -0
  110. package/dist/assets/vendor-katex-DdZNUVNG.css +1 -0
  111. package/dist/assets/{vendor-motion-dom-DcCgeG69.js → vendor-motion-dom-HXPOMxb7.js} +1 -1
  112. package/dist/assets/{vendor-opencode-client-DHzhmLwV.js → vendor-opencode-client-BmFcUkbQ.js} +1 -1
  113. package/dist/assets/{vendor-replit-codemirror-vim-DkzqxN2H.js → vendor-replit-codemirror-vim-BlJRn-cW.js} +2 -2
  114. package/dist/assets/{vendor-tanstack-react-virtual-H_JmXOFV.js → vendor-tanstack-react-virtual-CvuLsk7D.js} +1 -1
  115. package/dist/assets/{vendor-tanstack-virtual-core-Dxc8hSTB.js → vendor-tanstack-virtual-core-DPaNcJcT.js} +1 -1
  116. package/dist/assets/zh-CN-t2vigSB1.js +20 -0
  117. package/dist/assets/zh-TW-jAgqBokB.js +20 -0
  118. package/dist/index.html +6 -6
  119. package/dist/mini-chat.html +6 -6
  120. package/dist/mobile.html +6 -6
  121. package/package.json +8 -5
  122. package/server/index.js +80 -36
  123. package/server/lib/agent-memory/actions.js +1 -6
  124. package/server/lib/agent-memory/routes.js +1 -2
  125. package/server/lib/agent-memory/runtime.js +1 -8
  126. package/server/lib/agent-tool/DOCUMENTATION.md +7 -3
  127. package/server/lib/agent-tool/runtime.js +3 -6
  128. package/server/lib/agent-tool/runtime.test.js +0 -2
  129. package/server/lib/browser-control/DOCUMENTATION.md +6 -0
  130. package/server/lib/changelog/update-notes.js +1 -1
  131. package/server/lib/context-obligatory/runtime.js +1 -2
  132. package/server/lib/dev-tunnel/DOCUMENTATION.md +8 -0
  133. package/server/lib/dev-tunnel/client.js +21 -10
  134. package/server/lib/dev-tunnel/tunnel.test.js +34 -0
  135. package/server/lib/dispatch-results/runtime.js +1 -1
  136. package/server/lib/enterprise-mode.d.ts +1 -0
  137. package/server/lib/enterprise-mode.js +13 -2
  138. package/server/lib/enterprise-mode.test.js +21 -0
  139. package/server/lib/event-stream/DOCUMENTATION.md +2 -2
  140. package/server/lib/event-stream/index.js +1 -3
  141. package/server/lib/event-stream/translate-v2.js +1 -9
  142. package/server/lib/event-stream/translate-v2.test.js +0 -11
  143. package/server/lib/fs/DOCUMENTATION.md +5 -2
  144. package/server/lib/fs/routes.js +62 -15
  145. package/server/lib/fs/routes.test.js +278 -48
  146. package/server/lib/fs/workspace-file-names.js +104 -0
  147. package/server/lib/fs/workspace-file-names.test.js +62 -0
  148. package/server/lib/git/DOCUMENTATION.md +17 -5
  149. package/server/lib/git/contributor-provenance-storage.js +9 -6
  150. package/server/lib/git/contributor-provenance-storage.test.js +29 -0
  151. package/server/lib/git/credential-broker.js +1 -1
  152. package/server/lib/git/credential-resolver.js +1 -4
  153. package/server/lib/git/credentials.js +1 -0
  154. package/server/lib/git/discovery-endpoint.js +1 -1
  155. package/server/lib/git/identity-provisioning.js +2 -1
  156. package/server/lib/git/identity-storage.js +3 -4
  157. package/server/lib/git/lfs-discovery.js +14 -14
  158. package/server/lib/git/lfs-discovery.test.js +16 -6
  159. package/server/lib/git/network-operation-plan.js +50 -62
  160. package/server/lib/git/network-operation-plan.test.js +103 -1
  161. package/server/lib/git/network-operation-registry.js +4 -6
  162. package/server/lib/git/network-operation-storage.js +4 -6
  163. package/server/lib/git/network-operations.js +12 -4
  164. package/server/lib/git/network-operations.test.js +19 -6
  165. package/server/lib/git/redaction.js +2 -1
  166. package/server/lib/git/repository-credential-runtime.js +5 -3
  167. package/server/lib/git/repository-credential-runtime.test.js +22 -0
  168. package/server/lib/git/routes.js +31 -137
  169. package/server/lib/git/routes.test.js +2 -2
  170. package/server/lib/git/service.js +143 -27
  171. package/server/lib/git/service.test.js +215 -1
  172. package/server/lib/git/ssh-credential-storage.js +1 -4
  173. package/server/lib/git/submodule-discovery.js +1 -1
  174. package/server/lib/git/worktree-bootstrap-storage.js +3 -4
  175. package/server/lib/git/worktree-bootstrap-storage.test.js +15 -0
  176. package/server/lib/git/worktree-creation.js +194 -0
  177. package/server/lib/git/worktree-creation.test.js +155 -0
  178. package/server/lib/github/DOCUMENTATION.md +11 -6
  179. package/server/lib/github/auth.js +2 -4
  180. package/server/lib/github/device-flow.js +2 -2
  181. package/server/lib/github/gh-cli-credential.js +1 -0
  182. package/server/lib/github/octokit.js +31 -0
  183. package/server/lib/github/pr-status.js +35 -18
  184. package/server/lib/github/pr-status.test.js +271 -3
  185. package/server/lib/github/reference-search.js +102 -13
  186. package/server/lib/github/reference-search.test.js +48 -7
  187. package/server/lib/github/routes.auth.test.js +18 -0
  188. package/server/lib/github/routes.js +360 -6
  189. package/server/lib/github/routes.mutations.test.js +239 -0
  190. package/server/lib/github/routes.test.js +134 -2
  191. package/server/lib/gitlab/DOCUMENTATION.md +5 -1
  192. package/server/lib/gitlab/auth-storage.test.js +1 -0
  193. package/server/lib/gitlab/mappers.js +30 -1
  194. package/server/lib/gitlab/resources.js +230 -6
  195. package/server/lib/gitlab/resources.test.js +179 -0
  196. package/server/lib/gitlab/routes.js +200 -6
  197. package/server/lib/gitlab/routes.test.js +186 -2
  198. package/server/lib/gitlab/validation.js +1 -5
  199. package/server/lib/guests/DOCUMENTATION.md +1 -1
  200. package/server/lib/guests/oauth.js +2 -2
  201. package/server/lib/guests/service.js +1 -1
  202. package/server/lib/guests/sockets.js +1 -1
  203. package/server/lib/injected-env.d.ts +8 -0
  204. package/server/lib/injected-env.js +50 -0
  205. package/server/lib/injected-env.test.js +56 -0
  206. package/server/lib/linear/DOCUMENTATION.md +5 -6
  207. package/server/lib/linear/issues.js +62 -4
  208. package/server/lib/linear/issues.test.js +68 -0
  209. package/server/lib/linear/oauth.js +5 -5
  210. package/server/lib/linear/parse.js +2 -9
  211. package/server/lib/message-queue/DOCUMENTATION.md +23 -1
  212. package/server/lib/message-queue/runtime.js +85 -12
  213. package/server/lib/message-queue/runtime.test.js +172 -2
  214. package/server/lib/notifications/apns-runtime.js +5 -6
  215. package/server/lib/notifications/apns-runtime.test.js +20 -4
  216. package/server/lib/openchamber-control/DOCUMENTATION.md +19 -1
  217. package/server/lib/openchamber-control/actions.js +2 -1
  218. package/server/lib/openchamber-control/service.js +119 -20
  219. package/server/lib/openchamber-control/service.test.js +166 -0
  220. package/server/lib/openchamber-sessions/archive-store.js +1 -6
  221. package/server/lib/openchamber-sessions/routes.js +47 -12
  222. package/server/lib/openchamber-sessions/routes.test.js +65 -2
  223. package/server/lib/openchamber-sessions/session-metadata-store.js +1 -6
  224. package/server/lib/opencode/DOCUMENTATION.md +22 -8
  225. package/server/lib/opencode/agents.js +62 -0
  226. package/server/lib/opencode/auth-state-runtime.js +3 -1
  227. package/server/lib/opencode/auth.d.ts +1 -1
  228. package/server/lib/opencode/auth.js +8 -0
  229. package/server/lib/opencode/auth.test.js +31 -0
  230. package/server/lib/opencode/config-entity-routes.js +16 -0
  231. package/server/lib/opencode/config-entity-v2.test.js +16 -1
  232. package/server/lib/opencode/config-paths.test.js +2 -2
  233. package/server/lib/opencode/config-v2.js +2 -4
  234. package/server/lib/opencode/feature-routes-runtime.js +89 -49
  235. package/server/lib/opencode/lifecycle.js +41 -21
  236. package/server/lib/opencode/lifecycle.test.js +50 -2
  237. package/server/lib/opencode/model-discovery.js +1 -1
  238. package/server/lib/opencode/model-discovery.test.js +17 -0
  239. package/server/lib/opencode/models-metadata.js +0 -2
  240. package/server/lib/opencode/openchamber-routes.test.js +3 -3
  241. package/server/lib/opencode/plugins.js +6 -9
  242. package/server/lib/opencode/provider-source-route.test.js +101 -3
  243. package/server/lib/opencode/providers-disabled.test.js +59 -0
  244. package/server/lib/opencode/providers.js +34 -0
  245. package/server/lib/opencode/proxy.js +11 -3
  246. package/server/lib/opencode/routes.js +40 -2
  247. package/server/lib/opencode/settings-helpers.js +27 -1
  248. package/server/lib/opencode/settings-helpers.test.js +19 -7
  249. package/server/lib/opencode/settings-registry.json +27 -16
  250. package/server/lib/opencode/settings-runtime.test.js +18 -0
  251. package/server/lib/opencode/shared-service-runtime.js +23 -104
  252. package/server/lib/opencode/shared.js +77 -46
  253. package/server/lib/opencode/shared.test.js +12 -12
  254. package/server/lib/opencode/shutdown-runtime.test.js +2 -3
  255. package/server/lib/opencode/skill-routes.js +123 -38
  256. package/server/lib/opencode/skill-routes.test.js +201 -0
  257. package/server/lib/opencode/skills.js +81 -34
  258. package/server/lib/opencode/skills.test.js +176 -21
  259. package/server/lib/opencode/startup-pipeline-runtime.js +6 -3
  260. package/server/lib/opencode/startup-pipeline-runtime.test.js +2 -1
  261. package/server/lib/opencode/watcher.test.js +0 -54
  262. package/server/lib/outbound-proxy.js +52 -0
  263. package/server/lib/outbound-proxy.test.js +55 -0
  264. package/server/lib/package-manager.js +23 -17
  265. package/server/lib/package-manager.test.js +5 -18
  266. package/server/lib/permission-auto-accept/DOCUMENTATION.md +1 -1
  267. package/server/lib/permission-auto-accept/runtime.js +19 -47
  268. package/server/lib/permission-auto-accept/runtime.test.js +2 -2
  269. package/server/lib/project-context/DOCUMENTATION.md +7 -1
  270. package/server/lib/project-context/routes.js +1 -2
  271. package/server/lib/project-context/runtime.js +12 -14
  272. package/server/lib/project-context/runtime.test.js +78 -2
  273. package/server/lib/projects/project-config.js +30 -15
  274. package/server/lib/projects/project-config.test.js +30 -0
  275. package/server/lib/projects/project-setup.js +1 -2
  276. package/server/lib/projects/routes.js +1 -2
  277. package/server/lib/quota/DOCUMENTATION.md +35 -7
  278. package/server/lib/quota/index.js +1 -0
  279. package/server/lib/quota/providers/claude/index.js +1 -1
  280. package/server/lib/quota/providers/cline-pass.js +1 -1
  281. package/server/lib/quota/providers/exe-dev.js +1 -1
  282. package/server/lib/quota/providers/google/index.js +1 -1
  283. package/server/lib/quota/providers/hyper.js +1 -1
  284. package/server/lib/quota/providers/kimi.js +116 -36
  285. package/server/lib/quota/providers/kimi.test.js +128 -3
  286. package/server/lib/quota/providers/opencode-go.js +144 -21
  287. package/server/lib/quota/providers/opencode-go.test.js +229 -6
  288. package/server/lib/quota/providers/openrouter.js +1 -1
  289. package/server/lib/quota/providers/zenmux.js +1 -1
  290. package/server/lib/quota/utils/auth.js +1 -0
  291. package/server/lib/quota/utils/transformers.js +2 -6
  292. package/server/lib/relay/DOCUMENTATION.md +18 -3
  293. package/server/lib/relay/e2ee.js +7 -7
  294. package/server/lib/relay/host-client.js +84 -13
  295. package/server/lib/relay/host-client.test.js +201 -1
  296. package/server/lib/relay/identity.js +6 -29
  297. package/server/lib/relay/identity.test.js +18 -28
  298. package/server/lib/relay/key-store.js +141 -0
  299. package/server/lib/relay/key-store.test.js +138 -0
  300. package/server/lib/relay/service.js +8 -8
  301. package/server/lib/relay/service.test.js +10 -5
  302. package/server/lib/relay/signing-key.js +13 -36
  303. package/server/lib/relay/tunnel-codec.js +10 -10
  304. package/server/lib/route-module-exports.test.js +22 -0
  305. package/server/lib/routing/DOCUMENTATION.md +23 -3
  306. package/server/lib/routing/cache.js +87 -0
  307. package/server/lib/routing/defaults.js +11 -0
  308. package/server/lib/routing/history.js +12 -4
  309. package/server/lib/routing/runtime.js +39 -3
  310. package/server/lib/routing/runtime.test.js +81 -2
  311. package/server/lib/routing/store.js +12 -1
  312. package/server/lib/routing/store.test.js +13 -0
  313. package/server/lib/scheduled-tasks/DOCUMENTATION.md +59 -1
  314. package/server/lib/scheduled-tasks/existing-session.js +119 -0
  315. package/server/lib/scheduled-tasks/existing-session.test.js +350 -0
  316. package/server/lib/scheduled-tasks/loops.js +1 -8
  317. package/server/lib/scheduled-tasks/routes.js +8 -8
  318. package/server/lib/scheduled-tasks/runtime.js +103 -13
  319. package/server/lib/scheduled-tasks/runtime.test.js +48 -0
  320. package/server/lib/scheduled-tasks/service.js +33 -11
  321. package/server/lib/scheduled-tasks/service.test.js +128 -2
  322. package/server/lib/scheduled-tasks/session-defaults.js +27 -0
  323. package/server/lib/scheduled-tasks/session-defaults.test.js +19 -0
  324. package/server/lib/session-assist/context.js +1 -0
  325. package/server/lib/session-folders/routes.js +2 -2
  326. package/server/lib/session-goal/objectives.js +1 -1
  327. package/server/lib/session-knowledge/routes.js +2 -2
  328. package/server/lib/session-knowledge/runtime.js +1 -2
  329. package/server/lib/session-work/DOCUMENTATION.md +39 -14
  330. package/server/lib/session-work/questions.js +31 -2
  331. package/server/lib/session-work/runtime.js +22 -13
  332. package/server/lib/session-work/runtime.test.js +43 -26
  333. package/server/lib/session-work/state.js +10 -4
  334. package/server/lib/session-work/state.test.js +8 -1
  335. package/server/lib/shared/DOCUMENTATION.md +89 -0
  336. package/server/lib/shared/guards.js +53 -0
  337. package/server/lib/shared/guards.test.js +112 -0
  338. package/server/lib/skills-catalog/DOCUMENTATION.md +10 -8
  339. package/server/lib/skills-catalog/cache.js +3 -2
  340. package/server/lib/skills-catalog/curated-sources.js +5 -1
  341. package/server/lib/skills-catalog/git.js +107 -20
  342. package/server/lib/skills-catalog/git.test.js +78 -0
  343. package/server/lib/skills-catalog/install.js +21 -12
  344. package/server/lib/skills-catalog/scan.js +25 -15
  345. package/server/lib/skills-catalog/source.js +26 -1
  346. package/server/lib/skills-catalog/source.test.js +27 -0
  347. package/server/lib/source-control/DOCUMENTATION.md +2 -1
  348. package/server/lib/source-control/audit-storage.js +7 -6
  349. package/server/lib/source-control/audit-storage.test.js +11 -0
  350. package/server/lib/source-control/binding-service.js +2 -5
  351. package/server/lib/source-control/change-request-worktree.js +90 -0
  352. package/server/lib/source-control/change-request-worktree.test.js +121 -0
  353. package/server/lib/source-control/mutation-executor.js +1 -1
  354. package/server/lib/source-control/mutation-storage.js +11 -5
  355. package/server/lib/source-control/provider-instance.js +1 -2
  356. package/server/lib/source-control/repository-identity.js +1 -1
  357. package/server/lib/source-control/routes.js +14 -2
  358. package/server/lib/spaces/DOCUMENTATION.md +42 -17
  359. package/server/lib/spaces/command-stream.test.js +15 -0
  360. package/server/lib/spaces/dispatcher.js +7 -0
  361. package/server/lib/spaces/dispatcher.test.js +17 -0
  362. package/server/lib/spaces/hardening.js +2 -2
  363. package/server/lib/spaces/hardening.test.js +1 -1
  364. package/server/lib/spaces/host.js +2 -0
  365. package/server/lib/spaces/journey.js +66 -5
  366. package/server/lib/spaces/journey.test.js +95 -4
  367. package/server/lib/spaces/layout.js +1 -1
  368. package/server/lib/spaces/places/docker-disk.js +38 -14
  369. package/server/lib/spaces/places/docker-disk.test.js +80 -1
  370. package/server/lib/spaces/places/docker.js +35 -11
  371. package/server/lib/spaces/places/fake-docker.js +22 -3
  372. package/server/lib/spaces/places/memory-place.js +3 -0
  373. package/server/lib/spaces/places/packed.docker.live.test.js +1 -1
  374. package/server/lib/spaces/routes.js +5 -0
  375. package/server/lib/spaces/routes.test.js +4 -2
  376. package/server/lib/spaces/run-command.js +9 -2
  377. package/server/lib/spaces/tools.js +4 -5
  378. package/server/lib/spaces/tools.test.js +2 -2
  379. package/server/lib/terminal/runtime.js +18 -6
  380. package/server/lib/terminal/runtime.test.js +66 -0
  381. package/server/lib/tts/index.js +3 -0
  382. package/server/lib/tts/service.js +4 -2
  383. package/server/lib/walkthrough/DOCUMENTATION.md +3 -1
  384. package/server/lib/walkthrough/index.js +2 -3
  385. package/server/lib/walkthrough/pr-source.test.js +22 -0
  386. package/server/lib/walkthrough/sources.js +6 -1
  387. package/server/opencode-proxy.test.js +127 -0
  388. package/bin/lib/commands-startup.test.js +0 -15
  389. package/dist/assets/CodeMirrorCommentWidgets-CVDSjcrP.js +0 -1
  390. package/dist/assets/DiffView-Ct7vL3fD.js +0 -4
  391. package/dist/assets/FilesView-DT0uEYgT.js +0 -17
  392. package/dist/assets/GuestSurfacePane-DVvk1VPu.js +0 -1
  393. package/dist/assets/LinearIssuesView-C10MwA9h.js +0 -1
  394. package/dist/assets/MarkdownImageGallery-BbN_K_3d.js +0 -1
  395. package/dist/assets/MarkdownRendererImpl-BLt2Tkk9.js +0 -10
  396. package/dist/assets/OnboardingScreen-Bgp5A3WB.js +0 -1
  397. package/dist/assets/PierreDiffViewer-CWJLj-S-.js +0 -210
  398. package/dist/assets/PlanView-DsRWnsOI.js +0 -5
  399. package/dist/assets/PublishDialog-CCeKloRu.js +0 -2
  400. package/dist/assets/SettingsView-BuPPsNPp.js +0 -30
  401. package/dist/assets/SettingsWindow-CM5fL8Ot.js +0 -1
  402. package/dist/assets/SubmoduleDiffSummary-CfsMcfiQ.js +0 -1
  403. package/dist/assets/ToolOutputDialog-mZrfSofz.js +0 -15
  404. package/dist/assets/ToolPartDiffPreview-BUGCRYEL.js +0 -8
  405. package/dist/assets/WalkthroughView-ClBo7PPd.js +0 -4
  406. package/dist/assets/appThemeRegistry-CxzMxR-H.js +0 -1
  407. package/dist/assets/de-CBla4hjd.js +0 -14
  408. package/dist/assets/es-BmnZtxTD.js +0 -20
  409. package/dist/assets/fileTreeStatus-BSHFIGEg.js +0 -1
  410. package/dist/assets/fr-OVKIBUZO.js +0 -18
  411. package/dist/assets/index-CKCFKmjF.js +0 -1
  412. package/dist/assets/index-Dbx4V4GN.css +0 -1
  413. package/dist/assets/index-vQ1Pg1cr.js +0 -61
  414. package/dist/assets/ja-6v-yr-gE.js +0 -20
  415. package/dist/assets/ko-I_ZLvgQ9.js +0 -20
  416. package/dist/assets/main-CmO4dRtR.js +0 -2
  417. package/dist/assets/main-b9vqhlYM.js +0 -999
  418. package/dist/assets/markdownCore-BPEZRQfP.js +0 -7
  419. package/dist/assets/miniChat-B5Oi7B-6.js +0 -2
  420. package/dist/assets/mobile-CVtShif9.js +0 -2
  421. package/dist/assets/nl-DcvIJf46.js +0 -20
  422. package/dist/assets/patchFileDiff-CW3wCH1O.js +0 -13
  423. package/dist/assets/pl-JxCUl0sd.js +0 -20
  424. package/dist/assets/pt-BR-L89mjja2.js +0 -20
  425. package/dist/assets/relocateRestoredSession-DYh_9oBa.js +0 -1
  426. package/dist/assets/renderElectronMiniChatApp-BD-1JHSg.js +0 -2
  427. package/dist/assets/renderMobileApp-7s5InXt1.js +0 -25
  428. package/dist/assets/runtimeEndpointReset-DqLJC6L1.js +0 -53
  429. package/dist/assets/shikiHighlight-CXgZnvHh.js +0 -1
  430. package/dist/assets/space-apply-1trGVIl8.js +0 -1
  431. package/dist/assets/space-close-y7j-NuBv.js +0 -1
  432. package/dist/assets/tr-ka5J1flt.js +0 -11
  433. package/dist/assets/uk-PyGe6Q_u.js +0 -20
  434. package/dist/assets/useInlineCommentController-CrjSGCpi.js +0 -2
  435. package/dist/assets/vendor-codemirror-lang-python-B7ClrMIT.js +0 -3
  436. package/dist/assets/vendor-codemirror-lang-xml-C8tGnS64.js +0 -1
  437. package/dist/assets/vendor-codemirror-language-data-q2bKO1EM.js +0 -2
  438. package/dist/assets/vendor-framer-motion-Bq3iTZ6r.js +0 -1
  439. package/dist/assets/vendor-katex-CHD9BdDO.js +0 -257
  440. package/dist/assets/vendor-katex-Cp579UoJ.css +0 -1
  441. package/dist/assets/zh-CN-B0P2tybJ.js +0 -20
  442. package/dist/assets/zh-TW-Bs9C5cN8.js +0 -20
@@ -48,8 +48,72 @@ export const registerSkillRoutes = (app, dependencies) => {
48
48
  fetchGitHubRepoMetas,
49
49
  getProfiles,
50
50
  getProfile,
51
+ createHttpsCredentialReference,
52
+ resolveSourceControlAccount,
53
+ credentialResolver,
51
54
  } = dependencies;
52
55
 
56
+ const resolveCatalogGitIdentity = async (identityId, source) => {
57
+ if (!identityId || identityId === 'global') return { ok: true, identity: null };
58
+
59
+ const profile = getProfile(identityId);
60
+ if (!profile) {
61
+ return { ok: false, error: { kind: 'authRequired', message: 'Selected Git identity is unavailable' } };
62
+ }
63
+
64
+ const transport = profile.transport ?? 'system';
65
+ if (transport === 'system') return { ok: true, identity: null };
66
+ if (transport === 'anonymous') return { ok: true, identity: { anonymous: true, transport } };
67
+
68
+ const parsed = parseSkillRepoSource(source);
69
+ if (!parsed.ok) return { ok: true, identity: null };
70
+
71
+ if (transport === 'ssh') {
72
+ return {
73
+ ok: true,
74
+ identity: {
75
+ transport,
76
+ credentialId: profile.sshCredentialId,
77
+ endpoint: parsed.cloneUrlSsh,
78
+ },
79
+ };
80
+ }
81
+
82
+ if (transport === 'account' && profile.account && resolveSourceControlAccount && createHttpsCredentialReference) {
83
+ const account = await resolveSourceControlAccount(profile.account);
84
+ if (account?.credentialId !== profile.account.accountId
85
+ || account.status !== 'valid'
86
+ || !Number.isSafeInteger(account.credentialRevision)
87
+ || account.credentialRevision < 1) {
88
+ return { ok: false, error: { kind: 'authRequired', message: 'Selected Git identity is unavailable' } };
89
+ }
90
+
91
+ let credentialId;
92
+ try {
93
+ credentialId = createHttpsCredentialReference({
94
+ provider: profile.account.provider,
95
+ instance: profile.account.instance,
96
+ credentialId: account.credentialId,
97
+ credentialRevision: account.credentialRevision,
98
+ providerUserId: account.providerUserId,
99
+ });
100
+ } catch {
101
+ return { ok: false, error: { kind: 'authRequired', message: 'Selected Git identity is unavailable' } };
102
+ }
103
+
104
+ return {
105
+ ok: true,
106
+ identity: {
107
+ transport: 'https',
108
+ credentialId,
109
+ endpoint: parsed.cloneUrlHttps,
110
+ },
111
+ };
112
+ }
113
+
114
+ return { ok: false, error: { kind: 'authRequired', message: 'Selected Git identity is unavailable' } };
115
+ };
116
+
53
117
  const findWorktreeRootForSkills = (workingDirectory) => {
54
118
  if (!workingDirectory) return null;
55
119
  let current = path.resolve(workingDirectory);
@@ -87,7 +151,10 @@ export const registerSkillRoutes = (app, dependencies) => {
87
151
  return normalizedCandidate === normalizedParent || normalizedCandidate.startsWith(`${normalizedParent}${path.sep}`);
88
152
  };
89
153
 
90
- const inferSkillScopeAndSourceFromPath = (skillPath, workingDirectory) => {
154
+ // `projectAncestors` is `getSkillProjectAncestors(workingDirectory)`, worked
155
+ // out once per list: it probes the disk up to the repository root, and a
156
+ // list has hundreds of skills.
157
+ const inferSkillScopeAndSourceFromPath = (skillPath, projectAncestors) => {
91
158
  const resolvedPath = typeof skillPath === 'string' ? path.resolve(skillPath) : '';
92
159
  const home = os.homedir();
93
160
  const source = resolvedPath.includes(`${path.sep}.agents${path.sep}skills${path.sep}`)
@@ -96,7 +163,6 @@ export const registerSkillRoutes = (app, dependencies) => {
96
163
  ? 'claude'
97
164
  : 'opencode';
98
165
 
99
- const projectAncestors = getSkillProjectAncestors(workingDirectory);
100
166
  const isProjectScoped = projectAncestors.some((ancestor) => {
101
167
  const candidates = [
102
168
  path.join(ancestor, '.opencode'),
@@ -151,6 +217,7 @@ export const registerSkillRoutes = (app, dependencies) => {
151
217
  return null;
152
218
  }
153
219
 
220
+ const projectAncestors = getSkillProjectAncestors(workingDirectory);
154
221
  return payload
155
222
  .map((item) => {
156
223
  const name = typeof item?.name === 'string' ? item.name.trim() : '';
@@ -176,7 +243,7 @@ export const registerSkillRoutes = (app, dependencies) => {
176
243
  content,
177
244
  };
178
245
  }
179
- const inferred = inferSkillScopeAndSourceFromPath(location, workingDirectory);
246
+ const inferred = inferSkillScopeAndSourceFromPath(location, projectAncestors);
180
247
  const skill = {
181
248
  name,
182
249
  path: location,
@@ -205,22 +272,6 @@ export const registerSkillRoutes = (app, dependencies) => {
205
272
  }
206
273
  };
207
274
 
208
- const resolveGitIdentity = (profileId) => {
209
- if (!profileId) {
210
- return null;
211
- }
212
- try {
213
- const profile = getProfile(profileId);
214
- const sshKey = profile?.sshKey;
215
- if (typeof sshKey === 'string' && sshKey.trim()) {
216
- return { sshKey: sshKey.trim() };
217
- }
218
- } catch {
219
- // ignore
220
- }
221
- return null;
222
- };
223
-
224
275
  // Prefer an explicit request directory, then soft-fallback to the active
225
276
  // project / lastDirectory so repository-local skills stay visible when the
226
277
  // client omits `directory` (create already used resolveProjectDirectory).
@@ -252,13 +303,18 @@ export const registerSkillRoutes = (app, dependencies) => {
252
303
  return res.status(400).json({ error });
253
304
  }
254
305
  const openCodeSkills = await fetchOpenCodeDiscoveredSkills(directory);
255
- const localSkills = discoverSkills(directory);
306
+ const localSkills = await discoverSkills(directory);
256
307
  const skills = mergeDiscoveredSkills(openCodeSkills ?? [], localSkills);
257
308
 
258
- const enrichedSkills = skills.map((skill) => {
259
- const sources = getSkillSources(skill.name, directory, skill);
309
+ // One skill at a time: each await hands the event loop back, so other
310
+ // requests are served while hundreds of skills are read.
311
+ const enrichedSkills = [];
312
+ for (const skill of skills) {
313
+ // The list carries no supporting files; the skill page reads them
314
+ // from GET /api/config/skills/:name.
315
+ const sources = await getSkillSources(skill.name, directory, skill, { includeSupportingFiles: false });
260
316
  const skillPath = typeof skill.path === 'string' ? skill.path : null;
261
- return {
317
+ enrichedSkills.push({
262
318
  ...skill,
263
319
  sources,
264
320
  renamable: Boolean(
@@ -266,8 +322,8 @@ export const registerSkillRoutes = (app, dependencies) => {
266
322
  && skillPath !== '<built-in>'
267
323
  && isManagedSkillPath(skillPath, directory)
268
324
  ),
269
- };
270
- });
325
+ });
326
+ }
271
327
 
272
328
  // OpenCode decides which external skill roots it loads from process
273
329
  // env, and the browser cannot read that. Report the flags alongside the
@@ -383,7 +439,7 @@ export const registerSkillRoutes = (app, dependencies) => {
383
439
 
384
440
  const resolvedDiscovered = mergeDiscoveredSkills(
385
441
  (await fetchOpenCodeDiscoveredSkills(directory)) ?? [],
386
- discoverSkills(directory),
442
+ await discoverSkills(directory),
387
443
  );
388
444
  const installedByName = new Map(resolvedDiscovered.map((s) => [s.name, s]));
389
445
 
@@ -395,9 +451,17 @@ export const registerSkillRoutes = (app, dependencies) => {
395
451
  const effectiveSubpath = src.defaultSubpath || parsed.effectiveSubpath || null;
396
452
  const cacheKey = getCacheKey({
397
453
  normalizedRepo: parsed.normalizedRepo,
454
+ ref: parsed.ref,
398
455
  subpath: effectiveSubpath || '',
399
456
  identityId: src.gitIdentityId || '',
400
457
  });
458
+ const resolvedIdentity = await resolveCatalogGitIdentity(src.gitIdentityId, src.source);
459
+ if (!resolvedIdentity.ok) {
460
+ return res.status(401).json({
461
+ ok: false,
462
+ error: { ...resolvedIdentity.error, identities: listGitIdentitiesForResponse() },
463
+ });
464
+ }
401
465
 
402
466
  const scanResult = await scanWithCache(
403
467
  cacheKey,
@@ -405,12 +469,19 @@ export const registerSkillRoutes = (app, dependencies) => {
405
469
  source: src.source,
406
470
  subpath: src.defaultSubpath,
407
471
  defaultSubpath: src.defaultSubpath,
408
- identity: resolveGitIdentity(src.gitIdentityId),
472
+ identity: resolvedIdentity.identity,
473
+ credentialResolver,
409
474
  }),
410
475
  { refresh },
411
476
  );
412
477
 
413
478
  if (!scanResult.ok) {
479
+ if (scanResult.error?.kind === 'authRequired') {
480
+ return res.status(401).json({
481
+ ok: false,
482
+ error: { ...scanResult.error, identities: listGitIdentitiesForResponse() },
483
+ });
484
+ }
414
485
  return res.status(500).json({ ok: false, error: scanResult.error });
415
486
  }
416
487
 
@@ -440,12 +511,19 @@ export const registerSkillRoutes = (app, dependencies) => {
440
511
  app.post('/api/config/skills/scan', async (req, res) => {
441
512
  try {
442
513
  const { source, subpath, gitIdentityId } = req.body || {};
443
- const identity = resolveGitIdentity(gitIdentityId);
514
+ const resolvedIdentity = await resolveCatalogGitIdentity(gitIdentityId, source);
515
+ if (!resolvedIdentity.ok) {
516
+ return res.status(401).json({
517
+ ok: false,
518
+ error: { ...resolvedIdentity.error, identities: listGitIdentitiesForResponse() },
519
+ });
520
+ }
444
521
 
445
522
  const result = await scanSkillsRepository({
446
523
  source,
447
524
  subpath,
448
- identity,
525
+ identity: resolvedIdentity.identity,
526
+ credentialResolver,
449
527
  });
450
528
 
451
529
  if (!result.ok) {
@@ -494,12 +572,19 @@ export const registerSkillRoutes = (app, dependencies) => {
494
572
  workingDirectory = resolved.directory;
495
573
  }
496
574
 
497
- const identity = resolveGitIdentity(gitIdentityId);
575
+ const resolvedIdentity = await resolveCatalogGitIdentity(gitIdentityId, source);
576
+ if (!resolvedIdentity.ok) {
577
+ return res.status(401).json({
578
+ ok: false,
579
+ error: { ...resolvedIdentity.error, identities: listGitIdentitiesForResponse() },
580
+ });
581
+ }
498
582
 
499
583
  const result = await installSkillsFromRepository({
500
584
  source,
501
585
  subpath,
502
- identity,
586
+ identity: resolvedIdentity.identity,
587
+ credentialResolver,
503
588
  scope,
504
589
  targetSource,
505
590
  workingDirectory,
@@ -557,7 +642,7 @@ export const registerSkillRoutes = (app, dependencies) => {
557
642
  }
558
643
  const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) ?? [])
559
644
  .find((skill) => skill.name === skillName) || null;
560
- const sources = getSkillSources(skillName, directory, discoveredSkill);
645
+ const sources = await getSkillSources(skillName, directory, discoveredSkill);
561
646
 
562
647
  res.json({
563
648
  name: skillName,
@@ -586,7 +671,7 @@ export const registerSkillRoutes = (app, dependencies) => {
586
671
 
587
672
  const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) ?? [])
588
673
  .find((skill) => skill.name === skillName) || null;
589
- const sources = getSkillSources(skillName, directory, discoveredSkill);
674
+ const sources = await getSkillSources(skillName, directory, discoveredSkill);
590
675
  if (!sources.md.exists || !sources.md.dir) {
591
676
  return res.status(404).json({ error: 'Skill not found' });
592
677
  }
@@ -620,7 +705,7 @@ export const registerSkillRoutes = (app, dependencies) => {
620
705
  console.log('[Server] Creating skill:', skillName);
621
706
  console.log('[Server] Scope:', scope, 'Working directory:', directory);
622
707
 
623
- createSkill(skillName, { ...config, source: skillSource }, directory, scope);
708
+ await createSkill(skillName, { ...config, source: skillSource }, directory, scope);
624
709
  res.json(buildAppliedResponse(
625
710
  `Skill ${skillName} created successfully.`,
626
711
  ));
@@ -643,7 +728,7 @@ export const registerSkillRoutes = (app, dependencies) => {
643
728
  const newName = updates.renameTo.trim();
644
729
  console.log(`[Server] Renaming skill: ${skillName} -> ${newName}`);
645
730
  console.log('[Server] Working directory:', directory);
646
- renameSkill(skillName, newName, directory);
731
+ await renameSkill(skillName, newName, directory);
647
732
  // OpenCode 2 watches the skills directories: the renamed folder is
648
733
  // picked up like any other write, no restart and no client reload.
649
734
  return res.json({
@@ -655,7 +740,7 @@ export const registerSkillRoutes = (app, dependencies) => {
655
740
  console.log(`[Server] Updating skill: ${skillName}`);
656
741
  console.log('[Server] Working directory:', directory);
657
742
 
658
- updateSkill(skillName, updates, directory, updates?.targetPath);
743
+ await updateSkill(skillName, updates, directory, updates?.targetPath);
659
744
  res.json(buildAppliedResponse(
660
745
  `Skill ${skillName} updated successfully.`,
661
746
  ));
@@ -680,7 +765,7 @@ export const registerSkillRoutes = (app, dependencies) => {
680
765
 
681
766
  const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) ?? [])
682
767
  .find((skill) => skill.name === skillName) || null;
683
- const sources = getSkillSources(skillName, directory, discoveredSkill);
768
+ const sources = await getSkillSources(skillName, directory, discoveredSkill);
684
769
  if (!sources.md.exists || !sources.md.dir) {
685
770
  return res.status(404).json({ error: 'Skill not found' });
686
771
  }
@@ -714,7 +799,7 @@ export const registerSkillRoutes = (app, dependencies) => {
714
799
 
715
800
  const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) ?? [])
716
801
  .find((skill) => skill.name === skillName) || null;
717
- const sources = getSkillSources(skillName, directory, discoveredSkill);
802
+ const sources = await getSkillSources(skillName, directory, discoveredSkill);
718
803
  if (!sources.md.exists || !sources.md.dir) {
719
804
  return res.status(404).json({ error: 'Skill not found' });
720
805
  }
@@ -315,6 +315,32 @@ describe('skill-routes directory soft fallback', () => {
315
315
  expect(catalogPayload.sources[0]).not.toHaveProperty('excludedSkills');
316
316
  });
317
317
 
318
+ it('leaves supporting files out of the list and keeps them in the skill detail', async () => {
319
+ projectRoot = createTempProject();
320
+ const skillDir = path.join(projectRoot, '.agents', 'skills', 'files-skill');
321
+ fs.mkdirSync(path.join(skillDir, 'references'), { recursive: true });
322
+ fs.writeFileSync(
323
+ path.join(skillDir, 'SKILL.md'),
324
+ '---\nname: files-skill\ndescription: Has files\n---\nBody\n',
325
+ );
326
+ fs.writeFileSync(path.join(skillDir, 'references', 'guide.md'), '# Guide\n');
327
+
328
+ appHandle = startSkillsApp({ projectRoot });
329
+ const query = `?directory=${encodeURIComponent(projectRoot)}`;
330
+
331
+ const list = await (await fetch(`${appHandle.baseUrl}/api/config/skills${query}`)).json();
332
+ const listed = list.skills.find((skill) => skill.name === 'files-skill');
333
+ expect(listed.sources.md.description).toBe('Has files');
334
+ expect(listed.sources.md).not.toHaveProperty('supportingFiles');
335
+
336
+ const detail = await (await fetch(`${appHandle.baseUrl}/api/config/skills/files-skill${query}`)).json();
337
+ expect(detail.sources.md.supportingFiles).toEqual([{
338
+ name: 'guide.md',
339
+ path: path.join('references', 'guide.md'),
340
+ fullPath: path.join(skillDir, 'references', 'guide.md'),
341
+ }]);
342
+ });
343
+
318
344
  it('flags the list as partial when OpenCode skill list fails, and not when it succeeds', async () => {
319
345
  projectRoot = createTempProject();
320
346
  fs.mkdirSync(path.join(projectRoot, '.agents', 'skills', 'disk-skill'), { recursive: true });
@@ -359,4 +385,179 @@ describe('skill-routes directory soft fallback', () => {
359
385
  stubServer.close();
360
386
  }
361
387
  });
388
+
389
+ it('resolves account-token identities for scan and install', async () => {
390
+ projectRoot = createTempProject();
391
+ const account = { provider: 'github', instance: 'github.com', accountId: 'account-one' };
392
+ const profile = { id: 'work', transport: 'account', account };
393
+ const credentialResolver = { resolve: async () => ({}) };
394
+ const resolved = [];
395
+ const scanResponse = { ok: true, items: [] };
396
+ const installResponse = { ok: true, installed: [], skipped: [] };
397
+ appHandle = startSkillsApp({
398
+ projectRoot,
399
+ overrides: {
400
+ getProfile: () => profile,
401
+ resolveSourceControlAccount: async (input) => {
402
+ expect(input).toEqual(account);
403
+ return {
404
+ credentialId: 'account-one',
405
+ credentialRevision: 3,
406
+ providerUserId: 'github.com#42',
407
+ status: 'valid',
408
+ };
409
+ },
410
+ createHttpsCredentialReference: (input) => {
411
+ expect(input).toEqual({
412
+ provider: 'github',
413
+ instance: 'github.com',
414
+ credentialId: 'account-one',
415
+ credentialRevision: 3,
416
+ providerUserId: 'github.com#42',
417
+ });
418
+ return 'managed-account-reference';
419
+ },
420
+ credentialResolver,
421
+ parseSkillRepoSource: () => ({
422
+ ok: true,
423
+ host: 'github.com',
424
+ cloneUrlSsh: 'git@github.com:owner/private.git',
425
+ cloneUrlHttps: 'https://github.com/owner/private.git',
426
+ }),
427
+ scanSkillsRepository: async (input) => {
428
+ resolved.push(input);
429
+ return scanResponse;
430
+ },
431
+ installSkillsFromRepository: async (input) => {
432
+ resolved.push(input);
433
+ return installResponse;
434
+ },
435
+ },
436
+ });
437
+
438
+ const scan = await fetch(`${appHandle.baseUrl}/api/config/skills/scan`, {
439
+ method: 'POST',
440
+ headers: { 'Content-Type': 'application/json' },
441
+ body: JSON.stringify({ source: 'owner/private', gitIdentityId: 'work' }),
442
+ });
443
+ expect(scan.status).toBe(200);
444
+
445
+ const install = await fetch(`${appHandle.baseUrl}/api/config/skills/install`, {
446
+ method: 'POST',
447
+ headers: { 'Content-Type': 'application/json' },
448
+ body: JSON.stringify({
449
+ source: 'owner/private',
450
+ gitIdentityId: 'work',
451
+ scope: 'user',
452
+ selections: [{ skillDir: 'skills/private-skill' }],
453
+ }),
454
+ });
455
+ expect(install.status).toBe(200);
456
+ expect(resolved).toHaveLength(2);
457
+ for (const input of resolved) {
458
+ expect(input.identity).toEqual({
459
+ transport: 'https',
460
+ credentialId: 'managed-account-reference',
461
+ endpoint: 'https://github.com/owner/private.git',
462
+ });
463
+ expect(input.credentialResolver).toBe(credentialResolver);
464
+ }
465
+ });
466
+
467
+ it('uses the managed SSH identity and endpoint for catalog scans', async () => {
468
+ projectRoot = createTempProject();
469
+ let scanInput;
470
+ appHandle = startSkillsApp({
471
+ projectRoot,
472
+ overrides: {
473
+ getProfile: () => ({
474
+ id: 'deploy',
475
+ transport: 'ssh',
476
+ sshCredentialId: 'ocgit:v1:ssh:key-one',
477
+ }),
478
+ parseSkillRepoSource: () => ({
479
+ ok: true,
480
+ host: 'git.example.com',
481
+ cloneUrlSsh: 'git@git.example.com:owner/private.git',
482
+ cloneUrlHttps: 'https://git.example.com/owner/private.git',
483
+ }),
484
+ scanSkillsRepository: async (input) => {
485
+ scanInput = input;
486
+ return { ok: true, items: [] };
487
+ },
488
+ },
489
+ });
490
+
491
+ const response = await fetch(`${appHandle.baseUrl}/api/config/skills/scan`, {
492
+ method: 'POST',
493
+ headers: { 'Content-Type': 'application/json' },
494
+ body: JSON.stringify({ source: 'git@git.example.com:owner/private.git', gitIdentityId: 'deploy' }),
495
+ });
496
+ expect(response.status).toBe(200);
497
+ expect(scanInput.identity).toEqual({
498
+ transport: 'ssh',
499
+ credentialId: 'ocgit:v1:ssh:key-one',
500
+ endpoint: 'git@git.example.com:owner/private.git',
501
+ });
502
+ });
503
+
504
+ it('keeps the System identity on the server Git configuration', async () => {
505
+ projectRoot = createTempProject();
506
+ let scanInput;
507
+ appHandle = startSkillsApp({
508
+ projectRoot,
509
+ overrides: {
510
+ parseSkillRepoSource: () => ({
511
+ ok: true,
512
+ cloneUrlSsh: 'git@github.com:owner/repo.git',
513
+ cloneUrlHttps: 'https://github.com/owner/repo.git',
514
+ }),
515
+ scanSkillsRepository: async (input) => {
516
+ scanInput = input;
517
+ return { ok: true, items: [] };
518
+ },
519
+ },
520
+ });
521
+
522
+ const response = await fetch(`${appHandle.baseUrl}/api/config/skills/scan`, {
523
+ method: 'POST',
524
+ headers: { 'Content-Type': 'application/json' },
525
+ body: JSON.stringify({ source: 'owner/repo', gitIdentityId: 'global' }),
526
+ });
527
+ expect(response.status).toBe(200);
528
+ expect(scanInput.identity).toBeNull();
529
+ });
530
+
531
+ it('refuses to fall back when the selected account identity is unavailable', async () => {
532
+ projectRoot = createTempProject();
533
+ let scanned = false;
534
+ appHandle = startSkillsApp({
535
+ projectRoot,
536
+ overrides: {
537
+ getProfile: () => ({
538
+ id: 'work',
539
+ transport: 'account',
540
+ account: { provider: 'github', instance: 'github.com', accountId: 'account-one' },
541
+ }),
542
+ resolveSourceControlAccount: async () => null,
543
+ parseSkillRepoSource: () => ({
544
+ ok: true,
545
+ cloneUrlSsh: 'git@github.com:owner/private.git',
546
+ cloneUrlHttps: 'https://github.com/owner/private.git',
547
+ }),
548
+ scanSkillsRepository: async () => {
549
+ scanned = true;
550
+ return { ok: true, items: [] };
551
+ },
552
+ },
553
+ });
554
+
555
+ const response = await fetch(`${appHandle.baseUrl}/api/config/skills/scan`, {
556
+ method: 'POST',
557
+ headers: { 'Content-Type': 'application/json' },
558
+ body: JSON.stringify({ source: 'owner/private', gitIdentityId: 'work' }),
559
+ });
560
+ expect(response.status).toBe(401);
561
+ expect(scanned).toBe(false);
562
+ });
362
563
  });