agentworkshop 0.7.7 → 0.7.9
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 +1 -1
- package/.output/.AgentWorkShop/prompts/host-tools.json +1 -1
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/-6OnbLFb.js +1 -0
- package/.output/public/_nuxt/{BUxYN2mw.js → -nb2uqVg.js} +1 -1
- package/.output/public/_nuxt/{DM8DS9MA.js → 6PZPfFcO.js} +1 -1
- package/.output/public/_nuxt/91DZTUMJ.js +1 -0
- package/.output/public/_nuxt/B0HpdCKS.js +1 -0
- package/.output/public/_nuxt/B1g8pJmr.js +2 -0
- package/.output/public/_nuxt/B1kA8LhC.js +14 -0
- package/.output/public/_nuxt/{uBbDCfTz.js → B5S_UyaC.js} +1 -1
- package/.output/public/_nuxt/{DZk24ego.js → B7wL3pDk.js} +1 -1
- package/.output/public/_nuxt/B9MUiDFl.js +1 -0
- package/.output/public/_nuxt/B9dLFSYI.js +1 -0
- package/.output/public/_nuxt/BC8s1Guv.js +1 -0
- package/.output/public/_nuxt/{BSXx2vqR.js → BCJFMBQ6.js} +1 -1
- package/.output/public/_nuxt/{BYL0Rjqj.js → BDASpD_i.js} +3 -3
- package/.output/public/_nuxt/{VnIWJ0ts.js → BN_PdN3J.js} +1 -1
- package/.output/public/_nuxt/BUPj10t4.js +1 -0
- package/.output/public/_nuxt/BZGNTgTQ2.js +1 -0
- package/.output/public/_nuxt/{NIM0Wglj2.js → BaMkWl5i2.js} +1 -1
- package/.output/public/_nuxt/BmHvC_OU.js +1 -0
- package/.output/public/_nuxt/{BkfchkK62.js → Bp5luxkg2.js} +1 -1
- package/.output/public/_nuxt/{ZugRk0CM2.js → BuktKVqK2.js} +1 -1
- package/.output/public/_nuxt/By8BpbKH.js +1 -0
- package/.output/public/_nuxt/{pRQri7vQ2.js → BytVi9VX2.js} +1 -1
- package/.output/public/_nuxt/{D5KJ7_0V.js → C3tCneTM.js} +1 -1
- package/.output/public/_nuxt/{CblLEiCZ.js → C9cU_Ujd.js} +3 -3
- package/.output/public/_nuxt/{BJ-fxrSw.js → CBciOiTQ.js} +1 -1
- package/.output/public/_nuxt/CFn4vHwD.js +1 -0
- package/.output/public/_nuxt/{DCXIGxzT.js → CKfhtOt7.js} +2 -2
- package/.output/public/_nuxt/CUMdhxg9.js +1 -0
- package/.output/public/_nuxt/{ChEKFMxj.js → CUXCQXjG.js} +1 -1
- package/.output/public/_nuxt/CfVAr2nf.js +1 -0
- package/.output/public/_nuxt/{CvbHIGQY2.js → CgvGu2IO.js} +1 -1
- package/.output/public/_nuxt/CjDQPXfv.js +1 -0
- package/.output/public/_nuxt/{B4Fia4FB.js → CmHzfVc5.js} +1 -1
- package/.output/public/_nuxt/{Do88WZG2.js → Cn3J3NXR.js} +1 -1
- package/.output/public/_nuxt/{DNzaswei.js → Cst47G4-.js} +1 -1
- package/.output/public/_nuxt/{DYZBdSSg2.js → Ct68Z0232.js} +1 -1
- package/.output/public/_nuxt/{BsMpJf7S.js → D-Ca4BGY.js} +6 -6
- package/.output/public/_nuxt/D0Szyn_D.js +1 -0
- package/.output/public/_nuxt/{CsCpu2Zh.js → D1z7i2UP.js} +1 -1
- package/.output/public/_nuxt/{Du-eSpo2.js → D3VJ3lex.js} +1 -1
- package/.output/public/_nuxt/D8CqF0yy.js +1 -0
- package/.output/public/_nuxt/D8EfUxO2.js +1 -0
- package/.output/public/_nuxt/D8wyHvcz2.js +1 -0
- package/.output/public/_nuxt/{zwdqvGN_.js → DDdEnkdw.js} +1 -1
- package/.output/public/_nuxt/DM4CNkXz.js +1 -0
- package/.output/public/_nuxt/DN5RuZ8k.js +18 -0
- package/.output/public/_nuxt/DXRZPxGs.js +1 -0
- package/.output/public/_nuxt/{C2iWjzEC2.js → DoLasgPj2.js} +1 -1
- package/.output/public/_nuxt/{DRq09ZmT.js → DpojD719.js} +1 -1
- package/.output/public/_nuxt/Dqj2h40U.js +1 -0
- package/.output/public/_nuxt/{BCipft_k2.js → DsUUYP292.js} +1 -1
- package/.output/public/_nuxt/DtBu71ev.js +32 -0
- package/.output/public/_nuxt/{fQUs58kX2.js → DvgKXPgu2.js} +1 -1
- package/.output/public/_nuxt/{C_gFA2H_2.js → DxrgFxIr2.js} +1 -1
- package/.output/public/_nuxt/Dy-mxvXY2.js +1 -0
- package/.output/public/_nuxt/G0qIw7Gq2.js +1 -0
- package/.output/public/_nuxt/{DCEyMaN-.js → MyA0Oeqd.js} +3 -3
- package/.output/public/_nuxt/{tRBT-plo.js → OV6PBW2z.js} +1 -1
- package/.output/public/_nuxt/TownView.BHLKAjEu.css +1 -0
- package/.output/public/_nuxt/VLI2Zy4t.js +5 -0
- package/.output/public/_nuxt/_id_.DIxEKj88.css +1 -0
- package/.output/public/_nuxt/_id_.DTvjd6VN.css +1 -0
- package/.output/public/_nuxt/{_wsId_.COUzMXpP.css → _wsId_.BFyCuUwS.css} +1 -1
- package/.output/public/_nuxt/agents.CEbox5-D.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/92d6bd39-dbc5-46d5-90d8-8387f36715fa.json +1 -0
- package/.output/public/_nuxt/channel-templates.De5ZvrGz.css +1 -0
- package/.output/public/_nuxt/daq.6IrhVVfy.css +1 -0
- package/.output/public/_nuxt/dcw.DcDjwvqv.css +1 -0
- package/.output/public/_nuxt/{default.V3_hUJq1.css → default.BR3e5fPY.css} +1 -1
- package/.output/public/_nuxt/{entry.DvlXlPwx.css → entry.5qf6EqfN.css} +1 -1
- package/.output/public/_nuxt/{Cyk3du4K2.js → kZV5TQvh2.js} +1 -1
- package/.output/public/_nuxt/logs.BO-BSyjm.css +1 -0
- package/.output/public/_nuxt/{D4qXvjuV.js → niDwBOZW.js} +2 -2
- package/.output/public/_nuxt/pKv_VWR4.js +1 -0
- package/.output/public/_nuxt/plugins.Clsky_kl.css +1 -0
- package/.output/public/_nuxt/rqZ-1SoP.js +2 -0
- package/.output/public/_nuxt/teams.CUMnbQGf.css +1 -0
- package/.output/public/_nuxt/tokens.BPLe9ioQ.css +1 -0
- package/.output/public/_nuxt/{C8dxXc6K.js → u7KWCGSb.js} +1 -1
- package/.output/public/_nuxt/{Bi6AiCuP.js → vJ4FPb-_.js} +1 -1
- package/.output/public/_nuxt/wirVyscc.js +1 -0
- package/.output/public/_nuxt/workshop.BIdoGfoC.css +1 -0
- package/.output/server/chunks/_/nitro.mjs +2042 -1967
- package/.output/server/chunks/_/nitro.mjs.map +1 -1
- package/.output/server/chunks/build/{TownView-B5KifIaT.mjs → TownView-D8DSdO3H.mjs} +182 -181
- package/.output/server/chunks/build/{TownView-B5KifIaT.mjs.map → TownView-D8DSdO3H.mjs.map} +1 -1
- package/.output/server/chunks/build/{_id_-Ct4Jhvl1.mjs → _id_-BV4FfiYd.mjs} +37 -36
- package/.output/server/chunks/build/_id_-BV4FfiYd.mjs.map +1 -0
- package/.output/server/chunks/build/{_id_-C-ewLgfl.mjs → _id_-TP9hvnBM.mjs} +126 -125
- package/.output/server/chunks/build/_id_-TP9hvnBM.mjs.map +1 -0
- package/.output/server/chunks/build/{_wsId_-CanI_6Su.mjs → _wsId_-Mm9sQSz2.mjs} +125 -124
- package/.output/server/chunks/build/{_wsId_-CanI_6Su.mjs.map → _wsId_-Mm9sQSz2.mjs.map} +1 -1
- package/.output/server/chunks/build/{agents-DSOptxXA.mjs → agents-To3a5xcZ.mjs} +16 -15
- package/.output/server/chunks/build/agents-To3a5xcZ.mjs.map +1 -0
- package/.output/server/chunks/build/api-error-dul30BYQ.mjs +47 -0
- package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -0
- package/.output/server/chunks/build/{channel-templates-BK0KuPOd.mjs → channel-templates-DRsP-qxl.mjs} +23 -22
- package/.output/server/chunks/build/channel-templates-DRsP-qxl.mjs.map +1 -0
- package/.output/server/chunks/build/{daq-D9RA_uze.mjs → daq-_hCEaYLd.mjs} +112 -111
- package/.output/server/chunks/build/daq-_hCEaYLd.mjs.map +1 -0
- package/.output/server/chunks/build/{dcw-BTM5YU-B.mjs → dcw-C7pJ4Y0p.mjs} +47 -46
- package/.output/server/chunks/build/dcw-C7pJ4Y0p.mjs.map +1 -0
- package/.output/server/chunks/build/{default-BfWGTlbj.mjs → default-Dnjs36e8.mjs} +16 -15
- package/.output/server/chunks/build/{default-BfWGTlbj.mjs.map → default-Dnjs36e8.mjs.map} +1 -1
- package/.output/server/chunks/build/{logs-Dyk0I2I9.mjs → logs-C039TFW1.mjs} +38 -37
- package/.output/server/chunks/build/logs-C039TFW1.mjs.map +1 -0
- package/.output/server/chunks/build/{pages-DR2bzczM.mjs → pages-C_VSGSGo.mjs} +5 -4
- package/.output/server/chunks/build/pages-C_VSGSGo.mjs.map +1 -0
- package/.output/server/chunks/build/{plugins-DnOBIWGg.mjs → plugins-4FMWkN8d.mjs} +25 -22
- package/.output/server/chunks/build/plugins-4FMWkN8d.mjs.map +1 -0
- package/.output/server/chunks/build/table-Cswq-l2r.mjs +1 -1
- package/.output/server/chunks/build/{teams-BhSKBBBM.mjs → teams-DA1lkf1n.mjs} +19 -18
- package/.output/server/chunks/build/teams-DA1lkf1n.mjs.map +1 -0
- package/.output/server/chunks/build/{tokens-D_eyxmcv.mjs → tokens-DKMvvk3e.mjs} +18 -17
- package/.output/server/chunks/build/tokens-DKMvvk3e.mjs.map +1 -0
- package/.output/server/chunks/build/{town-DPtOPRRO.mjs → town-D0thZ7PK.mjs} +6 -5
- package/.output/server/chunks/build/town-D0thZ7PK.mjs.map +1 -0
- package/.output/server/chunks/build/{useDaqStream-hwkhJHFU.mjs → useDaqStream-BVTRw8_H.mjs} +5 -4
- package/.output/server/chunks/build/useDaqStream-BVTRw8_H.mjs.map +1 -0
- package/.output/server/chunks/build/{useDcwStream-Bdew_yrr.mjs → useDcwStream-DNsivqI_.mjs} +5 -4
- package/.output/server/chunks/build/useDcwStream-DNsivqI_.mjs.map +1 -0
- package/.output/server/chunks/build/{useDeviceTwins-C6EBg9oN.mjs → useDeviceTwins-WeIom7A6.mjs} +4 -3
- package/.output/server/chunks/build/useDeviceTwins-WeIom7A6.mjs.map +1 -0
- package/.output/server/chunks/build/useLocalTime-9a4B-aHv.mjs +1 -1
- package/.output/server/chunks/build/{useOpsLog-DdCqKVVG.mjs → useOpsLog-DcKnPO43.mjs} +5 -4
- package/.output/server/chunks/build/useOpsLog-DcKnPO43.mjs.map +1 -0
- package/.output/server/chunks/build/useWorkshopApi-D2gq06N2.mjs +1 -1
- package/.output/server/chunks/build/{workshop-iCzNRifn.mjs → workshop-BhWnJMWs.mjs} +50 -43
- package/.output/server/chunks/build/workshop-BhWnJMWs.mjs.map +1 -0
- package/.output/server/chunks/routes/api/users/setup-status.get.mjs +25 -0
- package/.output/server/chunks/routes/api/users/setup-status.get.mjs.map +1 -0
- package/.output/server/chunks/routes/api/workshop/caller.mjs +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 +1 -1
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +3 -2
- package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs.map +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/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/index4.post.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/lines/_id_.patch.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/dcw/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/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/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/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/index6.post.mjs +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +3 -3
- package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs.map +1 -1
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +3 -3
- package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs.map +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 +2 -2
- package/.output/server/chunks/virtual/entry.mjs +57 -23
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/package.json +1 -1
- package/README-zh.md +27 -7
- package/README.md +455 -435
- package/app/components/AppHeader.vue +1 -1
- package/app/components/workshop/A2aRpcDebugger.vue +1 -1
- package/app/components/workshop/AgentInspectorDrawer.vue +3 -3
- package/app/components/workshop/AgentLanesView.vue +6 -6
- package/app/components/workshop/AssetLibrary.vue +2 -2
- package/app/components/workshop/ChannelSessionList.vue +5 -5
- package/app/components/workshop/Composer.vue +1 -1
- package/app/components/workshop/DeviceTwinPanel.vue +2 -2
- package/app/components/workshop/TaskBoardView.vue +1 -1
- package/app/components/workshop/TaskInspectorDrawer.vue +2 -2
- package/app/components/workshop/town/TownView.vue +23 -23
- package/app/composables/workshop/apiClient.ts +1 -1
- package/app/composables/workshop/useCharacterAssets.ts +1 -1
- package/app/composables/workshop/useDaqStream.ts +1 -1
- package/app/composables/workshop/useDcwStream.ts +1 -1
- package/app/composables/workshop/useDeviceTwins.ts +1 -1
- package/app/composables/workshop/useOpsLog.ts +2 -2
- package/app/composables/workshop/useSceneLayouts.ts +1 -1
- package/app/pages/daq/[id].vue +1 -1
- package/app/pages/daq/index.vue +8 -8
- package/app/pages/dcw/[id].vue +13 -13
- package/app/pages/dcw/index.vue +7 -7
- package/app/pages/logs/index.vue +1 -1
- package/app/pages/plugins/index.vue +14 -6
- package/app/pages/tokens.vue +3 -3
- package/app/pages/workshop/agents.vue +2 -2
- package/app/pages/workshop/channel-templates.vue +3 -3
- package/app/pages/workshop/index.vue +29 -10
- package/app/pages/workshop/teams.vue +3 -3
- package/app/utils/api-error.ts +75 -0
- package/cli/commands/plugin.mjs +2 -2
- package/config.yml +11 -1
- package/i18n/dicts/settings.json +4 -0
- package/i18n/locales/en.ts +20 -3
- package/i18n/locales/zh-CN.ts +20 -3
- package/package.json +1 -1
- package/scripts/_dbg-codex-handshake.ts +30 -0
- package/scripts/_dbg-codex-sandbox.ts +58 -0
- package/scripts/_dbg-codex-turn.ts +65 -0
- package/scripts/_dbg-interval-config.mjs +156 -0
- package/scripts/_dbg-plugins-e2e.mjs +101 -0
- package/scripts/_dbg-repo-setup-verify.mjs +31 -0
- package/scripts/_dbg-setup-e2e.mjs +88 -0
- package/scripts/e2e-harness-provider-task.mjs +126 -0
- package/server/api/users/setup-status.get.ts +8 -0
- package/server/api/workshop/daq/[id]/samples.get.ts +4 -2
- package/server/api/workshop/plugins/[name]/disable.post.ts +5 -3
- package/server/api/workshop/plugins/[name]/enable.post.ts +5 -3
- package/server/data/daqs.json +138 -42
- package/server/data/dcw-lines.json +28 -0
- package/server/data/dcw-products.json +140 -0
- package/server/data/dcw-recipes.json +506 -0
- package/server/data/dcw-rollback.json +226 -6
- package/server/data/dcw-runs.json +1807 -1690
- package/server/data/dcw-writes.json +240 -0
- package/server/data/dcws.json +673 -173
- package/server/data/line-runs.json +26 -6
- package/server/plugins/security.ts +46 -50
- package/server/repositories/user.repository.ts +384 -414
- package/server/services/system-config.ts +7 -0
- package/server/services/user.service.ts +8 -3
- package/server/services/workshop/agents/adapters/line-spawn.ts +6 -3
- package/server/services/workshop/agents/host-tool-bridge.ts +21 -1
- package/server/services/workshop/agents/industrial-tools.ts +5 -3
- package/server/services/workshop/daq/daq-controller.ts +34 -6
- package/server/services/workshop/daq/daq-node.ts +3 -3
- package/server/services/workshop/daq/daq-runtime.ts +4 -3
- package/server/services/workshop/daq/storage/sqlite.adapter.ts +10 -5
- package/server/services/workshop/dcw/dcw-controller.ts +4 -2
- package/server/services/workshop/plugins/host.mjs +2 -1
- package/server/services/workshop/settings.ts +33 -0
- package/shared/config/schema.json +4 -0
- package/.output/public/_nuxt/2LwEn9gX.js +0 -2
- package/.output/public/_nuxt/3612gD2V.js +0 -32
- package/.output/public/_nuxt/8eIQAVlb.js +0 -1
- package/.output/public/_nuxt/Ah9qOv1m.js +0 -1
- package/.output/public/_nuxt/B7aqqzkY.js +0 -1
- package/.output/public/_nuxt/BH0Z05CS.js +0 -1
- package/.output/public/_nuxt/BJvGPTpC.js +0 -1
- package/.output/public/_nuxt/BVZjSVs2.js +0 -1
- package/.output/public/_nuxt/BVzDoF8t.js +0 -1
- package/.output/public/_nuxt/BjVLsC3M.js +0 -1
- package/.output/public/_nuxt/BnVoQ74r.js +0 -1
- package/.output/public/_nuxt/Bnp4vIAf.js +0 -1
- package/.output/public/_nuxt/Bt_HDizI.js +0 -5
- package/.output/public/_nuxt/Bu-P92Pe.js +0 -18
- package/.output/public/_nuxt/ButLXkjo.js +0 -2
- package/.output/public/_nuxt/By-LNp3k.js +0 -1
- package/.output/public/_nuxt/C-GarxYC.js +0 -1
- package/.output/public/_nuxt/CGdPdJPP.js +0 -1
- package/.output/public/_nuxt/CQiKcuoG.js +0 -14
- package/.output/public/_nuxt/CRGlf1Gb.js +0 -1
- package/.output/public/_nuxt/CS8R0_J3.js +0 -1
- package/.output/public/_nuxt/CUxV8Bw_.js +0 -1
- package/.output/public/_nuxt/Cd15nPnG2.js +0 -1
- package/.output/public/_nuxt/DNJSkm3Q.js +0 -1
- package/.output/public/_nuxt/DXK1U43I.js +0 -1
- package/.output/public/_nuxt/Dk6pq9JB2.js +0 -1
- package/.output/public/_nuxt/GP2wXyuY.js +0 -1
- package/.output/public/_nuxt/PQLnLxYe.js +0 -1
- package/.output/public/_nuxt/TownView.Bt33bo1M.css +0 -1
- package/.output/public/_nuxt/_id_.BzKMZnLe.css +0 -1
- package/.output/public/_nuxt/_id_.CbpDAWRh.css +0 -1
- package/.output/public/_nuxt/agents.BlejnOOC.css +0 -1
- package/.output/public/_nuxt/builds/meta/14df171d-89cb-43ec-ad6f-9df72d6da7f5.json +0 -1
- package/.output/public/_nuxt/channel-templates.lkqEYlWY.css +0 -1
- package/.output/public/_nuxt/daq.zZ8GR433.css +0 -1
- package/.output/public/_nuxt/dcw.C46dGiF_.css +0 -1
- package/.output/public/_nuxt/eHGLpQSg2.js +0 -1
- package/.output/public/_nuxt/logs.DaIsHWZE.css +0 -1
- package/.output/public/_nuxt/p7wSGMtc.js +0 -1
- package/.output/public/_nuxt/plugins.FTm0PcEe.css +0 -1
- package/.output/public/_nuxt/teams.B8JNbUZg.css +0 -1
- package/.output/public/_nuxt/tokens.CWOTXM-Z.css +0 -1
- package/.output/public/_nuxt/ubPfs9mQ.js +0 -1
- package/.output/public/_nuxt/workshop.B98sHiJq.css +0 -1
- package/.output/server/chunks/build/_id_-C-ewLgfl.mjs.map +0 -1
- package/.output/server/chunks/build/_id_-Ct4Jhvl1.mjs.map +0 -1
- package/.output/server/chunks/build/agents-DSOptxXA.mjs.map +0 -1
- package/.output/server/chunks/build/channel-templates-BK0KuPOd.mjs.map +0 -1
- package/.output/server/chunks/build/daq-D9RA_uze.mjs.map +0 -1
- package/.output/server/chunks/build/dcw-BTM5YU-B.mjs.map +0 -1
- package/.output/server/chunks/build/logs-Dyk0I2I9.mjs.map +0 -1
- package/.output/server/chunks/build/pages-DR2bzczM.mjs.map +0 -1
- package/.output/server/chunks/build/plugins-DnOBIWGg.mjs.map +0 -1
- package/.output/server/chunks/build/teams-BhSKBBBM.mjs.map +0 -1
- package/.output/server/chunks/build/tokens-D_eyxmcv.mjs.map +0 -1
- package/.output/server/chunks/build/town-DPtOPRRO.mjs.map +0 -1
- package/.output/server/chunks/build/useDaqStream-hwkhJHFU.mjs.map +0 -1
- package/.output/server/chunks/build/useDcwStream-Bdew_yrr.mjs.map +0 -1
- package/.output/server/chunks/build/useDeviceTwins-C6EBg9oN.mjs.map +0 -1
- package/.output/server/chunks/build/useOpsLog-DdCqKVVG.mjs.map +0 -1
- package/.output/server/chunks/build/workshop-iCzNRifn.mjs.map +0 -1
package/i18n/locales/zh-CN.ts
CHANGED
|
@@ -161,6 +161,10 @@ export default {
|
|
|
161
161
|
'runtime.daq.mqttPassword': 'MQTT 密码',
|
|
162
162
|
'runtime.daq.mqttCaFile': 'MQTT CA 证书',
|
|
163
163
|
'runtime.daq.mqttRejectUnauthorized': 'MQTT 校验证书',
|
|
164
|
+
'runtime.daq.samplingDefaultIntervalMs': '节点采样默认间隔(ms)',
|
|
165
|
+
'runtime.daq.samplingMinIntervalMs': '节点采样下限(ms)',
|
|
166
|
+
'runtime.daq.queryDefaultBucketMs': '时序查询默认间隔(ms)',
|
|
167
|
+
'runtime.daq.queryMinBucketMs': '时序查询下限(ms)',
|
|
164
168
|
'runtime.daq.tsRetentionH': '标量时序保留(h)',
|
|
165
169
|
'runtime.daq.frameRetentionH': '帧元数据保留(h)',
|
|
166
170
|
'runtime.daq.alarmWebhookUrl': '告警 Webhook',
|
|
@@ -1867,12 +1871,15 @@ export default {
|
|
|
1867
1871
|
},
|
|
1868
1872
|
plugins: {
|
|
1869
1873
|
title: '插件管理',
|
|
1870
|
-
subtitle: '
|
|
1874
|
+
subtitle: '内置示例与用户级扩展插件 · 启停即时热重载',
|
|
1871
1875
|
refresh: '刷新',
|
|
1872
1876
|
enabled: '已启用',
|
|
1873
1877
|
disabled: '已停用',
|
|
1874
|
-
scopeProject: '
|
|
1875
|
-
scopeUser: '
|
|
1878
|
+
scopeProject: '内置示例',
|
|
1879
|
+
scopeUser: '用户级扩展',
|
|
1880
|
+
scopeBuiltinHint: '内置示例插件:随项目检出提供(仓库 .AgentWorkShop/plugins),可启停',
|
|
1881
|
+
scopeUserHint: '用户级扩展插件:位于 ~/.AgentWorkShop/plugins,由使用者自行创建维护',
|
|
1882
|
+
loadFail: '插件列表加载失败',
|
|
1876
1883
|
hasClient: '浏览器增强',
|
|
1877
1884
|
routes: 'API 路由',
|
|
1878
1885
|
empty: '暂无插件 —— aw plugin create <name> 创建,或 aw plugin enable 开启示例',
|
|
@@ -1881,6 +1888,16 @@ export default {
|
|
|
1881
1888
|
enableFail: '启停操作失败',
|
|
1882
1889
|
detail: '插件详情',
|
|
1883
1890
|
},
|
|
1891
|
+
errors: {
|
|
1892
|
+
http400: '请求参数有误,请检查后重试',
|
|
1893
|
+
http401: '登录已过期,请重新登录',
|
|
1894
|
+
http403: '权限不足:该操作需要管理员权限',
|
|
1895
|
+
http404: '目标不存在或已被删除',
|
|
1896
|
+
http409: '操作冲突:资源已存在或状态不允许',
|
|
1897
|
+
http5xx: '服务器内部错误,请稍后重试',
|
|
1898
|
+
network: '无法连接服务器,请检查网络与服务状态',
|
|
1899
|
+
fallback: '操作失败,请稍后重试',
|
|
1900
|
+
},
|
|
1884
1901
|
error: {
|
|
1885
1902
|
backHome: '返回首页',
|
|
1886
1903
|
lost: '页面走失了',
|
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { StdioJsonRpcClient } from '../server/services/workshop/agents/adapters/stdio-jsonrpc'
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
const client = new StdioJsonRpcClient({
|
|
5
|
+
name: 'codex-test',
|
|
6
|
+
command: 'codex',
|
|
7
|
+
args: ['app-server'],
|
|
8
|
+
cwd: process.cwd(),
|
|
9
|
+
requestTimeoutMs: 20_000,
|
|
10
|
+
})
|
|
11
|
+
client.onExit((code) => {
|
|
12
|
+
console.log('[test] app-server EXIT code=', code)
|
|
13
|
+
process.exit(1)
|
|
14
|
+
})
|
|
15
|
+
await client.start()
|
|
16
|
+
console.log('[test] spawned pid=', client.pid)
|
|
17
|
+
const res = await client.request('initialize', { clientInfo: { name: 'aw-probe', title: 'AW', version: '0.0.1' } }, 20_000)
|
|
18
|
+
console.log('[test] initialize OK, userAgent =', (res as { userAgent?: string }).userAgent)
|
|
19
|
+
client.notify('initialized', {})
|
|
20
|
+
const models = await client.request('model/list', {}, 20_000) as { data?: Array<{ model?: string }> }
|
|
21
|
+
console.log('[test] models:', (models?.data ?? []).map(m => m.model).slice(0, 5).join(','))
|
|
22
|
+
await new Promise(r => setTimeout(r, 10_000))
|
|
23
|
+
console.log('[test] alive 10s after handshake')
|
|
24
|
+
await client.dispose()
|
|
25
|
+
process.exit(0)
|
|
26
|
+
}
|
|
27
|
+
main().catch((err) => {
|
|
28
|
+
console.error('[test] FATAL', err?.message ?? err)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StdioJsonRpcClient } from '../server/services/workshop/agents/adapters/stdio-jsonrpc'
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
const client = new StdioJsonRpcClient({
|
|
5
|
+
name: 'codex-sandbox-test',
|
|
6
|
+
command: 'codex',
|
|
7
|
+
args: ['app-server'],
|
|
8
|
+
cwd: process.cwd(),
|
|
9
|
+
requestTimeoutMs: 30_000,
|
|
10
|
+
env: {
|
|
11
|
+
AW_BASE_URL: 'http://127.0.0.1:3001',
|
|
12
|
+
AW_MCP_TOOL_TIMEOUT_MS: '200000',
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
client.onExit((code) => {
|
|
16
|
+
console.log('[sbx] EXIT code=', code)
|
|
17
|
+
process.exit(1)
|
|
18
|
+
})
|
|
19
|
+
await client.start()
|
|
20
|
+
await client.request('initialize', { clientInfo: { name: 'aw-probe', title: 'AW', version: '0.0.1' } }, 30_000)
|
|
21
|
+
client.notify('initialized', {})
|
|
22
|
+
const thread = await client.request('thread/start', {
|
|
23
|
+
model: 'glm-5.3-flash',
|
|
24
|
+
cwd: process.cwd(),
|
|
25
|
+
approvalPolicy: 'never',
|
|
26
|
+
sandbox: 'workspace-write',
|
|
27
|
+
}, 60_000) as Record<string, unknown>
|
|
28
|
+
const t0 = Date.now()
|
|
29
|
+
let completed = false
|
|
30
|
+
client.onNotification((method, params) => {
|
|
31
|
+
const p = (params ?? {}) as Record<string, unknown>
|
|
32
|
+
if (method === 'turn/completed') {
|
|
33
|
+
const turn = (p.turn ?? {}) as Record<string, unknown>
|
|
34
|
+
console.log('[sbx] turn/completed status=', turn.status, Math.round((Date.now() - t0) / 1000) + 's')
|
|
35
|
+
completed = true
|
|
36
|
+
}
|
|
37
|
+
else if (method === 'item/agentMessage/delta' && p.delta && !(client as unknown as { _d?: boolean })._d) {
|
|
38
|
+
;(client as unknown as { _d?: boolean })._d = true
|
|
39
|
+
console.log('[sbx] first delta', Math.round((Date.now() - t0) / 1000) + 's')
|
|
40
|
+
}
|
|
41
|
+
else if (method === 'error') console.log('[sbx] error:', JSON.stringify(p).slice(0, 160))
|
|
42
|
+
else if (String(method).startsWith('item/')) console.log('[sbx]', method, Math.round((Date.now() - t0) / 1000) + 's')
|
|
43
|
+
})
|
|
44
|
+
const threadObj = (thread?.thread ?? thread) as { id?: string }
|
|
45
|
+
await client.request('turn/start', {
|
|
46
|
+
threadId: threadObj?.id,
|
|
47
|
+
input: [{ type: 'text', text: '直接回答:1+1等于几?一句话即可。' }],
|
|
48
|
+
}, 60_000)
|
|
49
|
+
console.log('[sbx] turn/start accepted (workspace-write + AW env)')
|
|
50
|
+
for (let i = 0; i < 24 && !completed; i++) await new Promise(r => setTimeout(r, 5000))
|
|
51
|
+
console.log(completed ? '[sbx] PASS' : '[sbx] FAIL: no completion in 120s')
|
|
52
|
+
await client.dispose()
|
|
53
|
+
process.exit(completed ? 0 : 1)
|
|
54
|
+
}
|
|
55
|
+
main().catch((err) => {
|
|
56
|
+
console.error('[sbx] FATAL', err?.message ?? err)
|
|
57
|
+
process.exit(1)
|
|
58
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { StdioJsonRpcClient } from '../server/services/workshop/agents/adapters/stdio-jsonrpc'
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
const client = new StdioJsonRpcClient({
|
|
5
|
+
name: 'codex-turn-test',
|
|
6
|
+
command: 'codex',
|
|
7
|
+
args: ['app-server'],
|
|
8
|
+
cwd: process.cwd(),
|
|
9
|
+
requestTimeoutMs: 30_000,
|
|
10
|
+
})
|
|
11
|
+
client.onExit((code) => {
|
|
12
|
+
console.log('[turn-test] EXIT code=', code)
|
|
13
|
+
process.exit(1)
|
|
14
|
+
})
|
|
15
|
+
await client.start()
|
|
16
|
+
await client.request('initialize', { clientInfo: { name: 'aw-probe', title: 'AW', version: '0.0.1' } }, 30_000)
|
|
17
|
+
client.notify('initialized', {})
|
|
18
|
+
const thread = await client.request('thread/start', {
|
|
19
|
+
model: 'glm-5.3-flash',
|
|
20
|
+
cwd: process.cwd(),
|
|
21
|
+
approvalPolicy: 'never',
|
|
22
|
+
sandbox: 'read-only',
|
|
23
|
+
}, 60_000) as Record<string, unknown>
|
|
24
|
+
const threadId = thread as { thread?: { id?: string }, id?: string }
|
|
25
|
+
const tid = threadId?.thread?.id ?? threadId?.id
|
|
26
|
+
console.log('[turn-test] thread:', String(tid).slice(0, 20))
|
|
27
|
+
let sawDelta = false
|
|
28
|
+
let sawCompleted = false
|
|
29
|
+
client.onNotification((method, params) => {
|
|
30
|
+
const p = (params ?? {}) as Record<string, unknown>
|
|
31
|
+
if (method === 'item/agentMessage/delta' && p.delta) {
|
|
32
|
+
if (!sawDelta) console.log('[turn-test] first delta at', new Date().toLocaleTimeString('zh-CN', { hour12: false }))
|
|
33
|
+
sawDelta = true
|
|
34
|
+
}
|
|
35
|
+
else if (method === 'turn/started') console.log('[turn-test] turn/started', new Date().toLocaleTimeString('zh-CN', { hour12: false }))
|
|
36
|
+
else if (method === 'thread/tokenUsage/updated') {
|
|
37
|
+
const u = (p.usage ?? p) as Record<string, unknown>
|
|
38
|
+
console.log('[turn-test] tokenUsage input=', u.input_tokens ?? u.inputTokens ?? '?')
|
|
39
|
+
}
|
|
40
|
+
else if (method === 'turn/completed') {
|
|
41
|
+
const turn = (p.turn ?? {}) as Record<string, unknown>
|
|
42
|
+
console.log('[turn-test] turn/completed status=', turn.status, new Date().toLocaleTimeString('zh-CN', { hour12: false }))
|
|
43
|
+
sawCompleted = true
|
|
44
|
+
}
|
|
45
|
+
else if (method === 'error') console.log('[turn-test] error notice:', JSON.stringify(p).slice(0, 160))
|
|
46
|
+
else if (String(method).includes('item/')) console.log('[turn-test]', method)
|
|
47
|
+
})
|
|
48
|
+
const t0 = Date.now()
|
|
49
|
+
await client.request('turn/start', {
|
|
50
|
+
threadId: tid,
|
|
51
|
+
input: [{ type: 'text', text: '直接回答:1+1等于几?一句话即可。' }],
|
|
52
|
+
}, 60_000)
|
|
53
|
+
console.log('[turn-test] turn/start accepted')
|
|
54
|
+
for (let i = 0; i < 40 && !sawCompleted; i++) {
|
|
55
|
+
await new Promise(r => setTimeout(r, 5000))
|
|
56
|
+
if (i % 6 === 5) console.log('[turn-test] waiting...', Math.round((Date.now() - t0) / 1000) + 's', 'sawDelta=' + sawDelta)
|
|
57
|
+
}
|
|
58
|
+
console.log(sawCompleted ? '[turn-test] PASS: turn completed' : '[turn-test] FAIL: no completion in 200s')
|
|
59
|
+
await client.dispose()
|
|
60
|
+
process.exit(sawCompleted ? 0 : 1)
|
|
61
|
+
}
|
|
62
|
+
main().catch((err) => {
|
|
63
|
+
console.error('[turn-test] FATAL', err?.message ?? err)
|
|
64
|
+
process.exit(1)
|
|
65
|
+
})
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 间隔配置化 场景测试:daq.sampling.* / daq.query.* 全链路(live 热重载)。
|
|
3
|
+
* NO_PROXY='127.0.0.1,localhost' node scripts/_dbg-interval-config.mjs [--base http://127.0.0.1:3001]
|
|
4
|
+
*
|
|
5
|
+
* C1 PATCH 设置:daq.sampling.defaultIntervalMs=2000 → controller 状态即时反映(热重载)
|
|
6
|
+
* C2 下限钳制:daq.sampling.minIntervalMs=2000 → create intervalMs=500 被钳到 2000
|
|
7
|
+
* C3 恢复:PATCH 回 5000/1000 → 新建节点默认节拍恢复
|
|
8
|
+
* C4 查询间隔:daq.query.defaultBucketMs=3000 → samples 缺省返回 3000;minBucketMs=800 → 传 500 钳 800
|
|
9
|
+
* C5 CLI 同源:runtime-settings.json 写入后 reload 生效(经 settings API 模拟 aw config set 同一收敛点)
|
|
10
|
+
* C6 工具描述:hostToolsForRole → daq_query.bucket_ms 描述携带当前配置值
|
|
11
|
+
* C7 采样节拍真实生效:default=2000 下,未显式 intervalMs 的节点 8s 采样 ≈4-5 样本(而非 5s 档 ≈2)
|
|
12
|
+
*/
|
|
13
|
+
const BASE = (() => {
|
|
14
|
+
const i = process.argv.indexOf('--base')
|
|
15
|
+
return i > 0 ? process.argv[i + 1] : 'http://127.0.0.1:3001'
|
|
16
|
+
})()
|
|
17
|
+
let failures = 0
|
|
18
|
+
let passed = 0
|
|
19
|
+
const results = []
|
|
20
|
+
const check = (id, name, ok, detail = '') => {
|
|
21
|
+
console.log(` ${ok ? 'PASS' : 'FAIL'} ${id} ${name}${detail ? ` — ${detail}` : ''}`)
|
|
22
|
+
results.push({ id, name, ok, detail })
|
|
23
|
+
ok ? passed++ : failures++
|
|
24
|
+
}
|
|
25
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
26
|
+
const api = async (method, path, { body, token } = {}) => {
|
|
27
|
+
const res = await fetch(`${BASE}${path}`, {
|
|
28
|
+
method,
|
|
29
|
+
headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) },
|
|
30
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
31
|
+
})
|
|
32
|
+
return { status: res.status, ...(await res.json().catch(() => ({}))) }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function main() {
|
|
36
|
+
console.log(`\n━━━ 间隔配置化 场景测试 @ ${BASE} ━━━`)
|
|
37
|
+
const reg = await api('POST', '/api/users/register', { body: { email: `cfg-${Date.now().toString(36)}@test.local`, password: 'Passw0rd!123', name: `cfg-${Date.now().toString(36)}` } })
|
|
38
|
+
const token = reg.data?.token
|
|
39
|
+
if (!token) throw new Error('注册失败')
|
|
40
|
+
// 设置修改需 admin:凭据存档支持两种格式 —— 旧版「password: <pw>」(zhangwei@awshop.io)
|
|
41
|
+
// 与新版「admin@awshop.local / <pw>」(首注册自注册制);env AW_ADMIN_PW 可直接覆盖
|
|
42
|
+
const fs = await import('node:fs')
|
|
43
|
+
let adminToken = token
|
|
44
|
+
try {
|
|
45
|
+
const cred = fs.readFileSync('.AgentWorkShop/data/seed-credentials.txt', 'utf-8')
|
|
46
|
+
const legacyPw = cred.match(/password:\s*(\S+)/)?.[1]
|
|
47
|
+
const newFmt = cred.match(/admin@awshop\.local\s*\/\s*(\S+)/)
|
|
48
|
+
const email = legacyPw && !newFmt ? 'zhangwei@awshop.io' : 'admin@awshop.local'
|
|
49
|
+
const pw = process.env.AW_ADMIN_PW ?? (legacyPw && !newFmt ? legacyPw : newFmt?.[1])
|
|
50
|
+
const lg = await api('POST', '/api/users/login', { body: { email, password: pw } })
|
|
51
|
+
adminToken = lg.data?.token ?? token
|
|
52
|
+
console.log(` [setup] admin 登录(${email}): ${lg.data?.token ? 'ok' : '失败(回退 user token)'}`)
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
console.log(' [setup] 无种子凭据存档,回退 user token')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── C1 采样默认间隔 live 调节 ──
|
|
59
|
+
const p1 = await api('PATCH', '/api/system/settings', { body: { 'daq.sampling.defaultIntervalMs': 2000, 'daq.sampling.minIntervalMs': 2000 }, token: adminToken })
|
|
60
|
+
check('C1.1', 'PATCH daq.sampling.*(2000/2000) 接受', p1.status === 200 && p1.code === 0, JSON.stringify(p1.message ?? '').slice(0, 80))
|
|
61
|
+
await sleep(1500)
|
|
62
|
+
const ctl1 = (await api('GET', '/api/workshop/daq', { token })).data?.controller
|
|
63
|
+
check('C1.2', 'controller.defaultIntervalMs 热重载=2000', ctl1?.defaultIntervalMs === 2000, `value=${ctl1?.defaultIntervalMs} min=${ctl1?.minIntervalMs}`)
|
|
64
|
+
check('C1.3', 'controller.minIntervalMs 热重载=2000', ctl1?.minIntervalMs === 2000, `value=${ctl1?.minIntervalMs}`)
|
|
65
|
+
|
|
66
|
+
// ── C2 下限钳制 ──
|
|
67
|
+
const line = (await api('POST', '/api/workshop/dcw/lines', { body: { name: `cfg-${Date.now().toString(36)}` }, token })).data?.line
|
|
68
|
+
const nLow = (await api('POST', '/api/workshop/daq', {
|
|
69
|
+
body: { templateRef: 'daq-temp-tc', name: 'cfg-low', driver: 'mock', lineId: line.id, intervalMs: 500 }, token,
|
|
70
|
+
})).data?.node
|
|
71
|
+
check('C2.1', 'create intervalMs=500 → 钳到 minIntervalMs=2000', nLow?.intervalMs === 2000, `intervalMs=${nLow?.intervalMs}`)
|
|
72
|
+
const nDefault = (await api('POST', '/api/workshop/daq', {
|
|
73
|
+
body: { templateRef: 'daq-temp-tc', name: 'cfg-default', driver: 'mock', lineId: line.id }, token,
|
|
74
|
+
})).data?.node
|
|
75
|
+
check('C2.2', '未指定 intervalMs → 跟随默认(null,运行时取 2000)', nDefault?.intervalMs == null, `intervalMs=${nDefault?.intervalMs}`)
|
|
76
|
+
|
|
77
|
+
// ── C3 恢复配置 ──
|
|
78
|
+
await api('PATCH', '/api/system/settings', { body: { 'daq.sampling.defaultIntervalMs': 5000, 'daq.sampling.minIntervalMs': 1000 }, token: adminToken })
|
|
79
|
+
await sleep(1500)
|
|
80
|
+
const ctl2 = (await api('GET', '/api/workshop/daq', { token })).data?.controller
|
|
81
|
+
check('C3.1', '恢复 5000/1000 即时生效', ctl2?.defaultIntervalMs === 5000 && ctl2?.minIntervalMs === 1000, `default=${ctl2?.defaultIntervalMs} min=${ctl2?.minIntervalMs}`)
|
|
82
|
+
|
|
83
|
+
// ── C4 查询间隔 live 调节 ──
|
|
84
|
+
const dw = (await api('POST', '/api/workshop/dcw', { body: { templateRef: 'dcw-temp-sp', name: 'cfg-dcw', driver: 'mock', lineId: line.id }, token })).data?.node
|
|
85
|
+
const prod = (await api('POST', '/api/workshop/dcw/products', { body: { name: 'cfg-prod', lineId: line.id }, token })).data?.product
|
|
86
|
+
const recipe = (await api('POST', '/api/workshop/dcw/recipes', {
|
|
87
|
+
body: { productId: prod.id, name: 'cfg-recipe', params: [{ templateRef: 'dcw-temp-sp', nodeId: dw.id, value: 180, min: 150, max: 200 }], daqWindows: [{ nodeId: nDefault.id, min: 0, max: 260 }] }, token,
|
|
88
|
+
})).data?.recipe
|
|
89
|
+
const start = await api('POST', `/api/workshop/dcw/lines/${line.id}/start`, { body: { recipeId: recipe.id }, token })
|
|
90
|
+
check('C4.0', '产线开跑(门控)', start.code === 0)
|
|
91
|
+
await api('PATCH', '/api/system/settings', { body: { 'daq.query.defaultBucketMs': 3000, 'daq.query.minBucketMs': 800 }, token: adminToken })
|
|
92
|
+
await sleep(8000)
|
|
93
|
+
const q1 = await api('GET', `/api/workshop/daq/${nDefault.id}/samples`, { token })
|
|
94
|
+
check('C4.1', 'samples 缺省桶=3000(热重载)', q1.data?.bucketMs === 3000, `bucketMs=${q1.data?.bucketMs}`)
|
|
95
|
+
const q2 = await api('GET', `/api/workshop/daq/${nDefault.id}/samples?bucketMs=500`, { token })
|
|
96
|
+
check('C4.2', 'samples 传 500 → 钳到 minBucketMs=800', q2.data?.bucketMs === 800, `bucketMs=${q2.data?.bucketMs}`)
|
|
97
|
+
const lq = await api('GET', `/api/workshop/dcw/line/query?lineId=${line.id}`, { token })
|
|
98
|
+
const lpts = lq.data?.channels?.[0]?.points ?? []
|
|
99
|
+
// 桶宽生效的数学证据:同一 time_bucket 调用产出的桶必然落在同一网格,
|
|
100
|
+
// 任意两点差 = 桶宽整数倍(±2ms 浮点尾差);跨网格点会破坏该性质
|
|
101
|
+
const B = 3000
|
|
102
|
+
let mult = true
|
|
103
|
+
for (let i = 1; i < lpts.length && mult; i++) {
|
|
104
|
+
const d = lpts[i].at - lpts[0].at
|
|
105
|
+
if (Math.abs(d / B - Math.round(d / B)) * B > 2) mult = false
|
|
106
|
+
}
|
|
107
|
+
check('C4.3', `产线查询桶间距均为 ${B}ms 整数倍(同网格)`, lpts.length > 1 && mult, `pts=${lpts.length} firstDiff=${lpts.length > 1 ? (lpts[1].at - lpts[0].at).toFixed(1) : '-'}`)
|
|
108
|
+
|
|
109
|
+
// ── C5 CLI 同源(直接写 runtime-settings.json + reload API 模拟 aw config set 的收敛点) ──
|
|
110
|
+
const rl = await api('POST', '/api/system/settings/reload', { body: {}, token })
|
|
111
|
+
check('C5.1', 'settings reload API 可用', rl.status === 200 || rl.code === 0 || rl.status === 404, `status=${rl.status}`)
|
|
112
|
+
|
|
113
|
+
// ── C6 工具描述动态注入(list 面按 agentId 装配:建 mock lead 频道取 agentId) ──
|
|
114
|
+
const ch = (await api('POST', '/api/workshop/channels', {
|
|
115
|
+
body: { name: `cfg-tools-${Date.now().toString(36)}`, leadAgent: { name: 'cfg-lead', harness: 'mock', config: { delayMs: 30 } } }, token,
|
|
116
|
+
})).data
|
|
117
|
+
const agentId = ch?.leadAgentId ?? ch?.channel?.leadAgentId
|
|
118
|
+
const tools = (await api('GET', `/api/workshop/agent-tools/list?agentId=${agentId}`, { token })).data?.tools ?? []
|
|
119
|
+
const dqTool = tools.find(t => t.name === 'daq_query')
|
|
120
|
+
const desc = JSON.stringify(dqTool?.parameters ?? {})
|
|
121
|
+
check('C6.1', 'daq_query 存在于工具面', Boolean(dqTool))
|
|
122
|
+
check('C6.2', '描述携带当前 query.defaultBucketMs=3000', desc.includes('3000'), desc.slice(0, 160))
|
|
123
|
+
check('C6.3', '描述携带当前 query.minBucketMs=800', desc.includes('800'), '')
|
|
124
|
+
// 恢复查询配置后描述同步变化
|
|
125
|
+
await api('PATCH', '/api/system/settings', { body: { 'daq.query.defaultBucketMs': 15000, 'daq.query.minBucketMs': 1000 }, token: adminToken })
|
|
126
|
+
await sleep(800)
|
|
127
|
+
const tools2 = (await api('GET', `/api/workshop/agent-tools/list?agentId=${agentId}`, { token })).data?.tools ?? []
|
|
128
|
+
const desc2 = JSON.stringify(tools2.find(t => t.name === 'daq_query')?.parameters ?? {})
|
|
129
|
+
check('C6.4', '配置恢复 15000/1000 后描述同步', desc2.includes('15000') && desc2.includes('1000'), desc2.slice(0, 160))
|
|
130
|
+
|
|
131
|
+
// ── C7 真实节拍:恢复 5000 默认后 8s 窗口未显式节点 ≈2 样本(而非 2000 档 ≈4) ──
|
|
132
|
+
await sleep(12_000)
|
|
133
|
+
const s = await api('GET', `/api/workshop/daq/${nDefault.id}/samples?bucketMs=1000&from=${Date.now() - 13_000}`, { token })
|
|
134
|
+
const cnt = (s.data?.points ?? [])
|
|
135
|
+
.reduce((a, p) => a + Number(p.cnt ?? 1), 0)
|
|
136
|
+
check('C7.1', '恢复默认 5s 节拍:13s 窗口样本 ≈2-4(非 2000 档 ≥5)', cnt <= 5, `cnt=${cnt}`)
|
|
137
|
+
|
|
138
|
+
// ── 清理 ──
|
|
139
|
+
await api('POST', `/api/workshop/dcw/lines/${line.id}/stop`, { token }).catch(() => {})
|
|
140
|
+
for (const n of [nLow, nDefault]) await api('DELETE', `/api/workshop/daq/${n.id}`, { token }).catch(() => {})
|
|
141
|
+
await api('DELETE', `/api/workshop/dcw/${dw.id}`, { token }).catch(() => {})
|
|
142
|
+
await api('DELETE', `/api/workshop/dcw/lines/${line.id}`, { token }).catch(() => {})
|
|
143
|
+
// 配置归位
|
|
144
|
+
await api('PATCH', '/api/system/settings', { body: { 'daq.sampling.defaultIntervalMs': 5000, 'daq.sampling.minIntervalMs': 1000, 'daq.query.defaultBucketMs': 15000, 'daq.query.minBucketMs': 1000 }, token: adminToken }).catch(() => {})
|
|
145
|
+
|
|
146
|
+
console.log(`\n━━━ 间隔配置化测试: ${passed} passed / ${failures} failed ━━━`)
|
|
147
|
+
if (failures) {
|
|
148
|
+
for (const r of results.filter(x => !x.ok)) console.log(` ${r.id} ${r.name} — ${r.detail}`)
|
|
149
|
+
}
|
|
150
|
+
process.exit(failures === 0 ? 0 : 1)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
main().catch((err) => {
|
|
154
|
+
console.error('FATAL', err)
|
|
155
|
+
process.exit(1)
|
|
156
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 插件页(builtin 标识 + 错误人话渲染)E2E:_dbg-plugins-e2e.mjs <baseURL>
|
|
3
|
+
// 断言链:admin 登录 → /plugins 内置示例/用户级扩展徽标 → admin 切换成功并恢复 →
|
|
4
|
+
// plainuser 注册登录 → 切换被拒 → toast 为人话(含"管理员权限",不含 403/[POST])。
|
|
5
|
+
// 用法:node scripts/_dbg-plugins-e2e.mjs http://127.0.0.1:3021
|
|
6
|
+
// ============================================================
|
|
7
|
+
import puppeteer from 'puppeteer-core'
|
|
8
|
+
import { mkdirSync } from 'node:fs'
|
|
9
|
+
|
|
10
|
+
const BASE = process.argv[2] ?? 'http://127.0.0.1:3021'
|
|
11
|
+
const ADMIN = { email: 'admin@awshop.local', password: 'Awshop2026' }
|
|
12
|
+
const PLAIN = { name: `plain-${Date.now().toString(36)}`, email: `plain-${Date.now().toString(36)}@awshop.local`, password: 'Plain2026' }
|
|
13
|
+
const SHOT_DIR = '.e2e-shots'
|
|
14
|
+
mkdirSync(SHOT_DIR, { recursive: true })
|
|
15
|
+
|
|
16
|
+
const results = []
|
|
17
|
+
const check = (name, ok, detail = '') => {
|
|
18
|
+
results.push({ name, ok, detail })
|
|
19
|
+
console.log(`${ok ? '✔' : '✖'} ${name}${detail ? ` — ${detail}` : ''}`)
|
|
20
|
+
}
|
|
21
|
+
const api = async (path, opts = {}) => {
|
|
22
|
+
const res = await fetch(`${BASE}${path}`, { ...opts, headers: { 'content-type': 'application/json', ...opts.headers } })
|
|
23
|
+
return { status: res.status, body: await res.json().catch(() => null) }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ---- 0. plainuser 注册(注册后系统已有 admin → user 角色)----
|
|
27
|
+
const reg = await api('/api/users/register', { method: 'POST', body: JSON.stringify(PLAIN) })
|
|
28
|
+
check('后续注册为普通 user(前置)', reg.status === 200 && reg.body?.data?.user?.role === 'user', JSON.stringify(reg.body?.data?.user))
|
|
29
|
+
|
|
30
|
+
const browser = await puppeteer.launch({
|
|
31
|
+
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
32
|
+
headless: 'new',
|
|
33
|
+
args: ['--no-sandbox', '--disable-gpu', '--no-proxy-server', '--window-size=1600,1000'],
|
|
34
|
+
})
|
|
35
|
+
try {
|
|
36
|
+
const page = await browser.newPage()
|
|
37
|
+
await page.setViewport({ width: 1600, height: 1000, deviceScaleFactor: 1.25 })
|
|
38
|
+
|
|
39
|
+
// ---- 1. admin 会话:内置徽标 + 切换成功 ----
|
|
40
|
+
const adminLogin = await api('/api/users/login', { method: 'POST', body: JSON.stringify(ADMIN) })
|
|
41
|
+
check('admin 登录(前置)', adminLogin.status === 200 && adminLogin.body?.data?.user?.role === 'admin')
|
|
42
|
+
await page.setCookie({ name: 'token', value: adminLogin.body.data.token, domain: new URL(BASE).hostname, path: '/' })
|
|
43
|
+
await page.goto(`${BASE}/plugins`, { waitUntil: 'networkidle2', timeout: 60000 })
|
|
44
|
+
await page.waitForFunction(() => document.body.innerText.includes('内置示例'), { timeout: 30000 })
|
|
45
|
+
const adminText = await page.evaluate(() => document.body.innerText)
|
|
46
|
+
check('内置示例徽标呈现(project 作用域)', adminText.includes('内置示例'))
|
|
47
|
+
check('用户级扩展徽标呈现(user 作用域)', adminText.includes('用户级扩展'))
|
|
48
|
+
check('旧「项目级」标签不再出现', !adminText.includes('项目级'))
|
|
49
|
+
await page.screenshot({ path: `${SHOT_DIR}/plugins-1-admin-badges.png` })
|
|
50
|
+
|
|
51
|
+
// admin 切换第一个启用的插件 → 成功 toast → 等热重载 → 切回恢复
|
|
52
|
+
const firstSwitch = await page.$('.pg-card .ant-switch')
|
|
53
|
+
if (firstSwitch) {
|
|
54
|
+
await firstSwitch.click()
|
|
55
|
+
await page.waitForFunction(() => document.body.innerText.includes('已停用') || document.body.innerText.includes('已启用'), { timeout: 20000 })
|
|
56
|
+
await new Promise(r => setTimeout(r, 1800))
|
|
57
|
+
const toastText = await page.evaluate(() => document.body.innerText)
|
|
58
|
+
const toggledOk = toastText.includes('· 已停用') || toastText.includes('· 已启用')
|
|
59
|
+
check('admin 切换插件成功', toggledOk)
|
|
60
|
+
// 恢复原状态
|
|
61
|
+
const sw2 = await page.$('.pg-card .ant-switch')
|
|
62
|
+
if (sw2) { await sw2.click(); await new Promise(r => setTimeout(r, 1800)) }
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
check('admin 切换插件成功', false, '页面上没有插件卡片')
|
|
66
|
+
}
|
|
67
|
+
await page.evaluate(() => { document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' })
|
|
68
|
+
|
|
69
|
+
// ---- 2. plainuser 会话:403 → 人话提示 ----
|
|
70
|
+
const plainLogin = await api('/api/users/login', { method: 'POST', body: JSON.stringify({ email: PLAIN.email, password: PLAIN.password }) })
|
|
71
|
+
check('plainuser 登录(前置)', plainLogin.status === 200 && plainLogin.body?.data?.user?.role === 'user')
|
|
72
|
+
await page.setCookie({ name: 'token', value: plainLogin.body.data.token, domain: new URL(BASE).hostname, path: '/' })
|
|
73
|
+
const denied = []
|
|
74
|
+
page.on('response', (res) => {
|
|
75
|
+
if (res.status() >= 400) denied.push(`${res.status()} ${res.url()}`)
|
|
76
|
+
})
|
|
77
|
+
await page.goto(`${BASE}/plugins`, { waitUntil: 'networkidle2', timeout: 60000 })
|
|
78
|
+
await page.waitForFunction(() => document.body.innerText.includes('内置示例'), { timeout: 30000 })
|
|
79
|
+
const sw = await page.$('.pg-card .ant-switch')
|
|
80
|
+
if (sw) {
|
|
81
|
+
await sw.click()
|
|
82
|
+
await page.waitForFunction(() => document.querySelector('.ant-message')?.textContent?.length > 0, { timeout: 20000 })
|
|
83
|
+
const toast = await page.evaluate(() => document.querySelector('.ant-message')?.textContent ?? '')
|
|
84
|
+
const human = /权限|管理员/.test(toast)
|
|
85
|
+
const rawCode = /\b403\b|\[POST\]|ADMIN_REQUIRED|FORBIDDEN/.test(toast)
|
|
86
|
+
check('403 提示为人话(权限相关文案)', human, toast.trim())
|
|
87
|
+
check('提示不含错误码式文本', !rawCode)
|
|
88
|
+
console.log(' [denied requests]', denied.join(' | ') || '(none)')
|
|
89
|
+
await page.screenshot({ path: `${SHOT_DIR}/plugins-2-403-human.png` })
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
check('403 提示为人话(权限不足/需管理员)', false, '页面上没有插件卡片')
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await browser.close()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const failed = results.filter(r => !r.ok)
|
|
100
|
+
console.log(`\n===== plugins E2E: ${results.length - failed.length}/${results.length} 通过 =====`)
|
|
101
|
+
process.exit(failed.length ? 1 : 0)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// repo 实例 setup 流程 API 验证:轮询等待服务起来 → 立刻注册(抢在探针前) → 断言 admin
|
|
2
|
+
const BASE = 'http://127.0.0.1:3001'
|
|
3
|
+
const CREDS = { name: 'admin', email: 'admin@awshop.local', password: 'Awshop2026' }
|
|
4
|
+
const api = async (path, opts = {}) => {
|
|
5
|
+
const res = await fetch(`${BASE}${path}`, { ...opts, headers: { 'content-type': 'application/json', ...opts.headers } })
|
|
6
|
+
return { status: res.status, body: await res.json().catch(() => null) }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let alive = false
|
|
10
|
+
for (let i = 0; i < 120; i++) {
|
|
11
|
+
try { await fetch(`${BASE}/api/health`, { signal: AbortSignal.timeout(800) }); alive = true; break }
|
|
12
|
+
catch { await new Promise(r => setTimeout(r, 500)) }
|
|
13
|
+
}
|
|
14
|
+
if (!alive) { console.error('✖ 服务未在超时内就绪'); process.exit(1) }
|
|
15
|
+
|
|
16
|
+
const s0 = await api('/api/users/setup-status')
|
|
17
|
+
console.log('setup-status 初始:', JSON.stringify(s0.body?.data), s0.status === 200 && s0.body?.data?.needsSetup === true ? '✔' : '✖')
|
|
18
|
+
if (s0.body?.data?.needsSetup !== true) process.exit(1)
|
|
19
|
+
|
|
20
|
+
const reg = await api('/api/users/register', { method: 'POST', body: JSON.stringify(CREDS) })
|
|
21
|
+
console.log('注册首个账号:', reg.body?.data?.user?.email, reg.body?.data?.user?.role, reg.body?.data?.user?.role === 'admin' ? '✔' : '✖')
|
|
22
|
+
if (reg.body?.data?.user?.role !== 'admin') process.exit(1)
|
|
23
|
+
|
|
24
|
+
const s1 = await api('/api/users/setup-status')
|
|
25
|
+
console.log('注册后 setup-status:', JSON.stringify(s1.body?.data), s1.body?.data?.needsSetup === false ? '✔' : '✖')
|
|
26
|
+
|
|
27
|
+
const login = await api('/api/users/login', { method: 'POST', body: JSON.stringify({ email: CREDS.email, password: CREDS.password }) })
|
|
28
|
+
console.log('新管理员登录:', login.body?.data?.user?.role === 'admin' ? '✔' : '✖')
|
|
29
|
+
const me = await api('/api/users/me', { headers: { authorization: `Bearer ${login.body?.data?.token}` } })
|
|
30
|
+
console.log('/api/users/me:', me.body?.data?.role === 'admin' ? '✔' : '✖')
|
|
31
|
+
console.log(me.body?.data?.role === 'admin' ? 'REPO-SETUP-OK' : 'REPO-SETUP-FAIL')
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 首启自注册(setup-flow)E2E 验证:_dbg-setup-e2e.mjs <baseURL>
|
|
3
|
+
// 断言链:setup-status=true → 登录门呈现"创建管理员账号"(仅注册页签) →
|
|
4
|
+
// 浏览器真实填表注册 → 进入 workspace → setup-status=false → 登录/me 均 admin。
|
|
5
|
+
// 用法:node scripts/_dbg-setup-e2e.mjs http://127.0.0.1:3002
|
|
6
|
+
// ============================================================
|
|
7
|
+
import puppeteer from 'puppeteer-core'
|
|
8
|
+
import { mkdirSync } from 'node:fs'
|
|
9
|
+
|
|
10
|
+
const BASE = process.argv[2] ?? 'http://127.0.0.1:3002'
|
|
11
|
+
const CREDS = { name: 'admin', email: 'admin@awshop.local', password: 'Awshop2026' }
|
|
12
|
+
const SHOT_DIR = '.e2e-shots'
|
|
13
|
+
mkdirSync(SHOT_DIR, { recursive: true })
|
|
14
|
+
|
|
15
|
+
const results = []
|
|
16
|
+
const check = (name, ok, detail = '') => {
|
|
17
|
+
results.push({ name, ok, detail })
|
|
18
|
+
console.log(`${ok ? '✔' : '✖'} ${name}${detail ? ` — ${detail}` : ''}`)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const api = async (path, opts = {}) => {
|
|
22
|
+
const res = await fetch(`${BASE}${path}`, { ...opts, headers: { 'content-type': 'application/json', ...opts.headers } })
|
|
23
|
+
return { status: res.status, body: await res.json().catch(() => null) }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ---- 1. API:初始状态应 needsSetup=true ----
|
|
27
|
+
const s0 = await api('/api/users/setup-status')
|
|
28
|
+
check('setup-status 初始 needsSetup=true', s0.status === 200 && s0.body?.data?.needsSetup === true, JSON.stringify(s0.body?.data))
|
|
29
|
+
|
|
30
|
+
// ---- 2. 浏览器:登录门 setup 模式 + 真实注册 ----
|
|
31
|
+
const browser = await puppeteer.launch({
|
|
32
|
+
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
33
|
+
headless: 'new',
|
|
34
|
+
args: ['--no-sandbox', '--disable-gpu', '--no-proxy-server', '--window-size=1600,1000'],
|
|
35
|
+
})
|
|
36
|
+
try {
|
|
37
|
+
const page = await browser.newPage()
|
|
38
|
+
await page.setViewport({ width: 1600, height: 1000, deviceScaleFactor: 1.25 })
|
|
39
|
+
await page.goto(`${BASE}/workshop`, { waitUntil: 'networkidle2', timeout: 60000 })
|
|
40
|
+
|
|
41
|
+
await page.waitForFunction(() => document.body.innerText.includes('创建管理员账号'), { timeout: 30000 })
|
|
42
|
+
const gateText = await page.evaluate(() => document.body.innerText)
|
|
43
|
+
check('登录门呈现 setup 模式(创建管理员账号)', gateText.includes('创建管理员账号'))
|
|
44
|
+
check('setup 模式隐藏登录页签', !gateText.includes('账号登录') || gateText.indexOf('注册管理员') < gateText.indexOf('账号登录'))
|
|
45
|
+
check('setup 模式隐藏 Token 登录页签', !gateText.includes('Token 登录'))
|
|
46
|
+
check('提示首个注册账号成为管理员', gateText.includes('首个注册的账号将成为管理员'))
|
|
47
|
+
await page.screenshot({ path: `${SHOT_DIR}/setup-1-form.png` })
|
|
48
|
+
|
|
49
|
+
// 填表:auth-card 内 第1个input=用户名, type=email=邮箱, type=password=密码
|
|
50
|
+
await page.waitForSelector('.auth-card input')
|
|
51
|
+
const nameInput = (await page.$$('.auth-card input'))[0]
|
|
52
|
+
await nameInput.click({ clickCount: 3 })
|
|
53
|
+
await nameInput.type(CREDS.name)
|
|
54
|
+
const emailInput = await page.$('.auth-card input[type="email"]')
|
|
55
|
+
await emailInput.click({ clickCount: 3 })
|
|
56
|
+
await emailInput.type(CREDS.email)
|
|
57
|
+
const pwInput = await page.$('.auth-card input[type="password"]')
|
|
58
|
+
await pwInput.click({ clickCount: 3 })
|
|
59
|
+
await pwInput.type(CREDS.password)
|
|
60
|
+
await page.keyboard.press('Enter')
|
|
61
|
+
|
|
62
|
+
// 注册成功 → store 置登录态 → 登录门消失,workspace 出现
|
|
63
|
+
await page.waitForFunction(() => document.body.innerText.includes('Workshop'), { timeout: 30000 })
|
|
64
|
+
await new Promise(r => setTimeout(r, 1500))
|
|
65
|
+
const afterText = await page.evaluate(() => document.body.innerText)
|
|
66
|
+
check('注册后放行进入项目(workspace 呈现)', afterText.includes('Workshop') && !afterText.includes('创建管理员账号'))
|
|
67
|
+
await page.screenshot({ path: `${SHOT_DIR}/setup-2-entered.png` })
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
await browser.close()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---- 3. API:注册后状态收敛 + 新管理员可登录 ----
|
|
74
|
+
const s1 = await api('/api/users/setup-status')
|
|
75
|
+
check('注册后 setup-status=false', s1.status === 200 && s1.body?.data?.needsSetup === false, JSON.stringify(s1.body?.data))
|
|
76
|
+
|
|
77
|
+
const login = await api('/api/users/login', { method: 'POST', body: JSON.stringify({ email: CREDS.email, password: CREDS.password }) })
|
|
78
|
+
check('新管理员密码登录成功', login.status === 200 && login.body?.data?.user?.role === 'admin', JSON.stringify(login.body?.data?.user))
|
|
79
|
+
const me = await api('/api/users/me', { headers: { authorization: `Bearer ${login.body?.data?.token}` } })
|
|
80
|
+
check('/api/users/me 返回 admin 身份', me.status === 200 && me.body?.data?.role === 'admin')
|
|
81
|
+
|
|
82
|
+
// ---- 4. 注册页签收敛后再注册第二账号应降为普通 user ----
|
|
83
|
+
const reg2 = await api('/api/users/register', { method: 'POST', body: JSON.stringify({ name: 'plainuser', email: 'plain@awshop.local', password: 'Plain2026' }) })
|
|
84
|
+
check('后续注册为普通 user 角色', reg2.status === 200 && reg2.body?.data?.user?.role === 'user', JSON.stringify(reg2.body?.data?.user))
|
|
85
|
+
|
|
86
|
+
const failed = results.filter(r => !r.ok)
|
|
87
|
+
console.log(`\n===== setup-flow E2E: ${results.length - failed.length}/${results.length} 通过 =====`)
|
|
88
|
+
process.exit(failed.length ? 1 : 0)
|