@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,203 @@
1
+ import { describe, it, expect, afterEach } from "vitest";
2
+ import http from "http";
3
+ import {
4
+ createWebServer,
5
+ getLocalIP,
6
+ findAvailablePort,
7
+ type WebServerResult,
8
+ } from "../web-server";
9
+
10
+ function httpGet(url: string, headers?: Record<string, string>): Promise<{ status: number; body: string }> {
11
+ return new Promise((resolve, reject) => {
12
+ const req = http.get(url, { headers }, (res) => {
13
+ let data = "";
14
+ res.on("data", (chunk) => {
15
+ data += chunk;
16
+ });
17
+ res.on("end", () => resolve({ status: res.statusCode ?? 0, body: data }));
18
+ });
19
+ req.on("error", reject);
20
+ });
21
+ }
22
+
23
+ describe("createWebServer 工厂函数", () => {
24
+ let webServer: WebServerResult | null = null;
25
+
26
+ afterEach(async () => {
27
+ if (webServer) {
28
+ await webServer.close();
29
+ webServer = null;
30
+ }
31
+ });
32
+
33
+ it("应返回 httpServer + sse + port + authToken", async () => {
34
+ webServer = await createWebServer({
35
+ port: 0,
36
+ authToken: "test-token-001",
37
+ maxUploadSize: 1024,
38
+ corsOrigin: "*",
39
+ });
40
+
41
+ expect(webServer.httpServer).toBeDefined();
42
+ expect(webServer.sse).toBeDefined();
43
+ expect(webServer.port).toBeGreaterThan(0);
44
+ expect(webServer.authToken).toBe("test-token-001");
45
+ expect(typeof webServer.close).toBe("function");
46
+ });
47
+
48
+ it("应启动 HTTP 服务并监听指定端口", async () => {
49
+ const testServer = http.createServer();
50
+ const port = await new Promise<number>((resolve) => {
51
+ testServer.listen(0, () => {
52
+ const addr = testServer.address();
53
+ resolve(typeof addr === "object" && addr ? addr.port : 3100);
54
+ });
55
+ });
56
+ await new Promise<void>((resolve) => testServer.close(() => resolve()));
57
+
58
+ webServer = await createWebServer({
59
+ port,
60
+ authToken: "test-token-002",
61
+ maxUploadSize: 1024,
62
+ corsOrigin: "*",
63
+ });
64
+
65
+ expect(webServer.port).toBe(port);
66
+
67
+ const { status, body } = await httpGet(`http://localhost:${webServer.port}/health`);
68
+ expect(status).toBe(200);
69
+ const json = JSON.parse(body);
70
+ expect(json.status).toBe("ok");
71
+ });
72
+
73
+ it("端口冲突时应自动递增到可用端口", async () => {
74
+ const blocker = http.createServer();
75
+ const blockerPort = await new Promise<number>((resolve) => {
76
+ blocker.listen(0, () => {
77
+ const addr = blocker.address();
78
+ resolve(typeof addr === "object" && addr ? addr.port : 3100);
79
+ });
80
+ });
81
+
82
+ webServer = await createWebServer({
83
+ port: blockerPort,
84
+ authToken: "test-token-003",
85
+ maxUploadSize: 1024,
86
+ corsOrigin: "*",
87
+ });
88
+
89
+ expect(webServer.port).not.toBe(blockerPort);
90
+ expect(webServer.port).toBeGreaterThan(blockerPort);
91
+
92
+ await new Promise<void>((resolve) => blocker.close(() => resolve()));
93
+ });
94
+
95
+ it("SSE 连接需要 token 才能访问 /api/events", async () => {
96
+ webServer = await createWebServer({
97
+ port: 0,
98
+ authToken: "test-token-004",
99
+ maxUploadSize: 1024,
100
+ corsOrigin: "*",
101
+ });
102
+
103
+ // 无 token → 401
104
+ const { status } = await httpGet(`http://localhost:${webServer.port}/api/events`);
105
+ expect(status).toBe(401);
106
+ });
107
+
108
+ it("带 token 的 SSE 连接应返回 200 + text/event-stream", async () => {
109
+ webServer = await createWebServer({
110
+ port: 0,
111
+ authToken: "test-token-005",
112
+ maxUploadSize: 1024,
113
+ corsOrigin: "*",
114
+ });
115
+
116
+ // SSE is a long-lived stream — read just enough to verify headers + ready event
117
+ const result = await new Promise<{ status: number; headers: Record<string, string>; body: string }>((resolve, reject) => {
118
+ const req = http.get(
119
+ `http://localhost:${webServer!.port}/api/events?token=${webServer!.authToken}`,
120
+ (res) => {
121
+ let body = "";
122
+ const timer = setTimeout(() => {
123
+ req.destroy();
124
+ resolve({ status: res.statusCode ?? 0, headers: res.headers as Record<string, string>, body });
125
+ }, 300);
126
+ res.on("data", (chunk) => {
127
+ body += chunk;
128
+ // Once we have the ready event, we can close
129
+ if (body.includes("event: ready")) {
130
+ clearTimeout(timer);
131
+ req.destroy();
132
+ resolve({ status: res.statusCode ?? 0, headers: res.headers as Record<string, string>, body });
133
+ }
134
+ });
135
+ }
136
+ );
137
+ req.on("error", (err) => {
138
+ if (!err.message.includes("aborted")) reject(err);
139
+ });
140
+ });
141
+
142
+ expect(result.status).toBe(200);
143
+ expect(result.body).toContain("event: ready");
144
+ });
145
+
146
+ it("HTTP 请求需要 token 才能访问文件路由", async () => {
147
+ webServer = await createWebServer({
148
+ port: 0,
149
+ authToken: "test-token-006",
150
+ maxUploadSize: 1024,
151
+ corsOrigin: "*",
152
+ });
153
+
154
+ const { status } = await httpGet(`http://localhost:${webServer.port}/info/some/path`);
155
+ expect(status).toBe(401);
156
+ });
157
+
158
+ it("health 端点不需要 token", async () => {
159
+ webServer = await createWebServer({
160
+ port: 0,
161
+ authToken: "test-token-007",
162
+ maxUploadSize: 1024,
163
+ corsOrigin: "*",
164
+ });
165
+
166
+ const { status, body } = await httpGet(`http://localhost:${webServer.port}/health`);
167
+ expect(status).toBe(200);
168
+ const json = JSON.parse(body);
169
+ expect(json).toHaveProperty("status", "ok");
170
+ expect(json).toHaveProperty("clients");
171
+ });
172
+
173
+ it("关闭 webServer 应停止 HTTP 服务", async () => {
174
+ webServer = await createWebServer({
175
+ port: 0,
176
+ authToken: "test-token-008",
177
+ maxUploadSize: 1024,
178
+ corsOrigin: "*",
179
+ });
180
+
181
+ const port = webServer.port;
182
+ await webServer.close();
183
+ webServer = null;
184
+
185
+ await expect(httpGet(`http://localhost:${port}/health`)).rejects.toThrow();
186
+ });
187
+ });
188
+
189
+ describe("getLocalIP", () => {
190
+ it("应返回非空字符串", () => {
191
+ const ip = getLocalIP();
192
+ expect(typeof ip).toBe("string");
193
+ expect(ip.length).toBeGreaterThan(0);
194
+ expect(ip).toMatch(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/);
195
+ });
196
+ });
197
+
198
+ describe("findAvailablePort", () => {
199
+ it("应返回一个可用端口", async () => {
200
+ const port = await findAvailablePort(40000);
201
+ expect(port).toBeGreaterThanOrEqual(40000);
202
+ });
203
+ });
@@ -0,0 +1,384 @@
1
+ /**
2
+ * Agent — 基于 pi SDK RpcClient 的浏览器自动化 Agent
3
+ *
4
+ * 架构(对应 PRD §6):
5
+ * RpcClient(子进程 pi --mode rpc)
6
+ * ├── onEvent() → 流式事件推送
7
+ * ├── getMessages() → 完整历史消息(JSONL 持久化)
8
+ * ├── promptAndWait() → 发送消息触发 Agent 循环
9
+ * └── setActiveTools() → 激活浏览器工具
10
+ *
11
+ * 每次 prompt 前注入 <browser-context> XML + 选中的插件提示
12
+ */
13
+
14
+ import { RpcClient } from "@dyyz1993/pi-coding-agent";
15
+ import { spawn } from "child_process";
16
+
17
+ const XBROWSER_CMD = "/usr/local/bin/xbrowser";
18
+ const CDP_ENDPOINT = process.env.CDP_ENDPOINT || "http://localhost:9221";
19
+
20
+ /** Agent 轮次上限 */
21
+ const MAX_TURNS = Math.max(1, parseInt(process.env.AGENT_MAX_TURNS || "30", 10));
22
+ export const MAX_TURNS_CONFIG = MAX_TURNS;
23
+
24
+ /** Agent 整体超时 */
25
+ const AGENT_TIMEOUT_MS = Math.max(
26
+ 30_000,
27
+ parseInt(process.env.AGENT_TIMEOUT_MS || String(5 * 60 * 1000), 10),
28
+ );
29
+
30
+ // ===== xbrowser 命令执行 =====
31
+
32
+ export async function execXbrowser(args: string[]): Promise<any> {
33
+ const allArgs = [...args, "--cdp", CDP_ENDPOINT, "--json"];
34
+ return new Promise((resolve, reject) => {
35
+ const child = spawn(XBROWSER_CMD, allArgs, {
36
+ stdio: ["ignore", "pipe", "pipe"],
37
+ env: { ...process.env, NODE_OPTIONS: "" },
38
+ });
39
+ const chunks: Buffer[] = [];
40
+ child.stdout.on("data", (d: Buffer) => chunks.push(d));
41
+ const timeout = setTimeout(() => {
42
+ child.kill("SIGTERM");
43
+ reject(new Error("timeout"));
44
+ }, 60000);
45
+ child.on("error", (e) => {
46
+ clearTimeout(timeout);
47
+ reject(e);
48
+ });
49
+ child.on("close", () => {
50
+ clearTimeout(timeout);
51
+ const out = Buffer.concat(chunks as any).toString("utf8");
52
+ const m = out.match(/\{[\s\S]*\}/);
53
+ try {
54
+ resolve(m ? JSON.parse(m[0]) : { success: false });
55
+ } catch {
56
+ resolve({ success: false });
57
+ }
58
+ });
59
+ });
60
+ }
61
+
62
+ /**
63
+ * 带自定义超时的 xbrowser 执行。
64
+ * 用于 record start 这类后台命令——超时后不杀进程(keepAlive=true),
65
+ * 因为录制命令需要持续运行。
66
+ */
67
+ export async function execXbrowserTimed(
68
+ args: string[],
69
+ timeoutMs: number,
70
+ keepAlive = false,
71
+ ): Promise<any> {
72
+ const allArgs = [...args, "--cdp", CDP_ENDPOINT, "--json"];
73
+ return new Promise((resolve, reject) => {
74
+ const child = spawn(XBROWSER_CMD, allArgs, {
75
+ stdio: ["ignore", "pipe", "pipe"],
76
+ env: { ...process.env, NODE_OPTIONS: "" },
77
+ });
78
+ const chunks: Buffer[] = [];
79
+ let resolved = false;
80
+
81
+ child.stdout.on("data", (d: Buffer) => chunks.push(d));
82
+
83
+ const timer = setTimeout(() => {
84
+ if (resolved) return;
85
+ resolved = true;
86
+ if (!keepAlive) child.kill("SIGTERM");
87
+ // 超时:如果有输出就解析返回,否则 reject
88
+ const out = Buffer.concat(chunks as any).toString("utf8").trim();
89
+ if (out) {
90
+ const m = out.match(/\{[\s\S]*\}/);
91
+ try {
92
+ resolve(m ? JSON.parse(m[0]) : { success: true, timeout: true });
93
+ } catch {
94
+ resolve({ success: true, timeout: true });
95
+ }
96
+ } else {
97
+ resolve({ success: true, timeout: true });
98
+ }
99
+ }, timeoutMs);
100
+
101
+ child.on("error", (e) => {
102
+ if (resolved) return;
103
+ resolved = true;
104
+ clearTimeout(timer);
105
+ reject(e);
106
+ });
107
+
108
+ child.on("close", () => {
109
+ if (resolved) return;
110
+ resolved = true;
111
+ clearTimeout(timer);
112
+ const out = Buffer.concat(chunks as any).toString("utf8");
113
+ const m = out.match(/\{[\s\S]*\}/);
114
+ try {
115
+ resolve(m ? JSON.parse(m[0]) : { success: false });
116
+ } catch {
117
+ resolve({ success: false });
118
+ }
119
+ });
120
+ });
121
+ }
122
+
123
+ // ===== 浏览器上下文注入 =====
124
+
125
+ async function getBrowserContext(): Promise<string> {
126
+ try {
127
+ const tabsResult = await execXbrowser(["tab", "list"]);
128
+ const tabs = tabsResult?.data?.tabs || [];
129
+ const activeIndex = tabsResult?.data?.activeIndex ?? 0;
130
+
131
+ let currentUrl = "";
132
+ let currentTitle = "";
133
+ try {
134
+ currentUrl =
135
+ (await execXbrowser(["eval", "location.href"]))?.data?.result || "";
136
+ } catch {}
137
+ try {
138
+ currentTitle =
139
+ (await execXbrowser(["eval", "document.title"]))?.data?.result || "";
140
+ } catch {}
141
+
142
+ const tabLines = tabs
143
+ .map((t: any, i: number) => {
144
+ const url = t.url || (i === activeIndex ? currentUrl : "");
145
+ if (!url || url === "about:blank") return null;
146
+ const title = t.title || (i === activeIndex ? currentTitle : "");
147
+ const marker = i === activeIndex ? ' current="true"' : "";
148
+ return ` <tab index="${i}" url="${url}" title="${title}"${marker} />`;
149
+ })
150
+ .filter(Boolean)
151
+ .join("\n");
152
+
153
+ return `<browser-context>
154
+ <tabs total="${tabs.length}" active="${activeIndex}">
155
+ ${tabLines || " (无标签页)"}
156
+ </tabs>
157
+ <current_page url="${currentUrl}" title="${currentTitle}" />
158
+ </browser-context>`;
159
+ } catch {
160
+ return `<browser-context><error>无法获取浏览器状态</error></browser-context>`;
161
+ }
162
+ }
163
+
164
+ // ===== RPC Client 管理(每个 sessionId 一个独立持久化 session) =====
165
+
166
+ const _clients = new Map<string, RpcClient>();
167
+
168
+ export async function getClient(sessionId: string): Promise<RpcClient | null> {
169
+ if (_clients.has(sessionId)) return _clients.get(sessionId)!;
170
+
171
+ try {
172
+ const client = new RpcClient({
173
+ cliPath: "node_modules/.bin/pi",
174
+ args: ["--mode", "rpc"],
175
+ });
176
+
177
+ await client.start();
178
+
179
+ try {
180
+ await client.newSession(`browser-agent-${sessionId}`);
181
+ console.log(`[Agent] New persistent session for ${sessionId}`);
182
+ } catch (e: any) {
183
+ console.log("[Agent] newSession warning:", e.message);
184
+ }
185
+
186
+ await client.setActiveTools([
187
+ "xbrowser",
188
+ "web_search",
189
+ "fetch_content",
190
+ "todo",
191
+ ]);
192
+
193
+ _clients.set(sessionId, client);
194
+ return client;
195
+ } catch (e: any) {
196
+ console.error("[Agent] RPC client init failed:", e.message);
197
+ return null;
198
+ }
199
+ }
200
+
201
+ /** 清理 Agent session */
202
+ export function disposeAgentSession(sessionId: string): void {
203
+ const client = _clients.get(sessionId);
204
+ if (client) {
205
+ try {
206
+ client.stop();
207
+ } catch {}
208
+ _clients.delete(sessionId);
209
+ }
210
+ }
211
+
212
+ /** 获取 Agent session 的完整历史消息 */
213
+ export async function getAgentMessages(sessionId: string): Promise<any[]> {
214
+ const client = _clients.get(sessionId);
215
+ if (!client) return [];
216
+ try {
217
+ return (await client.getMessages()) || [];
218
+ } catch {
219
+ return [];
220
+ }
221
+ }
222
+
223
+ // ===== Agent 事件类型 =====
224
+
225
+ export interface AgentEvent {
226
+ type: string;
227
+ text?: string;
228
+ thinking?: string;
229
+ toolName?: string;
230
+ toolInput?: string;
231
+ toolOutput?: string;
232
+ toolCallId?: string;
233
+ turn?: number;
234
+ }
235
+
236
+ export interface AgentReply {
237
+ text: string;
238
+ steps?: { tool: string; input: string; output: string }[];
239
+ usedScrape?: boolean;
240
+ messages?: any[];
241
+ }
242
+
243
+ /**
244
+ * Agent 对话入口
245
+ * @param message 用户消息
246
+ * @param sessionId 会话 ID
247
+ * @param onEvent 实时事件回调
248
+ * @param activePlugins 选中插件列表
249
+ */
250
+ export async function agentChat(
251
+ message: string,
252
+ sessionId: string,
253
+ onEvent?: (event: AgentEvent) => void,
254
+ activePlugins?: string[],
255
+ ): Promise<AgentReply> {
256
+ const client = await getClient(sessionId);
257
+ if (!client) {
258
+ return { text: "Agent 初始化失败" };
259
+ }
260
+
261
+ // 注入浏览器上下文 + 选中插件提示
262
+ const browserContext = await getBrowserContext();
263
+ let pluginHint = "";
264
+ if (activePlugins && activePlugins.length > 0) {
265
+ // 把工具 ID 映射成 Agent 能理解的描述
266
+ const toolDescs: Record<string, string> = {
267
+ scrape: "采集当前页面内容转 Markdown(xbrowser scrape)",
268
+ crawl: "批量爬取网站多个页面(xbrowser crawl)",
269
+ map: "发现网站所有 URL 结构(xbrowser map)",
270
+ screenshot: "截取当前页面(xbrowser screenshot)",
271
+ search: "搜索引擎搜索关键词(xbrowser search)",
272
+ "scrape-xhs": "采集小红书笔记(scrapeXhs 专用流程)",
273
+ xiaohongshu: "采集小红书内容(xiaohongshu 插件)",
274
+ };
275
+ const toolList = activePlugins.map((p) => {
276
+ const desc = toolDescs[p];
277
+ return desc ? ` - ${p}: ${desc}` : ` - ${p}`;
278
+ }).join("\n");
279
+ pluginHint = `\n<active-plugins>\n 用户选择了以下工具/插件,请优先使用它们完成任务。根据用户的需求自动编排执行顺序:\n${toolList}\n</active-plugins>`;
280
+ }
281
+ const fullMessage = `${browserContext}${pluginHint}\n\n用户请求: ${message}`;
282
+
283
+ const steps: { tool: string; input: string; output: string }[] = [];
284
+ let fullText = "";
285
+ let usedScrape = false;
286
+ let turnCount = 0;
287
+ let hitTurnLimit = false;
288
+
289
+ const unsub = client.onEvent((event: any) => {
290
+ // 工具开始
291
+ if (event.type === "tool_execution_start") {
292
+ const toolName = event.toolName || "";
293
+ const args = event.args || event.input || event.arguments || {};
294
+ if (toolName === "xbrowser") {
295
+ const inputStr = JSON.stringify(args).toLowerCase();
296
+ if (inputStr.includes("scrape") || inputStr.includes("crawl"))
297
+ usedScrape = true;
298
+ }
299
+ const input = JSON.stringify(args).slice(0, 300);
300
+ steps.push({ tool: toolName, input, output: "" });
301
+ onEvent?.({
302
+ type: "tool_call",
303
+ toolName,
304
+ toolInput: input,
305
+ toolCallId: event.toolCallId,
306
+ });
307
+ }
308
+ // 工具结束
309
+ if (event.type === "tool_execution_end") {
310
+ const last = steps[steps.length - 1];
311
+ const output = (
312
+ event.result?.content?.[0]?.text || event.text || ""
313
+ ).slice(0, 300);
314
+ if (last) last.output = output;
315
+ onEvent?.({
316
+ type: "tool_result",
317
+ toolName: last?.tool || event.toolName,
318
+ toolOutput: output,
319
+ toolCallId: event.toolCallId,
320
+ });
321
+ }
322
+ // turn 结束
323
+ if (event.type === "turn_end") {
324
+ turnCount++;
325
+ if (turnCount >= MAX_TURNS && !hitTurnLimit) {
326
+ hitTurnLimit = true;
327
+ try {
328
+ client.abort();
329
+ } catch {}
330
+ }
331
+ onEvent?.({ type: "turn", turn: turnCount });
332
+ }
333
+ // 消息更新
334
+ if (event.type === "message_update") {
335
+ const ae = event.assistantMessageEvent;
336
+ if (ae?.type === "text_delta" && ae.delta) {
337
+ fullText += ae.delta;
338
+ onEvent?.({ type: "text", text: ae.delta });
339
+ } else if (ae?.type === "thinking_delta" && ae.delta) {
340
+ onEvent?.({ type: "thinking", text: ae.delta });
341
+ }
342
+ }
343
+ });
344
+
345
+ let timedOut = false;
346
+ const timer = setTimeout(() => {
347
+ timedOut = true;
348
+ try {
349
+ client.abort();
350
+ } catch {}
351
+ }, AGENT_TIMEOUT_MS);
352
+
353
+ try {
354
+ await client.promptAndWait(fullMessage, undefined, AGENT_TIMEOUT_MS);
355
+ } catch (e: any) {
356
+ if (!fullText && !timedOut && !hitTurnLimit)
357
+ fullText = "⚠️ " + e.message;
358
+ } finally {
359
+ clearTimeout(timer);
360
+ }
361
+
362
+ // 轮次上限总结
363
+ if (hitTurnLimit) {
364
+ const summaryPrompt = `已达到工具调用轮次上限(${MAX_TURNS} 轮)。请根据以上操作历史,用简短要点总结:\n1. 已经完成了什么;\n2. 当前卡在哪一步;\n3. 如果用户想继续,下一步该做什么。\n不要再调用任何工具,直接给出总结。`;
365
+ try {
366
+ await client.promptAndWait(summaryPrompt, undefined, 60_000);
367
+ } catch {}
368
+ fullText += `\n\n_(已达轮次上限 ${MAX_TURNS},以上为进展总结)_`;
369
+ }
370
+
371
+ if (typeof unsub === "function") unsub();
372
+
373
+ let messages: any[] = [];
374
+ try {
375
+ messages = (await client.getMessages()) || [];
376
+ } catch {}
377
+
378
+ return {
379
+ text: fullText.trim() || "(无回复)",
380
+ steps: steps.length > 0 ? steps : undefined,
381
+ usedScrape,
382
+ messages,
383
+ };
384
+ }