@dyyz1993/create-agent 2.1.0 → 2.1.2

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 (349) hide show
  1. package/package.json +5 -4
  2. package/src/__tests__/cli.test.ts +2 -0
  3. package/src/__tests__/copy.test.ts +110 -122
  4. package/src/__tests__/create-args.test.ts +113 -0
  5. package/src/__tests__/templates.test.ts +29 -3
  6. package/src/__tests__/update.test.ts +129 -0
  7. package/src/cli.ts +30 -32
  8. package/src/commands/create.ts +83 -24
  9. package/src/commands/update.ts +174 -0
  10. package/src/commands/workspace.ts +26 -6
  11. package/src/lib/copy.ts +233 -93
  12. package/src/lib/templates.ts +12 -0
  13. package/templates/agent/.husky/commit-msg +4 -0
  14. package/templates/agent/.husky/pre-commit +11 -0
  15. package/templates/agent/.husky/pre-push +6 -0
  16. package/templates/agent/.prettierignore +6 -0
  17. package/templates/agent/.prettierrc +9 -0
  18. package/templates/agent/CHANGELOG.md +15 -0
  19. package/templates/agent/commitlint.config.js +8 -0
  20. package/templates/agent/package.json +19 -4
  21. package/templates/agent/src/gateway/__tests__/ws-handler-token.test.ts +53 -39
  22. package/templates/agent/src/mainview/App.tsx +14 -12
  23. package/templates/agent/src/mainview/__tests__/hooks/use-rpc-init.test.ts +194 -190
  24. package/templates/agent/src/mainview/components/bash/BashPanel.tsx +210 -189
  25. package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +45 -20
  26. package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +41 -21
  27. package/templates/agent/src/mainview/components/debug/DebugPanel.tsx +181 -114
  28. package/templates/agent/src/mainview/components/debug/__tests__/DebugPanel.test.tsx +80 -80
  29. package/templates/agent/src/mainview/components/diff/__tests__/DiffViewerPanel.test.tsx +61 -58
  30. package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +43 -41
  31. package/templates/agent/src/mainview/components/file-preview/FilePreviewOverlay.tsx +69 -50
  32. package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +31 -18
  33. package/templates/agent/src/mainview/components/git/GitPanel.tsx +731 -490
  34. package/templates/agent/src/mainview/components/git/__tests__/GitPanel.test.tsx +157 -139
  35. package/templates/agent/src/mainview/components/layout/AppLayout.tsx +210 -161
  36. package/templates/agent/src/mainview/components/layout/__tests__/AppLayout.test.tsx +130 -122
  37. package/templates/agent/src/mainview/components/rules/RulesPanel.tsx +119 -109
  38. package/templates/agent/src/mainview/components/search/SearchPanel.tsx +123 -117
  39. package/templates/agent/src/mainview/components/search/__tests__/SearchPanel.test.tsx +64 -44
  40. package/templates/agent/src/mainview/components/sidebar/__tests__/PinButton.test.tsx +38 -38
  41. package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +40 -38
  42. package/templates/agent/src/mainview/components/todo/__tests__/TodoPanel.test.tsx +72 -72
  43. package/templates/agent/src/mainview/lib/i18n/locales/en.json +175 -155
  44. package/templates/agent/src/mainview/lib/i18n/locales/zh.json +173 -155
  45. package/templates/agent/src/mainview/stores/use-app-store.ts +96 -86
  46. package/templates/agent/src/mainview/stores/use-explorer-store.ts +292 -275
  47. package/templates/agent/src/mainview/types/index.ts +23 -22
  48. package/templates/agent/src/mainview/utils/file-icon.tsx +17 -21
  49. package/templates/agent/src/shared/handlers/chat.ts +53 -53
  50. package/templates/agent/src/shared/handlers/debug.ts +13 -3
  51. package/templates/browser-agent/.env.example +19 -0
  52. package/templates/browser-agent/.husky/commit-msg +4 -0
  53. package/templates/browser-agent/.husky/pre-commit +11 -0
  54. package/templates/browser-agent/.husky/pre-push +6 -0
  55. package/templates/browser-agent/.prettierignore +6 -0
  56. package/templates/browser-agent/.prettierrc +9 -0
  57. package/templates/browser-agent/AGENTS.md +396 -0
  58. package/templates/browser-agent/CHANGELOG.md +15 -0
  59. package/templates/browser-agent/LICENSE +21 -0
  60. package/templates/browser-agent/README.md +103 -0
  61. package/templates/browser-agent/commitlint.config.js +8 -0
  62. package/templates/browser-agent/electrobun.config.ts +27 -0
  63. package/templates/browser-agent/electron/main.js +46 -0
  64. package/templates/browser-agent/electron/preload.js +5 -0
  65. package/templates/browser-agent/electron-builder.json +40 -0
  66. package/templates/browser-agent/eslint.config.mjs +79 -0
  67. package/templates/browser-agent/llms.txt +24 -0
  68. package/templates/browser-agent/package.json +140 -0
  69. package/templates/browser-agent/postcss.config.js +6 -0
  70. package/templates/browser-agent/scripts/dev.ts +138 -0
  71. package/templates/browser-agent/src/__tests__/hybrid-mode.test.ts +126 -0
  72. package/templates/browser-agent/src/__tests__/server-config-security.test.ts +55 -0
  73. package/templates/browser-agent/src/__tests__/server-config.test.ts +59 -0
  74. package/templates/browser-agent/src/bun/index.ts +130 -0
  75. package/templates/browser-agent/src/bun/three.d.ts +1 -0
  76. package/templates/browser-agent/src/gateway/http-routes.ts +1 -0
  77. package/templates/browser-agent/src/gateway/ipc-transport.ts +68 -0
  78. package/templates/browser-agent/src/gateway/sse-transport.ts +221 -0
  79. package/templates/browser-agent/src/mainview/App.tsx +45 -0
  80. package/templates/browser-agent/src/mainview/__tests__/hooks/use-rpc-init.test.ts +202 -0
  81. package/templates/browser-agent/src/mainview/__tests__/hooks/use-sidebar-resize.test.ts +127 -0
  82. package/templates/browser-agent/src/mainview/__tests__/i18n/i18n.test.ts +49 -0
  83. package/templates/browser-agent/src/mainview/__tests__/setup.ts +40 -0
  84. package/templates/browser-agent/src/mainview/__tests__/stores/use-chat-store.test.ts +73 -0
  85. package/templates/browser-agent/src/mainview/__tests__/stores/use-sidebar-store.test.ts +61 -0
  86. package/templates/browser-agent/src/mainview/__tests__/theme-variables.test.ts +36 -0
  87. package/templates/browser-agent/src/mainview/components/assets/AssetsPanel.tsx +139 -0
  88. package/templates/browser-agent/src/mainview/components/chat/ChatPanel.tsx +219 -0
  89. package/templates/browser-agent/src/mainview/components/chat/CommandBar.tsx +184 -0
  90. package/templates/browser-agent/src/mainview/components/chat/MessageBubble.tsx +249 -0
  91. package/templates/browser-agent/src/mainview/components/chat/ToolPicker.tsx +115 -0
  92. package/templates/browser-agent/src/mainview/components/common/ErrorBoundary.tsx +1 -0
  93. package/templates/browser-agent/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  94. package/templates/browser-agent/src/mainview/components/common/ThemeToggle.tsx +45 -0
  95. package/templates/browser-agent/src/mainview/components/common/__tests__/ErrorBoundary.test.tsx +74 -0
  96. package/templates/browser-agent/src/mainview/components/common/__tests__/LanguageSwitcher.test.tsx +44 -0
  97. package/templates/browser-agent/src/mainview/components/common/__tests__/ThemeToggle.test.tsx +41 -0
  98. package/templates/browser-agent/src/mainview/components/dev/NetworkPanel.tsx +155 -0
  99. package/templates/browser-agent/src/mainview/components/layout/AppLayout.tsx +311 -0
  100. package/templates/browser-agent/src/mainview/components/onboarding/SetupWizard.tsx +310 -0
  101. package/templates/browser-agent/src/mainview/components/process/ProcessPanel.tsx +329 -0
  102. package/templates/browser-agent/src/mainview/components/sidebar/SessionSidebar.tsx +122 -0
  103. package/templates/browser-agent/src/mainview/components/sidebar/SkillSidebar.tsx +115 -0
  104. package/templates/browser-agent/src/mainview/components/topbar/TopBar.tsx +350 -0
  105. package/templates/browser-agent/src/mainview/global.d.ts +13 -0
  106. package/templates/browser-agent/src/mainview/hooks/__tests__/use-breakpoint.test.ts +151 -0
  107. package/templates/browser-agent/src/mainview/hooks/use-agent-chat.ts +157 -0
  108. package/templates/browser-agent/src/mainview/hooks/use-breakpoint.ts +68 -0
  109. package/templates/browser-agent/src/mainview/hooks/use-rpc-init.ts +71 -0
  110. package/templates/browser-agent/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  111. package/templates/browser-agent/src/mainview/index.css +71 -0
  112. package/templates/browser-agent/src/mainview/index.html +12 -0
  113. package/templates/browser-agent/src/mainview/lib/api-client.ts +397 -0
  114. package/templates/browser-agent/src/mainview/lib/i18n/index.ts +30 -0
  115. package/templates/browser-agent/src/mainview/lib/i18n/locales/en.json +130 -0
  116. package/templates/browser-agent/src/mainview/lib/i18n/locales/zh.json +128 -0
  117. package/templates/browser-agent/src/mainview/lib/network-bus.ts +92 -0
  118. package/templates/browser-agent/src/mainview/lib/rpc-cache.ts +94 -0
  119. package/templates/browser-agent/src/mainview/main.tsx +18 -0
  120. package/templates/browser-agent/src/mainview/stores/__tests__/use-connection-store.test.ts +26 -0
  121. package/templates/browser-agent/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  122. package/templates/browser-agent/src/mainview/stores/__tests__/use-log-store.test.ts +28 -0
  123. package/templates/browser-agent/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  124. package/templates/browser-agent/src/mainview/stores/message-batcher.ts +25 -0
  125. package/templates/browser-agent/src/mainview/stores/use-asset-store.ts +65 -0
  126. package/templates/browser-agent/src/mainview/stores/use-chat-store.ts +200 -0
  127. package/templates/browser-agent/src/mainview/stores/use-connection-store.ts +173 -0
  128. package/templates/browser-agent/src/mainview/stores/use-locale-store.ts +27 -0
  129. package/templates/browser-agent/src/mainview/stores/use-log-store.ts +16 -0
  130. package/templates/browser-agent/src/mainview/stores/use-network-panel-store.ts +18 -0
  131. package/templates/browser-agent/src/mainview/stores/use-record-store.ts +147 -0
  132. package/templates/browser-agent/src/mainview/stores/use-session-store.ts +151 -0
  133. package/templates/browser-agent/src/mainview/stores/use-sidebar-store.ts +161 -0
  134. package/templates/browser-agent/src/mainview/stores/use-theme-store.ts +46 -0
  135. package/templates/browser-agent/src/mainview/stores/use-view-store.ts +20 -0
  136. package/templates/browser-agent/src/mainview/types/index.ts +6 -0
  137. package/templates/browser-agent/src/server-config.ts +45 -0
  138. package/templates/browser-agent/src/server.ts +78 -0
  139. package/templates/browser-agent/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  140. package/templates/browser-agent/src/shared/handlers/__tests__/chat-handler.test.ts +77 -0
  141. package/templates/browser-agent/src/shared/handlers/__tests__/file-handler.test.ts +100 -0
  142. package/templates/browser-agent/src/shared/handlers/__tests__/system-handler.test.ts +55 -0
  143. package/templates/browser-agent/src/shared/handlers/__tests__/timer-handler.test.ts +77 -0
  144. package/templates/browser-agent/src/shared/handlers/browser.ts +596 -0
  145. package/templates/browser-agent/src/shared/handlers/chat.ts +213 -0
  146. package/templates/browser-agent/src/shared/handlers/file.ts +99 -0
  147. package/templates/browser-agent/src/shared/handlers/index.ts +7 -0
  148. package/templates/browser-agent/src/shared/handlers/session.ts +167 -0
  149. package/templates/browser-agent/src/shared/handlers/system.ts +27 -0
  150. package/templates/browser-agent/src/shared/handlers/timer.ts +34 -0
  151. package/templates/browser-agent/src/shared/http-routes.ts +437 -0
  152. package/templates/browser-agent/src/shared/lib/__tests__/logger.test.ts +92 -0
  153. package/templates/browser-agent/src/shared/lib/__tests__/path-security.test.ts +77 -0
  154. package/templates/browser-agent/src/shared/lib/__tests__/web-server.test.ts +203 -0
  155. package/templates/browser-agent/src/shared/lib/agent.ts +384 -0
  156. package/templates/browser-agent/src/shared/lib/cdp.ts +448 -0
  157. package/templates/browser-agent/src/shared/lib/generate.ts +111 -0
  158. package/templates/browser-agent/src/shared/lib/logger.ts +152 -0
  159. package/templates/browser-agent/src/shared/lib/mock-stream.ts +147 -0
  160. package/templates/browser-agent/src/shared/lib/path-security.ts +38 -0
  161. package/templates/browser-agent/src/shared/lib/port-registry.ts +103 -0
  162. package/templates/browser-agent/src/shared/lib/web-server.ts +127 -0
  163. package/templates/browser-agent/src/shared/lib/xhs-extract.ts +71 -0
  164. package/templates/browser-agent/src/shared/modules/browser.ts +86 -0
  165. package/templates/browser-agent/src/shared/modules/chat.ts +20 -0
  166. package/templates/browser-agent/src/shared/modules/file.ts +40 -0
  167. package/templates/browser-agent/src/shared/modules/session.ts +55 -0
  168. package/templates/browser-agent/src/shared/modules/system.ts +8 -0
  169. package/templates/browser-agent/src/shared/modules/timer.ts +11 -0
  170. package/templates/browser-agent/src/shared/register-all-handlers.ts +36 -0
  171. package/templates/browser-agent/src/shared/rpc-schema.ts +34 -0
  172. package/templates/browser-agent/tailwind.config.js +15 -0
  173. package/templates/browser-agent/tsconfig.ipc.json +14 -0
  174. package/templates/browser-agent/tsconfig.json +36 -0
  175. package/templates/browser-agent/vite.config.ts +3 -0
  176. package/templates/browser-agent/vitest.config.ts +3 -0
  177. package/templates/chat/.husky/commit-msg +4 -0
  178. package/templates/chat/.husky/pre-commit +11 -0
  179. package/templates/chat/.husky/pre-push +6 -0
  180. package/templates/chat/CHANGELOG.md +15 -0
  181. package/templates/chat/commitlint.config.js +8 -0
  182. package/templates/chat/package.json +19 -4
  183. package/templates/chat/src/mainview/__tests__/hooks/use-input-history.test.ts +125 -0
  184. package/templates/chat/src/mainview/__tests__/setup.ts +32 -29
  185. package/templates/chat/src/mainview/components/chat/ChatPanel.tsx +80 -55
  186. package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +40 -20
  187. package/templates/chat/src/mainview/lib/i18n/locales/en.json +175 -155
  188. package/templates/chat/src/mainview/lib/i18n/locales/zh.json +173 -155
  189. package/templates/chat/src/shared/handlers/chat.ts +17 -19
  190. package/templates/chat/src/shared/handlers/debug.ts +12 -2
  191. package/templates/cowork/.env.example +8 -0
  192. package/templates/cowork/.prettierignore +6 -0
  193. package/templates/cowork/.prettierrc +9 -0
  194. package/templates/cowork/AGENTS.md +236 -0
  195. package/templates/cowork/CHANGELOG.md +15 -0
  196. package/templates/cowork/LICENSE +21 -0
  197. package/templates/cowork/README.md +103 -0
  198. package/templates/cowork/commitlint.config.js +8 -0
  199. package/templates/cowork/docs/CODE-VIEW-PLAN.md +637 -0
  200. package/templates/cowork/docs/CODE-VIEW-V2.md +196 -0
  201. package/templates/cowork/docs/CODE-VIEW-V4.md +150 -0
  202. package/templates/cowork/electrobun.config.ts +27 -0
  203. package/templates/cowork/eslint.config.mjs +79 -0
  204. package/templates/cowork/llms.txt +24 -0
  205. package/templates/cowork/package.json +87 -0
  206. package/templates/cowork/postcss.config.js +6 -0
  207. package/templates/cowork/scripts/dev.ts +138 -0
  208. package/templates/cowork/src/__tests__/hybrid-mode.test.ts +126 -0
  209. package/templates/cowork/src/__tests__/server-config-security.test.ts +55 -0
  210. package/templates/cowork/src/__tests__/server-config.test.ts +59 -0
  211. package/templates/cowork/src/bun/index.ts +130 -0
  212. package/templates/cowork/src/bun/three.d.ts +1 -0
  213. package/templates/cowork/src/gateway/http-routes.ts +1 -0
  214. package/templates/cowork/src/gateway/ipc-transport.ts +68 -0
  215. package/templates/cowork/src/gateway/sse-transport.ts +231 -0
  216. package/templates/cowork/src/mainview/App.tsx +45 -0
  217. package/templates/cowork/src/mainview/__tests__/hooks/use-rpc-init.test.ts +202 -0
  218. package/templates/cowork/src/mainview/__tests__/hooks/use-sidebar-resize.test.ts +127 -0
  219. package/templates/cowork/src/mainview/__tests__/i18n/i18n.test.ts +49 -0
  220. package/templates/cowork/src/mainview/__tests__/setup.ts +40 -0
  221. package/templates/cowork/src/mainview/__tests__/stores/use-chat-store.test.ts +73 -0
  222. package/templates/cowork/src/mainview/__tests__/stores/use-sidebar-store.test.ts +61 -0
  223. package/templates/cowork/src/mainview/__tests__/theme-variables.test.ts +36 -0
  224. package/templates/cowork/src/mainview/components/chat/TaskChat.tsx +311 -0
  225. package/templates/cowork/src/mainview/components/chat/__tests__/localhost-links.test.ts +76 -0
  226. package/templates/cowork/src/mainview/components/common/ErrorBoundary.tsx +1 -0
  227. package/templates/cowork/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  228. package/templates/cowork/src/mainview/components/common/ThemeToggle.tsx +45 -0
  229. package/templates/cowork/src/mainview/components/common/__tests__/ErrorBoundary.test.tsx +74 -0
  230. package/templates/cowork/src/mainview/components/common/__tests__/LanguageSwitcher.test.tsx +44 -0
  231. package/templates/cowork/src/mainview/components/common/__tests__/ThemeToggle.test.tsx +41 -0
  232. package/templates/cowork/src/mainview/components/dev/NetworkPanel.tsx +155 -0
  233. package/templates/cowork/src/mainview/components/layout/AppLayout.tsx +216 -0
  234. package/templates/cowork/src/mainview/components/right/ArtifactsPanel.tsx +54 -0
  235. package/templates/cowork/src/mainview/components/right/ContextPanel.tsx +133 -0
  236. package/templates/cowork/src/mainview/components/right/ElementPicker.tsx +206 -0
  237. package/templates/cowork/src/mainview/components/right/PreviewBlock.tsx +155 -0
  238. package/templates/cowork/src/mainview/components/right/ProgressPanel.tsx +85 -0
  239. package/templates/cowork/src/mainview/components/sidebar/TaskSidebar.tsx +211 -0
  240. package/templates/cowork/src/mainview/components/topbar/TopBar.tsx +86 -0
  241. package/templates/cowork/src/mainview/global.d.ts +13 -0
  242. package/templates/cowork/src/mainview/hooks/__tests__/use-breakpoint.test.ts +123 -0
  243. package/templates/cowork/src/mainview/hooks/use-breakpoint.ts +53 -0
  244. package/templates/cowork/src/mainview/hooks/use-rpc-init.ts +26 -0
  245. package/templates/cowork/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  246. package/templates/cowork/src/mainview/index.css +89 -0
  247. package/templates/cowork/src/mainview/index.html +12 -0
  248. package/templates/cowork/src/mainview/lib/api-client.ts +404 -0
  249. package/templates/cowork/src/mainview/lib/i18n/index.ts +30 -0
  250. package/templates/cowork/src/mainview/lib/i18n/locales/en.json +130 -0
  251. package/templates/cowork/src/mainview/lib/i18n/locales/zh.json +128 -0
  252. package/templates/cowork/src/mainview/lib/network-bus.ts +92 -0
  253. package/templates/cowork/src/mainview/lib/rpc-cache.ts +94 -0
  254. package/templates/cowork/src/mainview/main.tsx +18 -0
  255. package/templates/cowork/src/mainview/stores/__tests__/use-connection-store.test.ts +26 -0
  256. package/templates/cowork/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  257. package/templates/cowork/src/mainview/stores/__tests__/use-log-store.test.ts +28 -0
  258. package/templates/cowork/src/mainview/stores/__tests__/use-preview-store.test.ts +193 -0
  259. package/templates/cowork/src/mainview/stores/__tests__/use-sidebar-store.test.ts +81 -0
  260. package/templates/cowork/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  261. package/templates/cowork/src/mainview/stores/message-batcher.ts +25 -0
  262. package/templates/cowork/src/mainview/stores/use-chat-store.ts +198 -0
  263. package/templates/cowork/src/mainview/stores/use-connection-store.ts +168 -0
  264. package/templates/cowork/src/mainview/stores/use-context-store.ts +68 -0
  265. package/templates/cowork/src/mainview/stores/use-locale-store.ts +27 -0
  266. package/templates/cowork/src/mainview/stores/use-log-store.ts +16 -0
  267. package/templates/cowork/src/mainview/stores/use-network-panel-store.ts +18 -0
  268. package/templates/cowork/src/mainview/stores/use-output-store.ts +47 -0
  269. package/templates/cowork/src/mainview/stores/use-preview-store.ts +97 -0
  270. package/templates/cowork/src/mainview/stores/use-sidebar-store.ts +272 -0
  271. package/templates/cowork/src/mainview/stores/use-task-store.ts +86 -0
  272. package/templates/cowork/src/mainview/stores/use-theme-store.ts +46 -0
  273. package/templates/cowork/src/mainview/stores/use-view-store.ts +29 -0
  274. package/templates/cowork/src/mainview/types/index.ts +6 -0
  275. package/templates/cowork/src/server-config.ts +44 -0
  276. package/templates/cowork/src/server.ts +78 -0
  277. package/templates/cowork/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  278. package/templates/cowork/src/shared/handlers/__tests__/chat-handler.test.ts +77 -0
  279. package/templates/cowork/src/shared/handlers/__tests__/file-handler.test.ts +100 -0
  280. package/templates/cowork/src/shared/handlers/__tests__/preview-handler.test.ts +172 -0
  281. package/templates/cowork/src/shared/handlers/__tests__/system-handler.test.ts +55 -0
  282. package/templates/cowork/src/shared/handlers/__tests__/timer-handler.test.ts +77 -0
  283. package/templates/cowork/src/shared/handlers/chat.ts +213 -0
  284. package/templates/cowork/src/shared/handlers/context.ts +72 -0
  285. package/templates/cowork/src/shared/handlers/file.ts +99 -0
  286. package/templates/cowork/src/shared/handlers/index.ts +9 -0
  287. package/templates/cowork/src/shared/handlers/output.ts +59 -0
  288. package/templates/cowork/src/shared/handlers/preview.ts +81 -0
  289. package/templates/cowork/src/shared/handlers/system.ts +27 -0
  290. package/templates/cowork/src/shared/handlers/task.ts +101 -0
  291. package/templates/cowork/src/shared/handlers/timer.ts +34 -0
  292. package/templates/cowork/src/shared/http-routes.ts +444 -0
  293. package/templates/cowork/src/shared/lib/__tests__/logger.test.ts +92 -0
  294. package/templates/cowork/src/shared/lib/__tests__/path-security.test.ts +77 -0
  295. package/templates/cowork/src/shared/lib/__tests__/web-server.test.ts +203 -0
  296. package/templates/cowork/src/shared/lib/logger.ts +152 -0
  297. package/templates/cowork/src/shared/lib/path-security.ts +38 -0
  298. package/templates/cowork/src/shared/lib/port-registry.ts +103 -0
  299. package/templates/cowork/src/shared/lib/web-server.ts +127 -0
  300. package/templates/cowork/src/shared/modules/chat.ts +20 -0
  301. package/templates/cowork/src/shared/modules/context.ts +32 -0
  302. package/templates/cowork/src/shared/modules/file.ts +40 -0
  303. package/templates/cowork/src/shared/modules/output.ts +20 -0
  304. package/templates/cowork/src/shared/modules/preview.ts +44 -0
  305. package/templates/cowork/src/shared/modules/system.ts +8 -0
  306. package/templates/cowork/src/shared/modules/task.ts +31 -0
  307. package/templates/cowork/src/shared/modules/timer.ts +11 -0
  308. package/templates/cowork/src/shared/register-all-handlers.ts +36 -0
  309. package/templates/cowork/src/shared/rpc-schema.ts +38 -0
  310. package/templates/cowork/tailwind.config.js +15 -0
  311. package/templates/cowork/test-upload.txt +0 -0
  312. package/templates/cowork/tsconfig.ipc.json +14 -0
  313. package/templates/cowork/tsconfig.json +36 -0
  314. package/templates/cowork/vite.config.ts +3 -0
  315. package/templates/cowork/vitest.config.ts +3 -0
  316. package/templates/general/.husky/commit-msg +4 -0
  317. package/templates/general/.husky/pre-commit +11 -0
  318. package/templates/general/.husky/pre-push +6 -0
  319. package/templates/general/CHANGELOG.md +15 -0
  320. package/templates/general/commitlint.config.js +8 -0
  321. package/templates/general/package.json +19 -4
  322. package/templates/general/src/mainview/__tests__/hooks/use-input-history.test.ts +125 -0
  323. package/templates/general/src/mainview/__tests__/setup.ts +32 -29
  324. package/templates/general/src/mainview/components/chat/ChatPanel.tsx +80 -55
  325. package/templates/general/src/mainview/components/common/ThemeToggle.tsx +40 -20
  326. package/templates/general/src/mainview/components/debug/DebugPanel.tsx +170 -156
  327. package/templates/general/src/mainview/components/debug/__tests__/DebugPanel.test.tsx +169 -0
  328. package/templates/general/src/mainview/components/explorer/ConfirmDialog.tsx +42 -42
  329. package/templates/general/src/mainview/components/explorer/ContextMenu.tsx +72 -67
  330. package/templates/general/src/mainview/components/feed/FeedPanel.tsx +7 -5
  331. package/templates/general/src/mainview/components/file-preview/FilePreviewOverlay.tsx +64 -45
  332. package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +24 -7
  333. package/templates/general/src/mainview/components/git/GitPanel.tsx +700 -473
  334. package/templates/general/src/mainview/components/layout/AppLayout.tsx +123 -114
  335. package/templates/general/src/mainview/components/search/SearchPanel.tsx +15 -19
  336. package/templates/general/src/mainview/lib/i18n/locales/en.json +175 -155
  337. package/templates/general/src/mainview/lib/i18n/locales/zh.json +173 -155
  338. package/templates/general/src/mainview/stores/__tests__/use-explorer-store.test.ts +259 -0
  339. package/templates/general/src/mainview/stores/__tests__/use-feed-store.test.ts +160 -0
  340. package/templates/general/src/mainview/stores/__tests__/use-git-store.test.ts +313 -0
  341. package/templates/general/src/mainview/stores/__tests__/use-notification-store.test.ts +115 -0
  342. package/templates/general/src/mainview/stores/__tests__/use-sidebar-store.test.ts +120 -0
  343. package/templates/general/src/mainview/stores/use-explorer-store.ts +277 -260
  344. package/templates/general/src/mainview/types/index.ts +21 -20
  345. package/templates/general/src/shared/handlers/chat.ts +1 -1
  346. package/templates/general/src/shared/handlers/debug.ts +12 -2
  347. package/templates/shared/components/ErrorBoundary.tsx +0 -1
  348. package/templates/shared/vite-base.config.ts +8 -7
  349. /package/templates/{shared → browser-agent}/test-upload.txt +0 -0
@@ -0,0 +1,68 @@
1
+ import { useEffect } from "react";
2
+ import { useSidebarStore, useAssetsPanelStore, type Breakpoint } from "../stores/use-sidebar-store";
3
+
4
+ function getBreakpoint(width: number): Breakpoint {
5
+ if (width < 768) return "mobile";
6
+ if (width < 1024) return "tablet";
7
+ if (width < 1280) return "desktop";
8
+ return "wide";
9
+ }
10
+
11
+ /**
12
+ * 单一 ResizeObserver,同步 breakpoint 到 store,并根据断点自动调整面板。
13
+ *
14
+ * 面板策略:
15
+ * - wide (≥1280px):侧栏 + 资源面板都固定展开
16
+ * - desktop (1024-1279px):侧栏固定,资源面板折叠为抽屉
17
+ * - tablet (768-1023px):侧栏抽屉,资源面板抽屉
18
+ * - mobile (<768px):全部抽屉,默认收起
19
+ */
20
+ export function useBreakpointSync() {
21
+ useEffect(() => {
22
+ const applyBreakpoint = (bp: Breakpoint): void => {
23
+ const sb = useSidebarStore.getState();
24
+ const ap = useAssetsPanelStore.getState();
25
+
26
+ if (bp === "wide") {
27
+ // 宽屏:都展开
28
+ if (!sb.isPinned) sb.setPinned(true);
29
+ if (!ap.assetsVisible) ap.setAssetsVisible(true);
30
+ if (ap.assetsDrawerOpen) ap.setAssetsDrawerOpen(false);
31
+ } else if (bp === "desktop") {
32
+ // 中屏:侧栏展开,资源面板折叠
33
+ if (!sb.isPinned) sb.setPinned(true);
34
+ if (ap.assetsVisible) ap.setAssetsVisible(false);
35
+ if (ap.assetsDrawerOpen) ap.setAssetsDrawerOpen(false);
36
+ } else {
37
+ // tablet / mobile:都收起为抽屉
38
+ if (sb.isPinned) sb.setPinned(false);
39
+ if (ap.assetsVisible) ap.setAssetsVisible(false);
40
+ }
41
+ };
42
+
43
+ let timer: ReturnType<typeof setTimeout>;
44
+ const obs = new ResizeObserver((entries) => {
45
+ clearTimeout(timer);
46
+ timer = setTimeout(() => {
47
+ for (const entry of entries) {
48
+ const bp = getBreakpoint(entry.contentRect.width);
49
+ const current = useSidebarStore.getState().breakpoint;
50
+ if (bp !== current) {
51
+ useSidebarStore.getState()._setBreakpoint(bp);
52
+ applyBreakpoint(bp);
53
+ }
54
+ }
55
+ }, 150);
56
+ });
57
+ obs.observe(document.documentElement);
58
+
59
+ // 初始也应用一次
60
+ const initBp = getBreakpoint(document.documentElement.clientWidth);
61
+ applyBreakpoint(initBp);
62
+
63
+ return () => {
64
+ clearTimeout(timer);
65
+ obs.disconnect();
66
+ };
67
+ }, []);
68
+ }
@@ -0,0 +1,71 @@
1
+ import { useEffect } from "react";
2
+ import { useConnectionStore } from "../stores/use-connection-store";
3
+ import { useLogStore } from "../stores/use-log-store";
4
+ import { useChatStore } from "../stores/use-chat-store";
5
+ import { useSessionStore } from "../stores/use-session-store";
6
+ import { apiClient } from "../lib/api-client";
7
+
8
+ export function useRpcInit() {
9
+ const ready = useConnectionStore((s) => s.ready);
10
+ const addLog = useLogStore((s) => s.addLog);
11
+ const initializeConnection = useConnectionStore((s) => s.initializeConnection);
12
+
13
+ useEffect(() => {
14
+ initializeConnection();
15
+ }, [initializeConnection]);
16
+
17
+ useEffect(() => {
18
+ if (!ready) return;
19
+
20
+ let subscriptionId: string | null = null;
21
+
22
+ const setup = async () => {
23
+ subscriptionId = await apiClient.subscribe("chat.message", (payload) => {
24
+ useChatStore.setState((s) => ({
25
+ messages: [
26
+ ...s.messages,
27
+ {
28
+ id: payload.id,
29
+ role: (payload.role === "assistant" ? "agent" : "user") as "user",
30
+ text: payload.content,
31
+ at: payload.timestamp,
32
+ } as any,
33
+ ],
34
+ }));
35
+ }, {});
36
+ addLog("Subscribed to chat.message");
37
+
38
+ try {
39
+ const history = await apiClient.call("chat.list", { limit: 100 });
40
+ if (history.messages.length > 0) {
41
+ useChatStore.getState().setMessages(
42
+ history.messages.map((m: { id: string; role: "user" | "assistant"; content: string; timestamp: number }) => ({
43
+ id: m.id,
44
+ role: m.role === "assistant" ? "agent" : "user",
45
+ text: m.content,
46
+ at: m.timestamp,
47
+ }))
48
+ );
49
+ addLog(`Loaded ${history.messages.length} history messages`);
50
+ }
51
+ } catch (err) {
52
+ addLog(`Failed to load history: ${err instanceof Error ? err.message : String(err)}`);
53
+ }
54
+
55
+ // 确保有一个默认空会话,用户可以直接输入
56
+ try {
57
+ const sid = await useSessionStore.getState().ensureDefaultSession();
58
+ addLog(`Active session: ${sid}`);
59
+ } catch (err) {
60
+ addLog(`Failed to ensure default session: ${err instanceof Error ? err.message : String(err)}`);
61
+ }
62
+ };
63
+ setup();
64
+
65
+ return () => {
66
+ if (subscriptionId) {
67
+ apiClient.unsubscribe(subscriptionId);
68
+ }
69
+ };
70
+ }, [ready, addLog]);
71
+ }
@@ -0,0 +1,40 @@
1
+ import { useCallback, useRef } from "react";
2
+ import { useSidebarStore } from "../stores/use-sidebar-store";
3
+
4
+ export function useSidebarResize() {
5
+ const sidebarWidth = useSidebarStore((s) => s.sidebarWidth);
6
+ const setSidebarWidth = useSidebarStore((s) => s.setSidebarWidth);
7
+
8
+ const resizingRef = useRef(false);
9
+ const startXRef = useRef(0);
10
+ const startWidthRef = useRef(0);
11
+
12
+ const handleResizeStart = useCallback(
13
+ (e: React.MouseEvent) => {
14
+ e.preventDefault();
15
+ resizingRef.current = true;
16
+ startXRef.current = e.clientX;
17
+ startWidthRef.current = sidebarWidth;
18
+ document.body.style.cursor = "col-resize";
19
+ document.body.style.userSelect = "none";
20
+
21
+ const onMove = (ev: MouseEvent) => {
22
+ if (!resizingRef.current) return;
23
+ const delta = ev.clientX - startXRef.current;
24
+ setSidebarWidth(startWidthRef.current + delta);
25
+ };
26
+ const onUp = () => {
27
+ resizingRef.current = false;
28
+ document.body.style.cursor = "";
29
+ document.body.style.userSelect = "";
30
+ window.removeEventListener("mousemove", onMove);
31
+ window.removeEventListener("mouseup", onUp);
32
+ };
33
+ window.addEventListener("mousemove", onMove);
34
+ window.addEventListener("mouseup", onUp);
35
+ },
36
+ [sidebarWidth, setSidebarWidth]
37
+ );
38
+
39
+ return { sidebarWidth, handleResizeStart };
40
+ }
@@ -0,0 +1,71 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+ --color-bg-primary: #ffffff;
7
+ --color-bg-secondary: #f3f4f6;
8
+ --color-bg-tertiary: #e5e7eb;
9
+ --color-bg-input: #f9fafb;
10
+ --color-bg-hover: #e5e7eb;
11
+ --color-bg-active: #d1d5db;
12
+ --color-bg-sidebar: #f3f4f6;
13
+ --color-bg-overlay: rgba(0, 0, 0, 0.5);
14
+
15
+ --color-text-primary: #111827;
16
+ --color-text-secondary: #4b5563;
17
+ --color-text-tertiary: #9ca3af;
18
+ --color-text-placeholder: #6b7280;
19
+ --color-text-accent: #4f46e5;
20
+ --color-text-success: #16a34a;
21
+ --color-text-error: #dc2626;
22
+ --color-text-info: #0891b2;
23
+
24
+ --color-border-primary: #d1d5db;
25
+ --color-border-secondary: #e5e7eb;
26
+
27
+ --color-accent: #4f46e5;
28
+ --color-accent-hover: #4338ca;
29
+
30
+ --color-badge-bg: #e0e7ff;
31
+ --color-badge-text: #3730a3;
32
+ }
33
+
34
+ .dark {
35
+ --color-bg-primary: #111827;
36
+ --color-bg-secondary: #1f2937;
37
+ --color-bg-tertiary: #374151;
38
+ --color-bg-input: #374151;
39
+ --color-bg-hover: #4b5563;
40
+ --color-bg-active: #6b7280;
41
+ --color-bg-sidebar: #1f2937;
42
+ --color-bg-overlay: rgba(0, 0, 0, 0.7);
43
+
44
+ --color-text-primary: #ffffff;
45
+ --color-text-secondary: #d1d5db;
46
+ --color-text-tertiary: #6b7280;
47
+ --color-text-placeholder: #9ca3af;
48
+ --color-text-accent: #818cf8;
49
+ --color-text-success: #4ade80;
50
+ --color-text-error: #f87171;
51
+ --color-text-info: #22d3ee;
52
+
53
+ --color-border-primary: #374151;
54
+ --color-border-secondary: #4b5563;
55
+
56
+ --color-accent: #6366f1;
57
+ --color-accent-hover: #4f46e5;
58
+
59
+ --color-badge-bg: #312e81;
60
+ --color-badge-text: #a5b4fc;
61
+ }
62
+
63
+ /* 侧边栏宽度过渡动画(三态折叠切换时平滑) */
64
+ .sidebar-container {
65
+ transition: width 200ms ease;
66
+ }
67
+
68
+ /* 拖拽 resize 时禁用动画,保证跟手 */
69
+ .sidebar-container.dragging {
70
+ transition: none;
71
+ }
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Browser Agent</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="./main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,397 @@
1
+ import { createTypedClient, IPCTransport } from "@dyyz1993/rpc-core";
2
+ import type {
3
+ TypedClient,
4
+ MethodParams,
5
+ MethodResult,
6
+ EventPayload,
7
+ EventMetadata,
8
+ Transport,
9
+ } from "@dyyz1993/rpc-core";
10
+ import type { RPCMethods, RPCEvents } from "../../shared/rpc-schema";
11
+ import { rpcCache, CACHEABLE_METHODS } from "./rpc-cache";
12
+ import { networkBus } from "./network-bus";
13
+
14
+ /**
15
+ * Token 来源优先级:
16
+ * 1. Vite 环境变量 VITE_AUTH_TOKEN(从 .env 注入)
17
+ * 2. URL query ?token=xxx(部署时注入)
18
+ * 3. localStorage "rpc-auth-token"
19
+ * 4. 默认值(开发备用)
20
+ */
21
+ function resolveAuthToken(): string {
22
+ if (typeof window !== "undefined") {
23
+ // Vite 注入的环境变量(来自 .env 的 VITE_AUTH_TOKEN)
24
+ const viteToken =
25
+ typeof import.meta !== "undefined" && (import.meta as any).env?.VITE_AUTH_TOKEN;
26
+ if (viteToken) return viteToken;
27
+
28
+ const fromQuery = new URLSearchParams(window.location.search).get("token");
29
+ if (fromQuery) return fromQuery;
30
+ const fromStorage = localStorage.getItem("rpc-auth-token");
31
+ if (fromStorage) return fromStorage;
32
+ }
33
+ return "pi-agent-template-token";
34
+ }
35
+
36
+ const AUTH_TOKEN = resolveAuthToken();
37
+
38
+ /**
39
+ * Browser SSE Transport — bridges EventSource (server→client) and
40
+ * fetch POST (client→server) into the rpc-core Transport interface.
41
+ *
42
+ * - send(): POST /api/rpc with the message JSON; response arrives via SSE.
43
+ * - onMessage: EventSource "data:" frames parsed and forwarded to handlers.
44
+ *
45
+ * The SSE stream delivers the clientId in a "ready" event; subsequent POSTs
46
+ * carry ?clientId= so the backend routes into the correct per-client RPCServer.
47
+ */
48
+ class BrowserSseTransport implements Transport {
49
+ private messageHandlers = new Set<(msg: unknown) => void>();
50
+ private errorHandlers = new Set<(err: Error) => void>();
51
+ private disconnectHandlers = new Set<() => void>();
52
+ private eventSource: EventSource | null = null;
53
+ clientId: string | null = null;
54
+ private baseUrl = "";
55
+ private connected = false;
56
+ private closed = false;
57
+
58
+ constructor(baseUrl: string, token: string) {
59
+ this.baseUrl = baseUrl;
60
+ this.connect(token);
61
+ }
62
+
63
+ private connect(token: string): void {
64
+ const url = `${this.baseUrl}/api/events?token=${encodeURIComponent(token)}`;
65
+ const es = new EventSource(url);
66
+ this.eventSource = es;
67
+
68
+ // "ready" event carries the clientId — fires on every (re)connect.
69
+ // Must update clientId each time because reconnect assigns a new one.
70
+ es.addEventListener("ready", (ev: MessageEvent) => {
71
+ try {
72
+ const data = JSON.parse(ev.data);
73
+ this.clientId = data.clientId;
74
+ this.connected = true;
75
+ } catch {
76
+ /* ignore */
77
+ }
78
+ });
79
+
80
+ // Default message channel: "data:" frames carry RPC responses/events
81
+ es.onmessage = (ev: MessageEvent) => {
82
+ try {
83
+ const msg = JSON.parse(ev.data);
84
+ // Debug: log SSE messages to console for troubleshooting
85
+ if (msg.type === "event") {
86
+ console.debug(`[SSE] event: ${msg.eventType}`, msg.payload);
87
+ } else if (msg.type === "response") {
88
+ console.debug(`[SSE] response: ${msg.id}`, msg.result || msg.error);
89
+ }
90
+ for (const handler of [...this.messageHandlers]) {
91
+ handler(msg);
92
+ }
93
+ } catch {
94
+ /* ignore parse errors */
95
+ }
96
+ };
97
+
98
+ es.onopen = () => {
99
+ this.connected = true;
100
+ };
101
+
102
+ es.onerror = () => {
103
+ if (this.closed) return;
104
+ this.connected = false;
105
+ for (const handler of [...this.errorHandlers]) {
106
+ handler(new Error("SSE connection error"));
107
+ }
108
+ for (const handler of [...this.disconnectHandlers]) {
109
+ handler();
110
+ }
111
+ // EventSource auto-reconnects natively; clientId will refresh on "ready"
112
+ };
113
+ }
114
+
115
+ async send(message: unknown): Promise<void> {
116
+ if (this.closed) throw new Error("SSE transport closed");
117
+
118
+ // Wait for clientId (with timeout to avoid hanging forever)
119
+ if (!this.clientId) {
120
+ await this.waitForClientId();
121
+ }
122
+
123
+ const url = `${this.baseUrl}/api/rpc?token=${encodeURIComponent(AUTH_TOKEN)}&clientId=${encodeURIComponent(this.clientId!)}`;
124
+ const res = await fetch(url, {
125
+ method: "POST",
126
+ headers: { "Content-Type": "application/json" },
127
+ body: JSON.stringify(message),
128
+ });
129
+
130
+ // 404 = clientId stale (SSE reconnected with a new ID). Retry once.
131
+ if (res.status === 404) {
132
+ // Force wait for fresh clientId
133
+ this.clientId = null;
134
+ this.connected = false;
135
+ await this.waitForClientId();
136
+ // Retry the POST with the fresh clientId
137
+ const retryUrl = `${this.baseUrl}/api/rpc?token=${encodeURIComponent(AUTH_TOKEN)}&clientId=${encodeURIComponent(this.clientId!)}`;
138
+ const retryRes = await fetch(retryUrl, {
139
+ method: "POST",
140
+ headers: { "Content-Type": "application/json" },
141
+ body: JSON.stringify(message),
142
+ });
143
+ if (!retryRes.ok) {
144
+ throw new Error(`RPC POST failed after retry: ${retryRes.status}`);
145
+ }
146
+ return;
147
+ }
148
+
149
+ if (!res.ok) {
150
+ throw new Error(`RPC POST failed: ${res.status}`);
151
+ }
152
+ }
153
+
154
+ private waitForClientId(timeoutMs = 10000): Promise<void> {
155
+ if (this.clientId) return Promise.resolve();
156
+ return new Promise((resolve, reject) => {
157
+ const start = Date.now();
158
+ const check = setInterval(() => {
159
+ if (this.clientId) {
160
+ clearInterval(check);
161
+ resolve();
162
+ } else if (Date.now() - start > timeoutMs) {
163
+ clearInterval(check);
164
+ reject(new Error("SSE 连接超时:未收到 clientId"));
165
+ }
166
+ }, 50);
167
+ });
168
+ }
169
+
170
+ /** 等待 SSE ready 事件(clientId + connected),供 initialize() 使用 */
171
+ async waitForReady(timeoutMs = 10000): Promise<void> {
172
+ await this.waitForClientId(timeoutMs);
173
+ }
174
+
175
+ onMessage(handler: (msg: unknown) => void): () => void {
176
+ this.messageHandlers.add(handler);
177
+ return () => {
178
+ this.messageHandlers.delete(handler);
179
+ };
180
+ }
181
+
182
+ onError(handler: (err: Error) => void): () => void {
183
+ this.errorHandlers.add(handler);
184
+ return () => {
185
+ this.errorHandlers.delete(handler);
186
+ };
187
+ }
188
+
189
+ onDisconnect(handler: () => void): () => void {
190
+ this.disconnectHandlers.add(handler);
191
+ return () => {
192
+ this.disconnectHandlers.delete(handler);
193
+ };
194
+ }
195
+
196
+ isConnected(): boolean {
197
+ return this.connected;
198
+ }
199
+
200
+ close(): void {
201
+ this.closed = true;
202
+ this.connected = false;
203
+ if (this.eventSource) {
204
+ this.eventSource.close();
205
+ this.eventSource = null;
206
+ }
207
+ this.messageHandlers.clear();
208
+ this.errorHandlers.clear();
209
+ this.disconnectHandlers.clear();
210
+ }
211
+ }
212
+
213
+ class APIClientImpl {
214
+ private client: TypedClient<RPCMethods, RPCEvents> | null = null;
215
+ private initPromise: Promise<void> | null = null;
216
+ private _transport: "ipc" | "sse" = "sse";
217
+ private _baseUrl: string | null = null;
218
+ private sseTransport: BrowserSseTransport | null = null;
219
+
220
+ /**
221
+ * 桌面端同步初始化:通过 executeJavascript 接收 + __electrobunBunBridge 发送
222
+ */
223
+ initSyncForDesktop(): void {
224
+ if (this.client) return;
225
+
226
+ const ipcTransport = new IPCTransport();
227
+ this._transport = "ipc";
228
+ this._baseUrl = null;
229
+ this.client = createTypedClient<RPCMethods, RPCEvents>(ipcTransport);
230
+ this.setupElectrobunBridge(ipcTransport);
231
+ }
232
+
233
+ /**
234
+ * 异步初始化:Web 端使用(连接 SSE + HTTP)
235
+ */
236
+ async initialize(): Promise<void> {
237
+ // 已连接且 transport 正常 → 直接返回
238
+ if (this.client && (this._transport === "ipc" || this.sseTransport?.isConnected())) {
239
+ return;
240
+ }
241
+
242
+ // client 存在但 SSE 断开了 → 清理后重连
243
+ if (this.client && this.sseTransport && !this.sseTransport.isConnected()) {
244
+ this.sseTransport.close();
245
+ this.sseTransport = null;
246
+ this.client = null;
247
+ this.initPromise = null;
248
+ }
249
+
250
+ if (this.initPromise) return this.initPromise;
251
+
252
+ this.initPromise = (async () => {
253
+ const env = this.detectEnvironment();
254
+
255
+ if (env === "electrobun") {
256
+ // 不应走到这里,桌面端应通过 initSyncForDesktop() 初始化
257
+ this.initSyncForDesktop();
258
+ } else {
259
+ try {
260
+ this._transport = "sse";
261
+ const baseUrl = this.resolveBaseUrl();
262
+ this.sseTransport = new BrowserSseTransport(baseUrl, AUTH_TOKEN);
263
+ this.client = createTypedClient<RPCMethods, RPCEvents>(this.sseTransport);
264
+ this._baseUrl = baseUrl;
265
+
266
+ // 等待 SSE ready(clientId 到达)才认为初始化完成
267
+ await this.sseTransport.waitForReady();
268
+ } catch (err) {
269
+ // 初始化失败 → 清理缓存,允许重试
270
+ this.sseTransport?.close();
271
+ this.sseTransport = null;
272
+ this.client = null;
273
+ this.initPromise = null;
274
+ throw err;
275
+ }
276
+ }
277
+ })();
278
+
279
+ return this.initPromise;
280
+ }
281
+
282
+ private detectEnvironment(): "electrobun" | "browser" {
283
+ if (typeof window === "undefined") return "browser";
284
+ if ((window as any).__electrobunBunBridge) return "electrobun";
285
+ return "browser";
286
+ }
287
+
288
+ private resolveBaseUrl(): string {
289
+ if (typeof window === "undefined") return "http://localhost:5200";
290
+ // In dev, Vite proxies /api → backend, so relative path works.
291
+ // For standalone deployment, use window.location.origin.
292
+ return window.location.origin;
293
+ }
294
+
295
+ /**
296
+ * 桌面端 IPC 桥接:
297
+ * - Bun → Browser: 通过 executeJavascript 调用 window.__piAgentIPC()
298
+ * - Browser → Bun: 通过 __electrobunBunBridge.postMessage 发送 Electrobun 消息格式
299
+ */
300
+ private setupElectrobunBridge(ipcTransport: IPCTransport): void {
301
+ if (typeof window === "undefined") return;
302
+
303
+ const win = window as any;
304
+
305
+ // 1. 注册接收函数:Bun 通过 executeJavascript 调用此函数发送消息到 Browser
306
+ win.__piAgentIPC = (msg: unknown) => {
307
+ ipcTransport.simulateMessage(msg);
308
+ };
309
+
310
+ // 2. 覆写 send:将 RPC-core 消息包装成 Electrobun 消息格式,通过原生桥接发送
311
+ const bridge = win.__electrobunBunBridge;
312
+
313
+ if (bridge) {
314
+ ipcTransport.send = async (message: unknown) => {
315
+ // 包装成 Electrobun message packet,bun 端 defineRPC 注册了 "rpc-message" handler
316
+ const electrobunPacket = {
317
+ type: "message",
318
+ id: "rpc-message",
319
+ payload: JSON.stringify(message),
320
+ };
321
+ bridge.postMessage(JSON.stringify(electrobunPacket));
322
+ };
323
+ }
324
+ }
325
+
326
+ getTransport(): "ipc" | "sse" {
327
+ return this._transport;
328
+ }
329
+
330
+ /** Web 端 HTTP 基础 URL(如 http://localhost:5200),桌面端返回 null */
331
+ getBaseUrl(): string | null {
332
+ return this._baseUrl;
333
+ }
334
+
335
+ /** 获取当前 auth token(用于需要直接调 HTTP 的场景) */
336
+ getAuthToken(): string {
337
+ return AUTH_TOKEN;
338
+ }
339
+
340
+ async call<K extends keyof RPCMethods>(
341
+ method: K,
342
+ params: MethodParams<RPCMethods, K>
343
+ ): Promise<MethodResult<RPCMethods, K>> {
344
+ const methodStr = method as string;
345
+ const ttl = CACHEABLE_METHODS[methodStr];
346
+ if (ttl !== undefined) {
347
+ const cached = rpcCache.get<MethodResult<RPCMethods, K>>(methodStr, params, ttl);
348
+ if (cached !== null) return cached;
349
+ }
350
+
351
+ await this.initialize();
352
+ const t0 = performance.now();
353
+ networkBus.emitRequest(methodStr, params);
354
+ const result = await this.client!.call(method, params);
355
+ const elapsed = Math.round(performance.now() - t0);
356
+ networkBus.emitResponse(methodStr, elapsed);
357
+
358
+ if (ttl !== undefined) {
359
+ rpcCache.set(methodStr, params, result);
360
+ }
361
+
362
+ return result;
363
+ }
364
+
365
+ async subscribe<K extends keyof RPCEvents>(
366
+ eventType: K,
367
+ handler: (payload: EventPayload<RPCEvents[K]>, metadata: EventMetadata<RPCEvents[K]>) => void,
368
+ filter?: Record<string, unknown>
369
+ ): Promise<string> {
370
+ await this.initialize();
371
+
372
+ // Wrap handler to capture SSE events for the network panel
373
+ const wrapped = (payload: EventPayload<RPCEvents[K]>, metadata: EventMetadata<RPCEvents[K]>) => {
374
+ networkBus.emitEvent(eventType as string, metadata as unknown as Record<string, unknown> | undefined);
375
+ handler(payload, metadata);
376
+ };
377
+
378
+ const subId = this.client!.subscribe(eventType, wrapped, filter);
379
+ return subId;
380
+ }
381
+
382
+ unsubscribe(subscriptionId: string): void {
383
+ this.client?.unsubscribe(subscriptionId);
384
+ }
385
+
386
+ isConnected(): boolean {
387
+ return this.client !== null;
388
+ }
389
+
390
+ close(): void {
391
+ this.client?.close();
392
+ }
393
+ }
394
+
395
+ export const apiClient = new APIClientImpl();
396
+ export type { APIClientImpl, RPCMethods, RPCEvents };
397
+ export type { Transport };
@@ -0,0 +1,30 @@
1
+ import i18n from "i18next";
2
+ import { initReactI18next } from "react-i18next";
3
+ import LanguageDetector from "i18next-browser-languagedetector";
4
+ import en from "./locales/en.json";
5
+ import zh from "./locales/zh.json";
6
+
7
+ export const supportedLocales = ["en", "zh"] as const;
8
+ export type Locale = (typeof supportedLocales)[number];
9
+
10
+ i18n
11
+ .use(LanguageDetector)
12
+ .use(initReactI18next)
13
+ .init({
14
+ resources: {
15
+ en: { translation: en },
16
+ zh: { translation: zh },
17
+ },
18
+ fallbackLng: "en",
19
+ supportedLngs: [...supportedLocales],
20
+ interpolation: {
21
+ escapeValue: false,
22
+ },
23
+ detection: {
24
+ order: ["localStorage", "navigator"],
25
+ lookupLocalStorage: "locale",
26
+ caches: ["localStorage"],
27
+ },
28
+ });
29
+
30
+ export default i18n;