agentworkshop 0.7.39 → 0.7.40

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 (373) 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/BOt2SP4z.js +1 -0
  7. package/.output/public/_nuxt/BP-NyKVG.js +18 -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/{C_OLzD09.js → C4oW1HsE.js} +1 -1
  12. package/.output/public/_nuxt/CFxtLwjp.js +1 -0
  13. package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
  14. package/.output/public/_nuxt/C_iYD-EB.js +1 -0
  15. package/.output/public/_nuxt/{DAe0h2MB.js → ClILjvWH.js} +1 -1
  16. package/.output/public/_nuxt/CssgYAAL2.js +1 -0
  17. package/.output/public/_nuxt/{CY9LbPCI.js → CzOCSGLe.js} +1 -1
  18. package/.output/public/_nuxt/D8qvkfyY.js +1 -0
  19. package/.output/public/_nuxt/DCn7QZ5N.js +2 -0
  20. package/.output/public/_nuxt/DGIuvyRN2.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/DZshyNLa.js +1 -0
  26. package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
  27. package/.output/public/_nuxt/DcsCcxp4.js +1 -0
  28. package/.output/public/_nuxt/{DUrecWPx.js → Dihnimlk.js} +2 -2
  29. package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
  30. package/.output/public/_nuxt/{CUDwzgLV.js → Dr9v6C0f.js} +9 -9
  31. package/.output/public/_nuxt/{8NMOXZOV.js → Du9ZJtnj.js} +1 -1
  32. package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
  33. package/.output/public/_nuxt/_id_.CM4bIgKY.css +1 -0
  34. package/.output/public/_nuxt/_id_.bNV69Dtf.css +1 -0
  35. package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
  36. package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
  37. package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
  38. package/.output/public/_nuxt/builds/latest.json +1 -1
  39. package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +1 -0
  40. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
  41. package/.output/public/_nuxt/daq.B0D3dPAc.css +1 -0
  42. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
  43. package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
  44. package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -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 +2225 -1445
  61. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  62. package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView-BuLAsmpR.mjs} +288 -206
  63. package/.output/server/chunks/build/TownView-BuLAsmpR.mjs.map +1 -0
  64. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-BU1wfOID.mjs} +138 -138
  65. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs.map → _id_-BU1wfOID.mjs.map} +1 -1
  66. package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-BYsEua57.mjs} +33 -32
  67. package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +1 -0
  68. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BxlDcJHg.mjs} +293 -259
  69. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BxlDcJHg.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-cgyjKmJu.mjs} +188 -122
  78. package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +1 -0
  79. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-CoTxBlAZ.mjs} +45 -45
  80. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-CoTxBlAZ.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-BSOeZwwI.mjs} +34 -34
  84. package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs-BSOeZwwI.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-DFxjn9Xa.mjs} +33 -17
  88. package/.output/server/chunks/build/pages-DFxjn9Xa.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-qMPql9Se.mjs} +36 -10
  100. package/.output/server/chunks/build/town-qMPql9Se.mjs.map +1 -0
  101. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs +1 -1
  102. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs +1 -1
  103. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
  104. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
  105. package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
  106. package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
  107. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
  108. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
  109. package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
  110. package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
  111. package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
  112. package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
  113. package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
  114. package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
  115. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
  116. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
  117. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
  118. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  120. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  121. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs.map +1 -1
  122. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
  123. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
  124. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
  125. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
  126. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
  127. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
  128. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
  129. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
  131. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
  133. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  134. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
  135. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
  137. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
  139. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
  140. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
  141. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
  142. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
  143. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
  144. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
  145. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
  146. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
  147. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
  148. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
  149. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
  150. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
  151. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  152. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  153. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  154. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  155. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  156. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  157. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +2 -0
  159. package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
  160. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -1
  161. package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
  162. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  164. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  165. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  166. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  167. package/.output/server/chunks/routes/renderer.mjs +3 -3
  168. package/.output/server/chunks/virtual/entry.mjs +51 -25
  169. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  170. package/.output/server/package.json +1 -1
  171. package/README-zh.md +199 -35
  172. package/README.md +739 -591
  173. package/app/app.vue +9 -3
  174. package/app/assets/css/main.css +517 -7
  175. package/app/components/AppHeader.vue +98 -7
  176. package/app/components/AppSidebar.vue +117 -11
  177. package/app/components/AwChart.vue +2 -1
  178. package/app/components/workshop/AgentInspectorDrawer.vue +6 -6
  179. package/app/components/workshop/AgentLanesView.vue +52 -1
  180. package/app/components/workshop/AssetLibrary.vue +4 -2
  181. package/app/components/workshop/ChannelSessionList.vue +4 -2
  182. package/app/components/workshop/Composer.vue +96 -0
  183. package/app/components/workshop/DaqNodeRow.vue +19 -0
  184. package/app/components/workshop/DaqTemplateIcon.vue +33 -0
  185. package/app/components/workshop/DeviceTwinPanel.vue +37 -35
  186. package/app/components/workshop/EventBlock.vue +3 -3
  187. package/app/components/workshop/InspectorPanel.vue +47 -11
  188. package/app/components/workshop/LiveAgentBar.vue +34 -0
  189. package/app/components/workshop/TaskBoardView.vue +83 -33
  190. package/app/components/workshop/TranscriptTimeline.vue +56 -0
  191. package/app/components/workshop/blocks/ClusterError.vue +1 -1
  192. package/app/components/workshop/blocks/ClusterRoute.vue +3 -1
  193. package/app/components/workshop/blocks/ClusterStatus.vue +2 -2
  194. package/app/components/workshop/blocks/ClusterStream.vue +8 -2
  195. package/app/components/workshop/town/ModelPreview3D.vue +1 -1
  196. package/app/components/workshop/town/TownView.vue +507 -82
  197. package/app/composables/useResponsive.ts +88 -0
  198. package/app/composables/useRouteMeta.ts +3 -0
  199. package/app/composables/workshop/useDcwStream.ts +1 -1
  200. package/app/composables/workshop/useVisibleInterval.ts +2 -2
  201. package/app/composables/workshop/useWorkshopApi.ts +1 -1
  202. package/app/layouts/default.vue +46 -2
  203. package/app/pages/aml/index.vue +712 -616
  204. package/app/pages/daq/[id].vue +3 -2
  205. package/app/pages/daq/index.vue +209 -9
  206. package/app/pages/dcw/[id].vue +109 -28
  207. package/app/pages/dcw/index.vue +83 -23
  208. package/app/pages/index.vue +35 -1
  209. package/app/pages/logs/index.vue +147 -13
  210. package/app/pages/monitor.vue +56 -12
  211. package/app/pages/permissions/index.vue +34 -0
  212. package/app/pages/plugins/index.vue +35 -4
  213. package/app/pages/settings.vue +90 -21
  214. package/app/pages/tokens.vue +23 -0
  215. package/app/pages/town.vue +50 -5
  216. package/app/pages/users.vue +23 -0
  217. package/app/pages/workshop/agents.vue +92 -2
  218. package/app/pages/workshop/channel-templates.vue +53 -0
  219. package/app/pages/workshop/index.vue +14 -0
  220. package/app/pages/workshop/teams.vue +66 -6
  221. package/app/pages/workshop/w/[wsId].vue +202 -14
  222. package/app/plugins/aw-plugins.client.ts +13 -8
  223. package/app/stores/app.ts +33 -1
  224. package/app/stores/workshop/user.ts +1 -1
  225. package/app/stores/workshop/workspaces.ts +18 -2
  226. package/app/utils/api-error.ts +1 -1
  227. package/cli/commands/build.mjs +8 -1
  228. package/docker-compose.yml +6 -0
  229. package/i18n/locales/en.ts +2 -0
  230. package/i18n/locales/zh-CN.ts +2 -0
  231. package/nuxt.config.ts +31 -0
  232. package/package.json +9 -1
  233. package/scripts/_dbg-param-map-e2e.ts +242 -0
  234. package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
  235. package/scripts/_dbg-param-real-scenario.mjs +153 -0
  236. package/scripts/api-live-e2e.mjs +40 -8
  237. package/scripts/capture-walkthrough.mjs +232 -0
  238. package/scripts/render-walkthrough-figure.mjs +46 -0
  239. package/scripts/seed-walkthrough-fixture.mjs +143 -0
  240. package/scripts/test-plugin-hardening.mjs +8 -0
  241. package/scripts/ui/admin-shots.mjs +46 -0
  242. package/scripts/ui/audit-layout.mjs +301 -0
  243. package/scripts/ui/bump-microfont.mjs +39 -0
  244. package/scripts/ui/lib.mjs +198 -0
  245. package/scripts/ui/linkcheck2.mjs +37 -0
  246. package/scripts/ui/live-render-check.mjs +72 -0
  247. package/scripts/ui/md-lint.mjs +80 -0
  248. package/scripts/ui/perf-audit.mjs +132 -0
  249. package/scripts/ui/readme-assets.mjs +30 -0
  250. package/scripts/ui/readme-shots.mjs +42 -0
  251. package/scripts/ui/record.mjs +225 -0
  252. package/scripts/ui/seed-admin.mjs +68 -0
  253. package/scripts/ui/seed-demo.mjs +83 -0
  254. package/scripts/ui/shot-hero.mjs +25 -0
  255. package/scripts/ui/shots.mjs +75 -0
  256. package/scripts/ui/sim-ui-shot.mjs +16 -0
  257. package/scripts/ui/verify-nav.mjs +51 -0
  258. package/scripts/ui/verify-responsive.mjs +285 -0
  259. package/server/api/system/config-groups/[id].patch.ts +1 -1
  260. package/server/api/system/config-groups/index.post.ts +1 -1
  261. package/server/api/system/settings/events.get.ts +1 -1
  262. package/server/api/system/settings/index.patch.ts +1 -1
  263. package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
  264. package/server/api/workshop/aml/datasets/[id].get.ts +8 -1
  265. package/server/api/workshop/daq/infra/reconnect.post.ts +1 -1
  266. package/server/api/workshop/daq/templates/[key].delete.ts +1 -0
  267. package/server/api/workshop/daq/templates/[key].patch.ts +1 -0
  268. package/server/api/workshop/daq/templates/index.post.ts +1 -0
  269. package/server/api/workshop/dcw/[id]/write.post.ts +10 -2
  270. package/server/api/workshop/dcw/[id].delete.ts +5 -2
  271. package/server/api/workshop/dcw/index.get.ts +2 -0
  272. package/server/api/workshop/dcw/lines/[id].patch.ts +1 -1
  273. package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -0
  274. package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -0
  275. package/server/api/workshop/dcw/params/[id].delete.ts +19 -0
  276. package/server/api/workshop/dcw/params/[id].patch.ts +23 -0
  277. package/server/api/workshop/dcw/params/index.get.ts +27 -0
  278. package/server/api/workshop/dcw/params/index.post.ts +25 -0
  279. package/server/api/workshop/permissions/index.get.ts +3 -1
  280. package/server/plugins/aw-plugins.ts +14 -3
  281. package/server/plugins/dev-stability-guard.ts +3 -1
  282. package/server/plugins/system-config.ts +6 -1
  283. package/server/services/system-config.ts +28 -9
  284. package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
  285. package/server/services/workshop/agents/agent-interface.ts +8 -6
  286. package/server/services/workshop/agents/base-agent.ts +18 -4
  287. package/server/services/workshop/agents/claude-agent.ts +7 -3
  288. package/server/services/workshop/agents/codex-agent.ts +3 -3
  289. package/server/services/workshop/agents/dsh-agent.ts +3 -3
  290. package/server/services/workshop/agents/goose-agent.ts +0 -1
  291. package/server/services/workshop/agents/harness-terminal.ts +14 -1
  292. package/server/services/workshop/agents/hermes-agent.ts +4 -2
  293. package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
  294. package/server/services/workshop/agents/industrial-context.ts +4 -1
  295. package/server/services/workshop/agents/industrial-tools.ts +164 -6
  296. package/server/services/workshop/agents/omp-agent.ts +15 -9
  297. package/server/services/workshop/agents/opencode-agent.ts +47 -21
  298. package/server/services/workshop/agents/qwen-agent.ts +10 -5
  299. package/server/services/workshop/aml/entity.ts +3 -1
  300. package/server/services/workshop/aml/python-runtime.ts +3 -2
  301. package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
  302. package/server/services/workshop/daq/daq-controller.ts +18 -3
  303. package/server/services/workshop/daq/daq-runtime.ts +5 -3
  304. package/server/services/workshop/daq/drivers.ts +63 -8
  305. package/server/services/workshop/daq/host-ports.ts +3 -2
  306. package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
  307. package/server/services/workshop/db/channel.repo.ts +4 -0
  308. package/server/services/workshop/db/ops.repo.ts +5 -2
  309. package/server/services/workshop/db/retention.ts +8 -3
  310. package/server/services/workshop/dcw/dcw-controller.ts +143 -16
  311. package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
  312. package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
  313. package/server/services/workshop/dcw/drivers.ts +5 -2
  314. package/server/services/workshop/dcw/param-conversion.ts +78 -0
  315. package/server/services/workshop/dcw/param-limits.ts +142 -0
  316. package/server/services/workshop/dcw/param-map.repo.ts +305 -0
  317. package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
  318. package/server/services/workshop/runtime/agent-runtime.ts +4 -0
  319. package/server/services/workshop/runtime/manager.ts +9 -2
  320. package/server/services/workshop/settings.ts +4 -2
  321. package/server/types/pg.d.ts +49 -0
  322. package/server/utils/stability-severity.ts +13 -2
  323. package/shared/config/groups.mjs +22 -1
  324. package/shared/config/home.d.mts +28 -0
  325. package/shared/dcw-protocol.ts +136 -0
  326. package/uno.config.ts +12 -1
  327. package/.output/public/_nuxt/BFkgkW8u.js +0 -4
  328. package/.output/public/_nuxt/C5gUkRwn.js +0 -1
  329. package/.output/public/_nuxt/CM12FuB42.js +0 -1
  330. package/.output/public/_nuxt/CZiw7sEV.js +0 -1
  331. package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
  332. package/.output/public/_nuxt/CdPzkOMV.js +0 -1
  333. package/.output/public/_nuxt/CmsOtOAA2.js +0 -1
  334. package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
  335. package/.output/public/_nuxt/D20Yamr4.js +0 -1
  336. package/.output/public/_nuxt/DmqknNvV.js +0 -18
  337. package/.output/public/_nuxt/DnUuIR83.js +0 -1
  338. package/.output/public/_nuxt/S_fVQwJK.js +0 -1
  339. package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
  340. package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
  341. package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
  342. package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
  343. package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
  344. package/.output/public/_nuxt/ahgEJu2R.js +0 -1
  345. package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
  346. package/.output/public/_nuxt/builds/meta/4457d63f-0866-4cea-b320-04a321f24795.json +0 -1
  347. package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
  348. package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
  349. package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
  350. package/.output/public/_nuxt/default.BXI_fZUi.css +0 -1
  351. package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
  352. package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
  353. package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
  354. package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
  355. package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
  356. package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
  357. package/.output/public/_nuxt/pz2JDAM7.js +0 -2
  358. package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
  359. package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
  360. package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
  361. package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
  362. package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
  363. package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
  364. package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
  365. package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
  366. package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
  367. package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
  368. package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
  369. package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
  370. package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
  371. package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
  372. /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
  373. /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ * README 结构自检:标签配平、图片/链接存在、模板残留、表格列数一致。
3
+ * GitHub 的渲染器很宽容,写坏了不报错 —— 只会在页面上难看,所以得自己查。
4
+ */
5
+ import fs from 'node:fs'
6
+ import path from 'node:path'
7
+
8
+ const files = process.argv.slice(2)
9
+ const FENCE = String.fromCharCode(96).repeat(3)
10
+ let bad = 0
11
+ const fail = (f, m) => {
12
+ bad += 1
13
+ console.log(' x ' + f + ': ' + m)
14
+ }
15
+
16
+ for (const f of files) {
17
+ const t = fs.readFileSync(f, 'utf8')
18
+ const ls = t.split(/\r?\n/)
19
+ console.log('·', f)
20
+
21
+ // 转义残留:只把'转义了不该转义的字符'当问题(表格里的 \\| 是合法的)
22
+ const esc = ls.findIndex(l => l.includes('\\' + String.fromCharCode(96)))
23
+ if (esc >= 0) fail(f, 'line ' + (esc + 1) + ' 存在反引号转义残留')
24
+
25
+ // ${...} 只在不处于围栏代码块里时才算未求值模板(shell 示例里的 ${VAR} 合法)
26
+ {
27
+ let fenced = false
28
+ let hit = -1
29
+ for (let i = 0; i < ls.length; i++) {
30
+ if (ls[i].trim().startsWith(FENCE)) {
31
+ fenced = !fenced
32
+ continue
33
+ }
34
+ if (!fenced && /\$\{[a-zA-Z_]/.test(ls[i])) {
35
+ hit = i
36
+ break
37
+ }
38
+ }
39
+ if (hit >= 0) fail(f, 'line ' + (hit + 1) + ' 存在未求值的模板占位')
40
+ }
41
+
42
+ // 标签配平
43
+ for (const tag of ['div', 'table', 'tr', 'td', 'details', 'summary', 'picture', 'sub', 'code', 'a']) {
44
+ const open = (t.match(new RegExp('<' + tag + '(\\s|>)', 'g')) || []).length
45
+ const close = (t.match(new RegExp('</' + tag + '>', 'g')) || []).length
46
+ const self = (t.match(new RegExp('<' + tag + '[^>]*/>', 'g')) || []).length
47
+ if (open - self !== close) fail(f, '<' + tag + '> 不配平: open=' + open + ' self=' + self + ' close=' + close)
48
+ }
49
+
50
+ // 本地图片 / 链接存在
51
+ for (const m of t.matchAll(/<img[^>]+src="([^"]+)"/g)) {
52
+ const src = m[1]
53
+ if (/^https?:/.test(src)) continue
54
+ const abs = path.resolve(path.dirname(f), src.replace(/^\.\//, ''))
55
+ if (!fs.existsSync(abs)) fail(f, '图片不存在: ' + src)
56
+ }
57
+ for (const m of t.matchAll(/\]\((\.\/[^)#]+)\)/g)) {
58
+ const abs = path.resolve(path.dirname(f), m[1].replace(/^\.\//, ''))
59
+ if (!fs.existsSync(abs)) fail(f, '链接目标不存在: ' + m[1])
60
+ }
61
+
62
+ // 表格列数一致(转义管道符不算分隔)
63
+ const cells = row => row.replace(/\\\|/g, '\u0000').split('|').length
64
+ let cols = null, start = 0
65
+ for (let i = 0; i <= ls.length; i++) {
66
+ const isRow = i < ls.length && /^\s*\|.*\|\s*$/.test(ls[i])
67
+ if (!isRow) {
68
+ cols = null
69
+ continue
70
+ }
71
+ const n = cells(ls[i])
72
+ if (cols === null) {
73
+ cols = n
74
+ start = i
75
+ }
76
+ else if (n !== cols) fail(f, 'line ' + (i + 1) + ' 表格列数 ' + n + ' != 表头 ' + cols + '(表从 line ' + (start + 1) + ' 起)')
77
+ }
78
+ }
79
+ console.log(bad ? '\n' + bad + ' 个问题' : '\n全部通过')
80
+ process.exit(bad ? 1 : 0)
@@ -0,0 +1,132 @@
1
+ /**
2
+ * 前端实时链路性能体检 —— 用真实浏览器量"数据刷新是否把主线程压垮"。
3
+ *
4
+ * 量的是四件事(全部来自运行时,不是估算):
5
+ * 1. Long Task:>50ms 的主线程阻塞任务数 与 总阻塞时长(TBT)
6
+ * 2. FPS:用 requestAnimationFrame 采样的真实帧率 + 掉帧比例
7
+ * 3. DOM 变更速率:每秒多少次节点/属性变更(重渲染churn 的直接代理指标)
8
+ * 4. CDP Performance 指标:ScriptDuration / LayoutDuration / RecalcStyleDuration 的增量
9
+ *
10
+ * 用法: node scripts/ui/perf-audit.mjs [--routes daq,town,dashboard] [--secs 20] [--vp 1440x900]
11
+ */
12
+ import { ensureVisualUser, launch, openPage, gotoReady, sleep, ROUTES } from './lib.mjs'
13
+
14
+ const argv = process.argv.slice(2)
15
+ const arg = (k, d) => {
16
+ const i = argv.indexOf(`--${k}`)
17
+ return i >= 0 ? argv[i + 1] : d
18
+ }
19
+ const ONLY = arg('routes', 'daq,town,dashboard,monitor')
20
+ const SECS = Number(arg('secs', 20))
21
+ const [W, H] = arg('vp', '1440x900').split('x').map(Number)
22
+
23
+ const picks = ROUTES.filter(r => ONLY.split(',').includes(r.name))
24
+ // 频道会话台(/workshop/w/<id>)不在静态 ROUTES 里,但它是事件最密的页面 ——
25
+ // 用真实 workspace id 合成一条,才能量到"Agent 事件流是否压垮主线程"。
26
+
27
+ const token = await ensureVisualUser()
28
+ // 频道会话台(/workshop/w/<id>)不在静态 ROUTES 里,但它是事件最密的页面 ——
29
+ // 用真实 workspace id 合成一条,才能量到"Agent 事件流是否压垮主线程"。
30
+ if (ONLY.includes('console')) {
31
+ const { api } = await import('./lib.mjs')
32
+ const ws = await api('GET', '/api/workshop/workspaces', { token })
33
+ const list = ws.data?.workspaces ?? ws.data ?? []
34
+ const wid = (Array.isArray(list) ? list[0] : null)?.id
35
+ if (wid) picks.push({ path: `/workshop/w/${wid}`, name: 'console', title: '频道会话台' })
36
+ else console.log('!! 没有可用工作区,跳过 console(先跑 scripts/ui/seed-demo.mjs)')
37
+ }
38
+ const browser = await launch()
39
+
40
+ const INSTRUMENT = () => {
41
+ const w = window
42
+ w.__perf = { longTasks: 0, tbt: 0, frames: 0, slowFrames: 0, mutations: 0, last: performance.now(), maxGap: 0 }
43
+ try {
44
+ new PerformanceObserver((list) => {
45
+ for (const e of list.getEntries()) {
46
+ w.__perf.longTasks++
47
+ w.__perf.tbt += Math.max(0, e.duration - 50)
48
+ }
49
+ }).observe({ entryTypes: ['longtask'] })
50
+ }
51
+ catch { /* 不支持 longtask 就只靠 FPS */ }
52
+ const mo = new MutationObserver((recs) => {
53
+ w.__perf.mutations += recs.length
54
+ })
55
+ mo.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true })
56
+ const tick = (t) => {
57
+ const p = w.__perf
58
+ const gap = t - p.last
59
+ p.last = t
60
+ if (p.frames > 0) {
61
+ if (gap > p.maxGap) p.maxGap = gap
62
+ if (gap > 33) p.slowFrames++
63
+ }
64
+ p.frames++
65
+ requestAnimationFrame(tick)
66
+ }
67
+ requestAnimationFrame(tick)
68
+ }
69
+
70
+ const rows = []
71
+ for (const r of picks) {
72
+ const page = await openPage(browser, { token, dark: true, width: W, height: H, deviceScaleFactor: 1 })
73
+ const errs = []
74
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 140)))
75
+ try {
76
+ await gotoReady(page, r.path, { wait: 4000 })
77
+ await page.evaluate(INSTRUMENT)
78
+ const client = await page.createCDPSession()
79
+ await client.send('Performance.enable')
80
+ const m0 = await client.send('Performance.getMetrics')
81
+ const t0 = Date.now()
82
+
83
+ await sleep(SECS * 1000)
84
+
85
+ const m1 = await client.send('Performance.getMetrics')
86
+ const p = await page.evaluate(() => ({ ...window.__perf }))
87
+ const metric = (name) => {
88
+ const a = m0.metrics.find(x => x.name === name)?.value ?? 0
89
+ const b = m1.metrics.find(x => x.name === name)?.value ?? 0
90
+ return b - a
91
+ }
92
+ const secs = (Date.now() - t0) / 1000
93
+ const row = {
94
+ route: r.name,
95
+ secs: +secs.toFixed(1),
96
+ fps: +(p.frames / secs).toFixed(1),
97
+ slowFramePct: +((p.slowFrames / Math.max(1, p.frames)) * 100).toFixed(1),
98
+ maxGapMs: Math.round(p.maxGap),
99
+ longTasks: p.longTasks,
100
+ tbtMs: Math.round(p.tbt),
101
+ mutPerSec: Math.round(p.mutations / secs),
102
+ scriptMs: Math.round(metric('ScriptDuration') * 1000),
103
+ layoutMs: Math.round(metric('LayoutDuration') * 1000),
104
+ styleMs: Math.round(metric('RecalcStyleDuration') * 1000),
105
+ nodes: Math.round(metric('Nodes')),
106
+ listeners: Math.round(metric('JSEventListeners')),
107
+ pageErrors: errs.length,
108
+ }
109
+ rows.push(row)
110
+ console.log(JSON.stringify(row))
111
+ }
112
+ catch (e) {
113
+ console.log(r.name, 'FAIL', String(e.message).slice(0, 120))
114
+ }
115
+ await page.close()
116
+ }
117
+ await browser.close()
118
+ console.log('\n路由 FPS 掉帧% 最大间隔 长任务 阻塞ms DOM变更/s 脚本ms 布局ms 样式ms')
119
+ for (const r of rows) {
120
+ console.log(
121
+ r.route.padEnd(12),
122
+ String(r.fps).padStart(5),
123
+ String(r.slowFramePct).padStart(6),
124
+ String(r.maxGapMs).padStart(8),
125
+ String(r.longTasks).padStart(7),
126
+ String(r.tbtMs).padStart(7),
127
+ String(r.mutPerSec).padStart(10),
128
+ String(r.scriptMs).padStart(7),
129
+ String(r.layoutMs).padStart(7),
130
+ String(r.styleMs).padStart(7),
131
+ )
132
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * README / 文档站素材一键重建。
3
+ *
4
+ * 顺序不能换:截图 → GIF → hero。hero.html 里内嵌的是同目录的 town.png / daq.png,
5
+ * 所以 hero 必须最后渲,否则首屏会用到上一轮的界面截图(实测踩过)。
6
+ *
7
+ * node scripts/ui/readme-assets.mjs [--skip-gif]
8
+ */
9
+ import fs from 'node:fs'
10
+ import { execFileSync } from 'node:child_process'
11
+
12
+ const skipGif = process.argv.includes('--skip-gif')
13
+ const run = (script, args = []) => {
14
+ console.log('\n▶ node ' + script + ' ' + args.join(' '))
15
+ execFileSync(process.execPath, [script, ...args], { stdio: 'inherit' })
16
+ }
17
+
18
+ run('scripts/ui/readme-shots.mjs')
19
+
20
+ if (!skipGif) {
21
+ for (const story of ['agents', 'daq', 'dcw', 'town', 'dashboard', 'responsive']) {
22
+ const num = { agents: '01-closedloop', daq: '02-daq', dcw: '03-dcw', town: '04-town', dashboard: '05-dashboard', responsive: '06-responsive' }[story]
23
+ // record.mjs 按剧本名输出 <story>.gif,这里落成 README 里的 fig-NN-*.gif 编号名
24
+ run('scripts/ui/record.mjs', [story, '--out', 'docs/readme-assets', '--gifw', '1000'])
25
+ fs.renameSync('docs/readme-assets/' + story + '.gif', 'docs/readme-assets/fig-' + num + '.gif')
26
+ }
27
+ }
28
+
29
+ run('scripts/ui/shot-hero.mjs')
30
+ console.log('\n✔ README 素材重建完成')
@@ -0,0 +1,42 @@
1
+ /**
2
+ * README 用真实截图(与 record.mjs 的 GIF 配套)。
3
+ * 全部 1600×1000 @2x 出图,固定暗色控制室主题 —— README 首屏是深色语境,
4
+ * 混入亮色截图会让整页忽明忽暗。
5
+ */
6
+ import path from 'node:path'
7
+ import { ensureVisualUser, launch, openPage, gotoReady } from './lib.mjs'
8
+
9
+ const OUT = 'docs/readme-assets'
10
+ const W = 1600
11
+ const H = 1000
12
+
13
+ const SHOTS = [
14
+ { file: 'town.png', route: '/town', wait: 11000 },
15
+ { file: 'daq.png', route: '/daq', wait: 8000 },
16
+ { file: 'shot-workshop.png', route: '/workshop', wait: 7000 },
17
+ { file: 'shot-monitor.png', route: '/monitor', wait: 6000 },
18
+ { file: 'shot-settings.png', route: '/settings', wait: 6000 },
19
+ { file: 'shot-plugins.png', route: '/plugins', wait: 6000 },
20
+ { file: 'shot-dcw.png', route: '/dcw', wait: 7000 },
21
+ { file: 'shot-aml.png', route: '/aml', wait: 7000 },
22
+ { file: 'shot-logs.png', route: '/logs', wait: 6000 },
23
+ ]
24
+
25
+ const token = await ensureVisualUser()
26
+ const browser = await launch()
27
+ for (const s of SHOTS) {
28
+ const page = await openPage(browser, { token, dark: true, width: W, height: H, deviceScaleFactor: 2 })
29
+ const errs = []
30
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 120)))
31
+ try {
32
+ await gotoReady(page, s.route, { wait: s.wait })
33
+ await page.screenshot({ path: path.join(OUT, s.file) })
34
+ console.log(' ok ' + s.file.padEnd(22) + s.route + (errs.length ? ' errs=' + errs.length : ''))
35
+ }
36
+ catch (e) {
37
+ console.log(' FAIL ' + s.file + ' — ' + String(e.message).slice(0, 120))
38
+ }
39
+ await page.close()
40
+ }
41
+ await browser.close()
42
+ console.log('-> ' + OUT)
@@ -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))