@adhdev/mesh-shared 1.0.49-rc.3 → 1.0.49-rc.4

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.
@@ -33,6 +33,73 @@ import { readString } from './json'
33
33
 
34
34
  const DAEMON_ID_PREFIXES = ['daemon_', 'standalone_'] as const
35
35
 
36
+ /**
37
+ * A daemon DO addressed by a raw 64-hex DO id (`idFromString`) rather than by a
38
+ * canonical name (`idFromName("daemon_mach_<hex>")`). Decide by FORMAT, not by
39
+ * length — the canonical name is 43+ chars, so a `length > 32` heuristic would
40
+ * misclassify it. Mirrors the server's session-routing `isRawDoId`.
41
+ */
42
+ export function isRawDaemonDoId(id: string | null | undefined): boolean {
43
+ const trimmed = readString(id)
44
+ return !!trimmed && /^[0-9a-f]{64}$/i.test(trimmed)
45
+ }
46
+
47
+ /** The machine-evidence fields a real daemon reports about its hardware. */
48
+ export interface DaemonMachineEvidence {
49
+ machineNickname?: string | null
50
+ nickname?: string | null
51
+ hostname?: string | null
52
+ platform?: string | null
53
+ machineId?: string | null
54
+ machine?: { hostname?: string | null; platform?: string | null } | null
55
+ sessions?: unknown[] | null
56
+ }
57
+
58
+ /**
59
+ * A phantom daemon entry — a UI surface must not offer it as a real machine.
60
+ *
61
+ * Symptom block for the raw-DO-id ghost (GHOST-MACHINE-REGISTRATIONS). When the
62
+ * `X-ADHDEV-Daemon` instanceId header is missing or unparseable, the /ws handler
63
+ * falls back to a random DO name, so every reconnect mints a FRESH raw 64-hex DO
64
+ * id. Those keys accumulate as hardware-less entries that render as a bare hash
65
+ * where a machine name belongs.
66
+ *
67
+ * BOTH conditions are required, and the second is what keeps this safe:
68
+ * 1. the id is a raw DO id — no `daemon_`/`standalone_` canonical prefix.
69
+ * 2. no machine evidence at all — no nickname, hostname, platform, registered
70
+ * machineId, and no sessions.
71
+ *
72
+ * A legacy / not-yet-reauthed daemon that ACTUALLY reports itself fails (2) and
73
+ * therefore still renders, still attaches, and still routes — dropping on (1)
74
+ * alone would make real daemons disappear from the picker. This is strictly a
75
+ * PRESENTATION filter: routing tables (server `lastStatuses`, P2P relay) keep
76
+ * the entry, because the raw key still resolves via `idFromString`.
77
+ *
78
+ * The server applies the same rule to its dashboard payloads via
79
+ * `_unresolvedIdentity` (UserSession.isPhantomMachineEntry). That marker is
80
+ * server-internal and never reaches the browser, and the client daemon store
81
+ * never evicts an entry once injected — so a client surface reading the merged
82
+ * P2P/WS store must re-derive the verdict from the entry SHAPE. This function is
83
+ * that shared rule.
84
+ */
85
+ export function isPhantomDaemonEntry(
86
+ entry: (DaemonMachineEvidence & { id?: string | null }) | null | undefined,
87
+ ): boolean {
88
+ if (!entry) return false
89
+ if (!isRawDaemonDoId(entry.id)) return false
90
+ const hasMachineEvidence = Boolean(
91
+ readString(entry.machineNickname)
92
+ || readString(entry.nickname)
93
+ || readString(entry.hostname)
94
+ || readString(entry.platform)
95
+ || readString(entry.machine?.hostname)
96
+ || readString(entry.machine?.platform)
97
+ || readString(entry.machineId)
98
+ || (Array.isArray(entry.sessions) && entry.sessions.length > 0),
99
+ )
100
+ return !hasMachineEvidence
101
+ }
102
+
36
103
  /**
37
104
  * Reduce any daemon-id form to its machine core: strips a leading `daemon_` /
38
105
  * `standalone_` prefix, leaving the bare `mach_<hex>` (or returning a non-prefixed