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
@@ -108,6 +108,13 @@ interface SeriesSummary {
108
108
  cleanedRatio: number
109
109
  }
110
110
  interface LagEstimate { controlId: string, targetId: string, lagSteps: number, corr: number }
111
+ /** 逐 run 画像(服务端 server/services/workshop/aml/stats.ts 导出;此处镜像其线格式) */
112
+ interface RunProfile {
113
+ runId: string
114
+ steps: number
115
+ /** 目标节点逐 run 均值(漂移检测) */
116
+ targetMeans: Record<string, number>
117
+ }
111
118
  interface AmlDatasetReport {
112
119
  builtAt: string
113
120
  nodeSummaries: SeriesSummary[]
@@ -1151,258 +1158,260 @@ async function doPredict(): Promise<void> {
1151
1158
  </button>
1152
1159
  </div>
1153
1160
  </div>
1154
- <table
1161
+ <div
1155
1162
  v-if="datasets.length"
1156
- class="tbl"
1163
+ class="tbl-scroll"
1157
1164
  >
1158
- <thead>
1159
- <tr>
1160
- <th>{{ $t('aml.k1amlx020') }}</th>
1161
- <th>{{ $t('aml.k1amlx021') }}</th>
1162
- <th>{{ $t('aml.k1amlx022') }}</th>
1163
- <th class="num">
1164
- {{ $t('aml.k1amlx023') }}
1165
- </th>
1166
- <th class="num">
1167
- {{ $t('aml.k1amlx024') }}
1168
- </th>
1169
- <th>{{ $t('aml.k1amlx025') }}</th>
1170
- <th>{{ $t('aml.k1amlx026') }}</th>
1171
- </tr>
1172
- </thead>
1173
- <tbody>
1174
- <template
1175
- v-for="d in datasets"
1176
- :key="d.id"
1177
- >
1178
- <tr
1179
- class="row-main"
1180
- :class="{ open: expandedDs === d.id }"
1181
- @click="toggleDs(d.id)"
1165
+ <table class="tbl tbl-datasets">
1166
+ <thead>
1167
+ <tr>
1168
+ <th>{{ $t('aml.k1amlx020') }}</th>
1169
+ <th>{{ $t('aml.k1amlx021') }}</th>
1170
+ <th>{{ $t('aml.k1amlx022') }}</th>
1171
+ <th class="num">
1172
+ {{ $t('aml.k1amlx023') }}
1173
+ </th>
1174
+ <th class="num">
1175
+ {{ $t('aml.k1amlx024') }}
1176
+ </th>
1177
+ <th>{{ $t('aml.k1amlx025') }}</th>
1178
+ <th>{{ $t('aml.k1amlx026') }}</th>
1179
+ </tr>
1180
+ </thead>
1181
+ <tbody>
1182
+ <template
1183
+ v-for="d in datasets"
1184
+ :key="d.id"
1182
1185
  >
1183
- <td class="mono dim">
1184
- {{ fmtTime(d.createdAt) }}
1185
- </td>
1186
- <td class="mono">
1187
- {{ shortId(d.productId) }}
1188
- </td>
1189
- <td class="mono">
1190
- {{ shortId(d.recipeId) }}
1191
- </td>
1192
- <td class="mono num">
1193
- {{ d.rowCount }}
1194
- </td>
1195
- <td class="mono num">
1196
- {{ d.runIds.length }}
1197
- </td>
1198
- <td>
1199
- {{ d.createdBy }}<small
1200
- class="kind"
1201
- :class="d.createdByKind"
1202
- >{{ d.createdByKind === 'agent' ? 'Agent' : $t('aml.k1amlx049') }}</small>
1203
- </td>
1204
- <td
1205
- class="note-cell"
1206
- @click.stop
1186
+ <tr
1187
+ class="row-main"
1188
+ :class="{ open: expandedDs === d.id }"
1189
+ @click="toggleDs(d.id)"
1207
1190
  >
1208
- <div
1209
- v-if="noteEditing === d.id"
1210
- class="note-edit"
1191
+ <td class="mono dim">
1192
+ {{ fmtTime(d.createdAt) }}
1193
+ </td>
1194
+ <td class="mono">
1195
+ {{ shortId(d.productId) }}
1196
+ </td>
1197
+ <td class="mono">
1198
+ {{ shortId(d.recipeId) }}
1199
+ </td>
1200
+ <td class="mono num">
1201
+ {{ d.rowCount }}
1202
+ </td>
1203
+ <td class="mono num">
1204
+ {{ d.runIds.length }}
1205
+ </td>
1206
+ <td>
1207
+ {{ d.createdBy }}<small
1208
+ class="kind"
1209
+ :class="d.createdByKind"
1210
+ >{{ d.createdByKind === 'agent' ? 'Agent' : $t('aml.k1amlx049') }}</small>
1211
+ </td>
1212
+ <td
1213
+ class="note-cell"
1214
+ @click.stop
1211
1215
  >
1212
- <input
1213
- v-model="noteDraft"
1214
- :placeholder="$t('aml.k1amlx189')"
1215
- @keyup.enter="saveNote('datasets', d.id)"
1216
- @keyup.esc="cancelEditNote"
1217
- >
1218
- <button
1219
- class="mini-btn"
1220
- @click="saveNote('datasets', d.id)"
1221
- >
1222
- {{ $t('aml.k1amlx191') }}
1223
- </button>
1224
- <button
1225
- class="mini-btn"
1226
- @click="cancelEditNote"
1216
+ <div
1217
+ v-if="noteEditing === d.id"
1218
+ class="note-edit"
1227
1219
  >
1228
- {{ $t('aml.k1amlx202') }}
1229
- </button>
1230
- </div>
1231
- <span
1232
- v-else
1233
- class="note-text"
1234
- >
1235
- <span
1236
- class="note"
1237
- :title="d.note"
1238
- >{{ d.note || '--' }}</span>
1239
- <span
1240
- class="i-tabler-pencil edit-i"
1241
- :title="$t('aml.k1amlx189')"
1242
- @click="startEditNote(d.id, d.note)"
1243
- />
1244
- <span
1245
- class="i-tabler-trash edit-i"
1246
- :title="$t('aml.k1amlx190')"
1247
- @click="removeEntity('datasets', d.id, $t('aml.k1amlx198'))"
1248
- />
1249
- </span>
1250
- </td>
1251
- </tr>
1252
- <tr
1253
- v-if="expandedDs === d.id"
1254
- class="detail-row"
1255
- >
1256
- <td colspan="7">
1257
- <div
1258
- v-if="dsDetails[d.id]?.loading"
1259
- class="dim pad"
1260
- >
1261
- {{ $t('aml.k1amlx030') }}
1262
- </div>
1263
- <div
1264
- v-else-if="dsDetails[d.id]?.error"
1265
- class="err pad"
1266
- >
1267
- {{ $t('aml.k1amlx031') }}:{{ dsDetails[d.id]?.error }}
1268
- </div>
1269
- <div
1270
- v-else-if="dsDetails[d.id]?.report"
1271
- class="det-grid"
1272
- >
1273
- <div>
1274
- <p class="det-title">
1275
- {{ $t('aml.k1amlx027') }}
1276
- </p>
1277
- <table class="sub-tbl">
1278
- <thead>
1279
- <tr>
1280
- <th>{{ $t('aml.k1amlx033') }}</th>
1281
- <th>{{ $t('aml.k1amlx034') }}</th>
1282
- <th class="num">
1283
- {{ $t('aml.k1amlx035') }}
1284
- </th>
1285
- <th class="num">
1286
- {{ $t('aml.k1amlx036') }}
1287
- </th>
1288
- <th class="num">
1289
- {{ $t('aml.k1amlx037') }}
1290
- </th>
1291
- <th class="num">
1292
- {{ $t('aml.k1amlx038') }}
1293
- </th>
1294
- <th class="num">
1295
- {{ $t('aml.k1amlx039') }}
1296
- </th>
1297
- </tr>
1298
- </thead>
1299
- <tbody>
1300
- <tr
1301
- v-for="s in dsDetails[d.id]?.report?.nodeSummaries"
1302
- :key="s.nodeId"
1303
- >
1304
- <td class="mono">
1305
- {{ s.nodeId }}
1306
- </td>
1307
- <td>
1308
- <small class="role-chip">{{ s.role }}</small>
1309
- </td>
1310
- <td class="mono num">
1311
- {{ fmtPct(s.cleanedRatio) }}
1312
- </td>
1313
- <td class="mono num">
1314
- {{ fmtPct(s.missingRatio) }}
1315
- </td>
1316
- <td class="mono num">
1317
- {{ fmtNum(s.mean, 3) }}
1318
- </td>
1319
- <td class="mono num">
1320
- {{ fmtNum(s.std, 3) }}
1321
- </td>
1322
- <td class="mono num">
1323
- {{ fmtNum(s.min, 2) }}~{{ fmtNum(s.max, 2) }}
1324
- </td>
1325
- </tr>
1326
- </tbody>
1327
- </table>
1328
- </div>
1329
- <div>
1330
- <p class="det-title">
1331
- {{ $t('aml.k1amlx028') }}
1332
- </p>
1333
- <table
1334
- v-if="dsDetails[d.id]?.report?.lagEstimates.length"
1335
- class="sub-tbl"
1220
+ <input
1221
+ v-model="noteDraft"
1222
+ :placeholder="$t('aml.k1amlx189')"
1223
+ @keyup.enter="saveNote('datasets', d.id)"
1224
+ @keyup.esc="cancelEditNote"
1336
1225
  >
1337
- <thead>
1338
- <tr>
1339
- <th>{{ $t('aml.k1amlx040') }}</th>
1340
- <th>{{ $t('aml.k1amlx041') }}</th>
1341
- <th class="num">
1342
- {{ $t('aml.k1amlx042') }}
1343
- </th>
1344
- <th class="num">
1345
- {{ $t('aml.k1amlx043') }}
1346
- </th>
1347
- </tr>
1348
- </thead>
1349
- <tbody>
1350
- <tr
1351
- v-for="l in dsDetails[d.id]?.report?.lagEstimates"
1352
- :key="`${l.controlId}->${l.targetId}`"
1353
- >
1354
- <td class="mono">
1355
- {{ l.controlId }}
1356
- </td>
1357
- <td class="mono">
1358
- {{ l.targetId }}
1359
- </td>
1360
- <td class="mono num">
1361
- {{ l.lagSteps }}
1362
- </td>
1363
- <td class="mono num">
1364
- {{ fmtNum(l.corr, 3) }}
1365
- </td>
1366
- </tr>
1367
- </tbody>
1368
- </table>
1369
- <p
1370
- v-else
1371
- class="dim"
1226
+ <button
1227
+ class="mini-btn"
1228
+ @click="saveNote('datasets', d.id)"
1372
1229
  >
1373
- {{ $t('aml.k1amlx032') }}
1374
- </p>
1375
- <p class="det-title">
1376
- {{ $t('aml.k1amlx029') }}
1377
- </p>
1378
- <p class="mono dim run-line">
1379
- {{ $t('aml.k1amlx044') }}
1380
- <b>{{ dsDetails[d.id]?.report?.runsUsed.length ?? 0 }}</b>
1381
- · {{ $t('aml.k1amlx045') }}
1382
- <b>{{ dsDetails[d.id]?.report?.runsDropped.length ?? 0 }}</b>
1383
- · {{ $t('aml.k1amlx046') }}
1384
- <b>{{ dsDetails[d.id]?.report?.windowCount.train }}/{{ dsDetails[d.id]?.report?.windowCount.val }}/{{ dsDetails[d.id]?.report?.windowCount.test }}</b>
1385
- </p>
1386
- <p
1387
- v-for="r in dsDetails[d.id]?.report?.runsDropped"
1388
- :key="r.runId"
1389
- class="mono drop-line"
1230
+ {{ $t('aml.k1amlx191') }}
1231
+ </button>
1232
+ <button
1233
+ class="mini-btn"
1234
+ @click="cancelEditNote"
1390
1235
  >
1391
- ✗ {{ shortId(r.runId) }} — {{ r.reason }}
1392
- </p>
1236
+ {{ $t('aml.k1amlx202') }}
1237
+ </button>
1393
1238
  </div>
1394
- </div>
1395
- <p
1396
- v-else
1397
- class="dim pad"
1398
- >
1399
- {{ $t('aml.k1amlx047') }}
1400
- </p>
1401
- </td>
1402
- </tr>
1403
- </template>
1404
- </tbody>
1405
- </table>
1239
+ <span
1240
+ v-else
1241
+ class="note-text"
1242
+ >
1243
+ <span
1244
+ class="note"
1245
+ :title="d.note"
1246
+ >{{ d.note || '--' }}</span>
1247
+ <span
1248
+ class="i-tabler-pencil edit-i"
1249
+ :title="$t('aml.k1amlx189')"
1250
+ @click="startEditNote(d.id, d.note)"
1251
+ />
1252
+ <span
1253
+ class="i-tabler-trash edit-i"
1254
+ :title="$t('aml.k1amlx190')"
1255
+ @click="removeEntity('datasets', d.id, $t('aml.k1amlx198'))"
1256
+ />
1257
+ </span>
1258
+ </td>
1259
+ </tr>
1260
+ <tr
1261
+ v-if="expandedDs === d.id"
1262
+ class="detail-row"
1263
+ >
1264
+ <td colspan="7">
1265
+ <div
1266
+ v-if="dsDetails[d.id]?.loading"
1267
+ class="dim pad"
1268
+ >
1269
+ {{ $t('aml.k1amlx030') }}
1270
+ </div>
1271
+ <div
1272
+ v-else-if="dsDetails[d.id]?.error"
1273
+ class="err pad"
1274
+ >
1275
+ {{ $t('aml.k1amlx031') }}:{{ dsDetails[d.id]?.error }}
1276
+ </div>
1277
+ <div
1278
+ v-else-if="dsDetails[d.id]?.report"
1279
+ class="det-grid"
1280
+ >
1281
+ <div>
1282
+ <p class="det-title">
1283
+ {{ $t('aml.k1amlx027') }}
1284
+ </p>
1285
+ <table class="sub-tbl">
1286
+ <thead>
1287
+ <tr>
1288
+ <th>{{ $t('aml.k1amlx033') }}</th>
1289
+ <th>{{ $t('aml.k1amlx034') }}</th>
1290
+ <th class="num">
1291
+ {{ $t('aml.k1amlx035') }}
1292
+ </th>
1293
+ <th class="num">
1294
+ {{ $t('aml.k1amlx036') }}
1295
+ </th>
1296
+ <th class="num">
1297
+ {{ $t('aml.k1amlx037') }}
1298
+ </th>
1299
+ <th class="num">
1300
+ {{ $t('aml.k1amlx038') }}
1301
+ </th>
1302
+ <th class="num">
1303
+ {{ $t('aml.k1amlx039') }}
1304
+ </th>
1305
+ </tr>
1306
+ </thead>
1307
+ <tbody>
1308
+ <tr
1309
+ v-for="s in dsDetails[d.id]?.report?.nodeSummaries"
1310
+ :key="s.nodeId"
1311
+ >
1312
+ <td class="mono">
1313
+ {{ s.nodeId }}
1314
+ </td>
1315
+ <td>
1316
+ <small class="role-chip">{{ s.role }}</small>
1317
+ </td>
1318
+ <td class="mono num">
1319
+ {{ fmtPct(s.cleanedRatio) }}
1320
+ </td>
1321
+ <td class="mono num">
1322
+ {{ fmtPct(s.missingRatio) }}
1323
+ </td>
1324
+ <td class="mono num">
1325
+ {{ fmtNum(s.mean, 3) }}
1326
+ </td>
1327
+ <td class="mono num">
1328
+ {{ fmtNum(s.std, 3) }}
1329
+ </td>
1330
+ <td class="mono num">
1331
+ {{ fmtNum(s.min, 2) }}~{{ fmtNum(s.max, 2) }}
1332
+ </td>
1333
+ </tr>
1334
+ </tbody>
1335
+ </table>
1336
+ </div>
1337
+ <div>
1338
+ <p class="det-title">
1339
+ {{ $t('aml.k1amlx028') }}
1340
+ </p>
1341
+ <table
1342
+ v-if="dsDetails[d.id]?.report?.lagEstimates.length"
1343
+ class="sub-tbl"
1344
+ >
1345
+ <thead>
1346
+ <tr>
1347
+ <th>{{ $t('aml.k1amlx040') }}</th>
1348
+ <th>{{ $t('aml.k1amlx041') }}</th>
1349
+ <th class="num">
1350
+ {{ $t('aml.k1amlx042') }}
1351
+ </th>
1352
+ <th class="num">
1353
+ {{ $t('aml.k1amlx043') }}
1354
+ </th>
1355
+ </tr>
1356
+ </thead>
1357
+ <tbody>
1358
+ <tr
1359
+ v-for="l in dsDetails[d.id]?.report?.lagEstimates"
1360
+ :key="`${l.controlId}->${l.targetId}`"
1361
+ >
1362
+ <td class="mono">
1363
+ {{ l.controlId }}
1364
+ </td>
1365
+ <td class="mono">
1366
+ {{ l.targetId }}
1367
+ </td>
1368
+ <td class="mono num">
1369
+ {{ l.lagSteps }}
1370
+ </td>
1371
+ <td class="mono num">
1372
+ {{ fmtNum(l.corr, 3) }}
1373
+ </td>
1374
+ </tr>
1375
+ </tbody>
1376
+ </table>
1377
+ <p
1378
+ v-else
1379
+ class="dim"
1380
+ >
1381
+ {{ $t('aml.k1amlx032') }}
1382
+ </p>
1383
+ <p class="det-title">
1384
+ {{ $t('aml.k1amlx029') }}
1385
+ </p>
1386
+ <p class="mono dim run-line">
1387
+ {{ $t('aml.k1amlx044') }}
1388
+ <b>{{ dsDetails[d.id]?.report?.runsUsed.length ?? 0 }}</b>
1389
+ · {{ $t('aml.k1amlx045') }}
1390
+ <b>{{ dsDetails[d.id]?.report?.runsDropped.length ?? 0 }}</b>
1391
+ · {{ $t('aml.k1amlx046') }}
1392
+ <b>{{ dsDetails[d.id]?.report?.windowCount.train }}/{{ dsDetails[d.id]?.report?.windowCount.val }}/{{ dsDetails[d.id]?.report?.windowCount.test }}</b>
1393
+ </p>
1394
+ <p
1395
+ v-for="r in dsDetails[d.id]?.report?.runsDropped"
1396
+ :key="r.runId"
1397
+ class="mono drop-line"
1398
+ >
1399
+ ✗ {{ shortId(r.runId) }} — {{ r.reason }}
1400
+ </p>
1401
+ </div>
1402
+ </div>
1403
+ <p
1404
+ v-else
1405
+ class="dim pad"
1406
+ >
1407
+ {{ $t('aml.k1amlx047') }}
1408
+ </p>
1409
+ </td>
1410
+ </tr>
1411
+ </template>
1412
+ </tbody>
1413
+ </table>
1414
+ </div>
1406
1415
  <p
1407
1416
  v-else
1408
1417
  class="empty"
@@ -1431,193 +1440,195 @@ async function doPredict(): Promise<void> {
1431
1440
  {{ $t('aml.k1amlx017') }}
1432
1441
  </button>
1433
1442
  </div>
1434
- <table
1443
+ <div
1435
1444
  v-if="jobRows.length"
1436
- class="tbl"
1445
+ class="tbl-scroll"
1437
1446
  >
1438
- <thead>
1439
- <tr>
1440
- <th>{{ $t('aml.k1amlx076') }}</th>
1441
- <th>{{ $t('aml.k1amlx011') }}</th>
1442
- <th>{{ $t('aml.k1amlx066') }}</th>
1443
- <th>{{ $t('aml.k1amlx077') }}</th>
1444
- <th>{{ $t('aml.k1amlx078') }}</th>
1445
- <th class="prog-th">
1446
- {{ $t('aml.k1amlx079') }}
1447
- </th>
1448
- <th>{{ $t('aml.k1amlx020') }}</th>
1449
- <th class="right">
1450
- {{ $t('aml.k1amlx127') }}
1451
- </th>
1452
- </tr>
1453
- </thead>
1454
- <tbody>
1455
- <template
1456
- v-for="j in jobRows"
1457
- :key="j.id"
1458
- >
1459
- <tr
1460
- class="row-main"
1461
- :class="{ open: expandedJob === j.id }"
1462
- @click="toggleJob(j.id)"
1463
- >
1464
- <td class="mono">
1465
- {{ shortId(j.id) }}
1466
- </td>
1467
- <td class="mono dim">
1468
- {{ shortId(j.datasetId) }}
1469
- </td>
1470
- <td class="mono dim">
1471
- {{ j.purpose }}
1472
- </td>
1473
- <td>
1474
- <span
1475
- class="st-pill"
1476
- :class="j.status"
1477
- >{{ statusLabel(j.status) }}</span>
1478
- </td>
1479
- <td class="mono dim">
1480
- {{ j.stage || '--' }}
1481
- </td>
1482
- <td class="prog-cell">
1483
- <span class="prog"><i
1484
- :style="{ width: `${Math.max(0, Math.min(100, j.progress))}%` }"
1485
- :class="{ done: j.status === 'done', bad: j.status === 'failed' }"
1486
- /></span>
1487
- <span class="mono prog-num">{{ Math.round(j.progress) }}%</span>
1488
- </td>
1489
- <td class="mono dim">
1490
- {{ fmtTime(j.createdAt) }}
1491
- </td>
1492
- <td class="right acts">
1493
- <button
1494
- v-if="isActiveStatus(j.status)"
1495
- class="mini-btn danger"
1496
- :disabled="cancelling === j.id"
1497
- @click.stop="onCancelJob(j.id)"
1498
- >
1499
- {{ confirmCancel === j.id ? $t('aml.k1amlx094') : (cancelling === j.id ? $t('aml.k1amlx093') : $t('aml.k1amlx071')) }}
1500
- </button>
1501
- <button
1502
- v-if="canRetry(j.status)"
1503
- class="mini-btn"
1504
- :disabled="retrying === j.id"
1505
- @click.stop="onRetryJob(j.id)"
1506
- >
1507
- {{ confirmRetry === j.id ? $t('aml.k1amlx096') : $t('aml.k1amlx095') }}
1508
- </button>
1509
- <span
1510
- v-if="!isActiveStatus(j.status)"
1511
- class="i-tabler-trash edit-i"
1512
- :title="$t('aml.k1amlx190')"
1513
- @click.stop="removeEntity('jobs', j.id, $t('aml.k1amlx200'))"
1514
- />
1515
- </td>
1447
+ <table class="tbl tbl-jobs">
1448
+ <thead>
1449
+ <tr>
1450
+ <th>{{ $t('aml.k1amlx076') }}</th>
1451
+ <th>{{ $t('aml.k1amlx011') }}</th>
1452
+ <th>{{ $t('aml.k1amlx066') }}</th>
1453
+ <th>{{ $t('aml.k1amlx077') }}</th>
1454
+ <th>{{ $t('aml.k1amlx078') }}</th>
1455
+ <th class="prog-th">
1456
+ {{ $t('aml.k1amlx079') }}
1457
+ </th>
1458
+ <th>{{ $t('aml.k1amlx020') }}</th>
1459
+ <th class="right">
1460
+ {{ $t('aml.k1amlx127') }}
1461
+ </th>
1516
1462
  </tr>
1517
- <tr
1518
- v-if="expandedJob === j.id"
1519
- class="detail-row"
1463
+ </thead>
1464
+ <tbody>
1465
+ <template
1466
+ v-for="j in jobRows"
1467
+ :key="j.id"
1520
1468
  >
1521
- <td colspan="8">
1522
- <div
1523
- v-if="jobDetails[j.id]?.loading"
1524
- class="dim pad"
1525
- >
1526
- {{ $t('aml.k1amlx030') }}
1527
- </div>
1528
- <div
1529
- v-else-if="jobDetails[j.id]?.error"
1530
- class="err pad"
1531
- >
1532
- {{ jobDetails[j.id]?.error }}
1533
- </div>
1534
- <div
1535
- v-else
1536
- class="det-grid"
1537
- >
1538
- <div>
1539
- <p class="det-title">
1540
- {{ $t('aml.k1amlx083') }}
1541
- </p>
1542
- <table
1543
- v-if="jobDetails[j.id]?.gates?.checks.length"
1544
- class="sub-tbl"
1545
- >
1546
- <thead>
1547
- <tr>
1548
- <th>{{ $t('aml.k1amlx084') }}</th>
1549
- <th class="num">
1550
- {{ $t('aml.k1amlx085') }}
1551
- </th>
1552
- <th class="num">
1553
- {{ $t('aml.k1amlx086') }}
1554
- </th>
1555
- <th>{{ $t('aml.k1amlx118') }}</th>
1556
- </tr>
1557
- </thead>
1558
- <tbody>
1559
- <tr
1560
- v-for="g in jobDetails[j.id]?.gates?.checks"
1561
- :key="g.id"
1562
- >
1563
- <td>
1564
- <b class="mono gate-id">{{ g.id }}</b>
1565
- <small class="dim"> {{ g.name }}</small>
1566
- </td>
1567
- <td class="mono num">
1568
- {{ fmtNum(g.value) }}
1569
- </td>
1570
- <td class="mono num dim">
1571
- {{ fmtNum(g.threshold) }}
1572
- </td>
1573
- <td>
1574
- <span
1575
- class="verdict"
1576
- :class="g.pass ? 'pass' : 'fail'"
1577
- >{{ g.pass ? $t('aml.k1amlx087') : $t('aml.k1amlx088') }}</span>
1578
- <small class="dim gate-detail">{{ g.detail }}</small>
1579
- </td>
1580
- </tr>
1581
- </tbody>
1582
- </table>
1583
- <p
1584
- v-else
1585
- class="dim"
1586
- >
1587
- {{ $t('aml.k1amlx082') }}
1588
- </p>
1589
- <p
1590
- v-if="jobDetails[j.id]?.metrics?.oneStepTest"
1591
- class="mono dim gate-detail"
1592
- >
1593
- {{ $t('aml.k1amlx099') }}: {{ $t('aml.k1amlx116') }} {{ fmtNum(jobDetails[j.id]?.metrics?.oneStepTest?.nrmse) }}
1594
- <template v-if="jobDetails[j.id]?.metrics?.rolloutTest">
1595
- · {{ $t('aml.k1amlx117') }} {{ fmtNum(jobDetails[j.id]?.metrics?.rolloutTest?.nrmse) }}
1596
- </template>
1597
- </p>
1598
- <p
1599
- v-if="j.error"
1600
- class="err gate-detail"
1601
- >
1602
- {{ $t('aml.k1amlx092') }}:{{ j.error }}
1603
- </p>
1469
+ <tr
1470
+ class="row-main"
1471
+ :class="{ open: expandedJob === j.id }"
1472
+ @click="toggleJob(j.id)"
1473
+ >
1474
+ <td class="mono">
1475
+ {{ shortId(j.id) }}
1476
+ </td>
1477
+ <td class="mono dim">
1478
+ {{ shortId(j.datasetId) }}
1479
+ </td>
1480
+ <td class="mono dim">
1481
+ {{ j.purpose }}
1482
+ </td>
1483
+ <td>
1484
+ <span
1485
+ class="st-pill"
1486
+ :class="j.status"
1487
+ >{{ statusLabel(j.status) }}</span>
1488
+ </td>
1489
+ <td class="mono dim">
1490
+ {{ j.stage || '--' }}
1491
+ </td>
1492
+ <td class="prog-cell">
1493
+ <span class="prog"><i
1494
+ :style="{ width: `${Math.max(0, Math.min(100, j.progress))}%` }"
1495
+ :class="{ done: j.status === 'done', bad: j.status === 'failed' }"
1496
+ /></span>
1497
+ <span class="mono prog-num">{{ Math.round(j.progress) }}%</span>
1498
+ </td>
1499
+ <td class="mono dim">
1500
+ {{ fmtTime(j.createdAt) }}
1501
+ </td>
1502
+ <td class="right acts">
1503
+ <button
1504
+ v-if="isActiveStatus(j.status)"
1505
+ class="mini-btn danger"
1506
+ :disabled="cancelling === j.id"
1507
+ @click.stop="onCancelJob(j.id)"
1508
+ >
1509
+ {{ confirmCancel === j.id ? $t('aml.k1amlx094') : (cancelling === j.id ? $t('aml.k1amlx093') : $t('aml.k1amlx071')) }}
1510
+ </button>
1511
+ <button
1512
+ v-if="canRetry(j.status)"
1513
+ class="mini-btn"
1514
+ :disabled="retrying === j.id"
1515
+ @click.stop="onRetryJob(j.id)"
1516
+ >
1517
+ {{ confirmRetry === j.id ? $t('aml.k1amlx096') : $t('aml.k1amlx095') }}
1518
+ </button>
1519
+ <span
1520
+ v-if="!isActiveStatus(j.status)"
1521
+ class="i-tabler-trash edit-i"
1522
+ :title="$t('aml.k1amlx190')"
1523
+ @click.stop="removeEntity('jobs', j.id, $t('aml.k1amlx200'))"
1524
+ />
1525
+ </td>
1526
+ </tr>
1527
+ <tr
1528
+ v-if="expandedJob === j.id"
1529
+ class="detail-row"
1530
+ >
1531
+ <td colspan="8">
1532
+ <div
1533
+ v-if="jobDetails[j.id]?.loading"
1534
+ class="dim pad"
1535
+ >
1536
+ {{ $t('aml.k1amlx030') }}
1604
1537
  </div>
1605
- <div>
1606
- <p class="det-title">
1607
- {{ $t('aml.k1amlx089') }}
1608
- <span
1609
- v-if="isActiveStatus(j.status)"
1610
- class="mono live-hint"
1611
- >{{ $t('aml.k1amlx091') }}</span>
1612
- </p>
1613
- <pre class="logs">{{ (jobDetails[j.id]?.logs ?? []).join('\n') || $t('aml.k1amlx090') }}</pre>
1538
+ <div
1539
+ v-else-if="jobDetails[j.id]?.error"
1540
+ class="err pad"
1541
+ >
1542
+ {{ jobDetails[j.id]?.error }}
1614
1543
  </div>
1615
- </div>
1616
- </td>
1617
- </tr>
1618
- </template>
1619
- </tbody>
1620
- </table>
1544
+ <div
1545
+ v-else
1546
+ class="det-grid"
1547
+ >
1548
+ <div>
1549
+ <p class="det-title">
1550
+ {{ $t('aml.k1amlx083') }}
1551
+ </p>
1552
+ <table
1553
+ v-if="jobDetails[j.id]?.gates?.checks.length"
1554
+ class="sub-tbl"
1555
+ >
1556
+ <thead>
1557
+ <tr>
1558
+ <th>{{ $t('aml.k1amlx084') }}</th>
1559
+ <th class="num">
1560
+ {{ $t('aml.k1amlx085') }}
1561
+ </th>
1562
+ <th class="num">
1563
+ {{ $t('aml.k1amlx086') }}
1564
+ </th>
1565
+ <th>{{ $t('aml.k1amlx118') }}</th>
1566
+ </tr>
1567
+ </thead>
1568
+ <tbody>
1569
+ <tr
1570
+ v-for="g in jobDetails[j.id]?.gates?.checks"
1571
+ :key="g.id"
1572
+ >
1573
+ <td>
1574
+ <b class="mono gate-id">{{ g.id }}</b>
1575
+ <small class="dim"> {{ g.name }}</small>
1576
+ </td>
1577
+ <td class="mono num">
1578
+ {{ fmtNum(g.value) }}
1579
+ </td>
1580
+ <td class="mono num dim">
1581
+ {{ fmtNum(g.threshold) }}
1582
+ </td>
1583
+ <td>
1584
+ <span
1585
+ class="verdict"
1586
+ :class="g.pass ? 'pass' : 'fail'"
1587
+ >{{ g.pass ? $t('aml.k1amlx087') : $t('aml.k1amlx088') }}</span>
1588
+ <small class="dim gate-detail">{{ g.detail }}</small>
1589
+ </td>
1590
+ </tr>
1591
+ </tbody>
1592
+ </table>
1593
+ <p
1594
+ v-else
1595
+ class="dim"
1596
+ >
1597
+ {{ $t('aml.k1amlx082') }}
1598
+ </p>
1599
+ <p
1600
+ v-if="jobDetails[j.id]?.metrics?.oneStepTest"
1601
+ class="mono dim gate-detail"
1602
+ >
1603
+ {{ $t('aml.k1amlx099') }}: {{ $t('aml.k1amlx116') }} {{ fmtNum(jobDetails[j.id]?.metrics?.oneStepTest?.nrmse) }}
1604
+ <template v-if="jobDetails[j.id]?.metrics?.rolloutTest">
1605
+ · {{ $t('aml.k1amlx117') }} {{ fmtNum(jobDetails[j.id]?.metrics?.rolloutTest?.nrmse) }}
1606
+ </template>
1607
+ </p>
1608
+ <p
1609
+ v-if="j.error"
1610
+ class="err gate-detail"
1611
+ >
1612
+ {{ $t('aml.k1amlx092') }}:{{ j.error }}
1613
+ </p>
1614
+ </div>
1615
+ <div>
1616
+ <p class="det-title">
1617
+ {{ $t('aml.k1amlx089') }}
1618
+ <span
1619
+ v-if="isActiveStatus(j.status)"
1620
+ class="mono live-hint"
1621
+ >{{ $t('aml.k1amlx091') }}</span>
1622
+ </p>
1623
+ <pre class="logs">{{ (jobDetails[j.id]?.logs ?? []).join('\n') || $t('aml.k1amlx090') }}</pre>
1624
+ </div>
1625
+ </div>
1626
+ </td>
1627
+ </tr>
1628
+ </template>
1629
+ </tbody>
1630
+ </table>
1631
+ </div>
1621
1632
  <p
1622
1633
  v-else
1623
1634
  class="empty"
@@ -1660,71 +1671,73 @@ async function doPredict(): Promise<void> {
1660
1671
  </button>
1661
1672
  </div>
1662
1673
  </div>
1663
- <table
1674
+ <div
1664
1675
  v-if="experiments.length"
1665
- class="tbl"
1676
+ class="tbl-scroll"
1666
1677
  >
1667
- <thead>
1668
- <tr>
1669
- <th>{{ $t('aml.k1amlx113') }}</th>
1670
- <th>{{ $t('aml.k1amlx020') }}</th>
1671
- <th>{{ $t('aml.k1amlx114') }}</th>
1672
- <th>{{ $t('aml.k1amlx115') }}</th>
1673
- <th
1674
- class="num"
1675
- :title="$t('aml.k1amlx121')"
1676
- >
1677
- {{ $t('aml.k1amlx116') }}
1678
- </th>
1679
- <th
1680
- class="num"
1681
- :title="$t('aml.k1amlx122')"
1678
+ <table class="tbl tbl-exp">
1679
+ <thead>
1680
+ <tr>
1681
+ <th>{{ $t('aml.k1amlx113') }}</th>
1682
+ <th>{{ $t('aml.k1amlx020') }}</th>
1683
+ <th>{{ $t('aml.k1amlx114') }}</th>
1684
+ <th>{{ $t('aml.k1amlx115') }}</th>
1685
+ <th
1686
+ class="num"
1687
+ :title="$t('aml.k1amlx121')"
1688
+ >
1689
+ {{ $t('aml.k1amlx116') }}
1690
+ </th>
1691
+ <th
1692
+ class="num"
1693
+ :title="$t('aml.k1amlx122')"
1694
+ >
1695
+ {{ $t('aml.k1amlx117') }}
1696
+ </th>
1697
+ <th>{{ $t('aml.k1amlx118') }}</th>
1698
+ </tr>
1699
+ </thead>
1700
+ <tbody>
1701
+ <tr
1702
+ v-for="e in experiments"
1703
+ :key="e.id"
1704
+ :class="{ best: e.id === bestExpId }"
1682
1705
  >
1683
- {{ $t('aml.k1amlx117') }}
1684
- </th>
1685
- <th>{{ $t('aml.k1amlx118') }}</th>
1686
- </tr>
1687
- </thead>
1688
- <tbody>
1689
- <tr
1690
- v-for="e in experiments"
1691
- :key="e.id"
1692
- :class="{ best: e.id === bestExpId }"
1693
- >
1694
- <td class="mono">
1695
- <span
1696
- v-if="e.id === bestExpId"
1697
- class="best-tag"
1698
- >{{ $t('aml.k1amlx120') }}</span>
1699
- {{ shortId(e.id) }}
1700
- </td>
1701
- <td class="mono dim">
1702
- {{ fmtTime(e.createdAt) }}
1703
- </td>
1704
- <td class="note-cell">
1705
- <span
1706
- class="note"
1707
- :title="e.changeNote"
1708
- >{{ e.changeNote || '--' }}</span>
1709
- </td>
1710
- <td class="mono dim">
1711
- {{ e.parentExperimentId ? shortId(e.parentExperimentId) : '--' }}
1712
- </td>
1713
- <td class="mono num">
1714
- {{ fmtNum(e.metrics?.oneStepTest?.nrmse) }}
1715
- </td>
1716
- <td class="mono num">
1717
- {{ fmtNum(e.metrics?.rolloutTest?.nrmse) }}
1718
- </td>
1719
- <td>
1720
- <span
1721
- class="st-pill"
1722
- :class="e.status"
1723
- >{{ expStatusLabel(e.status) }}</span>
1724
- </td>
1725
- </tr>
1726
- </tbody>
1727
- </table>
1706
+ <td class="mono">
1707
+ <span
1708
+ v-if="e.id === bestExpId"
1709
+ class="best-tag"
1710
+ >{{ $t('aml.k1amlx120') }}</span>
1711
+ {{ shortId(e.id) }}
1712
+ </td>
1713
+ <td class="mono dim">
1714
+ {{ fmtTime(e.createdAt) }}
1715
+ </td>
1716
+ <td class="note-cell">
1717
+ <span
1718
+ class="note"
1719
+ :title="e.changeNote"
1720
+ >{{ e.changeNote || '--' }}</span>
1721
+ </td>
1722
+ <td class="mono dim">
1723
+ {{ e.parentExperimentId ? shortId(e.parentExperimentId) : '--' }}
1724
+ </td>
1725
+ <td class="mono num">
1726
+ {{ fmtNum(e.metrics?.oneStepTest?.nrmse) }}
1727
+ </td>
1728
+ <td class="mono num">
1729
+ {{ fmtNum(e.metrics?.rolloutTest?.nrmse) }}
1730
+ </td>
1731
+ <td>
1732
+ <span
1733
+ class="st-pill"
1734
+ :class="e.status"
1735
+ >{{ expStatusLabel(e.status) }}</span>
1736
+ </td>
1737
+ </tr>
1738
+ </tbody>
1739
+ </table>
1740
+ </div>
1728
1741
  <p
1729
1742
  v-else
1730
1743
  class="empty"
@@ -1754,121 +1767,123 @@ async function doPredict(): Promise<void> {
1754
1767
  {{ $t('aml.k1amlx017') }}
1755
1768
  </button>
1756
1769
  </div>
1757
- <table
1770
+ <div
1758
1771
  v-if="models.length"
1759
- class="tbl"
1772
+ class="tbl-scroll"
1760
1773
  >
1761
- <thead>
1762
- <tr>
1763
- <th>{{ $t('aml.k1amlx125') }}</th>
1764
- <th>{{ $t('aml.k1amlx021') }}</th>
1765
- <th>{{ $t('aml.k1amlx022') }}</th>
1766
- <th>{{ $t('aml.k1amlx078') }}</th>
1767
- <th :title="$t('aml.k1amlx126')">
1768
- {{ $t('aml.k1amlx099') }}
1769
- </th>
1770
- <th>{{ $t('aml.k1amlx020') }}</th>
1771
- <th>{{ $t('aml.k1amlx026') }}</th>
1772
- <th class="right">
1773
- {{ $t('aml.k1amlx127') }}
1774
- </th>
1775
- </tr>
1776
- </thead>
1777
- <tbody>
1778
- <tr
1779
- v-for="m in models"
1780
- :key="m.id"
1781
- >
1782
- <td class="mono">
1783
- {{ shortId(m.id) }}
1784
- </td>
1785
- <td class="mono dim">
1786
- {{ shortId(m.productId) }}
1787
- </td>
1788
- <td class="mono dim">
1789
- {{ shortId(m.recipeId) }}
1790
- </td>
1791
- <td>
1792
- <span
1793
- class="stage-pill"
1794
- :class="m.stage"
1795
- >{{ m.stage }}</span>
1796
- </td>
1797
- <td class="mono dim">
1798
- {{ $t('aml.k1amlx116') }} {{ fmtNum(m.metrics?.oneStepTest?.nrmse) }} · {{ $t('aml.k1amlx117') }} {{ fmtNum(m.metrics?.rolloutTest?.nrmse) }}
1799
- </td>
1800
- <td class="mono dim">
1801
- {{ fmtTime(m.createdAt) }}
1802
- </td>
1803
- <td
1804
- class="note-cell"
1805
- @click.stop
1774
+ <table class="tbl tbl-models">
1775
+ <thead>
1776
+ <tr>
1777
+ <th>{{ $t('aml.k1amlx125') }}</th>
1778
+ <th>{{ $t('aml.k1amlx021') }}</th>
1779
+ <th>{{ $t('aml.k1amlx022') }}</th>
1780
+ <th>{{ $t('aml.k1amlx078') }}</th>
1781
+ <th :title="$t('aml.k1amlx126')">
1782
+ {{ $t('aml.k1amlx099') }}
1783
+ </th>
1784
+ <th>{{ $t('aml.k1amlx020') }}</th>
1785
+ <th>{{ $t('aml.k1amlx026') }}</th>
1786
+ <th class="right">
1787
+ {{ $t('aml.k1amlx127') }}
1788
+ </th>
1789
+ </tr>
1790
+ </thead>
1791
+ <tbody>
1792
+ <tr
1793
+ v-for="m in models"
1794
+ :key="m.id"
1806
1795
  >
1807
- <div
1808
- v-if="noteEditing === m.id"
1809
- class="note-edit"
1796
+ <td class="mono">
1797
+ {{ shortId(m.id) }}
1798
+ </td>
1799
+ <td class="mono dim">
1800
+ {{ shortId(m.productId) }}
1801
+ </td>
1802
+ <td class="mono dim">
1803
+ {{ shortId(m.recipeId) }}
1804
+ </td>
1805
+ <td>
1806
+ <span
1807
+ class="stage-pill"
1808
+ :class="m.stage"
1809
+ >{{ m.stage }}</span>
1810
+ </td>
1811
+ <td class="mono dim">
1812
+ {{ $t('aml.k1amlx116') }} {{ fmtNum(m.metrics?.oneStepTest?.nrmse) }} · {{ $t('aml.k1amlx117') }} {{ fmtNum(m.metrics?.rolloutTest?.nrmse) }}
1813
+ </td>
1814
+ <td class="mono dim">
1815
+ {{ fmtTime(m.createdAt) }}
1816
+ </td>
1817
+ <td
1818
+ class="note-cell"
1819
+ @click.stop
1810
1820
  >
1811
- <input
1812
- v-model="noteDraft"
1813
- :placeholder="$t('aml.k1amlx189')"
1814
- @keyup.enter="saveNote('models', m.id)"
1815
- @keyup.esc="cancelEditNote"
1821
+ <div
1822
+ v-if="noteEditing === m.id"
1823
+ class="note-edit"
1816
1824
  >
1817
- <button
1818
- class="mini-btn"
1819
- @click="saveNote('models', m.id)"
1825
+ <input
1826
+ v-model="noteDraft"
1827
+ :placeholder="$t('aml.k1amlx189')"
1828
+ @keyup.enter="saveNote('models', m.id)"
1829
+ @keyup.esc="cancelEditNote"
1830
+ >
1831
+ <button
1832
+ class="mini-btn"
1833
+ @click="saveNote('models', m.id)"
1834
+ >
1835
+ {{ $t('aml.k1amlx191') }}
1836
+ </button>
1837
+ <button
1838
+ class="mini-btn"
1839
+ @click="cancelEditNote"
1840
+ >
1841
+ {{ $t('aml.k1amlx202') }}
1842
+ </button>
1843
+ </div>
1844
+ <span
1845
+ v-else
1846
+ class="note-text"
1820
1847
  >
1821
- {{ $t('aml.k1amlx191') }}
1822
- </button>
1823
- <button
1824
- class="mini-btn"
1825
- @click="cancelEditNote"
1848
+ <span
1849
+ class="note"
1850
+ :title="m.note"
1851
+ >{{ m.note || '--' }}</span>
1852
+ <span
1853
+ class="i-tabler-pencil edit-i"
1854
+ :title="$t('aml.k1amlx189')"
1855
+ @click="startEditNote(m.id, m.note)"
1856
+ />
1857
+ </span>
1858
+ </td>
1859
+ <td class="right acts">
1860
+ <template
1861
+ v-for="a in promoteActionOf(m)"
1862
+ :key="a.to"
1826
1863
  >
1827
- {{ $t('aml.k1amlx202') }}
1828
- </button>
1829
- </div>
1830
- <span
1831
- v-else
1832
- class="note-text"
1833
- >
1864
+ <button
1865
+ class="mini-btn"
1866
+ :class="{ danger: a.to === 'retired' }"
1867
+ :disabled="promoting === `${m.id}:${a.to}`"
1868
+ @click="onPromote(m, a.to)"
1869
+ >
1870
+ {{ confirmPromote === `${m.id}:${a.to}` ? $t('aml.k1amlx131') : $t(a.key) }}
1871
+ </button>
1872
+ </template>
1834
1873
  <span
1835
- class="note"
1836
- :title="m.note"
1837
- >{{ m.note || '--' }}</span>
1874
+ v-if="promoteActionOf(m).length === 0"
1875
+ class="dim"
1876
+ >--</span>
1838
1877
  <span
1839
- class="i-tabler-pencil edit-i"
1840
- :title="$t('aml.k1amlx189')"
1841
- @click="startEditNote(m.id, m.note)"
1878
+ class="i-tabler-trash edit-i"
1879
+ :title="$t('aml.k1amlx190')"
1880
+ @click="removeEntity('models', m.id, $t('aml.k1amlx199'))"
1842
1881
  />
1843
- </span>
1844
- </td>
1845
- <td class="right acts">
1846
- <template
1847
- v-for="a in promoteActionOf(m)"
1848
- :key="a.to"
1849
- >
1850
- <button
1851
- class="mini-btn"
1852
- :class="{ danger: a.to === 'retired' }"
1853
- :disabled="promoting === `${m.id}:${a.to}`"
1854
- @click="onPromote(m, a.to)"
1855
- >
1856
- {{ confirmPromote === `${m.id}:${a.to}` ? $t('aml.k1amlx131') : $t(a.key) }}
1857
- </button>
1858
- </template>
1859
- <span
1860
- v-if="promoteActionOf(m).length === 0"
1861
- class="dim"
1862
- >--</span>
1863
- <span
1864
- class="i-tabler-trash edit-i"
1865
- :title="$t('aml.k1amlx190')"
1866
- @click="removeEntity('models', m.id, $t('aml.k1amlx199'))"
1867
- />
1868
- </td>
1869
- </tr>
1870
- </tbody>
1871
- </table>
1882
+ </td>
1883
+ </tr>
1884
+ </tbody>
1885
+ </table>
1886
+ </div>
1872
1887
  <p
1873
1888
  v-else
1874
1889
  class="empty"
@@ -2244,7 +2259,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2244
2259
  .badges { display: flex; gap: 8px; }
2245
2260
  .badge {
2246
2261
  padding: 3px 10px;
2247
- font-size: 11px;
2262
+ font-size: 11.5px;
2248
2263
  letter-spacing: 0.05em;
2249
2264
  color: var(--ink-soft);
2250
2265
  border: 1px solid var(--line-strong);
@@ -2256,13 +2271,21 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2256
2271
  .infra-banner {
2257
2272
  display: flex;
2258
2273
  gap: 10px;
2259
- align-items: center;
2274
+ /* 顶端对齐,不是居中:文案换行成 3 行时,居中的图标会停在**第二行**行首,
2275
+ * 看起来像一个走错位置的孤零零的三角(实测 /aml)。图标永远该跟第一行文字对齐。 */
2276
+ align-items: flex-start;
2260
2277
  padding: 10px 14px;
2261
2278
  margin-bottom: 14px;
2262
2279
  color: var(--tone-warning-dot);
2263
2280
  background: var(--tone-warning-bg);
2264
2281
  border: 1px solid color-mix(in srgb, var(--tone-warning-dot) 40%, transparent);
2265
- border-radius: var(--radius-chip);
2282
+ /* 多行横幅用面板圆角:chip 圆角是给单行药丸的,套在三行块上会显得没画完 */
2283
+ border-radius: var(--radius-panel-sm, 10px);
2284
+ }
2285
+
2286
+ .infra-banner > span:first-child {
2287
+ flex: none;
2288
+ margin-top: 2px;
2266
2289
  }
2267
2290
  .infra-banner .txt { flex: 1 1 auto; font-size: 12.5px; line-height: 1.5; }
2268
2291
 
@@ -2278,10 +2301,12 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2278
2301
  .ov-row .reload { margin-left: auto; }
2279
2302
 
2280
2303
  /* ── 通用区块 ── */
2281
- .zone { padding: 12px 18px 14px; margin-bottom: 14px; overflow-x: auto; }
2282
- .zone-head { display: flex; gap: 12px; align-items: center; justify-content: space-between; margin-bottom: 10px; }
2283
- .zone-head h2 { display: inline-flex; gap: 8px; align-items: center; margin: 0; font-size: 15px; font-weight: 600; letter-spacing: 0.01em; }
2284
- .zone-head h2 .cnt { font-size: 11px; font-weight: 400; color: var(--ink-faint); }
2304
+ /* 区块不再整块横扫 —— 那会让区块标题跟着一起滚走,也顺手豁免了
2305
+ 内部元素的越界检测。滚动权下放给 .tbl-scroll(只裹表格本体)。 */
2306
+ .zone { padding: 12px 18px 14px; margin-bottom: 14px; }
2307
+ .zone-head { display: flex; flex-wrap: wrap; gap: 10px 12px; align-items: center; justify-content: space-between; margin-bottom: 10px; }
2308
+ .zone-head h2 { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0; font-size: 15px; font-weight: 600; letter-spacing: 0.01em; }
2309
+ .zone-head h2 .cnt { font-size: 11.5px; font-weight: 400; color: var(--ink-faint); }
2285
2310
  .zone-actions { display: flex; gap: 8px; align-items: center; }
2286
2311
 
2287
2312
  /* ── 1b. 运行环境面板 ── */
@@ -2302,18 +2327,18 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2302
2327
  .env-card.ok { border-color: color-mix(in srgb, var(--tone-success-dot) 35%, transparent); }
2303
2328
  .env-card.bad { border-color: color-mix(in srgb, var(--tone-warning-dot) 40%, transparent); }
2304
2329
  .env-card .ec-head { display: flex; gap: 6px; align-items: center; font-size: 12.5px; }
2305
- .env-card .ec-head .ec-state { margin-left: auto; font-size: 11px; color: var(--ink-faint); }
2330
+ .env-card .ec-head .ec-state { margin-left: auto; font-size: 11.5px; color: var(--ink-faint); }
2306
2331
  .env-card.ok .ec-head > span:first-child { color: var(--tone-success-dot); }
2307
2332
  .env-card.bad .ec-head > span:first-child { color: var(--tone-warning-dot); }
2308
2333
  .env-card .ec-detail { margin: 0; font-size: 11.5px; line-height: 1.55; color: var(--ink-soft); overflow-wrap: anywhere; }
2309
2334
  .env-card .ec-detail.dim { color: var(--ink-faint); }
2310
2335
  .env-card .ec-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; padding-top: 4px; }
2311
- .env-card .ec-issues { max-height: 96px; margin: 0; padding: 0; overflow-y: auto; list-style: none; font-size: 11px; }
2336
+ .env-card .ec-issues { max-height: 96px; margin: 0; padding: 0; overflow-y: auto; list-style: none; font-size: 11.5px; }
2312
2337
  .env-card .ec-issues li { display: flex; gap: 6px; align-items: center; padding: 1px 0; color: var(--ink-soft); }
2313
2338
  .env-card .ec-issues .tag {
2314
2339
  flex: none;
2315
2340
  padding: 0 5px;
2316
- font-size: 10px;
2341
+ font-size: 11.5px;
2317
2342
  color: var(--tone-warning-dot);
2318
2343
  background: var(--tone-warning-bg);
2319
2344
  border-radius: 3px;
@@ -2327,7 +2352,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2327
2352
  padding: 8px 10px;
2328
2353
  margin: 6px 0 0;
2329
2354
  overflow: auto;
2330
- font-size: 11px;
2355
+ font-size: 11.5px;
2331
2356
  line-height: 1.5;
2332
2357
  white-space: pre-wrap;
2333
2358
  background: var(--surface-sunken, color-mix(in srgb, var(--ink) 4%, transparent));
@@ -2370,7 +2395,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2370
2395
  .tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
2371
2396
  .tbl th, .tbl td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--divider-hair); }
2372
2397
  .tbl th {
2373
- font-size: 11px;
2398
+ font-size: 11.5px;
2374
2399
  font-weight: 600;
2375
2400
  letter-spacing: 0.06em;
2376
2401
  text-transform: uppercase;
@@ -2391,7 +2416,7 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2391
2416
  .kind {
2392
2417
  margin-left: 6px;
2393
2418
  padding: 1px 6px;
2394
- font-size: 10px;
2419
+ font-size: 11.5px;
2395
2420
  border: 1px solid var(--line);
2396
2421
  border-radius: 99px;
2397
2422
  color: var(--ink-faint);
@@ -2404,23 +2429,24 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2404
2429
  @media (max-width: 1100px) {
2405
2430
  .det-grid { grid-template-columns: 1fr; }
2406
2431
  }
2407
- .det-title { margin: 10px 0 6px; font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); }
2432
+ .det-title { margin: 10px 0 6px; font-size: 11.5px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); }
2408
2433
  .det-title:first-child { margin-top: 10px; }
2409
2434
  .sub-tbl { width: 100%; border-collapse: collapse; font-size: 12px; }
2410
2435
  .sub-tbl th, .sub-tbl td { padding: 5px 8px; text-align: left; border-bottom: 1px solid var(--divider-hair); }
2411
- .sub-tbl th { font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-faint); }
2436
+ .sub-tbl th { font-size: 11.5px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-faint); }
2412
2437
  .sub-tbl .num { text-align: right; }
2413
- .role-chip { padding: 1px 6px; font-family: var(--font-mono); font-size: 10px; background: var(--paper-deep); border-radius: 5px; color: var(--ink-soft); }
2414
- .run-line { margin: 4px 0; font-size: 11.5px; }
2438
+ .role-chip { padding: 1px 6px; font-family: var(--font-mono); font-size: 11.5px; background: var(--paper-deep); border-radius: 5px; color: var(--ink-soft); }
2439
+ .run-line { margin: 4px 0; font-size: 11.5px; overflow-wrap: anywhere; }
2415
2440
  .run-line b { color: var(--ink); }
2416
- .drop-line { margin: 2px 0; overflow: hidden; font-size: 11px; color: var(--tone-danger-dot); text-overflow: ellipsis; white-space: nowrap; }
2441
+ /* 丢弃原因整行可见:窄屏不再 ellipsis 硬裁(审计的 text-clipped 来源之一) */
2442
+ .drop-line { margin: 2px 0; font-size: 11.5px; color: var(--tone-danger-dot); overflow-wrap: anywhere; }
2417
2443
 
2418
2444
  /* 作业状态 pill / 进度条 */
2419
2445
  .st-pill {
2420
2446
  display: inline-block;
2421
- padding: 2px 9px;
2447
+ padding: 3px 9px;
2422
2448
  font-family: var(--font-mono);
2423
- font-size: 10.5px;
2449
+ font-size: 11.5px;
2424
2450
  letter-spacing: 0.04em;
2425
2451
  border-radius: var(--radius-pill);
2426
2452
  }
@@ -2444,15 +2470,15 @@ h1 { margin: 2px 0 4px; font-size: 30px; font-weight: 400; letter-spacing: -0.01
2444
2470
  .prog i { display: block; height: 100%; background: var(--tone-info-dot); border-radius: 3px; transition: width 0.4s ease; }
2445
2471
  .prog i.done { background: var(--tone-success-dot); }
2446
2472
  .prog i.bad { background: var(--tone-danger-dot); }
2447
- .prog-num { margin-left: 7px; font-size: 11px; color: var(--ink-soft); }
2473
+ .prog-num { margin-left: 7px; font-size: 11.5px; color: var(--ink-soft); }
2448
2474
 
2449
2475
  /* 门禁 */
2450
2476
  .gate-id { font-weight: 700; }
2451
2477
  .gate-detail { display: block; margin-top: 3px; font-size: 11px; }
2452
- .verdict { padding: 1px 7px; font-size: 10.5px; border-radius: 99px; }
2478
+ .verdict { padding: 1px 7px; font-size: 11.5px; border-radius: 99px; }
2453
2479
  .verdict.pass { color: var(--tone-success-dot); background: var(--tone-success-bg); }
2454
2480
  .verdict.fail { color: var(--tone-danger-dot); background: var(--tone-danger-bg); }
2455
- .live-hint { margin-left: 8px; font-size: 10px; font-weight: 400; letter-spacing: 0; color: var(--tone-info-dot); text-transform: none; }
2481
+ .live-hint { margin-left: 8px; font-size: 11.5px; font-weight: 400; letter-spacing: 0; color: var(--tone-info-dot); text-transform: none; }
2456
2482
  .logs {
2457
2483
  max-height: 260px;
2458
2484
  margin: 0;
@@ -2475,7 +2501,7 @@ tr.best td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
2475
2501
  .best-tag {
2476
2502
  margin-right: 6px;
2477
2503
  padding: 1px 7px;
2478
- font-size: 10px;
2504
+ font-size: 11.5px;
2479
2505
  color: var(--on-accent);
2480
2506
  background: var(--accent);
2481
2507
  border-radius: 99px;
@@ -2484,9 +2510,9 @@ tr.best td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
2484
2510
  /* 模型阶段徽标 */
2485
2511
  .stage-pill {
2486
2512
  display: inline-block;
2487
- padding: 2px 9px;
2513
+ padding: 3px 9px;
2488
2514
  font-family: var(--font-mono);
2489
- font-size: 10.5px;
2515
+ font-size: 11.5px;
2490
2516
  letter-spacing: 0.04em;
2491
2517
  border: 1px solid var(--line-strong);
2492
2518
  border-radius: var(--radius-pill);
@@ -2568,4 +2594,74 @@ tr.best td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
2568
2594
  .m-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 12px; }
2569
2595
 
2570
2596
  .empty { margin: 6px 0; padding: 18px 0; font-size: 12.5px; color: var(--ink-faint); text-align: center; }
2597
+
2598
+ /* ══ 窄屏自适应层(≤900 手持/平板竖)═════════════════════════════════════
2599
+ 作业/实验/数据集/模型四张表在 355px 画布里被压成"每列 40px",
2600
+ 单元格文字会折成单字竖排 —— 保留列语义,给表一条可横扫的卷轴,
2601
+ 并把首列(身份列)钉在左缘,横扫时始终知道"这一行是谁"。 */
2602
+ .tbl-scroll {
2603
+ max-width: 100%;
2604
+ overflow-x: auto;
2605
+ -webkit-overflow-scrolling: touch;
2606
+ }
2607
+
2608
+ @media (max-width: 900px) {
2609
+ .tbl-scroll {
2610
+ overflow-x: auto;
2611
+ scrollbar-width: thin;
2612
+ }
2613
+
2614
+ /* 列不再被压扁:按各自列数给一个诚实的最小宽度,超出即横扫 */
2615
+ .tbl-datasets { min-width: 720px; }
2616
+ .tbl-jobs { min-width: 860px; }
2617
+ .tbl-exp { min-width: 700px; }
2618
+ .tbl-models { min-width: 780px; }
2619
+
2620
+ .tbl th,
2621
+ .tbl td { white-space: nowrap; }
2622
+
2623
+ /* 身份列钉在左缘(与 main.css 对 .ant-table 的处理同一套语言) */
2624
+ .tbl thead > tr > th:first-child,
2625
+ .tbl tbody > tr > td:first-child {
2626
+ position: sticky;
2627
+ left: 0;
2628
+ z-index: 2;
2629
+ background: var(--paper-raised);
2630
+ box-shadow: 1px 0 0 var(--line);
2631
+ }
2632
+
2633
+ .tbl thead > tr > th:first-child { z-index: 3; }
2634
+
2635
+ /* 展开详情行跨列,不能跟着首列 sticky */
2636
+ .detail-row > td:first-child { position: static; box-shadow: none; background: var(--frost-bg); }
2637
+
2638
+ /* 触摸目标:手持命中区 ≥40px(main.css 只在 pointer:coarse 下兜底) */
2639
+ .mini-btn,
2640
+ .pill-btn,
2641
+ .inp,
2642
+ .inp-sel,
2643
+ .note-edit input {
2644
+ min-height: 40px;
2645
+ }
2646
+
2647
+ .tbl .acts .mini-btn + .mini-btn { margin-left: 8px; }
2648
+ .note-cell { max-width: 200px; }
2649
+ .note-edit { flex-wrap: wrap; }
2650
+ .zone { padding: 12px 12px 14px; }
2651
+ .f-grid { grid-template-columns: 1fr; }
2652
+ .det-grid { grid-template-columns: 1fr; }
2653
+ .mx-actions,
2654
+ .m-actions { flex-wrap: wrap; }
2655
+ .modal { padding: 16px; }
2656
+ .node-row { flex-wrap: wrap; }
2657
+ .node-row .sel { flex: 1 1 100%; }
2658
+ .modal-mask { padding: 4vh 10px 12px; }
2659
+ }
2660
+
2661
+ @media (max-width: 640px) {
2662
+ .zone-head { gap: 8px; }
2663
+ .zone-head h2 { font-size: 14px; }
2664
+ .zone-actions { flex-wrap: wrap; }
2665
+ .ov-card { padding: 10px 12px; }
2666
+ }
2571
2667
  </style>