miravo 0.6.0 → 0.6.1

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.
Files changed (187) hide show
  1. package/LICENSE +199 -0
  2. package/README.md +7 -70
  3. package/bin/miravo.js +84 -0
  4. package/package.json +20 -36
  5. package/console-dist/assets/activity-DsWeRq4h.js +0 -1
  6. package/console-dist/assets/api-4AqKvsm9.js +0 -1
  7. package/console-dist/assets/arrow-right-Ea_UhTNi.js +0 -1
  8. package/console-dist/assets/arrow-up-down-DFSjzzl2.js +0 -1
  9. package/console-dist/assets/blueprint-card-classes-DBFCDy0M.js +0 -1
  10. package/console-dist/assets/box-C1WaoOMF.js +0 -1
  11. package/console-dist/assets/card-BjeP32PN.js +0 -1
  12. package/console-dist/assets/catalog-BJzyzbLR.js +0 -1
  13. package/console-dist/assets/catalog-DKjEm-c4.js +0 -1
  14. package/console-dist/assets/catalog-template-icons-BrUeHaCY.js +0 -1
  15. package/console-dist/assets/checkbox-DZlJXTE4.js +0 -1
  16. package/console-dist/assets/chunk-OE4NN4TA-CEjpfhS6.js +0 -3
  17. package/console-dist/assets/combobox-BGnuCYxO.js +0 -1
  18. package/console-dist/assets/control-CPhvwx9o.js +0 -1
  19. package/console-dist/assets/control-Cg2up9NA.js +0 -1
  20. package/console-dist/assets/copy-button-WkbNkMcd.js +0 -1
  21. package/console-dist/assets/createLucideIcon-Bwxw859K.js +0 -1
  22. package/console-dist/assets/deploy-tab-JeWDwPoZ.js +0 -1
  23. package/console-dist/assets/dist-BhbDML0a.js +0 -1
  24. package/console-dist/assets/dist-Cy8XTNFT.js +0 -2
  25. package/console-dist/assets/download-DQ6NH16U.js +0 -1
  26. package/console-dist/assets/engine-store-CrLnz692.js +0 -1
  27. package/console-dist/assets/events-CXm29Ok4.js +0 -1
  28. package/console-dist/assets/events-D01IxhnP.js +0 -1
  29. package/console-dist/assets/fira-code-cyrillic-ext-wght-normal-DhYMMuQd.woff2 +0 -0
  30. package/console-dist/assets/fira-code-cyrillic-wght-normal-Y3u8pIsh.woff2 +0 -0
  31. package/console-dist/assets/fira-code-greek-ext-wght-normal-wWus70Ix.woff2 +0 -0
  32. package/console-dist/assets/fira-code-greek-wght-normal-B2SviObF.woff2 +0 -0
  33. package/console-dist/assets/fira-code-latin-ext-wght-normal-Dvfvaomy.woff2 +0 -0
  34. package/console-dist/assets/fira-code-latin-wght-normal-CHoedHDv.woff2 +0 -0
  35. package/console-dist/assets/fira-code-symbols2-wght-normal-CE6EOz_n.woff2 +0 -0
  36. package/console-dist/assets/fleet-CfxbKXt9.js +0 -1
  37. package/console-dist/assets/fleet-lotMGVqe.js +0 -1
  38. package/console-dist/assets/format-kXTonpDJ.js +0 -1
  39. package/console-dist/assets/gauge-Ci8zNZlk.js +0 -1
  40. package/console-dist/assets/get-error-message-3Q5Q-2G5.js +0 -1
  41. package/console-dist/assets/index-CVPKSDvJ.js +0 -12
  42. package/console-dist/assets/index-DJkgDeun.css +0 -2
  43. package/console-dist/assets/input-BWEQFptx.js +0 -1
  44. package/console-dist/assets/instance-detail-C5IVwuh3.js +0 -1
  45. package/console-dist/assets/karla-latin-ext-wght-normal-Dq0rrjgs.woff2 +0 -0
  46. package/console-dist/assets/karla-latin-wght-normal-C3-ma4ov.woff2 +0 -0
  47. package/console-dist/assets/label-uephX5ZG.js +0 -1
  48. package/console-dist/assets/layers-CUsLQ1fq.js +0 -1
  49. package/console-dist/assets/login-C3RJli0m.js +0 -126
  50. package/console-dist/assets/models-B_UioWQ5.js +0 -1
  51. package/console-dist/assets/models-_IPlPiZ7.js +0 -1
  52. package/console-dist/assets/not-found-B8gAm08S.js +0 -1
  53. package/console-dist/assets/platform-targets-R3T-4eJ7.js +0 -1
  54. package/console-dist/assets/platforms-BWh0BQ3Y.js +0 -1
  55. package/console-dist/assets/platforms-DStNbtVc.js +0 -1
  56. package/console-dist/assets/proxy-CsnYqnPp.js +0 -9
  57. package/console-dist/assets/query-keys-BvtE7BMs.js +0 -1
  58. package/console-dist/assets/radio-CoHegOv5.js +0 -1
  59. package/console-dist/assets/rolldown-runtime-COnpUsM8.js +0 -1
  60. package/console-dist/assets/schibsted-grotesk-latin-ext-wght-normal-hsMS0n0O.woff2 +0 -0
  61. package/console-dist/assets/schibsted-grotesk-latin-wght-normal-Bb8VGrTG.woff2 +0 -0
  62. package/console-dist/assets/select-CeO4vP0G.js +0 -1
  63. package/console-dist/assets/select-display-items-CIzY8953.js +0 -1
  64. package/console-dist/assets/server-D9Kfn8fQ.js +0 -1
  65. package/console-dist/assets/shallow-6lGjQhtJ.js +0 -1
  66. package/console-dist/assets/tags-CiXOIHtY.js +0 -4
  67. package/console-dist/assets/tags-FQCXIxrP.js +0 -1
  68. package/console-dist/assets/tooltip-HGLF366Q.js +0 -1
  69. package/console-dist/assets/transition-nav-link-Df4Rk6vV.js +0 -1
  70. package/console-dist/assets/transition-types-CPlGtM8w.js +0 -1
  71. package/console-dist/assets/triangle-alert-adNk_sea.js +0 -1
  72. package/console-dist/assets/use-platform-stream-DGbK1k2L.js +0 -2
  73. package/console-dist/assets/use-transition-navigate-CCpcWXB0.js +0 -2
  74. package/console-dist/assets/validation-Blvud9_O.js +0 -1
  75. package/console-dist/assets/vendor-charts-CAZdaPlq.js +0 -60
  76. package/console-dist/assets/vendor-table-wd8JuMmT.js +0 -4
  77. package/console-dist/assets/vendor-ui-ByahnaY0.js +0 -1
  78. package/console-dist/favicon.svg +0 -37
  79. package/console-dist/index.html +0 -82
  80. package/console-dist/miravo-logo.png +0 -0
  81. package/src/__tests__/integration/executable-methods-runner.ts +0 -262
  82. package/src/__tests__/integration/executable-methods.integration.ts +0 -2348
  83. package/src/__tests__/integration/opcua-concurrent-clients.integration.ts +0 -314
  84. package/src/commands/adapter.ts +0 -249
  85. package/src/commands/apply.test.ts +0 -62
  86. package/src/commands/apply.ts +0 -216
  87. package/src/commands/cleanup.test.ts +0 -34
  88. package/src/commands/cleanup.ts +0 -201
  89. package/src/commands/cli-test-setup.ts +0 -91
  90. package/src/commands/export.test.ts +0 -51
  91. package/src/commands/export.ts +0 -404
  92. package/src/commands/fork.ts +0 -219
  93. package/src/commands/inject.test.ts +0 -57
  94. package/src/commands/inject.ts +0 -212
  95. package/src/commands/load.ts +0 -167
  96. package/src/commands/logs.ts +0 -173
  97. package/src/commands/models.test.ts +0 -162
  98. package/src/commands/models.ts +0 -338
  99. package/src/commands/pause.ts +0 -67
  100. package/src/commands/platforms.test.ts +0 -186
  101. package/src/commands/platforms.ts +0 -371
  102. package/src/commands/ps.ts +0 -116
  103. package/src/commands/remove.ts +0 -38
  104. package/src/commands/reset.ts +0 -44
  105. package/src/commands/rm.ts +0 -97
  106. package/src/commands/spawn.ts +0 -45
  107. package/src/commands/speed.ts +0 -40
  108. package/src/commands/start/adapter-config.ts +0 -157
  109. package/src/commands/start/catalog-le-dt.test.ts +0 -50
  110. package/src/commands/start/catalog-le-dt.ts +0 -187
  111. package/src/commands/start/catalog-model-schema.ts +0 -89
  112. package/src/commands/start/catalog-template-detail.ts +0 -54
  113. package/src/commands/start/daemon.ts +0 -255
  114. package/src/commands/start/display.ts +0 -150
  115. package/src/commands/start/engine-setup.ts +0 -247
  116. package/src/commands/start/events.ts +0 -126
  117. package/src/commands/start/index.ts +0 -897
  118. package/src/commands/start/protocol-plans.ts +0 -239
  119. package/src/commands/start/template-loader.ts +0 -197
  120. package/src/commands/start/template-resolver.ts +0 -185
  121. package/src/commands/start/types.ts +0 -73
  122. package/src/commands/start.test.ts +0 -480
  123. package/src/commands/status.test.ts +0 -20
  124. package/src/commands/status.ts +0 -470
  125. package/src/commands/stop.test.ts +0 -84
  126. package/src/commands/stop.ts +0 -169
  127. package/src/commands/templates.test.ts +0 -58
  128. package/src/commands/templates.ts +0 -277
  129. package/src/index.ts +0 -166
  130. package/src/lib/admin-auth.ts +0 -157
  131. package/src/lib/admin-client.test.ts +0 -51
  132. package/src/lib/admin-client.ts +0 -326
  133. package/src/lib/admin-platform-routes.ts +0 -1017
  134. package/src/lib/admin-platform-stream.test.ts +0 -434
  135. package/src/lib/admin-platform-stream.ts +0 -788
  136. package/src/lib/admin-responses.ts +0 -211
  137. package/src/lib/admin-server.test.ts +0 -781
  138. package/src/lib/admin-server.ts +0 -797
  139. package/src/lib/auth-store.ts +0 -87
  140. package/src/lib/daemon-registry.test.ts +0 -429
  141. package/src/lib/daemon-registry.ts +0 -343
  142. package/src/lib/le/le-apply.test.ts +0 -591
  143. package/src/lib/le/le-apply.ts +0 -832
  144. package/src/lib/le/le-auth.ts +0 -89
  145. package/src/lib/le/le-cleanup.test.ts +0 -168
  146. package/src/lib/le/le-cleanup.ts +0 -399
  147. package/src/lib/le/le-cli-helpers.ts +0 -77
  148. package/src/lib/le/le-client.ts +0 -184
  149. package/src/lib/le/le-concurrency.test.ts +0 -157
  150. package/src/lib/le/le-concurrency.ts +0 -78
  151. package/src/lib/le/le-connection-test.ts +0 -42
  152. package/src/lib/le/le-devicehub-properties.test.ts +0 -53
  153. package/src/lib/le/le-devicehub-properties.ts +0 -33
  154. package/src/lib/le/le-devicehub.ts +0 -213
  155. package/src/lib/le/le-digital-twins.ts +0 -285
  156. package/src/lib/le/le-drivers.ts +0 -23
  157. package/src/lib/le/le-endpoint.ts +0 -40
  158. package/src/lib/le/le-environments.test.ts +0 -73
  159. package/src/lib/le/le-environments.ts +0 -156
  160. package/src/lib/le/le-markers.ts +0 -87
  161. package/src/lib/le/le-plan.test.ts +0 -26
  162. package/src/lib/le/le-plan.ts +0 -109
  163. package/src/lib/le/le-platform-operations.test.ts +0 -198
  164. package/src/lib/le/le-platform-operations.ts +0 -340
  165. package/src/lib/le/le-static-model-schema.ts +0 -78
  166. package/src/lib/le/le-static-planner.test.ts +0 -255
  167. package/src/lib/le/le-static-planner.ts +0 -442
  168. package/src/lib/le/le-template-exporter.test.ts +0 -527
  169. package/src/lib/le/le-template-exporter.ts +0 -459
  170. package/src/lib/le/le-value-types.ts +0 -23
  171. package/src/lib/mqtt-client.ts +0 -115
  172. package/src/lib/parse.test.ts +0 -123
  173. package/src/lib/parse.ts +0 -87
  174. package/src/lib/resolve-simulation.test.ts +0 -118
  175. package/src/lib/resolve-simulation.ts +0 -181
  176. package/src/lib/session-store.ts +0 -110
  177. package/src/lib/shutdown.test.ts +0 -562
  178. package/src/lib/shutdown.ts +0 -621
  179. package/src/lib/template-category.test.ts +0 -71
  180. package/src/lib/template-category.ts +0 -72
  181. package/src/lib/template-locator.ts +0 -55
  182. package/src/lib/template-utils.ts +0 -45
  183. package/src/lib/ws-hub.ts +0 -281
  184. package/src/logger.test.ts +0 -172
  185. package/src/logger.ts +0 -402
  186. package/src/output.test.ts +0 -64
  187. package/src/output.ts +0 -160
@@ -1,213 +0,0 @@
1
- /**
2
- * Litmus Edge DeviceHub GraphQL operations.
3
- */
4
-
5
- import type { LEClient } from "./le-client.ts";
6
-
7
- // --- Types ---
8
-
9
- /** InputItem for GraphQL mutations (Key/Value). */
10
- export interface LEItem {
11
- readonly Key: string;
12
- readonly Value: string;
13
- }
14
-
15
- /** Item returned from GraphQL queries (Name/Value). */
16
- export interface LEItemOutput {
17
- readonly Name: string;
18
- readonly Value: string;
19
- }
20
-
21
- interface LEDriver {
22
- readonly ID: string;
23
- readonly Name: string;
24
- }
25
-
26
- interface LEDriverGroup {
27
- readonly ID: string;
28
- readonly Name: string;
29
- readonly Drivers: readonly LEDriver[] | null;
30
- }
31
-
32
- export interface LEDevice {
33
- readonly ID: string;
34
- readonly Name: string;
35
- readonly DriverID: string;
36
- readonly Properties: readonly LEItemOutput[];
37
- readonly MetaData: readonly LEItemOutput[];
38
- readonly Description: string;
39
- }
40
-
41
- interface LERegister {
42
- readonly ID: string;
43
- readonly DeviceID: string;
44
- readonly Name: string;
45
- readonly TagName: string;
46
- readonly ValueType: string;
47
- readonly Properties: readonly LEItemOutput[];
48
- readonly MetaData: readonly LEItemOutput[];
49
- readonly Description: string;
50
- }
51
-
52
- // --- Queries ---
53
-
54
- const LIST_DRIVER_GROUPS = `query {
55
- ListDriverGroups(input: {}) {
56
- ID Name
57
- Drivers { ID Name }
58
- }
59
- }`;
60
-
61
- const LIST_DEVICES = `query {
62
- ListDevices { ID Name DriverID Properties { Name Value } MetaData { Name Value } Description }
63
- }`;
64
-
65
- const LIST_REGISTERS = `query ListRegisters($deviceId: UUID!) {
66
- ListRegisters(input: { DeviceID: $deviceId, Limit: 1000 }) {
67
- Registers { ID DeviceID Name TagName ValueType Properties { Name Value } MetaData { Name Value } Description }
68
- }
69
- }`;
70
-
71
- // --- Mutations ---
72
-
73
- const CREATE_DEVICE = `mutation CreateDevice($input: CreateDeviceRequest!) {
74
- CreateDevice(input: $input) { ID Name DriverID Properties { Name Value } MetaData { Name Value } Description }
75
- }`;
76
-
77
- const UPDATE_DEVICE = `mutation UpdateDevice($input: UpdateDeviceRequest!) {
78
- UpdateDevice(input: $input) { ID Name DriverID Properties { Name Value } MetaData { Name Value } Description }
79
- }`;
80
-
81
- const DELETE_DEVICES = `mutation DeleteDevices($ids: [UUID!]!) {
82
- DeleteDevices(input: { IDs: $ids })
83
- }`;
84
-
85
- const CREATE_REGISTERS = `mutation CreateRegisters($input: CreateRegistersRequest!) {
86
- CreateRegisters(input: $input) { ID DeviceID Name TagName ValueType Properties { Name Value } MetaData { Name Value } Description }
87
- }`;
88
-
89
- const DELETE_REGISTERS = `mutation DeleteRegisters($ids: [UUID!]!) {
90
- DeleteRegisters(input: { IDs: $ids })
91
- }`;
92
-
93
- const DELETE_DEVICE_REGISTERS = `mutation DeleteDeviceRegisters($deviceId: UUID!) {
94
- DeleteDeviceRegisters(input: { DevID: $deviceId })
95
- }`;
96
-
97
- // --- API ---
98
-
99
- export class LEDeviceHub {
100
- constructor(private readonly client: LEClient) {}
101
-
102
- async listDriverGroups(): Promise<readonly LEDriverGroup[]> {
103
- const data = await this.client.gql<{ ListDriverGroups: LEDriverGroup[] }>(
104
- "/devicehub/v2",
105
- LIST_DRIVER_GROUPS,
106
- );
107
- return data.ListDriverGroups;
108
- }
109
-
110
- async resolveDriverByName(name: string): Promise<LEDriver> {
111
- const groups = await this.listDriverGroups();
112
- for (const group of groups) {
113
- if (!group.Drivers) continue;
114
- for (const driver of group.Drivers) {
115
- if (driver.Name === name) return driver;
116
- }
117
- }
118
- throw new Error(
119
- `LE driver "${name}" not found. Available drivers: ${groups.flatMap((g) => g.Drivers?.map((d) => d.Name) ?? []).join(", ")}`,
120
- );
121
- }
122
-
123
- async listDevices(): Promise<readonly LEDevice[]> {
124
- const data = await this.client.gql<{ ListDevices: LEDevice[] | null }>(
125
- "/devicehub/v2",
126
- LIST_DEVICES,
127
- );
128
- return data.ListDevices ?? [];
129
- }
130
-
131
- async listRegisters(deviceId: string): Promise<readonly LERegister[]> {
132
- const data = await this.client.gql<{
133
- ListRegisters: { Registers: LERegister[] } | null;
134
- }>("/devicehub/v2", LIST_REGISTERS, { deviceId });
135
- return data.ListRegisters?.Registers ?? [];
136
- }
137
-
138
- async createDevice(input: {
139
- DriverID: string;
140
- Name: string;
141
- Properties: readonly LEItem[];
142
- MetaData?: readonly LEItem[];
143
- Description?: string;
144
- }): Promise<LEDevice> {
145
- const data = await this.client.gql<{ CreateDevice: LEDevice }>("/devicehub/v2", CREATE_DEVICE, {
146
- input: {
147
- ...input,
148
- Description: input.Description ?? "",
149
- AliasTopics: true,
150
- },
151
- });
152
- return data.CreateDevice;
153
- }
154
-
155
- async updateDevice(input: {
156
- ID: string;
157
- Name: string;
158
- Properties: readonly LEItem[];
159
- MetaData?: readonly LEItem[];
160
- Description?: string;
161
- }): Promise<LEDevice> {
162
- const data = await this.client.gql<{ UpdateDevice: LEDevice }>("/devicehub/v2", UPDATE_DEVICE, {
163
- input: {
164
- ...input,
165
- Description: input.Description ?? "",
166
- AliasTopics: true,
167
- },
168
- });
169
- return data.UpdateDevice;
170
- }
171
-
172
- async deleteDevices(ids: readonly string[]): Promise<void> {
173
- if (ids.length === 0) return;
174
- await this.client.gql("/devicehub/v2", DELETE_DEVICES, { ids });
175
- }
176
-
177
- async createRegisters(
178
- registers: readonly {
179
- DeviceID: string;
180
- Name: string;
181
- TagName: string;
182
- ValueType: string;
183
- Properties: readonly LEItem[];
184
- MetaData?: readonly LEItem[];
185
- Description?: string;
186
- }[],
187
- ): Promise<readonly LERegister[]> {
188
- if (registers.length === 0) return [];
189
- const data = await this.client.gql<{ CreateRegisters: LERegister[] }>(
190
- "/devicehub/v2",
191
- CREATE_REGISTERS,
192
- {
193
- input: {
194
- Registers: registers.map((r) => ({
195
- ...r,
196
- Description: r.Description ?? "",
197
- PublishCoV: false,
198
- })),
199
- },
200
- },
201
- );
202
- return data.CreateRegisters;
203
- }
204
-
205
- async deleteRegisters(ids: readonly string[]): Promise<void> {
206
- if (ids.length === 0) return;
207
- await this.client.gql("/devicehub/v2", DELETE_REGISTERS, { ids });
208
- }
209
-
210
- async deleteDeviceRegisters(deviceId: string): Promise<void> {
211
- await this.client.gql("/devicehub/v2", DELETE_DEVICE_REGISTERS, { deviceId });
212
- }
213
- }
@@ -1,285 +0,0 @@
1
- /**
2
- * Litmus Edge Digital Twins GraphQL operations.
3
- */
4
-
5
- import type { LEClient } from "./le-client.ts";
6
-
7
- // --- Types ---
8
-
9
- export interface LEModel {
10
- readonly ID: string;
11
- readonly Name: string;
12
- readonly Description: string;
13
- readonly Version: number;
14
- }
15
-
16
- export interface LEInstance {
17
- readonly ID: string;
18
- readonly ModelID: string;
19
- readonly Name: string;
20
- readonly Topic: string;
21
- readonly Interval: number;
22
- readonly FlatHierarchy: boolean | null;
23
- readonly Version: number;
24
- }
25
-
26
- interface LEStaticAttribute {
27
- readonly ID: string;
28
- readonly ModelID: string | null;
29
- readonly InstanceID: string | null;
30
- readonly Key: string;
31
- readonly Value: string;
32
- }
33
-
34
- interface LEParameter {
35
- readonly ID: string;
36
- readonly OwnerID: string;
37
- readonly Name: string;
38
- readonly Value: string;
39
- }
40
-
41
- // --- Queries ---
42
-
43
- const LIST_MODELS = `query { ListModels { ID Name Description Version } }`;
44
-
45
- const LIST_INSTANCES = `query ListInstances($input: ListInstancesRequest) {
46
- ListInstances(input: $input) { ID ModelID Name Topic Interval FlatHierarchy Version }
47
- }`;
48
-
49
- const LIST_STATIC_ATTRIBUTES = `query ListStaticAttributes($modelId: UUID, $instanceId: UUID) {
50
- ListStaticAttributes(input: { ModelID: $modelId, InstanceID: $instanceId }) {
51
- ID ModelID InstanceID Key Value
52
- }
53
- }`;
54
-
55
- const LIST_PARAMETERS = `query ListParameters($ownerId: UUID) {
56
- ListParameters(input: { OwnerID: $ownerId }) { ID OwnerID Name Value }
57
- }`;
58
-
59
- // --- Mutations ---
60
-
61
- const IMPORT_MODEL = `mutation ImportModel($input: Any!) {
62
- ImportModel(input: $input)
63
- }`;
64
-
65
- const DELETE_MODEL = `mutation DeleteModel($id: UUID) {
66
- DeleteModel(input: { ID: $id })
67
- }`;
68
-
69
- const CREATE_INSTANCE = `mutation CreateInstance($input: CreateInstanceRequest!) {
70
- CreateInstance(input: $input) { ID ModelID Name Topic Interval FlatHierarchy Version }
71
- }`;
72
-
73
- const UPDATE_INSTANCE = `mutation UpdateInstance($input: UpdateInstanceRequest!) {
74
- UpdateInstance(input: $input) { ID ModelID Name Topic Interval FlatHierarchy Version }
75
- }`;
76
-
77
- const DELETE_INSTANCE = `mutation DeleteInstance($id: UUID) {
78
- DeleteInstance(input: { ID: $id })
79
- }`;
80
-
81
- const TRANSACTIONAL_INSTANCE_UPDATE = `mutation TransactionalInstanceUpdate($input: TransactionalInstanceUpdateRequest!) {
82
- TransactionalInstanceUpdate(input: $input)
83
- }`;
84
-
85
- const SET_INSTANCE_STATE = `mutation SetInstanceState($input: SetInstanceStateRequest!) {
86
- SetInstanceState(input: $input)
87
- }`;
88
-
89
- const CREATE_STATIC_ATTRIBUTES = `mutation CreateStaticAttributes($input: CreateStaticAttributesRequest!) {
90
- CreateStaticAttributes(input: $input) { ID ModelID InstanceID Key Value }
91
- }`;
92
-
93
- const UPDATE_STATIC_ATTRIBUTES = `mutation UpdateStaticAttributes($input: UpdateStaticAttributesRequest!) {
94
- UpdateStaticAttributes(input: $input) { ID ModelID InstanceID Key Value }
95
- }`;
96
-
97
- interface LETransactionalInstanceUpdateInput {
98
- readonly instanceID: string;
99
- readonly updateStaticAttributes?: {
100
- readonly Items: readonly {
101
- readonly ID: string;
102
- readonly ModelID?: string | null;
103
- readonly InstanceID?: string | null;
104
- readonly Key: string;
105
- readonly Value: string;
106
- }[];
107
- };
108
- readonly updateDynamicAttributes?: { readonly Items: readonly unknown[] };
109
- readonly setParameters?: {
110
- readonly Items: readonly {
111
- readonly ID?: string;
112
- readonly Name: string;
113
- readonly Value: string;
114
- }[];
115
- };
116
- }
117
-
118
- // --- API ---
119
-
120
- export class LEDigitalTwins {
121
- constructor(private readonly client: LEClient) {}
122
-
123
- private async transactionalInstanceUpdate(
124
- input: LETransactionalInstanceUpdateInput,
125
- ): Promise<void> {
126
- await this.client.gql("/digital-twins/v2", TRANSACTIONAL_INSTANCE_UPDATE, { input });
127
- }
128
-
129
- async listModels(): Promise<readonly LEModel[]> {
130
- const data = await this.client.gql<{ ListModels: LEModel[] }>("/digital-twins/v2", LIST_MODELS);
131
- return data.ListModels;
132
- }
133
-
134
- async listInstances(modelId?: string): Promise<readonly LEInstance[]> {
135
- const data = await this.client.gql<{ ListInstances: LEInstance[] }>(
136
- "/digital-twins/v2",
137
- LIST_INSTANCES,
138
- { input: modelId ? { ModelID: modelId } : {} },
139
- );
140
- return data.ListInstances;
141
- }
142
-
143
- async listStaticAttributes(opts: {
144
- modelId?: string | undefined;
145
- instanceId?: string | undefined;
146
- }): Promise<readonly LEStaticAttribute[]> {
147
- const data = await this.client.gql<{ ListStaticAttributes: LEStaticAttribute[] }>(
148
- "/digital-twins/v2",
149
- LIST_STATIC_ATTRIBUTES,
150
- { modelId: opts.modelId ?? null, instanceId: opts.instanceId ?? null },
151
- );
152
- return data.ListStaticAttributes;
153
- }
154
-
155
- async listParameters(ownerId: string): Promise<readonly LEParameter[]> {
156
- const data = await this.client.gql<{ ListParameters: LEParameter[] }>(
157
- "/digital-twins/v2",
158
- LIST_PARAMETERS,
159
- { ownerId },
160
- );
161
- return data.ListParameters;
162
- }
163
-
164
- async importModel(modelJson: unknown): Promise<boolean> {
165
- const data = await this.client.gql<{ ImportModel: boolean }>(
166
- "/digital-twins/v2",
167
- IMPORT_MODEL,
168
- { input: modelJson },
169
- );
170
- return data.ImportModel;
171
- }
172
-
173
- async deleteModel(id: string): Promise<void> {
174
- await this.client.gql("/digital-twins/v2", DELETE_MODEL, { id });
175
- }
176
-
177
- async createInstance(input: {
178
- ModelID: string;
179
- Name: string;
180
- Topic: string;
181
- Interval: number;
182
- FlatHierarchy?: boolean;
183
- }): Promise<LEInstance> {
184
- const data = await this.client.gql<{ CreateInstance: LEInstance }>(
185
- "/digital-twins/v2",
186
- CREATE_INSTANCE,
187
- { input },
188
- );
189
- return data.CreateInstance;
190
- }
191
-
192
- async updateInstance(input: {
193
- ID: string;
194
- ModelID: string;
195
- Name: string;
196
- Topic: string;
197
- Interval: number;
198
- FlatHierarchy?: boolean;
199
- }): Promise<LEInstance> {
200
- const data = await this.client.gql<{ UpdateInstance: LEInstance }>(
201
- "/digital-twins/v2",
202
- UPDATE_INSTANCE,
203
- { input },
204
- );
205
- return data.UpdateInstance;
206
- }
207
-
208
- async deleteInstance(id: string): Promise<void> {
209
- await this.client.gql("/digital-twins/v2", DELETE_INSTANCE, { id });
210
- }
211
-
212
- async setInstanceParameters(
213
- instanceId: string,
214
- params: readonly { Name: string; Value: string }[],
215
- ): Promise<void> {
216
- // TransactionalInstanceUpdate requires SetParameterRequest.ID to be set.
217
- // LE auto-creates default parameters when an instance is created, so the
218
- // lookup is always needed — even for brand-new instances.
219
- const existing = await this.listParameters(instanceId);
220
- const byName = new Map(existing.map((p) => [p.Name, p]));
221
-
222
- await this.transactionalInstanceUpdate({
223
- instanceID: instanceId,
224
- updateStaticAttributes: { Items: [] },
225
- updateDynamicAttributes: { Items: [] },
226
- setParameters: {
227
- Items: params.map((p) => {
228
- const existingParamId = byName.get(p.Name)?.ID;
229
- return existingParamId
230
- ? { ID: existingParamId, Name: p.Name, Value: p.Value }
231
- : { Name: p.Name, Value: p.Value };
232
- }),
233
- },
234
- });
235
- }
236
-
237
- async updateInstanceStaticAttributes(
238
- instanceId: string,
239
- attrs: readonly {
240
- ID: string;
241
- ModelID?: string | null;
242
- InstanceID?: string | null;
243
- Key: string;
244
- Value: string;
245
- }[],
246
- ): Promise<void> {
247
- if (attrs.length === 0) return;
248
- await this.transactionalInstanceUpdate({
249
- instanceID: instanceId,
250
- updateStaticAttributes: { Items: attrs },
251
- updateDynamicAttributes: { Items: [] },
252
- setParameters: { Items: [] },
253
- });
254
- }
255
-
256
- async setInstanceState(instanceId: string, enabled: boolean): Promise<void> {
257
- await this.client.gql("/digital-twins/v2", SET_INSTANCE_STATE, {
258
- input: { ID: instanceId, State: enabled },
259
- });
260
- }
261
-
262
- async createStaticAttributes(
263
- attrs: readonly { ModelID?: string; InstanceID?: string; Key: string; Value: string }[],
264
- ): Promise<readonly LEStaticAttribute[]> {
265
- if (attrs.length === 0) return [];
266
- const data = await this.client.gql<{ CreateStaticAttributes: LEStaticAttribute[] }>(
267
- "/digital-twins/v2",
268
- CREATE_STATIC_ATTRIBUTES,
269
- { input: { Items: attrs } },
270
- );
271
- return data.CreateStaticAttributes;
272
- }
273
-
274
- async updateStaticAttributes(
275
- attrs: readonly { ID: string; Key: string; Value: string }[],
276
- ): Promise<readonly LEStaticAttribute[]> {
277
- if (attrs.length === 0) return [];
278
- const data = await this.client.gql<{ UpdateStaticAttributes: LEStaticAttribute[] }>(
279
- "/digital-twins/v2",
280
- UPDATE_STATIC_ATTRIBUTES,
281
- { input: { Items: attrs } },
282
- );
283
- return data.UpdateStaticAttributes;
284
- }
285
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * LE driver resolution by stable name and ID.
3
- */
4
-
5
- import type { ProtocolName } from "@miravo/shared";
6
-
7
- /** Stable driver names per protocol. */
8
- export const LE_DRIVER_NAMES: Record<ProtocolName, string> = {
9
- opcua: "OPC UA Client Advanced",
10
- modbus: "Modbus TCP",
11
- mqtt: "MQTT Client",
12
- };
13
-
14
- /** Fixed DeviceHub driver UUIDs per protocol for LE 4.x. */
15
- export const LE_DRIVER_IDS: Record<ProtocolName, string> = {
16
- opcua: "2372E68F-BA1B-46E9-BC07-E92CA006B377",
17
- modbus: "2AF1FA08-D638-11E9-BB65-2A2AE2DBCCE4",
18
- mqtt: "236E3A53-E133-48D6-96F4-D95E61C7592A",
19
- };
20
-
21
- export function getDriverName(protocol: ProtocolName): string {
22
- return LE_DRIVER_NAMES[protocol];
23
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * LE endpoint resolution.
3
- *
4
- * Determines the host:port that LE devices should use to reach
5
- * Miravo's protocol servers.
6
- */
7
-
8
- import type { ProtocolName } from "@miravo/shared";
9
- import { DEFAULT_MODBUS_PORT, DEFAULT_MQTT_PORT, DEFAULT_OPCUA_PORT } from "@miravo/shared";
10
-
11
- interface EndpointInfo {
12
- readonly host: string;
13
- readonly port: number;
14
- }
15
-
16
- export function resolveProtocolPort(
17
- protocol: ProtocolName,
18
- protocolConfig?: {
19
- mqtt?: { port?: number };
20
- opcua?: { port?: number };
21
- modbus?: { port?: number };
22
- },
23
- ): number {
24
- switch (protocol) {
25
- case "mqtt":
26
- return protocolConfig?.mqtt?.port ?? DEFAULT_MQTT_PORT;
27
- case "opcua":
28
- return protocolConfig?.opcua?.port ?? DEFAULT_OPCUA_PORT;
29
- case "modbus":
30
- return protocolConfig?.modbus?.port ?? DEFAULT_MODBUS_PORT;
31
- default: {
32
- const _never: never = protocol;
33
- throw new Error(`Unknown protocol: ${_never}`);
34
- }
35
- }
36
- }
37
-
38
- export function formatEndpoint(info: EndpointInfo): string {
39
- return `${info.host}:${info.port}`;
40
- }
@@ -1,73 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { mkdtemp, rm } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import {
6
- addEnvironment,
7
- environmentsFilePath,
8
- getDefaultEnvironment,
9
- loadEnvironments,
10
- operationsFilePath,
11
- platformsDirPath,
12
- } from "./le-environments.ts";
13
-
14
- let testHome = "";
15
- let prevMiravoHome: string | undefined;
16
-
17
- beforeEach(async () => {
18
- prevMiravoHome = Bun.env["MIRAVO_HOME"];
19
- testHome = await mkdtemp(join(tmpdir(), "miravo-platforms-test-"));
20
- Bun.env["MIRAVO_HOME"] = testHome;
21
- });
22
-
23
- afterEach(async () => {
24
- if (prevMiravoHome === undefined) {
25
- delete Bun.env["MIRAVO_HOME"];
26
- } else {
27
- Bun.env["MIRAVO_HOME"] = prevMiravoHome;
28
- }
29
- await rm(testHome, { recursive: true, force: true });
30
- });
31
-
32
- describe("le-environments storage layout", () => {
33
- test("writes environments under ~/.miravo/platforms/environments.json", async () => {
34
- await addEnvironment({
35
- platformId: "litmus-edge",
36
- label: "Production LE",
37
- hostname: "10.1.2.3",
38
- apiToken: "token-1",
39
- sourceHostname: "host.docker.internal",
40
- });
41
-
42
- const envs = await loadEnvironments();
43
- expect(envs).toHaveLength(1);
44
- expect(envs[0]?.label).toBe("Production LE");
45
-
46
- const envFileExists = await Bun.file(environmentsFilePath()).exists();
47
- const oldPathExists = await Bun.file(`${testHome}/platforms.json`).exists();
48
- expect(platformsDirPath()).toBe(`${testHome}/platforms`);
49
- expect(envFileExists).toBe(true);
50
- expect(oldPathExists).toBe(false);
51
- });
52
-
53
- test("default environment is first entry in environments file", async () => {
54
- await addEnvironment({
55
- platformId: "litmus-edge",
56
- label: "First",
57
- hostname: "10.0.0.1",
58
- apiToken: "first-token",
59
- sourceHostname: "host.docker.internal",
60
- });
61
- await addEnvironment({
62
- platformId: "litmus-edge",
63
- label: "Second",
64
- hostname: "10.0.0.2",
65
- apiToken: "second-token",
66
- sourceHostname: "host.docker.internal",
67
- });
68
-
69
- const defaultEnv = await getDefaultEnvironment();
70
- expect(defaultEnv?.label).toBe("First");
71
- expect(operationsFilePath()).toBe(`${testHome}/platforms/operations.jsonl`);
72
- });
73
- });