@sherwoodagent/cli 0.59.17 → 0.59.18
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-IPSP5FBQ.js → chat-SL4O7Y3C.js} +11 -10
- package/dist/chat-SL4O7Y3C.js.map +1 -0
- package/dist/index.js +6 -6
- package/dist/{research-LRIALMFZ.js → research-RL2ASMQ5.js} +2 -2
- package/dist/{session-NQBWTJYS.js → session-FPQGAMSS.js} +2 -2
- package/dist/{trade-SRM3OYFW.js → trade-AJNB4ULF.js} +2 -2
- package/dist/{xmtp-BNPM7HJY.js → xmtp-MR6OJ76L.js} +8 -3
- package/dist/xmtp-MR6OJ76L.js.map +1 -0
- package/package.json +1 -1
- package/dist/chat-IPSP5FBQ.js.map +0 -1
- package/dist/xmtp-BNPM7HJY.js.map +0 -1
- /package/dist/{research-LRIALMFZ.js.map → research-RL2ASMQ5.js.map} +0 -0
- /package/dist/{session-NQBWTJYS.js.map → session-FPQGAMSS.js.map} +0 -0
- /package/dist/{trade-SRM3OYFW.js.map → trade-AJNB4ULF.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-MR6OJ76L.js");
|
|
33
33
|
}
|
|
34
34
|
function formatTimestamp(date) {
|
|
35
35
|
return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
|
|
@@ -380,22 +380,23 @@ async function handlePublishEns(name, groupIdOpt) {
|
|
|
380
380
|
}
|
|
381
381
|
}
|
|
382
382
|
async function handlePublic(name, on) {
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
process.exit(1);
|
|
387
|
-
}
|
|
383
|
+
const xmtp = await loadXmtp();
|
|
384
|
+
const spectatorAddress = xmtp.resolveSpectatorAddress();
|
|
385
|
+
const usingDefault = spectatorAddress === xmtp.DEFAULT_DASHBOARD_SPECTATOR_ADDRESS;
|
|
388
386
|
const spinner = ora(`${on ? "Enabling" : "Disabling"} public chat...`).start();
|
|
389
387
|
try {
|
|
390
|
-
const xmtp = await loadXmtp();
|
|
391
388
|
const group = await xmtp.getGroup("", name);
|
|
392
389
|
if (on) {
|
|
393
390
|
await xmtp.addMember(group, spectatorAddress);
|
|
394
|
-
spinner.succeed(
|
|
391
|
+
spinner.succeed(
|
|
392
|
+
`Public chat enabled \u2014 dashboard spectator added (${spectatorAddress}${usingDefault ? ", canonical" : ", overridden via env"})`
|
|
393
|
+
);
|
|
395
394
|
await notifySpectatorSync();
|
|
396
395
|
} else {
|
|
397
396
|
await xmtp.removeMember(group, spectatorAddress);
|
|
398
|
-
spinner.succeed(
|
|
397
|
+
spinner.succeed(
|
|
398
|
+
`Public chat disabled \u2014 dashboard spectator removed (${spectatorAddress})`
|
|
399
|
+
);
|
|
399
400
|
}
|
|
400
401
|
} catch (err) {
|
|
401
402
|
spinner.fail("Failed to toggle public chat");
|
|
@@ -562,4 +563,4 @@ function registerChatCommands(program) {
|
|
|
562
563
|
export {
|
|
563
564
|
registerChatCommands
|
|
564
565
|
};
|
|
565
|
-
//# sourceMappingURL=chat-
|
|
566
|
+
//# sourceMappingURL=chat-SL4O7Y3C.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 xmtp = await loadXmtp();\n // Default to the canonical production spectator; `DASHBOARD_SPECTATOR_ADDRESS`\n // is an override hook for testing or future rotation. Earlier versions\n // of this command required the env var with no default — when callers\n // (cron, manual one-liners) set it to the wrong wallet, the agent's\n // own wallet got added \"as the spectator\" and the dashboard stayed\n // OFFLINE because the real spectator service was never in the group.\n // Single source of truth lives in xmtp.ts: resolveSpectatorAddress().\n const spectatorAddress = xmtp.resolveSpectatorAddress();\n const usingDefault =\n spectatorAddress === xmtp.DEFAULT_DASHBOARD_SPECTATOR_ADDRESS;\n\n const spinner = ora(`${on ? \"Enabling\" : \"Disabling\"} public chat...`).start();\n try {\n const group = await xmtp.getGroup(\"\", name);\n\n if (on) {\n await xmtp.addMember(group, spectatorAddress);\n spinner.succeed(\n `Public chat enabled — dashboard spectator added (${spectatorAddress}${usingDefault ? \", canonical\" : \", overridden via env\"})`,\n );\n await notifySpectatorSync();\n } else {\n await xmtp.removeMember(group, spectatorAddress);\n spinner.succeed(\n `Public chat disabled — dashboard spectator removed (${spectatorAddress})`,\n );\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. Strategy:\n // 1. ALWAYS write to IPFS metadata — canonical source on every chain.\n // The dashboard's resolver chain is `ENS text → metadata → null`, so\n // metadata being populated guarantees the dashboard works regardless\n // of ENS state. Necessary because the V1.5 vault has no `executeBatch`\n // (V-C3 removal), so the ENS `setText` call below currently reverts on\n // Base mainnet — without metadata, the dashboard would show OFFLINE.\n // 2. ADDITIONALLY try the ENS text record (best-effort). Fast resolution\n // path for chains where the vault has a writable executeBatch path\n // (HyperEVM today, or a future vault setEnsText helper). On failure,\n // we log a debug line and keep going — metadata fallback covers it.\n spinner.text = \"Saving group ID to syndicate metadata...\";\n try {\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 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 to IPFS metadata\"));\n console.warn(chalk.dim(` ${metaErr instanceof Error ? metaErr.message : String(metaErr)}`));\n console.warn(chalk.dim(\" Dashboard may show OFFLINE until backfilled with `chat <name> publish-metadata`.\"));\n }\n\n // Best-effort ENS text record write. Currently reverts on Base mainnet\n // because the V1.5 vault has no owner-direct executeBatch path; metadata\n // above is the operative resolution source. Will succeed once a vault\n // setEnsText helper lands or on chains where the existing path works.\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 console.log(chalk.dim(\" ENS text record written: xmtpGroupId\"));\n } catch {\n console.log(chalk.dim(\" ENS text record skipped (metadata fallback active)\"));\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,OAAO,WAAW;AAClB,OAAO,SAAS;AAWhB,eAAe,WAAW;AACxB,SAAO,OAAO,oBAAgB;AAChC;AAIA,SAAS,gBAAgB,MAAoB;AAC3C,SAAO,GAAG,KAAK,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AACxG;AAEA,SAAS,gBAAgB,MAAsB;AAC7C,SAAO,GAAG,KAAK,MAAM,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;AAChD;AAEA,SAAS,YAAY,MAA6C;AAChE,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AAAA,IACL,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf;AACE,aAAO,MAAM;AAAA,EACjB;AACF;AAEA,SAAS,cAAc,KAA0B;AAC/C,QAAM,OAAO,MAAM,IAAI,IAAI,gBAAgB,IAAI,MAAM,CAAC,GAAG;AACzD,QAAM,SAAS,MAAM,IAAI,gBAAgB,IAAI,aAAa,CAAC;AAE3D,QAAM,OAAO,IAAI;AACjB,MAAI;AACF,UAAM,WAAyB,KAAK,MAAM,IAAI;AAC9C,UAAM,QAAQ,YAAY,SAAS,IAAI;AACvC,UAAM,OAAO,SAAS,OAAO,gBAAgB,SAAS,IAAI,IAAI;AAE9D,QAAI,SAAS,SAAS,YAAY;AAChC,YAAM,OAAO,SAAS;AACtB,aAAO,GAAG,IAAI,IAAI,MAAM,YAAY,MAAM,SAAS,GAAG,OAAO,gBAAgB,MAAM,aAAa,GAAG,CAAC;AAAA,IACtG;AAEA,QAAI,SAAS,SAAS,WAAW;AAC/B,UAAK,SAAS,MAAkC,WAAW,YAAY;AACrE,eAAO,GAAG,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC;AAAA,EAAK,SAAS,QAAQ,EAAE;AAAA,MAC3D;AACA,aAAO,GAAG,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,KAAK,SAAS,QAAQ,EAAE;AAAA,IAC3D;AAEA,QAAI,SAAS,SAAS,oBAAoB;AACxC,aAAO,GAAG,IAAI,IAAI,MAAM,IAAI,SAAS,IAAI,GAAG,CAAC,UAAU,gBAAgB,SAAS,OAAO,WAAW,GAAG,CAAC;AAAA,IACxG;AAEA,QAAI,SAAS,SAAS,eAAe;AACnC,aAAO,GAAG,IAAI,IAAI,MAAM,IAAI,SAAS,IAAI,GAAG,CAAC,IAAI,gBAAgB,SAAS,QAAQ,GAAG,CAAC;AAAA,IACxF;AAEA,QAAI,SAAS,SAAS,iBAAiB;AACrC,YAAM,IAAI,SAAS;AACnB,YAAM,QAAQ,GAAG,GAAG,YAAY,GAAG,IAAI,GAAG,aAAa,GAAG;AAC1D,aAAO,GAAG,IAAI,IAAI,MAAM,YAAY,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,GAAG,YAAY,GAAG;AAAA,IAC5F;AAEA,UAAM,UAAU,SAAS,QAAQ,SAAS;AAC1C,WAAO,GAAG,IAAI,IAAI,MAAM,IAAI,SAAS,IAAI,GAAG,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,KAAK,OAAO;AAAA,EAC9E,QAAQ;AACN,WAAO,GAAG,IAAI,IAAI,MAAM,KAAK,IAAI;AAAA,EACnC;AACF;AAIA,eAAe,aAAa,MAA6B;AACvD,QAAM,UAAU,IAAI,uBAAuB,EAAE,MAAM;AACnD,MAAI;AACF,UAAM,iBAAiB,IAAI;AAC3B,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,SAAS,MAAM,KAAK,cAAc;AACxC,UAAM,QAAQ,MAAM,KAAK,SAAS,QAAQ,IAAI;AAC9C,YAAQ,QAAQ,gBAAgB,IAAI,oBAAoB;AACxD,YAAQ,IAAI,MAAM,IAAI,0CAA0C,CAAC;AAEjE,UAAM,UAAU,MAAM,KAAK,eAAe,OAAO,CAAC,QAAQ;AACxD,cAAQ,IAAI,cAAc,GAAG,CAAC;AAAA,IAChC,CAAC;AAED,YAAQ,GAAG,UAAU,YAAY;AAC/B,cAAQ,IAAI,MAAM,IAAI,oBAAoB,CAAC;AAC3C,cAAQ;AACR,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAED,UAAM,IAAI,QAAQ,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5B,SAAS,KAAK;AACZ,YAAQ,KAAK,2BAA2B;AACxC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAIA,eAAe,YAA6B;AAC1C,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC;AAAA,EACjE;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AAC/C;AAEA,eAAe,WAAW,MAAc,SAAiB,UAAkC;AACzF,QAAM,UAAU,IAAI,YAAY,EAAE,MAAM;AACxC,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAE1C,QAAI,UAAU;AACZ,YAAM,KAAK,aAAa,OAAO,OAAO;AAAA,IACxC,OAAO;AACL,YAAM,WAAyB;AAAA,QAC7B,MAAM;AAAA,QACN,MAAM,WAAW,EAAE;AAAA,QACnB,MAAM;AAAA,QACN,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,MACzC;AACA,YAAM,KAAK,aAAa,OAAO,QAAQ;AAAA,IACzC;AAEA,YAAQ,QAAQ,cAAc;AAAA,EAChC,SAAS,KAAK;AACZ,YAAQ,KAAK,wBAAwB;AACrC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,YAAY,MAAc,WAAmB,OAA8B;AACxF,QAAM,UAAU,IAAI,aAAa,EAAE,MAAM;AACzC,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAC1C,UAAM,KAAK,aAAa,OAAO,WAAW,KAAK;AAC/C,YAAQ,QAAQ,WAAW,KAAK,EAAE;AAAA,EACpC,SAAS,KAAK;AACZ,YAAQ,KAAK,yBAAyB;AACtC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,UAAU,MAAc,OAA8B;AACnE,QAAM,UAAU,IAAI,qBAAqB,EAAE,MAAM;AACjD,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAC1C,UAAM,WAAW,MAAM,KAAK,kBAAkB,OAAO,KAAK;AAE1D,YAAQ,KAAK;AACb,YAAQ,IAAI;AACZ,YAAQ,IAAI,MAAM,KAAK,aAAa,IAAI,oBAAoB,CAAC;AAC7D,YAAQ,IAAI,MAAM,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAErC,QAAI,SAAS,WAAW,GAAG;AACzB,cAAQ,IAAI,MAAM,IAAI,mBAAmB,CAAC;AAAA,IAC5C,OAAO;AACL,iBAAW,OAAO,SAAS,QAAQ,GAAG;AACpC,gBAAQ,IAAI,cAAc,GAAG,CAAC;AAAA,MAChC;AAAA,IACF;AACA,YAAQ,IAAI;AAAA,EACd,SAAS,KAAK;AACZ,YAAQ,KAAK,yBAAyB;AACtC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,cAAc,MAA6B;AACxD,QAAM,UAAU,IAAI,oBAAoB,EAAE,MAAM;AAChD,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAC1C,UAAM,UAAU,MAAM,KAAK,WAAW,KAAK;AAE3C,YAAQ,KAAK;AACb,YAAQ,IAAI;AACZ,YAAQ,IAAI,MAAM,KAAK,YAAY,IAAI,oBAAoB,CAAC;AAC5D,YAAQ,IAAI,MAAM,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAErC,eAAW,UAAU,SAAS;AAC5B,YAAM,OAAO,OAAO,oBAAoB,gBACpC,MAAM,OAAO,gBAAgB,IAC7B,OAAO,oBAAoB,UACzB,MAAM,KAAK,UAAU,IACrB;AACN,cAAQ,IAAI,KAAK,OAAO,OAAO,GAAG,IAAI,EAAE;AAAA,IAC1C;AAEA,YAAQ,IAAI,MAAM,IAAI;AAAA,WAAc,QAAQ,MAAM,UAAU,CAAC;AAC7D,YAAQ,IAAI;AAAA,EACd,SAAS,KAAK;AACZ,YAAQ,KAAK,wBAAwB;AACrC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,UAAU,MAAc,SAAgC;AACrE,QAAM,UAAU,IAAI,kBAAkB,EAAE,MAAM;AAC9C,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAC1C,UAAM,KAAK,UAAU,OAAO,OAAO;AAEnC,UAAM,KAAK,aAAa,OAAO;AAAA,MAC7B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,WAAW;AAAA,MACX,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACzC,CAAC;AAED,YAAQ,QAAQ,iBAAiB,OAAO,EAAE;AAAA,EAC5C,SAAS,KAAK;AACZ,YAAQ,KAAK,sBAAsB;AACnC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,aAAa,MAAc,SAAgC;AACxE,QAAM,UAAU,IAAI,oBAAoB,EAAE,MAAM;AAChD,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAC1C,UAAM,KAAK,aAAa,OAAO,OAAO;AACtC,YAAQ,QAAQ,mBAAmB,OAAO,EAAE;AAAA,EAC9C,SAAS,KAAK;AACZ,YAAQ,KAAK,yBAAyB;AACtC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAOA,eAAe,sBAAqC;AAClD,QAAM,eACJ,QAAQ,IAAI,iBAAiB;AAC/B,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,YAAY,SAAS;AAAA,MAC9C,QAAQ;AAAA,MACR,QAAQ,YAAY,QAAQ,GAAK;AAAA,IACnC,CAAC;AACD,QAAI,IAAI,IAAI;AACV,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,cAAQ;AAAA,QACN,MAAM;AAAA,UACJ,6BAAwB,KAAK,UAAU,GAAG;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAEN,YAAQ;AAAA,MACN,MAAM,IAAI,kEAA6D;AAAA,IACzE;AAAA,EACF;AACF;AAEA,eAAe,aACb,MACA,SACA,gBACe;AACf,MAAI,YAAY,UAAa,mBAAmB,QAAW;AACzD,YAAQ;AAAA,MACN,MAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,UAAU,IAAI,4BAA4B,EAAE,MAAM;AACxD,MAAI;AACF,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAC1C,UAAM,KAAK,YAAY,OAAO,SAAS,cAAc;AACrD,YAAQ;AAAA,MACN,WAAW;AAAA,QACT,YAAY,SAAY,gBAAW,OAAO,MAAM;AAAA,QAChD,mBAAmB,SACf,uBAAkB,cAAc,MAChC;AAAA,MACN,EACG,OAAO,OAAO,EACd,KAAK,IAAI,CAAC;AAAA,IACf;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,KAAK,iCAAiC;AAC9C,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,sBACb,MACA,YACe;AACf,QAAM,UAAU,IAAI,gCAAgC,EAAE,MAAM;AAC5D,MAAI;AACF,UAAM,YAAY,MAAM,iBAAiB,IAAI;AAC7C,UAAM,gBAAgB,WAAW,EAAE,QAAQ,YAAY;AACvD,QAAI,UAAU,QAAQ,YAAY,MAAM,eAAe;AACrD,cAAQ,KAAK,gDAAgD;AAC7D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,OAAO,MAAM,SAAS;AAC5B,QAAI,UAAU,cAAc,iBAAiB,IAAI;AACjD,QAAI,CAAC,SAAS;AACZ,UAAI;AACF,kBAAU,MAAM,KAAK,SAAS,IAAI,IAAI;AAAA,MACxC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,CAAC,SAAS;AACZ,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,OAAO;AACf,UAAM,OAAO,MAAiB,aAAa,UAAU,EAAE;AACvD,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,cAAc,KAAK,WAAW;AAAA,IACjD,QAAQ;AACN,iBAAW;AAAA,QACT,QAAQ;AAAA,QACR;AAAA,QACA,aAAa;AAAA,QACb,OAAO;AAAA,QACP,YAAY,CAAC;AAAA,QACb,OAAO,CAAC;AAAA,QACR,OAAO,CAAC;AAAA,MACV;AAAA,IACF;AACA,aAAS,cAAc;AAEvB,YAAQ,OAAO;AACf,UAAM,SAAS,MAAM,eAAe,QAAQ;AAE5C,YAAQ,OAAO;AACf,UAAiB,eAAe,UAAU,IAAI,MAAM;AAEpD,YAAQ,QAAQ,yCAAoC,OAAO,EAAE;AAC7D,YAAQ,IAAI,MAAM,IAAI,cAAc,MAAM,EAAE,CAAC;AAC7C,YAAQ;AAAA,MACN,MAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,KAAK,2BAA2B;AACxC,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,iBAAiB,MAAc,YAAoC;AAChF,QAAM,UAAU,IAAI,+BAA+B,EAAE,MAAM;AAC3D,MAAI;AACF,UAAM,YAAY,MAAM,iBAAiB,IAAI;AAC7C,UAAM,gBAAgB,WAAW,EAAE,QAAQ,YAAY;AACvD,QAAI,UAAU,QAAQ,YAAY,MAAM,eAAe;AACrD,cAAQ,KAAK,kDAAkD;AAC/D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,OAAO,MAAM,SAAS;AAC5B,QAAI,UAAU,cAAc,iBAAiB,IAAI;AACjD,QAAI,CAAC,SAAS;AACZ,UAAI;AACF,kBAAU,MAAM,KAAK,SAAS,IAAI,IAAI;AAAA,MACxC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,QAAI,CAAC,SAAS;AACZ,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,cAAc,MAAM,eAAe,SAAS,UAAU,KAAK;AACjE,YAAQ,QAAQ,gCAA2B,OAAO,SAAS;AAC3D,YAAQ,IAAI,MAAM,IAAI,uDAAuD,CAAC;AAAA,EAChF,SAAS,KAAK;AACZ,YAAQ,KAAK,8BAA8B;AAC3C,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,aAAa,MAAc,IAA4B;AACpE,QAAM,OAAO,MAAM,SAAS;AAQ5B,QAAM,mBAAmB,KAAK,wBAAwB;AACtD,QAAM,eACJ,qBAAqB,KAAK;AAE5B,QAAM,UAAU,IAAI,GAAG,KAAK,aAAa,WAAW,iBAAiB,EAAE,MAAM;AAC7E,MAAI;AACF,UAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI;AAE1C,QAAI,IAAI;AACN,YAAM,KAAK,UAAU,OAAO,gBAAgB;AAC5C,cAAQ;AAAA,QACN,yDAAoD,gBAAgB,GAAG,eAAe,gBAAgB,sBAAsB;AAAA,MAC9H;AACA,YAAM,oBAAoB;AAAA,IAC5B,OAAO;AACL,YAAM,KAAK,aAAa,OAAO,gBAAgB;AAC/C,cAAQ;AAAA,QACN,4DAAuD,gBAAgB;AAAA,MACzE;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,KAAK,8BAA8B;AAC3C,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,WAAW,MAAc,OAAgB,UAAkC;AACxF,QAAM,UAAU,IAAI,4BAA4B,EAAE,MAAM;AACxD,MAAI;AACF,UAAM,YAAY,MAAM,iBAAiB,IAAI;AAC7C,UAAM,gBAAgB,WAAW,EAAE,QAAQ,YAAY;AACvD,QAAI,UAAU,QAAQ,YAAY,MAAM,eAAe;AACrD,cAAQ,KAAK,0DAA0D;AACvE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI,CAAC,OAAO;AACV,YAAM,aAAa,iBAAiB,IAAI,KAAK,MAAM,cAAc,MAAM,aAAa;AACpF,UAAI,YAAY;AACd,gBAAQ,QAAQ,8CAA8C;AAC9D,gBAAQ,IAAI,MAAM,IAAI,eAAe,UAAU,EAAE,CAAC;AAClD;AAAA,MACF;AAAA,IACF;AAGA,YAAQ,OAAO;AACf,UAAM,OAAO,MAAM,SAAS;AAC5B,UAAM,SAAS,MAAM,KAAK,cAAc;AACxC,UAAM,UAAU,MAAM,KAAK,qBAAqB,QAAQ,MAAM,QAAQ;AAEtE,iBAAa,MAAM,OAAO;AAa1B,YAAQ,OAAO;AACf,QAAI;AACF,YAAM,OAAO,MAAiB,aAAa,UAAU,EAAE;AACvD,UAAI;AACJ,UAAI;AACF,mBAAW,MAAM,cAAc,KAAK,WAAW;AAAA,MACjD,QAAQ;AACN,mBAAW;AAAA,UACT,QAAQ;AAAA,UACR;AAAA,UACA,aAAa;AAAA,UACb,OAAO;AAAA,UACP,YAAY,CAAC;AAAA,UACb,OAAO,CAAC;AAAA,UACR,OAAO,CAAC;AAAA,QACV;AAAA,MACF;AACA,eAAS,cAAc;AACvB,YAAM,SAAS,MAAM,eAAe,QAAQ;AAC5C,YAAiB,eAAe,UAAU,IAAI,MAAM;AACpD,cAAQ,IAAI,MAAM,IAAI;AAAA,gCAAmC,MAAM,EAAE,CAAC;AAAA,IACpE,SAAS,SAAS;AAChB,cAAQ,KAAK,MAAM,OAAO,wDAAmD,CAAC;AAC9E,cAAQ,KAAK,MAAM,IAAI,OAAO,mBAAmB,QAAQ,QAAQ,UAAU,OAAO,OAAO,CAAC,EAAE,CAAC;AAC7F,cAAQ,KAAK,MAAM,IAAI,sFAAsF,CAAC;AAAA,IAChH;AAMA,UAAM,SAAS,IAAI,EAAE,gBAAgB;AACrC,QAAI,QAAQ;AACV,UAAI;AACF,gBAAQ,OAAO;AACf,cAAM,cAAc,MAAM,eAAe,SAAS,UAAU,KAAK;AACjE,gBAAQ,IAAI,MAAM,IAAI,wCAAwC,CAAC;AAAA,MACjE,QAAQ;AACN,gBAAQ,IAAI,MAAM,IAAI,sDAAsD,CAAC;AAAA,MAC/E;AAAA,IACF;AAEA,YAAQ,QAAQ,0BAA0B,IAAI,oBAAoB;AAClE,YAAQ,IAAI,MAAM,IAAI,eAAe,OAAO,EAAE,CAAC;AAC/C,YAAQ,IAAI,MAAM,IAAI,6BAA6B,IAAI,EAAE,CAAC;AAG1D,QAAI,UAAU;AACZ,YAAM,oBAAoB;AAAA,IAC5B;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,KAAK,iCAAiC;AAC9C,YAAQ,MAAM,MAAM,IAAI,oBAAoB,GAAG,CAAC,CAAC;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAIO,SAAS,qBAAqB,SAAwB;AAC3D,UACG,QAAQ,sCAAsC,EAC9C,YAAY,6EAAwE,EACpF,OAAO,cAAc,oCAAoC,KAAK,EAC9D,OAAO,WAAW,yGAAyG,KAAK,EAChI,OAAO,eAAe,wCAAwC,IAAI,EAClE,OAAO,WAAW,gDAAgD,KAAK,EACvE,OAAO,YAAY,iEAA4D,KAAK,EACpF,OAAO,wBAAwB,8BAA8B,EAC7D,OAAO,mBAAmB,0CAA0C,EACpE,OAAO,OAAO,MAAc,QAA4B,YAAsB,SAAwI;AACrN,YAAQ,QAAQ;AAAA,MACd,KAAK,QAAQ;AACX,YAAI,UAAU,WAAW,CAAC;AAC1B,YAAI,KAAK,OAAO;AAGd,cAAI,QAAQ,MAAM,OAAO;AACvB,oBAAQ,MAAM,MAAM,OAAO,4KAA4K,CAAC;AAAA,UAC1M;AAGA,oBAAU,MAAM,UAAU;AAC1B,cAAI,CAAC,QAAQ,KAAK,GAAG;AACnB,oBAAQ,MAAM,MAAM,IAAI,mCAAmC,CAAC;AAC5D,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF;AACA,YAAI,CAAC,SAAS;AACZ,kBAAQ,MAAM,MAAM,IAAI,iHAAmH,CAAC;AAC5I,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,WAAW,MAAM,SAAS,KAAK,QAAQ;AAC7C;AAAA,MACF;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,CAAC,WAAW,KAAK,IAAI;AAC3B,YAAI,CAAC,aAAa,CAAC,OAAO;AACxB,kBAAQ,MAAM,MAAM,IAAI,uDAAuD,CAAC;AAChF,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,YAAY,MAAM,WAAW,KAAK;AACxC;AAAA,MACF;AAAA,MAEA,KAAK;AACH,cAAM,UAAU,MAAM,SAAS,KAAK,OAAO,EAAE,CAAC;AAC9C;AAAA,MAEF,KAAK;AACH,cAAM,cAAc,IAAI;AACxB;AAAA,MAEF,KAAK,OAAO;AACV,cAAM,UAAU,WAAW,CAAC;AAC5B,YAAI,CAAC,SAAS;AACZ,kBAAQ,MAAM,MAAM,IAAI,2CAA2C,CAAC;AACpE,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,UAAU,MAAM,OAAO;AAC7B;AAAA,MACF;AAAA,MAEA,KAAK,UAAU;AACb,cAAM,aAAa,WAAW,CAAC;AAC/B,YAAI,CAAC,YAAY;AACf,kBAAQ,MAAM,MAAM,IAAI,8CAA8C,CAAC;AACvE,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,aAAa,MAAM,UAAU;AACnC;AAAA,MACF;AAAA,MAEA,KAAK;AACH,cAAM,WAAW,MAAM,KAAK,OAAO,KAAK,MAAM;AAC9C;AAAA,MAEF,KAAK,UAAU;AACb,cAAM,UAAU,WAAW,CAAC;AAC5B,cAAM,aAAa,MAAM,SAAS,KAAK,WAAW;AAClD;AAAA,MACF;AAAA,MAEA,KAAK;AACH,cAAM,iBAAiB,MAAM,KAAK,OAAO;AACzC;AAAA,MAEF,KAAK;AACH,cAAM,sBAAsB,MAAM,KAAK,OAAO;AAC9C;AAAA,MAEF,KAAK,UAAU;AACb,cAAM,OAAO,WAAW,CAAC;AACzB,YAAI,SAAS,QAAQ,SAAS,OAAO;AACnC,kBAAQ,MAAM,MAAM,IAAI,2CAA2C,CAAC;AACpE,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,aAAa,MAAM,SAAS,IAAI;AACtC;AAAA,MACF;AAAA,MAEA,KAAK;AAAA,MACL;AACE,cAAM,aAAa,IAAI;AACvB;AAAA,IACJ;AAAA,EACF,CAAC;AACL;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -3813,7 +3813,7 @@ function printSimulationResult(result, callType) {
|
|
|
3813
3813
|
}
|
|
3814
3814
|
async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
|
|
3815
3815
|
try {
|
|
3816
|
-
const xmtp = await import("./xmtp-
|
|
3816
|
+
const xmtp = await import("./xmtp-MR6OJ76L.js");
|
|
3817
3817
|
const { getAccount: getAccount2 } = await import("./client-KBBFAMEI.js");
|
|
3818
3818
|
const group = await xmtp.getGroup("", subdomain);
|
|
3819
3819
|
const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
|
|
@@ -7283,7 +7283,7 @@ try {
|
|
|
7283
7283
|
var require2 = createRequire(import.meta.url);
|
|
7284
7284
|
var { version: CLI_VERSION } = require2("../package.json");
|
|
7285
7285
|
async function loadXmtp() {
|
|
7286
|
-
return import("./xmtp-
|
|
7286
|
+
return import("./xmtp-MR6OJ76L.js");
|
|
7287
7287
|
}
|
|
7288
7288
|
async function loadCron() {
|
|
7289
7289
|
return import("./cron-EOULYO3R.js");
|
|
@@ -8433,7 +8433,7 @@ ${info.name} (${info.type})`);
|
|
|
8433
8433
|
}
|
|
8434
8434
|
});
|
|
8435
8435
|
try {
|
|
8436
|
-
const { registerChatCommands } = await import("./chat-
|
|
8436
|
+
const { registerChatCommands } = await import("./chat-SL4O7Y3C.js");
|
|
8437
8437
|
registerChatCommands(program);
|
|
8438
8438
|
} catch {
|
|
8439
8439
|
program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
|
|
@@ -8443,7 +8443,7 @@ try {
|
|
|
8443
8443
|
process.exit(1);
|
|
8444
8444
|
});
|
|
8445
8445
|
}
|
|
8446
|
-
var { registerSessionCommands } = await import("./session-
|
|
8446
|
+
var { registerSessionCommands } = await import("./session-FPQGAMSS.js");
|
|
8447
8447
|
registerSessionCommands(program);
|
|
8448
8448
|
registerVeniceCommands(program);
|
|
8449
8449
|
registerAllowanceCommands(program);
|
|
@@ -8451,11 +8451,11 @@ registerIdentityCommands(program);
|
|
|
8451
8451
|
registerProposalCommands(program);
|
|
8452
8452
|
registerGovernorCommands(program);
|
|
8453
8453
|
registerGuardianCommands(program);
|
|
8454
|
-
var { registerResearchCommands } = await import("./research-
|
|
8454
|
+
var { registerResearchCommands } = await import("./research-RL2ASMQ5.js");
|
|
8455
8455
|
registerResearchCommands(program);
|
|
8456
8456
|
var { registerAgentCommands } = await import("./agent-IVESTCYM.js");
|
|
8457
8457
|
registerAgentCommands(program);
|
|
8458
|
-
var { registerTradeCommands } = await import("./trade-
|
|
8458
|
+
var { registerTradeCommands } = await import("./trade-AJNB4ULF.js");
|
|
8459
8459
|
registerTradeCommands(program);
|
|
8460
8460
|
registerGridCommand(program);
|
|
8461
8461
|
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-MR6OJ76L.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-RL2ASMQ5.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-MR6OJ76L.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-FPQGAMSS.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-MR6OJ76L.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-AJNB4ULF.js.map
|
|
@@ -29,6 +29,10 @@ import {
|
|
|
29
29
|
} from "@xmtp/node-sdk";
|
|
30
30
|
import { keccak256, toBytes } from "viem";
|
|
31
31
|
var _client = null;
|
|
32
|
+
var DEFAULT_DASHBOARD_SPECTATOR_ADDRESS = "0x9f6518e69a62c526ead155ad2661f5957b6b2fc3";
|
|
33
|
+
function resolveSpectatorAddress() {
|
|
34
|
+
return process.env.DASHBOARD_SPECTATOR_ADDRESS || DEFAULT_DASHBOARD_SPECTATOR_ADDRESS;
|
|
35
|
+
}
|
|
32
36
|
function getXmtpEnv() {
|
|
33
37
|
return getChainConfig().xmtpEnv;
|
|
34
38
|
}
|
|
@@ -113,9 +117,8 @@ async function createSyndicateGroup(_client2, subdomain, isPublic = false) {
|
|
|
113
117
|
await group.updateDescription(
|
|
114
118
|
`Sherwood syndicate: ${subdomain}.sherwoodagent.eth`
|
|
115
119
|
);
|
|
116
|
-
const SPECTATOR_ADDRESS = process.env.DASHBOARD_SPECTATOR_ADDRESS || "0x9f6518e69a62c526ead155ad2661f5957b6b2fc3";
|
|
117
120
|
if (isPublic) {
|
|
118
|
-
await addMember(groupId,
|
|
121
|
+
await addMember(groupId, resolveSpectatorAddress());
|
|
119
122
|
}
|
|
120
123
|
cacheGroupId(subdomain, groupId);
|
|
121
124
|
return groupId;
|
|
@@ -300,6 +303,7 @@ async function getMembers(groupId) {
|
|
|
300
303
|
}));
|
|
301
304
|
}
|
|
302
305
|
export {
|
|
306
|
+
DEFAULT_DASHBOARD_SPECTATOR_ADDRESS,
|
|
303
307
|
addMember,
|
|
304
308
|
createSyndicateGroup,
|
|
305
309
|
getGroup,
|
|
@@ -308,9 +312,10 @@ export {
|
|
|
308
312
|
getXmtpClient,
|
|
309
313
|
removeMember,
|
|
310
314
|
renameGroup,
|
|
315
|
+
resolveSpectatorAddress,
|
|
311
316
|
sendEnvelope,
|
|
312
317
|
sendMarkdown,
|
|
313
318
|
sendReaction,
|
|
314
319
|
streamMessages
|
|
315
320
|
};
|
|
316
|
-
//# sourceMappingURL=xmtp-
|
|
321
|
+
//# sourceMappingURL=xmtp-MR6OJ76L.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\n/**\n * Canonical Sherwood dashboard spectator address (XMTP production network,\n * inbox prefix `744cfb`). Used to enable the dashboard live feed by adding\n * the spectator service as a member of a syndicate group. The actual\n * service runs at spectator.sherwood.sh and pushes messages over websocket\n * to the dashboard's LiveFeed component, which filters by this exact inbox.\n *\n * Overridable via `DASHBOARD_SPECTATOR_ADDRESS` for testing, but the env\n * var should generally not be set in production — the literal IS the\n * production spectator. Verify against\n * curl -s https://spectator.sherwood.sh/health\n * which returns `.address` matching this value.\n */\nexport const DEFAULT_DASHBOARD_SPECTATOR_ADDRESS =\n \"0x9f6518e69a62c526ead155ad2661f5957b6b2fc3\";\n\n/**\n * Resolve the dashboard spectator address for `chat init --public` /\n * `chat <name> public on`. Falls back to the canonical production\n * spectator when `DASHBOARD_SPECTATOR_ADDRESS` is unset — both call\n * sites MUST share this default, otherwise a cron with a typo for the\n * env var silently adds nothing (or worse, the agent's own wallet) and\n * the dashboard never goes LIVE.\n */\nexport function resolveSpectatorAddress(): string {\n return (\n process.env.DASHBOARD_SPECTATOR_ADDRESS ||\n DEFAULT_DASHBOARD_SPECTATOR_ADDRESS\n );\n}\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 if (isPublic) {\n await addMember(groupId, resolveSpectatorAddress());\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 * 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. Strategy:\n // 1. ALWAYS write to IPFS metadata — canonical source on every chain.\n // The dashboard's resolver chain is `ENS text → metadata → null`, so\n // metadata being populated guarantees the dashboard works regardless\n // of ENS state. Necessary because the V1.5 vault has no `executeBatch`\n // (V-C3 removal), so the ENS `setText` call below currently reverts on\n // Base mainnet — without metadata, the dashboard would show OFFLINE.\n // 2. ADDITIONALLY try the ENS text record (best-effort). Fast resolution\n // path for chains where the vault has a writable executeBatch path\n // (HyperEVM today, or a future vault setEnsText helper). On failure,\n // we log a debug line and keep going — metadata fallback covers it.\n spinner.text = \"Saving group ID to syndicate metadata...\";\n try {\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 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 to IPFS metadata\"));\n console.warn(chalk.dim(` ${metaErr instanceof Error ? metaErr.message : String(metaErr)}`));\n console.warn(chalk.dim(\" Dashboard may show OFFLINE until backfilled with `chat <name> publish-metadata`.\"));\n }\n\n // Best-effort ENS text record write. Currently reverts on Base mainnet\n // because the V1.5 vault has no owner-direct executeBatch path; metadata\n // above is the operative resolution source. Will succeed once a vault\n // setEnsText helper lands or on chains where the existing path works.\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 console.log(chalk.dim(\" ENS text record written: xmtpGroupId\"));\n } catch {\n console.log(chalk.dim(\" ENS text record skipped (metadata fallback active)\"));\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":[]}
|
|
@@ -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 // 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":";;;;;;;;;;;;;;;;;;;;AAWA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OAOK;AACP,SAAS,WAAW,eAAe;AA+BnC,IAAI,UAAyB;AAE7B,SAAS,aAAqB;AAC5B,SAAO,eAAe,EAAE;AAC1B;AAEA,SAAS,mBAAmB,YAAgC;AAC1D,QAAM,OAAO,UAAU,QAAQ,aAAa,aAAa,CAAC;AAC1D,SAAO,QAAQ,IAAI;AACrB;AAEA,SAAS,YAAoB;AAC3B,QAAM,MAAM,KAAK,KAAK,QAAQ,GAAG,aAAa,MAAM;AACpD,KAAG,UAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAClD,SAAO,KAAK,KAAK,KAAK,UAAU;AAClC;AAEA,SAAS,cAAc,SAA6B;AAClD,SAAO;AAAA,IACL,YAAY,QAAQ,YAAY;AAAA,IAChC,gBAAgB,eAAe;AAAA,EACjC;AACF;AAEA,eAAe,oBAAqC;AAClD,MAAI,QAAS,QAAO;AAEpB,QAAM,SAAS,WAAW;AAC1B,MAAI,CAAC,OAAO,YAAY;AACtB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,WAAW;AAE3B,QAAM,SAAiB;AAAA,IACrB,MAAM;AAAA,IACN,eAAe,OAAO;AAAA,MACpB,YAAY,QAAQ,QAAQ,YAAY;AAAA,MACxC,gBAAgB,eAAe;AAAA,IACjC;AAAA,IACA,aAAa,OAAO,YAAoB;AACtC,YAAM,YAAY,MAAM,QAAQ,YAAY,EAAE,QAAQ,CAAC;AACvD,aAAO,QAAQ,SAAS;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,gBAAgC;AAAA,IACpC,KAAK,WAAW;AAAA,EAClB;AACA,YAAU,MAAM,OAAO,OAAO,QAAQ;AAAA,IACpC,GAAG;AAAA,IACH,iBAAiB,mBAAmB,OAAO,UAAU;AAAA,IACrD,QAAQ,UAAU;AAAA,EACpB,CAAC;AAKD,QAAM,QAAQ,cAAc,QAAQ;AAAA,IAClC,aAAa;AAAA,IACb,aAAa;AAAA,EACf,CAAC;AAGD,MAAI,CAAC,OAAO,eAAe;AACzB,QAAI;AACF,YAAM,QAAQ,4BAA4B;AAAA,IAC5C,QAAQ;AAAA,IAER;AACA,WAAO,gBAAgB;AACvB,eAAW,MAAM;AAAA,EACnB;AAEA,SAAO;AACT;AAIA,eAAsB,gBAAiC;AACrD,QAAM,SAAS,MAAM,kBAAkB;AAGvC,QAAM,SAAS,WAAW;AAC1B,MAAI,CAAC,OAAO,eAAe,OAAO,SAAS;AACzC,WAAO,cAAc,OAAO;AAC5B,eAAW,MAAM;AAAA,EACnB;AAEA,SAAO,OAAO;AAChB;AAIA,eAAsB,qBACpBA,UACA,WACA,WAAoB,OACH;AACjB,QAAM,SAAS,MAAM,kBAAkB;AAEvC,QAAM,QAAQ,MAAM,OAAO,cAAc,YAAY,CAAC,GAAG;AAAA,IACvD,MAAM;AAAA,IACN,aAAa,uBAAuB,SAAS;AAAA,IAC7C,iBAAiB,wBAAwB;AAAA,EAC3C,CAAuB;AAEvB,QAAM,UAAU,MAAM;AACtB,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,yDAAoD;AAAA,EACtE;AAMA,QAAM,MAAM,WAAW,SAAS;AAChC,QAAM,MAAM;AAAA,IACV,uBAAuB,SAAS;AAAA,EAClC;AAGA,QAAM,oBACJ,QAAQ,IAAI,+BACZ;AACF,MAAI,UAAU;AACZ,UAAM,UAAU,SAAS,iBAAiB;AAAA,EAC5C;AAGA,eAAa,WAAW,OAAO;AAE/B,SAAO;AACT;AAIA,eAAsB,SACpBA,UACA,WACiB;AACjB,QAAM,SAAS,MAAM,kBAAkB;AAIvC,QAAM,OAAO,cAAc,QAAQ;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,EACf,CAAC;AAKD,MAAI,UAA8B,iBAAiB,SAAS;AAC5D,MAAI,SAAS;AACX,UAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,OAAO;AACnE,QAAI,MAAM;AACR,aAAO;AAAA,IACT;AAEA,4BAAwB,SAAS;AACjC,cAAU;AAAA,EACZ;AAGA,MAAI,CAAC,SAAS;AACZ,QAAI;AACF,YAAM,QAAQ,MAAM,cAAc,WAAW,aAAa;AAC1D,UAAI,OAAO;AACT,cAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,KAAK;AACjE,YAAI,MAAM;AACR,oBAAU;AACV,uBAAa,WAAW,OAAO;AAAA,QACjC;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,MAAI,CAAC,SAAS;AACZ,UAAM,SAAS,OAAO,cAAc,WAAW;AAC/C,UAAM,QAAQ,OAAO;AAAA,MACnB,CAAC,MACC,EAAE,SAAS,aACX,EAAE,aAAa,SAAS,GAAG,SAAS,oBAAoB;AAAA,IAC5D;AACA,QAAI,OAAO;AACT,gBAAU,MAAM;AAChB,mBAAa,WAAW,OAAO;AAAA,IACjC;AAAA,EACF;AAEA,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,sCAAsC,SAAS,yBAAyB,SAAS;AAAA,IACnF;AAAA,EACF;AAEA,SAAO;AACT;AAIA,eAAsB,UACpB,SACA,SACe;AACf,QAAM,SAAS,MAAM,kBAAkB;AAGvC,QAAM,YAAY,MAAM,OAAO,WAAW,CAAC,cAAc,OAAO,CAAC,CAAC;AAElE,QAAM,cACJ,UAAU,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,YAAY,CAAC,KAAK;AACpE,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI;AAAA,MACR,GAAG,OAAO;AAAA,IACZ;AAAA,EACF;AAEA,QAAM,OAAO,cAAc,QAAQ;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,EACf,CAAC;AACD,QAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,OAAO;AACnE,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB,OAAO,YAAY;AAG9D,QAAM,QAAQ;AACd,QAAM,MAAM,wBAAwB,CAAC,cAAc,OAAO,CAAC,CAAC;AAG5D,MAAI;AACF,UAAM,mBAAmB,aAAa,OAAO;AAAA,EAC/C,QAAQ;AAAA,EAER;AACF;AAEA,eAAsB,aACpB,SACA,SACe;AACf,QAAM,SAAS,MAAM,kBAAkB;AACvC,QAAM,OAAO,cAAc,QAAQ;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,EACf,CAAC;AAED,QAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,OAAO;AACnE,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB,OAAO,YAAY;AAG9D,QAAM,QAAQ;AACd,QAAM,MAAM,2BAA2B,CAAC,cAAc,OAAO,CAAC,CAAC;AACjE;AAOA,eAAsB,YACpB,SACA,MACA,aACe;AACf,MAAI,SAAS,UAAa,gBAAgB,QAAW;AACnD,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AACA,QAAM,SAAS,MAAM,kBAAkB;AACvC,QAAM,OAAO,cAAc,QAAQ;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,EACf,CAAC;AAED,QAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,OAAO;AACnE,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB,OAAO,YAAY;AAC9D,QAAM,QAAQ;AAEd,MAAI,SAAS,OAAW,OAAM,MAAM,WAAW,IAAI;AACnD,MAAI,gBAAgB,OAAW,OAAM,MAAM,kBAAkB,WAAW;AAC1E;AAIA,eAAsB,aACpB,SACA,UACe;AACf,QAAM,SAAS,MAAM,kBAAkB;AACvC,QAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,OAAO;AACnE,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB,OAAO,YAAY;AAE9D,QAAM,OAAO,KAAK,UAAU,QAAQ;AACpC,QAAM,KAAK,SAAS,IAAI;AAC1B;AAEA,eAAsB,aACpB,SACA,UACe;AACf,QAAM,WAAyB;AAAA,IAC7B,MAAM;AAAA,IACN,MAAM,WAAW,EAAE;AAAA,IACnB,MAAM;AAAA,IACN,MAAM,EAAE,QAAQ,WAAW;AAAA,IAC3B,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,EACzC;AACA,QAAM,aAAa,SAAS,QAAQ;AACtC;AAEA,eAAsB,aACpB,SACA,WACA,OACe;AACf,QAAM,WAAyB;AAAA,IAC7B,MAAM;AAAA,IACN,MAAM,WAAW,EAAE;AAAA,IACnB,MAAM,EAAE,WAAW,WAAW,MAAM;AAAA,IACpC,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,EACzC;AACA,QAAM,aAAa,SAAS,QAAQ;AACtC;AAIA,eAAsB,eACpB,SACA,WACqB;AACrB,QAAM,SAAS,MAAM,kBAAkB;AAEvC,QAAM,SAAS,MAAM,OAAO,cAAc,kBAAkB;AAAA,IAC1D,eAAe,CAAC,aAAa,OAAO;AAAA,EACtC,CAAC;AAED,MAAI,UAAU;AAGd,GAAC,YAAY;AACX,QAAI;AACF,uBAAiB,WAAW,QAAQ;AAClC,YAAI,QAAS;AAEb,YAAI,QAAQ,mBAAmB,QAAS;AAExC,kBAAU;AAAA,UACR,IAAI,QAAQ,MAAM;AAAA,UAClB,gBAAgB,QAAQ,kBAAkB;AAAA,UAC1C,eAAe,QAAQ,iBAAiB;AAAA,UACxC,aACE,OAAO,QAAQ,gBAAgB,WAC1B,QAAQ,aAAqC,UAAU,SACxD,OAAO,QAAQ,eAAe,MAAM;AAAA,UAC1C,SACE,OAAO,QAAQ,YAAY,WACvB,QAAQ,UACR,KAAK,UAAU,QAAQ,OAAO;AAAA,UACpC,QAAQ,QAAQ,UAAU,oBAAI,KAAK;AAAA,QACrC,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF,GAAG;AAGH,SAAO,MAAM;AACX,cAAU;AACV,QAAI;AAEF,UAAI,OAAQ,OAAe,QAAQ,YAAY;AAC7C,QAAC,OAAe,IAAI;AAAA,MACtB,WAAW,OAAQ,OAAe,WAAW,YAAY;AACvD,QAAC,OAAe,OAAO;AAAA,MACzB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAIA,eAAsB,kBACpB,SACA,QAAgB,IACQ;AACxB,QAAM,SAAS,MAAM,kBAAkB;AACvC,QAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,OAAO;AACnE,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB,OAAO,YAAY;AAG9D,QAAM,KAAK,KAAK;AAEhB,QAAM,cAAc,MAAM,KAAK,SAAS,EAAE,MAAM,CAAC;AAEjD,SAAO,YAAY,IAAI,CAAC,OAAO;AAAA,IAC7B,IAAI,EAAE,MAAM;AAAA,IACZ,gBAAgB,EAAE,kBAAkB;AAAA,IACpC,eAAe,EAAE,iBAAiB;AAAA,IAClC,aACE,OAAO,EAAE,gBAAgB,WACpB,EAAE,aAAqC,UAAU,SAClD,OAAO,EAAE,eAAe,MAAM;AAAA,IACpC,SACE,OAAO,EAAE,YAAY,WACjB,EAAE,UACF,KAAK,UAAU,EAAE,OAAO;AAAA,IAC9B,QAAQ,EAAE,UAAU,oBAAI,KAAK;AAAA,EAC/B,EAAE;AACJ;AAIA,eAAsB,WACpB,SACuB;AACvB,QAAM,SAAS,MAAM,kBAAkB;AACvC,QAAM,OAAO,MAAM,OAAO,cAAc,oBAAoB,OAAO;AACnE,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB,OAAO,YAAY;AAE9D,QAAM,UAAU,MAAM,KAAK,QAAQ;AAEnC,UAAQ,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,GAAG,IAAI,CAAC,OAAY;AAAA,IAC9D,SAAS,OAAO,EAAE,WAAW,EAAE;AAAA,IAC/B,iBACE,EAAE,oBAAoB,IAClB,gBACA,EAAE,oBAAoB,IACpB,UACA;AAAA,EACV,EAAE;AACJ;","names":["_client"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|