@aryacode/dsh-engineering 0.4.6 → 0.4.20

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 (161) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +37 -26
  3. package/cordis.patch.yml +7 -6
  4. package/entries/config.js +62 -0
  5. package/entries/ftview.js +5 -4
  6. package/entries/l5x.js +61 -13
  7. package/entries/mwi.js +26 -6
  8. package/entries/optix.js +105 -0
  9. package/lib/client.js +335 -6
  10. package/package.json +11 -10
  11. package/vendor/l5x/README.md +27 -5
  12. package/vendor/l5x/studio-bridge/NuGet.Config +1 -1
  13. package/vendor/mwi/README.md +39 -18
  14. package/vendor/mwi/SKILL.md +26 -20
  15. package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -2763
  16. package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -30
  17. package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -22
  18. package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -339
  19. package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -765
  20. package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -399
  21. package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +313 -313
  22. package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -520
  23. package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -133
  24. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -672
  25. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -86
  26. package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -208
  27. package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +128 -128
  28. package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +673 -673
  29. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -790
  30. package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +642 -642
  31. package/vendor/mwi/lib/core/code-engine.d.ts +13 -0
  32. package/vendor/mwi/lib/core/code-engine.js +18 -1
  33. package/vendor/mwi/lib/edit.d.ts +1 -1
  34. package/vendor/mwi/lib/edit.js +1 -1
  35. package/vendor/mwi/lib/read/service.js +8 -1
  36. package/vendor/mwi/lib/session/backup.d.ts +55 -0
  37. package/vendor/mwi/lib/session/backup.js +232 -0
  38. package/vendor/mwi/lib/session/project.d.ts +4 -1
  39. package/vendor/mwi/lib/session/project.js +13 -2
  40. package/vendor/mwi/lib/session/service.d.ts +13 -6
  41. package/vendor/mwi/lib/session/service.js +127 -36
  42. package/vendor/mwi/lib/session/workspace.d.ts +10 -0
  43. package/vendor/mwi/lib/session/workspace.js +53 -3
  44. package/vendor/mwi/lib/skill.d.ts +2 -2
  45. package/vendor/mwi/lib/skill.js +2 -2
  46. package/vendor/mwi/lib/tools/edit.d.ts +3 -3
  47. package/vendor/mwi/lib/tools/edit.js +14 -15
  48. package/vendor/mwi/lib/tools/read.js +1 -1
  49. package/vendor/mwi/lib/tools/session.js +13 -9
  50. package/vendor/optix/templates/arya-optix-ipc/project/AryaNewHMIProject_IPC.optix +81 -81
  51. package/vendor/optix/templates/arya-optix-ipc/project/AryaNewHMIProject_IPC.optix.design +38 -38
  52. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Alarms/Alarms.yaml +3 -3
  53. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/AryaNewHMIProject_IPC.yaml +67 -67
  54. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/CommDrivers/CommDrivers.yaml +2 -2
  55. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Converters/Converters.yaml +2 -2
  56. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/DataStores/DataStores.yaml +2 -2
  57. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Loggers/Loggers.yaml +2 -2
  58. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/MQTT/MQTT.yaml +2 -2
  59. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Model/Model.yaml +2 -2
  60. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/NetLogic/NetLogic.yaml +26 -26
  61. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/OPC-UA/OPC-UA.yaml +2 -2
  62. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Recipes/Recipes.yaml +2 -2
  63. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Reports/Reports.yaml +2 -2
  64. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Retentivity/Retentivity.yaml +57 -57
  65. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Groups/Groups.yaml +3 -3
  66. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Roles/Roles.yaml +3 -3
  67. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Security.yaml +7 -7
  68. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Users/Users.yaml +3 -3
  69. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/System/System.yaml +2 -2
  70. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Translations/Translations.yaml +16 -16
  71. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/UI/Keyboards/Keyboards.yaml +2 -2
  72. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/UI/Screens/Screens.yaml +2 -2
  73. package/vendor/optix/templates/arya-optix-ipc/project/Nodes/UI/UI.yaml +250 -250
  74. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/.vscode/launch.json +18 -18
  75. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.code-workspace +9 -9
  76. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.csproj +19 -19
  77. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.references +172 -172
  78. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.sln +21 -21
  79. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/Private/TypeConstants.cs +20 -20
  80. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/Private/UITypeDefinitions.cs +11 -11
  81. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/StudioMCPBridge.cs +4340 -4340
  82. package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/UserDefinedModule.xml +26 -26
  83. package/vendor/optix/templates/arya-optix-panel/project/AryaNewHMIProject_OptixPanel.optix +97 -97
  84. package/vendor/optix/templates/arya-optix-panel/project/AryaNewHMIProject_OptixPanel.optix.design +38 -38
  85. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Alarms/Alarms.yaml +3 -3
  86. package/vendor/optix/templates/arya-optix-panel/project/Nodes/AryaNewHMIProject_OptixPanel.yaml +67 -67
  87. package/vendor/optix/templates/arya-optix-panel/project/Nodes/CommDrivers/CommDrivers.yaml +2 -2
  88. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Converters/Converters.yaml +2 -2
  89. package/vendor/optix/templates/arya-optix-panel/project/Nodes/DataStores/DataStores.yaml +2 -2
  90. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Loggers/Loggers.yaml +2 -2
  91. package/vendor/optix/templates/arya-optix-panel/project/Nodes/MQTT/MQTT.yaml +2 -2
  92. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Model/Model.yaml +2 -2
  93. package/vendor/optix/templates/arya-optix-panel/project/Nodes/NetLogic/NetLogic.yaml +26 -26
  94. package/vendor/optix/templates/arya-optix-panel/project/Nodes/OPC-UA/OPC-UA.yaml +2 -2
  95. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Recipes/Recipes.yaml +2 -2
  96. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Reports/Reports.yaml +2 -2
  97. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Retentivity/Retentivity.yaml +57 -57
  98. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Groups/Groups.yaml +3 -3
  99. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Roles/Roles.yaml +3 -3
  100. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Security.yaml +7 -7
  101. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Users/Users.yaml +3 -3
  102. package/vendor/optix/templates/arya-optix-panel/project/Nodes/System/System.yaml +444 -444
  103. package/vendor/optix/templates/arya-optix-panel/project/Nodes/Translations/Translations.yaml +81 -81
  104. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/Keyboards/Keyboards.yaml +2 -2
  105. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/Screens/Screens.yaml +2 -2
  106. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/DeviceSettingsWidget/DeviceSettingsWidget.yaml +163 -163
  107. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/DeviceSettingsWidget/DeviceSettingsWidgetComponents/DeviceSettingsWidgetComponents.yaml +1663 -1663
  108. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/FactoryTalkRemoteAccessWidget/FTRemoteAccessWidgetComponents/FTRemoteAccessWidgetComponents.yaml +2471 -2471
  109. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/FactoryTalkRemoteAccessWidget/FactoryTalkRemoteAccessWidget.yaml +218 -218
  110. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/NetworkSettingsWidget/NetworkSettingsWidget.yaml +524 -524
  111. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/NetworkSettingsWidget/NetworkSettingsWidgetComponents/NetworkSettingsWidgetComponents.yaml +690 -690
  112. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/SettingsWidgets.yaml +1265 -1265
  113. package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/UI.yaml +252 -252
  114. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/.vscode/launch.json +18 -18
  115. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/ApplicationNameLogic.cs +19 -19
  116. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.code-workspace +9 -9
  117. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.csproj +19 -19
  118. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.references +172 -172
  119. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.sln +21 -21
  120. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/ConnectionSwitchLogic.cs +74 -74
  121. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/DateAndTimeWidgetLogic.cs +420 -420
  122. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/DeviceSettingsWidgetLogic.cs +34 -34
  123. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/FTOptixStudioVersionLogic.cs +47 -47
  124. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/FTRemoteAccessWidgetSetupLogic.cs +153 -153
  125. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/IncomingConnectionApprovalLogic.cs +118 -118
  126. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/IncomingConnectionReceiverLogic.cs +166 -166
  127. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkConfiguratorEditModelsLogic.cs +56 -56
  128. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkInterfaceLogic.cs +138 -138
  129. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkInterfacesEditModelReaderLogic.cs +87 -87
  130. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkInterfacesEditModelToSystemWriterLogic.cs +139 -139
  131. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkSettingsLogic.cs +168 -168
  132. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/Private/FTRemoteAccessWidgetDataObject.cs +106 -106
  133. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/Private/TypeConstants.cs +37 -37
  134. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/Private/UITypeDefinitions.cs +91 -91
  135. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/RequestAssistanceSwitchLogic.cs +93 -93
  136. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/StudioMCPBridge.cs +4340 -4340
  137. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/SystemToNetworkInterfacesEditModelWriterLogic.cs +77 -77
  138. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/UserDefinedModule.xml +111 -111
  139. package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/WarningIcon.svg +4 -4
  140. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  141. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
  142. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
  143. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
  144. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
  145. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
  146. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
  147. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
  148. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
  149. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
  150. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
  151. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
  152. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
  153. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
  154. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
  155. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
  156. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
  157. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
  158. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
  159. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
  160. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
  161. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,204 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.20] - 2026-10-27
4
+
5
+ ### Fixed
6
+
7
+ - **The bundle no longer ships Python bytecode.** `__pycache__` and `*.pyc` from the MotionWorks
8
+ engine are ignored and excluded, so the tarball contains only the sources the plugin runs.
9
+
10
+ ## [0.4.19] - 2026-10-27
11
+
12
+ ### Fixed
13
+
14
+ - **Reads without a project name find the attached project.** `mw_get`, `mw_help` and `mw_search`
15
+ resolve the single project with a verified pre-edit backup when no `project` is passed, so the
16
+ direct workflow no longer fails with `NO_PROJECT`. A leftover `.motionworks/stage` copy no longer
17
+ shadows that project, and two attached projects are reported as `AMBIGUOUS_PROJECT` with both names.
18
+
19
+ ## [0.4.18] - 2026-10-27
20
+
21
+ ### Fixed
22
+
23
+ - **MotionWorks edits no longer demand a body hash the caller had to guess.** `mw_edit_code` with
24
+ `changes` hashes the body it is about to patch when `expected_body_sha256` is omitted; the backup
25
+ and exact find/replace still guard the write, and a supplied hash is still honored.
26
+ - **A stale stage no longer makes the direct workflow ambiguous.** When a project has a verified
27
+ pre-edit backup, `singleStage()` now prefers that project over old `.motionworks/stage` copies.
28
+
29
+ ## [0.4.17] - 2026-10-27
30
+
31
+ ### Fixed
32
+
33
+ - **A direct MotionWorks open was refused as "outside the staging root".** 0.4.13 promised a
34
+ backup-first direct open, but the copy was written to `<workspace>/backups/<project>/<timestamp>`
35
+ while the shipped bridge (`Get-DirectIdentity`) and the read-only engine (`assert_direct_attached`)
36
+ only accept an identity record at `<workspace>/.motionworks/attached/records/<id>.json` naming a
37
+ backup under `.motionworks/attached/backups`. The two never met: `mw_project_open` refused every
38
+ project the retired solo plugin had not already attached, which is why MotionWorks work still looked
39
+ like it required staging. The backup module now writes exactly that layout, verifies the copy before
40
+ writing the identity, and refuses to replace an identity that no longer verifies.
41
+ - **A project already open in the IDE could not be edited.** Edits, compile and verify required a
42
+ proven backup and failed with `NOT_ATTACHED` for a workspace project the IDE already had open.
43
+ `mw_project_open` now attaches that project — writes the verified backup and reports
44
+ `already_open` — instead of calling `OpenProject`, because a reload over unsaved editor changes is
45
+ data loss the backup cannot undo. An edit, compile or verify that finds an unattached workspace
46
+ project takes the backup before it refuses.
47
+ - **`mw_edit_code` could not be given the hash it demands.** Its precondition is the SHA-256 of the
48
+ saved body, and the tool tells the caller to read `text_body_sha256` from `mw_get`; `mw_get`
49
+ returned the body but no hash, so the hash had to be found elsewhere or guessed and the edit failed
50
+ with "Saved code differs from expected_body/hash". `mw_get` now returns the `text_body_sha256` of
51
+ exactly the body it returns.
52
+ - **A read before the open still pointed at staging.** The engine guard's refusal text belongs to the
53
+ retired staging flow and told the caller to stage a copy, which is what sent an agent back into
54
+ staging after it had been removed from the workflow. The engine boundary now translates that
55
+ refusal into `NOT_ATTACHED` with the fix "call `mw_project_open` first"; every other refusal keeps
56
+ its own text.
57
+
58
+ ### Documentation
59
+
60
+ - `packages/mwi/SKILL.md`: the backup lives under `<workspace>/.motionworks/attached/`, an
61
+ already-open project is attached and not reloaded, and the edit section names the hash `mw_get`
62
+ returns.
63
+ - `packages/mwi/README.md`: rows, backup layout, the attach-without-reload path and the guarded read,
64
+ including what the identity check does and does not recompute.
65
+ - README: the MotionWorks row, the composition diagram, the tool groups and the safety section
66
+ describe a direct edit. The row-default table and diagram now match the shipped defaults (every
67
+ surface on), which the 0.4.15 release changed but the diagram still showed as off.
68
+
69
+ ## [0.4.16] - 2026-10-27
70
+
71
+ ### Added
72
+
73
+ - **FactoryTalk Optix OCR is configurable from Plugins.** The `optix` row now
74
+ carries `ocrExecutable`, `ocrLanguage` and `ocrTessdataDirectory`. A configured
75
+ executable registers `optix_ocr`; an empty path keeps OCR off. An empty tessdata
76
+ folder derives `tessdata` beside the executable. OCR still requires the preview
77
+ browser and one matching tab, because it reads the rendered canvas.
78
+
79
+ ### Fixed
80
+
81
+ - **An empty preview URL no longer breaks `optix_preview` and `optix_screenshot`.**
82
+ A saved blank `previewUrl` or `cdpUrl` now falls back to the loopback defaults
83
+ instead of failing with `Invalid URL`.
84
+
85
+ ## [0.4.15] - 2026-10-27
86
+
87
+ ### Changed
88
+
89
+ - **Every tool surface ships on.** A fresh install no longer hides the expensive
90
+ halves: L5X gets `editing`, `studio`, `live` and `allowWrites`; MotionWorks IEC
91
+ gets `edit`; FactoryTalk Optix gets live authoring, destructive operations,
92
+ exported-method invocation, the emulator preview and preview input. The defaults
93
+ live in each row's `Config` schema, so the Configure page shows them on and a
94
+ read-only profile can still switch them off.
95
+ - Docs, row descriptions and the generated tool reference now describe the
96
+ on-by-default behaviour. `docs/architecture.md`, `README.md`, `cordis.patch.yml`
97
+ and the settings page copy were updated with the schemas.
98
+
99
+ ## [0.4.14] - 2026-10-27
100
+
101
+ ### Added
102
+
103
+ - **FactoryTalk Optix has a Configure page.** The `optix` row now mounts through
104
+ `entries/optix.js`, which owns a volatile config schema and hands the prebuilt
105
+ `@aryacode/dsh-tool-ft-optix` plugin plain options. Configure can set the Studio
106
+ bridge origins and token, live authoring, destructive operations, exported-method
107
+ invocation, the emulator preview and preview input. Live authoring is on by default from 0.4.15; the Configure page can
108
+ switch it off. Reads work as soon as a project with the Studio bridge is open.
109
+ ## [0.4.13] - 2026-10-27
110
+
111
+ ### Changed
112
+
113
+ - **MotionWorks edits the real project again.** `mw_project_open` and `mw_ide_start` now take a
114
+ workspace `.mwt` and work on its files directly. Before the first write they copy the wrapper and
115
+ the expanded project into `<workspace>/backups/<project>/<timestamp>/`, hash every copied file in
116
+ `backup-manifest.json`, verify the copy, and reuse that backup on later attaches. `mw_apply` saves
117
+ in place; there is no sync-back step.
118
+ - **Staging is optional.** `mw_project_stage` remains for a sandbox copy, but the skill and tool
119
+ descriptions lead with the direct path.
120
+
121
+ ## [0.4.12] - 2026-10-27
122
+
123
+ ### Fixed
124
+
125
+ - **The MotionWorks workspace setting was ignored.** `workspace` is volatile, so it arrives as a
126
+ live reference; passing the reference to `configureWorkspace` did nothing. The row now unwraps it
127
+ with `text()`, so the value saved in Configure is the folder the tools search.
128
+
129
+ ## [0.4.11] - 2026-10-27
130
+
131
+ ### Fixed
132
+
133
+ - **MotionWorks editing tools never mounted even when the switch was on.** A `.volatile()` config
134
+ field reaches `apply()` as a live accessor, so `config.edit === true` was always false and the
135
+ seven edit tools were never registered. Every row now resolves switches through
136
+ `entries/config.js` (`flag`/`list`).
137
+ - **Flipping a switch did not take effect until the row was disabled and re-enabled.** The loader
138
+ commits volatile-only changes without re-running `apply()`, then emits `loader/volatile-update`.
139
+ The L5X and MotionWorks rows now mount and unmount their optional surfaces from that event.
140
+ - **MotionWorks looked for projects in the profile directory.** The host does not always pass the
141
+ session cwd, so `workspaceOf()` fell back to `process.cwd()`. The row now carries a `workspace`
142
+ setting (shown in Configure) and falls back to the `mwi.workspace` marker in the profile, then the
143
+ process directory. Project discovery follows directory junctions without looping.
144
+
145
+ ## [0.4.10] - 2026-10-26
146
+
147
+ ### Fixed
148
+
149
+ - **The row settings page sat on "Loading configuration…".** The Plugins page snapshots a row's
150
+ configuration form once when it opens and does not subscribe to it, so a form that was still
151
+ loading at that moment never produced the switches. The page now reads the live form from the
152
+ client `configForms` service, whose `get` starts the one describe fetch, and subscribes to it;
153
+ the switches appear when the settings settle, and a row with no configuration says so instead of
154
+ loading forever.
155
+ - **The row switches were not served to Settings at all.** The config fields were plain
156
+ `z.boolean()` values, and the settings domain only publishes fields marked `.volatile()`; the
157
+ rows therefore had no settings namespace for the page to bind to. `editing`, `studio`, `live`,
158
+ `allowWrites`, `allowedAddresses`, `ftview.edit`/`studio` and `mwi.edit` are now volatile, so the
159
+ Configure page edits the running row instead of remounting it.
160
+
161
+ ## [0.4.9] - 2026-10-26
162
+
163
+ ### Fixed
164
+
165
+ - Republished the row settings pages with the client half's declared service list taken from every
166
+ bundled face rather than the two viewer faces. Same pages and same defaults as 0.4.8; this build is
167
+ the installable one.
168
+
169
+ ## [0.4.8] - 2026-10-26
170
+
171
+ ### Fixed
172
+
173
+ - **The product rows had no settings page.** The bundle's client half never registered a
174
+ `plugins.row.config` entry, so the Plugins page showed no Configure control for `l5x`, `ftview` or
175
+ `mwi`, and the switches the README documents were unreachable without editing the profile patch by
176
+ hand. `scripts/faces/settings.js` now registers one page per row: L5X editing, the Studio 5000 SDK,
177
+ live EtherNet/IP and the two live writes; FactoryTalk View ME edits and the Studio handoff;
178
+ MotionWorks IEC edits. Values are staged and saved through the row's own configuration form, and the
179
+ defaults are unchanged.
180
+
181
+ ## [0.4.7] - 2026-10-25
182
+
183
+ ### Fixed
184
+
185
+ - **`allowWrites` could not be reached from the L5X row.** The composite entry mounted the live entry
186
+ with no config, so the write switch had no path from the Plugins page. `entries/l5x.js` now declares
187
+ `allowWrites` and `allowedAddresses` and forwards both into the live entry; the write tools still
188
+ refuse without per-call approval.
189
+
190
+ ### Documentation
191
+
192
+ - README: the safety section states the two keys a live write takes, the tool groups list all seven
193
+ EtherNet/IP tools, and the L5X count is 40 (84 with Optix).
194
+ - `docs/examples.md`: a live section covering batch reads, discovery, the controller clock, the trend,
195
+ and a supervised write with the exact fields the reader must see and the refusals they may hit.
196
+ - `docs/architecture.md`: a new EtherNet/IP section — the two workers, the transaction engine's deadline
197
+ and decoding, and the four gates that stand between a write request and a controller.
198
+ - `docs/port/l5x.md`: the live surface is recorded as seven tools with the driver call each one uses, and
199
+ the unused capability (CIP subscriptions, UDT `Structure` reads) is named.
200
+ - `packages/l5x/README.md`: read and write tool tables, and the full live configuration row.
201
+
3
202
  ## [0.4.6] - 2026-10-25
4
203
 
5
204
  ### Added
package/README.md CHANGED
@@ -14,17 +14,19 @@ A general assistant pays for every tool schema on every request. An automation e
14
14
  surfaces — reading a 3 MB L5X, comparing two projects, staging ladder edits, talking to a controller —
15
15
  but only one or two of them at a time.
16
16
 
17
- This bundle splits those surfaces into one row per product. The expensive surfaces are a switch inside
18
- the row, off by default, so a read-only profile pays for the read tools and nothing else:
17
+ This bundle splits those surfaces into one row per product. The expensive surfaces are switches inside
18
+ the row. They ship on by default so a fresh install can use every tool; switch off what a
19
+ read-only or low-context profile does not need:
19
20
 
20
21
  | Product row | Config option | Default | What turning it on adds |
21
22
  |---|---|---|---|
22
- | `l5x` | `editing` | off | the nine `draft_*` schemas |
23
- | `l5x` | `studio` | off | the Logix Designer SDK and supervised controller schemas |
24
- | `l5x` | `live` | off | the EtherNet/IP read, batch, discovery, clock and trend schemas |
25
- | `l5x` | `allowWrites` | off | the two approval-gated writes (tag, wall clock) |
23
+ | `l5x` | `editing` | on | the nine `draft_*` schemas |
24
+ | `l5x` | `studio` | on | the Logix Designer SDK and supervised controller schemas |
25
+ | `l5x` | `live` | on | the EtherNet/IP read, batch, discovery, clock and trend schemas |
26
+ | `l5x` | `allowWrites` | on | the two approval-gated writes (tag, wall clock) |
26
27
  | `ftview` | `edit`, `studio` | on | staged ME edits and the Studio handoff |
27
- | `mwi` | `edit` | off | the seven MotionWorks edit schemas |
28
+ | `mwi` | `edit` | on | the seven MotionWorks edit schemas |
29
+ | `optix` | `allowWrites`, `allowDestructive`, `allowInvoke`, `preview`, `ocrExecutable` | on | Studio authoring, destructive ops, method invocation, emulator preview and OCR when Tesseract is set |
28
30
 
29
31
  The previews are independent of the tools — switching a product off does not remove the ladder viewer.
30
32
  Both previews mount from **one** row (`viewers`), because a package registers exactly one browser
@@ -34,17 +36,17 @@ bundle.
34
36
 
35
37
  | Product | Tools | Default | Needs |
36
38
  |---|---|---|---|
37
- | **Studio 5000 L5X** | 40 | read on; edit, Studio, live **off** | Node for offline reads; Studio 5000 + Logix Designer SDK for SDK work; a controller for live |
39
+ | **Studio 5000 L5X** | 40 | all on | Node for offline reads; Studio 5000 + Logix Designer SDK for SDK work; a controller for live |
38
40
  | **FactoryTalk View ME** | 14 | all on | Node for an export folder; FactoryTalk View Studio ME for discovery and the handoff |
39
- | **MotionWorks IEC** | 24 | session, read, verify on; edit **off** | MotionWorks IEC 3 Pro installed; verified against 1.19 |
40
- | **FactoryTalk Optix** | 6 | on | Optix Studio installed |
41
+ | **MotionWorks IEC** | 24 | all on | MotionWorks IEC 3 Pro installed; verified against 1.19 |
42
+ | **FactoryTalk Optix** | 9 (3 need preview) | all on | Optix Studio installed; Tesseract for OCR |
41
43
 
42
44
  Tool groups, by product:
43
45
 
44
46
  - **L5X** — 11 read (`open_project` … `compare_projects`), 9 staged drafts (`draft_*`), 6 Logix
45
47
  Designer SDK, 7 supervised controller, 7 live EtherNet/IP (2 reads, 2 identity/discovery, 1 batch read, 2 approval-gated writes)
46
48
  - **FactoryTalk View** — 6 read, 5 staged edits, 3 Studio handoff
47
- - **MotionWorks IEC** — 7 session, 7 saved-project reads, 3 verify, 7 staged edits
49
+ - **MotionWorks IEC** — 7 session, 7 saved-project reads, 3 verify, 7 direct edits
48
50
  - **Optix** — 6 tools, unlinked to a skill until one exists
49
51
 
50
52
  ## Switches
@@ -56,11 +58,11 @@ down:
56
58
 
57
59
  | Row | Mounts | Config options (default) |
58
60
  |---|---|---|
59
- | `l5x` | offline reads, checks, comparison and the `rockwell-l5x` guidance; optionally drafts, SDK, live (reads, batch, discovery, clock) and supervised writes | `editing` (off), `studio` (off), `live` (off), `allowWrites` (off) |
61
+ | `l5x` | offline reads, checks, comparison and the `rockwell-l5x` guidance; optionally drafts, SDK, live (reads, batch, discovery, clock) and supervised writes | `editing` (on), `studio` (on), `live` (on), `allowWrites` (on) |
60
62
  | `ftview` | FactoryTalk View ME read, guidance and edits; optionally the Studio handoff | `edit` (on), `studio` (on) |
61
- | `mwi` | MotionWorks IEC session, reads, verify and guidance; optionally the edits | `edit` (off) |
63
+ | `mwi` | MotionWorks IEC session, reads, verify, guidance and the edits | `workspace` (empty), `edit` (on) |
62
64
  | `viewers` | ladder + ME display previews | — |
63
- | `optix` | FactoryTalk Optix Studio authoring | — |
65
+ | `optix` | FactoryTalk Optix Studio authoring | `bridgeUrls` (8768), `bridgeToken` (empty), `allowWrites` (on), `allowDestructive` (on), `allowInvoke` (on), `preview` (on), `allowPreviewInput` (on), `cdpUrl`, `previewUrl`, `ocrExecutable` (empty), `ocrLanguage` (`eng`), `ocrTessdataDirectory` (empty) |
64
66
 
65
67
  **Why the row list collapsed.** The Plugins page lists a bundle's rows flat and skips `cordis:group`
66
68
  containers (`plugin-manager/src/index.ts`: *"Group rows are containers … their children carry the
@@ -90,13 +92,16 @@ Where each surface actually stands, based on what has been exercised.
90
92
  - Live EtherNet/IP: polling, one-request batch reads of up to 64 tags, controller identity and clock, LAN discovery, and a bounded trend
91
93
  - Supervised live writes: `write_live_tag` and `write_controller_clock` need the row's `allowWrites` switch **and** per-call human approval, and report the value the controller reads back
92
94
  - FactoryTalk View ME: export reads, structural checks, comparison, staged display and HMI tag edits
93
- - MotionWorks IEC: verified live against 3 Pro 1.19 — stage, start, worksheet, close, saved-project
94
- reads, compile diagnostics, code replacement, declaration add, POU create/delete, block insertion
95
- and a POU package export/delete/import round trip
95
+ - MotionWorks IEC: verified live against 3 Pro 1.19 — direct open with a verified pre-edit backup
96
+ under `.motionworks/attached`, attach-without-reload to a project the IDE already has open, start,
97
+ worksheet, close, saved-project reads, compile diagnostics, code replacement, declaration add, POU
98
+ create/delete, block insertion and a POU package export/delete/import round trip
96
99
  - Ladder viewer and FactoryTalk View display preview
97
100
 
98
- **Deliberately off by default** — enabled per profile: `l5x-editing`, `l5x-studio`, `l5x-live`, `allowWrites`,
99
- `mwi-edit`. A read-only profile should not pay for nine draft schemas or a controller surface.
101
+ **On by default** — every tool surface mounts on a fresh install. Switch one off from the row's
102
+ Configure page in Plugins (`l5x` editing/Studio/live/allowWrites, `mwi` edit, `optix` authoring/preview)
103
+ when the profile is read-only or the request budget is tight. Tool schemas are sent on every request, so
104
+ the lean profile is the one that turns them off.
100
105
 
101
106
  **Known limits** — stated in the bundle, not hidden:
102
107
 
@@ -105,7 +110,7 @@ Where each surface actually stands, based on what has been exercised.
105
110
  - FBD/SFC semantics are not exercised
106
111
  - FactoryTalk View Studio work is a **manual handoff**: the bundle plans the import and preflights
107
112
  changed files, it does not import
108
- - Optix has no skill, so its 6 tools cannot be switched off
113
+ - Optix has no skill yet; its tools follow the `optix` row config and the Studio bridge
109
114
 
110
115
  **Not in this repository** — CAD mounts as an independent plugin.
111
116
 
@@ -113,6 +118,8 @@ Where each surface actually stands, based on what has been exercised.
113
118
 
114
119
  | Version | What landed |
115
120
  |---|---|
121
+ | **0.4.17** | MotionWorks edits the real project again: the pre-edit backup is written where the bridge and engine verify it, an already-open project is attached instead of reloaded, and `mw_get` returns the body hash the edit takes |
122
+ | **0.4.15** | Every tool surface ships on by default; the row Configure pages switch them off |
116
123
  | **0.4.4** | One row per product: five rows, optional surfaces moved into each row's config |
117
124
  | **0.4.0** | Both previews fold into the bundle's own client face — one package, one browser bundle, 16 rows |
118
125
  | **0.4.6** | Full EtherNet/IP surface: batch reads, discovery, controller clock, and approval-gated tag and clock writes; 84 tools |
@@ -149,9 +156,9 @@ another bundle owns.
149
156
  ```mermaid
150
157
  flowchart TB
151
158
  Profile["Profile bundle list"] --> Bundle["@aryacode/dsh-engineering"]
152
- Bundle -->|insert| L5X["l5x (on) — read + guidance; editing / studio / live off"]
159
+ Bundle -->|insert| L5X["l5x (on) — read + guidance + drafts / SDK / live"]
153
160
  Bundle -->|insert| FTV["ftview (on) — read, edits, Studio handoff"]
154
- Bundle -->|insert| MWI["mwi (on) — session, read, verify; edit off"]
161
+ Bundle -->|insert| MWI["mwi (on) — session, read, verify, edit"]
155
162
  Bundle -->|insert| Viewers["viewers (on) — ladder + ME display previews"]
156
163
  Bundle -->|insert| Optix["optix (on)"]
157
164
  ```
@@ -161,21 +168,25 @@ even when the implementation behind a row is replaced.
161
168
 
162
169
  | Row id | Package entry | Config options |
163
170
  |---|---|---|
164
- | `l5x` | `dsh-engineering-l5x` | `editing` off · `studio` off · `live` off |
171
+ | `l5x` | `dsh-engineering-l5x` | `editing` on · `studio` on · `live` on · `allowWrites` on |
165
172
  | `ftview` | `dsh-engineering-ftview` | `edit` on · `studio` on |
166
- | `mwi` | `dsh-engineering-mwi` | `edit` off |
173
+ | `mwi` | `dsh-engineering-mwi` | `edit` on |
167
174
  | `viewers` | `@aryacode/dsh-engineering` (its own client face: ladder + ME display previews) | — |
168
- | `optix` | `@aryacode/dsh-tool-ft-optix` | — |
175
+ | `optix` | `@aryacode/dsh-engineering/optix` (wraps the prebuilt `@aryacode/dsh-tool-ft-optix`) | `allowWrites` on · `allowDestructive` on · `allowInvoke` on · `preview` on |
169
176
 
170
177
  ## Safety
171
178
 
172
179
  - **Read-only by default.** Reading, checks, comparison and the viewers never change a controller.
173
180
  - **Edits write new files.** `draft_apply` writes one new L5X beside the source and refuses to
174
181
  overwrite; `ftview_apply` writes a new export folder and leaves the source untouched. MotionWorks
175
- edits write only the staged copy and verify unrelated saved and native state.
182
+ edits write the real workspace project, after one complete backup under
183
+ `<workspace>/.motionworks/attached/`, and verify unrelated saved and native state.
176
184
  - **Controller changes need a human.** Upload, mode change and download require per-call approval and
177
185
  a session policy that permits asking. The tools refuse before contacting a controller when approval
178
186
  is unavailable, and a download accepts only a verification id produced by a passed build.
187
+ - **Live writes take two keys.** `write_live_tag` and `write_controller_clock` need the row's
188
+ `allowWrites` switch *and* approval for that exact write; the tag is validated against the export
189
+ first and the result is the value the controller reads back (`WRITE_NOT_VERIFIED` if it differs).
179
190
  - **No hidden vendor execution.** The FactoryTalk View Studio handoff returns a manual import plan; it
180
191
  does not run a vendor utility.
181
192
  - **Coverage is stated.** Every result names what it cannot establish, including whether online logic
package/cordis.patch.yml CHANGED
@@ -6,13 +6,14 @@
6
6
  #
7
7
  # The optional parts are the row's config, not separate rows:
8
8
  #
9
- # l5x → editing, studio, live (all off by default)
10
- # ftview → edit, studio (both on by default)
11
- # mwi → edit (off by default)
9
+ # l5x → editing, studio, live, allowWrites (all on by default)
10
+ # ftview → edit, studio (both on by default)
11
+ # mwi → edit (on by default)
12
+ # optix → authoring, destructive, invoke, preview (all on by default)
12
13
  #
13
- # That keeps the context decision with the product it belongs to: a read-only
14
- # profile pays for the read surface, and the expensive schemas are one switch
15
- # inside the row instead of four more toggles in the list.
14
+ # Every surface ships on so a fresh install can use the full plugin. Turn one
15
+ # off from the row's Configure page when the profile is read-only or the request
16
+ # budget is tight; the schemas are sent on every request.
16
17
  #
17
18
  # The patch only ever inserts. It must never target a row another bundle owns:
18
19
  # a profile without that row makes composition fail ("patch: entry ... not
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Resolve one value from a plugin row's config.
3
+ *
4
+ * A field marked `.volatile()` in the `Config` schema arrives as a live
5
+ * reference ({ get() }) rather than as the value itself: schemastery wraps it
6
+ * with `createVolatile`, so `config.edit` is the reference, not `true`. Reading
7
+ * the value directly made every volatile switch look off to `apply()` — the row
8
+ * mounted its read surface and never its edit surface, no matter what the
9
+ * profile patch or the settings page said.
10
+ *
11
+ * Keep this tolerance in one place so every product row resolves its switches
12
+ * the same way, and so a plain value (tests, older hosts) still works.
13
+ *
14
+ * @module dsh-engineering-config
15
+ */
16
+
17
+ /**
18
+ * Read a config field that may be a volatile reference.
19
+ *
20
+ * @param input - the raw field, a reference, or undefined.
21
+ * @param fallback - value to use when the field is absent.
22
+ * @returns the current value, or the fallback.
23
+ */
24
+ export function resolve(input, fallback) {
25
+ const current = input && typeof input.get === 'function' ? input.get() : input
26
+ return current === undefined ? fallback : current
27
+ }
28
+
29
+ /**
30
+ * Read a boolean switch.
31
+ *
32
+ * @param input - the raw field, a reference, or undefined.
33
+ * @param fallback - value to use when the field is absent or not a boolean.
34
+ * @returns the boolean value.
35
+ */
36
+ export function flag(input, fallback = false) {
37
+ const current = resolve(input, fallback)
38
+ return typeof current === 'boolean' ? current : fallback
39
+ }
40
+
41
+ /**
42
+ * Read a string-list field.
43
+ *
44
+ * @param input - the raw field, a reference, or undefined.
45
+ * @returns the list, or an empty list.
46
+ */
47
+ export function list(input) {
48
+ const current = resolve(input, [])
49
+ return Array.isArray(current) ? current : []
50
+ }
51
+
52
+ /**
53
+ * Read a string field.
54
+ *
55
+ * @param input - the raw field, a reference, or undefined.
56
+ * @param fallback - value to use when the field is absent or not a string.
57
+ * @returns the string value.
58
+ */
59
+ export function text(input, fallback = '') {
60
+ const current = resolve(input, fallback)
61
+ return typeof current === 'string' ? current : fallback
62
+ }
package/entries/ftview.js CHANGED
@@ -10,16 +10,17 @@ import * as read from '../vendor/ftview/lib/index.js'
10
10
  import * as skill from '../vendor/ftview/lib/skill.js'
11
11
  import * as edit from '../vendor/ftview/lib/edit.js'
12
12
  import * as studio from '../vendor/ftview/lib/studio.js'
13
+ import { flag } from './config.js'
13
14
 
14
15
  export const name = 'engineering-ftview'
15
16
  /** Union of the parts' requirements: the read entry owns the view service. */
16
17
  export const inject = ['tools', 'fs']
17
18
 
18
19
  export const Config = z.object({
19
- edit: z.boolean().default(true).description(
20
+ edit: z.boolean().default(true).volatile().description(
20
21
  'Staged display and HMI-tag edits, validated then written to a new export folder.',
21
22
  ),
22
- studio: z.boolean().default(true).description(
23
+ studio: z.boolean().default(true).volatile().description(
23
24
  'FactoryTalk View Studio discovery and the manual import plan. Runs no vendor utility.',
24
25
  ),
25
26
  })
@@ -32,6 +33,6 @@ export const Config = z.object({
32
33
  export function apply(ctx, config = {}) {
33
34
  ctx.plugin(read)
34
35
  ctx.plugin(skill)
35
- if (config.edit !== false) ctx.plugin(edit)
36
- if (config.studio !== false) ctx.plugin(studio)
36
+ if (flag(config.edit, true)) ctx.plugin(edit)
37
+ if (flag(config.studio, true)) ctx.plugin(studio)
37
38
  }
package/entries/l5x.js CHANGED
@@ -2,10 +2,14 @@
2
2
  * The Studio 5000 L5X product row: one switch for the whole L5X surface.
3
3
  *
4
4
  * The parts are the vendored entries; this module mounts them, so the Plugins
5
- * page shows one row per product instead of one per surface. The three
6
- * expensive surfaces stay off by default and are switched from this row's
7
- * config form, which is the same decision the old separate rows encoded —
8
- * just reachable from the product row instead of from the list.
5
+ * page shows one row per product instead of one per surface. The expensive
6
+ * surfaces are on by default and can be switched off from this row's config form,
7
+ * which is the same decision the old separate rows encoded — just reachable
8
+ * from the product row instead of from the list.
9
+ *
10
+ * `allowWrites` is forwarded into the live entry, not consumed here: the write
11
+ * tools refuse from inside that entry, so the switch and the per-call approval
12
+ * both live where the controller traffic happens.
9
13
  */
10
14
  import z from '@deepseek-ai/schemastery'
11
15
  import * as read from '../vendor/l5x/lib/index.js'
@@ -13,32 +17,76 @@ import * as skill from '../vendor/l5x/lib/skill.js'
13
17
  import * as editing from '../vendor/l5x/lib/editing.js'
14
18
  import * as studio from '../vendor/l5x/lib/studio.js'
15
19
  import * as live from '../vendor/l5x/lib/live.js'
20
+ import { flag, list } from './config.js'
16
21
 
17
22
  export const name = 'engineering-l5x'
18
23
  /** Union of the parts' requirements: the read entry owns the project service. */
19
24
  export const inject = ['tools', 'fs']
20
25
 
21
26
  export const Config = z.object({
22
- editing: z.boolean().default(false).description(
23
- 'Nine staged draft tools (draft_rung, draft_apply, ...). Off by default: a read-only profile should not pay for these schemas.',
27
+ editing: z.boolean().default(true).volatile().description(
28
+ 'Nine staged draft tools (draft_rung, draft_apply, ...). On by default; a read-only profile can switch it off to drop these schemas.',
24
29
  ),
25
- studio: z.boolean().default(false).description(
30
+ studio: z.boolean().default(true).volatile().description(
26
31
  'Logix Designer SDK and supervised controller tools. Needs Studio 5000 with the SDK service installed.',
27
32
  ),
28
- live: z.boolean().default(false).description(
29
- 'EtherNet/IP polling and trend tools. Needs a reachable controller address.',
33
+ live: z.boolean().default(true).volatile().description(
34
+ 'EtherNet/IP surface: polling, batch reads, discovery, controller clock and trends. Needs a reachable controller address.',
35
+ ),
36
+ allowWrites: z.boolean().default(true).volatile().description(
37
+ 'Permit the two live write tools (tag, wall clock). Each write still needs human approval for that exact operation, and only applies while live is on.',
38
+ ),
39
+ allowedAddresses: z.array(z.string()).default([]).volatile().description(
40
+ 'Optional IPv4 allowlist for live reads, writes and discovery. Empty permits any explicit address.',
30
41
  ),
31
42
  })
32
43
 
33
44
  /**
34
45
  * Mount the L5X surface.
35
46
  * @param ctx - plugin context.
36
- * @param config - the three optional surfaces, all off unless enabled.
47
+ * @param config - the optional surfaces, all off unless enabled.
37
48
  */
38
49
  export function apply(ctx, config = {}) {
39
50
  ctx.plugin(read)
40
51
  ctx.plugin(skill)
41
- if (config.editing === true) ctx.plugin(editing)
42
- if (config.studio === true) ctx.plugin(studio)
43
- if (config.live === true) ctx.plugin(live)
52
+ // Volatile-only config changes never re-run apply(): the loader commits the
53
+ // new values and emits loader/volatile-update. Remount the optional surfaces
54
+ // from that event so a switch flipped in the row page takes effect at once.
55
+ let editingFiber
56
+ let studioFiber
57
+ let liveFiber
58
+ let liveSignature = ''
59
+ const sync = () => {
60
+ const wantEditing = flag(config.editing)
61
+ if (wantEditing && editingFiber === undefined) editingFiber = ctx.plugin(editing)
62
+ else if (!wantEditing && editingFiber !== undefined) {
63
+ void editingFiber.dispose()
64
+ editingFiber = undefined
65
+ }
66
+
67
+ const wantStudio = flag(config.studio)
68
+ if (wantStudio && studioFiber === undefined) studioFiber = ctx.plugin(studio)
69
+ else if (!wantStudio && studioFiber !== undefined) {
70
+ void studioFiber.dispose()
71
+ studioFiber = undefined
72
+ }
73
+
74
+ const wantLive = flag(config.live)
75
+ const addresses = list(config.allowedAddresses)
76
+ const signature = wantLive ? JSON.stringify([flag(config.allowWrites), addresses]) : ''
77
+ if (wantLive && (liveFiber === undefined || signature !== liveSignature)) {
78
+ if (liveFiber !== undefined) void liveFiber.dispose()
79
+ liveFiber = ctx.plugin(live, {
80
+ allowWrites: flag(config.allowWrites),
81
+ ...(addresses.length === 0 ? {} : { allowedAddresses: addresses }),
82
+ })
83
+ liveSignature = signature
84
+ } else if (!wantLive && liveFiber !== undefined) {
85
+ void liveFiber.dispose()
86
+ liveFiber = undefined
87
+ liveSignature = ''
88
+ }
89
+ }
90
+ sync()
91
+ ctx.on('loader/volatile-update', sync)
44
92
  }
package/entries/mwi.js CHANGED
@@ -2,8 +2,8 @@
2
2
  * The MotionWorks IEC product row: one switch for the session, read, verify and
3
3
  * edit surfaces plus the skill.
4
4
  *
5
- * Editing is the largest schema set in the bundle, so it stays off until the
6
- * row's config form turns it on.
5
+ * Editing is the largest schema set in the bundle; it ships on by default and
6
+ * the row's config form can switch it off for a read-only profile.
7
7
  */
8
8
  import z from '@deepseek-ai/schemastery'
9
9
  import * as session from '../vendor/mwi/lib/index.js'
@@ -11,26 +11,46 @@ import * as read from '../vendor/mwi/lib/read.js'
11
11
  import * as skill from '../vendor/mwi/lib/skill.js'
12
12
  import * as verify from '../vendor/mwi/lib/verify.js'
13
13
  import * as edit from '../vendor/mwi/lib/edit.js'
14
+ import { configureWorkspace } from '../vendor/mwi/lib/session/workspace.js'
15
+ import { flag, text } from './config.js'
14
16
 
15
17
  export const name = 'engineering-mwi'
16
18
  /** The whole product needs the tool registry. */
17
19
  export const inject = ['tools']
18
20
 
19
21
  export const Config = z.object({
20
- edit: z.boolean().default(false).description(
21
- 'Seven staged edit tools (code, declarations, structure, blocks, POU packages). Off by default: the largest schemas in the bundle.',
22
+ workspace: z.string().default('').volatile().description(
23
+ 'Absolute workspace root for MotionWorks projects. Empty uses the session directory when the host passes one, then the mwi.workspace marker in the profile.',
24
+ ),
25
+ edit: z.boolean().default(true).volatile().description(
26
+ 'Seven staged edit tools (code, declarations, structure, blocks, POU packages). On by default; switch it off to drop the largest schemas in the bundle.',
22
27
  ),
23
28
  })
24
29
 
25
30
  /**
26
31
  * Mount the MotionWorks IEC surface.
27
32
  * @param ctx - plugin context.
28
- * @param config - the optional edit surface, off unless enabled.
33
+ * @param config - the workspace root and the optional edit surface.
29
34
  */
30
35
  export function apply(ctx, config = {}) {
31
36
  ctx.plugin(session)
32
37
  ctx.plugin(read)
33
38
  ctx.plugin(skill)
34
39
  ctx.plugin(verify)
35
- if (config.edit === true) ctx.plugin(edit)
40
+ // The loader commits volatile-only config changes into the live references
41
+ // and emits `loader/volatile-update` instead of re-running apply(). The edit
42
+ // switch must mount and unmount the edit tools in place, or the page would
43
+ // claim the tools are on while the request still lacks them.
44
+ let mounted
45
+ const sync = () => {
46
+ configureWorkspace(text(config.workspace))
47
+ const wanted = flag(config.edit)
48
+ if (wanted && mounted === undefined) mounted = ctx.plugin(edit)
49
+ else if (!wanted && mounted !== undefined) {
50
+ void mounted.dispose()
51
+ mounted = undefined
52
+ }
53
+ }
54
+ sync()
55
+ ctx.on('loader/volatile-update', sync)
36
56
  }