agentworkshop 0.7.39 → 0.7.41

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.
Files changed (405) hide show
  1. package/.AgentWorkShop/prompts/host-tools.json +55 -0
  2. package/.output/.AgentWorkShop/prompts/host-tools.json +55 -0
  3. package/.output/nitro.json +1 -1
  4. package/.output/public/_nuxt/88GOowgO2.js +1 -0
  5. package/.output/public/_nuxt/{BNT8gYU92.js → BC9wjTJK2.js} +2 -2
  6. package/.output/public/_nuxt/{CY9LbPCI.js → BN16aRdQ.js} +1 -1
  7. package/.output/public/_nuxt/BWVhZntO.js +1 -0
  8. package/.output/public/_nuxt/{HBLJ-wei.js → BXxH0UQE.js} +1 -1
  9. package/.output/public/_nuxt/{zIGh7R49.js → BneN7fbu.js} +1 -1
  10. package/.output/public/_nuxt/BsROmY89.js +1 -0
  11. package/.output/public/_nuxt/{DUrecWPx.js → C0ZrS1KL.js} +2 -2
  12. package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
  13. package/.output/public/_nuxt/{CUDwzgLV.js → C4s_niy-.js} +9 -9
  14. package/.output/public/_nuxt/CFxtLwjp.js +1 -0
  15. package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
  16. package/.output/public/_nuxt/CssgYAAL2.js +1 -0
  17. package/.output/public/_nuxt/D6mbFA-a.js +1 -0
  18. package/.output/public/_nuxt/D98NH8kG.js +1 -0
  19. package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
  20. package/.output/public/_nuxt/DGbd-UiG.js +1 -0
  21. package/.output/public/_nuxt/{CojCTLKD.js → DGj-WEBg.js} +1 -1
  22. package/.output/public/_nuxt/{DDShH71m.js → DS8qj8EL.js} +1 -1
  23. package/.output/public/_nuxt/DTFCy6h-.js +1 -0
  24. package/.output/public/_nuxt/{DrYV5i68.js → DUjW5Yq5.js} +1 -1
  25. package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
  26. package/.output/public/_nuxt/Dap5PmTL.js +2 -0
  27. package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
  28. package/.output/public/_nuxt/DvLxILQP.js +1 -0
  29. package/.output/public/_nuxt/IREZ1eC2.js +1 -0
  30. package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
  31. package/.output/public/_nuxt/_id_.D9ymtHB5.css +1 -0
  32. package/.output/public/_nuxt/_id_.jf-_8E_c.css +1 -0
  33. package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
  34. package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
  35. package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
  36. package/.output/public/_nuxt/builds/latest.json +1 -1
  37. package/.output/public/_nuxt/builds/meta/78a295bb-2072-48a4-8b46-6d545fdae743.json +1 -0
  38. package/.output/public/_nuxt/ca4TJ3Op.js +1 -0
  39. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
  40. package/.output/public/_nuxt/daq.DjSgY94l.css +1 -0
  41. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
  42. package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
  43. package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -0
  44. package/.output/public/_nuxt/eqq3wWQF.js +18 -0
  45. package/.output/public/_nuxt/logs.BpKzmKsh.css +1 -0
  46. package/.output/public/_nuxt/monitor.D9pe7qN6.css +1 -0
  47. package/.output/public/_nuxt/pages.D7aSDlUI.css +1 -0
  48. package/.output/public/_nuxt/permissions.DwTVlFe6.css +1 -0
  49. package/.output/public/_nuxt/plugins.sb4nDGqr.css +1 -0
  50. package/.output/public/_nuxt/{Cbq-mryP.js → qr6R6Ha1.js} +1 -1
  51. package/.output/public/_nuxt/settings.BTgR-NiE.css +1 -0
  52. package/.output/public/_nuxt/teams.CxnxKVVs.css +1 -0
  53. package/.output/public/_nuxt/tokens.BFY7qv4f.css +1 -0
  54. package/.output/public/_nuxt/{town.DKIAhKnx.css → town.BBXZmeoP.css} +1 -1
  55. package/.output/public/_nuxt/users.DLgZ1D0h.css +1 -0
  56. package/.output/public/_nuxt/workshop.CR_QzeAN.css +1 -0
  57. package/.output/public/_nuxt/{BzHEilTt.js → xh3_vYcu.js} +1 -1
  58. package/.output/public/_nuxt/yF0aYMF1.js +4 -0
  59. package/.output/server/chunks/_/entity.mjs.map +1 -1
  60. package/.output/server/chunks/_/nitro.mjs +2355 -1362
  61. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  62. package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView--otsnr_7.mjs} +290 -208
  63. package/.output/server/chunks/build/TownView--otsnr_7.mjs.map +1 -0
  64. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-Brv-JSvr.mjs} +142 -141
  65. package/.output/server/chunks/build/_id_-Brv-JSvr.mjs.map +1 -0
  66. package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-D9pl1OZy.mjs} +38 -36
  67. package/.output/server/chunks/build/_id_-D9pl1OZy.mjs.map +1 -0
  68. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BASfDf0s.mjs} +295 -261
  69. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BASfDf0s.mjs.map} +1 -1
  70. package/.output/server/chunks/build/{agents-Bvin73QY.mjs → agents-CLQqJF2y.mjs} +21 -27
  71. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +1 -0
  72. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs → aml-_7FdFL2p.mjs} +109 -109
  73. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs.map → aml-_7FdFL2p.mjs.map} +1 -1
  74. package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -1
  75. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs → channel-templates-p6SZNjlM.mjs} +19 -19
  76. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs.map → channel-templates-p6SZNjlM.mjs.map} +1 -1
  77. package/.output/server/chunks/build/{daq-C03Uu5UE.mjs → daq-DkMvD8sg.mjs} +193 -126
  78. package/.output/server/chunks/build/daq-DkMvD8sg.mjs.map +1 -0
  79. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-C1jgVJkO.mjs} +46 -46
  80. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-C1jgVJkO.mjs.map} +1 -1
  81. package/.output/server/chunks/build/{default-CZmZFpqH.mjs → default-BE4r0FZN.mjs} +152 -58
  82. package/.output/server/chunks/build/{default-CZmZFpqH.mjs.map → default-BE4r0FZN.mjs.map} +1 -1
  83. package/.output/server/chunks/build/{logs-B-xhuqln.mjs → logs--dByNyhw.mjs} +35 -35
  84. package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs--dByNyhw.mjs.map} +1 -1
  85. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs → monitor-C0okQaXz.mjs} +33 -33
  86. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs.map → monitor-C0okQaXz.mjs.map} +1 -1
  87. package/.output/server/chunks/build/{pages-BO7yZsXA.mjs → pages-B0bUWnC6.mjs} +35 -19
  88. package/.output/server/chunks/build/pages-B0bUWnC6.mjs.map +1 -0
  89. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs → permissions-68SsNjex.mjs} +12 -12
  90. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs.map → permissions-68SsNjex.mjs.map} +1 -1
  91. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs → plugins-lGlUr1lj.mjs} +17 -17
  92. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs.map → plugins-lGlUr1lj.mjs.map} +1 -1
  93. package/.output/server/chunks/build/{settings-DoQ2Oq3P.mjs → settings-Ddd1qx2Z.mjs} +50 -47
  94. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +1 -0
  95. package/.output/server/chunks/build/{teams-2kz3CusY.mjs → teams-MEDLZdMn.mjs} +38 -34
  96. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +1 -0
  97. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs → tokens-C7fTM5C2.mjs} +14 -14
  98. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs.map → tokens-C7fTM5C2.mjs.map} +1 -1
  99. package/.output/server/chunks/build/{town-BOl6hs9B.mjs → town-CoQdblbZ.mjs} +38 -12
  100. package/.output/server/chunks/build/town-CoQdblbZ.mjs.map +1 -0
  101. package/.output/server/chunks/build/{useDaqStream-CIBaiGAl.mjs → useDaqStream-DfCFpb3p.mjs} +9 -3
  102. package/.output/server/chunks/build/useDaqStream-DfCFpb3p.mjs.map +1 -0
  103. package/.output/server/chunks/build/{useDcwStream-CBbiYnWe.mjs → useDcwStream-UPiX-3iO.mjs} +11 -2
  104. package/.output/server/chunks/build/useDcwStream-UPiX-3iO.mjs.map +1 -0
  105. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
  106. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
  107. package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
  108. package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
  109. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
  110. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
  111. package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
  112. package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
  113. package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
  114. package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
  115. package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
  116. package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
  117. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
  118. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
  120. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
  121. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  122. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  123. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs.map +1 -1
  124. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
  125. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
  126. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
  127. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
  128. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
  129. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
  131. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
  133. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
  134. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
  135. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
  137. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
  139. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
  140. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
  141. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
  142. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
  143. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
  144. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
  145. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
  146. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
  147. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
  148. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
  149. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
  150. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
  151. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
  152. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
  153. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
  154. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
  155. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  156. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  157. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  159. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  160. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  161. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  162. package/.output/server/chunks/routes/api/workshop/index5.get.mjs +3 -1
  163. package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
  164. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +5 -1
  165. package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
  166. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -1
  167. package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
  168. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  169. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  170. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  171. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  172. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  173. package/.output/server/chunks/routes/renderer.mjs +3 -3
  174. package/.output/server/chunks/virtual/entry.mjs +51 -25
  175. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  176. package/.output/server/package.json +1 -1
  177. package/README-zh.md +203 -39
  178. package/README.md +739 -591
  179. package/app/app.vue +9 -3
  180. package/app/assets/css/main.css +517 -7
  181. package/app/components/AppHeader.vue +98 -7
  182. package/app/components/AppSidebar.vue +117 -11
  183. package/app/components/AwChart.vue +2 -1
  184. package/app/components/workshop/AgentInspectorDrawer.vue +6 -6
  185. package/app/components/workshop/AgentLanesView.vue +52 -1
  186. package/app/components/workshop/AssetLibrary.vue +4 -2
  187. package/app/components/workshop/ChannelSessionList.vue +4 -2
  188. package/app/components/workshop/Composer.vue +96 -0
  189. package/app/components/workshop/DaqNodeRow.vue +19 -0
  190. package/app/components/workshop/DaqTemplateIcon.vue +33 -0
  191. package/app/components/workshop/DeviceTwinPanel.vue +37 -35
  192. package/app/components/workshop/EventBlock.vue +3 -3
  193. package/app/components/workshop/InspectorPanel.vue +47 -11
  194. package/app/components/workshop/LiveAgentBar.vue +34 -0
  195. package/app/components/workshop/TaskBoardView.vue +83 -33
  196. package/app/components/workshop/TranscriptTimeline.vue +56 -0
  197. package/app/components/workshop/blocks/ClusterError.vue +1 -1
  198. package/app/components/workshop/blocks/ClusterRoute.vue +3 -1
  199. package/app/components/workshop/blocks/ClusterStatus.vue +2 -2
  200. package/app/components/workshop/blocks/ClusterStream.vue +8 -2
  201. package/app/components/workshop/town/ModelPreview3D.vue +1 -1
  202. package/app/components/workshop/town/TownView.vue +507 -82
  203. package/app/composables/useResponsive.ts +88 -0
  204. package/app/composables/useRouteMeta.ts +3 -0
  205. package/app/composables/workshop/useDaqStream.ts +7 -4
  206. package/app/composables/workshop/useDcwStream.ts +8 -2
  207. package/app/composables/workshop/useVisibleInterval.ts +2 -2
  208. package/app/composables/workshop/useWorkshopApi.ts +1 -1
  209. package/app/layouts/default.vue +46 -2
  210. package/app/pages/aml/index.vue +712 -616
  211. package/app/pages/daq/[id].vue +12 -7
  212. package/app/pages/daq/index.vue +216 -13
  213. package/app/pages/dcw/[id].vue +117 -32
  214. package/app/pages/dcw/index.vue +83 -23
  215. package/app/pages/index.vue +35 -1
  216. package/app/pages/logs/index.vue +147 -13
  217. package/app/pages/monitor.vue +56 -12
  218. package/app/pages/permissions/index.vue +34 -0
  219. package/app/pages/plugins/index.vue +35 -4
  220. package/app/pages/settings.vue +90 -21
  221. package/app/pages/tokens.vue +23 -0
  222. package/app/pages/town.vue +50 -5
  223. package/app/pages/users.vue +23 -0
  224. package/app/pages/workshop/agents.vue +92 -2
  225. package/app/pages/workshop/channel-templates.vue +53 -0
  226. package/app/pages/workshop/index.vue +14 -0
  227. package/app/pages/workshop/teams.vue +66 -6
  228. package/app/pages/workshop/w/[wsId].vue +202 -14
  229. package/app/plugins/aw-plugins.client.ts +13 -8
  230. package/app/stores/app.ts +33 -1
  231. package/app/stores/workshop/user.ts +1 -1
  232. package/app/stores/workshop/workspaces.ts +18 -2
  233. package/app/utils/api-error.ts +1 -1
  234. package/cli/commands/build.mjs +8 -1
  235. package/docker-compose.yml +6 -0
  236. package/docs/cli.en.md +2 -2
  237. package/docs/cli.md +2 -2
  238. package/docs/plugins.en.md +33 -1
  239. package/docs/plugins.md +29 -1
  240. package/docs/sdk.en.md +1 -1
  241. package/docs/sdk.md +1 -1
  242. package/i18n/locales/en.ts +2 -0
  243. package/i18n/locales/zh-CN.ts +2 -0
  244. package/nuxt.config.ts +31 -0
  245. package/package.json +11 -2
  246. package/scripts/_dbg-param-map-e2e.ts +242 -0
  247. package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
  248. package/scripts/_dbg-param-real-scenario.mjs +153 -0
  249. package/scripts/_loop-biax-campaign.mjs +124 -0
  250. package/scripts/api-live-e2e.mjs +40 -8
  251. package/scripts/capture-walkthrough.mjs +232 -0
  252. package/scripts/render-walkthrough-figure.mjs +46 -0
  253. package/scripts/seed-walkthrough-fixture.mjs +143 -0
  254. package/scripts/test-plugin-hardening.mjs +8 -0
  255. package/scripts/test-serial-bridge.mjs +418 -0
  256. package/scripts/ui/admin-shots.mjs +46 -0
  257. package/scripts/ui/audit-layout.mjs +301 -0
  258. package/scripts/ui/bump-microfont.mjs +39 -0
  259. package/scripts/ui/e2e-interactive.mjs +328 -0
  260. package/scripts/ui/lib.mjs +198 -0
  261. package/scripts/ui/linkcheck2.mjs +37 -0
  262. package/scripts/ui/live-render-check.mjs +72 -0
  263. package/scripts/ui/md-lint.mjs +80 -0
  264. package/scripts/ui/perf-audit.mjs +132 -0
  265. package/scripts/ui/readme-assets.mjs +30 -0
  266. package/scripts/ui/readme-shots.mjs +42 -0
  267. package/scripts/ui/record.mjs +225 -0
  268. package/scripts/ui/seed-admin.mjs +68 -0
  269. package/scripts/ui/seed-demo.mjs +83 -0
  270. package/scripts/ui/seed-industrial-demo.mjs +171 -0
  271. package/scripts/ui/shot-hero.mjs +25 -0
  272. package/scripts/ui/shots.mjs +75 -0
  273. package/scripts/ui/sim-ui-shot.mjs +16 -0
  274. package/scripts/ui/verify-nav.mjs +51 -0
  275. package/scripts/ui/verify-responsive.mjs +285 -0
  276. package/server/api/system/config-groups/[id].patch.ts +1 -1
  277. package/server/api/system/config-groups/index.post.ts +1 -1
  278. package/server/api/system/settings/events.get.ts +1 -1
  279. package/server/api/system/settings/index.patch.ts +1 -1
  280. package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
  281. package/server/api/workshop/aml/datasets/[id].get.ts +8 -1
  282. package/server/api/workshop/daq/index.get.ts +3 -1
  283. package/server/api/workshop/daq/infra/reconnect.post.ts +1 -1
  284. package/server/api/workshop/daq/templates/[key].delete.ts +1 -0
  285. package/server/api/workshop/daq/templates/[key].patch.ts +1 -0
  286. package/server/api/workshop/daq/templates/index.post.ts +1 -0
  287. package/server/api/workshop/dcw/[id]/write.post.ts +10 -2
  288. package/server/api/workshop/dcw/[id].delete.ts +5 -2
  289. package/server/api/workshop/dcw/index.get.ts +5 -0
  290. package/server/api/workshop/dcw/lines/[id].patch.ts +1 -1
  291. package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -0
  292. package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -0
  293. package/server/api/workshop/dcw/params/[id].delete.ts +19 -0
  294. package/server/api/workshop/dcw/params/[id].patch.ts +23 -0
  295. package/server/api/workshop/dcw/params/index.get.ts +27 -0
  296. package/server/api/workshop/dcw/params/index.post.ts +25 -0
  297. package/server/api/workshop/permissions/index.get.ts +3 -1
  298. package/server/plugins/aw-plugins.ts +23 -10
  299. package/server/plugins/dev-stability-guard.ts +3 -1
  300. package/server/plugins/system-config.ts +6 -1
  301. package/server/plugins-builtin/serial-bridge/client.mjs +210 -0
  302. package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -0
  303. package/server/plugins-builtin/serial-bridge/i18n.json +62 -0
  304. package/server/plugins-builtin/serial-bridge/index.mjs +247 -0
  305. package/server/plugins-builtin/serial-bridge/lib.mjs +208 -0
  306. package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -0
  307. package/server/services/system-config.ts +28 -9
  308. package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
  309. package/server/services/workshop/agents/agent-interface.ts +8 -6
  310. package/server/services/workshop/agents/base-agent.ts +18 -4
  311. package/server/services/workshop/agents/claude-agent.ts +7 -3
  312. package/server/services/workshop/agents/codex-agent.ts +3 -3
  313. package/server/services/workshop/agents/dsh-agent.ts +3 -3
  314. package/server/services/workshop/agents/goose-agent.ts +0 -1
  315. package/server/services/workshop/agents/harness-terminal.ts +14 -1
  316. package/server/services/workshop/agents/hermes-agent.ts +4 -2
  317. package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
  318. package/server/services/workshop/agents/industrial-context.ts +4 -1
  319. package/server/services/workshop/agents/industrial-tools.ts +164 -6
  320. package/server/services/workshop/agents/omp-agent.ts +15 -9
  321. package/server/services/workshop/agents/opencode-agent.ts +47 -21
  322. package/server/services/workshop/agents/qwen-agent.ts +10 -5
  323. package/server/services/workshop/aml/entity.ts +3 -1
  324. package/server/services/workshop/aml/python-runtime.ts +3 -2
  325. package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
  326. package/server/services/workshop/daq/daq-controller.ts +25 -5
  327. package/server/services/workshop/daq/daq-runtime.ts +5 -3
  328. package/server/services/workshop/daq/drivers.ts +141 -11
  329. package/server/services/workshop/daq/host-ports.ts +3 -2
  330. package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
  331. package/server/services/workshop/db/channel.repo.ts +4 -0
  332. package/server/services/workshop/db/ops.repo.ts +5 -2
  333. package/server/services/workshop/db/retention.ts +8 -3
  334. package/server/services/workshop/dcw/dcw-controller.ts +147 -16
  335. package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
  336. package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
  337. package/server/services/workshop/dcw/drivers.ts +90 -4
  338. package/server/services/workshop/dcw/param-conversion.ts +78 -0
  339. package/server/services/workshop/dcw/param-limits.ts +142 -0
  340. package/server/services/workshop/dcw/param-map.repo.ts +305 -0
  341. package/server/services/workshop/dcw/plugin-bridge.ts +45 -0
  342. package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
  343. package/server/services/workshop/plugins/host.mjs +30 -0
  344. package/server/services/workshop/runtime/agent-runtime.ts +4 -0
  345. package/server/services/workshop/runtime/manager.ts +9 -2
  346. package/server/services/workshop/settings.ts +4 -2
  347. package/server/types/pg.d.ts +49 -0
  348. package/server/utils/stability-severity.ts +13 -2
  349. package/shared/config/groups.mjs +22 -1
  350. package/shared/config/home.d.mts +28 -0
  351. package/shared/daq-protocol.ts +15 -0
  352. package/shared/dcw-protocol.ts +136 -0
  353. package/uno.config.ts +12 -1
  354. package/.output/public/_nuxt/8NMOXZOV.js +0 -1
  355. package/.output/public/_nuxt/BFkgkW8u.js +0 -4
  356. package/.output/public/_nuxt/C5gUkRwn.js +0 -1
  357. package/.output/public/_nuxt/CM12FuB42.js +0 -1
  358. package/.output/public/_nuxt/CZiw7sEV.js +0 -1
  359. package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
  360. package/.output/public/_nuxt/CdPzkOMV.js +0 -1
  361. package/.output/public/_nuxt/CmsOtOAA2.js +0 -1
  362. package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
  363. package/.output/public/_nuxt/D20Yamr4.js +0 -1
  364. package/.output/public/_nuxt/DAe0h2MB.js +0 -1
  365. package/.output/public/_nuxt/DmqknNvV.js +0 -18
  366. package/.output/public/_nuxt/DnUuIR83.js +0 -1
  367. package/.output/public/_nuxt/S_fVQwJK.js +0 -1
  368. package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
  369. package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
  370. package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
  371. package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
  372. package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
  373. package/.output/public/_nuxt/ahgEJu2R.js +0 -1
  374. package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
  375. package/.output/public/_nuxt/builds/meta/4457d63f-0866-4cea-b320-04a321f24795.json +0 -1
  376. package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
  377. package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
  378. package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
  379. package/.output/public/_nuxt/default.BXI_fZUi.css +0 -1
  380. package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
  381. package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
  382. package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
  383. package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
  384. package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
  385. package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
  386. package/.output/public/_nuxt/pz2JDAM7.js +0 -2
  387. package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
  388. package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
  389. package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
  390. package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
  391. package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
  392. package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
  393. package/.output/server/chunks/build/_id_-CfVosNg3.mjs.map +0 -1
  394. package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
  395. package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
  396. package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
  397. package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
  398. package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
  399. package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
  400. package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
  401. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs.map +0 -1
  402. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs.map +0 -1
  403. package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
  404. /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
  405. /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * 真实流程录屏 → GIF(README / 文档站用)。
3
+ *
4
+ * 为什么不用现成录屏工具:这些 GIF 要展示的是**系统的真实行为**
5
+ * (Agent 真的在跑、DAQ 数值真的在变),所以必须是一段"操作剧本"驱动真实页面,
6
+ * 再逐帧采集。所有帧都来自 127.0.0.1:3021 上跑着的真服务,没有任何摆拍。
7
+ *
8
+ * 管线: puppeteer 定频截图 → ffmpeg palettegen/paletteuse → 有界 GIF
9
+ *
10
+ * 用法: node scripts/ui/record.mjs <story> [--out docs/readme-assets] [--w 1440] [--h 900]
11
+ * node scripts/ui/record.mjs --list
12
+ */
13
+ import fs from 'node:fs'
14
+ import path from 'node:path'
15
+ import { execFileSync } from 'node:child_process'
16
+ import { ensureVisualUser, api, launch, openPage, gotoReady, sleep } from './lib.mjs'
17
+
18
+ const argv = process.argv.slice(2)
19
+ const arg = (k, d) => {
20
+ const i = argv.indexOf(`--${k}`)
21
+ return i >= 0 ? argv[i + 1] : d
22
+ }
23
+ const OUT = arg('out', 'docs/readme-assets')
24
+ const W = Number(arg('w', 1440))
25
+ const H = Number(arg('h', 900))
26
+ const FPS = Number(arg('fps', 12))
27
+ const GIF_W = Number(arg('gifw', 960))
28
+
29
+ const token = await ensureVisualUser()
30
+
31
+ /** 找到示范产线的频道(seed-demo.mjs 建的) */
32
+ async function demoChannels() {
33
+ const r = await api('GET', '/api/workshop/channels', { token })
34
+ const list = r.data?.channels ?? r.data ?? []
35
+ return Array.isArray(list) ? list : []
36
+ }
37
+
38
+ /* ─────────────────────────── 剧本 ─────────────────────────── */
39
+
40
+ const STORIES = {
41
+ /** 01 · Agent 团队真实跑一次任务:派发 → 拆解 → 并行执行 → 三个视图收敛 */
42
+ agents: {
43
+ title: 'Agent 团队执行任务',
44
+ async run(page, play) {
45
+ const chs = await demoChannels()
46
+ const ch = chs.find(c => c.name === '熔温优化') ?? chs[0]
47
+ if (!ch) throw new Error('没有可用频道,先跑 scripts/ui/seed-demo.mjs')
48
+ const cid = ch.channelId ?? ch.id
49
+ // ⚠️ 路由参数是 **workspaceId**,不是 channelId(/workshop/w/:wsId 里
50
+ // channelId 由活动工作区推导)。传 channelId 会落到空壳上。
51
+ const wsRes = await api('GET', '/api/workshop/workspaces', { token })
52
+ const wsList = wsRes.data?.workspaces ?? wsRes.data ?? []
53
+ const wid = (Array.isArray(wsList) ? wsList[0] : null)?.id
54
+ if (!wid) throw new Error('没有工作区,先跑 scripts/ui/seed-demo.mjs')
55
+
56
+ await gotoReady(page, `/workshop/w/${wid}`, { wait: 4500 })
57
+ await play(1500)
58
+
59
+ const goal = '分析 1 号挤出线的熔体温度趋势,给出把温控设定值下调 5°C 的依据与风险。'
60
+ // 用**和界面同一个 REST 入口**派发任务,而不是往 composer 里模拟打字:
61
+ // 富文本 composer 的首个 textarea 未必是它本体(踩过:录了 30 秒空时间线),
62
+ // 而"事件真的在流"才是这段 GIF 要证明的东西。
63
+ const sent = await api('POST', `/api/workshop/channels/${cid}/tasks`, {
64
+ token,
65
+ body: { title: goal, mode: 'goal', fromLabel: 'visual' },
66
+ })
67
+ if (sent.code !== 0) console.log(' ⚠ 派发失败:', JSON.stringify(sent).slice(0, 160))
68
+ await play(9000)
69
+
70
+ // 依次切到 Agent lanes / 任务板,展示"同一批事件的不同切面"
71
+ for (const tab of ['Agent lanes', '任务板', '时间线']) {
72
+ await page.evaluate((label) => {
73
+ const el = Array.from(document.querySelectorAll('button, .aw-seg button, [role="tab"]'))
74
+ .find(b => (b.textContent || '').trim() === label)
75
+ el?.click()
76
+ }, tab)
77
+ await play(tab === '任务板' ? 7000 : 6000)
78
+ }
79
+ await play(4000)
80
+ },
81
+ },
82
+
83
+ /** 02 · 数采实时链路:节点在线、读数跳动、趋势成形 */
84
+ daq: {
85
+ title: 'DAQ 实时采集',
86
+ async run(page, play) {
87
+ await gotoReady(page, '/daq', { wait: 4500 })
88
+ await play(6000)
89
+ // 展开「实时事件」流,展示消息在滚动
90
+ await page.evaluate(() => {
91
+ const els = Array.from(document.querySelectorAll('*'))
92
+ const t = els.find(e => e.children.length === 0 && (e.textContent || '').includes('实时事件'))
93
+ t?.scrollIntoView({ block: 'center' })
94
+ })
95
+ await play(7000)
96
+ await page.evaluate(() => window.scrollTo({ top: 0, behavior: 'smooth' }))
97
+ await play(3500)
98
+ },
99
+ },
100
+
101
+ /** 03 · 产线作业:节点卡片 + 展开一条产线看工艺链 */
102
+ dcw: {
103
+ title: '产线作业与写控制',
104
+ async run(page, play) {
105
+ await gotoReady(page, '/dcw', { wait: 4500 })
106
+ await play(4000)
107
+ await page.evaluate(() => window.scrollBy({ top: 260, behavior: 'smooth' }))
108
+ await play(4500)
109
+ await page.evaluate(() => window.scrollBy({ top: -260, behavior: 'smooth' }))
110
+ await play(3000)
111
+ },
112
+ },
113
+
114
+ /** 04 · 数字孪生:真实遥测驱动的 3D 产线 */
115
+ town: {
116
+ title: '三维数字孪生',
117
+ async run(page, play) {
118
+ await gotoReady(page, '/town', { wait: 9000 })
119
+ await play(9000)
120
+ // 缓慢平移视角(鼠标拖拽画布中心)
121
+ const box = await page.evaluate(() => {
122
+ const c = document.querySelector('canvas')
123
+ if (!c) return null
124
+ const r = c.getBoundingClientRect()
125
+ return { x: r.x + r.width / 2, y: r.y + r.height / 2 }
126
+ })
127
+ if (box) {
128
+ await page.mouse.move(box.x, box.y)
129
+ await page.mouse.down()
130
+ for (let i = 0; i < 24; i++) {
131
+ await page.mouse.move(box.x - i * 6, box.y - i * 1.2)
132
+ await play(60)
133
+ }
134
+ await page.mouse.up()
135
+ }
136
+ await play(8000)
137
+ },
138
+ },
139
+
140
+ /** 05 · 仪表盘:全局 KPI 与趋势 */
141
+ dashboard: {
142
+ title: '运行总览仪表盘',
143
+ async run(page, play) {
144
+ await gotoReady(page, '/', { wait: 5000 })
145
+ await play(8000)
146
+ await page.evaluate(() => window.scrollBy({ top: 420, behavior: 'smooth' }))
147
+ await play(7000)
148
+ },
149
+ },
150
+
151
+ /** 06 · 响应式:同一套界面在桌面 / 平板 / 手机上的形态 */
152
+ responsive: {
153
+ title: '自适应:桌面 / 平板 / 手机',
154
+ async run(page, play) {
155
+ await gotoReady(page, '/daq', { wait: 5000 })
156
+ await play(2500)
157
+ const steps = [
158
+ { w: 1024, h: 768, wait: 2000 },
159
+ { w: 768, h: 1024, wait: 2000 },
160
+ { w: 390, h: 844, wait: 2400 },
161
+ ]
162
+ for (const s of steps) {
163
+ await page.setViewport({ width: s.w, height: s.h, deviceScaleFactor: 1 })
164
+ await play(s.wait)
165
+ }
166
+ // 手机上拉开抽屉导航
167
+ await page.evaluate(() => {
168
+ document.querySelector('.collapse-btn')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
169
+ })
170
+ await play(3000)
171
+ },
172
+ },
173
+ }
174
+
175
+ // 剧本名 = 第一个**是已知剧本**的位置参数。
176
+ // (不能只判"前一个参数不是 --flag":首位参数的前一个参数是 undefined,
177
+ // 会被误判成"某个 flag 的值",于是永远解析不出剧本名 —— 实测踩过。)
178
+ const storyName = argv.find(a => !a.startsWith('--') && Object.hasOwn(STORIES, a))
179
+
180
+ if (argv.includes('--list') || !storyName || !STORIES[storyName]) {
181
+ console.log('可用剧本:')
182
+ for (const [k, v] of Object.entries(STORIES)) console.log(` ${k.padEnd(12)} ${v.title}`)
183
+ process.exit(0)
184
+ }
185
+
186
+ /* ─────────────────────── 采集 + 合成 ─────────────────────── */
187
+
188
+ const tmp = path.join('.e2e-shots', 'gif-frames', storyName)
189
+ fs.rmSync(tmp, { recursive: true, force: true })
190
+ fs.mkdirSync(tmp, { recursive: true })
191
+ // 输出目录必须显式建:ffmpeg 不会替你 mkdir,它会以 "Could not open file" 失败,
192
+ // 而 -loglevel error 下错误只出现在 stderr 末尾,很容易被误读成编码参数错误(实测踩过)。
193
+ fs.mkdirSync(OUT, { recursive: true })
194
+
195
+ const browser = await launch()
196
+ const page = await openPage(browser, { token, dark: true, width: W, height: H, deviceScaleFactor: 1 })
197
+ const errs = []
198
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 200)))
199
+
200
+ let n = 0
201
+ let stopped = false
202
+ /** 播放一段"时间":按 FPS 定频采样,长度 = ms */
203
+ async function play(ms) {
204
+ const frames = Math.max(1, Math.round((ms / 1000) * FPS))
205
+ for (let i = 0; i < frames && !stopped; i++) {
206
+ try {
207
+ await page.screenshot({ path: path.join(tmp, `f${String(n++).padStart(5, '0')}.png`) })
208
+ }
209
+ catch { /* 过渡帧丢失可接受 */ }
210
+ await sleep(Math.round(1000 / FPS))
211
+ }
212
+ }
213
+
214
+ console.log(`▶ 录制「${STORIES[storyName].title}」…`)
215
+ await STORIES[storyName].run(page, play)
216
+ stopped = true
217
+ await browser.close()
218
+ console.log(` 帧数 ${n}`)
219
+
220
+ const gif = path.join(OUT, `${storyName}.gif`)
221
+ const vf = `fps=${FPS},scale=${GIF_W}:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=192:stats_mode=diff[p];[s1][p]paletteuse=dither=bayer:bayer_scale=3:diff_mode=rectangle`
222
+ execFileSync('ffmpeg', ['-y', '-loglevel', 'error', '-i', path.join(tmp, 'f%05d.png'), '-vf', vf, '-loop', '0', gif], { stdio: 'inherit' })
223
+ const size = fs.statSync(gif).size
224
+ console.log(`✔ ${gif} (${(size / 1024 / 1024).toFixed(2)} MB, ${n} 帧)`)
225
+ if (errs.length) console.log(' ⚠ page errors:', errs.slice(0, 4))
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 给 admin 账号准备一套可看的演示数据(工作区 + 3 个频道 + 挂载)。
3
+ * 幂等:已存在就不重复建。admin 是文档里写的管理员账号(admin@awshop.local),
4
+ * 而演示工作区原本挂在另一个账号下 —— 不补的话 /town 与 /workshop 会是空态。
5
+ *
6
+ * ⚠️ 本文件的 api() 是**位置参数** (m, p, body, token);别照抄 lib.mjs 的
7
+ * options-object 写法 —— 混用会把 {body,token} 整体当成请求体序列化出去,
8
+ * 服务端 zod 校验失败但外层仍是 200,排查时极具迷惑性(已踩)。
9
+ */
10
+ const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3021'
11
+ const api = async (m, p, body, token) => {
12
+ const h = { 'content-type': 'application/json' }
13
+ if (token) h.authorization = 'Bearer ' + token
14
+ const r = await fetch(BASE + p, { method: m, headers: h, body: body ? JSON.stringify(body) : undefined })
15
+ const t = await r.text()
16
+ try {
17
+ return JSON.parse(t)
18
+ }
19
+ catch {
20
+ return { code: -1, message: 'HTTP ' + r.status }
21
+ }
22
+ }
23
+ const login = await api('POST', '/api/users/login', { email: 'admin@awshop.local', password: 'admin123' })
24
+ const token = login.data.token
25
+ console.log('admin:', login.data.user.name, login.data.user.role)
26
+
27
+ const WANT = [
28
+ { name: '熔温优化', prompt: '分析熔体温度趋势并优化设定值:读取历史,统计与异常剔除,给出新设定值建议,走 HITL 审批后写入 PLC 并回读校验。', lead: 'melt-lead', workers: ['trend-analyst', 'setpoint-writer'] },
29
+ { name: '厚度扫描诊断', prompt: '厚度扫描仪多点剖面诊断:定位横向厚度偏差来源,给出模头螺栓调整建议。', lead: 'scan-lead', workers: ['profile-analyst'] },
30
+ { name: '配方调优', prompt: '配方参数寻优:在工艺窗口内搜索更优参数组合,记录每次试验并给出推荐版本。', lead: 'recipe-lead', workers: ['doe-runner'] },
31
+ ]
32
+
33
+ let wsList = await api('GET', '/api/workshop/workspaces', null, token)
34
+ let list = wsList.data ?? []
35
+ let ws = list.find(w => w.name === '示范产线')
36
+ if (!ws) {
37
+ const r = await api('POST', '/api/workshop/workspaces', { name: '示范产线' }, token)
38
+ ws = r.data
39
+ }
40
+ const wsId = ws?.id
41
+ if (!wsId) throw new Error('工作区创建失败: ' + JSON.stringify(wsList).slice(0, 200))
42
+ console.log('workspace:', wsId, ws.name)
43
+
44
+ const chList = await api('GET', '/api/workshop/channels', null, token)
45
+ const all = chList.data?.channels ?? chList.data ?? []
46
+ for (const spec of WANT) {
47
+ let ch = (Array.isArray(all) ? all : []).find(c => c.name === spec.name)
48
+ if (!ch) {
49
+ const r = await api('POST', '/api/workshop/channels', {
50
+ name: spec.name,
51
+ scenarioPrompt: spec.prompt,
52
+ leadAgent: { name: spec.lead, harness: 'mock', config: { delayMs: 260, streamDemo: true } },
53
+ }, token)
54
+ ch = r.data
55
+ if (!ch) {
56
+ console.log(' 建频道失败', spec.name, JSON.stringify(r).slice(0, 150))
57
+ continue
58
+ }
59
+ for (const w of spec.workers) {
60
+ await api('POST', `/api/workshop/channels/${ch.channelId ?? ch.id}/agents`, { name: w, harness: 'mock', role: 'worker', config: { delayMs: 300, streamDemo: true } }, token)
61
+ }
62
+ }
63
+ const cid = ch.channelId ?? ch.id
64
+ await api('POST', `/api/workshop/workspaces/${wsId}/channels/${cid}`, {}, token)
65
+ console.log(' 挂载:', spec.name, cid)
66
+ }
67
+ const after = await api('GET', '/api/workshop/workspaces', null, token)
68
+ console.log('workspaces now:', JSON.stringify(after.data).slice(0, 300))
@@ -0,0 +1,83 @@
1
+ /**
2
+ * 视觉走查用示范数据集 —— 用 visual 账号通过公开 REST 建一套「像真产线」的工作区:
3
+ * 工作区 + 3 个语义化频道(mock harness, 带 streamDemo 流式事件) + 一次真实派发。
4
+ *
5
+ * 为什么不复用仓库里已有的 43 个频道:它们是压测遗留的 team-ip16i7gy 之类机读名,
6
+ * 拿来当 README/GIF 的素材会让截图说不出故事。这里只新增,不改动既有数据。
7
+ */
8
+ import { ensureVisualUser, api } from './lib.mjs'
9
+
10
+ const token = await ensureVisualUser()
11
+
12
+ const CHANNELS = [
13
+ {
14
+ name: '熔温优化',
15
+ scenarioPrompt: '分析熔体温度趋势并优化设定值:读取近 24h 历史,做统计与异常剔除,给出新设定值建议,走 HITL 审批后写入 PLC 并回读校验。',
16
+ lead: 'melt-lead',
17
+ workers: [
18
+ { name: 'trend-analyst', role: 'worker' },
19
+ { name: 'setpoint-writer', role: 'worker' },
20
+ ],
21
+ goal: '分析 1 号挤出线的熔体温度趋势,给出把温控设定值下调 5°C 的依据,并说明风险。',
22
+ },
23
+ {
24
+ name: '厚度扫描诊断',
25
+ scenarioPrompt: '厚度扫描仪的多点剖面数据诊断:定位横向厚度偏差来源,给出模头螺栓调整建议。',
26
+ lead: 'scan-lead',
27
+ workers: [{ name: 'profile-analyst', role: 'worker' }],
28
+ goal: '诊断 abw16t4v19 的横向厚度剖面偏差,指出偏差最大的点位。',
29
+ },
30
+ {
31
+ name: '配方调优',
32
+ scenarioPrompt: '配方参数寻优:在工艺窗口内搜索更优参数组合,记录每次试验并给出推荐版本。',
33
+ lead: 'recipe-lead',
34
+ workers: [{ name: 'doe-runner', role: 'worker' }],
35
+ goal: '在工艺窗口内搜索一组更优的牵引速度与冷却水温组合。',
36
+ },
37
+ ]
38
+
39
+ const existing = await api('GET', '/api/workshop/workspaces', { token })
40
+ const wsList = existing.data?.workspaces ?? existing.data ?? []
41
+ let ws = Array.isArray(wsList) ? wsList.find(w => w.name === '示范产线') : null
42
+ if (!ws) {
43
+ const r = await api('POST', '/api/workshop/workspaces', { body: { name: '示范产线' }, token })
44
+ ws = r.data
45
+ }
46
+ const wsId = ws?.id ?? ws?.workspaceId
47
+ if (!wsId) throw new Error('workspace 创建失败: ' + JSON.stringify(existing).slice(0, 300))
48
+ console.log('workspace:', wsId, ws?.name)
49
+
50
+ const channels = []
51
+ const chList = await api('GET', '/api/workshop/channels', { token })
52
+ const all = chList.data?.channels ?? chList.data ?? []
53
+ for (const spec of CHANNELS) {
54
+ let ch = (Array.isArray(all) ? all : []).find(c => c.name === spec.name)
55
+ if (!ch) {
56
+ const r = await api('POST', '/api/workshop/channels', {
57
+ body: {
58
+ name: spec.name,
59
+ scenarioPrompt: spec.scenarioPrompt,
60
+ leadAgent: { name: spec.lead, harness: 'mock', config: { delayMs: 260, streamDemo: true } },
61
+ },
62
+ token,
63
+ })
64
+ ch = r.data
65
+ if (!ch) {
66
+ console.log('channel fail', spec.name, JSON.stringify(r).slice(0, 200))
67
+ continue
68
+ }
69
+ const cid = ch.channelId ?? ch.id
70
+ for (const wk of spec.workers) {
71
+ await api('POST', `/api/workshop/channels/${cid}/agents`, {
72
+ body: { name: wk.name, harness: 'mock', role: wk.role, config: { delayMs: 300, streamDemo: true } },
73
+ token,
74
+ })
75
+ }
76
+ }
77
+ const cid = ch.channelId ?? ch.id
78
+ channels.push({ id: cid, name: spec.name, goal: spec.goal })
79
+ await api('POST', `/api/workshop/workspaces/${wsId}/channels/${cid}`, { token })
80
+ console.log('mounted channel:', spec.name, cid)
81
+ }
82
+
83
+ console.log(JSON.stringify({ wsId, channels }, null, 1))
@@ -0,0 +1,171 @@
1
+ /**
2
+ * 工业演示现场布线 —— 对真实 PLC 模拟器(:4010)做一次「可登录即可见」的完整接装:
3
+ * ①每台模拟器设备 → 平台节点(DCW 写控 / DAQ 数采,driverConfig 取自模拟器对接导出)
4
+ * ②每台设备一个数字孪生模型(modelRef=设备模型库)并把节点双向绑定到孪生
5
+ * ③产线+产品+配方(全量程窗)并开跑 → 数采在活动批次内真实采样
6
+ * ④逐节点 test-driver 真连通验证 + 一次受治理参数写→回读 + 数采样本增长
7
+ * 幂等:按「演示」标记清理上一次的演示线,再重建。
8
+ * 运行: AW_BASE=http://127.0.0.1:3001 node scripts/ui/seed-industrial-demo.mjs
9
+ */
10
+ const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3001'
11
+ const SIM = process.env.E2E_SIM_API ?? 'http://127.0.0.1:4010'
12
+ const ADMIN = { email: process.env.E2E_USER ?? 'admin@awshop.local', password: process.env.E2E_PASS ?? 'admin123' }
13
+
14
+ let failed = 0
15
+ const check = (name, ok, detail = '') => {
16
+ console.log(` ${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`)
17
+ if (!ok) failed++
18
+ }
19
+ const sleep = ms => new Promise(r => setTimeout(r, ms))
20
+
21
+ const login = await fetch(`${BASE}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(ADMIN) }).then(r => r.json())
22
+ const token = login.data?.token
23
+ check('管理员登录', !!token, `${ADMIN.email} ${login.message ?? ''}`)
24
+ if (!token) process.exit(1)
25
+ const H = { 'content-type': 'application/json', 'authorization': `Bearer ${token}` }
26
+ const api = async (m, u, b) => (await fetch(BASE + u, { method: m, headers: H, body: b ? JSON.stringify(b) : undefined })).json()
27
+ // 列表解包:data 可能是数组或 {lines/nodes/twins: [...]}(各路由形状不一)
28
+ const arr = (d, key) => (Array.isArray(d) ? d : d?.[key] ?? d?.nodes ?? d?.lines ?? d?.twins ?? [])
29
+ const sim = async (m, u, b) => (await fetch(SIM + u, { method: m, headers: { 'content-type': 'application/json' }, body: b ? JSON.stringify(b) : undefined })).json()
30
+
31
+ // ── ① 模拟器设备清单(真实五协议) ──
32
+ const devices = (await sim('GET', '/api/nodes')).data ?? []
33
+ check('PLC 模拟器在线', devices.length > 0, `${devices.length} 台设备: ${devices.map(d => d.name).join(' / ')}`)
34
+
35
+ // ── ② 清理上一轮演示实体(按「演示」标记) ──
36
+ const oldLines = arr((await api('GET', '/api/workshop/dcw/lines')).data, 'lines')
37
+ for (const l of oldLines.filter(x => x.name.startsWith('演示线'))) {
38
+ await api('DELETE', `/api/workshop/dcw/lines/${l.id}?purge=1`)
39
+ }
40
+ const oldTwins = arr((await api('GET', '/api/workshop/device-twins')).data, 'twins')
41
+ for (const n of arr((await api('GET', '/api/workshop/daq')).data, 'nodes').filter(x => x.name.startsWith('演示·'))) {
42
+ await api('DELETE', `/api/workshop/daq/${n.id}`)
43
+ }
44
+ for (const n of arr((await api('GET', '/api/workshop/dcw')).data, 'nodes').filter(x => x.name.startsWith('演示·'))) {
45
+ await api('DELETE', `/api/workshop/dcw/${n.id}`)
46
+ }
47
+ for (const t of oldTwins.filter(x => x.name.startsWith('演示·'))) {
48
+ await api('DELETE', `/api/workshop/device-twins/${t.id}`)
49
+ }
50
+ console.log(' · 旧演示实体已清理')
51
+
52
+ // ── ③ 逐设备建节点(SP→DCW 写控;标量 PV→DAQ 数采;driverConfig 取自模拟器导出) ──
53
+ // 模型映射:按设备语义挑数字孪生模型库里的模型
54
+ const MODEL_OF = {
55
+ 'Coating Oven PLC': 'caster', 'Remote RTU Temp Slave': 'power-cabinet',
56
+ 'OPC UA Temp Controller': 'extruder', 'MQTT Temp Sensor': 'device-scanner',
57
+ 'HTTP Flow Meter': 'pump',
58
+ }
59
+ const isSp = s => /SP/i.test(s.signal ?? '') || /^Set/i.test(s.signal ?? '')
60
+ const results = []
61
+ let lineIx = 0
62
+ const twins = []
63
+ for (const dev of devices) {
64
+ const exp = (await sim('GET', `/api/nodes/${dev.id}/export`)).data
65
+ const items = exp?.items ?? []
66
+ const spItem = items.find(i => isSp(i)) ?? null
67
+ const pvItems = items.filter(i => i !== spItem && (i.format ?? 'scalar') === 'scalar')
68
+ if (!spItem && pvItems.length === 0) continue
69
+ const modelKey = MODEL_OF[dev.name] ?? 'device-console'
70
+ const twin = (await api('POST', '/api/workshop/device-twins', {
71
+ name: `演示·${dev.name}`,
72
+ modelRef: modelKey,
73
+ posX: -14 + lineIx * 7,
74
+ posZ: 6 + (lineIx % 2) * 5,
75
+ rotationY: 0,
76
+ })).data?.twin
77
+ if (!twin) {
78
+ check(`孪生创建 ${dev.name}`, false)
79
+ continue
80
+ }
81
+ twins.push(twin)
82
+
83
+ let hostLine = null
84
+ const bindNode = async (nodeId) => {
85
+ const r = await api('POST', `/api/workshop/dcw/${nodeId}/bind`, { deviceId: twin.id }).catch(() => null)
86
+ return r?.code === 0
87
+ }
88
+ const bindDaq = async (nodeId) => {
89
+ const r = await api('POST', `/api/workshop/daq/${nodeId}/bind`, { deviceId: twin.id }).catch(() => null)
90
+ return r?.code === 0
91
+ }
92
+
93
+ // SP → 写控节点 + 自成产线(开跑后本线批次活动,数采采样门放行)
94
+ if (spItem) {
95
+ lineIx++
96
+ const line = (await api('POST', '/api/workshop/dcw/lines', { name: `演示线${lineIx}·${dev.name}` })).data?.line
97
+ const product = (await api('POST', '/api/workshop/dcw/products', { lineId: line.id, name: `演示产品${lineIx}` })).data?.product
98
+ hostLine = line
99
+ const cfg = spItem.driverConfig
100
+ const dcw = (await api('POST', '/api/workshop/dcw', {
101
+ templateRef: 'dcw-temp-sp', name: `演示·${dev.name}·${spItem.signal}`,
102
+ driver: spItem.driver, driverConfig: cfg,
103
+ unit: spItem.unit ?? '℃', decimals: spItem.decimals ?? 1,
104
+ min: spItem.min ?? 0, max: spItem.max ?? 300,
105
+ readIntervalMs: 3000, lineId: line.id, semantics: `模拟器设备「${dev.name}」的 ${spItem.signal}(真实 ${spItem.driver} 写控链路)`,
106
+ })).data?.node
107
+ const bindOk = await bindNode(dcw.id)
108
+ check(`DCW 节点+孪生绑定 ${dev.name}`, !!dcw?.id && bindOk, `${dcw.id} ↔ twin ${twin.id}(${modelKey})`)
109
+ const recipe = (await api('POST', '/api/workshop/dcw/recipes', {
110
+ productId: product.id, name: `演示配方${lineIx}`,
111
+ params: [{ nodeId: dcw.id, value: dev.signals?.find(x => x.id === spItem.signal)?.value ?? (Number(spItem.min ?? 0) + Number(spItem.max ?? 300)) / 2, min: spItem.min ?? 0, max: spItem.max ?? 300 }],
112
+ })).data?.recipe
113
+ const st = (await api('POST', `/api/workshop/dcw/lines/${line.id}/start`, { recipeId: recipe.id }))
114
+ check(`产线开跑 ${line.name}`, !!st.data?.run?.id)
115
+ results.push({ dev, dcw, line, spItem })
116
+ }
117
+
118
+ // 标量 PV → 数采节点(挂到本线或第一条既有演示线)
119
+ for (const pv of pvItems) {
120
+ const cfg = pv.driverConfig
121
+ const daq = (await api('POST', '/api/workshop/daq', {
122
+ templateRef: 'daq-temp-tc', name: `演示·${dev.name}·${pv.signal}`,
123
+ driver: pv.driver, driverConfig: cfg,
124
+ unit: pv.unit ?? '', min: pv.min ?? 0, max: pv.max ?? 400,
125
+ lineId: hostLine?.id ?? '', intervalMs: 2000, publishIntervalMs: 0,
126
+ semantics: `模拟器设备「${dev.name}」的 ${pv.signal}(真实 ${pv.driver} 采样链路)`,
127
+ })).data?.node
128
+ if (!daq) {
129
+ check(`DAQ 节点 ${dev.name}·${pv.signal}`, false)
130
+ continue
131
+ }
132
+ if (hostLine?.id) await bindDaq(daq.id)
133
+ results.push({ dev, daq, pvItem: pv })
134
+ }
135
+ }
136
+
137
+ // 卫星数采补绑定(无 SP 设备的 DAQ 挂到第一条演示线,否则不采样)
138
+ const firstLine = arr((await api('GET', '/api/workshop/dcw/lines')).data, 'lines').find(l => l.name.startsWith('演示线'))
139
+ if (firstLine) {
140
+ const allDaq = arr((await api('GET', '/api/workshop/daq')).data, 'nodes')
141
+ for (const n of allDaq.filter(x => x.name.startsWith('演示·') && !x.lineId)) {
142
+ await api('PATCH', `/api/workshop/daq/${n.id}`, { lineId: firstLine.id })
143
+ await bindDaqSoft(n.id)
144
+ }
145
+ async function bindDaqSoft(nodeId) {
146
+ const twin0 = twins[0]
147
+ if (twin0) await api('POST', `/api/workshop/daq/${nodeId}/bind`, { deviceId: twin0.id }).catch(() => {})
148
+ }
149
+ }
150
+ // ── ④ 真连通验证:逐节点 test-driver ──
151
+ const allDcw = arr((await api('GET', '/api/workshop/dcw')).data, 'nodes').filter(n => n.name.startsWith('演示·'))
152
+ let connOk = 0
153
+ for (const n of allDcw.filter(x => x.driver !== 'mock')) {
154
+ const t = await api('POST', `/api/workshop/dcw/${n.id}/test`)
155
+ connOk += t.data?.test?.ok === true ? 1 : 0
156
+ }
157
+ check('逐节点真连通(test-driver)', connOk === allDcw.filter(x => x.driver !== 'mock').length, `${connOk}/${allDcw.filter(x => x.driver !== 'mock').length} 台真实驱动连通`)
158
+
159
+ // ── ⑤ 数采样本增长(活动批次内真实采样) ──
160
+ await sleep(9000)
161
+ const allDaq = arr((await api('GET', '/api/workshop/daq')).data, 'nodes').filter(n => n.name.startsWith('演示·'))
162
+ let sampling = 0
163
+ for (const n of allDaq) {
164
+ const s = await api('GET', `/api/workshop/daq/${n.id}/samples?limit=5`)
165
+ const pts = s.data?.points ?? []
166
+ sampling += pts.length > 0 ? 1 : 0
167
+ }
168
+ check('数采真实采样', sampling === allDaq.length, `${sampling}/${allDaq.length} 通道有样本(9s 窗)`)
169
+
170
+ console.log(`\n${failed === 0 ? '✅ 演示现场就绪' : `❌ ${failed} 项失败`}`)
171
+ process.exit(failed === 0 ? 0 : 1)
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 把 docs/readme-assets/hero.html 渲成 2 倍图 hero.png(README 首屏).
3
+ * hero.html 里的截图是同目录下的真实运行截图(town.png / daq.png),
4
+ * 所以出图前必须先跑 scripts/ui/shots.mjs 更新它们。
5
+ */
6
+ import puppeteer from 'puppeteer-core'
7
+ import path from 'node:path'
8
+ import { CHROME } from './lib.mjs'
9
+
10
+ const dir = path.resolve('docs/readme-assets')
11
+ const browser = await puppeteer.launch({
12
+ executablePath: CHROME,
13
+ headless: true,
14
+ args: ['--no-sandbox', '--force-color-profile=srgb', '--font-render-hinting=none'],
15
+ defaultViewport: { width: 1300, height: 520, deviceScaleFactor: 2 },
16
+ })
17
+ const page = await browser.newPage()
18
+ const errs = []
19
+ page.on('pageerror', e => errs.push(e.message))
20
+ page.on('requestfailed', r => errs.push('failed: ' + r.url()))
21
+ await page.goto('file:///' + path.join(dir, 'hero.html').replace(/\\/g, '/'), { waitUntil: 'networkidle0' })
22
+ await new Promise(r => setTimeout(r, 400))
23
+ await page.screenshot({ path: path.join(dir, 'hero.png'), type: 'png' })
24
+ console.log('hero.png written', errs.length ? '⚠ ' + errs.join(' | ') : '')
25
+ await browser.close()
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 全站视觉走查截图 —— 视口 × 主题 矩阵批量出图。
3
+ * node scripts/ui/shots.mjs [--out .e2e-shots/new/base] [--theme dark|light|both]
4
+ * [--vp 1440x900] [--routes workshop,daq,...] [--wait 6000]
5
+ */
6
+ import fs from 'node:fs'
7
+ import path from 'node:path'
8
+ import { ensureVisualUser, launch, openPage, gotoReady, ROUTES } from './lib.mjs'
9
+
10
+ const argv = process.argv.slice(2)
11
+ const arg = (k, d) => {
12
+ const i = argv.indexOf(`--${k}`)
13
+ return i >= 0 ? argv[i + 1] : d
14
+ }
15
+ const OUT = arg('out', '.e2e-shots/new/base')
16
+ const THEME = arg('theme', 'dark')
17
+ const VP = arg('vp', '1440x900')
18
+ const ONLY = arg('routes', '')
19
+ const WAIT = Number(arg('wait', 6500))
20
+ const [WV, HV] = VP.split('x').map(Number)
21
+
22
+ const themes = THEME === 'both' ? [true, false] : [THEME === 'dark']
23
+ // 'all' 与不传等价:两个验收脚本的参数语义必须一致,
24
+ // 否则 `--routes all` 会被当成"找一个名叫 all 的路由"而筛出空集(实测踩过:
25
+ // 跑完只留下一份空报告,还打印了 "saved")。
26
+ const picks = (!ONLY || ONLY === 'all')
27
+ ? ROUTES
28
+ : ROUTES.filter(r => ONLY.split(',').includes(r.name))
29
+
30
+ fs.mkdirSync(OUT, { recursive: true })
31
+ const token = await ensureVisualUser()
32
+ const browser = await launch({ width: WV, height: HV })
33
+ const report = []
34
+
35
+ for (const dark of themes) {
36
+ for (const r of picks) {
37
+ const page = await openPage(browser, { token, dark, width: WV, height: HV })
38
+ const errs = []
39
+ const consoleErrs = []
40
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 200)))
41
+ page.on('console', (m) => {
42
+ if (m.type() === 'error') consoleErrs.push(m.text().slice(0, 200))
43
+ })
44
+ try {
45
+ await gotoReady(page, r.path, { wait: WAIT })
46
+ const geo = await page.evaluate(() => {
47
+ const de = document.documentElement
48
+ return {
49
+ scrollW: de.scrollWidth,
50
+ clientW: de.clientWidth,
51
+ scrollH: de.scrollHeight,
52
+ clientH: de.clientHeight,
53
+ url: location.pathname,
54
+ }
55
+ })
56
+ const file = path.join(OUT, `${dark ? 'dark' : 'light'}__${r.name}.png`)
57
+ await page.screenshot({ path: file })
58
+ report.push({
59
+ route: r.name, path: r.path, theme: dark ? 'dark' : 'light',
60
+ landed: geo.url, overflowX: geo.scrollW - geo.clientW,
61
+ scrollH: geo.scrollH, pageErrors: errs, consoleErrors: consoleErrs.slice(0, 3), file,
62
+ })
63
+ console.log(` ${dark ? 'dark ' : 'light'} ${r.name.padEnd(12)} overflowX=${geo.scrollW - geo.clientW} landed=${geo.url} errs=${errs.length}`)
64
+ }
65
+ catch (e) {
66
+ report.push({ route: r.name, theme: dark ? 'dark' : 'light', error: String(e.message).slice(0, 200) })
67
+ console.log(` FAIL ${r.name}: ${String(e.message).slice(0, 120)}`)
68
+ }
69
+ await page.close()
70
+ }
71
+ }
72
+
73
+ fs.writeFileSync(path.join(OUT, '_report.json'), JSON.stringify(report, null, 2))
74
+ await browser.close()
75
+ console.log('\nsaved ->', OUT)
@@ -0,0 +1,16 @@
1
+ import puppeteer from 'puppeteer-core'
2
+ import { CHROME, sleep } from './lib.mjs'
3
+ import fs from 'node:fs'
4
+
5
+ fs.mkdirSync('.e2e-shots/admin', { recursive: true })
6
+ const b = await puppeteer.launch({ executablePath: CHROME, headless: true, args: ['--no-sandbox', '--no-proxy-server'], defaultViewport: { width: 1600, height: 1000, deviceScaleFactor: 2 } })
7
+ const p = await b.newPage()
8
+ const errs = []
9
+ p.on('pageerror', e => errs.push(String(e.message).slice(0, 140)))
10
+ await p.goto('http://127.0.0.1:4010/', { waitUntil: 'networkidle0', timeout: 60000 })
11
+ await sleep(6000)
12
+ const info = await p.evaluate(() => ({ title: document.title, text: document.body.innerText.replace(/\s+/g, ' ').slice(0, 260), nodes: document.querySelectorAll('*').length }))
13
+ console.log('sim UI:', JSON.stringify(info))
14
+ console.log('errs:', errs.length, errs.slice(0, 3))
15
+ await p.screenshot({ path: '.e2e-shots/admin/00-plc-simulator.png' })
16
+ await b.close()