agentworkshop 0.7.38 → 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 (392) hide show
  1. package/.AgentWorkShop/prompts/aml-mission-default.md +20 -20
  2. package/.AgentWorkShop/prompts/aml-playbook.md +49 -49
  3. package/.AgentWorkShop/prompts/host-tools.json +1293 -1238
  4. package/.AgentWorkShop/prompts/peer-reply-cross-channel.md +10 -10
  5. package/.output/.AgentWorkShop/prompts/aml-mission-default.md +20 -20
  6. package/.output/.AgentWorkShop/prompts/aml-playbook.md +49 -49
  7. package/.output/.AgentWorkShop/prompts/host-tools.json +1293 -1238
  8. package/.output/.AgentWorkShop/prompts/peer-reply-cross-channel.md +10 -10
  9. package/.output/nitro.json +1 -1
  10. package/.output/public/_nuxt/88GOowgO2.js +1 -0
  11. package/.output/public/_nuxt/{BNT8gYU92.js → BC9wjTJK2.js} +2 -2
  12. package/.output/public/_nuxt/BOt2SP4z.js +1 -0
  13. package/.output/public/_nuxt/BP-NyKVG.js +18 -0
  14. package/.output/public/_nuxt/{HBLJ-wei.js → BXxH0UQE.js} +1 -1
  15. package/.output/public/_nuxt/{zIGh7R49.js → BneN7fbu.js} +1 -1
  16. package/.output/public/_nuxt/BsROmY89.js +1 -0
  17. package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
  18. package/.output/public/_nuxt/CFxtLwjp.js +1 -0
  19. package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
  20. package/.output/public/_nuxt/C_iYD-EB.js +1 -0
  21. package/.output/public/_nuxt/{DAe0h2MB.js → ClILjvWH.js} +1 -1
  22. package/.output/public/_nuxt/CssgYAAL2.js +1 -0
  23. package/.output/public/_nuxt/{CY9LbPCI.js → CzOCSGLe.js} +1 -1
  24. package/.output/public/_nuxt/D8qvkfyY.js +1 -0
  25. package/.output/public/_nuxt/DCn7QZ5N.js +2 -0
  26. package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
  27. package/.output/public/_nuxt/{CojCTLKD.js → DGj-WEBg.js} +1 -1
  28. package/.output/public/_nuxt/{DDShH71m.js → DS8qj8EL.js} +1 -1
  29. package/.output/public/_nuxt/DTFCy6h-.js +1 -0
  30. package/.output/public/_nuxt/{DrYV5i68.js → DUjW5Yq5.js} +1 -1
  31. package/.output/public/_nuxt/DZshyNLa.js +1 -0
  32. package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
  33. package/.output/public/_nuxt/DcsCcxp4.js +1 -0
  34. package/.output/public/_nuxt/{DUrecWPx.js → Dihnimlk.js} +2 -2
  35. package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
  36. package/.output/public/_nuxt/{BG8sCcwx.js → Dr9v6C0f.js} +9 -9
  37. package/.output/public/_nuxt/{8NMOXZOV.js → Du9ZJtnj.js} +1 -1
  38. package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
  39. package/.output/public/_nuxt/_id_.CM4bIgKY.css +1 -0
  40. package/.output/public/_nuxt/_id_.bNV69Dtf.css +1 -0
  41. package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
  42. package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
  43. package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
  44. package/.output/public/_nuxt/builds/latest.json +1 -1
  45. package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +1 -0
  46. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
  47. package/.output/public/_nuxt/daq.B0D3dPAc.css +1 -0
  48. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
  49. package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
  50. package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -0
  51. package/.output/public/_nuxt/logs.BpKzmKsh.css +1 -0
  52. package/.output/public/_nuxt/monitor.D9pe7qN6.css +1 -0
  53. package/.output/public/_nuxt/pages.D7aSDlUI.css +1 -0
  54. package/.output/public/_nuxt/permissions.DwTVlFe6.css +1 -0
  55. package/.output/public/_nuxt/plugins.sb4nDGqr.css +1 -0
  56. package/.output/public/_nuxt/{Cbq-mryP.js → qr6R6Ha1.js} +1 -1
  57. package/.output/public/_nuxt/settings.BTgR-NiE.css +1 -0
  58. package/.output/public/_nuxt/teams.CxnxKVVs.css +1 -0
  59. package/.output/public/_nuxt/tokens.BFY7qv4f.css +1 -0
  60. package/.output/public/_nuxt/{town.DKIAhKnx.css → town.BBXZmeoP.css} +1 -1
  61. package/.output/public/_nuxt/users.DLgZ1D0h.css +1 -0
  62. package/.output/public/_nuxt/workshop.CR_QzeAN.css +1 -0
  63. package/.output/public/_nuxt/{BzHEilTt.js → xh3_vYcu.js} +1 -1
  64. package/.output/public/_nuxt/yF0aYMF1.js +4 -0
  65. package/.output/server/chunks/_/entity.mjs.map +1 -1
  66. package/.output/server/chunks/_/nitro.mjs +2244 -1464
  67. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  68. package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView-BuLAsmpR.mjs} +288 -206
  69. package/.output/server/chunks/build/TownView-BuLAsmpR.mjs.map +1 -0
  70. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-BU1wfOID.mjs} +138 -138
  71. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs.map → _id_-BU1wfOID.mjs.map} +1 -1
  72. package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-BYsEua57.mjs} +33 -32
  73. package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +1 -0
  74. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BxlDcJHg.mjs} +293 -259
  75. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BxlDcJHg.mjs.map} +1 -1
  76. package/.output/server/chunks/build/{agents-Bvin73QY.mjs → agents-CLQqJF2y.mjs} +21 -27
  77. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +1 -0
  78. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs → aml-_7FdFL2p.mjs} +109 -109
  79. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs.map → aml-_7FdFL2p.mjs.map} +1 -1
  80. package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -1
  81. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs → channel-templates-p6SZNjlM.mjs} +19 -19
  82. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs.map → channel-templates-p6SZNjlM.mjs.map} +1 -1
  83. package/.output/server/chunks/build/{daq-C03Uu5UE.mjs → daq-cgyjKmJu.mjs} +188 -122
  84. package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +1 -0
  85. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-CoTxBlAZ.mjs} +45 -45
  86. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-CoTxBlAZ.mjs.map} +1 -1
  87. package/.output/server/chunks/build/{default-hseyOqBT.mjs → default-BE4r0FZN.mjs} +158 -79
  88. package/.output/server/chunks/build/{default-hseyOqBT.mjs.map → default-BE4r0FZN.mjs.map} +1 -1
  89. package/.output/server/chunks/build/{logs-B-xhuqln.mjs → logs-BSOeZwwI.mjs} +34 -34
  90. package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs-BSOeZwwI.mjs.map} +1 -1
  91. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs → monitor-C0okQaXz.mjs} +33 -33
  92. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs.map → monitor-C0okQaXz.mjs.map} +1 -1
  93. package/.output/server/chunks/build/{pages-BO7yZsXA.mjs → pages-DFxjn9Xa.mjs} +33 -17
  94. package/.output/server/chunks/build/pages-DFxjn9Xa.mjs.map +1 -0
  95. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs → permissions-68SsNjex.mjs} +12 -12
  96. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs.map → permissions-68SsNjex.mjs.map} +1 -1
  97. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs → plugins-lGlUr1lj.mjs} +17 -17
  98. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs.map → plugins-lGlUr1lj.mjs.map} +1 -1
  99. package/.output/server/chunks/build/{settings-DoQ2Oq3P.mjs → settings-Ddd1qx2Z.mjs} +50 -47
  100. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +1 -0
  101. package/.output/server/chunks/build/{teams-2kz3CusY.mjs → teams-MEDLZdMn.mjs} +38 -34
  102. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +1 -0
  103. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs → tokens-C7fTM5C2.mjs} +14 -14
  104. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs.map → tokens-C7fTM5C2.mjs.map} +1 -1
  105. package/.output/server/chunks/build/{town-BOl6hs9B.mjs → town-qMPql9Se.mjs} +36 -10
  106. package/.output/server/chunks/build/town-qMPql9Se.mjs.map +1 -0
  107. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs +1 -1
  108. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs +1 -1
  109. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
  110. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
  111. package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
  112. package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
  113. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
  114. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
  115. package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
  116. package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
  117. package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
  118. package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
  120. package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
  121. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
  122. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
  123. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
  124. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
  125. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  126. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  127. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs.map +1 -1
  128. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
  129. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
  131. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
  133. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
  134. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
  135. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
  137. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
  139. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  140. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
  141. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
  142. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
  143. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
  144. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
  145. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
  146. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
  147. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
  148. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
  149. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
  150. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
  151. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
  152. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
  153. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
  154. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
  155. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
  156. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
  157. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  159. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  160. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  161. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  162. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  164. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +2 -0
  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 +53 -25
  175. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  176. package/.output/server/package.json +1 -1
  177. package/README-zh.md +204 -40
  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 -24
  182. package/app/components/AppSidebar.vue +118 -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/useDcwStream.ts +1 -1
  206. package/app/composables/workshop/useVisibleInterval.ts +2 -2
  207. package/app/composables/workshop/useWorkshopApi.ts +1 -1
  208. package/app/layouts/default.vue +46 -2
  209. package/app/pages/aml/index.vue +712 -616
  210. package/app/pages/daq/[id].vue +3 -2
  211. package/app/pages/daq/index.vue +209 -9
  212. package/app/pages/dcw/[id].vue +109 -28
  213. package/app/pages/dcw/index.vue +83 -23
  214. package/app/pages/index.vue +35 -1
  215. package/app/pages/logs/index.vue +147 -13
  216. package/app/pages/monitor.vue +56 -12
  217. package/app/pages/permissions/index.vue +34 -0
  218. package/app/pages/plugins/index.vue +35 -4
  219. package/app/pages/settings.vue +90 -21
  220. package/app/pages/tokens.vue +23 -0
  221. package/app/pages/town.vue +50 -5
  222. package/app/pages/users.vue +23 -0
  223. package/app/pages/workshop/agents.vue +92 -2
  224. package/app/pages/workshop/channel-templates.vue +53 -0
  225. package/app/pages/workshop/index.vue +14 -0
  226. package/app/pages/workshop/teams.vue +66 -6
  227. package/app/pages/workshop/w/[wsId].vue +202 -14
  228. package/app/plugins/aw-plugins.client.ts +13 -8
  229. package/app/stores/app.ts +33 -1
  230. package/app/stores/workshop/user.ts +1 -1
  231. package/app/stores/workshop/workspaces.ts +18 -2
  232. package/app/utils/api-error.ts +1 -1
  233. package/cli/commands/build.mjs +8 -1
  234. package/docker-compose.yml +6 -0
  235. package/docs/full-test-plan.md +4 -0
  236. package/docs/plugins.en.md +1 -0
  237. package/docs/plugins.md +1 -0
  238. package/i18n/locales/en.ts +3 -0
  239. package/i18n/locales/zh-CN.ts +3 -0
  240. package/nuxt.config.ts +31 -0
  241. package/package.json +9 -1
  242. package/scripts/_audit/detached-start.mjs +103 -0
  243. package/scripts/_audit/guard-upstream-liveness.mjs +104 -0
  244. package/scripts/_dbg-param-map-e2e.ts +242 -0
  245. package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
  246. package/scripts/_dbg-param-real-scenario.mjs +153 -0
  247. package/scripts/_dbg-perms-e2e.mjs +10 -3
  248. package/scripts/_dbg-protocol-matrix.mjs +453 -441
  249. package/scripts/_dbg-render-regression.mjs +151 -130
  250. package/scripts/_dbg-town-agents-probe.mjs +60 -0
  251. package/scripts/_dbg-visual-acceptance.mjs +63 -0
  252. package/scripts/api-live-e2e.mjs +40 -8
  253. package/scripts/capture-walkthrough.mjs +232 -0
  254. package/scripts/e2e-agent-team.mjs +5 -3
  255. package/scripts/render-walkthrough-figure.mjs +46 -0
  256. package/scripts/seed-walkthrough-fixture.mjs +143 -0
  257. package/scripts/test-plugin-hardening.mjs +8 -0
  258. package/scripts/three-system-e2e.mjs +6 -6
  259. package/scripts/ui/admin-shots.mjs +46 -0
  260. package/scripts/ui/audit-layout.mjs +301 -0
  261. package/scripts/ui/bump-microfont.mjs +39 -0
  262. package/scripts/ui/lib.mjs +198 -0
  263. package/scripts/ui/linkcheck2.mjs +37 -0
  264. package/scripts/ui/live-render-check.mjs +72 -0
  265. package/scripts/ui/md-lint.mjs +80 -0
  266. package/scripts/ui/perf-audit.mjs +132 -0
  267. package/scripts/ui/readme-assets.mjs +30 -0
  268. package/scripts/ui/readme-shots.mjs +42 -0
  269. package/scripts/ui/record.mjs +225 -0
  270. package/scripts/ui/seed-admin.mjs +68 -0
  271. package/scripts/ui/seed-demo.mjs +83 -0
  272. package/scripts/ui/shot-hero.mjs +25 -0
  273. package/scripts/ui/shots.mjs +75 -0
  274. package/scripts/ui/sim-ui-shot.mjs +16 -0
  275. package/scripts/ui/verify-nav.mjs +51 -0
  276. package/scripts/ui/verify-responsive.mjs +285 -0
  277. package/server/api/system/config-groups/[id].patch.ts +1 -1
  278. package/server/api/system/config-groups/index.post.ts +1 -1
  279. package/server/api/system/settings/events.get.ts +1 -1
  280. package/server/api/system/settings/index.patch.ts +1 -1
  281. package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
  282. package/server/api/workshop/aml/datasets/[id].get.ts +8 -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 +2 -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 +14 -3
  299. package/server/plugins/dev-stability-guard.ts +3 -1
  300. package/server/plugins/system-config.ts +6 -1
  301. package/server/plugins-builtin/rag-bridge/index.mjs +9 -2
  302. package/server/services/system-config.ts +28 -9
  303. package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
  304. package/server/services/workshop/agents/agent-interface.ts +8 -6
  305. package/server/services/workshop/agents/base-agent.ts +18 -4
  306. package/server/services/workshop/agents/claude-agent.ts +7 -3
  307. package/server/services/workshop/agents/codex-agent.ts +3 -3
  308. package/server/services/workshop/agents/dsh-agent.ts +3 -3
  309. package/server/services/workshop/agents/goose-agent.ts +0 -1
  310. package/server/services/workshop/agents/harness-terminal.ts +14 -1
  311. package/server/services/workshop/agents/hermes-agent.ts +4 -2
  312. package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
  313. package/server/services/workshop/agents/industrial-context.ts +4 -1
  314. package/server/services/workshop/agents/industrial-tools.ts +164 -6
  315. package/server/services/workshop/agents/omp-agent.ts +15 -9
  316. package/server/services/workshop/agents/opencode-agent.ts +47 -21
  317. package/server/services/workshop/agents/qwen-agent.ts +10 -5
  318. package/server/services/workshop/aml/entity.ts +3 -1
  319. package/server/services/workshop/aml/python-runtime.ts +3 -2
  320. package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
  321. package/server/services/workshop/daq/daq-controller.ts +18 -3
  322. package/server/services/workshop/daq/daq-runtime.ts +5 -3
  323. package/server/services/workshop/daq/drivers.ts +63 -8
  324. package/server/services/workshop/daq/host-ports.ts +3 -2
  325. package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
  326. package/server/services/workshop/db/channel.repo.ts +4 -0
  327. package/server/services/workshop/db/ops.repo.ts +5 -2
  328. package/server/services/workshop/db/retention.ts +8 -3
  329. package/server/services/workshop/dcw/dcw-controller.ts +143 -16
  330. package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
  331. package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
  332. package/server/services/workshop/dcw/drivers.ts +5 -2
  333. package/server/services/workshop/dcw/param-conversion.ts +78 -0
  334. package/server/services/workshop/dcw/param-limits.ts +142 -0
  335. package/server/services/workshop/dcw/param-map.repo.ts +305 -0
  336. package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
  337. package/server/services/workshop/runtime/agent-runtime.ts +4 -0
  338. package/server/services/workshop/runtime/manager.ts +9 -2
  339. package/server/services/workshop/settings.ts +4 -2
  340. package/server/types/pg.d.ts +49 -0
  341. package/server/utils/stability-severity.ts +13 -2
  342. package/shared/config/groups.mjs +22 -1
  343. package/shared/config/home.d.mts +28 -0
  344. package/shared/dcw-protocol.ts +136 -0
  345. package/uno.config.ts +12 -1
  346. package/.output/public/_nuxt/BFkgkW8u.js +0 -4
  347. package/.output/public/_nuxt/C5gUkRwn.js +0 -1
  348. package/.output/public/_nuxt/CM12FuB42.js +0 -1
  349. package/.output/public/_nuxt/CZiw7sEV.js +0 -1
  350. package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
  351. package/.output/public/_nuxt/CdPzkOMV.js +0 -1
  352. package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
  353. package/.output/public/_nuxt/D20Yamr4.js +0 -1
  354. package/.output/public/_nuxt/DVsjtsvZ2.js +0 -1
  355. package/.output/public/_nuxt/DmqknNvV.js +0 -18
  356. package/.output/public/_nuxt/DnUuIR83.js +0 -1
  357. package/.output/public/_nuxt/S_fVQwJK.js +0 -1
  358. package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
  359. package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
  360. package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
  361. package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
  362. package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
  363. package/.output/public/_nuxt/ahgEJu2R.js +0 -1
  364. package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
  365. package/.output/public/_nuxt/builds/meta/44b66249-54d0-4c8a-9057-9755a32329e2.json +0 -1
  366. package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
  367. package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
  368. package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
  369. package/.output/public/_nuxt/default.DE_vT238.css +0 -1
  370. package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
  371. package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
  372. package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
  373. package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
  374. package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
  375. package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
  376. package/.output/public/_nuxt/pz2JDAM7.js +0 -2
  377. package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
  378. package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
  379. package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
  380. package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
  381. package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
  382. package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
  383. package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
  384. package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
  385. package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
  386. package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
  387. package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
  388. package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
  389. package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
  390. package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
  391. /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
  392. /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
@@ -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()
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 抽屉导航交互验收 —— 断言"汉堡能开、遮罩能关、Esc 能关、路由跳转自动收"。
3
+ * 这些是布局量测不到的行为,必须真的点。
4
+ */
5
+ import { ensureVisualUser, launch, openPage, gotoReady, sleep } from './lib.mjs'
6
+
7
+ const token = await ensureVisualUser()
8
+ const browser = await launch()
9
+ const page = await openPage(browser, { token, dark: true, width: 390, height: 844, deviceScaleFactor: 1 })
10
+ const errs = []
11
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 160)))
12
+ const results = []
13
+ const check = (name, ok, detail = '') => {
14
+ results.push({ name, ok, detail })
15
+ console.log(`${ok ? ' ok ' : 'FAIL'} ${name} ${detail}`)
16
+ }
17
+
18
+ await gotoReady(page, '/daq', { wait: 2500 })
19
+ const siderX = () => page.evaluate(() => {
20
+ const s = document.querySelector('.app-sider')
21
+ return s ? Math.round(s.getBoundingClientRect().x) : null
22
+ })
23
+ check('窄屏侧栏初始在画布外', (await siderX()) < 0, `x=${await siderX()}`)
24
+ check('body 带 nav-drawer', await page.evaluate(() => document.body.classList.contains('nav-drawer')))
25
+
26
+ await page.click('.collapse-btn')
27
+ await sleep(600)
28
+ check('点汉堡后抽屉进入画布', (await siderX()) >= -2, `x=${await siderX()}`)
29
+ check('遮罩出现', await page.evaluate(() => !!document.querySelector('.nav-scrim')))
30
+ check('画面滚动被锁', await page.evaluate(() => document.body.style.overflow === 'hidden'))
31
+
32
+ await page.keyboard.press('Escape')
33
+ await sleep(600)
34
+ check('Esc 关闭抽屉', (await siderX()) < 0, `x=${await siderX()}`)
35
+ check('滚动锁解除', await page.evaluate(() => document.body.style.overflow !== 'hidden'))
36
+
37
+ await page.click('.collapse-btn')
38
+ await sleep(500)
39
+ await page.evaluate(() => {
40
+ const el = Array.from(document.querySelectorAll('.menu-item')).find(e => (e.textContent || '').includes('产线运营'))
41
+ el?.click()
42
+ })
43
+ await sleep(2200)
44
+ check('点菜单后路由跳转', page.url().includes('/dcw'), page.url())
45
+ check('跳转后抽屉自动收起', (await siderX()) < 0, `x=${await siderX()}`)
46
+ check('无 pageerror', errs.length === 0, errs.join(' | ').slice(0, 200))
47
+
48
+ await browser.close()
49
+ const failed = results.filter(r => !r.ok).length
50
+ console.log(`\n=== 抽屉交互:${results.length - failed} 通过 / ${failed} 失败 ===`)
51
+ process.exit(failed ? 1 : 0)
@@ -0,0 +1,285 @@
1
+ /**
2
+ * 自适应验收门 —— 全站 × 视口矩阵 × 明暗双主题的**可判定**检查。
3
+ *
4
+ * 和 audit-layout.mjs 的区别:audit 是"体检报告"(尽量多报,供人工判断),
5
+ * verify 是"门禁"(只报确定是缺陷的,并给出通过/失败)。
6
+ * 判定阈值都在下面 THRESHOLDS 里,改阈值必须写理由 —— 这是全站验收的唯一口径。
7
+ *
8
+ * 用法: node scripts/ui/verify-responsive.mjs [--routes all] [--theme both] [--json out.json]
9
+ */
10
+ import fs from 'node:fs'
11
+ import path from 'node:path'
12
+ import { ensureVisualUser, launch, openPage, gotoReady, ROUTES, VIEWPORTS } 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 ROUTE_ARG = arg('routes', 'all')
20
+ const THEME = arg('theme', 'both')
21
+ const VP_ARG = arg('vp', '')
22
+ const WAIT = Number(arg('wait', 3500))
23
+ const JSON_OUT = arg('json', '.e2e-shots/verify/verify.json')
24
+ const routes = ROUTE_ARG === 'all' ? ROUTES : ROUTES.filter(r => ROUTE_ARG.split(',').includes(r.name))
25
+ const themes = THEME === 'both' ? [true, false] : [THEME === 'dark']
26
+ // --vp 收窄视口矩阵(联调期用;终验不带该参数,跑全矩阵)
27
+ const viewports = VP_ARG
28
+ ? VP_ARG.split(',').map((s) => {
29
+ const [w, h] = s.split('x').map(Number)
30
+ return VIEWPORTS.find(v => v.width === w && v.height === h) ?? { name: `${w}x${h}`, width: w, height: h, label: `${w}×${h}` }
31
+ })
32
+ : VIEWPORTS
33
+
34
+ /**
35
+ * 验收阈值(唯一口径)。
36
+ * - minFontPhone 11:窄屏(≤640)手持视距下,10px mono 标签只剩 6px 有效字号,读不了。
37
+ * - minFontDesktop 9.5:桌面保留"仪器铭牌"的 10px mono 微字是刻意的设计语言,
38
+ * 但低于 9.5px 连铭牌都算不上,一律判失败。
39
+ * - contrast AA:WCAG 2.1 正文 4.5:1。小字号的 mono 标签没有"large text"豁免。
40
+ */
41
+ const THRESHOLDS = {
42
+ minFontPhone: 11,
43
+ minFontDesktop: 9.5,
44
+ minTouch: 30,
45
+ minContrast: 4.5,
46
+ }
47
+
48
+ const AUDIT = function (th) {
49
+ const VW = window.innerWidth
50
+ const issues = []
51
+ const isSvg = el => el.namespaceURI === 'http://www.w3.org/2000/svg'
52
+ const describe = (el) => {
53
+ const cls = (typeof el.className === 'string' && el.className)
54
+ ? '.' + el.className.trim().split(/\s+/).slice(0, 2).join('.')
55
+ : ''
56
+ return `${el.tagName.toLowerCase()}${el.id ? '#' + el.id : ''}${cls}`
57
+ }
58
+ const txt = el => (el.innerText || el.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 50)
59
+ const rect = el => el.getBoundingClientRect()
60
+ const visible = (el) => {
61
+ const r = rect(el)
62
+ if (r.width < 1 || r.height < 1) return false
63
+ const cs = getComputedStyle(el)
64
+ if (cs.visibility === 'hidden' || cs.display === 'none' || Number(cs.opacity) < 0.05) return false
65
+ if (el.closest('[aria-hidden="true"]')) return false
66
+ // 被祖先的 overflow:hidden 整体推出可视区 = 看不见。
67
+ // 缺这一条会把 antd Switch 的"另一态文案"也算成可见文本(它在轨道外,被裁掉),
68
+ // 于是稳定报出 1.83:1 的假阳性(实测)。
69
+ for (let p = el.parentElement; p && p !== document.body; p = p.parentElement) {
70
+ const pcs = getComputedStyle(p)
71
+ if (pcs.overflow === 'visible' && pcs.overflowX === 'visible' && pcs.overflowY === 'visible') continue
72
+ const pr = p.getBoundingClientRect()
73
+ if (
74
+ r.right <= pr.left + 0.5 || r.left >= pr.right - 0.5
75
+ || r.bottom <= pr.top + 0.5 || r.top >= pr.bottom - 0.5
76
+ ) return false
77
+ }
78
+ return true
79
+ }
80
+ const inScroller = (el) => {
81
+ let p = el.parentElement
82
+ while (p && p !== document.body) {
83
+ const cs = getComputedStyle(p)
84
+ if (['auto', 'scroll', 'hidden'].includes(cs.overflowX)) return p
85
+ p = p.parentElement
86
+ }
87
+ return null
88
+ }
89
+
90
+ const de = document.documentElement
91
+ if (de.scrollWidth - de.clientWidth > 1) issues.push({ kind: 'doc-overflow-x', px: de.scrollWidth - de.clientWidth })
92
+
93
+ const all = Array.from(document.querySelectorAll('body *'))
94
+
95
+ // 侧栏形态(结构性断言:窄屏必须在文档流外)
96
+ const sider = document.querySelector('.app-sider')
97
+ const siderCs = sider ? getComputedStyle(sider) : null
98
+ const expectDrawer = VW < 900
99
+ if (sider && siderCs) {
100
+ const isFixed = siderCs.position === 'fixed'
101
+ if (expectDrawer && !isFixed) issues.push({ kind: 'nav-should-be-drawer', actual: siderCs.position, vw: VW })
102
+ if (!expectDrawer && isFixed) issues.push({ kind: 'nav-should-be-inline', actual: siderCs.position, vw: VW })
103
+ if (expectDrawer) {
104
+ const r = rect(sider)
105
+ if (r.right > 1) issues.push({ kind: 'drawer-not-offcanvas', right: Math.round(r.right) })
106
+ }
107
+ }
108
+
109
+ for (const el of all) {
110
+ if (isSvg(el)) continue // SVG 内部(ECharts 轴标签等)不是布局缺陷来源
111
+ if (!visible(el)) continue
112
+ const cs = getComputedStyle(el)
113
+ const r = rect(el)
114
+ const scroller = inScroller(el)
115
+
116
+ if (!scroller && cs.position !== 'fixed' && r.right > VW + 2) {
117
+ issues.push({ kind: 'el-overflow-right', el: describe(el), text: txt(el), over: Math.round(r.right - VW) })
118
+ }
119
+ if (el.children.length === 0) {
120
+ const t = (el.textContent || '').trim()
121
+ if (t.length > 1) {
122
+ const fs = parseFloat(cs.fontSize)
123
+ const floor = VW <= 640 ? th.minFontPhone : th.minFontDesktop
124
+ if (fs && fs < floor) issues.push({ kind: 'font-too-small', el: describe(el), text: t.slice(0, 30), px: fs, floor })
125
+ }
126
+ }
127
+ if (VW <= 900) {
128
+ const clickable = el.matches('button, a[href], [role="button"], .ant-switch, .ant-segmented-item, .ant-checkbox-wrapper')
129
+ // 命中区可以被伪元素外扩(把 16×16 的图标变成 40px 可点目标 —— 这是正当做法,
130
+ // 而且比"把图标本身画大"更尊重既有密度)。只看 rect 会把"做得对"判成"做得错",
131
+ // 所以要连伪元素一起量:要么它有实际的宽高,要么它用负 inset 撑开四边。
132
+ const hit = (pseudo) => {
133
+ const cs2 = getComputedStyle(el, pseudo)
134
+ if (!cs2 || cs2.content === 'none' || cs2.position !== 'absolute') return false
135
+ const w = Number.parseFloat(cs2.width)
136
+ const h = Number.parseFloat(cs2.height)
137
+ if (Number.isFinite(w) && Number.isFinite(h) && w >= th.minTouch && h >= th.minTouch) return true
138
+ const inset = ['top', 'left', 'right', 'bottom'].map(s => Number.parseFloat(cs2[s]))
139
+ return inset.every(v => Number.isFinite(v) && v <= -8)
140
+ }
141
+ const expanded = hit('::after') || hit('::before')
142
+ // 带文字的按钮/链接:宽度由文案决定,不该按 40px 判死;真正的要求是**高度**够。
143
+ // 纯图标控件(无文字)才要求两个方向都够,或已被伪元素外扩。
144
+ const hasText = txt(el).length > 0
145
+ const tooSmall = hasText
146
+ ? r.height < th.minTouch
147
+ : (r.width < th.minTouch || r.height < th.minTouch)
148
+ if (clickable && el.children.length === 0 && !expanded && tooSmall) {
149
+ issues.push({ kind: 'touch-target-small', el: describe(el), text: txt(el), w: Math.round(r.width), h: Math.round(r.height) })
150
+ }
151
+ }
152
+ if (cs.position === 'fixed' && (r.right > VW + 2 || r.left < -2) && el !== sider && !el.closest('.app-sider')) {
153
+ issues.push({ kind: 'fixed-out-of-view', el: describe(el), text: txt(el), left: Math.round(r.left), right: Math.round(r.right) })
154
+ }
155
+ }
156
+
157
+ // 对比度
158
+ const parseColor = (s) => {
159
+ const m = s.match(/rgba?\(([^)]+)\)/)
160
+ if (!m) return null
161
+ const p = m[1].split(',').map(Number)
162
+ return { r: p[0], g: p[1], b: p[2], a: p[3] === undefined ? 1 : p[3] }
163
+ }
164
+ const over = (fg, bg) => ({
165
+ r: fg.r * fg.a + bg.r * (1 - fg.a), g: fg.g * fg.a + bg.g * (1 - fg.a), b: fg.b * fg.a + bg.b * (1 - fg.a), a: 1,
166
+ })
167
+ const effBg = (el) => {
168
+ let p = el, acc = null
169
+ while (p && p !== document.documentElement.parentElement) {
170
+ const c = parseColor(getComputedStyle(p).backgroundColor)
171
+ if (c && c.a > 0.01) acc = acc ? over(acc, c) : c
172
+ if (acc && acc.a >= 0.999) break
173
+ if (c && c.a >= 0.999) break
174
+ p = p.parentElement
175
+ }
176
+ return acc ?? { r: 255, g: 255, b: 255, a: 1 }
177
+ }
178
+ const lum = (c) => {
179
+ const f = (v) => {
180
+ const x = v / 255
181
+ return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4
182
+ }
183
+ return 0.2126 * f(c.r) + 0.7152 * f(c.g) + 0.0722 * f(c.b)
184
+ }
185
+ const ratio = (a, b) => {
186
+ const l1 = lum(a)
187
+ const l2 = lum(b)
188
+ return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05)
189
+ }
190
+ const seen = new Set()
191
+ let worst = { ratio: 99, el: '', text: '' }
192
+ for (const el of all) {
193
+ if (isSvg(el) || el.children.length > 0) continue
194
+ const t = (el.textContent || '').trim()
195
+ if (t.length < 2 || !visible(el)) continue
196
+ // 非活动控件豁免:WCAG 1.4.3 明确把"未激活的界面组件"排除在对比度要求之外,
197
+ // 而 antd 的 disabled 文本用的是 ~25% 不透明度(实测 2.2:1 是**设计如此**)。
198
+ // 把它们算作失败,等于要求一个不存在的标准。
199
+ if (el.closest('[disabled], .ant-btn-disabled, [aria-disabled="true"], .ant-select-disabled')) continue
200
+ const cs = getComputedStyle(el)
201
+ const fg = parseColor(cs.color)
202
+ if (!fg) continue
203
+ // 背景是渐变/图片时对比度**无法**从计算样式推导(背景色只是垫底那一层)。
204
+ // 把这种元素报成"不达标"是假阳性 —— 实测 .avatar-fallback(绿青渐变 + 墨字)
205
+ // 被算成 1.06:1,而真实观感远高于 AA。宁可漏报,不要制造噪音。
206
+ let onImage = false
207
+ for (let p = el; p && p !== document.documentElement; p = p.parentElement) {
208
+ if (getComputedStyle(p).backgroundImage !== 'none') {
209
+ onImage = true
210
+ break
211
+ }
212
+ }
213
+ if (onImage) continue
214
+ const bg = effBg(el)
215
+ const cr = ratio(over(fg, bg), bg)
216
+ const fs = parseFloat(cs.fontSize) || 14
217
+ const bold = Number(cs.fontWeight) >= 700
218
+ const large = fs >= 24 || (fs >= 18.66 && bold)
219
+ const need = large ? 3 : th.minContrast
220
+ if (cr < worst.ratio) worst = { ratio: +cr.toFixed(2), el: describe(el), text: t.slice(0, 30) }
221
+ if (cr < need) {
222
+ const key = describe(el) + t.slice(0, 10)
223
+ if (seen.has(key)) continue
224
+ seen.add(key)
225
+ const hex = (c) => {
226
+ const h = v => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, '0')
227
+ return '#' + h(c.r) + h(c.g) + h(c.b)
228
+ }
229
+ issues.push({
230
+ kind: 'contrast', el: describe(el), text: t.slice(0, 30),
231
+ ratio: +cr.toFixed(2), need, px: fs, fg: hex(over(fg, bg)), bg: hex(bg),
232
+ })
233
+ }
234
+ }
235
+
236
+ const counts = {}
237
+ for (const i of issues) counts[i.kind] = (counts[i.kind] || 0) + 1
238
+ return {
239
+ vw: VW, counts, total: issues.length, worstContrast: worst,
240
+ issues: issues.slice(0, 60),
241
+ nav: siderCs ? { pos: siderCs.position, w: Math.round(rect(sider).width) } : null,
242
+ }
243
+ }
244
+
245
+ fs.mkdirSync(path.dirname(JSON_OUT), { recursive: true })
246
+ const token = await ensureVisualUser()
247
+ const browser = await launch()
248
+ const rows = []
249
+ let failures = 0
250
+
251
+ for (const vp of viewports) {
252
+ for (const dark of themes) {
253
+ for (const r of routes) {
254
+ const page = await openPage(browser, { token, dark, width: vp.width, height: vp.height, deviceScaleFactor: 1 })
255
+ const errs = []
256
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 160)))
257
+ try {
258
+ await gotoReady(page, r.path, { wait: WAIT })
259
+ const res = await page.evaluate(AUDIT, THRESHOLDS)
260
+ res.route = r.name
261
+ res.path = r.path
262
+ res.vp = vp.name
263
+ res.theme = dark ? 'dark' : 'light'
264
+ res.pageErrors = errs
265
+ const bad = res.total + errs.length
266
+ if (bad) failures++
267
+ rows.push(res)
268
+ console.log(`${bad ? 'FAIL' : ' ok '} ${vp.name.padEnd(16)} ${(dark ? 'dark' : 'light').padEnd(5)} ${r.name.padEnd(12)} ${JSON.stringify(res.counts)}${errs.length ? ' errs=' + errs.length : ''}`)
269
+ }
270
+ catch (e) {
271
+ failures++
272
+ rows.push({ route: r.name, vp: vp.name, theme: dark ? 'dark' : 'light', error: String(e.message).slice(0, 200) })
273
+ console.log(`FAIL ${vp.name} ${r.name} — ${String(e.message).slice(0, 90)}`)
274
+ }
275
+ await page.close()
276
+ }
277
+ }
278
+ }
279
+
280
+ await browser.close()
281
+ fs.writeFileSync(JSON_OUT, JSON.stringify({ thresholds: THRESHOLDS, failures, rows }, null, 2))
282
+ const total = rows.length
283
+ console.log(`\n=== ${total} 个组合:${total - failures} 通过 / ${failures} 失败 ===`)
284
+ console.log('报告 →', JSON_OUT)
285
+ process.exit(failures ? 1 : 0)
@@ -15,7 +15,7 @@ import { getSystemConfigService } from '../../../services/system-config'
15
15
  export default defineApiHandler(async (event) => {
16
16
  requireRole(event, ['admin'])
17
17
  const id = String(event.context.params?.id ?? '').trim()
18
- const body = (await readBody(event)) ?? {}
18
+ const body = (await readBody<Record<string, unknown>>(event)) ?? {}
19
19
  // id/source/plugin 是身份字段,不接受伪造
20
20
  const patch = { ...body }
21
21
  delete patch.id
@@ -26,7 +26,7 @@ function slugify(label: string): string {
26
26
 
27
27
  export default defineApiHandler(async (event) => {
28
28
  requireRole(event, ['admin'])
29
- const body = (await readBody(event)) ?? {}
29
+ const body = (await readBody<Record<string, unknown>>(event)) ?? {}
30
30
  const id = String(body.id ?? '').trim() || slugify(String(body.label ?? ''))
31
31
  const group = getSystemConfigService().createGroup({ ...body, id })
32
32
  return { ok: true, group }
@@ -4,7 +4,7 @@
4
4
  * 服务端任何写入(UI / CLI / 文件监听)都会 push { type, changed, restartRequired, effective }。
5
5
  * 连接前先鉴权(无效 token 直接 401,不开流)。
6
6
  */
7
- import { createEventStream } from 'h3'
7
+ import { createEventStream, defineEventHandler } from 'h3'
8
8
  import { resolveUser } from '../../workshop/caller'
9
9
  import { getSystemConfigService, type ConfigEventPayload } from '../../../services/system-config'
10
10
 
@@ -13,7 +13,7 @@ import { getSystemConfigService } from '../../../services/system-config'
13
13
 
14
14
  export default defineApiHandler(async (event) => {
15
15
  requireRole(event, ['admin'])
16
- const body = (await readBody(event)) ?? {}
16
+ const body = (await readBody<Record<string, unknown>>(event)) ?? {}
17
17
  const overrides = body.override ?? body.overrides ?? body
18
18
  if (!overrides || typeof overrides !== 'object' || Array.isArray(overrides)) {
19
19
  return { ok: false, message: '请求体应为 { "override": { key: value } }' }
@@ -18,6 +18,19 @@ import { resolveUser } from '@/server/api/workshop/caller'
18
18
  import { defineApiHandler } from '@/server/utils/response'
19
19
  import { AppError } from '@/server/utils/errors'
20
20
  import { getWorkshopManager } from '@/server/plugins/workshop'
21
+ import type { AgentChannelManager } from '@/server/services/workshop/runtime/manager'
22
+
23
+ /**
24
+ * manager 依赖仓储视图。
25
+ * 公开面没有「按 id 取 channel 行(含 ownerUserId、不抛错)」的等价方法:
26
+ * getChannelForUser 对遗留公共 channel(owner NULL)判放行、对缺失行抛 404,
27
+ * 与本路由「非属主一律 403」的既有口径不同,故直接读 manager.deps.repos
28
+ * (AgentChannelManager 的 deps 已声明为 public readonly —— 见其构造函数;
29
+ * 原写法用 `as unknown as { deps }` 断言绕过 private,现已不需要)。
30
+ */
31
+ function reposOf(manager: AgentChannelManager): AgentChannelManager['deps']['repos'] {
32
+ return manager.deps.repos
33
+ }
21
34
 
22
35
  export default defineApiHandler(async (event) => {
23
36
  const agentToken = getHeader(event, 'x-aw-agent-token')
@@ -38,10 +51,11 @@ export default defineApiHandler(async (event) => {
38
51
  }
39
52
  }
40
53
  else if (user) {
41
- const row = manager.deps.repos.channelAgents.findById(agentId)
54
+ const repos = reposOf(manager)
55
+ const row = repos.channelAgents.findById(agentId)
42
56
  if (!row) throw new AppError(404, 'NOT_FOUND', `agent 不存在: ${agentId}`)
43
57
  if (user.role !== 'admin') {
44
- const channel = manager.deps.repos.channels.findById(row.channelId)
58
+ const channel = repos.channels.findById(row.channelId)
45
59
  if (!channel || channel.ownerUserId !== user.id) {
46
60
  throw new AppError(403, 'SCOPE_VIOLATION', '该 agent 不属于当前用户,无权代为调用其工具')
47
61
  }
@@ -37,7 +37,14 @@ export default defineApiHandler(async (event) => {
37
37
  }
38
38
  }
39
39
  catch (err) {
40
- if (err instanceof AppError && err.statusCode === 410) {
40
+ // ⚠️ 类型漂移记录:AppError 的权威定义只有 status(见 server/utils/errors.ts),没有 statusCode。
41
+ // 原判定读的是不存在的属性 ⇒ 运行时恒为 undefined,这段「实体丢失 → 200 + 元数据告警」兜底
42
+ // **从未命中**(实际行为:loadDatasetEntity 抛 AppError(410) → defineApiHandler 统一转 410 错误信封)。
43
+ // 依「不改变运行时行为」约束,这里只把该事实写实、判定结果保持不变;
44
+ // 若要启用本兜底(与上方文件注释「实体缺失时返回 410 + 元数据行」及前端 report: ... | null 一致),
45
+ // 把下面的 legacyStatusCode 换成 err.status 即可(一行)。
46
+ const legacyStatusCode = (err as { statusCode?: number } | undefined)?.statusCode
47
+ if (err instanceof AppError && legacyStatusCode === 410) {
41
48
  // 元数据仍在但实体丢了:仍然 200 返回元数据 + 明确的实体告警,便于 UI 展示与清理引导
42
49
  return { dataset: ds, report: null, manifest: null, spec: null, entity: { dir: null, arrays: [], sizeBytes: 0, healthy: false, specFromEntity: false, warning: err.message } }
43
50
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { resolveUser } from '@/server/api/workshop/caller'
5
5
  import { defineApiHandler } from '@/server/utils/response'
6
- import { daqInfraConfig, daqInfraStatus } from '@/server/services/workshop/daq/infra'
6
+ import { daqInfraConfig, daqInfraStatus, type DaqInfraConfig } from '@/server/services/workshop/daq/infra'
7
7
 
8
8
  export default defineApiHandler(async (event) => {
9
9
  resolveUser(event)
@@ -2,6 +2,7 @@
2
2
  * DELETE /api/workshop/daq/templates/:key —— 删除自定义信号模板(内置模板拒绝)。
3
3
  * 成功后广播 daq.template.changed(op=removed);已建节点不受影响(域已落节点)。
4
4
  */
5
+ import { getRouterParam } from 'h3'
5
6
  import { resolveUser } from '@/server/api/workshop/caller'
6
7
  import { defineApiHandler } from '@/server/utils/response'
7
8
  import { getDaqTemplateRegistry } from '@/server/services/workshop/daq/daq-templates'
@@ -2,6 +2,7 @@
2
2
  * PATCH /api/workshop/daq/templates/:key —— 编辑自定义信号模板(内置模板拒绝)。
3
3
  * 成功后广播 daq.template.changed(op=updated)。
4
4
  */
5
+ import { getRouterParam, readBody } from 'h3'
5
6
  import { resolveUser } from '@/server/api/workshop/caller'
6
7
  import { defineApiHandler } from '@/server/utils/response'
7
8
  import { getDaqTemplateRegistry } from '@/server/services/workshop/daq/daq-templates'
@@ -2,6 +2,7 @@
2
2
  * POST /api/workshop/daq/templates —— 新建自定义信号模板(登录用户)。
3
3
  * 成功后广播 daq.template.changed(op=added)收敛全部客户端。
4
4
  */
5
+ import { readBody } from 'h3'
5
6
  import { resolveUser } from '@/server/api/workshop/caller'
6
7
  import { defineApiHandler } from '@/server/utils/response'
7
8
  import { getDaqTemplateRegistry } from '@/server/services/workshop/daq/daq-templates'
@@ -3,7 +3,7 @@
3
3
  * 工程量安全校验 → 驱动换算/写/回读校验 → ACK。越界 400;在飞 409。
4
4
  * R1 留痕由 write() 中央埋点统一完成(manual 来源携带真实用户身份)。
5
5
  */
6
- import { getRouterParam, readBody } from 'h3'
6
+ import { getRouterParam, readBody, getRequestHeader } from 'h3'
7
7
  import { resolveUser } from '@/server/api/workshop/caller'
8
8
  import { requireLineMode } from '@/server/services/workshop/permissions'
9
9
  import { defineApiHandler } from '@/server/utils/response'
@@ -17,6 +17,14 @@ export default defineApiHandler(async (event) => {
17
17
  const body = await readBody<{ value?: number }>(event) ?? {}
18
18
  // 产线权限:写控设定值需「可操控」(readonly/无权用户 403)
19
19
  requireLineMode(user, getDcwController().byId(id)?.lineId, 'operate')
20
- const outcome = await getDcwController().write(id, Number(body.value), null, { source: 'manual', actor: user.id, actorName: user.name })
20
+ // D8 基准消融臂透传(仅 AW_BENCH_MODE=1 时生效;生产模式恒 undefined=full 臂)
21
+
22
+ const benchArm = process.env.AW_BENCH_MODE === '1'
23
+
24
+ ? (getRequestHeader(event, 'x-aw-bench-arm') || undefined) as 'no-interlock' | 'no-readback' | 'ungated' | undefined
25
+
26
+ : undefined
27
+
28
+ const outcome = await getDcwController().write(id, Number(body.value), null, { source: 'manual', actor: user.id, actorName: user.name, benchArm })
21
29
  return { outcome }
22
30
  })
@@ -2,7 +2,7 @@
2
2
  * DELETE /api/workshop/dcw/:id —— 删除写控制节点。
3
3
  * R3:admin/editor 才可执行;approvalGate 开启时走双人复核(同 apply 口径)。
4
4
  */
5
- import { getRouterParam, readBody, setResponseStatus } from 'h3'
5
+ import { getQuery, getRouterParam, readBody, setResponseStatus } from 'h3'
6
6
  import { requireRole } from '@/server/api/workshop/caller'
7
7
  import { defineApiHandler } from '@/server/utils/response'
8
8
  import { gateDangerous } from '@/server/utils/approval-gate'
@@ -15,7 +15,10 @@ export default defineApiHandler(async (event) => {
15
15
  bindDcwBroadcast(broadcastSceneEvent)
16
16
  const id = getRouterParam(event, 'id') ?? ''
17
17
  // DELETE 语义下 body 常为空:approvalId 亦接受查询参数(?approvalId=…)
18
- const body = await readBody<{ approvalId?: string }>(event).catch(() => ({})) ?? {}
18
+ // 「无 body」与「body 解析失败」都退化为同一个显式标注的空对象(运行时仍是普通 {} 字面量,
19
+ // 只被读取、不会被修改),避免裸 {} 把 body.approvalId 的类型抹成 {}。
20
+ const emptyBody: { approvalId?: string } = {}
21
+ const body = await readBody<{ approvalId?: string }>(event).catch(() => emptyBody) ?? emptyBody
19
22
  const q = getQuery(event)
20
23
  const approvalId = body.approvalId ?? (typeof q.approvalId === 'string' ? q.approvalId : undefined)
21
24
  const gate = gateDangerous(useRuntimeConfig(event).approvalGate === true, user, { action: 'dcw.delete', targetId: id, summary: `删除写控制节点 ${id}` }, approvalId)
@@ -25,6 +25,8 @@ export default defineApiHandler(async (event) => {
25
25
  ? ctrl.controllerState()
26
26
  : { running: ctrl.controllerState().running === true },
27
27
  nodes: filterByLine(user, ctrl.listViews(), n => n.lineId),
28
+ /** 工艺参数映射面(用户/Agent 的参数语义读写面;PLC 寻址细节不透出) */
29
+ params: visible ? ctrl.listParamViews().filter(p => inLine(p.lineId)) : ctrl.listParamViews(),
28
30
  templates: listDcwTemplates(),
29
31
  recipes: visible ? ctrl.listRecipes().filter(r => inLine(r.lineId)) : ctrl.listRecipes(),
30
32
  runs: visible ? ctrl.listRuns().filter(r => inLine(r.lineId)) : ctrl.listRuns(),
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * PATCH /api/workshop/dcw/lines/:id —— 编辑产线(名称/光晕色/描述)。
3
3
  */
4
- import { readBody } from 'h3'
4
+ import { getRouterParam, readBody } from 'h3'
5
5
  import { resolveUser } from '@/server/api/workshop/caller'
6
6
  import { defineApiHandler } from '@/server/utils/response'
7
7
  import { getDcwController } from '@/server/services/workshop/dcw/dcw-controller'