@dyyz1993/create-agent 2.1.1 → 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 (346) 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 +127 -91
  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/CHANGELOG.md +15 -0
  17. package/templates/agent/package.json +3 -3
  18. package/templates/agent/src/gateway/__tests__/ws-handler-token.test.ts +53 -39
  19. package/templates/agent/src/mainview/App.tsx +14 -12
  20. package/templates/agent/src/mainview/__tests__/hooks/use-rpc-init.test.ts +194 -190
  21. package/templates/agent/src/mainview/components/bash/BashPanel.tsx +210 -189
  22. package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +45 -20
  23. package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +41 -21
  24. package/templates/agent/src/mainview/components/debug/DebugPanel.tsx +181 -114
  25. package/templates/agent/src/mainview/components/debug/__tests__/DebugPanel.test.tsx +80 -80
  26. package/templates/agent/src/mainview/components/diff/__tests__/DiffViewerPanel.test.tsx +61 -58
  27. package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +43 -41
  28. package/templates/agent/src/mainview/components/file-preview/FilePreviewOverlay.tsx +69 -50
  29. package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +31 -18
  30. package/templates/agent/src/mainview/components/git/GitPanel.tsx +731 -490
  31. package/templates/agent/src/mainview/components/git/__tests__/GitPanel.test.tsx +157 -139
  32. package/templates/agent/src/mainview/components/layout/AppLayout.tsx +210 -161
  33. package/templates/agent/src/mainview/components/layout/__tests__/AppLayout.test.tsx +130 -122
  34. package/templates/agent/src/mainview/components/rules/RulesPanel.tsx +119 -109
  35. package/templates/agent/src/mainview/components/search/SearchPanel.tsx +123 -117
  36. package/templates/agent/src/mainview/components/search/__tests__/SearchPanel.test.tsx +64 -44
  37. package/templates/agent/src/mainview/components/sidebar/__tests__/PinButton.test.tsx +38 -38
  38. package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +40 -38
  39. package/templates/agent/src/mainview/components/todo/__tests__/TodoPanel.test.tsx +72 -72
  40. package/templates/agent/src/mainview/lib/i18n/locales/en.json +175 -155
  41. package/templates/agent/src/mainview/lib/i18n/locales/zh.json +173 -155
  42. package/templates/agent/src/mainview/stores/use-app-store.ts +96 -86
  43. package/templates/agent/src/mainview/stores/use-explorer-store.ts +292 -275
  44. package/templates/agent/src/mainview/types/index.ts +23 -22
  45. package/templates/agent/src/mainview/utils/file-icon.tsx +17 -21
  46. package/templates/agent/src/shared/handlers/chat.ts +53 -53
  47. package/templates/agent/src/shared/handlers/debug.ts +13 -3
  48. package/templates/browser-agent/.env.example +19 -0
  49. package/templates/browser-agent/.husky/commit-msg +4 -0
  50. package/templates/browser-agent/.husky/pre-commit +11 -0
  51. package/templates/browser-agent/.husky/pre-push +6 -0
  52. package/templates/browser-agent/.prettierignore +6 -0
  53. package/templates/browser-agent/.prettierrc +9 -0
  54. package/templates/browser-agent/AGENTS.md +396 -0
  55. package/templates/browser-agent/CHANGELOG.md +15 -0
  56. package/templates/browser-agent/LICENSE +21 -0
  57. package/templates/browser-agent/README.md +103 -0
  58. package/templates/browser-agent/commitlint.config.js +8 -0
  59. package/templates/browser-agent/electrobun.config.ts +27 -0
  60. package/templates/browser-agent/electron/main.js +46 -0
  61. package/templates/browser-agent/electron/preload.js +5 -0
  62. package/templates/browser-agent/electron-builder.json +40 -0
  63. package/templates/browser-agent/eslint.config.mjs +79 -0
  64. package/templates/browser-agent/llms.txt +24 -0
  65. package/templates/browser-agent/package.json +140 -0
  66. package/templates/browser-agent/postcss.config.js +6 -0
  67. package/templates/browser-agent/scripts/dev.ts +138 -0
  68. package/templates/browser-agent/src/__tests__/hybrid-mode.test.ts +126 -0
  69. package/templates/browser-agent/src/__tests__/server-config-security.test.ts +55 -0
  70. package/templates/browser-agent/src/__tests__/server-config.test.ts +59 -0
  71. package/templates/browser-agent/src/bun/index.ts +130 -0
  72. package/templates/browser-agent/src/bun/three.d.ts +1 -0
  73. package/templates/browser-agent/src/gateway/http-routes.ts +1 -0
  74. package/templates/browser-agent/src/gateway/ipc-transport.ts +68 -0
  75. package/templates/browser-agent/src/gateway/sse-transport.ts +221 -0
  76. package/templates/browser-agent/src/mainview/App.tsx +45 -0
  77. package/templates/browser-agent/src/mainview/__tests__/hooks/use-rpc-init.test.ts +202 -0
  78. package/templates/browser-agent/src/mainview/__tests__/hooks/use-sidebar-resize.test.ts +127 -0
  79. package/templates/browser-agent/src/mainview/__tests__/i18n/i18n.test.ts +49 -0
  80. package/templates/browser-agent/src/mainview/__tests__/setup.ts +40 -0
  81. package/templates/browser-agent/src/mainview/__tests__/stores/use-chat-store.test.ts +73 -0
  82. package/templates/browser-agent/src/mainview/__tests__/stores/use-sidebar-store.test.ts +61 -0
  83. package/templates/browser-agent/src/mainview/__tests__/theme-variables.test.ts +36 -0
  84. package/templates/browser-agent/src/mainview/components/assets/AssetsPanel.tsx +139 -0
  85. package/templates/browser-agent/src/mainview/components/chat/ChatPanel.tsx +219 -0
  86. package/templates/browser-agent/src/mainview/components/chat/CommandBar.tsx +184 -0
  87. package/templates/browser-agent/src/mainview/components/chat/MessageBubble.tsx +249 -0
  88. package/templates/browser-agent/src/mainview/components/chat/ToolPicker.tsx +115 -0
  89. package/templates/browser-agent/src/mainview/components/common/ErrorBoundary.tsx +1 -0
  90. package/templates/browser-agent/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  91. package/templates/browser-agent/src/mainview/components/common/ThemeToggle.tsx +45 -0
  92. package/templates/browser-agent/src/mainview/components/common/__tests__/ErrorBoundary.test.tsx +74 -0
  93. package/templates/browser-agent/src/mainview/components/common/__tests__/LanguageSwitcher.test.tsx +44 -0
  94. package/templates/browser-agent/src/mainview/components/common/__tests__/ThemeToggle.test.tsx +41 -0
  95. package/templates/browser-agent/src/mainview/components/dev/NetworkPanel.tsx +155 -0
  96. package/templates/browser-agent/src/mainview/components/layout/AppLayout.tsx +311 -0
  97. package/templates/browser-agent/src/mainview/components/onboarding/SetupWizard.tsx +310 -0
  98. package/templates/browser-agent/src/mainview/components/process/ProcessPanel.tsx +329 -0
  99. package/templates/browser-agent/src/mainview/components/sidebar/SessionSidebar.tsx +122 -0
  100. package/templates/browser-agent/src/mainview/components/sidebar/SkillSidebar.tsx +115 -0
  101. package/templates/browser-agent/src/mainview/components/topbar/TopBar.tsx +350 -0
  102. package/templates/browser-agent/src/mainview/global.d.ts +13 -0
  103. package/templates/browser-agent/src/mainview/hooks/__tests__/use-breakpoint.test.ts +151 -0
  104. package/templates/browser-agent/src/mainview/hooks/use-agent-chat.ts +157 -0
  105. package/templates/browser-agent/src/mainview/hooks/use-breakpoint.ts +68 -0
  106. package/templates/browser-agent/src/mainview/hooks/use-rpc-init.ts +71 -0
  107. package/templates/browser-agent/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  108. package/templates/browser-agent/src/mainview/index.css +71 -0
  109. package/templates/browser-agent/src/mainview/index.html +12 -0
  110. package/templates/browser-agent/src/mainview/lib/api-client.ts +397 -0
  111. package/templates/browser-agent/src/mainview/lib/i18n/index.ts +30 -0
  112. package/templates/browser-agent/src/mainview/lib/i18n/locales/en.json +130 -0
  113. package/templates/browser-agent/src/mainview/lib/i18n/locales/zh.json +128 -0
  114. package/templates/browser-agent/src/mainview/lib/network-bus.ts +92 -0
  115. package/templates/browser-agent/src/mainview/lib/rpc-cache.ts +94 -0
  116. package/templates/browser-agent/src/mainview/main.tsx +18 -0
  117. package/templates/browser-agent/src/mainview/stores/__tests__/use-connection-store.test.ts +26 -0
  118. package/templates/browser-agent/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  119. package/templates/browser-agent/src/mainview/stores/__tests__/use-log-store.test.ts +28 -0
  120. package/templates/browser-agent/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  121. package/templates/browser-agent/src/mainview/stores/message-batcher.ts +25 -0
  122. package/templates/browser-agent/src/mainview/stores/use-asset-store.ts +65 -0
  123. package/templates/browser-agent/src/mainview/stores/use-chat-store.ts +200 -0
  124. package/templates/browser-agent/src/mainview/stores/use-connection-store.ts +173 -0
  125. package/templates/browser-agent/src/mainview/stores/use-locale-store.ts +27 -0
  126. package/templates/browser-agent/src/mainview/stores/use-log-store.ts +16 -0
  127. package/templates/browser-agent/src/mainview/stores/use-network-panel-store.ts +18 -0
  128. package/templates/browser-agent/src/mainview/stores/use-record-store.ts +147 -0
  129. package/templates/browser-agent/src/mainview/stores/use-session-store.ts +151 -0
  130. package/templates/browser-agent/src/mainview/stores/use-sidebar-store.ts +161 -0
  131. package/templates/browser-agent/src/mainview/stores/use-theme-store.ts +46 -0
  132. package/templates/browser-agent/src/mainview/stores/use-view-store.ts +20 -0
  133. package/templates/browser-agent/src/mainview/types/index.ts +6 -0
  134. package/templates/browser-agent/src/server-config.ts +45 -0
  135. package/templates/browser-agent/src/server.ts +78 -0
  136. package/templates/browser-agent/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  137. package/templates/browser-agent/src/shared/handlers/__tests__/chat-handler.test.ts +77 -0
  138. package/templates/browser-agent/src/shared/handlers/__tests__/file-handler.test.ts +100 -0
  139. package/templates/browser-agent/src/shared/handlers/__tests__/system-handler.test.ts +55 -0
  140. package/templates/browser-agent/src/shared/handlers/__tests__/timer-handler.test.ts +77 -0
  141. package/templates/browser-agent/src/shared/handlers/browser.ts +596 -0
  142. package/templates/browser-agent/src/shared/handlers/chat.ts +213 -0
  143. package/templates/browser-agent/src/shared/handlers/file.ts +99 -0
  144. package/templates/browser-agent/src/shared/handlers/index.ts +7 -0
  145. package/templates/browser-agent/src/shared/handlers/session.ts +167 -0
  146. package/templates/browser-agent/src/shared/handlers/system.ts +27 -0
  147. package/templates/browser-agent/src/shared/handlers/timer.ts +34 -0
  148. package/templates/browser-agent/src/shared/http-routes.ts +437 -0
  149. package/templates/browser-agent/src/shared/lib/__tests__/logger.test.ts +92 -0
  150. package/templates/browser-agent/src/shared/lib/__tests__/path-security.test.ts +77 -0
  151. package/templates/browser-agent/src/shared/lib/__tests__/web-server.test.ts +203 -0
  152. package/templates/browser-agent/src/shared/lib/agent.ts +384 -0
  153. package/templates/browser-agent/src/shared/lib/cdp.ts +448 -0
  154. package/templates/browser-agent/src/shared/lib/generate.ts +111 -0
  155. package/templates/browser-agent/src/shared/lib/logger.ts +152 -0
  156. package/templates/browser-agent/src/shared/lib/mock-stream.ts +147 -0
  157. package/templates/browser-agent/src/shared/lib/path-security.ts +38 -0
  158. package/templates/browser-agent/src/shared/lib/port-registry.ts +103 -0
  159. package/templates/browser-agent/src/shared/lib/web-server.ts +127 -0
  160. package/templates/browser-agent/src/shared/lib/xhs-extract.ts +71 -0
  161. package/templates/browser-agent/src/shared/modules/browser.ts +86 -0
  162. package/templates/browser-agent/src/shared/modules/chat.ts +20 -0
  163. package/templates/browser-agent/src/shared/modules/file.ts +40 -0
  164. package/templates/browser-agent/src/shared/modules/session.ts +55 -0
  165. package/templates/browser-agent/src/shared/modules/system.ts +8 -0
  166. package/templates/browser-agent/src/shared/modules/timer.ts +11 -0
  167. package/templates/browser-agent/src/shared/register-all-handlers.ts +36 -0
  168. package/templates/browser-agent/src/shared/rpc-schema.ts +34 -0
  169. package/templates/browser-agent/tailwind.config.js +15 -0
  170. package/templates/browser-agent/tsconfig.ipc.json +14 -0
  171. package/templates/browser-agent/tsconfig.json +36 -0
  172. package/templates/browser-agent/vite.config.ts +3 -0
  173. package/templates/browser-agent/vitest.config.ts +3 -0
  174. package/templates/chat/.husky/commit-msg +4 -0
  175. package/templates/chat/.husky/pre-commit +11 -0
  176. package/templates/chat/.husky/pre-push +6 -0
  177. package/templates/chat/CHANGELOG.md +15 -0
  178. package/templates/chat/commitlint.config.js +8 -0
  179. package/templates/chat/package.json +19 -4
  180. package/templates/chat/src/mainview/__tests__/hooks/use-input-history.test.ts +125 -0
  181. package/templates/chat/src/mainview/__tests__/setup.ts +32 -29
  182. package/templates/chat/src/mainview/components/chat/ChatPanel.tsx +80 -55
  183. package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +40 -20
  184. package/templates/chat/src/mainview/lib/i18n/locales/en.json +175 -155
  185. package/templates/chat/src/mainview/lib/i18n/locales/zh.json +173 -155
  186. package/templates/chat/src/shared/handlers/chat.ts +17 -19
  187. package/templates/chat/src/shared/handlers/debug.ts +12 -2
  188. package/templates/cowork/.env.example +8 -0
  189. package/templates/cowork/.prettierignore +6 -0
  190. package/templates/cowork/.prettierrc +9 -0
  191. package/templates/cowork/AGENTS.md +236 -0
  192. package/templates/cowork/CHANGELOG.md +15 -0
  193. package/templates/cowork/LICENSE +21 -0
  194. package/templates/cowork/README.md +103 -0
  195. package/templates/cowork/commitlint.config.js +8 -0
  196. package/templates/cowork/docs/CODE-VIEW-PLAN.md +637 -0
  197. package/templates/cowork/docs/CODE-VIEW-V2.md +196 -0
  198. package/templates/cowork/docs/CODE-VIEW-V4.md +150 -0
  199. package/templates/cowork/electrobun.config.ts +27 -0
  200. package/templates/cowork/eslint.config.mjs +79 -0
  201. package/templates/cowork/llms.txt +24 -0
  202. package/templates/cowork/package.json +87 -0
  203. package/templates/cowork/postcss.config.js +6 -0
  204. package/templates/cowork/scripts/dev.ts +138 -0
  205. package/templates/cowork/src/__tests__/hybrid-mode.test.ts +126 -0
  206. package/templates/cowork/src/__tests__/server-config-security.test.ts +55 -0
  207. package/templates/cowork/src/__tests__/server-config.test.ts +59 -0
  208. package/templates/cowork/src/bun/index.ts +130 -0
  209. package/templates/cowork/src/bun/three.d.ts +1 -0
  210. package/templates/cowork/src/gateway/http-routes.ts +1 -0
  211. package/templates/cowork/src/gateway/ipc-transport.ts +68 -0
  212. package/templates/cowork/src/gateway/sse-transport.ts +231 -0
  213. package/templates/cowork/src/mainview/App.tsx +45 -0
  214. package/templates/cowork/src/mainview/__tests__/hooks/use-rpc-init.test.ts +202 -0
  215. package/templates/cowork/src/mainview/__tests__/hooks/use-sidebar-resize.test.ts +127 -0
  216. package/templates/cowork/src/mainview/__tests__/i18n/i18n.test.ts +49 -0
  217. package/templates/cowork/src/mainview/__tests__/setup.ts +40 -0
  218. package/templates/cowork/src/mainview/__tests__/stores/use-chat-store.test.ts +73 -0
  219. package/templates/cowork/src/mainview/__tests__/stores/use-sidebar-store.test.ts +61 -0
  220. package/templates/cowork/src/mainview/__tests__/theme-variables.test.ts +36 -0
  221. package/templates/cowork/src/mainview/components/chat/TaskChat.tsx +311 -0
  222. package/templates/cowork/src/mainview/components/chat/__tests__/localhost-links.test.ts +76 -0
  223. package/templates/cowork/src/mainview/components/common/ErrorBoundary.tsx +1 -0
  224. package/templates/cowork/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  225. package/templates/cowork/src/mainview/components/common/ThemeToggle.tsx +45 -0
  226. package/templates/cowork/src/mainview/components/common/__tests__/ErrorBoundary.test.tsx +74 -0
  227. package/templates/cowork/src/mainview/components/common/__tests__/LanguageSwitcher.test.tsx +44 -0
  228. package/templates/cowork/src/mainview/components/common/__tests__/ThemeToggle.test.tsx +41 -0
  229. package/templates/cowork/src/mainview/components/dev/NetworkPanel.tsx +155 -0
  230. package/templates/cowork/src/mainview/components/layout/AppLayout.tsx +216 -0
  231. package/templates/cowork/src/mainview/components/right/ArtifactsPanel.tsx +54 -0
  232. package/templates/cowork/src/mainview/components/right/ContextPanel.tsx +133 -0
  233. package/templates/cowork/src/mainview/components/right/ElementPicker.tsx +206 -0
  234. package/templates/cowork/src/mainview/components/right/PreviewBlock.tsx +155 -0
  235. package/templates/cowork/src/mainview/components/right/ProgressPanel.tsx +85 -0
  236. package/templates/cowork/src/mainview/components/sidebar/TaskSidebar.tsx +211 -0
  237. package/templates/cowork/src/mainview/components/topbar/TopBar.tsx +86 -0
  238. package/templates/cowork/src/mainview/global.d.ts +13 -0
  239. package/templates/cowork/src/mainview/hooks/__tests__/use-breakpoint.test.ts +123 -0
  240. package/templates/cowork/src/mainview/hooks/use-breakpoint.ts +53 -0
  241. package/templates/cowork/src/mainview/hooks/use-rpc-init.ts +26 -0
  242. package/templates/cowork/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  243. package/templates/cowork/src/mainview/index.css +89 -0
  244. package/templates/cowork/src/mainview/index.html +12 -0
  245. package/templates/cowork/src/mainview/lib/api-client.ts +404 -0
  246. package/templates/cowork/src/mainview/lib/i18n/index.ts +30 -0
  247. package/templates/cowork/src/mainview/lib/i18n/locales/en.json +130 -0
  248. package/templates/cowork/src/mainview/lib/i18n/locales/zh.json +128 -0
  249. package/templates/cowork/src/mainview/lib/network-bus.ts +92 -0
  250. package/templates/cowork/src/mainview/lib/rpc-cache.ts +94 -0
  251. package/templates/cowork/src/mainview/main.tsx +18 -0
  252. package/templates/cowork/src/mainview/stores/__tests__/use-connection-store.test.ts +26 -0
  253. package/templates/cowork/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  254. package/templates/cowork/src/mainview/stores/__tests__/use-log-store.test.ts +28 -0
  255. package/templates/cowork/src/mainview/stores/__tests__/use-preview-store.test.ts +193 -0
  256. package/templates/cowork/src/mainview/stores/__tests__/use-sidebar-store.test.ts +81 -0
  257. package/templates/cowork/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  258. package/templates/cowork/src/mainview/stores/message-batcher.ts +25 -0
  259. package/templates/cowork/src/mainview/stores/use-chat-store.ts +198 -0
  260. package/templates/cowork/src/mainview/stores/use-connection-store.ts +168 -0
  261. package/templates/cowork/src/mainview/stores/use-context-store.ts +68 -0
  262. package/templates/cowork/src/mainview/stores/use-locale-store.ts +27 -0
  263. package/templates/cowork/src/mainview/stores/use-log-store.ts +16 -0
  264. package/templates/cowork/src/mainview/stores/use-network-panel-store.ts +18 -0
  265. package/templates/cowork/src/mainview/stores/use-output-store.ts +47 -0
  266. package/templates/cowork/src/mainview/stores/use-preview-store.ts +97 -0
  267. package/templates/cowork/src/mainview/stores/use-sidebar-store.ts +272 -0
  268. package/templates/cowork/src/mainview/stores/use-task-store.ts +86 -0
  269. package/templates/cowork/src/mainview/stores/use-theme-store.ts +46 -0
  270. package/templates/cowork/src/mainview/stores/use-view-store.ts +29 -0
  271. package/templates/cowork/src/mainview/types/index.ts +6 -0
  272. package/templates/cowork/src/server-config.ts +44 -0
  273. package/templates/cowork/src/server.ts +78 -0
  274. package/templates/cowork/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  275. package/templates/cowork/src/shared/handlers/__tests__/chat-handler.test.ts +77 -0
  276. package/templates/cowork/src/shared/handlers/__tests__/file-handler.test.ts +100 -0
  277. package/templates/cowork/src/shared/handlers/__tests__/preview-handler.test.ts +172 -0
  278. package/templates/cowork/src/shared/handlers/__tests__/system-handler.test.ts +55 -0
  279. package/templates/cowork/src/shared/handlers/__tests__/timer-handler.test.ts +77 -0
  280. package/templates/cowork/src/shared/handlers/chat.ts +213 -0
  281. package/templates/cowork/src/shared/handlers/context.ts +72 -0
  282. package/templates/cowork/src/shared/handlers/file.ts +99 -0
  283. package/templates/cowork/src/shared/handlers/index.ts +9 -0
  284. package/templates/cowork/src/shared/handlers/output.ts +59 -0
  285. package/templates/cowork/src/shared/handlers/preview.ts +81 -0
  286. package/templates/cowork/src/shared/handlers/system.ts +27 -0
  287. package/templates/cowork/src/shared/handlers/task.ts +101 -0
  288. package/templates/cowork/src/shared/handlers/timer.ts +34 -0
  289. package/templates/cowork/src/shared/http-routes.ts +444 -0
  290. package/templates/cowork/src/shared/lib/__tests__/logger.test.ts +92 -0
  291. package/templates/cowork/src/shared/lib/__tests__/path-security.test.ts +77 -0
  292. package/templates/cowork/src/shared/lib/__tests__/web-server.test.ts +203 -0
  293. package/templates/cowork/src/shared/lib/logger.ts +152 -0
  294. package/templates/cowork/src/shared/lib/path-security.ts +38 -0
  295. package/templates/cowork/src/shared/lib/port-registry.ts +103 -0
  296. package/templates/cowork/src/shared/lib/web-server.ts +127 -0
  297. package/templates/cowork/src/shared/modules/chat.ts +20 -0
  298. package/templates/cowork/src/shared/modules/context.ts +32 -0
  299. package/templates/cowork/src/shared/modules/file.ts +40 -0
  300. package/templates/cowork/src/shared/modules/output.ts +20 -0
  301. package/templates/cowork/src/shared/modules/preview.ts +44 -0
  302. package/templates/cowork/src/shared/modules/system.ts +8 -0
  303. package/templates/cowork/src/shared/modules/task.ts +31 -0
  304. package/templates/cowork/src/shared/modules/timer.ts +11 -0
  305. package/templates/cowork/src/shared/register-all-handlers.ts +36 -0
  306. package/templates/cowork/src/shared/rpc-schema.ts +38 -0
  307. package/templates/cowork/tailwind.config.js +15 -0
  308. package/templates/cowork/test-upload.txt +0 -0
  309. package/templates/cowork/tsconfig.ipc.json +14 -0
  310. package/templates/cowork/tsconfig.json +36 -0
  311. package/templates/cowork/vite.config.ts +3 -0
  312. package/templates/cowork/vitest.config.ts +3 -0
  313. package/templates/general/.husky/commit-msg +4 -0
  314. package/templates/general/.husky/pre-commit +11 -0
  315. package/templates/general/.husky/pre-push +6 -0
  316. package/templates/general/CHANGELOG.md +15 -0
  317. package/templates/general/commitlint.config.js +8 -0
  318. package/templates/general/package.json +19 -4
  319. package/templates/general/src/mainview/__tests__/hooks/use-input-history.test.ts +125 -0
  320. package/templates/general/src/mainview/__tests__/setup.ts +32 -29
  321. package/templates/general/src/mainview/components/chat/ChatPanel.tsx +80 -55
  322. package/templates/general/src/mainview/components/common/ThemeToggle.tsx +40 -20
  323. package/templates/general/src/mainview/components/debug/DebugPanel.tsx +170 -156
  324. package/templates/general/src/mainview/components/debug/__tests__/DebugPanel.test.tsx +169 -0
  325. package/templates/general/src/mainview/components/explorer/ConfirmDialog.tsx +42 -42
  326. package/templates/general/src/mainview/components/explorer/ContextMenu.tsx +72 -67
  327. package/templates/general/src/mainview/components/feed/FeedPanel.tsx +7 -5
  328. package/templates/general/src/mainview/components/file-preview/FilePreviewOverlay.tsx +64 -45
  329. package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +24 -7
  330. package/templates/general/src/mainview/components/git/GitPanel.tsx +700 -473
  331. package/templates/general/src/mainview/components/layout/AppLayout.tsx +123 -114
  332. package/templates/general/src/mainview/components/search/SearchPanel.tsx +15 -19
  333. package/templates/general/src/mainview/lib/i18n/locales/en.json +175 -155
  334. package/templates/general/src/mainview/lib/i18n/locales/zh.json +173 -155
  335. package/templates/general/src/mainview/stores/__tests__/use-explorer-store.test.ts +259 -0
  336. package/templates/general/src/mainview/stores/__tests__/use-feed-store.test.ts +160 -0
  337. package/templates/general/src/mainview/stores/__tests__/use-git-store.test.ts +313 -0
  338. package/templates/general/src/mainview/stores/__tests__/use-notification-store.test.ts +115 -0
  339. package/templates/general/src/mainview/stores/__tests__/use-sidebar-store.test.ts +120 -0
  340. package/templates/general/src/mainview/stores/use-explorer-store.ts +277 -260
  341. package/templates/general/src/mainview/types/index.ts +21 -20
  342. package/templates/general/src/shared/handlers/chat.ts +1 -1
  343. package/templates/general/src/shared/handlers/debug.ts +12 -2
  344. package/templates/shared/components/ErrorBoundary.tsx +0 -1
  345. package/templates/shared/vite-base.config.ts +8 -7
  346. /package/templates/{shared → browser-agent}/test-upload.txt +0 -0
@@ -0,0 +1,596 @@
1
+ /**
2
+ * Browser Handler — 浏览器控制、Agent 对话、采集
3
+ *
4
+ * 对应 PRD §7 API 设计 + §6 Agent 设计
5
+ */
6
+
7
+ import type { RPCServer } from '@dyyz1993/rpc-core';
8
+ import type { HandlerOptions } from '../rpc-schema';
9
+ import { createLogger } from '../lib/logger';
10
+ import { execXbrowser, execXbrowserTimed, agentChat } from '../lib/agent';
11
+ import { getOnlineBrowser, scrapeXhs } from '../lib/cdp';
12
+ import { runMockAgentChat } from '../lib/mock-stream';
13
+ import { config } from '../../server-config';
14
+
15
+ const log = createLogger('browser' as any);
16
+
17
+ // ===== 内部状态 =====
18
+
19
+ let _pluginsCache: unknown[] | null = null;
20
+ let _pluginsCacheTime = 0;
21
+ const _systemCache: { data: unknown; ts: number } = { data: null, ts: 0 };
22
+
23
+ /**
24
+ * 将命令字符串拆分为参数数组,支持引号包裹的参数(如 URL)。
25
+ * 例如:"scrape https://example.com --limit 5" → ["scrape", "https://example.com", "--limit", "5"]
26
+ */
27
+ function parseCommandString(cmd: string): string[] {
28
+ const args: string[] = [];
29
+ const regex = /"([^"]*)"|'([^']*)'|(\S+)/g;
30
+ let match: RegExpExecArray | null;
31
+ while ((match = regex.exec(cmd)) !== null) {
32
+ args.push(match[1] ?? match[2] ?? match[3] ?? '');
33
+ }
34
+ return args;
35
+ }
36
+
37
+ /**
38
+ * Web 模式下禁止的 xbrowser 子命令。
39
+ * record/replay/convert/extract 已解禁(录制功能需要)。
40
+ */
41
+ const BLOCKED_XBROWSER_COMMANDS = new Set<string>([
42
+ 'open', // 桌面端打开文件/程序
43
+ ]);
44
+
45
+ /** 检查命令是否被禁止,返回 null 表示允许,否则返回拒绝信息 */
46
+ function checkBlockedCommand(args: string[]): string | null {
47
+ if (args.length === 0) return null;
48
+ const cmd = args[0]!.toLowerCase();
49
+ if (BLOCKED_XBROWSER_COMMANDS.has(cmd)) {
50
+ return `🚫 命令 "${cmd}" 在 Web 模式下不可用。该命令仅在桌面端支持。`;
51
+ }
52
+ return null;
53
+ }
54
+
55
+ /**
56
+ * 使用 CDP 激活 Chrome 窗口(跨平台)。
57
+ * 通过 Browser.setWindowBounds({focused: true}) 将 Chrome 窗口弹到前台。
58
+ * 适用于所有支持 CDP 的平台(macOS/Windows/Linux)。
59
+ */
60
+ async function bringChromeToFront(): Promise<void> {
61
+ const cdpEndpoint = process.env.CDP_ENDPOINT || 'http://localhost:9221';
62
+ try {
63
+ // 1. 获取页面 target ID
64
+ const tabsRes = await fetch(`${cdpEndpoint}/json`).then((r) => r.json()) as any[];
65
+ const page = Array.isArray(tabsRes) ? (tabsRes.find((t: any) => t.type === 'page') || tabsRes[0]) : null;
66
+ if (!page?.id) return;
67
+
68
+ // 2. 获取浏览器 WebSocket URL
69
+ const versionRes = await fetch(`${cdpEndpoint}/json/version`).then((r) => r.json()) as any;
70
+ const wsUrl: string | undefined = versionRes.webSocketDebuggerUrl;
71
+ if (!wsUrl) return;
72
+
73
+ // 3. 连接浏览器 WebSocket,调用 CDP 命令
74
+ const ws = new WebSocket(wsUrl);
75
+ let msgId = 1;
76
+
77
+ await new Promise<void>((resolve) => {
78
+ const timer = setTimeout(() => { ws.close(); resolve(); }, 3000);
79
+
80
+ ws.addEventListener('open', () => {
81
+ ws.send(JSON.stringify({ id: msgId++, method: 'Browser.getWindowForTarget', params: { targetId: page.id } }));
82
+ });
83
+
84
+ ws.addEventListener('message', (ev: MessageEvent) => {
85
+ try {
86
+ const resp = JSON.parse(ev.data as string);
87
+ if (resp.id === 1 && resp.result?.windowId) {
88
+ ws.send(JSON.stringify({ id: msgId++, method: 'Browser.setWindowBounds', params: { windowId: resp.result.windowId, bounds: { focused: true } } }));
89
+ }
90
+ if (resp.id === 2 || (resp.id === 1 && resp.error)) {
91
+ clearTimeout(timer);
92
+ ws.close();
93
+ resolve();
94
+ }
95
+ } catch { /* ignore */ }
96
+ });
97
+
98
+ ws.addEventListener('error', () => { clearTimeout(timer); resolve(); });
99
+ });
100
+ } catch {
101
+ /* CDP 不可用,忽略 */
102
+ }
103
+ }
104
+
105
+ // ===== xbrowser 版本检测 =====
106
+
107
+ async function detectXbrowser(): Promise<{ available: boolean; version: string | null }> {
108
+ try {
109
+ const { execFileSync } = await import('child_process');
110
+ const out = execFileSync('/usr/local/bin/xbrowser', ['--version'], {
111
+ timeout: 5000,
112
+ encoding: 'utf8',
113
+ env: { ...process.env, NODE_OPTIONS: '' },
114
+ }).trim();
115
+ const m = out.match(/v?(\d+\.\d+\.\d+)/);
116
+ return { available: true, version: m ? (m[1] ?? null) : null };
117
+ } catch {
118
+ return { available: false, version: null };
119
+ }
120
+ }
121
+
122
+ async function getSystemInfo(force = false): Promise<unknown> {
123
+ const now = Date.now();
124
+ if (!force && _systemCache.data && now - _systemCache.ts < 30_000) {
125
+ return _systemCache.data;
126
+ }
127
+ const [xbrowser, browser] = await Promise.all([
128
+ detectXbrowser(),
129
+ getOnlineBrowser()
130
+ .then((b) => ({ connected: !!b, browsers: b ? [b] : [] }))
131
+ .catch(() => ({ connected: false, browsers: [] })),
132
+ ]);
133
+ const data = { xbrowser, browser, serverVersion: '0.4.0' };
134
+ _systemCache.data = data;
135
+ _systemCache.ts = now;
136
+ return data;
137
+ }
138
+
139
+ // ===== Handler 注册 =====
140
+
141
+ export function register(server: RPCServer, _options: HandlerOptions): void {
142
+ // 放宽类型:handler 统一转为 (params: unknown) => Promise<unknown>
143
+ const r = (method: string, handler: (params: any) => Promise<any>) => {
144
+ server.register(method, handler as (params: unknown) => Promise<unknown>);
145
+ };
146
+
147
+ r('browser.getSystemInfo', async () => {
148
+ return await getSystemInfo();
149
+ });
150
+
151
+ r('browser.checkConnection', async (params) => {
152
+ const browser = await getOnlineBrowser(params.pluginId);
153
+ return {
154
+ connected: !!browser,
155
+ browserCount: browser ? 1 : 0,
156
+ browsers: browser ? [browser] : [],
157
+ };
158
+ });
159
+
160
+ r('browser.getConnectionGuide', async () => {
161
+ const browser = await getOnlineBrowser();
162
+ return {
163
+ // 用户视角:只有"浏览器是否已连接"
164
+ connected: !!browser,
165
+ tabs: browser?.tabs ?? 0,
166
+ };
167
+ });
168
+
169
+ r('browser.listTabs', async () => {
170
+ try {
171
+ const result = await execXbrowser(['tab', 'list']);
172
+ const tabs = result?.data?.tabs || [];
173
+ return {
174
+ total: tabs.length,
175
+ activeIndex: result?.data?.activeIndex ?? 0,
176
+ tabs: tabs.map((t: Record<string, unknown>) => ({
177
+ index: t.index,
178
+ url: t.url,
179
+ title: t.title,
180
+ active: t.active,
181
+ })),
182
+ };
183
+ } catch (e: unknown) {
184
+ log.error('listTabs failed', { error: e instanceof Error ? e.message : String(e) });
185
+ return { total: 0, activeIndex: 0, tabs: [] };
186
+ }
187
+ });
188
+
189
+ r('browser.listPlugins', async () => {
190
+ if (_pluginsCache && Date.now() - _pluginsCacheTime < 5 * 60 * 1000) {
191
+ return { plugins: _pluginsCache };
192
+ }
193
+ try {
194
+ const { execFileSync } = await import('child_process');
195
+ const { tmpdir } = await import('os');
196
+ const { join } = await import('path');
197
+ const { unlinkSync, existsSync, readFileSync } = await import('fs');
198
+ const tmpFile = join(tmpdir(), `xb-plugins-${Date.now()}.json`);
199
+ execFileSync(
200
+ 'sh',
201
+ ['-c', `'/usr/local/bin/xbrowser' plugin list --json > '${tmpFile}' 2>/dev/null`],
202
+ {
203
+ timeout: 30000,
204
+ env: { ...process.env, NODE_OPTIONS: '' },
205
+ },
206
+ );
207
+ let plugins: unknown[] = [];
208
+ if (existsSync(tmpFile)) {
209
+ const raw = readFileSync(tmpFile, 'utf8').trim();
210
+ try {
211
+ const data = JSON.parse(raw);
212
+ const rawList = Array.isArray(data) ? data : data?.plugins || [];
213
+ plugins = rawList.map((p: any) => ({
214
+ name: p.name || p.id || p,
215
+ description: p.description || p.metadata?.description || '',
216
+ }));
217
+ } catch {}
218
+ try {
219
+ unlinkSync(tmpFile);
220
+ } catch {}
221
+ }
222
+ _pluginsCache = plugins;
223
+ _pluginsCacheTime = Date.now();
224
+ return { plugins };
225
+ } catch (e: unknown) {
226
+ log.error('listPlugins failed', { error: e instanceof Error ? e.message : String(e) });
227
+ return { plugins: [] };
228
+ }
229
+ });
230
+
231
+ r('browser.execXbrowser', async (params) => {
232
+ try {
233
+ // 把命令字符串拆分为参数数组(支持引号包裹的 URL 等)
234
+ const args = parseCommandString(params.command);
235
+
236
+ // Web 模式命令拦截
237
+ const blocked = checkBlockedCommand(args);
238
+ if (blocked) {
239
+ return { success: false, data: { error: blocked, blocked: true } };
240
+ }
241
+
242
+ // 如果指定了 tabIndex,自动注入 --tab 参数
243
+ if (params.tabIndex !== undefined && params.tabIndex >= 0) {
244
+ // 避免重复添加 --tab
245
+ if (!args.some((a) => a === '--tab' || a === '-t')) {
246
+ args.push('--tab', String(params.tabIndex));
247
+ }
248
+ }
249
+
250
+ const result = await execXbrowser(args);
251
+ return { success: !!result?.success, data: result?.data };
252
+ } catch (e: unknown) {
253
+ return { success: false, data: { error: e instanceof Error ? e.message : String(e) } };
254
+ }
255
+ });
256
+
257
+ // ===== 录制 =====
258
+
259
+ r('browser.recordStart', async (params) => {
260
+ // 每次录制用唯一 session 名,避免 default session 被污染
261
+ const session = params.session || `rec_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
262
+ const args = ['record', 'start', '--session', session];
263
+
264
+ // 录制需要 --url 才能正确注入事件监听器
265
+ // 如果没传 url,自动获取当前活跃标签页的 URL
266
+ let url = params.url;
267
+ if (!url) {
268
+ try {
269
+ const tabResult = await execXbrowser(['tab', 'list']);
270
+ const tabs = tabResult?.data?.tabs;
271
+ if (tabs && tabs.length > 0) {
272
+ const activeTab = tabs.find((t: any) => t.active) || tabs[0];
273
+ if (activeTab?.url && !activeTab.url.startsWith('about:')) {
274
+ url = activeTab.url;
275
+ }
276
+ }
277
+ } catch {
278
+ /* ignore */
279
+ }
280
+ }
281
+ if (url) {
282
+ args.push('--url', url);
283
+ }
284
+ try {
285
+ const result = await execXbrowserTimed(args, 10000);
286
+ // 激活 Chrome 窗口(跨平台 CDP 方案)
287
+ bringChromeToFront().catch(() => {});
288
+ return { success: !result?.error, session, startUrl: result?.startUrl || url };
289
+ } catch {
290
+ return { success: true, session, startUrl: url };
291
+ }
292
+ });
293
+
294
+ r('browser.recordStop', async (params) => {
295
+ const session = params.session || 'default';
296
+ const args = ['record', 'stop', '--session', session];
297
+ log.info('recordStop called', { session });
298
+ try {
299
+ const result = await execXbrowser(args);
300
+ log.info('recordStop execXbrowser result', JSON.stringify(result).slice(0, 300) as any);
301
+ return {
302
+ success: !!result?.ok,
303
+ actions: result?.actions || 0,
304
+ network: result?.network || 0,
305
+ durationMs: result?.durationMs || 0,
306
+ steps: result?.steps || 0,
307
+ data: result,
308
+ };
309
+ } catch (e: unknown) {
310
+ return {
311
+ success: false,
312
+ actions: 0,
313
+ network: 0,
314
+ durationMs: 0,
315
+ steps: 0,
316
+ data: { error: e instanceof Error ? e.message : String(e) },
317
+ };
318
+ }
319
+ });
320
+
321
+ r('browser.recordStatus', async (params) => {
322
+ const session = params.session || 'default';
323
+ try {
324
+ const result = await execXbrowser(['record', 'status', '--session', session]);
325
+ return {
326
+ recording: !!result?.recording,
327
+ actions: result?.actions,
328
+ network: result?.network,
329
+ hasRecording: result?.hasRecording,
330
+ };
331
+ } catch {
332
+ return { recording: false };
333
+ }
334
+ });
335
+
336
+ r('browser.processRecording', async (params) => {
337
+ const { sessionId, recordingData, title } = params;
338
+ if (!sessionId || !recordingData) {
339
+ return { messageId: '', text: '缺少录制数据' };
340
+ }
341
+
342
+ const messageId = `proc_${Date.now().toString(36)}`;
343
+ server.emitEvent('browser.agentStart', { messageId, reply: '🔧 正在分析录制数据...' });
344
+
345
+ // 构建 Agent 加工 prompt
346
+ // recordingData 可能是 recordStop 返回的摘要(actions 是数字),也可能是完整的录制文件
347
+ const actionCount = typeof recordingData.actions === 'number' ? recordingData.actions : (recordingData.actions?.length || recordingData.totalActions || 0);
348
+ const networkCount = typeof recordingData.network === 'number' ? recordingData.network : (recordingData.network?.length || recordingData.totalNetworkRequests || 0);
349
+ const durationSec = Math.round((recordingData.durationMs || 0) / 1000);
350
+ const startUrl = recordingData.startUrl || recordingData.data?.startUrl || '未知';
351
+
352
+ // 提取操作摘要(可能是完整 actions 数组,也可能是空)
353
+ const actions = Array.isArray(recordingData.actions) ? recordingData.actions : (Array.isArray(recordingData.data?.actions) ? recordingData.data.actions : []);
354
+ const actionSummary = actions.slice(0, 30).map((a: any, i: number) => {
355
+ const type = a.type || a.action?.type || 'unknown';
356
+ const selector = a.element?.selector || a.action?.element?.selector || '';
357
+ const value = a.value || a.action?.value || '';
358
+ const url = a.url || '';
359
+ return `${i + 1}. [${type}] ${selector ? `选择器: ${selector}` : ''} ${value ? `值: ${value}` : ''} ${url ? `URL: ${url}` : ''}`.trim();
360
+ }).join('\n');
361
+
362
+ // 提取网络请求摘要(最多 20 条去重)
363
+ const networks = Array.isArray(recordingData.network) ? recordingData.network : (Array.isArray(recordingData.data?.network) ? recordingData.data.network : []);
364
+ const seenPaths = new Set<string>();
365
+ const networkSummary = networks.slice(0, 50).filter((n: any) => {
366
+ const key = `${n.method || 'GET'} ${n.path || n.url}`;
367
+ if (seenPaths.has(key)) return false;
368
+ seenPaths.add(key);
369
+ return true;
370
+ }).slice(0, 20).map((n: any) => {
371
+ return `- ${n.method || 'GET'} ${n.path || n.url} → ${n.status || '?'} [${n.resourceType || ''}]`;
372
+ }).join('\n');
373
+
374
+ const hasActions = actionSummary.trim().length > 0;
375
+ const hasNetwork = networkSummary.trim().length > 0;
376
+
377
+ let prompt: string;
378
+ if (hasActions) {
379
+ prompt = `用户录制了一段浏览器操作(共 ${actionCount} 步,耗时 ${durationSec} 秒,起始页面: ${startUrl})。
380
+ ${title ? `用户建议的名称: ${title}\n` : ''}请分析这组操作,输出:
381
+
382
+ 1. **操作总结**:一句话描述这组操作做了什么
383
+ 2. **关键步骤**:列出核心步骤(去掉无意义的滚动/悬停)
384
+ 3. **技能命名**:建议一个简洁的技能名称
385
+ 4. **参数化建议**:哪些步骤可以替换为变量(如搜索关键词、URL 等)
386
+
387
+ 录制操作列表:
388
+ ${actionSummary}`;
389
+ } else if (hasNetwork) {
390
+ prompt = `用户录制了一段浏览器操作(共 ${networkCount} 个网络请求,耗时 ${durationSec} 秒,起始页面: ${startUrl})。
391
+ ${title ? `用户建议的名称: ${title}\n` : ''}虽然没有捕获到用户的具体操作步骤,但捕获到了浏览器的网络请求。请根据网络请求序列分析用户的操作意图:
392
+
393
+ 1. **意图总结**:用户大概想做什么(从访问的 URL 和 API 调用推断)
394
+ 2. **关键步骤**:从网络请求推断用户访问了哪些页面、调用了什么接口
395
+ 3. **技能命名**:建议一个简洁的技能名称
396
+ 4. **参数化建议**:哪些信息可以替换为变量
397
+
398
+ 网络请求列表:
399
+ ${networkSummary}`;
400
+ } else {
401
+ prompt = `用户录制了一段浏览器操作(耗时 ${durationSec} 秒,起始页面: ${startUrl}),但没有捕获到操作数据或网络请求。
402
+ ${title ? `用户建议的名称: ${title}\n` : ''}请基于起始页面推断用户的可能意图,并给出建议。`;
403
+ }
404
+
405
+ try {
406
+ const agentResult = await agentChat(
407
+ prompt,
408
+ sessionId,
409
+ (event) => {
410
+ if (event.type === 'tool_call') {
411
+ server.emitEvent('browser.toolCall', {
412
+ messageId,
413
+ toolCall: { id: event.toolCallId || `tc_${Date.now()}`, tool: event.toolName || '', input: event.toolInput || '', status: 'running' },
414
+ });
415
+ }
416
+ if (event.type === 'tool_result') {
417
+ server.emitEvent('browser.toolResult', { messageId, toolCallId: event.toolCallId, output: event.toolOutput || '' });
418
+ }
419
+ if (event.type === 'thinking' && event.text) {
420
+ server.emitEvent('browser.thinking', { messageId, delta: event.text });
421
+ }
422
+ if (event.type === 'text' && event.text) {
423
+ server.emitEvent('browser.textDelta', { messageId, delta: event.text });
424
+ }
425
+ if (event.type === 'turn') {
426
+ server.emitEvent('browser.turn', { messageId, turn: event.turn || 1, maxTurns: 10 });
427
+ }
428
+ },
429
+ [],
430
+ );
431
+
432
+ server.emitEvent('browser.done', {
433
+ messageId,
434
+ reply: agentResult.text || '加工完成',
435
+ steps: agentResult.steps || [],
436
+ });
437
+
438
+ return { messageId, text: agentResult.text || '加工完成' };
439
+ } catch (e: unknown) {
440
+ const errMsg = e instanceof Error ? e.message : String(e);
441
+ server.emitEvent('browser.done', { messageId, reply: `❌ 加工失败: ${errMsg}`, steps: [] });
442
+ return { messageId, text: `加工失败: ${errMsg}` };
443
+ }
444
+ });
445
+
446
+ r('browser.agentChat', async (params) => {
447
+ const { message, sessionId, activePlugins } = params;
448
+ log.info('agentChat received', {
449
+ message: message.slice(0, 50),
450
+ sessionId,
451
+ hasPlugins: !!activePlugins,
452
+ });
453
+ if (!message || !sessionId) {
454
+ return { messageId: '', text: '缺少必要参数', steps: [] };
455
+ }
456
+
457
+ const messageId = `msg_${Date.now().toString(36)}`;
458
+
459
+ // 发送 Agent 开始事件
460
+ server.emitEvent('browser.agentStart', {
461
+ messageId,
462
+ reply: '🤔 思考中...',
463
+ });
464
+
465
+ // ── Mock 流式模式:跳过真实 Agent,用脚本化演示 ──────────
466
+ if (config.enableMockStream) {
467
+ log.info('agentChat running in MOCK mode (ENABLE_MOCK_STREAM=true)');
468
+ const mockResult = await runMockAgentChat(message, messageId, (event, payload) => {
469
+ server.emitEvent(event as never, payload);
470
+ });
471
+ return { messageId, text: mockResult.text, steps: mockResult.steps };
472
+ }
473
+
474
+ // 检查浏览器连接
475
+ const browser = await getOnlineBrowser();
476
+ if (!browser) {
477
+ server.emitEvent('browser.done', {
478
+ messageId,
479
+ reply: '⚠️ 没有检测到在线浏览器,请先安装并加载 Chrome 扩展。',
480
+ steps: [],
481
+ });
482
+ return {
483
+ messageId,
484
+ text: '⚠️ 没有检测到在线浏览器',
485
+ steps: [],
486
+ };
487
+ }
488
+
489
+ // 调用 Agent
490
+ const liveToolCalls: {
491
+ id: string;
492
+ tool: string;
493
+ input: string;
494
+ output: string;
495
+ status: string;
496
+ }[] = [];
497
+ const agentResult = await agentChat(
498
+ message,
499
+ sessionId,
500
+ (event) => {
501
+ if (event.type === 'tool_call') {
502
+ const tcId = event.toolCallId || `tc_${liveToolCalls.length}`;
503
+ liveToolCalls.push({
504
+ id: tcId,
505
+ tool: event.toolName || '',
506
+ input: event.toolInput || '',
507
+ output: '',
508
+ status: 'running',
509
+ });
510
+ server.emitEvent('browser.toolCall', {
511
+ messageId,
512
+ toolCall: {
513
+ id: tcId,
514
+ tool: event.toolName || '',
515
+ input: event.toolInput || '',
516
+ status: 'running',
517
+ },
518
+ });
519
+ }
520
+ if (event.type === 'tool_result') {
521
+ const last = liveToolCalls[liveToolCalls.length - 1];
522
+ const tcId = event.toolCallId || last?.id;
523
+ if (last) {
524
+ last.output = event.toolOutput || '';
525
+ last.status = 'done';
526
+ }
527
+ server.emitEvent('browser.toolResult', {
528
+ messageId,
529
+ toolCallId: tcId,
530
+ output: event.toolOutput || '',
531
+ });
532
+ }
533
+ if (event.type === 'thinking' && event.text) {
534
+ server.emitEvent('browser.thinking', {
535
+ messageId,
536
+ delta: event.text,
537
+ });
538
+ }
539
+ if (event.type === 'turn' && event.turn) {
540
+ server.emitEvent('browser.turn', {
541
+ messageId,
542
+ turn: event.turn,
543
+ maxTurns: Number(process.env.AGENT_MAX_TURNS || 30),
544
+ });
545
+ }
546
+ if (event.type === 'text' && event.text) {
547
+ server.emitEvent('browser.textDelta', {
548
+ messageId,
549
+ delta: event.text,
550
+ });
551
+ }
552
+ },
553
+ activePlugins,
554
+ );
555
+
556
+ const steps = liveToolCalls.map((tc) => ({
557
+ label: tc.tool,
558
+ status: 'done' as const,
559
+ detail: tc.input.slice(0, 80),
560
+ }));
561
+
562
+ // 触发采集
563
+ let finalText = agentResult.text;
564
+ if (agentResult.usedScrape) {
565
+ const urlMatch = message.match(/https?:\/\/[^\s,,""''))]+/);
566
+ const targetUrl = urlMatch?.[0] || 'https://www.xiaohongshu.com/explore';
567
+
568
+ const scrapeResult = await scrapeXhs(
569
+ '',
570
+ sessionId,
571
+ messageId,
572
+ '',
573
+ (scrapeSteps) => {
574
+ const allSteps = [...steps, ...scrapeSteps];
575
+ server.emitEvent('browser.progress', {
576
+ messageId,
577
+ steps: allSteps,
578
+ });
579
+ },
580
+ { url: targetUrl },
581
+ );
582
+
583
+ finalText = agentResult.text + `\n\n✅ 采集完成: ${scrapeResult.notes.length} 条笔记`;
584
+ }
585
+
586
+ // 发送完成事件
587
+ const allSteps = steps;
588
+ server.emitEvent('browser.done', {
589
+ messageId,
590
+ reply: finalText,
591
+ steps: allSteps,
592
+ });
593
+
594
+ return { messageId, text: finalText, steps: allSteps };
595
+ });
596
+ }