@adhdev/mesh-shared 1.0.49-rc.2 → 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.
@@ -28,6 +28,56 @@
28
28
  * single machine core (`daemon_mach_X` only ever expands to other `mach_X` forms),
29
29
  * so an event scoped to a DIFFERENT coordinator is never falsely claimed.
30
30
  */
31
+ /**
32
+ * A daemon DO addressed by a raw 64-hex DO id (`idFromString`) rather than by a
33
+ * canonical name (`idFromName("daemon_mach_<hex>")`). Decide by FORMAT, not by
34
+ * length — the canonical name is 43+ chars, so a `length > 32` heuristic would
35
+ * misclassify it. Mirrors the server's session-routing `isRawDoId`.
36
+ */
37
+ export declare function isRawDaemonDoId(id: string | null | undefined): boolean;
38
+ /** The machine-evidence fields a real daemon reports about its hardware. */
39
+ export interface DaemonMachineEvidence {
40
+ machineNickname?: string | null;
41
+ nickname?: string | null;
42
+ hostname?: string | null;
43
+ platform?: string | null;
44
+ machineId?: string | null;
45
+ machine?: {
46
+ hostname?: string | null;
47
+ platform?: string | null;
48
+ } | null;
49
+ sessions?: unknown[] | null;
50
+ }
51
+ /**
52
+ * A phantom daemon entry — a UI surface must not offer it as a real machine.
53
+ *
54
+ * Symptom block for the raw-DO-id ghost (GHOST-MACHINE-REGISTRATIONS). When the
55
+ * `X-ADHDEV-Daemon` instanceId header is missing or unparseable, the /ws handler
56
+ * falls back to a random DO name, so every reconnect mints a FRESH raw 64-hex DO
57
+ * id. Those keys accumulate as hardware-less entries that render as a bare hash
58
+ * where a machine name belongs.
59
+ *
60
+ * BOTH conditions are required, and the second is what keeps this safe:
61
+ * 1. the id is a raw DO id — no `daemon_`/`standalone_` canonical prefix.
62
+ * 2. no machine evidence at all — no nickname, hostname, platform, registered
63
+ * machineId, and no sessions.
64
+ *
65
+ * A legacy / not-yet-reauthed daemon that ACTUALLY reports itself fails (2) and
66
+ * therefore still renders, still attaches, and still routes — dropping on (1)
67
+ * alone would make real daemons disappear from the picker. This is strictly a
68
+ * PRESENTATION filter: routing tables (server `lastStatuses`, P2P relay) keep
69
+ * the entry, because the raw key still resolves via `idFromString`.
70
+ *
71
+ * The server applies the same rule to its dashboard payloads via
72
+ * `_unresolvedIdentity` (UserSession.isPhantomMachineEntry). That marker is
73
+ * server-internal and never reaches the browser, and the client daemon store
74
+ * never evicts an entry once injected — so a client surface reading the merged
75
+ * P2P/WS store must re-derive the verdict from the entry SHAPE. This function is
76
+ * that shared rule.
77
+ */
78
+ export declare function isPhantomDaemonEntry(entry: (DaemonMachineEvidence & {
79
+ id?: string | null;
80
+ }) | null | undefined): boolean;
31
81
  /**
32
82
  * Reduce any daemon-id form to its machine core: strips a leading `daemon_` /
33
83
  * `standalone_` prefix, leaving the bare `mach_<hex>` (or returning a non-prefixed
package/dist/index.js CHANGED
@@ -42,6 +42,8 @@ __export(index_exports, {
42
42
  interpolateArgs: () => interpolateArgs,
43
43
  interpolateString: () => interpolateString,
44
44
  isMeshTaskDifficulty: () => isMeshTaskDifficulty,
45
+ isPhantomDaemonEntry: () => isPhantomDaemonEntry,
46
+ isRawDaemonDoId: () => isRawDaemonDoId,
45
47
  joinRepoPath: () => joinRepoPath,
46
48
  machineCoreFromDaemonId: () => machineCoreFromDaemonId,
47
49
  meshNodeIdMatches: () => meshNodeIdMatches,
@@ -359,6 +361,18 @@ function meshWorkspacesEquivalent(a, b) {
359
361
 
360
362
  // src/daemon-normalize.ts
361
363
  var DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
364
+ function isRawDaemonDoId(id) {
365
+ const trimmed = readString(id);
366
+ return !!trimmed && /^[0-9a-f]{64}$/i.test(trimmed);
367
+ }
368
+ function isPhantomDaemonEntry(entry) {
369
+ if (!entry) return false;
370
+ if (!isRawDaemonDoId(entry.id)) return false;
371
+ const hasMachineEvidence = Boolean(
372
+ readString(entry.machineNickname) || readString(entry.nickname) || readString(entry.hostname) || readString(entry.platform) || readString(entry.machine?.hostname) || readString(entry.machine?.platform) || readString(entry.machineId) || Array.isArray(entry.sessions) && entry.sessions.length > 0
373
+ );
374
+ return !hasMachineEvidence;
375
+ }
362
376
  function machineCoreFromDaemonId(id) {
363
377
  const trimmed = readString(id);
364
378
  if (!trimmed) return void 0;
@@ -808,6 +822,8 @@ function stripStatusProbeMarker(args) {
808
822
  interpolateArgs,
809
823
  interpolateString,
810
824
  isMeshTaskDifficulty,
825
+ isPhantomDaemonEntry,
826
+ isRawDaemonDoId,
811
827
  joinRepoPath,
812
828
  machineCoreFromDaemonId,
813
829
  meshNodeIdMatches,