agentworkshop 0.7.39 → 0.7.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/.AgentWorkShop/prompts/host-tools.json +55 -0
  2. package/.output/.AgentWorkShop/prompts/host-tools.json +55 -0
  3. package/.output/nitro.json +1 -1
  4. package/.output/public/_nuxt/88GOowgO2.js +1 -0
  5. package/.output/public/_nuxt/{BNT8gYU92.js → BC9wjTJK2.js} +2 -2
  6. package/.output/public/_nuxt/{CY9LbPCI.js → BN16aRdQ.js} +1 -1
  7. package/.output/public/_nuxt/BWVhZntO.js +1 -0
  8. package/.output/public/_nuxt/{HBLJ-wei.js → BXxH0UQE.js} +1 -1
  9. package/.output/public/_nuxt/{zIGh7R49.js → BneN7fbu.js} +1 -1
  10. package/.output/public/_nuxt/BsROmY89.js +1 -0
  11. package/.output/public/_nuxt/{DUrecWPx.js → C0ZrS1KL.js} +2 -2
  12. package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
  13. package/.output/public/_nuxt/{CUDwzgLV.js → C4s_niy-.js} +9 -9
  14. package/.output/public/_nuxt/CFxtLwjp.js +1 -0
  15. package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
  16. package/.output/public/_nuxt/CssgYAAL2.js +1 -0
  17. package/.output/public/_nuxt/D6mbFA-a.js +1 -0
  18. package/.output/public/_nuxt/D98NH8kG.js +1 -0
  19. package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
  20. package/.output/public/_nuxt/DGbd-UiG.js +1 -0
  21. package/.output/public/_nuxt/{CojCTLKD.js → DGj-WEBg.js} +1 -1
  22. package/.output/public/_nuxt/{DDShH71m.js → DS8qj8EL.js} +1 -1
  23. package/.output/public/_nuxt/DTFCy6h-.js +1 -0
  24. package/.output/public/_nuxt/{DrYV5i68.js → DUjW5Yq5.js} +1 -1
  25. package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
  26. package/.output/public/_nuxt/Dap5PmTL.js +2 -0
  27. package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
  28. package/.output/public/_nuxt/DvLxILQP.js +1 -0
  29. package/.output/public/_nuxt/IREZ1eC2.js +1 -0
  30. package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
  31. package/.output/public/_nuxt/_id_.D9ymtHB5.css +1 -0
  32. package/.output/public/_nuxt/_id_.jf-_8E_c.css +1 -0
  33. package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
  34. package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
  35. package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
  36. package/.output/public/_nuxt/builds/latest.json +1 -1
  37. package/.output/public/_nuxt/builds/meta/78a295bb-2072-48a4-8b46-6d545fdae743.json +1 -0
  38. package/.output/public/_nuxt/ca4TJ3Op.js +1 -0
  39. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
  40. package/.output/public/_nuxt/daq.DjSgY94l.css +1 -0
  41. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
  42. package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
  43. package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -0
  44. package/.output/public/_nuxt/eqq3wWQF.js +18 -0
  45. package/.output/public/_nuxt/logs.BpKzmKsh.css +1 -0
  46. package/.output/public/_nuxt/monitor.D9pe7qN6.css +1 -0
  47. package/.output/public/_nuxt/pages.D7aSDlUI.css +1 -0
  48. package/.output/public/_nuxt/permissions.DwTVlFe6.css +1 -0
  49. package/.output/public/_nuxt/plugins.sb4nDGqr.css +1 -0
  50. package/.output/public/_nuxt/{Cbq-mryP.js → qr6R6Ha1.js} +1 -1
  51. package/.output/public/_nuxt/settings.BTgR-NiE.css +1 -0
  52. package/.output/public/_nuxt/teams.CxnxKVVs.css +1 -0
  53. package/.output/public/_nuxt/tokens.BFY7qv4f.css +1 -0
  54. package/.output/public/_nuxt/{town.DKIAhKnx.css → town.BBXZmeoP.css} +1 -1
  55. package/.output/public/_nuxt/users.DLgZ1D0h.css +1 -0
  56. package/.output/public/_nuxt/workshop.CR_QzeAN.css +1 -0
  57. package/.output/public/_nuxt/{BzHEilTt.js → xh3_vYcu.js} +1 -1
  58. package/.output/public/_nuxt/yF0aYMF1.js +4 -0
  59. package/.output/server/chunks/_/entity.mjs.map +1 -1
  60. package/.output/server/chunks/_/nitro.mjs +2355 -1362
  61. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  62. package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView--otsnr_7.mjs} +290 -208
  63. package/.output/server/chunks/build/TownView--otsnr_7.mjs.map +1 -0
  64. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-Brv-JSvr.mjs} +142 -141
  65. package/.output/server/chunks/build/_id_-Brv-JSvr.mjs.map +1 -0
  66. package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-D9pl1OZy.mjs} +38 -36
  67. package/.output/server/chunks/build/_id_-D9pl1OZy.mjs.map +1 -0
  68. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BASfDf0s.mjs} +295 -261
  69. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BASfDf0s.mjs.map} +1 -1
  70. package/.output/server/chunks/build/{agents-Bvin73QY.mjs → agents-CLQqJF2y.mjs} +21 -27
  71. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +1 -0
  72. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs → aml-_7FdFL2p.mjs} +109 -109
  73. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs.map → aml-_7FdFL2p.mjs.map} +1 -1
  74. package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -1
  75. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs → channel-templates-p6SZNjlM.mjs} +19 -19
  76. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs.map → channel-templates-p6SZNjlM.mjs.map} +1 -1
  77. package/.output/server/chunks/build/{daq-C03Uu5UE.mjs → daq-DkMvD8sg.mjs} +193 -126
  78. package/.output/server/chunks/build/daq-DkMvD8sg.mjs.map +1 -0
  79. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-C1jgVJkO.mjs} +46 -46
  80. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-C1jgVJkO.mjs.map} +1 -1
  81. package/.output/server/chunks/build/{default-CZmZFpqH.mjs → default-BE4r0FZN.mjs} +152 -58
  82. package/.output/server/chunks/build/{default-CZmZFpqH.mjs.map → default-BE4r0FZN.mjs.map} +1 -1
  83. package/.output/server/chunks/build/{logs-B-xhuqln.mjs → logs--dByNyhw.mjs} +35 -35
  84. package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs--dByNyhw.mjs.map} +1 -1
  85. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs → monitor-C0okQaXz.mjs} +33 -33
  86. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs.map → monitor-C0okQaXz.mjs.map} +1 -1
  87. package/.output/server/chunks/build/{pages-BO7yZsXA.mjs → pages-B0bUWnC6.mjs} +35 -19
  88. package/.output/server/chunks/build/pages-B0bUWnC6.mjs.map +1 -0
  89. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs → permissions-68SsNjex.mjs} +12 -12
  90. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs.map → permissions-68SsNjex.mjs.map} +1 -1
  91. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs → plugins-lGlUr1lj.mjs} +17 -17
  92. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs.map → plugins-lGlUr1lj.mjs.map} +1 -1
  93. package/.output/server/chunks/build/{settings-DoQ2Oq3P.mjs → settings-Ddd1qx2Z.mjs} +50 -47
  94. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +1 -0
  95. package/.output/server/chunks/build/{teams-2kz3CusY.mjs → teams-MEDLZdMn.mjs} +38 -34
  96. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +1 -0
  97. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs → tokens-C7fTM5C2.mjs} +14 -14
  98. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs.map → tokens-C7fTM5C2.mjs.map} +1 -1
  99. package/.output/server/chunks/build/{town-BOl6hs9B.mjs → town-CoQdblbZ.mjs} +38 -12
  100. package/.output/server/chunks/build/town-CoQdblbZ.mjs.map +1 -0
  101. package/.output/server/chunks/build/{useDaqStream-CIBaiGAl.mjs → useDaqStream-DfCFpb3p.mjs} +9 -3
  102. package/.output/server/chunks/build/useDaqStream-DfCFpb3p.mjs.map +1 -0
  103. package/.output/server/chunks/build/{useDcwStream-CBbiYnWe.mjs → useDcwStream-UPiX-3iO.mjs} +11 -2
  104. package/.output/server/chunks/build/useDcwStream-UPiX-3iO.mjs.map +1 -0
  105. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
  106. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
  107. package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
  108. package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
  109. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
  110. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
  111. package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
  112. package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
  113. package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
  114. package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
  115. package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
  116. package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
  117. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
  118. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
  120. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
  121. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  122. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  123. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs.map +1 -1
  124. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
  125. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
  126. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
  127. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
  128. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
  129. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
  131. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
  133. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
  134. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
  135. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
  137. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
  139. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
  140. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
  141. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
  142. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
  143. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
  144. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
  145. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
  146. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
  147. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
  148. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
  149. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
  150. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
  151. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
  152. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
  153. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
  154. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
  155. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  156. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  157. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  159. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  160. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  161. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  162. package/.output/server/chunks/routes/api/workshop/index5.get.mjs +3 -1
  163. package/.output/server/chunks/routes/api/workshop/index5.get.mjs.map +1 -1
  164. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +5 -1
  165. package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
  166. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -1
  167. package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
  168. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  169. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  170. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  171. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  172. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  173. package/.output/server/chunks/routes/renderer.mjs +3 -3
  174. package/.output/server/chunks/virtual/entry.mjs +51 -25
  175. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  176. package/.output/server/package.json +1 -1
  177. package/README-zh.md +203 -39
  178. package/README.md +739 -591
  179. package/app/app.vue +9 -3
  180. package/app/assets/css/main.css +517 -7
  181. package/app/components/AppHeader.vue +98 -7
  182. package/app/components/AppSidebar.vue +117 -11
  183. package/app/components/AwChart.vue +2 -1
  184. package/app/components/workshop/AgentInspectorDrawer.vue +6 -6
  185. package/app/components/workshop/AgentLanesView.vue +52 -1
  186. package/app/components/workshop/AssetLibrary.vue +4 -2
  187. package/app/components/workshop/ChannelSessionList.vue +4 -2
  188. package/app/components/workshop/Composer.vue +96 -0
  189. package/app/components/workshop/DaqNodeRow.vue +19 -0
  190. package/app/components/workshop/DaqTemplateIcon.vue +33 -0
  191. package/app/components/workshop/DeviceTwinPanel.vue +37 -35
  192. package/app/components/workshop/EventBlock.vue +3 -3
  193. package/app/components/workshop/InspectorPanel.vue +47 -11
  194. package/app/components/workshop/LiveAgentBar.vue +34 -0
  195. package/app/components/workshop/TaskBoardView.vue +83 -33
  196. package/app/components/workshop/TranscriptTimeline.vue +56 -0
  197. package/app/components/workshop/blocks/ClusterError.vue +1 -1
  198. package/app/components/workshop/blocks/ClusterRoute.vue +3 -1
  199. package/app/components/workshop/blocks/ClusterStatus.vue +2 -2
  200. package/app/components/workshop/blocks/ClusterStream.vue +8 -2
  201. package/app/components/workshop/town/ModelPreview3D.vue +1 -1
  202. package/app/components/workshop/town/TownView.vue +507 -82
  203. package/app/composables/useResponsive.ts +88 -0
  204. package/app/composables/useRouteMeta.ts +3 -0
  205. package/app/composables/workshop/useDaqStream.ts +7 -4
  206. package/app/composables/workshop/useDcwStream.ts +8 -2
  207. package/app/composables/workshop/useVisibleInterval.ts +2 -2
  208. package/app/composables/workshop/useWorkshopApi.ts +1 -1
  209. package/app/layouts/default.vue +46 -2
  210. package/app/pages/aml/index.vue +712 -616
  211. package/app/pages/daq/[id].vue +12 -7
  212. package/app/pages/daq/index.vue +216 -13
  213. package/app/pages/dcw/[id].vue +117 -32
  214. package/app/pages/dcw/index.vue +83 -23
  215. package/app/pages/index.vue +35 -1
  216. package/app/pages/logs/index.vue +147 -13
  217. package/app/pages/monitor.vue +56 -12
  218. package/app/pages/permissions/index.vue +34 -0
  219. package/app/pages/plugins/index.vue +35 -4
  220. package/app/pages/settings.vue +90 -21
  221. package/app/pages/tokens.vue +23 -0
  222. package/app/pages/town.vue +50 -5
  223. package/app/pages/users.vue +23 -0
  224. package/app/pages/workshop/agents.vue +92 -2
  225. package/app/pages/workshop/channel-templates.vue +53 -0
  226. package/app/pages/workshop/index.vue +14 -0
  227. package/app/pages/workshop/teams.vue +66 -6
  228. package/app/pages/workshop/w/[wsId].vue +202 -14
  229. package/app/plugins/aw-plugins.client.ts +13 -8
  230. package/app/stores/app.ts +33 -1
  231. package/app/stores/workshop/user.ts +1 -1
  232. package/app/stores/workshop/workspaces.ts +18 -2
  233. package/app/utils/api-error.ts +1 -1
  234. package/cli/commands/build.mjs +8 -1
  235. package/docker-compose.yml +6 -0
  236. package/docs/cli.en.md +2 -2
  237. package/docs/cli.md +2 -2
  238. package/docs/plugins.en.md +33 -1
  239. package/docs/plugins.md +29 -1
  240. package/docs/sdk.en.md +1 -1
  241. package/docs/sdk.md +1 -1
  242. package/i18n/locales/en.ts +2 -0
  243. package/i18n/locales/zh-CN.ts +2 -0
  244. package/nuxt.config.ts +31 -0
  245. package/package.json +11 -2
  246. package/scripts/_dbg-param-map-e2e.ts +242 -0
  247. package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
  248. package/scripts/_dbg-param-real-scenario.mjs +153 -0
  249. package/scripts/_loop-biax-campaign.mjs +124 -0
  250. package/scripts/api-live-e2e.mjs +40 -8
  251. package/scripts/capture-walkthrough.mjs +232 -0
  252. package/scripts/render-walkthrough-figure.mjs +46 -0
  253. package/scripts/seed-walkthrough-fixture.mjs +143 -0
  254. package/scripts/test-plugin-hardening.mjs +8 -0
  255. package/scripts/test-serial-bridge.mjs +418 -0
  256. package/scripts/ui/admin-shots.mjs +46 -0
  257. package/scripts/ui/audit-layout.mjs +301 -0
  258. package/scripts/ui/bump-microfont.mjs +39 -0
  259. package/scripts/ui/e2e-interactive.mjs +328 -0
  260. package/scripts/ui/lib.mjs +198 -0
  261. package/scripts/ui/linkcheck2.mjs +37 -0
  262. package/scripts/ui/live-render-check.mjs +72 -0
  263. package/scripts/ui/md-lint.mjs +80 -0
  264. package/scripts/ui/perf-audit.mjs +132 -0
  265. package/scripts/ui/readme-assets.mjs +30 -0
  266. package/scripts/ui/readme-shots.mjs +42 -0
  267. package/scripts/ui/record.mjs +225 -0
  268. package/scripts/ui/seed-admin.mjs +68 -0
  269. package/scripts/ui/seed-demo.mjs +83 -0
  270. package/scripts/ui/seed-industrial-demo.mjs +171 -0
  271. package/scripts/ui/shot-hero.mjs +25 -0
  272. package/scripts/ui/shots.mjs +75 -0
  273. package/scripts/ui/sim-ui-shot.mjs +16 -0
  274. package/scripts/ui/verify-nav.mjs +51 -0
  275. package/scripts/ui/verify-responsive.mjs +285 -0
  276. package/server/api/system/config-groups/[id].patch.ts +1 -1
  277. package/server/api/system/config-groups/index.post.ts +1 -1
  278. package/server/api/system/settings/events.get.ts +1 -1
  279. package/server/api/system/settings/index.patch.ts +1 -1
  280. package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
  281. package/server/api/workshop/aml/datasets/[id].get.ts +8 -1
  282. package/server/api/workshop/daq/index.get.ts +3 -1
  283. package/server/api/workshop/daq/infra/reconnect.post.ts +1 -1
  284. package/server/api/workshop/daq/templates/[key].delete.ts +1 -0
  285. package/server/api/workshop/daq/templates/[key].patch.ts +1 -0
  286. package/server/api/workshop/daq/templates/index.post.ts +1 -0
  287. package/server/api/workshop/dcw/[id]/write.post.ts +10 -2
  288. package/server/api/workshop/dcw/[id].delete.ts +5 -2
  289. package/server/api/workshop/dcw/index.get.ts +5 -0
  290. package/server/api/workshop/dcw/lines/[id].patch.ts +1 -1
  291. package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -0
  292. package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -0
  293. package/server/api/workshop/dcw/params/[id].delete.ts +19 -0
  294. package/server/api/workshop/dcw/params/[id].patch.ts +23 -0
  295. package/server/api/workshop/dcw/params/index.get.ts +27 -0
  296. package/server/api/workshop/dcw/params/index.post.ts +25 -0
  297. package/server/api/workshop/permissions/index.get.ts +3 -1
  298. package/server/plugins/aw-plugins.ts +23 -10
  299. package/server/plugins/dev-stability-guard.ts +3 -1
  300. package/server/plugins/system-config.ts +6 -1
  301. package/server/plugins-builtin/serial-bridge/client.mjs +210 -0
  302. package/server/plugins-builtin/serial-bridge/drivers.mjs +409 -0
  303. package/server/plugins-builtin/serial-bridge/i18n.json +62 -0
  304. package/server/plugins-builtin/serial-bridge/index.mjs +247 -0
  305. package/server/plugins-builtin/serial-bridge/lib.mjs +208 -0
  306. package/server/plugins-builtin/serial-bridge/rtu.mjs +168 -0
  307. package/server/services/system-config.ts +28 -9
  308. package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
  309. package/server/services/workshop/agents/agent-interface.ts +8 -6
  310. package/server/services/workshop/agents/base-agent.ts +18 -4
  311. package/server/services/workshop/agents/claude-agent.ts +7 -3
  312. package/server/services/workshop/agents/codex-agent.ts +3 -3
  313. package/server/services/workshop/agents/dsh-agent.ts +3 -3
  314. package/server/services/workshop/agents/goose-agent.ts +0 -1
  315. package/server/services/workshop/agents/harness-terminal.ts +14 -1
  316. package/server/services/workshop/agents/hermes-agent.ts +4 -2
  317. package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
  318. package/server/services/workshop/agents/industrial-context.ts +4 -1
  319. package/server/services/workshop/agents/industrial-tools.ts +164 -6
  320. package/server/services/workshop/agents/omp-agent.ts +15 -9
  321. package/server/services/workshop/agents/opencode-agent.ts +47 -21
  322. package/server/services/workshop/agents/qwen-agent.ts +10 -5
  323. package/server/services/workshop/aml/entity.ts +3 -1
  324. package/server/services/workshop/aml/python-runtime.ts +3 -2
  325. package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
  326. package/server/services/workshop/daq/daq-controller.ts +25 -5
  327. package/server/services/workshop/daq/daq-runtime.ts +5 -3
  328. package/server/services/workshop/daq/drivers.ts +141 -11
  329. package/server/services/workshop/daq/host-ports.ts +3 -2
  330. package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
  331. package/server/services/workshop/db/channel.repo.ts +4 -0
  332. package/server/services/workshop/db/ops.repo.ts +5 -2
  333. package/server/services/workshop/db/retention.ts +8 -3
  334. package/server/services/workshop/dcw/dcw-controller.ts +147 -16
  335. package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
  336. package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
  337. package/server/services/workshop/dcw/drivers.ts +90 -4
  338. package/server/services/workshop/dcw/param-conversion.ts +78 -0
  339. package/server/services/workshop/dcw/param-limits.ts +142 -0
  340. package/server/services/workshop/dcw/param-map.repo.ts +305 -0
  341. package/server/services/workshop/dcw/plugin-bridge.ts +45 -0
  342. package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
  343. package/server/services/workshop/plugins/host.mjs +30 -0
  344. package/server/services/workshop/runtime/agent-runtime.ts +4 -0
  345. package/server/services/workshop/runtime/manager.ts +9 -2
  346. package/server/services/workshop/settings.ts +4 -2
  347. package/server/types/pg.d.ts +49 -0
  348. package/server/utils/stability-severity.ts +13 -2
  349. package/shared/config/groups.mjs +22 -1
  350. package/shared/config/home.d.mts +28 -0
  351. package/shared/daq-protocol.ts +15 -0
  352. package/shared/dcw-protocol.ts +136 -0
  353. package/uno.config.ts +12 -1
  354. package/.output/public/_nuxt/8NMOXZOV.js +0 -1
  355. package/.output/public/_nuxt/BFkgkW8u.js +0 -4
  356. package/.output/public/_nuxt/C5gUkRwn.js +0 -1
  357. package/.output/public/_nuxt/CM12FuB42.js +0 -1
  358. package/.output/public/_nuxt/CZiw7sEV.js +0 -1
  359. package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
  360. package/.output/public/_nuxt/CdPzkOMV.js +0 -1
  361. package/.output/public/_nuxt/CmsOtOAA2.js +0 -1
  362. package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
  363. package/.output/public/_nuxt/D20Yamr4.js +0 -1
  364. package/.output/public/_nuxt/DAe0h2MB.js +0 -1
  365. package/.output/public/_nuxt/DmqknNvV.js +0 -18
  366. package/.output/public/_nuxt/DnUuIR83.js +0 -1
  367. package/.output/public/_nuxt/S_fVQwJK.js +0 -1
  368. package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
  369. package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
  370. package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
  371. package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
  372. package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
  373. package/.output/public/_nuxt/ahgEJu2R.js +0 -1
  374. package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
  375. package/.output/public/_nuxt/builds/meta/4457d63f-0866-4cea-b320-04a321f24795.json +0 -1
  376. package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
  377. package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
  378. package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
  379. package/.output/public/_nuxt/default.BXI_fZUi.css +0 -1
  380. package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
  381. package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
  382. package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
  383. package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
  384. package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
  385. package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
  386. package/.output/public/_nuxt/pz2JDAM7.js +0 -2
  387. package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
  388. package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
  389. package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
  390. package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
  391. package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
  392. package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
  393. package/.output/server/chunks/build/_id_-CfVosNg3.mjs.map +0 -1
  394. package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
  395. package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
  396. package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
  397. package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
  398. package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
  399. package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
  400. package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
  401. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs.map +0 -1
  402. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs.map +0 -1
  403. package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
  404. /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
  405. /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
@@ -11,6 +11,7 @@ import { useEntitiesStore } from '../../../stores/workshop/entities'
11
11
  import { useWorkshopWs } from '../../../composables/workshop/useWorkshopWs'
12
12
  import { useUserStore } from '../../../stores/workshop/user'
13
13
  import { useStorage } from '@vueuse/core'
14
+ import { useResponsive } from '../../../composables/useResponsive'
14
15
 
15
16
  const { t } = useI18n()
16
17
 
@@ -36,6 +37,10 @@ onMounted(() => {
36
37
  const entities = useEntitiesStore()
37
38
  const { subscribe, unsubscribe, conn } = useWorkshopWs()
38
39
 
40
+ // 视口档位(唯一判据;SSR 期返回桌面档)——
41
+ // 窄屏(≤1023)三栏仪表台只剩 ~460px 画布,必须换成「单通道示波器」形态
42
+ const { isDesktop } = useResponsive()
43
+
39
44
  const workspace = computed(() => wsStore.workspaces.find(w => w.id === wsId.value))
40
45
  // 聚焦 channel:activeChannelId 须在挂载清单内(陈旧持久化/竞态下回退首频道),
41
46
  // 避免订阅死频道 → 快照永不到达 → 右栏/时间线长时间"空数据"假象
@@ -77,7 +82,8 @@ const lastSeq = computed(() => (channelId.value ? conn.cursors[channelId.value]
77
82
 
78
83
  // 视图切换(P1 三视图 + P2 多通道同屏 + P5 RPG 小镇)
79
84
  // 深链:?view=lanes/board/split/town 直达指定视图(可分享/收藏)
80
- type CenterView = 'timeline' | 'lanes' | 'board' | 'split' | 'town'
85
+ // inspector 只在窄屏出现(桌面它是右侧常驻栏),故不进深链白名单
86
+ type CenterView = 'timeline' | 'lanes' | 'board' | 'split' | 'town' | 'inspector'
81
87
  const VIEW_KEYS: Record<string, CenterView> = {
82
88
  1: 'timeline',
83
89
  2: 'lanes',
@@ -90,15 +96,24 @@ const initView = route.query.view
90
96
  const view = ref<CenterView>(
91
97
  typeof initView === 'string' && VIEW_VALUES.has(initView) ? initView as CenterView : 'timeline',
92
98
  )
93
- const viewOptions = [
94
- { value: 'timeline', label: t('wsView.k3otu32010') },
95
- { value: 'lanes', label: 'Agent lanes' },
96
- { value: 'board', label: t('wsView.k3ko7a8011') },
97
- { value: 'split', label: t('wsView.k3xbmo012') },
98
- { value: 'town', label: t('wsView.k1cz0pbw013') },
99
- ]
99
+ const viewOptions = computed(() => {
100
+ const base = [
101
+ { value: 'timeline', label: t('wsView.k3otu32010') },
102
+ { value: 'lanes', label: 'Agent lanes' },
103
+ { value: 'board', label: t('wsView.k3ko7a8011') },
104
+ { value: 'split', label: t('wsView.k3xbmo012') },
105
+ { value: 'town', label: t('wsView.k1cz0pbw013') },
106
+ ]
107
+ // 窄屏「一次一区」:检查器不占侧栏,并入切换条(四区都由同一条承载)
108
+ return narrowUI.value ? [...base, { value: 'inspector', label: '检查器' }] : base
109
+ })
100
110
  // 数字快捷键 1-5 直切视图(非输入焦点时;控制台型键盘操作与 ⌘K 面板同一取向)
101
111
  const onViewKey = (ev: KeyboardEvent): void => {
112
+ // 窄屏抽屉:Esc 收起(与全站侧栏抽屉一致)
113
+ if (ev.key === 'Escape' && narrowUI.value && leftOpen.value) {
114
+ leftOpen.value = false
115
+ return
116
+ }
102
117
  if (ev.metaKey || ev.ctrlKey || ev.altKey) return
103
118
  const t = ev.target as HTMLElement | null
104
119
  if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return
@@ -111,8 +126,31 @@ onMounted(() => window.addEventListener('keydown', onViewKey))
111
126
  onBeforeUnmount(() => window.removeEventListener('keydown', onViewKey))
112
127
 
113
128
  // 侧栏折叠(现代 harness 布局:左会话栏 / 右检查器可按需收起)
129
+ // 形态差异只在挂载后生效:SSR/首帧一律按桌面结构渲染,客户端接管后才翻档,
130
+ // 否则服务端 5 项切换条 vs 客户端 6 项会触发水合告警
131
+ const mounted = ref(false)
132
+ onMounted(() => {
133
+ mounted.value = true
134
+ })
135
+ const narrowUI = computed(() => mounted.value && !isDesktop.value)
136
+
114
137
  const leftOpen = ref(true)
115
138
  const rightOpen = ref(true)
139
+ /** 窄屏:左右侧栏都不再占位(收成覆盖式抽屉 / 并入切换条) */
140
+ const applyTier = (desktop: boolean): void => {
141
+ leftOpen.value = desktop
142
+ rightOpen.value = desktop
143
+ // 检查器只是窄屏的第 4 区:回桌面后它回到右栏,中部得有自己的区(否则中部空白)
144
+ if (desktop && view.value === 'inspector') view.value = 'timeline'
145
+ }
146
+ onMounted(() => {
147
+ if (!isDesktop.value) applyTier(false)
148
+ })
149
+ watch(isDesktop, d => applyTier(d))
150
+ // 抽屉里选中频道后自动收起(窄屏少一次手动关闭)
151
+ watch(channelId, (next, prev) => {
152
+ if (prev !== undefined && next !== prev && narrowUI.value) leftOpen.value = false
153
+ })
116
154
 
117
155
  // 侧栏宽度拖拽调节(PaneSplitter;localStorage 持久化,双击复位到默认值)
118
156
  const LEFT_W_DEFAULT = 248
@@ -192,8 +230,18 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
192
230
  />
193
231
  </div>
194
232
  <div class="right">
233
+ <!-- 窄屏:频道会话列表入口(覆盖式抽屉;该页一次只看一区) -->
195
234
  <button
196
- class="pane-toggle im"
235
+ v-if="narrowUI"
236
+ class="pane-toggle im toggle-left-narrow"
237
+ :class="{ off: !leftOpen }"
238
+ title="频道会话列表"
239
+ @click="leftOpen = !leftOpen"
240
+ >
241
+ <span class="i-tabler-list-details im-pop" />
242
+ </button>
243
+ <button
244
+ class="pane-toggle im toggle-left-desk"
197
245
  :class="{ off: !leftOpen }"
198
246
  :title="$t('wsView.k1tsy2e4002')"
199
247
  @click="leftOpen = !leftOpen"
@@ -201,7 +249,7 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
201
249
  <span class="i-tabler-layout-sidebar-left-collapse im-pop" />
202
250
  </button>
203
251
  <button
204
- class="pane-toggle im"
252
+ class="pane-toggle im toggle-right-desk"
205
253
  :class="{ off: !rightOpen }"
206
254
  :title="$t('wsView.k1tx0ppf003')"
207
255
  @click="rightOpen = !rightOpen"
@@ -236,10 +284,16 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
236
284
 
237
285
  <!-- 主体三栏(左/右侧栏可折叠 + 拖拽调宽;分隔条 hairline 即面板边界) -->
238
286
  <div class="main">
287
+ <!-- 窄屏抽屉遮罩:点空白收起(不盖住底部 Composer,输入不被"锁在"抽屉后) -->
288
+ <div
289
+ v-if="narrowUI && leftOpen"
290
+ class="drawer-scrim"
291
+ @click="leftOpen = false"
292
+ />
239
293
  <div
240
294
  v-if="leftOpen"
241
295
  class="left-pane"
242
- :style="{ flexBasis: `${leftWidth}px` }"
296
+ :style="{ flexBasis: narrowUI ? 'auto' : `${leftWidth}px` }"
243
297
  >
244
298
  <div class="left-scroll">
245
299
  <workshop-channel-session-list :ws-id="wsId" />
@@ -250,7 +304,7 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
250
304
  />
251
305
  </div>
252
306
  <workshop-pane-splitter
253
- v-if="leftOpen"
307
+ v-if="leftOpen && !narrowUI"
254
308
  :label="$t('wsView.k1fkfvra005')"
255
309
  @resize="resizeLeft"
256
310
  @reset="leftWidth = LEFT_W_DEFAULT"
@@ -279,6 +333,13 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
279
333
  v-else-if="view === 'town'"
280
334
  :channel-id="channelId"
281
335
  />
336
+ <!-- 窄屏第 4 区:检查器并入切换条(桌面仍是右侧常驻栏) -->
337
+ <workshop-inspector-panel
338
+ v-else-if="view === 'inspector' && narrowUI"
339
+ :channel-id="channelId"
340
+ @open-agent="openAgent"
341
+ @open-task="openTask"
342
+ />
282
343
  </template>
283
344
  <div
284
345
  v-else
@@ -294,13 +355,13 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
294
355
  </div>
295
356
  </div>
296
357
  <workshop-pane-splitter
297
- v-if="rightOpen"
358
+ v-if="rightOpen && !narrowUI"
298
359
  :label="$t('wsView.kwqb0st006')"
299
360
  @resize="resizeRight"
300
361
  @reset="rightWidth = RIGHT_W_DEFAULT"
301
362
  />
302
363
  <div
303
- v-if="rightOpen"
364
+ v-if="rightOpen && !narrowUI"
304
365
  class="right-pane"
305
366
  :class="{ empty: wsStore.loaded && !channelId }"
306
367
  :style="{ flexBasis: `${rightWidth}px` }"
@@ -513,4 +574,131 @@ useHead({ title: () => `${workspace.value?.name ?? 'Workspace'} · AgentWorkShop
513
574
  color: var(--ink-faint);
514
575
  }
515
576
  .composer-pane { flex: 0 0 auto; }
577
+
578
+ /* ══════════════════════════════════════════════════════════════════════════
579
+ 窄屏形态 · 单通道示波器(≤1023px) —— 一次只看一路信号
580
+ ──────────────────────────────────────────────────────────────────────────
581
+ 桌面是「三栏仪表台」:会话栏 248 + 画布 + 检查器 300 + 两条分隔条。
582
+ 到了 ≤1023,画布只剩 ~460px(900–1023 还要再让 64px 图标轨),
583
+ 硬塞的结果是三栏互相压扁。所以窄屏换一台仪器,而不是把仪表台缩小:
584
+
585
+ · 中部视图(时间线 / lanes / 任务板 / 检查器)→ 由顶栏切换条承载,
586
+ 一次只挂载一区,顶部切换条独占一行、可横扫;
587
+ · 频道会话列表 → 覆盖式抽屉(点遮罩/Esc/选中频道收起),不再占位;
588
+ · 拖拽分隔条在窄屏没有意义(没有第二栏可分配宽度)→ 隐藏;
589
+ · 触摸目标 ≥40px、标签 ≥11.5px(手持 30cm 距离下 10px 只剩 6px 有效字号)。
590
+
591
+ ⚠️ 断点数值与 main.css v5 / useResponsive.ts 一致(1024 = 三栏仪表台下限),
592
+ 此处不再引入新魔数。桌面(≥1024)样式完全不受影响。
593
+ ══════════════════════════════════════════════════════════════════════════ */
594
+ @media (max-width: 1023.98px) {
595
+ .harness {
596
+ /* 窄屏收掉壳体外留白,把纵向像素让给内容与底部 Composer */
597
+ height: calc(100dvh - var(--app-header-h, 56px) - var(--app-footer-h, 46px) - 8px);
598
+ }
599
+
600
+ .topbar {
601
+ flex-wrap: wrap;
602
+ gap: 6px 8px;
603
+ padding: 8px 10px;
604
+ }
605
+
606
+ .topbar .left {
607
+ row-gap: 8px;
608
+ }
609
+
610
+ .ws-name {
611
+ max-width: 40vw;
612
+ font-size: 15px;
613
+ }
614
+
615
+ .chan-chip {
616
+ max-width: 34vw;
617
+ font-size: 12px;
618
+ }
619
+
620
+ /* 切换条独占一行:五个视图名不再挤掉工作区名(channel 名仍可见) */
621
+ .view-switch {
622
+ flex: 1 1 100%;
623
+ margin-left: 0;
624
+ }
625
+
626
+ .ws-state,
627
+ .seq {
628
+ font-size: 11.5px;
629
+ }
630
+
631
+ /* 触摸目标:26px 的幽灵图标钮在手持设备上点不中 */
632
+ .pane-toggle {
633
+ width: 40px;
634
+ height: 40px;
635
+ font-size: 18px;
636
+ }
637
+
638
+ .toggle-left-desk,
639
+ .toggle-right-desk {
640
+ display: none;
641
+ }
642
+
643
+ .main {
644
+ position: relative;
645
+ }
646
+
647
+ .left-pane {
648
+ position: absolute;
649
+ top: 0;
650
+ bottom: 0;
651
+ left: 0;
652
+ z-index: 30;
653
+ width: min(86vw, 320px);
654
+ border-right: 1px solid var(--line);
655
+ box-shadow: var(--shadow-float);
656
+ animation: pane-in 0.18s cubic-bezier(0.22, 1, 0.36, 1);
657
+ }
658
+
659
+ .drawer-scrim {
660
+ position: absolute;
661
+ inset: 0;
662
+ z-index: 25;
663
+ background: color-mix(in srgb, var(--ink) 34%, transparent);
664
+ }
665
+
666
+ /* 窄屏没有第二栏可分宽度,拖拽条只会白占 9px */
667
+ .pane-splitter {
668
+ display: none;
669
+ }
670
+
671
+ .pane-loading {
672
+ font-size: 13px;
673
+ }
674
+ }
675
+
676
+ /* 窄屏专属入口在桌面不出现(响应式形态差异不用 JS 表达,避免水合抖动) */
677
+ .toggle-left-narrow {
678
+ display: none;
679
+ }
680
+
681
+ @media (max-width: 1023.98px) {
682
+ .toggle-left-narrow {
683
+ display: inline-flex;
684
+ }
685
+ }
686
+
687
+ @media (prefers-reduced-motion: reduce) {
688
+ .left-pane {
689
+ animation: none;
690
+ }
691
+ }
692
+
693
+ @keyframes pane-in {
694
+ from {
695
+ opacity: 0.5;
696
+ transform: translateX(-16px);
697
+ }
698
+
699
+ to {
700
+ opacity: 1;
701
+ transform: none;
702
+ }
703
+ }
516
704
  </style>
@@ -13,10 +13,12 @@
13
13
  */
14
14
  import { watch } from 'vue'
15
15
  import { createClientContext, type ClientContext } from '@/sdk/client.mjs'
16
- import { usePluginPanels } from '@/app/composables/workshop/usePluginPanels'
17
- import type { TownBus } from '~/composables/workshop/useTownBus'
16
+ import { usePluginPanels, type PluginPanelEntry } from '@/app/composables/workshop/usePluginPanels'
17
+ import { useTownBus, type TownBus } from '~/composables/workshop/useTownBus'
18
18
 
19
19
  interface ManifestEntry { name: string, enabled?: boolean, hasClient?: boolean }
20
+ /** vue-i18n 实例在 NuxtApp 上的最小面(只用到 mergeLocaleMessage + locale,故按鸭子类型声明确切形状) */
21
+ interface I18nLike { mergeLocaleMessage?: (locale: string, messages: Record<string, unknown>) => void, locale?: unknown }
20
22
  interface I18nBundle { i18n?: Record<string, Record<string, Record<string, string>>> }
21
23
 
22
24
  export default defineNuxtPlugin(async (nuxtApp) => {
@@ -26,8 +28,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
26
28
  const loaded = new Map<string, ClientContext>()
27
29
  const { unregisterPlugin } = usePluginPanels()
28
30
  const panels = usePluginPanels()
29
- const i18n: { mergeLocaleMessage?: (locale: string, messages: Record<string, unknown>) => void, locale?: unknown } | null
30
- = (nuxtApp.$i18n as never) ?? null
31
+ // NuxtApp.$i18n 的库类型与本文件用到的两个成员结构不同(mergeLocaleMessage 的参数更宽),故在边界处收敛成 I18nLike
32
+ const i18n = ((nuxtApp.$i18n ?? null) as unknown) as I18nLike | null
31
33
  let bus: TownBus | null = null
32
34
  try {
33
35
  bus = useTownBus()
@@ -102,10 +104,13 @@ export default defineNuxtPlugin(async (nuxtApp) => {
102
104
  }
103
105
  const ctx = createClientContext({
104
106
  name,
105
- eventBridge: bridgeFactory(bus),
107
+ eventBridge: bridgeFactory,
106
108
  ui: {
107
109
  slots: ['plugins.page', 'settings.plugins', 'dashboard.widgets'],
108
- registerPanel: entry => panels.registerPanel({ ...entry, plugin: name }),
110
+ // SDK 的 ClientPanelEntry.mount 声明为 void,宿主 PluginPanelEntry 则允许返回清理函数
111
+ // (宿主编排器按返回值回收面板)。这里只在边界处断言 mount 形状,entry 原样透传、不包一层:
112
+ // 插件真返回清理函数时仍会被回收,返回 void 时同样合法。
113
+ registerPanel: entry => panels.registerPanel({ ...entry, plugin: name, mount: entry.mount as PluginPanelEntry['mount'] }),
109
114
  },
110
115
  t: translatorFor(name),
111
116
  getLocale: () => {
@@ -168,7 +173,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
168
173
  const localeRef = i18n?.locale as { value?: string } | undefined
169
174
  if (localeRef && typeof localeRef === 'object') {
170
175
  watch(localeRef, (loc) => {
171
- for (const { ctx } of loaded.values()) {
176
+ for (const ctx of loaded.values()) {
172
177
  try {
173
178
  ctx?.hooks?.emit('i18n:changed', { locale: loc })
174
179
  }
@@ -185,7 +190,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
185
190
  path = useRoute().path ?? ''
186
191
  }
187
192
  catch { /* 路由上下文不可用时静默 */ }
188
- for (const { ctx } of loaded.values()) {
193
+ for (const ctx of loaded.values()) {
189
194
  try {
190
195
  ctx?.hooks?.emit('page:change', { path })
191
196
  }
package/app/stores/app.ts CHANGED
@@ -5,6 +5,8 @@ export const useAppStore = defineStore('app', () => {
5
5
  // 默认暗色 = Digital Twin 控制室是产品首态(可切换亮色,选择被持久化)
6
6
  const isDark = ref(true)
7
7
  const sidebarCollapsed = ref(false)
8
+ /** 窄屏抽屉导航是否展开(不持久化:每次进入页面都应关闭) */
9
+ const mobileNavOpen = ref(false)
8
10
  /**
9
11
  * 用户是否在设置页/页头显式切换过明暗。
10
12
  * 只有"显式选择"才锁死主题,阻止服务端 theme.mode 的运行时跟随——
@@ -26,11 +28,41 @@ export const useAppStore = defineStore('app', () => {
26
28
  sidebarCollapsed.value = !sidebarCollapsed.value
27
29
  }
28
30
 
31
+ /**
32
+ * 抽屉式导航(窄屏,<900px)。与 sidebarCollapsed 分开的原因:
33
+ * 折叠是**桌面用户的持久偏好**(图标轨),抽屉是**窄屏的临时浮层**——
34
+ * 两者语义不同,共用一个状态会让"手机上开过一次抽屉"变成
35
+ * "桌面侧栏永久收起"(实测:antd Sider 的 breakpoint 就会这么干)。
36
+ */
37
+ function openMobileNav() {
38
+ mobileNavOpen.value = true
39
+ }
40
+
41
+ function closeMobileNav() {
42
+ mobileNavOpen.value = false
43
+ }
44
+
45
+ function toggleMobileNav() {
46
+ mobileNavOpen.value = !mobileNavOpen.value
47
+ }
48
+
29
49
  function setAccent(color: string | null) {
30
50
  accent.value = color
31
51
  }
32
52
 
33
- return { isDark, sidebarCollapsed, accent, themeTouched, toggleDark, toggleSidebar, setAccent }
53
+ return {
54
+ isDark,
55
+ sidebarCollapsed,
56
+ mobileNavOpen,
57
+ accent,
58
+ themeTouched,
59
+ toggleDark,
60
+ toggleSidebar,
61
+ openMobileNav,
62
+ closeMobileNav,
63
+ toggleMobileNav,
64
+ setAccent,
65
+ }
34
66
  }, {
35
67
  persist: {
36
68
  pick: ['isDark', 'sidebarCollapsed', 'accent', 'themeTouched'],
@@ -176,7 +176,7 @@ export const useUserStore = defineStore('workshop.user', {
176
176
  // token 真源是 30 天 cookie;hydration 后从 cookie 回填,无 cookie 即回登出态
177
177
  pick: ['user'],
178
178
  afterHydrate: (ctx) => {
179
- const store = ctx.store as { user?: { token?: string } | null, user?: CurrentUser | null, applyCookie?: () => void }
179
+ const store = ctx.store as { user?: CurrentUser | null, applyCookie?: () => void }
180
180
  if (!store.user) return
181
181
  try {
182
182
  const cookie = useCookie<string | null>('token')
@@ -30,8 +30,21 @@ interface ServerWorkspace {
30
30
  }
31
31
 
32
32
  const ACTIVE_KEY = 'workshop.activeChannel'
33
+ const ACTIVE_WS_KEY = 'workshop.activeWorkspace'
34
+
35
+ /**
36
+ * localStorage 只在浏览器存在。这几个读写点都在 **store 的初始化路径**上
37
+ * (load() → saveActiveMap()),而 load() 完全可能被服务端调用 ——
38
+ * 少了守卫,一次 SSR 调用就会抛 "localStorage is not defined",
39
+ * 被 stability-guard 当成 fatal unhandledRejection **直接结束进程**
40
+ * (实测:生产实例在 /town 刷新后静默退出,日志只有一行 ReferenceError)。
41
+ *
42
+ * 约定与 app/stores/app.ts 一致:持久化层自己保证 SSR 安全,调用方不必先判断环境。
43
+ */
44
+ const hasLocalStorage = (): boolean => typeof localStorage !== 'undefined'
33
45
 
34
46
  function loadActiveMap(): Record<string, string> {
47
+ if (!hasLocalStorage()) return {}
35
48
  try {
36
49
  return JSON.parse(localStorage.getItem(ACTIVE_KEY) ?? '{}') as Record<string, string>
37
50
  }
@@ -41,6 +54,7 @@ function loadActiveMap(): Record<string, string> {
41
54
  }
42
55
 
43
56
  function saveActiveMap(map: Record<string, string>): void {
57
+ if (!hasLocalStorage()) return
44
58
  localStorage.setItem(ACTIVE_KEY, JSON.stringify(map))
45
59
  }
46
60
 
@@ -51,12 +65,14 @@ export const useWorkspacesStore = defineStore('workshop.workspaces', {
51
65
  }),
52
66
  getters: {
53
67
  activeWorkspaceId(state): string | null {
54
- return localStorage.getItem('workshop.activeWorkspace') ?? state.workspaces[0]?.id ?? null
68
+ if (!hasLocalStorage()) return state.workspaces[0]?.id ?? null
69
+ return localStorage.getItem(ACTIVE_WS_KEY) ?? state.workspaces[0]?.id ?? null
55
70
  },
56
71
  },
57
72
  actions: {
58
73
  setActiveWorkspaceId(id: string | null): void {
59
- localStorage.setItem('workshop.activeWorkspace', id ?? '')
74
+ if (!hasLocalStorage()) return
75
+ localStorage.setItem(ACTIVE_WS_KEY, id ?? '')
60
76
  },
61
77
  /** 拉取服务端 workspace 列表(合并本地 activeChannelId) */
62
78
  async load(): Promise<void> {
@@ -20,7 +20,7 @@ interface ErrLike {
20
20
  }
21
21
 
22
22
  /** i18n 取不到时的内置回退(与 locales errors.* 保持一致,zh 优先项目) */
23
- const FALLBACK: Record<string, string> = {
23
+ const FALLBACK = {
24
24
  http400: '请求参数有误,请检查后重试',
25
25
  http401: '登录已过期,请重新登录',
26
26
  http403: '权限不足:该操作需要管理员权限',
@@ -25,10 +25,17 @@ export async function run(argv, ctx) {
25
25
  console.log(`${color.cyan('›')} 构建生产产物 .output/ ...`)
26
26
  // 统一生命周期:转发 SIGINT/SIGTERM(子进程不再变孤儿) + 等待退出 + 超时强杀
27
27
  // + 退出码传播(被信号杀死 → 128+N,不再 code ?? 0 误报成功)
28
+ const env = localBypassEnv()
29
+ // Node ≥21 对 exports 里的"尾部斜杠映射"(\"./\": \"./x/\")发 DEP0155 弃用警告。
30
+ // 本项目依赖树里有两个这样的包(@babel/runtime → ./regenerator/、diff → ./lib/),
31
+ // 它们**不是我们的代码**,也改不动(除非 patch 上游);这条提示不影响产物正确性。
32
+ // 只关这一个 code,其它弃用/实验性警告照常显示 —— 不是把日志静音,是把
33
+ // "上游已弃用、我们无权修改"的确定噪音摘掉。
34
+ env.NODE_OPTIONS = [env.NODE_OPTIONS, '--disable-warning=DEP0155'].filter(Boolean).join(' ')
28
35
  const code = await runChild(process.execPath, [nuxtBin, 'build'], {
29
36
  cwd: root,
30
37
  stdio: 'inherit',
31
- env: localBypassEnv(),
38
+ env,
32
39
  onSpawnError: err => console.log(`${color.red('✖')} 构建失败: ${err.message}`),
33
40
  })
34
41
  if (code !== 0) return code
@@ -21,6 +21,12 @@ services:
21
21
  daq-timescale:
22
22
  image: timescale/timescaledb:latest-pg16
23
23
  container_name: awshop-daq-timescale
24
+ # ⚠️ 必须显式放大 /dev/shm:Docker 默认只给 64MB,而 PostgreSQL 的并行查询
25
+ # 会按需扩容共享内存段 —— 不够时报的错是
26
+ # could not resize shared memory segment "...": No space left on device
27
+ # 这句话看起来像"磁盘满了",实际是 shm 满了,极难排查(实测在回退账本的
28
+ # 兜底评估查询上稳定复现,而宿主机还剩 230GB)。512MB 是 Postgres 官方镜像文档的建议值。
29
+ shm_size: 512mb
24
30
  environment:
25
31
  POSTGRES_USER: postgres # config.yml daq.timescale.user
26
32
  POSTGRES_PASSWORD: awshop # config.yml daq.timescale.password
package/docs/cli.en.md CHANGED
@@ -62,7 +62,7 @@ pnpm tui # the terminal workbench
62
62
  ### 3. Quick use via npx (no global install)
63
63
 
64
64
  ```bash
65
- npx agentworkshop start # fetch and start (pin a version with: npx agentworkshop@0.7.38 start)
65
+ npx agentworkshop start # fetch and start (pin a version with: npx agentworkshop@0.7.41 start)
66
66
  npx agentworkshop doctor # subcommands work as usual
67
67
  npx -p agentworkshop aw doctor # use -p when you want the name aw
68
68
  ```
@@ -79,7 +79,7 @@ npx -p agentworkshop aw doctor # use -p when you want the name aw
79
79
  | Package manager | `pnpm@11.9.0` (`packageManager`, needed only for source development) |
80
80
  | Module format | ESM (`type: module`) |
81
81
  | License | `PolyForm-Noncommercial-1.0.0` |
82
- | Current version | `0.7.38` (`package.json`) |
82
+ | Current version | `0.7.41` (`package.json`) |
83
83
 
84
84
  ## 2. First start (the Claude Code experience)
85
85
 
package/docs/cli.md CHANGED
@@ -60,7 +60,7 @@ pnpm tui # 终端工作台
60
60
  ### 3. npx 快速使用(免全局安装)
61
61
 
62
62
  ```bash
63
- npx agentworkshop start # 拉取并启动(可锁定版本: npx agentworkshop@0.7.38 start)
63
+ npx agentworkshop start # 拉取并启动(可锁定版本: npx agentworkshop@0.7.41 start)
64
64
  npx agentworkshop doctor # 子命令照常可用
65
65
  npx -p agentworkshop aw doctor # 需要 aw 这个名字时用 -p 指定包
66
66
  ```
@@ -76,7 +76,7 @@ npx -p agentworkshop aw doctor # 需要 aw 这个名字时用 -p 指定包
76
76
  | 包管理器 | `pnpm@11.9.0`(`packageManager`,仅源码开发需要) |
77
77
  | 模块形态 | ESM(`type: module`) |
78
78
  | 许可证 | `PolyForm-Noncommercial-1.0.0` |
79
- | 当前版本 | `0.7.38`(`package.json`) |
79
+ | 当前版本 | `0.7.41`(`package.json`) |
80
80
 
81
81
  ## 二、首次启动(Claude Code 式)
82
82
 
@@ -9,7 +9,7 @@
9
9
  >
10
10
  > Applies to: plugin system v2 has been available since **v0.7.29** (frontend component
11
11
  > injection / plugin settings / plugin i18n / runtime services surface); this document
12
- > describes **v0.7.38**. `ctx.sdkVersion` is currently `0.3.0`.
12
+ > describes **v0.7.41**. `ctx.sdkVersion` is currently `0.3.0`.
13
13
 
14
14
  ## 1. Folder layout & quick start
15
15
 
@@ -210,10 +210,41 @@ async setup(ctx) {
210
210
  | Events | `ctx.events.on(type, fn)` / `ctx.events.off(type, fn)` | scene-event sugar: subscribes to `event:<type>` internally (see §7) |
211
211
  | Paths | `ctx.paths.home / configRoot / dataDir`; `ctx.dataDir` | `ctx.dataDir` is this plugin's KV folder, `<configRoot>/data/plugins/<plugin>` |
212
212
  | DAQ | `ctx.daq.registerDriver / registerProcessor / registerTemplate / onFrame / onSample / query / nodes` | driver, sink-processor and node-template registration + time-series queries + node metadata snapshot |
213
+ | Write control | `ctx.dcw.registerWriteDriver(driver)` | write-driver injection (symmetric to DAQ read drivers): contract `{ kind, available(), write(input), test(cfg), read?(input), meta? }`; disabled plugins lose their drivers on hot reload |
213
214
  | Tools | `ctx.omp.registerTool({ name, label?, description, parameters, roles?, handler })` | agent tool injection; `roles` accepts `'lead'` and `'worker'` only (see §10) |
214
215
  | Services | `ctx.services.names()` / `.get(name)` / `.provide(name, getter)` | runtime object surface: `get('daq' \| 'lines' \| 'channels' \| 'plugins')`; `provide` auto-prefixes `<plugin>.` |
215
216
  | Permissions | `ctx.permissions.lineMode / visibleLineIds / listGrants / setGrants` | line-grant queries and management |
216
217
 
218
+ ### Communication-protocol plugins (custom interface connections)
219
+
220
+ `ctx.daq.registerDriver` / `ctx.dcw.registerWriteDriver` are the **protocol plug-in points**: serial
221
+ links, other PLC protocols and private buses all inject here — the main pipelines (sampling loop /
222
+ write gateway / frontend forms) need zero changes.
223
+
224
+ ```js
225
+ ctx.daq.registerDriver({
226
+ kind: 'serial', // custom protocol kind (overriding a builtin logs a warning)
227
+ async available() { return true }, // protocol-stack probe (missing package → UI shows "not installed")
228
+ async sample({ driverConfig }) { return 25.3 }, // reading (scalar or v2 frame envelope)
229
+ async test(driverConfig) { return { ok: true, message: 'connected', sampleValue: 25.3 } },
230
+ meta: { // self-description (frontend "add node" dropdown + dynamic form)
231
+ label: 'Serial (RS-232/485)',
232
+ status: 'real',
233
+ configFields: [{ key: 'path', label: 'Port path', type: 'string', required: true, placeholder: 'COM3' }],
234
+ },
235
+ })
236
+ ```
237
+
238
+ Key points:
239
+ - The driver catalog is served via the `drivers` field of `GET /api/workshop/daq` (read) and
240
+ `GET /api/workshop/dcw` (write); the DAQ/DCW pages render plugin protocols and their forms from it
241
+ (with a ⌁ badge). Drivers registered without `meta` only appear in availability;
242
+ - Hot reload clears all plugin drivers first, then re-loads: **disabled/unloaded protocol drivers
243
+ take effect immediately upon removal** and never linger until restart;
244
+ - Builtin example: `server/plugins-builtin/serial-bridge` (serial Modbus RTU + ASCII line protocol,
245
+ with read/write drivers, `health`/`ports`/`probe` APIs, a frontend panel and the `serial_ports`
246
+ agent tool) — use it as the template for new protocol plugins.
247
+
217
248
  ### Where KV lives
218
249
 
219
250
  `<configRoot>/data/plugins/<plugin>/kv.json` — **not** inside the plugin folder. Writes are authoritative in memory and flushed atomically (`tmp` + `rename`) after a 200 ms debounce; on shutdown and hot reload the pending debounce is cancelled and the state is flushed synchronously. `ctx.kv` has exactly four methods: `get / set / all / bump`.
@@ -524,6 +555,7 @@ export default {
524
555
  - **A newly enabled browser plugin injects within 15 s** (instantly when the WS channel is up).
525
556
  - **`server:close` / `onDispose` depend on graceful shutdown signals**; a forced kill on Windows does not trigger them.
526
557
  - **`aw plugin list` only scans directories** — it reflects neither load failures nor runtime health.
558
+ - **Protocol plugins share the host process with their native stacks**: serial-bridge's RTU framing is a self-contained implementation (single serialport native stack; modbus-serial is deliberately NOT used — its wrapper reproducibly crashed under dev mode). Known boundary: **inside the `aw dev` process, a serial transaction that ends in write → timeout → close can trigger a native V8 crash in `bindings-cpp`** (HandleScope fatal — not catchable in JS, regardless of which port is used); **production mode (`aw start`) is unaffected — the exact same sequence was verified live to return an honest timeout with the server healthy**, and open-only probes are also fine under dev. Connect real hardware during development with this in mind; for hardened deployments, run the serial gateway in its own process (see the plc-node-simulator child-process pattern).
527
559
 
528
560
  ## 13. Pre-release checklist
529
561