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,232 @@
1
+ /**
2
+ * Canonical walkthrough-figure capture. High-DPI (3x), element/row-accurate crops
3
+ * of the four operational stages on a live AgentWorkShop instance.
4
+ *
5
+ * The shared production instance carries Chinese-named test data, so the four
6
+ * screenshots are taken against the small ENGLISH fixture created by
7
+ * scripts/seed-walkthrough-fixture.mjs (line "Cast-film line A", English DAQ
8
+ * nodes, an English agent team). The UI is forced to the `en` locale (persisted
9
+ * in localStorage under `aw.locale`, applied by app/plugins/locale-restore.client.ts)
10
+ * before any capture, so every UI string is English.
11
+ *
12
+ * Geometry discipline: every crop is ≤470 CSS px wide (12 px UI text must print
13
+ * ≥ ~6.4 pt at the ~88 mm panel width of the 181 mm figure), except panel 2 whose
14
+ * four table columns cannot fit narrower.
15
+ *
16
+ * Usage:
17
+ * NO_PROXY='127.0.0.1,localhost' node scripts/seed-walkthrough-fixture.mjs
18
+ * NO_PROXY='127.0.0.1,localhost' AW_BASE=http://127.0.0.1:3005 node scripts/capture-walkthrough.mjs
19
+ */
20
+ import puppeteer from 'puppeteer-core'
21
+ import fs from 'node:fs'
22
+ import path from 'node:path'
23
+
24
+ const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3005'
25
+ const OUT = process.env.AW_OUT ?? 'paper/tii/figures/walkthrough'
26
+ fs.mkdirSync(OUT, { recursive: true })
27
+
28
+ const sleep = ms => new Promise(r => setTimeout(r, ms))
29
+ const tok = async (e, p) => {
30
+ const r = await (await fetch(`${BASE}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: e, password: p }) })).json()
31
+ if (!r.data?.token) throw new Error(`login failed: ${e} (${JSON.stringify(r).slice(0, 120)})`)
32
+ return r.data.token
33
+ }
34
+ const ADMIN = await tok('admin@awshop.local', 'admin123')
35
+
36
+ const jget = async (u) => {
37
+ const r = await (await fetch(`${BASE}${u}`, { headers: { Authorization: `Bearer ${ADMIN}` } })).json()
38
+ return r.data
39
+ }
40
+
41
+ // ── fixture discovery (fail loudly rather than silently capturing Chinese) ──
42
+ const LINES = (await jget('/api/workshop/dcw/lines')).lines
43
+ const LINE = LINES.find(l => l.name === 'Cast-film line A')
44
+ if (!LINE) throw new Error('English fixture line "Cast-film line A" missing — run scripts/seed-walkthrough-fixture.mjs first')
45
+ const DAQ = await jget('/api/workshop/daq')
46
+ const modbusNode = DAQ.nodes.find(n => n.lineId === LINE.id && n.driver === 'modbus-tcp')
47
+ if (!modbusNode) throw new Error('fixture Modbus-TCP node missing on Cast-film line A')
48
+ // panel 4 uses the real judged optimization records already on the ledger of this node
49
+ const RECORD_LINE = 'ln-af002514'
50
+ const RECORD_NODE = 'dw-322b1978'
51
+ console.log(`fixture line=${LINE.id} modbus=${modbusNode.id} (${modbusNode.name}) records=${RECORD_NODE}`)
52
+
53
+ const browser = await puppeteer.launch({
54
+ executablePath: process.env.AW_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
55
+ headless: 'new',
56
+ args: ['--no-sandbox', '--disable-gpu', '--proxy-server=direct://', '--proxy-bypass-list=*'],
57
+ })
58
+ const page = await browser.newPage()
59
+ const errs = []
60
+ page.on('pageerror', e => errs.push(String(e).slice(0, 120)))
61
+ await page.setViewport({ width: 1680, height: 1600, deviceScaleFactor: 3 })
62
+
63
+ const auth = async (t) => { const c = await page.target().createCDPSession(); await c.send('Network.clearBrowserCookies'); await page.setCookie({ name: 'token', value: t, domain: '127.0.0.1', path: '/' }) }
64
+ const goto = async (u, w = 10000) => { await page.goto(`${BASE}${u}`, { waitUntil: 'domcontentloaded', timeout: 90_000 }); await sleep(w) }
65
+
66
+ // content-visibility:auto sections report estimated rects until rendered; force real
67
+ // layout so element-accurate clips do not drift. Also kill CSS transitions/animations
68
+ // so a mid-transition frame (e.g. the hold-interval cell double-painting its value)
69
+ // can never be captured.
70
+ const settle = async () => {
71
+ await page.evaluate(() => {
72
+ let st = document.getElementById('aw-capture-calm')
73
+ if (!st) {
74
+ st = document.createElement('style')
75
+ st.id = 'aw-capture-calm'
76
+ st.textContent = '*,*::before,*::after{transition:none!important;transition-duration:0s!important;animation:none!important;animation-duration:0s!important;animation-delay:0s!important}'
77
+ document.head.appendChild(st)
78
+ }
79
+ for (const e of document.querySelectorAll('*')) {
80
+ const cs = getComputedStyle(e)
81
+ if (cs.contentVisibility && cs.contentVisibility !== 'visible') e.style.contentVisibility = 'visible'
82
+ }
83
+ window.scrollTo(0, document.body.scrollHeight)
84
+ })
85
+ await sleep(700)
86
+ await page.evaluate(() => window.scrollTo(0, 0))
87
+ await sleep(300)
88
+ }
89
+
90
+ // Grow the viewport to the full document height BEFORE measuring, so that the
91
+ // screenshot's captureBeyondViewport expansion cannot re-layout the page and
92
+ // shift elements between measurement and capture.
93
+ const expand = async () => {
94
+ const h = await page.evaluate(() => document.documentElement.scrollHeight)
95
+ await page.setViewport({ width: 1680, height: Math.min(Math.ceil(h) + 40, 14000), deviceScaleFactor: 3 })
96
+ await sleep(1200)
97
+ await settle()
98
+ }
99
+
100
+ const repaint = async () => {
101
+ await page.evaluate(() => new Promise(res => {
102
+ window.scrollTo(0, 0)
103
+ requestAnimationFrame(() => requestAnimationFrame(res))
104
+ }))
105
+ await sleep(120)
106
+ }
107
+
108
+ // Capture only once the pixels stop changing: two consecutive identical captures are
109
+ // required (guards against mid-transition / late-commit frames like double-struck text).
110
+ const shot = async (n, x, y, w, h, tries = 5) => {
111
+ const clip = { x, y, width: w, height: h }
112
+ let prev = null
113
+ let stable = false
114
+ for (let i = 0; i < tries; i++) {
115
+ await repaint()
116
+ const buf = Buffer.from(await page.screenshot({ encoding: 'binary', clip, captureBeyondViewport: true }))
117
+ if (prev && Buffer.compare(prev, buf) === 0) { prev = buf; stable = true; break }
118
+ prev = buf
119
+ await sleep(1000)
120
+ }
121
+ fs.writeFileSync(path.join(OUT, n), prev)
122
+ console.log(` ✓ ${n} ${Math.round(w)}x${Math.round(h)} css @3x ${stable ? 'stable' : 'NOT STABLE (last frame written)'}`)
123
+ }
124
+
125
+ // ── force English UI before any capture ──────────────────────────────────
126
+ await auth(ADMIN)
127
+ await goto('/daq', 4000)
128
+ await page.evaluate(() => localStorage.setItem('aw.locale', 'en'))
129
+ await page.reload({ waitUntil: 'domcontentloaded' })
130
+ await sleep(6000)
131
+ const heads = await page.evaluate(() => [...document.querySelectorAll('.nodes-table thead th')].map(t => t.textContent.trim()))
132
+ if (!heads.some(h => /Sampling/i.test(h))) throw new Error(`locale not English (headers: ${JSON.stringify(heads)})`)
133
+ console.log(`locale : en (headers: ${heads.join(' | ')})`)
134
+
135
+ // ══ ① provision & connect — /daq nodes filtered to the English line ══════
136
+ console.log('\n== ① /daq ==')
137
+ await page.evaluate((ln) => {
138
+ for (const s of document.querySelectorAll('select')) {
139
+ if ([...s.options].some(o => o.value === ln)) { s.value = ln; s.dispatchEvent(new Event('change', { bubbles: true })); break }
140
+ }
141
+ }, LINE.id)
142
+ await sleep(3500)
143
+ await settle()
144
+ await expand()
145
+
146
+ const G = await page.evaluate(() => {
147
+ const tbl = document.querySelector('.nodes-table')
148
+ const rect = tbl.getBoundingClientRect()
149
+ const ths = [...tbl.querySelectorAll('thead th')].map(t => ({ x: Math.round(t.getBoundingClientRect().x - rect.x), text: t.textContent.trim() }))
150
+ const rows = [...tbl.querySelectorAll('tbody tr')].map((tr) => {
151
+ const q = tr.getBoundingClientRect()
152
+ const tds = tr.querySelectorAll('td')
153
+ return { y: Math.round(q.y + window.scrollY), h: q.height, name: (tr.querySelector('b')?.textContent || '').trim(), driver: (tds[6]?.textContent || '').trim() }
154
+ })
155
+ return { x: rect.x, ths, rows }
156
+ })
157
+ console.log(' rows:', G.rows.map(r => `${r.name}[${r.driver}]`).join(', '))
158
+ if (!G.rows.some(r => r.driver.includes('modbus'))) throw new Error('no Modbus-TCP row visible on filtered line')
159
+ // columns: 4 Sampling · 5 WS Publish · 6 Driver · 7 Line (indices are locale-stable).
160
+ // Keep the crop ≤470 css px: 12 px UI text must print ≥ ~6.4 pt at the 88 mm panel width.
161
+ const mbIdx = G.rows.findIndex(r => r.driver.includes('modbus'))
162
+ const mkIdx = mbIdx > 0 ? mbIdx - 1 : 0
163
+ const x0 = G.x + G.ths[4].x
164
+ const x1 = G.x + G.ths[8].x
165
+ const yS = G.rows[mkIdx].y
166
+ const yE = G.rows[mbIdx].y + G.rows[mbIdx].h
167
+ console.log(` pair: ${G.rows[mkIdx].name}[${G.rows[mkIdx].driver}] + ${G.rows[mbIdx].name}[${G.rows[mbIdx].driver}]`)
168
+ await shot('p1-driver.png', x0 - 2, yS, Math.min(x1 - x0 + 4, 470), yE - yS)
169
+
170
+ // ══ ② bind node → line / device — /dcw/{English line} ═══════════════════
171
+ console.log('\n== ② /dcw/' + LINE.id + ' ==')
172
+ await goto(`/dcw/${LINE.id}`, 9000)
173
+ await settle()
174
+ await expand()
175
+ const D = await page.evaluate(() => {
176
+ const t = document.querySelector('.aw-tile.table-card table')
177
+ const rect = t.getBoundingClientRect()
178
+ const ths = [...t.querySelectorAll('thead th')].map(x => ({ x: Math.round(x.getBoundingClientRect().x - rect.x), text: x.textContent.trim() }))
179
+ return { x: rect.x, y: rect.y + window.scrollY, h: rect.height, ths }
180
+ })
181
+ console.log(' cols:', D.ths.map(c => c.text).join(' | '))
182
+ // columns: 5 Setpoint · 6 Process Range · 7 Hold Interval · 8 Bound Device
183
+ const c0 = D.x + D.ths[5].x
184
+ const c1 = D.x + D.ths[9].x
185
+ await shot('p2-binding.png', c0 - 6, D.y, (c1 - c0) + 12, D.h)
186
+
187
+ // ══ ④ governed closed loop — ledger optimization records (real, judged) ══
188
+ console.log('\n== ④ /dcw/' + RECORD_LINE + ' ledger ==')
189
+ await goto(`/dcw/${RECORD_LINE}`, 9000)
190
+ await page.evaluate((nid) => {
191
+ const s = document.querySelector('.ledger-card select.inp')
192
+ if (!s) return
193
+ s.value = nid
194
+ s.dispatchEvent(new Event('change', { bubbles: true }))
195
+ }, RECORD_NODE)
196
+ await sleep(3500)
197
+ await settle()
198
+ await expand()
199
+ const L = await page.evaluate(() => {
200
+ const card = document.querySelector('.ledger-card')
201
+ const col = [...card.querySelectorAll('.ledger-col')].find(c => c.querySelector('li.ledger-rec')) || [...card.querySelectorAll('.ledger-col')].pop()
202
+ const recs = [...col.querySelectorAll('li.ledger-rec')]
203
+ const r = col.getBoundingClientRect()
204
+ const last = recs[recs.length - 1].getBoundingClientRect()
205
+ return { x: r.x, y: r.y + window.scrollY, w: r.width, h: last.bottom - r.top, texts: recs.map(li => li.textContent.replace(/\s+/g, ' ').trim()) }
206
+ })
207
+ console.log(' records:', JSON.stringify(L.texts))
208
+ // tight: header + the real records, without the empty box below them
209
+ await shot('p4-records.png', L.x - 5, L.y - 5, Math.min(L.w + 14, 470), L.h + 14)
210
+
211
+ // ══ ③ compose & deploy the agent team — /workshop/teams ═════════════════
212
+ // Desktop viewport: one card in the multi-column grid (~343 px wide), which keeps the
213
+ // crop ≤470 css px so member text prints ≥ ~6 pt at the 88 mm panel width.
214
+ console.log('\n== ③ /workshop/teams ==')
215
+ await page.setViewport({ width: 1680, height: 1600, deviceScaleFactor: 3 })
216
+ await goto('/workshop/teams', 9000)
217
+ await settle()
218
+ await expand()
219
+ const cards = await page.evaluate(() => [...document.querySelectorAll('.grid .card')].map(e => {
220
+ const r = e.getBoundingClientRect()
221
+ const members = e.querySelectorAll('.member')
222
+ const last = members[members.length - 1]?.getBoundingClientRect()
223
+ return { x: Math.round(r.x), y: Math.round(r.y + window.scrollY), w: Math.round(r.width), h: Math.round(r.height), memberH: last ? Math.round(last.bottom - r.top) : Math.round(r.height), t: e.textContent.replace(/\s+/g, ' ').slice(0, 40) }
224
+ }))
225
+ console.log(' cards:', JSON.stringify(cards))
226
+ const tgt = cards.find(c => c.t.includes('Cast-film control team'))
227
+ if (!tgt) throw new Error('English team card not found on /workshop/teams')
228
+ // crop head + member rows only (excludes the Add-Member button) to keep the figure compact
229
+ await shot('p3-team.png', tgt.x - 5, tgt.y - 5, Math.min(tgt.w + 10, 470), tgt.memberH + 8)
230
+
231
+ console.log('\npageerrors:', errs.length ? errs.join(' | ') : 'none')
232
+ await browser.close()
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Render the walkthrough figure HTML to (a) an exact-size PDF via headless Chrome
3
+ * --print-to-pdf and (b) a high-resolution PNG for inspection/archival.
4
+ *
5
+ * Usage: node scripts/render-walkthrough-figure.mjs
6
+ */
7
+ import { execFileSync } from 'node:child_process'
8
+ import fs from 'node:fs'
9
+ import path from 'node:path'
10
+ import puppeteer from 'puppeteer-core'
11
+
12
+ const CHROME = process.env.AW_CHROME ?? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
13
+ const DIR = path.resolve('paper/tii/figures/walkthrough')
14
+ const SRC = path.join(DIR, 'fig-walkthrough.html')
15
+ if (!fs.existsSync(SRC)) throw new Error('missing ' + SRC)
16
+
17
+ // ── (a) exact-size PDF ────────────────────────────────────────────────────
18
+ const pdfUrl = 'file:///' + SRC.replace(/\\/g, '/')
19
+ const outPdf = path.resolve('paper/tii/figures/pdf/fig-walkthrough.pdf')
20
+ fs.mkdirSync(path.dirname(outPdf), { recursive: true })
21
+ execFileSync(CHROME, [
22
+ '--headless=new', '--disable-gpu', '--no-pdf-header-footer',
23
+ `--print-to-pdf=${outPdf}`, pdfUrl,
24
+ ], { stdio: 'ignore' })
25
+ const info = execFileSync('pdfinfo', [outPdf], { encoding: 'utf8' })
26
+ console.log('PDF :', outPdf)
27
+ console.log(' ', info.split('\n').filter(l => /Page size|Pages/.test(l)).join(' | ').trim())
28
+
29
+ // ── (b) high-resolution PNG (overall + per-panel) ─────────────────────────
30
+ const browser = await puppeteer.launch({ executablePath: CHROME, headless: 'new', args: ['--no-sandbox', '--disable-gpu', '--allow-file-access-from-files'] })
31
+ const page = await browser.newPage()
32
+ // 181 mm wide at 96 dpi/css == 684 css px; height is a minimum — fullPage grows to content
33
+ await page.setViewport({ width: 684, height: 200, deviceScaleFactor: 8 })
34
+ await page.goto(pdfUrl, { waitUntil: 'networkidle0' })
35
+ await page.evaluate(() => document.fonts?.ready)
36
+ await new Promise(r => setTimeout(r, 500))
37
+ await page.screenshot({ path: path.join(DIR, 'fig-walkthrough.png'), fullPage: true })
38
+ const png = fs.statSync(path.join(DIR, 'fig-walkthrough.png'))
39
+ console.log('PNG :', path.join(DIR, 'fig-walkthrough.png'), `${(png.size / 1024).toFixed(0)} KB`)
40
+
41
+ // medium-res variant for quick review
42
+ await page.setViewport({ width: 684, height: 200, deviceScaleFactor: 2 })
43
+ await page.screenshot({ path: path.join(DIR, '_preview.png'), fullPage: true })
44
+ console.log('PREV :', path.join(DIR, '_preview.png'))
45
+
46
+ await browser.close()
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Purpose-built ENGLISH-locale fixture for the IEEE TII Figure 2 walkthrough.
3
+ *
4
+ * The shared production instance is full of Chinese-named test data; we must NOT
5
+ * rename or delete anything. Instead this script creates a small additive fixture
6
+ * with English entity names via the platform's own REST API, so the four
7
+ * walkthrough screenshots contain English strings only:
8
+ *
9
+ * - line "Cast-film line A"
10
+ * - DAQ nodes mock + a real Modbus-TCP node on the PLC simulator, one line
11
+ * - device "Die-head heater" twin, bound by a DCW node with a 150-200 C window
12
+ * - agents English lead + workers on the `omp` harness
13
+ * - team "Cast-film control team" (lead + 2 workers)
14
+ *
15
+ * Idempotent: re-running reuses rows with the same names. Prints a JSON summary.
16
+ *
17
+ * Usage: NO_PROXY='127.0.0.1,localhost' node scripts/seed-walkthrough-fixture.mjs
18
+ */
19
+ const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3005'
20
+
21
+ const login = async (e, p) =>
22
+ (await (await fetch(`${BASE}/api/users/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: e, password: p }) })).json()).data.token
23
+
24
+ const T = await login('admin@awshop.local', 'admin123')
25
+
26
+ const api = async (method, path, body) => {
27
+ const r = await fetch(`${BASE}${path}`, {
28
+ method,
29
+ headers: { Authorization: `Bearer ${T}`, ...(body ? { 'content-type': 'application/json' } : {}) },
30
+ body: body ? JSON.stringify(body) : undefined,
31
+ })
32
+ const j = await r.json().catch(() => ({}))
33
+ if (!r.ok || j.code !== 0) throw new Error(`${method} ${path} -> ${r.status} ${JSON.stringify(j).slice(0, 200)}`)
34
+ return j.data
35
+ }
36
+ const get = p => api('GET', p)
37
+ const post = (p, b) => api('POST', p, b)
38
+
39
+ // ── 1. production line ────────────────────────────────────────────────────
40
+ const LINE_NAME = 'Cast-film line A'
41
+ const lines = (await get('/api/workshop/dcw/lines')).lines
42
+ let line = lines.find(l => l.name === LINE_NAME)
43
+ if (!line) line = (await post('/api/workshop/dcw/lines', { name: LINE_NAME, color: '#f4c542' })).line
44
+ console.log(`line : ${line.id} ${line.name}`)
45
+
46
+ // ── 2. DAQ nodes on that line (mock + real Modbus-TCP share one loop) ─────
47
+ const daq = await get('/api/workshop/daq')
48
+ const haveNode = n => daq.nodes.find(x => x.name === n)
49
+ const mkNode = async (spec) => {
50
+ const existing = haveNode(spec.name)
51
+ if (existing) return existing
52
+ return (await post('/api/workshop/daq', { enabled: true, intervalMs: 1000, lineId: line.id, ...spec })).node
53
+ }
54
+
55
+ const nTemp = await mkNode({
56
+ name: 'Melt temperature', templateRef: 'daq-temp-tc', driver: 'mock',
57
+ unit: '\u00B0C', decimals: 1, min: 150, max: 200, warnLow: 155, warnHigh: 195,
58
+ })
59
+ const nSpeed = await mkNode({
60
+ name: 'Line speed', templateRef: 'daq-line-encoder', driver: 'mock',
61
+ unit: 'm/min', decimals: 0, min: 280, max: 360, warnLow: 290, warnHigh: 350,
62
+ })
63
+ const nPress = await mkNode({
64
+ name: 'Melt pressure', templateRef: 'daq-pressure-tx', driver: 'mock',
65
+ unit: 'MPa', decimals: 2, min: 0.6, max: 1.2, warnLow: 0.65, warnHigh: 1.15,
66
+ })
67
+ // real Modbus-TCP against the PLC simulator's "extruder" slave (melt-temp @ 40001)
68
+ const nModbus = await mkNode({
69
+ name: 'Die-head temperature', templateRef: 'daq-temp-tc', driver: 'modbus-tcp',
70
+ unit: '\u00B0C', decimals: 2, min: 0, max: 400, warnLow: 150, warnHigh: 260,
71
+ driverConfig: { host: '127.0.0.1', port: 16040, unitId: 1, register: 40001, dataType: 'float32', byteOrder: 'big' },
72
+ })
73
+ console.log(`daq : temp=${nTemp.id} speed=${nSpeed.id} pressure=${nPress.id} modbus=${nModbus.id}`)
74
+
75
+ // ── 3. device twin + DCW control node with a recipe window ────────────────
76
+ const twins = (await get('/api/workshop/device-twins')).twins
77
+ let twin = twins.find(t => t.name === 'Die-head heater')
78
+ if (!twin) twin = (await post('/api/workshop/device-twins', { name: 'Die-head heater', kind: 'device', controls: ['setpoint'] })).twin
79
+ console.log(`twin : ${twin.id} ${twin.name}`)
80
+
81
+ const dcw = await get('/api/workshop/dcw')
82
+ let dcwNode = dcw.nodes.find(n => n.name === 'Die-head setpoint')
83
+ if (!dcwNode) {
84
+ dcwNode = (await post('/api/workshop/dcw', {
85
+ name: 'Die-head setpoint', templateRef: 'dcw-temp-sp', driver: 'mock',
86
+ unit: '\u00B0C', decimals: 1, min: 150, max: 200,
87
+ holdIntervalMs: 5000, readIntervalMs: 5000, enabled: true,
88
+ lineId: line.id, deviceIds: [twin.id],
89
+ })).node
90
+ }
91
+ console.log(`dcw : ${dcwNode.id} ${dcwNode.name} bound=${twin.id} window=${dcwNode.min}-${dcwNode.max}`)
92
+
93
+ // ── 4. English agents + team (one lead, harness-backed workers) ───────────
94
+ const teams0 = await get('/api/workshop/teams')
95
+ let team = teams0.find(t => t.name === 'Cast-film control team')
96
+ if (!team) team = await post('/api/workshop/teams', { name: 'Cast-film control team', description: 'Cast-film process lead plus DAQ and modelling workers.', visibility: 'public' })
97
+
98
+ const teamId = team.id
99
+ const existingTeam = (await get('/api/workshop/teams')).find(t => t.id === teamId)
100
+ const memberNames = new Set((existingTeam?.members ?? []).map(m => m.name))
101
+
102
+ const AGENTS = [
103
+ { name: 'Cast-film process lead', harness: 'omp', role: 'lead' },
104
+ { name: 'DAQ engineer', harness: 'omp', role: 'worker' },
105
+ { name: 'Model trainer', harness: 'omp', role: 'worker' },
106
+ ]
107
+ for (const a of AGENTS) {
108
+ if (memberNames.has(a.name)) continue
109
+ const agent = await post('/api/workshop/agents', { name: a.name, harness: a.harness, visibility: 'public' })
110
+ await post(`/api/workshop/teams/${teamId}/members`, { agentId: agent.id, role: a.role })
111
+ }
112
+ const finalTeam = (await get('/api/workshop/teams')).find(t => t.id === teamId)
113
+ console.log(`team : ${teamId} ${finalTeam.name} members=${(finalTeam.members ?? []).map(m => `${m.role}:${m.name}:${m.harness}`).join(', ')}`)
114
+
115
+ // ── 5. start the line so DAQ acquisition is genuinely live ────────────────
116
+ // DAQ sampling is gated on an active per-line batch (recipe run). Without a run
117
+ // the nodes report `offline` and the panel-1 claim "share one acquisition loop"
118
+ // would be untrue. Create a product + recipe carrying one setpoint param and
119
+ // start the line; idempotent via the line's active-run flag.
120
+ const lineStates = (await get('/api/workshop/dcw/lines')).states
121
+ const alreadyRunning = lineStates.find(s => s.lineId === line.id && s.active)
122
+ if (alreadyRunning) {
123
+ console.log(`run : already active ${alreadyRunning.runId}`)
124
+ }
125
+ else {
126
+ const dcwState = await get('/api/workshop/dcw')
127
+ let product = (dcwState.products ?? []).find(p => p.name === 'Cast-film roll')
128
+ if (!product) product = (await post('/api/workshop/dcw/products', { name: 'Cast-film roll', lineId: line.id })).product
129
+ let recipe = (dcwState.recipes ?? []).find(r => r.name === 'Cast-film recipe' && r.lineId === line.id)
130
+ if (!recipe) {
131
+ recipe = (await post('/api/workshop/dcw/recipes', {
132
+ productId: product.id,
133
+ name: 'Cast-film recipe',
134
+ description: 'Cast-film line A baseline recipe.',
135
+ params: [{ nodeId: dcwNode.id, value: 178, min: 150, max: 200 }],
136
+ daqWindows: [{ nodeId: nTemp.id, min: 150, max: 200 }],
137
+ })).recipe
138
+ }
139
+ await post(`/api/workshop/dcw/lines/${line.id}/start`, { recipeId: recipe.id })
140
+ console.log(`run : started with recipe ${recipe.id} (${recipe.name})`)
141
+ }
142
+
143
+ console.log('\nFIXTURE ' + JSON.stringify({ lineId: line.id, lineName: line.name, modbusNodeId: nModbus.id, dcwNodeId: dcwNode.id, twinId: twin.id, teamId }))
@@ -60,6 +60,14 @@ console.log('\n━━━ 3. 稳定性护栏的严重性判定(哪类异常不该
60
60
  check('undici UND_ERR_CONNECT_TIMEOUT → upstream', classifyFatalCandidate(err('UND_ERR_CONNECT_TIMEOUT')) === 'upstream')
61
61
  check('无 code 但 message 带 ECONNREFUSED → upstream',
62
62
  classifyFatalCandidate(new Error('connect ECONNREFUSED 127.0.0.1:1502')) === 'upstream')
63
+ // 实测事故 2:工业驱动库(Modbus/OPC UA 客户端)超时时只给可读 message、不带 errno code,
64
+ // 旧实现据此判 fatal → 生产实例运行中 exit 1(平台带着数采/数控一起挂掉)。
65
+ check('无 code,message "TCP Connection Timed Out" → upstream',
66
+ classifyFatalCandidate(new Error('TCP Connection Timed Out')) === 'upstream')
67
+ check('无 code,message "connection timed out" → upstream',
68
+ classifyFatalCandidate(new Error('connect error: connection timed out')) === 'upstream')
69
+ check('无 code,message "socket hang up" → upstream',
70
+ classifyFatalCandidate(new Error('socket hang up')) === 'upstream')
63
71
 
64
72
  // 其余既有分级不得回退
65
73
  check('ECONNRESET(客户端硬断)→ socket', classifyFatalCandidate(err('ECONNRESET')) === 'socket')
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 以 **admin 账号** 登录并给数采 / 数控 / 数字孪生出图。
3
+ * 与 lib.mjs 的视觉走查账号(visual)区分:admin 是文档里写的管理员账号,
4
+ * 能看见全量节点与全部产线;演示工作区由 scripts/ui/seed-admin.mjs 准备。
5
+ */
6
+ import { launch, openPage, gotoReady } from './lib.mjs'
7
+ import fs from 'node:fs'
8
+
9
+ const BASE = process.env.AW_BASE ?? 'http://127.0.0.1:3021'
10
+ const OUT = process.env.AW_OUT ?? '.e2e-shots/admin'
11
+ fs.mkdirSync(OUT, { recursive: true })
12
+
13
+ const loginRes = await fetch(BASE + '/api/users/login', {
14
+ method: 'POST',
15
+ headers: { 'content-type': 'application/json' },
16
+ body: JSON.stringify({ email: 'admin@awshop.local', password: 'admin123' }),
17
+ })
18
+ const login = await loginRes.json()
19
+ if (login.code !== 0) throw new Error('admin 登录失败: ' + JSON.stringify(login).slice(0, 200))
20
+ const token = login.data.token
21
+ console.log('登录成功:', login.data.user.name, '(' + login.data.user.role + ')')
22
+
23
+ const browser = await launch()
24
+ const routes = [
25
+ { path: '/', name: '01-dashboard', wait: 9000 },
26
+ { path: '/daq', name: '02-daq', wait: 12000 },
27
+ { path: '/dcw', name: '03-dcw', wait: 9000 },
28
+ { path: '/town', name: '04-town', wait: 16000 },
29
+ { path: '/monitor', name: '05-monitor', wait: 7000 },
30
+ ]
31
+ for (const r of routes) {
32
+ const page = await openPage(browser, { token, dark: true, width: 1440, height: 900, deviceScaleFactor: 2 })
33
+ const errs = []
34
+ page.on('pageerror', e => errs.push(String(e.message).slice(0, 120)))
35
+ await gotoReady(page, r.path, { wait: r.wait })
36
+ const file = OUT + '/' + r.name + '.png'
37
+ await page.screenshot({ path: file })
38
+ const info = await page.evaluate(() => ({
39
+ nodes: document.querySelectorAll('*').length,
40
+ text: document.body.innerText.replace(/\s+/g, ' ').slice(0, 120),
41
+ }))
42
+ console.log(' ok ' + r.name.padEnd(16) + ' dom=' + String(info.nodes).padStart(6) + ' errs=' + errs.length)
43
+ await page.close()
44
+ }
45
+ await browser.close()
46
+ console.log('->', OUT)