@yancyyu/openhermit 1.5.8

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 (980) hide show
  1. package/LICENSE +611 -0
  2. package/README.md +220 -0
  3. package/bin/hermit.mjs +364 -0
  4. package/bin/kill-dev.js +20 -0
  5. package/dist-renderer/assets/01-BApSFlV4.png +0 -0
  6. package/dist-renderer/assets/02-CRQGs29u.png +0 -0
  7. package/dist-renderer/assets/03-BFCM2jnD.png +0 -0
  8. package/dist-renderer/assets/04-B2FThbKO.png +0 -0
  9. package/dist-renderer/assets/05-D9p0Znkd.png +0 -0
  10. package/dist-renderer/assets/06-DZAfbDlP.png +0 -0
  11. package/dist-renderer/assets/07-B_PXWGCc.png +0 -0
  12. package/dist-renderer/assets/08-DGRMZ6sl.png +0 -0
  13. package/dist-renderer/assets/09-SGCQvc7U.png +0 -0
  14. package/dist-renderer/assets/10-Cve81Q3W.png +0 -0
  15. package/dist-renderer/assets/11-DGglolDW.png +0 -0
  16. package/dist-renderer/assets/12-C3lnu79c.png +0 -0
  17. package/dist-renderer/assets/13-M59meqdw.png +0 -0
  18. package/dist-renderer/assets/ProjectEditorOverlay-BNoDw9T1.js +57 -0
  19. package/dist-renderer/assets/TeamGraphOverlay-CfGRKQIu.js +1 -0
  20. package/dist-renderer/assets/_basePickBy-Ct8Hm5_h.js +1 -0
  21. package/dist-renderer/assets/_baseUniq-BofrAFBx.js +1 -0
  22. package/dist-renderer/assets/apl-B4CMkyY2.js +1 -0
  23. package/dist-renderer/assets/arc-AbJgatzR.js +1 -0
  24. package/dist-renderer/assets/architectureDiagram-VXUJARFQ-gpniCJVk.js +36 -0
  25. package/dist-renderer/assets/asciiarmor-Df11BRmG.js +1 -0
  26. package/dist-renderer/assets/asn1-EdZsLKOL.js +1 -0
  27. package/dist-renderer/assets/asterisk-B-8jnY81.js +1 -0
  28. package/dist-renderer/assets/blockDiagram-VD42YOAC-aBbbmONC.js +122 -0
  29. package/dist-renderer/assets/brainfuck-C4LP7Hcl.js +1 -0
  30. package/dist-renderer/assets/c4Diagram-YG6GDRKO-DJio1IsU.js +10 -0
  31. package/dist-renderer/assets/channel-CZ8sd5Xf.js +1 -0
  32. package/dist-renderer/assets/chunk-4BX2VUAB-D1_HKao2.js +1 -0
  33. package/dist-renderer/assets/chunk-55IACEB6-NAmVxF4k.js +1 -0
  34. package/dist-renderer/assets/chunk-B4BG7PRW-Ce829laz.js +165 -0
  35. package/dist-renderer/assets/chunk-DI55MBZ5-Ct2Le12y.js +220 -0
  36. package/dist-renderer/assets/chunk-FMBD7UC4-Cie3DzKk.js +15 -0
  37. package/dist-renderer/assets/chunk-QN33PNHL-4f5Yb50e.js +1 -0
  38. package/dist-renderer/assets/chunk-QZHKN3VN-D9ranl9c.js +1 -0
  39. package/dist-renderer/assets/chunk-TZMSLE5B-bdGZWlEy.js +1 -0
  40. package/dist-renderer/assets/classDiagram-2ON5EDUG-CMcfSKj5.js +1 -0
  41. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CMcfSKj5.js +1 -0
  42. package/dist-renderer/assets/clike-B9uivgTg.js +1 -0
  43. package/dist-renderer/assets/clojure-BMjYHr_A.js +1 -0
  44. package/dist-renderer/assets/clone-CMuwA8RV.js +1 -0
  45. package/dist-renderer/assets/cmake-BQqOBYOt.js +1 -0
  46. package/dist-renderer/assets/cobol-CWcv1MsR.js +1 -0
  47. package/dist-renderer/assets/coffeescript-S37ZYGWr.js +1 -0
  48. package/dist-renderer/assets/commonlisp-DBKNyK5s.js +1 -0
  49. package/dist-renderer/assets/cose-bilkent-S5V4N54A-C6tvfcVi.js +1 -0
  50. package/dist-renderer/assets/crystal-SjHAIU92.js +1 -0
  51. package/dist-renderer/assets/css-BnMrqG3P.js +1 -0
  52. package/dist-renderer/assets/cypher-C_CwsFkJ.js +1 -0
  53. package/dist-renderer/assets/cytoscape.esm-DsxaTqgk.js +331 -0
  54. package/dist-renderer/assets/d-pRatUO7H.js +1 -0
  55. package/dist-renderer/assets/dagre-6UL2VRFP-B-4qcZam.js +4 -0
  56. package/dist-renderer/assets/defaultLocale-DX6XiGOO.js +1 -0
  57. package/dist-renderer/assets/diagram-PSM6KHXK-CwT3TLjx.js +24 -0
  58. package/dist-renderer/assets/diagram-QEK2KX5R-BWH6-ZFd.js +43 -0
  59. package/dist-renderer/assets/diagram-S2PKOQOG-DfpPnfi1.js +24 -0
  60. package/dist-renderer/assets/diff-DbItnlRl.js +1 -0
  61. package/dist-renderer/assets/dockerfile-BKs6k2Af.js +1 -0
  62. package/dist-renderer/assets/dtd-DF_7sFjM.js +1 -0
  63. package/dist-renderer/assets/dylan-DwRh75JA.js +1 -0
  64. package/dist-renderer/assets/ebnf-CDyGwa7X.js +1 -0
  65. package/dist-renderer/assets/ecl-Cabwm37j.js +1 -0
  66. package/dist-renderer/assets/eiffel-CnydiIhH.js +1 -0
  67. package/dist-renderer/assets/elm-vLlmbW-K.js +1 -0
  68. package/dist-renderer/assets/erDiagram-Q2GNP2WA-BFbEFR4x.js +60 -0
  69. package/dist-renderer/assets/erlang-BNw1qcRV.js +1 -0
  70. package/dist-renderer/assets/factor-kuTfRLto.js +1 -0
  71. package/dist-renderer/assets/favicon-B8xY-GVk.png +0 -0
  72. package/dist-renderer/assets/fcl-Kvtd6kyn.js +1 -0
  73. package/dist-renderer/assets/flowDiagram-NV44I4VS-Dg3cf5hW.js +162 -0
  74. package/dist-renderer/assets/forth-Ffai-XNe.js +1 -0
  75. package/dist-renderer/assets/fortran-DYz_wnZ1.js +1 -0
  76. package/dist-renderer/assets/ganttDiagram-JELNMOA3-B21y55W5.js +267 -0
  77. package/dist-renderer/assets/gas-Bneqetm1.js +1 -0
  78. package/dist-renderer/assets/gherkin-heZmZLOM.js +1 -0
  79. package/dist-renderer/assets/gitGraphDiagram-V2S2FVAM-BDV3BJzn.js +65 -0
  80. package/dist-renderer/assets/graph-BfaZ4hZt.js +1 -0
  81. package/dist-renderer/assets/groovy-D9Dt4D0W.js +1 -0
  82. package/dist-renderer/assets/haskell-BWDZoCOh.js +1 -0
  83. package/dist-renderer/assets/haxe-H-WmDvRZ.js +1 -0
  84. package/dist-renderer/assets/http-DBlCnlav.js +1 -0
  85. package/dist-renderer/assets/idl-BEugSyMb.js +1 -0
  86. package/dist-renderer/assets/index-BMXHMpkG.js +1 -0
  87. package/dist-renderer/assets/index-CCqtDawH.js +1 -0
  88. package/dist-renderer/assets/index-CVMSpK8C.js +1 -0
  89. package/dist-renderer/assets/index-CZltVMDP.js +1844 -0
  90. package/dist-renderer/assets/index-CaG9mf8s.css +1 -0
  91. package/dist-renderer/assets/index-Ct0-y9TF.js +1 -0
  92. package/dist-renderer/assets/index-pMg_LlsS.js +1 -0
  93. package/dist-renderer/assets/infoDiagram-HS3SLOUP-DvMlS0CL.js +2 -0
  94. package/dist-renderer/assets/init-Gi6I4Gst.js +1 -0
  95. package/dist-renderer/assets/javascript-qCveANmP.js +1 -0
  96. package/dist-renderer/assets/journeyDiagram-XKPGCS4Q-DIyMluRv.js +139 -0
  97. package/dist-renderer/assets/julia-DuME0IfC.js +1 -0
  98. package/dist-renderer/assets/kanban-definition-3W4ZIXB7-CVOx8f-7.js +89 -0
  99. package/dist-renderer/assets/katex-DGN8GczM.js +261 -0
  100. package/dist-renderer/assets/layout-BPKIXUf4.js +1 -0
  101. package/dist-renderer/assets/linear-CScZGLr2.js +1 -0
  102. package/dist-renderer/assets/livescript-BwQOo05w.js +1 -0
  103. package/dist-renderer/assets/lua-BgMRiT3U.js +1 -0
  104. package/dist-renderer/assets/mathematica-DTrFuWx2.js +1 -0
  105. package/dist-renderer/assets/mbox-CNhZ1qSd.js +1 -0
  106. package/dist-renderer/assets/mindmap-definition-VGOIOE7T-CmDQ7Wo6.js +68 -0
  107. package/dist-renderer/assets/mirc-CjQqDB4T.js +1 -0
  108. package/dist-renderer/assets/mllike-CXdrOF99.js +1 -0
  109. package/dist-renderer/assets/modelica-Dc1JOy9r.js +1 -0
  110. package/dist-renderer/assets/mscgen-BA5vi2Kp.js +1 -0
  111. package/dist-renderer/assets/mumps-BT43cFF4.js +1 -0
  112. package/dist-renderer/assets/nginx-DdIZxoE0.js +1 -0
  113. package/dist-renderer/assets/nsis-LdVXkNf5.js +1 -0
  114. package/dist-renderer/assets/ntriples-BfvgReVJ.js +1 -0
  115. package/dist-renderer/assets/octave-Ck1zUtKM.js +1 -0
  116. package/dist-renderer/assets/ordinal-Cboi1Yqb.js +1 -0
  117. package/dist-renderer/assets/oz-BzwKVEFT.js +1 -0
  118. package/dist-renderer/assets/pascal--L3eBynH.js +1 -0
  119. package/dist-renderer/assets/perl-CdXCOZ3F.js +1 -0
  120. package/dist-renderer/assets/pieDiagram-ADFJNKIX-DbVClin-.js +30 -0
  121. package/dist-renderer/assets/pig-CevX1Tat.js +1 -0
  122. package/dist-renderer/assets/powershell-CFHJl5sT.js +1 -0
  123. package/dist-renderer/assets/properties-C78fOPTZ.js +1 -0
  124. package/dist-renderer/assets/protobuf-ChK-085T.js +1 -0
  125. package/dist-renderer/assets/pug-DukmZTjD.js +1 -0
  126. package/dist-renderer/assets/puppet-DMA9R1ak.js +1 -0
  127. package/dist-renderer/assets/python-BuPzkPfP.js +1 -0
  128. package/dist-renderer/assets/q-pXgVlZs6.js +1 -0
  129. package/dist-renderer/assets/quadrantDiagram-AYHSOK5B-CAB0MYcW.js +7 -0
  130. package/dist-renderer/assets/r-DUYO_cvP.js +1 -0
  131. package/dist-renderer/assets/requirementDiagram-UZGBJVZJ-w2Lfpg3T.js +64 -0
  132. package/dist-renderer/assets/rpm-CTu-6PCP.js +1 -0
  133. package/dist-renderer/assets/ruby-B2Rjki9n.js +1 -0
  134. package/dist-renderer/assets/sankeyDiagram-TZEHDZUN-kvG1QoKY.js +10 -0
  135. package/dist-renderer/assets/sas-B4kiWyti.js +1 -0
  136. package/dist-renderer/assets/scheme-C41bIUwD.js +1 -0
  137. package/dist-renderer/assets/sequenceDiagram-WL72ISMW-DCVBQ23J.js +145 -0
  138. package/dist-renderer/assets/shell-CjFT_Tl9.js +1 -0
  139. package/dist-renderer/assets/sieve-C3Gn_uJK.js +1 -0
  140. package/dist-renderer/assets/simple-mode-GW_nhZxv.js +1 -0
  141. package/dist-renderer/assets/smalltalk-CnHTOXQT.js +1 -0
  142. package/dist-renderer/assets/solr-DehyRSwq.js +1 -0
  143. package/dist-renderer/assets/sparql-DkYu6x3z.js +1 -0
  144. package/dist-renderer/assets/splashScene-C8lWNnm4.js +1 -0
  145. package/dist-renderer/assets/spreadsheet-BCZA_wO0.js +1 -0
  146. package/dist-renderer/assets/sql-D0XecflT.js +1 -0
  147. package/dist-renderer/assets/stateDiagram-FKZM4ZOC-ItZ0JBvq.js +1 -0
  148. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Hpmw4dMm.js +1 -0
  149. package/dist-renderer/assets/stex-C3f8Ysf7.js +1 -0
  150. package/dist-renderer/assets/stylus-B533Al4x.js +1 -0
  151. package/dist-renderer/assets/swift-BzpIVaGY.js +1 -0
  152. package/dist-renderer/assets/tcl-DVfN8rqt.js +1 -0
  153. package/dist-renderer/assets/textile-CnDTJFAw.js +1 -0
  154. package/dist-renderer/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  155. package/dist-renderer/assets/tiki-DGYXhP31.js +1 -0
  156. package/dist-renderer/assets/timeline-definition-IT6M3QCI-BzSFaAjV.js +61 -0
  157. package/dist-renderer/assets/toml-Bm5Em-hy.js +1 -0
  158. package/dist-renderer/assets/treemap-GDKQZRPO-fSz4hQn0.js +162 -0
  159. package/dist-renderer/assets/troff-wAsdV37c.js +1 -0
  160. package/dist-renderer/assets/ttcn-CfJYG6tj.js +1 -0
  161. package/dist-renderer/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  162. package/dist-renderer/assets/turtle-B1tBg_DP.js +1 -0
  163. package/dist-renderer/assets/vb-CmGdzxic.js +1 -0
  164. package/dist-renderer/assets/vbscript-BuJXcnF6.js +1 -0
  165. package/dist-renderer/assets/velocity-D8B20fx6.js +1 -0
  166. package/dist-renderer/assets/verilog-C6RDOZhf.js +1 -0
  167. package/dist-renderer/assets/vhdl-lSbBsy5d.js +1 -0
  168. package/dist-renderer/assets/webidl-ZXfAyPTL.js +1 -0
  169. package/dist-renderer/assets/xquery-CQfU5ijd.js +1 -0
  170. package/dist-renderer/assets/xychartDiagram-PRI3JC2R-CT1kaGlv.js +7 -0
  171. package/dist-renderer/assets/yacas-BJ4BC0dw.js +1 -0
  172. package/dist-renderer/assets/z80-Hz9HOZM7.js +1 -0
  173. package/dist-renderer/index.html +1274 -0
  174. package/package.json +181 -0
  175. package/src/features/README.md +24 -0
  176. package/src/features/agent-graph/README.md +21 -0
  177. package/src/features/agent-graph/STABLE_SLOT_LAYOUT_PLAN.md +2846 -0
  178. package/src/features/agent-graph/core/domain/buildInlineActivityEntries.ts +416 -0
  179. package/src/features/agent-graph/core/domain/collapseOverflowStacks.ts +126 -0
  180. package/src/features/agent-graph/core/domain/graphOwnerIdentity.ts +55 -0
  181. package/src/features/agent-graph/core/domain/taskGraphSemantics.ts +48 -0
  182. package/src/features/agent-graph/renderer/adapters/TeamGraphAdapter.ts +1400 -0
  183. package/src/features/agent-graph/renderer/hooks/useGraphActivityContext.ts +34 -0
  184. package/src/features/agent-graph/renderer/hooks/useGraphCreateTaskDialog.tsx +126 -0
  185. package/src/features/agent-graph/renderer/hooks/useGraphMemberPopoverContext.ts +34 -0
  186. package/src/features/agent-graph/renderer/hooks/useGraphSidebarVisibility.ts +52 -0
  187. package/src/features/agent-graph/renderer/hooks/useTeamGraphAdapter.ts +173 -0
  188. package/src/features/agent-graph/renderer/hooks/useTeamGraphSurfaceActions.ts +98 -0
  189. package/src/features/agent-graph/renderer/index.ts +14 -0
  190. package/src/features/agent-graph/renderer/ui/GraphActivityCard.tsx +96 -0
  191. package/src/features/agent-graph/renderer/ui/GraphActivityHud.tsx +498 -0
  192. package/src/features/agent-graph/renderer/ui/GraphBlockingEdgePopover.tsx +207 -0
  193. package/src/features/agent-graph/renderer/ui/GraphNodePopover.tsx +573 -0
  194. package/src/features/agent-graph/renderer/ui/GraphProvisioningHud.tsx +113 -0
  195. package/src/features/agent-graph/renderer/ui/GraphTaskCard.tsx +149 -0
  196. package/src/features/agent-graph/renderer/ui/GraphTransientHandoffHud.tsx +176 -0
  197. package/src/features/agent-graph/renderer/ui/TeamGraphOverlay.tsx +224 -0
  198. package/src/features/agent-graph/renderer/ui/TeamGraphTab.tsx +257 -0
  199. package/src/features/agent-graph/renderer/ui/buildTransientHandoffMessage.ts +70 -0
  200. package/src/features/recent-projects/contracts/api.ts +5 -0
  201. package/src/features/recent-projects/contracts/channels.ts +2 -0
  202. package/src/features/recent-projects/contracts/dto.ts +24 -0
  203. package/src/features/recent-projects/contracts/index.ts +4 -0
  204. package/src/features/recent-projects/contracts/normalize.ts +32 -0
  205. package/src/features/recent-projects/core/application/models/ListDashboardRecentProjectsResponse.ts +6 -0
  206. package/src/features/recent-projects/core/application/ports/ClockPort.ts +3 -0
  207. package/src/features/recent-projects/core/application/ports/ListDashboardRecentProjectsOutputPort.ts +5 -0
  208. package/src/features/recent-projects/core/application/ports/LoggerPort.ts +5 -0
  209. package/src/features/recent-projects/core/application/ports/RecentProjectsCachePort.ts +5 -0
  210. package/src/features/recent-projects/core/application/ports/RecentProjectsSourcePort.ts +14 -0
  211. package/src/features/recent-projects/core/application/use-cases/ListDashboardRecentProjectsUseCase.ts +191 -0
  212. package/src/features/recent-projects/core/domain/models/ProviderId.ts +1 -0
  213. package/src/features/recent-projects/core/domain/models/RecentProjectAggregate.ts +14 -0
  214. package/src/features/recent-projects/core/domain/models/RecentProjectCandidate.ts +14 -0
  215. package/src/features/recent-projects/core/domain/models/RecentProjectOpenTarget.ts +3 -0
  216. package/src/features/recent-projects/core/domain/policies/mergeRecentProjectCandidates.ts +88 -0
  217. package/src/features/recent-projects/main/adapters/input/http/registerRecentProjectsHttp.ts +30 -0
  218. package/src/features/recent-projects/main/adapters/output/presenters/DashboardRecentProjectsPresenter.ts +27 -0
  219. package/src/features/recent-projects/main/adapters/output/sources/ClaudeRecentProjectsSourceAdapter.ts +91 -0
  220. package/src/features/recent-projects/main/adapters/output/sources/CodexRecentProjectsSourceAdapter.ts +326 -0
  221. package/src/features/recent-projects/main/composition/createRecentProjectsFeature.ts +43 -0
  222. package/src/features/recent-projects/main/index.ts +3 -0
  223. package/src/features/recent-projects/main/infrastructure/cache/InMemoryRecentProjectsCache.ts +34 -0
  224. package/src/features/recent-projects/main/infrastructure/codex/CodexAppServerClient.ts +116 -0
  225. package/src/features/recent-projects/main/infrastructure/identity/RecentProjectIdentityResolver.ts +20 -0
  226. package/src/features/recent-projects/main/infrastructure/identity/normalizeIdentityPath.ts +10 -0
  227. package/src/features/recent-projects/renderer/adapters/RecentProjectsSectionAdapter.ts +132 -0
  228. package/src/features/recent-projects/renderer/hooks/useOpenRecentProject.ts +143 -0
  229. package/src/features/recent-projects/renderer/hooks/useRecentProjectsSection.ts +289 -0
  230. package/src/features/recent-projects/renderer/index.ts +2 -0
  231. package/src/features/recent-projects/renderer/ui/RecentProjectCard.tsx +221 -0
  232. package/src/features/recent-projects/renderer/ui/RecentProjectsSection.tsx +167 -0
  233. package/src/features/recent-projects/renderer/utils/activeProjectTeams.ts +48 -0
  234. package/src/features/recent-projects/renderer/utils/navigation.ts +65 -0
  235. package/src/features/recent-projects/renderer/utils/projectDecorations.ts +11 -0
  236. package/src/features/recent-projects/renderer/utils/recentProjectOpenHistory.ts +268 -0
  237. package/src/features/recent-projects/renderer/utils/recentProjectsClientCache.ts +78 -0
  238. package/src/main/constants/messageTags.ts +46 -0
  239. package/src/main/constants/worktreePatterns.ts +47 -0
  240. package/src/main/server.ts +3705 -0
  241. package/src/main/services/UpdateService.ts +166 -0
  242. package/src/main/services/ccConnect/CcConnectBridge.ts +313 -0
  243. package/src/main/services/ccConnect/CcConnectClient.ts +397 -0
  244. package/src/main/services/ccConnect/MessageBridge.ts +162 -0
  245. package/src/main/services/ccConnect/ProjectMappingStore.ts +148 -0
  246. package/src/main/services/ccConnect/index.ts +8 -0
  247. package/src/main/services/teams-mvp/TeamProvisioningService.ts +275 -0
  248. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +404 -0
  249. package/src/main/services/teams-mvp/index.ts +26 -0
  250. package/src/main/types/chunks.ts +506 -0
  251. package/src/main/types/domain.ts +342 -0
  252. package/src/main/types/index.ts +24 -0
  253. package/src/main/types/jsonl.ts +355 -0
  254. package/src/main/types/messages.ts +395 -0
  255. package/src/renderer/App.tsx +287 -0
  256. package/src/renderer/api/httpClient.ts +2207 -0
  257. package/src/renderer/api/index.ts +19 -0
  258. package/src/renderer/api/providers.ts +77 -0
  259. package/src/renderer/assets/participant-avatars/01.png +0 -0
  260. package/src/renderer/assets/participant-avatars/02.png +0 -0
  261. package/src/renderer/assets/participant-avatars/03.png +0 -0
  262. package/src/renderer/assets/participant-avatars/04.png +0 -0
  263. package/src/renderer/assets/participant-avatars/05.png +0 -0
  264. package/src/renderer/assets/participant-avatars/06.png +0 -0
  265. package/src/renderer/assets/participant-avatars/07.png +0 -0
  266. package/src/renderer/assets/participant-avatars/08.png +0 -0
  267. package/src/renderer/assets/participant-avatars/09.png +0 -0
  268. package/src/renderer/assets/participant-avatars/10.png +0 -0
  269. package/src/renderer/assets/participant-avatars/11.png +0 -0
  270. package/src/renderer/assets/participant-avatars/12.png +0 -0
  271. package/src/renderer/assets/participant-avatars/13.png +0 -0
  272. package/src/renderer/components/chat/AIChatGroup.tsx +519 -0
  273. package/src/renderer/components/chat/ChatHistory.tsx +1115 -0
  274. package/src/renderer/components/chat/ChatHistoryEmptyState.tsx +15 -0
  275. package/src/renderer/components/chat/ChatHistoryItem.tsx +144 -0
  276. package/src/renderer/components/chat/ChatHistoryLoadingState.tsx +45 -0
  277. package/src/renderer/components/chat/CompactBoundary.tsx +169 -0
  278. package/src/renderer/components/chat/ContextBadge.tsx +582 -0
  279. package/src/renderer/components/chat/DisplayItemList.tsx +431 -0
  280. package/src/renderer/components/chat/LastOutputDisplay.tsx +259 -0
  281. package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/DirectoryTreeNode.tsx +125 -0
  282. package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/buildDirectoryTree.ts +47 -0
  283. package/src/renderer/components/chat/SessionContextPanel/DirectoryTree/types.ts +12 -0
  284. package/src/renderer/components/chat/SessionContextPanel/components/ClaudeMdFilesSection.tsx +90 -0
  285. package/src/renderer/components/chat/SessionContextPanel/components/ClaudeMdSection.tsx +86 -0
  286. package/src/renderer/components/chat/SessionContextPanel/components/CollapsibleSection.tsx +77 -0
  287. package/src/renderer/components/chat/SessionContextPanel/components/FlatInjectionList.tsx +248 -0
  288. package/src/renderer/components/chat/SessionContextPanel/components/MentionedFilesSection.tsx +50 -0
  289. package/src/renderer/components/chat/SessionContextPanel/components/RankedInjectionList.tsx +284 -0
  290. package/src/renderer/components/chat/SessionContextPanel/components/SessionContextHeader.tsx +290 -0
  291. package/src/renderer/components/chat/SessionContextPanel/components/SessionContextHelpTooltip.tsx +165 -0
  292. package/src/renderer/components/chat/SessionContextPanel/components/TaskCoordinationSection.tsx +47 -0
  293. package/src/renderer/components/chat/SessionContextPanel/components/ThinkingTextSection.tsx +47 -0
  294. package/src/renderer/components/chat/SessionContextPanel/components/ToolOutputsSection.tsx +47 -0
  295. package/src/renderer/components/chat/SessionContextPanel/components/UserMessagesSection.tsx +47 -0
  296. package/src/renderer/components/chat/SessionContextPanel/index.tsx +314 -0
  297. package/src/renderer/components/chat/SessionContextPanel/items/ClaudeMdItem.tsx +76 -0
  298. package/src/renderer/components/chat/SessionContextPanel/items/MentionedFileItem.tsx +91 -0
  299. package/src/renderer/components/chat/SessionContextPanel/items/TaskCoordinationItem.tsx +116 -0
  300. package/src/renderer/components/chat/SessionContextPanel/items/ThinkingTextItem.tsx +96 -0
  301. package/src/renderer/components/chat/SessionContextPanel/items/ToolBreakdownItem.tsx +38 -0
  302. package/src/renderer/components/chat/SessionContextPanel/items/ToolOutputItem.tsx +113 -0
  303. package/src/renderer/components/chat/SessionContextPanel/items/UserMessageItem.tsx +69 -0
  304. package/src/renderer/components/chat/SessionContextPanel/types.ts +96 -0
  305. package/src/renderer/components/chat/SessionContextPanel/utils/formatting.ts +6 -0
  306. package/src/renderer/components/chat/SessionContextPanel/utils/pathParsing.ts +23 -0
  307. package/src/renderer/components/chat/SystemChatGroup.tsx +60 -0
  308. package/src/renderer/components/chat/UserChatGroup.tsx +668 -0
  309. package/src/renderer/components/chat/items/BaseItem.tsx +213 -0
  310. package/src/renderer/components/chat/items/ExecutionTrace.tsx +279 -0
  311. package/src/renderer/components/chat/items/LinkedToolItem.tsx +235 -0
  312. package/src/renderer/components/chat/items/MetricsPill.tsx +215 -0
  313. package/src/renderer/components/chat/items/SlashItem.tsx +81 -0
  314. package/src/renderer/components/chat/items/SubagentItem.tsx +592 -0
  315. package/src/renderer/components/chat/items/TeammateMessageItem.tsx +261 -0
  316. package/src/renderer/components/chat/items/TextItem.tsx +82 -0
  317. package/src/renderer/components/chat/items/ThinkingItem.tsx +82 -0
  318. package/src/renderer/components/chat/items/baseItemHelpers.ts +42 -0
  319. package/src/renderer/components/chat/items/linkedTool/CollapsibleOutputSection.tsx +57 -0
  320. package/src/renderer/components/chat/items/linkedTool/DefaultToolViewer.tsx +56 -0
  321. package/src/renderer/components/chat/items/linkedTool/EditToolViewer.tsx +74 -0
  322. package/src/renderer/components/chat/items/linkedTool/ReadToolViewer.tsx +102 -0
  323. package/src/renderer/components/chat/items/linkedTool/SkillToolViewer.tsx +67 -0
  324. package/src/renderer/components/chat/items/linkedTool/ToolErrorDisplay.tsx +43 -0
  325. package/src/renderer/components/chat/items/linkedTool/WriteToolViewer.tsx +66 -0
  326. package/src/renderer/components/chat/items/linkedTool/index.ts +13 -0
  327. package/src/renderer/components/chat/items/linkedTool/renderHelpers.tsx +259 -0
  328. package/src/renderer/components/chat/markdownComponents.tsx +257 -0
  329. package/src/renderer/components/chat/markdownCopyUtils.ts +18 -0
  330. package/src/renderer/components/chat/searchHighlightUtils.ts +166 -0
  331. package/src/renderer/components/chat/viewers/CodeBlockViewer.tsx +244 -0
  332. package/src/renderer/components/chat/viewers/DiffViewer.tsx +459 -0
  333. package/src/renderer/components/chat/viewers/FileLink.tsx +182 -0
  334. package/src/renderer/components/chat/viewers/MarkdownViewer.tsx +1093 -0
  335. package/src/renderer/components/chat/viewers/MermaidDiagram.tsx +116 -0
  336. package/src/renderer/components/chat/viewers/index.ts +3 -0
  337. package/src/renderer/components/chat/viewers/syntaxHighlighter.ts +583 -0
  338. package/src/renderer/components/common/AppLogo.tsx +61 -0
  339. package/src/renderer/components/common/CliInstallWarningBanner.tsx +57 -0
  340. package/src/renderer/components/common/ConfirmDialog.tsx +176 -0
  341. package/src/renderer/components/common/ConnectionStatusBadge.tsx +56 -0
  342. package/src/renderer/components/common/ContextSwitchOverlay.tsx +38 -0
  343. package/src/renderer/components/common/CopyButton.tsx +85 -0
  344. package/src/renderer/components/common/CopyablePath.tsx +68 -0
  345. package/src/renderer/components/common/ErrorBoundary.tsx +211 -0
  346. package/src/renderer/components/common/ExportDropdown.tsx +142 -0
  347. package/src/renderer/components/common/FileTree.tsx +182 -0
  348. package/src/renderer/components/common/GlobalProviderStatusHeader.tsx +316 -0
  349. package/src/renderer/components/common/OngoingIndicator.tsx +67 -0
  350. package/src/renderer/components/common/ProviderBrandLogo.tsx +206 -0
  351. package/src/renderer/components/common/RepositoryDropdown.tsx +230 -0
  352. package/src/renderer/components/common/TokenUsageDisplay.tsx +581 -0
  353. package/src/renderer/components/common/WarningBanner.tsx +25 -0
  354. package/src/renderer/components/common/WorkspaceIndicator.tsx +183 -0
  355. package/src/renderer/components/common/WorktreeBadge.tsx +123 -0
  356. package/src/renderer/components/dashboard/CliStatusBanner.tsx +1845 -0
  357. package/src/renderer/components/dashboard/DashboardView.tsx +274 -0
  358. package/src/renderer/components/extensions/ExtensionStoreView.tsx +591 -0
  359. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +52 -0
  360. package/src/renderer/components/extensions/apikeys/ApiKeyCard.tsx +143 -0
  361. package/src/renderer/components/extensions/apikeys/ApiKeyFormDialog.tsx +282 -0
  362. package/src/renderer/components/extensions/apikeys/ApiKeysPanel.tsx +280 -0
  363. package/src/renderer/components/extensions/common/InstallButton.tsx +186 -0
  364. package/src/renderer/components/extensions/common/InstallCountBadge.tsx +21 -0
  365. package/src/renderer/components/extensions/common/SearchInput.tsx +70 -0
  366. package/src/renderer/components/extensions/common/SourceBadge.tsx +31 -0
  367. package/src/renderer/components/extensions/mcp/CustomMcpServerDialog.tsx +560 -0
  368. package/src/renderer/components/extensions/mcp/McpServerCard.tsx +314 -0
  369. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +669 -0
  370. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +543 -0
  371. package/src/renderer/components/extensions/plugins/CapabilityChips.tsx +70 -0
  372. package/src/renderer/components/extensions/plugins/CategoryChips.tsx +67 -0
  373. package/src/renderer/components/extensions/plugins/PluginCard.tsx +146 -0
  374. package/src/renderer/components/extensions/plugins/PluginDetailDialog.tsx +270 -0
  375. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +436 -0
  376. package/src/renderer/components/extensions/skills/SkillCodeEditor.tsx +117 -0
  377. package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +372 -0
  378. package/src/renderer/components/extensions/skills/SkillEditorDialog.tsx +856 -0
  379. package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +343 -0
  380. package/src/renderer/components/extensions/skills/SkillReviewDialog.tsx +166 -0
  381. package/src/renderer/components/extensions/skills/SkillsPanel.tsx +784 -0
  382. package/src/renderer/components/extensions/skills/skillDraftUtils.ts +234 -0
  383. package/src/renderer/components/extensions/skills/skillFolderNameUtils.ts +19 -0
  384. package/src/renderer/components/extensions/skills/skillProjectUtils.ts +13 -0
  385. package/src/renderer/components/extensions/skills/skillValidationUtils.ts +31 -0
  386. package/src/renderer/components/layout/MiddlePanel.tsx +18 -0
  387. package/src/renderer/components/layout/MoreMenu.tsx +243 -0
  388. package/src/renderer/components/layout/PaneContainer.tsx +27 -0
  389. package/src/renderer/components/layout/PaneContent.tsx +84 -0
  390. package/src/renderer/components/layout/PaneResizeHandle.tsx +85 -0
  391. package/src/renderer/components/layout/PaneSplitDropZone.tsx +54 -0
  392. package/src/renderer/components/layout/PaneView.tsx +75 -0
  393. package/src/renderer/components/layout/SessionTabContent.tsx +102 -0
  394. package/src/renderer/components/layout/Sidebar.tsx +205 -0
  395. package/src/renderer/components/layout/SortableTab.tsx +261 -0
  396. package/src/renderer/components/layout/TabBar.tsx +354 -0
  397. package/src/renderer/components/layout/TabBarActions.tsx +176 -0
  398. package/src/renderer/components/layout/TabBarRow.tsx +99 -0
  399. package/src/renderer/components/layout/TabContextMenu.tsx +171 -0
  400. package/src/renderer/components/layout/TabbedLayout.tsx +186 -0
  401. package/src/renderer/components/layout/TeamTabSectionNav.tsx +146 -0
  402. package/src/renderer/components/notifications/NotificationRow.tsx +228 -0
  403. package/src/renderer/components/notifications/NotificationsView.tsx +371 -0
  404. package/src/renderer/components/report/AssessmentBadge.tsx +78 -0
  405. package/src/renderer/components/report/ReportSection.tsx +58 -0
  406. package/src/renderer/components/report/SessionReportTab.tsx +102 -0
  407. package/src/renderer/components/report/sections/CostSection.tsx +259 -0
  408. package/src/renderer/components/report/sections/ErrorSection.tsx +100 -0
  409. package/src/renderer/components/report/sections/FrictionSection.tsx +91 -0
  410. package/src/renderer/components/report/sections/GitSection.tsx +72 -0
  411. package/src/renderer/components/report/sections/InsightsSection.tsx +207 -0
  412. package/src/renderer/components/report/sections/KeyTakeawaysSection.tsx +55 -0
  413. package/src/renderer/components/report/sections/OverviewSection.tsx +64 -0
  414. package/src/renderer/components/report/sections/QualitySection.tsx +151 -0
  415. package/src/renderer/components/report/sections/SubagentSection.tsx +88 -0
  416. package/src/renderer/components/report/sections/TimelineSection.tsx +111 -0
  417. package/src/renderer/components/report/sections/TokenSection.tsx +116 -0
  418. package/src/renderer/components/report/sections/ToolSection.tsx +77 -0
  419. package/src/renderer/components/runtime/ProviderModelBadges.tsx +142 -0
  420. package/src/renderer/components/runtime/ProviderRuntimeBackendSelector.tsx +327 -0
  421. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +288 -0
  422. package/src/renderer/components/runtime/providerConnectionUi.ts +408 -0
  423. package/src/renderer/components/schedules/SchedulesView.tsx +529 -0
  424. package/src/renderer/components/search/CommandPalette.tsx +610 -0
  425. package/src/renderer/components/search/SearchBar.tsx +171 -0
  426. package/src/renderer/components/settings/NotificationTriggerSettings/components/AddTriggerForm.tsx +233 -0
  427. package/src/renderer/components/settings/NotificationTriggerSettings/components/ColorPaletteSelector.tsx +144 -0
  428. package/src/renderer/components/settings/NotificationTriggerSettings/components/DynamicConfigSection.tsx +189 -0
  429. package/src/renderer/components/settings/NotificationTriggerSettings/components/GeneralInfoSection.tsx +68 -0
  430. package/src/renderer/components/settings/NotificationTriggerSettings/components/IgnorePatternsSection.tsx +73 -0
  431. package/src/renderer/components/settings/NotificationTriggerSettings/components/ModeSelector.tsx +45 -0
  432. package/src/renderer/components/settings/NotificationTriggerSettings/components/RepositoryScopeSection.tsx +63 -0
  433. package/src/renderer/components/settings/NotificationTriggerSettings/components/SectionHeader.tsx +15 -0
  434. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerCard.tsx +150 -0
  435. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerCardHeader.tsx +125 -0
  436. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerConfiguration.tsx +342 -0
  437. package/src/renderer/components/settings/NotificationTriggerSettings/components/TriggerPreview.tsx +108 -0
  438. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useAddTriggerFormHandlers.ts +218 -0
  439. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useAddTriggerFormState.ts +135 -0
  440. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useRepositoryLookup.ts +47 -0
  441. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useTriggerCardState.ts +281 -0
  442. package/src/renderer/components/settings/NotificationTriggerSettings/hooks/useTriggerForm.ts +185 -0
  443. package/src/renderer/components/settings/NotificationTriggerSettings/index.tsx +87 -0
  444. package/src/renderer/components/settings/NotificationTriggerSettings/types.ts +39 -0
  445. package/src/renderer/components/settings/NotificationTriggerSettings/utils/constants.ts +50 -0
  446. package/src/renderer/components/settings/NotificationTriggerSettings/utils/trigger.ts +113 -0
  447. package/src/renderer/components/settings/SettingsTabs.tsx +110 -0
  448. package/src/renderer/components/settings/SettingsView.tsx +153 -0
  449. package/src/renderer/components/settings/components/SettingRow.tsx +44 -0
  450. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +24 -0
  451. package/src/renderer/components/settings/components/SettingsSelect.tsx +100 -0
  452. package/src/renderer/components/settings/components/SettingsToggle.tsx +45 -0
  453. package/src/renderer/components/settings/components/index.ts +8 -0
  454. package/src/renderer/components/settings/hooks/index.ts +6 -0
  455. package/src/renderer/components/settings/hooks/useSettingsConfig.ts +270 -0
  456. package/src/renderer/components/settings/hooks/useSettingsHandlers.ts +468 -0
  457. package/src/renderer/components/settings/sections/AdvancedSection.tsx +234 -0
  458. package/src/renderer/components/settings/sections/CliStatusSection.tsx +930 -0
  459. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +391 -0
  460. package/src/renderer/components/settings/sections/GeneralSection.tsx +665 -0
  461. package/src/renderer/components/settings/sections/HarnessSection.tsx +133 -0
  462. package/src/renderer/components/settings/sections/PlatformsSection.tsx +517 -0
  463. package/src/renderer/components/settings/sections/index.ts +8 -0
  464. package/src/renderer/components/sidebar/DateGroupedSessions.tsx +1115 -0
  465. package/src/renderer/components/sidebar/GlobalTaskList.tsx +853 -0
  466. package/src/renderer/components/sidebar/SessionContextMenu.tsx +182 -0
  467. package/src/renderer/components/sidebar/SessionFiltersPopover.tsx +115 -0
  468. package/src/renderer/components/sidebar/SessionItem.tsx +393 -0
  469. package/src/renderer/components/sidebar/SidebarSessions.tsx +542 -0
  470. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +286 -0
  471. package/src/renderer/components/sidebar/TaskContextMenu.tsx +86 -0
  472. package/src/renderer/components/sidebar/TaskFiltersPopover.tsx +203 -0
  473. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +370 -0
  474. package/src/renderer/components/sidebar/dateGroupedSessionsSelection.ts +33 -0
  475. package/src/renderer/components/sidebar/projectGroupPagination.ts +89 -0
  476. package/src/renderer/components/sidebar/taskFiltersState.ts +82 -0
  477. package/src/renderer/components/splash/splashScene.ts +979 -0
  478. package/src/renderer/components/team/CcSessionsSection.tsx +202 -0
  479. package/src/renderer/components/team/ClaudeLogsDialog.tsx +71 -0
  480. package/src/renderer/components/team/ClaudeLogsFilterPopover.tsx +213 -0
  481. package/src/renderer/components/team/ClaudeLogsPanel.tsx +170 -0
  482. package/src/renderer/components/team/ClaudeLogsSection.tsx +154 -0
  483. package/src/renderer/components/team/CliLogsRichView.tsx +640 -0
  484. package/src/renderer/components/team/CollapsibleTeamSection.tsx +177 -0
  485. package/src/renderer/components/team/HarnessCards.ts +38 -0
  486. package/src/renderer/components/team/MemberBadge.tsx +117 -0
  487. package/src/renderer/components/team/ProcessesSection.tsx +193 -0
  488. package/src/renderer/components/team/ProvisioningProgressBlock.tsx +389 -0
  489. package/src/renderer/components/team/RoleSelect.tsx +171 -0
  490. package/src/renderer/components/team/StepProgressBar.tsx +165 -0
  491. package/src/renderer/components/team/TaskTooltip.tsx +197 -0
  492. package/src/renderer/components/team/TeamDetailView.tsx +3002 -0
  493. package/src/renderer/components/team/TeamEmptyState.tsx +102 -0
  494. package/src/renderer/components/team/TeamListFilterPopover.tsx +183 -0
  495. package/src/renderer/components/team/TeamListView.tsx +1336 -0
  496. package/src/renderer/components/team/TeamProvisioningBanner.tsx +16 -0
  497. package/src/renderer/components/team/TeamProvisioningPanel.tsx +115 -0
  498. package/src/renderer/components/team/TeamSessionsSection.tsx +267 -0
  499. package/src/renderer/components/team/ToolApprovalDiffPreview.tsx +206 -0
  500. package/src/renderer/components/team/ToolApprovalSheet.tsx +675 -0
  501. package/src/renderer/components/team/UnreadCommentsBadge.tsx +37 -0
  502. package/src/renderer/components/team/activity/ActiveTasksBlock.tsx +191 -0
  503. package/src/renderer/components/team/activity/ActivityItem.tsx +1649 -0
  504. package/src/renderer/components/team/activity/ActivityTimeline.tsx +959 -0
  505. package/src/renderer/components/team/activity/AnimatedHeightReveal.tsx +117 -0
  506. package/src/renderer/components/team/activity/LeadThoughtsGroup.tsx +1152 -0
  507. package/src/renderer/components/team/activity/MessageExpandDialog.tsx +213 -0
  508. package/src/renderer/components/team/activity/PendingRepliesBlock.tsx +275 -0
  509. package/src/renderer/components/team/activity/ReplyQuoteBlock.tsx +79 -0
  510. package/src/renderer/components/team/activity/ThoughtBodyContent.tsx +150 -0
  511. package/src/renderer/components/team/activity/activityMarkdown.ts +36 -0
  512. package/src/renderer/components/team/activity/activityMessageContext.ts +68 -0
  513. package/src/renderer/components/team/activity/collapseState.ts +66 -0
  514. package/src/renderer/components/team/activity/useNewItemKeys.ts +70 -0
  515. package/src/renderer/components/team/attachments/AttachmentDisplay.tsx +132 -0
  516. package/src/renderer/components/team/attachments/AttachmentPreviewItem.tsx +62 -0
  517. package/src/renderer/components/team/attachments/AttachmentPreviewList.tsx +193 -0
  518. package/src/renderer/components/team/attachments/AttachmentThumbnail.tsx +42 -0
  519. package/src/renderer/components/team/attachments/DropZoneOverlay.tsx +54 -0
  520. package/src/renderer/components/team/attachments/ImageLightbox.tsx +132 -0
  521. package/src/renderer/components/team/attachments/SourceMessageAttachments.tsx +70 -0
  522. package/src/renderer/components/team/dialogs/AddMemberDialog.tsx +222 -0
  523. package/src/renderer/components/team/dialogs/AdvancedCliSection.tsx +347 -0
  524. package/src/renderer/components/team/dialogs/AnthropicFastModeSelector.tsx +120 -0
  525. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +489 -0
  526. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +484 -0
  527. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +448 -0
  528. package/src/renderer/components/team/dialogs/EffortLevelSelector.tsx +69 -0
  529. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +165 -0
  530. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +2859 -0
  531. package/src/renderer/components/team/dialogs/LimitContextCheckbox.tsx +57 -0
  532. package/src/renderer/components/team/dialogs/MembersJsonEditor.tsx +123 -0
  533. package/src/renderer/components/team/dialogs/OptionalSettingsSection.tsx +124 -0
  534. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +145 -0
  535. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +289 -0
  536. package/src/renderer/components/team/dialogs/ProjectPathSelector.tsx +330 -0
  537. package/src/renderer/components/team/dialogs/ProvisioningProviderStatusList.tsx +744 -0
  538. package/src/renderer/components/team/dialogs/ReviewDialog.tsx +130 -0
  539. package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +530 -0
  540. package/src/renderer/components/team/dialogs/SkipPermissionsCheckbox.tsx +62 -0
  541. package/src/renderer/components/team/dialogs/StatusHistoryTimeline.tsx +229 -0
  542. package/src/renderer/components/team/dialogs/TaskAttachments.tsx +394 -0
  543. package/src/renderer/components/team/dialogs/TaskCommentAwaitingReply.tsx +57 -0
  544. package/src/renderer/components/team/dialogs/TaskCommentInput.tsx +420 -0
  545. package/src/renderer/components/team/dialogs/TaskCommentsSection.tsx +620 -0
  546. package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +1480 -0
  547. package/src/renderer/components/team/dialogs/TeamModelSelector.tsx +560 -0
  548. package/src/renderer/components/team/dialogs/TeammateRuntimeCompatibilityNotice.tsx +60 -0
  549. package/src/renderer/components/team/dialogs/ToolApprovalSettingsPanel.tsx +173 -0
  550. package/src/renderer/components/team/dialogs/editTeamRuntimeChanges.ts +179 -0
  551. package/src/renderer/components/team/dialogs/globalTaskDetailDialogLoading.ts +34 -0
  552. package/src/renderer/components/team/dialogs/launchDialogPrefill.ts +125 -0
  553. package/src/renderer/components/team/dialogs/memberModelScope.ts +77 -0
  554. package/src/renderer/components/team/dialogs/platformMeta.ts +118 -0
  555. package/src/renderer/components/team/dialogs/projectPathOptions.ts +50 -0
  556. package/src/renderer/components/team/dialogs/providerPrepareDiagnostics.ts +222 -0
  557. package/src/renderer/components/team/dialogs/providerPrepareRequestSignature.ts +122 -0
  558. package/src/renderer/components/team/dialogs/provisioningMemberScope.ts +10 -0
  559. package/src/renderer/components/team/dialogs/provisioningModelIssues.ts +124 -0
  560. package/src/renderer/components/team/dialogs/teamNameSets.ts +67 -0
  561. package/src/renderer/components/team/dialogs/teamRelaunchFlow.ts +30 -0
  562. package/src/renderer/components/team/dialogs/teammateLaunchMode.ts +49 -0
  563. package/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +101 -0
  564. package/src/renderer/components/team/editor/CodeMirrorEditor.tsx +506 -0
  565. package/src/renderer/components/team/editor/EditorBinaryPlaceholder.tsx +43 -0
  566. package/src/renderer/components/team/editor/EditorBinaryState.tsx +29 -0
  567. package/src/renderer/components/team/editor/EditorBreadcrumb.tsx +84 -0
  568. package/src/renderer/components/team/editor/EditorContextMenu.tsx +213 -0
  569. package/src/renderer/components/team/editor/EditorEmptyState.tsx +35 -0
  570. package/src/renderer/components/team/editor/EditorErrorBoundary.tsx +63 -0
  571. package/src/renderer/components/team/editor/EditorErrorState.tsx +41 -0
  572. package/src/renderer/components/team/editor/EditorFileTree.tsx +903 -0
  573. package/src/renderer/components/team/editor/EditorImagePreview.tsx +138 -0
  574. package/src/renderer/components/team/editor/EditorSearchPanel.tsx +508 -0
  575. package/src/renderer/components/team/editor/EditorSelectionMenu.tsx +112 -0
  576. package/src/renderer/components/team/editor/EditorShortcutsHelp.tsx +125 -0
  577. package/src/renderer/components/team/editor/EditorStatusBar.tsx +72 -0
  578. package/src/renderer/components/team/editor/EditorTabBar.tsx +265 -0
  579. package/src/renderer/components/team/editor/EditorTabContextMenu.tsx +88 -0
  580. package/src/renderer/components/team/editor/EditorToolbar.tsx +163 -0
  581. package/src/renderer/components/team/editor/FileIcon.tsx +66 -0
  582. package/src/renderer/components/team/editor/GitStatusBadge.tsx +47 -0
  583. package/src/renderer/components/team/editor/GoToLineDialog.tsx +186 -0
  584. package/src/renderer/components/team/editor/MarkdownPreviewPane.tsx +57 -0
  585. package/src/renderer/components/team/editor/MarkdownSplitView.tsx +127 -0
  586. package/src/renderer/components/team/editor/NewFileDialog.tsx +131 -0
  587. package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +924 -0
  588. package/src/renderer/components/team/editor/QuickOpenDialog.tsx +163 -0
  589. package/src/renderer/components/team/editor/SearchInFilesPanel.tsx +358 -0
  590. package/src/renderer/components/team/editor/fileIcons.ts +222 -0
  591. package/src/renderer/components/team/kanban/KanbanBoard.tsx +664 -0
  592. package/src/renderer/components/team/kanban/KanbanColumn.tsx +61 -0
  593. package/src/renderer/components/team/kanban/KanbanFilterPopover.tsx +210 -0
  594. package/src/renderer/components/team/kanban/KanbanGridLayout.tsx +460 -0
  595. package/src/renderer/components/team/kanban/KanbanSearchInput.tsx +284 -0
  596. package/src/renderer/components/team/kanban/KanbanSortPopover.tsx +140 -0
  597. package/src/renderer/components/team/kanban/KanbanTaskCard.test.tsx +199 -0
  598. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +446 -0
  599. package/src/renderer/components/team/kanban/TrashDialog.tsx +112 -0
  600. package/src/renderer/components/team/members/CurrentTaskIndicator.tsx +56 -0
  601. package/src/renderer/components/team/members/LeadModelRow.test.tsx +133 -0
  602. package/src/renderer/components/team/members/LeadModelRow.tsx +183 -0
  603. package/src/renderer/components/team/members/MemberCard.tsx +665 -0
  604. package/src/renderer/components/team/members/MemberDetailDialog.tsx +309 -0
  605. package/src/renderer/components/team/members/MemberDetailHeader.tsx +183 -0
  606. package/src/renderer/components/team/members/MemberDetailStats.tsx +80 -0
  607. package/src/renderer/components/team/members/MemberDraftRow.test.tsx +184 -0
  608. package/src/renderer/components/team/members/MemberDraftRow.tsx +515 -0
  609. package/src/renderer/components/team/members/MemberExecutionLog.tsx +224 -0
  610. package/src/renderer/components/team/members/MemberHoverCard.tsx +292 -0
  611. package/src/renderer/components/team/members/MemberLaunchDiagnosticsButton.tsx +60 -0
  612. package/src/renderer/components/team/members/MemberList.tsx +405 -0
  613. package/src/renderer/components/team/members/MemberLogsTab.tsx +958 -0
  614. package/src/renderer/components/team/members/MemberMessagesTab.tsx +251 -0
  615. package/src/renderer/components/team/members/MemberPresenceDot.tsx +28 -0
  616. package/src/renderer/components/team/members/MemberRoleEditor.tsx +84 -0
  617. package/src/renderer/components/team/members/MemberStatsTab.tsx +299 -0
  618. package/src/renderer/components/team/members/MemberTasksTab.tsx +87 -0
  619. package/src/renderer/components/team/members/MemberWorkspaceTab.tsx +141 -0
  620. package/src/renderer/components/team/members/MembersEditorSection.tsx +495 -0
  621. package/src/renderer/components/team/members/SubagentRecentMessagesPreview.tsx +125 -0
  622. package/src/renderer/components/team/members/TeamRosterEditorSection.tsx +153 -0
  623. package/src/renderer/components/team/members/memberActivityEntries.ts +42 -0
  624. package/src/renderer/components/team/members/memberDetailTypes.ts +3 -0
  625. package/src/renderer/components/team/members/memberNameSets.ts +65 -0
  626. package/src/renderer/components/team/members/membersEditorTypes.ts +21 -0
  627. package/src/renderer/components/team/members/membersEditorUtils.ts +267 -0
  628. package/src/renderer/components/team/messages/MessageComposer.tsx +939 -0
  629. package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +228 -0
  630. package/src/renderer/components/team/messages/MessagesPanel.tsx +1508 -0
  631. package/src/renderer/components/team/messages/OpenCodeDeliveryWarning.tsx +151 -0
  632. package/src/renderer/components/team/messages/StatusBlock.tsx +126 -0
  633. package/src/renderer/components/team/provisioningSteps.ts +363 -0
  634. package/src/renderer/components/team/review/ChangeReviewDialog.tsx +133 -0
  635. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +218 -0
  636. package/src/renderer/components/team/schedule/CronScheduleInput.tsx +254 -0
  637. package/src/renderer/components/team/schedule/ScheduleEmptyState.tsx +15 -0
  638. package/src/renderer/components/team/schedule/ScheduleRunLogDialog.tsx +277 -0
  639. package/src/renderer/components/team/schedule/ScheduleRunRow.tsx +106 -0
  640. package/src/renderer/components/team/schedule/ScheduleSection.tsx +281 -0
  641. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +55 -0
  642. package/src/renderer/components/team/sidebar/TeamSidebarHost.tsx +76 -0
  643. package/src/renderer/components/team/sidebar/TeamSidebarPortalManager.ts +173 -0
  644. package/src/renderer/components/team/sidebar/TeamSidebarPortalSource.tsx +66 -0
  645. package/src/renderer/components/team/sidebar/TeamSidebarRail.tsx +68 -0
  646. package/src/renderer/components/team/sidebar/teamSidebarUiState.ts +136 -0
  647. package/src/renderer/components/team/taskLogs/ExactTaskLogCard.tsx +132 -0
  648. package/src/renderer/components/team/taskLogs/ExactTaskLogsSection.tsx +258 -0
  649. package/src/renderer/components/team/taskLogs/ExecutionSessionsSection.tsx +48 -0
  650. package/src/renderer/components/team/taskLogs/TaskActivityLinkedToolCard.tsx +31 -0
  651. package/src/renderer/components/team/taskLogs/TaskActivitySection.tsx +462 -0
  652. package/src/renderer/components/team/taskLogs/TaskLogStreamSection.tsx +375 -0
  653. package/src/renderer/components/team/taskLogs/TaskLogsPanel.tsx +294 -0
  654. package/src/renderer/components/team/taskLogs/featureGates.ts +22 -0
  655. package/src/renderer/components/team/tasks/TaskList.tsx +111 -0
  656. package/src/renderer/components/team/tasks/TaskRow.tsx +65 -0
  657. package/src/renderer/components/team/teamProjectSelection.ts +156 -0
  658. package/src/renderer/components/team/teamSessionFetchGuards.ts +26 -0
  659. package/src/renderer/components/team/useClaudeLogsController.ts +668 -0
  660. package/src/renderer/components/team/useTeamProvisioningPresentation.ts +54 -0
  661. package/src/renderer/components/terminal/TerminalLogPanel.tsx +37 -0
  662. package/src/renderer/components/ui/ChipInteractionLayer.tsx +255 -0
  663. package/src/renderer/components/ui/CodeChipBadge.tsx +37 -0
  664. package/src/renderer/components/ui/ExpandableContent.tsx +110 -0
  665. package/src/renderer/components/ui/MemberSelect.tsx +209 -0
  666. package/src/renderer/components/ui/MentionInteractionLayer.tsx +121 -0
  667. package/src/renderer/components/ui/MentionSuggestionList.tsx +274 -0
  668. package/src/renderer/components/ui/MentionableTextarea.tsx +1426 -0
  669. package/src/renderer/components/ui/SlashCommandInteractionLayer.tsx +88 -0
  670. package/src/renderer/components/ui/TaskReferenceInteractionLayer.tsx +101 -0
  671. package/src/renderer/components/ui/UrlInteractionLayer.tsx +102 -0
  672. package/src/renderer/components/ui/alert-dialog.tsx +127 -0
  673. package/src/renderer/components/ui/auto-resize-textarea.tsx +93 -0
  674. package/src/renderer/components/ui/badge.tsx +37 -0
  675. package/src/renderer/components/ui/button.tsx +54 -0
  676. package/src/renderer/components/ui/checkbox.tsx +29 -0
  677. package/src/renderer/components/ui/combobox.tsx +168 -0
  678. package/src/renderer/components/ui/context-menu.tsx +124 -0
  679. package/src/renderer/components/ui/dialog.tsx +114 -0
  680. package/src/renderer/components/ui/hover-card.tsx +30 -0
  681. package/src/renderer/components/ui/input.tsx +22 -0
  682. package/src/renderer/components/ui/label.tsx +21 -0
  683. package/src/renderer/components/ui/popover.tsx +31 -0
  684. package/src/renderer/components/ui/select.tsx +150 -0
  685. package/src/renderer/components/ui/tabs.tsx +52 -0
  686. package/src/renderer/components/ui/textarea.tsx +21 -0
  687. package/src/renderer/components/ui/tiptap/TiptapBubbleMenu.tsx +75 -0
  688. package/src/renderer/components/ui/tiptap/TiptapEditor.tsx +73 -0
  689. package/src/renderer/components/ui/tiptap/TiptapToolbar.tsx +269 -0
  690. package/src/renderer/components/ui/tiptap/index.ts +3 -0
  691. package/src/renderer/components/ui/tiptap/presets.ts +46 -0
  692. package/src/renderer/components/ui/tiptap/tiptapStyles.css +235 -0
  693. package/src/renderer/components/ui/tiptap/types.ts +32 -0
  694. package/src/renderer/components/ui/tiptap/useTiptapEditor.ts +94 -0
  695. package/src/renderer/components/ui/tooltip.tsx +32 -0
  696. package/src/renderer/constants/cssVariables.ts +226 -0
  697. package/src/renderer/constants/layout.ts +6 -0
  698. package/src/renderer/constants/teamColors.ts +397 -0
  699. package/src/renderer/constants/teamRoles.ts +41 -0
  700. package/src/renderer/contexts/TabUIContext.tsx +51 -0
  701. package/src/renderer/contexts/useTabUIContext.ts +18 -0
  702. package/src/renderer/favicon.png +0 -0
  703. package/src/renderer/features/CLAUDE.md +19 -0
  704. package/src/renderer/hooks/navigation/utils.ts +263 -0
  705. package/src/renderer/hooks/useAttachments.ts +312 -0
  706. package/src/renderer/hooks/useAutoScrollBottom.ts +285 -0
  707. package/src/renderer/hooks/useBranchSync.ts +105 -0
  708. package/src/renderer/hooks/useChipDraftPersistence.ts +172 -0
  709. package/src/renderer/hooks/useCliInstaller.ts +106 -0
  710. package/src/renderer/hooks/useCollapsedGroups.ts +71 -0
  711. package/src/renderer/hooks/useComposerDraft.ts +504 -0
  712. package/src/renderer/hooks/useContinuousScrollNav.ts +50 -0
  713. package/src/renderer/hooks/useCreateTeamDraft.ts +280 -0
  714. package/src/renderer/hooks/useDraftPersistence.ts +140 -0
  715. package/src/renderer/hooks/useEditorKeyboardShortcuts.ts +257 -0
  716. package/src/renderer/hooks/useEffectiveCliProviderStatus.ts +66 -0
  717. package/src/renderer/hooks/useExtensionsTabState.ts +206 -0
  718. package/src/renderer/hooks/useFileListCacheWarmer.ts +38 -0
  719. package/src/renderer/hooks/useFileSuggestions.ts +255 -0
  720. package/src/renderer/hooks/useKeyboardShortcuts.ts +363 -0
  721. package/src/renderer/hooks/useLazyFileContent.ts +150 -0
  722. package/src/renderer/hooks/useMarkCommentsRead.ts +27 -0
  723. package/src/renderer/hooks/useMarkdownScrollSync.ts +158 -0
  724. package/src/renderer/hooks/useMemberStats.ts +45 -0
  725. package/src/renderer/hooks/useMentionDetection.ts +375 -0
  726. package/src/renderer/hooks/usePersistedGridLayout.ts +109 -0
  727. package/src/renderer/hooks/useResizableColumns.ts +140 -0
  728. package/src/renderer/hooks/useResizablePanel.ts +144 -0
  729. package/src/renderer/hooks/useStableTeamMentionMeta.ts +68 -0
  730. package/src/renderer/hooks/useSyncedAnimationStyle.ts +29 -0
  731. package/src/renderer/hooks/useTabNavigationController.ts +524 -0
  732. package/src/renderer/hooks/useTabUI.ts +252 -0
  733. package/src/renderer/hooks/useTaskLocalState.ts +163 -0
  734. package/src/renderer/hooks/useTaskSuggestions.ts +131 -0
  735. package/src/renderer/hooks/useTeamMessagesExpanded.ts +34 -0
  736. package/src/renderer/hooks/useTeamMessagesRead.ts +52 -0
  737. package/src/renderer/hooks/useTeamSuggestions.ts +78 -0
  738. package/src/renderer/hooks/useTheme.ts +138 -0
  739. package/src/renderer/hooks/useToolApprovalDiff.ts +212 -0
  740. package/src/renderer/hooks/useUnreadCommentCount.ts +14 -0
  741. package/src/renderer/hooks/useViewedFiles.ts +74 -0
  742. package/src/renderer/hooks/useViewportCommentRead.ts +147 -0
  743. package/src/renderer/hooks/useViewportObserver.ts +138 -0
  744. package/src/renderer/hooks/useVisibleAIGroup.ts +122 -0
  745. package/src/renderer/hooks/useVisibleFileSection.ts +114 -0
  746. package/src/renderer/hooks/useZoomFactor.ts +36 -0
  747. package/src/renderer/index.css +1560 -0
  748. package/src/renderer/index.html +1293 -0
  749. package/src/renderer/lib/utils.ts +6 -0
  750. package/src/renderer/main.tsx +30 -0
  751. package/src/renderer/sentry.ts +104 -0
  752. package/src/renderer/services/__tests__/createTeamPreferences.test.ts +67 -0
  753. package/src/renderer/services/commentReadStorage.ts +349 -0
  754. package/src/renderer/services/composerDraftStorage.ts +271 -0
  755. package/src/renderer/services/contextStorage.ts +201 -0
  756. package/src/renderer/services/createTeamDraftStorage.ts +151 -0
  757. package/src/renderer/services/createTeamPreferences.ts +361 -0
  758. package/src/renderer/services/dashboardCliStatusBannerPreference.ts +20 -0
  759. package/src/renderer/services/draftStorage.ts +128 -0
  760. package/src/renderer/services/layout-system/BrowserGridLayoutRepository.ts +111 -0
  761. package/src/renderer/services/layout-system/GridLayoutRepository.ts +8 -0
  762. package/src/renderer/services/layout-system/gridLayoutSchema.ts +137 -0
  763. package/src/renderer/services/layout-system/gridLayoutTypes.ts +17 -0
  764. package/src/renderer/store/index.ts +1556 -0
  765. package/src/renderer/store/slices/changeReviewSlice.ts +1694 -0
  766. package/src/renderer/store/slices/cliInstallerSlice.ts +689 -0
  767. package/src/renderer/store/slices/configSlice.ts +111 -0
  768. package/src/renderer/store/slices/connectionSlice.ts +221 -0
  769. package/src/renderer/store/slices/contextSlice.ts +394 -0
  770. package/src/renderer/store/slices/conversationSlice.ts +510 -0
  771. package/src/renderer/store/slices/editorSlice.ts +1455 -0
  772. package/src/renderer/store/slices/extensionsSlice.ts +1415 -0
  773. package/src/renderer/store/slices/notificationSlice.ts +277 -0
  774. package/src/renderer/store/slices/paneSlice.ts +357 -0
  775. package/src/renderer/store/slices/projectSlice.ts +70 -0
  776. package/src/renderer/store/slices/repositorySlice.ts +165 -0
  777. package/src/renderer/store/slices/scheduleSlice.ts +246 -0
  778. package/src/renderer/store/slices/sessionDetailSlice.ts +755 -0
  779. package/src/renderer/store/slices/sessionSlice.ts +539 -0
  780. package/src/renderer/store/slices/subagentSlice.ts +145 -0
  781. package/src/renderer/store/slices/tabSlice.ts +842 -0
  782. package/src/renderer/store/slices/tabUISlice.ts +319 -0
  783. package/src/renderer/store/slices/teamSlice.ts +5080 -0
  784. package/src/renderer/store/slices/uiSlice.ts +45 -0
  785. package/src/renderer/store/types.ts +103 -0
  786. package/src/renderer/store/utils/paneHelpers.ts +134 -0
  787. package/src/renderer/store/utils/pathResolution.ts +121 -0
  788. package/src/renderer/store/utils/stateResetHelpers.ts +72 -0
  789. package/src/renderer/types/api.ts +8 -0
  790. package/src/renderer/types/claudeMd.ts +74 -0
  791. package/src/renderer/types/contextInjection.ts +309 -0
  792. package/src/renderer/types/data.ts +144 -0
  793. package/src/renderer/types/groups.ts +406 -0
  794. package/src/renderer/types/inlineChip.ts +110 -0
  795. package/src/renderer/types/mention.ts +38 -0
  796. package/src/renderer/types/notifications.ts +18 -0
  797. package/src/renderer/types/panes.ts +35 -0
  798. package/src/renderer/types/sessionReport.ts +386 -0
  799. package/src/renderer/types/tabs.ts +249 -0
  800. package/src/renderer/types/teamMessagesPanelMode.ts +1 -0
  801. package/src/renderer/utils/__tests__/teamEffortOptions.test.ts +217 -0
  802. package/src/renderer/utils/__tests__/teamModelAvailability.codexCatalog.test.ts +383 -0
  803. package/src/renderer/utils/agentMessageFormatting.ts +139 -0
  804. package/src/renderer/utils/aiGroupEnhancer.ts +78 -0
  805. package/src/renderer/utils/aiGroupHelpers.ts +208 -0
  806. package/src/renderer/utils/attachmentUtils.ts +60 -0
  807. package/src/renderer/utils/bootstrapPromptSanitizer.ts +225 -0
  808. package/src/renderer/utils/bugReportUtils.ts +157 -0
  809. package/src/renderer/utils/buildSelectionAction.ts +116 -0
  810. package/src/renderer/utils/chipUtils.ts +372 -0
  811. package/src/renderer/utils/claudeCodeOnlyProviders.ts +126 -0
  812. package/src/renderer/utils/claudeMdTracker.ts +644 -0
  813. package/src/renderer/utils/codemirrorLanguages.ts +141 -0
  814. package/src/renderer/utils/codemirrorSelectionInfo.ts +41 -0
  815. package/src/renderer/utils/codemirrorTheme.ts +138 -0
  816. package/src/renderer/utils/contextMath.ts +55 -0
  817. package/src/renderer/utils/contextTracker.ts +1100 -0
  818. package/src/renderer/utils/crossTeamPendingReplies.ts +92 -0
  819. package/src/renderer/utils/dateGrouping.ts +91 -0
  820. package/src/renderer/utils/diffViewedStorage.ts +120 -0
  821. package/src/renderer/utils/displayItemBuilder.ts +587 -0
  822. package/src/renderer/utils/displaySummary.ts +74 -0
  823. package/src/renderer/utils/editorBridge.ts +90 -0
  824. package/src/renderer/utils/fileTreeBuilder.ts +110 -0
  825. package/src/renderer/utils/formatAgentRole.ts +24 -0
  826. package/src/renderer/utils/formatters.ts +61 -0
  827. package/src/renderer/utils/groupTransformer.ts +744 -0
  828. package/src/renderer/utils/idleNotificationSemantics.ts +76 -0
  829. package/src/renderer/utils/keyboardUtils.ts +92 -0
  830. package/src/renderer/utils/lastOutputDetector.ts +150 -0
  831. package/src/renderer/utils/markdownPlugins.ts +60 -0
  832. package/src/renderer/utils/memberAvatarCatalog.ts +38 -0
  833. package/src/renderer/utils/memberHelpers.ts +858 -0
  834. package/src/renderer/utils/memberLaunchDiagnostics.ts +216 -0
  835. package/src/renderer/utils/memberRuntimeSummary.ts +122 -0
  836. package/src/renderer/utils/memberSpawnStatusPolling.ts +29 -0
  837. package/src/renderer/utils/mentionLinkify.ts +90 -0
  838. package/src/renderer/utils/mentionSuggestions.ts +34 -0
  839. package/src/renderer/utils/mergeTeamMessages.ts +27 -0
  840. package/src/renderer/utils/messageRenderEquality.ts +158 -0
  841. package/src/renderer/utils/modelExtractor.ts +90 -0
  842. package/src/renderer/utils/multimodelProviderVisibility.ts +32 -0
  843. package/src/renderer/utils/openCodeModelRecommendations.ts +1326 -0
  844. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +79 -0
  845. package/src/renderer/utils/pathDisplay.ts +149 -0
  846. package/src/renderer/utils/pathNormalize.ts +61 -0
  847. package/src/renderer/utils/pathUtils.ts +47 -0
  848. package/src/renderer/utils/platformKeys.ts +24 -0
  849. package/src/renderer/utils/previewRegistry.ts +45 -0
  850. package/src/renderer/utils/projectColor.ts +54 -0
  851. package/src/renderer/utils/projectLookup.ts +64 -0
  852. package/src/renderer/utils/providerBackendIdentity.ts +43 -0
  853. package/src/renderer/utils/providerSlashCommands.ts +122 -0
  854. package/src/renderer/utils/quickOpenCache.ts +40 -0
  855. package/src/renderer/utils/refreshCliStatus.ts +17 -0
  856. package/src/renderer/utils/reportAssessments.ts +555 -0
  857. package/src/renderer/utils/reviewDecisionScope.ts +81 -0
  858. package/src/renderer/utils/reviewKey.ts +132 -0
  859. package/src/renderer/utils/runtimeDisplayName.ts +26 -0
  860. package/src/renderer/utils/scheduleFormatters.ts +45 -0
  861. package/src/renderer/utils/sessionAnalyzer.ts +1346 -0
  862. package/src/renderer/utils/sessionExporter.ts +427 -0
  863. package/src/renderer/utils/sessionTitleParser.ts +69 -0
  864. package/src/renderer/utils/skillCommandSuggestions.ts +77 -0
  865. package/src/renderer/utils/slashCommandExtractor.ts +154 -0
  866. package/src/renderer/utils/streamJsonParser.ts +393 -0
  867. package/src/renderer/utils/stringUtils.ts +50 -0
  868. package/src/renderer/utils/syntaxHighlighter.ts +158 -0
  869. package/src/renderer/utils/tabLabelDisambiguation.ts +99 -0
  870. package/src/renderer/utils/taskChangePresence.ts +19 -0
  871. package/src/renderer/utils/taskChangeRequest.ts +122 -0
  872. package/src/renderer/utils/taskCommentPendingReply.ts +74 -0
  873. package/src/renderer/utils/taskGrouping.ts +151 -0
  874. package/src/renderer/utils/taskReferenceUtils.ts +331 -0
  875. package/src/renderer/utils/teamEffortOptions.ts +150 -0
  876. package/src/renderer/utils/teamLaunchSummaryCopy.ts +17 -0
  877. package/src/renderer/utils/teamMessageExpandStorage.ts +39 -0
  878. package/src/renderer/utils/teamMessageFiltering.ts +95 -0
  879. package/src/renderer/utils/teamMessageKey.ts +14 -0
  880. package/src/renderer/utils/teamMessageReadStorage.ts +49 -0
  881. package/src/renderer/utils/teamModelAvailability.ts +465 -0
  882. package/src/renderer/utils/teamModelCatalog.ts +502 -0
  883. package/src/renderer/utils/teamModelContext.ts +34 -0
  884. package/src/renderer/utils/teamProvisioningPresentation.ts +772 -0
  885. package/src/renderer/utils/teamRuntimeSummary.ts +53 -0
  886. package/src/renderer/utils/toolLinkingEngine.ts +117 -0
  887. package/src/renderer/utils/toolRendering/index.ts +14 -0
  888. package/src/renderer/utils/toolRendering/toolContentChecks.ts +58 -0
  889. package/src/renderer/utils/toolRendering/toolSummaryHelpers.ts +276 -0
  890. package/src/renderer/utils/toolRendering/toolTokens.ts +57 -0
  891. package/src/renderer/utils/unwrapIpc.ts +31 -0
  892. package/src/renderer/utils/urlMatchUtils.ts +45 -0
  893. package/src/renderer/vite-env.d.ts +21 -0
  894. package/src/shared/constants/agentBlocks.ts +129 -0
  895. package/src/shared/constants/attachments.ts +175 -0
  896. package/src/shared/constants/cache.ts +12 -0
  897. package/src/shared/constants/cli.ts +15 -0
  898. package/src/shared/constants/crossTeam.ts +126 -0
  899. package/src/shared/constants/index.ts +15 -0
  900. package/src/shared/constants/kanban.ts +9 -0
  901. package/src/shared/constants/memberColors.ts +140 -0
  902. package/src/shared/constants/opencodeTaskLogAttribution.ts +1 -0
  903. package/src/shared/constants/teamLimits.ts +2 -0
  904. package/src/shared/constants/trafficLights.ts +60 -0
  905. package/src/shared/constants/triggerColors.ts +126 -0
  906. package/src/shared/constants/window.ts +12 -0
  907. package/src/shared/types/api.ts +1062 -0
  908. package/src/shared/types/ccConnect.ts +399 -0
  909. package/src/shared/types/cliInstaller.ts +335 -0
  910. package/src/shared/types/editor.ts +279 -0
  911. package/src/shared/types/extensions/api.ts +90 -0
  912. package/src/shared/types/extensions/apikey.ts +40 -0
  913. package/src/shared/types/extensions/common.ts +16 -0
  914. package/src/shared/types/extensions/index.ts +67 -0
  915. package/src/shared/types/extensions/mcp.ts +132 -0
  916. package/src/shared/types/extensions/plugin.ts +85 -0
  917. package/src/shared/types/extensions/skill.ts +173 -0
  918. package/src/shared/types/index.ts +48 -0
  919. package/src/shared/types/ipc.ts +5 -0
  920. package/src/shared/types/notifications.ts +407 -0
  921. package/src/shared/types/providers.ts +116 -0
  922. package/src/shared/types/review.ts +319 -0
  923. package/src/shared/types/schedule.ts +124 -0
  924. package/src/shared/types/team.ts +1726 -0
  925. package/src/shared/types/terminal.ts +49 -0
  926. package/src/shared/types/visualization.ts +60 -0
  927. package/src/shared/utils/__tests__/contextMetrics.test.ts +260 -0
  928. package/src/shared/utils/__tests__/ephemeralProjectPath.test.ts +42 -0
  929. package/src/shared/utils/__tests__/teamProvider.test.ts +29 -0
  930. package/src/shared/utils/agentLanguage.ts +122 -0
  931. package/src/shared/utils/anthropicLaunchModel.ts +96 -0
  932. package/src/shared/utils/anthropicModelDefaults.ts +3 -0
  933. package/src/shared/utils/apiErrorDetector.ts +13 -0
  934. package/src/shared/utils/boardTaskActivityLabels.ts +128 -0
  935. package/src/shared/utils/boardTaskActivityPresentation.ts +75 -0
  936. package/src/shared/utils/cliArgsParser.ts +129 -0
  937. package/src/shared/utils/contentSanitizer.ts +207 -0
  938. package/src/shared/utils/contextMetrics.ts +236 -0
  939. package/src/shared/utils/costFormatting.ts +45 -0
  940. package/src/shared/utils/diffContextHash.ts +22 -0
  941. package/src/shared/utils/effortLevels.ts +73 -0
  942. package/src/shared/utils/ephemeralProjectPath.ts +41 -0
  943. package/src/shared/utils/errorHandling.ts +26 -0
  944. package/src/shared/utils/extensionNormalizers.ts +336 -0
  945. package/src/shared/utils/idleNotificationSemantics.ts +86 -0
  946. package/src/shared/utils/inboxNoise.ts +158 -0
  947. package/src/shared/utils/leadDetection.ts +63 -0
  948. package/src/shared/utils/logger.ts +69 -0
  949. package/src/shared/utils/markdownTextSearch.ts +210 -0
  950. package/src/shared/utils/mcpScopes.ts +36 -0
  951. package/src/shared/utils/modelParser.ts +158 -0
  952. package/src/shared/utils/opencodeModelRef.ts +78 -0
  953. package/src/shared/utils/platformPath.ts +103 -0
  954. package/src/shared/utils/pricing.ts +129 -0
  955. package/src/shared/utils/providerBackend.ts +90 -0
  956. package/src/shared/utils/providerExtensionCapabilities.ts +92 -0
  957. package/src/shared/utils/providerModelSelection.ts +5 -0
  958. package/src/shared/utils/providerModelVisibility.ts +47 -0
  959. package/src/shared/utils/rateLimitDetector.ts +334 -0
  960. package/src/shared/utils/reviewState.ts +74 -0
  961. package/src/shared/utils/sentryConfig.ts +26 -0
  962. package/src/shared/utils/skillRoots.ts +93 -0
  963. package/src/shared/utils/slashCommands.ts +128 -0
  964. package/src/shared/utils/taskChangePresence.ts +35 -0
  965. package/src/shared/utils/taskChangeSince.ts +51 -0
  966. package/src/shared/utils/taskChangeState.ts +49 -0
  967. package/src/shared/utils/taskHistory.ts +82 -0
  968. package/src/shared/utils/taskIdentity.ts +32 -0
  969. package/src/shared/utils/teamGraphDefaultLayout.ts +97 -0
  970. package/src/shared/utils/teamMemberColors.ts +107 -0
  971. package/src/shared/utils/teamMemberName.ts +77 -0
  972. package/src/shared/utils/teamProvider.ts +73 -0
  973. package/src/shared/utils/teamStableOwnerId.ts +12 -0
  974. package/src/shared/utils/teammateMessageParser.ts +52 -0
  975. package/src/shared/utils/tokenFormatting.ts +91 -0
  976. package/src/shared/utils/toolSummary.ts +279 -0
  977. package/src/shared/utils/version.ts +29 -0
  978. package/src/types/agent-teams-controller.d.ts +163 -0
  979. package/src/types/node-pty.d.ts +22 -0
  980. package/src/types/pidusage.d.ts +23 -0
@@ -0,0 +1,2207 @@
1
+ /**
2
+ * HTTP-based implementation of ElectronAPI for browser mode.
3
+ *
4
+ * Replaces Electron IPC with fetch() for request/response and
5
+ * EventSource (SSE) for real-time events. Allows the renderer
6
+ * to run in a regular browser connected to an HTTP server.
7
+ */
8
+
9
+ import type { DashboardRecentProjectsPayload } from '@features/recent-projects/contracts';
10
+ import type {
11
+ AddMemberRequest,
12
+ AddTaskCommentRequest,
13
+ AppConfig,
14
+ AttachmentFileData,
15
+ BoardTaskActivityDetailResult,
16
+ BoardTaskExactLogDetailResult,
17
+ BoardTaskExactLogSummariesResponse,
18
+ BoardTaskLogStreamResponse,
19
+ BoardTaskLogStreamSummary,
20
+ ClaudeMdFileInfo,
21
+ ClaudeRootFolderSelection,
22
+ ClaudeRootInfo,
23
+ CliInstallerAPI,
24
+ ConfigAPI,
25
+ ContextInfo,
26
+ ConversationGroup,
27
+ CreateScheduleInput,
28
+ CreateTaskRequest,
29
+ CrossTeamAPI,
30
+ CrossTeamMessage,
31
+ CrossTeamSendResult,
32
+ ElectronAPI,
33
+ FileChangeEvent,
34
+ GlobalTask,
35
+ HttpServerAPI,
36
+ HttpServerStatus,
37
+ KanbanColumnId,
38
+ MachineProfile,
39
+ MachineRuntimeProcess,
40
+ MemberFullStats,
41
+ MemberLogSummary,
42
+ NotificationsAPI,
43
+ NotificationTrigger,
44
+ PaginatedSessionsResult,
45
+ Project,
46
+ ReplaceMembersRequest,
47
+ RepositoryGroup,
48
+ Schedule,
49
+ ScheduleChangeEvent,
50
+ ScheduleRun,
51
+ SearchSessionsResult,
52
+ SendMessageRequest,
53
+ SendMessageResult,
54
+ Session,
55
+ SessionAPI,
56
+ SessionDetail,
57
+ SessionMetrics,
58
+ SessionsByIdsOptions,
59
+ SessionsPaginationOptions,
60
+ SnippetDiff,
61
+ SshAPI,
62
+ SshConfigHostEntry,
63
+ SshConnectionConfig,
64
+ SshConnectionStatus,
65
+ SshLastConnection,
66
+ SubagentDetail,
67
+ TaskComment,
68
+ TeamChangeEvent,
69
+ TeamClaudeLogsQuery,
70
+ TeamClaudeLogsResponse,
71
+ TeamConfig,
72
+ TeamCreateConfigRequest,
73
+ TeamCreateRequest,
74
+ TeamCreateResponse,
75
+ TeamLaunchRequest,
76
+ TeamLaunchResponse,
77
+ TeamMemberActivityMeta,
78
+ TeamProvisioningModelVerificationMode,
79
+ TeamProvisioningPrepareResult,
80
+ TeamProvisioningProgress,
81
+ TeamsAPI,
82
+ TeamSummary,
83
+ TeamTask,
84
+ TeamTaskStatus,
85
+ TeamTemplateSource,
86
+ TeamTemplateSourcesSnapshot,
87
+ TeamUpdateConfigRequest,
88
+ TeamViewSnapshot,
89
+ ToolApprovalEvent,
90
+ ToolApprovalFileContent,
91
+ ToolApprovalSettings,
92
+ TriggerTestResult,
93
+ UpdateKanbanPatch,
94
+ UpdaterAPI,
95
+ UpdateSchedulePatch,
96
+ WaterfallData,
97
+ WslClaudeRootCandidate,
98
+ MemberSpawnStatusesSnapshot,
99
+ TeamAgentRuntimeSnapshot,
100
+ CcSession,
101
+ CcSessionDetail,
102
+ } from '@shared/types';
103
+
104
+ import type {
105
+ AgentChangeSet,
106
+ ApplyReviewResult,
107
+ ChangeStats,
108
+ ConflictCheckResult,
109
+ FileChangeWithContent,
110
+ HunkDecision,
111
+ RejectResult,
112
+ TaskChangeSetV2,
113
+ } from '@shared/types/review';
114
+ import type { CliProviderStatus } from '@shared/types/cliInstaller';
115
+ import type { AgentConfig } from '@shared/types/api';
116
+ import type { CliArgsValidationResult } from '@shared/utils/cliArgsParser';
117
+ import type { EditorAPI, ProjectAPI, WorkspaceListResponse } from '@shared/types/editor';
118
+ import type {
119
+ EnrichedPlugin,
120
+ InstalledMcpEntry,
121
+ McpCatalogItem,
122
+ McpCustomInstallRequest,
123
+ McpInstallRequest,
124
+ McpSearchResult,
125
+ McpServerDiagnostic,
126
+ OperationResult,
127
+ PluginInstallRequest,
128
+ SkillCatalogItem,
129
+ SkillDeleteRequest,
130
+ SkillDetail,
131
+ SkillImportRequest,
132
+ SkillReviewPreview,
133
+ SkillSource,
134
+ SkillSourcesSnapshot,
135
+ SkillUpsertRequest,
136
+ SkillWatcherEvent,
137
+ } from '@shared/types/extensions';
138
+ import type { ApplyReviewRequest } from '@shared/types/review';
139
+ import type { TerminalAPI } from '@shared/types/terminal';
140
+
141
+ export class HttpAPIClient implements ElectronAPI {
142
+ private baseUrl: string;
143
+ private eventSource: EventSource | null = null;
144
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- event callbacks have varying signatures
145
+ private eventListeners = new Map<string, Set<(...args: any[]) => void>>();
146
+
147
+ constructor(baseUrl: string) {
148
+ this.baseUrl = baseUrl;
149
+ // SSE is initialized lazily to avoid failing in test environments
150
+ // where EventSource is not available
151
+ }
152
+
153
+ // ---------------------------------------------------------------------------
154
+ // SSE event infrastructure
155
+ // ---------------------------------------------------------------------------
156
+
157
+ private initEventSource(): void {
158
+ if (this.eventSource) return;
159
+ if (typeof EventSource === 'undefined') return;
160
+ this.eventSource = new EventSource(`${this.baseUrl}/api/events`);
161
+ this.eventSource.onopen = () => console.log('[HttpAPIClient] SSE connected');
162
+ this.eventSource.onerror = () => {
163
+ // Auto-reconnect is built into EventSource
164
+ console.warn('[HttpAPIClient] SSE connection error, will reconnect...');
165
+ };
166
+ }
167
+
168
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- event callbacks have varying signatures
169
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- event callbacks have varying signatures
170
+ private addEventListener(channel: string, callback: (...args: any[]) => void): () => void {
171
+ this.initEventSource();
172
+ if (!this.eventListeners.has(channel)) {
173
+ this.eventListeners.set(channel, new Set());
174
+ // Register SSE listener for this channel once
175
+ this.eventSource?.addEventListener(channel, ((event: MessageEvent) => {
176
+ const data: unknown = JSON.parse(event.data as string);
177
+ const listeners = this.eventListeners.get(channel);
178
+ listeners?.forEach((cb) => cb(data));
179
+ }) as EventListener);
180
+ }
181
+ this.eventListeners.get(channel)!.add(callback);
182
+
183
+ return () => {
184
+ this.eventListeners.get(channel)?.delete(callback);
185
+ };
186
+ }
187
+
188
+ // ---------------------------------------------------------------------------
189
+ // HTTP helpers
190
+ // ---------------------------------------------------------------------------
191
+
192
+ /**
193
+ * JSON reviver that converts ISO 8601 date strings back to Date objects.
194
+ * Electron IPC preserves Date instances via structured clone, but HTTP JSON
195
+ * serialization turns them into strings. This restores them so that
196
+ * `.getTime()` and other Date methods work in the renderer.
197
+ */
198
+ // eslint-disable-next-line security/detect-unsafe-regex -- anchored pattern with bounded quantifier; no backtracking risk
199
+ private static readonly ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z?$/;
200
+
201
+ private static reviveDates(_key: string, value: unknown): unknown {
202
+ if (typeof value === 'string' && HttpAPIClient.ISO_DATE_RE.test(value)) {
203
+ const d = new Date(value);
204
+ if (!isNaN(d.getTime())) return d;
205
+ }
206
+ return value;
207
+ }
208
+
209
+ private createTimeoutController(timeoutMs: number): {
210
+ controller: AbortController;
211
+ timeout: ReturnType<typeof setTimeout>;
212
+ } {
213
+ const controller = new AbortController();
214
+ const timeout = setTimeout(() => {
215
+ controller.abort(new Error(`请求超时(${Math.round(timeoutMs / 1000)} 秒)`));
216
+ }, timeoutMs);
217
+ return { controller, timeout };
218
+ }
219
+
220
+ private async parseJson<T>(res: Response): Promise<T> {
221
+ const text = await res.text();
222
+ if (!res.ok) {
223
+ if (!text.trim()) throw new Error(`HTTP ${res.status}`);
224
+ try {
225
+ const parsed = JSON.parse(text) as { error?: string };
226
+ throw new Error(parsed.error ?? `HTTP ${res.status}`);
227
+ } catch (e) {
228
+ if (e instanceof SyntaxError) throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
229
+ throw e;
230
+ }
231
+ }
232
+ if (!text.trim()) return undefined as unknown as T;
233
+ return JSON.parse(text, (key, value) => HttpAPIClient.reviveDates(key, value)) as T;
234
+ }
235
+
236
+ private async get<T>(path: string): Promise<T> {
237
+ const { controller, timeout } = this.createTimeoutController(10_000);
238
+ try {
239
+ const res = await fetch(`${this.baseUrl}${path}`, { signal: controller.signal });
240
+ return this.parseJson<T>(res);
241
+ } finally {
242
+ clearTimeout(timeout);
243
+ }
244
+ }
245
+
246
+ private async post<T>(path: string, body?: unknown): Promise<T> {
247
+ const { controller, timeout } = this.createTimeoutController(10_000);
248
+ try {
249
+ const res = await fetch(`${this.baseUrl}${path}`, {
250
+ method: 'POST',
251
+ headers: body !== undefined ? { 'Content-Type': 'application/json' } : undefined,
252
+ body: body !== undefined ? JSON.stringify(body) : undefined,
253
+ signal: controller.signal,
254
+ });
255
+ return this.parseJson<T>(res);
256
+ } finally {
257
+ clearTimeout(timeout);
258
+ }
259
+ }
260
+
261
+ private async postLong<T>(path: string, body?: unknown, timeoutMs = 60_000): Promise<T> {
262
+ const { controller, timeout } = this.createTimeoutController(timeoutMs);
263
+ try {
264
+ const res = await fetch(`${this.baseUrl}${path}`, {
265
+ method: 'POST',
266
+ headers: body !== undefined ? { 'Content-Type': 'application/json' } : undefined,
267
+ body: body !== undefined ? JSON.stringify(body) : undefined,
268
+ signal: controller.signal,
269
+ });
270
+ return this.parseJson<T>(res);
271
+ } finally {
272
+ clearTimeout(timeout);
273
+ }
274
+ }
275
+
276
+ private async del<T>(path: string, body?: unknown): Promise<T> {
277
+ const { controller, timeout } = this.createTimeoutController(10_000);
278
+ try {
279
+ const res = await fetch(`${this.baseUrl}${path}`, {
280
+ method: 'DELETE',
281
+ headers: { 'Content-Type': 'application/json' },
282
+ body: body ? JSON.stringify(body) : undefined,
283
+ signal: controller.signal,
284
+ });
285
+ return this.parseJson<T>(res);
286
+ } finally {
287
+ clearTimeout(timeout);
288
+ }
289
+ }
290
+
291
+ private async put<T>(path: string, body?: unknown): Promise<T> {
292
+ const { controller, timeout } = this.createTimeoutController(10_000);
293
+ try {
294
+ const res = await fetch(`${this.baseUrl}${path}`, {
295
+ method: 'PUT',
296
+ headers: { 'Content-Type': 'application/json' },
297
+ body: body ? JSON.stringify(body) : undefined,
298
+ signal: controller.signal,
299
+ });
300
+ return this.parseJson<T>(res);
301
+ } finally {
302
+ clearTimeout(timeout);
303
+ }
304
+ }
305
+
306
+ private async patch<T>(path: string, body?: unknown): Promise<T> {
307
+ const { controller, timeout } = this.createTimeoutController(10_000);
308
+ try {
309
+ const res = await fetch(`${this.baseUrl}${path}`, {
310
+ method: 'PATCH',
311
+ headers: { 'Content-Type': 'application/json' },
312
+ body: body ? JSON.stringify(body) : undefined,
313
+ signal: controller.signal,
314
+ });
315
+ return this.parseJson<T>(res);
316
+ } finally {
317
+ clearTimeout(timeout);
318
+ }
319
+ }
320
+
321
+ private async delete<T>(path: string): Promise<T> {
322
+ const { controller, timeout } = this.createTimeoutController(10_000);
323
+ try {
324
+ const res = await fetch(`${this.baseUrl}${path}`, {
325
+ method: 'DELETE',
326
+ headers: { 'Content-Type': 'application/json' },
327
+ signal: controller.signal,
328
+ });
329
+ return this.parseJson<T>(res);
330
+ } finally {
331
+ clearTimeout(timeout);
332
+ }
333
+ }
334
+
335
+ // ---------------------------------------------------------------------------
336
+ // Core session/project APIs
337
+ // ---------------------------------------------------------------------------
338
+
339
+ getAppVersion = (): Promise<string> => this.get<string>('/api/version');
340
+
341
+ hermitConfig = {
342
+ get: async (): Promise<{ ccBaseUrl: string; ccBridgeUrl: string; ccToken: string; ccTokenSet: boolean }> => {
343
+ const res = await this.get<{ ok: boolean; data: { ccBaseUrl: string; ccBridgeUrl: string; ccToken: string; ccTokenSet: boolean } }>('/api/hermit-config');
344
+ return res.data;
345
+ },
346
+ update: async (patch: { ccBaseUrl?: string; ccToken?: string; ccBridgeUrl?: string }): Promise<void> => {
347
+ await this.post('/api/hermit-config', patch);
348
+ },
349
+ getRaw: async (): Promise<{ path: string; content: string }> => {
350
+ const res = await this.get<{ ok: boolean; data: { path: string; content: string } }>(
351
+ '/api/hermit-config/raw'
352
+ );
353
+ return res.data;
354
+ },
355
+ updateRaw: async (content: string): Promise<void> => {
356
+ await this.post('/api/hermit-config/raw', { content });
357
+ },
358
+ };
359
+
360
+ ccConfig = {
361
+ get: async (): Promise<Record<string, unknown>> => {
362
+ const res = await this.get<{ ok: boolean; data: Record<string, unknown> }>('/api/cc-config');
363
+ return res.data;
364
+ },
365
+ update: async (patch: Record<string, unknown>): Promise<{ needsRestart: boolean }> => {
366
+ const res = await this.post<{ ok: boolean; data: { needsRestart: boolean } }>('/api/cc-config', patch);
367
+ return res.data;
368
+ },
369
+ getRaw: async (): Promise<{ path: string; content: string }> => {
370
+ const res = await this.get<{ ok: boolean; data: { path: string; content: string } }>(
371
+ '/api/cc-config/raw'
372
+ );
373
+ return res.data;
374
+ },
375
+ updateRaw: async (content: string): Promise<void> => {
376
+ await this.post('/api/cc-config/raw', { content });
377
+ },
378
+ };
379
+
380
+ ccSettings = {
381
+ get: async (): Promise<Record<string, unknown>> => {
382
+ const res = await this.get<{ ok: boolean; data: Record<string, unknown> }>('/api/cc-settings');
383
+ return res.data;
384
+ },
385
+ patch: async (patch: Record<string, unknown>): Promise<void> => {
386
+ await this.patch('/api/cc-settings', patch);
387
+ },
388
+ restart: async (): Promise<void> => {
389
+ await this.post('/api/cc-restart', {});
390
+ },
391
+ reload: async (): Promise<void> => {
392
+ await this.post('/api/cc-reload', {});
393
+ },
394
+ };
395
+
396
+ // cc-connect setup flows (QR code + manual platform binding)
397
+ ccSetup = {
398
+ feishuBegin: async (): Promise<{ device_code: string; qr_url: string; base_url?: string; interval: number; expires_in: number }> => {
399
+ const res = await this.post<{ ok: boolean; data: { device_code: string; qr_url: string; base_url?: string; interval: number; expires_in: number } }>('/api/setup/feishu/begin', {});
400
+ return res.data;
401
+ },
402
+ feishuPoll: async (deviceCode: string, baseUrl?: string): Promise<{
403
+ status: string; base_url?: string; app_id?: string; app_secret?: string;
404
+ platform?: string; owner_open_id?: string; slow_down?: boolean; error?: string;
405
+ }> => {
406
+ const res = await this.post<{ ok: boolean; data: {
407
+ status: string; base_url?: string; app_id?: string; app_secret?: string;
408
+ platform?: string; owner_open_id?: string; slow_down?: boolean; error?: string;
409
+ } }>('/api/setup/feishu/poll', { device_code: deviceCode, base_url: baseUrl });
410
+ return res.data;
411
+ },
412
+ feishuSave: async (params: {
413
+ project: string; app_id: string; app_secret: string;
414
+ platform_type?: string; owner_open_id?: string;
415
+ work_dir?: string; agent_type?: string;
416
+ }): Promise<{ message: string; restart_required: boolean }> => {
417
+ const res = await this.post<{ ok: boolean; data: { message: string; restart_required: boolean } }>('/api/setup/feishu/save', params);
418
+ return res.data;
419
+ },
420
+
421
+ weixinBegin: async (apiUrl?: string): Promise<{ qr_key: string; qr_url: string }> => {
422
+ const res = await this.post<{ ok: boolean; data: { qr_key: string; qr_url: string } }>('/api/setup/weixin/begin', { api_url: apiUrl });
423
+ return res.data;
424
+ },
425
+ weixinPoll: async (qrKey: string, apiUrl?: string): Promise<{
426
+ status: string; bot_token?: string; ilink_bot_id?: string;
427
+ base_url?: string; ilink_user_id?: string;
428
+ }> => {
429
+ const res = await this.post<{ ok: boolean; data: {
430
+ status: string; bot_token?: string; ilink_bot_id?: string;
431
+ base_url?: string; ilink_user_id?: string;
432
+ } }>('/api/setup/weixin/poll', { qr_key: qrKey, api_url: apiUrl });
433
+ return res.data;
434
+ },
435
+ weixinSave: async (params: {
436
+ project: string; token: string; base_url?: string;
437
+ ilink_bot_id?: string; ilink_user_id?: string;
438
+ work_dir?: string; agent_type?: string;
439
+ }): Promise<{ message: string; restart_required: boolean }> => {
440
+ const res = await this.post<{ ok: boolean; data: { message: string; restart_required: boolean } }>('/api/setup/weixin/save', params);
441
+ return res.data;
442
+ },
443
+
444
+ addPlatform: async (projectName: string, body: {
445
+ type: string; options?: Record<string, unknown>;
446
+ work_dir?: string; agent_type?: string;
447
+ }): Promise<{ message: string; restart_required: boolean }> => {
448
+ const res = await this.post<{ ok: boolean; data: { message: string; restart_required: boolean } }>(`/api/projects/${encodeURIComponent(projectName)}/add-platform`, body);
449
+ return res.data;
450
+ },
451
+ };
452
+
453
+ getDashboardRecentProjects = (): Promise<DashboardRecentProjectsPayload> =>
454
+ this.get<DashboardRecentProjectsPayload>('/api/dashboard/recent-projects');
455
+
456
+ getProjects = (): Promise<Project[]> => this.get<Project[]>('/api/projects');
457
+
458
+ getSessions = (projectId: string): Promise<Session[]> =>
459
+ this.get<Session[]>(`/api/projects/${encodeURIComponent(projectId)}/sessions`);
460
+
461
+ getSessionsPaginated = (
462
+ projectId: string,
463
+ cursor: string | null,
464
+ limit?: number,
465
+ options?: SessionsPaginationOptions
466
+ ): Promise<PaginatedSessionsResult> => {
467
+ const params = new URLSearchParams();
468
+ if (cursor) params.set('cursor', cursor);
469
+ if (limit) params.set('limit', String(limit));
470
+ if (options?.includeTotalCount === false) params.set('includeTotalCount', 'false');
471
+ if (options?.prefilterAll === false) params.set('prefilterAll', 'false');
472
+ if (options?.metadataLevel) params.set('metadataLevel', options.metadataLevel);
473
+ const qs = params.toString();
474
+ const encodedId = encodeURIComponent(projectId);
475
+ const path = `/api/projects/${encodedId}/sessions-paginated`;
476
+ return this.get<PaginatedSessionsResult>(qs ? `${path}?${qs}` : path);
477
+ };
478
+
479
+ searchSessions = (
480
+ projectId: string,
481
+ query: string,
482
+ maxResults?: number
483
+ ): Promise<SearchSessionsResult> => {
484
+ const params = new URLSearchParams({ q: query });
485
+ if (maxResults) params.set('maxResults', String(maxResults));
486
+ return this.get<SearchSessionsResult>(
487
+ `/api/projects/${encodeURIComponent(projectId)}/search?${params}`
488
+ );
489
+ };
490
+
491
+ searchAllProjects = (query: string, maxResults?: number): Promise<SearchSessionsResult> => {
492
+ const params = new URLSearchParams({ q: query });
493
+ if (maxResults) params.set('maxResults', String(maxResults));
494
+ return this.get<SearchSessionsResult>(`/api/search?${params}`);
495
+ };
496
+
497
+ getSessionDetail = (
498
+ projectId: string,
499
+ sessionId: string,
500
+ options?: { bypassCache?: boolean }
501
+ ): Promise<SessionDetail | null> => {
502
+ const params = new URLSearchParams();
503
+ if (options?.bypassCache) params.set('bypassCache', 'true');
504
+ const qs = params.toString();
505
+ const suffix = qs ? `?${qs}` : '';
506
+ return this.get<SessionDetail | null>(
507
+ `/api/projects/${encodeURIComponent(projectId)}/sessions/${encodeURIComponent(sessionId)}${suffix}`
508
+ );
509
+ };
510
+
511
+ getSessionMetrics = (projectId: string, sessionId: string): Promise<SessionMetrics | null> =>
512
+ this.get<SessionMetrics | null>(
513
+ `/api/projects/${encodeURIComponent(projectId)}/sessions/${encodeURIComponent(sessionId)}/metrics`
514
+ );
515
+
516
+ getWaterfallData = (projectId: string, sessionId: string): Promise<WaterfallData | null> =>
517
+ this.get<WaterfallData | null>(
518
+ `/api/projects/${encodeURIComponent(projectId)}/sessions/${encodeURIComponent(sessionId)}/waterfall`
519
+ );
520
+
521
+ getSubagentDetail = (
522
+ projectId: string,
523
+ sessionId: string,
524
+ subagentId: string,
525
+ options?: { bypassCache?: boolean }
526
+ ): Promise<SubagentDetail | null> => {
527
+ const params = new URLSearchParams();
528
+ if (options?.bypassCache) params.set('bypassCache', 'true');
529
+ const qs = params.toString();
530
+ const suffix = qs ? `?${qs}` : '';
531
+ return this.get<SubagentDetail | null>(
532
+ `/api/projects/${encodeURIComponent(projectId)}/sessions/${encodeURIComponent(sessionId)}/subagents/${encodeURIComponent(subagentId)}${suffix}`
533
+ );
534
+ };
535
+
536
+ getSessionGroups = (projectId: string, sessionId: string): Promise<ConversationGroup[]> =>
537
+ this.get<ConversationGroup[]>(
538
+ `/api/projects/${encodeURIComponent(projectId)}/sessions/${encodeURIComponent(sessionId)}/groups`
539
+ );
540
+
541
+ getSessionsByIds = (
542
+ projectId: string,
543
+ sessionIds: string[],
544
+ options?: SessionsByIdsOptions
545
+ ): Promise<Session[]> =>
546
+ this.post<Session[]>(`/api/projects/${encodeURIComponent(projectId)}/sessions-by-ids`, {
547
+ sessionIds,
548
+ metadataLevel: options?.metadataLevel,
549
+ });
550
+
551
+ // ---------------------------------------------------------------------------
552
+ // Repository grouping
553
+ // ---------------------------------------------------------------------------
554
+
555
+ getRepositoryGroups = (): Promise<RepositoryGroup[]> =>
556
+ this.get<RepositoryGroup[]>('/api/repository-groups');
557
+
558
+ getWorktreeSessions = (worktreeId: string): Promise<Session[]> =>
559
+ this.get<Session[]>(`/api/worktrees/${encodeURIComponent(worktreeId)}/sessions`);
560
+
561
+ // ---------------------------------------------------------------------------
562
+ // Validation
563
+ // ---------------------------------------------------------------------------
564
+
565
+ validatePath = (
566
+ relativePath: string,
567
+ projectPath: string
568
+ ): Promise<{ exists: boolean; isDirectory?: boolean }> =>
569
+ this.post<{ exists: boolean; isDirectory?: boolean }>('/api/validate/path', {
570
+ relativePath,
571
+ projectPath,
572
+ });
573
+
574
+ validateMentions = (
575
+ mentions: { type: 'path'; value: string }[],
576
+ projectPath: string
577
+ ): Promise<Record<string, boolean>> =>
578
+ this.post<Record<string, boolean>>('/api/validate/mentions', { mentions, projectPath });
579
+
580
+ // ---------------------------------------------------------------------------
581
+ // CLAUDE.md reading
582
+ // ---------------------------------------------------------------------------
583
+
584
+ readClaudeMdFiles = (projectRoot: string): Promise<Record<string, ClaudeMdFileInfo>> =>
585
+ this.post<Record<string, ClaudeMdFileInfo>>('/api/read-claude-md', { projectRoot });
586
+
587
+ readDirectoryClaudeMd = (dirPath: string): Promise<ClaudeMdFileInfo> =>
588
+ this.post<ClaudeMdFileInfo>('/api/read-directory-claude-md', { dirPath });
589
+
590
+ readMentionedFile = (
591
+ absolutePath: string,
592
+ projectRoot: string,
593
+ maxTokens?: number
594
+ ): Promise<ClaudeMdFileInfo | null> =>
595
+ this.post<ClaudeMdFileInfo | null>('/api/read-mentioned-file', {
596
+ absolutePath,
597
+ projectRoot,
598
+ maxTokens,
599
+ });
600
+
601
+ // ---------------------------------------------------------------------------
602
+ // Agent config reading
603
+ // ---------------------------------------------------------------------------
604
+
605
+ readAgentConfigs = (projectRoot: string): Promise<Record<string, AgentConfig>> =>
606
+ this.post<Record<string, AgentConfig>>('/api/read-agent-configs', { projectRoot });
607
+
608
+ // ---------------------------------------------------------------------------
609
+ // Notifications (nested API)
610
+ // ---------------------------------------------------------------------------
611
+
612
+ notifications: NotificationsAPI = {
613
+ get: (options) =>
614
+ this.get(
615
+ `/api/notifications?${new URLSearchParams(
616
+ options
617
+ ? {
618
+ limit: String(options.limit ?? 20),
619
+ offset: String(options.offset ?? 0),
620
+ }
621
+ : {}
622
+ )}`
623
+ ),
624
+ markRead: (id) => this.post(`/api/notifications/${encodeURIComponent(id)}/read`),
625
+ markAllRead: () => this.post('/api/notifications/read-all'),
626
+ delete: (id) => this.del(`/api/notifications/${encodeURIComponent(id)}`),
627
+ clear: () => this.del('/api/notifications'),
628
+ getUnreadCount: () => this.get('/api/notifications/unread-count'),
629
+ testNotification: async () => ({
630
+ success: false,
631
+ error: 'Test notifications require Electron (not available in browser mode)',
632
+ }),
633
+ // IPC signature: (event: unknown, error: unknown) => void
634
+ onNew: (callback) =>
635
+ this.addEventListener('notification:new', (data: unknown) => callback(null, data)),
636
+ // IPC signature: (event: unknown, payload: { total; unreadCount }) => void
637
+ onUpdated: (callback) =>
638
+ this.addEventListener('notification:updated', (data: unknown) =>
639
+ callback(null, data as { total: number; unreadCount: number })
640
+ ),
641
+ // IPC signature: (event: unknown, data: unknown) => void
642
+ onClicked: (callback) =>
643
+ this.addEventListener('notification:clicked', (data: unknown) => callback(null, data)),
644
+ };
645
+
646
+ // ---------------------------------------------------------------------------
647
+ // Config (nested API)
648
+ // ---------------------------------------------------------------------------
649
+
650
+ config: ConfigAPI = {
651
+ get: async (): Promise<AppConfig> => {
652
+ const result = await this.get<{ success: boolean; data?: AppConfig; error?: string }>(
653
+ '/api/config'
654
+ );
655
+ if (!result.success) throw new Error(result.error ?? 'Failed to get config');
656
+ return result.data!;
657
+ },
658
+ update: async (section: string, data: object): Promise<AppConfig> => {
659
+ const result = await this.post<{ success: boolean; data?: AppConfig; error?: string }>(
660
+ '/api/config/update',
661
+ { section, data }
662
+ );
663
+ if (!result.success) throw new Error(result.error ?? 'Failed to update config');
664
+ return result.data!;
665
+ },
666
+ addIgnoreRegex: async (pattern: string): Promise<AppConfig> => {
667
+ await this.post('/api/config/ignore-regex', { pattern });
668
+ return this.config.get();
669
+ },
670
+ removeIgnoreRegex: async (pattern: string): Promise<AppConfig> => {
671
+ await this.del('/api/config/ignore-regex', { pattern });
672
+ return this.config.get();
673
+ },
674
+ addIgnoreRepository: async (repositoryId: string): Promise<AppConfig> => {
675
+ await this.post('/api/config/ignore-repository', { repositoryId });
676
+ return this.config.get();
677
+ },
678
+ removeIgnoreRepository: async (repositoryId: string): Promise<AppConfig> => {
679
+ await this.del('/api/config/ignore-repository', { repositoryId });
680
+ return this.config.get();
681
+ },
682
+ snooze: async (minutes: number): Promise<AppConfig> => {
683
+ await this.post('/api/config/snooze', { minutes });
684
+ return this.config.get();
685
+ },
686
+ clearSnooze: async (): Promise<AppConfig> => {
687
+ await this.post('/api/config/clear-snooze');
688
+ return this.config.get();
689
+ },
690
+ addTrigger: async (trigger): Promise<AppConfig> => {
691
+ await this.post('/api/config/triggers', trigger);
692
+ return this.config.get();
693
+ },
694
+ updateTrigger: async (triggerId: string, updates): Promise<AppConfig> => {
695
+ await this.put(`/api/config/triggers/${encodeURIComponent(triggerId)}`, updates);
696
+ return this.config.get();
697
+ },
698
+ removeTrigger: async (triggerId: string): Promise<AppConfig> => {
699
+ await this.del(`/api/config/triggers/${encodeURIComponent(triggerId)}`);
700
+ return this.config.get();
701
+ },
702
+ getTriggers: async (): Promise<NotificationTrigger[]> => {
703
+ const result = await this.get<{
704
+ success: boolean;
705
+ data?: NotificationTrigger[];
706
+ error?: string;
707
+ }>('/api/config/triggers');
708
+ if (!result.success) throw new Error(result.error ?? 'Failed to get triggers');
709
+ return result.data ?? [];
710
+ },
711
+ testTrigger: async (trigger: NotificationTrigger): Promise<TriggerTestResult> => {
712
+ const result = await this.post<{
713
+ success: boolean;
714
+ data?: TriggerTestResult;
715
+ error?: string;
716
+ }>(`/api/config/triggers/${encodeURIComponent(trigger.id)}/test`, trigger);
717
+ if (!result.success) throw new Error(result.error ?? 'Failed to test trigger');
718
+ return result.data!;
719
+ },
720
+ selectFolders: async (): Promise<string[]> => {
721
+ // Fallback: return home directory as default
722
+ return [process.env.HOME ?? '/'];
723
+ },
724
+ browseFolders: async (dirPath?: string): Promise<{ path: string; dirs: string[]; hasParent: boolean }> => {
725
+ const res = await this.post<{ success: boolean; data?: { path: string; dirs: string[]; hasParent: boolean }; error?: string }>(
726
+ '/api/config/browse-folders',
727
+ { path: dirPath ?? '' }
728
+ );
729
+ if (!res.success) throw new Error(res.error ?? '无法浏览目录');
730
+ return res.data!;
731
+ },
732
+ selectClaudeRootFolder: async (): Promise<ClaudeRootFolderSelection | null> => {
733
+ console.warn('[HttpAPIClient] selectClaudeRootFolder is not available in browser mode');
734
+ return null;
735
+ },
736
+ getClaudeRootInfo: async (): Promise<ClaudeRootInfo> => {
737
+ const config = await this.config.get();
738
+ const fallbackPath = config.general.claudeRootPath ?? '~/.claude';
739
+ return {
740
+ defaultPath: fallbackPath,
741
+ resolvedPath: fallbackPath,
742
+ customPath: config.general.claudeRootPath,
743
+ };
744
+ },
745
+ findWslClaudeRoots: async (): Promise<WslClaudeRootCandidate[]> => {
746
+ console.warn('[HttpAPIClient] findWslClaudeRoots is not available in browser mode');
747
+ return [];
748
+ },
749
+ openInEditor: async (): Promise<void> => {
750
+ console.warn('[HttpAPIClient] openInEditor is not available in browser mode');
751
+ },
752
+ pinSession: (projectId: string, sessionId: string): Promise<void> =>
753
+ this.post('/api/config/pin-session', { projectId, sessionId }),
754
+ unpinSession: (projectId: string, sessionId: string): Promise<void> =>
755
+ this.post('/api/config/unpin-session', { projectId, sessionId }),
756
+ hideSession: (projectId: string, sessionId: string): Promise<void> =>
757
+ this.post('/api/config/hide-session', { projectId, sessionId }),
758
+ unhideSession: (projectId: string, sessionId: string): Promise<void> =>
759
+ this.post('/api/config/unhide-session', { projectId, sessionId }),
760
+ hideSessions: (projectId: string, sessionIds: string[]): Promise<void> =>
761
+ this.post('/api/config/hide-sessions', { projectId, sessionIds }),
762
+ unhideSessions: (projectId: string, sessionIds: string[]): Promise<void> =>
763
+ this.post('/api/config/unhide-sessions', { projectId, sessionIds }),
764
+ addCustomProjectPath: (projectPath: string): Promise<void> =>
765
+ this.post('/api/config/add-custom-project-path', { projectPath }),
766
+ removeCustomProjectPath: (projectPath: string): Promise<void> =>
767
+ this.post('/api/config/remove-custom-project-path', { projectPath }),
768
+ getClaudeEnv: async (): Promise<Record<string, string>> => {
769
+ const result = await this.get<{
770
+ success: boolean;
771
+ data?: Record<string, string>;
772
+ error?: string;
773
+ }>('/api/config/claude-env');
774
+ if (!result.success) throw new Error(result.error ?? 'Failed to get claude env');
775
+ return result.data ?? {};
776
+ },
777
+ updateClaudeEnv: async (env: Record<string, string>): Promise<Record<string, string>> => {
778
+ const result = await this.post<{
779
+ success: boolean;
780
+ data?: Record<string, string>;
781
+ error?: string;
782
+ }>('/api/config/claude-env', env);
783
+ if (!result.success) throw new Error(result.error ?? 'Failed to update claude env');
784
+ return result.data ?? env;
785
+ },
786
+ };
787
+
788
+ // ---------------------------------------------------------------------------
789
+ // Session navigation
790
+ // ---------------------------------------------------------------------------
791
+
792
+ session: SessionAPI = {
793
+ scrollToLine: (sessionId: string, lineNumber: number): Promise<void> =>
794
+ this.post('/api/session/scroll-to-line', { sessionId, lineNumber }),
795
+ };
796
+
797
+ // ---------------------------------------------------------------------------
798
+ // Zoom (browser fallbacks)
799
+ // ---------------------------------------------------------------------------
800
+
801
+ getZoomFactor = async (): Promise<number> => 1.0;
802
+
803
+ onZoomFactorChanged = (_callback: (zoomFactor: number) => void): (() => void) => {
804
+ // No-op in browser mode — zoom is managed by the browser itself
805
+ return () => {};
806
+ };
807
+
808
+ // ---------------------------------------------------------------------------
809
+ // File change events (via SSE)
810
+ // ---------------------------------------------------------------------------
811
+
812
+ onFileChange = (callback: (event: FileChangeEvent) => void): (() => void) =>
813
+ this.addEventListener('file-change', callback);
814
+
815
+ onTodoChange = (callback: (event: FileChangeEvent) => void): (() => void) =>
816
+ this.addEventListener('todo-change', callback);
817
+
818
+ // ---------------------------------------------------------------------------
819
+ // Shell operations (browser fallbacks)
820
+ // ---------------------------------------------------------------------------
821
+
822
+ openPath = async (
823
+ _targetPath: string,
824
+ _projectRoot?: string
825
+ ): Promise<{ success: boolean; error?: string }> => {
826
+ console.warn('[HttpAPIClient] openPath is not available in browser mode');
827
+ return { success: false, error: 'Not available in browser mode' };
828
+ };
829
+
830
+ showInFolder = async (_filePath: string): Promise<void> => {
831
+ console.warn('[HttpAPIClient] showInFolder is not available in browser mode');
832
+ };
833
+
834
+ openExternal = async (url: string): Promise<{ success: boolean; error?: string }> => {
835
+ window.open(url, '_blank');
836
+ return { success: true };
837
+ };
838
+
839
+ windowControls = {
840
+ minimize: async (): Promise<void> => {},
841
+ maximize: async (): Promise<void> => {},
842
+ close: async (): Promise<void> => {},
843
+ isMaximized: async (): Promise<boolean> => false,
844
+ isFullScreen: async (): Promise<boolean> => false,
845
+ relaunch: async (): Promise<void> => {},
846
+ };
847
+
848
+ onFullScreenChange =
849
+ (_callback: (isFullScreen: boolean) => void): (() => void) =>
850
+ () => {};
851
+
852
+ // ---------------------------------------------------------------------------
853
+ // Updater (browser no-ops)
854
+ // ---------------------------------------------------------------------------
855
+
856
+ updater: UpdaterAPI = {
857
+ check: async (): Promise<void> => {
858
+ console.warn('[HttpAPIClient] updater not available in browser mode');
859
+ },
860
+ download: async (): Promise<void> => {
861
+ console.warn('[HttpAPIClient] updater not available in browser mode');
862
+ },
863
+ install: async (): Promise<void> => {
864
+ console.warn('[HttpAPIClient] updater not available in browser mode');
865
+ },
866
+ onStatus: (_callback): (() => void) => {
867
+ return () => {};
868
+ },
869
+ };
870
+
871
+ // ---------------------------------------------------------------------------
872
+ // SSH
873
+ // ---------------------------------------------------------------------------
874
+
875
+ ssh: SshAPI = {
876
+ connect: async (config: SshConnectionConfig): Promise<SshConnectionStatus> => {
877
+ const result = await this.post<{
878
+ success: boolean;
879
+ data?: SshConnectionStatus;
880
+ error?: string;
881
+ }>('/api/ssh/connect', config);
882
+ if (!result.success) throw new Error(result.error ?? 'SSH connect failed');
883
+ return result.data!;
884
+ },
885
+ disconnect: async (): Promise<SshConnectionStatus> => {
886
+ const result = await this.post<{
887
+ success: boolean;
888
+ data?: SshConnectionStatus;
889
+ error?: string;
890
+ }>('/api/ssh/disconnect');
891
+ if (!result.success) throw new Error(result.error ?? 'SSH disconnect failed');
892
+ return result.data!;
893
+ },
894
+ getState: (): Promise<SshConnectionStatus> => this.get('/api/ssh/state'),
895
+ test: async (config: SshConnectionConfig): Promise<{ success: boolean; error?: string }> => {
896
+ const result = await this.post<{
897
+ success: boolean;
898
+ data?: { success: boolean; error?: string };
899
+ error?: string;
900
+ }>('/api/ssh/test', config);
901
+ if (!result.success) return { success: false, error: result.error };
902
+ return result.data ?? { success: true };
903
+ },
904
+ listMachines: async () => {
905
+ const result = await this.get<{ success: boolean; data?: MachineProfile[] }>(
906
+ '/api/ssh/machines'
907
+ );
908
+ return result.data ?? [];
909
+ },
910
+ saveMachine: async (profile: MachineProfile) => {
911
+ const result = await this.post<{ success: boolean; data?: MachineProfile[] }>(
912
+ '/api/ssh/machines',
913
+ profile
914
+ );
915
+ return result.data ?? [];
916
+ },
917
+ removeMachine: async (machineId: string) => {
918
+ const result = await this.del<{ success: boolean; data?: MachineProfile[] }>(
919
+ `/api/ssh/machines/${encodeURIComponent(machineId)}`
920
+ );
921
+ return result.data ?? [];
922
+ },
923
+ checkMachine: async (machineId: string) => {
924
+ const result = await this.post<{ success: boolean; data?: MachineProfile }>(
925
+ `/api/ssh/machines/${encodeURIComponent(machineId)}/check`
926
+ );
927
+ if (!result.data) {
928
+ throw new Error('机器健康检查没有返回数据');
929
+ }
930
+ return result.data;
931
+ },
932
+ listMachineProcesses: async (machineId: string) => {
933
+ const result = await this.get<{ success: boolean; data?: MachineRuntimeProcess[] }>(
934
+ `/api/ssh/machines/${encodeURIComponent(machineId)}/processes`
935
+ );
936
+ return result.data ?? [];
937
+ },
938
+ stopMachineProcess: async (machineId: string, pid: number) => {
939
+ await this.post(`/api/ssh/machines/${encodeURIComponent(machineId)}/processes/stop`, {
940
+ pid,
941
+ });
942
+ },
943
+ getConfigHosts: async (): Promise<SshConfigHostEntry[]> => {
944
+ const result = await this.get<{ success: boolean; data?: SshConfigHostEntry[] }>(
945
+ '/api/ssh/config-hosts'
946
+ );
947
+ return result.data ?? [];
948
+ },
949
+ resolveHost: async (alias: string): Promise<SshConfigHostEntry | null> => {
950
+ const result = await this.post<{
951
+ success: boolean;
952
+ data?: SshConfigHostEntry | null;
953
+ }>('/api/ssh/resolve-host', { alias });
954
+ return result.data ?? null;
955
+ },
956
+ saveLastConnection: (config: SshLastConnection): Promise<void> =>
957
+ this.post('/api/ssh/save-last-connection', config),
958
+ getLastConnection: async (): Promise<SshLastConnection | null> => {
959
+ const result = await this.get<{ success: boolean; data?: SshLastConnection | null }>(
960
+ '/api/ssh/last-connection'
961
+ );
962
+ return result.data ?? null;
963
+ },
964
+ // IPC signature: (event: unknown, status: SshConnectionStatus) => void
965
+ onStatus: (callback): (() => void) =>
966
+ this.addEventListener('ssh:status', (data: unknown) =>
967
+ callback(null, data as SshConnectionStatus)
968
+ ),
969
+ };
970
+
971
+ // ---------------------------------------------------------------------------
972
+ // Context API
973
+ // ---------------------------------------------------------------------------
974
+
975
+ context = {
976
+ list: (): Promise<ContextInfo[]> => this.get<ContextInfo[]>('/api/contexts'),
977
+ getActive: (): Promise<string> => this.get<string>('/api/contexts/active'),
978
+ switch: (contextId: string): Promise<{ contextId: string }> =>
979
+ this.post<{ contextId: string }>('/api/contexts/switch', { contextId }),
980
+ onChanged: (callback: (event: unknown, data: ContextInfo) => void): (() => void) =>
981
+ this.addEventListener('context:changed', (data: unknown) =>
982
+ callback(null, data as ContextInfo)
983
+ ),
984
+ };
985
+
986
+ // HTTP Server API — in browser mode, server is already running (we're using it)
987
+ httpServer: HttpServerAPI = {
988
+ start: (): Promise<HttpServerStatus> =>
989
+ Promise.resolve({ running: true, port: parseInt(new URL(this.baseUrl).port, 10) }),
990
+ stop: (): Promise<HttpServerStatus> => {
991
+ console.warn('[HttpAPIClient] Cannot stop HTTP server from browser mode');
992
+ return Promise.resolve({ running: true, port: parseInt(new URL(this.baseUrl).port, 10) });
993
+ },
994
+ getStatus: (): Promise<HttpServerStatus> =>
995
+ Promise.resolve({ running: true, port: parseInt(new URL(this.baseUrl).port, 10) }),
996
+ };
997
+
998
+ teams: TeamsAPI = {
999
+ list: async (): Promise<TeamSummary[]> => this.get<TeamSummary[]>('/api/teams'),
1000
+ getData: async (teamName: string): Promise<TeamViewSnapshot> =>
1001
+ this.get<TeamViewSnapshot>(`/api/teams/${encodeURIComponent(teamName)}/data`),
1002
+ getTaskChangePresence: async (): Promise<
1003
+ Record<string, 'has_changes' | 'no_changes' | 'unknown'>
1004
+ > => {
1005
+ return {};
1006
+ },
1007
+ setChangePresenceTracking: async (): Promise<void> => {
1008
+ // Not available in browser mode — no-op.
1009
+ },
1010
+ setTaskLogStreamTracking: async (): Promise<void> => {
1011
+ // Not available in browser mode — no-op.
1012
+ },
1013
+ setToolActivityTracking: async (): Promise<void> => {
1014
+ // Not available in browser mode — no-op.
1015
+ },
1016
+ getClaudeLogs: async (
1017
+ teamName: string,
1018
+ query?: TeamClaudeLogsQuery
1019
+ ): Promise<TeamClaudeLogsResponse> => {
1020
+ const params = new URLSearchParams();
1021
+ if (typeof query?.offset === 'number') params.set('offset', String(query.offset));
1022
+ if (typeof query?.limit === 'number') params.set('limit', String(query.limit));
1023
+ const qs = params.toString();
1024
+ const path = `/api/teams/${encodeURIComponent(teamName)}/claude-logs`;
1025
+ return this.get<TeamClaudeLogsResponse>(qs ? `${path}?${qs}` : path);
1026
+ },
1027
+ deleteTeam: async (teamName: string): Promise<void> => {
1028
+ await this.del(`/api/teams/${encodeURIComponent(teamName)}`);
1029
+ },
1030
+ restoreTeam: async (teamName: string): Promise<void> => {
1031
+ await this.post(`/api/teams/${encodeURIComponent(teamName)}/restore`);
1032
+ },
1033
+ permanentlyDeleteTeam: async (teamName: string): Promise<void> => {
1034
+ await this.del(`/api/teams/${encodeURIComponent(teamName)}/permanent`);
1035
+ },
1036
+ getSavedRequest: async (teamName: string): Promise<TeamCreateRequest | null> => {
1037
+ try {
1038
+ return await this.get(`/api/teams/${encodeURIComponent(teamName)}/saved-request`);
1039
+ } catch {
1040
+ return null;
1041
+ }
1042
+ },
1043
+ deleteDraft: async (teamName: string): Promise<void> => {
1044
+ await this.del(`/api/teams/${encodeURIComponent(teamName)}/draft`);
1045
+ },
1046
+ prepareProvisioning: async (
1047
+ cwd?: string,
1048
+ providerId?: TeamLaunchRequest['providerId'],
1049
+ providerIds?: TeamLaunchRequest['providerId'][],
1050
+ selectedModels?: string[],
1051
+ limitContext?: boolean,
1052
+ modelVerificationMode?: TeamProvisioningModelVerificationMode
1053
+ ): Promise<TeamProvisioningPrepareResult> => {
1054
+ return this.post<TeamProvisioningPrepareResult>('/api/teams/provisioning/prepare', {
1055
+ cwd,
1056
+ providerId,
1057
+ providerIds,
1058
+ selectedModels,
1059
+ limitContext,
1060
+ modelVerificationMode,
1061
+ });
1062
+ },
1063
+ listTemplateSources: async (): Promise<TeamTemplateSourcesSnapshot> => {
1064
+ return this.get<TeamTemplateSourcesSnapshot>('/api/teams/templates');
1065
+ },
1066
+ saveTemplateSources: async (
1067
+ sources: TeamTemplateSource[]
1068
+ ): Promise<TeamTemplateSourcesSnapshot> => {
1069
+ return this.post<TeamTemplateSourcesSnapshot>('/api/teams/templates/save', sources);
1070
+ },
1071
+ refreshTemplateSources: async (): Promise<TeamTemplateSourcesSnapshot> => {
1072
+ return this.postLong<TeamTemplateSourcesSnapshot>('/api/teams/templates/refresh');
1073
+ },
1074
+ createTeam: async (request: TeamCreateRequest): Promise<TeamCreateResponse> => {
1075
+ return this.postLong<TeamCreateResponse>('/api/teams/create', request, 120_000);
1076
+ },
1077
+ launchTeam: async (request: TeamLaunchRequest): Promise<TeamLaunchResponse> => {
1078
+ return this.postLong<TeamLaunchResponse>(
1079
+ `/api/teams/${encodeURIComponent(request.teamName)}/launch`,
1080
+ request
1081
+ );
1082
+ },
1083
+ getProvisioningStatus: async (runId: string): Promise<TeamProvisioningProgress> => {
1084
+ return this.get<TeamProvisioningProgress>(
1085
+ `/api/teams/provisioning/${encodeURIComponent(runId)}`
1086
+ );
1087
+ },
1088
+ cancelProvisioning: async (runId: string): Promise<void> => {
1089
+ await this.post(`/api/teams/provisioning/${encodeURIComponent(runId)}/cancel`);
1090
+ },
1091
+ sendMessage: async (
1092
+ teamName: string,
1093
+ request: SendMessageRequest
1094
+ ): Promise<SendMessageResult> =>
1095
+ this.postLong<SendMessageResult>(
1096
+ `/api/teams/${encodeURIComponent(teamName)}/send-message`,
1097
+ request,
1098
+ 30_000
1099
+ ),
1100
+ getMessagesPage: async (
1101
+ teamName: string,
1102
+ opts?: { cursor?: string | null; limit?: number }
1103
+ ) => {
1104
+ const params = new URLSearchParams();
1105
+ if (opts?.cursor) params.set('cursor', opts.cursor);
1106
+ if (opts?.limit) params.set('limit', String(opts.limit));
1107
+ const qs = params.toString();
1108
+ const path = `/api/teams/${encodeURIComponent(teamName)}/messages`;
1109
+ return this.get(qs ? `${path}?${qs}` : path);
1110
+ },
1111
+ getMemberActivityMeta: async (teamName: string): Promise<TeamMemberActivityMeta> =>
1112
+ this.get<TeamMemberActivityMeta>(
1113
+ `/api/teams/${encodeURIComponent(teamName)}/member-activity`
1114
+ ),
1115
+ createTask: async (teamName: string, request: CreateTaskRequest): Promise<TeamTask> =>
1116
+ this.post<TeamTask>(`/api/teams/${encodeURIComponent(teamName)}/tasks`, request),
1117
+ requestReview: async (teamName: string, taskId: string): Promise<void> => {
1118
+ await this.post(
1119
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/review`
1120
+ );
1121
+ },
1122
+ updateKanban: async (
1123
+ teamName: string,
1124
+ taskId: string,
1125
+ patch: UpdateKanbanPatch
1126
+ ): Promise<void> => {
1127
+ await this.patch(
1128
+ `/api/teams/${encodeURIComponent(teamName)}/kanban/${encodeURIComponent(taskId)}`,
1129
+ patch
1130
+ );
1131
+ },
1132
+ updateKanbanColumnOrder: async (
1133
+ teamName: string,
1134
+ columnId: KanbanColumnId,
1135
+ orderedTaskIds: string[]
1136
+ ): Promise<void> => {
1137
+ await this.put(`/api/teams/${encodeURIComponent(teamName)}/kanban/column-order`, {
1138
+ columnId,
1139
+ orderedTaskIds,
1140
+ });
1141
+ },
1142
+ updateTaskStatus: async (
1143
+ teamName: string,
1144
+ taskId: string,
1145
+ status: TeamTaskStatus
1146
+ ): Promise<void> => {
1147
+ await this.patch(
1148
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/status`,
1149
+ { status }
1150
+ );
1151
+ },
1152
+ updateTaskOwner: async (
1153
+ teamName: string,
1154
+ taskId: string,
1155
+ owner: string | null
1156
+ ): Promise<void> => {
1157
+ await this.patch(
1158
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/owner`,
1159
+ { owner }
1160
+ );
1161
+ },
1162
+ updateTaskFields: async (
1163
+ teamName: string,
1164
+ taskId: string,
1165
+ fields: { subject?: string; description?: string }
1166
+ ): Promise<void> => {
1167
+ await this.patch(
1168
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/fields`,
1169
+ fields
1170
+ );
1171
+ },
1172
+ startTask: async (teamName: string, taskId: string): Promise<{ notifiedOwner: boolean }> =>
1173
+ this.post<{ notifiedOwner: boolean }>(
1174
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/start`
1175
+ ),
1176
+ startTaskByUser: async (
1177
+ teamName: string,
1178
+ taskId: string
1179
+ ): Promise<{ notifiedOwner: boolean }> =>
1180
+ this.post<{ notifiedOwner: boolean }>(
1181
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/start-by-user`
1182
+ ),
1183
+ processSend: async (teamName: string, message: string): Promise<void> => {
1184
+ await this.post(`/api/teams/${encodeURIComponent(teamName)}/process-send`, { message });
1185
+ },
1186
+ setCollaboration: async (teamName: string, collaboration: boolean): Promise<void> => {
1187
+ await this.patch(`/api/teams/${encodeURIComponent(teamName)}/collaboration`, { collaboration });
1188
+ },
1189
+ processAlive: async (teamName: string): Promise<boolean> => {
1190
+ try {
1191
+ const states = await this.get<
1192
+ { teamName: string; isAlive: boolean; runId: string | null }[]
1193
+ >('/api/teams/runtime/alive');
1194
+ return states.some((s) => s.teamName === teamName && s.isAlive);
1195
+ } catch {
1196
+ return false;
1197
+ }
1198
+ },
1199
+ aliveList: async (): Promise<string[]> => {
1200
+ const states = await this.get<{ teamName: string; isAlive: boolean; runId: string | null }[]>(
1201
+ '/api/teams/runtime/alive'
1202
+ );
1203
+ return states.filter((s) => s.isAlive).map((s) => s.teamName);
1204
+ },
1205
+ stop: async (teamName: string): Promise<void> => {
1206
+ await this.post(`/api/teams/${encodeURIComponent(teamName)}/stop`);
1207
+ },
1208
+ createConfig: async (request: TeamCreateConfigRequest): Promise<void> => {
1209
+ await this.post('/api/teams/config', request);
1210
+ },
1211
+ getMemberLogs: async (teamName: string, memberName: string): Promise<MemberLogSummary[]> => {
1212
+ return this.get<MemberLogSummary[]>(
1213
+ `/api/teams/${encodeURIComponent(teamName)}/member-logs/${encodeURIComponent(memberName)}`
1214
+ );
1215
+ },
1216
+ getLogsForTask: async (
1217
+ teamName: string,
1218
+ taskId: string,
1219
+ options?: {
1220
+ owner?: string;
1221
+ status?: string;
1222
+ intervals?: { startedAt: string; completedAt?: string }[];
1223
+ since?: string;
1224
+ }
1225
+ ): Promise<MemberLogSummary[]> => {
1226
+ const params = new URLSearchParams();
1227
+ if (options?.owner) params.set('owner', options.owner);
1228
+ if (options?.status) params.set('status', options.status);
1229
+ if (options?.intervals) params.set('intervals', JSON.stringify(options.intervals));
1230
+ if (options?.since) params.set('since', options.since);
1231
+ const qs = params.toString();
1232
+ const path = `/api/teams/${encodeURIComponent(teamName)}/task-logs/${encodeURIComponent(taskId)}`;
1233
+ return this.get(qs ? `${path}?${qs}` : path);
1234
+ },
1235
+ getTaskActivity: async (teamName: string, taskId: string) => {
1236
+ const params = new URLSearchParams();
1237
+ params.set('taskId', taskId);
1238
+ return this.get(`/api/teams/${encodeURIComponent(teamName)}/activity?${params}`);
1239
+ },
1240
+ getTaskActivityDetail: async (
1241
+ teamName: string,
1242
+ taskId: string,
1243
+ activityId: string
1244
+ ): Promise<BoardTaskActivityDetailResult> => {
1245
+ const params = new URLSearchParams();
1246
+ params.set('taskId', taskId);
1247
+ params.set('activityId', activityId);
1248
+ return this.get(`/api/teams/${encodeURIComponent(teamName)}/task-activity-detail?${params}`);
1249
+ },
1250
+ getTaskLogStreamSummary: async (
1251
+ teamName: string,
1252
+ taskId: string
1253
+ ): Promise<BoardTaskLogStreamSummary> => {
1254
+ return this.get(
1255
+ `/api/teams/${encodeURIComponent(teamName)}/task-log-stream-summary/${encodeURIComponent(taskId)}`
1256
+ );
1257
+ },
1258
+ getTaskLogStream: async (
1259
+ teamName: string,
1260
+ taskId: string
1261
+ ): Promise<BoardTaskLogStreamResponse> => {
1262
+ return this.get(
1263
+ `/api/teams/${encodeURIComponent(teamName)}/task-log-stream/${encodeURIComponent(taskId)}`
1264
+ );
1265
+ },
1266
+ getTaskExactLogSummaries: async (
1267
+ teamName: string,
1268
+ taskId: string
1269
+ ): Promise<BoardTaskExactLogSummariesResponse> => {
1270
+ return this.get(
1271
+ `/api/teams/${encodeURIComponent(teamName)}/exact-log-summaries/${encodeURIComponent(taskId)}`
1272
+ );
1273
+ },
1274
+ getTaskExactLogDetail: async (
1275
+ teamName: string,
1276
+ taskId: string,
1277
+ exactLogId: string,
1278
+ expectedSourceGeneration: string
1279
+ ): Promise<BoardTaskExactLogDetailResult> => {
1280
+ const params = new URLSearchParams();
1281
+ params.set('exactLogId', exactLogId);
1282
+ params.set('expectedSourceGeneration', expectedSourceGeneration);
1283
+ return this.get(
1284
+ `/api/teams/${encodeURIComponent(teamName)}/exact-log-detail/${encodeURIComponent(taskId)}?${params}`
1285
+ );
1286
+ },
1287
+ getMemberStats: async (teamName: string, memberName: string) => {
1288
+ return this.get<MemberFullStats>(
1289
+ `/api/teams/${encodeURIComponent(teamName)}/member-stats/${encodeURIComponent(memberName)}`
1290
+ );
1291
+ },
1292
+ getAllTasks: async (): Promise<GlobalTask[]> => this.get<GlobalTask[]>('/api/teams/tasks'),
1293
+ updateConfig: async (
1294
+ teamName: string,
1295
+ config: TeamUpdateConfigRequest
1296
+ ): Promise<TeamConfig> => {
1297
+ return this.put(`/api/teams/${encodeURIComponent(teamName)}/config`, config);
1298
+ },
1299
+ addTaskComment: async (
1300
+ teamName: string,
1301
+ taskId: string,
1302
+ request: AddTaskCommentRequest
1303
+ ): Promise<TaskComment> => {
1304
+ return this.post(
1305
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/comments`,
1306
+ request
1307
+ );
1308
+ },
1309
+ addMember: async (teamName: string, request: AddMemberRequest): Promise<void> => {
1310
+ await this.post(`/api/teams/${encodeURIComponent(teamName)}/members`, request);
1311
+ },
1312
+ replaceMembers: async (teamName: string, request: ReplaceMembersRequest): Promise<void> => {
1313
+ await this.post(`/api/teams/${encodeURIComponent(teamName)}/members`, {
1314
+ action: 'replace',
1315
+ ...request,
1316
+ });
1317
+ },
1318
+ removeMember: async (teamName: string, memberName: string): Promise<void> => {
1319
+ await this.del(
1320
+ `/api/teams/${encodeURIComponent(teamName)}/members/${encodeURIComponent(memberName)}`
1321
+ );
1322
+ },
1323
+ updateMemberRole: async (
1324
+ teamName: string,
1325
+ memberName: string,
1326
+ role: string | undefined
1327
+ ): Promise<void> => {
1328
+ await this.patch(
1329
+ `/api/teams/${encodeURIComponent(teamName)}/members/${encodeURIComponent(memberName)}/role`,
1330
+ { role }
1331
+ );
1332
+ },
1333
+ getProjectBranch: async (_projectPath: string): Promise<string | null> => {
1334
+ return null;
1335
+ },
1336
+ setProjectBranchTracking: async (): Promise<void> => {
1337
+ // Not available in browser mode — no-op.
1338
+ },
1339
+ getAttachments: async (
1340
+ _teamName: string,
1341
+ _messageId: string
1342
+ ): Promise<AttachmentFileData[]> => {
1343
+ return [];
1344
+ },
1345
+ killProcess: async (teamName: string, pid: number): Promise<void> => {
1346
+ await this.post(`/api/teams/${encodeURIComponent(teamName)}/kill-process`, { pid });
1347
+ },
1348
+ getLeadActivity: async (teamName: string) => {
1349
+ return this.get(`/api/teams/${encodeURIComponent(teamName)}/lead-activity`);
1350
+ },
1351
+ getLeadContext: async (teamName: string) => {
1352
+ return this.get(`/api/teams/${encodeURIComponent(teamName)}/lead-context`);
1353
+ },
1354
+ getMemberSpawnStatuses: async (teamName: string) => {
1355
+ return this.get<MemberSpawnStatusesSnapshot>(
1356
+ `/api/teams/${encodeURIComponent(teamName)}/member-spawn-statuses`
1357
+ );
1358
+ },
1359
+ getTeamAgentRuntime: async (teamName: string) => {
1360
+ return this.get<TeamAgentRuntimeSnapshot>(
1361
+ `/api/teams/${encodeURIComponent(teamName)}/agent-runtime`
1362
+ );
1363
+ },
1364
+ restartMember: async (teamName: string, memberName: string): Promise<void> => {
1365
+ await this.post(
1366
+ `/api/teams/${encodeURIComponent(teamName)}/members/${encodeURIComponent(memberName)}/restart`
1367
+ );
1368
+ },
1369
+ skipMemberForLaunch: async (teamName: string, memberName: string): Promise<void> => {
1370
+ await this.post(
1371
+ `/api/teams/${encodeURIComponent(teamName)}/members/${encodeURIComponent(memberName)}/skip`
1372
+ );
1373
+ },
1374
+ softDeleteTask: async (teamName: string, taskId: string): Promise<void> => {
1375
+ await this.post(
1376
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/soft-delete`
1377
+ );
1378
+ },
1379
+ restoreTask: async (teamName: string, taskId: string): Promise<void> => {
1380
+ await this.post(
1381
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/restore`
1382
+ );
1383
+ },
1384
+ getDeletedTasks: async (teamName: string): Promise<TeamTask[]> => {
1385
+ return this.get<TeamTask[]>(`/api/teams/${encodeURIComponent(teamName)}/deleted-tasks`);
1386
+ },
1387
+ setTaskClarification: async (
1388
+ teamName: string,
1389
+ taskId: string,
1390
+ value: 'lead' | 'user' | null
1391
+ ): Promise<void> => {
1392
+ await this.post(
1393
+ `/api/teams/${encodeURIComponent(teamName)}/task-clarification/${encodeURIComponent(taskId)}`,
1394
+ { value }
1395
+ );
1396
+ },
1397
+ showMessageNotification: async (): Promise<void> => {
1398
+ // Not available via HTTP client — native notifications require Electron
1399
+ },
1400
+ addTaskRelationship: async (
1401
+ teamName: string,
1402
+ taskId: string,
1403
+ targetId: string,
1404
+ type: 'blockedBy' | 'blocks' | 'related'
1405
+ ): Promise<void> => {
1406
+ await this.post(
1407
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/relationships`,
1408
+ { targetId, type }
1409
+ );
1410
+ },
1411
+ removeTaskRelationship: async (
1412
+ teamName: string,
1413
+ taskId: string,
1414
+ targetId: string,
1415
+ type: 'blockedBy' | 'blocks' | 'related'
1416
+ ): Promise<void> => {
1417
+ await this.del(
1418
+ `/api/teams/${encodeURIComponent(teamName)}/tasks/${encodeURIComponent(taskId)}/relationships`,
1419
+ { targetId, type }
1420
+ );
1421
+ },
1422
+ saveTaskAttachment: async (
1423
+ _teamName: string,
1424
+ _taskId: string,
1425
+ _attachmentId: string,
1426
+ _filename: string,
1427
+ _mimeType: string,
1428
+ _base64Data: string
1429
+ ): Promise<never> => {
1430
+ throw new Error('Task attachments are not available in browser mode');
1431
+ },
1432
+ getTaskAttachment: async (
1433
+ _teamName: string,
1434
+ _taskId: string,
1435
+ _attachmentId: string,
1436
+ _mimeType: string
1437
+ ): Promise<string | null> => {
1438
+ return null;
1439
+ },
1440
+ deleteTaskAttachment: async (
1441
+ _teamName: string,
1442
+ _taskId: string,
1443
+ _attachmentId: string,
1444
+ _mimeType: string
1445
+ ): Promise<void> => {
1446
+ throw new Error('Task attachments are not available in browser mode');
1447
+ },
1448
+ onProjectBranchChange: (): (() => void) => {
1449
+ return () => {};
1450
+ },
1451
+ getTeamSessions: async (teamName: string): Promise<CcSession[]> =>
1452
+ this.get<CcSession[]>(`/api/teams/${encodeURIComponent(teamName)}/sessions`),
1453
+ getSessionDetail: async (
1454
+ teamName: string,
1455
+ sessionId: string,
1456
+ historyLimit: number = 50
1457
+ ): Promise<CcSessionDetail> => {
1458
+ const params = new URLSearchParams();
1459
+ if (historyLimit) params.set('history_limit', String(historyLimit));
1460
+ const qs = params.toString();
1461
+ const suffix = qs ? `?${qs}` : '';
1462
+ return this.get<CcSessionDetail>(
1463
+ `/api/teams/${encodeURIComponent(teamName)}/sessions/${encodeURIComponent(sessionId)}${suffix}`
1464
+ );
1465
+ },
1466
+ cancelSession: async (teamName: string, sessionId: string): Promise<void> =>
1467
+ await this.delete(`/api/teams/${encodeURIComponent(teamName)}/sessions/${encodeURIComponent(sessionId)}`),
1468
+ onTeamChange: (callback: (event: unknown, data: TeamChangeEvent) => void): (() => void) => {
1469
+ return this.addEventListener('team-change', (data: unknown) =>
1470
+ callback(null, data as TeamChangeEvent)
1471
+ );
1472
+ },
1473
+ onProvisioningProgress: (
1474
+ callback: (event: unknown, data: TeamProvisioningProgress) => void
1475
+ ): (() => void) => {
1476
+ return this.addEventListener('provisioning-progress', (data: unknown) => {
1477
+ callback(null, data as TeamProvisioningProgress);
1478
+ });
1479
+ },
1480
+ respondToToolApproval: async (
1481
+ teamName: string,
1482
+ runId: string,
1483
+ requestId: string,
1484
+ allow: boolean,
1485
+ message?: string
1486
+ ): Promise<void> => {
1487
+ await this.post(`/api/teams/${encodeURIComponent(teamName)}/tool-approval/respond`, {
1488
+ runId,
1489
+ requestId,
1490
+ allow,
1491
+ message,
1492
+ });
1493
+ },
1494
+ validateCliArgs: async (rawArgs: string): Promise<CliArgsValidationResult> => {
1495
+ return this.post<CliArgsValidationResult>('/api/teams/validate-cli-args', { rawArgs });
1496
+ },
1497
+ onToolApprovalEvent: (
1498
+ callback: (event: unknown, data: ToolApprovalEvent) => void
1499
+ ): (() => void) => {
1500
+ return this.addEventListener('tool-approval-event', (data: unknown) => {
1501
+ callback(null, data as ToolApprovalEvent);
1502
+ });
1503
+ },
1504
+ updateToolApprovalSettings: async (
1505
+ teamName: string,
1506
+ settings: ToolApprovalSettings
1507
+ ): Promise<void> => {
1508
+ await this.post(
1509
+ `/api/teams/${encodeURIComponent(teamName)}/tool-approval/settings`,
1510
+ settings
1511
+ );
1512
+ },
1513
+ readFileForToolApproval: async (filePath: string): Promise<ToolApprovalFileContent> => {
1514
+ return this.post<ToolApprovalFileContent>('/api/teams/tool-approval/read-file', { filePath });
1515
+ },
1516
+ };
1517
+
1518
+ // Cross-team communication API
1519
+ crossTeam: CrossTeamAPI = {
1520
+ send: (request) => this.post<CrossTeamSendResult>('/api/cross-team/send', request),
1521
+ listTargets: (excludeTeam?: string) => {
1522
+ const params = new URLSearchParams();
1523
+ if (excludeTeam) params.set('excludeTeam', excludeTeam);
1524
+ const qs = params.toString();
1525
+ return this.get<
1526
+ {
1527
+ teamName: string;
1528
+ displayName: string;
1529
+ description?: string;
1530
+ color?: string;
1531
+ leadName?: string;
1532
+ leadColor?: string;
1533
+ isOnline?: boolean;
1534
+ }[]
1535
+ >(qs ? `/api/cross-team/targets?${qs}` : '/api/cross-team/targets');
1536
+ },
1537
+ getOutbox: (teamName: string) =>
1538
+ this.get<CrossTeamMessage[]>(`/api/cross-team/outbox/${encodeURIComponent(teamName)}`),
1539
+ };
1540
+
1541
+ // Review API
1542
+ review = {
1543
+ getAgentChanges: async (teamName: string, memberName: string) => {
1544
+ return this.get<AgentChangeSet>(
1545
+ `/api/teams/${encodeURIComponent(teamName)}/review/agent-changes/${encodeURIComponent(memberName)}`
1546
+ );
1547
+ },
1548
+ getTaskChanges: async (
1549
+ teamName: string,
1550
+ taskId: string,
1551
+ options?: {
1552
+ owner?: string;
1553
+ status?: string;
1554
+ intervals?: { startedAt: string; completedAt?: string }[];
1555
+ since?: string;
1556
+ stateBucket?: 'approved' | 'review' | 'completed' | 'active';
1557
+ summaryOnly?: boolean;
1558
+ forceFresh?: boolean;
1559
+ }
1560
+ ) => {
1561
+ const params = new URLSearchParams();
1562
+ if (options?.owner) params.set('owner', options.owner);
1563
+ if (options?.status) params.set('status', options.status);
1564
+ if (options?.since) params.set('since', options.since);
1565
+ if (options?.stateBucket) params.set('stateBucket', options.stateBucket);
1566
+ if (options?.summaryOnly) params.set('summaryOnly', 'true');
1567
+ if (options?.forceFresh) params.set('forceFresh', 'true');
1568
+ const qs = params.toString();
1569
+ const path = `/api/teams/${encodeURIComponent(teamName)}/review/task-changes/${encodeURIComponent(taskId)}`;
1570
+ return this.get<TaskChangeSetV2>(qs ? `${path}?${qs}` : path);
1571
+ },
1572
+ invalidateTaskChangeSummaries: async (): Promise<void> => {
1573
+ console.warn(
1574
+ '[HttpAPIClient] invalidateTaskChangeSummaries is not available in browser mode'
1575
+ );
1576
+ },
1577
+ getChangeStats: async (teamName: string, memberName: string) => {
1578
+ return this.get<ChangeStats>(
1579
+ `/api/teams/${encodeURIComponent(teamName)}/review/change-stats/${encodeURIComponent(memberName)}`
1580
+ );
1581
+ },
1582
+ getFileContent: async (
1583
+ teamName: string,
1584
+ memberName: string | undefined,
1585
+ filePath: string,
1586
+ _snippets: SnippetDiff[] = []
1587
+ ) => {
1588
+ const params = new URLSearchParams();
1589
+ params.set('filePath', filePath);
1590
+ if (memberName) params.set('memberName', memberName);
1591
+ return this.get<FileChangeWithContent>(
1592
+ `/api/teams/${encodeURIComponent(teamName)}/review/file-content?${params}`
1593
+ );
1594
+ },
1595
+ applyDecisions: async (request: ApplyReviewRequest) => {
1596
+ return this.post<ApplyReviewResult>(
1597
+ `/api/teams/${encodeURIComponent(request.teamName)}/review/apply-decisions`,
1598
+ request
1599
+ );
1600
+ },
1601
+ checkConflict: async (filePath: string, expectedModified: string) => {
1602
+ return this.post<ConflictCheckResult>('/api/teams/review/check-conflict', {
1603
+ filePath,
1604
+ expectedModified,
1605
+ });
1606
+ },
1607
+ rejectHunks: async (
1608
+ filePath: string,
1609
+ original: string,
1610
+ modified: string,
1611
+ hunkIndices: number[],
1612
+ snippets: SnippetDiff[]
1613
+ ) => {
1614
+ // TeamName not in API interface; reject-hunks endpoint requires it in URL.
1615
+ // This is a limitation in browser mode — would need teamName tracking.
1616
+ throw new Error('Review reject hunks is not available in browser mode');
1617
+ },
1618
+ rejectFile: async (filePath: string, original: string, modified: string) => {
1619
+ throw new Error('Review reject file is not available in browser mode');
1620
+ },
1621
+ previewReject: async (
1622
+ filePath: string,
1623
+ original: string,
1624
+ modified: string,
1625
+ hunkIndices: number[],
1626
+ snippets: SnippetDiff[]
1627
+ ) => {
1628
+ return this.post<{ preview: string; hasConflicts: boolean }>(
1629
+ '/api/teams/review/preview-reject',
1630
+ { filePath, original, modified, hunkIndices, snippets }
1631
+ );
1632
+ },
1633
+ saveEditedFile: async (filePath: string, content: string, projectPath?: string) => {
1634
+ return this.post<{ success: boolean }>('/api/teams/review/save-edited-file', {
1635
+ filePath,
1636
+ content,
1637
+ projectPath,
1638
+ });
1639
+ },
1640
+ watchFiles: async (): Promise<void> => {
1641
+ console.warn('[HttpAPIClient] Review file watching is not available in browser mode');
1642
+ },
1643
+ unwatchFiles: async (): Promise<void> => {
1644
+ console.warn('[HttpAPIClient] Review file watching is not available in browser mode');
1645
+ },
1646
+ onExternalFileChange: (): (() => void) => {
1647
+ return () => {};
1648
+ },
1649
+ loadDecisions: async (teamName: string, scopeKey: string, scopeToken?: string) => {
1650
+ return this.post<{
1651
+ hunkDecisions: Record<string, HunkDecision>;
1652
+ fileDecisions: Record<string, HunkDecision>;
1653
+ hunkContextHashesByFile?: Record<string, Record<number, string>>;
1654
+ } | null>('/api/teams/review/decisions/load', { teamName, scopeKey, scopeToken });
1655
+ },
1656
+ saveDecisions: async (
1657
+ teamName: string,
1658
+ scopeKey: string,
1659
+ scopeToken: string,
1660
+ hunkDecisions: Record<string, HunkDecision>,
1661
+ fileDecisions: Record<string, HunkDecision>,
1662
+ hunkContextHashesByFile?: Record<string, Record<number, string>>
1663
+ ): Promise<void> => {
1664
+ await this.post('/api/teams/review/decisions/save', {
1665
+ teamName,
1666
+ scopeKey,
1667
+ scopeToken,
1668
+ hunkDecisions,
1669
+ fileDecisions,
1670
+ hunkContextHashesByFile,
1671
+ });
1672
+ },
1673
+ clearDecisions: async (
1674
+ teamName: string,
1675
+ scopeKey: string,
1676
+ scopeToken?: string
1677
+ ): Promise<void> => {
1678
+ await this.post('/api/teams/review/decisions/clear', { teamName, scopeKey, scopeToken });
1679
+ },
1680
+ getGitFileLog: async (projectPath: string, filePath: string) => {
1681
+ const params = new URLSearchParams();
1682
+ params.set('projectPath', projectPath);
1683
+ params.set('filePath', filePath);
1684
+ return this.get<{ hash: string; timestamp: string; message: string }[]>(
1685
+ `/api/teams/review/git-file-log?${params}`
1686
+ );
1687
+ },
1688
+ };
1689
+
1690
+ // ---------------------------------------------------------------------------
1691
+ // CLI Installer (not available in browser mode)
1692
+ // ---------------------------------------------------------------------------
1693
+
1694
+ cliInstaller: CliInstallerAPI = {
1695
+ getStatus: async () => {
1696
+ try {
1697
+ const result = await this.get<{
1698
+ installed: boolean;
1699
+ version: string | null;
1700
+ path: string | null;
1701
+ authenticated: boolean;
1702
+ }>('/api/cli/status');
1703
+
1704
+ // Fetch providers in parallel for Web mode
1705
+ const providerIds = ['anthropic', 'codex', 'gemini', 'opencode'] as const;
1706
+ const providerResults = await Promise.all(
1707
+ providerIds.map(async (providerId): Promise<CliProviderStatus | null> => {
1708
+ try {
1709
+ return await this.get<CliProviderStatus>(`/api/cli/provider/${providerId}/status`);
1710
+ } catch {
1711
+ return null;
1712
+ }
1713
+ })
1714
+ );
1715
+ const providers = providerResults.filter((p): p is NonNullable<typeof p> => p !== null);
1716
+
1717
+ return {
1718
+ flavor: 'agent_teams_orchestrator',
1719
+ displayName: 'Agent CLI',
1720
+ supportsSelfUpdate: false,
1721
+ showVersionDetails: false,
1722
+ showBinaryPath: false,
1723
+ installed: result.installed,
1724
+ installedVersion: result.version,
1725
+ binaryPath: result.path,
1726
+ launchError: null,
1727
+ latestVersion: null,
1728
+ updateAvailable: false,
1729
+ authLoggedIn: result.authenticated,
1730
+ authStatusChecking: true,
1731
+ authMethod: null,
1732
+ providers,
1733
+ };
1734
+ } catch {
1735
+ return {
1736
+ flavor: 'agent_teams_orchestrator',
1737
+ displayName: 'Agent CLI',
1738
+ supportsSelfUpdate: false,
1739
+ showVersionDetails: false,
1740
+ showBinaryPath: false,
1741
+ installed: false,
1742
+ installedVersion: null,
1743
+ binaryPath: null,
1744
+ launchError: null,
1745
+ latestVersion: null,
1746
+ updateAvailable: false,
1747
+ authLoggedIn: false,
1748
+ authStatusChecking: false,
1749
+ authMethod: null,
1750
+ providers: [],
1751
+ };
1752
+ }
1753
+ },
1754
+ getProviderStatus: async (providerId: string): Promise<CliProviderStatus | null> => {
1755
+ try {
1756
+ return await this.get(`/api/cli/provider/${encodeURIComponent(providerId)}/status`);
1757
+ } catch {
1758
+ return null;
1759
+ }
1760
+ },
1761
+ verifyProviderModels: async (): Promise<null> => null,
1762
+ install: async (): Promise<void> => {
1763
+ console.warn('[HttpAPIClient] CLI installer not available in browser mode');
1764
+ },
1765
+ invalidateStatus: async (): Promise<void> => {
1766
+ try {
1767
+ await this.post('/api/cli/invalidate-status');
1768
+ } catch {
1769
+ /* ignore */
1770
+ }
1771
+ },
1772
+ onProgress: (): (() => void) => {
1773
+ return () => {};
1774
+ },
1775
+ };
1776
+
1777
+ // ---------------------------------------------------------------------------
1778
+ // Extensions (plugins, MCP registry, skills — HTTP API)
1779
+ // ---------------------------------------------------------------------------
1780
+
1781
+ plugins = {
1782
+ getAll: async (projectPath?: string, forceRefresh?: boolean) => {
1783
+ const params = new URLSearchParams();
1784
+ if (projectPath) params.set('projectPath', projectPath);
1785
+ if (forceRefresh) params.set('forceRefresh', 'true');
1786
+ const qs = params.toString();
1787
+ const result = await this.get<{ success: boolean; data?: EnrichedPlugin[]; error?: string }>(
1788
+ `/api/extensions/plugins${qs ? `?${qs}` : ''}`
1789
+ );
1790
+ if (!result.success) throw new Error(result.error ?? 'Failed to get plugins');
1791
+ return result.data ?? [];
1792
+ },
1793
+ getReadme: async (pluginId: string) => {
1794
+ const result = await this.get<{ success: boolean; data?: string | null; error?: string }>(
1795
+ `/api/extensions/plugins/${encodeURIComponent(pluginId)}/readme`
1796
+ );
1797
+ if (!result.success) throw new Error(result.error ?? 'Failed to get readme');
1798
+ return result.data ?? null;
1799
+ },
1800
+ install: async (request: PluginInstallRequest) => {
1801
+ const result = await this.post<{
1802
+ success: boolean;
1803
+ data?: OperationResult;
1804
+ error?: string;
1805
+ }>('/api/extensions/plugins/install', request);
1806
+ if (!result.success) return { state: 'error' as const, error: result.error };
1807
+ return result.data!;
1808
+ },
1809
+ uninstall: async (pluginId: string, scope?: string, projectPath?: string) => {
1810
+ const result = await this.post<{
1811
+ success: boolean;
1812
+ data?: OperationResult;
1813
+ error?: string;
1814
+ }>('/api/extensions/plugins/uninstall', { pluginId, scope, projectPath });
1815
+ if (!result.success) return { state: 'error' as const, error: result.error };
1816
+ return result.data!;
1817
+ },
1818
+ };
1819
+
1820
+ mcpRegistry = {
1821
+ search: async (query: string, limit?: number) => {
1822
+ const params = new URLSearchParams({ q: query });
1823
+ if (limit) params.set('limit', String(limit));
1824
+ const result = await this.get<{
1825
+ success: boolean;
1826
+ data?: McpSearchResult;
1827
+ error?: string;
1828
+ }>(`/api/extensions/mcp/search?${params}`);
1829
+ if (!result.success) {
1830
+ return { servers: [], total: 0, warnings: [result.error ?? 'Search failed'] };
1831
+ }
1832
+ return result.data!;
1833
+ },
1834
+ browse: async (cursor?: string, limit?: number) => {
1835
+ const params = new URLSearchParams();
1836
+ if (cursor) params.set('cursor', cursor);
1837
+ if (limit) params.set('limit', String(limit));
1838
+ const qs = params.toString();
1839
+ const result = await this.get<{
1840
+ success: boolean;
1841
+ data?: { servers: McpCatalogItem[]; nextCursor?: string };
1842
+ error?: string;
1843
+ }>(`/api/extensions/mcp/browse${qs ? `?${qs}` : ''}`);
1844
+ if (!result.success) return { servers: [] };
1845
+ return result.data!;
1846
+ },
1847
+ getById: async (registryId: string) => {
1848
+ const result = await this.get<{
1849
+ success: boolean;
1850
+ data?: McpCatalogItem | null;
1851
+ error?: string;
1852
+ }>(`/api/extensions/mcp/${encodeURIComponent(registryId)}`);
1853
+ if (!result.success) return null;
1854
+ return result.data ?? null;
1855
+ },
1856
+ getInstalled: async (projectPath?: string) => {
1857
+ const params = new URLSearchParams();
1858
+ if (projectPath) params.set('projectPath', projectPath);
1859
+ const qs = params.toString();
1860
+ const result = await this.get<{
1861
+ success: boolean;
1862
+ data?: InstalledMcpEntry[];
1863
+ error?: string;
1864
+ }>(`/api/extensions/mcp/installed${qs ? `?${qs}` : ''}`);
1865
+ if (!result.success) return [];
1866
+ return result.data ?? [];
1867
+ },
1868
+ diagnose: async (projectPath?: string) => {
1869
+ const params = new URLSearchParams();
1870
+ if (projectPath) params.set('projectPath', projectPath);
1871
+ const qs = params.toString();
1872
+ const result = await this.get<{
1873
+ success: boolean;
1874
+ data?: McpServerDiagnostic[];
1875
+ error?: string;
1876
+ }>(`/api/extensions/mcp/diagnose${qs ? `?${qs}` : ''}`);
1877
+ if (!result.success) return [];
1878
+ return result.data ?? [];
1879
+ },
1880
+ install: async (request: McpInstallRequest) => {
1881
+ const result = await this.post<{
1882
+ success: boolean;
1883
+ data?: OperationResult;
1884
+ error?: string;
1885
+ }>('/api/extensions/mcp/install', request);
1886
+ if (!result.success) return { state: 'error' as const, error: result.error };
1887
+ return result.data!;
1888
+ },
1889
+ installCustom: async (request: McpCustomInstallRequest) => {
1890
+ const result = await this.post<{
1891
+ success: boolean;
1892
+ data?: OperationResult;
1893
+ error?: string;
1894
+ }>('/api/extensions/mcp/install-custom', request);
1895
+ if (!result.success) return { state: 'error' as const, error: result.error };
1896
+ return result.data!;
1897
+ },
1898
+ uninstall: async (name: string, scope?: string, projectPath?: string) => {
1899
+ const result = await this.post<{
1900
+ success: boolean;
1901
+ data?: OperationResult;
1902
+ error?: string;
1903
+ }>('/api/extensions/mcp/uninstall', { name, scope, projectPath });
1904
+ if (!result.success) return { state: 'error' as const, error: result.error };
1905
+ return result.data!;
1906
+ },
1907
+ githubStars: async (repositoryUrls: string[]) => {
1908
+ const result = await this.post<{
1909
+ success: boolean;
1910
+ data?: Record<string, number>;
1911
+ error?: string;
1912
+ }>('/api/extensions/mcp/github-stars', { repositoryUrls });
1913
+ if (!result.success) return {};
1914
+ return result.data ?? {};
1915
+ },
1916
+ };
1917
+
1918
+ skills = {
1919
+ list: async (projectPath?: string) => {
1920
+ const params = new URLSearchParams();
1921
+ if (projectPath) params.set('projectPath', projectPath);
1922
+ const qs = params.toString();
1923
+ const result = await this.get<{
1924
+ success: boolean;
1925
+ data?: SkillCatalogItem[];
1926
+ error?: string;
1927
+ }>(`/api/extensions/skills${qs ? `?${qs}` : ''}`);
1928
+ if (!result.success) return [];
1929
+ return result.data ?? [];
1930
+ },
1931
+ getDetail: async (skillId: string, projectPath?: string) => {
1932
+ const params = new URLSearchParams();
1933
+ if (projectPath) params.set('projectPath', projectPath);
1934
+ const qs = params.toString();
1935
+ const result = await this.get<{
1936
+ success: boolean;
1937
+ data?: SkillDetail | null;
1938
+ error?: string;
1939
+ }>(`/api/extensions/skills/${encodeURIComponent(skillId)}${qs ? `?${qs}` : ''}`);
1940
+ if (!result.success) return null;
1941
+ return result.data ?? null;
1942
+ },
1943
+ previewUpsert: async (request: SkillUpsertRequest) => {
1944
+ const result = await this.post<{
1945
+ success: boolean;
1946
+ data?: SkillReviewPreview;
1947
+ error?: string;
1948
+ }>('/api/extensions/skills/preview-upsert', request);
1949
+ if (!result.success) throw new Error(result.error ?? 'Preview failed');
1950
+ return result.data!;
1951
+ },
1952
+ applyUpsert: async (request: SkillUpsertRequest) => {
1953
+ const result = await this.post<{
1954
+ success: boolean;
1955
+ data?: SkillDetail | null;
1956
+ error?: string;
1957
+ }>('/api/extensions/skills/apply-upsert', request);
1958
+ if (!result.success) throw new Error(result.error ?? 'Apply failed');
1959
+ return result.data ?? null;
1960
+ },
1961
+ previewImport: async (request: SkillImportRequest) => {
1962
+ const result = await this.post<{
1963
+ success: boolean;
1964
+ data?: SkillReviewPreview;
1965
+ error?: string;
1966
+ }>('/api/extensions/skills/preview-import', request);
1967
+ if (!result.success) throw new Error(result.error ?? 'Preview import failed');
1968
+ return result.data!;
1969
+ },
1970
+ applyImport: async (request: SkillImportRequest) => {
1971
+ const result = await this.post<{
1972
+ success: boolean;
1973
+ data?: SkillDetail | null;
1974
+ error?: string;
1975
+ }>('/api/extensions/skills/apply-import', request);
1976
+ if (!result.success) throw new Error(result.error ?? 'Apply import failed');
1977
+ return result.data ?? null;
1978
+ },
1979
+ deleteSkill: async (request: SkillDeleteRequest) => {
1980
+ const result = await this.post<{ success: boolean; error?: string }>(
1981
+ '/api/extensions/skills/delete',
1982
+ request
1983
+ );
1984
+ if (!result.success) throw new Error(result.error ?? 'Delete failed');
1985
+ },
1986
+ listSources: async () => {
1987
+ const result = await this.get<{
1988
+ success: boolean;
1989
+ data?: SkillSourcesSnapshot;
1990
+ error?: string;
1991
+ }>('/api/extensions/skills/sources');
1992
+ if (!result.success) return { sources: [] };
1993
+ return result.data ?? { sources: [] };
1994
+ },
1995
+ saveSources: async (sources: SkillSource[]) => {
1996
+ const result = await this.postLong<{
1997
+ success: boolean;
1998
+ data?: SkillSourcesSnapshot;
1999
+ error?: string;
2000
+ }>('/api/extensions/skills/sources/save', sources);
2001
+ if (!result.success) throw new Error(result.error ?? 'Save failed');
2002
+ return result.data ?? { sources: [] };
2003
+ },
2004
+ refreshSources: async () => {
2005
+ const result = await this.postLong<{
2006
+ success: boolean;
2007
+ data?: SkillSourcesSnapshot;
2008
+ error?: string;
2009
+ }>('/api/extensions/skills/sources/refresh');
2010
+ if (!result.success) throw new Error(result.error ?? 'Refresh failed');
2011
+ return result.data ?? { sources: [] };
2012
+ },
2013
+ startWatching: async (projectPath?: string) => {
2014
+ const params = new URLSearchParams();
2015
+ if (projectPath) params.set('projectPath', projectPath);
2016
+ const qs = params.toString();
2017
+ const result = await this.post<{ success: boolean; data?: string; error?: string }>(
2018
+ `/api/extensions/skills/watching/start${qs ? `?${qs}` : ''}`
2019
+ );
2020
+ if (!result.success) return '';
2021
+ return result.data ?? '';
2022
+ },
2023
+ stopWatching: async (watchId: string) => {
2024
+ const result = await this.post<{ success: boolean; error?: string }>(
2025
+ '/api/extensions/skills/watching/stop',
2026
+ { watchId }
2027
+ );
2028
+ if (!result.success) throw new Error(result.error ?? 'Stop watching failed');
2029
+ },
2030
+ onChanged: (callback: (event: SkillWatcherEvent) => void): (() => void) =>
2031
+ this.addEventListener('skills:changed', (data: unknown) =>
2032
+ callback(data as SkillWatcherEvent)
2033
+ ),
2034
+ };
2035
+
2036
+ // ---------------------------------------------------------------------------
2037
+ // Terminal (not available in browser mode)
2038
+ // ---------------------------------------------------------------------------
2039
+
2040
+ terminal: TerminalAPI = {
2041
+ spawn: async (): Promise<string> => {
2042
+ throw new Error('Terminal not available in browser mode');
2043
+ },
2044
+ write: () => {},
2045
+ resize: () => {},
2046
+ kill: () => {},
2047
+ onData: (): (() => void) => () => {},
2048
+ onExit: (): (() => void) => () => {},
2049
+ };
2050
+
2051
+ // ---------------------------------------------------------------------------
2052
+ // Project (browser mode — delegates to HTTP)
2053
+ // ---------------------------------------------------------------------------
2054
+
2055
+ project: ProjectAPI = {
2056
+ listFiles: async (projectPath: string) => {
2057
+ const params = new URLSearchParams({ root: projectPath });
2058
+ return this.get(`/api/editor/listFiles?${params}`);
2059
+ },
2060
+ };
2061
+
2062
+ // ---------------------------------------------------------------------------
2063
+ // Workspace (file system browsing)
2064
+ // ---------------------------------------------------------------------------
2065
+
2066
+ workspace = {
2067
+ list: async (dirPath: string): Promise<WorkspaceListResponse> =>
2068
+ this.post('/api/workspace/list', { dirPath }),
2069
+ };
2070
+
2071
+ // ---------------------------------------------------------------------------
2072
+ // Editor (browser mode — delegates to HTTP)
2073
+ // ---------------------------------------------------------------------------
2074
+
2075
+ private _editorRoot: string | null = null;
2076
+
2077
+ editor: EditorAPI = {
2078
+ open: async (projectPath: string) => {
2079
+ this._editorRoot = projectPath;
2080
+ },
2081
+ close: async () => {
2082
+ this._editorRoot = null;
2083
+ },
2084
+ readDir: async (dirPath: string, maxEntries?: number) => {
2085
+ const params = new URLSearchParams({ root: this._editorRoot!, dirPath });
2086
+ if (maxEntries) params.set('maxEntries', String(maxEntries));
2087
+ return this.get(`/api/editor/readDir?${params}`);
2088
+ },
2089
+ readFile: async (filePath: string) => {
2090
+ const params = new URLSearchParams({ root: this._editorRoot!, filePath });
2091
+ return this.get(`/api/editor/readFile?${params}`);
2092
+ },
2093
+ writeFile: async (filePath: string, content: string, baselineMtimeMs?: number) => {
2094
+ return this.post('/api/editor/writeFile', {
2095
+ root: this._editorRoot,
2096
+ filePath,
2097
+ content,
2098
+ baselineMtimeMs,
2099
+ });
2100
+ },
2101
+ createFile: async (parentDir: string, fileName: string) => {
2102
+ return this.post('/api/editor/createFile', {
2103
+ root: this._editorRoot,
2104
+ parentDir,
2105
+ fileName,
2106
+ });
2107
+ },
2108
+ createDir: async (parentDir: string, dirName: string) => {
2109
+ return this.post('/api/editor/createDir', {
2110
+ root: this._editorRoot,
2111
+ parentDir,
2112
+ dirName,
2113
+ });
2114
+ },
2115
+ deleteFile: async (filePath: string) => {
2116
+ return this.post('/api/editor/deleteFile', {
2117
+ root: this._editorRoot,
2118
+ filePath,
2119
+ });
2120
+ },
2121
+ moveFile: async (sourcePath: string, destDir: string) => {
2122
+ return this.post('/api/editor/moveFile', {
2123
+ root: this._editorRoot,
2124
+ sourcePath,
2125
+ destDir,
2126
+ });
2127
+ },
2128
+ renameFile: async (sourcePath: string, newName: string) => {
2129
+ return this.post('/api/editor/renameFile', {
2130
+ root: this._editorRoot,
2131
+ sourcePath,
2132
+ newName,
2133
+ });
2134
+ },
2135
+ searchInFiles: async (options) => {
2136
+ const params = new URLSearchParams({ root: this._editorRoot!, query: options.query });
2137
+ if (options.caseSensitive) params.set('caseSensitive', 'true');
2138
+ if (options.maxFiles) params.set('maxFiles', String(options.maxFiles));
2139
+ if (options.maxMatches) params.set('maxMatches', String(options.maxMatches));
2140
+ return this.get(`/api/editor/search?${params}`);
2141
+ },
2142
+ listFiles: async () => {
2143
+ const params = new URLSearchParams({ root: this._editorRoot! });
2144
+ return this.get(`/api/editor/listFiles?${params}`);
2145
+ },
2146
+ readBinaryPreview: async (filePath: string) => {
2147
+ const params = new URLSearchParams({ root: this._editorRoot!, filePath });
2148
+ return this.get(`/api/editor/readBinaryPreview?${params}`);
2149
+ },
2150
+ gitStatus: async () => {
2151
+ const params = new URLSearchParams({ root: this._editorRoot! });
2152
+ return this.get(`/api/editor/gitStatus?${params}`);
2153
+ },
2154
+ watchDir: async (): Promise<void> => {
2155
+ // File watching not supported in browser mode
2156
+ },
2157
+ setWatchedFiles: async (): Promise<void> => {
2158
+ // File watching not supported in browser mode
2159
+ },
2160
+ setWatchedDirs: async (): Promise<void> => {
2161
+ // File watching not supported in browser mode
2162
+ },
2163
+ onEditorChange: () => {
2164
+ return () => {};
2165
+ },
2166
+ };
2167
+
2168
+ schedules: ElectronAPI['schedules'] = {
2169
+ list: (): Promise<Schedule[]> => this.get<Schedule[]>('/api/schedules'),
2170
+ get: (id: string): Promise<Schedule | null> =>
2171
+ this.get<Schedule | null>(`/api/schedules/${encodeURIComponent(id)}`),
2172
+ create: (input: CreateScheduleInput): Promise<Schedule> =>
2173
+ this.post<Schedule>('/api/schedules', input),
2174
+ update: (id: string, patch: UpdateSchedulePatch): Promise<Schedule> =>
2175
+ this.patch<Schedule>(`/api/schedules/${encodeURIComponent(id)}`, patch),
2176
+ delete: (id: string): Promise<void> => this.del(`/api/schedules/${encodeURIComponent(id)}`),
2177
+ pause: (id: string): Promise<void> =>
2178
+ this.post(`/api/schedules/${encodeURIComponent(id)}/pause`),
2179
+ resume: (id: string): Promise<void> =>
2180
+ this.post(`/api/schedules/${encodeURIComponent(id)}/resume`),
2181
+ triggerNow: (id: string): Promise<ScheduleRun> =>
2182
+ this.post<ScheduleRun>(`/api/schedules/${encodeURIComponent(id)}/trigger`),
2183
+ getRuns: (
2184
+ scheduleId: string,
2185
+ opts?: { limit?: number; offset?: number }
2186
+ ): Promise<ScheduleRun[]> => {
2187
+ const params = new URLSearchParams();
2188
+ if (opts?.limit) params.set('limit', String(opts.limit));
2189
+ if (opts?.offset) params.set('offset', String(opts.offset));
2190
+ const qs = params.toString();
2191
+ const base = `/api/schedules/${encodeURIComponent(scheduleId)}/runs`;
2192
+ return this.get<ScheduleRun[]>(qs ? `${base}?${qs}` : base);
2193
+ },
2194
+ getRunLogs: (scheduleId: string, runId: string): Promise<{ stdout: string; stderr: string }> =>
2195
+ this.get<{ stdout: string; stderr: string }>(
2196
+ `/api/schedules/${encodeURIComponent(scheduleId)}/runs/${encodeURIComponent(runId)}/logs`
2197
+ ),
2198
+ onScheduleChange: (
2199
+ callback: (event: unknown, data: ScheduleChangeEvent) => void
2200
+ ): (() => void) =>
2201
+ this.addEventListener('schedule:change', (data: unknown) =>
2202
+ callback(null, data as ScheduleChangeEvent)
2203
+ ),
2204
+ };
2205
+
2206
+ getPathForFile = (_file: File): string => '';
2207
+ }