claudemesh-cli 1.14.0 → 1.15.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.
- package/dist/entrypoints/cli.js +432 -412
- package/dist/entrypoints/cli.js.map +5 -5
- package/dist/entrypoints/mcp.js +2 -2
- package/dist/entrypoints/mcp.js.map +1 -1
- package/package.json +1 -1
package/dist/entrypoints/cli.js
CHANGED
|
@@ -88,7 +88,7 @@ __export(exports_urls, {
|
|
|
88
88
|
VERSION: () => VERSION,
|
|
89
89
|
URLS: () => URLS
|
|
90
90
|
});
|
|
91
|
-
var URLS, VERSION = "1.
|
|
91
|
+
var URLS, VERSION = "1.15.0", env;
|
|
92
92
|
var init_urls = __esm(() => {
|
|
93
93
|
URLS = {
|
|
94
94
|
BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
|
|
@@ -11766,6 +11766,270 @@ var init_with_rest_key = __esm(() => {
|
|
|
11766
11766
|
init_connect();
|
|
11767
11767
|
});
|
|
11768
11768
|
|
|
11769
|
+
// src/commands/me.ts
|
|
11770
|
+
var exports_me = {};
|
|
11771
|
+
__export(exports_me, {
|
|
11772
|
+
runMeTopics: () => runMeTopics,
|
|
11773
|
+
runMeSearch: () => runMeSearch,
|
|
11774
|
+
runMeNotifications: () => runMeNotifications,
|
|
11775
|
+
runMeActivity: () => runMeActivity,
|
|
11776
|
+
runMe: () => runMe
|
|
11777
|
+
});
|
|
11778
|
+
function resolveMeshForMint(explicit) {
|
|
11779
|
+
if (explicit)
|
|
11780
|
+
return explicit;
|
|
11781
|
+
const cfg = readConfig();
|
|
11782
|
+
return cfg.meshes[0]?.slug ?? null;
|
|
11783
|
+
}
|
|
11784
|
+
async function runMe(flags) {
|
|
11785
|
+
return withRestKey({
|
|
11786
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
11787
|
+
purpose: "workspace-overview",
|
|
11788
|
+
capabilities: ["read"]
|
|
11789
|
+
}, async ({ secret }) => {
|
|
11790
|
+
const ws = await request({
|
|
11791
|
+
path: "/api/v1/me/workspace",
|
|
11792
|
+
token: secret
|
|
11793
|
+
});
|
|
11794
|
+
if (flags.json) {
|
|
11795
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
11796
|
+
return EXIT.SUCCESS;
|
|
11797
|
+
}
|
|
11798
|
+
render.section(`${clay("workspace")} — ${bold(ws.userId.slice(0, 8))} ${dim(`· ${ws.totals.meshes} mesh${ws.totals.meshes === 1 ? "" : "es"}`)}`);
|
|
11799
|
+
const totalsLine = [
|
|
11800
|
+
`${green(String(ws.totals.online))}/${ws.totals.peers} online`,
|
|
11801
|
+
`${ws.totals.topics} topic${ws.totals.topics === 1 ? "" : "s"}`,
|
|
11802
|
+
ws.totals.unreadMentions > 0 ? yellow(`${ws.totals.unreadMentions} unread @you`) : dim("0 unread @you")
|
|
11803
|
+
].join(dim(" · "));
|
|
11804
|
+
process.stdout.write(" " + totalsLine + `
|
|
11805
|
+
|
|
11806
|
+
`);
|
|
11807
|
+
if (ws.meshes.length === 0) {
|
|
11808
|
+
process.stdout.write(dim(" no meshes joined — run `claudemesh new` or accept an invite\n"));
|
|
11809
|
+
return EXIT.SUCCESS;
|
|
11810
|
+
}
|
|
11811
|
+
const slugWidth = Math.max(...ws.meshes.map((m) => m.slug.length), 8);
|
|
11812
|
+
for (const m of ws.meshes) {
|
|
11813
|
+
const slug = cyan(m.slug.padEnd(slugWidth));
|
|
11814
|
+
const peers = `${m.online}/${m.peers}`;
|
|
11815
|
+
const role = dim(m.myRole);
|
|
11816
|
+
const unread = m.unreadMentions > 0 ? " " + yellow(`${m.unreadMentions} @you`) : "";
|
|
11817
|
+
process.stdout.write(` ${slug} ${peers.padStart(5)} online ${dim(String(m.topics).padStart(2) + " topics")} ${role}${unread}
|
|
11818
|
+
`);
|
|
11819
|
+
}
|
|
11820
|
+
return EXIT.SUCCESS;
|
|
11821
|
+
});
|
|
11822
|
+
}
|
|
11823
|
+
async function runMeTopics(flags) {
|
|
11824
|
+
return withRestKey({
|
|
11825
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
11826
|
+
purpose: "workspace-topics",
|
|
11827
|
+
capabilities: ["read"]
|
|
11828
|
+
}, async ({ secret }) => {
|
|
11829
|
+
const ws = await request({
|
|
11830
|
+
path: "/api/v1/me/topics",
|
|
11831
|
+
token: secret
|
|
11832
|
+
});
|
|
11833
|
+
const visible = flags.unread ? ws.topics.filter((t) => t.unread > 0) : ws.topics;
|
|
11834
|
+
if (flags.json) {
|
|
11835
|
+
console.log(JSON.stringify({ topics: visible, totals: ws.totals }, null, 2));
|
|
11836
|
+
return EXIT.SUCCESS;
|
|
11837
|
+
}
|
|
11838
|
+
render.section(`${clay("topics")} — ${ws.totals.topics} across all meshes ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· all read")}`);
|
|
11839
|
+
if (visible.length === 0) {
|
|
11840
|
+
process.stdout.write(dim(flags.unread ? ` no unread topics
|
|
11841
|
+
` : " no topics — run `claudemesh topic create #general`\n"));
|
|
11842
|
+
return EXIT.SUCCESS;
|
|
11843
|
+
}
|
|
11844
|
+
const slugWidth = Math.max(...visible.map((t) => t.meshSlug.length), 6);
|
|
11845
|
+
const nameWidth = Math.max(...visible.map((t) => t.name.length), 8);
|
|
11846
|
+
for (const t of visible) {
|
|
11847
|
+
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
11848
|
+
const name = cyan(t.name.padEnd(nameWidth));
|
|
11849
|
+
const unread = t.unread > 0 ? yellow(`${t.unread} unread`.padStart(10)) : dim("·".padStart(10));
|
|
11850
|
+
const last = t.lastMessageAt ? dim(formatRelativeTime(t.lastMessageAt)) : dim("never");
|
|
11851
|
+
process.stdout.write(` ${slug} ${name} ${unread} ${last}
|
|
11852
|
+
`);
|
|
11853
|
+
}
|
|
11854
|
+
return EXIT.SUCCESS;
|
|
11855
|
+
});
|
|
11856
|
+
}
|
|
11857
|
+
async function runMeNotifications(flags) {
|
|
11858
|
+
return withRestKey({
|
|
11859
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
11860
|
+
purpose: "workspace-notifications",
|
|
11861
|
+
capabilities: ["read"]
|
|
11862
|
+
}, async ({ secret }) => {
|
|
11863
|
+
const params = new URLSearchParams;
|
|
11864
|
+
if (flags.all)
|
|
11865
|
+
params.set("include", "all");
|
|
11866
|
+
if (flags.since)
|
|
11867
|
+
params.set("since", flags.since);
|
|
11868
|
+
const path = "/api/v1/me/notifications" + (params.toString() ? `?${params.toString()}` : "");
|
|
11869
|
+
const ws = await request({
|
|
11870
|
+
path,
|
|
11871
|
+
token: secret
|
|
11872
|
+
});
|
|
11873
|
+
if (flags.json) {
|
|
11874
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
11875
|
+
return EXIT.SUCCESS;
|
|
11876
|
+
}
|
|
11877
|
+
const headerLabel = flags.all ? "@-mentions (all)" : "@-mentions (unread)";
|
|
11878
|
+
render.section(`${clay(headerLabel)} — ${ws.totals.total} ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· nothing pending")}`);
|
|
11879
|
+
if (ws.notifications.length === 0) {
|
|
11880
|
+
process.stdout.write(dim(flags.all ? ` no @-mentions in window
|
|
11881
|
+
` : ` inbox zero — nothing waiting
|
|
11882
|
+
`));
|
|
11883
|
+
return EXIT.SUCCESS;
|
|
11884
|
+
}
|
|
11885
|
+
const slugWidth = Math.max(...ws.notifications.map((n) => n.meshSlug.length), 6);
|
|
11886
|
+
for (const n of ws.notifications) {
|
|
11887
|
+
const slug = dim(n.meshSlug.padEnd(slugWidth));
|
|
11888
|
+
const topic = cyan(`#${n.topicName}`);
|
|
11889
|
+
const sender = n.senderName ? `from ${n.senderName}` : "from ?";
|
|
11890
|
+
const ago = formatRelativeTime(n.createdAt);
|
|
11891
|
+
const dot = n.read ? dim("·") : yellow("●");
|
|
11892
|
+
const snippet = n.snippet ?? (n.ciphertext ? dim("[encrypted]") : dim("[empty]"));
|
|
11893
|
+
process.stdout.write(` ${dot} ${slug} ${topic} ${dim(sender)} ${dim(ago)}
|
|
11894
|
+
` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
|
|
11895
|
+
`);
|
|
11896
|
+
}
|
|
11897
|
+
return EXIT.SUCCESS;
|
|
11898
|
+
});
|
|
11899
|
+
}
|
|
11900
|
+
async function runMeActivity(flags) {
|
|
11901
|
+
return withRestKey({
|
|
11902
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
11903
|
+
purpose: "workspace-activity",
|
|
11904
|
+
capabilities: ["read"]
|
|
11905
|
+
}, async ({ secret }) => {
|
|
11906
|
+
const params = new URLSearchParams;
|
|
11907
|
+
if (flags.since)
|
|
11908
|
+
params.set("since", flags.since);
|
|
11909
|
+
const path = "/api/v1/me/activity" + (params.toString() ? `?${params.toString()}` : "");
|
|
11910
|
+
const ws = await request({
|
|
11911
|
+
path,
|
|
11912
|
+
token: secret
|
|
11913
|
+
});
|
|
11914
|
+
if (flags.json) {
|
|
11915
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
11916
|
+
return EXIT.SUCCESS;
|
|
11917
|
+
}
|
|
11918
|
+
render.section(`${clay("activity")} — ${ws.totals.events} ${dim(flags.since ? `since ${flags.since}` : "in the last 24h")}`);
|
|
11919
|
+
if (ws.activity.length === 0) {
|
|
11920
|
+
process.stdout.write(dim(` quiet — no activity in window
|
|
11921
|
+
`));
|
|
11922
|
+
return EXIT.SUCCESS;
|
|
11923
|
+
}
|
|
11924
|
+
const slugWidth = Math.max(...ws.activity.map((a) => a.meshSlug.length), 6);
|
|
11925
|
+
for (const a of ws.activity) {
|
|
11926
|
+
const slug = dim(a.meshSlug.padEnd(slugWidth));
|
|
11927
|
+
const topic = cyan(`#${a.topicName}`);
|
|
11928
|
+
const sender = a.senderName ?? "?";
|
|
11929
|
+
const ago = formatRelativeTime(a.createdAt);
|
|
11930
|
+
const snippet = a.snippet ?? (a.ciphertext ? dim("[encrypted]") : dim("[empty]"));
|
|
11931
|
+
process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
|
|
11932
|
+
` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
|
|
11933
|
+
`);
|
|
11934
|
+
}
|
|
11935
|
+
return EXIT.SUCCESS;
|
|
11936
|
+
});
|
|
11937
|
+
}
|
|
11938
|
+
async function runMeSearch(flags) {
|
|
11939
|
+
if (!flags.query || flags.query.length < 2) {
|
|
11940
|
+
process.stderr.write(`Usage: claudemesh me search <query> (min 2 chars)
|
|
11941
|
+
`);
|
|
11942
|
+
return EXIT.INVALID_ARGS;
|
|
11943
|
+
}
|
|
11944
|
+
return withRestKey({
|
|
11945
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
11946
|
+
purpose: "workspace-search",
|
|
11947
|
+
capabilities: ["read"]
|
|
11948
|
+
}, async ({ secret }) => {
|
|
11949
|
+
const params = new URLSearchParams({ q: flags.query });
|
|
11950
|
+
const ws = await request({
|
|
11951
|
+
path: `/api/v1/me/search?${params.toString()}`,
|
|
11952
|
+
token: secret
|
|
11953
|
+
});
|
|
11954
|
+
if (flags.json) {
|
|
11955
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
11956
|
+
return EXIT.SUCCESS;
|
|
11957
|
+
}
|
|
11958
|
+
render.section(`${clay("search")} — "${flags.query}" ${dim(`${ws.totals.topics} topic${ws.totals.topics === 1 ? "" : "s"}, ` + `${ws.totals.messages} message${ws.totals.messages === 1 ? "" : "s"}`)}`);
|
|
11959
|
+
if (ws.topics.length === 0 && ws.messages.length === 0) {
|
|
11960
|
+
process.stdout.write(dim(` no matches
|
|
11961
|
+
`));
|
|
11962
|
+
return EXIT.SUCCESS;
|
|
11963
|
+
}
|
|
11964
|
+
if (ws.topics.length > 0) {
|
|
11965
|
+
process.stdout.write(dim(`
|
|
11966
|
+
topics
|
|
11967
|
+
`));
|
|
11968
|
+
const slugWidth = Math.max(...ws.topics.map((t) => t.meshSlug.length), 6);
|
|
11969
|
+
for (const t of ws.topics) {
|
|
11970
|
+
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
11971
|
+
const name = cyan(`#${t.name}`);
|
|
11972
|
+
const desc = t.description ? dim(` — ${t.description}`) : "";
|
|
11973
|
+
process.stdout.write(` ${slug} ${name}${desc}
|
|
11974
|
+
`);
|
|
11975
|
+
}
|
|
11976
|
+
}
|
|
11977
|
+
if (ws.messages.length > 0) {
|
|
11978
|
+
process.stdout.write(dim(`
|
|
11979
|
+
messages
|
|
11980
|
+
`));
|
|
11981
|
+
const slugWidth = Math.max(...ws.messages.map((m) => m.meshSlug.length), 6);
|
|
11982
|
+
for (const m of ws.messages) {
|
|
11983
|
+
const slug = dim(m.meshSlug.padEnd(slugWidth));
|
|
11984
|
+
const topic = cyan(`#${m.topicName}`);
|
|
11985
|
+
const sender = m.senderName;
|
|
11986
|
+
const ago = formatRelativeTime(m.createdAt);
|
|
11987
|
+
const snippet = m.snippet ?? (m.bodyVersion === 2 ? dim("[encrypted — open the topic to decrypt]") : dim("[empty]"));
|
|
11988
|
+
const highlighted = m.snippet ? highlightMatch(snippet, flags.query) : snippet;
|
|
11989
|
+
process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
|
|
11990
|
+
` + ` ${highlighted}
|
|
11991
|
+
`);
|
|
11992
|
+
}
|
|
11993
|
+
}
|
|
11994
|
+
return EXIT.SUCCESS;
|
|
11995
|
+
});
|
|
11996
|
+
}
|
|
11997
|
+
function highlightMatch(text, query) {
|
|
11998
|
+
if (!query)
|
|
11999
|
+
return text;
|
|
12000
|
+
const idx = text.toLowerCase().indexOf(query.toLowerCase());
|
|
12001
|
+
if (idx === -1)
|
|
12002
|
+
return text;
|
|
12003
|
+
const before = text.slice(0, idx);
|
|
12004
|
+
const match = text.slice(idx, idx + query.length);
|
|
12005
|
+
const after = text.slice(idx + query.length);
|
|
12006
|
+
return `${before}${yellow(match)}${after}`;
|
|
12007
|
+
}
|
|
12008
|
+
function formatRelativeTime(iso) {
|
|
12009
|
+
const then = new Date(iso).getTime();
|
|
12010
|
+
const now = Date.now();
|
|
12011
|
+
const sec = Math.max(0, Math.floor((now - then) / 1000));
|
|
12012
|
+
if (sec < 60)
|
|
12013
|
+
return `${sec}s ago`;
|
|
12014
|
+
if (sec < 3600)
|
|
12015
|
+
return `${Math.floor(sec / 60)}m ago`;
|
|
12016
|
+
if (sec < 86400)
|
|
12017
|
+
return `${Math.floor(sec / 3600)}h ago`;
|
|
12018
|
+
if (sec < 86400 * 30)
|
|
12019
|
+
return `${Math.floor(sec / 86400)}d ago`;
|
|
12020
|
+
if (sec < 86400 * 365)
|
|
12021
|
+
return `${Math.floor(sec / (86400 * 30))}mo ago`;
|
|
12022
|
+
return `${Math.floor(sec / (86400 * 365))}y ago`;
|
|
12023
|
+
}
|
|
12024
|
+
var init_me = __esm(() => {
|
|
12025
|
+
init_with_rest_key();
|
|
12026
|
+
init_client();
|
|
12027
|
+
init_facade();
|
|
12028
|
+
init_render();
|
|
12029
|
+
init_styles();
|
|
12030
|
+
init_exit_codes();
|
|
12031
|
+
});
|
|
12032
|
+
|
|
11769
12033
|
// src/services/crypto/topic-key.ts
|
|
11770
12034
|
function cacheKey(apiKeySecret, topicName) {
|
|
11771
12035
|
return `${apiKeySecret.slice(0, 12)}:${topicName}`;
|
|
@@ -12101,439 +12365,182 @@ async function runTopicTail(name, flags) {
|
|
|
12101
12365
|
render.err(`tail aborted: ${err.message}`);
|
|
12102
12366
|
return EXIT.INTERNAL_ERROR;
|
|
12103
12367
|
} finally {
|
|
12104
|
-
process.removeListener("SIGINT", onSig);
|
|
12105
|
-
process.removeListener("SIGTERM", onSig);
|
|
12106
|
-
if (resealTimer)
|
|
12107
|
-
clearInterval(resealTimer);
|
|
12108
|
-
}
|
|
12109
|
-
});
|
|
12110
|
-
}
|
|
12111
|
-
var RECENT_CACHE_MAX = 256;
|
|
12112
|
-
var init_topic_tail = __esm(() => {
|
|
12113
|
-
init_urls();
|
|
12114
|
-
init_with_rest_key();
|
|
12115
|
-
init_client();
|
|
12116
|
-
init_topic_key();
|
|
12117
|
-
init_render();
|
|
12118
|
-
init_styles();
|
|
12119
|
-
init_exit_codes();
|
|
12120
|
-
});
|
|
12121
|
-
|
|
12122
|
-
// src/commands/topic-post.ts
|
|
12123
|
-
var exports_topic_post = {};
|
|
12124
|
-
__export(exports_topic_post, {
|
|
12125
|
-
runTopicPost: () => runTopicPost
|
|
12126
|
-
});
|
|
12127
|
-
async function runTopicPost(topicName, message, flags) {
|
|
12128
|
-
if (!topicName || !message) {
|
|
12129
|
-
render.err("Usage: claudemesh topic post <topic> <message>");
|
|
12130
|
-
return EXIT.INVALID_ARGS;
|
|
12131
|
-
}
|
|
12132
|
-
const cleanName = topicName.replace(/^#/, "");
|
|
12133
|
-
const mentions = [];
|
|
12134
|
-
const mentionRe = /(^|[^A-Za-z0-9_-])@([A-Za-z0-9_-]{1,64})(?=$|[^A-Za-z0-9_-])/g;
|
|
12135
|
-
let m;
|
|
12136
|
-
while ((m = mentionRe.exec(message)) !== null) {
|
|
12137
|
-
mentions.push(m[2].toLowerCase());
|
|
12138
|
-
if (mentions.length >= 16)
|
|
12139
|
-
break;
|
|
12140
|
-
}
|
|
12141
|
-
return withRestKey({
|
|
12142
|
-
meshSlug: flags.mesh ?? null,
|
|
12143
|
-
purpose: `post-${cleanName}`,
|
|
12144
|
-
capabilities: ["read", "send"],
|
|
12145
|
-
topicScopes: [cleanName]
|
|
12146
|
-
}, async ({ secret, mesh }) => {
|
|
12147
|
-
let bodyVersion = 1;
|
|
12148
|
-
let ciphertext;
|
|
12149
|
-
let nonce;
|
|
12150
|
-
if (flags.plaintext) {
|
|
12151
|
-
ciphertext = Buffer.from(message, "utf-8").toString("base64");
|
|
12152
|
-
nonce = Buffer.from(new Uint8Array(24)).toString("base64");
|
|
12153
|
-
} else {
|
|
12154
|
-
const keyResult = await getTopicKey({
|
|
12155
|
-
apiKeySecret: secret,
|
|
12156
|
-
memberSecretKeyHex: mesh.secretKey,
|
|
12157
|
-
topicName: cleanName
|
|
12158
|
-
});
|
|
12159
|
-
if (keyResult.ok && keyResult.topicKey) {
|
|
12160
|
-
const enc = await encryptMessage(keyResult.topicKey, message);
|
|
12161
|
-
ciphertext = enc.ciphertext;
|
|
12162
|
-
nonce = enc.nonce;
|
|
12163
|
-
bodyVersion = 2;
|
|
12164
|
-
} else if (keyResult.error === "topic_unencrypted") {
|
|
12165
|
-
ciphertext = Buffer.from(message, "utf-8").toString("base64");
|
|
12166
|
-
nonce = Buffer.from(new Uint8Array(24)).toString("base64");
|
|
12167
|
-
} else {
|
|
12168
|
-
render.err(`cannot encrypt for #${cleanName}: ${keyResult.error ?? "unknown"}${keyResult.message ? " — " + keyResult.message : ""}`);
|
|
12169
|
-
return EXIT.INTERNAL_ERROR;
|
|
12170
|
-
}
|
|
12171
|
-
}
|
|
12172
|
-
let replyToId;
|
|
12173
|
-
if (flags.replyTo) {
|
|
12174
|
-
if (flags.replyTo.length >= 16) {
|
|
12175
|
-
replyToId = flags.replyTo;
|
|
12176
|
-
} else if (flags.replyTo.length >= 6) {
|
|
12177
|
-
const recent = await request({
|
|
12178
|
-
path: `/api/v1/topics/${encodeURIComponent(cleanName)}/messages?limit=200`,
|
|
12179
|
-
method: "GET",
|
|
12180
|
-
token: secret
|
|
12181
|
-
});
|
|
12182
|
-
const hit = recent.messages?.find((r) => r.id.startsWith(flags.replyTo));
|
|
12183
|
-
if (!hit) {
|
|
12184
|
-
render.err(`--reply-to ${flags.replyTo}: no recent message id starts with that prefix`);
|
|
12185
|
-
return EXIT.INVALID_ARGS;
|
|
12186
|
-
}
|
|
12187
|
-
replyToId = hit.id;
|
|
12188
|
-
} else {
|
|
12189
|
-
render.err("--reply-to needs at least 6 characters of the message id");
|
|
12190
|
-
return EXIT.INVALID_ARGS;
|
|
12191
|
-
}
|
|
12192
|
-
}
|
|
12193
|
-
const result = await request({
|
|
12194
|
-
path: "/api/v1/messages",
|
|
12195
|
-
method: "POST",
|
|
12196
|
-
token: secret,
|
|
12197
|
-
body: {
|
|
12198
|
-
topic: cleanName,
|
|
12199
|
-
ciphertext,
|
|
12200
|
-
nonce,
|
|
12201
|
-
bodyVersion,
|
|
12202
|
-
...mentions.length > 0 ? { mentions } : {},
|
|
12203
|
-
...replyToId ? { replyToId } : {}
|
|
12204
|
-
}
|
|
12205
|
-
});
|
|
12206
|
-
if (flags.json) {
|
|
12207
|
-
console.log(JSON.stringify({ ...result, bodyVersion, mentions }));
|
|
12208
|
-
return EXIT.SUCCESS;
|
|
12209
|
-
}
|
|
12210
|
-
const versionTag = bodyVersion === 2 ? green("\uD83D\uDD12 v2") : dim("v1");
|
|
12211
|
-
const replyTag = result.replyToId ? ` ${dim("↳ " + result.replyToId.slice(0, 8))}` : "";
|
|
12212
|
-
render.ok("posted", `${clay("#" + cleanName)} ${versionTag}${replyTag} ${dim(`(${result.notifications} mentions)`)}`);
|
|
12213
|
-
return EXIT.SUCCESS;
|
|
12214
|
-
});
|
|
12215
|
-
}
|
|
12216
|
-
var init_topic_post = __esm(() => {
|
|
12217
|
-
init_with_rest_key();
|
|
12218
|
-
init_client();
|
|
12219
|
-
init_topic_key();
|
|
12220
|
-
init_render();
|
|
12221
|
-
init_styles();
|
|
12222
|
-
init_exit_codes();
|
|
12223
|
-
});
|
|
12224
|
-
|
|
12225
|
-
// src/commands/notification.ts
|
|
12226
|
-
var exports_notification = {};
|
|
12227
|
-
__export(exports_notification, {
|
|
12228
|
-
runNotificationList: () => runNotificationList
|
|
12229
|
-
});
|
|
12230
|
-
function decodeCiphertext(b64) {
|
|
12231
|
-
try {
|
|
12232
|
-
return Buffer.from(b64, "base64").toString("utf-8");
|
|
12233
|
-
} catch {
|
|
12234
|
-
return "[decode failed]";
|
|
12235
|
-
}
|
|
12236
|
-
}
|
|
12237
|
-
function fmtRelative(iso) {
|
|
12238
|
-
const ms = Date.now() - new Date(iso).getTime();
|
|
12239
|
-
if (ms < 60000)
|
|
12240
|
-
return "now";
|
|
12241
|
-
if (ms < 3600000)
|
|
12242
|
-
return `${Math.floor(ms / 60000)}m`;
|
|
12243
|
-
if (ms < 86400000)
|
|
12244
|
-
return `${Math.floor(ms / 3600000)}h`;
|
|
12245
|
-
return `${Math.floor(ms / 86400000)}d`;
|
|
12246
|
-
}
|
|
12247
|
-
async function runNotificationList(flags) {
|
|
12248
|
-
return withRestKey({ meshSlug: flags.mesh ?? null, purpose: "notifications" }, async ({ secret }) => {
|
|
12249
|
-
const qs = flags.since ? `?since=${encodeURIComponent(flags.since)}` : "";
|
|
12250
|
-
const result = await request({
|
|
12251
|
-
path: `/api/v1/notifications${qs}`,
|
|
12252
|
-
token: secret
|
|
12253
|
-
});
|
|
12254
|
-
if (flags.json) {
|
|
12255
|
-
const decoded = result.notifications.map((n) => ({
|
|
12256
|
-
...n,
|
|
12257
|
-
message: decodeCiphertext(n.ciphertext)
|
|
12258
|
-
}));
|
|
12259
|
-
console.log(JSON.stringify({ ...result, notifications: decoded }, null, 2));
|
|
12260
|
-
return EXIT.SUCCESS;
|
|
12261
|
-
}
|
|
12262
|
-
if (result.notifications.length === 0) {
|
|
12263
|
-
render.info(dim(`no mentions of @${result.mentionedAs} since ${result.since}.`));
|
|
12264
|
-
return EXIT.SUCCESS;
|
|
12265
|
-
}
|
|
12266
|
-
render.section(`mentions of @${bold(result.mentionedAs)} (${result.notifications.length})`);
|
|
12267
|
-
for (const n of result.notifications) {
|
|
12268
|
-
const when = fmtRelative(n.createdAt);
|
|
12269
|
-
const msg = decodeCiphertext(n.ciphertext).replace(/\s+/g, " ").trim();
|
|
12270
|
-
const snippet = msg.length > 100 ? msg.slice(0, 97) + "…" : msg;
|
|
12271
|
-
process.stdout.write(` ${clay("#" + n.topicName)} ${dim(when)} ${bold(n.senderName)}
|
|
12272
|
-
`);
|
|
12273
|
-
process.stdout.write(` ${snippet}
|
|
12274
|
-
`);
|
|
12275
|
-
}
|
|
12276
|
-
return EXIT.SUCCESS;
|
|
12277
|
-
});
|
|
12278
|
-
}
|
|
12279
|
-
var init_notification = __esm(() => {
|
|
12280
|
-
init_with_rest_key();
|
|
12281
|
-
init_client();
|
|
12282
|
-
init_render();
|
|
12283
|
-
init_styles();
|
|
12284
|
-
init_exit_codes();
|
|
12285
|
-
});
|
|
12286
|
-
|
|
12287
|
-
// src/commands/me.ts
|
|
12288
|
-
var exports_me = {};
|
|
12289
|
-
__export(exports_me, {
|
|
12290
|
-
runMeTopics: () => runMeTopics,
|
|
12291
|
-
runMeSearch: () => runMeSearch,
|
|
12292
|
-
runMeNotifications: () => runMeNotifications,
|
|
12293
|
-
runMeActivity: () => runMeActivity,
|
|
12294
|
-
runMe: () => runMe
|
|
12295
|
-
});
|
|
12296
|
-
async function runMe(flags) {
|
|
12297
|
-
return withRestKey({
|
|
12298
|
-
meshSlug: flags.mesh ?? null,
|
|
12299
|
-
purpose: "workspace-overview",
|
|
12300
|
-
capabilities: ["read"]
|
|
12301
|
-
}, async ({ secret }) => {
|
|
12302
|
-
const ws = await request({
|
|
12303
|
-
path: "/api/v1/me/workspace",
|
|
12304
|
-
token: secret
|
|
12305
|
-
});
|
|
12306
|
-
if (flags.json) {
|
|
12307
|
-
console.log(JSON.stringify(ws, null, 2));
|
|
12308
|
-
return EXIT.SUCCESS;
|
|
12309
|
-
}
|
|
12310
|
-
render.section(`${clay("workspace")} — ${bold(ws.userId.slice(0, 8))} ${dim(`· ${ws.totals.meshes} mesh${ws.totals.meshes === 1 ? "" : "es"}`)}`);
|
|
12311
|
-
const totalsLine = [
|
|
12312
|
-
`${green(String(ws.totals.online))}/${ws.totals.peers} online`,
|
|
12313
|
-
`${ws.totals.topics} topic${ws.totals.topics === 1 ? "" : "s"}`,
|
|
12314
|
-
ws.totals.unreadMentions > 0 ? yellow(`${ws.totals.unreadMentions} unread @you`) : dim("0 unread @you")
|
|
12315
|
-
].join(dim(" · "));
|
|
12316
|
-
process.stdout.write(" " + totalsLine + `
|
|
12317
|
-
|
|
12318
|
-
`);
|
|
12319
|
-
if (ws.meshes.length === 0) {
|
|
12320
|
-
process.stdout.write(dim(" no meshes joined — run `claudemesh new` or accept an invite\n"));
|
|
12321
|
-
return EXIT.SUCCESS;
|
|
12322
|
-
}
|
|
12323
|
-
const slugWidth = Math.max(...ws.meshes.map((m) => m.slug.length), 8);
|
|
12324
|
-
for (const m of ws.meshes) {
|
|
12325
|
-
const slug = cyan(m.slug.padEnd(slugWidth));
|
|
12326
|
-
const peers = `${m.online}/${m.peers}`;
|
|
12327
|
-
const role = dim(m.myRole);
|
|
12328
|
-
const unread = m.unreadMentions > 0 ? " " + yellow(`${m.unreadMentions} @you`) : "";
|
|
12329
|
-
process.stdout.write(` ${slug} ${peers.padStart(5)} online ${dim(String(m.topics).padStart(2) + " topics")} ${role}${unread}
|
|
12330
|
-
`);
|
|
12331
|
-
}
|
|
12332
|
-
return EXIT.SUCCESS;
|
|
12333
|
-
});
|
|
12334
|
-
}
|
|
12335
|
-
async function runMeTopics(flags) {
|
|
12336
|
-
return withRestKey({
|
|
12337
|
-
meshSlug: flags.mesh ?? null,
|
|
12338
|
-
purpose: "workspace-topics",
|
|
12339
|
-
capabilities: ["read"]
|
|
12340
|
-
}, async ({ secret }) => {
|
|
12341
|
-
const ws = await request({
|
|
12342
|
-
path: "/api/v1/me/topics",
|
|
12343
|
-
token: secret
|
|
12344
|
-
});
|
|
12345
|
-
const visible = flags.unread ? ws.topics.filter((t) => t.unread > 0) : ws.topics;
|
|
12346
|
-
if (flags.json) {
|
|
12347
|
-
console.log(JSON.stringify({ topics: visible, totals: ws.totals }, null, 2));
|
|
12348
|
-
return EXIT.SUCCESS;
|
|
12349
|
-
}
|
|
12350
|
-
render.section(`${clay("topics")} — ${ws.totals.topics} across all meshes ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· all read")}`);
|
|
12351
|
-
if (visible.length === 0) {
|
|
12352
|
-
process.stdout.write(dim(flags.unread ? ` no unread topics
|
|
12353
|
-
` : " no topics — run `claudemesh topic create #general`\n"));
|
|
12354
|
-
return EXIT.SUCCESS;
|
|
12355
|
-
}
|
|
12356
|
-
const slugWidth = Math.max(...visible.map((t) => t.meshSlug.length), 6);
|
|
12357
|
-
const nameWidth = Math.max(...visible.map((t) => t.name.length), 8);
|
|
12358
|
-
for (const t of visible) {
|
|
12359
|
-
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
12360
|
-
const name = cyan(t.name.padEnd(nameWidth));
|
|
12361
|
-
const unread = t.unread > 0 ? yellow(`${t.unread} unread`.padStart(10)) : dim("·".padStart(10));
|
|
12362
|
-
const last = t.lastMessageAt ? dim(formatRelativeTime(t.lastMessageAt)) : dim("never");
|
|
12363
|
-
process.stdout.write(` ${slug} ${name} ${unread} ${last}
|
|
12364
|
-
`);
|
|
12365
|
-
}
|
|
12366
|
-
return EXIT.SUCCESS;
|
|
12367
|
-
});
|
|
12368
|
-
}
|
|
12369
|
-
async function runMeNotifications(flags) {
|
|
12370
|
-
return withRestKey({
|
|
12371
|
-
meshSlug: flags.mesh ?? null,
|
|
12372
|
-
purpose: "workspace-notifications",
|
|
12373
|
-
capabilities: ["read"]
|
|
12374
|
-
}, async ({ secret }) => {
|
|
12375
|
-
const params = new URLSearchParams;
|
|
12376
|
-
if (flags.all)
|
|
12377
|
-
params.set("include", "all");
|
|
12378
|
-
if (flags.since)
|
|
12379
|
-
params.set("since", flags.since);
|
|
12380
|
-
const path = "/api/v1/me/notifications" + (params.toString() ? `?${params.toString()}` : "");
|
|
12381
|
-
const ws = await request({
|
|
12382
|
-
path,
|
|
12383
|
-
token: secret
|
|
12384
|
-
});
|
|
12385
|
-
if (flags.json) {
|
|
12386
|
-
console.log(JSON.stringify(ws, null, 2));
|
|
12387
|
-
return EXIT.SUCCESS;
|
|
12388
|
-
}
|
|
12389
|
-
const headerLabel = flags.all ? "@-mentions (all)" : "@-mentions (unread)";
|
|
12390
|
-
render.section(`${clay(headerLabel)} — ${ws.totals.total} ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· nothing pending")}`);
|
|
12391
|
-
if (ws.notifications.length === 0) {
|
|
12392
|
-
process.stdout.write(dim(flags.all ? ` no @-mentions in window
|
|
12393
|
-
` : ` inbox zero — nothing waiting
|
|
12394
|
-
`));
|
|
12395
|
-
return EXIT.SUCCESS;
|
|
12396
|
-
}
|
|
12397
|
-
const slugWidth = Math.max(...ws.notifications.map((n) => n.meshSlug.length), 6);
|
|
12398
|
-
for (const n of ws.notifications) {
|
|
12399
|
-
const slug = dim(n.meshSlug.padEnd(slugWidth));
|
|
12400
|
-
const topic = cyan(`#${n.topicName}`);
|
|
12401
|
-
const sender = n.senderName ? `from ${n.senderName}` : "from ?";
|
|
12402
|
-
const ago = formatRelativeTime(n.createdAt);
|
|
12403
|
-
const dot = n.read ? dim("·") : yellow("●");
|
|
12404
|
-
const snippet = n.snippet ?? (n.ciphertext ? dim("[encrypted]") : dim("[empty]"));
|
|
12405
|
-
process.stdout.write(` ${dot} ${slug} ${topic} ${dim(sender)} ${dim(ago)}
|
|
12406
|
-
` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
|
|
12407
|
-
`);
|
|
12368
|
+
process.removeListener("SIGINT", onSig);
|
|
12369
|
+
process.removeListener("SIGTERM", onSig);
|
|
12370
|
+
if (resealTimer)
|
|
12371
|
+
clearInterval(resealTimer);
|
|
12408
12372
|
}
|
|
12409
|
-
return EXIT.SUCCESS;
|
|
12410
12373
|
});
|
|
12411
12374
|
}
|
|
12412
|
-
|
|
12375
|
+
var RECENT_CACHE_MAX = 256;
|
|
12376
|
+
var init_topic_tail = __esm(() => {
|
|
12377
|
+
init_urls();
|
|
12378
|
+
init_with_rest_key();
|
|
12379
|
+
init_client();
|
|
12380
|
+
init_topic_key();
|
|
12381
|
+
init_render();
|
|
12382
|
+
init_styles();
|
|
12383
|
+
init_exit_codes();
|
|
12384
|
+
});
|
|
12385
|
+
|
|
12386
|
+
// src/commands/topic-post.ts
|
|
12387
|
+
var exports_topic_post = {};
|
|
12388
|
+
__export(exports_topic_post, {
|
|
12389
|
+
runTopicPost: () => runTopicPost
|
|
12390
|
+
});
|
|
12391
|
+
async function runTopicPost(topicName, message, flags) {
|
|
12392
|
+
if (!topicName || !message) {
|
|
12393
|
+
render.err("Usage: claudemesh topic post <topic> <message>");
|
|
12394
|
+
return EXIT.INVALID_ARGS;
|
|
12395
|
+
}
|
|
12396
|
+
const cleanName = topicName.replace(/^#/, "");
|
|
12397
|
+
const mentions = [];
|
|
12398
|
+
const mentionRe = /(^|[^A-Za-z0-9_-])@([A-Za-z0-9_-]{1,64})(?=$|[^A-Za-z0-9_-])/g;
|
|
12399
|
+
let m;
|
|
12400
|
+
while ((m = mentionRe.exec(message)) !== null) {
|
|
12401
|
+
mentions.push(m[2].toLowerCase());
|
|
12402
|
+
if (mentions.length >= 16)
|
|
12403
|
+
break;
|
|
12404
|
+
}
|
|
12413
12405
|
return withRestKey({
|
|
12414
12406
|
meshSlug: flags.mesh ?? null,
|
|
12415
|
-
purpose:
|
|
12416
|
-
capabilities: ["read"]
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12407
|
+
purpose: `post-${cleanName}`,
|
|
12408
|
+
capabilities: ["read", "send"],
|
|
12409
|
+
topicScopes: [cleanName]
|
|
12410
|
+
}, async ({ secret, mesh }) => {
|
|
12411
|
+
let bodyVersion = 1;
|
|
12412
|
+
let ciphertext;
|
|
12413
|
+
let nonce;
|
|
12414
|
+
if (flags.plaintext) {
|
|
12415
|
+
ciphertext = Buffer.from(message, "utf-8").toString("base64");
|
|
12416
|
+
nonce = Buffer.from(new Uint8Array(24)).toString("base64");
|
|
12417
|
+
} else {
|
|
12418
|
+
const keyResult = await getTopicKey({
|
|
12419
|
+
apiKeySecret: secret,
|
|
12420
|
+
memberSecretKeyHex: mesh.secretKey,
|
|
12421
|
+
topicName: cleanName
|
|
12422
|
+
});
|
|
12423
|
+
if (keyResult.ok && keyResult.topicKey) {
|
|
12424
|
+
const enc = await encryptMessage(keyResult.topicKey, message);
|
|
12425
|
+
ciphertext = enc.ciphertext;
|
|
12426
|
+
nonce = enc.nonce;
|
|
12427
|
+
bodyVersion = 2;
|
|
12428
|
+
} else if (keyResult.error === "topic_unencrypted") {
|
|
12429
|
+
ciphertext = Buffer.from(message, "utf-8").toString("base64");
|
|
12430
|
+
nonce = Buffer.from(new Uint8Array(24)).toString("base64");
|
|
12431
|
+
} else {
|
|
12432
|
+
render.err(`cannot encrypt for #${cleanName}: ${keyResult.error ?? "unknown"}${keyResult.message ? " — " + keyResult.message : ""}`);
|
|
12433
|
+
return EXIT.INTERNAL_ERROR;
|
|
12434
|
+
}
|
|
12435
|
+
}
|
|
12436
|
+
let replyToId;
|
|
12437
|
+
if (flags.replyTo) {
|
|
12438
|
+
if (flags.replyTo.length >= 16) {
|
|
12439
|
+
replyToId = flags.replyTo;
|
|
12440
|
+
} else if (flags.replyTo.length >= 6) {
|
|
12441
|
+
const recent = await request({
|
|
12442
|
+
path: `/api/v1/topics/${encodeURIComponent(cleanName)}/messages?limit=200`,
|
|
12443
|
+
method: "GET",
|
|
12444
|
+
token: secret
|
|
12445
|
+
});
|
|
12446
|
+
const hit = recent.messages?.find((r) => r.id.startsWith(flags.replyTo));
|
|
12447
|
+
if (!hit) {
|
|
12448
|
+
render.err(`--reply-to ${flags.replyTo}: no recent message id starts with that prefix`);
|
|
12449
|
+
return EXIT.INVALID_ARGS;
|
|
12450
|
+
}
|
|
12451
|
+
replyToId = hit.id;
|
|
12452
|
+
} else {
|
|
12453
|
+
render.err("--reply-to needs at least 6 characters of the message id");
|
|
12454
|
+
return EXIT.INVALID_ARGS;
|
|
12455
|
+
}
|
|
12456
|
+
}
|
|
12457
|
+
const result = await request({
|
|
12458
|
+
path: "/api/v1/messages",
|
|
12459
|
+
method: "POST",
|
|
12460
|
+
token: secret,
|
|
12461
|
+
body: {
|
|
12462
|
+
topic: cleanName,
|
|
12463
|
+
ciphertext,
|
|
12464
|
+
nonce,
|
|
12465
|
+
bodyVersion,
|
|
12466
|
+
...mentions.length > 0 ? { mentions } : {},
|
|
12467
|
+
...replyToId ? { replyToId } : {}
|
|
12468
|
+
}
|
|
12425
12469
|
});
|
|
12426
12470
|
if (flags.json) {
|
|
12427
|
-
console.log(JSON.stringify(
|
|
12428
|
-
return EXIT.SUCCESS;
|
|
12429
|
-
}
|
|
12430
|
-
render.section(`${clay("activity")} — ${ws.totals.events} ${dim(flags.since ? `since ${flags.since}` : "in the last 24h")}`);
|
|
12431
|
-
if (ws.activity.length === 0) {
|
|
12432
|
-
process.stdout.write(dim(` quiet — no activity in window
|
|
12433
|
-
`));
|
|
12471
|
+
console.log(JSON.stringify({ ...result, bodyVersion, mentions }));
|
|
12434
12472
|
return EXIT.SUCCESS;
|
|
12435
12473
|
}
|
|
12436
|
-
const
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
const topic = cyan(`#${a.topicName}`);
|
|
12440
|
-
const sender = a.senderName ?? "?";
|
|
12441
|
-
const ago = formatRelativeTime(a.createdAt);
|
|
12442
|
-
const snippet = a.snippet ?? (a.ciphertext ? dim("[encrypted]") : dim("[empty]"));
|
|
12443
|
-
process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
|
|
12444
|
-
` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
|
|
12445
|
-
`);
|
|
12446
|
-
}
|
|
12474
|
+
const versionTag = bodyVersion === 2 ? green("\uD83D\uDD12 v2") : dim("v1");
|
|
12475
|
+
const replyTag = result.replyToId ? ` ${dim("↳ " + result.replyToId.slice(0, 8))}` : "";
|
|
12476
|
+
render.ok("posted", `${clay("#" + cleanName)} ${versionTag}${replyTag} ${dim(`(${result.notifications} mentions)`)}`);
|
|
12447
12477
|
return EXIT.SUCCESS;
|
|
12448
12478
|
});
|
|
12449
12479
|
}
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12480
|
+
var init_topic_post = __esm(() => {
|
|
12481
|
+
init_with_rest_key();
|
|
12482
|
+
init_client();
|
|
12483
|
+
init_topic_key();
|
|
12484
|
+
init_render();
|
|
12485
|
+
init_styles();
|
|
12486
|
+
init_exit_codes();
|
|
12487
|
+
});
|
|
12488
|
+
|
|
12489
|
+
// src/commands/notification.ts
|
|
12490
|
+
var exports_notification = {};
|
|
12491
|
+
__export(exports_notification, {
|
|
12492
|
+
runNotificationList: () => runNotificationList
|
|
12493
|
+
});
|
|
12494
|
+
function decodeCiphertext(b64) {
|
|
12495
|
+
try {
|
|
12496
|
+
return Buffer.from(b64, "base64").toString("utf-8");
|
|
12497
|
+
} catch {
|
|
12498
|
+
return "[decode failed]";
|
|
12455
12499
|
}
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12500
|
+
}
|
|
12501
|
+
function fmtRelative(iso) {
|
|
12502
|
+
const ms = Date.now() - new Date(iso).getTime();
|
|
12503
|
+
if (ms < 60000)
|
|
12504
|
+
return "now";
|
|
12505
|
+
if (ms < 3600000)
|
|
12506
|
+
return `${Math.floor(ms / 60000)}m`;
|
|
12507
|
+
if (ms < 86400000)
|
|
12508
|
+
return `${Math.floor(ms / 3600000)}h`;
|
|
12509
|
+
return `${Math.floor(ms / 86400000)}d`;
|
|
12510
|
+
}
|
|
12511
|
+
async function runNotificationList(flags) {
|
|
12512
|
+
return withRestKey({ meshSlug: flags.mesh ?? null, purpose: "notifications" }, async ({ secret }) => {
|
|
12513
|
+
const qs = flags.since ? `?since=${encodeURIComponent(flags.since)}` : "";
|
|
12514
|
+
const result = await request({
|
|
12515
|
+
path: `/api/v1/notifications${qs}`,
|
|
12464
12516
|
token: secret
|
|
12465
12517
|
});
|
|
12466
12518
|
if (flags.json) {
|
|
12467
|
-
|
|
12519
|
+
const decoded = result.notifications.map((n) => ({
|
|
12520
|
+
...n,
|
|
12521
|
+
message: decodeCiphertext(n.ciphertext)
|
|
12522
|
+
}));
|
|
12523
|
+
console.log(JSON.stringify({ ...result, notifications: decoded }, null, 2));
|
|
12468
12524
|
return EXIT.SUCCESS;
|
|
12469
12525
|
}
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
process.stdout.write(dim(` no matches
|
|
12473
|
-
`));
|
|
12526
|
+
if (result.notifications.length === 0) {
|
|
12527
|
+
render.info(dim(`no mentions of @${result.mentionedAs} since ${result.since}.`));
|
|
12474
12528
|
return EXIT.SUCCESS;
|
|
12475
12529
|
}
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
const
|
|
12481
|
-
|
|
12482
|
-
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
12483
|
-
const name = cyan(`#${t.name}`);
|
|
12484
|
-
const desc = t.description ? dim(` — ${t.description}`) : "";
|
|
12485
|
-
process.stdout.write(` ${slug} ${name}${desc}
|
|
12530
|
+
render.section(`mentions of @${bold(result.mentionedAs)} (${result.notifications.length})`);
|
|
12531
|
+
for (const n of result.notifications) {
|
|
12532
|
+
const when = fmtRelative(n.createdAt);
|
|
12533
|
+
const msg = decodeCiphertext(n.ciphertext).replace(/\s+/g, " ").trim();
|
|
12534
|
+
const snippet = msg.length > 100 ? msg.slice(0, 97) + "…" : msg;
|
|
12535
|
+
process.stdout.write(` ${clay("#" + n.topicName)} ${dim(when)} ${bold(n.senderName)}
|
|
12486
12536
|
`);
|
|
12487
|
-
}
|
|
12488
|
-
}
|
|
12489
|
-
if (ws.messages.length > 0) {
|
|
12490
|
-
process.stdout.write(dim(`
|
|
12491
|
-
messages
|
|
12492
|
-
`));
|
|
12493
|
-
const slugWidth = Math.max(...ws.messages.map((m) => m.meshSlug.length), 6);
|
|
12494
|
-
for (const m of ws.messages) {
|
|
12495
|
-
const slug = dim(m.meshSlug.padEnd(slugWidth));
|
|
12496
|
-
const topic = cyan(`#${m.topicName}`);
|
|
12497
|
-
const sender = m.senderName;
|
|
12498
|
-
const ago = formatRelativeTime(m.createdAt);
|
|
12499
|
-
const snippet = m.snippet ?? (m.bodyVersion === 2 ? dim("[encrypted — open the topic to decrypt]") : dim("[empty]"));
|
|
12500
|
-
const highlighted = m.snippet ? highlightMatch(snippet, flags.query) : snippet;
|
|
12501
|
-
process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
|
|
12502
|
-
` + ` ${highlighted}
|
|
12537
|
+
process.stdout.write(` ${snippet}
|
|
12503
12538
|
`);
|
|
12504
|
-
}
|
|
12505
12539
|
}
|
|
12506
12540
|
return EXIT.SUCCESS;
|
|
12507
12541
|
});
|
|
12508
12542
|
}
|
|
12509
|
-
|
|
12510
|
-
if (!query)
|
|
12511
|
-
return text;
|
|
12512
|
-
const idx = text.toLowerCase().indexOf(query.toLowerCase());
|
|
12513
|
-
if (idx === -1)
|
|
12514
|
-
return text;
|
|
12515
|
-
const before = text.slice(0, idx);
|
|
12516
|
-
const match = text.slice(idx, idx + query.length);
|
|
12517
|
-
const after = text.slice(idx + query.length);
|
|
12518
|
-
return `${before}${yellow(match)}${after}`;
|
|
12519
|
-
}
|
|
12520
|
-
function formatRelativeTime(iso) {
|
|
12521
|
-
const then = new Date(iso).getTime();
|
|
12522
|
-
const now = Date.now();
|
|
12523
|
-
const sec = Math.max(0, Math.floor((now - then) / 1000));
|
|
12524
|
-
if (sec < 60)
|
|
12525
|
-
return `${sec}s ago`;
|
|
12526
|
-
if (sec < 3600)
|
|
12527
|
-
return `${Math.floor(sec / 60)}m ago`;
|
|
12528
|
-
if (sec < 86400)
|
|
12529
|
-
return `${Math.floor(sec / 3600)}h ago`;
|
|
12530
|
-
if (sec < 86400 * 30)
|
|
12531
|
-
return `${Math.floor(sec / 86400)}d ago`;
|
|
12532
|
-
if (sec < 86400 * 365)
|
|
12533
|
-
return `${Math.floor(sec / (86400 * 30))}mo ago`;
|
|
12534
|
-
return `${Math.floor(sec / (86400 * 365))}y ago`;
|
|
12535
|
-
}
|
|
12536
|
-
var init_me = __esm(() => {
|
|
12543
|
+
var init_notification = __esm(() => {
|
|
12537
12544
|
init_with_rest_key();
|
|
12538
12545
|
init_client();
|
|
12539
12546
|
init_render();
|
|
@@ -14171,7 +14178,7 @@ Bridge (forward a topic between two meshes, v0.2.0)
|
|
|
14171
14178
|
|
|
14172
14179
|
Topic (conversation scope, v0.2.0)
|
|
14173
14180
|
claudemesh topic create <name> create a topic [--description --visibility]
|
|
14174
|
-
claudemesh topic list list topics
|
|
14181
|
+
claudemesh topic list list topics across all meshes (or --mesh <slug>)
|
|
14175
14182
|
claudemesh topic join <topic> subscribe (via name or id)
|
|
14176
14183
|
claudemesh topic leave <topic> unsubscribe
|
|
14177
14184
|
claudemesh topic members <t> list topic subscribers
|
|
@@ -14186,7 +14193,7 @@ Topic (conversation scope, v0.2.0)
|
|
|
14186
14193
|
claudemesh me activity cross-mesh recent messages [--since=ISO]
|
|
14187
14194
|
claudemesh me search <q> cross-mesh search (topics + messages)
|
|
14188
14195
|
claudemesh member list mesh roster with online state [--online]
|
|
14189
|
-
claudemesh notification list
|
|
14196
|
+
claudemesh notification list @-mentions across all meshes (or --mesh <slug>)
|
|
14190
14197
|
|
|
14191
14198
|
Schedule (resource form)
|
|
14192
14199
|
claudemesh schedule msg <m> one-shot or recurring (alias: remind)
|
|
@@ -14983,6 +14990,10 @@ async function main() {
|
|
|
14983
14990
|
const { runTopicCreate: runTopicCreate2 } = await Promise.resolve().then(() => (init_topic(), exports_topic));
|
|
14984
14991
|
process.exit(await runTopicCreate2(arg, f));
|
|
14985
14992
|
} else if (sub === "list") {
|
|
14993
|
+
if (!f.mesh) {
|
|
14994
|
+
const { runMeTopics: runMeTopics2 } = await Promise.resolve().then(() => (init_me(), exports_me));
|
|
14995
|
+
process.exit(await runMeTopics2({ mesh: undefined, json: f.json, unread: !!flags.unread }));
|
|
14996
|
+
}
|
|
14986
14997
|
const { runTopicList: runTopicList2 } = await Promise.resolve().then(() => (init_topic(), exports_topic));
|
|
14987
14998
|
process.exit(await runTopicList2(f));
|
|
14988
14999
|
} else if (sub === "join") {
|
|
@@ -15034,10 +15045,19 @@ async function main() {
|
|
|
15034
15045
|
since: flags.since
|
|
15035
15046
|
};
|
|
15036
15047
|
if (sub === "list") {
|
|
15048
|
+
if (!f.mesh) {
|
|
15049
|
+
const { runMeNotifications: runMeNotifications2 } = await Promise.resolve().then(() => (init_me(), exports_me));
|
|
15050
|
+
process.exit(await runMeNotifications2({
|
|
15051
|
+
mesh: undefined,
|
|
15052
|
+
json: f.json,
|
|
15053
|
+
all: !!flags.all,
|
|
15054
|
+
since: f.since
|
|
15055
|
+
}));
|
|
15056
|
+
}
|
|
15037
15057
|
const { runNotificationList: runNotificationList2 } = await Promise.resolve().then(() => (init_notification(), exports_notification));
|
|
15038
15058
|
process.exit(await runNotificationList2(f));
|
|
15039
15059
|
} else {
|
|
15040
|
-
console.error("Usage: claudemesh notification list [--since <ISO>]");
|
|
15060
|
+
console.error("Usage: claudemesh notification list [--mesh <slug>] [--since <ISO>] [--all]");
|
|
15041
15061
|
process.exit(EXIT.INVALID_ARGS);
|
|
15042
15062
|
}
|
|
15043
15063
|
break;
|
|
@@ -15150,4 +15170,4 @@ main().catch((err) => {
|
|
|
15150
15170
|
process.exit(EXIT.INTERNAL_ERROR);
|
|
15151
15171
|
});
|
|
15152
15172
|
|
|
15153
|
-
//# debugId=
|
|
15173
|
+
//# debugId=3168504A6207F58264756E2164756E21
|