@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/dist/cli/index.mjs
CHANGED
|
@@ -6,16 +6,16 @@ import { createRequire } from 'module';
|
|
|
6
6
|
import chalk4 from 'chalk';
|
|
7
7
|
import * as fs6 from 'fs';
|
|
8
8
|
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
9
|
-
import { OPTIMAL_CHUNK_SIZE, StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes,
|
|
10
|
-
import {
|
|
9
|
+
import { OPTIMAL_CHUNK_SIZE, StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, chunkDataForStorage, CHUNKED_STORAGE_CONTRACT, STORAGE_CONTRACT as STORAGE_CONTRACT$1, encodeStorageKeyForUrl } from '@net-protocol/storage';
|
|
10
|
+
import { createPublicClient, http, createWalletClient, stringToHex, hexToString, parseEther, encodeFunctionData, publicActions, concat, defineChain, decodeEventLog, formatEther, isAddress } from 'viem';
|
|
11
|
+
import { getSupportedChains, getNetContract, getChainName, getChainRpcUrls, getPublicClient, getBaseDataSuffix, NetClient, toBytes32, getChainBlockExplorer, NULL_ADDRESS, getChainSlug } from '@net-protocol/core';
|
|
11
12
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
12
|
-
import {
|
|
13
|
-
import { createRelayX402Client, createRelaySession, checkBackendWalletBalance, fundBackendWallet, batchTransactions, submitTransactionsViaRelay, waitForConfirmations, retryFailedTransactions as retryFailedTransactions$1 } from '@net-protocol/relay';
|
|
13
|
+
import { fundBackendWallet, checkBackendWalletBalance, createRelayX402Client, createRelaySession, batchTransactions, submitTransactionsViaRelay, waitForConfirmations, retryFailedTransactions as retryFailedTransactions$1 } from '@net-protocol/relay';
|
|
14
14
|
import { isCommentTopic, parseCommentData, FeedRegistryClient, FeedClient, AgentRegistryClient, COMMENT_TOPIC_SUFFIX, FEED_TOPIC_PREFIX } from '@net-protocol/feeds';
|
|
15
15
|
import '@net-protocol/chats';
|
|
16
16
|
import { isNetrSupportedChain, NetrClient } from '@net-protocol/netr';
|
|
17
17
|
import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, PROFILE_CANVAS_STORAGE_KEY, PROFILE_CSS_STORAGE_KEY, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getProfileMetadataStorageArgs, isValidBio, isValidDisplayName, isValidTokenAddress, DEMO_THEMES, MAX_CSS_SIZE, isValidCSS, getProfileCSSStorageArgs, buildCSSPrompt } from '@net-protocol/profiles';
|
|
18
|
-
import { base } from 'viem/chains';
|
|
18
|
+
import { baseSepolia, base } from 'viem/chains';
|
|
19
19
|
import * as path from 'path';
|
|
20
20
|
import { join } from 'path';
|
|
21
21
|
import { BazaarClient } from '@net-protocol/bazaar';
|
|
@@ -23,6 +23,7 @@ import * as os from 'os';
|
|
|
23
23
|
import { homedir } from 'os';
|
|
24
24
|
import * as readline from 'readline';
|
|
25
25
|
import { discoverTokenPool, PURE_ALPHA_STRATEGY, UNIV234_POOLS_STRATEGY, encodePoolKey, DYNAMIC_SPLIT_STRATEGY, getTokenScoreKey, UPVOTE_PRICE_ETH, UPVOTE_APP, ScoreClient, ALL_STRATEGY_ADDRESSES, NULL_ADDRESS as NULL_ADDRESS$1, UserUpvoteClient, calculateUpvoteCost, USER_UPVOTE_CONTRACT } from '@net-protocol/score';
|
|
26
|
+
import { RELAY_ACCESS_KEY, generateAgentChatTopic, AgentClient, isAgentChatTopic, parseAgentAddressFromTopic, buildSessionTypedData, NET_API_URL, exchangeSessionSignature, buildConversationAuthTypedData, NET_TESTNET_API_URL } from '@net-protocol/agents';
|
|
26
27
|
|
|
27
28
|
var DEFAULT_CHAIN_ID = 8453;
|
|
28
29
|
function getRequiredChainId(optionValue) {
|
|
@@ -129,6 +130,75 @@ function parseCommonOptionsWithDefault(options, supportsEncodeOnly = false) {
|
|
|
129
130
|
rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl)
|
|
130
131
|
};
|
|
131
132
|
}
|
|
133
|
+
var WEBSITE_BASE = "https://netprotocol.app";
|
|
134
|
+
var STORAGE_BASE = "https://storedon.net";
|
|
135
|
+
function chainSlug(chainId) {
|
|
136
|
+
return getChainSlug({ chainId }) ?? null;
|
|
137
|
+
}
|
|
138
|
+
function stripFeedPrefix(topic) {
|
|
139
|
+
const lower = topic.toLowerCase();
|
|
140
|
+
return lower.startsWith("feed-") ? lower.slice(5) : lower;
|
|
141
|
+
}
|
|
142
|
+
function feedUrl(chainId, feedName) {
|
|
143
|
+
const slug = chainSlug(chainId);
|
|
144
|
+
if (!slug) return null;
|
|
145
|
+
return `${WEBSITE_BASE}/app/feed/${slug}/${stripFeedPrefix(feedName)}`;
|
|
146
|
+
}
|
|
147
|
+
function walletUrl(chainId, address) {
|
|
148
|
+
const slug = chainSlug(chainId);
|
|
149
|
+
if (!slug) return null;
|
|
150
|
+
return `${WEBSITE_BASE}/app/feed/${slug}/${address.toLowerCase()}`;
|
|
151
|
+
}
|
|
152
|
+
function profileUrl(chainId, address) {
|
|
153
|
+
const slug = chainSlug(chainId);
|
|
154
|
+
if (!slug) return null;
|
|
155
|
+
return `${WEBSITE_BASE}/app/profile/${slug}/${address.toLowerCase()}`;
|
|
156
|
+
}
|
|
157
|
+
function tokenUrl(chainId, tokenAddress) {
|
|
158
|
+
const slug = chainSlug(chainId);
|
|
159
|
+
if (!slug) return null;
|
|
160
|
+
return `${WEBSITE_BASE}/app/token/${slug}/${tokenAddress.toLowerCase()}`;
|
|
161
|
+
}
|
|
162
|
+
function storageUrl(chainId, operatorAddress, key) {
|
|
163
|
+
return `${STORAGE_BASE}/net/${chainId}/storage/load/${operatorAddress.toLowerCase()}/${encodeStorageKeyForUrl(
|
|
164
|
+
key
|
|
165
|
+
)}`;
|
|
166
|
+
}
|
|
167
|
+
function explorerTxUrl(chainId, txHash) {
|
|
168
|
+
const base9 = getChainBlockExplorer({ chainId })?.url;
|
|
169
|
+
if (!base9) return null;
|
|
170
|
+
return `${base9}/tx/${txHash}`;
|
|
171
|
+
}
|
|
172
|
+
function explorerAddressUrl(chainId, address) {
|
|
173
|
+
const base9 = getChainBlockExplorer({ chainId })?.url;
|
|
174
|
+
if (!base9) return null;
|
|
175
|
+
return `${base9}/address/${address}`;
|
|
176
|
+
}
|
|
177
|
+
function postIdToCommentParam(postId) {
|
|
178
|
+
const colon = postId.indexOf(":");
|
|
179
|
+
if (colon === -1) return postId;
|
|
180
|
+
return `${postId.slice(0, colon)}-${postId.slice(colon + 1)}`;
|
|
181
|
+
}
|
|
182
|
+
function postPermalink(chainId, opts) {
|
|
183
|
+
const slug = chainSlug(chainId);
|
|
184
|
+
if (!slug) return null;
|
|
185
|
+
const params = new URLSearchParams();
|
|
186
|
+
if (opts.globalIndex != null) {
|
|
187
|
+
params.set("index", String(opts.globalIndex));
|
|
188
|
+
} else if (opts.topic != null && opts.topicIndex != null) {
|
|
189
|
+
params.set("topic", stripFeedPrefix(opts.topic));
|
|
190
|
+
params.set("index", String(opts.topicIndex));
|
|
191
|
+
} else if (opts.user != null && opts.userIndex != null) {
|
|
192
|
+
params.set("user", opts.user.toLowerCase());
|
|
193
|
+
params.set("index", String(opts.userIndex));
|
|
194
|
+
} else {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
if (opts.commentId) {
|
|
198
|
+
params.set("commentId", postIdToCommentParam(opts.commentId));
|
|
199
|
+
}
|
|
200
|
+
return `${WEBSITE_BASE}/app/feed/${slug}/post?${params.toString()}`;
|
|
201
|
+
}
|
|
132
202
|
async function checkNormalStorageExists(params) {
|
|
133
203
|
const { storageClient, storageKey, operatorAddress, expectedContent } = params;
|
|
134
204
|
const existing = await storageClient.get({
|
|
@@ -212,7 +282,7 @@ function extractTypedArgsFromTransaction(tx, type) {
|
|
|
212
282
|
}
|
|
213
283
|
function generateStorageUrl(operatorAddress, chainId, storageKey) {
|
|
214
284
|
if (!operatorAddress) return void 0;
|
|
215
|
-
return
|
|
285
|
+
return storageUrl(chainId, operatorAddress, storageKey);
|
|
216
286
|
}
|
|
217
287
|
async function checkTransactionExists(params) {
|
|
218
288
|
const { storageClient, tx, operatorAddress } = params;
|
|
@@ -1181,6 +1251,11 @@ async function executeStorageRead(options) {
|
|
|
1181
1251
|
key: options.key,
|
|
1182
1252
|
operator: options.operator,
|
|
1183
1253
|
chainId: readOnlyOptions.chainId,
|
|
1254
|
+
storageUrl: storageUrl(
|
|
1255
|
+
readOnlyOptions.chainId,
|
|
1256
|
+
options.operator,
|
|
1257
|
+
options.key
|
|
1258
|
+
),
|
|
1184
1259
|
text: result.text,
|
|
1185
1260
|
data: options.raw ? result.data : result.data,
|
|
1186
1261
|
isXml: result.isXml,
|
|
@@ -1373,7 +1448,7 @@ function registerStorageCommand(program2) {
|
|
|
1373
1448
|
try {
|
|
1374
1449
|
console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
|
|
1375
1450
|
const result = await uploadFile(uploadOptions);
|
|
1376
|
-
const
|
|
1451
|
+
const storageUrl2 = generateStorageUrl(
|
|
1377
1452
|
result.operatorAddress,
|
|
1378
1453
|
commonOptions.chainId,
|
|
1379
1454
|
options.key
|
|
@@ -1383,8 +1458,8 @@ function registerStorageCommand(program2) {
|
|
|
1383
1458
|
chalk4.green(
|
|
1384
1459
|
`\u2713 All data already stored - skipping upload
|
|
1385
1460
|
Storage Key: ${options.key}
|
|
1386
|
-
Skipped: ${result.transactionsSkipped} transaction(s)${
|
|
1387
|
-
Storage URL: ${chalk4.cyan(
|
|
1461
|
+
Skipped: ${result.transactionsSkipped} transaction(s)${storageUrl2 ? `
|
|
1462
|
+
Storage URL: ${chalk4.cyan(storageUrl2)}` : ""}`
|
|
1388
1463
|
)
|
|
1389
1464
|
);
|
|
1390
1465
|
process.exit(0);
|
|
@@ -1397,8 +1472,8 @@ function registerStorageCommand(program2) {
|
|
|
1397
1472
|
Storage Type: ${result.storageType === "xml" ? "XML" : "Normal"}
|
|
1398
1473
|
Transactions Sent: ${result.transactionsSent}
|
|
1399
1474
|
Transactions Skipped: ${result.transactionsSkipped}
|
|
1400
|
-
Final Transaction Hash: ${result.finalHash || "N/A"}${
|
|
1401
|
-
Storage URL: ${chalk4.cyan(
|
|
1475
|
+
Final Transaction Hash: ${result.finalHash || "N/A"}${storageUrl2 ? `
|
|
1476
|
+
Storage URL: ${chalk4.cyan(storageUrl2)}` : ""}`
|
|
1402
1477
|
)
|
|
1403
1478
|
);
|
|
1404
1479
|
process.exit(0);
|
|
@@ -1455,7 +1530,7 @@ function registerStorageCommand(program2) {
|
|
|
1455
1530
|
try {
|
|
1456
1531
|
console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
|
|
1457
1532
|
const result = await previewFile(previewOptions);
|
|
1458
|
-
const
|
|
1533
|
+
const storageUrl2 = generateStorageUrl(
|
|
1459
1534
|
result.operatorAddress,
|
|
1460
1535
|
commonOptions.chainId,
|
|
1461
1536
|
options.key
|
|
@@ -1491,8 +1566,8 @@ function registerStorageCommand(program2) {
|
|
|
1491
1566
|
console.log(
|
|
1492
1567
|
` Operator Address: ${chalk4.gray(result.operatorAddress)}`
|
|
1493
1568
|
);
|
|
1494
|
-
if (
|
|
1495
|
-
console.log(` Storage URL: ${chalk4.cyan(
|
|
1569
|
+
if (storageUrl2) {
|
|
1570
|
+
console.log(` Storage URL: ${chalk4.cyan(storageUrl2)}`);
|
|
1496
1571
|
}
|
|
1497
1572
|
if (result.needToStoreChunks === 0 && !result.metadataNeedsStorage) {
|
|
1498
1573
|
console.log(
|
|
@@ -1566,9 +1641,9 @@ function registerStorageCommand(program2) {
|
|
|
1566
1641
|
console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
|
|
1567
1642
|
console.log(chalk4.blue(`\u{1F517} Using relay API: ${options.apiUrl}`));
|
|
1568
1643
|
const result = await uploadFileWithRelay(uploadRelayOptions);
|
|
1569
|
-
const { privateKeyToAccount:
|
|
1570
|
-
const userAccount =
|
|
1571
|
-
const
|
|
1644
|
+
const { privateKeyToAccount: privateKeyToAccount28 } = await import('viem/accounts');
|
|
1645
|
+
const userAccount = privateKeyToAccount28(commonOptions.privateKey);
|
|
1646
|
+
const storageUrl2 = generateStorageUrl(
|
|
1572
1647
|
userAccount.address,
|
|
1573
1648
|
commonOptions.chainId,
|
|
1574
1649
|
options.key
|
|
@@ -1584,8 +1659,8 @@ function registerStorageCommand(program2) {
|
|
|
1584
1659
|
Metadata Submitted: ${result.metadataSubmitted ? "Yes" : "No (already exists)"}
|
|
1585
1660
|
Backend Wallet: ${result.backendWalletAddress}
|
|
1586
1661
|
Chunk Transaction Hashes: ${result.chunkTransactionHashes.length > 0 ? result.chunkTransactionHashes.join(", ") : "None"}${result.metadataTransactionHash ? `
|
|
1587
|
-
Metadata Transaction Hash: ${result.metadataTransactionHash}` : ""}${
|
|
1588
|
-
Storage URL: ${chalk4.cyan(
|
|
1662
|
+
Metadata Transaction Hash: ${result.metadataTransactionHash}` : ""}${storageUrl2 ? `
|
|
1663
|
+
Storage URL: ${chalk4.cyan(storageUrl2)}` : ""}`
|
|
1589
1664
|
)
|
|
1590
1665
|
);
|
|
1591
1666
|
process.exit(0);
|
|
@@ -1934,33 +2009,25 @@ function registerMessageCommand(program2) {
|
|
|
1934
2009
|
messageCommand.addCommand(readCommand);
|
|
1935
2010
|
messageCommand.addCommand(countCommand);
|
|
1936
2011
|
}
|
|
1937
|
-
var SUPPORTED_CHAINS = [
|
|
1938
|
-
{ id: 8453, name: "Base", type: "mainnet" },
|
|
1939
|
-
{ id: 1, name: "Ethereum", type: "mainnet" },
|
|
1940
|
-
{ id: 666666666, name: "Degen", type: "mainnet" },
|
|
1941
|
-
{ id: 5112, name: "Ham", type: "mainnet" },
|
|
1942
|
-
{ id: 57073, name: "Ink", type: "mainnet" },
|
|
1943
|
-
{ id: 130, name: "Unichain", type: "mainnet" },
|
|
1944
|
-
{ id: 999, name: "HyperEVM", type: "mainnet" },
|
|
1945
|
-
{ id: 9745, name: "Plasma", type: "mainnet" },
|
|
1946
|
-
{ id: 143, name: "Monad", type: "mainnet" },
|
|
1947
|
-
{ id: 84532, name: "Base Sepolia", type: "testnet" },
|
|
1948
|
-
{ id: 11155111, name: "Sepolia", type: "testnet" }
|
|
1949
|
-
];
|
|
1950
2012
|
function registerChainsCommand(program2) {
|
|
1951
2013
|
program2.command("chains").description("List supported chains").option("--json", "Output in JSON format").action((options) => {
|
|
2014
|
+
const chains = getSupportedChains();
|
|
1952
2015
|
if (options.json) {
|
|
1953
|
-
console.log(JSON.stringify(
|
|
2016
|
+
console.log(JSON.stringify(chains, null, 2));
|
|
1954
2017
|
return;
|
|
1955
2018
|
}
|
|
1956
2019
|
console.log(chalk4.white.bold("Supported Chains:\n"));
|
|
1957
2020
|
console.log(chalk4.cyan("Mainnets:"));
|
|
1958
|
-
|
|
1959
|
-
console.log(
|
|
2021
|
+
chains.filter((c) => c.type === "mainnet").forEach((chain) => {
|
|
2022
|
+
console.log(
|
|
2023
|
+
` ${chalk4.white(chain.name)} ${chalk4.gray(`(${chain.chainId})`)}`
|
|
2024
|
+
);
|
|
1960
2025
|
});
|
|
1961
2026
|
console.log(chalk4.cyan("\nTestnets:"));
|
|
1962
|
-
|
|
1963
|
-
console.log(
|
|
2027
|
+
chains.filter((c) => c.type === "testnet").forEach((chain) => {
|
|
2028
|
+
console.log(
|
|
2029
|
+
` ${chalk4.white(chain.name)} ${chalk4.gray(`(${chain.chainId})`)}`
|
|
2030
|
+
);
|
|
1964
2031
|
});
|
|
1965
2032
|
});
|
|
1966
2033
|
}
|
|
@@ -2224,10 +2291,19 @@ async function executeTokenInfo(options) {
|
|
|
2224
2291
|
const output = {
|
|
2225
2292
|
address: tokenAddress,
|
|
2226
2293
|
chainId: readOnlyOptions.chainId,
|
|
2294
|
+
tokenUrl: tokenUrl(readOnlyOptions.chainId, tokenAddress),
|
|
2295
|
+
explorerAddressUrl: explorerAddressUrl(
|
|
2296
|
+
readOnlyOptions.chainId,
|
|
2297
|
+
tokenAddress
|
|
2298
|
+
),
|
|
2227
2299
|
token: {
|
|
2228
2300
|
name: token.name,
|
|
2229
2301
|
symbol: token.symbol,
|
|
2230
2302
|
deployer: token.deployer,
|
|
2303
|
+
deployerProfileUrl: profileUrl(
|
|
2304
|
+
readOnlyOptions.chainId,
|
|
2305
|
+
token.deployer
|
|
2306
|
+
),
|
|
2231
2307
|
image: token.image,
|
|
2232
2308
|
animation: token.animation || null,
|
|
2233
2309
|
fid: token.fid.toString(),
|
|
@@ -2426,6 +2502,8 @@ async function executeProfileGet(options) {
|
|
|
2426
2502
|
const output = {
|
|
2427
2503
|
address: options.address,
|
|
2428
2504
|
chainId: readOnlyOptions.chainId,
|
|
2505
|
+
profileUrl: profileUrl(readOnlyOptions.chainId, options.address),
|
|
2506
|
+
walletUrl: walletUrl(readOnlyOptions.chainId, options.address),
|
|
2429
2507
|
profilePicture: profilePicture || null,
|
|
2430
2508
|
displayName: displayName || null,
|
|
2431
2509
|
xUsername: xUsername || null,
|
|
@@ -5678,38 +5756,70 @@ function formatComment(comment, depth) {
|
|
|
5678
5756
|
];
|
|
5679
5757
|
return lines.join("\n");
|
|
5680
5758
|
}
|
|
5681
|
-
function
|
|
5759
|
+
function stripFeedPrefix2(topic) {
|
|
5760
|
+
const match = topic.match(/^(.+?):comments:/);
|
|
5761
|
+
const base9 = match ? match[1] : topic;
|
|
5762
|
+
return base9.replace(/^feed-/i, "");
|
|
5763
|
+
}
|
|
5764
|
+
function postToJson(post, options) {
|
|
5765
|
+
const { chainId, topicIndex, userIndex, globalIndex, commentCount } = options;
|
|
5766
|
+
const feedName = stripFeedPrefix2(post.topic);
|
|
5767
|
+
const postId = `${post.sender}:${post.timestamp}`;
|
|
5768
|
+
const permalink = postPermalink(chainId, {
|
|
5769
|
+
globalIndex,
|
|
5770
|
+
topic: post.topic,
|
|
5771
|
+
topicIndex,
|
|
5772
|
+
user: userIndex !== void 0 ? post.sender : void 0,
|
|
5773
|
+
userIndex
|
|
5774
|
+
});
|
|
5682
5775
|
const result = {
|
|
5683
|
-
|
|
5776
|
+
postId,
|
|
5777
|
+
permalink,
|
|
5684
5778
|
sender: post.sender,
|
|
5779
|
+
senderProfileUrl: profileUrl(chainId, post.sender),
|
|
5780
|
+
senderWalletUrl: walletUrl(chainId, post.sender),
|
|
5685
5781
|
text: post.text,
|
|
5686
5782
|
timestamp: Number(post.timestamp),
|
|
5783
|
+
feed: feedName,
|
|
5784
|
+
feedUrl: feedUrl(chainId, feedName),
|
|
5687
5785
|
topic: post.topic
|
|
5688
5786
|
};
|
|
5689
|
-
if (
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
if (
|
|
5693
|
-
|
|
5694
|
-
}
|
|
5787
|
+
if (topicIndex !== void 0) result.topicIndex = topicIndex;
|
|
5788
|
+
if (userIndex !== void 0) result.userIndex = userIndex;
|
|
5789
|
+
if (globalIndex !== void 0) result.globalIndex = globalIndex;
|
|
5790
|
+
if (commentCount !== void 0) result.commentCount = commentCount;
|
|
5791
|
+
if (post.data && post.data !== "0x") result.data = post.data;
|
|
5695
5792
|
return result;
|
|
5696
5793
|
}
|
|
5697
|
-
function feedToJson(feed, index) {
|
|
5698
|
-
|
|
5794
|
+
function feedToJson(feed, index, chainId) {
|
|
5795
|
+
const result = {
|
|
5699
5796
|
index,
|
|
5700
5797
|
feedName: feed.feedName,
|
|
5701
5798
|
registrant: feed.registrant,
|
|
5702
5799
|
timestamp: feed.timestamp
|
|
5703
5800
|
};
|
|
5801
|
+
if (chainId !== void 0) {
|
|
5802
|
+
result.feedUrl = feedUrl(chainId, feed.feedName);
|
|
5803
|
+
}
|
|
5804
|
+
return result;
|
|
5704
5805
|
}
|
|
5705
|
-
function commentToJson(comment,
|
|
5706
|
-
|
|
5806
|
+
function commentToJson(comment, options) {
|
|
5807
|
+
const { chainId, depth, parentPostUrl } = options;
|
|
5808
|
+
const commentParam = `${comment.sender}-${comment.timestamp}`;
|
|
5809
|
+
const permalink = parentPostUrl ? `${parentPostUrl}${parentPostUrl.includes("?") ? "&" : "?"}commentId=${commentParam}` : null;
|
|
5810
|
+
const result = {
|
|
5811
|
+
commentId: `${comment.sender}:${comment.timestamp}`,
|
|
5812
|
+
permalink,
|
|
5707
5813
|
sender: comment.sender,
|
|
5814
|
+
senderProfileUrl: profileUrl(chainId, comment.sender),
|
|
5708
5815
|
text: comment.text,
|
|
5709
5816
|
timestamp: Number(comment.timestamp),
|
|
5710
|
-
depth
|
|
5711
|
-
data: comment.data !== "0x" ? comment.data : void 0
|
|
5817
|
+
depth
|
|
5712
5818
|
};
|
|
5819
|
+
if (comment.data !== "0x") {
|
|
5820
|
+
result.data = comment.data;
|
|
5821
|
+
}
|
|
5822
|
+
return result;
|
|
5713
5823
|
}
|
|
5714
5824
|
function formatAgent(agent, index) {
|
|
5715
5825
|
const timestamp = formatTimestamp(agent.timestamp);
|
|
@@ -5719,12 +5829,17 @@ function formatAgent(agent, index) {
|
|
|
5719
5829
|
];
|
|
5720
5830
|
return lines.join("\n");
|
|
5721
5831
|
}
|
|
5722
|
-
function agentToJson(agent, index) {
|
|
5723
|
-
|
|
5832
|
+
function agentToJson(agent, index, chainId) {
|
|
5833
|
+
const result = {
|
|
5724
5834
|
index,
|
|
5725
5835
|
address: agent.address,
|
|
5726
5836
|
timestamp: agent.timestamp
|
|
5727
5837
|
};
|
|
5838
|
+
if (chainId !== void 0) {
|
|
5839
|
+
result.profileUrl = profileUrl(chainId, agent.address);
|
|
5840
|
+
result.walletUrl = walletUrl(chainId, agent.address);
|
|
5841
|
+
}
|
|
5842
|
+
return result;
|
|
5728
5843
|
}
|
|
5729
5844
|
function printJson(data) {
|
|
5730
5845
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -5742,7 +5857,11 @@ async function executeFeedList(options) {
|
|
|
5742
5857
|
maxFeeds: options.limit ?? 50
|
|
5743
5858
|
});
|
|
5744
5859
|
if (options.json) {
|
|
5745
|
-
printJson(
|
|
5860
|
+
printJson(
|
|
5861
|
+
feeds.map(
|
|
5862
|
+
(feed, i) => feedToJson(feed, i, readOnlyOptions.chainId)
|
|
5863
|
+
)
|
|
5864
|
+
);
|
|
5746
5865
|
} else {
|
|
5747
5866
|
if (feeds.length === 0) {
|
|
5748
5867
|
console.log(chalk4.yellow("No registered feeds found"));
|
|
@@ -5968,26 +6087,31 @@ async function executeFeedRead(feed, options) {
|
|
|
5968
6087
|
return;
|
|
5969
6088
|
}
|
|
5970
6089
|
const fetchLimit = options.sender ? Math.max(limit * 5, 100) : limit;
|
|
5971
|
-
|
|
6090
|
+
const fetched = await client.getFeedPostsWithIndex({
|
|
5972
6091
|
topic: normalizedFeed,
|
|
5973
6092
|
maxPosts: fetchLimit
|
|
5974
6093
|
});
|
|
6094
|
+
let postsWithIndex = fetched.messages.map((post, i) => ({
|
|
6095
|
+
post,
|
|
6096
|
+
topicIndex: fetched.startIndex + i
|
|
6097
|
+
}));
|
|
5975
6098
|
if (options.sender) {
|
|
5976
6099
|
const senderLower = options.sender.toLowerCase();
|
|
5977
|
-
|
|
5978
|
-
(post) => post.sender.toLowerCase() === senderLower
|
|
6100
|
+
postsWithIndex = postsWithIndex.filter(
|
|
6101
|
+
({ post }) => post.sender.toLowerCase() === senderLower
|
|
5979
6102
|
);
|
|
5980
|
-
|
|
6103
|
+
postsWithIndex = postsWithIndex.slice(0, limit);
|
|
5981
6104
|
}
|
|
5982
6105
|
if (options.unseen) {
|
|
5983
6106
|
const lastSeen = getLastSeenTimestamp(normalizedFeed);
|
|
5984
6107
|
const myAddress = getMyAddress();
|
|
5985
|
-
|
|
6108
|
+
postsWithIndex = postsWithIndex.filter(({ post }) => {
|
|
5986
6109
|
const isNew = lastSeen === null || Number(post.timestamp) > lastSeen;
|
|
5987
6110
|
const isFromOther = !myAddress || post.sender.toLowerCase() !== myAddress;
|
|
5988
6111
|
return isNew && isFromOther;
|
|
5989
6112
|
});
|
|
5990
6113
|
}
|
|
6114
|
+
const posts = postsWithIndex.map(({ post }) => post);
|
|
5991
6115
|
if (options.markSeen) {
|
|
5992
6116
|
const allPosts = await client.getFeedPosts({
|
|
5993
6117
|
topic: normalizedFeed,
|
|
@@ -6003,7 +6127,13 @@ async function executeFeedRead(feed, options) {
|
|
|
6003
6127
|
);
|
|
6004
6128
|
if (options.json) {
|
|
6005
6129
|
printJson(
|
|
6006
|
-
|
|
6130
|
+
postsWithIndex.map(
|
|
6131
|
+
({ post, topicIndex }, i) => postToJson(post, {
|
|
6132
|
+
chainId: readOnlyOptions.chainId,
|
|
6133
|
+
topicIndex,
|
|
6134
|
+
commentCount: commentCounts[i]
|
|
6135
|
+
})
|
|
6136
|
+
)
|
|
6007
6137
|
);
|
|
6008
6138
|
} else {
|
|
6009
6139
|
if (posts.length === 0) {
|
|
@@ -6065,6 +6195,34 @@ async function executeTransaction(walletClient, txConfig) {
|
|
|
6065
6195
|
});
|
|
6066
6196
|
return hash;
|
|
6067
6197
|
}
|
|
6198
|
+
async function getMessageIndicesFromTx(params) {
|
|
6199
|
+
const publicClient = getPublicClient({
|
|
6200
|
+
chainId: params.chainId,
|
|
6201
|
+
rpcUrl: params.rpcUrl
|
|
6202
|
+
});
|
|
6203
|
+
const netContract = getNetContract(params.chainId);
|
|
6204
|
+
const contractAddress = netContract.address.toLowerCase();
|
|
6205
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
6206
|
+
hash: params.txHash
|
|
6207
|
+
});
|
|
6208
|
+
const indices = [];
|
|
6209
|
+
for (const log of receipt.logs) {
|
|
6210
|
+
if (log.address.toLowerCase() !== contractAddress) continue;
|
|
6211
|
+
try {
|
|
6212
|
+
const decoded = decodeEventLog({
|
|
6213
|
+
abi: netContract.abi,
|
|
6214
|
+
data: log.data,
|
|
6215
|
+
topics: log.topics
|
|
6216
|
+
});
|
|
6217
|
+
if (decoded.eventName === "MessageSent") {
|
|
6218
|
+
const args = decoded.args;
|
|
6219
|
+
indices.push(Number(args.messageIndex));
|
|
6220
|
+
}
|
|
6221
|
+
} catch {
|
|
6222
|
+
}
|
|
6223
|
+
}
|
|
6224
|
+
return indices;
|
|
6225
|
+
}
|
|
6068
6226
|
|
|
6069
6227
|
// src/commands/feed/post.ts
|
|
6070
6228
|
var MAX_MESSAGE_LENGTH = 4e3;
|
|
@@ -6116,11 +6274,23 @@ ${options.body}` : message;
|
|
|
6116
6274
|
commonOptions.chainId,
|
|
6117
6275
|
commonOptions.rpcUrl
|
|
6118
6276
|
);
|
|
6119
|
-
|
|
6277
|
+
if (!options.json) {
|
|
6278
|
+
console.log(chalk4.blue(`Posting to feed "${normalizedFeed}"...`));
|
|
6279
|
+
}
|
|
6120
6280
|
try {
|
|
6121
6281
|
const hash = await executeTransaction(walletClient, txConfig);
|
|
6122
6282
|
const senderAddress = walletClient.account.address;
|
|
6123
6283
|
let postId;
|
|
6284
|
+
let globalIndex;
|
|
6285
|
+
try {
|
|
6286
|
+
const indices = await getMessageIndicesFromTx({
|
|
6287
|
+
chainId: commonOptions.chainId,
|
|
6288
|
+
rpcUrl: commonOptions.rpcUrl,
|
|
6289
|
+
txHash: hash
|
|
6290
|
+
});
|
|
6291
|
+
globalIndex = indices[0];
|
|
6292
|
+
} catch {
|
|
6293
|
+
}
|
|
6124
6294
|
try {
|
|
6125
6295
|
const posts = await client.getFeedPosts({
|
|
6126
6296
|
topic: normalizedFeed,
|
|
@@ -6142,19 +6312,36 @@ ${options.body}` : message;
|
|
|
6142
6312
|
sender: senderAddress,
|
|
6143
6313
|
text: fullMessage,
|
|
6144
6314
|
postId
|
|
6145
|
-
// Now we have the actual post ID for checking comments later
|
|
6146
6315
|
});
|
|
6316
|
+
const permalink = postPermalink(commonOptions.chainId, {
|
|
6317
|
+
globalIndex
|
|
6318
|
+
});
|
|
6319
|
+
if (options.json) {
|
|
6320
|
+
printJson({
|
|
6321
|
+
success: true,
|
|
6322
|
+
txHash: hash,
|
|
6323
|
+
explorerTxUrl: explorerTxUrl(commonOptions.chainId, hash),
|
|
6324
|
+
postId,
|
|
6325
|
+
globalIndex,
|
|
6326
|
+
permalink,
|
|
6327
|
+
feed: normalizedFeed,
|
|
6328
|
+
feedUrl: feedUrl(commonOptions.chainId, normalizedFeed),
|
|
6329
|
+
sender: senderAddress,
|
|
6330
|
+
senderProfileUrl: profileUrl(commonOptions.chainId, senderAddress),
|
|
6331
|
+
text: fullMessage
|
|
6332
|
+
});
|
|
6333
|
+
return;
|
|
6334
|
+
}
|
|
6147
6335
|
const displayText = options.body ? `${message} (+ body)` : message;
|
|
6148
|
-
const
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
Text: ${displayText}`
|
|
6156
|
-
|
|
6157
|
-
);
|
|
6336
|
+
const lines = [
|
|
6337
|
+
`Message posted successfully!`,
|
|
6338
|
+
` Transaction: ${hash}`,
|
|
6339
|
+
` Feed: ${normalizedFeed}`
|
|
6340
|
+
];
|
|
6341
|
+
if (postId) lines.push(` Post ID: ${postId}`);
|
|
6342
|
+
if (permalink) lines.push(` Permalink: ${permalink}`);
|
|
6343
|
+
lines.push(` Text: ${displayText}`);
|
|
6344
|
+
console.log(chalk4.green(lines.join("\n")));
|
|
6158
6345
|
} catch (error) {
|
|
6159
6346
|
exitWithError(
|
|
6160
6347
|
`Failed to post message: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -6169,7 +6356,10 @@ function registerFeedPostCommand(parent) {
|
|
|
6169
6356
|
).option("--rpc-url <url>", "Custom RPC URL").option("--private-key <key>", "Private key (0x-prefixed)").option(
|
|
6170
6357
|
"--encode-only",
|
|
6171
6358
|
"Output transaction data as JSON instead of executing"
|
|
6172
|
-
).option("--data <data>", "Optional data to attach to the post").option("--body <text>", "Post body (message becomes the title)").
|
|
6359
|
+
).option("--data <data>", "Optional data to attach to the post").option("--body <text>", "Post body (message becomes the title)").option(
|
|
6360
|
+
"--json",
|
|
6361
|
+
"Output structured JSON (includes permalink and other URLs) after submission"
|
|
6362
|
+
).action(async (feed, message, options) => {
|
|
6173
6363
|
await executeFeedPost(feed, message, options);
|
|
6174
6364
|
});
|
|
6175
6365
|
}
|
|
@@ -6198,11 +6388,6 @@ function parsePostId(postId) {
|
|
|
6198
6388
|
timestamp
|
|
6199
6389
|
};
|
|
6200
6390
|
}
|
|
6201
|
-
function findPostByParsedId(posts, parsedId) {
|
|
6202
|
-
return posts.find(
|
|
6203
|
-
(p) => p.sender.toLowerCase() === parsedId.sender.toLowerCase() && p.timestamp === parsedId.timestamp
|
|
6204
|
-
);
|
|
6205
|
-
}
|
|
6206
6391
|
|
|
6207
6392
|
// src/commands/feed/comment-write.ts
|
|
6208
6393
|
var MAX_MESSAGE_LENGTH2 = 4e3;
|
|
@@ -6235,17 +6420,20 @@ async function executeFeedCommentWrite(feed, postId, message, options) {
|
|
|
6235
6420
|
`Feed "${normalizedFeed}" has no posts. Cannot find post ${postId}.`
|
|
6236
6421
|
);
|
|
6237
6422
|
}
|
|
6238
|
-
const
|
|
6423
|
+
const fetched = await client.getFeedPostsWithIndex({
|
|
6239
6424
|
topic: normalizedFeed,
|
|
6240
6425
|
maxPosts: 100
|
|
6241
|
-
// Fetch enough to find the post
|
|
6242
6426
|
});
|
|
6243
|
-
const
|
|
6427
|
+
const matchOffset = fetched.messages.findIndex(
|
|
6428
|
+
(p) => p.sender.toLowerCase() === parsedId.sender.toLowerCase() && p.timestamp === parsedId.timestamp
|
|
6429
|
+
);
|
|
6430
|
+
const targetPost = matchOffset >= 0 ? fetched.messages[matchOffset] : void 0;
|
|
6244
6431
|
if (!targetPost) {
|
|
6245
6432
|
exitWithError(
|
|
6246
6433
|
`Post not found with ID ${postId} in feed "${normalizedFeed}". Make sure the sender and timestamp are correct.`
|
|
6247
6434
|
);
|
|
6248
6435
|
}
|
|
6436
|
+
const parentTopicIndex = fetched.startIndex + matchOffset;
|
|
6249
6437
|
const txConfig = client.prepareComment({
|
|
6250
6438
|
post: targetPost,
|
|
6251
6439
|
text: message
|
|
@@ -6269,26 +6457,81 @@ async function executeFeedCommentWrite(feed, postId, message, options) {
|
|
|
6269
6457
|
commonOptions.chainId,
|
|
6270
6458
|
commonOptions.rpcUrl
|
|
6271
6459
|
);
|
|
6272
|
-
|
|
6460
|
+
if (!options.json) {
|
|
6461
|
+
console.log(chalk4.blue(`Commenting on post ${postId}...`));
|
|
6462
|
+
}
|
|
6273
6463
|
try {
|
|
6274
6464
|
const hash = await executeTransaction(walletClient, txConfig);
|
|
6465
|
+
const senderAddress = walletClient.account.address;
|
|
6466
|
+
let globalIndex;
|
|
6467
|
+
try {
|
|
6468
|
+
const indices = await getMessageIndicesFromTx({
|
|
6469
|
+
chainId: commonOptions.chainId,
|
|
6470
|
+
rpcUrl: commonOptions.rpcUrl,
|
|
6471
|
+
txHash: hash
|
|
6472
|
+
});
|
|
6473
|
+
globalIndex = indices[0];
|
|
6474
|
+
} catch {
|
|
6475
|
+
}
|
|
6476
|
+
let commentTimestamp;
|
|
6477
|
+
if (globalIndex !== void 0) {
|
|
6478
|
+
try {
|
|
6479
|
+
const netClient = createNetClient(commonOptions);
|
|
6480
|
+
const fetchedComment = await netClient.getMessageAtIndex({
|
|
6481
|
+
messageIndex: globalIndex
|
|
6482
|
+
});
|
|
6483
|
+
if (fetchedComment) {
|
|
6484
|
+
commentTimestamp = Number(fetchedComment.timestamp);
|
|
6485
|
+
}
|
|
6486
|
+
} catch {
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6275
6489
|
addHistoryEntry({
|
|
6276
6490
|
type: "comment",
|
|
6277
6491
|
txHash: hash,
|
|
6278
6492
|
chainId: commonOptions.chainId,
|
|
6279
6493
|
feed: normalizedFeed,
|
|
6280
|
-
sender:
|
|
6494
|
+
sender: senderAddress,
|
|
6281
6495
|
text: message,
|
|
6282
6496
|
postId
|
|
6283
6497
|
});
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6498
|
+
const parentPostUrl = postPermalink(commonOptions.chainId, {
|
|
6499
|
+
topic: normalizedFeed,
|
|
6500
|
+
topicIndex: parentTopicIndex
|
|
6501
|
+
});
|
|
6502
|
+
const commentPermalink = commentTimestamp !== void 0 ? postPermalink(commonOptions.chainId, {
|
|
6503
|
+
topic: normalizedFeed,
|
|
6504
|
+
topicIndex: parentTopicIndex,
|
|
6505
|
+
commentId: `${senderAddress}-${commentTimestamp}`
|
|
6506
|
+
}) : postPermalink(commonOptions.chainId, { globalIndex });
|
|
6507
|
+
if (options.json) {
|
|
6508
|
+
printJson({
|
|
6509
|
+
success: true,
|
|
6510
|
+
txHash: hash,
|
|
6511
|
+
explorerTxUrl: explorerTxUrl(commonOptions.chainId, hash),
|
|
6512
|
+
globalIndex,
|
|
6513
|
+
permalink: commentPermalink,
|
|
6514
|
+
parentPostId: postId,
|
|
6515
|
+
parentPostUrl,
|
|
6516
|
+
feed: normalizedFeed,
|
|
6517
|
+
feedUrl: feedUrl(commonOptions.chainId, normalizedFeed),
|
|
6518
|
+
sender: senderAddress,
|
|
6519
|
+
senderProfileUrl: profileUrl(commonOptions.chainId, senderAddress),
|
|
6520
|
+
text: message,
|
|
6521
|
+
...commentTimestamp !== void 0 && {
|
|
6522
|
+
commentId: `${senderAddress}:${commentTimestamp}`
|
|
6523
|
+
}
|
|
6524
|
+
});
|
|
6525
|
+
return;
|
|
6526
|
+
}
|
|
6527
|
+
const lines = [
|
|
6528
|
+
`Comment posted successfully!`,
|
|
6529
|
+
` Transaction: ${hash}`,
|
|
6530
|
+
` Post: ${postId}`
|
|
6531
|
+
];
|
|
6532
|
+
if (commentPermalink) lines.push(` Permalink: ${commentPermalink}`);
|
|
6533
|
+
lines.push(` Comment: ${message}`);
|
|
6534
|
+
console.log(chalk4.green(lines.join("\n")));
|
|
6292
6535
|
} catch (error) {
|
|
6293
6536
|
exitWithError(
|
|
6294
6537
|
`Failed to post comment: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -6305,6 +6548,9 @@ function registerFeedCommentWriteCommand(parent) {
|
|
|
6305
6548
|
).option("--rpc-url <url>", "Custom RPC URL").option("--private-key <key>", "Private key (0x-prefixed)").option(
|
|
6306
6549
|
"--encode-only",
|
|
6307
6550
|
"Output transaction data as JSON instead of executing"
|
|
6551
|
+
).option(
|
|
6552
|
+
"--json",
|
|
6553
|
+
"Output structured JSON (includes permalink and other URLs) after submission"
|
|
6308
6554
|
).action(async (feed, postId, message, options) => {
|
|
6309
6555
|
await executeFeedCommentWrite(feed, postId, message, options);
|
|
6310
6556
|
});
|
|
@@ -6331,17 +6577,25 @@ async function executeFeedCommentRead(feed, postId, options) {
|
|
|
6331
6577
|
`Feed "${normalizedFeed}" has no posts. Cannot find post ${postId}.`
|
|
6332
6578
|
);
|
|
6333
6579
|
}
|
|
6334
|
-
const
|
|
6580
|
+
const fetched = await client.getFeedPostsWithIndex({
|
|
6335
6581
|
topic: normalizedFeed,
|
|
6336
6582
|
maxPosts: 100
|
|
6337
6583
|
// Fetch enough to find the post
|
|
6338
6584
|
});
|
|
6339
|
-
const
|
|
6585
|
+
const matchIndex = fetched.messages.findIndex(
|
|
6586
|
+
(p) => p.sender.toLowerCase() === parsedId.sender.toLowerCase() && p.timestamp === parsedId.timestamp
|
|
6587
|
+
);
|
|
6588
|
+
const targetPost = matchIndex >= 0 ? fetched.messages[matchIndex] : void 0;
|
|
6340
6589
|
if (!targetPost) {
|
|
6341
6590
|
exitWithError(
|
|
6342
6591
|
`Post not found with ID ${postId} in feed "${normalizedFeed}". Make sure the sender and timestamp are correct.`
|
|
6343
6592
|
);
|
|
6344
6593
|
}
|
|
6594
|
+
const parentTopicIndex = fetched.startIndex + matchIndex;
|
|
6595
|
+
const parentPostUrl = postPermalink(readOnlyOptions.chainId, {
|
|
6596
|
+
topic: normalizedFeed,
|
|
6597
|
+
topicIndex: parentTopicIndex
|
|
6598
|
+
});
|
|
6345
6599
|
const commentCount = await client.getCommentCount(targetPost);
|
|
6346
6600
|
if (commentCount === 0) {
|
|
6347
6601
|
if (options.json) {
|
|
@@ -6362,7 +6616,11 @@ async function executeFeedCommentRead(feed, postId, options) {
|
|
|
6362
6616
|
if (options.json) {
|
|
6363
6617
|
printJson(
|
|
6364
6618
|
commentsWithDepth.map(
|
|
6365
|
-
({ comment, depth }) => commentToJson(comment,
|
|
6619
|
+
({ comment, depth }) => commentToJson(comment, {
|
|
6620
|
+
chainId: readOnlyOptions.chainId,
|
|
6621
|
+
depth,
|
|
6622
|
+
parentPostUrl
|
|
6623
|
+
})
|
|
6366
6624
|
)
|
|
6367
6625
|
);
|
|
6368
6626
|
} else {
|
|
@@ -6503,8 +6761,10 @@ async function executeFeedReplies(options) {
|
|
|
6503
6761
|
rpcUrl: options.rpcUrl
|
|
6504
6762
|
});
|
|
6505
6763
|
const client = createFeedClient(readOnlyOptions);
|
|
6506
|
-
|
|
6764
|
+
if (!options.json) {
|
|
6765
|
+
console.log(chalk4.blue(`Checking replies on ${postsWithIds.length} posts...
|
|
6507
6766
|
`));
|
|
6767
|
+
}
|
|
6508
6768
|
const results = [];
|
|
6509
6769
|
for (const entry of postsWithIds) {
|
|
6510
6770
|
try {
|
|
@@ -6519,12 +6779,31 @@ async function executeFeedReplies(options) {
|
|
|
6519
6779
|
data: "0x"
|
|
6520
6780
|
};
|
|
6521
6781
|
const commentCount = await client.getCommentCount(postObj);
|
|
6782
|
+
let permalink = null;
|
|
6783
|
+
try {
|
|
6784
|
+
const indices = await getMessageIndicesFromTx({
|
|
6785
|
+
chainId: readOnlyOptions.chainId,
|
|
6786
|
+
rpcUrl: readOnlyOptions.rpcUrl,
|
|
6787
|
+
txHash: entry.txHash
|
|
6788
|
+
});
|
|
6789
|
+
if (indices[0] !== void 0) {
|
|
6790
|
+
permalink = postPermalink(readOnlyOptions.chainId, {
|
|
6791
|
+
globalIndex: indices[0]
|
|
6792
|
+
});
|
|
6793
|
+
}
|
|
6794
|
+
} catch {
|
|
6795
|
+
}
|
|
6522
6796
|
results.push({
|
|
6523
6797
|
feed: entry.feed,
|
|
6524
6798
|
postId: entry.postId,
|
|
6525
6799
|
text: entry.text ?? "",
|
|
6526
6800
|
postedAt: entry.timestamp,
|
|
6527
|
-
commentCount: Number(commentCount)
|
|
6801
|
+
commentCount: Number(commentCount),
|
|
6802
|
+
permalink,
|
|
6803
|
+
feedUrl: feedUrl(readOnlyOptions.chainId, entry.feed),
|
|
6804
|
+
senderProfileUrl: entry.sender ? profileUrl(readOnlyOptions.chainId, entry.sender) : null,
|
|
6805
|
+
explorerTxUrl: explorerTxUrl(readOnlyOptions.chainId, entry.txHash),
|
|
6806
|
+
txHash: entry.txHash
|
|
6528
6807
|
});
|
|
6529
6808
|
} catch {
|
|
6530
6809
|
}
|
|
@@ -6609,7 +6888,14 @@ async function executeFeedPosts(address, options) {
|
|
|
6609
6888
|
endIndex: count
|
|
6610
6889
|
});
|
|
6611
6890
|
if (options.json) {
|
|
6612
|
-
printJson(
|
|
6891
|
+
printJson(
|
|
6892
|
+
messages.map(
|
|
6893
|
+
(msg, i) => postToJson(msg, {
|
|
6894
|
+
chainId: readOnlyOptions.chainId,
|
|
6895
|
+
userIndex: startIndex + i
|
|
6896
|
+
})
|
|
6897
|
+
)
|
|
6898
|
+
);
|
|
6613
6899
|
} else {
|
|
6614
6900
|
console.log(
|
|
6615
6901
|
chalk4.white(`Found ${messages.length} post(s) by ${address}:
|
|
@@ -6787,11 +7073,14 @@ function historyEntryToJson(entry, index) {
|
|
|
6787
7073
|
type: entry.type,
|
|
6788
7074
|
timestamp: entry.timestamp,
|
|
6789
7075
|
txHash: entry.txHash,
|
|
7076
|
+
explorerTxUrl: explorerTxUrl(entry.chainId, entry.txHash),
|
|
6790
7077
|
chainId: entry.chainId,
|
|
6791
|
-
feed: entry.feed
|
|
7078
|
+
feed: entry.feed,
|
|
7079
|
+
feedUrl: feedUrl(entry.chainId, entry.feed)
|
|
6792
7080
|
};
|
|
6793
7081
|
if (entry.sender) {
|
|
6794
7082
|
result.sender = entry.sender;
|
|
7083
|
+
result.senderProfileUrl = profileUrl(entry.chainId, entry.sender);
|
|
6795
7084
|
}
|
|
6796
7085
|
if (entry.text) {
|
|
6797
7086
|
result.text = entry.text;
|
|
@@ -6965,7 +7254,9 @@ async function executeListAgents(options) {
|
|
|
6965
7254
|
if (options.json) {
|
|
6966
7255
|
printJson({
|
|
6967
7256
|
totalCount,
|
|
6968
|
-
agents: agents.map(
|
|
7257
|
+
agents: agents.map(
|
|
7258
|
+
(agent, i) => agentToJson(agent, i, readOnlyOptions.chainId)
|
|
7259
|
+
)
|
|
6969
7260
|
});
|
|
6970
7261
|
} else {
|
|
6971
7262
|
if (agents.length === 0) {
|
|
@@ -7024,9 +7315,13 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
7024
7315
|
const netClient = createNetClient(readOnlyOptions);
|
|
7025
7316
|
const existingHistory = getHistory();
|
|
7026
7317
|
if (existingHistory.some((entry) => entry.txHash === txHash)) {
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7318
|
+
if (options.json) {
|
|
7319
|
+
printJson({ alreadyRecorded: true, txHash });
|
|
7320
|
+
} else {
|
|
7321
|
+
console.log(
|
|
7322
|
+
chalk4.yellow("Transaction already recorded in history. Skipping.")
|
|
7323
|
+
);
|
|
7324
|
+
}
|
|
7030
7325
|
return;
|
|
7031
7326
|
}
|
|
7032
7327
|
const receipt = await publicClient.getTransactionReceipt({ hash: txHash }).catch(
|
|
@@ -7061,11 +7356,13 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
7061
7356
|
"Transaction does not contain any Net protocol messages."
|
|
7062
7357
|
);
|
|
7063
7358
|
}
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7359
|
+
if (!options.json) {
|
|
7360
|
+
console.log(
|
|
7361
|
+
chalk4.blue(
|
|
7362
|
+
`Found ${messageSentEvents.length} message(s) in transaction. Fetching details...`
|
|
7363
|
+
)
|
|
7364
|
+
);
|
|
7365
|
+
}
|
|
7069
7366
|
const messages = await Promise.all(
|
|
7070
7367
|
messageSentEvents.map(
|
|
7071
7368
|
(event) => netClient.getMessageAtIndex({
|
|
@@ -7073,41 +7370,40 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
7073
7370
|
})
|
|
7074
7371
|
)
|
|
7075
7372
|
);
|
|
7373
|
+
const entries = [];
|
|
7076
7374
|
let recorded = 0;
|
|
7077
7375
|
for (let i = 0; i < messages.length; i++) {
|
|
7078
7376
|
const message = messages[i];
|
|
7079
7377
|
if (!message) {
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7378
|
+
if (!options.json) {
|
|
7379
|
+
console.log(
|
|
7380
|
+
chalk4.yellow(
|
|
7381
|
+
` Could not fetch message at index ${messageSentEvents[i].messageIndex}. Skipping.`
|
|
7382
|
+
)
|
|
7383
|
+
);
|
|
7384
|
+
}
|
|
7085
7385
|
continue;
|
|
7086
7386
|
}
|
|
7387
|
+
const globalIndex = Number(messageSentEvents[i].messageIndex);
|
|
7087
7388
|
const feedName = extractFeedName(message.topic);
|
|
7088
7389
|
const isComment = isCommentTopic(message.topic);
|
|
7089
7390
|
let type;
|
|
7090
7391
|
let postId;
|
|
7091
|
-
let
|
|
7392
|
+
let parentPostId;
|
|
7393
|
+
let permalink = null;
|
|
7092
7394
|
if (isComment) {
|
|
7093
7395
|
type = "comment";
|
|
7094
7396
|
const commentData = parseCommentData(message.data);
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
Tx: ${txHash}`;
|
|
7397
|
+
parentPostId = commentData ? `${commentData.parentSender}:${commentData.parentTimestamp}` : void 0;
|
|
7398
|
+
const commentParam = `${message.sender}-${Number(message.timestamp)}`;
|
|
7399
|
+
permalink = postPermalink(readOnlyOptions.chainId, {
|
|
7400
|
+
globalIndex,
|
|
7401
|
+
commentId: commentParam
|
|
7402
|
+
});
|
|
7102
7403
|
} else {
|
|
7103
7404
|
type = "post";
|
|
7104
7405
|
postId = createPostId(message);
|
|
7105
|
-
|
|
7106
|
-
Feed: ${feedName}
|
|
7107
|
-
Sender: ${message.sender}
|
|
7108
|
-
Text: ${message.text}
|
|
7109
|
-
Post ID: ${postId}
|
|
7110
|
-
Tx: ${txHash}`;
|
|
7406
|
+
permalink = postPermalink(readOnlyOptions.chainId, { globalIndex });
|
|
7111
7407
|
}
|
|
7112
7408
|
addHistoryEntry({
|
|
7113
7409
|
type,
|
|
@@ -7116,11 +7412,49 @@ async function executeFeedVerifyClaim(txHash, options) {
|
|
|
7116
7412
|
feed: feedName,
|
|
7117
7413
|
sender: message.sender,
|
|
7118
7414
|
text: message.text,
|
|
7119
|
-
postId
|
|
7415
|
+
postId: type === "comment" ? parentPostId : postId
|
|
7120
7416
|
});
|
|
7121
|
-
|
|
7417
|
+
const entry = {
|
|
7418
|
+
type,
|
|
7419
|
+
txHash,
|
|
7420
|
+
explorerTxUrl: explorerTxUrl(readOnlyOptions.chainId, txHash),
|
|
7421
|
+
globalIndex,
|
|
7422
|
+
permalink,
|
|
7423
|
+
feed: feedName,
|
|
7424
|
+
feedUrl: feedUrl(readOnlyOptions.chainId, feedName),
|
|
7425
|
+
sender: message.sender,
|
|
7426
|
+
senderProfileUrl: profileUrl(
|
|
7427
|
+
readOnlyOptions.chainId,
|
|
7428
|
+
message.sender
|
|
7429
|
+
),
|
|
7430
|
+
text: message.text,
|
|
7431
|
+
timestamp: Number(message.timestamp)
|
|
7432
|
+
};
|
|
7433
|
+
if (type === "post") entry.postId = postId;
|
|
7434
|
+
if (type === "comment") entry.parentPostId = parentPostId;
|
|
7435
|
+
entries.push(entry);
|
|
7436
|
+
if (!options.json) {
|
|
7437
|
+
const label = type === "comment" ? `Verified comment:
|
|
7438
|
+
Feed: ${feedName}
|
|
7439
|
+
Sender: ${message.sender}
|
|
7440
|
+
Text: ${message.text}
|
|
7441
|
+
Parent post: ${parentPostId ?? "unknown"}
|
|
7442
|
+
Permalink: ${permalink ?? "(unavailable)"}
|
|
7443
|
+
Tx: ${txHash}` : `Verified post:
|
|
7444
|
+
Feed: ${feedName}
|
|
7445
|
+
Sender: ${message.sender}
|
|
7446
|
+
Text: ${message.text}
|
|
7447
|
+
Post ID: ${postId}
|
|
7448
|
+
Permalink: ${permalink ?? "(unavailable)"}
|
|
7449
|
+
Tx: ${txHash}`;
|
|
7450
|
+
console.log(chalk4.green(` ${label}`));
|
|
7451
|
+
}
|
|
7122
7452
|
recorded++;
|
|
7123
7453
|
}
|
|
7454
|
+
if (options.json) {
|
|
7455
|
+
printJson({ recorded, entries });
|
|
7456
|
+
return;
|
|
7457
|
+
}
|
|
7124
7458
|
if (recorded > 0) {
|
|
7125
7459
|
console.log(
|
|
7126
7460
|
chalk4.green(`
|
|
@@ -7135,7 +7469,10 @@ function registerFeedVerifyClaimCommand(parent) {
|
|
|
7135
7469
|
"--chain-id <id>",
|
|
7136
7470
|
"Chain ID (default: 8453 for Base)",
|
|
7137
7471
|
(value) => parseInt(value, 10)
|
|
7138
|
-
).option("--rpc-url <url>", "Custom RPC URL").
|
|
7472
|
+
).option("--rpc-url <url>", "Custom RPC URL").option(
|
|
7473
|
+
"--json",
|
|
7474
|
+
"Output structured JSON (includes permalink and other URLs)"
|
|
7475
|
+
).action(async (txHash, options) => {
|
|
7139
7476
|
await executeFeedVerifyClaim(txHash, options);
|
|
7140
7477
|
});
|
|
7141
7478
|
}
|
|
@@ -7331,6 +7668,7 @@ async function executeGetUpvotes(options) {
|
|
|
7331
7668
|
JSON.stringify(
|
|
7332
7669
|
{
|
|
7333
7670
|
tokenAddress,
|
|
7671
|
+
tokenUrl: tokenUrl(readOnlyOptions.chainId, tokenAddress),
|
|
7334
7672
|
scoreKey,
|
|
7335
7673
|
total,
|
|
7336
7674
|
strategies: strategyCounts.map((s) => ({
|
|
@@ -7499,6 +7837,8 @@ async function executeGetUserUpvotes(options) {
|
|
|
7499
7837
|
{
|
|
7500
7838
|
address: userAddress,
|
|
7501
7839
|
chainId: readOnlyOptions.chainId,
|
|
7840
|
+
profileUrl: profileUrl(readOnlyOptions.chainId, userAddress),
|
|
7841
|
+
walletUrl: walletUrl(readOnlyOptions.chainId, userAddress),
|
|
7502
7842
|
upvotesGiven: Number(given),
|
|
7503
7843
|
upvotesReceived: Number(received),
|
|
7504
7844
|
upvotePriceWei: upvotePrice.toString(),
|
|
@@ -7540,6 +7880,845 @@ function registerUpvoteCommand(program2) {
|
|
|
7540
7880
|
registerUpvoteUserCommand(upvoteCommand);
|
|
7541
7881
|
registerGetUserUpvotesCommand(upvoteCommand);
|
|
7542
7882
|
}
|
|
7883
|
+
var VALID_RUN_MODES = ["auto", "feeds", "chats"];
|
|
7884
|
+
function addAuthOptions(cmd) {
|
|
7885
|
+
return addCommonOptions(cmd).option("--private-key <key>", "Private key (0x-prefixed)").option(
|
|
7886
|
+
"--session-token <token>",
|
|
7887
|
+
"Pre-existing session token (alternative to --private-key)"
|
|
7888
|
+
).option(
|
|
7889
|
+
"--operator <address>",
|
|
7890
|
+
"Operator address (required with --session-token)"
|
|
7891
|
+
);
|
|
7892
|
+
}
|
|
7893
|
+
function addCommonOptions(cmd) {
|
|
7894
|
+
return cmd.option("--chain-id <id>", "Chain ID (default: 8453)", (v) => parseInt(v, 10)).option("--rpc-url <url>", "Custom RPC URL").option("--api-url <url>", "Net Protocol API URL");
|
|
7895
|
+
}
|
|
7896
|
+
function addFilterOptions(cmd) {
|
|
7897
|
+
return cmd.option("--include-feed <pattern...>", "Only engage with matching feeds").option("--exclude-feed <pattern...>", "Never engage with matching feeds").option("--preferred-feed <pattern...>", "Prioritize matching feeds").option("--chat-topic <topic...>", "Chat topics to participate in");
|
|
7898
|
+
}
|
|
7899
|
+
function addProfileOptions(cmd) {
|
|
7900
|
+
return cmd.option("--display-name <name>", "Agent display name").option("--bio <text>", "Agent bio");
|
|
7901
|
+
}
|
|
7902
|
+
function buildFilters(options) {
|
|
7903
|
+
const filters = {};
|
|
7904
|
+
if (options.includeFeed?.length) filters.includeFeedPatterns = options.includeFeed;
|
|
7905
|
+
if (options.excludeFeed?.length) filters.excludeFeedPatterns = options.excludeFeed;
|
|
7906
|
+
if (options.preferredFeed?.length) filters.preferredFeedPatterns = options.preferredFeed;
|
|
7907
|
+
if (options.chatTopic?.length) filters.preferredChatTopics = options.chatTopic;
|
|
7908
|
+
return Object.keys(filters).length > 0 ? filters : void 0;
|
|
7909
|
+
}
|
|
7910
|
+
function buildProfile(options) {
|
|
7911
|
+
const profile = {};
|
|
7912
|
+
if (options.displayName) profile.displayName = options.displayName;
|
|
7913
|
+
if (options.bio) profile.bio = options.bio;
|
|
7914
|
+
return Object.keys(profile).length > 0 ? profile : void 0;
|
|
7915
|
+
}
|
|
7916
|
+
function parseRunMode(raw) {
|
|
7917
|
+
const mode = raw ?? "auto";
|
|
7918
|
+
if (!VALID_RUN_MODES.includes(mode)) {
|
|
7919
|
+
exitWithError(`Invalid mode "${mode}". Must be one of: ${VALID_RUN_MODES.join(", ")}`);
|
|
7920
|
+
}
|
|
7921
|
+
return mode;
|
|
7922
|
+
}
|
|
7923
|
+
function resolveReadOnly(options) {
|
|
7924
|
+
const readOnly = parseReadOnlyOptionsWithDefault({
|
|
7925
|
+
chainId: options.chainId,
|
|
7926
|
+
rpcUrl: options.rpcUrl
|
|
7927
|
+
});
|
|
7928
|
+
const apiUrl = options.apiUrl || process.env.NET_API_URL || NET_API_URL;
|
|
7929
|
+
let operator;
|
|
7930
|
+
if (options.operator) {
|
|
7931
|
+
if (!isAddress(options.operator)) {
|
|
7932
|
+
exitWithError(`Invalid operator address: ${options.operator}`);
|
|
7933
|
+
}
|
|
7934
|
+
operator = options.operator;
|
|
7935
|
+
}
|
|
7936
|
+
return { chainId: readOnly.chainId, apiUrl, operator };
|
|
7937
|
+
}
|
|
7938
|
+
async function resolveAuth(options) {
|
|
7939
|
+
const sessionToken = options.sessionToken || process.env.NET_SESSION_TOKEN;
|
|
7940
|
+
const privateKey = options.privateKey || process.env.NET_PRIVATE_KEY || process.env.PRIVATE_KEY;
|
|
7941
|
+
if (sessionToken && options.privateKey) {
|
|
7942
|
+
exitWithError("Cannot use both --session-token and --private-key. Pick one.");
|
|
7943
|
+
}
|
|
7944
|
+
const apiUrl = options.apiUrl || process.env.NET_API_URL || NET_API_URL;
|
|
7945
|
+
if (sessionToken) {
|
|
7946
|
+
if (!options.operator) {
|
|
7947
|
+
exitWithError(
|
|
7948
|
+
"--operator <address> is required when using --session-token. It must match the address that signed the session."
|
|
7949
|
+
);
|
|
7950
|
+
}
|
|
7951
|
+
if (!isAddress(options.operator)) {
|
|
7952
|
+
exitWithError(`Invalid operator address: ${options.operator}`);
|
|
7953
|
+
}
|
|
7954
|
+
const readOnly = parseReadOnlyOptionsWithDefault({
|
|
7955
|
+
chainId: options.chainId,
|
|
7956
|
+
rpcUrl: options.rpcUrl
|
|
7957
|
+
});
|
|
7958
|
+
const client2 = new AgentClient({ apiUrl, chainId: readOnly.chainId });
|
|
7959
|
+
return {
|
|
7960
|
+
client: client2,
|
|
7961
|
+
sessionToken,
|
|
7962
|
+
operatorAddress: options.operator,
|
|
7963
|
+
chainId: readOnly.chainId,
|
|
7964
|
+
apiUrl
|
|
7965
|
+
};
|
|
7966
|
+
}
|
|
7967
|
+
if (!privateKey) {
|
|
7968
|
+
exitWithError(
|
|
7969
|
+
"No auth provided. Use one of:\n --private-key <key> (or NET_PRIVATE_KEY env var)\n --session-token <token> + --operator <address> (for Bankr or other external signers)"
|
|
7970
|
+
);
|
|
7971
|
+
}
|
|
7972
|
+
const commonOptions = parseCommonOptionsWithDefault({
|
|
7973
|
+
privateKey,
|
|
7974
|
+
chainId: options.chainId,
|
|
7975
|
+
rpcUrl: options.rpcUrl
|
|
7976
|
+
});
|
|
7977
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
7978
|
+
console.log(chalk4.blue("Creating session..."));
|
|
7979
|
+
const { sessionToken: token } = await createRelaySession({
|
|
7980
|
+
apiUrl,
|
|
7981
|
+
chainId: commonOptions.chainId,
|
|
7982
|
+
operatorAddress: account.address,
|
|
7983
|
+
secretKey: RELAY_ACCESS_KEY,
|
|
7984
|
+
account
|
|
7985
|
+
});
|
|
7986
|
+
const client = new AgentClient({ apiUrl, chainId: commonOptions.chainId });
|
|
7987
|
+
return {
|
|
7988
|
+
client,
|
|
7989
|
+
sessionToken: token,
|
|
7990
|
+
account,
|
|
7991
|
+
operatorAddress: account.address,
|
|
7992
|
+
chainId: commonOptions.chainId,
|
|
7993
|
+
apiUrl
|
|
7994
|
+
};
|
|
7995
|
+
}
|
|
7996
|
+
function bigintReplacer5(_key, value) {
|
|
7997
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
7998
|
+
}
|
|
7999
|
+
function jsonStringify(obj, indent = 2) {
|
|
8000
|
+
return JSON.stringify(obj, bigintReplacer5, indent);
|
|
8001
|
+
}
|
|
8002
|
+
|
|
8003
|
+
// src/commands/agent/create.ts
|
|
8004
|
+
async function executeCreate(name, options) {
|
|
8005
|
+
try {
|
|
8006
|
+
const auth = await resolveAuth(options);
|
|
8007
|
+
const config = {
|
|
8008
|
+
name,
|
|
8009
|
+
systemPrompt: options.systemPrompt
|
|
8010
|
+
};
|
|
8011
|
+
if (options.schedule) config.runIntervalMinutes = options.schedule;
|
|
8012
|
+
const filters = buildFilters(options);
|
|
8013
|
+
if (filters) config.filters = filters;
|
|
8014
|
+
const profile = buildProfile(options);
|
|
8015
|
+
console.log(chalk4.blue(`Creating agent "${name}"...`));
|
|
8016
|
+
const result = await auth.client.createAgent({
|
|
8017
|
+
sessionToken: auth.sessionToken,
|
|
8018
|
+
config,
|
|
8019
|
+
profile
|
|
8020
|
+
});
|
|
8021
|
+
if (!result.success) {
|
|
8022
|
+
exitWithError(result.error || "Failed to create agent");
|
|
8023
|
+
}
|
|
8024
|
+
if (options.json) {
|
|
8025
|
+
console.log(jsonStringify(result));
|
|
8026
|
+
return;
|
|
8027
|
+
}
|
|
8028
|
+
console.log(chalk4.green("Agent created successfully!"));
|
|
8029
|
+
console.log(` Agent ID: ${result.agentId}`);
|
|
8030
|
+
console.log(` Wallet: ${result.agentWalletAddress}`);
|
|
8031
|
+
if (result.scheduleError) {
|
|
8032
|
+
console.log(chalk4.yellow(` Schedule warning: ${result.scheduleError}`));
|
|
8033
|
+
}
|
|
8034
|
+
} catch (error) {
|
|
8035
|
+
exitWithError(
|
|
8036
|
+
`Failed to create agent: ${error instanceof Error ? error.message : String(error)}`
|
|
8037
|
+
);
|
|
8038
|
+
}
|
|
8039
|
+
}
|
|
8040
|
+
function registerAgentCreateCommand(parent) {
|
|
8041
|
+
const cmd = parent.command("create <name>").description("Create a new onchain agent").requiredOption("--system-prompt <prompt>", "Agent system prompt (personality)").option("--schedule <minutes>", "Auto-run interval in minutes", (v) => parseInt(v, 10)).option("--json", "Output as JSON");
|
|
8042
|
+
addAuthOptions(cmd);
|
|
8043
|
+
addFilterOptions(cmd);
|
|
8044
|
+
addProfileOptions(cmd);
|
|
8045
|
+
cmd.action(async (name, options) => {
|
|
8046
|
+
await executeCreate(name, options);
|
|
8047
|
+
});
|
|
8048
|
+
}
|
|
8049
|
+
async function executeList(options) {
|
|
8050
|
+
try {
|
|
8051
|
+
const auth = await resolveAuth(options);
|
|
8052
|
+
const result = await auth.client.listAgents({ sessionToken: auth.sessionToken });
|
|
8053
|
+
if (!result.success) {
|
|
8054
|
+
exitWithError(result.error || "Failed to list agents");
|
|
8055
|
+
}
|
|
8056
|
+
const agents = result.agents || [];
|
|
8057
|
+
const visible = options.showHidden ? agents : agents.filter((a) => !a.config.hidden);
|
|
8058
|
+
if (options.json) {
|
|
8059
|
+
console.log(jsonStringify(visible));
|
|
8060
|
+
return;
|
|
8061
|
+
}
|
|
8062
|
+
if (visible.length === 0) {
|
|
8063
|
+
console.log(chalk4.yellow("No agents found."));
|
|
8064
|
+
return;
|
|
8065
|
+
}
|
|
8066
|
+
console.log(chalk4.bold(`Agents (${visible.length}):
|
|
8067
|
+
`));
|
|
8068
|
+
for (const agent of visible) {
|
|
8069
|
+
const { config, walletAddress } = agent;
|
|
8070
|
+
const schedule = config.runIntervalMinutes ? `every ${config.runIntervalMinutes}m` : "manual";
|
|
8071
|
+
const hidden = config.hidden ? chalk4.gray(" [hidden]") : "";
|
|
8072
|
+
const promptPreview = config.systemPrompt.length > 80 ? `${config.systemPrompt.slice(0, 80)}...` : config.systemPrompt;
|
|
8073
|
+
console.log(` ${chalk4.cyan(config.name)}${hidden}`);
|
|
8074
|
+
console.log(` ID: ${config.id}`);
|
|
8075
|
+
console.log(` Wallet: ${walletAddress}`);
|
|
8076
|
+
console.log(` Schedule: ${schedule}`);
|
|
8077
|
+
console.log(` Prompt: ${promptPreview}`);
|
|
8078
|
+
console.log();
|
|
8079
|
+
}
|
|
8080
|
+
} catch (error) {
|
|
8081
|
+
exitWithError(
|
|
8082
|
+
`Failed to list agents: ${error instanceof Error ? error.message : String(error)}`
|
|
8083
|
+
);
|
|
8084
|
+
}
|
|
8085
|
+
}
|
|
8086
|
+
function registerAgentListCommand(parent) {
|
|
8087
|
+
const cmd = parent.command("list").description("List your onchain agents").option("--json", "Output as JSON").option("--show-hidden", "Include hidden agents");
|
|
8088
|
+
addAuthOptions(cmd);
|
|
8089
|
+
cmd.action(async (options) => {
|
|
8090
|
+
await executeList(options);
|
|
8091
|
+
});
|
|
8092
|
+
}
|
|
8093
|
+
async function executeUpdate(agentId, options) {
|
|
8094
|
+
try {
|
|
8095
|
+
const auth = await resolveAuth(options);
|
|
8096
|
+
const config = {};
|
|
8097
|
+
let hasConfigChanges = false;
|
|
8098
|
+
if (options.name) {
|
|
8099
|
+
config.name = options.name;
|
|
8100
|
+
hasConfigChanges = true;
|
|
8101
|
+
}
|
|
8102
|
+
if (options.systemPrompt) {
|
|
8103
|
+
config.systemPrompt = options.systemPrompt;
|
|
8104
|
+
hasConfigChanges = true;
|
|
8105
|
+
}
|
|
8106
|
+
if (options.schedule) {
|
|
8107
|
+
config.runIntervalMinutes = options.schedule;
|
|
8108
|
+
hasConfigChanges = true;
|
|
8109
|
+
}
|
|
8110
|
+
if (options.disableSchedule) {
|
|
8111
|
+
config.runIntervalMinutes = 0;
|
|
8112
|
+
hasConfigChanges = true;
|
|
8113
|
+
}
|
|
8114
|
+
const filters = buildFilters(options);
|
|
8115
|
+
if (filters) {
|
|
8116
|
+
config.filters = filters;
|
|
8117
|
+
hasConfigChanges = true;
|
|
8118
|
+
}
|
|
8119
|
+
const profile = buildProfile(options);
|
|
8120
|
+
if (!hasConfigChanges && !profile) {
|
|
8121
|
+
exitWithError(
|
|
8122
|
+
"No changes specified. Use --name, --system-prompt, --schedule, --display-name, --bio, or filter options."
|
|
8123
|
+
);
|
|
8124
|
+
}
|
|
8125
|
+
console.log(chalk4.blue(`Updating agent ${agentId}...`));
|
|
8126
|
+
const result = await auth.client.updateAgent({
|
|
8127
|
+
sessionToken: auth.sessionToken,
|
|
8128
|
+
agentId,
|
|
8129
|
+
config: hasConfigChanges ? config : void 0,
|
|
8130
|
+
profile
|
|
8131
|
+
});
|
|
8132
|
+
if (!result.success) {
|
|
8133
|
+
exitWithError(result.error || "Failed to update agent");
|
|
8134
|
+
}
|
|
8135
|
+
if (options.json) {
|
|
8136
|
+
console.log(jsonStringify(result));
|
|
8137
|
+
return;
|
|
8138
|
+
}
|
|
8139
|
+
console.log(chalk4.green("Agent updated successfully!"));
|
|
8140
|
+
if (result.profileError) {
|
|
8141
|
+
console.log(chalk4.yellow(` Profile warning: ${result.profileError}`));
|
|
8142
|
+
}
|
|
8143
|
+
} catch (error) {
|
|
8144
|
+
exitWithError(
|
|
8145
|
+
`Failed to update agent: ${error instanceof Error ? error.message : String(error)}`
|
|
8146
|
+
);
|
|
8147
|
+
}
|
|
8148
|
+
}
|
|
8149
|
+
function registerAgentUpdateCommand(parent) {
|
|
8150
|
+
const cmd = parent.command("update <agentId>").description("Update an existing agent").option("--name <name>", "New agent name").option("--system-prompt <prompt>", "New system prompt").option("--schedule <minutes>", "Auto-run interval in minutes", (v) => parseInt(v, 10)).option("--disable-schedule", "Disable automatic scheduling").option("--json", "Output as JSON");
|
|
8151
|
+
addAuthOptions(cmd);
|
|
8152
|
+
addFilterOptions(cmd);
|
|
8153
|
+
addProfileOptions(cmd);
|
|
8154
|
+
cmd.action(async (agentId, options) => {
|
|
8155
|
+
await executeUpdate(agentId, options);
|
|
8156
|
+
});
|
|
8157
|
+
}
|
|
8158
|
+
async function executeToggleHidden(agentId, options, hide) {
|
|
8159
|
+
const verb = hide ? "Hiding" : "Unhiding";
|
|
8160
|
+
const past = hide ? "hidden" : "unhidden";
|
|
8161
|
+
try {
|
|
8162
|
+
const auth = await resolveAuth(options);
|
|
8163
|
+
console.log(chalk4.blue(`${verb} agent ${agentId}...`));
|
|
8164
|
+
const result = hide ? await auth.client.hideAgent(auth.sessionToken, agentId) : await auth.client.unhideAgent(auth.sessionToken, agentId);
|
|
8165
|
+
if (!result.success) {
|
|
8166
|
+
exitWithError(result.error || `Failed to ${hide ? "hide" : "unhide"} agent`);
|
|
8167
|
+
}
|
|
8168
|
+
console.log(chalk4.green(`Agent ${past} successfully.`));
|
|
8169
|
+
} catch (error) {
|
|
8170
|
+
exitWithError(
|
|
8171
|
+
`Failed to ${hide ? "hide" : "unhide"} agent: ${error instanceof Error ? error.message : String(error)}`
|
|
8172
|
+
);
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8175
|
+
function registerAgentHideCommand(parent) {
|
|
8176
|
+
const cmd = parent.command("hide <agentId>").description("Hide an agent (soft-delete)");
|
|
8177
|
+
addAuthOptions(cmd).action(async (agentId, options) => {
|
|
8178
|
+
await executeToggleHidden(agentId, options, true);
|
|
8179
|
+
});
|
|
8180
|
+
}
|
|
8181
|
+
function registerAgentUnhideCommand(parent) {
|
|
8182
|
+
const cmd = parent.command("unhide <agentId>").description("Unhide a previously hidden agent");
|
|
8183
|
+
addAuthOptions(cmd).action(async (agentId, options) => {
|
|
8184
|
+
await executeToggleHidden(agentId, options, false);
|
|
8185
|
+
});
|
|
8186
|
+
}
|
|
8187
|
+
async function executeRun(agentId, options) {
|
|
8188
|
+
try {
|
|
8189
|
+
const auth = await resolveAuth(options);
|
|
8190
|
+
const mode = parseRunMode(options.mode);
|
|
8191
|
+
console.log(chalk4.blue(`Running agent ${agentId} (mode: ${mode})...`));
|
|
8192
|
+
const result = await auth.client.runAgent({
|
|
8193
|
+
sessionToken: auth.sessionToken,
|
|
8194
|
+
agentId,
|
|
8195
|
+
mode
|
|
8196
|
+
});
|
|
8197
|
+
if (options.json) {
|
|
8198
|
+
console.log(jsonStringify(result));
|
|
8199
|
+
return;
|
|
8200
|
+
}
|
|
8201
|
+
if (!result.success) {
|
|
8202
|
+
exitWithError(result.error || "Agent run failed");
|
|
8203
|
+
}
|
|
8204
|
+
console.log(chalk4.green(`Agent run complete: ${result.action}`));
|
|
8205
|
+
if (result.summary) {
|
|
8206
|
+
console.log(` Summary: ${result.summary}`);
|
|
8207
|
+
}
|
|
8208
|
+
if (result.actions.length > 0) {
|
|
8209
|
+
console.log(" Actions:");
|
|
8210
|
+
for (const action of result.actions) {
|
|
8211
|
+
const textPreview = action.text.length > 60 ? `${action.text.slice(0, 60)}...` : action.text;
|
|
8212
|
+
console.log(` - ${action.type} in ${action.topic}: "${textPreview}"`);
|
|
8213
|
+
console.log(` tx: ${action.transactionHash}`);
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
if (result.autoFunded) {
|
|
8217
|
+
console.log(
|
|
8218
|
+
chalk4.blue(
|
|
8219
|
+
` Auto-funded: $${result.autoFunded.amountUsd.toFixed(4)} (${result.autoFunded.amountEth} ETH)`
|
|
8220
|
+
)
|
|
8221
|
+
);
|
|
8222
|
+
}
|
|
8223
|
+
if (result.agentBalanceUsd !== void 0) {
|
|
8224
|
+
console.log(` Agent balance: $${result.agentBalanceUsd.toFixed(4)}`);
|
|
8225
|
+
}
|
|
8226
|
+
if (result.mainBalanceUsd !== void 0) {
|
|
8227
|
+
console.log(` Credits balance: $${result.mainBalanceUsd.toFixed(4)}`);
|
|
8228
|
+
}
|
|
8229
|
+
} catch (error) {
|
|
8230
|
+
exitWithError(
|
|
8231
|
+
`Failed to run agent: ${error instanceof Error ? error.message : String(error)}`
|
|
8232
|
+
);
|
|
8233
|
+
}
|
|
8234
|
+
}
|
|
8235
|
+
function registerAgentRunCommand(parent) {
|
|
8236
|
+
const cmd = parent.command("run <agentId>").description("Execute one agent cycle").option("--mode <mode>", "Run mode: auto, feeds, or chats (default: auto)").option("--json", "Output as JSON");
|
|
8237
|
+
addAuthOptions(cmd).action(async (agentId, options) => {
|
|
8238
|
+
await executeRun(agentId, options);
|
|
8239
|
+
});
|
|
8240
|
+
}
|
|
8241
|
+
async function executeInfo(agentId, options) {
|
|
8242
|
+
try {
|
|
8243
|
+
const auth = await resolveAuth(options);
|
|
8244
|
+
const agent = await auth.client.getAgent(auth.sessionToken, agentId);
|
|
8245
|
+
if (!agent) {
|
|
8246
|
+
exitWithError(`Agent not found: ${agentId}`);
|
|
8247
|
+
return;
|
|
8248
|
+
}
|
|
8249
|
+
if (options.json) {
|
|
8250
|
+
console.log(jsonStringify(agent));
|
|
8251
|
+
return;
|
|
8252
|
+
}
|
|
8253
|
+
const { config, walletAddress } = agent;
|
|
8254
|
+
const schedule = config.runIntervalMinutes ? `every ${config.runIntervalMinutes} minutes` : "manual only";
|
|
8255
|
+
console.log(
|
|
8256
|
+
chalk4.bold(config.name) + (config.hidden ? chalk4.gray(" [hidden]") : "")
|
|
8257
|
+
);
|
|
8258
|
+
console.log();
|
|
8259
|
+
console.log(` ID: ${config.id}`);
|
|
8260
|
+
console.log(` Wallet: ${walletAddress}`);
|
|
8261
|
+
console.log(` Owner: ${config.ownerAddress}`);
|
|
8262
|
+
console.log(` Schedule: ${schedule}`);
|
|
8263
|
+
console.log(` Created: ${new Date(config.createdAt).toLocaleString()}`);
|
|
8264
|
+
console.log(` Updated: ${new Date(config.updatedAt).toLocaleString()}`);
|
|
8265
|
+
console.log();
|
|
8266
|
+
console.log(" System Prompt:");
|
|
8267
|
+
console.log(` ${config.systemPrompt}`);
|
|
8268
|
+
if (config.filters) {
|
|
8269
|
+
console.log();
|
|
8270
|
+
console.log(" Filters:");
|
|
8271
|
+
if (config.filters.includeFeedPatterns?.length) {
|
|
8272
|
+
console.log(` Include feeds: ${config.filters.includeFeedPatterns.join(", ")}`);
|
|
8273
|
+
}
|
|
8274
|
+
if (config.filters.excludeFeedPatterns?.length) {
|
|
8275
|
+
console.log(` Exclude feeds: ${config.filters.excludeFeedPatterns.join(", ")}`);
|
|
8276
|
+
}
|
|
8277
|
+
if (config.filters.preferredFeedPatterns?.length) {
|
|
8278
|
+
console.log(
|
|
8279
|
+
` Preferred feeds: ${config.filters.preferredFeedPatterns.join(", ")}`
|
|
8280
|
+
);
|
|
8281
|
+
}
|
|
8282
|
+
if (config.filters.preferredChatTopics?.length) {
|
|
8283
|
+
console.log(` Chat topics: ${config.filters.preferredChatTopics.join(", ")}`);
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
} catch (error) {
|
|
8287
|
+
exitWithError(
|
|
8288
|
+
`Failed to get agent info: ${error instanceof Error ? error.message : String(error)}`
|
|
8289
|
+
);
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8292
|
+
function registerAgentInfoCommand(parent) {
|
|
8293
|
+
const cmd = parent.command("info <agentId>").description("Show detailed agent information").option("--json", "Output as JSON");
|
|
8294
|
+
addAuthOptions(cmd).action(async (agentId, options) => {
|
|
8295
|
+
await executeInfo(agentId, options);
|
|
8296
|
+
});
|
|
8297
|
+
}
|
|
8298
|
+
async function executeDm(agentAddress, message, options) {
|
|
8299
|
+
try {
|
|
8300
|
+
if (!isAddress(agentAddress)) {
|
|
8301
|
+
exitWithError(`Invalid agent address: ${agentAddress}`);
|
|
8302
|
+
}
|
|
8303
|
+
if (options.topicSignature && !options.topic) {
|
|
8304
|
+
exitWithError(
|
|
8305
|
+
"--topic-signature requires --topic (the topic the signature authorizes)"
|
|
8306
|
+
);
|
|
8307
|
+
}
|
|
8308
|
+
const usingSessionToken = !!(options.sessionToken || process.env.NET_SESSION_TOKEN);
|
|
8309
|
+
if (usingSessionToken && (!options.topic || !options.topicSignature)) {
|
|
8310
|
+
exitWithError(
|
|
8311
|
+
"When using --session-token, you must also provide --topic and --topic-signature.\n Obtain the signature with:\n netp agent dm-auth-encode --agent-address <addr> \u2192 produces { typedData, topic }\n [sign .typedData with your external signer, e.g. Bankr /agent/sign]\n Then:\n netp agent dm <addr> <message> --session-token <token> --operator <addr> \\\n --topic <topic> --topic-signature <sig>"
|
|
8312
|
+
);
|
|
8313
|
+
}
|
|
8314
|
+
const auth = await resolveAuth(options);
|
|
8315
|
+
const topic = options.topic ?? generateAgentChatTopic(agentAddress);
|
|
8316
|
+
const isNewConversation = !options.topic;
|
|
8317
|
+
console.log(
|
|
8318
|
+
chalk4.blue(
|
|
8319
|
+
isNewConversation ? `Starting new conversation with ${agentAddress}...` : `Continuing conversation ${topic}...`
|
|
8320
|
+
)
|
|
8321
|
+
);
|
|
8322
|
+
const result = await auth.client.sendMessage(
|
|
8323
|
+
{
|
|
8324
|
+
sessionToken: auth.sessionToken,
|
|
8325
|
+
agentAddress,
|
|
8326
|
+
topic,
|
|
8327
|
+
message,
|
|
8328
|
+
userSignature: options.topicSignature
|
|
8329
|
+
},
|
|
8330
|
+
auth.account
|
|
8331
|
+
);
|
|
8332
|
+
if (options.json) {
|
|
8333
|
+
console.log(jsonStringify(result));
|
|
8334
|
+
return;
|
|
8335
|
+
}
|
|
8336
|
+
console.log();
|
|
8337
|
+
console.log(chalk4.cyan("You: ") + message);
|
|
8338
|
+
console.log(chalk4.green("Agent: ") + result.aiMessage);
|
|
8339
|
+
console.log();
|
|
8340
|
+
console.log(chalk4.gray(` Topic: ${result.topic}`));
|
|
8341
|
+
console.log(chalk4.gray(` TX: ${result.transactionHash}`));
|
|
8342
|
+
if (isNewConversation) {
|
|
8343
|
+
console.log(
|
|
8344
|
+
chalk4.gray(` (Use --topic ${result.topic} to continue this conversation)`)
|
|
8345
|
+
);
|
|
8346
|
+
}
|
|
8347
|
+
} catch (error) {
|
|
8348
|
+
exitWithError(
|
|
8349
|
+
`Failed to send DM: ${error instanceof Error ? error.message : String(error)}`
|
|
8350
|
+
);
|
|
8351
|
+
}
|
|
8352
|
+
}
|
|
8353
|
+
function registerAgentDmCommand(parent) {
|
|
8354
|
+
const cmd = parent.command("dm <agentAddress> <message>").description("Send a DM to an onchain agent").option("--topic <topic>", "Continue an existing conversation").option(
|
|
8355
|
+
"--topic-signature <hex>",
|
|
8356
|
+
"Pre-signed ConversationAuth signature (requires --topic). Obtain via `agent dm-auth-encode` + external signer."
|
|
8357
|
+
).option("--json", "Output as JSON");
|
|
8358
|
+
addAuthOptions(cmd).action(async (agentAddress, message, options) => {
|
|
8359
|
+
await executeDm(agentAddress, message, options);
|
|
8360
|
+
});
|
|
8361
|
+
}
|
|
8362
|
+
async function executeDmList(options) {
|
|
8363
|
+
try {
|
|
8364
|
+
const { chainId, apiUrl, operator } = resolveReadOnly(options);
|
|
8365
|
+
if (!operator) {
|
|
8366
|
+
exitWithError(
|
|
8367
|
+
"--operator <address> is required (user wallet address to list conversations for)"
|
|
8368
|
+
);
|
|
8369
|
+
}
|
|
8370
|
+
const client = new AgentClient({ apiUrl, chainId });
|
|
8371
|
+
console.log(chalk4.blue("Loading conversations..."));
|
|
8372
|
+
const conversations = await client.listConversations(operator, {
|
|
8373
|
+
limit: options.limit
|
|
8374
|
+
});
|
|
8375
|
+
const agentConversations = conversations.filter((c) => isAgentChatTopic(c.topic));
|
|
8376
|
+
if (options.json) {
|
|
8377
|
+
console.log(jsonStringify(agentConversations));
|
|
8378
|
+
return;
|
|
8379
|
+
}
|
|
8380
|
+
if (agentConversations.length === 0) {
|
|
8381
|
+
console.log(chalk4.yellow("No agent conversations found."));
|
|
8382
|
+
return;
|
|
8383
|
+
}
|
|
8384
|
+
console.log(chalk4.bold(`Agent Conversations (${agentConversations.length}):
|
|
8385
|
+
`));
|
|
8386
|
+
for (const conv of agentConversations) {
|
|
8387
|
+
const agentAddr = parseAgentAddressFromTopic(conv.topic);
|
|
8388
|
+
const encrypted = conv.isEncrypted ? chalk4.yellow(" [encrypted]") : "";
|
|
8389
|
+
const date = new Date(conv.lastMessageTimestamp * 1e3).toLocaleString();
|
|
8390
|
+
const preview = conv.lastMessage ? conv.lastMessage.length > 60 ? `${conv.lastMessage.slice(0, 60)}...` : conv.lastMessage : null;
|
|
8391
|
+
console.log(` ${chalk4.cyan(agentAddr || "unknown")}${encrypted}`);
|
|
8392
|
+
console.log(` Topic: ${conv.topic}`);
|
|
8393
|
+
console.log(` Messages: ${conv.messageCount}`);
|
|
8394
|
+
console.log(` Last: ${date}`);
|
|
8395
|
+
if (preview) console.log(` Preview: ${preview}`);
|
|
8396
|
+
console.log();
|
|
8397
|
+
}
|
|
8398
|
+
} catch (error) {
|
|
8399
|
+
exitWithError(
|
|
8400
|
+
`Failed to list conversations: ${error instanceof Error ? error.message : String(error)}`
|
|
8401
|
+
);
|
|
8402
|
+
}
|
|
8403
|
+
}
|
|
8404
|
+
function registerAgentDmListCommand(parent) {
|
|
8405
|
+
const cmd = parent.command("dm-list").description("List your DM conversations with agents (pure chain read)").requiredOption("--operator <address>", "User wallet address").option("--limit <n>", "Max conversations to fetch", (v) => parseInt(v, 10)).option("--json", "Output as JSON");
|
|
8406
|
+
addCommonOptions(cmd).action(async (options) => {
|
|
8407
|
+
await executeDmList(options);
|
|
8408
|
+
});
|
|
8409
|
+
}
|
|
8410
|
+
async function executeDmHistory(topic, options) {
|
|
8411
|
+
try {
|
|
8412
|
+
const { chainId, apiUrl, operator } = resolveReadOnly(options);
|
|
8413
|
+
if (!operator) {
|
|
8414
|
+
exitWithError(
|
|
8415
|
+
"--operator <address> is required (user wallet address for this conversation)"
|
|
8416
|
+
);
|
|
8417
|
+
}
|
|
8418
|
+
const client = new AgentClient({ apiUrl, chainId });
|
|
8419
|
+
console.log(chalk4.blue("Loading conversation history..."));
|
|
8420
|
+
const messages = await client.getConversationHistory(operator, topic, {
|
|
8421
|
+
limit: options.limit
|
|
8422
|
+
});
|
|
8423
|
+
if (options.json) {
|
|
8424
|
+
console.log(jsonStringify(messages));
|
|
8425
|
+
return;
|
|
8426
|
+
}
|
|
8427
|
+
if (messages.length === 0) {
|
|
8428
|
+
console.log(chalk4.yellow("No messages found."));
|
|
8429
|
+
return;
|
|
8430
|
+
}
|
|
8431
|
+
const agentAddr = parseAgentAddressFromTopic(topic);
|
|
8432
|
+
console.log(
|
|
8433
|
+
chalk4.bold(
|
|
8434
|
+
`Conversation with ${agentAddr || topic} (${messages.length} messages):
|
|
8435
|
+
`
|
|
8436
|
+
)
|
|
8437
|
+
);
|
|
8438
|
+
for (const msg of messages) {
|
|
8439
|
+
const date = new Date(msg.timestamp * 1e3).toLocaleString();
|
|
8440
|
+
const prefix = msg.sender === "user" ? chalk4.cyan("You") : chalk4.green("Agent");
|
|
8441
|
+
const encrypted = msg.encrypted ? chalk4.yellow(" [encrypted]") : "";
|
|
8442
|
+
console.log(` ${prefix}${encrypted} (${chalk4.gray(date)}):`);
|
|
8443
|
+
console.log(` ${msg.text}`);
|
|
8444
|
+
console.log();
|
|
8445
|
+
}
|
|
8446
|
+
} catch (error) {
|
|
8447
|
+
exitWithError(
|
|
8448
|
+
`Failed to load history: ${error instanceof Error ? error.message : String(error)}`
|
|
8449
|
+
);
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
function registerAgentDmHistoryCommand(parent) {
|
|
8453
|
+
const cmd = parent.command("dm-history <topic>").description("Read DM conversation history (pure chain read)").requiredOption(
|
|
8454
|
+
"--operator <address>",
|
|
8455
|
+
"User wallet address for this conversation"
|
|
8456
|
+
).option("--limit <n>", "Max recent messages to fetch", (v) => parseInt(v, 10)).option("--json", "Output as JSON");
|
|
8457
|
+
addCommonOptions(cmd).action(async (topic, options) => {
|
|
8458
|
+
await executeDmHistory(topic, options);
|
|
8459
|
+
});
|
|
8460
|
+
}
|
|
8461
|
+
async function executeDmAuthEncode(options) {
|
|
8462
|
+
try {
|
|
8463
|
+
const { chainId } = parseReadOnlyOptionsWithDefault({
|
|
8464
|
+
chainId: options.chainId
|
|
8465
|
+
});
|
|
8466
|
+
let topic = options.topic;
|
|
8467
|
+
if (!topic) {
|
|
8468
|
+
if (!options.agentAddress) {
|
|
8469
|
+
exitWithError(
|
|
8470
|
+
"Must provide either --topic or --agent-address to generate a topic"
|
|
8471
|
+
);
|
|
8472
|
+
}
|
|
8473
|
+
if (!isAddress(options.agentAddress)) {
|
|
8474
|
+
exitWithError(`Invalid agent address: ${options.agentAddress}`);
|
|
8475
|
+
}
|
|
8476
|
+
topic = generateAgentChatTopic(options.agentAddress);
|
|
8477
|
+
}
|
|
8478
|
+
const result = buildConversationAuthTypedData({ topic, chainId });
|
|
8479
|
+
console.log(jsonStringify(result));
|
|
8480
|
+
} catch (error) {
|
|
8481
|
+
exitWithError(
|
|
8482
|
+
`dm-auth-encode failed: ${error instanceof Error ? error.message : String(error)}`
|
|
8483
|
+
);
|
|
8484
|
+
}
|
|
8485
|
+
}
|
|
8486
|
+
function registerAgentDmAuthEncodeCommand(parent) {
|
|
8487
|
+
parent.command("dm-auth-encode").description(
|
|
8488
|
+
"Emit { typedData, topic } for external signing. Pipe .typedData to your signer, pass .topic + the resulting signature to `agent dm --topic ... --topic-signature ...`."
|
|
8489
|
+
).option(
|
|
8490
|
+
"--topic <topic>",
|
|
8491
|
+
"Existing topic to authorize (e.g. agent-chat-0x...-nanoid). If omitted, a new topic is generated from --agent-address."
|
|
8492
|
+
).option(
|
|
8493
|
+
"--agent-address <address>",
|
|
8494
|
+
"Agent address \u2014 used to generate a new topic when --topic is omitted"
|
|
8495
|
+
).option("--chain-id <id>", "Chain ID (default: 8453)", (v) => parseInt(v, 10)).action(async (options) => {
|
|
8496
|
+
await executeDmAuthEncode(options);
|
|
8497
|
+
});
|
|
8498
|
+
}
|
|
8499
|
+
async function executeSessionEncode(options) {
|
|
8500
|
+
try {
|
|
8501
|
+
if (!isAddress(options.operator)) {
|
|
8502
|
+
exitWithError(`Invalid operator address: ${options.operator}`);
|
|
8503
|
+
}
|
|
8504
|
+
const { chainId } = parseReadOnlyOptionsWithDefault({
|
|
8505
|
+
chainId: options.chainId
|
|
8506
|
+
});
|
|
8507
|
+
const result = buildSessionTypedData({
|
|
8508
|
+
operatorAddress: options.operator,
|
|
8509
|
+
chainId,
|
|
8510
|
+
expiresIn: options.expiresIn
|
|
8511
|
+
});
|
|
8512
|
+
console.log(jsonStringify(result));
|
|
8513
|
+
} catch (error) {
|
|
8514
|
+
exitWithError(
|
|
8515
|
+
`session-encode failed: ${error instanceof Error ? error.message : String(error)}`
|
|
8516
|
+
);
|
|
8517
|
+
}
|
|
8518
|
+
}
|
|
8519
|
+
function registerAgentSessionEncodeCommand(parent) {
|
|
8520
|
+
parent.command("session-encode").description(
|
|
8521
|
+
"Emit { typedData, expiresAt } for external signing (e.g., Bankr). Pipe .typedData to your signer, pass .expiresAt + the resulting signature to `agent session-create`."
|
|
8522
|
+
).requiredOption(
|
|
8523
|
+
"--operator <address>",
|
|
8524
|
+
"Address that will sign the session (must match signer)"
|
|
8525
|
+
).option("--chain-id <id>", "Chain ID (default: 8453)", (v) => parseInt(v, 10)).option(
|
|
8526
|
+
"--expires-in <seconds>",
|
|
8527
|
+
"Session lifetime in seconds (default: 3600, max: 86400)",
|
|
8528
|
+
(v) => parseInt(v, 10)
|
|
8529
|
+
).action(async (options) => {
|
|
8530
|
+
await executeSessionEncode(options);
|
|
8531
|
+
});
|
|
8532
|
+
}
|
|
8533
|
+
async function executeSessionCreate(options) {
|
|
8534
|
+
try {
|
|
8535
|
+
if (!isAddress(options.operator)) {
|
|
8536
|
+
exitWithError(`Invalid operator address: ${options.operator}`);
|
|
8537
|
+
}
|
|
8538
|
+
if (!options.signature.startsWith("0x")) {
|
|
8539
|
+
exitWithError("--signature must be a 0x-prefixed hex string");
|
|
8540
|
+
}
|
|
8541
|
+
if (!options.expiresAt || !Number.isFinite(options.expiresAt)) {
|
|
8542
|
+
exitWithError("--expires-at must be a unix timestamp (seconds)");
|
|
8543
|
+
}
|
|
8544
|
+
const { chainId } = parseReadOnlyOptionsWithDefault({
|
|
8545
|
+
chainId: options.chainId
|
|
8546
|
+
});
|
|
8547
|
+
const apiUrl = options.apiUrl || process.env.NET_API_URL || NET_API_URL;
|
|
8548
|
+
const result = await exchangeSessionSignature({
|
|
8549
|
+
apiUrl,
|
|
8550
|
+
chainId,
|
|
8551
|
+
operatorAddress: options.operator,
|
|
8552
|
+
signature: options.signature,
|
|
8553
|
+
expiresAt: options.expiresAt
|
|
8554
|
+
});
|
|
8555
|
+
console.log(jsonStringify(result));
|
|
8556
|
+
} catch (error) {
|
|
8557
|
+
exitWithError(
|
|
8558
|
+
`session-create failed: ${error instanceof Error ? error.message : String(error)}`
|
|
8559
|
+
);
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8562
|
+
function registerAgentSessionCreateCommand(parent) {
|
|
8563
|
+
parent.command("session-create").description(
|
|
8564
|
+
"Exchange an externally-produced signature for a session token. Pair with `agent session-encode`."
|
|
8565
|
+
).requiredOption(
|
|
8566
|
+
"--operator <address>",
|
|
8567
|
+
"Address that produced the signature (must match the ecrecover result)"
|
|
8568
|
+
).requiredOption("--signature <hex>", "EIP-712 signature over the session typed data").requiredOption(
|
|
8569
|
+
"--expires-at <timestamp>",
|
|
8570
|
+
"expiresAt value from session-encode (unix seconds)",
|
|
8571
|
+
(v) => parseInt(v, 10)
|
|
8572
|
+
).option("--chain-id <id>", "Chain ID (default: 8453)", (v) => parseInt(v, 10)).option("--api-url <url>", "Net Protocol API URL").action(async (options) => {
|
|
8573
|
+
await executeSessionCreate(options);
|
|
8574
|
+
});
|
|
8575
|
+
}
|
|
8576
|
+
|
|
8577
|
+
// src/commands/agent/index.ts
|
|
8578
|
+
function registerAgentCommand(program2) {
|
|
8579
|
+
const agentCommand = program2.command("agent").description("Onchain agent operations (create, manage, run, DM)");
|
|
8580
|
+
registerAgentCreateCommand(agentCommand);
|
|
8581
|
+
registerAgentListCommand(agentCommand);
|
|
8582
|
+
registerAgentUpdateCommand(agentCommand);
|
|
8583
|
+
registerAgentHideCommand(agentCommand);
|
|
8584
|
+
registerAgentUnhideCommand(agentCommand);
|
|
8585
|
+
registerAgentRunCommand(agentCommand);
|
|
8586
|
+
registerAgentInfoCommand(agentCommand);
|
|
8587
|
+
registerAgentDmCommand(agentCommand);
|
|
8588
|
+
registerAgentDmListCommand(agentCommand);
|
|
8589
|
+
registerAgentDmHistoryCommand(agentCommand);
|
|
8590
|
+
registerAgentSessionEncodeCommand(agentCommand);
|
|
8591
|
+
registerAgentSessionCreateCommand(agentCommand);
|
|
8592
|
+
registerAgentDmAuthEncodeCommand(agentCommand);
|
|
8593
|
+
}
|
|
8594
|
+
var CHAINS = {
|
|
8595
|
+
8453: base,
|
|
8596
|
+
84532: baseSepolia
|
|
8597
|
+
};
|
|
8598
|
+
function getApiUrl(chainId) {
|
|
8599
|
+
return chainId === 84532 ? NET_TESTNET_API_URL : NET_API_URL;
|
|
8600
|
+
}
|
|
8601
|
+
function registerRelayCommand(program2) {
|
|
8602
|
+
const relayCommand = program2.command("relay").description("Relay operations (fund credits, check balance)");
|
|
8603
|
+
relayCommand.command("fund").description("Add Net credits via x402 USDC payment").option("--amount <usd>", "Amount in USD to fund (default: 0.10)", "0.10").option("--chain-id <id>", "Chain ID (default: 8453)").option("--rpc-url <url>", "Custom RPC URL").option("--private-key <key>", "Private key (0x-prefixed)").option("--json", "Output as JSON").action(async (options) => {
|
|
8604
|
+
const { privateKey, chainId, rpcUrl } = parseCommonOptionsWithDefault({
|
|
8605
|
+
privateKey: options.privateKey,
|
|
8606
|
+
chainId: options.chainId ? parseInt(options.chainId, 10) : void 0,
|
|
8607
|
+
rpcUrl: options.rpcUrl
|
|
8608
|
+
});
|
|
8609
|
+
const amount = parseFloat(options.amount);
|
|
8610
|
+
if (isNaN(amount) || amount <= 0) {
|
|
8611
|
+
exitWithError("Invalid amount. Must be a positive number.");
|
|
8612
|
+
}
|
|
8613
|
+
const chain = CHAINS[chainId];
|
|
8614
|
+
if (!chain) {
|
|
8615
|
+
exitWithError(
|
|
8616
|
+
`Chain ${chainId} not supported for relay funding. Use Base (8453) or Base Sepolia (84532).`
|
|
8617
|
+
);
|
|
8618
|
+
}
|
|
8619
|
+
const account = privateKeyToAccount(privateKey);
|
|
8620
|
+
const rpcUrls = getChainRpcUrls({ chainId, rpcUrl });
|
|
8621
|
+
const publicClient = createPublicClient({
|
|
8622
|
+
chain,
|
|
8623
|
+
transport: http(rpcUrls[0])
|
|
8624
|
+
});
|
|
8625
|
+
const walletClient = createWalletClient({
|
|
8626
|
+
account,
|
|
8627
|
+
chain,
|
|
8628
|
+
transport: http(rpcUrls[0])
|
|
8629
|
+
});
|
|
8630
|
+
const signer = {
|
|
8631
|
+
address: account.address,
|
|
8632
|
+
signTypedData: (msg) => walletClient.signTypedData(msg),
|
|
8633
|
+
readContract: (args) => publicClient.readContract(args),
|
|
8634
|
+
sendTransaction: (args) => walletClient.sendTransaction(args)
|
|
8635
|
+
};
|
|
8636
|
+
const { x402Client, wrapFetchWithPayment, x402HTTPClient } = await import('@x402/fetch');
|
|
8637
|
+
const { registerExactEvmScheme } = await import('@x402/evm/exact/client');
|
|
8638
|
+
const client = new x402Client();
|
|
8639
|
+
registerExactEvmScheme(client, { signer });
|
|
8640
|
+
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
|
|
8641
|
+
const httpClient = new x402HTTPClient(client);
|
|
8642
|
+
const apiUrl = getApiUrl(chainId);
|
|
8643
|
+
if (!options.json) {
|
|
8644
|
+
console.log(
|
|
8645
|
+
`Funding $${amount.toFixed(2)} USDC to relay on chain ${chainId}...`
|
|
8646
|
+
);
|
|
8647
|
+
}
|
|
8648
|
+
try {
|
|
8649
|
+
const result = await fundBackendWallet({
|
|
8650
|
+
apiUrl,
|
|
8651
|
+
chainId,
|
|
8652
|
+
operatorAddress: account.address,
|
|
8653
|
+
secretKey: RELAY_ACCESS_KEY,
|
|
8654
|
+
fetchWithPayment,
|
|
8655
|
+
httpClient,
|
|
8656
|
+
amount
|
|
8657
|
+
});
|
|
8658
|
+
if (options.json) {
|
|
8659
|
+
console.log(
|
|
8660
|
+
JSON.stringify(
|
|
8661
|
+
{
|
|
8662
|
+
success: true,
|
|
8663
|
+
paymentTxHash: result.paymentTxHash,
|
|
8664
|
+
backendWalletAddress: result.backendWalletAddress,
|
|
8665
|
+
amountUsd: amount
|
|
8666
|
+
},
|
|
8667
|
+
null,
|
|
8668
|
+
2
|
|
8669
|
+
)
|
|
8670
|
+
);
|
|
8671
|
+
} else {
|
|
8672
|
+
console.log(chalk4.green(`
|
|
8673
|
+
\u2713 Funded $${amount.toFixed(2)} successfully`));
|
|
8674
|
+
console.log(` Payment tx: ${result.paymentTxHash}`);
|
|
8675
|
+
console.log(
|
|
8676
|
+
` Backend wallet: ${result.backendWalletAddress}`
|
|
8677
|
+
);
|
|
8678
|
+
}
|
|
8679
|
+
} catch (error) {
|
|
8680
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8681
|
+
if (options.json) {
|
|
8682
|
+
console.log(JSON.stringify({ success: false, error: msg }, null, 2));
|
|
8683
|
+
process.exit(1);
|
|
8684
|
+
}
|
|
8685
|
+
exitWithError(`Funding failed: ${msg}`);
|
|
8686
|
+
}
|
|
8687
|
+
});
|
|
8688
|
+
relayCommand.command("balance").description("Check relay backend wallet balance").option("--chain-id <id>", "Chain ID (default: 8453)").option("--private-key <key>", "Private key (0x-prefixed)").option("--json", "Output as JSON").action(async (options) => {
|
|
8689
|
+
const { privateKey, chainId } = parseCommonOptionsWithDefault({
|
|
8690
|
+
privateKey: options.privateKey,
|
|
8691
|
+
chainId: options.chainId ? parseInt(options.chainId, 10) : void 0
|
|
8692
|
+
});
|
|
8693
|
+
const account = privateKeyToAccount(privateKey);
|
|
8694
|
+
const apiUrl = getApiUrl(chainId);
|
|
8695
|
+
try {
|
|
8696
|
+
const result = await checkBackendWalletBalance({
|
|
8697
|
+
apiUrl,
|
|
8698
|
+
chainId,
|
|
8699
|
+
operatorAddress: account.address,
|
|
8700
|
+
secretKey: RELAY_ACCESS_KEY
|
|
8701
|
+
});
|
|
8702
|
+
if (options.json) {
|
|
8703
|
+
console.log(JSON.stringify(result, null, 2));
|
|
8704
|
+
} else {
|
|
8705
|
+
console.log(`Relay Balance`);
|
|
8706
|
+
console.log(` Backend wallet: ${result.backendWalletAddress}`);
|
|
8707
|
+
console.log(` Balance: ${result.balanceEth} ETH`);
|
|
8708
|
+
console.log(
|
|
8709
|
+
` Sufficient: ${result.sufficientBalance ? chalk4.green("Yes") : chalk4.red("No")} (min: ${result.minRequiredEth} ETH)`
|
|
8710
|
+
);
|
|
8711
|
+
}
|
|
8712
|
+
} catch (error) {
|
|
8713
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8714
|
+
if (options.json) {
|
|
8715
|
+
console.log(JSON.stringify({ success: false, error: msg }, null, 2));
|
|
8716
|
+
process.exit(1);
|
|
8717
|
+
}
|
|
8718
|
+
exitWithError(`Balance check failed: ${msg}`);
|
|
8719
|
+
}
|
|
8720
|
+
});
|
|
8721
|
+
}
|
|
7543
8722
|
var CACHE_DIR = join(homedir(), ".netp");
|
|
7544
8723
|
var CACHE_FILE = join(CACHE_DIR, "update-check.json");
|
|
7545
8724
|
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
@@ -7640,6 +8819,8 @@ registerProfileCommand(program);
|
|
|
7640
8819
|
registerBazaarCommand(program);
|
|
7641
8820
|
registerFeedCommand(program);
|
|
7642
8821
|
registerUpvoteCommand(program);
|
|
8822
|
+
registerAgentCommand(program);
|
|
8823
|
+
registerRelayCommand(program);
|
|
7643
8824
|
program.command("update").description("Update netp to the latest version").action(async () => {
|
|
7644
8825
|
const { execSync } = await import('child_process');
|
|
7645
8826
|
console.log("Updating @net-protocol/cli...");
|