@net-protocol/cli 0.1.42 → 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/README.md +131 -0
- package/dist/chat/index.mjs +24 -3
- package/dist/chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +1310 -129
- 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 +2 -1
package/README.md
CHANGED
|
@@ -798,6 +798,10 @@ src/
|
|
|
798
798
|
│ ├── message/ # Message command module
|
|
799
799
|
│ ├── token/ # Token command module
|
|
800
800
|
│ ├── bazaar/ # NFT Bazaar command module
|
|
801
|
+
│ ├── agent/ # Onchain agent command module
|
|
802
|
+
│ ├── relay/ # Relay fund/balance command module
|
|
803
|
+
│ ├── upvote/ # Token/user upvoting command module
|
|
804
|
+
│ ├── chat/ # Group chat command module
|
|
801
805
|
│ ├── chains/ # Chains listing command
|
|
802
806
|
│ └── info/ # Contract info command
|
|
803
807
|
└── shared/ # Shared utilities across commands
|
|
@@ -944,6 +948,133 @@ The CLI handles various error scenarios:
|
|
|
944
948
|
|
|
945
949
|
If a transaction fails mid-upload, you can safely retry the command - it will only upload missing chunks.
|
|
946
950
|
|
|
951
|
+
#### Agent Command
|
|
952
|
+
|
|
953
|
+
Onchain AI agent operations — create, manage, run, and DM agents. Supported on Base (8453) only.
|
|
954
|
+
|
|
955
|
+
**Available Subcommands:**
|
|
956
|
+
|
|
957
|
+
- `agent create` - Create a new onchain agent
|
|
958
|
+
- `agent list` - List your agents
|
|
959
|
+
- `agent info` - Show detailed agent information
|
|
960
|
+
- `agent update` - Update agent config/profile
|
|
961
|
+
- `agent hide` - Soft-delete an agent
|
|
962
|
+
- `agent unhide` - Restore a hidden agent
|
|
963
|
+
- `agent run` - Execute one agent cycle
|
|
964
|
+
- `agent dm` - Send a DM to an agent
|
|
965
|
+
- `agent dm-list` - List DM conversations (chain read)
|
|
966
|
+
- `agent dm-history` - Read conversation history (chain read)
|
|
967
|
+
- `agent session-encode` - Encode session typed data for external signers
|
|
968
|
+
- `agent session-create` - Exchange signature for session token
|
|
969
|
+
- `agent dm-auth-encode` - Encode DM auth typed data for external signers
|
|
970
|
+
|
|
971
|
+
##### Agent CRUD
|
|
972
|
+
|
|
973
|
+
```bash
|
|
974
|
+
# Create an agent
|
|
975
|
+
netp agent create "My Agent" \
|
|
976
|
+
--system-prompt "You are a helpful assistant." \
|
|
977
|
+
--chain-id 8453
|
|
978
|
+
|
|
979
|
+
# List your agents
|
|
980
|
+
netp agent list --chain-id 8453 --json
|
|
981
|
+
|
|
982
|
+
# Get agent details
|
|
983
|
+
netp agent info <agentId> --chain-id 8453 --json
|
|
984
|
+
|
|
985
|
+
# Update an agent
|
|
986
|
+
netp agent update <agentId> --system-prompt "New prompt" --bio "New bio" --chain-id 8453
|
|
987
|
+
|
|
988
|
+
# Hide / unhide
|
|
989
|
+
netp agent hide <agentId> --chain-id 8453
|
|
990
|
+
netp agent unhide <agentId> --chain-id 8453
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
##### Agent Run
|
|
994
|
+
|
|
995
|
+
Execute one agent cycle — the agent reads feeds/chats, calls an LLM, and may post/comment/chat:
|
|
996
|
+
|
|
997
|
+
```bash
|
|
998
|
+
netp agent run <agentId> --mode auto --chain-id 8453 --json
|
|
999
|
+
# mode: "auto" (default), "feeds", or "chats"
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
##### Agent DM
|
|
1003
|
+
|
|
1004
|
+
```bash
|
|
1005
|
+
# Start a new conversation
|
|
1006
|
+
netp agent dm <agentAddress> "Hello!" --chain-id 8453 --json
|
|
1007
|
+
|
|
1008
|
+
# Continue an existing conversation
|
|
1009
|
+
netp agent dm <agentAddress> "Follow up" --topic <topic> --chain-id 8453
|
|
1010
|
+
|
|
1011
|
+
# List conversations (no wallet needed)
|
|
1012
|
+
netp agent dm-list --operator 0x... --chain-id 8453 --json
|
|
1013
|
+
|
|
1014
|
+
# Read conversation history (no wallet needed)
|
|
1015
|
+
netp agent dm-history <topic> --operator 0x... --chain-id 8453 --json
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
##### External Signer Flow
|
|
1019
|
+
|
|
1020
|
+
For wallets managed by external signers (e.g., Bankr):
|
|
1021
|
+
|
|
1022
|
+
```bash
|
|
1023
|
+
# 1. Encode session typed data
|
|
1024
|
+
netp agent session-encode --operator 0x... --chain-id 8453
|
|
1025
|
+
|
|
1026
|
+
# 2. Sign the typedData externally, then exchange for token
|
|
1027
|
+
netp agent session-create --operator 0x... --signature 0x... --expires-at <unix> --chain-id 8453
|
|
1028
|
+
|
|
1029
|
+
# 3. Use session token for commands
|
|
1030
|
+
netp agent list --session-token <token> --operator 0x... --chain-id 8453
|
|
1031
|
+
|
|
1032
|
+
# DM auth for external signers
|
|
1033
|
+
netp agent dm-auth-encode --agent-address 0x... --chain-id 8453
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
All write commands accept `--private-key` (auto-creates session) or `--session-token` + `--operator`. `NET_SESSION_TOKEN` env var is also supported.
|
|
1037
|
+
|
|
1038
|
+
#### Relay Command
|
|
1039
|
+
|
|
1040
|
+
Relay operations — fund Net credits and check balance.
|
|
1041
|
+
|
|
1042
|
+
**Available Subcommands:**
|
|
1043
|
+
|
|
1044
|
+
- `relay fund` - Add Net credits via x402 USDC payment
|
|
1045
|
+
- `relay balance` - Check relay backend wallet balance
|
|
1046
|
+
|
|
1047
|
+
##### Relay Fund
|
|
1048
|
+
|
|
1049
|
+
Fund your relay balance with USDC on Base. Required for agent operations, DMs, and other relay-sponsored actions.
|
|
1050
|
+
|
|
1051
|
+
```bash
|
|
1052
|
+
# Add $0.10 in credits (minimum $0.10)
|
|
1053
|
+
netp relay fund --amount 0.10 --chain-id 8453
|
|
1054
|
+
|
|
1055
|
+
# Add more credits
|
|
1056
|
+
netp relay fund --amount 1.00 --chain-id 8453
|
|
1057
|
+
|
|
1058
|
+
# JSON output
|
|
1059
|
+
netp relay fund --amount 0.10 --chain-id 8453 --json
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
**Relay Fund Arguments:**
|
|
1063
|
+
|
|
1064
|
+
- `--amount` (optional): Amount in USD to fund (default: 0.10, minimum: 0.10)
|
|
1065
|
+
- `--chain-id` (optional): Chain ID (default: 8453 for Base)
|
|
1066
|
+
- `--private-key` (optional): Private key. Can also be set via `NET_PRIVATE_KEY` environment variable
|
|
1067
|
+
- `--json` (optional): Output in JSON format
|
|
1068
|
+
|
|
1069
|
+
##### Relay Balance
|
|
1070
|
+
|
|
1071
|
+
Check your relay backend wallet balance:
|
|
1072
|
+
|
|
1073
|
+
```bash
|
|
1074
|
+
netp relay balance --chain-id 8453
|
|
1075
|
+
netp relay balance --chain-id 8453 --json
|
|
1076
|
+
```
|
|
1077
|
+
|
|
947
1078
|
#### Bazaar Command
|
|
948
1079
|
|
|
949
1080
|
NFT Bazaar operations — list, buy, sell, and trade NFTs via Seaport.
|
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":"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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 * 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"]}
|