agentworkshop 0.7.23 → 0.7.27
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/nitro.json +1 -1
- package/.output/public/_nuxt/0jKrzLuM.js +19 -0
- package/.output/public/_nuxt/2jaJHWIJ.js +1 -0
- package/.output/public/_nuxt/{Cor9siSf.js → 6PZPfFcO.js} +1 -1
- package/.output/public/_nuxt/B7gZokg82.js +209 -0
- package/.output/public/_nuxt/B8kt898d.js +1 -0
- package/.output/public/_nuxt/{B-xskWta.js → BCJFMBQ6.js} +1 -1
- package/.output/public/_nuxt/BFGpEo8S.js +1 -0
- package/.output/public/_nuxt/BG4zduPf.js +14 -0
- package/.output/public/_nuxt/{DNxVTfhe.js → BGhOrgle.js} +1 -1
- package/.output/public/_nuxt/{n3vMFfrv.js → BHcpzI2n.js} +3 -3
- package/.output/public/_nuxt/{CEHmSufQ.js → BTLEvDxM.js} +1 -1
- package/.output/public/_nuxt/{CYBq3kS7.js → BWyR3awA.js} +1 -1
- package/.output/public/_nuxt/Bc89zKq2.js +1 -0
- package/.output/public/_nuxt/BetmGSO4.js +1 -0
- package/.output/public/_nuxt/{DAR70Lf5.js → BgCrZKv_2.js} +1 -1
- package/.output/public/_nuxt/BowTbasd.js +1 -0
- package/.output/public/_nuxt/BvtknqSC2.js +1 -0
- package/.output/public/_nuxt/CJviSvm-.js +1 -0
- package/.output/public/_nuxt/CTfqWGdy.js +1 -0
- package/.output/public/_nuxt/Cb3SnTM8.js +1 -0
- package/.output/public/_nuxt/Chc5pfAA.js +1 -0
- package/.output/public/_nuxt/CmHzfVc5.js +3 -0
- package/.output/public/_nuxt/Cn3J3NXR.js +23 -0
- package/.output/public/_nuxt/CobSUzQv.js +2 -0
- package/.output/public/_nuxt/{kZV5TQvh2.js → CucID8qg2.js} +1 -1
- package/.output/public/_nuxt/{CfVAr2nf.js → Cz9gD2Yo.js} +1 -1
- package/.output/public/_nuxt/{E88a-JCG.js → DGAOugLt.js} +1 -1
- package/.output/public/_nuxt/DHbTyoAV.js +1 -0
- package/.output/public/_nuxt/{BxtpDtzC.js → DNQF_TXX.js} +1 -1
- package/.output/public/_nuxt/DfLUN8kY.js +1 -0
- package/.output/public/_nuxt/DjU9Dx5Y.js +1 -0
- package/.output/public/_nuxt/DnziMlcX.js +1 -0
- package/.output/public/_nuxt/{BBwMH_hG.js → DoAlYsiF.js} +1 -1
- package/.output/public/_nuxt/{DA4lxCWC2.js → DosOJEmZ2.js} +2 -2
- package/.output/public/_nuxt/{DWcEk1ur2.js → DvgKXPgu2.js} +1 -1
- package/.output/public/_nuxt/{ed5qX-x5.js → Q_SJDSgx.js} +1 -1
- package/.output/public/_nuxt/{TownView.zu2rU5nS.css → TownView.CtskuMXS.css} +1 -1
- package/.output/public/_nuxt/XY2z78rj.js +1 -0
- package/.output/public/_nuxt/_id_.DdbYeLqn.css +1 -0
- package/.output/public/_nuxt/bFnZa2yd.js +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/f7a8e33d-5422-45ac-895d-27168f97e3ad.json +1 -0
- package/.output/public/_nuxt/daq.DGV9QS8d.css +1 -0
- package/.output/public/_nuxt/entry.BN02X8iR.css +1 -0
- package/.output/public/_nuxt/monitor.B--1dPzs.css +1 -0
- package/.output/public/_nuxt/permissions.B1pjGChZ.css +1 -0
- package/.output/public/_nuxt/plugins.DpCuwaJt.css +1 -0
- package/.output/public/_nuxt/rkVgzg62.js +1 -0
- package/.output/public/_nuxt/settings.W6qrYAui.css +1 -0
- package/.output/public/_nuxt/teams.BBXVkyKq.css +1 -0
- package/.output/public/_nuxt/tokens.xRb1fSPU.css +1 -0
- package/.output/public/_nuxt/users.DuAt_Zwf.css +1 -0
- package/.output/public/_nuxt/workshop.BJg84pOK.css +1 -0
- package/.output/public/assets/game/character/yangyang-walk.glb +0 -0
- package/.output/server/chunks/_/host-bindings.mjs +1 -1
- package/.output/server/chunks/_/nitro.mjs +1073 -925
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{OmpTerminalPanel-Ban3yJZ4.mjs → OmpTerminalPanel-Cw_Vw9oO.mjs} +3 -3
- package/.output/server/chunks/build/{OmpTerminalPanel-Ban3yJZ4.mjs.map → OmpTerminalPanel-Cw_Vw9oO.mjs.map} +1 -1
- package/.output/server/chunks/build/{TownView-DxeQe1Bl.mjs → TownView-ftIylqiD.mjs} +178 -151
- package/.output/server/chunks/build/{TownView-DxeQe1Bl.mjs.map → TownView-ftIylqiD.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-DQDqj-cZ.mjs → _id_-3Axz2-gV.mjs} +35 -35
- package/.output/server/chunks/build/{_id_-DQDqj-cZ.mjs.map → _id_-3Axz2-gV.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-DY6v6sqB.mjs → _id_-CahfkmEt.mjs} +5 -5
- package/.output/server/chunks/build/{_id_-DY6v6sqB.mjs.map → _id_-CahfkmEt.mjs.map} +1 -1
- package/.output/server/chunks/build/{_wsId_-C2O5zZsI.mjs → _wsId_-BymlQhzS.mjs} +13 -15
- package/.output/server/chunks/build/{_wsId_-C2O5zZsI.mjs.map → _wsId_-BymlQhzS.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents--6bzGtES.mjs → agents-DuMiSWKp.mjs} +7 -9
- package/.output/server/chunks/build/agents-DuMiSWKp.mjs.map +1 -0
- package/.output/server/chunks/build/button-DEcZFcqN.mjs +1 -1
- package/.output/server/chunks/build/{card-CCPQoKlh.mjs → card-Dipvaykh.mjs} +4 -4
- package/.output/server/chunks/build/{card-CCPQoKlh.mjs.map → card-Dipvaykh.mjs.map} +1 -1
- package/.output/server/chunks/build/{channel-templates-C1Bb-U75.mjs → channel-templates-BStqJ-of.mjs} +5 -7
- package/.output/server/chunks/build/channel-templates-BStqJ-of.mjs.map +1 -0
- package/.output/server/chunks/build/{checkbox-Dv1kjaUZ.mjs → checkbox-C1gABOca.mjs} +5 -5
- package/.output/server/chunks/build/{checkbox-Dv1kjaUZ.mjs.map → checkbox-C1gABOca.mjs.map} +1 -1
- package/.output/server/chunks/build/{daq-B-3xCp_2.mjs → daq-ChYCABz1.mjs} +312 -426
- package/.output/server/chunks/build/daq-ChYCABz1.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-BL2dhuz7.mjs → dcw-CcQNGggh.mjs} +3 -3
- package/.output/server/chunks/build/{dcw-BL2dhuz7.mjs.map → dcw-CcQNGggh.mjs.map} +1 -1
- package/.output/server/chunks/build/{default-DdV642BY.mjs → default-95-zOTPt.mjs} +3 -4
- package/.output/server/chunks/build/{default-DdV642BY.mjs.map → default-95-zOTPt.mjs.map} +1 -1
- package/.output/server/chunks/build/dropdown-vhn1NPFs.mjs +1 -1
- package/.output/server/chunks/build/{input-number-CZ3ZrCsy.mjs → input-number-Bd55elNN.mjs} +3 -3
- package/.output/server/chunks/build/{input-number-CZ3ZrCsy.mjs.map → input-number-Bd55elNN.mjs.map} +1 -1
- package/.output/server/chunks/build/{input-DxDqeyQW.mjs → input-sf_BT6o6.mjs} +553 -5
- package/.output/server/chunks/build/input-sf_BT6o6.mjs.map +1 -0
- package/.output/server/chunks/build/{logs-Dto_umFY.mjs → logs-D-2UyGtE.mjs} +3 -3
- package/.output/server/chunks/build/{logs-Dto_umFY.mjs.map → logs-D-2UyGtE.mjs.map} +1 -1
- package/.output/server/chunks/build/menu-WCrlpYVh.mjs +2 -2
- package/.output/server/chunks/build/modal-yvUquPj5.mjs +2 -2
- package/.output/server/chunks/build/{monitor-miqq9xHB.mjs → monitor-mfG5jBtz.mjs} +70 -192
- package/.output/server/chunks/build/monitor-mfG5jBtz.mjs.map +1 -0
- package/.output/server/chunks/build/{pages-D9tOoJsf.mjs → pages-Dfosfchr.mjs} +5 -6
- package/.output/server/chunks/build/pages-Dfosfchr.mjs.map +1 -0
- package/.output/server/chunks/build/{permissions-CxEgXic0.mjs → permissions-B-hhZuzm.mjs} +288 -194
- package/.output/server/chunks/build/permissions-B-hhZuzm.mjs.map +1 -0
- package/.output/server/chunks/build/{plugins-wLIKHcIE.mjs → plugins-DZR0T4Mh.mjs} +17 -17
- package/.output/server/chunks/build/{plugins-wLIKHcIE.mjs.map → plugins-DZR0T4Mh.mjs.map} +1 -1
- package/.output/server/chunks/build/popconfirm-CRaHcz5O.mjs +1 -1
- package/.output/server/chunks/build/{segmented-ChmQZwz6.mjs → segmented-LPPREMtZ.mjs} +336 -5
- package/.output/server/chunks/build/{segmented-ChmQZwz6.mjs.map → segmented-LPPREMtZ.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-BPBll_7K.mjs → settings-BajN9TQW.mjs} +254 -27
- package/.output/server/chunks/build/settings-BajN9TQW.mjs.map +1 -0
- package/.output/server/chunks/build/space-CCBYAkXt.mjs +1 -1
- package/.output/server/chunks/build/switch-CelRXpcy.mjs +1 -1
- package/.output/server/chunks/build/{table-BtksyDlQ.mjs → table-Bf1r2JjE.mjs} +5 -5
- package/.output/server/chunks/build/{table-BtksyDlQ.mjs.map → table-Bf1r2JjE.mjs.map} +1 -1
- package/.output/server/chunks/build/{tabs-DQv-_-R6.mjs → tabs-BM3tAh_J.mjs} +2 -2
- package/.output/server/chunks/build/{tabs-DQv-_-R6.mjs.map → tabs-BM3tAh_J.mjs.map} +1 -1
- package/.output/server/chunks/build/{tag-A0tZ2Ouq.mjs → tag-NO9jEqKy.mjs} +3 -3
- package/.output/server/chunks/build/{tag-A0tZ2Ouq.mjs.map → tag-NO9jEqKy.mjs.map} +1 -1
- package/.output/server/chunks/build/{teams-DD8ZXTKL.mjs → teams-t2SkRlJP.mjs} +349 -51
- package/.output/server/chunks/build/teams-t2SkRlJP.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-Dxnuw96_.mjs → tokens-DiCojbN8.mjs} +296 -200
- package/.output/server/chunks/build/tokens-DiCojbN8.mjs.map +1 -0
- package/.output/server/chunks/build/{town-Dsh0jlwZ.mjs → town-C7QM8DYK.mjs} +5 -5
- package/.output/server/chunks/build/{town-Dsh0jlwZ.mjs.map → town-C7QM8DYK.mjs.map} +1 -1
- package/.output/server/chunks/build/{useDaqStream-DkRL3tEW.mjs → useDaqStream-BeA2X8KR.mjs} +26 -11
- package/.output/server/chunks/build/useDaqStream-BeA2X8KR.mjs.map +1 -0
- package/.output/server/chunks/build/{useDcwStream-DyB2ufQI.mjs → useDcwStream-BJW_MitY.mjs} +14 -6
- package/.output/server/chunks/build/useDcwStream-BJW_MitY.mjs.map +1 -0
- package/.output/server/chunks/build/{useDeviceTwins-ZbT86L9O.mjs → useDeviceTwins-Dwz_EY_i.mjs} +2 -2
- package/.output/server/chunks/build/{useDeviceTwins-ZbT86L9O.mjs.map → useDeviceTwins-Dwz_EY_i.mjs.map} +1 -1
- package/.output/server/chunks/build/{useOpsLog-8HCYFb6n.mjs → useOpsLog-Dm8QEb1I.mjs} +2 -2
- package/.output/server/chunks/build/{useOpsLog-8HCYFb6n.mjs.map → useOpsLog-Dm8QEb1I.mjs.map} +1 -1
- package/.output/server/chunks/build/{useWorkshopApi-D2gq06N2.mjs → useWorkshopApi-B-L3MmIX.mjs} +9 -2
- package/.output/server/chunks/build/{useWorkshopApi-D2gq06N2.mjs.map → useWorkshopApi-B-L3MmIX.mjs.map} +1 -1
- package/.output/server/chunks/build/{users-D6RNylrW.mjs → users-B9SAdEtH.mjs} +12 -13
- package/.output/server/chunks/build/users-B9SAdEtH.mjs.map +1 -0
- package/.output/server/chunks/build/{workshop-CTcYDq2i.mjs → workshop-81xhz368.mjs} +21 -22
- package/.output/server/chunks/build/workshop-81xhz368.mjs.map +1 -0
- package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs +14 -2
- package/.output/server/chunks/routes/api/plugins/_name/_...path_.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.put.mjs +40 -0
- package/.output/server/chunks/routes/api/workshop/channels/_id/index.put.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/channels/_id/index4.get.mjs +21 -9
- package/.output/server/chunks/routes/api/workshop/channels/_id/index4.get.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channels/_id/index5.get.mjs +33 -0
- package/.output/server/chunks/routes/api/workshop/channels/_id/index5.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/channels/_id/terminals.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +15 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/bindings.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames/content.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/frames.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/test.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/alarms/_id/ack.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/controller.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/mark-good.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/device-twins/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index3.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index4.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index4.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index5.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index6.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +3 -3
- package/.output/server/chunks/virtual/entry.mjs +381 -438
- package/.output/server/chunks/virtual/entry.mjs.map +1 -1
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/app/app.vue +7 -0
- package/app/assets/css/main.css +12 -1
- package/app/components/workshop/DaqNodeRow.vue +398 -0
- package/app/components/workshop/town/ModelPreview3D.vue +26 -4
- package/app/components/workshop/town/TownScene3D.ts +60 -12
- package/app/components/workshop/town/TownView.vue +4 -3
- package/app/composables/workshop/useCharacterAssets.ts +1 -0
- package/app/composables/workshop/useDaqStream.ts +24 -5
- package/app/composables/workshop/useDcwStream.ts +13 -4
- package/app/composables/workshop/useWorkshopApi.ts +294 -259
- package/app/pages/daq/[id].vue +27 -3
- package/app/pages/daq/index.vue +8 -355
- package/app/pages/monitor.vue +56 -68
- package/app/pages/permissions/index.vue +86 -82
- package/app/pages/plugins/index.vue +12 -7
- package/app/pages/settings.vue +253 -2
- package/app/pages/tokens.vue +94 -96
- package/app/pages/users.vue +10 -18
- package/app/pages/workshop/index.vue +34 -5
- package/app/pages/workshop/teams.vue +212 -2
- package/i18n/dicts/_en.json +361 -1
- package/i18n/dicts/plugins.json +10 -0
- package/i18n/dicts/settings.json +1 -0
- package/i18n/dicts/teamPlugins.json +10 -0
- package/i18n/locales/en.ts +1861 -1901
- package/i18n/locales/zh-CN.ts +1789 -1830
- package/package.json +149 -148
- package/public/assets/game/character/yangyang-walk.glb +0 -0
- package/scripts/_dbg-cpu-profile.mjs +53 -0
- package/scripts/_dbg-logs-ui-e2e.py +3 -2
- package/scripts/_dbg-render-perf.mjs +142 -0
- package/scripts/_dbg-render-regression.mjs +130 -0
- package/scripts/_dbg-ui-survey.py +52 -0
- package/scripts/i18n-merge.mjs +152 -30
- package/scripts/production-closed-loop-e2e.mjs +254 -0
- package/scripts/team-plugins-api-smoke.mjs +149 -0
- package/scripts/three-system-e2e.mjs +448 -0
- package/server/api/plugins/[name]/[...path].ts +46 -31
- package/server/api/workshop/channels/[id]/plugins/index.get.ts +35 -0
- package/server/api/workshop/channels/[id]/plugins/index.put.ts +39 -0
- package/server/api/workshop/channels.post.ts +17 -0
- package/server/data/dcw-lines.json +14 -0
- package/server/data/dcw-products.json +14 -0
- package/server/data/dcw-recipes.json +23 -1
- package/server/data/dcw-rollback.json +585 -10
- package/server/data/dcw-runs.json +27 -27
- package/server/data/dcw-writes.json +96 -0
- package/server/data/dcws.json +394 -366
- package/server/data/scene-layouts.json +66 -0
- package/server/plugins/aw-plugins.ts +35 -27
- package/server/plugins-builtin/diag-bridge/index.mjs +556 -0
- package/server/plugins-builtin/rag-bridge/index.mjs +364 -0
- package/server/services/workshop/agents/host-tool-bridge.ts +659 -642
- package/server/services/workshop/agents/omp-agent.ts +1178 -1178
- package/server/services/workshop/agents/plugin-tools.ts +10 -1
- package/server/services/workshop/db/channel-plugins.repo.ts +65 -0
- package/server/services/workshop/plugins/host.mjs +52 -4
- package/server/services/workshop/runtime/manager.ts +2976 -2968
- package/shared/config/schema.json +4 -0
- package/.output/public/_nuxt/BJbhwS5o.js +0 -1
- package/.output/public/_nuxt/BUcmySDa2.js +0 -209
- package/.output/public/_nuxt/BWC4HQHQ.js +0 -1
- package/.output/public/_nuxt/BbZAzXjZ2.js +0 -1
- package/.output/public/_nuxt/BgOhoDTI.js +0 -1
- package/.output/public/_nuxt/Bo8t5fkJ.js +0 -1
- package/.output/public/_nuxt/BuiGDfMB.js +0 -1
- package/.output/public/_nuxt/BxPPAkGA.js +0 -3
- package/.output/public/_nuxt/C53drffV.js +0 -1
- package/.output/public/_nuxt/CcwJ2mrD2.js +0 -1
- package/.output/public/_nuxt/Cr9DkD1d.js +0 -1
- package/.output/public/_nuxt/DTYBfjRY.js +0 -1
- package/.output/public/_nuxt/DVoWWlUW.js +0 -1
- package/.output/public/_nuxt/Dc32T41C.js +0 -1
- package/.output/public/_nuxt/Dhqra7NB.js +0 -1
- package/.output/public/_nuxt/DlRO_q5L.js +0 -1
- package/.output/public/_nuxt/DsUUYP292.js +0 -1
- package/.output/public/_nuxt/Dsdy4Ua-.js +0 -1
- package/.output/public/_nuxt/DtzZqC0z.js +0 -1
- package/.output/public/_nuxt/Ko9mMsJj.js +0 -14
- package/.output/public/_nuxt/RzXYI9FM.js +0 -23
- package/.output/public/_nuxt/UtviM1bg.js +0 -1
- package/.output/public/_nuxt/_id_.C-YXjn8Q.css +0 -1
- package/.output/public/_nuxt/bAFueD6a.js +0 -1
- package/.output/public/_nuxt/builds/meta/1adc96e0-8bbd-4a79-ac7e-b6952556fef8.json +0 -1
- package/.output/public/_nuxt/daq.3OKj58HH.css +0 -1
- package/.output/public/_nuxt/dbi5bHkY.js +0 -2
- package/.output/public/_nuxt/eHYnGew0.js +0 -19
- package/.output/public/_nuxt/entry.OR_psZHp.css +0 -1
- package/.output/public/_nuxt/jyQyulh-.js +0 -1
- package/.output/public/_nuxt/monitor.CmwpEUCI.css +0 -1
- package/.output/public/_nuxt/permissions.DTRtZeTw.css +0 -1
- package/.output/public/_nuxt/plugins.BcSEwwGb.css +0 -1
- package/.output/public/_nuxt/settings.BqMF2nfj.css +0 -1
- package/.output/public/_nuxt/teams.DN0pAGGu.css +0 -1
- package/.output/public/_nuxt/tokens.BVXU1jOK.css +0 -1
- package/.output/public/_nuxt/users.D8lcLE62.css +0 -1
- package/.output/public/_nuxt/workshop.CbymC2LY.css +0 -1
- package/.output/public/_nuxt/zVkqCZ78.js +0 -1
- package/.output/server/chunks/build/Col-biaNnEPf.mjs +0 -338
- package/.output/server/chunks/build/Col-biaNnEPf.mjs.map +0 -1
- package/.output/server/chunks/build/agents--6bzGtES.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-C1Bb-U75.mjs.map +0 -1
- package/.output/server/chunks/build/daq-B-3xCp_2.mjs.map +0 -1
- package/.output/server/chunks/build/hasIn-vQ9LWt-J.mjs +0 -554
- package/.output/server/chunks/build/hasIn-vQ9LWt-J.mjs.map +0 -1
- package/.output/server/chunks/build/input-DxDqeyQW.mjs.map +0 -1
- package/.output/server/chunks/build/monitor-miqq9xHB.mjs.map +0 -1
- package/.output/server/chunks/build/pages-D9tOoJsf.mjs.map +0 -1
- package/.output/server/chunks/build/permissions-CxEgXic0.mjs.map +0 -1
- package/.output/server/chunks/build/settings-BPBll_7K.mjs.map +0 -1
- package/.output/server/chunks/build/teams-DD8ZXTKL.mjs.map +0 -1
- package/.output/server/chunks/build/tokens-Dxnuw96_.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqStream-DkRL3tEW.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwStream-DyB2ufQI.mjs.map +0 -1
- package/.output/server/chunks/build/useSiteConfig-DJe_KMVr.mjs +0 -27
- package/.output/server/chunks/build/useSiteConfig-DJe_KMVr.mjs.map +0 -1
- package/.output/server/chunks/build/users-D6RNylrW.mjs.map +0 -1
- package/.output/server/chunks/build/workshop-CTcYDq2i.mjs.map +0 -1
- /package/.output/public/_nuxt/{CNs_Ozdc.js → CNs_Ozdc2.js} +0 -0
- /package/.output/public/_nuxt/{Cno6kYiO.js → Cno6kYiO2.js} +0 -0
- /package/.output/public/_nuxt/{ORt9oDIy2.js → ORt9oDIy.js} +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 团队级插件开关 + 内置插件 + 配置化 API — 轻量 HTTP 冒烟(AgentWorkShop 3001)。
|
|
3
|
+
* 覆盖:内置识别 / 建队带 plugins / 团队插件 GET+PUT / 工具清单团队过滤 /
|
|
4
|
+
* dispatch 拒绝 / 系统配置 plugins 组读写与热更新 / 插件健康检测。
|
|
5
|
+
* 无真实诊断、无 LLM 会话消耗。
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from 'node:fs'
|
|
8
|
+
import { DatabaseSync } from 'node:sqlite'
|
|
9
|
+
|
|
10
|
+
const BASE = process.env.AW_BASE ?? 'http://localhost:3001'
|
|
11
|
+
const saved = JSON.parse(readFileSync(new URL('../.AgentWorkShop/data/.three-system-e2e-token.json', import.meta.url), 'utf8'))
|
|
12
|
+
const T = process.env.AW_E2E_TOKEN ?? saved.token
|
|
13
|
+
|
|
14
|
+
// admin 夹具:settings API 需要 admin/editor;临时把 e2e 夹具用户全升为 editor,结束还原
|
|
15
|
+
const db = new DatabaseSync(new URL('../.AgentWorkShop/data/users.sqlite', import.meta.url))
|
|
16
|
+
db.exec('PRAGMA busy_timeout=4000')
|
|
17
|
+
const fixtureIds = (db.prepare('SELECT id FROM users WHERE name = \'three-system-demo\' OR name LIKE \'e2e3sys-%\'').all() ?? [])
|
|
18
|
+
.map(r => r.id)
|
|
19
|
+
const upgraded = []
|
|
20
|
+
for (const id of fixtureIds) {
|
|
21
|
+
const role = db.prepare('SELECT role FROM users WHERE id = ?').get(id)?.role
|
|
22
|
+
if (role && role !== 'admin') {
|
|
23
|
+
upgraded.push({ id, role })
|
|
24
|
+
db.prepare('UPDATE users SET role = ? WHERE id = ?').run('admin', id)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let pass = 0
|
|
29
|
+
let fail = 0
|
|
30
|
+
const ok = (cond, label, detail = '') => {
|
|
31
|
+
if (cond) {
|
|
32
|
+
pass++
|
|
33
|
+
console.log(` ✔ ${label}${detail ? ` —— ${detail}` : ''}`)
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
fail++
|
|
37
|
+
console.error(` ✘ ${label}${detail ? ` —— ${detail}` : ''}`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
41
|
+
async function api(method, path, { body, token, timeoutMs = 20000 } = {}) {
|
|
42
|
+
const headers = {}
|
|
43
|
+
if (body !== undefined) headers['content-type'] = 'application/json'
|
|
44
|
+
if (token) headers.authorization = `Bearer ${token}`
|
|
45
|
+
const res = await fetch(`${BASE}${path}`, { method, headers, body: body !== undefined ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(timeoutMs) })
|
|
46
|
+
return { status: res.status, json: await res.json().catch(() => null) }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log('\n══ 团队插件开关 + 内置插件 + 配置化 API 冒烟 ══')
|
|
50
|
+
// 1) 内置识别
|
|
51
|
+
const plugs = await api('GET', '/api/workshop/plugins', { token: T })
|
|
52
|
+
const list = plugs.json?.plugins ?? plugs.json?.data?.plugins ?? []
|
|
53
|
+
const bridges = list.filter(p => ['rag-bridge', 'diag-bridge'].includes(p?.name))
|
|
54
|
+
ok(list.length > 0, '插件清单可读', `count=${list.length}`)
|
|
55
|
+
ok(bridges.length === 2 && bridges.every(p => p.builtin && p.enabled && p.scope === 'builtin'), '两桥接插件以内置作用域识别且启用', bridges.map(p => `${p.name}(${p.scope})`).join(','))
|
|
56
|
+
|
|
57
|
+
// 2) 健康检测
|
|
58
|
+
const ragH = await api('GET', '/api/plugins/rag-bridge/health', { token: T })
|
|
59
|
+
ok(ragH.status === 200 && ragH.json?.backend?.ok === true && ragH.json?.web?.ok === true, 'rag-bridge 健康检测(backend+web)', `kb=${ragH.json?.kb?.name}`)
|
|
60
|
+
const dgH = await api('GET', '/api/plugins/diag-bridge/health', { token: T })
|
|
61
|
+
ok(dgH.status === 200 && dgH.json?.remote?.status === 'ok', 'diag-bridge 健康检测(3210 可达)', `activeRuns=${dgH.json?.remote?.activeRuns}`)
|
|
62
|
+
|
|
63
|
+
// 3) 系统配置 plugins 组(读 + 写 + 热更新)
|
|
64
|
+
const cfg = await api('GET', '/api/system/settings', { token: T })
|
|
65
|
+
const eff = cfg.json?.data?.effective ?? cfg.json?.effective ?? {}
|
|
66
|
+
ok(eff['plugins.kb.base_url'] === 'http://127.0.0.1:8770' && eff['plugins.diag.base_url'] === 'http://127.0.0.1:3210', '系统配置 plugins 组可读(schema 驱动)', `kb=${eff['plugins.kb.base_url']} diag=${eff['plugins.diag.base_url']}`)
|
|
67
|
+
const patch = await api('PATCH', '/api/system/settings', { token: T, body: { override: { 'plugins.kb.web_url': 'http://127.0.0.1:6789' } } })
|
|
68
|
+
const pRes = patch.json?.data ?? patch.json ?? {}
|
|
69
|
+
ok(pRes.ok === true, 'plugins 配置项可写(热生效)', `changed=${(pRes.changed ?? []).join(',') || '-'}`)
|
|
70
|
+
|
|
71
|
+
// 4) 建队带 plugins(默认全启用)与关闭插件团队
|
|
72
|
+
const nameA = `冒烟-全启用-${Date.now().toString(36)}`
|
|
73
|
+
const cA = await api('POST', '/api/workshop/channels', {
|
|
74
|
+
token: T,
|
|
75
|
+
body: { name: nameA, leadAgent: { name: '冒烟组长', harness: 'mock' } },
|
|
76
|
+
})
|
|
77
|
+
const chA = cA.json?.data?.channelId
|
|
78
|
+
ok(Boolean(chA), '建队(未传 plugins → 默认全启用)')
|
|
79
|
+
const nameB = `冒烟-关闭KB-${Date.now().toString(36)}`
|
|
80
|
+
const cB = await api('POST', '/api/workshop/channels', {
|
|
81
|
+
token: T,
|
|
82
|
+
body: {
|
|
83
|
+
name: nameB,
|
|
84
|
+
leadAgent: { name: '冒烟组长B', harness: 'mock' },
|
|
85
|
+
plugins: [{ name: 'rag-bridge', enabled: false }, { name: 'diag-bridge', enabled: true }],
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
const chB = cB.json?.data?.channelId
|
|
89
|
+
ok(Boolean(chB), '建队带 plugins 勾选(rag-bridge 关闭)')
|
|
90
|
+
|
|
91
|
+
// 5) 团队插件视图
|
|
92
|
+
const viewA = await api('GET', `/api/workshop/channels/${chA}/plugins`, { token: T })
|
|
93
|
+
ok(viewA.json?.data?.source === 'default' && viewA.json?.data?.plugins?.every(p => p.enabled), 'A 团队视图:source=default 全启用', `count=${viewA.json?.data?.plugins?.length}`)
|
|
94
|
+
const viewB = await api('GET', `/api/workshop/channels/${chB}/plugins`, { token: T })
|
|
95
|
+
const kbView = (viewB.json?.data?.plugins ?? []).find(p => p.name === 'rag-bridge')
|
|
96
|
+
const dgView = (viewB.json?.data?.plugins ?? []).find(p => p.name === 'diag-bridge')
|
|
97
|
+
ok(viewB.json?.data?.source === 'explicit' && kbView?.enabled === false && dgView?.enabled === true, 'B 团队视图:显式配置(rag 关/diag 开)', `source=${viewB.json?.data?.source}`)
|
|
98
|
+
|
|
99
|
+
// 工具直调用 omp 执行器(mock lead 无 dispatch 面);建队后补执行器成员
|
|
100
|
+
const mkExec = async (ch, name) => {
|
|
101
|
+
const members = (await api('GET', `/api/workshop/channels/${ch}/agents`, { token: T })).json?.data ?? []
|
|
102
|
+
if (!members.some(m => m?.name === name)) {
|
|
103
|
+
await api('POST', `/api/workshop/channels/${ch}/agents`, { token: T, body: { name, harness: 'omp', role: 'worker' } })
|
|
104
|
+
}
|
|
105
|
+
return ((await api('GET', `/api/workshop/channels/${ch}/agents`, { token: T })).json?.data ?? []).find(m => m?.name === name)
|
|
106
|
+
}
|
|
107
|
+
const execA = await mkExec(chA, '冒烟执行器A')
|
|
108
|
+
const execB = await mkExec(chB, '冒烟执行器B')
|
|
109
|
+
|
|
110
|
+
// 6) 工具清单团队过滤(A 团队执行器含 kb_*;B 团队执行器无 kb_*)
|
|
111
|
+
const toolsA = (await api('GET', `/api/workshop/agent-tools/list?agentId=${execA.id}`, { token: T })).json?.data?.tools ?? []
|
|
112
|
+
const toolsB = (await api('GET', `/api/workshop/agent-tools/list?agentId=${execB.id}`, { token: T })).json?.data?.tools ?? []
|
|
113
|
+
ok(toolsA.some(t => t.name === 'kb_search') && toolsA.some(t => t.name === 'diag_run'), 'A 团队工具清单含 kb_*/diag_*(插件工具注入)', `total=${toolsA.length}`)
|
|
114
|
+
ok(!toolsB.some(t => t.name === 'kb_search') && !toolsB.some(t => t.name === 'kb_store'), 'B 团队工具清单无 kb_*(关闭插件不注入,防上下文污染)', `total=${toolsB.length}`)
|
|
115
|
+
ok(toolsB.some(t => t.name === 'diag_run'), 'B 团队 diag_* 仍在(diag-bridge 开启)')
|
|
116
|
+
|
|
117
|
+
// 7) dispatch 拒绝:B 团队调 kb_search → isError
|
|
118
|
+
const invB = await api('POST', '/api/workshop/agent-tools/invoke', {
|
|
119
|
+
token: T,
|
|
120
|
+
body: { agentId: execB.id, tool: 'kb_search', args: { query: '冒烟' } },
|
|
121
|
+
})
|
|
122
|
+
const rB = invB.json?.data?.result
|
|
123
|
+
ok(rB?.isError === true && String(rB?.text).includes('未启用插件'), 'B 团队调用被拒插件工具 → isError + 引导开启', String(rB?.text).slice(0, 70))
|
|
124
|
+
// A 团队正常
|
|
125
|
+
const invA = await api('POST', '/api/workshop/agent-tools/invoke', {
|
|
126
|
+
token: T, timeoutMs: 60000,
|
|
127
|
+
body: { agentId: execA.id, tool: 'kb_search', args: { query: '冒烟验证' } },
|
|
128
|
+
})
|
|
129
|
+
const rA = invA.json?.data?.result
|
|
130
|
+
ok(rA && rA.isError !== true, 'A 团队调用插件工具正常(工具桥链路通)', String(rA?.text ?? '').slice(0, 60))
|
|
131
|
+
|
|
132
|
+
// 8) 团队设置切换:PUT 开回 rag-bridge → 工具回来
|
|
133
|
+
const put = await api('PUT', `/api/workshop/channels/${chB}/plugins`, {
|
|
134
|
+
token: T,
|
|
135
|
+
body: { plugins: [{ name: 'rag-bridge', enabled: true }, { name: 'diag-bridge', enabled: true }] },
|
|
136
|
+
})
|
|
137
|
+
ok(put.status === 200 && put.json?.data?.saved === 2, '团队设置 PUT 切换开关', `saved=${put.json?.data?.saved}`)
|
|
138
|
+
await sleep(1000)
|
|
139
|
+
const toolsB2 = (await api('GET', `/api/workshop/agent-tools/list?agentId=${execB.id}`, { token: T })).json?.data?.tools ?? []
|
|
140
|
+
ok(toolsB2.some(t => t.name === 'kb_search'), '开回后 kb_search 回到 B 团队工具清单(热生效)')
|
|
141
|
+
|
|
142
|
+
// 9) 清理:还原角色、删冒烟频道(删除级联 channel_plugins)
|
|
143
|
+
for (const u of upgraded) db.prepare('UPDATE users SET role = ? WHERE id = ?').run(u.role, u.id)
|
|
144
|
+
db.close()
|
|
145
|
+
for (const ch of [chA, chB]) {
|
|
146
|
+
await api('DELETE', `/api/workshop/channels/${ch}`, { token: T }).catch(() => null)
|
|
147
|
+
}
|
|
148
|
+
console.log(`\n══ 冒烟结果:${pass} 通过 / ${fail} 失败 ══`)
|
|
149
|
+
process.exit(fail > 0 ? 1 : 0)
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 三系统集成 e2e — AgentWorkShop × industrial-deep-diagnostic × rag-knowledge
|
|
3
|
+
*
|
|
4
|
+
* 覆盖(对应 .omc/plans/2026-09-07-three-system-integration.md 验收标准):
|
|
5
|
+
* Stage 1 系统与插件健康:四服务存活特征 / 插件装载 / 插件路由鉴权门(auth:'user')
|
|
6
|
+
* Stage 2 工具链与团队演示:DAQ 实时→快照 CSV / kb_index+kb_search 回环 / kb_store /
|
|
7
|
+
* 双团队(数据分析组+闭环控制组)/ 跨通道消息 / DCW(manual)→HITL→写回
|
|
8
|
+
* Stage 3(--full)真实诊断:diag_run(omp 引擎)→ 轮询至完成 → 报告自动入 KB → 检索命中
|
|
9
|
+
* Stage 4 插件启停:plugins-state.json 热重载 → 工具与路由同时消失/恢复
|
|
10
|
+
*
|
|
11
|
+
* 用法:node scripts/three-system-e2e.mjs [--full]
|
|
12
|
+
* 鉴权:沿用 api-live-e2e 惯例(env AW_E2E_TOKEN 复用,否则注册临时用户);
|
|
13
|
+
* 产线权限由脚本直接向 users.sqlite 种一行 user_line_grants(测试夹具,非平台面)。
|
|
14
|
+
*/
|
|
15
|
+
import { DatabaseSync } from 'node:sqlite'
|
|
16
|
+
import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs'
|
|
17
|
+
import { fileURLToPath } from 'node:url'
|
|
18
|
+
import { dirname, join, resolve } from 'node:path'
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
21
|
+
const ROOT = resolve(__dirname, '..')
|
|
22
|
+
const FULL = process.argv.includes('--full')
|
|
23
|
+
|
|
24
|
+
const BASE = process.env.AW_BASE ?? 'http://localhost:3021'
|
|
25
|
+
const KB = process.env.KB_BASE ?? 'http://127.0.0.1:8770'
|
|
26
|
+
const KBWEB = process.env.KB_WEB ?? 'http://127.0.0.1:6789'
|
|
27
|
+
const DIAG = process.env.DIAG_BASE ?? 'http://127.0.0.1:3210'
|
|
28
|
+
const LINE = process.env.E2E_LINE ?? 'ln-af002514' // 1号产线(6 个 mock 数采节点)
|
|
29
|
+
const DCW_NODE = process.env.E2E_DCW_NODE ?? 'dw-e92bb0e7' // 压力设定器(mock 驱动)
|
|
30
|
+
const CH_A = '产线数据分析组'
|
|
31
|
+
const CH_B = '闭环控制组'
|
|
32
|
+
|
|
33
|
+
let pass = 0
|
|
34
|
+
let fail = 0
|
|
35
|
+
const failures = []
|
|
36
|
+
function ok(cond, label, detail = '') {
|
|
37
|
+
if (cond) {
|
|
38
|
+
pass++
|
|
39
|
+
console.log(` ✔ ${label}${detail ? ` —— ${detail}` : ''}`)
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
fail++
|
|
43
|
+
failures.push(label)
|
|
44
|
+
console.error(` ✘ ${label}${detail ? ` —— ${detail}` : ''}`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
48
|
+
|
|
49
|
+
async function raw(method, url, { body, token, agent, timeoutMs = 20000 } = {}) {
|
|
50
|
+
const headers = {}
|
|
51
|
+
if (body !== undefined) headers['content-type'] = 'application/json'
|
|
52
|
+
if (token) headers.authorization = `Bearer ${token}`
|
|
53
|
+
if (agent) headers['x-aw-agent-token'] = agent.token
|
|
54
|
+
const res = await fetch(url, {
|
|
55
|
+
method, headers,
|
|
56
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
57
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
58
|
+
})
|
|
59
|
+
const json = await res.json().catch(() => null)
|
|
60
|
+
return { status: res.status, json }
|
|
61
|
+
}
|
|
62
|
+
const api = (method, path, opts = {}) => raw(method, `${BASE}${path}`, opts)
|
|
63
|
+
const envelopeData = r => r?.json?.data
|
|
64
|
+
const resultText = r => String(r?.json?.data?.result?.text ?? '')
|
|
65
|
+
const invoke = (agent, tool, args, timeoutMs = 60000) => api('POST', '/api/workshop/agent-tools/invoke', {
|
|
66
|
+
agent, timeoutMs, body: { agentId: agent.id, tool, args },
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// ── 0. 用户 token(持久化演示账号 → env → 现场注册)────────────────────────
|
|
70
|
+
console.log('\n══ 三系统集成 e2e ══')
|
|
71
|
+
const TOKEN_FILE = join(ROOT, '.AgentWorkShop', 'data', '.three-system-e2e-token.json')
|
|
72
|
+
let userToken = ''
|
|
73
|
+
let userId = ''
|
|
74
|
+
try {
|
|
75
|
+
const saved = JSON.parse(readFileSync(TOKEN_FILE, 'utf8'))
|
|
76
|
+
userToken = saved.token ?? ''
|
|
77
|
+
userId = saved.uid ?? ''
|
|
78
|
+
}
|
|
79
|
+
catch { /* 首跑 */ }
|
|
80
|
+
if (!userToken && process.env.AW_E2E_TOKEN) userToken = process.env.AW_E2E_TOKEN
|
|
81
|
+
if (!userToken) {
|
|
82
|
+
const name = `e2e3sys-${Date.now().toString(36)}`
|
|
83
|
+
const reg = await api('POST', '/api/workshop/users/register', { body: { name } })
|
|
84
|
+
userToken = reg?.json?.data?.token ?? ''
|
|
85
|
+
userId = reg?.json?.data?.id ?? ''
|
|
86
|
+
try {
|
|
87
|
+
writeFileSync(TOKEN_FILE, JSON.stringify({ token: userToken, uid: userId, name }))
|
|
88
|
+
}
|
|
89
|
+
catch { /* 忽略 */ }
|
|
90
|
+
ok(Boolean(userToken), '注册 e2e 用户拿 token', userToken ? 'ok' : `resp=${JSON.stringify(reg.json).slice(0, 120)}`)
|
|
91
|
+
if (!userToken) process.exit(1)
|
|
92
|
+
}
|
|
93
|
+
else { console.log(' · 复用持久化演示账号 token') }
|
|
94
|
+
|
|
95
|
+
// ── 0b. 夹具种子:e2e 用户对目标产线 operate 授权(users.sqlite 直插,测试专用)──
|
|
96
|
+
try {
|
|
97
|
+
const dbPath = join(ROOT, '.AgentWorkShop', 'data', 'users.sqlite')
|
|
98
|
+
if (!existsSync(dbPath)) throw new Error(`users.sqlite 不存在: ${dbPath}`)
|
|
99
|
+
let uid = userId
|
|
100
|
+
if (!uid) {
|
|
101
|
+
const db = new DatabaseSync(dbPath)
|
|
102
|
+
db.exec('PRAGMA busy_timeout=4000')
|
|
103
|
+
const cand = db.prepare('SELECT id FROM users ORDER BY created_at DESC LIMIT 200').all()
|
|
104
|
+
.find(() => true) // 兜底:下面按 token 哈希反查不可行,直接用最新 e2e 用户
|
|
105
|
+
uid = cand?.id ?? ''
|
|
106
|
+
db.close()
|
|
107
|
+
}
|
|
108
|
+
if (uid) {
|
|
109
|
+
const db2 = new DatabaseSync(dbPath)
|
|
110
|
+
db2.exec('PRAGMA busy_timeout=4000')
|
|
111
|
+
db2.prepare(`INSERT OR REPLACE INTO user_line_grants (user_id, line_id, mode, granted_by, granted_at) VALUES (?, ?, 'operate', 'three-system-e2e', ?)`)
|
|
112
|
+
.run(uid, LINE, new Date().toISOString())
|
|
113
|
+
db2.close()
|
|
114
|
+
console.log(` · 已为用户 ${uid} 种入产线 ${LINE} operate 授权(测试夹具)`)
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.log(' ! 未能取得 user id,产线授权未种(相关断言可能失败)')
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.log(` ! 授权夹具异常(继续): ${err?.message ?? err}`)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ══ Stage 1:四服务健康 + 插件装载 + 鉴权门 ═══════════════════════════════
|
|
125
|
+
console.log('\n── Stage 1 系统与插件健康 ──')
|
|
126
|
+
{
|
|
127
|
+
const awsGate = await api('GET', '/api/workshop/plugins')
|
|
128
|
+
ok(awsGate.status === 401, 'AWS 3021 活着(无 token 401 信封)', `status=${awsGate.status}`)
|
|
129
|
+
|
|
130
|
+
const kbH = await raw('GET', `${KB}/api/v1/health`)
|
|
131
|
+
ok(kbH.json?.status === 'healthy', 'rag-knowledge 后端 8770 healthy', JSON.stringify(kbH.json))
|
|
132
|
+
const kbWeb = await raw('GET', `${KBWEB}/api/kb/catalog`)
|
|
133
|
+
ok(kbWeb.json?.success === true, 'rag-knowledge web 6789 catalog 可用', `count=${kbWeb.json?.count}`)
|
|
134
|
+
const dgH = await raw('GET', `${DIAG}/api/health`)
|
|
135
|
+
ok(dgH.json?.status === 'ok', '诊断服务 3210 healthy', `activeRuns=${dgH.json?.checks?.activeRuns}`)
|
|
136
|
+
const rgH = await raw('GET', 'http://127.0.0.1:8764/health', { timeoutMs: 5000 }).catch(e => ({ json: { status: String(e) } }))
|
|
137
|
+
ok(rgH.json?.status === 'healthy', '诊断 RAG 引擎 8764 healthy(降级容忍)', JSON.stringify(rgH.json ?? {}))
|
|
138
|
+
|
|
139
|
+
const plugs = await api('GET', '/api/workshop/plugins', { token: userToken })
|
|
140
|
+
// 兼容两种形状:顶层 {plugins:[...]}(插件清单端点未包信封)/ 信封 data.plugins
|
|
141
|
+
const list = plugs.json?.plugins ?? envelopeData(plugs)?.plugins ?? []
|
|
142
|
+
const names = list.map(p => p?.name)
|
|
143
|
+
const enabled = n => list.find(p => p?.name === n)?.enabled === true
|
|
144
|
+
ok(Array.isArray(list) && list.length > 0, '插件清单可读', list.length ? `count=${list.length}` : `resp=${JSON.stringify(plugs.json).slice(0, 160)}`)
|
|
145
|
+
ok(names.includes('rag-bridge'), 'rag-bridge 已装载')
|
|
146
|
+
ok(names.includes('diag-bridge'), 'diag-bridge 已装载')
|
|
147
|
+
ok(enabled('rag-bridge') && enabled('diag-bridge'), '两桥接插件均为启用态')
|
|
148
|
+
|
|
149
|
+
const noAuthRag = await raw('GET', `${BASE}/api/plugins/rag-bridge/health`)
|
|
150
|
+
const noAuthDiag = await raw('GET', `${BASE}/api/plugins/diag-bridge/health`)
|
|
151
|
+
ok(noAuthRag.status === 401 && noAuthDiag.status === 401, '插件路由鉴权门生效(auth:user → 无 token 401)', `rag=${noAuthRag.status} diag=${noAuthDiag.status}`)
|
|
152
|
+
|
|
153
|
+
const ragH = await raw('GET', `${BASE}/api/plugins/rag-bridge/health`, { token: userToken })
|
|
154
|
+
ok(ragH.status === 200 && ragH.json?.outbound === true && ragH.json?.kb?.id, 'rag-bridge 健康(kb.id 就绪)', `kb=${ragH.json?.kb?.id}`)
|
|
155
|
+
const diagH = await raw('GET', `${BASE}/api/plugins/diag-bridge/health`, { token: userToken })
|
|
156
|
+
ok(diagH.status === 200 && diagH.json?.remote?.status === 'ok', 'diag-bridge 健康(3210 可达)', `activeRuns=${diagH.json?.remote?.activeRuns}`)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ══ Stage 2:双团队 + 工具链 + 跨通道 + HITL ═══════════════════════════════
|
|
160
|
+
let leadA
|
|
161
|
+
let toolInvoker // 工具直调执行器(omp;mock lead 不实现 dispatch 面)
|
|
162
|
+
let leadB
|
|
163
|
+
let channelA
|
|
164
|
+
let channelB
|
|
165
|
+
{
|
|
166
|
+
console.log('\n── Stage 2 团队与工具链 ──')
|
|
167
|
+
async function ensureChannel(name, leadName, execName) {
|
|
168
|
+
const found = await api('GET', '/api/workshop/channels', { token: userToken })
|
|
169
|
+
const items = envelopeData(found) ?? []
|
|
170
|
+
const hit = (Array.isArray(items) ? items : []).find(c => c?.name === name)
|
|
171
|
+
let channelId = hit?.id ?? ''
|
|
172
|
+
if (!channelId) {
|
|
173
|
+
let created = await api('POST', '/api/workshop/channels', {
|
|
174
|
+
token: userToken,
|
|
175
|
+
body: { name, description: `三系统集成示例团队(e2e 创建)`, leadAgent: { name: leadName, harness: 'mock' } },
|
|
176
|
+
})
|
|
177
|
+
if (!(created.status === 200 && envelopeData(created)?.channelId)) {
|
|
178
|
+
// 同名频道被其他账号占用 → 加随机后缀重建(演示语义不变)
|
|
179
|
+
name = `${name}-${Math.random().toString(36).slice(2, 6)}`
|
|
180
|
+
created = await api('POST', '/api/workshop/channels', {
|
|
181
|
+
token: userToken,
|
|
182
|
+
body: { name, description: `三系统集成示例团队(e2e 创建)`, leadAgent: { name: leadName, harness: 'mock' } },
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
channelId = envelopeData(created)?.channelId ?? ''
|
|
186
|
+
}
|
|
187
|
+
const readMembers = async () => (envelopeData(await api('GET', `/api/workshop/channels/${channelId}/agents`, { token: userToken })) ?? [])
|
|
188
|
+
let members = await readMembers()
|
|
189
|
+
// 每队配一个 omp「执行器」成员:REST 直调工具经共享 host-tool-bridge 分发,零 LLM 会话
|
|
190
|
+
if (!members.some(m => m?.name === execName)) {
|
|
191
|
+
await api('POST', `/api/workshop/channels/${channelId}/agents`, {
|
|
192
|
+
token: userToken,
|
|
193
|
+
body: { name: execName, harness: 'omp', role: 'worker' },
|
|
194
|
+
})
|
|
195
|
+
members = await readMembers()
|
|
196
|
+
}
|
|
197
|
+
const lead = members.find(m => m?.role === 'lead')
|
|
198
|
+
const exec = members.find(m => m?.name === execName)
|
|
199
|
+
return { channelId, lead, exec, name }
|
|
200
|
+
}
|
|
201
|
+
const a = await ensureChannel(CH_A, '分析组长', '数据分析执行器')
|
|
202
|
+
const b = await ensureChannel(CH_B, '控制组长', '控制执行器')
|
|
203
|
+
channelA = a.channelId
|
|
204
|
+
channelB = b.channelId
|
|
205
|
+
leadA = a.lead
|
|
206
|
+
leadB = b.lead
|
|
207
|
+
const execA = a.exec
|
|
208
|
+
const execB = b.exec
|
|
209
|
+
const nameB = b.name ?? CH_B
|
|
210
|
+
ok(Boolean(channelA && leadA?.id && leadA?.token), `团队A「${a.name ?? CH_A}」就绪(lead=${leadA?.name})`)
|
|
211
|
+
ok(Boolean(channelB && leadB?.id && leadB?.token), `团队B「${nameB}」就绪(lead=${leadB?.name})`)
|
|
212
|
+
ok(Boolean(execA?.id && execA?.token), '团队A 执行器(omp)就绪', execA?.name)
|
|
213
|
+
ok(Boolean(execB?.id && execB?.token), '团队B 执行器(omp)就绪', execB?.name)
|
|
214
|
+
toolInvoker = execA ?? leadA
|
|
215
|
+
|
|
216
|
+
// 工具清单:5 工具可见 + 无 url/base_url/host 参数(SSRF 面)
|
|
217
|
+
const listR = await api('GET', `/api/workshop/agent-tools/list?agentId=${toolInvoker.id}`, { agent: toolInvoker })
|
|
218
|
+
const tools = listR?.json?.tools ?? listR?.json?.data?.tools ?? []
|
|
219
|
+
const toolNames = tools.map(t => t?.name)
|
|
220
|
+
for (const t of ['diag_run', 'diag_status', 'kb_search', 'kb_store', 'kb_index']) {
|
|
221
|
+
ok(toolNames.includes(t), `agent 工具清单含 ${t}`)
|
|
222
|
+
}
|
|
223
|
+
const bridge = tools.filter(t => ['diag_run', 'diag_status', 'kb_search', 'kb_store', 'kb_index'].includes(t?.name))
|
|
224
|
+
const leak = bridge.some(t => Object.keys(t?.parameters?.properties ?? {}).some(k => /url|host/i.test(k)))
|
|
225
|
+
ok(bridge.length === 5 && !leak, '桥接工具参数面无 url/host 注入口', `tools=${bridge.length}`)
|
|
226
|
+
|
|
227
|
+
// DAQ 实时数据 → 快照导出
|
|
228
|
+
let snap = await api('POST', '/api/plugins/diag-bridge/snapshot', {
|
|
229
|
+
token: userToken,
|
|
230
|
+
body: { line: LINE, from_ms: Date.now() - 15 * 60_000, to_ms: Date.now() },
|
|
231
|
+
})
|
|
232
|
+
if (snap.json?.success === false) {
|
|
233
|
+
snap = await api('POST', '/api/plugins/diag-bridge/snapshot', {
|
|
234
|
+
token: userToken,
|
|
235
|
+
body: { line: LINE, from_ms: Date.now() - 120 * 60_000, to_ms: Date.now() },
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
ok(snap.json?.success === true && snap.json?.rows > 0, 'DAQ 实时数采 → 快照 CSV 导出成功', `rows=${snap.json?.rows} nodes=${snap.json?.nodes} path=${snap.json?.csvPath}`)
|
|
239
|
+
if (snap.json?.csvPath) {
|
|
240
|
+
ok(String(snap.json.csvPath).startsWith('data/aw-snapshots/'), '快照落诊断服务 aw-snapshots 目录(相对路径)', snap.json.csvPath)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// KB 回环:kb_index 入库独特自然语句 → kb_search 命中(嵌入/分词对自然语句才可靠);kb_store 沉淀经验
|
|
244
|
+
const marker = `三系统验证密语${Date.now().toString(36)}:青花瓷泵站三十七号叶片于黄昏完成校准`
|
|
245
|
+
const idx = await invoke(toolInvoker, 'kb_index', {
|
|
246
|
+
title: `三系统集成验证 ${Date.now().toString(36)}`,
|
|
247
|
+
content: `# 三系统集成验证\n\n${marker}。\n\n本文由 three-system-e2e 经 rag-bridge 插件写入,验证 文档写盘 → 向量索引 → kb_search 检索 全链路。\n`,
|
|
248
|
+
tags: ['e2e'],
|
|
249
|
+
})
|
|
250
|
+
ok(resultText(idx).includes('已入库'), 'kb_index 文档入库成功', resultText(idx).slice(0, 80))
|
|
251
|
+
let hit = ''
|
|
252
|
+
for (let i = 0; i < 10; i++) { // 首次检索可能触发 BGE-M3 模型加载,放宽到 10×3s
|
|
253
|
+
await sleep(3000)
|
|
254
|
+
const s = await invoke(toolInvoker, 'kb_search', { query: marker })
|
|
255
|
+
hit = resultText(s)
|
|
256
|
+
if (hit.includes(marker)) break
|
|
257
|
+
}
|
|
258
|
+
ok(hit.includes(marker), 'kb_search 检索命中刚入库文档', hit.split('\n')[1]?.slice(0, 90) ?? hit.slice(0, 90))
|
|
259
|
+
|
|
260
|
+
const store = await invoke(toolInvoker, 'kb_store', {
|
|
261
|
+
title: `e2e 经验:${marker} 快照导出前先确认产线在采样`,
|
|
262
|
+
category: 'troubleshooting',
|
|
263
|
+
problem: 'diag_run 报「时窗内无数采样本」',
|
|
264
|
+
solution: '先查产线 LineRun 是否活动、节点 publishIntervalMs 是否在节拍上,再缩窗重试。',
|
|
265
|
+
key_lessons: ['空窗先查打标窗口', 'bucket 5s 可对齐多节点'],
|
|
266
|
+
tags: ['e2e', 'daq'],
|
|
267
|
+
})
|
|
268
|
+
ok(resultText(store).includes('已沉淀'), 'kb_store 经验沉淀成功', resultText(store).slice(0, 80))
|
|
269
|
+
|
|
270
|
+
// 跨通道:分析组长 → 控制组长(require_reply;用频道 ID 定向,避免跨账号同名频道歧义)
|
|
271
|
+
const xmsg = await invoke(leadA, 'send_cross_channel_message', {
|
|
272
|
+
to_channel_id: channelB,
|
|
273
|
+
message: `分析组已完成 ${LINE} 最近时窗的数据诊断与知识检索;请控制组给出 ${DCW_NODE}(压力设定器)的控制策略建议。`,
|
|
274
|
+
require_reply: true,
|
|
275
|
+
}, 120000)
|
|
276
|
+
ok(!xmsg.json?.result?.isError, '分析组长 → 控制组长 跨通道消息发送成功', resultText(xmsg).slice(0, 80))
|
|
277
|
+
let landedB = ''
|
|
278
|
+
for (let i = 0; i < 8; i++) {
|
|
279
|
+
await sleep(1000)
|
|
280
|
+
const msgs = await api('GET', `/api/workshop/channels/${channelB}/messages`, { token: userToken })
|
|
281
|
+
const items = envelopeData(msgs)?.items ?? envelopeData(msgs) ?? []
|
|
282
|
+
const arr = Array.isArray(items) ? items : (items?.messages ?? [])
|
|
283
|
+
landedB = JSON.stringify(arr)
|
|
284
|
+
if (landedB.includes('控制策略建议')) break
|
|
285
|
+
}
|
|
286
|
+
ok(landedB.includes('控制策略建议'), '跨通道消息落入控制组频道')
|
|
287
|
+
|
|
288
|
+
// DCW(manual)→ HITL → 写回
|
|
289
|
+
const dcwView = await api('GET', '/api/workshop/dcw', { token: userToken })
|
|
290
|
+
const dcwNodes = envelopeData(dcwView)?.nodes ?? []
|
|
291
|
+
const node = dcwNodes.find(n => n?.id === DCW_NODE)
|
|
292
|
+
ok(Boolean(node), `DCW 节点 ${DCW_NODE} 对授权用户可见`, `driver=${node?.driver} value=${node?.value}`)
|
|
293
|
+
const target = Number(node?.value ?? 0)
|
|
294
|
+
const bind = await api('POST', '/api/workshop/agent-tools/bindings', {
|
|
295
|
+
token: userToken,
|
|
296
|
+
body: { agentId: execB.id, nodeId: DCW_NODE, kind: 'dcw', mode: 'manual' },
|
|
297
|
+
})
|
|
298
|
+
ok(bind.status === 200 && Boolean(envelopeData(bind)?.binding?.id), '控制执行器绑定 DCW 节点(manual)', bind.status === 200 ? `mode=${envelopeData(bind)?.binding?.mode}` : JSON.stringify(bind.json).slice(0, 120))
|
|
299
|
+
ok(envelopeData(bind)?.binding?.mode === 'manual', '绑定模式确为 manual(审批路径生效前置)')
|
|
300
|
+
|
|
301
|
+
const pendingInvoke = invoke(execB, 'dcw_control', {
|
|
302
|
+
node_id: DCW_NODE,
|
|
303
|
+
value: target,
|
|
304
|
+
hypothesis: `依据分析组诊断结论与历史经验,维持设定值 ${target}(e2e 闭环演示)`,
|
|
305
|
+
}, 300000)
|
|
306
|
+
let approved = false
|
|
307
|
+
let pendRaw = '[]'
|
|
308
|
+
for (let i = 0; i < 15 && !approved; i++) {
|
|
309
|
+
await sleep(2000)
|
|
310
|
+
const pend = await api('GET', '/api/workshop/hitl/pending', { token: userToken })
|
|
311
|
+
const pd = envelopeData(pend)
|
|
312
|
+
const arr = Array.isArray(pd) ? pd : (pd?.items ?? pd?.pending ?? [])
|
|
313
|
+
pendRaw = JSON.stringify(arr)
|
|
314
|
+
const item = arr.find(x => x?.kind === 'dcw-approval' && x?.agentId === execB.id)
|
|
315
|
+
if (item?.id) {
|
|
316
|
+
const resp = await api('POST', '/api/workshop/hitl/respond', {
|
|
317
|
+
token: userToken,
|
|
318
|
+
body: { kind: 'dcw-approval', id: item.id, confirmed: true, comment: 'three-system-e2e 批准' },
|
|
319
|
+
})
|
|
320
|
+
approved = resp.status === 200
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const writeRes = await pendingInvoke
|
|
324
|
+
ok(approved, 'HITL 待办出现并经 respond 批准', approved ? 'ok' : `pending=${pendRaw.slice(0, 200)}`)
|
|
325
|
+
ok(resultText(writeRes).includes('下发成功'), 'dcw_control 经审批写回成功', resultText(writeRes).slice(0, 120))
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ══ Stage 3:真实诊断(kickoff 常跑;--full 才轮询到完成)═══════════════════
|
|
329
|
+
let runId = ''
|
|
330
|
+
{
|
|
331
|
+
console.log('\n── Stage 3 真实深度诊断(omp 引擎)──')
|
|
332
|
+
let r = null
|
|
333
|
+
for (const mins of [30, 180, 1440]) { // 采样间歇(共享实例节拍被并行调节)→ 逐步加宽窗口
|
|
334
|
+
r = await invoke(toolInvoker, 'diag_run', {
|
|
335
|
+
line: LINE,
|
|
336
|
+
from_ms: Date.now() - mins * 60_000,
|
|
337
|
+
to_ms: Date.now(),
|
|
338
|
+
question: `${LINE} 近期数采数据深度根因诊断(three-system-e2e,窗口 ${mins} 分钟)`,
|
|
339
|
+
scene: 'three_system_e2e',
|
|
340
|
+
}, 120000)
|
|
341
|
+
if (resultText(r).includes('runId=')) break
|
|
342
|
+
console.log(` … ${mins} 分钟窗无样本,加宽重试`)
|
|
343
|
+
await sleep(2000)
|
|
344
|
+
}
|
|
345
|
+
const text = resultText(r)
|
|
346
|
+
runId = (text.match(/runId=([a-z0-9-]+)/i) ?? [])[1] ?? ''
|
|
347
|
+
if (!runId) {
|
|
348
|
+
// 产线上已有在跑诊断(如事件自动触发)→ 收养它继续验证,而非报错
|
|
349
|
+
const adopted = (text.match(/run_id=([a-z0-9-]+)/i) ?? [])[1] ?? ''
|
|
350
|
+
if (adopted) {
|
|
351
|
+
runId = adopted
|
|
352
|
+
console.log(` · 产线已有在跑诊断,收养 run_id=${adopted} 继续验证`)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
ok(Boolean(runId), 'diag_run 异步发起(或收养在跑诊断)拿到 runId', runId || `resp=${JSON.stringify(r.json).slice(0, 260)}`)
|
|
356
|
+
|
|
357
|
+
if (FULL && runId) {
|
|
358
|
+
let done = false
|
|
359
|
+
let lastText = ''
|
|
360
|
+
const maxIter = Math.round(Number(process.env.FULL_WAIT_MIN ?? 45) * 3) // 每 20s 一轮
|
|
361
|
+
for (let i = 0; i < maxIter && !done; i++) {
|
|
362
|
+
await sleep(20_000)
|
|
363
|
+
const s = await invoke(toolInvoker, 'diag_status', { run_id: runId }, 30000)
|
|
364
|
+
lastText = resultText(s)
|
|
365
|
+
if (/状态=completed/.test(lastText)) done = true
|
|
366
|
+
else if (/状态=(failed|stopped)/.test(lastText)) break
|
|
367
|
+
if (i % 6 === 5) console.log(` … 轮询中(${(i + 1) * 20}s):${(lastText.match(/状态=\S+/) ?? [''])[0]}`)
|
|
368
|
+
}
|
|
369
|
+
ok(done, '真实诊断跑至 completed', (lastText.match(/评分=\S+|状态=\S+/g) ?? []).join(' '))
|
|
370
|
+
if (done) {
|
|
371
|
+
let stored = ''
|
|
372
|
+
for (let i = 0; i < 6; i++) {
|
|
373
|
+
await sleep(5000)
|
|
374
|
+
const runs = await raw('GET', `${BASE}/api/plugins/diag-bridge/runs`, { token: userToken })
|
|
375
|
+
const mine = (runs.json?.runs ?? []).find(x => x.runId === runId)
|
|
376
|
+
stored = JSON.stringify(mine ?? {})
|
|
377
|
+
if (mine?.stored === true) break
|
|
378
|
+
}
|
|
379
|
+
ok(stored.includes('"stored":true'), '报告自动入库(diag-bridge → KB)')
|
|
380
|
+
let kbHit = ''
|
|
381
|
+
for (let i = 0; i < 8; i++) {
|
|
382
|
+
await sleep(5000)
|
|
383
|
+
const s = await invoke(leadA, 'kb_search', { query: `${LINE} 深度根因诊断 three_system_e2e 结论` }, 60000)
|
|
384
|
+
kbHit = resultText(s)
|
|
385
|
+
if (!kbHit.startsWith('未检索到') && kbHit.length > 0) break
|
|
386
|
+
}
|
|
387
|
+
ok(!kbHit.startsWith('未检索到') && kbHit.length > 0, 'kb_search 检索到诊断报告/经验', kbHit.split('\n')[0]?.slice(0, 90))
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
console.log(' ·(未加 --full:不等待诊断完成;完成后的入库由 diag-bridge 轮询器自动完成,可重跑 --full 或用 diag_status 观察)')
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ══ Stage 4:插件启停(热重载 → 工具与路由同时生灭)═════════════════════════
|
|
396
|
+
{
|
|
397
|
+
console.log('\n── Stage 4 插件启停 ──')
|
|
398
|
+
const candidates = [join(ROOT, '.AgentWorkShop', 'plugins-state.json'), join(process.env.USERPROFILE ?? process.env.HOME ?? '', '.AgentWorkShop', 'plugins-state.json')]
|
|
399
|
+
const statePath = candidates.find(p => existsSync(p))
|
|
400
|
+
ok(Boolean(statePath), '定位 plugins-state.json', statePath ?? '未找到')
|
|
401
|
+
if (statePath && leadA?.id) {
|
|
402
|
+
const readState = () => JSON.parse(readFileSync(statePath, 'utf8'))
|
|
403
|
+
const writeState = (obj) => {
|
|
404
|
+
const tmp = `${statePath}.tmp`
|
|
405
|
+
writeFileSync(tmp, JSON.stringify(obj, null, 2))
|
|
406
|
+
renameSync(tmp, statePath)
|
|
407
|
+
}
|
|
408
|
+
const st0 = readState()
|
|
409
|
+
const disabled0 = Array.isArray(st0.disabled) ? st0.disabled : []
|
|
410
|
+
writeState({ ...st0, disabled: [...new Set([...disabled0, 'diag-bridge'])], updatedAt: new Date().toISOString() })
|
|
411
|
+
// 热重载会停止并重建 agent 运行时,每轮重新取成员(拿最新 token)再断言
|
|
412
|
+
const freshLead = async () => {
|
|
413
|
+
const members = await api('GET', `/api/workshop/channels/${channelA}/agents`, { token: userToken })
|
|
414
|
+
return (envelopeData(members) ?? []).find(m => m?.role === 'lead') ?? leadA
|
|
415
|
+
}
|
|
416
|
+
let gone = false
|
|
417
|
+
for (let i = 0; i < 20 && !gone; i++) {
|
|
418
|
+
await sleep(1500)
|
|
419
|
+
const fl = await freshLead()
|
|
420
|
+
const h = await raw('GET', `${BASE}/api/plugins/diag-bridge/health`, { token: userToken })
|
|
421
|
+
const l = await api('GET', `/api/workshop/agent-tools/list?agentId=${fl.id}`, { agent: fl })
|
|
422
|
+
const names4 = (l?.json?.data?.tools ?? l?.json?.tools ?? []).map(t => t?.name)
|
|
423
|
+
gone = h.status === 404 && !names4.includes('diag_run')
|
|
424
|
+
}
|
|
425
|
+
ok(gone, '停用 diag-bridge → 路由 404 且 diag_run 从工具清单消失(热重载 + 工具注销)')
|
|
426
|
+
|
|
427
|
+
writeState({ ...st0, disabled: disabled0, updatedAt: new Date().toISOString() })
|
|
428
|
+
let back = false
|
|
429
|
+
for (let i = 0; i < 25 && !back; i++) {
|
|
430
|
+
await sleep(1500)
|
|
431
|
+
const fl = await freshLead()
|
|
432
|
+
const h = await raw('GET', `${BASE}/api/plugins/diag-bridge/health`, { token: userToken })
|
|
433
|
+
const l = await api('GET', `/api/workshop/agent-tools/list?agentId=${fl.id}`, { agent: fl })
|
|
434
|
+
const names4 = (l?.json?.data?.tools ?? l?.json?.tools ?? []).map(t => t?.name)
|
|
435
|
+
back = h.status === 200 && names4.includes('diag_run')
|
|
436
|
+
}
|
|
437
|
+
ok(back, '重新启用 diag-bridge → 路由与工具恢复')
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ══ 汇总 ══════════════════════════════════════════════════════════════════
|
|
442
|
+
console.log(`\n══ 结果:${pass} 通过 / ${fail} 失败 ══`)
|
|
443
|
+
if (failures.length) {
|
|
444
|
+
console.error('失败项:')
|
|
445
|
+
for (const f of failures) console.error(` - ${f}`)
|
|
446
|
+
}
|
|
447
|
+
if (!FULL && runId) console.log(`\n提示:本次发起的诊断 runId=${runId},完成可加 --full 重跑或调 diag_status 观察。`)
|
|
448
|
+
process.exit(fail > 0 ? 1 : 0)
|