@aryacode/dsh-engineering 0.4.20 → 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 +24 -0
- package/README.md +7 -1
- package/package.json +1 -1
- package/vendor/ftview/SKILL.md +17 -0
- package/vendor/l5x/SKILL.md +17 -0
- package/vendor/mwi/README.md +10 -8
- package/vendor/mwi/SKILL.md +14 -1
- package/vendor/mwi/bridge/mw_bridge.ps1 +7 -2
- 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/staging.py +6 -2
- package/vendor/mwi/lib/session/backup.d.ts +24 -7
- package/vendor/mwi/lib/session/backup.js +34 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.21] - 2026-10-27
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **One backup tree for every engineering product.** Backups land in
|
|
8
|
+
`<workspace>/backups/<product>/<subject>/<UTC stamp>/` with a `backup-manifest.json` naming every
|
|
9
|
+
copied file and its SHA-256: `motionworks` for the `.mwt` and its expanded folder, `l5x` for the ACD
|
|
10
|
+
or L5X, `ftview` for an export folder, `optix` for a Studio project export. Studio 5000 and
|
|
11
|
+
FactoryTalk View take one on request (their writes already produce a new file or folder); Optix has
|
|
12
|
+
the location but no automated action here, because its tree is the prebuilt component. Every skill
|
|
13
|
+
states the same tree and a test fails when one drifts. `<workspace>/_backups` and the pre-0.4.21
|
|
14
|
+
`<workspace>/backups/<project>/<stamp>/` layout are read-only history and are never written.
|
|
15
|
+
- **MotionWorks moves its copies into that tree.** `mw_project_open` writes
|
|
16
|
+
`backups/motionworks/<project>/<stamp>/`, and both the bridge (`Get-DirectIdentity`) and the engine
|
|
17
|
+
(`assert_direct_attached`) accept a record that points at the old `.motionworks/attached/backups`
|
|
18
|
+
root, so an existing workspace keeps its verified backup with no migration and no second copy.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **`pnpm pack` can no longer ship a stale component.** `scripts/assemble.mjs` refuses a component
|
|
23
|
+
whose `lib/` is older than its `src/`, and the release procedure builds the components before
|
|
24
|
+
packing. That is how the 0.4.20 market tarball came to carry the 0.4.16 MotionWorks build under a
|
|
25
|
+
0.4.20 version.
|
|
26
|
+
|
|
3
27
|
## [0.4.20] - 2026-10-27
|
|
4
28
|
|
|
5
29
|
### Fixed
|
package/README.md
CHANGED
|
@@ -180,7 +180,13 @@ even when the implementation behind a row is replaced.
|
|
|
180
180
|
- **Edits write new files.** `draft_apply` writes one new L5X beside the source and refuses to
|
|
181
181
|
overwrite; `ftview_apply` writes a new export folder and leaves the source untouched. MotionWorks
|
|
182
182
|
edits write the real workspace project, after one complete backup under
|
|
183
|
-
`<workspace
|
|
183
|
+
`<workspace>/backups/motionworks/`, and verify unrelated saved and native state.
|
|
184
|
+
- **One backup tree.** Every product backs up into
|
|
185
|
+
`<workspace>/backups/<product>/<subject>/<UTC stamp>/` with a `backup-manifest.json` naming each
|
|
186
|
+
copied file and its SHA-256, so "back up this project" means the same place in every skill.
|
|
187
|
+
MotionWorks takes its backup on open; L5X, FactoryTalk View and Optix take one on request and before
|
|
188
|
+
an in-place change. `<workspace>/_backups` and `<workspace>/backups/<project>/…` are older layouts
|
|
189
|
+
and are never written.
|
|
184
190
|
- **Controller changes need a human.** Upload, mode change and download require per-call approval and
|
|
185
191
|
a session policy that permits asking. The tools refuse before contacting a controller when approval
|
|
186
192
|
is unavailable, and a download accepts only a verification id produced by a passed build.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aryacode/dsh-engineering",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.21",
|
|
4
4
|
"description": "AryaCode engineering bundle: one install surface for the Studio 5000 L5X, FactoryTalk View ME, MotionWorks IEC and FactoryTalk Optix surfaces, one row per product, with the ladder and display previews.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
package/vendor/ftview/SKILL.md
CHANGED
|
@@ -13,6 +13,23 @@ A staged edit is written to a NEW export folder; the source is never modified. T
|
|
|
13
13
|
a manual import for a human; it does not drive Studio, contact a terminal, or decode MER or MED
|
|
14
14
|
databases.
|
|
15
15
|
|
|
16
|
+
## Backups
|
|
17
|
+
|
|
18
|
+
Every engineering product backs up into the same tree, so a backup means one place whichever surface
|
|
19
|
+
you are in:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
<workspace>/backups/<product>/<subject>/<UTC stamp>/
|
|
23
|
+
backup-manifest.json every copied file: relative path + SHA-256
|
|
24
|
+
<the copy>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
FactoryTalk View is the `ftview` product: the copy is the whole export folder (display XML plus the tag
|
|
28
|
+
CSV), one stamp per backup. `ftview_apply` already writes a new folder, so this is a backup on request
|
|
29
|
+
("back up this export"). Build it as a copy plus `backup-manifest.json` listing every file's relative
|
|
30
|
+
path and SHA-256. `<workspace>/_backups` and `<workspace>/backups/<project>/…` are older layouts: never
|
|
31
|
+
write there, and leave what is there alone.
|
|
32
|
+
|
|
16
33
|
## Start
|
|
17
34
|
|
|
18
35
|
1. Call `ftview_open` with the export folder. Keep the returned `view_id`.
|
package/vendor/l5x/SKILL.md
CHANGED
|
@@ -10,6 +10,23 @@ whenToUse: Use when the user asks about a Studio 5000 L5X export, Logix tags, ro
|
|
|
10
10
|
|
|
11
11
|
This surface reads and drafts. A draft writes only a new L5X file: it never overwrites the source, imports into Studio, connects to a controller, changes mode, or writes runtime data.
|
|
12
12
|
|
|
13
|
+
## Backups
|
|
14
|
+
|
|
15
|
+
Every engineering product backs up into the same tree, so a backup means one place whichever surface
|
|
16
|
+
you are in:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
<workspace>/backups/<product>/<subject>/<UTC stamp>/
|
|
20
|
+
backup-manifest.json every copied file: relative path + SHA-256
|
|
21
|
+
<the copy>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Studio 5000 is the `l5x` product: the copy holds the `.ACD` being worked on, or the `.L5X` export when
|
|
25
|
+
that is the source of truth, one stamp per backup. Drafts and SDK builds already write new files, so
|
|
26
|
+
this is a backup on request ("back up this project"). Build it as a copy plus
|
|
27
|
+
`backup-manifest.json` listing every file's relative path and SHA-256. `<workspace>/_backups` and
|
|
28
|
+
`<workspace>/backups/<project>/…` are older layouts: never write there, and leave what is there alone.
|
|
29
|
+
|
|
13
30
|
## Start
|
|
14
31
|
|
|
15
32
|
1. Call `open_project` with the export path. Keep the returned `project_id`.
|
package/vendor/mwi/README.md
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
MotionWorks IEC 3 Pro tools, redesigned for the `dsh-engineering` bundle. The product edits the
|
|
4
4
|
**real workspace project**: `mw_project_open` writes one complete hash-verified backup under
|
|
5
|
-
`<workspace
|
|
6
|
-
COM bridge. A project the IDE already has open is attached (backup recorded) instead of
|
|
7
|
-
unsaved editor changes survive. `mw_project_stage` remains as an optional sandbox.
|
|
5
|
+
`<workspace>/backups/motionworks/<project>/<stamp>/` first, then drives the running IDE through the
|
|
6
|
+
existing 32-bit COM bridge. A project the IDE already has open is attached (backup recorded) instead of
|
|
7
|
+
reloaded, so unsaved editor changes survive. `mw_project_stage` remains as an optional sandbox.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
backup
|
|
12
|
-
|
|
9
|
+
`<workspace>/backups/<product>/<subject>/<stamp>/` is the one tree every engineering product writes
|
|
10
|
+
under, so a backup is one place whichever surface the user is in. MotionWorks is the product whose
|
|
11
|
+
backup is also a contract: the shipped bridge (`Get-DirectIdentity`) and the read-only engine
|
|
12
|
+
(`assert_direct_attached`) refuse a real project unless an identity record names a copy in that tree,
|
|
13
|
+
with a manifest whose SHA-256 matches. Records written before 0.4.21 that point at
|
|
14
|
+
`.motionworks/attached/backups` stay valid.
|
|
13
15
|
|
|
14
16
|
## Rows
|
|
15
17
|
|
|
@@ -33,7 +35,7 @@ Edit tools: `mw_edit_code`, `mw_edit_variables`, `mw_edit_structure`, `mw_insert
|
|
|
33
35
|
|
|
34
36
|
Verified against MotionWorks IEC 3 Pro 1.19 with a copy of a real project in a temporary workspace:
|
|
35
37
|
|
|
36
|
-
1. `open` writes
|
|
38
|
+
1. `open` writes `backups/motionworks/<project>/<stamp>/` with `backup-manifest.json` and
|
|
37
39
|
`.motionworks/attached/records/<id>.json`, verified before the IDE is asked to open anything, and
|
|
38
40
|
reuses that backup on later attaches.
|
|
39
41
|
2. A project already open in the IDE is attached without an `OpenProject` call: `mw_project_open`
|
package/vendor/mwi/SKILL.md
CHANGED
|
@@ -9,12 +9,25 @@ whenToUse: The user has MotionWorks IEC 3 Pro open or asks for work in it — a
|
|
|
9
9
|
## Boundary
|
|
10
10
|
|
|
11
11
|
This surface edits the **real workspace project**. `mw_project_open` takes one complete, hash-verified
|
|
12
|
-
backup under `<workspace
|
|
12
|
+
backup under `<workspace>/backups/motionworks/` before the IDE opens the project, and reuses that
|
|
13
13
|
backup for later attaches. A project the IDE already has open is attached — the backup is written and
|
|
14
14
|
the project is **not** reloaded, so unsaved editor changes survive. `mw_project_stage` remains
|
|
15
15
|
available as an optional sandbox when you want the IDE to open a copy instead. Nothing here downloads
|
|
16
16
|
to a controller or commands motion.
|
|
17
17
|
|
|
18
|
+
Every engineering product backs up into the same tree, so a backup means one place whichever surface
|
|
19
|
+
you are in:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
<workspace>/backups/<product>/<subject>/<UTC stamp>/
|
|
23
|
+
backup-manifest.json every copied file: relative path + SHA-256
|
|
24
|
+
<the copy>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
MotionWorks is the `motionworks` product and the copy holds the project's `.mwt` beside its expanded
|
|
28
|
+
folder. The open already takes it; do not take a second one. `<workspace>/_backups` and
|
|
29
|
+
`<workspace>/backups/<project>/…` are older layouts: never write there, and leave what is there alone.
|
|
30
|
+
|
|
18
31
|
The native IDE API is the editing path. Desktop input belongs to the computer-use companion and is a
|
|
19
32
|
fallback for operations without a native call, never a substitute for a refused native one.
|
|
20
33
|
|
|
@@ -1019,7 +1019,11 @@ function Get-DirectIdentity([string]$path) {
|
|
|
1019
1019
|
try { $raw = Get-Content -LiteralPath $file.FullName -Raw -Encoding UTF8 | ConvertFrom-Json }
|
|
1020
1020
|
catch { continue }
|
|
1021
1021
|
if ([string]$raw.mode -cne 'direct' -or -not (Test-SameProject ([string]$raw.mwt) $path)) { continue }
|
|
1022
|
-
|
|
1022
|
+
# One workspace backup tree for every engineering product:
|
|
1023
|
+
# <workspace>\backups\<product>\<subject>\<stamp>. Records written before
|
|
1024
|
+
# 0.4.21 point at .motionworks\attached\backups and stay valid.
|
|
1025
|
+
$backupRoot = Normalize-MwPath (Join-Path $script:WorkspaceRoot 'backups\motionworks')
|
|
1026
|
+
$legacyRoot = Normalize-MwPath (Join-Path $script:WorkspaceRoot '.motionworks\attached\backups')
|
|
1023
1027
|
$backup = Normalize-MwPath ([string]$raw.backup)
|
|
1024
1028
|
$manifestPath = Normalize-MwPath ([string]$raw.backup_manifest)
|
|
1025
1029
|
if (-not (Test-InsideWorkspace $backup) -or -not (Test-InsideWorkspace $manifestPath)) { return $null }
|
|
@@ -1029,7 +1033,8 @@ function Get-DirectIdentity([string]$path) {
|
|
|
1029
1033
|
-not (Test-SameProject ([string]$raw.directory) $path) -or
|
|
1030
1034
|
-not (Test-Path -LiteralPath ([string]$raw.mwt)) -or
|
|
1031
1035
|
-not (Test-Path -LiteralPath ([string]$raw.directory)) -or
|
|
1032
|
-
-not $backup.StartsWith($backupRoot + '\', [StringComparison]::OrdinalIgnoreCase) -or
|
|
1036
|
+
-not ($backup.StartsWith($backupRoot + '\', [StringComparison]::OrdinalIgnoreCase) -or
|
|
1037
|
+
$backup.StartsWith($legacyRoot + '\', [StringComparison]::OrdinalIgnoreCase)) -or
|
|
1033
1038
|
-not $manifestPath.StartsWith($backup + '\', [StringComparison]::OrdinalIgnoreCase) -or
|
|
1034
1039
|
-not (Test-Path -LiteralPath ([string]$raw.backup_manifest))) { return $null }
|
|
1035
1040
|
if ((Get-MwSha256 ([string]$raw.backup_manifest)) -ine [string]$raw.backup_manifest_sha256) { return $null }
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -149,9 +149,13 @@ def assert_direct_attached(project_root: Path | str) -> Path:
|
|
|
149
149
|
continue
|
|
150
150
|
backup = Path(record["backup"]).resolve()
|
|
151
151
|
manifest = Path(record["backup_manifest"]).resolve()
|
|
152
|
-
|
|
152
|
+
# One workspace backup tree for every engineering product:
|
|
153
|
+
# <workspace>/backups/<product>/<subject>/<stamp>. Records written
|
|
154
|
+
# before 0.4.21 point at .motionworks/attached/backups and stay valid.
|
|
155
|
+
backup_root = workspace / "backups" / "motionworks"
|
|
156
|
+
legacy_root = workspace / ".motionworks" / "attached" / "backups"
|
|
153
157
|
if (Path(record["workspace"]).resolve() != workspace
|
|
154
|
-
or not _inside(backup, backup_root)
|
|
158
|
+
or not (_inside(backup, backup_root) or _inside(backup, legacy_root))
|
|
155
159
|
or not _inside(manifest, backup)
|
|
156
160
|
or not manifest.is_file()
|
|
157
161
|
or hashlib.sha256(manifest.read_bytes()).hexdigest() != record["backup_manifest_sha256"]
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Editing happens on the real workspace project. The first write takes one
|
|
5
5
|
* complete, hash-verified copy under
|
|
6
|
-
* `<workspace
|
|
7
|
-
* and records the project identity in
|
|
6
|
+
* `<workspace>/backups/motionworks/<project>/<UTC stamp>`, writes a manifest
|
|
7
|
+
* beside it and records the project identity in
|
|
8
|
+
* `<workspace>/.motionworks/attached/records`.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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.
|
|
13
18
|
*
|
|
14
19
|
* @module dsh-engineering-mwi/session/backup
|
|
15
20
|
*/
|
|
@@ -37,8 +42,20 @@ export interface BackupRecord {
|
|
|
37
42
|
/** When the copy was written. */
|
|
38
43
|
readonly created_at: string;
|
|
39
44
|
}
|
|
40
|
-
/**
|
|
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
|
+
*/
|
|
41
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;
|
|
42
59
|
/** The identity record directory of one workspace. */
|
|
43
60
|
export declare function recordRootOf(workspace: string): string;
|
|
44
61
|
/** Read the record for a project, when a valid backup exists. */
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Editing happens on the real workspace project. The first write takes one
|
|
5
5
|
* complete, hash-verified copy under
|
|
6
|
-
* `<workspace
|
|
7
|
-
* and records the project identity in
|
|
6
|
+
* `<workspace>/backups/motionworks/<project>/<UTC stamp>`, writes a manifest
|
|
7
|
+
* beside it and records the project identity in
|
|
8
|
+
* `<workspace>/.motionworks/attached/records`.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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.
|
|
13
18
|
*
|
|
14
19
|
* @module dsh-engineering-mwi/session/backup
|
|
15
20
|
*/
|
|
@@ -18,8 +23,22 @@ import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSy
|
|
|
18
23
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
19
24
|
import { MwiError } from "../core/errors.js";
|
|
20
25
|
import { isInside } from "./workspace.js";
|
|
21
|
-
/**
|
|
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
|
+
*/
|
|
22
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) {
|
|
23
42
|
return join(workspace, '.motionworks', 'attached', 'backups');
|
|
24
43
|
}
|
|
25
44
|
/** The identity record directory of one workspace. */
|
|
@@ -66,7 +85,11 @@ function valid(workspace, record) {
|
|
|
66
85
|
return false;
|
|
67
86
|
if (!same(record.directory, record.mwt.slice(0, -4)))
|
|
68
87
|
return false;
|
|
69
|
-
|
|
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))
|
|
70
93
|
return false;
|
|
71
94
|
if (!existsSync(record.mwt) || !existsSync(record.directory) || !existsSync(record.backup_manifest))
|
|
72
95
|
return false;
|
|
@@ -179,7 +202,9 @@ export function ensureBackup(workspace, path) {
|
|
|
179
202
|
}
|
|
180
203
|
}
|
|
181
204
|
const id = randomUUID();
|
|
182
|
-
const
|
|
205
|
+
const name = basename(mwt, '.mwt');
|
|
206
|
+
const stamp = new Date().toISOString().replace(/[:.]/gu, '-');
|
|
207
|
+
const target = join(backupRootOf(workspace), name, stamp);
|
|
183
208
|
const files = [];
|
|
184
209
|
const sources = [mwt];
|
|
185
210
|
tree(directory, sources);
|