miravo 0.5.0 → 0.6.0

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 (161) hide show
  1. package/README.md +1 -1
  2. package/console-dist/assets/activity-DsWeRq4h.js +1 -0
  3. package/console-dist/assets/api-4AqKvsm9.js +1 -0
  4. package/console-dist/assets/arrow-right-Ea_UhTNi.js +1 -0
  5. package/console-dist/assets/arrow-up-down-DFSjzzl2.js +1 -0
  6. package/console-dist/assets/blueprint-card-classes-DBFCDy0M.js +1 -0
  7. package/console-dist/assets/box-C1WaoOMF.js +1 -0
  8. package/console-dist/assets/card-BjeP32PN.js +1 -0
  9. package/console-dist/assets/catalog-BJzyzbLR.js +1 -0
  10. package/console-dist/assets/catalog-DKjEm-c4.js +1 -0
  11. package/console-dist/assets/catalog-template-icons-BrUeHaCY.js +1 -0
  12. package/console-dist/assets/checkbox-DZlJXTE4.js +1 -0
  13. package/console-dist/assets/chunk-OE4NN4TA-CEjpfhS6.js +3 -0
  14. package/console-dist/assets/combobox-BGnuCYxO.js +1 -0
  15. package/console-dist/assets/control-CPhvwx9o.js +1 -0
  16. package/console-dist/assets/control-Cg2up9NA.js +1 -0
  17. package/console-dist/assets/copy-button-WkbNkMcd.js +1 -0
  18. package/console-dist/assets/createLucideIcon-Bwxw859K.js +1 -0
  19. package/console-dist/assets/deploy-tab-JeWDwPoZ.js +1 -0
  20. package/console-dist/assets/dist-BhbDML0a.js +1 -0
  21. package/console-dist/assets/dist-Cy8XTNFT.js +2 -0
  22. package/console-dist/assets/download-DQ6NH16U.js +1 -0
  23. package/console-dist/assets/engine-store-CrLnz692.js +1 -0
  24. package/console-dist/assets/events-CXm29Ok4.js +1 -0
  25. package/console-dist/assets/events-D01IxhnP.js +1 -0
  26. package/console-dist/assets/fleet-CfxbKXt9.js +1 -0
  27. package/console-dist/assets/fleet-lotMGVqe.js +1 -0
  28. package/console-dist/assets/format-kXTonpDJ.js +1 -0
  29. package/console-dist/assets/gauge-Ci8zNZlk.js +1 -0
  30. package/console-dist/assets/get-error-message-3Q5Q-2G5.js +1 -0
  31. package/console-dist/assets/index-CVPKSDvJ.js +12 -0
  32. package/console-dist/assets/index-DJkgDeun.css +2 -0
  33. package/console-dist/assets/input-BWEQFptx.js +1 -0
  34. package/console-dist/assets/instance-detail-C5IVwuh3.js +1 -0
  35. package/console-dist/assets/label-uephX5ZG.js +1 -0
  36. package/console-dist/assets/layers-CUsLQ1fq.js +1 -0
  37. package/console-dist/assets/login-C3RJli0m.js +126 -0
  38. package/console-dist/assets/models-B_UioWQ5.js +1 -0
  39. package/console-dist/assets/models-_IPlPiZ7.js +1 -0
  40. package/console-dist/assets/not-found-B8gAm08S.js +1 -0
  41. package/console-dist/assets/platform-targets-R3T-4eJ7.js +1 -0
  42. package/console-dist/assets/platforms-BWh0BQ3Y.js +1 -0
  43. package/console-dist/assets/platforms-DStNbtVc.js +1 -0
  44. package/console-dist/assets/proxy-CsnYqnPp.js +9 -0
  45. package/console-dist/assets/query-keys-BvtE7BMs.js +1 -0
  46. package/console-dist/assets/radio-CoHegOv5.js +1 -0
  47. package/console-dist/assets/rolldown-runtime-COnpUsM8.js +1 -0
  48. package/console-dist/assets/select-CeO4vP0G.js +1 -0
  49. package/console-dist/assets/select-display-items-CIzY8953.js +1 -0
  50. package/console-dist/assets/server-D9Kfn8fQ.js +1 -0
  51. package/console-dist/assets/shallow-6lGjQhtJ.js +1 -0
  52. package/console-dist/assets/tags-CiXOIHtY.js +4 -0
  53. package/console-dist/assets/tags-FQCXIxrP.js +1 -0
  54. package/console-dist/assets/tooltip-HGLF366Q.js +1 -0
  55. package/console-dist/assets/transition-nav-link-Df4Rk6vV.js +1 -0
  56. package/console-dist/assets/transition-types-CPlGtM8w.js +1 -0
  57. package/console-dist/assets/triangle-alert-adNk_sea.js +1 -0
  58. package/console-dist/assets/use-platform-stream-DGbK1k2L.js +2 -0
  59. package/console-dist/assets/use-transition-navigate-CCpcWXB0.js +2 -0
  60. package/console-dist/assets/validation-Blvud9_O.js +1 -0
  61. package/console-dist/assets/vendor-charts-CAZdaPlq.js +60 -0
  62. package/console-dist/assets/vendor-table-wd8JuMmT.js +4 -0
  63. package/console-dist/assets/vendor-ui-ByahnaY0.js +1 -0
  64. package/console-dist/index.html +70 -2
  65. package/package.json +9 -9
  66. package/src/__tests__/integration/executable-methods-runner.ts +7 -6
  67. package/src/__tests__/integration/executable-methods.integration.ts +214 -122
  68. package/src/__tests__/integration/opcua-concurrent-clients.integration.ts +314 -0
  69. package/src/commands/adapter.ts +180 -7
  70. package/src/commands/apply.test.ts +62 -0
  71. package/src/commands/apply.ts +216 -0
  72. package/src/commands/cleanup.test.ts +34 -0
  73. package/src/commands/cleanup.ts +201 -0
  74. package/src/commands/export.test.ts +51 -0
  75. package/src/commands/export.ts +404 -0
  76. package/src/commands/fork.ts +2 -2
  77. package/src/commands/inject.ts +2 -2
  78. package/src/commands/load.ts +1 -1
  79. package/src/commands/logs.ts +13 -15
  80. package/src/commands/models.test.ts +1 -1
  81. package/src/commands/models.ts +1 -1
  82. package/src/commands/pause.ts +3 -3
  83. package/src/commands/platforms.test.ts +186 -0
  84. package/src/commands/platforms.ts +371 -0
  85. package/src/commands/remove.ts +1 -1
  86. package/src/commands/reset.ts +2 -2
  87. package/src/commands/spawn.ts +2 -2
  88. package/src/commands/speed.ts +2 -2
  89. package/src/commands/start/adapter-config.ts +157 -0
  90. package/src/commands/start/catalog-le-dt.test.ts +50 -0
  91. package/src/commands/start/catalog-le-dt.ts +187 -0
  92. package/src/commands/start/catalog-model-schema.ts +89 -0
  93. package/src/commands/start/catalog-template-detail.ts +54 -0
  94. package/src/commands/start/daemon.ts +255 -0
  95. package/src/commands/start/display.ts +150 -0
  96. package/src/commands/start/engine-setup.ts +247 -0
  97. package/src/commands/start/events.ts +126 -0
  98. package/src/commands/start/index.ts +897 -0
  99. package/src/commands/start/protocol-plans.ts +239 -0
  100. package/src/commands/start/template-loader.ts +197 -0
  101. package/src/commands/start/template-resolver.ts +185 -0
  102. package/src/commands/start/types.ts +73 -0
  103. package/src/commands/start.test.ts +20 -21
  104. package/src/commands/status.ts +1 -1
  105. package/src/commands/stop.ts +19 -8
  106. package/src/commands/templates.test.ts +1 -1
  107. package/src/commands/templates.ts +1 -3
  108. package/src/index.ts +11 -3
  109. package/src/lib/admin-auth.ts +157 -0
  110. package/src/lib/admin-client.test.ts +31 -15
  111. package/src/lib/admin-client.ts +106 -6
  112. package/src/lib/admin-platform-routes.ts +1017 -0
  113. package/src/lib/admin-platform-stream.test.ts +434 -0
  114. package/src/lib/admin-platform-stream.ts +788 -0
  115. package/src/lib/admin-responses.ts +211 -0
  116. package/src/lib/admin-server.test.ts +274 -55
  117. package/src/lib/admin-server.ts +387 -248
  118. package/src/lib/auth-store.ts +87 -0
  119. package/src/lib/daemon-registry.ts +12 -14
  120. package/src/lib/le/le-apply.test.ts +591 -0
  121. package/src/lib/le/le-apply.ts +832 -0
  122. package/src/lib/le/le-auth.ts +89 -0
  123. package/src/lib/le/le-cleanup.test.ts +168 -0
  124. package/src/lib/le/le-cleanup.ts +399 -0
  125. package/src/lib/le/le-cli-helpers.ts +77 -0
  126. package/src/lib/le/le-client.ts +184 -0
  127. package/src/lib/le/le-concurrency.test.ts +157 -0
  128. package/src/lib/le/le-concurrency.ts +78 -0
  129. package/src/lib/le/le-connection-test.ts +42 -0
  130. package/src/lib/le/le-devicehub-properties.test.ts +53 -0
  131. package/src/lib/le/le-devicehub-properties.ts +33 -0
  132. package/src/lib/le/le-devicehub.ts +213 -0
  133. package/src/lib/le/le-digital-twins.ts +285 -0
  134. package/src/lib/le/le-drivers.ts +23 -0
  135. package/src/lib/le/le-endpoint.ts +40 -0
  136. package/src/lib/le/le-environments.test.ts +73 -0
  137. package/src/lib/le/le-environments.ts +156 -0
  138. package/src/lib/le/le-markers.ts +87 -0
  139. package/src/lib/le/le-plan.test.ts +26 -0
  140. package/src/lib/le/le-plan.ts +109 -0
  141. package/src/lib/le/le-platform-operations.test.ts +198 -0
  142. package/src/lib/le/le-platform-operations.ts +340 -0
  143. package/src/lib/le/le-static-model-schema.ts +78 -0
  144. package/src/lib/le/le-static-planner.test.ts +255 -0
  145. package/src/lib/le/le-static-planner.ts +442 -0
  146. package/src/lib/le/le-template-exporter.test.ts +527 -0
  147. package/src/lib/le/le-template-exporter.ts +459 -0
  148. package/src/lib/le/le-value-types.ts +23 -0
  149. package/src/lib/mqtt-client.ts +1 -2
  150. package/src/lib/resolve-simulation.ts +1 -6
  151. package/src/lib/session-store.ts +110 -0
  152. package/src/lib/shutdown.test.ts +184 -2
  153. package/src/lib/shutdown.ts +96 -36
  154. package/src/lib/template-category.ts +3 -3
  155. package/src/lib/template-locator.ts +8 -2
  156. package/src/lib/ws-hub.ts +72 -46
  157. package/src/logger.ts +18 -10
  158. package/src/output.ts +3 -0
  159. package/console-dist/assets/index-C1oOHU1J.js +0 -119
  160. package/console-dist/assets/index-C3XMYq26.css +0 -2
  161. package/src/commands/start.ts +0 -2253
package/src/lib/ws-hub.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { EventBus, MiravoEvents } from "@miravo/core";
2
2
  import { subscribeToDomainEvents } from "@miravo/core";
3
- import type { EngineRunState, WSMessage } from "@miravo/shared";
3
+ import type { RuntimeSnapshot, WSMessage } from "@miravo/shared";
4
4
  import type { ServerWebSocket } from "bun";
5
5
 
6
6
  /** Channels a client can subscribe to. */
@@ -25,10 +25,20 @@ interface WSClientData {
25
25
  channels: Set<Channel>;
26
26
  }
27
27
 
28
+ /**
29
+ * Callback that provides the engine data needed to build a RuntimeSnapshot.
30
+ * Accepts any structurally compatible type — the data is JSON-serialized in
31
+ * buildRuntimeSnapshot(), so readonly core types and mutable Zod types both work.
32
+ */
33
+ interface RuntimeSnapshotSource {
34
+ readonly getState: () => unknown;
35
+ readonly getMetrics: () => unknown;
36
+ readonly getRunningTemplates: () => unknown;
37
+ }
38
+
28
39
  interface WSHubOptions {
29
40
  readonly bus: EventBus;
30
- readonly getEngineState: () => EngineRunState;
31
- readonly getTickInfo: () => { tick: number; simulationTime: number; instanceCount: number };
41
+ readonly snapshotSource: RuntimeSnapshotSource;
32
42
  readonly maxConnections?: number;
33
43
  readonly backpressureThreshold?: number;
34
44
  }
@@ -44,8 +54,7 @@ class WSHub {
44
54
  readonly #clients = new Set<ServerWebSocket<WSClientData>>();
45
55
  readonly #maxConnections: number;
46
56
  readonly #backpressureThreshold: number;
47
- readonly #getEngineState: () => EngineRunState;
48
- readonly #getTickInfo: () => { tick: number; simulationTime: number; instanceCount: number };
57
+ readonly #snapshotSource: RuntimeSnapshotSource;
49
58
  readonly #bus: EventBus;
50
59
  readonly #tickHandler: (evt: MiravoEvents["tick:complete"]) => void;
51
60
  readonly #adapterEnabledHandler: (evt: MiravoEvents["adapter:enabled"]) => void;
@@ -56,14 +65,13 @@ class WSHub {
56
65
  constructor(options: WSHubOptions) {
57
66
  this.#maxConnections = options.maxConnections ?? DEFAULT_MAX_CONNECTIONS;
58
67
  this.#backpressureThreshold = options.backpressureThreshold ?? DEFAULT_BACKPRESSURE_THRESHOLD;
59
- this.#getEngineState = options.getEngineState;
60
- this.#getTickInfo = options.getTickInfo;
68
+ this.#snapshotSource = options.snapshotSource;
61
69
  this.#bus = options.bus;
62
70
 
63
71
  // Tick events are on their own channel (highest frequency)
64
72
  this.#tickHandler = (evt) => {
65
73
  this.#broadcast("tick", {
66
- type: "tick",
74
+ type: "runtime:tick",
67
75
  revision: this.#nextRevision(),
68
76
  tick: evt.tick,
69
77
  simulationTime: evt.simulationTime,
@@ -76,17 +84,16 @@ class WSHub {
76
84
  // Adapter lifecycle events
77
85
  this.#adapterEnabledHandler = (evt) => {
78
86
  this.#broadcast("adapters", {
79
- type: "adapter:enabled",
87
+ type: "runtime:event",
80
88
  revision: this.#nextRevision(),
81
- name: evt.name,
82
- endpoint: evt.endpoint,
89
+ event: { kind: "adapter:enabled", name: evt.name, endpoint: evt.endpoint },
83
90
  });
84
91
  };
85
92
  this.#adapterDisabledHandler = (evt) => {
86
93
  this.#broadcast("adapters", {
87
- type: "adapter:disabled",
94
+ type: "runtime:event",
88
95
  revision: this.#nextRevision(),
89
- name: evt.name,
96
+ event: { kind: "adapter:disabled", name: evt.name },
90
97
  });
91
98
  };
92
99
  this.#bus.on("adapter:enabled", this.#adapterEnabledHandler);
@@ -96,53 +103,60 @@ class WSHub {
96
103
  this.#unsubscribeDomain = subscribeToDomainEvents(options.bus, {
97
104
  "fault:triggered": (evt) => {
98
105
  this.#broadcast("faults", {
99
- type: "fault:triggered",
106
+ type: "runtime:event",
100
107
  revision: this.#nextRevision(),
101
- instanceId: evt.instanceId,
102
- fault: evt.fault,
103
- effects: evt.effects,
108
+ event: {
109
+ kind: "fault:triggered",
110
+ instanceId: evt.instanceId,
111
+ fault: evt.fault,
112
+ effects: evt.effects,
113
+ },
104
114
  });
105
115
  },
106
116
  "fault:cleared": (evt) => {
107
117
  this.#broadcast("faults", {
108
- type: "fault:cleared",
118
+ type: "runtime:event",
109
119
  revision: this.#nextRevision(),
110
- instanceId: evt.instanceId,
111
- fault: evt.fault,
120
+ event: { kind: "fault:cleared", instanceId: evt.instanceId, fault: evt.fault },
112
121
  });
113
122
  },
114
123
  "lifecycle:changed": (evt) => {
115
124
  this.#broadcast("lifecycle", {
116
- type: "lifecycle:changed",
125
+ type: "runtime:event",
117
126
  revision: this.#nextRevision(),
118
- instanceId: evt.instanceId,
119
- from: evt.from,
120
- to: evt.to,
121
- operatingHours: evt.operatingHours,
127
+ event: {
128
+ kind: "lifecycle:changed",
129
+ instanceId: evt.instanceId,
130
+ from: evt.from,
131
+ to: evt.to,
132
+ operatingHours: evt.operatingHours,
133
+ },
122
134
  });
123
135
  },
124
136
  "instance:created": (evt) => {
125
137
  this.#broadcast("instances", {
126
- type: "instance:created",
138
+ type: "runtime:event",
127
139
  revision: this.#nextRevision(),
128
- id: evt.id,
129
- assetType: evt.assetType,
130
- browsePath: evt.browsePath,
140
+ event: {
141
+ kind: "instance:created",
142
+ id: evt.id,
143
+ assetType: evt.assetType,
144
+ browsePath: evt.browsePath,
145
+ },
131
146
  });
132
147
  },
133
148
  "instance:removed": (evt) => {
134
149
  this.#broadcast("instances", {
135
- type: "instance:removed",
150
+ type: "runtime:event",
136
151
  revision: this.#nextRevision(),
137
- id: evt.id,
152
+ event: { kind: "instance:removed", id: evt.id },
138
153
  });
139
154
  },
140
155
  "engine:state-changed": (evt) => {
141
156
  this.#broadcast("engine", {
142
- type: "engine:state",
157
+ type: "runtime:event",
143
158
  revision: this.#nextRevision(),
144
- from: evt.from,
145
- to: evt.to,
159
+ event: { kind: "engine:state", from: evt.from, to: evt.to },
146
160
  });
147
161
  },
148
162
  });
@@ -152,6 +166,25 @@ class WSHub {
152
166
  return ++this.#revision;
153
167
  }
154
168
 
169
+ /** Current monotonic revision counter (does not increment). */
170
+ get currentRevision(): number {
171
+ return this.#revision;
172
+ }
173
+
174
+ /** Build a canonical RuntimeSnapshot using the current engine state. */
175
+ buildRuntimeSnapshot(): RuntimeSnapshot {
176
+ // JSON round-trip reconciles readonly engine types with Zod-inferred mutable types.
177
+ // Negligible cost: snapshots are serialized for HTTP/WS anyway.
178
+ return JSON.parse(
179
+ JSON.stringify({
180
+ state: this.#snapshotSource.getState(),
181
+ metrics: this.#snapshotSource.getMetrics(),
182
+ runningTemplates: this.#snapshotSource.getRunningTemplates(),
183
+ revision: this.#nextRevision(),
184
+ }),
185
+ ) as RuntimeSnapshot;
186
+ }
187
+
155
188
  #broadcast(channel: Channel, message: WSMessage): void {
156
189
  const json = JSON.stringify(message);
157
190
  for (const ws of this.#clients) {
@@ -186,17 +219,10 @@ class WSHub {
186
219
 
187
220
  this.#clients.add(ws);
188
221
 
189
- // Send initial snapshot
190
- const tickInfo = this.#getTickInfo();
191
- const snapshot: WSMessage = {
192
- type: "snapshot",
193
- revision: this.#nextRevision(),
194
- state: this.#getEngineState(),
195
- tick: tickInfo.tick,
196
- simulationTime: tickInfo.simulationTime,
197
- instanceCount: tickInfo.instanceCount,
198
- };
199
- ws.send(JSON.stringify(snapshot));
222
+ // Send initial canonical runtime snapshot
223
+ const snapshot = this.buildRuntimeSnapshot();
224
+ const msg: WSMessage = { type: "runtime:snapshot", snapshot };
225
+ ws.send(JSON.stringify(msg));
200
226
  }
201
227
 
202
228
  /** Called when a WebSocket message is received from a client. */
@@ -251,5 +277,5 @@ class WSHub {
251
277
  }
252
278
  }
253
279
 
254
- export type { Channel, WSClientData, WSHubOptions };
280
+ export type { WSClientData };
255
281
  export { ALL_CHANNELS, WSHub };
package/src/logger.ts CHANGED
@@ -3,8 +3,8 @@ import type { ConsolaInstance } from "consola";
3
3
  import { createConsola } from "consola";
4
4
  import { colors, stripAnsi } from "consola/utils";
5
5
 
6
- export type LogFormat = "pretty" | "json";
7
- export type LogLevel = "debug" | "info" | "warn" | "error";
6
+ type LogFormat = "pretty" | "json";
7
+ type LogLevel = "debug" | "info" | "warn" | "error";
8
8
 
9
9
  const LOG_LEVEL_MAP: Readonly<Record<LogLevel, number>> = {
10
10
  debug: 4,
@@ -26,6 +26,7 @@ const LOG_LEVEL_MAP: Readonly<Record<LogLevel, number>> = {
26
26
  * lifecycle → magenta brand chart-4 #8b5cf6 (violet)
27
27
  * fault → redBright brand destructive #f43f5e (rose)
28
28
  * control → blueBright lighter blue, distinct from opcua
29
+ * platform → greenBright LE apply/cleanup operations
29
30
  */
30
31
  const TAG_COLORS: Readonly<Record<string, (s: string) => string>> = {
31
32
  engine: colors.yellow,
@@ -36,6 +37,7 @@ const TAG_COLORS: Readonly<Record<string, (s: string) => string>> = {
36
37
  lifecycle: colors.magenta,
37
38
  fault: colors.redBright,
38
39
  control: colors.blueBright,
40
+ platform: colors.greenBright,
39
41
  };
40
42
 
41
43
  /**
@@ -51,6 +53,7 @@ const TAG_DISPLAY_NAMES: Readonly<Record<string, string>> = {
51
53
  lifecycle: "Lifecycle",
52
54
  fault: "Fault",
53
55
  control: "Control",
56
+ platform: "Platform",
54
57
  };
55
58
 
56
59
  /**
@@ -189,6 +192,17 @@ export const modbusLog: ConsolaInstance = logger.withTag("modbus");
189
192
  /** Control plane status and command routing events. */
190
193
  export const controlLog: ConsolaInstance = logger.withTag("control");
191
194
 
195
+ /** Litmus Edge platform apply/cleanup operation events. */
196
+ export const platformLog: ConsolaInstance = logger.withTag("platform");
197
+
198
+ /** Route an adapter name to its dedicated logger. Falls back to engineLog for unknown names. */
199
+ export function adapterLogger(name: string): ConsolaInstance {
200
+ if (name === "mqtt") return mqttLog;
201
+ if (name === "opcua") return opcuaLog;
202
+ if (name === "modbus") return modbusLog;
203
+ return engineLog;
204
+ }
205
+
192
206
  /** All tag loggers, for bulk level updates. */
193
207
  const allLoggers: ConsolaInstance[] = [
194
208
  logger,
@@ -200,6 +214,7 @@ const allLoggers: ConsolaInstance[] = [
200
214
  lifecycleLog,
201
215
  faultLog,
202
216
  controlLog,
217
+ platformLog,
203
218
  ];
204
219
 
205
220
  function applyLogReporter(format: LogFormat): void {
@@ -210,7 +225,7 @@ function applyLogReporter(format: LogFormat): void {
210
225
  }
211
226
 
212
227
  /** Options for configuring log verbosity from CLI flags. */
213
- export interface LoggerOptions {
228
+ interface LoggerOptions {
214
229
  readonly verbose?: boolean | undefined;
215
230
  readonly quiet?: boolean | undefined;
216
231
  readonly plain?: boolean | undefined;
@@ -222,7 +237,6 @@ let _outputSuppressed = false;
222
237
  let _plainOutput = false;
223
238
  let _locked = false;
224
239
  let _logFormat: LogFormat = "pretty";
225
- let _currentLevel = LOG_LEVEL_MAP.info;
226
240
  const runtimeLogContext: { simulationName: string | undefined; runId: string | undefined } = {
227
241
  simulationName: undefined,
228
242
  runId: undefined,
@@ -285,7 +299,6 @@ export function configureLogger(opts: LoggerOptions): void {
285
299
  level = LOG_LEVEL_MAP.info;
286
300
  _outputSuppressed = false;
287
301
  }
288
- _currentLevel = level;
289
302
  for (const l of allLoggers) {
290
303
  l.level = level;
291
304
  }
@@ -350,11 +363,6 @@ export function isPlain(): boolean {
350
363
  return _plainOutput;
351
364
  }
352
365
 
353
- /** Returns true when log level is debug (--verbose or --log-level debug). */
354
- export function isDebugLevel(): boolean {
355
- return _currentLevel >= LOG_LEVEL_MAP.debug;
356
- }
357
-
358
366
  /**
359
367
  * Handle a caught error in a CLI command action. If it's a `MiravoError`,
360
368
  * log the message and fix string and set a non-zero exit code. Otherwise
package/src/output.ts CHANGED
@@ -11,6 +11,9 @@ interface TableRow {
11
11
  readonly [key: string]: string;
12
12
  }
13
13
 
14
+ /** Dim separator used between values on a single summary line. */
15
+ export const sep = colors.dim(" \u00B7 ");
16
+
14
17
  /**
15
18
  * Format a list of objects as a column-aligned table.
16
19
  * Column widths are auto-calculated from header labels and cell values.