agentworkshop 0.8.1 → 0.8.3
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/.output/.AgentWorkShop/aml-python/train-example.py +92 -92
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{BCLUwdP52.js → 39W1CH-l2.js} +1 -1
- package/.output/public/_nuxt/7xbYPfY6.js +1 -0
- package/.output/public/_nuxt/{BfvuWqSa.js → ATAvN55g.js} +1 -1
- package/.output/public/_nuxt/{B2NbzUau2.js → B3eCVqcT2.js} +1 -1
- package/.output/public/_nuxt/{DuUc4bbY2.js → B9qIhT7k2.js} +1 -1
- package/.output/public/_nuxt/{BkEokGX5.js → BE9oKpOt.js} +1 -1
- package/.output/public/_nuxt/{D_59BNP1.js → Bbk1XCCM.js} +3 -3
- package/.output/public/_nuxt/{Dz9C2pTa.js → CMzExk8F.js} +1 -1
- package/.output/public/_nuxt/{dHwWyr6b.js → CT4bi_Y4.js} +1 -1
- package/.output/public/_nuxt/{26OL4Ao82.js → CeCSjB9O2.js} +1 -1
- package/.output/public/_nuxt/{Dyxf2xZw.js → Cot5VNae.js} +1 -1
- package/.output/public/_nuxt/{BVg2R2VB2.js → Crf8_Ad02.js} +1 -1
- package/.output/public/_nuxt/{rDvMijKA.js → D9tNM-1a.js} +2 -2
- package/.output/public/_nuxt/{YAA98rWJ.js → DAug2CPf.js} +7 -7
- package/.output/public/_nuxt/{CQSxS_Ek.js → DbhTN7-T.js} +1 -1
- package/.output/public/_nuxt/DgGzwjAr2.js +2 -0
- package/.output/public/_nuxt/DkRozlDP2.js +1 -0
- package/.output/public/_nuxt/{DGu_fna7.js → DoChWkff.js} +1 -1
- package/.output/public/_nuxt/{Byiiiwc72.js → DxH5focQ2.js} +1 -1
- package/.output/public/_nuxt/Dyr3LPGL.js +1 -0
- package/.output/public/_nuxt/{CC2Pv8VL2.js → MC_i34H42.js} +1 -1
- package/.output/public/_nuxt/OmpTerminalPanel.DMWgGwDs.css +1 -0
- package/.output/public/_nuxt/PluginSlot.BqBzLD4T.css +1 -0
- package/.output/public/_nuxt/TownView.Cn8SPG7S.css +1 -0
- package/.output/public/_nuxt/{CmuGMxDP.js → TugRY_4O.js} +1 -1
- package/.output/public/_nuxt/{BPUQZ9Lq2.js → WybiLTow2.js} +1 -1
- package/.output/public/_nuxt/{BaYDgyaR.js → Zn0BZpf0.js} +3 -3
- package/.output/public/_nuxt/{_id_.BQRWK7J2.css → _id_.Be5JaH-P.css} +1 -1
- package/.output/public/_nuxt/{_wsId_.DCnKQQdY.css → _wsId_.p_vAYtTp.css} +1 -1
- package/.output/public/_nuxt/{agents.D-JPovTZ.css → agents.CRomddYR.css} +1 -1
- package/.output/public/_nuxt/aml.DgOMpDpv.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/fd0033e4-1eaa-42f8-9a04-3132717a703b.json +1 -0
- package/.output/public/_nuxt/{default.BEXGWZFQ.css → default.CpAx70gC.css} +1 -1
- package/.output/public/_nuxt/{logs.BkJPbR_K.css → logs.DcSvz6vi.css} +1 -1
- package/.output/public/_nuxt/{monitor.D3yEqqrv.css → monitor.OoNTwfMJ.css} +1 -1
- package/.output/public/_nuxt/{ontology.B_3Wn907.css → ontology.JqJ6PjV1.css} +1 -1
- package/.output/public/_nuxt/{operations.-l6u44Ar.css → operations.C48ggFa5.css} +1 -1
- package/.output/public/_nuxt/{pages.Cdpm_B-G.css → pages.Dpa28tJW.css} +1 -1
- package/.output/public/_nuxt/permissions.CrV-4t-6.css +1 -0
- package/.output/public/_nuxt/{BfeXib9G.js → rcdmiszh2.js} +1 -1
- package/.output/public/_nuxt/schedules.DYWOjGHm.css +1 -0
- package/.output/public/_nuxt/{teams.CLQaXno5.css → teams.DEJoJ4QV.css} +1 -1
- package/.output/public/_nuxt/{tokens.BDSUWF-L.css → tokens.DKZWCP5v.css} +1 -1
- package/.output/public/_nuxt/tools.DP-nsTli.css +1 -0
- package/.output/public/_nuxt/wK6Pp4Zc2.js +2 -0
- package/.output/public/_nuxt/{workshop.BBiFhXuM.css → workshop.PT1xhP8C.css} +1 -1
- package/.output/public/favicon.svg +14 -14
- package/.output/public/robots.txt +3 -3
- package/.output/server/chunks/_/LeftOutlined.mjs.map +1 -1
- package/.output/server/chunks/_/RightOutlined.mjs.map +1 -1
- package/.output/server/chunks/_/SearchOutlined.mjs +1230 -1261
- package/.output/server/chunks/_/SearchOutlined.mjs.map +1 -1
- package/.output/server/chunks/_/error-500.mjs.map +1 -1
- package/.output/server/chunks/_/nitro.mjs +3308 -3389
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/AuthGate-G74Wr5IV.mjs +704 -0
- package/.output/server/chunks/build/AuthGate-G74Wr5IV.mjs.map +1 -0
- package/.output/server/chunks/build/FormItemContext-DmuQqaeJ.mjs +77 -0
- package/.output/server/chunks/build/FormItemContext-DmuQqaeJ.mjs.map +1 -0
- package/.output/server/chunks/build/OmpTerminalPanel-5cB-78zp.mjs +889 -0
- package/.output/server/chunks/build/OmpTerminalPanel-5cB-78zp.mjs.map +1 -0
- package/.output/server/chunks/build/PluginSlot-CZlGlRoR.mjs +131 -0
- package/.output/server/chunks/build/PluginSlot-CZlGlRoR.mjs.map +1 -0
- package/.output/server/chunks/build/TownView-CUbzOT_M.mjs +5960 -0
- package/.output/server/chunks/build/TownView-CUbzOT_M.mjs.map +1 -0
- package/.output/server/chunks/build/_id_-BHWZiRQZ.mjs +373 -0
- package/.output/server/chunks/build/_id_-BHWZiRQZ.mjs.map +1 -0
- package/.output/server/chunks/build/_id_-JaGx0yBE.mjs +1922 -0
- package/.output/server/chunks/build/_id_-JaGx0yBE.mjs.map +1 -0
- package/.output/server/chunks/build/_wsId_-C4DKkEqv.mjs +13707 -0
- package/.output/server/chunks/build/_wsId_-C4DKkEqv.mjs.map +1 -0
- package/.output/server/chunks/build/agents-DRod4lTC.mjs +1047 -0
- package/.output/server/chunks/build/agents-DRod4lTC.mjs.map +1 -0
- package/.output/server/chunks/build/alert-CNGhs--Y.mjs +260 -0
- package/.output/server/chunks/build/alert-CNGhs--Y.mjs.map +1 -0
- package/.output/server/chunks/build/aml-B3Umz4DR.mjs +729 -0
- package/.output/server/chunks/build/aml-B3Umz4DR.mjs.map +1 -0
- package/.output/server/chunks/build/analysis-tNpFLtMt.mjs +502 -0
- package/.output/server/chunks/build/analysis-tNpFLtMt.mjs.map +1 -0
- package/.output/server/chunks/build/{api-B4Om0mEc.mjs → api-D-zkgkj-.mjs} +8 -10
- package/.output/server/chunks/build/api-D-zkgkj-.mjs.map +1 -0
- package/.output/server/chunks/build/{api-BsAgXWxi.mjs → api-D3gy2e2q.mjs} +22 -24
- package/.output/server/chunks/build/api-D3gy2e2q.mjs.map +1 -0
- package/.output/server/chunks/build/{api-BEW_ebY_.mjs → api-bn4OKkl_.mjs} +17 -19
- package/.output/server/chunks/build/api-bn4OKkl_.mjs.map +1 -0
- package/.output/server/chunks/build/api-error-CnW_xiNI.mjs +46 -0
- package/.output/server/chunks/build/api-error-CnW_xiNI.mjs.map +1 -0
- package/.output/server/chunks/build/apiClient-DIXx9sfD.mjs +70 -0
- package/.output/server/chunks/build/apiClient-DIXx9sfD.mjs.map +1 -0
- package/.output/server/chunks/build/board-B2oYXGBy.mjs +172 -0
- package/.output/server/chunks/build/board-B2oYXGBy.mjs.map +1 -0
- package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs +81 -94
- package/.output/server/chunks/build/builtin-catalog-edpDcgfL.mjs.map +1 -1
- package/.output/server/chunks/build/card-BECE6y_G.mjs +922 -0
- package/.output/server/chunks/build/card-BECE6y_G.mjs.map +1 -0
- package/.output/server/chunks/build/channel-templates-R2TInv8B.mjs +950 -0
- package/.output/server/chunks/build/channel-templates-R2TInv8B.mjs.map +1 -0
- package/.output/server/chunks/build/chart-trend-BWeZh5Lv.mjs +113 -112
- package/.output/server/chunks/build/chart-trend-BWeZh5Lv.mjs.map +1 -1
- package/.output/server/chunks/build/checkbox-C6xY5uc6.mjs +429 -0
- package/.output/server/chunks/build/checkbox-C6xY5uc6.mjs.map +1 -0
- package/.output/server/chunks/build/client-only-S7k53PIm.mjs +42 -0
- package/.output/server/chunks/build/client-only-S7k53PIm.mjs.map +1 -0
- package/.output/server/chunks/build/constants-HPHJvjne.mjs +65 -75
- package/.output/server/chunks/build/constants-HPHJvjne.mjs.map +1 -1
- package/.output/server/chunks/build/core-CRlsQhzd.mjs +292 -0
- package/.output/server/chunks/build/core-CRlsQhzd.mjs.map +1 -0
- package/.output/server/chunks/build/daq-DHsYtWC-.mjs +1210 -0
- package/.output/server/chunks/build/daq-DHsYtWC-.mjs.map +1 -0
- package/.output/server/chunks/build/data-Cl4wRZO6.mjs +459 -0
- package/.output/server/chunks/build/data-Cl4wRZO6.mjs.map +1 -0
- package/.output/server/chunks/build/dcw-DnjOM6p1.mjs +467 -0
- package/.output/server/chunks/build/dcw-DnjOM6p1.mjs.map +1 -0
- package/.output/server/chunks/build/dcw-driver-badge-Df5wL_ZA.mjs +3 -15
- package/.output/server/chunks/build/dcw-driver-badge-Df5wL_ZA.mjs.map +1 -1
- package/.output/server/chunks/build/default-DDRKP2Mb.mjs +2246 -0
- package/.output/server/chunks/build/default-DDRKP2Mb.mjs.map +1 -0
- package/.output/server/chunks/build/descriptions-B3uhDesi.mjs +401 -0
- package/.output/server/chunks/build/descriptions-B3uhDesi.mjs.map +1 -0
- package/.output/server/chunks/build/drawer-DwXxapMg.mjs +822 -0
- package/.output/server/chunks/build/drawer-DwXxapMg.mjs.map +1 -0
- package/.output/server/chunks/build/dropdown-DLrGy-Fs.mjs +12 -0
- package/.output/server/chunks/build/dropdown-DLrGy-Fs.mjs.map +1 -0
- package/.output/server/chunks/build/dropdown-nsGv8Gqx.mjs +530 -0
- package/.output/server/chunks/build/dropdown-nsGv8Gqx.mjs.map +1 -0
- package/.output/server/chunks/build/{es-Bxu10NJ2.mjs → es-DXSEkmAm.mjs} +1 -2
- package/.output/server/chunks/build/es-DXSEkmAm.mjs.map +1 -0
- package/.output/server/chunks/build/firstNotUndefined-laiThpLD.mjs +2082 -0
- package/.output/server/chunks/build/firstNotUndefined-laiThpLD.mjs.map +1 -0
- package/.output/server/chunks/build/form-Cw2Ex9w5.mjs +3543 -0
- package/.output/server/chunks/build/form-Cw2Ex9w5.mjs.map +1 -0
- package/.output/server/chunks/build/hasIn-RYRGomyg.mjs +192 -0
- package/.output/server/chunks/build/hasIn-RYRGomyg.mjs.map +1 -0
- package/.output/server/chunks/build/hitl-B4cHRlkC.mjs +120 -0
- package/.output/server/chunks/build/hitl-B4cHRlkC.mjs.map +1 -0
- package/.output/server/chunks/build/input-CJgB-RL6.mjs +1705 -0
- package/.output/server/chunks/build/input-CJgB-RL6.mjs.map +1 -0
- package/.output/server/chunks/build/input-number-CQKL42p_.mjs +1027 -0
- package/.output/server/chunks/build/input-number-CQKL42p_.mjs.map +1 -0
- package/.output/server/chunks/build/interval-DiAUqODq.mjs +8 -0
- package/.output/server/chunks/build/interval-DiAUqODq.mjs.map +1 -0
- package/.output/server/chunks/build/isNumeric-DQiJ-b15.mjs +6 -0
- package/.output/server/chunks/build/isNumeric-DQiJ-b15.mjs.map +1 -0
- package/.output/server/chunks/build/logs-C4vKFkHQ.mjs +381 -0
- package/.output/server/chunks/build/logs-C4vKFkHQ.mjs.map +1 -0
- package/.output/server/chunks/build/menu-xO8ocVtL.mjs +1833 -0
- package/.output/server/chunks/build/menu-xO8ocVtL.mjs.map +1 -0
- package/.output/server/chunks/build/monitor-BNrG6v3b.mjs +2216 -0
- package/.output/server/chunks/build/monitor-BNrG6v3b.mjs.map +1 -0
- package/.output/server/chunks/build/narrow-fetch-CcG-BPxc.mjs +0 -2
- package/.output/server/chunks/build/narrow-fetch-CcG-BPxc.mjs.map +1 -1
- package/.output/server/chunks/build/ontology-B_0Vm6j-.mjs +1416 -0
- package/.output/server/chunks/build/ontology-B_0Vm6j-.mjs.map +1 -0
- package/.output/server/chunks/build/operations-D8D6-Jm-.mjs +278 -0
- package/.output/server/chunks/build/operations-D8D6-Jm-.mjs.map +1 -0
- package/.output/server/chunks/build/optimizer-DaTyaY3c.mjs +202 -0
- package/.output/server/chunks/build/optimizer-DaTyaY3c.mjs.map +1 -0
- package/.output/server/chunks/build/pages-Dac4icQC.mjs +439 -0
- package/.output/server/chunks/build/pages-Dac4icQC.mjs.map +1 -0
- package/.output/server/chunks/build/pagination-BTzq7dD5.mjs +1330 -0
- package/.output/server/chunks/build/pagination-BTzq7dD5.mjs.map +1 -0
- package/.output/server/chunks/build/permissions-qmimwB0M.mjs +649 -0
- package/.output/server/chunks/build/permissions-qmimwB0M.mjs.map +1 -0
- package/.output/server/chunks/build/plugins-Dh_goy-k.mjs +269 -0
- package/.output/server/chunks/build/plugins-Dh_goy-k.mjs.map +1 -0
- package/.output/server/chunks/build/popconfirm-DuGF7ty-.mjs +333 -0
- package/.output/server/chunks/build/popconfirm-DuGF7ty-.mjs.map +1 -0
- package/.output/server/chunks/build/responsiveObserve-CzRpNXkr.mjs +77 -0
- package/.output/server/chunks/build/responsiveObserve-CzRpNXkr.mjs.map +1 -0
- package/.output/server/chunks/build/schedules-C2_x38gj.mjs +7039 -0
- package/.output/server/chunks/build/schedules-C2_x38gj.mjs.map +1 -0
- package/.output/server/chunks/build/segmented-29uEKZ3D.mjs +332 -0
- package/.output/server/chunks/build/segmented-29uEKZ3D.mjs.map +1 -0
- package/.output/server/chunks/build/select-CJb2XrQf.mjs +3734 -0
- package/.output/server/chunks/build/select-CJb2XrQf.mjs.map +1 -0
- package/.output/server/chunks/build/settings-Cz2Plds0.mjs +944 -0
- package/.output/server/chunks/build/settings-Cz2Plds0.mjs.map +1 -0
- package/.output/server/chunks/build/space-BByD-mFE.mjs +107 -0
- package/.output/server/chunks/build/space-BByD-mFE.mjs.map +1 -0
- package/.output/server/chunks/build/spin-XhbgGeLP.mjs +313 -0
- package/.output/server/chunks/build/spin-XhbgGeLP.mjs.map +1 -0
- package/.output/server/chunks/build/studio-CwYXl00G.mjs +329 -0
- package/.output/server/chunks/build/studio-CwYXl00G.mjs.map +1 -0
- package/.output/server/chunks/build/switch-DWcwVFxW.mjs +374 -0
- package/.output/server/chunks/build/switch-DWcwVFxW.mjs.map +1 -0
- package/.output/server/chunks/build/table-Zhuo7Fq-.mjs +7249 -0
- package/.output/server/chunks/build/table-Zhuo7Fq-.mjs.map +1 -0
- package/.output/server/chunks/build/tabs-BCwBXjNt.mjs +1838 -0
- package/.output/server/chunks/build/tabs-BCwBXjNt.mjs.map +1 -0
- package/.output/server/chunks/build/tag-CRW-anwy.mjs +1010 -0
- package/.output/server/chunks/build/tag-CRW-anwy.mjs.map +1 -0
- package/.output/server/chunks/build/teams-CwhFdcYn.mjs +1160 -0
- package/.output/server/chunks/build/teams-CwhFdcYn.mjs.map +1 -0
- package/.output/server/chunks/build/tokens-C5ODO1HA.mjs +992 -0
- package/.output/server/chunks/build/tokens-C5ODO1HA.mjs.map +1 -0
- package/.output/server/chunks/build/tools-C0YKragH.mjs +487 -0
- package/.output/server/chunks/build/tools-C0YKragH.mjs.map +1 -0
- package/.output/server/chunks/build/tooltip-GlMrUZzx.mjs +888 -0
- package/.output/server/chunks/build/tooltip-GlMrUZzx.mjs.map +1 -0
- package/.output/server/chunks/build/town-BV-hbwb5.mjs +16 -29
- package/.output/server/chunks/build/town-BV-hbwb5.mjs.map +1 -1
- package/.output/server/chunks/build/town-LkDwWnKA.mjs +117 -0
- package/.output/server/chunks/build/town-LkDwWnKA.mjs.map +1 -0
- package/.output/server/chunks/build/types-D6J7slEa.mjs.map +1 -1
- package/.output/server/chunks/build/types-DWqIeMRP.mjs +28 -29
- package/.output/server/chunks/build/types-DWqIeMRP.mjs.map +1 -1
- package/.output/server/chunks/build/useAgentHarnessOptions-CYKuLtZ2.mjs +120 -0
- package/.output/server/chunks/build/useAgentHarnessOptions-CYKuLtZ2.mjs.map +1 -0
- package/.output/server/chunks/build/useAgentTemplateActions-Da_V1LSD.mjs +71 -0
- package/.output/server/chunks/build/useAgentTemplateActions-Da_V1LSD.mjs.map +1 -0
- package/.output/server/chunks/build/useAgentTemplateDialogState-CZzpu5og.mjs +16 -23
- package/.output/server/chunks/build/useAgentTemplateDialogState-CZzpu5og.mjs.map +1 -1
- package/.output/server/chunks/build/useAgentTemplateForm-BWvax5_E.mjs +112 -0
- package/.output/server/chunks/build/useAgentTemplateForm-BWvax5_E.mjs.map +1 -0
- package/.output/server/chunks/build/useAgentTemplatesCatalog-BnYjZy59.mjs +140 -0
- package/.output/server/chunks/build/useAgentTemplatesCatalog-BnYjZy59.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlDatasets-nGi10DAl.mjs +88 -0
- package/.output/server/chunks/build/useAmlDatasets-nGi10DAl.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlEntities-CTgNAgcZ.mjs +79 -0
- package/.output/server/chunks/build/useAmlEntities-CTgNAgcZ.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlEnv-Da2TVfyK.mjs +188 -0
- package/.output/server/chunks/build/useAmlEnv-Da2TVfyK.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlExperiments-U9AQ52RI.mjs +86 -0
- package/.output/server/chunks/build/useAmlExperiments-U9AQ52RI.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlFormat-C2sl3MGB.mjs +32 -37
- package/.output/server/chunks/build/useAmlFormat-C2sl3MGB.mjs.map +1 -1
- package/.output/server/chunks/build/useAmlJobs-BM1mu5iH.mjs +251 -0
- package/.output/server/chunks/build/useAmlJobs-BM1mu5iH.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlModels-C1DwIOMi.mjs +90 -0
- package/.output/server/chunks/build/useAmlModels-C1DwIOMi.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlNotes-CNm47QdS.mjs +80 -0
- package/.output/server/chunks/build/useAmlNotes-CNm47QdS.mjs.map +1 -0
- package/.output/server/chunks/build/useAmlPredict-m8I1AdRn.mjs +126 -0
- package/.output/server/chunks/build/useAmlPredict-m8I1AdRn.mjs.map +1 -0
- package/.output/server/chunks/build/useAnalysisFeed-CpbOaiBD.mjs +120 -0
- package/.output/server/chunks/build/useAnalysisFeed-CpbOaiBD.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqAddNode-CRorxgOY.mjs +141 -0
- package/.output/server/chunks/build/useDaqAddNode-CRorxgOY.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqDetailBinding-Ck-YUW9J.mjs +72 -0
- package/.output/server/chunks/build/useDaqDetailBinding-Ck-YUW9J.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqDetailFrames-CLMoreaz.mjs +78 -0
- package/.output/server/chunks/build/useDaqDetailFrames-CLMoreaz.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqDetailHistory-BVNQXwC_.mjs +166 -0
- package/.output/server/chunks/build/useDaqDetailHistory-BVNQXwC_.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqDetailNode-BC0e9OfX.mjs +103 -0
- package/.output/server/chunks/build/useDaqDetailNode-BC0e9OfX.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqDetailParams-D173iwFg.mjs +157 -0
- package/.output/server/chunks/build/useDaqDetailParams-D173iwFg.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqFilters-CC-o_21d.mjs +224 -0
- package/.output/server/chunks/build/useDaqFilters-CC-o_21d.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqNodeWindow-1DWj2H5G.mjs +43 -44
- package/.output/server/chunks/build/useDaqNodeWindow-1DWj2H5G.mjs.map +1 -1
- package/.output/server/chunks/build/useDaqOptimizations-4vK2w1yn.mjs +132 -0
- package/.output/server/chunks/build/useDaqOptimizations-4vK2w1yn.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqStream-BHKpSJFd.mjs +385 -0
- package/.output/server/chunks/build/useDaqStream-BHKpSJFd.mjs.map +1 -0
- package/.output/server/chunks/build/useDaqTemplates-CsXSc7yI.mjs +181 -0
- package/.output/server/chunks/build/useDaqTemplates-CsXSc7yI.mjs.map +1 -0
- package/.output/server/chunks/build/useDashboardCharts-D45083s_.mjs +312 -310
- package/.output/server/chunks/build/useDashboardCharts-D45083s_.mjs.map +1 -1
- package/.output/server/chunks/build/useDashboardData-DuKjuJTz.mjs +139 -0
- package/.output/server/chunks/build/useDashboardData-DuKjuJTz.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwAddNode-C5DcxsHF.mjs +217 -0
- package/.output/server/chunks/build/useDcwAddNode-C5DcxsHF.mjs.map +1 -0
- package/.output/server/chunks/build/{useDcwCards-SaiImIYQ.mjs → useDcwCards-0bAVo6uB.mjs} +20 -23
- package/.output/server/chunks/build/useDcwCards-0bAVo6uB.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwDetailScope-BNFOlt2v.mjs +184 -0
- package/.output/server/chunks/build/useDcwDetailScope-BNFOlt2v.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwDetailTemplates-DSH-o0tE.mjs +143 -0
- package/.output/server/chunks/build/useDcwDetailTemplates-DSH-o0tE.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwFilter-W61BFUcT.mjs +65 -0
- package/.output/server/chunks/build/useDcwFilter-W61BFUcT.mjs.map +1 -0
- package/.output/server/chunks/build/{useDcwLedger-0ie-0WTc.mjs → useDcwLedger-DsColLdc.mjs} +29 -35
- package/.output/server/chunks/build/useDcwLedger-DsColLdc.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwLineMutations-Cx4Ir7g3.mjs +133 -0
- package/.output/server/chunks/build/useDcwLineMutations-Cx4Ir7g3.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwLineRun-DQ0vB08z.mjs +100 -0
- package/.output/server/chunks/build/useDcwLineRun-DQ0vB08z.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwParamApprovals-CYspRuDk.mjs +119 -0
- package/.output/server/chunks/build/useDcwParamApprovals-CYspRuDk.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwProducts-CTbm_5S_.mjs +88 -0
- package/.output/server/chunks/build/useDcwProducts-CTbm_5S_.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwQuery-E0yjZs7G.mjs +93 -0
- package/.output/server/chunks/build/useDcwQuery-E0yjZs7G.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwQuickActions-BvT5VFOY.mjs +84 -0
- package/.output/server/chunks/build/useDcwQuickActions-BvT5VFOY.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwRecipes-zngShyk0.mjs +263 -0
- package/.output/server/chunks/build/useDcwRecipes-zngShyk0.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwStream-82IdjJKk.mjs +455 -0
- package/.output/server/chunks/build/useDcwStream-82IdjJKk.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwTemplates-CpHrcnvJ.mjs +87 -0
- package/.output/server/chunks/build/useDcwTemplates-CpHrcnvJ.mjs.map +1 -0
- package/.output/server/chunks/build/useDcwWrites-BlOhljp9.mjs +136 -0
- package/.output/server/chunks/build/useDcwWrites-BlOhljp9.mjs.map +1 -0
- package/.output/server/chunks/build/useDeviceTwins-DL-VLFeX.mjs +118 -0
- package/.output/server/chunks/build/useDeviceTwins-DL-VLFeX.mjs.map +1 -0
- package/.output/server/chunks/build/useExpConfirmations-DgwqTb9O.mjs +104 -0
- package/.output/server/chunks/build/useExpConfirmations-DgwqTb9O.mjs.map +1 -0
- package/.output/server/chunks/build/useLocalTime-C__kwn8h.mjs +52 -0
- package/.output/server/chunks/build/useLocalTime-C__kwn8h.mjs.map +1 -0
- package/.output/server/chunks/build/useLogsFormat-B-o4MHy7.mjs +30 -33
- package/.output/server/chunks/build/useLogsFormat-B-o4MHy7.mjs.map +1 -1
- package/.output/server/chunks/build/useLogsManual-BcfanT0s.mjs +80 -0
- package/.output/server/chunks/build/useLogsManual-BcfanT0s.mjs.map +1 -0
- package/.output/server/chunks/build/useLogsQuery-BGlKVKpV.mjs +136 -0
- package/.output/server/chunks/build/useLogsQuery-BGlKVKpV.mjs.map +1 -0
- package/.output/server/chunks/build/useLogsScope-BRyy550Y.mjs +75 -0
- package/.output/server/chunks/build/useLogsScope-BRyy550Y.mjs.map +1 -0
- package/.output/server/chunks/build/useMonitorColumns-vVnt3r5Q.mjs +155 -166
- package/.output/server/chunks/build/useMonitorColumns-vVnt3r5Q.mjs.map +1 -1
- package/.output/server/chunks/build/useMonitorData-BUey2Xly.mjs +128 -0
- package/.output/server/chunks/build/useMonitorData-BUey2Xly.mjs.map +1 -0
- package/.output/server/chunks/build/useMonitorFormat-rhs7FlNo.mjs +77 -0
- package/.output/server/chunks/build/useMonitorFormat-rhs7FlNo.mjs.map +1 -0
- package/.output/server/chunks/build/useMonitorTerminal-Dj4QwRNW.mjs +20 -27
- package/.output/server/chunks/build/useMonitorTerminal-Dj4QwRNW.mjs.map +1 -1
- package/.output/server/chunks/build/useOperationsFeed-DWfCjP4l.mjs +182 -0
- package/.output/server/chunks/build/useOperationsFeed-DWfCjP4l.mjs.map +1 -0
- package/.output/server/chunks/build/useOpsLog--yf8erUi.mjs +139 -0
- package/.output/server/chunks/build/useOpsLog--yf8erUi.mjs.map +1 -0
- package/.output/server/chunks/build/useRecipeDispatchApprovals-CKF2sB9P.mjs +146 -0
- package/.output/server/chunks/build/useRecipeDispatchApprovals-CKF2sB9P.mjs.map +1 -0
- package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +23 -39
- package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -1
- package/.output/server/chunks/build/useTeamActions-C08sAVqY.mjs +71 -0
- package/.output/server/chunks/build/useTeamActions-C08sAVqY.mjs.map +1 -0
- package/.output/server/chunks/build/useTeamAddMemberDialog-fzTmflr8.mjs +78 -0
- package/.output/server/chunks/build/useTeamAddMemberDialog-fzTmflr8.mjs.map +1 -0
- package/.output/server/chunks/build/useTeamCreateDialog-De63ep-X.mjs +91 -0
- package/.output/server/chunks/build/useTeamCreateDialog-De63ep-X.mjs.map +1 -0
- package/.output/server/chunks/build/useTeamDeployDialog-CpAtG5Jy.mjs +76 -0
- package/.output/server/chunks/build/useTeamDeployDialog-CpAtG5Jy.mjs.map +1 -0
- package/.output/server/chunks/build/useTeamDialogState-CFRUQxAf.mjs +31 -38
- package/.output/server/chunks/build/useTeamDialogState-CFRUQxAf.mjs.map +1 -1
- package/.output/server/chunks/build/useTeamPluginDialog-C1OOe1sL.mjs +99 -0
- package/.output/server/chunks/build/useTeamPluginDialog-C1OOe1sL.mjs.map +1 -0
- package/.output/server/chunks/build/useTeamsCatalog-DA41SMDu.mjs +124 -0
- package/.output/server/chunks/build/useTeamsCatalog-DA41SMDu.mjs.map +1 -0
- package/.output/server/chunks/build/useTokenClipboard-CwQpoFeP.mjs +21 -28
- package/.output/server/chunks/build/useTokenClipboard-CwQpoFeP.mjs.map +1 -1
- package/.output/server/chunks/build/useTokenCreate-XHTQExra.mjs +106 -0
- package/.output/server/chunks/build/useTokenCreate-XHTQExra.mjs.map +1 -0
- package/.output/server/chunks/build/useTokenRename-Cf1B0cRy.mjs +81 -0
- package/.output/server/chunks/build/useTokenRename-Cf1B0cRy.mjs.map +1 -0
- package/.output/server/chunks/build/useTokenReveal-C8n1Vt9t.mjs +108 -0
- package/.output/server/chunks/build/useTokenReveal-C8n1Vt9t.mjs.map +1 -0
- package/.output/server/chunks/build/useTokensList-MMIq24E-.mjs +86 -0
- package/.output/server/chunks/build/useTokensList-MMIq24E-.mjs.map +1 -0
- package/.output/server/chunks/build/useTownBus-D9SoIsAN.mjs +16 -17
- package/.output/server/chunks/build/useTownBus-D9SoIsAN.mjs.map +1 -1
- package/.output/server/chunks/build/useWorkbenchAuth-Dyc_czui.mjs +131 -0
- package/.output/server/chunks/build/useWorkbenchAuth-Dyc_czui.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkbenchWorkspaces-DT33HbBD.mjs +128 -0
- package/.output/server/chunks/build/useWorkbenchWorkspaces-DT33HbBD.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkshopApi-BkEgGBIM.mjs +118 -0
- package/.output/server/chunks/build/useWorkshopApi-BkEgGBIM.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkshopWs-BWrVj4jL.mjs +1577 -0
- package/.output/server/chunks/build/useWorkshopWs-BWrVj4jL.mjs.map +1 -0
- package/.output/server/chunks/build/{useWorkspaceAuth-Dlvj9csD.mjs → useWorkspaceAuth-TahC9EC7.mjs} +4 -16
- package/.output/server/chunks/build/useWorkspaceAuth-TahC9EC7.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkspaceChannels-BVAHmhN3.mjs +94 -0
- package/.output/server/chunks/build/useWorkspaceChannels-BVAHmhN3.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkspacePanels-CTwREmq0.mjs +30 -33
- package/.output/server/chunks/build/useWorkspacePanels-CTwREmq0.mjs.map +1 -1
- package/.output/server/chunks/build/useWorkspaceShell-rQHWNNWJ.mjs +150 -0
- package/.output/server/chunks/build/useWorkspaceShell-rQHWNNWJ.mjs.map +1 -0
- package/.output/server/chunks/build/users-pVCK50z-.mjs +566 -0
- package/.output/server/chunks/build/users-pVCK50z-.mjs.map +1 -0
- package/.output/server/chunks/build/workshop-DAgsBgG4.mjs +499 -0
- package/.output/server/chunks/build/workshop-DAgsBgG4.mjs.map +1 -0
- package/.output/server/chunks/build/workspaces-CmW0cpOt.mjs +111 -121
- package/.output/server/chunks/build/workspaces-CmW0cpOt.mjs.map +1 -1
- package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs +4 -1
- package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs.map +1 -1
- package/.output/server/chunks/routes/renderer.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs.map +1 -1
- package/.output/server/chunks/virtual/entry.mjs +9357 -10721
- package/.output/server/chunks/virtual/entry.mjs.map +1 -1
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/chunks/virtual/precomputed.mjs.map +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +2 -2
- package/README.md +3 -3
- package/app/components/AwLogo.vue +75 -75
- package/app/components/aml/AmlDatasetFormModal.vue +279 -279
- package/app/components/aml/AmlDatasetTable.vue +319 -319
- package/app/components/aml/AmlExperimentsTable.vue +142 -142
- package/app/components/aml/AmlInfraBanner.vue +23 -23
- package/app/components/aml/AmlJobsTable.vue +248 -248
- package/app/components/aml/AmlOverviewBar.vue +62 -62
- package/app/components/aml/AmlPageHeader.vue +47 -47
- package/app/components/aml/AmlPredictConsole.vue +201 -201
- package/app/components/app-header/HitlBell.vue +423 -423
- package/app/components/daq/DaqAddNodeModal.vue +209 -209
- package/app/components/daq/DaqAlarmBar.vue +46 -46
- package/app/components/daq/DaqControlCard.vue +159 -159
- package/app/components/daq/DaqInfraBanner.vue +31 -31
- package/app/components/daq/DaqLineBanner.vue +47 -47
- package/app/components/daq/DaqNodePanel.vue +396 -396
- package/app/components/daq/DaqOptPanel.vue +197 -197
- package/app/components/daq/DaqTemplateModal.vue +246 -246
- package/app/components/dashboard/DashboardGaugeBand.vue +78 -78
- package/app/components/dashboard/DashboardHero.vue +209 -209
- package/app/components/dashboard/DashboardLineStatePanel.vue +78 -78
- package/app/components/dashboard/DashboardNodeStatePanel.vue +46 -46
- package/app/components/dashboard/DashboardPipelinePanel.vue +46 -46
- package/app/components/dashboard/DashboardTrendPanel.vue +83 -83
- package/app/components/dashboard/DashboardWritePanel.vue +52 -52
- package/app/components/dcw/DcwCreateLineModal.vue +105 -105
- package/app/components/dcw/DcwDeleteLineModal.vue +73 -73
- package/app/components/dcw/DcwDetailTemplateModal.vue +238 -238
- package/app/components/dcw/DcwLedgerCard.vue +160 -160
- package/app/components/dcw/DcwMesArtifacts.vue +140 -140
- package/app/components/dcw/DcwProductCreateModal.vue +113 -113
- package/app/components/dcw/DcwRecipeDispatchHistoryModal.vue +244 -244
- package/app/components/dcw/DcwRecipeEditModal.vue +399 -399
- package/app/components/dcw/DcwRecipeHistoryModal.vue +165 -165
- package/app/components/dcw/DcwRunDataModal.vue +191 -191
- package/app/components/dcw/DcwTemplateModal.vue +227 -227
- package/app/components/dcw/DcwWriteHistoryCard.vue +96 -96
- package/app/components/dcw/ExpConfirmCard.vue +103 -103
- package/app/components/dcw/RecipeDispatchApprovalCard.vue +330 -330
- package/app/components/logs/LogsPageHead.vue +71 -71
- package/app/components/monitor/MonitorAgentTable.vue +143 -143
- package/app/components/monitor/MonitorChannelTable.vue +80 -80
- package/app/components/monitor/MonitorLoginGate.vue +24 -24
- package/app/components/monitor/MonitorPageHead.vue +92 -92
- package/app/components/monitor/MonitorProcessTable.vue +159 -159
- package/app/components/ontology/OntologyGraph.vue +356 -356
- package/app/components/operations/OperationsFilterCard.vue +164 -164
- package/app/components/operations/OperationsPageHead.vue +47 -47
- package/app/components/tokens/TokenCreateModal.vue +42 -42
- package/app/components/tokens/TokenOnceModal.vue +164 -164
- package/app/components/tokens/TokenRenameModal.vue +40 -40
- package/app/components/tokens/TokensPageHead.vue +76 -76
- package/app/components/workshop/A2aRpcDebugger.vue +237 -237
- package/app/components/workshop/AgentInspectorDrawer.vue +424 -424
- package/app/components/workshop/AgentLanesView.vue +278 -278
- package/app/components/workshop/ArtifactCard.vue +120 -120
- package/app/components/workshop/AssetLibrary.vue +452 -452
- package/app/components/workshop/ChatMemberPanel.vue +556 -556
- package/app/components/workshop/CommandPalette.vue +270 -270
- package/app/components/workshop/Composer.vue +540 -540
- package/app/components/workshop/DaqTemplateIcon.vue +33 -33
- package/app/components/workshop/DeviceTwinPanel.vue +167 -167
- package/app/components/workshop/FileSelectorModal.vue +188 -188
- package/app/components/workshop/InspectorPanel.vue +301 -301
- package/app/components/workshop/PluginSlot.vue +120 -120
- package/app/components/workshop/TranscriptTimeline.vue +223 -223
- package/app/components/workshop/agents/AgentTemplateEditModal.vue +139 -139
- package/app/components/workshop/blocks/ClusterArtifact.vue +61 -61
- package/app/components/workshop/blocks/ClusterError.vue +85 -85
- package/app/components/workshop/blocks/ClusterLife.vue +87 -87
- package/app/components/workshop/blocks/ClusterMember.vue +95 -95
- package/app/components/workshop/blocks/ClusterMemory.vue +84 -84
- package/app/components/workshop/blocks/ClusterOther.vue +67 -67
- package/app/components/workshop/blocks/ClusterStatus.vue +149 -149
- package/app/components/workshop/blocks/ClusterTool.vue +176 -176
- package/app/components/workshop/blocks/EventBlockAvatar.vue +114 -114
- package/app/components/workshop/blocks/EventBlockHead.vue +232 -232
- package/app/components/workshop/composer/ComposerToolbar.vue +278 -278
- package/app/components/workshop/composer/MentionMenu.vue +135 -135
- package/app/components/workshop/composer/ReplyChip.vue +65 -65
- package/app/components/workshop/composer/StatusChip.vue +171 -171
- package/app/components/workshop/console/Dock.vue +71 -71
- package/app/components/workshop/console/LeftPane.vue +84 -84
- package/app/components/workshop/device-twin/DeviceTwinCard.vue +265 -265
- package/app/components/workshop/device-twin/DeviceTwinDcwSection.vue +203 -203
- package/app/components/workshop/lanes/AddMemberModal.vue +270 -270
- package/app/components/workshop/lanes/EditMemberModal.vue +106 -106
- package/app/components/workshop/lanes/LanesToolbar.vue +97 -97
- package/app/components/workshop/session-list/ChannelSettingsModal.vue +3 -3
- package/app/components/workshop/session-list/MountChannelModal.vue +124 -124
- package/app/components/workshop/session-list/SaveTemplateModal.vue +93 -93
- package/app/components/workshop/task-board/Card.vue +172 -172
- package/app/components/workshop/task-board/Column.vue +151 -151
- package/app/components/workshop/task-board/List.vue +157 -157
- package/app/components/workshop/task-board/StatusMenu.vue +88 -88
- package/app/components/workshop/teams/TeamAddMemberModal.vue +53 -53
- package/app/components/workshop/teams/TeamCreateModal.vue +94 -94
- package/app/components/workshop/teams/TeamDeployModal.vue +40 -40
- package/app/components/workshop/teams/TeamPluginModal.vue +119 -119
- package/app/components/workshop/terminal/OmpTerminalPanel.vue +270 -270
- package/app/components/workshop/terminal/TerminalHitlCard.vue +170 -170
- package/app/components/workshop/town/TownScene.ts +466 -466
- package/app/components/workshop/town/TownView.vue +561 -561
- package/app/components/workshop/town/components/AutoModeConfirmModal.vue +94 -94
- package/app/components/workshop/town/components/TownAgentBindings.vue +405 -405
- package/app/components/workshop/town/components/TownAgentInspector.vue +554 -554
- package/app/components/workshop/town/components/TownAlarmList.vue +147 -147
- package/app/components/workshop/town/components/TownBoundaryPanel.vue +350 -350
- package/app/components/workshop/town/components/TownCalloutLayer.vue +147 -147
- package/app/components/workshop/town/components/TownChannelDock.vue +116 -116
- package/app/components/workshop/town/components/TownControlDock.vue +405 -405
- package/app/components/workshop/town/components/TownDaqNodeInspector.vue +345 -345
- package/app/components/workshop/town/components/TownDaqTree.vue +366 -366
- package/app/components/workshop/town/components/TownDcwBindSection.vue +473 -473
- package/app/components/workshop/town/components/TownDcwNodeInspector.vue +185 -185
- package/app/components/workshop/town/components/TownDcwTree.vue +280 -280
- package/app/components/workshop/town/components/TownDeviceBindings.vue +394 -394
- package/app/components/workshop/town/components/TownDeviceTransform.vue +248 -248
- package/app/components/workshop/town/components/TownEventList.vue +120 -120
- package/app/components/workshop/town/components/TownHealthBody.vue +100 -100
- package/app/components/workshop/town/components/TownKpiStrip.vue +207 -207
- package/app/components/workshop/town/components/TownLeftRail.vue +308 -308
- package/app/components/workshop/town/components/TownLoadingMask.vue +61 -61
- package/app/components/workshop/town/components/TownNavMap.vue +154 -154
- package/app/components/workshop/town/components/TownRightRail.vue +543 -543
- package/app/components/workshop/town/components/TownStage.vue +281 -281
- package/app/components/workshop/town/components/TownStageTop.vue +238 -238
- package/app/components/workshop/town/components/TownStatusBar.vue +108 -108
- package/app/components/workshop/town/components/TownTopNav.vue +367 -367
- package/app/components/workshop/town/components/TownTrendDock.vue +178 -178
- package/app/components/workshop/town/scene/town-scene-agents.ts +254 -254
- package/app/components/workshop/town/scene/town-scene-behavior.ts +194 -194
- package/app/components/workshop/town/scene/town-scene-bubbles.ts +96 -96
- package/app/components/workshop/town/scene/town-scene-core.ts +158 -158
- package/app/components/workshop/town/scene/town-scene-models.ts +109 -109
- package/app/components/workshop/town/scene/town-scene-visuals.ts +279 -279
- package/app/components/workshop/town/scene/town-scene3d-agents.ts +308 -308
- package/app/components/workshop/town/scene/town-scene3d-blocks.ts +297 -297
- package/app/components/workshop/town/scene/town-scene3d-bubbles.ts +281 -281
- package/app/components/workshop/town/scene/town-scene3d-camera.ts +282 -282
- package/app/components/workshop/town/scene/town-scene3d-devices.ts +433 -433
- package/app/components/workshop/town/scene/town-scene3d-editing.ts +490 -490
- package/app/components/workshop/town/scene/town-scene3d-events.ts +173 -173
- package/app/components/workshop/town/scene/town-scene3d-factory.ts +412 -412
- package/app/components/workshop/town/scene/town-scene3d-hud.ts +110 -110
- package/app/components/workshop/town/scene/town-scene3d-lifecycle.ts +144 -144
- package/app/components/workshop/town/scene/town-scene3d-links.ts +141 -141
- package/app/components/workshop/town/scene/town-scene3d-loop.ts +286 -286
- package/app/components/workshop/town/scene/town-scene3d-models.ts +154 -154
- package/app/components/workshop/town/scene/town-scene3d-nodes.ts +562 -562
- package/app/components/workshop/town/scene/town-scene3d-ranges.ts +273 -273
- package/app/components/workshop/town/scene/town-scene3d-renderer.ts +320 -320
- package/app/components/workshop/town/scene/town-scene3d-selection.ts +257 -257
- package/app/components/workshop/town/scene/town-scene3d-types.ts +264 -264
- package/app/components/workshop/transcript/TranscriptBlockList.vue +68 -68
- package/app/components/workshop/transcript/TranscriptEarlierPager.vue +81 -81
- package/app/components/workshop/transcript/TranscriptFilterBar.vue +126 -126
- package/app/components/workshop/workbench/CreateWorkspaceModal.vue +36 -36
- package/app/components/workshop/workbench/WorkspaceEmpty.vue +56 -56
- package/app/composables/useResponsive.ts +88 -88
- package/app/composables/workshop/device-twin-types.ts +29 -29
- package/app/composables/workshop/event-blocks/attention.ts +58 -58
- package/app/composables/workshop/event-blocks/classify.ts +44 -44
- package/app/composables/workshop/event-blocks/clusterer.ts +231 -231
- package/app/composables/workshop/event-blocks/fold-stream.ts +45 -45
- package/app/composables/workshop/event-blocks/markdown.ts +81 -81
- package/app/composables/workshop/event-blocks/mentions.ts +49 -49
- package/app/composables/workshop/event-blocks/text-utils.ts +100 -100
- package/app/composables/workshop/event-blocks/types.ts +40 -40
- package/app/composables/workshop/town/town-relay-props.ts +231 -231
- package/app/composables/workshop/town/town-view-types.ts +145 -145
- package/app/composables/workshop/town/useTownAgentBindings.ts +182 -182
- package/app/composables/workshop/town/useTownAgentChat.ts +203 -203
- package/app/composables/workshop/town/useTownAlarms.ts +36 -36
- package/app/composables/workshop/town/useTownCallouts.ts +174 -174
- package/app/composables/workshop/town/useTownChannels.ts +220 -220
- package/app/composables/workshop/town/useTownHealth.ts +66 -66
- package/app/composables/workshop/town/useTownHudTickers.ts +77 -77
- package/app/composables/workshop/town/useTownLayout.ts +277 -277
- package/app/composables/workshop/town/useTownNavMap.ts +301 -301
- package/app/composables/workshop/town/useTownNodeBindings.ts +345 -345
- package/app/composables/workshop/town/useTownPanelDrag.ts +91 -91
- package/app/composables/workshop/town/useTownRailData.ts +421 -421
- package/app/composables/workshop/town/useTownSceneControl.ts +92 -92
- package/app/composables/workshop/town/useTownSceneInput.ts +275 -275
- package/app/composables/workshop/town/useTownSceneRuntime.ts +390 -390
- package/app/composables/workshop/town/useTownSheets.ts +41 -41
- package/app/composables/workshop/town/useTownTrends.ts +93 -93
- package/app/composables/workshop/useChannelList.ts +88 -88
- package/app/composables/workshop/useChannelTerminals.ts +65 -65
- package/app/composables/workshop/useComposerMentions.ts +130 -130
- package/app/composables/workshop/useDcwStream.ts +374 -374
- package/app/composables/workshop/useDeviceTwinDcwWrite.ts +77 -77
- package/app/composables/workshop/useDeviceTwinPanel.ts +56 -56
- package/app/composables/workshop/useEscClose.ts +44 -44
- package/app/composables/workshop/useEventBlockView.ts +159 -159
- package/app/composables/workshop/useEventBlocks.ts +33 -33
- package/app/composables/workshop/useHitlAnswer.ts +184 -184
- package/app/composables/workshop/useLaneWidths.ts +27 -27
- package/app/composables/workshop/useOmpTerminalSession.ts +424 -424
- package/app/composables/workshop/usePluginAdmin.ts +152 -152
- package/app/composables/workshop/usePluginPanels.ts +72 -72
- package/app/composables/workshop/useRuntimeConfigDraft.ts +118 -118
- package/app/composables/workshop/useSettingsGroups.ts +243 -243
- package/app/composables/workshop/useTaskBoardColumns.ts +47 -47
- package/app/composables/workshop/useTerminalRenderer.ts +308 -308
- package/app/composables/workshop/useTranscriptEnterStage.ts +48 -48
- package/app/composables/workshop/useTranscriptHistory.ts +52 -52
- package/app/composables/workshop/useTranscriptScroll.ts +113 -113
- package/app/composables/workshop/useVisibleInterval.ts +42 -42
- package/app/composables/workshop/useWorkshopWs.ts +265 -265
- package/app/config/index.ts +1 -1
- package/app/middleware/locale-cookie.global.ts +22 -22
- package/app/pages/aml/api.ts +6 -6
- package/app/pages/aml/composables/useAmlDatasets.ts +38 -38
- package/app/pages/aml/composables/useAmlEntities.ts +57 -57
- package/app/pages/aml/composables/useAmlEnv.ts +191 -191
- package/app/pages/aml/composables/useAmlExperiments.ts +45 -45
- package/app/pages/aml/composables/useAmlFormat.ts +44 -44
- package/app/pages/aml/composables/useAmlJobs.ts +131 -131
- package/app/pages/aml/composables/useAmlModels.ts +56 -56
- package/app/pages/aml/composables/useAmlNotes.ts +47 -47
- package/app/pages/aml/composables/useAmlPredict.ts +93 -93
- package/app/pages/aml/constants.ts +63 -63
- package/app/pages/aml/index.vue +236 -236
- package/app/pages/composables/useDashboardCharts.ts +192 -192
- package/app/pages/composables/useDashboardData.ts +144 -144
- package/app/pages/daq/composables/useDaqAddNode.ts +126 -126
- package/app/pages/daq/composables/useDaqDetailBinding.ts +29 -29
- package/app/pages/daq/composables/useDaqDetailFrames.ts +42 -42
- package/app/pages/daq/composables/useDaqDetailNode.ts +53 -53
- package/app/pages/daq/composables/useDaqDetailParams.ts +138 -138
- package/app/pages/daq/composables/useDaqFilters.ts +187 -187
- package/app/pages/daq/composables/useDaqNodeWindow.ts +86 -86
- package/app/pages/daq/composables/useDaqOptimizations.ts +99 -99
- package/app/pages/daq/composables/useDaqTemplates.ts +152 -152
- package/app/pages/daq/index.vue +260 -260
- package/app/pages/dcw/composables/useDcwCards.ts +27 -27
- package/app/pages/dcw/composables/useDcwDetailScope.ts +145 -145
- package/app/pages/dcw/composables/useDcwDetailTemplates.ts +94 -94
- package/app/pages/dcw/composables/useDcwFilter.ts +29 -29
- package/app/pages/dcw/composables/useDcwLedger.ts +34 -34
- package/app/pages/dcw/composables/useDcwLineMutations.ts +115 -115
- package/app/pages/dcw/composables/useDcwLineRun.ts +60 -60
- package/app/pages/dcw/composables/useDcwProducts.ts +54 -54
- package/app/pages/dcw/composables/useDcwQuery.ts +68 -68
- package/app/pages/dcw/composables/useDcwQuickActions.ts +47 -47
- package/app/pages/dcw/composables/useDcwRecipes.ts +256 -256
- package/app/pages/dcw/composables/useDcwTemplates.ts +50 -50
- package/app/pages/dcw/index.vue +195 -195
- package/app/pages/dcw/types.ts +9 -9
- package/app/pages/index.vue +126 -126
- package/app/pages/logs/composables/useLogsFormat.ts +43 -43
- package/app/pages/logs/composables/useLogsScope.ts +39 -39
- package/app/pages/monitor/types.ts +61 -61
- package/app/pages/operations.vue +61 -61
- package/app/pages/permissions/index.vue +369 -369
- package/app/pages/tokens/composables/useTokenClipboard.ts +33 -33
- package/app/pages/tokens/composables/useTokenCreate.ts +93 -93
- package/app/pages/tokens/composables/useTokenRename.ts +54 -54
- package/app/pages/tokens/composables/useTokenReveal.ts +84 -84
- package/app/pages/tokens/composables/useTokensList.ts +60 -60
- package/app/pages/tokens.vue +111 -111
- package/app/pages/tools/index.vue +98 -98
- package/app/pages/workshop/agents.vue +130 -130
- package/app/pages/workshop/composables/useAgentHarnessOptions.ts +59 -59
- package/app/pages/workshop/composables/useAgentTemplateActions.ts +42 -42
- package/app/pages/workshop/composables/useAgentTemplateDialogState.ts +24 -24
- package/app/pages/workshop/composables/useAgentTemplateForm.ts +88 -88
- package/app/pages/workshop/composables/useAgentTemplatesCatalog.ts +92 -92
- package/app/pages/workshop/composables/useTeamActions.ts +44 -44
- package/app/pages/workshop/composables/useTeamAddMemberDialog.ts +49 -49
- package/app/pages/workshop/composables/useTeamCreateDialog.ts +67 -67
- package/app/pages/workshop/composables/useTeamDeployDialog.ts +49 -49
- package/app/pages/workshop/composables/useTeamDialogState.ts +46 -46
- package/app/pages/workshop/composables/useTeamPluginDialog.ts +72 -72
- package/app/pages/workshop/composables/useTeamsCatalog.ts +100 -100
- package/app/pages/workshop/composables/useWorkbenchWorkspaces.ts +123 -123
- package/app/pages/workshop/composables/useWorkspaceAuth.ts +31 -31
- package/app/pages/workshop/composables/useWorkspaceChannels.ts +62 -62
- package/app/pages/workshop/composables/useWorkspaceShell.ts +136 -136
- package/app/pages/workshop/schedules.vue +553 -553
- package/app/pages/workshop/w/[wsId].vue +221 -221
- package/app/plugins/aw-plugins.client.ts +213 -213
- package/app/plugins/esc-close.client.ts +31 -31
- package/app/stores/workshop/hitl.ts +183 -183
- package/app/stores/workshop/narrow-fetch.ts +23 -23
- package/app/utils/api-error.ts +75 -75
- package/app/utils/builtin-catalog.ts +104 -104
- package/app/utils/chart-trend.ts +97 -97
- package/app/utils/dcw-driver-badge.ts +16 -16
- package/app/utils/dcw-recipe-form.ts +26 -26
- package/cli/commands/tui.mjs +28 -28
- package/cli/commands/update.mjs +124 -124
- package/cli/core/child-lifecycle.mjs +136 -136
- package/cli/core/meta.mjs +38 -38
- package/cli/core/post-build.mjs +96 -96
- package/config.yml +1 -1
- package/docs/cli.en.md +2 -2
- package/docs/cli.md +2 -2
- package/docs/mcp.en.md +27 -27
- package/docs/multi-harness-architecture.md +248 -248
- package/docs/plugins.en.md +1 -1
- package/docs/plugins.md +1 -1
- package/docs/sdk.en.md +1 -1
- package/docs/sdk.md +1 -1
- package/docs/tui.md +43 -43
- package/i18n/dicts/_en.json +1551 -1551
- package/i18n/dicts/_en2.json +300 -300
- package/i18n/dicts/_en4.json +114 -114
- package/i18n/dicts/agentLanesView.json +61 -61
- package/i18n/dicts/appHeader.json +17 -17
- package/i18n/dicts/channelSessionList.json +51 -51
- package/i18n/dicts/chat.json +79 -79
- package/i18n/dicts/clusterTool.json +4 -4
- package/i18n/dicts/composer.json +38 -38
- package/i18n/dicts/ctpl.json +37 -37
- package/i18n/dicts/dcwDetail.json +219 -219
- package/i18n/dicts/lineOps.json +14 -14
- package/i18n/dicts/multiChannelView.json +13 -13
- package/i18n/dicts/notifications.json +13 -13
- package/i18n/dicts/taskInspectorDrawer.json +25 -25
- package/i18n/dicts/teamPlugins.json +10 -10
- package/i18n/dicts/townView.json +209 -209
- package/mcp/discovery.mjs +227 -227
- package/package.json +1 -1
- package/public/favicon.svg +14 -14
- package/public/robots.txt +3 -3
- package/scripts/_audit/audit-harness-adapters.md +664 -664
- package/scripts/_audit/aw-mcp-live-e2e.mjs +293 -293
- package/scripts/_audit/bump-version.mjs +51 -51
- package/scripts/_audit/detached-instance.mjs +112 -112
- package/scripts/_audit/detached-start.mjs +103 -103
- package/scripts/_audit/eda-ui-fix-acceptance.mjs +212 -212
- package/scripts/_audit/guard-upstream-liveness.mjs +104 -104
- package/scripts/_audit/http-api-authz-audit.md +657 -657
- package/scripts/_audit/kb-team-mock-e2e.mjs +146 -146
- package/scripts/_audit/kb-team-omp-e2e.mjs +229 -229
- package/scripts/_audit/run.mjs +21 -21
- package/scripts/_audit/runtime-audit.md +716 -716
- package/scripts/_audit/skills-guided-e2e.mjs +316 -316
- package/scripts/_audit/stability-soak.mjs +252 -252
- package/scripts/_audit/stubs/nuxt-imports.mjs +67 -67
- package/scripts/_audit/ts-register-hook.mjs +12 -12
- package/scripts/_audit/ts-resolve-hook.mjs +96 -96
- package/scripts/_aw-packaged-start.cjs +22 -22
- package/scripts/_aw0746-stage12.mjs +94 -94
- package/scripts/_aw0746-stage34.mjs +136 -136
- package/scripts/_aw0746-stage6b-train-sweep.mjs +145 -145
- package/scripts/_aw0746-stage6c-jobsmoke.mjs +69 -69
- package/scripts/_dbg-configroot-audit.mjs +86 -86
- package/scripts/_dbg-daq-intervals-func.mjs +115 -115
- package/scripts/_dbg-perms-e2e.mjs +142 -142
- package/scripts/_dbg-protocol-matrix.mjs +462 -462
- package/scripts/_dbg-render-regression.mjs +193 -193
- package/scripts/_dbg-schedule-e2e.mjs +262 -262
- package/scripts/_e2e-ui-functional.mjs +371 -371
- package/scripts/_i18n-en-cjk-scan.mjs +28 -28
- package/scripts/_i18n-en-verify.mjs +107 -107
- package/scripts/_i18n-extra-checks.mjs +53 -53
- package/scripts/_i18n-scan-hardcoded.mjs +180 -180
- package/scripts/_lib-aml-python-run.mjs +17 -17
- package/scripts/_lint-reflow.mjs +105 -105
- package/scripts/_loop-biax-campaign.mjs +130 -130
- package/scripts/_plc-sim-detached.cjs +22 -22
- package/scripts/_probe-daq-omp-task.mjs +100 -100
- package/scripts/_probe-daq-scope-default.mjs +123 -123
- package/scripts/_probe-model-channel-link.cjs +25 -25
- package/scripts/_probe-recipe-switch.mjs +92 -92
- package/scripts/_provision-injection-line.mjs +39 -39
- package/scripts/_run-matrix.ps1 +53 -53
- package/scripts/_sweep-button-contrast.mjs +87 -87
- package/scripts/acceptance-aml-hybrid-e2e.ts +396 -396
- package/scripts/acceptance-aml-hybrid-live.mjs +93 -93
- package/scripts/acceptance-aml-model-identity.ts +137 -137
- package/scripts/agent-daq-isolation-e2e.mjs +91 -91
- package/scripts/api-contract-smoke.mjs +126 -126
- package/scripts/ask-hitl-e2e.mjs +96 -96
- package/scripts/aw-expose.mjs +159 -159
- package/scripts/channel-interaction-smoke.mjs +80 -80
- package/scripts/data-studio-ui-e2e.mjs +163 -163
- package/scripts/demo-group-chat-all-real.ts +279 -279
- package/scripts/demo-group-chat-omp-cases.ts +210 -210
- package/scripts/demo-group-chat-omp.ts +184 -184
- package/scripts/demo-group-chat-order.ts +197 -197
- package/scripts/dev-modbus-simulator.mjs +55 -55
- package/scripts/docs-shots.mjs +36 -36
- package/scripts/e2e-agentteam-chat.mjs +473 -473
- package/scripts/e2e-agentteam-real-triage.mjs +421 -421
- package/scripts/e2e-aml-decoupled.mjs +162 -162
- package/scripts/e2e-aw-tui-global.mjs +277 -277
- package/scripts/e2e-closedloop/cleanup.mjs +27 -27
- package/scripts/e2e-closedloop/lib.mjs +109 -109
- package/scripts/e2e-closedloop/main.mjs +59 -59
- package/scripts/e2e-closedloop/scenarios-accounts.mjs +210 -210
- package/scripts/e2e-closedloop/scenarios-authz.mjs +196 -196
- package/scripts/e2e-closedloop/scenarios-daq-dcw.mjs +222 -222
- package/scripts/e2e-closedloop/scenarios-plugins.mjs +169 -169
- package/scripts/e2e-closedloop/state.mjs +10 -10
- package/scripts/e2e-config-groups.mjs +238 -238
- package/scripts/e2e-crash-cycle.mjs +108 -108
- package/scripts/e2e-full-closedloop.mjs +16 -16
- package/scripts/e2e-hil-multi-user.mjs +307 -307
- package/scripts/e2e-hil-restart.mjs +339 -339
- package/scripts/e2e-idd-results-ui.mjs +175 -0
- package/scripts/e2e-idd-results.mjs +48 -16
- package/scripts/e2e-injection-recipe-closedloop.mjs +203 -203
- package/scripts/e2e-line-governance.mjs +352 -352
- package/scripts/e2e-ontology.mjs +393 -393
- package/scripts/e2e-plc-plugin-closedloop.mjs +549 -549
- package/scripts/e2e-timezone-settings.mjs +57 -57
- package/scripts/full-stack-shots.mjs +145 -145
- package/scripts/full-tool-injection-harness-e2e.mjs +214 -214
- package/scripts/hitl-reject-e2e.mjs +131 -131
- package/scripts/line-log-smoke.mjs +260 -260
- package/scripts/live-aml-mockomp.mjs +304 -304
- package/scripts/live-aml-optloop-resume.mjs +322 -322
- package/scripts/live-aml-optloop.mjs +588 -588
- package/scripts/live-aml-stage9.mjs +169 -169
- package/scripts/live-aml-two-goals.mjs +315 -315
- package/scripts/live-optloop-two-modes.mjs +857 -857
- package/scripts/mes-agent-integration-e2e.mjs +361 -361
- package/scripts/ops-plugins-e2e.mjs +85 -85
- package/scripts/proof-omp-real.ts +189 -189
- package/scripts/scenario-suite-setup.mjs +337 -337
- package/scripts/scenario-suite-stability.mjs +144 -144
- package/scripts/smoke-optimization-verify.ts +72 -72
- package/scripts/team-coordination-e2e.mjs +96 -96
- package/scripts/test-agentteam-task-chat-flow.ts +420 -420
- package/scripts/test-agentteam-workflow.ts +472 -472
- package/scripts/test-aw-skills.mjs +102 -102
- package/scripts/test-cli-exit.mjs +343 -343
- package/scripts/test-daq-frame-race.mjs +358 -358
- package/scripts/test-data-root.mjs +208 -208
- package/scripts/test-events-index.mjs +176 -176
- package/scripts/test-group-chat-hardening.ts +265 -265
- package/scripts/test-group-chat-unit/lib.ts +186 -186
- package/scripts/test-group-chat-unit/main.ts +45 -45
- package/scripts/test-group-chat-unit/section-a-migration.ts +75 -75
- package/scripts/test-group-chat-unit/section-b-permission-matrix.ts +88 -88
- package/scripts/test-group-chat-unit/section-c-member-lifecycle.ts +64 -64
- package/scripts/test-group-chat-unit/section-d-mention-resolution.ts +36 -36
- package/scripts/test-group-chat-unit/section-e-chat-facts.ts +97 -97
- package/scripts/test-group-chat-unit/section-f-user-notifications.ts +52 -52
- package/scripts/test-group-chat-unit/section-g-member-projection.ts +44 -44
- package/scripts/test-group-chat-unit/section-h-outbox-consistency.ts +30 -30
- package/scripts/test-group-chat-unit/section-i-read-idempotency.ts +24 -24
- package/scripts/test-group-chat-unit/section-j-permission-scope.ts +150 -150
- package/scripts/test-group-chat-unit.ts +35 -35
- package/scripts/test-hitl-decision.ts +526 -526
- package/scripts/test-hitl-legacy-channel-auth.ts +143 -143
- package/scripts/test-hitl-registry.ts +112 -112
- package/scripts/test-log-flooding.mjs +160 -160
- package/scripts/test-lru.mjs +101 -101
- package/scripts/test-memory-month-query.mjs +70 -70
- package/scripts/test-plugin-hardening.mjs +90 -90
- package/scripts/test-plugin-lifecycle.mjs +173 -173
- package/scripts/test-rollback-index.mjs +137 -137
- package/scripts/test-schedule-runtime.ts +309 -309
- package/scripts/test-scheduler-loop/scenarios-supervise.ts +25 -25
- package/scripts/test-sdk-surface.mjs +158 -158
- package/scripts/test-serial-bridge.mjs +418 -418
- package/scripts/test-tui-commands.mjs +148 -148
- package/scripts/test-tui-reducers.mjs +72 -72
- package/scripts/three-system-e2e/lib.mjs +79 -79
- package/scripts/three-system-e2e/main.mjs +40 -40
- package/scripts/three-system-e2e/phases/phase-0-user-token.mjs +42 -42
- package/scripts/three-system-e2e/phases/phase-0a2-line-autoselect.mjs +35 -35
- package/scripts/three-system-e2e/phases/phase-0b-fixture-grants.mjs +39 -39
- package/scripts/three-system-e2e/phases/phase-0c-plugin-outbound-auth.mjs +95 -95
- package/scripts/three-system-e2e/phases/stage-1-health.mjs +47 -47
- package/scripts/three-system-e2e/phases/stage-2-5-team-plugins.mjs +84 -84
- package/scripts/three-system-e2e/phases/stage-2-teams-tools-hitl.mjs +184 -184
- package/scripts/three-system-e2e/phases/stage-3-diag.mjs +73 -73
- package/scripts/three-system-e2e/phases/stage-4-plugin-toggle.mjs +53 -53
- package/scripts/three-system-e2e/state.mjs +87 -87
- package/scripts/three-system-e2e.mjs +40 -40
- package/scripts/tool-face-batch-identity-e2e.mjs +222 -222
- package/scripts/tsconfig.json +14 -14
- package/scripts/tui-smoke.mjs +100 -100
- package/scripts/typecheck.mjs +75 -75
- package/scripts/ui/admin-shots.mjs +46 -46
- package/scripts/ui/bump-microfont.mjs +39 -39
- package/scripts/ui/linkcheck2.mjs +37 -37
- package/scripts/ui/live-render-check.mjs +72 -72
- package/scripts/ui/md-lint.mjs +80 -80
- package/scripts/ui/perf-audit.mjs +132 -132
- package/scripts/ui/readme-assets.mjs +30 -30
- package/scripts/ui/readme-shots.mjs +42 -42
- package/scripts/ui/record-demo.mjs +557 -557
- package/scripts/ui/record.mjs +225 -225
- package/scripts/ui/seed-admin.mjs +68 -68
- package/scripts/ui/seed-demo.mjs +83 -83
- package/scripts/ui/seed-industrial-demo.mjs +171 -171
- package/scripts/ui/shot-hero.mjs +25 -25
- package/scripts/ui/shots.mjs +75 -75
- package/scripts/ui/sim-ui-shot.mjs +16 -16
- package/scripts/ui/smoke-refactor.mjs +258 -258
- package/scripts/ui/verify-nav.mjs +51 -51
- package/scripts/ui/verify-responsive.mjs +285 -285
- package/sdk/client.d.mts +110 -110
- package/sdk/examples/line-sentinel/index.mjs +105 -105
- package/sdk/examples/ops-notifier/index.mjs +34 -34
- package/sdk/examples/sample-insight/index.mjs +39 -39
- package/server/api/plugins/[name]/[...path].ts +5 -1
- package/server/api/plugins/client/[name].get.ts +18 -18
- package/server/api/plugins/i18n.get.ts +12 -12
- package/server/api/plugins/manifest.get.ts +10 -10
- package/server/api/system/backup/list.get.ts +32 -32
- package/server/api/system/backup/run.post.ts +21 -21
- package/server/api/system/config-groups/[id].delete.ts +21 -21
- package/server/api/system/config-groups/[id].patch.ts +26 -26
- package/server/api/system/config-groups/index.get.ts +21 -21
- package/server/api/system/config-groups/index.post.ts +33 -33
- package/server/api/system/timezone.get.ts +20 -20
- package/server/api/users/tokens/[id]/token.get.ts +11 -11
- package/server/api/workshop/[...path].ts +13 -13
- package/server/api/workshop/agent-tools/approvals/index.get.ts +43 -43
- package/server/api/workshop/agent-tools/bindings/[id].delete.ts +26 -26
- package/server/api/workshop/agent-tools/bindings/grant.post.ts +45 -45
- package/server/api/workshop/agent-tools/bindings/index.get.ts +41 -41
- package/server/api/workshop/agent-tools/bindings/revoke.post.ts +42 -42
- package/server/api/workshop/aml/datasets/[id]/preview.get.ts +68 -68
- package/server/api/workshop/aml/datasets/[id].delete.ts +39 -39
- package/server/api/workshop/aml/datasets/[id].get.ts +53 -53
- package/server/api/workshop/aml/datasets/[id].patch.ts +30 -30
- package/server/api/workshop/aml/datasets/index.get.ts +20 -20
- package/server/api/workshop/aml/datasets/index.post.ts +27 -27
- package/server/api/workshop/aml/entities/index.get.ts +13 -13
- package/server/api/workshop/aml/entities/prune.post.ts +28 -28
- package/server/api/workshop/aml/env/index.get.ts +13 -13
- package/server/api/workshop/aml/env/recheck.post.ts +14 -14
- package/server/api/workshop/aml/env/uv.post.ts +21 -21
- package/server/api/workshop/aml/env/venv.post.ts +24 -24
- package/server/api/workshop/aml/experiments/index.get.ts +35 -35
- package/server/api/workshop/aml/index.get.ts +37 -37
- package/server/api/workshop/aml/jobs/[id]/cancel.post.ts +21 -21
- package/server/api/workshop/aml/jobs/[id]/logs.get.ts +23 -23
- package/server/api/workshop/aml/jobs/[id]/retry.post.ts +20 -20
- package/server/api/workshop/aml/jobs/[id].delete.ts +44 -44
- package/server/api/workshop/aml/jobs/[id].get.ts +23 -23
- package/server/api/workshop/aml/jobs/index.get.ts +26 -26
- package/server/api/workshop/aml/models/[id]/predict.post.ts +24 -24
- package/server/api/workshop/aml/models/[id]/promote.post.ts +30 -30
- package/server/api/workshop/aml/models/index.get.ts +48 -48
- package/server/api/workshop/aml/training-plans/[id]/train.post.ts +18 -18
- package/server/api/workshop/aml/training-plans/[id].delete.ts +18 -18
- package/server/api/workshop/aml/training-plans/[id].patch.ts +30 -30
- package/server/api/workshop/aml/training-plans/index.get.ts +9 -9
- package/server/api/workshop/aml/training-plans/index.post.ts +43 -43
- package/server/api/workshop/approvals/[id]/decide.post.ts +30 -30
- package/server/api/workshop/approvals/index.get.ts +22 -22
- package/server/api/workshop/audit.get.ts +43 -43
- package/server/api/workshop/channels/[id]/chat/messages/[messageId]/read.post.ts +22 -22
- package/server/api/workshop/channels/[id]/chat/messages/index.get.ts +29 -29
- package/server/api/workshop/channels/[id]/chat/messages/index.post.ts +51 -51
- package/server/api/workshop/channels/[id]/chat/permissions.get.ts +35 -35
- package/server/api/workshop/channels/[id]/index.patch.ts +64 -64
- package/server/api/workshop/channels/[id]/members/[userId]/approve.post.ts +20 -20
- package/server/api/workshop/channels/[id]/members/[userId].delete.ts +21 -21
- package/server/api/workshop/channels/[id]/members/index.get.ts +23 -23
- package/server/api/workshop/channels/[id]/members/leave.post.ts +20 -20
- package/server/api/workshop/channels/[id]/twin-profile.get.ts +45 -45
- package/server/api/workshop/channels/[id]/twin-profile.patch.ts +72 -72
- package/server/api/workshop/daq/[id]/test.post.ts +1 -1
- package/server/api/workshop/daq/alarms.get.ts +16 -16
- package/server/api/workshop/dcw/[id]/write.post.ts +4 -4
- package/server/api/workshop/dcw/[id].delete.ts +41 -41
- package/server/api/workshop/dcw/[id].patch.ts +40 -40
- package/server/api/workshop/dcw/mes-test-read.post.ts +24 -24
- package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -17
- package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -21
- package/server/api/workshop/dcw/params/[id].delete.ts +19 -19
- package/server/api/workshop/dcw/params/[id].patch.ts +23 -23
- package/server/api/workshop/dcw/params/index.get.ts +27 -27
- package/server/api/workshop/dcw/params/index.post.ts +25 -25
- package/server/api/workshop/dcw/products/[id].delete.ts +16 -16
- package/server/api/workshop/dcw/products/[id].patch.ts +17 -17
- package/server/api/workshop/dcw/recipes/[id]/index.patch.ts +22 -22
- package/server/api/workshop/dcw/recipes/[id]/versions.get.ts +14 -14
- package/server/api/workshop/dcw/recipes/[id].delete.ts +25 -25
- package/server/api/workshop/dcw/recipes/index.get.ts +12 -12
- package/server/api/workshop/dcw/runs/[id]/close.post.ts +15 -15
- package/server/api/workshop/dcw/templates/[key].delete.ts +14 -14
- package/server/api/workshop/dcw/templates/[key].patch.ts +16 -16
- package/server/api/workshop/dcw/templates/index.post.ts +15 -15
- package/server/api/workshop/dcw/test-driver.post.ts +16 -16
- package/server/api/workshop/device-twins/[id].delete.ts +25 -25
- package/server/api/workshop/eda/runs/[id]/labels.patch.ts +25 -25
- package/server/api/workshop/exp/confirmations/[id]/decide.post.ts +41 -41
- package/server/api/workshop/exp/confirmations/index.get.ts +25 -25
- package/server/api/workshop/harnesses/[id]/providers.get.ts +16 -16
- package/server/api/workshop/hitl/respond.post.ts +94 -94
- package/server/api/workshop/notifications/index.get.ts +60 -60
- package/server/api/workshop/notifications/read.post.ts +31 -31
- package/server/api/workshop/ontology/drafts/[draftId]/subgraph.ts +24 -24
- package/server/api/workshop/plugins/[name]/disable.post.ts +21 -21
- package/server/api/workshop/plugins/[name]/enable.post.ts +21 -21
- package/server/api/workshop/plugins/[name]/index.get.ts +14 -14
- package/server/api/workshop/schedules/[id]/run.post.ts +15 -15
- package/server/api/workshop/schedules/[id]/runs.get.ts +15 -15
- package/server/api/workshop/schedules/[id].delete.ts +14 -14
- package/server/api/workshop/schedules/[id].get.ts +13 -13
- package/server/api/workshop/schedules/[id].patch.ts +29 -29
- package/server/api/workshop/schedules/index.get.ts +12 -12
- package/server/api/workshop/schedules/index.post.ts +32 -32
- package/server/api/workshop/tasks/[id]/reassign.post.ts +32 -32
- package/server/api/workshop/tasks/[id]/update.post.ts +35 -35
- package/server/api/workshop/teams/[id]/plugins/index.get.ts +36 -36
- package/server/api/workshop/teams/[id]/plugins/index.put.ts +42 -42
- package/server/api/workshop/ws/audience.ts +56 -56
- package/server/api/workshop/ws/db-buffer.ts +68 -68
- package/server/api/workshop/ws/event-mapping.ts +47 -47
- package/server/api/workshop/ws/handlers.ts +132 -132
- package/server/api/workshop/ws/hub.ts +133 -133
- package/server/api/workshop/ws/index.ts +23 -23
- package/server/api/workshop/ws/notification-uplink.ts +77 -77
- package/server/api/workshop/ws/shared.ts +94 -94
- package/server/api/workshop/ws/streams.ts +146 -146
- package/server/middleware/00-security-headers.ts +38 -38
- package/server/middleware/01-rate-guard.ts +101 -101
- package/server/plugins/aml-twin-worker.ts +32 -32
- package/server/plugins/dev-stability-guard.ts +42 -42
- package/server/plugins-builtin/diag-bridge/client.mjs +32 -12
- package/server/plugins-builtin/diag-bridge/host/constants.mjs +25 -25
- package/server/plugins-builtin/diag-bridge/host/exports.mjs +104 -104
- package/server/plugins-builtin/diag-bridge/host/helpers.mjs +39 -9
- package/server/plugins-builtin/diag-bridge/host/index.mjs +15 -15
- package/server/plugins-builtin/diag-bridge/host/intents.mjs +62 -62
- package/server/plugins-builtin/diag-bridge/host/plugin.mjs +60 -26
- package/server/plugins-builtin/diag-bridge/host/snapshot.mjs +93 -93
- package/server/plugins-builtin/diag-bridge/index.mjs +9 -9
- package/server/plugins-builtin/serial-bridge/client.mjs +211 -211
- package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -409
- package/server/plugins-builtin/serial-bridge/i18n.json +62 -62
- package/server/plugins-builtin/serial-bridge/index.mjs +247 -247
- package/server/plugins-builtin/serial-bridge/lib.mjs +208 -208
- package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -168
- package/server/services/system-config/contracts.ts +29 -29
- package/server/services/system-config/dispose.ts +19 -19
- package/server/services/system-config/facade.ts +26 -26
- package/server/services/system-config/groups.ts +108 -108
- package/server/services/system-config/index.ts +21 -21
- package/server/services/system-config/init.ts +26 -26
- package/server/services/system-config/mutate.ts +103 -103
- package/server/services/system-config/plugins.ts +104 -104
- package/server/services/system-config/snapshot.ts +59 -59
- package/server/services/system-config/state.ts +79 -79
- package/server/services/system-config/types.ts +58 -58
- package/server/services/workshop/agents/adapters/omp-rpc-client/api.ts +111 -111
- package/server/services/workshop/agents/adapters/omp-rpc-client/contracts.ts +22 -22
- package/server/services/workshop/agents/adapters/omp-rpc-client/facade.ts +7 -7
- package/server/services/workshop/agents/adapters/omp-rpc-client/frames.ts +0 -18
- package/server/services/workshop/agents/adapters/omp-rpc-client/helpers.ts +38 -38
- package/server/services/workshop/agents/adapters/omp-rpc-client/index.ts +25 -25
- package/server/services/workshop/agents/adapters/omp-rpc-client/start.ts +70 -70
- package/server/services/workshop/agents/adapters/omp-rpc-client/state.ts +65 -65
- package/server/services/workshop/agents/adapters/omp-rpc-client/stdout.ts +92 -92
- package/server/services/workshop/agents/adapters/omp-rpc-client/usage.ts +47 -47
- package/server/services/workshop/agents/agent-badge.ts +38 -38
- package/server/services/workshop/agents/binding-authz.ts +60 -60
- package/server/services/workshop/agents/binding-mode.ts +51 -51
- package/server/services/workshop/agents/codex-agent/client.ts +170 -170
- package/server/services/workshop/agents/codex-agent/core.ts +119 -119
- package/server/services/workshop/agents/codex-agent/facade.ts +8 -8
- package/server/services/workshop/agents/codex-agent/helpers.ts +13 -13
- package/server/services/workshop/agents/codex-agent/hitl.ts +138 -138
- package/server/services/workshop/agents/codex-agent/index.ts +14 -14
- package/server/services/workshop/agents/codex-agent/turn.ts +367 -367
- package/server/services/workshop/agents/codex-agent/types.ts +42 -42
- package/server/services/workshop/agents/harness-process.ts +3 -3
- package/server/services/workshop/agents/harness-terminal/api.ts +197 -197
- package/server/services/workshop/agents/harness-terminal/index.ts +17 -17
- package/server/services/workshop/agents/harness-terminal/io.ts +120 -120
- package/server/services/workshop/agents/harness-terminal/park.ts +55 -55
- package/server/services/workshop/agents/harness-terminal/sanitize.ts +98 -98
- package/server/services/workshop/agents/harness-terminal/session.ts +124 -124
- package/server/services/workshop/agents/harness-terminal/shared.ts +74 -74
- package/server/services/workshop/agents/hitl-capabilities.ts +312 -312
- package/server/services/workshop/agents/hitl-decision/answers.ts +32 -32
- package/server/services/workshop/agents/hitl-decision/decide.ts +268 -268
- package/server/services/workshop/agents/hitl-decision/payload.ts +45 -45
- package/server/services/workshop/agents/hitl-decision/shared.ts +108 -108
- package/server/services/workshop/agents/hitl-decision/visibility.ts +94 -94
- package/server/services/workshop/agents/host-tool-bridge/tools/progress.ts +84 -84
- package/server/services/workshop/agents/host-tool-bridge/tools/team-delegation.ts +104 -104
- package/server/services/workshop/agents/host-tool-bridge/tools/team.ts +70 -70
- package/server/services/workshop/agents/industrial/aml-activity.ts +131 -131
- package/server/services/workshop/agents/industrial/audit-query.ts +66 -66
- package/server/services/workshop/agents/industrial/daq-tools.ts +352 -352
- package/server/services/workshop/agents/industrial/exp-tools.ts +104 -104
- package/server/services/workshop/agents/industrial/ops-tools.ts +16 -54
- package/server/services/workshop/agents/industrial/param-tools.ts +69 -69
- package/server/services/workshop/agents/industrial/recipe-gate.ts +6 -19
- package/server/services/workshop/agents/industrial/recipe-propose-tools.ts +1 -1
- package/server/services/workshop/agents/node-bindings.repo.ts +207 -207
- package/server/services/workshop/agents/node-delegation.ts +159 -159
- package/server/services/workshop/agents/omp-agent/core.ts +146 -146
- package/server/services/workshop/agents/omp-agent/facade.ts +8 -8
- package/server/services/workshop/agents/omp-agent/helpers.ts +37 -37
- package/server/services/workshop/agents/omp-agent/index.ts +16 -16
- package/server/services/workshop/agents/omp-agent/tools-steer.ts +1 -18
- package/server/services/workshop/agents/omp-agent/types.ts +45 -45
- package/server/services/workshop/agents/opencode-agent/core.ts +77 -77
- package/server/services/workshop/agents/opencode-agent/facade.ts +8 -8
- package/server/services/workshop/agents/opencode-agent/helpers.ts +29 -29
- package/server/services/workshop/agents/opencode-agent/hitl.ts +113 -113
- package/server/services/workshop/agents/opencode-agent/index.ts +16 -16
- package/server/services/workshop/agents/opencode-agent/lifecycle.ts +44 -44
- package/server/services/workshop/agents/opencode-agent/server.ts +244 -244
- package/server/services/workshop/agents/opencode-agent/turn.ts +278 -278
- package/server/services/workshop/agents/opencode-agent/types.ts +54 -54
- package/server/services/workshop/agents/tool-classes.ts +44 -44
- package/server/services/workshop/aml/clean.ts +120 -120
- package/server/services/workshop/aml/entity.ts +367 -367
- package/server/services/workshop/aml/job-orchestrator/broadcast.ts +37 -37
- package/server/services/workshop/aml/job-orchestrator/control.ts +94 -94
- package/server/services/workshop/aml/job-orchestrator/failure.ts +96 -96
- package/server/services/workshop/aml/job-orchestrator/index.ts +22 -22
- package/server/services/workshop/aml/job-orchestrator/process.ts +101 -101
- package/server/services/workshop/aml/job-orchestrator/shared.ts +53 -53
- package/server/services/workshop/aml/job-orchestrator/status.ts +20 -20
- package/server/services/workshop/aml/job-orchestrator/tick.ts +37 -37
- package/server/services/workshop/aml/python/train-example.py +92 -92
- package/server/services/workshop/aml/runtime.ts +137 -137
- package/server/services/workshop/aml/stats.ts +140 -140
- package/server/services/workshop/aml/twin/calibration-worker.ts +150 -150
- package/server/services/workshop/aml/twin/model-backed-provider.ts +332 -332
- package/server/services/workshop/aml/twin/physics-spec-draft.ts +185 -185
- package/server/services/workshop/aml/twin/prompt-composer.ts +143 -143
- package/server/services/workshop/aml/twin/training-plans.ts +260 -260
- package/server/services/workshop/aml/twin/write-guard.ts +19 -19
- package/server/services/workshop/assets/character-asset.repo.ts +1 -1
- package/server/services/workshop/assets/device-twin.repo.ts +1 -1
- package/server/services/workshop/daq/bus/index.ts +5 -12
- package/server/services/workshop/daq/daq-controller/contracts.ts +39 -39
- package/server/services/workshop/daq/daq-controller/facade.ts +28 -28
- package/server/services/workshop/daq/daq-controller/frames.ts +137 -137
- package/server/services/workshop/daq/daq-controller/helpers.ts +52 -52
- package/server/services/workshop/daq/daq-controller/index.ts +22 -22
- package/server/services/workshop/daq/daq-controller/runtime-control.ts +72 -72
- package/server/services/workshop/daq/daq-controller/sweep-alarms.ts +176 -176
- package/server/services/workshop/daq/daq-controller/tsdb-twin.ts +138 -138
- package/server/services/workshop/daq/drivers/http.ts +95 -95
- package/server/services/workshop/daq/drivers/mock.ts +130 -130
- package/server/services/workshop/daq/drivers/modbus-rtu.ts +67 -67
- package/server/services/workshop/daq/drivers/modbus-tcp.ts +307 -307
- package/server/services/workshop/daq/drivers/opcua.ts +227 -227
- package/server/services/workshop/daq/drivers/s7-stub.ts +29 -29
- package/server/services/workshop/daq/host-bindings.ts +71 -71
- package/server/services/workshop/daq/host-ports.ts +65 -65
- package/server/services/workshop/db/aml-team-seeds.ts +118 -118
- package/server/services/workshop/db/backup-registry.ts +33 -33
- package/server/services/workshop/db/channel-member.repo.ts +122 -122
- package/server/services/workshop/db/channel.repo.ts +203 -203
- package/server/services/workshop/db/chat-message.repo.ts +233 -233
- package/server/services/workshop/db/database/index.ts +18 -18
- package/server/services/workshop/db/database/json.ts +14 -14
- package/server/services/workshop/db/database/open.ts +97 -97
- package/server/services/workshop/db/database/seed-data.ts +60 -60
- package/server/services/workshop/db/hitl-request.repo.ts +221 -221
- package/server/services/workshop/db/notification.repo.ts +199 -199
- package/server/services/workshop/db/readonly.ts +30 -30
- package/server/services/workshop/db/scheduled-task.repo.ts +235 -235
- package/server/services/workshop/db/transaction.ts +72 -72
- package/server/services/workshop/dcw/dcw-controller/contracts.ts +16 -16
- package/server/services/workshop/dcw/dcw-controller/control.ts +78 -78
- package/server/services/workshop/dcw/dcw-controller/facade.ts +19 -19
- package/server/services/workshop/dcw/dcw-controller/index.ts +22 -22
- package/server/services/workshop/dcw/dcw-controller/params.ts +97 -97
- package/server/services/workshop/dcw/dcw-controller/products.ts +103 -103
- package/server/services/workshop/dcw/dcw-controller/write-audit.ts +49 -49
- package/server/services/workshop/dcw/dcw-templates.ts +136 -136
- package/server/services/workshop/dcw/param-conversion.ts +78 -78
- package/server/services/workshop/dcw/param-map.repo.ts +349 -349
- package/server/services/workshop/dcw/plugin-bridge.ts +45 -45
- package/server/services/workshop/dcw/recipe-rollback-manager/contracts.ts +26 -26
- package/server/services/workshop/dcw/recipe-rollback-manager/facade.ts +13 -13
- package/server/services/workshop/dcw/recipe-rollback-manager/helpers.ts +30 -30
- package/server/services/workshop/dcw/recipe-rollback-manager/index.ts +17 -17
- package/server/services/workshop/dcw/recipe-rollback-manager/internal.ts +33 -33
- package/server/services/workshop/dcw/recipe-rollback-manager/judge.ts +52 -52
- package/server/services/workshop/dcw/recipe-rollback-manager/lock.ts +137 -137
- package/server/services/workshop/dcw/recipe-rollback-manager/rollback.ts +190 -190
- package/server/services/workshop/dcw/recipe-rollback-manager/state.ts +21 -21
- package/server/services/workshop/dcw/recipe-rollback-manager/sweep.ts +77 -77
- package/server/services/workshop/dcw/recipe-rollback-manager/types.ts +3 -3
- package/server/services/workshop/dcw/recipe-rollback-manager/views.ts +214 -214
- package/server/services/workshop/exp/exp-collector.ts +275 -275
- package/server/services/workshop/exp/exp-state.repo.ts +339 -339
- package/server/services/workshop/mes/mes-controller.ts +1155 -1155
- package/server/services/workshop/mes/mes-hook.ts +567 -567
- package/server/services/workshop/ontology/subgraph.core.ts +300 -300
- package/server/services/workshop/ontology/subgraph.service.ts +113 -113
- package/server/services/workshop/plugins/host/api.mjs +90 -90
- package/server/services/workshop/plugins/host/config.mjs +135 -135
- package/server/services/workshop/plugins/host/index.mjs +23 -23
- package/server/services/workshop/plugins/host/init.mjs +109 -109
- package/server/services/workshop/plugins/host/load.mjs +321 -321
- package/server/services/workshop/plugins/host/reload.mjs +94 -94
- package/server/services/workshop/plugins/host/rollback.mjs +72 -72
- package/server/services/workshop/plugins/host/state.mjs +54 -54
- package/server/services/workshop/plugins/host/toggle.mjs +64 -64
- package/server/services/workshop/plugins/host.mjs +9 -9
- package/server/services/workshop/runtime/agent-runtime/facade.ts +7 -7
- package/server/services/workshop/runtime/agent-runtime/helpers.ts +18 -18
- package/server/services/workshop/runtime/agent-runtime/index.ts +19 -19
- package/server/services/workshop/runtime/agent-runtime/lifecycle.ts +137 -137
- package/server/services/workshop/runtime/chat-mention.ts +164 -164
- package/server/services/workshop/runtime/chat-projection.ts +246 -246
- package/server/services/workshop/runtime/manager/admin-agents.ts +268 -268
- package/server/services/workshop/runtime/manager/bus.ts +182 -182
- package/server/services/workshop/runtime/manager/channel-template-constants.ts +20 -20
- package/server/services/workshop/runtime/manager/chat-reply.ts +181 -181
- package/server/services/workshop/runtime/manager/chat.ts +384 -384
- package/server/services/workshop/runtime/manager/facade.ts +24 -24
- package/server/services/workshop/runtime/manager/helpers.ts +119 -119
- package/server/services/workshop/runtime/manager/index.ts +42 -42
- package/server/services/workshop/runtime/manager/internal.ts +73 -73
- package/server/services/workshop/runtime/manager/notifications.ts +108 -108
- package/server/services/workshop/runtime/manager/teams.ts +148 -148
- package/server/services/workshop/runtime/memory/contracts.ts +18 -18
- package/server/services/workshop/runtime/memory/facade.ts +10 -10
- package/server/services/workshop/runtime/memory/helpers.ts +196 -196
- package/server/services/workshop/runtime/memory/index.ts +27 -27
- package/server/services/workshop/runtime/memory/score.ts +33 -33
- package/server/services/workshop/runtime/memory/state.ts +40 -40
- package/server/services/workshop/runtime/monitor.ts +3 -3
- package/server/services/workshop/runtime/platform-notice.ts +113 -113
- package/server/services/workshop/runtime/schedule-runtime.ts +347 -347
- package/server/services/workshop/runtime/scheduler-loop/contracts.ts +29 -29
- package/server/services/workshop/runtime/scheduler-loop/facade.ts +7 -7
- package/server/services/workshop/runtime/scheduler-loop/helpers.ts +14 -14
- package/server/services/workshop/runtime/scheduler-loop/index.ts +16 -16
- package/server/services/workshop/runtime/task-engine/facade.ts +7 -7
- package/server/services/workshop/runtime/task-engine/index.ts +15 -15
- package/server/services/workshop/runtime/task-engine/types.ts +3 -3
- package/server/services/workshop/runtime/user-notification-hub.ts +192 -192
- package/server/types/pg.d.ts +49 -49
- package/server/utils/approval-gate.ts +67 -67
- package/server/utils/mojibake-guard.ts +29 -29
- package/server/utils/stability-severity.ts +77 -77
- package/shared/config/groups.mjs +241 -241
- package/shared/dcw-protocol/catalog.ts +232 -232
- package/shared/dcw-protocol/closed-loop.ts +137 -137
- package/shared/lru.mjs +97 -97
- package/tui/README.md +74 -74
- package/tui/aw-tui.mjs +445 -445
- package/tui/commands/index.mjs +287 -287
- package/tui/components/chat-log.mjs +42 -42
- package/tui/components/monitor-pane.mjs +39 -39
- package/tui/components/root.mjs +53 -53
- package/tui/components/status-bar.mjs +33 -33
- package/tui/lib/api.mjs +82 -82
- package/tui/lib/pickers.mjs +121 -121
- package/tui/lib/reducers.mjs +218 -218
- package/tui/lib/state.mjs +86 -86
- package/tui/lib/virtual-terminal.mjs +48 -48
- package/tui/lib/ws.mjs +125 -125
- package/tui/theme.mjs +38 -38
- package/.output/.AgentWorkShop/aml-python/__pycache__/aml_eval.cpython-312.pyc +0 -0
- package/.output/.AgentWorkShop/aml-python/__pycache__/amlkit.cpython-312.pyc +0 -0
- package/.output/.AgentWorkShop/aml-python/__pycache__/train-example.cpython-312.pyc +0 -0
- package/.output/.AgentWorkShop/aml-python/__pycache__/train-hybrid-example.cpython-312.pyc +0 -0
- package/.output/public/_nuxt/BRYBzVMJ.js +0 -1
- package/.output/public/_nuxt/BW2uTUx52.js +0 -2
- package/.output/public/_nuxt/BtZKe9ZV.js +0 -1
- package/.output/public/_nuxt/CfFUK6932.js +0 -2
- package/.output/public/_nuxt/DhW7cFOa.js +0 -1
- package/.output/public/_nuxt/OmpTerminalPanel.ComMDrA8.css +0 -1
- package/.output/public/_nuxt/PluginSlot.CGH0vb95.css +0 -1
- package/.output/public/_nuxt/TownView.CC3t-Dte.css +0 -1
- package/.output/public/_nuxt/aml.PalMp-5n.css +0 -1
- package/.output/public/_nuxt/builds/meta/fa0af8b5-3929-428d-a6de-3b5cc6b8248f.json +0 -1
- package/.output/public/_nuxt/permissions.DxuOOIqQ.css +0 -1
- package/.output/public/_nuxt/schedules.BXNJ9q_I.css +0 -1
- package/.output/public/_nuxt/tools.BwXFAb-X.css +0 -1
- package/.output/server/chunks/build/AuthGate-nF1evO4v.mjs +0 -716
- package/.output/server/chunks/build/AuthGate-nF1evO4v.mjs.map +0 -1
- package/.output/server/chunks/build/FormItemContext-BlyzocS0.mjs +0 -72
- package/.output/server/chunks/build/FormItemContext-BlyzocS0.mjs.map +0 -1
- package/.output/server/chunks/build/OmpTerminalPanel-BhvhQ1gG.mjs +0 -942
- package/.output/server/chunks/build/OmpTerminalPanel-BhvhQ1gG.mjs.map +0 -1
- package/.output/server/chunks/build/PluginSlot-BUKXwV2X.mjs +0 -141
- package/.output/server/chunks/build/PluginSlot-BUKXwV2X.mjs.map +0 -1
- package/.output/server/chunks/build/TownView-BWMDbosY.mjs +0 -6489
- package/.output/server/chunks/build/TownView-BWMDbosY.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-BK-t8nRG.mjs +0 -392
- package/.output/server/chunks/build/_id_-BK-t8nRG.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-CZPlPDm4.mjs +0 -2045
- package/.output/server/chunks/build/_id_-CZPlPDm4.mjs.map +0 -1
- package/.output/server/chunks/build/_wsId_-BPy-Aj9L.mjs +0 -14297
- package/.output/server/chunks/build/_wsId_-BPy-Aj9L.mjs.map +0 -1
- package/.output/server/chunks/build/agents-CpNLdI8E.mjs +0 -1078
- package/.output/server/chunks/build/agents-CpNLdI8E.mjs.map +0 -1
- package/.output/server/chunks/build/alert-D0ivtTGy.mjs +0 -263
- package/.output/server/chunks/build/alert-D0ivtTGy.mjs.map +0 -1
- package/.output/server/chunks/build/aml-DRAFRcGU.mjs +0 -814
- package/.output/server/chunks/build/aml-DRAFRcGU.mjs.map +0 -1
- package/.output/server/chunks/build/analysis-p-dBkjoK.mjs +0 -533
- package/.output/server/chunks/build/analysis-p-dBkjoK.mjs.map +0 -1
- package/.output/server/chunks/build/api-B4Om0mEc.mjs.map +0 -1
- package/.output/server/chunks/build/api-BEW_ebY_.mjs.map +0 -1
- package/.output/server/chunks/build/api-BsAgXWxi.mjs.map +0 -1
- package/.output/server/chunks/build/api-error-BwAQLw44.mjs +0 -47
- package/.output/server/chunks/build/api-error-BwAQLw44.mjs.map +0 -1
- package/.output/server/chunks/build/apiClient-BvXtNzC8.mjs +0 -81
- package/.output/server/chunks/build/apiClient-BvXtNzC8.mjs.map +0 -1
- package/.output/server/chunks/build/board-Dl2Ar22r.mjs +0 -178
- package/.output/server/chunks/build/board-Dl2Ar22r.mjs.map +0 -1
- package/.output/server/chunks/build/card-Cqk0Ur-L.mjs +0 -953
- package/.output/server/chunks/build/card-Cqk0Ur-L.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-ByHOQQkO.mjs +0 -942
- package/.output/server/chunks/build/channel-templates-ByHOQQkO.mjs.map +0 -1
- package/.output/server/chunks/build/checkbox-CrT4cnfu.mjs +0 -439
- package/.output/server/chunks/build/checkbox-CrT4cnfu.mjs.map +0 -1
- package/.output/server/chunks/build/client-only-qNA2OVV_.mjs +0 -43
- package/.output/server/chunks/build/client-only-qNA2OVV_.mjs.map +0 -1
- package/.output/server/chunks/build/core-CgTOPS4t.mjs +0 -294
- package/.output/server/chunks/build/core-CgTOPS4t.mjs.map +0 -1
- package/.output/server/chunks/build/daq-CdxcfY_G.mjs +0 -1266
- package/.output/server/chunks/build/daq-CdxcfY_G.mjs.map +0 -1
- package/.output/server/chunks/build/data-D8uX1dnm.mjs +0 -481
- package/.output/server/chunks/build/data-D8uX1dnm.mjs.map +0 -1
- package/.output/server/chunks/build/dcw-BlDQtS4q.mjs +0 -505
- package/.output/server/chunks/build/dcw-BlDQtS4q.mjs.map +0 -1
- package/.output/server/chunks/build/default-CNWS6NUw.mjs +0 -2353
- package/.output/server/chunks/build/default-CNWS6NUw.mjs.map +0 -1
- package/.output/server/chunks/build/descriptions-BNlfKG9y.mjs +0 -406
- package/.output/server/chunks/build/descriptions-BNlfKG9y.mjs.map +0 -1
- package/.output/server/chunks/build/drawer-Caji0PLs.mjs +0 -834
- package/.output/server/chunks/build/drawer-Caji0PLs.mjs.map +0 -1
- package/.output/server/chunks/build/dropdown-BNwTVGde.mjs +0 -14
- package/.output/server/chunks/build/dropdown-BNwTVGde.mjs.map +0 -1
- package/.output/server/chunks/build/dropdown-Cd-xPhR6.mjs +0 -540
- package/.output/server/chunks/build/dropdown-Cd-xPhR6.mjs.map +0 -1
- package/.output/server/chunks/build/es-Bxu10NJ2.mjs.map +0 -1
- package/.output/server/chunks/build/firstNotUndefined-8H5_qHUu.mjs +0 -2448
- package/.output/server/chunks/build/firstNotUndefined-8H5_qHUu.mjs.map +0 -1
- package/.output/server/chunks/build/form-D_UpWPN4.mjs +0 -4712
- package/.output/server/chunks/build/form-D_UpWPN4.mjs.map +0 -1
- package/.output/server/chunks/build/hasIn-DBdElbjM.mjs +0 -553
- package/.output/server/chunks/build/hasIn-DBdElbjM.mjs.map +0 -1
- package/.output/server/chunks/build/hitl-Bd30yr1J.mjs +0 -132
- package/.output/server/chunks/build/hitl-Bd30yr1J.mjs.map +0 -1
- package/.output/server/chunks/build/input-GtPuSEgt.mjs +0 -2181
- package/.output/server/chunks/build/input-GtPuSEgt.mjs.map +0 -1
- package/.output/server/chunks/build/input-number-BNlNDK7H.mjs +0 -1120
- package/.output/server/chunks/build/input-number-BNlNDK7H.mjs.map +0 -1
- package/.output/server/chunks/build/interval-DvHBIIZh.mjs +0 -9
- package/.output/server/chunks/build/interval-DvHBIIZh.mjs.map +0 -1
- package/.output/server/chunks/build/isNumeric-CP7q_yhz.mjs +0 -7
- package/.output/server/chunks/build/isNumeric-CP7q_yhz.mjs.map +0 -1
- package/.output/server/chunks/build/logs-pRb-TMWa.mjs +0 -427
- package/.output/server/chunks/build/logs-pRb-TMWa.mjs.map +0 -1
- package/.output/server/chunks/build/menu-BXt-X0dt.mjs +0 -1952
- package/.output/server/chunks/build/menu-BXt-X0dt.mjs.map +0 -1
- package/.output/server/chunks/build/monitor-Cq6hRFeQ.mjs +0 -2253
- package/.output/server/chunks/build/monitor-Cq6hRFeQ.mjs.map +0 -1
- package/.output/server/chunks/build/ontology-DK0Mf0zT.mjs +0 -1406
- package/.output/server/chunks/build/ontology-DK0Mf0zT.mjs.map +0 -1
- package/.output/server/chunks/build/operations-SMQrRAcr.mjs +0 -311
- package/.output/server/chunks/build/operations-SMQrRAcr.mjs.map +0 -1
- package/.output/server/chunks/build/optimizer-BlWTsM-W.mjs +0 -202
- package/.output/server/chunks/build/optimizer-BlWTsM-W.mjs.map +0 -1
- package/.output/server/chunks/build/pages-AFRP4-EL.mjs +0 -542
- package/.output/server/chunks/build/pages-AFRP4-EL.mjs.map +0 -1
- package/.output/server/chunks/build/pagination-DTAconUo.mjs +0 -1346
- package/.output/server/chunks/build/pagination-DTAconUo.mjs.map +0 -1
- package/.output/server/chunks/build/permissions-DgXQZSyx.mjs +0 -637
- package/.output/server/chunks/build/permissions-DgXQZSyx.mjs.map +0 -1
- package/.output/server/chunks/build/plugins-LRjaSEZy.mjs +0 -272
- package/.output/server/chunks/build/plugins-LRjaSEZy.mjs.map +0 -1
- package/.output/server/chunks/build/popconfirm-DxYPGRgS.mjs +0 -340
- package/.output/server/chunks/build/popconfirm-DxYPGRgS.mjs.map +0 -1
- package/.output/server/chunks/build/responsiveObserve-DM3hpzhU.mjs +0 -78
- package/.output/server/chunks/build/responsiveObserve-DM3hpzhU.mjs.map +0 -1
- package/.output/server/chunks/build/schedules-dSnr8url.mjs +0 -7172
- package/.output/server/chunks/build/schedules-dSnr8url.mjs.map +0 -1
- package/.output/server/chunks/build/segmented-D3HWtHPw.mjs +0 -341
- package/.output/server/chunks/build/segmented-D3HWtHPw.mjs.map +0 -1
- package/.output/server/chunks/build/select-B3Dsb9yq.mjs +0 -3923
- package/.output/server/chunks/build/select-B3Dsb9yq.mjs.map +0 -1
- package/.output/server/chunks/build/settings-BrIhnS2Z.mjs +0 -1014
- package/.output/server/chunks/build/settings-BrIhnS2Z.mjs.map +0 -1
- package/.output/server/chunks/build/space-Dwu2Skt8.mjs +0 -110
- package/.output/server/chunks/build/space-Dwu2Skt8.mjs.map +0 -1
- package/.output/server/chunks/build/spin-C2cuvGth.mjs +0 -357
- package/.output/server/chunks/build/spin-C2cuvGth.mjs.map +0 -1
- package/.output/server/chunks/build/studio-cdoNCfyy.mjs +0 -332
- package/.output/server/chunks/build/studio-cdoNCfyy.mjs.map +0 -1
- package/.output/server/chunks/build/switch-CyR-H6Fb.mjs +0 -377
- package/.output/server/chunks/build/switch-CyR-H6Fb.mjs.map +0 -1
- package/.output/server/chunks/build/table-CmVD-cA9.mjs +0 -7546
- package/.output/server/chunks/build/table-CmVD-cA9.mjs.map +0 -1
- package/.output/server/chunks/build/tabs-Vg0e7cZE.mjs +0 -1924
- package/.output/server/chunks/build/tabs-Vg0e7cZE.mjs.map +0 -1
- package/.output/server/chunks/build/tag-y0sn-xv9.mjs +0 -1156
- package/.output/server/chunks/build/tag-y0sn-xv9.mjs.map +0 -1
- package/.output/server/chunks/build/teams-B1d32eHK.mjs +0 -1203
- package/.output/server/chunks/build/teams-B1d32eHK.mjs.map +0 -1
- package/.output/server/chunks/build/tokens-GYaGJ35Z.mjs +0 -1062
- package/.output/server/chunks/build/tokens-GYaGJ35Z.mjs.map +0 -1
- package/.output/server/chunks/build/tools-s_9U1Djj.mjs +0 -487
- package/.output/server/chunks/build/tools-s_9U1Djj.mjs.map +0 -1
- package/.output/server/chunks/build/tooltip-C2n2nNiQ.mjs +0 -920
- package/.output/server/chunks/build/tooltip-C2n2nNiQ.mjs.map +0 -1
- package/.output/server/chunks/build/town-B5aoiG98.mjs +0 -133
- package/.output/server/chunks/build/town-B5aoiG98.mjs.map +0 -1
- package/.output/server/chunks/build/useAgentHarnessOptions-DOrQHeBh.mjs +0 -124
- package/.output/server/chunks/build/useAgentHarnessOptions-DOrQHeBh.mjs.map +0 -1
- package/.output/server/chunks/build/useAgentTemplateActions-CzlplRWh.mjs +0 -77
- package/.output/server/chunks/build/useAgentTemplateActions-CzlplRWh.mjs.map +0 -1
- package/.output/server/chunks/build/useAgentTemplateForm-DQo1enf2.mjs +0 -117
- package/.output/server/chunks/build/useAgentTemplateForm-DQo1enf2.mjs.map +0 -1
- package/.output/server/chunks/build/useAgentTemplatesCatalog-DP-iMmqz.mjs +0 -133
- package/.output/server/chunks/build/useAgentTemplatesCatalog-DP-iMmqz.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlDatasets-DeoE1idn.mjs +0 -92
- package/.output/server/chunks/build/useAmlDatasets-DeoE1idn.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlEntities-BJhvdqHJ.mjs +0 -86
- package/.output/server/chunks/build/useAmlEntities-BJhvdqHJ.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlEnv-CDIZ6xTe.mjs +0 -194
- package/.output/server/chunks/build/useAmlEnv-CDIZ6xTe.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlExperiments-Dloi834i.mjs +0 -89
- package/.output/server/chunks/build/useAmlExperiments-Dloi834i.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlJobs-BF9LpwXz.mjs +0 -267
- package/.output/server/chunks/build/useAmlJobs-BF9LpwXz.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlModels-_bweOr3R.mjs +0 -93
- package/.output/server/chunks/build/useAmlModels-_bweOr3R.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlNotes-DQj980iX.mjs +0 -86
- package/.output/server/chunks/build/useAmlNotes-DQj980iX.mjs.map +0 -1
- package/.output/server/chunks/build/useAmlPredict-DHNgDSJn.mjs +0 -130
- package/.output/server/chunks/build/useAmlPredict-DHNgDSJn.mjs.map +0 -1
- package/.output/server/chunks/build/useAnalysisFeed-BrEGV_9x.mjs +0 -129
- package/.output/server/chunks/build/useAnalysisFeed-BrEGV_9x.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqAddNode-D_Rkju0f.mjs +0 -142
- package/.output/server/chunks/build/useDaqAddNode-D_Rkju0f.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqDetailBinding-DCWaLKXT.mjs +0 -70
- package/.output/server/chunks/build/useDaqDetailBinding-DCWaLKXT.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqDetailFrames-BOIcOJRB.mjs +0 -79
- package/.output/server/chunks/build/useDaqDetailFrames-BOIcOJRB.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqDetailHistory-D6WulPVs.mjs +0 -176
- package/.output/server/chunks/build/useDaqDetailHistory-D6WulPVs.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqDetailNode-DeOsDxK5.mjs +0 -99
- package/.output/server/chunks/build/useDaqDetailNode-DeOsDxK5.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqDetailParams-Cm9a9r5G.mjs +0 -159
- package/.output/server/chunks/build/useDaqDetailParams-Cm9a9r5G.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqFilters-CctmO03p.mjs +0 -222
- package/.output/server/chunks/build/useDaqFilters-CctmO03p.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqOptimizations-CmX_dpBc.mjs +0 -136
- package/.output/server/chunks/build/useDaqOptimizations-CmX_dpBc.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqStream-B9ZBVQrI.mjs +0 -395
- package/.output/server/chunks/build/useDaqStream-B9ZBVQrI.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqTemplates-CK37sMfi.mjs +0 -187
- package/.output/server/chunks/build/useDaqTemplates-CK37sMfi.mjs.map +0 -1
- package/.output/server/chunks/build/useDashboardData-B36wVWo2.mjs +0 -153
- package/.output/server/chunks/build/useDashboardData-B36wVWo2.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwAddNode-DACVcEHs.mjs +0 -219
- package/.output/server/chunks/build/useDcwAddNode-DACVcEHs.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwCards-SaiImIYQ.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwDetailScope-DLOklSMO.mjs +0 -184
- package/.output/server/chunks/build/useDcwDetailScope-DLOklSMO.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwDetailTemplates-ClmIdZ40.mjs +0 -148
- package/.output/server/chunks/build/useDcwDetailTemplates-ClmIdZ40.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwFilter-CPMyCltx.mjs +0 -71
- package/.output/server/chunks/build/useDcwFilter-CPMyCltx.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwLedger-0ie-0WTc.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwLineMutations-DNxo66yM.mjs +0 -137
- package/.output/server/chunks/build/useDcwLineMutations-DNxo66yM.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwLineRun-CHskwscT.mjs +0 -104
- package/.output/server/chunks/build/useDcwLineRun-CHskwscT.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwParamApprovals-DtScUc-W.mjs +0 -121
- package/.output/server/chunks/build/useDcwParamApprovals-DtScUc-W.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwProducts-BMaxzLu6.mjs +0 -93
- package/.output/server/chunks/build/useDcwProducts-BMaxzLu6.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwQuery-TVU3Eo0M.mjs +0 -98
- package/.output/server/chunks/build/useDcwQuery-TVU3Eo0M.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwQuickActions-BREFmR9T.mjs +0 -88
- package/.output/server/chunks/build/useDcwQuickActions-BREFmR9T.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwRecipes-Bc9urA1N.mjs +0 -258
- package/.output/server/chunks/build/useDcwRecipes-Bc9urA1N.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwStream-Dir5ufAU.mjs +0 -452
- package/.output/server/chunks/build/useDcwStream-Dir5ufAU.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwTemplates-BzagZztS.mjs +0 -91
- package/.output/server/chunks/build/useDcwTemplates-BzagZztS.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwWrites-BROwTLCn.mjs +0 -140
- package/.output/server/chunks/build/useDcwWrites-BROwTLCn.mjs.map +0 -1
- package/.output/server/chunks/build/useDeviceTwins-BqwVJaZg.mjs +0 -128
- package/.output/server/chunks/build/useDeviceTwins-BqwVJaZg.mjs.map +0 -1
- package/.output/server/chunks/build/useExpConfirmations-BM626N4p.mjs +0 -107
- package/.output/server/chunks/build/useExpConfirmations-BM626N4p.mjs.map +0 -1
- package/.output/server/chunks/build/useLocalTime-uVSM44Um.mjs +0 -55
- package/.output/server/chunks/build/useLocalTime-uVSM44Um.mjs.map +0 -1
- package/.output/server/chunks/build/useLogsManual-cVBZQ_Mt.mjs +0 -88
- package/.output/server/chunks/build/useLogsManual-cVBZQ_Mt.mjs.map +0 -1
- package/.output/server/chunks/build/useLogsQuery-D7hrJ3yn.mjs +0 -150
- package/.output/server/chunks/build/useLogsQuery-D7hrJ3yn.mjs.map +0 -1
- package/.output/server/chunks/build/useLogsScope-aaPQeTRm.mjs +0 -83
- package/.output/server/chunks/build/useLogsScope-aaPQeTRm.mjs.map +0 -1
- package/.output/server/chunks/build/useMonitorData-DIkiLr_h.mjs +0 -135
- package/.output/server/chunks/build/useMonitorData-DIkiLr_h.mjs.map +0 -1
- package/.output/server/chunks/build/useMonitorFormat-CyURtH8N.mjs +0 -91
- package/.output/server/chunks/build/useMonitorFormat-CyURtH8N.mjs.map +0 -1
- package/.output/server/chunks/build/useOperationsFeed-Ci4mZAn_.mjs +0 -195
- package/.output/server/chunks/build/useOperationsFeed-Ci4mZAn_.mjs.map +0 -1
- package/.output/server/chunks/build/useOpsLog-C-M6Eace.mjs +0 -149
- package/.output/server/chunks/build/useOpsLog-C-M6Eace.mjs.map +0 -1
- package/.output/server/chunks/build/useRecipeDispatchApprovals-BYtiqfeb.mjs +0 -156
- package/.output/server/chunks/build/useRecipeDispatchApprovals-BYtiqfeb.mjs.map +0 -1
- package/.output/server/chunks/build/useTeamActions-I_8QWp9w.mjs +0 -79
- package/.output/server/chunks/build/useTeamActions-I_8QWp9w.mjs.map +0 -1
- package/.output/server/chunks/build/useTeamAddMemberDialog-BLn4nTuy.mjs +0 -84
- package/.output/server/chunks/build/useTeamAddMemberDialog-BLn4nTuy.mjs.map +0 -1
- package/.output/server/chunks/build/useTeamCreateDialog-Bp5qKtpI.mjs +0 -97
- package/.output/server/chunks/build/useTeamCreateDialog-Bp5qKtpI.mjs.map +0 -1
- package/.output/server/chunks/build/useTeamDeployDialog-DwIaz6Cx.mjs +0 -80
- package/.output/server/chunks/build/useTeamDeployDialog-DwIaz6Cx.mjs.map +0 -1
- package/.output/server/chunks/build/useTeamPluginDialog-B932gZOI.mjs +0 -103
- package/.output/server/chunks/build/useTeamPluginDialog-B932gZOI.mjs.map +0 -1
- package/.output/server/chunks/build/useTeamsCatalog-DBwaLQBw.mjs +0 -123
- package/.output/server/chunks/build/useTeamsCatalog-DBwaLQBw.mjs.map +0 -1
- package/.output/server/chunks/build/useTokenCreate-Ctl0YGhL.mjs +0 -115
- package/.output/server/chunks/build/useTokenCreate-Ctl0YGhL.mjs.map +0 -1
- package/.output/server/chunks/build/useTokenRename-Cm3ypSfN.mjs +0 -87
- package/.output/server/chunks/build/useTokenRename-Cm3ypSfN.mjs.map +0 -1
- package/.output/server/chunks/build/useTokenReveal-CeoWL5Mt.mjs +0 -118
- package/.output/server/chunks/build/useTokenReveal-CeoWL5Mt.mjs.map +0 -1
- package/.output/server/chunks/build/useTokensList-CNIzON3u.mjs +0 -92
- package/.output/server/chunks/build/useTokensList-CNIzON3u.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkbenchAuth-CY-bVFMu.mjs +0 -141
- package/.output/server/chunks/build/useWorkbenchAuth-CY-bVFMu.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkbenchWorkspaces-DhZQFss9.mjs +0 -138
- package/.output/server/chunks/build/useWorkbenchWorkspaces-DhZQFss9.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkshopApi-CaSItJVq.mjs +0 -119
- package/.output/server/chunks/build/useWorkshopApi-CaSItJVq.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkshopWs-YQ5URrbt.mjs +0 -1659
- package/.output/server/chunks/build/useWorkshopWs-YQ5URrbt.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkspaceAuth-Dlvj9csD.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkspaceChannels-CPFepxTN.mjs +0 -92
- package/.output/server/chunks/build/useWorkspaceChannels-CPFepxTN.mjs.map +0 -1
- package/.output/server/chunks/build/useWorkspaceShell-BCypjjjE.mjs +0 -168
- package/.output/server/chunks/build/useWorkspaceShell-BCypjjjE.mjs.map +0 -1
- package/.output/server/chunks/build/users-DjiVC_tS.mjs +0 -569
- package/.output/server/chunks/build/users-DjiVC_tS.mjs.map +0 -1
- package/.output/server/chunks/build/workshop-1gKeDASV.mjs +0 -546
- package/.output/server/chunks/build/workshop-1gKeDASV.mjs.map +0 -1
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.json +0 -1
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/1459e6269990a5d229b9228c8b2f62ffaa7d350068380ae8b875fee0db45cca6.source +0 -180
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/c2991e909c70c5c12b90a407c7adcf63384c51668a2b334e6e047fa675c268fe.source +0 -305
- package/app/.mimosa/hook-state/sess_05189059-e42d-4188-8911-4f7baabe009b.mtg0vz68-49792-75b0709555.baseline/e09129039d1e27e3fac24becc9bdafd2d1a7c8049bff24173df007be865f0165.source +0 -264
- package/app/.mimosa/hook-status/sess_05189059-e42d-4188-8911-4f7baabe009b-28ef7c75ea.json +0 -14
- package/scripts/e2e-direct-rest-simulator-paper.mjs +0 -624
- package/scripts/e2e-pi-simulator-paper.mjs +0 -2316
- package/scripts/e2e-registered-harness-simulator-paper.mjs +0 -1926
- package/scripts/e2e-simulator-optimizer-paper.mjs +0 -264
- package/scripts/fixtures/omp-paper-harness.mjs +0 -163
- package/server/.mimosa/hook-state/sess_869e5bfd-07f4-4b6e-a991-6221fcf6b7d2.json +0 -1
- package/server/.mimosa/hook-state/sess_869e5bfd-07f4-4b6e-a991-6221fcf6b7d2.mvhn1irp-99488-e3fdc1cbfc.baseline/942d2679b87b96c612b4f2a425c3ccc5b6f3234f0cf589a8441602f866fbe277.source +0 -41
- package/server/.mimosa/hook-state/sess_869e5bfd-07f4-4b6e-a991-6221fcf6b7d2.mvhn1irp-99488-e3fdc1cbfc.baseline/dd617a82339d2c61e49e28e416aabeb2fc100fb3cb1beca18d02dfb833214af6.source +0 -51
- package/server/.mimosa/hook-status/sess_869e5bfd-07f4-4b6e-a991-6221fcf6b7d2-e67b6b91bf.json +0 -14
- package/server/services/workshop/.mimosa/hook-state/sess_fed429fb-c8b7-4a50-bbd6-45afc580893f.json +0 -1
- package/server/services/workshop/.mimosa/hook-status/sess_fed429fb-c8b7-4a50-bbd6-45afc580893f-e6b75bbfe6.json +0 -14
- package/server/services/workshop/agents/omp-agent/supervision-history.ts +0 -38
- package/server/services/workshop/aml/python/__pycache__/aml_eval.cpython-312.pyc +0 -0
- package/server/services/workshop/aml/python/__pycache__/amlkit.cpython-312.pyc +0 -0
- package/server/services/workshop/aml/python/__pycache__/train-example.cpython-312.pyc +0 -0
- package/server/services/workshop/aml/python/__pycache__/train-hybrid-example.cpython-312.pyc +0 -0
- package/server/services/workshop/optimization/python/.mimosa/hook-state/session_187ec97f45d4bd02369bbe94da527cf1.json +0 -1
- package/server/services/workshop/optimization/python/.mimosa/hook-state/session_187ec97f45d4bd02369bbe94da527cf1.mvjqkcs6-68688-fb7131a89c.baseline/190a6772a4e43801737b4ba6072eb9d53d3099b5d8a9e6f57bc5e8592a26a6c4.source +0 -274
- package/server/services/workshop/optimization/python/.mimosa/hook-status/session_187ec97f45d4bd02369bbe94da527cf1-01ed7f04fa.json +0 -14
- package/server/services/workshop/optimization/python/__pycache__/problems.cpython-312.pyc +0 -0
- /package/.output/public/_nuxt/{9j2J4k4w.js → 9j2J4k4w2.js} +0 -0
- /package/.output/public/_nuxt/{BY3Lbz3h2.js → BY3Lbz3h.js} +0 -0
- /package/.output/public/_nuxt/{Iub61dX92.js → Iub61dX9.js} +0 -0
- /package/.output/public/_nuxt/{Q-JpBT032.js → Q-JpBT03.js} +0 -0
- /package/.output/public/_nuxt/{dVw8IHY62.js → dVw8IHY6.js} +0 -0
- /package/.output/public/_nuxt/{k9b520K-.js → k9b520K-2.js} +0 -0
|
@@ -1,657 +1,657 @@
|
|
|
1
|
-
# HTTP API authz / read-path / perf audit — agents + runtime + industrial nodes
|
|
2
|
-
|
|
3
|
-
Read-only audit. Every claim cites `file:line` with verbatim code. No source file was modified.
|
|
4
|
-
|
|
5
|
-
Scope read in full: `server/api/workshop/agent-tools/**`, `agents/**`, `channels/**`, `dcw/**`, `daq/**`,
|
|
6
|
-
`runtime.get.ts`, `tasks.get.ts`, `mailbox.get.ts`, `caller.ts`, `server/services/workshop/scene-events.ts`,
|
|
7
|
-
`server/services/workshop/agents/agent-badge.ts`, plus the guards they delegate to
|
|
8
|
-
(`permissions.ts`, `runtime/manager.ts`, `agents/tool-approvals.ts`, `agents/node-bindings.repo.ts`,
|
|
9
|
-
`agents/host-tool-bridge.ts`, `agents/industrial-tools.ts`, `dcw/dcw-controller.ts`, `daq/daq-controller.ts`,
|
|
10
|
-
`aml/job-orchestrator.ts`, `aml/python-runtime.ts`).
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 1. AUTHZ AUDIT
|
|
15
|
-
|
|
16
|
-
### 0. Global facts
|
|
17
|
-
|
|
18
|
-
* No global auth middleware exists. The only middleware is `server/middleware/request-log.ts:7` — logging only.
|
|
19
|
-
* Unmatched `/api/workshop/**` paths hit `server/api/workshop/[...path].ts:12` → static 404.
|
|
20
|
-
* Auth primitives (`server/api/workshop/caller.ts`):
|
|
21
|
-
* `resolveUser` (`:71-79`) — Bearer **user** token, else 401.
|
|
22
|
-
* `resolveCaller` (`:16-24`) — Bearer **agent instance** token, else 401.
|
|
23
|
-
* `resolveAgentOrUser` (`:45-54`) — either domain.
|
|
24
|
-
* `requireAdmin` (`:82-88`), `requireRole` (`:91-97`).
|
|
25
|
-
* Line-grant model (`server/services/workshop/permissions.ts`): `isPrivilegedRole` (`:26-28`),
|
|
26
|
-
`lineMode` (`:31-36`), `visibleLineIds` (`:39-42`), `filterByLine` (`:45-52`),
|
|
27
|
-
`requireLineMode` (`:55-62`). admin/editor bypass everything.
|
|
28
|
-
|
|
29
|
-
### 1.1 [P0] `agent-tools/invoke.post.ts` — user token + client-supplied `agentId` = full agent impersonation
|
|
30
|
-
|
|
31
|
-
The whole handler (27 lines):
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
// server/api/workshop/agent-tools/invoke.post.ts:16-27
|
|
35
|
-
export default defineApiHandler(async (event) => {
|
|
36
|
-
const agentToken = getHeader(event, 'x-aw-agent-token')
|
|
37
|
-
if (!agentToken) resolveUser(event) // 桥路径免用户鉴权,由 token 校验兜底
|
|
38
|
-
const body = await readBody<{ agentId?: string, tool?: string, args?: Record<string, unknown> }>(event) ?? {}
|
|
39
|
-
const agentId = String(body.agentId ?? '')
|
|
40
|
-
const tool = String(body.tool ?? '')
|
|
41
|
-
const args = body.args ?? {}
|
|
42
|
-
if (!agentId) throw createError({ statusCode: 400, statusMessage: 'agentId required' })
|
|
43
|
-
if (!tool) throw createError({ statusCode: 400, statusMessage: 'tool required' })
|
|
44
|
-
const result = await getWorkshopManager().invokeHostTool({ agentId, tool, args, token: agentToken || undefined })
|
|
45
|
-
return { result }
|
|
46
|
-
})
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Note `invoke.post.ts:18`: `resolveUser(event)`'s **return value is discarded** — the authenticated user is never
|
|
50
|
-
passed downstream. `agentId` comes only from the request body (`:20`).
|
|
51
|
-
|
|
52
|
-
The guard it delegates to:
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
// server/services/workshop/runtime/manager.ts:2746-2752
|
|
56
|
-
async invokeHostTool(input: { agentId: string, token?: string, tool: string, args?: Record<string, unknown> }): Promise<{ text: string, isError?: boolean }> {
|
|
57
|
-
const row = this.deps.repos.channelAgents.findById(input.agentId)
|
|
58
|
-
if (!row || row.enabled !== 1) throw new AppError(404, 'NOT_FOUND', `agent 不存在或已停用: ${input.agentId}`)
|
|
59
|
-
// token 鉴权(MCP 桥路径必带;缺省视为服务端内部调用,走 REST 已有用户鉴权)
|
|
60
|
-
if (input.token !== undefined && input.token !== row.token) {
|
|
61
|
-
throw new AppError(401, 'UNAUTHORIZED', 'agent token 校验失败')
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`invoke.post.ts:25` passes `token: agentToken || undefined`. **Consequences, exactly:**
|
|
66
|
-
|
|
67
|
-
| Request | `agentToken` | `input.token` | `manager.ts:2750` | Result |
|
|
68
|
-
|---|---|---|---|---|
|
|
69
|
-
| valid **user** Bearer, **no** `x-aw-agent-token`, `agentId` = any other agent | undefined | `undefined` | `input.token !== undefined` is **false** → check **skipped** | **Executes any tool as that agent** |
|
|
70
|
-
| **no** `Authorization` at all, no `x-aw-agent-token` | undefined | — never reached | — | `resolveUser` at `invoke.post.ts:18` throws `AppError(401,'USER_UNAUTHORIZED')` (`caller.ts:76`) → **blocked** |
|
|
71
|
-
| own agent token in `x-aw-agent-token`, `agentId` = another agent | own token | own token | `!== row.token` → true | 401 — **blocked** |
|
|
72
|
-
| valid agent token, matching `agentId` | token | `=== row.token` | passes | legitimate |
|
|
73
|
-
|
|
74
|
-
So the hole is exactly: **valid user token + arbitrary body `agentId` + absent `x-aw-agent-token`.** No ownership,
|
|
75
|
-
membership or role check exists anywhere on this path. The authenticated `ResolvedUser` never reaches `manager`.
|
|
76
|
-
|
|
77
|
-
After the guard, the impersonated identity drives every downstream authorization:
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
// server/services/workshop/runtime/manager.ts:2769-2775
|
|
81
|
-
const identity = {
|
|
82
|
-
agentId: row.id,
|
|
83
|
-
channelId: row.channelId,
|
|
84
|
-
role: (row.role === 'lead' ? 'lead' : 'worker') as 'lead' | 'worker',
|
|
85
|
-
name: row.name,
|
|
86
|
-
}
|
|
87
|
-
return await pluginTool.handler(input.args ?? {}, identity)
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
// server/services/workshop/runtime/manager.ts:2777-2783
|
|
92
|
-
const runtime = this.ensureAgentRuntime(row.channelId, input.agentId)
|
|
93
|
-
if (runtime) {
|
|
94
|
-
const viaImpl = await runtime.dispatchHostTool(input.tool, input.args ?? {})
|
|
95
|
-
if (viaImpl) return viaImpl
|
|
96
|
-
}
|
|
97
|
-
// 回退:impl 未实现工具面(如 mock)→ 协作工具族直调
|
|
98
|
-
return await this.invokeAgentWorkspaceTool(input.agentId, input.tool, input.args ?? {})
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
`invokeHostTool` also applies **no role/tool filtering** — unlike `hostToolsForRole`
|
|
102
|
-
(`host-tool-bridge.ts:70-87`) it never consults `LEAD_ONLY_TOOL_NAMES` (`host-tool-bridge.ts:33-43`).
|
|
103
|
-
Every tool is dispatchable for any named agent that exists and is enabled.
|
|
104
|
-
|
|
105
|
-
**Which tool classes are reachable, by whom**
|
|
106
|
-
|
|
107
|
-
Reachable by *any* authenticated user token of *any* role (including a plain `user` with zero line grants):
|
|
108
|
-
pick any lead/worker agent id.
|
|
109
|
-
|
|
110
|
-
* **Read-only** (dispatch table `host-tool-bridge.ts:162-211` + `:219+`):
|
|
111
|
-
`my_industrial_nodes`, `aml_node_catalog`, `aml_dataset_stats`, `aml_job_status`, `aml_job_logs`,
|
|
112
|
-
`aml_leaderboard`, `aml_model_reference`, `dcw_read`, `daq_query`, `daq_frames`, `dcw_journal`, `ops_log`,
|
|
113
|
-
`recipe_log`, `recipe_versions`, `line_context`, `list_other_teams`, `search_other_teams_memory`,
|
|
114
|
-
mailbox/list-mail/memory tools.
|
|
115
|
-
* **Mutating / control-plane**:
|
|
116
|
-
* `dcw_control` — real PLC write. Its only authz is the *impersonated agent's* binding:
|
|
117
|
-
```ts
|
|
118
|
-
// server/services/workshop/agents/industrial-tools.ts:99-108
|
|
119
|
-
const repo = getAgentNodeBindingRepo()
|
|
120
|
-
const binding: AgentNodeBinding | undefined = nodeId ? repo.find(agentId, nodeId, 'dcw') : undefined
|
|
121
|
-
if (!binding) {
|
|
122
|
-
```
|
|
123
|
-
HITL applies only when `binding.mode === 'manual'` (`industrial-tools.ts:128`); with `mode:'auto'`
|
|
124
|
-
the write executes immediately (`industrial-tools.ts:157`).
|
|
125
|
-
* `recipe_update` (`host-tool-bridge.ts:207-208`), `recipe_rollback` (`:209-210`), `dcw_judge` (`:193-194`),
|
|
126
|
-
`dcw_rollback` (`:195-196`), `aml_dataset_build` (`:167-168`), `aml_job_cancel` (`:177-178`),
|
|
127
|
-
`send_cross_channel_message` (`manager.ts:2725-2734`), `dispatch_task`, `create_team_agent`,
|
|
128
|
-
`update_team_agent`, `remove_team_agent`, `complete_task`, `report_progress`.
|
|
129
|
-
* `aml_model_promote` — nominally lead-only; its check is against the **DB role of the supplied agentId**:
|
|
130
|
-
```ts
|
|
131
|
-
// server/services/workshop/agents/industrial-tools.ts:1290-1294
|
|
132
|
-
// 治理硬守卫:晋升仅限 lead 实例(注入层过滤 + 分发层双重校验,HTTP 直调不可绕过)
|
|
133
|
-
const roleRow = getAmlRuntime().db.prepare('SELECT role FROM channel_agents WHERE id = ? AND enabled = 1').get(agentId) as { role?: string } | undefined
|
|
134
|
-
if (roleRow?.role !== 'lead') {
|
|
135
|
-
return amlErr(`aml_model_promote 为 lead 专属工具:实例 ${agentId}(role=${roleRow?.role ?? '未知'})无权晋升模型。请由团队 lead 发起,晋升需人工 HITL 批准。`)
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
Naming a lead's `agentId` satisfies it (then HITL approval at `industrial-tools.ts:1316`).
|
|
139
|
-
* `aml_job_submit` — **arbitrary Python execution on the server host.** The tool takes up to 500 KB of code:
|
|
140
|
-
```ts
|
|
141
|
-
// server/services/workshop/agents/industrial-tools.ts:1152-1156
|
|
142
|
-
if (!code.trim()) return amlErr('code 必填:train.py 全文(单文件;契约见工具说明)。')
|
|
143
|
-
if (code.length > 512_000) return amlErr(`code 过大(${Math.round(code.length / 1024)}KB),上限 500KB;精简训练代码(模型定义/训练循环拆薄)后重试。`)
|
|
144
|
-
if (!/\bimport\s+amlkit\b/.test(code)) {
|
|
145
|
-
```
|
|
146
|
-
It is written to disk and executed:
|
|
147
|
-
```ts
|
|
148
|
-
// server/services/workshop/aml/job-orchestrator.ts:285-287
|
|
149
|
-
if (typeof inline === 'string' && inline.length > 0 && inline.length <= 512_000) {
|
|
150
|
-
writeFileSync(join(workspace, 'train.py'), inline)
|
|
151
|
-
trainFile = 'train.py'
|
|
152
|
-
```
|
|
153
|
-
```ts
|
|
154
|
-
// server/services/workshop/aml/job-orchestrator.ts:328
|
|
155
|
-
const exit1 = await runPython(run, vpy, [join(workspace, trainFile)], workspace)
|
|
156
|
-
```
|
|
157
|
-
No container/OS sandbox; the only mitigation is a scrubbed env
|
|
158
|
-
(`aml/python-runtime.ts:186-214`, `jobEnv` whitelist, "服务器 secrets … 一律不可见").
|
|
159
|
-
The `import amlkit` requirement is a regex (`industrial-tools.ts:1154`), trivially satisfied.
|
|
160
|
-
* Plugin tools: identity is the impersonated agent (`manager.ts:2769-2775`), no independent check —
|
|
161
|
-
e.g. `diag_run` (`server/plugins-builtin/diag-bridge/index.mjs:449-450`) exports line DAQ snapshots,
|
|
162
|
-
`kb_store`/`kb_index` (`server/plugins-builtin/rag-bridge/index.mjs:199,259`) mutate the knowledge base.
|
|
163
|
-
|
|
164
|
-
**Contrast with the sibling endpoint, which does it right:**
|
|
165
|
-
|
|
166
|
-
```ts
|
|
167
|
-
// server/api/workshop/agent-tools/list.get.ts:17-22
|
|
168
|
-
if (agentToken) {
|
|
169
|
-
const resolved = getWorkshopManager().resolveAgentByToken(String(agentToken))
|
|
170
|
-
if (!resolved || resolved.agentId !== agentId) {
|
|
171
|
-
throw createError({ statusCode: 401, statusMessage: 'agent token 校验失败' })
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**Enumeration is trivial**, so no guessing is needed: `agent-tools/bindings/index.get.ts:13` returns
|
|
177
|
-
`{ bindings: agentId ? getAgentNodeBindingRepo().byAgent(agentId) : getAgentNodeBindingRepo().all() }` — with no
|
|
178
|
-
`agentId` query param, **every** binding of every agent (agentId + nodeId + kind + mode) is returned to any
|
|
179
|
-
authenticated user.
|
|
180
|
-
|
|
181
|
-
⚠UNVERIFIED: I did not execute a live exploit; the conclusion is from the code paths above (all read directly).
|
|
182
|
-
Corroboration: the same finding is already written up at `docs/audit/audit-agents.md:186-249` ("the manager's
|
|
183
|
-
token check is **skipped entirely** when no token is supplied … Minimal hardening: in `invokeHostTool` change the
|
|
184
|
-
token check to `if (input.token !== row.token)`") — and the code is **unchanged**, i.e. unfixed.
|
|
185
|
-
|
|
186
|
-
### 1.2 `bindings/index.post.ts` — permission check quoted; **target agent ownership NOT verified**
|
|
187
|
-
|
|
188
|
-
```ts
|
|
189
|
-
// server/api/workshop/agent-tools/bindings/index.post.ts:16-39
|
|
190
|
-
export default defineApiHandler(async (event) => {
|
|
191
|
-
const user = resolveUser(event)
|
|
192
|
-
const body = await readBody<{ agentId?: string, nodeId?: string, kind?: 'dcw' | 'daq', mode?: 'auto' | 'manual' }>(event) ?? {}
|
|
193
|
-
const kind = body.kind ?? 'dcw'
|
|
194
|
-
const nodeId = String(body.nodeId ?? '')
|
|
195
|
-
// 绑定前置:节点必须存在,且用户对其所属产线有足额权限
|
|
196
|
-
const lineId = kind === 'daq'
|
|
197
|
-
? getDaqController().byId(nodeId)?.lineId
|
|
198
|
-
: getDcwController().byId(nodeId)?.lineId
|
|
199
|
-
if (!lineId) {
|
|
200
|
-
throw new AppError(404, ErrorCodes.NOT_FOUND, `${kind === 'daq' ? '数采' : '写控'}节点不存在: ${nodeId}`)
|
|
201
|
-
}
|
|
202
|
-
const mode = lineMode(user, lineId)
|
|
203
|
-
const needOperate = kind === 'dcw'
|
|
204
|
-
if (mode === 'none' || (needOperate && mode !== 'operate')) {
|
|
205
|
-
throw new AppError(403, 'LINE_FORBIDDEN', `无该产线权限:绑定${kind === 'daq' ? '数采' : '写控'}节点需对产线「${lineId}」拥有${needOperate ? '可操控' : '仅查看'}及以上权限`)
|
|
206
|
-
}
|
|
207
|
-
const binding = getAgentNodeBindingRepo().bind(
|
|
208
|
-
String(body.agentId ?? ''),
|
|
209
|
-
nodeId,
|
|
210
|
-
kind,
|
|
211
|
-
body.mode ?? 'auto',
|
|
212
|
-
)
|
|
213
|
-
return { binding }
|
|
214
|
-
})
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
The check at `:28-32` covers **the node's line only**. `body.agentId` is never resolved, never checked for
|
|
218
|
-
existence, never checked for ownership/membership:
|
|
219
|
-
|
|
220
|
-
```ts
|
|
221
|
-
// server/services/workshop/agents/node-bindings.repo.ts:73-78
|
|
222
|
-
bind(agentId: string, nodeId: string, kind: AgentNodeBindingKind, mode: AgentNodeBindingMode): AgentNodeBinding {
|
|
223
|
-
if (!agentId) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, 'agentId 必填')
|
|
224
|
-
if (!nodeId) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, 'nodeId 必填')
|
|
225
|
-
if (kind !== 'dcw' && kind !== 'daq') throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `未知节点类型: ${kind}`)
|
|
226
|
-
if (mode !== 'auto' && mode !== 'manual') throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `未知控制模式: ${mode}`)
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Effect: **any** authenticated user with `operate` on one line can grant that control right to **any** agent id —
|
|
230
|
-
their own, another user's agent, or a lead in an unrelated channel — or re-bind an existing agent with
|
|
231
|
-
`"mode":"auto"` to strip HITL, because `bind()` updates the mode of an existing row
|
|
232
|
-
(`node-bindings.repo.ts:79-83`: `if (prev) { prev.mode = mode; this.flush(); return prev }`).
|
|
233
|
-
The granting user need not own, belong to, or even know anything about that agent.
|
|
234
|
-
|
|
235
|
-
### 1.3 Handlers that authenticate but never authorize the resource (IDOR)
|
|
236
|
-
|
|
237
|
-
All of these call `resolveUser` and **discard the result** or use it only for logging — no ownership/role/line check:
|
|
238
|
-
|
|
239
|
-
| Handler | Auth | Missing check | Impact |
|
|
240
|
-
|---|---|---|---|
|
|
241
|
-
| `agent-tools/bindings/[id].patch.ts:10-13` | `resolveUser(event)` (`:10`) | owner / role / line | Any user flips **any** binding's mode: `getAgentNodeBindingRepo().setMode(id, body.mode ?? 'auto')` → `"auto"` **disables HITL** for another team's writes (`node-bindings.repo.ts:97-104`) |
|
|
242
|
-
| `agent-tools/bindings/[id].delete.ts:11-17` | `resolveUser(event)` (`:11`) | owner / role / line | Any user unbinds any agent↔node pair (`:17 !repo.unbind(id)`), also cancels that agent's pending approvals (`:19 getToolApprovals().cancelPendingFor(...)`) |
|
|
243
|
-
| `agent-tools/bindings/index.get.ts:10-13` | `resolveUser(event)` (`:10`) | any scope filter | Full cross-tenant binding dump (see 1.1) |
|
|
244
|
-
| `agent-tools/approvals/index.get.ts:11-16` | `resolveUser(event)` (`:11`) | ownership of `agentId` | Any user lists pending approvals of any agent: `getToolApprovals().listPending(agentId)`; history via `scope=history` |
|
|
245
|
-
| `agent-tools/list.get.ts:13-23` | user token path (`:13`) | `agentId` ownership | Tool-schema disclosure for any agent |
|
|
246
|
-
| `dcw/[id]/read.post.ts:13-16` | `resolveUser(event)` (`:13`) | **`requireLineMode` absent** | Any user triggers a real driver read on **any** dcw node id: `await getDcwController().readNow(id)` (`:16`) — cross-line data read |
|
|
247
|
-
| `dcw/[id].patch.ts:12-16` | `resolveUser(event)` (`:12`) | role / line | Any user patches **any** dcw node: name/driver/range/enabled/position — `getDcwController().patch(id, body)` (`:16`). Widening `min/max` weakens the write interlock that `dcw_control` depends on |
|
|
248
|
-
| `dcw/index.post.ts:13-16` | `resolveUser(event)` (`:13`) | role / line | Any user creates a write-control node (the comment at `:17` calls it "获得直写物理设备能力的入口") |
|
|
249
|
-
| `dcw/lines/[id].delete.ts:15-21` | `resolveUser(event)` (`:15`) | role / line | Any user deletes **any** line; `?purge=1` cascades nodes/products/recipes (`:19-21`). The sibling create is admin/editor-gated (`dcw/lines/index.post.ts:14`) |
|
|
250
|
-
| `dcw/lines/[id].patch.ts:11-14` | `resolveUser(event)` (`:11`) | role / line | Any user renames/recolors any line |
|
|
251
|
-
| `dcw/recipes/[id].delete.ts:10-12` | `resolveUser(event)` (`:10`) | role | Any user deletes any recipe |
|
|
252
|
-
| `dcw/recipes/index.post.ts:11-13` | `resolveUser(event)` (`:11`) | role | Any user creates recipes |
|
|
253
|
-
| `dcw/recipes/[id]/index.patch.ts:12` , `[id]/revert.post.ts:12` | `resolveUser` | role | Any user edits/rewrites any recipe version history |
|
|
254
|
-
| `dcw/products/[id].delete.ts:11-14`, `[id].patch.ts:12-16` | `resolveUser` | role | Any user deletes/edits any product (create requires admin/editor, `products/index.post.ts:12`) |
|
|
255
|
-
| `dcw/runs/[id]/close.post.ts:11-14` | `resolveUser(event)` (`:11`) | role | Any user closes any production run window |
|
|
256
|
-
| `dcw/templates/index.post.ts:11-13`, `[key].patch.ts:11-14`, `[key].delete.ts:10-12` | `resolveUser` | role | Any user mutates control templates (driver configs) |
|
|
257
|
-
| `dcw/test-driver.post.ts:12-15` | `resolveUser(event)` (`:12`) | role | Any user makes the server open outbound connections with a caller-supplied `driverConfig`: `getDcwController().testDriver(body.driver ?? 'mock', body.driverConfig ?? {})` |
|
|
258
|
-
| `daq/test-driver.post.ts:17-21` | `resolveUser(event)` (`:17`) | role | Same SSRF-ish connect primitive for DAQ protocols |
|
|
259
|
-
| `daq/[id]/test.post.ts:12-15` | `resolveUser(event)` (`:12`) | **`requireLineMode` absent** | Any user triggers a real connection test on any DAQ node (`:15 testNode(id)`); the dcw twin requires `operate` (`dcw/[id]/test.post.ts:16`) |
|
|
260
|
-
| `daq/[id].delete.ts:13-17` | `resolveUser(event)` (`:13`) | **`requireLineMode` absent** | Any user deletes any DAQ node; the patch twin requires `operate` (`daq/[id].patch.ts:19`) |
|
|
261
|
-
| `daq/infra/reconnect.post.ts:9-15` | `resolveUser(event)` (`:9`) | role | Any user triggers infra bring-up: `await apply(daqInfraConfig())` → `__daqApplyInfra` runs `docker compose -f … up -d …` (`server/services/workshop/daq/infra.ts:85`) |
|
|
262
|
-
| `daq/alarms.get.ts:11-15` | `resolveUser(event)` (`:11`) | line filter | Any user reads **all** alarms of **all** lines (`:15 listAlarms(scope, limit)`); `daq/index.get.ts:27` filters by line, this one does not |
|
|
263
|
-
| `daq/templates/*` (`index.post.ts:12`, `[key].patch.ts:12`, `[key].delete.ts:12`) | `resolveUser` | role | Any user mutates signal templates |
|
|
264
|
-
| `dcw/journal/index.get.ts:11-19` | `resolveUser(event)` (`:11`) | line scope | Any user reads the full parameter ledger for any `lineId`/`nodeId` supplied in the query |
|
|
265
|
-
| `dcw/[id]/param-ledger.get.ts:11-13` | `resolveUser(event)` (`:11`) | **`requireLineMode` absent** | Any user reads any node's setpoints/targets/last-good values |
|
|
266
|
-
| `dcw/optimizations/index.get.ts:11-21`, `[id].get.ts:11-16`, `[id]/series.get.ts:12-17` | `resolveUser` | line scope | Any user reads any line's optimization records + raw DAQ series |
|
|
267
|
-
| `dcw/recipes/index.get.ts:9-11` | `resolveUser(event)` (`:9`) | line scope | Returns `repo.list()`/`listRuns()` with **no** `visibleLineIds` filter (contrast `dcw/index.get.ts:29-30`, `dcw/runs/index.get.ts:12`) |
|
|
268
|
-
| `dcw/recipes/[id]/versions.get.ts:11-13` | `resolveUser` | line scope | Any user reads any recipe's full parameter version history |
|
|
269
|
-
| `server/api/workshop/runtime.get.ts:9-10` | `resolveUser(event)` (`:9`) | role | Any user enumerates wired harness agent ids + active channels |
|
|
270
|
-
| `server/api/workshop/daq/infra.get.ts:9` | `resolveUser` | role | Infra status disclosure (low) |
|
|
271
|
-
|
|
272
|
-
### 1.4 Handlers that ARE correctly guarded (control set)
|
|
273
|
-
|
|
274
|
-
* `agents/[id].get.ts:16-17` → `manager.requireTemplateReadable`; `agents/[id].patch.ts:28`, `[id].delete.ts:17` → `requireWritable` (`manager.ts:1083-1090`).
|
|
275
|
-
* `channels/[id].delete.ts:14-15`, `index.patch.ts:35-36`, `agents/index.post.ts:32-33`, `agents/[agentId].delete.ts:17-18`, `[agentId].patch.ts:18-19`, `model.patch.ts:20-21`, `position.patch.ts:19-20`, `range.patch.ts:19-20`, `stop.post.ts:18-19` → `getChannelForUser` + `requireWritable` + membership check (`manager.ts:1103-1110`, `:1067-1074`).
|
|
276
|
-
* `channels/[id]/messages/index.post.ts:38-39`, `tasks/index.post.ts:67-68`, `tasks/index.get.ts:22`, `queue.get.ts:17`, `events.get.ts:28`, `terminals.get.ts:19`, `messages/index.get.ts:21`, `channels/[id]/index.get.ts:13` → `getChannelForUser` (owner-or-legacy-public read).
|
|
277
|
-
* Memory planes: `channels/[id]/memories/**` and `channels/[id]/agents/[agentId]/memories/**` → `resolveAgentOrUser` + same-channel or `getChannelForUser`+`requireOwned`, and the manager re-checks role: `manager.ts:1508` (`仅本人或 lead 可策展 Agent 记忆`), `:1463` (`仅 lead 可写团队记忆`), `:1487`, `:1548`.
|
|
278
|
-
⚠ Note (by design, not a bug): `manager.searchAgentMemories` (`manager.ts:1441-1446`) only calls `requireMember(channelId, callerAgentId)` — **any teammate agent in the channel can search another agent's private memory** by passing that agent's id in the URL with `scope:'private'`.
|
|
279
|
-
* `agents/subscribe.post.ts:17-19` → `resolveCaller` + `manager.subscribe` → `requireMember` + same-channel target check (`manager.ts:2884-2893`).
|
|
280
|
-
* `tasks.get.ts:11-12` / `mailbox.get.ts:10-13` → `resolveCaller`, scope forced to `caller.channelId`/`caller.id`.
|
|
281
|
-
* Role-gated: `dcw/controller.post.ts:15`, `dcw/[id].delete.ts:14`, `dcw/lines/index.post.ts:14`, `dcw/journal/node/[nodeId]/rollback.post.ts:15`, `dcw/optimizations/[id]/judge.post.ts:15`, `[id]/rollback.post.ts:17`, `dcw/products/index.post.ts:12`, `dcw/recipes/[id]/apply.post.ts:15`, `[id]/mark-good.post.ts:13`, `[id]/rollback-good.post.ts:13`, `dcw/runs/[id]/rollback.post.ts:14`, `daq/controller.post.ts:29,49`, `daq/index.post.ts:16` (all `requireRole`/`gateDangerous`).
|
|
282
|
-
* `agent-tools/approvals/[id]/decide.post.ts:12-19` — `resolveUser` **used** (decider recorded + `audit`).
|
|
283
|
-
|
|
284
|
-
### 1.5 `requireLineMode` / `lineMode` audit for daq/dcw handlers
|
|
285
|
-
|
|
286
|
-
**Called (correct):**
|
|
287
|
-
|
|
288
|
-
| Handler | Line | Quote |
|
|
289
|
-
|---|---|---|
|
|
290
|
-
| `dcw/[id]/write.post.ts` | `:19` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
291
|
-
| `dcw/[id]/test.post.ts` | `:16` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
292
|
-
| `dcw/[id]/bind.post.ts` | `:16` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
293
|
-
| `dcw/[id]/bindings.put.ts` | `:17` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
294
|
-
| `dcw/lines/[id]/start.post.ts` | `:20` | `requireLineMode(user, id, 'operate')` |
|
|
295
|
-
| `dcw/lines/[id]/stop.post.ts` | `:20` | `requireLineMode(user, id, 'operate')` |
|
|
296
|
-
| `dcw/runs/[id]/data.get.ts` | `:18` | `requireLineMode(user, run?.lineId, 'readonly')` |
|
|
297
|
-
| `daq/[id].patch.ts` | `:19` | `requireLineMode(user, getDaqController().byId(id)?.lineId, 'operate')` |
|
|
298
|
-
| `daq/[id]/bind.post.ts` | `:18`, `daq/[id]/bindings.put.ts:18` | `requireLineMode(user, getDaqController().byId(id)?.lineId, 'operate')` |
|
|
299
|
-
| `daq/[id]/samples.get.ts` | `:16`, `daq/[id]/frames.get.ts:16`, `daq/[id]/frames/content.get.ts:17` | `requireLineMode(user, getDaqController().byId(id)?.lineId, 'readonly')` |
|
|
300
|
-
| `daq/alarms/[id]/ack.post.ts` | `:17` | `requireLineMode(user, alarmNodeId ? getDaqController().byId(alarmNodeId)?.lineId : undefined, 'operate')` |
|
|
301
|
-
| `dcw/index.get.ts` | `:18-36` | `visibleLineIds` + `filterByLine` (+ per-line `.filter`) |
|
|
302
|
-
| `dcw/lines/index.get.ts` | `:15-18`, `dcw/line/query.get.ts:18-21`, `dcw/runs/index.get.ts:12`, `daq/index.get.ts:27` | `visibleLineIds`/`filterByLine`; `dcw/line/query.get.ts:19-21` throws `403 LINE_FORBIDDEN` when `lineId` is outside the visible set |
|
|
303
|
-
| `agent-tools/bindings/index.post.ts` | `:28-32` | `lineMode(user, lineId)` (node line only — see 1.2) |
|
|
304
|
-
|
|
305
|
-
**NOT called, where a line-scoped resource is addressed** (all cited in §1.3 with their guards):
|
|
306
|
-
`dcw/[id]/read.post.ts` (no line check on a real driver read — worst of these),
|
|
307
|
-
`dcw/[id].patch.ts`, `dcw/[id]/param-ledger.get.ts`, `dcw/journal/index.get.ts`,
|
|
308
|
-
`dcw/optimizations/index.get.ts`, `dcw/optimizations/[id].get.ts`, `dcw/optimizations/[id]/series.get.ts`,
|
|
309
|
-
`dcw/recipes/index.get.ts`, `dcw/recipes/[id]/versions.get.ts`, `dcw/recipes/[id].delete.ts`,
|
|
310
|
-
`dcw/recipes/[id]/index.patch.ts`, `dcw/recipes/[id]/revert.post.ts`, `dcw/products/[id].patch.ts`,
|
|
311
|
-
`dcw/products/[id].delete.ts`, `dcw/runs/[id]/close.post.ts`, `dcw/lines/[id].patch.ts`,
|
|
312
|
-
`dcw/lines/[id].delete.ts`, `dcw/index.post.ts`, `dcw/templates/*`,
|
|
313
|
-
`daq/[id].delete.ts`, `daq/[id]/test.post.ts`, `daq/alarms.get.ts`, `daq/templates/*`,
|
|
314
|
-
`daq/test-driver.post.ts`, `dcw/test-driver.post.ts`.
|
|
315
|
-
|
|
316
|
-
---
|
|
317
|
-
|
|
318
|
-
## 2. READ-PATH WRITES (`*.get.ts` that mutate)
|
|
319
|
-
|
|
320
|
-
| GET handler | Write / side effect | Quote |
|
|
321
|
-
|---|---|---|
|
|
322
|
-
| `server/api/workshop/daq/index.get.ts:22` | **DB insert + runtime creation + 250 ms timer**: `provisionLegacyTwins()` → `this.repo.insert(node)` | `ctrl.provisionLegacyTwins()` (`index.get.ts:22`); `daq-controller.ts:1142-1159`: `provisionLegacyTwins(): void {` … `this.repo.insert(node)` (`:1157`) … `this.syncRuntimes()` (`:1159`); `ensureLoop()` at `:1143` starts `this.timer = setInterval(() => this.sweep(), 250)` (`:191`) |
|
|
323
|
-
| `server/api/workshop/daq/index.get.ts:20` | Pipeline power-on: queue consumer + TSDB init | `await Promise.all([tsdbReady, getDaqQueue()])` (`:20`); comment `:18` `// ws.ts 出口装配 + 管线上电(幂等;路由模块加载即绑定)` |
|
|
324
|
-
| `server/api/workshop/daq/[id]/samples.get.ts:23` (via `:16` guard) | **Starts acquisition**: `samples()` → `ensureLoop()` | route `:23 const points = await getDaqController().samples(id, {...})`; `daq-controller.ts:793-795`: `async samples(id, opts) {` / `this.ensureLoop()` / `await tsdbReady` |
|
|
325
|
-
| `server/api/workshop/daq/[id]/frames.get.ts:20` | same | route `:20 const frames = await getDaqController().frames(id, {...})`; `daq-controller.ts:835 this.ensureLoop()` |
|
|
326
|
-
| `server/api/workshop/daq/[id]/frames/content.get.ts:23` | same | route `:23 const { data, mime } = await getDaqController().frameContent(...)`; `daq-controller.ts:854 this.ensureLoop()` |
|
|
327
|
-
| `server/api/workshop/channels/[id]/index.get.ts:16` | **Assembles lead runtime + starts SchedulerLoop (timer + loop resubmit callback)** | `ensureLeadSchedulerLoop(manager, channelId)` (`:16`), comment `:15` `// 兜底:详情请求时确保 lead 的调度循环已装配(如启动恢复遗漏)`; → `plugins/workshop.ts:73 manager.ensureChannelActive(channelId, options)` → `manager.ts:697-719` `const cr = this.ensureChannelRuntime(channelId)` … `cr.scheduler = loop` / `loop.start()` ⚠(whether `wireMember` immediately spawns a harness subprocess was not traced) |
|
|
328
|
-
| `server/api/workshop/dcw/index.get.ts:13`, `dcw/lines/index.get.ts:12`, `dcw/line/query.get.ts:15`, `dcw/runs/[id]/data.get.ts:14` | Global binding mutation: repoints the controller's broadcast sink each request | `bindDcwBroadcast(broadcastSceneEvent)` → `dcw-controller.ts` `setBroadcast(fn)`; `daq/index.get.ts:19`, `dcw/[id]/read.post.ts:14` etc. use `bindDaqHost(broadcastSceneEvent)` |
|
|
329
|
-
| `server/api/workshop/dcw/index.get.ts:32` | Over-fetch then JS trim (not a write, but a read amplification) | `ctrl.listHistory(400).filter(h => inLine(nodeLine(h.nodeId))).slice(0, 60)` |
|
|
330
|
-
|
|
331
|
-
**Purely read-only GETs (verified line by line):** `agent-tools/list.get.ts`, `agent-tools/approvals/index.get.ts`,
|
|
332
|
-
`agent-tools/bindings/index.get.ts`, `agents/index.get.ts`, `agents/[id].get.ts`,
|
|
333
|
-
`channels/[id]/events.get.ts`, `channels/[id]/queue.get.ts`, `channels/[id]/tasks/index.get.ts`,
|
|
334
|
-
`channels/[id]/terminals.get.ts` (`harness-terminal.ts:451-466` pure map over a Map),
|
|
335
|
-
`channels/[id]/messages/index.get.ts`, `channels/[id]/agents/index.get.ts`,
|
|
336
|
-
`channels/[id]/memories/index.get.ts`, `channels/[id]/agents/[agentId]/memories/index.get.ts`,
|
|
337
|
-
`channels/[id]/plugins/index.get.ts`, `dcw/index.get.ts` (only the binding call above),
|
|
338
|
-
`dcw/lines/index.get.ts`, `dcw/line/query.get.ts`, `dcw/recipes/index.get.ts`, `dcw/recipes/[id]/versions.get.ts`,
|
|
339
|
-
`dcw/runs/index.get.ts`, `dcw/runs/[id]/data.get.ts` (`dcw-controller.ts:964-999` queries only),
|
|
340
|
-
`dcw/journal/index.get.ts` (`recipe-rollback-manager.ts:652-654`), `dcw/[id]/param-ledger.get.ts` (`:627-649`),
|
|
341
|
-
`dcw/optimizations/index.get.ts` (`:656-658`), `dcw/optimizations/[id].get.ts`,
|
|
342
|
-
`dcw/optimizations/[id]/series.get.ts` (`:602-623` read-only), `daq/alarms.get.ts` (`daq-controller.ts:600-604`),
|
|
343
|
-
`daq/infra.get.ts`, `runtime.get.ts` (`manager.ts:803-808`), `tasks.get.ts`, `mailbox.get.ts`
|
|
344
|
-
(`manager.ts:2834-2840`; the query has no LIMIT — see §3).
|
|
345
|
-
|
|
346
|
-
Note the memory list GET `channels/[id]/agents/[agentId]/memories/index.get.ts:28` calls `manager.listMemories`
|
|
347
|
-
which is a pure read (`manager.ts:1421-1426`).
|
|
348
|
-
|
|
349
|
-
---
|
|
350
|
-
|
|
351
|
-
## 3. UNBOUNDED STATE + PERF
|
|
352
|
-
|
|
353
|
-
### 3.1 Module-level maps/sets/arrays in scope
|
|
354
|
-
|
|
355
|
-
1. **`server/api/workshop/caller.ts:122` — unbounded, no eviction.**
|
|
356
|
-
```ts
|
|
357
|
-
const nameCache = new Map<string, { name: string | null, at: number }>()
|
|
358
|
-
```
|
|
359
|
-
Writes (all writes in the file):
|
|
360
|
-
```ts
|
|
361
|
-
// caller.ts:124-134
|
|
362
|
-
const hit = nameCache.get(userId)
|
|
363
|
-
if (hit && Date.now() - hit.at < 60_000) return hit.name
|
|
364
|
-
...
|
|
365
|
-
nameCache.set(userId, { name, at: Date.now() })
|
|
366
|
-
```
|
|
367
|
-
The 60 s TTL is consulted only when the **same** key is read again; there is **no eviction found** (no sweep,
|
|
368
|
-
no size cap, no `delete`). Every distinct `ownerUserId` ever rendered by `withOwnerNames` stays for the process
|
|
369
|
-
lifetime. `withOwnerNames` (`caller.ts:105-119`) creates a per-call `Map` (`:106`) which is request-scoped (fine).
|
|
370
|
-
2. **`server/services/workshop/scene-events.ts:20-27` — unbounded, no cap.**
|
|
371
|
-
```ts
|
|
372
|
-
const g = globalThis as typeof globalThis & {
|
|
373
|
-
__sceneEventPeers?: Set<WsPeer>
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/** 在线 peer 注册表(ws 路由 sub 成功时登记,断开/退订移除) */
|
|
377
|
-
function peers(): Set<WsPeer> {
|
|
378
|
-
return g.__sceneEventPeers ??= new Set()
|
|
379
|
-
}
|
|
380
|
-
```
|
|
381
|
-
Write: `scene-events.ts:50-52 export function registerScenePeer(peer: WsPeer): void { peers().add(peer) }`.
|
|
382
|
-
Full analysis in §5.
|
|
383
|
-
3. **`server/services/workshop/runtime/manager.ts:329` — in the invoke path, unbounded, no eviction.**
|
|
384
|
-
```ts
|
|
385
|
-
private readonly lastToolInvokeAt = new Map<string, number>()
|
|
386
|
-
```
|
|
387
|
-
Writes: `manager.ts:2757 this.lastToolInvokeAt.set(input.agentId, Date.now())` (every `invokeHostTool` call);
|
|
388
|
-
reads: `manager.ts:704 toolActivityOf: (agentId: string) => this.lastToolInvokeAt.get(agentId) ?? null`.
|
|
389
|
-
No `delete`/cap → one entry per distinct agentId ever invoked, forever (small, but unbounded and attacker-reachable
|
|
390
|
-
via §1.1, e.g. spraying `agentId` values that exist).
|
|
391
|
-
4. **`server/services/workshop/agents/tool-approvals.ts:37-43` — bounded.**
|
|
392
|
-
```ts
|
|
393
|
-
private pending = new Map<string, { approval: ToolApproval, resolve: ..., timer: NodeJS.Timeout }>()
|
|
394
|
-
...
|
|
395
|
-
private history: ToolApproval[] = []
|
|
396
|
-
```
|
|
397
|
-
Eviction exists: timeout deletes (`:75 this.pending.delete(approval.id)`), decide deletes (`:92`),
|
|
398
|
-
cancel deletes (`:117`), history capped (`:162 if (this.history.length > HISTORY_CAP) this.history.splice(HISTORY_CAP)`).
|
|
399
|
-
5. `scene-events.ts` per-peer visible-lines is stashed on the peer object (`:35`), no separate collection.
|
|
400
|
-
6. Request-scoped only (not leaks): `harnesses.get.ts:18`, `channels/[id]/plugins/index.put.ts:30`,
|
|
401
|
-
`channels.post.ts:51`, `daq/alarms/[id]/ack.post.ts` (no map), `ws.ts:188 peerBudget = new WeakMap(...)` (GC-safe).
|
|
402
|
-
`harness-terminal.ts` `sessions` Map has a sweep — `harness-terminal.ts:469-479 sweepTerminalSessions(...)`
|
|
403
|
-
(`sessions.delete(pid)`), invoked from `manager.ts:816`.
|
|
404
|
-
|
|
405
|
-
### 3.2 Full-table fetch + JS filtering
|
|
406
|
-
|
|
407
|
-
* `server/api/workshop/dcw/index.get.ts:29-36` — five full arrays filtered in JS:
|
|
408
|
-
`recipes: visible ? ctrl.listRecipes().filter(r => inLine(r.lineId)) : ctrl.listRecipes()`, likewise `runs`,
|
|
409
|
-
`products`, `lines`, `lineStates`.
|
|
410
|
-
* `server/api/workshop/dcw/index.get.ts:31-33` — **N+1 in JS**: `ctrl.listHistory(400).filter(h => inLine(nodeLine(h.nodeId))).slice(0, 60)`
|
|
411
|
-
where `nodeLine = (nodeId: string): string | undefined => ctrl.byId(nodeId)?.lineId` (`:22`) and
|
|
412
|
-
`byId` is a linear scan: `dcw-node.repo.ts:40-42 byId(id: string): DcwNode | undefined { return this.list.find(n => n.id === id) }`
|
|
413
|
-
→ up to 400 × nodeCount comparisons per request.
|
|
414
|
-
* `server/api/workshop/agent-tools/bindings/index.get.ts:13` — `getAgentNodeBindingRepo().all()` returns the whole JSON list (`node-bindings.repo.ts:48-50`).
|
|
415
|
-
* `server/api/workshop/dcw/recipes/index.get.ts:11` — `{ recipes: repo.list(), runs: repo.listRuns() }`, no filter/limit.
|
|
416
|
-
* `server/api/workshop/channels/[id]/tasks/index.get.ts:23` — `taskEngineOf(manager).list(channelId)` →
|
|
417
|
-
`task.repo.ts:67 SELECT ${COLS} FROM tasks WHERE channel_id = ? ORDER BY createdAt ASC, rowid ASC` — **no LIMIT**; every task row of the channel is returned.
|
|
418
|
-
* `server/api/workshop/mailbox.get.ts:12-13` — client `limit` (uncapped) then fetch-all-and-slice:
|
|
419
|
-
`manager.ts:2834-2839 pollMailbox(...) { this.requireMember(...); return this.deps.repos.messages.listPendingByChannelAgent(channelId, callerAgentId).slice(0, limit).map(rowToMessage) }` — the repo query at that call site passes no limit (`message.repo.ts:91-95` has a `LIMIT ?` variant, unused here).
|
|
420
|
-
* `manager.ts:1552 listByAgent(targetAgentId, 10_000)` (delete memory path) and
|
|
421
|
-
`manager.ts:1489 listByAgentChannel(channelId, TEAM_AGENT_ID, 1_000_000)` (delete team memory) — huge limits.
|
|
422
|
-
* Member lookups fetch the whole roster then search: `channels/[id]/agents/[agentId].patch.ts:20-21`,
|
|
423
|
-
`[agentId].delete.ts:19-20`, `[agentId]/stop.post.ts:20-21`, `model.patch.ts:22-23`, `position.patch.ts:21-22`,
|
|
424
|
-
`range.patch.ts:21-22` all do `const members = await manager.listChannelAgents(channelId)` + `.some(...)`/`.find(...)`.
|
|
425
|
-
* `dcw/line/query.get.ts:26` → `dcw-controller.ts:888 getDaqNodeRepo().all().filter(...)`, then one TSDB round trip.
|
|
426
|
-
* `dcw/runs/[id]/data.get.ts:19` → `dcw-controller.ts:979-986`: `getDaqNodeRepo().all().filter(...)` then
|
|
427
|
-
`await Promise.all(lineNodes.map(async (node) => ... getTsdb().query(node.id, ...)))` — one TSDB query **per node**
|
|
428
|
-
(documented as intentional at `:977`).
|
|
429
|
-
* `channels/[id]/agents/[agentId]/model.patch.ts:34` — `for (const a of repo.listAll())` full asset list per request.
|
|
430
|
-
* `agents/index.get.ts:12` → `withOwnerNames(templates)` does one user lookup per template, 60 s-cached, never evicted (`caller.ts:105-135`).
|
|
431
|
-
|
|
432
|
-
### 3.3 Limits
|
|
433
|
-
|
|
434
|
-
Huge / unbounded as passed from the route:
|
|
435
|
-
|
|
436
|
-
* `mailbox.get.ts:12-13` — no upper bound: `const limit = Number.parseInt(url.searchParams.get('limit') ?? '50', 10)` … `limit > 0 ? limit : 50`.
|
|
437
|
-
* `channels/[id]/tasks/index.get.ts:23` — no limit parameter at all.
|
|
438
|
-
* `dcw/index.get.ts:32` — `listHistory(400)` (then slices to 60).
|
|
439
|
-
* `dcw/line/query.get.ts:35`, `dcw/[id]/param-ledger.get.ts` (`limit: 30`/`20` internal), `dcw/[id]/samples`:
|
|
440
|
-
`limit: num(q.limit)` passed raw, clamped only downstream at the adapter:
|
|
441
|
-
`timescale.adapter.ts:109 const limit = Math.min(opts.limit ?? 500, 5000)` / `:157 Math.min(q.limit ?? 2000, 10_000)`;
|
|
442
|
-
`sqlite.adapter.ts:128`, `:182` identical.
|
|
443
|
-
* Properly capped: `audit.get.ts:15 Math.min(500, Math.max(1, Number(q.limit) || 100))`,
|
|
444
|
-
`dcw/journal/index.get.ts:18 Math.min(limit, 500)`, `dcw/optimizations/index.get.ts:20`, `events.get.ts:18 limit: z.coerce.number().int().min(1).max(1000)`,
|
|
445
|
-
`messages/index.get.ts:13 max(500)`, `daq/alarms.get.ts:14 Math.min(500, ...)`, `daq/[id]/frames` clamp at
|
|
446
|
-
`daq-controller.ts:849 Math.min(Math.max(opts.limit ?? 100, 1), 1000)`, `manager.listChannelMail` `Math.min(500, ...)` (`manager.ts:2874`).
|
|
447
|
-
|
|
448
|
-
---
|
|
449
|
-
|
|
450
|
-
## 4. ERROR PROPAGATION
|
|
451
|
-
|
|
452
|
-
### 4.1 Swallowed errors that hide failures
|
|
453
|
-
|
|
454
|
-
* `server/api/workshop/caller.ts:127-132` — user-name resolution failure is invisible:
|
|
455
|
-
```ts
|
|
456
|
-
try {
|
|
457
|
-
name = userRepository.findById(userId)?.name ?? null
|
|
458
|
-
}
|
|
459
|
-
catch {
|
|
460
|
-
name = null
|
|
461
|
-
}
|
|
462
|
-
```
|
|
463
|
-
`withOwnerNames` then renders `ownerName: null` (`caller.ts:113-118`) with no log — a broken `users.sqlite`
|
|
464
|
-
looks like "no owner" in every list that uses it (`agents/index.get.ts:12`).
|
|
465
|
-
* `server/api/workshop/caller.ts:28-33` — `resolveCallerOrNull` swallows every error including 401:
|
|
466
|
-
```ts
|
|
467
|
-
try {
|
|
468
|
-
return resolveCaller(event)
|
|
469
|
-
}
|
|
470
|
-
catch {
|
|
471
|
-
return null
|
|
472
|
-
}
|
|
473
|
-
```
|
|
474
|
-
Consumed at `channels/[id]/agents/index.get.ts:20-23`, where a genuine failure of the user path is silently
|
|
475
|
-
downgraded to the agent-token path.
|
|
476
|
-
* `server/api/workshop/audit.get.ts:24-29` — corrupt audit detail is silently replaced by `{}`:
|
|
477
|
-
```ts
|
|
478
|
-
try {
|
|
479
|
-
detail = JSON.parse(String(r.detailJson ?? '{}'))
|
|
480
|
-
}
|
|
481
|
-
catch {
|
|
482
|
-
detail = {}
|
|
483
|
-
}
|
|
484
|
-
```
|
|
485
|
-
* `server/api/workshop/dcw/[id].delete.ts:18` — `const body = await readBody<{ approvalId?: string }>(event).catch(() => ({})) ?? {}` (deliberate, DELETE-with-empty-body).
|
|
486
|
-
* Sanctioned-but-opaque fire-and-forget in the memory write paths: `manager.ts:1478`,
|
|
487
|
-
`manager.ts:1515-1521`, `manager.ts:1540` — `void vectorizeMemory(...).catch(() => {})` and
|
|
488
|
-
`.then(...).catch(() => {})` (vectorization failures never surface).
|
|
489
|
-
* `server/services/workshop/permissions.ts:70` and `:76` — `}).catch(() => {})` on both broadcast paths
|
|
490
|
-
(`broadcastSceneEvent('permissions.changed')`, `emitPluginEvent('permissions:changed')`): a failed permission-change
|
|
491
|
-
broadcast means clients keep stale grants with no log.
|
|
492
|
-
* `server/services/workshop/scene-events.ts:74-79` — send failures delete the peer silently (see §5).
|
|
493
|
-
* `server/services/workshop/daq/daq-controller.ts:1189` — `.catch(() => {})` on queue start;
|
|
494
|
-
`dcw-controller.ts:836` — `.catch(() => {})` on `markLineOffline`.
|
|
495
|
-
|
|
496
|
-
### 4.2 Internal error messages returned to the client (info leak)
|
|
497
|
-
|
|
498
|
-
* `server/services/workshop/runtime/manager.ts:2785-2788` — raw internal `Error.message` is returned to the HTTP
|
|
499
|
-
caller as tool output (this is the endpoint of §1.1):
|
|
500
|
-
```ts
|
|
501
|
-
catch (err) {
|
|
502
|
-
const reason = err instanceof Error ? err.message : String(err)
|
|
503
|
-
return { text: `工具「${input.tool}」调用失败: ${reason}`, isError: true }
|
|
504
|
-
}
|
|
505
|
-
```
|
|
506
|
-
e.g. SQLite constraint text, filesystem paths from `saveJsonFileAtomic`, driver/Modbus errors.
|
|
507
|
-
* `server/api/workshop/ws.ts:739` — `message: error instanceof Error ? error.message : 'workshop 未初始化'` is sent
|
|
508
|
-
to the client in an `error` control frame.
|
|
509
|
-
* Generic 500 path is clean: `server/utils/response.ts:44-46` logs the error and returns
|
|
510
|
-
`{ code: 'INTERNAL_ERROR', message: '服务器内部错误' }` — no stack, no message.
|
|
511
|
-
|
|
512
|
-
### 4.3 Status-code mis-mapping (failures reported as 500)
|
|
513
|
-
|
|
514
|
-
`server/api/workshop/daq/[id]/samples.get.ts` / `frames.get.ts` / `frames/content.get.ts` call controller methods
|
|
515
|
-
that throw a **plain** `Error` carrying a non-standard `status` property:
|
|
516
|
-
|
|
517
|
-
```ts
|
|
518
|
-
// server/services/workshop/daq/daq-controller.ts:797
|
|
519
|
-
if (!node) throw Object.assign(new Error(`数采节点不存在: ${id}`), { status: 404 })
|
|
520
|
-
```
|
|
521
|
-
```ts
|
|
522
|
-
// server/services/workshop/daq/daq-controller.ts:857, :862
|
|
523
|
-
if (!node) throw Object.assign(new Error(`数采节点不存在: ${id}`), { status: 404 })
|
|
524
|
-
...
|
|
525
|
-
if (!row || row.kind !== 'image') throw Object.assign(new Error(`帧不存在: ${id}@${tsMs}`), { status: 404 })
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
`defineApiHandler` only honors `AppError` (`response.ts:30-33`) and `H3Error` (`:34-43`); a plain `Error` falls to
|
|
529
|
-
`console.error('[api] unhandled error:', error)` + HTTP 500 (`response.ts:44-46`). So "node not found" surfaces to
|
|
530
|
-
the UI as a 500, and the internal process is logged as an unhandled error.
|
|
531
|
-
|
|
532
|
-
---
|
|
533
|
-
|
|
534
|
-
## 5. SCENE EVENTS (`server/services/workshop/scene-events.ts`)
|
|
535
|
-
|
|
536
|
-
**Subscriber tracking** — a `globalThis`-backed `Set` of duck-typed peers:
|
|
537
|
-
|
|
538
|
-
```ts
|
|
539
|
-
// scene-events.ts:14-18
|
|
540
|
-
interface WsPeer {
|
|
541
|
-
send(data: string | Uint8Array): void
|
|
542
|
-
close(code?: number, reason?: string): void
|
|
543
|
-
}
|
|
544
|
-
```
|
|
545
|
-
```ts
|
|
546
|
-
// scene-events.ts:20-27
|
|
547
|
-
const g = globalThis as typeof globalThis & {
|
|
548
|
-
__sceneEventPeers?: Set<WsPeer>
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/** 在线 peer 注册表(ws 路由 sub 成功时登记,断开/退订移除) */
|
|
552
|
-
function peers(): Set<WsPeer> {
|
|
553
|
-
return g.__sceneEventPeers ??= new Set()
|
|
554
|
-
}
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
**Subscribe path** (only after user auth; a per-peer line-visibility set is stamped on the peer object):
|
|
558
|
-
|
|
559
|
-
```ts
|
|
560
|
-
// server/api/workshop/ws.ts:174-177
|
|
561
|
-
function attachScenePeer(peer: WsPeer, user: { id: string, role: string }): void {
|
|
562
|
-
setPeerVisibleLines(peer, visibleLineIds(user))
|
|
563
|
-
registerScenePeer(peer)
|
|
564
|
-
}
|
|
565
|
-
```
|
|
566
|
-
```ts
|
|
567
|
-
// scene-events.ts:34-36
|
|
568
|
-
export function setPeerVisibleLines(peer: WsPeer, visible: Set<string> | null): void {
|
|
569
|
-
(peer as WsPeer & { __awVisibleLines?: Set<string> | null }).__awVisibleLines = visible
|
|
570
|
-
}
|
|
571
|
-
```
|
|
572
|
-
```ts
|
|
573
|
-
// scene-events.ts:50-52
|
|
574
|
-
export function registerScenePeer(peer: WsPeer): void {
|
|
575
|
-
peers().add(peer)
|
|
576
|
-
}
|
|
577
|
-
```
|
|
578
|
-
Call sites: `ws.ts:176` only (grep: `registerScenePeer` appears in `scene-events.ts:50` and `ws.ts:176`).
|
|
579
|
-
|
|
580
|
-
**Unsubscribe path — exists, on socket close and error:**
|
|
581
|
-
|
|
582
|
-
```ts
|
|
583
|
-
// server/api/workshop/ws.ts:753-762
|
|
584
|
-
close(peer) {
|
|
585
|
-
unsubscribePeer(peer)
|
|
586
|
-
unregisterScenePeer(peer)
|
|
587
|
-
},
|
|
588
|
-
|
|
589
|
-
error(peer, error) {
|
|
590
|
-
console.error('[workshop-ws] connection error:', error)
|
|
591
|
-
unsubscribePeer(peer)
|
|
592
|
-
unregisterScenePeer(peer)
|
|
593
|
-
},
|
|
594
|
-
```
|
|
595
|
-
```ts
|
|
596
|
-
// scene-events.ts:54-56
|
|
597
|
-
export function unregisterScenePeer(peer: WsPeer): void {
|
|
598
|
-
peers().delete(peer)
|
|
599
|
-
}
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
**Max subscriber count: NONE.** There is no cap anywhere — grep for `__sceneEventPeers` yields only `scene-events.ts:21,26`;
|
|
603
|
-
`registerScenePeer` (`:50-52`) performs an unconditional `add` with no size check, and `sceneEventPeerCount()`
|
|
604
|
-
(`:58-60`) is exported but never called by any handler (grep: only its own declaration).
|
|
605
|
-
|
|
606
|
-
**Dead-subscriber removal (two mechanisms, both partial):**
|
|
607
|
-
|
|
608
|
-
1. Close/error hooks above.
|
|
609
|
-
2. Lazy removal when a send throws:
|
|
610
|
-
```ts
|
|
611
|
-
// scene-events.ts:72-80
|
|
612
|
-
for (const peer of peers()) {
|
|
613
|
-
if (lineId !== undefined && !peerSeesLine(peer, lineId)) continue
|
|
614
|
-
try {
|
|
615
|
-
peer.send(frame)
|
|
616
|
-
}
|
|
617
|
-
catch {
|
|
618
|
-
peers().delete(peer)
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
```
|
|
622
|
-
same pattern in `broadcastPeerEvent` (`:92-99`).
|
|
623
|
-
|
|
624
|
-
**Can a subscriber leak? Yes — with the caveat that the primary paths are covered.**
|
|
625
|
-
* The removal depends entirely on the ws route's `close`/`error` hooks firing (`ws.ts:753-762`). There is **no
|
|
626
|
-
heartbeat/liveness timeout and no size cap** in the scene-peer set itself: a peer whose `send()` never throws
|
|
627
|
-
(e.g. half-open TCP where the write lands in the kernel buffer) and whose close hook never fires stays registered
|
|
628
|
-
for the process lifetime. The slow-consumer guard at `ws.ts:199-206` (`peer.close(1013, 'slow consumer')` +
|
|
629
|
-
`stream.peers.delete(peer)`) exists but only bounds *stream* fan-out, and relies on that same close hook to reach
|
|
630
|
-
`unregisterScenePeer`.
|
|
631
|
-
* The registry lives on `globalThis` (`scene-events.ts:20-22`) while peers are created per module instance, so any
|
|
632
|
-
module re-evaluation (dev HMR / nitro re-import) keeps the old peer objects in the same Set until their close hook
|
|
633
|
-
fires; nothing prunes stale entries.
|
|
634
|
-
* Broadcast cost is unbounded per frame: `broadcastSceneEvent` (`:66-81`) serializes once (`:71`) then loops all
|
|
635
|
-
peers and `send`s synchronously — with N pages open, every scene event costs N sends (by design per the comment at
|
|
636
|
-
`:62-65`), so a subscriber-count leak degrades CPU linearly with no ceiling.
|
|
637
|
-
|
|
638
|
-
---
|
|
639
|
-
|
|
640
|
-
## Priority summary
|
|
641
|
-
|
|
642
|
-
1. **P0** `agent-tools/invoke.post.ts:18+25` + `manager.ts:2750` — any user token + arbitrary `agentId` ⇒ act as any
|
|
643
|
-
agent (PLC writes, recipe writes, model promotion, arbitrary training Python). Unfixed; previously documented at
|
|
644
|
-
`docs/audit/audit-agents.md:186-249`.
|
|
645
|
-
2. **P0** `bindings/[id].patch.ts` — any user can flip any binding to `mode:'auto'`, silently removing HITL from
|
|
646
|
-
another team's control path.
|
|
647
|
-
3. **P1** `dcw/[id]/read.post.ts`, `dcw/[id].patch.ts`, `dcw/index.post.ts`, `dcw/lines/[id].delete.ts`,
|
|
648
|
-
`daq/[id].delete.ts`, `daq/[id]/test.post.ts`, `daq/alarms.get.ts`, `dcw/journal/*`, `dcw/optimizations/*`,
|
|
649
|
-
`dcw/recipes/index.get.ts`, `dcw/recipes/[id]/versions.get.ts`, `dcw/[id]/param-ledger.get.ts` — authenticated
|
|
650
|
-
but unscoped access to industrial data/control.
|
|
651
|
-
4. **P1** `bindings/index.post.ts:33-38` — binds any `agentId` without resolving or authorizing it.
|
|
652
|
-
5. **P2** GET handlers that start acquisition/pipelines and insert rows (`daq/index.get.ts:20-22`,
|
|
653
|
-
`daq/[id]/samples.get.ts`→`daq-controller.ts:794`, `channels/[id]/index.get.ts:16`).
|
|
654
|
-
6. **P2** `daq/test-driver.post.ts` / `dcw/test-driver.post.ts` — caller-controlled outbound connect primitive.
|
|
655
|
-
7. **P3** `caller.ts:122` uncapped `nameCache`; `manager.ts:329` uncapped `lastToolInvokeAt`; no scene-peer cap.
|
|
656
|
-
8. **P3** `daq` 404s surface as 500 (`daq-controller.ts:797,838,857,862` vs `response.ts:29-47`).
|
|
657
|
-
9. **P3** swaggered error text: `manager.ts:2787` returns raw `err.message` to callers.
|
|
1
|
+
# HTTP API authz / read-path / perf audit — agents + runtime + industrial nodes
|
|
2
|
+
|
|
3
|
+
Read-only audit. Every claim cites `file:line` with verbatim code. No source file was modified.
|
|
4
|
+
|
|
5
|
+
Scope read in full: `server/api/workshop/agent-tools/**`, `agents/**`, `channels/**`, `dcw/**`, `daq/**`,
|
|
6
|
+
`runtime.get.ts`, `tasks.get.ts`, `mailbox.get.ts`, `caller.ts`, `server/services/workshop/scene-events.ts`,
|
|
7
|
+
`server/services/workshop/agents/agent-badge.ts`, plus the guards they delegate to
|
|
8
|
+
(`permissions.ts`, `runtime/manager.ts`, `agents/tool-approvals.ts`, `agents/node-bindings.repo.ts`,
|
|
9
|
+
`agents/host-tool-bridge.ts`, `agents/industrial-tools.ts`, `dcw/dcw-controller.ts`, `daq/daq-controller.ts`,
|
|
10
|
+
`aml/job-orchestrator.ts`, `aml/python-runtime.ts`).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. AUTHZ AUDIT
|
|
15
|
+
|
|
16
|
+
### 0. Global facts
|
|
17
|
+
|
|
18
|
+
* No global auth middleware exists. The only middleware is `server/middleware/request-log.ts:7` — logging only.
|
|
19
|
+
* Unmatched `/api/workshop/**` paths hit `server/api/workshop/[...path].ts:12` → static 404.
|
|
20
|
+
* Auth primitives (`server/api/workshop/caller.ts`):
|
|
21
|
+
* `resolveUser` (`:71-79`) — Bearer **user** token, else 401.
|
|
22
|
+
* `resolveCaller` (`:16-24`) — Bearer **agent instance** token, else 401.
|
|
23
|
+
* `resolveAgentOrUser` (`:45-54`) — either domain.
|
|
24
|
+
* `requireAdmin` (`:82-88`), `requireRole` (`:91-97`).
|
|
25
|
+
* Line-grant model (`server/services/workshop/permissions.ts`): `isPrivilegedRole` (`:26-28`),
|
|
26
|
+
`lineMode` (`:31-36`), `visibleLineIds` (`:39-42`), `filterByLine` (`:45-52`),
|
|
27
|
+
`requireLineMode` (`:55-62`). admin/editor bypass everything.
|
|
28
|
+
|
|
29
|
+
### 1.1 [P0] `agent-tools/invoke.post.ts` — user token + client-supplied `agentId` = full agent impersonation
|
|
30
|
+
|
|
31
|
+
The whole handler (27 lines):
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// server/api/workshop/agent-tools/invoke.post.ts:16-27
|
|
35
|
+
export default defineApiHandler(async (event) => {
|
|
36
|
+
const agentToken = getHeader(event, 'x-aw-agent-token')
|
|
37
|
+
if (!agentToken) resolveUser(event) // 桥路径免用户鉴权,由 token 校验兜底
|
|
38
|
+
const body = await readBody<{ agentId?: string, tool?: string, args?: Record<string, unknown> }>(event) ?? {}
|
|
39
|
+
const agentId = String(body.agentId ?? '')
|
|
40
|
+
const tool = String(body.tool ?? '')
|
|
41
|
+
const args = body.args ?? {}
|
|
42
|
+
if (!agentId) throw createError({ statusCode: 400, statusMessage: 'agentId required' })
|
|
43
|
+
if (!tool) throw createError({ statusCode: 400, statusMessage: 'tool required' })
|
|
44
|
+
const result = await getWorkshopManager().invokeHostTool({ agentId, tool, args, token: agentToken || undefined })
|
|
45
|
+
return { result }
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Note `invoke.post.ts:18`: `resolveUser(event)`'s **return value is discarded** — the authenticated user is never
|
|
50
|
+
passed downstream. `agentId` comes only from the request body (`:20`).
|
|
51
|
+
|
|
52
|
+
The guard it delegates to:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// server/services/workshop/runtime/manager.ts:2746-2752
|
|
56
|
+
async invokeHostTool(input: { agentId: string, token?: string, tool: string, args?: Record<string, unknown> }): Promise<{ text: string, isError?: boolean }> {
|
|
57
|
+
const row = this.deps.repos.channelAgents.findById(input.agentId)
|
|
58
|
+
if (!row || row.enabled !== 1) throw new AppError(404, 'NOT_FOUND', `agent 不存在或已停用: ${input.agentId}`)
|
|
59
|
+
// token 鉴权(MCP 桥路径必带;缺省视为服务端内部调用,走 REST 已有用户鉴权)
|
|
60
|
+
if (input.token !== undefined && input.token !== row.token) {
|
|
61
|
+
throw new AppError(401, 'UNAUTHORIZED', 'agent token 校验失败')
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`invoke.post.ts:25` passes `token: agentToken || undefined`. **Consequences, exactly:**
|
|
66
|
+
|
|
67
|
+
| Request | `agentToken` | `input.token` | `manager.ts:2750` | Result |
|
|
68
|
+
|---|---|---|---|---|
|
|
69
|
+
| valid **user** Bearer, **no** `x-aw-agent-token`, `agentId` = any other agent | undefined | `undefined` | `input.token !== undefined` is **false** → check **skipped** | **Executes any tool as that agent** |
|
|
70
|
+
| **no** `Authorization` at all, no `x-aw-agent-token` | undefined | — never reached | — | `resolveUser` at `invoke.post.ts:18` throws `AppError(401,'USER_UNAUTHORIZED')` (`caller.ts:76`) → **blocked** |
|
|
71
|
+
| own agent token in `x-aw-agent-token`, `agentId` = another agent | own token | own token | `!== row.token` → true | 401 — **blocked** |
|
|
72
|
+
| valid agent token, matching `agentId` | token | `=== row.token` | passes | legitimate |
|
|
73
|
+
|
|
74
|
+
So the hole is exactly: **valid user token + arbitrary body `agentId` + absent `x-aw-agent-token`.** No ownership,
|
|
75
|
+
membership or role check exists anywhere on this path. The authenticated `ResolvedUser` never reaches `manager`.
|
|
76
|
+
|
|
77
|
+
After the guard, the impersonated identity drives every downstream authorization:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
// server/services/workshop/runtime/manager.ts:2769-2775
|
|
81
|
+
const identity = {
|
|
82
|
+
agentId: row.id,
|
|
83
|
+
channelId: row.channelId,
|
|
84
|
+
role: (row.role === 'lead' ? 'lead' : 'worker') as 'lead' | 'worker',
|
|
85
|
+
name: row.name,
|
|
86
|
+
}
|
|
87
|
+
return await pluginTool.handler(input.args ?? {}, identity)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// server/services/workshop/runtime/manager.ts:2777-2783
|
|
92
|
+
const runtime = this.ensureAgentRuntime(row.channelId, input.agentId)
|
|
93
|
+
if (runtime) {
|
|
94
|
+
const viaImpl = await runtime.dispatchHostTool(input.tool, input.args ?? {})
|
|
95
|
+
if (viaImpl) return viaImpl
|
|
96
|
+
}
|
|
97
|
+
// 回退:impl 未实现工具面(如 mock)→ 协作工具族直调
|
|
98
|
+
return await this.invokeAgentWorkspaceTool(input.agentId, input.tool, input.args ?? {})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`invokeHostTool` also applies **no role/tool filtering** — unlike `hostToolsForRole`
|
|
102
|
+
(`host-tool-bridge.ts:70-87`) it never consults `LEAD_ONLY_TOOL_NAMES` (`host-tool-bridge.ts:33-43`).
|
|
103
|
+
Every tool is dispatchable for any named agent that exists and is enabled.
|
|
104
|
+
|
|
105
|
+
**Which tool classes are reachable, by whom**
|
|
106
|
+
|
|
107
|
+
Reachable by *any* authenticated user token of *any* role (including a plain `user` with zero line grants):
|
|
108
|
+
pick any lead/worker agent id.
|
|
109
|
+
|
|
110
|
+
* **Read-only** (dispatch table `host-tool-bridge.ts:162-211` + `:219+`):
|
|
111
|
+
`my_industrial_nodes`, `aml_node_catalog`, `aml_dataset_stats`, `aml_job_status`, `aml_job_logs`,
|
|
112
|
+
`aml_leaderboard`, `aml_model_reference`, `dcw_read`, `daq_query`, `daq_frames`, `dcw_journal`, `ops_log`,
|
|
113
|
+
`recipe_log`, `recipe_versions`, `line_context`, `list_other_teams`, `search_other_teams_memory`,
|
|
114
|
+
mailbox/list-mail/memory tools.
|
|
115
|
+
* **Mutating / control-plane**:
|
|
116
|
+
* `dcw_control` — real PLC write. Its only authz is the *impersonated agent's* binding:
|
|
117
|
+
```ts
|
|
118
|
+
// server/services/workshop/agents/industrial-tools.ts:99-108
|
|
119
|
+
const repo = getAgentNodeBindingRepo()
|
|
120
|
+
const binding: AgentNodeBinding | undefined = nodeId ? repo.find(agentId, nodeId, 'dcw') : undefined
|
|
121
|
+
if (!binding) {
|
|
122
|
+
```
|
|
123
|
+
HITL applies only when `binding.mode === 'manual'` (`industrial-tools.ts:128`); with `mode:'auto'`
|
|
124
|
+
the write executes immediately (`industrial-tools.ts:157`).
|
|
125
|
+
* `recipe_update` (`host-tool-bridge.ts:207-208`), `recipe_rollback` (`:209-210`), `dcw_judge` (`:193-194`),
|
|
126
|
+
`dcw_rollback` (`:195-196`), `aml_dataset_build` (`:167-168`), `aml_job_cancel` (`:177-178`),
|
|
127
|
+
`send_cross_channel_message` (`manager.ts:2725-2734`), `dispatch_task`, `create_team_agent`,
|
|
128
|
+
`update_team_agent`, `remove_team_agent`, `complete_task`, `report_progress`.
|
|
129
|
+
* `aml_model_promote` — nominally lead-only; its check is against the **DB role of the supplied agentId**:
|
|
130
|
+
```ts
|
|
131
|
+
// server/services/workshop/agents/industrial-tools.ts:1290-1294
|
|
132
|
+
// 治理硬守卫:晋升仅限 lead 实例(注入层过滤 + 分发层双重校验,HTTP 直调不可绕过)
|
|
133
|
+
const roleRow = getAmlRuntime().db.prepare('SELECT role FROM channel_agents WHERE id = ? AND enabled = 1').get(agentId) as { role?: string } | undefined
|
|
134
|
+
if (roleRow?.role !== 'lead') {
|
|
135
|
+
return amlErr(`aml_model_promote 为 lead 专属工具:实例 ${agentId}(role=${roleRow?.role ?? '未知'})无权晋升模型。请由团队 lead 发起,晋升需人工 HITL 批准。`)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
Naming a lead's `agentId` satisfies it (then HITL approval at `industrial-tools.ts:1316`).
|
|
139
|
+
* `aml_job_submit` — **arbitrary Python execution on the server host.** The tool takes up to 500 KB of code:
|
|
140
|
+
```ts
|
|
141
|
+
// server/services/workshop/agents/industrial-tools.ts:1152-1156
|
|
142
|
+
if (!code.trim()) return amlErr('code 必填:train.py 全文(单文件;契约见工具说明)。')
|
|
143
|
+
if (code.length > 512_000) return amlErr(`code 过大(${Math.round(code.length / 1024)}KB),上限 500KB;精简训练代码(模型定义/训练循环拆薄)后重试。`)
|
|
144
|
+
if (!/\bimport\s+amlkit\b/.test(code)) {
|
|
145
|
+
```
|
|
146
|
+
It is written to disk and executed:
|
|
147
|
+
```ts
|
|
148
|
+
// server/services/workshop/aml/job-orchestrator.ts:285-287
|
|
149
|
+
if (typeof inline === 'string' && inline.length > 0 && inline.length <= 512_000) {
|
|
150
|
+
writeFileSync(join(workspace, 'train.py'), inline)
|
|
151
|
+
trainFile = 'train.py'
|
|
152
|
+
```
|
|
153
|
+
```ts
|
|
154
|
+
// server/services/workshop/aml/job-orchestrator.ts:328
|
|
155
|
+
const exit1 = await runPython(run, vpy, [join(workspace, trainFile)], workspace)
|
|
156
|
+
```
|
|
157
|
+
No container/OS sandbox; the only mitigation is a scrubbed env
|
|
158
|
+
(`aml/python-runtime.ts:186-214`, `jobEnv` whitelist, "服务器 secrets … 一律不可见").
|
|
159
|
+
The `import amlkit` requirement is a regex (`industrial-tools.ts:1154`), trivially satisfied.
|
|
160
|
+
* Plugin tools: identity is the impersonated agent (`manager.ts:2769-2775`), no independent check —
|
|
161
|
+
e.g. `diag_run` (`server/plugins-builtin/diag-bridge/index.mjs:449-450`) exports line DAQ snapshots,
|
|
162
|
+
`kb_store`/`kb_index` (`server/plugins-builtin/rag-bridge/index.mjs:199,259`) mutate the knowledge base.
|
|
163
|
+
|
|
164
|
+
**Contrast with the sibling endpoint, which does it right:**
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// server/api/workshop/agent-tools/list.get.ts:17-22
|
|
168
|
+
if (agentToken) {
|
|
169
|
+
const resolved = getWorkshopManager().resolveAgentByToken(String(agentToken))
|
|
170
|
+
if (!resolved || resolved.agentId !== agentId) {
|
|
171
|
+
throw createError({ statusCode: 401, statusMessage: 'agent token 校验失败' })
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Enumeration is trivial**, so no guessing is needed: `agent-tools/bindings/index.get.ts:13` returns
|
|
177
|
+
`{ bindings: agentId ? getAgentNodeBindingRepo().byAgent(agentId) : getAgentNodeBindingRepo().all() }` — with no
|
|
178
|
+
`agentId` query param, **every** binding of every agent (agentId + nodeId + kind + mode) is returned to any
|
|
179
|
+
authenticated user.
|
|
180
|
+
|
|
181
|
+
⚠UNVERIFIED: I did not execute a live exploit; the conclusion is from the code paths above (all read directly).
|
|
182
|
+
Corroboration: the same finding is already written up at `docs/audit/audit-agents.md:186-249` ("the manager's
|
|
183
|
+
token check is **skipped entirely** when no token is supplied … Minimal hardening: in `invokeHostTool` change the
|
|
184
|
+
token check to `if (input.token !== row.token)`") — and the code is **unchanged**, i.e. unfixed.
|
|
185
|
+
|
|
186
|
+
### 1.2 `bindings/index.post.ts` — permission check quoted; **target agent ownership NOT verified**
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
// server/api/workshop/agent-tools/bindings/index.post.ts:16-39
|
|
190
|
+
export default defineApiHandler(async (event) => {
|
|
191
|
+
const user = resolveUser(event)
|
|
192
|
+
const body = await readBody<{ agentId?: string, nodeId?: string, kind?: 'dcw' | 'daq', mode?: 'auto' | 'manual' }>(event) ?? {}
|
|
193
|
+
const kind = body.kind ?? 'dcw'
|
|
194
|
+
const nodeId = String(body.nodeId ?? '')
|
|
195
|
+
// 绑定前置:节点必须存在,且用户对其所属产线有足额权限
|
|
196
|
+
const lineId = kind === 'daq'
|
|
197
|
+
? getDaqController().byId(nodeId)?.lineId
|
|
198
|
+
: getDcwController().byId(nodeId)?.lineId
|
|
199
|
+
if (!lineId) {
|
|
200
|
+
throw new AppError(404, ErrorCodes.NOT_FOUND, `${kind === 'daq' ? '数采' : '写控'}节点不存在: ${nodeId}`)
|
|
201
|
+
}
|
|
202
|
+
const mode = lineMode(user, lineId)
|
|
203
|
+
const needOperate = kind === 'dcw'
|
|
204
|
+
if (mode === 'none' || (needOperate && mode !== 'operate')) {
|
|
205
|
+
throw new AppError(403, 'LINE_FORBIDDEN', `无该产线权限:绑定${kind === 'daq' ? '数采' : '写控'}节点需对产线「${lineId}」拥有${needOperate ? '可操控' : '仅查看'}及以上权限`)
|
|
206
|
+
}
|
|
207
|
+
const binding = getAgentNodeBindingRepo().bind(
|
|
208
|
+
String(body.agentId ?? ''),
|
|
209
|
+
nodeId,
|
|
210
|
+
kind,
|
|
211
|
+
body.mode ?? 'auto',
|
|
212
|
+
)
|
|
213
|
+
return { binding }
|
|
214
|
+
})
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The check at `:28-32` covers **the node's line only**. `body.agentId` is never resolved, never checked for
|
|
218
|
+
existence, never checked for ownership/membership:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
// server/services/workshop/agents/node-bindings.repo.ts:73-78
|
|
222
|
+
bind(agentId: string, nodeId: string, kind: AgentNodeBindingKind, mode: AgentNodeBindingMode): AgentNodeBinding {
|
|
223
|
+
if (!agentId) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, 'agentId 必填')
|
|
224
|
+
if (!nodeId) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, 'nodeId 必填')
|
|
225
|
+
if (kind !== 'dcw' && kind !== 'daq') throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `未知节点类型: ${kind}`)
|
|
226
|
+
if (mode !== 'auto' && mode !== 'manual') throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `未知控制模式: ${mode}`)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Effect: **any** authenticated user with `operate` on one line can grant that control right to **any** agent id —
|
|
230
|
+
their own, another user's agent, or a lead in an unrelated channel — or re-bind an existing agent with
|
|
231
|
+
`"mode":"auto"` to strip HITL, because `bind()` updates the mode of an existing row
|
|
232
|
+
(`node-bindings.repo.ts:79-83`: `if (prev) { prev.mode = mode; this.flush(); return prev }`).
|
|
233
|
+
The granting user need not own, belong to, or even know anything about that agent.
|
|
234
|
+
|
|
235
|
+
### 1.3 Handlers that authenticate but never authorize the resource (IDOR)
|
|
236
|
+
|
|
237
|
+
All of these call `resolveUser` and **discard the result** or use it only for logging — no ownership/role/line check:
|
|
238
|
+
|
|
239
|
+
| Handler | Auth | Missing check | Impact |
|
|
240
|
+
|---|---|---|---|
|
|
241
|
+
| `agent-tools/bindings/[id].patch.ts:10-13` | `resolveUser(event)` (`:10`) | owner / role / line | Any user flips **any** binding's mode: `getAgentNodeBindingRepo().setMode(id, body.mode ?? 'auto')` → `"auto"` **disables HITL** for another team's writes (`node-bindings.repo.ts:97-104`) |
|
|
242
|
+
| `agent-tools/bindings/[id].delete.ts:11-17` | `resolveUser(event)` (`:11`) | owner / role / line | Any user unbinds any agent↔node pair (`:17 !repo.unbind(id)`), also cancels that agent's pending approvals (`:19 getToolApprovals().cancelPendingFor(...)`) |
|
|
243
|
+
| `agent-tools/bindings/index.get.ts:10-13` | `resolveUser(event)` (`:10`) | any scope filter | Full cross-tenant binding dump (see 1.1) |
|
|
244
|
+
| `agent-tools/approvals/index.get.ts:11-16` | `resolveUser(event)` (`:11`) | ownership of `agentId` | Any user lists pending approvals of any agent: `getToolApprovals().listPending(agentId)`; history via `scope=history` |
|
|
245
|
+
| `agent-tools/list.get.ts:13-23` | user token path (`:13`) | `agentId` ownership | Tool-schema disclosure for any agent |
|
|
246
|
+
| `dcw/[id]/read.post.ts:13-16` | `resolveUser(event)` (`:13`) | **`requireLineMode` absent** | Any user triggers a real driver read on **any** dcw node id: `await getDcwController().readNow(id)` (`:16`) — cross-line data read |
|
|
247
|
+
| `dcw/[id].patch.ts:12-16` | `resolveUser(event)` (`:12`) | role / line | Any user patches **any** dcw node: name/driver/range/enabled/position — `getDcwController().patch(id, body)` (`:16`). Widening `min/max` weakens the write interlock that `dcw_control` depends on |
|
|
248
|
+
| `dcw/index.post.ts:13-16` | `resolveUser(event)` (`:13`) | role / line | Any user creates a write-control node (the comment at `:17` calls it "获得直写物理设备能力的入口") |
|
|
249
|
+
| `dcw/lines/[id].delete.ts:15-21` | `resolveUser(event)` (`:15`) | role / line | Any user deletes **any** line; `?purge=1` cascades nodes/products/recipes (`:19-21`). The sibling create is admin/editor-gated (`dcw/lines/index.post.ts:14`) |
|
|
250
|
+
| `dcw/lines/[id].patch.ts:11-14` | `resolveUser(event)` (`:11`) | role / line | Any user renames/recolors any line |
|
|
251
|
+
| `dcw/recipes/[id].delete.ts:10-12` | `resolveUser(event)` (`:10`) | role | Any user deletes any recipe |
|
|
252
|
+
| `dcw/recipes/index.post.ts:11-13` | `resolveUser(event)` (`:11`) | role | Any user creates recipes |
|
|
253
|
+
| `dcw/recipes/[id]/index.patch.ts:12` , `[id]/revert.post.ts:12` | `resolveUser` | role | Any user edits/rewrites any recipe version history |
|
|
254
|
+
| `dcw/products/[id].delete.ts:11-14`, `[id].patch.ts:12-16` | `resolveUser` | role | Any user deletes/edits any product (create requires admin/editor, `products/index.post.ts:12`) |
|
|
255
|
+
| `dcw/runs/[id]/close.post.ts:11-14` | `resolveUser(event)` (`:11`) | role | Any user closes any production run window |
|
|
256
|
+
| `dcw/templates/index.post.ts:11-13`, `[key].patch.ts:11-14`, `[key].delete.ts:10-12` | `resolveUser` | role | Any user mutates control templates (driver configs) |
|
|
257
|
+
| `dcw/test-driver.post.ts:12-15` | `resolveUser(event)` (`:12`) | role | Any user makes the server open outbound connections with a caller-supplied `driverConfig`: `getDcwController().testDriver(body.driver ?? 'mock', body.driverConfig ?? {})` |
|
|
258
|
+
| `daq/test-driver.post.ts:17-21` | `resolveUser(event)` (`:17`) | role | Same SSRF-ish connect primitive for DAQ protocols |
|
|
259
|
+
| `daq/[id]/test.post.ts:12-15` | `resolveUser(event)` (`:12`) | **`requireLineMode` absent** | Any user triggers a real connection test on any DAQ node (`:15 testNode(id)`); the dcw twin requires `operate` (`dcw/[id]/test.post.ts:16`) |
|
|
260
|
+
| `daq/[id].delete.ts:13-17` | `resolveUser(event)` (`:13`) | **`requireLineMode` absent** | Any user deletes any DAQ node; the patch twin requires `operate` (`daq/[id].patch.ts:19`) |
|
|
261
|
+
| `daq/infra/reconnect.post.ts:9-15` | `resolveUser(event)` (`:9`) | role | Any user triggers infra bring-up: `await apply(daqInfraConfig())` → `__daqApplyInfra` runs `docker compose -f … up -d …` (`server/services/workshop/daq/infra.ts:85`) |
|
|
262
|
+
| `daq/alarms.get.ts:11-15` | `resolveUser(event)` (`:11`) | line filter | Any user reads **all** alarms of **all** lines (`:15 listAlarms(scope, limit)`); `daq/index.get.ts:27` filters by line, this one does not |
|
|
263
|
+
| `daq/templates/*` (`index.post.ts:12`, `[key].patch.ts:12`, `[key].delete.ts:12`) | `resolveUser` | role | Any user mutates signal templates |
|
|
264
|
+
| `dcw/journal/index.get.ts:11-19` | `resolveUser(event)` (`:11`) | line scope | Any user reads the full parameter ledger for any `lineId`/`nodeId` supplied in the query |
|
|
265
|
+
| `dcw/[id]/param-ledger.get.ts:11-13` | `resolveUser(event)` (`:11`) | **`requireLineMode` absent** | Any user reads any node's setpoints/targets/last-good values |
|
|
266
|
+
| `dcw/optimizations/index.get.ts:11-21`, `[id].get.ts:11-16`, `[id]/series.get.ts:12-17` | `resolveUser` | line scope | Any user reads any line's optimization records + raw DAQ series |
|
|
267
|
+
| `dcw/recipes/index.get.ts:9-11` | `resolveUser(event)` (`:9`) | line scope | Returns `repo.list()`/`listRuns()` with **no** `visibleLineIds` filter (contrast `dcw/index.get.ts:29-30`, `dcw/runs/index.get.ts:12`) |
|
|
268
|
+
| `dcw/recipes/[id]/versions.get.ts:11-13` | `resolveUser` | line scope | Any user reads any recipe's full parameter version history |
|
|
269
|
+
| `server/api/workshop/runtime.get.ts:9-10` | `resolveUser(event)` (`:9`) | role | Any user enumerates wired harness agent ids + active channels |
|
|
270
|
+
| `server/api/workshop/daq/infra.get.ts:9` | `resolveUser` | role | Infra status disclosure (low) |
|
|
271
|
+
|
|
272
|
+
### 1.4 Handlers that ARE correctly guarded (control set)
|
|
273
|
+
|
|
274
|
+
* `agents/[id].get.ts:16-17` → `manager.requireTemplateReadable`; `agents/[id].patch.ts:28`, `[id].delete.ts:17` → `requireWritable` (`manager.ts:1083-1090`).
|
|
275
|
+
* `channels/[id].delete.ts:14-15`, `index.patch.ts:35-36`, `agents/index.post.ts:32-33`, `agents/[agentId].delete.ts:17-18`, `[agentId].patch.ts:18-19`, `model.patch.ts:20-21`, `position.patch.ts:19-20`, `range.patch.ts:19-20`, `stop.post.ts:18-19` → `getChannelForUser` + `requireWritable` + membership check (`manager.ts:1103-1110`, `:1067-1074`).
|
|
276
|
+
* `channels/[id]/messages/index.post.ts:38-39`, `tasks/index.post.ts:67-68`, `tasks/index.get.ts:22`, `queue.get.ts:17`, `events.get.ts:28`, `terminals.get.ts:19`, `messages/index.get.ts:21`, `channels/[id]/index.get.ts:13` → `getChannelForUser` (owner-or-legacy-public read).
|
|
277
|
+
* Memory planes: `channels/[id]/memories/**` and `channels/[id]/agents/[agentId]/memories/**` → `resolveAgentOrUser` + same-channel or `getChannelForUser`+`requireOwned`, and the manager re-checks role: `manager.ts:1508` (`仅本人或 lead 可策展 Agent 记忆`), `:1463` (`仅 lead 可写团队记忆`), `:1487`, `:1548`.
|
|
278
|
+
⚠ Note (by design, not a bug): `manager.searchAgentMemories` (`manager.ts:1441-1446`) only calls `requireMember(channelId, callerAgentId)` — **any teammate agent in the channel can search another agent's private memory** by passing that agent's id in the URL with `scope:'private'`.
|
|
279
|
+
* `agents/subscribe.post.ts:17-19` → `resolveCaller` + `manager.subscribe` → `requireMember` + same-channel target check (`manager.ts:2884-2893`).
|
|
280
|
+
* `tasks.get.ts:11-12` / `mailbox.get.ts:10-13` → `resolveCaller`, scope forced to `caller.channelId`/`caller.id`.
|
|
281
|
+
* Role-gated: `dcw/controller.post.ts:15`, `dcw/[id].delete.ts:14`, `dcw/lines/index.post.ts:14`, `dcw/journal/node/[nodeId]/rollback.post.ts:15`, `dcw/optimizations/[id]/judge.post.ts:15`, `[id]/rollback.post.ts:17`, `dcw/products/index.post.ts:12`, `dcw/recipes/[id]/apply.post.ts:15`, `[id]/mark-good.post.ts:13`, `[id]/rollback-good.post.ts:13`, `dcw/runs/[id]/rollback.post.ts:14`, `daq/controller.post.ts:29,49`, `daq/index.post.ts:16` (all `requireRole`/`gateDangerous`).
|
|
282
|
+
* `agent-tools/approvals/[id]/decide.post.ts:12-19` — `resolveUser` **used** (decider recorded + `audit`).
|
|
283
|
+
|
|
284
|
+
### 1.5 `requireLineMode` / `lineMode` audit for daq/dcw handlers
|
|
285
|
+
|
|
286
|
+
**Called (correct):**
|
|
287
|
+
|
|
288
|
+
| Handler | Line | Quote |
|
|
289
|
+
|---|---|---|
|
|
290
|
+
| `dcw/[id]/write.post.ts` | `:19` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
291
|
+
| `dcw/[id]/test.post.ts` | `:16` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
292
|
+
| `dcw/[id]/bind.post.ts` | `:16` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
293
|
+
| `dcw/[id]/bindings.put.ts` | `:17` | `requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')` |
|
|
294
|
+
| `dcw/lines/[id]/start.post.ts` | `:20` | `requireLineMode(user, id, 'operate')` |
|
|
295
|
+
| `dcw/lines/[id]/stop.post.ts` | `:20` | `requireLineMode(user, id, 'operate')` |
|
|
296
|
+
| `dcw/runs/[id]/data.get.ts` | `:18` | `requireLineMode(user, run?.lineId, 'readonly')` |
|
|
297
|
+
| `daq/[id].patch.ts` | `:19` | `requireLineMode(user, getDaqController().byId(id)?.lineId, 'operate')` |
|
|
298
|
+
| `daq/[id]/bind.post.ts` | `:18`, `daq/[id]/bindings.put.ts:18` | `requireLineMode(user, getDaqController().byId(id)?.lineId, 'operate')` |
|
|
299
|
+
| `daq/[id]/samples.get.ts` | `:16`, `daq/[id]/frames.get.ts:16`, `daq/[id]/frames/content.get.ts:17` | `requireLineMode(user, getDaqController().byId(id)?.lineId, 'readonly')` |
|
|
300
|
+
| `daq/alarms/[id]/ack.post.ts` | `:17` | `requireLineMode(user, alarmNodeId ? getDaqController().byId(alarmNodeId)?.lineId : undefined, 'operate')` |
|
|
301
|
+
| `dcw/index.get.ts` | `:18-36` | `visibleLineIds` + `filterByLine` (+ per-line `.filter`) |
|
|
302
|
+
| `dcw/lines/index.get.ts` | `:15-18`, `dcw/line/query.get.ts:18-21`, `dcw/runs/index.get.ts:12`, `daq/index.get.ts:27` | `visibleLineIds`/`filterByLine`; `dcw/line/query.get.ts:19-21` throws `403 LINE_FORBIDDEN` when `lineId` is outside the visible set |
|
|
303
|
+
| `agent-tools/bindings/index.post.ts` | `:28-32` | `lineMode(user, lineId)` (node line only — see 1.2) |
|
|
304
|
+
|
|
305
|
+
**NOT called, where a line-scoped resource is addressed** (all cited in §1.3 with their guards):
|
|
306
|
+
`dcw/[id]/read.post.ts` (no line check on a real driver read — worst of these),
|
|
307
|
+
`dcw/[id].patch.ts`, `dcw/[id]/param-ledger.get.ts`, `dcw/journal/index.get.ts`,
|
|
308
|
+
`dcw/optimizations/index.get.ts`, `dcw/optimizations/[id].get.ts`, `dcw/optimizations/[id]/series.get.ts`,
|
|
309
|
+
`dcw/recipes/index.get.ts`, `dcw/recipes/[id]/versions.get.ts`, `dcw/recipes/[id].delete.ts`,
|
|
310
|
+
`dcw/recipes/[id]/index.patch.ts`, `dcw/recipes/[id]/revert.post.ts`, `dcw/products/[id].patch.ts`,
|
|
311
|
+
`dcw/products/[id].delete.ts`, `dcw/runs/[id]/close.post.ts`, `dcw/lines/[id].patch.ts`,
|
|
312
|
+
`dcw/lines/[id].delete.ts`, `dcw/index.post.ts`, `dcw/templates/*`,
|
|
313
|
+
`daq/[id].delete.ts`, `daq/[id]/test.post.ts`, `daq/alarms.get.ts`, `daq/templates/*`,
|
|
314
|
+
`daq/test-driver.post.ts`, `dcw/test-driver.post.ts`.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 2. READ-PATH WRITES (`*.get.ts` that mutate)
|
|
319
|
+
|
|
320
|
+
| GET handler | Write / side effect | Quote |
|
|
321
|
+
|---|---|---|
|
|
322
|
+
| `server/api/workshop/daq/index.get.ts:22` | **DB insert + runtime creation + 250 ms timer**: `provisionLegacyTwins()` → `this.repo.insert(node)` | `ctrl.provisionLegacyTwins()` (`index.get.ts:22`); `daq-controller.ts:1142-1159`: `provisionLegacyTwins(): void {` … `this.repo.insert(node)` (`:1157`) … `this.syncRuntimes()` (`:1159`); `ensureLoop()` at `:1143` starts `this.timer = setInterval(() => this.sweep(), 250)` (`:191`) |
|
|
323
|
+
| `server/api/workshop/daq/index.get.ts:20` | Pipeline power-on: queue consumer + TSDB init | `await Promise.all([tsdbReady, getDaqQueue()])` (`:20`); comment `:18` `// ws.ts 出口装配 + 管线上电(幂等;路由模块加载即绑定)` |
|
|
324
|
+
| `server/api/workshop/daq/[id]/samples.get.ts:23` (via `:16` guard) | **Starts acquisition**: `samples()` → `ensureLoop()` | route `:23 const points = await getDaqController().samples(id, {...})`; `daq-controller.ts:793-795`: `async samples(id, opts) {` / `this.ensureLoop()` / `await tsdbReady` |
|
|
325
|
+
| `server/api/workshop/daq/[id]/frames.get.ts:20` | same | route `:20 const frames = await getDaqController().frames(id, {...})`; `daq-controller.ts:835 this.ensureLoop()` |
|
|
326
|
+
| `server/api/workshop/daq/[id]/frames/content.get.ts:23` | same | route `:23 const { data, mime } = await getDaqController().frameContent(...)`; `daq-controller.ts:854 this.ensureLoop()` |
|
|
327
|
+
| `server/api/workshop/channels/[id]/index.get.ts:16` | **Assembles lead runtime + starts SchedulerLoop (timer + loop resubmit callback)** | `ensureLeadSchedulerLoop(manager, channelId)` (`:16`), comment `:15` `// 兜底:详情请求时确保 lead 的调度循环已装配(如启动恢复遗漏)`; → `plugins/workshop.ts:73 manager.ensureChannelActive(channelId, options)` → `manager.ts:697-719` `const cr = this.ensureChannelRuntime(channelId)` … `cr.scheduler = loop` / `loop.start()` ⚠(whether `wireMember` immediately spawns a harness subprocess was not traced) |
|
|
328
|
+
| `server/api/workshop/dcw/index.get.ts:13`, `dcw/lines/index.get.ts:12`, `dcw/line/query.get.ts:15`, `dcw/runs/[id]/data.get.ts:14` | Global binding mutation: repoints the controller's broadcast sink each request | `bindDcwBroadcast(broadcastSceneEvent)` → `dcw-controller.ts` `setBroadcast(fn)`; `daq/index.get.ts:19`, `dcw/[id]/read.post.ts:14` etc. use `bindDaqHost(broadcastSceneEvent)` |
|
|
329
|
+
| `server/api/workshop/dcw/index.get.ts:32` | Over-fetch then JS trim (not a write, but a read amplification) | `ctrl.listHistory(400).filter(h => inLine(nodeLine(h.nodeId))).slice(0, 60)` |
|
|
330
|
+
|
|
331
|
+
**Purely read-only GETs (verified line by line):** `agent-tools/list.get.ts`, `agent-tools/approvals/index.get.ts`,
|
|
332
|
+
`agent-tools/bindings/index.get.ts`, `agents/index.get.ts`, `agents/[id].get.ts`,
|
|
333
|
+
`channels/[id]/events.get.ts`, `channels/[id]/queue.get.ts`, `channels/[id]/tasks/index.get.ts`,
|
|
334
|
+
`channels/[id]/terminals.get.ts` (`harness-terminal.ts:451-466` pure map over a Map),
|
|
335
|
+
`channels/[id]/messages/index.get.ts`, `channels/[id]/agents/index.get.ts`,
|
|
336
|
+
`channels/[id]/memories/index.get.ts`, `channels/[id]/agents/[agentId]/memories/index.get.ts`,
|
|
337
|
+
`channels/[id]/plugins/index.get.ts`, `dcw/index.get.ts` (only the binding call above),
|
|
338
|
+
`dcw/lines/index.get.ts`, `dcw/line/query.get.ts`, `dcw/recipes/index.get.ts`, `dcw/recipes/[id]/versions.get.ts`,
|
|
339
|
+
`dcw/runs/index.get.ts`, `dcw/runs/[id]/data.get.ts` (`dcw-controller.ts:964-999` queries only),
|
|
340
|
+
`dcw/journal/index.get.ts` (`recipe-rollback-manager.ts:652-654`), `dcw/[id]/param-ledger.get.ts` (`:627-649`),
|
|
341
|
+
`dcw/optimizations/index.get.ts` (`:656-658`), `dcw/optimizations/[id].get.ts`,
|
|
342
|
+
`dcw/optimizations/[id]/series.get.ts` (`:602-623` read-only), `daq/alarms.get.ts` (`daq-controller.ts:600-604`),
|
|
343
|
+
`daq/infra.get.ts`, `runtime.get.ts` (`manager.ts:803-808`), `tasks.get.ts`, `mailbox.get.ts`
|
|
344
|
+
(`manager.ts:2834-2840`; the query has no LIMIT — see §3).
|
|
345
|
+
|
|
346
|
+
Note the memory list GET `channels/[id]/agents/[agentId]/memories/index.get.ts:28` calls `manager.listMemories`
|
|
347
|
+
which is a pure read (`manager.ts:1421-1426`).
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 3. UNBOUNDED STATE + PERF
|
|
352
|
+
|
|
353
|
+
### 3.1 Module-level maps/sets/arrays in scope
|
|
354
|
+
|
|
355
|
+
1. **`server/api/workshop/caller.ts:122` — unbounded, no eviction.**
|
|
356
|
+
```ts
|
|
357
|
+
const nameCache = new Map<string, { name: string | null, at: number }>()
|
|
358
|
+
```
|
|
359
|
+
Writes (all writes in the file):
|
|
360
|
+
```ts
|
|
361
|
+
// caller.ts:124-134
|
|
362
|
+
const hit = nameCache.get(userId)
|
|
363
|
+
if (hit && Date.now() - hit.at < 60_000) return hit.name
|
|
364
|
+
...
|
|
365
|
+
nameCache.set(userId, { name, at: Date.now() })
|
|
366
|
+
```
|
|
367
|
+
The 60 s TTL is consulted only when the **same** key is read again; there is **no eviction found** (no sweep,
|
|
368
|
+
no size cap, no `delete`). Every distinct `ownerUserId` ever rendered by `withOwnerNames` stays for the process
|
|
369
|
+
lifetime. `withOwnerNames` (`caller.ts:105-119`) creates a per-call `Map` (`:106`) which is request-scoped (fine).
|
|
370
|
+
2. **`server/services/workshop/scene-events.ts:20-27` — unbounded, no cap.**
|
|
371
|
+
```ts
|
|
372
|
+
const g = globalThis as typeof globalThis & {
|
|
373
|
+
__sceneEventPeers?: Set<WsPeer>
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** 在线 peer 注册表(ws 路由 sub 成功时登记,断开/退订移除) */
|
|
377
|
+
function peers(): Set<WsPeer> {
|
|
378
|
+
return g.__sceneEventPeers ??= new Set()
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
Write: `scene-events.ts:50-52 export function registerScenePeer(peer: WsPeer): void { peers().add(peer) }`.
|
|
382
|
+
Full analysis in §5.
|
|
383
|
+
3. **`server/services/workshop/runtime/manager.ts:329` — in the invoke path, unbounded, no eviction.**
|
|
384
|
+
```ts
|
|
385
|
+
private readonly lastToolInvokeAt = new Map<string, number>()
|
|
386
|
+
```
|
|
387
|
+
Writes: `manager.ts:2757 this.lastToolInvokeAt.set(input.agentId, Date.now())` (every `invokeHostTool` call);
|
|
388
|
+
reads: `manager.ts:704 toolActivityOf: (agentId: string) => this.lastToolInvokeAt.get(agentId) ?? null`.
|
|
389
|
+
No `delete`/cap → one entry per distinct agentId ever invoked, forever (small, but unbounded and attacker-reachable
|
|
390
|
+
via §1.1, e.g. spraying `agentId` values that exist).
|
|
391
|
+
4. **`server/services/workshop/agents/tool-approvals.ts:37-43` — bounded.**
|
|
392
|
+
```ts
|
|
393
|
+
private pending = new Map<string, { approval: ToolApproval, resolve: ..., timer: NodeJS.Timeout }>()
|
|
394
|
+
...
|
|
395
|
+
private history: ToolApproval[] = []
|
|
396
|
+
```
|
|
397
|
+
Eviction exists: timeout deletes (`:75 this.pending.delete(approval.id)`), decide deletes (`:92`),
|
|
398
|
+
cancel deletes (`:117`), history capped (`:162 if (this.history.length > HISTORY_CAP) this.history.splice(HISTORY_CAP)`).
|
|
399
|
+
5. `scene-events.ts` per-peer visible-lines is stashed on the peer object (`:35`), no separate collection.
|
|
400
|
+
6. Request-scoped only (not leaks): `harnesses.get.ts:18`, `channels/[id]/plugins/index.put.ts:30`,
|
|
401
|
+
`channels.post.ts:51`, `daq/alarms/[id]/ack.post.ts` (no map), `ws.ts:188 peerBudget = new WeakMap(...)` (GC-safe).
|
|
402
|
+
`harness-terminal.ts` `sessions` Map has a sweep — `harness-terminal.ts:469-479 sweepTerminalSessions(...)`
|
|
403
|
+
(`sessions.delete(pid)`), invoked from `manager.ts:816`.
|
|
404
|
+
|
|
405
|
+
### 3.2 Full-table fetch + JS filtering
|
|
406
|
+
|
|
407
|
+
* `server/api/workshop/dcw/index.get.ts:29-36` — five full arrays filtered in JS:
|
|
408
|
+
`recipes: visible ? ctrl.listRecipes().filter(r => inLine(r.lineId)) : ctrl.listRecipes()`, likewise `runs`,
|
|
409
|
+
`products`, `lines`, `lineStates`.
|
|
410
|
+
* `server/api/workshop/dcw/index.get.ts:31-33` — **N+1 in JS**: `ctrl.listHistory(400).filter(h => inLine(nodeLine(h.nodeId))).slice(0, 60)`
|
|
411
|
+
where `nodeLine = (nodeId: string): string | undefined => ctrl.byId(nodeId)?.lineId` (`:22`) and
|
|
412
|
+
`byId` is a linear scan: `dcw-node.repo.ts:40-42 byId(id: string): DcwNode | undefined { return this.list.find(n => n.id === id) }`
|
|
413
|
+
→ up to 400 × nodeCount comparisons per request.
|
|
414
|
+
* `server/api/workshop/agent-tools/bindings/index.get.ts:13` — `getAgentNodeBindingRepo().all()` returns the whole JSON list (`node-bindings.repo.ts:48-50`).
|
|
415
|
+
* `server/api/workshop/dcw/recipes/index.get.ts:11` — `{ recipes: repo.list(), runs: repo.listRuns() }`, no filter/limit.
|
|
416
|
+
* `server/api/workshop/channels/[id]/tasks/index.get.ts:23` — `taskEngineOf(manager).list(channelId)` →
|
|
417
|
+
`task.repo.ts:67 SELECT ${COLS} FROM tasks WHERE channel_id = ? ORDER BY createdAt ASC, rowid ASC` — **no LIMIT**; every task row of the channel is returned.
|
|
418
|
+
* `server/api/workshop/mailbox.get.ts:12-13` — client `limit` (uncapped) then fetch-all-and-slice:
|
|
419
|
+
`manager.ts:2834-2839 pollMailbox(...) { this.requireMember(...); return this.deps.repos.messages.listPendingByChannelAgent(channelId, callerAgentId).slice(0, limit).map(rowToMessage) }` — the repo query at that call site passes no limit (`message.repo.ts:91-95` has a `LIMIT ?` variant, unused here).
|
|
420
|
+
* `manager.ts:1552 listByAgent(targetAgentId, 10_000)` (delete memory path) and
|
|
421
|
+
`manager.ts:1489 listByAgentChannel(channelId, TEAM_AGENT_ID, 1_000_000)` (delete team memory) — huge limits.
|
|
422
|
+
* Member lookups fetch the whole roster then search: `channels/[id]/agents/[agentId].patch.ts:20-21`,
|
|
423
|
+
`[agentId].delete.ts:19-20`, `[agentId]/stop.post.ts:20-21`, `model.patch.ts:22-23`, `position.patch.ts:21-22`,
|
|
424
|
+
`range.patch.ts:21-22` all do `const members = await manager.listChannelAgents(channelId)` + `.some(...)`/`.find(...)`.
|
|
425
|
+
* `dcw/line/query.get.ts:26` → `dcw-controller.ts:888 getDaqNodeRepo().all().filter(...)`, then one TSDB round trip.
|
|
426
|
+
* `dcw/runs/[id]/data.get.ts:19` → `dcw-controller.ts:979-986`: `getDaqNodeRepo().all().filter(...)` then
|
|
427
|
+
`await Promise.all(lineNodes.map(async (node) => ... getTsdb().query(node.id, ...)))` — one TSDB query **per node**
|
|
428
|
+
(documented as intentional at `:977`).
|
|
429
|
+
* `channels/[id]/agents/[agentId]/model.patch.ts:34` — `for (const a of repo.listAll())` full asset list per request.
|
|
430
|
+
* `agents/index.get.ts:12` → `withOwnerNames(templates)` does one user lookup per template, 60 s-cached, never evicted (`caller.ts:105-135`).
|
|
431
|
+
|
|
432
|
+
### 3.3 Limits
|
|
433
|
+
|
|
434
|
+
Huge / unbounded as passed from the route:
|
|
435
|
+
|
|
436
|
+
* `mailbox.get.ts:12-13` — no upper bound: `const limit = Number.parseInt(url.searchParams.get('limit') ?? '50', 10)` … `limit > 0 ? limit : 50`.
|
|
437
|
+
* `channels/[id]/tasks/index.get.ts:23` — no limit parameter at all.
|
|
438
|
+
* `dcw/index.get.ts:32` — `listHistory(400)` (then slices to 60).
|
|
439
|
+
* `dcw/line/query.get.ts:35`, `dcw/[id]/param-ledger.get.ts` (`limit: 30`/`20` internal), `dcw/[id]/samples`:
|
|
440
|
+
`limit: num(q.limit)` passed raw, clamped only downstream at the adapter:
|
|
441
|
+
`timescale.adapter.ts:109 const limit = Math.min(opts.limit ?? 500, 5000)` / `:157 Math.min(q.limit ?? 2000, 10_000)`;
|
|
442
|
+
`sqlite.adapter.ts:128`, `:182` identical.
|
|
443
|
+
* Properly capped: `audit.get.ts:15 Math.min(500, Math.max(1, Number(q.limit) || 100))`,
|
|
444
|
+
`dcw/journal/index.get.ts:18 Math.min(limit, 500)`, `dcw/optimizations/index.get.ts:20`, `events.get.ts:18 limit: z.coerce.number().int().min(1).max(1000)`,
|
|
445
|
+
`messages/index.get.ts:13 max(500)`, `daq/alarms.get.ts:14 Math.min(500, ...)`, `daq/[id]/frames` clamp at
|
|
446
|
+
`daq-controller.ts:849 Math.min(Math.max(opts.limit ?? 100, 1), 1000)`, `manager.listChannelMail` `Math.min(500, ...)` (`manager.ts:2874`).
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## 4. ERROR PROPAGATION
|
|
451
|
+
|
|
452
|
+
### 4.1 Swallowed errors that hide failures
|
|
453
|
+
|
|
454
|
+
* `server/api/workshop/caller.ts:127-132` — user-name resolution failure is invisible:
|
|
455
|
+
```ts
|
|
456
|
+
try {
|
|
457
|
+
name = userRepository.findById(userId)?.name ?? null
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
name = null
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
`withOwnerNames` then renders `ownerName: null` (`caller.ts:113-118`) with no log — a broken `users.sqlite`
|
|
464
|
+
looks like "no owner" in every list that uses it (`agents/index.get.ts:12`).
|
|
465
|
+
* `server/api/workshop/caller.ts:28-33` — `resolveCallerOrNull` swallows every error including 401:
|
|
466
|
+
```ts
|
|
467
|
+
try {
|
|
468
|
+
return resolveCaller(event)
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
return null
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
Consumed at `channels/[id]/agents/index.get.ts:20-23`, where a genuine failure of the user path is silently
|
|
475
|
+
downgraded to the agent-token path.
|
|
476
|
+
* `server/api/workshop/audit.get.ts:24-29` — corrupt audit detail is silently replaced by `{}`:
|
|
477
|
+
```ts
|
|
478
|
+
try {
|
|
479
|
+
detail = JSON.parse(String(r.detailJson ?? '{}'))
|
|
480
|
+
}
|
|
481
|
+
catch {
|
|
482
|
+
detail = {}
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
* `server/api/workshop/dcw/[id].delete.ts:18` — `const body = await readBody<{ approvalId?: string }>(event).catch(() => ({})) ?? {}` (deliberate, DELETE-with-empty-body).
|
|
486
|
+
* Sanctioned-but-opaque fire-and-forget in the memory write paths: `manager.ts:1478`,
|
|
487
|
+
`manager.ts:1515-1521`, `manager.ts:1540` — `void vectorizeMemory(...).catch(() => {})` and
|
|
488
|
+
`.then(...).catch(() => {})` (vectorization failures never surface).
|
|
489
|
+
* `server/services/workshop/permissions.ts:70` and `:76` — `}).catch(() => {})` on both broadcast paths
|
|
490
|
+
(`broadcastSceneEvent('permissions.changed')`, `emitPluginEvent('permissions:changed')`): a failed permission-change
|
|
491
|
+
broadcast means clients keep stale grants with no log.
|
|
492
|
+
* `server/services/workshop/scene-events.ts:74-79` — send failures delete the peer silently (see §5).
|
|
493
|
+
* `server/services/workshop/daq/daq-controller.ts:1189` — `.catch(() => {})` on queue start;
|
|
494
|
+
`dcw-controller.ts:836` — `.catch(() => {})` on `markLineOffline`.
|
|
495
|
+
|
|
496
|
+
### 4.2 Internal error messages returned to the client (info leak)
|
|
497
|
+
|
|
498
|
+
* `server/services/workshop/runtime/manager.ts:2785-2788` — raw internal `Error.message` is returned to the HTTP
|
|
499
|
+
caller as tool output (this is the endpoint of §1.1):
|
|
500
|
+
```ts
|
|
501
|
+
catch (err) {
|
|
502
|
+
const reason = err instanceof Error ? err.message : String(err)
|
|
503
|
+
return { text: `工具「${input.tool}」调用失败: ${reason}`, isError: true }
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
e.g. SQLite constraint text, filesystem paths from `saveJsonFileAtomic`, driver/Modbus errors.
|
|
507
|
+
* `server/api/workshop/ws.ts:739` — `message: error instanceof Error ? error.message : 'workshop 未初始化'` is sent
|
|
508
|
+
to the client in an `error` control frame.
|
|
509
|
+
* Generic 500 path is clean: `server/utils/response.ts:44-46` logs the error and returns
|
|
510
|
+
`{ code: 'INTERNAL_ERROR', message: '服务器内部错误' }` — no stack, no message.
|
|
511
|
+
|
|
512
|
+
### 4.3 Status-code mis-mapping (failures reported as 500)
|
|
513
|
+
|
|
514
|
+
`server/api/workshop/daq/[id]/samples.get.ts` / `frames.get.ts` / `frames/content.get.ts` call controller methods
|
|
515
|
+
that throw a **plain** `Error` carrying a non-standard `status` property:
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
// server/services/workshop/daq/daq-controller.ts:797
|
|
519
|
+
if (!node) throw Object.assign(new Error(`数采节点不存在: ${id}`), { status: 404 })
|
|
520
|
+
```
|
|
521
|
+
```ts
|
|
522
|
+
// server/services/workshop/daq/daq-controller.ts:857, :862
|
|
523
|
+
if (!node) throw Object.assign(new Error(`数采节点不存在: ${id}`), { status: 404 })
|
|
524
|
+
...
|
|
525
|
+
if (!row || row.kind !== 'image') throw Object.assign(new Error(`帧不存在: ${id}@${tsMs}`), { status: 404 })
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
`defineApiHandler` only honors `AppError` (`response.ts:30-33`) and `H3Error` (`:34-43`); a plain `Error` falls to
|
|
529
|
+
`console.error('[api] unhandled error:', error)` + HTTP 500 (`response.ts:44-46`). So "node not found" surfaces to
|
|
530
|
+
the UI as a 500, and the internal process is logged as an unhandled error.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## 5. SCENE EVENTS (`server/services/workshop/scene-events.ts`)
|
|
535
|
+
|
|
536
|
+
**Subscriber tracking** — a `globalThis`-backed `Set` of duck-typed peers:
|
|
537
|
+
|
|
538
|
+
```ts
|
|
539
|
+
// scene-events.ts:14-18
|
|
540
|
+
interface WsPeer {
|
|
541
|
+
send(data: string | Uint8Array): void
|
|
542
|
+
close(code?: number, reason?: string): void
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
```ts
|
|
546
|
+
// scene-events.ts:20-27
|
|
547
|
+
const g = globalThis as typeof globalThis & {
|
|
548
|
+
__sceneEventPeers?: Set<WsPeer>
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** 在线 peer 注册表(ws 路由 sub 成功时登记,断开/退订移除) */
|
|
552
|
+
function peers(): Set<WsPeer> {
|
|
553
|
+
return g.__sceneEventPeers ??= new Set()
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Subscribe path** (only after user auth; a per-peer line-visibility set is stamped on the peer object):
|
|
558
|
+
|
|
559
|
+
```ts
|
|
560
|
+
// server/api/workshop/ws.ts:174-177
|
|
561
|
+
function attachScenePeer(peer: WsPeer, user: { id: string, role: string }): void {
|
|
562
|
+
setPeerVisibleLines(peer, visibleLineIds(user))
|
|
563
|
+
registerScenePeer(peer)
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
```ts
|
|
567
|
+
// scene-events.ts:34-36
|
|
568
|
+
export function setPeerVisibleLines(peer: WsPeer, visible: Set<string> | null): void {
|
|
569
|
+
(peer as WsPeer & { __awVisibleLines?: Set<string> | null }).__awVisibleLines = visible
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
```ts
|
|
573
|
+
// scene-events.ts:50-52
|
|
574
|
+
export function registerScenePeer(peer: WsPeer): void {
|
|
575
|
+
peers().add(peer)
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
Call sites: `ws.ts:176` only (grep: `registerScenePeer` appears in `scene-events.ts:50` and `ws.ts:176`).
|
|
579
|
+
|
|
580
|
+
**Unsubscribe path — exists, on socket close and error:**
|
|
581
|
+
|
|
582
|
+
```ts
|
|
583
|
+
// server/api/workshop/ws.ts:753-762
|
|
584
|
+
close(peer) {
|
|
585
|
+
unsubscribePeer(peer)
|
|
586
|
+
unregisterScenePeer(peer)
|
|
587
|
+
},
|
|
588
|
+
|
|
589
|
+
error(peer, error) {
|
|
590
|
+
console.error('[workshop-ws] connection error:', error)
|
|
591
|
+
unsubscribePeer(peer)
|
|
592
|
+
unregisterScenePeer(peer)
|
|
593
|
+
},
|
|
594
|
+
```
|
|
595
|
+
```ts
|
|
596
|
+
// scene-events.ts:54-56
|
|
597
|
+
export function unregisterScenePeer(peer: WsPeer): void {
|
|
598
|
+
peers().delete(peer)
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**Max subscriber count: NONE.** There is no cap anywhere — grep for `__sceneEventPeers` yields only `scene-events.ts:21,26`;
|
|
603
|
+
`registerScenePeer` (`:50-52`) performs an unconditional `add` with no size check, and `sceneEventPeerCount()`
|
|
604
|
+
(`:58-60`) is exported but never called by any handler (grep: only its own declaration).
|
|
605
|
+
|
|
606
|
+
**Dead-subscriber removal (two mechanisms, both partial):**
|
|
607
|
+
|
|
608
|
+
1. Close/error hooks above.
|
|
609
|
+
2. Lazy removal when a send throws:
|
|
610
|
+
```ts
|
|
611
|
+
// scene-events.ts:72-80
|
|
612
|
+
for (const peer of peers()) {
|
|
613
|
+
if (lineId !== undefined && !peerSeesLine(peer, lineId)) continue
|
|
614
|
+
try {
|
|
615
|
+
peer.send(frame)
|
|
616
|
+
}
|
|
617
|
+
catch {
|
|
618
|
+
peers().delete(peer)
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
same pattern in `broadcastPeerEvent` (`:92-99`).
|
|
623
|
+
|
|
624
|
+
**Can a subscriber leak? Yes — with the caveat that the primary paths are covered.**
|
|
625
|
+
* The removal depends entirely on the ws route's `close`/`error` hooks firing (`ws.ts:753-762`). There is **no
|
|
626
|
+
heartbeat/liveness timeout and no size cap** in the scene-peer set itself: a peer whose `send()` never throws
|
|
627
|
+
(e.g. half-open TCP where the write lands in the kernel buffer) and whose close hook never fires stays registered
|
|
628
|
+
for the process lifetime. The slow-consumer guard at `ws.ts:199-206` (`peer.close(1013, 'slow consumer')` +
|
|
629
|
+
`stream.peers.delete(peer)`) exists but only bounds *stream* fan-out, and relies on that same close hook to reach
|
|
630
|
+
`unregisterScenePeer`.
|
|
631
|
+
* The registry lives on `globalThis` (`scene-events.ts:20-22`) while peers are created per module instance, so any
|
|
632
|
+
module re-evaluation (dev HMR / nitro re-import) keeps the old peer objects in the same Set until their close hook
|
|
633
|
+
fires; nothing prunes stale entries.
|
|
634
|
+
* Broadcast cost is unbounded per frame: `broadcastSceneEvent` (`:66-81`) serializes once (`:71`) then loops all
|
|
635
|
+
peers and `send`s synchronously — with N pages open, every scene event costs N sends (by design per the comment at
|
|
636
|
+
`:62-65`), so a subscriber-count leak degrades CPU linearly with no ceiling.
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
## Priority summary
|
|
641
|
+
|
|
642
|
+
1. **P0** `agent-tools/invoke.post.ts:18+25` + `manager.ts:2750` — any user token + arbitrary `agentId` ⇒ act as any
|
|
643
|
+
agent (PLC writes, recipe writes, model promotion, arbitrary training Python). Unfixed; previously documented at
|
|
644
|
+
`docs/audit/audit-agents.md:186-249`.
|
|
645
|
+
2. **P0** `bindings/[id].patch.ts` — any user can flip any binding to `mode:'auto'`, silently removing HITL from
|
|
646
|
+
another team's control path.
|
|
647
|
+
3. **P1** `dcw/[id]/read.post.ts`, `dcw/[id].patch.ts`, `dcw/index.post.ts`, `dcw/lines/[id].delete.ts`,
|
|
648
|
+
`daq/[id].delete.ts`, `daq/[id]/test.post.ts`, `daq/alarms.get.ts`, `dcw/journal/*`, `dcw/optimizations/*`,
|
|
649
|
+
`dcw/recipes/index.get.ts`, `dcw/recipes/[id]/versions.get.ts`, `dcw/[id]/param-ledger.get.ts` — authenticated
|
|
650
|
+
but unscoped access to industrial data/control.
|
|
651
|
+
4. **P1** `bindings/index.post.ts:33-38` — binds any `agentId` without resolving or authorizing it.
|
|
652
|
+
5. **P2** GET handlers that start acquisition/pipelines and insert rows (`daq/index.get.ts:20-22`,
|
|
653
|
+
`daq/[id]/samples.get.ts`→`daq-controller.ts:794`, `channels/[id]/index.get.ts:16`).
|
|
654
|
+
6. **P2** `daq/test-driver.post.ts` / `dcw/test-driver.post.ts` — caller-controlled outbound connect primitive.
|
|
655
|
+
7. **P3** `caller.ts:122` uncapped `nameCache`; `manager.ts:329` uncapped `lastToolInvokeAt`; no scene-peer cap.
|
|
656
|
+
8. **P3** `daq` 404s surface as 500 (`daq-controller.ts:797,838,857,862` vs `response.ts:29-47`).
|
|
657
|
+
9. **P3** swaggered error text: `manager.ts:2787` returns raw `err.message` to callers.
|