agentworkshop 0.8.5 → 0.8.6

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 (349) hide show
  1. package/.AgentWorkShop/prompts/host-tools.en.json +3 -3
  2. package/.AgentWorkShop/prompts/host-tools.json +14 -4
  3. package/.output/.AgentWorkShop/prompts/host-tools.en.json +3 -3
  4. package/.output/.AgentWorkShop/prompts/host-tools.json +14 -4
  5. package/.output/nitro.json +1 -1
  6. package/.output/public/_nuxt/{U_n8pryv2.js → 7op-BMk82.js} +1 -1
  7. package/.output/public/_nuxt/{zWZ26UWi.js → B1SIgyrl.js} +1 -1
  8. package/.output/public/_nuxt/{C4tXwpSt.js → BA2lmfW4.js} +1 -1
  9. package/.output/public/_nuxt/{EJkmXP432.js → BDXQqSkl2.js} +1 -1
  10. package/.output/public/_nuxt/{CY_7u1ST2.js → BI9jj13f2.js} +1 -1
  11. package/.output/public/_nuxt/{CGf9neEK2.js → BiXqyYNw2.js} +1 -1
  12. package/.output/public/_nuxt/{D9GoOkyP2.js → BlOm8JIs2.js} +1 -1
  13. package/.output/public/_nuxt/{CQ31GiJU2.js → BoXKUOiN2.js} +1 -1
  14. package/.output/public/_nuxt/{DZzbCFY2.js → BuLoPmcf.js} +1 -1
  15. package/.output/public/_nuxt/{D9ppMCbw.js → BwOK42JQ.js} +1 -1
  16. package/.output/public/_nuxt/{CZJ5cw6r2.js → C1RMfPoB2.js} +1 -1
  17. package/.output/public/_nuxt/{CGviZq4E.js → COfo_Bg3.js} +1 -1
  18. package/.output/public/_nuxt/CTGJHZ8X2.js +1 -0
  19. package/.output/public/_nuxt/{SgsmmrsZ.js → CY2NkFvI.js} +1 -1
  20. package/.output/public/_nuxt/{B4GOTVfN.js → C_4mzQ5X.js} +1 -1
  21. package/.output/public/_nuxt/{DGmFHb7m.js → CdvMP3lv.js} +1 -1
  22. package/.output/public/_nuxt/{iqxZGXsY.js → CooauJ7D.js} +1 -1
  23. package/.output/public/_nuxt/{C3vxePED.js → D1eVi1zr.js} +1 -1
  24. package/.output/public/_nuxt/{DnuJ66Jy.js → D6PUmBb0.js} +1 -1
  25. package/.output/public/_nuxt/{BBhroEeN.js → DC8sMA36.js} +1 -1
  26. package/.output/public/_nuxt/{DOUuGOQR2.js → DDHh85c42.js} +1 -1
  27. package/.output/public/_nuxt/{Cy_HDgSl.js → DDXOfmo2.js} +2 -2
  28. package/.output/public/_nuxt/{Cr2xJ_CY.js → DMFwLxfu.js} +1 -1
  29. package/.output/public/_nuxt/{BZg_MIK42.js → DoprKJ5a2.js} +1 -1
  30. package/.output/public/_nuxt/{BzBBEE-o2.js → Dr1FVeQG2.js} +1 -1
  31. package/.output/public/_nuxt/{W0sNnJQh.js → DtnsTW7e.js} +1 -1
  32. package/.output/public/_nuxt/{gUYi1hLJ2.js → DxDqd6T42.js} +1 -1
  33. package/.output/public/_nuxt/{B0UUODeh.js → DzU3zEKX.js} +1 -1
  34. package/.output/public/_nuxt/{BkO5WdX5.js → EgO70wrE.js} +1 -1
  35. package/.output/public/_nuxt/{7OZQiw002.js → HJKu4eEV2.js} +1 -1
  36. package/.output/public/_nuxt/{ChF6qLpY2.js → K5Xen0wZ2.js} +1 -1
  37. package/.output/public/_nuxt/{CNrRZmSx2.js → Rb4o4o-82.js} +1 -1
  38. package/.output/public/_nuxt/{BqBTlfiz.js → Tf6SJSDj.js} +1 -1
  39. package/.output/public/_nuxt/{DJaGVY4v2.js → ZzOyoXex2.js} +1 -1
  40. package/.output/public/_nuxt/{PbzVdCTA2.js → azOGy_tU2.js} +1 -1
  41. package/.output/public/_nuxt/{D1thFC3z.js → be-4wyTo.js} +1 -1
  42. package/.output/public/_nuxt/builds/latest.json +1 -1
  43. package/.output/public/_nuxt/builds/meta/593b65bd-cf1d-4b7f-a993-57e939c38f36.json +1 -0
  44. package/.output/public/_nuxt/{8b9CKi6F.js → dcjPjLPI.js} +1 -1
  45. package/.output/public/_nuxt/{ae2-5ZPt.js → h-Fn7pRD.js} +1 -1
  46. package/.output/public/_nuxt/{-gy9k_TE2.js → hyLdZsOd2.js} +1 -1
  47. package/.output/public/_nuxt/{BHxG41IQ2.js → o8sYG6xx2.js} +1 -1
  48. package/.output/server/chunks/_/access.mjs +1 -1
  49. package/.output/server/chunks/_/agent-dto.mjs +26 -0
  50. package/.output/server/chunks/_/agent-dto.mjs.map +1 -0
  51. package/.output/server/chunks/_/data-export.mjs +6 -6
  52. package/.output/server/chunks/_/data-query.mjs +3 -3
  53. package/.output/server/chunks/_/eda-run.repo.mjs +2 -2
  54. package/.output/server/chunks/_/features.mjs +5 -5
  55. package/.output/server/chunks/_/golden.mjs +3 -3
  56. package/.output/server/chunks/_/harness-spawn-policy.mjs +77 -0
  57. package/.output/server/chunks/_/harness-spawn-policy.mjs.map +1 -0
  58. package/.output/server/chunks/_/index.mjs +1 -1
  59. package/.output/server/chunks/_/line-scope.mjs +32 -0
  60. package/.output/server/chunks/_/line-scope.mjs.map +1 -0
  61. package/.output/server/chunks/_/mes-console.mjs +1 -1
  62. package/.output/server/chunks/_/nitro.mjs +2429 -1726
  63. package/.output/server/chunks/_/nitro.mjs.map +1 -1
  64. package/.output/server/chunks/_/ocap.mjs +1 -1
  65. package/.output/server/chunks/_/rest-auth.mjs +1 -1
  66. package/.output/server/chunks/_/spc.mjs +1 -1
  67. package/.output/server/chunks/build/TownView-BWMDbosY.mjs +1 -1
  68. package/.output/server/chunks/build/daq-CdxcfY_G.mjs +1 -1
  69. package/.output/server/chunks/build/dcw-BlDQtS4q.mjs +1 -1
  70. package/.output/server/chunks/build/types-D6J7slEa.mjs +1 -1
  71. package/.output/server/chunks/build/useDaqAddNode-D_Rkju0f.mjs +1 -1
  72. package/.output/server/chunks/build/useDaqDetailNode-DeOsDxK5.mjs +1 -1
  73. package/.output/server/chunks/build/useDaqDetailParams-Cm9a9r5G.mjs +1 -1
  74. package/.output/server/chunks/build/useDaqFilters-CctmO03p.mjs +1 -1
  75. package/.output/server/chunks/build/useDaqStream-B9ZBVQrI.mjs +1 -1
  76. package/.output/server/chunks/build/useDaqTemplates-CK37sMfi.mjs +1 -1
  77. package/.output/server/chunks/build/useDcwAddNode-DACVcEHs.mjs +1 -1
  78. package/.output/server/chunks/build/useDcwStream-Dir5ufAU.mjs +1 -1
  79. package/.output/server/chunks/routes/api/mcp/workshop.mjs +1 -1
  80. package/.output/server/chunks/routes/api/system/backup/run.post.mjs +2 -2
  81. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs +17 -9
  82. package/.output/server/chunks/routes/api/workshop/agent-tools/index.post.mjs.map +1 -1
  83. package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs +3 -0
  84. package/.output/server/chunks/routes/api/workshop/agents/_id_.patch.mjs.map +1 -1
  85. package/.output/server/chunks/routes/api/workshop/channel-templates/_id/instantiate.post.mjs +1 -1
  86. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs +6 -0
  87. package/.output/server/chunks/routes/api/workshop/channel-templates/_id_.patch.mjs.map +1 -1
  88. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs +5 -0
  89. package/.output/server/chunks/routes/api/workshop/channels/_id/agents/_agentId_.patch.mjs.map +1 -1
  90. package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs +5 -2
  91. package/.output/server/chunks/routes/api/workshop/channels/_id/index.get.mjs.map +1 -1
  92. package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs +7 -1
  93. package/.output/server/chunks/routes/api/workshop/channels/_id/index.post.mjs.map +1 -1
  94. package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs +3 -1
  95. package/.output/server/chunks/routes/api/workshop/channels/index.get.mjs.map +1 -1
  96. package/.output/server/chunks/routes/api/workshop/channels.post.mjs +8 -6
  97. package/.output/server/chunks/routes/api/workshop/channels.post.mjs.map +1 -1
  98. package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs +29 -49
  99. package/.output/server/chunks/routes/api/workshop/daq/_id/ingest.post.mjs.map +1 -1
  100. package/.output/server/chunks/routes/api/workshop/daq/_id/samples.get.mjs +1 -1
  101. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs +6 -1
  102. package/.output/server/chunks/routes/api/workshop/daq/_id_.patch.mjs.map +1 -1
  103. package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs +4 -2
  104. package/.output/server/chunks/routes/api/workshop/daq/alarms.get.mjs.map +1 -1
  105. package/.output/server/chunks/routes/api/workshop/daq/index2.post.mjs +1 -1
  106. package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs +14 -5
  107. package/.output/server/chunks/routes/api/workshop/daq/infra/reconnect.post.mjs.map +1 -1
  108. package/.output/server/chunks/routes/api/workshop/daq/infra.get.mjs +1 -1
  109. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.delete.mjs +1 -1
  110. package/.output/server/chunks/routes/api/workshop/daq/templates/_key_.patch.mjs +1 -1
  111. package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs +7 -4
  112. package/.output/server/chunks/routes/api/workshop/daq/test-driver.post.mjs.map +1 -1
  113. package/.output/server/chunks/routes/api/workshop/dcw/_id/bind.post.mjs +1 -1
  114. package/.output/server/chunks/routes/api/workshop/dcw/_id/bindings.put.mjs +1 -1
  115. package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs +6 -2
  116. package/.output/server/chunks/routes/api/workshop/dcw/_id/param-ledger.get.mjs.map +1 -1
  117. package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs +6 -2
  118. package/.output/server/chunks/routes/api/workshop/dcw/_id/read.post.mjs.map +1 -1
  119. package/.output/server/chunks/routes/api/workshop/dcw/_id/test.post.mjs +1 -1
  120. package/.output/server/chunks/routes/api/workshop/dcw/_id/write.post.mjs +1 -1
  121. package/.output/server/chunks/routes/api/workshop/dcw/_id_.delete.mjs +1 -1
  122. package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs +6 -1
  123. package/.output/server/chunks/routes/api/workshop/dcw/_id_.patch.mjs.map +1 -1
  124. package/.output/server/chunks/routes/api/workshop/dcw/controller.post.mjs +1 -1
  125. package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs +7 -4
  126. package/.output/server/chunks/routes/api/workshop/dcw/index.get.mjs.map +1 -1
  127. package/.output/server/chunks/routes/api/workshop/dcw/index.post.mjs +1 -1
  128. package/.output/server/chunks/routes/api/workshop/dcw/index2.get.mjs +1 -1
  129. package/.output/server/chunks/routes/api/workshop/dcw/index2.post.mjs +1 -1
  130. package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs +6 -2
  131. package/.output/server/chunks/routes/api/workshop/dcw/index3.get.mjs.map +1 -1
  132. package/.output/server/chunks/routes/api/workshop/dcw/index3.post.mjs +1 -1
  133. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs +7 -4
  134. package/.output/server/chunks/routes/api/workshop/dcw/index4.get.mjs.map +1 -1
  135. package/.output/server/chunks/routes/api/workshop/dcw/index4.post.mjs +1 -1
  136. package/.output/server/chunks/routes/api/workshop/dcw/index5.get.mjs +1 -1
  137. package/.output/server/chunks/routes/api/workshop/dcw/index5.post.mjs +1 -1
  138. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs +6 -2
  139. package/.output/server/chunks/routes/api/workshop/dcw/index6.get.mjs.map +1 -1
  140. package/.output/server/chunks/routes/api/workshop/dcw/journal/node/_nodeId/rollback.post.mjs +1 -1
  141. package/.output/server/chunks/routes/api/workshop/dcw/line/query.get.mjs +1 -1
  142. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/start.post.mjs +1 -1
  143. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id/stop.post.mjs +1 -1
  144. package/.output/server/chunks/routes/api/workshop/dcw/lines/_id_.delete.mjs +1 -1
  145. package/.output/server/chunks/routes/api/workshop/dcw/mes-artifacts/file.get.mjs +6 -2
  146. package/.output/server/chunks/routes/api/workshop/dcw/mes-artifacts/file.get.mjs.map +1 -1
  147. package/.output/server/chunks/routes/api/workshop/dcw/mes-hook-test.post.mjs +1 -1
  148. package/.output/server/chunks/routes/api/workshop/dcw/mes-test-read.post.mjs +1 -1
  149. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/judge.post.mjs +1 -1
  150. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/rollback.post.mjs +1 -1
  151. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs +6 -2
  152. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id/series.get.mjs.map +1 -1
  153. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs +4 -2
  154. package/.output/server/chunks/routes/api/workshop/dcw/optimizations/_id_.get.mjs.map +1 -1
  155. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs +5 -2
  156. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/read.post.mjs.map +1 -1
  157. package/.output/server/chunks/routes/api/workshop/dcw/params/_id/write.post.mjs +1 -1
  158. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.delete.mjs +1 -1
  159. package/.output/server/chunks/routes/api/workshop/dcw/params/_id_.patch.mjs +1 -1
  160. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.delete.mjs +1 -1
  161. package/.output/server/chunks/routes/api/workshop/dcw/products/_id_.patch.mjs +1 -1
  162. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/apply.post.mjs +1 -1
  163. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/rollback-good.post.mjs +1 -1
  164. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/versions.get.mjs +7 -3
  165. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id/versions.get.mjs.map +1 -1
  166. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs +8 -1
  167. package/.output/server/chunks/routes/api/workshop/dcw/recipes/_id_.delete.mjs.map +1 -1
  168. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs +6 -2
  169. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/close.post.mjs.map +1 -1
  170. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/data.get.mjs +1 -1
  171. package/.output/server/chunks/routes/api/workshop/dcw/runs/_id/rollback.post.mjs +1 -1
  172. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.delete.mjs +1 -1
  173. package/.output/server/chunks/routes/api/workshop/dcw/templates/_key_.patch.mjs +1 -1
  174. package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs +7 -4
  175. package/.output/server/chunks/routes/api/workshop/dcw/test-driver.post.mjs.map +1 -1
  176. package/.output/server/chunks/routes/api/workshop/eda/commonality.get.mjs +1 -1
  177. package/.output/server/chunks/routes/api/workshop/eda/vm/predict.post.mjs +1 -1
  178. package/.output/server/chunks/routes/api/workshop/exp/confirmations/_id/decide.post.mjs +1 -1
  179. package/.output/server/chunks/routes/api/workshop/exp/index.get.mjs +1 -1
  180. package/.output/server/chunks/routes/api/workshop/fs/dirs.get.mjs +1 -1
  181. package/.output/server/chunks/routes/api/workshop/harnesses/_id/providers.get.mjs +1 -1
  182. package/.output/server/chunks/routes/api/workshop/harnesses.get.mjs +1 -1
  183. package/.output/server/chunks/routes/api/workshop/hitl/pending.get.mjs +1 -1
  184. package/.output/server/chunks/routes/api/workshop/hitl/respond.post.mjs +1 -1
  185. package/.output/server/chunks/routes/api/workshop/index.mjs +1 -1
  186. package/.output/server/chunks/routes/api/workshop/index.post.mjs +2 -0
  187. package/.output/server/chunks/routes/api/workshop/index.post.mjs.map +1 -1
  188. package/.output/server/chunks/routes/api/workshop/index.put.mjs +1 -1
  189. package/.output/server/chunks/routes/api/workshop/index3.post.mjs +8 -3
  190. package/.output/server/chunks/routes/api/workshop/index3.post.mjs.map +1 -1
  191. package/.output/server/chunks/routes/api/workshop/index5.post.mjs +1 -1
  192. package/.output/server/chunks/routes/api/workshop/index6.get.mjs +1 -1
  193. package/.output/server/chunks/routes/api/workshop/index7.get.mjs +1 -1
  194. package/.output/server/chunks/routes/api/workshop/index7.post.mjs +1 -1
  195. package/.output/server/chunks/routes/api/workshop/mes/datasets.get.mjs +1 -1
  196. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/autofix.mjs +1 -1
  197. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/compile.mjs +1 -1
  198. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/evidence.mjs +1 -1
  199. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/publish.mjs +1 -1
  200. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval-decision.mjs +1 -1
  201. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/approval.mjs +1 -1
  202. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/rules/_ruleId/transition.mjs +1 -1
  203. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/subgraph.mjs +1 -1
  204. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId/validate.mjs +1 -1
  205. package/.output/server/chunks/routes/api/workshop/ontology/drafts/_draftId_.mjs +1 -1
  206. package/.output/server/chunks/routes/api/workshop/ontology/export/_ontologyId_.mjs +1 -1
  207. package/.output/server/chunks/routes/api/workshop/ontology/index.mjs +1 -1
  208. package/.output/server/chunks/routes/api/workshop/ontology/scene.mjs +1 -1
  209. package/.output/server/chunks/routes/api/workshop/ontology/traces/_ontologyId_.mjs +1 -1
  210. package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId/compare.mjs +1 -1
  211. package/.output/server/chunks/routes/api/workshop/ontology/versions/_ontologyId_.mjs +1 -1
  212. package/.output/server/chunks/routes/api/workshop/optimization/capabilities.get.mjs +1 -1
  213. package/.output/server/chunks/routes/api/workshop/optimization/index.get.mjs +1 -1
  214. package/.output/server/chunks/routes/api/workshop/optimization/index.mjs +1 -1
  215. package/.output/server/chunks/routes/api/workshop/optimization/index2.mjs +1 -1
  216. package/.output/server/chunks/routes/api/workshop/optimization/profiles/_id/publish.post.mjs +1 -1
  217. package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/crosscheck.post.mjs +1 -1
  218. package/.output/server/chunks/routes/api/workshop/optimization/recommendations/_id/dispatch.post.mjs +1 -1
  219. package/.output/server/chunks/routes/api/workshop/optimization/solve.post.mjs +1 -1
  220. package/.output/server/chunks/routes/api/workshop/plugins/_name/disable.post.mjs +1 -1
  221. package/.output/server/chunks/routes/api/workshop/plugins/_name/enable.post.mjs +1 -1
  222. package/.output/server/chunks/routes/api/workshop/process-model/datasets.post.mjs +1 -1
  223. package/.output/server/chunks/routes/api/workshop/process-model/eligibility.mjs +1 -1
  224. package/.output/server/chunks/routes/api/workshop/process-model/index.mjs +1 -1
  225. package/.output/server/chunks/routes/api/workshop/process-model/specs/_id/publish.post.mjs +1 -1
  226. package/.output/server/chunks/routes/api/workshop/process-model/train.post.mjs +1 -1
  227. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.delete.mjs +1 -1
  228. package/.output/server/chunks/routes/api/workshop/scene/layouts/_channelId_.put.mjs +1 -1
  229. package/.output/server/chunks/routes/api/workshop/tools/sync.post.mjs +1 -1
  230. package/.output/server/chunks/routes/api/workshop/workspaces/_id/channel-templates/_tplId_.post.mjs +1 -1
  231. package/.output/server/chunks/routes/renderer.mjs +2 -2
  232. package/.output/server/chunks/virtual/entry.mjs +1 -1
  233. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  234. package/.output/server/package.json +1 -1
  235. package/README-zh.md +4 -2
  236. package/README.md +9 -2
  237. package/config.yml +1 -1
  238. package/docker-compose.yml +4 -2
  239. package/docs/cli.en.md +2 -2
  240. package/docs/cli.md +2 -2
  241. package/docs/plugins.en.md +1 -1
  242. package/docs/plugins.md +1 -1
  243. package/docs/sdk.en.md +1 -1
  244. package/docs/sdk.md +1 -1
  245. package/package.json +1 -1
  246. package/scripts/_audit/_live-mqtt-e2e-20261028.mjs +219 -0
  247. package/scripts/_audit/prompt-injection-probe.mts +76 -0
  248. package/scripts/api-live-e2e.mjs +9 -3
  249. package/scripts/e2e-multiexpert-remediation-real.mjs +453 -0
  250. package/scripts/e2e-node-binding-3mode-real.mjs +697 -0
  251. package/scripts/e2e-node-binding-authz.mjs +332 -0
  252. package/scripts/security-acceptance.mjs +218 -0
  253. package/server/api/workshop/agent-tools/bindings/index.post.ts +33 -17
  254. package/server/api/workshop/agents/[id].patch.ts +3 -0
  255. package/server/api/workshop/agents/index.post.ts +3 -0
  256. package/server/api/workshop/channel-templates/[id].patch.ts +5 -0
  257. package/server/api/workshop/channel-templates/index.post.ts +6 -0
  258. package/server/api/workshop/channels/[id]/agents/[agentId].patch.ts +5 -0
  259. package/server/api/workshop/channels/[id]/agents/index.get.ts +8 -2
  260. package/server/api/workshop/channels/[id]/agents/index.post.ts +7 -0
  261. package/server/api/workshop/channels/[id]/index.get.ts +10 -4
  262. package/server/api/workshop/channels.post.ts +3 -0
  263. package/server/api/workshop/daq/[id]/ingest.post.ts +26 -22
  264. package/server/api/workshop/daq/[id].patch.ts +8 -0
  265. package/server/api/workshop/daq/alarms.get.ts +18 -16
  266. package/server/api/workshop/daq/infra/reconnect.post.ts +21 -5
  267. package/server/api/workshop/daq/test-driver.post.ts +10 -3
  268. package/server/api/workshop/dcw/[id]/param-ledger.get.ts +7 -1
  269. package/server/api/workshop/dcw/[id]/read.post.ts +6 -1
  270. package/server/api/workshop/dcw/[id].patch.ts +49 -40
  271. package/server/api/workshop/dcw/journal/index.get.ts +7 -3
  272. package/server/api/workshop/dcw/mes-artifacts/file.get.ts +7 -1
  273. package/server/api/workshop/dcw/mes-artifacts/index.get.ts +7 -1
  274. package/server/api/workshop/dcw/optimizations/[id]/series.get.ts +6 -1
  275. package/server/api/workshop/dcw/optimizations/[id].get.ts +3 -1
  276. package/server/api/workshop/dcw/optimizations/index.get.ts +6 -3
  277. package/server/api/workshop/dcw/params/[id]/read.post.ts +22 -17
  278. package/server/api/workshop/dcw/recipes/[id]/versions.get.ts +20 -14
  279. package/server/api/workshop/dcw/recipes/[id].delete.ts +33 -25
  280. package/server/api/workshop/dcw/recipes/index.get.ts +17 -12
  281. package/server/api/workshop/dcw/runs/[id]/close.post.ts +21 -15
  282. package/server/api/workshop/dcw/test-driver.post.ts +21 -16
  283. package/server/api/workshop/ws/hub.ts +2 -1
  284. package/server/plugins/01-instance-guard.ts +82 -0
  285. package/server/plugins/daq.ts +40 -8
  286. package/server/plugins/workshop.ts +12 -0
  287. package/server/services/workshop/agents/adapters/line-spawn.ts +24 -1
  288. package/server/services/workshop/agents/agent-dto.ts +54 -0
  289. package/server/services/workshop/agents/harness-spawn-policy.ts +114 -0
  290. package/server/services/workshop/agents/host-tool-bridge/dispatch.ts +22 -0
  291. package/server/services/workshop/agents/host-tool-bridge/index.ts +1 -1
  292. package/server/services/workshop/agents/host-tool-bridge/per-agent.ts +17 -3
  293. package/server/services/workshop/agents/host-tool-bridge/tools/tasks.ts +1 -1
  294. package/server/services/workshop/agents/host-tool-bridge/tools/team-delegation.ts +5 -1
  295. package/server/services/workshop/agents/industrial/dcw-tools.ts +6 -2
  296. package/server/services/workshop/agents/industrial/ops-tools.ts +54 -12
  297. package/server/services/workshop/agents/industrial/recipe-gate.ts +68 -15
  298. package/server/services/workshop/agents/industrial/tool-verify-legacy-specs.ts +2 -2
  299. package/server/services/workshop/agents/industrial/tool-verify.ts +31 -2
  300. package/server/services/workshop/agents/node-bindings.repo.ts +4 -3
  301. package/server/services/workshop/agents/node-delegation.ts +49 -4
  302. package/server/services/workshop/agents/tool-approvals.ts +23 -0
  303. package/server/services/workshop/agents/tool-reference-spec.ts +3 -0
  304. package/server/services/workshop/daq/bus/mqtt.adapter.ts +12 -0
  305. package/server/services/workshop/daq/daq-controller/crud.ts +5 -3
  306. package/server/services/workshop/daq/daq-controller/ingest.ts +23 -0
  307. package/server/services/workshop/daq/daq-controller/push.ts +24 -3
  308. package/server/services/workshop/daq/daq-controller/runtime-control.ts +6 -0
  309. package/server/services/workshop/daq/daq-controller/sweep-alarms.ts +3 -1
  310. package/server/services/workshop/daq/daq-controller/tsdb-twin.ts +21 -1
  311. package/server/services/workshop/daq/daq-node.repo.ts +10 -7
  312. package/server/services/workshop/daq/daq-node.ts +2 -0
  313. package/server/services/workshop/daq/daq-runtime.ts +10 -2
  314. package/server/services/workshop/daq/drivers/modbus-tcp.ts +8 -5
  315. package/server/services/workshop/daq/drivers/registry.ts +5 -4
  316. package/server/services/workshop/daq/infra.ts +13 -0
  317. package/server/services/workshop/daq/ingest-policy.ts +24 -1
  318. package/server/services/workshop/daq/storage/timescale.adapter.ts +42 -3
  319. package/server/services/workshop/db/database/migrations.ts +83 -68
  320. package/server/services/workshop/dcw/dcw-controller/crud.ts +27 -2
  321. package/server/services/workshop/dcw/dcw-controller/lines.ts +14 -3
  322. package/server/services/workshop/dcw/dcw-controller/recipes.ts +63 -5
  323. package/server/services/workshop/dcw/dcw-recipe.repo.ts +10 -1
  324. package/server/services/workshop/dcw/dcw-runtime.ts +37 -14
  325. package/server/services/workshop/dcw/drivers/shared.ts +62 -6
  326. package/server/services/workshop/dcw/recipe-op-anchor.ts +28 -0
  327. package/server/services/workshop/dcw/recipe-rollback-manager/rollback.ts +204 -190
  328. package/server/services/workshop/dcw/recipe-rollback-manager/sweep.ts +97 -77
  329. package/server/services/workshop/line-scope.ts +80 -0
  330. package/server/services/workshop/optimization/dispatch.service.ts +18 -2
  331. package/server/services/workshop/permissions.ts +2 -0
  332. package/server/services/workshop/runtime/agent-runtime/message.ts +17 -0
  333. package/server/services/workshop/runtime/agent-runtime/state.ts +9 -3
  334. package/server/services/workshop/runtime/agent-runtime/types.ts +2 -1
  335. package/server/services/workshop/runtime/channel-runtime.ts +3 -1
  336. package/server/services/workshop/runtime/mailbox.ts +6 -3
  337. package/server/services/workshop/runtime/manager/a2a.ts +4 -2
  338. package/server/services/workshop/runtime/manager/admin-channel.ts +1 -1
  339. package/server/services/workshop/runtime/manager/chat.ts +3 -1
  340. package/server/services/workshop/runtime/manager/host-tools.ts +1 -1
  341. package/server/services/workshop/runtime/manager/runtime-wiring.ts +31 -19
  342. package/server/services/workshop/runtime/manager/state.ts +43 -1
  343. package/server/services/workshop/runtime/permission-scope.ts +21 -0
  344. package/server/services/workshop/runtime/scheduler-loop/tick.ts +16 -4
  345. package/server/services/workshop/scene-events.ts +19 -0
  346. package/shared/daq-protocol.ts +24 -5
  347. package/shared/dcw-protocol/catalog.ts +4 -4
  348. package/.output/public/_nuxt/CCI-fwcS2.js +0 -1
  349. package/.output/public/_nuxt/builds/meta/5cd11284-05bf-4fcd-9a69-56a01c9f56dd.json +0 -1
@@ -2,9 +2,10 @@
2
2
  * AgentNodeBindingRepo —— Agent ↔ 工业对象绑定持久化(agent-node-bindings.json)。
3
3
  *
4
4
  * 一个绑定 = 某 Channel 内的 Agent 对某个工业对象的授权:
5
- * - kind: 'daq' = 可查询数采; 'recipe' = 可操作配方(参数写入+下发;nodeId 字段存 recipeId);
6
- * 'dcw' = (已废弃)直写数控节点 —— 新绑定一律拒绝,存量绑定仅保留只读可见性,
7
- * 写路径在工具层全部收敛到 recipe(权限模型 v2:Agent 不直碰节点,只碰配方)。
5
+ * - kind: 'daq' = 可查询数采; 'recipe' = 配方操作权(参数写入+下发+版本;nodeId 字段存 recipeId);
6
+ * 'dcw' = 点位写授权(2026-10-28 恢复,非直写入口):对活动配方的 trial/apply,
7
+ * 覆盖集 ⊆ 节点绑定面即放行;直写工具(param_control/dcw_control)仍恒拒,
8
+ * 写路径 100% 收敛在配方管道(权限模型 v2)。
8
9
  * - mode: 'auto' = 工具调用自动执行; 'manual' = 每次执行需用户批准(可附备注)
9
10
  */
10
11
 
@@ -4,8 +4,10 @@
4
4
  * 语义:lead 把**自己已绑定**的 DAQ/DCW 节点授权给同 Channel 的 worker:
5
5
  * - 节点必须是 leader 本人已有绑定的节点(权限不可超越授予者,hard rule);
6
6
  * - 目标必须是同 Channel 的启用 worker(不能授给 lead 自己/其他频道成员);
7
- * - 绑定逐 (nodeId, kind) 复制 leader 的 kind/tuning;mode 缺省随 leader,
8
- * 显式传 mode 可覆盖(auto↔manual 均允许 —— mode 只决定是否走 HITL,不放大节点面);
7
+ * - 绑定逐 (nodeId, kind) 复制 leader 的 kind/tuning;mode 缺省 **manual**
8
+ * (不继承 leader 的 auto);显式 mode=auto 等于摘掉该 worker 的逐次人工审批闸门,
9
+ * 必须同时携带 confirm:true,并落审计(2026-10-28 审查 B-P1-3:此前委派可无
10
+ * confirm/无审计把 manual 抬成 auto,与 REST 面刻意的不变式矛盾);
9
11
  * - 委派产生的绑定带 grantedByAgentId/grantedAt 溯源;撤销时 leader 只能撤
10
12
  * 「自己授予的」或「自己也持有的」绑定。
11
13
  * 校验全部 fail-closed:任何节点不满足 → 整单拒绝并列出明细,不做部分授予。
@@ -13,6 +15,7 @@
13
15
  import { AppError, ErrorCodes } from '../../../utils/errors'
14
16
  import { getAgentNodeBindingRepo, type AgentNodeBinding, type AgentNodeBindingMode, type AgentNodeBindingKind } from './node-bindings.repo'
15
17
  import { getToolApprovals } from './tool-approvals'
18
+ import { recordOps } from '../ops/ops'
16
19
 
17
20
  export interface DelegationMember {
18
21
  id: string
@@ -27,6 +30,8 @@ export interface DelegationGrantInput {
27
30
  nodeIds: string[]
28
31
  members: DelegationMember[]
29
32
  mode?: AgentNodeBindingMode
33
+ /** mode=auto 的风险确认(严格 === true;与 REST bindings PATCH 同语义) */
34
+ confirm?: unknown
30
35
  }
31
36
 
32
37
  export interface DelegationPlanItem {
@@ -73,6 +78,8 @@ export function planDelegation(input: DelegationGrantInput): DelegationPlan {
73
78
  const missing: string[] = []
74
79
  const grants: DelegationPlanItem[] = []
75
80
  const seen = new Set<string>()
81
+ const repoForExisting = getAgentNodeBindingRepo()
82
+ const autoWithoutConfirm: string[] = []
76
83
  for (const nodeId of nodeIds) {
77
84
  if (seen.has(nodeId)) continue
78
85
  seen.add(nodeId)
@@ -82,10 +89,20 @@ export function planDelegation(input: DelegationGrantInput): DelegationPlan {
82
89
  continue
83
90
  }
84
91
  for (const h of held) {
92
+ // mode 缺省 = manual(安全侧)。旧实现 `input.mode ?? h.mode` 会让 lead 的 auto
93
+ // 静默下放给 worker,且没有 confirm 与审计 —— 委派面成了摘闸门的旁路。
94
+ const targetMode: AgentNodeBindingMode = input.mode ?? 'manual'
95
+ const existing = repoForExisting.find(targetAgentId, nodeId, h.kind)
96
+ if (targetMode === 'auto' && existing?.mode !== 'auto') {
97
+ if (input.confirm !== true) {
98
+ autoWithoutConfirm.push(nodeId)
99
+ continue
100
+ }
101
+ }
85
102
  grants.push({
86
103
  nodeId,
87
104
  kind: h.kind,
88
- mode: input.mode ?? h.mode,
105
+ mode: targetMode,
89
106
  tuning: h.tuning ? { ...h.tuning } : undefined,
90
107
  })
91
108
  }
@@ -93,16 +110,44 @@ export function planDelegation(input: DelegationGrantInput): DelegationPlan {
93
110
  if (missing.length) {
94
111
  throw new AppError(403, 'DELEGATION_NOT_OWNED', `以下节点不在 lead 的绑定面内,拒绝授予(权限不可超越授予者):${missing.join('、')}`)
95
112
  }
113
+ if (autoWithoutConfirm.length) {
114
+ throw new AppError(400, 'DELEGATION_MODE_CONFIRM_REQUIRED',
115
+ `授予 auto 模式将摘除 worker 对这些节点的逐次人工审批闸门,需显式风险确认:`
116
+ + `请在参数中携带 confirm: true(布尔)。涉及节点:${autoWithoutConfirm.join('、')}。`
117
+ + `未做任何授权变更;如需保留人工审批,请改用 mode=manual(缺省值)。`)
118
+ }
96
119
  return { targetAgentId, grants }
97
120
  }
98
121
 
99
122
  /** 执行授予:按计划逐条落库(已存在的绑定更新 mode/溯源,不重复建行) */
100
123
  export function applyDelegation(plan: DelegationPlan, leaderAgentId: string): AgentNodeBinding[] {
101
124
  const repo = getAgentNodeBindingRepo()
102
- return plan.grants.map((g) => {
125
+ const out = plan.grants.map((g) => {
103
126
  const binding = repo.bind(plan.targetAgentId, g.nodeId, g.kind, g.mode, g.tuning, { grantedByAgentId: leaderAgentId })
104
127
  return binding
105
128
  })
129
+ // B-P1-3:auto 授予必须留痕(与 REST bindings PATCH 的 binding.mode 审计同口径)
130
+ const autoGrants = out.filter(b => b.mode === 'auto')
131
+ if (autoGrants.length > 0) {
132
+ recordOps({
133
+ actor: leaderAgentId,
134
+ actorName: `lead(${leaderAgentId})`,
135
+ actorKind: 'agent',
136
+ action: 'binding.mode',
137
+ kind: 'system',
138
+ summary: `委派授予 auto 控制模式:${autoGrants.map(b => `${b.kind}:${b.nodeId}`).join('、')}`,
139
+ targetKind: 'agent_node_binding',
140
+ targetId: autoGrants[0]!.id,
141
+ detail: {
142
+ targetAgentId: plan.targetAgentId,
143
+ grantedByAgentId: leaderAgentId,
144
+ grants: autoGrants.map(b => ({ bindingId: b.id, nodeId: b.nodeId, kind: b.kind, mode: b.mode })),
145
+ confirm: true,
146
+ source: 'team_grant_nodes',
147
+ },
148
+ })
149
+ }
150
+ return out
106
151
  }
107
152
 
108
153
  export interface DelegationRevokeInput {
@@ -14,6 +14,7 @@ import { sendHitlNote } from '../runtime/platform-notice'
14
14
  import { notifyLineOperators } from '../runtime/line-notify'
15
15
  import { getDcwController } from '../dcw/dcw-controller'
16
16
  import { recordRecipeOpAnchor } from '../dcw/recipe-op-anchor'
17
+ import { assertRecipeOperation } from './industrial/recipe-gate'
17
18
 
18
19
  export interface ToolApproval {
19
20
  id: string
@@ -312,6 +313,28 @@ class ToolApprovalService {
312
313
  }
313
314
  catch { /* 配方已删除 → 无参数节点可清理 */ }
314
315
  }
316
+ else if (binding.kind === 'dcw') {
317
+ // 2026-10-28 节点级写授权:收回点位授权后,在飞的配方审批单按当前覆盖集重新判定 ——
318
+ // 覆盖失效即按拒收敛(收回即失活)。仍被其余节点绑定/配方绑定完整覆盖的审批保留,
319
+ // 不做过度撤销。
320
+ for (const entry of [...this.pending.values()]) {
321
+ if (entry.approval.agentId !== agentId) continue
322
+ const nodeRef = entry.approval.nodeId
323
+ const recipeId = nodeRef.startsWith('recipe:')
324
+ ? nodeRef.slice('recipe:'.length)
325
+ : nodeRef.startsWith('recipe-propose:') ? nodeRef.slice('recipe-propose:'.length) : ''
326
+ if (!recipeId) continue
327
+ const recipe = getDcwController().listRecipes().find(r => r.id === recipeId)
328
+ if (!recipe) continue
329
+ const payload = entry.approval.payload as { op?: string, params?: Array<{ nodeId?: string }> } | undefined
330
+ const op = payload?.op === 'update' || payload?.op === 'rollback' || payload?.op === 'trial'
331
+ ? payload.op
332
+ : 'dispatch'
333
+ const target = (payload?.params ?? []).map(p => String(p.nodeId ?? '')).filter(Boolean)
334
+ const gate = assertRecipeOperation(agentId, recipe, op, target.length > 0 ? target : undefined)
335
+ if (!gate.ok) n += this.cancelPendingFor(agentId, nodeRef)
336
+ }
337
+ }
315
338
  return n
316
339
  }
317
340
 
@@ -42,6 +42,9 @@ export interface ToolVerifySpec {
42
42
  nodeLists?: string[]
43
43
  /** 深路径节点引用(如 'params[].node_id' / 'packages[].params[].node_id') */
44
44
  deepNodeRefs?: string[]
45
+ /** 深路径节点引用,要求落在**写授权面** W(a)=dcw 绑定节点 ∪ 绑定配方参数节点
46
+ * (2026-10-28 节点级写授权:配方 trial/update 的 params[].node_id) */
47
+ writeNodeRefs?: string[]
45
48
  /** 链式引用(dataset_id/job_id/model_id/plan_id/scene_id/binding_id/run_id/recommendation_id) */
46
49
  chained?: ToolChainRule[]
47
50
  /** 只读工具:节点只要归属可读产线即可引用(与频道绑线只读扩权同口径;写工具不得开启) */
@@ -35,6 +35,8 @@ export class MqttQueueAdapter implements DaqQueuePort {
35
35
  private consumers = new Set<DaqConsumer>()
36
36
  published = 0
37
37
  received = 0
38
+ /** topic 与 payload.nodeId 不一致而丢弃的帧数(S13 身份校验) */
39
+ identityMismatch = 0
38
40
  /** 断连窗口未发布计数(仪表可见的队列层丢弃) */
39
41
  lostCount = 0
40
42
  /** 断连离线缓冲(重连后按原序补发;带上限防内存无限涨) */
@@ -82,6 +84,16 @@ export class MqttQueueAdapter implements DaqQueuePort {
82
84
  this.client.on('message', (topic, payload) => {
83
85
  try {
84
86
  const env = JSON.parse(payload.toString()) as DaqSampleEnvelope
87
+ // 2026-10-28(S13):topic 的 nodeId 与 payload.nodeId 必须一致 ——
88
+ // 否则任何能发布到 broker 的进程都能冒充任意节点注入样本。
89
+ const topicNode = String(topic ?? '').split('/')[2] ?? ''
90
+ if (topicNode && env.nodeId && topicNode !== env.nodeId) {
91
+ this.identityMismatch++
92
+ if (this.identityMismatch <= 5 || this.identityMismatch % 100 === 0) {
93
+ log.warn(`[daq-mqtt] 丢弃身份不一致帧:topic=${topic} payload.nodeId=${env.nodeId}(累计 ${this.identityMismatch})`)
94
+ }
95
+ return
96
+ }
85
97
  this.received++
86
98
  for (const fn of this.consumers) fn(env)
87
99
  }
@@ -6,7 +6,7 @@ import { DaqControllerRuntimeControl } from './runtime-control'
6
6
  import type { DaqCreateInput, DaqPatchInput } from './types'
7
7
  import { AppError, ErrorCodes } from '../../../../utils/errors'
8
8
  import { DaqNode } from '../daq-node'
9
- import { daqKeyFromRef, normalizeDataTransform } from '../../../../../shared/daq-protocol'
9
+ import { daqKeyFromRef, normalizeDataTransform, stripMaskedSecrets } from '../../../../../shared/daq-protocol'
10
10
  import { findDaqTemplate } from '../daq-templates'
11
11
  import { getDaqHostPorts } from '../host-ports'
12
12
  import { requireDriverKind } from '../drivers'
@@ -31,7 +31,7 @@ export abstract class DaqControllerCrud extends DaqControllerRuntimeControl {
31
31
  templateRef: input.templateRef,
32
32
  name: input.name ?? (tpl ? `${tpl.name} ${String(seq).padStart(2, '0')}` : (input.driver && input.driver !== 'mock' ? `${input.driver.toUpperCase()} 通道` : undefined)),
33
33
  driver: input.driver ? requireDriverKind(input.driver) : undefined,
34
- driverConfig: input.driverConfig ?? {},
34
+ driverConfig: stripMaskedSecrets(input.driverConfig ?? {}),
35
35
  transform: normalizeDataTransform(input.transform),
36
36
  enabled: input.enabled,
37
37
  intervalMs: input.intervalMs == null ? null : Math.max(this.minIntervalMs, Math.min(60_000, Math.round(input.intervalMs))),
@@ -61,7 +61,9 @@ export abstract class DaqControllerCrud extends DaqControllerRuntimeControl {
61
61
  if (!node) throw Object.assign(new Error(`数采节点不存在: ${id}`), { status: 404 })
62
62
  if (patch.name !== undefined) node.name = patch.name
63
63
  if (patch.driver !== undefined) node.driver = requireDriverKind(patch.driver)
64
- if (patch.driverConfig !== undefined) node.driverConfig = { ...node.driverConfig, ...patch.driverConfig }
64
+ // P0-2(2026-10-28 审查):GET 投影的 `******` 可能是整包 PATCH 回来的回显,
65
+ // 入口单点剔除,绝不让掩码覆盖真实凭据(新明文照常写入)。
66
+ if (patch.driverConfig !== undefined) node.driverConfig = { ...node.driverConfig, ...stripMaskedSecrets(patch.driverConfig) }
65
67
  if (patch.transform !== undefined) {
66
68
  if (patch.transform.kind === 'linear' && (!Number.isFinite(Number(patch.transform.scale)) || Number(patch.transform.scale) === 0)) {
67
69
  throw new AppError(400, ErrorCodes.VALIDATION_ERROR, '标定系数 scale 必须为非零数字(物理值 = scale × PLC值 + offset)')
@@ -12,6 +12,7 @@ import { findDaqTemplate } from '../daq-templates'
12
12
  import { getDaqHostPorts } from '../host-ports'
13
13
  import { getDaqQueue } from '../bus'
14
14
  import { tsdbRowOf } from './tsdb-row'
15
+ import type { DaqFrameSample } from '../drivers'
15
16
 
16
17
  export abstract class DaqControllerIngest extends DaqControllerPush {
17
18
  /** 生产面:样本帧入队 */
@@ -20,6 +21,28 @@ export abstract class DaqControllerIngest extends DaqControllerPush {
20
21
  this.producedCount++
21
22
  }
22
23
 
24
+ /**
25
+ * 外部推送帧的统一后处理入口(2026-10-28 审查 A-P1-3)。
26
+ *
27
+ * 轮询采样 / 被动订阅 / 外部 ingest 三条生产路径必须共用同一 sink 管线:
28
+ * 模板 processor(thumbnail/deriveMetric/qualityGate)→ 图像落对象存储 → 服务端
29
+ * 计算 sha256/size。外部调用方只提供原始 blob 或点列,绝不信任其自报指标与指纹;
30
+ * 否则"完全相同链路"的承诺不成立(缩略图缺失、质量门被绕过、派生指标为空)。
31
+ */
32
+ async processExternalFrame(
33
+ node: DaqNode,
34
+ raw:
35
+ | { kind: 'vector', points: number[], metrics?: Record<string, number> }
36
+ | { kind: 'image', blob: Buffer, mime: string, width?: number, height?: number, metrics?: Record<string, number> },
37
+ atMs: number,
38
+ ): Promise<{ frame: NonNullable<DaqSampleEnvelope['frame']> } | null> {
39
+ // 外部推送未必携带像素尺寸;缺省记 0(未知),不阻断落库与缩略图生成
40
+ const frame: DaqFrameSample['frame'] = raw.kind === 'image'
41
+ ? { ...raw, width: raw.width ?? 0, height: raw.height ?? 0 }
42
+ : raw
43
+ return this.processFrameSample(node, frame, atMs)
44
+ }
45
+
23
46
  /** 消费面:样本入网关管线(状态派生已在 runtime 完成)。
24
47
  * allowPublish = 通过该节点 WS 下发节拍门控;入库/遥测回写不受门控(每帧必达)。 */
25
48
  /**
@@ -23,6 +23,25 @@ import { log } from './helpers'
23
23
  /** 订阅失败后的重试退避(避免 sweep 每 250ms 打一次 broker) */
24
24
  const PUSH_RETRY_BACKOFF_MS = 5_000
25
25
 
26
+ /** 停线缓存值的最大补种年龄(超过视为无关,等设备新报文) */
27
+ export const RETAINED_SEED_MAX_AGE_MS = 600_000
28
+
29
+ /**
30
+ * 停线补种计划(纯函数,便于单测;A-P1-2)。
31
+ *
32
+ * retained-only 设备在停线期间只发过一条状态,开跑后需把缓存值补种一次。补种的
33
+ * **时间戳必须是补种时刻**(now):用缓存到达时刻会被运行时乱序游标判 late 丢弃
34
+ * (上一批样本已推进游标),而 runSeeded 又已置真 → 整批零数据。
35
+ */
36
+ export function retainedSeedPlan(
37
+ bind: { runSeeded: boolean, lastValue: number | null, lastAt: number },
38
+ now: number,
39
+ ): { value: number, atMs: number } | null {
40
+ if (bind.runSeeded || bind.lastValue == null) return null
41
+ if (now - bind.lastAt > RETAINED_SEED_MAX_AGE_MS) return null
42
+ return { value: bind.lastValue, atMs: now }
43
+ }
44
+
26
45
  interface PushBinding {
27
46
  stop: () => void | Promise<void>
28
47
  /** 订阅参数指纹(节点 topic/host/jsonPath 变更 → 重建订阅) */
@@ -308,13 +327,15 @@ export abstract class DaqControllerPush extends DaqControllerState {
308
327
  continue
309
328
  }
310
329
  // 新批次开跑:把停线期间缓存的最近值补种一次(retained-only 设备整批只有这一条状态;
311
- // 时间戳=消息到达时刻,超过 10 分钟的旧值不补,等新报文)
330
+ // 超过 10 分钟的旧缓存不补,等新报文)
312
331
  if (bind.runId !== run.runId) {
313
332
  bind.runId = run.runId
314
333
  bind.runSeeded = false
315
334
  }
316
- if (!bind.runSeeded && bind.lastValue != null && (now - bind.lastAt) <= 600_000) {
317
- this.publishPushValue(node, bind.lastValue, bind.lastAt || now)
335
+ if (!bind.runSeeded && bind.lastValue != null && (now - bind.lastAt) <= RETAINED_SEED_MAX_AGE_MS) {
336
+ // 2026-10-28 审查 A-P1-2:补种时间戳 = 补种时刻(见 retainedSeedPlan 注释)。
337
+ const seed = retainedSeedPlan(bind, now)
338
+ if (seed) this.publishPushValue(node, seed.value, seed.atMs)
318
339
  bind.runSeeded = true
319
340
  if (node.lastError) node.lastError = null
320
341
  }
@@ -10,6 +10,7 @@ import { getObjectStore } from '../objectstore'
10
10
  import { getTsdb } from '../storage'
11
11
  import { listPluginDrivers } from '../drivers'
12
12
  import { startAlarmEscalator } from '../alarm-notify'
13
+ import { AppError } from '../../../../utils/errors'
13
14
 
14
15
  export abstract class DaqControllerRuntimeControl extends DaqControllerFrames {
15
16
  /** 后端能力自描述(meta 用) */
@@ -20,6 +21,11 @@ export abstract class DaqControllerRuntimeControl extends DaqControllerFrames {
20
21
  // ---------- 控制器全局 ----------
21
22
 
22
23
  startAll(): AepDaqControllerState {
24
+ // S10:后端明确降级时禁止开采(手动 resume 也不能绕过启动屏障)
25
+ const infra = (globalThis as unknown as { __daqInfraStatus?: () => { degraded: boolean, warning?: string } }).__daqInfraStatus?.()
26
+ if (infra?.degraded) {
27
+ throw new AppError(409, 'DAQ_INFRA_DEGRADED', `数采基础设施降级,禁止启动采集:${infra.warning ?? '后端不可用'}。请先修复后端或调用 /api/workshop/daq/infra/reconnect。`)
28
+ }
23
29
  this.running = true
24
30
  this.ensureLoop()
25
31
  startAlarmEscalator() // S5:升级通知扫描器(懒启动,进程内单例)
@@ -118,6 +118,8 @@ export abstract class DaqControllerSweepAlarms extends DaqControllerIngest {
118
118
  const payload = {
119
119
  id, nodeId: node.id, nodeName: node.name, metric,
120
120
  value, rule, threshold, escalation: 0, createdAt,
121
+ // S14:告警帧必须带产线归属,否则 scene-events 会当作"线无关"广播给所有 peer
122
+ lineId: node.lineId || null,
121
123
  }
122
124
  this.broadcast?.('daq.alarm', payload)
123
125
  notifyAlarm(payload)
@@ -143,7 +145,7 @@ export abstract class DaqControllerSweepAlarms extends DaqControllerIngest {
143
145
 
144
146
  /** alarm 恢复沿:广播恢复(报警保持 open 待人工 ack) */
145
147
  protected handleAlarmRecover(node: DaqNode, value: number): void {
146
- this.broadcast?.('daq.alarm.changed', { nodeId: node.id, nodeName: node.name, recovered: true, value, at: new Date().toISOString() })
148
+ this.broadcast?.('daq.alarm.changed', { nodeId: node.id, nodeName: node.name, recovered: true, value, at: new Date().toISOString(), lineId: node.lineId || null })
147
149
  }
148
150
 
149
151
  /** 报警确认(HITL 闭环的人为一步;幂等:已确认返回 false) */
@@ -11,6 +11,7 @@ import { TSDB_WRITE_RETRIES, log } from './helpers'
11
11
  import { findDaqTemplate } from '../daq-templates'
12
12
  import { getDaqHostPorts } from '../host-ports'
13
13
  import { getTsdb } from '../storage'
14
+ import { getDcwNodeRepo } from '../../dcw/dcw-node.repo'
14
15
 
15
16
  export abstract class DaqControllerTsdbTwin extends DaqControllerSweepAlarms {
16
17
  /** 网关消费泵:队列帧按节点分发回其运行时(乱序防御/状态派生/下发门控在 runtime)。
@@ -131,11 +132,30 @@ export abstract class DaqControllerTsdbTwin extends DaqControllerSweepAlarms {
131
132
  /** 遥测 WS 推送(1s/设备节流):孪生状态/遥测事件化直推,前端全量轮询降级为断线兜底 */
132
133
  protected twinPushAt = new Map<string, number>()
133
134
 
135
+ /** 孪生绑定节点所属产线(唯一时返回;多线/未绑定 → null,保持全局语义) */
136
+ private twinLineId(twinId: string): string | null {
137
+ try {
138
+ const lines = new Set<string>()
139
+ for (const n of this.repo.all()) {
140
+ if (n.lineId && (n.deviceIds ?? []).includes(twinId)) lines.add(n.lineId)
141
+ }
142
+ for (const n of getDcwNodeRepo().all()) {
143
+ if (n.lineId && (n.deviceIds ?? []).includes(twinId)) lines.add(n.lineId)
144
+ }
145
+ return lines.size === 1 ? [...lines][0]! : null
146
+ }
147
+ catch { return null }
148
+ }
149
+
134
150
  protected pushTwinTelemetry(twinId: string): void {
135
151
  const now = Date.now()
136
152
  if (now - (this.twinPushAt.get(twinId) ?? 0) < 1000) return
137
153
  this.twinPushAt.set(twinId, now)
138
154
  const payload = getDaqHostPorts()?.telemetry.scenePayload(twinId)
139
- if (payload) this.broadcast?.('device.updated', payload)
155
+ if (payload) {
156
+ // S14:补产线归属,让 scene-events 按 peer 可见集过滤;唯一产线才补,多线保持全局
157
+ const lineId = this.twinLineId(twinId)
158
+ this.broadcast?.('device.updated', lineId ? { ...(payload as object), lineId } : payload)
159
+ }
140
160
  }
141
161
  }
@@ -23,17 +23,20 @@ function load(): { nodes: DaqNode[], migrated: number } {
23
23
  let migrated = 0
24
24
  const nodes = (parsed as Record<string, unknown>[]).map((r) => {
25
25
  const node = DaqNode.fromRow(r)
26
- // P0-5 启动迁移:旧命名归一(modbus→modbus-tcp 等);无法归一的未知 kind
27
- // 降级 mock 并打标(fail-visible,不砖启动)—— 该节点采样路径显式失败待重配。
26
+ // P0-5 启动迁移:旧命名归一(modbus→modbus-tcp 等)。
27
+ // S11(2026-10-28):未知 kind 保留原名并显式标记不可用,**绝不降级 mock** ——
28
+ // 真实节点被悄悄换成假数据会污染训练/质量链;采样由 resolveDaqDriver 显式失败。
28
29
  if (r.driver != null) {
29
30
  const m = migrateDriverKind(String(r.driver))
30
- if (m.kind !== node.driver) {
31
+ if (m.unknown) {
32
+ node.driver = m.kind
33
+ migrated++
34
+ node.lastError = `驱动「${String(r.driver)}」未注册 —— 已拒绝采样,请安装对应驱动插件或重新配置节点驱动`
35
+ log.warn(`[daq] 节点 ${node.id}(${node.name}) 驱动「${String(r.driver)}」未注册,已拒绝采样(不降级 mock)`)
36
+ }
37
+ else if (m.kind !== node.driver) {
31
38
  node.driver = m.kind
32
39
  migrated++
33
- if (m.downgraded) {
34
- node.lastError = `驱动「${String(r.driver)}」未注册,已降级 mock —— 请重新配置节点驱动后重采`
35
- log.warn(`[daq] 节点 ${node.id}(${node.name}) 驱动「${String(r.driver)}」未注册,已降级 mock`)
36
- }
37
40
  }
38
41
  }
39
42
  return node
@@ -211,6 +211,8 @@ export class DaqNode {
211
211
  value: this.value,
212
212
  state: this.state,
213
213
  lastAt: this.lastAt,
214
+ // S11:驱动不可用/采样失败原因必须随快照落盘,否则重启即丢、UI 无从解释
215
+ lastError: this.lastError,
214
216
  createdAt: this.createdAt,
215
217
  }
216
218
  }
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { applyTransform } from '../../../../shared/daq-protocol'
13
13
  import { classifyCommError, type DaqFrameSample } from './drivers'
14
+ import { ingestFutureSkewMs } from './ingest-policy'
14
15
  import type { DaqNode } from './daq-node'
15
16
  import type { DaqSampleEnvelope } from './bus/queue-port'
16
17
 
@@ -45,7 +46,7 @@ export interface DaqRuntimeHost {
45
46
  }
46
47
 
47
48
  /** onSample 结果(网关据此计指标) */
48
- export type ConsumeVerdict = 'late' | 'ok' | 'unknown-node'
49
+ export type ConsumeVerdict = 'late' | 'future' | 'ok' | 'unknown-node'
49
50
 
50
51
  export class DaqNodeRuntime {
51
52
  /** 采样时点(epoch ms;-Inf = 立即到期) */
@@ -157,6 +158,9 @@ export class DaqNodeRuntime {
157
158
  onSample(env: DaqSampleEnvelope): ConsumeVerdict {
158
159
  const node = this.node
159
160
  const tsMs = Date.parse(env.at)
161
+ // 2026-10-28(S12):未来时间戳必须拒绝且**不推进游标** —— 否则一条未来样本会把
162
+ // lastIngestAt 推到未来,之后所有实时样本被判 late 静默丢弃。
163
+ if (tsMs && tsMs > Date.now() + ingestFutureSkewMs()) return 'future'
160
164
  // 乱序防御(broker 多投递者场景):迟到帧直接丢弃
161
165
  if (tsMs && tsMs <= this.lastIngestAt) return 'late'
162
166
  this.lastIngestAt = tsMs
@@ -165,7 +169,11 @@ export class DaqNodeRuntime {
165
169
  // 指标阈值告警在网关 ingestFrame 按模板 metrics 规则边沿判定,这里只做
166
170
  // 节点活跃度/展示值更新 + WS 下发节拍门控。
167
171
  if (env.frame) {
168
- node.state = 'ok'
172
+ // 2026-10-28 审查 A-P1-1:帧只负责"活性"(offline→ok)。指标阈值告警是跨帧持续态,
173
+ // 旧代码每帧无条件置 ok,会把 ingestFrame 刚判出的 alarm 立即抹掉,告警面板
174
+ // (metricStates)与节点态直接矛盾。warn/alarm 的进入与恢复由网关
175
+ // evaluateMetricAlarms 的边沿语义独占,此处不得改写。
176
+ if (node.state === 'offline') node.state = 'ok'
169
177
  node.touchReading(env.value, env.at)
170
178
  const pubMs = Math.max(0, Math.min(PUBLISH_INTERVAL_MAX, node.publishIntervalMs ?? this.host.defaults().publishIntervalMs))
171
179
  const now = Date.now()
@@ -162,14 +162,17 @@ export function decodeRegisters(data: number[], dataType: string, byteOrder: str
162
162
  const raw = data[0] ?? 0
163
163
  return dataType === 'int16' ? (raw << 16) >> 16 : raw
164
164
  }
165
- // 字节序约定(寄存器字 w0=[A B], w1=[C D],float32 内存字节序按 4 字节解):
166
- // big = ABCD(w0 高字在前,w0/w1 各自大端) —— Modbus 标准大端,最常用
167
- // little = DCBA(全小端:w1 字节反转在前)
168
- // wordSwap = CDAB(字交换:每字内部仍大端,低字在前)
165
+ // 字节序约定(值字节 A B C D = MSB..LSB;寄存器字 w0/w1 各按"高字节在前"读):
166
+ // big = ABCD → w0=[A B], w1=[C D] —— Modbus 标准大端,最常用
167
+ // wordSwap = CDAB → w0=[C D], w1=[A B](仅交换字序)
168
+ // little = DCBA → w0=[D C], w1=[B A](整 32 位小端:每字字节反转 + 字序反转)
169
+ // 2026-10-28 审查 P0-1:旧 little 分支实际解出 BADC(与注释 DCBA、写侧 encodeWords
170
+ // 均不一致),真实 DCBA/CDAB 设备会静默读成 1e-41 量级错值。三序现与
171
+ // dcw/drivers/shared.ts:encodeWords 严格互逆,并与 UI 目录标签同义。
169
172
  const hi = (x: number) => (x >> 8) & 0xFF
170
173
  const lo = (x: number) => x & 0xFF
171
174
  let b: number[]
172
- if (byteOrder === 'little') b = [hi(data[1]!), lo(data[1]!), hi(data[0]!), lo(data[0]!)].reverse()
175
+ if (byteOrder === 'little') b = [lo(data[1]!), hi(data[1]!), lo(data[0]!), hi(data[0]!)]
173
176
  else if (byteOrder === 'wordSwap') b = [hi(data[1]!), lo(data[1]!), hi(data[0]!), lo(data[0]!)]
174
177
  else b = [hi(data[0]!), lo(data[0]!), hi(data[1]!), lo(data[1]!)]
175
178
  const buf = Buffer.from(b)
@@ -85,14 +85,15 @@ export function requireDriverKind(kind: string): DaqDriverKind {
85
85
  }
86
86
 
87
87
  /**
88
- * 启动迁移:磁盘遗留 kind 别名归一;无法归一的未知 kind 降级 mock 并打标
89
- * (fail-visible,不砖启动 —— 由 daq-node.repo load() 调用,该节点采样路径显式失败待重配)。
88
+ * 启动迁移:磁盘遗留 kind 别名归一。
89
+ * 2026-10-28(S11):未知 kind **不再降级为 mock**(真实节点被悄悄换成假数据是工业高危默认);
90
+ * 保留原 kind,由 resolveDaqDriver 的 unavailableDaqDriver 显式拒绝采样,等插件注册/人工重配。
90
91
  */
91
- export function migrateDriverKind(kind: string): { kind: DaqDriverKind, downgraded: boolean } {
92
+ export function migrateDriverKind(kind: string): { kind: DaqDriverKind, downgraded: boolean, unknown?: boolean } {
92
93
  if (kind === 'modbus') return { kind: 'modbus-tcp', downgraded: false }
93
94
  if (kind === 'rtu' || kind === 'modbus-rtu-tcp') return { kind: 'modbus-rtu', downgraded: false }
94
95
  if (kind in REGISTRY || pluginRegistry().has(kind)) return { kind: kind as DaqDriverKind, downgraded: false }
95
- return { kind: 'mock', downgraded: true }
96
+ return { kind: kind as DaqDriverKind, downgraded: false, unknown: true }
96
97
  }
97
98
 
98
99
  /**
@@ -109,6 +109,19 @@ export function daqInfraStatus(): DaqInfraStatus {
109
109
  return { ...state().status }
110
110
  }
111
111
 
112
+ /**
113
+ * 后端装配失败(S10):端口探测通过但 TSDB/队列/对象存储 init 失败时,
114
+ * 必须把状态显式置为 degraded —— 否则 30s 自动重连永不触发,采样会写进占位后端。
115
+ */
116
+ export function markDaqInfraFailure(err: unknown): void {
117
+ const s = state().status
118
+ s.degraded = true
119
+ s.lastCheckAt = new Date().toISOString()
120
+ s.lastError = err instanceof Error ? err.message : String(err)
121
+ s.warning = `数采后端初始化失败,已降级并停采:${s.lastError}`
122
+ log.error('[daq-infra]', s.warning)
123
+ }
124
+
112
125
  /** 运行配置快照(启动插件装配时写入;重连端点复用) */
113
126
  export function daqInfraConfig(): DaqInfraConfig {
114
127
  return state().cfg
@@ -29,11 +29,34 @@ export function isWideBackfillWindow(env: Record<string, string | undefined> = p
29
29
  return ingestBackfillWindowMs(env) > WIDE_WINDOW_THRESHOLD_MS
30
30
  }
31
31
 
32
- /** 时间点是否在允许窗内 */
32
+ /** 时间点是否在允许窗内(旧口径:双向 ±window;新代码请用 ingestTimeVerdict) */
33
33
  export function withinBackfillWindow(atMs: number, now: number, windowMs: number): boolean {
34
34
  return Number.isFinite(atMs) && Math.abs(atMs - now) <= windowMs
35
35
  }
36
36
 
37
+ /** 未来时间戳容忍度(ms):缺省 60s;env AW_DAQ_INGEST_FUTURE_SKEW_MS 可调,钳 0..1h */
38
+ export function ingestFutureSkewMs(env: Record<string, string | undefined> = process.env): number {
39
+ const raw = Number(env.AW_DAQ_INGEST_FUTURE_SKEW_MS)
40
+ if (!Number.isFinite(raw) || raw < 0) return 60_000
41
+ return Math.min(raw, 3600_000)
42
+ }
43
+
44
+ export type IngestTimeVerdict = 'ok' | 'future' | 'out-of-window'
45
+
46
+ /**
47
+ * 时间戳方向判定(2026-10-28 S12):
48
+ * - 未来超过 skew → future(必须拒绝;否则单条未来样本会把该节点 lastIngestAt 推到未来,
49
+ * 之后所有实时样本被判 late 丢弃,最长持续到墙钟追上);
50
+ * - 过去超过回填窗 → out-of-window;
51
+ * - 其余 ok。
52
+ */
53
+ export function ingestTimeVerdict(atMs: number, now: number, windowMs: number, skewMs: number): IngestTimeVerdict {
54
+ if (!Number.isFinite(atMs)) return 'out-of-window'
55
+ if (atMs > now + skewMs) return 'future'
56
+ if (now - atMs > windowMs) return 'out-of-window'
57
+ return 'ok'
58
+ }
59
+
37
60
  /** 推送鉴权模式判定(纯函数便于单测):token 已配置 → 校验头;未配置 → 会话+产线权限 */
38
61
  export function ingestAuthMode(input: { configuredToken?: string, providedToken?: string }): 'token' | 'token-mismatch' | 'session' {
39
62
  if (input.configuredToken) {
@@ -229,17 +229,56 @@ export class TimescaleAdapter implements TsdbPort {
229
229
  return out
230
230
  }
231
231
  const { rows } = await this.pool.query(
232
- `SELECT node_id, ts, value FROM daq_samples WHERE ${whereSql} ORDER BY ts ASC LIMIT $${params.length + 1}`,
232
+ `SELECT node_id, ts, value, state, ingested_at FROM daq_samples WHERE ${whereSql} ORDER BY ts ASC LIMIT $${params.length + 1}`,
233
233
  [...params, limit],
234
234
  )
235
- for (const r of rows as Array<{ node_id: string, ts: string, value: number }>) {
235
+ for (const r of rows as Array<{ node_id: string, ts: string, value: number, state: string | null, ingested_at: string | number | null }>) {
236
236
  const list = out.get(String(r.node_id)) ?? []
237
- list.push({ at: tsToMs(r.ts), value: Number(r.value) })
237
+ // D-P1-1(2026-10-28 审查):state/ingested_at 与 sqlite 同口径返回;
238
+ // 否则生产后端上的坏点清洗(cleanPoints)与入库水位判定是静默空操作。
239
+ list.push({
240
+ at: tsToMs(r.ts),
241
+ value: Number(r.value),
242
+ state: String(r.state ?? 'ok'),
243
+ ...(r.ingested_at == null ? {} : { ingestedAt: Number(r.ingested_at) }),
244
+ })
238
245
  out.set(String(r.node_id), list)
239
246
  }
240
247
  return out
241
248
  }
242
249
 
250
+ /**
251
+ * 逐节点 raw 分页(D-P1-1,2026-10-28 审查:此前后端缺 queryTaggedPage,
252
+ * 训练/导出在 Timescale 上退化为 query() 截断)。
253
+ * 语义与 sqlite.adapter 完全一致:稳定 ASC、游标严格大于 afterTs、每页限额、
254
+ * done=false 时 nextCursor = 本页最后 at。
255
+ */
256
+ async queryTaggedPage(nodeId: string, q: import('./tsdb-port').TsdbPageQuery = {}): Promise<import('./tsdb-port').TsdbPage> {
257
+ if (!this.pool) return { nodeId, points: [], nextCursor: null, done: true }
258
+ const limit = Math.max(1, Math.min(q.limit ?? 5000, 500_000))
259
+ const from = Math.max(q.fromMs ?? 0, (q.afterTs ?? -1) + 1)
260
+ const to = q.toMs ?? Date.now()
261
+ const withState = q.withState !== false
262
+ const args = [nodeId, new Date(from).toISOString(), new Date(to).toISOString(), limit]
263
+ const rows = withState
264
+ ? (await this.pool.query(
265
+ 'SELECT ts, value, state, ingested_at FROM daq_samples WHERE node_id = $1 AND ts >= $2 AND ts <= $3 ORDER BY ts ASC LIMIT $4',
266
+ args,
267
+ )).rows
268
+ : (await this.pool.query(
269
+ 'SELECT ts, value FROM daq_samples WHERE node_id = $1 AND ts >= $2 AND ts <= $3 ORDER BY ts ASC LIMIT $4',
270
+ args,
271
+ )).rows
272
+ const points = (rows as Array<{ ts: string, value: number, state?: string | null, ingested_at?: string | number | null }>).map(r => ({
273
+ at: tsToMs(r.ts),
274
+ value: Number(r.value),
275
+ ...(withState ? { state: String(r.state ?? 'ok') } : {}),
276
+ ...(r.ingested_at == null ? {} : { ingestedAt: Number(r.ingested_at) }),
277
+ }))
278
+ const done = points.length < limit
279
+ return { nodeId, points, nextCursor: done ? null : points[points.length - 1]?.at ?? null, done }
280
+ }
281
+
243
282
  async latest(): Promise<Map<string, DaqSampleRow>> {
244
283
  const out = new Map<string, DaqSampleRow>()
245
284
  if (!this.pool) return out