@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
package/src/lib/copy.ts CHANGED
@@ -7,11 +7,46 @@ import {
7
7
  unlinkSync,
8
8
  chmodSync,
9
9
  } from "fs";
10
- import { join, resolve } from "path";
11
- import { execSync } from "child_process";
10
+ import { join, resolve, extname } from "path";
11
+ import { homedir } from "os";
12
+ import { execFileSync } from "child_process";
13
+ import { getRootDir } from "./templates.js";
12
14
 
13
15
  const SKIP_DIRS = new Set(["node_modules", "build", "dist", ".git", ".husky", ".trae"]);
14
- const SKIP_FILES = new Set(["bun.lock", "rpc-browser.js"]);
16
+ const SKIP_FILES = new Set(["bun.lock"]);
17
+
18
+ const BINARY_EXTENSIONS = new Set([
19
+ ".png",
20
+ ".jpg",
21
+ ".jpeg",
22
+ ".gif",
23
+ ".webp",
24
+ ".ico",
25
+ ".icns",
26
+ ".ttf",
27
+ ".otf",
28
+ ".woff",
29
+ ".woff2",
30
+ ".mp3",
31
+ ".mp4",
32
+ ".wav",
33
+ ".pdf",
34
+ ".zip",
35
+ ".gz",
36
+ ]);
37
+
38
+ export function expandTilde(targetPath: string): string {
39
+ if (targetPath === "~") return homedir();
40
+ if (targetPath.startsWith("~/")) return join(homedir(), targetPath.slice(2));
41
+ return targetPath;
42
+ }
43
+
44
+ function isBinaryFile(filePath: string, buffer: Buffer): boolean {
45
+ if (BINARY_EXTENSIONS.has(extname(filePath).toLowerCase())) return true;
46
+ // A NUL byte in the first 8KB almost certainly means binary content;
47
+ // reading such a file as utf-8 would silently corrupt it.
48
+ return buffer.subarray(0, 8192).includes(0);
49
+ }
15
50
 
16
51
  export interface CopyOptions {
17
52
  projectName: string;
@@ -43,14 +78,20 @@ export function copyAndReplace(srcDir: string, destDir: string, projectName: str
43
78
  } else {
44
79
  if (SKIP_FILES.has(entry.name)) continue;
45
80
 
46
- let content = readFileSync(srcPath, "utf-8");
81
+ const raw = readFileSync(srcPath);
82
+
83
+ if (isBinaryFile(entry.name, raw)) {
84
+ writeFileSync(destPath, raw);
85
+ continue;
86
+ }
87
+
88
+ let content = raw.toString("utf-8");
47
89
 
48
90
  content = content.replace(/pi-agent-template/g, projectName);
49
91
  content = content.replace(/Pi Agent Template/g, pascalName);
50
92
  content = content.replace(/Pi Agent/g, pascalName);
51
93
  content = content.replace(/com\.piagent\.template/g, identifier);
52
94
  content = content.replace(/com\.piagent/g, shortId);
53
- content = content.replace(/@pi-agent\//g, `@${projectName}/`);
54
95
 
55
96
  writeFileSync(destPath, content);
56
97
  }
@@ -74,8 +115,8 @@ function copyTraeRules(monorepoRoot: string, targetDir: string, projectName: str
74
115
  }
75
116
  }
76
117
 
77
- function copySharedModules(monorepoRoot: string, targetDir: string, projectName: string): void {
78
- const sharedDir = resolve(monorepoRoot, "templates", "shared");
118
+ function copySharedModules(sourceRoot: string, targetDir: string, projectName: string): void {
119
+ const sharedDir = resolve(sourceRoot, "templates", "shared");
79
120
  if (!existsSync(sharedDir)) return;
80
121
 
81
122
  const destSharedDir = join(targetDir, "shared");
@@ -145,76 +186,83 @@ function cleanSharedViteConfig(sharedDir: string): void {
145
186
  }
146
187
  }
147
188
 
148
- function resolvePackageVersion(packageName: string): string {
189
+ function resolvePackageVersion(packageName: string, fallbackRange?: string): string {
149
190
  try {
150
- return execSync(`npm view ${packageName} version`, { encoding: "utf-8" }).trim();
191
+ const version = execFileSync("npm", ["view", packageName, "version"], {
192
+ encoding: "utf-8",
193
+ }).trim();
194
+ return `^${version}`;
151
195
  } catch {
152
- return "1.0.0";
196
+ // Offline / registry failure: keep a usable range instead of silently
197
+ // downgrading the project to an ancient version.
198
+ if (fallbackRange && fallbackRange !== "workspace:*") {
199
+ console.warn(`(Could not look up latest ${packageName}; keeping ${fallbackRange})`);
200
+ return fallbackRange;
201
+ }
202
+ const fallback = NPM_FALLBACK_VERSIONS[packageName];
203
+ if (fallback) {
204
+ console.warn(`(Could not look up latest ${packageName}; falling back to ${fallback})`);
205
+ return fallback;
206
+ }
207
+ console.warn(`(Could not look up latest ${packageName}; keeping ${String(fallbackRange)})`);
208
+ return fallbackRange ?? "*";
153
209
  }
154
210
  }
155
211
 
212
+ // Only used when the registry is unreachable AND no usable range is present;
213
+ // bundled templates carry pinned versions via post-sync-templates.mjs.
214
+ const NPM_FALLBACK_VERSIONS: Record<string, string> = {
215
+ "@dyyz1993/rpc-core": "^2.2.0",
216
+ "@dyyz1993/eslint-plugin-rpc": "^1.1.0",
217
+ };
218
+
156
219
  const WORKSPACE_PACKAGES = ["@dyyz1993/rpc-core", "@dyyz1993/eslint-plugin-rpc"];
157
220
 
158
- function updatePackageJson(targetDir: string, _projectName: string): void {
221
+ function updatePackageJson(targetDir: string, projectName: string): void {
159
222
  const rootPkgPath = join(targetDir, "package.json");
160
223
  if (!existsSync(rootPkgPath)) return;
161
224
 
162
225
  const rootPkg = JSON.parse(readFileSync(rootPkgPath, "utf-8"));
163
226
 
164
227
  delete rootPkg.workspaces;
228
+ rootPkg.name = projectName;
165
229
 
166
230
  for (const depKey of ["dependencies", "devDependencies"] as const) {
167
231
  if (!rootPkg[depKey]) continue;
168
232
  for (const pkgName of WORKSPACE_PACKAGES) {
169
233
  if (!rootPkg[depKey][pkgName]) continue;
170
- if (pkgName === "@dyyz1993/rpc-core") {
171
- const version = resolvePackageVersion(pkgName);
172
- rootPkg[depKey][pkgName] = `^${version}`;
173
- } else {
174
- delete rootPkg[depKey][pkgName];
175
- }
234
+ rootPkg[depKey][pkgName] = resolvePackageVersion(pkgName, rootPkg[depKey][pkgName]);
176
235
  }
177
236
  }
178
237
 
179
238
  writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, "\t") + "\n");
239
+ }
180
240
 
181
- const eslintConfigPath = join(targetDir, "eslint.config.mjs");
182
- if (existsSync(eslintConfigPath)) {
183
- const lines = readFileSync(eslintConfigPath, "utf-8").split("\n");
184
- const filteredLines: string[] = [];
185
-
186
- let inRpcSection = false;
187
- for (let i = 0; i < lines.length; i++) {
188
- const line = lines[i];
189
- if (line === undefined) continue;
190
-
191
- if (line.includes("import rpcPlugin") && line.includes("@dyyz1993/eslint-plugin-rpc")) {
192
- continue;
193
- }
194
-
195
- if (line.includes("RPC") && line.includes("规范规则")) {
196
- inRpcSection = true;
197
- continue;
198
- }
199
-
200
- if (inRpcSection) {
201
- if (line.trim().startsWith("'rpc/") || line.trim().startsWith('"rpc/')) {
202
- continue;
203
- }
204
- inRpcSection = false;
205
- }
206
-
207
- if (line.includes("rpc: rpcPlugin")) {
208
- continue;
209
- }
241
+ /**
242
+ * Shared post-processing after raw template files land in the target project.
243
+ * Used by both `create` and `update --force`; skipping it used to leave
244
+ * `workspace:*` deps and `../shared` imports in user projects.
245
+ */
246
+ export function postProcessTemplate(targetDir: string, projectName: string): void {
247
+ copySharedModules(getRootDir(), targetDir, projectName);
248
+ cleanViteConfig(targetDir);
249
+ updatePackageJson(targetDir, projectName);
250
+ }
210
251
 
211
- filteredLines.push(line);
252
+ function precheckExternalTools(): void {
253
+ const missing: string[] = [];
254
+ for (const tool of ["git", "bun"]) {
255
+ try {
256
+ execFileSync(tool, ["--version"], { stdio: "pipe" });
257
+ } catch {
258
+ missing.push(tool);
212
259
  }
213
-
214
- let content = filteredLines.join("\n");
215
- content = content.replace(/\n\s+plugins:\s*\{\s*\},?\s*\n/g, "\n");
216
-
217
- writeFileSync(eslintConfigPath, content);
260
+ }
261
+ if (missing.length > 0) {
262
+ throw new Error(
263
+ `Required tools not found: ${missing.join(", ")}. ` +
264
+ `Install them before creating a project (bun: https://bun.sh).`
265
+ );
218
266
  }
219
267
  }
220
268
 
@@ -227,6 +275,8 @@ export async function copyTemplate(options: CopyOptions): Promise<void> {
227
275
  throw new Error(`Directory "${targetDir}" already exists.`);
228
276
  }
229
277
 
278
+ precheckExternalTools();
279
+
230
280
  const { identifier } = deriveNames(projectName);
231
281
 
232
282
  console.log(`Creating project: ${projectName}`);
@@ -237,37 +287,18 @@ export async function copyTemplate(options: CopyOptions): Promise<void> {
237
287
  copyAndReplace(templateDir, targetDir, projectName);
238
288
  if (isMonorepo) {
239
289
  copyTraeRules(localRoot, targetDir, projectName);
240
- copySharedModules(localRoot, targetDir, projectName);
241
- } else {
242
- const publishedShared = resolve(import.meta.dir, "..", "..", "templates", "shared");
243
- if (existsSync(publishedShared)) {
244
- copyAndReplace(publishedShared, join(targetDir, "shared"), projectName);
245
- for (const f of ["http-routes.ts", "logger.ts"]) {
246
- const p = join(targetDir, "shared", f);
247
- if (existsSync(p)) unlinkSync(p);
248
- }
249
- const tsconfigPath = join(targetDir, "tsconfig.json");
250
- if (existsSync(tsconfigPath)) {
251
- let tsconfig = readFileSync(tsconfigPath, "utf-8");
252
- tsconfig = tsconfig.replace(/"\.\.\/shared\/\*"/g, '"./shared/*"');
253
- tsconfig = tsconfig.replace(/"\.\.\/shared"/g, '"./shared"');
254
- writeFileSync(tsconfigPath, tsconfig);
255
- }
256
- cleanSharedViteConfig(join(targetDir, "shared"));
257
- }
258
290
  }
259
- cleanViteConfig(targetDir);
260
- updatePackageJson(targetDir, projectName);
291
+ postProcessTemplate(targetDir, projectName);
261
292
 
262
293
  console.log("Initializing git...");
263
- execSync("git init", { cwd: targetDir, stdio: "pipe" });
294
+ execFileSync("git", ["init"], { cwd: targetDir, stdio: "pipe" });
264
295
 
265
296
  console.log("Installing dependencies...");
266
- execSync("bun install", { cwd: targetDir, stdio: "inherit" });
297
+ execFileSync("bun", ["install"], { cwd: targetDir, stdio: "inherit" });
267
298
 
268
299
  console.log("Building browser bundle...");
269
300
  try {
270
- execSync("bun run build:browser", { cwd: targetDir, stdio: "pipe" });
301
+ execFileSync("bun", ["run", "build:browser"], { cwd: targetDir, stdio: "pipe" });
271
302
  } catch {
272
303
  console.log("(build:browser skipped - script not found)");
273
304
  }
@@ -284,7 +315,7 @@ export async function copyTemplate(options: CopyOptions): Promise<void> {
284
315
  hook(
285
316
  "pre-commit",
286
317
  `#!/bin/sh
287
- npx lint-staged
318
+ bunx lint-staged
288
319
 
289
320
  STAGED_TS=$(git diff --cached --name-only --diff-filter=ACMR | grep -c '\\.tsx\\?$' || true)
290
321
 
@@ -300,7 +331,7 @@ echo "✅ Pre-commit checks passed"
300
331
  hook(
301
332
  "commit-msg",
302
333
  `#!/bin/sh
303
- npx --no -- commitlint --edit "$1"
334
+ bunx commitlint --edit "$1"
304
335
  `
305
336
  );
306
337
 
@@ -311,7 +342,7 @@ echo "⏳ Linting..."
311
342
  bun run lint || { echo "❌ Lint failed."; exit 1; }
312
343
 
313
344
  echo "⏳ Checking lockfile sync..."
314
- git diff --name-only HEAD -- pnpm-lock.yaml | grep -q . && { echo "⚠️ pnpm-lock.yaml has uncommitted changes."; exit 1; }
345
+ git diff --name-only HEAD -- bun.lock | grep -q . && { echo "⚠️ bun.lock has uncommitted changes."; exit 1; }
315
346
 
316
347
  echo "✅ Pre-push checks passed (full tests run in CI). Pushing..."
317
348
  `
@@ -356,9 +387,9 @@ fi
356
387
  echo "⏳ Checking for dependency changes..."
357
388
  CHANGED=$(git diff HEAD@{1} --name-only HEAD)
358
389
 
359
- if echo "$CHANGED" | grep -q "pnpm-lock.yaml\\|package.json"; then
360
- echo "📦 Dependencies changed, running pnpm install..."
361
- pnpm install
390
+ if echo "$CHANGED" | grep -q "bun.lock\\|package.json"; then
391
+ echo "📦 Dependencies changed, running bun install..."
392
+ bun install
362
393
  fi
363
394
  `
364
395
  );
@@ -372,26 +403,31 @@ IS_BRANCH="$3"
372
403
 
373
404
  if [ "$IS_BRANCH" = "1" ]; then
374
405
  CHANGED=$(git diff --name-only "$PREV_HEAD" "$NEW_HEAD" 2>/dev/null)
375
- if echo "$CHANGED" | grep -q "pnpm-lock.yaml\\|package.json"; then
376
- echo "📦 Dependencies changed between branches, running pnpm install..."
377
- pnpm install
406
+ if echo "$CHANGED" | grep -q "bun.lock\\|package.json"; then
407
+ echo "📦 Dependencies changed between branches, running bun install..."
408
+ bun install
378
409
  fi
379
410
  fi
380
411
  `
381
412
  );
382
413
 
383
414
  console.log("Initializing husky...");
384
- execSync("pnpm run prepare", { cwd: targetDir, stdio: "pipe" });
415
+ try {
416
+ execFileSync("bun", ["run", "prepare"], { cwd: targetDir, stdio: "pipe" });
417
+ } catch {
418
+ console.warn("(husky init skipped - prepare script failed; git hooks not installed)");
419
+ }
385
420
 
386
- execSync("git add -A", { cwd: targetDir, stdio: "pipe" });
421
+ execFileSync("git", ["add", "-A"], { cwd: targetDir, stdio: "pipe" });
387
422
 
388
423
  try {
389
- execSync(`git commit --no-verify -m "feat: init ${projectName} from pi-agent-template"`, {
390
- cwd: targetDir,
391
- stdio: "pipe",
392
- });
424
+ execFileSync(
425
+ "git",
426
+ ["commit", "--no-verify", "-m", `feat: init ${projectName} from pi-agent-template`],
427
+ { cwd: targetDir, stdio: "pipe" }
428
+ );
393
429
  } catch {
394
- console.log("(git commit skipped - no files to commit)");
430
+ console.log("(git commit skipped - check git user.name / user.email config)");
395
431
  }
396
432
 
397
433
  console.log("");
@@ -27,6 +27,18 @@ const TEMPLATES: TemplateInfo[] = [
27
27
  available: true,
28
28
  dir: 'templates/agent',
29
29
  },
30
+ {
31
+ type: 'browser-agent',
32
+ description: 'Browser automation agent (AI-powered browser control + xbrowser + Desktop/Web)',
33
+ available: true,
34
+ dir: 'templates/browser-agent',
35
+ },
36
+ {
37
+ type: 'cowork',
38
+ description: 'Cowork task collaboration agent (Task orchestration + Progress tracking + Artifacts + Context management)',
39
+ available: true,
40
+ dir: 'templates/cowork',
41
+ },
30
42
  ];
31
43
 
32
44
  export function getTemplateInfo(type: string): TemplateInfo | undefined {
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ npx --no -- commitlint --edit "$1"
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ # Secret detection
5
+ if git diff --cached --name-only --diff-filter=ACM -z | xargs -0 grep -ilE '(password|secret|api.key|private.key|token|credential).*=.*[A-Za-z0-9]{16,}' 2>/dev/null; then
6
+ echo "⚠️ Potential secrets detected in staged files!"
7
+ echo "Please review and remove any sensitive data before committing."
8
+ exit 1
9
+ fi
10
+
11
+ npx lint-staged
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ echo "⏳ Running lint..."
5
+ pnpm run lint
6
+ echo "✅ Pre-push checks passed. Pushing..."
@@ -0,0 +1,15 @@
1
+ # pi-agent-app
2
+
3
+ ## 1.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`29d2171`](https://github.com/dyyz1993/pi-agent-template/commit/29d217148f6e0b496cc06716e11847b3e522e266)]:
8
+ - @dyyz1993/rpc-core@2.2.1
9
+
10
+ ## 1.0.1
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [[`29d2171`](https://github.com/dyyz1993/pi-agent-template/commit/29d217148f6e0b496cc06716e11847b3e522e266), [`724470a`](https://github.com/dyyz1993/pi-agent-template/commit/724470a784b48a66591e06299fab8ad72f023962)]:
15
+ - @dyyz1993/rpc-core@3.0.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-agent-app",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Coding agent template with Bash, Rules, and Todo management",
5
5
  "author": {
6
6
  "name": "xuyingzhou",
@@ -75,7 +75,7 @@
75
75
  "prepare": "husky"
76
76
  },
77
77
  "dependencies": {
78
- "@dyyz1993/rpc-core": "^1.2.0",
78
+ "@dyyz1993/rpc-core": "^2.2.1",
79
79
  "@tanstack/react-virtual": "^3.13.24",
80
80
  "electrobun": "1.18.1",
81
81
  "lucide-react": "^1.9.0",
@@ -91,7 +91,7 @@
91
91
  "zustand": "^5.0.12"
92
92
  },
93
93
  "devDependencies": {
94
- "@dyyz1993/eslint-plugin-rpc": "workspace:*",
94
+ "@dyyz1993/eslint-plugin-rpc": "^1.1.0",
95
95
  "@eslint/js": "^9.0.0",
96
96
  "@testing-library/jest-dom": "^6.9.1",
97
97
  "@testing-library/react": "^16.3.2",
@@ -1,11 +1,13 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { EventEmitter } from "events";
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { EventEmitter } from 'events';
3
+ import type { Server } from 'http';
4
+ import type { WebSocketServer } from 'ws';
3
5
 
4
- vi.mock("../../shared/register-all-handlers", () => ({
6
+ vi.mock('../../shared/register-all-handlers', () => ({
5
7
  registerAllHandlers: vi.fn(),
6
8
  }));
7
9
 
8
- vi.mock("../../shared/lib/logger", () => ({
10
+ vi.mock('../../shared/lib/logger', () => ({
9
11
  createLogger: () => ({
10
12
  info: vi.fn(),
11
13
  error: vi.fn(),
@@ -14,14 +16,22 @@ vi.mock("../../shared/lib/logger", () => ({
14
16
  }),
15
17
  }));
16
18
 
17
- const MOCK_AUTH_TOKEN = "test-secure-token-123456";
19
+ const MOCK_AUTH_TOKEN = 'test-secure-token-123456';
18
20
 
19
- function createMockWs() {
20
- const ws = new EventEmitter();
21
- (ws as any).readyState = 1;
22
- (ws as any).close = vi.fn();
23
- (ws as any).send = vi.fn();
24
- (ws as any).ping = vi.fn();
21
+ interface MockWs extends EventEmitter {
22
+ readyState: number;
23
+ close: ReturnType<typeof vi.fn>;
24
+ send: ReturnType<typeof vi.fn>;
25
+ ping: ReturnType<typeof vi.fn>;
26
+ }
27
+
28
+ function createMockWs(): MockWs {
29
+ const ws = Object.assign(new EventEmitter(), {
30
+ readyState: 1,
31
+ close: vi.fn(),
32
+ send: vi.fn(),
33
+ ping: vi.fn(),
34
+ }) as MockWs;
25
35
  return ws;
26
36
  }
27
37
 
@@ -29,19 +39,19 @@ function createMockReq(url: string, headers: Record<string, string> = {}) {
29
39
  return { url, headers };
30
40
  }
31
41
 
32
- describe("WS Handler Token 安全", () => {
33
- let _wssInstance: EventEmitter & { clients: Set<any> };
42
+ describe('WS Handler Token 安全', () => {
43
+ let _wssInstance: EventEmitter & { clients: Set<unknown> };
34
44
 
35
45
  beforeEach(async () => {
36
46
  vi.resetModules();
37
47
 
38
- const { WebSocketServer } = await import("ws");
48
+ const { WebSocketServer } = await import('ws');
39
49
  const _originalWSS = WebSocketServer;
40
50
 
41
- vi.doMock("ws", () => {
51
+ vi.doMock('ws', () => {
42
52
  class MockWSS extends EventEmitter {
43
- clients = new Set();
44
- constructor(_opts: any) {
53
+ clients = new Set<unknown>();
54
+ constructor(_opts: unknown) {
45
55
  super();
46
56
  // eslint-disable-next-line @typescript-eslint/no-this-alias
47
57
  _wssInstance = this;
@@ -52,67 +62,71 @@ describe("WS Handler Token 安全", () => {
52
62
  });
53
63
 
54
64
  async function setupTest() {
55
- const { createWsHandler } = await import("../ws-handler");
65
+ const { createWsHandler } = await import('../ws-handler');
56
66
  const httpServer = new EventEmitter();
57
- const wss = createWsHandler(httpServer as any, {
67
+ const wss = createWsHandler(httpServer as unknown as Server, {
58
68
  config: { port: 3100, authToken: MOCK_AUTH_TOKEN, maxUploadSize: 50 * 1024 * 1024 },
59
69
  });
60
70
  return wss;
61
71
  }
62
72
 
63
- function emitConnection(wss: any, ws: any, req: any) {
64
- wss.emit("connection", ws, req);
73
+ function emitConnection(
74
+ wss: WebSocketServer,
75
+ ws: MockWs,
76
+ req: { url: string; headers: Record<string, string> },
77
+ ) {
78
+ wss.emit('connection', ws, req);
65
79
  }
66
80
 
67
- it("无 token 应被拒绝 (4001)", async () => {
81
+ it('无 token 应被拒绝 (4001)', async () => {
68
82
  const wss = await setupTest();
69
83
  const ws = createMockWs();
70
- const req = createMockReq("/ws");
84
+ const req = createMockReq('/ws');
71
85
  emitConnection(wss, ws, req);
72
- expect((ws as any).close).toHaveBeenCalledWith(4001, "Unauthorized");
86
+ expect(ws.close).toHaveBeenCalledWith(4001, 'Unauthorized');
73
87
  });
74
88
 
75
- it("错误 token 应被拒绝 (4001)", async () => {
89
+ it('错误 token 应被拒绝 (4001)', async () => {
76
90
  const wss = await setupTest();
77
91
  const ws = createMockWs();
78
- const req = createMockReq("/ws?token=wrong-token");
92
+ const req = createMockReq('/ws?token=wrong-token');
79
93
  emitConnection(wss, ws, req);
80
- expect((ws as any).close).toHaveBeenCalledWith(4001, "Unauthorized");
94
+ expect(ws.close).toHaveBeenCalledWith(4001, 'Unauthorized');
81
95
  });
82
96
 
83
- it("正确 token 通过 URL query 应被接受", async () => {
97
+ it('正确 token 通过 URL query 应被接受', async () => {
84
98
  const wss = await setupTest();
85
99
  const ws = createMockWs();
86
100
  const req = createMockReq(`/ws?token=${MOCK_AUTH_TOKEN}`);
87
101
  emitConnection(wss, ws, req);
88
- expect((ws as any).close).not.toHaveBeenCalled();
102
+ expect(ws.close).not.toHaveBeenCalled();
89
103
  });
90
104
 
91
- it("正确 token 通过 Sec-WebSocket-Protocol header 应被接受", async () => {
105
+ it('正确 token 通过 Sec-WebSocket-Protocol header 应被接受', async () => {
92
106
  const wss = await setupTest();
93
107
  const ws = createMockWs();
94
- const req = createMockReq("/ws", {
95
- "sec-websocket-protocol": MOCK_AUTH_TOKEN,
108
+ const req = createMockReq('/ws', {
109
+ 'sec-websocket-protocol': MOCK_AUTH_TOKEN,
96
110
  });
97
111
  emitConnection(wss, ws, req);
98
- expect((ws as any).close).not.toHaveBeenCalled();
112
+ expect(ws.close).not.toHaveBeenCalled();
99
113
  });
100
114
 
101
- it("header 方式优先于 query 方式 (header 正确, query 错误)", async () => {
115
+ it('header 方式优先于 query 方式 (header 正确, query 错误)', async () => {
102
116
  const wss = await setupTest();
103
117
  const ws = createMockWs();
104
- const req = createMockReq("/ws?token=wrong-token", {
105
- "sec-websocket-protocol": MOCK_AUTH_TOKEN,
118
+ const req = createMockReq('/ws?token=wrong-token', {
119
+ 'sec-websocket-protocol': MOCK_AUTH_TOKEN,
106
120
  });
107
121
  emitConnection(wss, ws, req);
108
- expect((ws as any).close).not.toHaveBeenCalled();
122
+ expect(ws.close).not.toHaveBeenCalled();
109
123
  });
110
124
 
111
- it("token 验证使用 timingSafeEqual(长度不等返回 false)", async () => {
125
+ it('token 验证使用 timingSafeEqual(长度不等返回 false)', async () => {
112
126
  const wss = await setupTest();
113
127
  const ws = createMockWs();
114
128
  const req = createMockReq(`/ws?token=short`);
115
129
  emitConnection(wss, ws, req);
116
- expect((ws as any).close).toHaveBeenCalledWith(4001, "Unauthorized");
130
+ expect(ws.close).toHaveBeenCalledWith(4001, 'Unauthorized');
117
131
  });
118
132
  });
@@ -1,17 +1,19 @@
1
- import type { ReactNode } from "react";
2
- import { useState } from "react";
3
- import { useConnectionStore } from "./stores/use-connection-store";
4
- import { useBreakpointSync } from "./hooks/use-breakpoint";
5
- import { useRpcInit } from "./hooks/use-rpc-init";
6
- import { useSidebarResize } from "./hooks/use-sidebar-resize";
7
- import { AppLayout, type CenterTab } from "./components/layout/AppLayout";
8
- import { ErrorBoundary as _EB } from "./components/common/ErrorBoundary";
1
+ import type { ReactNode } from 'react';
2
+ import { useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { useConnectionStore } from './stores/use-connection-store';
5
+ import { useBreakpointSync } from './hooks/use-breakpoint';
6
+ import { useRpcInit } from './hooks/use-rpc-init';
7
+ import { useSidebarResize } from './hooks/use-sidebar-resize';
8
+ import { AppLayout, type CenterTab } from './components/layout/AppLayout';
9
+ import { ErrorBoundary as _EB } from './components/common/ErrorBoundary';
9
10
 
10
11
  const ErrorBoundary = _EB as unknown as React.FC<{ children: ReactNode }>;
11
12
 
12
13
  function App() {
14
+ const { t } = useTranslation();
13
15
  const ready = useConnectionStore((s) => s.ready);
14
- const [centerTab, setCenterTab] = useState<CenterTab>("chat");
16
+ const [centerTab, setCenterTab] = useState<CenterTab>('chat');
15
17
 
16
18
  useBreakpointSync();
17
19
  useRpcInit();
@@ -20,10 +22,10 @@ function App() {
20
22
 
21
23
  if (!ready) {
22
24
  return (
23
- <div className="h-screen bg-gray-900 flex items-center justify-center">
25
+ <div className="h-screen bg-[var(--color-bg-primary)] flex items-center justify-center">
24
26
  <div className="text-center">
25
- <div className="inline-block w-8 h-8 border-2 border-indigo-400 border-t-transparent rounded-full animate-spin mb-4" />
26
- <div className="text-gray-400 text-sm">Connecting to RPC server...</div>
27
+ <div className="inline-block w-8 h-8 border-2 border-[var(--color-text-accent)] border-t-transparent rounded-full animate-spin mb-4" />
28
+ <div className="text-[var(--color-text-tertiary)] text-sm">{t('app.connecting')}</div>
27
29
  </div>
28
30
  </div>
29
31
  );