@aryacode/dsh-engineering 0.0.0-stage → 0.3.0

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 (281) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -2
  4. package/cordis.patch.yml +122 -0
  5. package/locale/en.json +6 -0
  6. package/locale/zh.json +6 -0
  7. package/package.json +77 -3
  8. package/vendor/ftview/README.md +55 -0
  9. package/vendor/ftview/SKILL.md +74 -0
  10. package/vendor/ftview/lib/checks.d.ts +27 -0
  11. package/vendor/ftview/lib/checks.js +39 -0
  12. package/vendor/ftview/lib/core.d.ts +208 -0
  13. package/vendor/ftview/lib/core.js +160 -0
  14. package/vendor/ftview/lib/edit/drafts.d.ts +49 -0
  15. package/vendor/ftview/lib/edit/drafts.js +84 -0
  16. package/vendor/ftview/lib/edit/operations.d.ts +62 -0
  17. package/vendor/ftview/lib/edit/operations.js +126 -0
  18. package/vendor/ftview/lib/edit/prepare.d.ts +38 -0
  19. package/vendor/ftview/lib/edit/prepare.js +133 -0
  20. package/vendor/ftview/lib/edit/tools.d.ts +25 -0
  21. package/vendor/ftview/lib/edit/tools.js +198 -0
  22. package/vendor/ftview/lib/edit.d.ts +30 -0
  23. package/vendor/ftview/lib/edit.js +38 -0
  24. package/vendor/ftview/lib/index.d.ts +31 -0
  25. package/vendor/ftview/lib/index.js +36 -0
  26. package/vendor/ftview/lib/model.d.ts +80 -0
  27. package/vendor/ftview/lib/model.js +267 -0
  28. package/vendor/ftview/lib/session.d.ts +48 -0
  29. package/vendor/ftview/lib/session.js +98 -0
  30. package/vendor/ftview/lib/skill.d.ts +29 -0
  31. package/vendor/ftview/lib/skill.js +43 -0
  32. package/vendor/ftview/lib/studio/discover.d.ts +36 -0
  33. package/vendor/ftview/lib/studio/discover.js +87 -0
  34. package/vendor/ftview/lib/studio/preflight.d.ts +51 -0
  35. package/vendor/ftview/lib/studio/preflight.js +64 -0
  36. package/vendor/ftview/lib/studio/tools.d.ts +15 -0
  37. package/vendor/ftview/lib/studio/tools.js +83 -0
  38. package/vendor/ftview/lib/studio.d.ts +30 -0
  39. package/vendor/ftview/lib/studio.js +37 -0
  40. package/vendor/ftview/lib/tools.d.ts +14 -0
  41. package/vendor/ftview/lib/tools.js +202 -0
  42. package/vendor/l5x/README.md +62 -0
  43. package/vendor/l5x/SKILL.md +81 -0
  44. package/vendor/l5x/lib/approvals.d.ts +50 -0
  45. package/vendor/l5x/lib/approvals.js +93 -0
  46. package/vendor/l5x/lib/client.js +412 -0
  47. package/vendor/l5x/lib/client.js.map +1 -0
  48. package/vendor/l5x/lib/core/errors.d.ts +38 -0
  49. package/vendor/l5x/lib/core/errors.js +48 -0
  50. package/vendor/l5x/lib/core/params.d.ts +49 -0
  51. package/vendor/l5x/lib/core/params.js +39 -0
  52. package/vendor/l5x/lib/core/result.d.ts +46 -0
  53. package/vendor/l5x/lib/core/result.js +21 -0
  54. package/vendor/l5x/lib/core/tool.d.ts +102 -0
  55. package/vendor/l5x/lib/core/tool.js +19 -0
  56. package/vendor/l5x/lib/drafts.d.ts +66 -0
  57. package/vendor/l5x/lib/drafts.js +98 -0
  58. package/vendor/l5x/lib/editing.d.ts +30 -0
  59. package/vendor/l5x/lib/editing.js +42 -0
  60. package/vendor/l5x/lib/engine/acd-edit.d.ts +13 -0
  61. package/vendor/l5x/lib/engine/acd-edit.js +115 -0
  62. package/vendor/l5x/lib/engine/analysis.d.ts +22 -0
  63. package/vendor/l5x/lib/engine/analysis.js +258 -0
  64. package/vendor/l5x/lib/engine/capabilities.d.ts +9 -0
  65. package/vendor/l5x/lib/engine/capabilities.js +40 -0
  66. package/vendor/l5x/lib/engine/change-set.d.ts +122 -0
  67. package/vendor/l5x/lib/engine/change-set.js +446 -0
  68. package/vendor/l5x/lib/engine/configuration.d.ts +21 -0
  69. package/vendor/l5x/lib/engine/configuration.js +42 -0
  70. package/vendor/l5x/lib/engine/details.d.ts +47 -0
  71. package/vendor/l5x/lib/engine/details.js +173 -0
  72. package/vendor/l5x/lib/engine/direct-polling.d.ts +91 -0
  73. package/vendor/l5x/lib/engine/direct-polling.js +193 -0
  74. package/vendor/l5x/lib/engine/download-review.d.ts +15 -0
  75. package/vendor/l5x/lib/engine/download-review.js +63 -0
  76. package/vendor/l5x/lib/engine/logic-syntax.d.ts +60 -0
  77. package/vendor/l5x/lib/engine/logic-syntax.js +237 -0
  78. package/vendor/l5x/lib/engine/observations.d.ts +41 -0
  79. package/vendor/l5x/lib/engine/observations.js +67 -0
  80. package/vendor/l5x/lib/engine/project-analysis.d.ts +131 -0
  81. package/vendor/l5x/lib/engine/project-analysis.js +314 -0
  82. package/vendor/l5x/lib/engine/review-evidence.d.ts +8 -0
  83. package/vendor/l5x/lib/engine/review-evidence.js +49 -0
  84. package/vendor/l5x/lib/engine/review.d.ts +18 -0
  85. package/vendor/l5x/lib/engine/review.js +123 -0
  86. package/vendor/l5x/lib/engine/studio-sdk.d.ts +101 -0
  87. package/vendor/l5x/lib/engine/studio-sdk.js +571 -0
  88. package/vendor/l5x/lib/engine/tag-resolution.d.ts +20 -0
  89. package/vendor/l5x/lib/engine/tag-resolution.js +169 -0
  90. package/vendor/l5x/lib/engine/usage.d.ts +16 -0
  91. package/vendor/l5x/lib/engine/usage.js +92 -0
  92. package/vendor/l5x/lib/engine/xml-regions.d.ts +23 -0
  93. package/vendor/l5x/lib/engine/xml-regions.js +77 -0
  94. package/vendor/l5x/lib/fs.d.ts +17 -0
  95. package/vendor/l5x/lib/fs.js +36 -0
  96. package/vendor/l5x/lib/index.d.ts +36 -0
  97. package/vendor/l5x/lib/index.js +42 -0
  98. package/vendor/l5x/lib/live.d.ts +48 -0
  99. package/vendor/l5x/lib/live.js +48 -0
  100. package/vendor/l5x/lib/model/aois.d.ts +39 -0
  101. package/vendor/l5x/lib/model/aois.js +112 -0
  102. package/vendor/l5x/lib/model/errors.d.ts +15 -0
  103. package/vendor/l5x/lib/model/errors.js +18 -0
  104. package/vendor/l5x/lib/model/index.d.ts +20 -0
  105. package/vendor/l5x/lib/model/index.js +19 -0
  106. package/vendor/l5x/lib/model/module.d.ts +37 -0
  107. package/vendor/l5x/lib/model/module.js +54 -0
  108. package/vendor/l5x/lib/model/modules.d.ts +24 -0
  109. package/vendor/l5x/lib/model/modules.js +55 -0
  110. package/vendor/l5x/lib/model/parse.d.ts +32 -0
  111. package/vendor/l5x/lib/model/parse.js +116 -0
  112. package/vendor/l5x/lib/model/project.d.ts +15 -0
  113. package/vendor/l5x/lib/model/project.js +31 -0
  114. package/vendor/l5x/lib/model/routines.d.ts +71 -0
  115. package/vendor/l5x/lib/model/routines.js +183 -0
  116. package/vendor/l5x/lib/model/tags.d.ts +51 -0
  117. package/vendor/l5x/lib/model/tags.js +119 -0
  118. package/vendor/l5x/lib/model/types.d.ts +304 -0
  119. package/vendor/l5x/lib/model/types.js +12 -0
  120. package/vendor/l5x/lib/model/udts.d.ts +34 -0
  121. package/vendor/l5x/lib/model/udts.js +81 -0
  122. package/vendor/l5x/lib/model/xml.d.ts +139 -0
  123. package/vendor/l5x/lib/model/xml.js +291 -0
  124. package/vendor/l5x/lib/model/xref.d.ts +28 -0
  125. package/vendor/l5x/lib/model/xref.js +154 -0
  126. package/vendor/l5x/lib/session.d.ts +119 -0
  127. package/vendor/l5x/lib/session.js +236 -0
  128. package/vendor/l5x/lib/skill.d.ts +30 -0
  129. package/vendor/l5x/lib/skill.js +44 -0
  130. package/vendor/l5x/lib/studio-state.d.ts +77 -0
  131. package/vendor/l5x/lib/studio-state.js +79 -0
  132. package/vendor/l5x/lib/studio.d.ts +29 -0
  133. package/vendor/l5x/lib/studio.js +40 -0
  134. package/vendor/l5x/lib/tools/checks.d.ts +19 -0
  135. package/vendor/l5x/lib/tools/checks.js +140 -0
  136. package/vendor/l5x/lib/tools/common.d.ts +41 -0
  137. package/vendor/l5x/lib/tools/common.js +84 -0
  138. package/vendor/l5x/lib/tools/controller.d.ts +21 -0
  139. package/vendor/l5x/lib/tools/controller.js +170 -0
  140. package/vendor/l5x/lib/tools/drafts.d.ts +23 -0
  141. package/vendor/l5x/lib/tools/drafts.js +270 -0
  142. package/vendor/l5x/lib/tools/live.d.ts +21 -0
  143. package/vendor/l5x/lib/tools/live.js +175 -0
  144. package/vendor/l5x/lib/tools/read.d.ts +19 -0
  145. package/vendor/l5x/lib/tools/read.js +245 -0
  146. package/vendor/l5x/lib/tools/references.d.ts +20 -0
  147. package/vendor/l5x/lib/tools/references.js +269 -0
  148. package/vendor/l5x/lib/tools/studio.d.ts +21 -0
  149. package/vendor/l5x/lib/tools/studio.js +193 -0
  150. package/vendor/l5x/poll-worker.mjs +77 -0
  151. package/vendor/l5x/studio-bridge/NuGet.Config +1 -0
  152. package/vendor/l5x/studio-bridge/Program.cs +304 -0
  153. package/vendor/l5x/studio-bridge/StudioBridge.csproj +11 -0
  154. package/vendor/mwi/README.md +60 -0
  155. package/vendor/mwi/SKILL.md +87 -0
  156. package/vendor/mwi/bridge/README.md +8 -0
  157. package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -0
  158. package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -0
  159. package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -0
  160. package/vendor/mwi/bridge/start_bridge.cmd +21 -0
  161. package/vendor/mwi/bridge/watch_dialogs.cmd +13 -0
  162. package/vendor/mwi/bridge/watch_dialogs.ps1 +153 -0
  163. package/vendor/mwi/code/engine/motionworks_iec_mcp/__init__.py +14 -0
  164. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  165. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
  166. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
  167. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
  168. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
  169. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
  170. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
  171. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
  172. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
  173. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
  174. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
  175. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
  176. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
  177. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
  178. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
  179. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
  180. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
  181. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
  182. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
  183. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
  184. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
  185. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
  186. package/vendor/mwi/code/engine/motionworks_iec_mcp/block_interfaces.py +275 -0
  187. package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -0
  188. package/vendor/mwi/code/engine/motionworks_iec_mcp/capture.py +448 -0
  189. package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -0
  190. package/vendor/mwi/code/engine/motionworks_iec_mcp/com.py +268 -0
  191. package/vendor/mwi/code/engine/motionworks_iec_mcp/datatypes.py +204 -0
  192. package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -0
  193. package/vendor/mwi/code/engine/motionworks_iec_mcp/descriptions.py +30 -0
  194. package/vendor/mwi/code/engine/motionworks_iec_mcp/eip.py +292 -0
  195. package/vendor/mwi/code/engine/motionworks_iec_mcp/errors.py +36 -0
  196. package/vendor/mwi/code/engine/motionworks_iec_mcp/graphical_listing.py +208 -0
  197. package/vendor/mwi/code/engine/motionworks_iec_mcp/grid.py +227 -0
  198. package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +317 -0
  199. package/vendor/mwi/code/engine/motionworks_iec_mcp/iec.py +317 -0
  200. package/vendor/mwi/code/engine/motionworks_iec_mcp/installed_help.py +151 -0
  201. package/vendor/mwi/code/engine/motionworks_iec_mcp/knowledge.py +218 -0
  202. package/vendor/mwi/code/engine/motionworks_iec_mcp/libraries.py +250 -0
  203. package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -0
  204. package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -0
  205. package/vendor/mwi/code/engine/motionworks_iec_mcp/navigation.py +44 -0
  206. package/vendor/mwi/code/engine/motionworks_iec_mcp/pou_writer.py +1547 -0
  207. package/vendor/mwi/code/engine/motionworks_iec_mcp/program_checks.py +360 -0
  208. package/vendor/mwi/code/engine/motionworks_iec_mcp/project.py +704 -0
  209. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -0
  210. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -0
  211. package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -0
  212. package/vendor/mwi/code/engine/motionworks_iec_mcp/server.py +1913 -0
  213. package/vendor/mwi/code/engine/motionworks_iec_mcp/snapshot.py +138 -0
  214. package/vendor/mwi/code/engine/motionworks_iec_mcp/st_comments.py +25 -0
  215. package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +179 -0
  216. package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +676 -0
  217. package/vendor/mwi/code/engine/motionworks_iec_mcp/structure.py +98 -0
  218. package/vendor/mwi/code/engine/motionworks_iec_mcp/task_bindings.py +68 -0
  219. package/vendor/mwi/code/engine/motionworks_iec_mcp/tasks.py +162 -0
  220. package/vendor/mwi/code/engine/motionworks_iec_mcp/transaction.py +157 -0
  221. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree.py +563 -0
  222. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -0
  223. package/vendor/mwi/code/engine/motionworks_iec_mcp/uia.py +465 -0
  224. package/vendor/mwi/code/engine/motionworks_iec_mcp/validation.py +41 -0
  225. package/vendor/mwi/code/engine/motionworks_iec_mcp/variable_edit.py +183 -0
  226. package/vendor/mwi/code/engine/motionworks_iec_mcp/variables.py +472 -0
  227. package/vendor/mwi/code/engine/motionworks_iec_mcp/workflow.py +130 -0
  228. package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +666 -0
  229. package/vendor/mwi/code/mw_code.py +1258 -0
  230. package/vendor/mwi/legacy/edit-session.js +50 -0
  231. package/vendor/mwi/legacy/fb-insertion.js +74 -0
  232. package/vendor/mwi/legacy/native-code.js +83 -0
  233. package/vendor/mwi/legacy/native-groups.js +75 -0
  234. package/vendor/mwi/legacy/native-structure.js +143 -0
  235. package/vendor/mwi/legacy/native-variables.js +77 -0
  236. package/vendor/mwi/legacy/pou-conversion.js +47 -0
  237. package/vendor/mwi/legacy/pou-package.js +66 -0
  238. package/vendor/mwi/lib/core/bridge.d.ts +71 -0
  239. package/vendor/mwi/lib/core/bridge.js +53 -0
  240. package/vendor/mwi/lib/core/code-engine.d.ts +47 -0
  241. package/vendor/mwi/lib/core/code-engine.js +109 -0
  242. package/vendor/mwi/lib/core/errors.d.ts +37 -0
  243. package/vendor/mwi/lib/core/errors.js +47 -0
  244. package/vendor/mwi/lib/core/params.d.ts +52 -0
  245. package/vendor/mwi/lib/core/params.js +46 -0
  246. package/vendor/mwi/lib/core/ps-bridge.d.ts +62 -0
  247. package/vendor/mwi/lib/core/ps-bridge.js +127 -0
  248. package/vendor/mwi/lib/core/result.d.ts +45 -0
  249. package/vendor/mwi/lib/core/result.js +20 -0
  250. package/vendor/mwi/lib/core/tool.d.ts +80 -0
  251. package/vendor/mwi/lib/core/tool.js +55 -0
  252. package/vendor/mwi/lib/edit/compare.d.ts +33 -0
  253. package/vendor/mwi/lib/edit/compare.js +70 -0
  254. package/vendor/mwi/lib/edit.d.ts +20 -0
  255. package/vendor/mwi/lib/edit.js +32 -0
  256. package/vendor/mwi/lib/index.d.ts +21 -0
  257. package/vendor/mwi/lib/index.js +33 -0
  258. package/vendor/mwi/lib/read/service.d.ts +117 -0
  259. package/vendor/mwi/lib/read/service.js +229 -0
  260. package/vendor/mwi/lib/read.d.ts +21 -0
  261. package/vendor/mwi/lib/read.js +33 -0
  262. package/vendor/mwi/lib/session/project.d.ts +34 -0
  263. package/vendor/mwi/lib/session/project.js +54 -0
  264. package/vendor/mwi/lib/session/service.d.ts +167 -0
  265. package/vendor/mwi/lib/session/service.js +566 -0
  266. package/vendor/mwi/lib/session/stage.d.ts +84 -0
  267. package/vendor/mwi/lib/session/stage.js +187 -0
  268. package/vendor/mwi/lib/session/workspace.d.ts +52 -0
  269. package/vendor/mwi/lib/session/workspace.js +90 -0
  270. package/vendor/mwi/lib/skill.d.ts +36 -0
  271. package/vendor/mwi/lib/skill.js +56 -0
  272. package/vendor/mwi/lib/tools/edit.d.ts +20 -0
  273. package/vendor/mwi/lib/tools/edit.js +286 -0
  274. package/vendor/mwi/lib/tools/read.d.ts +19 -0
  275. package/vendor/mwi/lib/tools/read.js +161 -0
  276. package/vendor/mwi/lib/tools/session.d.ts +20 -0
  277. package/vendor/mwi/lib/tools/session.js +166 -0
  278. package/vendor/mwi/lib/tools/verify.d.ts +19 -0
  279. package/vendor/mwi/lib/tools/verify.js +85 -0
  280. package/vendor/mwi/lib/verify.d.ts +17 -0
  281. package/vendor/mwi/lib/verify.js +29 -0
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: rockwell-l5x
3
+ description: Read and review Studio 5000 L5X exports: open a project, list and read components, find references, trace dependencies, resolve addresses, map bit usage, run checks, and compare exports.
4
+ whenToUse: Use when the user asks about a Studio 5000 L5X export, Logix tags, routines, UDTs, AOIs, I/O modules, references, dependency tracing, checks, or comparison between exports.
5
+ ---
6
+
7
+ # Rockwell L5X engineering
8
+
9
+ ## Boundary
10
+
11
+ This surface reads and drafts. A draft writes only a new L5X file: it never overwrites the source, imports into Studio, connects to a controller, changes mode, or writes runtime data.
12
+
13
+ ## Start
14
+
15
+ 1. Call `open_project` with the export path. Keep the returned `project_id`.
16
+ 2. Later calls may omit `project_id` when exactly one project is open; pass it when several are.
17
+ 3. A read refuses a changed export with `STALE_EXPORT`; call `open_project` again before continuing.
18
+
19
+ ## Find the thing
20
+
21
+ - `list_components` enumerates one kind: program, task, tag, udt, routine, aoi, module, alarm. Filter by name, page with offset and limit.
22
+ - `get_component` reads one in full by exact name. Pass `scope` for a program-scoped tag or routine.
23
+ - `search_logic` finds literal text in rungs, ST lines and diagram labels.
24
+ - `find_references` answers where a symbol is read or written. Use `detail: "evidence"` for rung or line rows with access type and context.
25
+ - `trace_dependencies` follows upstream or downstream operands. It is static evidence, not runtime causality.
26
+ - `resolve_tag` resolves an alias chain, member, integer bit or array subscript.
27
+ - `usage_map` reports which array elements and bits a tag uses and which remain free.
28
+
29
+ ## Scope rules
30
+
31
+ - Program-scoped tags and routines need `scope` set to the program name.
32
+ - AOI members are written `AOI_Name::Member`, for example `AOI_EncoderScale::Out_FPM`. `find_references` accepts that spelling; `trace_dependencies` splits it and resolves the member against the AOI.
33
+ - An unresolved symbol is refused with `UNRESOLVED_SYMBOL` and candidate scope names. Do not guess past it; fix the scope or the spelling.
34
+
35
+ ## Checks and comparison
36
+
37
+ - `run_checks` returns findings with a rule and severity. Filter with `severity` or `rule`, page with offset and limit. Findings are candidates for engineering review, never proof of a defect or of safety.
38
+ - `compare_projects` diffs two open exports by component. Export timestamps and XML formatting are ignored by design.
39
+
40
+ ## Live reads
41
+
42
+ The `l5x-live` entry reads declared scalar tags straight from a controller over EtherNet/IP, without Studio 5000, FactoryTalk Linx or the SDK. It may be disabled; if `poll_live_tags` is unavailable, say live reads are not enabled rather than trying another tool.
43
+
44
+ - `poll_live_tags` takes an explicit IPv4 address, a CPU slot and 1 to 16 declared scalar tags (`BOOL`, `INT`, `DINT`, `REAL`), and returns timestamped sweeps, quality and the controller identity.
45
+ - `start_plc_trend` samples the same way as a session-owned job the chart can follow; stop it through the job controls.
46
+ - These are read-only. Never write controller data, force tags, toggle bits or edit online.
47
+ - Report `live_logic_matches_export: "unverified"`: the read proves a value at a moment, not that the controller runs the exported logic, and sequential reads can miss transitions.
48
+
49
+ ## Reporting
50
+
51
+ - Every result carries `summary`, `data`, `coverage` and `next`. Read `coverage` before claiming the answer is complete.
52
+ - State what the export cannot prove: FBD and SFC execution semantics, dynamic indices, runtime behavior, external HMI and driver consumers, and whether online logic matches the export.
53
+ - Quote the program, routine and rung or ST line for each finding.
54
+ - Do not call a tag unused, or an output permanently zero, from the absence of references alone. External consumers are invisible here.
55
+
56
+ ## Revisions
57
+
58
+ - Reading accepts any export revision 19 or later. Revisions outside the observed matrix open and report `verification: "untested_revision"`.
59
+ - Reading never needs Studio 5000. The studio entry adds offline import and build, and its controller
60
+ tools handle upload, mode change and download under per-call approval.
61
+
62
+ ## Studio
63
+
64
+ The `l5x-studio` entry adds offline SDK work; it may be disabled. Call `studio_sdk_status` before using it.
65
+
66
+ - `export_acd_to_l5x` writes one new full L5X from an ACD. No controller contact.
67
+ - `verify_offline_l5x` imports a full L5X into a new offline ACD and builds it. Build needs project revision 37 or later; `probe: true` attempts a lower revision and reports the SDK error. A Studio-exported L5X builds; an L5X produced by `export_acd_to_l5x` may be refused by the SDK as "no changes".
68
+ - `build_acd_from_draft` imports one changed RLL or ST routine into a new ACD copy, builds it and exports it. The baseline must be an L5X exported from that ACD in this chat, and neither file may change afterwards.
69
+ - `studio_review_evidence` checks program, routine and rung or line names and verbatim quotes before you report findings.
70
+ - `controller_approval_status` reports whether this session can ask a human. `capture_controller_snapshot`, `change_controller_mode` and `download_project` each require per-call human approval of the exact controller, route and target, and proceed only on `allowed-once`; `download_project` accepts only a `verification_id` from a passed build in this chat.
71
+ - `go_online`, `read_controller_mode` and `read_live_tag` are read-only and need no approval. Never write controller data, force tags, toggle bits or edit online.
72
+
73
+ ## Editing
74
+
75
+ Editing uses the `draft_*` family. It may be disabled in this profile; if a draft tool is unavailable, say editing is not enabled here rather than trying another tool.
76
+
77
+ - One operation per call: `draft_rung`, `draft_st`, `draft_routine`, `draft_tag`, `draft_tag_description`, `draft_udt_member`, `draft_udt_description`. Each returns the draft id and the operation count.
78
+ - `draft_validate` prepares the accumulated draft against the project and returns its hash, diff summary and check findings. Read the findings before applying.
79
+ - `draft_apply` writes the draft to ONE new `.L5X` path. It never overwrites an existing file.
80
+ - After applying, reopen the written file with `open_project` to review it.
81
+ - Drafts belong to this chat. Another chat starts with none, and an applied draft is cleared.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Human approval for the controller actions that change a machine.
3
+ *
4
+ * Reading a controller needs no approval. Uploading a snapshot, changing mode
5
+ * and downloading a project each ask the session's approval service for this
6
+ * exact operation, and proceed only on `allowed-once`. A session whose policy
7
+ * is not `ask` cannot request approval at all, and the tool refuses before it
8
+ * touches the controller.
9
+ *
10
+ * @module dsh-engineering-l5x/approvals
11
+ */
12
+ import type { ExecContext, PluginContext } from './core/tool.ts';
13
+ /** The approval service the composition may provide. */
14
+ export interface ApprovalService {
15
+ request?(request: {
16
+ readonly agent: unknown;
17
+ readonly toolName: string;
18
+ readonly callId: unknown;
19
+ readonly reason: string;
20
+ readonly signal?: AbortSignal;
21
+ }): Promise<string>;
22
+ overrideOf?(session: unknown): 'ask' | 'never' | undefined;
23
+ readonly config?: {
24
+ readonly policy?: 'ask' | 'never';
25
+ };
26
+ }
27
+ /**
28
+ * Report whether this session can request per-operation approval.
29
+ *
30
+ * @param ctx - Plugin context that may provide the approval service.
31
+ * @param exec - Calling context carrying the agent.
32
+ * @returns The status fields the tool shows the reader.
33
+ */
34
+ export declare function approvalStatus(ctx: PluginContext, exec?: ExecContext): Record<string, unknown>;
35
+ /**
36
+ * Refuse when approval cannot be requested at all.
37
+ *
38
+ * @param ctx - Plugin context.
39
+ * @param exec - Calling context.
40
+ */
41
+ export declare function ensureApprovalReady(ctx: PluginContext, exec?: ExecContext): void;
42
+ /**
43
+ * Ask for approval of one exact operation and refuse unless it is granted once.
44
+ *
45
+ * @param ctx - Plugin context.
46
+ * @param exec - Calling context.
47
+ * @param toolName - Tool asking, recorded with the request.
48
+ * @param reason - Exact operation the human is approving.
49
+ */
50
+ export declare function requireHumanApproval(ctx: PluginContext, exec: ExecContext | undefined, toolName: string, reason: string): Promise<void>;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Human approval for the controller actions that change a machine.
3
+ *
4
+ * Reading a controller needs no approval. Uploading a snapshot, changing mode
5
+ * and downloading a project each ask the session's approval service for this
6
+ * exact operation, and proceed only on `allowed-once`. A session whose policy
7
+ * is not `ask` cannot request approval at all, and the tool refuses before it
8
+ * touches the controller.
9
+ *
10
+ * @module dsh-engineering-l5x/approvals
11
+ */
12
+ import { L5xError } from "./core/errors.js";
13
+ /** The calling session behind an agent context. */
14
+ function sessionOf(exec) {
15
+ return exec?.agent?.session;
16
+ }
17
+ /**
18
+ * Report whether this session can request per-operation approval.
19
+ *
20
+ * @param ctx - Plugin context that may provide the approval service.
21
+ * @param exec - Calling context carrying the agent.
22
+ * @returns The status fields the tool shows the reader.
23
+ */
24
+ export function approvalStatus(ctx, exec) {
25
+ const approval = ctx.get?.('approval');
26
+ const session = sessionOf(exec);
27
+ const presets = ctx.get?.('permissionPresets');
28
+ const preset = session === undefined ? undefined : presets?.current?.(session);
29
+ const policy = session === undefined || approval === undefined
30
+ ? undefined
31
+ : approval.overrideOf?.(session) ?? approval.config?.policy ?? 'ask';
32
+ const requestable = session !== undefined && typeof approval?.request === 'function' && policy === 'ask';
33
+ return {
34
+ permission_preset: preset ?? 'unknown',
35
+ approval_policy: policy ?? 'unknown',
36
+ approval_service: typeof approval?.request === 'function',
37
+ agent_context: session !== undefined,
38
+ approved_controller_actions_requestable: requestable,
39
+ approval_required_per_call: ['capture_controller_snapshot', 'change_controller_mode', 'download_project'],
40
+ ...(requestable
41
+ ? { note: 'A human must still approve each exact operation; this does not confirm an answerer is online.' }
42
+ : {
43
+ next_step: policy === 'never'
44
+ ? preset === 'danger-full-access'
45
+ ? 'Switch this session from Full access to a preset that asks for approval, then call this tool again.'
46
+ : 'This session has Never approval policy. Change it to Ask before a controller action, then call this tool again.'
47
+ : 'Use a session with an agent context and an approval service that can ask. Do not bypass the plugin.',
48
+ }),
49
+ };
50
+ }
51
+ /**
52
+ * Refuse when approval cannot be requested at all.
53
+ *
54
+ * @param ctx - Plugin context.
55
+ * @param exec - Calling context.
56
+ */
57
+ export function ensureApprovalReady(ctx, exec) {
58
+ const status = approvalStatus(ctx, exec);
59
+ if (!status.approved_controller_actions_requestable) {
60
+ throw new L5xError('APPROVAL_UNAVAILABLE', `Human approval cannot be requested (policy: ${status.approval_policy}). ${status.next_step ?? ''}`, 'Switch to an approval preset that asks, then call this tool again.');
61
+ }
62
+ }
63
+ /**
64
+ * Ask for approval of one exact operation and refuse unless it is granted once.
65
+ *
66
+ * @param ctx - Plugin context.
67
+ * @param exec - Calling context.
68
+ * @param toolName - Tool asking, recorded with the request.
69
+ * @param reason - Exact operation the human is approving.
70
+ */
71
+ export async function requireHumanApproval(ctx, exec, toolName, reason) {
72
+ ensureApprovalReady(ctx, exec);
73
+ const approval = ctx.get?.('approval');
74
+ if (exec?.agent === undefined || typeof approval?.request !== 'function') {
75
+ throw new L5xError('APPROVAL_UNAVAILABLE', `${toolName} requires human approval for this exact operation.`, 'Use a session with an agent context and an approval service, then call this tool again.');
76
+ }
77
+ let outcome;
78
+ try {
79
+ outcome = await approval.request({
80
+ agent: exec.agent,
81
+ toolName,
82
+ callId: exec.callId,
83
+ reason,
84
+ ...(exec.signal === undefined ? {} : { signal: exec.signal }),
85
+ });
86
+ }
87
+ catch (error) {
88
+ throw new L5xError('APPROVAL_UNAVAILABLE', error instanceof Error ? error.message : String(error), 'Check that a human can answer approval requests in this session, then retry.');
89
+ }
90
+ if (outcome !== 'allowed-once') {
91
+ throw new L5xError('APPROVAL_DENIED', `${toolName} was not approved (${outcome}).`, 'Nothing was sent to the controller. Ask the human what to change, then request a new operation.');
92
+ }
93
+ }
@@ -0,0 +1,412 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@aryacode/dsh-engineering-l5x",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ // src/client/locales.ts
10
+ /** Copy owned by the Studio 5000 loader console. */
11
+ /** Locale namespace this console registers its dictionaries under. */
12
+ const NS = "studio5000";
13
+ /** Chinese copy. */
14
+ const zh = {
15
+ "trend.title": "PLC 实时趋势",
16
+ "trend.actualInterval": "显示窗口平均实际间隔",
17
+ "trend.overruns": "观察到的超时周期",
18
+ "trend.badValue": "读数不可用",
19
+ "trend.connecting": "正在连接…",
20
+ "trend.running": "采集中",
21
+ "trend.completed": "采集完成",
22
+ "trend.stopped": "已停止",
23
+ "trend.failed": "采集失败",
24
+ "trend.unavailable": "实时趋势流不可用",
25
+ "trend.gap": "部分历史数据不可用",
26
+ "trend.stopFailed": "无法停止采集,请重试",
27
+ "trend.stop": "停止",
28
+ "trend.interval": "请求间隔",
29
+ "trend.samples": "显示采样数",
30
+ "trend.source": "源文件",
31
+ "trend.limitation": "读取并非 PLC 扫描同步,可能遗漏变化;尚未验证在线逻辑与导出一致",
32
+ title: "Studio 5000",
33
+ workspace: "工作区",
34
+ refresh: "刷新",
35
+ rescan: "重新扫描",
36
+ recent: "最近打开",
37
+ projects: "项目",
38
+ open: "在 Studio 5000 中打开",
39
+ filter: "筛选 ACD 项目",
40
+ "fallback.open": "使用 Windows 默认应用打开",
41
+ "fallback.description": "文件处理程序查询不可用或未找到 Studio。此操作将使用此 ACD 的系统默认应用。",
42
+ "status.defaultOpened": "已发送给系统默认应用。请确认打开的是正确项目。",
43
+ "launch.description": "在已安装的 Studio 5000 中打开项目。",
44
+ "status.desktop": "此主机没有可用的桌面。",
45
+ "status.discovery": "无法检查此文件的应用程序。请更新主机或检查连接。",
46
+ "status.handler": "没有找到 Studio 5000 文件处理程序。请检查安装和 ACD 文件关联。",
47
+ reveal: "在资源管理器中显示",
48
+ "empty.none": "此工作区中没有找到 .acd 文件。",
49
+ "empty.scanning": "正在扫描工作区…",
50
+ "empty.noSession": "此工作区还没有会话,无法列出文件。",
51
+ "empty.noWorkspace": "还没有工作区。请先打开一个项目文件夹。",
52
+ "unavailable.listing": "此配置未提供工作区文件列表功能。",
53
+ "unavailable.launch": "此主机无法打开本机文件。",
54
+ "unavailable.desktop": "当前主机没有可用的桌面程序。",
55
+ "status.opened": "已发送打开请求。请在 Studio 5000 中确认项目已打开。",
56
+ "status.revealed": "已在资源管理器中显示。",
57
+ "status.failed": "打开失败。",
58
+ "status.scanFailed": "部分目录无法读取。",
59
+ "status.truncated": "列表已被截断。",
60
+ size: "大小",
61
+ truncated: "(已截断)"
62
+ };
63
+ /** English copy, same keys. */
64
+ const en = {
65
+ "trend.title": "Live PLC trend",
66
+ "trend.actualInterval": "Mean actual interval in displayed window",
67
+ "trend.overruns": "Observed sweep overruns",
68
+ "trend.badValue": "Reading unavailable",
69
+ "trend.connecting": "Connecting…",
70
+ "trend.running": "Collecting",
71
+ "trend.completed": "Capture complete",
72
+ "trend.stopped": "Stopped",
73
+ "trend.failed": "Capture failed",
74
+ "trend.unavailable": "Live trend stream unavailable",
75
+ "trend.gap": "Some history is unavailable",
76
+ "trend.stopFailed": "Could not stop capture. Try again.",
77
+ "trend.stop": "Stop",
78
+ "trend.interval": "Requested interval",
79
+ "trend.samples": "Displayed samples",
80
+ "trend.source": "Source export",
81
+ "trend.limitation": "Reads are not scan-synchronized and may miss transitions. Online logic matching this export is unverified.",
82
+ title: "Studio 5000",
83
+ workspace: "Workspace",
84
+ refresh: "Refresh",
85
+ rescan: "Rescan",
86
+ recent: "Recently opened",
87
+ projects: "Projects",
88
+ open: "Open in Studio 5000",
89
+ filter: "Filter ACD projects",
90
+ "fallback.open": "Open with Windows default",
91
+ "fallback.description": "Handler discovery is unavailable or found no Studio handler. This opens the selected ACD using its system default application.",
92
+ "status.defaultOpened": "Sent to the system default application. Confirm the correct project opened.",
93
+ "launch.description": "Open projects in installed Studio 5000.",
94
+ "status.desktop": "No desktop is available on this host.",
95
+ "status.discovery": "Could not check file handlers. Update the host or check the connection.",
96
+ "status.handler": "No Studio 5000 file handler found. Check the installation and ACD file association.",
97
+ reveal: "Reveal in Explorer",
98
+ "empty.none": "No .acd files found in this workspace.",
99
+ "empty.scanning": "Scanning the workspace…",
100
+ "empty.noSession": "This workspace has no Session yet, so its files cannot be listed.",
101
+ "empty.noWorkspace": "No workspace yet. Open a project folder first.",
102
+ "unavailable.listing": "Workspace file listing is not available in this profile.",
103
+ "unavailable.launch": "This host cannot open local files.",
104
+ "unavailable.desktop": "No desktop application is available on this host.",
105
+ "status.opened": "Open request sent. Confirm the project opened in Studio 5000.",
106
+ "status.revealed": "Revealed in the file manager.",
107
+ "status.failed": "Could not open the project.",
108
+ "status.scanFailed": "Some directories could not be read.",
109
+ "status.truncated": "The listing was truncated.",
110
+ size: "Size",
111
+ truncated: "(truncated)"
112
+ };
113
+ // src/client/trend-model.ts
114
+ /** Parse a recorded start receipt. @param content - Logged tool content. @returns Valid receipt or undefined. */
115
+ function trendReceipt(content) {
116
+ if (!Array.isArray(content)) return void 0;
117
+ for (const entry of content) {
118
+ if (!entry || typeof entry !== "object" || entry.type !== "text" || typeof entry.text !== "string") continue;
119
+ try {
120
+ const value = JSON.parse(entry.text);
121
+ if (value && typeof value === "object" && "view" in value && value.view === "plc-trend" && "job_id" in value && typeof value.job_id === "string" && /^plc-trend-\d+$/u.test(value.job_id) && "owner_session_id" in value && typeof value.owner_session_id === "string" && "requested_interval_ms" in value && typeof value.requested_interval_ms === "number" && "source_sha256" in value && typeof value.source_sha256 === "string" && "display_refresh_ms" in value && typeof value.display_refresh_ms === "number" && value.display_refresh_ms >= 50 && value.display_refresh_ms <= 1e4 && "display_max_samples" in value && typeof value.display_max_samples === "number" && value.display_max_samples >= 2 && value.display_max_samples <= 2e4) return {
122
+ jobId: value.job_id,
123
+ sessionId: value.owner_session_id,
124
+ interval: value.requested_interval_ms,
125
+ sourceHash: value.source_sha256,
126
+ refresh: value.display_refresh_ms,
127
+ maxSamples: value.display_max_samples
128
+ };
129
+ } catch (error) {}
130
+ }
131
+ }
132
+ /** Parse one sampler line. @param line - Retained job output. @returns Plot point or undefined for other frames. */
133
+ function trendPoint(line) {
134
+ const value = JSON.parse(line);
135
+ if (!value || typeof value !== "object" || !("type" in value) || value.type !== "sample") return void 0;
136
+ if (!("elapsed_ms" in value) || typeof value.elapsed_ms !== "number" || !Number.isFinite(value.elapsed_ms) || !("values" in value) || !Array.isArray(value.values)) throw new Error("Invalid trend sample");
137
+ const values = value.values.map((item) => {
138
+ if (!item || typeof item !== "object" || !("tag" in item) || typeof item.tag !== "string" || !("quality" in item) || typeof item.quality !== "string" || !("value" in item) || !(item.value === null || typeof item.value === "boolean" || typeof item.value === "number" && Number.isFinite(item.value))) throw new Error("Invalid trend value");
139
+ return {
140
+ tag: item.tag,
141
+ quality: item.quality,
142
+ value: item.value
143
+ };
144
+ });
145
+ return {
146
+ elapsed: value.elapsed_ms,
147
+ overrun: "overrun" in value && typeof value.overrun === "boolean" ? value.overrun : void 0,
148
+ values
149
+ };
150
+ }
151
+ /** Build distinct line segments so bad samples do not imply continuity. @param points - Retained samples. @param tag - Scoped tag. @returns SVG line segments and range. */
152
+ function trendSegments(points, tag) {
153
+ const finite = points.flatMap((point) => point.values.filter((item) => item.tag === tag && item.quality === "good" && item.value !== null).map((item) => Number(item.value)));
154
+ const min = Math.min(0, ...finite);
155
+ const max = Math.max(1, ...finite);
156
+ const start = points[0]?.elapsed ?? 0;
157
+ const duration = Math.max(1, (points.at(-1)?.elapsed ?? start) - start);
158
+ const segments = [];
159
+ let current = [];
160
+ for (const point of points) {
161
+ const value = point.values.find((item) => item.tag === tag);
162
+ if (!value || value.quality !== "good" || value.value === null) {
163
+ if (current.length) segments.push(current.join(" "));
164
+ current = [];
165
+ continue;
166
+ }
167
+ const x = 10 + (point.elapsed - start) / duration * 580;
168
+ const y = 100 - (Number(value.value) - min) / (max - min) * 90;
169
+ if (typeof value.value === "boolean" && current.length) current.push(`${x},${current.at(-1)?.split(",")[1]}`);
170
+ current.push(`${x},${y}`);
171
+ }
172
+ if (current.length) segments.push(current.join(" "));
173
+ return {
174
+ segments,
175
+ min,
176
+ max
177
+ };
178
+ }
179
+ // src/client/TrendCard.tsx
180
+ /** Inline live PLC trend driven by the existing session-authorized job stream. */
181
+ /** Display a live watch and stop it without cancelling the chat. @param props - Tool seat and existing job Remote. @returns Trend card. */
182
+ function TrendCard({ phase, block, trendJobs, t }) {
183
+ const receipt = phase === "result" ? trendReceipt(block.content) : void 0;
184
+ const [points, setPoints] = (0, react.useState)([]);
185
+ const [state, setState] = (0, react.useState)("trend.connecting");
186
+ const [stopping, setStopping] = (0, react.useState)(false);
187
+ (0, react.useEffect)(() => {
188
+ if (!receipt || !trendJobs) return;
189
+ const abort = new AbortController();
190
+ let pending = [];
191
+ let dirty = false;
192
+ let tail = "";
193
+ const flush = () => {
194
+ if (!dirty || abort.signal.aborted) return;
195
+ dirty = false;
196
+ setPoints([...pending]);
197
+ };
198
+ const timer = setInterval(flush, receipt.refresh);
199
+ const run = async () => {
200
+ try {
201
+ for await (const frame of trendJobs.follow({
202
+ jobId: receipt.jobId,
203
+ sessionId: receipt.sessionId
204
+ }, abort.signal)) {
205
+ if (abort.signal.aborted) return;
206
+ if (!frame || typeof frame !== "object" || !("type" in frame)) throw new Error("Invalid job frame");
207
+ if (frame.type === "opened") setState("trend.running");
208
+ if (frame.type === "output" && "chunks" in frame && Array.isArray(frame.chunks)) {
209
+ if ("lossy" in frame && frame.lossy === true) {
210
+ pending = [];
211
+ tail = "";
212
+ setState("trend.gap");
213
+ }
214
+ for (const chunk of frame.chunks) {
215
+ if (!chunk || typeof chunk !== "object" || typeof chunk.text !== "string") throw new Error("Invalid trend output");
216
+ if (chunk.gapBefore === true) {
217
+ pending = [];
218
+ tail = "";
219
+ setState("trend.gap");
220
+ }
221
+ tail += chunk.text;
222
+ const lines = tail.split("\n");
223
+ tail = lines.pop() ?? "";
224
+ for (const line of lines) {
225
+ const point = trendPoint(line);
226
+ if (point) {
227
+ pending.push(point);
228
+ if (pending.length > receipt.maxSamples) pending.shift();
229
+ dirty = true;
230
+ }
231
+ }
232
+ }
233
+ }
234
+ if (frame.type === "status" && "job" in frame && frame.job && typeof frame.job === "object" && "status" in frame.job) {
235
+ flush();
236
+ setState(frame.job.status === "completed" ? "trend.completed" : frame.job.status === "killed" ? "trend.stopped" : "trend.failed");
237
+ }
238
+ }
239
+ } catch (error) {
240
+ if (!abort.signal.aborted) setState("trend.unavailable");
241
+ } finally {
242
+ clearInterval(timer);
243
+ flush();
244
+ }
245
+ };
246
+ run();
247
+ return () => {
248
+ abort.abort();
249
+ clearInterval(timer);
250
+ };
251
+ }, [
252
+ receipt?.jobId,
253
+ receipt?.sessionId,
254
+ receipt?.refresh,
255
+ receipt?.maxSamples,
256
+ trendJobs
257
+ ]);
258
+ if (!receipt) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
259
+ role: "status",
260
+ children: t(block.isError ? "trend.failed" : "trend.connecting")
261
+ });
262
+ const tags = [...new Set(points.flatMap((point) => point.values.map((value) => value.tag)))];
263
+ const stop = async () => {
264
+ if (!trendJobs || stopping) return;
265
+ setStopping(true);
266
+ try {
267
+ if (!(await trendJobs.kill({
268
+ jobId: receipt.jobId,
269
+ sessionId: receipt.sessionId
270
+ })).ok) setState("trend.stopFailed");
271
+ } catch (error) {
272
+ setState("trend.stopFailed");
273
+ } finally {
274
+ setStopping(false);
275
+ }
276
+ };
277
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
278
+ "aria-label": t("trend.title"),
279
+ style: {
280
+ padding: "12px",
281
+ border: "1px solid var(--dsw-alias-border-l3)",
282
+ borderRadius: "8px"
283
+ },
284
+ children: [
285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
286
+ style: {
287
+ display: "flex",
288
+ gap: "12px",
289
+ alignItems: "center"
290
+ },
291
+ children: [
292
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("trend.title") }),
293
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
294
+ role: "status",
295
+ children: t(trendJobs ? state : "trend.unavailable")
296
+ }),
297
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
298
+ type: "button",
299
+ style: {
300
+ color: "var(--dsw-alias-label-primary)",
301
+ background: "transparent",
302
+ border: "1px solid var(--dsw-alias-border-l3)",
303
+ borderRadius: "6px",
304
+ font: "inherit",
305
+ padding: "4px 10px"
306
+ },
307
+ disabled: !trendJobs || stopping || [
308
+ "trend.completed",
309
+ "trend.stopped",
310
+ "trend.failed"
311
+ ].includes(state),
312
+ onClick: () => {
313
+ stop();
314
+ },
315
+ children: t("trend.stop")
316
+ })
317
+ ]
318
+ }),
319
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
320
+ t("trend.interval"),
321
+ ": ",
322
+ receipt.interval,
323
+ " ms · ",
324
+ t("trend.samples"),
325
+ ": ",
326
+ points.length
327
+ ] }),
328
+ points.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
329
+ t("trend.actualInterval"),
330
+ ": ",
331
+ ((points.at(-1).elapsed - points[0].elapsed) / (points.length - 1)).toFixed(1),
332
+ " ms · ",
333
+ t("trend.overruns"),
334
+ ": ",
335
+ points.filter((point) => point.overrun === true).length
336
+ ] }),
337
+ tags.map((tag) => {
338
+ const plot = trendSegments(points, tag);
339
+ const latest = points.at(-1)?.values.find((value) => value.tag === tag);
340
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("figure", {
341
+ style: { margin: "12px 0" },
342
+ children: [
343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("figcaption", { children: [
344
+ tag,
345
+ " · ",
346
+ latest?.quality === "good" ? String(latest.value) : t("trend.badValue"),
347
+ " · ",
348
+ plot.min,
349
+ "–",
350
+ plot.max
351
+ ] }),
352
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
353
+ viewBox: "0 0 600 110",
354
+ role: "img",
355
+ "aria-label": tag,
356
+ style: {
357
+ width: "100%",
358
+ height: "110px"
359
+ },
360
+ children: plot.segments.map((segment, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", {
361
+ points: segment,
362
+ fill: "none",
363
+ stroke: "currentColor",
364
+ strokeWidth: "1.5"
365
+ }, index))
366
+ }),
367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
368
+ ((points[0]?.elapsed ?? 0) / 1e3).toFixed(2),
369
+ "–",
370
+ ((points.at(-1)?.elapsed ?? 0) / 1e3).toFixed(2),
371
+ " s"
372
+ ] })
373
+ ]
374
+ }, tag);
375
+ }),
376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [
377
+ t("trend.source"),
378
+ ": ",
379
+ receipt.sourceHash.slice(0, 12),
380
+ " · ",
381
+ t("trend.limitation")
382
+ ] })
383
+ ]
384
+ });
385
+ }
386
+ // src/client/index.ts
387
+ /** Required client services; job availability is explained inside the card. */
388
+ const inject = [
389
+ "slots",
390
+ "locale",
391
+ "remote"
392
+ ];
393
+ /** Register the live trend card without a Studio launcher sidebar. @param ctx - Browser service context. */
394
+ function apply(ctx) {
395
+ ctx.effect(() => ctx.locale.register(NS, {
396
+ zh,
397
+ en
398
+ }), "l5x-live: trend copy");
399
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
400
+ name: "tool.call.toolview",
401
+ key: "start_plc_trend",
402
+ locale: NS,
403
+ inject: () => ({ trendJobs: ctx.remote.job })
404
+ }, TrendCard));
405
+ }
406
+ exports.apply = apply;
407
+ exports.inject = inject;
408
+ return module.exports;
409
+ }
410
+ });
411
+
412
+ //# sourceMappingURL=client.js.map