claudemesh-cli 1.21.1 → 1.22.0

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.
@@ -88,7 +88,7 @@ __export(exports_urls, {
88
88
  VERSION: () => VERSION,
89
89
  URLS: () => URLS
90
90
  });
91
- var URLS, VERSION = "1.21.1", env;
91
+ var URLS, VERSION = "1.22.0", env;
92
92
  var init_urls = __esm(() => {
93
93
  URLS = {
94
94
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -6834,6 +6834,154 @@ var init_peers = __esm(() => {
6834
6834
  };
6835
6835
  });
6836
6836
 
6837
+ // src/daemon/paths.ts
6838
+ import { join as join6 } from "node:path";
6839
+ var DAEMON_PATHS, DAEMON_TCP_HOST = "127.0.0.1", DAEMON_TCP_DEFAULT_PORT = 47823;
6840
+ var init_paths2 = __esm(() => {
6841
+ init_paths();
6842
+ DAEMON_PATHS = {
6843
+ get DAEMON_DIR() {
6844
+ return join6(PATHS.CONFIG_DIR, "daemon");
6845
+ },
6846
+ get PID_FILE() {
6847
+ return join6(this.DAEMON_DIR, "daemon.pid");
6848
+ },
6849
+ get SOCK_FILE() {
6850
+ return join6(this.DAEMON_DIR, "daemon.sock");
6851
+ },
6852
+ get TOKEN_FILE() {
6853
+ return join6(this.DAEMON_DIR, "local-token");
6854
+ },
6855
+ get OUTBOX_DB() {
6856
+ return join6(this.DAEMON_DIR, "outbox.db");
6857
+ },
6858
+ get INBOX_DB() {
6859
+ return join6(this.DAEMON_DIR, "inbox.db");
6860
+ },
6861
+ get LOG_FILE() {
6862
+ return join6(this.DAEMON_DIR, "daemon.log");
6863
+ }
6864
+ };
6865
+ });
6866
+
6867
+ // src/daemon/local-token.ts
6868
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
6869
+ import { dirname as dirname3 } from "node:path";
6870
+ import { randomBytes as randomBytes4 } from "node:crypto";
6871
+ function readLocalToken() {
6872
+ try {
6873
+ return readFileSync5(DAEMON_PATHS.TOKEN_FILE, "utf8").trim();
6874
+ } catch {
6875
+ return null;
6876
+ }
6877
+ }
6878
+ function ensureLocalToken() {
6879
+ const existing = readLocalToken();
6880
+ if (existing)
6881
+ return existing;
6882
+ mkdirSync4(dirname3(DAEMON_PATHS.TOKEN_FILE), { recursive: true, mode: 448 });
6883
+ const tok = randomBytes4(32).toString("base64url");
6884
+ writeFileSync6(DAEMON_PATHS.TOKEN_FILE, tok + `
6885
+ `, { mode: 384 });
6886
+ return tok;
6887
+ }
6888
+ var init_local_token = __esm(() => {
6889
+ init_paths2();
6890
+ });
6891
+
6892
+ // src/daemon/ipc/client.ts
6893
+ import { request as httpRequest } from "node:http";
6894
+ async function ipc(opts) {
6895
+ const useTcp = !!opts.preferTcp;
6896
+ const headers = {
6897
+ accept: "application/json",
6898
+ host: "localhost"
6899
+ };
6900
+ let bodyBuf;
6901
+ if (opts.body !== undefined) {
6902
+ bodyBuf = Buffer.from(JSON.stringify(opts.body), "utf8");
6903
+ headers["content-type"] = "application/json";
6904
+ headers["content-length"] = String(bodyBuf.length);
6905
+ }
6906
+ if (useTcp) {
6907
+ const tok = readLocalToken();
6908
+ if (!tok)
6909
+ throw new IpcError(0, null, "daemon local token not found; is the daemon running?");
6910
+ headers.authorization = `Bearer ${tok}`;
6911
+ }
6912
+ return new Promise((resolve, reject) => {
6913
+ const req = httpRequest(useTcp ? { host: DAEMON_TCP_HOST, port: DAEMON_TCP_DEFAULT_PORT, path: opts.path, method: opts.method ?? "GET", headers } : { socketPath: DAEMON_PATHS.SOCK_FILE, path: opts.path, method: opts.method ?? "GET", headers }, (res) => {
6914
+ const chunks = [];
6915
+ res.on("data", (c) => chunks.push(c));
6916
+ res.on("end", () => {
6917
+ const raw = Buffer.concat(chunks).toString("utf8");
6918
+ let parsed = raw;
6919
+ try {
6920
+ parsed = raw.length > 0 ? JSON.parse(raw) : null;
6921
+ } catch {}
6922
+ resolve({ status: res.statusCode ?? 0, body: parsed });
6923
+ });
6924
+ });
6925
+ req.setTimeout(opts.timeoutMs ?? 5000, () => req.destroy(new Error("ipc_timeout")));
6926
+ req.on("error", (err) => reject(err));
6927
+ if (bodyBuf)
6928
+ req.write(bodyBuf);
6929
+ req.end();
6930
+ });
6931
+ }
6932
+ var IpcError;
6933
+ var init_client4 = __esm(() => {
6934
+ init_paths2();
6935
+ init_local_token();
6936
+ IpcError = class IpcError extends Error {
6937
+ status;
6938
+ payload;
6939
+ constructor(status, payload, msg) {
6940
+ super(msg);
6941
+ this.status = status;
6942
+ this.payload = payload;
6943
+ }
6944
+ };
6945
+ });
6946
+
6947
+ // src/services/bridge/daemon-route.ts
6948
+ import { existsSync as existsSync7 } from "node:fs";
6949
+ async function trySendViaDaemon(args) {
6950
+ if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
6951
+ return null;
6952
+ try {
6953
+ const res = await ipc({
6954
+ method: "POST",
6955
+ path: "/v1/send",
6956
+ timeoutMs: 3000,
6957
+ body: {
6958
+ to: args.to,
6959
+ message: args.message,
6960
+ priority: args.priority,
6961
+ ...args.idempotencyKey ? { client_message_id: args.idempotencyKey } : {}
6962
+ }
6963
+ });
6964
+ if (res.status === 202 || res.status === 200) {
6965
+ return {
6966
+ ok: true,
6967
+ messageId: res.body.broker_message_id ?? res.body.client_message_id ?? "",
6968
+ duplicate: res.body.duplicate,
6969
+ status: res.body.status
6970
+ };
6971
+ }
6972
+ return { ok: false, error: res.body.error ?? `daemon http ${res.status}` };
6973
+ } catch (err) {
6974
+ const msg = String(err);
6975
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
6976
+ return null;
6977
+ return { ok: false, error: msg };
6978
+ }
6979
+ }
6980
+ var init_daemon_route = __esm(() => {
6981
+ init_client4();
6982
+ init_paths2();
6983
+ });
6984
+
6837
6985
  // src/commands/send.ts
6838
6986
  var exports_send = {};
6839
6987
  __export(exports_send, {
@@ -6855,6 +7003,23 @@ async function runSend(flags, to, message) {
6855
7003
  process.exit(1);
6856
7004
  }
6857
7005
  }
7006
+ {
7007
+ const dr = await trySendViaDaemon({ to, message, priority, expectedMesh: meshSlug ?? undefined });
7008
+ if (dr !== null) {
7009
+ if (dr.ok) {
7010
+ if (flags.json)
7011
+ console.log(JSON.stringify({ ok: true, messageId: dr.messageId, target: to, via: "daemon", duplicate: !!dr.duplicate }));
7012
+ else
7013
+ render.ok(`sent to ${to} (daemon)`, dr.messageId ? dim(dr.messageId.slice(0, 8)) : undefined);
7014
+ return;
7015
+ }
7016
+ if (flags.json)
7017
+ console.log(JSON.stringify({ ok: false, error: dr.error, via: "daemon" }));
7018
+ else
7019
+ render.err(`send failed (daemon): ${dr.error}`);
7020
+ process.exit(1);
7021
+ }
7022
+ }
6858
7023
  if (meshSlug) {
6859
7024
  const bridged = await tryBridge(meshSlug, "send", { to, message, priority });
6860
7025
  if (bridged !== null) {
@@ -6918,6 +7083,7 @@ var init_send = __esm(() => {
6918
7083
  init_connect();
6919
7084
  init_facade();
6920
7085
  init_client3();
7086
+ init_daemon_route();
6921
7087
  init_render();
6922
7088
  init_styles();
6923
7089
  });
@@ -8001,180 +8167,2292 @@ async function runRemind(flags, positional) {
8001
8167
  console.log(JSON.stringify(scheduled, null, 2));
8002
8168
  return;
8003
8169
  }
8004
- if (scheduled.length === 0) {
8005
- render.info(dim("No pending reminders."));
8006
- return;
8170
+ if (scheduled.length === 0) {
8171
+ render.info(dim("No pending reminders."));
8172
+ return;
8173
+ }
8174
+ render.section(`reminders (${scheduled.length})`);
8175
+ for (const m of scheduled) {
8176
+ const when = new Date(m.deliverAt).toLocaleString();
8177
+ const to = m.to === client.getSessionPubkey() ? dim("(self)") : m.to;
8178
+ process.stdout.write(` ${bold(m.id.slice(0, 8))} ${dim("→")} ${to} ${dim("at")} ${when}
8179
+ `);
8180
+ process.stdout.write(` ${dim(m.message.slice(0, 80))}
8181
+
8182
+ `);
8183
+ }
8184
+ });
8185
+ return;
8186
+ }
8187
+ if (action === "cancel") {
8188
+ const id = positional[1];
8189
+ if (!id) {
8190
+ render.err("Usage: claudemesh remind cancel <id>");
8191
+ process.exit(1);
8192
+ }
8193
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
8194
+ const ok = await client.cancelScheduled(id);
8195
+ if (ok)
8196
+ render.ok(`cancelled ${bold(id.slice(0, 8))}`);
8197
+ else {
8198
+ render.err(`not found or already fired: ${id}`);
8199
+ process.exit(1);
8200
+ }
8201
+ });
8202
+ return;
8203
+ }
8204
+ const message = action ?? positional.join(" ");
8205
+ if (!message) {
8206
+ render.err("Usage: claudemesh remind <message> --in <duration>");
8207
+ render.info(dim(" claudemesh remind <message> --at <time>"));
8208
+ render.info(dim(' claudemesh remind <message> --cron "0 */2 * * *"'));
8209
+ render.info(dim(" claudemesh remind list"));
8210
+ render.info(dim(" claudemesh remind cancel <id>"));
8211
+ process.exit(1);
8212
+ }
8213
+ const isCron = !!flags.cron;
8214
+ const deliverAt = isCron ? 0 : parseDeliverAt(flags);
8215
+ if (!isCron && deliverAt === null) {
8216
+ render.err("Specify when", 'use --in <duration> (e.g. "2h", "30m"), --at <time> (e.g. "15:00"), or --cron <expression>');
8217
+ process.exit(1);
8218
+ }
8219
+ await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
8220
+ let targetSpec;
8221
+ if (flags.to && flags.to !== "self") {
8222
+ if (flags.to.startsWith("@") || flags.to === "*" || /^[0-9a-f]{64}$/i.test(flags.to)) {
8223
+ targetSpec = flags.to;
8224
+ } else {
8225
+ const peers = await client.listPeers();
8226
+ const match = peers.find((p) => p.displayName.toLowerCase() === flags.to.toLowerCase());
8227
+ if (!match) {
8228
+ render.err(`Peer "${flags.to}" not found`, `online: ${peers.map((p) => p.displayName).join(", ") || "(none)"}`);
8229
+ process.exit(1);
8230
+ }
8231
+ targetSpec = match.pubkey;
8232
+ }
8233
+ } else {
8234
+ targetSpec = client.getSessionPubkey() ?? "*";
8235
+ }
8236
+ const result = await client.scheduleMessage(targetSpec, message, deliverAt ?? 0, false, flags.cron);
8237
+ if (!result) {
8238
+ render.err("Broker did not acknowledge — check connection");
8239
+ process.exit(1);
8240
+ }
8241
+ if (flags.json) {
8242
+ console.log(JSON.stringify(result));
8243
+ return;
8244
+ }
8245
+ const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
8246
+ if (isCron) {
8247
+ const nextFire = new Date(result.deliverAt).toLocaleString();
8248
+ render.ok(`recurring reminder set`, `${result.scheduledId.slice(0, 8)} · ${clay(message)} → ${toLabel} · cron ${flags.cron} · next ${nextFire}`);
8249
+ } else {
8250
+ const when = new Date(result.deliverAt).toLocaleString();
8251
+ render.ok(`reminder set`, `${result.scheduledId.slice(0, 8)} · ${clay(message)} → ${toLabel} at ${when}`);
8252
+ }
8253
+ });
8254
+ }
8255
+ var init_remind = __esm(() => {
8256
+ init_connect();
8257
+ init_render();
8258
+ init_styles();
8259
+ });
8260
+
8261
+ // src/commands/register.ts
8262
+ var exports_register = {};
8263
+ __export(exports_register, {
8264
+ register: () => register2
8265
+ });
8266
+ async function register2() {
8267
+ return login();
8268
+ }
8269
+ var init_register = __esm(() => {
8270
+ init_login();
8271
+ });
8272
+
8273
+ // src/commands/logout.ts
8274
+ var exports_logout = {};
8275
+ __export(exports_logout, {
8276
+ logout: () => logout2
8277
+ });
8278
+ async function logout2() {
8279
+ try {
8280
+ const { revoked } = await logout();
8281
+ if (revoked) {
8282
+ console.log(` ${green(icons.check)} Revoked session on claudemesh.com`);
8283
+ } else {
8284
+ console.log(` ${yellow(icons.warn)} Could not revoke session on claudemesh.com.`);
8285
+ console.log(` Revoke manually at https://claudemesh.com/dashboard/settings/sessions`);
8286
+ }
8287
+ console.log(` ${green(icons.check)} Removed local credentials.`);
8288
+ return EXIT.SUCCESS;
8289
+ } catch (err) {
8290
+ console.error(` ${icons.cross} Logout failed: ${err instanceof Error ? err.message : err}`);
8291
+ return EXIT.AUTH_FAILED;
8292
+ }
8293
+ }
8294
+ var init_logout = __esm(() => {
8295
+ init_facade6();
8296
+ init_styles();
8297
+ init_exit_codes();
8298
+ });
8299
+
8300
+ // src/commands/whoami.ts
8301
+ var exports_whoami = {};
8302
+ __export(exports_whoami, {
8303
+ whoami: () => whoami
8304
+ });
8305
+ async function whoami(opts) {
8306
+ const result = await whoAmI();
8307
+ if (opts.json) {
8308
+ console.log(JSON.stringify({ schema_version: "1.0", ...result }, null, 2));
8309
+ return result.signed_in || result.local ? EXIT.SUCCESS : EXIT.AUTH_FAILED;
8310
+ }
8311
+ if (!result.signed_in && !result.local) {
8312
+ render.err("Not signed in", "Run `claudemesh login` to sign in or `claudemesh <invite>` to join.");
8313
+ return EXIT.AUTH_FAILED;
8314
+ }
8315
+ render.section("whoami");
8316
+ if (result.signed_in) {
8317
+ render.kv([
8318
+ ["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
8319
+ ["token", `${result.token_source} ${dim("(~/.claudemesh/auth.json)")}`],
8320
+ ...result.meshes ? [["meshes", `${result.meshes.owned} owned · ${result.meshes.guest} guest`]] : []
8321
+ ]);
8322
+ } else {
8323
+ render.kv([
8324
+ ["web", dim("not signed in · run `claudemesh login` for account features")]
8325
+ ]);
8326
+ }
8327
+ if (result.local) {
8328
+ render.blank();
8329
+ render.kv([
8330
+ ["local", `${result.local.meshes.length} mesh${result.local.meshes.length === 1 ? "" : "es"} · ${dim(result.local.config_path)}`]
8331
+ ]);
8332
+ for (const m of result.local.meshes) {
8333
+ console.log(` ${clay("●")} ${bold(m.slug)} ${dim(`member ${m.member_id.slice(0, 8)}… pk ${m.pubkey_prefix}…`)}`);
8334
+ }
8335
+ }
8336
+ render.blank();
8337
+ return EXIT.SUCCESS;
8338
+ }
8339
+ var init_whoami = __esm(() => {
8340
+ init_facade6();
8341
+ init_render();
8342
+ init_styles();
8343
+ init_exit_codes();
8344
+ });
8345
+
8346
+ // src/daemon/lock.ts
8347
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync7 } from "node:fs";
8348
+ import { dirname as dirname4 } from "node:path";
8349
+ function acquireSingletonLock() {
8350
+ mkdirSync5(dirname4(DAEMON_PATHS.PID_FILE), { recursive: true, mode: 448 });
8351
+ if (existsSync8(DAEMON_PATHS.PID_FILE)) {
8352
+ const raw = readFileSync6(DAEMON_PATHS.PID_FILE, "utf8").trim();
8353
+ const oldPid = Number.parseInt(raw, 10);
8354
+ if (Number.isFinite(oldPid) && oldPid > 0 && isProcessAlive(oldPid)) {
8355
+ return { result: "already-running", pid: oldPid };
8356
+ }
8357
+ try {
8358
+ unlinkSync2(DAEMON_PATHS.PID_FILE);
8359
+ } catch {}
8360
+ writeFileSync7(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8361
+ return { result: "stale", pid: process.pid };
8362
+ }
8363
+ writeFileSync7(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8364
+ return { result: "acquired", pid: process.pid };
8365
+ }
8366
+ function releaseSingletonLock() {
8367
+ try {
8368
+ const raw = readFileSync6(DAEMON_PATHS.PID_FILE, "utf8").trim();
8369
+ if (Number.parseInt(raw, 10) === process.pid)
8370
+ unlinkSync2(DAEMON_PATHS.PID_FILE);
8371
+ } catch {}
8372
+ }
8373
+ function readRunningPid() {
8374
+ try {
8375
+ const raw = readFileSync6(DAEMON_PATHS.PID_FILE, "utf8").trim();
8376
+ const pid = Number.parseInt(raw, 10);
8377
+ if (Number.isFinite(pid) && pid > 0 && isProcessAlive(pid))
8378
+ return pid;
8379
+ } catch {}
8380
+ return null;
8381
+ }
8382
+ function isProcessAlive(pid) {
8383
+ try {
8384
+ process.kill(pid, 0);
8385
+ return true;
8386
+ } catch (err) {
8387
+ return err.code === "EPERM";
8388
+ }
8389
+ }
8390
+ var init_lock = __esm(() => {
8391
+ init_paths2();
8392
+ });
8393
+
8394
+ // src/daemon/db/outbox.ts
8395
+ function migrateOutbox(db) {
8396
+ db.exec(`
8397
+ CREATE TABLE IF NOT EXISTS outbox (
8398
+ id TEXT PRIMARY KEY,
8399
+ client_message_id TEXT NOT NULL UNIQUE,
8400
+ request_fingerprint BLOB NOT NULL,
8401
+ payload BLOB NOT NULL,
8402
+ enqueued_at INTEGER NOT NULL,
8403
+ attempts INTEGER NOT NULL DEFAULT 0,
8404
+ next_attempt_at INTEGER NOT NULL,
8405
+ status TEXT NOT NULL CHECK(status IN
8406
+ ('pending','inflight','done','dead','aborted')),
8407
+ last_error TEXT,
8408
+ delivered_at INTEGER,
8409
+ broker_message_id TEXT,
8410
+ aborted_at INTEGER,
8411
+ aborted_by TEXT,
8412
+ superseded_by TEXT
8413
+ );
8414
+ CREATE INDEX IF NOT EXISTS outbox_pending
8415
+ ON outbox(status, next_attempt_at);
8416
+ CREATE INDEX IF NOT EXISTS outbox_aborted
8417
+ ON outbox(status, aborted_at) WHERE status = 'aborted';
8418
+ `);
8419
+ }
8420
+ function findByClientId(db, clientMessageId) {
8421
+ const row = db.prepare(`
8422
+ SELECT id, client_message_id, request_fingerprint, payload, enqueued_at,
8423
+ attempts, next_attempt_at, status, last_error, delivered_at,
8424
+ broker_message_id, aborted_at, aborted_by, superseded_by
8425
+ FROM outbox WHERE client_message_id = ?
8426
+ `).get(clientMessageId);
8427
+ return row ?? null;
8428
+ }
8429
+ function insertPending(db, input) {
8430
+ db.prepare(`
8431
+ INSERT INTO outbox (
8432
+ id, client_message_id, request_fingerprint, payload,
8433
+ enqueued_at, attempts, next_attempt_at, status
8434
+ ) VALUES (?, ?, ?, ?, ?, 0, ?, 'pending')
8435
+ `).run(input.id, input.client_message_id, input.request_fingerprint, input.payload, input.now, input.now);
8436
+ }
8437
+ function fingerprintsEqual(a, b) {
8438
+ if (a.length !== b.length)
8439
+ return false;
8440
+ let diff = 0;
8441
+ for (let i = 0;i < a.length; i++)
8442
+ diff |= a[i] ^ b[i];
8443
+ return diff === 0;
8444
+ }
8445
+ function listOutbox(db, p = {}) {
8446
+ const where = [];
8447
+ const args = [];
8448
+ if (p.status) {
8449
+ where.push("status = ?");
8450
+ args.push(p.status);
8451
+ }
8452
+ const sql = `
8453
+ SELECT id, client_message_id, request_fingerprint, payload, enqueued_at,
8454
+ attempts, next_attempt_at, status, last_error, delivered_at,
8455
+ broker_message_id, aborted_at, aborted_by, superseded_by
8456
+ FROM outbox
8457
+ ${where.length ? "WHERE " + where.join(" AND ") : ""}
8458
+ ORDER BY enqueued_at DESC
8459
+ LIMIT ?
8460
+ `;
8461
+ args.push(Math.min(Math.max(p.limit ?? 50, 1), 500));
8462
+ return db.prepare(sql).all(...args);
8463
+ }
8464
+ function findById(db, id) {
8465
+ return db.prepare(`
8466
+ SELECT id, client_message_id, request_fingerprint, payload, enqueued_at,
8467
+ attempts, next_attempt_at, status, last_error, delivered_at,
8468
+ broker_message_id, aborted_at, aborted_by, superseded_by
8469
+ FROM outbox WHERE id = ?
8470
+ `).get(id) ?? null;
8471
+ }
8472
+ function requeueDeadOrPending(db, args) {
8473
+ const existing = findById(db, args.id);
8474
+ if (!existing)
8475
+ return null;
8476
+ if (existing.status === "aborted" || existing.status === "done")
8477
+ return null;
8478
+ db.prepare(`
8479
+ UPDATE outbox
8480
+ SET status = 'aborted', aborted_at = ?, aborted_by = ?, superseded_by = ?
8481
+ WHERE id = ? AND status IN ('pending','inflight','dead')
8482
+ `).run(args.now, args.abortedBy, args.newRowId, args.id);
8483
+ db.prepare(`
8484
+ INSERT INTO outbox (
8485
+ id, client_message_id, request_fingerprint, payload,
8486
+ enqueued_at, attempts, next_attempt_at, status
8487
+ ) VALUES (?, ?, ?, ?, ?, 0, ?, 'pending')
8488
+ `).run(args.newRowId, args.newClientMessageId, existing.request_fingerprint, existing.payload, args.now, args.now);
8489
+ return {
8490
+ abortedRowId: existing.id,
8491
+ newRowId: args.newRowId,
8492
+ newClientMessageId: args.newClientMessageId
8493
+ };
8494
+ }
8495
+
8496
+ // src/daemon/db/sqlite.ts
8497
+ async function loadSqlite() {
8498
+ if (cached)
8499
+ return cached;
8500
+ try {
8501
+ const mod = await import("node:sqlite");
8502
+ cached = mod.DatabaseSync;
8503
+ return cached;
8504
+ } catch (nodeErr) {
8505
+ try {
8506
+ const bunMod = await import("bun:sqlite");
8507
+ cached = bunMod.Database;
8508
+ return cached;
8509
+ } catch {
8510
+ const msg = `claudemesh daemon requires Node.js 22.5+ for the embedded SQLite store (node:sqlite), or Bun (bun:sqlite) for dev. Current: ${process.version}. Original error: ${String(nodeErr)}`;
8511
+ throw new Error(msg);
8512
+ }
8513
+ }
8514
+ }
8515
+ async function openSqlite(path) {
8516
+ const Database = await loadSqlite();
8517
+ const db = new Database(path);
8518
+ db.exec(`
8519
+ PRAGMA journal_mode = WAL;
8520
+ PRAGMA synchronous = NORMAL;
8521
+ PRAGMA foreign_keys = ON;
8522
+ PRAGMA busy_timeout = 5000;
8523
+ `);
8524
+ return db;
8525
+ }
8526
+ function inImmediateTx(db, fn) {
8527
+ db.exec("BEGIN IMMEDIATE");
8528
+ try {
8529
+ const out = fn();
8530
+ db.exec("COMMIT");
8531
+ return out;
8532
+ } catch (err) {
8533
+ try {
8534
+ db.exec("ROLLBACK");
8535
+ } catch {}
8536
+ throw err;
8537
+ }
8538
+ }
8539
+ var cached = null;
8540
+
8541
+ // src/daemon/fingerprint.ts
8542
+ import { createHash } from "node:crypto";
8543
+ function computeRequestFingerprint(req) {
8544
+ const h = createHash("sha256");
8545
+ h.update(String(req.envelope_version), "utf8");
8546
+ h.update(NUL);
8547
+ h.update(req.destination_kind, "utf8");
8548
+ h.update(NUL);
8549
+ h.update(req.destination_ref, "utf8");
8550
+ h.update(NUL);
8551
+ h.update(req.reply_to_id ?? "", "utf8");
8552
+ h.update(NUL);
8553
+ h.update(req.priority, "utf8");
8554
+ h.update(NUL);
8555
+ h.update(req.meta ? canonicalJson(req.meta) : "", "utf8");
8556
+ h.update(NUL);
8557
+ h.update(createHash("sha256").update(req.body).digest());
8558
+ return h.digest();
8559
+ }
8560
+ function canonicalJson(value) {
8561
+ return JSON.stringify(sortKeys(value));
8562
+ }
8563
+ function sortKeys(value) {
8564
+ if (Array.isArray(value))
8565
+ return value.map(sortKeys);
8566
+ if (value !== null && typeof value === "object") {
8567
+ const obj = value;
8568
+ const out = {};
8569
+ for (const k of Object.keys(obj).sort())
8570
+ out[k] = sortKeys(obj[k]);
8571
+ return out;
8572
+ }
8573
+ return value;
8574
+ }
8575
+ function fingerprintHexPrefix(fp, bytes = 8) {
8576
+ let s = "";
8577
+ for (let i = 0;i < bytes && i < fp.length; i++) {
8578
+ s += fp[i].toString(16).padStart(2, "0");
8579
+ }
8580
+ return s;
8581
+ }
8582
+ var NUL;
8583
+ var init_fingerprint = __esm(() => {
8584
+ NUL = Buffer.from([0]);
8585
+ });
8586
+
8587
+ // src/daemon/ipc/handlers/send.ts
8588
+ import { randomUUID as randomUUID3 } from "node:crypto";
8589
+ function acceptSend(req, deps) {
8590
+ const now = (deps.now ?? Date.now)();
8591
+ const newId = deps.newId ?? randomUUID3;
8592
+ const clientId = req.client_message_id?.trim() || ulidLike(newId);
8593
+ const body = Buffer.from(req.message, "utf8");
8594
+ const fingerprint = computeRequestFingerprint({
8595
+ envelope_version: ENVELOPE_VERSION,
8596
+ destination_kind: req.destination_kind,
8597
+ destination_ref: req.destination_ref,
8598
+ reply_to_id: req.reply_to_id ?? null,
8599
+ priority: req.priority ?? "next",
8600
+ meta: req.meta ?? null,
8601
+ body
8602
+ });
8603
+ return inImmediateTx(deps.db, () => {
8604
+ const existing = findByClientId(deps.db, clientId);
8605
+ if (!existing) {
8606
+ insertPending(deps.db, {
8607
+ id: newId(),
8608
+ client_message_id: clientId,
8609
+ request_fingerprint: fingerprint,
8610
+ payload: body,
8611
+ now
8612
+ });
8613
+ return { kind: "accepted_pending", status: 202, client_message_id: clientId };
8614
+ }
8615
+ return decideForExistingRow(existing, fingerprint);
8616
+ });
8617
+ }
8618
+ function decideForExistingRow(row, fp) {
8619
+ const match = fingerprintsEqual(fp, row.request_fingerprint);
8620
+ const fpPrefix = fingerprintHexPrefix(fp);
8621
+ switch (row.status) {
8622
+ case "pending":
8623
+ return match ? { kind: "accepted_pending", status: 202, client_message_id: row.client_message_id } : conflict("outbox_pending_fingerprint_mismatch", fpPrefix);
8624
+ case "inflight":
8625
+ return match ? { kind: "accepted_inflight", status: 202, client_message_id: row.client_message_id } : conflict("outbox_inflight_fingerprint_mismatch", fpPrefix);
8626
+ case "done":
8627
+ return match ? {
8628
+ kind: "accepted_done",
8629
+ status: 200,
8630
+ client_message_id: row.client_message_id,
8631
+ broker_message_id: row.broker_message_id
8632
+ } : conflict("outbox_done_fingerprint_mismatch", fpPrefix, row.broker_message_id);
8633
+ case "dead":
8634
+ return match ? conflict("outbox_dead_fingerprint_match", fpPrefix, row.broker_message_id) : conflict("outbox_dead_fingerprint_mismatch", fpPrefix);
8635
+ case "aborted":
8636
+ return match ? conflict("outbox_aborted_fingerprint_match", fpPrefix) : conflict("outbox_aborted_fingerprint_mismatch", fpPrefix);
8637
+ default: {
8638
+ const _ = row.status;
8639
+ throw new Error(`unknown outbox status: ${String(_)}`);
8640
+ }
8641
+ }
8642
+ }
8643
+ function conflict(reason, fpPrefix, brokerMessageId = null) {
8644
+ return {
8645
+ kind: "conflict",
8646
+ status: 409,
8647
+ reason,
8648
+ daemon_fingerprint_prefix: fpPrefix,
8649
+ broker_message_id: brokerMessageId
8650
+ };
8651
+ }
8652
+ function ulidLike(newId) {
8653
+ return newId();
8654
+ }
8655
+ var ENVELOPE_VERSION = 1;
8656
+ var init_send2 = __esm(() => {
8657
+ init_fingerprint();
8658
+ });
8659
+
8660
+ // src/daemon/db/inbox.ts
8661
+ function migrateInbox(db) {
8662
+ db.exec(`
8663
+ CREATE TABLE IF NOT EXISTS inbox (
8664
+ id TEXT PRIMARY KEY,
8665
+ client_message_id TEXT NOT NULL UNIQUE,
8666
+ broker_message_id TEXT,
8667
+ mesh TEXT NOT NULL,
8668
+ topic TEXT,
8669
+ sender_pubkey TEXT NOT NULL,
8670
+ sender_name TEXT NOT NULL,
8671
+ body TEXT,
8672
+ meta TEXT,
8673
+ received_at INTEGER NOT NULL,
8674
+ reply_to_id TEXT
8675
+ );
8676
+ CREATE INDEX IF NOT EXISTS inbox_received_at ON inbox(received_at);
8677
+ CREATE INDEX IF NOT EXISTS inbox_topic ON inbox(topic);
8678
+ CREATE INDEX IF NOT EXISTS inbox_sender ON inbox(sender_pubkey);
8679
+ `);
8680
+ }
8681
+ function insertIfNew(db, row) {
8682
+ const before = db.prepare(`SELECT id FROM inbox WHERE client_message_id = ?`).get(row.client_message_id);
8683
+ if (before)
8684
+ return null;
8685
+ db.prepare(`
8686
+ INSERT INTO inbox (
8687
+ id, client_message_id, broker_message_id, mesh, topic,
8688
+ sender_pubkey, sender_name, body, meta, received_at, reply_to_id
8689
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
8690
+ ON CONFLICT(client_message_id) DO NOTHING
8691
+ `).run(row.id, row.client_message_id, row.broker_message_id, row.mesh, row.topic, row.sender_pubkey, row.sender_name, row.body, row.meta, row.received_at, row.reply_to_id);
8692
+ const after = db.prepare(`SELECT id FROM inbox WHERE client_message_id = ?`).get(row.client_message_id);
8693
+ return after?.id === row.id ? row.id : null;
8694
+ }
8695
+ function listInbox(db, p) {
8696
+ const where = [];
8697
+ const args = [];
8698
+ if (p.since !== undefined) {
8699
+ where.push("received_at >= ?");
8700
+ args.push(p.since);
8701
+ }
8702
+ if (p.topic !== undefined) {
8703
+ where.push("topic = ?");
8704
+ args.push(p.topic);
8705
+ }
8706
+ if (p.fromPubkey !== undefined) {
8707
+ where.push("sender_pubkey = ?");
8708
+ args.push(p.fromPubkey);
8709
+ }
8710
+ const sql = `
8711
+ SELECT id, client_message_id, broker_message_id, mesh, topic,
8712
+ sender_pubkey, sender_name, body, meta, received_at, reply_to_id
8713
+ FROM inbox
8714
+ ${where.length ? "WHERE " + where.join(" AND ") : ""}
8715
+ ORDER BY received_at DESC
8716
+ LIMIT ?
8717
+ `;
8718
+ args.push(Math.min(Math.max(p.limit ?? 100, 1), 1000));
8719
+ return db.prepare(sql).all(...args);
8720
+ }
8721
+
8722
+ // src/daemon/events.ts
8723
+ class EventBus {
8724
+ subs = new Set;
8725
+ publish(kind, data) {
8726
+ const e = { kind, ts: new Date().toISOString(), data };
8727
+ for (const s of this.subs) {
8728
+ try {
8729
+ s(e);
8730
+ } catch {}
8731
+ }
8732
+ }
8733
+ subscribe(fn) {
8734
+ this.subs.add(fn);
8735
+ return () => this.subs.delete(fn);
8736
+ }
8737
+ }
8738
+ function writeSse(res, e, idCounter) {
8739
+ res.write(`id: ${idCounter}
8740
+ `);
8741
+ res.write(`event: ${e.kind}
8742
+ `);
8743
+ res.write(`data: ${JSON.stringify({ ts: e.ts, ...e.data })}
8744
+
8745
+ `);
8746
+ }
8747
+ function bindSseStream(res, bus) {
8748
+ res.statusCode = 200;
8749
+ res.setHeader("Content-Type", "text/event-stream");
8750
+ res.setHeader("Cache-Control", "no-cache, no-transform");
8751
+ res.setHeader("Connection", "keep-alive");
8752
+ res.setHeader("X-Accel-Buffering", "no");
8753
+ res.write(`: connected
8754
+
8755
+ `);
8756
+ let counter = 0;
8757
+ const unsubscribe = bus.subscribe((e) => writeSse(res, e, ++counter));
8758
+ const heartbeat = setInterval(() => {
8759
+ try {
8760
+ res.write(`: keepalive
8761
+
8762
+ `);
8763
+ } catch {}
8764
+ }, 15000);
8765
+ const cleanup = () => {
8766
+ clearInterval(heartbeat);
8767
+ unsubscribe();
8768
+ try {
8769
+ res.end();
8770
+ } catch {}
8771
+ };
8772
+ res.on("close", cleanup);
8773
+ res.on("error", cleanup);
8774
+ return cleanup;
8775
+ }
8776
+
8777
+ // src/daemon/ipc/server.ts
8778
+ import { createServer as createServer2 } from "node:http";
8779
+ import { chmodSync as chmodSync3, existsSync as existsSync9, unlinkSync as unlinkSync3 } from "node:fs";
8780
+ import { timingSafeEqual } from "node:crypto";
8781
+ import { randomUUID as randomUUID4 } from "node:crypto";
8782
+ function startIpcServer(opts) {
8783
+ const log2 = opts.log ?? defaultLogger;
8784
+ const handler = makeHandler({
8785
+ localToken: opts.localToken,
8786
+ publicHealthCheck: !!opts.publicHealthCheck,
8787
+ log: log2,
8788
+ outboxDb: opts.outboxDb,
8789
+ inboxDb: opts.inboxDb,
8790
+ bus: opts.bus,
8791
+ broker: opts.broker,
8792
+ onPendingInserted: opts.onPendingInserted
8793
+ });
8794
+ if (existsSync9(DAEMON_PATHS.SOCK_FILE)) {
8795
+ try {
8796
+ unlinkSync3(DAEMON_PATHS.SOCK_FILE);
8797
+ } catch {}
8798
+ }
8799
+ const uds = createServer2(handler);
8800
+ const udsReady = new Promise((resolve, reject) => {
8801
+ uds.once("error", reject);
8802
+ uds.listen(DAEMON_PATHS.SOCK_FILE, () => {
8803
+ try {
8804
+ chmodSync3(DAEMON_PATHS.SOCK_FILE, 384);
8805
+ } catch (err) {
8806
+ log2("warn", "uds_chmod_failed", { err: String(err) });
8807
+ }
8808
+ resolve();
8809
+ });
8810
+ });
8811
+ let tcp = null;
8812
+ let tcpReady = Promise.resolve();
8813
+ if (opts.tcpEnabled !== false) {
8814
+ tcp = createServer2(handler);
8815
+ tcpReady = new Promise((resolve, reject) => {
8816
+ tcp.once("error", reject);
8817
+ tcp.listen(opts.tcpPort ?? DAEMON_TCP_DEFAULT_PORT, DAEMON_TCP_HOST, () => resolve());
8818
+ });
8819
+ }
8820
+ return {
8821
+ uds,
8822
+ tcp,
8823
+ ready: Promise.all([udsReady, tcpReady]).then(() => {
8824
+ return;
8825
+ }),
8826
+ close: async () => {
8827
+ await Promise.allSettled([
8828
+ new Promise((res) => uds.close(() => res())),
8829
+ tcp ? new Promise((res) => tcp.close(() => res())) : Promise.resolve()
8830
+ ]);
8831
+ try {
8832
+ unlinkSync3(DAEMON_PATHS.SOCK_FILE);
8833
+ } catch {}
8834
+ }
8835
+ };
8836
+ }
8837
+ function defaultLogger(level, msg, meta) {
8838
+ const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
8839
+ if (level === "info")
8840
+ process.stdout.write(line + `
8841
+ `);
8842
+ else
8843
+ process.stderr.write(line + `
8844
+ `);
8845
+ }
8846
+ function makeHandler(opts) {
8847
+ const tokenBytes = Buffer.from(opts.localToken, "utf8");
8848
+ return async (req, res) => {
8849
+ const url = new URL(req.url ?? "/", "http://daemon.local");
8850
+ if (url.searchParams.has("token")) {
8851
+ opts.log("warn", "ipc_token_in_query_string_rejected", { path: url.pathname });
8852
+ respond(res, 400, { error: "token must be in Authorization header, not query string" });
8853
+ return;
8854
+ }
8855
+ const host = (req.headers.host ?? "").toLowerCase().split(":")[0]?.trim() ?? "";
8856
+ if (host && host !== "localhost" && host !== "127.0.0.1" && host !== "[::1]" && host !== "::1") {
8857
+ respond(res, 403, { error: "forbidden host" });
8858
+ return;
8859
+ }
8860
+ if (req.headers.origin) {
8861
+ respond(res, 403, { error: "forbidden origin" });
8862
+ return;
8863
+ }
8864
+ const isUds = req.socket.remoteAddress === undefined;
8865
+ const isPublicHealth = opts.publicHealthCheck && url.pathname === "/v1/health";
8866
+ if (!isUds && !isPublicHealth) {
8867
+ const authz = req.headers.authorization ?? "";
8868
+ const m = /^Bearer\s+(.+)$/.exec(authz.trim());
8869
+ if (!m || !m[1]) {
8870
+ respond(res, 401, { error: "missing bearer token" });
8871
+ return;
8872
+ }
8873
+ const provided = Buffer.from(m[1], "utf8");
8874
+ if (provided.length !== tokenBytes.length || !timingSafeEqual(provided, tokenBytes)) {
8875
+ opts.log("warn", "ipc_bearer_mismatch", { path: url.pathname });
8876
+ respond(res, 401, { error: "invalid bearer token" });
8877
+ return;
8878
+ }
8879
+ }
8880
+ if (req.method === "GET" && url.pathname === "/v1/version") {
8881
+ respond(res, 200, {
8882
+ daemon_version: VERSION,
8883
+ ipc_api: "v1",
8884
+ ipc_features: ["version", "health", "send", "inbox", "events", "peers", "profile"],
8885
+ schema_version: 1
8886
+ });
8887
+ return;
8888
+ }
8889
+ if (req.method === "GET" && url.pathname === "/v1/health") {
8890
+ respond(res, 200, { ok: true, pid: process.pid });
8891
+ return;
8892
+ }
8893
+ if (req.method === "GET" && url.pathname === "/v1/events") {
8894
+ if (!opts.bus) {
8895
+ respond(res, 503, { error: "event bus not initialised" });
8896
+ return;
8897
+ }
8898
+ bindSseStream(res, opts.bus);
8899
+ return;
8900
+ }
8901
+ if (req.method === "GET" && url.pathname === "/v1/peers") {
8902
+ if (!opts.broker) {
8903
+ respond(res, 503, { error: "broker not initialised" });
8904
+ return;
8905
+ }
8906
+ try {
8907
+ const peers = await opts.broker.listPeers();
8908
+ respond(res, 200, { peers });
8909
+ } catch (e) {
8910
+ respond(res, 502, { error: "broker_unreachable", detail: String(e) });
8911
+ }
8912
+ return;
8913
+ }
8914
+ if (req.method === "POST" && url.pathname === "/v1/profile") {
8915
+ if (!opts.broker) {
8916
+ respond(res, 503, { error: "broker not initialised" });
8917
+ return;
8918
+ }
8919
+ try {
8920
+ const body = await readJsonBody(req, 16 * 1024);
8921
+ if (!body) {
8922
+ respond(res, 400, { error: "expected JSON object" });
8923
+ return;
8924
+ }
8925
+ const updates = {};
8926
+ if (typeof body.summary === "string")
8927
+ opts.broker.setSummary(body.summary);
8928
+ if (body.status === "idle" || body.status === "working" || body.status === "dnd")
8929
+ opts.broker.setStatus(body.status);
8930
+ if (typeof body.visible === "boolean")
8931
+ opts.broker.setVisible(body.visible);
8932
+ const profile = {};
8933
+ if (typeof body.avatar === "string")
8934
+ profile.avatar = body.avatar;
8935
+ if (typeof body.title === "string")
8936
+ profile.title = body.title;
8937
+ if (typeof body.bio === "string")
8938
+ profile.bio = body.bio;
8939
+ if (Array.isArray(body.capabilities))
8940
+ profile.capabilities = body.capabilities.filter((c) => typeof c === "string");
8941
+ if (Object.keys(profile).length > 0)
8942
+ opts.broker.setProfile(profile);
8943
+ Object.assign(updates, body);
8944
+ respond(res, 200, { ok: true, applied: Object.keys(updates) });
8945
+ } catch (e) {
8946
+ respond(res, 400, { error: String(e) });
8947
+ }
8948
+ return;
8949
+ }
8950
+ if (req.method === "GET" && url.pathname === "/v1/inbox") {
8951
+ if (!opts.inboxDb) {
8952
+ respond(res, 503, { error: "inbox not initialised" });
8953
+ return;
8954
+ }
8955
+ const sinceRaw = url.searchParams.get("since");
8956
+ const since = sinceRaw ? Date.parse(sinceRaw) : undefined;
8957
+ const topic = url.searchParams.get("topic") ?? undefined;
8958
+ const fromPubkey = url.searchParams.get("from") ?? undefined;
8959
+ const limitRaw = url.searchParams.get("limit");
8960
+ const limit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;
8961
+ const rows = listInbox(opts.inboxDb, {
8962
+ since: Number.isFinite(since) ? since : undefined,
8963
+ topic,
8964
+ fromPubkey,
8965
+ limit: Number.isFinite(limit ?? NaN) ? limit : undefined
8966
+ });
8967
+ respond(res, 200, {
8968
+ items: rows.map((r) => ({
8969
+ id: r.id,
8970
+ client_message_id: r.client_message_id,
8971
+ broker_message_id: r.broker_message_id,
8972
+ mesh: r.mesh,
8973
+ topic: r.topic,
8974
+ sender_pubkey: r.sender_pubkey,
8975
+ sender_name: r.sender_name,
8976
+ body: r.body,
8977
+ received_at: new Date(r.received_at).toISOString(),
8978
+ reply_to_id: r.reply_to_id
8979
+ }))
8980
+ });
8981
+ return;
8982
+ }
8983
+ if (req.method === "GET" && url.pathname === "/v1/outbox") {
8984
+ if (!opts.outboxDb) {
8985
+ respond(res, 503, { error: "outbox not initialised" });
8986
+ return;
8987
+ }
8988
+ const statusParam = url.searchParams.get("status") ?? undefined;
8989
+ const allowed = ["pending", "inflight", "done", "dead", "aborted"];
8990
+ const status = statusParam && allowed.includes(statusParam) ? statusParam : undefined;
8991
+ const limitRaw = url.searchParams.get("limit");
8992
+ const limit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;
8993
+ const rows = listOutbox(opts.outboxDb, {
8994
+ status,
8995
+ limit: Number.isFinite(limit ?? NaN) ? limit : undefined
8996
+ });
8997
+ respond(res, 200, {
8998
+ items: rows.map((r) => ({
8999
+ id: r.id,
9000
+ client_message_id: r.client_message_id,
9001
+ status: r.status,
9002
+ attempts: r.attempts,
9003
+ enqueued_at: new Date(r.enqueued_at).toISOString(),
9004
+ next_attempt_at: new Date(r.next_attempt_at).toISOString(),
9005
+ delivered_at: r.delivered_at ? new Date(r.delivered_at).toISOString() : null,
9006
+ broker_message_id: r.broker_message_id,
9007
+ last_error: r.last_error,
9008
+ aborted_at: r.aborted_at ? new Date(r.aborted_at).toISOString() : null,
9009
+ aborted_by: r.aborted_by,
9010
+ superseded_by: r.superseded_by,
9011
+ payload_bytes: r.payload?.byteLength ?? 0
9012
+ }))
9013
+ });
9014
+ return;
9015
+ }
9016
+ if (req.method === "POST" && url.pathname === "/v1/outbox/requeue") {
9017
+ if (!opts.outboxDb) {
9018
+ respond(res, 503, { error: "outbox not initialised" });
9019
+ return;
9020
+ }
9021
+ try {
9022
+ const body = await readJsonBody(req, 4 * 1024);
9023
+ if (!body || typeof body.id !== "string") {
9024
+ respond(res, 400, { error: "missing 'id'" });
9025
+ return;
9026
+ }
9027
+ const newId = typeof body.new_client_message_id === "string" && body.new_client_message_id.trim() ? body.new_client_message_id.trim() : randomUUID4();
9028
+ const result = requeueDeadOrPending(opts.outboxDb, {
9029
+ id: body.id,
9030
+ newClientMessageId: newId,
9031
+ newRowId: randomUUID4(),
9032
+ now: Date.now(),
9033
+ abortedBy: typeof body.aborted_by === "string" ? body.aborted_by : "operator"
9034
+ });
9035
+ if (!result) {
9036
+ respond(res, 409, { error: "row not found, already aborted, or already done" });
9037
+ return;
9038
+ }
9039
+ respond(res, 200, {
9040
+ aborted_row_id: result.abortedRowId,
9041
+ new_row_id: result.newRowId,
9042
+ new_client_message_id: result.newClientMessageId
9043
+ });
9044
+ opts.onPendingInserted?.();
9045
+ } catch (e) {
9046
+ respond(res, 400, { error: String(e) });
9047
+ }
9048
+ return;
9049
+ }
9050
+ if (req.method === "POST" && url.pathname === "/v1/send") {
9051
+ if (!opts.outboxDb) {
9052
+ respond(res, 503, { error: "outbox not initialised" });
9053
+ return;
9054
+ }
9055
+ try {
9056
+ const body = await readJsonBody(req, 256 * 1024);
9057
+ const parsed = parseSendRequest(body, req.headers["idempotency-key"]);
9058
+ if ("error" in parsed) {
9059
+ respond(res, 400, { error: parsed.error });
9060
+ return;
9061
+ }
9062
+ const outcome = acceptSend(parsed.req, { db: opts.outboxDb });
9063
+ switch (outcome.kind) {
9064
+ case "accepted_pending":
9065
+ respond(res, outcome.status, {
9066
+ client_message_id: outcome.client_message_id,
9067
+ status: "queued"
9068
+ });
9069
+ opts.onPendingInserted?.();
9070
+ return;
9071
+ case "accepted_inflight":
9072
+ respond(res, outcome.status, {
9073
+ client_message_id: outcome.client_message_id,
9074
+ status: "inflight"
9075
+ });
9076
+ return;
9077
+ case "accepted_done":
9078
+ respond(res, outcome.status, {
9079
+ client_message_id: outcome.client_message_id,
9080
+ broker_message_id: outcome.broker_message_id,
9081
+ duplicate: true
9082
+ });
9083
+ return;
9084
+ case "conflict":
9085
+ respond(res, outcome.status, {
9086
+ error: "idempotency_key_reused",
9087
+ conflict: outcome.reason,
9088
+ daemon_fingerprint_prefix: outcome.daemon_fingerprint_prefix,
9089
+ broker_message_id: outcome.broker_message_id ?? null
9090
+ });
9091
+ return;
9092
+ }
9093
+ } catch (err) {
9094
+ opts.log("error", "ipc_send_failed", { err: String(err) });
9095
+ respond(res, 500, { error: "internal" });
9096
+ return;
9097
+ }
9098
+ }
9099
+ respond(res, 404, { error: "not found" });
9100
+ };
9101
+ }
9102
+ async function readJsonBody(req, maxBytes) {
9103
+ const chunks = [];
9104
+ let total = 0;
9105
+ for await (const chunk of req) {
9106
+ const buf = chunk;
9107
+ total += buf.length;
9108
+ if (total > maxBytes)
9109
+ throw new Error("payload_too_large");
9110
+ chunks.push(buf);
9111
+ }
9112
+ if (total === 0)
9113
+ return null;
9114
+ const text = Buffer.concat(chunks).toString("utf8");
9115
+ try {
9116
+ return JSON.parse(text);
9117
+ } catch {
9118
+ throw new Error("invalid_json");
9119
+ }
9120
+ }
9121
+ function parseSendRequest(body, idempotencyHeader) {
9122
+ if (!body || typeof body !== "object")
9123
+ return { error: "expected JSON object" };
9124
+ const b = body;
9125
+ const to = typeof b.to === "string" ? b.to.trim() : "";
9126
+ const message = typeof b.message === "string" ? b.message : "";
9127
+ if (!to)
9128
+ return { error: "missing 'to'" };
9129
+ if (!message)
9130
+ return { error: "missing 'message'" };
9131
+ const priority = b.priority;
9132
+ if (priority !== undefined && priority !== "now" && priority !== "next" && priority !== "low") {
9133
+ return { error: "priority must be 'now' | 'next' | 'low'" };
9134
+ }
9135
+ const meta = b.meta;
9136
+ if (meta !== undefined && meta !== null && (typeof meta !== "object" || Array.isArray(meta))) {
9137
+ return { error: "'meta' must be an object" };
9138
+ }
9139
+ let destination_kind;
9140
+ let destination_ref;
9141
+ if (to.startsWith("@")) {
9142
+ destination_kind = "topic";
9143
+ destination_ref = to.slice(1);
9144
+ } else if (to === "*") {
9145
+ destination_kind = "topic";
9146
+ destination_ref = "*";
9147
+ } else {
9148
+ destination_kind = "dm";
9149
+ destination_ref = to;
9150
+ }
9151
+ const headerId = Array.isArray(idempotencyHeader) ? idempotencyHeader[0] : idempotencyHeader;
9152
+ const client_message_id = typeof b.client_message_id === "string" && b.client_message_id.trim() ? b.client_message_id.trim() : typeof headerId === "string" && headerId.trim() ? headerId.trim() : undefined;
9153
+ const reply_to_id = typeof b.reply_to_id === "string" ? b.reply_to_id : undefined;
9154
+ return {
9155
+ req: {
9156
+ to,
9157
+ message,
9158
+ priority,
9159
+ meta: meta ?? undefined,
9160
+ reply_to_id,
9161
+ client_message_id,
9162
+ destination_kind,
9163
+ destination_ref
9164
+ }
9165
+ };
9166
+ }
9167
+ function respond(res, status, body) {
9168
+ const json = JSON.stringify(body);
9169
+ res.statusCode = status;
9170
+ res.setHeader("Content-Type", "application/json");
9171
+ res.setHeader("Content-Length", Buffer.byteLength(json));
9172
+ res.end(json);
9173
+ }
9174
+ var init_server = __esm(() => {
9175
+ init_paths2();
9176
+ init_send2();
9177
+ init_urls();
9178
+ });
9179
+
9180
+ // src/daemon/broker.ts
9181
+ import WebSocket2 from "ws";
9182
+
9183
+ class DaemonBrokerClient {
9184
+ mesh;
9185
+ opts;
9186
+ ws = null;
9187
+ _status = "closed";
9188
+ closed = false;
9189
+ reconnectAttempt = 0;
9190
+ reconnectTimer = null;
9191
+ helloTimer = null;
9192
+ pendingAcks = new Map;
9193
+ peerListResolvers = new Map;
9194
+ sessionPubkey = null;
9195
+ sessionSecretKey = null;
9196
+ opens = [];
9197
+ reqCounter = 0;
9198
+ constructor(mesh, opts = {}) {
9199
+ this.mesh = mesh;
9200
+ this.opts = opts;
9201
+ }
9202
+ get status() {
9203
+ return this._status;
9204
+ }
9205
+ get meshSlug() {
9206
+ return this.mesh.slug;
9207
+ }
9208
+ get meshId() {
9209
+ return this.mesh.meshId;
9210
+ }
9211
+ log = (level, msg, meta) => {
9212
+ (this.opts.log ?? defaultLog)(level, msg, { mesh: this.mesh.slug, ...meta });
9213
+ };
9214
+ setConnStatus(s) {
9215
+ if (this._status === s)
9216
+ return;
9217
+ this._status = s;
9218
+ this.opts.onStatusChange?.(s);
9219
+ }
9220
+ async connect() {
9221
+ if (this.closed)
9222
+ throw new Error("client_closed");
9223
+ if (this._status === "connecting" || this._status === "open")
9224
+ return;
9225
+ this.setConnStatus("connecting");
9226
+ const ws = new WebSocket2(this.mesh.brokerUrl);
9227
+ this.ws = ws;
9228
+ return new Promise((resolve, reject) => {
9229
+ ws.on("open", async () => {
9230
+ try {
9231
+ if (!this.sessionPubkey) {
9232
+ const { generateKeypair: generateKeypair4 } = await Promise.resolve().then(() => (init_facade7(), exports_facade4));
9233
+ const kp = await generateKeypair4();
9234
+ this.sessionPubkey = kp.publicKey;
9235
+ this.sessionSecretKey = kp.secretKey;
9236
+ }
9237
+ const { timestamp: timestamp2, signature } = await signHello(this.mesh.meshId, this.mesh.memberId, this.mesh.pubkey, this.mesh.secretKey);
9238
+ ws.send(JSON.stringify({
9239
+ type: "hello",
9240
+ meshId: this.mesh.meshId,
9241
+ memberId: this.mesh.memberId,
9242
+ pubkey: this.mesh.pubkey,
9243
+ sessionPubkey: this.sessionPubkey,
9244
+ displayName: this.opts.displayName,
9245
+ sessionId: `daemon-${process.pid}`,
9246
+ pid: process.pid,
9247
+ cwd: process.cwd(),
9248
+ hostname: __require("node:os").hostname(),
9249
+ peerType: "ai",
9250
+ channel: "claudemesh-daemon",
9251
+ timestamp: timestamp2,
9252
+ signature
9253
+ }));
9254
+ this.helloTimer = setTimeout(() => {
9255
+ this.log("warn", "broker_hello_ack_timeout");
9256
+ try {
9257
+ ws.close();
9258
+ } catch {}
9259
+ reject(new Error("hello_ack_timeout"));
9260
+ }, HELLO_ACK_TIMEOUT_MS2);
9261
+ } catch (e) {
9262
+ reject(e instanceof Error ? e : new Error(String(e)));
9263
+ }
9264
+ });
9265
+ ws.on("message", (raw) => {
9266
+ let msg;
9267
+ try {
9268
+ msg = JSON.parse(raw.toString());
9269
+ } catch {
9270
+ return;
9271
+ }
9272
+ if (msg.type === "hello_ack") {
9273
+ if (this.helloTimer)
9274
+ clearTimeout(this.helloTimer);
9275
+ this.helloTimer = null;
9276
+ this.setConnStatus("open");
9277
+ this.reconnectAttempt = 0;
9278
+ const queued = this.opens.slice();
9279
+ this.opens.length = 0;
9280
+ for (const fn of queued) {
9281
+ try {
9282
+ fn();
9283
+ } catch (e) {
9284
+ this.log("warn", "open_handler_failed", { err: String(e) });
9285
+ }
9286
+ }
9287
+ resolve();
9288
+ return;
9289
+ }
9290
+ if (msg.type === "ack") {
9291
+ const id = String(msg.id ?? "");
9292
+ const ack = this.pendingAcks.get(id);
9293
+ if (ack) {
9294
+ this.pendingAcks.delete(id);
9295
+ clearTimeout(ack.timer);
9296
+ if (typeof msg.error === "string" && msg.error.length > 0) {
9297
+ ack.resolve({ ok: false, error: msg.error, permanent: classifyPermanent(msg.error) });
9298
+ } else {
9299
+ ack.resolve({ ok: true, messageId: String(msg.messageId ?? id) });
9300
+ }
9301
+ }
9302
+ return;
9303
+ }
9304
+ if (msg.type === "peers_list") {
9305
+ const reqId = String(msg._reqId ?? "");
9306
+ const pending = this.peerListResolvers.get(reqId);
9307
+ if (pending) {
9308
+ this.peerListResolvers.delete(reqId);
9309
+ clearTimeout(pending.timer);
9310
+ pending.resolve(Array.isArray(msg.peers) ? msg.peers : []);
9311
+ }
9312
+ return;
9313
+ }
9314
+ if (msg.type === "push" || msg.type === "inbound") {
9315
+ this.opts.onPush?.(msg);
9316
+ return;
9317
+ }
9318
+ });
9319
+ ws.on("close", (code, reason) => {
9320
+ if (this.helloTimer) {
9321
+ clearTimeout(this.helloTimer);
9322
+ this.helloTimer = null;
9323
+ }
9324
+ this.failPendingAcks(`broker_disconnected_${code}`);
9325
+ if (this.closed) {
9326
+ this.setConnStatus("closed");
9327
+ return;
9328
+ }
9329
+ this.setConnStatus("reconnecting");
9330
+ const wait = BACKOFF_CAPS_MS[Math.min(this.reconnectAttempt, BACKOFF_CAPS_MS.length - 1)] ?? 30000;
9331
+ this.reconnectAttempt++;
9332
+ this.log("info", "broker_reconnect_scheduled", { wait_ms: wait, code, reason: reason.toString("utf8") });
9333
+ this.reconnectTimer = setTimeout(() => this.connect().catch((err) => this.log("warn", "broker_reconnect_failed", { err: String(err) })), wait);
9334
+ if (this._status === "connecting")
9335
+ reject(new Error(`closed_before_hello_${code}`));
9336
+ });
9337
+ ws.on("error", (err) => this.log("warn", "broker_ws_error", { err: err.message }));
9338
+ });
9339
+ }
9340
+ send(req) {
9341
+ return new Promise((resolve) => {
9342
+ const dispatch = () => {
9343
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
9344
+ resolve({ ok: false, error: "broker_not_open", permanent: false });
9345
+ return;
9346
+ }
9347
+ const id = req.client_message_id;
9348
+ const timer = setTimeout(() => {
9349
+ if (this.pendingAcks.delete(id)) {
9350
+ resolve({ ok: false, error: "ack_timeout", permanent: false });
9351
+ }
9352
+ }, SEND_ACK_TIMEOUT_MS);
9353
+ this.pendingAcks.set(id, { resolve, timer });
9354
+ try {
9355
+ this.ws.send(JSON.stringify({
9356
+ type: "send",
9357
+ id,
9358
+ client_message_id: id,
9359
+ request_fingerprint: req.request_fingerprint_hex,
9360
+ targetSpec: req.targetSpec,
9361
+ priority: req.priority,
9362
+ nonce: req.nonce,
9363
+ ciphertext: req.ciphertext
9364
+ }));
9365
+ } catch (e) {
9366
+ this.pendingAcks.delete(id);
9367
+ clearTimeout(timer);
9368
+ resolve({ ok: false, error: `ws_write_failed: ${String(e)}`, permanent: false });
9369
+ }
9370
+ };
9371
+ if (this._status === "open")
9372
+ dispatch();
9373
+ else
9374
+ this.opens.push(dispatch);
9375
+ });
9376
+ }
9377
+ async listPeers(timeoutMs = 5000) {
9378
+ if (this._status !== "open" || !this.ws)
9379
+ return [];
9380
+ return new Promise((resolve) => {
9381
+ const reqId = `pl-${++this.reqCounter}`;
9382
+ const timer = setTimeout(() => {
9383
+ if (this.peerListResolvers.delete(reqId))
9384
+ resolve([]);
9385
+ }, timeoutMs);
9386
+ this.peerListResolvers.set(reqId, { resolve, timer });
9387
+ try {
9388
+ this.ws.send(JSON.stringify({ type: "list_peers", _reqId: reqId }));
9389
+ } catch {
9390
+ this.peerListResolvers.delete(reqId);
9391
+ clearTimeout(timer);
9392
+ resolve([]);
9393
+ }
9394
+ });
9395
+ }
9396
+ setProfile(profile) {
9397
+ if (this._status !== "open" || !this.ws)
9398
+ return;
9399
+ try {
9400
+ this.ws.send(JSON.stringify({ type: "set_profile", ...profile }));
9401
+ } catch {}
9402
+ }
9403
+ setSummary(summary) {
9404
+ if (this._status !== "open" || !this.ws)
9405
+ return;
9406
+ try {
9407
+ this.ws.send(JSON.stringify({ type: "set_summary", summary }));
9408
+ } catch {}
9409
+ }
9410
+ setStatus(status) {
9411
+ if (this._status !== "open" || !this.ws)
9412
+ return;
9413
+ try {
9414
+ this.ws.send(JSON.stringify({ type: "set_status", status }));
9415
+ } catch {}
9416
+ }
9417
+ setVisible(visible) {
9418
+ if (this._status !== "open" || !this.ws)
9419
+ return;
9420
+ try {
9421
+ this.ws.send(JSON.stringify({ type: "set_visible", visible }));
9422
+ } catch {}
9423
+ }
9424
+ async close() {
9425
+ this.closed = true;
9426
+ if (this.reconnectTimer) {
9427
+ clearTimeout(this.reconnectTimer);
9428
+ this.reconnectTimer = null;
9429
+ }
9430
+ if (this.helloTimer) {
9431
+ clearTimeout(this.helloTimer);
9432
+ this.helloTimer = null;
9433
+ }
9434
+ this.failPendingAcks("daemon_shutdown");
9435
+ try {
9436
+ this.ws?.close();
9437
+ } catch {}
9438
+ this.setConnStatus("closed");
9439
+ }
9440
+ getSessionKeys() {
9441
+ if (!this.sessionPubkey || !this.sessionSecretKey)
9442
+ return null;
9443
+ return { sessionPubkey: this.sessionPubkey, sessionSecretKey: this.sessionSecretKey };
9444
+ }
9445
+ failPendingAcks(reason) {
9446
+ for (const [id, ack] of this.pendingAcks) {
9447
+ clearTimeout(ack.timer);
9448
+ ack.resolve({ ok: false, error: reason, permanent: false });
9449
+ this.pendingAcks.delete(id);
9450
+ }
9451
+ }
9452
+ }
9453
+ function defaultLog(level, msg, meta) {
9454
+ const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
9455
+ if (level === "info")
9456
+ process.stdout.write(line + `
9457
+ `);
9458
+ else
9459
+ process.stderr.write(line + `
9460
+ `);
9461
+ }
9462
+ function classifyPermanent(err) {
9463
+ return /payload_too_large|forbidden|not_found|invalid|schema|auth|signature/i.test(err);
9464
+ }
9465
+ var HELLO_ACK_TIMEOUT_MS2 = 5000, SEND_ACK_TIMEOUT_MS = 15000, BACKOFF_CAPS_MS;
9466
+ var init_broker = __esm(() => {
9467
+ init_hello_sig();
9468
+ BACKOFF_CAPS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
9469
+ });
9470
+
9471
+ // src/daemon/drain.ts
9472
+ function startDrainWorker(opts) {
9473
+ const log2 = opts.log ?? defaultLog2;
9474
+ let stopped = false;
9475
+ let wakeResolve = null;
9476
+ let wakePromise = new Promise((r) => {
9477
+ wakeResolve = r;
9478
+ });
9479
+ const wake = () => {
9480
+ if (wakeResolve) {
9481
+ const r = wakeResolve;
9482
+ wakeResolve = null;
9483
+ r();
9484
+ }
9485
+ };
9486
+ const tick = async () => {
9487
+ while (!stopped) {
9488
+ try {
9489
+ await drainOnce(opts, log2);
9490
+ } catch (e) {
9491
+ log2("warn", "drain_tick_failed", { err: String(e) });
9492
+ }
9493
+ await Promise.race([
9494
+ wakePromise,
9495
+ new Promise((r) => setTimeout(r, POLL_INTERVAL_MS))
9496
+ ]);
9497
+ wakePromise = new Promise((r) => {
9498
+ wakeResolve = r;
9499
+ });
9500
+ }
9501
+ };
9502
+ tick();
9503
+ return {
9504
+ wake,
9505
+ close: async () => {
9506
+ stopped = true;
9507
+ wake();
9508
+ }
9509
+ };
9510
+ }
9511
+ async function drainOnce(opts, log2) {
9512
+ const now = Date.now();
9513
+ const rows = opts.db.prepare(`
9514
+ SELECT id, client_message_id, request_fingerprint, payload, attempts
9515
+ FROM outbox
9516
+ WHERE status = 'pending' AND next_attempt_at <= ?
9517
+ ORDER BY enqueued_at
9518
+ LIMIT 32
9519
+ `).all(now);
9520
+ if (rows.length === 0)
9521
+ return;
9522
+ for (const row of rows) {
9523
+ if (markInflight(opts.db, row.id, now) === 0)
9524
+ continue;
9525
+ const fpHex = bufferToHex(row.request_fingerprint);
9526
+ const sessionKeys = opts.broker.getSessionKeys();
9527
+ const targetSpec = "*";
9528
+ const nonce = await randomNonce2();
9529
+ const ciphertext = Buffer.from(row.payload).toString("base64");
9530
+ let res;
9531
+ try {
9532
+ res = await opts.broker.send({
9533
+ targetSpec,
9534
+ priority: "next",
9535
+ nonce,
9536
+ ciphertext,
9537
+ client_message_id: row.client_message_id,
9538
+ request_fingerprint_hex: fpHex
9539
+ });
9540
+ } catch (e) {
9541
+ log2("warn", "drain_send_threw", { id: row.id, err: String(e) });
9542
+ backoffPending(opts.db, row.id, row.attempts + 1, "exception", String(e));
9543
+ continue;
9544
+ }
9545
+ if (res.ok) {
9546
+ markDone(opts.db, row.id, res.messageId, Date.now());
9547
+ } else if (res.permanent) {
9548
+ log2("warn", "drain_permanent_failure", { id: row.id, err: res.error });
9549
+ markDead(opts.db, row.id, res.error);
9550
+ } else if (row.attempts + 1 >= MAX_ATTEMPTS_PER_ROW) {
9551
+ log2("warn", "drain_max_attempts", { id: row.id, err: res.error });
9552
+ markDead(opts.db, row.id, `max_attempts: ${res.error}`);
9553
+ } else {
9554
+ backoffPending(opts.db, row.id, row.attempts + 1, "retry", res.error);
9555
+ }
9556
+ }
9557
+ }
9558
+ function markInflight(db, id, now) {
9559
+ return Number(db.prepare(`
9560
+ UPDATE outbox
9561
+ SET status = 'inflight', attempts = attempts + 1, next_attempt_at = ?
9562
+ WHERE id = ? AND status = 'pending'
9563
+ `).run(now + BACKOFF_CAP_MS, id).changes);
9564
+ }
9565
+ function markDone(db, id, brokerMessageId, now) {
9566
+ db.prepare(`
9567
+ UPDATE outbox
9568
+ SET status = 'done', delivered_at = ?, broker_message_id = ?, last_error = NULL
9569
+ WHERE id = ?
9570
+ `).run(now, brokerMessageId, id);
9571
+ }
9572
+ function markDead(db, id, err) {
9573
+ db.prepare(`UPDATE outbox SET status = 'dead', last_error = ? WHERE id = ?`).run(err, id);
9574
+ }
9575
+ function backoffPending(db, id, attempts, _kind, err) {
9576
+ const wait = Math.min(BACKOFF_CAP_MS, BACKOFF_BASE_MS * 2 ** Math.min(attempts, 12));
9577
+ const next = Date.now() + wait;
9578
+ db.prepare(`
9579
+ UPDATE outbox
9580
+ SET status = 'pending', attempts = ?, next_attempt_at = ?, last_error = ?
9581
+ WHERE id = ?
9582
+ `).run(attempts, next, err, id);
9583
+ }
9584
+ function bufferToHex(b) {
9585
+ let s = "";
9586
+ for (let i = 0;i < b.length; i++)
9587
+ s += b[i].toString(16).padStart(2, "0");
9588
+ return s;
9589
+ }
9590
+ async function randomNonce2() {
9591
+ const { randomBytes: randomBytes5 } = await import("node:crypto");
9592
+ return randomBytes5(24).toString("base64");
9593
+ }
9594
+ function defaultLog2(level, msg, meta) {
9595
+ const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
9596
+ if (level === "info")
9597
+ process.stdout.write(line + `
9598
+ `);
9599
+ else
9600
+ process.stderr.write(line + `
9601
+ `);
9602
+ }
9603
+ var POLL_INTERVAL_MS = 500, MAX_ATTEMPTS_PER_ROW = 25, BACKOFF_BASE_MS = 500, BACKOFF_CAP_MS = 30000;
9604
+ var init_drain = () => {};
9605
+
9606
+ // src/daemon/inbound.ts
9607
+ import { randomUUID as randomUUID5 } from "node:crypto";
9608
+ async function handleBrokerPush(msg, ctx) {
9609
+ if (msg.subtype === "system" && typeof msg.event === "string") {
9610
+ ctx.bus.publish(mapSystemEventKind(msg.event), {
9611
+ mesh: ctx.meshSlug,
9612
+ event: msg.event,
9613
+ ...msg.eventData ?? {}
9614
+ });
9615
+ return;
9616
+ }
9617
+ if (msg.type !== "push")
9618
+ return;
9619
+ const brokerMessageId = stringOrNull(msg.messageId);
9620
+ const senderPubkey = stringOrNull(msg.senderPubkey) ?? "";
9621
+ const senderName = stringOrNull(msg.senderName) ?? senderPubkey.slice(0, 8);
9622
+ const topic = stringOrNull(msg.topic);
9623
+ const replyToId = stringOrNull(msg.replyToId);
9624
+ const ciphertext = stringOrNull(msg.ciphertext) ?? "";
9625
+ const nonce = stringOrNull(msg.nonce) ?? "";
9626
+ const createdAt = stringOrNull(msg.createdAt);
9627
+ const clientMessageId = stringOrNull(msg.client_message_id) ?? brokerMessageId ?? randomUUID5();
9628
+ const body = await decryptOrFallback({
9629
+ ciphertext,
9630
+ nonce,
9631
+ senderPubkey,
9632
+ ctx
9633
+ });
9634
+ const id = randomUUID5();
9635
+ const inserted = insertIfNew(ctx.db, {
9636
+ id,
9637
+ client_message_id: clientMessageId,
9638
+ broker_message_id: brokerMessageId,
9639
+ mesh: ctx.meshSlug,
9640
+ topic,
9641
+ sender_pubkey: senderPubkey,
9642
+ sender_name: senderName,
9643
+ body,
9644
+ meta: createdAt ? JSON.stringify({ created_at: createdAt }) : null,
9645
+ received_at: Date.now(),
9646
+ reply_to_id: replyToId
9647
+ });
9648
+ if (!inserted)
9649
+ return;
9650
+ ctx.bus.publish("message", {
9651
+ id,
9652
+ mesh: ctx.meshSlug,
9653
+ client_message_id: clientMessageId,
9654
+ broker_message_id: brokerMessageId,
9655
+ sender_pubkey: senderPubkey,
9656
+ sender_name: senderName,
9657
+ topic,
9658
+ reply_to_id: replyToId,
9659
+ body,
9660
+ created_at: createdAt
9661
+ });
9662
+ }
9663
+ async function decryptOrFallback(args) {
9664
+ const { ciphertext, nonce, senderPubkey, ctx } = args;
9665
+ if (!ciphertext)
9666
+ return null;
9667
+ if (nonce && senderPubkey) {
9668
+ const envelope = { nonce, ciphertext };
9669
+ if (ctx.sessionSecretKeyHex) {
9670
+ const pt = await decryptDirect(envelope, senderPubkey, ctx.sessionSecretKeyHex);
9671
+ if (pt !== null)
9672
+ return pt;
9673
+ }
9674
+ if (ctx.recipientSecretKeyHex) {
9675
+ const pt = await decryptDirect(envelope, senderPubkey, ctx.recipientSecretKeyHex);
9676
+ if (pt !== null)
9677
+ return pt;
9678
+ }
9679
+ }
9680
+ try {
9681
+ return Buffer.from(ciphertext, "base64").toString("utf8");
9682
+ } catch (e) {
9683
+ ctx.log?.("warn", "inbound_b64_decode_failed", { err: String(e) });
9684
+ return null;
9685
+ }
9686
+ }
9687
+ function stringOrNull(v) {
9688
+ return typeof v === "string" && v.length > 0 ? v : null;
9689
+ }
9690
+ function mapSystemEventKind(event) {
9691
+ if (event === "peer_joined")
9692
+ return "peer_join";
9693
+ if (event === "peer_left")
9694
+ return "peer_leave";
9695
+ return "system";
9696
+ }
9697
+ var init_inbound = __esm(() => {
9698
+ init_facade7();
9699
+ });
9700
+
9701
+ // src/daemon/identity.ts
9702
+ var exports_identity = {};
9703
+ __export(exports_identity, {
9704
+ computeCurrentFingerprint: () => computeCurrentFingerprint,
9705
+ checkFingerprint: () => checkFingerprint,
9706
+ acceptCurrentHost: () => acceptCurrentHost
9707
+ });
9708
+ import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
9709
+ import { join as join7 } from "node:path";
9710
+ import { createHash as createHash2 } from "node:crypto";
9711
+ import { networkInterfaces } from "node:os";
9712
+ function path() {
9713
+ return join7(DAEMON_PATHS.DAEMON_DIR, FILE_NAME);
9714
+ }
9715
+ function computeCurrentFingerprint() {
9716
+ const host_id = readHostId() ?? "";
9717
+ const stable_mac = pickStableMac() ?? "";
9718
+ const fp = createHash2("sha256").update(host_id, "utf8").update("\x00").update(stable_mac, "utf8").digest("hex");
9719
+ return {
9720
+ schema_version: 1,
9721
+ fingerprint: fp,
9722
+ host_id,
9723
+ stable_mac,
9724
+ written_at: new Date().toISOString()
9725
+ };
9726
+ }
9727
+ function checkFingerprint() {
9728
+ const current = computeCurrentFingerprint();
9729
+ if (!existsSync10(path())) {
9730
+ writeFileSync8(path(), JSON.stringify(current, null, 2), { mode: 384 });
9731
+ return { result: "first_run", current };
9732
+ }
9733
+ let stored;
9734
+ try {
9735
+ stored = JSON.parse(readFileSync7(path(), "utf8"));
9736
+ } catch {
9737
+ return { result: "unavailable", current };
9738
+ }
9739
+ if (stored.fingerprint === current.fingerprint)
9740
+ return { result: "match", current, stored };
9741
+ return { result: "mismatch", current, stored };
9742
+ }
9743
+ function acceptCurrentHost() {
9744
+ const current = computeCurrentFingerprint();
9745
+ writeFileSync8(path(), JSON.stringify(current, null, 2), { mode: 384 });
9746
+ return current;
9747
+ }
9748
+ function readHostId() {
9749
+ if (process.platform === "linux") {
9750
+ for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
9751
+ try {
9752
+ const raw = readFileSync7(p, "utf8").trim();
9753
+ if (raw)
9754
+ return `linux:${raw}`;
9755
+ } catch {}
9756
+ }
9757
+ return null;
9758
+ }
9759
+ if (process.platform === "darwin") {
9760
+ return null;
9761
+ }
9762
+ return null;
9763
+ }
9764
+ function pickStableMac() {
9765
+ const ifs = networkInterfaces();
9766
+ const candidates = [];
9767
+ for (const [name, addrs] of Object.entries(ifs)) {
9768
+ if (!addrs)
9769
+ continue;
9770
+ if (isIgnoredInterface(name))
9771
+ continue;
9772
+ for (const a of addrs) {
9773
+ if (a.internal)
9774
+ continue;
9775
+ if (!a.mac || a.mac === "00:00:00:00:00:00")
9776
+ continue;
9777
+ candidates.push(`${name}::${a.mac}`);
9778
+ break;
9779
+ }
9780
+ }
9781
+ if (candidates.length === 0)
9782
+ return null;
9783
+ candidates.sort();
9784
+ const first = candidates[0];
9785
+ const idx = first.indexOf("::");
9786
+ return idx >= 0 ? first.slice(idx + 2) : first;
9787
+ }
9788
+ function isIgnoredInterface(name) {
9789
+ return /^(lo|docker|br-|veth|tap|tun|tailscale|wg|utun|ppp|vboxnet|vmnet|awdl|llw)/i.test(name);
9790
+ }
9791
+ var FILE_NAME = "host_fingerprint.json";
9792
+ var init_identity = __esm(() => {
9793
+ init_paths2();
9794
+ });
9795
+
9796
+ // src/daemon/run.ts
9797
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync8 } from "node:fs";
9798
+ function detectContainer() {
9799
+ if (process.env.KUBERNETES_SERVICE_HOST)
9800
+ return true;
9801
+ if (process.env.CONTAINER === "1")
9802
+ return true;
9803
+ try {
9804
+ if (existsSync11("/.dockerenv"))
9805
+ return true;
9806
+ const cg = readFileSync8("/proc/1/cgroup", "utf8");
9807
+ if (/(docker|kubepods|containerd)/.test(cg))
9808
+ return true;
9809
+ } catch {}
9810
+ return false;
9811
+ }
9812
+ async function runDaemon(opts = {}) {
9813
+ mkdirSync6(DAEMON_PATHS.DAEMON_DIR, { recursive: true, mode: 448 });
9814
+ const lock = acquireSingletonLock();
9815
+ if (lock.result === "already-running") {
9816
+ process.stderr.write(`daemon already running (pid ${lock.pid})
9817
+ `);
9818
+ return 1;
9819
+ }
9820
+ if (lock.result === "stale") {
9821
+ process.stderr.write(`recovered stale pid file; starting fresh
9822
+ `);
9823
+ }
9824
+ const fpCheck = checkFingerprint();
9825
+ const policy = opts.clonePolicy ?? "refuse";
9826
+ if (fpCheck.result === "mismatch") {
9827
+ const msg = `host_fingerprint mismatch: this daemon dir was started on a different host.`;
9828
+ if (policy === "refuse") {
9829
+ process.stderr.write(`${msg}
9830
+ `);
9831
+ process.stderr.write(` stored host_id: ${fpCheck.stored?.host_id}
9832
+ `);
9833
+ process.stderr.write(` current host_id: ${fpCheck.current.host_id}
9834
+ `);
9835
+ process.stderr.write(`Run \`claudemesh daemon accept-host\` to write a fresh fingerprint, or
9836
+ `);
9837
+ process.stderr.write(`run \`claudemesh daemon remint\` to mint a new keypair (Sprint 7+).
9838
+ `);
9839
+ releaseSingletonLock();
9840
+ return 4;
9841
+ }
9842
+ if (policy === "warn") {
9843
+ process.stderr.write(`WARN: ${msg} (continuing per [clone] policy=warn)
9844
+ `);
9845
+ }
9846
+ }
9847
+ if (fpCheck.result === "first_run") {
9848
+ process.stdout.write(JSON.stringify({
9849
+ msg: "host_fingerprint_written",
9850
+ fingerprint_prefix: fpCheck.current.fingerprint.slice(0, 16),
9851
+ ts: new Date().toISOString()
9852
+ }) + `
9853
+ `);
9854
+ }
9855
+ const localToken = ensureLocalToken();
9856
+ const tcpEnabled = opts.tcpEnabled ?? !detectContainer();
9857
+ let outboxDb;
9858
+ let inboxDb;
9859
+ try {
9860
+ outboxDb = await openSqlite(DAEMON_PATHS.OUTBOX_DB);
9861
+ migrateOutbox(outboxDb);
9862
+ inboxDb = await openSqlite(DAEMON_PATHS.INBOX_DB);
9863
+ migrateInbox(inboxDb);
9864
+ } catch (err) {
9865
+ process.stderr.write(`db open failed: ${String(err)}
9866
+ `);
9867
+ releaseSingletonLock();
9868
+ return 1;
9869
+ }
9870
+ const bus = new EventBus;
9871
+ const cfg = readConfig();
9872
+ let mesh = null;
9873
+ if (opts.mesh) {
9874
+ mesh = cfg.meshes.find((m) => m.slug === opts.mesh) ?? null;
9875
+ if (!mesh) {
9876
+ process.stderr.write(`mesh not found: ${opts.mesh}
9877
+ `);
9878
+ process.stderr.write(`joined meshes: ${cfg.meshes.map((m) => m.slug).join(", ") || "(none)"}
9879
+ `);
9880
+ releaseSingletonLock();
9881
+ try {
9882
+ outboxDb.close();
9883
+ } catch {}
9884
+ return 2;
9885
+ }
9886
+ } else if (cfg.meshes.length === 1) {
9887
+ mesh = cfg.meshes[0];
9888
+ } else if (cfg.meshes.length === 0) {
9889
+ process.stderr.write(`no mesh joined; run \`claudemesh join <invite-url>\` first
9890
+ `);
9891
+ releaseSingletonLock();
9892
+ try {
9893
+ outboxDb.close();
9894
+ } catch {}
9895
+ return 2;
9896
+ } else {
9897
+ process.stderr.write(`multiple meshes joined; pass --mesh <slug>
9898
+ `);
9899
+ process.stderr.write(`available: ${cfg.meshes.map((m) => m.slug).join(", ")}
9900
+ `);
9901
+ releaseSingletonLock();
9902
+ try {
9903
+ outboxDb.close();
9904
+ } catch {}
9905
+ return 2;
9906
+ }
9907
+ const broker = new DaemonBrokerClient(mesh, {
9908
+ displayName: opts.displayName,
9909
+ onStatusChange: (s) => {
9910
+ process.stdout.write(JSON.stringify({
9911
+ msg: "broker_status",
9912
+ status: s,
9913
+ mesh: mesh.slug,
9914
+ ts: new Date().toISOString()
9915
+ }) + `
9916
+ `);
9917
+ bus.publish("broker_status", { mesh: mesh.slug, status: s });
9918
+ },
9919
+ onPush: (m) => {
9920
+ const sessionKeys = broker.getSessionKeys();
9921
+ handleBrokerPush(m, {
9922
+ db: inboxDb,
9923
+ bus,
9924
+ meshSlug: mesh.slug,
9925
+ recipientSecretKeyHex: mesh.secretKey,
9926
+ sessionSecretKeyHex: sessionKeys?.sessionSecretKey
9927
+ });
9928
+ }
9929
+ });
9930
+ broker.connect().catch((err) => process.stderr.write(`broker connect failed: ${String(err)}
9931
+ `));
9932
+ let drain = null;
9933
+ drain = startDrainWorker({ db: outboxDb, broker });
9934
+ const ipc2 = startIpcServer({
9935
+ localToken,
9936
+ tcpEnabled,
9937
+ publicHealthCheck: opts.publicHealthCheck,
9938
+ outboxDb,
9939
+ inboxDb,
9940
+ bus,
9941
+ broker,
9942
+ onPendingInserted: () => drain?.wake()
9943
+ });
9944
+ try {
9945
+ await ipc2.ready;
9946
+ } catch (err) {
9947
+ process.stderr.write(`ipc listen failed: ${String(err)}
9948
+ `);
9949
+ releaseSingletonLock();
9950
+ return 1;
9951
+ }
9952
+ process.stdout.write(JSON.stringify({
9953
+ msg: "daemon_started",
9954
+ pid: process.pid,
9955
+ sock: DAEMON_PATHS.SOCK_FILE,
9956
+ tcp: tcpEnabled ? `127.0.0.1:47823` : null,
9957
+ mesh: mesh.slug,
9958
+ ts: new Date().toISOString()
9959
+ }) + `
9960
+ `);
9961
+ let shuttingDown = false;
9962
+ const shutdown = async (sig) => {
9963
+ if (shuttingDown)
9964
+ return;
9965
+ shuttingDown = true;
9966
+ process.stdout.write(JSON.stringify({ msg: "daemon_shutdown", signal: sig, ts: new Date().toISOString() }) + `
9967
+ `);
9968
+ if (drain)
9969
+ await drain.close();
9970
+ await broker.close();
9971
+ await ipc2.close();
9972
+ try {
9973
+ outboxDb.close();
9974
+ } catch {}
9975
+ try {
9976
+ inboxDb.close();
9977
+ } catch {}
9978
+ releaseSingletonLock();
9979
+ process.exit(0);
9980
+ };
9981
+ process.on("SIGINT", () => shutdown("SIGINT"));
9982
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
9983
+ return new Promise(() => {});
9984
+ }
9985
+ var init_run = __esm(() => {
9986
+ init_paths2();
9987
+ init_lock();
9988
+ init_local_token();
9989
+ init_server();
9990
+ init_broker();
9991
+ init_drain();
9992
+ init_inbound();
9993
+ init_identity();
9994
+ init_facade();
9995
+ });
9996
+
9997
+ // src/daemon/service-install.ts
9998
+ var exports_service_install = {};
9999
+ __export(exports_service_install, {
10000
+ uninstallService: () => uninstallService,
10001
+ readInstalledUnit: () => readInstalledUnit,
10002
+ installService: () => installService,
10003
+ detectPlatform: () => detectPlatform
10004
+ });
10005
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync9, unlinkSync as unlinkSync4, readFileSync as readFileSync9 } from "node:fs";
10006
+ import { execSync as execSync2 } from "node:child_process";
10007
+ import { homedir as homedir6 } from "node:os";
10008
+ import { join as join8, dirname as dirname5 } from "node:path";
10009
+ function detectPlatform() {
10010
+ if (process.platform === "darwin")
10011
+ return "darwin";
10012
+ if (process.platform === "linux")
10013
+ return "linux";
10014
+ return null;
10015
+ }
10016
+ function isCi() {
10017
+ return !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.BUILDKITE || process.env.CIRCLECI || process.env.JENKINS_URL);
10018
+ }
10019
+ function installService(args) {
10020
+ const platform5 = detectPlatform();
10021
+ if (!platform5)
10022
+ throw new Error(`unsupported platform: ${process.platform}`);
10023
+ if (isCi() && !args.allowCi) {
10024
+ throw new Error("Refusing to install persistent service in CI; pass --allow-ci-persistent to override.");
10025
+ }
10026
+ if (!existsSync12(args.binaryPath)) {
10027
+ throw new Error(`binary not found at ${args.binaryPath}`);
10028
+ }
10029
+ mkdirSync7(DAEMON_PATHS.DAEMON_DIR, { recursive: true, mode: 448 });
10030
+ if (platform5 === "darwin")
10031
+ return installDarwin(args);
10032
+ return installLinux(args);
10033
+ }
10034
+ function uninstallService() {
10035
+ const platform5 = detectPlatform();
10036
+ const removed = [];
10037
+ if (platform5 === "darwin") {
10038
+ const p = darwinPlistPath();
10039
+ try {
10040
+ execSync2(`launchctl bootout gui/$(id -u)/${SERVICE_LABEL}`, { stdio: "ignore" });
10041
+ } catch {}
10042
+ if (existsSync12(p)) {
10043
+ unlinkSync4(p);
10044
+ removed.push(p);
10045
+ }
10046
+ } else if (platform5 === "linux") {
10047
+ const p = linuxUnitPath();
10048
+ try {
10049
+ execSync2(`systemctl --user disable --now ${SYSTEMD_UNIT}`, { stdio: "ignore" });
10050
+ } catch {}
10051
+ if (existsSync12(p)) {
10052
+ unlinkSync4(p);
10053
+ removed.push(p);
10054
+ }
10055
+ }
10056
+ return { platform: platform5, removed };
10057
+ }
10058
+ function darwinPlistPath() {
10059
+ return join8(homedir6(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
10060
+ }
10061
+ function installDarwin(args) {
10062
+ const plist = darwinPlistPath();
10063
+ mkdirSync7(dirname5(plist), { recursive: true });
10064
+ const log2 = DAEMON_PATHS.LOG_FILE;
10065
+ const meshArgs = [
10066
+ "<string>daemon</string>",
10067
+ "<string>up</string>",
10068
+ "<string>--mesh</string>",
10069
+ `<string>${escapeXml(args.meshSlug)}</string>`,
10070
+ ...args.displayName ? ["<string>--name</string>", `<string>${escapeXml(args.displayName)}</string>`] : []
10071
+ ].join(`
10072
+ `);
10073
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
10074
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
10075
+ <plist version="1.0">
10076
+ <dict>
10077
+ <key>Label</key>
10078
+ <string>${SERVICE_LABEL}</string>
10079
+ <key>ProgramArguments</key>
10080
+ <array>
10081
+ <string>${escapeXml(args.binaryPath)}</string>
10082
+ ${meshArgs}
10083
+ </array>
10084
+ <key>RunAtLoad</key>
10085
+ <true/>
10086
+ <key>KeepAlive</key>
10087
+ <true/>
10088
+ <key>StandardOutPath</key>
10089
+ <string>${escapeXml(log2)}</string>
10090
+ <key>StandardErrorPath</key>
10091
+ <string>${escapeXml(log2)}</string>
10092
+ <key>WorkingDirectory</key>
10093
+ <string>${escapeXml(homedir6())}</string>
10094
+ <key>EnvironmentVariables</key>
10095
+ <dict>
10096
+ <key>HOME</key>
10097
+ <string>${escapeXml(homedir6())}</string>
10098
+ <key>PATH</key>
10099
+ <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
10100
+ </dict>
10101
+ </dict>
10102
+ </plist>
10103
+ `;
10104
+ writeFileSync9(plist, xml, { mode: 420 });
10105
+ return {
10106
+ platform: "darwin",
10107
+ unitPath: plist,
10108
+ bootCommand: `launchctl bootstrap gui/$(id -u) ${shellQuote(plist)}`
10109
+ };
10110
+ }
10111
+ function linuxUnitPath() {
10112
+ return join8(homedir6(), ".config", "systemd", "user", SYSTEMD_UNIT);
10113
+ }
10114
+ function installLinux(args) {
10115
+ const unit = linuxUnitPath();
10116
+ mkdirSync7(dirname5(unit), { recursive: true });
10117
+ const execArgs = [
10118
+ "daemon",
10119
+ "up",
10120
+ "--mesh",
10121
+ args.meshSlug,
10122
+ ...args.displayName ? ["--name", args.displayName] : []
10123
+ ].map(shellQuote).join(" ");
10124
+ const content = `[Unit]
10125
+ Description=claudemesh daemon (peer mesh runtime)
10126
+ After=network-online.target
10127
+ Wants=network-online.target
10128
+
10129
+ [Service]
10130
+ Type=simple
10131
+ ExecStart=${shellQuote(args.binaryPath)} ${execArgs}
10132
+ Restart=always
10133
+ RestartSec=3
10134
+ StandardOutput=append:${DAEMON_PATHS.LOG_FILE}
10135
+ StandardError=append:${DAEMON_PATHS.LOG_FILE}
10136
+ Environment=PATH=/usr/local/bin:/usr/bin:/bin
10137
+
10138
+ [Install]
10139
+ WantedBy=default.target
10140
+ `;
10141
+ writeFileSync9(unit, content, { mode: 420 });
10142
+ return {
10143
+ platform: "linux",
10144
+ unitPath: unit,
10145
+ bootCommand: `systemctl --user daemon-reload && systemctl --user enable --now ${SYSTEMD_UNIT}`
10146
+ };
10147
+ }
10148
+ function escapeXml(s) {
10149
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
10150
+ }
10151
+ function shellQuote(s) {
10152
+ if (/^[\w@%+=:,./-]+$/.test(s))
10153
+ return s;
10154
+ return "'" + s.replace(/'/g, `'"'"'`) + "'";
10155
+ }
10156
+ function readInstalledUnit() {
10157
+ const platform5 = detectPlatform();
10158
+ if (!platform5)
10159
+ return { platform: null, path: null, content: null };
10160
+ const path2 = platform5 === "darwin" ? darwinPlistPath() : linuxUnitPath();
10161
+ if (!existsSync12(path2))
10162
+ return { platform: platform5, path: null, content: null };
10163
+ try {
10164
+ return { platform: platform5, path: path2, content: readFileSync9(path2, "utf8") };
10165
+ } catch {
10166
+ return { platform: platform5, path: path2, content: null };
10167
+ }
10168
+ }
10169
+ var SERVICE_LABEL = "com.claudemesh.daemon", SYSTEMD_UNIT = "claudemesh-daemon.service";
10170
+ var init_service_install = __esm(() => {
10171
+ init_paths2();
10172
+ });
10173
+
10174
+ // src/commands/daemon.ts
10175
+ var exports_daemon = {};
10176
+ __export(exports_daemon, {
10177
+ runDaemonCommand: () => runDaemonCommand
10178
+ });
10179
+ async function runDaemonCommand(sub, opts, rest = []) {
10180
+ switch (sub) {
10181
+ case undefined:
10182
+ case "up":
10183
+ case "start":
10184
+ return runDaemon({
10185
+ tcpEnabled: !opts.noTcp,
10186
+ publicHealthCheck: opts.publicHealth,
10187
+ mesh: opts.mesh,
10188
+ displayName: opts.displayName
10189
+ });
10190
+ case "status":
10191
+ return runStatus(opts);
10192
+ case "version":
10193
+ return runVersion(opts);
10194
+ case "down":
10195
+ case "stop":
10196
+ return runStop(opts);
10197
+ case "accept-host":
10198
+ return runAcceptHost(opts);
10199
+ case "outbox":
10200
+ return runOutbox(rest, opts);
10201
+ case "install-service":
10202
+ return runInstallService(opts);
10203
+ case "uninstall-service":
10204
+ return runUninstallService(opts);
10205
+ default:
10206
+ process.stderr.write(`unknown daemon subcommand: ${sub}
10207
+ `);
10208
+ process.stderr.write(`usage: claudemesh daemon [up|status|version|down|accept-host|outbox|install-service|uninstall-service]
10209
+ `);
10210
+ return 2;
10211
+ }
10212
+ }
10213
+ async function runOutbox(rest, opts) {
10214
+ const sub = rest[0];
10215
+ switch (sub) {
10216
+ case undefined:
10217
+ case "list": {
10218
+ const status = opts.outboxStatus;
10219
+ const path2 = `/v1/outbox${status ? `?status=${status}` : ""}`;
10220
+ try {
10221
+ const res = await ipc({ path: path2 });
10222
+ if (opts.json) {
10223
+ process.stdout.write(JSON.stringify(res.body) + `
10224
+ `);
10225
+ return 0;
10226
+ }
10227
+ if (!res.body.items?.length) {
10228
+ process.stdout.write(`(empty)
10229
+ `);
10230
+ return 0;
10231
+ }
10232
+ for (const r of res.body.items) {
10233
+ const tag = r.status.padEnd(8);
10234
+ const bm = r.broker_message_id ? ` → ${r.broker_message_id}` : "";
10235
+ const err = r.last_error ? ` last_error="${r.last_error.slice(0, 60)}"` : "";
10236
+ process.stdout.write(`${tag} ${r.id} cid=${r.client_message_id} attempts=${r.attempts}${bm}${err}
10237
+ `);
10238
+ }
10239
+ return 0;
10240
+ } catch (err) {
10241
+ process.stderr.write(`daemon unreachable: ${String(err)}
10242
+ `);
10243
+ return 1;
10244
+ }
10245
+ }
10246
+ case "requeue": {
10247
+ const id = rest[1];
10248
+ if (!id) {
10249
+ process.stderr.write(`usage: claudemesh daemon outbox requeue <id> [--new-client-id <id>]
10250
+ `);
10251
+ return 2;
8007
10252
  }
8008
- render.section(`reminders (${scheduled.length})`);
8009
- for (const m of scheduled) {
8010
- const when = new Date(m.deliverAt).toLocaleString();
8011
- const to = m.to === client.getSessionPubkey() ? dim("(self)") : m.to;
8012
- process.stdout.write(` ${bold(m.id.slice(0, 8))} ${dim("")} ${to} ${dim("at")} ${when}
10253
+ const newClientMessageId = opts.newClientId;
10254
+ try {
10255
+ const res = await ipc({
10256
+ method: "POST",
10257
+ path: "/v1/outbox/requeue",
10258
+ body: { id, new_client_message_id: newClientMessageId }
10259
+ });
10260
+ if (res.status === 200) {
10261
+ if (opts.json)
10262
+ process.stdout.write(JSON.stringify(res.body) + `
8013
10263
  `);
8014
- process.stdout.write(` ${dim(m.message.slice(0, 80))}
8015
-
10264
+ else
10265
+ process.stdout.write(`requeued: aborted ${res.body.aborted_row_id} → new ${res.body.new_row_id} ` + `(client_message_id=${res.body.new_client_message_id})
10266
+ `);
10267
+ return 0;
10268
+ }
10269
+ process.stderr.write(`requeue failed (${res.status}): ${res.body.error ?? "unknown"}
10270
+ `);
10271
+ return 1;
10272
+ } catch (err) {
10273
+ process.stderr.write(`daemon unreachable: ${String(err)}
8016
10274
  `);
10275
+ return 1;
8017
10276
  }
8018
- });
8019
- return;
8020
- }
8021
- if (action === "cancel") {
8022
- const id = positional[1];
8023
- if (!id) {
8024
- render.err("Usage: claudemesh remind cancel <id>");
8025
- process.exit(1);
8026
10277
  }
8027
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
8028
- const ok = await client.cancelScheduled(id);
8029
- if (ok)
8030
- render.ok(`cancelled ${bold(id.slice(0, 8))}`);
8031
- else {
8032
- render.err(`not found or already fired: ${id}`);
8033
- process.exit(1);
8034
- }
8035
- });
8036
- return;
10278
+ default:
10279
+ process.stderr.write(`unknown outbox subcommand: ${sub}
10280
+ `);
10281
+ process.stderr.write(`usage: claudemesh daemon outbox [list|requeue <id>]
10282
+ `);
10283
+ return 2;
8037
10284
  }
8038
- const message = action ?? positional.join(" ");
8039
- if (!message) {
8040
- render.err("Usage: claudemesh remind <message> --in <duration>");
8041
- render.info(dim(" claudemesh remind <message> --at <time>"));
8042
- render.info(dim(' claudemesh remind <message> --cron "0 */2 * * *"'));
8043
- render.info(dim(" claudemesh remind list"));
8044
- render.info(dim(" claudemesh remind cancel <id>"));
8045
- process.exit(1);
10285
+ }
10286
+ async function runInstallService(opts) {
10287
+ const { installService: installService2, detectPlatform: detectPlatform2 } = await Promise.resolve().then(() => (init_service_install(), exports_service_install));
10288
+ const platform5 = detectPlatform2();
10289
+ if (!platform5) {
10290
+ process.stderr.write(`unsupported platform: ${process.platform}
10291
+ `);
10292
+ return 2;
8046
10293
  }
8047
- const isCron = !!flags.cron;
8048
- const deliverAt = isCron ? 0 : parseDeliverAt(flags);
8049
- if (!isCron && deliverAt === null) {
8050
- render.err("Specify when", 'use --in <duration> (e.g. "2h", "30m"), --at <time> (e.g. "15:00"), or --cron <expression>');
8051
- process.exit(1);
10294
+ if (!opts.mesh) {
10295
+ process.stderr.write(`pass --mesh <slug> so the service knows which mesh to attach to
10296
+ `);
10297
+ return 2;
8052
10298
  }
8053
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
8054
- let targetSpec;
8055
- if (flags.to && flags.to !== "self") {
8056
- if (flags.to.startsWith("@") || flags.to === "*" || /^[0-9a-f]{64}$/i.test(flags.to)) {
8057
- targetSpec = flags.to;
8058
- } else {
8059
- const peers = await client.listPeers();
8060
- const match = peers.find((p) => p.displayName.toLowerCase() === flags.to.toLowerCase());
8061
- if (!match) {
8062
- render.err(`Peer "${flags.to}" not found`, `online: ${peers.map((p) => p.displayName).join(", ") || "(none)"}`);
8063
- process.exit(1);
8064
- }
8065
- targetSpec = match.pubkey;
8066
- }
8067
- } else {
8068
- targetSpec = client.getSessionPubkey() ?? "*";
8069
- }
8070
- const result = await client.scheduleMessage(targetSpec, message, deliverAt ?? 0, false, flags.cron);
8071
- if (!result) {
8072
- render.err("Broker did not acknowledge — check connection");
8073
- process.exit(1);
8074
- }
8075
- if (flags.json) {
8076
- console.log(JSON.stringify(result));
8077
- return;
10299
+ let binary = process.argv[1] ?? "";
10300
+ if (!binary || /\.ts$/.test(binary) || /node_modules|src\/entrypoints/.test(binary)) {
10301
+ try {
10302
+ const { execSync: execSync3 } = await import("node:child_process");
10303
+ binary = execSync3("which claudemesh", { encoding: "utf8" }).trim();
10304
+ } catch {
10305
+ process.stderr.write(`couldn't resolve a 'claudemesh' binary on PATH; install via npm/homebrew first
10306
+ `);
10307
+ return 1;
8078
10308
  }
8079
- const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
8080
- if (isCron) {
8081
- const nextFire = new Date(result.deliverAt).toLocaleString();
8082
- render.ok(`recurring reminder set`, `${result.scheduledId.slice(0, 8)} · ${clay(message)} → ${toLabel} · cron ${flags.cron} · next ${nextFire}`);
10309
+ }
10310
+ try {
10311
+ const r = installService2({
10312
+ binaryPath: binary,
10313
+ meshSlug: opts.mesh,
10314
+ displayName: opts.displayName
10315
+ });
10316
+ if (opts.json) {
10317
+ process.stdout.write(JSON.stringify({ ok: true, ...r }) + `
10318
+ `);
8083
10319
  } else {
8084
- const when = new Date(result.deliverAt).toLocaleString();
8085
- render.ok(`reminder set`, `${result.scheduledId.slice(0, 8)} · ${clay(message)} → ${toLabel} at ${when}`);
10320
+ process.stdout.write(`installed ${r.platform} service unit: ${r.unitPath}
10321
+ `);
10322
+ process.stdout.write(`bring it up now: ${r.bootCommand}
10323
+ `);
8086
10324
  }
8087
- });
10325
+ return 0;
10326
+ } catch (err) {
10327
+ process.stderr.write(`install-service failed: ${String(err)}
10328
+ `);
10329
+ return 1;
10330
+ }
8088
10331
  }
8089
- var init_remind = __esm(() => {
8090
- init_connect();
8091
- init_render();
8092
- init_styles();
8093
- });
8094
-
8095
- // src/commands/register.ts
8096
- var exports_register = {};
8097
- __export(exports_register, {
8098
- register: () => register2
8099
- });
8100
- async function register2() {
8101
- return login();
10332
+ async function runUninstallService(opts) {
10333
+ const { uninstallService: uninstallService2 } = await Promise.resolve().then(() => (init_service_install(), exports_service_install));
10334
+ const r = uninstallService2();
10335
+ if (opts.json)
10336
+ process.stdout.write(JSON.stringify(r) + `
10337
+ `);
10338
+ else if (r.removed.length === 0)
10339
+ process.stdout.write(`no service unit installed
10340
+ `);
10341
+ else
10342
+ process.stdout.write(`removed: ${r.removed.join(", ")}
10343
+ `);
10344
+ return 0;
8102
10345
  }
8103
- var init_register = __esm(() => {
8104
- init_login();
8105
- });
8106
-
8107
- // src/commands/logout.ts
8108
- var exports_logout = {};
8109
- __export(exports_logout, {
8110
- logout: () => logout2
8111
- });
8112
- async function logout2() {
10346
+ async function runAcceptHost(opts) {
10347
+ const { acceptCurrentHost: acceptCurrentHost2 } = await Promise.resolve().then(() => (init_identity(), exports_identity));
10348
+ const fp = acceptCurrentHost2();
10349
+ if (opts.json)
10350
+ process.stdout.write(JSON.stringify({ ok: true, fingerprint_prefix: fp.fingerprint.slice(0, 16) }) + `
10351
+ `);
10352
+ else
10353
+ process.stdout.write(`host fingerprint accepted: ${fp.fingerprint.slice(0, 16)}…
10354
+ `);
10355
+ return 0;
10356
+ }
10357
+ async function runStatus(opts) {
10358
+ const pid = readRunningPid();
10359
+ if (!pid) {
10360
+ if (opts.json)
10361
+ process.stdout.write(JSON.stringify({ running: false }) + `
10362
+ `);
10363
+ else
10364
+ process.stdout.write(`daemon: not running
10365
+ `);
10366
+ return 1;
10367
+ }
8113
10368
  try {
8114
- const { revoked } = await logout();
8115
- if (revoked) {
8116
- console.log(` ${green(icons.check)} Revoked session on claudemesh.com`);
10369
+ const res = await ipc({ path: "/v1/health" });
10370
+ if (opts.json) {
10371
+ process.stdout.write(JSON.stringify({ running: true, pid, health: res.body }) + `
10372
+ `);
8117
10373
  } else {
8118
- console.log(` ${yellow(icons.warn)} Could not revoke session on claudemesh.com.`);
8119
- console.log(` Revoke manually at https://claudemesh.com/dashboard/settings/sessions`);
10374
+ process.stdout.write(`daemon: running (pid ${pid})
10375
+ `);
10376
+ process.stdout.write(`socket: ${DAEMON_PATHS.SOCK_FILE}
10377
+ `);
8120
10378
  }
8121
- console.log(` ${green(icons.check)} Removed local credentials.`);
8122
- return EXIT.SUCCESS;
10379
+ return 0;
8123
10380
  } catch (err) {
8124
- console.error(` ${icons.cross} Logout failed: ${err instanceof Error ? err.message : err}`);
8125
- return EXIT.AUTH_FAILED;
10381
+ if (opts.json)
10382
+ process.stdout.write(JSON.stringify({ running: true, pid, ipc_error: String(err) }) + `
10383
+ `);
10384
+ else
10385
+ process.stdout.write(`daemon: pid ${pid} alive but IPC unreachable (${String(err)})
10386
+ `);
10387
+ return 1;
8126
10388
  }
8127
10389
  }
8128
- var init_logout = __esm(() => {
8129
- init_facade6();
8130
- init_styles();
8131
- init_exit_codes();
8132
- });
8133
-
8134
- // src/commands/whoami.ts
8135
- var exports_whoami = {};
8136
- __export(exports_whoami, {
8137
- whoami: () => whoami
8138
- });
8139
- async function whoami(opts) {
8140
- const result = await whoAmI();
8141
- if (opts.json) {
8142
- console.log(JSON.stringify({ schema_version: "1.0", ...result }, null, 2));
8143
- return result.signed_in || result.local ? EXIT.SUCCESS : EXIT.AUTH_FAILED;
10390
+ async function runVersion(opts) {
10391
+ try {
10392
+ const res = await ipc({ path: "/v1/version" });
10393
+ if (opts.json)
10394
+ process.stdout.write(JSON.stringify(res.body) + `
10395
+ `);
10396
+ else {
10397
+ const v = res.body;
10398
+ process.stdout.write(`daemon ${v.daemon_version ?? "unknown"} (ipc ${v.ipc_api ?? "?"}, schema ${v.schema_version ?? "?"})
10399
+ `);
10400
+ }
10401
+ return 0;
10402
+ } catch (err) {
10403
+ if (err instanceof IpcError) {
10404
+ process.stderr.write(`${err.message}
10405
+ `);
10406
+ return err.status === 401 ? 3 : 1;
10407
+ }
10408
+ process.stderr.write(`daemon unreachable: ${String(err)}
10409
+ `);
10410
+ return 1;
8144
10411
  }
8145
- if (!result.signed_in && !result.local) {
8146
- render.err("Not signed in", "Run `claudemesh login` to sign in or `claudemesh <invite>` to join.");
8147
- return EXIT.AUTH_FAILED;
10412
+ }
10413
+ async function runStop(opts) {
10414
+ const pid = readRunningPid();
10415
+ if (!pid) {
10416
+ if (opts.json)
10417
+ process.stdout.write(JSON.stringify({ stopped: false, reason: "not_running" }) + `
10418
+ `);
10419
+ else
10420
+ process.stdout.write(`daemon: not running
10421
+ `);
10422
+ return 0;
8148
10423
  }
8149
- render.section("whoami");
8150
- if (result.signed_in) {
8151
- render.kv([
8152
- ["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
8153
- ["token", `${result.token_source} ${dim("(~/.claudemesh/auth.json)")}`],
8154
- ...result.meshes ? [["meshes", `${result.meshes.owned} owned · ${result.meshes.guest} guest`]] : []
8155
- ]);
8156
- } else {
8157
- render.kv([
8158
- ["web", dim("not signed in · run `claudemesh login` for account features")]
8159
- ]);
10424
+ try {
10425
+ process.kill(pid, "SIGTERM");
10426
+ } catch (err) {
10427
+ process.stderr.write(`failed to signal pid ${pid}: ${String(err)}
10428
+ `);
10429
+ return 1;
8160
10430
  }
8161
- if (result.local) {
8162
- render.blank();
8163
- render.kv([
8164
- ["local", `${result.local.meshes.length} mesh${result.local.meshes.length === 1 ? "" : "es"} · ${dim(result.local.config_path)}`]
8165
- ]);
8166
- for (const m of result.local.meshes) {
8167
- console.log(` ${clay("●")} ${bold(m.slug)} ${dim(`member ${m.member_id.slice(0, 8)}… pk ${m.pubkey_prefix}…`)}`);
10431
+ for (let i = 0;i < 50; i++) {
10432
+ await new Promise((r) => setTimeout(r, 100));
10433
+ if (!readRunningPid()) {
10434
+ if (opts.json)
10435
+ process.stdout.write(JSON.stringify({ stopped: true, pid }) + `
10436
+ `);
10437
+ else
10438
+ process.stdout.write(`daemon: stopped (was pid ${pid})
10439
+ `);
10440
+ return 0;
8168
10441
  }
8169
10442
  }
8170
- render.blank();
8171
- return EXIT.SUCCESS;
10443
+ if (opts.json)
10444
+ process.stdout.write(JSON.stringify({ stopped: false, pid, reason: "shutdown_timeout" }) + `
10445
+ `);
10446
+ else
10447
+ process.stdout.write(`daemon: signaled but did not exit within 5s (pid ${pid})
10448
+ `);
10449
+ return 1;
8172
10450
  }
8173
- var init_whoami = __esm(() => {
8174
- init_facade6();
8175
- init_render();
8176
- init_styles();
8177
- init_exit_codes();
10451
+ var init_daemon = __esm(() => {
10452
+ init_run();
10453
+ init_client4();
10454
+ init_lock();
10455
+ init_paths2();
8178
10456
  });
8179
10457
 
8180
10458
  // src/commands/install.ts
@@ -8184,21 +10462,21 @@ __export(exports_install, {
8184
10462
  runInstall: () => runInstall
8185
10463
  });
8186
10464
  import {
8187
- chmodSync as chmodSync3,
10465
+ chmodSync as chmodSync4,
8188
10466
  copyFileSync,
8189
- existsSync as existsSync7,
8190
- mkdirSync as mkdirSync4,
8191
- readFileSync as readFileSync5,
8192
- writeFileSync as writeFileSync6
10467
+ existsSync as existsSync13,
10468
+ mkdirSync as mkdirSync8,
10469
+ readFileSync as readFileSync10,
10470
+ writeFileSync as writeFileSync10
8193
10471
  } from "node:fs";
8194
- import { homedir as homedir6, platform as platform5 } from "node:os";
8195
- import { dirname as dirname3, join as join6, resolve } from "node:path";
10472
+ import { homedir as homedir7, platform as platform5 } from "node:os";
10473
+ import { dirname as dirname6, join as join9, resolve } from "node:path";
8196
10474
  import { fileURLToPath } from "node:url";
8197
10475
  import { spawnSync as spawnSync3 } from "node:child_process";
8198
10476
  function readClaudeConfig() {
8199
- if (!existsSync7(CLAUDE_CONFIG))
10477
+ if (!existsSync13(CLAUDE_CONFIG))
8200
10478
  return {};
8201
- const text = readFileSync5(CLAUDE_CONFIG, "utf-8").trim();
10479
+ const text = readFileSync10(CLAUDE_CONFIG, "utf-8").trim();
8202
10480
  if (!text)
8203
10481
  return {};
8204
10482
  try {
@@ -8208,12 +10486,12 @@ function readClaudeConfig() {
8208
10486
  }
8209
10487
  }
8210
10488
  function backupClaudeConfig() {
8211
- if (!existsSync7(CLAUDE_CONFIG))
10489
+ if (!existsSync13(CLAUDE_CONFIG))
8212
10490
  return;
8213
- const backupDir = join6(dirname3(CLAUDE_CONFIG), ".claude", "backups");
8214
- mkdirSync4(backupDir, { recursive: true });
10491
+ const backupDir = join9(dirname6(CLAUDE_CONFIG), ".claude", "backups");
10492
+ mkdirSync8(backupDir, { recursive: true });
8215
10493
  const ts = Date.now();
8216
- const dest = join6(backupDir, `.claude.json.pre-claudemesh.${ts}`);
10494
+ const dest = join9(backupDir, `.claude.json.pre-claudemesh.${ts}`);
8217
10495
  copyFileSync(CLAUDE_CONFIG, dest);
8218
10496
  }
8219
10497
  function patchMcpServer(entry) {
@@ -8237,7 +10515,7 @@ function patchMcpServer(entry) {
8237
10515
  return action;
8238
10516
  }
8239
10517
  function removeMcpServer() {
8240
- if (!existsSync7(CLAUDE_CONFIG))
10518
+ if (!existsSync13(CLAUDE_CONFIG))
8241
10519
  return false;
8242
10520
  backupClaudeConfig();
8243
10521
  const cfg = readClaudeConfig();
@@ -8250,11 +10528,11 @@ function removeMcpServer() {
8250
10528
  return true;
8251
10529
  }
8252
10530
  function flushClaudeConfig(obj) {
8253
- mkdirSync4(dirname3(CLAUDE_CONFIG), { recursive: true });
8254
- writeFileSync6(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
10531
+ mkdirSync8(dirname6(CLAUDE_CONFIG), { recursive: true });
10532
+ writeFileSync10(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
8255
10533
  `, "utf-8");
8256
10534
  try {
8257
- chmodSync3(CLAUDE_CONFIG, 384);
10535
+ chmodSync4(CLAUDE_CONFIG, 384);
8258
10536
  } catch {}
8259
10537
  }
8260
10538
  function bunAvailable() {
@@ -8268,13 +10546,13 @@ function resolveEntry() {
8268
10546
  const here = fileURLToPath(import.meta.url);
8269
10547
  if (isBundledFile(here))
8270
10548
  return here;
8271
- return resolve(dirname3(here), "..", "index.ts");
10549
+ return resolve(dirname6(here), "..", "index.ts");
8272
10550
  }
8273
10551
  function resolveBundledSkillsDir() {
8274
10552
  const here = fileURLToPath(import.meta.url);
8275
- const pkgRoot = resolve(dirname3(here), "..", "..");
8276
- const skillsDir = join6(pkgRoot, "skills");
8277
- if (existsSync7(skillsDir))
10553
+ const pkgRoot = resolve(dirname6(here), "..", "..");
10554
+ const skillsDir = join9(pkgRoot, "skills");
10555
+ if (existsSync13(skillsDir))
8278
10556
  return skillsDir;
8279
10557
  return null;
8280
10558
  }
@@ -8287,13 +10565,13 @@ function installSkills() {
8287
10565
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
8288
10566
  if (!entry.isDirectory())
8289
10567
  continue;
8290
- const srcDir = join6(src, entry.name);
8291
- const dstDir = join6(CLAUDE_SKILLS_ROOT, entry.name);
8292
- mkdirSync4(dstDir, { recursive: true });
10568
+ const srcDir = join9(src, entry.name);
10569
+ const dstDir = join9(CLAUDE_SKILLS_ROOT, entry.name);
10570
+ mkdirSync8(dstDir, { recursive: true });
8293
10571
  for (const file of fs.readdirSync(srcDir, { withFileTypes: true })) {
8294
10572
  if (!file.isFile())
8295
10573
  continue;
8296
- copyFileSync(join6(srcDir, file.name), join6(dstDir, file.name));
10574
+ copyFileSync(join9(srcDir, file.name), join9(dstDir, file.name));
8297
10575
  }
8298
10576
  installed.push(entry.name);
8299
10577
  }
@@ -8308,8 +10586,8 @@ function uninstallSkills() {
8308
10586
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
8309
10587
  if (!entry.isDirectory())
8310
10588
  continue;
8311
- const dstDir = join6(CLAUDE_SKILLS_ROOT, entry.name);
8312
- if (existsSync7(dstDir)) {
10589
+ const dstDir = join9(CLAUDE_SKILLS_ROOT, entry.name);
10590
+ if (existsSync13(dstDir)) {
8313
10591
  try {
8314
10592
  fs.rmSync(dstDir, { recursive: true, force: true });
8315
10593
  removed.push(entry.name);
@@ -8334,9 +10612,9 @@ function entriesEqual(a, b) {
8334
10612
  return a.command === b.command && JSON.stringify(a.args ?? []) === JSON.stringify(b.args ?? []);
8335
10613
  }
8336
10614
  function readClaudeSettings() {
8337
- if (!existsSync7(CLAUDE_SETTINGS))
10615
+ if (!existsSync13(CLAUDE_SETTINGS))
8338
10616
  return {};
8339
- const text = readFileSync5(CLAUDE_SETTINGS, "utf-8").trim();
10617
+ const text = readFileSync10(CLAUDE_SETTINGS, "utf-8").trim();
8340
10618
  if (!text)
8341
10619
  return {};
8342
10620
  try {
@@ -8346,8 +10624,8 @@ function readClaudeSettings() {
8346
10624
  }
8347
10625
  }
8348
10626
  function writeClaudeSettings(obj) {
8349
- mkdirSync4(dirname3(CLAUDE_SETTINGS), { recursive: true });
8350
- writeFileSync6(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
10627
+ mkdirSync8(dirname6(CLAUDE_SETTINGS), { recursive: true });
10628
+ writeFileSync10(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
8351
10629
  `, "utf-8");
8352
10630
  }
8353
10631
  function installAllowedTools() {
@@ -8361,7 +10639,7 @@ function installAllowedTools() {
8361
10639
  return { added: toAdd, unchanged: CLAUDEMESH_TOOLS.length - toAdd.length };
8362
10640
  }
8363
10641
  function uninstallAllowedTools() {
8364
- if (!existsSync7(CLAUDE_SETTINGS))
10642
+ if (!existsSync13(CLAUDE_SETTINGS))
8365
10643
  return 0;
8366
10644
  const settings = readClaudeSettings();
8367
10645
  const existing = settings.allowedTools ?? [];
@@ -8396,7 +10674,7 @@ function installHooks() {
8396
10674
  return { added, unchanged };
8397
10675
  }
8398
10676
  function uninstallHooks() {
8399
- if (!existsSync7(CLAUDE_SETTINGS))
10677
+ if (!existsSync13(CLAUDE_SETTINGS))
8400
10678
  return 0;
8401
10679
  const settings = readClaudeSettings();
8402
10680
  const hooks = settings.hooks;
@@ -8445,7 +10723,7 @@ function runInstall(args = []) {
8445
10723
  render.err("`bun` is not on PATH.", "Install Bun first: https://bun.com");
8446
10724
  process.exit(1);
8447
10725
  }
8448
- if (!existsSync7(entry)) {
10726
+ if (!existsSync13(entry)) {
8449
10727
  render.err(`MCP entry not found at ${entry}`);
8450
10728
  process.exit(1);
8451
10729
  }
@@ -8496,7 +10774,7 @@ function runInstall(args = []) {
8496
10774
  const installed = installSkills();
8497
10775
  if (installed.length > 0) {
8498
10776
  render.ok(`Claude skill${installed.length === 1 ? "" : "s"} installed`, installed.join(", "));
8499
- render.info(dim(` ${join6(CLAUDE_SKILLS_ROOT, installed[0])}/SKILL.md`));
10777
+ render.info(dim(` ${join9(CLAUDE_SKILLS_ROOT, installed[0])}/SKILL.md`));
8500
10778
  }
8501
10779
  } catch (e) {
8502
10780
  render.warn(`skill install failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -8584,9 +10862,9 @@ var init_install = __esm(() => {
8584
10862
  init_facade();
8585
10863
  init_render();
8586
10864
  init_styles();
8587
- CLAUDE_CONFIG = join6(homedir6(), ".claude.json");
8588
- CLAUDE_SETTINGS = join6(homedir6(), ".claude", "settings.json");
8589
- CLAUDE_SKILLS_ROOT = join6(homedir6(), ".claude", "skills");
10865
+ CLAUDE_CONFIG = join9(homedir7(), ".claude.json");
10866
+ CLAUDE_SETTINGS = join9(homedir7(), ".claude", "settings.json");
10867
+ CLAUDE_SKILLS_ROOT = join9(homedir7(), ".claude", "skills");
8590
10868
  CLAUDEMESH_TOOLS = [
8591
10869
  "mcp__claudemesh__cancel_scheduled",
8592
10870
  "mcp__claudemesh__check_messages",
@@ -8641,35 +10919,35 @@ var exports_uninstall = {};
8641
10919
  __export(exports_uninstall, {
8642
10920
  uninstall: () => uninstall
8643
10921
  });
8644
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync7, existsSync as existsSync8, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
8645
- import { join as join7, dirname as dirname4 } from "node:path";
8646
- import { homedir as homedir7 } from "node:os";
10922
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, existsSync as existsSync14, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
10923
+ import { join as join10, dirname as dirname7 } from "node:path";
10924
+ import { homedir as homedir8 } from "node:os";
8647
10925
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8648
10926
  function bundledSkillsDir() {
8649
10927
  const here = fileURLToPath2(import.meta.url);
8650
- const pkgRoot = join7(dirname4(here), "..", "..");
8651
- const skillsDir = join7(pkgRoot, "skills");
8652
- return existsSync8(skillsDir) ? skillsDir : null;
10928
+ const pkgRoot = join10(dirname7(here), "..", "..");
10929
+ const skillsDir = join10(pkgRoot, "skills");
10930
+ return existsSync14(skillsDir) ? skillsDir : null;
8653
10931
  }
8654
10932
  async function uninstall() {
8655
10933
  let removed = 0;
8656
- if (existsSync8(PATHS.CLAUDE_JSON)) {
10934
+ if (existsSync14(PATHS.CLAUDE_JSON)) {
8657
10935
  try {
8658
- const raw = readFileSync6(PATHS.CLAUDE_JSON, "utf-8");
10936
+ const raw = readFileSync11(PATHS.CLAUDE_JSON, "utf-8");
8659
10937
  const config = JSON.parse(raw);
8660
10938
  const servers = config.mcpServers;
8661
10939
  if (servers && "claudemesh" in servers) {
8662
10940
  delete servers.claudemesh;
8663
- writeFileSync7(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
10941
+ writeFileSync11(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
8664
10942
  `, "utf-8");
8665
10943
  render.ok("removed MCP server", dim("~/.claude.json"));
8666
10944
  removed++;
8667
10945
  }
8668
10946
  } catch {}
8669
10947
  }
8670
- if (existsSync8(PATHS.CLAUDE_SETTINGS)) {
10948
+ if (existsSync14(PATHS.CLAUDE_SETTINGS)) {
8671
10949
  try {
8672
- const raw = readFileSync6(PATHS.CLAUDE_SETTINGS, "utf-8");
10950
+ const raw = readFileSync11(PATHS.CLAUDE_SETTINGS, "utf-8");
8673
10951
  const config = JSON.parse(raw);
8674
10952
  const hooks = config.hooks;
8675
10953
  if (hooks) {
@@ -8690,7 +10968,7 @@ async function uninstall() {
8690
10968
  }
8691
10969
  }
8692
10970
  if (removedHooks > 0) {
8693
- writeFileSync7(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
10971
+ writeFileSync11(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
8694
10972
  `, "utf-8");
8695
10973
  render.ok(`removed ${removedHooks} claudemesh hook${removedHooks === 1 ? "" : "s"}`, dim("settings.json"));
8696
10974
  removed++;
@@ -8705,8 +10983,8 @@ async function uninstall() {
8705
10983
  for (const entry of readdirSync2(src, { withFileTypes: true })) {
8706
10984
  if (!entry.isDirectory())
8707
10985
  continue;
8708
- const dst = join7(CLAUDE_SKILLS_ROOT2, entry.name);
8709
- if (existsSync8(dst)) {
10986
+ const dst = join10(CLAUDE_SKILLS_ROOT2, entry.name);
10987
+ if (existsSync14(dst)) {
8710
10988
  try {
8711
10989
  rmSync2(dst, { recursive: true, force: true });
8712
10990
  removedSkills.push(entry.name);
@@ -8730,7 +11008,7 @@ var init_uninstall = __esm(() => {
8730
11008
  init_render();
8731
11009
  init_styles();
8732
11010
  init_exit_codes();
8733
- CLAUDE_SKILLS_ROOT2 = join7(homedir7(), ".claude", "skills");
11011
+ CLAUDE_SKILLS_ROOT2 = join10(homedir8(), ".claude", "skills");
8734
11012
  });
8735
11013
 
8736
11014
  // src/commands/doctor.ts
@@ -8738,9 +11016,9 @@ var exports_doctor = {};
8738
11016
  __export(exports_doctor, {
8739
11017
  runDoctor: () => runDoctor
8740
11018
  });
8741
- import { existsSync as existsSync9, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
8742
- import { homedir as homedir8, platform as platform6 } from "node:os";
8743
- import { join as join8 } from "node:path";
11019
+ import { existsSync as existsSync15, readFileSync as readFileSync12, statSync as statSync2 } from "node:fs";
11020
+ import { homedir as homedir9, platform as platform6 } from "node:os";
11021
+ import { join as join11 } from "node:path";
8744
11022
  import { spawnSync as spawnSync4 } from "node:child_process";
8745
11023
  function checkNode() {
8746
11024
  const major = Number(process.versions.node.split(".")[0]);
@@ -8764,8 +11042,8 @@ function checkClaudeOnPath() {
8764
11042
  };
8765
11043
  }
8766
11044
  function checkMcpRegistered() {
8767
- const claudeConfig = join8(homedir8(), ".claude.json");
8768
- if (!existsSync9(claudeConfig)) {
11045
+ const claudeConfig = join11(homedir9(), ".claude.json");
11046
+ if (!existsSync15(claudeConfig)) {
8769
11047
  return {
8770
11048
  name: "claudemesh MCP registered in ~/.claude.json",
8771
11049
  pass: false,
@@ -8773,7 +11051,7 @@ function checkMcpRegistered() {
8773
11051
  };
8774
11052
  }
8775
11053
  try {
8776
- const cfg = JSON.parse(readFileSync7(claudeConfig, "utf-8"));
11054
+ const cfg = JSON.parse(readFileSync12(claudeConfig, "utf-8"));
8777
11055
  const registered = Boolean(cfg.mcpServers?.["claudemesh"]);
8778
11056
  return {
8779
11057
  name: "claudemesh MCP registered in ~/.claude.json",
@@ -8790,8 +11068,8 @@ function checkMcpRegistered() {
8790
11068
  }
8791
11069
  }
8792
11070
  function checkHooksRegistered() {
8793
- const settings = join8(homedir8(), ".claude", "settings.json");
8794
- if (!existsSync9(settings)) {
11071
+ const settings = join11(homedir9(), ".claude", "settings.json");
11072
+ if (!existsSync15(settings)) {
8795
11073
  return {
8796
11074
  name: "Status hooks registered in ~/.claude/settings.json",
8797
11075
  pass: false,
@@ -8799,7 +11077,7 @@ function checkHooksRegistered() {
8799
11077
  };
8800
11078
  }
8801
11079
  try {
8802
- const raw = readFileSync7(settings, "utf-8");
11080
+ const raw = readFileSync12(settings, "utf-8");
8803
11081
  const has = raw.includes("claudemesh hook ");
8804
11082
  return {
8805
11083
  name: "Status hooks registered in ~/.claude/settings.json",
@@ -8815,8 +11093,8 @@ function checkHooksRegistered() {
8815
11093
  }
8816
11094
  }
8817
11095
  function checkConfigFile() {
8818
- const path = getConfigPath();
8819
- if (!existsSync9(path)) {
11096
+ const path2 = getConfigPath();
11097
+ if (!existsSync15(path2)) {
8820
11098
  return {
8821
11099
  name: "~/.claudemesh/config.json exists and parses",
8822
11100
  pass: true,
@@ -8825,14 +11103,14 @@ function checkConfigFile() {
8825
11103
  }
8826
11104
  try {
8827
11105
  readConfig();
8828
- const st = statSync2(path);
11106
+ const st = statSync2(path2);
8829
11107
  const mode = (st.mode & 511).toString(8);
8830
11108
  const secure = platform6() === "win32" || mode === "600";
8831
11109
  return {
8832
11110
  name: "~/.claudemesh/config.json parses + chmod 0600",
8833
11111
  pass: secure,
8834
11112
  detail: platform6() === "win32" ? "chmod skipped on Windows" : `0${mode}`,
8835
- fix: secure ? undefined : `chmod 600 ${path}`
11113
+ fix: secure ? undefined : `chmod 600 ${path2}`
8836
11114
  };
8837
11115
  } catch (e) {
8838
11116
  return {
@@ -8888,8 +11166,8 @@ async function checkBrokerWs() {
8888
11166
  const wsUrl = URLS.BROKER;
8889
11167
  const start = Date.now();
8890
11168
  try {
8891
- const WebSocket2 = (await import("ws")).default;
8892
- const ws = new WebSocket2(wsUrl);
11169
+ const WebSocket3 = (await import("ws")).default;
11170
+ const ws = new WebSocket3(wsUrl);
8893
11171
  const result = await new Promise((resolve2) => {
8894
11172
  const timer = setTimeout(() => {
8895
11173
  try {
@@ -8997,14 +11275,14 @@ var init_doctor = __esm(() => {
8997
11275
  // src/commands/status.ts
8998
11276
  var exports_status = {};
8999
11277
  __export(exports_status, {
9000
- runStatus: () => runStatus
11278
+ runStatus: () => runStatus2
9001
11279
  });
9002
- import { statSync as statSync3, existsSync as existsSync10 } from "node:fs";
9003
- import WebSocket2 from "ws";
11280
+ import { statSync as statSync3, existsSync as existsSync16 } from "node:fs";
11281
+ import WebSocket3 from "ws";
9004
11282
  async function probeBroker(url, timeoutMs = 4000) {
9005
11283
  return new Promise((resolve2) => {
9006
11284
  const started = Date.now();
9007
- const ws = new WebSocket2(url);
11285
+ const ws = new WebSocket3(url);
9008
11286
  const timer = setTimeout(() => {
9009
11287
  try {
9010
11288
  ws.terminate();
@@ -9025,11 +11303,11 @@ async function probeBroker(url, timeoutMs = 4000) {
9025
11303
  });
9026
11304
  });
9027
11305
  }
9028
- async function runStatus() {
11306
+ async function runStatus2() {
9029
11307
  render.section(`status (v${VERSION})`);
9030
11308
  const configPath = getConfigPath();
9031
11309
  let configPermsNote = "missing";
9032
- if (existsSync10(configPath)) {
11310
+ if (existsSync16(configPath)) {
9033
11311
  const mode = (statSync3(configPath).mode & 511).toString(8).padStart(4, "0");
9034
11312
  configPermsNote = mode === "0600" ? `${mode}` : `${mode} — expected 0600`;
9035
11313
  }
@@ -9175,13 +11453,13 @@ var init_check_claude_binary = __esm(() => {
9175
11453
  });
9176
11454
 
9177
11455
  // src/services/health/check-mcp-registered.ts
9178
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
11456
+ import { existsSync as existsSync17, readFileSync as readFileSync13 } from "node:fs";
9179
11457
  function checkMcpRegistered2() {
9180
11458
  try {
9181
- if (!existsSync11(PATHS.CLAUDE_JSON)) {
11459
+ if (!existsSync17(PATHS.CLAUDE_JSON)) {
9182
11460
  return { name: "mcp-registered", ok: false, message: "~/.claude.json not found" };
9183
11461
  }
9184
- const raw = readFileSync8(PATHS.CLAUDE_JSON, "utf-8");
11462
+ const raw = readFileSync13(PATHS.CLAUDE_JSON, "utf-8");
9185
11463
  const config = JSON.parse(raw);
9186
11464
  if (config.mcpServers && "claudemesh" in config.mcpServers) {
9187
11465
  return { name: "mcp-registered", ok: true, message: "MCP server registered" };
@@ -9196,13 +11474,13 @@ var init_check_mcp_registered = __esm(() => {
9196
11474
  });
9197
11475
 
9198
11476
  // src/services/health/check-hooks-registered.ts
9199
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
11477
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
9200
11478
  function checkHooksRegistered2() {
9201
11479
  try {
9202
- if (!existsSync12(PATHS.CLAUDE_SETTINGS)) {
11480
+ if (!existsSync18(PATHS.CLAUDE_SETTINGS)) {
9203
11481
  return { name: "hooks-registered", ok: false, message: "~/.claude/settings.json not found" };
9204
11482
  }
9205
- const raw = readFileSync9(PATHS.CLAUDE_SETTINGS, "utf-8");
11483
+ const raw = readFileSync14(PATHS.CLAUDE_SETTINGS, "utf-8");
9206
11484
  const config = JSON.parse(raw);
9207
11485
  if (config.hooks) {
9208
11486
  return { name: "hooks-registered", ok: true, message: "Hooks configured" };
@@ -9217,10 +11495,10 @@ var init_check_hooks_registered = __esm(() => {
9217
11495
  });
9218
11496
 
9219
11497
  // src/services/health/check-config-perms.ts
9220
- import { existsSync as existsSync13, statSync as statSync4 } from "node:fs";
11498
+ import { existsSync as existsSync19, statSync as statSync4 } from "node:fs";
9221
11499
  function checkConfigPerms() {
9222
11500
  const configFile = PATHS.CONFIG_FILE;
9223
- if (!existsSync13(configFile)) {
11501
+ if (!existsSync19(configFile)) {
9224
11502
  return { name: "config-perms", ok: true, message: "No config file yet (first run)" };
9225
11503
  }
9226
11504
  try {
@@ -9238,13 +11516,13 @@ var init_check_config_perms = __esm(() => {
9238
11516
  });
9239
11517
 
9240
11518
  // src/services/health/check-keypairs-valid.ts
9241
- import { existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
11519
+ import { existsSync as existsSync20, readFileSync as readFileSync15 } from "node:fs";
9242
11520
  function checkKeypairsValid() {
9243
- if (!existsSync14(PATHS.CONFIG_FILE)) {
11521
+ if (!existsSync20(PATHS.CONFIG_FILE)) {
9244
11522
  return { name: "keypairs-valid", ok: true, message: "No config (first run)" };
9245
11523
  }
9246
11524
  try {
9247
- const raw = readFileSync10(PATHS.CONFIG_FILE, "utf-8");
11525
+ const raw = readFileSync15(PATHS.CONFIG_FILE, "utf-8");
9248
11526
  const config = JSON.parse(raw);
9249
11527
  const meshes = config.meshes ?? [];
9250
11528
  if (meshes.length === 0) {
@@ -9645,12 +11923,12 @@ var exports_verify = {};
9645
11923
  __export(exports_verify, {
9646
11924
  runVerify: () => runVerify
9647
11925
  });
9648
- import { createHash } from "node:crypto";
11926
+ import { createHash as createHash3 } from "node:crypto";
9649
11927
  function safetyNumber(myPubkey, peerPubkey) {
9650
11928
  const a = Buffer.from(myPubkey, "hex");
9651
11929
  const b = Buffer.from(peerPubkey, "hex");
9652
11930
  const [lo, hi] = Buffer.compare(a, b) < 0 ? [a, b] : [b, a];
9653
- const hash = createHash("sha256").update(lo).update(hi).digest();
11931
+ const hash = createHash3("sha256").update(lo).update(hi).digest();
9654
11932
  const bits = [];
9655
11933
  for (let i = 0;i < 15; i++) {
9656
11934
  for (let b2 = 7;b2 >= 0; b2--) {
@@ -9724,19 +12002,19 @@ var exports_url_handler = {};
9724
12002
  __export(exports_url_handler, {
9725
12003
  runUrlHandler: () => runUrlHandler
9726
12004
  });
9727
- import { platform as platform7, homedir as homedir9 } from "node:os";
9728
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8, rmSync as rmSync3, chmodSync as chmodSync4 } from "node:fs";
9729
- import { join as join9 } from "node:path";
12005
+ import { platform as platform7, homedir as homedir10 } from "node:os";
12006
+ import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync12, rmSync as rmSync3, chmodSync as chmodSync5 } from "node:fs";
12007
+ import { join as join12 } from "node:path";
9730
12008
  import { spawnSync as spawnSync5 } from "node:child_process";
9731
12009
  function resolveClaudemeshBin() {
9732
12010
  return process.argv[1] ?? "claudemesh";
9733
12011
  }
9734
- function installDarwin() {
12012
+ function installDarwin2() {
9735
12013
  const binPath = resolveClaudemeshBin();
9736
- const appDir = join9(homedir9(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
9737
- const contents = join9(appDir, "Contents");
9738
- const macOS = join9(contents, "MacOS");
9739
- mkdirSync5(macOS, { recursive: true });
12014
+ const appDir = join12(homedir10(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
12015
+ const contents = join12(appDir, "Contents");
12016
+ const macOS = join12(contents, "MacOS");
12017
+ mkdirSync9(macOS, { recursive: true });
9740
12018
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
9741
12019
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
9742
12020
  <plist version="1.0">
@@ -9759,7 +12037,7 @@ function installDarwin() {
9759
12037
  </array>
9760
12038
  </dict>
9761
12039
  </plist>`;
9762
- writeFileSync8(join9(contents, "Info.plist"), plist);
12040
+ writeFileSync12(join12(contents, "Info.plist"), plist);
9763
12041
  const shim = `#!/bin/sh
9764
12042
  URL="$1"
9765
12043
  CODE=\${URL#claudemesh://}
@@ -9773,9 +12051,9 @@ tell application "Terminal"
9773
12051
  end tell
9774
12052
  EOF
9775
12053
  `;
9776
- const shimPath = join9(macOS, "open-url");
9777
- writeFileSync8(shimPath, shim);
9778
- chmodSync4(shimPath, 493);
12054
+ const shimPath = join12(macOS, "open-url");
12055
+ writeFileSync12(shimPath, shim);
12056
+ chmodSync5(shimPath, 493);
9779
12057
  const lsreg = spawnSync5("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", ["-f", appDir], { encoding: "utf-8" });
9780
12058
  if (lsreg.status !== 0) {
9781
12059
  render.warn("lsregister returned non-zero", "scheme may not activate until Finder rescans.");
@@ -9783,10 +12061,10 @@ EOF
9783
12061
  render.ok("registered claudemesh:// scheme on macOS", dim(appDir));
9784
12062
  return EXIT.SUCCESS;
9785
12063
  }
9786
- function installLinux() {
12064
+ function installLinux2() {
9787
12065
  const binPath = resolveClaudemeshBin();
9788
- const appsDir = join9(homedir9(), ".local", "share", "applications");
9789
- mkdirSync5(appsDir, { recursive: true });
12066
+ const appsDir = join12(homedir10(), ".local", "share", "applications");
12067
+ mkdirSync9(appsDir, { recursive: true });
9790
12068
  const desktop = `[Desktop Entry]
9791
12069
  Type=Application
9792
12070
  Name=Claudemesh
@@ -9797,8 +12075,8 @@ Terminal=true
9797
12075
  MimeType=x-scheme-handler/claudemesh;
9798
12076
  NoDisplay=true
9799
12077
  `;
9800
- const desktopPath = join9(appsDir, "claudemesh.desktop");
9801
- writeFileSync8(desktopPath, desktop);
12078
+ const desktopPath = join12(appsDir, "claudemesh.desktop");
12079
+ writeFileSync12(desktopPath, desktop);
9802
12080
  const xdg1 = spawnSync5("xdg-mime", ["default", "claudemesh.desktop", "x-scheme-handler/claudemesh"], { encoding: "utf-8" });
9803
12081
  if (xdg1.status !== 0) {
9804
12082
  render.warn("xdg-mime not available — skipped mime default registration");
@@ -9820,8 +12098,8 @@ function installWindows() {
9820
12098
  `[HKEY_CURRENT_USER\\Software\\Classes\\claudemesh\\shell\\open\\command]`,
9821
12099
  `@="\\"${binPath.replace(/\\/g, "\\\\")}\\" \\"%1\\""`
9822
12100
  ];
9823
- const regPath = join9(homedir9(), "claudemesh-handler.reg");
9824
- writeFileSync8(regPath, lines.join(`\r
12101
+ const regPath = join12(homedir10(), "claudemesh-handler.reg");
12102
+ writeFileSync12(regPath, lines.join(`\r
9825
12103
  `));
9826
12104
  const res = spawnSync5("reg.exe", ["import", regPath], { encoding: "utf-8" });
9827
12105
  if (res.status !== 0) {
@@ -9832,15 +12110,15 @@ function installWindows() {
9832
12110
  return EXIT.SUCCESS;
9833
12111
  }
9834
12112
  function uninstallDarwin() {
9835
- const appDir = join9(homedir9(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
9836
- if (existsSync15(appDir))
12113
+ const appDir = join12(homedir10(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
12114
+ if (existsSync21(appDir))
9837
12115
  rmSync3(appDir, { recursive: true, force: true });
9838
12116
  render.ok("removed claudemesh:// handler on macOS");
9839
12117
  return EXIT.SUCCESS;
9840
12118
  }
9841
12119
  function uninstallLinux() {
9842
- const desktopPath = join9(homedir9(), ".local", "share", "applications", "claudemesh.desktop");
9843
- if (existsSync15(desktopPath))
12120
+ const desktopPath = join12(homedir10(), ".local", "share", "applications", "claudemesh.desktop");
12121
+ if (existsSync21(desktopPath))
9844
12122
  rmSync3(desktopPath, { force: true });
9845
12123
  render.ok("removed claudemesh:// handler on Linux");
9846
12124
  return EXIT.SUCCESS;
@@ -9855,9 +12133,9 @@ async function runUrlHandler(action) {
9855
12133
  const p = platform7();
9856
12134
  if (act === "install") {
9857
12135
  if (p === "darwin")
9858
- return installDarwin();
12136
+ return installDarwin2();
9859
12137
  if (p === "linux")
9860
- return installLinux();
12138
+ return installLinux2();
9861
12139
  if (p === "win32")
9862
12140
  return installWindows();
9863
12141
  } else if (act === "uninstall" || act === "remove") {
@@ -9885,9 +12163,9 @@ var exports_status_line = {};
9885
12163
  __export(exports_status_line, {
9886
12164
  runStatusLine: () => runStatusLine
9887
12165
  });
9888
- import { existsSync as existsSync16, readFileSync as readFileSync11 } from "node:fs";
9889
- import { join as join10 } from "node:path";
9890
- import { homedir as homedir10 } from "node:os";
12166
+ import { existsSync as existsSync22, readFileSync as readFileSync16 } from "node:fs";
12167
+ import { join as join13 } from "node:path";
12168
+ import { homedir as homedir11 } from "node:os";
9891
12169
  async function runStatusLine() {
9892
12170
  try {
9893
12171
  const config = readConfig();
@@ -9895,11 +12173,11 @@ async function runStatusLine() {
9895
12173
  process.stdout.write("◇ claudemesh (not joined)");
9896
12174
  return EXIT.SUCCESS;
9897
12175
  }
9898
- const cachePath = join10(homedir10(), ".claudemesh", "peer-cache.json");
12176
+ const cachePath = join13(homedir11(), ".claudemesh", "peer-cache.json");
9899
12177
  let cache = {};
9900
- if (existsSync16(cachePath)) {
12178
+ if (existsSync22(cachePath)) {
9901
12179
  try {
9902
- cache = JSON.parse(readFileSync11(cachePath, "utf-8"));
12180
+ cache = JSON.parse(readFileSync16(cachePath, "utf-8"));
9903
12181
  } catch {}
9904
12182
  }
9905
12183
  const pick = config.meshes[0];
@@ -9930,7 +12208,7 @@ __export(exports_backup, {
9930
12208
  runRestore: () => runRestore,
9931
12209
  runBackup: () => runBackup
9932
12210
  });
9933
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync17 } from "node:fs";
12211
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync23 } from "node:fs";
9934
12212
  import { createInterface as createInterface11 } from "node:readline";
9935
12213
  function readHidden(prompt5) {
9936
12214
  return new Promise((resolve2) => {
@@ -9972,11 +12250,11 @@ async function deriveKey(pass, salt, s) {
9972
12250
  }
9973
12251
  async function runBackup(outPath) {
9974
12252
  const configPath = getConfigPath();
9975
- if (!existsSync17(configPath)) {
12253
+ if (!existsSync23(configPath)) {
9976
12254
  console.error(" No config found — nothing to back up. Join a mesh first.");
9977
12255
  return EXIT.NOT_FOUND;
9978
12256
  }
9979
- const plaintext = readFileSync12(configPath);
12257
+ const plaintext = readFileSync17(configPath);
9980
12258
  const pass = await readHidden(" Passphrase (min 12 chars): ");
9981
12259
  if (pass.length < 12) {
9982
12260
  console.error(" ✗ Passphrase too short.");
@@ -9994,7 +12272,7 @@ async function runBackup(outPath) {
9994
12272
  const ciphertext = Buffer.from(s.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, null, null, nonce, key));
9995
12273
  const blob = Buffer.concat([MAGIC, salt, nonce, ciphertext]);
9996
12274
  const file = outPath ?? `claudemesh-backup-${new Date().toISOString().replace(/[:.]/g, "-")}.cmb`;
9997
- writeFileSync9(file, blob, { mode: 384 });
12275
+ writeFileSync13(file, blob, { mode: 384 });
9998
12276
  console.log(`
9999
12277
  ✓ Backup saved: ${file}`);
10000
12278
  console.log(` Size: ${blob.length} bytes. Guard the passphrase — there is no recovery.
@@ -10006,11 +12284,11 @@ async function runRestore(inPath) {
10006
12284
  console.error(" Usage: claudemesh restore <backup-file>");
10007
12285
  return EXIT.INVALID_ARGS;
10008
12286
  }
10009
- if (!existsSync17(inPath)) {
12287
+ if (!existsSync23(inPath)) {
10010
12288
  console.error(` ✗ File not found: ${inPath}`);
10011
12289
  return EXIT.NOT_FOUND;
10012
12290
  }
10013
- const blob = readFileSync12(inPath);
12291
+ const blob = readFileSync17(inPath);
10014
12292
  if (blob.length < 4 + 16 + 24 + 17 || !blob.subarray(0, 4).equals(MAGIC)) {
10015
12293
  console.error(" ✗ Not a claudemesh backup file (bad magic).");
10016
12294
  return EXIT.INVALID_ARGS;
@@ -10029,12 +12307,12 @@ async function runRestore(inPath) {
10029
12307
  return EXIT.INTERNAL_ERROR;
10030
12308
  }
10031
12309
  const configPath = getConfigPath();
10032
- if (existsSync17(configPath)) {
12310
+ if (existsSync23(configPath)) {
10033
12311
  const backupOld = `${configPath}.before-restore.${Date.now()}`;
10034
- writeFileSync9(backupOld, readFileSync12(configPath), { mode: 384 });
12312
+ writeFileSync13(backupOld, readFileSync17(configPath), { mode: 384 });
10035
12313
  console.log(` ↻ Existing config saved to ${backupOld}`);
10036
12314
  }
10037
- writeFileSync9(configPath, Buffer.from(plaintext), { mode: 384 });
12315
+ writeFileSync13(configPath, Buffer.from(plaintext), { mode: 384 });
10038
12316
  console.log(`
10039
12317
  ✓ Config restored to ${configPath}`);
10040
12318
  console.log(" Run `claudemesh list` to verify your meshes.\n");
@@ -10054,8 +12332,8 @@ __export(exports_upgrade, {
10054
12332
  runUpgrade: () => runUpgrade
10055
12333
  });
10056
12334
  import { spawnSync as spawnSync6 } from "node:child_process";
10057
- import { existsSync as existsSync18 } from "node:fs";
10058
- import { dirname as dirname5, join as join11, resolve as resolve2 } from "node:path";
12335
+ import { existsSync as existsSync24 } from "node:fs";
12336
+ import { dirname as dirname8, join as join14, resolve as resolve2 } from "node:path";
10059
12337
  async function latestVersion() {
10060
12338
  try {
10061
12339
  const res = await fetch(URLS.NPM_REGISTRY, { signal: AbortSignal.timeout(8000) });
@@ -10068,15 +12346,15 @@ async function latestVersion() {
10068
12346
  }
10069
12347
  }
10070
12348
  function findNpm() {
10071
- const portable = join11(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
10072
- if (existsSync18(portable)) {
10073
- return { npm: portable, prefix: join11(process.env.HOME ?? "", ".claudemesh") };
12349
+ const portable = join14(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
12350
+ if (existsSync24(portable)) {
12351
+ return { npm: portable, prefix: join14(process.env.HOME ?? "", ".claudemesh") };
10074
12352
  }
10075
12353
  let cur = resolve2(process.argv[1] ?? ".");
10076
12354
  for (let i = 0;i < 6; i++) {
10077
- cur = dirname5(cur);
10078
- const candidate = join11(cur, "bin", "npm");
10079
- if (existsSync18(candidate))
12355
+ cur = dirname8(cur);
12356
+ const candidate = join14(cur, "bin", "npm");
12357
+ if (existsSync24(candidate))
10080
12358
  return { npm: candidate };
10081
12359
  }
10082
12360
  return { npm: "npm" };
@@ -10138,9 +12416,9 @@ __export(exports_grants, {
10138
12416
  runBlock: () => runBlock,
10139
12417
  isAllowed: () => isAllowed
10140
12418
  });
10141
- import { existsSync as existsSync19, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "node:fs";
10142
- import { homedir as homedir11 } from "node:os";
10143
- import { join as join12 } from "node:path";
12419
+ import { existsSync as existsSync25, mkdirSync as mkdirSync10, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "node:fs";
12420
+ import { homedir as homedir12 } from "node:os";
12421
+ import { join as join15 } from "node:path";
10144
12422
  async function syncToBroker(meshSlug, grants) {
10145
12423
  const auth = getStoredToken();
10146
12424
  if (!auth)
@@ -10158,19 +12436,19 @@ async function syncToBroker(meshSlug, grants) {
10158
12436
  }
10159
12437
  }
10160
12438
  function readGrants() {
10161
- if (!existsSync19(GRANT_FILE))
12439
+ if (!existsSync25(GRANT_FILE))
10162
12440
  return {};
10163
12441
  try {
10164
- return JSON.parse(readFileSync13(GRANT_FILE, "utf-8"));
12442
+ return JSON.parse(readFileSync18(GRANT_FILE, "utf-8"));
10165
12443
  } catch {
10166
12444
  return {};
10167
12445
  }
10168
12446
  }
10169
12447
  function writeGrants(g) {
10170
- const dir = join12(homedir11(), ".claudemesh");
10171
- if (!existsSync19(dir))
10172
- mkdirSync6(dir, { recursive: true });
10173
- writeFileSync10(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
12448
+ const dir = join15(homedir12(), ".claudemesh");
12449
+ if (!existsSync25(dir))
12450
+ mkdirSync10(dir, { recursive: true });
12451
+ writeFileSync14(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
10174
12452
  }
10175
12453
  function resolveCaps(input) {
10176
12454
  if (input.includes("all"))
@@ -10326,7 +12604,7 @@ var init_grants = __esm(() => {
10326
12604
  BROKER_HTTP7 = URLS.BROKER.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
10327
12605
  ALL_CAPS = ["read", "dm", "broadcast", "state-read", "state-write", "file-read"];
10328
12606
  DEFAULT_CAPS = ["read", "dm", "broadcast", "state-read"];
10329
- GRANT_FILE = join12(homedir11(), ".claudemesh", "grants.json");
12607
+ GRANT_FILE = join15(homedir12(), ".claudemesh", "grants.json");
10330
12608
  });
10331
12609
 
10332
12610
  // src/commands/profile.ts
@@ -11805,8 +14083,8 @@ __export(exports_file, {
11805
14083
  runFileGet: () => runFileGet
11806
14084
  });
11807
14085
  import { hostname as osHostname } from "node:os";
11808
- import { resolve as resolvePath, basename, dirname as dirname6 } from "node:path";
11809
- import { statSync as statSync6, existsSync as existsSync20, writeFileSync as writeFileSync11, mkdirSync as mkdirSync7 } from "node:fs";
14086
+ import { resolve as resolvePath, basename, dirname as dirname9 } from "node:path";
14087
+ import { statSync as statSync6, existsSync as existsSync26, writeFileSync as writeFileSync15, mkdirSync as mkdirSync11 } from "node:fs";
11810
14088
  function emitJson2(data) {
11811
14089
  console.log(JSON.stringify(data, null, 2));
11812
14090
  }
@@ -11823,7 +14101,7 @@ async function runFileShare(filePath, opts) {
11823
14101
  return EXIT.INVALID_ARGS;
11824
14102
  }
11825
14103
  const absPath = resolvePath(filePath);
11826
- if (!existsSync20(absPath)) {
14104
+ if (!existsSync26(absPath)) {
11827
14105
  render.err(`File not found: ${absPath}`);
11828
14106
  return EXIT.INVALID_ARGS;
11829
14107
  }
@@ -11902,8 +14180,8 @@ async function runFileGet(fileId, opts) {
11902
14180
  }
11903
14181
  const buf = Buffer.from(await res.arrayBuffer());
11904
14182
  const outPath = opts.out ? resolvePath(opts.out) : resolvePath(process.cwd(), meta.name);
11905
- mkdirSync7(dirname6(outPath), { recursive: true });
11906
- writeFileSync11(outPath, buf);
14183
+ mkdirSync11(dirname9(outPath), { recursive: true });
14184
+ writeFileSync15(outPath, buf);
11907
14185
  if (opts.json) {
11908
14186
  emitJson2({ fileId, name: meta.name, savedTo: outPath, sizeBytes: buf.length });
11909
14187
  } else {
@@ -11998,7 +14276,7 @@ var require_client = __commonJS((exports) => {
11998
14276
  var ws_1 = __importDefault(__require("ws"));
11999
14277
  var crypto_js_1 = require_crypto();
12000
14278
  var MAX_QUEUED2 = 100;
12001
- var HELLO_ACK_TIMEOUT_MS2 = 5000;
14279
+ var HELLO_ACK_TIMEOUT_MS3 = 5000;
12002
14280
  var BACKOFF_CAPS2 = [1000, 2000, 4000, 8000, 16000, 30000];
12003
14281
 
12004
14282
  class MeshClient extends node_events_1.EventEmitter {
@@ -12063,7 +14341,7 @@ var require_client = __commonJS((exports) => {
12063
14341
  this.debug("hello_ack timeout");
12064
14342
  ws.close();
12065
14343
  reject(new Error("hello_ack timeout"));
12066
- }, HELLO_ACK_TIMEOUT_MS2);
14344
+ }, HELLO_ACK_TIMEOUT_MS3);
12067
14345
  };
12068
14346
  const onMessage = (raw) => {
12069
14347
  let msg;
@@ -12513,7 +14791,7 @@ __export(exports_bridge, {
12513
14791
  runBridge: () => runBridge,
12514
14792
  bridgeConfigTemplate: () => bridgeConfigTemplate
12515
14793
  });
12516
- import { readFileSync as readFileSync14, existsSync as existsSync21 } from "node:fs";
14794
+ import { readFileSync as readFileSync19, existsSync as existsSync27 } from "node:fs";
12517
14795
  function parseConfig(text) {
12518
14796
  const trimmed = text.trim();
12519
14797
  if (trimmed.startsWith("{"))
@@ -12557,13 +14835,13 @@ async function runBridge(configPath) {
12557
14835
  render.err("Usage: claudemesh bridge run <config.yaml>");
12558
14836
  return EXIT.INVALID_ARGS;
12559
14837
  }
12560
- if (!existsSync21(configPath)) {
14838
+ if (!existsSync27(configPath)) {
12561
14839
  render.err(`config file not found: ${configPath}`);
12562
14840
  return EXIT.NOT_FOUND;
12563
14841
  }
12564
14842
  let cfg;
12565
14843
  try {
12566
- cfg = parseConfig(readFileSync14(configPath, "utf-8"));
14844
+ cfg = parseConfig(readFileSync19(configPath, "utf-8"));
12567
14845
  } catch (e) {
12568
14846
  render.err(`failed to parse ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
12569
14847
  return EXIT.INVALID_ARGS;
@@ -12967,9 +15245,9 @@ function cacheKey(apiKeySecret, topicName) {
12967
15245
  async function getTopicKey(args) {
12968
15246
  const cacheId = cacheKey(args.apiKeySecret, args.topicName);
12969
15247
  if (!args.fresh) {
12970
- const cached = cache.get(cacheId);
12971
- if (cached)
12972
- return { ok: true, topicKey: cached.topicKey };
15248
+ const cached2 = cache.get(cacheId);
15249
+ if (cached2)
15250
+ return { ok: true, topicKey: cached2.topicKey };
12973
15251
  }
12974
15252
  let sealed;
12975
15253
  try {
@@ -13533,8 +15811,8 @@ var init_definitions = __esm(() => {
13533
15811
  });
13534
15812
 
13535
15813
  // src/services/bridge/server.ts
13536
- import { createServer as createServer2 } from "node:net";
13537
- import { mkdirSync as mkdirSync8, unlinkSync as unlinkSync2, existsSync as existsSync22, chmodSync as chmodSync5 } from "node:fs";
15814
+ import { createServer as createServer3 } from "node:net";
15815
+ import { mkdirSync as mkdirSync12, unlinkSync as unlinkSync5, existsSync as existsSync28, chmodSync as chmodSync6 } from "node:fs";
13538
15816
  async function resolveTarget2(client, to) {
13539
15817
  if (to.startsWith("@") || to === "*" || /^[0-9a-f]{64}$/i.test(to)) {
13540
15818
  return { ok: true, spec: to };
@@ -13646,21 +15924,21 @@ function handleConnection(socket, client) {
13646
15924
  socket.on("error", () => {});
13647
15925
  }
13648
15926
  function startBridgeServer(client) {
13649
- const path = socketPath(client.meshSlug);
15927
+ const path2 = socketPath(client.meshSlug);
13650
15928
  const dir = socketDir();
13651
- if (!existsSync22(dir)) {
13652
- mkdirSync8(dir, { recursive: true, mode: 448 });
15929
+ if (!existsSync28(dir)) {
15930
+ mkdirSync12(dir, { recursive: true, mode: 448 });
13653
15931
  }
13654
- if (existsSync22(path)) {
15932
+ if (existsSync28(path2)) {
13655
15933
  try {
13656
- unlinkSync2(path);
15934
+ unlinkSync5(path2);
13657
15935
  } catch {}
13658
15936
  }
13659
- const server = createServer2((socket) => handleConnection(socket, client));
15937
+ const server = createServer3((socket) => handleConnection(socket, client));
13660
15938
  try {
13661
- server.listen(path);
15939
+ server.listen(path2);
13662
15940
  } catch (err) {
13663
- process.stderr.write(`[claudemesh] bridge: failed to bind ${path}: ${String(err)}
15941
+ process.stderr.write(`[claudemesh] bridge: failed to bind ${path2}: ${String(err)}
13664
15942
  `);
13665
15943
  return null;
13666
15944
  }
@@ -13669,11 +15947,11 @@ function startBridgeServer(client) {
13669
15947
  `);
13670
15948
  });
13671
15949
  try {
13672
- chmodSync5(path, 384);
15950
+ chmodSync6(path2, 384);
13673
15951
  } catch {}
13674
15952
  let stopped = false;
13675
15953
  return {
13676
- path,
15954
+ path: path2,
13677
15955
  stop() {
13678
15956
  if (stopped)
13679
15957
  return;
@@ -13682,12 +15960,12 @@ function startBridgeServer(client) {
13682
15960
  server.close();
13683
15961
  } catch {}
13684
15962
  try {
13685
- unlinkSync2(path);
15963
+ unlinkSync5(path2);
13686
15964
  } catch {}
13687
15965
  }
13688
15966
  };
13689
15967
  }
13690
- var init_server = __esm(() => {
15968
+ var init_server2 = __esm(() => {
13691
15969
  init_protocol();
13692
15970
  });
13693
15971
 
@@ -14258,9 +16536,9 @@ async function startServiceProxy(serviceName) {
14258
16536
  const fetched = await client.getServiceTools(serviceName);
14259
16537
  tools = fetched;
14260
16538
  } catch {
14261
- const cached = client.serviceCatalog.find((s) => s.name === serviceName);
14262
- if (cached) {
14263
- tools = cached.tools;
16539
+ const cached2 = client.serviceCatalog.find((s) => s.name === serviceName);
16540
+ if (cached2) {
16541
+ tools = cached2.tools;
14264
16542
  }
14265
16543
  }
14266
16544
  if (tools.length === 0) {
@@ -14349,11 +16627,11 @@ async function startServiceProxy(serviceName) {
14349
16627
  process.on("SIGINT", shutdown);
14350
16628
  }
14351
16629
  var peerNameCache, peerNameCacheAge = 0, CACHE_TTL_MS = 30000;
14352
- var init_server2 = __esm(() => {
16630
+ var init_server3 = __esm(() => {
14353
16631
  init_definitions();
14354
16632
  init_facade();
14355
16633
  init_facade8();
14356
- init_server();
16634
+ init_server2();
14357
16635
  peerNameCache = new Map;
14358
16636
  });
14359
16637
 
@@ -14369,7 +16647,7 @@ async function runMcp() {
14369
16647
  process.exit(0);
14370
16648
  }
14371
16649
  var init_mcp = __esm(() => {
14372
- init_server2();
16650
+ init_server3();
14373
16651
  });
14374
16652
 
14375
16653
  // src/commands/hook.ts
@@ -15470,6 +17748,23 @@ async function main() {
15470
17748
  process.exit(await whoami2({ json: !!flags.json }));
15471
17749
  break;
15472
17750
  }
17751
+ case "daemon": {
17752
+ const { runDaemonCommand: runDaemonCommand2 } = await Promise.resolve().then(() => (init_daemon(), exports_daemon));
17753
+ const sub = positionals[0];
17754
+ const rest = positionals.slice(1);
17755
+ const outboxStatus = flags.failed ? "dead" : flags.pending ? "pending" : flags.inflight ? "inflight" : flags.done ? "done" : flags.aborted ? "aborted" : undefined;
17756
+ const code = await runDaemonCommand2(sub, {
17757
+ json: !!flags.json,
17758
+ noTcp: !!flags["no-tcp"],
17759
+ publicHealth: !!flags["public-health"],
17760
+ mesh: flags.mesh,
17761
+ displayName: flags.name,
17762
+ outboxStatus,
17763
+ newClientId: flags["new-client-id"]
17764
+ }, rest);
17765
+ process.exit(code);
17766
+ break;
17767
+ }
15473
17768
  case "install": {
15474
17769
  const { runInstall: runInstall2 } = await Promise.resolve().then(() => (init_install(), exports_install));
15475
17770
  runInstall2(positionals);
@@ -15490,8 +17785,8 @@ async function main() {
15490
17785
  const { runStatusSet: runStatusSet2 } = await Promise.resolve().then(() => (init_broker_actions(), exports_broker_actions));
15491
17786
  process.exit(await runStatusSet2(positionals[1] ?? "", { mesh: flags.mesh, json: !!flags.json }));
15492
17787
  } else {
15493
- const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), exports_status));
15494
- await runStatus2();
17788
+ const { runStatus: runStatus3 } = await Promise.resolve().then(() => (init_status(), exports_status));
17789
+ await runStatus3();
15495
17790
  }
15496
17791
  break;
15497
17792
  }
@@ -15643,8 +17938,8 @@ async function main() {
15643
17938
  const { runStatusSet: runStatusSet2 } = await Promise.resolve().then(() => (init_broker_actions(), exports_broker_actions));
15644
17939
  process.exit(await runStatusSet2(positionals[2] ?? "", { mesh: flags.mesh, json: !!flags.json }));
15645
17940
  } else {
15646
- const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), exports_status));
15647
- await runStatus2();
17941
+ const { runStatus: runStatus3 } = await Promise.resolve().then(() => (init_status(), exports_status));
17942
+ await runStatus3();
15648
17943
  }
15649
17944
  } else {
15650
17945
  console.error("Usage: claudemesh profile [summary|visible|status]");
@@ -16131,4 +18426,4 @@ main().catch((err) => {
16131
18426
  process.exit(EXIT.INTERNAL_ERROR);
16132
18427
  });
16133
18428
 
16134
- //# debugId=5139AEA096A0916B64756E2164756E21
18429
+ //# debugId=BA4EDB138BD2E1AE64756E2164756E21