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
@@ -50,9 +50,9 @@ const loadHarnessAvail = async (): Promise<void> => {
50
50
  }
51
51
  if (import.meta.client) void loadHarnessAvail()
52
52
  const tplUnavailable = (tpl: AgentTemplateDto): boolean => harnessAvail.value[tpl.harness] === false
53
- const memberOptions = computed(() => templates.value.map((t) => {
54
- const un = tplUnavailable(t)
55
- return { value: t.id, label: un ? `${t.name}(${t.harness} · ${t('agents.notInstalled')})` : `${t.name}(${t.harness})`, disabled: un }
53
+ const memberOptions = computed(() => templates.value.map((tpl) => {
54
+ const un = tplUnavailable(tpl)
55
+ return { value: tpl.id, label: un ? `${tpl.name}(${tpl.harness} · ${t('agents.notInstalled')})` : `${tpl.name}(${tpl.harness})`, disabled: un }
56
56
  }))
57
57
 
58
58
  // ===== 过滤 =====
@@ -312,12 +312,12 @@ useHead({ title: () => t('titles.teams') })
312
312
  :class="visTag(team).icon"
313
313
  />{{ visTag(team).text }}
314
314
  </a-tag>
315
+ <!-- 开关不带文字:左边那枚 tag 已经在说"公开/私有"了(与模板库同一条规则) -->
315
316
  <a-switch
316
317
  v-if="!team.isBuiltin && canWrite(team)"
317
318
  :checked="team.visibility === 'public'"
318
319
  size="small"
319
- checked-children="公开"
320
- un-checked-children="私有"
320
+ :title="team.visibility === 'public' ? '点击转为私有' : '点击公开(全员可部署)'"
321
321
  @change="(v: unknown) => toggleVisibility(team, v === true)"
322
322
  />
323
323
  <span class="owner">{{ team.ownerName ?? '-' }}</span>
@@ -340,10 +340,13 @@ useHead({ title: () => t('titles.teams') })
340
340
  </a-menu>
341
341
  </template>
342
342
  </a-dropdown>
343
+ <!-- 用 outlined 而不是 text:text 按钮在卡片头上与旁边的 owner 文本
344
+ 长得一模一样,用户看不出这里是**唯一的主操作**(实测三张卡片都如此)。 -->
343
345
  <a-button
344
346
  v-else
347
+ class="deploy-btn"
345
348
  size="small"
346
- type="text"
349
+ type="default"
347
350
  :title="$t('teams.kxheuf1001')"
348
351
  @click="openDeploy(team)"
349
352
  >
@@ -591,6 +594,12 @@ h2 { margin: 0 0 4px; }
591
594
  font-size: 11px;
592
595
  color: var(--ink-faint);
593
596
  }
597
+ .deploy-btn {
598
+ flex: none;
599
+ font-size: 11.5px;
600
+ border-color: var(--line-strong);
601
+ }
602
+
594
603
  .op { cursor: pointer; opacity: 0.4; }
595
604
  .op:hover { opacity: 1; }
596
605
  .members {
@@ -677,4 +686,55 @@ h2 { margin: 0 0 4px; }
677
686
  line-height: 1.5;
678
687
  color: var(--ink-faint);
679
688
  }
689
+
690
+ /* ══ 窄屏(v9):页头纵向堆叠 / 筛选条换行 / 卡片单列 ══════════════════════ */
691
+ @media (max-width: 900px) {
692
+ .head {
693
+ flex-direction: column;
694
+ align-items: stretch;
695
+ gap: 12px;
696
+ }
697
+
698
+ .head > div { min-width: 0; }
699
+ .head h2 { font-size: 21px; line-height: 1.25; }
700
+
701
+ .head :deep(.ant-space) {
702
+ display: flex;
703
+ flex-direction: column;
704
+ gap: 8px;
705
+ align-items: stretch;
706
+ width: 100%;
707
+ }
708
+
709
+ .head :deep(.ant-space-item) { width: 100%; }
710
+ .head :deep(.ant-btn) { width: 100%; min-height: 40px; }
711
+
712
+ .toolbar {
713
+ flex-wrap: wrap;
714
+ gap: 8px;
715
+ }
716
+
717
+ .toolbar :deep(.ant-segmented) {
718
+ flex: 1 1 100%;
719
+ min-width: 0;
720
+ }
721
+
722
+ .admin-note {
723
+ flex: 1 1 100%;
724
+ min-width: 0;
725
+ font-size: 11.5px;
726
+ line-height: 1.5;
727
+ }
728
+
729
+ .grid { grid-template-columns: 1fr; }
730
+ .card-head { flex-wrap: wrap; }
731
+ .member { flex-wrap: wrap; }
732
+ .member-harness { font-size: 11.5px; }
733
+ }
734
+
735
+ @media (max-width: 640px) {
736
+ .page { padding: 0; }
737
+ .head h2 { font-size: 19px; }
738
+ .sub { font-size: 11.5px; line-height: 1.5; }
739
+ }
680
740
  </style>
@@ -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
@@ -36,6 +36,8 @@ export default {
36
36
  header: {
37
37
  collapse: 'Collapse menu',
38
38
  expand: 'Expand menu',
39
+ openNav: 'Open navigation',
40
+ closeNav: 'Close navigation',
39
41
  language: 'Language',
40
42
  theme: 'Theme',
41
43
  fullscreen: 'Fullscreen',
@@ -36,6 +36,8 @@ export default {
36
36
  header: {
37
37
  collapse: '折叠菜单',
38
38
  expand: '展开菜单',
39
+ openNav: '打开导航',
40
+ closeNav: '关闭导航',
39
41
  language: '语言',
40
42
  theme: '主题',
41
43
  fullscreen: '全屏',