agentworkshop 0.8.5 → 0.8.6
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.en.json +3 -3
- package/.AgentWorkShop/prompts/host-tools.json +14 -4
- package/.output/.AgentWorkShop/prompts/host-tools.en.json +3 -3
- package/.output/.AgentWorkShop/prompts/host-tools.json +14 -4
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{U_n8pryv2.js → 7op-BMk82.js} +1 -1
- package/.output/public/_nuxt/{zWZ26UWi.js → B1SIgyrl.js} +1 -1
- package/.output/public/_nuxt/{C4tXwpSt.js → BA2lmfW4.js} +1 -1
- package/.output/public/_nuxt/{EJkmXP432.js → BDXQqSkl2.js} +1 -1
- package/.output/public/_nuxt/{CY_7u1ST2.js → BI9jj13f2.js} +1 -1
- package/.output/public/_nuxt/{CGf9neEK2.js → BiXqyYNw2.js} +1 -1
- package/.output/public/_nuxt/{D9GoOkyP2.js → BlOm8JIs2.js} +1 -1
- package/.output/public/_nuxt/{CQ31GiJU2.js → BoXKUOiN2.js} +1 -1
- package/.output/public/_nuxt/{DZzbCFY2.js → BuLoPmcf.js} +1 -1
- package/.output/public/_nuxt/{D9ppMCbw.js → BwOK42JQ.js} +1 -1
- package/.output/public/_nuxt/{CZJ5cw6r2.js → C1RMfPoB2.js} +1 -1
- package/.output/public/_nuxt/{CGviZq4E.js → COfo_Bg3.js} +1 -1
- package/.output/public/_nuxt/CTGJHZ8X2.js +1 -0
- package/.output/public/_nuxt/{SgsmmrsZ.js → CY2NkFvI.js} +1 -1
- package/.output/public/_nuxt/{B4GOTVfN.js → C_4mzQ5X.js} +1 -1
- package/.output/public/_nuxt/{DGmFHb7m.js → CdvMP3lv.js} +1 -1
- package/.output/public/_nuxt/{iqxZGXsY.js → CooauJ7D.js} +1 -1
- package/.output/public/_nuxt/{C3vxePED.js → D1eVi1zr.js} +1 -1
- package/.output/public/_nuxt/{DnuJ66Jy.js → D6PUmBb0.js} +1 -1
- package/.output/public/_nuxt/{BBhroEeN.js → DC8sMA36.js} +1 -1
- package/.output/public/_nuxt/{DOUuGOQR2.js → DDHh85c42.js} +1 -1
- package/.output/public/_nuxt/{Cy_HDgSl.js → DDXOfmo2.js} +2 -2
- package/.output/public/_nuxt/{Cr2xJ_CY.js → DMFwLxfu.js} +1 -1
- package/.output/public/_nuxt/{BZg_MIK42.js → DoprKJ5a2.js} +1 -1
- package/.output/public/_nuxt/{BzBBEE-o2.js → Dr1FVeQG2.js} +1 -1
- package/.output/public/_nuxt/{W0sNnJQh.js → DtnsTW7e.js} +1 -1
- package/.output/public/_nuxt/{gUYi1hLJ2.js → DxDqd6T42.js} +1 -1
- package/.output/public/_nuxt/{B0UUODeh.js → DzU3zEKX.js} +1 -1
- package/.output/public/_nuxt/{BkO5WdX5.js → EgO70wrE.js} +1 -1
- package/.output/public/_nuxt/{7OZQiw002.js → HJKu4eEV2.js} +1 -1
- package/.output/public/_nuxt/{ChF6qLpY2.js → K5Xen0wZ2.js} +1 -1
- package/.output/public/_nuxt/{CNrRZmSx2.js → Rb4o4o-82.js} +1 -1
- package/.output/public/_nuxt/{BqBTlfiz.js → Tf6SJSDj.js} +1 -1
- package/.output/public/_nuxt/{DJaGVY4v2.js → ZzOyoXex2.js} +1 -1
- package/.output/public/_nuxt/{PbzVdCTA2.js → azOGy_tU2.js} +1 -1
- package/.output/public/_nuxt/{D1thFC3z.js → be-4wyTo.js} +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/593b65bd-cf1d-4b7f-a993-57e939c38f36.json +1 -0
- package/.output/public/_nuxt/{8b9CKi6F.js → dcjPjLPI.js} +1 -1
- package/.output/public/_nuxt/{ae2-5ZPt.js → h-Fn7pRD.js} +1 -1
- package/.output/public/_nuxt/{-gy9k_TE2.js → hyLdZsOd2.js} +1 -1
- package/.output/public/_nuxt/{BHxG41IQ2.js → o8sYG6xx2.js} +1 -1
- package/.output/server/chunks/_/access.mjs +1 -1
- package/.output/server/chunks/_/agent-dto.mjs +26 -0
- package/.output/server/chunks/_/agent-dto.mjs.map +1 -0
- package/.output/server/chunks/_/data-export.mjs +6 -6
- package/.output/server/chunks/_/data-query.mjs +3 -3
- package/.output/server/chunks/_/eda-run.repo.mjs +2 -2
- package/.output/server/chunks/_/features.mjs +5 -5
- package/.output/server/chunks/_/golden.mjs +3 -3
- package/.output/server/chunks/_/harness-spawn-policy.mjs +77 -0
- package/.output/server/chunks/_/harness-spawn-policy.mjs.map +1 -0
- package/.output/server/chunks/_/index.mjs +1 -1
- package/.output/server/chunks/_/line-scope.mjs +32 -0
- package/.output/server/chunks/_/line-scope.mjs.map +1 -0
- package/.output/server/chunks/_/mes-console.mjs +1 -1
- package/.output/server/chunks/_/nitro.mjs +2429 -1726
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/_/ocap.mjs +1 -1
- package/.output/server/chunks/_/rest-auth.mjs +1 -1
- package/.output/server/chunks/_/spc.mjs +1 -1
- package/.output/server/chunks/build/TownView-BWMDbosY.mjs +1 -1
- package/.output/server/chunks/build/daq-CdxcfY_G.mjs +1 -1
- package/.output/server/chunks/build/dcw-BlDQtS4q.mjs +1 -1
- package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
- package/.output/server/chunks/build/useDaqAddNode-D_Rkju0f.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailNode-DeOsDxK5.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailParams-Cm9a9r5G.mjs +1 -1
- package/.output/server/chunks/build/useDaqFilters-CctmO03p.mjs +1 -1
- package/.output/server/chunks/build/useDaqStream-B9ZBVQrI.mjs +1 -1
- package/.output/server/chunks/build/useDaqTemplates-CK37sMfi.mjs +1 -1
- package/.output/server/chunks/build/useDcwAddNode-DACVcEHs.mjs +1 -1
- package/.output/server/chunks/build/useDcwStream-Dir5ufAU.mjs +1 -1
- package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
- package/.output/server/chunks/routes/api/system/backup/run.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +17 -9
- package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +3 -0
- package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.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/channel-templates/_id_.patch.mjs +6 -0
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs +5 -0
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs +5 -2
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +7 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +3 -1
- package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +8 -6
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs +29 -49
- package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +6 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +4 -2
- package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +14 -5
- package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +7 -4
- package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +6 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +7 -4
- package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +7 -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 +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/mes-artifacts/file.get.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/dcw/mes-artifacts/file.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/mes-hook-test.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/mes-test-read.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +4 -2
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +5 -2
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/versions.get.mjs +7 -3
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/versions.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +8 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +6 -2
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +7 -4
- package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/eda/commonality.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/eda/vm/predict.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/exp/confirmations/_id/decide.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/exp/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/fs/dirs.get.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.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.post.mjs +2 -0
- package/.output/server/chunks/routes/api/workshop/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.post.mjs +8 -3
- package/.output/server/chunks/routes/api/workshop/index3.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index7.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/mes/datasets.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/autofix.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/compile.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/evidence.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/publish.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval-decision.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/transition.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/subgraph.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/validate.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/export/_ontologyId_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/index.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/scene.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/traces/_ontologyId_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId/compare.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/capabilities.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/index.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/index2.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/profiles/_id/publish.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/crosscheck.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/dispatch.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/solve.post.mjs +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/process-model/datasets.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/eligibility.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/index.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/specs/_id/publish.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/train.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/tools/sync.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +2 -2
- package/.output/server/chunks/virtual/entry.mjs +1 -1
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +4 -2
- package/README.md +9 -2
- package/config.yml +1 -1
- package/docker-compose.yml +4 -2
- package/docs/cli.en.md +2 -2
- package/docs/cli.md +2 -2
- package/docs/plugins.en.md +1 -1
- package/docs/plugins.md +1 -1
- package/docs/sdk.en.md +1 -1
- package/docs/sdk.md +1 -1
- package/package.json +1 -1
- package/scripts/_audit/_live-mqtt-e2e-20261028.mjs +219 -0
- package/scripts/_audit/prompt-injection-probe.mts +76 -0
- package/scripts/api-live-e2e.mjs +9 -3
- package/scripts/e2e-multiexpert-remediation-real.mjs +453 -0
- package/scripts/e2e-node-binding-3mode-real.mjs +697 -0
- package/scripts/e2e-node-binding-authz.mjs +332 -0
- package/scripts/security-acceptance.mjs +218 -0
- package/server/api/workshop/agent-tools/bindings/index.post.ts +33 -17
- package/server/api/workshop/agents/[id].patch.ts +3 -0
- package/server/api/workshop/agents/index.post.ts +3 -0
- package/server/api/workshop/channel-templates/[id].patch.ts +5 -0
- package/server/api/workshop/channel-templates/index.post.ts +6 -0
- package/server/api/workshop/channels/[id]/agents/[agentId].patch.ts +5 -0
- package/server/api/workshop/channels/[id]/agents/index.get.ts +8 -2
- package/server/api/workshop/channels/[id]/agents/index.post.ts +7 -0
- package/server/api/workshop/channels/[id]/index.get.ts +10 -4
- package/server/api/workshop/channels.post.ts +3 -0
- package/server/api/workshop/daq/[id]/ingest.post.ts +26 -22
- package/server/api/workshop/daq/[id].patch.ts +8 -0
- package/server/api/workshop/daq/alarms.get.ts +18 -16
- package/server/api/workshop/daq/infra/reconnect.post.ts +21 -5
- package/server/api/workshop/daq/test-driver.post.ts +10 -3
- package/server/api/workshop/dcw/[id]/param-ledger.get.ts +7 -1
- package/server/api/workshop/dcw/[id]/read.post.ts +6 -1
- package/server/api/workshop/dcw/[id].patch.ts +49 -40
- package/server/api/workshop/dcw/journal/index.get.ts +7 -3
- package/server/api/workshop/dcw/mes-artifacts/file.get.ts +7 -1
- package/server/api/workshop/dcw/mes-artifacts/index.get.ts +7 -1
- package/server/api/workshop/dcw/optimizations/[id]/series.get.ts +6 -1
- package/server/api/workshop/dcw/optimizations/[id].get.ts +3 -1
- package/server/api/workshop/dcw/optimizations/index.get.ts +6 -3
- package/server/api/workshop/dcw/params/[id]/read.post.ts +22 -17
- package/server/api/workshop/dcw/recipes/[id]/versions.get.ts +20 -14
- package/server/api/workshop/dcw/recipes/[id].delete.ts +33 -25
- package/server/api/workshop/dcw/recipes/index.get.ts +17 -12
- package/server/api/workshop/dcw/runs/[id]/close.post.ts +21 -15
- package/server/api/workshop/dcw/test-driver.post.ts +21 -16
- package/server/api/workshop/ws/hub.ts +2 -1
- package/server/plugins/01-instance-guard.ts +82 -0
- package/server/plugins/daq.ts +40 -8
- package/server/plugins/workshop.ts +12 -0
- package/server/services/workshop/agents/adapters/line-spawn.ts +24 -1
- package/server/services/workshop/agents/agent-dto.ts +54 -0
- package/server/services/workshop/agents/harness-spawn-policy.ts +114 -0
- package/server/services/workshop/agents/host-tool-bridge/dispatch.ts +22 -0
- package/server/services/workshop/agents/host-tool-bridge/index.ts +1 -1
- package/server/services/workshop/agents/host-tool-bridge/per-agent.ts +17 -3
- package/server/services/workshop/agents/host-tool-bridge/tools/tasks.ts +1 -1
- package/server/services/workshop/agents/host-tool-bridge/tools/team-delegation.ts +5 -1
- package/server/services/workshop/agents/industrial/dcw-tools.ts +6 -2
- package/server/services/workshop/agents/industrial/ops-tools.ts +54 -12
- package/server/services/workshop/agents/industrial/recipe-gate.ts +68 -15
- package/server/services/workshop/agents/industrial/tool-verify-legacy-specs.ts +2 -2
- package/server/services/workshop/agents/industrial/tool-verify.ts +31 -2
- package/server/services/workshop/agents/node-bindings.repo.ts +4 -3
- package/server/services/workshop/agents/node-delegation.ts +49 -4
- package/server/services/workshop/agents/tool-approvals.ts +23 -0
- package/server/services/workshop/agents/tool-reference-spec.ts +3 -0
- package/server/services/workshop/daq/bus/mqtt.adapter.ts +12 -0
- package/server/services/workshop/daq/daq-controller/crud.ts +5 -3
- package/server/services/workshop/daq/daq-controller/ingest.ts +23 -0
- package/server/services/workshop/daq/daq-controller/push.ts +24 -3
- package/server/services/workshop/daq/daq-controller/runtime-control.ts +6 -0
- package/server/services/workshop/daq/daq-controller/sweep-alarms.ts +3 -1
- package/server/services/workshop/daq/daq-controller/tsdb-twin.ts +21 -1
- package/server/services/workshop/daq/daq-node.repo.ts +10 -7
- package/server/services/workshop/daq/daq-node.ts +2 -0
- package/server/services/workshop/daq/daq-runtime.ts +10 -2
- package/server/services/workshop/daq/drivers/modbus-tcp.ts +8 -5
- package/server/services/workshop/daq/drivers/registry.ts +5 -4
- package/server/services/workshop/daq/infra.ts +13 -0
- package/server/services/workshop/daq/ingest-policy.ts +24 -1
- package/server/services/workshop/daq/storage/timescale.adapter.ts +42 -3
- package/server/services/workshop/db/database/migrations.ts +83 -68
- package/server/services/workshop/dcw/dcw-controller/crud.ts +27 -2
- package/server/services/workshop/dcw/dcw-controller/lines.ts +14 -3
- package/server/services/workshop/dcw/dcw-controller/recipes.ts +63 -5
- package/server/services/workshop/dcw/dcw-recipe.repo.ts +10 -1
- package/server/services/workshop/dcw/dcw-runtime.ts +37 -14
- package/server/services/workshop/dcw/drivers/shared.ts +62 -6
- package/server/services/workshop/dcw/recipe-op-anchor.ts +28 -0
- package/server/services/workshop/dcw/recipe-rollback-manager/rollback.ts +204 -190
- package/server/services/workshop/dcw/recipe-rollback-manager/sweep.ts +97 -77
- package/server/services/workshop/line-scope.ts +80 -0
- package/server/services/workshop/optimization/dispatch.service.ts +18 -2
- package/server/services/workshop/permissions.ts +2 -0
- package/server/services/workshop/runtime/agent-runtime/message.ts +17 -0
- package/server/services/workshop/runtime/agent-runtime/state.ts +9 -3
- package/server/services/workshop/runtime/agent-runtime/types.ts +2 -1
- package/server/services/workshop/runtime/channel-runtime.ts +3 -1
- package/server/services/workshop/runtime/mailbox.ts +6 -3
- package/server/services/workshop/runtime/manager/a2a.ts +4 -2
- package/server/services/workshop/runtime/manager/admin-channel.ts +1 -1
- package/server/services/workshop/runtime/manager/chat.ts +3 -1
- package/server/services/workshop/runtime/manager/host-tools.ts +1 -1
- package/server/services/workshop/runtime/manager/runtime-wiring.ts +31 -19
- package/server/services/workshop/runtime/manager/state.ts +43 -1
- package/server/services/workshop/runtime/permission-scope.ts +21 -0
- package/server/services/workshop/runtime/scheduler-loop/tick.ts +16 -4
- package/server/services/workshop/scene-events.ts +19 -0
- package/shared/daq-protocol.ts +24 -5
- package/shared/dcw-protocol/catalog.ts +4 -4
- package/.output/public/_nuxt/CCI-fwcS2.js +0 -1
- package/.output/public/_nuxt/builds/meta/5cd11284-05bf-4fcd-9a69-56a01c9f56dd.json +0 -1
|
@@ -9,6 +9,7 @@ import { zValidator } from '../../../utils/validate'
|
|
|
9
9
|
import { defineApiHandler } from '../../../utils/response'
|
|
10
10
|
import { AppError } from '../../../utils/errors'
|
|
11
11
|
import { getWorkshopManager } from '../../../plugins/workshop'
|
|
12
|
+
import { assertSpawnConfigAllowed } from '../../../services/workshop/agents/harness-spawn-policy'
|
|
12
13
|
|
|
13
14
|
const patchAgentSchema = z.object({
|
|
14
15
|
name: z.string().min(1).optional(),
|
|
@@ -26,5 +27,7 @@ export default defineApiHandler(async (event) => {
|
|
|
26
27
|
const agent = manager.getAgent(agentId)
|
|
27
28
|
if (!agent) throw new AppError(404, 'NOT_FOUND', `Agent 不存在: ${agentId}`)
|
|
28
29
|
manager.requireWritable(agent.ownerUserId, user, 'Agent 模板')
|
|
30
|
+
// 启动面收权(2026-10-28):command/args/cwd/env 只有 admin 能改,且命令须与引擎同族
|
|
31
|
+
assertSpawnConfigAllowed(String(body.harness ?? agent.harness), body.config, user)
|
|
29
32
|
return manager.updateAgent(agentId, body)
|
|
30
33
|
})
|
|
@@ -8,6 +8,7 @@ import { readValidatedBody } from 'h3'
|
|
|
8
8
|
import { zValidator } from '../../../utils/validate'
|
|
9
9
|
import { defineApiHandler } from '../../../utils/response'
|
|
10
10
|
import { getWorkshopManager } from '../../../plugins/workshop'
|
|
11
|
+
import { assertSpawnConfigAllowed } from '../../../services/workshop/agents/harness-spawn-policy'
|
|
11
12
|
|
|
12
13
|
const createAgentSchema = z.object({
|
|
13
14
|
name: z.string().min(1, 'name 必填'),
|
|
@@ -19,6 +20,8 @@ const createAgentSchema = z.object({
|
|
|
19
20
|
export default defineApiHandler(async (event) => {
|
|
20
21
|
const user = resolveUser(event)
|
|
21
22
|
const body = await readValidatedBody(event, zValidator(createAgentSchema))
|
|
23
|
+
// 启动面收权(2026-10-28):非 admin 不得指定 command/args/cwd/env
|
|
24
|
+
assertSpawnConfigAllowed(body.harness, body.config, user)
|
|
22
25
|
return getWorkshopManager().createAgent({
|
|
23
26
|
name: body.name,
|
|
24
27
|
harness: body.harness,
|
|
@@ -9,6 +9,7 @@ import { defineApiHandler } from '../../../utils/response'
|
|
|
9
9
|
import { AppError } from '../../../utils/errors'
|
|
10
10
|
import { resolveUser } from '../caller'
|
|
11
11
|
import { getWorkshopManager } from '../../../plugins/workshop'
|
|
12
|
+
import { assertSpawnConfigAllowed } from '../../../services/workshop/agents/harness-spawn-policy'
|
|
12
13
|
|
|
13
14
|
const memberSchema = z.union([
|
|
14
15
|
z.object({ templateId: z.string().min(1), role: z.enum(['lead', 'worker']) }),
|
|
@@ -44,5 +45,9 @@ export default defineApiHandler(async (event) => {
|
|
|
44
45
|
const tpl = manager.getChannelTemplate(templateId)
|
|
45
46
|
if (!tpl) throw new AppError(404, 'NOT_FOUND', `Channel 模板不存在: ${templateId}`)
|
|
46
47
|
manager.requireWritable(tpl.ownerUserId, user, 'Channel 模板')
|
|
48
|
+
if (body.lead) assertSpawnConfigAllowed(body.lead.harness, body.lead.config, user)
|
|
49
|
+
for (const m of body.members ?? []) {
|
|
50
|
+
if ('inline' in m) assertSpawnConfigAllowed(m.inline.harness, m.inline.config, user)
|
|
51
|
+
}
|
|
47
52
|
return manager.updateChannelTemplate(templateId, body)
|
|
48
53
|
})
|
|
@@ -9,6 +9,7 @@ import { zValidator } from '../../../utils/validate'
|
|
|
9
9
|
import { defineApiHandler } from '../../../utils/response'
|
|
10
10
|
import { AppError } from '../../../utils/errors'
|
|
11
11
|
import { getWorkshopManager } from '../../../plugins/workshop'
|
|
12
|
+
import { assertSpawnConfigAllowed } from '../../../services/workshop/agents/harness-spawn-policy'
|
|
12
13
|
|
|
13
14
|
const memberSchema = z.union([
|
|
14
15
|
z.object({ templateId: z.string().min(1), role: z.enum(['lead', 'worker']) }),
|
|
@@ -40,6 +41,11 @@ export default defineApiHandler(async (event) => {
|
|
|
40
41
|
const user = resolveUser(event)
|
|
41
42
|
const body = await readValidatedBody(event, zValidator(createSchema))
|
|
42
43
|
const manager = getWorkshopManager()
|
|
44
|
+
// 启动面收权(2026-10-28):模板内联成员同样不得由非 admin 指定 command/args/cwd/env
|
|
45
|
+
if (body.lead) assertSpawnConfigAllowed(body.lead.harness, body.lead.config, user)
|
|
46
|
+
for (const m of body.members ?? []) {
|
|
47
|
+
if ('inline' in m) assertSpawnConfigAllowed(m.inline.harness, m.inline.config, user)
|
|
48
|
+
}
|
|
43
49
|
// 成员模板引用校验:引用的 Agent 模板须操作者可读
|
|
44
50
|
for (const m of body.members ?? []) {
|
|
45
51
|
if ('templateId' in m) {
|
|
@@ -8,6 +8,7 @@ import { resolveUser } from '../../../caller'
|
|
|
8
8
|
import { AppError } from '../../../../../utils/errors'
|
|
9
9
|
import { defineApiHandler } from '../../../../../utils/response'
|
|
10
10
|
import { getWorkshopManager } from '../../../../../plugins/workshop'
|
|
11
|
+
import { assertSpawnConfigAllowed } from '../../../../../services/workshop/agents/harness-spawn-policy'
|
|
11
12
|
|
|
12
13
|
export default defineApiHandler(async (event) => {
|
|
13
14
|
const channelId = getRouterParam(event, 'id')!
|
|
@@ -19,6 +20,10 @@ export default defineApiHandler(async (event) => {
|
|
|
19
20
|
manager.requireWritable(channel.ownerUserId, user, 'channel')
|
|
20
21
|
const members = await manager.listChannelAgents(channelId)
|
|
21
22
|
if (!members.some(a => a.id === agentId)) throw new AppError(404, 'NOT_FOUND', `成员不存在: ${agentId}`)
|
|
23
|
+
if (body?.config) {
|
|
24
|
+
const member = members.find(a => a.id === agentId)!
|
|
25
|
+
assertSpawnConfigAllowed(member.harness, body.config, user)
|
|
26
|
+
}
|
|
22
27
|
const patch: { name?: string, config?: Record<string, unknown>, enabled?: number } = {}
|
|
23
28
|
if (typeof body?.name === 'string' && body.name.trim()) patch.name = body.name.trim()
|
|
24
29
|
if (body?.config && typeof body.config === 'object') patch.config = body.config
|
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
* GET /api/workshop/channels/:id/agents —— channel 成员列表。
|
|
3
3
|
* 双轨鉴权:用户 token(owner/遗留公共可见)或 agent token(本 channel 成员)。
|
|
4
4
|
* - channel 不存在 → 404 NOT_FOUND
|
|
5
|
+
*
|
|
6
|
+
* 2026-10-28 安全修复:响应不再回显 agent token(worker 借此冒充 lead);
|
|
7
|
+
* config 仅属主/admin 可见,且由 DTO 剔除启动类敏感键。
|
|
5
8
|
*/
|
|
6
9
|
import { getRouterParam } from 'h3'
|
|
7
10
|
import { resolveUser, resolveCallerOrNull } from '../../../caller'
|
|
8
11
|
import { defineApiHandler } from '../../../../../utils/response'
|
|
9
12
|
import { AppError } from '../../../../../utils/errors'
|
|
10
13
|
import { getWorkshopManager } from '../../../../../plugins/workshop'
|
|
14
|
+
import { channelAgentSummaries } from '../../../../../services/workshop/agents/agent-dto'
|
|
11
15
|
|
|
12
16
|
export default defineApiHandler(async (event) => {
|
|
13
17
|
const channelId = getRouterParam(event, 'id')!
|
|
14
18
|
const manager = getWorkshopManager()
|
|
15
19
|
// 双轨:优先按用户 token;无用户 token 时接受本 channel 成员的 agent token
|
|
20
|
+
let user: { id: string, role: string } | null = null
|
|
16
21
|
try {
|
|
17
|
-
|
|
22
|
+
user = resolveUser(event)
|
|
18
23
|
manager.getChannelForUser(channelId, user.id)
|
|
19
24
|
}
|
|
20
25
|
catch (err) {
|
|
@@ -23,5 +28,6 @@ export default defineApiHandler(async (event) => {
|
|
|
23
28
|
}
|
|
24
29
|
const channel = (await manager.listChannels()).find(c => c.id === channelId)
|
|
25
30
|
if (!channel) throw new AppError(404, 'NOT_FOUND', `channel 不存在: ${channelId}`)
|
|
26
|
-
|
|
31
|
+
const includeConfig = Boolean(user && (user.role === 'admin' || channel.ownerUserId === user.id))
|
|
32
|
+
return channelAgentSummaries(await manager.listChannelAgents(channelId), { includeConfig })
|
|
27
33
|
})
|
|
@@ -12,6 +12,7 @@ import { zValidator } from '../../../../../utils/validate'
|
|
|
12
12
|
import { defineApiHandler } from '../../../../../utils/response'
|
|
13
13
|
import { AppError } from '../../../../../utils/errors'
|
|
14
14
|
import { getWorkshopManager, ensureLeadSchedulerLoop } from '../../../../../plugins/workshop'
|
|
15
|
+
import { assertSpawnConfigAllowed } from '../../../../../services/workshop/agents/harness-spawn-policy'
|
|
15
16
|
|
|
16
17
|
const createAgentSchema = z.object({
|
|
17
18
|
agentId: z.string().min(1).optional(),
|
|
@@ -32,6 +33,12 @@ export default defineApiHandler(async (event) => {
|
|
|
32
33
|
const ch = manager.getChannelForUser(channelId, user.id)
|
|
33
34
|
manager.requireWritable(ch.ownerUserId, user, 'channel')
|
|
34
35
|
const role = body.role ?? 'worker'
|
|
36
|
+
// 启动面收权(2026-10-28):成员 config 的 command/args/cwd/env 同样只有 admin 可给
|
|
37
|
+
if (body.harness) assertSpawnConfigAllowed(body.harness, body.config, user)
|
|
38
|
+
else if (body.config) {
|
|
39
|
+
const tplHarness = body.agentId ? manager.getAgent(body.agentId)?.harness : undefined
|
|
40
|
+
if (tplHarness) assertSpawnConfigAllowed(tplHarness, body.config, user)
|
|
41
|
+
}
|
|
35
42
|
// 克隆模板时校验模板可读(属主/public 含内置/admin;他人 private → 403)
|
|
36
43
|
if (body.agentId) {
|
|
37
44
|
const tpl = manager.getAgent(body.agentId)
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GET /api/workshop/channels/:id —— channel 详情(含 workspace 与成员列表)。
|
|
3
3
|
* - channel 不存在 → 404 NOT_FOUND
|
|
4
|
+
*
|
|
5
|
+
* 2026-10-28 安全修复:成员列表经 DTO 投影,永不回显 agent token;
|
|
6
|
+
* config 仅属主/admin 可见。
|
|
4
7
|
*/
|
|
5
|
-
import { getRouterParam } from 'h3'
|
|
6
|
-
import { resolveUser } from '../../caller'
|
|
8
|
+
import { getRouterParam } from 'h3'
|
|
9
|
+
import { resolveUser } from '../../caller'
|
|
7
10
|
import { defineApiHandler } from '../../../../utils/response'
|
|
8
|
-
import { getWorkshopManager, ensureLeadSchedulerLoop } from '../../../../plugins/workshop'
|
|
11
|
+
import { getWorkshopManager, ensureLeadSchedulerLoop } from '../../../../plugins/workshop'
|
|
12
|
+
import { channelAgentSummaries } from '../../../../services/workshop/agents/agent-dto'
|
|
13
|
+
|
|
9
14
|
export default defineApiHandler(async (event) => {
|
|
10
15
|
const channelId = getRouterParam(event, 'id')!
|
|
11
16
|
const user = resolveUser(event)
|
|
@@ -14,5 +19,6 @@ export default defineApiHandler(async (event) => {
|
|
|
14
19
|
const channel = await manager.getChannel(channelId)
|
|
15
20
|
// 兜底:详情请求时确保 lead 的调度循环已装配(如启动恢复遗漏)
|
|
16
21
|
ensureLeadSchedulerLoop(manager, channelId)
|
|
17
|
-
|
|
22
|
+
const includeConfig = user.role === 'admin' || channel.ownerUserId === user.id
|
|
23
|
+
return { ...channel, agents: channelAgentSummaries(channel.agents, { includeConfig }) }
|
|
18
24
|
})
|
|
@@ -9,6 +9,7 @@ import { readValidatedBody } from 'h3'
|
|
|
9
9
|
import { zValidator } from '../../utils/validate'
|
|
10
10
|
import { defineApiHandler } from '../../utils/response'
|
|
11
11
|
import { getWorkshopManager, ensureLeadSchedulerLoop } from '../../plugins/workshop'
|
|
12
|
+
import { assertSpawnConfigAllowed } from '../../services/workshop/agents/harness-spawn-policy'
|
|
12
13
|
|
|
13
14
|
const createChannelSchema = z.object({
|
|
14
15
|
name: z.string().min(1, 'name 必填'),
|
|
@@ -38,6 +39,8 @@ const createChannelSchema = z.object({
|
|
|
38
39
|
export default defineApiHandler(async (event) => {
|
|
39
40
|
const user = resolveUser(event)
|
|
40
41
|
const body = await readValidatedBody(event, zValidator(createChannelSchema))
|
|
42
|
+
// 启动面收权(2026-10-28):lead harness 的 command/args/cwd/env 只有 admin 能指定
|
|
43
|
+
if (body.leadAgent) assertSpawnConfigAllowed(body.leadAgent.harness, body.leadAgent.config, user)
|
|
41
44
|
const manager = getWorkshopManager()
|
|
42
45
|
const result = await manager.createChannel({
|
|
43
46
|
name: body.name,
|
|
@@ -17,17 +17,14 @@
|
|
|
17
17
|
* - 向量 1..4096 个有限数;图像 ≤10MB 且 MIME image/*;
|
|
18
18
|
* - at 缺省=当前时间,回填窗默认 ±1h(env AW_DAQ_INGEST_BACKFILL_MS 可调,上限 7 天)。
|
|
19
19
|
*/
|
|
20
|
-
import { createHash } from 'node:crypto'
|
|
21
20
|
import { getHeader, getRouterParam, readBody } from 'h3'
|
|
22
21
|
import { resolveUser } from '@/server/api/workshop/caller'
|
|
23
22
|
import { defineApiHandler } from '@/server/utils/response'
|
|
24
23
|
import { getDaqController } from '@/server/services/workshop/daq/daq-controller'
|
|
25
24
|
import { findDaqTemplate } from '@/server/services/workshop/daq/daq-templates'
|
|
26
25
|
import { getDaqQueue } from '@/server/services/workshop/daq/bus'
|
|
27
|
-
import { getObjectStore } from '@/server/services/workshop/daq/objectstore'
|
|
28
|
-
import { daqObjectKey } from '@/server/services/workshop/daq/objectstore/objectstore-port'
|
|
29
26
|
import type { DaqSampleEnvelope } from '@/server/services/workshop/daq/bus/queue-port'
|
|
30
|
-
import { ingestAuthMode, ingestBackfillWindowMs,
|
|
27
|
+
import { ingestAuthMode, ingestBackfillWindowMs, ingestFutureSkewMs, ingestTimeVerdict, isWideBackfillWindow } from '@/server/services/workshop/daq/ingest-policy'
|
|
31
28
|
import { recordOps } from '@/server/services/workshop/ops/ops'
|
|
32
29
|
import { AppError } from '@/server/utils/errors'
|
|
33
30
|
|
|
@@ -116,8 +113,12 @@ export default defineApiHandler(async (event) => {
|
|
|
116
113
|
const atMs = atRaw ? Date.parse(atRaw) : now
|
|
117
114
|
if (!Number.isFinite(atMs)) throw new AppError(400, 'VALIDATION_ERROR', `at 不是合法时间: ${atRaw}(示例 2026-10-19T12:00:00+08:00)`)
|
|
118
115
|
const windowMs = ingestBackfillWindowMs()
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
const verdict = ingestTimeVerdict(atMs, now, windowMs, ingestFutureSkewMs())
|
|
117
|
+
if (verdict === 'future') {
|
|
118
|
+
throw new AppError(400, 'FUTURE_TIMESTAMP', `at 超出当前时间允许的前瞻偏差(最多 ${Math.round(ingestFutureSkewMs() / 1000)}s);请校准上游时钟后重试。`)
|
|
119
|
+
}
|
|
120
|
+
if (verdict === 'out-of-window') {
|
|
121
|
+
throw new AppError(400, 'VALIDATION_ERROR', `at 超出允许的回填范围(${Math.round(windowMs / 60_000)} 分钟),请检查时区/时钟。`)
|
|
121
122
|
}
|
|
122
123
|
const at = new Date(atMs).toISOString()
|
|
123
124
|
const state = ['ok', 'warn', 'alarm', 'offline'].includes(String(body.state ?? '')) ? String(body.state) : 'ok'
|
|
@@ -141,27 +142,27 @@ export default defineApiHandler(async (event) => {
|
|
|
141
142
|
}
|
|
142
143
|
if (buf.length === 0) throw new AppError(400, 'VALIDATION_ERROR', '图像内容为空。')
|
|
143
144
|
if (buf.length > IMAGE_MAX_BYTES) throw new AppError(413, 'PAYLOAD_TOO_LARGE', `图像超过 ${IMAGE_MAX_BYTES / 1024 / 1024}MB 上限。`)
|
|
144
|
-
const objectKey = daqObjectKey(node.id, atMs, mime.includes('jpeg') ? '.jpg' : mime.includes('webp') ? '.webp' : '.png')
|
|
145
|
-
await getObjectStore().put(objectKey, buf, mime)
|
|
146
|
-
const sha256 = createHash('sha256').update(buf).digest('hex')
|
|
147
145
|
const width = Number(body.image && typeof body.image === 'object' ? body.image.width : NaN)
|
|
148
146
|
const height = Number(body.image && typeof body.image === 'object' ? body.image.height : NaN)
|
|
147
|
+
// A-P1-3(2026-10-28 审查):外部推送与驱动采样/推送订阅走**同一个** processFrameSample
|
|
148
|
+
// —— 模板 sink 管线(thumbnail/deriveMetric/qualityGate)与落对象存储、sha256/size
|
|
149
|
+
// 计算都在服务端完成,绝不信任调用方自报的指标/指纹,也不绕过质量门。
|
|
150
|
+
const processed = await getDaqController().processExternalFrame(node, {
|
|
151
|
+
kind: 'image',
|
|
152
|
+
blob: buf,
|
|
153
|
+
mime,
|
|
154
|
+
width: Number.isFinite(width) ? width : undefined,
|
|
155
|
+
height: Number.isFinite(height) ? height : undefined,
|
|
156
|
+
metrics,
|
|
157
|
+
}, atMs)
|
|
158
|
+
if (!processed) throw new AppError(400, 'VALIDATION_ERROR', '图像帧被 sink 管线拒绝(模板/处理器配置不支持该帧)。')
|
|
149
159
|
env = {
|
|
150
160
|
nodeId: node.id,
|
|
151
161
|
templateRef: node.templateRef,
|
|
152
|
-
value: Number((metrics.avg ?? 0).toFixed(node.decimals)),
|
|
162
|
+
value: Number((processed.frame.metrics?.avg ?? metrics.avg ?? 0).toFixed(node.decimals)),
|
|
153
163
|
state,
|
|
154
164
|
at,
|
|
155
|
-
frame:
|
|
156
|
-
kind: 'image',
|
|
157
|
-
objectKey,
|
|
158
|
-
mime,
|
|
159
|
-
width: Number.isFinite(width) ? width : undefined,
|
|
160
|
-
height: Number.isFinite(height) ? height : undefined,
|
|
161
|
-
sha256,
|
|
162
|
-
size: buf.length,
|
|
163
|
-
metrics,
|
|
164
|
-
},
|
|
165
|
+
frame: processed.frame,
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
else if (kind === 'vector') {
|
|
@@ -169,13 +170,16 @@ export default defineApiHandler(async (event) => {
|
|
|
169
170
|
if (!points || points.length === 0) throw new AppError(400, 'VALIDATION_ERROR', '向量节点的载荷必须提供 points 数组。')
|
|
170
171
|
if (points.length > VECTOR_MAX_POINTS) throw new AppError(413, 'PAYLOAD_TOO_LARGE', `向量点数 ${points.length} 超过 ${VECTOR_MAX_POINTS} 上限。`)
|
|
171
172
|
const avg = points.reduce((a, b) => a + b, 0) / points.length
|
|
173
|
+
// 同上:派生指标由模板 sink 处理器产生(调用方只提供原始点列/可选初值)
|
|
174
|
+
const processed = await getDaqController().processExternalFrame(node, { kind: 'vector', points, metrics }, atMs)
|
|
175
|
+
if (!processed) throw new AppError(400, 'VALIDATION_ERROR', '向量帧被 sink 管线拒绝(模板/处理器配置不支持该帧)。')
|
|
172
176
|
env = {
|
|
173
177
|
nodeId: node.id,
|
|
174
178
|
templateRef: node.templateRef,
|
|
175
|
-
value: Number((metrics.avg ?? avg).toFixed(node.decimals)),
|
|
179
|
+
value: Number((processed.frame.metrics?.avg ?? metrics.avg ?? avg).toFixed(node.decimals)),
|
|
176
180
|
state,
|
|
177
181
|
at,
|
|
178
|
-
frame:
|
|
182
|
+
frame: processed.frame,
|
|
179
183
|
}
|
|
180
184
|
}
|
|
181
185
|
else {
|
|
@@ -5,6 +5,7 @@ import { getRouterParam, readBody } from 'h3'
|
|
|
5
5
|
import { resolveUser } from '@/server/api/workshop/caller'
|
|
6
6
|
import { requireLineMode } from '@/server/services/workshop/permissions'
|
|
7
7
|
import { defineApiHandler } from '@/server/utils/response'
|
|
8
|
+
import { AppError } from '@/server/utils/errors'
|
|
8
9
|
import { bindDaqHost } from '@/server/services/workshop/daq/host-bindings'
|
|
9
10
|
import { getDaqController, type DaqPatchInput } from '@/server/services/workshop/daq/daq-controller'
|
|
10
11
|
import { broadcastSceneEvent } from '../../../services/workshop/scene-events'
|
|
@@ -17,6 +18,13 @@ export default defineApiHandler(async (event) => {
|
|
|
17
18
|
const body = await readBody<DaqPatchInput>(event) ?? {}
|
|
18
19
|
// 产线权限:参数下发=操控能力(readonly/无权用户 403)
|
|
19
20
|
requireLineMode(user, getDaqController().byId(id)?.lineId, 'operate')
|
|
21
|
+
// 驱动/连接配置分权(2026-10-28):http/mqtt 驱动的 URL/topic 由 driverConfig 决定,
|
|
22
|
+
// operate 可借此让服务端连接任意目标;与节点 create 同口径收敛到 admin。
|
|
23
|
+
const DAQ_ADMIN_ONLY = ['driver', 'driverConfig'] as const
|
|
24
|
+
const denied = DAQ_ADMIN_ONLY.filter(k => (body as Record<string, unknown>)[k] !== undefined)
|
|
25
|
+
if (denied.length > 0 && user.role !== 'admin') {
|
|
26
|
+
throw new AppError(403, 'DAQ_CONFIG_ADMIN_ONLY', `字段「${denied.join('/')}」决定采集落点与协议参数,仅管理员可修改(当前角色 ${user.role})。`)
|
|
27
|
+
}
|
|
20
28
|
const node = getDaqController().patch(id, body)
|
|
21
29
|
recordOps({
|
|
22
30
|
actor: user.id,
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GET /api/workshop/daq/alarms?scope=open|all&limit= —— 报警列表(S5)。
|
|
3
|
-
* open = 未确认(默认);all = 含已确认历史(审计/复盘)。
|
|
4
|
-
*/
|
|
5
|
-
import { getQuery } from 'h3'
|
|
6
|
-
import { resolveUser } from '@/server/api/workshop/caller'
|
|
7
|
-
import { defineApiHandler } from '@/server/utils/response'
|
|
8
|
-
import { getDaqController } from '@/server/services/workshop/daq/daq-controller'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/workshop/daq/alarms?scope=open|all&limit= —— 报警列表(S5)。
|
|
3
|
+
* open = 未确认(默认);all = 含已确认历史(审计/复盘)。
|
|
4
|
+
*/
|
|
5
|
+
import { getQuery } from 'h3'
|
|
6
|
+
import { resolveUser } from '@/server/api/workshop/caller'
|
|
7
|
+
import { defineApiHandler } from '@/server/utils/response'
|
|
8
|
+
import { getDaqController } from '@/server/services/workshop/daq/daq-controller'
|
|
9
|
+
import { daqNodeLineId, filterRowsByLine } from '@/server/services/workshop/line-scope'
|
|
10
|
+
|
|
11
|
+
export default defineApiHandler(async (event) => {
|
|
12
|
+
const user = resolveUser(event)
|
|
13
|
+
const q = getQuery(event)
|
|
14
|
+
const scope = q.scope === 'all' ? 'all' : 'open'
|
|
15
|
+
const limit = Math.min(500, Math.max(1, Number(q.limit) || 100))
|
|
16
|
+
const alarms = getDaqController().listAlarms(scope, limit)
|
|
17
|
+
return { alarms: filterRowsByLine(user, alarms, a => daqNodeLineId(a.nodeId)) }
|
|
18
|
+
})
|
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* POST /api/workshop/daq/infra/reconnect —— 手动重连(探测→Docker 拉起→重建后端→恢复/停用采集)。
|
|
3
|
+
*
|
|
4
|
+
* 2026-10-28 安全修复:此前仅 resolveUser —— 任意登录用户都能触发 docker compose、
|
|
5
|
+
* 重建后端并全局启停采样;总控 /daq/controller 却要求 admin/editor,口径不一致。
|
|
6
|
+
* 同时加单飞:并发 reconnect 会互相打断采集。
|
|
3
7
|
*/
|
|
4
|
-
import {
|
|
8
|
+
import { requireRole } from '@/server/api/workshop/caller'
|
|
5
9
|
import { defineApiHandler } from '@/server/utils/response'
|
|
10
|
+
import { AppError } from '@/server/utils/errors'
|
|
6
11
|
import { daqInfraConfig, daqInfraStatus, type DaqInfraConfig } from '@/server/services/workshop/daq/infra'
|
|
7
12
|
|
|
13
|
+
const g = globalThis as unknown as { __daqReconnectInFlight?: boolean }
|
|
14
|
+
|
|
8
15
|
export default defineApiHandler(async (event) => {
|
|
9
|
-
|
|
16
|
+
requireRole(event, ['admin', 'editor'])
|
|
10
17
|
const apply = (globalThis as unknown as { __daqApplyInfra?: (c: DaqInfraConfig) => Promise<void> }).__daqApplyInfra
|
|
11
18
|
if (!apply) {
|
|
12
19
|
return { infra: { ...daqInfraStatus(), warning: '启动插件未就绪,稍后重试' } }
|
|
13
20
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
if (g.__daqReconnectInFlight) {
|
|
22
|
+
throw new AppError(409, 'RECONNECT_IN_FLIGHT', '已有一次基础设施重连在执行,请等待完成后再试')
|
|
23
|
+
}
|
|
24
|
+
g.__daqReconnectInFlight = true
|
|
25
|
+
try {
|
|
26
|
+
// cfg 由启动插件闭包持有(与运行配置同源);此处触发一次完整装配
|
|
27
|
+
await apply(daqInfraConfig())
|
|
28
|
+
return { infra: daqInfraStatus() }
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
g.__daqReconnectInFlight = false
|
|
32
|
+
}
|
|
17
33
|
})
|
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
* body: { driver: DaqDriverKind, driverConfig: Record<string, string|number|boolean> }
|
|
4
4
|
* → DriverTestResult { ok, message, sampleValue?, latencyMs? }
|
|
5
5
|
* 前端添加节点向导"测试连接"直达;通过后可直接创建节点开始采集。
|
|
6
|
+
*
|
|
7
|
+
* 2026-10-28 安全修复:仅 admin/editor 可调用(此前任意登录用户可提交任意连接配置
|
|
8
|
+
* 让服务端连内网目标);driverConfig.allowPrivateHost 不再接受调用方自填,
|
|
9
|
+
* 私网白名单只认服务端开关 AW_TEST_DRIVER_ALLOW_PRIVATE=1。
|
|
6
10
|
*/
|
|
7
11
|
import { readBody } from 'h3'
|
|
8
|
-
import {
|
|
12
|
+
import { requireRole } from '@/server/api/workshop/caller'
|
|
9
13
|
import { defineApiHandler } from '@/server/utils/response'
|
|
10
14
|
import { bindDaqHost } from '@/server/services/workshop/daq/host-bindings'
|
|
11
15
|
import { getDaqController } from '@/server/services/workshop/daq/daq-controller'
|
|
@@ -14,10 +18,13 @@ import { requireDriverKind } from '@/server/services/workshop/daq/drivers'
|
|
|
14
18
|
import type { DaqDriverKind } from '#shared/daq-protocol'
|
|
15
19
|
|
|
16
20
|
export default defineApiHandler(async (event) => {
|
|
17
|
-
|
|
21
|
+
requireRole(event, ['admin', 'editor'])
|
|
18
22
|
bindDaqHost(broadcastSceneEvent)
|
|
19
23
|
const body = await readBody<{ driver?: DaqDriverKind, driverConfig?: Record<string, string | number | boolean> }>(event) ?? {}
|
|
20
24
|
if (!body.driver) return { ok: false, message: '缺少 driver' }
|
|
21
|
-
const
|
|
25
|
+
const cfg: Record<string, string | number | boolean> = { ...(body.driverConfig ?? {}) }
|
|
26
|
+
delete cfg.allowPrivateHost
|
|
27
|
+
if (process.env.AW_TEST_DRIVER_ALLOW_PRIVATE === '1') cfg.allowPrivateHost = 'true'
|
|
28
|
+
const result = await getDaqController().testDriver(requireDriverKind(body.driver), cfg)
|
|
22
29
|
return { test: result }
|
|
23
30
|
})
|
|
@@ -6,10 +6,16 @@ import { getRouterParam } from 'h3'
|
|
|
6
6
|
import { resolveUser } from '@/server/api/workshop/caller'
|
|
7
7
|
import { defineApiHandler } from '@/server/utils/response'
|
|
8
8
|
import { getRecipeRollBackManager } from '@/server/services/workshop/dcw/recipe-rollback-manager'
|
|
9
|
+
import { getDcwController } from '@/server/services/workshop/dcw/dcw-controller'
|
|
10
|
+
import { AppError, ErrorCodes } from '@/server/utils/errors'
|
|
11
|
+
import { requireLineForResource } from '@/server/services/workshop/line-scope'
|
|
9
12
|
|
|
10
13
|
export default defineApiHandler(async (event) => {
|
|
11
|
-
resolveUser(event)
|
|
14
|
+
const user = resolveUser(event)
|
|
12
15
|
const id = getRouterParam(event, 'id') ?? ''
|
|
16
|
+
const node = getDcwController().byId(id)
|
|
17
|
+
if (!node) throw new AppError(404, ErrorCodes.NOT_FOUND, `控制节点不存在: ${id}`)
|
|
18
|
+
requireLineForResource(user, node.lineId, '数控节点', 'readonly')
|
|
13
19
|
const ledger = getRecipeRollBackManager().ledger(id)
|
|
14
20
|
return { ledger }
|
|
15
21
|
})
|
|
@@ -8,11 +8,16 @@ import { resolveUser } from '@/server/api/workshop/caller'
|
|
|
8
8
|
import { defineApiHandler } from '@/server/utils/response'
|
|
9
9
|
import { bindDcwBroadcast, getDcwController } from '@/server/services/workshop/dcw/dcw-controller'
|
|
10
10
|
import { broadcastSceneEvent } from '@/server/services/workshop/scene-events'
|
|
11
|
+
import { AppError, ErrorCodes } from '@/server/utils/errors'
|
|
12
|
+
import { requireLineForResource } from '@/server/services/workshop/line-scope'
|
|
11
13
|
|
|
12
14
|
export default defineApiHandler(async (event) => {
|
|
13
|
-
resolveUser(event)
|
|
15
|
+
const user = resolveUser(event)
|
|
14
16
|
bindDcwBroadcast(broadcastSceneEvent)
|
|
15
17
|
const id = getRouterParam(event, 'id') ?? ''
|
|
18
|
+
const node = getDcwController().byId(id)
|
|
19
|
+
if (!node) throw new AppError(404, ErrorCodes.NOT_FOUND, `控制节点不存在: ${id}`)
|
|
20
|
+
requireLineForResource(user, node.lineId, '数控节点', 'readonly')
|
|
16
21
|
const read = await getDcwController().readNow(id)
|
|
17
22
|
return { read }
|
|
18
23
|
})
|
|
@@ -1,40 +1,49 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PATCH /api/workshop/dcw/:id —— 单节点参数(名称/驱动/保写周期/量程/启停/落点)。
|
|
3
|
-
*
|
|
4
|
-
* 鉴权:与 write/test/bind 同口径 requireLineMode(..., 'operate')。
|
|
5
|
-
* 本端点可改 driverConfig(落点 host:register)、min/max(直接决定 writeTolerance)、
|
|
6
|
-
* holdIntervalMs(保写心跳周期)与 enabled —— 改完之后**网关自己的心跳**就会把
|
|
7
|
-
* 该节点的既有设定值写进攻击者指定的目标。即「改参数」等价于「远程写 PLC」,
|
|
8
|
-
* 早先此处只有 resolveUser,任何登录用户(零产线授权)都能改任意节点。
|
|
9
|
-
*/
|
|
10
|
-
import { createError, getRouterParam, readBody } from 'h3'
|
|
11
|
-
import type { DcwPatchInput } from '@/server/services/workshop/dcw/dcw-controller'
|
|
12
|
-
import { resolveUser } from '@/server/api/workshop/caller'
|
|
13
|
-
import { defineApiHandler } from '@/server/utils/response'
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
1
|
+
/**
|
|
2
|
+
* PATCH /api/workshop/dcw/:id —— 单节点参数(名称/驱动/保写周期/量程/启停/落点)。
|
|
3
|
+
*
|
|
4
|
+
* 鉴权:与 write/test/bind 同口径 requireLineMode(..., 'operate')。
|
|
5
|
+
* 本端点可改 driverConfig(落点 host:register)、min/max(直接决定 writeTolerance)、
|
|
6
|
+
* holdIntervalMs(保写心跳周期)与 enabled —— 改完之后**网关自己的心跳**就会把
|
|
7
|
+
* 该节点的既有设定值写进攻击者指定的目标。即「改参数」等价于「远程写 PLC」,
|
|
8
|
+
* 早先此处只有 resolveUser,任何登录用户(零产线授权)都能改任意节点。
|
|
9
|
+
*/
|
|
10
|
+
import { createError, getRouterParam, readBody } from 'h3'
|
|
11
|
+
import type { DcwPatchInput } from '@/server/services/workshop/dcw/dcw-controller'
|
|
12
|
+
import { resolveUser } from '@/server/api/workshop/caller'
|
|
13
|
+
import { defineApiHandler } from '@/server/utils/response'
|
|
14
|
+
import { AppError } from '@/server/utils/errors'
|
|
15
|
+
import { requireLineMode } from '@/server/services/workshop/permissions'
|
|
16
|
+
import { bindDcwBroadcast, getDcwController } from '@/server/services/workshop/dcw/dcw-controller'
|
|
17
|
+
import { broadcastSceneEvent } from '@/server/services/workshop/scene-events'
|
|
18
|
+
import { recordOps } from '@/server/services/workshop/ops/ops'
|
|
19
|
+
|
|
20
|
+
export default defineApiHandler(async (event) => {
|
|
21
|
+
const user = resolveUser(event)
|
|
22
|
+
bindDcwBroadcast(broadcastSceneEvent)
|
|
23
|
+
const id = getRouterParam(event, 'id') ?? ''
|
|
24
|
+
const body = await readBody<DcwPatchInput>(event) ?? {}
|
|
25
|
+
const node0 = getDcwController().byId(id)
|
|
26
|
+
if (!node0) throw createError({ statusCode: 404, statusMessage: `控制节点不存在: ${id}` })
|
|
27
|
+
// 目标产线:允许本次 PATCH 迁移产线,则新产线也须有操控权(防止把节点搬到无权产线)
|
|
28
|
+
const targetLine = body.lineId !== undefined ? String(body.lineId || '') || null : node0.lineId
|
|
29
|
+
requireLineMode(user, targetLine, 'operate')
|
|
30
|
+
// 落点/安全配置分权(2026-10-28):operate 只能改运行参数,不能改"往哪个设备写、
|
|
31
|
+
// 量程/步限/保持窗、标定函数"。driverConfig 支持 hook/任意 host,等价于代码执行与
|
|
32
|
+
// 跨线重定向,必须与 create 同权(admin)。此前这里只校验 operate。
|
|
33
|
+
const ADMIN_ONLY_FIELDS = ['driver', 'driverConfig', 'transform', 'min', 'max', 'decimals', 'stepLimit', 'writeLockSeconds', 'lineId'] as const
|
|
34
|
+
const deniedFields = ADMIN_ONLY_FIELDS.filter(k => body[k] !== undefined)
|
|
35
|
+
if (deniedFields.length > 0 && user.role !== 'admin') {
|
|
36
|
+
throw new AppError(403, 'DCW_CONFIG_ADMIN_ONLY', `字段「${deniedFields.join('/')}」决定设备落点与安全限界,仅管理员可修改(当前角色 ${user.role})。运行参数(name/enabled/holdIntervalMs/readIntervalMs/semantics/位置)可由产线操控者调整。`)
|
|
37
|
+
}
|
|
38
|
+
const node = getDcwController().patch(id, body)
|
|
39
|
+
// 节点参数/落点/量程/启停变更 = 产线可写面变更,必须进产线日志(含跨线迁移)
|
|
40
|
+
const changed = Object.keys(body)
|
|
41
|
+
recordOps({
|
|
42
|
+
actor: user.id, actorName: user.name, actorKind: 'user',
|
|
43
|
+
action: 'dcw.patch', kind: 'line', targetKind: 'dcw-node', targetId: id,
|
|
44
|
+
lineId: node.lineId ?? '',
|
|
45
|
+
summary: `修改数控节点「${node.name}」(${changed.join('/') || '无变更'})${node0.lineId !== node.lineId ? `:产线 ${node0.lineId || '(无)'} → ${node.lineId || '(无)'}` : ''}`,
|
|
46
|
+
detail: { keys: changed, fromLine: node0.lineId ?? '', toLine: node.lineId ?? '' },
|
|
47
|
+
})
|
|
48
|
+
return { node: node.toView() }
|
|
49
|
+
})
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GET /api/workshop/dcw/journal —— 参数变更账本查询(在册历史;append-only)。
|
|
3
3
|
* 查询参数:nodeId / lineId / source / limit。
|
|
4
|
+
* 2026-10-28:显式 lineId 需只读授权;未指定时按调用者可见产线过滤。
|
|
4
5
|
*/
|
|
5
6
|
import { getQuery } from 'h3'
|
|
6
7
|
import { resolveUser } from '@/server/api/workshop/caller'
|
|
7
8
|
import { defineApiHandler } from '@/server/utils/response'
|
|
8
9
|
import { getRecipeRollBackManager } from '@/server/services/workshop/dcw/recipe-rollback-manager'
|
|
10
|
+
import { filterRowsByLine, requireLineForResource } from '@/server/services/workshop/line-scope'
|
|
9
11
|
|
|
10
12
|
export default defineApiHandler(async (event) => {
|
|
11
|
-
resolveUser(event)
|
|
13
|
+
const user = resolveUser(event)
|
|
12
14
|
const q = getQuery(event)
|
|
15
|
+
const lineId = q.lineId ? String(q.lineId) : undefined
|
|
16
|
+
if (lineId) requireLineForResource(user, lineId, '产线', 'readonly')
|
|
13
17
|
const limit = Number(q.limit ?? 100)
|
|
14
18
|
const anchors = getRecipeRollBackManager().journal({
|
|
15
19
|
nodeId: q.nodeId ? String(q.nodeId) : undefined,
|
|
16
|
-
lineId
|
|
20
|
+
lineId,
|
|
17
21
|
source: q.source ? String(q.source) : undefined,
|
|
18
22
|
limit: Number.isFinite(limit) ? Math.min(limit, 500) : 100,
|
|
19
23
|
})
|
|
20
|
-
return { anchors }
|
|
24
|
+
return { anchors: filterRowsByLine(user, anchors, a => a.lineId) }
|
|
21
25
|
})
|
|
@@ -10,6 +10,9 @@ import { readFileSync, statSync } from 'node:fs'
|
|
|
10
10
|
import { basename, join, resolve, sep } from 'node:path'
|
|
11
11
|
import { resolveUser } from '@/server/api/workshop/caller'
|
|
12
12
|
import { mesArtifactsRoot } from '@/server/services/workshop/mes/mes-hook'
|
|
13
|
+
import { getDcwController } from '@/server/services/workshop/dcw/dcw-controller'
|
|
14
|
+
import { AppError, ErrorCodes } from '@/server/utils/errors'
|
|
15
|
+
import { requireLineForResource } from '@/server/services/workshop/line-scope'
|
|
13
16
|
|
|
14
17
|
const MIME_OF = (name: string): string => {
|
|
15
18
|
const n = name.toLowerCase()
|
|
@@ -22,13 +25,16 @@ const MIME_OF = (name: string): string => {
|
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export default defineEventHandler(async (event) => {
|
|
25
|
-
resolveUser(event)
|
|
28
|
+
const user = resolveUser(event)
|
|
26
29
|
const q = getQuery(event)
|
|
27
30
|
const nodeId = String(q.node_id ?? '').trim().slice(0, 64)
|
|
28
31
|
const name = basename(String(q.name ?? '').trim())
|
|
29
32
|
if (!nodeId || !name) {
|
|
30
33
|
throw createError({ statusCode: 400, statusMessage: 'node_id 与 name 必填' })
|
|
31
34
|
}
|
|
35
|
+
const node = getDcwController().byId(nodeId)
|
|
36
|
+
if (!node) throw new AppError(404, ErrorCodes.NOT_FOUND, `控制节点不存在: ${nodeId}`)
|
|
37
|
+
requireLineForResource(user, node.lineId, 'MES 点位', 'readonly')
|
|
32
38
|
const root = resolve(join(mesArtifactsRoot(), nodeId))
|
|
33
39
|
const file = resolve(join(root, name))
|
|
34
40
|
if (file !== root && !file.startsWith(root + sep)) {
|