claudemesh-cli 1.15.0 → 1.17.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.15.0", env;
91
+ var URLS, VERSION = "1.17.0", env;
92
92
  var init_urls = __esm(() => {
93
93
  URLS = {
94
94
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -6698,18 +6698,27 @@ function projectFields(record, fields) {
6698
6698
  return out;
6699
6699
  }
6700
6700
  async function listPeersForMesh(slug) {
6701
+ const config = readConfig();
6702
+ const joined = config.meshes.find((m) => m.slug === slug);
6703
+ const selfMemberPubkey = joined?.pubkey ?? null;
6701
6704
  const bridged = await tryBridge(slug, "peers");
6702
6705
  if (bridged && bridged.ok) {
6703
- return bridged.result;
6706
+ const peers = bridged.result;
6707
+ return peers.map((p) => annotateSelf(p, selfMemberPubkey, null));
6704
6708
  }
6705
6709
  let result = [];
6706
6710
  await withMesh({ meshSlug: slug }, async (client) => {
6707
6711
  const all = await client.listPeers();
6708
- const selfPubkey = client.getSessionPubkey();
6709
- result = selfPubkey ? all.filter((p) => p.pubkey !== selfPubkey) : all;
6712
+ const selfSessionPubkey = client.getSessionPubkey();
6713
+ result = all.map((p) => annotateSelf(p, selfMemberPubkey, selfSessionPubkey));
6710
6714
  });
6711
6715
  return result;
6712
6716
  }
6717
+ function annotateSelf(peer, selfMemberPubkey, selfSessionPubkey) {
6718
+ const isSelf = !!(selfMemberPubkey && peer.memberPubkey && peer.memberPubkey === selfMemberPubkey);
6719
+ const isThisSession = !!(isSelf && selfSessionPubkey && peer.pubkey === selfSessionPubkey);
6720
+ return { ...peer, isSelf, isThisSession };
6721
+ }
6713
6722
  async function runPeers(flags) {
6714
6723
  const config = readConfig();
6715
6724
  const slugs = flags.mesh ? [flags.mesh] : config.meshes.map((m) => m.slug);
@@ -6748,7 +6757,8 @@ async function runPeers(flags) {
6748
6757
  const metaStr = meta.length ? dim(` (${meta.join(", ")})`) : "";
6749
6758
  const summary = p.summary ? dim(` — ${p.summary}`) : "";
6750
6759
  const pubkeyTag = dim(` · ${p.pubkey.slice(0, 16)}…`);
6751
- render.info(`${statusDot} ${name}${groups}${metaStr}${pubkeyTag}${summary}`);
6760
+ const selfTag = p.isThisSession ? dim(" ") + yellow("(this session)") : p.isSelf ? dim(" ") + yellow("(your other session)") : "";
6761
+ render.info(`${statusDot} ${name}${selfTag}${groups}${metaStr}${pubkeyTag}${summary}`);
6752
6762
  if (p.cwd)
6753
6763
  render.info(dim(` cwd: ${p.cwd}`));
6754
6764
  }
@@ -6786,6 +6796,14 @@ async function runSend(flags, to, message) {
6786
6796
  const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
6787
6797
  const config = readConfig();
6788
6798
  const meshSlug = flags.mesh ?? (config.meshes.length === 1 ? config.meshes[0].slug : null);
6799
+ if (!flags.self && meshSlug) {
6800
+ const joined = config.meshes.find((m) => m.slug === meshSlug);
6801
+ if (joined && /^[0-9a-f]{64}$/i.test(to) && to.toLowerCase() === joined.pubkey.toLowerCase()) {
6802
+ render.err(`Target "${to.slice(0, 16)}…" is your own member pubkey on mesh "${meshSlug}".`);
6803
+ render.hint("Pass --self to message a sibling session of your own member, or pick a different peer's pubkey.");
6804
+ process.exit(1);
6805
+ }
6806
+ }
6789
6807
  if (meshSlug) {
6790
6808
  const bridged = await tryBridge(meshSlug, "send", { to, message, priority });
6791
6809
  if (bridged !== null) {
@@ -6950,10 +6968,420 @@ async function runStateList(flags) {
6950
6968
  }
6951
6969
  });
6952
6970
  }
6953
- var init_state = __esm(() => {
6954
- init_connect();
6971
+ var init_state = __esm(() => {
6972
+ init_connect();
6973
+ init_render();
6974
+ init_styles();
6975
+ });
6976
+
6977
+ // src/services/api/with-rest-key.ts
6978
+ async function withRestKey(opts, fn) {
6979
+ return withMesh({ meshSlug: opts.meshSlug ?? null }, async (client, mesh) => {
6980
+ const result = await client.apiKeyCreate({
6981
+ label: `cli-${opts.purpose ?? "rest"}-${process.pid}`,
6982
+ capabilities: opts.capabilities ?? ["read"],
6983
+ topicScopes: opts.topicScopes ?? undefined,
6984
+ expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString()
6985
+ });
6986
+ if (!result || !result.secret) {
6987
+ throw new Error("apikey mint failed — broker did not return a secret");
6988
+ }
6989
+ try {
6990
+ return await fn({
6991
+ secret: result.secret,
6992
+ meshId: mesh.meshId,
6993
+ meshSlug: mesh.slug,
6994
+ client,
6995
+ mesh
6996
+ });
6997
+ } finally {
6998
+ try {
6999
+ await client.apiKeyRevoke(result.id);
7000
+ } catch {}
7001
+ }
7002
+ });
7003
+ }
7004
+ var init_with_rest_key = __esm(() => {
7005
+ init_connect();
7006
+ });
7007
+
7008
+ // src/commands/me.ts
7009
+ var exports_me = {};
7010
+ __export(exports_me, {
7011
+ runMeTopics: () => runMeTopics,
7012
+ runMeTasks: () => runMeTasks,
7013
+ runMeState: () => runMeState,
7014
+ runMeSearch: () => runMeSearch,
7015
+ runMeNotifications: () => runMeNotifications,
7016
+ runMeMemory: () => runMeMemory,
7017
+ runMeActivity: () => runMeActivity,
7018
+ runMe: () => runMe
7019
+ });
7020
+ function resolveMeshForMint(explicit) {
7021
+ if (explicit)
7022
+ return explicit;
7023
+ const cfg = readConfig();
7024
+ return cfg.meshes[0]?.slug ?? null;
7025
+ }
7026
+ async function runMe(flags) {
7027
+ return withRestKey({
7028
+ meshSlug: resolveMeshForMint(flags.mesh),
7029
+ purpose: "workspace-overview",
7030
+ capabilities: ["read"]
7031
+ }, async ({ secret }) => {
7032
+ const ws = await request({
7033
+ path: "/api/v1/me/workspace",
7034
+ token: secret
7035
+ });
7036
+ if (flags.json) {
7037
+ console.log(JSON.stringify(ws, null, 2));
7038
+ return EXIT.SUCCESS;
7039
+ }
7040
+ render.section(`${clay("workspace")} — ${bold(ws.userId.slice(0, 8))} ${dim(`· ${ws.totals.meshes} mesh${ws.totals.meshes === 1 ? "" : "es"}`)}`);
7041
+ const totalsLine = [
7042
+ `${green(String(ws.totals.online))}/${ws.totals.peers} online`,
7043
+ `${ws.totals.topics} topic${ws.totals.topics === 1 ? "" : "s"}`,
7044
+ ws.totals.unreadMentions > 0 ? yellow(`${ws.totals.unreadMentions} unread @you`) : dim("0 unread @you")
7045
+ ].join(dim(" · "));
7046
+ process.stdout.write(" " + totalsLine + `
7047
+
7048
+ `);
7049
+ if (ws.meshes.length === 0) {
7050
+ process.stdout.write(dim(" no meshes joined — run `claudemesh new` or accept an invite\n"));
7051
+ return EXIT.SUCCESS;
7052
+ }
7053
+ const slugWidth = Math.max(...ws.meshes.map((m) => m.slug.length), 8);
7054
+ for (const m of ws.meshes) {
7055
+ const slug = cyan(m.slug.padEnd(slugWidth));
7056
+ const peers = `${m.online}/${m.peers}`;
7057
+ const role = dim(m.myRole);
7058
+ const unread = m.unreadMentions > 0 ? " " + yellow(`${m.unreadMentions} @you`) : "";
7059
+ process.stdout.write(` ${slug} ${peers.padStart(5)} online ${dim(String(m.topics).padStart(2) + " topics")} ${role}${unread}
7060
+ `);
7061
+ }
7062
+ return EXIT.SUCCESS;
7063
+ });
7064
+ }
7065
+ async function runMeTopics(flags) {
7066
+ return withRestKey({
7067
+ meshSlug: resolveMeshForMint(flags.mesh),
7068
+ purpose: "workspace-topics",
7069
+ capabilities: ["read"]
7070
+ }, async ({ secret }) => {
7071
+ const ws = await request({
7072
+ path: "/api/v1/me/topics",
7073
+ token: secret
7074
+ });
7075
+ const visible = flags.unread ? ws.topics.filter((t) => t.unread > 0) : ws.topics;
7076
+ if (flags.json) {
7077
+ console.log(JSON.stringify({ topics: visible, totals: ws.totals }, null, 2));
7078
+ return EXIT.SUCCESS;
7079
+ }
7080
+ render.section(`${clay("topics")} — ${ws.totals.topics} across all meshes ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· all read")}`);
7081
+ if (visible.length === 0) {
7082
+ process.stdout.write(dim(flags.unread ? ` no unread topics
7083
+ ` : " no topics — run `claudemesh topic create #general`\n"));
7084
+ return EXIT.SUCCESS;
7085
+ }
7086
+ const slugWidth = Math.max(...visible.map((t) => t.meshSlug.length), 6);
7087
+ const nameWidth = Math.max(...visible.map((t) => t.name.length), 8);
7088
+ for (const t of visible) {
7089
+ const slug = dim(t.meshSlug.padEnd(slugWidth));
7090
+ const name = cyan(t.name.padEnd(nameWidth));
7091
+ const unread = t.unread > 0 ? yellow(`${t.unread} unread`.padStart(10)) : dim("·".padStart(10));
7092
+ const last = t.lastMessageAt ? dim(formatRelativeTime(t.lastMessageAt)) : dim("never");
7093
+ process.stdout.write(` ${slug} ${name} ${unread} ${last}
7094
+ `);
7095
+ }
7096
+ return EXIT.SUCCESS;
7097
+ });
7098
+ }
7099
+ async function runMeNotifications(flags) {
7100
+ return withRestKey({
7101
+ meshSlug: resolveMeshForMint(flags.mesh),
7102
+ purpose: "workspace-notifications",
7103
+ capabilities: ["read"]
7104
+ }, async ({ secret }) => {
7105
+ const params = new URLSearchParams;
7106
+ if (flags.all)
7107
+ params.set("include", "all");
7108
+ if (flags.since)
7109
+ params.set("since", flags.since);
7110
+ const path = "/api/v1/me/notifications" + (params.toString() ? `?${params.toString()}` : "");
7111
+ const ws = await request({
7112
+ path,
7113
+ token: secret
7114
+ });
7115
+ if (flags.json) {
7116
+ console.log(JSON.stringify(ws, null, 2));
7117
+ return EXIT.SUCCESS;
7118
+ }
7119
+ const headerLabel = flags.all ? "@-mentions (all)" : "@-mentions (unread)";
7120
+ render.section(`${clay(headerLabel)} — ${ws.totals.total} ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· nothing pending")}`);
7121
+ if (ws.notifications.length === 0) {
7122
+ process.stdout.write(dim(flags.all ? ` no @-mentions in window
7123
+ ` : ` inbox zero — nothing waiting
7124
+ `));
7125
+ return EXIT.SUCCESS;
7126
+ }
7127
+ const slugWidth = Math.max(...ws.notifications.map((n) => n.meshSlug.length), 6);
7128
+ for (const n of ws.notifications) {
7129
+ const slug = dim(n.meshSlug.padEnd(slugWidth));
7130
+ const topic = cyan(`#${n.topicName}`);
7131
+ const sender = n.senderName ? `from ${n.senderName}` : "from ?";
7132
+ const ago = formatRelativeTime(n.createdAt);
7133
+ const dot = n.read ? dim("·") : yellow("●");
7134
+ const snippet = n.snippet ?? (n.ciphertext ? dim("[encrypted]") : dim("[empty]"));
7135
+ process.stdout.write(` ${dot} ${slug} ${topic} ${dim(sender)} ${dim(ago)}
7136
+ ` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
7137
+ `);
7138
+ }
7139
+ return EXIT.SUCCESS;
7140
+ });
7141
+ }
7142
+ async function runMeActivity(flags) {
7143
+ return withRestKey({
7144
+ meshSlug: resolveMeshForMint(flags.mesh),
7145
+ purpose: "workspace-activity",
7146
+ capabilities: ["read"]
7147
+ }, async ({ secret }) => {
7148
+ const params = new URLSearchParams;
7149
+ if (flags.since)
7150
+ params.set("since", flags.since);
7151
+ const path = "/api/v1/me/activity" + (params.toString() ? `?${params.toString()}` : "");
7152
+ const ws = await request({
7153
+ path,
7154
+ token: secret
7155
+ });
7156
+ if (flags.json) {
7157
+ console.log(JSON.stringify(ws, null, 2));
7158
+ return EXIT.SUCCESS;
7159
+ }
7160
+ render.section(`${clay("activity")} — ${ws.totals.events} ${dim(flags.since ? `since ${flags.since}` : "in the last 24h")}`);
7161
+ if (ws.activity.length === 0) {
7162
+ process.stdout.write(dim(` quiet — no activity in window
7163
+ `));
7164
+ return EXIT.SUCCESS;
7165
+ }
7166
+ const slugWidth = Math.max(...ws.activity.map((a) => a.meshSlug.length), 6);
7167
+ for (const a of ws.activity) {
7168
+ const slug = dim(a.meshSlug.padEnd(slugWidth));
7169
+ const topic = cyan(`#${a.topicName}`);
7170
+ const sender = a.senderName ?? "?";
7171
+ const ago = formatRelativeTime(a.createdAt);
7172
+ const snippet = a.snippet ?? (a.ciphertext ? dim("[encrypted]") : dim("[empty]"));
7173
+ process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
7174
+ ` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
7175
+ `);
7176
+ }
7177
+ return EXIT.SUCCESS;
7178
+ });
7179
+ }
7180
+ async function runMeSearch(flags) {
7181
+ if (!flags.query || flags.query.length < 2) {
7182
+ process.stderr.write(`Usage: claudemesh me search <query> (min 2 chars)
7183
+ `);
7184
+ return EXIT.INVALID_ARGS;
7185
+ }
7186
+ return withRestKey({
7187
+ meshSlug: resolveMeshForMint(flags.mesh),
7188
+ purpose: "workspace-search",
7189
+ capabilities: ["read"]
7190
+ }, async ({ secret }) => {
7191
+ const params = new URLSearchParams({ q: flags.query });
7192
+ const ws = await request({
7193
+ path: `/api/v1/me/search?${params.toString()}`,
7194
+ token: secret
7195
+ });
7196
+ if (flags.json) {
7197
+ console.log(JSON.stringify(ws, null, 2));
7198
+ return EXIT.SUCCESS;
7199
+ }
7200
+ 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"}`)}`);
7201
+ if (ws.topics.length === 0 && ws.messages.length === 0) {
7202
+ process.stdout.write(dim(` no matches
7203
+ `));
7204
+ return EXIT.SUCCESS;
7205
+ }
7206
+ if (ws.topics.length > 0) {
7207
+ process.stdout.write(dim(`
7208
+ topics
7209
+ `));
7210
+ const slugWidth = Math.max(...ws.topics.map((t) => t.meshSlug.length), 6);
7211
+ for (const t of ws.topics) {
7212
+ const slug = dim(t.meshSlug.padEnd(slugWidth));
7213
+ const name = cyan(`#${t.name}`);
7214
+ const desc = t.description ? dim(` — ${t.description}`) : "";
7215
+ process.stdout.write(` ${slug} ${name}${desc}
7216
+ `);
7217
+ }
7218
+ }
7219
+ if (ws.messages.length > 0) {
7220
+ process.stdout.write(dim(`
7221
+ messages
7222
+ `));
7223
+ const slugWidth = Math.max(...ws.messages.map((m) => m.meshSlug.length), 6);
7224
+ for (const m of ws.messages) {
7225
+ const slug = dim(m.meshSlug.padEnd(slugWidth));
7226
+ const topic = cyan(`#${m.topicName}`);
7227
+ const sender = m.senderName;
7228
+ const ago = formatRelativeTime(m.createdAt);
7229
+ const snippet = m.snippet ?? (m.bodyVersion === 2 ? dim("[encrypted — open the topic to decrypt]") : dim("[empty]"));
7230
+ const highlighted = m.snippet ? highlightMatch(snippet, flags.query) : snippet;
7231
+ process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
7232
+ ` + ` ${highlighted}
7233
+ `);
7234
+ }
7235
+ }
7236
+ return EXIT.SUCCESS;
7237
+ });
7238
+ }
7239
+ function highlightMatch(text, query) {
7240
+ if (!query)
7241
+ return text;
7242
+ const idx = text.toLowerCase().indexOf(query.toLowerCase());
7243
+ if (idx === -1)
7244
+ return text;
7245
+ const before = text.slice(0, idx);
7246
+ const match = text.slice(idx, idx + query.length);
7247
+ const after = text.slice(idx + query.length);
7248
+ return `${before}${yellow(match)}${after}`;
7249
+ }
7250
+ async function runMeTasks(flags) {
7251
+ return withRestKey({
7252
+ meshSlug: resolveMeshForMint(flags.mesh),
7253
+ purpose: "workspace-tasks",
7254
+ capabilities: ["read"]
7255
+ }, async ({ secret }) => {
7256
+ const params = new URLSearchParams;
7257
+ if (flags.status)
7258
+ params.set("status", flags.status);
7259
+ const path = "/api/v1/me/tasks" + (params.toString() ? `?${params.toString()}` : "");
7260
+ const ws = await request({
7261
+ path,
7262
+ token: secret
7263
+ });
7264
+ if (flags.json) {
7265
+ console.log(JSON.stringify(ws, null, 2));
7266
+ return EXIT.SUCCESS;
7267
+ }
7268
+ render.section(`${clay("tasks")} — ${dim(`${ws.totals.open} open · ${ws.totals.claimed} in-flight · ${ws.totals.completed} done`)}`);
7269
+ if (ws.tasks.length === 0) {
7270
+ process.stdout.write(dim(` no tasks in window
7271
+ `));
7272
+ return EXIT.SUCCESS;
7273
+ }
7274
+ const slugWidth = Math.max(...ws.tasks.map((t) => t.meshSlug.length), 6);
7275
+ for (const t of ws.tasks) {
7276
+ const slug = dim(t.meshSlug.padEnd(slugWidth));
7277
+ const status = t.status === "open" ? yellow("open ") : t.status === "claimed" ? cyan("working ") : green("done ");
7278
+ const prio = t.priority === "urgent" ? yellow("!") : t.priority === "low" ? dim("·") : " ";
7279
+ const claimer = t.claimedByName ? dim(` ← ${t.claimedByName}`) : "";
7280
+ process.stdout.write(` ${slug} ${prio} ${status} ${t.title}${claimer}
7281
+ `);
7282
+ }
7283
+ return EXIT.SUCCESS;
7284
+ });
7285
+ }
7286
+ async function runMeState(flags) {
7287
+ return withRestKey({
7288
+ meshSlug: resolveMeshForMint(flags.mesh),
7289
+ purpose: "workspace-state",
7290
+ capabilities: ["read"]
7291
+ }, async ({ secret }) => {
7292
+ const params = new URLSearchParams;
7293
+ if (flags.key)
7294
+ params.set("key", flags.key);
7295
+ const path = "/api/v1/me/state" + (params.toString() ? `?${params.toString()}` : "");
7296
+ const ws = await request({
7297
+ path,
7298
+ token: secret
7299
+ });
7300
+ if (flags.json) {
7301
+ console.log(JSON.stringify(ws, null, 2));
7302
+ return EXIT.SUCCESS;
7303
+ }
7304
+ render.section(`${clay("state")} — ${ws.totals.entries} entr${ws.totals.entries === 1 ? "y" : "ies"} ${dim(`across ${ws.totals.meshes} mesh${ws.totals.meshes === 1 ? "" : "es"}`)}`);
7305
+ if (ws.entries.length === 0) {
7306
+ process.stdout.write(dim(` no state entries
7307
+ `));
7308
+ return EXIT.SUCCESS;
7309
+ }
7310
+ const slugWidth = Math.max(...ws.entries.map((e) => e.meshSlug.length), 6);
7311
+ const keyWidth = Math.max(...ws.entries.map((e) => e.key.length), 8);
7312
+ for (const e of ws.entries) {
7313
+ const slug = dim(e.meshSlug.padEnd(slugWidth));
7314
+ const key = cyan(e.key.padEnd(keyWidth));
7315
+ const valueStr = typeof e.value === "string" ? e.value : JSON.stringify(e.value);
7316
+ const trimmed = valueStr.length > 80 ? valueStr.slice(0, 80) + "…" : valueStr;
7317
+ const ago = dim(formatRelativeTime(e.updatedAt));
7318
+ process.stdout.write(` ${slug} ${key} ${trimmed} ${ago}
7319
+ `);
7320
+ }
7321
+ return EXIT.SUCCESS;
7322
+ });
7323
+ }
7324
+ async function runMeMemory(flags) {
7325
+ return withRestKey({
7326
+ meshSlug: resolveMeshForMint(flags.mesh),
7327
+ purpose: "workspace-memory",
7328
+ capabilities: ["read"]
7329
+ }, async ({ secret }) => {
7330
+ const params = new URLSearchParams;
7331
+ if (flags.query)
7332
+ params.set("q", flags.query);
7333
+ const path = "/api/v1/me/memory" + (params.toString() ? `?${params.toString()}` : "");
7334
+ const ws = await request({
7335
+ path,
7336
+ token: secret
7337
+ });
7338
+ if (flags.json) {
7339
+ console.log(JSON.stringify(ws, null, 2));
7340
+ return EXIT.SUCCESS;
7341
+ }
7342
+ const headerLabel = flags.query ? `recall — "${flags.query}"` : "recall — last 30 days";
7343
+ render.section(`${clay(headerLabel)} ${dim(`${ws.totals.entries} match${ws.totals.entries === 1 ? "" : "es"}`)}`);
7344
+ if (ws.memories.length === 0) {
7345
+ process.stdout.write(dim(` no memories
7346
+ `));
7347
+ return EXIT.SUCCESS;
7348
+ }
7349
+ const slugWidth = Math.max(...ws.memories.map((m) => m.meshSlug.length), 6);
7350
+ for (const m of ws.memories) {
7351
+ const slug = dim(m.meshSlug.padEnd(slugWidth));
7352
+ const ago = dim(formatRelativeTime(m.rememberedAt));
7353
+ const tags = m.tags.length > 0 ? " " + dim("[" + m.tags.join(", ") + "]") : "";
7354
+ const content = m.content.length > 240 ? m.content.slice(0, 240) + "…" : m.content;
7355
+ process.stdout.write(` ${slug} ${ago}${tags}
7356
+ ${content}
7357
+ `);
7358
+ }
7359
+ return EXIT.SUCCESS;
7360
+ });
7361
+ }
7362
+ function formatRelativeTime(iso) {
7363
+ const then = new Date(iso).getTime();
7364
+ const now = Date.now();
7365
+ const sec = Math.max(0, Math.floor((now - then) / 1000));
7366
+ if (sec < 60)
7367
+ return `${sec}s ago`;
7368
+ if (sec < 3600)
7369
+ return `${Math.floor(sec / 60)}m ago`;
7370
+ if (sec < 86400)
7371
+ return `${Math.floor(sec / 3600)}h ago`;
7372
+ if (sec < 86400 * 30)
7373
+ return `${Math.floor(sec / 86400)}d ago`;
7374
+ if (sec < 86400 * 365)
7375
+ return `${Math.floor(sec / (86400 * 30))}mo ago`;
7376
+ return `${Math.floor(sec / (86400 * 365))}y ago`;
7377
+ }
7378
+ var init_me = __esm(() => {
7379
+ init_with_rest_key();
7380
+ init_client();
7381
+ init_facade();
6955
7382
  init_render();
6956
7383
  init_styles();
7384
+ init_exit_codes();
6957
7385
  });
6958
7386
 
6959
7387
  // src/commands/info.ts
@@ -11590,441 +12018,146 @@ async function runTopicCreate(name, flags) {
11590
12018
  }
11591
12019
  return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11592
12020
  const result = await client.topicCreate({
11593
- name,
11594
- description: flags.description,
11595
- visibility: flags.visibility
11596
- });
11597
- if (!result) {
11598
- render.err("topic create failed");
11599
- return EXIT.INTERNAL_ERROR;
11600
- }
11601
- if (flags.json) {
11602
- console.log(JSON.stringify(result));
11603
- return EXIT.SUCCESS;
11604
- }
11605
- if (result.created) {
11606
- render.ok("created", `${clay("#" + name)} ${dim(result.id.slice(0, 8))}`);
11607
- } else {
11608
- render.info(dim(`already exists: #${name} ${result.id.slice(0, 8)}`));
11609
- }
11610
- return EXIT.SUCCESS;
11611
- });
11612
- }
11613
- async function runTopicList(flags) {
11614
- return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11615
- const topics = await client.topicList();
11616
- if (flags.json) {
11617
- console.log(JSON.stringify(topics, null, 2));
11618
- return EXIT.SUCCESS;
11619
- }
11620
- if (topics.length === 0) {
11621
- render.info(dim("no topics in this mesh."));
11622
- return EXIT.SUCCESS;
11623
- }
11624
- render.section(`topics (${topics.length})`);
11625
- for (const t of topics) {
11626
- const vis = t.visibility === "public" ? green(t.visibility) : dim(t.visibility);
11627
- process.stdout.write(` ${clay("#" + t.name)} ${vis} ${dim(`${t.memberCount} member${t.memberCount === 1 ? "" : "s"}`)}
11628
- `);
11629
- if (t.description)
11630
- process.stdout.write(` ${dim(t.description)}
11631
- `);
11632
- }
11633
- return EXIT.SUCCESS;
11634
- });
11635
- }
11636
- async function runTopicJoin(topic, flags) {
11637
- if (!topic) {
11638
- render.err("Usage: claudemesh topic join <topic> [--role lead|member|observer]");
11639
- return EXIT.INVALID_ARGS;
11640
- }
11641
- return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11642
- await client.topicJoin(topic, flags.role);
11643
- if (flags.json)
11644
- console.log(JSON.stringify({ joined: topic }));
11645
- else
11646
- render.ok("joined", clay("#" + topic));
11647
- return EXIT.SUCCESS;
11648
- });
11649
- }
11650
- async function runTopicLeave(topic, flags) {
11651
- if (!topic) {
11652
- render.err("Usage: claudemesh topic leave <topic>");
11653
- return EXIT.INVALID_ARGS;
11654
- }
11655
- return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11656
- await client.topicLeave(topic);
11657
- if (flags.json)
11658
- console.log(JSON.stringify({ left: topic }));
11659
- else
11660
- render.ok("left", clay("#" + topic));
11661
- return EXIT.SUCCESS;
11662
- });
11663
- }
11664
- async function runTopicMembers(topic, flags) {
11665
- if (!topic) {
11666
- render.err("Usage: claudemesh topic members <topic>");
11667
- return EXIT.INVALID_ARGS;
11668
- }
11669
- return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11670
- const members = await client.topicMembers(topic);
11671
- if (flags.json) {
11672
- console.log(JSON.stringify(members, null, 2));
11673
- return EXIT.SUCCESS;
11674
- }
11675
- if (members.length === 0) {
11676
- render.info(dim(`no members in ${clay("#" + topic)}.`));
11677
- return EXIT.SUCCESS;
11678
- }
11679
- render.section(`${clay("#" + topic)} members (${members.length})`);
11680
- for (const m of members) {
11681
- process.stdout.write(` ${bold(m.displayName)} ${dim(m.role)} ${dim(m.pubkey.slice(0, 8))}
11682
- `);
11683
- }
11684
- return EXIT.SUCCESS;
11685
- });
11686
- }
11687
- async function runTopicHistory(topic, flags) {
11688
- if (!topic) {
11689
- render.err("Usage: claudemesh topic history <topic> [--limit N] [--before <id>]");
11690
- return EXIT.INVALID_ARGS;
11691
- }
11692
- return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11693
- const limit = flags.limit ? Number(flags.limit) : undefined;
11694
- const messages = await client.topicHistory({
11695
- topic,
11696
- limit,
11697
- beforeId: flags.before
11698
- });
11699
- if (flags.json) {
11700
- console.log(JSON.stringify(messages, null, 2));
11701
- return EXIT.SUCCESS;
11702
- }
11703
- if (messages.length === 0) {
11704
- render.info(dim(`no messages in ${clay("#" + topic)}.`));
11705
- return EXIT.SUCCESS;
11706
- }
11707
- const ordered = [...messages].reverse();
11708
- render.section(`${clay("#" + topic)} history (${ordered.length})`);
11709
- for (const m of ordered) {
11710
- const t = new Date(m.createdAt).toLocaleString();
11711
- process.stdout.write(` ${dim(t)} ${bold(m.senderPubkey.slice(0, 8))} ${dim("(encrypted, " + m.ciphertext.length + "b)")}
11712
- `);
11713
- }
11714
- return EXIT.SUCCESS;
11715
- });
11716
- }
11717
- async function runTopicMarkRead(topic, flags) {
11718
- if (!topic) {
11719
- render.err("Usage: claudemesh topic read <topic>");
11720
- return EXIT.INVALID_ARGS;
11721
- }
11722
- return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11723
- await client.topicMarkRead(topic);
11724
- if (flags.json)
11725
- console.log(JSON.stringify({ read: topic }));
11726
- else
11727
- render.ok("marked read", clay("#" + topic));
11728
- return EXIT.SUCCESS;
11729
- });
11730
- }
11731
- var init_topic = __esm(() => {
11732
- init_connect();
11733
- init_render();
11734
- init_styles();
11735
- init_exit_codes();
11736
- });
11737
-
11738
- // src/services/api/with-rest-key.ts
11739
- async function withRestKey(opts, fn) {
11740
- return withMesh({ meshSlug: opts.meshSlug ?? null }, async (client, mesh) => {
11741
- const result = await client.apiKeyCreate({
11742
- label: `cli-${opts.purpose ?? "rest"}-${process.pid}`,
11743
- capabilities: opts.capabilities ?? ["read"],
11744
- topicScopes: opts.topicScopes ?? undefined,
11745
- expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString()
11746
- });
11747
- if (!result || !result.secret) {
11748
- throw new Error("apikey mint failed — broker did not return a secret");
11749
- }
11750
- try {
11751
- return await fn({
11752
- secret: result.secret,
11753
- meshId: mesh.meshId,
11754
- meshSlug: mesh.slug,
11755
- client,
11756
- mesh
11757
- });
11758
- } finally {
11759
- try {
11760
- await client.apiKeyRevoke(result.id);
11761
- } catch {}
11762
- }
11763
- });
11764
- }
11765
- var init_with_rest_key = __esm(() => {
11766
- init_connect();
11767
- });
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
12021
+ name,
12022
+ description: flags.description,
12023
+ visibility: flags.visibility
11832
12024
  });
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;
12025
+ if (!result) {
12026
+ render.err("topic create failed");
12027
+ return EXIT.INTERNAL_ERROR;
11837
12028
  }
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"));
12029
+ if (flags.json) {
12030
+ console.log(JSON.stringify(result));
11842
12031
  return EXIT.SUCCESS;
11843
12032
  }
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
- `);
12033
+ if (result.created) {
12034
+ render.ok("created", `${clay("#" + name)} ${dim(result.id.slice(0, 8))}`);
12035
+ } else {
12036
+ render.info(dim(`already exists: #${name} ${result.id.slice(0, 8)}`));
11853
12037
  }
11854
12038
  return EXIT.SUCCESS;
11855
12039
  });
11856
12040
  }
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
- });
12041
+ async function runTopicList(flags) {
12042
+ return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
12043
+ const topics = await client.topicList();
11873
12044
  if (flags.json) {
11874
- console.log(JSON.stringify(ws, null, 2));
12045
+ console.log(JSON.stringify(topics, null, 2));
11875
12046
  return EXIT.SUCCESS;
11876
12047
  }
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
- `));
12048
+ if (topics.length === 0) {
12049
+ render.info(dim("no topics in this mesh."));
11883
12050
  return EXIT.SUCCESS;
11884
12051
  }
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}
12052
+ render.section(`topics (${topics.length})`);
12053
+ for (const t of topics) {
12054
+ const vis = t.visibility === "public" ? green(t.visibility) : dim(t.visibility);
12055
+ process.stdout.write(` ${clay("#" + t.name)} ${vis} ${dim(`${t.memberCount} member${t.memberCount === 1 ? "" : "s"}`)}
12056
+ `);
12057
+ if (t.description)
12058
+ process.stdout.write(` ${dim(t.description)}
11895
12059
  `);
11896
12060
  }
11897
12061
  return EXIT.SUCCESS;
11898
12062
  });
11899
12063
  }
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
- });
12064
+ async function runTopicJoin(topic, flags) {
12065
+ if (!topic) {
12066
+ render.err("Usage: claudemesh topic join <topic> [--role lead|member|observer]");
12067
+ return EXIT.INVALID_ARGS;
12068
+ }
12069
+ return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
12070
+ await client.topicJoin(topic, flags.role);
12071
+ if (flags.json)
12072
+ console.log(JSON.stringify({ joined: topic }));
12073
+ else
12074
+ render.ok("joined", clay("#" + topic));
12075
+ return EXIT.SUCCESS;
12076
+ });
12077
+ }
12078
+ async function runTopicLeave(topic, flags) {
12079
+ if (!topic) {
12080
+ render.err("Usage: claudemesh topic leave <topic>");
12081
+ return EXIT.INVALID_ARGS;
12082
+ }
12083
+ return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
12084
+ await client.topicLeave(topic);
12085
+ if (flags.json)
12086
+ console.log(JSON.stringify({ left: topic }));
12087
+ else
12088
+ render.ok("left", clay("#" + topic));
12089
+ return EXIT.SUCCESS;
12090
+ });
12091
+ }
12092
+ async function runTopicMembers(topic, flags) {
12093
+ if (!topic) {
12094
+ render.err("Usage: claudemesh topic members <topic>");
12095
+ return EXIT.INVALID_ARGS;
12096
+ }
12097
+ return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
12098
+ const members = await client.topicMembers(topic);
11914
12099
  if (flags.json) {
11915
- console.log(JSON.stringify(ws, null, 2));
12100
+ console.log(JSON.stringify(members, null, 2));
11916
12101
  return EXIT.SUCCESS;
11917
12102
  }
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
- `));
12103
+ if (members.length === 0) {
12104
+ render.info(dim(`no members in ${clay("#" + topic)}.`));
11922
12105
  return EXIT.SUCCESS;
11923
12106
  }
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}
12107
+ render.section(`${clay("#" + topic)} members (${members.length})`);
12108
+ for (const m of members) {
12109
+ process.stdout.write(` ${bold(m.displayName)} ${dim(m.role)} ${dim(m.pubkey.slice(0, 8))}
11933
12110
  `);
11934
12111
  }
11935
12112
  return EXIT.SUCCESS;
11936
12113
  });
11937
12114
  }
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
- `);
12115
+ async function runTopicHistory(topic, flags) {
12116
+ if (!topic) {
12117
+ render.err("Usage: claudemesh topic history <topic> [--limit N] [--before <id>]");
11942
12118
  return EXIT.INVALID_ARGS;
11943
12119
  }
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
12120
+ return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
12121
+ const limit = flags.limit ? Number(flags.limit) : undefined;
12122
+ const messages = await client.topicHistory({
12123
+ topic,
12124
+ limit,
12125
+ beforeId: flags.before
11953
12126
  });
11954
12127
  if (flags.json) {
11955
- console.log(JSON.stringify(ws, null, 2));
12128
+ console.log(JSON.stringify(messages, null, 2));
11956
12129
  return EXIT.SUCCESS;
11957
12130
  }
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
- `));
12131
+ if (messages.length === 0) {
12132
+ render.info(dim(`no messages in ${clay("#" + topic)}.`));
11962
12133
  return EXIT.SUCCESS;
11963
12134
  }
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}
12135
+ const ordered = [...messages].reverse();
12136
+ render.section(`${clay("#" + topic)} history (${ordered.length})`);
12137
+ for (const m of ordered) {
12138
+ const t = new Date(m.createdAt).toLocaleString();
12139
+ process.stdout.write(` ${dim(t)} ${bold(m.senderPubkey.slice(0, 8))} ${dim("(encrypted, " + m.ciphertext.length + "b)")}
11991
12140
  `);
11992
- }
11993
12141
  }
11994
12142
  return EXIT.SUCCESS;
11995
12143
  });
11996
12144
  }
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`;
12145
+ async function runTopicMarkRead(topic, flags) {
12146
+ if (!topic) {
12147
+ render.err("Usage: claudemesh topic read <topic>");
12148
+ return EXIT.INVALID_ARGS;
12149
+ }
12150
+ return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
12151
+ await client.topicMarkRead(topic);
12152
+ if (flags.json)
12153
+ console.log(JSON.stringify({ read: topic }));
12154
+ else
12155
+ render.ok("marked read", clay("#" + topic));
12156
+ return EXIT.SUCCESS;
12157
+ });
12023
12158
  }
12024
- var init_me = __esm(() => {
12025
- init_with_rest_key();
12026
- init_client();
12027
- init_facade();
12159
+ var init_topic = __esm(() => {
12160
+ init_connect();
12028
12161
  init_render();
12029
12162
  init_styles();
12030
12163
  init_exit_codes();
@@ -14423,7 +14556,7 @@ async function main() {
14423
14556
  }
14424
14557
  case "send": {
14425
14558
  const { runSend: runSend2 } = await Promise.resolve().then(() => (init_send(), exports_send));
14426
- await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json }, positionals[0] ?? "", positionals.slice(1).join(" "));
14559
+ await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json, self: !!flags.self }, positionals[0] ?? "", positionals.slice(1).join(" "));
14427
14560
  break;
14428
14561
  }
14429
14562
  case "inbox": {
@@ -14437,6 +14570,10 @@ async function main() {
14437
14570
  const { runStateSet: runStateSet2 } = await Promise.resolve().then(() => (init_state(), exports_state));
14438
14571
  await runStateSet2({}, positionals[1] ?? "", positionals[2] ?? "");
14439
14572
  } else if (sub === "list") {
14573
+ if (!flags.mesh) {
14574
+ const { runMeState: runMeState2 } = await Promise.resolve().then(() => (init_me(), exports_me));
14575
+ process.exit(await runMeState2({ json: !!flags.json, key: flags.key }));
14576
+ }
14440
14577
  const { runStateList: runStateList2 } = await Promise.resolve().then(() => (init_state(), exports_state));
14441
14578
  await runStateList2({});
14442
14579
  } else {
@@ -14456,6 +14593,10 @@ async function main() {
14456
14593
  break;
14457
14594
  }
14458
14595
  case "recall": {
14596
+ if (!flags.mesh) {
14597
+ const { runMeMemory: runMeMemory2 } = await Promise.resolve().then(() => (init_me(), exports_me));
14598
+ process.exit(await runMeMemory2({ json: !!flags.json, query: positionals.join(" ") }));
14599
+ }
14459
14600
  const { recall: recall2 } = await Promise.resolve().then(() => (init_recall(), exports_recall));
14460
14601
  process.exit(await recall2(positionals.join(" "), { mesh: flags.mesh, json: !!flags.json }));
14461
14602
  break;
@@ -15132,6 +15273,10 @@ async function main() {
15132
15273
  const { runTaskComplete: runTaskComplete2 } = await Promise.resolve().then(() => (init_broker_actions(), exports_broker_actions));
15133
15274
  process.exit(await runTaskComplete2(positionals[1], positionals.slice(2).join(" ") || undefined, f));
15134
15275
  } else if (sub === "list") {
15276
+ if (!f.mesh) {
15277
+ const { runMeTasks: runMeTasks2 } = await Promise.resolve().then(() => (init_me(), exports_me));
15278
+ process.exit(await runMeTasks2({ json: f.json, status: flags.status }));
15279
+ }
15135
15280
  const { runTaskList: runTaskList2 } = await Promise.resolve().then(() => (init_platform_actions(), exports_platform_actions));
15136
15281
  process.exit(await runTaskList2({ ...f, status: flags.status, assignee: flags.assignee }));
15137
15282
  } else if (sub === "create") {
@@ -15170,4 +15315,4 @@ main().catch((err) => {
15170
15315
  process.exit(EXIT.INTERNAL_ERROR);
15171
15316
  });
15172
15317
 
15173
- //# debugId=3168504A6207F58264756E2164756E21
15318
+ //# debugId=E54B2ED5C4F2639E64756E2164756E21