@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.
- package/CHANGELOG.md +199 -0
- package/README.md +37 -26
- package/cordis.patch.yml +7 -6
- package/entries/config.js +62 -0
- package/entries/ftview.js +5 -4
- package/entries/l5x.js +61 -13
- package/entries/mwi.js +26 -6
- package/entries/optix.js +105 -0
- package/lib/client.js +335 -6
- package/package.json +11 -10
- package/vendor/l5x/README.md +27 -5
- package/vendor/l5x/studio-bridge/NuGet.Config +1 -1
- package/vendor/mwi/README.md +39 -18
- package/vendor/mwi/SKILL.md +26 -20
- package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -2763
- package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -30
- package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -22
- package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -339
- package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -765
- package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -399
- package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +313 -313
- package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -520
- package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -133
- package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -672
- package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -86
- package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -208
- package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +128 -128
- package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +673 -673
- package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -790
- package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +642 -642
- package/vendor/mwi/lib/core/code-engine.d.ts +13 -0
- package/vendor/mwi/lib/core/code-engine.js +18 -1
- package/vendor/mwi/lib/edit.d.ts +1 -1
- package/vendor/mwi/lib/edit.js +1 -1
- package/vendor/mwi/lib/read/service.js +8 -1
- package/vendor/mwi/lib/session/backup.d.ts +55 -0
- package/vendor/mwi/lib/session/backup.js +232 -0
- package/vendor/mwi/lib/session/project.d.ts +4 -1
- package/vendor/mwi/lib/session/project.js +13 -2
- package/vendor/mwi/lib/session/service.d.ts +13 -6
- package/vendor/mwi/lib/session/service.js +127 -36
- package/vendor/mwi/lib/session/workspace.d.ts +10 -0
- package/vendor/mwi/lib/session/workspace.js +53 -3
- package/vendor/mwi/lib/skill.d.ts +2 -2
- package/vendor/mwi/lib/skill.js +2 -2
- package/vendor/mwi/lib/tools/edit.d.ts +3 -3
- package/vendor/mwi/lib/tools/edit.js +14 -15
- package/vendor/mwi/lib/tools/read.js +1 -1
- package/vendor/mwi/lib/tools/session.js +13 -9
- package/vendor/optix/templates/arya-optix-ipc/project/AryaNewHMIProject_IPC.optix +81 -81
- package/vendor/optix/templates/arya-optix-ipc/project/AryaNewHMIProject_IPC.optix.design +38 -38
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Alarms/Alarms.yaml +3 -3
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/AryaNewHMIProject_IPC.yaml +67 -67
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/CommDrivers/CommDrivers.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Converters/Converters.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/DataStores/DataStores.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Loggers/Loggers.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/MQTT/MQTT.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Model/Model.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/NetLogic/NetLogic.yaml +26 -26
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/OPC-UA/OPC-UA.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Recipes/Recipes.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Reports/Reports.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Retentivity/Retentivity.yaml +57 -57
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Groups/Groups.yaml +3 -3
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Roles/Roles.yaml +3 -3
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Security.yaml +7 -7
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Security/Users/Users.yaml +3 -3
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/System/System.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/Translations/Translations.yaml +16 -16
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/UI/Keyboards/Keyboards.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/UI/Screens/Screens.yaml +2 -2
- package/vendor/optix/templates/arya-optix-ipc/project/Nodes/UI/UI.yaml +250 -250
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/.vscode/launch.json +18 -18
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.code-workspace +9 -9
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.csproj +19 -19
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.references +172 -172
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/AryaNewHMIProject_IPC.sln +21 -21
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/Private/TypeConstants.cs +20 -20
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/Private/UITypeDefinitions.cs +11 -11
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/NetSolution/StudioMCPBridge.cs +4340 -4340
- package/vendor/optix/templates/arya-optix-ipc/project/ProjectFiles/UserDefinedModule.xml +26 -26
- package/vendor/optix/templates/arya-optix-panel/project/AryaNewHMIProject_OptixPanel.optix +97 -97
- package/vendor/optix/templates/arya-optix-panel/project/AryaNewHMIProject_OptixPanel.optix.design +38 -38
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Alarms/Alarms.yaml +3 -3
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/AryaNewHMIProject_OptixPanel.yaml +67 -67
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/CommDrivers/CommDrivers.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Converters/Converters.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/DataStores/DataStores.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Loggers/Loggers.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/MQTT/MQTT.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Model/Model.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/NetLogic/NetLogic.yaml +26 -26
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/OPC-UA/OPC-UA.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Recipes/Recipes.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Reports/Reports.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Retentivity/Retentivity.yaml +57 -57
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Groups/Groups.yaml +3 -3
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Roles/Roles.yaml +3 -3
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Security.yaml +7 -7
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Security/Users/Users.yaml +3 -3
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/System/System.yaml +444 -444
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/Translations/Translations.yaml +81 -81
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/Keyboards/Keyboards.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/Screens/Screens.yaml +2 -2
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/DeviceSettingsWidget/DeviceSettingsWidget.yaml +163 -163
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/DeviceSettingsWidget/DeviceSettingsWidgetComponents/DeviceSettingsWidgetComponents.yaml +1663 -1663
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/FactoryTalkRemoteAccessWidget/FTRemoteAccessWidgetComponents/FTRemoteAccessWidgetComponents.yaml +2471 -2471
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/FactoryTalkRemoteAccessWidget/FactoryTalkRemoteAccessWidget.yaml +218 -218
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/NetworkSettingsWidget/NetworkSettingsWidget.yaml +524 -524
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/NetworkSettingsWidget/NetworkSettingsWidgetComponents/NetworkSettingsWidgetComponents.yaml +690 -690
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/SettingsWidgets/SettingsWidgets.yaml +1265 -1265
- package/vendor/optix/templates/arya-optix-panel/project/Nodes/UI/UI.yaml +252 -252
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/.vscode/launch.json +18 -18
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/ApplicationNameLogic.cs +19 -19
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.code-workspace +9 -9
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.csproj +19 -19
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.references +172 -172
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/AryaNewHMIProject_OptixPanel.sln +21 -21
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/ConnectionSwitchLogic.cs +74 -74
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/DateAndTimeWidgetLogic.cs +420 -420
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/DeviceSettingsWidgetLogic.cs +34 -34
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/FTOptixStudioVersionLogic.cs +47 -47
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/FTRemoteAccessWidgetSetupLogic.cs +153 -153
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/IncomingConnectionApprovalLogic.cs +118 -118
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/IncomingConnectionReceiverLogic.cs +166 -166
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkConfiguratorEditModelsLogic.cs +56 -56
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkInterfaceLogic.cs +138 -138
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkInterfacesEditModelReaderLogic.cs +87 -87
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkInterfacesEditModelToSystemWriterLogic.cs +139 -139
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/NetworkSettingsLogic.cs +168 -168
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/Private/FTRemoteAccessWidgetDataObject.cs +106 -106
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/Private/TypeConstants.cs +37 -37
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/Private/UITypeDefinitions.cs +91 -91
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/RequestAssistanceSwitchLogic.cs +93 -93
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/StudioMCPBridge.cs +4340 -4340
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/NetSolution/SystemToNetworkInterfacesEditModelWriterLogic.cs +77 -77
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/UserDefinedModule.xml +111 -111
- package/vendor/optix/templates/arya-optix-panel/project/ProjectFiles/WarningIcon.svg +4 -4
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
|
@@ -24,6 +24,7 @@ import { CodeEngine } from "../core/code-engine.js";
|
|
|
24
24
|
import { MwiError } from "../core/errors.js";
|
|
25
25
|
import { PowerShellBridgeTransport } from "../core/ps-bridge.js";
|
|
26
26
|
import { StageService } from "./stage.js";
|
|
27
|
+
import { ensureBackup, findBackup, listBackups } from "./backup.js";
|
|
27
28
|
import { compareVariables } from "../edit/compare.js";
|
|
28
29
|
import { ReadService } from "../read/service.js";
|
|
29
30
|
import { stageRootOf, workspaceOf, isInside } from "./workspace.js";
|
|
@@ -33,6 +34,25 @@ export const BRIDGE_DIR = fileURLToPath(new URL('../../bridge/', import.meta.url
|
|
|
33
34
|
export const CODE_DIR = fileURLToPath(new URL('../../code/', import.meta.url));
|
|
34
35
|
/** Vendored proven mutation helpers. */
|
|
35
36
|
export const LEGACY_DIR = fileURLToPath(new URL('../../legacy/', import.meta.url));
|
|
37
|
+
/** A stage-shaped view of a real, backed-up project. */
|
|
38
|
+
function directRecord(backup) {
|
|
39
|
+
return {
|
|
40
|
+
name: backup.name,
|
|
41
|
+
source: backup.mwt,
|
|
42
|
+
source_directory: backup.directory,
|
|
43
|
+
workspace: backup.workspace,
|
|
44
|
+
staged_mwt: backup.mwt,
|
|
45
|
+
staged_directory: backup.directory,
|
|
46
|
+
bound_to: backup.directory,
|
|
47
|
+
previous_embedded_path: null,
|
|
48
|
+
staged_at: backup.created_at,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Whether two paths name the same project, with or without the `.mwt` suffix. */
|
|
52
|
+
function sameProject(left, right) {
|
|
53
|
+
const bare = (path) => resolve(path).toLowerCase().replace(/\.mwt$/u, '');
|
|
54
|
+
return bare(left) === bare(right);
|
|
55
|
+
}
|
|
36
56
|
/** Hash every file in a native package, refusing links and oversized trees. */
|
|
37
57
|
function nativePackageManifest(directory, workspace) {
|
|
38
58
|
const files = [];
|
|
@@ -92,6 +112,19 @@ export class PowerShellSessionRuntime {
|
|
|
92
112
|
async ideState() {
|
|
93
113
|
return await this.bridge.run('ide_state', { include_window_state: true });
|
|
94
114
|
}
|
|
115
|
+
/** @returns The open project path, or undefined when no IDE or no project is open. */
|
|
116
|
+
async activeProject() {
|
|
117
|
+
let status;
|
|
118
|
+
try {
|
|
119
|
+
status = await this.status();
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error instanceof MwiError && error.code === 'IDE_NOT_RUNNING')
|
|
123
|
+
return undefined;
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
return status.is_project_open === true && typeof status.active_project === 'string' ? status.active_project : undefined;
|
|
127
|
+
}
|
|
95
128
|
/** @returns The written stage record. */
|
|
96
129
|
async stage(source, options = {}) {
|
|
97
130
|
return await this.stages.stage(source, {
|
|
@@ -116,11 +149,40 @@ export class PowerShellSessionRuntime {
|
|
|
116
149
|
},
|
|
117
150
|
});
|
|
118
151
|
}
|
|
152
|
+
/** @returns The verified pre-edit backup record. */
|
|
153
|
+
attach(path) {
|
|
154
|
+
return ensureBackup(this.workspace, path);
|
|
155
|
+
}
|
|
119
156
|
/** @returns The bridge's open result. */
|
|
120
157
|
async open(path, options) {
|
|
121
|
-
const resolved = resolve(path);
|
|
122
|
-
if (!isInside(stageRootOf(this.workspace), resolved)
|
|
123
|
-
|
|
158
|
+
const resolved = resolve(this.workspace, path);
|
|
159
|
+
if (!isInside(stageRootOf(this.workspace), resolved)) {
|
|
160
|
+
const record = this.attach(resolved);
|
|
161
|
+
// An already-open project is attached, not reopened: OpenProject reloads the
|
|
162
|
+
// files, and a reload over unsaved editor changes is data loss the backup
|
|
163
|
+
// cannot undo. The verified identity is the whole job in that case.
|
|
164
|
+
const active = await this.activeProject();
|
|
165
|
+
if (active !== undefined && sameProject(active, record.mwt)) {
|
|
166
|
+
return {
|
|
167
|
+
requested: record.mwt,
|
|
168
|
+
is_project_open: true,
|
|
169
|
+
active_project: active,
|
|
170
|
+
matches_request: true,
|
|
171
|
+
already_open: true,
|
|
172
|
+
backup: record.backup,
|
|
173
|
+
backup_manifest: record.manifest,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const binding = await this.engine.run('check_mwt', { project: record.mwt });
|
|
177
|
+
return await this.bridge.run('open', {
|
|
178
|
+
path: record.mwt,
|
|
179
|
+
wrapper_sha256: binding.wrapper_sha256,
|
|
180
|
+
user_approved: options.userApproved === true,
|
|
181
|
+
...(options.expectedProject === undefined ? {} : { expected_project: options.expectedProject }),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (!existsSync(resolved)) {
|
|
185
|
+
throw new MwiError('NOT_STAGED', `${resolved} is not a staged project.`, 'Pass a workspace .mwt, or stage the workspace project with mw_project_stage.');
|
|
124
186
|
}
|
|
125
187
|
const name = resolved.replace(/\\/gu, '/').split('/').pop()?.replace(/\.mwt$/iu, '') ?? '';
|
|
126
188
|
if (this.stages.identityOf(name) === undefined) {
|
|
@@ -136,11 +198,11 @@ export class PowerShellSessionRuntime {
|
|
|
136
198
|
}
|
|
137
199
|
/** @returns The bridge's start result. */
|
|
138
200
|
async start(project) {
|
|
139
|
-
const
|
|
140
|
-
const binding = await this.engine.run('check_mwt', { project:
|
|
201
|
+
const target = project === undefined ? this.singleStage() : this.projectMwt(project);
|
|
202
|
+
const binding = await this.engine.run('check_mwt', { project: target });
|
|
141
203
|
return await this.bridge.run('start_ide', {
|
|
142
204
|
include_trial_method: true,
|
|
143
|
-
path:
|
|
205
|
+
path: target,
|
|
144
206
|
wrapper_sha256: binding.wrapper_sha256,
|
|
145
207
|
});
|
|
146
208
|
}
|
|
@@ -150,9 +212,9 @@ export class PowerShellSessionRuntime {
|
|
|
150
212
|
if (status.is_project_open !== true || typeof status.active_project !== 'string') {
|
|
151
213
|
throw new MwiError('NO_PROJECT', 'No project is open in the IDE.', 'Open a staged project first with mw_project_open.');
|
|
152
214
|
}
|
|
153
|
-
const record = this.
|
|
215
|
+
const record = this.stagedAt(status.active_project);
|
|
154
216
|
if (record === undefined) {
|
|
155
|
-
throw new MwiError('
|
|
217
|
+
throw new MwiError('NOT_ATTACHED', `The open project is not proven: ${status.active_project}.`, 'Open the workspace project with mw_project_open first.');
|
|
156
218
|
}
|
|
157
219
|
const target = await this.engine.run('worksheet_target', {
|
|
158
220
|
project: record.staged_directory,
|
|
@@ -175,9 +237,13 @@ export class PowerShellSessionRuntime {
|
|
|
175
237
|
...(options.expectedProject === undefined ? {} : { expected_project: options.expectedProject }),
|
|
176
238
|
});
|
|
177
239
|
}
|
|
178
|
-
/** @returns The stage record for a path. */
|
|
240
|
+
/** @returns The stage record for a path, when it is staged or directly backed up. */
|
|
179
241
|
stagedAt(path) {
|
|
180
|
-
|
|
242
|
+
const staged = this.stages.stagedAt(path);
|
|
243
|
+
if (staged !== undefined)
|
|
244
|
+
return staged;
|
|
245
|
+
const backup = findBackup(this.workspace, path);
|
|
246
|
+
return backup === undefined ? undefined : directRecord(backup);
|
|
181
247
|
}
|
|
182
248
|
/** @returns The bridge's compile result. */
|
|
183
249
|
async compile(mode) {
|
|
@@ -466,10 +532,20 @@ export class PowerShellSessionRuntime {
|
|
|
466
532
|
mkdirSync(evidenceDirectory, { recursive: true });
|
|
467
533
|
const evidence = join(evidenceDirectory, `native-code-${randomUUID()}.json`);
|
|
468
534
|
const args = { project, pou: input.pou, baseline_saved: true };
|
|
535
|
+
let expectedHash = input.expectedHash;
|
|
536
|
+
if (input.changes !== undefined && expectedHash === undefined) {
|
|
537
|
+
// The hash guards the exact find/replace against a stale body. The caller no
|
|
538
|
+
// longer has to carry it: hash the body this edit is about to patch.
|
|
539
|
+
const read = await this.engine.run('read_text', { project, pou: input.pou });
|
|
540
|
+
if (typeof read.body !== 'string' || read.body.length === 0) {
|
|
541
|
+
throw new MwiError('EDIT_REFUSED', `Cannot read ${input.pou} to hash it: ${String(read.body_error ?? 'empty body')}.`, 'Read the POU with mw_get, then retry the edit.');
|
|
542
|
+
}
|
|
543
|
+
expectedHash = createHash('sha256').update(read.body).digest('hex');
|
|
544
|
+
}
|
|
469
545
|
if (input.changes !== undefined) {
|
|
470
546
|
args.changes = input.changes;
|
|
471
|
-
if (
|
|
472
|
-
args.expected_body_sha256 =
|
|
547
|
+
if (expectedHash !== undefined)
|
|
548
|
+
args.expected_body_sha256 = expectedHash;
|
|
473
549
|
}
|
|
474
550
|
else {
|
|
475
551
|
args.code = input.body;
|
|
@@ -506,42 +582,57 @@ export class PowerShellSessionRuntime {
|
|
|
506
582
|
throw new MwiError('EDIT_REFUSED', `${error.message}; evidence at ${evidence}`, 'Read the refusal, correct the request, and retry. Do not repeat an unverified edit.');
|
|
507
583
|
}
|
|
508
584
|
}
|
|
509
|
-
/** The open project must be a proven stage; every compile and verify depends on it. */
|
|
585
|
+
/** The open project must be a proven stage or a verified direct backup; every compile and verify depends on it. */
|
|
510
586
|
async openStagedRecord() {
|
|
511
|
-
const
|
|
512
|
-
if (
|
|
513
|
-
throw new MwiError('NO_PROJECT', 'No project is open in the IDE.', 'Open a
|
|
514
|
-
}
|
|
515
|
-
const record = this.
|
|
516
|
-
if (record
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
587
|
+
const active = await this.activeProject();
|
|
588
|
+
if (active === undefined) {
|
|
589
|
+
throw new MwiError('NO_PROJECT', 'No project is open in the IDE.', 'Open a project with mw_project_open before compiling.');
|
|
590
|
+
}
|
|
591
|
+
const record = this.stagedAt(active);
|
|
592
|
+
if (record !== undefined)
|
|
593
|
+
return record;
|
|
594
|
+
// The IDE already has a workspace project open that this bundle did not open.
|
|
595
|
+
// Take the verified backup now rather than refuse: the edit still needs the
|
|
596
|
+
// same rollback evidence, and the guarded edit itself still refuses an
|
|
597
|
+
// unsaved project before it writes.
|
|
598
|
+
if (!isInside(this.workspace, active) || isInside(join(this.workspace, '.motionworks'), active)) {
|
|
599
|
+
throw new MwiError('NOT_ATTACHED', `The open project has no proven backup: ${active}.`, 'Open a workspace project with mw_project_open so a verified backup is taken first.');
|
|
600
|
+
}
|
|
601
|
+
return directRecord(this.attach(active));
|
|
520
602
|
}
|
|
521
|
-
/** The only
|
|
603
|
+
/** The only openable project, staged or backed up, or a refusal that explains the choice. */
|
|
522
604
|
singleStage() {
|
|
523
|
-
|
|
605
|
+
const stages = [];
|
|
524
606
|
try {
|
|
525
|
-
|
|
607
|
+
for (const name of readdirSync(stageRootOf(this.workspace))) {
|
|
608
|
+
if (name.toLowerCase().endsWith('.mwt'))
|
|
609
|
+
stages.push(join(stageRootOf(this.workspace), name));
|
|
610
|
+
}
|
|
526
611
|
}
|
|
527
612
|
catch {
|
|
528
|
-
|
|
613
|
+
// No stage directory.
|
|
614
|
+
}
|
|
615
|
+
const attached = listBackups(this.workspace).map(record => record.mwt);
|
|
616
|
+
// Direct editing is the default: a proven backup wins over a stale stage copy.
|
|
617
|
+
const candidates = attached.length > 0 ? attached : stages;
|
|
618
|
+
if (candidates.length === 0) {
|
|
619
|
+
throw new MwiError('NO_PROJECT', 'No project is staged or backed up in this workspace.', 'Call mw_project_find, then mw_project_open with the workspace .mwt.');
|
|
529
620
|
}
|
|
530
|
-
if (
|
|
531
|
-
throw new MwiError('
|
|
532
|
-
if (names.length > 1) {
|
|
533
|
-
throw new MwiError('AMBIGUOUS_STAGE', `${names.length} projects are staged.`, 'Pass project: the staged .mwt to start.', names);
|
|
621
|
+
if (candidates.length > 1) {
|
|
622
|
+
throw new MwiError('AMBIGUOUS_PROJECT', `${candidates.length} projects are openable.`, 'Pass project: the exact .mwt to start.', candidates);
|
|
534
623
|
}
|
|
535
|
-
return
|
|
624
|
+
return candidates[0];
|
|
536
625
|
}
|
|
537
|
-
/** Resolve a requested
|
|
538
|
-
|
|
626
|
+
/** Resolve a requested project, taking a backup for a real workspace project. */
|
|
627
|
+
projectMwt(project) {
|
|
539
628
|
const resolved = resolve(this.workspace, project);
|
|
540
629
|
const mwt = resolved.toLowerCase().endsWith('.mwt') ? resolved : `${resolved}.mwt`;
|
|
541
|
-
if (
|
|
542
|
-
|
|
630
|
+
if (isInside(stageRootOf(this.workspace), mwt)) {
|
|
631
|
+
if (!existsSync(mwt))
|
|
632
|
+
throw new MwiError('NOT_STAGED', `${mwt} is not a staged project.`, 'Stage the workspace project first, or open the workspace .mwt directly.');
|
|
633
|
+
return mwt;
|
|
543
634
|
}
|
|
544
|
-
return mwt;
|
|
635
|
+
return ensureBackup(this.workspace, mwt).mwt;
|
|
545
636
|
}
|
|
546
637
|
}
|
|
547
638
|
const runtimes = new Map();
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module dsh-engineering-mwi/session/workspace
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* Set the workspace root from the product row's config.
|
|
11
|
+
*
|
|
12
|
+
* The host does not always pass the session cwd to tool calls (it is undefined
|
|
13
|
+
* in the current desktop build), so the row carries the workspace explicitly.
|
|
14
|
+
* An empty string clears it and falls back to the marker / process directory.
|
|
15
|
+
*
|
|
16
|
+
* @param root - Absolute workspace root, or an empty string.
|
|
17
|
+
*/
|
|
18
|
+
export declare function configureWorkspace(root: string | undefined): void;
|
|
9
19
|
/** One MotionWorks project found in the workspace. */
|
|
10
20
|
export interface FoundProject {
|
|
11
21
|
/** Project name: the `.mwt` basename. */
|
|
@@ -6,10 +6,38 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module dsh-engineering-mwi/session/workspace
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
9
|
+
import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
10
10
|
import { join, resolve, sep } from 'node:path';
|
|
11
11
|
/** Directories never scanned for projects. */
|
|
12
12
|
const SKIP = new Set(['node_modules', '.git', 'stage', 'backups', '_backups', '__pycache__', 'chm_out', 'help_extract', '.dsh']);
|
|
13
|
+
/** Workspace root set from the row's config, when one is configured. */
|
|
14
|
+
let configured;
|
|
15
|
+
/**
|
|
16
|
+
* Set the workspace root from the product row's config.
|
|
17
|
+
*
|
|
18
|
+
* The host does not always pass the session cwd to tool calls (it is undefined
|
|
19
|
+
* in the current desktop build), so the row carries the workspace explicitly.
|
|
20
|
+
* An empty string clears it and falls back to the marker / process directory.
|
|
21
|
+
*
|
|
22
|
+
* @param root - Absolute workspace root, or an empty string.
|
|
23
|
+
*/
|
|
24
|
+
export function configureWorkspace(root) {
|
|
25
|
+
const value = typeof root === 'string' ? root.trim() : '';
|
|
26
|
+
configured = value === '' ? undefined : resolve(value);
|
|
27
|
+
}
|
|
28
|
+
/** Read the documented `mwi.workspace` marker from the profile directory. */
|
|
29
|
+
function recordedWorkspace() {
|
|
30
|
+
try {
|
|
31
|
+
const marker = join(process.env.DSH_PROFILE_DIR ?? '', 'mwi.workspace');
|
|
32
|
+
if (!existsSync(marker))
|
|
33
|
+
return undefined;
|
|
34
|
+
const recorded = readFileSync(marker, 'utf8').replace(/^\uFEFF/u, '').trim();
|
|
35
|
+
return recorded === '' ? undefined : resolve(recorded);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
13
41
|
/**
|
|
14
42
|
* Resolve the caller's workspace.
|
|
15
43
|
*
|
|
@@ -17,7 +45,9 @@ const SKIP = new Set(['node_modules', '.git', 'stage', 'backups', '_backups', '_
|
|
|
17
45
|
* @returns The absolute workspace root.
|
|
18
46
|
*/
|
|
19
47
|
export function workspaceOf(cwd) {
|
|
20
|
-
|
|
48
|
+
if (typeof cwd === 'string' && cwd.trim() !== '')
|
|
49
|
+
return resolve(cwd);
|
|
50
|
+
return configured ?? recordedWorkspace() ?? resolve(process.cwd());
|
|
21
51
|
}
|
|
22
52
|
/**
|
|
23
53
|
* The stage root of one workspace.
|
|
@@ -52,9 +82,20 @@ export function findProjects(workspace, options = {}) {
|
|
|
52
82
|
const filter = options.filter?.toLowerCase();
|
|
53
83
|
const stage = stageRootOf(workspace);
|
|
54
84
|
const found = [];
|
|
85
|
+
const seen = new Set();
|
|
55
86
|
const walk = (directory, depth) => {
|
|
56
87
|
if (found.length >= limit || depth > 6)
|
|
57
88
|
return;
|
|
89
|
+
let real;
|
|
90
|
+
try {
|
|
91
|
+
real = realpathSync(directory);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (seen.has(real))
|
|
97
|
+
return;
|
|
98
|
+
seen.add(real);
|
|
58
99
|
let entries;
|
|
59
100
|
try {
|
|
60
101
|
entries = readdirSync(directory, { withFileTypes: true });
|
|
@@ -66,7 +107,16 @@ export function findProjects(workspace, options = {}) {
|
|
|
66
107
|
if (found.length >= limit)
|
|
67
108
|
return;
|
|
68
109
|
const absolute = join(directory, entry.name);
|
|
69
|
-
|
|
110
|
+
let isDirectory = entry.isDirectory();
|
|
111
|
+
if (!isDirectory && entry.isSymbolicLink()) {
|
|
112
|
+
try {
|
|
113
|
+
isDirectory = statSync(absolute).isDirectory();
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
isDirectory = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (isDirectory) {
|
|
70
120
|
if (SKIP.has(entry.name) || isInside(stage, absolute))
|
|
71
121
|
continue;
|
|
72
122
|
walk(absolute, depth + 1);
|
|
@@ -22,8 +22,8 @@ export interface SkillContext {
|
|
|
22
22
|
/** Registration metadata, kept here so a test can assert it without the file. */
|
|
23
23
|
export declare const SKILL: {
|
|
24
24
|
readonly name: "motionworks-iec";
|
|
25
|
-
readonly description: "Operate a running Yaskawa MotionWorks IEC 3 Pro IDE
|
|
26
|
-
readonly whenToUse: "The user has MotionWorks IEC 3 Pro open or asks for work in it — a build or compile verdict, the live project model, reading or changing POU code, variable declarations, tasks, or the IDE error list. Also use when a MotionWorks project is mentioned: find which project is in the workspace before answering. For a question about a project outside the workspace, say it cannot be
|
|
25
|
+
readonly description: "Operate a running Yaskawa MotionWorks IEC 3 Pro IDE on the real project files — find and open a workspace project (one verified backup first), read the live object model and POU code, edit declarations and code, compile, read diagnostics, and verify the change.";
|
|
26
|
+
readonly whenToUse: "The user has MotionWorks IEC 3 Pro open or asks for work in it — a build or compile verdict, the live project model, reading or changing POU code, variable declarations, tasks, or the IDE error list. Also use when a MotionWorks project is mentioned: find which project is in the workspace before answering. For a question about a project outside the workspace, say it cannot be opened.";
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
29
|
* Register the MotionWorks IEC skill.
|
package/vendor/mwi/lib/skill.js
CHANGED
|
@@ -17,8 +17,8 @@ export const inject = ['skills'];
|
|
|
17
17
|
/** Registration metadata, kept here so a test can assert it without the file. */
|
|
18
18
|
export const SKILL = {
|
|
19
19
|
name: 'motionworks-iec',
|
|
20
|
-
description: 'Operate a running Yaskawa MotionWorks IEC 3 Pro IDE
|
|
21
|
-
whenToUse: 'The user has MotionWorks IEC 3 Pro open or asks for work in it — a build or compile verdict, the live project model, reading or changing POU code, variable declarations, tasks, or the IDE error list. Also use when a MotionWorks project is mentioned: find which project is in the workspace before answering. For a question about a project outside the workspace, say it cannot be
|
|
20
|
+
description: 'Operate a running Yaskawa MotionWorks IEC 3 Pro IDE on the real project files — find and open a workspace project (one verified backup first), read the live object model and POU code, edit declarations and code, compile, read diagnostics, and verify the change.',
|
|
21
|
+
whenToUse: 'The user has MotionWorks IEC 3 Pro open or asks for work in it — a build or compile verdict, the live project model, reading or changing POU code, variable declarations, tasks, or the IDE error list. Also use when a MotionWorks project is mentioned: find which project is in the workspace before answering. For a question about a project outside the workspace, say it cannot be opened.',
|
|
22
22
|
};
|
|
23
23
|
/**
|
|
24
24
|
* Register the MotionWorks IEC skill.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The edit row: replace code, edit declarations, change structure and apply.
|
|
3
3
|
*
|
|
4
|
-
* Editing is
|
|
5
|
-
*
|
|
6
|
-
* change.
|
|
4
|
+
* Editing is on by default; a read-only profile can switch it off. Every tool writes the open project (the real files
|
|
5
|
+
* for a workspace project, with a verified backup already taken) and the proven
|
|
6
|
+
* native helpers verify that unrelated saved and native state did not change.
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-engineering-mwi/tools/edit
|
|
9
9
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The edit row: replace code, edit declarations, change structure and apply.
|
|
3
3
|
*
|
|
4
|
-
* Editing is
|
|
5
|
-
*
|
|
6
|
-
* change.
|
|
4
|
+
* Editing is on by default; a read-only profile can switch it off. Every tool writes the open project (the real files
|
|
5
|
+
* for a workspace project, with a verified backup already taken) and the proven
|
|
6
|
+
* native helpers verify that unrelated saved and native state did not change.
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-engineering-mwi/tools/edit
|
|
9
9
|
*/
|
|
@@ -32,7 +32,7 @@ export function editTools(runtimeOf = runtimeFor) {
|
|
|
32
32
|
return [
|
|
33
33
|
tool({
|
|
34
34
|
name: 'mw_edit_code',
|
|
35
|
-
description: 'Replace one ST or IL body in the open
|
|
35
|
+
description: 'Replace one ST or IL body in the open project through the native ChangeCodeWS path. Supply exact find/replace changes with the body hash, or a complete body with the current body. Verifies saved and native state, then leaves compilation to mw_compile.',
|
|
36
36
|
parameters: object({
|
|
37
37
|
pou: string('Exact POU name.'),
|
|
38
38
|
changes: list('Exact find/replace changes; each must match its expected count.', objectItem('One exact change.', {
|
|
@@ -51,17 +51,16 @@ export function editTools(runtimeOf = runtimeFor) {
|
|
|
51
51
|
const hasChanges = changes !== undefined && changes.length > 0;
|
|
52
52
|
const hasBody = typeof args.body === 'string';
|
|
53
53
|
if (hasChanges === hasBody) {
|
|
54
|
-
throw new MwiError('INVALID_ARGUMENT', 'Supply either changes
|
|
55
|
-
}
|
|
56
|
-
if (hasChanges && typeof args.expected_body_sha256 !== 'string') {
|
|
57
|
-
throw new MwiError('INVALID_ARGUMENT', 'Changes need expected_body_sha256.', 'Read the POU with mw_get and pass its text_body_sha256.');
|
|
54
|
+
throw new MwiError('INVALID_ARGUMENT', 'Supply either changes, or body with expected_body.', 'Use changes for a small exact patch, or body for a full replacement; do not mix them.');
|
|
58
55
|
}
|
|
59
56
|
if (hasBody && typeof args.expected_body !== 'string') {
|
|
60
57
|
throw new MwiError('INVALID_ARGUMENT', 'A complete body needs expected_body.', 'Read the POU with mw_get and pass its current body.');
|
|
61
58
|
}
|
|
62
59
|
const result = await runtimeOf(workspace).editCode({
|
|
63
60
|
pou,
|
|
64
|
-
...(hasChanges
|
|
61
|
+
...(hasChanges
|
|
62
|
+
? { changes, ...(typeof args.expected_body_sha256 === 'string' ? { expectedHash: args.expected_body_sha256 } : {}) }
|
|
63
|
+
: { body: String(args.body), expectedBody: String(args.expected_body) }),
|
|
65
64
|
});
|
|
66
65
|
const verification = result.verification;
|
|
67
66
|
const accepted = verification?.accepted === true;
|
|
@@ -72,7 +71,7 @@ export function editTools(runtimeOf = runtimeFor) {
|
|
|
72
71
|
}),
|
|
73
72
|
tool({
|
|
74
73
|
name: 'mw_edit_variables',
|
|
75
|
-
description: 'Add, edit or delete one declaration in the open
|
|
74
|
+
description: 'Add, edit or delete one declaration in the open project through the native IDE API. Edit replaces the complete seven-field declaration and optional boolean flags; delete needs approval and reference review.',
|
|
76
75
|
parameters: object({
|
|
77
76
|
operation: choice('Variable operation.', ['add', 'edit', 'delete']),
|
|
78
77
|
pou: string('POU for a local declaration; omit for resource globals.'),
|
|
@@ -225,7 +224,7 @@ export function editTools(runtimeOf = runtimeFor) {
|
|
|
225
224
|
}),
|
|
226
225
|
tool({
|
|
227
226
|
name: 'mw_transfer_pou',
|
|
228
|
-
description: 'Export one writable POU to a private workspace package, or import that untouched package into this
|
|
227
|
+
description: 'Export one writable POU to a private workspace package, or import that untouched package into this project. Import needs the export token and a dependency review; a token is consumed by one attempt.',
|
|
229
228
|
parameters: object({
|
|
230
229
|
operation: choice('Package operation.', ['export', 'import']),
|
|
231
230
|
pou: string('Exact POU to export.'),
|
|
@@ -255,7 +254,7 @@ export function editTools(runtimeOf = runtimeFor) {
|
|
|
255
254
|
}),
|
|
256
255
|
tool({
|
|
257
256
|
name: 'mw_validate',
|
|
258
|
-
description: 'Validate the open
|
|
257
|
+
description: 'Validate the open project before applying: workflow blockers, native validation and an optional program review for one POU or proposed body. Read the findings before saving or compiling.',
|
|
259
258
|
parameters: object({
|
|
260
259
|
pou: string('POU to review.'),
|
|
261
260
|
body: string('Proposed replacement body to review without writing it.'),
|
|
@@ -268,18 +267,18 @@ export function editTools(runtimeOf = runtimeFor) {
|
|
|
268
267
|
});
|
|
269
268
|
const ready = result.ready === true;
|
|
270
269
|
return success(ready ? 'Validation found no blocker.' : `Validation found ${String(result.blockers?.length ?? 0)} blocker(s) and ${String(result.validation_errors?.length ?? 0)} validation error(s).`, result, ready
|
|
271
|
-
? [{ tool: 'mw_apply', why: 'Save the validated
|
|
270
|
+
? [{ tool: 'mw_apply', why: 'Save the validated project.' }]
|
|
272
271
|
: [{ tool: 'mw_review', why: 'Inspect the components named by the findings.' }], EDIT_COVERAGE);
|
|
273
272
|
},
|
|
274
273
|
}),
|
|
275
274
|
tool({
|
|
276
275
|
name: 'mw_apply',
|
|
277
|
-
description: 'Save the open
|
|
276
|
+
description: 'Save the open project through the IDE. A workspace project is written in place; its verified pre-edit backup stays under <workspace>/backups/.',
|
|
278
277
|
parameters: object({}),
|
|
279
278
|
async run(_args, exec) {
|
|
280
279
|
const workspace = workspaceOf(exec.cwd);
|
|
281
280
|
const result = await runtimeOf(workspace).apply();
|
|
282
|
-
return success('Saved the
|
|
281
|
+
return success('Saved the open project through the IDE.', result, [{ tool: 'mw_compile', why: 'Compile the saved project.' }], EDIT_COVERAGE);
|
|
283
282
|
},
|
|
284
283
|
}),
|
|
285
284
|
];
|
|
@@ -75,7 +75,7 @@ export function readTools(readsOf = workspace => runtimeFor(workspace).reads) {
|
|
|
75
75
|
}),
|
|
76
76
|
tool({
|
|
77
77
|
name: 'mw_get',
|
|
78
|
-
description: 'Read one POU with its declarations, one type, one task row, one block interface or the EtherNet/IP assembly map.',
|
|
78
|
+
description: 'Read one POU with its declarations and the text_body_sha256 that the edit preconditions take, one type, one task row, one block interface or the EtherNet/IP assembly map.',
|
|
79
79
|
parameters: object({
|
|
80
80
|
kind: choice('What to read.', GET_KINDS),
|
|
81
81
|
name: string('Exact component name; omit only for kind eip.'),
|
|
@@ -12,6 +12,7 @@ import { success } from "../core/result.js";
|
|
|
12
12
|
import { MwiError } from "../core/errors.js";
|
|
13
13
|
import { tool } from "../core/tool.js";
|
|
14
14
|
import { findProjects, stageRootOf, workspaceOf } from "../session/workspace.js";
|
|
15
|
+
import { listBackups } from "../session/backup.js";
|
|
15
16
|
import { runtimeFor } from "../session/service.js";
|
|
16
17
|
/** What every IDE answer cannot establish. */
|
|
17
18
|
const IDE_COVERAGE = {
|
|
@@ -43,7 +44,7 @@ export function sessionTools(runtimeOf = runtimeFor) {
|
|
|
43
44
|
return [
|
|
44
45
|
tool({
|
|
45
46
|
name: 'mw_project_find',
|
|
46
|
-
description: 'Find MotionWorks IEC projects inside the calling workspace. Returns each .mwt, its expanded directory
|
|
47
|
+
description: 'Find MotionWorks IEC projects inside the calling workspace. Returns each .mwt, its expanded directory, whether a staged copy exists and whether a pre-edit backup exists. Call this before opening anything.',
|
|
47
48
|
parameters: object({
|
|
48
49
|
filter: string('Optional case-insensitive substring matched against the project name or path.'),
|
|
49
50
|
limit: integer('Most projects to return. Defaults to 20.', { minimum: 1, maximum: 100 }),
|
|
@@ -55,11 +56,12 @@ export function sessionTools(runtimeOf = runtimeFor) {
|
|
|
55
56
|
limit: typeof args.limit === 'number' ? args.limit : 20,
|
|
56
57
|
});
|
|
57
58
|
const staged = projects.filter(project => project.staged !== null).length;
|
|
59
|
+
const backups = listBackups(workspace);
|
|
58
60
|
return success(projects.length === 0
|
|
59
61
|
? `No MotionWorks project was found inside ${workspace}.`
|
|
60
|
-
: `Found ${projects.length} MotionWorks project(s) in ${workspace}; ${staged}
|
|
62
|
+
: `Found ${projects.length} MotionWorks project(s) in ${workspace}; ${staged} staged, ${backups.length} with a pre-edit backup.`, { workspace, stage_root: stageRootOf(workspace), backups, projects }, projects.length === 0
|
|
61
63
|
? []
|
|
62
|
-
: [{ tool: '
|
|
64
|
+
: [{ tool: 'mw_project_open', why: 'Open the workspace project; a verified backup is taken first.', args: { path: projects[0]?.mwt } }], { complete: false, unverified: ['projects outside the workspace are never listed or staged'] });
|
|
63
65
|
},
|
|
64
66
|
}),
|
|
65
67
|
tool({
|
|
@@ -85,7 +87,7 @@ export function sessionTools(runtimeOf = runtimeFor) {
|
|
|
85
87
|
}),
|
|
86
88
|
tool({
|
|
87
89
|
name: 'mw_project_stage',
|
|
88
|
-
description: '
|
|
90
|
+
description: 'Optional sandbox: copy one workspace project into .motionworks/stage so the IDE opens a copy instead of the real files. Normal editing opens the workspace project directly.',
|
|
89
91
|
parameters: object({
|
|
90
92
|
source: string('Workspace project folder or its .mwt. Must be inside the workspace.'),
|
|
91
93
|
replace_existing: boolean('Replace an existing stage after review; defaults false. The previous stage is retained under backups/.'),
|
|
@@ -98,9 +100,9 @@ export function sessionTools(runtimeOf = runtimeFor) {
|
|
|
98
100
|
}),
|
|
99
101
|
tool({
|
|
100
102
|
name: 'mw_project_open',
|
|
101
|
-
description: 'Open
|
|
103
|
+
description: 'Open a workspace project directly, or a staged copy. A direct open first writes and verifies one complete backup under <workspace>/.motionworks/attached/, and a project the IDE already has open is attached without being reloaded. Refuses to replace another open project without the exact approved path.',
|
|
102
104
|
parameters: object({
|
|
103
|
-
path: string('
|
|
105
|
+
path: string('Workspace .mwt (edits the real project, backup taken first) or a staged .mwt from mw_project_stage.'),
|
|
104
106
|
user_approved: boolean('True only after the user approved saving and closing another open project.'),
|
|
105
107
|
expected_project: string('Exact open project path named in that approval.'),
|
|
106
108
|
}, ['path']),
|
|
@@ -112,14 +114,16 @@ export function sessionTools(runtimeOf = runtimeFor) {
|
|
|
112
114
|
});
|
|
113
115
|
return success(opened.matches_request === false
|
|
114
116
|
? 'The open call returned, but the IDE has a different project open.'
|
|
115
|
-
:
|
|
117
|
+
: opened.already_open === true
|
|
118
|
+
? `Already open; verified the pre-edit backup for ${String(opened.active_project ?? args.path)}.`
|
|
119
|
+
: `Opened ${String(opened.active_project ?? args.path)}.`, opened, [{ tool: 'mw_ide_status', why: 'Confirm the active project and compile state.' }], IDE_COVERAGE);
|
|
116
120
|
},
|
|
117
121
|
}),
|
|
118
122
|
tool({
|
|
119
123
|
name: 'mw_ide_start',
|
|
120
|
-
description: 'Start MotionWorks IEC with one staged
|
|
124
|
+
description: 'Start MotionWorks IEC with one workspace project (backup first) or a staged copy, or attach to the IDE that is already running. Refuses when several projects are openable without a project argument.',
|
|
121
125
|
parameters: object({
|
|
122
|
-
project: string('
|
|
126
|
+
project: string('Workspace or staged .mwt to start with. Omit when exactly one project is openable.'),
|
|
123
127
|
}),
|
|
124
128
|
async run(args, exec) {
|
|
125
129
|
const workspace = workspaceOf(exec.cwd);
|