@aryacode/dsh-engineering 0.4.7 → 0.4.21
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 +202 -0
- package/README.md +40 -26
- package/cordis.patch.yml +7 -6
- package/entries/config.js +62 -0
- package/entries/ftview.js +5 -4
- package/entries/l5x.js +47 -15
- 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/ftview/SKILL.md +17 -0
- package/vendor/l5x/SKILL.md +17 -0
- package/vendor/l5x/studio-bridge/NuGet.Config +1 -1
- package/vendor/mwi/README.md +41 -18
- package/vendor/mwi/SKILL.md +39 -20
- package/vendor/mwi/bridge/mw_bridge.ps1 +2768 -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/__pycache__/__init__.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__/staging.cpython-312.pyc +0 -0
- 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 +134 -130
- 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 +72 -0
- package/vendor/mwi/lib/session/backup.js +257 -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__/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__/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__/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
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module dsh-engineering-mwi/core/code-engine
|
|
10
10
|
*/
|
|
11
|
+
import { MwiError } from './errors.ts';
|
|
11
12
|
/** Where the engine lives and which workspace it serves. */
|
|
12
13
|
export interface CodeEngineOptions {
|
|
13
14
|
/** Absolute workspace root. */
|
|
@@ -23,6 +24,18 @@ export interface CodeEngineOptions {
|
|
|
23
24
|
export type EngineResult = Readonly<Record<string, unknown>>;
|
|
24
25
|
/** Resolve the first usable Python 3 interpreter. */
|
|
25
26
|
export declare function findPython(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Translate one engine refusal into the failure the caller should act on.
|
|
29
|
+
*
|
|
30
|
+
* The engine guard is shared with the retired staging flow, so its refusal says
|
|
31
|
+
* "stage a copy". In this bundle a real workspace project is opened directly, and
|
|
32
|
+
* the identity record that an open writes is exactly what the guard accepts: point
|
|
33
|
+
* at the open instead of letting the caller loop back into staging.
|
|
34
|
+
*
|
|
35
|
+
* @param message - The refusal text the engine wrote.
|
|
36
|
+
* @returns The error to throw.
|
|
37
|
+
*/
|
|
38
|
+
export declare function engineFailure(message: string): MwiError;
|
|
26
39
|
/** The typed engine client. */
|
|
27
40
|
export declare class CodeEngine {
|
|
28
41
|
private readonly options;
|
|
@@ -28,6 +28,23 @@ export function findPython() {
|
|
|
28
28
|
}
|
|
29
29
|
return 'python';
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Translate one engine refusal into the failure the caller should act on.
|
|
33
|
+
*
|
|
34
|
+
* The engine guard is shared with the retired staging flow, so its refusal says
|
|
35
|
+
* "stage a copy". In this bundle a real workspace project is opened directly, and
|
|
36
|
+
* the identity record that an open writes is exactly what the guard accepts: point
|
|
37
|
+
* at the open instead of letting the caller loop back into staging.
|
|
38
|
+
*
|
|
39
|
+
* @param message - The refusal text the engine wrote.
|
|
40
|
+
* @returns The error to throw.
|
|
41
|
+
*/
|
|
42
|
+
export function engineFailure(message) {
|
|
43
|
+
if (/outside the staging root/iu.test(message)) {
|
|
44
|
+
return new MwiError('NOT_ATTACHED', message, 'Call mw_project_open with this project first: it writes the verified attachment record the engine guard requires. mw_project_stage is only an optional sandbox.');
|
|
45
|
+
}
|
|
46
|
+
return new MwiError('ENGINE_REFUSED', message, 'Read the message, correct the request, and retry. Do not retry the same invalid request.');
|
|
47
|
+
}
|
|
31
48
|
/** The typed engine client. */
|
|
32
49
|
export class CodeEngine {
|
|
33
50
|
options;
|
|
@@ -83,7 +100,7 @@ export class CodeEngine {
|
|
|
83
100
|
if (existsSync(responsePath)) {
|
|
84
101
|
const payload = await this.readResponse(responsePath);
|
|
85
102
|
if (payload.ok === false) {
|
|
86
|
-
throw
|
|
103
|
+
throw engineFailure(typeof payload.error === 'string' ? payload.error : `The MotionWorks engine refused ${verb}.`);
|
|
87
104
|
}
|
|
88
105
|
return payload;
|
|
89
106
|
}
|
package/vendor/mwi/lib/edit.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `mwi-edit` entry: staged edits, validation and apply.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* On by default; a read-only profile can switch it off. The row writes only the staged project and verifies that
|
|
5
5
|
* unrelated saved and native state did not change.
|
|
6
6
|
*
|
|
7
7
|
* @module dsh-engineering-mwi/edit
|
package/vendor/mwi/lib/edit.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `mwi-edit` entry: staged edits, validation and apply.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* On by default; a read-only profile can switch it off. The row writes only the staged project and verifies that
|
|
5
5
|
* unrelated saved and native state did not change.
|
|
6
6
|
*
|
|
7
7
|
* @module dsh-engineering-mwi/edit
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module dsh-engineering-mwi/read/service
|
|
8
8
|
*/
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
9
10
|
import { MwiError } from "../core/errors.js";
|
|
10
11
|
/** Kinds `mw_list` can enumerate. */
|
|
11
12
|
export const LIST_KINDS = ['pou', 'variable', 'global', 'type', 'task', 'library'];
|
|
@@ -52,7 +53,13 @@ export class ReadService {
|
|
|
52
53
|
async get(project, kind, name, library) {
|
|
53
54
|
if (kind === 'pou') {
|
|
54
55
|
const text = await this.engine.run('read_text', { project: project.directory, pou: name }, 60_000);
|
|
55
|
-
|
|
56
|
+
// The edit preconditions take the hash of exactly this body. Computing it
|
|
57
|
+
// here is what makes the documented `expected_body_sha256` obtainable from
|
|
58
|
+
// this read at all: the engine returns the body, not its digest, and a
|
|
59
|
+
// caller that has to find the hash elsewhere is a caller that guesses.
|
|
60
|
+
const body = typeof text.body === 'string' ? text.body : undefined;
|
|
61
|
+
const digest = body === undefined ? {} : { text_body_sha256: createHash('sha256').update(body).digest('hex') };
|
|
62
|
+
return { ...text, project: project.directory, ...digest };
|
|
56
63
|
}
|
|
57
64
|
if (kind === 'block') {
|
|
58
65
|
return await this.engine.run('block_interface', {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-edit backups shared by the automation surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Editing happens on the real workspace project. The first write takes one
|
|
5
|
+
* complete, hash-verified copy under
|
|
6
|
+
* `<workspace>/backups/motionworks/<project>/<UTC stamp>`, writes a manifest
|
|
7
|
+
* beside it and records the project identity in
|
|
8
|
+
* `<workspace>/.motionworks/attached/records`.
|
|
9
|
+
*
|
|
10
|
+
* `<workspace>/backups/<product>/…` is the one backup tree every engineering
|
|
11
|
+
* product writes under, so "back up this project" means the same place whichever
|
|
12
|
+
* product is in front of you. MotionWorks keeps its identity records private
|
|
13
|
+
* under `.motionworks`, and both the shipped bridge (`Get-DirectIdentity`) and
|
|
14
|
+
* the read-only engine (`assert_direct_attached`) verify a record before they
|
|
15
|
+
* open, read or write a real project. A copy outside that tree is not evidence:
|
|
16
|
+
* those guards refuse it, so the tools must write exactly this shape. Records
|
|
17
|
+
* written before 0.4.21 that point at `.motionworks/attached/backups` stay valid.
|
|
18
|
+
*
|
|
19
|
+
* @module dsh-engineering-mwi/session/backup
|
|
20
|
+
*/
|
|
21
|
+
/** One file in a backup manifest. */
|
|
22
|
+
export interface BackupFile {
|
|
23
|
+
/** Path relative to the project's parent directory. */
|
|
24
|
+
readonly path: string;
|
|
25
|
+
/** SHA-256 of the copied bytes. */
|
|
26
|
+
readonly sha256: string;
|
|
27
|
+
}
|
|
28
|
+
/** One verified pre-edit backup. */
|
|
29
|
+
export interface BackupRecord {
|
|
30
|
+
/** Project name, the `.mwt` basename. */
|
|
31
|
+
readonly name: string;
|
|
32
|
+
/** Workspace that owns the backup. */
|
|
33
|
+
readonly workspace: string;
|
|
34
|
+
/** The real `.mwt` the backup was taken from. */
|
|
35
|
+
readonly mwt: string;
|
|
36
|
+
/** The real expanded directory. */
|
|
37
|
+
readonly directory: string;
|
|
38
|
+
/** Directory holding the copy. */
|
|
39
|
+
readonly backup: string;
|
|
40
|
+
/** Manifest path inside the backup directory. */
|
|
41
|
+
readonly manifest: string;
|
|
42
|
+
/** When the copy was written. */
|
|
43
|
+
readonly created_at: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The workspace backup root every engineering product writes under:
|
|
47
|
+
* `<workspace>/backups/<product>/<subject>/<UTC stamp>/`.
|
|
48
|
+
*
|
|
49
|
+
* MotionWorks is the `motionworks` product. Keeping the copies in one visible
|
|
50
|
+
* tree is what makes "back up this project" mean the same place for every
|
|
51
|
+
* product; the identity records stay product-private under `.motionworks`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function backupRootOf(workspace: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* The pre-0.4.21 backup root. Records written by earlier builds point here and
|
|
56
|
+
* stay valid: a shipped record is evidence, so it is accepted, never rewritten.
|
|
57
|
+
*/
|
|
58
|
+
export declare function legacyBackupRootOf(workspace: string): string;
|
|
59
|
+
/** The identity record directory of one workspace. */
|
|
60
|
+
export declare function recordRootOf(workspace: string): string;
|
|
61
|
+
/** Read the record for a project, when a valid backup exists. */
|
|
62
|
+
export declare function findBackup(workspace: string, path: string): BackupRecord | undefined;
|
|
63
|
+
/** Every project with a valid backup in this workspace, one row per project. */
|
|
64
|
+
export declare function listBackups(workspace: string): BackupRecord[];
|
|
65
|
+
/**
|
|
66
|
+
* Back up one real project, or return the valid backup already recorded.
|
|
67
|
+
*
|
|
68
|
+
* @param workspace - Absolute workspace root.
|
|
69
|
+
* @param path - Real `.mwt` or its expanded directory.
|
|
70
|
+
* @returns The verified backup record.
|
|
71
|
+
*/
|
|
72
|
+
export declare function ensureBackup(workspace: string, path: string): BackupRecord;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-edit backups shared by the automation surfaces.
|
|
3
|
+
*
|
|
4
|
+
* Editing happens on the real workspace project. The first write takes one
|
|
5
|
+
* complete, hash-verified copy under
|
|
6
|
+
* `<workspace>/backups/motionworks/<project>/<UTC stamp>`, writes a manifest
|
|
7
|
+
* beside it and records the project identity in
|
|
8
|
+
* `<workspace>/.motionworks/attached/records`.
|
|
9
|
+
*
|
|
10
|
+
* `<workspace>/backups/<product>/…` is the one backup tree every engineering
|
|
11
|
+
* product writes under, so "back up this project" means the same place whichever
|
|
12
|
+
* product is in front of you. MotionWorks keeps its identity records private
|
|
13
|
+
* under `.motionworks`, and both the shipped bridge (`Get-DirectIdentity`) and
|
|
14
|
+
* the read-only engine (`assert_direct_attached`) verify a record before they
|
|
15
|
+
* open, read or write a real project. A copy outside that tree is not evidence:
|
|
16
|
+
* those guards refuse it, so the tools must write exactly this shape. Records
|
|
17
|
+
* written before 0.4.21 that point at `.motionworks/attached/backups` stay valid.
|
|
18
|
+
*
|
|
19
|
+
* @module dsh-engineering-mwi/session/backup
|
|
20
|
+
*/
|
|
21
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
22
|
+
import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
24
|
+
import { MwiError } from "../core/errors.js";
|
|
25
|
+
import { isInside } from "./workspace.js";
|
|
26
|
+
/**
|
|
27
|
+
* The workspace backup root every engineering product writes under:
|
|
28
|
+
* `<workspace>/backups/<product>/<subject>/<UTC stamp>/`.
|
|
29
|
+
*
|
|
30
|
+
* MotionWorks is the `motionworks` product. Keeping the copies in one visible
|
|
31
|
+
* tree is what makes "back up this project" mean the same place for every
|
|
32
|
+
* product; the identity records stay product-private under `.motionworks`.
|
|
33
|
+
*/
|
|
34
|
+
export function backupRootOf(workspace) {
|
|
35
|
+
return join(workspace, 'backups', 'motionworks');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The pre-0.4.21 backup root. Records written by earlier builds point here and
|
|
39
|
+
* stay valid: a shipped record is evidence, so it is accepted, never rewritten.
|
|
40
|
+
*/
|
|
41
|
+
export function legacyBackupRootOf(workspace) {
|
|
42
|
+
return join(workspace, '.motionworks', 'attached', 'backups');
|
|
43
|
+
}
|
|
44
|
+
/** The identity record directory of one workspace. */
|
|
45
|
+
export function recordRootOf(workspace) {
|
|
46
|
+
return join(workspace, '.motionworks', 'attached', 'records');
|
|
47
|
+
}
|
|
48
|
+
function digest(path) {
|
|
49
|
+
return createHash('sha256').update(readFileSync(path)).digest('hex');
|
|
50
|
+
}
|
|
51
|
+
function same(left, right) {
|
|
52
|
+
return resolve(left).toLowerCase() === resolve(right).toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
/** Refuse a link anywhere in a tree the backup will copy. */
|
|
55
|
+
function assertPlainTree(path, workspace) {
|
|
56
|
+
if (!isInside(workspace, path)) {
|
|
57
|
+
throw new MwiError('OUTSIDE_WORKSPACE', `${path} is outside ${workspace}.`, 'Back up a project from the calling workspace.');
|
|
58
|
+
}
|
|
59
|
+
if (lstatSync(path).isSymbolicLink()) {
|
|
60
|
+
throw new MwiError('LINKED_PROJECT', `The project contains a link: ${path}.`, 'Back up a project whose files are all local; links are refused.');
|
|
61
|
+
}
|
|
62
|
+
if (!statSync(path).isDirectory())
|
|
63
|
+
return;
|
|
64
|
+
for (const entry of readdirSync(path))
|
|
65
|
+
assertPlainTree(join(path, entry), workspace);
|
|
66
|
+
}
|
|
67
|
+
/** Every file under a directory, in stable order. */
|
|
68
|
+
function tree(path, files) {
|
|
69
|
+
for (const entry of readdirSync(path, { withFileTypes: true })) {
|
|
70
|
+
const child = join(path, entry.name);
|
|
71
|
+
if (entry.isDirectory())
|
|
72
|
+
tree(child, files);
|
|
73
|
+
else
|
|
74
|
+
files.push(child);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Read and fully validate one identity record. */
|
|
78
|
+
function valid(workspace, record) {
|
|
79
|
+
try {
|
|
80
|
+
if (record.mode !== 'direct' || typeof record.mwt !== 'string' || typeof record.directory !== 'string')
|
|
81
|
+
return false;
|
|
82
|
+
if (!same(record.workspace, workspace))
|
|
83
|
+
return false;
|
|
84
|
+
if (!isInside(workspace, record.mwt) || !isInside(workspace, record.directory))
|
|
85
|
+
return false;
|
|
86
|
+
if (!same(record.directory, record.mwt.slice(0, -4)))
|
|
87
|
+
return false;
|
|
88
|
+
const root = backupRootOf(workspace);
|
|
89
|
+
const legacy = legacyBackupRootOf(workspace);
|
|
90
|
+
if (!isInside(root, record.backup) && !isInside(legacy, record.backup))
|
|
91
|
+
return false;
|
|
92
|
+
if (!isInside(record.backup, record.backup_manifest))
|
|
93
|
+
return false;
|
|
94
|
+
if (!existsSync(record.mwt) || !existsSync(record.directory) || !existsSync(record.backup_manifest))
|
|
95
|
+
return false;
|
|
96
|
+
if (digest(record.backup_manifest) !== record.backup_manifest_sha256)
|
|
97
|
+
return false;
|
|
98
|
+
const manifest = JSON.parse(readFileSync(record.backup_manifest, 'utf8'));
|
|
99
|
+
if (!Array.isArray(manifest.files))
|
|
100
|
+
return false;
|
|
101
|
+
for (const file of manifest.files) {
|
|
102
|
+
if (typeof file?.path !== 'string')
|
|
103
|
+
return false;
|
|
104
|
+
const member = join(record.backup, file.path);
|
|
105
|
+
if (!isInside(record.backup, member) || !existsSync(member))
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
assertPlainTree(record.directory, workspace);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** The record shape the tools return, from an on-disk identity record. */
|
|
116
|
+
function toBackup(record) {
|
|
117
|
+
return {
|
|
118
|
+
name: basename(record.mwt, '.mwt'),
|
|
119
|
+
workspace: record.workspace,
|
|
120
|
+
mwt: record.mwt,
|
|
121
|
+
directory: record.directory,
|
|
122
|
+
backup: record.backup,
|
|
123
|
+
manifest: record.backup_manifest,
|
|
124
|
+
created_at: record.attached_at,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Read every identity record in the workspace, valid or not. */
|
|
128
|
+
function records(workspace) {
|
|
129
|
+
let names = [];
|
|
130
|
+
try {
|
|
131
|
+
names = readdirSync(recordRootOf(workspace)).filter(name => name.endsWith('.json'));
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
const found = [];
|
|
137
|
+
for (const name of names) {
|
|
138
|
+
try {
|
|
139
|
+
const record = JSON.parse(readFileSync(join(recordRootOf(workspace), name), 'utf8'));
|
|
140
|
+
if (typeof record?.mwt === 'string' && typeof record?.directory === 'string')
|
|
141
|
+
found.push(record);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return found;
|
|
148
|
+
}
|
|
149
|
+
/** Read the record for a project, when a valid backup exists. */
|
|
150
|
+
export function findBackup(workspace, path) {
|
|
151
|
+
for (const record of records(workspace)) {
|
|
152
|
+
if (!same(path, record.mwt) && !same(path, record.directory))
|
|
153
|
+
continue;
|
|
154
|
+
if (!valid(workspace, record))
|
|
155
|
+
continue;
|
|
156
|
+
return toBackup(record);
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
/** Every project with a valid backup in this workspace, one row per project. */
|
|
161
|
+
export function listBackups(workspace) {
|
|
162
|
+
const found = [];
|
|
163
|
+
for (const record of records(workspace)) {
|
|
164
|
+
if (!valid(workspace, record))
|
|
165
|
+
continue;
|
|
166
|
+
if (found.some(existing => same(existing.mwt, record.mwt)))
|
|
167
|
+
continue;
|
|
168
|
+
found.push(toBackup(record));
|
|
169
|
+
}
|
|
170
|
+
return found;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Back up one real project, or return the valid backup already recorded.
|
|
174
|
+
*
|
|
175
|
+
* @param workspace - Absolute workspace root.
|
|
176
|
+
* @param path - Real `.mwt` or its expanded directory.
|
|
177
|
+
* @returns The verified backup record.
|
|
178
|
+
*/
|
|
179
|
+
export function ensureBackup(workspace, path) {
|
|
180
|
+
const asked = resolve(path);
|
|
181
|
+
const mwt = asked.toLowerCase().endsWith('.mwt') ? asked : `${asked}.mwt`;
|
|
182
|
+
const directory = mwt.slice(0, -4);
|
|
183
|
+
if (!existsSync(mwt) || !existsSync(directory)) {
|
|
184
|
+
throw new MwiError('PROJECT_INCOMPLETE', `${mwt} has no expanded project directory beside it.`, 'Open a complete project: the .mwt and its sibling folder.');
|
|
185
|
+
}
|
|
186
|
+
if (!isInside(workspace, mwt) || !isInside(workspace, directory)) {
|
|
187
|
+
throw new MwiError('OUTSIDE_WORKSPACE', `${mwt} is outside ${workspace}.`, 'Work on a project inside the calling workspace.');
|
|
188
|
+
}
|
|
189
|
+
if (isInside(join(workspace, '.motionworks'), mwt)) {
|
|
190
|
+
throw new MwiError('REFUSED', `${mwt} lives under .motionworks.`, 'Use the staged-project path for a project in the stage.');
|
|
191
|
+
}
|
|
192
|
+
assertPlainTree(mwt, workspace);
|
|
193
|
+
assertPlainTree(directory, workspace);
|
|
194
|
+
const existing = findBackup(workspace, mwt);
|
|
195
|
+
if (existing !== undefined)
|
|
196
|
+
return existing;
|
|
197
|
+
// A record that no longer validates must be inspected, not silently replaced:
|
|
198
|
+
// a missing or altered backup member is the evidence a restore would need.
|
|
199
|
+
for (const record of records(workspace)) {
|
|
200
|
+
if (same(record.mwt, mwt)) {
|
|
201
|
+
throw new MwiError('BACKUP_INVALID', `The existing backup identity for ${mwt} does not verify.`, `Inspect ${record.backup} and its manifest before attaching again; a tampered or incomplete backup is not replaced automatically.`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const id = randomUUID();
|
|
205
|
+
const name = basename(mwt, '.mwt');
|
|
206
|
+
const stamp = new Date().toISOString().replace(/[:.]/gu, '-');
|
|
207
|
+
const target = join(backupRootOf(workspace), name, stamp);
|
|
208
|
+
const files = [];
|
|
209
|
+
const sources = [mwt];
|
|
210
|
+
tree(directory, sources);
|
|
211
|
+
const before = sources.map(source => ({ source, sha256: digest(source) }));
|
|
212
|
+
mkdirSync(target, { recursive: true });
|
|
213
|
+
try {
|
|
214
|
+
for (const item of before) {
|
|
215
|
+
const rel = relative(dirname(mwt), item.source);
|
|
216
|
+
const destination = join(target, rel);
|
|
217
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
218
|
+
copyFileSync(item.source, destination);
|
|
219
|
+
if (digest(destination) !== item.sha256 || digest(item.source) !== item.sha256) {
|
|
220
|
+
throw new MwiError('BACKUP_UNSTABLE', `The project changed while it was being copied: ${item.source}.`, `Keep ${target} for inspection and retry on an unchanged project.`);
|
|
221
|
+
}
|
|
222
|
+
files.push({ path: rel, sha256: item.sha256 });
|
|
223
|
+
}
|
|
224
|
+
for (const item of before) {
|
|
225
|
+
if (digest(item.source) !== item.sha256) {
|
|
226
|
+
throw new MwiError('BACKUP_UNSTABLE', `The project changed after it was copied: ${item.source}.`, `Keep ${target} for inspection and retry on an unchanged project.`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
if (error instanceof MwiError)
|
|
232
|
+
throw error;
|
|
233
|
+
throw new MwiError('BACKUP_FAILED', error.message, `Keep ${target} for inspection; no identity was written.`);
|
|
234
|
+
}
|
|
235
|
+
const manifest = join(target, 'backup-manifest.json');
|
|
236
|
+
writeFileSync(manifest, JSON.stringify({ workspace, mwt, directory, files }, null, 2));
|
|
237
|
+
const record = {
|
|
238
|
+
mode: 'direct',
|
|
239
|
+
workspace,
|
|
240
|
+
mwt,
|
|
241
|
+
directory,
|
|
242
|
+
session_id: null,
|
|
243
|
+
backup: target,
|
|
244
|
+
backup_manifest: manifest,
|
|
245
|
+
backup_manifest_sha256: digest(manifest),
|
|
246
|
+
attached_at: new Date().toISOString(),
|
|
247
|
+
};
|
|
248
|
+
mkdirSync(recordRootOf(workspace), { recursive: true });
|
|
249
|
+
const pending = join(recordRootOf(workspace), `${id}.json.tmp`);
|
|
250
|
+
const final = join(recordRootOf(workspace), `${id}.json`);
|
|
251
|
+
writeFileSync(pending, JSON.stringify(record, null, 2));
|
|
252
|
+
renameSync(pending, final);
|
|
253
|
+
if (!valid(workspace, record)) {
|
|
254
|
+
throw new MwiError('BACKUP_UNVERIFIED', `The backup at ${target} did not verify.`, `Keep ${target} for inspection and take a fresh copy of an unchanged project.`);
|
|
255
|
+
}
|
|
256
|
+
return toBackup(record);
|
|
257
|
+
}
|
|
@@ -28,7 +28,10 @@ export declare function resolveProject(workspace: string, requested: string): Pr
|
|
|
28
28
|
/**
|
|
29
29
|
* Resolve the project a call means when it names none.
|
|
30
30
|
*
|
|
31
|
+
* An attached direct project wins over a stale staged copy: the direct
|
|
32
|
+
* workflow has no stage, and a leftover stage must not make the call ambiguous.
|
|
33
|
+
*
|
|
31
34
|
* @param workspace - Absolute workspace root.
|
|
32
|
-
* @returns The single staged project.
|
|
35
|
+
* @returns The single attached or staged project.
|
|
33
36
|
*/
|
|
34
37
|
export declare function defaultProject(workspace: string): ProjectRef;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { existsSync, readdirSync } from 'node:fs';
|
|
10
10
|
import { basename, join, resolve } from 'node:path';
|
|
11
11
|
import { MwiError } from "../core/errors.js";
|
|
12
|
+
import { listBackups } from "./backup.js";
|
|
12
13
|
import { isInside, stageRootOf } from "./workspace.js";
|
|
13
14
|
/**
|
|
14
15
|
* Resolve one project path.
|
|
@@ -33,10 +34,20 @@ export function resolveProject(workspace, requested) {
|
|
|
33
34
|
/**
|
|
34
35
|
* Resolve the project a call means when it names none.
|
|
35
36
|
*
|
|
37
|
+
* An attached direct project wins over a stale staged copy: the direct
|
|
38
|
+
* workflow has no stage, and a leftover stage must not make the call ambiguous.
|
|
39
|
+
*
|
|
36
40
|
* @param workspace - Absolute workspace root.
|
|
37
|
-
* @returns The single staged project.
|
|
41
|
+
* @returns The single attached or staged project.
|
|
38
42
|
*/
|
|
39
43
|
export function defaultProject(workspace) {
|
|
44
|
+
const attached = listBackups(workspace);
|
|
45
|
+
if (attached.length === 1) {
|
|
46
|
+
return resolveProject(workspace, attached[0].mwt);
|
|
47
|
+
}
|
|
48
|
+
if (attached.length > 1) {
|
|
49
|
+
throw new MwiError('AMBIGUOUS_PROJECT', `${attached.length} projects have a pre-edit backup.`, 'Pass project: the project directory or .mwt.', attached.map(record => record.name));
|
|
50
|
+
}
|
|
40
51
|
let names = [];
|
|
41
52
|
try {
|
|
42
53
|
names = readdirSync(stageRootOf(workspace)).filter(name => name.toLowerCase().endsWith('.mwt'));
|
|
@@ -45,7 +56,7 @@ export function defaultProject(workspace) {
|
|
|
45
56
|
names = [];
|
|
46
57
|
}
|
|
47
58
|
if (names.length === 0) {
|
|
48
|
-
throw new MwiError('NO_PROJECT', 'No project is staged in this workspace.', 'Call mw_project_find, then
|
|
59
|
+
throw new MwiError('NO_PROJECT', 'No project is open, backed up or staged in this workspace.', 'Call mw_project_find, then mw_project_open with the workspace .mwt.');
|
|
49
60
|
}
|
|
50
61
|
if (names.length > 1) {
|
|
51
62
|
throw new MwiError('AMBIGUOUS_PROJECT', `${names.length} projects are staged.`, 'Pass project: the project directory or .mwt.', names.map(name => name.replace(/\.mwt$/iu, '')));
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module dsh-engineering-mwi/session/service
|
|
9
9
|
*/
|
|
10
10
|
import { type StageOptions, type StageRecord } from './stage.ts';
|
|
11
|
+
import { type BackupRecord } from './backup.ts';
|
|
11
12
|
import { ReadService } from '../read/service.ts';
|
|
12
13
|
/** The bridge directory shipped with this package. */
|
|
13
14
|
export declare const BRIDGE_DIR: string;
|
|
@@ -32,7 +33,9 @@ export interface SessionRuntime {
|
|
|
32
33
|
ideState(): Promise<Record<string, unknown>>;
|
|
33
34
|
/** Stage one workspace project. */
|
|
34
35
|
stage(source: string, options?: StageOptions): Promise<StageRecord>;
|
|
35
|
-
/**
|
|
36
|
+
/** Back up a real workspace project before the first write, or reuse its verified backup. */
|
|
37
|
+
attach(path: string): BackupRecord;
|
|
38
|
+
/** Open a staged or a real (backed-up) project. */
|
|
36
39
|
open(path: string, options: {
|
|
37
40
|
userApproved?: boolean;
|
|
38
41
|
expectedProject?: string;
|
|
@@ -100,8 +103,12 @@ export declare class PowerShellSessionRuntime implements SessionRuntime {
|
|
|
100
103
|
status(): Promise<Record<string, unknown>>;
|
|
101
104
|
/** @returns The bridge's live state document. */
|
|
102
105
|
ideState(): Promise<Record<string, unknown>>;
|
|
106
|
+
/** @returns The open project path, or undefined when no IDE or no project is open. */
|
|
107
|
+
private activeProject;
|
|
103
108
|
/** @returns The written stage record. */
|
|
104
109
|
stage(source: string, options?: StageOptions): Promise<StageRecord>;
|
|
110
|
+
/** @returns The verified pre-edit backup record. */
|
|
111
|
+
attach(path: string): BackupRecord;
|
|
105
112
|
/** @returns The bridge's open result. */
|
|
106
113
|
open(path: string, options: {
|
|
107
114
|
userApproved?: boolean;
|
|
@@ -116,7 +123,7 @@ export declare class PowerShellSessionRuntime implements SessionRuntime {
|
|
|
116
123
|
userApproved: boolean;
|
|
117
124
|
expectedProject?: string;
|
|
118
125
|
}): Promise<Record<string, unknown>>;
|
|
119
|
-
/** @returns The stage record for a path. */
|
|
126
|
+
/** @returns The stage record for a path, when it is staged or directly backed up. */
|
|
120
127
|
stagedAt(path: string): StageRecord | undefined;
|
|
121
128
|
/** @returns The bridge's compile result. */
|
|
122
129
|
compile(mode: 'make' | 'build' | 'state'): Promise<Record<string, unknown>>;
|
|
@@ -147,12 +154,12 @@ export declare class PowerShellSessionRuntime implements SessionRuntime {
|
|
|
147
154
|
expectedBody?: string;
|
|
148
155
|
expectedHash?: string;
|
|
149
156
|
}): Promise<Record<string, unknown>>;
|
|
150
|
-
/** The open project must be a proven stage; every compile and verify depends on it. */
|
|
157
|
+
/** The open project must be a proven stage or a verified direct backup; every compile and verify depends on it. */
|
|
151
158
|
private openStagedRecord;
|
|
152
|
-
/** The only
|
|
159
|
+
/** The only openable project, staged or backed up, or a refusal that explains the choice. */
|
|
153
160
|
private singleStage;
|
|
154
|
-
/** Resolve a requested
|
|
155
|
-
private
|
|
161
|
+
/** Resolve a requested project, taking a backup for a real workspace project. */
|
|
162
|
+
private projectMwt;
|
|
156
163
|
}
|
|
157
164
|
/**
|
|
158
165
|
* Resolve the runtime for one workspace.
|