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
|
@@ -11,6 +11,7 @@ import { useEntitiesStore } from '../../../stores/workshop/entities'
|
|
|
11
11
|
import { useWorkshopWs } from '../../../composables/workshop/useWorkshopWs'
|
|
12
12
|
import { useUserStore } from '../../../stores/workshop/user'
|
|
13
13
|
import { useStorage } from '@vueuse/core'
|
|
14
|
+
import { useResponsive } from '../../../composables/useResponsive'
|
|
14
15
|
|
|
15
16
|
const { t } = useI18n()
|
|
16
17
|
|
|
@@ -36,6 +37,10 @@ onMounted(() => {
|
|
|
36
37
|
const entities = useEntitiesStore()
|
|
37
38
|
const { subscribe, unsubscribe, conn } = useWorkshopWs()
|
|
38
39
|
|
|
40
|
+
// 视口档位(唯一判据;SSR 期返回桌面档)——
|
|
41
|
+
// 窄屏(≤1023)三栏仪表台只剩 ~460px 画布,必须换成「单通道示波器」形态
|
|
42
|
+
const { isDesktop } = useResponsive()
|
|
43
|
+
|
|
39
44
|
const workspace = computed(() => wsStore.workspaces.find(w => w.id === wsId.value))
|
|
40
45
|
// 聚焦 channel:activeChannelId 须在挂载清单内(陈旧持久化/竞态下回退首频道),
|
|
41
46
|
// 避免订阅死频道 → 快照永不到达 → 右栏/时间线长时间"空数据"假象
|
|
@@ -77,7 +82,8 @@ const lastSeq = computed(() => (channelId.value ? conn.cursors[channelId.value]
|
|
|
77
82
|
|
|
78
83
|
// 视图切换(P1 三视图 + P2 多通道同屏 + P5 RPG 小镇)
|
|
79
84
|
// 深链:?view=lanes/board/split/town 直达指定视图(可分享/收藏)
|
|
80
|
-
|
|
85
|
+
// inspector 只在窄屏出现(桌面它是右侧常驻栏),故不进深链白名单
|
|
86
|
+
type CenterView = 'timeline' | 'lanes' | 'board' | 'split' | 'town' | 'inspector'
|
|
81
87
|
const VIEW_KEYS: Record<string, CenterView> = {
|
|
82
88
|
1: 'timeline',
|
|
83
89
|
2: 'lanes',
|
|
@@ -90,15 +96,24 @@ const initView = route.query.view
|
|
|
90
96
|
const view = ref<CenterView>(
|
|
91
97
|
typeof initView === 'string' && VIEW_VALUES.has(initView) ? initView as CenterView : 'timeline',
|
|
92
98
|
)
|
|
93
|
-
const viewOptions =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
const viewOptions = computed(() => {
|
|
100
|
+
const base = [
|
|
101
|
+
{ value: 'timeline', label: t('wsView.k3otu32010') },
|
|
102
|
+
{ value: 'lanes', label: 'Agent lanes' },
|
|
103
|
+
{ value: 'board', label: t('wsView.k3ko7a8011') },
|
|
104
|
+
{ value: 'split', label: t('wsView.k3xbmo012') },
|
|
105
|
+
{ value: 'town', label: t('wsView.k1cz0pbw013') },
|
|
106
|
+
]
|
|
107
|
+
// 窄屏「一次一区」:检查器不占侧栏,并入切换条(四区都由同一条承载)
|
|
108
|
+
return narrowUI.value ? [...base, { value: 'inspector', label: '检查器' }] : base
|
|
109
|
+
})
|
|
100
110
|
// 数字快捷键 1-5 直切视图(非输入焦点时;控制台型键盘操作与 ⌘K 面板同一取向)
|
|
101
111
|
const onViewKey = (ev: KeyboardEvent): void => {
|
|
112
|
+
// 窄屏抽屉:Esc 收起(与全站侧栏抽屉一致)
|
|
113
|
+
if (ev.key === 'Escape' && narrowUI.value && leftOpen.value) {
|
|
114
|
+
leftOpen.value = false
|
|
115
|
+
return
|
|
116
|
+
}
|
|
102
117
|
if (ev.metaKey || ev.ctrlKey || ev.altKey) return
|
|
103
118
|
const t = ev.target as HTMLElement | null
|
|
104
119
|
if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return
|
|
@@ -111,8 +126,31 @@ onMounted(() => window.addEventListener('keydown', onViewKey))
|
|
|
111
126
|
onBeforeUnmount(() => window.removeEventListener('keydown', onViewKey))
|
|
112
127
|
|
|
113
128
|
// 侧栏折叠(现代 harness 布局:左会话栏 / 右检查器可按需收起)
|
|
129
|
+
// 形态差异只在挂载后生效:SSR/首帧一律按桌面结构渲染,客户端接管后才翻档,
|
|
130
|
+
// 否则服务端 5 项切换条 vs 客户端 6 项会触发水合告警
|
|
131
|
+
const mounted = ref(false)
|
|
132
|
+
onMounted(() => {
|
|
133
|
+
mounted.value = true
|
|
134
|
+
})
|
|
135
|
+
const narrowUI = computed(() => mounted.value && !isDesktop.value)
|
|
136
|
+
|
|
114
137
|
const leftOpen = ref(true)
|
|
115
138
|
const rightOpen = ref(true)
|
|
139
|
+
/** 窄屏:左右侧栏都不再占位(收成覆盖式抽屉 / 并入切换条) */
|
|
140
|
+
const applyTier = (desktop: boolean): void => {
|
|
141
|
+
leftOpen.value = desktop
|
|
142
|
+
rightOpen.value = desktop
|
|
143
|
+
// 检查器只是窄屏的第 4 区:回桌面后它回到右栏,中部得有自己的区(否则中部空白)
|
|
144
|
+
if (desktop && view.value === 'inspector') view.value = 'timeline'
|
|
145
|
+
}
|
|
146
|
+
onMounted(() => {
|
|
147
|
+
if (!isDesktop.value) applyTier(false)
|
|
148
|
+
})
|
|
149
|
+
watch(isDesktop, d => applyTier(d))
|
|
150
|
+
// 抽屉里选中频道后自动收起(窄屏少一次手动关闭)
|
|
151
|
+
watch(channelId, (next, prev) => {
|
|
152
|
+
if (prev !== undefined && next !== prev && narrowUI.value) leftOpen.value = false
|
|
153
|
+
})
|
|
116
154
|
|
|
117
155
|
// 侧栏宽度拖拽调节(PaneSplitter;localStorage 持久化,双击复位到默认值)
|
|
118
156
|
const LEFT_W_DEFAULT = 248
|
|
@@ -192,8 +230,18 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
|
|
|
192
230
|
/>
|
|
193
231
|
</div>
|
|
194
232
|
<div class="right">
|
|
233
|
+
<!-- 窄屏:频道会话列表入口(覆盖式抽屉;该页一次只看一区) -->
|
|
195
234
|
<button
|
|
196
|
-
|
|
235
|
+
v-if="narrowUI"
|
|
236
|
+
class="pane-toggle im toggle-left-narrow"
|
|
237
|
+
:class="{ off: !leftOpen }"
|
|
238
|
+
title="频道会话列表"
|
|
239
|
+
@click="leftOpen = !leftOpen"
|
|
240
|
+
>
|
|
241
|
+
<span class="i-tabler-list-details im-pop" />
|
|
242
|
+
</button>
|
|
243
|
+
<button
|
|
244
|
+
class="pane-toggle im toggle-left-desk"
|
|
197
245
|
:class="{ off: !leftOpen }"
|
|
198
246
|
:title="$t('wsView.k1tsy2e4002')"
|
|
199
247
|
@click="leftOpen = !leftOpen"
|
|
@@ -201,7 +249,7 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
|
|
|
201
249
|
<span class="i-tabler-layout-sidebar-left-collapse im-pop" />
|
|
202
250
|
</button>
|
|
203
251
|
<button
|
|
204
|
-
class="pane-toggle im"
|
|
252
|
+
class="pane-toggle im toggle-right-desk"
|
|
205
253
|
:class="{ off: !rightOpen }"
|
|
206
254
|
:title="$t('wsView.k1tx0ppf003')"
|
|
207
255
|
@click="rightOpen = !rightOpen"
|
|
@@ -236,10 +284,16 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
|
|
|
236
284
|
|
|
237
285
|
<!-- 主体三栏(左/右侧栏可折叠 + 拖拽调宽;分隔条 hairline 即面板边界) -->
|
|
238
286
|
<div class="main">
|
|
287
|
+
<!-- 窄屏抽屉遮罩:点空白收起(不盖住底部 Composer,输入不被"锁在"抽屉后) -->
|
|
288
|
+
<div
|
|
289
|
+
v-if="narrowUI && leftOpen"
|
|
290
|
+
class="drawer-scrim"
|
|
291
|
+
@click="leftOpen = false"
|
|
292
|
+
/>
|
|
239
293
|
<div
|
|
240
294
|
v-if="leftOpen"
|
|
241
295
|
class="left-pane"
|
|
242
|
-
:style="{ flexBasis: `${leftWidth}px` }"
|
|
296
|
+
:style="{ flexBasis: narrowUI ? 'auto' : `${leftWidth}px` }"
|
|
243
297
|
>
|
|
244
298
|
<div class="left-scroll">
|
|
245
299
|
<workshop-channel-session-list :ws-id="wsId" />
|
|
@@ -250,7 +304,7 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
|
|
|
250
304
|
/>
|
|
251
305
|
</div>
|
|
252
306
|
<workshop-pane-splitter
|
|
253
|
-
v-if="leftOpen"
|
|
307
|
+
v-if="leftOpen && !narrowUI"
|
|
254
308
|
:label="$t('wsView.k1fkfvra005')"
|
|
255
309
|
@resize="resizeLeft"
|
|
256
310
|
@reset="leftWidth = LEFT_W_DEFAULT"
|
|
@@ -279,6 +333,13 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
|
|
|
279
333
|
v-else-if="view === 'town'"
|
|
280
334
|
:channel-id="channelId"
|
|
281
335
|
/>
|
|
336
|
+
<!-- 窄屏第 4 区:检查器并入切换条(桌面仍是右侧常驻栏) -->
|
|
337
|
+
<workshop-inspector-panel
|
|
338
|
+
v-else-if="view === 'inspector' && narrowUI"
|
|
339
|
+
:channel-id="channelId"
|
|
340
|
+
@open-agent="openAgent"
|
|
341
|
+
@open-task="openTask"
|
|
342
|
+
/>
|
|
282
343
|
</template>
|
|
283
344
|
<div
|
|
284
345
|
v-else
|
|
@@ -294,13 +355,13 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
|
|
|
294
355
|
</div>
|
|
295
356
|
</div>
|
|
296
357
|
<workshop-pane-splitter
|
|
297
|
-
v-if="rightOpen"
|
|
358
|
+
v-if="rightOpen && !narrowUI"
|
|
298
359
|
:label="$t('wsView.kwqb0st006')"
|
|
299
360
|
@resize="resizeRight"
|
|
300
361
|
@reset="rightWidth = RIGHT_W_DEFAULT"
|
|
301
362
|
/>
|
|
302
363
|
<div
|
|
303
|
-
v-if="rightOpen"
|
|
364
|
+
v-if="rightOpen && !narrowUI"
|
|
304
365
|
class="right-pane"
|
|
305
366
|
:class="{ empty: wsStore.loaded && !channelId }"
|
|
306
367
|
:style="{ flexBasis: `${rightWidth}px` }"
|
|
@@ -513,4 +574,131 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
|
|
|
513
574
|
color: var(--ink-faint);
|
|
514
575
|
}
|
|
515
576
|
.composer-pane { flex: 0 0 auto; }
|
|
577
|
+
|
|
578
|
+
/* ══════════════════════════════════════════════════════════════════════════
|
|
579
|
+
窄屏形态 · 单通道示波器(≤1023px) —— 一次只看一路信号
|
|
580
|
+
──────────────────────────────────────────────────────────────────────────
|
|
581
|
+
桌面是「三栏仪表台」:会话栏 248 + 画布 + 检查器 300 + 两条分隔条。
|
|
582
|
+
到了 ≤1023,画布只剩 ~460px(900–1023 还要再让 64px 图标轨),
|
|
583
|
+
硬塞的结果是三栏互相压扁。所以窄屏换一台仪器,而不是把仪表台缩小:
|
|
584
|
+
|
|
585
|
+
· 中部视图(时间线 / lanes / 任务板 / 检查器)→ 由顶栏切换条承载,
|
|
586
|
+
一次只挂载一区,顶部切换条独占一行、可横扫;
|
|
587
|
+
· 频道会话列表 → 覆盖式抽屉(点遮罩/Esc/选中频道收起),不再占位;
|
|
588
|
+
· 拖拽分隔条在窄屏没有意义(没有第二栏可分配宽度)→ 隐藏;
|
|
589
|
+
· 触摸目标 ≥40px、标签 ≥11.5px(手持 30cm 距离下 10px 只剩 6px 有效字号)。
|
|
590
|
+
|
|
591
|
+
⚠️ 断点数值与 main.css v5 / useResponsive.ts 一致(1024 = 三栏仪表台下限),
|
|
592
|
+
此处不再引入新魔数。桌面(≥1024)样式完全不受影响。
|
|
593
|
+
══════════════════════════════════════════════════════════════════════════ */
|
|
594
|
+
@media (max-width: 1023.98px) {
|
|
595
|
+
.harness {
|
|
596
|
+
/* 窄屏收掉壳体外留白,把纵向像素让给内容与底部 Composer */
|
|
597
|
+
height: calc(100dvh - var(--app-header-h, 56px) - var(--app-footer-h, 46px) - 8px);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.topbar {
|
|
601
|
+
flex-wrap: wrap;
|
|
602
|
+
gap: 6px 8px;
|
|
603
|
+
padding: 8px 10px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.topbar .left {
|
|
607
|
+
row-gap: 8px;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.ws-name {
|
|
611
|
+
max-width: 40vw;
|
|
612
|
+
font-size: 15px;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.chan-chip {
|
|
616
|
+
max-width: 34vw;
|
|
617
|
+
font-size: 12px;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/* 切换条独占一行:五个视图名不再挤掉工作区名(channel 名仍可见) */
|
|
621
|
+
.view-switch {
|
|
622
|
+
flex: 1 1 100%;
|
|
623
|
+
margin-left: 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.ws-state,
|
|
627
|
+
.seq {
|
|
628
|
+
font-size: 11.5px;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* 触摸目标:26px 的幽灵图标钮在手持设备上点不中 */
|
|
632
|
+
.pane-toggle {
|
|
633
|
+
width: 40px;
|
|
634
|
+
height: 40px;
|
|
635
|
+
font-size: 18px;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.toggle-left-desk,
|
|
639
|
+
.toggle-right-desk {
|
|
640
|
+
display: none;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.main {
|
|
644
|
+
position: relative;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.left-pane {
|
|
648
|
+
position: absolute;
|
|
649
|
+
top: 0;
|
|
650
|
+
bottom: 0;
|
|
651
|
+
left: 0;
|
|
652
|
+
z-index: 30;
|
|
653
|
+
width: min(86vw, 320px);
|
|
654
|
+
border-right: 1px solid var(--line);
|
|
655
|
+
box-shadow: var(--shadow-float);
|
|
656
|
+
animation: pane-in 0.18s cubic-bezier(0.22, 1, 0.36, 1);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.drawer-scrim {
|
|
660
|
+
position: absolute;
|
|
661
|
+
inset: 0;
|
|
662
|
+
z-index: 25;
|
|
663
|
+
background: color-mix(in srgb, var(--ink) 34%, transparent);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/* 窄屏没有第二栏可分宽度,拖拽条只会白占 9px */
|
|
667
|
+
.pane-splitter {
|
|
668
|
+
display: none;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
.pane-loading {
|
|
672
|
+
font-size: 13px;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/* 窄屏专属入口在桌面不出现(响应式形态差异不用 JS 表达,避免水合抖动) */
|
|
677
|
+
.toggle-left-narrow {
|
|
678
|
+
display: none;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
@media (max-width: 1023.98px) {
|
|
682
|
+
.toggle-left-narrow {
|
|
683
|
+
display: inline-flex;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
@media (prefers-reduced-motion: reduce) {
|
|
688
|
+
.left-pane {
|
|
689
|
+
animation: none;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
@keyframes pane-in {
|
|
694
|
+
from {
|
|
695
|
+
opacity: 0.5;
|
|
696
|
+
transform: translateX(-16px);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
to {
|
|
700
|
+
opacity: 1;
|
|
701
|
+
transform: none;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
516
704
|
</style>
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { watch } from 'vue'
|
|
15
15
|
import { createClientContext, type ClientContext } from '@/sdk/client.mjs'
|
|
16
|
-
import { usePluginPanels } from '@/app/composables/workshop/usePluginPanels'
|
|
17
|
-
import type
|
|
16
|
+
import { usePluginPanels, type PluginPanelEntry } from '@/app/composables/workshop/usePluginPanels'
|
|
17
|
+
import { useTownBus, type TownBus } from '~/composables/workshop/useTownBus'
|
|
18
18
|
|
|
19
19
|
interface ManifestEntry { name: string, enabled?: boolean, hasClient?: boolean }
|
|
20
|
+
/** vue-i18n 实例在 NuxtApp 上的最小面(只用到 mergeLocaleMessage + locale,故按鸭子类型声明确切形状) */
|
|
21
|
+
interface I18nLike { mergeLocaleMessage?: (locale: string, messages: Record<string, unknown>) => void, locale?: unknown }
|
|
20
22
|
interface I18nBundle { i18n?: Record<string, Record<string, Record<string, string>>> }
|
|
21
23
|
|
|
22
24
|
export default defineNuxtPlugin(async (nuxtApp) => {
|
|
@@ -26,8 +28,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
26
28
|
const loaded = new Map<string, ClientContext>()
|
|
27
29
|
const { unregisterPlugin } = usePluginPanels()
|
|
28
30
|
const panels = usePluginPanels()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
// NuxtApp.$i18n 的库类型与本文件用到的两个成员结构不同(mergeLocaleMessage 的参数更宽),故在边界处收敛成 I18nLike
|
|
32
|
+
const i18n = ((nuxtApp.$i18n ?? null) as unknown) as I18nLike | null
|
|
31
33
|
let bus: TownBus | null = null
|
|
32
34
|
try {
|
|
33
35
|
bus = useTownBus()
|
|
@@ -102,10 +104,13 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
102
104
|
}
|
|
103
105
|
const ctx = createClientContext({
|
|
104
106
|
name,
|
|
105
|
-
eventBridge: bridgeFactory
|
|
107
|
+
eventBridge: bridgeFactory,
|
|
106
108
|
ui: {
|
|
107
109
|
slots: ['plugins.page', 'settings.plugins', 'dashboard.widgets'],
|
|
108
|
-
|
|
110
|
+
// SDK 的 ClientPanelEntry.mount 声明为 void,宿主 PluginPanelEntry 则允许返回清理函数
|
|
111
|
+
// (宿主编排器按返回值回收面板)。这里只在边界处断言 mount 形状,entry 原样透传、不包一层:
|
|
112
|
+
// 插件真返回清理函数时仍会被回收,返回 void 时同样合法。
|
|
113
|
+
registerPanel: entry => panels.registerPanel({ ...entry, plugin: name, mount: entry.mount as PluginPanelEntry['mount'] }),
|
|
109
114
|
},
|
|
110
115
|
t: translatorFor(name),
|
|
111
116
|
getLocale: () => {
|
|
@@ -168,7 +173,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
168
173
|
const localeRef = i18n?.locale as { value?: string } | undefined
|
|
169
174
|
if (localeRef && typeof localeRef === 'object') {
|
|
170
175
|
watch(localeRef, (loc) => {
|
|
171
|
-
for (const
|
|
176
|
+
for (const ctx of loaded.values()) {
|
|
172
177
|
try {
|
|
173
178
|
ctx?.hooks?.emit('i18n:changed', { locale: loc })
|
|
174
179
|
}
|
|
@@ -185,7 +190,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|
|
185
190
|
path = useRoute().path ?? ''
|
|
186
191
|
}
|
|
187
192
|
catch { /* 路由上下文不可用时静默 */ }
|
|
188
|
-
for (const
|
|
193
|
+
for (const ctx of loaded.values()) {
|
|
189
194
|
try {
|
|
190
195
|
ctx?.hooks?.emit('page:change', { path })
|
|
191
196
|
}
|
package/app/stores/app.ts
CHANGED
|
@@ -5,6 +5,8 @@ export const useAppStore = defineStore('app', () => {
|
|
|
5
5
|
// 默认暗色 = Digital Twin 控制室是产品首态(可切换亮色,选择被持久化)
|
|
6
6
|
const isDark = ref(true)
|
|
7
7
|
const sidebarCollapsed = ref(false)
|
|
8
|
+
/** 窄屏抽屉导航是否展开(不持久化:每次进入页面都应关闭) */
|
|
9
|
+
const mobileNavOpen = ref(false)
|
|
8
10
|
/**
|
|
9
11
|
* 用户是否在设置页/页头显式切换过明暗。
|
|
10
12
|
* 只有"显式选择"才锁死主题,阻止服务端 theme.mode 的运行时跟随——
|
|
@@ -26,11 +28,41 @@ export const useAppStore = defineStore('app', () => {
|
|
|
26
28
|
sidebarCollapsed.value = !sidebarCollapsed.value
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
/**
|
|
32
|
+
* 抽屉式导航(窄屏,<900px)。与 sidebarCollapsed 分开的原因:
|
|
33
|
+
* 折叠是**桌面用户的持久偏好**(图标轨),抽屉是**窄屏的临时浮层**——
|
|
34
|
+
* 两者语义不同,共用一个状态会让"手机上开过一次抽屉"变成
|
|
35
|
+
* "桌面侧栏永久收起"(实测:antd Sider 的 breakpoint 就会这么干)。
|
|
36
|
+
*/
|
|
37
|
+
function openMobileNav() {
|
|
38
|
+
mobileNavOpen.value = true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function closeMobileNav() {
|
|
42
|
+
mobileNavOpen.value = false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toggleMobileNav() {
|
|
46
|
+
mobileNavOpen.value = !mobileNavOpen.value
|
|
47
|
+
}
|
|
48
|
+
|
|
29
49
|
function setAccent(color: string | null) {
|
|
30
50
|
accent.value = color
|
|
31
51
|
}
|
|
32
52
|
|
|
33
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
isDark,
|
|
55
|
+
sidebarCollapsed,
|
|
56
|
+
mobileNavOpen,
|
|
57
|
+
accent,
|
|
58
|
+
themeTouched,
|
|
59
|
+
toggleDark,
|
|
60
|
+
toggleSidebar,
|
|
61
|
+
openMobileNav,
|
|
62
|
+
closeMobileNav,
|
|
63
|
+
toggleMobileNav,
|
|
64
|
+
setAccent,
|
|
65
|
+
}
|
|
34
66
|
}, {
|
|
35
67
|
persist: {
|
|
36
68
|
pick: ['isDark', 'sidebarCollapsed', 'accent', 'themeTouched'],
|
|
@@ -176,7 +176,7 @@ export const useUserStore = defineStore('workshop.user', {
|
|
|
176
176
|
// token 真源是 30 天 cookie;hydration 后从 cookie 回填,无 cookie 即回登出态
|
|
177
177
|
pick: ['user'],
|
|
178
178
|
afterHydrate: (ctx) => {
|
|
179
|
-
const store = ctx.store as { user?:
|
|
179
|
+
const store = ctx.store as { user?: CurrentUser | null, applyCookie?: () => void }
|
|
180
180
|
if (!store.user) return
|
|
181
181
|
try {
|
|
182
182
|
const cookie = useCookie<string | null>('token')
|
|
@@ -30,8 +30,21 @@ interface ServerWorkspace {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const ACTIVE_KEY = 'workshop.activeChannel'
|
|
33
|
+
const ACTIVE_WS_KEY = 'workshop.activeWorkspace'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* localStorage 只在浏览器存在。这几个读写点都在 **store 的初始化路径**上
|
|
37
|
+
* (load() → saveActiveMap()),而 load() 完全可能被服务端调用 ——
|
|
38
|
+
* 少了守卫,一次 SSR 调用就会抛 "localStorage is not defined",
|
|
39
|
+
* 被 stability-guard 当成 fatal unhandledRejection **直接结束进程**
|
|
40
|
+
* (实测:生产实例在 /town 刷新后静默退出,日志只有一行 ReferenceError)。
|
|
41
|
+
*
|
|
42
|
+
* 约定与 app/stores/app.ts 一致:持久化层自己保证 SSR 安全,调用方不必先判断环境。
|
|
43
|
+
*/
|
|
44
|
+
const hasLocalStorage = (): boolean => typeof localStorage !== 'undefined'
|
|
33
45
|
|
|
34
46
|
function loadActiveMap(): Record<string, string> {
|
|
47
|
+
if (!hasLocalStorage()) return {}
|
|
35
48
|
try {
|
|
36
49
|
return JSON.parse(localStorage.getItem(ACTIVE_KEY) ?? '{}') as Record<string, string>
|
|
37
50
|
}
|
|
@@ -41,6 +54,7 @@ function loadActiveMap(): Record<string, string> {
|
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
function saveActiveMap(map: Record<string, string>): void {
|
|
57
|
+
if (!hasLocalStorage()) return
|
|
44
58
|
localStorage.setItem(ACTIVE_KEY, JSON.stringify(map))
|
|
45
59
|
}
|
|
46
60
|
|
|
@@ -51,12 +65,14 @@ export const useWorkspacesStore = defineStore('workshop.workspaces', {
|
|
|
51
65
|
}),
|
|
52
66
|
getters: {
|
|
53
67
|
activeWorkspaceId(state): string | null {
|
|
54
|
-
|
|
68
|
+
if (!hasLocalStorage()) return state.workspaces[0]?.id ?? null
|
|
69
|
+
return localStorage.getItem(ACTIVE_WS_KEY) ?? state.workspaces[0]?.id ?? null
|
|
55
70
|
},
|
|
56
71
|
},
|
|
57
72
|
actions: {
|
|
58
73
|
setActiveWorkspaceId(id: string | null): void {
|
|
59
|
-
|
|
74
|
+
if (!hasLocalStorage()) return
|
|
75
|
+
localStorage.setItem(ACTIVE_WS_KEY, id ?? '')
|
|
60
76
|
},
|
|
61
77
|
/** 拉取服务端 workspace 列表(合并本地 activeChannelId) */
|
|
62
78
|
async load(): Promise<void> {
|
package/app/utils/api-error.ts
CHANGED
package/cli/commands/build.mjs
CHANGED
|
@@ -25,10 +25,17 @@ export async function run(argv, ctx) {
|
|
|
25
25
|
console.log(`${color.cyan('›')} 构建生产产物 .output/ ...`)
|
|
26
26
|
// 统一生命周期:转发 SIGINT/SIGTERM(子进程不再变孤儿) + 等待退出 + 超时强杀
|
|
27
27
|
// + 退出码传播(被信号杀死 → 128+N,不再 code ?? 0 误报成功)
|
|
28
|
+
const env = localBypassEnv()
|
|
29
|
+
// Node ≥21 对 exports 里的"尾部斜杠映射"(\"./\": \"./x/\")发 DEP0155 弃用警告。
|
|
30
|
+
// 本项目依赖树里有两个这样的包(@babel/runtime → ./regenerator/、diff → ./lib/),
|
|
31
|
+
// 它们**不是我们的代码**,也改不动(除非 patch 上游);这条提示不影响产物正确性。
|
|
32
|
+
// 只关这一个 code,其它弃用/实验性警告照常显示 —— 不是把日志静音,是把
|
|
33
|
+
// "上游已弃用、我们无权修改"的确定噪音摘掉。
|
|
34
|
+
env.NODE_OPTIONS = [env.NODE_OPTIONS, '--disable-warning=DEP0155'].filter(Boolean).join(' ')
|
|
28
35
|
const code = await runChild(process.execPath, [nuxtBin, 'build'], {
|
|
29
36
|
cwd: root,
|
|
30
37
|
stdio: 'inherit',
|
|
31
|
-
env
|
|
38
|
+
env,
|
|
32
39
|
onSpawnError: err => console.log(`${color.red('✖')} 构建失败: ${err.message}`),
|
|
33
40
|
})
|
|
34
41
|
if (code !== 0) return code
|
package/docker-compose.yml
CHANGED
|
@@ -21,6 +21,12 @@ services:
|
|
|
21
21
|
daq-timescale:
|
|
22
22
|
image: timescale/timescaledb:latest-pg16
|
|
23
23
|
container_name: awshop-daq-timescale
|
|
24
|
+
# ⚠️ 必须显式放大 /dev/shm:Docker 默认只给 64MB,而 PostgreSQL 的并行查询
|
|
25
|
+
# 会按需扩容共享内存段 —— 不够时报的错是
|
|
26
|
+
# could not resize shared memory segment "...": No space left on device
|
|
27
|
+
# 这句话看起来像"磁盘满了",实际是 shm 满了,极难排查(实测在回退账本的
|
|
28
|
+
# 兜底评估查询上稳定复现,而宿主机还剩 230GB)。512MB 是 Postgres 官方镜像文档的建议值。
|
|
29
|
+
shm_size: 512mb
|
|
24
30
|
environment:
|
|
25
31
|
POSTGRES_USER: postgres # config.yml daq.timescale.user
|
|
26
32
|
POSTGRES_PASSWORD: awshop # config.yml daq.timescale.password
|
package/docs/cli.en.md
CHANGED
|
@@ -62,7 +62,7 @@ pnpm tui # the terminal workbench
|
|
|
62
62
|
### 3. Quick use via npx (no global install)
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
npx agentworkshop start # fetch and start (pin a version with: npx agentworkshop@0.7.
|
|
65
|
+
npx agentworkshop start # fetch and start (pin a version with: npx agentworkshop@0.7.41 start)
|
|
66
66
|
npx agentworkshop doctor # subcommands work as usual
|
|
67
67
|
npx -p agentworkshop aw doctor # use -p when you want the name aw
|
|
68
68
|
```
|
|
@@ -79,7 +79,7 @@ npx -p agentworkshop aw doctor # use -p when you want the name aw
|
|
|
79
79
|
| Package manager | `pnpm@11.9.0` (`packageManager`, needed only for source development) |
|
|
80
80
|
| Module format | ESM (`type: module`) |
|
|
81
81
|
| License | `PolyForm-Noncommercial-1.0.0` |
|
|
82
|
-
| Current version | `0.7.
|
|
82
|
+
| Current version | `0.7.41` (`package.json`) |
|
|
83
83
|
|
|
84
84
|
## 2. First start (the Claude Code experience)
|
|
85
85
|
|
package/docs/cli.md
CHANGED
|
@@ -60,7 +60,7 @@ pnpm tui # 终端工作台
|
|
|
60
60
|
### 3. npx 快速使用(免全局安装)
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
npx agentworkshop start # 拉取并启动(可锁定版本: npx agentworkshop@0.7.
|
|
63
|
+
npx agentworkshop start # 拉取并启动(可锁定版本: npx agentworkshop@0.7.41 start)
|
|
64
64
|
npx agentworkshop doctor # 子命令照常可用
|
|
65
65
|
npx -p agentworkshop aw doctor # 需要 aw 这个名字时用 -p 指定包
|
|
66
66
|
```
|
|
@@ -76,7 +76,7 @@ npx -p agentworkshop aw doctor # 需要 aw 这个名字时用 -p 指定包
|
|
|
76
76
|
| 包管理器 | `pnpm@11.9.0`(`packageManager`,仅源码开发需要) |
|
|
77
77
|
| 模块形态 | ESM(`type: module`) |
|
|
78
78
|
| 许可证 | `PolyForm-Noncommercial-1.0.0` |
|
|
79
|
-
| 当前版本 | `0.7.
|
|
79
|
+
| 当前版本 | `0.7.41`(`package.json`) |
|
|
80
80
|
|
|
81
81
|
## 二、首次启动(Claude Code 式)
|
|
82
82
|
|
package/docs/plugins.en.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
>
|
|
10
10
|
> Applies to: plugin system v2 has been available since **v0.7.29** (frontend component
|
|
11
11
|
> injection / plugin settings / plugin i18n / runtime services surface); this document
|
|
12
|
-
> describes **v0.7.
|
|
12
|
+
> describes **v0.7.41**. `ctx.sdkVersion` is currently `0.3.0`.
|
|
13
13
|
|
|
14
14
|
## 1. Folder layout & quick start
|
|
15
15
|
|
|
@@ -210,10 +210,41 @@ async setup(ctx) {
|
|
|
210
210
|
| Events | `ctx.events.on(type, fn)` / `ctx.events.off(type, fn)` | scene-event sugar: subscribes to `event:<type>` internally (see §7) |
|
|
211
211
|
| Paths | `ctx.paths.home / configRoot / dataDir`; `ctx.dataDir` | `ctx.dataDir` is this plugin's KV folder, `<configRoot>/data/plugins/<plugin>` |
|
|
212
212
|
| DAQ | `ctx.daq.registerDriver / registerProcessor / registerTemplate / onFrame / onSample / query / nodes` | driver, sink-processor and node-template registration + time-series queries + node metadata snapshot |
|
|
213
|
+
| Write control | `ctx.dcw.registerWriteDriver(driver)` | write-driver injection (symmetric to DAQ read drivers): contract `{ kind, available(), write(input), test(cfg), read?(input), meta? }`; disabled plugins lose their drivers on hot reload |
|
|
213
214
|
| Tools | `ctx.omp.registerTool({ name, label?, description, parameters, roles?, handler })` | agent tool injection; `roles` accepts `'lead'` and `'worker'` only (see §10) |
|
|
214
215
|
| Services | `ctx.services.names()` / `.get(name)` / `.provide(name, getter)` | runtime object surface: `get('daq' \| 'lines' \| 'channels' \| 'plugins')`; `provide` auto-prefixes `<plugin>.` |
|
|
215
216
|
| Permissions | `ctx.permissions.lineMode / visibleLineIds / listGrants / setGrants` | line-grant queries and management |
|
|
216
217
|
|
|
218
|
+
### Communication-protocol plugins (custom interface connections)
|
|
219
|
+
|
|
220
|
+
`ctx.daq.registerDriver` / `ctx.dcw.registerWriteDriver` are the **protocol plug-in points**: serial
|
|
221
|
+
links, other PLC protocols and private buses all inject here — the main pipelines (sampling loop /
|
|
222
|
+
write gateway / frontend forms) need zero changes.
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
ctx.daq.registerDriver({
|
|
226
|
+
kind: 'serial', // custom protocol kind (overriding a builtin logs a warning)
|
|
227
|
+
async available() { return true }, // protocol-stack probe (missing package → UI shows "not installed")
|
|
228
|
+
async sample({ driverConfig }) { return 25.3 }, // reading (scalar or v2 frame envelope)
|
|
229
|
+
async test(driverConfig) { return { ok: true, message: 'connected', sampleValue: 25.3 } },
|
|
230
|
+
meta: { // self-description (frontend "add node" dropdown + dynamic form)
|
|
231
|
+
label: 'Serial (RS-232/485)',
|
|
232
|
+
status: 'real',
|
|
233
|
+
configFields: [{ key: 'path', label: 'Port path', type: 'string', required: true, placeholder: 'COM3' }],
|
|
234
|
+
},
|
|
235
|
+
})
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Key points:
|
|
239
|
+
- The driver catalog is served via the `drivers` field of `GET /api/workshop/daq` (read) and
|
|
240
|
+
`GET /api/workshop/dcw` (write); the DAQ/DCW pages render plugin protocols and their forms from it
|
|
241
|
+
(with a ⌁ badge). Drivers registered without `meta` only appear in availability;
|
|
242
|
+
- Hot reload clears all plugin drivers first, then re-loads: **disabled/unloaded protocol drivers
|
|
243
|
+
take effect immediately upon removal** and never linger until restart;
|
|
244
|
+
- Builtin example: `server/plugins-builtin/serial-bridge` (serial Modbus RTU + ASCII line protocol,
|
|
245
|
+
with read/write drivers, `health`/`ports`/`probe` APIs, a frontend panel and the `serial_ports`
|
|
246
|
+
agent tool) — use it as the template for new protocol plugins.
|
|
247
|
+
|
|
217
248
|
### Where KV lives
|
|
218
249
|
|
|
219
250
|
`<configRoot>/data/plugins/<plugin>/kv.json` — **not** inside the plugin folder. Writes are authoritative in memory and flushed atomically (`tmp` + `rename`) after a 200 ms debounce; on shutdown and hot reload the pending debounce is cancelled and the state is flushed synchronously. `ctx.kv` has exactly four methods: `get / set / all / bump`.
|
|
@@ -524,6 +555,7 @@ export default {
|
|
|
524
555
|
- **A newly enabled browser plugin injects within 15 s** (instantly when the WS channel is up).
|
|
525
556
|
- **`server:close` / `onDispose` depend on graceful shutdown signals**; a forced kill on Windows does not trigger them.
|
|
526
557
|
- **`aw plugin list` only scans directories** — it reflects neither load failures nor runtime health.
|
|
558
|
+
- **Protocol plugins share the host process with their native stacks**: serial-bridge's RTU framing is a self-contained implementation (single serialport native stack; modbus-serial is deliberately NOT used — its wrapper reproducibly crashed under dev mode). Known boundary: **inside the `aw dev` process, a serial transaction that ends in write → timeout → close can trigger a native V8 crash in `bindings-cpp`** (HandleScope fatal — not catchable in JS, regardless of which port is used); **production mode (`aw start`) is unaffected — the exact same sequence was verified live to return an honest timeout with the server healthy**, and open-only probes are also fine under dev. Connect real hardware during development with this in mind; for hardened deployments, run the serial gateway in its own process (see the plc-node-simulator child-process pattern).
|
|
527
559
|
|
|
528
560
|
## 13. Pre-release checklist
|
|
529
561
|
|