agentworkshop 0.7.52 → 0.7.53
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 +70 -1
- package/.output/.AgentWorkShop/prompts/host-tools.json +70 -1
- package/.output/.AgentWorkShop/schema.json +2 -0
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{D0ldVwl-.js → 77DRSnQv.js} +1 -1
- package/.output/public/_nuxt/8oRmGaAV.js +1 -0
- package/.output/public/_nuxt/{BPou3U7b.js → BLnLjzka.js} +1 -1
- package/.output/public/_nuxt/{CFQyYMEn2.js → BSdJTnk12.js} +1 -1
- package/.output/public/_nuxt/{C4lagVdN2.js → BUD1CNvB2.js} +1 -1
- package/.output/public/_nuxt/{CTvh8v4T.js → BXR3tgEk.js} +1 -1
- package/.output/public/_nuxt/{Cbeifv55.js → BhlRInVa.js} +1 -1
- package/.output/public/_nuxt/{mNnuUoSy.js → BkfA3BbD.js} +1 -1
- package/.output/public/_nuxt/{ChGuLxwW2.js → BpcQDwKM2.js} +1 -1
- package/.output/public/_nuxt/{DQMbdg4t.js → BwlGxRxA.js} +1 -1
- package/.output/public/_nuxt/{Bt2ZL5332.js → C4sQKuhk2.js} +1 -1
- package/.output/public/_nuxt/{CkPxL4UX2.js → CBUIODly2.js} +1 -1
- package/.output/public/_nuxt/CR-ILcRu2.js +1 -0
- package/.output/public/_nuxt/CZLnEnbE.js +14 -0
- package/.output/public/_nuxt/{qPsch_ny2.js → Chxx-DIE2.js} +1 -1
- package/.output/public/_nuxt/{BiUhxH3t2.js → CjwxhpwR2.js} +1 -1
- package/.output/public/_nuxt/{B9cGf8mg.js → CvGYm7w8.js} +1 -1
- package/.output/public/_nuxt/{B9BmiUil.js → DAGuPgXa.js} +1 -1
- package/.output/public/_nuxt/{DefDtlKK.js → DE6H1HdJ.js} +1 -1
- package/.output/public/_nuxt/{D_Ce9Fon2.js → DFhyhDGa2.js} +1 -1
- package/.output/public/_nuxt/{BXx9AxdF2.js → DSqMyLCV2.js} +1 -1
- package/.output/public/_nuxt/{B8zP7kaj2.js → DV5joWTn2.js} +1 -1
- package/.output/public/_nuxt/{Da90gveK.js → DVOR2pWg.js} +1 -1
- package/.output/public/_nuxt/DWsgRq2c.js +1 -0
- package/.output/public/_nuxt/{mgWHSRc8.js → DZDi8inD.js} +1 -1
- package/.output/public/_nuxt/{xQX5r6tW.js → Dep0Hc9w.js} +1 -1
- package/.output/public/_nuxt/{evt5VGDh.js → DffgG8vT.js} +1 -1
- package/.output/public/_nuxt/{B_opxrHJ.js → DlCktWDT.js} +1 -1
- package/.output/public/_nuxt/{BVzw5a_0.js → DmzmgYHb.js} +1 -1
- package/.output/public/_nuxt/PluginSlot.CGH0vb95.css +1 -0
- package/.output/public/_nuxt/{ty5W5QwO.js → WmMwIS4y.js} +1 -1
- package/.output/public/_nuxt/{_id_.DamiFT0Q.css → _id_.rdGx-UDx.css} +1 -1
- package/.output/public/_nuxt/{BgEDwFMf.js → _kUzq9VF.js} +1 -1
- package/.output/public/_nuxt/_wsId_.XjmieZaJ.css +1 -0
- package/.output/public/_nuxt/{C00Rd6Cg.js → aV6YPjg0.js} +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/5ccc21c2-aba0-4b63-9a13-410e0569e345.json +1 -0
- package/.output/public/_nuxt/{default.CXb_zoN6.css → default.CyLpQD1C.css} +1 -1
- package/.output/public/_nuxt/o9rJf00k.js +1 -0
- package/.output/public/_nuxt/sl1EEoCr2.js +21 -0
- package/.output/public/robots.txt +3 -0
- package/.output/server/chunks/_/binding-authz.mjs +1 -1
- package/.output/server/chunks/_/character-asset.repo.mjs +1 -1
- package/.output/server/chunks/_/entity.mjs +1 -1
- package/.output/server/chunks/_/host-bindings.mjs +1 -1
- package/.output/server/chunks/_/nitro.mjs +1945 -1420
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/_/scene-layout.repo.mjs +1 -1
- package/.output/server/chunks/build/{PluginSlot-D2WUZgbp.mjs → PluginSlot-CT4YaNrB.mjs} +5 -5
- package/.output/server/chunks/build/{PluginSlot-D2WUZgbp.mjs.map → PluginSlot-CT4YaNrB.mjs.map} +1 -1
- package/.output/server/chunks/build/TownView-fP9BiALv.mjs +1 -1
- package/.output/server/chunks/build/{_id_-BUpQSlzo.mjs → _id_-CK-QFrDV.mjs} +55 -51
- package/.output/server/chunks/build/_id_-CK-QFrDV.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-C4L9fVHC.mjs → _wsId_-DdqXzycE.mjs} +320 -202
- package/.output/server/chunks/build/{_wsId_-C4L9fVHC.mjs.map → _wsId_-DdqXzycE.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-ydYu-Ol9.mjs → agents-DqICy4AL.mjs} +6 -6
- package/.output/server/chunks/build/{agents-ydYu-Ol9.mjs.map → agents-DqICy4AL.mjs.map} +1 -1
- package/.output/server/chunks/build/{channel-templates-DP1ndA5K.mjs → channel-templates-gdCLajKy.mjs} +2 -2
- package/.output/server/chunks/build/{channel-templates-DP1ndA5K.mjs.map → channel-templates-gdCLajKy.mjs.map} +1 -1
- package/.output/server/chunks/build/daq-N69AOtgw.mjs +1 -1
- package/.output/server/chunks/build/dcw-CN9d3Rs7.mjs +1 -1
- package/.output/server/chunks/build/{default-DCf9tn1i.mjs → default-CXUe4HL7.mjs} +65 -62
- package/.output/server/chunks/build/{default-DCf9tn1i.mjs.map → default-CXUe4HL7.mjs.map} +1 -1
- package/.output/server/chunks/build/{pages-BzmrmLL_.mjs → pages-CRMeXrxG.mjs} +3 -3
- package/.output/server/chunks/build/{pages-BzmrmLL_.mjs.map → pages-CRMeXrxG.mjs.map} +1 -1
- package/.output/server/chunks/build/{plugins-DSka7-vS.mjs → plugins-NAS9MQ5r.mjs} +2 -2
- package/.output/server/chunks/build/{plugins-DSka7-vS.mjs.map → plugins-NAS9MQ5r.mjs.map} +1 -1
- package/.output/server/chunks/build/{schedules-BYR-lA1k.mjs → schedules-D8vYHgQ5.mjs} +2 -2
- package/.output/server/chunks/build/{schedules-BYR-lA1k.mjs.map → schedules-D8vYHgQ5.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-UyZ78w2e.mjs → settings-B7HA-sdb.mjs} +3 -3
- package/.output/server/chunks/build/{settings-UyZ78w2e.mjs.map → settings-B7HA-sdb.mjs.map} +1 -1
- package/.output/server/chunks/build/{teams-D-Y5mzU7.mjs → teams-wM-MdLv9.mjs} +8 -8
- package/.output/server/chunks/build/{teams-D-Y5mzU7.mjs.map → teams-wM-MdLv9.mjs.map} +1 -1
- package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
- package/.output/server/chunks/build/{useAgentHarnessOptions-CRyQYYY0.mjs → useAgentHarnessOptions-BnFCh23W.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentHarnessOptions-CRyQYYY0.mjs.map → useAgentHarnessOptions-BnFCh23W.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAgentTemplateActions-CaRsOBmB.mjs → useAgentTemplateActions-DsBRUQni.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentTemplateActions-CaRsOBmB.mjs.map → useAgentTemplateActions-DsBRUQni.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAgentTemplateForm-ChiIuPYr.mjs → useAgentTemplateForm-DzQ_t2Ue.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentTemplateForm-ChiIuPYr.mjs.map → useAgentTemplateForm-DzQ_t2Ue.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAgentTemplatesCatalog-Bf5hk6Jf.mjs → useAgentTemplatesCatalog-Q7LgG1Af.mjs} +2 -2
- package/.output/server/chunks/build/{useAgentTemplatesCatalog-Bf5hk6Jf.mjs.map → useAgentTemplatesCatalog-Q7LgG1Af.mjs.map} +1 -1
- package/.output/server/chunks/build/useDaqAddNode-kW7uTSMQ.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailNode-D_vhG0jF.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailParams-JfqHvTxr.mjs +1 -1
- package/.output/server/chunks/build/useDaqFilters-BUQtZ82a.mjs +1 -1
- package/.output/server/chunks/build/useDaqStream-wOxZcMDA.mjs +1 -1
- package/.output/server/chunks/build/useDaqTemplates-BqQRdrIm.mjs +1 -1
- package/.output/server/chunks/build/{useDashboardData-CNOY4Bi3.mjs → useDashboardData-DWJ6vHjm.mjs} +2 -2
- package/.output/server/chunks/build/{useDashboardData-CNOY4Bi3.mjs.map → useDashboardData-DWJ6vHjm.mjs.map} +1 -1
- package/.output/server/chunks/build/useDcwAddNode-C1HDP3yG.mjs +1 -1
- package/.output/server/chunks/build/useDcwStream-D04kCV3J.mjs +1 -1
- package/.output/server/chunks/build/{useTeamActions-C51ciLb0.mjs → useTeamActions-BOLQkYxg.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamActions-C51ciLb0.mjs.map → useTeamActions-BOLQkYxg.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamAddMemberDialog-DthpCMP8.mjs → useTeamAddMemberDialog-BCgrLPxI.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamAddMemberDialog-DthpCMP8.mjs.map → useTeamAddMemberDialog-BCgrLPxI.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamCreateDialog-Dc2KTwyg.mjs → useTeamCreateDialog-CJk40LqS.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamCreateDialog-Dc2KTwyg.mjs.map → useTeamCreateDialog-CJk40LqS.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamDeployDialog-CXpvPnJD.mjs → useTeamDeployDialog-49hdUXQP.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamDeployDialog-CXpvPnJD.mjs.map → useTeamDeployDialog-49hdUXQP.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamPluginDialog-DTs-7SSm.mjs → useTeamPluginDialog-Dh-QOoiU.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamPluginDialog-DTs-7SSm.mjs.map → useTeamPluginDialog-Dh-QOoiU.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamsCatalog-CcpetYyW.mjs → useTeamsCatalog-HBSyKt5P.mjs} +2 -2
- package/.output/server/chunks/build/{useTeamsCatalog-CcpetYyW.mjs.map → useTeamsCatalog-HBSyKt5P.mjs.map} +1 -1
- package/.output/server/chunks/build/{useWorkshopApi-CO3ophqt.mjs → useWorkshopApi-C9sTYu1F.mjs} +9 -1
- package/.output/server/chunks/build/useWorkshopApi-C9sTYu1F.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkshopWs-Bm80bLn7.mjs.map +1 -1
- package/.output/server/chunks/routes/api/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/index.post.mjs +14 -3
- package/.output/server/chunks/routes/api/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/mcp/workshop.mjs +43 -14
- package/.output/server/chunks/routes/api/mcp/workshop.mjs.map +1 -1
- package/.output/server/chunks/routes/api/metrics.mjs +2 -0
- package/.output/server/chunks/routes/api/metrics.mjs.map +1 -1
- package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs +1 -1
- package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/backup/list.get.mjs +47 -0
- package/.output/server/chunks/routes/api/system/backup/list.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/system/backup/run.post.mjs +35 -0
- package/.output/server/chunks/routes/api/system/backup/run.post.mjs.map +1 -0
- package/.output/server/chunks/routes/api/system/config-groups/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/system/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/system/index.patch.mjs +15 -4
- package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/system/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/system/monitor/terminal/ws.mjs +1 -1
- package/.output/server/chunks/routes/api/system/monitor/terminate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/system/settings/events.get.mjs +1 -1
- package/.output/server/chunks/routes/api/system/settings/reset.post.mjs +15 -3
- package/.output/server/chunks/routes/api/system/settings/reset.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/system/timezone.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/_id_.delete.mjs +14 -3
- package/.output/server/chunks/routes/api/users/_id_.delete.mjs.map +1 -1
- package/.output/server/chunks/routes/api/users/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/_id_.put.mjs +14 -3
- package/.output/server/chunks/routes/api/users/_id_.put.mjs.map +1 -1
- package/.output/server/chunks/routes/api/users/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/users/login.post.mjs +3 -2
- package/.output/server/chunks/routes/api/users/login.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/users/logout.post.mjs +1 -1
- package/.output/server/chunks/routes/api/users/me.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/register.post.mjs +4 -1
- package/.output/server/chunks/routes/api/users/register.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/users/setup-status.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/tokens/_id/token.get.mjs +1 -1
- package/.output/server/chunks/routes/api/users/tokens/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/users/tokens/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.post.mjs +19 -15
- package/.output/server/chunks/routes/api/workshop/a2a/_agentId/index.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/a2a/send.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/approvals/_id/decide.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/grant.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/bindings/revoke.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agent-tools/list.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/agents/subscribe.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id/preview.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/entities/prune.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/env/recheck.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/env/uv.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/env/venv.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index3.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index4.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index6.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/index7.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/cancel.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/logs.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id/retry.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/jobs/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id/predict.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id/promote.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id/train.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/training-plans/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/aml/twin/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/approvals/_id/decide.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/assets/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/audit.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/caller.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +8 -3
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/from-channel.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/activate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId/memories/search.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/chat/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/chat/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/events.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index4.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/line.put.mjs +37 -0
- package/.output/server/chunks/routes/api/workshop/channels/_id/line.put.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/channels/_id/terminals.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/twin-profile.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/twin-profile.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/index.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +4 -4
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs +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/test.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +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 +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 +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +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 +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 +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 +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 +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 +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 +6 -1
- package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs.map +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/lines/_id_.patch.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 +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +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/mark-good.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/revert.post.mjs +3 -3
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/revert.post.mjs.map +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 +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/index.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +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 +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id/control.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id/telemetry.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs +14 -2
- package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs.map +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.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index10.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs +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/index6.post.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/index8.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index8.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -3
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index9.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/notifications/read.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/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/schedules/_id/runs.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/schedules/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/complete.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/dispatch.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id/report.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tasks/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/deploy.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/users/register.post.mjs +4 -1
- package/.output/server/chunks/routes/api/workshop/users/register.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +2 -2
- package/.output/server/chunks/routes/renderer.mjs +2 -2
- package/.output/server/chunks/virtual/entry.mjs +340 -52
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +7 -3
- package/README.md +11 -5
- package/app/components/app-header/HitlBell.vue +9 -9
- package/app/components/dcw/DcwAddNodeModal.vue +3 -3
- package/app/components/dcw/DcwNodesTable.vue +4 -4
- package/app/components/dcw/DcwRecipeEditModal.vue +2 -2
- package/app/components/workshop/ChatMemberPanel.vue +39 -38
- package/app/components/workshop/ChatTimeline.vue +33 -24
- package/app/components/workshop/Composer.vue +7 -7
- package/app/components/workshop/NotificationCenter.vue +22 -13
- package/app/components/workshop/PluginSlot.vue +1 -1
- package/app/components/workshop/TaskInspectorDrawer.vue +5 -5
- package/app/components/workshop/blocks/ClusterTool.vue +1 -1
- package/app/components/workshop/composer/ComposerToolbar.vue +2 -2
- package/app/components/workshop/composer/MentionMenu.vue +1 -1
- package/app/components/workshop/composer/ReplyChip.vue +2 -2
- package/app/components/workshop/composer/StatusChip.vue +7 -7
- package/app/components/workshop/console/LineOperationsPanel.vue +15 -14
- package/app/components/workshop/lanes/LaneHeader.vue +5 -3
- package/app/components/workshop/session-list/ChannelSettingsModal.vue +43 -1
- package/app/composables/useCodeCopy.ts +16 -2
- package/app/composables/workshop/useWorkshopApi.ts +7 -0
- package/app/stores/workshop/notifications.ts +43 -17
- package/docs/cli.en.md +1 -1
- package/i18n/dicts/_en.json +1551 -1557
- package/i18n/dicts/_en2.json +300 -300
- package/i18n/dicts/_en4.json +114 -115
- package/i18n/dicts/agentLanesView.json +61 -57
- package/i18n/dicts/appHeader.json +17 -7
- package/i18n/dicts/channelSessionList.json +51 -48
- package/i18n/dicts/chat.json +79 -0
- package/i18n/dicts/clusterTool.json +4 -3
- package/i18n/dicts/composer.json +38 -38
- package/i18n/dicts/dcwDetail.json +219 -209
- package/i18n/dicts/lineOps.json +14 -0
- package/i18n/dicts/multiChannelView.json +13 -13
- package/i18n/dicts/notifications.json +13 -0
- package/i18n/dicts/taskInspectorDrawer.json +25 -19
- package/i18n/locales/en.ts +152 -8
- package/i18n/locales/zh-CN.ts +152 -8
- package/package.json +1 -1
- package/public/robots.txt +3 -0
- package/server/api/mcp/workshop.ts +8 -1
- package/server/api/metrics.ts +4 -1
- package/server/api/plugins/[name]/[...path].ts +4 -3
- package/server/api/system/backup/list.get.ts +32 -0
- package/server/api/system/backup/run.post.ts +21 -0
- package/server/api/system/settings/index.patch.ts +12 -2
- package/server/api/system/settings/reset.post.ts +11 -2
- package/server/api/users/[id].delete.ts +9 -2
- package/server/api/users/[id].put.ts +9 -2
- package/server/api/users/index.post.ts +9 -2
- package/server/api/users/login.post.ts +3 -3
- package/server/api/users/register.post.ts +11 -1
- package/server/api/workshop/a2a/[agentId]/rpc/index.post.ts +23 -13
- package/server/api/workshop/channel-templates/[id]/instantiate.post.ts +6 -0
- package/server/api/workshop/channels/[id]/line.put.ts +30 -0
- package/server/api/workshop/dcw/line/query.get.ts +6 -0
- package/server/api/workshop/dcw/recipes/[id]/revert.post.ts +3 -2
- package/server/api/workshop/fs/dirs.get.ts +19 -1
- package/server/api/workshop/ops-logs/index.get.ts +3 -2
- package/server/api/workshop/users/register.post.ts +7 -0
- package/server/mcp/workshop-server.ts +36 -13
- package/server/middleware/00-security-headers.ts +38 -0
- package/server/middleware/01-rate-guard.ts +101 -0
- package/server/plugins/backup.ts +56 -1
- package/server/plugins-builtin/diag-bridge/host/plugin.mjs +18 -11
- package/server/repositories/user.repository.ts +10 -5
- package/server/services/user.service.ts +24 -1
- package/server/services/workshop/agents/host-tool-bridge/dispatch.ts +1 -1
- package/server/services/workshop/agents/host-tool-bridge/tools/industrial.ts +4 -2
- package/server/services/workshop/agents/industrial/index.ts +1 -1
- package/server/services/workshop/agents/industrial/ops-tools.ts +168 -26
- package/server/services/workshop/agents/tool-classes.ts +2 -0
- package/server/services/workshop/aml/dataset-builder.ts +4 -2
- package/server/services/workshop/aml/job-orchestrator/process.ts +1 -0
- package/server/services/workshop/aml/job-orchestrator/shared.ts +2 -0
- package/server/services/workshop/aml/job-orchestrator/start-job.ts +5 -2
- package/server/services/workshop/db/channel.repo.ts +11 -1
- package/server/services/workshop/db/database/migrations.ts +2 -0
- package/server/services/workshop/db/database/rows.ts +2 -0
- package/server/services/workshop/db/database/seed.ts +19 -9
- package/server/services/workshop/dcw/dcw-controller/recipes.ts +60 -4
- package/server/services/workshop/dcw/dcw-controller/write.ts +35 -6
- package/server/services/workshop/dcw/dcw-recipe.repo.ts +4 -3
- package/server/services/workshop/dcw/param-map.repo.ts +3 -0
- package/server/services/workshop/plugins/host/init.mjs +9 -1
- package/server/services/workshop/plugins/host/load.mjs +2 -2
- package/server/services/workshop/runtime/manager/admin-channel.ts +27 -1
- package/server/services/workshop/runtime/manager/channel-templates.ts +5 -4
- package/server/services/workshop/runtime/manager/runtime-wiring.ts +17 -1
- package/shared/config/schema.json +2 -0
- package/.output/public/_nuxt/4wrEQ3np2.js +0 -21
- package/.output/public/_nuxt/CGQvF-Gg.js +0 -14
- package/.output/public/_nuxt/CcGuXiOa.js +0 -1
- package/.output/public/_nuxt/D0SNhlnt.js +0 -1
- package/.output/public/_nuxt/DnH9SRMW2.js +0 -1
- package/.output/public/_nuxt/ON2x-t6n.js +0 -1
- package/.output/public/_nuxt/PluginSlot.DmjNbrcX.css +0 -1
- package/.output/public/_nuxt/_wsId_.DkrQkT6r.css +0 -1
- package/.output/public/_nuxt/builds/meta/c64014ca-3595-4b7a-a5ca-055687c83ba2.json +0 -1
- package/.output/server/chunks/build/_id_-BUpQSlzo.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkshopApi-CO3ophqt.mjs.map +0 -1
- package/scripts/.chk-tok +0 -1
- package/scripts/.retrain-body.json +0 -1
- package/scripts/.retrain3-body.json +0 -1
- package/scripts/.rt-tok +0 -1
- package/scripts/.rt3-tok +0 -1
package/server/plugins/backup.ts
CHANGED
|
@@ -53,7 +53,41 @@ async function backupOne(src: string, target: string): Promise<void> {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
/** 数据目录内的 JSON 仓储/状态文件(kv、参数面、节点绑定、插件 kv 等)+ 上级配置根的 config.yml/runtime-settings.json */
|
|
57
|
+
async function backupFlatFiles(dataDir: string, backupDir: string, stamp: string): Promise<void> {
|
|
58
|
+
const bundleDir = resolve(backupDir, `files-${stamp}`)
|
|
59
|
+
mkdirSync(bundleDir, { recursive: true })
|
|
60
|
+
const targets: Array<{ src: string, name: string }> = []
|
|
61
|
+
// dataDir 下所有 .json(逐文件拷贝,目录型子树由各自仓储自轮转,不整树递归)
|
|
62
|
+
try {
|
|
63
|
+
for (const f of readdirSync(dataDir, { withFileTypes: true })) {
|
|
64
|
+
if (f.isFile() && f.name.endsWith('.json')) targets.push({ src: resolve(dataDir, f.name), name: f.name })
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch { /* 数据目录不可读时跳过 JSON 面 */ }
|
|
68
|
+
// 配置根(config.yml + runtime-settings.json)——重建实例的"怎么做"半边
|
|
69
|
+
try {
|
|
70
|
+
const homeMod = await import('@/shared/config/home.mjs')
|
|
71
|
+
const rm = homeMod.resolveRunMode({ cwd: process.cwd(), env: process.env })
|
|
72
|
+
for (const cfg of ['config.yml', 'runtime-settings.json']) {
|
|
73
|
+
const p = resolve(rm.configRoot, cfg)
|
|
74
|
+
if (existsSync(p)) targets.push({ src: p, name: cfg })
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch { /* 模式解析失败只降级 JSON 面 */ }
|
|
78
|
+
for (const t of targets) {
|
|
79
|
+
try {
|
|
80
|
+
const tmp = resolve(bundleDir, `${t.name}.tmp`)
|
|
81
|
+
await copyFileAsync(t.src, tmp)
|
|
82
|
+
renameSync(tmp, resolve(bundleDir, t.name))
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
console.error(`[backup] ${t.name} 快照失败:`, err instanceof Error ? err.message : err)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function backupOnce(dataDir: string): Promise<void> {
|
|
57
91
|
const backupDir = resolve(dataDir, 'backups')
|
|
58
92
|
mkdirSync(backupDir, { recursive: true })
|
|
59
93
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
|
@@ -69,6 +103,8 @@ async function backupOnce(dataDir: string): Promise<void> {
|
|
|
69
103
|
console.error(`[backup] ${file} 快照失败:`, err instanceof Error ? err.message : err)
|
|
70
104
|
}
|
|
71
105
|
}
|
|
106
|
+
// JSON 仓储 + 配置面快照(三库之外的可重建性半边)
|
|
107
|
+
await backupFlatFiles(dataDir, backupDir, stamp)
|
|
72
108
|
// 轮转:每库仅保留最近 backup.keep 份(按文件名内时间戳倒序;env BACKUP_KEEP 兼容)
|
|
73
109
|
const keep = Math.max(1, backupSettings().keep)
|
|
74
110
|
for (const file of DB_FILES) {
|
|
@@ -83,9 +119,28 @@ async function backupOnce(dataDir: string): Promise<void> {
|
|
|
83
119
|
catch { /* 轮转失败不致命 */ }
|
|
84
120
|
}
|
|
85
121
|
}
|
|
122
|
+
// files-<stamp>/ 目录同 keep 轮转
|
|
123
|
+
const fileBundles = readdirSync(backupDir)
|
|
124
|
+
.filter(f => f.startsWith('files-') && !f.includes('.'))
|
|
125
|
+
.sort()
|
|
126
|
+
.reverse()
|
|
127
|
+
for (const stale of fileBundles.slice(keep)) {
|
|
128
|
+
try {
|
|
129
|
+
rmSync(resolve(backupDir, stale), { recursive: true })
|
|
130
|
+
}
|
|
131
|
+
catch { /* 轮转失败不致命 */ }
|
|
132
|
+
}
|
|
86
133
|
console.log('[backup] 快照完成 →', backupDir)
|
|
87
134
|
// R4:记录最近一次成功备份时间,供 /api/metrics 观测
|
|
88
135
|
g.__awBackupLastAt = new Date().toISOString()
|
|
136
|
+
// 备份成败留痕(audit_log;动态导入避免插件装载期循环依赖)
|
|
137
|
+
void import('../services/workshop/ops/ops').then(({ recordOps }) => {
|
|
138
|
+
recordOps({
|
|
139
|
+
actor: 'system', actorName: 'backup', actorKind: 'system',
|
|
140
|
+
action: 'system.backup.run', kind: 'system',
|
|
141
|
+
summary: `数据库快照完成(${DB_FILES.length} 库 + JSON/配置)→ ${backupDir}`,
|
|
142
|
+
})
|
|
143
|
+
}).catch(() => {})
|
|
89
144
|
}
|
|
90
145
|
|
|
91
146
|
export default function backupPlugin() {
|
|
@@ -68,17 +68,24 @@ export default {
|
|
|
68
68
|
if (Date.now() - last < AUTO_COOLDOWN_MS) return
|
|
69
69
|
const now = Date.now()
|
|
70
70
|
ctx.kv.set(`cooldown:${line}`, now) // 先占冷却,防同窗连发
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
// 首拍竞态:开线后首个越限样本即刻触发,快照查询早于 tsdb 首次刷盘(TSDB_FLUSH_MS=500ms)
|
|
72
|
+
// → 时窗查空失败。空窗失败定向重试(≤3 次、间隔 5s,总窗 ≤20s);冷却不回滚,仍只此一次派发。
|
|
73
|
+
const question = `${line} 产线该时窗数据深度根因诊断(自动触发:节点 ${s.nodeId} 采样值 ${s.value} ${rule.op === 'lt' ? '<' : '>'} ${rule.value})`
|
|
74
|
+
const attempt = (n) => {
|
|
75
|
+
startDiagnosis(ctx, {
|
|
76
|
+
line,
|
|
77
|
+
fromMs: now - 60 * MIN,
|
|
78
|
+
toMs: now,
|
|
79
|
+
question,
|
|
80
|
+
scene: `${line}_auto`,
|
|
81
|
+
source: 'auto',
|
|
82
|
+
}).then((r) => {
|
|
83
|
+
if (r.ok) ctx.logger.warn(`自动诊断已发起:产线 ${line} runId=${r.runId}(节点 ${s.nodeId}=${s.value} 越限)`)
|
|
84
|
+
else if (n < 3 && String(r.error ?? '').includes('无数采样本')) setTimeout(() => attempt(n + 1), 5000)
|
|
85
|
+
else ctx.logger.warn(`自动诊断发起失败(产线 ${line}): ${r.error}`)
|
|
86
|
+
}).catch(() => {})
|
|
87
|
+
}
|
|
88
|
+
attempt(0)
|
|
82
89
|
}
|
|
83
90
|
catch { /* 钩子内绝不抛 */ }
|
|
84
91
|
})
|
|
@@ -139,7 +139,8 @@ function importLegacyWorkshopUsers(d: DatabaseSync): void {
|
|
|
139
139
|
insertUser.run(row.id, row.name, email, hashPassword(randomPassword()), 'user', 'active', row.created_at ?? now())
|
|
140
140
|
const hash = hashToken(row.token)
|
|
141
141
|
if (!tokenExists.get(hash)) {
|
|
142
|
-
|
|
142
|
+
// 安全基线:token_plain 不落明文(哈希即可鉴权;明文列仅为旧库形状兼容,恒 NULL)
|
|
143
|
+
insertToken.run(randomUUID(), row.id, 'legacy', hash, null, row.created_at ?? now())
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
}
|
|
@@ -241,17 +242,21 @@ function toUser(row: Record<string, unknown>): User {
|
|
|
241
242
|
export const userRepository = {
|
|
242
243
|
list({ page, pageSize, keyword }: UserListQuery): Paginated<User> {
|
|
243
244
|
const d = getDb()
|
|
244
|
-
|
|
245
|
+
// 分页边界钳制:超大/非法分页参数在 SQL 外收口(OFFSET 1e20 会打穿 sqlite 抛 500)
|
|
246
|
+
const safePage = Number.isSafeInteger(page) && page >= 1 ? page : 1
|
|
247
|
+
const safeSize = Number.isSafeInteger(pageSize) && pageSize >= 1 ? Math.min(pageSize, 200) : 20
|
|
248
|
+
// LIKE 通配符转义:用户关键词里的 %/_ 按字面匹配,不放大匹配面(ESCAPE 子句配套)
|
|
249
|
+
const kw = keyword?.toLowerCase() ? keyword.toLowerCase().replace(/[\\%_]/g, ch => `\\${ch}`) : ''
|
|
245
250
|
const where = kw
|
|
246
|
-
? `WHERE LOWER(name) LIKE ? OR LOWER(email) LIKE
|
|
251
|
+
? `WHERE LOWER(name) LIKE ? ESCAPE '\\' OR LOWER(email) LIKE ? ESCAPE '\\'`
|
|
247
252
|
: ''
|
|
248
253
|
const params = kw ? [`%${kw}%`, `%${kw}%`] : []
|
|
249
254
|
const total = (d.prepare(`SELECT COUNT(*) AS n FROM users ${where}`).get(...params) as { n: number }).n
|
|
250
255
|
const rows = d.prepare(
|
|
251
256
|
`SELECT id, name, email, role, status, created_at AS createdAt FROM users ${where}
|
|
252
257
|
ORDER BY created_at DESC LIMIT ? OFFSET ?`,
|
|
253
|
-
).all(...params,
|
|
254
|
-
return { items: rows.map(toUser), total, page, pageSize }
|
|
258
|
+
).all(...params, safeSize, (safePage - 1) * safeSize) as Array<Record<string, unknown>>
|
|
259
|
+
return { items: rows.map(toUser), total, page: safePage, pageSize: safeSize }
|
|
255
260
|
},
|
|
256
261
|
|
|
257
262
|
/** 全量用户(不分页;管理面授权矩阵用,避免分页截断) */
|
|
@@ -23,6 +23,12 @@ export function resolveUserByToken(token: string): UserProfile | null {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export const userService = {
|
|
26
|
+
/**
|
|
27
|
+
* 登录防爆破:同 IP+邮箱在 15 分钟窗口内累计失败 ≥5 次 → 锁到窗口结束(429)。
|
|
28
|
+
* 内存态,重启即清;多实例部署由反代层限流补齐。计数表封顶 5000 条防内存滥用。
|
|
29
|
+
*/
|
|
30
|
+
_loginFails: new Map<string, { count: number, firstAt: number }>(),
|
|
31
|
+
|
|
26
32
|
// ===== 管理面(与既有契约一致)=====
|
|
27
33
|
|
|
28
34
|
list(input: UserListInput) {
|
|
@@ -104,9 +110,25 @@ export const userService = {
|
|
|
104
110
|
},
|
|
105
111
|
|
|
106
112
|
/** 登录:密码校验通过后签发新 token(每次登录生成一个可独立吊销的会话 token) */
|
|
107
|
-
login(input: UserLogin): AuthResult {
|
|
113
|
+
login(input: UserLogin, opts: { ip?: string } = {}): AuthResult {
|
|
114
|
+
const FAIL_WINDOW_MS = 15 * 60_000
|
|
115
|
+
const LOCK_THRESHOLD = 5
|
|
116
|
+
const lockKey = `${opts.ip ?? 'local'}|${input.email.toLowerCase()}`
|
|
117
|
+
const fails = this._loginFails
|
|
118
|
+
if (fails.size > 5000) {
|
|
119
|
+
const oldest = fails.keys().next().value
|
|
120
|
+
if (oldest) fails.delete(oldest)
|
|
121
|
+
}
|
|
122
|
+
const rec = fails.get(lockKey)
|
|
123
|
+
// 已达阈值且窗口未过 → 锁定;偶发失败(1-4 次)不锁,窗口滑过即重置
|
|
124
|
+
if (rec && rec.count >= LOCK_THRESHOLD && Date.now() - rec.firstAt < FAIL_WINDOW_MS) {
|
|
125
|
+
const waitMin = Math.ceil((FAIL_WINDOW_MS - (Date.now() - rec.firstAt)) / 60_000)
|
|
126
|
+
throw new AppError(429, 'LOGIN_LOCKED', `失败次数过多,账号已临时锁定,请约 ${waitMin} 分钟后再试`)
|
|
127
|
+
}
|
|
108
128
|
const { id, hash } = userRepository.getPasswordHash(input.email) ?? {}
|
|
109
129
|
if (!id || !hash || !verifyPassword(input.password, hash)) {
|
|
130
|
+
if (!rec || Date.now() - rec.firstAt >= FAIL_WINDOW_MS) fails.set(lockKey, { count: 1, firstAt: Date.now() })
|
|
131
|
+
else rec.count += 1
|
|
110
132
|
throw new AppError(401, 'UNAUTHORIZED', '邮箱或密码错误')
|
|
111
133
|
}
|
|
112
134
|
const user = userRepository.findById(id)
|
|
@@ -116,6 +138,7 @@ export const userService = {
|
|
|
116
138
|
if (user.status !== 'active') {
|
|
117
139
|
throw new AppError(403, 'FORBIDDEN', '账号已禁用')
|
|
118
140
|
}
|
|
141
|
+
fails.delete(lockKey)
|
|
119
142
|
const { raw } = userRepository.createToken(id, `session-${Date.now()}`)
|
|
120
143
|
return { user: this.publicProfile(user), token: raw }
|
|
121
144
|
},
|
|
@@ -96,7 +96,7 @@ export async function dispatchHostTool(ctx: HostToolBridgeContext, req: HostTool
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
// 所有真实写入口在统一分发层再做一次服务端 fail-closed 守卫;工具目录隐藏不是安全边界。
|
|
99
|
-
if (['dcw_control', 'param_control', 'dcw_rollback', 'recipe_update', 'recipe_rollback'].includes(req.toolName)) {
|
|
99
|
+
if (['dcw_control', 'param_control', 'dcw_rollback', 'recipe_update', 'recipe_rollback', 'recipe_trial', 'recipe_apply'].includes(req.toolName)) {
|
|
100
100
|
const profile = getChannelTwinProfile(identity.channelId)
|
|
101
101
|
const guard = twinWriteGuard(identity.channelId, profile)
|
|
102
102
|
if (!guard.allowed) return { text: `${guard.code}: ${guard.message}`, isError: true }
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 该族**不依赖 workspace**,由 dispatch 在 workspace 门控之前分流(保持原分发顺序)。
|
|
10
10
|
*/
|
|
11
11
|
import type { HostToolResult } from '../types'
|
|
12
|
-
import { toolAmlDatasetBuild, toolAmlDatasetStats, toolAmlJobCancel, toolAmlJobLogs, toolAmlJobStatus, toolAmlJobSubmit, toolAmlLeaderboard, toolAmlModelFind, toolAmlModelPromote, toolAmlModelReference, toolAmlNodeCatalog, toolAmlTrainingPlanCreate, toolAmlTrainingPlanList, toolAmlTrainingPlanTrain, toolDaqFrames, toolDaqQuery, toolDcwControl, toolDcwJudge, toolDcwJournal, toolDcwRead, toolDcwRollback, toolLineContext, toolMyIndustrialNodes, toolOpsLog, toolParamControl, toolParamRead, toolRecipeLog, toolRecipeRollback, toolRecipeUpdate, toolRecipeVersions, toolOptimizationExplore, toolTwinProviderCatalog, toolTwinSceneDiscover, toolTwinSceneCompile, toolTwinSceneFreeze, toolTwinSceneRead, toolTwinPhysicsSpecDraft, toolTwinPhysicsSpecValidate, toolTwinPhysicsSpecCompile, toolTwinSnapshotCreate, toolTwinTrialRun, toolMpcOptimize, toolTwinGateEvaluate, toolTwinBayesOptimize, toolTwinCalibrationRequest } from '../../industrial'
|
|
12
|
+
import { toolAmlDatasetBuild, toolAmlDatasetStats, toolAmlJobCancel, toolAmlJobLogs, toolAmlJobStatus, toolAmlJobSubmit, toolAmlLeaderboard, toolAmlModelFind, toolAmlModelPromote, toolAmlModelReference, toolAmlNodeCatalog, toolAmlTrainingPlanCreate, toolAmlTrainingPlanList, toolAmlTrainingPlanTrain, toolDaqFrames, toolDaqQuery, toolDcwControl, toolDcwJudge, toolDcwJournal, toolDcwRead, toolDcwRollback, toolLineContext, toolMyIndustrialNodes, toolOpsLog, toolParamControl, toolParamRead, toolRecipeApply, toolRecipeLog, toolRecipeRollback, toolRecipeTrial, toolRecipeUpdate, toolRecipeVersions, toolOptimizationExplore, toolTwinProviderCatalog, toolTwinSceneDiscover, toolTwinSceneCompile, toolTwinSceneFreeze, toolTwinSceneRead, toolTwinPhysicsSpecDraft, toolTwinPhysicsSpecValidate, toolTwinPhysicsSpecCompile, toolTwinSnapshotCreate, toolTwinTrialRun, toolMpcOptimize, toolTwinGateEvaluate, toolTwinBayesOptimize, toolTwinCalibrationRequest } from '../../industrial'
|
|
13
13
|
|
|
14
14
|
type IndustrialToolHandler = (agentId: string, args: Record<string, unknown>, channelId?: string) => Promise<HostToolResult>
|
|
15
15
|
|
|
@@ -59,7 +59,9 @@ const INDUSTRIAL_HANDLERS = new Map<string, IndustrialToolHandler>([
|
|
|
59
59
|
['line_context', (agentId, args) => toolLineContext(agentId, args as { line_id?: string })],
|
|
60
60
|
['recipe_versions', (agentId, args) => toolRecipeVersions(agentId, args as { recipe_id?: string, limit?: number | string })],
|
|
61
61
|
['recipe_update', (agentId, args) => toolRecipeUpdate(agentId, args as Parameters<typeof toolRecipeUpdate>[1])],
|
|
62
|
-
['recipe_rollback', (agentId, args) => toolRecipeRollback(agentId, args as Parameters<typeof toolRecipeRollback>[1])],
|
|
62
|
+
['recipe_rollback', (agentId, args) => toolRecipeRollback(agentId, args as Parameters<typeof toolRecipeRollback>[1])],
|
|
63
|
+
['recipe_trial', (agentId, args) => toolRecipeTrial(agentId, args as Parameters<typeof toolRecipeTrial>[1])],
|
|
64
|
+
['recipe_apply', (agentId, args) => toolRecipeApply(agentId, args as Parameters<typeof toolRecipeApply>[1])],
|
|
63
65
|
])
|
|
64
66
|
|
|
65
67
|
/** 工业工具名集合(与 INDUSTRIAL_HANDLERS 同源;dispatch 据此在 workspace 门控前分流) */
|
|
@@ -17,7 +17,7 @@ export { toolMyIndustrialNodes, toolDcwControl, toolDcwRead } from './dcw-tools'
|
|
|
17
17
|
export { toolParamControl, toolParamRead } from './param-tools'
|
|
18
18
|
export { toolDaqQuery, toolDaqFrames } from './daq-tools'
|
|
19
19
|
export { toolDcwJudge, toolDcwRollback, toolDcwJournal } from './dcw-judge-tools'
|
|
20
|
-
export { toolOpsLog, toolRecipeLog, toolLineContext, toolRecipeVersions, toolRecipeUpdate, toolRecipeRollback } from './ops-tools'
|
|
20
|
+
export { toolOpsLog, toolRecipeLog, toolLineContext, toolRecipeVersions, toolRecipeUpdate, toolRecipeRollback, toolRecipeTrial, toolRecipeApply } from './ops-tools'
|
|
21
21
|
export { toolAmlNodeCatalog, toolAmlDatasetBuild, toolAmlDatasetStats } from './aml-dataset-tools'
|
|
22
22
|
export { toolAmlJobSubmit, toolAmlJobStatus, toolAmlJobLogs, toolAmlJobCancel, toolAmlLeaderboard, toolAmlModelFind, toolAmlModelPromote, toolAmlModelReference, toolAmlTrainingPlanList, toolAmlTrainingPlanTrain, toolAmlTrainingPlanCreate } from './aml-job-tools'
|
|
23
23
|
|
|
@@ -17,10 +17,26 @@ import { fanoutQuery, windowOf } from './audit-query'
|
|
|
17
17
|
// 运维日志 / Recipe 变更史查询(Agent 自查面:负责产线 scoped)
|
|
18
18
|
// ================================================================
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/** v18:频道绑定产线(只读扩权)——经全局 manager 读;manager 未装配/未绑定 = null */
|
|
21
|
+
function channelBoundLineId(agentId: string): string | null {
|
|
22
|
+
try {
|
|
23
|
+
const g = globalThis as typeof globalThis & {
|
|
24
|
+
__workshopManager?: { channelLineBindingOf(id: string): string | null }
|
|
25
|
+
}
|
|
26
|
+
return g.__workshopManager?.channelLineBindingOf(agentId) ?? null
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Agent 负责范围 = 绑定节点的全集(节点 id 集 + 这些节点所属产线集;写权限依据)
|
|
34
|
+
* ∪ 频道绑定产线(boundLineIds;v18 只读扩权:日志/配方史/上下文可读,写仍走节点授权)。 */
|
|
35
|
+
export function agentOpsScope(agentId: string): { lineIds: string[], nodeIds: Set<string>, boundLineIds: string[] } | null {
|
|
22
36
|
const bindings = getAgentNodeBindingRepo().byAgent(agentId)
|
|
23
|
-
|
|
37
|
+
const boundLine = channelBoundLineId(agentId)
|
|
38
|
+
const boundLineIds = boundLine ? [boundLine] : []
|
|
39
|
+
if (bindings.length === 0 && boundLineIds.length === 0) return null
|
|
24
40
|
const lineIds = new Set<string>()
|
|
25
41
|
const nodeIds = new Set<string>()
|
|
26
42
|
for (const b of bindings) {
|
|
@@ -31,7 +47,17 @@ export function agentOpsScope(agentId: string): { lineIds: string[], nodeIds: Se
|
|
|
31
47
|
}
|
|
32
48
|
catch { /* 节点刚被删等情况忽略 */ }
|
|
33
49
|
}
|
|
34
|
-
return { lineIds: [...lineIds], nodeIds }
|
|
50
|
+
return { lineIds: [...lineIds], nodeIds, boundLineIds }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 读工具的可见产线全集 = 节点授权线 ∪ 频道绑定线(去重) */
|
|
54
|
+
function readableLineIds(scope: { lineIds: string[], boundLineIds: string[] }): string[] {
|
|
55
|
+
return [...new Set([...scope.lineIds, ...scope.boundLineIds])]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 产线展示标注:仅频道绑定(非节点授权)的线标注只读来源 */
|
|
59
|
+
function lineTag(lid: string, scope: { lineIds: string[], boundLineIds: string[] }): string {
|
|
60
|
+
return scope.lineIds.includes(lid) ? '' : '(频道绑定,只读)'
|
|
35
61
|
}
|
|
36
62
|
|
|
37
63
|
/** 配方参数的节点失效态(数据一致性展示/Agent 提示共用):null=正常 */
|
|
@@ -63,19 +89,20 @@ export async function toolOpsLog(agentId: string, args: {
|
|
|
63
89
|
const audit = getOps()?.audit
|
|
64
90
|
if (!audit) return { text: '审计仓储未装配(服务未就绪),请稍后重试。', isError: true }
|
|
65
91
|
const scope = agentOpsScope(agentId)
|
|
66
|
-
if (!scope) return { text: '
|
|
92
|
+
if (!scope) return { text: '你尚未绑定任何工业节点,所在频道也未绑定产线,无日志可查(日志权限跟随节点绑定或频道绑线)。', isError: true }
|
|
67
93
|
const nodeId = String(args.node_id ?? '').trim()
|
|
68
94
|
const lineId = String(args.line_id ?? '').trim()
|
|
95
|
+
const readable = readableLineIds(scope)
|
|
69
96
|
if (nodeId && !scope.nodeIds.has(nodeId))
|
|
70
97
|
return { text: `无权查询节点 ${nodeId} 的日志(仅可查自己绑定的节点;用 my_industrial_nodes 查看绑定)。`, isError: true }
|
|
71
|
-
if (lineId && !
|
|
72
|
-
return { text: `无权查询产线 ${lineId} 的日志(
|
|
98
|
+
if (lineId && !readable.includes(lineId))
|
|
99
|
+
return { text: `无权查询产线 ${lineId} 的日志(你的可读产线:${readable.join(', ') || '(无)'})。`, isError: true }
|
|
73
100
|
const kind = String(args.kind ?? '').trim() || undefined
|
|
74
101
|
const actorKind = String(args.actor_kind ?? '').trim() || undefined
|
|
75
102
|
const mine = args.mine === true || args.mine === 'true'
|
|
76
103
|
const { minutes, limit, from } = windowOf(args)
|
|
77
104
|
|
|
78
|
-
const lines = lineId ? [lineId] :
|
|
105
|
+
const lines = lineId ? [lineId] : readable
|
|
79
106
|
let rows = fanoutQuery(audit, lines, lid => ({ lineId: lid, kind, actorKind, actor: mine ? agentId : undefined, from, limit }))
|
|
80
107
|
if (nodeId) rows = rows.filter(r => r.targetId === nodeId)
|
|
81
108
|
rows.sort((a, b) => String(b.at).localeCompare(String(a.at)))
|
|
@@ -86,7 +113,7 @@ export async function toolOpsLog(agentId: string, args: {
|
|
|
86
113
|
const src = OPS_ACTOR_LABEL[String(r.actorKind)] ?? String(r.actorKind)
|
|
87
114
|
return `- [${fmtAuditAt(String(r.at))}] 来源=${src} 操作者=${String(r.actorName) || String(r.actor)} | ${String(r.action)} | ${String(r.summary)}`
|
|
88
115
|
})
|
|
89
|
-
const scopeNote = lineId ? `产线 ${lineId}` :
|
|
116
|
+
const scopeNote = lineId ? `产线 ${lineId}` : `可读产线 ${readable.length} 条`
|
|
90
117
|
return {
|
|
91
118
|
text: `运维日志(${scopeNote},近 ${minutes} 分钟,${rows.length} 条,新→旧):\n${body.join('\n')}\n\n说明:来源=Agent 的操作者格式为「Channel名/成员名」;需要节点级参数值变更史用 dcw_journal,配方下发/回退专门视图用 recipe_log。`,
|
|
92
119
|
}
|
|
@@ -103,14 +130,15 @@ export async function toolRecipeLog(agentId: string, args: {
|
|
|
103
130
|
const audit = getOps()?.audit
|
|
104
131
|
if (!audit) return { text: '审计仓储未装配(服务未就绪),请稍后重试。', isError: true }
|
|
105
132
|
const scope = agentOpsScope(agentId)
|
|
106
|
-
if (!scope) return { text: '
|
|
133
|
+
if (!scope) return { text: '你尚未绑定任何工业节点,所在频道也未绑定产线,无 Recipe 历史可查(权限跟随节点绑定或频道绑线)。', isError: true }
|
|
107
134
|
const lineId = String(args.line_id ?? '').trim()
|
|
108
|
-
|
|
109
|
-
|
|
135
|
+
const readable = readableLineIds(scope)
|
|
136
|
+
if (lineId && !readable.includes(lineId))
|
|
137
|
+
return { text: `无权查询产线 ${lineId} 的 Recipe 历史(你的可读产线:${readable.join(', ') || '(无)'})。`, isError: true }
|
|
110
138
|
const recipeId = String(args.recipe_id ?? '').trim() || undefined
|
|
111
139
|
const { minutes, limit, from } = windowOf(args)
|
|
112
140
|
|
|
113
|
-
const lines = lineId ? [lineId] :
|
|
141
|
+
const lines = lineId ? [lineId] : readable
|
|
114
142
|
const rows = fanoutQuery(audit, lines, lid => [
|
|
115
143
|
{ lineId: lid, kind: 'recipe', recipeId, from, limit },
|
|
116
144
|
{ lineId: lid, kind: 'rollback', recipeId, from, limit },
|
|
@@ -131,9 +159,8 @@ export async function toolRecipeLog(agentId: string, args: {
|
|
|
131
159
|
: action
|
|
132
160
|
return `- [${fmtAuditAt(String(r.at))}] ${tag} 来源=${src} 操作者=${String(r.actorName) || String(r.actor)} | ${String(r.summary)}${r.recipeId ? `(配方 ${String(r.recipeId).slice(0, 8)})` : ''}`
|
|
133
161
|
})
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
162
|
+
const text = `Recipe 变更史(${lineId ? `产线 ${lineId}` : `可读产线 ${readable.length} 条`},近 ${minutes} 分钟,${rows.length} 条,新→旧):\n${body.join('\n')}\n\n说明:配方下发=整批参数写命令;优化开窗=单次设定变更(含 Agent 假设);回退执行=已恢复基线值。节点级参数值逐笔历史用 dcw_journal(node_id)。`
|
|
163
|
+
return { text }
|
|
137
164
|
}
|
|
138
165
|
|
|
139
166
|
// ================================================================
|
|
@@ -145,11 +172,12 @@ export async function toolRecipeLog(agentId: string, args: {
|
|
|
145
172
|
* 配方目标 vs PLC 当前值对照、lastGood 批次。 */
|
|
146
173
|
export async function toolLineContext(agentId: string, args: { line_id?: string } = {}): Promise<{ text: string, isError?: boolean }> {
|
|
147
174
|
const scope = agentOpsScope(agentId)
|
|
148
|
-
if (!scope) return { text: '
|
|
175
|
+
if (!scope) return { text: '你尚未绑定任何工业节点,所在频道也未绑定产线,暂无产线上下文(可在频道设置绑定产线获得只读上下文,或在数字孪生界面绑定节点)。' }
|
|
149
176
|
const wanted = String(args.line_id ?? '').trim()
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
177
|
+
const readable = readableLineIds(scope)
|
|
178
|
+
const lineIds = wanted ? [wanted] : readable
|
|
179
|
+
if (wanted && !readable.includes(wanted)) {
|
|
180
|
+
return { text: `产线 ${wanted} 不在你的可读范围(节点授权与频道绑定覆盖:${readable.join(', ') || '无'})。`, isError: true }
|
|
153
181
|
}
|
|
154
182
|
if (lineIds.length === 0) return { text: '你绑定的节点均未分配产线(未挂线的节点没有产线/产品/配方上下文)。' }
|
|
155
183
|
|
|
@@ -159,7 +187,7 @@ export async function toolLineContext(agentId: string, args: { line_id?: string
|
|
|
159
187
|
const run = getActiveLineRun(lid)
|
|
160
188
|
const recipe = run?.recipeId ? getDcwController().listRecipes().find(r => r.id === run.recipeId) : undefined
|
|
161
189
|
const parts: string[] = []
|
|
162
|
-
parts.push(`■ 产线 ${line?.name ?? lid}(${lid})· 状态:${run ? '运行中' : '待机/停线'}`)
|
|
190
|
+
parts.push(`■ 产线 ${line?.name ?? lid}(${lid})${lineTag(lid, scope)} · 状态:${run ? '运行中' : '待机/停线'}`)
|
|
163
191
|
if (run) {
|
|
164
192
|
parts.push(` 活动批次 ${run.runId.slice(0, 8)} · 产品「${run.productName}」(${run.productId}) · 开跑 ${run.startedAt.slice(11, 19)} · 已打标 ${run.taggedSamples} 样本`)
|
|
165
193
|
}
|
|
@@ -210,7 +238,7 @@ export async function toolLineContext(agentId: string, args: { line_id?: string
|
|
|
210
238
|
sections.push(parts.join('\n'))
|
|
211
239
|
}
|
|
212
240
|
return {
|
|
213
|
-
text: `你控制的产线全景(${sections.length} 条):\n\n${sections.join('\n\n')}\n\n说明:配方目标=开跑批次冻结的工艺窗口;PLC
|
|
241
|
+
text: `你控制的产线全景(${sections.length} 条):\n\n${sections.join('\n\n')}\n\n说明:配方目标=开跑批次冻结的工艺窗口;PLC 当前值=实时读数;(频道绑定,只读)= 该线来自频道绑定的只读授权,可看日志/配方/状态,参数下发仍需节点授权。把验证过的最佳参数固化到配方用 recipe_update;回退配方到稳定版本用 recipe_rollback;逐节点参数变更史用 dcw_journal;配方版本史用 recipe_versions。`,
|
|
214
242
|
}
|
|
215
243
|
}
|
|
216
244
|
|
|
@@ -221,9 +249,11 @@ export async function toolRecipeVersions(agentId: string, args: { recipe_id?: st
|
|
|
221
249
|
if (!recipeId) return { text: 'recipe_id 必填(line_context 可看到当前配方的 id)。', isError: true }
|
|
222
250
|
const recipe = getDcwController().listRecipes().find(r => r.id === recipeId)
|
|
223
251
|
if (!recipe) return { text: `配方 ${recipeId} 不存在。`, isError: true }
|
|
252
|
+
// 只读面:节点授权线 ∪ 频道绑定线均可看版本史(回退走 toolRecipeRollback 的写门)
|
|
224
253
|
const scope = agentOpsScope(agentId)
|
|
225
|
-
|
|
226
|
-
|
|
254
|
+
const readable = scope ? readableLineIds(scope) : []
|
|
255
|
+
if (!recipe.lineId || !readable.includes(recipe.lineId)) {
|
|
256
|
+
return { text: `无权查看配方 ${recipeId} 的版本史(该配方不在你可读的产线上)。`, isError: true }
|
|
227
257
|
}
|
|
228
258
|
const rows = getDcwController().recipeVersions(recipeId)
|
|
229
259
|
const limit = Math.min(Number(args.limit) || 20, 50)
|
|
@@ -327,16 +357,19 @@ export async function toolRecipeUpdate(agentId: string, args: {
|
|
|
327
357
|
}
|
|
328
358
|
|
|
329
359
|
/** 工具:recipe_rollback —— 回退配方参数到稳定版本(指定历史版本或已知良好批次快照)。
|
|
330
|
-
* 生成新版本(非破坏,历史保留);不改运行中 PLC 当前值。
|
|
360
|
+
* 生成新版本(非破坏,历史保留);不改运行中 PLC 当前值。
|
|
361
|
+
* v19 dispatch=true = **统一回退**:定义回退 + 该版本参数整批重下发到 PLC(批次级恢复)。 */
|
|
331
362
|
export async function toolRecipeRollback(agentId: string, args: {
|
|
332
363
|
recipe_id?: string
|
|
333
364
|
version?: number | string
|
|
334
365
|
to_last_good?: boolean | string
|
|
366
|
+
dispatch?: boolean | string
|
|
335
367
|
reason?: string
|
|
336
368
|
}): Promise<{ text: string, isError?: boolean }> {
|
|
337
369
|
const recipeId = String(args.recipe_id ?? '').trim()
|
|
338
370
|
const reason = String(args.reason ?? '').trim()
|
|
339
371
|
const toLastGood = args.to_last_good === true || args.to_last_good === 'true'
|
|
372
|
+
const dispatch = args.dispatch === true || args.dispatch === 'true'
|
|
340
373
|
const version = Number(args.version)
|
|
341
374
|
if (!recipeId) return { text: 'recipe_id 必填。', isError: true }
|
|
342
375
|
if (!reason) return { text: 'reason 必填:回退必须说明原因(如优化翻车/越限),便于版本史追溯。', isError: true }
|
|
@@ -351,6 +384,20 @@ export async function toolRecipeRollback(agentId: string, args: {
|
|
|
351
384
|
return { text: `不能回退到 v${version}(当前已是 v${recipe.version ?? 1};回退目标是更早的版本)。`, isError: true }
|
|
352
385
|
}
|
|
353
386
|
try {
|
|
387
|
+
if (dispatch) {
|
|
388
|
+
const { recipe: updated, run } = await getDcwController().rollbackRecipeAndDispatch(recipeId, {
|
|
389
|
+
version: toLastGood ? undefined : version,
|
|
390
|
+
toLastGood,
|
|
391
|
+
by: 'agent',
|
|
392
|
+
actorName: agentBadgeLabel(agentId),
|
|
393
|
+
actor: agentId,
|
|
394
|
+
description: reason,
|
|
395
|
+
})
|
|
396
|
+
const okN = run.results.filter(r => r.ok).length
|
|
397
|
+
return {
|
|
398
|
+
text: `统一回退完成:配方「${updated.name}」已生成 v${updated.version ?? 1},参数恢复为目标版本(${toLastGood ? '已知良好批次冻结' : `v${version}`}),并已整批重下发到 PLC(${okN}/${run.results.length} 参数成功);原因:${reason}。\n用 daq_query 复测确认恢复效果;版本史用 recipe_versions 复核。`,
|
|
399
|
+
}
|
|
400
|
+
}
|
|
354
401
|
const updated = getDcwController().revertRecipe(recipeId, {
|
|
355
402
|
version: toLastGood ? undefined : version,
|
|
356
403
|
toLastGood,
|
|
@@ -361,10 +408,105 @@ export async function toolRecipeRollback(agentId: string, args: {
|
|
|
361
408
|
description: reason,
|
|
362
409
|
})
|
|
363
410
|
return {
|
|
364
|
-
text: `回退完成:配方「${updated.name}」已生成 v${updated.version ?? 1},参数恢复为目标版本(${toLastGood ? '已知良好批次冻结' : `v${version}`});原因:${reason}。\n
|
|
411
|
+
text: `回退完成:配方「${updated.name}」已生成 v${updated.version ?? 1},参数恢复为目标版本(${toLastGood ? '已知良好批次冻结' : `v${version}`});原因:${reason}。\n注意:仅回退了配方定义,运行中 PLC 未变动;要把恢复参数写入产线(统一回退),用 recipe_rollback 并带 dispatch=true。版本史用 recipe_versions 复核。`,
|
|
365
412
|
}
|
|
366
413
|
}
|
|
367
414
|
catch (err) {
|
|
368
415
|
return { text: `回退失败:${err instanceof Error ? err.message : String(err)}`, isError: true }
|
|
369
416
|
}
|
|
370
417
|
}
|
|
418
|
+
|
|
419
|
+
/** 工具:recipe_trial —— 多参数候选整批试验下发(v19 配方链路核心)。
|
|
420
|
+
* 候选值只覆盖列出节点(**必须是自己绑定的数控节点**);整批一次落产线,**不写配方版本** ——
|
|
421
|
+
* 复测判读:有进步/达标 → recipe_update 固化(同 id 版本+1);劣化 → recipe_rollback(dispatch=true) 统一回退。
|
|
422
|
+
* 受同线试验节拍卡控(默认 ≥5 分钟,防连发震荡);四层限界(量程∩参数∩产品)照常拦截。 */
|
|
423
|
+
export async function toolRecipeTrial(agentId: string, args: {
|
|
424
|
+
recipe_id?: string
|
|
425
|
+
params?: Array<{ node_id?: string, value?: number | string }>
|
|
426
|
+
hypothesis?: string
|
|
427
|
+
}): Promise<{ text: string, isError?: boolean }> {
|
|
428
|
+
const recipeId = String(args.recipe_id ?? '').trim()
|
|
429
|
+
const hypothesis = String(args.hypothesis ?? '').trim()
|
|
430
|
+
if (!recipeId) return { text: 'recipe_id 必填(line_context 可看到当前配方 id)。', isError: true }
|
|
431
|
+
if (!hypothesis) return { text: 'hypothesis 必填:试验必须声明依据(知识库条目/数据分析结论),便于审计追溯。', isError: true }
|
|
432
|
+
const list = (args.params ?? []).filter(p => p && String(p.node_id ?? '').trim() && Number.isFinite(Number(p.value)))
|
|
433
|
+
.map(p => ({ nodeId: String(p.node_id).trim(), value: Number(p.value) }))
|
|
434
|
+
if (list.length === 0) return { text: 'params 至少提供一条 {node_id, value}(value 必须为数字);试验的意义就是多参数一次整批改动。', isError: true }
|
|
435
|
+
const recipe = getDcwController().listRecipes().find(r => r.id === recipeId)
|
|
436
|
+
if (!recipe) return { text: `配方 ${recipeId} 不存在。`, isError: true }
|
|
437
|
+
const repo = getAgentNodeBindingRepo()
|
|
438
|
+
for (const p of list) {
|
|
439
|
+
const node = getDcwController().byId(p.nodeId)
|
|
440
|
+
if (!node) return { text: `节点 ${p.nodeId} 已删除,不能纳入试验。`, isError: true }
|
|
441
|
+
if (node.lineId !== recipe.lineId) return { text: `节点「${node.name}」不在配方「${recipe.name}」所在产线,不可混入试验。`, isError: true }
|
|
442
|
+
if (!node.enabled) return { text: `节点「${node.name}」已停用,试验被拒。`, isError: true }
|
|
443
|
+
if (!repo.find(agentId, p.nodeId, 'dcw')) {
|
|
444
|
+
return { text: `无权试验节点「${node.name}」(仅可改自己绑定的数控节点;缺授权找 lead 用 team_grant_nodes 授予)。`, isError: true }
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const run = await getDcwController().applyRecipe(recipeId, { overrides: list, trial: true })
|
|
449
|
+
const okN = run.results.filter(r => r.ok).length
|
|
450
|
+
// 批次级留痕:试验动作入审计(逐参数写已有 write-audit;这里是"一次试验"粒度)
|
|
451
|
+
try {
|
|
452
|
+
const { recordOps } = await import('../../ops/ops')
|
|
453
|
+
recordOps({
|
|
454
|
+
actor: agentId, actorName: agentBadgeLabel(agentId), actorKind: 'agent',
|
|
455
|
+
action: 'recipe.trial', kind: 'recipe', targetKind: 'recipe', targetId: recipeId, recipeId, lineId: recipe.lineId,
|
|
456
|
+
summary: `配方试验整批下发(${okN}/${run.results.length} 成功,批次 ${run.id.slice(0, 8)}):${list.map(p => `${p.nodeId.slice(0, 8)}→${p.value}`).join(';')} | 假设:${hypothesis}`,
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
catch { /* 审计失败不影响试验结果 */ }
|
|
460
|
+
const changed = run.results.map((r) => {
|
|
461
|
+
const node = r.nodeId ? getDcwController().byId(r.nodeId) : undefined
|
|
462
|
+
const reason = r.ok ? '' : `(${String(r.message).slice(0, 60)})`
|
|
463
|
+
return `${node?.name ?? r.templateRef}→${r.value}${node?.unit ?? ''}${r.ok ? '' : ` 被拒${reason}`}`
|
|
464
|
+
}).join(';')
|
|
465
|
+
return {
|
|
466
|
+
text: `试验已整批下发(${okN}/${run.results.length} 成功,批次 ${run.id.slice(0, 8)}):${changed}。假设:${hypothesis}。注意:本次试验**未写入配方版本** —— 等待工艺惯性后 daq_query 复测判读:有进步/达标 → recipe_update 把候选值固化进配方(同 id 版本+1);无进步/劣化 → recipe_rollback(recipe_id, dispatch=true, reason=…) 统一回退(定义回退+PLC 整批恢复)。`,
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
catch (err) {
|
|
470
|
+
return { text: `试验下发失败:${err instanceof Error ? err.message : String(err)}`, isError: true }
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/** 工具:recipe_apply —— 把配方当前已固化参数整批下发到产线(改配方(recipe_update)之后用)。
|
|
475
|
+
* 与 recipe_trial 的区别:apply 下发的是**已固化版本**(无候选覆盖),用于"改配方 → 配方下发"链路的正式下发。 */
|
|
476
|
+
export async function toolRecipeApply(agentId: string, args: {
|
|
477
|
+
recipe_id?: string
|
|
478
|
+
reason?: string
|
|
479
|
+
}): Promise<{ text: string, isError?: boolean }> {
|
|
480
|
+
const recipeId = String(args.recipe_id ?? '').trim()
|
|
481
|
+
if (!recipeId) return { text: 'recipe_id 必填(line_context 可看到当前配方 id)。', isError: true }
|
|
482
|
+
const recipe = getDcwController().listRecipes().find(r => r.id === recipeId)
|
|
483
|
+
if (!recipe) return { text: `配方 ${recipeId} 不存在。`, isError: true }
|
|
484
|
+
const scope = agentOpsScope(agentId)
|
|
485
|
+
if (!scope || !recipe.lineId || !scope.lineIds.includes(recipe.lineId)) {
|
|
486
|
+
return { text: `无权下发配方 ${recipeId}(该配方不在你负责的产线上)。`, isError: true }
|
|
487
|
+
}
|
|
488
|
+
const boundInRecipe = getAgentNodeBindingRepo().byAgent(agentId)
|
|
489
|
+
.some(b => b.kind === 'dcw' && recipe.params.some(p => p.nodeId === b.nodeId))
|
|
490
|
+
if (!boundInRecipe) {
|
|
491
|
+
return { text: `无权下发配方 ${recipeId}(需持有该配方至少一个数控节点的授权;找 lead 用 team_grant_nodes 授予)。`, isError: true }
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
const run = await getDcwController().applyRecipe(recipeId)
|
|
495
|
+
const okN = run.results.filter(r => r.ok).length
|
|
496
|
+
try {
|
|
497
|
+
const { recordOps } = await import('../../ops/ops')
|
|
498
|
+
recordOps({
|
|
499
|
+
actor: agentId, actorName: agentBadgeLabel(agentId), actorKind: 'agent',
|
|
500
|
+
action: 'recipe.apply', kind: 'recipe', targetKind: 'recipe', targetId: recipeId, recipeId, lineId: recipe.lineId,
|
|
501
|
+
summary: `Agent 整批下发配方 v${recipe.version ?? 1}(${okN}/${run.results.length} 参数成功,批次 ${run.id.slice(0, 8)})${args.reason ? ` | 原因:${String(args.reason).trim()}` : ''}`,
|
|
502
|
+
})
|
|
503
|
+
}
|
|
504
|
+
catch { /* 审计失败不影响下发结果 */ }
|
|
505
|
+
return {
|
|
506
|
+
text: `配方「${recipe.name}」v${recipe.version ?? 1} 已整批下发(${okN}/${run.results.length} 参数成功,批次 ${run.id.slice(0, 8)})。用 daq_query 复测确认工艺响应。`,
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
return { text: `配方下发失败:${err instanceof Error ? err.message : String(err)}`, isError: true }
|
|
511
|
+
}
|
|
512
|
+
}
|
|
@@ -194,7 +194,9 @@ export async function buildDataset(spec: AmlDatasetSpec, by: { id: string, kind:
|
|
|
194
194
|
continue
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
// 公共网格:[max(t0), min(t1)];逐节点最近邻对齐,网格点缺失计 NaN
|
|
197
|
+
// 公共网格:[max(t0), min(t1)];逐节点最近邻对齐,网格点缺失计 NaN。
|
|
198
|
+
// 全节点查空(如该 run 采样缺失/时序库丢数)时 g0/g1 保持 ±Infinity,
|
|
199
|
+
// 必须按丢弃处理 —— 否则 steps=Infinity 会把网格循环推到 RangeError(500)。
|
|
198
200
|
let g0 = -Infinity
|
|
199
201
|
let g1 = Infinity
|
|
200
202
|
for (const n of order) {
|
|
@@ -206,7 +208,7 @@ export async function buildDataset(spec: AmlDatasetSpec, by: { id: string, kind:
|
|
|
206
208
|
g0 = Math.max(g0, first.at)
|
|
207
209
|
g1 = Math.min(g1, last.at)
|
|
208
210
|
}
|
|
209
|
-
const steps = g1 > g0 ? Math.floor((g1 - g0) / beat) + 1 : 0
|
|
211
|
+
const steps = Number.isFinite(g0) && Number.isFinite(g1) && g1 > g0 ? Math.floor((g1 - g0) / beat) + 1 : 0
|
|
210
212
|
if (steps < spec.window.historySteps + spec.window.horizonSteps + 2) {
|
|
211
213
|
runsDropped.push({ runId: run.id, reason: `有效网格 ${steps} 步不足一个窗口` })
|
|
212
214
|
continue
|
|
@@ -96,5 +96,6 @@ export function handleProtocolLine(run: RunningJob, rawLine: string): void {
|
|
|
96
96
|
}
|
|
97
97
|
else if (evt.type === 'error' && evt.message) {
|
|
98
98
|
pushLog(run, `[error] ${evt.message}`)
|
|
99
|
+
run.lastProtocolError = String(evt.message).slice(0, 300) // 结题失败原因优先用它(见 start-job 评估段)
|
|
99
100
|
}
|
|
100
101
|
}
|
|
@@ -140,8 +140,11 @@ export async function startJob(row: AmlJobRow): Promise<void> {
|
|
|
140
140
|
const exit2 = await runPython(run, vpy, [evalScript, '--job', join(jobDir, 'job.json')], jobDir)
|
|
141
141
|
if (run.killed) return
|
|
142
142
|
if (exit2.code !== 0) {
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
// 失败原因优先取评估器的 ##AML error 协议消息(如「test 切分为空…」,人读可操作);
|
|
144
|
+
// stderr 尾兜底 —— 评估器把错误走 stdout 协议行时 errTail 常为空,只给 "exit" 无从排查。
|
|
145
|
+
const reason = run.lastProtocolError || lastErrLine(exit2.errTail)
|
|
146
|
+
rt.repo.job.fail(row.id, 'failed', `评估失败:${reason}(永久错误不重试)`, new Date().toISOString())
|
|
147
|
+
experimentFail(run, `评估失败:${reason}`)
|
|
145
148
|
finishRun(run)
|
|
146
149
|
return
|
|
147
150
|
}
|