miravo 0.6.0 → 0.6.2

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 +29 -59
  3. package/bin/miravo.js +84 -0
  4. package/package.json +21 -37
  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,169 +0,0 @@
1
- import type { Command } from "commander";
2
- import { isPidAlive, probeEntryIdentity, readEntry, removeEntry } from "../lib/daemon-registry.ts";
3
- import { parseTimeoutSeconds } from "../lib/parse.ts";
4
- import { handleCommandError, writeOutput } from "../logger.ts";
5
-
6
- interface StopOptions {
7
- readonly json?: boolean;
8
- readonly timeout?: number;
9
- }
10
-
11
- export function registerStopCommand(program: Command): void {
12
- program
13
- .command("stop <name>")
14
- .description("Stop a running background simulation")
15
- .option("--json", "Machine-readable JSON output")
16
- .option("--timeout <seconds>", "Shutdown timeout in seconds (default: 10)", parseTimeoutSeconds)
17
- .addHelpText(
18
- "after",
19
- `
20
- Examples:
21
- $ miravo stop my-sim Graceful shutdown
22
- $ miravo stop my-sim --timeout 30 Wait up to 30s before force kill`,
23
- )
24
- .action(async (name: string, options: StopOptions) => {
25
- try {
26
- await stopAction(name, options);
27
- } catch (error) {
28
- handleCommandError(error);
29
- }
30
- });
31
- }
32
-
33
- function jsonOut(obj: Record<string, unknown>): void {
34
- console.log(JSON.stringify(obj, null, 2));
35
- }
36
-
37
- async function stopAction(name: string, options: StopOptions): Promise<void> {
38
- const entry = await readEntry(name);
39
- if (!entry) {
40
- const msg = `Simulation "${name}" not found. Use 'miravo ps' to list running simulations.`;
41
- if (options.json) {
42
- jsonOut({ success: false, name, message: msg });
43
- } else {
44
- writeOutput(msg);
45
- }
46
- process.exitCode = 1;
47
- return;
48
- }
49
-
50
- // Verify identity before taking any action — guards against PID reuse
51
- const identity = await probeEntryIdentity(entry);
52
- if (identity.status === "dead" || identity.status === "mismatch") {
53
- await removeEntry(name);
54
- const msg = `Simulation "${name}" was not running (stale registry entry removed).`;
55
- if (options.json) {
56
- jsonOut({ success: true, name, message: msg });
57
- } else {
58
- writeOutput(msg);
59
- }
60
- return;
61
- }
62
- if (identity.status === "unreachable") {
63
- const msg = `Simulation "${name}" could not be safely verified because its admin server is unreachable while PID ${entry.pid} is still alive.`;
64
- if (options.json) {
65
- jsonOut({
66
- success: false,
67
- name,
68
- message: msg,
69
- error: "Retry later, or inspect the process manually before removing the registry entry.",
70
- });
71
- } else {
72
- writeOutput(
73
- `${msg} Retry later, or inspect the process manually before removing the registry entry.`,
74
- );
75
- }
76
- process.exitCode = 1;
77
- return;
78
- }
79
-
80
- // Try graceful shutdown via admin server (identity-safe — admin server belongs to this process)
81
- let shutdownSent = false;
82
- let canForceKill = true;
83
- try {
84
- const adminBase = `http://${entry.adminHost}:${entry.adminPort}`;
85
- // Authenticate with session-based login
86
- // Resolve password: env > default.
87
- // If credential file exists, we can't reverse the hash — use "admin" as default.
88
- // The server will verify against the stored hash. If the user changed their password,
89
- // they need to set MIRAVO_ADMIN_PASSWORD env var for CLI access.
90
- const envPassword = Bun.env["MIRAVO_ADMIN_PASSWORD"];
91
- const password = envPassword && envPassword.trim().length > 0 ? envPassword : "admin";
92
- const loginResponse = await fetch(`${adminBase}/auth/login`, {
93
- method: "POST",
94
- headers: { "content-type": "application/json" },
95
- body: JSON.stringify({ username: "admin", password }),
96
- signal: AbortSignal.timeout(2_000),
97
- });
98
- const setCookie = loginResponse.ok ? loginResponse.headers.get("set-cookie") : null;
99
- const cookie = setCookie ? (setCookie.split(";")[0] ?? "") : "";
100
-
101
- const response = await fetch(`${adminBase}/shutdown`, {
102
- method: "POST",
103
- headers: { "content-type": "application/json", ...(cookie ? { cookie } : {}) },
104
- body: JSON.stringify({ reason: "miravo stop" }),
105
- signal: AbortSignal.timeout(2_000),
106
- });
107
- shutdownSent = response.ok;
108
- } catch {
109
- // Admin server unreachable — fall back to SIGTERM only if we can re-verify identity
110
- }
111
-
112
- if (!shutdownSent) {
113
- try {
114
- process.kill(entry.pid, "SIGTERM");
115
- } catch {
116
- // Process may have already exited
117
- }
118
- }
119
-
120
- // Poll for process exit
121
- const timeoutMs = options.timeout ?? 10_000;
122
- const deadline = Date.now() + timeoutMs;
123
-
124
- while (isPidAlive(entry.pid) && Date.now() < deadline) {
125
- await Bun.sleep(500);
126
- }
127
-
128
- // After /shutdown succeeds the admin server may exit before engine.stop() finishes,
129
- // so keep the verified identity from the successful request for the SIGKILL fallback.
130
- if (shutdownSent) {
131
- canForceKill = true;
132
- }
133
-
134
- // Force kill only if still alive and we already verified identity earlier.
135
- if (isPidAlive(entry.pid) && canForceKill) {
136
- try {
137
- process.kill(entry.pid, "SIGKILL");
138
- } catch {
139
- // Ignore — process may have just exited
140
- }
141
- await Bun.sleep(200);
142
- }
143
-
144
- if (isPidAlive(entry.pid)) {
145
- const msg = `Simulation "${name}" did not exit within the timeout.`;
146
- if (options.json) {
147
- jsonOut({
148
- success: false,
149
- name,
150
- message: msg,
151
- error: "Inspect the process manually and retry with a longer --timeout if needed.",
152
- });
153
- } else {
154
- writeOutput(
155
- `${msg} Inspect the process manually and retry with a longer --timeout if needed.`,
156
- );
157
- }
158
- process.exitCode = 1;
159
- return;
160
- }
161
-
162
- await removeEntry(name);
163
- const msg = `Simulation "${name}" stopped.`;
164
- if (options.json) {
165
- jsonOut({ success: true, name, message: msg });
166
- } else {
167
- writeOutput(msg);
168
- }
169
- }
@@ -1,58 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { resolve } from "node:path";
3
- import { loadTemplate } from "@miravo/core";
4
- import { createTmpFileUtils } from "@miravo/core/test-tmp-utils";
5
- import { stripAnsi } from "consola/utils";
6
- import { configureLogger } from "../logger.ts";
7
- import { runCliCommand } from "./cli-test-setup.ts";
8
- import { formatTemplateInspect } from "./templates.ts";
9
-
10
- configureLogger({ quiet: true });
11
-
12
- const templatesDir = resolve(import.meta.dir, "..", "..", "..", "content", "templates");
13
- const { cleanupTmpDir } = createTmpFileUtils(resolve(import.meta.dir, ".tmp-test-templates"));
14
-
15
- beforeEach(() => {
16
- process.exitCode = 0;
17
- });
18
-
19
- afterEach(async () => {
20
- process.exitCode = 0;
21
- await cleanupTmpDir();
22
- });
23
-
24
- describe("formatTemplateInspect", () => {
25
- test("includes topology summary and source details", async () => {
26
- const templatePath = resolve(templatesDir, "smart-factory.miravo.yaml");
27
- const template = await loadTemplate(templatePath);
28
-
29
- const raw = formatTemplateInspect(template, templatePath, "built-in");
30
- const output = stripAnsi(raw);
31
-
32
- expect(output).toContain("Smart Factory Production Line");
33
- expect(output).toContain("built-in");
34
- expect(output).toContain("Template ID:");
35
- expect(output).toContain("smart-factory");
36
- expect(output).toContain("Namespace:");
37
- expect(output).toContain("Twin assignments:");
38
- expect(output).toContain("Areas:");
39
- expect(output).toContain("machining");
40
- });
41
- });
42
-
43
- describe("miravo templates", () => {
44
- test("templates inspect nonexistent template sets exit code 1", async () => {
45
- await runCliCommand(["templates", "inspect", "nonexistent-template"]);
46
- expect(process.exitCode).toBe(1);
47
- });
48
-
49
- test("templates inspect supports template ID", async () => {
50
- await runCliCommand(["templates", "inspect", "smart-factory"]);
51
- expect(process.exitCode).toBe(0);
52
- });
53
-
54
- test("templates list with bad directory sets exit code 1", async () => {
55
- await runCliCommand(["templates", "list", "--dir", "/nonexistent/templates-dir"]);
56
- expect(process.exitCode).toBe(1);
57
- });
58
- });
@@ -1,277 +0,0 @@
1
- import { basename, relative, resolve } from "node:path";
2
- import { type DiscoveredTemplateWithSource, discoverTemplates, loadTemplate } from "@miravo/core";
3
- import { type DiscoveredTemplate, MiravoError, TEMPLATE_FILE_EXTENSION } from "@miravo/shared";
4
- import type { Command } from "commander";
5
- import { colors } from "consola/utils";
6
- import {
7
- groupTemplatesByCategory,
8
- isStressBenchmarkTemplate,
9
- sortTemplatesByCategoryAndName,
10
- } from "../lib/template-category.ts";
11
- import { createCatalog, resolveTemplateIdentifier } from "../lib/template-locator.ts";
12
- import { collectReferencedModels } from "../lib/template-utils.ts";
13
- import { configLog, handleCommandError, writeOutput } from "../logger.ts";
14
- import { formatKeyValue, formatTable } from "../output.ts";
15
-
16
- export function registerTemplatesCommand(program: Command): void {
17
- const templates = program.command("templates").description("List and inspect Miravo templates");
18
-
19
- templates
20
- .command("list")
21
- .description("Discover and list available templates")
22
- .option("--dir <path>", "Additional directory to search for .miravo.yaml templates")
23
- .addHelpText(
24
- "after",
25
- `
26
- Examples:
27
- $ miravo templates list List all discoverable templates`,
28
- )
29
- .action(async (options: { readonly dir?: string }) => {
30
- await templatesListAction(options);
31
- });
32
-
33
- templates
34
- .command("inspect <idNameOrPath>")
35
- .description("Show detailed information about a template by id, name, or path")
36
- .option("--raw", "Dump the raw YAML file to stdout")
37
- .addHelpText(
38
- "after",
39
- `
40
- Examples:
41
- $ miravo templates inspect water-treatment Show template details
42
- $ miravo templates inspect water-treatment --raw Dump raw YAML`,
43
- )
44
- .action(
45
- async (
46
- idNameOrPath: string,
47
- options: {
48
- readonly raw?: boolean;
49
- },
50
- ) => {
51
- await templatesInspectAction(idNameOrPath, options);
52
- },
53
- );
54
- }
55
-
56
- interface TemplateListOptions {
57
- readonly dir?: string;
58
- }
59
-
60
- interface TemplateInspectOptions {
61
- readonly raw?: boolean;
62
- }
63
-
64
- async function templatesListAction(options: TemplateListOptions): Promise<void> {
65
- try {
66
- const catalog = createCatalog();
67
- const all = await catalog.listTemplates();
68
- const builtinTemplates = all.filter(
69
- (t) => t.source.kind === "builtin" && !isStressBenchmarkTemplate(t.id),
70
- );
71
- const localTemplates: DiscoveredTemplateWithSource[] = all.filter(
72
- (t) => t.source.kind !== "builtin",
73
- );
74
- const localPaths = new Set(localTemplates.map((t) => t.path));
75
-
76
- if (options.dir) {
77
- const resolvedDir = resolve(options.dir);
78
- const { existsSync, statSync } = await import("node:fs");
79
- if (!existsSync(resolvedDir) || !statSync(resolvedDir).isDirectory()) {
80
- throw new MiravoError(
81
- `Templates directory not found: "${resolvedDir}"`,
82
- "Check the path passed to --dir. Ensure the directory exists.",
83
- );
84
- }
85
- const extraTemplates = await discoverTemplates(resolvedDir);
86
- for (const extra of extraTemplates) {
87
- if (!localPaths.has(extra.path)) {
88
- localPaths.add(extra.path);
89
- localTemplates.push({ ...extra, source: { kind: "cwd", path: resolvedDir } });
90
- }
91
- }
92
- }
93
-
94
- if (builtinTemplates.length === 0 && localTemplates.length === 0) {
95
- configLog.info("No templates found.");
96
- return;
97
- }
98
-
99
- const headers = {
100
- id: colors.dim("ID"),
101
- name: colors.dim("Name"),
102
- areas: colors.dim("Areas"),
103
- assignments: colors.dim("Assignments"),
104
- models: colors.dim("Models"),
105
- inline: colors.dim("Inline"),
106
- twins: colors.dim("Twins"),
107
- };
108
-
109
- const renderTemplateTable = (templates: readonly DiscoveredTemplate[]): string =>
110
- formatTable(
111
- headers,
112
- templates.map((template) => ({
113
- id: colors.bold(template.id),
114
- name: colors.cyan(template.name),
115
- areas: String(template.areaCount),
116
- assignments: String(template.twinAssignmentCount),
117
- models: String(template.uniqueModelCount),
118
- inline: String(template.inlineModelCount),
119
- twins: String(template.totalTwinCount),
120
- })),
121
- );
122
-
123
- const renderSection = (label: string, templates: readonly DiscoveredTemplate[]): void => {
124
- if (templates.length === 0) return;
125
- writeOutput();
126
- writeOutput(` ${colors.bold(`${label}:`)}`);
127
- writeOutput(renderTemplateTable(templates));
128
- };
129
-
130
- const sortedBuiltinTemplates = sortTemplatesByCategoryAndName(builtinTemplates);
131
- const groupedBuiltinTemplates = groupTemplatesByCategory(sortedBuiltinTemplates);
132
- if (groupedBuiltinTemplates.length > 0) {
133
- writeOutput();
134
- writeOutput(` ${colors.bold("Built-in:")}`);
135
- groupedBuiltinTemplates.forEach((group, idx) => {
136
- writeOutput(` ${colors.dim(group.category)}`);
137
- writeOutput(renderTemplateTable(group.templates));
138
- if (idx < groupedBuiltinTemplates.length - 1) {
139
- writeOutput();
140
- }
141
- });
142
- }
143
-
144
- const sortedLocalTemplates = [...localTemplates].sort((a, b) => a.name.localeCompare(b.name));
145
- renderSection("Local", sortedLocalTemplates);
146
-
147
- writeOutput();
148
- } catch (error) {
149
- handleCommandError(error);
150
- }
151
- }
152
-
153
- async function templatesInspectAction(
154
- idNameOrPath: string,
155
- options: TemplateInspectOptions,
156
- ): Promise<void> {
157
- try {
158
- const catalog = createCatalog();
159
- const resolvedPath = await resolveTemplateIdentifier(idNameOrPath, catalog);
160
- const sourceLabel = catalog.getSourceLabel(resolvedPath);
161
-
162
- if (options.raw) {
163
- const rawText = await Bun.file(resolvedPath).text();
164
- process.stdout.write(rawText);
165
- return;
166
- }
167
-
168
- const template = await loadTemplate(resolvedPath);
169
- writeOutput(formatTemplateInspect(template, resolvedPath, sourceLabel));
170
- } catch (error) {
171
- handleCommandError(error);
172
- }
173
- }
174
-
175
- export function formatTemplateInspect(
176
- template: import("@miravo/shared").MiravoTemplate,
177
- sourcePath: string,
178
- sourceLabel: string,
179
- ): string {
180
- const lines: string[] = [];
181
-
182
- if (template.description) {
183
- lines.push(
184
- ` ${colors.cyan(colors.bold(template.name))} ${colors.dim("\u2014")} ${template.description}`,
185
- );
186
- } else {
187
- lines.push(` ${colors.cyan(colors.bold(template.name))}`);
188
- }
189
- lines.push("");
190
-
191
- const totalLineCount = template.areas.reduce((sum, area) => sum + area.lines.length, 0);
192
- const twinAssignmentCount = template.areas.reduce(
193
- (sum, area) => sum + area.lines.reduce((lineSum, line) => lineSum + line.twins.length, 0),
194
- 0,
195
- );
196
- const totalTwinCount = template.areas.reduce(
197
- (sum, area) =>
198
- sum +
199
- area.lines.reduce(
200
- (lineSum, line) => lineSum + line.twins.reduce((t, twin) => t + twin.count, 0),
201
- 0,
202
- ),
203
- 0,
204
- );
205
-
206
- const referencedModels = collectReferencedModels(template);
207
-
208
- const namespace = template.namespace
209
- ? "enterprise" in template.namespace
210
- ? `${template.namespace.enterprise}/${template.namespace.site}`
211
- : template.namespace.prefix
212
- : colors.dim("none");
213
-
214
- const protocols = template.protocols
215
- ? Object.keys(template.protocols).join(", ")
216
- : colors.dim("none");
217
- const relPath = relative(process.cwd(), sourcePath);
218
- const templateId = basename(sourcePath, TEMPLATE_FILE_EXTENSION);
219
-
220
- const settings = template.settings;
221
- const settingsSummary = settings
222
- ? `tick ${settings.tick_interval_ms}ms · speed ${settings.time_multiplier}x${settings.seed !== undefined ? ` · seed ${settings.seed}` : ""}`
223
- : colors.dim("defaults");
224
-
225
- lines.push(
226
- formatKeyValue([
227
- [colors.dim("Source"), `${sourceLabel} ${colors.gray(`(${relPath})`)}`],
228
- [colors.dim("Template ID"), colors.bold(templateId)],
229
- [colors.dim("Namespace"), namespace],
230
- [colors.dim("Areas"), String(template.areas.length)],
231
- [colors.dim("Lines"), String(totalLineCount)],
232
- [colors.dim("Twin assignments"), String(twinAssignmentCount)],
233
- [colors.dim("Total twins"), String(totalTwinCount)],
234
- [colors.dim("Referenced models"), String(referencedModels.size)],
235
- [colors.dim("Inline models"), String(template.models?.length ?? 0)],
236
- [colors.dim("Protocols"), protocols],
237
- [colors.dim("Settings"), settingsSummary],
238
- ]),
239
- );
240
-
241
- lines.push("");
242
- lines.push(` ${colors.bold("Areas:")}`);
243
- lines.push(
244
- formatTable(
245
- {
246
- area: colors.dim("Area"),
247
- lines: colors.dim("Lines"),
248
- assignments: colors.dim("Assignments"),
249
- twins: colors.dim("Twins"),
250
- models: colors.dim("Models"),
251
- },
252
- template.areas.map((area) => {
253
- const assignments = area.lines.reduce((sum, line) => sum + line.twins.length, 0);
254
- const twins = area.lines.reduce(
255
- (sum, line) => sum + line.twins.reduce((acc, twin) => acc + twin.count, 0),
256
- 0,
257
- );
258
- const modelNames = new Set<string>();
259
- for (const line of area.lines) {
260
- for (const twin of line.twins) {
261
- modelNames.add(twin.model);
262
- }
263
- }
264
-
265
- return {
266
- area: area.name,
267
- lines: String(area.lines.length),
268
- assignments: String(assignments),
269
- twins: String(twins),
270
- models: [...modelNames].join(", "),
271
- };
272
- }),
273
- ),
274
- );
275
-
276
- return lines.join("\n");
277
- }
package/src/index.ts DELETED
@@ -1,166 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { MIRAVO_VERSION } from "@miravo/shared";
3
- import { Command, InvalidOptionArgumentError } from "commander";
4
- import { registerAdapterCommand } from "./commands/adapter.ts";
5
- import { registerApplyCommand } from "./commands/apply.ts";
6
- import { registerCleanupCommand } from "./commands/cleanup.ts";
7
- import { registerExportCommand } from "./commands/export.ts";
8
- import { registerForkCommand } from "./commands/fork.ts";
9
- import { registerInjectCommand } from "./commands/inject.ts";
10
- import { registerLoadCommand } from "./commands/load.ts";
11
- import { registerLogsCommand } from "./commands/logs.ts";
12
- import { registerModelsCommand } from "./commands/models.ts";
13
- import { registerPauseCommand, registerResumeCommand } from "./commands/pause.ts";
14
- import { registerEnvironmentsCommand } from "./commands/platforms.ts";
15
- import { registerPsCommand } from "./commands/ps.ts";
16
- import { registerRemoveCommand } from "./commands/remove.ts";
17
- import { registerResetCommand } from "./commands/reset.ts";
18
- import { registerRmCommand } from "./commands/rm.ts";
19
- import { registerSpawnCommand } from "./commands/spawn.ts";
20
- import { registerSpeedCommand } from "./commands/speed.ts";
21
- import { registerStartCommand } from "./commands/start/index.ts";
22
- import { registerStatusCommand } from "./commands/status.ts";
23
- import { registerStopCommand } from "./commands/stop.ts";
24
- import { registerTemplatesCommand } from "./commands/templates.ts";
25
- import { configureLogger, parseLogFormat, parseLogLevel, writeOutput } from "./logger.ts";
26
-
27
- /**
28
- * Create and configure the root Commander.js program.
29
- * Exported for testing — tests call createProgram() without triggering parse().
30
- */
31
- export function createProgram(): Command {
32
- const program = new Command();
33
-
34
- program
35
- .name("miravo")
36
- .description("Industrial environment platform for realistic software-defined plants")
37
- .version(MIRAVO_VERSION, "-V, --version", "Print version")
38
- .helpOption("-h, --help", "Show help for command")
39
- .addHelpCommand("help [command]", "Show help for command")
40
- .option("-v, --verbose", "Shorthand for --log-level debug")
41
- .option("-q, --quiet", "Suppress all output except errors")
42
- .option("--plain", "Use stable plain-text output (no ANSI styling)")
43
- .option("--log-level <level>", "Log level: debug | info | warn | error", (value: string) => {
44
- try {
45
- return parseLogLevel(value);
46
- } catch (error) {
47
- const message = error instanceof Error ? error.message : String(error);
48
- throw new InvalidOptionArgumentError(message);
49
- }
50
- })
51
- .option(
52
- "--log-format <format>",
53
- "Log format: pretty | json",
54
- (value: string) => {
55
- try {
56
- return parseLogFormat(value);
57
- } catch (error) {
58
- const message = error instanceof Error ? error.message : String(error);
59
- throw new InvalidOptionArgumentError(message);
60
- }
61
- },
62
- "pretty",
63
- )
64
- .addHelpText("afterAll", "\nDocs: https://miravo.dev/docs")
65
- .action(() => {
66
- writeOutput(`miravo v${MIRAVO_VERSION} — Industrial environment platform
67
-
68
- Quick start:
69
- $ miravo start Start with auto-detected template
70
- $ miravo status View simulation dashboard
71
- $ miravo ps List background simulations
72
-
73
- Run miravo --help for all commands.
74
- Docs: https://miravo.dev/docs`);
75
- })
76
- .hook("preAction", (_thisCommand, actionCommand) => {
77
- const rootOpts = program.opts<{
78
- verbose?: boolean;
79
- quiet?: boolean;
80
- plain?: boolean;
81
- logLevel?: "debug" | "info" | "warn" | "error";
82
- logFormat?: "pretty" | "json";
83
- }>();
84
- const cmdOpts = actionCommand.opts<{
85
- verbose?: boolean;
86
- quiet?: boolean;
87
- plain?: boolean;
88
- logLevel?: "debug" | "info" | "warn" | "error";
89
- logFormat?: "pretty" | "json";
90
- }>();
91
- configureLogger({
92
- verbose: rootOpts.verbose ?? cmdOpts.verbose,
93
- quiet: rootOpts.quiet ?? cmdOpts.quiet,
94
- plain: rootOpts.plain ?? cmdOpts.plain,
95
- logLevel: rootOpts.logLevel ?? cmdOpts.logLevel,
96
- logFormat: rootOpts.logFormat ?? cmdOpts.logFormat,
97
- });
98
- });
99
-
100
- // Simulation lifecycle
101
- registerStartCommand(program);
102
- registerStatusCommand(program);
103
- registerPsCommand(program);
104
- registerStopCommand(program);
105
- registerLogsCommand(program);
106
- registerRmCommand(program);
107
-
108
- // Runtime control
109
- registerSpawnCommand(program);
110
- registerRemoveCommand(program);
111
- registerPauseCommand(program);
112
- registerResumeCommand(program);
113
- registerSpeedCommand(program);
114
- registerLoadCommand(program);
115
- registerResetCommand(program);
116
- registerInjectCommand(program);
117
- registerAdapterCommand(program);
118
- registerExportCommand(program);
119
- registerApplyCommand(program);
120
- registerCleanupCommand(program);
121
- registerEnvironmentsCommand(program);
122
-
123
- // Content discovery
124
- registerModelsCommand(program);
125
- registerTemplatesCommand(program);
126
- registerForkCommand(program);
127
-
128
- return program;
129
- }
130
-
131
- // Only run when executed directly, not when imported in tests
132
- if (import.meta.path === Bun.main) {
133
- // In compiled binaries, extract embedded content to a temp directory so
134
- // ContentCatalog's filesystem-based discovery can find built-in models/templates.
135
- // The manifest only exists during `bun run build:standalone`; the catch is a no-op in dev.
136
- try {
137
- // @ts-expect-error — file is generated at build time and does not exist in source tree
138
- const manifest = await import("./_embedded-manifest.ts");
139
- const { modelPaths, templatePaths } = manifest as {
140
- modelPaths: string[];
141
- templatePaths: string[];
142
- };
143
- const { basename, join } = await import("node:path");
144
- const { tmpdir } = await import("node:os");
145
- const { mkdirSync } = await import("node:fs");
146
- const { registerEmbeddedContentDir } = await import("@miravo/core");
147
-
148
- const contentDir = join(tmpdir(), "miravo-embedded");
149
- const modelsDir = join(contentDir, "models");
150
- const templatesDir = join(contentDir, "templates");
151
- mkdirSync(modelsDir, { recursive: true });
152
- mkdirSync(templatesDir, { recursive: true });
153
-
154
- for (const p of modelPaths) {
155
- await Bun.write(join(modelsDir, basename(p)), Bun.file(p));
156
- }
157
- for (const p of templatePaths) {
158
- await Bun.write(join(templatesDir, basename(p)), Bun.file(p));
159
- }
160
-
161
- registerEmbeddedContentDir(contentDir);
162
- } catch {
163
- // Not a compiled binary or manifest not present — expected in dev mode
164
- }
165
- createProgram().parse();
166
- }