agentworkshop 0.7.39 → 0.7.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. package/.AgentWorkShop/prompts/host-tools.json +55 -0
  2. package/.output/.AgentWorkShop/prompts/host-tools.json +55 -0
  3. package/.output/nitro.json +1 -1
  4. package/.output/public/_nuxt/88GOowgO2.js +1 -0
  5. package/.output/public/_nuxt/{BNT8gYU92.js → BC9wjTJK2.js} +2 -2
  6. package/.output/public/_nuxt/BOt2SP4z.js +1 -0
  7. package/.output/public/_nuxt/BP-NyKVG.js +18 -0
  8. package/.output/public/_nuxt/{HBLJ-wei.js → BXxH0UQE.js} +1 -1
  9. package/.output/public/_nuxt/{zIGh7R49.js → BneN7fbu.js} +1 -1
  10. package/.output/public/_nuxt/BsROmY89.js +1 -0
  11. package/.output/public/_nuxt/{C_OLzD09.js → C4oW1HsE.js} +1 -1
  12. package/.output/public/_nuxt/CFxtLwjp.js +1 -0
  13. package/.output/public/_nuxt/{Cec0Ua3F.js → CS50LB_l.js} +1 -1
  14. package/.output/public/_nuxt/C_iYD-EB.js +1 -0
  15. package/.output/public/_nuxt/{DAe0h2MB.js → ClILjvWH.js} +1 -1
  16. package/.output/public/_nuxt/CssgYAAL2.js +1 -0
  17. package/.output/public/_nuxt/{CY9LbPCI.js → CzOCSGLe.js} +1 -1
  18. package/.output/public/_nuxt/D8qvkfyY.js +1 -0
  19. package/.output/public/_nuxt/DCn7QZ5N.js +2 -0
  20. package/.output/public/_nuxt/DGIuvyRN2.js +1 -0
  21. package/.output/public/_nuxt/{CojCTLKD.js → DGj-WEBg.js} +1 -1
  22. package/.output/public/_nuxt/{DDShH71m.js → DS8qj8EL.js} +1 -1
  23. package/.output/public/_nuxt/DTFCy6h-.js +1 -0
  24. package/.output/public/_nuxt/{DrYV5i68.js → DUjW5Yq5.js} +1 -1
  25. package/.output/public/_nuxt/DZshyNLa.js +1 -0
  26. package/.output/public/_nuxt/{45zuyAso.js → D_OTvQza.js} +1 -1
  27. package/.output/public/_nuxt/DcsCcxp4.js +1 -0
  28. package/.output/public/_nuxt/{DUrecWPx.js → Dihnimlk.js} +2 -2
  29. package/.output/public/_nuxt/Dqw1lpr-.js +1 -0
  30. package/.output/public/_nuxt/{CUDwzgLV.js → Dr9v6C0f.js} +9 -9
  31. package/.output/public/_nuxt/{8NMOXZOV.js → Du9ZJtnj.js} +1 -1
  32. package/.output/public/_nuxt/TownView.f-YOdXRs.css +1 -0
  33. package/.output/public/_nuxt/_id_.CM4bIgKY.css +1 -0
  34. package/.output/public/_nuxt/_id_.bNV69Dtf.css +1 -0
  35. package/.output/public/_nuxt/_wsId_.B4WiqkIS.css +1 -0
  36. package/.output/public/_nuxt/agents.ZD0rVdHE.css +1 -0
  37. package/.output/public/_nuxt/aml.BN8ZEW4h.css +1 -0
  38. package/.output/public/_nuxt/builds/latest.json +1 -1
  39. package/.output/public/_nuxt/builds/meta/f4707884-7cf4-4a31-80d2-e3d72b73745f.json +1 -0
  40. package/.output/public/_nuxt/channel-templates.BOtahzPi.css +1 -0
  41. package/.output/public/_nuxt/daq.B0D3dPAc.css +1 -0
  42. package/.output/public/_nuxt/dcw.8Y7FqCVN.css +1 -0
  43. package/.output/public/_nuxt/default.J32SgqFY.css +1 -0
  44. package/.output/public/_nuxt/entry.bdm0tv6M.css +1 -0
  45. package/.output/public/_nuxt/logs.BpKzmKsh.css +1 -0
  46. package/.output/public/_nuxt/monitor.D9pe7qN6.css +1 -0
  47. package/.output/public/_nuxt/pages.D7aSDlUI.css +1 -0
  48. package/.output/public/_nuxt/permissions.DwTVlFe6.css +1 -0
  49. package/.output/public/_nuxt/plugins.sb4nDGqr.css +1 -0
  50. package/.output/public/_nuxt/{Cbq-mryP.js → qr6R6Ha1.js} +1 -1
  51. package/.output/public/_nuxt/settings.BTgR-NiE.css +1 -0
  52. package/.output/public/_nuxt/teams.CxnxKVVs.css +1 -0
  53. package/.output/public/_nuxt/tokens.BFY7qv4f.css +1 -0
  54. package/.output/public/_nuxt/{town.DKIAhKnx.css → town.BBXZmeoP.css} +1 -1
  55. package/.output/public/_nuxt/users.DLgZ1D0h.css +1 -0
  56. package/.output/public/_nuxt/workshop.CR_QzeAN.css +1 -0
  57. package/.output/public/_nuxt/{BzHEilTt.js → xh3_vYcu.js} +1 -1
  58. package/.output/public/_nuxt/yF0aYMF1.js +4 -0
  59. package/.output/server/chunks/_/entity.mjs.map +1 -1
  60. package/.output/server/chunks/_/nitro.mjs +2225 -1445
  61. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  62. package/.output/server/chunks/build/{TownView-jVazYOjN.mjs → TownView-BuLAsmpR.mjs} +288 -206
  63. package/.output/server/chunks/build/TownView-BuLAsmpR.mjs.map +1 -0
  64. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs → _id_-BU1wfOID.mjs} +138 -138
  65. package/.output/server/chunks/build/{_id_-CfVosNg3.mjs.map → _id_-BU1wfOID.mjs.map} +1 -1
  66. package/.output/server/chunks/build/{_id_-Hwr4rZpr.mjs → _id_-BYsEua57.mjs} +33 -32
  67. package/.output/server/chunks/build/_id_-BYsEua57.mjs.map +1 -0
  68. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs → _wsId_-BxlDcJHg.mjs} +293 -259
  69. package/.output/server/chunks/build/{_wsId_-BVg3bB77.mjs.map → _wsId_-BxlDcJHg.mjs.map} +1 -1
  70. package/.output/server/chunks/build/{agents-Bvin73QY.mjs → agents-CLQqJF2y.mjs} +21 -27
  71. package/.output/server/chunks/build/agents-CLQqJF2y.mjs.map +1 -0
  72. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs → aml-_7FdFL2p.mjs} +109 -109
  73. package/.output/server/chunks/build/{aml-BCSJCdX7.mjs.map → aml-_7FdFL2p.mjs.map} +1 -1
  74. package/.output/server/chunks/build/api-error-dul30BYQ.mjs.map +1 -1
  75. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs → channel-templates-p6SZNjlM.mjs} +19 -19
  76. package/.output/server/chunks/build/{channel-templates-BTq8570n.mjs.map → channel-templates-p6SZNjlM.mjs.map} +1 -1
  77. package/.output/server/chunks/build/{daq-C03Uu5UE.mjs → daq-cgyjKmJu.mjs} +188 -122
  78. package/.output/server/chunks/build/daq-cgyjKmJu.mjs.map +1 -0
  79. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs → dcw-CoTxBlAZ.mjs} +45 -45
  80. package/.output/server/chunks/build/{dcw-6vEYVeje.mjs.map → dcw-CoTxBlAZ.mjs.map} +1 -1
  81. package/.output/server/chunks/build/{default-CZmZFpqH.mjs → default-BE4r0FZN.mjs} +152 -58
  82. package/.output/server/chunks/build/{default-CZmZFpqH.mjs.map → default-BE4r0FZN.mjs.map} +1 -1
  83. package/.output/server/chunks/build/{logs-B-xhuqln.mjs → logs-BSOeZwwI.mjs} +34 -34
  84. package/.output/server/chunks/build/{logs-B-xhuqln.mjs.map → logs-BSOeZwwI.mjs.map} +1 -1
  85. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs → monitor-C0okQaXz.mjs} +33 -33
  86. package/.output/server/chunks/build/{monitor-CyfDt4nR.mjs.map → monitor-C0okQaXz.mjs.map} +1 -1
  87. package/.output/server/chunks/build/{pages-BO7yZsXA.mjs → pages-DFxjn9Xa.mjs} +33 -17
  88. package/.output/server/chunks/build/pages-DFxjn9Xa.mjs.map +1 -0
  89. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs → permissions-68SsNjex.mjs} +12 -12
  90. package/.output/server/chunks/build/{permissions-B52oOmaO.mjs.map → permissions-68SsNjex.mjs.map} +1 -1
  91. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs → plugins-lGlUr1lj.mjs} +17 -17
  92. package/.output/server/chunks/build/{plugins-CUWmMdyt.mjs.map → plugins-lGlUr1lj.mjs.map} +1 -1
  93. package/.output/server/chunks/build/{settings-DoQ2Oq3P.mjs → settings-Ddd1qx2Z.mjs} +50 -47
  94. package/.output/server/chunks/build/settings-Ddd1qx2Z.mjs.map +1 -0
  95. package/.output/server/chunks/build/{teams-2kz3CusY.mjs → teams-MEDLZdMn.mjs} +38 -34
  96. package/.output/server/chunks/build/teams-MEDLZdMn.mjs.map +1 -0
  97. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs → tokens-C7fTM5C2.mjs} +14 -14
  98. package/.output/server/chunks/build/{tokens-BbWLqtVz.mjs.map → tokens-C7fTM5C2.mjs.map} +1 -1
  99. package/.output/server/chunks/build/{town-BOl6hs9B.mjs → town-qMPql9Se.mjs} +36 -10
  100. package/.output/server/chunks/build/town-qMPql9Se.mjs.map +1 -0
  101. package/.output/server/chunks/build/useDaqStream-CIBaiGAl.mjs +1 -1
  102. package/.output/server/chunks/build/useDcwStream-CBbiYnWe.mjs +1 -1
  103. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs +49 -0
  104. package/.output/server/chunks/build/useResponsive-BKDMGTHs.mjs.map +1 -0
  105. package/.output/server/chunks/build/{users-fohSmF-J.mjs → users-Cfd5-l2-.mjs} +6 -6
  106. package/.output/server/chunks/build/{users-fohSmF-J.mjs.map → users-Cfd5-l2-.mjs.map} +1 -1
  107. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs → workshop-BL5bhfs0.mjs} +15 -15
  108. package/.output/server/chunks/build/{workshop-DX5-ldc4.mjs.map → workshop-BL5bhfs0.mjs.map} +1 -1
  109. package/.output/server/chunks/build/{workspaces-BuydxM-3.mjs → workspaces-Bgz5Ekwp.mjs} +18 -3
  110. package/.output/server/chunks/build/workspaces-Bgz5Ekwp.mjs.map +1 -0
  111. package/.output/server/chunks/routes/api/system/config-groups/_id_.patch.mjs.map +1 -1
  112. package/.output/server/chunks/routes/api/system/index.patch.mjs.map +1 -1
  113. package/.output/server/chunks/routes/api/system/index.post.mjs.map +1 -1
  114. package/.output/server/chunks/routes/api/system/settings/events.get.mjs.map +1 -1
  115. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs +6 -2
  116. package/.output/server/chunks/routes/api/workshop/agent-tools/invoke.post.mjs.map +1 -1
  117. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs +2 -1
  118. package/.output/server/chunks/routes/api/workshop/aml/datasets/_id_.get.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  120. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +3 -3
  121. package/.output/server/chunks/routes/api/workshop/daq/index.post.mjs.map +1 -1
  122. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs.map +1 -1
  123. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs.map +1 -1
  124. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +3 -2
  125. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs.map +1 -1
  126. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +2 -1
  127. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs.map +1 -1
  128. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +8 -6
  129. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs.map +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +5 -5
  131. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +14 -4
  133. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  134. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +4 -4
  135. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs.map +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +4 -3
  137. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs.map +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +31 -0
  139. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs.map +1 -0
  140. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +28 -0
  141. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -0
  142. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.patch.mjs.map +1 -1
  143. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +32 -0
  144. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -0
  145. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +34 -0
  146. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs.map +1 -0
  147. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +33 -0
  148. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs.map +1 -0
  149. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +34 -0
  150. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs.map +1 -0
  151. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  152. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  153. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  154. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  155. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  156. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  157. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +2 -0
  159. package/.output/server/chunks/routes/api/workshop/index6.get.mjs.map +1 -1
  160. package/.output/server/chunks/routes/api/workshop/index9.get.mjs +3 -1
  161. package/.output/server/chunks/routes/api/workshop/index9.get.mjs.map +1 -1
  162. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  164. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  165. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  166. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  167. package/.output/server/chunks/routes/renderer.mjs +3 -3
  168. package/.output/server/chunks/virtual/entry.mjs +51 -25
  169. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  170. package/.output/server/package.json +1 -1
  171. package/README-zh.md +199 -35
  172. package/README.md +739 -591
  173. package/app/app.vue +9 -3
  174. package/app/assets/css/main.css +517 -7
  175. package/app/components/AppHeader.vue +98 -7
  176. package/app/components/AppSidebar.vue +117 -11
  177. package/app/components/AwChart.vue +2 -1
  178. package/app/components/workshop/AgentInspectorDrawer.vue +6 -6
  179. package/app/components/workshop/AgentLanesView.vue +52 -1
  180. package/app/components/workshop/AssetLibrary.vue +4 -2
  181. package/app/components/workshop/ChannelSessionList.vue +4 -2
  182. package/app/components/workshop/Composer.vue +96 -0
  183. package/app/components/workshop/DaqNodeRow.vue +19 -0
  184. package/app/components/workshop/DaqTemplateIcon.vue +33 -0
  185. package/app/components/workshop/DeviceTwinPanel.vue +37 -35
  186. package/app/components/workshop/EventBlock.vue +3 -3
  187. package/app/components/workshop/InspectorPanel.vue +47 -11
  188. package/app/components/workshop/LiveAgentBar.vue +34 -0
  189. package/app/components/workshop/TaskBoardView.vue +83 -33
  190. package/app/components/workshop/TranscriptTimeline.vue +56 -0
  191. package/app/components/workshop/blocks/ClusterError.vue +1 -1
  192. package/app/components/workshop/blocks/ClusterRoute.vue +3 -1
  193. package/app/components/workshop/blocks/ClusterStatus.vue +2 -2
  194. package/app/components/workshop/blocks/ClusterStream.vue +8 -2
  195. package/app/components/workshop/town/ModelPreview3D.vue +1 -1
  196. package/app/components/workshop/town/TownView.vue +507 -82
  197. package/app/composables/useResponsive.ts +88 -0
  198. package/app/composables/useRouteMeta.ts +3 -0
  199. package/app/composables/workshop/useDcwStream.ts +1 -1
  200. package/app/composables/workshop/useVisibleInterval.ts +2 -2
  201. package/app/composables/workshop/useWorkshopApi.ts +1 -1
  202. package/app/layouts/default.vue +46 -2
  203. package/app/pages/aml/index.vue +712 -616
  204. package/app/pages/daq/[id].vue +3 -2
  205. package/app/pages/daq/index.vue +209 -9
  206. package/app/pages/dcw/[id].vue +109 -28
  207. package/app/pages/dcw/index.vue +83 -23
  208. package/app/pages/index.vue +35 -1
  209. package/app/pages/logs/index.vue +147 -13
  210. package/app/pages/monitor.vue +56 -12
  211. package/app/pages/permissions/index.vue +34 -0
  212. package/app/pages/plugins/index.vue +35 -4
  213. package/app/pages/settings.vue +90 -21
  214. package/app/pages/tokens.vue +23 -0
  215. package/app/pages/town.vue +50 -5
  216. package/app/pages/users.vue +23 -0
  217. package/app/pages/workshop/agents.vue +92 -2
  218. package/app/pages/workshop/channel-templates.vue +53 -0
  219. package/app/pages/workshop/index.vue +14 -0
  220. package/app/pages/workshop/teams.vue +66 -6
  221. package/app/pages/workshop/w/[wsId].vue +202 -14
  222. package/app/plugins/aw-plugins.client.ts +13 -8
  223. package/app/stores/app.ts +33 -1
  224. package/app/stores/workshop/user.ts +1 -1
  225. package/app/stores/workshop/workspaces.ts +18 -2
  226. package/app/utils/api-error.ts +1 -1
  227. package/cli/commands/build.mjs +8 -1
  228. package/docker-compose.yml +6 -0
  229. package/i18n/locales/en.ts +2 -0
  230. package/i18n/locales/zh-CN.ts +2 -0
  231. package/nuxt.config.ts +31 -0
  232. package/package.json +9 -1
  233. package/scripts/_dbg-param-map-e2e.ts +242 -0
  234. package/scripts/_dbg-param-map-rest-smoke.mjs +73 -0
  235. package/scripts/_dbg-param-real-scenario.mjs +153 -0
  236. package/scripts/api-live-e2e.mjs +40 -8
  237. package/scripts/capture-walkthrough.mjs +232 -0
  238. package/scripts/render-walkthrough-figure.mjs +46 -0
  239. package/scripts/seed-walkthrough-fixture.mjs +143 -0
  240. package/scripts/test-plugin-hardening.mjs +8 -0
  241. package/scripts/ui/admin-shots.mjs +46 -0
  242. package/scripts/ui/audit-layout.mjs +301 -0
  243. package/scripts/ui/bump-microfont.mjs +39 -0
  244. package/scripts/ui/lib.mjs +198 -0
  245. package/scripts/ui/linkcheck2.mjs +37 -0
  246. package/scripts/ui/live-render-check.mjs +72 -0
  247. package/scripts/ui/md-lint.mjs +80 -0
  248. package/scripts/ui/perf-audit.mjs +132 -0
  249. package/scripts/ui/readme-assets.mjs +30 -0
  250. package/scripts/ui/readme-shots.mjs +42 -0
  251. package/scripts/ui/record.mjs +225 -0
  252. package/scripts/ui/seed-admin.mjs +68 -0
  253. package/scripts/ui/seed-demo.mjs +83 -0
  254. package/scripts/ui/shot-hero.mjs +25 -0
  255. package/scripts/ui/shots.mjs +75 -0
  256. package/scripts/ui/sim-ui-shot.mjs +16 -0
  257. package/scripts/ui/verify-nav.mjs +51 -0
  258. package/scripts/ui/verify-responsive.mjs +285 -0
  259. package/server/api/system/config-groups/[id].patch.ts +1 -1
  260. package/server/api/system/config-groups/index.post.ts +1 -1
  261. package/server/api/system/settings/events.get.ts +1 -1
  262. package/server/api/system/settings/index.patch.ts +1 -1
  263. package/server/api/workshop/agent-tools/invoke.post.ts +16 -2
  264. package/server/api/workshop/aml/datasets/[id].get.ts +8 -1
  265. package/server/api/workshop/daq/infra/reconnect.post.ts +1 -1
  266. package/server/api/workshop/daq/templates/[key].delete.ts +1 -0
  267. package/server/api/workshop/daq/templates/[key].patch.ts +1 -0
  268. package/server/api/workshop/daq/templates/index.post.ts +1 -0
  269. package/server/api/workshop/dcw/[id]/write.post.ts +10 -2
  270. package/server/api/workshop/dcw/[id].delete.ts +5 -2
  271. package/server/api/workshop/dcw/index.get.ts +2 -0
  272. package/server/api/workshop/dcw/lines/[id].patch.ts +1 -1
  273. package/server/api/workshop/dcw/params/[id]/read.post.ts +17 -0
  274. package/server/api/workshop/dcw/params/[id]/write.post.ts +21 -0
  275. package/server/api/workshop/dcw/params/[id].delete.ts +19 -0
  276. package/server/api/workshop/dcw/params/[id].patch.ts +23 -0
  277. package/server/api/workshop/dcw/params/index.get.ts +27 -0
  278. package/server/api/workshop/dcw/params/index.post.ts +25 -0
  279. package/server/api/workshop/permissions/index.get.ts +3 -1
  280. package/server/plugins/aw-plugins.ts +14 -3
  281. package/server/plugins/dev-stability-guard.ts +3 -1
  282. package/server/plugins/system-config.ts +6 -1
  283. package/server/services/system-config.ts +28 -9
  284. package/server/services/workshop/agents/adapters/one-shot-cli-agent.ts +2 -1
  285. package/server/services/workshop/agents/agent-interface.ts +8 -6
  286. package/server/services/workshop/agents/base-agent.ts +18 -4
  287. package/server/services/workshop/agents/claude-agent.ts +7 -3
  288. package/server/services/workshop/agents/codex-agent.ts +3 -3
  289. package/server/services/workshop/agents/dsh-agent.ts +3 -3
  290. package/server/services/workshop/agents/goose-agent.ts +0 -1
  291. package/server/services/workshop/agents/harness-terminal.ts +14 -1
  292. package/server/services/workshop/agents/hermes-agent.ts +4 -2
  293. package/server/services/workshop/agents/host-tool-bridge.ts +5 -1
  294. package/server/services/workshop/agents/industrial-context.ts +4 -1
  295. package/server/services/workshop/agents/industrial-tools.ts +164 -6
  296. package/server/services/workshop/agents/omp-agent.ts +15 -9
  297. package/server/services/workshop/agents/opencode-agent.ts +47 -21
  298. package/server/services/workshop/agents/qwen-agent.ts +10 -5
  299. package/server/services/workshop/aml/entity.ts +3 -1
  300. package/server/services/workshop/aml/python-runtime.ts +3 -2
  301. package/server/services/workshop/daq/bus/mqtt.adapter.ts +8 -4
  302. package/server/services/workshop/daq/daq-controller.ts +18 -3
  303. package/server/services/workshop/daq/daq-runtime.ts +5 -3
  304. package/server/services/workshop/daq/drivers.ts +63 -8
  305. package/server/services/workshop/daq/host-ports.ts +3 -2
  306. package/server/services/workshop/daq/storage/timescale.adapter.ts +3 -1
  307. package/server/services/workshop/db/channel.repo.ts +4 -0
  308. package/server/services/workshop/db/ops.repo.ts +5 -2
  309. package/server/services/workshop/db/retention.ts +8 -3
  310. package/server/services/workshop/dcw/dcw-controller.ts +143 -16
  311. package/server/services/workshop/dcw/dcw-product.repo.ts +38 -1
  312. package/server/services/workshop/dcw/dcw-runtime.ts +3 -2
  313. package/server/services/workshop/dcw/drivers.ts +5 -2
  314. package/server/services/workshop/dcw/param-conversion.ts +78 -0
  315. package/server/services/workshop/dcw/param-limits.ts +142 -0
  316. package/server/services/workshop/dcw/param-map.repo.ts +305 -0
  317. package/server/services/workshop/dcw/recipe-rollback-manager.ts +13 -7
  318. package/server/services/workshop/runtime/agent-runtime.ts +4 -0
  319. package/server/services/workshop/runtime/manager.ts +9 -2
  320. package/server/services/workshop/settings.ts +4 -2
  321. package/server/types/pg.d.ts +49 -0
  322. package/server/utils/stability-severity.ts +13 -2
  323. package/shared/config/groups.mjs +22 -1
  324. package/shared/config/home.d.mts +28 -0
  325. package/shared/dcw-protocol.ts +136 -0
  326. package/uno.config.ts +12 -1
  327. package/.output/public/_nuxt/BFkgkW8u.js +0 -4
  328. package/.output/public/_nuxt/C5gUkRwn.js +0 -1
  329. package/.output/public/_nuxt/CM12FuB42.js +0 -1
  330. package/.output/public/_nuxt/CZiw7sEV.js +0 -1
  331. package/.output/public/_nuxt/Cc_9VzD42.js +0 -1
  332. package/.output/public/_nuxt/CdPzkOMV.js +0 -1
  333. package/.output/public/_nuxt/CmsOtOAA2.js +0 -1
  334. package/.output/public/_nuxt/D1YDxZpZ.js +0 -1
  335. package/.output/public/_nuxt/D20Yamr4.js +0 -1
  336. package/.output/public/_nuxt/DmqknNvV.js +0 -18
  337. package/.output/public/_nuxt/DnUuIR83.js +0 -1
  338. package/.output/public/_nuxt/S_fVQwJK.js +0 -1
  339. package/.output/public/_nuxt/TownView.0oLiQgTC.css +0 -1
  340. package/.output/public/_nuxt/_id_.D-SYuyZb.css +0 -1
  341. package/.output/public/_nuxt/_id_.hma_BU6T.css +0 -1
  342. package/.output/public/_nuxt/_wsId_.B12v3PhY.css +0 -1
  343. package/.output/public/_nuxt/agents.BV70ioCv.css +0 -1
  344. package/.output/public/_nuxt/ahgEJu2R.js +0 -1
  345. package/.output/public/_nuxt/aml.COEKNNqK.css +0 -1
  346. package/.output/public/_nuxt/builds/meta/4457d63f-0866-4cea-b320-04a321f24795.json +0 -1
  347. package/.output/public/_nuxt/channel-templates.C-M5nKcC.css +0 -1
  348. package/.output/public/_nuxt/daq.B3265ZDn.css +0 -1
  349. package/.output/public/_nuxt/dcw.DLYZqEhT.css +0 -1
  350. package/.output/public/_nuxt/default.BXI_fZUi.css +0 -1
  351. package/.output/public/_nuxt/entry.BB-Eeh-J.css +0 -1
  352. package/.output/public/_nuxt/logs.CqFyMrJw.css +0 -1
  353. package/.output/public/_nuxt/monitor.B--1dPzs.css +0 -1
  354. package/.output/public/_nuxt/pages.ooEzrMm9.css +0 -1
  355. package/.output/public/_nuxt/permissions.B1pjGChZ.css +0 -1
  356. package/.output/public/_nuxt/plugins.5e5chZ67.css +0 -1
  357. package/.output/public/_nuxt/pz2JDAM7.js +0 -2
  358. package/.output/public/_nuxt/settings.Dm3M0RLy.css +0 -1
  359. package/.output/public/_nuxt/teams.BC75EGnR.css +0 -1
  360. package/.output/public/_nuxt/tokens.xRb1fSPU.css +0 -1
  361. package/.output/public/_nuxt/users.CXnP2E08.css +0 -1
  362. package/.output/public/_nuxt/workshop.Jk5HxWPE.css +0 -1
  363. package/.output/server/chunks/build/TownView-jVazYOjN.mjs.map +0 -1
  364. package/.output/server/chunks/build/_id_-Hwr4rZpr.mjs.map +0 -1
  365. package/.output/server/chunks/build/agents-Bvin73QY.mjs.map +0 -1
  366. package/.output/server/chunks/build/daq-C03Uu5UE.mjs.map +0 -1
  367. package/.output/server/chunks/build/pages-BO7yZsXA.mjs.map +0 -1
  368. package/.output/server/chunks/build/settings-DoQ2Oq3P.mjs.map +0 -1
  369. package/.output/server/chunks/build/teams-2kz3CusY.mjs.map +0 -1
  370. package/.output/server/chunks/build/town-BOl6hs9B.mjs.map +0 -1
  371. package/.output/server/chunks/build/workspaces-BuydxM-3.mjs.map +0 -1
  372. /package/.output/public/_nuxt/{36oeS8_R2.js → 36oeS8_R.js} +0 -0
  373. /package/.output/public/_nuxt/{CYWVXZTS.js → CYWVXZTS2.js} +0 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * ParamLimits —— 工艺参数写入限界的分层解析与联锁断言(策略层)。
3
+ *
4
+ * 分层限界(层层收窄,任一层越界即拒;有效限界 = 各生效层交集):
5
+ * 1. node 节点工艺安全量程(结构性硬边界,驱动层 validateEng 结构性兜底,不可旁路)
6
+ * 2. param 工艺参数基准限界(常驻边界,配置在参数映射上)
7
+ * 3. product 产品限界(该产线活动批次产品的 paramLimits[paramKey];换产品即换边界)
8
+ * 4. recipe 配方窗口(活动批次配方对该执行节点的 param.min/max;开跑期间生效)
9
+ *
10
+ * 四路写路径(手动 REST / Agent 工具 / 配方下发 / 回退)共用 DcwController.write
11
+ * 咽喉点 → 断言在咽喉点单点生效,用户与 Agent 一体约束,不存在旁路来源。
12
+ * D8 基准消融:includeSoft=false 时仅保留 node 结构层(软联锁消融臂语义)。
13
+ */
14
+
15
+ import { AppError, ErrorCodes } from '../../../utils/errors'
16
+ import type { ParamLimitLayer, ParamLimitsBreakdown } from '../../../../shared/dcw-protocol'
17
+ import type { DcwNode } from './dcw-node'
18
+ import { getDcwParamRepo } from './param-map.repo'
19
+ import { getDcwProductRepo } from './dcw-product.repo'
20
+ import { getDcwRecipeRepo } from './dcw-recipe.repo'
21
+ import { getActiveLineRun } from './line-run'
22
+
23
+ export interface LimitResolveOpts {
24
+ /** false = 仅结构层(消融臂/展示结构层);缺省 true = 全层 */
25
+ includeSoft?: boolean
26
+ /** 跳过配方窗口层(配方下发路径传 true:下发值已在配方保存时对自身窗口校验,
27
+ * 且中途应用新配方不应被旧批次配方窗口误伤;手动/Agent 写恒受活动配方窗口约束) */
28
+ skipRecipe?: boolean
29
+ }
30
+
31
+ /** 解析某执行节点当前生效的限界层(node 层恒在;软层按配置/运行态可有可无) */
32
+ export function limitLayersOf(node: DcwNode, opts: LimitResolveOpts = {}): ParamLimitLayer[] {
33
+ const includeSoft = opts.includeSoft !== false
34
+ const layers: ParamLimitLayer[] = [{
35
+ layer: 'node',
36
+ label: '节点安全量程',
37
+ min: node.min,
38
+ max: node.max,
39
+ }]
40
+ if (!includeSoft) return layers
41
+
42
+ const paramRepo = getDcwParamRepo()
43
+ const param = paramRepo.byNode(node.id)
44
+ const paramKey = param?.key ?? null
45
+ if (param && (param.min != null || param.max != null)) {
46
+ layers.push({
47
+ layer: 'param',
48
+ label: `工艺参数「${param.key}」基准限界`,
49
+ min: param.min,
50
+ max: param.max,
51
+ })
52
+ }
53
+
54
+ const run = getActiveLineRun(node.lineId)
55
+ if (!run) return layers
56
+
57
+ if (paramKey) {
58
+ const product = getDcwProductRepo().byId(run.productId)
59
+ const range = product?.paramLimits?.[paramKey]
60
+ if (product && range && (range.min != null || range.max != null)) {
61
+ layers.push({
62
+ layer: 'product',
63
+ label: `产品「${product.name}」限界`,
64
+ min: range.min ?? null,
65
+ max: range.max ?? null,
66
+ })
67
+ }
68
+ }
69
+
70
+ const recipe = getDcwRecipeRepo().byId(run.recipeId)
71
+ const recipeParam = recipe?.params.find(p => p.nodeId === node.id)
72
+ if (!opts.skipRecipe && recipe && recipeParam && (recipeParam.min != null || recipeParam.max != null)) {
73
+ layers.push({
74
+ layer: 'recipe',
75
+ label: `配方「${recipe.name}」工艺窗口`,
76
+ min: recipeParam.min ?? null,
77
+ max: recipeParam.max ?? null,
78
+ })
79
+ }
80
+ return layers
81
+ }
82
+
83
+ /** 各层交集(最紧有效限界;node 层恒在,恒有值) */
84
+ export function intersectLayers(layers: ParamLimitLayer[]): { min: number, max: number } {
85
+ let min = Number.NEGATIVE_INFINITY
86
+ let max = Number.POSITIVE_INFINITY
87
+ for (const l of layers) {
88
+ if (l.min != null && l.min > min) min = l.min
89
+ if (l.max != null && l.max < max) max = l.max
90
+ }
91
+ if (!Number.isFinite(min) || !Number.isFinite(max)) {
92
+ // 结构层缺失属编程错误(节点构造恒有量程);兜底全开域避免误杀写路径
93
+ return { min: Number.isFinite(min) ? min : -Number.MAX_VALUE, max: Number.isFinite(max) ? max : Number.MAX_VALUE }
94
+ }
95
+ return { min, max }
96
+ }
97
+
98
+ /** 有效限界全量剖面(参数台账/前端展示/Agent 语义回包共用) */
99
+ export function limitsBreakdownOf(node: DcwNode, opts: LimitResolveOpts = {}): ParamLimitsBreakdown {
100
+ const param = getDcwParamRepo().byNode(node.id)
101
+ const layers = limitLayersOf(node, opts)
102
+ return {
103
+ nodeId: node.id,
104
+ paramId: param?.id ?? null,
105
+ paramKey: param?.key ?? null,
106
+ layers,
107
+ effective: intersectLayers(layers),
108
+ }
109
+ }
110
+
111
+ /**
112
+ * 写入联锁断言:逐层校验,越界即 400,错误消息点名层来源与可用区间。
113
+ * 在 DcwController.write 咽喉点调用 —— 手动/Agent/配方/回退四路一体约束。
114
+ */
115
+ export function assertWithinLimits(node: DcwNode, eng: number, opts: LimitResolveOpts = {}): void {
116
+ if (!Number.isFinite(eng)) {
117
+ throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `设定值需为数字(当前: ${String(eng)})`)
118
+ }
119
+ const layers = limitLayersOf(node, opts)
120
+ const paramKey = getDcwParamRepo().byNode(node.id)?.key ?? null
121
+ const unit = node.unit ?? ''
122
+ const { min, max } = intersectLayers(layers)
123
+ for (const l of layers) {
124
+ if (l.min == null && l.max == null) continue
125
+ if (l.min != null && eng < l.min) {
126
+ throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `${violatedText(l, '低于下限', eng, unit, paramKey)}(当前有效写入区间 ${min}~${max}${unit};各层限界按安全规约取交集,越界写入已拒绝)`)
127
+ }
128
+ if (l.max != null && eng > l.max) {
129
+ throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `${violatedText(l, '超出上限', eng, unit, paramKey)}(当前有效写入区间 ${min}~${max}${unit};各层限界按安全规约取交集,越界写入已拒绝)`)
130
+ }
131
+ }
132
+ }
133
+
134
+ function violatedText(l: ParamLimitLayer, dir: string, eng: number, unit: string, paramKey: string | null): string {
135
+ const bound = dir === '低于下限' ? l.min : l.max
136
+ const subject = l.layer === 'node'
137
+ ? '节点工艺安全量程'
138
+ : l.layer === 'param'
139
+ ? `工艺参数「${paramKey ?? ''}」基准限界`
140
+ : l.label + (paramKey ? `(工艺参数 ${paramKey})` : '')
141
+ return `设定值 ${eng}${unit} ${dir} ${subject} 的 ${bound}${unit} —— 约束层:${l.label}`
142
+ }
@@ -0,0 +1,305 @@
1
+ /**
2
+ * DcwParamRepo —— 工艺参数映射仓库(data/dcw-params.json)。
3
+ *
4
+ * 工艺参数映射 = 「工艺参数(语义面) → 写控制执行节点(PLC 面)」的显式绑定:
5
+ * 用户/Agent 只面向参数(key/单位/工程量)读写,寄存器/数据类型/字节序等 PLC
6
+ * 细节全部封装在执行节点驱动配置内(单一事实源,参数面不透出)。
7
+ *
8
+ * 生命周期:节点创建即自动生成同名映射(开箱即得的参数面,ensureForNode);
9
+ * 节点删除级联清理映射(removeForNode);映射删除不影响节点执行能力。
10
+ * lineId 不落盘 —— 派生自执行节点(节点换线参数自动跟随,单一事实源)。
11
+ */
12
+
13
+ import { join } from 'node:path'
14
+ import { randomUUID } from 'node:crypto'
15
+ import type { DcwNodeView, DcwParamInput, DcwParamView } from '../../../../shared/dcw-protocol'
16
+ import { ensureDataDir } from '@/shared/config/home.mjs'
17
+ import { AppError, ErrorCodes } from '../../../utils/errors'
18
+ import { createLogger } from '../logger'
19
+ import { loadJsonFile, saveJsonFileAtomic } from '../json-store.mjs'
20
+ import { getDcwNodeRepo } from './dcw-node.repo'
21
+ import type { DcwNode } from './dcw-node'
22
+
23
+ const log = createLogger('dcw.param-repo')
24
+
25
+ // 配置根 .AgentWorkShop/data(ensureDataDir 自动迁移旧 cwd/server/data 位置)
26
+ const DB_PATH = join(ensureDataDir(), 'dcw-params.json')
27
+
28
+ interface DcwParamRow {
29
+ id: string
30
+ /** 参数键(执行节点所属产线内唯一) */
31
+ key: string
32
+ name: string
33
+ templateRef: string
34
+ unit: string
35
+ decimals: number
36
+ /** 基准写入限界(null = 该侧不约束) */
37
+ min: number | null
38
+ max: number | null
39
+ /** 映射目标:写控制执行节点 */
40
+ nodeId: string
41
+ /** 标准转换模式摘要(配置期选定;运行期换算以执行节点驱动配置为单一事实源) */
42
+ conversion?: DcwParamView['conversion']
43
+ createdAt: string
44
+ }
45
+
46
+ function load(): DcwParamRow[] {
47
+ try {
48
+ const parsed = loadJsonFile(DB_PATH, null)
49
+ return Array.isArray(parsed) ? parsed as DcwParamRow[] : []
50
+ }
51
+ catch {
52
+ return []
53
+ }
54
+ }
55
+
56
+ /** 参数键规范化:去空白;空串拒绝 */
57
+ const normKey = (key: unknown): string => String(key ?? '').trim()
58
+
59
+ class DcwParamRepo {
60
+ private list: DcwParamRow[] = load()
61
+
62
+ all(): DcwParamRow[] {
63
+ return this.list
64
+ }
65
+
66
+ byId(id: string): DcwParamRow | undefined {
67
+ return this.list.find(p => p.id === id)
68
+ }
69
+
70
+ byNode(nodeId: string): DcwParamRow | undefined {
71
+ return this.list.find(p => p.nodeId === nodeId)
72
+ }
73
+
74
+ /** 按 key 检索(可能跨产线同名;返回全部命中) */
75
+ byKey(key: string): DcwParamRow[] {
76
+ const k = normKey(key)
77
+ return this.list.filter(p => p.key === k)
78
+ }
79
+
80
+ /** key 在某产线内是否已被占用(经执行节点的产线归属判定) */
81
+ keyTakenOnLine(key: string, lineId: string, excludeId?: string): boolean {
82
+ const k = normKey(key)
83
+ const nodeRepo = getDcwNodeRepo()
84
+ return this.list.some((p) => {
85
+ if (p.id === excludeId || p.key !== k) return false
86
+ return (nodeRepo.byId(p.nodeId)?.lineId ?? '') === lineId
87
+ })
88
+ }
89
+
90
+ /** 产线内唯一化 key:base / base-2 / base-3 … */
91
+ uniqueKeyOnLine(base: string, lineId: string): string {
92
+ const root = normKey(base) || 'param'
93
+ if (!this.keyTakenOnLine(root, lineId)) return root
94
+ for (let i = 2; ; i++) {
95
+ const candidate = `${root}-${i}`
96
+ if (!this.keyTakenOnLine(candidate, lineId)) return candidate
97
+ }
98
+ }
99
+
100
+ /**
101
+ * 创建映射。校验:执行节点存在;templateRef 可解析(缺省继承节点);
102
+ * key 产线内唯一(缺省继承模板 key,冲突自动加后缀);min<=max。
103
+ */
104
+ create(input: DcwParamInput): DcwParamRow {
105
+ const nodeRepo = getDcwNodeRepo()
106
+ const nodeId = String(input.nodeId ?? '').trim()
107
+ if (!nodeId) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, 'nodeId 必填:工艺参数映射必须指向一个写控制执行节点')
108
+ const node = nodeRepo.byId(nodeId)
109
+ if (!node) throw new AppError(404, ErrorCodes.NOT_FOUND, `执行节点不存在: ${nodeId}`)
110
+ const templateRef = String(input.templateRef ?? node.templateRef ?? '').trim()
111
+ if (!templateRef) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, 'templateRef 必填:工艺参数需绑定语义模板')
112
+ const lineId = node.lineId
113
+ const key = input.key == null
114
+ ? this.uniqueKeyOnLine(node.templateKey, lineId)
115
+ : (() => {
116
+ const k = normKey(input.key)
117
+ if (!k) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, '参数键 key 不可为空')
118
+ if (this.keyTakenOnLine(k, lineId)) throw new AppError(409, ErrorCodes.CONFLICT, `参数键「${k}」在本产线已被占用(产线内唯一)`)
119
+ return k
120
+ })()
121
+ const { min, max } = normRange(input.min, input.max, key)
122
+ const row: DcwParamRow = {
123
+ id: `pp-${randomUUID().slice(0, 8)}`,
124
+ key,
125
+ name: String(input.name ?? node.name ?? key).trim() || key,
126
+ templateRef,
127
+ unit: String(input.unit ?? node.unit ?? '').trim(),
128
+ decimals: clampDecimals(input.decimals ?? node.decimals),
129
+ min,
130
+ max,
131
+ nodeId,
132
+ createdAt: new Date().toISOString(),
133
+ }
134
+ if (input.conversion) row.conversion = input.conversion
135
+ this.list.push(row)
136
+ this.flush()
137
+ return row
138
+ }
139
+
140
+ update(id: string, patch: DcwParamInput): DcwParamRow {
141
+ const row = this.byId(id)
142
+ if (!row) throw new AppError(404, ErrorCodes.NOT_FOUND, `工艺参数映射不存在: ${id}`)
143
+ const node = getDcwNodeRepo().byId(row.nodeId)
144
+ const lineId = node?.lineId ?? ''
145
+ if (patch.key !== undefined) {
146
+ const k = normKey(patch.key)
147
+ if (!k) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, '参数键 key 不可为空')
148
+ if (this.keyTakenOnLine(k, lineId, row.id)) throw new AppError(409, ErrorCodes.CONFLICT, `参数键「${k}」在本产线已被占用(产线内唯一)`)
149
+ row.key = k
150
+ }
151
+ if (patch.name !== undefined) row.name = String(patch.name).trim() || row.key
152
+ if (patch.templateRef !== undefined) {
153
+ const t = String(patch.templateRef).trim()
154
+ if (!t) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, 'templateRef 不可为空')
155
+ row.templateRef = t
156
+ }
157
+ if (patch.unit !== undefined) row.unit = String(patch.unit).trim()
158
+ if (patch.decimals !== undefined) row.decimals = clampDecimals(patch.decimals)
159
+ if (patch.min !== undefined || patch.max !== undefined) {
160
+ const { min, max } = normRange(
161
+ patch.min === undefined ? row.min : patch.min,
162
+ patch.max === undefined ? row.max : patch.max,
163
+ row.key,
164
+ )
165
+ row.min = min
166
+ row.max = max
167
+ }
168
+ if (patch.conversion !== undefined) {
169
+ if (patch.conversion == null) delete row.conversion
170
+ else row.conversion = patch.conversion
171
+ }
172
+ // nodeId 映射目标不可变:换绑请删建(避免账本/限界层悬空引用)
173
+ this.flush()
174
+ return row
175
+ }
176
+
177
+ remove(id: string): boolean {
178
+ const before = this.list.length
179
+ this.list = this.list.filter(p => p.id !== id)
180
+ if (this.list.length !== before) {
181
+ this.flush()
182
+ return true
183
+ }
184
+ return false
185
+ }
186
+
187
+ removeForNode(nodeId: string): boolean {
188
+ const before = this.list.length
189
+ this.list = this.list.filter(p => p.nodeId !== nodeId)
190
+ if (this.list.length !== before) {
191
+ this.flush()
192
+ return true
193
+ }
194
+ return false
195
+ }
196
+
197
+ /**
198
+ * 节点创建即自动生成参数映射(开箱即得的参数面)。防御性:任何失败只记日志
199
+ * 不阻断节点创建(节点是执行主体,映射面可事后手工补建)。
200
+ */
201
+ ensureForNode(node: DcwNode): DcwParamRow | undefined {
202
+ try {
203
+ const existing = this.byNode(node.id)
204
+ if (existing) return existing
205
+ const row: DcwParamRow = {
206
+ id: `pp-${randomUUID().slice(0, 8)}`,
207
+ key: this.uniqueKeyOnLine(node.templateKey, node.lineId),
208
+ name: node.name,
209
+ templateRef: node.templateRef,
210
+ unit: node.unit,
211
+ decimals: node.decimals,
212
+ min: null,
213
+ max: null,
214
+ nodeId: node.id,
215
+ createdAt: new Date().toISOString(),
216
+ }
217
+ this.list.push(row)
218
+ this.flush()
219
+ return row
220
+ }
221
+ catch (err) {
222
+ log.error('[dcw-param] 节点参数面自动生成失败(不影响节点):', err)
223
+ return undefined
224
+ }
225
+ }
226
+
227
+ /** 视图投影(联接执行节点;节点已删除的悬空行过滤返回 null) */
228
+ viewOf(row: DcwParamRow): DcwParamView | null {
229
+ const node = getDcwNodeRepo().byId(row.nodeId)
230
+ if (!node) return null
231
+ return this.project(row, node.toView())
232
+ }
233
+
234
+ listViews(): DcwParamView[] {
235
+ const nodeRepo = getDcwNodeRepo()
236
+ const views = new Map(nodeRepo.all().map(n => [n.id, n.toView()]))
237
+ return this.list
238
+ .map(p => views.get(p.nodeId) ? this.project(p, views.get(p.nodeId)!) : null)
239
+ .filter((v): v is DcwParamView => v != null)
240
+ }
241
+
242
+ private project(row: DcwParamRow, node: DcwNodeView): DcwParamView {
243
+ return {
244
+ id: row.id,
245
+ key: row.key,
246
+ name: row.name,
247
+ templateRef: row.templateRef,
248
+ unit: row.unit || node.unit,
249
+ decimals: row.decimals,
250
+ min: row.min,
251
+ max: row.max,
252
+ nodeId: row.nodeId,
253
+ lineId: node.lineId,
254
+ driver: node.driver,
255
+ enabled: node.enabled,
256
+ value: node.value,
257
+ readValue: node.readValue,
258
+ state: node.state,
259
+ conversion: row.conversion,
260
+ createdAt: row.createdAt,
261
+ }
262
+ }
263
+
264
+ private flush(): void {
265
+ try {
266
+ saveJsonFileAtomic(DB_PATH, this.list)
267
+ }
268
+ catch (err) {
269
+ log.error('[dcw-param] 落盘失败:', err)
270
+ }
271
+ }
272
+ }
273
+
274
+ /** 限界规范化:有限数字或 null;min<=max */
275
+ function normRange(min: unknown, max: unknown, label: string): { min: number | null, max: number | null } {
276
+ const nOf = (v: unknown): number | null => {
277
+ if (v == null || v === '') return null
278
+ const n = Number(v)
279
+ if (!Number.isFinite(n)) throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `工艺参数「${label}」限界需为数字或空(当前: ${String(v)})`)
280
+ return n
281
+ }
282
+ const lo = nOf(min)
283
+ const hi = nOf(max)
284
+ if (lo != null && hi != null && lo > hi) {
285
+ throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `工艺参数「${label}」限界非法:min ${lo} > max ${hi}`)
286
+ }
287
+ return { min: lo, max: hi }
288
+ }
289
+
290
+ function clampDecimals(v: unknown): number {
291
+ const d = Number(v)
292
+ if (!Number.isInteger(d) || d < 0 || d > 6) {
293
+ throw new AppError(400, ErrorCodes.VALIDATION_ERROR, `decimals 必须为 0..6 的整数(当前: ${String(v)})`)
294
+ }
295
+ return d
296
+ }
297
+
298
+ const g = globalThis as typeof globalThis & { __dcwParamRepo?: DcwParamRepo }
299
+
300
+ export function getDcwParamRepo(): DcwParamRepo {
301
+ g.__dcwParamRepo ??= new DcwParamRepo()
302
+ return g.__dcwParamRepo
303
+ }
304
+
305
+ export type { DcwParamRow }
@@ -108,7 +108,9 @@ export class RecipeRollBackManager {
108
108
  * 返回 anchorId/recordId 供工具回包;内部异常不外抛(记账失败不影响写结果)。
109
109
  */
110
110
  afterWrite(
111
- node: { id: string, name: string, lineId: string, templateRef: string },
111
+ // unit 参与入册摘要(下方 `node.unit ?? ''`);调用方传的是 DcwNode 实例(unit: string),
112
+ // 原先这里漏声明该字段,导致实现读到了参数类型之外的属性
113
+ node: { id: string, name: string, lineId: string, templateRef: string, unit?: string | null },
112
114
  eng: number,
113
115
  prevValue: number | null,
114
116
  meta: DcwWriteMeta & { rollbackOf?: string },
@@ -535,12 +537,16 @@ export class RecipeRollBackManager {
535
537
  values.push(v)
536
538
  }
537
539
  const win = recipe?.daqWindows?.find(w => w.nodeId === ch.daqNodeId)
538
- let breaches = win && (win.min != null || win.max != null) ? 0 : -1
539
- for (const v of values) {
540
- if (breaches === -1)
541
- break
542
- if ((win.min != null && v < win.min) || (win.max != null && v > win.max))
543
- breaches++
540
+ // 「有窗口且设了边界」才计越界:原实现用 breaches=-1 + 循环首拍 break 短路,
541
+ // 等价于整段跳过(循环体在 -1 时没有任何副作用)。把这条不变量显式上移,
542
+ // win 在循环内即可被收窄(不再需要断言/非空断言)。
543
+ const windowed = win != null && (win.min != null || win.max != null)
544
+ let breaches = windowed ? 0 : -1
545
+ if (win && windowed) {
546
+ for (const v of values) {
547
+ if ((win.min != null && v < win.min) || (win.max != null && v > win.max))
548
+ breaches++
549
+ }
544
550
  }
545
551
  ch.latest = values.length ? values[values.length - 1]! : null
546
552
  ch.avg = values.length ? Number((values.reduce((a, b) => a + b, 0) / values.length).toFixed(2)) : null
@@ -137,6 +137,10 @@ export interface TaskEngine {
137
137
  queueViewOf(channelId: string, agentId: string): AgentTaskQueueView
138
138
  /** 批量队列视图(一次查询聚合全 channel;调度快照热路径) */
139
139
  queueViewsOf(channelId: string): Map<string, AgentTaskQueueView>
140
+ /** 批量队列视图 lite 版(元数据投影,免 artifacts/history 大列解析;调度快照热路径) */
141
+ queueViewsOfLite(channelId: string): Map<string, AgentTaskQueueView>
142
+ /** 任务列表 lite 版(元数据投影;调度决策/规则引擎仅消费 id/state/parent/进度/标题) */
143
+ listLite(channelId: string): WorkspaceTask[]
140
144
  }
141
145
 
142
146
  /**
@@ -332,7 +332,12 @@ export class AgentChannelManager {
332
332
  /** agent 最近一次工具 invoke 时刻(调度器停滞看门狗的活性源;工具调用即健康推进) */
333
333
  private readonly lastToolInvokeAt = new Map<string, number>()
334
334
 
335
- constructor(private deps: ManagerDeps) {
335
+ /** 依赖集(repos/implFactory/db)。
336
+ * API 作业面(ws/agent-tools/a2a card/…)需要直接读 repo —— 原先只能靠
337
+ * `(manager as unknown as { deps })` 断言绕过 private。这里改成 public readonly:
338
+ * TS 的 private 是纯类型层(擦除后运行时无差异),readonly 保证不可重绑定,
339
+ * 既去掉断言又保持封装语义(依赖注入本身就是本类的构造契约)。 */
340
+ constructor(readonly deps: ManagerDeps) {
336
341
  // 记忆衰减清理定时器(失败只记日志,绝不抛出;unref 不阻进程退出;非法/非正 env 回退默认)
337
342
  this.memoryTimer = setInterval(() => {
338
343
  try {
@@ -2510,7 +2515,7 @@ export class AgentChannelManager {
2510
2515
  * 用途:校验「Agent ↔ 工业节点绑定」的主体 —— 运行时持绑定做鉴权的是**成员实例**,
2511
2516
  * 不是 Agent 模板;绑到模板 id 上会得到一条永远不生效的静默绑定(实测踩过)。
2512
2517
  */
2513
- findChannelAgentById(channelAgentId: string): { id: string, channelId: string, templateId: string } | undefined {
2518
+ findChannelAgentById(channelAgentId: string): { id: string, channelId: string, templateId: string | null } | undefined {
2514
2519
  const row = this.deps.repos.channelAgents.findById(channelAgentId)
2515
2520
  return row ? { id: row.id, channelId: row.channelId, templateId: row.templateId } : undefined
2516
2521
  }
@@ -2640,6 +2645,8 @@ export class AgentChannelManager {
2640
2645
  name: string
2641
2646
  description: string
2642
2647
  leadName: string | null
2648
+ /** 团队共享域知识量(实现体第 2661 行已产出;原声明漏写 → 与 AgentWorkspace.listOtherTeams 漂移) */
2649
+ sharedMemories: number
2643
2650
  activeTasks: Array<{ id: string, title: string, state: string }>
2644
2651
  recentCompleted: Array<{ title: string }>
2645
2652
  }> {
@@ -54,8 +54,10 @@ export function settingOf(key: string): unknown {
54
54
  return effective()[key]
55
55
  }
56
56
 
57
- /** 聚合组(前缀匹配;dotted 键 → 平铺键名,如 memory.primer_tokens → { primer_tokens }) */
58
- function section<G extends string>(group: string): G {
57
+ /** 聚合组(前缀匹配;dotted 键 → 平铺键名,如 memory.primer_tokens → { primer_tokens })
58
+ * G 是调用方声明的**设置组对象类型**(如 MemorySettings):本函数只负责按前缀聚合,
59
+ * 不校验形状,故约束为 object 而非 string —— 原约束 'string' 与全部调用点都不相容。 */
60
+ function section<G extends object>(group: string): G {
59
61
  const out: Record<string, unknown> = {}
60
62
  const prefix = `${group}.`
61
63
  for (const [k, v] of Object.entries(effective())) {
@@ -0,0 +1,49 @@
1
+ /**
2
+ * pg 的最小环境声明(补第三方**缺失的类型**,不是屏蔽错误)。
3
+ *
4
+ * 依据(实测 node_modules/pg@8.23.0):
5
+ * - package.json 无 "types"/"typings" 字段,files 只有 lib / esm/SPONSORS.md —— 包内**零 .d.ts**;
6
+ * 仓库也未安装 @types/pg(实测 node_modules/@types 只有 js-yaml / node / three),
7
+ * 所以 `import type { Pool } from 'pg'` 必然 TS7016(隐式 any)。
8
+ * - exports["."].import → ./esm/index.mjs(TS7016 报的就是该文件);运行时导出面见 lib/index.js
9
+ * (Pool / Client / types 等),本文件只声明代码真正用到的成员。
10
+ * - 行值形状由 SQL 与 pg 的 type parser 在运行时决定(timestamptz → Date、float8 → number、
11
+ * count(*) → string),查询期无法静态得知 —— 与官方 @types/pg 一致,用索引签名表达"逐查询动态";
12
+ * 需要窄化的查询点在调用侧显式断言(见 timescale.adapter.ts queryTagged 的 rows as Array<{...}>)。
13
+ *
14
+ * 覆盖范围:server/services/workshop/daq/storage/timescale.adapter.ts 的用法
15
+ * (Pool 构造 / query(sql[, values]) / connect()→query+release / end())。新增用法时按需扩展。
16
+ */
17
+ declare module 'pg' {
18
+ /** 行对象形状逐查询动态(官方 @types/pg 同样用索引签名表达) */
19
+ export interface QueryResultRow {
20
+ [column: string]: any
21
+ }
22
+
23
+ export interface QueryResult<R extends QueryResultRow = QueryResultRow> {
24
+ rows: R[]
25
+ rowCount: number | null
26
+ command: string
27
+ }
28
+
29
+ export interface PoolConfig {
30
+ connectionString?: string
31
+ max?: number
32
+ min?: number
33
+ idleTimeoutMillis?: number
34
+ connectionTimeoutMillis?: number
35
+ }
36
+
37
+ /** 池借出的连接:复合语句事务(BEGIN/COMMIT/ROLLBACK)与参数化查询 */
38
+ export interface PoolClient {
39
+ query<R extends QueryResultRow = QueryResultRow>(text: string, values?: unknown[]): Promise<QueryResult<R>>
40
+ release(err?: Error | boolean): void
41
+ }
42
+
43
+ export class Pool {
44
+ constructor(config?: PoolConfig)
45
+ query<R extends QueryResultRow = QueryResultRow>(text: string, values?: unknown[]): Promise<QueryResult<R>>
46
+ connect(): Promise<PoolClient>
47
+ end(): Promise<void>
48
+ }
49
+ }
@@ -28,7 +28,18 @@
28
28
  export const SOCKET_NOISE = /ECONNRESET|EPIPE|ECONNABORTED|ETIMEDOUT|ERR_STREAM_PREMATURE_CLOSE|ERR_STREAM_WRITE_AFTER_END/
29
29
 
30
30
  /** 外部设备/上游不可达(运维常态,非程序缺陷) */
31
- export const UPSTREAM_UNREACHABLE = /ECONNREFUSED|ENOTFOUND|EHOSTUNREACH|ENETUNREACH|ERR_SOCKET_CONNECTION_TIMEOUT|UND_ERR_CONNECT_TIMEOUT/
31
+ export const UPSTREAM_UNREACHABLE = /ECONNREFUSED|ENOTFOUND|EHOSTUNREACH|ENETUNREACH|ERR_SOCKET_CONNECTION_TIMEOUT|UND_ERR_CONNECT_TIMEOUT/i
32
+
33
+ /**
34
+ * 人类可读的"设备不可达"文本兜底。
35
+ *
36
+ * 修复动因(实测事故):部分工业驱动库(Modbus/OPC UA 客户端)在链路超时时
37
+ * **不抛 errno code**,只给可读 message("TCP Connection Timed Out")。
38
+ * 旧实现只认 errno token / ERR_SOCKET_CONNECTION_TIMEOUT,该 message 一路落到
39
+ * `fatal` → 整个生产实例 exit 1(实测 13 分钟内即发生一次),与守卫自述的
40
+ * "设备下电/模拟器未启不该带走平台"直接矛盾。
41
+ */
42
+ export const UPSTREAM_UNREACHABLE_TEXT = /TCP Connection Timed Out|connection timed out|connect(?:ion)? timeout|socket hang ?up/i
32
43
 
33
44
  /** SQLite 忙/锁(hardening ST-1) */
34
45
  export const SQLITE_BUSY = /SQLITE_BUSY|SQLITE_LOCKED|database is locked/i
@@ -58,7 +69,7 @@ export function classifyFatalCandidate(reason: unknown): FatalSeverity {
58
69
 
59
70
  if (SOCKET_NOISE.test(text)) return 'socket'
60
71
  if (SQLITE_BUSY.test(text)) return 'db-busy'
61
- if (UPSTREAM_UNREACHABLE.test(text)) return 'upstream'
72
+ if (UPSTREAM_UNREACHABLE.test(text) || UPSTREAM_UNREACHABLE_TEXT.test(text)) return 'upstream'
62
73
  // 引擎边界只看首行:避免 stack 里偶然出现的引擎名把真实错误降级
63
74
  const head = describeReason(reason).split('\n')[0] ?? ''
64
75
  if (ENGINE_BOUNDARY.test(head)) return 'engine'
@@ -47,12 +47,31 @@ export function defaultPluginGroupId(pluginName) {
47
47
 
48
48
  const ID_RE = /^[A-Za-z0-9_-]{1,48}$/
49
49
 
50
+ /**
51
+ * 配置分组(与 server/services/system-config.ts 的 ConfigGroup 同形的数据形状)。
52
+ * order 可为空:持久化条目/插件声明未写 order 时由 mergeGroups 沿用内置声明顺序兜底,
53
+ * 故这里如实声明为可选(消费侧一律按「可能没有 order」处理)。
54
+ * @typedef {object} ConfigGroupDef
55
+ * @property {string} id
56
+ * @property {string} label
57
+ * @property {string} [labelKey]
58
+ * @property {string} description
59
+ * @property {number} [order]
60
+ * @property {boolean} collapsed
61
+ * @property {boolean} collapsible
62
+ * @property {string} icon
63
+ * @property {'builtin' | 'user' | 'plugin'} source
64
+ * @property {string} [plugin] source=plugin 时的插件名
65
+ */
66
+
50
67
  /**
51
68
  * 规范化分组定义。
52
69
  * order 只在「显式给出」或调用方提供 fallbackOrder(新建分组时)才落值 ——
53
70
  * 从磁盘读回的旧条目若没写 order,交给 mergeGroups 沿用内置声明顺序,
54
71
  * 否则会平白跳到列表末尾。
55
- * @returns {{ ok: true, group: object } | { ok: false, error: string }}
72
+ * @param {object} def 待规范化的分组定义(id/label/description/order/collapsed/collapsible/icon/labelKey)
73
+ * @param {{ source?: 'builtin' | 'user' | 'plugin', plugin?: string | null, fallbackOrder?: number }} [opts]
74
+ * @returns {{ ok: true, group: ConfigGroupDef } | { ok: false, error: string }}
56
75
  */
57
76
  export function normalizeGroup(def, { source = 'user', plugin = null, fallbackOrder } = {}) {
58
77
  const d = def ?? {}
@@ -101,6 +120,7 @@ export function saveGroups(groups, groupsPath) {
101
120
  return payload
102
121
  }
103
122
 
123
+ /** @returns {ConfigGroupDef[]} 规范化后的持久化分组(非法条目直接丢弃) */
104
124
  export function readGroups(groupsPath) {
105
125
  if (!existsSync(groupsPath)) return []
106
126
  const out = []
@@ -141,6 +161,7 @@ export function builtinGroupOrder(descriptors = []) {
141
161
  * @param {Array} o.persisted 持久化分组(含管理员定制)
142
162
  * @param {Array} o.pluginGroups 本轮插件声明的分组
143
163
  * @param {Map<string,string>} [o.pluginLabelById] 插件名 → 展示名(用于自动生成组标题)
164
+ * @returns {ConfigGroupDef[]} 有序分组(已按 order、id 排序)
144
165
  */
145
166
  export function mergeGroups({ descriptors = [], persisted = [], pluginGroups = [], pluginLabelById = new Map() } = {}) {
146
167
  const out = new Map()