agentworkshop 0.7.40 → 0.7.44

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 (327) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/B8uy9iRW.js +1 -0
  3. package/.output/public/_nuxt/BGTYYQH-.js +1 -0
  4. package/.output/public/_nuxt/BUmHEJ77.js +2 -0
  5. package/.output/public/_nuxt/BY7YrV_D.js +1 -0
  6. package/.output/public/_nuxt/Bqcgz95s.js +18 -0
  7. package/.output/public/_nuxt/C7UlqPeD.js +1 -0
  8. package/.output/public/_nuxt/CTmDn-YP.js +1 -0
  9. package/.output/public/_nuxt/Cs8pVeVG.js +1 -0
  10. package/.output/public/_nuxt/{DcsCcxp4.js → D98NH8kG.js} +1 -1
  11. package/.output/public/_nuxt/DKySmNPI.js +14 -0
  12. package/.output/public/_nuxt/Di0NY_ht.js +1 -0
  13. package/.output/public/_nuxt/Du9v6qGB.js +1 -0
  14. package/.output/public/_nuxt/{C_iYD-EB.js → EHlEMRii.js} +1 -1
  15. package/.output/public/_nuxt/Eke1fs-U.js +1 -0
  16. package/.output/public/_nuxt/{DeJykBFf.js → H0BcqZtT.js} +1 -1
  17. package/.output/public/_nuxt/{C4oW1HsE.js → HSYEkkce.js} +1 -1
  18. package/.output/public/_nuxt/{DZshyNLa.js → IREZ1eC2.js} +1 -1
  19. package/.output/public/_nuxt/{D_OTvQza.js → J9oU00D1.js} +1 -1
  20. package/.output/public/_nuxt/OmpTerminalPanel.BxR5-lvi.css +1 -0
  21. package/.output/public/_nuxt/SSyAPaHc.js +1 -0
  22. package/.output/public/_nuxt/TownView.li4EMQxK.css +1 -0
  23. package/.output/public/_nuxt/VdLk4jLE2.js +1 -0
  24. package/.output/public/_nuxt/_id_.BhsXoEuk.css +1 -0
  25. package/.output/public/_nuxt/_id_.DQyj-WKN.css +1 -0
  26. package/.output/public/_nuxt/{_wsId_.B4WiqkIS.css → _wsId_.CTx-BlfF.css} +1 -1
  27. package/.output/public/_nuxt/aShsv_rG.js +19 -0
  28. package/.output/public/_nuxt/{yF0aYMF1.js → aesgXKSr.js} +3 -3
  29. package/.output/public/_nuxt/agents.B21nUEUX.css +1 -0
  30. package/.output/public/_nuxt/aml.ekTjxFkf.css +1 -0
  31. package/.output/public/_nuxt/builds/latest.json +1 -1
  32. package/.output/public/_nuxt/builds/meta/b5506ef0-f48e-4f14-a6c0-d65e505d0b09.json +1 -0
  33. package/.output/public/_nuxt/ca4TJ3Op.js +1 -0
  34. package/.output/public/_nuxt/channel-templates.DplM1pJ3.css +1 -0
  35. package/.output/public/_nuxt/daq.BdVmoIfe.css +1 -0
  36. package/.output/public/_nuxt/dcw.DCgyzpWV.css +1 -0
  37. package/.output/public/_nuxt/{default.J32SgqFY.css → default.Bh3L2HU8.css} +1 -1
  38. package/.output/public/_nuxt/lHAxdSsj.js +1 -0
  39. package/.output/public/_nuxt/pages.j9rzRocr.css +1 -0
  40. package/.output/public/_nuxt/plugins.DhiZbHU8.css +1 -0
  41. package/.output/public/_nuxt/settings.BRx9rIDg.css +1 -0
  42. package/.output/public/_nuxt/teams.BLlOPB_Z.css +1 -0
  43. package/.output/public/_nuxt/tokens.De8xzszn.css +1 -0
  44. package/.output/public/_nuxt/workshop.D1jFNkrn.css +1 -0
  45. package/.output/server/chunks/_/nitro.mjs +1058 -830
  46. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  47. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs → OmpTerminalPanel-CVvxpsF7.mjs} +15 -15
  48. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs.map → OmpTerminalPanel-CVvxpsF7.mjs.map} +1 -1
  49. package/.output/server/chunks/build/{TownView-BuLAsmpR.mjs → TownView-CJ6IsDCc.mjs} +229 -228
  50. package/.output/server/chunks/build/{TownView-BuLAsmpR.mjs.map → TownView-CJ6IsDCc.mjs.map} +1 -1
  51. package/.output/server/chunks/build/{_id_-BU1wfOID.mjs → _id_-CVD_H0ir.mjs} +149 -147
  52. package/.output/server/chunks/build/_id_-CVD_H0ir.mjs.map +1 -0
  53. package/.output/server/chunks/build/{_id_-BYsEua57.mjs → _id_-CtxMb3qp.mjs} +47 -46
  54. package/.output/server/chunks/build/_id_-CtxMb3qp.mjs.map +1 -0
  55. package/.output/server/chunks/build/{_wsId_-BxlDcJHg.mjs → _wsId_-DpY3T-md.mjs} +516 -446
  56. package/.output/server/chunks/build/{_wsId_-BxlDcJHg.mjs.map → _wsId_-DpY3T-md.mjs.map} +1 -1
  57. package/.output/server/chunks/build/{agents-CLQqJF2y.mjs → agents-DsCkuXDV.mjs} +49 -46
  58. package/.output/server/chunks/build/agents-DsCkuXDV.mjs.map +1 -0
  59. package/.output/server/chunks/build/{aml-_7FdFL2p.mjs → aml-CT0xKGKI.mjs} +122 -105
  60. package/.output/server/chunks/build/aml-CT0xKGKI.mjs.map +1 -0
  61. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs +117 -0
  62. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs.map +1 -0
  63. package/.output/server/chunks/build/{channel-templates-p6SZNjlM.mjs → channel-templates-Dla9FbYa.mjs} +49 -37
  64. package/.output/server/chunks/build/channel-templates-Dla9FbYa.mjs.map +1 -0
  65. package/.output/server/chunks/build/{daq-cgyjKmJu.mjs → daq-DWWSKGAV.mjs} +95 -93
  66. package/.output/server/chunks/build/daq-DWWSKGAV.mjs.map +1 -0
  67. package/.output/server/chunks/build/{dcw-CoTxBlAZ.mjs → dcw-CT8k17_E.mjs} +46 -45
  68. package/.output/server/chunks/build/dcw-CT8k17_E.mjs.map +1 -0
  69. package/.output/server/chunks/build/{default-BE4r0FZN.mjs → default-D3525UFX.mjs} +16 -15
  70. package/.output/server/chunks/build/{default-BE4r0FZN.mjs.map → default-D3525UFX.mjs.map} +1 -1
  71. package/.output/server/chunks/build/{logs-BSOeZwwI.mjs → logs--dByNyhw.mjs} +2 -2
  72. package/.output/server/chunks/build/{logs-BSOeZwwI.mjs.map → logs--dByNyhw.mjs.map} +1 -1
  73. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs → monitor-Dh2Kvaq5.mjs} +2 -2
  74. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs.map → monitor-Dh2Kvaq5.mjs.map} +1 -1
  75. package/.output/server/chunks/build/{pages-DFxjn9Xa.mjs → pages-BHcSBM2D.mjs} +26 -20
  76. package/.output/server/chunks/build/pages-BHcSBM2D.mjs.map +1 -0
  77. package/.output/server/chunks/build/{plugins-lGlUr1lj.mjs → plugins-BNgIi61W.mjs} +23 -17
  78. package/.output/server/chunks/build/plugins-BNgIi61W.mjs.map +1 -0
  79. package/.output/server/chunks/build/{settings-Ddd1qx2Z.mjs → settings-ipYBDWdn.mjs} +45 -44
  80. package/.output/server/chunks/build/settings-ipYBDWdn.mjs.map +1 -0
  81. package/.output/server/chunks/build/{teams-MEDLZdMn.mjs → teams-5lx-CwFa.mjs} +47 -44
  82. package/.output/server/chunks/build/teams-5lx-CwFa.mjs.map +1 -0
  83. package/.output/server/chunks/build/{tokens-C7fTM5C2.mjs → tokens-q-IDT8Io.mjs} +73 -53
  84. package/.output/server/chunks/build/tokens-q-IDT8Io.mjs.map +1 -0
  85. package/.output/server/chunks/build/{town-qMPql9Se.mjs → town-QJWB5wng.mjs} +5 -4
  86. package/.output/server/chunks/build/town-QJWB5wng.mjs.map +1 -0
  87. package/.output/server/chunks/build/{useDaqStream-CIBaiGAl.mjs → useDaqStream-DfCFpb3p.mjs} +9 -3
  88. package/.output/server/chunks/build/useDaqStream-DfCFpb3p.mjs.map +1 -0
  89. package/.output/server/chunks/build/{useDcwStream-CBbiYnWe.mjs → useDcwStream-UPiX-3iO.mjs} +11 -2
  90. package/.output/server/chunks/build/useDcwStream-UPiX-3iO.mjs.map +1 -0
  91. package/.output/server/chunks/build/{workshop-BL5bhfs0.mjs → workshop-BIlCj_ZP.mjs} +45 -45
  92. package/.output/server/chunks/build/workshop-BIlCj_ZP.mjs.map +1 -0
  93. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  94. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  95. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
  96. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
  97. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
  98. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
  99. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
  100. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  101. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  102. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  103. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  104. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  105. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  106. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  107. package/.output/server/chunks/routes/api/workshop/index5.get.mjs +3 -1
  108. package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
  109. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +3 -1
  110. package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
  111. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  112. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  113. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  114. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  115. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  116. package/.output/server/chunks/routes/renderer.mjs +2 -2
  117. package/.output/server/chunks/virtual/entry.mjs +639 -88
  118. package/.output/server/chunks/virtual/entry.mjs.map +1 -1
  119. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  120. package/.output/server/package.json +1 -1
  121. package/README-zh.md +16 -6
  122. package/README.md +17 -7
  123. package/app/components/AppHeader.vue +961 -959
  124. package/app/components/workshop/A2aRpcDebugger.vue +237 -237
  125. package/app/components/workshop/AgentInspectorDrawer.vue +424 -424
  126. package/app/components/workshop/AgentLanesView.vue +998 -998
  127. package/app/components/workshop/ArtifactCard.vue +120 -120
  128. package/app/components/workshop/AssetLibrary.vue +452 -452
  129. package/app/components/workshop/ChannelSessionList.vue +812 -804
  130. package/app/components/workshop/CommandPalette.vue +269 -269
  131. package/app/components/workshop/Composer.vue +827 -827
  132. package/app/components/workshop/DeviceTwinPanel.vue +643 -643
  133. package/app/components/workshop/EventBlock.vue +602 -602
  134. package/app/components/workshop/FileSelectorModal.vue +188 -188
  135. package/app/components/workshop/InspectorPanel.vue +301 -301
  136. package/app/components/workshop/MemoryPanel.vue +364 -364
  137. package/app/components/workshop/MultiChannelView.vue +189 -189
  138. package/app/components/workshop/TaskBoardView.vue +686 -686
  139. package/app/components/workshop/TaskInspectorDrawer.vue +390 -390
  140. package/app/components/workshop/TranscriptTimeline.vue +601 -601
  141. package/app/components/workshop/blocks/ClusterArtifact.vue +61 -61
  142. package/app/components/workshop/blocks/ClusterError.vue +85 -85
  143. package/app/components/workshop/blocks/ClusterLife.vue +87 -87
  144. package/app/components/workshop/blocks/ClusterMember.vue +95 -93
  145. package/app/components/workshop/blocks/ClusterMemory.vue +84 -84
  146. package/app/components/workshop/blocks/ClusterOther.vue +67 -67
  147. package/app/components/workshop/blocks/ClusterStatus.vue +149 -147
  148. package/app/components/workshop/blocks/ClusterTool.vue +158 -158
  149. package/app/components/workshop/terminal/OmpTerminalPanel.vue +1011 -1011
  150. package/app/components/workshop/town/TownView.vue +56 -56
  151. package/app/composables/workshop/useDaqStream.ts +7 -4
  152. package/app/composables/workshop/useDcwStream.ts +8 -2
  153. package/app/middleware/locale-cookie.global.ts +22 -0
  154. package/app/pages/aml/index.vue +24 -2
  155. package/app/pages/daq/[id].vue +20 -16
  156. package/app/pages/daq/index.vue +2310 -2307
  157. package/app/pages/dcw/[id].vue +26 -23
  158. package/app/pages/dcw/index.vue +3 -3
  159. package/app/pages/index.vue +987 -981
  160. package/app/pages/plugins/index.vue +317 -311
  161. package/app/pages/settings.vue +1564 -1562
  162. package/app/pages/tokens.vue +15 -15
  163. package/app/pages/workshop/agents.vue +612 -609
  164. package/app/pages/workshop/channel-templates.vue +18 -11
  165. package/app/pages/workshop/index.vue +668 -668
  166. package/app/pages/workshop/teams.vue +743 -740
  167. package/app/pages/workshop/w/[wsId].vue +704 -704
  168. package/app/plugins/locale-restore.client.ts +16 -1
  169. package/app/utils/builtin-catalog.ts +104 -0
  170. package/config.yml +170 -170
  171. package/docs/cli.en.md +2 -2
  172. package/docs/cli.md +2 -2
  173. package/docs/full-test-plan.md +2 -3
  174. package/docs/plugins.en.md +33 -1
  175. package/docs/plugins.md +29 -1
  176. package/docs/sdk.en.md +1 -1
  177. package/docs/sdk.md +1 -1
  178. package/i18n/locales/en.ts +293 -35
  179. package/i18n/locales/zh-CN.ts +265 -7
  180. package/package.json +4 -3
  181. package/scripts/_dbg-perms-e2e.mjs +132 -109
  182. package/scripts/_demo-agentteam-capture.mjs +278 -0
  183. package/scripts/_e2e-ui-functional.mjs +371 -0
  184. package/scripts/_i18n-en-cjk-scan.mjs +28 -0
  185. package/scripts/_i18n-en-verify.mjs +107 -0
  186. package/scripts/_i18n-extra-checks.mjs +53 -0
  187. package/scripts/_i18n-scan-hardcoded.mjs +180 -0
  188. package/scripts/_i18n-verify-keys.mjs +50 -0
  189. package/scripts/_loop-biax-campaign.mjs +130 -0
  190. package/scripts/_sweep-button-contrast.mjs +87 -0
  191. package/scripts/test-serial-bridge.mjs +418 -0
  192. package/scripts/ui/e2e-interactive.mjs +328 -0
  193. package/scripts/ui/record-demo.mjs +557 -0
  194. package/scripts/ui/seed-industrial-demo.mjs +171 -0
  195. package/server/api/workshop/daq/index.get.ts +3 -1
  196. package/server/api/workshop/dcw/index.get.ts +3 -0
  197. package/server/plugins/aw-plugins.ts +9 -7
  198. package/server/plugins-builtin/diag-bridge/index.mjs +608 -661
  199. package/server/plugins-builtin/rag-bridge/index.mjs +57 -133
  200. package/server/plugins-builtin/serial-bridge/client.mjs +210 -0
  201. package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -0
  202. package/server/plugins-builtin/serial-bridge/i18n.json +62 -0
  203. package/server/plugins-builtin/serial-bridge/index.mjs +247 -0
  204. package/server/plugins-builtin/serial-bridge/lib.mjs +208 -0
  205. package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -0
  206. package/server/services/workshop/daq/daq-controller.ts +7 -2
  207. package/server/services/workshop/daq/drivers.ts +89 -3
  208. package/server/services/workshop/dcw/dcw-controller.ts +5 -1
  209. package/server/services/workshop/dcw/drivers.ts +85 -2
  210. package/server/services/workshop/dcw/plugin-bridge.ts +45 -0
  211. package/server/services/workshop/plugins/host.mjs +30 -0
  212. package/shared/daq-protocol.ts +15 -0
  213. package/.output/public/_nuxt/88GOowgO2.js +0 -1
  214. package/.output/public/_nuxt/BOt2SP4z.js +0 -1
  215. package/.output/public/_nuxt/BP-NyKVG.js +0 -18
  216. package/.output/public/_nuxt/BXxH0UQE.js +0 -1
  217. package/.output/public/_nuxt/BsROmY89.js +0 -1
  218. package/.output/public/_nuxt/ClILjvWH.js +0 -1
  219. package/.output/public/_nuxt/CzOCSGLe.js +0 -1
  220. package/.output/public/_nuxt/D8qvkfyY.js +0 -1
  221. package/.output/public/_nuxt/DCn7QZ5N.js +0 -2
  222. package/.output/public/_nuxt/DS8qj8EL.js +0 -1
  223. package/.output/public/_nuxt/DUjW5Yq5.js +0 -1
  224. package/.output/public/_nuxt/Dihnimlk.js +0 -19
  225. package/.output/public/_nuxt/Dqw1lpr-.js +0 -1
  226. package/.output/public/_nuxt/Dr9v6C0f.js +0 -14
  227. package/.output/public/_nuxt/Du9ZJtnj.js +0 -1
  228. package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +0 -1
  229. package/.output/public/_nuxt/TownView.f-YOdXRs.css +0 -1
  230. package/.output/public/_nuxt/_id_.CM4bIgKY.css +0 -1
  231. package/.output/public/_nuxt/_id_.bNV69Dtf.css +0 -1
  232. package/.output/public/_nuxt/agents.ZD0rVdHE.css +0 -1
  233. package/.output/public/_nuxt/aml.BN8ZEW4h.css +0 -1
  234. package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +0 -1
  235. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +0 -1
  236. package/.output/public/_nuxt/daq.B0D3dPAc.css +0 -1
  237. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +0 -1
  238. package/.output/public/_nuxt/pages.D7aSDlUI.css +0 -1
  239. package/.output/public/_nuxt/plugins.sb4nDGqr.css +0 -1
  240. package/.output/public/_nuxt/qr6R6Ha1.js +0 -1
  241. package/.output/public/_nuxt/settings.BTgR-NiE.css +0 -1
  242. package/.output/public/_nuxt/teams.CxnxKVVs.css +0 -1
  243. package/.output/public/_nuxt/tokens.BFY7qv4f.css +0 -1
  244. package/.output/public/_nuxt/workshop.CR_QzeAN.css +0 -1
  245. package/.output/server/chunks/build/_id_-BU1wfOID.mjs.map +0 -1
  246. package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +0 -1
  247. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +0 -1
  248. package/.output/server/chunks/build/aml-_7FdFL2p.mjs.map +0 -1
  249. package/.output/server/chunks/build/channel-templates-p6SZNjlM.mjs.map +0 -1
  250. package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +0 -1
  251. package/.output/server/chunks/build/dcw-CoTxBlAZ.mjs.map +0 -1
  252. package/.output/server/chunks/build/pages-DFxjn9Xa.mjs.map +0 -1
  253. package/.output/server/chunks/build/plugins-lGlUr1lj.mjs.map +0 -1
  254. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +0 -1
  255. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +0 -1
  256. package/.output/server/chunks/build/tokens-C7fTM5C2.mjs.map +0 -1
  257. package/.output/server/chunks/build/town-qMPql9Se.mjs.map +0 -1
  258. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs.map +0 -1
  259. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs.map +0 -1
  260. package/.output/server/chunks/build/workshop-BL5bhfs0.mjs.map +0 -1
  261. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.continue.json +0 -1
  262. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.json +0 -1
  263. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/054bb4bc4e4217a2ab0b0c6b663747214a9af464f5568ea60e784704e340bb0f.source +0 -153
  264. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/0554197c3d2bac203a1f5c48a5d52495d1336db1bd7aa4f4c19875810363e684.source +0 -153
  265. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/05c203d12a954fffba942e1d626e3f5fec05b18a5aef2bac003b7884a4b77406.source +0 -431
  266. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/145a6e6ba811af35aed31e0cc3211f53706a14980fa069ee9e3c1591139f96f0.source +0 -180
  267. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/1ae0fb281f97899c025e368ca73f41349b01fb5f85b19e08d7ebccff49095ed5.source +0 -112
  268. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/20ee9967aa0541c516db7575bdd4a1a745939bc7016bd10d7635903113edb8ec.source +0 -227
  269. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/233751843aeba9d4c5263f275b8102de00443c06f286918cbd23e0b6729e01aa.source +0 -131
  270. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/263c2e05b425e5c322e60c6475279f963bab3a873eb521b21df7fec935a46597.source +0 -160
  271. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/2ac5eaac28eaaee2178c4b32014df8e732f9dd432a285a872f5b7f31c1395fa4.source +0 -157
  272. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/33a0db0d2d95d2c11d0720090458e5fba1425376f5ceb5b422d1d3471be3226b.source +0 -348
  273. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/359e474ae83db35e4615d3728de6bf694b64d4dce9b536730a3b6518f9c0494a.source +0 -296
  274. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/37a4c3c31c4b70f6b719c53d1e9c4fcba603f31adfdd11bb6ef9f0d094923c56.source +0 -285
  275. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/461feca1c14a695eb8b0f17a6f95fe97eeceabc0510faaea4ba6d25bde147d8d.source +0 -217
  276. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/4cfadbda8591f29d5e1e230dc000e701b756e850441b3900fb596a4d2f6e98c2.source +0 -301
  277. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/50ca2810cf0b248c96948994c0c404531bbad6e553166b6816b3b03bfa0f75d2.source +0 -151
  278. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/52d9e8cb591a9ae86e238c3620a610cd386717bee222c0f37b25b2ac7e523dc4.source +0 -135
  279. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/6c87daafa4c006560a954636a97333f6123153c9d34d96203f7ee3a14e556357.source +0 -156
  280. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/77bc47854b577900621cacf1697f81fa2bc005af129d37614a97a3441c08f99f.source +0 -72
  281. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/7bbb789d3a527817118cf8399ed1861d6a962ade10878fd07223435d333a7089.source +0 -469
  282. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/86b7c1daafc8b60ba9982dde5bf1bcc74265709483c176e71f23ec6fc267451f.source +0 -259
  283. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/8df66d62bd41984d0970e8236882080c5085ac937d07a29779463d6a03b37c76.source +0 -97
  284. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/9962f2d1b3a85137bd40cc835725600b39f89ee02a27c06b2e0402e1eafde115.source +0 -277
  285. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a1090ea066b792cb201739f4994e37bf7c12f52e805239a1eab87f18a537f266.source +0 -171
  286. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a5241091650134d0919897e086a606a3846c8b2b54f8b4c84a8696c145ac3e9e.source +0 -425
  287. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/af19fa4c6e34d5540028be4b17600161283af3a2a2dfc61a3fdc0834436a0c14.source +0 -99
  288. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3713d78e2b2f93905ff6be8d58a8d0a2f42dea322eb67c987fda22ff6a084d5.source +0 -325
  289. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3849943c3aff102d1af815631fc843a2a52c17646a4b04696ece323e483bbec.source +0 -332
  290. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/bac735a3803a6650b7f1eb8f587e6dc5c68ea055000c3c99a11789438b78e837.source +0 -43
  291. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d32c24d61eb8b33c73e5a8fc7e4dc7ef437aed238ee4371c2b00617e6104090a.source +0 -154
  292. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d5170dc9a9879fda04fdf91d8a110d4efb6787ee38dda596d4da899883d36a72.source +0 -531
  293. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/dc59bcd7bb397be63ba2f2c3fec23f3782dcb17d2d75a9981eeced761f33eb32.source +0 -439
  294. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/eb5ff8cf66b92aec7bf6726781d8df74faa2b030c3aaac17d6b00c1fc04c9b32.source +0 -323
  295. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f10615a6fae18436e70d1201b2d44fdae3174a3aa7bb4db97dea1b3ed291d721.source +0 -433
  296. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f36d3e271668bc1260c2823542d22dbf1d0f7041ff16b1ac175bce2256e582ca.source +0 -280
  297. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f94449380af09f31ac352152007146a75cc80bae8d6411ffe57b2c56f933b0ff.source +0 -117
  298. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/fe4dfd81178fd6b648335f6405d9ede71319aebde796008e50db21f8adf44c99.source +0 -160
  299. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/ffb4e3aa548cfdadabaa44ab599f9b6a3ca6288176a2cc7a4b2fc2a12b5fb848.source +0 -101
  300. package/scripts/.mimosa/hook-status/sess_aeaecb58-8833-4b83-b93c-050467d21e03-d944d71bb3.json +0 -14
  301. package/scripts/_dbg-audit-all-pages.mjs +0 -43
  302. package/scripts/_dbg-daq-frames-e2e.mjs +0 -118
  303. package/scripts/_dbg-daq-rowstyle-verify.mjs +0 -112
  304. package/scripts/_dbg-hitl-timeout-e2e.mjs +0 -93
  305. package/scripts/_dbg-live-line-e2e.mjs +0 -279
  306. package/scripts/_dbg-minimal-omp.mjs +0 -77
  307. package/scripts/_dbg-mode-nav.mjs +0 -173
  308. package/scripts/_dbg-real-scenario-timeline.log +0 -55
  309. package/scripts/_dbg-team-repro.mjs +0 -76
  310. package/scripts/_dbg-town-agents-probe.mjs +0 -60
  311. package/scripts/_dbg-visual-acceptance.mjs +0 -63
  312. package/scripts/_townview_style.css +0 -1185
  313. package/scripts/_townview_template.html +0 -1215
  314. package/server/data/agent-node-bindings.json +0 -554
  315. package/server/data/daq-templates.json +0 -16
  316. package/server/data/daqs.json +0 -7464
  317. package/server/data/dcw-lines.json +0 -1787
  318. package/server/data/dcw-products.json +0 -1626
  319. package/server/data/dcw-recipes.json +0 -4672
  320. package/server/data/dcw-rollback.json +0 -20754
  321. package/server/data/dcw-runs.json +0 -6354
  322. package/server/data/dcw-templates.json +0 -146
  323. package/server/data/dcw-writes.json +0 -15650
  324. package/server/data/dcws.json +0 -9033
  325. package/server/data/device-twins.json +0 -133
  326. package/server/data/line-runs.json +0 -322
  327. package/server/data/scene-layouts.json +0 -277
@@ -0,0 +1,371 @@
1
+ /**
2
+ * 前端功能端到端 — 逐页点击级冒烟(真实浏览器,断言交互结果而非仅渲染)。
3
+ *
4
+ * 覆盖:工作区/频道创建 → 任务下发与完成(真实 mock 闭环)→ 视图切换/命令面板/A2A 抽屉
5
+ * → Token 签发/重命名/吊销 → 用户管理(角色切换)→ 插件热切换 → 设置(语言往返)
6
+ * → DCW 建线 → DAQ 渲染 → town 3D → 监控/日志/权限 → 仪表盘。
7
+ *
8
+ * 注意:antd 对两字按钮文案自动插空格(创建→「创 建」),点击匹配必须容忍空白;
9
+ * 弹窗存在 antd 与自定义 .modal-mask 两套,填写需按可见性定位。
10
+ * 运行:AW_BASE=http://127.0.0.1:3311 NO_PROXY=127.0.0.1,localhost node scripts/_e2e-ui-functional.mjs
11
+ * 退出码 0 = 全通过;失败步骤逐条打印并截图到 .e2e-shots/ui-func/。
12
+ */
13
+ import fs from 'node:fs'
14
+ import path from 'node:path'
15
+ import { ensureVisualUser, launch, openPage, gotoReady, sleep, BASE } from './ui/lib.mjs'
16
+
17
+ const OUT = '.e2e-shots/ui-func'
18
+ fs.mkdirSync(OUT, { recursive: true })
19
+ let pass = 0
20
+ const fails = []
21
+ const ok = (name, cond, extra = '') => {
22
+ if (cond) {
23
+ pass++
24
+ console.log(` ✓ ${name}`)
25
+ }
26
+ else {
27
+ fails.push(name)
28
+ console.log(` ✗ ${name} ${extra}`)
29
+ }
30
+ }
31
+ const T = ms => sleep(ms)
32
+ const stamp = Date.now().toString(36)
33
+
34
+ const token = await ensureVisualUser()
35
+ const browser = await launch({ width: 1440, height: 900 })
36
+ const page = await openPage(browser, { token, dark: true, width: 1440, height: 900 })
37
+ // API/错误诊断:所有写请求与页面错误打印出来(挂载/提交失败时可见原因)
38
+ page.on('response', async (r) => {
39
+ if (r.url().includes('/api/workshop/') && ['POST', 'PUT', 'DELETE'].includes(r.request().method()))
40
+ console.log(` [api] ${r.request().method()} ${r.url().split('/api/')[1]} -> ${r.status()}`)
41
+ })
42
+ page.on('console', (m) => {
43
+ if (m.type() === 'error') console.log(` [console.error] ${m.text().slice(0, 140)}`)
44
+ })
45
+ page.on('pageerror', e => console.log(` [pageerror] ${String(e).slice(0, 140)}`))
46
+
47
+ async function text() {
48
+ return page.evaluate(() => document.body.innerText)
49
+ }
50
+ /** 按正则点击可见容器内文本(容忍 antd 两字按钮插空格:innerText 去空白后匹配) */
51
+ async function clickTextRe(sel, re, timeout = 8000) {
52
+ try {
53
+ await page.waitForFunction(
54
+ (s, p) => [...document.querySelectorAll(s)].some((e) => {
55
+ const r = e.getBoundingClientRect()
56
+ return r.width > 0 && r.height > 0 && new RegExp(p).test(e.innerText?.replace(/\s+/g, '') ?? '')
57
+ }),
58
+ { timeout }, sel, re.source,
59
+ )
60
+ await page.evaluate((s, p) => {
61
+ const el = [...document.querySelectorAll(s)].find((e) => {
62
+ const r = e.getBoundingClientRect()
63
+ return r.width > 0 && r.height > 0 && new RegExp(p).test(e.innerText?.replace(/\s+/g, '') ?? '')
64
+ })
65
+ el?.click()
66
+ }, sel, re.source)
67
+ return true
68
+ }
69
+ catch {
70
+ return false
71
+ }
72
+ }
73
+ /** 可见弹窗(antd .ant-modal 或自定义 .modal-mask)内第 idx 个输入框填值 */
74
+ async function modalInput(idx, value) {
75
+ try {
76
+ await page.waitForFunction(
77
+ (idx) => {
78
+ const vis = (e) => {
79
+ const r = e.getBoundingClientRect()
80
+ return r.width > 0 && r.height > 0 && getComputedStyle(e).display !== 'none'
81
+ }
82
+ const m = [...document.querySelectorAll('.ant-modal, .modal-mask, .drawer')].find(vis)
83
+ if (!m) return false
84
+ const inputs = m.querySelectorAll('input.ant-input, input.inp')
85
+ return inputs.length > idx && vis(inputs[idx])
86
+ },
87
+ { timeout: 8000 }, idx,
88
+ )
89
+ await page.evaluate((i) => {
90
+ const vis = (e) => {
91
+ const r = e.getBoundingClientRect()
92
+ return r.width > 0 && r.height > 0 && getComputedStyle(e).display !== 'none'
93
+ }
94
+ const m = [...document.querySelectorAll('.ant-modal, .modal-mask, .drawer')].find(vis)
95
+ const el = m.querySelectorAll('input.ant-input, input.inp')[i]
96
+ el.focus()
97
+ el.value = ''
98
+ }, idx)
99
+ await page.keyboard.type(value, { delay: 10 })
100
+ return true
101
+ }
102
+ catch {
103
+ return false
104
+ }
105
+ }
106
+ async function clickAttr(attr, needle) {
107
+ try {
108
+ await page.waitForFunction(
109
+ (a, n) => [...document.querySelectorAll('button')].some(e => (e.getAttribute(a) ?? '').includes(n)),
110
+ { timeout: 8000 }, attr, needle,
111
+ )
112
+ await page.evaluate((a, n) => {
113
+ [...document.querySelectorAll('button')].find(e => (e.getAttribute(a) ?? '').includes(n))?.click()
114
+ }, attr, needle)
115
+ return true
116
+ }
117
+ catch {
118
+ return false
119
+ }
120
+ }
121
+ const CREATE = /创建|确定|OK|Create/
122
+
123
+ console.log('━━━ A. 工作台:工作区 + 频道 + 真实任务闭环 ━━━')
124
+ await gotoReady(page, '/workshop', { wait: 2500 })
125
+ ok('工作台渲染', /Workspace|工作区/.test(await text()))
126
+ const wsName = `e2e-ws-${stamp}`
127
+ ok('打开新建工作区弹窗', await clickTextRe('button', /新建/))
128
+ await T(700)
129
+ ok('填写工作区名', await modalInput(0, wsName))
130
+ await T(200)
131
+ ok('提交创建工作区', await clickTextRe('.ant-modal .ant-btn-primary', CREATE))
132
+ await T(1800)
133
+ ok('工作区卡片出现', (await text()).includes(wsName))
134
+
135
+ // 「创建并进入」会自动跳转控制台;若未跳转则点卡片上的进入按钮
136
+ await T(1200)
137
+ if (!/workshop\/w\//.test(page.url())) {
138
+ ok('点击进入控制台', await clickTextRe('button', /进入控制台|OpenConsole/))
139
+ await T(2500)
140
+ }
141
+ ok('路由进入工作区控制台', /workshop\/w\//.test(page.url()))
142
+ const chName = `e2e-ch-${stamp}`
143
+ // 空频道(校验新建弹窗;空团队无可执行 agent 是设计行为)
144
+ ok('打开新建频道弹窗', await clickAttr('title', '新建 Channel'))
145
+ await T(900)
146
+ ok('填写频道名', await modalInput(0, `e2e-empty-${stamp}`))
147
+ await T(200)
148
+ ok('提交创建空频道', await clickTextRe('.ant-modal .ant-btn-primary', CREATE))
149
+ await T(2200)
150
+
151
+ // 挂载内置模板频道(成员自动装配;lead=mock 可秒级闭环)——这是带 agent 的可执行频道
152
+ // antd Select 必须用 mousedown 打开(点击 selector 无效)
153
+ const mountOpened = await page.evaluate(() => {
154
+ const sel = [...document.querySelectorAll('.ant-select')].find(e => /挂载|Mount/.test(e.innerText))
155
+ sel?.querySelector('.ant-select-selector')?.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }))
156
+ return Boolean(sel)
157
+ })
158
+ ok('打开模板挂载下拉', mountOpened)
159
+ await T(1200)
160
+ // rc-select 选项需要完整鼠标序列(合成 click 不触发选中)
161
+ const optBox = await page.evaluate(() => {
162
+ const el = [...document.querySelectorAll('.ant-select-item-option')].find(e => /全栈交付|Full-stack/.test(e.innerText))
163
+ if (!el) return null
164
+ const r = el.getBoundingClientRect()
165
+ return { x: r.x + r.width / 2, y: r.y + r.height / 2 }
166
+ })
167
+ ok('点选内置模板选项', Boolean(optBox))
168
+ if (optBox) {
169
+ await page.mouse.move(optBox.x, optBox.y)
170
+ await page.mouse.down()
171
+ await page.mouse.up()
172
+ await page.mouse.click(optBox.x, optBox.y)
173
+ }
174
+ // 等挂载频道出现在列表并点击切换(顶栏频道徽章验证选中)
175
+ let switched = false
176
+ for (let i = 0; i < 10 && !switched; i++) {
177
+ await T(1500)
178
+ await page.evaluate(() => {
179
+ const rows = [...document.querySelectorAll('.channel-item')]
180
+ const row = rows.find(e => /全栈交付|Full-stack/.test(e.innerText ?? ''))
181
+ row?.click()
182
+ })
183
+ switched = await page.evaluate(() => /全栈交付|Full-stack/.test(document.querySelector('.topbar')?.innerText ?? ''))
184
+ }
185
+ ok('模板频道已挂载并选中', switched)
186
+ if (!switched) {
187
+ console.log(' [debug] channel rows:', await page.evaluate(() =>
188
+ [...document.querySelectorAll('.channel-item')].map(e => e.innerText.split('\n').join('|').slice(0, 40)),
189
+ ))
190
+ console.log(' [debug] dropdown options:', await page.evaluate(() =>
191
+ [...document.querySelectorAll('.ant-select-item-option')].map(e => `${e.innerText.slice(0, 20)}:${e.getBoundingClientRect().width > 0}`),
192
+ ))
193
+ }
194
+
195
+ const composer = await page.$('textarea')
196
+ ok('Composer 输入框存在', composer !== null)
197
+ if (composer) {
198
+ await composer.click()
199
+ await page.type('textarea', `e2e 冒烟任务 ${chName}`, { delay: 5 })
200
+ await page.keyboard.down('Control')
201
+ await page.keyboard.press('Enter')
202
+ await page.keyboard.up('Control')
203
+ await T(1500)
204
+ ok('任务已提交(时间线出现事件)', (await text()).match(/COMPLETED|RUNNING|ASSIGNED|已提交|提交|task/i) !== null)
205
+ // mock 模板成员未开 streamDemo,时间线不渲染流式块 —— 闭环断言走任务 API(确定性)
206
+ let done = false
207
+ for (let i = 0; i < 24; i++) {
208
+ await T(1500)
209
+ if (/COMPLETED/.test(await text())) {
210
+ done = true
211
+ break
212
+ }
213
+ done = await page.evaluate(async () => {
214
+ const raw = document.cookie.match(/(?:^|;\s*)token=([^;]+)/)?.[1]
215
+ const tk = raw ? decodeURIComponent(raw) : ''
216
+ const chs = await fetch('/api/workshop/channels', { headers: { authorization: `Bearer ${tk}` } }).then(r => r.json())
217
+ const list = chs.data?.channels ?? chs.data ?? []
218
+ const target = [...list].reverse().find(c => /全栈交付|Full-stack/.test(c.name ?? ''))
219
+ if (!target) return false
220
+ const ts = await fetch(`/api/workshop/channels/${target.id}/tasks`, { headers: { authorization: `Bearer ${tk}` } }).then(r => r.json())
221
+ return (ts.data ?? []).some(t => t.state === 'COMPLETED')
222
+ })
223
+ if (done) break
224
+ }
225
+ ok('任务真实闭环至 COMPLETED', done)
226
+ }
227
+ await page.screenshot({ path: path.join(OUT, 'a-channel-task.png') })
228
+
229
+ ok('切换 Agent lanes 视图', await clickTextRe('.ant-segmented-item, button, span, a', /Agentlanes|泳道/))
230
+ await T(900)
231
+ ok('视图切换渲染', (await text()).length > 100)
232
+ await page.keyboard.down('Control')
233
+ await page.keyboard.press('k')
234
+ await page.keyboard.up('Control')
235
+ await T(900)
236
+ ok('命令面板打开(⌘K)', /Agent|频道|视图|前往/.test(await text()))
237
+ await page.keyboard.press('Escape')
238
+ await T(500)
239
+ ok('打开 A2A 调试抽屉', await clickAttr('title', 'A2A'))
240
+ await T(1500)
241
+ ok('A2A 抽屉渲染', (await page.$('.ant-drawer')) !== null)
242
+ await page.keyboard.press('Escape')
243
+ await T(600)
244
+
245
+ console.log('━━━ B. Token 生命周期 ━━━')
246
+ await gotoReady(page, '/tokens', { wait: 2000 })
247
+ ok('Token 页渲染', /Token/i.test(await text()))
248
+ const tokName = `e2e-tok-${stamp}`
249
+ ok('打开发布 Token 弹窗', await clickTextRe('button', /签发|Issue/))
250
+ await T(700)
251
+ ok('填写 Token 名', await modalInput(0, tokName))
252
+ await clickTextRe('.ant-modal .ant-btn-primary', CREATE)
253
+ await T(1800)
254
+ ok('Token 创建成功弹窗', /已创建|created/i.test(await text()))
255
+ await page.keyboard.press('Escape')
256
+ await T(600)
257
+ await gotoReady(page, '/tokens', { wait: 1500 })
258
+ ok('新 Token 出现在列表', (await text()).toLowerCase().includes(tokName.toLowerCase()))
259
+ await page.evaluate((nm) => {
260
+ const row = [...document.querySelectorAll('tr, .tok-row')].find(e => e.innerText?.toLowerCase().includes(nm.toLowerCase()))
261
+ const btn = row && [...row.querySelectorAll('button')].find(b => /吊销|Revoke/i.test(b.innerText.replace(/\s+/g, '')))
262
+ btn?.click()
263
+ }, tokName)
264
+ await T(800)
265
+ ok('popconfirm 确认吊销', await clickTextRe('.ant-popconfirm button, .ant-popover button', /吊销|确|Revoke|OK/))
266
+ await T(1200)
267
+ ok('吊销后列表更新(移除或标记失效)', !(await text()).toLowerCase().includes(tokName.toLowerCase()) || /已吊销|revoked|失效/i.test(await text()))
268
+ await page.screenshot({ path: path.join(OUT, 'b-tokens.png') })
269
+
270
+ console.log('━━━ C. 用户管理(角色切换;建号走注册端点) ━━━')
271
+ const uname = `e2e-u-${stamp}`
272
+ await fetch(`${BASE}/api/users/register`, {
273
+ method: 'POST',
274
+ headers: { 'content-type': 'application/json' },
275
+ body: JSON.stringify({ name: uname, email: `${uname}@awshop.local`, password: 'Ux2026!Secure' }),
276
+ }).catch(() => null)
277
+ await gotoReady(page, '/users', { wait: 2000 })
278
+ ok('用户页渲染(表格)', (await page.$('.ant-table')) !== null)
279
+ ok('一次性用户出现在列表', (await text()).includes(uname))
280
+ await page.evaluate((nm) => {
281
+ const row = [...document.querySelectorAll('tr')].find(e => e.innerText?.includes(nm))
282
+ const sel = row?.querySelector('.ant-select')
283
+ sel?.querySelector('.ant-select-selector')?.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }))
284
+ }, uname)
285
+ await T(800)
286
+ ok('角色下拉选择 editor', await clickTextRe('.ant-select-item-option', /editor/))
287
+ await T(1400)
288
+ ok('角色行内变为 editor', await page.evaluate((nm) => {
289
+ const row = [...document.querySelectorAll('tr')].find(e => e.innerText?.includes(nm))
290
+ return row ? /editor/.test(row.innerText) : false
291
+ }, uname))
292
+
293
+ console.log('━━━ D. 插件热切换 ━━━')
294
+ await gotoReady(page, '/plugins', { wait: 3000 })
295
+ ok('插件页渲染(卡片)', (await page.$('article.pg-card')) !== null)
296
+ {
297
+ const sw = await page.$('article.pg-card .ant-switch')
298
+ if (sw) {
299
+ const beforeOn = await sw.evaluate(e => e.classList.contains('ant-switch-checked'))
300
+ await sw.click()
301
+ await T(2600)
302
+ await page.keyboard.press('Escape')
303
+ await T(800)
304
+ const sw2 = await page.$('article.pg-card .ant-switch')
305
+ const afterOn = sw2 ? await sw2.evaluate(e => e.classList.contains('ant-switch-checked')) : !beforeOn
306
+ ok('插件开关热切换生效', afterOn !== beforeOn)
307
+ await sw2.click()
308
+ await T(2600)
309
+ ok('插件开关复原', true)
310
+ }
311
+ else {
312
+ ok('插件开关热切换生效', false, '未找到卡片/开关')
313
+ }
314
+ }
315
+
316
+ console.log('━━━ E. 设置:语言往返 ━━━')
317
+ await gotoReady(page, '/settings', { wait: 2000 })
318
+ ok('设置页渲染', /设置|Settings|语言|Language/.test(await text()))
319
+ await page.evaluate(() => {
320
+ const sel = [...document.querySelectorAll('.ant-select')].find(e => /中文|English|语言/.test(e.innerText))
321
+ sel?.querySelector('.ant-select-selector')?.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }))
322
+ })
323
+ await T(900)
324
+ ok('切换到 English', await clickTextRe('.ant-select-item-option', /^English$/))
325
+ await T(3500)
326
+ ok('界面切换为英文', /Dashboard|Settings|English/i.test(await text()))
327
+ await page.evaluate(() => {
328
+ const sel = [...document.querySelectorAll('.ant-select')].find(e => /English|中文/.test(e.innerText))
329
+ sel?.querySelector('.ant-select-selector')?.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }))
330
+ })
331
+ await T(900)
332
+ ok('切回中文', await clickTextRe('.ant-select-item-option', /中文/))
333
+ await T(3500)
334
+ ok('界面恢复中文', /仪表盘|设置|中文/.test(await text()))
335
+
336
+ console.log('━━━ F. DCW 建线 ━━━')
337
+ await gotoReady(page, '/dcw', { wait: 2500 })
338
+ ok('产线运营页渲染', /产线|Line/.test(await text()))
339
+ const lineName = `e2e-line-${stamp.slice(-4)}`
340
+ ok('打开新建产线', await clickTextRe('button', /新建产线|新建/))
341
+ await T(800)
342
+ ok('填写产线名', await modalInput(0, lineName))
343
+ await clickTextRe('.modal-mask button, .ant-modal .ant-btn-primary', CREATE)
344
+ await T(2000)
345
+ ok('新产线出现在列表', (await text()).includes(lineName))
346
+
347
+ console.log('━━━ G-I. DAQ / Town / 监控 / 日志 / 权限 ━━━')
348
+ await gotoReady(page, '/daq', { wait: 2500 })
349
+ ok('数采中心渲染', /数采|DAQ/.test(await text()))
350
+ await gotoReady(page, '/town', { wait: 6000 })
351
+ ok('town 画布渲染(canvas)', (await page.$('canvas')) !== null)
352
+ await gotoReady(page, '/monitor', { wait: 2500 })
353
+ ok('运行时监控渲染', (await text()).length > 100)
354
+ await gotoReady(page, '/logs', { wait: 2500 })
355
+ ok('日志页渲染(表格)', (await page.$('.ant-table, table')) !== null)
356
+ ok('人工录入弹窗可开', await clickTextRe('button', /人工|Manual/))
357
+ await T(800)
358
+ await page.keyboard.press('Escape')
359
+ await gotoReady(page, '/permissions', { wait: 2500 })
360
+ ok('权限页渲染', /权限|Permission/.test(await text()))
361
+
362
+ console.log('━━━ J. 仪表盘 ━━━')
363
+ await gotoReady(page, '/', { wait: 3500 })
364
+ ok('仪表盘渲染(KPI/引擎面板)', /Agent Harness|引擎|LIVE/.test(await text()))
365
+ await page.screenshot({ path: path.join(OUT, 'j-dashboard.png') })
366
+
367
+ await browser.close()
368
+ fs.writeFileSync(path.join(OUT, 'result.json'), JSON.stringify({ pass, fails }, null, 2))
369
+ console.log(`\n===== UI 功能端到端:${pass} 通过 / ${fails.length} 失败 =====`)
370
+ if (fails.length) console.log('失败项:\n - ' + fails.join('\n - '))
371
+ process.exitCode = fails.length ? 1 : 0
@@ -0,0 +1,28 @@
1
+ // Scan en.ts (and zh-CN.ts) for values that still contain CJK — en must be CJK-free.
2
+ import { mkdtempSync, copyFileSync } from 'node:fs'
3
+ import { join } from 'node:path'
4
+ import { tmpdir } from 'node:os'
5
+ import { pathToFileURL } from 'node:url'
6
+
7
+ const tmp = mkdtempSync(join(tmpdir(), 'aw-i18n-'))
8
+ const load = async (p) => {
9
+ const t = join(tmp, p.replace(/[^\w.-]/g, '_') + '.mjs')
10
+ copyFileSync(p, t)
11
+ return (await import(pathToFileURL(t).href)).default
12
+ }
13
+ const en = await load('i18n/locales/en.ts')
14
+ const zh = await load('i18n/locales/zh-CN.ts')
15
+
16
+ const CJK = /[\u4e00-\u9fff\u3400-\u4dbf]/
17
+ function walk(o, prefix = '', out = []) {
18
+ for (const [k, v] of Object.entries(o)) {
19
+ if (typeof v === 'object' && v !== null) walk(v, prefix + k + '.', out)
20
+ else if (CJK.test(String(v))) out.push([prefix + k, String(v)])
21
+ }
22
+ return out
23
+ }
24
+ const enBad = walk(en)
25
+ console.log('en.ts CJK values:', enBad.length)
26
+ for (const [k, v] of enBad) console.log(` ${k} = ${v.slice(0, 80)}`)
27
+ const zhBad = walk(zh)
28
+ console.log('zh-CN.ts CJK values (expected = all):', zhBad.length)
@@ -0,0 +1,107 @@
1
+ // EN-mode i18n verification: seed minimal EN-named data, visit every route,
2
+ // scan rendered innerText for residual CJK (missing translations), screenshot each page.
3
+ // AW_BASE=http://127.0.0.1:3311 node scripts/_i18n-en-verify.mjs [--out .e2e-shots/i18n-en]
4
+ import fs from 'node:fs'
5
+ import path from 'node:path'
6
+ import { ensureVisualUser, api, launch, openPage, gotoReady, sleep, ROUTES } from './ui/lib.mjs'
7
+
8
+ const argv = process.argv.slice(2)
9
+ const arg = (k, d) => {
10
+ const i = argv.indexOf(`--${k}`)
11
+ return i >= 0 ? argv[i + 1] : d
12
+ }
13
+ const OUT = arg('out', '.e2e-shots/i18n-en')
14
+ fs.mkdirSync(OUT, { recursive: true })
15
+
16
+ const CJK = /[\u4e00-\u9fff\u3400-\u4dbf]/
17
+
18
+ const token = await ensureVisualUser()
19
+ console.log('token ok')
20
+
21
+ // ---- seed minimal EN-named data (idempotent by name) ----
22
+ let wsId
23
+ const wsList = await api('GET', '/api/workshop/workspaces', { token })
24
+ const wss = wsList.data?.workspaces ?? wsList.data ?? []
25
+ let ws = Array.isArray(wss) ? wss.find(w => w.name === 'Demo Factory') : null
26
+ if (!ws) {
27
+ const r = await api('POST', '/api/workshop/workspaces', { body: { name: 'Demo Factory' }, token })
28
+ ws = r.data
29
+ }
30
+ wsId = ws?.id ?? ws?.workspaceId
31
+ console.log('workspace:', wsId)
32
+
33
+ const chList = await api('GET', '/api/workshop/channels', { token })
34
+ const all = chList.data?.channels ?? chList.data ?? []
35
+ let ch = Array.isArray(all) ? all.find(c => c.name === 'demo-extrusion') : null
36
+ if (!ch) {
37
+ const r = await api('POST', '/api/workshop/channels', {
38
+ body: {
39
+ name: 'demo-extrusion',
40
+ scenarioPrompt: 'Demo scenario prompt for i18n verification.',
41
+ leadAgent: { name: 'demo-lead', harness: 'mock', config: { delayMs: 200, streamDemo: true } },
42
+ },
43
+ token,
44
+ })
45
+ ch = r.data
46
+ }
47
+ const cid = ch?.channelId ?? ch?.id
48
+ if (wsId && cid) await api('POST', `/api/workshop/workspaces/${wsId}/channels/${cid}`, { token })
49
+ console.log('channel:', cid)
50
+
51
+ // ---- browser pass ----
52
+ const browser = await launch({ width: 1440, height: 900 })
53
+ const report = []
54
+
55
+ async function visit(page, { routePath, name, wait = 3500 }) {
56
+ const errs = []
57
+ const shot = (routePath || 'root').replace(/[^a-zA-Z0-9]+/g, '_').slice(0, 60)
58
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 200)))
59
+ try {
60
+ await gotoReady(page, routePath, { wait })
61
+ await sleep(500)
62
+ const text = await page.evaluate(() => document.body.innerText)
63
+ const cjkLines = text.split('\n').map(l => l.trim()).filter(l => CJK.test(l))
64
+ await page.screenshot({ path: path.join(OUT, `${name || shot}.png`) })
65
+ report.push({ routePath, name: name || shot, cjk: cjkLines.length, lines: cjkLines.slice(0, 12), errors: errs })
66
+ console.log(`[${cjkLines.length === 0 ? 'PASS' : 'CJK!'}] ${routePath} (${cjkLines.length} cjk lines)`)
67
+ if (cjkLines.length) console.log(' ' + cjkLines.slice(0, 8).join(' | ').slice(0, 400))
68
+ if (errs.length) console.log(' pageerror:', errs.slice(0, 3).join(' / '))
69
+ }
70
+ catch (e) {
71
+ report.push({ routePath, name: name || shot, error: String(e).slice(0, 300) })
72
+ console.log(`[ERR] ${routePath}: ${String(e).slice(0, 200)}`)
73
+ }
74
+ }
75
+
76
+ const mainPage = await openPage(browser, { token, dark: true, width: 1440, height: 900 })
77
+ // restore locale before any app script runs
78
+ await mainPage.evaluateOnNewDocument(() => {
79
+ try {
80
+ localStorage.setItem('aw.locale', 'en')
81
+ }
82
+ catch { /* about:blank 无 origin,忽略 */ }
83
+ })
84
+
85
+ for (const r of ROUTES) {
86
+ await visit(mainPage, { routePath: r.path, name: r.name })
87
+ }
88
+ // workspace view (needs wsId)
89
+ if (wsId) await visit(mainPage, { routePath: `/workshop/w/${wsId}`, name: 'ws_view', wait: 5000 })
90
+
91
+ // login card (unauthenticated, fresh page without token cookie)
92
+ const anon = await browser.newPage()
93
+ await anon.setViewport({ width: 1440, height: 900 })
94
+ await anon.evaluateOnNewDocument(() => {
95
+ try {
96
+ localStorage.setItem('aw.locale', 'en')
97
+ localStorage.setItem('app', JSON.stringify({ isDark: true, sidebarCollapsed: false, accent: null, themeTouched: true }))
98
+ }
99
+ catch { /* about:blank 无 origin,忽略 */ }
100
+ })
101
+ await visit(anon, { routePath: '/workshop', name: 'login_card' })
102
+ await anon.close()
103
+
104
+ await browser.close()
105
+ fs.writeFileSync(path.join(OUT, 'report.json'), JSON.stringify(report, null, 2))
106
+ const bad = report.filter(r => (r.cjk ?? 0) > 0 || r.error)
107
+ console.log(`\nDONE: ${report.length} pages, ${bad.length} with CJK/errors → ${OUT}/report.json`)
@@ -0,0 +1,53 @@
1
+ // Extra checks: (1) login card in EN via incognito context (no token), (2) zh regression on key routes.
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+ import { launch, openPage, gotoReady, sleep, ensureVisualUser } from './ui/lib.mjs'
5
+
6
+ const OUT = '.e2e-shots/i18n-en'
7
+ fs.mkdirSync(OUT, { recursive: true })
8
+ const CJK = /[\u4e00-\u9fff]/
9
+ const token = await ensureVisualUser()
10
+
11
+ const browser = await launch({ width: 1440, height: 900 })
12
+
13
+ // ---- 1) EN login card, incognito (no cookies) ----
14
+ const ctx = await browser.createBrowserContext()
15
+ const anon = await ctx.newPage()
16
+ await anon.setViewport({ width: 1440, height: 900 })
17
+ await anon.evaluateOnNewDocument(() => {
18
+ try {
19
+ localStorage.setItem('aw.locale', 'en')
20
+ localStorage.setItem('app', JSON.stringify({ isDark: true, sidebarCollapsed: false, accent: null, themeTouched: true }))
21
+ }
22
+ catch { /* about:blank 无 origin,忽略 */ }
23
+ })
24
+ await gotoReady(anon, '/workshop', { wait: 3000 })
25
+ await sleep(800)
26
+ const enText = await anon.evaluate(() => document.body.innerText)
27
+ const enCjk = enText.split('\n').map(l => l.trim()).filter(l => CJK.test(l))
28
+ await anon.screenshot({ path: path.join(OUT, 'login_card.png') })
29
+ console.log(`[login EN] cjk=${enCjk.length}${enCjk.length ? ' → ' + enCjk.join(' | ').slice(0, 200) : ''}`)
30
+ await ctx.close()
31
+
32
+ // ---- 2) zh regression: default locale (no aw.locale) ----
33
+ const zhPage = await openPage(browser, { token, dark: true, width: 1440, height: 900 })
34
+ // no token: use login-less routes? need auth → reuse stored token from report env
35
+ await zhPage.evaluateOnNewDocument(() => {
36
+ try {
37
+ localStorage.removeItem('aw.locale')
38
+ }
39
+ catch { /* 忽略 */ }
40
+ })
41
+ for (const p of ['/town', '/dcw', '/settings']) {
42
+ try {
43
+ await gotoReady(zhPage, p, { wait: 3500 })
44
+ await sleep(500)
45
+ await zhPage.screenshot({ path: path.join(OUT, `zh-regression${p.replace(/\//g, '_')}.png`) })
46
+ const t = await zhPage.evaluate(() => document.body.innerText)
47
+ const hasZh = CJK.test(t)
48
+ console.log(`[zh] ${p} containsChinese=${hasZh}`)
49
+ }
50
+ catch (e) { console.log(`[zh] ${p} ERR ${String(e).slice(0, 120)}`) }
51
+ }
52
+ await browser.close()
53
+ console.log('DONE')