@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,270 @@
1
+ /**
2
+ * The draft family: one operation per call, accumulated, validated, applied.
3
+ *
4
+ * Replaces a single tool that took a JSON string of every operation. Each tool
5
+ * here is flat, names its own intent, and returns the growing operation count,
6
+ * so a small model can see the draft take shape without holding a nested
7
+ * document in its head.
8
+ *
9
+ * @module dsh-engineering-l5x/tools/drafts
10
+ */
11
+ import { prepareChangeSet } from "../engine/change-set.js";
12
+ import { L5xError } from "../core/errors.js";
13
+ import { success } from "../core/result.js";
14
+ import { choice, integer, object, string } from "../core/params.js";
15
+ import { PROJECT_ID, optionalInteger, optionalNumber, optionalString, requiredString, tool } from "./common.js";
16
+ /** A prepared draft is not a Studio acceptance. */
17
+ const DRAFT_COVERAGE = {
18
+ complete: false,
19
+ unverified: [
20
+ 'Studio 5000 import and build are not performed by this surface',
21
+ 'runtime behavior',
22
+ 'the source export is never modified',
23
+ ],
24
+ };
25
+ /** The version-1 request the change-set engine accepts. */
26
+ function requestOf(draft) {
27
+ return JSON.stringify({ version: 1, operations: draft.operations });
28
+ }
29
+ /** The success document every append answers with. */
30
+ function appended(draft, operation) {
31
+ return success(`Draft ${draft.id}: ${draft.operations.length} ${draft.operations.length === 1 ? 'operation' : 'operations'}.`, { draft_id: draft.id, operation_count: draft.operations.length, last_operation: operation }, [{ tool: 'draft_validate', args: { draft_id: draft.id }, why: 'Validate the whole draft before writing anything.' }]);
32
+ }
33
+ /**
34
+ * The draft tools, bound to one chat's project and draft stores.
35
+ *
36
+ * @param session - Project handles for the calling chat.
37
+ * @param drafts - Drafts for the calling chat.
38
+ * @param maxOperations - Inclusive operations per draft.
39
+ * @param maxFileBytes - Byte budget the preparation may read.
40
+ * @returns Tool definitions in registration order.
41
+ */
42
+ export function draftTools(session, drafts, maxOperations, maxFileBytes) {
43
+ return [
44
+ tool({
45
+ name: 'draft_rung',
46
+ description: 'Append one ladder rung operation to the chat draft: replace, insert or delete. Nothing is written until draft_apply.',
47
+ parameters: object({
48
+ program: string('Program that owns the routine. Example: "MainProgram".'),
49
+ routine: string('Routine the rung belongs to. Example: "MainRoutine".'),
50
+ mode: choice('replace rewrites an existing rung, insert adds one, delete removes one.', ['replace', 'insert', 'delete']),
51
+ rung: integer('Rung number the operation targets. Required for replace and delete; for insert it is the new rung number.', { minimum: 0 }),
52
+ text: string('NeutralText for replace or insert. Omit for delete. Example: "XIC(Start)OTE(Run);"'),
53
+ before_rung: integer('For insert only: insert before this rung number instead of after rung.', { minimum: 0 }),
54
+ project_id: PROJECT_ID,
55
+ }, ['program', 'routine', 'mode']),
56
+ async run(args, exec) {
57
+ const project = await session.require(optionalString(args, 'project_id'), exec);
58
+ const program = requiredString(args, 'program');
59
+ const routine = requiredString(args, 'routine');
60
+ const mode = requiredString(args, 'mode');
61
+ const text = optionalString(args, 'text');
62
+ const rung = optionalInteger(args, 'rung');
63
+ const before = optionalInteger(args, 'before_rung');
64
+ let operation;
65
+ if (mode === 'delete') {
66
+ if (rung === undefined)
67
+ throw invalid('rung', 'Required when mode is delete.', 'Pass the rung number to remove.');
68
+ operation = { operation: 'delete_rung', program, routine, rung };
69
+ }
70
+ else if (mode === 'replace') {
71
+ if (rung === undefined || text === undefined)
72
+ throw invalid('rung and text', 'Required when mode is replace.', 'Pass the rung number and its new NeutralText.');
73
+ operation = { operation: 'replace_rung', program, routine, rung, text };
74
+ }
75
+ else if (mode === 'insert') {
76
+ if (rung === undefined || text === undefined)
77
+ throw invalid('rung and text', 'Required when mode is insert.', 'Pass the new rung number and its NeutralText.');
78
+ operation = { operation: 'insert_rung', program, routine, rung, text, ...(before === undefined ? {} : { before_rung: before }) };
79
+ }
80
+ else {
81
+ throw invalid('mode', `Unknown mode ${JSON.stringify(mode)}.`, 'Pass one of: replace, insert, delete.');
82
+ }
83
+ return appended(drafts.append(exec, project.id, operation, maxOperations), operation);
84
+ },
85
+ }),
86
+ tool({
87
+ name: 'draft_st',
88
+ description: 'Append one Structured Text body replacement to the chat draft. Nothing is written until draft_apply.',
89
+ parameters: object({
90
+ program: string('Program that owns the routine. Example: "MainProgram".'),
91
+ routine: string('Structured Text routine to replace. Example: "Calc".'),
92
+ code: string('Full replacement body, one statement per line. Example: "Counter := Counter + 1;\\nIF Counter > 10 THEN Counter := 0; END_IF;"'),
93
+ project_id: PROJECT_ID,
94
+ }, ['program', 'routine', 'code']),
95
+ async run(args, exec) {
96
+ const project = await session.require(optionalString(args, 'project_id'), exec);
97
+ const operation = {
98
+ operation: 'replace_st',
99
+ program: requiredString(args, 'program'),
100
+ routine: requiredString(args, 'routine'),
101
+ code: requiredString(args, 'code'),
102
+ };
103
+ return appended(drafts.append(exec, project.id, operation, maxOperations), operation);
104
+ },
105
+ }),
106
+ tool({
107
+ name: 'draft_routine',
108
+ description: 'Append one new routine to the chat draft. Nothing is written until draft_apply.',
109
+ parameters: object({
110
+ program: string('Program that will own the new routine. Example: "MainProgram".'),
111
+ name: string('New routine name, a Logix identifier of at most 40 characters. Example: "Calc".'),
112
+ language: choice('Language of the new routine: RLL for ladder, ST for Structured Text.', ['RLL', 'ST']),
113
+ code: string('Body: NeutralText rungs for RLL, statements for ST.'),
114
+ project_id: PROJECT_ID,
115
+ }, ['program', 'name', 'language', 'code']),
116
+ async run(args, exec) {
117
+ const project = await session.require(optionalString(args, 'project_id'), exec);
118
+ const operation = {
119
+ operation: 'add_routine',
120
+ program: requiredString(args, 'program'),
121
+ name: requiredString(args, 'name'),
122
+ language: requiredString(args, 'language'),
123
+ code: requiredString(args, 'code'),
124
+ };
125
+ return appended(drafts.append(exec, project.id, operation, maxOperations), operation);
126
+ },
127
+ }),
128
+ tool({
129
+ name: 'draft_tag',
130
+ description: 'Append one new tag to the chat draft. Nothing is written until draft_apply.',
131
+ parameters: object({
132
+ scope: string('Declaring scope: "controller" or a program name. Example: "controller".'),
133
+ name: string('New tag name, a Logix identifier of at most 40 characters. Example: "SP_Start".'),
134
+ data_type: string('Declared data type. Example: "REAL" or a UDT name.'),
135
+ description: string('Tag description shown in Studio. Example: "Start threshold".'),
136
+ value: integer('Optional scalar initial value. Example: 80.'),
137
+ dimensions: string('Optional array dimensions, space separated. Example: "4 2".'),
138
+ external_access: choice('Optional external access.', ['Read/Write', 'Read Only', 'None']),
139
+ project_id: PROJECT_ID,
140
+ }, ['scope', 'name', 'data_type', 'description']),
141
+ async run(args, exec) {
142
+ const project = await session.require(optionalString(args, 'project_id'), exec);
143
+ const value = optionalNumber(args, 'value');
144
+ const dimensions = optionalString(args, 'dimensions');
145
+ const externalAccess = optionalString(args, 'external_access');
146
+ const operation = {
147
+ operation: 'add_tag',
148
+ scope: requiredString(args, 'scope'),
149
+ name: requiredString(args, 'name'),
150
+ data_type: requiredString(args, 'data_type'),
151
+ description: requiredString(args, 'description'),
152
+ ...(value === undefined ? {} : { value }),
153
+ ...(dimensions === undefined ? {} : { dimensions }),
154
+ ...(externalAccess === undefined ? {} : { external_access: externalAccess }),
155
+ };
156
+ return appended(drafts.append(exec, project.id, operation, maxOperations), operation);
157
+ },
158
+ }),
159
+ tool({
160
+ name: 'draft_tag_description',
161
+ description: 'Append one existing tag description change to the chat draft. Nothing is written until draft_apply.',
162
+ parameters: object({
163
+ scope: string('Declaring scope: "controller" or a program name.'),
164
+ name: string('Existing tag name. Example: "LineSpeed".'),
165
+ description: string('New description text for the existing tag.'),
166
+ project_id: PROJECT_ID,
167
+ }, ['scope', 'name', 'description']),
168
+ async run(args, exec) {
169
+ const project = await session.require(optionalString(args, 'project_id'), exec);
170
+ const operation = {
171
+ operation: 'set_tag_description',
172
+ scope: requiredString(args, 'scope'),
173
+ name: requiredString(args, 'name'),
174
+ description: requiredString(args, 'description'),
175
+ };
176
+ return appended(drafts.append(exec, project.id, operation, maxOperations), operation);
177
+ },
178
+ }),
179
+ tool({
180
+ name: 'draft_udt_member',
181
+ description: 'Append one new member to a standard data type in the chat draft. Refused later if the type already has instances. Nothing is written until draft_apply.',
182
+ parameters: object({
183
+ udt: string('Existing standard data type name. Example: "Motor_UDT".'),
184
+ name: string('New member name. Example: "Speed".'),
185
+ data_type: string('Member data type. Example: "REAL".'),
186
+ description: string('Description text for the new member.'),
187
+ project_id: PROJECT_ID,
188
+ }, ['udt', 'name', 'data_type', 'description']),
189
+ async run(args, exec) {
190
+ const project = await session.require(optionalString(args, 'project_id'), exec);
191
+ const operation = {
192
+ operation: 'add_udt_member',
193
+ udt: requiredString(args, 'udt'),
194
+ name: requiredString(args, 'name'),
195
+ data_type: requiredString(args, 'data_type'),
196
+ description: requiredString(args, 'description'),
197
+ };
198
+ return appended(drafts.append(exec, project.id, operation, maxOperations), operation);
199
+ },
200
+ }),
201
+ tool({
202
+ name: 'draft_udt_description',
203
+ description: 'Append one standard data type description change, or one member description change when member is supplied. Nothing is written until draft_apply.',
204
+ parameters: object({
205
+ udt: string('Existing standard data type name.'),
206
+ member: string('Member name to change. Omit to change the type description itself.'),
207
+ description: string('New description text for the type or the member.'),
208
+ project_id: PROJECT_ID,
209
+ }, ['udt', 'description']),
210
+ async run(args, exec) {
211
+ const project = await session.require(optionalString(args, 'project_id'), exec);
212
+ const udt = requiredString(args, 'udt');
213
+ const description = requiredString(args, 'description');
214
+ const member = optionalString(args, 'member');
215
+ const operation = member === undefined
216
+ ? { operation: 'set_udt_description', udt, description }
217
+ : { operation: 'set_udt_member_description', udt, member, description };
218
+ return appended(drafts.append(exec, project.id, operation, maxOperations), operation);
219
+ },
220
+ }),
221
+ tool({
222
+ name: 'draft_validate',
223
+ description: 'Validate the accumulated draft against the open project and return its hash, diff summary and check findings. Writes nothing.',
224
+ parameters: object({
225
+ draft_id: string('Draft handle returned by a draft_* tool. Omit when exactly one draft exists in this chat.'),
226
+ project_id: PROJECT_ID,
227
+ }, []),
228
+ async run(args, exec) {
229
+ const draft = drafts.get(exec, optionalString(args, 'draft_id'));
230
+ const project = await session.require(draft.projectId, exec);
231
+ const prepared = prepareChangeSet(project, requestOf(draft), { operations: maxOperations, bytes: maxFileBytes });
232
+ return success(`${draft.operations.length} ${draft.operations.length === 1 ? 'operation' : 'operations'} prepared; draft hash ${prepared.draft_sha256.slice(0, 12)}.`, {
233
+ draft_id: draft.id,
234
+ operation_count: draft.operations.length,
235
+ source_sha256: prepared.source_sha256,
236
+ draft_sha256: prepared.draft_sha256,
237
+ validation: prepared.validation,
238
+ checks: prepared.checks,
239
+ diff: prepared.diff,
240
+ }, [{ tool: 'draft_apply', args: { draft_id: draft.id }, why: 'Write the validated draft to a new L5X.' }], DRAFT_COVERAGE);
241
+ },
242
+ }),
243
+ tool({
244
+ name: 'draft_apply',
245
+ description: 'Apply the validated draft to ONE new L5X file beside the source. Never overwrites, never imports into Studio, never contacts a controller.',
246
+ parameters: object({
247
+ draft_id: string('Draft handle returned by a draft_* tool. Omit when exactly one draft exists in this chat.'),
248
+ output_path: string('New .L5X path to write. It must not exist.'),
249
+ project_id: PROJECT_ID,
250
+ }, ['output_path']),
251
+ async run(args, exec) {
252
+ const draft = drafts.get(exec, optionalString(args, 'draft_id'));
253
+ const project = await session.require(draft.projectId, exec);
254
+ const prepared = prepareChangeSet(project, requestOf(draft), { operations: maxOperations, bytes: maxFileBytes });
255
+ const target = await session.writeNew(requiredString(args, 'output_path'), prepared.xml, exec);
256
+ drafts.remove(exec, draft.id);
257
+ return success(`Wrote ${draft.operations.length} ${draft.operations.length === 1 ? 'operation' : 'operations'} to ${target}.`, {
258
+ output_path: target,
259
+ draft_sha256: prepared.draft_sha256,
260
+ operation_count: draft.operations.length,
261
+ validation: prepared.validation,
262
+ }, [{ tool: 'open_project', args: { path: target }, why: 'Reopen the written draft and review it.' }], DRAFT_COVERAGE);
263
+ },
264
+ }),
265
+ ];
266
+ }
267
+ /** One argument failure with the fix. */
268
+ function invalid(field, message, fix) {
269
+ return new L5xError('INVALID_ARGUMENT', `${field}: ${message}`, fix);
270
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * SDK-free live reads over EtherNet/IP.
3
+ *
4
+ * Two tools: a bounded foreground poll, and a session-owned trend job the chart
5
+ * can follow without a model call per sample. Both read declared scalar tags,
6
+ * never write, and state that live logic is not the exported logic.
7
+ *
8
+ * @module dsh-engineering-l5x/tools/live
9
+ */
10
+ import type { PluginContext, ToolDefinition } from '../core/tool.ts';
11
+ import type { ProjectSession } from '../session.ts';
12
+ import { type DirectPolling } from '../engine/direct-polling.ts';
13
+ /**
14
+ * The live tools.
15
+ *
16
+ * @param session - Project handles for the calling chat.
17
+ * @param polling - The polling engine and its configuration.
18
+ * @param ctx - Plugin context that may provide the job service.
19
+ * @returns Tool definitions in registration order.
20
+ */
21
+ export declare function liveTools(session: ProjectSession, polling: DirectPolling, ctx: PluginContext): readonly ToolDefinition[];
@@ -0,0 +1,175 @@
1
+ /**
2
+ * SDK-free live reads over EtherNet/IP.
3
+ *
4
+ * Two tools: a bounded foreground poll, and a session-owned trend job the chart
5
+ * can follow without a model call per sample. Both read declared scalar tags,
6
+ * never write, and state that live logic is not the exported logic.
7
+ *
8
+ * @module dsh-engineering-l5x/tools/live
9
+ */
10
+ import { success } from "../core/result.js";
11
+ import { integer, list, object, objectItem, string } from "../core/params.js";
12
+ import { pollingRequest } from "../engine/direct-polling.js";
13
+ import { L5xError } from "../core/errors.js";
14
+ import { PROJECT_ID, optionalInteger, optionalString, requiredString, tool } from "./common.js";
15
+ /** A direct read is not a guarantee about the scan. */
16
+ const LIVE_COVERAGE = {
17
+ complete: false,
18
+ unverified: [
19
+ 'live logic matching the exported logic',
20
+ 'values synchronized to the controller scan',
21
+ 'transitions between sweeps',
22
+ ],
23
+ };
24
+ /** The tags argument, as flat objects rather than a JSON string. */
25
+ function tagsOf(args) {
26
+ const raw = args.tags;
27
+ if (!Array.isArray(raw)) {
28
+ throw new L5xError('INVALID_POLL', 'tags must be an array of 1 to 16 tag objects.', 'Pass [{ "name": "LineSpeed" }], not a JSON string.');
29
+ }
30
+ const tags = raw.map((entry) => {
31
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
32
+ throw new L5xError('INVALID_POLL', 'Each tag must be an object with a name.', 'Pass [{ "name": "LineSpeed" }].');
33
+ }
34
+ const row = entry;
35
+ const name = row.name;
36
+ if (typeof name !== 'string' || name === '') {
37
+ throw new L5xError('INVALID_POLL', 'Each tag needs a name.', 'Pass [{ "name": "LineSpeed" }].');
38
+ }
39
+ const program = row.program;
40
+ return program === undefined ? { name } : { name, scope: program };
41
+ });
42
+ return JSON.stringify(tags);
43
+ }
44
+ /** The same tag parameter for both tools. */
45
+ const TAGS_PARAM = list('Controller-scoped scalar tags to read, 1 to 16. Each entry names the tag and optionally its program.', objectItem('One declared scalar tag.', {
46
+ name: string('Tag name. Example: "LineSpeed".'),
47
+ program: string('Program scope for a program-scoped tag. Omit for a controller tag.'),
48
+ }, ['name']), { minItems: 1, maxItems: 16 });
49
+ /**
50
+ * The live tools.
51
+ *
52
+ * @param session - Project handles for the calling chat.
53
+ * @param polling - The polling engine and its configuration.
54
+ * @param ctx - Plugin context that may provide the job service.
55
+ * @returns Tool definitions in registration order.
56
+ */
57
+ export function liveTools(session, polling, ctx) {
58
+ return [
59
+ tool({
60
+ name: 'poll_live_tags',
61
+ description: 'Read declared scalar PLC tags directly over EtherNet/IP without Studio, FactoryTalk Linx, an ACD upload or the SDK. Explicit controller IPv4 and slot required. Returns timestamped sweeps, quality and controller identity; no writes.',
62
+ parameters: object({
63
+ project_id: PROJECT_ID,
64
+ ip_address: string('Controller IPv4 address the human named. Example: "192.168.1.10".'),
65
+ slot: integer('CPU slot, 0 to 31. Default 0.', { minimum: 0, maximum: 31, default: 0 }),
66
+ tags: TAGS_PARAM,
67
+ sample_count: integer('Number of sweeps to read. Default 1.', { minimum: 1, maximum: 1200, default: 1 }),
68
+ interval_ms: integer('Requested interval between sweeps. Default 200.', { minimum: 50, maximum: 600000, default: 200 }),
69
+ }, ['ip_address', 'tags']),
70
+ async run(args, exec) {
71
+ const project = await session.require(optionalString(args, 'project_id'), exec);
72
+ const { request, evidence } = pollingRequest(project, {
73
+ ip_address: requiredString(args, 'ip_address'),
74
+ slot: optionalInteger(args, 'slot') ?? 0,
75
+ sample_count: optionalInteger(args, 'sample_count') ?? 1,
76
+ interval_ms: optionalInteger(args, 'interval_ms') ?? polling.config.defaultIntervalMs,
77
+ tags_json: tagsOf(args),
78
+ }, polling.config);
79
+ const frames = await polling.start(request, () => { }, exec.signal).done;
80
+ await session.require(project.id, exec);
81
+ return success(`Read ${request.samples} sweep(s) from ${request.ip}.`, {
82
+ project_id: project.id,
83
+ source_sha256: project.sha256,
84
+ ip_address: request.ip,
85
+ slot: request.slot,
86
+ provider: 'ethernet-ip',
87
+ sdk_required: false,
88
+ controller_contacted: true,
89
+ live_logic_matches_export: 'unverified',
90
+ requested_interval_ms: request.interval,
91
+ evidence,
92
+ controller: frames.find(frame => frame.type === 'identity'),
93
+ samples: frames.filter(frame => frame.type === 'sample'),
94
+ limitation: 'Sequential explicit reads can miss transitions and are not synchronized to the PLC scan.',
95
+ }, [], LIVE_COVERAGE);
96
+ },
97
+ }),
98
+ tool({
99
+ name: 'start_plc_trend',
100
+ description: 'Start a bounded, read-only PLC trend as a session-owned background job. Explicit IPv4, slot and declared scalar tags required. SDK-independent EtherNet/IP; the chart follows the job without a model call per sample.',
101
+ parameters: object({
102
+ project_id: PROJECT_ID,
103
+ ip_address: string('Controller IPv4 address the human named. Example: "192.168.1.10".'),
104
+ slot: integer('CPU slot, 0 to 31. Default 0.', { minimum: 0, maximum: 31, default: 0 }),
105
+ tags: TAGS_PARAM,
106
+ sample_count: integer('Number of sweeps in the trend. Default 300.', { minimum: 1, maximum: 1200, default: 300 }),
107
+ interval_ms: integer('Requested interval between sweeps. Default 200.', { minimum: 50, maximum: 600000, default: 200 }),
108
+ }, ['ip_address', 'tags']),
109
+ async run(args, exec) {
110
+ const project = await session.require(optionalString(args, 'project_id'), exec);
111
+ const { request, evidence } = pollingRequest(project, {
112
+ ip_address: requiredString(args, 'ip_address'),
113
+ slot: optionalInteger(args, 'slot') ?? 0,
114
+ sample_count: optionalInteger(args, 'sample_count') ?? 300,
115
+ interval_ms: optionalInteger(args, 'interval_ms') ?? polling.config.defaultIntervalMs,
116
+ tags_json: tagsOf(args),
117
+ }, polling.config);
118
+ const jobs = ctx.get?.('jobs');
119
+ const owner = exec.sessionId;
120
+ if (jobs === undefined || owner === undefined) {
121
+ throw new L5xError('TREND_JOBS_UNAVAILABLE', 'Live trends require the session-owned job service.', 'Use poll_live_tags for a foreground read, or run in a session with the jobs service.');
122
+ }
123
+ const jobId = jobs.start({
124
+ kind: 'plc-trend',
125
+ owner,
126
+ label: `PLC trend ${request.ip} slot ${request.slot}`,
127
+ run(job) {
128
+ job.append(JSON.stringify({
129
+ type: 'source',
130
+ project_id: project.id,
131
+ source_sha256: project.sha256,
132
+ ip_address: request.ip,
133
+ slot: request.slot,
134
+ tags: request.tags,
135
+ evidence,
136
+ requested_interval_ms: request.interval,
137
+ live_logic_matches_export: 'unverified',
138
+ }) + '\n');
139
+ const handle = polling.start(request, frame => job.append(JSON.stringify(frame) + '\n'));
140
+ const done = handle.done
141
+ .then(async (frames) => {
142
+ await session.require(project.id);
143
+ return {
144
+ status: 'completed',
145
+ result: JSON.stringify({
146
+ project_id: project.id,
147
+ source_sha256: project.sha256,
148
+ sample_count: frames.filter(frame => frame.type === 'sample').length,
149
+ live_logic_matches_export: 'unverified',
150
+ }),
151
+ };
152
+ })
153
+ .catch((error) => ({
154
+ status: error instanceof L5xError && error.code === 'POLL_CANCELLED' ? 'killed' : 'failed',
155
+ detail: error instanceof Error ? error.message : 'Polling failed',
156
+ }));
157
+ return { cancel: handle.cancel, done };
158
+ },
159
+ });
160
+ return success(`Trend ${jobId} started for ${request.samples} sweep(s) from ${request.ip}.`, {
161
+ job_id: jobId,
162
+ owner_session_id: owner,
163
+ project_id: project.id,
164
+ source_sha256: project.sha256,
165
+ provider: 'ethernet-ip',
166
+ sample_count: request.samples,
167
+ requested_interval_ms: request.interval,
168
+ live_logic_matches_export: 'unverified',
169
+ view: 'plc-trend',
170
+ stop: 'Use the trend Stop control or stop the job.',
171
+ }, [], LIVE_COVERAGE);
172
+ },
173
+ }),
174
+ ];
175
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The offline read surface: open, look, and read one component.
3
+ *
4
+ * Every tool answers with the same document — `summary`, `data`, optional
5
+ * `coverage`, and concrete `next` calls — and every expected failure answers
6
+ * with a code, a message and a fix. The tool descriptions name the one intent
7
+ * each tool serves; parameters carry their own meaning and an example.
8
+ *
9
+ * @module dsh-engineering-l5x/tools/read
10
+ */
11
+ import type { ToolDefinition } from '../core/tool.ts';
12
+ import type { ProjectSession } from '../session.ts';
13
+ /**
14
+ * The offline tools, bound to one chat's project store.
15
+ *
16
+ * @param session - Project handles for the calling chat.
17
+ * @returns Tool definitions in registration order.
18
+ */
19
+ export declare function readTools(session: ProjectSession): readonly ToolDefinition[];