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.
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/B8uy9iRW.js +1 -0
- package/.output/public/_nuxt/BGTYYQH-.js +1 -0
- package/.output/public/_nuxt/BUmHEJ77.js +2 -0
- package/.output/public/_nuxt/BY7YrV_D.js +1 -0
- package/.output/public/_nuxt/Bqcgz95s.js +18 -0
- package/.output/public/_nuxt/C7UlqPeD.js +1 -0
- package/.output/public/_nuxt/CTmDn-YP.js +1 -0
- package/.output/public/_nuxt/Cs8pVeVG.js +1 -0
- package/.output/public/_nuxt/{DcsCcxp4.js → D98NH8kG.js} +1 -1
- package/.output/public/_nuxt/DKySmNPI.js +14 -0
- package/.output/public/_nuxt/Di0NY_ht.js +1 -0
- package/.output/public/_nuxt/Du9v6qGB.js +1 -0
- package/.output/public/_nuxt/{C_iYD-EB.js → EHlEMRii.js} +1 -1
- package/.output/public/_nuxt/Eke1fs-U.js +1 -0
- package/.output/public/_nuxt/{DeJykBFf.js → H0BcqZtT.js} +1 -1
- package/.output/public/_nuxt/{C4oW1HsE.js → HSYEkkce.js} +1 -1
- package/.output/public/_nuxt/{DZshyNLa.js → IREZ1eC2.js} +1 -1
- package/.output/public/_nuxt/{D_OTvQza.js → J9oU00D1.js} +1 -1
- package/.output/public/_nuxt/OmpTerminalPanel.BxR5-lvi.css +1 -0
- package/.output/public/_nuxt/SSyAPaHc.js +1 -0
- package/.output/public/_nuxt/TownView.li4EMQxK.css +1 -0
- package/.output/public/_nuxt/VdLk4jLE2.js +1 -0
- package/.output/public/_nuxt/_id_.BhsXoEuk.css +1 -0
- package/.output/public/_nuxt/_id_.DQyj-WKN.css +1 -0
- package/.output/public/_nuxt/{_wsId_.B4WiqkIS.css → _wsId_.CTx-BlfF.css} +1 -1
- package/.output/public/_nuxt/aShsv_rG.js +19 -0
- package/.output/public/_nuxt/{yF0aYMF1.js → aesgXKSr.js} +3 -3
- package/.output/public/_nuxt/agents.B21nUEUX.css +1 -0
- package/.output/public/_nuxt/aml.ekTjxFkf.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/b5506ef0-f48e-4f14-a6c0-d65e505d0b09.json +1 -0
- package/.output/public/_nuxt/ca4TJ3Op.js +1 -0
- package/.output/public/_nuxt/channel-templates.DplM1pJ3.css +1 -0
- package/.output/public/_nuxt/daq.BdVmoIfe.css +1 -0
- package/.output/public/_nuxt/dcw.DCgyzpWV.css +1 -0
- package/.output/public/_nuxt/{default.J32SgqFY.css → default.Bh3L2HU8.css} +1 -1
- package/.output/public/_nuxt/lHAxdSsj.js +1 -0
- package/.output/public/_nuxt/pages.j9rzRocr.css +1 -0
- package/.output/public/_nuxt/plugins.DhiZbHU8.css +1 -0
- package/.output/public/_nuxt/settings.BRx9rIDg.css +1 -0
- package/.output/public/_nuxt/teams.BLlOPB_Z.css +1 -0
- package/.output/public/_nuxt/tokens.De8xzszn.css +1 -0
- package/.output/public/_nuxt/workshop.D1jFNkrn.css +1 -0
- package/.output/server/chunks/_/nitro.mjs +1058 -830
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs → OmpTerminalPanel-CVvxpsF7.mjs} +15 -15
- package/.output/server/chunks/build/{OmpTerminalPanel-Cu_s4i0A.mjs.map → OmpTerminalPanel-CVvxpsF7.mjs.map} +1 -1
- package/.output/server/chunks/build/{TownView-BuLAsmpR.mjs → TownView-CJ6IsDCc.mjs} +229 -228
- package/.output/server/chunks/build/{TownView-BuLAsmpR.mjs.map → TownView-CJ6IsDCc.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-BU1wfOID.mjs → _id_-CVD_H0ir.mjs} +149 -147
- package/.output/server/chunks/build/_id_-CVD_H0ir.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-BYsEua57.mjs → _id_-CtxMb3qp.mjs} +47 -46
- package/.output/server/chunks/build/_id_-CtxMb3qp.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-BxlDcJHg.mjs → _wsId_-DpY3T-md.mjs} +516 -446
- package/.output/server/chunks/build/{_wsId_-BxlDcJHg.mjs.map → _wsId_-DpY3T-md.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-CLQqJF2y.mjs → agents-DsCkuXDV.mjs} +49 -46
- package/.output/server/chunks/build/agents-DsCkuXDV.mjs.map +1 -0
- package/.output/server/chunks/build/{aml-_7FdFL2p.mjs → aml-CT0xKGKI.mjs} +122 -105
- package/.output/server/chunks/build/aml-CT0xKGKI.mjs.map +1 -0
- package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs +117 -0
- package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs.map +1 -0
- package/.output/server/chunks/build/{channel-templates-p6SZNjlM.mjs → channel-templates-Dla9FbYa.mjs} +49 -37
- package/.output/server/chunks/build/channel-templates-Dla9FbYa.mjs.map +1 -0
- package/.output/server/chunks/build/{daq-cgyjKmJu.mjs → daq-DWWSKGAV.mjs} +95 -93
- package/.output/server/chunks/build/daq-DWWSKGAV.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-CoTxBlAZ.mjs → dcw-CT8k17_E.mjs} +46 -45
- package/.output/server/chunks/build/dcw-CT8k17_E.mjs.map +1 -0
- package/.output/server/chunks/build/{default-BE4r0FZN.mjs → default-D3525UFX.mjs} +16 -15
- package/.output/server/chunks/build/{default-BE4r0FZN.mjs.map → default-D3525UFX.mjs.map} +1 -1
- package/.output/server/chunks/build/{logs-BSOeZwwI.mjs → logs--dByNyhw.mjs} +2 -2
- package/.output/server/chunks/build/{logs-BSOeZwwI.mjs.map → logs--dByNyhw.mjs.map} +1 -1
- package/.output/server/chunks/build/{monitor-C0okQaXz.mjs → monitor-Dh2Kvaq5.mjs} +2 -2
- package/.output/server/chunks/build/{monitor-C0okQaXz.mjs.map → monitor-Dh2Kvaq5.mjs.map} +1 -1
- package/.output/server/chunks/build/{pages-DFxjn9Xa.mjs → pages-BHcSBM2D.mjs} +26 -20
- package/.output/server/chunks/build/pages-BHcSBM2D.mjs.map +1 -0
- package/.output/server/chunks/build/{plugins-lGlUr1lj.mjs → plugins-BNgIi61W.mjs} +23 -17
- package/.output/server/chunks/build/plugins-BNgIi61W.mjs.map +1 -0
- package/.output/server/chunks/build/{settings-Ddd1qx2Z.mjs → settings-ipYBDWdn.mjs} +45 -44
- package/.output/server/chunks/build/settings-ipYBDWdn.mjs.map +1 -0
- package/.output/server/chunks/build/{teams-MEDLZdMn.mjs → teams-5lx-CwFa.mjs} +47 -44
- package/.output/server/chunks/build/teams-5lx-CwFa.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-C7fTM5C2.mjs → tokens-q-IDT8Io.mjs} +73 -53
- package/.output/server/chunks/build/tokens-q-IDT8Io.mjs.map +1 -0
- package/.output/server/chunks/build/{town-qMPql9Se.mjs → town-QJWB5wng.mjs} +5 -4
- package/.output/server/chunks/build/town-QJWB5wng.mjs.map +1 -0
- package/.output/server/chunks/build/{useDaqStream-CIBaiGAl.mjs → useDaqStream-DfCFpb3p.mjs} +9 -3
- package/.output/server/chunks/build/useDaqStream-DfCFpb3p.mjs.map +1 -0
- package/.output/server/chunks/build/{useDcwStream-CBbiYnWe.mjs → useDcwStream-UPiX-3iO.mjs} +11 -2
- package/.output/server/chunks/build/useDcwStream-UPiX-3iO.mjs.map +1 -0
- package/.output/server/chunks/build/{workshop-BL5bhfs0.mjs → workshop-BIlCj_ZP.mjs} +45 -45
- package/.output/server/chunks/build/workshop-BIlCj_ZP.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
- package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs +3 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs +3 -1
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +2 -2
- package/.output/server/chunks/virtual/entry.mjs +639 -88
- package/.output/server/chunks/virtual/entry.mjs.map +1 -1
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +16 -6
- package/README.md +17 -7
- package/app/components/AppHeader.vue +961 -959
- package/app/components/workshop/A2aRpcDebugger.vue +237 -237
- package/app/components/workshop/AgentInspectorDrawer.vue +424 -424
- package/app/components/workshop/AgentLanesView.vue +998 -998
- package/app/components/workshop/ArtifactCard.vue +120 -120
- package/app/components/workshop/AssetLibrary.vue +452 -452
- package/app/components/workshop/ChannelSessionList.vue +812 -804
- package/app/components/workshop/CommandPalette.vue +269 -269
- package/app/components/workshop/Composer.vue +827 -827
- package/app/components/workshop/DeviceTwinPanel.vue +643 -643
- package/app/components/workshop/EventBlock.vue +602 -602
- package/app/components/workshop/FileSelectorModal.vue +188 -188
- package/app/components/workshop/InspectorPanel.vue +301 -301
- package/app/components/workshop/MemoryPanel.vue +364 -364
- package/app/components/workshop/MultiChannelView.vue +189 -189
- package/app/components/workshop/TaskBoardView.vue +686 -686
- package/app/components/workshop/TaskInspectorDrawer.vue +390 -390
- package/app/components/workshop/TranscriptTimeline.vue +601 -601
- package/app/components/workshop/blocks/ClusterArtifact.vue +61 -61
- package/app/components/workshop/blocks/ClusterError.vue +85 -85
- package/app/components/workshop/blocks/ClusterLife.vue +87 -87
- package/app/components/workshop/blocks/ClusterMember.vue +95 -93
- package/app/components/workshop/blocks/ClusterMemory.vue +84 -84
- package/app/components/workshop/blocks/ClusterOther.vue +67 -67
- package/app/components/workshop/blocks/ClusterStatus.vue +149 -147
- package/app/components/workshop/blocks/ClusterTool.vue +158 -158
- package/app/components/workshop/terminal/OmpTerminalPanel.vue +1011 -1011
- package/app/components/workshop/town/TownView.vue +56 -56
- package/app/composables/workshop/useDaqStream.ts +7 -4
- package/app/composables/workshop/useDcwStream.ts +8 -2
- package/app/middleware/locale-cookie.global.ts +22 -0
- package/app/pages/aml/index.vue +24 -2
- package/app/pages/daq/[id].vue +20 -16
- package/app/pages/daq/index.vue +2310 -2307
- package/app/pages/dcw/[id].vue +26 -23
- package/app/pages/dcw/index.vue +3 -3
- package/app/pages/index.vue +987 -981
- package/app/pages/plugins/index.vue +317 -311
- package/app/pages/settings.vue +1564 -1562
- package/app/pages/tokens.vue +15 -15
- package/app/pages/workshop/agents.vue +612 -609
- package/app/pages/workshop/channel-templates.vue +18 -11
- package/app/pages/workshop/index.vue +668 -668
- package/app/pages/workshop/teams.vue +743 -740
- package/app/pages/workshop/w/[wsId].vue +704 -704
- package/app/plugins/locale-restore.client.ts +16 -1
- package/app/utils/builtin-catalog.ts +104 -0
- package/config.yml +170 -170
- package/docs/cli.en.md +2 -2
- package/docs/cli.md +2 -2
- package/docs/full-test-plan.md +2 -3
- package/docs/plugins.en.md +33 -1
- package/docs/plugins.md +29 -1
- package/docs/sdk.en.md +1 -1
- package/docs/sdk.md +1 -1
- package/i18n/locales/en.ts +293 -35
- package/i18n/locales/zh-CN.ts +265 -7
- package/package.json +4 -3
- package/scripts/_dbg-perms-e2e.mjs +132 -109
- package/scripts/_demo-agentteam-capture.mjs +278 -0
- package/scripts/_e2e-ui-functional.mjs +371 -0
- package/scripts/_i18n-en-cjk-scan.mjs +28 -0
- package/scripts/_i18n-en-verify.mjs +107 -0
- package/scripts/_i18n-extra-checks.mjs +53 -0
- package/scripts/_i18n-scan-hardcoded.mjs +180 -0
- package/scripts/_i18n-verify-keys.mjs +50 -0
- package/scripts/_loop-biax-campaign.mjs +130 -0
- package/scripts/_sweep-button-contrast.mjs +87 -0
- package/scripts/test-serial-bridge.mjs +418 -0
- package/scripts/ui/e2e-interactive.mjs +328 -0
- package/scripts/ui/record-demo.mjs +557 -0
- package/scripts/ui/seed-industrial-demo.mjs +171 -0
- package/server/api/workshop/daq/index.get.ts +3 -1
- package/server/api/workshop/dcw/index.get.ts +3 -0
- package/server/plugins/aw-plugins.ts +9 -7
- package/server/plugins-builtin/diag-bridge/index.mjs +608 -661
- package/server/plugins-builtin/rag-bridge/index.mjs +57 -133
- package/server/plugins-builtin/serial-bridge/client.mjs +210 -0
- package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -0
- package/server/plugins-builtin/serial-bridge/i18n.json +62 -0
- package/server/plugins-builtin/serial-bridge/index.mjs +247 -0
- package/server/plugins-builtin/serial-bridge/lib.mjs +208 -0
- package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -0
- package/server/services/workshop/daq/daq-controller.ts +7 -2
- package/server/services/workshop/daq/drivers.ts +89 -3
- package/server/services/workshop/dcw/dcw-controller.ts +5 -1
- package/server/services/workshop/dcw/drivers.ts +85 -2
- package/server/services/workshop/dcw/plugin-bridge.ts +45 -0
- package/server/services/workshop/plugins/host.mjs +30 -0
- package/shared/daq-protocol.ts +15 -0
- package/.output/public/_nuxt/88GOowgO2.js +0 -1
- package/.output/public/_nuxt/BOt2SP4z.js +0 -1
- package/.output/public/_nuxt/BP-NyKVG.js +0 -18
- package/.output/public/_nuxt/BXxH0UQE.js +0 -1
- package/.output/public/_nuxt/BsROmY89.js +0 -1
- package/.output/public/_nuxt/ClILjvWH.js +0 -1
- package/.output/public/_nuxt/CzOCSGLe.js +0 -1
- package/.output/public/_nuxt/D8qvkfyY.js +0 -1
- package/.output/public/_nuxt/DCn7QZ5N.js +0 -2
- package/.output/public/_nuxt/DS8qj8EL.js +0 -1
- package/.output/public/_nuxt/DUjW5Yq5.js +0 -1
- package/.output/public/_nuxt/Dihnimlk.js +0 -19
- package/.output/public/_nuxt/Dqw1lpr-.js +0 -1
- package/.output/public/_nuxt/Dr9v6C0f.js +0 -14
- package/.output/public/_nuxt/Du9ZJtnj.js +0 -1
- package/.output/public/_nuxt/OmpTerminalPanel.B_Pl5Ynm.css +0 -1
- package/.output/public/_nuxt/TownView.f-YOdXRs.css +0 -1
- package/.output/public/_nuxt/_id_.CM4bIgKY.css +0 -1
- package/.output/public/_nuxt/_id_.bNV69Dtf.css +0 -1
- package/.output/public/_nuxt/agents.ZD0rVdHE.css +0 -1
- package/.output/public/_nuxt/aml.BN8ZEW4h.css +0 -1
- package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +0 -1
- package/.output/public/_nuxt/channel-templates.BOtahzPi.css +0 -1
- package/.output/public/_nuxt/daq.B0D3dPAc.css +0 -1
- package/.output/public/_nuxt/dcw.8Y7FqCVN.css +0 -1
- package/.output/public/_nuxt/pages.D7aSDlUI.css +0 -1
- package/.output/public/_nuxt/plugins.sb4nDGqr.css +0 -1
- package/.output/public/_nuxt/qr6R6Ha1.js +0 -1
- package/.output/public/_nuxt/settings.BTgR-NiE.css +0 -1
- package/.output/public/_nuxt/teams.CxnxKVVs.css +0 -1
- package/.output/public/_nuxt/tokens.BFY7qv4f.css +0 -1
- package/.output/public/_nuxt/workshop.CR_QzeAN.css +0 -1
- package/.output/server/chunks/build/_id_-BU1wfOID.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +0 -1
- package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +0 -1
- package/.output/server/chunks/build/aml-_7FdFL2p.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-p6SZNjlM.mjs.map +0 -1
- package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +0 -1
- package/.output/server/chunks/build/dcw-CoTxBlAZ.mjs.map +0 -1
- package/.output/server/chunks/build/pages-DFxjn9Xa.mjs.map +0 -1
- package/.output/server/chunks/build/plugins-lGlUr1lj.mjs.map +0 -1
- package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +0 -1
- package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +0 -1
- package/.output/server/chunks/build/tokens-C7fTM5C2.mjs.map +0 -1
- package/.output/server/chunks/build/town-qMPql9Se.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs.map +0 -1
- package/.output/server/chunks/build/workshop-BL5bhfs0.mjs.map +0 -1
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.continue.json +0 -1
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.json +0 -1
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/054bb4bc4e4217a2ab0b0c6b663747214a9af464f5568ea60e784704e340bb0f.source +0 -153
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/0554197c3d2bac203a1f5c48a5d52495d1336db1bd7aa4f4c19875810363e684.source +0 -153
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/05c203d12a954fffba942e1d626e3f5fec05b18a5aef2bac003b7884a4b77406.source +0 -431
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/145a6e6ba811af35aed31e0cc3211f53706a14980fa069ee9e3c1591139f96f0.source +0 -180
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/1ae0fb281f97899c025e368ca73f41349b01fb5f85b19e08d7ebccff49095ed5.source +0 -112
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/20ee9967aa0541c516db7575bdd4a1a745939bc7016bd10d7635903113edb8ec.source +0 -227
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/233751843aeba9d4c5263f275b8102de00443c06f286918cbd23e0b6729e01aa.source +0 -131
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/263c2e05b425e5c322e60c6475279f963bab3a873eb521b21df7fec935a46597.source +0 -160
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/2ac5eaac28eaaee2178c4b32014df8e732f9dd432a285a872f5b7f31c1395fa4.source +0 -157
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/33a0db0d2d95d2c11d0720090458e5fba1425376f5ceb5b422d1d3471be3226b.source +0 -348
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/359e474ae83db35e4615d3728de6bf694b64d4dce9b536730a3b6518f9c0494a.source +0 -296
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/37a4c3c31c4b70f6b719c53d1e9c4fcba603f31adfdd11bb6ef9f0d094923c56.source +0 -285
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/461feca1c14a695eb8b0f17a6f95fe97eeceabc0510faaea4ba6d25bde147d8d.source +0 -217
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/4cfadbda8591f29d5e1e230dc000e701b756e850441b3900fb596a4d2f6e98c2.source +0 -301
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/50ca2810cf0b248c96948994c0c404531bbad6e553166b6816b3b03bfa0f75d2.source +0 -151
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/52d9e8cb591a9ae86e238c3620a610cd386717bee222c0f37b25b2ac7e523dc4.source +0 -135
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/6c87daafa4c006560a954636a97333f6123153c9d34d96203f7ee3a14e556357.source +0 -156
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/77bc47854b577900621cacf1697f81fa2bc005af129d37614a97a3441c08f99f.source +0 -72
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/7bbb789d3a527817118cf8399ed1861d6a962ade10878fd07223435d333a7089.source +0 -469
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/86b7c1daafc8b60ba9982dde5bf1bcc74265709483c176e71f23ec6fc267451f.source +0 -259
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/8df66d62bd41984d0970e8236882080c5085ac937d07a29779463d6a03b37c76.source +0 -97
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/9962f2d1b3a85137bd40cc835725600b39f89ee02a27c06b2e0402e1eafde115.source +0 -277
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a1090ea066b792cb201739f4994e37bf7c12f52e805239a1eab87f18a537f266.source +0 -171
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/a5241091650134d0919897e086a606a3846c8b2b54f8b4c84a8696c145ac3e9e.source +0 -425
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/af19fa4c6e34d5540028be4b17600161283af3a2a2dfc61a3fdc0834436a0c14.source +0 -99
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3713d78e2b2f93905ff6be8d58a8d0a2f42dea322eb67c987fda22ff6a084d5.source +0 -325
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/b3849943c3aff102d1af815631fc843a2a52c17646a4b04696ece323e483bbec.source +0 -332
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/bac735a3803a6650b7f1eb8f587e6dc5c68ea055000c3c99a11789438b78e837.source +0 -43
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d32c24d61eb8b33c73e5a8fc7e4dc7ef437aed238ee4371c2b00617e6104090a.source +0 -154
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/d5170dc9a9879fda04fdf91d8a110d4efb6787ee38dda596d4da899883d36a72.source +0 -531
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/dc59bcd7bb397be63ba2f2c3fec23f3782dcb17d2d75a9981eeced761f33eb32.source +0 -439
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/eb5ff8cf66b92aec7bf6726781d8df74faa2b030c3aaac17d6b00c1fc04c9b32.source +0 -323
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f10615a6fae18436e70d1201b2d44fdae3174a3aa7bb4db97dea1b3ed291d721.source +0 -433
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f36d3e271668bc1260c2823542d22dbf1d0f7041ff16b1ac175bce2256e582ca.source +0 -280
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/f94449380af09f31ac352152007146a75cc80bae8d6411ffe57b2c56f933b0ff.source +0 -117
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/fe4dfd81178fd6b648335f6405d9ede71319aebde796008e50db21f8adf44c99.source +0 -160
- package/scripts/.mimosa/hook-state/sess_aeaecb58-8833-4b83-b93c-050467d21e03.mtxwtxme-61028-1f4519fbc4.baseline/ffb4e3aa548cfdadabaa44ab599f9b6a3ca6288176a2cc7a4b2fc2a12b5fb848.source +0 -101
- package/scripts/.mimosa/hook-status/sess_aeaecb58-8833-4b83-b93c-050467d21e03-d944d71bb3.json +0 -14
- package/scripts/_dbg-audit-all-pages.mjs +0 -43
- package/scripts/_dbg-daq-frames-e2e.mjs +0 -118
- package/scripts/_dbg-daq-rowstyle-verify.mjs +0 -112
- package/scripts/_dbg-hitl-timeout-e2e.mjs +0 -93
- package/scripts/_dbg-live-line-e2e.mjs +0 -279
- package/scripts/_dbg-minimal-omp.mjs +0 -77
- package/scripts/_dbg-mode-nav.mjs +0 -173
- package/scripts/_dbg-real-scenario-timeline.log +0 -55
- package/scripts/_dbg-team-repro.mjs +0 -76
- package/scripts/_dbg-town-agents-probe.mjs +0 -60
- package/scripts/_dbg-visual-acceptance.mjs +0 -63
- package/scripts/_townview_style.css +0 -1185
- package/scripts/_townview_template.html +0 -1215
- package/server/data/agent-node-bindings.json +0 -554
- package/server/data/daq-templates.json +0 -16
- package/server/data/daqs.json +0 -7464
- package/server/data/dcw-lines.json +0 -1787
- package/server/data/dcw-products.json +0 -1626
- package/server/data/dcw-recipes.json +0 -4672
- package/server/data/dcw-rollback.json +0 -20754
- package/server/data/dcw-runs.json +0 -6354
- package/server/data/dcw-templates.json +0 -146
- package/server/data/dcw-writes.json +0 -15650
- package/server/data/dcws.json +0 -9033
- package/server/data/device-twins.json +0 -133
- package/server/data/line-runs.json +0 -322
- 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')
|