agentworkshop 0.7.39 → 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/host-tools.json +55 -0
- package/.output/.AgentWorkShop/prompts/host-tools.json +55 -0
- 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/{CUDwzgLV.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 +2225 -1445
- 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-CZmZFpqH.mjs → default-BE4r0FZN.mjs} +152 -58
- package/.output/server/chunks/build/{default-CZmZFpqH.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 +51 -25
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +199 -35
- 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 -7
- package/app/components/AppSidebar.vue +117 -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/i18n/locales/en.ts +2 -0
- package/i18n/locales/zh-CN.ts +2 -0
- package/nuxt.config.ts +31 -0
- package/package.json +9 -1
- 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/api-live-e2e.mjs +40 -8
- package/scripts/capture-walkthrough.mjs +232 -0
- 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/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/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/CmsOtOAA2.js +0 -1
- package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
- package/.output/public/_nuxt/D20Yamr4.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/4457d63f-0866-4cea-b320-04a321f24795.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.BXI_fZUi.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()
|
|
@@ -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')
|
|
@@ -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)
|