@thevinci/web 1.0.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 (337) hide show
  1. package/README.md +197 -0
  2. package/bin/cli-entry.js +55 -0
  3. package/bin/cli-output.js +145 -0
  4. package/bin/cli.js +4887 -0
  5. package/bin/cli.test.js +64 -0
  6. package/dist/apple-touch-icon-120x120.png +0 -0
  7. package/dist/apple-touch-icon-152x152.png +0 -0
  8. package/dist/apple-touch-icon-167x167.png +0 -0
  9. package/dist/apple-touch-icon-180x180.png +0 -0
  10. package/dist/apple-touch-icon.png +0 -0
  11. package/dist/apple-touch-icon.svg +528 -0
  12. package/dist/assets/JsonTreeView-CSm9OzXG.js +1 -0
  13. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  14. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  15. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  16. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  17. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  18. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  19. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  20. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  21. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  22. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  23. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  24. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  25. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  26. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  27. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  28. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  29. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  30. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  31. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  32. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  33. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  34. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  35. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  36. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  37. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  38. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  39. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  40. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  41. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  42. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  43. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  44. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  45. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  48. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  49. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  50. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  51. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  52. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  53. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  54. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  55. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  56. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  57. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  58. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  59. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  60. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  61. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  62. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  63. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  64. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  65. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  66. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  67. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  68. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  69. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  70. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  71. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  72. package/dist/assets/MarkdownRendererImpl-DensKOLc.js +6 -0
  73. package/dist/assets/MultiRunWindow-Bo7THayo.js +1 -0
  74. package/dist/assets/OnboardingScreen-BDqmzTVR.js +2 -0
  75. package/dist/assets/SettingsWindow-coz__Ykw.js +1 -0
  76. package/dist/assets/TerminalView-DrZ-i3Dr.js +1 -0
  77. package/dist/assets/ToolOutputDialog-Eglzslt3.js +16 -0
  78. package/dist/assets/es-4o9ciP61.js +15 -0
  79. package/dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  80. package/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  81. package/dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  82. package/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  83. package/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  84. package/dist/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
  85. package/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  86. package/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  87. package/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  88. package/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  89. package/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  90. package/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  91. package/dist/assets/index-DLTDToSP.css +1 -0
  92. package/dist/assets/index-DgiFEKGN.js +1 -0
  93. package/dist/assets/ko-B20imCHE.js +15 -0
  94. package/dist/assets/main-BV3KOtdA.css +1 -0
  95. package/dist/assets/main-CDKJj0sH.js +226 -0
  96. package/dist/assets/main-LC-PSNVM.js +2 -0
  97. package/dist/assets/miniChat-CQUiG_cr.js +2 -0
  98. package/dist/assets/modelPrefsAutoSave-Dm799vzR.js +6986 -0
  99. package/dist/assets/pl-DQJ7LSzj.js +15 -0
  100. package/dist/assets/pt-BR-OmjHUz9y.js +15 -0
  101. package/dist/assets/renderElectronMiniChatApp-CARbeW0G.js +2 -0
  102. package/dist/assets/uk-BNFxOlO4.js +15 -0
  103. package/dist/assets/vendor--DBfsbEis.css +1 -0
  104. package/dist/assets/vendor-.bun-B9l0ZNi2.js +4094 -0
  105. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  106. package/dist/assets/wasmSttWorker-Dtlxac_K.js +1 -0
  107. package/dist/assets/wasmSttWorker-oo7Dm_jy.js +1806 -0
  108. package/dist/assets/worker-CbT6TVo7.js +155 -0
  109. package/dist/assets/zh-CN-C6T-Ac7F.js +15 -0
  110. package/dist/favicon-16.png +0 -0
  111. package/dist/favicon-32.png +0 -0
  112. package/dist/favicon.png +0 -0
  113. package/dist/favicon.svg +528 -0
  114. package/dist/index.html +607 -0
  115. package/dist/logo-dark-192x192.png +0 -0
  116. package/dist/logo-dark-512x512.png +0 -0
  117. package/dist/logo-dark-512x512.svg +528 -0
  118. package/dist/logo-light-192x192.png +0 -0
  119. package/dist/logo-light-512x512.png +0 -0
  120. package/dist/logo-light-512x512.svg +528 -0
  121. package/dist/mini-chat.html +16 -0
  122. package/dist/pwa-192.png +0 -0
  123. package/dist/pwa-512.png +0 -0
  124. package/dist/pwa-maskable-192.png +0 -0
  125. package/dist/pwa-maskable-512.png +0 -0
  126. package/dist/site.webmanifest +21 -0
  127. package/dist/sw.js +1 -0
  128. package/package.json +118 -0
  129. package/public/apple-touch-icon-120x120.png +0 -0
  130. package/public/apple-touch-icon-152x152.png +0 -0
  131. package/public/apple-touch-icon-167x167.png +0 -0
  132. package/public/apple-touch-icon-180x180.png +0 -0
  133. package/public/apple-touch-icon.png +0 -0
  134. package/public/apple-touch-icon.svg +528 -0
  135. package/public/favicon-16.png +0 -0
  136. package/public/favicon-32.png +0 -0
  137. package/public/favicon.png +0 -0
  138. package/public/favicon.svg +528 -0
  139. package/public/logo-dark-192x192.png +0 -0
  140. package/public/logo-dark-512x512.png +0 -0
  141. package/public/logo-dark-512x512.svg +528 -0
  142. package/public/logo-light-192x192.png +0 -0
  143. package/public/logo-light-512x512.png +0 -0
  144. package/public/logo-light-512x512.svg +528 -0
  145. package/public/pwa-192.png +0 -0
  146. package/public/pwa-512.png +0 -0
  147. package/public/pwa-maskable-192.png +0 -0
  148. package/public/pwa-maskable-512.png +0 -0
  149. package/public/site.webmanifest +21 -0
  150. package/server/TERMINAL_WS_PROTOCOL.md +48 -0
  151. package/server/index.d.ts +39 -0
  152. package/server/index.js +1311 -0
  153. package/server/lib/cloudflare-tunnel.js +650 -0
  154. package/server/lib/event-stream/DOCUMENTATION.md +61 -0
  155. package/server/lib/event-stream/directory-ws-bridge.js +185 -0
  156. package/server/lib/event-stream/global-hub.js +158 -0
  157. package/server/lib/event-stream/global-hub.test.js +140 -0
  158. package/server/lib/event-stream/global-ws-bridge.js +206 -0
  159. package/server/lib/event-stream/index.js +25 -0
  160. package/server/lib/event-stream/protocol.js +131 -0
  161. package/server/lib/event-stream/protocol.test.js +182 -0
  162. package/server/lib/event-stream/runtime.js +180 -0
  163. package/server/lib/event-stream/runtime.test.js +512 -0
  164. package/server/lib/event-stream/upstream-reader.js +226 -0
  165. package/server/lib/event-stream/upstream-reader.test.js +276 -0
  166. package/server/lib/fs/DOCUMENTATION.md +36 -0
  167. package/server/lib/fs/routes.js +1040 -0
  168. package/server/lib/fs/search.js +238 -0
  169. package/server/lib/git/DOCUMENTATION.md +152 -0
  170. package/server/lib/git/credentials.js +74 -0
  171. package/server/lib/git/identity-storage.js +112 -0
  172. package/server/lib/git/index.js +6 -0
  173. package/server/lib/git/routes.js +972 -0
  174. package/server/lib/git/service.js +3432 -0
  175. package/server/lib/git/service.test.js +39 -0
  176. package/server/lib/github/DOCUMENTATION.md +171 -0
  177. package/server/lib/github/auth.js +307 -0
  178. package/server/lib/github/device-flow.js +50 -0
  179. package/server/lib/github/index.js +24 -0
  180. package/server/lib/github/octokit.js +10 -0
  181. package/server/lib/github/pr-status.js +519 -0
  182. package/server/lib/github/repo/fork-detection.js +102 -0
  183. package/server/lib/github/repo/index.js +55 -0
  184. package/server/lib/github/routes.js +1560 -0
  185. package/server/lib/magic-prompts/routes.js +63 -0
  186. package/server/lib/magic-prompts/runtime.js +119 -0
  187. package/server/lib/notifications/DOCUMENTATION.md +122 -0
  188. package/server/lib/notifications/emitter-runtime.js +102 -0
  189. package/server/lib/notifications/index.js +4 -0
  190. package/server/lib/notifications/message.js +52 -0
  191. package/server/lib/notifications/message.test.js +34 -0
  192. package/server/lib/notifications/push-runtime.js +304 -0
  193. package/server/lib/notifications/routes.js +315 -0
  194. package/server/lib/notifications/runtime.js +566 -0
  195. package/server/lib/notifications/template-runtime.js +349 -0
  196. package/server/lib/notifications/template-runtime.test.js +26 -0
  197. package/server/lib/opencode/DOCUMENTATION.md +362 -0
  198. package/server/lib/opencode/agents.js +634 -0
  199. package/server/lib/opencode/auth-state-runtime.js +88 -0
  200. package/server/lib/opencode/auth.js +83 -0
  201. package/server/lib/opencode/bootstrap-runtime.js +131 -0
  202. package/server/lib/opencode/cli-entry-runtime.js +43 -0
  203. package/server/lib/opencode/cli-options.js +128 -0
  204. package/server/lib/opencode/commands.js +339 -0
  205. package/server/lib/opencode/config-entity-routes.js +370 -0
  206. package/server/lib/opencode/core-routes.js +500 -0
  207. package/server/lib/opencode/core-routes.test.js +26 -0
  208. package/server/lib/opencode/env-config.js +74 -0
  209. package/server/lib/opencode/env-keys.js +68 -0
  210. package/server/lib/opencode/env-runtime.js +1162 -0
  211. package/server/lib/opencode/env-runtime.test.js +116 -0
  212. package/server/lib/opencode/feature-routes-runtime.js +244 -0
  213. package/server/lib/opencode/hmr-state-runtime.js +85 -0
  214. package/server/lib/opencode/index.js +66 -0
  215. package/server/lib/opencode/lifecycle.js +1019 -0
  216. package/server/lib/opencode/lifecycle.test.js +240 -0
  217. package/server/lib/opencode/mcp.js +278 -0
  218. package/server/lib/opencode/network-runtime.js +104 -0
  219. package/server/lib/opencode/network-runtime.test.js +37 -0
  220. package/server/lib/opencode/opencode-resolution-runtime.js +71 -0
  221. package/server/lib/opencode/path-utils.js +100 -0
  222. package/server/lib/opencode/path-utils.test.js +71 -0
  223. package/server/lib/opencode/project-directory-runtime.js +124 -0
  224. package/server/lib/opencode/project-icon-routes.js +399 -0
  225. package/server/lib/opencode/project-icon-routes.test.js +107 -0
  226. package/server/lib/opencode/providers.js +96 -0
  227. package/server/lib/opencode/proxy.js +445 -0
  228. package/server/lib/opencode/pwa-manifest-routes.js +257 -0
  229. package/server/lib/opencode/pwa-manifest-routes.test.js +133 -0
  230. package/server/lib/opencode/routes.js +541 -0
  231. package/server/lib/opencode/server-startup-runtime.js +156 -0
  232. package/server/lib/opencode/server-utils-runtime.js +168 -0
  233. package/server/lib/opencode/server-utils-runtime.test.js +135 -0
  234. package/server/lib/opencode/session-runtime.js +356 -0
  235. package/server/lib/opencode/session-runtime.test.js +151 -0
  236. package/server/lib/opencode/settings-helpers.js +770 -0
  237. package/server/lib/opencode/settings-helpers.test.js +109 -0
  238. package/server/lib/opencode/settings-normalization-runtime.js +428 -0
  239. package/server/lib/opencode/settings-runtime.js +826 -0
  240. package/server/lib/opencode/settings-runtime.test.js +85 -0
  241. package/server/lib/opencode/shared.js +615 -0
  242. package/server/lib/opencode/shutdown-runtime.js +139 -0
  243. package/server/lib/opencode/shutdown-runtime.test.js +58 -0
  244. package/server/lib/opencode/skill-routes.js +701 -0
  245. package/server/lib/opencode/skills.js +548 -0
  246. package/server/lib/opencode/startup-pipeline-runtime.js +130 -0
  247. package/server/lib/opencode/static-routes-runtime.js +65 -0
  248. package/server/lib/opencode/theme-runtime.js +167 -0
  249. package/server/lib/opencode/tunnel-auth.js +591 -0
  250. package/server/lib/opencode/tunnel-wiring-runtime.js +94 -0
  251. package/server/lib/opencode/vinci-routes.js +76 -0
  252. package/server/lib/opencode/watcher.js +115 -0
  253. package/server/lib/opencode/watcher.test.js +239 -0
  254. package/server/lib/preview/proxy-runtime.js +1333 -0
  255. package/server/lib/preview/proxy-runtime.test.js +144 -0
  256. package/server/lib/projects/project-config.js +567 -0
  257. package/server/lib/projects/project-config.test.js +175 -0
  258. package/server/lib/projects/project-id.js +13 -0
  259. package/server/lib/quota/DOCUMENTATION.md +58 -0
  260. package/server/lib/quota/index.js +25 -0
  261. package/server/lib/quota/providers/claude.js +107 -0
  262. package/server/lib/quota/providers/codex.js +113 -0
  263. package/server/lib/quota/providers/copilot.js +165 -0
  264. package/server/lib/quota/providers/google/api.js +92 -0
  265. package/server/lib/quota/providers/google/auth.js +108 -0
  266. package/server/lib/quota/providers/google/index.js +124 -0
  267. package/server/lib/quota/providers/google/transforms.js +109 -0
  268. package/server/lib/quota/providers/index.js +168 -0
  269. package/server/lib/quota/providers/interface.js +55 -0
  270. package/server/lib/quota/providers/kimi.js +108 -0
  271. package/server/lib/quota/providers/minimax-cn-coding-plan.js +140 -0
  272. package/server/lib/quota/providers/minimax-coding-plan.js +139 -0
  273. package/server/lib/quota/providers/nanogpt.js +124 -0
  274. package/server/lib/quota/providers/ollama-cloud.js +112 -0
  275. package/server/lib/quota/providers/openai.js +91 -0
  276. package/server/lib/quota/providers/openrouter.js +92 -0
  277. package/server/lib/quota/providers/zai.js +91 -0
  278. package/server/lib/quota/providers/zhipuai-coding-plan.js +133 -0
  279. package/server/lib/quota/providers/zhipuai.js +114 -0
  280. package/server/lib/quota/routes.js +27 -0
  281. package/server/lib/quota/utils/auth.js +50 -0
  282. package/server/lib/quota/utils/formatters.js +85 -0
  283. package/server/lib/quota/utils/formatters.test.js +54 -0
  284. package/server/lib/quota/utils/index.js +10 -0
  285. package/server/lib/quota/utils/transformers.js +55 -0
  286. package/server/lib/scheduled-tasks/DOCUMENTATION.md +44 -0
  287. package/server/lib/scheduled-tasks/routes.js +235 -0
  288. package/server/lib/scheduled-tasks/runtime.js +773 -0
  289. package/server/lib/scheduled-tasks/runtime.test.js +100 -0
  290. package/server/lib/security/request-security.js +115 -0
  291. package/server/lib/session-folders/routes.js +63 -0
  292. package/server/lib/session-folders/routes.test.js +102 -0
  293. package/server/lib/skills-catalog/DOCUMENTATION.md +178 -0
  294. package/server/lib/skills-catalog/cache.js +29 -0
  295. package/server/lib/skills-catalog/clawdhub/api.js +158 -0
  296. package/server/lib/skills-catalog/clawdhub/index.js +30 -0
  297. package/server/lib/skills-catalog/clawdhub/install.js +238 -0
  298. package/server/lib/skills-catalog/clawdhub/scan.js +113 -0
  299. package/server/lib/skills-catalog/curated-sources.js +21 -0
  300. package/server/lib/skills-catalog/git.js +77 -0
  301. package/server/lib/skills-catalog/index.js +42 -0
  302. package/server/lib/skills-catalog/install.js +294 -0
  303. package/server/lib/skills-catalog/scan.js +221 -0
  304. package/server/lib/skills-catalog/source.js +87 -0
  305. package/server/lib/terminal/DOCUMENTATION.md +76 -0
  306. package/server/lib/terminal/index.js +31 -0
  307. package/server/lib/terminal/output-replay-buffer.js +78 -0
  308. package/server/lib/terminal/output-replay-buffer.test.js +75 -0
  309. package/server/lib/terminal/runtime.js +850 -0
  310. package/server/lib/terminal/runtime.test.js +96 -0
  311. package/server/lib/terminal/terminal-ws-protocol.js +68 -0
  312. package/server/lib/terminal/terminal-ws-protocol.test.js +145 -0
  313. package/server/lib/text/DOCUMENTATION.md +35 -0
  314. package/server/lib/text/summarization.js +138 -0
  315. package/server/lib/text/summarization.test.js +34 -0
  316. package/server/lib/tts/DOCUMENTATION.md +146 -0
  317. package/server/lib/tts/base-url.js +62 -0
  318. package/server/lib/tts/capability-runtime.js +31 -0
  319. package/server/lib/tts/index.js +19 -0
  320. package/server/lib/tts/routes.js +261 -0
  321. package/server/lib/tts/routes.test.js +53 -0
  322. package/server/lib/tts/service.js +178 -0
  323. package/server/lib/tts/stt.js +75 -0
  324. package/server/lib/tunnels/DOCUMENTATION.md +18 -0
  325. package/server/lib/tunnels/index.js +166 -0
  326. package/server/lib/tunnels/managed-config.js +201 -0
  327. package/server/lib/tunnels/providers/cloudflare.js +260 -0
  328. package/server/lib/tunnels/registry.js +51 -0
  329. package/server/lib/tunnels/routes.js +605 -0
  330. package/server/lib/tunnels/types.js +219 -0
  331. package/server/lib/ui-auth/DOCUMENTATION.md +38 -0
  332. package/server/lib/ui-auth/ui-auth.js +673 -0
  333. package/server/lib/ui-auth/ui-passkeys.js +545 -0
  334. package/server/opencode-proxy.test.js +151 -0
  335. package/server/proxy-headers.js +61 -0
  336. package/server/proxy-headers.test.js +58 -0
  337. package/server/sse-routes.test.js +152 -0
@@ -0,0 +1,238 @@
1
+ const FILE_SEARCH_MAX_CONCURRENCY = 5;
2
+ const FILE_SEARCH_EXCLUDED_DIRS = new Set([
3
+ 'node_modules',
4
+ '.git',
5
+ 'dist',
6
+ 'build',
7
+ '.next',
8
+ '.turbo',
9
+ '.cache',
10
+ 'coverage',
11
+ 'tmp',
12
+ 'logs',
13
+ ]);
14
+
15
+ const normalizeRelativeSearchPath = (rootPath, targetPath, path) => {
16
+ const relative = path.relative(rootPath, targetPath) || path.basename(targetPath);
17
+ return relative.split(path.sep).join('/') || targetPath;
18
+ };
19
+
20
+ const shouldSkipSearchDirectory = (name, includeHidden) => {
21
+ if (!name) {
22
+ return false;
23
+ }
24
+ if (!includeHidden && name.startsWith('.')) {
25
+ return true;
26
+ }
27
+ return FILE_SEARCH_EXCLUDED_DIRS.has(name.toLowerCase());
28
+ };
29
+
30
+ const listDirectoryEntries = async (dirPath, fsPromises) => {
31
+ try {
32
+ return await fsPromises.readdir(dirPath, { withFileTypes: true });
33
+ } catch {
34
+ return [];
35
+ }
36
+ };
37
+
38
+ const fuzzyMatchScoreNormalized = (normalizedQuery, candidate) => {
39
+ if (!normalizedQuery) return 0;
40
+
41
+ const q = normalizedQuery;
42
+ const c = candidate.toLowerCase();
43
+ if (c.includes(q)) {
44
+ const idx = c.indexOf(q);
45
+ let bonus = 0;
46
+ if (idx === 0) {
47
+ bonus = 20;
48
+ } else {
49
+ const prev = c[idx - 1];
50
+ if (prev === '/' || prev === '_' || prev === '-' || prev === '.' || prev === ' ') {
51
+ bonus = 15;
52
+ }
53
+ }
54
+ return 100 + bonus - Math.min(idx, 20) - Math.floor(c.length / 5);
55
+ }
56
+
57
+ let score = 0;
58
+ let lastIndex = -1;
59
+ let consecutive = 0;
60
+
61
+ for (let i = 0; i < q.length; i += 1) {
62
+ const ch = q[i];
63
+ if (!ch || ch === ' ') continue;
64
+
65
+ const idx = c.indexOf(ch, lastIndex + 1);
66
+ if (idx === -1) {
67
+ return null;
68
+ }
69
+
70
+ const gap = idx - lastIndex - 1;
71
+ if (gap === 0) {
72
+ consecutive += 1;
73
+ } else {
74
+ consecutive = 0;
75
+ }
76
+
77
+ score += 10;
78
+ score += Math.max(0, 18 - idx);
79
+ score -= Math.min(gap, 10);
80
+
81
+ if (idx === 0) {
82
+ score += 12;
83
+ } else {
84
+ const prev = c[idx - 1];
85
+ if (prev === '/' || prev === '_' || prev === '-' || prev === '.' || prev === ' ') {
86
+ score += 10;
87
+ }
88
+ }
89
+
90
+ score += consecutive > 0 ? 12 : 0;
91
+ lastIndex = idx;
92
+ }
93
+
94
+ score += Math.max(0, 24 - Math.floor(c.length / 3));
95
+ return score;
96
+ };
97
+
98
+ export const createFsSearchRuntime = ({ fsPromises, path, spawn, resolveGitBinaryForSpawn }) => {
99
+ const searchFilesystemFiles = async (rootPath, options) => {
100
+ const { limit, query, includeHidden, respectGitignore } = options;
101
+ const includeHiddenEntries = Boolean(includeHidden);
102
+ const normalizedQuery = query.trim().toLowerCase();
103
+ const matchAll = normalizedQuery.length === 0;
104
+ const queue = [rootPath];
105
+ const visited = new Set([rootPath]);
106
+ const shouldRespectGitignore = respectGitignore !== false;
107
+ const collectLimit = matchAll ? limit : Math.max(limit * 3, 200);
108
+ const candidates = [];
109
+
110
+ while (queue.length > 0 && candidates.length < collectLimit) {
111
+ const batch = queue.splice(0, FILE_SEARCH_MAX_CONCURRENCY);
112
+
113
+ const dirResults = await Promise.all(
114
+ batch.map(async (dir) => {
115
+ if (!shouldRespectGitignore) {
116
+ return { dir, dirents: await listDirectoryEntries(dir, fsPromises), ignoredPaths: new Set() };
117
+ }
118
+
119
+ try {
120
+ const dirents = await listDirectoryEntries(dir, fsPromises);
121
+ const pathsToCheck = dirents.map((dirent) => dirent.name).filter(Boolean);
122
+ if (pathsToCheck.length === 0) {
123
+ return { dir, dirents, ignoredPaths: new Set() };
124
+ }
125
+
126
+ const result = await new Promise((resolve) => {
127
+ const child = spawn(resolveGitBinaryForSpawn(), ['check-ignore', '--', ...pathsToCheck], {
128
+ cwd: dir,
129
+ windowsHide: true,
130
+ stdio: ['ignore', 'pipe', 'pipe'],
131
+ });
132
+
133
+ let stdout = '';
134
+ child.stdout.on('data', (data) => { stdout += data.toString(); });
135
+ child.on('close', () => resolve(stdout));
136
+ child.on('error', () => resolve(''));
137
+ });
138
+
139
+ const ignoredNames = new Set(
140
+ String(result)
141
+ .split('\n')
142
+ .map((name) => name.trim())
143
+ .filter(Boolean)
144
+ );
145
+
146
+ return { dir, dirents, ignoredPaths: ignoredNames };
147
+ } catch {
148
+ return { dir, dirents: await listDirectoryEntries(dir, fsPromises), ignoredPaths: new Set() };
149
+ }
150
+ })
151
+ );
152
+
153
+ for (const { dir: currentDir, dirents, ignoredPaths } of dirResults) {
154
+ for (const dirent of dirents) {
155
+ const entryName = dirent.name;
156
+ if (!entryName || (!includeHiddenEntries && entryName.startsWith('.'))) {
157
+ continue;
158
+ }
159
+
160
+ if (shouldRespectGitignore && ignoredPaths.has(entryName)) {
161
+ continue;
162
+ }
163
+
164
+ const entryPath = path.join(currentDir, entryName);
165
+
166
+ if (dirent.isDirectory()) {
167
+ if (shouldSkipSearchDirectory(entryName, includeHiddenEntries)) {
168
+ continue;
169
+ }
170
+ if (!visited.has(entryPath)) {
171
+ visited.add(entryPath);
172
+ queue.push(entryPath);
173
+ }
174
+ continue;
175
+ }
176
+
177
+ if (!dirent.isFile()) {
178
+ continue;
179
+ }
180
+
181
+ const relativePath = normalizeRelativeSearchPath(rootPath, entryPath, path);
182
+ const extension = entryName.includes('.') ? entryName.split('.').pop()?.toLowerCase() : undefined;
183
+
184
+ if (matchAll) {
185
+ candidates.push({
186
+ name: entryName,
187
+ path: entryPath,
188
+ relativePath,
189
+ extension,
190
+ score: 0,
191
+ });
192
+ } else {
193
+ const score = fuzzyMatchScoreNormalized(normalizedQuery, relativePath);
194
+ if (score !== null) {
195
+ candidates.push({
196
+ name: entryName,
197
+ path: entryPath,
198
+ relativePath,
199
+ extension,
200
+ score,
201
+ });
202
+ }
203
+ }
204
+
205
+ if (candidates.length >= collectLimit) {
206
+ queue.length = 0;
207
+ break;
208
+ }
209
+ }
210
+
211
+ if (candidates.length >= collectLimit) {
212
+ break;
213
+ }
214
+ }
215
+ }
216
+
217
+ if (!matchAll) {
218
+ candidates.sort((a, b) => {
219
+ if (b.score !== a.score) return b.score - a.score;
220
+ if (a.relativePath.length !== b.relativePath.length) {
221
+ return a.relativePath.length - b.relativePath.length;
222
+ }
223
+ return a.relativePath.localeCompare(b.relativePath);
224
+ });
225
+ }
226
+
227
+ return candidates.slice(0, limit).map(({ name, path: filePath, relativePath, extension }) => ({
228
+ name,
229
+ path: filePath,
230
+ relativePath,
231
+ extension,
232
+ }));
233
+ };
234
+
235
+ return {
236
+ searchFilesystemFiles,
237
+ };
238
+ };
@@ -0,0 +1,152 @@
1
+ # Git Module Documentation
2
+
3
+ ## Purpose
4
+ This module provides Git repository operations for the web server runtime, including repository management, branch/worktree operations, status/diff queries, commit handling, and merge/rebase workflows.
5
+
6
+ ## Entrypoints and structure
7
+ - `packages/web/server/lib/git/`: Git module directory containing all Git-related functionality.
8
+ - `index.js`: Public API entry point imported by `packages/web/server/index.js`.
9
+ - `routes.js`: Express route registration for `/api/git/*` endpoints.
10
+ - `service.js`: Core Git operations (repository, branch, worktree, commit, merge/rebase, status/diff, log).
11
+ - `credentials.js`: Git credentials management.
12
+ - `identity-storage.js`: Git identity (user.name, user.email) storage.
13
+
14
+ ## Public API
15
+
16
+ The following functions are exported and used by the web server:
17
+
18
+ ### Repository Operations
19
+ - `isGitRepository(directory)`: Check if a directory is a Git repository.
20
+ - `getGlobalIdentity()`: Get global Git user.name, user.email, and core.sshCommand.
21
+ - `getCurrentIdentity(directory)`: Get local Git identity (fallback to global if not set locally).
22
+ - `hasLocalIdentity(directory)`: Check if local Git identity is configured.
23
+ - `setLocalIdentity(directory, profile)`: Set local Git identity (userName, userEmail, authType, sshKey/host).
24
+ - `getRemoteUrl(directory, remoteName)`: Get URL for a specific remote.
25
+
26
+ ### Status and Diff Operations
27
+ - `getStatus(directory)`: Get comprehensive Git status including current branch, tracking, ahead/behind, file changes, diff stats, merge/rebase state.
28
+ - `getDiff(directory, { path, staged, contextLines })`: Get diff output for files or entire working tree.
29
+ - `getRangeDiff(directory, { base, head, path, contextLines })`: Get diff between two refs.
30
+ - `getRangeFiles(directory, { base, head })`: Get list of changed files between two refs.
31
+ - `getFileDiff(directory, { path, staged })`: Get original and modified file contents for a single file (handles images as data URLs).
32
+ - `collectDiffs(directory, files)`: Collect diff output for multiple files.
33
+ - `revertFile(directory, filePath)`: Revert a file to HEAD state.
34
+
35
+ ### Branch Operations
36
+ - `getBranches(directory)`: Get list of local and remote branches (filtered to active remote branches).
37
+ - `createBranch(directory, branchName, options)`: Create and checkout a new branch.
38
+ - `checkoutBranch(directory, branchName)`: Checkout an existing branch.
39
+ - `deleteBranch(directory, branch, options)`: Delete a branch (supports force flag).
40
+ - `renameBranch(directory, oldName, newName)`: Rename a branch and preserve upstream tracking.
41
+ - `getRemotes(directory)`: Get list of configured remotes.
42
+
43
+ ### Worktree Operations
44
+ - `getWorktrees(directory)`: List all git worktrees for a repository.
45
+ - `validateWorktreeCreate(directory, input)`: Validate worktree creation parameters (mode, branchName, startRef, upstream config).
46
+ - `createWorktree(directory, input)`: Create a new worktree (supports 'new' and 'existing' modes, upstream setup).
47
+ - `removeWorktree(directory, input)`: Remove a worktree (optionally delete local branch).
48
+ - `isLinkedWorktree(directory)`: Check if directory is a linked worktree (not primary).
49
+
50
+ ### Commit and Remote Operations
51
+ - `commit(directory, message, options)`: Create a commit (supports addAll or specific files).
52
+ - `pull(directory, options)`: Pull changes from remote.
53
+ - `push(directory, options)`: Push changes to remote (auto-sets upstream if needed).
54
+ - `fetch(directory, options)`: Fetch changes from remote.
55
+ - `removeRemote(directory, options)`: Remove a configured remote (except `origin`).
56
+ - `deleteRemoteBranch(directory, options)`: Delete a remote branch.
57
+
58
+ ### Log Operations
59
+ - `getLog(directory, options)`: Get commit history with stats (supports maxCount, from, to, file filters).
60
+ - `getCommitFiles(directory, commitHash)`: Get file changes for a specific commit.
61
+ - `getCommitFileDiff(directory, hash, filePath, isBinary)`: Get before/after content for a specific file in a commit. Returns `{ original, modified, isBinary }`. Runs `git show <hash>^:<path>` and `git show <hash>:<path>` in parallel; returns empty strings on failure (added/deleted/root-commit edge cases).
62
+
63
+ ### Merge and Rebase Operations
64
+ - `rebase(directory, options)`: Start a rebase onto a target branch.
65
+ - `abortRebase(directory)`: Abort an in-progress rebase.
66
+ - `continueRebase(directory)`: Continue a rebase after conflict resolution.
67
+ - `merge(directory, options)`: Merge a branch into current branch.
68
+ - `abortMerge(directory)`: Abort an in-progress merge.
69
+ - `continueMerge(directory)`: Continue a merge after conflict resolution.
70
+ - `getConflictDetails(directory)`: Get detailed conflict information including operation type, unmerged files, and diff.
71
+
72
+ ### Stash Operations
73
+ - `listStashes(directory)`: List stash entries with ref, message, relative time, and hash.
74
+ - `countStashFiles(directory, refs)`: Batch-count changed files for stash refs with bounded concurrency.
75
+ - `stashPush(directory, options)`: Stash changes, always including untracked files, with optional message.
76
+ - `stashApply(directory, options)`: Apply a stash by ref without removing it.
77
+ - `stashPop(directory, options)`: Apply a stash by ref and drop it only after a successful apply.
78
+ - `stashDrop(directory, options)`: Drop a stash by ref.
79
+
80
+ ## Internal Helpers
81
+
82
+ The following functions are internal helpers used by exported functions:
83
+ - `buildSshCommand(sshKeyPath)`: Build SSH command string for git config.
84
+ - `buildGitEnv()`: Build Git environment with SSH_AUTH_SOCK resolution.
85
+ - `createGit(directory)`: Create simple-git instance with environment.
86
+ - `normalizeDirectoryPath(value)`: Normalize directory paths (supports ~ expansion).
87
+ - `cleanBranchName(branch)`: Remove refs/heads/ or refs/ prefixes.
88
+ - `parseWorktreePorcelain(raw)`: Parse `git worktree list --porcelain` output.
89
+ - `resolveWorktreeProjectContext(directory)`: Resolve project context (projectID, primaryWorktree, worktreeRoot).
90
+ - `resolveCandidateDirectory(...)`: Generate unique worktree directory candidates.
91
+ - `resolveBranchForExistingMode(...)`: Resolve branch for existing-mode worktree creation.
92
+ - `applyUpstreamConfiguration(...)`: Set upstream tracking for new branches.
93
+ - And various other internal helpers for Git command execution and parsing.
94
+
95
+ ## Response Contracts
96
+
97
+ ### Status Response
98
+ - `current`: Current branch name.
99
+ - `tracking`: Upstream branch (e.g., 'origin/main').
100
+ - `ahead`: Number of commits ahead of upstream.
101
+ - `behind`: Number of commits behind upstream.
102
+ - `files`: Array of file objects with `path`, `index`, `working_dir` status codes.
103
+ - `isClean`: Boolean indicating if working tree is clean.
104
+ - `diffStats`: Object mapping file paths to `{ insertions, deletions }`.
105
+ - `mergeInProgress`: Object with `{ head, message }` if merge in progress.
106
+ - `rebaseInProgress`: Object with `{ headName, onto }` if rebase in progress.
107
+
108
+ ### Worktree Create/Remove Response
109
+ - `head`: HEAD commit SHA.
110
+ - `name`: Worktree name.
111
+ - `branch`: Local branch name.
112
+ - `path`: Absolute path to worktree directory.
113
+
114
+ ### Log Response
115
+ - `all`: Array of commit objects with hash, date, message, author info, stats.
116
+ - `latest`: Latest commit object or null.
117
+ - `total`: Total number of commits.
118
+
119
+ ## Notes for Contributors
120
+
121
+ ### Adding a New Git Operation
122
+ 1. Add the function to `packages/web/server/lib/git/service.js`.
123
+ 2. Export the function if it's part of the public API.
124
+ 3. Use `createGit(directory)` to get a simple-git instance with the correct environment.
125
+ 4. Use `runGitCommand(cwd, args)` for direct git command execution with better error handling.
126
+ 5. Use `runGitCommandOrThrow(cwd, args, fallbackMessage)` for commands that must succeed.
127
+ 6. Return consistent error messages; use `parseGitErrorText(error)` to extract meaningful git errors.
128
+ 7. Update this file with the new function in the appropriate API section.
129
+
130
+ ### SSH Key Handling
131
+ - SSH keys are escaped and validated via `escapeSshKeyPath` to prevent command injection.
132
+ - On Windows, paths are converted to MSYS format (`C:/path` → `/c/path`).
133
+ - SSH_AUTH_SOCK is automatically resolved via `resolveSshAuthSock` (checks GPG agent, gpgconf).
134
+
135
+ ### Worktree Naming
136
+ - Worktree names are slugified via `slugWorktreeName`.
137
+ - Random names use adjectives/nouns from `OPENCODE_ADJECTIVES` and `OPENCODE_NOUNS` lists.
138
+ - Branches created for new worktrees use `vinci/<worktree-name>` pattern.
139
+
140
+ ### Cross-Platform Considerations
141
+ - Use `normalizeDirectoryPath` for all directory inputs to handle `~` and path separators.
142
+ - Use `canonicalPath` for path comparisons to handle case-insensitive filesystems (Windows).
143
+ - Windows Git commands use MSYS/MinGW paths; avoid direct Windows paths in git commands.
144
+
145
+ ### Error Handling
146
+ - All exported functions should throw errors with descriptive messages.
147
+ - Use `console.error` for logging Git operation failures.
148
+ - Return structured objects for operations that need partial success reporting (e.g., merge/rebase conflicts).
149
+
150
+ ### Testing
151
+ - Run `bun run type-check`, `bun run lint`, and `bun run build` before finalizing changes.
152
+ - Consider edge cases: non-Git directories, missing remotes, conflict states, concurrent worktree operations.
@@ -0,0 +1,74 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ const GIT_CREDENTIALS_PATH = path.join(os.homedir(), '.git-credentials');
6
+
7
+ export function discoverGitCredentials() {
8
+ const credentials = [];
9
+
10
+ if (!fs.existsSync(GIT_CREDENTIALS_PATH)) {
11
+ return credentials;
12
+ }
13
+
14
+ try {
15
+ const content = fs.readFileSync(GIT_CREDENTIALS_PATH, 'utf8');
16
+ const lines = content.split('\n').filter(line => line.trim());
17
+
18
+ for (const line of lines) {
19
+ try {
20
+ const url = new URL(line.trim());
21
+ const hostname = url.hostname;
22
+ const pathname = url.pathname && url.pathname !== '/' ? url.pathname : '';
23
+ const host = hostname + pathname;
24
+ const username = url.username || '';
25
+
26
+ if (host && username) {
27
+ const exists = credentials.some(c => c.host === host && c.username === username);
28
+ if (!exists) {
29
+ credentials.push({ host, username });
30
+ }
31
+ }
32
+ } catch {
33
+ continue;
34
+ }
35
+ }
36
+ } catch (error) {
37
+ console.error('Failed to read .git-credentials:', error);
38
+ }
39
+
40
+ return credentials;
41
+ }
42
+
43
+ export function getCredentialForHost(host) {
44
+ if (!fs.existsSync(GIT_CREDENTIALS_PATH)) {
45
+ return null;
46
+ }
47
+
48
+ try {
49
+ const content = fs.readFileSync(GIT_CREDENTIALS_PATH, 'utf8');
50
+ const lines = content.split('\n').filter(line => line.trim());
51
+
52
+ for (const line of lines) {
53
+ try {
54
+ const url = new URL(line.trim());
55
+ const hostname = url.hostname;
56
+ const pathname = url.pathname && url.pathname !== '/' ? url.pathname : '';
57
+ const credHost = hostname + pathname;
58
+
59
+ if (credHost === host) {
60
+ return {
61
+ username: url.username || '',
62
+ token: url.password || ''
63
+ };
64
+ }
65
+ } catch {
66
+ continue;
67
+ }
68
+ }
69
+ } catch (error) {
70
+ console.error('Failed to read .git-credentials for host lookup:', error);
71
+ }
72
+
73
+ return null;
74
+ }
@@ -0,0 +1,112 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ const STORAGE_DIR = process.env.VINCI_DATA_DIR
6
+ ? path.resolve(process.env.VINCI_DATA_DIR)
7
+ : path.join(os.homedir(), '.vinci');
8
+ const STORAGE_FILE = path.join(STORAGE_DIR, 'git-identities.json');
9
+
10
+ function ensureStorageDir() {
11
+ if (!fs.existsSync(STORAGE_DIR)) {
12
+ fs.mkdirSync(STORAGE_DIR, { recursive: true });
13
+ }
14
+ }
15
+
16
+ export function loadProfiles() {
17
+ ensureStorageDir();
18
+
19
+ if (!fs.existsSync(STORAGE_FILE)) {
20
+ return { profiles: [] };
21
+ }
22
+
23
+ try {
24
+ const content = fs.readFileSync(STORAGE_FILE, 'utf8');
25
+ const data = JSON.parse(content);
26
+ return data;
27
+ } catch (error) {
28
+ console.error('Failed to load git identity profiles:', error);
29
+ return { profiles: [] };
30
+ }
31
+ }
32
+
33
+ export function saveProfiles(data) {
34
+ ensureStorageDir();
35
+
36
+ try {
37
+ fs.writeFileSync(STORAGE_FILE, JSON.stringify(data, null, 2), 'utf8');
38
+ return true;
39
+ } catch (error) {
40
+ console.error('Failed to save git identity profiles:', error);
41
+ throw error;
42
+ }
43
+ }
44
+
45
+ export function getProfiles() {
46
+ const data = loadProfiles();
47
+ return data.profiles || [];
48
+ }
49
+
50
+ export function getProfile(id) {
51
+ const profiles = getProfiles();
52
+ return profiles.find(p => p.id === id) || null;
53
+ }
54
+
55
+ export function createProfile(profileData) {
56
+ const profiles = getProfiles();
57
+
58
+ if (profiles.some(p => p.id === profileData.id)) {
59
+ throw new Error(`Profile with ID "${profileData.id}" already exists`);
60
+ }
61
+
62
+ if (!profileData.id || !profileData.userName || !profileData.userEmail) {
63
+ throw new Error('Profile must have id, userName, and userEmail');
64
+ }
65
+
66
+ const newProfile = {
67
+ id: profileData.id,
68
+ name: profileData.name || profileData.userName,
69
+ userName: profileData.userName,
70
+ userEmail: profileData.userEmail,
71
+ authType: profileData.authType || 'ssh',
72
+ sshKey: profileData.sshKey || null,
73
+ host: profileData.host || null,
74
+ color: profileData.color || 'keyword',
75
+ icon: profileData.icon || 'branch'
76
+ };
77
+
78
+ profiles.push(newProfile);
79
+ saveProfiles({ profiles });
80
+
81
+ return newProfile;
82
+ }
83
+
84
+ export function updateProfile(id, updates) {
85
+ const profiles = getProfiles();
86
+ const index = profiles.findIndex(p => p.id === id);
87
+
88
+ if (index === -1) {
89
+ throw new Error(`Profile with ID "${id}" not found`);
90
+ }
91
+
92
+ profiles[index] = {
93
+ ...profiles[index],
94
+ ...updates,
95
+ id: profiles[index].id
96
+ };
97
+
98
+ saveProfiles({ profiles });
99
+ return profiles[index];
100
+ }
101
+
102
+ export function deleteProfile(id) {
103
+ const profiles = getProfiles();
104
+ const filteredProfiles = profiles.filter(p => p.id !== id);
105
+
106
+ if (filteredProfiles.length === profiles.length) {
107
+ throw new Error(`Profile with ID "${id}" not found`);
108
+ }
109
+
110
+ saveProfiles({ profiles: filteredProfiles });
111
+ return true;
112
+ }
@@ -0,0 +1,6 @@
1
+ // Git library public entrypoint
2
+ // Re-exports all Git operations, credentials, and identity storage functions
3
+
4
+ export * from './service.js';
5
+ export * from './credentials.js';
6
+ export * from './identity-storage.js';