# Package Manager Deep Dive

Date: 2026-05-05

## Scope

This investigation focused on the local System Platform package manager path that sits below GRAccess and appears to be the missing write path for object-level script body fields and some settings.

Tools used:

- `TlbImp.exe` against registered COM/type libraries.
- .NET reflection over generated interop assemblies.
- Registry inspection of `HKCR\TypeLib`.
- Binary string extraction from package/runtime DLLs.
- `dumpbin.exe` on native package/runtime DLLs.
- Existing decompiled IDE wrapper notes under `analysis/ide-edit-investigation`.

Ghidra was available at `C:\Users\dohertj2\Desktop\focas\tools\ghidra_12.0.4_PUBLIC\support\analyzeHeadless.bat`, but the registered type libraries and embedded primitive XML exposed the actionable surface without needing a full native decompile.

## Registered components

### Galaxy package server

- Type library: `xxGalaxyPackageServer 1.0 Type Library`
- TypeLib GUID: `{74B9B2CA-05E6-4BC4-81C3-1CAFF652035D}`
- Binary: `C:\Program Files (x86)\ArchestrA\Framework\Bin\xxGalaxyPackageServer.dll`
- ProgID: `XxGalaxyPackageServer.GalaxyPackage`
- CLSID: `{F6C78E0D-8EA2-48B8-9592-6D9FB507DAD3}`
- Threading model: `Apartment`

### Configuration access component

- Type library: `ConfigurationAccessComponent 1.0 Type Library`
- TypeLib GUID: `{2539619B-EA9B-4035-9AE6-71421DA6C8FD}`
- Binary: `C:\Program Files (x86)\ArchestrA\Framework\Bin\ConfigurationAccessComponent.dll`
- Main COM server: `ConfigurationAccessComponent.ConfigurationAccessServer`
- CLSID from interop: `{55414847-A533-4642-8E92-76B191B24B87}`

### Script package

- Type library: `ScriptPackage`
- TypeLib GUID: `{8DF25C4D-C021-4080-849D-74A81D97D548}`
- Binary: `C:\Program Files (x86)\ArchestrA\Framework\Bin\ScriptPackage.dll`
- Package CLSID: `{A261929D-90AB-4217-9234-667A83756FFB}`
- Managed compiler helper: `C:\Program Files (x86)\ArchestrA\Framework\Bin\ScriptPackage.Net.dll`

### Script runtime primitive definition

The `ScriptExtension` primitive definition is embedded in:

- `C:\Program Files (x86)\ArchestrA\Framework\Bin\ScriptRuntime.dll`

Important embedded metadata:

- `DefaultInternalName`: `ScriptExtension`
- `Package_CLSID`: `{A261929D-90AB-4217-9234-667A83756FFB}`
- `Runtime_CLSID`: `{2B2616D6-98A0-4e94-ABAD-C75BB022E2D6}`
- Default `ExecutionGroup`: `Custom 1`

## Package manager surface

The important public COM interfaces are already in the local package server type library.

### `IGalaxyConfiguration` / `IGalaxyConfigurationV30`

This is the high-level object lifecycle and package access surface. Important methods:

- `GetIDFromObjectName(Namespace nSpace, string objectName) -> int`
- `GetGObjectInfoFromName(string name, Namespace nSpace)`
- `CheckOutObjects(int[] gObjectIds, int hint, IGObjectOperationStatus callback)`
- `CheckinObjects(int[] gObjectIds, string comment, int hint, IGObjectOperationStatus callback)`
- `UndoCheckoutObjects(int[] gObjectIds, int hint, bool override, IGObjectOperationStatus callback)`
- `GetObject(int objectId, ECODEMODULES codeModules, out object package, out ERRORCODE status, out string message, EPACKAGEPROP prop)`
- `GetObjectInternal(int objectId, ECODEMODULES code, bool editCheckRequired, out object package, out ERRORCODE status, out string message, EPACKAGEPROP prop)`
- `GetGalaxyConfigurationNet() -> object`
- `GetIGalaxy() -> object`

### `IPackageManager`

This is broader and includes lower-level attribute APIs. Important methods:

- `GetObject(...)`
- `GetObjectInternal(...)`
- `GetFullObjectByName(string name) -> object`
- `GetIDFromName(string name) -> int`
- `ReleaseFsObjectEditsession(int fsObjectId)`
- `GetObjectAttribute(Namespace, objectName, attributeName, EATTRIBUTEPROPERTY, MxValue, out result)`
- `InternalSetObjectAttributesEx(Namespace, objectName, EPACKAGETYPE, string[] attributeNames, EATTRIBUTEPROPERTY[] properties, MxValue[] values, bool saveObjectEvenErrorOccurs, out status, out statusMsg)`
- `InternalGetObjectAttributesEx(...)`

The `InternalSetObjectAttributesEx` method is a promising direct write path because it accepts object name, package type, attribute names, property ids, and MxValues without needing the IDE configuration access component.

### `IConfigurationEditorSite2`

This is the direct package editor site returned by package access calls. Important methods:

- `GetAttributeCookie(string attributeFullName) -> int`
- `GetAttribute(int cookie, short propertyId, MxValue value)`
- `SetAttribute(int cookie, short propertyId, MxValue value) -> bool`
- `Validate()`
- `Commit(out string errorString) -> EPACKAGEOPERATIONSTATUS`
- `IsReadOnly()`
- `SetSubscriberInfo(object subscription)`
- `AddExtensionPrimitive(...)`
- `DeleteExtensionPrimitive(...)`
- `RenameExtensionPrimitive(...)`
- `AddUDA(...)`
- `DeleteUDA(...)`
- `RenameUDA(...)`
- `UpdateUDA(...)`

For normal value writes, property id `10` is `EATTRIBUTEPROPERTY.idxAttribPropValue`.

### `IPrimitivePackageSite7`

The script package uses the generic primitive package site contract:

- `GetAttributeHandle(string attributeFullName) -> AttributeHandle`
- `GetAllCategoryAttributeHandle(string attributeFullName) -> AttributeHandle`
- `GetAttribute(AttributeHandle, MxValue)`
- `SetAttribute(AttributeHandle, MxValue)`
- `GetAttributeHandleEx(...)`
- `GetPrimitiveIdByInternalNameEx(...)`
- `GetPrimitiveAttributeIds(...)`
- dynamic attribute operations.

The script package itself exposes:

- `CoScriptPackageClass.Initialize(short primitiveId, IPrimitivePackageSite site)`
- `SetHandler(ref AttributeHandle, MxValue) -> string`
- `Validate() -> EPACKAGESTATUS`

There is no script-specific "SetScriptText" API in the script package typelib. It is generic attribute/handler based.

## ScriptExtension attribute facts

`ScriptRuntime.dll` embeds the actual script primitive attributes. The important ones:

- `ExecuteText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True`
- `DeclarationsText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True`
- `StartupText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True`
- `OnScanText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True`
- `OffScanText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True`
- `ShutdownText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True`
- `Expression`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True`
- `TriggerType`: `MxCustomEnum`, `Writeable_C_Lockable`, `CfgSethandler=True`
- `_TriggerTypeEnum`: values `WhileTrue`, `WhileFalse`, `OnTrue`, `OnFalse`, `DataChange`, `Periodic`
- `TriggerPeriod`: `MxElapsedTime`, `Writeable_C_Lockable`, `CfgSethandler=True`
- `DataChangeDeadband`: `MxDouble`, `Writeable_UC_Lockable`, `CfgSethandler=True`
- `RunsAsync`: `MxBoolean`, `Writeable_C_Lockable`, `CfgSethandler=True`
- `ExecuteTimeout.Limit`: `MxInteger`, `Writeable_C_Lockable`, `CfgSethandler=True`
- `ScriptExecutionGroup`: `MxCustomEnum`, `PackageOnly_Lockable`, `CfgSethandler=False`
- `ScriptOrder`: `MxInteger`, `PackageOnly_Lockable`, `CfgSethandler=False`

This confirms the current GRAccess `IAttribute.SetValue` path is not enough for script bodies. The body fields are package-only and cfg-sethandler driven.

## IDE wrapper behavior

The decompiled IDE wrapper creates `ConfigurationAccessComponent.ConfigurationAccessServer`, casts it to `IConfigurationEditor`, and initializes it with the object package site:

1. `configurationEditor.Initialize("", inObjectManage, this, readOnly)`
2. `inObjectManage.SetSubscriberInfo(configurationEditor)`
3. `inObjectManage.SetSubscriberInfo(this)`
4. Subscribes to `DConfigurationAccessEvents`.
5. Sets CAS data:
   - `ww:SupportedLocales`
   - `ww:IPackageManager`
   - `ww:Tagname`
   - `ww:ContainedName`
   - `ww:DerivedFrom`
   - `ww:KeepCheckedOut`

This is a full IDE editor-host path. It is probably more complete than direct attribute writes, but it requires implementing enough editor-site/event plumbing in the CLI.

## Feasible edit paths

### Path A: direct package editor site

Likely flow:

1. Use existing GRAccess login to obtain `IGalaxy`.
2. Call `IGalaxy.GetGalaxyConfiguration()` and cast the returned object to `IGalaxyConfiguration` or `IGalaxyConfigurationV30`.
3. Resolve object id with `GetIDFromObjectName(Namespace.AutomationObject, tagName)`.
4. Check out the object via existing GRAccess or `CheckOutObjects`.
5. Get the editable package with `GetObjectInternal(id, eEditorAndPackageCodeModules, true, out package, ...)` or `GetObject(...)`.
6. Cast the package object to `IConfigurationEditorSite2`.
7. Resolve `scriptName.ExecuteText` with `GetAttributeCookie`.
8. Create `MxValueClass` and call `PutString(newScriptBody)`.
9. Call `SetAttribute(cookie, idxAttribPropValue, mxValue)`.
10. Call `Validate()`.
11. Call `Commit(out error)`.
12. Check in or undo checkout and release edit session on failure.

Pros:

- Uses typed COM APIs.
- Avoids the full IDE configuration access server.
- Directly targets the package-only fields.

Risks:

- Needs live validation that the object returned by `GetObjectInternal` supports `IConfigurationEditorSite2` in this context.
- Needs confirmation that `SetAttribute` invokes the script package cfg set handler and recompiles `_Binary`.
- Needs correct custom enum MxValue creation for `TriggerType`.

### Path B: package manager internal batch set

Likely flow:

1. Get `IPackageManager` from the same object returned by `IGalaxy.GetGalaxyConfiguration()` or via package-manager support.
2. Check out object.
3. Call `InternalSetObjectAttributesEx` with:
   - `Namespace.AutomationObject`
   - object tagname
   - likely `EPACKAGETYPE.eBeingEditedPackage`
   - attribute names such as `UpdateTestChangingInt.ExecuteText`
   - property ids `[idxAttribPropValue]`
   - MxValues for the new data
   - `saveObjectEvenErrorOccurs=false` initially
4. Check status and status message.
5. Check in or undo checkout.

Pros:

- Smallest API surface.
- No editor host required.
- Best candidate for a CLI-first package write helper.

Risks:

- It is explicitly named `Internal`.
- It may require exact package type and checkout state.
- Unknown whether it invokes all cfg-sethandler compile side effects for script bodies.

### Path C: configuration access component

Likely flow:

1. Get editable object package as `IConfigurationEditorSite2`.
2. Instantiate `ConfigurationAccessComponent.ConfigurationAccessServer`.
3. Implement minimal `IBaseEditorSite` and event sink in the CLI.
4. Call `IConfigurationEditor.Initialize`.
5. Set the IDE wrapper's `ww:*` data entries.
6. Set `Data("ScriptName.ExecuteText", "Value") = body`.
7. Apply/commit through the editor site.

Pros:

- Closest to the IDE behavior.
- Most likely to run all validation/dirty/event plumbing.

Risks:

- Largest implementation.
- Requires COM connection-point/event hosting.
- More fragile in a headless CLI.

## Current CLI implication

The current `object scripts set` path writes through `IAttribute.SetValue`. That cannot persist the package-only body fields shown above.

The current `object scripts settings set --trigger-period-ms` should be more viable because `TriggerPeriod` is `Writeable_C_Lockable`, but it still goes through generic GRAccess `IAttribute.SetValue`.

The current `object scripts settings set --trigger-type` is suspicious because `TriggerType` is `MxCustomEnum`; the CLI currently creates an `MxValue` string for non-explicit data types. The package metadata says the valid values are backed by `_TriggerTypeEnum`, so a package-manager implementation should set `TriggerType` as a custom enum or verify that package `SetAttribute` accepts the string form.

## Recommended next implementation

Do not add more typelibs first. The useful typelibs already exist locally:

- `ArchestrA.GRAccess.dll`
- `xxGalaxyPackageServer.dll`
- `ConfigurationAccessComponent.dll`
- `ScriptPackage.dll`

The next improvement should add a small, isolated package-manager interop layer and a probe/edit helper:

1. Add minimal `[ComImport]` declarations for only the needed package-manager interfaces, or add a checked-in interop reference generated from `xxGalaxyPackageServer.dll`.
2. Bridge from `IGalaxy.GetGalaxyConfiguration()` to `IGalaxyConfigurationV30` / `IPackageManager`.
3. Implement a read-only probe first:
   - object id resolution
   - `GetObjectInternal`
   - cast checks for `IConfigurationEditorSite2`, `IObjectManage`, `IPackageManager`
   - `GetAttributeCookie` for script fields/settings
   - `GetAttribute` for `idxAttribPropValue`
4. Implement one guarded write path for `ExecuteText`.
5. Verify whether `_Binary`, `_ErrorMessage`, `_ErrorLine`, and `_ErrorColumn` update after commit. If they do not, switch from Path A/B to the CAS path.
6. Only then wire it into `object scripts set` and package-only script settings.

## Live validation blocker

Previous live GRAccess validation against the `ZB` galaxy failed because the local license was unavailable:

`cmdLicenseUnavailable (105); Feature 'wspdevstudio-iocount'`

The package-manager write path needs live validation after license access is restored because static metadata cannot prove whether script cfg-sethandler side effects fire on direct package writes.

