claudemesh-cli 1.34.7 → 1.34.8
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.
- package/dist/entrypoints/cli.js +184 -19
- package/dist/entrypoints/cli.js.map +13 -12
- package/dist/entrypoints/mcp.js +43 -6
- package/dist/entrypoints/mcp.js.map +3 -3
- package/package.json +1 -1
- package/skills/claudemesh/SKILL.md +8 -1
package/dist/entrypoints/cli.js
CHANGED
|
@@ -104,7 +104,7 @@ __export(exports_urls, {
|
|
|
104
104
|
VERSION: () => VERSION,
|
|
105
105
|
URLS: () => URLS
|
|
106
106
|
});
|
|
107
|
-
var URLS, VERSION = "1.34.
|
|
107
|
+
var URLS, VERSION = "1.34.8", env;
|
|
108
108
|
var init_urls = __esm(() => {
|
|
109
109
|
URLS = {
|
|
110
110
|
BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
|
|
@@ -4052,11 +4052,18 @@ async function tryListPeersViaDaemon(mesh) {
|
|
|
4052
4052
|
return null;
|
|
4053
4053
|
}
|
|
4054
4054
|
}
|
|
4055
|
-
async function tryListInboxViaDaemon(mesh, limit = 100) {
|
|
4055
|
+
async function tryListInboxViaDaemon(mesh, limit = 100, opts = {}) {
|
|
4056
4056
|
if (!await daemonReachable())
|
|
4057
4057
|
return null;
|
|
4058
4058
|
try {
|
|
4059
|
-
const
|
|
4059
|
+
const params = [`limit=${limit}`];
|
|
4060
|
+
if (mesh)
|
|
4061
|
+
params.push(`mesh=${encodeURIComponent(mesh)}`);
|
|
4062
|
+
if (opts.unreadOnly)
|
|
4063
|
+
params.push("unread_only=true");
|
|
4064
|
+
if (opts.markSeen === false)
|
|
4065
|
+
params.push("mark_seen=false");
|
|
4066
|
+
const path = `/v1/inbox?${params.join("&")}`;
|
|
4060
4067
|
const res = await ipc({ path, timeoutMs: 3000 });
|
|
4061
4068
|
if (res.status !== 200)
|
|
4062
4069
|
return null;
|
|
@@ -8380,7 +8387,10 @@ function formatMessage(msg, includeMesh) {
|
|
|
8380
8387
|
}
|
|
8381
8388
|
async function runInbox(flags) {
|
|
8382
8389
|
const meshSlug = flags.mesh;
|
|
8383
|
-
const items = await tryListInboxViaDaemon(meshSlug, flags.limit ?? 100
|
|
8390
|
+
const items = await tryListInboxViaDaemon(meshSlug, flags.limit ?? 100, {
|
|
8391
|
+
unreadOnly: flags.unread === true,
|
|
8392
|
+
markSeen: true
|
|
8393
|
+
});
|
|
8384
8394
|
if (items === null) {
|
|
8385
8395
|
if (flags.json) {
|
|
8386
8396
|
process.stdout.write(`[]
|
|
@@ -8397,10 +8407,12 @@ async function runInbox(flags) {
|
|
|
8397
8407
|
}
|
|
8398
8408
|
if (items.length === 0) {
|
|
8399
8409
|
const scope = meshSlug ? `mesh "${meshSlug}"` : "any mesh";
|
|
8400
|
-
|
|
8410
|
+
const filter = flags.unread ? "unread " : "";
|
|
8411
|
+
render.info(dim(`No ${filter}messages on ${scope}.`));
|
|
8401
8412
|
return;
|
|
8402
8413
|
}
|
|
8403
|
-
const
|
|
8414
|
+
const filterTag = flags.unread ? " unread" : "";
|
|
8415
|
+
const heading = meshSlug ? `inbox — ${meshSlug} (${items.length}${filterTag} message${items.length === 1 ? "" : "s"})` : `inbox (${items.length}${filterTag} message${items.length === 1 ? "" : "s"})`;
|
|
8404
8416
|
render.section(heading);
|
|
8405
8417
|
for (const msg of items) {
|
|
8406
8418
|
process.stdout.write(formatMessage(msg, !meshSlug) + `
|
|
@@ -9582,6 +9594,11 @@ function migrateInbox(db) {
|
|
|
9582
9594
|
CREATE INDEX IF NOT EXISTS inbox_topic ON inbox(topic);
|
|
9583
9595
|
CREATE INDEX IF NOT EXISTS inbox_sender ON inbox(sender_pubkey);
|
|
9584
9596
|
`);
|
|
9597
|
+
const cols = db.prepare(`PRAGMA table_info(inbox)`).all();
|
|
9598
|
+
if (!cols.some((c) => c.name === "seen_at")) {
|
|
9599
|
+
db.exec(`ALTER TABLE inbox ADD COLUMN seen_at INTEGER`);
|
|
9600
|
+
db.exec(`CREATE INDEX IF NOT EXISTS inbox_seen_at ON inbox(seen_at)`);
|
|
9601
|
+
}
|
|
9585
9602
|
}
|
|
9586
9603
|
function insertIfNew(db, row) {
|
|
9587
9604
|
const before = db.prepare(`SELECT id FROM inbox WHERE client_message_id = ?`).get(row.client_message_id);
|
|
@@ -9616,9 +9633,12 @@ function listInbox(db, p) {
|
|
|
9616
9633
|
where.push("mesh = ?");
|
|
9617
9634
|
args.push(p.mesh);
|
|
9618
9635
|
}
|
|
9636
|
+
if (p.unreadOnly === true) {
|
|
9637
|
+
where.push("seen_at IS NULL");
|
|
9638
|
+
}
|
|
9619
9639
|
const sql = `
|
|
9620
9640
|
SELECT id, client_message_id, broker_message_id, mesh, topic,
|
|
9621
|
-
sender_pubkey, sender_name, body, meta, received_at, reply_to_id
|
|
9641
|
+
sender_pubkey, sender_name, body, meta, received_at, reply_to_id, seen_at
|
|
9622
9642
|
FROM inbox
|
|
9623
9643
|
${where.length ? "WHERE " + where.join(" AND ") : ""}
|
|
9624
9644
|
ORDER BY received_at DESC
|
|
@@ -9627,6 +9647,17 @@ function listInbox(db, p) {
|
|
|
9627
9647
|
args.push(Math.min(Math.max(p.limit ?? 100, 1), 1000));
|
|
9628
9648
|
return db.prepare(sql).all(...args);
|
|
9629
9649
|
}
|
|
9650
|
+
function markInboxSeen(db, ids, now = Date.now()) {
|
|
9651
|
+
if (ids.length === 0)
|
|
9652
|
+
return 0;
|
|
9653
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
9654
|
+
const r = db.prepare(`UPDATE inbox SET seen_at = ? WHERE seen_at IS NULL AND id IN (${placeholders})`).run(now, ...ids);
|
|
9655
|
+
return Number(r.changes);
|
|
9656
|
+
}
|
|
9657
|
+
function pruneInboxBefore(db, cutoffMs) {
|
|
9658
|
+
const r = db.prepare(`DELETE FROM inbox WHERE received_at < ?`).run(cutoffMs);
|
|
9659
|
+
return Number(r.changes);
|
|
9660
|
+
}
|
|
9630
9661
|
function deleteInboxRow(db, id) {
|
|
9631
9662
|
const r = db.prepare(`DELETE FROM inbox WHERE id = ?`).run(id);
|
|
9632
9663
|
return Number(r.changes) > 0;
|
|
@@ -10387,13 +10418,23 @@ function makeHandler(opts) {
|
|
|
10387
10418
|
const limitRaw = url.searchParams.get("limit");
|
|
10388
10419
|
const limit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;
|
|
10389
10420
|
const meshFilter = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
|
|
10421
|
+
const unreadOnly = url.searchParams.get("unread_only") === "true";
|
|
10422
|
+
const markSeen = url.searchParams.get("mark_seen") !== "false";
|
|
10390
10423
|
const rows = listInbox(opts.inboxDb, {
|
|
10391
10424
|
since: Number.isFinite(since) ? since : undefined,
|
|
10392
10425
|
topic,
|
|
10393
10426
|
fromPubkey,
|
|
10394
10427
|
...meshFilter ? { mesh: meshFilter } : {},
|
|
10428
|
+
unreadOnly,
|
|
10395
10429
|
limit: Number.isFinite(limit ?? NaN) ? limit : undefined
|
|
10396
10430
|
});
|
|
10431
|
+
let flippedCount = 0;
|
|
10432
|
+
if (markSeen) {
|
|
10433
|
+
const unreadIds = rows.filter((r) => r.seen_at == null).map((r) => r.id);
|
|
10434
|
+
if (unreadIds.length > 0) {
|
|
10435
|
+
flippedCount = markInboxSeen(opts.inboxDb, unreadIds);
|
|
10436
|
+
}
|
|
10437
|
+
}
|
|
10397
10438
|
respond(res, 200, {
|
|
10398
10439
|
items: rows.map((r) => ({
|
|
10399
10440
|
id: r.id,
|
|
@@ -10405,11 +10446,32 @@ function makeHandler(opts) {
|
|
|
10405
10446
|
sender_name: r.sender_name,
|
|
10406
10447
|
body: r.body,
|
|
10407
10448
|
received_at: new Date(r.received_at).toISOString(),
|
|
10408
|
-
reply_to_id: r.reply_to_id
|
|
10409
|
-
|
|
10449
|
+
reply_to_id: r.reply_to_id,
|
|
10450
|
+
seen_at: r.seen_at ? new Date(r.seen_at).toISOString() : null
|
|
10451
|
+
})),
|
|
10452
|
+
marked_seen: flippedCount
|
|
10410
10453
|
});
|
|
10411
10454
|
return;
|
|
10412
10455
|
}
|
|
10456
|
+
if (req.method === "POST" && url.pathname === "/v1/inbox/seen") {
|
|
10457
|
+
if (!opts.inboxDb) {
|
|
10458
|
+
respond(res, 503, { error: "inbox not initialised" });
|
|
10459
|
+
return;
|
|
10460
|
+
}
|
|
10461
|
+
try {
|
|
10462
|
+
const body = await readJsonBody(req, 64 * 1024);
|
|
10463
|
+
const ids = Array.isArray(body?.ids) ? body.ids.filter((x) => typeof x === "string") : [];
|
|
10464
|
+
if (ids.length === 0) {
|
|
10465
|
+
respond(res, 400, { error: "missing 'ids' (string[])" });
|
|
10466
|
+
return;
|
|
10467
|
+
}
|
|
10468
|
+
const flipped = markInboxSeen(opts.inboxDb, ids);
|
|
10469
|
+
respond(res, 200, { marked_seen: flipped });
|
|
10470
|
+
} catch (e) {
|
|
10471
|
+
respond(res, 400, { error: String(e) });
|
|
10472
|
+
}
|
|
10473
|
+
return;
|
|
10474
|
+
}
|
|
10413
10475
|
if (req.method === "DELETE" && url.pathname === "/v1/inbox") {
|
|
10414
10476
|
if (!opts.inboxDb) {
|
|
10415
10477
|
respond(res, 503, { error: "inbox not initialised" });
|
|
@@ -11376,6 +11438,11 @@ class SessionBrokerClient {
|
|
|
11376
11438
|
return;
|
|
11377
11439
|
}
|
|
11378
11440
|
if (msg.type === "push" || msg.type === "inbound") {
|
|
11441
|
+
const senderPubkey = String(msg.senderPubkey ?? "").toLowerCase();
|
|
11442
|
+
if (senderPubkey && senderPubkey === this.opts.sessionPubkey.toLowerCase()) {
|
|
11443
|
+
this.log("info", "self_echo_dropped", { sender: senderPubkey.slice(0, 12) });
|
|
11444
|
+
return;
|
|
11445
|
+
}
|
|
11379
11446
|
this.opts.onPush?.(msg);
|
|
11380
11447
|
return;
|
|
11381
11448
|
}
|
|
@@ -11667,6 +11734,46 @@ function defaultLog4(level, msg, meta) {
|
|
|
11667
11734
|
var POLL_INTERVAL_MS = 500, MAX_ATTEMPTS_PER_ROW = 25, BACKOFF_BASE_MS = 500, BACKOFF_CAP_MS = 30000;
|
|
11668
11735
|
var init_drain = () => {};
|
|
11669
11736
|
|
|
11737
|
+
// src/daemon/inbox-pruner.ts
|
|
11738
|
+
function startInboxPruner(opts) {
|
|
11739
|
+
const retentionMs = opts.retentionMs ?? DEFAULT_RETENTION_MS;
|
|
11740
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
11741
|
+
const log2 = opts.log ?? defaultLog5;
|
|
11742
|
+
const tick = () => {
|
|
11743
|
+
try {
|
|
11744
|
+
const cutoff = Date.now() - retentionMs;
|
|
11745
|
+
const removed = pruneInboxBefore(opts.db, cutoff);
|
|
11746
|
+
if (removed > 0) {
|
|
11747
|
+
log2("info", "inbox_prune_completed", {
|
|
11748
|
+
removed,
|
|
11749
|
+
retention_days: Math.round(retentionMs / (24 * 60 * 60 * 1000))
|
|
11750
|
+
});
|
|
11751
|
+
}
|
|
11752
|
+
} catch (e) {
|
|
11753
|
+
log2("warn", "inbox_prune_failed", { err: String(e) });
|
|
11754
|
+
}
|
|
11755
|
+
};
|
|
11756
|
+
tick();
|
|
11757
|
+
const handle = setInterval(tick, intervalMs);
|
|
11758
|
+
if (typeof handle.unref === "function")
|
|
11759
|
+
handle.unref();
|
|
11760
|
+
return { stop: () => clearInterval(handle) };
|
|
11761
|
+
}
|
|
11762
|
+
function defaultLog5(level, msg, meta) {
|
|
11763
|
+
const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
|
|
11764
|
+
if (level === "info")
|
|
11765
|
+
process.stdout.write(line + `
|
|
11766
|
+
`);
|
|
11767
|
+
else
|
|
11768
|
+
process.stderr.write(line + `
|
|
11769
|
+
`);
|
|
11770
|
+
}
|
|
11771
|
+
var DEFAULT_RETENTION_MS, DEFAULT_INTERVAL_MS;
|
|
11772
|
+
var init_inbox_pruner = __esm(() => {
|
|
11773
|
+
DEFAULT_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
11774
|
+
DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
11775
|
+
});
|
|
11776
|
+
|
|
11670
11777
|
// src/daemon/inbound.ts
|
|
11671
11778
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
11672
11779
|
async function handleBrokerPush(msg, ctx) {
|
|
@@ -11983,6 +12090,12 @@ async function runDaemon(opts = {}) {
|
|
|
11983
12090
|
bus.publish("broker_status", { mesh: mesh.slug, status: s });
|
|
11984
12091
|
},
|
|
11985
12092
|
onPush: (m) => {
|
|
12093
|
+
const senderMemberPk = String(m.senderMemberPubkey ?? "").toLowerCase();
|
|
12094
|
+
const senderPubkey = String(m.senderPubkey ?? "").toLowerCase();
|
|
12095
|
+
const ownMember = mesh.pubkey.toLowerCase();
|
|
12096
|
+
if (senderMemberPk && senderMemberPk === ownMember && senderPubkey === ownMember) {
|
|
12097
|
+
return;
|
|
12098
|
+
}
|
|
11986
12099
|
handleBrokerPush(m, {
|
|
11987
12100
|
db: inboxDb,
|
|
11988
12101
|
bus,
|
|
@@ -12004,6 +12117,7 @@ async function runDaemon(opts = {}) {
|
|
|
12004
12117
|
brokers,
|
|
12005
12118
|
getSessionBrokerByPubkey: (pubkey) => sessionBrokersByPubkey.get(pubkey)
|
|
12006
12119
|
});
|
|
12120
|
+
const inboxPruner = startInboxPruner({ db: inboxDb });
|
|
12007
12121
|
setRegistryHooks({
|
|
12008
12122
|
onRegister: (info) => {
|
|
12009
12123
|
if (!info.presence)
|
|
@@ -12107,6 +12221,7 @@ async function runDaemon(opts = {}) {
|
|
|
12107
12221
|
shuttingDown = true;
|
|
12108
12222
|
process.stdout.write(JSON.stringify({ msg: "daemon_shutdown", signal: sig, ts: new Date().toISOString() }) + `
|
|
12109
12223
|
`);
|
|
12224
|
+
inboxPruner.stop();
|
|
12110
12225
|
if (drain)
|
|
12111
12226
|
await drain.close();
|
|
12112
12227
|
for (const b of brokers.values()) {
|
|
@@ -12143,6 +12258,7 @@ var init_run = __esm(() => {
|
|
|
12143
12258
|
init_broker();
|
|
12144
12259
|
init_session_broker();
|
|
12145
12260
|
init_drain();
|
|
12261
|
+
init_inbox_pruner();
|
|
12146
12262
|
init_inbound();
|
|
12147
12263
|
init_identity();
|
|
12148
12264
|
init_facade();
|
|
@@ -16270,7 +16386,8 @@ claudemesh message send <p> "..." --priority low # pull-only
|
|
|
16270
16386
|
claudemesh inbox # all attached meshes, last 100
|
|
16271
16387
|
claudemesh inbox --mesh <slug> # scoped to one mesh
|
|
16272
16388
|
claudemesh inbox --mesh <slug> --limit 20 # custom cap
|
|
16273
|
-
claudemesh inbox --json # full row (sender_pubkey, mesh, body, received_at, …)
|
|
16389
|
+
claudemesh inbox --json # full row (sender_pubkey, mesh, body, received_at, seen_at, …)
|
|
16390
|
+
claudemesh inbox --unread # 1.34.8+ only rows whose seen_at IS NULL
|
|
16274
16391
|
|
|
16275
16392
|
# inbox flush + delete — 1.34.7+
|
|
16276
16393
|
claudemesh inbox flush --mesh <slug> # delete all rows on one mesh
|
|
@@ -16286,6 +16403,12 @@ claudemesh message status <message-id> --json
|
|
|
16286
16403
|
|
|
16287
16404
|
**Inbox source (1.34.0+):** \`claudemesh inbox\` queries the daemon's persistent \`~/.claudemesh/daemon/inbox.db\` over IPC — it is NOT a fresh broker-WS buffer drain. Rows survive daemon restarts. Sender attribution is the actual session pubkey of the launched session that originated the send (NOT the stable member pubkey of the sender's daemon), so two sibling sessions of the same human appear as distinct rows.
|
|
16288
16405
|
|
|
16406
|
+
**Read-state (1.34.8+):** every inbox row carries a \`seen_at\` timestamp. \`null\` = never surfaced; an ISO string = first surfaced at that moment. The flag flips automatically when (a) the row is returned by an interactive \`claudemesh inbox\` listing, or (b) the MCP server emits a live \`<channel>\` reminder for it. The launch welcome push uses \`unread_only=true\` to surface only rows the user hasn't seen — so a session relaunched a day later sees what it actually missed, not the same 24h batch every time. Use \`claudemesh inbox --unread\` to get the same filter from the CLI.
|
|
16407
|
+
|
|
16408
|
+
**Self-echo guard (1.34.8+):** broker fan-out paths sometimes mirror an outbound DM back to the originating session-WS. The daemon now drops those at the WS boundary (matching on \`senderPubkey === own.session_pubkey\`), so the sender no longer sees their own \`claudemesh send\` arrive as a \`← claudemesh: <self>: ...\` channel push immediately after dispatching it.
|
|
16409
|
+
|
|
16410
|
+
**Inbox TTL (1.34.8+):** the daemon runs an hourly prune that deletes rows older than 30 days. Without this the inbox grew unbounded; now it self-trims while preserving "I went on holiday and want to see what I missed" recovery for a generous window. No CLI knob — it's a built-in retention policy. To override, manually \`claudemesh inbox flush --before <iso>\`.
|
|
16411
|
+
|
|
16289
16412
|
\`send\` JSON output: \`{"ok": true, "messageId": "...", "target": "..."}\`. Errors: \`{"ok": false, "error": "..."}\`.
|
|
16290
16413
|
|
|
16291
16414
|
### \`state\` — shared per-mesh key-value store
|
|
@@ -18389,6 +18512,32 @@ function daemonGet(path2, opts = {}) {
|
|
|
18389
18512
|
req.end();
|
|
18390
18513
|
});
|
|
18391
18514
|
}
|
|
18515
|
+
function daemonMarkSeen(ids, sessionToken) {
|
|
18516
|
+
return new Promise((resolve3) => {
|
|
18517
|
+
if (ids.length === 0) {
|
|
18518
|
+
resolve3();
|
|
18519
|
+
return;
|
|
18520
|
+
}
|
|
18521
|
+
const body = JSON.stringify({ ids });
|
|
18522
|
+
const headers = {
|
|
18523
|
+
"Content-Type": "application/json",
|
|
18524
|
+
"Content-Length": String(Buffer.byteLength(body))
|
|
18525
|
+
};
|
|
18526
|
+
if (sessionToken)
|
|
18527
|
+
headers.Authorization = `ClaudeMesh-Session ${sessionToken}`;
|
|
18528
|
+
const req = httpRequest2({ socketPath: DAEMON_PATHS.SOCK_FILE, path: "/v1/inbox/seen", method: "POST", timeout: 3000, headers }, (res) => {
|
|
18529
|
+
res.on("data", () => {});
|
|
18530
|
+
res.on("end", () => resolve3());
|
|
18531
|
+
});
|
|
18532
|
+
req.on("error", () => resolve3());
|
|
18533
|
+
req.on("timeout", () => {
|
|
18534
|
+
req.destroy();
|
|
18535
|
+
resolve3();
|
|
18536
|
+
});
|
|
18537
|
+
req.write(body);
|
|
18538
|
+
req.end();
|
|
18539
|
+
});
|
|
18540
|
+
}
|
|
18392
18541
|
function subscribeEvents(onEvent) {
|
|
18393
18542
|
let active = true;
|
|
18394
18543
|
let req = null;
|
|
@@ -18601,6 +18750,8 @@ ${mf.allowed_tools.map((t) => ` - ${t}`).join(`
|
|
|
18601
18750
|
} catch {}
|
|
18602
18751
|
};
|
|
18603
18752
|
mcpLog("mcp_started", { version: VERSION });
|
|
18753
|
+
const { readSessionTokenFromEnv: readSessionTokenFromEnv2 } = await Promise.resolve().then(() => (init_token(), exports_token));
|
|
18754
|
+
const sessionTokenForSeen = readSessionTokenFromEnv2();
|
|
18604
18755
|
const sub = subscribeEvents(async (ev) => {
|
|
18605
18756
|
mcpLog("sse_event_received", { kind: ev.kind });
|
|
18606
18757
|
if (ev.kind === "message") {
|
|
@@ -18633,6 +18784,10 @@ ${mf.allowed_tools.map((t) => ` - ${t}`).join(`
|
|
|
18633
18784
|
}
|
|
18634
18785
|
});
|
|
18635
18786
|
mcpLog("channel_emitted", { content_preview: content.slice(0, 80), mesh: String(d.mesh ?? "") });
|
|
18787
|
+
const inboxRowId = String(d.id ?? "");
|
|
18788
|
+
if (inboxRowId) {
|
|
18789
|
+
daemonMarkSeen([inboxRowId], sessionTokenForSeen).catch(() => {});
|
|
18790
|
+
}
|
|
18636
18791
|
} catch (err) {
|
|
18637
18792
|
mcpLog("channel_emit_failed", { err: String(err) });
|
|
18638
18793
|
process.stderr.write(`[claudemesh-mcp] channel emit failed: ${err}
|
|
@@ -18732,8 +18887,7 @@ async function emitMeshWelcome(server, mcpLog) {
|
|
|
18732
18887
|
} catch (e) {
|
|
18733
18888
|
mcpLog("welcome_peers_lookup_failed", { err: String(e) });
|
|
18734
18889
|
}
|
|
18735
|
-
const
|
|
18736
|
-
const inboxPath = selfMeshSlug ? `/v1/inbox?mesh=${encodeURIComponent(selfMeshSlug)}&since=${encodeURIComponent(sinceIso)}&limit=20` : `/v1/inbox?since=${encodeURIComponent(sinceIso)}&limit=20`;
|
|
18890
|
+
const inboxPath = selfMeshSlug ? `/v1/inbox?mesh=${encodeURIComponent(selfMeshSlug)}&unread_only=true&mark_seen=false&limit=50` : `/v1/inbox?unread_only=true&mark_seen=false&limit=50`;
|
|
18737
18891
|
let inboxItems = [];
|
|
18738
18892
|
try {
|
|
18739
18893
|
const { status, body } = await daemonGet(inboxPath, { sessionToken });
|
|
@@ -18757,9 +18911,9 @@ async function emitMeshWelcome(server, mcpLog) {
|
|
|
18757
18911
|
lines.push(`\uD83D\uDC65 Peer list unavailable (daemon query failed).`);
|
|
18758
18912
|
}
|
|
18759
18913
|
if (inboxItems.length === 0) {
|
|
18760
|
-
lines.push(`\uD83D\uDCE5
|
|
18914
|
+
lines.push(`\uD83D\uDCE5 No unread messages.`);
|
|
18761
18915
|
} else {
|
|
18762
|
-
lines.push(`\uD83D\uDCE5 ${inboxItems.length} message${inboxItems.length === 1 ? "" : "s"}
|
|
18916
|
+
lines.push(`\uD83D\uDCE5 ${inboxItems.length} unread message${inboxItems.length === 1 ? "" : "s"}:`);
|
|
18763
18917
|
for (const it of inboxItems.slice(0, 3)) {
|
|
18764
18918
|
const sender = String(it.sender_name ?? "unknown");
|
|
18765
18919
|
const senderPub = String(it.sender_pubkey ?? "").slice(0, 8);
|
|
@@ -18798,6 +18952,12 @@ async function emitMeshWelcome(server, mcpLog) {
|
|
|
18798
18952
|
peer_count: peerCount,
|
|
18799
18953
|
unread_count: inboxItems.length
|
|
18800
18954
|
});
|
|
18955
|
+
if (inboxItems.length > 0) {
|
|
18956
|
+
const ids = inboxItems.map((it) => String(it.id ?? "")).filter(Boolean);
|
|
18957
|
+
if (ids.length > 0) {
|
|
18958
|
+
daemonMarkSeen(ids, sessionToken).catch(() => {});
|
|
18959
|
+
}
|
|
18960
|
+
}
|
|
18801
18961
|
} catch (err) {
|
|
18802
18962
|
mcpLog("welcome_emit_failed", { err: String(err) });
|
|
18803
18963
|
}
|
|
@@ -19060,7 +19220,8 @@ var BOOLEAN_FLAGS = new Set([
|
|
|
19060
19220
|
"force",
|
|
19061
19221
|
"dry-run",
|
|
19062
19222
|
"verbose",
|
|
19063
|
-
"skip-service"
|
|
19223
|
+
"skip-service",
|
|
19224
|
+
"unread"
|
|
19064
19225
|
]);
|
|
19065
19226
|
function parseArgv(argv) {
|
|
19066
19227
|
const args = argv.slice(2);
|
|
@@ -19673,8 +19834,10 @@ Message (resource form)
|
|
|
19673
19834
|
fans out to every sibling session of your member)
|
|
19674
19835
|
[--json] (machine-readable result)
|
|
19675
19836
|
claudemesh message inbox read persisted inbox (alias: inbox)
|
|
19676
|
-
flags: [--mesh <slug>] [--limit N] [--json]
|
|
19837
|
+
flags: [--mesh <slug>] [--limit N] [--unread] [--json]
|
|
19677
19838
|
reads ~/.claudemesh/daemon/inbox.db via daemon
|
|
19839
|
+
--unread → only rows never surfaced before (seen_at IS NULL);
|
|
19840
|
+
listing stamps returned rows seen as a side effect
|
|
19678
19841
|
claudemesh inbox flush bulk-delete inbox rows
|
|
19679
19842
|
flags: [--mesh <slug>] [--before <iso-timestamp>] [--all]
|
|
19680
19843
|
--all required when neither --mesh nor --before is set
|
|
@@ -20052,7 +20215,8 @@ async function main() {
|
|
|
20052
20215
|
await runInbox2({
|
|
20053
20216
|
mesh: flags.mesh,
|
|
20054
20217
|
json: !!flags.json,
|
|
20055
|
-
limit: typeof flags.limit === "number" ? flags.limit : typeof flags.limit === "string" ? Number.parseInt(flags.limit, 10) : undefined
|
|
20218
|
+
limit: typeof flags.limit === "number" ? flags.limit : typeof flags.limit === "string" ? Number.parseInt(flags.limit, 10) : undefined,
|
|
20219
|
+
unread: !!flags.unread
|
|
20056
20220
|
});
|
|
20057
20221
|
}
|
|
20058
20222
|
break;
|
|
@@ -20325,7 +20489,8 @@ async function main() {
|
|
|
20325
20489
|
await runInbox2({
|
|
20326
20490
|
mesh: flags.mesh,
|
|
20327
20491
|
json: !!flags.json,
|
|
20328
|
-
limit: typeof flags.limit === "number" ? flags.limit : typeof flags.limit === "string" ? Number.parseInt(flags.limit, 10) : undefined
|
|
20492
|
+
limit: typeof flags.limit === "number" ? flags.limit : typeof flags.limit === "string" ? Number.parseInt(flags.limit, 10) : undefined,
|
|
20493
|
+
unread: !!flags.unread
|
|
20329
20494
|
});
|
|
20330
20495
|
}
|
|
20331
20496
|
} else if (sub === "status") {
|
|
@@ -20880,4 +21045,4 @@ main().catch((err) => {
|
|
|
20880
21045
|
process.exit(EXIT.INTERNAL_ERROR);
|
|
20881
21046
|
});
|
|
20882
21047
|
|
|
20883
|
-
//# debugId=
|
|
21048
|
+
//# debugId=99681AB539B313DF64756E2164756E21
|