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.
- package/LICENSE +199 -0
- package/README.md +29 -59
- package/bin/miravo.js +84 -0
- package/package.json +21 -37
- package/console-dist/assets/activity-DsWeRq4h.js +0 -1
- package/console-dist/assets/api-4AqKvsm9.js +0 -1
- package/console-dist/assets/arrow-right-Ea_UhTNi.js +0 -1
- package/console-dist/assets/arrow-up-down-DFSjzzl2.js +0 -1
- package/console-dist/assets/blueprint-card-classes-DBFCDy0M.js +0 -1
- package/console-dist/assets/box-C1WaoOMF.js +0 -1
- package/console-dist/assets/card-BjeP32PN.js +0 -1
- package/console-dist/assets/catalog-BJzyzbLR.js +0 -1
- package/console-dist/assets/catalog-DKjEm-c4.js +0 -1
- package/console-dist/assets/catalog-template-icons-BrUeHaCY.js +0 -1
- package/console-dist/assets/checkbox-DZlJXTE4.js +0 -1
- package/console-dist/assets/chunk-OE4NN4TA-CEjpfhS6.js +0 -3
- package/console-dist/assets/combobox-BGnuCYxO.js +0 -1
- package/console-dist/assets/control-CPhvwx9o.js +0 -1
- package/console-dist/assets/control-Cg2up9NA.js +0 -1
- package/console-dist/assets/copy-button-WkbNkMcd.js +0 -1
- package/console-dist/assets/createLucideIcon-Bwxw859K.js +0 -1
- package/console-dist/assets/deploy-tab-JeWDwPoZ.js +0 -1
- package/console-dist/assets/dist-BhbDML0a.js +0 -1
- package/console-dist/assets/dist-Cy8XTNFT.js +0 -2
- package/console-dist/assets/download-DQ6NH16U.js +0 -1
- package/console-dist/assets/engine-store-CrLnz692.js +0 -1
- package/console-dist/assets/events-CXm29Ok4.js +0 -1
- package/console-dist/assets/events-D01IxhnP.js +0 -1
- package/console-dist/assets/fira-code-cyrillic-ext-wght-normal-DhYMMuQd.woff2 +0 -0
- package/console-dist/assets/fira-code-cyrillic-wght-normal-Y3u8pIsh.woff2 +0 -0
- package/console-dist/assets/fira-code-greek-ext-wght-normal-wWus70Ix.woff2 +0 -0
- package/console-dist/assets/fira-code-greek-wght-normal-B2SviObF.woff2 +0 -0
- package/console-dist/assets/fira-code-latin-ext-wght-normal-Dvfvaomy.woff2 +0 -0
- package/console-dist/assets/fira-code-latin-wght-normal-CHoedHDv.woff2 +0 -0
- package/console-dist/assets/fira-code-symbols2-wght-normal-CE6EOz_n.woff2 +0 -0
- package/console-dist/assets/fleet-CfxbKXt9.js +0 -1
- package/console-dist/assets/fleet-lotMGVqe.js +0 -1
- package/console-dist/assets/format-kXTonpDJ.js +0 -1
- package/console-dist/assets/gauge-Ci8zNZlk.js +0 -1
- package/console-dist/assets/get-error-message-3Q5Q-2G5.js +0 -1
- package/console-dist/assets/index-CVPKSDvJ.js +0 -12
- package/console-dist/assets/index-DJkgDeun.css +0 -2
- package/console-dist/assets/input-BWEQFptx.js +0 -1
- package/console-dist/assets/instance-detail-C5IVwuh3.js +0 -1
- package/console-dist/assets/karla-latin-ext-wght-normal-Dq0rrjgs.woff2 +0 -0
- package/console-dist/assets/karla-latin-wght-normal-C3-ma4ov.woff2 +0 -0
- package/console-dist/assets/label-uephX5ZG.js +0 -1
- package/console-dist/assets/layers-CUsLQ1fq.js +0 -1
- package/console-dist/assets/login-C3RJli0m.js +0 -126
- package/console-dist/assets/models-B_UioWQ5.js +0 -1
- package/console-dist/assets/models-_IPlPiZ7.js +0 -1
- package/console-dist/assets/not-found-B8gAm08S.js +0 -1
- package/console-dist/assets/platform-targets-R3T-4eJ7.js +0 -1
- package/console-dist/assets/platforms-BWh0BQ3Y.js +0 -1
- package/console-dist/assets/platforms-DStNbtVc.js +0 -1
- package/console-dist/assets/proxy-CsnYqnPp.js +0 -9
- package/console-dist/assets/query-keys-BvtE7BMs.js +0 -1
- package/console-dist/assets/radio-CoHegOv5.js +0 -1
- package/console-dist/assets/rolldown-runtime-COnpUsM8.js +0 -1
- package/console-dist/assets/schibsted-grotesk-latin-ext-wght-normal-hsMS0n0O.woff2 +0 -0
- package/console-dist/assets/schibsted-grotesk-latin-wght-normal-Bb8VGrTG.woff2 +0 -0
- package/console-dist/assets/select-CeO4vP0G.js +0 -1
- package/console-dist/assets/select-display-items-CIzY8953.js +0 -1
- package/console-dist/assets/server-D9Kfn8fQ.js +0 -1
- package/console-dist/assets/shallow-6lGjQhtJ.js +0 -1
- package/console-dist/assets/tags-CiXOIHtY.js +0 -4
- package/console-dist/assets/tags-FQCXIxrP.js +0 -1
- package/console-dist/assets/tooltip-HGLF366Q.js +0 -1
- package/console-dist/assets/transition-nav-link-Df4Rk6vV.js +0 -1
- package/console-dist/assets/transition-types-CPlGtM8w.js +0 -1
- package/console-dist/assets/triangle-alert-adNk_sea.js +0 -1
- package/console-dist/assets/use-platform-stream-DGbK1k2L.js +0 -2
- package/console-dist/assets/use-transition-navigate-CCpcWXB0.js +0 -2
- package/console-dist/assets/validation-Blvud9_O.js +0 -1
- package/console-dist/assets/vendor-charts-CAZdaPlq.js +0 -60
- package/console-dist/assets/vendor-table-wd8JuMmT.js +0 -4
- package/console-dist/assets/vendor-ui-ByahnaY0.js +0 -1
- package/console-dist/favicon.svg +0 -37
- package/console-dist/index.html +0 -82
- package/console-dist/miravo-logo.png +0 -0
- package/src/__tests__/integration/executable-methods-runner.ts +0 -262
- package/src/__tests__/integration/executable-methods.integration.ts +0 -2348
- package/src/__tests__/integration/opcua-concurrent-clients.integration.ts +0 -314
- package/src/commands/adapter.ts +0 -249
- package/src/commands/apply.test.ts +0 -62
- package/src/commands/apply.ts +0 -216
- package/src/commands/cleanup.test.ts +0 -34
- package/src/commands/cleanup.ts +0 -201
- package/src/commands/cli-test-setup.ts +0 -91
- package/src/commands/export.test.ts +0 -51
- package/src/commands/export.ts +0 -404
- package/src/commands/fork.ts +0 -219
- package/src/commands/inject.test.ts +0 -57
- package/src/commands/inject.ts +0 -212
- package/src/commands/load.ts +0 -167
- package/src/commands/logs.ts +0 -173
- package/src/commands/models.test.ts +0 -162
- package/src/commands/models.ts +0 -338
- package/src/commands/pause.ts +0 -67
- package/src/commands/platforms.test.ts +0 -186
- package/src/commands/platforms.ts +0 -371
- package/src/commands/ps.ts +0 -116
- package/src/commands/remove.ts +0 -38
- package/src/commands/reset.ts +0 -44
- package/src/commands/rm.ts +0 -97
- package/src/commands/spawn.ts +0 -45
- package/src/commands/speed.ts +0 -40
- package/src/commands/start/adapter-config.ts +0 -157
- package/src/commands/start/catalog-le-dt.test.ts +0 -50
- package/src/commands/start/catalog-le-dt.ts +0 -187
- package/src/commands/start/catalog-model-schema.ts +0 -89
- package/src/commands/start/catalog-template-detail.ts +0 -54
- package/src/commands/start/daemon.ts +0 -255
- package/src/commands/start/display.ts +0 -150
- package/src/commands/start/engine-setup.ts +0 -247
- package/src/commands/start/events.ts +0 -126
- package/src/commands/start/index.ts +0 -897
- package/src/commands/start/protocol-plans.ts +0 -239
- package/src/commands/start/template-loader.ts +0 -197
- package/src/commands/start/template-resolver.ts +0 -185
- package/src/commands/start/types.ts +0 -73
- package/src/commands/start.test.ts +0 -480
- package/src/commands/status.test.ts +0 -20
- package/src/commands/status.ts +0 -470
- package/src/commands/stop.test.ts +0 -84
- package/src/commands/stop.ts +0 -169
- package/src/commands/templates.test.ts +0 -58
- package/src/commands/templates.ts +0 -277
- package/src/index.ts +0 -166
- package/src/lib/admin-auth.ts +0 -157
- package/src/lib/admin-client.test.ts +0 -51
- package/src/lib/admin-client.ts +0 -326
- package/src/lib/admin-platform-routes.ts +0 -1017
- package/src/lib/admin-platform-stream.test.ts +0 -434
- package/src/lib/admin-platform-stream.ts +0 -788
- package/src/lib/admin-responses.ts +0 -211
- package/src/lib/admin-server.test.ts +0 -781
- package/src/lib/admin-server.ts +0 -797
- package/src/lib/auth-store.ts +0 -87
- package/src/lib/daemon-registry.test.ts +0 -429
- package/src/lib/daemon-registry.ts +0 -343
- package/src/lib/le/le-apply.test.ts +0 -591
- package/src/lib/le/le-apply.ts +0 -832
- package/src/lib/le/le-auth.ts +0 -89
- package/src/lib/le/le-cleanup.test.ts +0 -168
- package/src/lib/le/le-cleanup.ts +0 -399
- package/src/lib/le/le-cli-helpers.ts +0 -77
- package/src/lib/le/le-client.ts +0 -184
- package/src/lib/le/le-concurrency.test.ts +0 -157
- package/src/lib/le/le-concurrency.ts +0 -78
- package/src/lib/le/le-connection-test.ts +0 -42
- package/src/lib/le/le-devicehub-properties.test.ts +0 -53
- package/src/lib/le/le-devicehub-properties.ts +0 -33
- package/src/lib/le/le-devicehub.ts +0 -213
- package/src/lib/le/le-digital-twins.ts +0 -285
- package/src/lib/le/le-drivers.ts +0 -23
- package/src/lib/le/le-endpoint.ts +0 -40
- package/src/lib/le/le-environments.test.ts +0 -73
- package/src/lib/le/le-environments.ts +0 -156
- package/src/lib/le/le-markers.ts +0 -87
- package/src/lib/le/le-plan.test.ts +0 -26
- package/src/lib/le/le-plan.ts +0 -109
- package/src/lib/le/le-platform-operations.test.ts +0 -198
- package/src/lib/le/le-platform-operations.ts +0 -340
- package/src/lib/le/le-static-model-schema.ts +0 -78
- package/src/lib/le/le-static-planner.test.ts +0 -255
- package/src/lib/le/le-static-planner.ts +0 -442
- package/src/lib/le/le-template-exporter.test.ts +0 -527
- package/src/lib/le/le-template-exporter.ts +0 -459
- package/src/lib/le/le-value-types.ts +0 -23
- package/src/lib/mqtt-client.ts +0 -115
- package/src/lib/parse.test.ts +0 -123
- package/src/lib/parse.ts +0 -87
- package/src/lib/resolve-simulation.test.ts +0 -118
- package/src/lib/resolve-simulation.ts +0 -181
- package/src/lib/session-store.ts +0 -110
- package/src/lib/shutdown.test.ts +0 -562
- package/src/lib/shutdown.ts +0 -621
- package/src/lib/template-category.test.ts +0 -71
- package/src/lib/template-category.ts +0 -72
- package/src/lib/template-locator.ts +0 -55
- package/src/lib/template-utils.ts +0 -45
- package/src/lib/ws-hub.ts +0 -281
- package/src/logger.test.ts +0 -172
- package/src/logger.ts +0 -402
- package/src/output.test.ts +0 -64
- package/src/output.ts +0 -160
package/src/commands/remove.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { EngineCommand } from "@miravo/shared";
|
|
2
|
-
import { CONTROL_CMD_TIMEOUT_MS } from "@miravo/shared";
|
|
3
|
-
import type { Command } from "commander";
|
|
4
|
-
import { executeAdminCommand, resolveAdminUrl } from "../lib/admin-client.ts";
|
|
5
|
-
import { parseTimeoutSeconds } from "../lib/parse.ts";
|
|
6
|
-
import { handleCommandError, handleCommandResult } from "../logger.ts";
|
|
7
|
-
|
|
8
|
-
interface RemoveOptions {
|
|
9
|
-
readonly json?: boolean;
|
|
10
|
-
readonly adminUrl?: string;
|
|
11
|
-
readonly timeout?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function registerRemoveCommand(program: Command): void {
|
|
15
|
-
program
|
|
16
|
-
.command("remove <instanceId>")
|
|
17
|
-
.description("Remove an instance from the running simulation")
|
|
18
|
-
.option("--json", "Machine-readable JSON output")
|
|
19
|
-
.option("--admin-url <url>", "Admin API URL (env: MIRAVO_ADMIN_URL)")
|
|
20
|
-
.option("--timeout <seconds>", "Timeout in seconds (default: 5)", parseTimeoutSeconds)
|
|
21
|
-
.addHelpText(
|
|
22
|
-
"after",
|
|
23
|
-
`
|
|
24
|
-
Examples:
|
|
25
|
-
$ miravo remove pump-001 Remove instance by ID`,
|
|
26
|
-
)
|
|
27
|
-
.action(async (instanceId: string, options: RemoveOptions) => {
|
|
28
|
-
try {
|
|
29
|
-
const adminUrl = resolveAdminUrl(options.adminUrl);
|
|
30
|
-
const timeoutMs = options.timeout ?? CONTROL_CMD_TIMEOUT_MS;
|
|
31
|
-
const command: EngineCommand = { type: "removeInstance", instanceId };
|
|
32
|
-
const result = await executeAdminCommand(adminUrl, command, timeoutMs);
|
|
33
|
-
handleCommandResult(result, options.json);
|
|
34
|
-
} catch (error) {
|
|
35
|
-
handleCommandError(error);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
package/src/commands/reset.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { EngineCommand } from "@miravo/shared";
|
|
2
|
-
import { CONTROL_CMD_TIMEOUT_MS } from "@miravo/shared";
|
|
3
|
-
import type { Command } from "commander";
|
|
4
|
-
import { executeAdminCommand, resolveAdminUrl } from "../lib/admin-client.ts";
|
|
5
|
-
import { parseTimeoutSeconds } from "../lib/parse.ts";
|
|
6
|
-
import { handleCommandError, handleCommandResult } from "../logger.ts";
|
|
7
|
-
|
|
8
|
-
interface ResetOptions {
|
|
9
|
-
readonly clearPersistence?: boolean;
|
|
10
|
-
readonly json?: boolean;
|
|
11
|
-
readonly adminUrl?: string;
|
|
12
|
-
readonly timeout?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function registerResetCommand(program: Command): void {
|
|
16
|
-
program
|
|
17
|
-
.command("reset")
|
|
18
|
-
.description("Reset simulation state: remove all instances and template runs")
|
|
19
|
-
.option("--clear-persistence", "Also clear persisted state (start fresh on next boot)")
|
|
20
|
-
.option("--json", "Machine-readable JSON output")
|
|
21
|
-
.option("--admin-url <url>", "Admin API URL (env: MIRAVO_ADMIN_URL)")
|
|
22
|
-
.option("--timeout <seconds>", "Timeout in seconds (default: 5)", parseTimeoutSeconds)
|
|
23
|
-
.addHelpText(
|
|
24
|
-
"after",
|
|
25
|
-
`
|
|
26
|
-
Examples:
|
|
27
|
-
$ miravo reset Reset simulation state
|
|
28
|
-
$ miravo reset --clear-persistence Reset and clear saved state`,
|
|
29
|
-
)
|
|
30
|
-
.action(async (options: ResetOptions) => {
|
|
31
|
-
try {
|
|
32
|
-
const adminUrl = resolveAdminUrl(options.adminUrl);
|
|
33
|
-
const timeoutMs = options.timeout ?? CONTROL_CMD_TIMEOUT_MS;
|
|
34
|
-
const command: EngineCommand = {
|
|
35
|
-
type: "resetSimulation",
|
|
36
|
-
clearPersistence: options.clearPersistence ?? false,
|
|
37
|
-
};
|
|
38
|
-
const result = await executeAdminCommand(adminUrl, command, timeoutMs);
|
|
39
|
-
handleCommandResult(result, options.json);
|
|
40
|
-
} catch (error) {
|
|
41
|
-
handleCommandError(error);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
package/src/commands/rm.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type { Command } from "commander";
|
|
2
|
-
import { probeEntryIdentity, readEntry, removeEntry } from "../lib/daemon-registry.ts";
|
|
3
|
-
import { handleCommandError, writeOutput } from "../logger.ts";
|
|
4
|
-
|
|
5
|
-
interface RmOptions {
|
|
6
|
-
readonly force?: boolean;
|
|
7
|
-
readonly json?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function registerRmCommand(program: Command): void {
|
|
11
|
-
program
|
|
12
|
-
.command("rm <name>")
|
|
13
|
-
.description("Remove a stale daemon registry entry")
|
|
14
|
-
.option("-f, --force", "Kill the process if still alive before removing")
|
|
15
|
-
.option("--json", "Machine-readable JSON output")
|
|
16
|
-
.addHelpText(
|
|
17
|
-
"after",
|
|
18
|
-
`
|
|
19
|
-
Examples:
|
|
20
|
-
$ miravo rm my-sim Remove stale registry entry
|
|
21
|
-
$ miravo rm my-sim --force Kill process and remove entry`,
|
|
22
|
-
)
|
|
23
|
-
.action(async (name: string, options: RmOptions) => {
|
|
24
|
-
try {
|
|
25
|
-
await rmAction(name, options);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
handleCommandError(error);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function jsonOut(obj: Record<string, unknown>): void {
|
|
33
|
-
console.log(JSON.stringify(obj, null, 2));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function rmAction(name: string, options: RmOptions): Promise<void> {
|
|
37
|
-
const entry = await readEntry(name);
|
|
38
|
-
if (!entry) {
|
|
39
|
-
const msg = `Simulation "${name}" not found in daemon registry.`;
|
|
40
|
-
if (options.json) {
|
|
41
|
-
jsonOut({ success: false, name, message: msg });
|
|
42
|
-
} else {
|
|
43
|
-
writeOutput(msg);
|
|
44
|
-
}
|
|
45
|
-
process.exitCode = 1;
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const identity = await probeEntryIdentity(entry);
|
|
50
|
-
|
|
51
|
-
if (identity.status === "unreachable") {
|
|
52
|
-
const msg = `Simulation "${name}" could not be safely verified because its admin server is unreachable while PID ${entry.pid} is still alive.`;
|
|
53
|
-
if (options.json) {
|
|
54
|
-
jsonOut({
|
|
55
|
-
success: false,
|
|
56
|
-
name,
|
|
57
|
-
message: msg,
|
|
58
|
-
error: "Retry later, or inspect the process manually before removing the registry entry.",
|
|
59
|
-
});
|
|
60
|
-
} else {
|
|
61
|
-
writeOutput(
|
|
62
|
-
`${msg} Retry later, or inspect the process manually before removing the registry entry.`,
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
process.exitCode = 1;
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (identity.status === "verified") {
|
|
70
|
-
if (!options.force) {
|
|
71
|
-
const msg = `Simulation "${name}" is still running (PID ${entry.pid}). Use --force to kill it, or 'miravo stop ${name}' for graceful shutdown.`;
|
|
72
|
-
if (options.json) {
|
|
73
|
-
jsonOut({ success: false, name, message: msg });
|
|
74
|
-
} else {
|
|
75
|
-
writeOutput(msg);
|
|
76
|
-
}
|
|
77
|
-
process.exitCode = 1;
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Force kill
|
|
82
|
-
try {
|
|
83
|
-
process.kill(entry.pid, "SIGKILL");
|
|
84
|
-
} catch {
|
|
85
|
-
// Ignore — may have exited between check and kill
|
|
86
|
-
}
|
|
87
|
-
await Bun.sleep(200);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
await removeEntry(name);
|
|
91
|
-
const msg = `Removed daemon registry entry for "${name}".`;
|
|
92
|
-
if (options.json) {
|
|
93
|
-
jsonOut({ success: true, name, message: msg });
|
|
94
|
-
} else {
|
|
95
|
-
writeOutput(msg);
|
|
96
|
-
}
|
|
97
|
-
}
|
package/src/commands/spawn.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { EngineCommand } from "@miravo/shared";
|
|
2
|
-
import { CONTROL_CMD_TIMEOUT_MS } from "@miravo/shared";
|
|
3
|
-
import type { Command } from "commander";
|
|
4
|
-
import { executeAdminCommand, resolveAdminUrl } from "../lib/admin-client.ts";
|
|
5
|
-
import { parsePositiveInt, parseTimeoutSeconds } from "../lib/parse.ts";
|
|
6
|
-
import { handleCommandError, handleCommandResult } from "../logger.ts";
|
|
7
|
-
|
|
8
|
-
interface SpawnOptions {
|
|
9
|
-
readonly count?: number;
|
|
10
|
-
readonly json?: boolean;
|
|
11
|
-
readonly adminUrl?: string;
|
|
12
|
-
readonly timeout?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function registerSpawnCommand(program: Command): void {
|
|
16
|
-
program
|
|
17
|
-
.command("spawn <model>")
|
|
18
|
-
.description("Spawn new instances of a model into the running simulation")
|
|
19
|
-
.option("-c, --count <n>", "Number of instances to spawn (default: 1)", parsePositiveInt)
|
|
20
|
-
.option("--json", "Machine-readable JSON output")
|
|
21
|
-
.option("--admin-url <url>", "Admin API URL (env: MIRAVO_ADMIN_URL)")
|
|
22
|
-
.option("--timeout <seconds>", "Timeout in seconds (default: 5)", parseTimeoutSeconds)
|
|
23
|
-
.addHelpText(
|
|
24
|
-
"after",
|
|
25
|
-
`
|
|
26
|
-
Examples:
|
|
27
|
-
$ miravo spawn centrifugal-pump Spawn one instance
|
|
28
|
-
$ miravo spawn centrifugal-pump -c 5 Spawn five instances`,
|
|
29
|
-
)
|
|
30
|
-
.action(async (model: string, options: SpawnOptions) => {
|
|
31
|
-
try {
|
|
32
|
-
const adminUrl = resolveAdminUrl(options.adminUrl);
|
|
33
|
-
const timeoutMs = options.timeout ?? CONTROL_CMD_TIMEOUT_MS;
|
|
34
|
-
const command: EngineCommand = {
|
|
35
|
-
type: "spawn",
|
|
36
|
-
model,
|
|
37
|
-
count: options.count ?? 1,
|
|
38
|
-
};
|
|
39
|
-
const result = await executeAdminCommand(adminUrl, command, timeoutMs);
|
|
40
|
-
handleCommandResult(result, options.json);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
handleCommandError(error);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
package/src/commands/speed.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { EngineCommand } from "@miravo/shared";
|
|
2
|
-
import { CONTROL_CMD_TIMEOUT_MS } from "@miravo/shared";
|
|
3
|
-
import type { Command } from "commander";
|
|
4
|
-
import { executeAdminCommand, resolveAdminUrl } from "../lib/admin-client.ts";
|
|
5
|
-
import { parsePositiveFloat, parseTimeoutSeconds } from "../lib/parse.ts";
|
|
6
|
-
import { handleCommandError, handleCommandResult } from "../logger.ts";
|
|
7
|
-
|
|
8
|
-
interface SpeedOptions {
|
|
9
|
-
readonly json?: boolean;
|
|
10
|
-
readonly adminUrl?: string;
|
|
11
|
-
readonly timeout?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function registerSpeedCommand(program: Command): void {
|
|
15
|
-
program
|
|
16
|
-
.command("speed <multiplier>")
|
|
17
|
-
.description("Set simulation time multiplier (1.0 = real-time)")
|
|
18
|
-
.option("--json", "Machine-readable JSON output")
|
|
19
|
-
.option("--admin-url <url>", "Admin API URL (env: MIRAVO_ADMIN_URL)")
|
|
20
|
-
.option("--timeout <seconds>", "Timeout in seconds (default: 5)", parseTimeoutSeconds)
|
|
21
|
-
.addHelpText(
|
|
22
|
-
"after",
|
|
23
|
-
`
|
|
24
|
-
Examples:
|
|
25
|
-
$ miravo speed 10 Run at 10x real time
|
|
26
|
-
$ miravo speed 0.5 Run at half speed`,
|
|
27
|
-
)
|
|
28
|
-
.action(async (multiplierStr: string, options: SpeedOptions) => {
|
|
29
|
-
try {
|
|
30
|
-
const multiplier = parsePositiveFloat(multiplierStr);
|
|
31
|
-
const adminUrl = resolveAdminUrl(options.adminUrl);
|
|
32
|
-
const timeoutMs = options.timeout ?? CONTROL_CMD_TIMEOUT_MS;
|
|
33
|
-
const command: EngineCommand = { type: "setSpeed", multiplier };
|
|
34
|
-
const result = await executeAdminCommand(adminUrl, command, timeoutMs);
|
|
35
|
-
handleCommandResult(result, options.json);
|
|
36
|
-
} catch (error) {
|
|
37
|
-
handleCommandError(error);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import type { MiravoTemplate, ProtocolConfig } from "@miravo/shared";
|
|
2
|
-
import {
|
|
3
|
-
ConfigValidationError,
|
|
4
|
-
DEFAULT_MODBUS_PORT,
|
|
5
|
-
DEFAULT_MQTT_PORT,
|
|
6
|
-
DEFAULT_OPCUA_PORT,
|
|
7
|
-
} from "@miravo/shared";
|
|
8
|
-
import { displayHost } from "../../lib/daemon-registry.ts";
|
|
9
|
-
import type { MqttMode, MqttRunOverrides } from "./types.ts";
|
|
10
|
-
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Step 5: Adapter default config builders
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
export function buildMQTTDefaultConfig(
|
|
16
|
-
template: MiravoTemplate,
|
|
17
|
-
portOverride: number | undefined,
|
|
18
|
-
mqttOverrides: MqttRunOverrides,
|
|
19
|
-
simulationName: string,
|
|
20
|
-
): Record<string, unknown> {
|
|
21
|
-
const mqttConfig = template.protocols?.mqtt;
|
|
22
|
-
const port = portOverride ?? mqttConfig?.port ?? DEFAULT_MQTT_PORT;
|
|
23
|
-
let embedded = mqttConfig?.embedded ?? true;
|
|
24
|
-
let external = mqttConfig?.external;
|
|
25
|
-
let host = "0.0.0.0";
|
|
26
|
-
let username = mqttConfig?.username;
|
|
27
|
-
let password = mqttConfig?.password;
|
|
28
|
-
const modeOverride = mqttOverrides.mode;
|
|
29
|
-
if (modeOverride !== undefined) {
|
|
30
|
-
embedded = modeOverride === "embedded";
|
|
31
|
-
if (embedded) {
|
|
32
|
-
external = undefined;
|
|
33
|
-
username = undefined;
|
|
34
|
-
password = undefined;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (mqttOverrides.url !== undefined) external = mqttOverrides.url;
|
|
38
|
-
if (mqttOverrides.host !== undefined) host = mqttOverrides.host;
|
|
39
|
-
if (mqttOverrides.username !== undefined) username = mqttOverrides.username;
|
|
40
|
-
if (mqttOverrides.password !== undefined) password = mqttOverrides.password;
|
|
41
|
-
|
|
42
|
-
if (embedded && (external || username || password)) {
|
|
43
|
-
throw new ConfigValidationError({
|
|
44
|
-
message: "Embedded MQTT mode cannot be combined with external MQTT URL or credentials.",
|
|
45
|
-
fix: "Use --mqtt-mode external for external broker settings, or remove external MQTT overrides.",
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
if (!embedded && !external) {
|
|
49
|
-
throw new ConfigValidationError({
|
|
50
|
-
message: "MQTT mode is external but no broker URL was provided.",
|
|
51
|
-
fix: "Set protocols.mqtt.external in template or pass --mqtt-url / MIRAVO_MQTT_URL.",
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
if (password && !username) {
|
|
55
|
-
throw new ConfigValidationError({
|
|
56
|
-
message: "MQTT password was provided without username.",
|
|
57
|
-
fix: "Provide --mqtt-username / MIRAVO_MQTT_USERNAME when setting a password.",
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const drainBudget = mqttOverrides.drainBudget ?? mqttConfig?.drain_budget;
|
|
62
|
-
const batchSize = mqttOverrides.batchSize ?? mqttConfig?.batch_size;
|
|
63
|
-
const brokerConcurrency = mqttOverrides.brokerConcurrency ?? mqttConfig?.broker_concurrency;
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
name: "mqtt",
|
|
67
|
-
enabled: true,
|
|
68
|
-
simulationName,
|
|
69
|
-
embedded,
|
|
70
|
-
port,
|
|
71
|
-
host,
|
|
72
|
-
retain: mqttConfig?.retain ?? false,
|
|
73
|
-
...(external !== undefined ? { external } : {}),
|
|
74
|
-
...(username !== undefined ? { username } : {}),
|
|
75
|
-
...(password !== undefined ? { password } : {}),
|
|
76
|
-
...(mqttConfig?.reconnect !== undefined ? { reconnect: mqttConfig.reconnect } : {}),
|
|
77
|
-
...(mqttConfig?.rbe !== undefined ? { rbe: mqttConfig.rbe } : {}),
|
|
78
|
-
...(mqttConfig?.metadata !== undefined ? { metadata: mqttConfig.metadata } : {}),
|
|
79
|
-
...(drainBudget !== undefined ? { drain_budget: drainBudget } : {}),
|
|
80
|
-
...(batchSize !== undefined ? { batch_size: batchSize } : {}),
|
|
81
|
-
...(brokerConcurrency !== undefined ? { broker_concurrency: brokerConcurrency } : {}),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function buildOPCUADefaultConfig(
|
|
86
|
-
template: MiravoTemplate,
|
|
87
|
-
portOverride: number | undefined,
|
|
88
|
-
hostOverride: string | undefined,
|
|
89
|
-
simulationName: string,
|
|
90
|
-
writeBudgetOverride?: number,
|
|
91
|
-
): Record<string, unknown> {
|
|
92
|
-
const opcuaConfig = template.protocols?.opcua;
|
|
93
|
-
const port = portOverride ?? opcuaConfig?.port ?? DEFAULT_OPCUA_PORT;
|
|
94
|
-
const host = hostOverride ?? opcuaConfig?.host ?? "0.0.0.0";
|
|
95
|
-
const writeBudget = writeBudgetOverride ?? opcuaConfig?.write_budget;
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
name: "opcua",
|
|
99
|
-
enabled: true,
|
|
100
|
-
port,
|
|
101
|
-
host,
|
|
102
|
-
security: opcuaConfig?.security ?? "none",
|
|
103
|
-
simulationName,
|
|
104
|
-
...(opcuaConfig?.buildInfo ? { buildInfo: opcuaConfig.buildInfo } : {}),
|
|
105
|
-
...(writeBudget !== undefined ? { write_budget: writeBudget } : {}),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function buildModbusDefaultConfig(
|
|
110
|
-
template: MiravoTemplate,
|
|
111
|
-
portOverride: number | undefined,
|
|
112
|
-
hostOverride: string | undefined,
|
|
113
|
-
): Record<string, unknown> {
|
|
114
|
-
const modbusConfig = template.protocols?.modbus;
|
|
115
|
-
const port = portOverride ?? modbusConfig?.port ?? DEFAULT_MODBUS_PORT;
|
|
116
|
-
const host = hostOverride ?? modbusConfig?.host ?? "0.0.0.0";
|
|
117
|
-
return { name: "modbus", enabled: true, port, host };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Derive the MQTT endpoint string from a resolved adapter config. */
|
|
121
|
-
export function buildMQTTEndpoint(config: ProtocolConfig): string {
|
|
122
|
-
const cfg = config as Record<string, unknown>;
|
|
123
|
-
const port = cfg["port"] ?? DEFAULT_MQTT_PORT;
|
|
124
|
-
const embedded = cfg["embedded"] ?? true;
|
|
125
|
-
const external = cfg["external"] as string | undefined;
|
|
126
|
-
return embedded ? `mqtt://localhost:${port}` : (external as string);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Derive the OPC UA endpoint string from a resolved adapter config. */
|
|
130
|
-
export function buildOPCUAEndpoint(config: ProtocolConfig): string {
|
|
131
|
-
const cfg = config as Record<string, unknown>;
|
|
132
|
-
const port = cfg["port"] ?? DEFAULT_OPCUA_PORT;
|
|
133
|
-
const host = cfg["host"] ?? "0.0.0.0";
|
|
134
|
-
const resolvedDisplayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
135
|
-
return `opc.tcp://${resolvedDisplayHost}:${port}`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function buildModbusEndpoint(config: Record<string, unknown>): string {
|
|
139
|
-
const port = (config as { port?: number }).port ?? DEFAULT_MODBUS_PORT;
|
|
140
|
-
const host = (config as { host?: string }).host ?? "0.0.0.0";
|
|
141
|
-
return `modbus://${displayHost(String(host))}:${port}`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function parseMqttModeOption(raw: string | undefined): MqttMode | undefined {
|
|
145
|
-
if (raw === undefined) {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (raw === "embedded" || raw === "external") {
|
|
150
|
-
return raw;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
throw new ConfigValidationError({
|
|
154
|
-
message: `Invalid MQTT mode "${raw}".`,
|
|
155
|
-
fix: "Use --mqtt-mode embedded|external or MIRAVO_MQTT_MODE=embedded|external.",
|
|
156
|
-
});
|
|
157
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import type { ModelSchema } from "@miravo/shared";
|
|
3
|
-
import { buildLeDtJsonFromSchema } from "./catalog-le-dt.ts";
|
|
4
|
-
|
|
5
|
-
function parseExport(json: string): {
|
|
6
|
-
staticAttributes: { key: string; value: string }[];
|
|
7
|
-
} {
|
|
8
|
-
const payload = JSON.parse(json) as [{ staticAttributes: { key: string; value: string }[] }];
|
|
9
|
-
return payload[0];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const modelSchema: ModelSchema = {
|
|
13
|
-
name: "pump",
|
|
14
|
-
description: "Pump model",
|
|
15
|
-
parameters: {},
|
|
16
|
-
members: {
|
|
17
|
-
serial: {
|
|
18
|
-
kind: "property",
|
|
19
|
-
dataType: "String",
|
|
20
|
-
access: "read",
|
|
21
|
-
unit: null,
|
|
22
|
-
euRange: null,
|
|
23
|
-
default: "P-001",
|
|
24
|
-
},
|
|
25
|
-
rpm: {
|
|
26
|
-
kind: "variable",
|
|
27
|
-
dataType: "Float",
|
|
28
|
-
access: "read",
|
|
29
|
-
unit: null,
|
|
30
|
-
euRange: null,
|
|
31
|
-
default: null,
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
methods: {},
|
|
35
|
-
faults: [],
|
|
36
|
-
lifecycle: null,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
describe("catalog LE DT export", () => {
|
|
40
|
-
test("includes Miravo marker placeholders in single-model export", () => {
|
|
41
|
-
const exported = parseExport(buildLeDtJsonFromSchema(modelSchema));
|
|
42
|
-
const markerValues = new Map(exported.staticAttributes.map((attr) => [attr.key, attr.value]));
|
|
43
|
-
|
|
44
|
-
expect(markerValues.get("miravo_managed")).toBe("false");
|
|
45
|
-
expect(markerValues.get("miravo_template")).toBe("__miravo_placeholder__");
|
|
46
|
-
expect(markerValues.get("miravo_model")).toBe("__miravo_placeholder__");
|
|
47
|
-
expect(markerValues.get("miravo_instance")).toBe("__miravo_placeholder__");
|
|
48
|
-
expect(markerValues.get("serial")).toBe("P-001");
|
|
49
|
-
});
|
|
50
|
-
});
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates Litmus Edge Digital Twin import JSON from Miravo models.
|
|
3
|
-
*
|
|
4
|
-
* The output matches the format produced by LE's `ExportModel` GraphQL mutation
|
|
5
|
-
* and can be imported directly via `ImportModel` or the LE UI.
|
|
6
|
-
*
|
|
7
|
-
* Model export mapping (`buildLeDtJsonFromSchema`):
|
|
8
|
-
* - kind: property → LE static attributes (placeholder values)
|
|
9
|
-
* - kind: variable (r/rw)→ LE dynamic attributes via topicExpression devicehub.alias.{device}.<member>
|
|
10
|
-
* - LE parameters → wiring placeholders only ({device})
|
|
11
|
-
* - Methods/faults/lifecycle are not exported
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { ModelSchema } from "@miravo/shared";
|
|
15
|
-
|
|
16
|
-
/** Maps Miravo dataType → LE DynamicAttributeType enum values. */
|
|
17
|
-
const LE_DATA_TYPE: Readonly<Record<string, string>> = {
|
|
18
|
-
Double: "number",
|
|
19
|
-
Float: "number",
|
|
20
|
-
Int32: "number",
|
|
21
|
-
UInt16: "number",
|
|
22
|
-
UInt32: "number",
|
|
23
|
-
Boolean: "boolean",
|
|
24
|
-
String: "string",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const ZERO = "00000000-0000-0000-0000-000000000000";
|
|
28
|
-
const MIRAVO_MARKER_PLACEHOLDER = "__miravo_placeholder__";
|
|
29
|
-
|
|
30
|
-
interface StaticAttr {
|
|
31
|
-
readonly id: string;
|
|
32
|
-
readonly parentId: string;
|
|
33
|
-
readonly modelId: string;
|
|
34
|
-
readonly instanceId: string;
|
|
35
|
-
readonly key: string;
|
|
36
|
-
readonly value: string;
|
|
37
|
-
readonly type: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function createStaticAttr(modelId: string, key: string, value: string): StaticAttr {
|
|
41
|
-
return {
|
|
42
|
-
id: crypto.randomUUID(),
|
|
43
|
-
parentId: ZERO,
|
|
44
|
-
modelId,
|
|
45
|
-
instanceId: ZERO,
|
|
46
|
-
key,
|
|
47
|
-
value,
|
|
48
|
-
type: 0,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function buildMiravoMarkerPlaceholders(modelId: string): StaticAttr[] {
|
|
53
|
-
return [
|
|
54
|
-
createStaticAttr(modelId, "miravo_managed", "false"),
|
|
55
|
-
createStaticAttr(modelId, "miravo_template", MIRAVO_MARKER_PLACEHOLDER),
|
|
56
|
-
createStaticAttr(modelId, "miravo_model", MIRAVO_MARKER_PLACEHOLDER),
|
|
57
|
-
createStaticAttr(modelId, "miravo_instance", MIRAVO_MARKER_PLACEHOLDER),
|
|
58
|
-
createStaticAttr(modelId, "uns_path", MIRAVO_MARKER_PLACEHOLDER),
|
|
59
|
-
];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function buildLeDtJsonFromSchema(schema: ModelSchema): string {
|
|
63
|
-
const modelId = crypto.randomUUID();
|
|
64
|
-
|
|
65
|
-
// Static attributes: Miravo marker placeholders + property members.
|
|
66
|
-
// LE materializes instance-owned static attrs from model attrs present at
|
|
67
|
-
// instance creation time, so these placeholders must exist on the model.
|
|
68
|
-
const staticAttributes = [
|
|
69
|
-
...buildMiravoMarkerPlaceholders(modelId),
|
|
70
|
-
...Object.entries(schema.members)
|
|
71
|
-
.filter(([, m]) => m.kind === "property")
|
|
72
|
-
.map(([key, m]) =>
|
|
73
|
-
createStaticAttr(modelId, key, m.default != null ? String(m.default) : "<>"),
|
|
74
|
-
),
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
// Dynamic attributes: readable variable members
|
|
78
|
-
const dynamicAttributes = Object.entries(schema.members)
|
|
79
|
-
.filter(([, m]) => m.kind === "variable")
|
|
80
|
-
.map(([name, m]) => ({
|
|
81
|
-
id: crypto.randomUUID(),
|
|
82
|
-
modelId,
|
|
83
|
-
parentId: ZERO,
|
|
84
|
-
instanceId: ZERO,
|
|
85
|
-
topic: "",
|
|
86
|
-
name,
|
|
87
|
-
unit: m.unit ?? "",
|
|
88
|
-
dataType: LE_DATA_TYPE[m.dataType] ?? "number",
|
|
89
|
-
schemaId: null,
|
|
90
|
-
skipParentKey: false,
|
|
91
|
-
topicExpression: `devicehub.alias.{device}.${name}`,
|
|
92
|
-
}));
|
|
93
|
-
|
|
94
|
-
// Hierarchy: "info" folder → static attrs, "measurements" folder → dynamic attrs
|
|
95
|
-
const hierarchyNodes = buildHierarchy(modelId, staticAttributes, dynamicAttributes);
|
|
96
|
-
|
|
97
|
-
return JSON.stringify(
|
|
98
|
-
[
|
|
99
|
-
{
|
|
100
|
-
id: modelId,
|
|
101
|
-
assets: "ASSET",
|
|
102
|
-
name: schema.name.toUpperCase(),
|
|
103
|
-
description: schema.description ?? "",
|
|
104
|
-
image: null,
|
|
105
|
-
dynamicAttributes,
|
|
106
|
-
staticAttributes,
|
|
107
|
-
hierarchyNodes,
|
|
108
|
-
transformationBackup: null,
|
|
109
|
-
parameters: [{ name: "device", value: "miravo" }],
|
|
110
|
-
isUNS: false,
|
|
111
|
-
connectorName: "",
|
|
112
|
-
version: 0,
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
null,
|
|
116
|
-
2,
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function buildHierarchy(
|
|
121
|
-
modelId: string,
|
|
122
|
-
staticAttrs: ReadonlyArray<{ id: string; key: string }>,
|
|
123
|
-
dynamicAttrs: ReadonlyArray<{ id: string; name: string }>,
|
|
124
|
-
): Array<Record<string, unknown>> {
|
|
125
|
-
const nodes: Array<Record<string, unknown>> = [];
|
|
126
|
-
let rootPos = 0;
|
|
127
|
-
|
|
128
|
-
if (staticAttrs.length > 0) {
|
|
129
|
-
const folderId = crypto.randomUUID();
|
|
130
|
-
nodes.push(hFolder(folderId, modelId, rootPos++, "info"));
|
|
131
|
-
let pos = 0;
|
|
132
|
-
for (const a of staticAttrs) {
|
|
133
|
-
nodes.push(hLeaf(modelId, pos++, folderId, a.key, a.id, "static"));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (dynamicAttrs.length > 0) {
|
|
138
|
-
const folderId = crypto.randomUUID();
|
|
139
|
-
nodes.push(hFolder(folderId, modelId, rootPos, "measurements"));
|
|
140
|
-
let pos = 0;
|
|
141
|
-
for (const a of dynamicAttrs) {
|
|
142
|
-
nodes.push(hLeaf(modelId, pos++, folderId, a.name, a.id, "dynamic"));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return nodes;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function hFolder(
|
|
150
|
-
id: string,
|
|
151
|
-
modelId: string,
|
|
152
|
-
position: number,
|
|
153
|
-
name: string,
|
|
154
|
-
): Record<string, unknown> {
|
|
155
|
-
return {
|
|
156
|
-
id,
|
|
157
|
-
modelId,
|
|
158
|
-
position,
|
|
159
|
-
parentId: null,
|
|
160
|
-
name,
|
|
161
|
-
isFolder: true,
|
|
162
|
-
attributeId: null,
|
|
163
|
-
attributeType: null,
|
|
164
|
-
nodeType: "folder",
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function hLeaf(
|
|
169
|
-
modelId: string,
|
|
170
|
-
position: number,
|
|
171
|
-
parentId: string,
|
|
172
|
-
name: string,
|
|
173
|
-
attributeId: string,
|
|
174
|
-
attributeType: string,
|
|
175
|
-
): Record<string, unknown> {
|
|
176
|
-
return {
|
|
177
|
-
id: crypto.randomUUID(),
|
|
178
|
-
modelId,
|
|
179
|
-
position,
|
|
180
|
-
parentId,
|
|
181
|
-
name,
|
|
182
|
-
isFolder: false,
|
|
183
|
-
attributeId,
|
|
184
|
-
attributeType,
|
|
185
|
-
nodeType: "attribute",
|
|
186
|
-
};
|
|
187
|
-
}
|