@sherwoodagent/cli 0.59.0 → 0.59.5
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/{chat-TMM5YVBZ.js → chat-ZY2NTT7G.js} +129 -3
- package/dist/chat-ZY2NTT7G.js.map +1 -0
- package/dist/index.js +6 -6
- package/dist/{research-XLCS2DGQ.js → research-P6AJVTVE.js} +2 -2
- package/dist/{session-WLR37ZQI.js → session-B77JKCGZ.js} +2 -2
- package/dist/{trade-72SJMUNU.js → trade-WB2LKYKT.js} +2 -2
- package/dist/{xmtp-RZP3XLJJ.js → xmtp-HC774TEI.js} +37 -5
- package/dist/xmtp-HC774TEI.js.map +1 -0
- package/package.json +1 -1
- package/dist/chat-TMM5YVBZ.js.map +0 -1
- package/dist/xmtp-RZP3XLJJ.js.map +0 -1
- /package/dist/{research-XLCS2DGQ.js.map → research-P6AJVTVE.js.map} +0 -0
- /package/dist/{session-WLR37ZQI.js.map → session-B77JKCGZ.js.map} +0 -0
- /package/dist/{trade-72SJMUNU.js.map → trade-WB2LKYKT.js.map} +0 -0
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
import chalk from "chalk";
|
|
30
30
|
import ora from "ora";
|
|
31
31
|
async function loadXmtp() {
|
|
32
|
-
return import("./xmtp-
|
|
32
|
+
return import("./xmtp-HC774TEI.js");
|
|
33
33
|
}
|
|
34
34
|
function formatTimestamp(date) {
|
|
35
35
|
return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
|
|
@@ -264,6 +264,121 @@ async function notifySpectatorSync() {
|
|
|
264
264
|
);
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
+
async function handleRename(name, newName, newDescription) {
|
|
268
|
+
if (newName === void 0 && newDescription === void 0) {
|
|
269
|
+
console.error(
|
|
270
|
+
chalk.red(
|
|
271
|
+
'Usage: sherwood chat <name> rename [new-name] [--description "..."]'
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
const spinner = ora("Updating group metadata...").start();
|
|
277
|
+
try {
|
|
278
|
+
const xmtp = await loadXmtp();
|
|
279
|
+
const group = await xmtp.getGroup("", name);
|
|
280
|
+
await xmtp.renameGroup(group, newName, newDescription);
|
|
281
|
+
spinner.succeed(
|
|
282
|
+
`Updated ${[
|
|
283
|
+
newName !== void 0 ? `name \u2192 "${newName}"` : null,
|
|
284
|
+
newDescription !== void 0 ? `description \u2192 "${newDescription}"` : null
|
|
285
|
+
].filter(Boolean).join(", ")}`
|
|
286
|
+
);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
spinner.fail("Failed to update group metadata");
|
|
289
|
+
console.error(chalk.red(formatContractError(err)));
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async function handlePublishMetadata(name, groupIdOpt) {
|
|
294
|
+
const spinner = ora("Updating syndicate metadata...").start();
|
|
295
|
+
try {
|
|
296
|
+
const syndicate = await resolveSyndicate(name);
|
|
297
|
+
const callerAddress = getAccount().address.toLowerCase();
|
|
298
|
+
if (syndicate.creator.toLowerCase() !== callerAddress) {
|
|
299
|
+
spinner.fail("Only the syndicate creator can update metadata");
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
const xmtp = await loadXmtp();
|
|
303
|
+
let groupId = groupIdOpt || getCachedGroupId(name);
|
|
304
|
+
if (!groupId) {
|
|
305
|
+
try {
|
|
306
|
+
groupId = await xmtp.getGroup("", name);
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (!groupId) {
|
|
311
|
+
spinner.fail(
|
|
312
|
+
"Could not determine group ID \u2014 pass --group-id <id> or run from a machine that has the group cached"
|
|
313
|
+
);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
spinner.text = "Fetching current metadata...";
|
|
317
|
+
const info = await getSyndicate(syndicate.id);
|
|
318
|
+
let metadata;
|
|
319
|
+
try {
|
|
320
|
+
metadata = await fetchMetadata(info.metadataURI);
|
|
321
|
+
} catch {
|
|
322
|
+
metadata = {
|
|
323
|
+
schema: "sherwood/syndicate/v1",
|
|
324
|
+
name,
|
|
325
|
+
description: "",
|
|
326
|
+
chain: "",
|
|
327
|
+
strategies: [],
|
|
328
|
+
terms: {},
|
|
329
|
+
links: {}
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
metadata.xmtpGroupId = groupId;
|
|
333
|
+
spinner.text = "Pinning updated metadata to IPFS...";
|
|
334
|
+
const newURI = await uploadMetadata(metadata);
|
|
335
|
+
spinner.text = "Calling factory.updateMetadata...";
|
|
336
|
+
await updateMetadata(syndicate.id, newURI);
|
|
337
|
+
spinner.succeed(`Metadata updated \u2014 xmtpGroupId = ${groupId}`);
|
|
338
|
+
console.log(chalk.dim(` New URI: ${newURI}`));
|
|
339
|
+
console.log(
|
|
340
|
+
chalk.dim(
|
|
341
|
+
" Dashboard will resolve the group via metadata fallback on next page load."
|
|
342
|
+
)
|
|
343
|
+
);
|
|
344
|
+
} catch (err) {
|
|
345
|
+
spinner.fail("Failed to update metadata");
|
|
346
|
+
console.error(chalk.red(formatContractError(err)));
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async function handlePublishEns(name, groupIdOpt) {
|
|
351
|
+
const spinner = ora("Publishing group ID to ENS...").start();
|
|
352
|
+
try {
|
|
353
|
+
const syndicate = await resolveSyndicate(name);
|
|
354
|
+
const callerAddress = getAccount().address.toLowerCase();
|
|
355
|
+
if (syndicate.creator.toLowerCase() !== callerAddress) {
|
|
356
|
+
spinner.fail("Only the syndicate creator can write ENS records");
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
const xmtp = await loadXmtp();
|
|
360
|
+
let groupId = groupIdOpt || getCachedGroupId(name);
|
|
361
|
+
if (!groupId) {
|
|
362
|
+
try {
|
|
363
|
+
groupId = await xmtp.getGroup("", name);
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (!groupId) {
|
|
368
|
+
spinner.fail(
|
|
369
|
+
"Could not determine group ID \u2014 pass --group-id <id> or run from a machine that has the group cached"
|
|
370
|
+
);
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
await setTextRecord(name, "xmtpGroupId", groupId, syndicate.vault);
|
|
374
|
+
spinner.succeed(`Published xmtpGroupId \u2192 ${groupId} on ENS`);
|
|
375
|
+
console.log(chalk.dim(" Dashboard will pick up the group on next page load."));
|
|
376
|
+
} catch (err) {
|
|
377
|
+
spinner.fail("Failed to publish ENS record");
|
|
378
|
+
console.error(chalk.red(formatContractError(err)));
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
267
382
|
async function handlePublic(name, on) {
|
|
268
383
|
const spectatorAddress = process.env.DASHBOARD_SPECTATOR_ADDRESS;
|
|
269
384
|
if (!spectatorAddress) {
|
|
@@ -352,7 +467,7 @@ async function handleInit(name, force, isPublic) {
|
|
|
352
467
|
}
|
|
353
468
|
}
|
|
354
469
|
function registerChatCommands(program) {
|
|
355
|
-
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat \u2014 stream, send, log, members, add, remove, init, public").option("--markdown", "Send as rich markdown (for send)", false).option("--stdin", "Read send message from stdin instead of CLI arg (avoids shell quoting issues with $ chars and quotes)", false).option("--limit <n>", "Number of messages to show (for log)", "20").option("--force", "Recreate group even if one exists (for init)", false).option("--public", "Enable public chat \u2014 adds dashboard spectator (for init)", false).action(async (name, action, actionArgs, opts) => {
|
|
470
|
+
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat \u2014 stream, send, log, members, add, remove, init, public").option("--markdown", "Send as rich markdown (for send)", false).option("--stdin", "Read send message from stdin instead of CLI arg (avoids shell quoting issues with $ chars and quotes)", false).option("--limit <n>", "Number of messages to show (for log)", "20").option("--force", "Recreate group even if one exists (for init)", false).option("--public", "Enable public chat \u2014 adds dashboard spectator (for init)", false).option("--description <text>", "New description (for rename)").option("--group-id <id>", "Explicit XMTP group ID (for publish-ens)").action(async (name, action, actionArgs, opts) => {
|
|
356
471
|
switch (action) {
|
|
357
472
|
case "send": {
|
|
358
473
|
let message = actionArgs[0];
|
|
@@ -409,6 +524,17 @@ function registerChatCommands(program) {
|
|
|
409
524
|
case "init":
|
|
410
525
|
await handleInit(name, opts.force, opts.public);
|
|
411
526
|
break;
|
|
527
|
+
case "rename": {
|
|
528
|
+
const newName = actionArgs[0];
|
|
529
|
+
await handleRename(name, newName, opts.description);
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
case "publish-ens":
|
|
533
|
+
await handlePublishEns(name, opts.groupId);
|
|
534
|
+
break;
|
|
535
|
+
case "publish-metadata":
|
|
536
|
+
await handlePublishMetadata(name, opts.groupId);
|
|
537
|
+
break;
|
|
412
538
|
case "public": {
|
|
413
539
|
const flag = actionArgs[0];
|
|
414
540
|
if (flag !== "on" && flag !== "off") {
|
|
@@ -428,4 +554,4 @@ function registerChatCommands(program) {
|
|
|
428
554
|
export {
|
|
429
555
|
registerChatCommands
|
|
430
556
|
};
|
|
431
|
-
//# sourceMappingURL=chat-
|
|
557
|
+
//# sourceMappingURL=chat-ZY2NTT7G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/chat.ts"],"sourcesContent":["/**\n * Chat commands — sherwood chat <syndicate-name> [action] [args...]\n *\n * Uses XMTP for encrypted group messaging tied to syndicates.\n * Shells out to the @xmtp/cli binary for all XMTP operations.\n *\n * Commander can't dispatch subcommands when the parent has a positional <name> arg\n * (it always runs the parent action). So we use manual dispatch: a single .action()\n * that routes based on the [action] argument.\n *\n * Usage:\n * sherwood chat <name> — stream messages (default)\n * sherwood chat <name> send \"hello\" — send a text message\n * sherwood chat <name> send \"hello\" --markdown — send formatted markdown\n * sherwood chat <name> react <id> <emoji> — react to a message\n * sherwood chat <name> log [--limit 50] — show recent messages\n * sherwood chat <name> members — list group members\n * sherwood chat <name> add <address> — add member (creator only)\n * sherwood chat <name> init [--force] [--public] — create XMTP group + write ENS record\n * sherwood chat <name> public on/off — toggle dashboard spectator access\n * sherwood chat <name> rename <new-name> [--description \"...\"] — update group metadata (admin only)\n * sherwood chat <name> publish-ens [--group-id <id>] — write xmtpGroupId ENS text record (creator only, recovery for failed init)\n * sherwood chat <name> publish-metadata [--group-id <id>] — backfill xmtpGroupId into IPFS metadata (creator only; use when ENS write reverts)\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { getAccount, formatContractError } from \"../lib/client.js\";\nimport { resolveSyndicate, setTextRecord, getTextRecord } from \"../lib/ens.js\";\nimport { cacheGroupId, getCachedGroupId } from \"../lib/config.js\";\nimport { fetchMetadata, uploadMetadata } from \"../lib/ipfs.js\";\nimport * as factoryLib from \"../lib/factory.js\";\nimport { ENS } from \"../lib/addresses.js\";\nimport type { ChatEnvelope, MessageType } from \"../lib/types.js\";\nimport type { XmtpMessage } from \"../lib/xmtp.js\";\n\n// Lazy-load XMTP to avoid breaking non-chat commands when @xmtp/cli is missing\nasync function loadXmtp() {\n return import(\"../lib/xmtp.js\");\n}\n\n// ── Formatting ──\n\nfunction formatTimestamp(date: Date): string {\n return `${date.getHours().toString().padStart(2, \"0\")}:${date.getMinutes().toString().padStart(2, \"0\")}`;\n}\n\nfunction truncateAddress(addr: string): string {\n return `${addr.slice(0, 6)}...${addr.slice(-4)}`;\n}\n\nfunction colorByType(type: MessageType): (text: string) => string {\n switch (type) {\n case \"TRADE_EXECUTED\":\n return chalk.green;\n case \"RISK_ALERT\":\n return chalk.red;\n case \"TRADE_SIGNAL\":\n return chalk.yellow;\n case \"POSITION_UPDATE\":\n return chalk.cyan;\n case \"LP_REPORT\":\n return chalk.magenta;\n case \"X402_RESEARCH\":\n return chalk.cyan;\n case \"AGENT_REGISTERED\":\n case \"MEMBER_JOIN\":\n return chalk.blue;\n case \"RAGEQUIT_NOTICE\":\n return chalk.red;\n default:\n return chalk.white;\n }\n}\n\nfunction formatMessage(msg: XmtpMessage): string {\n const time = chalk.dim(`[${formatTimestamp(msg.sentAt)}]`);\n const sender = chalk.dim(truncateAddress(msg.senderInboxId));\n\n const text = msg.content;\n try {\n const envelope: ChatEnvelope = JSON.parse(text);\n const color = colorByType(envelope.type);\n const from = envelope.from ? truncateAddress(envelope.from) : sender;\n\n if (envelope.type === \"REACTION\") {\n const data = envelope.data as { reference?: string; emoji?: string } | undefined;\n return `${time} ${sender} reacted ${data?.emoji || \"?\"} to ${truncateAddress(data?.reference || \"?\")}`;\n }\n\n if (envelope.type === \"MESSAGE\") {\n if ((envelope.data as Record<string, unknown>)?.format === \"markdown\") {\n return `${time} ${chalk.dim(from)}\\n${envelope.text || \"\"}`;\n }\n return `${time} ${chalk.dim(from)}: ${envelope.text || \"\"}`;\n }\n\n if (envelope.type === \"AGENT_REGISTERED\") {\n return `${time} ${color(`[${envelope.type}]`)} Agent ${truncateAddress(envelope.agent?.address || \"?\")} registered`;\n }\n\n if (envelope.type === \"MEMBER_JOIN\") {\n return `${time} ${color(`[${envelope.type}]`)} ${truncateAddress(envelope.from || \"?\")} joined`;\n }\n\n if (envelope.type === \"X402_RESEARCH\") {\n const d = envelope.data as { provider?: string; queryType?: string; costUsdc?: string; attestationUid?: string } | undefined;\n const label = `${d?.provider || \"?\"} ${d?.queryType || \"?\"}`;\n return `${time} ${color(`[RESEARCH]`)} ${chalk.dim(from)}: ${label} ($${d?.costUsdc || \"?\"} USDC)`;\n }\n\n const summary = envelope.text || envelope.type;\n return `${time} ${color(`[${envelope.type}]`)} ${chalk.dim(from)}: ${summary}`;\n } catch {\n return `${time} ${sender}: ${text}`;\n }\n}\n\n// ── Action handlers ──\n\nasync function handleStream(name: string): Promise<void> {\n const spinner = ora(\"Connecting to chat...\").start();\n try {\n await resolveSyndicate(name);\n const xmtp = await loadXmtp();\n const client = await xmtp.getXmtpClient();\n const group = await xmtp.getGroup(client, name);\n spinner.succeed(`Connected to ${name}.sherwoodagent.eth`);\n console.log(chalk.dim(\"Streaming messages... (Ctrl+C to exit)\\n\"));\n\n const cleanup = await xmtp.streamMessages(group, (msg) => {\n console.log(formatMessage(msg));\n });\n\n process.on(\"SIGINT\", async () => {\n console.log(chalk.dim(\"\\nDisconnecting...\"));\n cleanup();\n process.exit(0);\n });\n\n await new Promise(() => {});\n } catch (err) {\n spinner.fail(\"Failed to connect to chat\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n/** Drain stdin into a single string. Used by `chat send --stdin` to bypass\n * shell expansion on dynamic messages (e.g. amounts containing `$`). */\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf-8');\n}\n\nasync function handleSend(name: string, message: string, markdown: boolean): Promise<void> {\n const spinner = ora(\"Sending...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n\n if (markdown) {\n await xmtp.sendMarkdown(group, message);\n } else {\n const envelope: ChatEnvelope = {\n type: \"MESSAGE\",\n from: getAccount().address,\n text: message,\n timestamp: Math.floor(Date.now() / 1000),\n };\n await xmtp.sendEnvelope(group, envelope);\n }\n\n spinner.succeed(\"Message sent\");\n } catch (err) {\n spinner.fail(\"Failed to send message\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleReact(name: string, messageId: string, emoji: string): Promise<void> {\n const spinner = ora(\"Reacting...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.sendReaction(group, messageId, emoji);\n spinner.succeed(`Reacted ${emoji}`);\n } catch (err) {\n spinner.fail(\"Failed to send reaction\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleLog(name: string, limit: number): Promise<void> {\n const spinner = ora(\"Loading messages...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n const messages = await xmtp.getRecentMessages(group, limit);\n\n spinner.stop();\n console.log();\n console.log(chalk.bold(`Chat log: ${name}.sherwoodagent.eth`));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n if (messages.length === 0) {\n console.log(chalk.dim(\" No messages yet\"));\n } else {\n for (const msg of messages.reverse()) {\n console.log(formatMessage(msg));\n }\n }\n console.log();\n } catch (err) {\n spinner.fail(\"Failed to load messages\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleMembers(name: string): Promise<void> {\n const spinner = ora(\"Loading members...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n const members = await xmtp.getMembers(group);\n\n spinner.stop();\n console.log();\n console.log(chalk.bold(`Members: ${name}.sherwoodagent.eth`));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n for (const member of members) {\n const role = member.permissionLevel === \"super_admin\"\n ? chalk.yellow(\" (super admin)\")\n : member.permissionLevel === \"admin\"\n ? chalk.blue(\" (admin)\")\n : \"\";\n console.log(` ${member.inboxId}${role}`);\n }\n\n console.log(chalk.dim(`\\n Total: ${members.length} members`));\n console.log();\n } catch (err) {\n spinner.fail(\"Failed to load members\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleAdd(name: string, address: string): Promise<void> {\n const spinner = ora(\"Adding member...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.addMember(group, address);\n\n await xmtp.sendEnvelope(group, {\n type: \"MEMBER_JOIN\",\n from: address,\n syndicate: name,\n timestamp: Math.floor(Date.now() / 1000),\n });\n\n spinner.succeed(`Member added: ${address}`);\n } catch (err) {\n spinner.fail(\"Failed to add member\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleRemove(name: string, address: string): Promise<void> {\n const spinner = ora(\"Removing member...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.removeMember(group, address);\n spinner.succeed(`Member removed: ${address}`);\n } catch (err) {\n spinner.fail(\"Failed to remove member\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n/**\n * Notify the spectator service to sync new groups.\n * Called after adding the spectator wallet to an XMTP group so the service\n * invalidates its cache, calls syncAll(), and discovers the new group.\n */\nasync function notifySpectatorSync(): Promise<void> {\n const spectatorUrl =\n process.env.SPECTATOR_URL || \"https://spectator.sherwood.sh\";\n try {\n const res = await fetch(`${spectatorUrl}/sync`, {\n method: \"POST\",\n signal: AbortSignal.timeout(8_000),\n });\n if (res.ok) {\n const data = (await res.json()) as { groups?: number };\n console.log(\n chalk.dim(\n ` Spectator synced — ${data.groups ?? \"?\"} groups discovered`,\n ),\n );\n }\n } catch {\n // Non-fatal — spectator will self-sync within 60 s via cache TTL\n console.log(\n chalk.dim(\" (Spectator sync skipped — will auto-discover within 60 s)\"),\n );\n }\n}\n\nasync function handleRename(\n name: string,\n newName?: string,\n newDescription?: string,\n): Promise<void> {\n if (newName === undefined && newDescription === undefined) {\n console.error(\n chalk.red(\n \"Usage: sherwood chat <name> rename [new-name] [--description \\\"...\\\"]\",\n ),\n );\n process.exit(1);\n }\n const spinner = ora(\"Updating group metadata...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.renameGroup(group, newName, newDescription);\n spinner.succeed(\n `Updated ${[\n newName !== undefined ? `name → \"${newName}\"` : null,\n newDescription !== undefined\n ? `description → \"${newDescription}\"`\n : null,\n ]\n .filter(Boolean)\n .join(\", \")}`,\n );\n } catch (err) {\n spinner.fail(\"Failed to update group metadata\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handlePublishMetadata(\n name: string,\n groupIdOpt?: string,\n): Promise<void> {\n const spinner = ora(\"Updating syndicate metadata...\").start();\n try {\n const syndicate = await resolveSyndicate(name);\n const callerAddress = getAccount().address.toLowerCase();\n if (syndicate.creator.toLowerCase() !== callerAddress) {\n spinner.fail(\"Only the syndicate creator can update metadata\");\n process.exit(1);\n }\n\n const xmtp = await loadXmtp();\n let groupId = groupIdOpt || getCachedGroupId(name);\n if (!groupId) {\n try {\n groupId = await xmtp.getGroup(\"\", name);\n } catch {\n // ignore\n }\n }\n if (!groupId) {\n spinner.fail(\n \"Could not determine group ID — pass --group-id <id> or run from a machine that has the group cached\",\n );\n process.exit(1);\n }\n\n spinner.text = \"Fetching current metadata...\";\n const info = await factoryLib.getSyndicate(syndicate.id);\n let metadata;\n try {\n metadata = await fetchMetadata(info.metadataURI);\n } catch {\n metadata = {\n schema: \"sherwood/syndicate/v1\",\n name,\n description: \"\",\n chain: \"\",\n strategies: [],\n terms: {},\n links: {},\n };\n }\n metadata.xmtpGroupId = groupId;\n\n spinner.text = \"Pinning updated metadata to IPFS...\";\n const newURI = await uploadMetadata(metadata);\n\n spinner.text = \"Calling factory.updateMetadata...\";\n await factoryLib.updateMetadata(syndicate.id, newURI);\n\n spinner.succeed(`Metadata updated — xmtpGroupId = ${groupId}`);\n console.log(chalk.dim(` New URI: ${newURI}`));\n console.log(\n chalk.dim(\n \" Dashboard will resolve the group via metadata fallback on next page load.\",\n ),\n );\n } catch (err) {\n spinner.fail(\"Failed to update metadata\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handlePublishEns(name: string, groupIdOpt?: string): Promise<void> {\n const spinner = ora(\"Publishing group ID to ENS...\").start();\n try {\n const syndicate = await resolveSyndicate(name);\n const callerAddress = getAccount().address.toLowerCase();\n if (syndicate.creator.toLowerCase() !== callerAddress) {\n spinner.fail(\"Only the syndicate creator can write ENS records\");\n process.exit(1);\n }\n\n // Resolve group ID: explicit arg → local cache → existing ENS record (no-op)\n const xmtp = await loadXmtp();\n let groupId = groupIdOpt || getCachedGroupId(name);\n if (!groupId) {\n try {\n groupId = await xmtp.getGroup(\"\", name); // syncs DB + name-search fallback\n } catch {\n // ignore\n }\n }\n if (!groupId) {\n spinner.fail(\n \"Could not determine group ID — pass --group-id <id> or run from a machine that has the group cached\",\n );\n process.exit(1);\n }\n\n await setTextRecord(name, \"xmtpGroupId\", groupId, syndicate.vault);\n spinner.succeed(`Published xmtpGroupId → ${groupId} on ENS`);\n console.log(chalk.dim(\" Dashboard will pick up the group on next page load.\"));\n } catch (err) {\n spinner.fail(\"Failed to publish ENS record\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handlePublic(name: string, on: boolean): Promise<void> {\n const spectatorAddress = process.env.DASHBOARD_SPECTATOR_ADDRESS;\n if (!spectatorAddress) {\n console.error(chalk.red(\"DASHBOARD_SPECTATOR_ADDRESS env var is required\"));\n process.exit(1);\n }\n\n const spinner = ora(`${on ? \"Enabling\" : \"Disabling\"} public chat...`).start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n\n if (on) {\n await xmtp.addMember(group, spectatorAddress);\n spinner.succeed(\"Public chat enabled — dashboard spectator added\");\n await notifySpectatorSync();\n } else {\n await xmtp.removeMember(group, spectatorAddress);\n spinner.succeed(\"Public chat disabled — dashboard spectator removed\");\n }\n } catch (err) {\n spinner.fail(\"Failed to toggle public chat\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleInit(name: string, force: boolean, isPublic: boolean): Promise<void> {\n const spinner = ora(\"Initializing chat group...\").start();\n try {\n const syndicate = await resolveSyndicate(name);\n const callerAddress = getAccount().address.toLowerCase();\n if (syndicate.creator.toLowerCase() !== callerAddress) {\n spinner.fail(\"Only the syndicate creator can initialize the chat group\");\n process.exit(1);\n }\n\n // Idempotency check\n if (!force) {\n const existingId = getCachedGroupId(name) || await getTextRecord(name, \"xmtpGroupId\");\n if (existingId) {\n spinner.succeed(\"XMTP group already exists for this syndicate\");\n console.log(chalk.dim(` Group ID: ${existingId}`));\n return;\n }\n }\n\n // Create the group\n spinner.text = \"Creating XMTP group...\";\n const xmtp = await loadXmtp();\n const client = await xmtp.getXmtpClient();\n const groupId = await xmtp.createSyndicateGroup(client, name, isPublic);\n\n cacheGroupId(name, groupId);\n\n // Persist group ID: ENS on chains with L2 Registry, IPFS metadata otherwise\n const hasENS = ENS().L2_REGISTRY !== \"0x0000000000000000000000000000000000000000\";\n if (hasENS) {\n try {\n spinner.text = \"Writing group ID to ENS...\";\n await setTextRecord(name, \"xmtpGroupId\", groupId, syndicate.vault);\n } catch (ensErr) {\n console.warn(chalk.yellow(\"\\n ⚠ Could not write ENS text record\"));\n console.warn(chalk.dim(` ${ensErr instanceof Error ? ensErr.message : String(ensErr)}`));\n }\n } else {\n // No ENS (e.g. HyperEVM) — persist in IPFS metadata\n try {\n spinner.text = \"Saving group ID to syndicate metadata...\";\n const info = await factoryLib.getSyndicate(syndicate.id);\n let metadata;\n try {\n metadata = await fetchMetadata(info.metadataURI);\n } catch {\n metadata = { schema: \"sherwood/syndicate/v1\", name, description: \"\", chain: \"\", strategies: [], terms: {}, links: {} };\n }\n metadata.xmtpGroupId = groupId;\n const newURI = await uploadMetadata(metadata);\n await factoryLib.updateMetadata(syndicate.id, newURI);\n console.log(chalk.dim(`\\n Group ID saved to metadata: ${newURI}`));\n } catch (metaErr) {\n console.warn(chalk.yellow(\"\\n ⚠ Could not persist group ID (cached locally only)\"));\n console.warn(chalk.dim(` ${metaErr instanceof Error ? metaErr.message : String(metaErr)}`));\n }\n }\n\n spinner.succeed(`Chat group created for ${name}.sherwoodagent.eth`);\n console.log(chalk.dim(` Group ID: ${groupId}`));\n console.log(chalk.dim(` Stream: sherwood chat ${name}`));\n\n // Notify spectator to sync and discover the new public group\n if (isPublic) {\n await notifySpectatorSync();\n }\n } catch (err) {\n spinner.fail(\"Failed to initialize chat group\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n// ── Command Registration ──\n\nexport function registerChatCommands(program: Command): void {\n program\n .command(\"chat <name> [action] [actionArgs...]\")\n .description(\"Syndicate chat — stream, send, log, members, add, remove, init, public\")\n .option(\"--markdown\", \"Send as rich markdown (for send)\", false)\n .option(\"--stdin\", \"Read send message from stdin instead of CLI arg (avoids shell quoting issues with $ chars and quotes)\", false)\n .option(\"--limit <n>\", \"Number of messages to show (for log)\", \"20\")\n .option(\"--force\", \"Recreate group even if one exists (for init)\", false)\n .option(\"--public\", \"Enable public chat — adds dashboard spectator (for init)\", false)\n .option(\"--description <text>\", \"New description (for rename)\")\n .option(\"--group-id <id>\", \"Explicit XMTP group ID (for publish-ens)\")\n .action(async (name: string, action: string | undefined, actionArgs: string[], opts: { markdown: boolean; stdin: boolean; limit: string; force: boolean; public: boolean; description?: string; groupId?: string }) => {\n switch (action) {\n case \"send\": {\n let message = actionArgs[0];\n if (opts.stdin) {\n // If stdin is a terminal, the user forgot to pipe — readStdin\n // would block forever waiting for EOF (Ctrl+D). Warn explicitly.\n if (process.stdin.isTTY) {\n console.error(chalk.yellow(\"--stdin specified but stdin is a terminal. Either pipe the message in or omit --stdin and pass it as an argument. Press Ctrl+D to send what you've typed, Ctrl+C to abort.\"));\n }\n // Read entire stdin into the message — sidesteps bash $-expansion\n // when callers (cron jobs, scripts) construct dynamic message text.\n message = await readStdin();\n if (!message.trim()) {\n console.error(chalk.red(\"--stdin given but stdin was empty\"));\n process.exit(1);\n }\n }\n if (!message) {\n console.error(chalk.red(\"Usage: sherwood chat <name> send <message> [--markdown]\\n echo \\\"$msg\\\" | sherwood chat <name> send --stdin\"));\n process.exit(1);\n }\n await handleSend(name, message, opts.markdown);\n break;\n }\n\n case \"react\": {\n const [messageId, emoji] = actionArgs;\n if (!messageId || !emoji) {\n console.error(chalk.red(\"Usage: sherwood chat <name> react <messageId> <emoji>\"));\n process.exit(1);\n }\n await handleReact(name, messageId, emoji);\n break;\n }\n\n case \"log\":\n await handleLog(name, parseInt(opts.limit, 10));\n break;\n\n case \"members\":\n await handleMembers(name);\n break;\n\n case \"add\": {\n const address = actionArgs[0];\n if (!address) {\n console.error(chalk.red(\"Usage: sherwood chat <name> add <address>\"));\n process.exit(1);\n }\n await handleAdd(name, address);\n break;\n }\n\n case \"remove\": {\n const removeAddr = actionArgs[0];\n if (!removeAddr) {\n console.error(chalk.red(\"Usage: sherwood chat <name> remove <address>\"));\n process.exit(1);\n }\n await handleRemove(name, removeAddr);\n break;\n }\n\n case \"init\":\n await handleInit(name, opts.force, opts.public);\n break;\n\n case \"rename\": {\n const newName = actionArgs[0]; // optional positional\n await handleRename(name, newName, opts.description);\n break;\n }\n\n case \"publish-ens\":\n await handlePublishEns(name, opts.groupId);\n break;\n\n case \"publish-metadata\":\n await handlePublishMetadata(name, opts.groupId);\n break;\n\n case \"public\": {\n const flag = actionArgs[0];\n if (flag !== \"on\" && flag !== \"off\") {\n console.error(chalk.red(\"Usage: sherwood chat <name> public on/off\"));\n process.exit(1);\n }\n await handlePublic(name, flag === \"on\");\n break;\n }\n\n case undefined:\n default:\n await handleStream(name);\n break;\n }\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -3742,7 +3742,7 @@ function printSimulationResult(result, callType) {
|
|
|
3742
3742
|
}
|
|
3743
3743
|
async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
|
|
3744
3744
|
try {
|
|
3745
|
-
const xmtp = await import("./xmtp-
|
|
3745
|
+
const xmtp = await import("./xmtp-HC774TEI.js");
|
|
3746
3746
|
const { getAccount: getAccount2 } = await import("./client-UHHCB6EZ.js");
|
|
3747
3747
|
const group = await xmtp.getGroup("", subdomain);
|
|
3748
3748
|
const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
|
|
@@ -7194,7 +7194,7 @@ try {
|
|
|
7194
7194
|
var require2 = createRequire(import.meta.url);
|
|
7195
7195
|
var { version: CLI_VERSION } = require2("../package.json");
|
|
7196
7196
|
async function loadXmtp() {
|
|
7197
|
-
return import("./xmtp-
|
|
7197
|
+
return import("./xmtp-HC774TEI.js");
|
|
7198
7198
|
}
|
|
7199
7199
|
async function loadCron() {
|
|
7200
7200
|
return import("./cron-EOULYO3R.js");
|
|
@@ -8344,7 +8344,7 @@ ${info.name} (${info.type})`);
|
|
|
8344
8344
|
}
|
|
8345
8345
|
});
|
|
8346
8346
|
try {
|
|
8347
|
-
const { registerChatCommands } = await import("./chat-
|
|
8347
|
+
const { registerChatCommands } = await import("./chat-ZY2NTT7G.js");
|
|
8348
8348
|
registerChatCommands(program);
|
|
8349
8349
|
} catch {
|
|
8350
8350
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
@@ -8354,7 +8354,7 @@ try {
|
|
|
8354
8354
|
process.exit(1);
|
|
8355
8355
|
});
|
|
8356
8356
|
}
|
|
8357
|
-
var { registerSessionCommands } = await import("./session-
|
|
8357
|
+
var { registerSessionCommands } = await import("./session-B77JKCGZ.js");
|
|
8358
8358
|
registerSessionCommands(program);
|
|
8359
8359
|
registerVeniceCommands(program);
|
|
8360
8360
|
registerAllowanceCommands(program);
|
|
@@ -8362,11 +8362,11 @@ registerIdentityCommands(program);
|
|
|
8362
8362
|
registerProposalCommands(program);
|
|
8363
8363
|
registerGovernorCommands(program);
|
|
8364
8364
|
registerGuardianCommands(program);
|
|
8365
|
-
var { registerResearchCommands } = await import("./research-
|
|
8365
|
+
var { registerResearchCommands } = await import("./research-P6AJVTVE.js");
|
|
8366
8366
|
registerResearchCommands(program);
|
|
8367
8367
|
var { registerAgentCommands } = await import("./agent-GKHZEPOT.js");
|
|
8368
8368
|
registerAgentCommands(program);
|
|
8369
|
-
var { registerTradeCommands } = await import("./trade-
|
|
8369
|
+
var { registerTradeCommands } = await import("./trade-WB2LKYKT.js");
|
|
8370
8370
|
registerTradeCommands(program);
|
|
8371
8371
|
registerGridCommand(program);
|
|
8372
8372
|
var configCmd = program.command("config");
|
|
@@ -16,7 +16,7 @@ import ora from "ora";
|
|
|
16
16
|
import { confirm } from "@inquirer/prompts";
|
|
17
17
|
import { isAddress } from "viem";
|
|
18
18
|
async function loadXmtp() {
|
|
19
|
-
return import("./xmtp-
|
|
19
|
+
return import("./xmtp-HC774TEI.js");
|
|
20
20
|
}
|
|
21
21
|
var COST_ESTIMATES = {
|
|
22
22
|
messari: MESSARI_COST_ESTIMATE,
|
|
@@ -312,4 +312,4 @@ function registerResearchCommands(program) {
|
|
|
312
312
|
export {
|
|
313
313
|
registerResearchCommands
|
|
314
314
|
};
|
|
315
|
-
//# sourceMappingURL=research-
|
|
315
|
+
//# sourceMappingURL=research-P6AJVTVE.js.map
|
|
@@ -187,7 +187,7 @@ async function enrichProposalEvents(events, metadataCache) {
|
|
|
187
187
|
|
|
188
188
|
// src/commands/session.ts
|
|
189
189
|
async function loadXmtp() {
|
|
190
|
-
return import("./xmtp-
|
|
190
|
+
return import("./xmtp-HC774TEI.js");
|
|
191
191
|
}
|
|
192
192
|
async function resolveGovernor(vaultAddress) {
|
|
193
193
|
const client = getPublicClient();
|
|
@@ -477,4 +477,4 @@ function registerSessionCommands(program) {
|
|
|
477
477
|
export {
|
|
478
478
|
registerSessionCommands
|
|
479
479
|
};
|
|
480
|
-
//# sourceMappingURL=session-
|
|
480
|
+
//# sourceMappingURL=session-B77JKCGZ.js.map
|
|
@@ -334,7 +334,7 @@ async function getCurrentPrice(tokenAddress, tokenDecimals, feeTier) {
|
|
|
334
334
|
// src/commands/trade.ts
|
|
335
335
|
var uniswap = new UniswapProvider();
|
|
336
336
|
async function loadXmtp() {
|
|
337
|
-
return import("./xmtp-
|
|
337
|
+
return import("./xmtp-HC774TEI.js");
|
|
338
338
|
}
|
|
339
339
|
var ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
340
340
|
var KNOWN_MEMECOINS = {
|
|
@@ -946,4 +946,4 @@ function sleep(ms) {
|
|
|
946
946
|
export {
|
|
947
947
|
registerTradeCommands
|
|
948
948
|
};
|
|
949
|
-
//# sourceMappingURL=trade-
|
|
949
|
+
//# sourceMappingURL=trade-WB2LKYKT.js.map
|
|
@@ -75,7 +75,10 @@ async function getOrCreateClient() {
|
|
|
75
75
|
dbEncryptionKey: getDbEncryptionKey(config.privateKey),
|
|
76
76
|
dbPath: getDbPath()
|
|
77
77
|
});
|
|
78
|
-
await _client.conversations.syncAll([
|
|
78
|
+
await _client.conversations.syncAll([
|
|
79
|
+
ConsentState.Allowed,
|
|
80
|
+
ConsentState.Unknown
|
|
81
|
+
]);
|
|
79
82
|
if (!config._xmtpMigrated) {
|
|
80
83
|
try {
|
|
81
84
|
await _client.revokeAllOtherInstallations();
|
|
@@ -106,6 +109,10 @@ async function createSyndicateGroup(_client2, subdomain, isPublic = false) {
|
|
|
106
109
|
if (!groupId) {
|
|
107
110
|
throw new Error("Failed to create XMTP group \u2014 no group ID returned");
|
|
108
111
|
}
|
|
112
|
+
await group.updateName(subdomain);
|
|
113
|
+
await group.updateDescription(
|
|
114
|
+
`Sherwood syndicate: ${subdomain}.sherwoodagent.eth`
|
|
115
|
+
);
|
|
109
116
|
const SPECTATOR_ADDRESS = process.env.DASHBOARD_SPECTATOR_ADDRESS || "0x9f6518e69a62c526ead155ad2661f5957b6b2fc3";
|
|
110
117
|
if (isPublic) {
|
|
111
118
|
await addMember(groupId, SPECTATOR_ADDRESS);
|
|
@@ -115,7 +122,10 @@ async function createSyndicateGroup(_client2, subdomain, isPublic = false) {
|
|
|
115
122
|
}
|
|
116
123
|
async function getGroup(_client2, subdomain) {
|
|
117
124
|
const client = await getOrCreateClient();
|
|
118
|
-
await client.conversations.syncAll([
|
|
125
|
+
await client.conversations.syncAll([
|
|
126
|
+
ConsentState.Allowed,
|
|
127
|
+
ConsentState.Unknown
|
|
128
|
+
]);
|
|
119
129
|
let groupId = getCachedGroupId(subdomain);
|
|
120
130
|
if (groupId) {
|
|
121
131
|
const conv = await client.conversations.getConversationById(groupId);
|
|
@@ -164,7 +174,10 @@ async function addMember(groupId, address) {
|
|
|
164
174
|
`${address} is not reachable on XMTP. They need to initialize their client first.`
|
|
165
175
|
);
|
|
166
176
|
}
|
|
167
|
-
await client.conversations.syncAll([
|
|
177
|
+
await client.conversations.syncAll([
|
|
178
|
+
ConsentState.Allowed,
|
|
179
|
+
ConsentState.Unknown
|
|
180
|
+
]);
|
|
168
181
|
const conv = await client.conversations.getConversationById(groupId);
|
|
169
182
|
if (!conv) throw new Error(`Conversation ${groupId} not found`);
|
|
170
183
|
const group = conv;
|
|
@@ -176,12 +189,30 @@ async function addMember(groupId, address) {
|
|
|
176
189
|
}
|
|
177
190
|
async function removeMember(groupId, address) {
|
|
178
191
|
const client = await getOrCreateClient();
|
|
179
|
-
await client.conversations.syncAll([
|
|
192
|
+
await client.conversations.syncAll([
|
|
193
|
+
ConsentState.Allowed,
|
|
194
|
+
ConsentState.Unknown
|
|
195
|
+
]);
|
|
180
196
|
const conv = await client.conversations.getConversationById(groupId);
|
|
181
197
|
if (!conv) throw new Error(`Conversation ${groupId} not found`);
|
|
182
198
|
const group = conv;
|
|
183
199
|
await group.removeMembersByIdentifiers([ethIdentifier(address)]);
|
|
184
200
|
}
|
|
201
|
+
async function renameGroup(groupId, name, description) {
|
|
202
|
+
if (name === void 0 && description === void 0) {
|
|
203
|
+
throw new Error("renameGroup: at least one of name or description required");
|
|
204
|
+
}
|
|
205
|
+
const client = await getOrCreateClient();
|
|
206
|
+
await client.conversations.syncAll([
|
|
207
|
+
ConsentState.Allowed,
|
|
208
|
+
ConsentState.Unknown
|
|
209
|
+
]);
|
|
210
|
+
const conv = await client.conversations.getConversationById(groupId);
|
|
211
|
+
if (!conv) throw new Error(`Conversation ${groupId} not found`);
|
|
212
|
+
const group = conv;
|
|
213
|
+
if (name !== void 0) await group.updateName(name);
|
|
214
|
+
if (description !== void 0) await group.updateDescription(description);
|
|
215
|
+
}
|
|
185
216
|
async function sendEnvelope(groupId, envelope) {
|
|
186
217
|
const client = await getOrCreateClient();
|
|
187
218
|
const conv = await client.conversations.getConversationById(groupId);
|
|
@@ -276,9 +307,10 @@ export {
|
|
|
276
307
|
getRecentMessages,
|
|
277
308
|
getXmtpClient,
|
|
278
309
|
removeMember,
|
|
310
|
+
renameGroup,
|
|
279
311
|
sendEnvelope,
|
|
280
312
|
sendMarkdown,
|
|
281
313
|
sendReaction,
|
|
282
314
|
streamMessages
|
|
283
315
|
};
|
|
284
|
-
//# sourceMappingURL=xmtp-
|
|
316
|
+
//# sourceMappingURL=xmtp-HC774TEI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/xmtp.ts"],"sourcesContent":["/**\n * XMTP client and group operations for syndicate chat.\n *\n * Uses @xmtp/node-sdk directly with a singleton Client instance.\n * Deterministic DB path (~/.sherwood/xmtp/) and encryption key derived\n * from the sherwood private key via keccak256.\n *\n * This replaces the previous @xmtp/cli subprocess architecture which\n * caused stale MLS installations (issue #110).\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { homedir } from \"node:os\";\nimport {\n Client,\n ConsentState,\n Group,\n GroupPermissionsOptions,\n IdentifierKind,\n type CreateGroupOptions,\n type DecodedMessage,\n type Identifier,\n type NetworkOptions,\n type Signer,\n type XmtpEnv,\n} from \"@xmtp/node-sdk\";\nimport { keccak256, toBytes } from \"viem\";\nimport {\n loadConfig,\n saveConfig,\n cacheGroupId,\n getCachedGroupId,\n invalidateCachedGroupId,\n} from \"./config.js\";\nimport { getTextRecord } from \"./ens.js\";\nimport { getChainConfig } from \"./network.js\";\nimport type { ChatEnvelope } from \"./types.js\";\nimport { getAccount } from \"./client.js\";\n\n// ── Types ──\n\nexport interface XmtpMessage {\n id: string;\n conversationId: string;\n senderInboxId: string;\n contentType: string;\n content: string;\n sentAt: Date;\n}\n\nexport interface XmtpMember {\n inboxId: string;\n permissionLevel: string;\n}\n\n// ── Internal helpers ──\n\nlet _client: Client | null = null;\n\nfunction getXmtpEnv(): string {\n return getChainConfig().xmtpEnv;\n}\n\nfunction getDbEncryptionKey(privateKey: string): Uint8Array {\n const hash = keccak256(toBytes(privateKey + \"xmtp-db-key\"));\n return toBytes(hash);\n}\n\nfunction getDbPath(): string {\n const dir = path.join(homedir(), \".sherwood\", \"xmtp\");\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n return path.join(dir, \"xmtp.db3\");\n}\n\nfunction ethIdentifier(address: string): Identifier {\n return {\n identifier: address.toLowerCase(),\n identifierKind: IdentifierKind.Ethereum,\n };\n}\n\nasync function getOrCreateClient(): Promise<Client> {\n if (_client) return _client;\n\n const config = loadConfig();\n if (!config.privateKey) {\n throw new Error(\n 'No private key configured. Run \"sherwood config set --private-key 0x...\"',\n );\n }\n\n const account = getAccount();\n\n const signer: Signer = {\n type: \"EOA\",\n getIdentifier: () => ({\n identifier: account.address.toLowerCase(),\n identifierKind: IdentifierKind.Ethereum,\n }),\n signMessage: async (message: string) => {\n const signature = await account.signMessage({ message });\n return toBytes(signature);\n },\n };\n\n const clientOptions: NetworkOptions = {\n env: getXmtpEnv() as XmtpEnv,\n };\n _client = await Client.create(signer, {\n ...clientOptions,\n dbEncryptionKey: getDbEncryptionKey(config.privateKey),\n dbPath: getDbPath(),\n });\n\n // Sync all conversations on first connect (processes MLS welcome messages).\n // Includes Unknown — fresh welcomes default to Unknown consent and are missed\n // if we filter to Allowed only (agent-side group invisibility bug, 2026-05-07).\n await _client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n // One-time migration: revoke stale installations from the old ~/.xmtp/ era\n if (!config._xmtpMigrated) {\n try {\n await _client.revokeAllOtherInstallations();\n } catch {\n // Non-fatal — stale installations are a UX issue, not a blocker\n }\n config._xmtpMigrated = true;\n saveConfig(config);\n }\n\n return _client;\n}\n\n// ── Client ──\n\nexport async function getXmtpClient(): Promise<string> {\n const client = await getOrCreateClient();\n\n // Cache inbox ID\n const config = loadConfig();\n if (!config.xmtpInboxId && client.inboxId) {\n config.xmtpInboxId = client.inboxId;\n saveConfig(config);\n }\n\n return client.inboxId;\n}\n\n// ── Group Creation ──\n\nexport async function createSyndicateGroup(\n _client: string,\n subdomain: string,\n isPublic: boolean = false,\n): Promise<string> {\n const client = await getOrCreateClient();\n\n const group = await client.conversations.createGroup([], {\n name: subdomain,\n description: `Sherwood syndicate: ${subdomain}.sherwoodagent.eth`,\n permissionLevel: GroupPermissionsOptions.AdminOnly,\n } as CreateGroupOptions);\n\n const groupId = group.id;\n if (!groupId) {\n throw new Error(\"Failed to create XMTP group — no group ID returned\");\n }\n\n // Commit name/description as MLS messages so they propagate to welcome\n // recipients. Passing them in CreateGroupOptions sets only creator-local\n // metadata; without an explicit update*, member installations see empty\n // values (spectator's \"groups: 0\" symptom, 2026-05-07).\n await group.updateName(subdomain);\n await group.updateDescription(\n `Sherwood syndicate: ${subdomain}.sherwoodagent.eth`,\n );\n\n // Add spectator if requested — enables dashboard live feed\n const SPECTATOR_ADDRESS =\n process.env.DASHBOARD_SPECTATOR_ADDRESS ||\n \"0x9f6518e69a62c526ead155ad2661f5957b6b2fc3\";\n if (isPublic) {\n await addMember(groupId, SPECTATOR_ADDRESS);\n }\n\n // Cache locally\n cacheGroupId(subdomain, groupId);\n\n return groupId;\n}\n\n// ── Group Lookup ──\n\nexport async function getGroup(\n _client: string,\n subdomain: string,\n): Promise<string> {\n const client = await getOrCreateClient();\n\n // Sync to get latest conversations (including MLS welcome messages).\n // Includes Unknown so a fresh agent-side welcome (defaults to Unknown) lands.\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n // Try local cache — validate it exists in the local DB before trusting.\n // Stale cache entries (e.g. after `init --force` recreated the group) would\n // otherwise short-circuit every downstream call with a nonexistent group.\n let groupId: string | undefined = getCachedGroupId(subdomain);\n if (groupId) {\n const conv = await client.conversations.getConversationById(groupId);\n if (conv) {\n return groupId;\n }\n // Stale cache — clear and fall through to ENS / name search.\n invalidateCachedGroupId(subdomain);\n groupId = undefined;\n }\n\n // Fall back to on-chain ENS text record (may not exist on all chains)\n if (!groupId) {\n try {\n const ensId = await getTextRecord(subdomain, \"xmtpGroupId\");\n if (ensId) {\n const conv = await client.conversations.getConversationById(ensId);\n if (conv) {\n groupId = ensId;\n cacheGroupId(subdomain, groupId);\n }\n }\n } catch {\n // ENS not available on this chain (e.g. HyperEVM) — skip\n }\n }\n\n // Last resort: search synced conversations by name\n if (!groupId) {\n const groups = client.conversations.listGroups();\n const match = groups.find(\n (g) =>\n g.name === subdomain ||\n g.description?.includes(`${subdomain}.sherwoodagent.eth`),\n );\n if (match) {\n groupId = match.id;\n cacheGroupId(subdomain, groupId);\n }\n }\n\n if (!groupId) {\n throw new Error(\n `No XMTP group found for syndicate \"${subdomain}\". Run \"sherwood chat ${subdomain} init\" to create one.`,\n );\n }\n\n return groupId;\n}\n\n// ── Member Management ──\n\nexport async function addMember(\n groupId: string,\n address: string,\n): Promise<void> {\n const client = await getOrCreateClient();\n\n // Verify the target has an active XMTP identity before adding\n const reachable = await client.canMessage([ethIdentifier(address)]);\n // Try both original and lowercased as map key\n const isReachable =\n reachable.get(address) ?? reachable.get(address.toLowerCase()) ?? false;\n if (!isReachable) {\n throw new Error(\n `${address} is not reachable on XMTP. They need to initialize their client first.`,\n );\n }\n\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Cast to Group — we know syndicate conversations are always groups\n const group = conv as Group;\n await group.addMembersByIdentifiers([ethIdentifier(address)]);\n\n // Set consent to \"allowed\" so subsequent operations don't skip this conversation\n try {\n group.updateConsentState(ConsentState.Allowed);\n } catch {\n // Non-fatal — consent state is a filtering concern, not a blocker\n }\n}\n\nexport async function removeMember(\n groupId: string,\n address: string,\n): Promise<void> {\n const client = await getOrCreateClient();\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Cast to Group — we know syndicate conversations are always groups\n const group = conv as Group;\n await group.removeMembersByIdentifiers([ethIdentifier(address)]);\n}\n\n/**\n * Update a group's name and/or description as MLS commits so the change\n * propagates to all members' installations (including welcome recipients\n * that joined before metadata was committed).\n */\nexport async function renameGroup(\n groupId: string,\n name?: string,\n description?: string,\n): Promise<void> {\n if (name === undefined && description === undefined) {\n throw new Error(\"renameGroup: at least one of name or description required\");\n }\n const client = await getOrCreateClient();\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n const group = conv as Group;\n\n if (name !== undefined) await group.updateName(name);\n if (description !== undefined) await group.updateDescription(description);\n}\n\n// ── Messaging ──\n\nexport async function sendEnvelope(\n groupId: string,\n envelope: ChatEnvelope,\n): Promise<void> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n const text = JSON.stringify(envelope);\n await conv.sendText(text);\n}\n\nexport async function sendMarkdown(\n groupId: string,\n markdown: string,\n): Promise<void> {\n const envelope: ChatEnvelope = {\n type: \"MESSAGE\",\n from: getAccount().address,\n text: markdown,\n data: { format: \"markdown\" },\n timestamp: Math.floor(Date.now() / 1000),\n };\n await sendEnvelope(groupId, envelope);\n}\n\nexport async function sendReaction(\n groupId: string,\n messageId: string,\n emoji: string,\n): Promise<void> {\n const envelope: ChatEnvelope = {\n type: \"REACTION\",\n from: getAccount().address,\n data: { reference: messageId, emoji },\n timestamp: Math.floor(Date.now() / 1000),\n };\n await sendEnvelope(groupId, envelope);\n}\n\n// ── Streaming ──\n\nexport async function streamMessages(\n groupId: string,\n onMessage: (msg: XmtpMessage) => void,\n): Promise<() => void> {\n const client = await getOrCreateClient();\n\n const stream = await client.conversations.streamAllMessages({\n consentStates: [ConsentState.Allowed],\n });\n\n let stopped = false;\n\n // Consume stream in background\n (async () => {\n try {\n for await (const message of stream) {\n if (stopped) break;\n // Filter to our group\n if (message.conversationId !== groupId) continue;\n\n onMessage({\n id: message.id || \"\",\n conversationId: message.conversationId || \"\",\n senderInboxId: message.senderInboxId || \"\",\n contentType:\n typeof message.contentType === \"object\"\n ? (message.contentType as { typeId?: string })?.typeId || \"text\"\n : String(message.contentType || \"text\"),\n content:\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content),\n sentAt: message.sentAt || new Date(),\n });\n }\n } catch {\n // Stream ended or errored — expected on cleanup\n }\n })();\n\n // Return cleanup function\n return () => {\n stopped = true;\n try {\n // AsyncStreamProxy exposes end() to terminate the stream\n if (typeof (stream as any).end === \"function\") {\n (stream as any).end();\n } else if (typeof (stream as any).return === \"function\") {\n (stream as any).return();\n }\n } catch {\n // Best-effort cleanup\n }\n };\n}\n\n// ── Message History ──\n\nexport async function getRecentMessages(\n groupId: string,\n limit: number = 20,\n): Promise<XmtpMessage[]> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Sync to get latest messages\n await conv.sync();\n\n const rawMessages = await conv.messages({ limit });\n\n return rawMessages.map((m) => ({\n id: m.id || \"\",\n conversationId: m.conversationId || \"\",\n senderInboxId: m.senderInboxId || \"\",\n contentType:\n typeof m.contentType === \"object\"\n ? (m.contentType as { typeId?: string })?.typeId || \"text\"\n : String(m.contentType || \"text\"),\n content:\n typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content),\n sentAt: m.sentAt || new Date(),\n }));\n}\n\n// ── Members ──\n\nexport async function getMembers(\n groupId: string,\n): Promise<XmtpMember[]> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n const members = await conv.members();\n\n return (Array.isArray(members) ? members : []).map((m: any) => ({\n inboxId: String(m.inboxId || \"\"),\n permissionLevel:\n m.permissionLevel === 2\n ? \"super_admin\"\n : m.permissionLevel === 1\n ? \"admin\"\n : \"member\",\n }));\n}\n"],"mappings":"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","names":["_client"]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/chat.ts"],"sourcesContent":["/**\n * Chat commands — sherwood chat <syndicate-name> [action] [args...]\n *\n * Uses XMTP for encrypted group messaging tied to syndicates.\n * Shells out to the @xmtp/cli binary for all XMTP operations.\n *\n * Commander can't dispatch subcommands when the parent has a positional <name> arg\n * (it always runs the parent action). So we use manual dispatch: a single .action()\n * that routes based on the [action] argument.\n *\n * Usage:\n * sherwood chat <name> — stream messages (default)\n * sherwood chat <name> send \"hello\" — send a text message\n * sherwood chat <name> send \"hello\" --markdown — send formatted markdown\n * sherwood chat <name> react <id> <emoji> — react to a message\n * sherwood chat <name> log [--limit 50] — show recent messages\n * sherwood chat <name> members — list group members\n * sherwood chat <name> add <address> — add member (creator only)\n * sherwood chat <name> init [--force] [--public] — create XMTP group + write ENS record\n * sherwood chat <name> public on/off — toggle dashboard spectator access\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { getAccount, formatContractError } from \"../lib/client.js\";\nimport { resolveSyndicate, setTextRecord, getTextRecord } from \"../lib/ens.js\";\nimport { cacheGroupId, getCachedGroupId } from \"../lib/config.js\";\nimport { fetchMetadata, uploadMetadata } from \"../lib/ipfs.js\";\nimport * as factoryLib from \"../lib/factory.js\";\nimport { ENS } from \"../lib/addresses.js\";\nimport type { ChatEnvelope, MessageType } from \"../lib/types.js\";\nimport type { XmtpMessage } from \"../lib/xmtp.js\";\n\n// Lazy-load XMTP to avoid breaking non-chat commands when @xmtp/cli is missing\nasync function loadXmtp() {\n return import(\"../lib/xmtp.js\");\n}\n\n// ── Formatting ──\n\nfunction formatTimestamp(date: Date): string {\n return `${date.getHours().toString().padStart(2, \"0\")}:${date.getMinutes().toString().padStart(2, \"0\")}`;\n}\n\nfunction truncateAddress(addr: string): string {\n return `${addr.slice(0, 6)}...${addr.slice(-4)}`;\n}\n\nfunction colorByType(type: MessageType): (text: string) => string {\n switch (type) {\n case \"TRADE_EXECUTED\":\n return chalk.green;\n case \"RISK_ALERT\":\n return chalk.red;\n case \"TRADE_SIGNAL\":\n return chalk.yellow;\n case \"POSITION_UPDATE\":\n return chalk.cyan;\n case \"LP_REPORT\":\n return chalk.magenta;\n case \"X402_RESEARCH\":\n return chalk.cyan;\n case \"AGENT_REGISTERED\":\n case \"MEMBER_JOIN\":\n return chalk.blue;\n case \"RAGEQUIT_NOTICE\":\n return chalk.red;\n default:\n return chalk.white;\n }\n}\n\nfunction formatMessage(msg: XmtpMessage): string {\n const time = chalk.dim(`[${formatTimestamp(msg.sentAt)}]`);\n const sender = chalk.dim(truncateAddress(msg.senderInboxId));\n\n const text = msg.content;\n try {\n const envelope: ChatEnvelope = JSON.parse(text);\n const color = colorByType(envelope.type);\n const from = envelope.from ? truncateAddress(envelope.from) : sender;\n\n if (envelope.type === \"REACTION\") {\n const data = envelope.data as { reference?: string; emoji?: string } | undefined;\n return `${time} ${sender} reacted ${data?.emoji || \"?\"} to ${truncateAddress(data?.reference || \"?\")}`;\n }\n\n if (envelope.type === \"MESSAGE\") {\n if ((envelope.data as Record<string, unknown>)?.format === \"markdown\") {\n return `${time} ${chalk.dim(from)}\\n${envelope.text || \"\"}`;\n }\n return `${time} ${chalk.dim(from)}: ${envelope.text || \"\"}`;\n }\n\n if (envelope.type === \"AGENT_REGISTERED\") {\n return `${time} ${color(`[${envelope.type}]`)} Agent ${truncateAddress(envelope.agent?.address || \"?\")} registered`;\n }\n\n if (envelope.type === \"MEMBER_JOIN\") {\n return `${time} ${color(`[${envelope.type}]`)} ${truncateAddress(envelope.from || \"?\")} joined`;\n }\n\n if (envelope.type === \"X402_RESEARCH\") {\n const d = envelope.data as { provider?: string; queryType?: string; costUsdc?: string; attestationUid?: string } | undefined;\n const label = `${d?.provider || \"?\"} ${d?.queryType || \"?\"}`;\n return `${time} ${color(`[RESEARCH]`)} ${chalk.dim(from)}: ${label} ($${d?.costUsdc || \"?\"} USDC)`;\n }\n\n const summary = envelope.text || envelope.type;\n return `${time} ${color(`[${envelope.type}]`)} ${chalk.dim(from)}: ${summary}`;\n } catch {\n return `${time} ${sender}: ${text}`;\n }\n}\n\n// ── Action handlers ──\n\nasync function handleStream(name: string): Promise<void> {\n const spinner = ora(\"Connecting to chat...\").start();\n try {\n await resolveSyndicate(name);\n const xmtp = await loadXmtp();\n const client = await xmtp.getXmtpClient();\n const group = await xmtp.getGroup(client, name);\n spinner.succeed(`Connected to ${name}.sherwoodagent.eth`);\n console.log(chalk.dim(\"Streaming messages... (Ctrl+C to exit)\\n\"));\n\n const cleanup = await xmtp.streamMessages(group, (msg) => {\n console.log(formatMessage(msg));\n });\n\n process.on(\"SIGINT\", async () => {\n console.log(chalk.dim(\"\\nDisconnecting...\"));\n cleanup();\n process.exit(0);\n });\n\n await new Promise(() => {});\n } catch (err) {\n spinner.fail(\"Failed to connect to chat\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n/** Drain stdin into a single string. Used by `chat send --stdin` to bypass\n * shell expansion on dynamic messages (e.g. amounts containing `$`). */\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString('utf-8');\n}\n\nasync function handleSend(name: string, message: string, markdown: boolean): Promise<void> {\n const spinner = ora(\"Sending...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n\n if (markdown) {\n await xmtp.sendMarkdown(group, message);\n } else {\n const envelope: ChatEnvelope = {\n type: \"MESSAGE\",\n from: getAccount().address,\n text: message,\n timestamp: Math.floor(Date.now() / 1000),\n };\n await xmtp.sendEnvelope(group, envelope);\n }\n\n spinner.succeed(\"Message sent\");\n } catch (err) {\n spinner.fail(\"Failed to send message\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleReact(name: string, messageId: string, emoji: string): Promise<void> {\n const spinner = ora(\"Reacting...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.sendReaction(group, messageId, emoji);\n spinner.succeed(`Reacted ${emoji}`);\n } catch (err) {\n spinner.fail(\"Failed to send reaction\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleLog(name: string, limit: number): Promise<void> {\n const spinner = ora(\"Loading messages...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n const messages = await xmtp.getRecentMessages(group, limit);\n\n spinner.stop();\n console.log();\n console.log(chalk.bold(`Chat log: ${name}.sherwoodagent.eth`));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n if (messages.length === 0) {\n console.log(chalk.dim(\" No messages yet\"));\n } else {\n for (const msg of messages.reverse()) {\n console.log(formatMessage(msg));\n }\n }\n console.log();\n } catch (err) {\n spinner.fail(\"Failed to load messages\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleMembers(name: string): Promise<void> {\n const spinner = ora(\"Loading members...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n const members = await xmtp.getMembers(group);\n\n spinner.stop();\n console.log();\n console.log(chalk.bold(`Members: ${name}.sherwoodagent.eth`));\n console.log(chalk.dim(\"─\".repeat(50)));\n\n for (const member of members) {\n const role = member.permissionLevel === \"super_admin\"\n ? chalk.yellow(\" (super admin)\")\n : member.permissionLevel === \"admin\"\n ? chalk.blue(\" (admin)\")\n : \"\";\n console.log(` ${member.inboxId}${role}`);\n }\n\n console.log(chalk.dim(`\\n Total: ${members.length} members`));\n console.log();\n } catch (err) {\n spinner.fail(\"Failed to load members\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleAdd(name: string, address: string): Promise<void> {\n const spinner = ora(\"Adding member...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.addMember(group, address);\n\n await xmtp.sendEnvelope(group, {\n type: \"MEMBER_JOIN\",\n from: address,\n syndicate: name,\n timestamp: Math.floor(Date.now() / 1000),\n });\n\n spinner.succeed(`Member added: ${address}`);\n } catch (err) {\n spinner.fail(\"Failed to add member\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleRemove(name: string, address: string): Promise<void> {\n const spinner = ora(\"Removing member...\").start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n await xmtp.removeMember(group, address);\n spinner.succeed(`Member removed: ${address}`);\n } catch (err) {\n spinner.fail(\"Failed to remove member\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n/**\n * Notify the spectator service to sync new groups.\n * Called after adding the spectator wallet to an XMTP group so the service\n * invalidates its cache, calls syncAll(), and discovers the new group.\n */\nasync function notifySpectatorSync(): Promise<void> {\n const spectatorUrl =\n process.env.SPECTATOR_URL || \"https://spectator.sherwood.sh\";\n try {\n const res = await fetch(`${spectatorUrl}/sync`, {\n method: \"POST\",\n signal: AbortSignal.timeout(8_000),\n });\n if (res.ok) {\n const data = (await res.json()) as { groups?: number };\n console.log(\n chalk.dim(\n ` Spectator synced — ${data.groups ?? \"?\"} groups discovered`,\n ),\n );\n }\n } catch {\n // Non-fatal — spectator will self-sync within 60 s via cache TTL\n console.log(\n chalk.dim(\" (Spectator sync skipped — will auto-discover within 60 s)\"),\n );\n }\n}\n\nasync function handlePublic(name: string, on: boolean): Promise<void> {\n const spectatorAddress = process.env.DASHBOARD_SPECTATOR_ADDRESS;\n if (!spectatorAddress) {\n console.error(chalk.red(\"DASHBOARD_SPECTATOR_ADDRESS env var is required\"));\n process.exit(1);\n }\n\n const spinner = ora(`${on ? \"Enabling\" : \"Disabling\"} public chat...`).start();\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", name);\n\n if (on) {\n await xmtp.addMember(group, spectatorAddress);\n spinner.succeed(\"Public chat enabled — dashboard spectator added\");\n await notifySpectatorSync();\n } else {\n await xmtp.removeMember(group, spectatorAddress);\n spinner.succeed(\"Public chat disabled — dashboard spectator removed\");\n }\n } catch (err) {\n spinner.fail(\"Failed to toggle public chat\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\nasync function handleInit(name: string, force: boolean, isPublic: boolean): Promise<void> {\n const spinner = ora(\"Initializing chat group...\").start();\n try {\n const syndicate = await resolveSyndicate(name);\n const callerAddress = getAccount().address.toLowerCase();\n if (syndicate.creator.toLowerCase() !== callerAddress) {\n spinner.fail(\"Only the syndicate creator can initialize the chat group\");\n process.exit(1);\n }\n\n // Idempotency check\n if (!force) {\n const existingId = getCachedGroupId(name) || await getTextRecord(name, \"xmtpGroupId\");\n if (existingId) {\n spinner.succeed(\"XMTP group already exists for this syndicate\");\n console.log(chalk.dim(` Group ID: ${existingId}`));\n return;\n }\n }\n\n // Create the group\n spinner.text = \"Creating XMTP group...\";\n const xmtp = await loadXmtp();\n const client = await xmtp.getXmtpClient();\n const groupId = await xmtp.createSyndicateGroup(client, name, isPublic);\n\n cacheGroupId(name, groupId);\n\n // Persist group ID: ENS on chains with L2 Registry, IPFS metadata otherwise\n const hasENS = ENS().L2_REGISTRY !== \"0x0000000000000000000000000000000000000000\";\n if (hasENS) {\n try {\n spinner.text = \"Writing group ID to ENS...\";\n await setTextRecord(name, \"xmtpGroupId\", groupId, syndicate.vault);\n } catch (ensErr) {\n console.warn(chalk.yellow(\"\\n ⚠ Could not write ENS text record\"));\n console.warn(chalk.dim(` ${ensErr instanceof Error ? ensErr.message : String(ensErr)}`));\n }\n } else {\n // No ENS (e.g. HyperEVM) — persist in IPFS metadata\n try {\n spinner.text = \"Saving group ID to syndicate metadata...\";\n const info = await factoryLib.getSyndicate(syndicate.id);\n let metadata;\n try {\n metadata = await fetchMetadata(info.metadataURI);\n } catch {\n metadata = { schema: \"sherwood/syndicate/v1\", name, description: \"\", chain: \"\", strategies: [], terms: {}, links: {} };\n }\n metadata.xmtpGroupId = groupId;\n const newURI = await uploadMetadata(metadata);\n await factoryLib.updateMetadata(syndicate.id, newURI);\n console.log(chalk.dim(`\\n Group ID saved to metadata: ${newURI}`));\n } catch (metaErr) {\n console.warn(chalk.yellow(\"\\n ⚠ Could not persist group ID (cached locally only)\"));\n console.warn(chalk.dim(` ${metaErr instanceof Error ? metaErr.message : String(metaErr)}`));\n }\n }\n\n spinner.succeed(`Chat group created for ${name}.sherwoodagent.eth`);\n console.log(chalk.dim(` Group ID: ${groupId}`));\n console.log(chalk.dim(` Stream: sherwood chat ${name}`));\n\n // Notify spectator to sync and discover the new public group\n if (isPublic) {\n await notifySpectatorSync();\n }\n } catch (err) {\n spinner.fail(\"Failed to initialize chat group\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n}\n\n// ── Command Registration ──\n\nexport function registerChatCommands(program: Command): void {\n program\n .command(\"chat <name> [action] [actionArgs...]\")\n .description(\"Syndicate chat — stream, send, log, members, add, remove, init, public\")\n .option(\"--markdown\", \"Send as rich markdown (for send)\", false)\n .option(\"--stdin\", \"Read send message from stdin instead of CLI arg (avoids shell quoting issues with $ chars and quotes)\", false)\n .option(\"--limit <n>\", \"Number of messages to show (for log)\", \"20\")\n .option(\"--force\", \"Recreate group even if one exists (for init)\", false)\n .option(\"--public\", \"Enable public chat — adds dashboard spectator (for init)\", false)\n .action(async (name: string, action: string | undefined, actionArgs: string[], opts: { markdown: boolean; stdin: boolean; limit: string; force: boolean; public: boolean }) => {\n switch (action) {\n case \"send\": {\n let message = actionArgs[0];\n if (opts.stdin) {\n // If stdin is a terminal, the user forgot to pipe — readStdin\n // would block forever waiting for EOF (Ctrl+D). Warn explicitly.\n if (process.stdin.isTTY) {\n console.error(chalk.yellow(\"--stdin specified but stdin is a terminal. Either pipe the message in or omit --stdin and pass it as an argument. Press Ctrl+D to send what you've typed, Ctrl+C to abort.\"));\n }\n // Read entire stdin into the message — sidesteps bash $-expansion\n // when callers (cron jobs, scripts) construct dynamic message text.\n message = await readStdin();\n if (!message.trim()) {\n console.error(chalk.red(\"--stdin given but stdin was empty\"));\n process.exit(1);\n }\n }\n if (!message) {\n console.error(chalk.red(\"Usage: sherwood chat <name> send <message> [--markdown]\\n echo \\\"$msg\\\" | sherwood chat <name> send --stdin\"));\n process.exit(1);\n }\n await handleSend(name, message, opts.markdown);\n break;\n }\n\n case \"react\": {\n const [messageId, emoji] = actionArgs;\n if (!messageId || !emoji) {\n console.error(chalk.red(\"Usage: sherwood chat <name> react <messageId> <emoji>\"));\n process.exit(1);\n }\n await handleReact(name, messageId, emoji);\n break;\n }\n\n case \"log\":\n await handleLog(name, parseInt(opts.limit, 10));\n break;\n\n case \"members\":\n await handleMembers(name);\n break;\n\n case \"add\": {\n const address = actionArgs[0];\n if (!address) {\n console.error(chalk.red(\"Usage: sherwood chat <name> add <address>\"));\n process.exit(1);\n }\n await handleAdd(name, address);\n break;\n }\n\n case \"remove\": {\n const removeAddr = actionArgs[0];\n if (!removeAddr) {\n console.error(chalk.red(\"Usage: sherwood chat <name> remove <address>\"));\n process.exit(1);\n }\n await handleRemove(name, removeAddr);\n break;\n }\n\n case \"init\":\n await handleInit(name, opts.force, opts.public);\n break;\n\n case \"public\": {\n const flag = actionArgs[0];\n if (flag !== \"on\" && flag !== \"off\") {\n console.error(chalk.red(\"Usage: sherwood chat <name> public on/off\"));\n process.exit(1);\n }\n await handlePublic(name, flag === \"on\");\n break;\n }\n\n case undefined:\n default:\n await handleStream(name);\n break;\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/xmtp.ts"],"sourcesContent":["/**\n * XMTP client and group operations for syndicate chat.\n *\n * Uses @xmtp/node-sdk directly with a singleton Client instance.\n * Deterministic DB path (~/.sherwood/xmtp/) and encryption key derived\n * from the sherwood private key via keccak256.\n *\n * This replaces the previous @xmtp/cli subprocess architecture which\n * caused stale MLS installations (issue #110).\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { homedir } from \"node:os\";\nimport {\n Client,\n ConsentState,\n Group,\n GroupPermissionsOptions,\n IdentifierKind,\n type CreateGroupOptions,\n type DecodedMessage,\n type Identifier,\n type NetworkOptions,\n type Signer,\n type XmtpEnv,\n} from \"@xmtp/node-sdk\";\nimport { keccak256, toBytes } from \"viem\";\nimport {\n loadConfig,\n saveConfig,\n cacheGroupId,\n getCachedGroupId,\n invalidateCachedGroupId,\n} from \"./config.js\";\nimport { getTextRecord } from \"./ens.js\";\nimport { getChainConfig } from \"./network.js\";\nimport type { ChatEnvelope } from \"./types.js\";\nimport { getAccount } from \"./client.js\";\n\n// ── Types ──\n\nexport interface XmtpMessage {\n id: string;\n conversationId: string;\n senderInboxId: string;\n contentType: string;\n content: string;\n sentAt: Date;\n}\n\nexport interface XmtpMember {\n inboxId: string;\n permissionLevel: string;\n}\n\n// ── Internal helpers ──\n\nlet _client: Client | null = null;\n\nfunction getXmtpEnv(): string {\n return getChainConfig().xmtpEnv;\n}\n\nfunction getDbEncryptionKey(privateKey: string): Uint8Array {\n const hash = keccak256(toBytes(privateKey + \"xmtp-db-key\"));\n return toBytes(hash);\n}\n\nfunction getDbPath(): string {\n const dir = path.join(homedir(), \".sherwood\", \"xmtp\");\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n return path.join(dir, \"xmtp.db3\");\n}\n\nfunction ethIdentifier(address: string): Identifier {\n return {\n identifier: address.toLowerCase(),\n identifierKind: IdentifierKind.Ethereum,\n };\n}\n\nasync function getOrCreateClient(): Promise<Client> {\n if (_client) return _client;\n\n const config = loadConfig();\n if (!config.privateKey) {\n throw new Error(\n 'No private key configured. Run \"sherwood config set --private-key 0x...\"',\n );\n }\n\n const account = getAccount();\n\n const signer: Signer = {\n type: \"EOA\",\n getIdentifier: () => ({\n identifier: account.address.toLowerCase(),\n identifierKind: IdentifierKind.Ethereum,\n }),\n signMessage: async (message: string) => {\n const signature = await account.signMessage({ message });\n return toBytes(signature);\n },\n };\n\n const clientOptions: NetworkOptions = {\n env: getXmtpEnv() as XmtpEnv,\n };\n _client = await Client.create(signer, {\n ...clientOptions,\n dbEncryptionKey: getDbEncryptionKey(config.privateKey),\n dbPath: getDbPath(),\n });\n\n // Sync all conversations on first connect (processes MLS welcome messages)\n await _client.conversations.syncAll([ConsentState.Allowed]);\n\n // One-time migration: revoke stale installations from the old ~/.xmtp/ era\n if (!config._xmtpMigrated) {\n try {\n await _client.revokeAllOtherInstallations();\n } catch {\n // Non-fatal — stale installations are a UX issue, not a blocker\n }\n config._xmtpMigrated = true;\n saveConfig(config);\n }\n\n return _client;\n}\n\n// ── Client ──\n\nexport async function getXmtpClient(): Promise<string> {\n const client = await getOrCreateClient();\n\n // Cache inbox ID\n const config = loadConfig();\n if (!config.xmtpInboxId && client.inboxId) {\n config.xmtpInboxId = client.inboxId;\n saveConfig(config);\n }\n\n return client.inboxId;\n}\n\n// ── Group Creation ──\n\nexport async function createSyndicateGroup(\n _client: string,\n subdomain: string,\n isPublic: boolean = false,\n): Promise<string> {\n const client = await getOrCreateClient();\n\n const group = await client.conversations.createGroup([], {\n name: subdomain,\n description: `Sherwood syndicate: ${subdomain}.sherwoodagent.eth`,\n permissionLevel: GroupPermissionsOptions.AdminOnly,\n } as CreateGroupOptions);\n\n const groupId = group.id;\n if (!groupId) {\n throw new Error(\"Failed to create XMTP group — no group ID returned\");\n }\n\n // Add spectator if requested — enables dashboard live feed\n const SPECTATOR_ADDRESS =\n process.env.DASHBOARD_SPECTATOR_ADDRESS ||\n \"0x9f6518e69a62c526ead155ad2661f5957b6b2fc3\";\n if (isPublic) {\n await addMember(groupId, SPECTATOR_ADDRESS);\n }\n\n // Cache locally\n cacheGroupId(subdomain, groupId);\n\n return groupId;\n}\n\n// ── Group Lookup ──\n\nexport async function getGroup(\n _client: string,\n subdomain: string,\n): Promise<string> {\n const client = await getOrCreateClient();\n\n // Sync to get latest conversations (including MLS welcome messages)\n await client.conversations.syncAll([ConsentState.Allowed]);\n\n // Try local cache — validate it exists in the local DB before trusting.\n // Stale cache entries (e.g. after `init --force` recreated the group) would\n // otherwise short-circuit every downstream call with a nonexistent group.\n let groupId: string | undefined = getCachedGroupId(subdomain);\n if (groupId) {\n const conv = await client.conversations.getConversationById(groupId);\n if (conv) {\n return groupId;\n }\n // Stale cache — clear and fall through to ENS / name search.\n invalidateCachedGroupId(subdomain);\n groupId = undefined;\n }\n\n // Fall back to on-chain ENS text record (may not exist on all chains)\n if (!groupId) {\n try {\n const ensId = await getTextRecord(subdomain, \"xmtpGroupId\");\n if (ensId) {\n const conv = await client.conversations.getConversationById(ensId);\n if (conv) {\n groupId = ensId;\n cacheGroupId(subdomain, groupId);\n }\n }\n } catch {\n // ENS not available on this chain (e.g. HyperEVM) — skip\n }\n }\n\n // Last resort: search synced conversations by name\n if (!groupId) {\n const groups = client.conversations.listGroups();\n const match = groups.find(\n (g) =>\n g.name === subdomain ||\n g.description?.includes(`${subdomain}.sherwoodagent.eth`),\n );\n if (match) {\n groupId = match.id;\n cacheGroupId(subdomain, groupId);\n }\n }\n\n if (!groupId) {\n throw new Error(\n `No XMTP group found for syndicate \"${subdomain}\". Run \"sherwood chat ${subdomain} init\" to create one.`,\n );\n }\n\n return groupId;\n}\n\n// ── Member Management ──\n\nexport async function addMember(\n groupId: string,\n address: string,\n): Promise<void> {\n const client = await getOrCreateClient();\n\n // Verify the target has an active XMTP identity before adding\n const reachable = await client.canMessage([ethIdentifier(address)]);\n // Try both original and lowercased as map key\n const isReachable =\n reachable.get(address) ?? reachable.get(address.toLowerCase()) ?? false;\n if (!isReachable) {\n throw new Error(\n `${address} is not reachable on XMTP. They need to initialize their client first.`,\n );\n }\n\n await client.conversations.syncAll([ConsentState.Allowed]);\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Cast to Group — we know syndicate conversations are always groups\n const group = conv as Group;\n await group.addMembersByIdentifiers([ethIdentifier(address)]);\n\n // Set consent to \"allowed\" so subsequent operations don't skip this conversation\n try {\n group.updateConsentState(ConsentState.Allowed);\n } catch {\n // Non-fatal — consent state is a filtering concern, not a blocker\n }\n}\n\nexport async function removeMember(\n groupId: string,\n address: string,\n): Promise<void> {\n const client = await getOrCreateClient();\n await client.conversations.syncAll([ConsentState.Allowed]);\n\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Cast to Group — we know syndicate conversations are always groups\n const group = conv as Group;\n await group.removeMembersByIdentifiers([ethIdentifier(address)]);\n}\n\n// ── Messaging ──\n\nexport async function sendEnvelope(\n groupId: string,\n envelope: ChatEnvelope,\n): Promise<void> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n const text = JSON.stringify(envelope);\n await conv.sendText(text);\n}\n\nexport async function sendMarkdown(\n groupId: string,\n markdown: string,\n): Promise<void> {\n const envelope: ChatEnvelope = {\n type: \"MESSAGE\",\n from: getAccount().address,\n text: markdown,\n data: { format: \"markdown\" },\n timestamp: Math.floor(Date.now() / 1000),\n };\n await sendEnvelope(groupId, envelope);\n}\n\nexport async function sendReaction(\n groupId: string,\n messageId: string,\n emoji: string,\n): Promise<void> {\n const envelope: ChatEnvelope = {\n type: \"REACTION\",\n from: getAccount().address,\n data: { reference: messageId, emoji },\n timestamp: Math.floor(Date.now() / 1000),\n };\n await sendEnvelope(groupId, envelope);\n}\n\n// ── Streaming ──\n\nexport async function streamMessages(\n groupId: string,\n onMessage: (msg: XmtpMessage) => void,\n): Promise<() => void> {\n const client = await getOrCreateClient();\n\n const stream = await client.conversations.streamAllMessages({\n consentStates: [ConsentState.Allowed],\n });\n\n let stopped = false;\n\n // Consume stream in background\n (async () => {\n try {\n for await (const message of stream) {\n if (stopped) break;\n // Filter to our group\n if (message.conversationId !== groupId) continue;\n\n onMessage({\n id: message.id || \"\",\n conversationId: message.conversationId || \"\",\n senderInboxId: message.senderInboxId || \"\",\n contentType:\n typeof message.contentType === \"object\"\n ? (message.contentType as { typeId?: string })?.typeId || \"text\"\n : String(message.contentType || \"text\"),\n content:\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content),\n sentAt: message.sentAt || new Date(),\n });\n }\n } catch {\n // Stream ended or errored — expected on cleanup\n }\n })();\n\n // Return cleanup function\n return () => {\n stopped = true;\n try {\n // AsyncStreamProxy exposes end() to terminate the stream\n if (typeof (stream as any).end === \"function\") {\n (stream as any).end();\n } else if (typeof (stream as any).return === \"function\") {\n (stream as any).return();\n }\n } catch {\n // Best-effort cleanup\n }\n };\n}\n\n// ── Message History ──\n\nexport async function getRecentMessages(\n groupId: string,\n limit: number = 20,\n): Promise<XmtpMessage[]> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Sync to get latest messages\n await conv.sync();\n\n const rawMessages = await conv.messages({ limit });\n\n return rawMessages.map((m) => ({\n id: m.id || \"\",\n conversationId: m.conversationId || \"\",\n senderInboxId: m.senderInboxId || \"\",\n contentType:\n typeof m.contentType === \"object\"\n ? (m.contentType as { typeId?: string })?.typeId || \"text\"\n : String(m.contentType || \"text\"),\n content:\n typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content),\n sentAt: m.sentAt || new Date(),\n }));\n}\n\n// ── Members ──\n\nexport async function getMembers(\n groupId: string,\n): Promise<XmtpMember[]> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n const members = await conv.members();\n\n return (Array.isArray(members) ? members : []).map((m: any) => ({\n inboxId: String(m.inboxId || \"\"),\n permissionLevel:\n m.permissionLevel === 2\n ? \"super_admin\"\n : m.permissionLevel === 1\n ? \"admin\"\n : \"member\",\n }));\n}\n"],"mappings":"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","names":["_client"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|