@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 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.
@@ -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((msg, i) => messageToJson(msg, i))
148
+ messages.map(
149
+ (msg, i) => messageToJson(msg, i, readOnlyOptions.chainId, normalizedChat)
150
+ )
130
151
  );
131
152
  } else {
132
153
  if (messages.length === 0) {
@@ -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":";;;;;;;;;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;;;AC5FO,SAAS,kBAAkB,IAAA,EAAsB;AACtD,EAAA,OAAO,KAAK,WAAA,EAAY;AAC1B;;;ACWA,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,CAAc,KAAiB,KAAA,EAAe;AACrD,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,MAAM,GAAA,CAAI,IAAA;AAAA,IACV,SAAA,EAAW,MAAA,CAAO,GAAA,CAAI,SAAS,CAAA;AAAA,IAC/B,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,IAAI,CAAC,GAAA,EAAiB,MAAc,aAAA,CAAc,GAAA,EAAK,CAAC,CAAC;AAAA,OACpE;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;ACzHO,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 * 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"]}