@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,42 @@
1
+ /**
2
+ * The L5X editing entry: drafts over the read entry's project handles.
3
+ *
4
+ * Kept separate so a profile that only reads, checks and compares does not pay
5
+ * for nine draft schemas. It consumes the `l5xProjects` service the read entry
6
+ * provides, so a project is opened once and both entries see the same handle.
7
+ *
8
+ * @module dsh-engineering-l5x/editing
9
+ */
10
+ import { DraftStore } from "./drafts.js";
11
+ import { draftTools } from "./tools/drafts.js";
12
+ import { PROJECTS_SERVICE } from "./index.js";
13
+ /** Plugin name in a composition. */
14
+ export const name = 'l5x-editing';
15
+ /** The read entry's project service must already be mounted. */
16
+ export const inject = ['tools', PROJECTS_SERVICE];
17
+ /**
18
+ * Register the draft tools.
19
+ *
20
+ * @param ctx - Plugin context carrying the read entry's project service.
21
+ * @param config - Deployment limits.
22
+ */
23
+ export function apply(ctx, config = {}) {
24
+ const session = ctx.get?.(PROJECTS_SERVICE);
25
+ if (session === undefined) {
26
+ throw new Error('l5x-editing: the l5xProjects service is unavailable; mount the L5X read entry before this one');
27
+ }
28
+ const maxFileBytes = config.maxFileBytes ?? 64 * 1024 * 1024;
29
+ if (!Number.isInteger(maxFileBytes) || maxFileBytes <= 0) {
30
+ throw new Error('l5x-editing: maxFileBytes must be a positive integer');
31
+ }
32
+ const maxDraftOperations = config.maxDraftOperations ?? 200;
33
+ if (!Number.isInteger(maxDraftOperations) || maxDraftOperations <= 0) {
34
+ throw new Error('l5x-editing: maxDraftOperations must be a positive integer');
35
+ }
36
+ const drafts = new DraftStore();
37
+ for (const definition of draftTools(session, drafts, maxDraftOperations, maxFileBytes)) {
38
+ ctx.tools.register(definition);
39
+ }
40
+ }
41
+ export { DraftStore } from "./drafts.js";
42
+ export { draftTools } from "./tools/drafts.js";
@@ -0,0 +1,13 @@
1
+ import { type ProjectSnapshot } from '../session.ts';
2
+ export declare function controllerContent(project: ProjectSnapshot, ignoreModifiedDate?: boolean, ignoreEncodedPayload?: boolean): string;
3
+ /** Validate one edited routine component for review in the user's IDE.
4
+ * @param baseline - original routine export from Studio.
5
+ * @param draft - unchanged-on-disk proposal derived from that export.
6
+ * @param program - intended containing program.
7
+ * @param routine - intended existing routine.
8
+ * @returns import target and source identity; no Studio import or controller operation occurs.
9
+ */
10
+ export declare function studioRoutineImportPlan(baseline: ProjectSnapshot, draft: ProjectSnapshot, program: string, routine: string): object;
11
+ export declare function routineEditPlan(baseline: ProjectSnapshot, draft: ProjectSnapshot, program: string, routine: string): {
12
+ xpath: string;
13
+ };
@@ -0,0 +1,115 @@
1
+ import { documentRoot, elementChildren, firstChild, parseXml } from "../model/xml.js";
2
+ import { EngineeringError } from "../session.js";
3
+ import { reviewDownloadSource } from "./download-review.js";
4
+ function controller(project) {
5
+ const node = firstChild(documentRoot(parseXml(project.xml)), 'Controller');
6
+ if (project.exportKind !== 'controller' || node === undefined)
7
+ throw new EngineeringError('CONTROLLER_EXPORT_REQUIRED', 'A full controller export is required');
8
+ return node;
9
+ }
10
+ function named(parent, kind, name) {
11
+ const matches = parent === undefined ? [] : elementChildren(parent, kind).filter(node => node.attrs.Name === name);
12
+ if (matches.length !== 1)
13
+ throw new EngineeringError('AMBIGUOUS_TARGET', `Expected one ${kind} named ${name}`);
14
+ return matches[0];
15
+ }
16
+ /** Compare every controller XML structure, including fields not modeled by the index. */
17
+ function canonical(node, omit, ignoreModifiedDate = false, ignoreEncodedPayload = false) {
18
+ if (node === omit)
19
+ return '#target-routine';
20
+ if (!('children' in node))
21
+ return node.name === '#comment' || node.text.trim() === '' ? null : ['text', node.text];
22
+ const attrs = Object.entries(node.attrs).filter(([key]) => !(ignoreModifiedDate && node.name === 'Controller' && key === 'LastModifiedDate'));
23
+ const children = node.children.filter(child => !(ignoreEncodedPayload && node.name === 'EncodedData' && !('children' in child)));
24
+ return [node.name, attrs.sort(([a], [b]) => a.localeCompare(b)), children.map(child => canonical(child, omit, ignoreModifiedDate, ignoreEncodedPayload)).filter(value => value !== null)];
25
+ }
26
+ export function controllerContent(project, ignoreModifiedDate = false, ignoreEncodedPayload = false) {
27
+ return JSON.stringify(canonical(controller(project), undefined, ignoreModifiedDate, ignoreEncodedPayload));
28
+ }
29
+ /** Validate one edited routine component for review in the user's IDE.
30
+ * @param baseline - original routine export from Studio.
31
+ * @param draft - unchanged-on-disk proposal derived from that export.
32
+ * @param program - intended containing program.
33
+ * @param routine - intended existing routine.
34
+ * @returns import target and source identity; no Studio import or controller operation occurs.
35
+ */
36
+ export function studioRoutineImportPlan(baseline, draft, program, routine) {
37
+ for (const value of [program, routine])
38
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(value))
39
+ throw new EngineeringError('INVALID_TARGET', 'Program and routine must be Logix identifiers');
40
+ const before = documentRoot(parseXml(baseline.xml));
41
+ const after = documentRoot(parseXml(draft.xml));
42
+ if (before.attrs.TargetType !== 'Routine' || before.attrs.TargetName !== routine
43
+ || after.attrs.TargetType !== 'Routine' || after.attrs.TargetName !== routine) {
44
+ throw new EngineeringError('ROUTINE_COMPONENT_REQUIRED', 'Export the selected routine from the visible IDE first; full controller exports cannot be used as routine import files');
45
+ }
46
+ const beforeController = firstChild(before, 'Controller');
47
+ const afterController = firstChild(after, 'Controller');
48
+ if (beforeController === undefined || afterController === undefined)
49
+ throw new EngineeringError('INVALID_COMPONENT', 'Routine export requires controller context');
50
+ const oldProgram = named(firstChild(beforeController, 'Programs'), 'Program', program);
51
+ const newProgram = named(firstChild(afterController, 'Programs'), 'Program', program);
52
+ const oldRoutine = named(firstChild(oldProgram, 'Routines'), 'Routine', routine);
53
+ const newRoutine = named(firstChild(newProgram, 'Routines'), 'Routine', routine);
54
+ const protectedNode = (node) => 'children' in node && (node.name === 'EncodedData'
55
+ || /safety/iu.test(node.attrs.Class ?? '')
56
+ || Object.entries(node.attrs).some(([key, value]) => /Encrypted|SourceProtected/iu.test(key) && value !== 'false')
57
+ || node.children.some(protectedNode));
58
+ if (protectedNode(before) || protectedNode(after) || /safety/iu.test(program))
59
+ throw new EngineeringError('PROTECTED_TARGET', 'Safety or protected components require the engineer workflow');
60
+ if (!['RLL', 'ST'].includes(oldRoutine.attrs.Type ?? '') || oldRoutine.attrs.Type !== newRoutine.attrs.Type
61
+ || oldRoutine.attrs.Use !== 'Target' || newRoutine.attrs.Use !== 'Target')
62
+ throw new EngineeringError('UNSUPPORTED_TARGET', 'Use an existing unprotected RLL or ST routine exported as Target');
63
+ if (JSON.stringify(canonical(before, oldRoutine)) !== JSON.stringify(canonical(after, newRoutine)))
64
+ throw new EngineeringError('UNRELATED_CHANGE', 'Only the selected routine may change; preserve dependency and export context');
65
+ if (JSON.stringify(canonical(oldRoutine)) === JSON.stringify(canonical(newRoutine)))
66
+ throw new EngineeringError('NO_CHANGE', 'Routine is unchanged');
67
+ return {
68
+ operation: 'studio_ide_routine_import_plan', program, routine, routine_type: newRoutine.attrs.Type,
69
+ controller_name: beforeController?.attrs.Name ?? null, source_path: baseline.path, source_sha256: baseline.sha256,
70
+ component_path: draft.path, component_sha256: draft.sha256, software_revision: baseline.softwareRevision,
71
+ validation: { xml: 'passed', source_and_draft_hashes: 'checked_by_calling_tool', changes_confined_to_routine: true, studio_import: 'not_run', studio_verify: 'not_run', machine_behavior: 'not_verified' },
72
+ controller_contacted: false, ide_modified: false,
73
+ steps: ['Observe the same Studio IDE and confirm the saved project, program, routine and offline state.',
74
+ 'Back up the saved project before replacement. Revalidate the proposal immediately before import.',
75
+ 'Navigate to the intended program routine import command using fresh observed UI controls; choose component_path.',
76
+ 'Review the collision preview: replace only this routine, retain existing dependencies, and stop for unexpected changes.',
77
+ 'After import, run Verify in Studio and inspect its diagnostics; unresolved errors block handoff.',
78
+ 'Present the result for engineering review. The engineer performs download; do not accept online edits or change controller mode through UI automation.'],
79
+ };
80
+ }
81
+ export function routineEditPlan(baseline, draft, program, routine) {
82
+ for (const value of [program, routine])
83
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(value))
84
+ throw new EngineeringError('INVALID_TARGET', 'Program and routine must be Logix identifiers');
85
+ const before = controller(baseline);
86
+ const after = controller(draft);
87
+ const oldProgram = named(firstChild(before, 'Programs'), 'Program', program);
88
+ const newProgram = named(firstChild(after, 'Programs'), 'Program', program);
89
+ const tasks = firstChild(before, 'Tasks');
90
+ const safetyScheduled = tasks !== undefined && elementChildren(tasks, 'Task').some(task => /safety/iu.test(task.attrs.Class ?? '')
91
+ && firstChild(task, 'ScheduledPrograms')?.children.some(child => 'attrs' in child && child.attrs.Name === program));
92
+ if (/safety/iu.test(oldProgram.attrs.Class ?? '') || /safety/iu.test(program)
93
+ || safetyScheduled
94
+ || baseline.document.tasks.some(task => /safety/iu.test(task.name) && task.programs.includes(program)))
95
+ throw new EngineeringError('PROTECTED_TARGET', 'Safety programs cannot be imported through the ACD edit tool');
96
+ const oldRoutine = named(firstChild(oldProgram, 'Routines'), 'Routine', routine);
97
+ const newRoutine = named(firstChild(newProgram, 'Routines'), 'Routine', routine);
98
+ if (/safety/iu.test(oldRoutine.attrs.Class ?? '') || /safety/iu.test(newRoutine.attrs.Class ?? ''))
99
+ throw new EngineeringError('PROTECTED_TARGET', 'Safety routines cannot be edited');
100
+ if (!['RLL', 'ST'].includes(oldRoutine.attrs.Type ?? '') || oldRoutine.attrs.Type !== newRoutine.attrs.Type)
101
+ throw new EngineeringError('UNSUPPORTED_TARGET', 'Only existing RLL and ST routines of the same type are supported');
102
+ const protectedRoutine = (node) => node.name === 'EncodedData'
103
+ || Object.entries(node.attrs).some(([key, value]) => /Encrypted|SourceProtected/iu.test(key) && value !== 'false')
104
+ || node.children.some(child => 'children' in child && protectedRoutine(child));
105
+ if (protectedRoutine(oldRoutine) || protectedRoutine(newRoutine))
106
+ throw new EngineeringError('PROTECTED_TARGET', 'Encoded or protected routines cannot be edited');
107
+ const review = reviewDownloadSource(baseline, draft);
108
+ if (!review.eligible)
109
+ throw new EngineeringError('EDIT_BLOCKED', review.blockers.join('; '));
110
+ if (JSON.stringify(canonical(before, oldRoutine)) !== JSON.stringify(canonical(after, newRoutine)))
111
+ throw new EngineeringError('UNRELATED_CHANGE', 'The draft must change only the selected routine; tags, modules, other logic and controller metadata must remain intact');
112
+ if (JSON.stringify(canonical(oldRoutine)) === JSON.stringify(canonical(newRoutine)))
113
+ throw new EngineeringError('NO_CHANGE', 'Selected routine is unchanged');
114
+ return { xpath: `Controller/Programs/Program[@Name='${program}']/Routines/Routine[@Name='${routine}']` };
115
+ }
@@ -0,0 +1,22 @@
1
+ import type { L5xDocument } from '../model/types.ts';
2
+ import type { ProjectSnapshot } from '../session.ts';
3
+ export type Access = 'read' | 'write' | 'read_write' | 'call' | 'unknown';
4
+ export interface Evidence {
5
+ readonly scope: string;
6
+ readonly routine: string;
7
+ readonly language: string;
8
+ readonly rung?: number;
9
+ readonly line?: number;
10
+ readonly sheet?: number;
11
+ readonly nodeId?: string;
12
+ readonly symbol: string;
13
+ readonly resolved?: string;
14
+ readonly access: Access;
15
+ readonly confidence: 'high' | 'medium' | 'low';
16
+ readonly context: string;
17
+ }
18
+ /** Resolve only declarations proven by the export. Never guess an absent member. */
19
+ export declare function resolveSymbol(document: L5xDocument, scope: string, symbol: string): string | undefined;
20
+ export declare function evidenceOf(project: ProjectSnapshot): readonly Evidence[];
21
+ export declare function findReferences(project: ProjectSnapshot, symbol: string, scope?: string): readonly Evidence[];
22
+ export declare function traceSymbol(project: ProjectSnapshot, symbol: string, scope?: string): object;
@@ -0,0 +1,258 @@
1
+ import { controllerElementOf } from "../model/parse.js";
2
+ import { elementChildren, firstChild } from "../model/xml.js";
3
+ import { memberOf } from "./details.js";
4
+ const TOKEN = /[A-Za-z_][A-Za-z0-9_]*(?::[A-Za-z0-9_]+)*(?:\[[^\]]+\])?(?:\.[A-Za-z0-9_]+(?:\[[^\]]+\])?)*/gu;
5
+ const LADDER = /([A-Za-z_][A-Za-z0-9_]*)\s*\(([^()]*)\)/gu;
6
+ const READ_OPS = new Set(['XIC', 'XIO', 'EQU', 'EQ', 'NEQ', 'NE', 'GRT', 'GT', 'GEQ', 'GE', 'LES', 'LT', 'LEQ', 'LE', 'LIM', 'LIMIT', 'CMP']);
7
+ const WRITE_OPS = new Set(['OTE', 'OTL', 'OTU', 'CLR']);
8
+ const SOURCE_DEST_OPS = new Set(['MOV', 'MOVE', 'FLL', 'COP', 'ADD', 'SUB', 'MUL', 'DIV']);
9
+ const READ_WRITE_OPS = new Set(['TON', 'TOF', 'RTO', 'CTU', 'CTD', 'RES']);
10
+ const KEYWORDS = new Set(['IF', 'THEN', 'ELSE', 'ELSIF', 'END_IF', 'FOR', 'TO', 'DO', 'END_FOR', 'WHILE', 'END_WHILE', 'RETURN', 'TRUE', 'FALSE', 'AND', 'OR', 'NOT', 'MOD']);
11
+ const BUILTIN_MEMBERS = {
12
+ TIMER: new Set(['EN', 'TT', 'DN', 'PRE', 'ACC']),
13
+ TIMER_T: new Set(['EN', 'TT', 'DN', 'PRE', 'ACC']),
14
+ COUNTER: new Set(['CU', 'CD', 'DN', 'OV', 'UN', 'PRE', 'ACC']),
15
+ };
16
+ /** Integer types that expose bit addresses as `Tag.bit`. */
17
+ const INTEGER_TYPES = new Set(['SINT', 'INT', 'DINT', 'LINT', 'USINT', 'UINT', 'UDINT', 'ULINT']);
18
+ /**
19
+ * Split instruction operands on top-level commas.
20
+ *
21
+ * A two-dimensional subscript such as `Alarm_Timer[0,0].DN` contains a comma
22
+ * that is not an operand separator; splitting blindly turned that operand into
23
+ * `Alarm_Timer[0` and `0].DN`, which is where hundreds of false `DN`
24
+ * references came from.
25
+ */
26
+ function splitArguments(text) {
27
+ const parts = [];
28
+ let depth = 0;
29
+ let quote = '';
30
+ let start = 0;
31
+ for (let index = 0; index < text.length; index++) {
32
+ const char = text[index];
33
+ if (quote !== '') {
34
+ if (char === quote)
35
+ quote = '';
36
+ continue;
37
+ }
38
+ if (char === '"' || char === "'") {
39
+ quote = char;
40
+ continue;
41
+ }
42
+ if (char === '[')
43
+ depth += 1;
44
+ else if (char === ']')
45
+ depth = Math.max(0, depth - 1);
46
+ else if (char === ',' && depth === 0) {
47
+ parts.push(text.slice(start, index));
48
+ start = index + 1;
49
+ }
50
+ }
51
+ parts.push(text.slice(start));
52
+ return parts.map(part => part.trim());
53
+ }
54
+ const tables = new WeakMap();
55
+ const evidenceCache = new WeakMap();
56
+ function tagKey(scope, name) { return `${scope}::${name.toLowerCase()}`; }
57
+ function aliasElements(container, scope, aliases) {
58
+ const tags = container === undefined ? undefined : firstChild(container, 'Tags');
59
+ if (tags === undefined)
60
+ return;
61
+ for (const tag of elementChildren(tags, 'Tag')) {
62
+ const name = tag.attrs['Name'];
63
+ const target = tag.attrs['AliasFor'];
64
+ if (name !== undefined && target !== undefined)
65
+ aliases.set(tagKey(scope, name), target);
66
+ }
67
+ }
68
+ function tableOf(document) {
69
+ const cached = tables.get(document);
70
+ if (cached !== undefined)
71
+ return cached;
72
+ const tags = new Map(document.tags.map(tag => [tagKey(tag.scope, tag.name), tag]));
73
+ const aliases = new Map();
74
+ const controller = controllerElementOf(document);
75
+ aliasElements(controller, 'controller', aliases);
76
+ const programs = firstChild(controller, 'Programs');
77
+ if (programs !== undefined)
78
+ for (const program of elementChildren(programs, 'Program')) {
79
+ aliasElements(program, program.attrs['Name'] ?? '', aliases);
80
+ }
81
+ const table = { tags, aliases };
82
+ tables.set(document, table);
83
+ return table;
84
+ }
85
+ /** Resolve only declarations proven by the export. Never guess an absent member. */
86
+ export function resolveSymbol(document, scope, symbol) {
87
+ const table = tableOf(document);
88
+ const aoi = document.aois.find(entry => entry.name === scope);
89
+ const parts = symbol.replace(/\[[^\]]+\]/gu, '').split('.');
90
+ const base = parts.shift();
91
+ if (base === undefined)
92
+ return undefined;
93
+ if (aoi !== undefined && (aoi.parameters.some(p => p.name.toLowerCase() === base.toLowerCase()) || aoi.localTags.some(t => t.name.toLowerCase() === base.toLowerCase()))) {
94
+ return `${scope}::${symbol}`;
95
+ }
96
+ let tag = table.tags.get(tagKey(scope, base)) ?? table.tags.get(tagKey('controller', base));
97
+ if (tag === undefined)
98
+ return undefined;
99
+ let resolvedBase = tag.name;
100
+ const seen = new Set();
101
+ while (tag !== undefined) {
102
+ const key = tagKey(tag.scope, tag.name);
103
+ const alias = table.aliases.get(key);
104
+ if (alias === undefined)
105
+ break;
106
+ if (seen.has(key))
107
+ return undefined;
108
+ seen.add(key);
109
+ resolvedBase = alias;
110
+ const aliasBase = alias.split('.')[0];
111
+ if (aliasBase === undefined)
112
+ break;
113
+ tag = table.tags.get(tagKey(tag.scope, aliasBase)) ?? table.tags.get(tagKey('controller', aliasBase));
114
+ if (tag === undefined)
115
+ return undefined;
116
+ }
117
+ const path = [...resolvedBase.split('.').slice(1), ...parts];
118
+ if (path.length > 0) {
119
+ let type = tag?.dataType;
120
+ for (const [index, part] of path.entries()) {
121
+ if (/^\d+$/u.test(part)) {
122
+ // `Alarm[8].12` is bit 12 of an integer element; nothing may follow it.
123
+ if (index !== path.length - 1 || !INTEGER_TYPES.has(type?.toUpperCase() ?? ''))
124
+ return undefined;
125
+ type = 'BOOL';
126
+ continue;
127
+ }
128
+ if (type !== undefined && BUILTIN_MEMBERS[type.toUpperCase()]?.has(part.toUpperCase())) {
129
+ if (index !== path.length - 1)
130
+ return undefined;
131
+ continue;
132
+ }
133
+ const aoiDefinition = document.aois.find(entry => entry.name.toLowerCase() === type?.toLowerCase());
134
+ if (aoiDefinition !== undefined) {
135
+ const member = aoiDefinition.parameters.find(entry => entry.name.toLowerCase() === part.toLowerCase())
136
+ ?? aoiDefinition.localTags.find(entry => entry.name.toLowerCase() === part.toLowerCase());
137
+ if (member === undefined)
138
+ return undefined;
139
+ type = member.dataType;
140
+ continue;
141
+ }
142
+ const member = memberOf(document, type ?? '', part);
143
+ if (member === undefined)
144
+ return undefined;
145
+ type = member.dataType;
146
+ }
147
+ }
148
+ return `${tag?.scope ?? scope}::${resolvedBase}${parts.length > 0 ? `.${parts.join('.')}` : ''}`;
149
+ }
150
+ function tokens(text) { return [...text.matchAll(TOKEN)].map(match => match[0]).filter(token => !KEYWORDS.has(token.toUpperCase()) && !/^\d/u.test(token)); }
151
+ function add(out, document, routine, symbol, access, context, location, confidence) {
152
+ const resolved = resolveSymbol(document, routine.program, symbol);
153
+ out.push({ scope: routine.program, routine: routine.name, language: routine.type, ...location, symbol, ...(resolved === undefined ? {} : { resolved }), access, confidence, context });
154
+ }
155
+ function ladderEvidence(out, document, routine, text, rung) {
156
+ for (const match of text.matchAll(LADDER)) {
157
+ const op = match[1]?.toUpperCase() ?? '';
158
+ const args = splitArguments(match[2] ?? '');
159
+ if (op === 'JSR') {
160
+ const target = args[0] ?? '';
161
+ const called = document.routines.find(entry => entry.program === routine.program && entry.name.toLowerCase() === target.toLowerCase());
162
+ out.push({ scope: routine.program, routine: routine.name, language: routine.type, rung, symbol: target, ...(called === undefined ? {} : { resolved: `${called.program}::${called.name}` }), access: 'call', confidence: called === undefined ? 'low' : 'high', context: text });
163
+ continue;
164
+ }
165
+ const aoi = document.aois.find(entry => entry.name.toUpperCase() === op);
166
+ if (aoi !== undefined) {
167
+ out.push({ scope: routine.program, routine: routine.name, language: routine.type, rung, symbol: aoi.name, resolved: `AOI::${aoi.name}`, access: 'call', confidence: 'high', context: text });
168
+ const visible = aoi.parameters.filter(parameter => parameter.visible !== false && parameter.name !== 'EnableIn' && parameter.name !== 'EnableOut');
169
+ const positional = args.length === visible.length + 1;
170
+ for (const [index, arg] of args.entries()) {
171
+ const usage = positional && index > 0 ? visible[index - 1]?.usage : undefined;
172
+ const access = index === 0 ? 'read_write' : usage === 'Input' ? 'read' : usage === 'Output' ? 'write' : usage === 'InOut' ? 'read_write' : 'unknown';
173
+ for (const symbol of tokens(arg ?? ''))
174
+ add(out, document, routine, symbol, access, text, { rung }, usage === undefined ? 'low' : 'medium');
175
+ }
176
+ continue;
177
+ }
178
+ for (const [index, arg] of args.entries()) {
179
+ const destination = op === 'COP' || op === 'FLL' ? 1 : args.length - 1;
180
+ const access = READ_OPS.has(op) ? 'read' : WRITE_OPS.has(op) ? 'write' : SOURCE_DEST_OPS.has(op) ? (index === destination ? 'write' : 'read') : READ_WRITE_OPS.has(op) ? 'read_write' : 'unknown';
181
+ for (const symbol of tokens(arg ?? ''))
182
+ add(out, document, routine, symbol, access, text, { rung }, access === 'unknown' ? 'low' : 'medium');
183
+ }
184
+ }
185
+ }
186
+ function structuredEvidence(out, document, routine, code) {
187
+ // Keep offsets and line positions while excluding strings and multiline comments.
188
+ const text = code.replace(/\(\*[\s\S]*?\*\)|\/\*[\s\S]*?\*\/|\/\/[^\n]*|'(?:\$[\s\S]|''|[^'])*'|"(?:\$[\s\S]|""|[^"])*"/gu, value => value.replace(/[^\n]/gu, ' '));
189
+ let start = 0;
190
+ for (const statement of text.split(';')) {
191
+ const assignment = statement.indexOf(':=');
192
+ const target = assignment < 0 ? undefined : /([A-Za-z_][A-Za-z0-9_.]*(?:\[[^\]]+\])?)\s*$/u.exec(statement.slice(0, assignment));
193
+ const targetStart = target === undefined || target === null ? -1 : target.index;
194
+ const ambiguous = assignment >= 0 && statement.indexOf(':=', assignment + 2) >= 0;
195
+ for (const match of statement.matchAll(TOKEN)) {
196
+ const symbol = match[0];
197
+ if (KEYWORDS.has(symbol.toUpperCase()) || /^(?:BY|END_FOR|REPEAT|UNTIL|END_REPEAT|CASE|OF|END_CASE|EXIT|END_WHILE)$/iu.test(symbol))
198
+ continue;
199
+ const call = /^\s*\(/u.test(statement.slice(match.index + symbol.length));
200
+ const indexed = symbol.includes('[');
201
+ const access = ambiguous || call || indexed ? 'unknown'
202
+ : assignment >= 0 && match.index === targetStart ? 'write' : 'read';
203
+ const line = text.slice(0, start + match.index).split('\n').length - 1;
204
+ add(out, document, routine, symbol, access, code.split('\n')[line]?.trim() ?? '', { line }, access === 'unknown' ? 'low' : 'medium');
205
+ }
206
+ start += statement.length + 1;
207
+ }
208
+ }
209
+ export function evidenceOf(project) {
210
+ const cached = evidenceCache.get(project);
211
+ if (cached !== undefined)
212
+ return cached;
213
+ const out = [];
214
+ const routines = [...project.document.routines, ...project.document.aois.flatMap(aoi => aoi.routines)];
215
+ for (const routine of routines) {
216
+ if (routine.body.type === 'RLL') {
217
+ for (const rung of routine.body.rungs)
218
+ ladderEvidence(out, project.document, routine, rung.text, rung.number);
219
+ }
220
+ else if (routine.body.type === 'ST') {
221
+ structuredEvidence(out, project.document, routine, routine.body.code);
222
+ }
223
+ else if (routine.body.type === 'FBD' || routine.body.type === 'SFC') {
224
+ for (const node of routine.body.nodes) {
225
+ const expression = node.operand ?? (node.kind === 'Line' ? node.text : undefined);
226
+ if (expression === undefined)
227
+ continue;
228
+ const access = node.kind === 'IRef' ? 'read' : node.kind === 'ORef' ? 'write' : 'unknown';
229
+ for (const symbol of tokens(expression))
230
+ add(out, project.document, routine, symbol, access, `${node.kind} sheet ${node.sheet} id ${node.id ?? '?'}`, { sheet: node.sheet, ...(node.id === undefined ? {} : { nodeId: node.id }) }, access === 'unknown' ? 'low' : 'medium');
231
+ }
232
+ }
233
+ }
234
+ evidenceCache.set(project, out);
235
+ return out;
236
+ }
237
+ export function findReferences(project, symbol, scope) {
238
+ const query = symbol.toLowerCase();
239
+ return evidenceOf(project).filter(entry => (scope === undefined || entry.scope === scope) && (entry.symbol.toLowerCase() === query || entry.symbol.toLowerCase().startsWith(`${query}.`) ||
240
+ entry.resolved?.toLowerCase() === query || entry.resolved?.toLowerCase().endsWith(`::${query}`) ||
241
+ entry.resolved?.toLowerCase().includes(`::${query}.`)));
242
+ }
243
+ export function traceSymbol(project, symbol, scope) {
244
+ const references = findReferences(project, symbol, scope);
245
+ const writers = references.filter(r => r.access === 'write' || r.access === 'read_write');
246
+ const all = evidenceOf(project);
247
+ const originCandidates = writers.map(writer => ({
248
+ writer,
249
+ inputsAtLocation: all.filter(ref => ref.scope === writer.scope && ref.routine === writer.routine && ref.rung === writer.rung && ref.line === writer.line && ref.sheet === writer.sheet && ref.access === 'read'),
250
+ }));
251
+ return {
252
+ symbol, references, writers, originCandidates,
253
+ readers: references.filter(r => r.access === 'read' || r.access === 'read_write'),
254
+ complete: false,
255
+ classification_complete: references.length > 0 && references.every(r => r.access !== 'unknown' && r.resolved !== undefined) && project.features.functionBlock !== 'partial' && project.features.sequentialFunctionChart !== 'partial',
256
+ note: 'Inputs at a writer location are candidates, not proven causal or runtime data flow. External controller behavior is not analyzed.',
257
+ };
258
+ }
@@ -0,0 +1,9 @@
1
+ /** Capability guidance separates offline analysis, SDK builds and live diagnostics. */
2
+ import type { SdkStatus } from './studio-sdk.ts';
3
+ import type { ProjectSnapshot } from '../session.ts';
4
+ /** Resolve an installed SDK report against the selected export revision.
5
+ * @param sdk - local dependency observations, without controller contact.
6
+ * @param project - optional selected export.
7
+ * @returns capability availability and concrete workflow steps.
8
+ */
9
+ export declare function engineeringCapabilities(sdk: SdkStatus, project?: ProjectSnapshot): object;
@@ -0,0 +1,40 @@
1
+ /** The SDK opens projects at these revisions. */
2
+ const OPEN_MINIMUM = 31;
3
+ /** Build is a Logix Designer project-revision feature, not an installed-app feature. */
4
+ const BUILD_MINIMUM = 37;
5
+ /** Resolve an installed SDK report against the selected export revision.
6
+ * @param sdk - local dependency observations, without controller contact.
7
+ * @param project - optional selected export.
8
+ * @returns capability availability and concrete workflow steps.
9
+ */
10
+ export function engineeringCapabilities(sdk, project) {
11
+ const revision = project === undefined ? undefined : Number.parseInt(project.softwareRevision, 10);
12
+ // The SDK opens a project with the Logix Designer release matching its revision,
13
+ // so an installed copy of that exact revision is required.
14
+ const matching = revision === undefined || sdk.installed_studio_versions.includes(revision);
15
+ const opening = sdk.available && matching && (revision === undefined || revision >= OPEN_MINIMUM);
16
+ const build = opening && (revision === undefined || revision >= BUILD_MINIMUM);
17
+ const versionAxes = {
18
+ engine_selected_by: 'project_revision',
19
+ open_supported_project_revisions: `>= ${OPEN_MINIMUM}`,
20
+ build_supported_project_revisions: `>= ${BUILD_MINIMUM}`,
21
+ sdk_release: sdk.sdk_release,
22
+ installed_logix_designer_releases: sdk.installed_studio_versions,
23
+ observed_build_refusal: 'Operation not supported on Logix Designer version 36.0',
24
+ };
25
+ return {
26
+ observed_at: new Date().toISOString(), controller_contacted: false,
27
+ project_revision: project?.softwareRevision ?? null, sdk, version_axes: versionAxes,
28
+ capabilities: {
29
+ l5x_analysis: { available: true, tools: ['open_project', 'project_overview', 'get_component', 'find_references', 'trace_symbol', 'run_checks', 'compare_projects'] },
30
+ parsed_analysis: { available: true, tools: ['resolve_tag', 'analyze_project', 'find_parsed_references', 'trace_signal', 'analyze_impact', 'project_execution_graph'], runtime_causality: 'unverified', diagram_execution: 'unsupported' },
31
+ draft_editing: { available: true, tools: ['propose_change', 'validate_proposal', 'prepare_change_set', 'validate_change_set', 'apply_change_set', 'propose_udt_change'], studio_validation: 'required', coordinated_operations: ['replace_rung', 'insert_rung', 'delete_rung', 'replace_st', 'add_routine', 'add_tag', 'set_tag_description', 'add_udt_member', 'set_udt_description', 'set_udt_member_description'] },
32
+ references: { available: true, tools: ['rockwell_reference_sources', 'rockwell_reference_sync', 'rockwell_reference_search'], downloads_optional: true },
33
+ acd_export: { available: opening, next_step: opening ? 'Export the selected ACD to a new L5X and open it.' : 'Export an L5X manually in the matching Studio 5000 version; continue offline analysis.' },
34
+ offline_build: { available: build, project_selected: project !== undefined, requires_project_revision: BUILD_MINIMUM, probe_tool: 'verify_offline_l5x with probe=true', next_step: build ? 'Review a full baseline and draft, then verify_offline_l5x or edit_offline_acd.' : `Use Studio 5000 Import + Verify manually. Build is supported only for project revision ${BUILD_MINIMUM}+ because the SDK selects the engine by project revision; probe=true attempts it and reports the SDK error.` },
35
+ live_diagnostics: { available: opening && sdk.live_communications_available === true, tools: ['read_controller_mode', 'read_live_tag', 'sample_live_tags', 'observe_logic'], next_step: opening && sdk.live_communications_available ? 'Obtain explicit controller IP and use a matching uploaded ACD snapshot.' : 'Continue offline inspection; resolve SDK and FactoryTalk Linx dependencies before live reads.' },
36
+ controller_transfers: { available: opening && sdk.live_communications_available === true, approval: 'per-operation human approval required; call controller_approval_status', download: 'verified artifact and Program mode required' },
37
+ },
38
+ limitation: revision === undefined ? 'Select an export to verify its revision against installed Studio versions.' : !matching ? `Matching Studio ${revision} is not installed; the SDK opens a project with the release matching its revision.` : !sdk.available ? sdk.reason : null,
39
+ };
40
+ }
@@ -0,0 +1,122 @@
1
+ import { type ProjectSnapshot } from '../session.ts';
2
+ type Operation = {
3
+ readonly operation: 'replace_rung';
4
+ readonly program: string;
5
+ readonly routine: string;
6
+ readonly rung: number;
7
+ readonly text: string;
8
+ } | {
9
+ readonly operation: 'delete_rung';
10
+ readonly program: string;
11
+ readonly routine: string;
12
+ readonly rung: number;
13
+ } | {
14
+ readonly operation: 'insert_rung';
15
+ readonly program: string;
16
+ readonly routine: string;
17
+ readonly rung: number;
18
+ readonly text: string;
19
+ readonly before_rung?: number;
20
+ } | {
21
+ readonly operation: 'replace_st';
22
+ readonly program: string;
23
+ readonly routine: string;
24
+ readonly code: string;
25
+ } | {
26
+ readonly operation: 'add_routine';
27
+ readonly program: string;
28
+ readonly name: string;
29
+ readonly language: 'RLL' | 'ST';
30
+ readonly code: string;
31
+ } | {
32
+ readonly operation: 'add_tag';
33
+ readonly scope: string;
34
+ readonly name: string;
35
+ readonly data_type: string;
36
+ readonly value?: number;
37
+ readonly dimensions?: string;
38
+ readonly external_access?: string;
39
+ readonly description: string;
40
+ } | {
41
+ readonly operation: 'set_tag_description';
42
+ readonly scope: string;
43
+ readonly name: string;
44
+ readonly description: string;
45
+ } | {
46
+ readonly operation: 'add_udt_member';
47
+ readonly udt: string;
48
+ readonly name: string;
49
+ readonly data_type: string;
50
+ readonly description: string;
51
+ } | {
52
+ readonly operation: 'set_udt_description';
53
+ readonly udt: string;
54
+ readonly description: string;
55
+ } | {
56
+ readonly operation: 'set_udt_member_description';
57
+ readonly udt: string;
58
+ readonly member: string;
59
+ readonly description: string;
60
+ };
61
+ /** Compact contract returned with validation errors and embedded in tool descriptions. */
62
+ export declare const CHANGE_SET_HELP = "Operations (each also needs \"operation\"):\nreplace_rung {program,routine,rung,text}\ninsert_rung {program,routine,rung,text,before_rung?}\ndelete_rung {program,routine,rung}\nreplace_st {program,routine,code}\nadd_routine {program,name,language:\"RLL\"|\"ST\",code}\nadd_tag {scope,name,data_type,description,value? scalar, dimensions? \"4 2\", external_access? \"Read/Write\"|\"Read Only\"|\"None\"}\nset_tag_description {scope,name,description}\nadd_udt_member {udt,name,data_type,description}\nset_udt_description {udt,description}\nset_udt_member_description {udt,member,description}\nIdentifiers are at most 40 characters. Example: {\"version\":1,\"operations\":[{\"operation\":\"add_tag\",\"scope\":\"controller\",\"name\":\"SP_Start\",\"data_type\":\"REAL\",\"value\":80,\"description\":\"Start threshold\"}]}";
63
+ /** Limits are resolved by the plugin's configuration before tools call this module. */
64
+ export interface ChangeSetLimits {
65
+ readonly operations: number;
66
+ readonly bytes: number;
67
+ }
68
+ /** One prepared draft and its review data. */
69
+ export interface PreparedChangeSet {
70
+ readonly xml: string;
71
+ readonly operations: readonly Operation[];
72
+ readonly source_sha256: string;
73
+ readonly draft_sha256: string;
74
+ readonly diff: unknown;
75
+ readonly checks: unknown;
76
+ readonly validation: {
77
+ readonly xml: string;
78
+ readonly studio_import: string;
79
+ readonly studio_build: string;
80
+ readonly behavior: string;
81
+ };
82
+ readonly prepared: 'computed' | 'cache';
83
+ }
84
+ /** Prepare every operation in memory, without writing a draft or touching Studio.
85
+ * @param project - Hash-verified baseline.
86
+ * @param request - Version-1 JSON change set.
87
+ * @param limits - Validated operation and draft-size limits.
88
+ * @returns Draft text and review data; malformed, unsupported or protected changes throw.
89
+ */
90
+ export declare function prepareChangeSet(project: ProjectSnapshot, request: string, limits: ChangeSetLimits): PreparedChangeSet;
91
+ /** One changed data type as a Studio Data Type import component.
92
+ * @param project - Hash-verified baseline.
93
+ * @param request - Version-1 JSON change set holding one or more UDT operations.
94
+ * @returns Component L5X text plus the type-level difference; no file is written.
95
+ */
96
+ export declare function prepareUdtComponent(project: ProjectSnapshot, request: string): {
97
+ xml: string;
98
+ operations: readonly Operation[];
99
+ udt: string;
100
+ source_sha256: string;
101
+ component_sha256: string;
102
+ changes: {
103
+ before: import("../model/types.ts").L5xUdt;
104
+ after: import("../model/types.ts").L5xUdt;
105
+ };
106
+ validation: {
107
+ xml: string;
108
+ studio_import: "not_run";
109
+ };
110
+ };
111
+ export {};
112
+ /** Stage a fully prepared change set into a new file, retaining proposal validation compatibility.
113
+ * @param ctx - Filesystem service.
114
+ * @param session - Calling chat's project/proposal records.
115
+ * @param project - Current source snapshot.
116
+ * @param request - Version-1 operations JSON.
117
+ * @param outputPath - New draft beside the source.
118
+ * @param expectedHash - Baseline hash reviewed during preparation.
119
+ * @param limits - Operation and byte limits.
120
+ * @param signal - Cancellation for filesystem operations.
121
+ * @returns Proposal identity, hash-bound changes and static validation; no SDK or controller operations.
122
+ */