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,343 +0,0 @@
1
- /**
2
- * Daemon registry — tracks background simulation processes.
3
- *
4
- * Each background simulation gets a JSON entry at `~/.miravo/daemon/<name>.json`.
5
- * Management commands (ps, stop, logs, rm) use this registry to discover and
6
- * interact with running simulations via their admin HTTP server.
7
- */
8
-
9
- import { mkdir, open, readdir, rename, unlink } from "node:fs/promises";
10
- import { resolveMiravoHome } from "@miravo/core";
11
- import { MiravoError, SimulationNameSchema } from "@miravo/shared";
12
- import { z } from "zod";
13
-
14
- // ---------------------------------------------------------------------------
15
- // Schema (CLI-only — not in packages/shared/)
16
- // ---------------------------------------------------------------------------
17
-
18
- const DaemonAdapterEntrySchema = z.object({
19
- port: z.number().int().positive(),
20
- host: z.string().min(1),
21
- });
22
-
23
- const DaemonRegistryEntrySchema = z.object({
24
- name: z.string().min(1),
25
- pid: z.number().int().positive(),
26
- adminPort: z.number().int().positive(),
27
- adminHost: z.string().min(1),
28
- adapters: z.record(z.string(), DaemonAdapterEntrySchema),
29
- templatePath: z.string().min(1),
30
- startedAt: z.string().min(1),
31
- seed: z.number().finite(),
32
- runId: z.string().min(1),
33
- });
34
-
35
- type DaemonAdapterEntry = z.infer<typeof DaemonAdapterEntrySchema>;
36
- export type DaemonRegistryEntry = z.infer<typeof DaemonRegistryEntrySchema>;
37
-
38
- interface EntryIdentityResult {
39
- readonly status: "dead" | "verified" | "mismatch" | "unreachable";
40
- readonly ready: boolean;
41
- }
42
-
43
- // ---------------------------------------------------------------------------
44
- // Directory resolution
45
- // ---------------------------------------------------------------------------
46
-
47
- /** Returns the path to the daemon registry directory (`~/.miravo/daemon/`). */
48
- function daemonDir(): string {
49
- return `${resolveMiravoHome()}/daemon`;
50
- }
51
-
52
- /**
53
- * Validate a simulation name to prevent path traversal.
54
- * Reuses the shared SimulationNameSchema (lowercase alphanumeric + hyphens).
55
- */
56
- function validateName(name: string): void {
57
- const result = SimulationNameSchema.safeParse(name);
58
- if (!result.success) {
59
- throw new MiravoError(
60
- `Invalid simulation name: "${name}".`,
61
- "Use lowercase letters, numbers, and hyphens only (e.g., my-sim-1).",
62
- );
63
- }
64
- }
65
-
66
- function entryPath(name: string): string {
67
- validateName(name);
68
- return `${daemonDir()}/${name}.json`;
69
- }
70
-
71
- // ---------------------------------------------------------------------------
72
- // CRUD operations
73
- // ---------------------------------------------------------------------------
74
-
75
- /** Write a registry entry atomically (temp file + rename). */
76
- export async function writeEntry(entry: DaemonRegistryEntry): Promise<void> {
77
- const dir = daemonDir();
78
- await mkdir(dir, { recursive: true });
79
- const filePath = entryPath(entry.name);
80
- const tmpPath = `${filePath}.${process.pid}.tmp`;
81
- await Bun.write(tmpPath, JSON.stringify(entry, null, 2));
82
- await rename(tmpPath, filePath);
83
- }
84
-
85
- /**
86
- * Acquire an exclusive lock for a simulation name.
87
- * Prevents concurrent `start -d` from spawning duplicate daemons.
88
- * Returns a release function; call it when done (even on error).
89
- *
90
- * Stale locks from crashed processes are detected via PID liveness and
91
- * cleaned up automatically before retrying.
92
- *
93
- * @throws MiravoError if the name is already locked by a live process.
94
- */
95
- export async function acquireNameLock(name: string): Promise<() => Promise<void>> {
96
- validateName(name);
97
- const dir = daemonDir();
98
- await mkdir(dir, { recursive: true });
99
- const lockPath = `${dir}/${name}.lock`;
100
-
101
- const tryAcquire = async (): Promise<void> => {
102
- try {
103
- // O_CREAT | O_EXCL — fails if file already exists
104
- const handle = await open(lockPath, "wx");
105
- await handle.writeFile(String(process.pid));
106
- await handle.close();
107
- } catch (error) {
108
- if ((error as NodeJS.ErrnoException).code === "EEXIST") {
109
- // Check if the lock belongs to a live process or is stale (crashed).
110
- try {
111
- const content = await Bun.file(lockPath).text();
112
- const pid = parseInt(content.trim(), 10);
113
- if (!Number.isNaN(pid) && !isPidAlive(pid)) {
114
- // Stale lock from a crashed process — remove and retry.
115
- await unlink(lockPath);
116
- return tryAcquire();
117
- }
118
- } catch {
119
- // Lock file disappeared between EEXIST and reading — retry.
120
- return tryAcquire();
121
- }
122
- throw new MiravoError(
123
- `Another 'miravo start -d' is already starting simulation "${name}".`,
124
- "Wait for the other start to complete, or remove the lock with 'miravo rm'.",
125
- );
126
- }
127
- throw error;
128
- }
129
- };
130
-
131
- await tryAcquire();
132
-
133
- return async () => {
134
- try {
135
- await unlink(lockPath);
136
- } catch {
137
- // Ignore — lock may have been cleaned up already
138
- }
139
- };
140
- }
141
-
142
- /** Read and validate a registry entry. Returns null if missing or invalid. */
143
- export async function readEntry(name: string): Promise<DaemonRegistryEntry | null> {
144
- const filePath = entryPath(name);
145
- const file = Bun.file(filePath);
146
- if (!(await file.exists())) return null;
147
- try {
148
- const content = await file.text();
149
- const parsed: unknown = JSON.parse(content);
150
- const result = DaemonRegistryEntrySchema.safeParse(parsed);
151
- return result.success ? result.data : null;
152
- } catch {
153
- return null;
154
- }
155
- }
156
-
157
- /** Remove a registry entry. Safe if file does not exist. */
158
- export async function removeEntry(name: string): Promise<void> {
159
- try {
160
- await unlink(entryPath(name));
161
- } catch (error) {
162
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
163
- }
164
- }
165
-
166
- /** List all valid registry entries. Invalid files are silently skipped. */
167
- export async function listEntries(): Promise<readonly DaemonRegistryEntry[]> {
168
- const dir = daemonDir();
169
- let files: string[];
170
- try {
171
- files = await readdir(dir);
172
- } catch (error) {
173
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
174
- throw error;
175
- }
176
-
177
- const entries: DaemonRegistryEntry[] = [];
178
- for (const file of files) {
179
- if (!file.endsWith(".json") || file.endsWith(".tmp")) continue;
180
- const name = file.slice(0, -5); // strip .json
181
- const entry = await readEntry(name);
182
- if (entry) entries.push(entry);
183
- }
184
- return entries;
185
- }
186
-
187
- // ---------------------------------------------------------------------------
188
- // Liveness checks
189
- // ---------------------------------------------------------------------------
190
-
191
- /** Check if a PID is alive using signal 0. */
192
- export function isPidAlive(pid: number): boolean {
193
- try {
194
- process.kill(pid, 0);
195
- return true;
196
- } catch (err) {
197
- if (
198
- err &&
199
- typeof err === "object" &&
200
- "code" in err &&
201
- (err.code === "EPERM" || err.code === "EACCES")
202
- ) {
203
- return true; // process exists but we can't signal it
204
- }
205
- return false;
206
- }
207
- }
208
-
209
- /**
210
- * Verify that a registry entry corresponds to a live Miravo simulation.
211
- *
212
- * Two-step check:
213
- * 1. PID alive (fast, avoids network I/O for dead processes)
214
- * 2. RunId matches via GET /readyz (guards against PID reuse after reboot)
215
- */
216
- export async function probeEntryIdentity(entry: DaemonRegistryEntry): Promise<EntryIdentityResult> {
217
- if (!isPidAlive(entry.pid)) {
218
- return { status: "dead", ready: false };
219
- }
220
-
221
- try {
222
- const url = `http://${entry.adminHost}:${entry.adminPort}/readyz`;
223
- const response = await fetch(url, { signal: AbortSignal.timeout(2_000) });
224
- const bodyText = await response.text();
225
- let body: unknown;
226
- try {
227
- body = JSON.parse(bodyText) as unknown;
228
- } catch {
229
- return { status: "mismatch", ready: false };
230
- }
231
- if (
232
- typeof body !== "object" ||
233
- body === null ||
234
- !("runId" in body) ||
235
- typeof body["runId"] !== "string"
236
- ) {
237
- return { status: "mismatch", ready: false };
238
- }
239
- if (body["runId"] !== entry.runId) {
240
- return { status: "mismatch", ready: false };
241
- }
242
- return { status: "verified", ready: response.ok };
243
- } catch {
244
- // Keep the registry entry when the admin server is temporarily unreachable,
245
- // but do not treat the identity as safe for raw signal delivery.
246
- return { status: "unreachable", ready: false };
247
- }
248
- }
249
-
250
- /**
251
- * Remove all registry entries whose processes are no longer alive.
252
- * Uses full identity verification (PID + runId) to handle PID reuse.
253
- */
254
- export async function sweepStaleEntries(): Promise<void> {
255
- const entries = await listEntries();
256
- for (const entry of entries) {
257
- const identity = await probeEntryIdentity(entry);
258
- if (identity.status === "dead" || identity.status === "mismatch") {
259
- await removeEntry(entry.name);
260
- }
261
- }
262
- }
263
-
264
- // ---------------------------------------------------------------------------
265
- // Conflict detection
266
- // ---------------------------------------------------------------------------
267
-
268
- /**
269
- * Check for port conflicts against live registry entries.
270
- *
271
- * @param ports - Map of label → port to check (e.g., "admin" → 8080, "mqtt" → 1883)
272
- * @param excludeName - Optional entry name to exclude (for re-checking own entry)
273
- * @returns Array of human-readable conflict messages (empty = no conflicts)
274
- */
275
- export async function checkPortConflicts(
276
- ports: ReadonlyMap<string, number>,
277
- excludeName?: string,
278
- ): Promise<readonly string[]> {
279
- const entries = await listEntries();
280
- const conflicts: string[] = [];
281
-
282
- for (const entry of entries) {
283
- if (entry.name === excludeName) continue;
284
- // Use PID check only (not full identity verification) for performance —
285
- // port conflicts are about actual port usage, not Miravo identity.
286
- if (!isPidAlive(entry.pid)) continue;
287
-
288
- // Check admin port
289
- for (const [label, port] of ports) {
290
- if (port === entry.adminPort) {
291
- conflicts.push(
292
- `Port ${port} (${label}) is already in use by simulation "${entry.name}". Stop it first: miravo stop ${entry.name}`,
293
- );
294
- }
295
- // Check adapter ports
296
- for (const [adapterName, adapter] of Object.entries(entry.adapters) as [
297
- string,
298
- DaemonAdapterEntry,
299
- ][]) {
300
- if (port === adapter.port) {
301
- conflicts.push(
302
- `Port ${port} (${label}) conflicts with ${adapterName} port of simulation "${entry.name}". Stop it first: miravo stop ${entry.name}`,
303
- );
304
- }
305
- }
306
- }
307
- }
308
-
309
- return conflicts;
310
- }
311
-
312
- /**
313
- * Check if a simulation name is already in use by a live entry.
314
- * Removes stale entries as a side effect.
315
- *
316
- * @returns The alive entry if the name is taken, null otherwise.
317
- */
318
- export async function checkNameConflict(name: string): Promise<DaemonRegistryEntry | null> {
319
- const entry = await readEntry(name);
320
- if (!entry) return null;
321
-
322
- const identity = await probeEntryIdentity(entry);
323
- if (identity.status === "verified" || identity.status === "unreachable") return entry;
324
-
325
- if (identity.status === "dead" || identity.status === "mismatch") {
326
- // Stale entry — clean it up
327
- await removeEntry(name);
328
- }
329
- return null;
330
- }
331
-
332
- // ---------------------------------------------------------------------------
333
- // Display helpers
334
- // ---------------------------------------------------------------------------
335
-
336
- /**
337
- * Map bind addresses to connectable addresses for display.
338
- * `0.0.0.0` and `::` are replaced with `127.0.0.1`.
339
- */
340
- export function displayHost(host: string): string {
341
- if (host === "0.0.0.0" || host === "::") return "127.0.0.1";
342
- return host;
343
- }