claudemesh-cli 1.34.15 → 1.34.18

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.
@@ -104,7 +104,7 @@ __export(exports_urls, {
104
104
  VERSION: () => VERSION,
105
105
  URLS: () => URLS
106
106
  });
107
- var URLS, VERSION = "1.34.15", env;
107
+ var URLS, VERSION = "1.34.18", env;
108
108
  var init_urls = __esm(() => {
109
109
  URLS = {
110
110
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -10899,6 +10899,10 @@ function connectWsWithBackoff(opts) {
10899
10899
  status = s;
10900
10900
  opts.onStatusChange?.(s);
10901
10901
  };
10902
+ const PING_INTERVAL_MS = 30000;
10903
+ const STALE_THRESHOLD_MS = 75000;
10904
+ let lastActivity = Date.now();
10905
+ let watchdogTimer = null;
10902
10906
  const openOnce = () => {
10903
10907
  if (closed)
10904
10908
  return Promise.reject(new Error("client_closed"));
@@ -10906,6 +10910,7 @@ function connectWsWithBackoff(opts) {
10906
10910
  log2("info", "ws_open_attempt", { url: opts.url });
10907
10911
  const sock = new WebSocket2(opts.url);
10908
10912
  ws = sock;
10913
+ lastActivity = Date.now();
10909
10914
  return new Promise((resolve, reject) => {
10910
10915
  sock.on("open", () => {
10911
10916
  log2("info", "ws_open_ok", { url: opts.url });
@@ -10928,6 +10933,7 @@ function connectWsWithBackoff(opts) {
10928
10933
  })();
10929
10934
  });
10930
10935
  sock.on("message", (raw) => {
10936
+ lastActivity = Date.now();
10931
10937
  let msg;
10932
10938
  try {
10933
10939
  msg = JSON.parse(raw.toString());
@@ -10942,16 +10948,43 @@ function connectWsWithBackoff(opts) {
10942
10948
  setStatus("open");
10943
10949
  reconnectAttempt = 0;
10944
10950
  log2("info", "ws_hello_acked", { url: opts.url });
10951
+ if (watchdogTimer)
10952
+ clearInterval(watchdogTimer);
10953
+ watchdogTimer = setInterval(() => {
10954
+ if (sock.readyState !== sock.OPEN)
10955
+ return;
10956
+ const idle = Date.now() - lastActivity;
10957
+ if (idle > STALE_THRESHOLD_MS) {
10958
+ log2("warn", "ws_stale_terminate", { url: opts.url, idle_ms: idle });
10959
+ try {
10960
+ sock.terminate();
10961
+ } catch {}
10962
+ return;
10963
+ }
10964
+ try {
10965
+ sock.ping();
10966
+ } catch {}
10967
+ }, PING_INTERVAL_MS);
10945
10968
  resolve();
10946
10969
  return;
10947
10970
  }
10948
10971
  opts.onMessage(msg);
10949
10972
  });
10973
+ sock.on("ping", () => {
10974
+ lastActivity = Date.now();
10975
+ });
10976
+ sock.on("pong", () => {
10977
+ lastActivity = Date.now();
10978
+ });
10950
10979
  sock.on("close", (code, reason) => {
10951
10980
  if (helloTimer) {
10952
10981
  clearTimeout(helloTimer);
10953
10982
  helloTimer = null;
10954
10983
  }
10984
+ if (watchdogTimer) {
10985
+ clearInterval(watchdogTimer);
10986
+ watchdogTimer = null;
10987
+ }
10955
10988
  const reasonStr = reason.toString("utf8");
10956
10989
  log2("warn", "ws_closed", { url: opts.url, code, reason: reasonStr, status });
10957
10990
  opts.onBeforeReconnect?.(code, reasonStr);
@@ -10995,6 +11028,10 @@ function connectWsWithBackoff(opts) {
10995
11028
  clearTimeout(helloTimer);
10996
11029
  helloTimer = null;
10997
11030
  }
11031
+ if (watchdogTimer) {
11032
+ clearInterval(watchdogTimer);
11033
+ watchdogTimer = null;
11034
+ }
10998
11035
  try {
10999
11036
  ws?.close();
11000
11037
  } catch {}
@@ -11994,24 +12031,28 @@ var init_inbound = __esm(() => {
11994
12031
  // src/daemon/identity.ts
11995
12032
  var exports_identity = {};
11996
12033
  __export(exports_identity, {
12034
+ pickStableMacFromInterfaces: () => pickStableMacFromInterfaces,
12035
+ fingerprintV2: () => fingerprintV2,
11997
12036
  computeCurrentFingerprint: () => computeCurrentFingerprint,
11998
12037
  checkFingerprint: () => checkFingerprint,
11999
- acceptCurrentHost: () => acceptCurrentHost
12038
+ acceptCurrentHost: () => acceptCurrentHost,
12039
+ __resetHostIdCacheForTests: () => __resetHostIdCacheForTests,
12040
+ __computeV1FingerprintForTests: () => __computeV1FingerprintForTests
12000
12041
  });
12001
12042
  import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "node:fs";
12002
12043
  import { join as join7 } from "node:path";
12003
12044
  import { createHash as createHash2 } from "node:crypto";
12045
+ import { execFileSync } from "node:child_process";
12004
12046
  import { networkInterfaces } from "node:os";
12005
12047
  function path() {
12006
12048
  return join7(DAEMON_PATHS.DAEMON_DIR, FILE_NAME);
12007
12049
  }
12008
12050
  function computeCurrentFingerprint() {
12009
- const host_id = readHostId() ?? "";
12010
- const stable_mac = pickStableMac() ?? "";
12011
- const fp = createHash2("sha256").update(host_id, "utf8").update("\x00").update(stable_mac, "utf8").digest("hex");
12051
+ const host_id = readHostIdV2() ?? "";
12052
+ const stable_mac = pickStableMacFromInterfaces(networkInterfaces()) ?? "";
12012
12053
  return {
12013
- schema_version: 1,
12014
- fingerprint: fp,
12054
+ schema_version: CURRENT_SCHEMA,
12055
+ fingerprint: fingerprintV2(host_id, stable_mac),
12015
12056
  host_id,
12016
12057
  stable_mac,
12017
12058
  written_at: new Date().toISOString()
@@ -12029,16 +12070,74 @@ function checkFingerprint() {
12029
12070
  } catch {
12030
12071
  return { result: "unavailable", current };
12031
12072
  }
12032
- if (stored.fingerprint === current.fingerprint)
12033
- return { result: "match", current, stored };
12034
- return { result: "mismatch", current, stored };
12073
+ if (stored.schema_version === 2) {
12074
+ if (stored.fingerprint === current.fingerprint)
12075
+ return { result: "match", current, stored };
12076
+ if (stored.host_id && stored.host_id === current.host_id) {
12077
+ writeFileSync10(path(), JSON.stringify(current, null, 2), { mode: 384 });
12078
+ return { result: "match", current, stored };
12079
+ }
12080
+ return { result: "mismatch", current, stored };
12081
+ }
12082
+ if (stored.schema_version === 1) {
12083
+ const v1 = computeCurrentFingerprintV1();
12084
+ if (stored.fingerprint === v1.fingerprint) {
12085
+ writeFileSync10(path(), JSON.stringify(current, null, 2), { mode: 384 });
12086
+ return { result: "match", current, stored };
12087
+ }
12088
+ return { result: "mismatch", current, stored };
12089
+ }
12090
+ return { result: "unavailable", current };
12035
12091
  }
12036
12092
  function acceptCurrentHost() {
12037
12093
  const current = computeCurrentFingerprint();
12038
12094
  writeFileSync10(path(), JSON.stringify(current, null, 2), { mode: 384 });
12039
12095
  return current;
12040
12096
  }
12041
- function readHostId() {
12097
+ function fingerprintV2(host_id, stable_mac) {
12098
+ return createHash2("sha256").update("v2", "utf8").update("\x00").update(host_id, "utf8").update("\x00").update(stable_mac, "utf8").digest("hex");
12099
+ }
12100
+ function pickStableMacFromInterfaces(ifs) {
12101
+ const hardware = [];
12102
+ const fallback = [];
12103
+ for (const [name, addrs] of Object.entries(ifs)) {
12104
+ if (!addrs)
12105
+ continue;
12106
+ if (isIgnoredInterface(name))
12107
+ continue;
12108
+ for (const a of addrs) {
12109
+ if (a.internal)
12110
+ continue;
12111
+ if (!a.mac || a.mac === "00:00:00:00:00:00")
12112
+ continue;
12113
+ const entry = { name, mac: a.mac };
12114
+ if (isLocallyAdministered(a.mac)) {
12115
+ fallback.push(entry);
12116
+ } else {
12117
+ hardware.push(entry);
12118
+ }
12119
+ break;
12120
+ }
12121
+ }
12122
+ const pool = hardware.length > 0 ? hardware : fallback;
12123
+ if (pool.length === 0)
12124
+ return null;
12125
+ pool.sort((a, b) => a.name.localeCompare(b.name));
12126
+ return pool[0].mac;
12127
+ }
12128
+ function computeCurrentFingerprintV1() {
12129
+ const host_id = readHostIdV1() ?? "";
12130
+ const stable_mac = pickStableMacV1() ?? "";
12131
+ const fp = createHash2("sha256").update(host_id, "utf8").update("\x00").update(stable_mac, "utf8").digest("hex");
12132
+ return {
12133
+ schema_version: 1,
12134
+ fingerprint: fp,
12135
+ host_id,
12136
+ stable_mac,
12137
+ written_at: new Date().toISOString()
12138
+ };
12139
+ }
12140
+ function readHostIdV1() {
12042
12141
  if (process.platform === "linux") {
12043
12142
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
12044
12143
  try {
@@ -12049,18 +12148,15 @@ function readHostId() {
12049
12148
  }
12050
12149
  return null;
12051
12150
  }
12052
- if (process.platform === "darwin") {
12053
- return null;
12054
- }
12055
12151
  return null;
12056
12152
  }
12057
- function pickStableMac() {
12153
+ function pickStableMacV1() {
12058
12154
  const ifs = networkInterfaces();
12059
12155
  const candidates = [];
12060
12156
  for (const [name, addrs] of Object.entries(ifs)) {
12061
12157
  if (!addrs)
12062
12158
  continue;
12063
- if (isIgnoredInterface(name))
12159
+ if (isIgnoredInterfaceV1(name))
12064
12160
  continue;
12065
12161
  for (const a of addrs) {
12066
12162
  if (a.internal)
@@ -12078,10 +12174,57 @@ function pickStableMac() {
12078
12174
  const idx = first.indexOf("::");
12079
12175
  return idx >= 0 ? first.slice(idx + 2) : first;
12080
12176
  }
12081
- function isIgnoredInterface(name) {
12177
+ function isIgnoredInterfaceV1(name) {
12082
12178
  return /^(lo|docker|br-|veth|tap|tun|tailscale|wg|utun|ppp|vboxnet|vmnet|awdl|llw)/i.test(name);
12083
12179
  }
12084
- var FILE_NAME = "host_fingerprint.json";
12180
+ function readHostIdV2() {
12181
+ if (cachedHostIdV2 !== undefined)
12182
+ return cachedHostIdV2;
12183
+ cachedHostIdV2 = readHostIdV2Uncached();
12184
+ return cachedHostIdV2;
12185
+ }
12186
+ function readHostIdV2Uncached() {
12187
+ if (process.platform === "linux") {
12188
+ for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
12189
+ try {
12190
+ const raw = readFileSync9(p, "utf8").trim();
12191
+ if (raw)
12192
+ return `linux:${raw}`;
12193
+ } catch {}
12194
+ }
12195
+ return null;
12196
+ }
12197
+ if (process.platform === "darwin") {
12198
+ try {
12199
+ const out = execFileSync("/usr/sbin/ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
12200
+ encoding: "utf8",
12201
+ timeout: 2000,
12202
+ stdio: ["ignore", "pipe", "ignore"]
12203
+ });
12204
+ const m = out.match(/"IOPlatformUUID"\s*=\s*"([0-9A-Fa-f-]+)"/);
12205
+ if (m && m[1])
12206
+ return `darwin:${m[1]}`;
12207
+ } catch {}
12208
+ return null;
12209
+ }
12210
+ return null;
12211
+ }
12212
+ function isLocallyAdministered(mac) {
12213
+ const firstByte = parseInt(mac.split(":")[0] ?? "0", 16);
12214
+ if (Number.isNaN(firstByte))
12215
+ return false;
12216
+ return (firstByte & 2) !== 0;
12217
+ }
12218
+ function isIgnoredInterface(name) {
12219
+ return /^(lo|docker|br-|bridge|veth|tap|tun|tailscale|wg|utun|ppp|vboxnet|vmnet|awdl|llw|anpi|ap\d)/i.test(name);
12220
+ }
12221
+ function __resetHostIdCacheForTests() {
12222
+ cachedHostIdV2 = undefined;
12223
+ }
12224
+ function __computeV1FingerprintForTests() {
12225
+ return computeCurrentFingerprintV1();
12226
+ }
12227
+ var FILE_NAME = "host_fingerprint.json", CURRENT_SCHEMA = 2, cachedHostIdV2;
12085
12228
  var init_identity = __esm(() => {
12086
12229
  init_paths2();
12087
12230
  });
@@ -21249,4 +21392,4 @@ main().catch((err) => {
21249
21392
  process.exit(EXIT.INTERNAL_ERROR);
21250
21393
  });
21251
21394
 
21252
- //# debugId=89AC9B5F79C7438764756E2164756E21
21395
+ //# debugId=C8B9D377DC161E3E64756E2164756E21