agentworkshop 0.7.39 → 0.7.41
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/{CY9LbPCI.js → BN16aRdQ.js} +1 -1
- package/.output/public/_nuxt/BWVhZntO.js +1 -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/{DUrecWPx.js → C0ZrS1KL.js} +2 -2
- package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
- package/.output/public/_nuxt/{CUDwzgLV.js → C4s_niy-.js} +9 -9
- package/.output/public/_nuxt/CFxtLwjp.js +1 -0
- package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
- package/.output/public/_nuxt/CssgYAAL2.js +1 -0
- package/.output/public/_nuxt/D6mbFA-a.js +1 -0
- package/.output/public/_nuxt/D98NH8kG.js +1 -0
- package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
- package/.output/public/_nuxt/DGbd-UiG.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/{45zuyAso.js → D_OTvQza.js} +1 -1
- package/.output/public/_nuxt/Dap5PmTL.js +2 -0
- package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
- package/.output/public/_nuxt/DvLxILQP.js +1 -0
- package/.output/public/_nuxt/IREZ1eC2.js +1 -0
- package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
- package/.output/public/_nuxt/_id_.D9ymtHB5.css +1 -0
- package/.output/public/_nuxt/_id_.jf-_8E_c.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/78a295bb-2072-48a4-8b46-6d545fdae743.json +1 -0
- package/.output/public/_nuxt/ca4TJ3Op.js +1 -0
- package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
- package/.output/public/_nuxt/daq.DjSgY94l.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/eqq3wWQF.js +18 -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 +2355 -1362
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView--otsnr_7.mjs} +290 -208
- package/.output/server/chunks/build/TownView--otsnr_7.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-Brv-JSvr.mjs} +142 -141
- package/.output/server/chunks/build/_id_-Brv-JSvr.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-D9pl1OZy.mjs} +38 -36
- package/.output/server/chunks/build/_id_-D9pl1OZy.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BASfDf0s.mjs} +295 -261
- package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BASfDf0s.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-DkMvD8sg.mjs} +193 -126
- package/.output/server/chunks/build/daq-DkMvD8sg.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-C1jgVJkO.mjs} +46 -46
- package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-C1jgVJkO.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--dByNyhw.mjs} +35 -35
- package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs--dByNyhw.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-B0bUWnC6.mjs} +35 -19
- package/.output/server/chunks/build/pages-B0bUWnC6.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-CoQdblbZ.mjs} +38 -12
- package/.output/server/chunks/build/town-CoQdblbZ.mjs.map +1 -0
- package/.output/server/chunks/build/{useDaqStream-CIBaiGAl.mjs → useDaqStream-DfCFpb3p.mjs} +9 -3
- package/.output/server/chunks/build/useDaqStream-DfCFpb3p.mjs.map +1 -0
- package/.output/server/chunks/build/{useDcwStream-CBbiYnWe.mjs → useDcwStream-UPiX-3iO.mjs} +11 -2
- package/.output/server/chunks/build/useDcwStream-UPiX-3iO.mjs.map +1 -0
- package/.output/server/chunks/build/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/start.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/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/index5.get.mjs +3 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs +5 -1
- 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 +203 -39
- 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/useDaqStream.ts +7 -4
- package/app/composables/workshop/useDcwStream.ts +8 -2
- 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 +12 -7
- package/app/pages/daq/index.vue +216 -13
- package/app/pages/dcw/[id].vue +117 -32
- 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/cli.en.md +2 -2
- package/docs/cli.md +2 -2
- package/docs/plugins.en.md +33 -1
- package/docs/plugins.md +29 -1
- package/docs/sdk.en.md +1 -1
- package/docs/sdk.md +1 -1
- package/i18n/locales/en.ts +2 -0
- package/i18n/locales/zh-CN.ts +2 -0
- package/nuxt.config.ts +31 -0
- package/package.json +11 -2
- 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/_loop-biax-campaign.mjs +124 -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/test-serial-bridge.mjs +418 -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/e2e-interactive.mjs +328 -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/seed-industrial-demo.mjs +171 -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/index.get.ts +3 -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 +5 -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 +23 -10
- package/server/plugins/dev-stability-guard.ts +3 -1
- package/server/plugins/system-config.ts +6 -1
- package/server/plugins-builtin/serial-bridge/client.mjs +210 -0
- package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -0
- package/server/plugins-builtin/serial-bridge/i18n.json +62 -0
- package/server/plugins-builtin/serial-bridge/index.mjs +247 -0
- package/server/plugins-builtin/serial-bridge/lib.mjs +208 -0
- package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -0
- package/server/services/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 +25 -5
- package/server/services/workshop/daq/daq-runtime.ts +5 -3
- package/server/services/workshop/daq/drivers.ts +141 -11
- 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 +147 -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 +90 -4
- 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/plugin-bridge.ts +45 -0
- package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
- package/server/services/workshop/plugins/host.mjs +30 -0
- 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/daq-protocol.ts +15 -0
- package/shared/dcw-protocol.ts +136 -0
- package/uno.config.ts +12 -1
- package/.output/public/_nuxt/8NMOXZOV.js +0 -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/DAe0h2MB.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_-CfVosNg3.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/useDaqStream-CIBaiGAl.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwStream-CBbiYnWe.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
|
@@ -32,7 +32,7 @@ import { extractJsonArray, peerPrompt, supervisePrompt, systemManual, toolArgsPr
|
|
|
32
32
|
import { getHitlRegistry } from './hitl-registry'
|
|
33
33
|
import { harnessSettings } from '../settings'
|
|
34
34
|
import { spawnLineProcess } from './adapters/line-spawn'
|
|
35
|
-
import { BaseAgentImpl } from './base-agent'
|
|
35
|
+
import { BaseAgentImpl, type BaseAgentConfigView } from './base-agent'
|
|
36
36
|
import { resolveBridgePath, resolvePlatformBaseUrl } from './harness-env'
|
|
37
37
|
|
|
38
38
|
const log = createLogger('workshop.opencode')
|
|
@@ -152,7 +152,7 @@ export class OpenCodeAgentImpl extends BaseAgentImpl implements AgentInterface {
|
|
|
152
152
|
return 'opencode'
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
protected configRecord():
|
|
155
|
+
protected configRecord(): BaseAgentConfigView {
|
|
156
156
|
return this.config
|
|
157
157
|
}
|
|
158
158
|
|
|
@@ -222,7 +222,7 @@ export class OpenCodeAgentImpl extends BaseAgentImpl implements AgentInterface {
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
/** HITL 应答(codex/opencode/dsh 统一入口;本 impl 处理 opencode-permission) */
|
|
225
|
-
async respondHitl(kind: string, id: string, outcome: {
|
|
225
|
+
override async respondHitl(kind: string, id: string, outcome: {
|
|
226
226
|
confirmed?: boolean
|
|
227
227
|
cancelled?: boolean
|
|
228
228
|
value?: string
|
|
@@ -247,7 +247,9 @@ export class OpenCodeAgentImpl extends BaseAgentImpl implements AgentInterface {
|
|
|
247
247
|
await this.api('POST', `/question/${encodeURIComponent(id)}/reply`, { answer: outcome.value ?? outcome.comment ?? '' })
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
|
-
|
|
250
|
+
// 原为 clearTimeout(p.timer):p 在本作用域不存在(ReferenceError,且仅当计时器非空时触发),
|
|
251
|
+
// 该待办条目在此处就是 pending(与 dsh/codex/hermes 的同名分支写法一致)。
|
|
252
|
+
if (pending.timer) clearTimeout(pending.timer)
|
|
251
253
|
this.pendingHitl.delete(id)
|
|
252
254
|
getHitlRegistry().resolve(kind, id, cancelled ? 'cancelled' : 'answered')
|
|
253
255
|
}
|
|
@@ -273,9 +275,9 @@ export class OpenCodeAgentImpl extends BaseAgentImpl implements AgentInterface {
|
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
277
|
|
|
276
|
-
|
|
278
|
+
// supervising 由 BaseAgentImpl 持有(protected):同名私有声明会与基类形成"两个私有声明"(TS2415)。
|
|
277
279
|
|
|
278
|
-
async supervise(snapshot: SupervisionSnapshot, ctx: AgentRunContext, opts?: { signal?: AbortSignal }): Promise<SupervisionDecision[]> {
|
|
280
|
+
override async supervise(snapshot: SupervisionSnapshot, ctx: AgentRunContext, opts?: { signal?: AbortSignal }): Promise<SupervisionDecision[]> {
|
|
279
281
|
// 引擎/会话不可用 → 本轮监督降级为空(调度器回退规则引擎);绝不让引擎故障
|
|
280
282
|
// 以 unhandledRejection 形态逃逸(dev-stability-guard 会据此杀掉整个服务端)
|
|
281
283
|
try {
|
|
@@ -296,16 +298,22 @@ export class OpenCodeAgentImpl extends BaseAgentImpl implements AgentInterface {
|
|
|
296
298
|
memory: ctx.memory,
|
|
297
299
|
})
|
|
298
300
|
const timeoutMs = this.config.superviseTimeoutMs ?? 150_000
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
301
|
+
// runTurn 是 async generator(返回 AsyncGenerator,不是 Promise),必须以异步迭代消费完整个回合。
|
|
302
|
+
// (原实现写 .then(...)/.catch(...):运行时是 "then is not a function" 的 TypeError,supervise 整条路径实际不可用。)
|
|
303
|
+
try {
|
|
304
|
+
const events: AgentEvent[] = []
|
|
305
|
+
for await (const e of this.runTurn(prompt, undefined, {
|
|
306
|
+
timeoutMs,
|
|
307
|
+
signal: opts?.signal,
|
|
308
|
+
onDone: async () => {
|
|
309
|
+
this.supervising = false
|
|
310
|
+
},
|
|
311
|
+
beforeStart: () => {
|
|
312
|
+
this.supervising = true
|
|
313
|
+
},
|
|
314
|
+
})) {
|
|
315
|
+
events.push(e)
|
|
316
|
+
}
|
|
309
317
|
// 从事件流提取最终文本 → JSON 决策兜底(工具直执行路径返回空)
|
|
310
318
|
let text = ''
|
|
311
319
|
for (const e of events) {
|
|
@@ -315,10 +323,21 @@ export class OpenCodeAgentImpl extends BaseAgentImpl implements AgentInterface {
|
|
|
315
323
|
}
|
|
316
324
|
const parsed = extractJsonArray(text)
|
|
317
325
|
return parsed && parsed.length > 0 ? parsed as SupervisionDecision[] : []
|
|
318
|
-
}
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
319
328
|
this.supervising = false
|
|
320
329
|
return []
|
|
321
|
-
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** supervise 用:收齐整个回合的事件(基类抽象成员;本类 supervise 自行消费并需 beforeStart/onDone 钩子) */
|
|
334
|
+
protected async collectTurnEvents(prompt: string, timeoutMs: number, signal?: AbortSignal): Promise<AgentEvent[]> {
|
|
335
|
+
const events: AgentEvent[] = []
|
|
336
|
+
for await (const e of this.runTurn(prompt, undefined, { timeoutMs, signal })) {
|
|
337
|
+
events.push(e)
|
|
338
|
+
if (e.kind === 'error') break
|
|
339
|
+
}
|
|
340
|
+
return events
|
|
322
341
|
}
|
|
323
342
|
|
|
324
343
|
protected async* workerTurn(request: AgentRunRequest, ctx: AgentRunContext): AsyncGenerator<AgentEvent, void, unknown> {
|
|
@@ -800,10 +819,17 @@ export class OpenCodeAgentImpl extends BaseAgentImpl implements AgentInterface {
|
|
|
800
819
|
}
|
|
801
820
|
const createWith = async (extra: Record<string, unknown> | undefined): Promise<Record<string, unknown>> =>
|
|
802
821
|
this.api('POST', '/session', { ...body, ...(extra ?? {}) })
|
|
803
|
-
|
|
822
|
+
// 历史实现写作 `await (cond) ? a : b`:await 只作用于条件,整个三元表达式的结果是**未 await** 的
|
|
823
|
+
// Promise,故 created?.id 恒为 undefined → sessionId 恒为 String(undefined ?? '') === '' →
|
|
824
|
+
// 必然落到下面的 2.5s 重试循环(真正被使用的会话来自重试那次 createWith(undefined))。
|
|
825
|
+
// 此处逐字保留该运行时语义:同样的 await 让出一次微任务、同样的请求照发、首次结果同样丢弃。
|
|
826
|
+
// 直白的修法是给整个三元表达式加括号 await(会用上带 permission 的首次会话、省掉 2.5s 重试),
|
|
827
|
+
// 但那会改变 opencode 会话的权限策略与启动时序,故本次不改 —— 见报告"未修的缺陷"一节。
|
|
828
|
+
const firstAttempt = await (this.config.permission ?? DEFAULT_PERMISSION)
|
|
829
|
+
void (firstAttempt
|
|
804
830
|
? createWith({ permission: this.config.permission ?? DEFAULT_PERMISSION }).catch(() => createWith(undefined))
|
|
805
|
-
: createWith(undefined)
|
|
806
|
-
this.sessionId =
|
|
831
|
+
: createWith(undefined))
|
|
832
|
+
this.sessionId = ''
|
|
807
833
|
if (!this.sessionId) {
|
|
808
834
|
// 瞬态失败(实例刚起/内部分配竞态/权限规则不兼容)→ 无附加字段重试,最多 3 次
|
|
809
835
|
for (let i = 0; i < 3 && !this.sessionId; i++) {
|
|
@@ -27,7 +27,7 @@ import type {
|
|
|
27
27
|
} from './agent-interface'
|
|
28
28
|
import type { AgentContextStats } from '../types/task'
|
|
29
29
|
import { markHarnessProcessExit, registerHarnessProcess, bindHarnessProcess, killHarnessProcess } from './harness-process'
|
|
30
|
-
import { peerPrompt, systemManual, toolArgsPreview, workerPrompt } from './prompt-builder'
|
|
30
|
+
import { extractJsonArray, peerPrompt, supervisePrompt, systemManual, toolArgsPreview, workerPrompt } from './prompt-builder'
|
|
31
31
|
import { getHitlRegistry } from './hitl-registry'
|
|
32
32
|
import { harnessSettings } from '../settings'
|
|
33
33
|
import { StdioJsonRpcClient, type JsonRpcRequestIncoming } from './adapters/stdio-jsonrpc'
|
|
@@ -111,7 +111,9 @@ export interface QwenAgentConfig {
|
|
|
111
111
|
export class QwenAgentImpl extends BaseAgentImpl {
|
|
112
112
|
private readonly config: QwenAgentConfig
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
// agentRole 由 BaseAgentImpl 持有(protected);同名私有声明会遮蔽基类 identity 角色。
|
|
115
|
+
// 仅收窄可见性声明,运行时代码不变。
|
|
116
|
+
protected override agentRole: 'lead' | 'worker' = 'worker'
|
|
115
117
|
private client: StdioJsonRpcClient | null = null
|
|
116
118
|
private clientStarting: Promise<void> | null = null
|
|
117
119
|
private turnActive = false
|
|
@@ -177,7 +179,7 @@ export class QwenAgentImpl extends BaseAgentImpl {
|
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
/** HITL 应答:requestToolCallConfirmation → outcome allow/reject/cancel(fail-closed) */
|
|
180
|
-
async respondHitl(kind: string, id: string, outcome: {
|
|
182
|
+
override async respondHitl(kind: string, id: string, outcome: {
|
|
181
183
|
confirmed?: boolean
|
|
182
184
|
cancelled?: boolean
|
|
183
185
|
}): Promise<void> {
|
|
@@ -202,7 +204,7 @@ export class QwenAgentImpl extends BaseAgentImpl {
|
|
|
202
204
|
|
|
203
205
|
// ===== run / supervise =====
|
|
204
206
|
|
|
205
|
-
async supervise(snapshot: import('./agent-interface').SupervisionSnapshot, ctx: AgentRunContext, opts?: { signal?: AbortSignal }): Promise<import('./agent-interface').SupervisionDecision[]> {
|
|
207
|
+
override async supervise(snapshot: import('./agent-interface').SupervisionSnapshot, ctx: AgentRunContext, opts?: { signal?: AbortSignal }): Promise<import('./agent-interface').SupervisionDecision[]> {
|
|
206
208
|
await this.ensureClient(ctx)
|
|
207
209
|
if (!this.client || !this.sessionStarted) return []
|
|
208
210
|
if (this.supervising) return []
|
|
@@ -216,7 +218,10 @@ export class QwenAgentImpl extends BaseAgentImpl {
|
|
|
216
218
|
})
|
|
217
219
|
this.supervising = true
|
|
218
220
|
try {
|
|
219
|
-
|
|
221
|
+
// 原为 this.collectTurn(...):该类唯一存在的整回合收集实现是 collectTurnEvents
|
|
222
|
+
// (与基类 supervise 调用的同名抽象成员同签名)。原调用在运行时抛 TypeError 并被
|
|
223
|
+
// 本方法的 catch 吞掉 → supervise 恒返回 [],详见报告。
|
|
224
|
+
const events = await this.collectTurnEvents(prompt, this.config.superviseTimeoutMs ?? 150_000, opts?.signal)
|
|
220
225
|
let text = ''
|
|
221
226
|
for (const e of events) {
|
|
222
227
|
if (e.kind === 'artifact') {
|
|
@@ -72,7 +72,9 @@ export function inventory(): AmlInventory {
|
|
|
72
72
|
kind: EntityIssue['kind'],
|
|
73
73
|
rows: Array<{ id: string, path?: string }>,
|
|
74
74
|
baseDir: string,
|
|
75
|
-
contract
|
|
75
|
+
// 非空契约列表:三个调用点都传字面量,且下面按"多选一"语义读 contract[0] 作展示路径,
|
|
76
|
+
// 用非空元组表达"至少一项"这一既有前提(纯类型,运行时零变化)
|
|
77
|
+
contract: readonly [string, ...string[]],
|
|
76
78
|
): number => {
|
|
77
79
|
const ids = new Set(rows.map(r => r.id))
|
|
78
80
|
for (const r of rows) {
|
|
@@ -362,7 +362,8 @@ export async function installUv(rt: AmlRuntime, onLog?: ProgressFn): Promise<{ o
|
|
|
362
362
|
const hit = findUvInDir(rt.toolsDir)
|
|
363
363
|
if (hit) {
|
|
364
364
|
const v = await runCapture(hit, ['--version'], 15_000)
|
|
365
|
-
|
|
365
|
+
// 运行时 `''.split('\n')[0] === ''`,首行恒存在;?? '' 只是 noUncheckedIndexedAccess 下的写法
|
|
366
|
+
const version = (v.out || v.err).trim().split('\n')[0] ?? ''
|
|
366
367
|
writeUvRecord(rt, { uvPath: hit, version, installedAt: new Date().toISOString(), installDir: rt.toolsDir, method: 'official-script' })
|
|
367
368
|
resetProbeCache()
|
|
368
369
|
emit(`安装成功:${version} @ ${hit}`)
|
|
@@ -390,7 +391,7 @@ export async function installUv(rt: AmlRuntime, onLog?: ProgressFn): Promise<{ o
|
|
|
390
391
|
}
|
|
391
392
|
catch { /* Windows 无 x 位 */ }
|
|
392
393
|
const v = await runCapture(hit, ['--version'], 15_000)
|
|
393
|
-
const version = (v.out || v.err).trim().split('\n')[0]
|
|
394
|
+
const version = (v.out || v.err).trim().split('\n')[0] ?? ''
|
|
394
395
|
writeUvRecord(rt, { uvPath: hit, version, installedAt: new Date().toISOString(), installDir: rt.toolsDir, method: 'pip-target' })
|
|
395
396
|
resetProbeCache()
|
|
396
397
|
emit(`安装成功:${version} @ ${hit}`)
|
|
@@ -25,6 +25,10 @@ const TOPIC_WILDCARD = `${TOPIC_ROOT}/+/sample`
|
|
|
25
25
|
/** 断连离线缓冲上限(满丢最旧;补发帧按消费侧乱序防御自然去重) */
|
|
26
26
|
const OFFLINE_CAP = 2000
|
|
27
27
|
|
|
28
|
+
/** MQTT QoS 协议取值(0/1/2;shared/config/schema.json 的 daq.mqtt.qos 声明 min 0 / max 2)。
|
|
29
|
+
* settings 面(settings.ts)把该配置读成 number,这里按协议收窄 —— 运行时原值透传,不做钳制/兜底。 */
|
|
30
|
+
type MqttQos = 0 | 1 | 2
|
|
31
|
+
|
|
28
32
|
export class MqttQueueAdapter implements DaqQueuePort {
|
|
29
33
|
readonly backend = 'mqtt' as const
|
|
30
34
|
private client: MqttClient | null = null
|
|
@@ -48,7 +52,7 @@ export class MqttQueueAdapter implements DaqQueuePort {
|
|
|
48
52
|
const mod = requireMqtt('mqtt') as unknown as { connect: (url: string, opts?: Record<string, unknown>) => MqttClient }
|
|
49
53
|
// 凭据/QoS/TLS 配置驱动(daq.mqtt.*;legacy DAQ_MQTT_* env 别名兼容,DAQ_MQTT_URL 整串覆盖仍最高优先)
|
|
50
54
|
const mqttCfg = daqRuntimeSettings().mqtt
|
|
51
|
-
const qos = mqttCfg.qos
|
|
55
|
+
const qos = mqttCfg.qos as MqttQos
|
|
52
56
|
const opts: Record<string, unknown> = {
|
|
53
57
|
clientId: `aw-daq-${process.pid}-${Math.random().toString(36).slice(2, 6)}`,
|
|
54
58
|
reconnectPeriod: 2000,
|
|
@@ -99,7 +103,7 @@ export class MqttQueueAdapter implements DaqQueuePort {
|
|
|
99
103
|
return
|
|
100
104
|
}
|
|
101
105
|
this.published++
|
|
102
|
-
this.client.publish(TOPIC_SAMPLE(env.nodeId), JSON.stringify(env), { qos: daqRuntimeSettings().mqtt.qos })
|
|
106
|
+
this.client.publish(TOPIC_SAMPLE(env.nodeId), JSON.stringify(env), { qos: daqRuntimeSettings().mqtt.qos as MqttQos })
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
consume(fn: DaqConsumer): () => void {
|
|
@@ -123,8 +127,8 @@ export class MqttQueueAdapter implements DaqQueuePort {
|
|
|
123
127
|
// end() 回调在某些断连路径下不触发,旧 client 的 connect/message/error 监听器
|
|
124
128
|
// 会连同 socket 一起滞留(client 对象被闭包引用,GC 不可达)。
|
|
125
129
|
// rebuildDaqQueue 反复换装时会累积;显式摘除是唯一确定性的清理点。
|
|
126
|
-
try {
|
|
127
|
-
c.removeAllListeners()
|
|
130
|
+
try {
|
|
131
|
+
c.removeAllListeners()
|
|
128
132
|
}
|
|
129
133
|
catch { /* 已销毁等场景:尽力而为 */ }
|
|
130
134
|
}
|
|
@@ -27,7 +27,7 @@ import { daqRuntimeSettings } from '../settings'
|
|
|
27
27
|
import { getSystemConfigService } from '../../system-config'
|
|
28
28
|
import { daqKeyFromRef, normalizeDataTransform, normalizeSignalKind, DAQ_DRIVERS, type AepDaqControllerState, type AepDaqFrame, type AepDaqReading, type AepDaqNodeChange, type DaqDriverKind, type DaqNodeView, type DataTransform, type DriverTestResult } from '../../../../shared/daq-protocol'
|
|
29
29
|
import { AppError, ErrorCodes } from '../../../utils/errors'
|
|
30
|
-
import { normalizeDriverKind, resolveDaqDriver, probeDriverAvailability, listPluginDrivers, type DaqFrameSample } from './drivers'
|
|
30
|
+
import { normalizeDriverKind, resolveDaqDriver, probeDriverAvailability, listPluginDrivers, driverCatalog, type DaqFrameSample } from './drivers'
|
|
31
31
|
import { findDaqTemplate } from './daq-templates'
|
|
32
32
|
import { DaqNode } from './daq-node'
|
|
33
33
|
import { DaqNodeRuntime, type DaqRuntimeHost } from './daq-runtime'
|
|
@@ -106,6 +106,16 @@ export interface DaqPatchInput {
|
|
|
106
106
|
|
|
107
107
|
type BroadcastFn = (type: string, payload: unknown) => void
|
|
108
108
|
|
|
109
|
+
/** 有界 LRU 的最小泛型面:shared/lru.mjs 是 allowJs 推断出的**非泛型**类(JS 里没有 @template),
|
|
110
|
+
* 直接 `new LruMap<K, V>()` 无处安放类型参数。这里按实际使用面(get/set/delete/size)声明键值类型,
|
|
111
|
+
* 调用点即可拿到准确的读写类型;运行时仍是同一个 LruMap(纯类型声明,零运行时差异)。 */
|
|
112
|
+
interface LruLike<K, V> {
|
|
113
|
+
readonly size: number
|
|
114
|
+
get(key: K): V | undefined
|
|
115
|
+
set(key: K, value: V): unknown
|
|
116
|
+
delete(key: K): unknown
|
|
117
|
+
}
|
|
118
|
+
|
|
109
119
|
/** TSDB 批量写窗口(ms):消费端攒批再落盘 */
|
|
110
120
|
const TSDB_FLUSH_MS = 500
|
|
111
121
|
/** TSDB 攒批缓冲上限(背压:满则丢最旧并计数,防慢库拖爆内存) */
|
|
@@ -416,7 +426,9 @@ class DaqController {
|
|
|
416
426
|
// 指标阈值告警(模板 metrics 规则;alarm 硬限边沿 → 既有告警链路)
|
|
417
427
|
this.evaluateMetricAlarms(node, tpl, metrics)
|
|
418
428
|
if (!allowPublish) return
|
|
419
|
-
|
|
429
|
+
// lineId 是 WS 逐产线扇出的依据(scene-events.payloadLineId 读的就是 payload.lineId,
|
|
430
|
+
// 与 AepDaqReading.lineId 同义);AepDaqFrame 协议类型漏了该字段 —— 见报告。
|
|
431
|
+
const payload: AepDaqFrame & { lineId: string | null } = {
|
|
420
432
|
nodeId: node.id,
|
|
421
433
|
templateRef: node.templateRef,
|
|
422
434
|
kind: f.kind,
|
|
@@ -694,7 +706,7 @@ class DaqController {
|
|
|
694
706
|
|
|
695
707
|
/** 同绑定 siblings 缓存(1s TTL):回写热路径不再逐样本全量 scan,O(N²)→O(N)/周期。
|
|
696
708
|
* 用有界 LRU 而非「超 500 整体 clear()」——后者会造成周期性全量缓存穿透。 */
|
|
697
|
-
private siblingsCache
|
|
709
|
+
private siblingsCache: LruLike<string, { at: number, list: DaqNode[] }> = new LruMap(500)
|
|
698
710
|
|
|
699
711
|
private siblingsOf(bindingId: string): DaqNode[] {
|
|
700
712
|
const hit = this.siblingsCache.get(bindingId)
|
|
@@ -759,7 +771,10 @@ class DaqController {
|
|
|
759
771
|
this.broadcast = fn
|
|
760
772
|
}
|
|
761
773
|
|
|
762
|
-
|
|
774
|
+
/** minIntervalMs / alarmsRaised / framesStored 都是本方法**实际返回**的字段,但 AepDaqControllerState
|
|
775
|
+
* 未收录(协议类型与实现漂移):minIntervalMs 被前端 useDaqStream 消费,alarmsRaised 被
|
|
776
|
+
* server/api/metrics.ts 消费(framesStored 供管线指标展示)。这里按实现补齐返回面 —— 见报告。 */
|
|
777
|
+
controllerState(): AepDaqControllerState & { minIntervalMs: number, alarmsRaised: number, framesStored: number, produced?: number, consumed?: number, dropped?: number, samplesStored?: number, tsdbDropped?: number } {
|
|
763
778
|
// 丢弃 = 队列层真实丢弃(inproc 拥塞/mqtt 断连)+ 消费侧乱序迟到帧(诚实可见)
|
|
764
779
|
const queueLost = g_queueLost()
|
|
765
780
|
// 单次遍历(hardening PERF-1):status 高频路径不再对全表多次 all()
|
|
@@ -1201,11 +1216,16 @@ class DaqController {
|
|
|
1201
1216
|
return this.testDriver(node.driver, node.driverConfig)
|
|
1202
1217
|
}
|
|
1203
1218
|
|
|
1204
|
-
/** 驱动可用性(meta;包缺失 → planned
|
|
1219
|
+
/** 驱动可用性(meta;包缺失 → planned 提示而非硬失败;含插件驱动) */
|
|
1205
1220
|
async driverAvailability(): Promise<Record<string, boolean>> {
|
|
1206
1221
|
return probeDriverAvailability()
|
|
1207
1222
|
}
|
|
1208
1223
|
|
|
1224
|
+
/** 驱动目录(内置 + 插件自描述合并;前端「添加节点」下拉/动态参数表单数据源) */
|
|
1225
|
+
async driverCatalog(): Promise<ReturnType<typeof driverCatalog>> {
|
|
1226
|
+
return driverCatalog()
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1209
1229
|
// ---------- 存量迁移(device-twins kind=daq → DaqNode 幂等供给) ----------
|
|
1210
1230
|
|
|
1211
1231
|
/**
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { applyTransform } from '../../../../shared/daq-protocol'
|
|
13
|
-
import { classifyCommError } from './drivers'
|
|
13
|
+
import { classifyCommError, type DaqFrameSample } from './drivers'
|
|
14
14
|
import type { DaqNode } from './daq-node'
|
|
15
|
-
import type {
|
|
15
|
+
import type { DaqSampleEnvelope } from './bus/queue-port'
|
|
16
16
|
|
|
17
17
|
/** 驱动故障广播节流(ms;每运行时独立计时) */
|
|
18
18
|
const ERR_THROTTLE_MS = 30_000
|
|
@@ -94,7 +94,9 @@ export class DaqNodeRuntime {
|
|
|
94
94
|
if (now < this.lastSampleAt + node.effectiveInterval(defs.intervalMs, defs.minIntervalMs)) return
|
|
95
95
|
this.sampling = true
|
|
96
96
|
try {
|
|
97
|
-
|
|
97
|
+
// 生产面可能返回三种形态:标量 / 驱动原生帧(DaqFrameSample)/ 生产侧已剥离 blob 的帧信封
|
|
98
|
+
// (与 DaqRuntimeHost.sample 的返回面一致 —— 后者不能丢,否则帧信封分支拿不到 kind/points)
|
|
99
|
+
let v: number | DaqFrameSample | { frame: NonNullable<DaqSampleEnvelope['frame']> } | null = null
|
|
98
100
|
try {
|
|
99
101
|
v = await this.host.sample(node, now)
|
|
100
102
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { createLogger } from '../logger'
|
|
18
18
|
import { createRequire } from 'node:module'
|
|
19
19
|
import { encodePng } from './png-enc'
|
|
20
|
-
import type
|
|
20
|
+
import { DAQ_DRIVERS, type DaqDriverKind, type DriverTestResult } from '../../../../shared/daq-protocol'
|
|
21
21
|
|
|
22
22
|
const log = createLogger('daq.drivers')
|
|
23
23
|
|
|
@@ -200,8 +200,17 @@ function mockImageSample(
|
|
|
200
200
|
// Modbus TCP 真实驱动(modbus-serial;连接池按 host:port:unitId 复用)
|
|
201
201
|
// ============================================================
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* modbus-serial 客户端实例类型。
|
|
205
|
+
*
|
|
206
|
+
* 该包 index.d.ts 只写 `import { ModbusRTU } from "./ModbusRTU"` + `export default ModbusRTU`,
|
|
207
|
+
* **没有**具名 re-export(运行时 module.exports = 构造器,并自挂 module.exports.default = 自己),
|
|
208
|
+
* 因此 `import('modbus-serial').ModbusRTU` 在类型上并不存在 —— 从 default 导出取才与实际导出形态一致。
|
|
209
|
+
*/
|
|
210
|
+
type ModbusRtuClient = InstanceType<typeof import('modbus-serial').default>
|
|
211
|
+
|
|
203
212
|
export interface ModbusConn {
|
|
204
|
-
client:
|
|
213
|
+
client: ModbusRtuClient
|
|
205
214
|
lastUsed: number
|
|
206
215
|
/** 连接级操作队列尾(采/控共用链路串行化:TCP 网关并发事务会协议错乱) */
|
|
207
216
|
tail: Promise<unknown>
|
|
@@ -232,7 +241,7 @@ export function evictModbusConn(cfg: Record<string, unknown>, transport: ModbusT
|
|
|
232
241
|
* 安全关闭:半开 socket(对端断电)时 modbus-serial close() 等待 FIN 永不返回,
|
|
233
242
|
* 一律 500ms 超时保护(fire-and-forget 底层 close,调用方不阻塞)。
|
|
234
243
|
*/
|
|
235
|
-
export async function closeModbusSafely(client:
|
|
244
|
+
export async function closeModbusSafely(client: ModbusRtuClient): Promise<void> {
|
|
236
245
|
await Promise.race([
|
|
237
246
|
Promise.resolve()
|
|
238
247
|
.then(() => client.close())
|
|
@@ -252,7 +261,7 @@ export function withModbusConn<R>(conn: ModbusConn, fn: () => Promise<R>): Promi
|
|
|
252
261
|
const run = conn.tail.then(() => fn(), () => fn())
|
|
253
262
|
let timer: NodeJS.Timeout | undefined
|
|
254
263
|
let dead = false
|
|
255
|
-
const guarded = Promise.race([
|
|
264
|
+
const guarded: Promise<R> = Promise.race([
|
|
256
265
|
run,
|
|
257
266
|
new Promise<never>((_, rej) => {
|
|
258
267
|
timer = setTimeout(() => {
|
|
@@ -264,7 +273,7 @@ export function withModbusConn<R>(conn: ModbusConn, fn: () => Promise<R>): Promi
|
|
|
264
273
|
rej(new Error('Modbus 响应超时(3s)——连接可能半开,已作废重连'))
|
|
265
274
|
}, 3000)
|
|
266
275
|
}),
|
|
267
|
-
]
|
|
276
|
+
])
|
|
268
277
|
void guarded.catch(() => { /* 超时分支先 reject,防 unhandled */ })
|
|
269
278
|
conn.tail = guarded.then(
|
|
270
279
|
() => {
|
|
@@ -298,7 +307,7 @@ export async function getModbusConn(cfg: Record<string, unknown>, transport: Mod
|
|
|
298
307
|
}
|
|
299
308
|
// CJS 互操作:require 形状可能是 { ModbusRTU } / default.ModbusRTU / 构造器本身
|
|
300
309
|
const mod = reqNative('modbus-serial') as unknown as { ModbusRTU?: unknown, default?: { ModbusRTU?: unknown } | unknown }
|
|
301
|
-
const ModbusRTU = (mod.ModbusRTU ?? (mod.default as { ModbusRTU?: unknown } | undefined)?.ModbusRTU ?? mod) as new () =>
|
|
310
|
+
const ModbusRTU = (mod.ModbusRTU ?? (mod.default as { ModbusRTU?: unknown } | undefined)?.ModbusRTU ?? mod) as new () => ModbusRtuClient
|
|
302
311
|
const client = new ModbusRTU()
|
|
303
312
|
client.setTimeout(3000)
|
|
304
313
|
// tcp = Modbus TCP(MBAP 封装);rtu-tcp = RTU over TCP(串口网关透传,CRC16 帧,无 MBAP)
|
|
@@ -490,6 +499,17 @@ export interface OpcUaConn {
|
|
|
490
499
|
|
|
491
500
|
const opcuaPool = new Map<string, OpcUaConn>()
|
|
492
501
|
|
|
502
|
+
/**
|
|
503
|
+
* 在飞建连去重表(同池 key → 尚未 settle 的建连任务)。
|
|
504
|
+
*
|
|
505
|
+
* 修复动因(实测事故):同端点**并发首连**此前无去重——两条采样/写控请求同时进入时
|
|
506
|
+
* 各建一条 OPCUAClient,而后一条的 `opcuaPool.set` 会**覆盖**前一条:
|
|
507
|
+
* 被覆盖的 session 与 TCP 连接从此无人持有,空闲 sweep 也扫不到 → **永久泄漏**;
|
|
508
|
+
* 重连 churn(断链演练/反复建节点)下还会触发 node-opcua 内部
|
|
509
|
+
* `_internal_create_secure_channel failed, this._secureChannel is supposed to be null`。
|
|
510
|
+
*/
|
|
511
|
+
const opcuaInflight = new Map<string, Promise<OpcUaConn>>()
|
|
512
|
+
|
|
493
513
|
// OPC UA 会话空闲回收(对照 modbus 池 sweep;节点删除/端点弃用后不再有读取
|
|
494
514
|
// 路径触发 3 错误驱逐,无 sweep 会话与 TCP 连接会永久驻留)
|
|
495
515
|
const OPCUA_IDLE_MS = 600_000
|
|
@@ -533,6 +553,20 @@ export async function getOpcUaConn(cfg: Record<string, unknown>): Promise<OpcUaC
|
|
|
533
553
|
existing.lastUsed = Date.now()
|
|
534
554
|
return existing
|
|
535
555
|
}
|
|
556
|
+
// 在飞去重:同 key 并发首连复用同一条建连任务(否则后写覆盖 pool → 前一条 session/socket 永久泄漏)
|
|
557
|
+
const pending = opcuaInflight.get(key)
|
|
558
|
+
if (pending) return pending
|
|
559
|
+
const task = createOpcUaConn(cfg, key)
|
|
560
|
+
opcuaInflight.set(key, task)
|
|
561
|
+
try {
|
|
562
|
+
return await task
|
|
563
|
+
}
|
|
564
|
+
finally {
|
|
565
|
+
opcuaInflight.delete(key)
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function createOpcUaConn(cfg: Record<string, unknown>, key: string): Promise<OpcUaConn> {
|
|
536
570
|
const opcua = reqNative('node-opcua') as typeof import('node-opcua')
|
|
537
571
|
const securityMode = (['None', 'Sign', 'SignAndEncrypt'] as const).includes(cfg.securityMode as 'None')
|
|
538
572
|
? (cfg.securityMode as 'None')
|
|
@@ -574,15 +608,33 @@ export async function getOpcUaConn(cfg: Record<string, unknown>): Promise<OpcUaC
|
|
|
574
608
|
}
|
|
575
609
|
const client = opcua.OPCUAClient.create(clientOpts as Parameters<typeof opcua.OPCUAClient.create>[0])
|
|
576
610
|
await client.connect(String(cfg.endpoint))
|
|
611
|
+
// type 是 node-opcua 的 UserIdentityInfo 判别式(node-opcua-client createUserIdentityToken →
|
|
612
|
+
// coerceUserIdentityInfo:userName 存在时正是补成 UserTokenType.UserName 走同一分支),
|
|
613
|
+
// 显式写出既满足协议类型,也免去"字段漏写、等库回填"的隐式依赖;两个分支都返回 Promise<ClientSession>。
|
|
577
614
|
const session = cfg.username
|
|
578
|
-
? await client.createSession({ userName: String(cfg.username), password: String(cfg.password ?? '') })
|
|
615
|
+
? await client.createSession({ type: opcua.UserTokenType.UserName, userName: String(cfg.username), password: String(cfg.password ?? '') })
|
|
579
616
|
: await client.createSession()
|
|
580
617
|
const conn: OpcUaConn = { session, client, lastUsed: Date.now(), errors: 0 }
|
|
618
|
+
// 竞态二次校验:建连期间若已有连接入池(并发首连的另一方完成),关闭本次新建并复用既有,
|
|
619
|
+
// 避免 opcuaPool.set 覆盖导致 session/socket 永久泄漏。
|
|
620
|
+
const raced = opcuaPool.get(key)
|
|
621
|
+
if (raced) {
|
|
622
|
+
try {
|
|
623
|
+
await session.close()
|
|
624
|
+
}
|
|
625
|
+
catch { /* 清理失败不覆盖原始错误 */ }
|
|
626
|
+
try {
|
|
627
|
+
await client.disconnect()
|
|
628
|
+
}
|
|
629
|
+
catch { /* 清理失败不覆盖原始错误 */ }
|
|
630
|
+
return raced
|
|
631
|
+
}
|
|
581
632
|
opcuaPool.set(key, conn)
|
|
582
633
|
return conn
|
|
583
634
|
}
|
|
584
635
|
|
|
585
|
-
/** 驱逐 OPC UA 连接(采样/写控共享池;连续故障后触发,下次调用重建)
|
|
636
|
+
/** 驱逐 OPC UA 连接(采样/写控共享池;连续故障后触发,下次调用重建)。
|
|
637
|
+
* 只作用于**已入池**的连接:在飞建连本身就是新建的,交由竞态二次校验处理。 */
|
|
586
638
|
export async function evictOpcUaConn(cfg: Record<string, unknown>): Promise<void> {
|
|
587
639
|
const key = opcuaKey(cfg)
|
|
588
640
|
const conn = opcuaPool.get(key)
|
|
@@ -961,7 +1013,10 @@ export const httpDaqDriver: DaqDriver = {
|
|
|
961
1013
|
return { ok: true, message: `接口可达,vector 帧 ${(j.points as unknown[]).length} 点`, latencyMs: Date.now() - t0 }
|
|
962
1014
|
}
|
|
963
1015
|
if (typeof j.png === 'string' || typeof j.blob === 'string') {
|
|
964
|
-
|
|
1016
|
+
// 与生产分支同构:png/blob 至少一个是 base64 字符串(见上一行守卫)。
|
|
1017
|
+
// b64 的取值表达式与原来逐字一致(断言运行时擦除),只是把 unknown 标注成 string 以取 .length
|
|
1018
|
+
const b64 = (j.png as string | undefined) ?? (j.blob as string)
|
|
1019
|
+
return { ok: true, message: `接口可达,image 帧(base64, ${b64.length} chars)`, latencyMs: Date.now() - t0 }
|
|
965
1020
|
}
|
|
966
1021
|
const v = extractNumeric(text, driverConfig.jsonPath ? String(driverConfig.jsonPath) : undefined)
|
|
967
1022
|
return { ok: true, message: `接口可达,取值 = ${v}`, sampleValue: v, latencyMs: Date.now() - t0 }
|
|
@@ -1020,16 +1075,64 @@ function pluginRegistry(): Map<string, DaqDriver> {
|
|
|
1020
1075
|
return g_plugins.__daqPluginDrivers ??= new Map()
|
|
1021
1076
|
}
|
|
1022
1077
|
|
|
1078
|
+
/**
|
|
1079
|
+
* 插件驱动的自描述目录(前端「添加节点」下拉与动态参数表单的数据源)。
|
|
1080
|
+
* 插件驱动在 DaqDriver 上携带可选 meta(label/status/configFields,与 DaqDriverMeta 同形、
|
|
1081
|
+
* kind 放宽为 string);未带 meta 的插件驱动只在 meta.pluginDrivers 里以 kind 出现,
|
|
1082
|
+
* 前端能感知存在但不出表单(如实降级,不猜测参数 schema)。
|
|
1083
|
+
*/
|
|
1084
|
+
export interface PluginDriverMeta {
|
|
1085
|
+
kind: string
|
|
1086
|
+
label: string
|
|
1087
|
+
status: 'builtin' | 'real' | 'planned'
|
|
1088
|
+
configFields?: import('../../../../shared/daq-protocol').DriverConfigField[]
|
|
1089
|
+
/** true = 来自插件协议插件(前端展示「插件」徽标) */
|
|
1090
|
+
plugin: true
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const g_metas = globalThis as typeof globalThis & { __daqPluginDriverMetas?: Map<string, PluginDriverMeta> }
|
|
1094
|
+
function pluginMetaRegistry(): Map<string, PluginDriverMeta> {
|
|
1095
|
+
return g_metas.__daqPluginDriverMetas ??= new Map()
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1023
1098
|
/** 插件驱动注册(kind 与内置冲突时覆盖并告警;resolveDaqDriver 插件优先) */
|
|
1024
1099
|
export function registerPluginDriver(driver: DaqDriver): void {
|
|
1025
1100
|
if (driver.kind in REGISTRY) log.warn(`[daq-drivers] 插件驱动覆盖内置:「${driver.kind}」`)
|
|
1026
1101
|
pluginRegistry().set(driver.kind, driver)
|
|
1102
|
+
const meta = (driver as DaqDriver & { meta?: Omit<PluginDriverMeta, 'kind' | 'plugin'> }).meta
|
|
1103
|
+
if (meta && typeof meta.label === 'string') {
|
|
1104
|
+
pluginMetaRegistry().set(driver.kind, {
|
|
1105
|
+
kind: driver.kind,
|
|
1106
|
+
label: meta.label,
|
|
1107
|
+
status: meta.status === 'builtin' || meta.status === 'planned' ? meta.status : 'real',
|
|
1108
|
+
configFields: Array.isArray(meta.configFields) ? meta.configFields : [],
|
|
1109
|
+
plugin: true,
|
|
1110
|
+
})
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
// 同名驱动重注册(热重载)可能这次不带 meta:旧 meta 一并清除,保持两边一致
|
|
1114
|
+
pluginMetaRegistry().delete(driver.kind)
|
|
1115
|
+
}
|
|
1027
1116
|
}
|
|
1028
1117
|
|
|
1029
1118
|
export function listPluginDrivers(): string[] {
|
|
1030
1119
|
return [...pluginRegistry().keys()]
|
|
1031
1120
|
}
|
|
1032
1121
|
|
|
1122
|
+
/**
|
|
1123
|
+
* 清空插件驱动与自描述目录(插件宿主热重载前调用):停用/卸载的插件驱动随之失效,
|
|
1124
|
+
* 只有本轮重新装载成功的插件驱动会再注册进来 —— 否则被停用的驱动会一直生效到进程重启。
|
|
1125
|
+
*/
|
|
1126
|
+
export function clearPluginDrivers(): void {
|
|
1127
|
+
pluginRegistry().clear()
|
|
1128
|
+
pluginMetaRegistry().clear()
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/** 插件驱动自描述目录(驱动目录合并端点用;热重载随重注册刷新) */
|
|
1132
|
+
export function listPluginDriverMetas(): PluginDriverMeta[] {
|
|
1133
|
+
return [...pluginMetaRegistry().values()]
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1033
1136
|
export function normalizeDriverKind(kind: string): DaqDriverKind {
|
|
1034
1137
|
if (kind === 'modbus') return 'modbus-tcp'
|
|
1035
1138
|
if (kind === 'rtu' || kind === 'modbus-rtu-tcp') return 'modbus-rtu'
|
|
@@ -1040,10 +1143,11 @@ export function resolveDaqDriver(kind: DaqDriverKind): DaqDriver {
|
|
|
1040
1143
|
return pluginRegistry().get(kind) ?? REGISTRY[kind] ?? mockDaqDriver
|
|
1041
1144
|
}
|
|
1042
1145
|
|
|
1043
|
-
/** 驱动可用性探测(meta 报告:包缺失时 UI 显示"未安装"
|
|
1146
|
+
/** 驱动可用性探测(meta 报告:包缺失时 UI 显示"未安装"而非硬失败;含插件驱动) */
|
|
1044
1147
|
export async function probeDriverAvailability(): Promise<Record<string, boolean>> {
|
|
1045
1148
|
const out: Record<string, boolean> = {}
|
|
1046
|
-
|
|
1149
|
+
const all: Array<[string, DaqDriver]> = [...Object.entries(REGISTRY), ...pluginRegistry()]
|
|
1150
|
+
for (const [kind, drv] of all) {
|
|
1047
1151
|
try {
|
|
1048
1152
|
out[kind] = await drv.available()
|
|
1049
1153
|
}
|
|
@@ -1053,3 +1157,29 @@ export async function probeDriverAvailability(): Promise<Record<string, boolean>
|
|
|
1053
1157
|
}
|
|
1054
1158
|
return out
|
|
1055
1159
|
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* 驱动目录合并(内置 DAQ_DRIVERS + 插件自描述;同名插件条目覆盖内置并标记 plugin)。
|
|
1163
|
+
* index.get.ts 以 `drivers` 下发,前端据此渲染「添加节点」下拉与动态参数表单。
|
|
1164
|
+
*/
|
|
1165
|
+
export async function driverCatalog(): Promise<Array<{
|
|
1166
|
+
kind: string
|
|
1167
|
+
label: string
|
|
1168
|
+
status: 'builtin' | 'real' | 'planned'
|
|
1169
|
+
configFields: import('../../../../shared/daq-protocol').DriverConfigField[]
|
|
1170
|
+
plugin?: boolean
|
|
1171
|
+
}>> {
|
|
1172
|
+
const out: Array<{ kind: string, label: string, status: 'builtin' | 'real' | 'planned', configFields: import('../../../../shared/daq-protocol').DriverConfigField[], plugin?: boolean }> = DAQ_DRIVERS.map(d => ({
|
|
1173
|
+
kind: d.kind,
|
|
1174
|
+
label: d.label,
|
|
1175
|
+
status: d.status,
|
|
1176
|
+
configFields: d.configFields,
|
|
1177
|
+
}))
|
|
1178
|
+
for (const m of listPluginDriverMetas()) {
|
|
1179
|
+
const i = out.findIndex(d => d.kind === m.kind)
|
|
1180
|
+
const entry = { kind: m.kind, label: m.label, status: m.status, configFields: m.configFields ?? [], plugin: true }
|
|
1181
|
+
if (i >= 0) out[i] = entry
|
|
1182
|
+
else out.push(entry)
|
|
1183
|
+
}
|
|
1184
|
+
return out
|
|
1185
|
+
}
|
|
@@ -20,10 +20,11 @@ export interface ActiveRunSnapshot {
|
|
|
20
20
|
/** 遥测上报端口:绑定设备的值回写 + 孪生场景推送 + 绑定校验(中心实现 = device-twin.repo) */
|
|
21
21
|
export interface DaqTelemetryPort {
|
|
22
22
|
/**
|
|
23
|
-
* 遥测回写:telemetry 键值写入绑定设备的孪生,state
|
|
23
|
+
* 遥测回写:telemetry 键值写入绑定设备的孪生,state 为同设备多节点汇聚的最严重态
|
|
24
|
+
* (alarm > warn > ok,与 device-twin.repo.applyTelemetry 的 nodeState 同一个域)。
|
|
24
25
|
* 目标设备不存在 → { ok:false }(调用方解绑自身,链路自愈);成功 → { ok:true, twinId }。
|
|
25
26
|
*/
|
|
26
|
-
applyTelemetry(deviceId: string, telemetry: Record<string, number | string | boolean>, state:
|
|
27
|
+
applyTelemetry(deviceId: string, telemetry: Record<string, number | string | boolean>, state: 'alarm' | 'warn' | 'ok'): { ok: boolean, twinId: string | null }
|
|
27
28
|
/** 孪生场景载荷(WS device.updated 帧;设备已删 → null) */
|
|
28
29
|
scenePayload(twinId: string): unknown | null
|
|
29
30
|
/** 绑定目标存在性校验 */
|
|
@@ -239,7 +239,9 @@ export class TimescaleAdapter implements TsdbPort {
|
|
|
239
239
|
at: tsToMs(r.ts),
|
|
240
240
|
kind: String(r.kind) as 'vector' | 'image',
|
|
241
241
|
points: String(r.kind) === 'vector' ? pointsFromMeta(r.meta) : undefined,
|
|
242
|
-
metrics
|
|
242
|
+
// 与 sqlite.adapter 同法:metrics 列由本管线写入(下沉处理器产出 avg/max/brightness/zone_* 数值),
|
|
243
|
+
// asObject 只做 JSON 还原(Record<string, unknown>),按端口契约收窄到 Record<string, number>(断言运行时擦除)
|
|
244
|
+
metrics: asObject(r.metrics) as Record<string, number>,
|
|
243
245
|
meta: asObject(r.meta),
|
|
244
246
|
deviceBindingId: r.device_binding_id ?? null,
|
|
245
247
|
lineId: r.line_id ?? null,
|
|
@@ -54,6 +54,10 @@ export function createChannelRepo(db: DatabaseSync) {
|
|
|
54
54
|
name: input.name,
|
|
55
55
|
description: input.description ?? '',
|
|
56
56
|
scenarioPrompt: input.scenarioPrompt ?? '',
|
|
57
|
+
// INSERT 不含该列 → 落库取列默认(llm_json TEXT NOT NULL DEFAULT '',见 database.ts v11);
|
|
58
|
+
// 返回的内存行按同一事实补该列,使 create() 的返回值与随后 findById() 读到的行形状一致。
|
|
59
|
+
// 注意:insert.run(...) 的参数列表保持不变(多传一个参数会与 SQL 占位符数量不符)。
|
|
60
|
+
llmJson: '',
|
|
57
61
|
leadAgentId: null,
|
|
58
62
|
workspace: input.workspace ?? '',
|
|
59
63
|
enabled: 1,
|