agentworkshop 0.7.38 → 0.7.40
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/.AgentWorkShop/prompts/aml-mission-default.md +20 -20
- package/.AgentWorkShop/prompts/aml-playbook.md +49 -49
- package/.AgentWorkShop/prompts/host-tools.json +1293 -1238
- package/.AgentWorkShop/prompts/peer-reply-cross-channel.md +10 -10
- package/.output/.AgentWorkShop/prompts/aml-mission-default.md +20 -20
- package/.output/.AgentWorkShop/prompts/aml-playbook.md +49 -49
- package/.output/.AgentWorkShop/prompts/host-tools.json +1293 -1238
- package/.output/.AgentWorkShop/prompts/peer-reply-cross-channel.md +10 -10
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/88GOowgO2.js +1 -0
- package/.output/public/_nuxt/{BNT8gYU92.js → BC9wjTJK2.js} +2 -2
- package/.output/public/_nuxt/BOt2SP4z.js +1 -0
- package/.output/public/_nuxt/BP-NyKVG.js +18 -0
- package/.output/public/_nuxt/{HBLJ-wei.js → BXxH0UQE.js} +1 -1
- package/.output/public/_nuxt/{zIGh7R49.js → BneN7fbu.js} +1 -1
- package/.output/public/_nuxt/BsROmY89.js +1 -0
- package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
- package/.output/public/_nuxt/CFxtLwjp.js +1 -0
- package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
- package/.output/public/_nuxt/C_iYD-EB.js +1 -0
- package/.output/public/_nuxt/{DAe0h2MB.js → ClILjvWH.js} +1 -1
- package/.output/public/_nuxt/CssgYAAL2.js +1 -0
- package/.output/public/_nuxt/{CY9LbPCI.js → CzOCSGLe.js} +1 -1
- package/.output/public/_nuxt/D8qvkfyY.js +1 -0
- package/.output/public/_nuxt/DCn7QZ5N.js +2 -0
- package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
- package/.output/public/_nuxt/{CojCTLKD.js → DGj-WEBg.js} +1 -1
- package/.output/public/_nuxt/{DDShH71m.js → DS8qj8EL.js} +1 -1
- package/.output/public/_nuxt/DTFCy6h-.js +1 -0
- package/.output/public/_nuxt/{DrYV5i68.js → DUjW5Yq5.js} +1 -1
- package/.output/public/_nuxt/DZshyNLa.js +1 -0
- package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
- package/.output/public/_nuxt/DcsCcxp4.js +1 -0
- package/.output/public/_nuxt/{DUrecWPx.js → Dihnimlk.js} +2 -2
- package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
- package/.output/public/_nuxt/{BG8sCcwx.js → Dr9v6C0f.js} +9 -9
- package/.output/public/_nuxt/{8NMOXZOV.js → Du9ZJtnj.js} +1 -1
- package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
- package/.output/public/_nuxt/_id_.CM4bIgKY.css +1 -0
- package/.output/public/_nuxt/_id_.bNV69Dtf.css +1 -0
- package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
- package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
- package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +1 -0
- package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
- package/.output/public/_nuxt/daq.B0D3dPAc.css +1 -0
- package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
- package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
- package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -0
- package/.output/public/_nuxt/logs.BpKzmKsh.css +1 -0
- package/.output/public/_nuxt/monitor.D9pe7qN6.css +1 -0
- package/.output/public/_nuxt/pages.D7aSDlUI.css +1 -0
- package/.output/public/_nuxt/permissions.DwTVlFe6.css +1 -0
- package/.output/public/_nuxt/plugins.sb4nDGqr.css +1 -0
- package/.output/public/_nuxt/{Cbq-mryP.js → qr6R6Ha1.js} +1 -1
- package/.output/public/_nuxt/settings.BTgR-NiE.css +1 -0
- package/.output/public/_nuxt/teams.CxnxKVVs.css +1 -0
- package/.output/public/_nuxt/tokens.BFY7qv4f.css +1 -0
- package/.output/public/_nuxt/{town.DKIAhKnx.css → town.BBXZmeoP.css} +1 -1
- package/.output/public/_nuxt/users.DLgZ1D0h.css +1 -0
- package/.output/public/_nuxt/workshop.CR_QzeAN.css +1 -0
- package/.output/public/_nuxt/{BzHEilTt.js → xh3_vYcu.js} +1 -1
- package/.output/public/_nuxt/yF0aYMF1.js +4 -0
- package/.output/server/chunks/_/entity.mjs.map +1 -1
- package/.output/server/chunks/_/nitro.mjs +2244 -1464
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView-BuLAsmpR.mjs} +288 -206
- package/.output/server/chunks/build/TownView-BuLAsmpR.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-BU1wfOID.mjs} +138 -138
- package/.output/server/chunks/build/{_id_-CfVosNg3.mjs.map → _id_-BU1wfOID.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-BYsEua57.mjs} +33 -32
- package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BxlDcJHg.mjs} +293 -259
- package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BxlDcJHg.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-Bvin73QY.mjs → agents-CLQqJF2y.mjs} +21 -27
- package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +1 -0
- package/.output/server/chunks/build/{aml-BCSJCdX7.mjs → aml-_7FdFL2p.mjs} +109 -109
- package/.output/server/chunks/build/{aml-BCSJCdX7.mjs.map → aml-_7FdFL2p.mjs.map} +1 -1
- package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -1
- package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs → channel-templates-p6SZNjlM.mjs} +19 -19
- package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs.map → channel-templates-p6SZNjlM.mjs.map} +1 -1
- package/.output/server/chunks/build/{daq-C03Uu5UE.mjs → daq-cgyjKmJu.mjs} +188 -122
- package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-CoTxBlAZ.mjs} +45 -45
- package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-CoTxBlAZ.mjs.map} +1 -1
- package/.output/server/chunks/build/{default-hseyOqBT.mjs → default-BE4r0FZN.mjs} +158 -79
- package/.output/server/chunks/build/{default-hseyOqBT.mjs.map → default-BE4r0FZN.mjs.map} +1 -1
- package/.output/server/chunks/build/{logs-B-xhuqln.mjs → logs-BSOeZwwI.mjs} +34 -34
- package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs-BSOeZwwI.mjs.map} +1 -1
- package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs → monitor-C0okQaXz.mjs} +33 -33
- package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs.map → monitor-C0okQaXz.mjs.map} +1 -1
- package/.output/server/chunks/build/{pages-BO7yZsXA.mjs → pages-DFxjn9Xa.mjs} +33 -17
- package/.output/server/chunks/build/pages-DFxjn9Xa.mjs.map +1 -0
- package/.output/server/chunks/build/{permissions-B52oOmaO.mjs → permissions-68SsNjex.mjs} +12 -12
- package/.output/server/chunks/build/{permissions-B52oOmaO.mjs.map → permissions-68SsNjex.mjs.map} +1 -1
- package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs → plugins-lGlUr1lj.mjs} +17 -17
- package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs.map → plugins-lGlUr1lj.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-DoQ2Oq3P.mjs → settings-Ddd1qx2Z.mjs} +50 -47
- package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +1 -0
- package/.output/server/chunks/build/{teams-2kz3CusY.mjs → teams-MEDLZdMn.mjs} +38 -34
- package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs → tokens-C7fTM5C2.mjs} +14 -14
- package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs.map → tokens-C7fTM5C2.mjs.map} +1 -1
- package/.output/server/chunks/build/{town-BOl6hs9B.mjs → town-qMPql9Se.mjs} +36 -10
- package/.output/server/chunks/build/town-qMPql9Se.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs +1 -1
- package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs +1 -1
- package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
- package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
- package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
- package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
- package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
- package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
- package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
- package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
- package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
- 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/daq/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
- package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
- package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
- package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
- 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/index6.get.mjs +2 -0
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -1
- package/.output/server/chunks/routes/api/workshop/index9.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 +3 -3
- package/.output/server/chunks/virtual/entry.mjs +53 -25
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +204 -40
- package/README.md +739 -591
- package/app/app.vue +9 -3
- package/app/assets/css/main.css +517 -7
- package/app/components/AppHeader.vue +98 -24
- package/app/components/AppSidebar.vue +118 -11
- package/app/components/AwChart.vue +2 -1
- package/app/components/workshop/AgentInspectorDrawer.vue +6 -6
- package/app/components/workshop/AgentLanesView.vue +52 -1
- package/app/components/workshop/AssetLibrary.vue +4 -2
- package/app/components/workshop/ChannelSessionList.vue +4 -2
- package/app/components/workshop/Composer.vue +96 -0
- package/app/components/workshop/DaqNodeRow.vue +19 -0
- package/app/components/workshop/DaqTemplateIcon.vue +33 -0
- package/app/components/workshop/DeviceTwinPanel.vue +37 -35
- package/app/components/workshop/EventBlock.vue +3 -3
- package/app/components/workshop/InspectorPanel.vue +47 -11
- package/app/components/workshop/LiveAgentBar.vue +34 -0
- package/app/components/workshop/TaskBoardView.vue +83 -33
- package/app/components/workshop/TranscriptTimeline.vue +56 -0
- package/app/components/workshop/blocks/ClusterError.vue +1 -1
- package/app/components/workshop/blocks/ClusterRoute.vue +3 -1
- package/app/components/workshop/blocks/ClusterStatus.vue +2 -2
- package/app/components/workshop/blocks/ClusterStream.vue +8 -2
- package/app/components/workshop/town/ModelPreview3D.vue +1 -1
- package/app/components/workshop/town/TownView.vue +507 -82
- package/app/composables/useResponsive.ts +88 -0
- package/app/composables/useRouteMeta.ts +3 -0
- package/app/composables/workshop/useDcwStream.ts +1 -1
- package/app/composables/workshop/useVisibleInterval.ts +2 -2
- package/app/composables/workshop/useWorkshopApi.ts +1 -1
- package/app/layouts/default.vue +46 -2
- package/app/pages/aml/index.vue +712 -616
- package/app/pages/daq/[id].vue +3 -2
- package/app/pages/daq/index.vue +209 -9
- package/app/pages/dcw/[id].vue +109 -28
- package/app/pages/dcw/index.vue +83 -23
- package/app/pages/index.vue +35 -1
- package/app/pages/logs/index.vue +147 -13
- package/app/pages/monitor.vue +56 -12
- package/app/pages/permissions/index.vue +34 -0
- package/app/pages/plugins/index.vue +35 -4
- package/app/pages/settings.vue +90 -21
- package/app/pages/tokens.vue +23 -0
- package/app/pages/town.vue +50 -5
- package/app/pages/users.vue +23 -0
- package/app/pages/workshop/agents.vue +92 -2
- package/app/pages/workshop/channel-templates.vue +53 -0
- package/app/pages/workshop/index.vue +14 -0
- package/app/pages/workshop/teams.vue +66 -6
- package/app/pages/workshop/w/[wsId].vue +202 -14
- package/app/plugins/aw-plugins.client.ts +13 -8
- package/app/stores/app.ts +33 -1
- package/app/stores/workshop/user.ts +1 -1
- package/app/stores/workshop/workspaces.ts +18 -2
- package/app/utils/api-error.ts +1 -1
- package/cli/commands/build.mjs +8 -1
- package/docker-compose.yml +6 -0
- package/docs/full-test-plan.md +4 -0
- package/docs/plugins.en.md +1 -0
- package/docs/plugins.md +1 -0
- package/i18n/locales/en.ts +3 -0
- package/i18n/locales/zh-CN.ts +3 -0
- package/nuxt.config.ts +31 -0
- package/package.json +9 -1
- package/scripts/_audit/detached-start.mjs +103 -0
- package/scripts/_audit/guard-upstream-liveness.mjs +104 -0
- package/scripts/_dbg-param-map-e2e.ts +242 -0
- package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
- package/scripts/_dbg-param-real-scenario.mjs +153 -0
- package/scripts/_dbg-perms-e2e.mjs +10 -3
- package/scripts/_dbg-protocol-matrix.mjs +453 -441
- package/scripts/_dbg-render-regression.mjs +151 -130
- package/scripts/_dbg-town-agents-probe.mjs +60 -0
- package/scripts/_dbg-visual-acceptance.mjs +63 -0
- package/scripts/api-live-e2e.mjs +40 -8
- package/scripts/capture-walkthrough.mjs +232 -0
- package/scripts/e2e-agent-team.mjs +5 -3
- package/scripts/render-walkthrough-figure.mjs +46 -0
- package/scripts/seed-walkthrough-fixture.mjs +143 -0
- package/scripts/test-plugin-hardening.mjs +8 -0
- package/scripts/three-system-e2e.mjs +6 -6
- package/scripts/ui/admin-shots.mjs +46 -0
- package/scripts/ui/audit-layout.mjs +301 -0
- package/scripts/ui/bump-microfont.mjs +39 -0
- package/scripts/ui/lib.mjs +198 -0
- package/scripts/ui/linkcheck2.mjs +37 -0
- package/scripts/ui/live-render-check.mjs +72 -0
- package/scripts/ui/md-lint.mjs +80 -0
- package/scripts/ui/perf-audit.mjs +132 -0
- package/scripts/ui/readme-assets.mjs +30 -0
- package/scripts/ui/readme-shots.mjs +42 -0
- package/scripts/ui/record.mjs +225 -0
- package/scripts/ui/seed-admin.mjs +68 -0
- package/scripts/ui/seed-demo.mjs +83 -0
- package/scripts/ui/shot-hero.mjs +25 -0
- package/scripts/ui/shots.mjs +75 -0
- package/scripts/ui/sim-ui-shot.mjs +16 -0
- package/scripts/ui/verify-nav.mjs +51 -0
- package/scripts/ui/verify-responsive.mjs +285 -0
- package/server/api/system/config-groups/[id].patch.ts +1 -1
- package/server/api/system/config-groups/index.post.ts +1 -1
- package/server/api/system/settings/events.get.ts +1 -1
- package/server/api/system/settings/index.patch.ts +1 -1
- package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
- package/server/api/workshop/aml/datasets/[id].get.ts +8 -1
- package/server/api/workshop/daq/infra/reconnect.post.ts +1 -1
- package/server/api/workshop/daq/templates/[key].delete.ts +1 -0
- package/server/api/workshop/daq/templates/[key].patch.ts +1 -0
- package/server/api/workshop/daq/templates/index.post.ts +1 -0
- package/server/api/workshop/dcw/[id]/write.post.ts +10 -2
- package/server/api/workshop/dcw/[id].delete.ts +5 -2
- package/server/api/workshop/dcw/index.get.ts +2 -0
- package/server/api/workshop/dcw/lines/[id].patch.ts +1 -1
- package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -0
- package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -0
- package/server/api/workshop/dcw/params/[id].delete.ts +19 -0
- package/server/api/workshop/dcw/params/[id].patch.ts +23 -0
- package/server/api/workshop/dcw/params/index.get.ts +27 -0
- package/server/api/workshop/dcw/params/index.post.ts +25 -0
- package/server/api/workshop/permissions/index.get.ts +3 -1
- package/server/plugins/aw-plugins.ts +14 -3
- package/server/plugins/dev-stability-guard.ts +3 -1
- package/server/plugins/system-config.ts +6 -1
- package/server/plugins-builtin/rag-bridge/index.mjs +9 -2
- package/server/services/system-config.ts +28 -9
- package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
- package/server/services/workshop/agents/agent-interface.ts +8 -6
- package/server/services/workshop/agents/base-agent.ts +18 -4
- package/server/services/workshop/agents/claude-agent.ts +7 -3
- package/server/services/workshop/agents/codex-agent.ts +3 -3
- package/server/services/workshop/agents/dsh-agent.ts +3 -3
- package/server/services/workshop/agents/goose-agent.ts +0 -1
- package/server/services/workshop/agents/harness-terminal.ts +14 -1
- package/server/services/workshop/agents/hermes-agent.ts +4 -2
- package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
- package/server/services/workshop/agents/industrial-context.ts +4 -1
- package/server/services/workshop/agents/industrial-tools.ts +164 -6
- package/server/services/workshop/agents/omp-agent.ts +15 -9
- package/server/services/workshop/agents/opencode-agent.ts +47 -21
- package/server/services/workshop/agents/qwen-agent.ts +10 -5
- package/server/services/workshop/aml/entity.ts +3 -1
- package/server/services/workshop/aml/python-runtime.ts +3 -2
- package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
- package/server/services/workshop/daq/daq-controller.ts +18 -3
- package/server/services/workshop/daq/daq-runtime.ts +5 -3
- package/server/services/workshop/daq/drivers.ts +63 -8
- package/server/services/workshop/daq/host-ports.ts +3 -2
- package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
- package/server/services/workshop/db/channel.repo.ts +4 -0
- package/server/services/workshop/db/ops.repo.ts +5 -2
- package/server/services/workshop/db/retention.ts +8 -3
- package/server/services/workshop/dcw/dcw-controller.ts +143 -16
- package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
- package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
- package/server/services/workshop/dcw/drivers.ts +5 -2
- package/server/services/workshop/dcw/param-conversion.ts +78 -0
- package/server/services/workshop/dcw/param-limits.ts +142 -0
- package/server/services/workshop/dcw/param-map.repo.ts +305 -0
- package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
- package/server/services/workshop/runtime/agent-runtime.ts +4 -0
- package/server/services/workshop/runtime/manager.ts +9 -2
- package/server/services/workshop/settings.ts +4 -2
- package/server/types/pg.d.ts +49 -0
- package/server/utils/stability-severity.ts +13 -2
- package/shared/config/groups.mjs +22 -1
- package/shared/config/home.d.mts +28 -0
- package/shared/dcw-protocol.ts +136 -0
- package/uno.config.ts +12 -1
- package/.output/public/_nuxt/BFkgkW8u.js +0 -4
- package/.output/public/_nuxt/C5gUkRwn.js +0 -1
- package/.output/public/_nuxt/CM12FuB42.js +0 -1
- package/.output/public/_nuxt/CZiw7sEV.js +0 -1
- package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
- package/.output/public/_nuxt/CdPzkOMV.js +0 -1
- package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
- package/.output/public/_nuxt/D20Yamr4.js +0 -1
- package/.output/public/_nuxt/DVsjtsvZ2.js +0 -1
- package/.output/public/_nuxt/DmqknNvV.js +0 -18
- package/.output/public/_nuxt/DnUuIR83.js +0 -1
- package/.output/public/_nuxt/S_fVQwJK.js +0 -1
- package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
- package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
- package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
- package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
- package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
- package/.output/public/_nuxt/ahgEJu2R.js +0 -1
- package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
- package/.output/public/_nuxt/builds/meta/44b66249-54d0-4c8a-9057-9755a32329e2.json +0 -1
- package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
- package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
- package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
- package/.output/public/_nuxt/default.DE_vT238.css +0 -1
- package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
- package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
- package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
- package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
- package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
- package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
- package/.output/public/_nuxt/pz2JDAM7.js +0 -2
- package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
- package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
- package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
- package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
- package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
- package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
- package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
- package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
- package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
- package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
- package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
- package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
- package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
- /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
- /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical walkthrough-figure capture. High-DPI (3x), element/row-accurate crops
|
|
3
|
+
* of the four operational stages on a live AgentWorkShop instance.
|
|
4
|
+
*
|
|
5
|
+
* The shared production instance carries Chinese-named test data, so the four
|
|
6
|
+
* screenshots are taken against the small ENGLISH fixture created by
|
|
7
|
+
* scripts/seed-walkthrough-fixture.mjs (line "Cast-film line A", English DAQ
|
|
8
|
+
* nodes, an English agent team). The UI is forced to the `en` locale (persisted
|
|
9
|
+
* in localStorage under `aw.locale`, applied by app/plugins/locale-restore.client.ts)
|
|
10
|
+
* before any capture, so every UI string is English.
|
|
11
|
+
*
|
|
12
|
+
* Geometry discipline: every crop is ≤470 CSS px wide (12 px UI text must print
|
|
13
|
+
* ≥ ~6.4 pt at the ~88 mm panel width of the 181 mm figure), except panel 2 whose
|
|
14
|
+
* four table columns cannot fit narrower.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* NO_PROXY='127.0.0.1,localhost' node scripts/seed-walkthrough-fixture.mjs
|
|
18
|
+
* NO_PROXY='127.0.0.1,localhost' AW_BASE=http://127.0.0.1:3005 node scripts/capture-walkthrough.mjs
|
|
19
|
+
*/
|
|
20
|
+
import puppeteer from 'puppeteer-core'
|
|
21
|
+
import fs from 'node:fs'
|
|
22
|
+
import path from 'node:path'
|
|
23
|
+
|
|
24
|
+
const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3005'
|
|
25
|
+
const OUT = process.env.AW_OUT ?? 'paper/tii/figures/walkthrough'
|
|
26
|
+
fs.mkdirSync(OUT, { recursive: true })
|
|
27
|
+
|
|
28
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
29
|
+
const tok = async (e, p) => {
|
|
30
|
+
const r = await (await fetch(`${BASE}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: e, password: p }) })).json()
|
|
31
|
+
if (!r.data?.token) throw new Error(`login failed: ${e} (${JSON.stringify(r).slice(0, 120)})`)
|
|
32
|
+
return r.data.token
|
|
33
|
+
}
|
|
34
|
+
const ADMIN = await tok('admin@awshop.local', 'admin123')
|
|
35
|
+
|
|
36
|
+
const jget = async (u) => {
|
|
37
|
+
const r = await (await fetch(`${BASE}${u}`, { headers: { Authorization: `Bearer ${ADMIN}` } })).json()
|
|
38
|
+
return r.data
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── fixture discovery (fail loudly rather than silently capturing Chinese) ──
|
|
42
|
+
const LINES = (await jget('/api/workshop/dcw/lines')).lines
|
|
43
|
+
const LINE = LINES.find(l => l.name === 'Cast-film line A')
|
|
44
|
+
if (!LINE) throw new Error('English fixture line "Cast-film line A" missing — run scripts/seed-walkthrough-fixture.mjs first')
|
|
45
|
+
const DAQ = await jget('/api/workshop/daq')
|
|
46
|
+
const modbusNode = DAQ.nodes.find(n => n.lineId === LINE.id && n.driver === 'modbus-tcp')
|
|
47
|
+
if (!modbusNode) throw new Error('fixture Modbus-TCP node missing on Cast-film line A')
|
|
48
|
+
// panel 4 uses the real judged optimization records already on the ledger of this node
|
|
49
|
+
const RECORD_LINE = 'ln-af002514'
|
|
50
|
+
const RECORD_NODE = 'dw-322b1978'
|
|
51
|
+
console.log(`fixture line=${LINE.id} modbus=${modbusNode.id} (${modbusNode.name}) records=${RECORD_NODE}`)
|
|
52
|
+
|
|
53
|
+
const browser = await puppeteer.launch({
|
|
54
|
+
executablePath: process.env.AW_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
55
|
+
headless: 'new',
|
|
56
|
+
args: ['--no-sandbox', '--disable-gpu', '--proxy-server=direct://', '--proxy-bypass-list=*'],
|
|
57
|
+
})
|
|
58
|
+
const page = await browser.newPage()
|
|
59
|
+
const errs = []
|
|
60
|
+
page.on('pageerror', e => errs.push(String(e).slice(0, 120)))
|
|
61
|
+
await page.setViewport({ width: 1680, height: 1600, deviceScaleFactor: 3 })
|
|
62
|
+
|
|
63
|
+
const auth = async (t) => { const c = await page.target().createCDPSession(); await c.send('Network.clearBrowserCookies'); await page.setCookie({ name: 'token', value: t, domain: '127.0.0.1', path: '/' }) }
|
|
64
|
+
const goto = async (u, w = 10000) => { await page.goto(`${BASE}${u}`, { waitUntil: 'domcontentloaded', timeout: 90_000 }); await sleep(w) }
|
|
65
|
+
|
|
66
|
+
// content-visibility:auto sections report estimated rects until rendered; force real
|
|
67
|
+
// layout so element-accurate clips do not drift. Also kill CSS transitions/animations
|
|
68
|
+
// so a mid-transition frame (e.g. the hold-interval cell double-painting its value)
|
|
69
|
+
// can never be captured.
|
|
70
|
+
const settle = async () => {
|
|
71
|
+
await page.evaluate(() => {
|
|
72
|
+
let st = document.getElementById('aw-capture-calm')
|
|
73
|
+
if (!st) {
|
|
74
|
+
st = document.createElement('style')
|
|
75
|
+
st.id = 'aw-capture-calm'
|
|
76
|
+
st.textContent = '*,*::before,*::after{transition:none!important;transition-duration:0s!important;animation:none!important;animation-duration:0s!important;animation-delay:0s!important}'
|
|
77
|
+
document.head.appendChild(st)
|
|
78
|
+
}
|
|
79
|
+
for (const e of document.querySelectorAll('*')) {
|
|
80
|
+
const cs = getComputedStyle(e)
|
|
81
|
+
if (cs.contentVisibility && cs.contentVisibility !== 'visible') e.style.contentVisibility = 'visible'
|
|
82
|
+
}
|
|
83
|
+
window.scrollTo(0, document.body.scrollHeight)
|
|
84
|
+
})
|
|
85
|
+
await sleep(700)
|
|
86
|
+
await page.evaluate(() => window.scrollTo(0, 0))
|
|
87
|
+
await sleep(300)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Grow the viewport to the full document height BEFORE measuring, so that the
|
|
91
|
+
// screenshot's captureBeyondViewport expansion cannot re-layout the page and
|
|
92
|
+
// shift elements between measurement and capture.
|
|
93
|
+
const expand = async () => {
|
|
94
|
+
const h = await page.evaluate(() => document.documentElement.scrollHeight)
|
|
95
|
+
await page.setViewport({ width: 1680, height: Math.min(Math.ceil(h) + 40, 14000), deviceScaleFactor: 3 })
|
|
96
|
+
await sleep(1200)
|
|
97
|
+
await settle()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const repaint = async () => {
|
|
101
|
+
await page.evaluate(() => new Promise(res => {
|
|
102
|
+
window.scrollTo(0, 0)
|
|
103
|
+
requestAnimationFrame(() => requestAnimationFrame(res))
|
|
104
|
+
}))
|
|
105
|
+
await sleep(120)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Capture only once the pixels stop changing: two consecutive identical captures are
|
|
109
|
+
// required (guards against mid-transition / late-commit frames like double-struck text).
|
|
110
|
+
const shot = async (n, x, y, w, h, tries = 5) => {
|
|
111
|
+
const clip = { x, y, width: w, height: h }
|
|
112
|
+
let prev = null
|
|
113
|
+
let stable = false
|
|
114
|
+
for (let i = 0; i < tries; i++) {
|
|
115
|
+
await repaint()
|
|
116
|
+
const buf = Buffer.from(await page.screenshot({ encoding: 'binary', clip, captureBeyondViewport: true }))
|
|
117
|
+
if (prev && Buffer.compare(prev, buf) === 0) { prev = buf; stable = true; break }
|
|
118
|
+
prev = buf
|
|
119
|
+
await sleep(1000)
|
|
120
|
+
}
|
|
121
|
+
fs.writeFileSync(path.join(OUT, n), prev)
|
|
122
|
+
console.log(` ✓ ${n} ${Math.round(w)}x${Math.round(h)} css @3x ${stable ? 'stable' : 'NOT STABLE (last frame written)'}`)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── force English UI before any capture ──────────────────────────────────
|
|
126
|
+
await auth(ADMIN)
|
|
127
|
+
await goto('/daq', 4000)
|
|
128
|
+
await page.evaluate(() => localStorage.setItem('aw.locale', 'en'))
|
|
129
|
+
await page.reload({ waitUntil: 'domcontentloaded' })
|
|
130
|
+
await sleep(6000)
|
|
131
|
+
const heads = await page.evaluate(() => [...document.querySelectorAll('.nodes-table thead th')].map(t => t.textContent.trim()))
|
|
132
|
+
if (!heads.some(h => /Sampling/i.test(h))) throw new Error(`locale not English (headers: ${JSON.stringify(heads)})`)
|
|
133
|
+
console.log(`locale : en (headers: ${heads.join(' | ')})`)
|
|
134
|
+
|
|
135
|
+
// ══ ① provision & connect — /daq nodes filtered to the English line ══════
|
|
136
|
+
console.log('\n== ① /daq ==')
|
|
137
|
+
await page.evaluate((ln) => {
|
|
138
|
+
for (const s of document.querySelectorAll('select')) {
|
|
139
|
+
if ([...s.options].some(o => o.value === ln)) { s.value = ln; s.dispatchEvent(new Event('change', { bubbles: true })); break }
|
|
140
|
+
}
|
|
141
|
+
}, LINE.id)
|
|
142
|
+
await sleep(3500)
|
|
143
|
+
await settle()
|
|
144
|
+
await expand()
|
|
145
|
+
|
|
146
|
+
const G = await page.evaluate(() => {
|
|
147
|
+
const tbl = document.querySelector('.nodes-table')
|
|
148
|
+
const rect = tbl.getBoundingClientRect()
|
|
149
|
+
const ths = [...tbl.querySelectorAll('thead th')].map(t => ({ x: Math.round(t.getBoundingClientRect().x - rect.x), text: t.textContent.trim() }))
|
|
150
|
+
const rows = [...tbl.querySelectorAll('tbody tr')].map((tr) => {
|
|
151
|
+
const q = tr.getBoundingClientRect()
|
|
152
|
+
const tds = tr.querySelectorAll('td')
|
|
153
|
+
return { y: Math.round(q.y + window.scrollY), h: q.height, name: (tr.querySelector('b')?.textContent || '').trim(), driver: (tds[6]?.textContent || '').trim() }
|
|
154
|
+
})
|
|
155
|
+
return { x: rect.x, ths, rows }
|
|
156
|
+
})
|
|
157
|
+
console.log(' rows:', G.rows.map(r => `${r.name}[${r.driver}]`).join(', '))
|
|
158
|
+
if (!G.rows.some(r => r.driver.includes('modbus'))) throw new Error('no Modbus-TCP row visible on filtered line')
|
|
159
|
+
// columns: 4 Sampling · 5 WS Publish · 6 Driver · 7 Line (indices are locale-stable).
|
|
160
|
+
// Keep the crop ≤470 css px: 12 px UI text must print ≥ ~6.4 pt at the 88 mm panel width.
|
|
161
|
+
const mbIdx = G.rows.findIndex(r => r.driver.includes('modbus'))
|
|
162
|
+
const mkIdx = mbIdx > 0 ? mbIdx - 1 : 0
|
|
163
|
+
const x0 = G.x + G.ths[4].x
|
|
164
|
+
const x1 = G.x + G.ths[8].x
|
|
165
|
+
const yS = G.rows[mkIdx].y
|
|
166
|
+
const yE = G.rows[mbIdx].y + G.rows[mbIdx].h
|
|
167
|
+
console.log(` pair: ${G.rows[mkIdx].name}[${G.rows[mkIdx].driver}] + ${G.rows[mbIdx].name}[${G.rows[mbIdx].driver}]`)
|
|
168
|
+
await shot('p1-driver.png', x0 - 2, yS, Math.min(x1 - x0 + 4, 470), yE - yS)
|
|
169
|
+
|
|
170
|
+
// ══ ② bind node → line / device — /dcw/{English line} ═══════════════════
|
|
171
|
+
console.log('\n== ② /dcw/' + LINE.id + ' ==')
|
|
172
|
+
await goto(`/dcw/${LINE.id}`, 9000)
|
|
173
|
+
await settle()
|
|
174
|
+
await expand()
|
|
175
|
+
const D = await page.evaluate(() => {
|
|
176
|
+
const t = document.querySelector('.aw-tile.table-card table')
|
|
177
|
+
const rect = t.getBoundingClientRect()
|
|
178
|
+
const ths = [...t.querySelectorAll('thead th')].map(x => ({ x: Math.round(x.getBoundingClientRect().x - rect.x), text: x.textContent.trim() }))
|
|
179
|
+
return { x: rect.x, y: rect.y + window.scrollY, h: rect.height, ths }
|
|
180
|
+
})
|
|
181
|
+
console.log(' cols:', D.ths.map(c => c.text).join(' | '))
|
|
182
|
+
// columns: 5 Setpoint · 6 Process Range · 7 Hold Interval · 8 Bound Device
|
|
183
|
+
const c0 = D.x + D.ths[5].x
|
|
184
|
+
const c1 = D.x + D.ths[9].x
|
|
185
|
+
await shot('p2-binding.png', c0 - 6, D.y, (c1 - c0) + 12, D.h)
|
|
186
|
+
|
|
187
|
+
// ══ ④ governed closed loop — ledger optimization records (real, judged) ══
|
|
188
|
+
console.log('\n== ④ /dcw/' + RECORD_LINE + ' ledger ==')
|
|
189
|
+
await goto(`/dcw/${RECORD_LINE}`, 9000)
|
|
190
|
+
await page.evaluate((nid) => {
|
|
191
|
+
const s = document.querySelector('.ledger-card select.inp')
|
|
192
|
+
if (!s) return
|
|
193
|
+
s.value = nid
|
|
194
|
+
s.dispatchEvent(new Event('change', { bubbles: true }))
|
|
195
|
+
}, RECORD_NODE)
|
|
196
|
+
await sleep(3500)
|
|
197
|
+
await settle()
|
|
198
|
+
await expand()
|
|
199
|
+
const L = await page.evaluate(() => {
|
|
200
|
+
const card = document.querySelector('.ledger-card')
|
|
201
|
+
const col = [...card.querySelectorAll('.ledger-col')].find(c => c.querySelector('li.ledger-rec')) || [...card.querySelectorAll('.ledger-col')].pop()
|
|
202
|
+
const recs = [...col.querySelectorAll('li.ledger-rec')]
|
|
203
|
+
const r = col.getBoundingClientRect()
|
|
204
|
+
const last = recs[recs.length - 1].getBoundingClientRect()
|
|
205
|
+
return { x: r.x, y: r.y + window.scrollY, w: r.width, h: last.bottom - r.top, texts: recs.map(li => li.textContent.replace(/\s+/g, ' ').trim()) }
|
|
206
|
+
})
|
|
207
|
+
console.log(' records:', JSON.stringify(L.texts))
|
|
208
|
+
// tight: header + the real records, without the empty box below them
|
|
209
|
+
await shot('p4-records.png', L.x - 5, L.y - 5, Math.min(L.w + 14, 470), L.h + 14)
|
|
210
|
+
|
|
211
|
+
// ══ ③ compose & deploy the agent team — /workshop/teams ═════════════════
|
|
212
|
+
// Desktop viewport: one card in the multi-column grid (~343 px wide), which keeps the
|
|
213
|
+
// crop ≤470 css px so member text prints ≥ ~6 pt at the 88 mm panel width.
|
|
214
|
+
console.log('\n== ③ /workshop/teams ==')
|
|
215
|
+
await page.setViewport({ width: 1680, height: 1600, deviceScaleFactor: 3 })
|
|
216
|
+
await goto('/workshop/teams', 9000)
|
|
217
|
+
await settle()
|
|
218
|
+
await expand()
|
|
219
|
+
const cards = await page.evaluate(() => [...document.querySelectorAll('.grid .card')].map(e => {
|
|
220
|
+
const r = e.getBoundingClientRect()
|
|
221
|
+
const members = e.querySelectorAll('.member')
|
|
222
|
+
const last = members[members.length - 1]?.getBoundingClientRect()
|
|
223
|
+
return { x: Math.round(r.x), y: Math.round(r.y + window.scrollY), w: Math.round(r.width), h: Math.round(r.height), memberH: last ? Math.round(last.bottom - r.top) : Math.round(r.height), t: e.textContent.replace(/\s+/g, ' ').slice(0, 40) }
|
|
224
|
+
}))
|
|
225
|
+
console.log(' cards:', JSON.stringify(cards))
|
|
226
|
+
const tgt = cards.find(c => c.t.includes('Cast-film control team'))
|
|
227
|
+
if (!tgt) throw new Error('English team card not found on /workshop/teams')
|
|
228
|
+
// crop head + member rows only (excludes the Add-Member button) to keep the figure compact
|
|
229
|
+
await shot('p3-team.png', tgt.x - 5, tgt.y - 5, Math.min(tgt.w + 10, 470), tgt.memberH + 8)
|
|
230
|
+
|
|
231
|
+
console.log('\npageerrors:', errs.length ? errs.join(' | ') : 'none')
|
|
232
|
+
await browser.close()
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* 5. 成员移除 / team 更新 / 删除
|
|
8
8
|
* 运行: node scripts/e2e-agent-team.mjs [--base http://127.0.0.1:3000]
|
|
9
9
|
*/
|
|
10
|
-
const BASE = (process.argv.find(a => a.startsWith('--base='))?.slice(7) ?? 'http://127.0.0.1:3000') + '/api/workshop'
|
|
10
|
+
const BASE = (process.argv.find(a => a.startsWith('--base='))?.slice(7) ?? 'http://127.0.0.1:3000') + '/api/workshop'
|
|
11
|
+
// 注册走平台 origin 的 /api/users/register(不在 /api/workshop 下;注册现要求 email+password)
|
|
12
|
+
const ORIGIN = BASE.slice(0, BASE.indexOf('/api'))
|
|
11
13
|
|
|
12
14
|
let pass = 0
|
|
13
15
|
let fail = 0
|
|
@@ -21,9 +23,9 @@ function check(name, ok, detail = '') {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
// 用户级隔离:注册测试用户;管理面 API 全程携带用户 token
|
|
24
|
-
const __user = await fetch(
|
|
26
|
+
const __user = await fetch(ORIGIN + '/api/users/register', {
|
|
25
27
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
26
|
-
body: JSON.stringify({ name: 'e2e-' + Math.random().toString(36).slice(2, 10) }),
|
|
28
|
+
body: JSON.stringify({ name: 'e2e-' + Math.random().toString(36).slice(2, 10), email: `e2e-team-${Date.now()}@test.local`, password: 'Passw0rd!123' }),
|
|
27
29
|
}).then(r => r.json()).catch(() => null)
|
|
28
30
|
const __userToken = __user?.data?.token
|
|
29
31
|
if (!__userToken) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the walkthrough figure HTML to (a) an exact-size PDF via headless Chrome
|
|
3
|
+
* --print-to-pdf and (b) a high-resolution PNG for inspection/archival.
|
|
4
|
+
*
|
|
5
|
+
* Usage: node scripts/render-walkthrough-figure.mjs
|
|
6
|
+
*/
|
|
7
|
+
import { execFileSync } from 'node:child_process'
|
|
8
|
+
import fs from 'node:fs'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import puppeteer from 'puppeteer-core'
|
|
11
|
+
|
|
12
|
+
const CHROME = process.env.AW_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
|
|
13
|
+
const DIR = path.resolve('paper/tii/figures/walkthrough')
|
|
14
|
+
const SRC = path.join(DIR, 'fig-walkthrough.html')
|
|
15
|
+
if (!fs.existsSync(SRC)) throw new Error('missing ' + SRC)
|
|
16
|
+
|
|
17
|
+
// ── (a) exact-size PDF ────────────────────────────────────────────────────
|
|
18
|
+
const pdfUrl = 'file:///' + SRC.replace(/\\/g, '/')
|
|
19
|
+
const outPdf = path.resolve('paper/tii/figures/pdf/fig-walkthrough.pdf')
|
|
20
|
+
fs.mkdirSync(path.dirname(outPdf), { recursive: true })
|
|
21
|
+
execFileSync(CHROME, [
|
|
22
|
+
'--headless=new', '--disable-gpu', '--no-pdf-header-footer',
|
|
23
|
+
`--print-to-pdf=${outPdf}`, pdfUrl,
|
|
24
|
+
], { stdio: 'ignore' })
|
|
25
|
+
const info = execFileSync('pdfinfo', [outPdf], { encoding: 'utf8' })
|
|
26
|
+
console.log('PDF :', outPdf)
|
|
27
|
+
console.log(' ', info.split('\n').filter(l => /Page size|Pages/.test(l)).join(' | ').trim())
|
|
28
|
+
|
|
29
|
+
// ── (b) high-resolution PNG (overall + per-panel) ─────────────────────────
|
|
30
|
+
const browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox', '--disable-gpu', '--allow-file-access-from-files'] })
|
|
31
|
+
const page = await browser.newPage()
|
|
32
|
+
// 181 mm wide at 96 dpi/css == 684 css px; height is a minimum — fullPage grows to content
|
|
33
|
+
await page.setViewport({ width: 684, height: 200, deviceScaleFactor: 8 })
|
|
34
|
+
await page.goto(pdfUrl, { waitUntil: 'networkidle0' })
|
|
35
|
+
await page.evaluate(() => document.fonts?.ready)
|
|
36
|
+
await new Promise(r => setTimeout(r, 500))
|
|
37
|
+
await page.screenshot({ path: path.join(DIR, 'fig-walkthrough.png'), fullPage: true })
|
|
38
|
+
const png = fs.statSync(path.join(DIR, 'fig-walkthrough.png'))
|
|
39
|
+
console.log('PNG :', path.join(DIR, 'fig-walkthrough.png'), `${(png.size / 1024).toFixed(0)} KB`)
|
|
40
|
+
|
|
41
|
+
// medium-res variant for quick review
|
|
42
|
+
await page.setViewport({ width: 684, height: 200, deviceScaleFactor: 2 })
|
|
43
|
+
await page.screenshot({ path: path.join(DIR, '_preview.png'), fullPage: true })
|
|
44
|
+
console.log('PREV :', path.join(DIR, '_preview.png'))
|
|
45
|
+
|
|
46
|
+
await browser.close()
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose-built ENGLISH-locale fixture for the IEEE TII Figure 2 walkthrough.
|
|
3
|
+
*
|
|
4
|
+
* The shared production instance is full of Chinese-named test data; we must NOT
|
|
5
|
+
* rename or delete anything. Instead this script creates a small additive fixture
|
|
6
|
+
* with English entity names via the platform's own REST API, so the four
|
|
7
|
+
* walkthrough screenshots contain English strings only:
|
|
8
|
+
*
|
|
9
|
+
* - line "Cast-film line A"
|
|
10
|
+
* - DAQ nodes mock + a real Modbus-TCP node on the PLC simulator, one line
|
|
11
|
+
* - device "Die-head heater" twin, bound by a DCW node with a 150-200 C window
|
|
12
|
+
* - agents English lead + workers on the `omp` harness
|
|
13
|
+
* - team "Cast-film control team" (lead + 2 workers)
|
|
14
|
+
*
|
|
15
|
+
* Idempotent: re-running reuses rows with the same names. Prints a JSON summary.
|
|
16
|
+
*
|
|
17
|
+
* Usage: NO_PROXY='127.0.0.1,localhost' node scripts/seed-walkthrough-fixture.mjs
|
|
18
|
+
*/
|
|
19
|
+
const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3005'
|
|
20
|
+
|
|
21
|
+
const login = async (e, p) =>
|
|
22
|
+
(await (await fetch(`${BASE}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: e, password: p }) })).json()).data.token
|
|
23
|
+
|
|
24
|
+
const T = await login('admin@awshop.local', 'admin123')
|
|
25
|
+
|
|
26
|
+
const api = async (method, path, body) => {
|
|
27
|
+
const r = await fetch(`${BASE}${path}`, {
|
|
28
|
+
method,
|
|
29
|
+
headers: { Authorization: `Bearer ${T}`, ...(body ? { 'content-type': 'application/json' } : {}) },
|
|
30
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
31
|
+
})
|
|
32
|
+
const j = await r.json().catch(() => ({}))
|
|
33
|
+
if (!r.ok || j.code !== 0) throw new Error(`${method} ${path} -> ${r.status} ${JSON.stringify(j).slice(0, 200)}`)
|
|
34
|
+
return j.data
|
|
35
|
+
}
|
|
36
|
+
const get = p => api('GET', p)
|
|
37
|
+
const post = (p, b) => api('POST', p, b)
|
|
38
|
+
|
|
39
|
+
// ── 1. production line ────────────────────────────────────────────────────
|
|
40
|
+
const LINE_NAME = 'Cast-film line A'
|
|
41
|
+
const lines = (await get('/api/workshop/dcw/lines')).lines
|
|
42
|
+
let line = lines.find(l => l.name === LINE_NAME)
|
|
43
|
+
if (!line) line = (await post('/api/workshop/dcw/lines', { name: LINE_NAME, color: '#f4c542' })).line
|
|
44
|
+
console.log(`line : ${line.id} ${line.name}`)
|
|
45
|
+
|
|
46
|
+
// ── 2. DAQ nodes on that line (mock + real Modbus-TCP share one loop) ─────
|
|
47
|
+
const daq = await get('/api/workshop/daq')
|
|
48
|
+
const haveNode = n => daq.nodes.find(x => x.name === n)
|
|
49
|
+
const mkNode = async (spec) => {
|
|
50
|
+
const existing = haveNode(spec.name)
|
|
51
|
+
if (existing) return existing
|
|
52
|
+
return (await post('/api/workshop/daq', { enabled: true, intervalMs: 1000, lineId: line.id, ...spec })).node
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const nTemp = await mkNode({
|
|
56
|
+
name: 'Melt temperature', templateRef: 'daq-temp-tc', driver: 'mock',
|
|
57
|
+
unit: '\u00B0C', decimals: 1, min: 150, max: 200, warnLow: 155, warnHigh: 195,
|
|
58
|
+
})
|
|
59
|
+
const nSpeed = await mkNode({
|
|
60
|
+
name: 'Line speed', templateRef: 'daq-line-encoder', driver: 'mock',
|
|
61
|
+
unit: 'm/min', decimals: 0, min: 280, max: 360, warnLow: 290, warnHigh: 350,
|
|
62
|
+
})
|
|
63
|
+
const nPress = await mkNode({
|
|
64
|
+
name: 'Melt pressure', templateRef: 'daq-pressure-tx', driver: 'mock',
|
|
65
|
+
unit: 'MPa', decimals: 2, min: 0.6, max: 1.2, warnLow: 0.65, warnHigh: 1.15,
|
|
66
|
+
})
|
|
67
|
+
// real Modbus-TCP against the PLC simulator's "extruder" slave (melt-temp @ 40001)
|
|
68
|
+
const nModbus = await mkNode({
|
|
69
|
+
name: 'Die-head temperature', templateRef: 'daq-temp-tc', driver: 'modbus-tcp',
|
|
70
|
+
unit: '\u00B0C', decimals: 2, min: 0, max: 400, warnLow: 150, warnHigh: 260,
|
|
71
|
+
driverConfig: { host: '127.0.0.1', port: 16040, unitId: 1, register: 40001, dataType: 'float32', byteOrder: 'big' },
|
|
72
|
+
})
|
|
73
|
+
console.log(`daq : temp=${nTemp.id} speed=${nSpeed.id} pressure=${nPress.id} modbus=${nModbus.id}`)
|
|
74
|
+
|
|
75
|
+
// ── 3. device twin + DCW control node with a recipe window ────────────────
|
|
76
|
+
const twins = (await get('/api/workshop/device-twins')).twins
|
|
77
|
+
let twin = twins.find(t => t.name === 'Die-head heater')
|
|
78
|
+
if (!twin) twin = (await post('/api/workshop/device-twins', { name: 'Die-head heater', kind: 'device', controls: ['setpoint'] })).twin
|
|
79
|
+
console.log(`twin : ${twin.id} ${twin.name}`)
|
|
80
|
+
|
|
81
|
+
const dcw = await get('/api/workshop/dcw')
|
|
82
|
+
let dcwNode = dcw.nodes.find(n => n.name === 'Die-head setpoint')
|
|
83
|
+
if (!dcwNode) {
|
|
84
|
+
dcwNode = (await post('/api/workshop/dcw', {
|
|
85
|
+
name: 'Die-head setpoint', templateRef: 'dcw-temp-sp', driver: 'mock',
|
|
86
|
+
unit: '\u00B0C', decimals: 1, min: 150, max: 200,
|
|
87
|
+
holdIntervalMs: 5000, readIntervalMs: 5000, enabled: true,
|
|
88
|
+
lineId: line.id, deviceIds: [twin.id],
|
|
89
|
+
})).node
|
|
90
|
+
}
|
|
91
|
+
console.log(`dcw : ${dcwNode.id} ${dcwNode.name} bound=${twin.id} window=${dcwNode.min}-${dcwNode.max}`)
|
|
92
|
+
|
|
93
|
+
// ── 4. English agents + team (one lead, harness-backed workers) ───────────
|
|
94
|
+
const teams0 = await get('/api/workshop/teams')
|
|
95
|
+
let team = teams0.find(t => t.name === 'Cast-film control team')
|
|
96
|
+
if (!team) team = await post('/api/workshop/teams', { name: 'Cast-film control team', description: 'Cast-film process lead plus DAQ and modelling workers.', visibility: 'public' })
|
|
97
|
+
|
|
98
|
+
const teamId = team.id
|
|
99
|
+
const existingTeam = (await get('/api/workshop/teams')).find(t => t.id === teamId)
|
|
100
|
+
const memberNames = new Set((existingTeam?.members ?? []).map(m => m.name))
|
|
101
|
+
|
|
102
|
+
const AGENTS = [
|
|
103
|
+
{ name: 'Cast-film process lead', harness: 'omp', role: 'lead' },
|
|
104
|
+
{ name: 'DAQ engineer', harness: 'omp', role: 'worker' },
|
|
105
|
+
{ name: 'Model trainer', harness: 'omp', role: 'worker' },
|
|
106
|
+
]
|
|
107
|
+
for (const a of AGENTS) {
|
|
108
|
+
if (memberNames.has(a.name)) continue
|
|
109
|
+
const agent = await post('/api/workshop/agents', { name: a.name, harness: a.harness, visibility: 'public' })
|
|
110
|
+
await post(`/api/workshop/teams/${teamId}/members`, { agentId: agent.id, role: a.role })
|
|
111
|
+
}
|
|
112
|
+
const finalTeam = (await get('/api/workshop/teams')).find(t => t.id === teamId)
|
|
113
|
+
console.log(`team : ${teamId} ${finalTeam.name} members=${(finalTeam.members ?? []).map(m => `${m.role}:${m.name}:${m.harness}`).join(', ')}`)
|
|
114
|
+
|
|
115
|
+
// ── 5. start the line so DAQ acquisition is genuinely live ────────────────
|
|
116
|
+
// DAQ sampling is gated on an active per-line batch (recipe run). Without a run
|
|
117
|
+
// the nodes report `offline` and the panel-1 claim "share one acquisition loop"
|
|
118
|
+
// would be untrue. Create a product + recipe carrying one setpoint param and
|
|
119
|
+
// start the line; idempotent via the line's active-run flag.
|
|
120
|
+
const lineStates = (await get('/api/workshop/dcw/lines')).states
|
|
121
|
+
const alreadyRunning = lineStates.find(s => s.lineId === line.id && s.active)
|
|
122
|
+
if (alreadyRunning) {
|
|
123
|
+
console.log(`run : already active ${alreadyRunning.runId}`)
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
const dcwState = await get('/api/workshop/dcw')
|
|
127
|
+
let product = (dcwState.products ?? []).find(p => p.name === 'Cast-film roll')
|
|
128
|
+
if (!product) product = (await post('/api/workshop/dcw/products', { name: 'Cast-film roll', lineId: line.id })).product
|
|
129
|
+
let recipe = (dcwState.recipes ?? []).find(r => r.name === 'Cast-film recipe' && r.lineId === line.id)
|
|
130
|
+
if (!recipe) {
|
|
131
|
+
recipe = (await post('/api/workshop/dcw/recipes', {
|
|
132
|
+
productId: product.id,
|
|
133
|
+
name: 'Cast-film recipe',
|
|
134
|
+
description: 'Cast-film line A baseline recipe.',
|
|
135
|
+
params: [{ nodeId: dcwNode.id, value: 178, min: 150, max: 200 }],
|
|
136
|
+
daqWindows: [{ nodeId: nTemp.id, min: 150, max: 200 }],
|
|
137
|
+
})).recipe
|
|
138
|
+
}
|
|
139
|
+
await post(`/api/workshop/dcw/lines/${line.id}/start`, { recipeId: recipe.id })
|
|
140
|
+
console.log(`run : started with recipe ${recipe.id} (${recipe.name})`)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log('\nFIXTURE ' + JSON.stringify({ lineId: line.id, lineName: line.name, modbusNodeId: nModbus.id, dcwNodeId: dcwNode.id, twinId: twin.id, teamId }))
|
|
@@ -60,6 +60,14 @@ console.log('\n━━━ 3. 稳定性护栏的严重性判定(哪类异常不该
|
|
|
60
60
|
check('undici UND_ERR_CONNECT_TIMEOUT → upstream', classifyFatalCandidate(err('UND_ERR_CONNECT_TIMEOUT')) === 'upstream')
|
|
61
61
|
check('无 code 但 message 带 ECONNREFUSED → upstream',
|
|
62
62
|
classifyFatalCandidate(new Error('connect ECONNREFUSED 127.0.0.1:1502')) === 'upstream')
|
|
63
|
+
// 实测事故 2:工业驱动库(Modbus/OPC UA 客户端)超时时只给可读 message、不带 errno code,
|
|
64
|
+
// 旧实现据此判 fatal → 生产实例运行中 exit 1(平台带着数采/数控一起挂掉)。
|
|
65
|
+
check('无 code,message "TCP Connection Timed Out" → upstream',
|
|
66
|
+
classifyFatalCandidate(new Error('TCP Connection Timed Out')) === 'upstream')
|
|
67
|
+
check('无 code,message "connection timed out" → upstream',
|
|
68
|
+
classifyFatalCandidate(new Error('connect error: connection timed out')) === 'upstream')
|
|
69
|
+
check('无 code,message "socket hang up" → upstream',
|
|
70
|
+
classifyFatalCandidate(new Error('socket hang up')) === 'upstream')
|
|
63
71
|
|
|
64
72
|
// 其余既有分级不得回退
|
|
65
73
|
check('ECONNRESET(客户端硬断)→ socket', classifyFatalCandidate(err('ECONNRESET')) === 'socket')
|
|
@@ -378,16 +378,16 @@ let channelB
|
|
|
378
378
|
|
|
379
379
|
// KB 回环:kb_index 入库独特自然语句 → kb_search 命中(嵌入/分词对自然语句才可靠);kb_store 沉淀经验
|
|
380
380
|
const marker = `三系统验证密语${Date.now().toString(36)}:青花瓷泵站三十七号叶片于黄昏完成校准`
|
|
381
|
-
const idx = await invoke(toolInvoker, 'kb_index', {
|
|
381
|
+
const idx = await invoke(toolInvoker, 'kb_index', { // rag 负载下事件循环停顿可达 60s+,放宽超时
|
|
382
382
|
title: `三系统集成验证 ${Date.now().toString(36)}`,
|
|
383
383
|
content: `# 三系统集成验证\n\n${marker}。\n\n本文由 three-system-e2e 经 rag-bridge 插件写入,验证 文档写盘 → 向量索引 → kb_search 检索 全链路。\n`,
|
|
384
384
|
tags: ['e2e'],
|
|
385
|
-
})
|
|
385
|
+
}, 120000)
|
|
386
386
|
ok(resultText(idx).includes('已入库'), 'kb_index 文档入库成功', resultText(idx).slice(0, 80))
|
|
387
387
|
let hit = ''
|
|
388
|
-
for (let i = 0; i <
|
|
388
|
+
for (let i = 0; i < 20; i++) { // 首次检索可能触发 BGE-M3 加载/异步索引落盘,放宽到 20×3s
|
|
389
389
|
await sleep(3000)
|
|
390
|
-
const s = await invoke(toolInvoker, 'kb_search', { query: marker })
|
|
390
|
+
const s = await invoke(toolInvoker, 'kb_search', { query: marker }, 120000)
|
|
391
391
|
hit = resultText(s)
|
|
392
392
|
if (hit.includes(marker)) break
|
|
393
393
|
}
|
|
@@ -400,7 +400,7 @@ let channelB
|
|
|
400
400
|
solution: '先查产线 LineRun 是否活动、节点 publishIntervalMs 是否在节拍上,再缩窗重试。',
|
|
401
401
|
key_lessons: ['空窗先查打标窗口', 'bucket 5s 可对齐多节点'],
|
|
402
402
|
tags: ['e2e', 'daq'],
|
|
403
|
-
})
|
|
403
|
+
}, 120000)
|
|
404
404
|
ok(resultText(store).includes('已沉淀'), 'kb_store 经验沉淀成功', resultText(store).slice(0, 80))
|
|
405
405
|
|
|
406
406
|
// 跨通道:分析组长 → 控制组长(require_reply;用频道 ID 定向,避免跨账号同名频道歧义)
|
|
@@ -628,7 +628,7 @@ let runId = ''
|
|
|
628
628
|
return (envelopeData(members) ?? []).find(m => m?.role === 'lead') ?? leadA
|
|
629
629
|
}
|
|
630
630
|
let gone = false
|
|
631
|
-
for (let i = 0; i <
|
|
631
|
+
for (let i = 0; i < 40 && !gone; i++) { // 共享实例 agent 运行时重建慢,60s 窗
|
|
632
632
|
await sleep(1500)
|
|
633
633
|
const fl = await freshLead()
|
|
634
634
|
const h = await raw('GET', `${BASE}/api/plugins/diag-bridge/health`, { token: userToken })
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 以 **admin 账号** 登录并给数采 / 数控 / 数字孪生出图。
|
|
3
|
+
* 与 lib.mjs 的视觉走查账号(visual)区分:admin 是文档里写的管理员账号,
|
|
4
|
+
* 能看见全量节点与全部产线;演示工作区由 scripts/ui/seed-admin.mjs 准备。
|
|
5
|
+
*/
|
|
6
|
+
import { launch, openPage, gotoReady } from './lib.mjs'
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
|
|
9
|
+
const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3021'
|
|
10
|
+
const OUT = process.env.AW_OUT ?? '.e2e-shots/admin'
|
|
11
|
+
fs.mkdirSync(OUT, { recursive: true })
|
|
12
|
+
|
|
13
|
+
const loginRes = await fetch(BASE + '/api/users/login', {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: { 'content-type': 'application/json' },
|
|
16
|
+
body: JSON.stringify({ email: 'admin@awshop.local', password: 'admin123' }),
|
|
17
|
+
})
|
|
18
|
+
const login = await loginRes.json()
|
|
19
|
+
if (login.code !== 0) throw new Error('admin 登录失败: ' + JSON.stringify(login).slice(0, 200))
|
|
20
|
+
const token = login.data.token
|
|
21
|
+
console.log('登录成功:', login.data.user.name, '(' + login.data.user.role + ')')
|
|
22
|
+
|
|
23
|
+
const browser = await launch()
|
|
24
|
+
const routes = [
|
|
25
|
+
{ path: '/', name: '01-dashboard', wait: 9000 },
|
|
26
|
+
{ path: '/daq', name: '02-daq', wait: 12000 },
|
|
27
|
+
{ path: '/dcw', name: '03-dcw', wait: 9000 },
|
|
28
|
+
{ path: '/town', name: '04-town', wait: 16000 },
|
|
29
|
+
{ path: '/monitor', name: '05-monitor', wait: 7000 },
|
|
30
|
+
]
|
|
31
|
+
for (const r of routes) {
|
|
32
|
+
const page = await openPage(browser, { token, dark: true, width: 1440, height: 900, deviceScaleFactor: 2 })
|
|
33
|
+
const errs = []
|
|
34
|
+
page.on('pageerror', e => errs.push(String(e.message).slice(0, 120)))
|
|
35
|
+
await gotoReady(page, r.path, { wait: r.wait })
|
|
36
|
+
const file = OUT + '/' + r.name + '.png'
|
|
37
|
+
await page.screenshot({ path: file })
|
|
38
|
+
const info = await page.evaluate(() => ({
|
|
39
|
+
nodes: document.querySelectorAll('*').length,
|
|
40
|
+
text: document.body.innerText.replace(/\s+/g, ' ').slice(0, 120),
|
|
41
|
+
}))
|
|
42
|
+
console.log(' ok ' + r.name.padEnd(16) + ' dom=' + String(info.nodes).padStart(6) + ' errs=' + errs.length)
|
|
43
|
+
await page.close()
|
|
44
|
+
}
|
|
45
|
+
await browser.close()
|
|
46
|
+
console.log('->', OUT)
|