@aryacode/dsh-engineering 0.9.1 → 0.9.3
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 +21 -0
- package/package.json +1 -1
- package/vendor/ftview/lib/vendor-tools.js +1 -1
- package/vendor/ftview/lib/vendor.js +1 -1
- package/vendor/resources/lib/index.d.ts +15 -17
- package/vendor/resources/lib/index.js +15 -41
- package/vendor/resources/lib/skill.d.ts +46 -0
- package/vendor/resources/lib/skill.js +74 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.3] - 2026-10-27
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`ftview_export` reported `0 display(s)` for a successful export.** The display count used a pattern that
|
|
8
|
+
required a leading slash, but export paths are relative (`Displays/Main Screen.xml`), so every export said
|
|
9
|
+
zero displays even when it wrote thirteen. The count now matches the paths the tool actually returns.
|
|
10
|
+
- **The tags and alarms warning now says what it means.** The copied `Alarms/Alarms.xml` and
|
|
11
|
+
`Tags/<application>.csv` are snapshots Studio last wrote when those components were exported from their
|
|
12
|
+
editors — not fresh data. The warning states that, and that the application export in Studio is what
|
|
13
|
+
refreshes them. There is no supported headless export for either component; this is the honest boundary.
|
|
14
|
+
## [0.9.2] - 2026-10-27
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **The `engineering-standards` skill was missing from the catalog.** 0.9.1 mounted it by calling the skills
|
|
19
|
+
service from inside the resources row's `apply()`, which only works if that service already exists at that
|
|
20
|
+
row's turn — it mounts before the Optix row, so the skill was silently skipped (the tools still loaded).
|
|
21
|
+
The skill is now its own plugin with `inject: ['skills']`, the same pattern the product skills use, so the
|
|
22
|
+
host activates it when the service is up regardless of row order. A test asserts the injection list and
|
|
23
|
+
registration.
|
|
3
24
|
## [0.9.1] - 2026-10-27
|
|
4
25
|
|
|
5
26
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aryacode/dsh-engineering",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
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",
|
|
@@ -66,7 +66,7 @@ export function vendorTools(fs) {
|
|
|
66
66
|
}
|
|
67
67
|
const files = exported.value?.files ?? [];
|
|
68
68
|
const warnings = exported.value?.warnings ?? [];
|
|
69
|
-
const displays = files.filter(file =>
|
|
69
|
+
const displays = files.filter(file => /^Displays\/.+\.xml$/iu.test(file) && !/batchimport/iu.test(file));
|
|
70
70
|
const manifests = files.filter(file => /batchimport/iu.test(file));
|
|
71
71
|
const alarms = files.filter(file => /^Alarms\//iu.test(file));
|
|
72
72
|
const tags = files.filter(file => /^Tags\/.+\.csv$/iu.test(file));
|
|
@@ -148,7 +148,7 @@ function freshness(file, relative, database) {
|
|
|
148
148
|
try {
|
|
149
149
|
if (statSync(file).mtimeMs + 60_000 >= statSync(database).mtimeMs)
|
|
150
150
|
return undefined;
|
|
151
|
-
return `${relative} was written ${modifiedOn(file)}, older than the application database (${modifiedOn(database)})
|
|
151
|
+
return `${relative} was written ${modifiedOn(file)}, older than the application database (${modifiedOn(database)}). This is the last snapshot Studio wrote for that component, not a fresh export: run the application export in FactoryTalk View Studio once to refresh it, then reopen that folder.`;
|
|
152
152
|
}
|
|
153
153
|
catch {
|
|
154
154
|
return undefined;
|
|
@@ -5,24 +5,18 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The corpus is generated data (`index.json`, `SOURCE.json`) over authored
|
|
7
7
|
* Markdown; the profile is produced by whichever product package has a project
|
|
8
|
-
* open. This entry
|
|
9
|
-
*
|
|
8
|
+
* open. This entry wires them to the tool registry and mounts the skill as a
|
|
9
|
+
* sub-plugin that waits for the skills service, so row order cannot drop it.
|
|
10
10
|
*
|
|
11
11
|
* @module dsh-engineering-resources
|
|
12
12
|
*/
|
|
13
13
|
import type { StyleProfile } from './profile.ts';
|
|
14
14
|
/** Plugin name in a composition. */
|
|
15
15
|
export declare const name = "engineering-resources";
|
|
16
|
-
/** The tools need the registry; the skill is
|
|
16
|
+
/** The tools need the registry; the skill is a sub-plugin with its own injection. */
|
|
17
17
|
export declare const inject: string[];
|
|
18
18
|
/** The service name a generation tool can inject to read a profile by handle. */
|
|
19
19
|
export declare const PROFILES_SERVICE = "engineeringProfiles";
|
|
20
|
-
/** The skill metadata, kept here so a test can assert it without the file. */
|
|
21
|
-
export declare const SKILL: {
|
|
22
|
-
name: string;
|
|
23
|
-
description: string;
|
|
24
|
-
whenToUse: string;
|
|
25
|
-
};
|
|
26
20
|
/** Configuration for the row. */
|
|
27
21
|
export interface Config {
|
|
28
22
|
/** Whether the reference and profile tools are registered. Defaults to true. */
|
|
@@ -33,23 +27,27 @@ export interface ProfilesService {
|
|
|
33
27
|
/** The profile for one handle, when it is still in memory. */
|
|
34
28
|
get(id: string): StyleProfile | undefined;
|
|
35
29
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
* @param ctx - Plugin context with the tool registry and optional services.
|
|
40
|
-
* @param config - Row configuration.
|
|
41
|
-
*/
|
|
42
|
-
export declare function apply(ctx: {
|
|
30
|
+
/** The plugin context shape this row needs. */
|
|
31
|
+
interface RowContext {
|
|
43
32
|
tools: {
|
|
44
33
|
register(definition: unknown): void;
|
|
45
34
|
};
|
|
46
35
|
get?: (name: string) => unknown;
|
|
47
36
|
provide?: (name: string, value: unknown) => void;
|
|
37
|
+
plugin?: (plugin: unknown) => void;
|
|
48
38
|
logger?: {
|
|
49
39
|
warn?(message: string): void;
|
|
50
40
|
};
|
|
51
|
-
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Register the resource tools and mount the standards skill.
|
|
44
|
+
*
|
|
45
|
+
* @param ctx - Plugin context with the tool registry and optional services.
|
|
46
|
+
* @param config - Row configuration.
|
|
47
|
+
*/
|
|
48
|
+
export declare function apply(ctx: RowContext, config?: Config): void;
|
|
52
49
|
export { getProfile } from './store.ts';
|
|
53
50
|
export { validateProfile, profileId } from './profile.ts';
|
|
54
51
|
export type { StyleProfile } from './profile.ts';
|
|
55
52
|
export { resourceTools, referenceTool, profileTool } from './tools.ts';
|
|
53
|
+
export { SKILL } from './skill.ts';
|
|
@@ -5,39 +5,22 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The corpus is generated data (`index.json`, `SOURCE.json`) over authored
|
|
7
7
|
* Markdown; the profile is produced by whichever product package has a project
|
|
8
|
-
* open. This entry
|
|
9
|
-
*
|
|
8
|
+
* open. This entry wires them to the tool registry and mounts the skill as a
|
|
9
|
+
* sub-plugin that waits for the skills service, so row order cannot drop it.
|
|
10
10
|
*
|
|
11
11
|
* @module dsh-engineering-resources
|
|
12
12
|
*/
|
|
13
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
14
|
-
import { dirname, join } from 'node:path';
|
|
15
|
-
import { fileURLToPath } from 'node:url';
|
|
16
13
|
import { resourceTools } from "./tools.js";
|
|
17
14
|
import { getProfile } from "./store.js";
|
|
15
|
+
import * as skill from "./skill.js";
|
|
18
16
|
/** Plugin name in a composition. */
|
|
19
17
|
export const name = 'engineering-resources';
|
|
20
|
-
/** The tools need the registry; the skill is
|
|
18
|
+
/** The tools need the registry; the skill is a sub-plugin with its own injection. */
|
|
21
19
|
export const inject = ['tools'];
|
|
22
20
|
/** The service name a generation tool can inject to read a profile by handle. */
|
|
23
21
|
export const PROFILES_SERVICE = 'engineeringProfiles';
|
|
24
|
-
/** The skill metadata, kept here so a test can assert it without the file. */
|
|
25
|
-
export const SKILL = {
|
|
26
|
-
name: 'engineering-standards',
|
|
27
|
-
description: 'Engineering standards and house style for control code and HMI: read the local corpus for IEC 61131-3, Rockwell, Yaskawa, FactoryTalk and craft rules, profile the open project to follow its own conventions, and cite rule IDs in findings.',
|
|
28
|
-
whenToUse: 'Before writing or reviewing PLC code, ladder, Structured Text, FactoryTalk View or Optix screens — especially when matching an existing project’s style, or when starting new work that should meet a professional standard.',
|
|
29
|
-
};
|
|
30
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
31
|
-
/** The skill file, in the built package or the source tree. */
|
|
32
|
-
function skillPath() {
|
|
33
|
-
for (const candidate of [join(HERE, '..', 'SKILL.md'), join(HERE, '..', '..', 'SKILL.md')]) {
|
|
34
|
-
if (existsSync(candidate))
|
|
35
|
-
return candidate;
|
|
36
|
-
}
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
22
|
/**
|
|
40
|
-
* Register the resource tools and the standards skill.
|
|
23
|
+
* Register the resource tools and mount the standards skill.
|
|
41
24
|
*
|
|
42
25
|
* @param ctx - Plugin context with the tool registry and optional services.
|
|
43
26
|
* @param config - Row configuration.
|
|
@@ -53,26 +36,16 @@ export function apply(ctx, config = {}) {
|
|
|
53
36
|
ctx.tools.register(definition);
|
|
54
37
|
}
|
|
55
38
|
ctx.provide?.(PROFILES_SERVICE, { get: getProfile });
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return;
|
|
39
|
+
// Mount the skill as its own plugin: it declares `inject: ['skills']`, so the
|
|
40
|
+
// host activates it when that service exists rather than at this row's turn.
|
|
41
|
+
if (typeof ctx.plugin === 'function') {
|
|
42
|
+
ctx.plugin(skill);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const skills = getService('skills');
|
|
46
|
+
if (skills !== undefined)
|
|
47
|
+
skill.registerSkill(skills);
|
|
66
48
|
}
|
|
67
|
-
const content = readFileSync(path, 'utf8').replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/u, '');
|
|
68
|
-
skills.register({
|
|
69
|
-
...SKILL,
|
|
70
|
-
content,
|
|
71
|
-
path,
|
|
72
|
-
source: 'custom',
|
|
73
|
-
provider: '@aryacode/dsh-engineering',
|
|
74
|
-
invocation: { modelInvocable: true, userInvocable: true },
|
|
75
|
-
});
|
|
76
49
|
}
|
|
77
50
|
catch (error) {
|
|
78
51
|
try {
|
|
@@ -86,3 +59,4 @@ export function apply(ctx, config = {}) {
|
|
|
86
59
|
export { getProfile } from "./store.js";
|
|
87
60
|
export { validateProfile, profileId } from "./profile.js";
|
|
88
61
|
export { resourceTools, referenceTool, profileTool } from "./tools.js";
|
|
62
|
+
export { SKILL } from "./skill.js";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engineering-standards skill, mounted as its own plugin.
|
|
3
|
+
*
|
|
4
|
+
* The first version registered the skill inside the resources row's `apply`,
|
|
5
|
+
* which meant it ran only if the skills service already existed at that moment.
|
|
6
|
+
* The resources row mounts before the Optix row, and the skill silently
|
|
7
|
+
* disappeared from the catalog when the service was not up yet. Declaring
|
|
8
|
+
* `inject: ['skills']` lets the host activate this plugin when the service
|
|
9
|
+
* exists, exactly like the product skill plugins.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-engineering-resources/skill
|
|
12
|
+
*/
|
|
13
|
+
/** Plugin name in a composition. */
|
|
14
|
+
export declare const name = "engineering-resources-skill";
|
|
15
|
+
/** The host must offer the skills service before this plugin activates. */
|
|
16
|
+
export declare const inject: string[];
|
|
17
|
+
/** The skill metadata. */
|
|
18
|
+
export declare const SKILL: {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
whenToUse: string;
|
|
22
|
+
};
|
|
23
|
+
/** The skill file, in the built package or the source tree. */
|
|
24
|
+
export declare function skillPath(): string | undefined;
|
|
25
|
+
/** The skill body, with any front matter removed. */
|
|
26
|
+
export declare function skillContent(path: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Register the skill in one skills service.
|
|
29
|
+
*
|
|
30
|
+
* @param service - The host's skills service.
|
|
31
|
+
* @returns True when it was registered; false when the file is missing.
|
|
32
|
+
*/
|
|
33
|
+
export declare function registerSkill(service: {
|
|
34
|
+
register?(definition: unknown): void;
|
|
35
|
+
}): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Activate the skill plugin.
|
|
38
|
+
*
|
|
39
|
+
* @param ctx - Plugin context, with the skills service present by injection.
|
|
40
|
+
*/
|
|
41
|
+
export declare function apply(ctx: {
|
|
42
|
+
get?: (name: string) => unknown;
|
|
43
|
+
logger?: {
|
|
44
|
+
warn?(message: string): void;
|
|
45
|
+
};
|
|
46
|
+
}): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engineering-standards skill, mounted as its own plugin.
|
|
3
|
+
*
|
|
4
|
+
* The first version registered the skill inside the resources row's `apply`,
|
|
5
|
+
* which meant it ran only if the skills service already existed at that moment.
|
|
6
|
+
* The resources row mounts before the Optix row, and the skill silently
|
|
7
|
+
* disappeared from the catalog when the service was not up yet. Declaring
|
|
8
|
+
* `inject: ['skills']` lets the host activate this plugin when the service
|
|
9
|
+
* exists, exactly like the product skill plugins.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-engineering-resources/skill
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
/** Plugin name in a composition. */
|
|
17
|
+
export const name = 'engineering-resources-skill';
|
|
18
|
+
/** The host must offer the skills service before this plugin activates. */
|
|
19
|
+
export const inject = ['skills'];
|
|
20
|
+
/** The skill metadata. */
|
|
21
|
+
export const SKILL = {
|
|
22
|
+
name: 'engineering-standards',
|
|
23
|
+
description: 'Engineering standards and house style for control code and HMI: read the local corpus for IEC 61131-3, Rockwell, Yaskawa, FactoryTalk and craft rules, profile the open project to follow its own conventions, and cite rule IDs in findings.',
|
|
24
|
+
whenToUse: 'Before writing or reviewing PLC code, ladder, Structured Text, FactoryTalk View or Optix screens — especially when matching an existing project’s style, or when starting new work that should meet a professional standard.',
|
|
25
|
+
};
|
|
26
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
/** The skill file, in the built package or the source tree. */
|
|
28
|
+
export function skillPath() {
|
|
29
|
+
for (const candidate of [join(HERE, '..', 'SKILL.md'), join(HERE, '..', '..', 'SKILL.md')]) {
|
|
30
|
+
if (existsSync(candidate))
|
|
31
|
+
return candidate;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
/** The skill body, with any front matter removed. */
|
|
36
|
+
export function skillContent(path) {
|
|
37
|
+
return readFileSync(path, 'utf8').replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/u, '');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Register the skill in one skills service.
|
|
41
|
+
*
|
|
42
|
+
* @param service - The host's skills service.
|
|
43
|
+
* @returns True when it was registered; false when the file is missing.
|
|
44
|
+
*/
|
|
45
|
+
export function registerSkill(service) {
|
|
46
|
+
const path = skillPath();
|
|
47
|
+
if (path === undefined || service.register === undefined)
|
|
48
|
+
return false;
|
|
49
|
+
service.register({
|
|
50
|
+
...SKILL,
|
|
51
|
+
content: skillContent(path),
|
|
52
|
+
path,
|
|
53
|
+
source: 'custom',
|
|
54
|
+
provider: '@aryacode/dsh-engineering',
|
|
55
|
+
invocation: { modelInvocable: true, userInvocable: true },
|
|
56
|
+
});
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Activate the skill plugin.
|
|
61
|
+
*
|
|
62
|
+
* @param ctx - Plugin context, with the skills service present by injection.
|
|
63
|
+
*/
|
|
64
|
+
export function apply(ctx) {
|
|
65
|
+
const skills = typeof ctx.get === 'function' ? ctx.get('skills') : undefined;
|
|
66
|
+
if (skills === undefined || !registerSkill(skills)) {
|
|
67
|
+
try {
|
|
68
|
+
ctx.logger?.warn?.('engineering-resources: the engineering-standards skill could not be registered; the tools are available.');
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Nothing left to report to.
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|