agentworkshop 0.7.59 → 0.7.61
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 +108 -0
- package/.output/.AgentWorkShop/prompts/host-tools.json +108 -0
- package/.output/.AgentWorkShop/schema.json +1 -0
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/9j2J4k4w.js +1 -0
- package/.output/public/_nuxt/{DTx_DtUo.js → B0w8DZ4C.js} +1 -1
- package/.output/public/_nuxt/{cBI0JqsV.js → BMlesJth.js} +1 -1
- package/.output/public/_nuxt/BTXdYngG.js +1 -0
- package/.output/public/_nuxt/{DMPpE41S2.js → BXbKTAwC2.js} +1 -1
- package/.output/public/_nuxt/{Bn1N3mJv.js → B_6yBa_Z.js} +1 -1
- package/.output/public/_nuxt/{D6c5ppqB.js → B_ic601Y.js} +1 -1
- package/.output/public/_nuxt/BegV7-FE2.js +1 -0
- package/.output/public/_nuxt/BhllzW3K2.js +1 -0
- package/.output/public/_nuxt/BrRXGr472.js +1 -0
- package/.output/public/_nuxt/{BMZ1d25O.js → BtBgrPNT.js} +1 -1
- package/.output/public/_nuxt/C5MPzgon2.js +1 -0
- package/.output/public/_nuxt/CGChCy-w2.js +1 -0
- package/.output/public/_nuxt/CI5sliIF2.js +1 -0
- package/.output/public/_nuxt/{DTZO6WMs2.js → CI8awYVQ2.js} +1 -1
- package/.output/public/_nuxt/{B03LfmKR.js → CK2ts9sD.js} +1 -1
- package/.output/public/_nuxt/CPZKwmLQ.js +1 -0
- package/.output/public/_nuxt/CUjdqsjC2.js +1 -0
- package/.output/public/_nuxt/{C5wBzGO7.js → CVUMEGa1.js} +1 -1
- package/.output/public/_nuxt/{CYGkUj5M2.js → CcLFZjCI2.js} +1 -1
- package/.output/public/_nuxt/{iZa330UY.js → Cdw4juXw.js} +1 -1
- package/.output/public/_nuxt/{h46i74oE.js → CeEzUIvN.js} +4 -4
- package/.output/public/_nuxt/{zGDAozGA.js → CfIGBe_4.js} +4 -4
- package/.output/public/_nuxt/{CmTqFlYZ.js → Ci5kuU1q.js} +1 -1
- package/.output/public/_nuxt/CiyTTgqr.js +106 -0
- package/.output/public/_nuxt/{Dy-V8OEW2.js → CkYk8bj02.js} +6 -6
- package/.output/public/_nuxt/Cmiz_VOb2.js +47 -0
- package/.output/public/_nuxt/{VVomhVqu2.js → Cnf1PFhB2.js} +1 -1
- package/.output/public/_nuxt/CoTm7jd3.js +28 -0
- package/.output/public/_nuxt/{BzKT-ne_2.js → CsN8RHJ02.js} +1 -1
- package/.output/public/_nuxt/Cst8yEet.js +1 -0
- package/.output/public/_nuxt/Cuw2--X2.js +2 -0
- package/.output/public/_nuxt/D-jg-SoR.js +1 -0
- package/.output/public/_nuxt/{B4R5CHkk.js → D-z6N1Cz.js} +1 -1
- package/.output/public/_nuxt/D3gfSKKi2.js +1 -0
- package/.output/public/_nuxt/{BtNT93vm2.js → D4elH6Bo2.js} +1 -1
- package/.output/public/_nuxt/{BuVH9MB-.js → D4tAdwYm.js} +1 -1
- package/.output/public/_nuxt/D5-QB_-l.js +1 -0
- package/.output/public/_nuxt/DBzdrPzw2.js +1 -0
- package/.output/public/_nuxt/{CCCq9H7D2.js → DC7WzYCf2.js} +1 -1
- package/.output/public/_nuxt/DD4J2Otc.js +1 -0
- package/.output/public/_nuxt/DE-qkHRd2.js +1 -0
- package/.output/public/_nuxt/{BXKCB3rS.js → DE2kUGPK.js} +1 -1
- package/.output/public/_nuxt/{CoGETbe-2.js → DI4sdLmu2.js} +1 -1
- package/.output/public/_nuxt/DKHA3XxF.js +2 -0
- package/.output/public/_nuxt/{Drhta6gr.js → DL6fliAT.js} +2 -2
- package/.output/public/_nuxt/{DvUt9uMt2.js → DP6tsk912.js} +1 -1
- package/.output/public/_nuxt/DP_e7Vwv.js +24 -0
- package/.output/public/_nuxt/{DKhRushG.js → DR83EvmX.js} +1 -1
- package/.output/public/_nuxt/DSWuWYx_2.js +1 -0
- package/.output/public/_nuxt/{BhrnGVGz.js → DSdxfFX_.js} +1 -1
- package/.output/public/_nuxt/DVS06wUy2.js +13 -0
- package/.output/public/_nuxt/DXlAfhEi.js +3 -0
- package/.output/public/_nuxt/Dc5NeCDq.js +1 -0
- package/.output/public/_nuxt/{CW1HGp5X.js → DiHL9ehB.js} +1 -1
- package/.output/public/_nuxt/DjtDexGg.js +1 -0
- package/.output/public/_nuxt/{CuRtzvsa2.js → DnunMTRD2.js} +1 -1
- package/.output/public/_nuxt/{CZ0Gt81h.js → Dok0B4Ol.js} +2 -2
- package/.output/public/_nuxt/DqT2_Jtw2.js +1 -0
- package/.output/public/_nuxt/{DJZzthKt2.js → DtMlAxCm2.js} +1 -1
- package/.output/public/_nuxt/{Du0zf-d3.js → DwzJpr7P.js} +1 -1
- package/.output/public/_nuxt/J94L7hHq2.js +1 -0
- package/.output/public/_nuxt/LQBLwrYS2.js +1 -0
- package/.output/public/_nuxt/N9TYd_Mk2.js +1 -0
- package/.output/public/_nuxt/QSY9fE482.js +1 -0
- package/.output/public/_nuxt/{MEwEMEMl2.js → Qa9rS9oK2.js} +6 -6
- package/.output/public/_nuxt/{pNy4V4kd.js → R3gBFghJ2.js} +1 -1
- package/.output/public/_nuxt/{BAqtRt0q.js → ZxBwmbs9.js} +1 -1
- package/.output/public/_nuxt/{BMUEVeOK.js → _mD4IkJj.js} +1 -1
- package/.output/public/_nuxt/analysis.DfMg76el.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/eba8574d-1991-47df-944c-18608bb58181.json +1 -0
- package/.output/public/_nuxt/c_nqArmG.js +1 -0
- package/.output/public/_nuxt/{default.8ac6QU28.css → default.Bx6RJSbr.css} +1 -1
- package/.output/public/_nuxt/{entry.BHNNQA9H.css → entry.BlbU43qz.css} +1 -1
- package/.output/public/_nuxt/h8ddxY892.js +1 -0
- package/.output/public/_nuxt/{TjGWqrHo.js → i3KBHle7.js} +1 -1
- package/.output/public/_nuxt/jmKfTQMN.js +13 -0
- package/.output/public/_nuxt/{CZwDnomB.js → taHLOXkS.js} +1 -1
- package/.output/public/_nuxt/uYP6z4G72.js +1 -0
- package/.output/public/_nuxt/{CIz7SM4H.js → yuIP6K9s.js} +1 -1
- package/.output/public/_nuxt/zWg-9WN6.js +19 -0
- package/.output/public/_nuxt/zrgcKVTq.js +2 -0
- package/.output/server/chunks/_/LeftOutlined.mjs +1 -1
- package/.output/server/chunks/_/character-asset.repo.mjs +1 -1
- package/.output/server/chunks/_/host-bindings.mjs +1 -1
- package/.output/server/chunks/_/nitro.mjs +1662 -1232
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/_/scene-layout.repo.mjs +1 -1
- package/.output/server/chunks/build/{AuthGate-DDPYswkA.mjs → AuthGate-BK2RqkbD.mjs} +5 -5
- package/.output/server/chunks/build/{AuthGate-DDPYswkA.mjs.map → AuthGate-BK2RqkbD.mjs.map} +1 -1
- package/.output/server/chunks/build/Checkbox-4EdGAAl_.mjs +1 -1
- package/.output/server/chunks/build/{FormItemContext-4ORSzhWv.mjs → FormItemContext-BqtgOBDe.mjs} +2 -2
- package/.output/server/chunks/build/{FormItemContext-4ORSzhWv.mjs.map → FormItemContext-BqtgOBDe.mjs.map} +1 -1
- package/.output/server/chunks/build/{OmpTerminalPanel-DZ2wHMS1.mjs → OmpTerminalPanel-Bun6rpAk.mjs} +4 -4
- package/.output/server/chunks/build/{OmpTerminalPanel-DZ2wHMS1.mjs.map → OmpTerminalPanel-Bun6rpAk.mjs.map} +1 -1
- package/.output/server/chunks/build/{PluginSlot-CT4YaNrB.mjs → PluginSlot-Do6dX0RE.mjs} +2 -2
- package/.output/server/chunks/build/{PluginSlot-CT4YaNrB.mjs.map → PluginSlot-Do6dX0RE.mjs.map} +1 -1
- package/.output/server/chunks/build/{TownView-CZbFn6mO.mjs → TownView-kQZFf4zj.mjs} +3 -3
- package/.output/server/chunks/build/{TownView-CZbFn6mO.mjs.map → TownView-kQZFf4zj.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-Df2pgsAq.mjs → _id_-Bbx1b74O.mjs} +3 -3
- package/.output/server/chunks/build/{_id_-Df2pgsAq.mjs.map → _id_-Bbx1b74O.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-CCVhpJtj.mjs → _id_-IijrRw3f.mjs} +3 -3
- package/.output/server/chunks/build/{_id_-CCVhpJtj.mjs.map → _id_-IijrRw3f.mjs.map} +1 -1
- package/.output/server/chunks/build/{_wsId_-CJqF-hmP.mjs → _wsId_-Dp3ACbQ5.mjs} +22 -21
- package/.output/server/chunks/build/{_wsId_-CJqF-hmP.mjs.map → _wsId_-Dp3ACbQ5.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-BTsbna5H.mjs → agents-BfIoGr_p.mjs} +18 -17
- package/.output/server/chunks/build/agents-BfIoGr_p.mjs.map +1 -0
- package/.output/server/chunks/build/{aml-BBKfWto1.mjs → aml-B5zKrvB3.mjs} +7 -7
- package/.output/server/chunks/build/{aml-BBKfWto1.mjs.map → aml-B5zKrvB3.mjs.map} +1 -1
- package/.output/server/chunks/build/analysis-MVEnlO7A.mjs +531 -0
- package/.output/server/chunks/build/analysis-MVEnlO7A.mjs.map +1 -0
- package/.output/server/chunks/build/{card-DJOYGuVH.mjs → card-IyQjdGXA.mjs} +4 -4
- package/.output/server/chunks/build/{card-DJOYGuVH.mjs.map → card-IyQjdGXA.mjs.map} +1 -1
- package/.output/server/chunks/build/{channel-templates-BPKJ8e4l.mjs → channel-templates-Bd8bgBCh.mjs} +11 -10
- package/.output/server/chunks/build/channel-templates-Bd8bgBCh.mjs.map +1 -0
- package/.output/server/chunks/build/{checkbox-C4AKj81e.mjs → checkbox-NnjtOam0.mjs} +3 -3
- package/.output/server/chunks/build/{checkbox-C4AKj81e.mjs.map → checkbox-NnjtOam0.mjs.map} +1 -1
- package/.output/server/chunks/build/{daq-Cz7TaUAM.mjs → daq-mC3-5A0L.mjs} +5 -5
- package/.output/server/chunks/build/{daq-Cz7TaUAM.mjs.map → daq-mC3-5A0L.mjs.map} +1 -1
- package/.output/server/chunks/build/dcw-EhjJpVQM.mjs +2 -2
- package/.output/server/chunks/build/{default-DweMY872.mjs → default-flcNWlNg.mjs} +30 -19
- package/.output/server/chunks/build/{default-DweMY872.mjs.map → default-flcNWlNg.mjs.map} +1 -1
- package/.output/server/chunks/build/{descriptions-CFaUq4y4.mjs → descriptions-DuAefuzp.mjs} +2 -2
- package/.output/server/chunks/build/{descriptions-CFaUq4y4.mjs.map → descriptions-DuAefuzp.mjs.map} +1 -1
- package/.output/server/chunks/build/{drawer-Cfh9_zft.mjs → drawer-DEIB1_X6.mjs} +2 -2
- package/.output/server/chunks/build/{drawer-Cfh9_zft.mjs.map → drawer-DEIB1_X6.mjs.map} +1 -1
- package/.output/server/chunks/build/{dropdown-CV9GW3z9.mjs → dropdown-BuD1pxH2.mjs} +6 -5
- package/.output/server/chunks/build/{dropdown-CV9GW3z9.mjs.map → dropdown-BuD1pxH2.mjs.map} +1 -1
- package/.output/server/chunks/build/{dropdown-CIsqbX4P.mjs → dropdown-w7qhyN2D.mjs} +2 -2
- package/.output/server/chunks/build/{dropdown-CIsqbX4P.mjs.map → dropdown-w7qhyN2D.mjs.map} +1 -1
- package/.output/server/chunks/build/es-DfgEl5n-.mjs +42 -0
- package/.output/server/chunks/build/es-DfgEl5n-.mjs.map +1 -0
- package/.output/server/chunks/build/{form-cs_RcfGx.mjs → form-CMLL_-Xx.mjs} +7 -6
- package/.output/server/chunks/build/{form-cs_RcfGx.mjs.map → form-CMLL_-Xx.mjs.map} +1 -1
- package/.output/server/chunks/build/{input-CobQ5LJ5.mjs → input-DZcioTwL.mjs} +93 -3449
- package/.output/server/chunks/build/input-DZcioTwL.mjs.map +1 -0
- package/.output/server/chunks/build/{input-number-BpPYAWMk.mjs → input-number-BRnbiKLd.mjs} +6 -5
- package/.output/server/chunks/build/{input-number-BpPYAWMk.mjs.map → input-number-BRnbiKLd.mjs.map} +1 -1
- package/.output/server/chunks/build/{logs-As9fZqU-.mjs → logs-BYop3JfX.mjs} +3 -3
- package/.output/server/chunks/build/{logs-As9fZqU-.mjs.map → logs-BYop3JfX.mjs.map} +1 -1
- package/.output/server/chunks/build/{menu-CVmvHyFT.mjs → menu-DXfhc0qN.mjs} +4 -3
- package/.output/server/chunks/build/{menu-CVmvHyFT.mjs.map → menu-DXfhc0qN.mjs.map} +1 -1
- package/.output/server/chunks/build/{monitor-CdFQ2z4g.mjs → monitor-BjBGkUm2.mjs} +20 -19
- package/.output/server/chunks/build/{monitor-CdFQ2z4g.mjs.map → monitor-BjBGkUm2.mjs.map} +1 -1
- package/.output/server/chunks/build/{operations-B2gHCWgZ.mjs → operations-fxI8lEBJ.mjs} +3 -3
- package/.output/server/chunks/build/{operations-B2gHCWgZ.mjs.map → operations-fxI8lEBJ.mjs.map} +1 -1
- package/.output/server/chunks/build/{pages-BItCt1ar.mjs → pages-6JD9VSbx.mjs} +5 -5
- package/.output/server/chunks/build/{pages-BItCt1ar.mjs.map → pages-6JD9VSbx.mjs.map} +1 -1
- package/.output/server/chunks/build/{permissions-D6mQw_9j.mjs → permissions-u5SZL8Fe.mjs} +14 -13
- package/.output/server/chunks/build/permissions-u5SZL8Fe.mjs.map +1 -0
- package/.output/server/chunks/build/{plugins-NAS9MQ5r.mjs → plugins-Cas6KOF-.mjs} +5 -5
- package/.output/server/chunks/build/{plugins-NAS9MQ5r.mjs.map → plugins-Cas6KOF-.mjs.map} +1 -1
- package/.output/server/chunks/build/{popconfirm-DFfoFhmt.mjs → popconfirm-BWxt-53K.mjs} +3 -3
- package/.output/server/chunks/build/{popconfirm-DFfoFhmt.mjs.map → popconfirm-BWxt-53K.mjs.map} +1 -1
- package/.output/server/chunks/build/{schedules-BjWNDWL5.mjs → schedules-Ct68Jrvw.mjs} +20 -19
- package/.output/server/chunks/build/{schedules-BjWNDWL5.mjs.map → schedules-Ct68Jrvw.mjs.map} +1 -1
- package/.output/server/chunks/build/{segmented-D7FkCgg7.mjs → segmented-DN62bOFu.mjs} +3 -3
- package/.output/server/chunks/build/{segmented-D7FkCgg7.mjs.map → segmented-DN62bOFu.mjs.map} +1 -1
- package/.output/server/chunks/build/{select-FqBNxgxT.mjs → select-DQv1DXcW.mjs} +5 -4
- package/.output/server/chunks/build/{select-FqBNxgxT.mjs.map → select-DQv1DXcW.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-Cl_KRyEn.mjs → settings-CQzR1YXR.mjs} +14 -13
- package/.output/server/chunks/build/settings-CQzR1YXR.mjs.map +1 -0
- package/.output/server/chunks/build/{space-B2uhHJG8.mjs → space-6FfG3llN.mjs} +2 -2
- package/.output/server/chunks/build/{space-B2uhHJG8.mjs.map → space-6FfG3llN.mjs.map} +1 -1
- package/.output/server/chunks/build/{switch-DZAUIbIN.mjs → switch-CfsCVFQP.mjs} +3 -3
- package/.output/server/chunks/build/{switch-DZAUIbIN.mjs.map → switch-CfsCVFQP.mjs.map} +1 -1
- package/.output/server/chunks/build/{table-CeOYERqr.mjs → table-HOjbB5YY.mjs} +11 -10
- package/.output/server/chunks/build/{table-CeOYERqr.mjs.map → table-HOjbB5YY.mjs.map} +1 -1
- package/.output/server/chunks/build/{tabs-LLNSdLl2.mjs → tabs-CC26Rbcw.mjs} +6 -5
- package/.output/server/chunks/build/{tabs-LLNSdLl2.mjs.map → tabs-CC26Rbcw.mjs.map} +1 -1
- package/.output/server/chunks/build/{tag-Dwvkh0Dj.mjs → tag-BtOMjpDx.mjs} +5 -4
- package/.output/server/chunks/build/{tag-Dwvkh0Dj.mjs.map → tag-BtOMjpDx.mjs.map} +1 -1
- package/.output/server/chunks/build/{teams-CGDZHaQl.mjs → teams-Re0wY2SQ.mjs} +19 -18
- package/.output/server/chunks/build/teams-Re0wY2SQ.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-BOYkZuoB.mjs → tokens-DLFIUvXE.mjs} +19 -18
- package/.output/server/chunks/build/tokens-DLFIUvXE.mjs.map +1 -0
- package/.output/server/chunks/build/tooltip-BSDwL_LM.mjs +3362 -0
- package/.output/server/chunks/build/tooltip-BSDwL_LM.mjs.map +1 -0
- package/.output/server/chunks/build/town-BV-hbwb5.mjs +1 -1
- package/.output/server/chunks/build/{town-P1rYoi0v.mjs → town-Bfme7P2I.mjs} +5 -5
- package/.output/server/chunks/build/{town-P1rYoi0v.mjs.map → town-Bfme7P2I.mjs.map} +1 -1
- package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
- package/.output/server/chunks/build/types-DWqIeMRP.mjs +75 -0
- package/.output/server/chunks/build/types-DWqIeMRP.mjs.map +1 -0
- package/.output/server/chunks/build/{useAgentTemplateActions-DsBRUQni.mjs → useAgentTemplateActions-CjBwCb3u.mjs} +1 -1
- package/.output/server/chunks/build/{useAgentTemplateActions-DsBRUQni.mjs.map → useAgentTemplateActions-CjBwCb3u.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAgentTemplateForm-DzQ_t2Ue.mjs → useAgentTemplateForm-BX9_xqj8.mjs} +1 -1
- package/.output/server/chunks/build/{useAgentTemplateForm-DzQ_t2Ue.mjs.map → useAgentTemplateForm-BX9_xqj8.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAmlEntities-BMLHU_39.mjs → useAmlEntities-BrcTqHwa.mjs} +2 -2
- package/.output/server/chunks/build/{useAmlEntities-BMLHU_39.mjs.map → useAmlEntities-BrcTqHwa.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAmlEnv-DdlyVzPu.mjs → useAmlEnv-jiGYK790.mjs} +1 -1
- package/.output/server/chunks/build/{useAmlEnv-DdlyVzPu.mjs.map → useAmlEnv-jiGYK790.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAmlJobs-BG_v21hJ.mjs → useAmlJobs-CVZ1btzg.mjs} +1 -1
- package/.output/server/chunks/build/{useAmlJobs-BG_v21hJ.mjs.map → useAmlJobs-CVZ1btzg.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAmlModels-D7MuOWOy.mjs → useAmlModels-DJkQzcCU.mjs} +1 -1
- package/.output/server/chunks/build/{useAmlModels-D7MuOWOy.mjs.map → useAmlModels-DJkQzcCU.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAmlNotes-CTZe-GI0.mjs → useAmlNotes-DKnx9a8x.mjs} +1 -1
- package/.output/server/chunks/build/{useAmlNotes-CTZe-GI0.mjs.map → useAmlNotes-DKnx9a8x.mjs.map} +1 -1
- package/.output/server/chunks/build/{useAmlPredict-BToRrVRV.mjs → useAmlPredict-Bp4ehwKw.mjs} +1 -1
- package/.output/server/chunks/build/{useAmlPredict-BToRrVRV.mjs.map → useAmlPredict-Bp4ehwKw.mjs.map} +1 -1
- package/.output/server/chunks/build/useAnalysisFeed-B4OwUd7p.mjs +128 -0
- package/.output/server/chunks/build/useAnalysisFeed-B4OwUd7p.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqAddNode-CaOCbe4-.mjs +1 -1
- package/.output/server/chunks/build/{useDaqDetailNode-DXgwAu5u.mjs → useDaqDetailNode-DtZ0oDAn.mjs} +3 -3
- package/.output/server/chunks/build/{useDaqDetailNode-DXgwAu5u.mjs.map → useDaqDetailNode-DtZ0oDAn.mjs.map} +1 -1
- package/.output/server/chunks/build/useDaqDetailParams-CrTMDs-2.mjs +1 -1
- package/.output/server/chunks/build/{useDaqFilters-Jx6EERfr.mjs → useDaqFilters-DYHGhECw.mjs} +3 -3
- package/.output/server/chunks/build/{useDaqFilters-Jx6EERfr.mjs.map → useDaqFilters-DYHGhECw.mjs.map} +1 -1
- package/.output/server/chunks/build/useDaqStream-DhzdoyIR.mjs +1 -1
- package/.output/server/chunks/build/useDaqTemplates-C7GzQmdJ.mjs +1 -1
- package/.output/server/chunks/build/{useDashboardData-Ce-EU5DJ.mjs → useDashboardData-B3lZjE7x.mjs} +3 -3
- package/.output/server/chunks/build/{useDashboardData-Ce-EU5DJ.mjs.map → useDashboardData-B3lZjE7x.mjs.map} +1 -1
- package/.output/server/chunks/build/useDcwAddNode-4mTPQ6T8.mjs +1 -1
- package/.output/server/chunks/build/{useDcwDetailScope-4j3B03Pi.mjs → useDcwDetailScope-BWH7jnVh.mjs} +2 -2
- package/.output/server/chunks/build/{useDcwDetailScope-4j3B03Pi.mjs.map → useDcwDetailScope-BWH7jnVh.mjs.map} +1 -1
- package/.output/server/chunks/build/useDcwStream-BXJ2V8HM.mjs +1 -1
- package/.output/server/chunks/build/{useLogsManual-CMjD62nt.mjs → useLogsManual-CElmZuJh.mjs} +1 -1
- package/.output/server/chunks/build/{useLogsManual-CMjD62nt.mjs.map → useLogsManual-CElmZuJh.mjs.map} +1 -1
- package/.output/server/chunks/build/{useMonitorData-D2gFSonG.mjs → useMonitorData-DcIfMEiz.mjs} +1 -1
- package/.output/server/chunks/build/{useMonitorData-D2gFSonG.mjs.map → useMonitorData-DcIfMEiz.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamActions-BOLQkYxg.mjs → useTeamActions-C3_O_AIU.mjs} +1 -1
- package/.output/server/chunks/build/{useTeamActions-BOLQkYxg.mjs.map → useTeamActions-C3_O_AIU.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamAddMemberDialog-BCgrLPxI.mjs → useTeamAddMemberDialog-DWAoZCKd.mjs} +1 -1
- package/.output/server/chunks/build/{useTeamAddMemberDialog-BCgrLPxI.mjs.map → useTeamAddMemberDialog-DWAoZCKd.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamCreateDialog-CJk40LqS.mjs → useTeamCreateDialog-911mIPWd.mjs} +1 -1
- package/.output/server/chunks/build/{useTeamCreateDialog-CJk40LqS.mjs.map → useTeamCreateDialog-911mIPWd.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamDeployDialog-49hdUXQP.mjs → useTeamDeployDialog-D0STROoQ.mjs} +1 -1
- package/.output/server/chunks/build/{useTeamDeployDialog-49hdUXQP.mjs.map → useTeamDeployDialog-D0STROoQ.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTeamPluginDialog-Dh-QOoiU.mjs → useTeamPluginDialog-Df6-e2OW.mjs} +1 -1
- package/.output/server/chunks/build/{useTeamPluginDialog-Dh-QOoiU.mjs.map → useTeamPluginDialog-Df6-e2OW.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTokenCreate-DkYvEQUD.mjs → useTokenCreate-DOojAhM5.mjs} +1 -1
- package/.output/server/chunks/build/{useTokenCreate-DkYvEQUD.mjs.map → useTokenCreate-DOojAhM5.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTokenRename-DOCP4aeI.mjs → useTokenRename-CMA5dLHx.mjs} +1 -1
- package/.output/server/chunks/build/{useTokenRename-DOCP4aeI.mjs.map → useTokenRename-CMA5dLHx.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTokenReveal-CybwwflV.mjs → useTokenReveal-C19aCKzt.mjs} +1 -1
- package/.output/server/chunks/build/{useTokenReveal-CybwwflV.mjs.map → useTokenReveal-C19aCKzt.mjs.map} +1 -1
- package/.output/server/chunks/build/{useTokensList-BLiMvrc4.mjs → useTokensList-D0ZpLOhR.mjs} +1 -1
- package/.output/server/chunks/build/{useTokensList-BLiMvrc4.mjs.map → useTokensList-D0ZpLOhR.mjs.map} +1 -1
- package/.output/server/chunks/build/{useWorkbenchAuth-DAabQl7X.mjs → useWorkbenchAuth-CN5E3J5L.mjs} +1 -1
- package/.output/server/chunks/build/{useWorkbenchAuth-DAabQl7X.mjs.map → useWorkbenchAuth-CN5E3J5L.mjs.map} +1 -1
- package/.output/server/chunks/build/{useWorkbenchWorkspaces-6-mpCGCR.mjs → useWorkbenchWorkspaces-ChKByQSD.mjs} +1 -1
- package/.output/server/chunks/build/{useWorkbenchWorkspaces-6-mpCGCR.mjs.map → useWorkbenchWorkspaces-ChKByQSD.mjs.map} +1 -1
- package/.output/server/chunks/build/{users-B_pHStEa.mjs → users-BW4jowK4.mjs} +15 -14
- package/.output/server/chunks/build/users-BW4jowK4.mjs.map +1 -0
- package/.output/server/chunks/build/{workshop-C0DefygA.mjs → workshop-Bx55czXL.mjs} +11 -10
- package/.output/server/chunks/build/workshop-Bx55czXL.mjs.map +1 -0
- 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/analysis/_id_.delete.mjs +46 -0
- package/.output/server/chunks/routes/api/workshop/analysis/_id_.delete.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/analysis/_id_.get.mjs +40 -0
- package/.output/server/chunks/routes/api/workshop/analysis/_id_.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/analysis/_id_.patch.mjs +103 -0
- package/.output/server/chunks/routes/api/workshop/analysis/_id_.patch.mjs.map +1 -0
- 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/channel-templates/_id/instantiate.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/channels/_id/activate.post.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/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/index3.post.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 +1 -1
- 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.post.mjs +7 -7
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.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/index7.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 +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 +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 +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/rollback-good.post.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/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/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.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index10.get.mjs +20 -23
- package/.output/server/chunks/routes/api/workshop/index10.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index11.get.mjs +25 -11
- package/.output/server/chunks/routes/api/workshop/index11.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index12.get.mjs +10 -4
- package/.output/server/chunks/routes/api/workshop/index12.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index13.get.mjs +2 -3
- package/.output/server/chunks/routes/api/workshop/index13.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index14.get.mjs +3 -2
- package/.output/server/chunks/routes/api/workshop/index14.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index15.get.mjs +30 -0
- package/.output/server/chunks/routes/api/workshop/index15.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/index3.get.mjs +24 -9
- package/.output/server/chunks/routes/api/workshop/index3.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index4.get.mjs +10 -4
- package/.output/server/chunks/routes/api/workshop/index4.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs +4 -29
- package/.output/server/chunks/routes/api/workshop/index5.get.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 +25 -22
- package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index7.get.mjs +26 -5
- package/.output/server/chunks/routes/api/workshop/index7.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index8.get.mjs +4 -39
- package/.output/server/chunks/routes/api/workshop/index8.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs +40 -20
- package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/teams/_id/deploy.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/workspaces/_id/channel-templates/_tplId_.post.mjs +2 -2
- package/.output/server/chunks/routes/renderer.mjs +3 -3
- package/.output/server/chunks/virtual/entry.mjs +199 -468
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +17 -0
- package/README.md +20 -0
- package/app/components/AppSidebar.vue +1 -0
- package/app/components/analysis/AnalysisEditModal.vue +192 -0
- package/app/components/analysis/AnalysisFilterCard.vue +159 -0
- package/app/components/analysis/AnalysisPageHead.vue +47 -0
- package/app/components/analysis/AnalysisTable.vue +322 -0
- package/app/composables/useRouteMeta.ts +1 -0
- package/app/config/schema.ts +1 -0
- package/app/pages/analysis/composables/useAnalysisFeed.ts +85 -0
- package/app/pages/analysis/index.vue +128 -0
- package/app/pages/analysis/types.ts +50 -0
- package/cli/commands/mcp.mjs +160 -160
- package/i18n/locales/en.ts +63 -0
- package/i18n/locales/zh-CN.ts +63 -0
- package/mcp/aw-mcp-server.mjs +799 -799
- package/package.json +2 -2
- package/scripts/onboarding/onboard.mjs +5 -2
- package/scripts/onboarding/sim-twin.mjs +312 -0
- package/server/api/workshop/analysis/[id].delete.ts +30 -0
- package/server/api/workshop/analysis/[id].get.ts +25 -0
- package/server/api/workshop/analysis/[id].patch.ts +100 -0
- package/server/api/workshop/analysis/index.get.ts +39 -0
- package/server/plugins/workshop.ts +4 -0
- package/server/repositories/user.repository.ts +2 -2
- package/server/services/workshop/agents/host-tool-bridge/catalog.ts +4 -1
- package/server/services/workshop/agents/host-tool-bridge/tools/industrial.ts +5 -2
- package/server/services/workshop/agents/industrial/analysis-tools.ts +242 -0
- package/server/services/workshop/agents/industrial/index.ts +1 -0
- package/server/services/workshop/agents/industrial/ops-tools.ts +3 -2
- package/server/services/workshop/analysis/analysis.repo.ts +241 -0
- package/server/services/workshop/db/database/migrations.ts +10 -0
- package/server/services/workshop/db/database/open.ts +3 -1
- package/server/services/workshop/db/database/schema.ts +32 -1
- package/server/services/workshop/db/ops.repo.ts +1 -1
- package/server/services/workshop/db/retention.ts +2 -0
- package/server/services/workshop/db/schema.sql +31 -0
- package/server/services/workshop/settings.ts +1 -0
- package/shared/config/schema.json +1 -0
- package/.output/public/_nuxt/B-Y_rsfn.js +0 -106
- package/.output/public/_nuxt/B02cxZz72.js +0 -1
- package/.output/public/_nuxt/BKHd_sKD2.js +0 -47
- package/.output/public/_nuxt/BikIgp9R.js +0 -1
- package/.output/public/_nuxt/BkGqyjWm.js +0 -3
- package/.output/public/_nuxt/Br2LgfbH2.js +0 -13
- package/.output/public/_nuxt/BrFF_9742.js +0 -1
- package/.output/public/_nuxt/BtYg4aGR2.js +0 -1
- package/.output/public/_nuxt/C4X_Z1lA.js +0 -1
- package/.output/public/_nuxt/CBATzTUf.js +0 -13
- package/.output/public/_nuxt/CEP4UrDv.js +0 -1
- package/.output/public/_nuxt/CIPKYgrH2.js +0 -1
- package/.output/public/_nuxt/CPSHP_EI.js +0 -1
- package/.output/public/_nuxt/CSyIwYcJ2.js +0 -1
- package/.output/public/_nuxt/CZkTlW93.js +0 -2
- package/.output/public/_nuxt/CZyYdpqZ2.js +0 -1
- package/.output/public/_nuxt/CeDkXWCz.js +0 -1
- package/.output/public/_nuxt/Cl19UD7U.js +0 -1
- package/.output/public/_nuxt/ClrkV0C4.js +0 -1
- package/.output/public/_nuxt/Cv_H1gOM2.js +0 -1
- package/.output/public/_nuxt/D5i2ofVw2.js +0 -1
- package/.output/public/_nuxt/D6XGn5vM.js +0 -2
- package/.output/public/_nuxt/D8VQGThE.js +0 -28
- package/.output/public/_nuxt/DBwuADr82.js +0 -1
- package/.output/public/_nuxt/DfqRQSFn.js +0 -19
- package/.output/public/_nuxt/DraEWn4N2.js +0 -1
- package/.output/public/_nuxt/DyMgrV29.js +0 -1
- package/.output/public/_nuxt/H7Rbu32y2.js +0 -1
- package/.output/public/_nuxt/YDs3mbQu2.js +0 -1
- package/.output/public/_nuxt/Z3MSTcdu2.js +0 -1
- package/.output/public/_nuxt/_vR_sc7t.js +0 -24
- package/.output/public/_nuxt/builds/meta/92fd7e2e-18f2-45c3-8588-d93004dda6e3.json +0 -1
- package/.output/public/_nuxt/gbaq2UOO.js +0 -1
- package/.output/public/_nuxt/o-G6YtmK2.js +0 -1
- package/.output/public/_nuxt/ylowSquM2.js +0 -1
- package/.output/server/chunks/build/agents-BTsbna5H.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-BPKJ8e4l.mjs.map +0 -1
- package/.output/server/chunks/build/input-CobQ5LJ5.mjs.map +0 -1
- package/.output/server/chunks/build/permissions-D6mQw_9j.mjs.map +0 -1
- package/.output/server/chunks/build/settings-Cl_KRyEn.mjs.map +0 -1
- package/.output/server/chunks/build/teams-CGDZHaQl.mjs.map +0 -1
- package/.output/server/chunks/build/tokens-BOYkZuoB.mjs.map +0 -1
- package/.output/server/chunks/build/users-B_pHStEa.mjs.map +0 -1
- package/.output/server/chunks/build/workshop-C0DefygA.mjs.map +0 -1
- package/scripts/_audit/.tmp/audit-data/dcw-params.json +0 -15
- package/scripts/_audit/.tmp/audit-data/dcws.json +0 -30
- package/scripts/_audit/aml-layout-diag.mjs +0 -83
- package/scripts/_audit/channel-inspect.mjs +0 -49
- package/scripts/_audit/cleanup-apply.mjs +0 -89
- package/scripts/_audit/cleanup-plan.mjs +0 -82
- package/scripts/_audit/contrast-check.mjs +0 -43
- package/scripts/_audit/dcw-controller-e2e.ts +0 -106
- package/scripts/_audit/dcw-flush-cost.ts +0 -43
- package/scripts/_audit/dcw-rollback-ledger.ts +0 -156
- package/scripts/_audit/dcw-unrecorded-write.ts +0 -171
- package/scripts/_audit/dcw-write-mutex.ts +0 -157
- package/scripts/_audit/design-shots.mjs +0 -145
- package/scripts/_audit/docs-font-probe.mjs +0 -68
- package/scripts/_audit/docs-probe.mjs +0 -107
- package/scripts/_audit/docs-shots.mjs +0 -171
- package/scripts/_audit/docs-v3-verify.mjs +0 -255
- package/scripts/_audit/docs-vue-lint.mjs +0 -81
- package/scripts/_audit/docs-vue-lint2.mjs +0 -91
- package/scripts/_audit/exp-cost-scaling.mjs +0 -57
- package/scripts/_audit/exp-envelope-and-png.mjs +0 -78
- package/scripts/_audit/exp-png-decode.mjs +0 -67
- package/scripts/_audit/exp-retention-plan.mjs +0 -61
- package/scripts/_audit/exp-retention-rescan.mjs +0 -42
- package/scripts/_audit/exp-sweep-fairness.mjs +0 -50
- package/scripts/_audit/feature-audit-e2e.mjs +0 -342
- package/scripts/_audit/fix-settings-count.mjs +0 -57
- package/scripts/_audit/full-e2e-adopt.mjs +0 -238
- package/scripts/_audit/full-e2e-close-loop.mjs +0 -134
- package/scripts/_audit/full-e2e-retrain.mjs +0 -320
- package/scripts/_audit/full-e2e-supplement.mjs +0 -192
- package/scripts/_audit/full-e2e-ui-final.mjs +0 -111
- package/scripts/_audit/full-e2e-ui-mount.mjs +0 -111
- package/scripts/_audit/full-feature-e2e-20260927.mjs +0 -627
- package/scripts/_audit/glass-verify.mjs +0 -155
- package/scripts/_audit/hydration-diff.mjs +0 -104
- package/scripts/_audit/hydration-head.mjs +0 -36
- package/scripts/_audit/hydration-probe.mjs +0 -59
- package/scripts/_audit/idd-scenarios-e2e.mjs +0 -239
- package/scripts/_audit/kb-diag-complete.mjs +0 -261
- package/scripts/_audit/kb-diag-complete2.mjs +0 -271
- package/scripts/_audit/kb-diag-loop-e2e.mjs +0 -357
- package/scripts/_audit/kb-diag-loop-mock.mjs +0 -416
- package/scripts/_audit/kb-diag-loop-v2.mjs +0 -314
- package/scripts/_audit/lead-matrix-test.mjs +0 -172
- package/scripts/_audit/plc-agent-task.mjs +0 -74
- package/scripts/_audit/plc-clean-scenario.mjs +0 -434
- package/scripts/_audit/plc-clean-shots.mjs +0 -161
- package/scripts/_audit/plc-fc10-probe.mjs +0 -30
- package/scripts/_audit/probe-binding.mjs +0 -114
- package/scripts/_audit/recipe-save-e2e.mjs +0 -365
- package/scripts/_audit/review-fix-verify.mjs +0 -158
- package/scripts/_audit/runtime-dir-e2e.mjs +0 -393
- package/scripts/_audit/town-focus-shot.mjs +0 -129
- package/scripts/_audit/town-reposition.mjs +0 -85
- package/scripts/_audit/verify-cleanup.mjs +0 -67
- package/scripts/_audit/verify-data-integrity.mjs +0 -112
- package/scripts/_audit/watchdog-recovery-test.mjs +0 -149
- package/scripts/_dbg-optloop-ui-shots.mjs +0 -52
- /package/.output/public/_nuxt/{4RTUlYvj2.js → 4RTUlYvj.js} +0 -0
- /package/.output/public/_nuxt/{BBhLVYdM.js → BBhLVYdM2.js} +0 -0
- /package/.output/public/_nuxt/{BG7XSX-S2.js → BG7XSX-S.js} +0 -0
- /package/.output/public/_nuxt/{BU7WuuKh.js → BU7WuuKh2.js} +0 -0
- /package/.output/public/_nuxt/{BrRX-l9B2.js → BrRX-l9B.js} +0 -0
- /package/.output/public/_nuxt/{CHyzK5m52.js → CHyzK5m5.js} +0 -0
- /package/.output/public/_nuxt/{Ckafrjnw2.js → Ckafrjnw.js} +0 -0
- /package/.output/public/_nuxt/{D1Xw30Dj2.js → D1Xw30Dj.js} +0 -0
- /package/.output/public/_nuxt/{DEhjFhZ2.js → DEhjFhZ22.js} +0 -0
- /package/.output/public/_nuxt/{DdNujZN5.js → DdNujZN52.js} +0 -0
- /package/.output/public/_nuxt/{DzwIZ81f.js → DzwIZ81f2.js} +0 -0
- /package/.output/public/_nuxt/{M6NcguDO.js → M6NcguDO2.js} +0 -0
- /package/.output/public/_nuxt/{MW-V7IIP2.js → MW-V7IIP.js} +0 -0
- /package/.output/public/_nuxt/{WmMwIS4y.js → WmMwIS4y2.js} +0 -0
- /package/.output/public/_nuxt/{qIQFsHtF.js → qIQFsHtF2.js} +0 -0
package/mcp/aw-mcp-server.mjs
CHANGED
|
@@ -1,799 +1,799 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* mcp/aw-mcp-server.mjs —— AgentWorkShop 工业 MCP server(stdio,零依赖)。
|
|
4
|
-
*
|
|
5
|
-
* 与 server/harness/aw-mcp-bridge.mjs(host tools 回程桥,面向频道内 agent)不同,
|
|
6
|
-
* 本 server 面向 **外部 skill / MCP 客户端 / 工程师工作台**:把平台 REST 的关键方法
|
|
7
|
-
* (产线/节点绑定/参数映射/配方开跑/频道与优化闭环/插件管理)暴露为一等 MCP 工具,
|
|
8
|
-
* 并 **自动发现本机运行实例的端口**(锁文件 → 惯例端口 → config.yml,见 discovery.mjs),
|
|
9
|
-
* 连接中断自动重发现 —— 挂上就能用,不要求手填端口。
|
|
10
|
-
*
|
|
11
|
-
* 启动: aw mcp (或 node mcp/aw-mcp-server.mjs)
|
|
12
|
-
* 诊断: aw mcp --doctor
|
|
13
|
-
* 客户端配置: aw mcp --print-config
|
|
14
|
-
*
|
|
15
|
-
* 环境变量(全部可选):
|
|
16
|
-
* AW_BASE_URL 显式平台地址(跳过自动发现)
|
|
17
|
-
* AW_PORT 多实例并存时钉住端口
|
|
18
|
-
* AW_HOME home 级配置根(锁文件发现用)
|
|
19
|
-
* AW_TOKEN 直接带 token(优先)
|
|
20
|
-
* AW_EMAIL / AW_PASSWORD 自动登录(首次鉴权调用时懒登录)
|
|
21
|
-
* AW_MCP_TIMEOUT_MS 默认工具 HTTP 超时(默认 30000)
|
|
22
|
-
*
|
|
23
|
-
* 协议:MCP stdio(JSON-RPC 2.0,逐行),与 aw-mcp-bridge 同款约定。
|
|
24
|
-
*/
|
|
25
|
-
import { createInterface } from 'node:readline'
|
|
26
|
-
import { resolve } from 'node:path'
|
|
27
|
-
import { pathToFileURL } from 'node:url'
|
|
28
|
-
import { realpathSync } from 'node:fs'
|
|
29
|
-
import { discoverInstance, discoverWithReport, readMcpEnabled } from './discovery.mjs'
|
|
30
|
-
|
|
31
|
-
const DEFAULT_TIMEOUT_MS = Number(process.env.AW_MCP_TIMEOUT_MS ?? 30_000)
|
|
32
|
-
const INVOKE_TIMEOUT_MS = 200_000 // agent 工具可能阻塞(HITL 审批 180s)
|
|
33
|
-
|
|
34
|
-
/** 会话状态:createState 一次,handleMessage 复用 */
|
|
35
|
-
export function createState(opts = {}) {
|
|
36
|
-
const env = opts.env ?? process.env
|
|
37
|
-
return {
|
|
38
|
-
env,
|
|
39
|
-
cwd: opts.cwd ?? process.cwd(),
|
|
40
|
-
base: opts.env?.AW_BASE_URL ? opts.env.AW_BASE_URL.replace(/\/$/, '') : null,
|
|
41
|
-
port: null,
|
|
42
|
-
source: null,
|
|
43
|
-
token: env.AW_TOKEN ?? null,
|
|
44
|
-
user: null,
|
|
45
|
-
loginError: null,
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export class AuthRequiredError extends Error {}
|
|
50
|
-
|
|
51
|
-
/** 登录被拒(凭据错误/账号不存在)——不是网络问题,不得触发端口重发现 */
|
|
52
|
-
export class LoginError extends Error {}
|
|
53
|
-
|
|
54
|
-
/** fetch 网络层失败(连接拒绝/超时),才值得作废 base 重发现 */
|
|
55
|
-
function isNetworkError(err) {
|
|
56
|
-
return err instanceof TypeError || err?.name === 'AbortError' || err?.name === 'TimeoutError'
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ── 平台 HTTP ──────────────────────────────────────────────
|
|
60
|
-
|
|
61
|
-
async function ensureBase(state) {
|
|
62
|
-
if (state.base) return state.base
|
|
63
|
-
const found = await discoverInstance({ cwd: state.cwd, env: state.env })
|
|
64
|
-
if (!found) {
|
|
65
|
-
throw new Error('未发现运行中的 AgentWorkShop 实例(已探测锁文件/惯例端口/config.yml)。请先 aw start,或为 MCP 进程设置 AW_BASE_URL / AW_PORT。')
|
|
66
|
-
}
|
|
67
|
-
state.base = found.base
|
|
68
|
-
state.port = found.port
|
|
69
|
-
state.source = found.source
|
|
70
|
-
return state.base
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function doLogin(state, email, password) {
|
|
74
|
-
const base = await ensureBase(state)
|
|
75
|
-
const res = await fetch(`${base}/api/users/login`, {
|
|
76
|
-
method: 'POST',
|
|
77
|
-
headers: { 'content-type': 'application/json' },
|
|
78
|
-
body: JSON.stringify({ email, password }),
|
|
79
|
-
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
80
|
-
})
|
|
81
|
-
const json = await res.json().catch(() => null)
|
|
82
|
-
if (!res.ok || !json?.data?.token) {
|
|
83
|
-
throw new LoginError(`登录失败(HTTP ${res.status}): ${json?.message ?? res.statusText}`)
|
|
84
|
-
}
|
|
85
|
-
state.token = json.data.token
|
|
86
|
-
state.user = json.data.user ?? null
|
|
87
|
-
state.loginError = null
|
|
88
|
-
return { user: state.user }
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function ensureToken(state) {
|
|
92
|
-
if (state.token) return state.token
|
|
93
|
-
const email = state.env.AW_EMAIL
|
|
94
|
-
const password = state.env.AW_PASSWORD
|
|
95
|
-
if (!email || !password) {
|
|
96
|
-
throw new AuthRequiredError('未鉴权:请先调用 aw_login 工具,或为 MCP 进程设置 AW_EMAIL + AW_PASSWORD(或 AW_TOKEN)环境变量。实例若从未初始化管理员,先用 /setup 注册(首注册即 admin)。')
|
|
97
|
-
}
|
|
98
|
-
await doLogin(state, email, password)
|
|
99
|
-
return state.token
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* 统一平台调用。返回 { ok, status, data, message }。
|
|
104
|
-
* - 网络层失败:作废 base 自动重发现一次再重试("确保连接")
|
|
105
|
-
* - 401:作废 token 懒重登录一次再重试
|
|
106
|
-
*/
|
|
107
|
-
export async function apiCall(state, method, path, body, opts = {}) {
|
|
108
|
-
const { timeoutMs = DEFAULT_TIMEOUT_MS, auth = true } = opts
|
|
109
|
-
const doFetch = async () => {
|
|
110
|
-
// base 每次 fetch 时重新解析(闭包不得缓存:重发现后旧 base 即刻失效)
|
|
111
|
-
const base = await ensureBase(state)
|
|
112
|
-
const headers = { 'content-type': 'application/json' }
|
|
113
|
-
if (auth) headers.authorization = `Bearer ${await ensureToken(state)}`
|
|
114
|
-
return fetch(`${base}${path}`, {
|
|
115
|
-
method,
|
|
116
|
-
headers,
|
|
117
|
-
body: body === undefined ? undefined : JSON.stringify(body),
|
|
118
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
let res
|
|
122
|
-
try {
|
|
123
|
-
res = await doFetch()
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
if (!isNetworkError(err)) throw err // 登录失败/未鉴权等业务错误直接上抛
|
|
127
|
-
// 连接断了:重新发现端口再试一次(实例可能顺延换端口)
|
|
128
|
-
state.base = null
|
|
129
|
-
state.port = null
|
|
130
|
-
state.source = null
|
|
131
|
-
const base2 = await ensureBase(state)
|
|
132
|
-
try {
|
|
133
|
-
res = await doFetch()
|
|
134
|
-
}
|
|
135
|
-
catch (err2) {
|
|
136
|
-
throw new Error(`平台不可达(重发现后 ${base2} 仍失败): ${err2?.message ?? err?.message}`, { cause: err2 })
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (res.status === 401 && auth) {
|
|
140
|
-
state.token = null
|
|
141
|
-
await ensureToken(state)
|
|
142
|
-
res = await doFetch()
|
|
143
|
-
}
|
|
144
|
-
const json = await res.json().catch(() => null)
|
|
145
|
-
return {
|
|
146
|
-
ok: res.ok,
|
|
147
|
-
status: res.status,
|
|
148
|
-
// 信封嗅探:绝大多数端点走 defineApiHandler({code,message,data}),
|
|
149
|
-
// 少数(如 GET /api/workshop/plugins)是裸 defineEventHandler 直返载荷
|
|
150
|
-
data: isEnvelope(json) ? json.data : json,
|
|
151
|
-
message: json?.message ?? res.statusText,
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function isEnvelope(json) {
|
|
156
|
-
return json != null && typeof json === 'object' && !Array.isArray(json) && 'code' in json && 'data' in json
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// ── 工具注册表 ──────────────────────────────────────────────
|
|
160
|
-
|
|
161
|
-
const obj = (properties = {}, required = []) => ({ type: 'object', properties, required, additionalProperties: true })
|
|
162
|
-
const s = (description, type = 'string') => ({ type, description })
|
|
163
|
-
|
|
164
|
-
/** 结果包装:成功吐 JSON 文本;失败 isError */
|
|
165
|
-
function ok(data) {
|
|
166
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
167
|
-
}
|
|
168
|
-
function fail(message) {
|
|
169
|
-
return { content: [{ type: 'text', text: String(message) }], isError: true }
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** 生成"调 REST → 包装结果"的处理器 */
|
|
173
|
-
function rest(method, pathOf, { timeoutMs, auth = true, bodyOf } = {}) {
|
|
174
|
-
return async (args, state) => {
|
|
175
|
-
const path = typeof pathOf === 'function' ? pathOf(args) : pathOf
|
|
176
|
-
const body = method === 'GET' ? undefined : (bodyOf ? bodyOf(args) : args)
|
|
177
|
-
const r = await apiCall(state, method, path, body, { timeoutMs, auth })
|
|
178
|
-
if (!r.ok) return fail(`${method} ${path} → HTTP ${r.status}: ${r.message}`)
|
|
179
|
-
return ok(r.data)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function queryOf(args) {
|
|
184
|
-
const qs = new URLSearchParams()
|
|
185
|
-
for (const [k, v] of Object.entries(args ?? {})) {
|
|
186
|
-
if (v === undefined || v === null || k === 'body') continue
|
|
187
|
-
qs.set(k, String(v))
|
|
188
|
-
}
|
|
189
|
-
const q = qs.toString()
|
|
190
|
-
return q ? `?${q}` : ''
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/** 摘掉路径参数后的剩余字段作 body(路径参数不属于请求体) */
|
|
194
|
-
function omit(obj, ...keys) {
|
|
195
|
-
const copy = { ...obj }
|
|
196
|
-
for (const k of keys) delete copy[k]
|
|
197
|
-
return copy
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* 工具面(35 个)—— 按三个 skill 的标准流程分组:
|
|
202
|
-
* 连接:aw_status / aw_login
|
|
203
|
-
* 节点绑定:aw_line_* / aw_product_create / aw_recipe_create / aw_dcw_* / aw_param_* / aw_daq_*
|
|
204
|
-
* 优化 channel:aw_channel_* / aw_team_provision / aw_agent_tool_* / aw_twin_profile_* / aw_task_* / aw_model_* / aw_optimization_judge
|
|
205
|
-
* 插件:aw_plugin_list / aw_plugin_toggle
|
|
206
|
-
* 逃生舱:aw_request(限 /api/ 前缀)
|
|
207
|
-
*/
|
|
208
|
-
export function buildToolDefs() {
|
|
209
|
-
const defs = [
|
|
210
|
-
// ── 连接与鉴权 ──
|
|
211
|
-
{
|
|
212
|
-
name: 'aw_status',
|
|
213
|
-
description: '发现并探测本机 AgentWorkShop 实例:返回实际端口/发现来源/健康摘要/鉴权状态。任何流程的第一步。',
|
|
214
|
-
inputSchema: obj(),
|
|
215
|
-
run: async (_args, state) => {
|
|
216
|
-
const report = await discoverWithReport({ cwd: state.cwd, env: state.env })
|
|
217
|
-
if (!report.hit) {
|
|
218
|
-
state.base = null
|
|
219
|
-
return fail(`未发现运行实例。探测记录:\n${JSON.stringify(report.candidates, null, 2)}`)
|
|
220
|
-
}
|
|
221
|
-
state.base = report.hit.base
|
|
222
|
-
state.port = report.hit.base ? Number(new URL(report.hit.base).port) || null : null
|
|
223
|
-
state.source = report.hit.source
|
|
224
|
-
let setup
|
|
225
|
-
try {
|
|
226
|
-
const r = await apiCall(state, 'GET', '/api/users/setup-status', undefined, { auth: false })
|
|
227
|
-
setup = r.data
|
|
228
|
-
}
|
|
229
|
-
catch {
|
|
230
|
-
setup = null
|
|
231
|
-
}
|
|
232
|
-
return ok({
|
|
233
|
-
base: report.hit.base,
|
|
234
|
-
port: state.port,
|
|
235
|
-
discoveredVia: report.hit.source,
|
|
236
|
-
health: report.hit.health,
|
|
237
|
-
needsSetup: setup?.needsSetup ?? null,
|
|
238
|
-
authed: Boolean(state.token),
|
|
239
|
-
mcpEnabled: isMcpEnabled(state),
|
|
240
|
-
mcpEnableHint: '系统设置 → MCP 集成 → 启动 MCP 集成(或 env AW_MCP_ENABLED=true)',
|
|
241
|
-
user: state.user,
|
|
242
|
-
candidates: report.candidates,
|
|
243
|
-
})
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
name: 'aw_login',
|
|
248
|
-
description: '登录平台换取 token(email 或用户名 + 密码;缺省回落 AW_EMAIL/AW_PASSWORD 环境变量)。返回当前用户。',
|
|
249
|
-
inputSchema: obj({
|
|
250
|
-
email: s('邮箱或用户名(缺省用环境变量 AW_EMAIL)'),
|
|
251
|
-
password: s('密码(缺省用环境变量 AW_PASSWORD;不会回显)'),
|
|
252
|
-
}),
|
|
253
|
-
run: async (args, state) => {
|
|
254
|
-
const email = args?.email ?? state.env.AW_EMAIL
|
|
255
|
-
const password = args?.password ?? state.env.AW_PASSWORD
|
|
256
|
-
if (!email || !password) return fail('缺少凭据:请传 email/password,或设置 AW_EMAIL/AW_PASSWORD 环境变量。')
|
|
257
|
-
try {
|
|
258
|
-
const { user } = await doLogin(state, email, password)
|
|
259
|
-
return ok({ user, authed: true })
|
|
260
|
-
}
|
|
261
|
-
catch (err) {
|
|
262
|
-
state.loginError = err.message
|
|
263
|
-
return fail(err.message)
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
// ── 产线 / 产品 / 配方(执行侧)──
|
|
269
|
-
{
|
|
270
|
-
name: 'aw_line_list',
|
|
271
|
-
description: '产线列表(含运行状态)。返回 data 为产线数组。',
|
|
272
|
-
inputSchema: obj(),
|
|
273
|
-
run: rest('GET', '/api/workshop/dcw/lines'),
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
name: 'aw_line_create',
|
|
277
|
-
description: '新建产线。{ name, color?, description? } → 返回 { line }。',
|
|
278
|
-
inputSchema: obj({ name: s('产线名', 'string'), color: s('颜色,如 #35e0a0'), description: s('描述') }, ['name']),
|
|
279
|
-
run: rest('POST', '/api/workshop/dcw/lines'),
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
name: 'aw_line_start',
|
|
283
|
-
description: '产线开跑:按配方逐参数下发并激活打标采集窗。{ lineId, recipeId }(recipe 须属本产线产品)。',
|
|
284
|
-
inputSchema: obj({ lineId: s('产线 id'), recipeId: s('配方 id') }, ['lineId', 'recipeId']),
|
|
285
|
-
run: async (args, state) => {
|
|
286
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/lines/${args.lineId}/start`, { recipeId: args.recipeId })
|
|
287
|
-
if (!r.ok) return fail(`开跑失败 → HTTP ${r.status}: ${r.message}`)
|
|
288
|
-
return ok(r.data)
|
|
289
|
-
},
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
name: 'aw_line_stop',
|
|
293
|
-
description: '停线(停止采集/关闭打标窗)。{ lineId }。',
|
|
294
|
-
inputSchema: obj({ lineId: s('产线 id') }, ['lineId']),
|
|
295
|
-
run: async (args, state) => {
|
|
296
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/lines/${args.lineId}/stop`, {})
|
|
297
|
-
if (!r.ok) return fail(`停线失败 → HTTP ${r.status}: ${r.message}`)
|
|
298
|
-
return ok(r.data)
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
name: 'aw_product_create',
|
|
303
|
-
description: '创建产品(产线隔离顶层归属)。{ lineId, name, description?, paramLimits? }(paramLimits: 参数 key → {min,max},仅可收窄)。',
|
|
304
|
-
inputSchema: obj({ lineId: s('产线 id'), name: s('产品名'), description: s('描述'), paramLimits: s('参数限界对象', 'object') }, ['lineId', 'name']),
|
|
305
|
-
run: async (args, state) => {
|
|
306
|
-
const { lineId, ...body } = args
|
|
307
|
-
const r = await apiCall(state, 'POST', '/api/workshop/dcw/products', { ...body, lineId })
|
|
308
|
-
if (!r.ok) return fail(`创建产品失败 → HTTP ${r.status}: ${r.message}`)
|
|
309
|
-
return ok(r.data)
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
name: 'aw_recipe_create',
|
|
314
|
-
description: '创建配方(节点级参数绑定)。{ productId?, name, description?, params?: [{nodeId, value, min?, max?, stepLimit?}], daqWindows?: [{nodeId, min?, max?}] }。description 是配方的元字段:用一句话写明目标工况(如「目标:膜厚 50±2μm;线速 99/模口 0.97;探索收敛于 2026-09-27」),便于按目标检索与区分不同目标保存的配方。',
|
|
315
|
-
inputSchema: obj({
|
|
316
|
-
productId: s('产品 id'),
|
|
317
|
-
name: s('配方名'),
|
|
318
|
-
description: s('配方描述(元字段):优化目标/工况要点/来源,建议一句话讲清"这个配方是为了什么"'),
|
|
319
|
-
params: s('参数绑定数组', 'array'),
|
|
320
|
-
daqWindows: s('数采监控窗口数组', 'array'),
|
|
321
|
-
}, ['name']),
|
|
322
|
-
run: rest('POST', '/api/workshop/dcw/recipes'),
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
name: 'aw_recipe_list',
|
|
326
|
-
description: '配方列表(含 description 元字段/params/版本/lastGoodRunId)+ 批次列表。{ productId?, lineId? } 过滤可选。用于核对"不同目标保存的不同配方"。',
|
|
327
|
-
inputSchema: obj({ productId: s('按产品过滤'), lineId: s('按产线过滤') }),
|
|
328
|
-
run: async (args, state) => {
|
|
329
|
-
const r = await apiCall(state, 'GET', '/api/workshop/dcw/recipes', undefined)
|
|
330
|
-
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
331
|
-
const data = r.data ?? {}
|
|
332
|
-
let recipes = Array.isArray(data) ? data : (data.recipes ?? [])
|
|
333
|
-
if (args.productId) recipes = recipes.filter(x => x.productId === args.productId)
|
|
334
|
-
if (args.lineId) recipes = recipes.filter(x => x.lineId === args.lineId)
|
|
335
|
-
return ok({ recipes: recipes.map(x => ({ id: x.id, name: x.name, description: x.description, productId: x.productId, lineId: x.lineId, params: x.params, version: x.version, lastGoodRunId: x.lastGoodRunId ?? null })), runs: data.runs ?? [] })
|
|
336
|
-
},
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
name: 'aw_recipe_update',
|
|
340
|
-
description: '编辑配方(params 变更自动版本化入史;description/name 可直接改)。{ id, name?, description?, params? }。优化收敛后用本工具把最佳参数与目标描述写回配方。',
|
|
341
|
-
inputSchema: obj({
|
|
342
|
-
id: s('配方 id'),
|
|
343
|
-
name: s('新名称'),
|
|
344
|
-
description: s('新描述(元字段)'),
|
|
345
|
-
params: s('新参数绑定数组(自动版本化)', 'array'),
|
|
346
|
-
}, ['id']),
|
|
347
|
-
run: async (args, state) => {
|
|
348
|
-
const { id, ...body } = args
|
|
349
|
-
const r = await apiCall(state, 'PATCH', `/api/workshop/dcw/recipes/${id}`, body)
|
|
350
|
-
if (!r.ok) return fail(`更新被拒 → HTTP ${r.status}: ${r.message}`)
|
|
351
|
-
return ok(r.data)
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
name: 'aw_recipe_mark_good',
|
|
356
|
-
description: '标记已知良好批次(lastGoodRunId,回退/基准恢复的目标)。{ id, runId }。',
|
|
357
|
-
inputSchema: obj({ id: s('配方 id'), runId: s('批次 id(该配方的已完结批次)') }, ['id', 'runId']),
|
|
358
|
-
run: async (args, state) => {
|
|
359
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/recipes/${args.id}/mark-good`, { runId: args.runId })
|
|
360
|
-
if (!r.ok) return fail(`标记失败 → HTTP ${r.status}: ${r.message}`)
|
|
361
|
-
return ok(r.data)
|
|
362
|
-
},
|
|
363
|
-
},
|
|
364
|
-
|
|
365
|
-
// ── DCW 写控制节点与参数映射 ──
|
|
366
|
-
{
|
|
367
|
-
name: 'aw_dcw_snapshot',
|
|
368
|
-
description: 'DCW 全量快照:controller/nodes/params/templates/drivers/recipes/runs/products/lines。查模板 ref 与现有拓扑的标准入口。',
|
|
369
|
-
inputSchema: obj(),
|
|
370
|
-
run: rest('GET', '/api/workshop/dcw'),
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
name: 'aw_dcw_create',
|
|
374
|
-
description: '创建写控制节点(PLC 执行面)。必填 templateRef(模板 ref,如 dcw-temp-sp);driver+driverConfig 必须取自模拟器 export 或真实设备规约,禁止手抄;建议显式传 lineId/unit/min/max/decimals/holdIntervalMs。节点创建即自动生成同名工艺参数映射。',
|
|
375
|
-
inputSchema: obj({
|
|
376
|
-
templateRef: s('模板 ref(dcw-<key>)'),
|
|
377
|
-
name: s('节点名'),
|
|
378
|
-
lineId: s('所属产线 id'),
|
|
379
|
-
driver: s('驱动:modbus-tcp | modbus-rtu | opcua | mqtt | http'),
|
|
380
|
-
driverConfig: s('驱动配置对象(取自设备 export)', 'object'),
|
|
381
|
-
unit: s('工程单位'), min: s('量程下限', 'number'), max: s('量程上限', 'number'), decimals: s('小数位(量化精度)', 'number'),
|
|
382
|
-
holdIntervalMs: s('保持周期 ms', 'number'), readIntervalMs: s('回读周期 ms', 'number'), writeLockSeconds: s('写锁秒数', 'number'), stepLimit: s('单步限幅', 'number'),
|
|
383
|
-
semantics: s('工艺语义文案'),
|
|
384
|
-
transform: s('量程变换 {kind,scale,offset}', 'object'),
|
|
385
|
-
}, ['templateRef']),
|
|
386
|
-
run: rest('POST', '/api/workshop/dcw'),
|
|
387
|
-
},
|
|
388
|
-
{
|
|
389
|
-
name: 'aw_dcw_write',
|
|
390
|
-
description: '节点直写(核心写命令,四层限界联锁生效,越界 400 并点名约束层)。{ id, value }。',
|
|
391
|
-
inputSchema: obj({ id: s('节点 id'), value: s('工程量纲目标值', 'number') }, ['id', 'value']),
|
|
392
|
-
run: async (args, state) => {
|
|
393
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/${args.id}/write`, { value: args.value })
|
|
394
|
-
if (!r.ok) return fail(`写入被拒 → HTTP ${r.status}: ${r.message}`)
|
|
395
|
-
return ok(r.data)
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
|
-
{
|
|
399
|
-
name: 'aw_dcw_read',
|
|
400
|
-
description: '读节点 PLC 实时值(被动观测免审批)。{ id }。',
|
|
401
|
-
inputSchema: obj({ id: s('节点 id') }, ['id']),
|
|
402
|
-
run: async (args, state) => {
|
|
403
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/${args.id}/read`, {})
|
|
404
|
-
if (!r.ok) return fail(`读失败 → HTTP ${r.status}: ${r.message}`)
|
|
405
|
-
return ok(r.data)
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
name: 'aw_param_list',
|
|
410
|
-
description: '工艺参数映射列表(语义面,不透出寄存器细节)。{ lineId?, limits? }(limits=1 附四层写入限界剖面)。',
|
|
411
|
-
inputSchema: obj({ lineId: s('按产线过滤'), limits: s('是否附限界剖面(1/0)') }),
|
|
412
|
-
run: async (args, state) => {
|
|
413
|
-
const r = await apiCall(state, 'GET', `/api/workshop/dcw/params${queryOf(args)}`, undefined)
|
|
414
|
-
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
415
|
-
return ok(r.data)
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
name: 'aw_param_write',
|
|
420
|
-
description: '参数映射标准写入口(工程量纲;四层限界联锁:参数基准 ∩ 产品限界 ∩ 配方窗 ∩ 节点量程)。{ id, value }。',
|
|
421
|
-
inputSchema: obj({ id: s('参数映射 id'), value: s('工程量纲目标值', 'number') }, ['id', 'value']),
|
|
422
|
-
run: async (args, state) => {
|
|
423
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/params/${args.id}/write`, { value: args.value })
|
|
424
|
-
if (!r.ok) return fail(`写入被拒 → HTTP ${r.status}: ${r.message}`)
|
|
425
|
-
return ok(r.data)
|
|
426
|
-
},
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
name: 'aw_param_read',
|
|
430
|
-
description: '读参数映射当前工程量纲值。{ id }。',
|
|
431
|
-
inputSchema: obj({ id: s('参数映射 id') }, ['id']),
|
|
432
|
-
run: async (args, state) => {
|
|
433
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/params/${args.id}/read`, {})
|
|
434
|
-
if (!r.ok) return fail(`读失败 → HTTP ${r.status}: ${r.message}`)
|
|
435
|
-
return ok(r.data)
|
|
436
|
-
},
|
|
437
|
-
},
|
|
438
|
-
|
|
439
|
-
// ── DAQ 数采 ──
|
|
440
|
-
{
|
|
441
|
-
name: 'aw_daq_create',
|
|
442
|
-
description: '创建数采节点(观测面;不下发)。{ templateRef, name?, lineId?, driver?, driverConfig?, unit?, min?, max?, decimals?, warnLow?, warnHigh?, intervalMs?, semantics? }。',
|
|
443
|
-
inputSchema: obj({
|
|
444
|
-
templateRef: s('数采模板 ref(如 daq-temp-tc)'),
|
|
445
|
-
name: s('节点名'), lineId: s('所属产线 id'),
|
|
446
|
-
driver: s('驱动'), driverConfig: s('驱动配置对象', 'object'),
|
|
447
|
-
unit: s('单位'), min: s('量程下限', 'number'), max: s('量程上限', 'number'), decimals: s('小数位', 'number'),
|
|
448
|
-
warnLow: s('低警线', 'number'), warnHigh: s('高警线', 'number'), intervalMs: s('采样周期 ms', 'number'),
|
|
449
|
-
semantics: s('语义标注(场景编译 target 词表推断依据,建议写明"…质量输出")'),
|
|
450
|
-
}, ['templateRef']),
|
|
451
|
-
run: rest('POST', '/api/workshop/daq'),
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
name: 'aw_daq_controller',
|
|
455
|
-
description: '数采采集总控。{ action: start|stop|pause|resume|config, defaultIntervalMs? }。重启实例后必须显式 start;未开产线不采样是设计。',
|
|
456
|
-
inputSchema: obj({ action: s('start | stop | pause | resume | config'), defaultIntervalMs: s('默认采样周期 ms', 'number') }, ['action']),
|
|
457
|
-
run: rest('POST', '/api/workshop/daq/controller'),
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
name: 'aw_daq_samples',
|
|
461
|
-
description: '数采时序历史(点序新→旧!)。{ id, bucketMs?, limit?, from?, to? }。取时间窗口前先按 at 升序整理。',
|
|
462
|
-
inputSchema: obj({ id: s('数采节点 id'), bucketMs: s('桶化毫秒', 'number'), limit: s('条数上限', 'number'), from: s('起始 ISO 时间'), to: s('结束 ISO 时间') }, ['id']),
|
|
463
|
-
run: async (args, state) => {
|
|
464
|
-
const { id, ...query } = args
|
|
465
|
-
const r = await apiCall(state, 'GET', `/api/workshop/daq/${id}/samples${queryOf(query)}`, undefined)
|
|
466
|
-
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
467
|
-
return ok(r.data)
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
|
|
471
|
-
// ── 场景优化 Channel 与闭环 ──
|
|
472
|
-
{
|
|
473
|
-
name: 'aw_channel_list',
|
|
474
|
-
description: '频道(Agent 团队)列表。',
|
|
475
|
-
inputSchema: obj(),
|
|
476
|
-
run: rest('GET', '/api/workshop/channels'),
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
name: 'aw_channel_create',
|
|
480
|
-
description: '直接创建频道。{ name, scenarioPrompt?, workspace? } → { channelId }。要建「优化闭环频道」优先用 aw_channel_template_instantiate。',
|
|
481
|
-
inputSchema: obj({ name: s('频道名'), scenarioPrompt: s('场景纪律 prompt'), workspace: s('工作目录') }, ['name']),
|
|
482
|
-
run: rest('POST', '/api/workshop/channels'),
|
|
483
|
-
},
|
|
484
|
-
{
|
|
485
|
-
name: 'aw_channel_template_list',
|
|
486
|
-
description: '频道模板列表(含内置 seed:chtpl-aml-optimization-default 工艺优化通道、chtpl-aml-training-default 训练通道等)。',
|
|
487
|
-
inputSchema: obj(),
|
|
488
|
-
run: rest('GET', '/api/workshop/channel-templates'),
|
|
489
|
-
},
|
|
490
|
-
{
|
|
491
|
-
name: 'aw_channel_template_instantiate',
|
|
492
|
-
description: '从模板实例化优化频道。{ id, name?, toolProfile?(\u0027aml_optimization\u0027等), controlPolicy?(\u0027recommendation_only\u0027|\u0027hitl_governed\u0027|\u0027bounded_auto\u0027), optimizationMode?(\u0027exploration\u0027|\u0027aml\u0027), boundModelId?, objective?, scene?, scenePackId?, providerId? } → { channelId, leadAgentId }。',
|
|
493
|
-
inputSchema: obj({
|
|
494
|
-
id: s('模板 id'),
|
|
495
|
-
name: s('新频道名'),
|
|
496
|
-
toolProfile: s('legacy | hybrid_twin | aml_training | aml_optimization'),
|
|
497
|
-
controlPolicy: s('recommendation_only | hitl_governed | bounded_auto'),
|
|
498
|
-
optimizationMode: s('exploration | aml'),
|
|
499
|
-
boundModelId: s('绑定 AML 模型 id(可选)'),
|
|
500
|
-
objective: s('优化目标对象', 'object'),
|
|
501
|
-
scene: s('场景对象 {sceneId,sceneVersion,lineId,productId,recipeId}', 'object'),
|
|
502
|
-
}, ['id']),
|
|
503
|
-
run: rest('POST', args => `/api/workshop/channel-templates/${args.id}/instantiate`, { bodyOf: args => omit(args, 'id') }),
|
|
504
|
-
},
|
|
505
|
-
{
|
|
506
|
-
name: 'aw_team_provision',
|
|
507
|
-
description: '组装频道剧组(组合工具):建 lead+worker 两个 agent → 建 team → 加成员 → deploy 到频道 → 返回频道内实例 agent 列表(绑定节点工具用实例 id)。',
|
|
508
|
-
inputSchema: obj({
|
|
509
|
-
channelId: s('目标频道 id'),
|
|
510
|
-
lead: s('lead 定义 {name, harness, config?}', 'object'),
|
|
511
|
-
worker: s('worker 定义 {name, harness, config?}', 'object'),
|
|
512
|
-
teamName: s('团队名(缺省自动)'),
|
|
513
|
-
}, ['channelId', 'lead', 'worker']),
|
|
514
|
-
run: async (args, state) => {
|
|
515
|
-
const mkAgent = async (def, role) => {
|
|
516
|
-
const r = await apiCall(state, 'POST', '/api/workshop/agents', { name: def.name, harness: def.harness ?? 'omp', config: def.config ?? {} })
|
|
517
|
-
if (!r.ok) throw new Error(`创建 ${role} agent 失败: HTTP ${r.status} ${r.message}`)
|
|
518
|
-
return r.data
|
|
519
|
-
}
|
|
520
|
-
try {
|
|
521
|
-
const lead = await mkAgent(args.lead, 'lead')
|
|
522
|
-
const worker = await mkAgent(args.worker, 'worker')
|
|
523
|
-
const teamR = await apiCall(state, 'POST', '/api/workshop/teams', { name: args.teamName ?? `team-${Date.now()}` })
|
|
524
|
-
if (!teamR.ok) throw new Error(`建 team 失败: HTTP ${teamR.status} ${teamR.message}`)
|
|
525
|
-
const teamId = teamR.data?.id ?? teamR.data?.team?.id
|
|
526
|
-
for (const [agentId, role] of [[lead.id, 'lead'], [worker.id, 'worker']]) {
|
|
527
|
-
const m = await apiCall(state, 'POST', `/api/workshop/teams/${teamId}/members`, { agentId, role })
|
|
528
|
-
if (!m.ok) throw new Error(`加成员 ${role} 失败: HTTP ${m.status} ${m.message}`)
|
|
529
|
-
}
|
|
530
|
-
const d = await apiCall(state, 'POST', `/api/workshop/teams/${teamId}/deploy`, { channelId: args.channelId })
|
|
531
|
-
if (!d.ok) throw new Error(`deploy 失败: HTTP ${d.status} ${d.message}`)
|
|
532
|
-
const inst = await apiCall(state, 'GET', `/api/workshop/channels/${args.channelId}/agents`, undefined)
|
|
533
|
-
const instances = inst.data ?? []
|
|
534
|
-
return ok({
|
|
535
|
-
channelId: args.channelId,
|
|
536
|
-
teamId,
|
|
537
|
-
leadInstanceId: instances.find(a => a.role === 'lead')?.id ?? null,
|
|
538
|
-
workerInstanceId: instances.find(a => a.role === 'worker')?.id ?? null,
|
|
539
|
-
instances,
|
|
540
|
-
})
|
|
541
|
-
}
|
|
542
|
-
catch (err) {
|
|
543
|
-
return fail(err.message)
|
|
544
|
-
}
|
|
545
|
-
},
|
|
546
|
-
},
|
|
547
|
-
{
|
|
548
|
-
name: 'aw_agent_tool_bind',
|
|
549
|
-
description: '把节点授权给频道成员实例(Agent 工具鉴权基础)。{ agentId(实例 id,非模板 id), nodeId, kind: dcw|daq, mode: auto|manual }。',
|
|
550
|
-
inputSchema: obj({ agentId: s('频道成员实例 id'), nodeId: s('节点 id'), kind: s('dcw | daq'), mode: s('auto | manual') }, ['agentId', 'nodeId', 'kind']),
|
|
551
|
-
run: rest('POST', '/api/workshop/agent-tools/bindings'),
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
name: 'aw_agent_tool_invoke',
|
|
555
|
-
description: '以频道成员实例身份调用宿主工具(optimization_explore / mpc_optimize / twin_* / dcw_control / param_control / daq_query 等,面见 GET /api/workshop/agent-tools/list)。可能阻塞(HITL 审批)。',
|
|
556
|
-
inputSchema: obj({ agentId: s('实例 id'), tool: s('工具名'), args: s('工具参数对象', 'object') }, ['agentId', 'tool']),
|
|
557
|
-
timeoutMs: INVOKE_TIMEOUT_MS,
|
|
558
|
-
run: async (args, state) => {
|
|
559
|
-
const r = await apiCall(state, 'POST', '/api/workshop/agent-tools/invoke', { agentId: args.agentId, tool: args.tool, args: args.args ?? {} }, { timeoutMs: INVOKE_TIMEOUT_MS })
|
|
560
|
-
if (!r.ok) return fail(`工具调用失败(${args.tool}) → HTTP ${r.status}: ${r.message}`)
|
|
561
|
-
// 宿主工具的业务错误(HTTP 200 但 result.isError)必须透传为 MCP isError
|
|
562
|
-
const result = r.data?.result
|
|
563
|
-
if (result?.isError) return { content: [{ type: 'text', text: String(result.text ?? '') }], isError: true }
|
|
564
|
-
return ok(r.data)
|
|
565
|
-
},
|
|
566
|
-
},
|
|
567
|
-
{
|
|
568
|
-
name: 'aw_twin_profile_get',
|
|
569
|
-
description: '读频道孪生 profile(优化维度):toolProfile/optimizationMode(exploration|aml)/boundModelId/objective/controlPolicy;?agentId 附 promptPreview。',
|
|
570
|
-
inputSchema: obj({ channelId: s('频道 id'), agentId: s('可选:附该成员的 prompt 预览') }, ['channelId']),
|
|
571
|
-
run: async (args, state) => {
|
|
572
|
-
const { channelId, agentId } = args
|
|
573
|
-
const q = agentId ? `?agentId=${encodeURIComponent(agentId)}` : ''
|
|
574
|
-
const r = await apiCall(state, 'GET', `/api/workshop/channels/${channelId}/twin-profile${q}`, undefined)
|
|
575
|
-
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
576
|
-
return ok(r.data)
|
|
577
|
-
},
|
|
578
|
-
},
|
|
579
|
-
{
|
|
580
|
-
name: 'aw_twin_profile_patch',
|
|
581
|
-
description: '切换频道优化模式/绑定模型(闭环开关)。{ channelId, optimizationMode?, controlPolicy?, objective?, boundModelId? }。绑模型即自动 exploration→aml(fail-closed:谱系一致+门禁全过,否则 409);boundModelId=null 解绑自动回落 exploration。',
|
|
582
|
-
inputSchema: obj({
|
|
583
|
-
channelId: s('频道 id'),
|
|
584
|
-
optimizationMode: s('exploration | aml'),
|
|
585
|
-
controlPolicy: s('recommendation_only | hitl_governed | bounded_auto'),
|
|
586
|
-
objective: s('优化目标对象', 'object'),
|
|
587
|
-
boundModelId: s('绑定模型 id;null=解绑回落 exploration'),
|
|
588
|
-
}, ['channelId']),
|
|
589
|
-
run: rest('PATCH', args => `/api/workshop/channels/${args.channelId}/twin-profile`, { bodyOf: args => omit(args, 'channelId') }),
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
name: 'aw_task_create',
|
|
593
|
-
description: '向频道下达任务。{ channelId, title, parts: [{text}], mode: goal|..., modeConfig? } (goal 模式配 goalCriteria 验收判据)。',
|
|
594
|
-
inputSchema: obj({
|
|
595
|
-
channelId: s('频道 id'), title: s('任务标题'),
|
|
596
|
-
parts: s('任务书分段 [{text}]', 'array'),
|
|
597
|
-
mode: s('执行模式(如 goal)'),
|
|
598
|
-
modeConfig: s('模式配置,如 {goalCriteria}', 'object'),
|
|
599
|
-
}, ['channelId', 'title', 'parts']),
|
|
600
|
-
run: async (args, state) => {
|
|
601
|
-
const { channelId, ...body } = args
|
|
602
|
-
const r = await apiCall(state, 'POST', `/api/workshop/channels/${channelId}/tasks`, body)
|
|
603
|
-
if (!r.ok) return fail(`下达失败 → HTTP ${r.status}: ${r.message}`)
|
|
604
|
-
return ok(r.data)
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
name: 'aw_task_list',
|
|
609
|
-
description: '频道任务列表(轮询状态用)。{ channelId }。',
|
|
610
|
-
inputSchema: obj({ channelId: s('频道 id') }, ['channelId']),
|
|
611
|
-
run: async (args, state) => {
|
|
612
|
-
const r = await apiCall(state, 'GET', `/api/workshop/channels/${args.channelId}/tasks`, undefined)
|
|
613
|
-
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
614
|
-
return ok(r.data)
|
|
615
|
-
},
|
|
616
|
-
},
|
|
617
|
-
{
|
|
618
|
-
name: 'aw_model_list',
|
|
619
|
-
description: 'AML 模型列表(含 stage: candidate|shadow|production|retired 与门禁摘要)。{ lineId?, stage? } 等 query 透传。',
|
|
620
|
-
inputSchema: obj({ lineId: s('按产线过滤'), stage: s('按阶段过滤') }),
|
|
621
|
-
run: async (args, state) => {
|
|
622
|
-
const r = await apiCall(state, 'GET', `/api/workshop/aml/models${queryOf(args)}`, undefined)
|
|
623
|
-
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
624
|
-
return ok(r.data)
|
|
625
|
-
},
|
|
626
|
-
},
|
|
627
|
-
{
|
|
628
|
-
name: 'aw_model_promote',
|
|
629
|
-
description: '模型阶段晋升(candidate→shadow→production;production 前做 ONNX 深检)。{ id, toStage }。',
|
|
630
|
-
inputSchema: obj({ id: s('模型 id'), toStage: s('目标阶段 shadow | production') }, ['id', 'toStage']),
|
|
631
|
-
run: async (args, state) => {
|
|
632
|
-
const r = await apiCall(state, 'POST', `/api/workshop/aml/models/${args.id}/promote`, { toStage: args.toStage })
|
|
633
|
-
if (!r.ok) return fail(`晋升被拒 → HTTP ${r.status}: ${r.message}`)
|
|
634
|
-
return ok(r.data)
|
|
635
|
-
},
|
|
636
|
-
},
|
|
637
|
-
{
|
|
638
|
-
name: 'aw_optimization_judge',
|
|
639
|
-
description: '优化 step 落判定(判定与执行分离)。{ id, verdict: keep|rollback|uncertain, reason }。',
|
|
640
|
-
inputSchema: obj({ id: s('优化记录 id'), verdict: s('keep | rollback | uncertain'), reason: s('判定理由') }, ['id', 'verdict', 'reason']),
|
|
641
|
-
run: async (args, state) => {
|
|
642
|
-
const { id, ...body } = args
|
|
643
|
-
const r = await apiCall(state, 'POST', `/api/workshop/dcw/optimizations/${id}/judge`, body)
|
|
644
|
-
if (!r.ok) return fail(`判定失败 → HTTP ${r.status}: ${r.message}`)
|
|
645
|
-
return ok(r.data)
|
|
646
|
-
},
|
|
647
|
-
},
|
|
648
|
-
|
|
649
|
-
// ── 插件 ──
|
|
650
|
-
{
|
|
651
|
-
name: 'aw_plugin_list',
|
|
652
|
-
description: '插件管理清单(含启停状态/路由/装载失败 failures)。热重载验证入口。',
|
|
653
|
-
inputSchema: obj(),
|
|
654
|
-
run: rest('GET', '/api/workshop/plugins'),
|
|
655
|
-
},
|
|
656
|
-
{
|
|
657
|
-
name: 'aw_plugin_toggle',
|
|
658
|
-
description: '启用/停用插件(写 plugins-state.json,宿主 ~1s 热重载,无需重启)。{ name, enabled }。',
|
|
659
|
-
inputSchema: obj({ name: s('插件名'), enabled: s('true=启用 false=停用', 'boolean') }, ['name', 'enabled']),
|
|
660
|
-
run: async (args, state) => {
|
|
661
|
-
const action = args.enabled ? 'enable' : 'disable'
|
|
662
|
-
const r = await apiCall(state, 'POST', `/api/workshop/plugins/${encodeURIComponent(args.name)}/${action}`, {})
|
|
663
|
-
if (!r.ok) return fail(`${action} 失败 → HTTP ${r.status}: ${r.message}`)
|
|
664
|
-
return ok(r.data)
|
|
665
|
-
},
|
|
666
|
-
},
|
|
667
|
-
|
|
668
|
-
// ── 逃生舱 ──
|
|
669
|
-
{
|
|
670
|
-
name: 'aw_request',
|
|
671
|
-
description: '通用平台 REST 调用(逃生舱:覆盖工具面未覆盖的端点,如 aml/datasets、aml/jobs、optimizations 查询)。path 必须以 /api/ 开头。',
|
|
672
|
-
inputSchema: obj({
|
|
673
|
-
method: s('GET | POST | PATCH | PUT | DELETE'),
|
|
674
|
-
path: s('路径,必须 /api/ 开头,可含 query'),
|
|
675
|
-
body: s('请求体对象', 'object'),
|
|
676
|
-
auth: s('是否携带鉴权(缺省 true)', 'boolean'),
|
|
677
|
-
}, ['method', 'path']),
|
|
678
|
-
run: async (args, state) => {
|
|
679
|
-
const path = String(args.path ?? '')
|
|
680
|
-
if (!path.startsWith('/api/')) return fail('path 必须以 /api/ 开头(本工具只访问平台 API,不代理任意外网)。')
|
|
681
|
-
if (!['GET', 'POST', 'PATCH', 'PUT', 'DELETE'].includes(String(args.method).toUpperCase())) return fail(`不支持的 method: ${args.method}`)
|
|
682
|
-
const r = await apiCall(state, String(args.method).toUpperCase(), path, args.body, { auth: args.auth !== false })
|
|
683
|
-
if (!r.ok) return fail(`${args.method} ${path} → HTTP ${r.status}: ${r.message}`)
|
|
684
|
-
return ok(r.data)
|
|
685
|
-
},
|
|
686
|
-
},
|
|
687
|
-
]
|
|
688
|
-
return defs
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
let _defs = null
|
|
692
|
-
/** 工具面(模块级缓存;测试可直接读) */
|
|
693
|
-
export function TOOL_DEFS() {
|
|
694
|
-
if (!_defs) _defs = buildToolDefs()
|
|
695
|
-
return _defs
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// ── JSON-RPC(MCP stdio,逐行)──
|
|
699
|
-
|
|
700
|
-
/** 处理一条 JSON-RPC 消息;通知返回 null */
|
|
701
|
-
export async function handleMessage(state, msg) {
|
|
702
|
-
const { id, method, params } = msg ?? {}
|
|
703
|
-
const isRequest = id !== undefined && id !== null
|
|
704
|
-
const respond = result => (isRequest ? { jsonrpc: '2.0', id, result } : null)
|
|
705
|
-
const respondError = (code, message) => (isRequest ? { jsonrpc: '2.0', id, error: { code, message } } : null)
|
|
706
|
-
|
|
707
|
-
try {
|
|
708
|
-
switch (method) {
|
|
709
|
-
case 'initialize':
|
|
710
|
-
return respond({
|
|
711
|
-
protocolVersion: params?.protocolVersion ?? '2024-11-05',
|
|
712
|
-
capabilities: { tools: { listChanged: false } },
|
|
713
|
-
serverInfo: { name: 'aw-industrial-mcp', title: 'AgentWorkShop industrial MCP', version: '1.0.0' },
|
|
714
|
-
})
|
|
715
|
-
case 'notifications/initialized':
|
|
716
|
-
case 'initialized':
|
|
717
|
-
return null
|
|
718
|
-
case 'ping':
|
|
719
|
-
return respond({})
|
|
720
|
-
case 'tools/list':
|
|
721
|
-
return respond({
|
|
722
|
-
tools: TOOL_DEFS().map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
|
|
723
|
-
})
|
|
724
|
-
case 'tools/call': {
|
|
725
|
-
const name = params?.name
|
|
726
|
-
if (!name) return respondError(-32602, 'tools/call 缺少 name')
|
|
727
|
-
// 开关门禁:停用时仅放行 aw_status(自诊断),其余工具一律拒绝
|
|
728
|
-
if (name !== 'aw_status' && !isMcpEnabled(state)) {
|
|
729
|
-
return respond({ content: [{ type: 'text', text: MCP_DISABLED_TEXT }], isError: true })
|
|
730
|
-
}
|
|
731
|
-
const def = TOOL_DEFS().find(t => t.name === name)
|
|
732
|
-
if (!def) return respondError(-32602, `未知工具: ${name}`)
|
|
733
|
-
try {
|
|
734
|
-
const result = await def.run(params?.arguments ?? {}, state)
|
|
735
|
-
return respond(result)
|
|
736
|
-
}
|
|
737
|
-
catch (err) {
|
|
738
|
-
// 工具执行异常按 MCP 约定返回 isError 结果(不中断回合)
|
|
739
|
-
return respond(fail(`工具执行异常(${name}): ${err?.message ?? err}`))
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
default:
|
|
743
|
-
return respondError(-32601, `方法不存在: ${method}`)
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
catch (err) {
|
|
747
|
-
return respondError(-32603, err?.message ?? String(err))
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
function write(msg) {
|
|
752
|
-
process.stdout.write(JSON.stringify(msg) + '\n')
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* MCP 集成开关(系统设置 → MCP 集成 → 启动 MCP 集成;mcp.enabled,live 键)。
|
|
757
|
-
* 3s TTL 缓存:设置页切换后 ≤3s 传导到本服务,无需重启。
|
|
758
|
-
*/
|
|
759
|
-
const MCP_DISABLED_TEXT = 'MCP 集成已停用:请到平台「系统设置 → MCP 集成」打开「启动 MCP 集成」,或为 MCP 进程设置环境变量 AW_MCP_ENABLED=true。'
|
|
760
|
-
export function isMcpEnabled(state) {
|
|
761
|
-
const now = Date.now()
|
|
762
|
-
if (!state._mcpEnabledCache || now - state._mcpEnabledCache.at > 3000) {
|
|
763
|
-
state._mcpEnabledCache = { at: now, value: readMcpEnabled({ cwd: state.cwd, env: state.env }) }
|
|
764
|
-
}
|
|
765
|
-
return state._mcpEnabledCache.value
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
/** stdio 主循环(直接执行本文件时启动) */
|
|
769
|
-
export function runStdio() {
|
|
770
|
-
const state = createState()
|
|
771
|
-
const rl = createInterface({ input: process.stdin })
|
|
772
|
-
rl.on('line', (line) => {
|
|
773
|
-
const trimmed = line.trim()
|
|
774
|
-
if (!trimmed) return
|
|
775
|
-
let msg
|
|
776
|
-
try {
|
|
777
|
-
msg = JSON.parse(trimmed)
|
|
778
|
-
}
|
|
779
|
-
catch {
|
|
780
|
-
return // 非 JSON 行忽略(防御宿主 stderr 混流)
|
|
781
|
-
}
|
|
782
|
-
handleMessage(state, msg)
|
|
783
|
-
.then((out) => {
|
|
784
|
-
if (out) write(out)
|
|
785
|
-
})
|
|
786
|
-
.catch((err) => {
|
|
787
|
-
write({ jsonrpc: '2.0', id: msg?.id ?? null, error: { code: -32603, message: err?.message ?? String(err) } })
|
|
788
|
-
})
|
|
789
|
-
})
|
|
790
|
-
process.stdin.on('end', () => process.exit(0))
|
|
791
|
-
process.stdout.on('error', () => process.exit(0))
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// argv 路径必须先 realpath 再对比:nvm-windows 全局安装经目录 junction(C:\nvm4w\nodejs → 实际版本目录),
|
|
795
|
-
// import.meta.url 是 realpath 而 argv 不是 —— 直接字符串对比恒 false,全局用户的 MCP 直连会静默退出(0.7.58 实测)。
|
|
796
|
-
const isMain = process.argv[1] && import.meta.url === pathToFileURL(realpathSync(resolve(process.argv[1]))).href
|
|
797
|
-
if (isMain) {
|
|
798
|
-
runStdio()
|
|
799
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* mcp/aw-mcp-server.mjs —— AgentWorkShop 工业 MCP server(stdio,零依赖)。
|
|
4
|
+
*
|
|
5
|
+
* 与 server/harness/aw-mcp-bridge.mjs(host tools 回程桥,面向频道内 agent)不同,
|
|
6
|
+
* 本 server 面向 **外部 skill / MCP 客户端 / 工程师工作台**:把平台 REST 的关键方法
|
|
7
|
+
* (产线/节点绑定/参数映射/配方开跑/频道与优化闭环/插件管理)暴露为一等 MCP 工具,
|
|
8
|
+
* 并 **自动发现本机运行实例的端口**(锁文件 → 惯例端口 → config.yml,见 discovery.mjs),
|
|
9
|
+
* 连接中断自动重发现 —— 挂上就能用,不要求手填端口。
|
|
10
|
+
*
|
|
11
|
+
* 启动: aw mcp (或 node mcp/aw-mcp-server.mjs)
|
|
12
|
+
* 诊断: aw mcp --doctor
|
|
13
|
+
* 客户端配置: aw mcp --print-config
|
|
14
|
+
*
|
|
15
|
+
* 环境变量(全部可选):
|
|
16
|
+
* AW_BASE_URL 显式平台地址(跳过自动发现)
|
|
17
|
+
* AW_PORT 多实例并存时钉住端口
|
|
18
|
+
* AW_HOME home 级配置根(锁文件发现用)
|
|
19
|
+
* AW_TOKEN 直接带 token(优先)
|
|
20
|
+
* AW_EMAIL / AW_PASSWORD 自动登录(首次鉴权调用时懒登录)
|
|
21
|
+
* AW_MCP_TIMEOUT_MS 默认工具 HTTP 超时(默认 30000)
|
|
22
|
+
*
|
|
23
|
+
* 协议:MCP stdio(JSON-RPC 2.0,逐行),与 aw-mcp-bridge 同款约定。
|
|
24
|
+
*/
|
|
25
|
+
import { createInterface } from 'node:readline'
|
|
26
|
+
import { resolve } from 'node:path'
|
|
27
|
+
import { pathToFileURL } from 'node:url'
|
|
28
|
+
import { realpathSync } from 'node:fs'
|
|
29
|
+
import { discoverInstance, discoverWithReport, readMcpEnabled } from './discovery.mjs'
|
|
30
|
+
|
|
31
|
+
const DEFAULT_TIMEOUT_MS = Number(process.env.AW_MCP_TIMEOUT_MS ?? 30_000)
|
|
32
|
+
const INVOKE_TIMEOUT_MS = 200_000 // agent 工具可能阻塞(HITL 审批 180s)
|
|
33
|
+
|
|
34
|
+
/** 会话状态:createState 一次,handleMessage 复用 */
|
|
35
|
+
export function createState(opts = {}) {
|
|
36
|
+
const env = opts.env ?? process.env
|
|
37
|
+
return {
|
|
38
|
+
env,
|
|
39
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
40
|
+
base: opts.env?.AW_BASE_URL ? opts.env.AW_BASE_URL.replace(/\/$/, '') : null,
|
|
41
|
+
port: null,
|
|
42
|
+
source: null,
|
|
43
|
+
token: env.AW_TOKEN ?? null,
|
|
44
|
+
user: null,
|
|
45
|
+
loginError: null,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class AuthRequiredError extends Error {}
|
|
50
|
+
|
|
51
|
+
/** 登录被拒(凭据错误/账号不存在)——不是网络问题,不得触发端口重发现 */
|
|
52
|
+
export class LoginError extends Error {}
|
|
53
|
+
|
|
54
|
+
/** fetch 网络层失败(连接拒绝/超时),才值得作废 base 重发现 */
|
|
55
|
+
function isNetworkError(err) {
|
|
56
|
+
return err instanceof TypeError || err?.name === 'AbortError' || err?.name === 'TimeoutError'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── 平台 HTTP ──────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
async function ensureBase(state) {
|
|
62
|
+
if (state.base) return state.base
|
|
63
|
+
const found = await discoverInstance({ cwd: state.cwd, env: state.env })
|
|
64
|
+
if (!found) {
|
|
65
|
+
throw new Error('未发现运行中的 AgentWorkShop 实例(已探测锁文件/惯例端口/config.yml)。请先 aw start,或为 MCP 进程设置 AW_BASE_URL / AW_PORT。')
|
|
66
|
+
}
|
|
67
|
+
state.base = found.base
|
|
68
|
+
state.port = found.port
|
|
69
|
+
state.source = found.source
|
|
70
|
+
return state.base
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function doLogin(state, email, password) {
|
|
74
|
+
const base = await ensureBase(state)
|
|
75
|
+
const res = await fetch(`${base}/api/users/login`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'content-type': 'application/json' },
|
|
78
|
+
body: JSON.stringify({ email, password }),
|
|
79
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
80
|
+
})
|
|
81
|
+
const json = await res.json().catch(() => null)
|
|
82
|
+
if (!res.ok || !json?.data?.token) {
|
|
83
|
+
throw new LoginError(`登录失败(HTTP ${res.status}): ${json?.message ?? res.statusText}`)
|
|
84
|
+
}
|
|
85
|
+
state.token = json.data.token
|
|
86
|
+
state.user = json.data.user ?? null
|
|
87
|
+
state.loginError = null
|
|
88
|
+
return { user: state.user }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function ensureToken(state) {
|
|
92
|
+
if (state.token) return state.token
|
|
93
|
+
const email = state.env.AW_EMAIL
|
|
94
|
+
const password = state.env.AW_PASSWORD
|
|
95
|
+
if (!email || !password) {
|
|
96
|
+
throw new AuthRequiredError('未鉴权:请先调用 aw_login 工具,或为 MCP 进程设置 AW_EMAIL + AW_PASSWORD(或 AW_TOKEN)环境变量。实例若从未初始化管理员,先用 /setup 注册(首注册即 admin)。')
|
|
97
|
+
}
|
|
98
|
+
await doLogin(state, email, password)
|
|
99
|
+
return state.token
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 统一平台调用。返回 { ok, status, data, message }。
|
|
104
|
+
* - 网络层失败:作废 base 自动重发现一次再重试("确保连接")
|
|
105
|
+
* - 401:作废 token 懒重登录一次再重试
|
|
106
|
+
*/
|
|
107
|
+
export async function apiCall(state, method, path, body, opts = {}) {
|
|
108
|
+
const { timeoutMs = DEFAULT_TIMEOUT_MS, auth = true } = opts
|
|
109
|
+
const doFetch = async () => {
|
|
110
|
+
// base 每次 fetch 时重新解析(闭包不得缓存:重发现后旧 base 即刻失效)
|
|
111
|
+
const base = await ensureBase(state)
|
|
112
|
+
const headers = { 'content-type': 'application/json' }
|
|
113
|
+
if (auth) headers.authorization = `Bearer ${await ensureToken(state)}`
|
|
114
|
+
return fetch(`${base}${path}`, {
|
|
115
|
+
method,
|
|
116
|
+
headers,
|
|
117
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
118
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
let res
|
|
122
|
+
try {
|
|
123
|
+
res = await doFetch()
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (!isNetworkError(err)) throw err // 登录失败/未鉴权等业务错误直接上抛
|
|
127
|
+
// 连接断了:重新发现端口再试一次(实例可能顺延换端口)
|
|
128
|
+
state.base = null
|
|
129
|
+
state.port = null
|
|
130
|
+
state.source = null
|
|
131
|
+
const base2 = await ensureBase(state)
|
|
132
|
+
try {
|
|
133
|
+
res = await doFetch()
|
|
134
|
+
}
|
|
135
|
+
catch (err2) {
|
|
136
|
+
throw new Error(`平台不可达(重发现后 ${base2} 仍失败): ${err2?.message ?? err?.message}`, { cause: err2 })
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (res.status === 401 && auth) {
|
|
140
|
+
state.token = null
|
|
141
|
+
await ensureToken(state)
|
|
142
|
+
res = await doFetch()
|
|
143
|
+
}
|
|
144
|
+
const json = await res.json().catch(() => null)
|
|
145
|
+
return {
|
|
146
|
+
ok: res.ok,
|
|
147
|
+
status: res.status,
|
|
148
|
+
// 信封嗅探:绝大多数端点走 defineApiHandler({code,message,data}),
|
|
149
|
+
// 少数(如 GET /api/workshop/plugins)是裸 defineEventHandler 直返载荷
|
|
150
|
+
data: isEnvelope(json) ? json.data : json,
|
|
151
|
+
message: json?.message ?? res.statusText,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isEnvelope(json) {
|
|
156
|
+
return json != null && typeof json === 'object' && !Array.isArray(json) && 'code' in json && 'data' in json
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── 工具注册表 ──────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
const obj = (properties = {}, required = []) => ({ type: 'object', properties, required, additionalProperties: true })
|
|
162
|
+
const s = (description, type = 'string') => ({ type, description })
|
|
163
|
+
|
|
164
|
+
/** 结果包装:成功吐 JSON 文本;失败 isError */
|
|
165
|
+
function ok(data) {
|
|
166
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
167
|
+
}
|
|
168
|
+
function fail(message) {
|
|
169
|
+
return { content: [{ type: 'text', text: String(message) }], isError: true }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** 生成"调 REST → 包装结果"的处理器 */
|
|
173
|
+
function rest(method, pathOf, { timeoutMs, auth = true, bodyOf } = {}) {
|
|
174
|
+
return async (args, state) => {
|
|
175
|
+
const path = typeof pathOf === 'function' ? pathOf(args) : pathOf
|
|
176
|
+
const body = method === 'GET' ? undefined : (bodyOf ? bodyOf(args) : args)
|
|
177
|
+
const r = await apiCall(state, method, path, body, { timeoutMs, auth })
|
|
178
|
+
if (!r.ok) return fail(`${method} ${path} → HTTP ${r.status}: ${r.message}`)
|
|
179
|
+
return ok(r.data)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function queryOf(args) {
|
|
184
|
+
const qs = new URLSearchParams()
|
|
185
|
+
for (const [k, v] of Object.entries(args ?? {})) {
|
|
186
|
+
if (v === undefined || v === null || k === 'body') continue
|
|
187
|
+
qs.set(k, String(v))
|
|
188
|
+
}
|
|
189
|
+
const q = qs.toString()
|
|
190
|
+
return q ? `?${q}` : ''
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** 摘掉路径参数后的剩余字段作 body(路径参数不属于请求体) */
|
|
194
|
+
function omit(obj, ...keys) {
|
|
195
|
+
const copy = { ...obj }
|
|
196
|
+
for (const k of keys) delete copy[k]
|
|
197
|
+
return copy
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 工具面(35 个)—— 按三个 skill 的标准流程分组:
|
|
202
|
+
* 连接:aw_status / aw_login
|
|
203
|
+
* 节点绑定:aw_line_* / aw_product_create / aw_recipe_create / aw_dcw_* / aw_param_* / aw_daq_*
|
|
204
|
+
* 优化 channel:aw_channel_* / aw_team_provision / aw_agent_tool_* / aw_twin_profile_* / aw_task_* / aw_model_* / aw_optimization_judge
|
|
205
|
+
* 插件:aw_plugin_list / aw_plugin_toggle
|
|
206
|
+
* 逃生舱:aw_request(限 /api/ 前缀)
|
|
207
|
+
*/
|
|
208
|
+
export function buildToolDefs() {
|
|
209
|
+
const defs = [
|
|
210
|
+
// ── 连接与鉴权 ──
|
|
211
|
+
{
|
|
212
|
+
name: 'aw_status',
|
|
213
|
+
description: '发现并探测本机 AgentWorkShop 实例:返回实际端口/发现来源/健康摘要/鉴权状态。任何流程的第一步。',
|
|
214
|
+
inputSchema: obj(),
|
|
215
|
+
run: async (_args, state) => {
|
|
216
|
+
const report = await discoverWithReport({ cwd: state.cwd, env: state.env })
|
|
217
|
+
if (!report.hit) {
|
|
218
|
+
state.base = null
|
|
219
|
+
return fail(`未发现运行实例。探测记录:\n${JSON.stringify(report.candidates, null, 2)}`)
|
|
220
|
+
}
|
|
221
|
+
state.base = report.hit.base
|
|
222
|
+
state.port = report.hit.base ? Number(new URL(report.hit.base).port) || null : null
|
|
223
|
+
state.source = report.hit.source
|
|
224
|
+
let setup
|
|
225
|
+
try {
|
|
226
|
+
const r = await apiCall(state, 'GET', '/api/users/setup-status', undefined, { auth: false })
|
|
227
|
+
setup = r.data
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
setup = null
|
|
231
|
+
}
|
|
232
|
+
return ok({
|
|
233
|
+
base: report.hit.base,
|
|
234
|
+
port: state.port,
|
|
235
|
+
discoveredVia: report.hit.source,
|
|
236
|
+
health: report.hit.health,
|
|
237
|
+
needsSetup: setup?.needsSetup ?? null,
|
|
238
|
+
authed: Boolean(state.token),
|
|
239
|
+
mcpEnabled: isMcpEnabled(state),
|
|
240
|
+
mcpEnableHint: '系统设置 → MCP 集成 → 启动 MCP 集成(或 env AW_MCP_ENABLED=true)',
|
|
241
|
+
user: state.user,
|
|
242
|
+
candidates: report.candidates,
|
|
243
|
+
})
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: 'aw_login',
|
|
248
|
+
description: '登录平台换取 token(email 或用户名 + 密码;缺省回落 AW_EMAIL/AW_PASSWORD 环境变量)。返回当前用户。',
|
|
249
|
+
inputSchema: obj({
|
|
250
|
+
email: s('邮箱或用户名(缺省用环境变量 AW_EMAIL)'),
|
|
251
|
+
password: s('密码(缺省用环境变量 AW_PASSWORD;不会回显)'),
|
|
252
|
+
}),
|
|
253
|
+
run: async (args, state) => {
|
|
254
|
+
const email = args?.email ?? state.env.AW_EMAIL
|
|
255
|
+
const password = args?.password ?? state.env.AW_PASSWORD
|
|
256
|
+
if (!email || !password) return fail('缺少凭据:请传 email/password,或设置 AW_EMAIL/AW_PASSWORD 环境变量。')
|
|
257
|
+
try {
|
|
258
|
+
const { user } = await doLogin(state, email, password)
|
|
259
|
+
return ok({ user, authed: true })
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
state.loginError = err.message
|
|
263
|
+
return fail(err.message)
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
// ── 产线 / 产品 / 配方(执行侧)──
|
|
269
|
+
{
|
|
270
|
+
name: 'aw_line_list',
|
|
271
|
+
description: '产线列表(含运行状态)。返回 data 为产线数组。',
|
|
272
|
+
inputSchema: obj(),
|
|
273
|
+
run: rest('GET', '/api/workshop/dcw/lines'),
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: 'aw_line_create',
|
|
277
|
+
description: '新建产线。{ name, color?, description? } → 返回 { line }。',
|
|
278
|
+
inputSchema: obj({ name: s('产线名', 'string'), color: s('颜色,如 #35e0a0'), description: s('描述') }, ['name']),
|
|
279
|
+
run: rest('POST', '/api/workshop/dcw/lines'),
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'aw_line_start',
|
|
283
|
+
description: '产线开跑:按配方逐参数下发并激活打标采集窗。{ lineId, recipeId }(recipe 须属本产线产品)。',
|
|
284
|
+
inputSchema: obj({ lineId: s('产线 id'), recipeId: s('配方 id') }, ['lineId', 'recipeId']),
|
|
285
|
+
run: async (args, state) => {
|
|
286
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/lines/${args.lineId}/start`, { recipeId: args.recipeId })
|
|
287
|
+
if (!r.ok) return fail(`开跑失败 → HTTP ${r.status}: ${r.message}`)
|
|
288
|
+
return ok(r.data)
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'aw_line_stop',
|
|
293
|
+
description: '停线(停止采集/关闭打标窗)。{ lineId }。',
|
|
294
|
+
inputSchema: obj({ lineId: s('产线 id') }, ['lineId']),
|
|
295
|
+
run: async (args, state) => {
|
|
296
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/lines/${args.lineId}/stop`, {})
|
|
297
|
+
if (!r.ok) return fail(`停线失败 → HTTP ${r.status}: ${r.message}`)
|
|
298
|
+
return ok(r.data)
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'aw_product_create',
|
|
303
|
+
description: '创建产品(产线隔离顶层归属)。{ lineId, name, description?, paramLimits? }(paramLimits: 参数 key → {min,max},仅可收窄)。',
|
|
304
|
+
inputSchema: obj({ lineId: s('产线 id'), name: s('产品名'), description: s('描述'), paramLimits: s('参数限界对象', 'object') }, ['lineId', 'name']),
|
|
305
|
+
run: async (args, state) => {
|
|
306
|
+
const { lineId, ...body } = args
|
|
307
|
+
const r = await apiCall(state, 'POST', '/api/workshop/dcw/products', { ...body, lineId })
|
|
308
|
+
if (!r.ok) return fail(`创建产品失败 → HTTP ${r.status}: ${r.message}`)
|
|
309
|
+
return ok(r.data)
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'aw_recipe_create',
|
|
314
|
+
description: '创建配方(节点级参数绑定)。{ productId?, name, description?, params?: [{nodeId, value, min?, max?, stepLimit?}], daqWindows?: [{nodeId, min?, max?}] }。description 是配方的元字段:用一句话写明目标工况(如「目标:膜厚 50±2μm;线速 99/模口 0.97;探索收敛于 2026-09-27」),便于按目标检索与区分不同目标保存的配方。',
|
|
315
|
+
inputSchema: obj({
|
|
316
|
+
productId: s('产品 id'),
|
|
317
|
+
name: s('配方名'),
|
|
318
|
+
description: s('配方描述(元字段):优化目标/工况要点/来源,建议一句话讲清"这个配方是为了什么"'),
|
|
319
|
+
params: s('参数绑定数组', 'array'),
|
|
320
|
+
daqWindows: s('数采监控窗口数组', 'array'),
|
|
321
|
+
}, ['name']),
|
|
322
|
+
run: rest('POST', '/api/workshop/dcw/recipes'),
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'aw_recipe_list',
|
|
326
|
+
description: '配方列表(含 description 元字段/params/版本/lastGoodRunId)+ 批次列表。{ productId?, lineId? } 过滤可选。用于核对"不同目标保存的不同配方"。',
|
|
327
|
+
inputSchema: obj({ productId: s('按产品过滤'), lineId: s('按产线过滤') }),
|
|
328
|
+
run: async (args, state) => {
|
|
329
|
+
const r = await apiCall(state, 'GET', '/api/workshop/dcw/recipes', undefined)
|
|
330
|
+
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
331
|
+
const data = r.data ?? {}
|
|
332
|
+
let recipes = Array.isArray(data) ? data : (data.recipes ?? [])
|
|
333
|
+
if (args.productId) recipes = recipes.filter(x => x.productId === args.productId)
|
|
334
|
+
if (args.lineId) recipes = recipes.filter(x => x.lineId === args.lineId)
|
|
335
|
+
return ok({ recipes: recipes.map(x => ({ id: x.id, name: x.name, description: x.description, productId: x.productId, lineId: x.lineId, params: x.params, version: x.version, lastGoodRunId: x.lastGoodRunId ?? null })), runs: data.runs ?? [] })
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: 'aw_recipe_update',
|
|
340
|
+
description: '编辑配方(params 变更自动版本化入史;description/name 可直接改)。{ id, name?, description?, params? }。优化收敛后用本工具把最佳参数与目标描述写回配方。',
|
|
341
|
+
inputSchema: obj({
|
|
342
|
+
id: s('配方 id'),
|
|
343
|
+
name: s('新名称'),
|
|
344
|
+
description: s('新描述(元字段)'),
|
|
345
|
+
params: s('新参数绑定数组(自动版本化)', 'array'),
|
|
346
|
+
}, ['id']),
|
|
347
|
+
run: async (args, state) => {
|
|
348
|
+
const { id, ...body } = args
|
|
349
|
+
const r = await apiCall(state, 'PATCH', `/api/workshop/dcw/recipes/${id}`, body)
|
|
350
|
+
if (!r.ok) return fail(`更新被拒 → HTTP ${r.status}: ${r.message}`)
|
|
351
|
+
return ok(r.data)
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: 'aw_recipe_mark_good',
|
|
356
|
+
description: '标记已知良好批次(lastGoodRunId,回退/基准恢复的目标)。{ id, runId }。',
|
|
357
|
+
inputSchema: obj({ id: s('配方 id'), runId: s('批次 id(该配方的已完结批次)') }, ['id', 'runId']),
|
|
358
|
+
run: async (args, state) => {
|
|
359
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/recipes/${args.id}/mark-good`, { runId: args.runId })
|
|
360
|
+
if (!r.ok) return fail(`标记失败 → HTTP ${r.status}: ${r.message}`)
|
|
361
|
+
return ok(r.data)
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
// ── DCW 写控制节点与参数映射 ──
|
|
366
|
+
{
|
|
367
|
+
name: 'aw_dcw_snapshot',
|
|
368
|
+
description: 'DCW 全量快照:controller/nodes/params/templates/drivers/recipes/runs/products/lines。查模板 ref 与现有拓扑的标准入口。',
|
|
369
|
+
inputSchema: obj(),
|
|
370
|
+
run: rest('GET', '/api/workshop/dcw'),
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
name: 'aw_dcw_create',
|
|
374
|
+
description: '创建写控制节点(PLC 执行面)。必填 templateRef(模板 ref,如 dcw-temp-sp);driver+driverConfig 必须取自模拟器 export 或真实设备规约,禁止手抄;建议显式传 lineId/unit/min/max/decimals/holdIntervalMs。节点创建即自动生成同名工艺参数映射。',
|
|
375
|
+
inputSchema: obj({
|
|
376
|
+
templateRef: s('模板 ref(dcw-<key>)'),
|
|
377
|
+
name: s('节点名'),
|
|
378
|
+
lineId: s('所属产线 id'),
|
|
379
|
+
driver: s('驱动:modbus-tcp | modbus-rtu | opcua | mqtt | http'),
|
|
380
|
+
driverConfig: s('驱动配置对象(取自设备 export)', 'object'),
|
|
381
|
+
unit: s('工程单位'), min: s('量程下限', 'number'), max: s('量程上限', 'number'), decimals: s('小数位(量化精度)', 'number'),
|
|
382
|
+
holdIntervalMs: s('保持周期 ms', 'number'), readIntervalMs: s('回读周期 ms', 'number'), writeLockSeconds: s('写锁秒数', 'number'), stepLimit: s('单步限幅', 'number'),
|
|
383
|
+
semantics: s('工艺语义文案'),
|
|
384
|
+
transform: s('量程变换 {kind,scale,offset}', 'object'),
|
|
385
|
+
}, ['templateRef']),
|
|
386
|
+
run: rest('POST', '/api/workshop/dcw'),
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'aw_dcw_write',
|
|
390
|
+
description: '节点直写(核心写命令,四层限界联锁生效,越界 400 并点名约束层)。{ id, value }。',
|
|
391
|
+
inputSchema: obj({ id: s('节点 id'), value: s('工程量纲目标值', 'number') }, ['id', 'value']),
|
|
392
|
+
run: async (args, state) => {
|
|
393
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/${args.id}/write`, { value: args.value })
|
|
394
|
+
if (!r.ok) return fail(`写入被拒 → HTTP ${r.status}: ${r.message}`)
|
|
395
|
+
return ok(r.data)
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: 'aw_dcw_read',
|
|
400
|
+
description: '读节点 PLC 实时值(被动观测免审批)。{ id }。',
|
|
401
|
+
inputSchema: obj({ id: s('节点 id') }, ['id']),
|
|
402
|
+
run: async (args, state) => {
|
|
403
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/${args.id}/read`, {})
|
|
404
|
+
if (!r.ok) return fail(`读失败 → HTTP ${r.status}: ${r.message}`)
|
|
405
|
+
return ok(r.data)
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: 'aw_param_list',
|
|
410
|
+
description: '工艺参数映射列表(语义面,不透出寄存器细节)。{ lineId?, limits? }(limits=1 附四层写入限界剖面)。',
|
|
411
|
+
inputSchema: obj({ lineId: s('按产线过滤'), limits: s('是否附限界剖面(1/0)') }),
|
|
412
|
+
run: async (args, state) => {
|
|
413
|
+
const r = await apiCall(state, 'GET', `/api/workshop/dcw/params${queryOf(args)}`, undefined)
|
|
414
|
+
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
415
|
+
return ok(r.data)
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: 'aw_param_write',
|
|
420
|
+
description: '参数映射标准写入口(工程量纲;四层限界联锁:参数基准 ∩ 产品限界 ∩ 配方窗 ∩ 节点量程)。{ id, value }。',
|
|
421
|
+
inputSchema: obj({ id: s('参数映射 id'), value: s('工程量纲目标值', 'number') }, ['id', 'value']),
|
|
422
|
+
run: async (args, state) => {
|
|
423
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/params/${args.id}/write`, { value: args.value })
|
|
424
|
+
if (!r.ok) return fail(`写入被拒 → HTTP ${r.status}: ${r.message}`)
|
|
425
|
+
return ok(r.data)
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
name: 'aw_param_read',
|
|
430
|
+
description: '读参数映射当前工程量纲值。{ id }。',
|
|
431
|
+
inputSchema: obj({ id: s('参数映射 id') }, ['id']),
|
|
432
|
+
run: async (args, state) => {
|
|
433
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/params/${args.id}/read`, {})
|
|
434
|
+
if (!r.ok) return fail(`读失败 → HTTP ${r.status}: ${r.message}`)
|
|
435
|
+
return ok(r.data)
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
|
|
439
|
+
// ── DAQ 数采 ──
|
|
440
|
+
{
|
|
441
|
+
name: 'aw_daq_create',
|
|
442
|
+
description: '创建数采节点(观测面;不下发)。{ templateRef, name?, lineId?, driver?, driverConfig?, unit?, min?, max?, decimals?, warnLow?, warnHigh?, intervalMs?, semantics? }。',
|
|
443
|
+
inputSchema: obj({
|
|
444
|
+
templateRef: s('数采模板 ref(如 daq-temp-tc)'),
|
|
445
|
+
name: s('节点名'), lineId: s('所属产线 id'),
|
|
446
|
+
driver: s('驱动'), driverConfig: s('驱动配置对象', 'object'),
|
|
447
|
+
unit: s('单位'), min: s('量程下限', 'number'), max: s('量程上限', 'number'), decimals: s('小数位', 'number'),
|
|
448
|
+
warnLow: s('低警线', 'number'), warnHigh: s('高警线', 'number'), intervalMs: s('采样周期 ms', 'number'),
|
|
449
|
+
semantics: s('语义标注(场景编译 target 词表推断依据,建议写明"…质量输出")'),
|
|
450
|
+
}, ['templateRef']),
|
|
451
|
+
run: rest('POST', '/api/workshop/daq'),
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: 'aw_daq_controller',
|
|
455
|
+
description: '数采采集总控。{ action: start|stop|pause|resume|config, defaultIntervalMs? }。重启实例后必须显式 start;未开产线不采样是设计。',
|
|
456
|
+
inputSchema: obj({ action: s('start | stop | pause | resume | config'), defaultIntervalMs: s('默认采样周期 ms', 'number') }, ['action']),
|
|
457
|
+
run: rest('POST', '/api/workshop/daq/controller'),
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
name: 'aw_daq_samples',
|
|
461
|
+
description: '数采时序历史(点序新→旧!)。{ id, bucketMs?, limit?, from?, to? }。取时间窗口前先按 at 升序整理。',
|
|
462
|
+
inputSchema: obj({ id: s('数采节点 id'), bucketMs: s('桶化毫秒', 'number'), limit: s('条数上限', 'number'), from: s('起始 ISO 时间'), to: s('结束 ISO 时间') }, ['id']),
|
|
463
|
+
run: async (args, state) => {
|
|
464
|
+
const { id, ...query } = args
|
|
465
|
+
const r = await apiCall(state, 'GET', `/api/workshop/daq/${id}/samples${queryOf(query)}`, undefined)
|
|
466
|
+
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
467
|
+
return ok(r.data)
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
// ── 场景优化 Channel 与闭环 ──
|
|
472
|
+
{
|
|
473
|
+
name: 'aw_channel_list',
|
|
474
|
+
description: '频道(Agent 团队)列表。',
|
|
475
|
+
inputSchema: obj(),
|
|
476
|
+
run: rest('GET', '/api/workshop/channels'),
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: 'aw_channel_create',
|
|
480
|
+
description: '直接创建频道。{ name, scenarioPrompt?, workspace? } → { channelId }。要建「优化闭环频道」优先用 aw_channel_template_instantiate。',
|
|
481
|
+
inputSchema: obj({ name: s('频道名'), scenarioPrompt: s('场景纪律 prompt'), workspace: s('工作目录') }, ['name']),
|
|
482
|
+
run: rest('POST', '/api/workshop/channels'),
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: 'aw_channel_template_list',
|
|
486
|
+
description: '频道模板列表(含内置 seed:chtpl-aml-optimization-default 工艺优化通道、chtpl-aml-training-default 训练通道等)。',
|
|
487
|
+
inputSchema: obj(),
|
|
488
|
+
run: rest('GET', '/api/workshop/channel-templates'),
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
name: 'aw_channel_template_instantiate',
|
|
492
|
+
description: '从模板实例化优化频道。{ id, name?, toolProfile?(\u0027aml_optimization\u0027等), controlPolicy?(\u0027recommendation_only\u0027|\u0027hitl_governed\u0027|\u0027bounded_auto\u0027), optimizationMode?(\u0027exploration\u0027|\u0027aml\u0027), boundModelId?, objective?, scene?, scenePackId?, providerId? } → { channelId, leadAgentId }。',
|
|
493
|
+
inputSchema: obj({
|
|
494
|
+
id: s('模板 id'),
|
|
495
|
+
name: s('新频道名'),
|
|
496
|
+
toolProfile: s('legacy | hybrid_twin | aml_training | aml_optimization'),
|
|
497
|
+
controlPolicy: s('recommendation_only | hitl_governed | bounded_auto'),
|
|
498
|
+
optimizationMode: s('exploration | aml'),
|
|
499
|
+
boundModelId: s('绑定 AML 模型 id(可选)'),
|
|
500
|
+
objective: s('优化目标对象', 'object'),
|
|
501
|
+
scene: s('场景对象 {sceneId,sceneVersion,lineId,productId,recipeId}', 'object'),
|
|
502
|
+
}, ['id']),
|
|
503
|
+
run: rest('POST', args => `/api/workshop/channel-templates/${args.id}/instantiate`, { bodyOf: args => omit(args, 'id') }),
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
name: 'aw_team_provision',
|
|
507
|
+
description: '组装频道剧组(组合工具):建 lead+worker 两个 agent → 建 team → 加成员 → deploy 到频道 → 返回频道内实例 agent 列表(绑定节点工具用实例 id)。',
|
|
508
|
+
inputSchema: obj({
|
|
509
|
+
channelId: s('目标频道 id'),
|
|
510
|
+
lead: s('lead 定义 {name, harness, config?}', 'object'),
|
|
511
|
+
worker: s('worker 定义 {name, harness, config?}', 'object'),
|
|
512
|
+
teamName: s('团队名(缺省自动)'),
|
|
513
|
+
}, ['channelId', 'lead', 'worker']),
|
|
514
|
+
run: async (args, state) => {
|
|
515
|
+
const mkAgent = async (def, role) => {
|
|
516
|
+
const r = await apiCall(state, 'POST', '/api/workshop/agents', { name: def.name, harness: def.harness ?? 'omp', config: def.config ?? {} })
|
|
517
|
+
if (!r.ok) throw new Error(`创建 ${role} agent 失败: HTTP ${r.status} ${r.message}`)
|
|
518
|
+
return r.data
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
const lead = await mkAgent(args.lead, 'lead')
|
|
522
|
+
const worker = await mkAgent(args.worker, 'worker')
|
|
523
|
+
const teamR = await apiCall(state, 'POST', '/api/workshop/teams', { name: args.teamName ?? `team-${Date.now()}` })
|
|
524
|
+
if (!teamR.ok) throw new Error(`建 team 失败: HTTP ${teamR.status} ${teamR.message}`)
|
|
525
|
+
const teamId = teamR.data?.id ?? teamR.data?.team?.id
|
|
526
|
+
for (const [agentId, role] of [[lead.id, 'lead'], [worker.id, 'worker']]) {
|
|
527
|
+
const m = await apiCall(state, 'POST', `/api/workshop/teams/${teamId}/members`, { agentId, role })
|
|
528
|
+
if (!m.ok) throw new Error(`加成员 ${role} 失败: HTTP ${m.status} ${m.message}`)
|
|
529
|
+
}
|
|
530
|
+
const d = await apiCall(state, 'POST', `/api/workshop/teams/${teamId}/deploy`, { channelId: args.channelId })
|
|
531
|
+
if (!d.ok) throw new Error(`deploy 失败: HTTP ${d.status} ${d.message}`)
|
|
532
|
+
const inst = await apiCall(state, 'GET', `/api/workshop/channels/${args.channelId}/agents`, undefined)
|
|
533
|
+
const instances = inst.data ?? []
|
|
534
|
+
return ok({
|
|
535
|
+
channelId: args.channelId,
|
|
536
|
+
teamId,
|
|
537
|
+
leadInstanceId: instances.find(a => a.role === 'lead')?.id ?? null,
|
|
538
|
+
workerInstanceId: instances.find(a => a.role === 'worker')?.id ?? null,
|
|
539
|
+
instances,
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
catch (err) {
|
|
543
|
+
return fail(err.message)
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: 'aw_agent_tool_bind',
|
|
549
|
+
description: '把节点授权给频道成员实例(Agent 工具鉴权基础)。{ agentId(实例 id,非模板 id), nodeId, kind: dcw|daq, mode: auto|manual }。',
|
|
550
|
+
inputSchema: obj({ agentId: s('频道成员实例 id'), nodeId: s('节点 id'), kind: s('dcw | daq'), mode: s('auto | manual') }, ['agentId', 'nodeId', 'kind']),
|
|
551
|
+
run: rest('POST', '/api/workshop/agent-tools/bindings'),
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: 'aw_agent_tool_invoke',
|
|
555
|
+
description: '以频道成员实例身份调用宿主工具(optimization_explore / mpc_optimize / twin_* / dcw_control / param_control / daq_query 等,面见 GET /api/workshop/agent-tools/list)。可能阻塞(HITL 审批)。',
|
|
556
|
+
inputSchema: obj({ agentId: s('实例 id'), tool: s('工具名'), args: s('工具参数对象', 'object') }, ['agentId', 'tool']),
|
|
557
|
+
timeoutMs: INVOKE_TIMEOUT_MS,
|
|
558
|
+
run: async (args, state) => {
|
|
559
|
+
const r = await apiCall(state, 'POST', '/api/workshop/agent-tools/invoke', { agentId: args.agentId, tool: args.tool, args: args.args ?? {} }, { timeoutMs: INVOKE_TIMEOUT_MS })
|
|
560
|
+
if (!r.ok) return fail(`工具调用失败(${args.tool}) → HTTP ${r.status}: ${r.message}`)
|
|
561
|
+
// 宿主工具的业务错误(HTTP 200 但 result.isError)必须透传为 MCP isError
|
|
562
|
+
const result = r.data?.result
|
|
563
|
+
if (result?.isError) return { content: [{ type: 'text', text: String(result.text ?? '') }], isError: true }
|
|
564
|
+
return ok(r.data)
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
name: 'aw_twin_profile_get',
|
|
569
|
+
description: '读频道孪生 profile(优化维度):toolProfile/optimizationMode(exploration|aml)/boundModelId/objective/controlPolicy;?agentId 附 promptPreview。',
|
|
570
|
+
inputSchema: obj({ channelId: s('频道 id'), agentId: s('可选:附该成员的 prompt 预览') }, ['channelId']),
|
|
571
|
+
run: async (args, state) => {
|
|
572
|
+
const { channelId, agentId } = args
|
|
573
|
+
const q = agentId ? `?agentId=${encodeURIComponent(agentId)}` : ''
|
|
574
|
+
const r = await apiCall(state, 'GET', `/api/workshop/channels/${channelId}/twin-profile${q}`, undefined)
|
|
575
|
+
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
576
|
+
return ok(r.data)
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
name: 'aw_twin_profile_patch',
|
|
581
|
+
description: '切换频道优化模式/绑定模型(闭环开关)。{ channelId, optimizationMode?, controlPolicy?, objective?, boundModelId? }。绑模型即自动 exploration→aml(fail-closed:谱系一致+门禁全过,否则 409);boundModelId=null 解绑自动回落 exploration。',
|
|
582
|
+
inputSchema: obj({
|
|
583
|
+
channelId: s('频道 id'),
|
|
584
|
+
optimizationMode: s('exploration | aml'),
|
|
585
|
+
controlPolicy: s('recommendation_only | hitl_governed | bounded_auto'),
|
|
586
|
+
objective: s('优化目标对象', 'object'),
|
|
587
|
+
boundModelId: s('绑定模型 id;null=解绑回落 exploration'),
|
|
588
|
+
}, ['channelId']),
|
|
589
|
+
run: rest('PATCH', args => `/api/workshop/channels/${args.channelId}/twin-profile`, { bodyOf: args => omit(args, 'channelId') }),
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
name: 'aw_task_create',
|
|
593
|
+
description: '向频道下达任务。{ channelId, title, parts: [{text}], mode: goal|..., modeConfig? } (goal 模式配 goalCriteria 验收判据)。',
|
|
594
|
+
inputSchema: obj({
|
|
595
|
+
channelId: s('频道 id'), title: s('任务标题'),
|
|
596
|
+
parts: s('任务书分段 [{text}]', 'array'),
|
|
597
|
+
mode: s('执行模式(如 goal)'),
|
|
598
|
+
modeConfig: s('模式配置,如 {goalCriteria}', 'object'),
|
|
599
|
+
}, ['channelId', 'title', 'parts']),
|
|
600
|
+
run: async (args, state) => {
|
|
601
|
+
const { channelId, ...body } = args
|
|
602
|
+
const r = await apiCall(state, 'POST', `/api/workshop/channels/${channelId}/tasks`, body)
|
|
603
|
+
if (!r.ok) return fail(`下达失败 → HTTP ${r.status}: ${r.message}`)
|
|
604
|
+
return ok(r.data)
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
name: 'aw_task_list',
|
|
609
|
+
description: '频道任务列表(轮询状态用)。{ channelId }。',
|
|
610
|
+
inputSchema: obj({ channelId: s('频道 id') }, ['channelId']),
|
|
611
|
+
run: async (args, state) => {
|
|
612
|
+
const r = await apiCall(state, 'GET', `/api/workshop/channels/${args.channelId}/tasks`, undefined)
|
|
613
|
+
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
614
|
+
return ok(r.data)
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
name: 'aw_model_list',
|
|
619
|
+
description: 'AML 模型列表(含 stage: candidate|shadow|production|retired 与门禁摘要)。{ lineId?, stage? } 等 query 透传。',
|
|
620
|
+
inputSchema: obj({ lineId: s('按产线过滤'), stage: s('按阶段过滤') }),
|
|
621
|
+
run: async (args, state) => {
|
|
622
|
+
const r = await apiCall(state, 'GET', `/api/workshop/aml/models${queryOf(args)}`, undefined)
|
|
623
|
+
if (!r.ok) return fail(`HTTP ${r.status}: ${r.message}`)
|
|
624
|
+
return ok(r.data)
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
name: 'aw_model_promote',
|
|
629
|
+
description: '模型阶段晋升(candidate→shadow→production;production 前做 ONNX 深检)。{ id, toStage }。',
|
|
630
|
+
inputSchema: obj({ id: s('模型 id'), toStage: s('目标阶段 shadow | production') }, ['id', 'toStage']),
|
|
631
|
+
run: async (args, state) => {
|
|
632
|
+
const r = await apiCall(state, 'POST', `/api/workshop/aml/models/${args.id}/promote`, { toStage: args.toStage })
|
|
633
|
+
if (!r.ok) return fail(`晋升被拒 → HTTP ${r.status}: ${r.message}`)
|
|
634
|
+
return ok(r.data)
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: 'aw_optimization_judge',
|
|
639
|
+
description: '优化 step 落判定(判定与执行分离)。{ id, verdict: keep|rollback|uncertain, reason }。',
|
|
640
|
+
inputSchema: obj({ id: s('优化记录 id'), verdict: s('keep | rollback | uncertain'), reason: s('判定理由') }, ['id', 'verdict', 'reason']),
|
|
641
|
+
run: async (args, state) => {
|
|
642
|
+
const { id, ...body } = args
|
|
643
|
+
const r = await apiCall(state, 'POST', `/api/workshop/dcw/optimizations/${id}/judge`, body)
|
|
644
|
+
if (!r.ok) return fail(`判定失败 → HTTP ${r.status}: ${r.message}`)
|
|
645
|
+
return ok(r.data)
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
// ── 插件 ──
|
|
650
|
+
{
|
|
651
|
+
name: 'aw_plugin_list',
|
|
652
|
+
description: '插件管理清单(含启停状态/路由/装载失败 failures)。热重载验证入口。',
|
|
653
|
+
inputSchema: obj(),
|
|
654
|
+
run: rest('GET', '/api/workshop/plugins'),
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
name: 'aw_plugin_toggle',
|
|
658
|
+
description: '启用/停用插件(写 plugins-state.json,宿主 ~1s 热重载,无需重启)。{ name, enabled }。',
|
|
659
|
+
inputSchema: obj({ name: s('插件名'), enabled: s('true=启用 false=停用', 'boolean') }, ['name', 'enabled']),
|
|
660
|
+
run: async (args, state) => {
|
|
661
|
+
const action = args.enabled ? 'enable' : 'disable'
|
|
662
|
+
const r = await apiCall(state, 'POST', `/api/workshop/plugins/${encodeURIComponent(args.name)}/${action}`, {})
|
|
663
|
+
if (!r.ok) return fail(`${action} 失败 → HTTP ${r.status}: ${r.message}`)
|
|
664
|
+
return ok(r.data)
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
|
|
668
|
+
// ── 逃生舱 ──
|
|
669
|
+
{
|
|
670
|
+
name: 'aw_request',
|
|
671
|
+
description: '通用平台 REST 调用(逃生舱:覆盖工具面未覆盖的端点,如 aml/datasets、aml/jobs、optimizations 查询)。path 必须以 /api/ 开头。',
|
|
672
|
+
inputSchema: obj({
|
|
673
|
+
method: s('GET | POST | PATCH | PUT | DELETE'),
|
|
674
|
+
path: s('路径,必须 /api/ 开头,可含 query'),
|
|
675
|
+
body: s('请求体对象', 'object'),
|
|
676
|
+
auth: s('是否携带鉴权(缺省 true)', 'boolean'),
|
|
677
|
+
}, ['method', 'path']),
|
|
678
|
+
run: async (args, state) => {
|
|
679
|
+
const path = String(args.path ?? '')
|
|
680
|
+
if (!path.startsWith('/api/')) return fail('path 必须以 /api/ 开头(本工具只访问平台 API,不代理任意外网)。')
|
|
681
|
+
if (!['GET', 'POST', 'PATCH', 'PUT', 'DELETE'].includes(String(args.method).toUpperCase())) return fail(`不支持的 method: ${args.method}`)
|
|
682
|
+
const r = await apiCall(state, String(args.method).toUpperCase(), path, args.body, { auth: args.auth !== false })
|
|
683
|
+
if (!r.ok) return fail(`${args.method} ${path} → HTTP ${r.status}: ${r.message}`)
|
|
684
|
+
return ok(r.data)
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
]
|
|
688
|
+
return defs
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
let _defs = null
|
|
692
|
+
/** 工具面(模块级缓存;测试可直接读) */
|
|
693
|
+
export function TOOL_DEFS() {
|
|
694
|
+
if (!_defs) _defs = buildToolDefs()
|
|
695
|
+
return _defs
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// ── JSON-RPC(MCP stdio,逐行)──
|
|
699
|
+
|
|
700
|
+
/** 处理一条 JSON-RPC 消息;通知返回 null */
|
|
701
|
+
export async function handleMessage(state, msg) {
|
|
702
|
+
const { id, method, params } = msg ?? {}
|
|
703
|
+
const isRequest = id !== undefined && id !== null
|
|
704
|
+
const respond = result => (isRequest ? { jsonrpc: '2.0', id, result } : null)
|
|
705
|
+
const respondError = (code, message) => (isRequest ? { jsonrpc: '2.0', id, error: { code, message } } : null)
|
|
706
|
+
|
|
707
|
+
try {
|
|
708
|
+
switch (method) {
|
|
709
|
+
case 'initialize':
|
|
710
|
+
return respond({
|
|
711
|
+
protocolVersion: params?.protocolVersion ?? '2024-11-05',
|
|
712
|
+
capabilities: { tools: { listChanged: false } },
|
|
713
|
+
serverInfo: { name: 'aw-industrial-mcp', title: 'AgentWorkShop industrial MCP', version: '1.0.0' },
|
|
714
|
+
})
|
|
715
|
+
case 'notifications/initialized':
|
|
716
|
+
case 'initialized':
|
|
717
|
+
return null
|
|
718
|
+
case 'ping':
|
|
719
|
+
return respond({})
|
|
720
|
+
case 'tools/list':
|
|
721
|
+
return respond({
|
|
722
|
+
tools: TOOL_DEFS().map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
|
|
723
|
+
})
|
|
724
|
+
case 'tools/call': {
|
|
725
|
+
const name = params?.name
|
|
726
|
+
if (!name) return respondError(-32602, 'tools/call 缺少 name')
|
|
727
|
+
// 开关门禁:停用时仅放行 aw_status(自诊断),其余工具一律拒绝
|
|
728
|
+
if (name !== 'aw_status' && !isMcpEnabled(state)) {
|
|
729
|
+
return respond({ content: [{ type: 'text', text: MCP_DISABLED_TEXT }], isError: true })
|
|
730
|
+
}
|
|
731
|
+
const def = TOOL_DEFS().find(t => t.name === name)
|
|
732
|
+
if (!def) return respondError(-32602, `未知工具: ${name}`)
|
|
733
|
+
try {
|
|
734
|
+
const result = await def.run(params?.arguments ?? {}, state)
|
|
735
|
+
return respond(result)
|
|
736
|
+
}
|
|
737
|
+
catch (err) {
|
|
738
|
+
// 工具执行异常按 MCP 约定返回 isError 结果(不中断回合)
|
|
739
|
+
return respond(fail(`工具执行异常(${name}): ${err?.message ?? err}`))
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
default:
|
|
743
|
+
return respondError(-32601, `方法不存在: ${method}`)
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
catch (err) {
|
|
747
|
+
return respondError(-32603, err?.message ?? String(err))
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function write(msg) {
|
|
752
|
+
process.stdout.write(JSON.stringify(msg) + '\n')
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* MCP 集成开关(系统设置 → MCP 集成 → 启动 MCP 集成;mcp.enabled,live 键)。
|
|
757
|
+
* 3s TTL 缓存:设置页切换后 ≤3s 传导到本服务,无需重启。
|
|
758
|
+
*/
|
|
759
|
+
const MCP_DISABLED_TEXT = 'MCP 集成已停用:请到平台「系统设置 → MCP 集成」打开「启动 MCP 集成」,或为 MCP 进程设置环境变量 AW_MCP_ENABLED=true。'
|
|
760
|
+
export function isMcpEnabled(state) {
|
|
761
|
+
const now = Date.now()
|
|
762
|
+
if (!state._mcpEnabledCache || now - state._mcpEnabledCache.at > 3000) {
|
|
763
|
+
state._mcpEnabledCache = { at: now, value: readMcpEnabled({ cwd: state.cwd, env: state.env }) }
|
|
764
|
+
}
|
|
765
|
+
return state._mcpEnabledCache.value
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/** stdio 主循环(直接执行本文件时启动) */
|
|
769
|
+
export function runStdio() {
|
|
770
|
+
const state = createState()
|
|
771
|
+
const rl = createInterface({ input: process.stdin })
|
|
772
|
+
rl.on('line', (line) => {
|
|
773
|
+
const trimmed = line.trim()
|
|
774
|
+
if (!trimmed) return
|
|
775
|
+
let msg
|
|
776
|
+
try {
|
|
777
|
+
msg = JSON.parse(trimmed)
|
|
778
|
+
}
|
|
779
|
+
catch {
|
|
780
|
+
return // 非 JSON 行忽略(防御宿主 stderr 混流)
|
|
781
|
+
}
|
|
782
|
+
handleMessage(state, msg)
|
|
783
|
+
.then((out) => {
|
|
784
|
+
if (out) write(out)
|
|
785
|
+
})
|
|
786
|
+
.catch((err) => {
|
|
787
|
+
write({ jsonrpc: '2.0', id: msg?.id ?? null, error: { code: -32603, message: err?.message ?? String(err) } })
|
|
788
|
+
})
|
|
789
|
+
})
|
|
790
|
+
process.stdin.on('end', () => process.exit(0))
|
|
791
|
+
process.stdout.on('error', () => process.exit(0))
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// argv 路径必须先 realpath 再对比:nvm-windows 全局安装经目录 junction(C:\nvm4w\nodejs → 实际版本目录),
|
|
795
|
+
// import.meta.url 是 realpath 而 argv 不是 —— 直接字符串对比恒 false,全局用户的 MCP 直连会静默退出(0.7.58 实测)。
|
|
796
|
+
const isMain = process.argv[1] && import.meta.url === pathToFileURL(realpathSync(resolve(process.argv[1]))).href
|
|
797
|
+
if (isMain) {
|
|
798
|
+
runStdio()
|
|
799
|
+
}
|