Files
wwtools/graccesscli/docs/usage.md
Joseph Doherty c52d8d0171 graccesscli: correct script-edit docs to TN-537 truth (writes DO persist)
I was wrong. AVEVA Tech Note 537 ("Creating an Application Object Script
Using GRAccess", April 2008) documents the supported pattern:
ConfigurableAttributes[<script>.<field>].SetValue(MxValue) inside a
CheckOut/Save/CheckIn cycle. graccesscli's existing
FindAttributeForMutation already follows this — writes to MxCategoryPackageOnly_Lockable
script-text fields persist correctly.

The earlier "writeback gap" diagnosis was a phantom caused by a reader-side
issue. `object attribute value get` against a script body returns
"Supported: False / Attribute value is not exposed" because
MxValueDetails uses a case-sensitive `ReadProperty(attr, "Value")` lookup
plus an accessor probe (GetBoolean -> GetInteger -> GetFloat -> GetDouble
-> GetString) that can fall through silently for some MxValue shapes. The
COM-side property is exposed as `value` (lowercase), readable as
`attr.value.GetString()` -- which the live probe at
`analysis/ide-edit-investigation/probe_setvalue/` does and confirms the
post-write content matches the marker exactly.

Live verification on $TestMachine.UpdateTestChangingInt.DeclarationsText
and $DelmiaReceiver.ProcessRecipe.{ExecuteText,DeclarationsText}:

  === verdict ===
    marker landed on same-proxy   ConfigurableAttributes: True
    marker landed on same-proxy   Attributes            : True
    marker landed on fresh-proxy  ConfigurableAttributes: True
    marker landed on fresh-proxy  Attributes            : True

The probe also confirmed that two earlier graccesscli `object scripts set`
invocations (which I had wrongly believed failed) had persisted -- the
marker text I wrote previously was still on disk in
ProcessRecipe.{ExecuteText,DeclarationsText} when read directly via
attr.value.GetString(). The probe restored both fields to their original
values.

This commit:

- Updates the misleading [Command(...)] / [CommandOption(...)]
  descriptions in GRAccessSurfaceCommands.cs back to honest versions
  citing TN-537.
- Restores the --file-using examples for `object scripts set` and
  `object scripts create` across script-editing.md, llm-integration.md,
  usage.md, and zb-testmachine.md.
- Removes the test that asserted the (wrong) EnsureMutableViaSetValue
  guard. Re-aims ScriptCommandDescriptions_… at the corrected wording.
- Removes two leftover EnsureMutableViaSetValue calls in the trigger-period
  / trigger-type write paths (both targeted MxCategoryWriteable_C_Lockable
  attributes; would never have fired even if the helper still existed).
- Adds analysis/ide-edit-investigation/REPORT.md (replacing the earlier
  wrong report) plus the probe sources under probe_setvalue/.

The MxValueDetails reader gap (case-sensitive ReadProperty + accessor
probe) is a real follow-up: `object attribute value get` should
case-insensitively read `value` and try GetString first when the
underlying MxValue.DataType is MxString. Out of scope here -- that's a
separate, smaller fix.

Test count delta: 67 -> 66 (-2 wrong tests, +1 corrected description test).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 21:33:51 -04:00

436 lines
20 KiB
Markdown

# GRAccess CLI Usage
Command-line interface for Aveva System Platform Galaxy management via the GRAccess library.
## Global Options
All commands that interact with a galaxy require:
| Option | Short | Description | Required |
|---|---|---|---|
| `--galaxy` | `-g` | Galaxy name | Yes |
| `--node` | `-n` | GR node name. Blank defaults to local node; `.` is normalized to the local machine name. | One-shot mode only |
In session mode, `--node` is not needed because the daemon already holds the connection.
Machine-facing commands also support:
| Option | Description |
|---|---|
| `--json` | Legacy command-specific JSON. Existing shapes are preserved. |
| `--llm-json` | Stable success/error envelope for LLM and tool callers. |
| `--dry-run` | For mutating routed commands, validate arguments and confirmation without invoking mutating GRAccess calls. |
`--llm-json` envelope shape:
```json
{
"success": true,
"command": "object get",
"galaxy": "ZB",
"target": "TestMachine",
"data": {},
"commandResult": null,
"warnings": [],
"unavailable": [],
"error": null,
"exitCode": 0
}
```
## LLM And Tooling Commands
### `capabilities`
Return the code-backed command registry.
```powershell
graccess capabilities --json
graccess capabilities --llm-json
```
The registry includes command names, arguments, mutation status, session routing support, confirmation target rules, and output schema names.
### `validate`
Validate a single-command or batch plan file without connecting to GRAccess.
```powershell
graccess validate --request plan.json --llm-json
```
### `batch`
Validate or execute a command plan. Execution stops on first failure. Every mutating step must include its own `confirm=true` and exact `confirm-target`.
```powershell
graccess batch --file plan.json --mode validate --llm-json
graccess batch --file plan.json --mode execute --llm-json
```
Plan example:
```json
{
"Galaxy": "ZB",
"Node": ".",
"Commands": [
{
"Command": "object attribute value set",
"Args": {
"name": "TestMachine",
"type": "template",
"attribute": "Description",
"value": "Updated",
"data-type": "string",
"confirm": true,
"confirm-target": "TestMachine"
}
}
]
}
```
## Galaxy Commands
### `galaxy list`
List available galaxies on a GR node.
```
graccess galaxy list [--node <node>] [--json] [--llm-json]
```
| Option | Short | Required | Description |
|---|---|---|---|
| `--node` | `-n` | No | GR node name to query. Blank defaults to local node; `.` is normalized to the local machine name. |
| `--json` | | No | Output as JSON array |
| `--llm-json` | | No | Output stable LLM envelope |
This command always runs in one-shot mode (no galaxy login needed). It does not use or require an active session.
Example output:
```
MyGalaxy1
MyGalaxy2
TestGalaxy
```
With `--json`:
```json
[
"MyGalaxy1",
"MyGalaxy2",
"TestGalaxy"
]
```
---
## Object Query Commands
These commands route through an active session when one exists for the target galaxy. Without an active session, they connect to a galaxy, run a read-only GRAccess query, and disconnect. Name patterns use the GRAccess `namedLike` condition; use `%` as the wildcard.
### `object list`
List templates, instances, or both.
```
graccess object list --galaxy <name> --node <node> [--type all|template|instance] [--pattern <pattern>] [--json]
```
| Option | Short | Default | Description |
|---|---|---|---|
| `--galaxy` | `-g` | (required) | Galaxy name |
| `--node` | `-n` | local node | GR node name. `.` is normalized to the local machine name. |
| `--type` | `-t` | `all` | Object type: `all`, `template`, or `instance` |
| `--pattern` | `-p` | `%` | GRAccess name pattern. Use `%` as wildcard. |
| `--json` | | `false` | Output as JSON |
Example:
```powershell
graccess object list --galaxy ZB --node . --type instance --pattern 'TestMachine_%'
```
### `template list`
List templates.
```
graccess template list --galaxy <name> --node <node> [--pattern <pattern>] [--json]
```
### `instance list`
List instances.
```
graccess instance list --galaxy <name> --node <node> [--pattern <pattern>] [--json]
```
Example:
```powershell
graccess instance list --galaxy ZB --node . --pattern '%'
```
### `object attributes`
List attributes for a template or instance.
```
graccess object attributes --galaxy <name> --node <node> --name <tagname> [--type all|template|instance] [--configurable] [--json]
```
| Option | Short | Default | Description |
|---|---|---|---|
| `--galaxy` | `-g` | (required) | Galaxy name |
| `--node` | `-n` | local node | GR node name. `.` is normalized to the local machine name. |
| `--name` | | (required) | Template or instance tagname |
| `--type` | `-t` | `all` | Object type: `all`, `template`, or `instance` |
| `--configurable` | | `false` | Query `ConfigurableAttributes` instead of `Attributes` |
| `--json` | | `false` | Output as JSON |
Some optional COM-backed attribute properties are not available for every object. In JSON output those values are emitted as `null`; text output includes the stable name, data type, and category columns.
Example:
```powershell
graccess object attributes --galaxy ZB --node . --name DEV --type instance --configurable
```
---
## LLM Snapshot And IDE Commands
### `object snapshot`
Return a bundled object snapshot for planning and verification.
```powershell
graccess object snapshot --galaxy ZB --name TestMachine --type template --llm-json
```
Snapshot data includes object identity/status, all attributes, configurable attributes, extended attributes, relationships, lineage, children, contained objects, package-backed attribute values and script bodies where available, and unavailable field details.
Use snapshots, direct lineage commands, and relationship queries together to parse inheritance and containment:
```powershell
graccess object snapshot --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object lineage --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object children --galaxy ZB --name '$TestMachine' --type template --llm-json
graccess object query-condition --galaxy ZB --type all --condition derivedOrInstantiatedFrom --value '$gMachine' --llm-json
graccess object query-condition --galaxy ZB --type all --condition basedOn --value '$gMachine' --llm-json
graccess object query-condition --galaxy ZB --type all --condition containedBy --value '$TestMachine' --llm-json
graccess object query-condition --galaxy ZB --type all --condition hierarchicalNameLike --value '%TestMachine%' --llm-json
```
`object lineage` walks typed `ITemplate` / `IInstance` relationship properties first and falls back to the read-only package snapshot when GRAccess export is available. `object children` combines package-backed containment with direct GRAccess scans for objects whose `DerivedFrom`, `BasedOn`, `Container`, or `HierarchicalName` point at the target.
When direct GRAccess does not expose a field, the CLI records a structured unavailable entry instead of guessing. Normal CLI usage does not query the Galaxy SQL database; SQL is allowed only for development verification/debugging outside the supported command path.
### Attribute values
```powershell
graccess object attribute value get --galaxy ZB --name TestMachine --type template --attribute Description --llm-json
graccess object attribute value set --galaxy ZB --name TestMachine --type template --attribute Description --value Updated --data-type string --confirm --confirm-target TestMachine --llm-json
```
Scalar `string`, `bool`, `int`, `float`, and `double` writes are supported first. Value reads try direct `IAttribute.Value` first, then use the read-only exported package fallback for scalar package values. Array and complex readback returns structured unavailable details unless parsed safely.
### Object scripts
```powershell
graccess object scripts list --galaxy ZB --name TestMachine --type template --llm-json
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json
graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt.ExecuteText --llm-json
graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --llm-json
graccess object scripts settings set --galaxy ZB --name '$TestMachine' --type template --script UpdateTestChangingInt --trigger-period-ms 500 --lock-trigger-period --confirm --confirm-target '$TestMachine' --llm-json
graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --file OnScan.txt --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' --llm-json
```
For writes, the CLI follows AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess"): script body and setting mutations prefer `IgObject.ConfigurableAttributes[...]`, then fall back to `Attributes[...]` only if the configurable collection does not expose the requested field. `object scripts set` writes the matching script body attribute via `IAttribute.SetValue(MxValue)`; a bare `--script <name>` defaults to `<name>.ExecuteText`; pass `--field <FieldName>` to target `DeclarationsText`, `StartupText`, `ShutdownText`, `OnScanText`, `OffScanText`, or `Expression`. `object scripts settings set` writes common script settings such as `TriggerPeriod`, `TriggerType`, and `Expression`; `--lock-trigger-period` / `--lock-trigger-type` apply `MxLockedInMe` so derived instances inherit the value on deploy. `MxElapsedTime` values are stored in 100 ns increments, so `--trigger-period-ms 500` writes `5,000,000`. `object scripts create` calls `AddExtensionPrimitive("ScriptExtension", <script>, true)` then initializes the body/settings in the same checkout flow.
For reads, `object scripts get --llm-json` and `object snapshot --llm-json` use the package-export fallback to surface body content. `object attribute value get` against a script text field may report `Supported: False` ("Attribute value is not exposed by this GRAccess attribute") even when the value is set — that's a `MxValueDetails` accessor-probe gap (the value persisted, the reader didn't surface it). Use `object scripts get` to verify post-write content. Round-trip evidence: `analysis/ide-edit-investigation/probe_setvalue/`.
### Area, engine, assignment, and I/O wrappers
```powershell
graccess area list --galaxy ZB --llm-json
graccess area create --galaxy ZB --template '$Area' --name Area_Test --confirm --confirm-target '$Area' --llm-json
graccess engine list --galaxy ZB --llm-json
graccess engine create --galaxy ZB --template '$AppEngine' --name AppEngine_Test --confirm --confirm-target '$AppEngine' --llm-json
graccess instance assign-area --galaxy ZB --name TestMachine_001 --area Area_Test --confirm --confirm-target TestMachine_001 --llm-json
graccess instance assign-engine --galaxy ZB --name TestMachine_001 --engine AppEngine_Test --confirm --confirm-target TestMachine_001 --llm-json
graccess instance assign-container --galaxy ZB --name TestMachine_001 --container ParentObject --confirm --confirm-target TestMachine_001 --llm-json
graccess io assign --galaxy ZB --name TestMachine_001 --attribute DeviceAddress --value D100 --confirm --confirm-target TestMachine_001 --llm-json
```
`object set --property area|host|container|toolset|security-group` resolves named GRAccess objects before falling back to string assignment.
`instance assign-engine` sets the host when the object model accepts a direct engine host. If the instance is already assigned to an area hosted by that engine, the command treats that state as successful because some GRAccess object families use the area as the assignable host.
### Extending templates and embedded objects
Use `template derive` to extend an existing template:
```powershell
graccess template derive --galaxy ZB --name '$gMachine' --type template --new-name '$MyMachine' --confirm --confirm-target '$gMachine' --llm-json
```
Use `--create-contained` when deriving or instantiating a template family whose embedded objects should come along:
```powershell
graccess template derive --galaxy ZB --name '$TestMachine' --type template --new-name '$MyMachine' --create-contained --confirm --confirm-target '$TestMachine' --llm-json
graccess template instantiate --galaxy ZB --name '$TestMachine' --type template --new-name TestMachine_021 --create-contained --confirm --confirm-target '$TestMachine' --llm-json
```
`template delete --force-option` defaults to `dontForceTemplateDelete`, which fails when instances exist instead of cascading. `instance delete --force-option` defaults to `undeployIfDeployed` so cleanup can remove deployed test instances after confirmation. `object uda add` and `object uda update` default `--category` to `MxCategoryWriteable_USC`; GRAccess builds that reject non-lockable UDA categories are normalized to `MxCategoryWriteable_USC_Lockable` at the COM call. Pass another valid `MxAttributeCategory` explicitly when a configure-only or calculated category is required. `object extension add/delete` pass typed extension calls to GRAccess; the legacy `AnalogLimitAlarm` extension type is accepted as an alias for the local `AnalogExtension` primitive type.
Single-object UDA, extension, attribute, script, and I/O mutations are atomic: the CLI checks out the object, applies the mutation, saves, and checks it back in. If the mutation or save fails after checkout, the CLI attempts `UndoCheckOut` before returning the original error. The explicit `object checkout/save/checkin` and `objects checkout/checkin` commands remain available for manual lifecycle operations.
Contained templates and child instances are edited as normal objects by targeting their own tagname:
```powershell
graccess object snapshot --galaxy ZB --name '$TestMachine.DelmiaReceiver' --type template --llm-json
graccess object snapshot --galaxy ZB --name DelmiaReceiver_001 --type instance --llm-json
graccess object checkout --galaxy ZB --name DelmiaReceiver_001 --type instance --confirm --confirm-target DelmiaReceiver_001
graccess object attribute value set --galaxy ZB --name DelmiaReceiver_001 --type instance --attribute DownloadPath --value 'C:\Recipes\001' --data-type string --confirm --confirm-target DelmiaReceiver_001 --llm-json
graccess object save --galaxy ZB --name DelmiaReceiver_001 --type instance --confirm --confirm-target DelmiaReceiver_001
graccess object checkin --galaxy ZB --name DelmiaReceiver_001 --type instance --comment 'Configure embedded receiver' --confirm --confirm-target DelmiaReceiver_001
```
Use `instance assign-container` or `object set --property container` only after confirming the object model allows moving/reparenting that child.
---
## Session Management
Session mode keeps a GRAccess connection open in a background daemon process, avoiding expensive reconnection on each CLI invocation.
### `session start`
Start a background session for a galaxy.
```
graccess session start --galaxy <name> --node <node> [--idle-timeout <minutes>]
```
| Option | Default | Description |
|---|---|---|
| `--galaxy`, `-g` | (required) | Galaxy name |
| `--node`, `-n` | (required) | GR node name. `.` is normalized to the local machine name. |
| `--idle-timeout` | 30 | Minutes of inactivity before auto-shutdown |
Behavior:
- Spawns a background daemon process
- Waits up to 90 seconds for the daemon to connect and become ready
- If a session is already running for this galaxy, reports the existing PID
- Daemon logs to `%LOCALAPPDATA%\ZB.MOM.WW.GRAccess.Cli\logs\daemon-{galaxy}.log`
### `session stop`
Stop a running session.
```
graccess session stop --galaxy <name>
```
Sends a shutdown signal to the daemon via named pipe. The daemon disconnects from the galaxy and exits.
### `session status`
Check if a session is running.
```
graccess session status --galaxy <name>
```
Output includes: galaxy name, node, PID, start time, and pipe name. Automatically cleans up stale session files from crashed daemons.
## Execution Modes
### Session mode (fast)
When a session is active, commands route through the daemon automatically:
```bash
graccess session start --galaxy MyGalaxy --node MyNode
graccess <command> --galaxy MyGalaxy [options] # routed via named pipe
graccess <command> --galaxy MyGalaxy [options] # reuses same connection
graccess session stop --galaxy MyGalaxy
```
### One-shot mode (no session)
Without an active session, commands open a direct connection, execute, and disconnect:
```bash
graccess <command> --galaxy MyGalaxy --node MyNode [options]
```
This is slower but requires no setup. The `--node` option is required in one-shot mode.
### Routing logic
The `CommandRouter` checks for an active session first:
1. Look up session info file at `%LOCALAPPDATA%\ZB.MOM.WW.GRAccess.Cli\sessions\{galaxy}.json`
2. Verify the daemon process is alive (PID check)
3. If alive, send command as JSON via named pipe `graccess-session-{galaxy}`
4. If no session, fall back to one-shot mode (requires `--node`)
Currently routed through the session daemon:
- `object list`
- `template list`
- `instance list`
- `object attributes`
- All other logged-in galaxy commands added under `galaxy`, `object`, `template`, `instance`, `objects`, `toolset`, `script-library`, `security`, and `settings`.
Pre-login galaxy administration commands remain one-shot because they operate before a galaxy session exists:
- `galaxy list`
- `galaxy create`
- `galaxy create-from-template`
- `galaxy delete`
## Expanded Command Surface
The CLI exposes the GRAccess operations listed in `graccess_operations.md` through these command families:
| Family | Commands |
|---|---|
| Galaxy | `galaxy info`, `galaxy sync`, `galaxy cdi-version`, `galaxy defaults get/set`, `galaxy backup/restore/migrate`, `galaxy import-objects`, `galaxy import-objects-ex`, `galaxy import-script-library`, `galaxy export-all`, `galaxy grload`, `galaxy create`, `galaxy create-from-template`, `galaxy delete` |
| Objects | `object get`, `object snapshot`, `object lineage`, `object children`, `object query-name`, `object query-condition`, `object query-multi`, `object extended-attributes`, `object help-url`, `object scripts list/get/set`, `object checkout/checkin/undo-checkout/save/unload/set` |
| Templates and instances | `template derive`, `template instantiate`, `template delete`, `instance delete/deploy/undeploy/upload`, `instance assign-area/assign-engine/assign-container` |
| Attributes, UDAs, extensions | `object attribute get/set/value get/value set/lock/security/buffer`, `object uda add/delete/rename/update`, `object extension add/delete/rename` |
| Bulk objects | `objects checkout/checkin/undo-checkout/deploy/undeploy/upload/delete/export/export-protected` |
| IDE wrappers and tooling | `capabilities`, `validate`, `batch`, `area list/create`, `engine list/create`, `io assign` |
| Toolsets/scripts/security/settings | `toolset list/tree/add/delete/rename/move`, `script-library list/add/import/export`, `security info/roles/users/groups/permissions`, `settings locale get`, `settings time-master get` |
Mutating commands require `--confirm`. Mutating commands also validate `--confirm-target` against the exact object, galaxy, bulk target list, or file target used by the command.
## IPC Protocol
Communication between CLI and daemon uses newline-delimited JSON over named pipes.
### Request format
```json
{"type":"execute","command":"<cmd>","subcommand":"<subcmd>","args":{"key":"value"}}
```
Request types: `execute`, `shutdown`, `status`
### Response format
```json
{"success":true,"output":"...","exitCode":0}
{"success":false,"error":"...","exitCode":1}
```
## Daemon Details
- **Pipe name**: `graccess-session-{galaxyname}` (lowercase)
- **Mutex**: `Global\graccess-session-{galaxyname}` (prevents duplicate daemons per galaxy)
- **Session file**: `%LOCALAPPDATA%\ZB.MOM.WW.GRAccess.Cli\sessions\{galaxy}.json`
- **Log file**: `%LOCALAPPDATA%\ZB.MOM.WW.GRAccess.Cli\logs\daemon-{galaxy}.log` (daily rolling, 7 day retention)
- **Idle timeout**: Checked every 30 seconds; daemon self-exits when exceeded
- **COM threading**: All GRAccess calls run on a dedicated STA thread with a Win32 message pump (`StaComThread`)