agentworkshop 0.7.41 → 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 (265) 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/DKySmNPI.js +14 -0
  11. package/.output/public/_nuxt/Di0NY_ht.js +1 -0
  12. package/.output/public/_nuxt/Du9v6qGB.js +1 -0
  13. package/.output/public/_nuxt/{DGbd-UiG.js → EHlEMRii.js} +1 -1
  14. package/.output/public/_nuxt/Eke1fs-U.js +1 -0
  15. package/.output/public/_nuxt/{DeJykBFf.js → H0BcqZtT.js} +1 -1
  16. package/.output/public/_nuxt/{C4oW1HsE.js → HSYEkkce.js} +1 -1
  17. package/.output/public/_nuxt/{D_OTvQza.js → J9oU00D1.js} +1 -1
  18. package/.output/public/_nuxt/OmpTerminalPanel.BxR5-lvi.css +1 -0
  19. package/.output/public/_nuxt/SSyAPaHc.js +1 -0
  20. package/.output/public/_nuxt/TownView.li4EMQxK.css +1 -0
  21. package/.output/public/_nuxt/VdLk4jLE2.js +1 -0
  22. package/.output/public/_nuxt/_id_.BhsXoEuk.css +1 -0
  23. package/.output/public/_nuxt/_id_.DQyj-WKN.css +1 -0
  24. package/.output/public/_nuxt/{_wsId_.B4WiqkIS.css → _wsId_.CTx-BlfF.css} +1 -1
  25. package/.output/public/_nuxt/aShsv_rG.js +19 -0
  26. package/.output/public/_nuxt/{yF0aYMF1.js → aesgXKSr.js} +3 -3
  27. package/.output/public/_nuxt/agents.B21nUEUX.css +1 -0
  28. package/.output/public/_nuxt/aml.ekTjxFkf.css +1 -0
  29. package/.output/public/_nuxt/builds/latest.json +1 -1
  30. package/.output/public/_nuxt/builds/meta/b5506ef0-f48e-4f14-a6c0-d65e505d0b09.json +1 -0
  31. package/.output/public/_nuxt/channel-templates.DplM1pJ3.css +1 -0
  32. package/.output/public/_nuxt/daq.BdVmoIfe.css +1 -0
  33. package/.output/public/_nuxt/dcw.DCgyzpWV.css +1 -0
  34. package/.output/public/_nuxt/{default.J32SgqFY.css → default.Bh3L2HU8.css} +1 -1
  35. package/.output/public/_nuxt/lHAxdSsj.js +1 -0
  36. package/.output/public/_nuxt/pages.j9rzRocr.css +1 -0
  37. package/.output/public/_nuxt/plugins.DhiZbHU8.css +1 -0
  38. package/.output/public/_nuxt/settings.BRx9rIDg.css +1 -0
  39. package/.output/public/_nuxt/teams.BLlOPB_Z.css +1 -0
  40. package/.output/public/_nuxt/tokens.De8xzszn.css +1 -0
  41. package/.output/public/_nuxt/workshop.D1jFNkrn.css +1 -0
  42. package/.output/server/chunks/_/nitro.mjs +748 -733
  43. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs → OmpTerminalPanel-CVvxpsF7.mjs} +15 -15
  44. package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs.map → OmpTerminalPanel-CVvxpsF7.mjs.map} +1 -1
  45. package/.output/server/chunks/build/{TownView--otsnr_7.mjs → TownView-CJ6IsDCc.mjs} +226 -225
  46. package/.output/server/chunks/build/{TownView--otsnr_7.mjs.map → TownView-CJ6IsDCc.mjs.map} +1 -1
  47. package/.output/server/chunks/build/{_id_-Brv-JSvr.mjs → _id_-CVD_H0ir.mjs} +143 -142
  48. package/.output/server/chunks/build/_id_-CVD_H0ir.mjs.map +1 -0
  49. package/.output/server/chunks/build/{_id_-D9pl1OZy.mjs → _id_-CtxMb3qp.mjs} +41 -41
  50. package/.output/server/chunks/build/_id_-CtxMb3qp.mjs.map +1 -0
  51. package/.output/server/chunks/build/{_wsId_-BASfDf0s.mjs → _wsId_-DpY3T-md.mjs} +514 -444
  52. package/.output/server/chunks/build/{_wsId_-BASfDf0s.mjs.map → _wsId_-DpY3T-md.mjs.map} +1 -1
  53. package/.output/server/chunks/build/{agents-CLQqJF2y.mjs → agents-DsCkuXDV.mjs} +49 -46
  54. package/.output/server/chunks/build/agents-DsCkuXDV.mjs.map +1 -0
  55. package/.output/server/chunks/build/{aml-_7FdFL2p.mjs → aml-CT0xKGKI.mjs} +122 -105
  56. package/.output/server/chunks/build/aml-CT0xKGKI.mjs.map +1 -0
  57. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs +117 -0
  58. package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs.map +1 -0
  59. package/.output/server/chunks/build/{channel-templates-p6SZNjlM.mjs → channel-templates-Dla9FbYa.mjs} +49 -37
  60. package/.output/server/chunks/build/channel-templates-Dla9FbYa.mjs.map +1 -0
  61. package/.output/server/chunks/build/{daq-DkMvD8sg.mjs → daq-DWWSKGAV.mjs} +89 -88
  62. package/.output/server/chunks/build/daq-DWWSKGAV.mjs.map +1 -0
  63. package/.output/server/chunks/build/{dcw-C1jgVJkO.mjs → dcw-CT8k17_E.mjs} +44 -43
  64. package/.output/server/chunks/build/dcw-CT8k17_E.mjs.map +1 -0
  65. package/.output/server/chunks/build/{default-BE4r0FZN.mjs → default-D3525UFX.mjs} +16 -15
  66. package/.output/server/chunks/build/{default-BE4r0FZN.mjs.map → default-D3525UFX.mjs.map} +1 -1
  67. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs → monitor-Dh2Kvaq5.mjs} +2 -2
  68. package/.output/server/chunks/build/{monitor-C0okQaXz.mjs.map → monitor-Dh2Kvaq5.mjs.map} +1 -1
  69. package/.output/server/chunks/build/{pages-B0bUWnC6.mjs → pages-BHcSBM2D.mjs} +24 -18
  70. package/.output/server/chunks/build/pages-BHcSBM2D.mjs.map +1 -0
  71. package/.output/server/chunks/build/{plugins-lGlUr1lj.mjs → plugins-BNgIi61W.mjs} +23 -17
  72. package/.output/server/chunks/build/plugins-BNgIi61W.mjs.map +1 -0
  73. package/.output/server/chunks/build/{settings-Ddd1qx2Z.mjs → settings-ipYBDWdn.mjs} +45 -44
  74. package/.output/server/chunks/build/settings-ipYBDWdn.mjs.map +1 -0
  75. package/.output/server/chunks/build/{teams-MEDLZdMn.mjs → teams-5lx-CwFa.mjs} +47 -44
  76. package/.output/server/chunks/build/teams-5lx-CwFa.mjs.map +1 -0
  77. package/.output/server/chunks/build/{tokens-C7fTM5C2.mjs → tokens-q-IDT8Io.mjs} +73 -53
  78. package/.output/server/chunks/build/tokens-q-IDT8Io.mjs.map +1 -0
  79. package/.output/server/chunks/build/{town-CoQdblbZ.mjs → town-QJWB5wng.mjs} +3 -2
  80. package/.output/server/chunks/build/town-QJWB5wng.mjs.map +1 -0
  81. package/.output/server/chunks/build/{workshop-BL5bhfs0.mjs → workshop-BIlCj_ZP.mjs} +45 -45
  82. package/.output/server/chunks/build/workshop-BIlCj_ZP.mjs.map +1 -0
  83. package/.output/server/chunks/routes/renderer.mjs +1 -1
  84. package/.output/server/chunks/virtual/entry.mjs +637 -86
  85. package/.output/server/chunks/virtual/entry.mjs.map +1 -1
  86. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  87. package/.output/server/package.json +1 -1
  88. package/README-zh.md +13 -3
  89. package/README.md +13 -3
  90. package/app/components/AppHeader.vue +961 -959
  91. package/app/components/workshop/A2aRpcDebugger.vue +237 -237
  92. package/app/components/workshop/AgentInspectorDrawer.vue +424 -424
  93. package/app/components/workshop/AgentLanesView.vue +998 -998
  94. package/app/components/workshop/ArtifactCard.vue +120 -120
  95. package/app/components/workshop/AssetLibrary.vue +452 -452
  96. package/app/components/workshop/ChannelSessionList.vue +812 -804
  97. package/app/components/workshop/CommandPalette.vue +269 -269
  98. package/app/components/workshop/Composer.vue +827 -827
  99. package/app/components/workshop/DeviceTwinPanel.vue +643 -643
  100. package/app/components/workshop/EventBlock.vue +602 -602
  101. package/app/components/workshop/FileSelectorModal.vue +188 -188
  102. package/app/components/workshop/InspectorPanel.vue +301 -301
  103. package/app/components/workshop/MemoryPanel.vue +364 -364
  104. package/app/components/workshop/MultiChannelView.vue +189 -189
  105. package/app/components/workshop/TaskBoardView.vue +686 -686
  106. package/app/components/workshop/TaskInspectorDrawer.vue +390 -390
  107. package/app/components/workshop/TranscriptTimeline.vue +601 -601
  108. package/app/components/workshop/blocks/ClusterArtifact.vue +61 -61
  109. package/app/components/workshop/blocks/ClusterError.vue +85 -85
  110. package/app/components/workshop/blocks/ClusterLife.vue +87 -87
  111. package/app/components/workshop/blocks/ClusterMember.vue +95 -93
  112. package/app/components/workshop/blocks/ClusterMemory.vue +84 -84
  113. package/app/components/workshop/blocks/ClusterOther.vue +67 -67
  114. package/app/components/workshop/blocks/ClusterStatus.vue +149 -147
  115. package/app/components/workshop/blocks/ClusterTool.vue +158 -158
  116. package/app/components/workshop/terminal/OmpTerminalPanel.vue +1011 -1011
  117. package/app/components/workshop/town/TownView.vue +56 -56
  118. package/app/middleware/locale-cookie.global.ts +22 -0
  119. package/app/pages/aml/index.vue +24 -2
  120. package/app/pages/daq/[id].vue +11 -11
  121. package/app/pages/daq/index.vue +2316 -2316
  122. package/app/pages/dcw/[id].vue +18 -19
  123. package/app/pages/dcw/index.vue +3 -3
  124. package/app/pages/index.vue +987 -981
  125. package/app/pages/plugins/index.vue +317 -311
  126. package/app/pages/settings.vue +1564 -1562
  127. package/app/pages/tokens.vue +15 -15
  128. package/app/pages/workshop/agents.vue +612 -609
  129. package/app/pages/workshop/channel-templates.vue +18 -11
  130. package/app/pages/workshop/index.vue +668 -668
  131. package/app/pages/workshop/teams.vue +743 -740
  132. package/app/pages/workshop/w/[wsId].vue +704 -704
  133. package/app/plugins/locale-restore.client.ts +16 -1
  134. package/app/utils/builtin-catalog.ts +104 -0
  135. package/config.yml +170 -170
  136. package/docs/full-test-plan.md +2 -3
  137. package/i18n/locales/en.ts +293 -35
  138. package/i18n/locales/zh-CN.ts +265 -7
  139. package/package.json +2 -2
  140. package/scripts/_dbg-perms-e2e.mjs +132 -109
  141. package/scripts/_demo-agentteam-capture.mjs +278 -0
  142. package/scripts/_e2e-ui-functional.mjs +371 -0
  143. package/scripts/_i18n-en-cjk-scan.mjs +28 -0
  144. package/scripts/_i18n-en-verify.mjs +107 -0
  145. package/scripts/_i18n-extra-checks.mjs +53 -0
  146. package/scripts/_i18n-scan-hardcoded.mjs +180 -0
  147. package/scripts/_i18n-verify-keys.mjs +50 -0
  148. package/scripts/_loop-biax-campaign.mjs +15 -9
  149. package/scripts/_sweep-button-contrast.mjs +87 -0
  150. package/scripts/ui/record-demo.mjs +557 -0
  151. package/server/plugins-builtin/diag-bridge/index.mjs +608 -661
  152. package/server/plugins-builtin/rag-bridge/index.mjs +57 -133
  153. package/server/services/workshop/daq/drivers.ts +11 -0
  154. package/.output/public/_nuxt/88GOowgO2.js +0 -1
  155. package/.output/public/_nuxt/BN16aRdQ.js +0 -1
  156. package/.output/public/_nuxt/BWVhZntO.js +0 -1
  157. package/.output/public/_nuxt/BXxH0UQE.js +0 -1
  158. package/.output/public/_nuxt/BsROmY89.js +0 -1
  159. package/.output/public/_nuxt/C0ZrS1KL.js +0 -19
  160. package/.output/public/_nuxt/C4s_niy-.js +0 -14
  161. package/.output/public/_nuxt/D6mbFA-a.js +0 -1
  162. package/.output/public/_nuxt/DS8qj8EL.js +0 -1
  163. package/.output/public/_nuxt/DUjW5Yq5.js +0 -1
  164. package/.output/public/_nuxt/Dap5PmTL.js +0 -2
  165. package/.output/public/_nuxt/Dqw1lpr-.js +0 -1
  166. package/.output/public/_nuxt/DvLxILQP.js +0 -1
  167. package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +0 -1
  168. package/.output/public/_nuxt/TownView.f-YOdXRs.css +0 -1
  169. package/.output/public/_nuxt/_id_.D9ymtHB5.css +0 -1
  170. package/.output/public/_nuxt/_id_.jf-_8E_c.css +0 -1
  171. package/.output/public/_nuxt/agents.ZD0rVdHE.css +0 -1
  172. package/.output/public/_nuxt/aml.BN8ZEW4h.css +0 -1
  173. package/.output/public/_nuxt/builds/meta/78a295bb-2072-48a4-8b46-6d545fdae743.json +0 -1
  174. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +0 -1
  175. package/.output/public/_nuxt/daq.DjSgY94l.css +0 -1
  176. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +0 -1
  177. package/.output/public/_nuxt/eqq3wWQF.js +0 -18
  178. package/.output/public/_nuxt/pages.D7aSDlUI.css +0 -1
  179. package/.output/public/_nuxt/plugins.sb4nDGqr.css +0 -1
  180. package/.output/public/_nuxt/qr6R6Ha1.js +0 -1
  181. package/.output/public/_nuxt/settings.BTgR-NiE.css +0 -1
  182. package/.output/public/_nuxt/teams.CxnxKVVs.css +0 -1
  183. package/.output/public/_nuxt/tokens.BFY7qv4f.css +0 -1
  184. package/.output/public/_nuxt/workshop.CR_QzeAN.css +0 -1
  185. package/.output/server/chunks/build/_id_-Brv-JSvr.mjs.map +0 -1
  186. package/.output/server/chunks/build/_id_-D9pl1OZy.mjs.map +0 -1
  187. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +0 -1
  188. package/.output/server/chunks/build/aml-_7FdFL2p.mjs.map +0 -1
  189. package/.output/server/chunks/build/channel-templates-p6SZNjlM.mjs.map +0 -1
  190. package/.output/server/chunks/build/daq-DkMvD8sg.mjs.map +0 -1
  191. package/.output/server/chunks/build/dcw-C1jgVJkO.mjs.map +0 -1
  192. package/.output/server/chunks/build/pages-B0bUWnC6.mjs.map +0 -1
  193. package/.output/server/chunks/build/plugins-lGlUr1lj.mjs.map +0 -1
  194. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +0 -1
  195. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +0 -1
  196. package/.output/server/chunks/build/tokens-C7fTM5C2.mjs.map +0 -1
  197. package/.output/server/chunks/build/town-CoQdblbZ.mjs.map +0 -1
  198. package/.output/server/chunks/build/workshop-BL5bhfs0.mjs.map +0 -1
  199. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.continue.json +0 -1
  200. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.json +0 -1
  201. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/054bb4bc4e4217a2ab0b0c6b663747214a9af464f5568ea60e784704e340bb0f.source +0 -153
  202. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/0554197c3d2bac203a1f5c48a5d52495d1336db1bd7aa4f4c19875810363e684.source +0 -153
  203. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/05c203d12a954fffba942e1d626e3f5fec05b18a5aef2bac003b7884a4b77406.source +0 -431
  204. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/145a6e6ba811af35aed31e0cc3211f53706a14980fa069ee9e3c1591139f96f0.source +0 -180
  205. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/1ae0fb281f97899c025e368ca73f41349b01fb5f85b19e08d7ebccff49095ed5.source +0 -112
  206. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/20ee9967aa0541c516db7575bdd4a1a745939bc7016bd10d7635903113edb8ec.source +0 -227
  207. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/233751843aeba9d4c5263f275b8102de00443c06f286918cbd23e0b6729e01aa.source +0 -131
  208. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/263c2e05b425e5c322e60c6475279f963bab3a873eb521b21df7fec935a46597.source +0 -160
  209. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/2ac5eaac28eaaee2178c4b32014df8e732f9dd432a285a872f5b7f31c1395fa4.source +0 -157
  210. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/33a0db0d2d95d2c11d0720090458e5fba1425376f5ceb5b422d1d3471be3226b.source +0 -348
  211. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/359e474ae83db35e4615d3728de6bf694b64d4dce9b536730a3b6518f9c0494a.source +0 -296
  212. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/37a4c3c31c4b70f6b719c53d1e9c4fcba603f31adfdd11bb6ef9f0d094923c56.source +0 -285
  213. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/461feca1c14a695eb8b0f17a6f95fe97eeceabc0510faaea4ba6d25bde147d8d.source +0 -217
  214. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/4cfadbda8591f29d5e1e230dc000e701b756e850441b3900fb596a4d2f6e98c2.source +0 -301
  215. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/50ca2810cf0b248c96948994c0c404531bbad6e553166b6816b3b03bfa0f75d2.source +0 -151
  216. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/52d9e8cb591a9ae86e238c3620a610cd386717bee222c0f37b25b2ac7e523dc4.source +0 -135
  217. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/6c87daafa4c006560a954636a97333f6123153c9d34d96203f7ee3a14e556357.source +0 -156
  218. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/77bc47854b577900621cacf1697f81fa2bc005af129d37614a97a3441c08f99f.source +0 -72
  219. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/7bbb789d3a527817118cf8399ed1861d6a962ade10878fd07223435d333a7089.source +0 -469
  220. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/86b7c1daafc8b60ba9982dde5bf1bcc74265709483c176e71f23ec6fc267451f.source +0 -259
  221. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/8df66d62bd41984d0970e8236882080c5085ac937d07a29779463d6a03b37c76.source +0 -97
  222. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/9962f2d1b3a85137bd40cc835725600b39f89ee02a27c06b2e0402e1eafde115.source +0 -277
  223. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a1090ea066b792cb201739f4994e37bf7c12f52e805239a1eab87f18a537f266.source +0 -171
  224. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a5241091650134d0919897e086a606a3846c8b2b54f8b4c84a8696c145ac3e9e.source +0 -425
  225. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/af19fa4c6e34d5540028be4b17600161283af3a2a2dfc61a3fdc0834436a0c14.source +0 -99
  226. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3713d78e2b2f93905ff6be8d58a8d0a2f42dea322eb67c987fda22ff6a084d5.source +0 -325
  227. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3849943c3aff102d1af815631fc843a2a52c17646a4b04696ece323e483bbec.source +0 -332
  228. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/bac735a3803a6650b7f1eb8f587e6dc5c68ea055000c3c99a11789438b78e837.source +0 -43
  229. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d32c24d61eb8b33c73e5a8fc7e4dc7ef437aed238ee4371c2b00617e6104090a.source +0 -154
  230. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d5170dc9a9879fda04fdf91d8a110d4efb6787ee38dda596d4da899883d36a72.source +0 -531
  231. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/dc59bcd7bb397be63ba2f2c3fec23f3782dcb17d2d75a9981eeced761f33eb32.source +0 -439
  232. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/eb5ff8cf66b92aec7bf6726781d8df74faa2b030c3aaac17d6b00c1fc04c9b32.source +0 -323
  233. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f10615a6fae18436e70d1201b2d44fdae3174a3aa7bb4db97dea1b3ed291d721.source +0 -433
  234. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f36d3e271668bc1260c2823542d22dbf1d0f7041ff16b1ac175bce2256e582ca.source +0 -280
  235. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f94449380af09f31ac352152007146a75cc80bae8d6411ffe57b2c56f933b0ff.source +0 -117
  236. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/fe4dfd81178fd6b648335f6405d9ede71319aebde796008e50db21f8adf44c99.source +0 -160
  237. package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/ffb4e3aa548cfdadabaa44ab599f9b6a3ca6288176a2cc7a4b2fc2a12b5fb848.source +0 -101
  238. package/scripts/.mimosa/hook-status/sess_aeaecb58-8833-4b83-b93c-050467d21e03-d944d71bb3.json +0 -14
  239. package/scripts/_dbg-audit-all-pages.mjs +0 -43
  240. package/scripts/_dbg-daq-frames-e2e.mjs +0 -118
  241. package/scripts/_dbg-daq-rowstyle-verify.mjs +0 -112
  242. package/scripts/_dbg-hitl-timeout-e2e.mjs +0 -93
  243. package/scripts/_dbg-live-line-e2e.mjs +0 -279
  244. package/scripts/_dbg-minimal-omp.mjs +0 -77
  245. package/scripts/_dbg-mode-nav.mjs +0 -173
  246. package/scripts/_dbg-real-scenario-timeline.log +0 -55
  247. package/scripts/_dbg-team-repro.mjs +0 -76
  248. package/scripts/_dbg-town-agents-probe.mjs +0 -60
  249. package/scripts/_dbg-visual-acceptance.mjs +0 -63
  250. package/scripts/_townview_style.css +0 -1185
  251. package/scripts/_townview_template.html +0 -1215
  252. package/server/data/agent-node-bindings.json +0 -554
  253. package/server/data/daq-templates.json +0 -16
  254. package/server/data/daqs.json +0 -7464
  255. package/server/data/dcw-lines.json +0 -1787
  256. package/server/data/dcw-products.json +0 -1626
  257. package/server/data/dcw-recipes.json +0 -4672
  258. package/server/data/dcw-rollback.json +0 -20754
  259. package/server/data/dcw-runs.json +0 -6354
  260. package/server/data/dcw-templates.json +0 -146
  261. package/server/data/dcw-writes.json +0 -15650
  262. package/server/data/dcws.json +0 -9033
  263. package/server/data/device-twins.json +0 -133
  264. package/server/data/line-runs.json +0 -322
  265. 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')