claudemesh-cli 1.21.0 → 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.0", 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",
@@ -5180,6 +5180,8 @@ async function rename(oldSlug, newSlug) {
5180
5180
  return EXIT.PERMISSION_DENIED;
5181
5181
  if (err.status === 404)
5182
5182
  return EXIT.NOT_FOUND;
5183
+ if (err.status === 409)
5184
+ return EXIT.ALREADY_EXISTS;
5183
5185
  if (err.status === 400)
5184
5186
  return EXIT.INVALID_ARGS;
5185
5187
  return EXIT.INTERNAL_ERROR;
@@ -6832,6 +6834,154 @@ var init_peers = __esm(() => {
6832
6834
  };
6833
6835
  });
6834
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
+
6835
6985
  // src/commands/send.ts
6836
6986
  var exports_send = {};
6837
6987
  __export(exports_send, {
@@ -6853,6 +7003,23 @@ async function runSend(flags, to, message) {
6853
7003
  process.exit(1);
6854
7004
  }
6855
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
+ }
6856
7023
  if (meshSlug) {
6857
7024
  const bridged = await tryBridge(meshSlug, "send", { to, message, priority });
6858
7025
  if (bridged !== null) {
@@ -6916,6 +7083,7 @@ var init_send = __esm(() => {
6916
7083
  init_connect();
6917
7084
  init_facade();
6918
7085
  init_client3();
7086
+ init_daemon_route();
6919
7087
  init_render();
6920
7088
  init_styles();
6921
7089
  });
@@ -7999,180 +8167,2292 @@ async function runRemind(flags, positional) {
7999
8167
  console.log(JSON.stringify(scheduled, null, 2));
8000
8168
  return;
8001
8169
  }
8002
- if (scheduled.length === 0) {
8003
- render.info(dim("No pending reminders."));
8004
- 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;
8005
10252
  }
8006
- render.section(`reminders (${scheduled.length})`);
8007
- for (const m of scheduled) {
8008
- const when = new Date(m.deliverAt).toLocaleString();
8009
- const to = m.to === client.getSessionPubkey() ? dim("(self)") : m.to;
8010
- 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) + `
8011
10263
  `);
8012
- process.stdout.write(` ${dim(m.message.slice(0, 80))}
8013
-
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)}
8014
10274
  `);
10275
+ return 1;
8015
10276
  }
8016
- });
8017
- return;
8018
- }
8019
- if (action === "cancel") {
8020
- const id = positional[1];
8021
- if (!id) {
8022
- render.err("Usage: claudemesh remind cancel <id>");
8023
- process.exit(1);
8024
10277
  }
8025
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
8026
- const ok = await client.cancelScheduled(id);
8027
- if (ok)
8028
- render.ok(`cancelled ${bold(id.slice(0, 8))}`);
8029
- else {
8030
- render.err(`not found or already fired: ${id}`);
8031
- process.exit(1);
8032
- }
8033
- });
8034
- 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;
8035
10284
  }
8036
- const message = action ?? positional.join(" ");
8037
- if (!message) {
8038
- render.err("Usage: claudemesh remind <message> --in <duration>");
8039
- render.info(dim(" claudemesh remind <message> --at <time>"));
8040
- render.info(dim(' claudemesh remind <message> --cron "0 */2 * * *"'));
8041
- render.info(dim(" claudemesh remind list"));
8042
- render.info(dim(" claudemesh remind cancel <id>"));
8043
- 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;
8044
10293
  }
8045
- const isCron = !!flags.cron;
8046
- const deliverAt = isCron ? 0 : parseDeliverAt(flags);
8047
- if (!isCron && deliverAt === null) {
8048
- render.err("Specify when", 'use --in <duration> (e.g. "2h", "30m"), --at <time> (e.g. "15:00"), or --cron <expression>');
8049
- 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;
8050
10298
  }
8051
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
8052
- let targetSpec;
8053
- if (flags.to && flags.to !== "self") {
8054
- if (flags.to.startsWith("@") || flags.to === "*" || /^[0-9a-f]{64}$/i.test(flags.to)) {
8055
- targetSpec = flags.to;
8056
- } else {
8057
- const peers = await client.listPeers();
8058
- const match = peers.find((p) => p.displayName.toLowerCase() === flags.to.toLowerCase());
8059
- if (!match) {
8060
- render.err(`Peer "${flags.to}" not found`, `online: ${peers.map((p) => p.displayName).join(", ") || "(none)"}`);
8061
- process.exit(1);
8062
- }
8063
- targetSpec = match.pubkey;
8064
- }
8065
- } else {
8066
- targetSpec = client.getSessionPubkey() ?? "*";
8067
- }
8068
- const result = await client.scheduleMessage(targetSpec, message, deliverAt ?? 0, false, flags.cron);
8069
- if (!result) {
8070
- render.err("Broker did not acknowledge — check connection");
8071
- process.exit(1);
8072
- }
8073
- if (flags.json) {
8074
- console.log(JSON.stringify(result));
8075
- 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;
8076
10308
  }
8077
- const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
8078
- if (isCron) {
8079
- const nextFire = new Date(result.deliverAt).toLocaleString();
8080
- 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
+ `);
8081
10319
  } else {
8082
- const when = new Date(result.deliverAt).toLocaleString();
8083
- 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
+ `);
8084
10324
  }
8085
- });
10325
+ return 0;
10326
+ } catch (err) {
10327
+ process.stderr.write(`install-service failed: ${String(err)}
10328
+ `);
10329
+ return 1;
10330
+ }
8086
10331
  }
8087
- var init_remind = __esm(() => {
8088
- init_connect();
8089
- init_render();
8090
- init_styles();
8091
- });
8092
-
8093
- // src/commands/register.ts
8094
- var exports_register = {};
8095
- __export(exports_register, {
8096
- register: () => register2
8097
- });
8098
- async function register2() {
8099
- 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;
8100
10345
  }
8101
- var init_register = __esm(() => {
8102
- init_login();
8103
- });
8104
-
8105
- // src/commands/logout.ts
8106
- var exports_logout = {};
8107
- __export(exports_logout, {
8108
- logout: () => logout2
8109
- });
8110
- 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
+ }
8111
10368
  try {
8112
- const { revoked } = await logout();
8113
- if (revoked) {
8114
- 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
+ `);
8115
10373
  } else {
8116
- console.log(` ${yellow(icons.warn)} Could not revoke session on claudemesh.com.`);
8117
- 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
+ `);
8118
10378
  }
8119
- console.log(` ${green(icons.check)} Removed local credentials.`);
8120
- return EXIT.SUCCESS;
10379
+ return 0;
8121
10380
  } catch (err) {
8122
- console.error(` ${icons.cross} Logout failed: ${err instanceof Error ? err.message : err}`);
8123
- 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;
8124
10388
  }
8125
10389
  }
8126
- var init_logout = __esm(() => {
8127
- init_facade6();
8128
- init_styles();
8129
- init_exit_codes();
8130
- });
8131
-
8132
- // src/commands/whoami.ts
8133
- var exports_whoami = {};
8134
- __export(exports_whoami, {
8135
- whoami: () => whoami
8136
- });
8137
- async function whoami(opts) {
8138
- const result = await whoAmI();
8139
- if (opts.json) {
8140
- console.log(JSON.stringify({ schema_version: "1.0", ...result }, null, 2));
8141
- 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;
8142
10411
  }
8143
- if (!result.signed_in && !result.local) {
8144
- render.err("Not signed in", "Run `claudemesh login` to sign in or `claudemesh <invite>` to join.");
8145
- 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;
8146
10423
  }
8147
- render.section("whoami");
8148
- if (result.signed_in) {
8149
- render.kv([
8150
- ["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
8151
- ["token", `${result.token_source} ${dim("(~/.claudemesh/auth.json)")}`],
8152
- ...result.meshes ? [["meshes", `${result.meshes.owned} owned · ${result.meshes.guest} guest`]] : []
8153
- ]);
8154
- } else {
8155
- render.kv([
8156
- ["web", dim("not signed in · run `claudemesh login` for account features")]
8157
- ]);
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;
8158
10430
  }
8159
- if (result.local) {
8160
- render.blank();
8161
- render.kv([
8162
- ["local", `${result.local.meshes.length} mesh${result.local.meshes.length === 1 ? "" : "es"} · ${dim(result.local.config_path)}`]
8163
- ]);
8164
- for (const m of result.local.meshes) {
8165
- 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;
8166
10441
  }
8167
10442
  }
8168
- render.blank();
8169
- 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;
8170
10450
  }
8171
- var init_whoami = __esm(() => {
8172
- init_facade6();
8173
- init_render();
8174
- init_styles();
8175
- init_exit_codes();
10451
+ var init_daemon = __esm(() => {
10452
+ init_run();
10453
+ init_client4();
10454
+ init_lock();
10455
+ init_paths2();
8176
10456
  });
8177
10457
 
8178
10458
  // src/commands/install.ts
@@ -8182,21 +10462,21 @@ __export(exports_install, {
8182
10462
  runInstall: () => runInstall
8183
10463
  });
8184
10464
  import {
8185
- chmodSync as chmodSync3,
10465
+ chmodSync as chmodSync4,
8186
10466
  copyFileSync,
8187
- existsSync as existsSync7,
8188
- mkdirSync as mkdirSync4,
8189
- readFileSync as readFileSync5,
8190
- writeFileSync as writeFileSync6
10467
+ existsSync as existsSync13,
10468
+ mkdirSync as mkdirSync8,
10469
+ readFileSync as readFileSync10,
10470
+ writeFileSync as writeFileSync10
8191
10471
  } from "node:fs";
8192
- import { homedir as homedir6, platform as platform5 } from "node:os";
8193
- 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";
8194
10474
  import { fileURLToPath } from "node:url";
8195
10475
  import { spawnSync as spawnSync3 } from "node:child_process";
8196
10476
  function readClaudeConfig() {
8197
- if (!existsSync7(CLAUDE_CONFIG))
10477
+ if (!existsSync13(CLAUDE_CONFIG))
8198
10478
  return {};
8199
- const text = readFileSync5(CLAUDE_CONFIG, "utf-8").trim();
10479
+ const text = readFileSync10(CLAUDE_CONFIG, "utf-8").trim();
8200
10480
  if (!text)
8201
10481
  return {};
8202
10482
  try {
@@ -8206,12 +10486,12 @@ function readClaudeConfig() {
8206
10486
  }
8207
10487
  }
8208
10488
  function backupClaudeConfig() {
8209
- if (!existsSync7(CLAUDE_CONFIG))
10489
+ if (!existsSync13(CLAUDE_CONFIG))
8210
10490
  return;
8211
- const backupDir = join6(dirname3(CLAUDE_CONFIG), ".claude", "backups");
8212
- mkdirSync4(backupDir, { recursive: true });
10491
+ const backupDir = join9(dirname6(CLAUDE_CONFIG), ".claude", "backups");
10492
+ mkdirSync8(backupDir, { recursive: true });
8213
10493
  const ts = Date.now();
8214
- const dest = join6(backupDir, `.claude.json.pre-claudemesh.${ts}`);
10494
+ const dest = join9(backupDir, `.claude.json.pre-claudemesh.${ts}`);
8215
10495
  copyFileSync(CLAUDE_CONFIG, dest);
8216
10496
  }
8217
10497
  function patchMcpServer(entry) {
@@ -8235,7 +10515,7 @@ function patchMcpServer(entry) {
8235
10515
  return action;
8236
10516
  }
8237
10517
  function removeMcpServer() {
8238
- if (!existsSync7(CLAUDE_CONFIG))
10518
+ if (!existsSync13(CLAUDE_CONFIG))
8239
10519
  return false;
8240
10520
  backupClaudeConfig();
8241
10521
  const cfg = readClaudeConfig();
@@ -8248,11 +10528,11 @@ function removeMcpServer() {
8248
10528
  return true;
8249
10529
  }
8250
10530
  function flushClaudeConfig(obj) {
8251
- mkdirSync4(dirname3(CLAUDE_CONFIG), { recursive: true });
8252
- writeFileSync6(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
10531
+ mkdirSync8(dirname6(CLAUDE_CONFIG), { recursive: true });
10532
+ writeFileSync10(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
8253
10533
  `, "utf-8");
8254
10534
  try {
8255
- chmodSync3(CLAUDE_CONFIG, 384);
10535
+ chmodSync4(CLAUDE_CONFIG, 384);
8256
10536
  } catch {}
8257
10537
  }
8258
10538
  function bunAvailable() {
@@ -8266,13 +10546,13 @@ function resolveEntry() {
8266
10546
  const here = fileURLToPath(import.meta.url);
8267
10547
  if (isBundledFile(here))
8268
10548
  return here;
8269
- return resolve(dirname3(here), "..", "index.ts");
10549
+ return resolve(dirname6(here), "..", "index.ts");
8270
10550
  }
8271
10551
  function resolveBundledSkillsDir() {
8272
10552
  const here = fileURLToPath(import.meta.url);
8273
- const pkgRoot = resolve(dirname3(here), "..", "..");
8274
- const skillsDir = join6(pkgRoot, "skills");
8275
- if (existsSync7(skillsDir))
10553
+ const pkgRoot = resolve(dirname6(here), "..", "..");
10554
+ const skillsDir = join9(pkgRoot, "skills");
10555
+ if (existsSync13(skillsDir))
8276
10556
  return skillsDir;
8277
10557
  return null;
8278
10558
  }
@@ -8285,13 +10565,13 @@ function installSkills() {
8285
10565
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
8286
10566
  if (!entry.isDirectory())
8287
10567
  continue;
8288
- const srcDir = join6(src, entry.name);
8289
- const dstDir = join6(CLAUDE_SKILLS_ROOT, entry.name);
8290
- 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 });
8291
10571
  for (const file of fs.readdirSync(srcDir, { withFileTypes: true })) {
8292
10572
  if (!file.isFile())
8293
10573
  continue;
8294
- copyFileSync(join6(srcDir, file.name), join6(dstDir, file.name));
10574
+ copyFileSync(join9(srcDir, file.name), join9(dstDir, file.name));
8295
10575
  }
8296
10576
  installed.push(entry.name);
8297
10577
  }
@@ -8306,8 +10586,8 @@ function uninstallSkills() {
8306
10586
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
8307
10587
  if (!entry.isDirectory())
8308
10588
  continue;
8309
- const dstDir = join6(CLAUDE_SKILLS_ROOT, entry.name);
8310
- if (existsSync7(dstDir)) {
10589
+ const dstDir = join9(CLAUDE_SKILLS_ROOT, entry.name);
10590
+ if (existsSync13(dstDir)) {
8311
10591
  try {
8312
10592
  fs.rmSync(dstDir, { recursive: true, force: true });
8313
10593
  removed.push(entry.name);
@@ -8332,9 +10612,9 @@ function entriesEqual(a, b) {
8332
10612
  return a.command === b.command && JSON.stringify(a.args ?? []) === JSON.stringify(b.args ?? []);
8333
10613
  }
8334
10614
  function readClaudeSettings() {
8335
- if (!existsSync7(CLAUDE_SETTINGS))
10615
+ if (!existsSync13(CLAUDE_SETTINGS))
8336
10616
  return {};
8337
- const text = readFileSync5(CLAUDE_SETTINGS, "utf-8").trim();
10617
+ const text = readFileSync10(CLAUDE_SETTINGS, "utf-8").trim();
8338
10618
  if (!text)
8339
10619
  return {};
8340
10620
  try {
@@ -8344,8 +10624,8 @@ function readClaudeSettings() {
8344
10624
  }
8345
10625
  }
8346
10626
  function writeClaudeSettings(obj) {
8347
- mkdirSync4(dirname3(CLAUDE_SETTINGS), { recursive: true });
8348
- writeFileSync6(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
10627
+ mkdirSync8(dirname6(CLAUDE_SETTINGS), { recursive: true });
10628
+ writeFileSync10(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
8349
10629
  `, "utf-8");
8350
10630
  }
8351
10631
  function installAllowedTools() {
@@ -8359,7 +10639,7 @@ function installAllowedTools() {
8359
10639
  return { added: toAdd, unchanged: CLAUDEMESH_TOOLS.length - toAdd.length };
8360
10640
  }
8361
10641
  function uninstallAllowedTools() {
8362
- if (!existsSync7(CLAUDE_SETTINGS))
10642
+ if (!existsSync13(CLAUDE_SETTINGS))
8363
10643
  return 0;
8364
10644
  const settings = readClaudeSettings();
8365
10645
  const existing = settings.allowedTools ?? [];
@@ -8394,7 +10674,7 @@ function installHooks() {
8394
10674
  return { added, unchanged };
8395
10675
  }
8396
10676
  function uninstallHooks() {
8397
- if (!existsSync7(CLAUDE_SETTINGS))
10677
+ if (!existsSync13(CLAUDE_SETTINGS))
8398
10678
  return 0;
8399
10679
  const settings = readClaudeSettings();
8400
10680
  const hooks = settings.hooks;
@@ -8443,7 +10723,7 @@ function runInstall(args = []) {
8443
10723
  render.err("`bun` is not on PATH.", "Install Bun first: https://bun.com");
8444
10724
  process.exit(1);
8445
10725
  }
8446
- if (!existsSync7(entry)) {
10726
+ if (!existsSync13(entry)) {
8447
10727
  render.err(`MCP entry not found at ${entry}`);
8448
10728
  process.exit(1);
8449
10729
  }
@@ -8494,7 +10774,7 @@ function runInstall(args = []) {
8494
10774
  const installed = installSkills();
8495
10775
  if (installed.length > 0) {
8496
10776
  render.ok(`Claude skill${installed.length === 1 ? "" : "s"} installed`, installed.join(", "));
8497
- render.info(dim(` ${join6(CLAUDE_SKILLS_ROOT, installed[0])}/SKILL.md`));
10777
+ render.info(dim(` ${join9(CLAUDE_SKILLS_ROOT, installed[0])}/SKILL.md`));
8498
10778
  }
8499
10779
  } catch (e) {
8500
10780
  render.warn(`skill install failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -8582,9 +10862,9 @@ var init_install = __esm(() => {
8582
10862
  init_facade();
8583
10863
  init_render();
8584
10864
  init_styles();
8585
- CLAUDE_CONFIG = join6(homedir6(), ".claude.json");
8586
- CLAUDE_SETTINGS = join6(homedir6(), ".claude", "settings.json");
8587
- 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");
8588
10868
  CLAUDEMESH_TOOLS = [
8589
10869
  "mcp__claudemesh__cancel_scheduled",
8590
10870
  "mcp__claudemesh__check_messages",
@@ -8639,35 +10919,35 @@ var exports_uninstall = {};
8639
10919
  __export(exports_uninstall, {
8640
10920
  uninstall: () => uninstall
8641
10921
  });
8642
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync7, existsSync as existsSync8, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
8643
- import { join as join7, dirname as dirname4 } from "node:path";
8644
- 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";
8645
10925
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8646
10926
  function bundledSkillsDir() {
8647
10927
  const here = fileURLToPath2(import.meta.url);
8648
- const pkgRoot = join7(dirname4(here), "..", "..");
8649
- const skillsDir = join7(pkgRoot, "skills");
8650
- return existsSync8(skillsDir) ? skillsDir : null;
10928
+ const pkgRoot = join10(dirname7(here), "..", "..");
10929
+ const skillsDir = join10(pkgRoot, "skills");
10930
+ return existsSync14(skillsDir) ? skillsDir : null;
8651
10931
  }
8652
10932
  async function uninstall() {
8653
10933
  let removed = 0;
8654
- if (existsSync8(PATHS.CLAUDE_JSON)) {
10934
+ if (existsSync14(PATHS.CLAUDE_JSON)) {
8655
10935
  try {
8656
- const raw = readFileSync6(PATHS.CLAUDE_JSON, "utf-8");
10936
+ const raw = readFileSync11(PATHS.CLAUDE_JSON, "utf-8");
8657
10937
  const config = JSON.parse(raw);
8658
10938
  const servers = config.mcpServers;
8659
10939
  if (servers && "claudemesh" in servers) {
8660
10940
  delete servers.claudemesh;
8661
- writeFileSync7(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
10941
+ writeFileSync11(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
8662
10942
  `, "utf-8");
8663
10943
  render.ok("removed MCP server", dim("~/.claude.json"));
8664
10944
  removed++;
8665
10945
  }
8666
10946
  } catch {}
8667
10947
  }
8668
- if (existsSync8(PATHS.CLAUDE_SETTINGS)) {
10948
+ if (existsSync14(PATHS.CLAUDE_SETTINGS)) {
8669
10949
  try {
8670
- const raw = readFileSync6(PATHS.CLAUDE_SETTINGS, "utf-8");
10950
+ const raw = readFileSync11(PATHS.CLAUDE_SETTINGS, "utf-8");
8671
10951
  const config = JSON.parse(raw);
8672
10952
  const hooks = config.hooks;
8673
10953
  if (hooks) {
@@ -8688,7 +10968,7 @@ async function uninstall() {
8688
10968
  }
8689
10969
  }
8690
10970
  if (removedHooks > 0) {
8691
- writeFileSync7(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
10971
+ writeFileSync11(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
8692
10972
  `, "utf-8");
8693
10973
  render.ok(`removed ${removedHooks} claudemesh hook${removedHooks === 1 ? "" : "s"}`, dim("settings.json"));
8694
10974
  removed++;
@@ -8703,8 +10983,8 @@ async function uninstall() {
8703
10983
  for (const entry of readdirSync2(src, { withFileTypes: true })) {
8704
10984
  if (!entry.isDirectory())
8705
10985
  continue;
8706
- const dst = join7(CLAUDE_SKILLS_ROOT2, entry.name);
8707
- if (existsSync8(dst)) {
10986
+ const dst = join10(CLAUDE_SKILLS_ROOT2, entry.name);
10987
+ if (existsSync14(dst)) {
8708
10988
  try {
8709
10989
  rmSync2(dst, { recursive: true, force: true });
8710
10990
  removedSkills.push(entry.name);
@@ -8728,7 +11008,7 @@ var init_uninstall = __esm(() => {
8728
11008
  init_render();
8729
11009
  init_styles();
8730
11010
  init_exit_codes();
8731
- CLAUDE_SKILLS_ROOT2 = join7(homedir7(), ".claude", "skills");
11011
+ CLAUDE_SKILLS_ROOT2 = join10(homedir8(), ".claude", "skills");
8732
11012
  });
8733
11013
 
8734
11014
  // src/commands/doctor.ts
@@ -8736,9 +11016,9 @@ var exports_doctor = {};
8736
11016
  __export(exports_doctor, {
8737
11017
  runDoctor: () => runDoctor
8738
11018
  });
8739
- import { existsSync as existsSync9, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
8740
- import { homedir as homedir8, platform as platform6 } from "node:os";
8741
- 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";
8742
11022
  import { spawnSync as spawnSync4 } from "node:child_process";
8743
11023
  function checkNode() {
8744
11024
  const major = Number(process.versions.node.split(".")[0]);
@@ -8762,8 +11042,8 @@ function checkClaudeOnPath() {
8762
11042
  };
8763
11043
  }
8764
11044
  function checkMcpRegistered() {
8765
- const claudeConfig = join8(homedir8(), ".claude.json");
8766
- if (!existsSync9(claudeConfig)) {
11045
+ const claudeConfig = join11(homedir9(), ".claude.json");
11046
+ if (!existsSync15(claudeConfig)) {
8767
11047
  return {
8768
11048
  name: "claudemesh MCP registered in ~/.claude.json",
8769
11049
  pass: false,
@@ -8771,7 +11051,7 @@ function checkMcpRegistered() {
8771
11051
  };
8772
11052
  }
8773
11053
  try {
8774
- const cfg = JSON.parse(readFileSync7(claudeConfig, "utf-8"));
11054
+ const cfg = JSON.parse(readFileSync12(claudeConfig, "utf-8"));
8775
11055
  const registered = Boolean(cfg.mcpServers?.["claudemesh"]);
8776
11056
  return {
8777
11057
  name: "claudemesh MCP registered in ~/.claude.json",
@@ -8788,8 +11068,8 @@ function checkMcpRegistered() {
8788
11068
  }
8789
11069
  }
8790
11070
  function checkHooksRegistered() {
8791
- const settings = join8(homedir8(), ".claude", "settings.json");
8792
- if (!existsSync9(settings)) {
11071
+ const settings = join11(homedir9(), ".claude", "settings.json");
11072
+ if (!existsSync15(settings)) {
8793
11073
  return {
8794
11074
  name: "Status hooks registered in ~/.claude/settings.json",
8795
11075
  pass: false,
@@ -8797,7 +11077,7 @@ function checkHooksRegistered() {
8797
11077
  };
8798
11078
  }
8799
11079
  try {
8800
- const raw = readFileSync7(settings, "utf-8");
11080
+ const raw = readFileSync12(settings, "utf-8");
8801
11081
  const has = raw.includes("claudemesh hook ");
8802
11082
  return {
8803
11083
  name: "Status hooks registered in ~/.claude/settings.json",
@@ -8813,8 +11093,8 @@ function checkHooksRegistered() {
8813
11093
  }
8814
11094
  }
8815
11095
  function checkConfigFile() {
8816
- const path = getConfigPath();
8817
- if (!existsSync9(path)) {
11096
+ const path2 = getConfigPath();
11097
+ if (!existsSync15(path2)) {
8818
11098
  return {
8819
11099
  name: "~/.claudemesh/config.json exists and parses",
8820
11100
  pass: true,
@@ -8823,14 +11103,14 @@ function checkConfigFile() {
8823
11103
  }
8824
11104
  try {
8825
11105
  readConfig();
8826
- const st = statSync2(path);
11106
+ const st = statSync2(path2);
8827
11107
  const mode = (st.mode & 511).toString(8);
8828
11108
  const secure = platform6() === "win32" || mode === "600";
8829
11109
  return {
8830
11110
  name: "~/.claudemesh/config.json parses + chmod 0600",
8831
11111
  pass: secure,
8832
11112
  detail: platform6() === "win32" ? "chmod skipped on Windows" : `0${mode}`,
8833
- fix: secure ? undefined : `chmod 600 ${path}`
11113
+ fix: secure ? undefined : `chmod 600 ${path2}`
8834
11114
  };
8835
11115
  } catch (e) {
8836
11116
  return {
@@ -8886,8 +11166,8 @@ async function checkBrokerWs() {
8886
11166
  const wsUrl = URLS.BROKER;
8887
11167
  const start = Date.now();
8888
11168
  try {
8889
- const WebSocket2 = (await import("ws")).default;
8890
- const ws = new WebSocket2(wsUrl);
11169
+ const WebSocket3 = (await import("ws")).default;
11170
+ const ws = new WebSocket3(wsUrl);
8891
11171
  const result = await new Promise((resolve2) => {
8892
11172
  const timer = setTimeout(() => {
8893
11173
  try {
@@ -8995,14 +11275,14 @@ var init_doctor = __esm(() => {
8995
11275
  // src/commands/status.ts
8996
11276
  var exports_status = {};
8997
11277
  __export(exports_status, {
8998
- runStatus: () => runStatus
11278
+ runStatus: () => runStatus2
8999
11279
  });
9000
- import { statSync as statSync3, existsSync as existsSync10 } from "node:fs";
9001
- import WebSocket2 from "ws";
11280
+ import { statSync as statSync3, existsSync as existsSync16 } from "node:fs";
11281
+ import WebSocket3 from "ws";
9002
11282
  async function probeBroker(url, timeoutMs = 4000) {
9003
11283
  return new Promise((resolve2) => {
9004
11284
  const started = Date.now();
9005
- const ws = new WebSocket2(url);
11285
+ const ws = new WebSocket3(url);
9006
11286
  const timer = setTimeout(() => {
9007
11287
  try {
9008
11288
  ws.terminate();
@@ -9023,11 +11303,11 @@ async function probeBroker(url, timeoutMs = 4000) {
9023
11303
  });
9024
11304
  });
9025
11305
  }
9026
- async function runStatus() {
11306
+ async function runStatus2() {
9027
11307
  render.section(`status (v${VERSION})`);
9028
11308
  const configPath = getConfigPath();
9029
11309
  let configPermsNote = "missing";
9030
- if (existsSync10(configPath)) {
11310
+ if (existsSync16(configPath)) {
9031
11311
  const mode = (statSync3(configPath).mode & 511).toString(8).padStart(4, "0");
9032
11312
  configPermsNote = mode === "0600" ? `${mode}` : `${mode} — expected 0600`;
9033
11313
  }
@@ -9173,13 +11453,13 @@ var init_check_claude_binary = __esm(() => {
9173
11453
  });
9174
11454
 
9175
11455
  // src/services/health/check-mcp-registered.ts
9176
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
11456
+ import { existsSync as existsSync17, readFileSync as readFileSync13 } from "node:fs";
9177
11457
  function checkMcpRegistered2() {
9178
11458
  try {
9179
- if (!existsSync11(PATHS.CLAUDE_JSON)) {
11459
+ if (!existsSync17(PATHS.CLAUDE_JSON)) {
9180
11460
  return { name: "mcp-registered", ok: false, message: "~/.claude.json not found" };
9181
11461
  }
9182
- const raw = readFileSync8(PATHS.CLAUDE_JSON, "utf-8");
11462
+ const raw = readFileSync13(PATHS.CLAUDE_JSON, "utf-8");
9183
11463
  const config = JSON.parse(raw);
9184
11464
  if (config.mcpServers && "claudemesh" in config.mcpServers) {
9185
11465
  return { name: "mcp-registered", ok: true, message: "MCP server registered" };
@@ -9194,13 +11474,13 @@ var init_check_mcp_registered = __esm(() => {
9194
11474
  });
9195
11475
 
9196
11476
  // src/services/health/check-hooks-registered.ts
9197
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
11477
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
9198
11478
  function checkHooksRegistered2() {
9199
11479
  try {
9200
- if (!existsSync12(PATHS.CLAUDE_SETTINGS)) {
11480
+ if (!existsSync18(PATHS.CLAUDE_SETTINGS)) {
9201
11481
  return { name: "hooks-registered", ok: false, message: "~/.claude/settings.json not found" };
9202
11482
  }
9203
- const raw = readFileSync9(PATHS.CLAUDE_SETTINGS, "utf-8");
11483
+ const raw = readFileSync14(PATHS.CLAUDE_SETTINGS, "utf-8");
9204
11484
  const config = JSON.parse(raw);
9205
11485
  if (config.hooks) {
9206
11486
  return { name: "hooks-registered", ok: true, message: "Hooks configured" };
@@ -9215,10 +11495,10 @@ var init_check_hooks_registered = __esm(() => {
9215
11495
  });
9216
11496
 
9217
11497
  // src/services/health/check-config-perms.ts
9218
- import { existsSync as existsSync13, statSync as statSync4 } from "node:fs";
11498
+ import { existsSync as existsSync19, statSync as statSync4 } from "node:fs";
9219
11499
  function checkConfigPerms() {
9220
11500
  const configFile = PATHS.CONFIG_FILE;
9221
- if (!existsSync13(configFile)) {
11501
+ if (!existsSync19(configFile)) {
9222
11502
  return { name: "config-perms", ok: true, message: "No config file yet (first run)" };
9223
11503
  }
9224
11504
  try {
@@ -9236,13 +11516,13 @@ var init_check_config_perms = __esm(() => {
9236
11516
  });
9237
11517
 
9238
11518
  // src/services/health/check-keypairs-valid.ts
9239
- import { existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
11519
+ import { existsSync as existsSync20, readFileSync as readFileSync15 } from "node:fs";
9240
11520
  function checkKeypairsValid() {
9241
- if (!existsSync14(PATHS.CONFIG_FILE)) {
11521
+ if (!existsSync20(PATHS.CONFIG_FILE)) {
9242
11522
  return { name: "keypairs-valid", ok: true, message: "No config (first run)" };
9243
11523
  }
9244
11524
  try {
9245
- const raw = readFileSync10(PATHS.CONFIG_FILE, "utf-8");
11525
+ const raw = readFileSync15(PATHS.CONFIG_FILE, "utf-8");
9246
11526
  const config = JSON.parse(raw);
9247
11527
  const meshes = config.meshes ?? [];
9248
11528
  if (meshes.length === 0) {
@@ -9643,12 +11923,12 @@ var exports_verify = {};
9643
11923
  __export(exports_verify, {
9644
11924
  runVerify: () => runVerify
9645
11925
  });
9646
- import { createHash } from "node:crypto";
11926
+ import { createHash as createHash3 } from "node:crypto";
9647
11927
  function safetyNumber(myPubkey, peerPubkey) {
9648
11928
  const a = Buffer.from(myPubkey, "hex");
9649
11929
  const b = Buffer.from(peerPubkey, "hex");
9650
11930
  const [lo, hi] = Buffer.compare(a, b) < 0 ? [a, b] : [b, a];
9651
- const hash = createHash("sha256").update(lo).update(hi).digest();
11931
+ const hash = createHash3("sha256").update(lo).update(hi).digest();
9652
11932
  const bits = [];
9653
11933
  for (let i = 0;i < 15; i++) {
9654
11934
  for (let b2 = 7;b2 >= 0; b2--) {
@@ -9722,19 +12002,19 @@ var exports_url_handler = {};
9722
12002
  __export(exports_url_handler, {
9723
12003
  runUrlHandler: () => runUrlHandler
9724
12004
  });
9725
- import { platform as platform7, homedir as homedir9 } from "node:os";
9726
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync8, rmSync as rmSync3, chmodSync as chmodSync4 } from "node:fs";
9727
- 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";
9728
12008
  import { spawnSync as spawnSync5 } from "node:child_process";
9729
12009
  function resolveClaudemeshBin() {
9730
12010
  return process.argv[1] ?? "claudemesh";
9731
12011
  }
9732
- function installDarwin() {
12012
+ function installDarwin2() {
9733
12013
  const binPath = resolveClaudemeshBin();
9734
- const appDir = join9(homedir9(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
9735
- const contents = join9(appDir, "Contents");
9736
- const macOS = join9(contents, "MacOS");
9737
- 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 });
9738
12018
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
9739
12019
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
9740
12020
  <plist version="1.0">
@@ -9757,7 +12037,7 @@ function installDarwin() {
9757
12037
  </array>
9758
12038
  </dict>
9759
12039
  </plist>`;
9760
- writeFileSync8(join9(contents, "Info.plist"), plist);
12040
+ writeFileSync12(join12(contents, "Info.plist"), plist);
9761
12041
  const shim = `#!/bin/sh
9762
12042
  URL="$1"
9763
12043
  CODE=\${URL#claudemesh://}
@@ -9771,9 +12051,9 @@ tell application "Terminal"
9771
12051
  end tell
9772
12052
  EOF
9773
12053
  `;
9774
- const shimPath = join9(macOS, "open-url");
9775
- writeFileSync8(shimPath, shim);
9776
- chmodSync4(shimPath, 493);
12054
+ const shimPath = join12(macOS, "open-url");
12055
+ writeFileSync12(shimPath, shim);
12056
+ chmodSync5(shimPath, 493);
9777
12057
  const lsreg = spawnSync5("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", ["-f", appDir], { encoding: "utf-8" });
9778
12058
  if (lsreg.status !== 0) {
9779
12059
  render.warn("lsregister returned non-zero", "scheme may not activate until Finder rescans.");
@@ -9781,10 +12061,10 @@ EOF
9781
12061
  render.ok("registered claudemesh:// scheme on macOS", dim(appDir));
9782
12062
  return EXIT.SUCCESS;
9783
12063
  }
9784
- function installLinux() {
12064
+ function installLinux2() {
9785
12065
  const binPath = resolveClaudemeshBin();
9786
- const appsDir = join9(homedir9(), ".local", "share", "applications");
9787
- mkdirSync5(appsDir, { recursive: true });
12066
+ const appsDir = join12(homedir10(), ".local", "share", "applications");
12067
+ mkdirSync9(appsDir, { recursive: true });
9788
12068
  const desktop = `[Desktop Entry]
9789
12069
  Type=Application
9790
12070
  Name=Claudemesh
@@ -9795,8 +12075,8 @@ Terminal=true
9795
12075
  MimeType=x-scheme-handler/claudemesh;
9796
12076
  NoDisplay=true
9797
12077
  `;
9798
- const desktopPath = join9(appsDir, "claudemesh.desktop");
9799
- writeFileSync8(desktopPath, desktop);
12078
+ const desktopPath = join12(appsDir, "claudemesh.desktop");
12079
+ writeFileSync12(desktopPath, desktop);
9800
12080
  const xdg1 = spawnSync5("xdg-mime", ["default", "claudemesh.desktop", "x-scheme-handler/claudemesh"], { encoding: "utf-8" });
9801
12081
  if (xdg1.status !== 0) {
9802
12082
  render.warn("xdg-mime not available — skipped mime default registration");
@@ -9818,8 +12098,8 @@ function installWindows() {
9818
12098
  `[HKEY_CURRENT_USER\\Software\\Classes\\claudemesh\\shell\\open\\command]`,
9819
12099
  `@="\\"${binPath.replace(/\\/g, "\\\\")}\\" \\"%1\\""`
9820
12100
  ];
9821
- const regPath = join9(homedir9(), "claudemesh-handler.reg");
9822
- writeFileSync8(regPath, lines.join(`\r
12101
+ const regPath = join12(homedir10(), "claudemesh-handler.reg");
12102
+ writeFileSync12(regPath, lines.join(`\r
9823
12103
  `));
9824
12104
  const res = spawnSync5("reg.exe", ["import", regPath], { encoding: "utf-8" });
9825
12105
  if (res.status !== 0) {
@@ -9830,15 +12110,15 @@ function installWindows() {
9830
12110
  return EXIT.SUCCESS;
9831
12111
  }
9832
12112
  function uninstallDarwin() {
9833
- const appDir = join9(homedir9(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
9834
- if (existsSync15(appDir))
12113
+ const appDir = join12(homedir10(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
12114
+ if (existsSync21(appDir))
9835
12115
  rmSync3(appDir, { recursive: true, force: true });
9836
12116
  render.ok("removed claudemesh:// handler on macOS");
9837
12117
  return EXIT.SUCCESS;
9838
12118
  }
9839
12119
  function uninstallLinux() {
9840
- const desktopPath = join9(homedir9(), ".local", "share", "applications", "claudemesh.desktop");
9841
- if (existsSync15(desktopPath))
12120
+ const desktopPath = join12(homedir10(), ".local", "share", "applications", "claudemesh.desktop");
12121
+ if (existsSync21(desktopPath))
9842
12122
  rmSync3(desktopPath, { force: true });
9843
12123
  render.ok("removed claudemesh:// handler on Linux");
9844
12124
  return EXIT.SUCCESS;
@@ -9853,9 +12133,9 @@ async function runUrlHandler(action) {
9853
12133
  const p = platform7();
9854
12134
  if (act === "install") {
9855
12135
  if (p === "darwin")
9856
- return installDarwin();
12136
+ return installDarwin2();
9857
12137
  if (p === "linux")
9858
- return installLinux();
12138
+ return installLinux2();
9859
12139
  if (p === "win32")
9860
12140
  return installWindows();
9861
12141
  } else if (act === "uninstall" || act === "remove") {
@@ -9883,9 +12163,9 @@ var exports_status_line = {};
9883
12163
  __export(exports_status_line, {
9884
12164
  runStatusLine: () => runStatusLine
9885
12165
  });
9886
- import { existsSync as existsSync16, readFileSync as readFileSync11 } from "node:fs";
9887
- import { join as join10 } from "node:path";
9888
- 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";
9889
12169
  async function runStatusLine() {
9890
12170
  try {
9891
12171
  const config = readConfig();
@@ -9893,11 +12173,11 @@ async function runStatusLine() {
9893
12173
  process.stdout.write("◇ claudemesh (not joined)");
9894
12174
  return EXIT.SUCCESS;
9895
12175
  }
9896
- const cachePath = join10(homedir10(), ".claudemesh", "peer-cache.json");
12176
+ const cachePath = join13(homedir11(), ".claudemesh", "peer-cache.json");
9897
12177
  let cache = {};
9898
- if (existsSync16(cachePath)) {
12178
+ if (existsSync22(cachePath)) {
9899
12179
  try {
9900
- cache = JSON.parse(readFileSync11(cachePath, "utf-8"));
12180
+ cache = JSON.parse(readFileSync16(cachePath, "utf-8"));
9901
12181
  } catch {}
9902
12182
  }
9903
12183
  const pick = config.meshes[0];
@@ -9928,7 +12208,7 @@ __export(exports_backup, {
9928
12208
  runRestore: () => runRestore,
9929
12209
  runBackup: () => runBackup
9930
12210
  });
9931
- 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";
9932
12212
  import { createInterface as createInterface11 } from "node:readline";
9933
12213
  function readHidden(prompt5) {
9934
12214
  return new Promise((resolve2) => {
@@ -9970,11 +12250,11 @@ async function deriveKey(pass, salt, s) {
9970
12250
  }
9971
12251
  async function runBackup(outPath) {
9972
12252
  const configPath = getConfigPath();
9973
- if (!existsSync17(configPath)) {
12253
+ if (!existsSync23(configPath)) {
9974
12254
  console.error(" No config found — nothing to back up. Join a mesh first.");
9975
12255
  return EXIT.NOT_FOUND;
9976
12256
  }
9977
- const plaintext = readFileSync12(configPath);
12257
+ const plaintext = readFileSync17(configPath);
9978
12258
  const pass = await readHidden(" Passphrase (min 12 chars): ");
9979
12259
  if (pass.length < 12) {
9980
12260
  console.error(" ✗ Passphrase too short.");
@@ -9992,7 +12272,7 @@ async function runBackup(outPath) {
9992
12272
  const ciphertext = Buffer.from(s.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, null, null, nonce, key));
9993
12273
  const blob = Buffer.concat([MAGIC, salt, nonce, ciphertext]);
9994
12274
  const file = outPath ?? `claudemesh-backup-${new Date().toISOString().replace(/[:.]/g, "-")}.cmb`;
9995
- writeFileSync9(file, blob, { mode: 384 });
12275
+ writeFileSync13(file, blob, { mode: 384 });
9996
12276
  console.log(`
9997
12277
  ✓ Backup saved: ${file}`);
9998
12278
  console.log(` Size: ${blob.length} bytes. Guard the passphrase — there is no recovery.
@@ -10004,11 +12284,11 @@ async function runRestore(inPath) {
10004
12284
  console.error(" Usage: claudemesh restore <backup-file>");
10005
12285
  return EXIT.INVALID_ARGS;
10006
12286
  }
10007
- if (!existsSync17(inPath)) {
12287
+ if (!existsSync23(inPath)) {
10008
12288
  console.error(` ✗ File not found: ${inPath}`);
10009
12289
  return EXIT.NOT_FOUND;
10010
12290
  }
10011
- const blob = readFileSync12(inPath);
12291
+ const blob = readFileSync17(inPath);
10012
12292
  if (blob.length < 4 + 16 + 24 + 17 || !blob.subarray(0, 4).equals(MAGIC)) {
10013
12293
  console.error(" ✗ Not a claudemesh backup file (bad magic).");
10014
12294
  return EXIT.INVALID_ARGS;
@@ -10027,12 +12307,12 @@ async function runRestore(inPath) {
10027
12307
  return EXIT.INTERNAL_ERROR;
10028
12308
  }
10029
12309
  const configPath = getConfigPath();
10030
- if (existsSync17(configPath)) {
12310
+ if (existsSync23(configPath)) {
10031
12311
  const backupOld = `${configPath}.before-restore.${Date.now()}`;
10032
- writeFileSync9(backupOld, readFileSync12(configPath), { mode: 384 });
12312
+ writeFileSync13(backupOld, readFileSync17(configPath), { mode: 384 });
10033
12313
  console.log(` ↻ Existing config saved to ${backupOld}`);
10034
12314
  }
10035
- writeFileSync9(configPath, Buffer.from(plaintext), { mode: 384 });
12315
+ writeFileSync13(configPath, Buffer.from(plaintext), { mode: 384 });
10036
12316
  console.log(`
10037
12317
  ✓ Config restored to ${configPath}`);
10038
12318
  console.log(" Run `claudemesh list` to verify your meshes.\n");
@@ -10052,8 +12332,8 @@ __export(exports_upgrade, {
10052
12332
  runUpgrade: () => runUpgrade
10053
12333
  });
10054
12334
  import { spawnSync as spawnSync6 } from "node:child_process";
10055
- import { existsSync as existsSync18 } from "node:fs";
10056
- 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";
10057
12337
  async function latestVersion() {
10058
12338
  try {
10059
12339
  const res = await fetch(URLS.NPM_REGISTRY, { signal: AbortSignal.timeout(8000) });
@@ -10066,15 +12346,15 @@ async function latestVersion() {
10066
12346
  }
10067
12347
  }
10068
12348
  function findNpm() {
10069
- const portable = join11(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
10070
- if (existsSync18(portable)) {
10071
- 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") };
10072
12352
  }
10073
12353
  let cur = resolve2(process.argv[1] ?? ".");
10074
12354
  for (let i = 0;i < 6; i++) {
10075
- cur = dirname5(cur);
10076
- const candidate = join11(cur, "bin", "npm");
10077
- if (existsSync18(candidate))
12355
+ cur = dirname8(cur);
12356
+ const candidate = join14(cur, "bin", "npm");
12357
+ if (existsSync24(candidate))
10078
12358
  return { npm: candidate };
10079
12359
  }
10080
12360
  return { npm: "npm" };
@@ -10136,9 +12416,9 @@ __export(exports_grants, {
10136
12416
  runBlock: () => runBlock,
10137
12417
  isAllowed: () => isAllowed
10138
12418
  });
10139
- import { existsSync as existsSync19, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "node:fs";
10140
- import { homedir as homedir11 } from "node:os";
10141
- 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";
10142
12422
  async function syncToBroker(meshSlug, grants) {
10143
12423
  const auth = getStoredToken();
10144
12424
  if (!auth)
@@ -10156,19 +12436,19 @@ async function syncToBroker(meshSlug, grants) {
10156
12436
  }
10157
12437
  }
10158
12438
  function readGrants() {
10159
- if (!existsSync19(GRANT_FILE))
12439
+ if (!existsSync25(GRANT_FILE))
10160
12440
  return {};
10161
12441
  try {
10162
- return JSON.parse(readFileSync13(GRANT_FILE, "utf-8"));
12442
+ return JSON.parse(readFileSync18(GRANT_FILE, "utf-8"));
10163
12443
  } catch {
10164
12444
  return {};
10165
12445
  }
10166
12446
  }
10167
12447
  function writeGrants(g) {
10168
- const dir = join12(homedir11(), ".claudemesh");
10169
- if (!existsSync19(dir))
10170
- mkdirSync6(dir, { recursive: true });
10171
- 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 });
10172
12452
  }
10173
12453
  function resolveCaps(input) {
10174
12454
  if (input.includes("all"))
@@ -10324,7 +12604,7 @@ var init_grants = __esm(() => {
10324
12604
  BROKER_HTTP7 = URLS.BROKER.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
10325
12605
  ALL_CAPS = ["read", "dm", "broadcast", "state-read", "state-write", "file-read"];
10326
12606
  DEFAULT_CAPS = ["read", "dm", "broadcast", "state-read"];
10327
- GRANT_FILE = join12(homedir11(), ".claudemesh", "grants.json");
12607
+ GRANT_FILE = join15(homedir12(), ".claudemesh", "grants.json");
10328
12608
  });
10329
12609
 
10330
12610
  // src/commands/profile.ts
@@ -11803,8 +14083,8 @@ __export(exports_file, {
11803
14083
  runFileGet: () => runFileGet
11804
14084
  });
11805
14085
  import { hostname as osHostname } from "node:os";
11806
- import { resolve as resolvePath, basename, dirname as dirname6 } from "node:path";
11807
- 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";
11808
14088
  function emitJson2(data) {
11809
14089
  console.log(JSON.stringify(data, null, 2));
11810
14090
  }
@@ -11821,7 +14101,7 @@ async function runFileShare(filePath, opts) {
11821
14101
  return EXIT.INVALID_ARGS;
11822
14102
  }
11823
14103
  const absPath = resolvePath(filePath);
11824
- if (!existsSync20(absPath)) {
14104
+ if (!existsSync26(absPath)) {
11825
14105
  render.err(`File not found: ${absPath}`);
11826
14106
  return EXIT.INVALID_ARGS;
11827
14107
  }
@@ -11900,8 +14180,8 @@ async function runFileGet(fileId, opts) {
11900
14180
  }
11901
14181
  const buf = Buffer.from(await res.arrayBuffer());
11902
14182
  const outPath = opts.out ? resolvePath(opts.out) : resolvePath(process.cwd(), meta.name);
11903
- mkdirSync7(dirname6(outPath), { recursive: true });
11904
- writeFileSync11(outPath, buf);
14183
+ mkdirSync11(dirname9(outPath), { recursive: true });
14184
+ writeFileSync15(outPath, buf);
11905
14185
  if (opts.json) {
11906
14186
  emitJson2({ fileId, name: meta.name, savedTo: outPath, sizeBytes: buf.length });
11907
14187
  } else {
@@ -11996,7 +14276,7 @@ var require_client = __commonJS((exports) => {
11996
14276
  var ws_1 = __importDefault(__require("ws"));
11997
14277
  var crypto_js_1 = require_crypto();
11998
14278
  var MAX_QUEUED2 = 100;
11999
- var HELLO_ACK_TIMEOUT_MS2 = 5000;
14279
+ var HELLO_ACK_TIMEOUT_MS3 = 5000;
12000
14280
  var BACKOFF_CAPS2 = [1000, 2000, 4000, 8000, 16000, 30000];
12001
14281
 
12002
14282
  class MeshClient extends node_events_1.EventEmitter {
@@ -12061,7 +14341,7 @@ var require_client = __commonJS((exports) => {
12061
14341
  this.debug("hello_ack timeout");
12062
14342
  ws.close();
12063
14343
  reject(new Error("hello_ack timeout"));
12064
- }, HELLO_ACK_TIMEOUT_MS2);
14344
+ }, HELLO_ACK_TIMEOUT_MS3);
12065
14345
  };
12066
14346
  const onMessage = (raw) => {
12067
14347
  let msg;
@@ -12511,7 +14791,7 @@ __export(exports_bridge, {
12511
14791
  runBridge: () => runBridge,
12512
14792
  bridgeConfigTemplate: () => bridgeConfigTemplate
12513
14793
  });
12514
- import { readFileSync as readFileSync14, existsSync as existsSync21 } from "node:fs";
14794
+ import { readFileSync as readFileSync19, existsSync as existsSync27 } from "node:fs";
12515
14795
  function parseConfig(text) {
12516
14796
  const trimmed = text.trim();
12517
14797
  if (trimmed.startsWith("{"))
@@ -12555,13 +14835,13 @@ async function runBridge(configPath) {
12555
14835
  render.err("Usage: claudemesh bridge run <config.yaml>");
12556
14836
  return EXIT.INVALID_ARGS;
12557
14837
  }
12558
- if (!existsSync21(configPath)) {
14838
+ if (!existsSync27(configPath)) {
12559
14839
  render.err(`config file not found: ${configPath}`);
12560
14840
  return EXIT.NOT_FOUND;
12561
14841
  }
12562
14842
  let cfg;
12563
14843
  try {
12564
- cfg = parseConfig(readFileSync14(configPath, "utf-8"));
14844
+ cfg = parseConfig(readFileSync19(configPath, "utf-8"));
12565
14845
  } catch (e) {
12566
14846
  render.err(`failed to parse ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
12567
14847
  return EXIT.INVALID_ARGS;
@@ -12965,9 +15245,9 @@ function cacheKey(apiKeySecret, topicName) {
12965
15245
  async function getTopicKey(args) {
12966
15246
  const cacheId = cacheKey(args.apiKeySecret, args.topicName);
12967
15247
  if (!args.fresh) {
12968
- const cached = cache.get(cacheId);
12969
- if (cached)
12970
- return { ok: true, topicKey: cached.topicKey };
15248
+ const cached2 = cache.get(cacheId);
15249
+ if (cached2)
15250
+ return { ok: true, topicKey: cached2.topicKey };
12971
15251
  }
12972
15252
  let sealed;
12973
15253
  try {
@@ -13531,8 +15811,8 @@ var init_definitions = __esm(() => {
13531
15811
  });
13532
15812
 
13533
15813
  // src/services/bridge/server.ts
13534
- import { createServer as createServer2 } from "node:net";
13535
- 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";
13536
15816
  async function resolveTarget2(client, to) {
13537
15817
  if (to.startsWith("@") || to === "*" || /^[0-9a-f]{64}$/i.test(to)) {
13538
15818
  return { ok: true, spec: to };
@@ -13644,21 +15924,21 @@ function handleConnection(socket, client) {
13644
15924
  socket.on("error", () => {});
13645
15925
  }
13646
15926
  function startBridgeServer(client) {
13647
- const path = socketPath(client.meshSlug);
15927
+ const path2 = socketPath(client.meshSlug);
13648
15928
  const dir = socketDir();
13649
- if (!existsSync22(dir)) {
13650
- mkdirSync8(dir, { recursive: true, mode: 448 });
15929
+ if (!existsSync28(dir)) {
15930
+ mkdirSync12(dir, { recursive: true, mode: 448 });
13651
15931
  }
13652
- if (existsSync22(path)) {
15932
+ if (existsSync28(path2)) {
13653
15933
  try {
13654
- unlinkSync2(path);
15934
+ unlinkSync5(path2);
13655
15935
  } catch {}
13656
15936
  }
13657
- const server = createServer2((socket) => handleConnection(socket, client));
15937
+ const server = createServer3((socket) => handleConnection(socket, client));
13658
15938
  try {
13659
- server.listen(path);
15939
+ server.listen(path2);
13660
15940
  } catch (err) {
13661
- process.stderr.write(`[claudemesh] bridge: failed to bind ${path}: ${String(err)}
15941
+ process.stderr.write(`[claudemesh] bridge: failed to bind ${path2}: ${String(err)}
13662
15942
  `);
13663
15943
  return null;
13664
15944
  }
@@ -13667,11 +15947,11 @@ function startBridgeServer(client) {
13667
15947
  `);
13668
15948
  });
13669
15949
  try {
13670
- chmodSync5(path, 384);
15950
+ chmodSync6(path2, 384);
13671
15951
  } catch {}
13672
15952
  let stopped = false;
13673
15953
  return {
13674
- path,
15954
+ path: path2,
13675
15955
  stop() {
13676
15956
  if (stopped)
13677
15957
  return;
@@ -13680,12 +15960,12 @@ function startBridgeServer(client) {
13680
15960
  server.close();
13681
15961
  } catch {}
13682
15962
  try {
13683
- unlinkSync2(path);
15963
+ unlinkSync5(path2);
13684
15964
  } catch {}
13685
15965
  }
13686
15966
  };
13687
15967
  }
13688
- var init_server = __esm(() => {
15968
+ var init_server2 = __esm(() => {
13689
15969
  init_protocol();
13690
15970
  });
13691
15971
 
@@ -14256,9 +16536,9 @@ async function startServiceProxy(serviceName) {
14256
16536
  const fetched = await client.getServiceTools(serviceName);
14257
16537
  tools = fetched;
14258
16538
  } catch {
14259
- const cached = client.serviceCatalog.find((s) => s.name === serviceName);
14260
- if (cached) {
14261
- tools = cached.tools;
16539
+ const cached2 = client.serviceCatalog.find((s) => s.name === serviceName);
16540
+ if (cached2) {
16541
+ tools = cached2.tools;
14262
16542
  }
14263
16543
  }
14264
16544
  if (tools.length === 0) {
@@ -14347,11 +16627,11 @@ async function startServiceProxy(serviceName) {
14347
16627
  process.on("SIGINT", shutdown);
14348
16628
  }
14349
16629
  var peerNameCache, peerNameCacheAge = 0, CACHE_TTL_MS = 30000;
14350
- var init_server2 = __esm(() => {
16630
+ var init_server3 = __esm(() => {
14351
16631
  init_definitions();
14352
16632
  init_facade();
14353
16633
  init_facade8();
14354
- init_server();
16634
+ init_server2();
14355
16635
  peerNameCache = new Map;
14356
16636
  });
14357
16637
 
@@ -14367,7 +16647,7 @@ async function runMcp() {
14367
16647
  process.exit(0);
14368
16648
  }
14369
16649
  var init_mcp = __esm(() => {
14370
- init_server2();
16650
+ init_server3();
14371
16651
  });
14372
16652
 
14373
16653
  // src/commands/hook.ts
@@ -15468,6 +17748,23 @@ async function main() {
15468
17748
  process.exit(await whoami2({ json: !!flags.json }));
15469
17749
  break;
15470
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
+ }
15471
17768
  case "install": {
15472
17769
  const { runInstall: runInstall2 } = await Promise.resolve().then(() => (init_install(), exports_install));
15473
17770
  runInstall2(positionals);
@@ -15488,8 +17785,8 @@ async function main() {
15488
17785
  const { runStatusSet: runStatusSet2 } = await Promise.resolve().then(() => (init_broker_actions(), exports_broker_actions));
15489
17786
  process.exit(await runStatusSet2(positionals[1] ?? "", { mesh: flags.mesh, json: !!flags.json }));
15490
17787
  } else {
15491
- const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), exports_status));
15492
- await runStatus2();
17788
+ const { runStatus: runStatus3 } = await Promise.resolve().then(() => (init_status(), exports_status));
17789
+ await runStatus3();
15493
17790
  }
15494
17791
  break;
15495
17792
  }
@@ -15641,8 +17938,8 @@ async function main() {
15641
17938
  const { runStatusSet: runStatusSet2 } = await Promise.resolve().then(() => (init_broker_actions(), exports_broker_actions));
15642
17939
  process.exit(await runStatusSet2(positionals[2] ?? "", { mesh: flags.mesh, json: !!flags.json }));
15643
17940
  } else {
15644
- const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), exports_status));
15645
- await runStatus2();
17941
+ const { runStatus: runStatus3 } = await Promise.resolve().then(() => (init_status(), exports_status));
17942
+ await runStatus3();
15646
17943
  }
15647
17944
  } else {
15648
17945
  console.error("Usage: claudemesh profile [summary|visible|status]");
@@ -16129,4 +18426,4 @@ main().catch((err) => {
16129
18426
  process.exit(EXIT.INTERNAL_ERROR);
16130
18427
  });
16131
18428
 
16132
- //# debugId=D2E0E354A78D4E1B64756E2164756E21
18429
+ //# debugId=BA4EDB138BD2E1AE64756E2164756E21