agentworkshop 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.AgentWorkShop/prompts/host-tools.json +123 -0
- package/.output/.AgentWorkShop/prompts/host-tools.json +123 -0
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{DJJDigim2.js → -FhgRqJl2.js} +1 -1
- package/.output/public/_nuxt/{CGk_VUhO2.js → 0qvcO_KM2.js} +1 -1
- package/.output/public/_nuxt/26OL4Ao82.js +1 -0
- package/.output/public/_nuxt/{sBDZymdy2.js → 2Kwo_j9G2.js} +1 -1
- package/.output/public/_nuxt/{BqYVLAcm.js → 2v5GdspM.js} +1 -1
- package/.output/public/_nuxt/{DkQBMVTs2.js → 8M7wFbEN2.js} +1 -1
- package/.output/public/_nuxt/{DtiY69xi2.js → 8SZLbrI42.js} +1 -1
- package/.output/public/_nuxt/{DF32f5EL2.js → B2NbzUau2.js} +1 -1
- package/.output/public/_nuxt/{CwZW1ZCe2.js → B4JlRHBB2.js} +1 -1
- package/.output/public/_nuxt/{BQOn0q6Y.js → B6C4WofF.js} +1 -1
- package/.output/public/_nuxt/{BC7W6lNm.js → B6UwtJ1a.js} +1 -1
- package/.output/public/_nuxt/{BfQ-ptH0.js → B8HFUsMH.js} +1 -1
- package/.output/public/_nuxt/{DzBbYWrw2.js → BBB_j2LU2.js} +1 -1
- package/.output/public/_nuxt/BCLUwdP52.js +1 -0
- package/.output/public/_nuxt/{CLxmY3Rh.js → BK4wbfGS.js} +1 -1
- package/.output/public/_nuxt/{BRMVDfPl2.js → BKidykmV2.js} +1 -1
- package/.output/public/_nuxt/BPUQZ9Lq2.js +8 -0
- package/.output/public/_nuxt/BRYBzVMJ.js +1 -0
- package/.output/public/_nuxt/{Bj5sjA5O.js → BRci9D_l.js} +1 -1
- package/.output/public/_nuxt/{Dmgqz2np2.js → BVGzwAxr2.js} +1 -1
- package/.output/public/_nuxt/BVg2R2VB2.js +1 -0
- package/.output/public/_nuxt/{C3aUN4Vp2.js → BW2uTUx52.js} +1 -1
- package/.output/public/_nuxt/{B5FKaMM02.js → BY3Lbz3h2.js} +1 -1
- package/.output/public/_nuxt/{DIwmMa_7.js → BYzim_Bn.js} +1 -1
- package/.output/public/_nuxt/{D8xUsQGt.js → B_Obcbiu.js} +1 -1
- package/.output/public/_nuxt/BaYDgyaR.js +3 -0
- package/.output/public/_nuxt/{DZJ11BTI.js → BbACaREb.js} +1 -1
- package/.output/public/_nuxt/{BJrj5JVe2.js → BcosvNXb2.js} +1 -1
- package/.output/public/_nuxt/{B_-vAvzb.js → BfeXib9G.js} +1 -1
- package/.output/public/_nuxt/{CeLqojCJ.js → BfgTar9L.js} +1 -1
- package/.output/public/_nuxt/BfvuWqSa.js +1 -0
- package/.output/public/_nuxt/{D8r8PNQa2.js → BiZWwdcD2.js} +1 -1
- package/.output/public/_nuxt/{DIFkTkdS.js → BkEokGX5.js} +1 -1
- package/.output/public/_nuxt/{CJSohaqk2.js → BklGqEto2.js} +2 -2
- package/.output/public/_nuxt/{DkpmSnHX.js → Bko5adbu.js} +1 -1
- package/.output/public/_nuxt/{DTQTGA6v2.js → BmBlIIJo2.js} +1 -1
- package/.output/public/_nuxt/{D_NjL-Hz.js → Bmvt9ydE.js} +1 -1
- package/.output/public/_nuxt/{Cp4r6Eiz.js → BoyuIoSm.js} +1 -1
- package/.output/public/_nuxt/{62J4vljm.js → Bq-tmkst.js} +1 -1
- package/.output/public/_nuxt/{BMMoqi94.js → BqFaZ4kS.js} +1 -1
- package/.output/public/_nuxt/BsJYieAL2.js +1 -0
- package/.output/public/_nuxt/{DD4J2Otc.js → BtZKe9ZV.js} +1 -1
- package/.output/public/_nuxt/{BnpY_zNx.js → BtumQhZK.js} +1 -1
- package/.output/public/_nuxt/{BjLzqWJH2.js → BwWs9EdO2.js} +1 -1
- package/.output/public/_nuxt/{Bx2a9cuY2.js → BxxpJxZf2.js} +1 -1
- package/.output/public/_nuxt/{qRXa-PM22.js → ByTnUqFw2.js} +1 -1
- package/.output/public/_nuxt/{DvOETuoT2.js → Byiiiwc72.js} +1 -1
- package/.output/public/_nuxt/{Car2lkbb.js → C1-SuwUN.js} +1 -1
- package/.output/public/_nuxt/{Dg81dOEO.js → C14pM5G4.js} +1 -1
- package/.output/public/_nuxt/{MDhaRQq_.js → C2Yp-B8m.js} +1 -1
- package/.output/public/_nuxt/{BJ2nJNlK2.js → C4e6_nom2.js} +1 -1
- package/.output/public/_nuxt/{CiCEprsp2.js → C5OStZJ62.js} +1 -1
- package/.output/public/_nuxt/{ByMj6e7s.js → C6lssmvl.js} +2 -2
- package/.output/public/_nuxt/{C6uBP0Xa.js → CC-GEjfN.js} +1 -1
- package/.output/public/_nuxt/CC2Pv8VL2.js +1 -0
- package/.output/public/_nuxt/{DKiqdYnj.js → CCcMgxV9.js} +1 -1
- package/.output/public/_nuxt/{5S26I34j2.js → CDL30A2z2.js} +1 -1
- package/.output/public/_nuxt/{B8C-QJB5.js → CDYLasP8.js} +1 -1
- package/.output/public/_nuxt/CElzwyXQ.js +1 -0
- package/.output/public/_nuxt/{2KE8xGVm.js → CEvJu-xI.js} +1 -1
- package/.output/public/_nuxt/{CR612R752.js → CFQRFctQ2.js} +1 -1
- package/.output/public/_nuxt/{DnzjxC0u.js → CHk9XxNi.js} +2 -2
- package/.output/public/_nuxt/{Bx4MR0jd.js → CLFmtXU7.js} +1 -1
- package/.output/public/_nuxt/{DW8k_0dQ.js → CMJi6yYS.js} +1 -1
- package/.output/public/_nuxt/{DD-r0S1p.js → CPvFGbr8.js} +1 -1
- package/.output/public/_nuxt/{CCgbgkid.js → CQSxS_Ek.js} +2 -2
- package/.output/public/_nuxt/{jGxHnjW6.js → CQuo31jF.js} +1 -1
- package/.output/public/_nuxt/{SoO2K-di.js → CRCrWy-6.js} +1 -1
- package/.output/public/_nuxt/CUCFFL9m.js +84 -0
- package/.output/public/_nuxt/CZYNy60j2.js +1 -0
- package/.output/public/_nuxt/{WPwdj9pg.js → CZsFf7ck.js} +1 -1
- package/.output/public/_nuxt/CfFUK6932.js +2 -0
- package/.output/public/_nuxt/{DAsjfdZD.js → CfIbFnJz.js} +1 -1
- package/.output/public/_nuxt/{Tk6TQZjU.js → CjF4QY9h.js} +1 -1
- package/.output/public/_nuxt/{DlUQa0wn.js → Cjo1t2IF.js} +1 -1
- package/.output/public/_nuxt/{CDn0eDn8.js → CmuGMxDP.js} +1 -1
- package/.output/public/_nuxt/Cn6EojFe.js +1 -0
- package/.output/public/_nuxt/{DLqSQbGv.js → CnYazk8d.js} +1 -1
- package/.output/public/_nuxt/{DQfy6R_2.js → CnrGVJIr.js} +1 -1
- package/.output/public/_nuxt/{C0YL0HKK.js → CtcBhkci.js} +1 -1
- package/.output/public/_nuxt/{h5gsh8E92.js → Cv-Mv7_h2.js} +1 -1
- package/.output/public/_nuxt/{vPcnBJsa2.js → CzlZrfq82.js} +1 -1
- package/.output/public/_nuxt/{C2YYOgMe.js → CznnUTZx.js} +2 -2
- package/.output/public/_nuxt/{csitHKnE2.js → D-80EfyA2.js} +1 -1
- package/.output/public/_nuxt/{BRdoISnJ2.js → D1C6qtS12.js} +1 -1
- package/.output/public/_nuxt/{Cnrmlixo.js → D1MkndMZ.js} +2 -2
- package/.output/public/_nuxt/{BLwDhlaa.js → D37gODe0.js} +1 -1
- package/.output/public/_nuxt/{NXY37IO62.js → D6FhDrYe2.js} +1 -1
- package/.output/public/_nuxt/{Btx8wL4-.js → D7TZLu4F.js} +1 -1
- package/.output/public/_nuxt/{qoUdVDnC.js → D7tEwf-o.js} +3 -3
- package/.output/public/_nuxt/D8HQPtm62.js +1 -0
- package/.output/public/_nuxt/D9O4L4jK.js +1 -0
- package/.output/public/_nuxt/{aQ1B7BZa2.js → D9lL3fJM2.js} +2 -2
- package/.output/public/_nuxt/{CT-vTjcG2.js → DC-2eKo62.js} +1 -1
- package/.output/public/_nuxt/DF9ETYJ32.js +1 -0
- package/.output/public/_nuxt/DGGBrc0q2.js +1 -0
- package/.output/public/_nuxt/DGu_fna7.js +1 -0
- package/.output/public/_nuxt/{BupBjt6B.js → DKOvbitt.js} +1 -1
- package/.output/public/_nuxt/{B1PzYsqN.js → DNxudyND.js} +1 -1
- package/.output/public/_nuxt/{BfX6_AjY.js → DOT-XQyi.js} +1 -1
- package/.output/public/_nuxt/{BsTZnkVK.js → DPPC9Teq.js} +1 -1
- package/.output/public/_nuxt/{HxohXQ_r.js → DRskyE7l.js} +1 -1
- package/.output/public/_nuxt/{B-U7E_Mc2.js → DW4ZgInh2.js} +1 -1
- package/.output/public/_nuxt/{BErG4kzc2.js → DWe6BMlp2.js} +1 -1
- package/.output/public/_nuxt/{BH8RvpVy.js → DWy0AjON.js} +1 -1
- package/.output/public/_nuxt/{Bc_k6AVA.js → DXgw1dVL.js} +4 -4
- package/.output/public/_nuxt/{C3sWJ8SD2.js → DZsKIzKD2.js} +1 -1
- package/.output/public/_nuxt/D_59BNP1.js +14 -0
- package/.output/public/_nuxt/{BQIO4MkA2.js → D_FZ3KZI2.js} +1 -1
- package/.output/public/_nuxt/{n_Njdwii2.js → D__vJGHF2.js} +1 -1
- package/.output/public/_nuxt/{C9gErLQc2.js → DaizNGUm2.js} +1 -1
- package/.output/public/_nuxt/{C-MY-JQE.js → DdyE0ylS.js} +1 -1
- package/.output/public/_nuxt/DhLlTxQh2.js +1 -0
- package/.output/public/_nuxt/{CxVhG0Xx.js → DhW7cFOa.js} +1 -1
- package/.output/public/_nuxt/{Rt2i6k7C2.js → DhjgWrB62.js} +1 -1
- package/.output/public/_nuxt/{KwT4sN_d.js → DiWcX4gj.js} +1 -1
- package/.output/public/_nuxt/DiloA8vK.js +1 -0
- package/.output/public/_nuxt/{DX-c5laM2.js → DlS-SkQ42.js} +1 -1
- package/.output/public/_nuxt/{D_Zspo1y.js → DmHp9yQ3.js} +1 -1
- package/.output/public/_nuxt/{dq1HKoGD.js → DoL9u6wu.js} +1 -1
- package/.output/public/_nuxt/{CIvMT5Ic.js → DqxBCeDe.js} +1 -1
- package/.output/public/_nuxt/{BnNmkGJS2.js → DrlnrdvY2.js} +3 -3
- package/.output/public/_nuxt/{BAcjVcku2.js → Dt8TL8_X2.js} +1 -1
- package/.output/public/_nuxt/{BtfwbNC0.js → Du7gS8RU.js} +1 -1
- package/.output/public/_nuxt/DuUc4bbY2.js +1 -0
- package/.output/public/_nuxt/{DPUXbTg9.js → DudoisLM.js} +1 -1
- package/.output/public/_nuxt/{Ce5R-ZGf2.js → DwgVlehj2.js} +1 -1
- package/.output/public/_nuxt/Dyxf2xZw.js +1 -0
- package/.output/public/_nuxt/Dz9C2pTa.js +1 -0
- package/.output/public/_nuxt/{BYxG9Hb2.js → EMbqTHfT.js} +1 -1
- package/.output/public/_nuxt/{B1SzppY12.js → FsRVXpyF2.js} +1 -1
- package/.output/public/_nuxt/{0MLy6ogg2.js → GQrEW8nk2.js} +1 -1
- package/.output/public/_nuxt/{Tuz5LcLM.js → IXR1BWP5.js} +1 -1
- package/.output/public/_nuxt/{-jpUnM1I2.js → Iub61dX92.js} +1 -1
- package/.output/public/_nuxt/{CQgOzpvI.js → JegDslce.js} +2 -2
- package/.output/public/_nuxt/{DrF3TWoj.js → KzK37yIr.js} +1 -1
- package/.output/public/_nuxt/{Br3MggOL.js → N-CCmCsX.js} +1 -1
- package/.output/public/_nuxt/{DHhmq7s_.js → NfvZ5Lzv.js} +1 -1
- package/.output/public/_nuxt/{CzX_kaPL.js → POTcePF_.js} +1 -1
- package/.output/public/_nuxt/{D7s96J3r2.js → Q-JpBT032.js} +1 -1
- package/.output/public/_nuxt/{DzwIZ81f2.js → R1g64DNi2.js} +1 -1
- package/.output/public/_nuxt/{JgheoP3e.js → R5Pz7BqB.js} +1 -1
- package/.output/public/_nuxt/{yLyz0-Yz.js → SvjzWbn8.js} +1 -1
- package/.output/public/_nuxt/{Dd7MZc_Q.js → XqXoaoDs.js} +1 -1
- package/.output/public/_nuxt/{B6jtLy6z.js → YAA98rWJ.js} +11 -11
- package/.output/public/_nuxt/{HX4_xnak2.js → YLYSczSq2.js} +1 -1
- package/.output/public/_nuxt/{BBw9ylUY2.js → YNhAoK-R2.js} +1 -1
- package/.output/public/_nuxt/Zkbt8Uk5.js +1 -0
- package/.output/public/_nuxt/aml.PalMp-5n.css +1 -0
- package/.output/public/_nuxt/{C41CcsiD.js → b7UcqLKi.js} +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/fa0af8b5-3929-428d-a6de-3b5cc6b8248f.json +1 -0
- package/.output/public/_nuxt/{DVkFSOZi.js → cKj4vSo5.js} +1 -1
- package/.output/public/_nuxt/{r1CpGsFp.js → dHwWyr6b.js} +1 -1
- package/.output/public/_nuxt/{DlcA9Gxl.js → eePbiWEh.js} +1 -1
- package/.output/public/_nuxt/{cjRzSvAg2.js → eymRov9Y2.js} +1 -1
- package/.output/public/_nuxt/{B8cViLJT.js → geMze5qw.js} +1 -1
- package/.output/public/_nuxt/{uu1wfdfR2.js → gh-WMxV_2.js} +1 -1
- package/.output/public/_nuxt/{Ce6e1mPk2.js → hCN555XP2.js} +1 -1
- package/.output/public/_nuxt/{rdUE8Khf.js → hZG5MLjK.js} +3 -3
- package/.output/public/_nuxt/{CcX_jP-d.js → hlfahYxK.js} +1 -1
- package/.output/public/_nuxt/{cGSl2GZp2.js → ilJHF1nA2.js} +1 -1
- package/.output/public/_nuxt/{D6ZcgHPz.js → jDuiDtGb.js} +1 -1
- package/.output/public/_nuxt/{DbeNMehP.js → k9b520K-.js} +1 -1
- package/.output/public/_nuxt/ontology.B_3Wn907.css +1 -0
- package/.output/public/_nuxt/optimizer.CLA8I1sy.css +1 -0
- package/.output/public/_nuxt/{DSORcpTJ.js → rDvMijKA.js} +2 -2
- package/.output/public/_nuxt/{CAiHhX8P2.js → rF7mggl72.js} +2 -2
- package/.output/public/_nuxt/ri9UF8rk.js +2 -0
- package/.output/public/_nuxt/{KgnxOsMr.js → toG_qAMH.js} +1 -1
- package/.output/public/_nuxt/{KQZmHR_K.js → u3h4pGpm.js} +2 -2
- package/.output/public/_nuxt/{dJ7gK7C2.js → uomnHjhM.js} +1 -1
- package/.output/public/_nuxt/{BUTg4W_x2.js → v77qozuJ2.js} +1 -1
- package/.output/public/_nuxt/{BcEr7tIB.js → v7cV7hJa.js} +1 -1
- package/.output/public/_nuxt/{D25dVZ822.js → vkQoYNQR2.js} +2 -2
- package/.output/public/_nuxt/{wR_w07322.js → vp3LtnTz2.js} +1 -1
- package/.output/public/_nuxt/{Bnc8HELj2.js → we7-sMSo2.js} +1 -1
- package/.output/public/_nuxt/{kN3ZzR1-.js → wxISMS1i.js} +1 -1
- package/.output/public/_nuxt/{DGrrucbG.js → zy7efYKR.js} +1 -1
- package/.output/server/chunks/_/data-export.mjs +5 -5
- package/.output/server/chunks/_/data-query.mjs +2 -2
- package/.output/server/chunks/_/eda-run.repo.mjs +2 -2
- package/.output/server/chunks/_/features.mjs +4 -4
- package/.output/server/chunks/_/golden.mjs +2 -2
- package/.output/server/chunks/_/index.mjs +1 -1
- package/.output/server/chunks/_/mes-console.mjs +1 -1
- package/.output/server/chunks/_/nitro.mjs +2149 -1520
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/_/spc.mjs +1 -1
- package/.output/server/chunks/build/TownView-BWMDbosY.mjs +1 -1
- package/.output/server/chunks/build/{_wsId_-BNKrrV1H.mjs → _wsId_-BPy-Aj9L.mjs} +3 -3
- package/.output/server/chunks/build/{_wsId_-BNKrrV1H.mjs.map → _wsId_-BPy-Aj9L.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-8tV9xL97.mjs → agents-CpNLdI8E.mjs} +6 -6
- package/.output/server/chunks/build/{agents-8tV9xL97.mjs.map → agents-CpNLdI8E.mjs.map} +1 -1
- package/.output/server/chunks/build/{aml-BNTEDOsl.mjs → aml-DRAFRcGU.mjs} +15 -12
- package/.output/server/chunks/build/{aml-BNTEDOsl.mjs.map → aml-DRAFRcGU.mjs.map} +1 -1
- package/.output/server/chunks/build/{channel-templates-Iot9Y3vV.mjs → channel-templates-ByHOQQkO.mjs} +2 -2
- package/.output/server/chunks/build/{channel-templates-Iot9Y3vV.mjs.map → channel-templates-ByHOQQkO.mjs.map} +1 -1
- package/.output/server/chunks/build/daq-CdxcfY_G.mjs +1 -1
- package/.output/server/chunks/build/dcw-BlDQtS4q.mjs +1 -1
- package/.output/server/chunks/build/{default-CJkOcmrm.mjs → default-CNWS6NUw.mjs} +4 -4
- package/.output/server/chunks/build/{default-CJkOcmrm.mjs.map → default-CNWS6NUw.mjs.map} +1 -1
- package/.output/server/chunks/build/{dropdown-CjwsxU39.mjs → dropdown-BNwTVGde.mjs} +2 -2
- package/.output/server/chunks/build/{dropdown-CjwsxU39.mjs.map → dropdown-BNwTVGde.mjs.map} +1 -1
- package/.output/server/chunks/build/{dropdown-C6YH_kSO.mjs → dropdown-Cd-xPhR6.mjs} +2 -2
- package/.output/server/chunks/build/{dropdown-C6YH_kSO.mjs.map → dropdown-Cd-xPhR6.mjs.map} +1 -1
- package/.output/server/chunks/build/input-number-BNlNDK7H.mjs +1 -1
- package/.output/server/chunks/build/{logs-BTfJQ6vQ.mjs → logs-pRb-TMWa.mjs} +3 -3
- package/.output/server/chunks/build/{logs-BTfJQ6vQ.mjs.map → logs-pRb-TMWa.mjs.map} +1 -1
- package/.output/server/chunks/build/{monitor-DvaR3yo8.mjs → monitor-Cq6hRFeQ.mjs} +6 -6
- package/.output/server/chunks/build/{monitor-DvaR3yo8.mjs.map → monitor-Cq6hRFeQ.mjs.map} +1 -1
- package/.output/server/chunks/build/{ontology-C3HS5pMo.mjs → ontology-DK0Mf0zT.mjs} +510 -81
- package/.output/server/chunks/build/ontology-DK0Mf0zT.mjs.map +1 -0
- package/.output/server/chunks/build/{operations-Cl8nVm9D.mjs → operations-SMQrRAcr.mjs} +3 -3
- package/.output/server/chunks/build/{operations-Cl8nVm9D.mjs.map → operations-SMQrRAcr.mjs.map} +1 -1
- package/.output/server/chunks/build/optimizer-BlWTsM-W.mjs +202 -0
- package/.output/server/chunks/build/optimizer-BlWTsM-W.mjs.map +1 -0
- package/.output/server/chunks/build/{pagination-KaIvHBzi.mjs → pagination-DTAconUo.mjs} +2 -2
- package/.output/server/chunks/build/{pagination-KaIvHBzi.mjs.map → pagination-DTAconUo.mjs.map} +1 -1
- package/.output/server/chunks/build/{permissions-CVTfixZj.mjs → permissions-DgXQZSyx.mjs} +6 -6
- package/.output/server/chunks/build/{permissions-CVTfixZj.mjs.map → permissions-DgXQZSyx.mjs.map} +1 -1
- package/.output/server/chunks/build/{schedules-BLhn2CLn.mjs → schedules-dSnr8url.mjs} +6 -6
- package/.output/server/chunks/build/{schedules-BLhn2CLn.mjs.map → schedules-dSnr8url.mjs.map} +1 -1
- package/.output/server/chunks/build/{select--XFl7CtY.mjs → select-B3Dsb9yq.mjs} +3 -3
- package/.output/server/chunks/build/{select--XFl7CtY.mjs.map → select-B3Dsb9yq.mjs.map} +1 -1
- package/.output/server/chunks/build/{settings-DIf_iYdu.mjs → settings-BrIhnS2Z.mjs} +2 -2
- package/.output/server/chunks/build/{settings-DIf_iYdu.mjs.map → settings-BrIhnS2Z.mjs.map} +1 -1
- package/.output/server/chunks/build/{table-DIu_YCJN.mjs → table-CmVD-cA9.mjs} +4 -4
- package/.output/server/chunks/build/{table-DIu_YCJN.mjs.map → table-CmVD-cA9.mjs.map} +1 -1
- package/.output/server/chunks/build/{teams-B2J6ouRY.mjs → teams-B1d32eHK.mjs} +4 -4
- package/.output/server/chunks/build/{teams-B2J6ouRY.mjs.map → teams-B1d32eHK.mjs.map} +1 -1
- package/.output/server/chunks/build/{tokens-DFQGaN2n.mjs → tokens-GYaGJ35Z.mjs} +6 -6
- package/.output/server/chunks/build/{tokens-DFQGaN2n.mjs.map → tokens-GYaGJ35Z.mjs.map} +1 -1
- package/.output/server/chunks/build/{tools-DWJFnU_t.mjs → tools-s_9U1Djj.mjs} +6 -6
- package/.output/server/chunks/build/{tools-DWJFnU_t.mjs.map → tools-s_9U1Djj.mjs.map} +1 -1
- package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
- package/.output/server/chunks/build/useDaqAddNode-D_Rkju0f.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailNode-DeOsDxK5.mjs +1 -1
- package/.output/server/chunks/build/useDaqDetailParams-Cm9a9r5G.mjs +1 -1
- package/.output/server/chunks/build/useDaqFilters-CctmO03p.mjs +1 -1
- package/.output/server/chunks/build/useDaqStream-B9ZBVQrI.mjs +1 -1
- package/.output/server/chunks/build/useDaqTemplates-CK37sMfi.mjs +1 -1
- package/.output/server/chunks/build/useDcwAddNode-DACVcEHs.mjs +1 -1
- package/.output/server/chunks/build/useDcwStream-Dir5ufAU.mjs +1 -1
- package/.output/server/chunks/build/{users-BWw0VCr9.mjs → users-DjiVC_tS.mjs} +6 -6
- package/.output/server/chunks/build/{users-BWw0VCr9.mjs.map → users-DjiVC_tS.mjs.map} +1 -1
- package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
- package/.output/server/chunks/routes/api/system/backup/run.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/aml/models/_id_.patch.mjs +7 -1
- package/.output/server/chunks/routes/api/workshop/aml/models/_id_.patch.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/channels.post.mjs +6 -6
- package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs +2 -2
- package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/autofix.mjs +52 -0
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/autofix.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/compile.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/evidence.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/publish.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval-decision.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/transition.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/subgraph.mjs +38 -0
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/subgraph.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/validate.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/export/_ontologyId_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/index.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/scene.mjs +8 -1
- package/.output/server/chunks/routes/api/workshop/ontology/scene.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/traces/_ontologyId_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId/compare.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId_.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/capabilities.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/index.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/index2.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/profiles/_id/publish.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/crosscheck.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/dispatch.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/optimization/solve.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/datasets.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/eligibility.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/index.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/specs/_id/publish.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/process-model/train.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/tools/sync.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
- package/.output/server/chunks/routes/renderer.mjs +2 -2
- package/.output/server/chunks/virtual/entry.mjs +92 -18
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +5 -4
- package/README.md +6 -5
- package/app/components/aml/AmlModelsTable.vue +5 -0
- package/app/components/ontology/OntologyGraph.vue +356 -0
- package/app/pages/aml/styles.css +1 -0
- package/app/pages/ontology/index.vue +318 -35
- package/app/pages/ontology/types.ts +42 -0
- package/app/pages/optimizer/index.vue +21 -4
- package/config.yml +1 -1
- package/docs/cli.en.md +2 -2
- package/docs/cli.md +2 -2
- package/docs/plugins.en.md +1 -1
- package/docs/plugins.md +1 -1
- package/docs/sdk.en.md +1 -1
- package/docs/sdk.md +1 -1
- package/i18n/locales/en.ts +26 -0
- package/i18n/locales/zh-CN.ts +26 -0
- package/mcp/aw-mcp-server.mjs +20 -2
- package/package.json +1 -1
- package/scripts/e2e-direct-rest-simulator-paper.mjs +624 -0
- package/scripts/e2e-idd-results.mjs +177 -0
- package/scripts/e2e-model-loop-live.mjs +58 -4
- package/scripts/e2e-ontology-live.mjs +138 -3
- package/scripts/e2e-ontology.mjs +393 -328
- package/scripts/e2e-pi-simulator-paper.mjs +2316 -0
- package/scripts/e2e-registered-harness-simulator-paper.mjs +1926 -0
- package/scripts/e2e-simulator-optimizer-paper.mjs +264 -0
- package/scripts/fixtures/omp-paper-harness.mjs +163 -0
- package/sdk/context.mjs +6 -0
- package/sdk/index.d.mts +2 -0
- package/server/api/workshop/aml/models/[id].patch.ts +36 -30
- package/server/api/workshop/ontology/drafts/[draftId]/autofix.ts +31 -0
- package/server/api/workshop/ontology/drafts/[draftId]/subgraph.ts +24 -0
- package/server/api/workshop/ontology/scene.ts +14 -1
- package/server/plugins-builtin/diag-bridge/client.mjs +390 -239
- package/server/plugins-builtin/diag-bridge/host/helpers.mjs +80 -0
- package/server/plugins-builtin/diag-bridge/host/plugin.mjs +264 -2
- package/server/plugins-builtin/diag-bridge/i18n.json +84 -4
- package/server/services/workshop/agents/adapters/omp-rpc-client/frames.ts +129 -111
- package/server/services/workshop/agents/host-tool-bridge/tools/industrial.ts +6 -3
- package/server/services/workshop/agents/industrial/aml-job-tools.ts +602 -569
- package/server/services/workshop/agents/industrial/grant-guard.ts +15 -1
- package/server/services/workshop/agents/industrial/index.ts +2 -2
- package/server/services/workshop/agents/industrial/ontology-tools.ts +66 -6
- package/server/services/workshop/agents/industrial/ops-tools.ts +54 -16
- package/server/services/workshop/agents/industrial/optimizer-tools.ts +2 -0
- package/server/services/workshop/agents/industrial/recipe-gate.ts +19 -6
- package/server/services/workshop/agents/industrial/recipe-propose-tools.ts +1 -1
- package/server/services/workshop/agents/omp-agent/supervision-history.ts +38 -0
- package/server/services/workshop/agents/omp-agent/tools-steer.ts +18 -1
- package/server/services/workshop/aml/aml.repo.ts +16 -0
- package/server/services/workshop/daq/bus/index.ts +12 -5
- package/server/services/workshop/db/database/open.ts +97 -97
- package/server/services/workshop/db/database/schema.ts +1 -0
- package/server/services/workshop/ontology/autofix.ts +198 -0
- package/server/services/workshop/ontology/contracts.ts +3 -1
- package/server/services/workshop/ontology/index.ts +2 -0
- package/server/services/workshop/ontology/ontology.service.ts +46 -0
- package/server/services/workshop/ontology/subgraph.core.ts +300 -0
- package/server/services/workshop/ontology/subgraph.service.ts +113 -0
- package/server/services/workshop/optimization/contracts.ts +2 -0
- package/server/services/workshop/optimization/profile-adopt.service.ts +5 -0
- package/server/services/workshop/optimization/profile.repo.ts +6 -4
- package/.output/public/_nuxt/B3Y5bCrM.js +0 -1
- package/.output/public/_nuxt/B8N8nsad2.js +0 -2
- package/.output/public/_nuxt/BO0BJx3w2.js +0 -8
- package/.output/public/_nuxt/BWn-lfCp2.js +0 -1
- package/.output/public/_nuxt/Bcs9zzO62.js +0 -1
- package/.output/public/_nuxt/BwO2Y5FH2.js +0 -1
- package/.output/public/_nuxt/Byf4IxU2.js +0 -14
- package/.output/public/_nuxt/C-LU6xv_.js +0 -1
- package/.output/public/_nuxt/CJ0bjZ6v2.js +0 -1
- package/.output/public/_nuxt/CJ3Vul2u.js +0 -1
- package/.output/public/_nuxt/CNwx04ln.js +0 -84
- package/.output/public/_nuxt/CVCscYzb2.js +0 -1
- package/.output/public/_nuxt/C_A0MCol2.js +0 -1
- package/.output/public/_nuxt/CnP6snIz.js +0 -3
- package/.output/public/_nuxt/D0mzKkZr.js +0 -1
- package/.output/public/_nuxt/D3buuIkE.js +0 -1
- package/.output/public/_nuxt/D4_INWgq.js +0 -1
- package/.output/public/_nuxt/D5kCimyw2.js +0 -1
- package/.output/public/_nuxt/DBtXLkyA.js +0 -1
- package/.output/public/_nuxt/DHURbGkn2.js +0 -1
- package/.output/public/_nuxt/DaBzS-iu.js +0 -1
- package/.output/public/_nuxt/DnlhV7Xe.js +0 -1
- package/.output/public/_nuxt/GtI6PUI32.js +0 -1
- package/.output/public/_nuxt/Nw_6OpQZ.js +0 -1
- package/.output/public/_nuxt/Y4sMSPrS2.js +0 -1
- package/.output/public/_nuxt/aml.CeLxRzvI.css +0 -1
- package/.output/public/_nuxt/builds/meta/7a79a7e4-d691-4cf5-ae3a-3b01814479fb.json +0 -1
- package/.output/public/_nuxt/goF_oZHF.js +0 -2
- package/.output/public/_nuxt/ontology.QMGeh66l.css +0 -1
- package/.output/public/_nuxt/optimizer.B8gCbU1b.css +0 -1
- package/.output/public/_nuxt/rJ7To74v2.js +0 -1
- package/.output/server/chunks/build/ontology-C3HS5pMo.mjs.map +0 -1
- package/.output/server/chunks/build/optimizer-BOtGWEtc.mjs +0 -200
- package/.output/server/chunks/build/optimizer-BOtGWEtc.mjs.map +0 -1
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Isolated direct-REST route evaluation for AgentWorkShop's recipe_trial flow.
|
|
4
|
+
* Runs one approved, bounded candidate per simulator scene through
|
|
5
|
+
* POST /api/workshop/agent-tools/invoke. A non-scheduled worker identity is
|
|
6
|
+
* used only to provide the platform's agent-scoped permission context; no
|
|
7
|
+
* registered harness transport or LLM decision is used.
|
|
8
|
+
*/
|
|
9
|
+
import { spawn, spawnSync } from 'node:child_process'
|
|
10
|
+
import { createHash } from 'node:crypto'
|
|
11
|
+
import { closeSync, copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, symlinkSync, writeFileSync } from 'node:fs'
|
|
12
|
+
import { createServer } from 'node:net'
|
|
13
|
+
import { dirname, join, resolve } from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import { setTimeout as delay } from 'node:timers/promises'
|
|
16
|
+
import { resolveRunMode } from '../shared/config/home.mjs'
|
|
17
|
+
|
|
18
|
+
const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
19
|
+
const VERSION = JSON.parse(readFileSync(join(REPO, 'package.json'), 'utf8')).version
|
|
20
|
+
const PORTS = { app: 3997, sim: 4020, mqtt: 18840, appMqtt: 45323, tsdb: 45321, objectstore: 45322, forbidden: 4010 }
|
|
21
|
+
const APP_BASE = `http://127.0.0.1:${PORTS.app}`
|
|
22
|
+
const SIM_BASE = `http://127.0.0.1:${PORTS.sim}`
|
|
23
|
+
const SIM_SOURCE = join(REPO, 'plc-node-simulator')
|
|
24
|
+
const NUXT_CLI = join(REPO, 'node_modules', 'nuxt', 'bin', 'nuxt.mjs')
|
|
25
|
+
const OUTPUT_ROOT = join(REPO, 'docs', 'audit', 'evidence', '2026-10-25-current-cross-scene', 'direct-rest-trial-2026-10-25')
|
|
26
|
+
mkdirSync(OUTPUT_ROOT, { recursive: true })
|
|
27
|
+
const OUTPUT = mkdtempSync(join(OUTPUT_ROOT, 'run-'))
|
|
28
|
+
const TAG = `drt${Date.now().toString(36)}`
|
|
29
|
+
mkdirSync(join(REPO, '.omc'), { recursive: true })
|
|
30
|
+
const ROOT = mkdtempSync(join(REPO, '.omc', 'direct-rest-paper-'))
|
|
31
|
+
const HOME = join(ROOT, 'aw-home')
|
|
32
|
+
const SIM_ROOT = join(ROOT, 'simulator')
|
|
33
|
+
const EMPTY_DOTENV = join(HOME, 'empty.env')
|
|
34
|
+
const APP_LOG = join(OUTPUT, 'agentworkshop.log')
|
|
35
|
+
const SIM_LOG = join(OUTPUT, 'simulator.log')
|
|
36
|
+
const appLogFd = openSync(APP_LOG, 'a')
|
|
37
|
+
const simLogFd = openSync(SIM_LOG, 'a')
|
|
38
|
+
const CHILD_OS_ENV_KEYS = process.platform === 'win32'
|
|
39
|
+
? ['PATH', 'PATHEXT', 'SystemRoot', 'WINDIR', 'TEMP', 'TMP', 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'HOMEDRIVE', 'HOMEPATH', 'ProgramData']
|
|
40
|
+
: ['PATH', 'HOME', 'TMPDIR', 'TMP', 'TEMP', 'LANG', 'LC_ALL']
|
|
41
|
+
const children = new Set()
|
|
42
|
+
const allocatedDevicePorts = []
|
|
43
|
+
const evidence = {
|
|
44
|
+
schemaVersion: 1,
|
|
45
|
+
experiment: {
|
|
46
|
+
id: TAG,
|
|
47
|
+
name: 'direct-REST recipe_trial route coverage',
|
|
48
|
+
projectVersion: VERSION,
|
|
49
|
+
route: 'POST /api/workshop/agent-tools/invoke',
|
|
50
|
+
executionMode: 'direct HTTP invocation; worker identity supplies agent scope, with no harness transport or LLM decision',
|
|
51
|
+
startedAt: new Date().toISOString(),
|
|
52
|
+
postTrialSnapshotDelaySeconds: 20,
|
|
53
|
+
scenes: [],
|
|
54
|
+
},
|
|
55
|
+
isolation: { result: 'pending', appBase: APP_BASE, simulatorBase: SIM_BASE, awHome: 'fresh isolated home mode', loopbackOnly: true, checks: [] },
|
|
56
|
+
checks: [],
|
|
57
|
+
status: 'running',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let appProcess
|
|
61
|
+
let simProcess
|
|
62
|
+
let authToken
|
|
63
|
+
let syntheticAdminId
|
|
64
|
+
let stage = 'preflight'
|
|
65
|
+
let sceneId = null
|
|
66
|
+
|
|
67
|
+
function check(id, pass, detail = {}) {
|
|
68
|
+
evidence.checks.push({ id, status: pass ? 'pass' : 'fail', detail })
|
|
69
|
+
if (!pass) throw new Error(`check failed: ${id}`)
|
|
70
|
+
}
|
|
71
|
+
function isolationCheck(id, pass, detail = {}) {
|
|
72
|
+
evidence.isolation.checks.push({ id, status: pass ? 'pass' : 'fail', detail })
|
|
73
|
+
if (!pass) throw new Error(`isolation check failed: ${id}`)
|
|
74
|
+
}
|
|
75
|
+
function safeText(value, limit = 600) {
|
|
76
|
+
let text = String(value ?? '')
|
|
77
|
+
if (authToken) text = text.split(authToken).join('[REDACTED]')
|
|
78
|
+
text = text.replace(/\bBearer\s+[^\s,;]+/gi, 'Bearer [REDACTED]')
|
|
79
|
+
.replace(/\b(?:api[_-]?key|access[_-]?token|client[_-]?secret|password|secret)\s*[:=]\s*["']?[^\s,"'}]+/gi, '[REDACTED_CREDENTIAL]')
|
|
80
|
+
return text.slice(0, limit)
|
|
81
|
+
}
|
|
82
|
+
function parsePayload(body) {
|
|
83
|
+
return body && typeof body === 'object' && Object.hasOwn(body, 'data') ? body.data : body
|
|
84
|
+
}
|
|
85
|
+
function assertLoopback(base, port) {
|
|
86
|
+
const url = new URL(base)
|
|
87
|
+
if (url.protocol !== 'http:' || url.hostname !== '127.0.0.1' || Number(url.port) !== port || port === PORTS.forbidden) {
|
|
88
|
+
throw new Error('network boundary rejected a non-approved endpoint')
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function freePort(port, label) {
|
|
92
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535 || port === PORTS.forbidden) throw new Error(`invalid port for ${label}`)
|
|
93
|
+
const server = createServer()
|
|
94
|
+
await new Promise((resolvePromise, reject) => {
|
|
95
|
+
server.once('error', reject)
|
|
96
|
+
server.listen(port, '127.0.0.1', resolvePromise)
|
|
97
|
+
})
|
|
98
|
+
await new Promise(resolvePromise => server.close(resolvePromise))
|
|
99
|
+
}
|
|
100
|
+
async function preflight() {
|
|
101
|
+
if (VERSION !== '0.8.0') throw new Error(`expected package 0.8.0, got ${VERSION}`)
|
|
102
|
+
if (!existsSync(join(SIM_SOURCE, 'src', 'server', 'index.ts')) || !existsSync(join(SIM_SOURCE, 'node_modules')) || !existsSync(NUXT_CLI)) {
|
|
103
|
+
throw new Error('local simulator source/runtime or Nuxt CLI is missing')
|
|
104
|
+
}
|
|
105
|
+
if (resolveRunMode({ cwd: REPO, packageRoot: REPO, env: { AW_HOME: HOME, AW_MODE: 'home' } }).mode !== 'home') throw new Error('fresh AW_HOME did not resolve to home mode')
|
|
106
|
+
for (const [name, port] of Object.entries(PORTS)) {
|
|
107
|
+
if (name === 'forbidden') continue
|
|
108
|
+
await freePort(port, name)
|
|
109
|
+
isolationCheck(`free-loopback-${name}-port`, true, { host: '127.0.0.1', port })
|
|
110
|
+
}
|
|
111
|
+
if (PORTS.app === PORTS.forbidden || PORTS.sim === PORTS.forbidden || PORTS.mqtt === PORTS.forbidden) throw new Error('forbidden service port configured')
|
|
112
|
+
isolationCheck('local-inputs-only', true, { version: VERSION, simulatorSource: true, simulatorRuntime: true, noExternalArchiveOrLLM: true })
|
|
113
|
+
}
|
|
114
|
+
function childEnv(extra = {}) {
|
|
115
|
+
const env = {}
|
|
116
|
+
for (const key of CHILD_OS_ENV_KEYS) if (process.env[key] !== undefined) env[key] = process.env[key]
|
|
117
|
+
Object.assign(env, {
|
|
118
|
+
AW_HOME: HOME,
|
|
119
|
+
AW_MODE: 'home',
|
|
120
|
+
AW_PROMPTS_DIR: join(HOME, 'prompts'),
|
|
121
|
+
NUXT_SESSION_PASSWORD: 'paper-isolated-session-secret-not-for-evidence',
|
|
122
|
+
NO_PROXY: '127.0.0.1,localhost',
|
|
123
|
+
no_proxy: '127.0.0.1,localhost',
|
|
124
|
+
})
|
|
125
|
+
Object.assign(env, extra)
|
|
126
|
+
return env
|
|
127
|
+
}
|
|
128
|
+
function spawnLogged(command, args, { cwd = REPO, env = childEnv(), logFd, label }) {
|
|
129
|
+
const child = spawn(command, args, { cwd, env, stdio: ['ignore', logFd, logFd], windowsHide: true, detached: process.platform !== 'win32' })
|
|
130
|
+
child.label = label
|
|
131
|
+
children.add(child)
|
|
132
|
+
child.once('exit', () => children.delete(child))
|
|
133
|
+
child.once('error', () => children.delete(child))
|
|
134
|
+
return child
|
|
135
|
+
}
|
|
136
|
+
async function waitExit(child, ms = 8000) {
|
|
137
|
+
if (!child || child.exitCode !== null || child.signalCode !== null) return
|
|
138
|
+
await Promise.race([new Promise(resolvePromise => child.once('exit', resolvePromise)), delay(ms)])
|
|
139
|
+
}
|
|
140
|
+
function processGroupExists(pid) {
|
|
141
|
+
try {
|
|
142
|
+
process.kill(-pid, 0)
|
|
143
|
+
return true
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (error?.code === 'ESRCH') return false
|
|
147
|
+
if (error?.code === 'EPERM') return true
|
|
148
|
+
throw error
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function windowsProcessExists(pid) {
|
|
152
|
+
const result = spawnSync('tasklist.exe', ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'], { windowsHide: true, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], env: childEnv() })
|
|
153
|
+
if (result.error || result.status !== 0) return null
|
|
154
|
+
return (result.stdout ?? '').split(/\r?\n/).some(line => line.match(/^"(?:[^"]|"")*","(\d+)"(?:,|$)/)?.[1] === String(pid))
|
|
155
|
+
}
|
|
156
|
+
async function stopTree(child, label) {
|
|
157
|
+
if (!child?.pid) return false
|
|
158
|
+
try {
|
|
159
|
+
if (process.platform === 'win32') spawnSync('taskkill.exe', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore', env: childEnv() })
|
|
160
|
+
else process.kill(-child.pid, 'SIGTERM')
|
|
161
|
+
}
|
|
162
|
+
catch { /* process may already have exited */ }
|
|
163
|
+
await waitExit(child)
|
|
164
|
+
if (process.platform !== 'win32' && processGroupExists(child.pid)) {
|
|
165
|
+
try {
|
|
166
|
+
process.kill(-child.pid, 'SIGKILL')
|
|
167
|
+
}
|
|
168
|
+
catch { /* process may already have exited */ }
|
|
169
|
+
}
|
|
170
|
+
else if (process.platform === 'win32' && windowsProcessExists(child.pid) === true) {
|
|
171
|
+
spawnSync('taskkill.exe', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore', env: childEnv() })
|
|
172
|
+
}
|
|
173
|
+
await waitExit(child, 2000)
|
|
174
|
+
let gone
|
|
175
|
+
if (process.platform === 'win32') gone = windowsProcessExists(child.pid) === false
|
|
176
|
+
else {
|
|
177
|
+
const deadline = Date.now() + 3000
|
|
178
|
+
do {
|
|
179
|
+
gone = !processGroupExists(child.pid)
|
|
180
|
+
if (gone) break
|
|
181
|
+
await delay(100)
|
|
182
|
+
} while (Date.now() < deadline)
|
|
183
|
+
}
|
|
184
|
+
evidence.checks.push({ id: `${label}-stopped`, status: gone ? 'pass' : 'fail', detail: { pid: child.pid, treeStopped: gone } })
|
|
185
|
+
return gone
|
|
186
|
+
}
|
|
187
|
+
function configureHome() {
|
|
188
|
+
mkdirSync(join(HOME, 'data'), { recursive: true })
|
|
189
|
+
copyFileSync(join(REPO, 'config.yml'), join(HOME, 'config.yml'))
|
|
190
|
+
writeFileSync(join(HOME, 'runtime-settings.json'), JSON.stringify({ version: 1, updatedAt: new Date().toISOString(), overrides: {
|
|
191
|
+
'daq.startInfrastructure': 'never',
|
|
192
|
+
'daq.mqtt.host': '127.0.0.1', 'daq.mqtt.port': PORTS.appMqtt,
|
|
193
|
+
'daq.timescale.host': '127.0.0.1', 'daq.timescale.port': PORTS.tsdb,
|
|
194
|
+
'daq.objectstore.host': '127.0.0.1', 'daq.objectstore.port': PORTS.objectstore,
|
|
195
|
+
'plugins.rag-bridge.base_url': 'http://paper-direct-rest-outbound-disabled.invalid',
|
|
196
|
+
} }, null, 2) + '\n')
|
|
197
|
+
const kvDir = join(HOME, 'data', 'plugins', 'rag-bridge')
|
|
198
|
+
mkdirSync(kvDir, { recursive: true })
|
|
199
|
+
writeFileSync(join(kvDir, 'kv.json'), JSON.stringify({ 'kb.base_url': 'http://paper-direct-rest-outbound-disabled.invalid' }, null, 2) + '\n')
|
|
200
|
+
cpSync(join(REPO, '.AgentWorkShop', 'prompts'), join(HOME, 'prompts'), { recursive: true })
|
|
201
|
+
writeFileSync(join(HOME, 'empty.env'), '')
|
|
202
|
+
const builtin = pluginNames(join(REPO, 'server', 'plugins-builtin'))
|
|
203
|
+
const project = pluginNames(join(REPO, '.AgentWorkShop', 'plugins'))
|
|
204
|
+
const users = pluginNames(join(HOME, 'plugins'))
|
|
205
|
+
const all = [...new Set([...builtin, ...project, ...users])].sort()
|
|
206
|
+
const disabled = all.filter(name => name !== 'rag-bridge')
|
|
207
|
+
writeFileSync(join(HOME, 'plugins-state.json'), JSON.stringify({ version: 1, updatedAt: new Date().toISOString(), disabled }, null, 2) + '\n')
|
|
208
|
+
isolationCheck('isolated-home-settings', true, { homeMode: true, infrastructure: 'never', offlineServices: 'loopback only', promptAssetsCopied: true, pluginsDisabledExcept: 'outbound-disabled rag-bridge' })
|
|
209
|
+
}
|
|
210
|
+
function pluginNames(root) {
|
|
211
|
+
if (!existsSync(root)) return []
|
|
212
|
+
return readdirSync(root, { withFileTypes: true }).filter(entry => entry.isDirectory() && existsSync(join(root, entry.name, 'index.mjs'))).map(entry => entry.name).sort()
|
|
213
|
+
}
|
|
214
|
+
async function request(base, port, method, path, body, { token, timeoutMs = 10000, noTimeout = false } = {}) {
|
|
215
|
+
assertLoopback(base, port)
|
|
216
|
+
if (!path.startsWith('/api/')) throw new Error('API path boundary rejected')
|
|
217
|
+
const response = await fetch(`${base}${path}`, {
|
|
218
|
+
method,
|
|
219
|
+
headers: { ...(body === undefined ? {} : { 'content-type': 'application/json' }), ...(token ? { authorization: `Bearer ${token}` } : {}) },
|
|
220
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
221
|
+
...(noTimeout ? {} : { signal: AbortSignal.timeout(timeoutMs) }),
|
|
222
|
+
})
|
|
223
|
+
const parsed = await response.json().catch(() => null)
|
|
224
|
+
return { status: response.status, body: parsed, data: parsePayload(parsed) }
|
|
225
|
+
}
|
|
226
|
+
const app = (method, path, body, opts = {}) => request(APP_BASE, PORTS.app, method, path, body, { token: opts.token === undefined ? authToken : opts.token, timeoutMs: opts.timeoutMs, noTimeout: opts.noTimeout })
|
|
227
|
+
const sim = (method, path, body) => request(SIM_BASE, PORTS.sim, method, path, body, { timeoutMs: 8000 })
|
|
228
|
+
function success(result, label, expected = 200) {
|
|
229
|
+
if (result.status !== expected) throw new Error(`${label} returned HTTP ${result.status}: ${safeText(result.body?.message ?? result.body?.statusMessage ?? 'no response detail')}`)
|
|
230
|
+
return result.data
|
|
231
|
+
}
|
|
232
|
+
async function bootSimulator() {
|
|
233
|
+
mkdirSync(SIM_ROOT, { recursive: true })
|
|
234
|
+
cpSync(join(SIM_SOURCE, 'src'), join(SIM_ROOT, 'src'), { recursive: true })
|
|
235
|
+
copyFileSync(join(SIM_SOURCE, 'package.json'), join(SIM_ROOT, 'package.json'))
|
|
236
|
+
const entry = join(SIM_ROOT, 'src', 'server', 'index.ts')
|
|
237
|
+
const source = readFileSync(entry, 'utf8')
|
|
238
|
+
const listenCall = 'server.listen(PORT, () => resolve())'
|
|
239
|
+
const brokerCall = 'await startBroker(Number(process.env.MQTT_BROKER_PORT ?? 18830))'
|
|
240
|
+
if (source.split(listenCall).length !== 2 || source.split(brokerCall).length !== 2) throw new Error('simulator source does not match the known loopback-isolation patch points')
|
|
241
|
+
const patched = source.replace(listenCall, `server.listen(PORT, '127.0.0.1', () => {\n const a = server.address()\n if (!a || typeof a === 'string' || a.address !== '127.0.0.1' || a.port !== PORT) process.exit(70)\n console.log('[direct-paper-isolation] listener=' + a.address + ':' + a.port)\n resolve()\n })`).replace(brokerCall, `await startBroker(Number(process.env.MQTT_BROKER_PORT ?? 18830), '127.0.0.1')`)
|
|
242
|
+
writeFileSync(entry, patched)
|
|
243
|
+
symlinkSync(join(SIM_SOURCE, 'node_modules'), join(SIM_ROOT, 'node_modules'), process.platform === 'win32' ? 'junction' : 'dir')
|
|
244
|
+
simProcess = spawnLogged(process.execPath, [join(REPO, 'node_modules', 'tsx', 'dist', 'cli.mjs'), entry], { cwd: SIM_ROOT, env: childEnv({ SIM_PORT: String(PORTS.sim), MQTT_BROKER_PORT: String(PORTS.mqtt) }), logFd: simLogFd, label: 'simulator' })
|
|
245
|
+
for (let i = 0; i < 50; i++) {
|
|
246
|
+
try {
|
|
247
|
+
const result = await sim('GET', '/api/nodes')
|
|
248
|
+
if (result.status === 200) {
|
|
249
|
+
const log = readFileSync(SIM_LOG, 'utf8')
|
|
250
|
+
if (!log.includes(`[direct-paper-isolation] listener=127.0.0.1:${PORTS.sim}`) || !log.includes(`mqtt://127.0.0.1:${PORTS.mqtt}`)) throw new Error('simulator listener or broker was not verified as loopback-only')
|
|
251
|
+
isolationCheck('simulator-http-and-mqtt-loopback', true, { http: APP_BASE.replace(String(PORTS.app), String(PORTS.sim)), mqtt: `127.0.0.1:${PORTS.mqtt}` })
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (error) { if (String(error.message).includes('not verified')) throw error }
|
|
256
|
+
await delay(400)
|
|
257
|
+
}
|
|
258
|
+
throw new Error('simulator startup timed out')
|
|
259
|
+
}
|
|
260
|
+
async function startApplication() {
|
|
261
|
+
appProcess = spawnLogged(process.execPath, [NUXT_CLI, 'dev', '--host', '127.0.0.1', '--port', String(PORTS.app), '--dotenv', EMPTY_DOTENV], { cwd: REPO, env: childEnv(), logFd: appLogFd, label: 'agentworkshop' })
|
|
262
|
+
for (let i = 0; i < 90; i++) {
|
|
263
|
+
try {
|
|
264
|
+
const result = await app('GET', '/api/health', undefined, { token: undefined, timeoutMs: 2000 })
|
|
265
|
+
if (result.status === 200 && result.data?.status === 'ok' && result.data?.workshop === 'ready' && result.data?.version === VERSION) {
|
|
266
|
+
isolationCheck('application-loopback-ready', true, { version: result.data.version, host: '127.0.0.1', port: PORTS.app })
|
|
267
|
+
return result.data
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch { /* startup in progress */ }
|
|
271
|
+
await delay(500)
|
|
272
|
+
}
|
|
273
|
+
throw new Error('AgentWorkShop did not become ready')
|
|
274
|
+
}
|
|
275
|
+
function normalizeConfig(value, portMap, key = '') {
|
|
276
|
+
if (Array.isArray(value)) return value.map(item => normalizeConfig(item, portMap, key))
|
|
277
|
+
if (!value || typeof value !== 'object') {
|
|
278
|
+
if (typeof value !== 'string') return value
|
|
279
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(value)) {
|
|
280
|
+
const parsed = new URL(value)
|
|
281
|
+
if (!['127.0.0.1', 'localhost', '::1'].includes(parsed.hostname)) throw new Error('preset includes a non-loopback device URL')
|
|
282
|
+
if (parsed.port && portMap.has(Number(parsed.port))) parsed.port = String(portMap.get(Number(parsed.port)))
|
|
283
|
+
parsed.hostname = '127.0.0.1'
|
|
284
|
+
return parsed.toString()
|
|
285
|
+
}
|
|
286
|
+
if (/(?:host|hostname|ip|address)$/i.test(key)) return '127.0.0.1'
|
|
287
|
+
return value
|
|
288
|
+
}
|
|
289
|
+
const out = {}
|
|
290
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
291
|
+
if (/^port$/i.test(childKey) && Number.isInteger(childValue) && portMap.has(childValue)) out[childKey] = portMap.get(childValue)
|
|
292
|
+
else if (/(?:host|hostname|ip|address)$/i.test(childKey) && typeof childValue === 'string') out[childKey] = '127.0.0.1'
|
|
293
|
+
else out[childKey] = normalizeConfig(childValue, portMap, childKey)
|
|
294
|
+
}
|
|
295
|
+
return out
|
|
296
|
+
}
|
|
297
|
+
async function loadScene(scene, index) {
|
|
298
|
+
const preset = success(await sim('GET', `/api/presets/${encodeURIComponent(scene.preset)}`), `${scene.id} simulator preset`)
|
|
299
|
+
if (!preset?.plantModel || !Array.isArray(preset.nodes)) throw new Error(`${scene.id} simulator preset is malformed`)
|
|
300
|
+
let nextPort = 44400 + index * 100
|
|
301
|
+
const portMapByDeviceId = new Map()
|
|
302
|
+
const nodes = []
|
|
303
|
+
for (const raw of preset.nodes) {
|
|
304
|
+
const node = structuredClone(raw)
|
|
305
|
+
if ((node.protocol && /serial|rtu/i.test(String(node.protocol))) || /serialPort|comPort|devicePath/i.test(JSON.stringify(node.config ?? {}))) continue
|
|
306
|
+
node.config ||= {}
|
|
307
|
+
const oldPort = Number(node.config.port)
|
|
308
|
+
const portMap = new Map()
|
|
309
|
+
if (Number.isInteger(oldPort) && oldPort > 0) {
|
|
310
|
+
while (allocatedDevicePorts.includes(nextPort) || nextPort === PORTS.forbidden) nextPort++
|
|
311
|
+
await freePort(nextPort, `${scene.id}-device`)
|
|
312
|
+
portMap.set(oldPort, nextPort)
|
|
313
|
+
allocatedDevicePorts.push(nextPort)
|
|
314
|
+
node.config.port = nextPort++
|
|
315
|
+
}
|
|
316
|
+
node.config = normalizeConfig(node.config, portMap)
|
|
317
|
+
if (/mqtt/i.test(String(node.protocol))) node.config.brokerUrl = `mqtt://127.0.0.1:${PORTS.mqtt}`
|
|
318
|
+
portMapByDeviceId.set(node.id, portMap)
|
|
319
|
+
nodes.push(node)
|
|
320
|
+
}
|
|
321
|
+
if (!nodes.length) throw new Error(`${scene.id} preset has no supported devices`)
|
|
322
|
+
for (const node of nodes) success(await sim('POST', '/api/nodes', node), `${scene.id} create simulator device`)
|
|
323
|
+
const plant = { ...preset.plantModel, id: `${TAG}-${scene.id}`, seed: 42, timeScale: 1, truthExport: false }
|
|
324
|
+
success(await sim('PUT', '/api/plant/config', { plantModel: plant, warm: true, upsert: true }), `${scene.id} load plant`)
|
|
325
|
+
const exports = []
|
|
326
|
+
for (const node of nodes) {
|
|
327
|
+
const data = success(await sim('GET', `/api/nodes/${encodeURIComponent(node.id)}/export`), `${scene.id} export ${node.id}`)
|
|
328
|
+
for (const item of data?.items ?? []) exports.push({ ...item, deviceId: node.id })
|
|
329
|
+
}
|
|
330
|
+
for (const item of exports) item.driverConfig = normalizeConfig(item.driverConfig ?? {}, portMapByDeviceId.get(item.deviceId) ?? new Map())
|
|
331
|
+
const mv = exports.find(item => item.deviceId === scene.sourceDevice && item.signal === scene.mvSignal)
|
|
332
|
+
const cv = exports.find(item => item.signal === scene.cvSignal)
|
|
333
|
+
if (!mv || !cv || !['opcua', 'modbus-tcp'].includes(String(mv.driver))) throw new Error(`${scene.id} required signals or approved MV protocol missing`)
|
|
334
|
+
return { mv, cv, plant: { id: plant.id, kind: plant.kind ?? 'simulator', seed: 42, timeScale: 1 }, deviceCount: nodes.length }
|
|
335
|
+
}
|
|
336
|
+
async function setupScene(scene, simulator) {
|
|
337
|
+
const line = success(await app('POST', '/api/workshop/dcw/lines', { name: `${TAG}-${scene.id}`, description: 'Synthetic isolated direct-REST simulator experiment', simulated: true, simSceneId: scene.preset, controlMode: 'manual' }), `${scene.id} line`).line
|
|
338
|
+
const product = success(await app('POST', '/api/workshop/dcw/products', { name: `${TAG}-${scene.id}-product`, lineId: line.id }), `${scene.id} product`).product
|
|
339
|
+
const template = success(await app('POST', '/api/workshop/dcw/templates', { name: `${TAG}-${scene.id}-control-template`, unit: simulator.mv.unit, min: simulator.mv.min, max: simulator.mv.max, decimals: simulator.mv.decimals ?? 0 }), `${scene.id} template`).template
|
|
340
|
+
const control = success(await app('POST', '/api/workshop/dcw', { templateRef: template.key, name: `${TAG}-${scene.id}-control`, driver: simulator.mv.driver, driverConfig: simulator.mv.driverConfig, lineId: line.id, min: simulator.mv.min, max: simulator.mv.max, unit: simulator.mv.unit, decimals: simulator.mv.decimals ?? 0, stepLimit: 30, writeLockSeconds: 0, holdIntervalMs: 0, readIntervalMs: 500, semantics: `Bounded simulated ${scene.mvSignal} setpoint` }), `${scene.id} control node`).node
|
|
341
|
+
const daq = success(await app('POST', '/api/workshop/daq', { templateRef: 'daq-temp-tc', name: `${TAG}-${scene.id}-daq-cv`, driver: simulator.cv.driver, driverConfig: simulator.cv.driverConfig, lineId: line.id, min: simulator.cv.min, max: simulator.cv.max, unit: simulator.cv.unit, intervalMs: 1000, publishIntervalMs: 0, semantics: `Bounded simulated ${scene.cvSignal} sample` }), `${scene.id} DAQ node`).node
|
|
342
|
+
const recipe = success(await app('POST', '/api/workshop/dcw/recipes', { name: `${TAG}-${scene.id}-recipe`, productId: product.id, params: [{ nodeId: control.id, templateRef: template.key, value: scene.baseline, min: simulator.mv.min, max: simulator.mv.max, stepLimit: 30 }], daqWindows: [{ nodeId: daq.id, min: simulator.cv.min, max: simulator.cv.max }] }), `${scene.id} recipe`).recipe
|
|
343
|
+
const start = success(await app('POST', `/api/workshop/dcw/lines/${encodeURIComponent(line.id)}/start`, { recipeId: recipe.id }), `${scene.id} start line`)
|
|
344
|
+
const activeRunId = String(start.run?.id ?? '')
|
|
345
|
+
if (!activeRunId) throw new Error(`${scene.id} start response did not include run ID`)
|
|
346
|
+
const channel = success(await app('POST', '/api/workshop/channels', { name: `${TAG}-${scene.id}-direct-route`, description: 'Direct REST route test; no registered harness launched' }), `${scene.id} channel`)
|
|
347
|
+
const agent = success(await app('POST', `/api/workshop/channels/${encodeURIComponent(channel.channelId)}/agents`, { name: `${TAG}-${scene.id}-direct-agent`, harness: 'omp', role: 'worker', config: { command: process.execPath, cwd: HOME, args: ['--version'], rpcMode: 'rpc' } }), `${scene.id} worker identity`)
|
|
348
|
+
const recipeBinding = success(await app('POST', '/api/workshop/agent-tools/bindings', { agentId: agent.id, nodeId: recipe.id, kind: 'recipe' }), `${scene.id} recipe binding`).binding
|
|
349
|
+
const daqBinding = success(await app('POST', '/api/workshop/agent-tools/bindings', { agentId: agent.id, nodeId: daq.id, kind: 'daq', mode: 'auto' }), `${scene.id} DAQ binding`).binding
|
|
350
|
+
if (recipeBinding.mode !== 'manual' || daqBinding.kind !== 'daq') throw new Error(`${scene.id} route binding did not match manual recipe + DAQ observation policy`)
|
|
351
|
+
const candidate = scene.candidate
|
|
352
|
+
if (!Number.isFinite(candidate) || candidate < Number(simulator.mv.min) || candidate > Number(simulator.mv.max) || Math.abs(candidate - scene.baseline) > 30) throw new Error(`${scene.id} candidate violates exported limits or step bound`)
|
|
353
|
+
return { line, product, template, control, daq, recipe, channel, agent, activeRunId, candidate, simulator }
|
|
354
|
+
}
|
|
355
|
+
async function approveDirectInvocation(ctx, invocationPromise) {
|
|
356
|
+
const deadline = Date.now() + 30000
|
|
357
|
+
let approval
|
|
358
|
+
while (Date.now() < deadline) {
|
|
359
|
+
const result = await app('GET', `/api/workshop/agent-tools/approvals?agentId=${encodeURIComponent(ctx.agent.id)}&scope=pending`)
|
|
360
|
+
if (result.status === 200) {
|
|
361
|
+
const matches = (result.data?.approvals ?? []).filter(item => item.agentId === ctx.agent.id && item.nodeId === `recipe:${ctx.recipe.id}` && item.status === 'pending')
|
|
362
|
+
if (matches.length > 1) throw new Error(`${ctx.scene.id} invocation produced multiple pending recipe approvals`)
|
|
363
|
+
if (matches.length === 1) {
|
|
364
|
+
approval = matches[0]
|
|
365
|
+
break
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (invocationPromise.settled) throw new Error(`${ctx.scene.id} direct REST invocation settled before an approval was observed`)
|
|
369
|
+
await delay(300)
|
|
370
|
+
}
|
|
371
|
+
if (!approval) throw new Error(`${ctx.scene.id} direct REST recipe_trial approval was not observed within 30 seconds`)
|
|
372
|
+
ctx.evidence.approval = { id: approval.id, nodeId: approval.nodeId, statusBeforeDecision: approval.status, title: safeText(approval.title, 300), decision: 'approved by synthetic isolated administrator' }
|
|
373
|
+
const decision = await app('POST', `/api/workshop/agent-tools/approvals/${encodeURIComponent(approval.id)}/decide`, { approved: true, comment: 'Approved for one bounded direct-REST local simulator check.' })
|
|
374
|
+
success(decision, `${ctx.scene.id} approve pending recipe trial`)
|
|
375
|
+
const history = success(await app('GET', `/api/workshop/agent-tools/approvals?agentId=${encodeURIComponent(ctx.agent.id)}&scope=history`), `${ctx.scene.id} approval history`)
|
|
376
|
+
const finalApproval = (history.approvals ?? []).find(item => item.id === approval.id)
|
|
377
|
+
if (!finalApproval || finalApproval.status !== 'approved') throw new Error(`${ctx.scene.id} approval history did not confirm approval`)
|
|
378
|
+
ctx.evidence.approval.statusAfterDecision = finalApproval.status
|
|
379
|
+
ctx.evidence.approval.syntheticDecisionActor = 'first administrator in fresh isolated AW_HOME'
|
|
380
|
+
}
|
|
381
|
+
async function collectEvidence(ctx, invocation) {
|
|
382
|
+
const responseData = invocation.data
|
|
383
|
+
const toolResult = responseData?.result
|
|
384
|
+
const toolText = String(toolResult?.text ?? toolResult?.content?.[0]?.text ?? '')
|
|
385
|
+
const isError = toolResult?.isError === true
|
|
386
|
+
check(`${ctx.scene.id}-direct-rest-http-success`, invocation.status === 200, { status: invocation.status, endpoint: '/api/workshop/agent-tools/invoke' })
|
|
387
|
+
check(`${ctx.scene.id}-tool-result-success`, Boolean(toolResult) && !isError && /全部设备证实/.test(toolText), { present: Boolean(toolResult), isError, text: safeText(toolText, 800) })
|
|
388
|
+
ctx.evidence.invocation.httpStatus = invocation.status
|
|
389
|
+
ctx.evidence.invocation.toolResult = { present: Boolean(toolResult), isError, text: safeText(toolText, 1000) }
|
|
390
|
+
|
|
391
|
+
const readback = success(await app('POST', `/api/workshop/dcw/${encodeURIComponent(ctx.control.id)}/read`), `${ctx.scene.id} DCW readback`)
|
|
392
|
+
const read = readback?.outcome ?? readback?.read ?? readback
|
|
393
|
+
const readValue = Number(read?.value ?? read?.data?.value)
|
|
394
|
+
check(`${ctx.scene.id}-candidate-readback`, Number.isFinite(readValue) && Math.abs(readValue - ctx.candidate) <= 0.01, { candidate: ctx.candidate, observed: Number.isFinite(readValue) ? readValue : null })
|
|
395
|
+
|
|
396
|
+
const logResponse = success(await app('GET', `/api/workshop/ops-logs?scope=line&lineId=${encodeURIComponent(ctx.line.id)}&recipeId=${encodeURIComponent(ctx.recipe.id)}&limit=100`), `${ctx.scene.id} operation logs`)
|
|
397
|
+
const recipeRows = (logResponse?.logs ?? []).filter(row => String(row.action).startsWith('recipe.')).map((row) => {
|
|
398
|
+
let detail = row.detail
|
|
399
|
+
if (!detail || typeof detail !== 'object' || Array.isArray(detail)) {
|
|
400
|
+
try {
|
|
401
|
+
detail = JSON.parse(String(row.detailJson ?? '{}'))
|
|
402
|
+
}
|
|
403
|
+
catch { detail = {} }
|
|
404
|
+
}
|
|
405
|
+
return { ...row, detail }
|
|
406
|
+
})
|
|
407
|
+
const trialRows = recipeRows.filter(row => row.action === 'recipe.trial')
|
|
408
|
+
const ackRows = recipeRows.filter(row => row.action === 'recipe.dispatch.ack' && row.detail?.trial === true)
|
|
409
|
+
const trialRunId = String(ackRows[0]?.detail?.runId ?? '')
|
|
410
|
+
if (trialRows.length !== 1 || ackRows.length !== 1 || !trialRunId || String(trialRows[0]?.detail?.runId ?? '') !== trialRunId) throw new Error(`${ctx.scene.id} did not produce exactly one linked recipe.trial + ACK record`)
|
|
411
|
+
const trialRun = success(await app('GET', `/api/workshop/dcw/runs/${encodeURIComponent(trialRunId)}/data`), `${ctx.scene.id} trial run data`)
|
|
412
|
+
const run = trialRun?.run
|
|
413
|
+
const resultRow = run?.results?.[0]
|
|
414
|
+
const ack = ackRows[0].detail?.ackSummary
|
|
415
|
+
if (run?.id !== trialRunId || run?.results?.length !== 1 || resultRow?.ok !== true || resultRow?.verify?.verdict !== 'verified' || ack?.total !== 1 || ack?.verified !== 1 || ack?.unverified !== 0 || ack?.failed !== 0) {
|
|
416
|
+
throw new Error(`${ctx.scene.id} trial receipt or ACK was not independently verified`)
|
|
417
|
+
}
|
|
418
|
+
ctx.evidence.trial = { runId: trialRunId, linkedTrialAckRows: true, ackSummary: ack, result: resultRow }
|
|
419
|
+
ctx.evidence.readback = { expected: ctx.candidate, observed: readValue, matches: true }
|
|
420
|
+
ctx.evidence.recipeRecords = { trialCount: trialRows.length, trialAckCount: ackRows.length, trialRunId }
|
|
421
|
+
|
|
422
|
+
const activeRunData = success(await app('GET', `/api/workshop/dcw/runs/${encodeURIComponent(ctx.activeRunId)}/data`), `${ctx.scene.id} active run data`)
|
|
423
|
+
const waitStartedAt = new Date().toISOString()
|
|
424
|
+
await delay(20000)
|
|
425
|
+
const probeStartedAt = new Date().toISOString()
|
|
426
|
+
const probeResponse = success(await app('POST', `/api/workshop/daq/${encodeURIComponent(ctx.daq.id)}/test`), `${ctx.scene.id} DAQ driver probe`)
|
|
427
|
+
const probe = probeResponse?.test
|
|
428
|
+
const sampleValue = Number(probe?.sampleValue)
|
|
429
|
+
if (probe?.ok !== true || !Number.isFinite(sampleValue)) throw new Error(`${ctx.scene.id} DAQ probe returned no finite sample`)
|
|
430
|
+
const ingestedAt = new Date().toISOString()
|
|
431
|
+
const ingest = success(await app('POST', `/api/workshop/daq/${encodeURIComponent(ctx.daq.id)}/ingest`, { at: ingestedAt, value: sampleValue, state: 'ok' }), `${ctx.scene.id} DAQ snapshot ingest`)
|
|
432
|
+
if (ingest?.accepted !== true || ingest.nodeId !== ctx.daq.id || ingest.lineId !== ctx.line.id) throw new Error(`${ctx.scene.id} DAQ snapshot was not accepted by the expected node and line`)
|
|
433
|
+
await delay(800)
|
|
434
|
+
const atMs = Date.parse(String(ingest.at ?? ingestedAt))
|
|
435
|
+
let taggedQuery
|
|
436
|
+
let point
|
|
437
|
+
const queryDeadline = Date.now() + 12000
|
|
438
|
+
while (Date.now() < queryDeadline) {
|
|
439
|
+
const query = await app('GET', `/api/workshop/daq/query?line_id=${encodeURIComponent(ctx.line.id)}&node_ids=${encodeURIComponent(ctx.daq.id)}&run_id=${encodeURIComponent(ctx.activeRunId)}&from=${atMs}&to=${atMs + 1}&bucket_ms=1000`)
|
|
440
|
+
if (query.status === 200) {
|
|
441
|
+
const series = query.data?.series?.find(row => row.nodeId === ctx.daq.id)
|
|
442
|
+
const bucketMs = Number(query.data?.window?.bucketMs) || 1000
|
|
443
|
+
const bucketAt = Math.floor(atMs / bucketMs) * bucketMs
|
|
444
|
+
point = series?.points?.find(row => Number(row.at) === bucketAt && Math.abs(Number(row.v) - sampleValue) <= 0.01)
|
|
445
|
+
if (point && query.data?.activeRun?.runId === ctx.activeRunId) {
|
|
446
|
+
taggedQuery = query.data
|
|
447
|
+
break
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
await delay(300)
|
|
451
|
+
}
|
|
452
|
+
if (!taggedQuery || !point) throw new Error(`${ctx.scene.id} DAQ readback was not linked to its active run`)
|
|
453
|
+
const target = { min: ctx.scene.targetMin, max: ctx.scene.targetMax }
|
|
454
|
+
ctx.evidence.observation = {
|
|
455
|
+
method: 'one driver probe, accepted into the isolated time-series store, then queried by active run ID',
|
|
456
|
+
postTrialDelaySeconds: 20,
|
|
457
|
+
waitStartedAt,
|
|
458
|
+
probeStartedAt,
|
|
459
|
+
probeCompletedAt: new Date().toISOString(),
|
|
460
|
+
driver: ctx.daq.driver,
|
|
461
|
+
value: sampleValue,
|
|
462
|
+
unit: ctx.simulator.cv.unit,
|
|
463
|
+
targetBand: target,
|
|
464
|
+
withinTargetBand: sampleValue >= target.min && sampleValue <= target.max,
|
|
465
|
+
acceptedIngest: true,
|
|
466
|
+
queryRunId: taggedQuery.activeRun?.runId,
|
|
467
|
+
matchingPoint: point,
|
|
468
|
+
}
|
|
469
|
+
ctx.evidence.activeRunDataKeys = Object.keys(activeRunData ?? {})
|
|
470
|
+
ctx.evidence.status = 'verified'
|
|
471
|
+
const stopped = success(await app('POST', `/api/workshop/dcw/lines/${encodeURIComponent(ctx.line.id)}/stop`), `${ctx.scene.id} stop line`)
|
|
472
|
+
ctx.evidence.activeRun.endedAt = stopped?.run?.endedAt ?? null
|
|
473
|
+
}
|
|
474
|
+
async function runScene(scene, index) {
|
|
475
|
+
stage = `${scene.id}:simulator-setup`
|
|
476
|
+
sceneId = scene.id
|
|
477
|
+
const simulator = await loadScene(scene, index)
|
|
478
|
+
stage = `${scene.id}:application-setup`
|
|
479
|
+
const ctx = await setupScene(scene, simulator)
|
|
480
|
+
ctx.scene = scene
|
|
481
|
+
ctx.evidence = {
|
|
482
|
+
id: scene.id,
|
|
483
|
+
status: 'running',
|
|
484
|
+
preset: scene.preset,
|
|
485
|
+
process: scene.process,
|
|
486
|
+
entities: { lineId: ctx.line.id, productId: ctx.product.id, recipeId: ctx.recipe.id, controlNodeId: ctx.control.id, daqNodeId: ctx.daq.id, channelId: ctx.channel.channelId, agentId: ctx.agent.id },
|
|
487
|
+
bindings: { recipe: 'manual', daq: 'auto' },
|
|
488
|
+
protocolMapping: { manipulationDevice: ctx.simulator.mv.deviceId, manipulationSignal: ctx.simulator.mv.signal, manipulationProtocol: ctx.simulator.mv.driver, observationDevice: ctx.simulator.cv.deviceId, observationSignal: ctx.simulator.cv.signal, observationProtocol: ctx.simulator.cv.driver, candidate: ctx.candidate, baseline: scene.baseline },
|
|
489
|
+
activeRun: { id: ctx.activeRunId, recipeId: ctx.recipe.id },
|
|
490
|
+
invocation: {
|
|
491
|
+
endpoint: '/api/workshop/agent-tools/invoke',
|
|
492
|
+
method: 'POST',
|
|
493
|
+
auth: 'synthetic administrator bearer; secret omitted',
|
|
494
|
+
body: { agentId: ctx.agent.id, tool: 'recipe_trial', args: { recipe_id: ctx.recipe.id, params: [{ node_id: ctx.control.id, value: ctx.candidate }], hypothesis: `Bounded single-candidate direct-route check for ${scene.id} simulator mapping.` } },
|
|
495
|
+
retryCount: 0,
|
|
496
|
+
},
|
|
497
|
+
}
|
|
498
|
+
evidence.experiment.scenes.push(ctx.evidence)
|
|
499
|
+
stage = `${scene.id}:direct-rest-invocation`
|
|
500
|
+
const invokePromise = app('POST', '/api/workshop/agent-tools/invoke', ctx.evidence.invocation.body, { noTimeout: true })
|
|
501
|
+
invokePromise.settled = false
|
|
502
|
+
const markSettled = invokePromise.then(
|
|
503
|
+
() => { invokePromise.settled = true },
|
|
504
|
+
() => { invokePromise.settled = true },
|
|
505
|
+
)
|
|
506
|
+
ctx.evidence.invocation.startedAt = new Date().toISOString()
|
|
507
|
+
await approveDirectInvocation(ctx, invokePromise)
|
|
508
|
+
stage = `${scene.id}:direct-rest-result-and-snapshot`
|
|
509
|
+
const timeout = delay(45000).then(() => {
|
|
510
|
+
throw new Error(`${scene.id} direct invocation did not return within 45 seconds after approval`)
|
|
511
|
+
})
|
|
512
|
+
const invocation = await Promise.race([invokePromise, timeout])
|
|
513
|
+
await markSettled
|
|
514
|
+
ctx.evidence.invocation.completedAt = new Date().toISOString()
|
|
515
|
+
await collectEvidence(ctx, invocation)
|
|
516
|
+
}
|
|
517
|
+
async function main() {
|
|
518
|
+
mkdirSync(join(REPO, '.omc'), { recursive: true })
|
|
519
|
+
await preflight()
|
|
520
|
+
mkdirSync(join(HOME, 'data'), { recursive: true })
|
|
521
|
+
configureHome()
|
|
522
|
+
stage = 'simulator-startup'
|
|
523
|
+
await bootSimulator()
|
|
524
|
+
stage = 'application-startup'
|
|
525
|
+
const health = await startApplication()
|
|
526
|
+
evidence.experiment.server = { version: health.version, mode: health.mode, workshop: health.workshop }
|
|
527
|
+
const first = await app('POST', '/api/users/register', { name: `Paper Direct ${TAG}`, email: `${TAG}@example.invalid`, password: `Paper-${Date.now()}-safe9` }, { token: undefined })
|
|
528
|
+
const admin = success(first, 'synthetic administrator registration')
|
|
529
|
+
if (!admin?.token || admin?.user?.role !== 'admin') throw new Error('fresh isolated home did not create its first administrator')
|
|
530
|
+
authToken = String(admin.token)
|
|
531
|
+
syntheticAdminId = String(admin.user.id)
|
|
532
|
+
evidence.experiment.syntheticAdmin = { id: syntheticAdminId, role: admin.user.role, source: 'first registration in fresh isolated AW_HOME' }
|
|
533
|
+
const plugins = success(await app('GET', '/api/workshop/plugins'), 'plugin isolation manifest')
|
|
534
|
+
const enabled = (plugins.plugins ?? []).filter(plugin => plugin.enabled).map(plugin => plugin.name).sort()
|
|
535
|
+
if (enabled.join(',') !== 'rag-bridge') throw new Error(`isolated plugin allowlist mismatch: ${enabled.join(',')}`)
|
|
536
|
+
isolationCheck('only-outbound-disabled-rag-bridge-enabled', true, { enabled, outboundGuard: 'invalid non-routable host configured' })
|
|
537
|
+
const bridgeHealth = success(await app('GET', '/api/plugins/rag-bridge/health'), 'outbound plugin guard')
|
|
538
|
+
if (bridgeHealth.outbound !== false) throw new Error('rag-bridge outbound calls are not disabled')
|
|
539
|
+
isolationCheck('external-rag-outbound-disabled', true, { outbound: bridgeHealth.outbound })
|
|
540
|
+
const daqStatus = success(await app('GET', '/api/workshop/daq', undefined, { timeoutMs: 45000 }), 'DAQ isolated backend status')
|
|
541
|
+
if (daqStatus.meta?.queue !== 'inproc' || daqStatus.meta?.tsdb !== 'sqlite-emulated') throw new Error('DAQ did not use isolated in-process queue and SQLite fallback')
|
|
542
|
+
check('isolated-daq-backends', true, { queue: daqStatus.meta.queue, tsdb: daqStatus.meta.tsdb })
|
|
543
|
+
evidence.isolation.result = 'passed'
|
|
544
|
+
evidence.experiment.sourceIdentity = {
|
|
545
|
+
gitHead: spawnSync('git', ['rev-parse', 'HEAD'], { cwd: REPO, encoding: 'utf8', windowsHide: true }).stdout.trim(),
|
|
546
|
+
appTreeSha256: spawnSync('git', ['diff', '--binary', 'HEAD', '--', 'server', 'shared', 'app', 'config.yml'], { cwd: REPO, encoding: 'buffer', windowsHide: true }).stdout ? createHash('sha256').update(spawnSync('git', ['diff', '--binary', 'HEAD', '--', 'server', 'shared', 'app', 'config.yml'], { cwd: REPO, encoding: 'buffer', windowsHide: true }).stdout).digest('hex') : null,
|
|
547
|
+
simulatorCommit: spawnSync('git', ['rev-parse', 'HEAD'], { cwd: SIM_SOURCE, encoding: 'utf8', windowsHide: true }).stdout.trim(),
|
|
548
|
+
scriptSha256: createHash('sha256').update(readFileSync(fileURLToPath(import.meta.url))).digest('hex'),
|
|
549
|
+
}
|
|
550
|
+
const scenes = [
|
|
551
|
+
{ id: 'cast-film', preset: 'cast-film-physics', sourceDevice: 'dev-extruder-opcua', mvSignal: 'ScrewSpeedSP', cvSignal: 'thick', baseline: 120, candidate: 125, targetMin: 48, targetMax: 52, process: 'cast-film extrusion' },
|
|
552
|
+
{ id: 'annealing', preset: 'anneal-line', sourceDevice: 'anneal-heating-mbtcp', mvSignal: '均热区3炉温SP', cvSignal: 'StripTemp', baseline: 730, candidate: 735, targetMin: 700, targetMax: 760, process: 'continuous annealing' },
|
|
553
|
+
]
|
|
554
|
+
for (const [index, scene] of scenes.entries()) await runScene(scene, index)
|
|
555
|
+
evidence.status = 'passed'
|
|
556
|
+
}
|
|
557
|
+
async function cleanup() {
|
|
558
|
+
let ok = true
|
|
559
|
+
for (const [child, label] of [[appProcess, 'agentworkshop'], [simProcess, 'simulator']].reverse()) {
|
|
560
|
+
if (!child) continue
|
|
561
|
+
const stopped = await stopTree(child, label).catch(() => false)
|
|
562
|
+
ok = ok && stopped
|
|
563
|
+
}
|
|
564
|
+
for (const [label, port] of [['app', PORTS.app], ['simulator-http', PORTS.sim], ['simulator-mqtt', PORTS.mqtt], ['app-mqtt', PORTS.appMqtt], ['timeseries', PORTS.tsdb], ['objectstore', PORTS.objectstore], ...allocatedDevicePorts.map((port, i) => [`simulator-device-${i}`, port])]) {
|
|
565
|
+
try {
|
|
566
|
+
await freePort(port, label)
|
|
567
|
+
evidence.checks.push({ id: `${label}-port-released`, status: 'pass', detail: { port } })
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
ok = false
|
|
571
|
+
evidence.checks.push({ id: `${label}-port-released`, status: 'fail', detail: { port, error: safeText(error.message) } })
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
closeSync(appLogFd)
|
|
576
|
+
}
|
|
577
|
+
catch { /* already closed */ }
|
|
578
|
+
try {
|
|
579
|
+
closeSync(simLogFd)
|
|
580
|
+
}
|
|
581
|
+
catch { /* already closed */ }
|
|
582
|
+
return ok
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
await main()
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
evidence.status = 'failed'
|
|
590
|
+
evidence.failure = { stage, scene: sceneId, name: error?.name ?? 'Error', message: safeText(error?.message ?? error) }
|
|
591
|
+
if (evidence.isolation.result === 'pending') evidence.isolation.result = stage === 'preflight' ? 'failed' : 'incomplete'
|
|
592
|
+
}
|
|
593
|
+
finally {
|
|
594
|
+
const cleanupOk = await cleanup()
|
|
595
|
+
if (!cleanupOk) {
|
|
596
|
+
evidence.status = 'failed'
|
|
597
|
+
evidence.isolation.result = 'failed'
|
|
598
|
+
evidence.failure ??= { stage: 'cleanup', message: 'process stop or port-release verification failed' }
|
|
599
|
+
}
|
|
600
|
+
if (evidence.status === 'passed' && evidence.checks.some(item => item.status !== 'pass')) {
|
|
601
|
+
evidence.status = 'failed'
|
|
602
|
+
evidence.isolation.result = 'failed'
|
|
603
|
+
}
|
|
604
|
+
evidence.cleanup = { status: cleanupOk ? 'passed' : 'failed', requiredProcessesAndPortsChecked: true }
|
|
605
|
+
evidence.experiment.finishedAt = new Date().toISOString()
|
|
606
|
+
evidence.experiment.outputDirectory = 'direct-rest-trial-2026-10-25/run-*'
|
|
607
|
+
writeFileSync(join(OUTPUT, 'evidence.json'), JSON.stringify(evidence, null, 2) + '\n')
|
|
608
|
+
const readme = [
|
|
609
|
+
'# Direct REST simulator route evidence',
|
|
610
|
+
'',
|
|
611
|
+
`- Run: ${TAG}`,
|
|
612
|
+
'- Scope: two local simulator scenes; one manual approval and one direct REST recipe_trial invocation per scene.',
|
|
613
|
+
'- Route: POST /api/workshop/agent-tools/invoke; the worker identity only supplies platform agent scope. No registered harness transport, LLM decision, or physical PLC was used.',
|
|
614
|
+
'- Data: evidence.json; service logs: agentworkshop.log and simulator.log.',
|
|
615
|
+
'- Reproduce from the repository root with: node scripts/e2e-direct-rest-simulator-paper.mjs.',
|
|
616
|
+
'- Interpretation: route and protocol mapping coverage only; the post-trial DAQ result is a single run-tagged snapshot, not process-attainment or dynamic-response evidence.',
|
|
617
|
+
'',
|
|
618
|
+
].join('\n')
|
|
619
|
+
writeFileSync(join(OUTPUT, 'README.md'), readme)
|
|
620
|
+
console.log(`DIRECT_REST_E2E=${evidence.status}`)
|
|
621
|
+
console.log(`EVIDENCE=${join(OUTPUT, 'evidence.json')}`)
|
|
622
|
+
console.log(`STAGE=${evidence.failure?.stage ?? 'complete'}`)
|
|
623
|
+
}
|
|
624
|
+
if (evidence.status !== 'passed') process.exitCode = 1
|