@net-protocol/cli 0.1.43 → 0.1.44
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/index.mjs +24 -3
- package/dist/chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +463 -124
- package/dist/cli/index.mjs.map +1 -1
- package/dist/feed/index.mjs +407 -93
- package/dist/feed/index.mjs.map +1 -1
- package/dist/profile/index.mjs +17 -1
- package/dist/profile/index.mjs.map +1 -1
- package/dist/upvote/index.mjs +24 -1
- package/dist/upvote/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/chat/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk3 from 'chalk';
|
|
2
2
|
import '@net-protocol/feeds';
|
|
3
3
|
import { ChatClient } from '@net-protocol/chats';
|
|
4
|
-
import { getBaseDataSuffix, getChainRpcUrls } from '@net-protocol/core';
|
|
4
|
+
import { getBaseDataSuffix, getChainRpcUrls, getChainSlug } from '@net-protocol/core';
|
|
5
5
|
import '@net-protocol/storage';
|
|
6
6
|
import { encodeFunctionData, concat, createWalletClient, http } from 'viem';
|
|
7
7
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
@@ -69,6 +69,22 @@ function exitWithError(message) {
|
|
|
69
69
|
console.error(chalk3.red(`Error: ${message}`));
|
|
70
70
|
process.exit(1);
|
|
71
71
|
}
|
|
72
|
+
var WEBSITE_BASE = "https://netprotocol.app";
|
|
73
|
+
function chainSlug(chainId) {
|
|
74
|
+
return getChainSlug({ chainId }) ?? null;
|
|
75
|
+
}
|
|
76
|
+
function profileUrl(chainId, address) {
|
|
77
|
+
const slug = chainSlug(chainId);
|
|
78
|
+
if (!slug) return null;
|
|
79
|
+
return `${WEBSITE_BASE}/app/profile/${slug}/${address.toLowerCase()}`;
|
|
80
|
+
}
|
|
81
|
+
function chatUrl(chainId, chatName) {
|
|
82
|
+
const slug = chainSlug(chainId);
|
|
83
|
+
if (!slug) return null;
|
|
84
|
+
return `${WEBSITE_BASE}/app/chat/${slug}/${encodeURIComponent(
|
|
85
|
+
chatName.toLowerCase()
|
|
86
|
+
)}`;
|
|
87
|
+
}
|
|
72
88
|
|
|
73
89
|
// src/commands/chat/types.ts
|
|
74
90
|
function normalizeChatName(chat) {
|
|
@@ -82,12 +98,15 @@ function formatMessage(msg, index) {
|
|
|
82
98
|
return ` ${chalk3.gray(`[${index + 1}]`)} ${chalk3.cyan(sender)} ${chalk3.gray(time)}
|
|
83
99
|
${msg.text}`;
|
|
84
100
|
}
|
|
85
|
-
function messageToJson(msg, index) {
|
|
101
|
+
function messageToJson(msg, index, chainId, chatName) {
|
|
86
102
|
return {
|
|
87
103
|
index,
|
|
88
104
|
sender: msg.sender,
|
|
105
|
+
senderProfileUrl: profileUrl(chainId, msg.sender),
|
|
89
106
|
text: msg.text,
|
|
90
107
|
timestamp: Number(msg.timestamp),
|
|
108
|
+
chat: chatName,
|
|
109
|
+
chatUrl: chatUrl(chainId, chatName),
|
|
91
110
|
data: msg.data
|
|
92
111
|
};
|
|
93
112
|
}
|
|
@@ -126,7 +145,9 @@ async function executeChatRead(chat, options) {
|
|
|
126
145
|
}
|
|
127
146
|
if (options.json) {
|
|
128
147
|
printJson(
|
|
129
|
-
messages.map(
|
|
148
|
+
messages.map(
|
|
149
|
+
(msg, i) => messageToJson(msg, i, readOnlyOptions.chainId, normalizedChat)
|
|
150
|
+
)
|
|
130
151
|
);
|
|
131
152
|
} else {
|
|
132
153
|
if (messages.length === 0) {
|
package/dist/chat/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/shared.ts","../../src/shared/client.ts","../../src/shared/output.ts","../../src/commands/chat/types.ts","../../src/commands/chat/read.ts","../../src/shared/wallet.ts","../../src/shared/encode.ts","../../src/commands/chat/send.ts","../../src/commands/chat/index.ts"],"names":["chalk","senderNote","getBaseDataSuffix","client","txConfig"],"mappings":"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","file":"index.mjs","sourcesContent":["import chalk from \"chalk\";\nimport type { CommonOptions, ReadOnlyOptions } from \"../shared/types\";\n\n/**\n * Default chain ID (Base mainnet) - used by feed commands\n */\nexport const DEFAULT_CHAIN_ID = 8453;\n\n/**\n * Get chain ID from option or environment variable, exit if not found\n */\nfunction getRequiredChainId(optionValue?: number): number {\n const chainId =\n optionValue ||\n (process.env.NET_CHAIN_ID\n ? parseInt(process.env.NET_CHAIN_ID, 10)\n : undefined);\n\n if (!chainId) {\n console.error(\n chalk.red(\n \"Error: Chain ID is required. Provide via --chain-id flag or NET_CHAIN_ID environment variable\"\n )\n );\n process.exit(1);\n }\n\n return chainId;\n}\n\n/**\n * Get chain ID from option or environment variable, defaulting to Base (8453)\n * Also checks BOTCHAN_* env vars for backward compat\n */\nfunction getChainIdWithDefault(optionValue?: number): number {\n if (optionValue) {\n return optionValue;\n }\n\n const envChainId =\n process.env.BOTCHAN_CHAIN_ID || process.env.NET_CHAIN_ID;\n\n if (envChainId) {\n return parseInt(envChainId, 10);\n }\n\n return DEFAULT_CHAIN_ID;\n}\n\n/**\n * Get RPC URL from option or environment variable\n */\nfunction getRpcUrl(optionValue?: string): string | undefined {\n return optionValue || process.env.NET_RPC_URL;\n}\n\n/**\n * Get RPC URL from option or environment variable, also checking BOTCHAN_* env vars.\n * Used only by feed commands for backward compat.\n */\nfunction getRpcUrlWithBotchanFallback(optionValue?: string): string | undefined {\n return optionValue || process.env.BOTCHAN_RPC_URL || process.env.NET_RPC_URL;\n}\n\n/**\n * Parse and validate common options shared across all commands.\n * Extracts private key, chain ID, and RPC URL from command options or environment variables.\n * @param options - Command options\n * @param supportsEncodeOnly - If true, mention --encode-only in error messages as an alternative\n */\nexport function parseCommonOptions(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse and validate read-only options for commands that don't need a private key.\n * Extracts chain ID and RPC URL from command options or environment variables.\n */\nexport function parseReadOnlyOptions(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse read-only options with a default chain ID (8453/Base).\n * Used by feed commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseReadOnlyOptionsWithDefault(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n\n/**\n * Parse common options with a default chain ID (8453/Base).\n * Used by feed write commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseCommonOptionsWithDefault(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.BOTCHAN_PRIVATE_KEY ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/BOTCHAN_PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n","import { FeedClient, FeedRegistryClient, AgentRegistryClient } from \"@net-protocol/feeds\";\nimport { ChatClient } from \"@net-protocol/chats\";\nimport { NetClient } from \"@net-protocol/core\";\nimport { StorageClient } from \"@net-protocol/storage\";\nimport type { ReadOnlyOptions } from \"./types\";\n\n/**\n * Create a FeedClient from read-only options\n */\nexport function createFeedClient(options: ReadOnlyOptions): FeedClient {\n return new FeedClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a FeedRegistryClient from read-only options\n */\nexport function createFeedRegistryClient(\n options: ReadOnlyOptions\n): FeedRegistryClient {\n return new FeedRegistryClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a ChatClient from read-only options\n */\nexport function createChatClient(options: ReadOnlyOptions): ChatClient {\n return new ChatClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a NetClient from read-only options\n */\nexport function createNetClient(options: ReadOnlyOptions): NetClient {\n return new NetClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create an AgentRegistryClient from read-only options\n */\nexport function createAgentRegistryClient(\n options: ReadOnlyOptions\n): AgentRegistryClient {\n return new AgentRegistryClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a StorageClient from read-only options\n */\nexport function createStorageClient(options: ReadOnlyOptions): StorageClient {\n return new StorageClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n","import chalk from \"chalk\";\nimport type { NetMessage } from \"@net-protocol/core\";\n\n/**\n * Format a message for human-readable output\n */\nexport function formatMessage(\n message: NetMessage,\n index: number\n): string {\n const timestamp = new Date(Number(message.timestamp) * 1000).toISOString();\n const lines = [\n chalk.cyan(`[${index}]`) + ` ${chalk.gray(timestamp)}`,\n ` ${chalk.white(\"Sender:\")} ${message.sender}`,\n ` ${chalk.white(\"App:\")} ${message.app}`,\n ];\n\n if (message.topic) {\n lines.push(` ${chalk.white(\"Topic:\")} ${message.topic}`);\n }\n\n lines.push(` ${chalk.white(\"Text:\")} ${message.text}`);\n\n if (message.data && message.data !== \"0x\") {\n lines.push(` ${chalk.white(\"Data:\")} ${message.data}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a message for JSON output\n */\nexport function messageToJson(\n message: NetMessage,\n index: number\n): Record<string, unknown> {\n return {\n index,\n sender: message.sender,\n app: message.app,\n timestamp: Number(message.timestamp),\n text: message.text,\n topic: message.topic,\n data: message.data,\n };\n}\n\n/**\n * Print messages in human-readable or JSON format\n */\nexport function printMessages(\n messages: NetMessage[],\n startIndex: number,\n json: boolean\n): void {\n if (json) {\n const output = messages.map((msg, i) => messageToJson(msg, startIndex + i));\n console.log(JSON.stringify(output, null, 2));\n } else {\n if (messages.length === 0) {\n console.log(chalk.yellow(\"No messages found\"));\n return;\n }\n\n messages.forEach((msg, i) => {\n console.log(formatMessage(msg, startIndex + i));\n if (i < messages.length - 1) {\n console.log(); // Empty line between messages\n }\n });\n }\n}\n\n/**\n * Print a count result\n */\nexport function printCount(\n count: number,\n label: string,\n json: boolean\n): void {\n if (json) {\n console.log(JSON.stringify({ count }, null, 2));\n } else {\n console.log(`${chalk.white(label)} ${chalk.cyan(count)}`);\n }\n}\n\n/**\n * Print an error message and exit\n */\nexport function exitWithError(message: string): never {\n console.error(chalk.red(`Error: ${message}`));\n process.exit(1);\n}\n","/**\n * Normalize a chat name to lowercase for consistency.\n */\nexport function normalizeChatName(chat: string): string {\n return chat.toLowerCase();\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport type { NetMessage } from \"@net-protocol/chats\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { createChatClient } from \"../../shared/client\";\nimport { exitWithError } from \"../../shared/output\";\nimport { normalizeChatName } from \"./types\";\n\ninterface ReadOptions {\n limit?: number;\n chainId?: number;\n rpcUrl?: string;\n json?: boolean;\n sender?: string;\n}\n\nfunction formatMessage(msg: NetMessage, index: number): string {\n const time = new Date(Number(msg.timestamp) * 1000).toLocaleString();\n const sender = msg.sender.slice(0, 6) + \"...\" + msg.sender.slice(-4);\n return ` ${chalk.gray(`[${index + 1}]`)} ${chalk.cyan(sender)} ${chalk.gray(time)}\\n ${msg.text}`;\n}\n\nfunction messageToJson(msg: NetMessage, index: number) {\n return {\n index,\n sender: msg.sender,\n text: msg.text,\n timestamp: Number(msg.timestamp),\n data: msg.data,\n };\n}\n\nfunction printJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/**\n * Execute the chat read command\n */\nasync function executeChatRead(chat: string, options: ReadOptions): Promise<void> {\n const normalizedChat = normalizeChatName(chat);\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = createChatClient(readOnlyOptions);\n const limit = options.limit ?? 20;\n\n try {\n const count = await client.getChatMessageCount(normalizedChat);\n\n if (count === 0) {\n if (options.json) {\n printJson([]);\n } else {\n console.log(chalk.yellow(`No messages found in chat \"${normalizedChat}\"`));\n }\n return;\n }\n\n const fetchLimit = options.sender ? Math.max(limit * 5, 100) : limit;\n\n let messages = await client.getChatMessages({\n topic: normalizedChat,\n maxMessages: fetchLimit,\n });\n\n // Filter by sender if specified\n if (options.sender) {\n const senderLower = options.sender.toLowerCase();\n messages = messages.filter(\n (msg: NetMessage) => msg.sender.toLowerCase() === senderLower\n );\n messages = messages.slice(0, limit);\n }\n\n if (options.json) {\n printJson(\n messages.map((msg: NetMessage, i: number) => messageToJson(msg, i))\n );\n } else {\n if (messages.length === 0) {\n const senderNote = options.sender ? ` by ${options.sender}` : \"\";\n console.log(chalk.yellow(`No messages found in chat \"${normalizedChat}\"${senderNote}`));\n return;\n }\n\n const senderNote = options.sender ? ` by ${options.sender}` : \"\";\n console.log(\n chalk.white(`Found ${messages.length} message(s) in chat \"${normalizedChat}\"${senderNote}:\\n`)\n );\n messages.forEach((msg: NetMessage, i: number) => {\n console.log(formatMessage(msg, i));\n if (i < messages.length - 1) {\n console.log(); // Empty line between messages\n }\n });\n }\n } catch (error) {\n exitWithError(\n `Failed to read chat: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Register the chat read subcommand\n */\nexport function registerChatReadCommand(parent: Command): void {\n parent\n .command(\"read <chat>\")\n .description(\"Read messages from a group chat (NOT 'read' — that's for feeds)\")\n .option(\n \"--limit <n>\",\n \"Maximum number of messages to display\",\n (value) => parseInt(value, 10)\n )\n .option(\n \"--chain-id <id>\",\n \"Chain ID (default: 8453 for Base)\",\n (value) => parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--sender <address>\", \"Filter messages by sender address\")\n .option(\"--json\", \"Output in JSON format\")\n .action(async (chat, options) => {\n await executeChatRead(chat, options);\n });\n}\n","import { createWalletClient, http } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { getChainRpcUrls, getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\n\n/**\n * Create a wallet client from a private key\n */\nexport function createWallet(\n privateKey: `0x${string}`,\n chainId: number,\n rpcUrl?: string\n) {\n const account = privateKeyToAccount(privateKey);\n const rpcUrls = getChainRpcUrls({\n chainId,\n rpcUrl: rpcUrl,\n });\n\n return createWalletClient({\n account,\n transport: http(rpcUrls[0]),\n dataSuffix: getBaseDataSuffix(chainId),\n });\n}\n\n/**\n * Execute a transaction using a wallet client\n */\nexport async function executeTransaction(\n walletClient: ReturnType<typeof createWallet>,\n txConfig: WriteTransactionConfig\n): Promise<`0x${string}`> {\n const hash = await walletClient.writeContract({\n address: txConfig.to,\n abi: txConfig.abi,\n functionName: txConfig.functionName,\n args: txConfig.args,\n value: txConfig.value,\n chain: null,\n } as Parameters<typeof walletClient.writeContract>[0]);\n\n return hash;\n}\n","import { encodeFunctionData, concat } from \"viem\";\nimport { getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\nimport type { EncodedTransaction } from \"./types\";\n\nexport type { EncodedTransaction };\n\n/**\n * Encode a write transaction config into transaction data\n * Used for --encode-only mode where we output transaction data instead of executing\n */\nexport function encodeTransaction(\n config: WriteTransactionConfig,\n chainId: number\n): EncodedTransaction {\n const calldata = encodeFunctionData({\n abi: config.abi,\n functionName: config.functionName,\n args: config.args,\n });\n\n const suffix = getBaseDataSuffix(chainId);\n const data = suffix ? concat([calldata, suffix]) : calldata;\n\n return {\n to: config.to,\n data,\n chainId,\n value: config.value?.toString() ?? \"0\",\n };\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { parseReadOnlyOptionsWithDefault, parseCommonOptionsWithDefault } from \"../../cli/shared\";\nimport { createChatClient } from \"../../shared/client\";\nimport { createWallet, executeTransaction } from \"../../shared/wallet\";\nimport { encodeTransaction } from \"../../shared/encode\";\nimport { exitWithError } from \"../../shared/output\";\nimport { normalizeChatName } from \"./types\";\n\ninterface SendOptions {\n chainId?: number;\n rpcUrl?: string;\n privateKey?: string;\n encodeOnly?: boolean;\n data?: string;\n}\n\nconst MAX_MESSAGE_LENGTH = 4000;\n\n/**\n * Execute the chat send command\n */\nasync function executeChatSend(\n chat: string,\n message: string,\n options: SendOptions\n): Promise<void> {\n const normalizedChat = normalizeChatName(chat);\n\n if (message.length === 0) {\n exitWithError(\"Message cannot be empty\");\n }\n\n if (message.length > MAX_MESSAGE_LENGTH) {\n exitWithError(\n `Message too long (${message.length} chars). Maximum is ${MAX_MESSAGE_LENGTH} characters.`\n );\n }\n\n // For encode-only mode, we don't need a private key\n if (options.encodeOnly) {\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = createChatClient(readOnlyOptions);\n const txConfig = client.prepareSendChatMessage({\n topic: normalizedChat,\n text: message,\n data: options.data,\n });\n const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);\n\n console.log(JSON.stringify(encoded, null, 2));\n return;\n }\n\n // For actual execution, we need a private key\n const commonOptions = parseCommonOptionsWithDefault(\n {\n privateKey: options.privateKey,\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n },\n true // supports --encode-only\n );\n\n const client = createChatClient(commonOptions);\n const txConfig = client.prepareSendChatMessage({\n topic: normalizedChat,\n text: message,\n data: options.data,\n });\n\n const walletClient = createWallet(\n commonOptions.privateKey,\n commonOptions.chainId,\n commonOptions.rpcUrl\n );\n\n console.log(chalk.blue(`Sending message to chat \"${normalizedChat}\"...`));\n\n try {\n const hash = await executeTransaction(walletClient, txConfig);\n\n console.log(\n chalk.green(\n `Message sent successfully!\\n Transaction: ${hash}\\n Chat: ${normalizedChat}\\n Text: ${message}`\n )\n );\n } catch (error) {\n exitWithError(\n `Failed to send message: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Register the chat send subcommand\n */\nexport function registerChatSendCommand(parent: Command): void {\n parent\n .command(\"send <chat> <message>\")\n .description(\"Send a message to a group chat (NOT 'post' — that's for feeds)\")\n .option(\n \"--chain-id <id>\",\n \"Chain ID (default: 8453 for Base)\",\n (value) => parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--private-key <key>\", \"Private key (0x-prefixed)\")\n .option(\n \"--encode-only\",\n \"Output transaction data as JSON instead of executing\"\n )\n .option(\"--data <data>\", \"Optional data to attach to the message\")\n .action(async (chat, message, options) => {\n await executeChatSend(chat, message, options);\n });\n}\n","import { Command } from \"commander\";\nimport { registerChatReadCommand } from \"./read\";\nimport { registerChatSendCommand } from \"./send\";\n\n/**\n * Register the chat command group with the commander program\n */\nexport function registerChatCommand(program: Command): void {\n const chatCommand = program\n .command(\"chat\")\n .description(\"Group chat operations — use 'chat send' and 'chat read' (NOT 'post'/'read', which are for feeds)\");\n\n registerChatReadCommand(chatCommand);\n registerChatSendCommand(chatCommand);\n}\n\n// Re-export individual command registrations for botchan wrapper\nexport { registerChatReadCommand } from \"./read\";\nexport { registerChatSendCommand } from \"./send\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/shared.ts","../../src/shared/client.ts","../../src/shared/output.ts","../../src/shared/urls.ts","../../src/commands/chat/types.ts","../../src/commands/chat/read.ts","../../src/shared/wallet.ts","../../src/shared/encode.ts","../../src/commands/chat/send.ts","../../src/commands/chat/index.ts"],"names":["chalk","senderNote","getBaseDataSuffix","client","txConfig"],"mappings":";;;;;;;;;AAMO,IAAM,gBAAA,GAAmB,IAAA;AA4BhC,SAAS,sBAAsB,WAAA,EAA8B;AAC3D,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAO,WAAA;AAAA,EACT;AAEA,EAAA,MAAM,UAAA,GACJ,OAAA,CAAQ,GAAA,CAAI,gBAAA,IAAoB,QAAQ,GAAA,CAAI,YAAA;AAE9C,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,OAAO,QAAA,CAAS,YAAY,EAAE,CAAA;AAAA,EAChC;AAEA,EAAA,OAAO,gBAAA;AACT;AAaA,SAAS,6BAA6B,WAAA,EAA0C;AAC9E,EAAA,OAAO,WAAA,IAAe,OAAA,CAAQ,GAAA,CAAI,eAAA,IAAmB,QAAQ,GAAA,CAAI,WAAA;AACnE;AA4EO,SAAS,gCAAgC,OAAA,EAG5B;AAClB,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,qBAAA,CAAsB,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC9C,MAAA,EAAQ,4BAAA,CAA6B,OAAA,CAAQ,MAAM;AAAA,GACrD;AACF;AAOO,SAAS,6BAAA,CACd,OAAA,EAKA,kBAAA,GAAqB,KAAA,EACN;AACf,EAAA,MAAM,UAAA,GACJ,OAAA,CAAQ,UAAA,IACR,OAAA,CAAQ,GAAA,CAAI,uBACZ,OAAA,CAAQ,GAAA,CAAI,eAAA,IACZ,OAAA,CAAQ,GAAA,CAAI,WAAA;AAEd,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,MAAM,cAAA,GAAiB,qBACnB,sEAAA,GACA,EAAA;AACJ,IAAA,OAAA,CAAQ,KAAA;AAAA,MACNA,MAAA,CAAM,GAAA;AAAA,QACJ,6HAA6H,cAAc,CAAA;AAAA;AAC7I,KACF;AACA,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,IAAI,CAAC,UAAA,CAAW,UAAA,CAAW,IAAI,CAAA,IAAK,UAAA,CAAW,WAAW,EAAA,EAAI;AAC5D,IAAA,OAAA,CAAQ,KAAA;AAAA,MACNA,MAAA,CAAM,GAAA;AAAA,QACJ;AAAA;AACF,KACF;AACA,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,IAAA,OAAA,CAAQ,IAAA;AAAA,MACNA,MAAA,CAAM,MAAA;AAAA,QACJ;AAAA;AACF,KACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,OAAA,EAAS,qBAAA,CAAsB,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC9C,MAAA,EAAQ,4BAAA,CAA6B,OAAA,CAAQ,MAAM;AAAA,GACrD;AACF;AC1KO,SAAS,iBAAiB,OAAA,EAAsC;AACrE,EAAA,OAAO,IAAI,UAAA,CAAW;AAAA,IACpB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,SAAA,EAAW,QAAQ,MAAA,GAAS,EAAE,SAAS,CAAC,OAAA,CAAQ,MAAM,CAAA,EAAE,GAAI;AAAA,GAC7D,CAAA;AACH;ACwDO,SAAS,cAAc,OAAA,EAAwB;AACpD,EAAA,OAAA,CAAQ,MAAMA,MAAAA,CAAM,GAAA,CAAI,CAAA,OAAA,EAAU,OAAO,EAAE,CAAC,CAAA;AAC5C,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAChB;AC5EA,IAAM,YAAA,GAAe,yBAAA;AAGd,SAAS,UAAU,OAAA,EAAgC;AACxD,EAAA,OAAO,YAAA,CAAa,EAAE,OAAA,EAAS,CAAA,IAAK,IAAA;AACtC;AAiCO,SAAS,UAAA,CAAW,SAAiB,OAAA,EAAgC;AAC1E,EAAA,MAAM,IAAA,GAAO,UAAU,OAAO,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,GAAG,YAAY,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA,EAAI,OAAA,CAAQ,aAAa,CAAA,CAAA;AACrE;AAKO,SAAS,OAAA,CAAQ,SAAiB,QAAA,EAAiC;AACxE,EAAA,MAAM,IAAA,GAAO,UAAU,OAAO,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,CAAA,EAAG,YAAY,CAAA,UAAA,EAAa,IAAI,CAAA,CAAA,EAAI,kBAAA;AAAA,IACzC,SAAS,WAAA;AAAY,GACtB,CAAA,CAAA;AACH;;;ACrEO,SAAS,kBAAkB,IAAA,EAAsB;AACtD,EAAA,OAAO,KAAK,WAAA,EAAY;AAC1B;;;ACeA,SAAS,aAAA,CAAc,KAAiB,KAAA,EAAuB;AAC7D,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,MAAA,CAAO,IAAI,SAAS,CAAA,GAAI,GAAI,CAAA,CAAE,cAAA,EAAe;AACnE,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,GAAI,KAAA,GAAQ,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,EAAE,CAAA;AACnE,EAAA,OAAO,KAAKA,MAAAA,CAAM,IAAA,CAAK,CAAA,CAAA,EAAI,KAAA,GAAQ,CAAC,CAAA,CAAA,CAAG,CAAC,CAAA,CAAA,EAAIA,MAAAA,CAAM,KAAK,MAAM,CAAC,IAAIA,MAAAA,CAAM,IAAA,CAAK,IAAI,CAAC;AAAA,EAAA,EAAO,IAAI,IAAI,CAAA,CAAA;AACnG;AAEA,SAAS,aAAA,CACP,GAAA,EACA,KAAA,EACA,OAAA,EACA,QAAA,EACA;AACA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,gBAAA,EAAkB,UAAA,CAAgB,OAAA,EAAS,GAAA,CAAI,MAAM,CAAA;AAAA,IACrD,MAAM,GAAA,CAAI,IAAA;AAAA,IACV,SAAA,EAAW,MAAA,CAAO,GAAA,CAAI,SAAS,CAAA;AAAA,IAC/B,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EAAS,OAAA,CAAa,OAAA,EAAS,QAAQ,CAAA;AAAA,IACvC,MAAM,GAAA,CAAI;AAAA,GACZ;AACF;AAEA,SAAS,UAAU,IAAA,EAAqB;AACtC,EAAA,OAAA,CAAQ,IAAI,IAAA,CAAK,SAAA,CAAU,IAAA,EAAM,IAAA,EAAM,CAAC,CAAC,CAAA;AAC3C;AAKA,eAAe,eAAA,CAAgB,MAAc,OAAA,EAAqC;AAChF,EAAA,MAAM,cAAA,GAAiB,kBAAkB,IAAI,CAAA;AAC7C,EAAA,MAAM,kBAAkB,+BAAA,CAAgC;AAAA,IACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,iBAAiB,eAAe,CAAA;AAC/C,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,EAAA;AAE/B,EAAA,IAAI;AACF,IAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,mBAAA,CAAoB,cAAc,CAAA;AAE7D,IAAA,IAAI,UAAU,CAAA,EAAG;AACf,MAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,QAAA,SAAA,CAAU,EAAE,CAAA;AAAA,MACd,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,MAAA,CAAO,CAAA,2BAAA,EAA8B,cAAc,GAAG,CAAC,CAAA;AAAA,MAC3E;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,QAAQ,MAAA,GAAS,IAAA,CAAK,IAAI,KAAA,GAAQ,CAAA,EAAG,GAAG,CAAA,GAAI,KAAA;AAE/D,IAAA,IAAI,QAAA,GAAW,MAAM,MAAA,CAAO,eAAA,CAAgB;AAAA,MAC1C,KAAA,EAAO,cAAA;AAAA,MACP,WAAA,EAAa;AAAA,KACd,CAAA;AAGD,IAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,MAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,MAAA,CAAO,WAAA,EAAY;AAC/C,MAAA,QAAA,GAAW,QAAA,CAAS,MAAA;AAAA,QAClB,CAAC,GAAA,KAAoB,GAAA,CAAI,MAAA,CAAO,aAAY,KAAM;AAAA,OACpD;AACA,MAAA,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA;AAAA,IACpC;AAEA,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,SAAA;AAAA,QACE,QAAA,CAAS,GAAA;AAAA,UAAI,CAAC,KAAiB,CAAA,KAC7B,aAAA,CAAc,KAAK,CAAA,EAAG,eAAA,CAAgB,SAAS,cAAc;AAAA;AAC/D,OACF;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,QAAA,MAAMC,cAAa,OAAA,CAAQ,MAAA,GAAS,CAAA,IAAA,EAAO,OAAA,CAAQ,MAAM,CAAA,CAAA,GAAK,EAAA;AAC9D,QAAA,OAAA,CAAQ,GAAA,CAAID,OAAM,MAAA,CAAO,CAAA,2BAAA,EAA8B,cAAc,CAAA,CAAA,EAAIC,WAAU,EAAE,CAAC,CAAA;AACtF,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,aAAa,OAAA,CAAQ,MAAA,GAAS,CAAA,IAAA,EAAO,OAAA,CAAQ,MAAM,CAAA,CAAA,GAAK,EAAA;AAC9D,MAAA,OAAA,CAAQ,GAAA;AAAA,QACND,MAAAA,CAAM,MAAM,CAAA,MAAA,EAAS,QAAA,CAAS,MAAM,CAAA,qBAAA,EAAwB,cAAc,IAAI,UAAU,CAAA;AAAA,CAAK;AAAA,OAC/F;AACA,MAAA,QAAA,CAAS,OAAA,CAAQ,CAAC,GAAA,EAAiB,CAAA,KAAc;AAC/C,QAAA,OAAA,CAAQ,GAAA,CAAI,aAAA,CAAc,GAAA,EAAK,CAAC,CAAC,CAAA;AACjC,QAAA,IAAI,CAAA,GAAI,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AAC3B,UAAA,OAAA,CAAQ,GAAA,EAAI;AAAA,QACd;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,wBAAwB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KAChF;AAAA,EACF;AACF;AAKO,SAAS,wBAAwB,MAAA,EAAuB;AAC7D,EAAA,MAAA,CACG,OAAA,CAAQ,aAAa,CAAA,CACrB,WAAA,CAAY,sEAAiE,CAAA,CAC7E,MAAA;AAAA,IACC,aAAA;AAAA,IACA,uCAAA;AAAA,IACA,CAAC,KAAA,KAAU,QAAA,CAAS,KAAA,EAAO,EAAE;AAAA,GAC/B,CACC,MAAA;AAAA,IACC,iBAAA;AAAA,IACA,mCAAA;AAAA,IACA,CAAC,KAAA,KAAU,QAAA,CAAS,KAAA,EAAO,EAAE;AAAA,IAE9B,MAAA,CAAO,iBAAA,EAAmB,gBAAgB,CAAA,CAC1C,OAAO,oBAAA,EAAsB,mCAAmC,CAAA,CAChE,MAAA,CAAO,UAAU,uBAAuB,CAAA,CACxC,MAAA,CAAO,OAAO,MAAM,OAAA,KAAY;AAC/B,IAAA,MAAM,eAAA,CAAgB,MAAM,OAAO,CAAA;AAAA,EACrC,CAAC,CAAA;AACL;ACvIO,SAAS,YAAA,CACd,UAAA,EACA,OAAA,EACA,MAAA,EACA;AACA,EAAA,MAAM,OAAA,GAAU,oBAAoB,UAAU,CAAA;AAC9C,EAAA,MAAM,UAAU,eAAA,CAAgB;AAAA,IAC9B,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,OAAO,kBAAA,CAAmB;AAAA,IACxB,OAAA;AAAA,IACA,SAAA,EAAW,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,IAC1B,UAAA,EAAY,kBAAkB,OAAO;AAAA,GACtC,CAAA;AACH;AAKA,eAAsB,kBAAA,CACpB,cACA,QAAA,EACwB;AACxB,EAAA,MAAM,IAAA,GAAO,MAAM,YAAA,CAAa,aAAA,CAAc;AAAA,IAC5C,SAAS,QAAA,CAAS,EAAA;AAAA,IAClB,KAAK,QAAA,CAAS,GAAA;AAAA,IACd,cAAc,QAAA,CAAS,YAAA;AAAA,IACvB,MAAM,QAAA,CAAS,IAAA;AAAA,IACf,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB,KAAA,EAAO;AAAA,GAC4C,CAAA;AAErD,EAAA,OAAO,IAAA;AACT;AChCO,SAAS,iBAAA,CACd,QACA,OAAA,EACoB;AACpB,EAAA,MAAM,WAAW,kBAAA,CAAmB;AAAA,IAClC,KAAK,MAAA,CAAO,GAAA;AAAA,IACZ,cAAc,MAAA,CAAO,YAAA;AAAA,IACrB,MAAM,MAAA,CAAO;AAAA,GACd,CAAA;AAED,EAAA,MAAM,MAAA,GAASE,kBAAkB,OAAO,CAAA;AACxC,EAAA,MAAM,OAAO,MAAA,GAAS,MAAA,CAAO,CAAC,QAAA,EAAU,MAAM,CAAC,CAAA,GAAI,QAAA;AAEnD,EAAA,OAAO;AAAA,IACL,IAAI,MAAA,CAAO,EAAA;AAAA,IACX,IAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,QAAA,EAAS,IAAK;AAAA,GACrC;AACF;;;ACbA,IAAM,kBAAA,GAAqB,GAAA;AAK3B,eAAe,eAAA,CACb,IAAA,EACA,OAAA,EACA,OAAA,EACe;AACf,EAAA,MAAM,cAAA,GAAiB,kBAAkB,IAAI,CAAA;AAE7C,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,IAAA,aAAA,CAAc,yBAAyB,CAAA;AAAA,EACzC;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,kBAAA,EAAoB;AACvC,IAAA,aAAA;AAAA,MACE,CAAA,kBAAA,EAAqB,OAAA,CAAQ,MAAM,CAAA,oBAAA,EAAuB,kBAAkB,CAAA,YAAA;AAAA,KAC9E;AAAA,EACF;AAGA,EAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,IAAA,MAAM,kBAAkB,+BAAA,CAAgC;AAAA,MACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,QAAQ,OAAA,CAAQ;AAAA,KACjB,CAAA;AAED,IAAA,MAAMC,OAAAA,GAAS,iBAAiB,eAAe,CAAA;AAC/C,IAAA,MAAMC,SAAAA,GAAWD,QAAO,sBAAA,CAAuB;AAAA,MAC7C,KAAA,EAAO,cAAA;AAAA,MACP,IAAA,EAAM,OAAA;AAAA,MACN,MAAM,OAAA,CAAQ;AAAA,KACf,CAAA;AACD,IAAA,MAAM,OAAA,GAAU,iBAAA,CAAkBC,SAAAA,EAAU,eAAA,CAAgB,OAAO,CAAA;AAEnE,IAAA,OAAA,CAAQ,IAAI,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,IAAA,EAAM,CAAC,CAAC,CAAA;AAC5C,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,aAAA,GAAgB,6BAAA;AAAA,IACpB;AAAA,MACE,YAAY,OAAA,CAAQ,UAAA;AAAA,MACpB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,QAAQ,OAAA,CAAQ;AAAA,KAClB;AAAA,IACA;AAAA;AAAA,GACF;AAEA,EAAA,MAAM,MAAA,GAAS,iBAAiB,aAAa,CAAA;AAC7C,EAAA,MAAM,QAAA,GAAW,OAAO,sBAAA,CAAuB;AAAA,IAC7C,KAAA,EAAO,cAAA;AAAA,IACP,IAAA,EAAM,OAAA;AAAA,IACN,MAAM,OAAA,CAAQ;AAAA,GACf,CAAA;AAED,EAAA,MAAM,YAAA,GAAe,YAAA;AAAA,IACnB,aAAA,CAAc,UAAA;AAAA,IACd,aAAA,CAAc,OAAA;AAAA,IACd,aAAA,CAAc;AAAA,GAChB;AAEA,EAAA,OAAA,CAAQ,IAAIJ,MAAAA,CAAM,IAAA,CAAK,CAAA,yBAAA,EAA4B,cAAc,MAAM,CAAC,CAAA;AAExE,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAM,kBAAA,CAAmB,YAAA,EAAc,QAAQ,CAAA;AAE5D,IAAA,OAAA,CAAQ,GAAA;AAAA,MACNA,MAAAA,CAAM,KAAA;AAAA,QACJ,CAAA;AAAA,eAAA,EAA8C,IAAI;AAAA,QAAA,EAAa,cAAc;AAAA,QAAA,EAAa,OAAO,CAAA;AAAA;AACnG,KACF;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,2BAA2B,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACnF;AAAA,EACF;AACF;AAKO,SAAS,wBAAwB,MAAA,EAAuB;AAC7D,EAAA,MAAA,CACG,OAAA,CAAQ,uBAAuB,CAAA,CAC/B,WAAA,CAAY,qEAAgE,CAAA,CAC5E,MAAA;AAAA,IACC,iBAAA;AAAA,IACA,mCAAA;AAAA,IACA,CAAC,KAAA,KAAU,QAAA,CAAS,KAAA,EAAO,EAAE;AAAA,GAC/B,CACC,OAAO,iBAAA,EAAmB,gBAAgB,EAC1C,MAAA,CAAO,qBAAA,EAAuB,2BAA2B,CAAA,CACzD,MAAA;AAAA,IACC,eAAA;AAAA,IACA;AAAA,GACF,CACC,OAAO,eAAA,EAAiB,wCAAwC,EAChE,MAAA,CAAO,OAAO,IAAA,EAAM,OAAA,EAAS,OAAA,KAAY;AACxC,IAAA,MAAM,eAAA,CAAgB,IAAA,EAAM,OAAA,EAAS,OAAO,CAAA;AAAA,EAC9C,CAAC,CAAA;AACL;;;ACjHO,SAAS,oBAAoB,OAAA,EAAwB;AAC1D,EAAA,MAAM,cAAc,OAAA,CACjB,OAAA,CAAQ,MAAM,CAAA,CACd,YAAY,uGAAkG,CAAA;AAEjH,EAAA,uBAAA,CAAwB,WAAW,CAAA;AACnC,EAAA,uBAAA,CAAwB,WAAW,CAAA;AACrC","file":"index.mjs","sourcesContent":["import chalk from \"chalk\";\nimport type { CommonOptions, ReadOnlyOptions } from \"../shared/types\";\n\n/**\n * Default chain ID (Base mainnet) - used by feed commands\n */\nexport const DEFAULT_CHAIN_ID = 8453;\n\n/**\n * Get chain ID from option or environment variable, exit if not found\n */\nfunction getRequiredChainId(optionValue?: number): number {\n const chainId =\n optionValue ||\n (process.env.NET_CHAIN_ID\n ? parseInt(process.env.NET_CHAIN_ID, 10)\n : undefined);\n\n if (!chainId) {\n console.error(\n chalk.red(\n \"Error: Chain ID is required. Provide via --chain-id flag or NET_CHAIN_ID environment variable\"\n )\n );\n process.exit(1);\n }\n\n return chainId;\n}\n\n/**\n * Get chain ID from option or environment variable, defaulting to Base (8453)\n * Also checks BOTCHAN_* env vars for backward compat\n */\nfunction getChainIdWithDefault(optionValue?: number): number {\n if (optionValue) {\n return optionValue;\n }\n\n const envChainId =\n process.env.BOTCHAN_CHAIN_ID || process.env.NET_CHAIN_ID;\n\n if (envChainId) {\n return parseInt(envChainId, 10);\n }\n\n return DEFAULT_CHAIN_ID;\n}\n\n/**\n * Get RPC URL from option or environment variable\n */\nfunction getRpcUrl(optionValue?: string): string | undefined {\n return optionValue || process.env.NET_RPC_URL;\n}\n\n/**\n * Get RPC URL from option or environment variable, also checking BOTCHAN_* env vars.\n * Used only by feed commands for backward compat.\n */\nfunction getRpcUrlWithBotchanFallback(optionValue?: string): string | undefined {\n return optionValue || process.env.BOTCHAN_RPC_URL || process.env.NET_RPC_URL;\n}\n\n/**\n * Parse and validate common options shared across all commands.\n * Extracts private key, chain ID, and RPC URL from command options or environment variables.\n * @param options - Command options\n * @param supportsEncodeOnly - If true, mention --encode-only in error messages as an alternative\n */\nexport function parseCommonOptions(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse and validate read-only options for commands that don't need a private key.\n * Extracts chain ID and RPC URL from command options or environment variables.\n */\nexport function parseReadOnlyOptions(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse read-only options with a default chain ID (8453/Base).\n * Used by feed commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseReadOnlyOptionsWithDefault(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n\n/**\n * Parse common options with a default chain ID (8453/Base).\n * Used by feed write commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseCommonOptionsWithDefault(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.BOTCHAN_PRIVATE_KEY ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/BOTCHAN_PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n","import { FeedClient, FeedRegistryClient, AgentRegistryClient } from \"@net-protocol/feeds\";\nimport { ChatClient } from \"@net-protocol/chats\";\nimport { NetClient } from \"@net-protocol/core\";\nimport { StorageClient } from \"@net-protocol/storage\";\nimport type { ReadOnlyOptions } from \"./types\";\n\n/**\n * Create a FeedClient from read-only options\n */\nexport function createFeedClient(options: ReadOnlyOptions): FeedClient {\n return new FeedClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a FeedRegistryClient from read-only options\n */\nexport function createFeedRegistryClient(\n options: ReadOnlyOptions\n): FeedRegistryClient {\n return new FeedRegistryClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a ChatClient from read-only options\n */\nexport function createChatClient(options: ReadOnlyOptions): ChatClient {\n return new ChatClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a NetClient from read-only options\n */\nexport function createNetClient(options: ReadOnlyOptions): NetClient {\n return new NetClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create an AgentRegistryClient from read-only options\n */\nexport function createAgentRegistryClient(\n options: ReadOnlyOptions\n): AgentRegistryClient {\n return new AgentRegistryClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n\n/**\n * Create a StorageClient from read-only options\n */\nexport function createStorageClient(options: ReadOnlyOptions): StorageClient {\n return new StorageClient({\n chainId: options.chainId,\n overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : undefined,\n });\n}\n","import chalk from \"chalk\";\nimport type { NetMessage } from \"@net-protocol/core\";\n\n/**\n * Format a message for human-readable output\n */\nexport function formatMessage(\n message: NetMessage,\n index: number\n): string {\n const timestamp = new Date(Number(message.timestamp) * 1000).toISOString();\n const lines = [\n chalk.cyan(`[${index}]`) + ` ${chalk.gray(timestamp)}`,\n ` ${chalk.white(\"Sender:\")} ${message.sender}`,\n ` ${chalk.white(\"App:\")} ${message.app}`,\n ];\n\n if (message.topic) {\n lines.push(` ${chalk.white(\"Topic:\")} ${message.topic}`);\n }\n\n lines.push(` ${chalk.white(\"Text:\")} ${message.text}`);\n\n if (message.data && message.data !== \"0x\") {\n lines.push(` ${chalk.white(\"Data:\")} ${message.data}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a message for JSON output\n */\nexport function messageToJson(\n message: NetMessage,\n index: number\n): Record<string, unknown> {\n return {\n index,\n sender: message.sender,\n app: message.app,\n timestamp: Number(message.timestamp),\n text: message.text,\n topic: message.topic,\n data: message.data,\n };\n}\n\n/**\n * Print messages in human-readable or JSON format\n */\nexport function printMessages(\n messages: NetMessage[],\n startIndex: number,\n json: boolean\n): void {\n if (json) {\n const output = messages.map((msg, i) => messageToJson(msg, startIndex + i));\n console.log(JSON.stringify(output, null, 2));\n } else {\n if (messages.length === 0) {\n console.log(chalk.yellow(\"No messages found\"));\n return;\n }\n\n messages.forEach((msg, i) => {\n console.log(formatMessage(msg, startIndex + i));\n if (i < messages.length - 1) {\n console.log(); // Empty line between messages\n }\n });\n }\n}\n\n/**\n * Print a count result\n */\nexport function printCount(\n count: number,\n label: string,\n json: boolean\n): void {\n if (json) {\n console.log(JSON.stringify({ count }, null, 2));\n } else {\n console.log(`${chalk.white(label)} ${chalk.cyan(count)}`);\n }\n}\n\n/**\n * Print an error message and exit\n */\nexport function exitWithError(message: string): never {\n console.error(chalk.red(`Error: ${message}`));\n process.exit(1);\n}\n","/**\n * URL builders for Net Protocol web pages and external services.\n *\n * These helpers exist so callers (and AI agents reading CLI JSON output) never\n * need to construct URLs by hand. URL grammar quirks (the \"feed-\" topic\n * prefix, hyphen-vs-colon separators in comment IDs, lowercased addresses,\n * etc.) all live here in one place.\n *\n * Chain slugs and block-explorer base URLs come from\n * `@net-protocol/core`'s chain config — this module deliberately holds no\n * per-chain tables of its own.\n */\n\nimport {\n getChainBlockExplorer,\n getChainSlug,\n} from \"@net-protocol/core\";\nimport { encodeStorageKeyForUrl } from \"@net-protocol/storage\";\n\nconst WEBSITE_BASE = \"https://netprotocol.app\";\nconst STORAGE_BASE = \"https://storedon.net\";\n\nexport function chainSlug(chainId: number): string | null {\n return getChainSlug({ chainId }) ?? null;\n}\n\n/**\n * Strip the \"feed-\" prefix from a topic if present (case-insensitive).\n * Used when converting an on-chain topic to the URL path/query form.\n */\nfunction stripFeedPrefix(topic: string): string {\n const lower = topic.toLowerCase();\n return lower.startsWith(\"feed-\") ? lower.slice(5) : lower;\n}\n\n/**\n * URL of a feed page. `feedName` may be passed with or without the \"feed-\"\n * prefix; either way the URL form is stripped+lowercased.\n */\nexport function feedUrl(chainId: number, feedName: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/feed/${slug}/${stripFeedPrefix(feedName)}`;\n}\n\n/**\n * URL of an address's wall (their personal feed at `feed-{lower(address)}`).\n */\nexport function walletUrl(chainId: number, address: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/feed/${slug}/${address.toLowerCase()}`;\n}\n\n/**\n * URL of a user/agent profile page.\n */\nexport function profileUrl(chainId: number, address: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/profile/${slug}/${address.toLowerCase()}`;\n}\n\n/**\n * URL of a group chat page.\n */\nexport function chatUrl(chainId: number, chatName: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/chat/${slug}/${encodeURIComponent(\n chatName.toLowerCase()\n )}`;\n}\n\n/**\n * URL of a Netr token page.\n */\nexport function tokenUrl(\n chainId: number,\n tokenAddress: string\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/token/${slug}/${tokenAddress.toLowerCase()}`;\n}\n\n/**\n * URL of a Bazaar NFT collection page.\n */\nexport function bazaarUrl(\n chainId: number,\n nftAddress: string\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/bazaar/${slug}/${nftAddress.toLowerCase()}`;\n}\n\n/**\n * URL of an onchain agent detail page.\n */\nexport function agentUrl(chainId: number, agentId: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/agents/${slug}/${encodeURIComponent(agentId)}`;\n}\n\n/**\n * Public storage retrieval URL via storedon.net. Works on any chain with Net\n * Storage deployed (returns a URL even when chainSlug is unknown — storedon\n * uses numeric chain IDs).\n *\n * Uses `encodeStorageKeyForUrl` from `@net-protocol/storage` so the encoder\n * stays in sync with the storage SDK if it ever needs to handle binary keys\n * or other special characters differently from `encodeURIComponent`.\n */\nexport function storageUrl(\n chainId: number,\n operatorAddress: string,\n key: string\n): string {\n return `${STORAGE_BASE}/net/${chainId}/storage/load/${operatorAddress.toLowerCase()}/${encodeStorageKeyForUrl(\n key\n )}`;\n}\n\nexport function explorerTxUrl(\n chainId: number,\n txHash: string\n): string | null {\n const base = getChainBlockExplorer({ chainId })?.url;\n if (!base) return null;\n return `${base}/tx/${txHash}`;\n}\n\nexport function explorerAddressUrl(\n chainId: number,\n address: string\n): string | null {\n const base = getChainBlockExplorer({ chainId })?.url;\n if (!base) return null;\n return `${base}/address/${address}`;\n}\n\n/**\n * Convert a post ID (`{sender}:{timestamp}`) into the comment-permalink form\n * used by the web's `?commentId=` query parameter (`{sender}-{timestamp}`).\n *\n * The two formats are intentionally documented separately because the website\n * scrolls to the DOM id `comment-{sender}-{timestamp}` and matches against the\n * raw query value (see CommentThread.tsx in the Net repo).\n */\nexport function postIdToCommentParam(postId: string): string {\n const colon = postId.indexOf(\":\");\n if (colon === -1) return postId;\n return `${postId.slice(0, colon)}-${postId.slice(colon + 1)}`;\n}\n\nexport interface PostPermalinkOptions {\n /**\n * Global message index from the contract's MessageSent event. Most reliable\n * when available — works regardless of how the post was queried.\n */\n globalIndex?: number;\n /** Topic-filtered absolute index (from a topic-scoped read). */\n topic?: string;\n topicIndex?: number;\n /** Maker-filtered absolute index (from a sender-scoped read). */\n user?: string;\n userIndex?: number;\n /**\n * Optional comment to deep-link. Pass either a post-ID-style string\n * (`{sender}:{timestamp}` — colon will be normalized to hyphen) or the\n * already-converted form.\n */\n commentId?: string;\n}\n\n/**\n * Build a permalink to the dedicated post page. Picks the most reliable URL\n * form available, in priority order: global -> topic -> user. Returns null\n * when no usable index is provided or chain is unknown.\n */\nexport function postPermalink(\n chainId: number,\n opts: PostPermalinkOptions\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n\n const params = new URLSearchParams();\n\n if (opts.globalIndex != null) {\n params.set(\"index\", String(opts.globalIndex));\n } else if (opts.topic != null && opts.topicIndex != null) {\n params.set(\"topic\", stripFeedPrefix(opts.topic));\n params.set(\"index\", String(opts.topicIndex));\n } else if (opts.user != null && opts.userIndex != null) {\n params.set(\"user\", opts.user.toLowerCase());\n params.set(\"index\", String(opts.userIndex));\n } else {\n return null;\n }\n\n if (opts.commentId) {\n params.set(\"commentId\", postIdToCommentParam(opts.commentId));\n }\n\n return `${WEBSITE_BASE}/app/feed/${slug}/post?${params.toString()}`;\n}\n\n/**\n * URL of the canonical hosted skill (proxies to net-public/SKILL.md).\n */\nexport const HOSTED_SKILL_URL = `${WEBSITE_BASE}/skill.md`;\n","/**\n * Normalize a chat name to lowercase for consistency.\n */\nexport function normalizeChatName(chat: string): string {\n return chat.toLowerCase();\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport type { NetMessage } from \"@net-protocol/chats\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { createChatClient } from \"../../shared/client\";\nimport { exitWithError } from \"../../shared/output\";\nimport {\n chatUrl as buildChatUrl,\n profileUrl as buildProfileUrl,\n} from \"../../shared/urls\";\nimport { normalizeChatName } from \"./types\";\n\ninterface ReadOptions {\n limit?: number;\n chainId?: number;\n rpcUrl?: string;\n json?: boolean;\n sender?: string;\n}\n\nfunction formatMessage(msg: NetMessage, index: number): string {\n const time = new Date(Number(msg.timestamp) * 1000).toLocaleString();\n const sender = msg.sender.slice(0, 6) + \"...\" + msg.sender.slice(-4);\n return ` ${chalk.gray(`[${index + 1}]`)} ${chalk.cyan(sender)} ${chalk.gray(time)}\\n ${msg.text}`;\n}\n\nfunction messageToJson(\n msg: NetMessage,\n index: number,\n chainId: number,\n chatName: string\n) {\n return {\n index,\n sender: msg.sender,\n senderProfileUrl: buildProfileUrl(chainId, msg.sender),\n text: msg.text,\n timestamp: Number(msg.timestamp),\n chat: chatName,\n chatUrl: buildChatUrl(chainId, chatName),\n data: msg.data,\n };\n}\n\nfunction printJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/**\n * Execute the chat read command\n */\nasync function executeChatRead(chat: string, options: ReadOptions): Promise<void> {\n const normalizedChat = normalizeChatName(chat);\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = createChatClient(readOnlyOptions);\n const limit = options.limit ?? 20;\n\n try {\n const count = await client.getChatMessageCount(normalizedChat);\n\n if (count === 0) {\n if (options.json) {\n printJson([]);\n } else {\n console.log(chalk.yellow(`No messages found in chat \"${normalizedChat}\"`));\n }\n return;\n }\n\n const fetchLimit = options.sender ? Math.max(limit * 5, 100) : limit;\n\n let messages = await client.getChatMessages({\n topic: normalizedChat,\n maxMessages: fetchLimit,\n });\n\n // Filter by sender if specified\n if (options.sender) {\n const senderLower = options.sender.toLowerCase();\n messages = messages.filter(\n (msg: NetMessage) => msg.sender.toLowerCase() === senderLower\n );\n messages = messages.slice(0, limit);\n }\n\n if (options.json) {\n printJson(\n messages.map((msg: NetMessage, i: number) =>\n messageToJson(msg, i, readOnlyOptions.chainId, normalizedChat)\n )\n );\n } else {\n if (messages.length === 0) {\n const senderNote = options.sender ? ` by ${options.sender}` : \"\";\n console.log(chalk.yellow(`No messages found in chat \"${normalizedChat}\"${senderNote}`));\n return;\n }\n\n const senderNote = options.sender ? ` by ${options.sender}` : \"\";\n console.log(\n chalk.white(`Found ${messages.length} message(s) in chat \"${normalizedChat}\"${senderNote}:\\n`)\n );\n messages.forEach((msg: NetMessage, i: number) => {\n console.log(formatMessage(msg, i));\n if (i < messages.length - 1) {\n console.log(); // Empty line between messages\n }\n });\n }\n } catch (error) {\n exitWithError(\n `Failed to read chat: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Register the chat read subcommand\n */\nexport function registerChatReadCommand(parent: Command): void {\n parent\n .command(\"read <chat>\")\n .description(\"Read messages from a group chat (NOT 'read' — that's for feeds)\")\n .option(\n \"--limit <n>\",\n \"Maximum number of messages to display\",\n (value) => parseInt(value, 10)\n )\n .option(\n \"--chain-id <id>\",\n \"Chain ID (default: 8453 for Base)\",\n (value) => parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--sender <address>\", \"Filter messages by sender address\")\n .option(\"--json\", \"Output in JSON format\")\n .action(async (chat, options) => {\n await executeChatRead(chat, options);\n });\n}\n","import { createWalletClient, http } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { getChainRpcUrls, getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\n\n/**\n * Create a wallet client from a private key\n */\nexport function createWallet(\n privateKey: `0x${string}`,\n chainId: number,\n rpcUrl?: string\n) {\n const account = privateKeyToAccount(privateKey);\n const rpcUrls = getChainRpcUrls({\n chainId,\n rpcUrl: rpcUrl,\n });\n\n return createWalletClient({\n account,\n transport: http(rpcUrls[0]),\n dataSuffix: getBaseDataSuffix(chainId),\n });\n}\n\n/**\n * Execute a transaction using a wallet client\n */\nexport async function executeTransaction(\n walletClient: ReturnType<typeof createWallet>,\n txConfig: WriteTransactionConfig\n): Promise<`0x${string}`> {\n const hash = await walletClient.writeContract({\n address: txConfig.to,\n abi: txConfig.abi,\n functionName: txConfig.functionName,\n args: txConfig.args,\n value: txConfig.value,\n chain: null,\n } as Parameters<typeof walletClient.writeContract>[0]);\n\n return hash;\n}\n","import { encodeFunctionData, concat } from \"viem\";\nimport { getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\nimport type { EncodedTransaction } from \"./types\";\n\nexport type { EncodedTransaction };\n\n/**\n * Encode a write transaction config into transaction data\n * Used for --encode-only mode where we output transaction data instead of executing\n */\nexport function encodeTransaction(\n config: WriteTransactionConfig,\n chainId: number\n): EncodedTransaction {\n const calldata = encodeFunctionData({\n abi: config.abi,\n functionName: config.functionName,\n args: config.args,\n });\n\n const suffix = getBaseDataSuffix(chainId);\n const data = suffix ? concat([calldata, suffix]) : calldata;\n\n return {\n to: config.to,\n data,\n chainId,\n value: config.value?.toString() ?? \"0\",\n };\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { parseReadOnlyOptionsWithDefault, parseCommonOptionsWithDefault } from \"../../cli/shared\";\nimport { createChatClient } from \"../../shared/client\";\nimport { createWallet, executeTransaction } from \"../../shared/wallet\";\nimport { encodeTransaction } from \"../../shared/encode\";\nimport { exitWithError } from \"../../shared/output\";\nimport { normalizeChatName } from \"./types\";\n\ninterface SendOptions {\n chainId?: number;\n rpcUrl?: string;\n privateKey?: string;\n encodeOnly?: boolean;\n data?: string;\n}\n\nconst MAX_MESSAGE_LENGTH = 4000;\n\n/**\n * Execute the chat send command\n */\nasync function executeChatSend(\n chat: string,\n message: string,\n options: SendOptions\n): Promise<void> {\n const normalizedChat = normalizeChatName(chat);\n\n if (message.length === 0) {\n exitWithError(\"Message cannot be empty\");\n }\n\n if (message.length > MAX_MESSAGE_LENGTH) {\n exitWithError(\n `Message too long (${message.length} chars). Maximum is ${MAX_MESSAGE_LENGTH} characters.`\n );\n }\n\n // For encode-only mode, we don't need a private key\n if (options.encodeOnly) {\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = createChatClient(readOnlyOptions);\n const txConfig = client.prepareSendChatMessage({\n topic: normalizedChat,\n text: message,\n data: options.data,\n });\n const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);\n\n console.log(JSON.stringify(encoded, null, 2));\n return;\n }\n\n // For actual execution, we need a private key\n const commonOptions = parseCommonOptionsWithDefault(\n {\n privateKey: options.privateKey,\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n },\n true // supports --encode-only\n );\n\n const client = createChatClient(commonOptions);\n const txConfig = client.prepareSendChatMessage({\n topic: normalizedChat,\n text: message,\n data: options.data,\n });\n\n const walletClient = createWallet(\n commonOptions.privateKey,\n commonOptions.chainId,\n commonOptions.rpcUrl\n );\n\n console.log(chalk.blue(`Sending message to chat \"${normalizedChat}\"...`));\n\n try {\n const hash = await executeTransaction(walletClient, txConfig);\n\n console.log(\n chalk.green(\n `Message sent successfully!\\n Transaction: ${hash}\\n Chat: ${normalizedChat}\\n Text: ${message}`\n )\n );\n } catch (error) {\n exitWithError(\n `Failed to send message: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Register the chat send subcommand\n */\nexport function registerChatSendCommand(parent: Command): void {\n parent\n .command(\"send <chat> <message>\")\n .description(\"Send a message to a group chat (NOT 'post' — that's for feeds)\")\n .option(\n \"--chain-id <id>\",\n \"Chain ID (default: 8453 for Base)\",\n (value) => parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--private-key <key>\", \"Private key (0x-prefixed)\")\n .option(\n \"--encode-only\",\n \"Output transaction data as JSON instead of executing\"\n )\n .option(\"--data <data>\", \"Optional data to attach to the message\")\n .action(async (chat, message, options) => {\n await executeChatSend(chat, message, options);\n });\n}\n","import { Command } from \"commander\";\nimport { registerChatReadCommand } from \"./read\";\nimport { registerChatSendCommand } from \"./send\";\n\n/**\n * Register the chat command group with the commander program\n */\nexport function registerChatCommand(program: Command): void {\n const chatCommand = program\n .command(\"chat\")\n .description(\"Group chat operations — use 'chat send' and 'chat read' (NOT 'post'/'read', which are for feeds)\");\n\n registerChatReadCommand(chatCommand);\n registerChatSendCommand(chatCommand);\n}\n\n// Re-export individual command registrations for botchan wrapper\nexport { registerChatReadCommand } from \"./read\";\nexport { registerChatSendCommand } from \"./send\";\n"]}
|