@net-protocol/cli 0.1.42 → 0.1.43

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.
@@ -7,15 +7,15 @@ import chalk4 from 'chalk';
7
7
  import * as fs6 from 'fs';
8
8
  import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
9
9
  import { OPTIMAL_CHUNK_SIZE, StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, chunkDataForStorage, CHUNKED_STORAGE_CONTRACT, STORAGE_CONTRACT as STORAGE_CONTRACT$1 } from '@net-protocol/storage';
10
- import { stringToHex, createWalletClient, http, hexToString, parseEther, encodeFunctionData, publicActions, concat, defineChain, decodeEventLog, createPublicClient, formatEther } from 'viem';
10
+ import { createPublicClient, http, createWalletClient, stringToHex, hexToString, parseEther, encodeFunctionData, publicActions, concat, defineChain, decodeEventLog, formatEther, isAddress } from 'viem';
11
11
  import { privateKeyToAccount } from 'viem/accounts';
12
- import { getNetContract, getChainName, getPublicClient, getChainRpcUrls, getBaseDataSuffix, NetClient, toBytes32, NULL_ADDRESS } from '@net-protocol/core';
13
- import { createRelayX402Client, createRelaySession, checkBackendWalletBalance, fundBackendWallet, batchTransactions, submitTransactionsViaRelay, waitForConfirmations, retryFailedTransactions as retryFailedTransactions$1 } from '@net-protocol/relay';
12
+ import { getNetContract, getChainName, getChainRpcUrls, getPublicClient, getBaseDataSuffix, NetClient, toBytes32, NULL_ADDRESS } from '@net-protocol/core';
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) {
@@ -1566,8 +1567,8 @@ function registerStorageCommand(program2) {
1566
1567
  console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
1567
1568
  console.log(chalk4.blue(`\u{1F517} Using relay API: ${options.apiUrl}`));
1568
1569
  const result = await uploadFileWithRelay(uploadRelayOptions);
1569
- const { privateKeyToAccount: privateKeyToAccount26 } = await import('viem/accounts');
1570
- const userAccount = privateKeyToAccount26(commonOptions.privateKey);
1570
+ const { privateKeyToAccount: privateKeyToAccount28 } = await import('viem/accounts');
1571
+ const userAccount = privateKeyToAccount28(commonOptions.privateKey);
1571
1572
  const storageUrl = generateStorageUrl(
1572
1573
  userAccount.address,
1573
1574
  commonOptions.chainId,
@@ -7540,6 +7541,845 @@ function registerUpvoteCommand(program2) {
7540
7541
  registerUpvoteUserCommand(upvoteCommand);
7541
7542
  registerGetUserUpvotesCommand(upvoteCommand);
7542
7543
  }
7544
+ var VALID_RUN_MODES = ["auto", "feeds", "chats"];
7545
+ function addAuthOptions(cmd) {
7546
+ return addCommonOptions(cmd).option("--private-key <key>", "Private key (0x-prefixed)").option(
7547
+ "--session-token <token>",
7548
+ "Pre-existing session token (alternative to --private-key)"
7549
+ ).option(
7550
+ "--operator <address>",
7551
+ "Operator address (required with --session-token)"
7552
+ );
7553
+ }
7554
+ function addCommonOptions(cmd) {
7555
+ 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");
7556
+ }
7557
+ function addFilterOptions(cmd) {
7558
+ 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");
7559
+ }
7560
+ function addProfileOptions(cmd) {
7561
+ return cmd.option("--display-name <name>", "Agent display name").option("--bio <text>", "Agent bio");
7562
+ }
7563
+ function buildFilters(options) {
7564
+ const filters = {};
7565
+ if (options.includeFeed?.length) filters.includeFeedPatterns = options.includeFeed;
7566
+ if (options.excludeFeed?.length) filters.excludeFeedPatterns = options.excludeFeed;
7567
+ if (options.preferredFeed?.length) filters.preferredFeedPatterns = options.preferredFeed;
7568
+ if (options.chatTopic?.length) filters.preferredChatTopics = options.chatTopic;
7569
+ return Object.keys(filters).length > 0 ? filters : void 0;
7570
+ }
7571
+ function buildProfile(options) {
7572
+ const profile = {};
7573
+ if (options.displayName) profile.displayName = options.displayName;
7574
+ if (options.bio) profile.bio = options.bio;
7575
+ return Object.keys(profile).length > 0 ? profile : void 0;
7576
+ }
7577
+ function parseRunMode(raw) {
7578
+ const mode = raw ?? "auto";
7579
+ if (!VALID_RUN_MODES.includes(mode)) {
7580
+ exitWithError(`Invalid mode "${mode}". Must be one of: ${VALID_RUN_MODES.join(", ")}`);
7581
+ }
7582
+ return mode;
7583
+ }
7584
+ function resolveReadOnly(options) {
7585
+ const readOnly = parseReadOnlyOptionsWithDefault({
7586
+ chainId: options.chainId,
7587
+ rpcUrl: options.rpcUrl
7588
+ });
7589
+ const apiUrl = options.apiUrl || process.env.NET_API_URL || NET_API_URL;
7590
+ let operator;
7591
+ if (options.operator) {
7592
+ if (!isAddress(options.operator)) {
7593
+ exitWithError(`Invalid operator address: ${options.operator}`);
7594
+ }
7595
+ operator = options.operator;
7596
+ }
7597
+ return { chainId: readOnly.chainId, apiUrl, operator };
7598
+ }
7599
+ async function resolveAuth(options) {
7600
+ const sessionToken = options.sessionToken || process.env.NET_SESSION_TOKEN;
7601
+ const privateKey = options.privateKey || process.env.NET_PRIVATE_KEY || process.env.PRIVATE_KEY;
7602
+ if (sessionToken && options.privateKey) {
7603
+ exitWithError("Cannot use both --session-token and --private-key. Pick one.");
7604
+ }
7605
+ const apiUrl = options.apiUrl || process.env.NET_API_URL || NET_API_URL;
7606
+ if (sessionToken) {
7607
+ if (!options.operator) {
7608
+ exitWithError(
7609
+ "--operator <address> is required when using --session-token. It must match the address that signed the session."
7610
+ );
7611
+ }
7612
+ if (!isAddress(options.operator)) {
7613
+ exitWithError(`Invalid operator address: ${options.operator}`);
7614
+ }
7615
+ const readOnly = parseReadOnlyOptionsWithDefault({
7616
+ chainId: options.chainId,
7617
+ rpcUrl: options.rpcUrl
7618
+ });
7619
+ const client2 = new AgentClient({ apiUrl, chainId: readOnly.chainId });
7620
+ return {
7621
+ client: client2,
7622
+ sessionToken,
7623
+ operatorAddress: options.operator,
7624
+ chainId: readOnly.chainId,
7625
+ apiUrl
7626
+ };
7627
+ }
7628
+ if (!privateKey) {
7629
+ exitWithError(
7630
+ "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)"
7631
+ );
7632
+ }
7633
+ const commonOptions = parseCommonOptionsWithDefault({
7634
+ privateKey,
7635
+ chainId: options.chainId,
7636
+ rpcUrl: options.rpcUrl
7637
+ });
7638
+ const account = privateKeyToAccount(commonOptions.privateKey);
7639
+ console.log(chalk4.blue("Creating session..."));
7640
+ const { sessionToken: token } = await createRelaySession({
7641
+ apiUrl,
7642
+ chainId: commonOptions.chainId,
7643
+ operatorAddress: account.address,
7644
+ secretKey: RELAY_ACCESS_KEY,
7645
+ account
7646
+ });
7647
+ const client = new AgentClient({ apiUrl, chainId: commonOptions.chainId });
7648
+ return {
7649
+ client,
7650
+ sessionToken: token,
7651
+ account,
7652
+ operatorAddress: account.address,
7653
+ chainId: commonOptions.chainId,
7654
+ apiUrl
7655
+ };
7656
+ }
7657
+ function bigintReplacer5(_key, value) {
7658
+ return typeof value === "bigint" ? value.toString() : value;
7659
+ }
7660
+ function jsonStringify(obj, indent = 2) {
7661
+ return JSON.stringify(obj, bigintReplacer5, indent);
7662
+ }
7663
+
7664
+ // src/commands/agent/create.ts
7665
+ async function executeCreate(name, options) {
7666
+ try {
7667
+ const auth = await resolveAuth(options);
7668
+ const config = {
7669
+ name,
7670
+ systemPrompt: options.systemPrompt
7671
+ };
7672
+ if (options.schedule) config.runIntervalMinutes = options.schedule;
7673
+ const filters = buildFilters(options);
7674
+ if (filters) config.filters = filters;
7675
+ const profile = buildProfile(options);
7676
+ console.log(chalk4.blue(`Creating agent "${name}"...`));
7677
+ const result = await auth.client.createAgent({
7678
+ sessionToken: auth.sessionToken,
7679
+ config,
7680
+ profile
7681
+ });
7682
+ if (!result.success) {
7683
+ exitWithError(result.error || "Failed to create agent");
7684
+ }
7685
+ if (options.json) {
7686
+ console.log(jsonStringify(result));
7687
+ return;
7688
+ }
7689
+ console.log(chalk4.green("Agent created successfully!"));
7690
+ console.log(` Agent ID: ${result.agentId}`);
7691
+ console.log(` Wallet: ${result.agentWalletAddress}`);
7692
+ if (result.scheduleError) {
7693
+ console.log(chalk4.yellow(` Schedule warning: ${result.scheduleError}`));
7694
+ }
7695
+ } catch (error) {
7696
+ exitWithError(
7697
+ `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`
7698
+ );
7699
+ }
7700
+ }
7701
+ function registerAgentCreateCommand(parent) {
7702
+ 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");
7703
+ addAuthOptions(cmd);
7704
+ addFilterOptions(cmd);
7705
+ addProfileOptions(cmd);
7706
+ cmd.action(async (name, options) => {
7707
+ await executeCreate(name, options);
7708
+ });
7709
+ }
7710
+ async function executeList(options) {
7711
+ try {
7712
+ const auth = await resolveAuth(options);
7713
+ const result = await auth.client.listAgents({ sessionToken: auth.sessionToken });
7714
+ if (!result.success) {
7715
+ exitWithError(result.error || "Failed to list agents");
7716
+ }
7717
+ const agents = result.agents || [];
7718
+ const visible = options.showHidden ? agents : agents.filter((a) => !a.config.hidden);
7719
+ if (options.json) {
7720
+ console.log(jsonStringify(visible));
7721
+ return;
7722
+ }
7723
+ if (visible.length === 0) {
7724
+ console.log(chalk4.yellow("No agents found."));
7725
+ return;
7726
+ }
7727
+ console.log(chalk4.bold(`Agents (${visible.length}):
7728
+ `));
7729
+ for (const agent of visible) {
7730
+ const { config, walletAddress } = agent;
7731
+ const schedule = config.runIntervalMinutes ? `every ${config.runIntervalMinutes}m` : "manual";
7732
+ const hidden = config.hidden ? chalk4.gray(" [hidden]") : "";
7733
+ const promptPreview = config.systemPrompt.length > 80 ? `${config.systemPrompt.slice(0, 80)}...` : config.systemPrompt;
7734
+ console.log(` ${chalk4.cyan(config.name)}${hidden}`);
7735
+ console.log(` ID: ${config.id}`);
7736
+ console.log(` Wallet: ${walletAddress}`);
7737
+ console.log(` Schedule: ${schedule}`);
7738
+ console.log(` Prompt: ${promptPreview}`);
7739
+ console.log();
7740
+ }
7741
+ } catch (error) {
7742
+ exitWithError(
7743
+ `Failed to list agents: ${error instanceof Error ? error.message : String(error)}`
7744
+ );
7745
+ }
7746
+ }
7747
+ function registerAgentListCommand(parent) {
7748
+ const cmd = parent.command("list").description("List your onchain agents").option("--json", "Output as JSON").option("--show-hidden", "Include hidden agents");
7749
+ addAuthOptions(cmd);
7750
+ cmd.action(async (options) => {
7751
+ await executeList(options);
7752
+ });
7753
+ }
7754
+ async function executeUpdate(agentId, options) {
7755
+ try {
7756
+ const auth = await resolveAuth(options);
7757
+ const config = {};
7758
+ let hasConfigChanges = false;
7759
+ if (options.name) {
7760
+ config.name = options.name;
7761
+ hasConfigChanges = true;
7762
+ }
7763
+ if (options.systemPrompt) {
7764
+ config.systemPrompt = options.systemPrompt;
7765
+ hasConfigChanges = true;
7766
+ }
7767
+ if (options.schedule) {
7768
+ config.runIntervalMinutes = options.schedule;
7769
+ hasConfigChanges = true;
7770
+ }
7771
+ if (options.disableSchedule) {
7772
+ config.runIntervalMinutes = 0;
7773
+ hasConfigChanges = true;
7774
+ }
7775
+ const filters = buildFilters(options);
7776
+ if (filters) {
7777
+ config.filters = filters;
7778
+ hasConfigChanges = true;
7779
+ }
7780
+ const profile = buildProfile(options);
7781
+ if (!hasConfigChanges && !profile) {
7782
+ exitWithError(
7783
+ "No changes specified. Use --name, --system-prompt, --schedule, --display-name, --bio, or filter options."
7784
+ );
7785
+ }
7786
+ console.log(chalk4.blue(`Updating agent ${agentId}...`));
7787
+ const result = await auth.client.updateAgent({
7788
+ sessionToken: auth.sessionToken,
7789
+ agentId,
7790
+ config: hasConfigChanges ? config : void 0,
7791
+ profile
7792
+ });
7793
+ if (!result.success) {
7794
+ exitWithError(result.error || "Failed to update agent");
7795
+ }
7796
+ if (options.json) {
7797
+ console.log(jsonStringify(result));
7798
+ return;
7799
+ }
7800
+ console.log(chalk4.green("Agent updated successfully!"));
7801
+ if (result.profileError) {
7802
+ console.log(chalk4.yellow(` Profile warning: ${result.profileError}`));
7803
+ }
7804
+ } catch (error) {
7805
+ exitWithError(
7806
+ `Failed to update agent: ${error instanceof Error ? error.message : String(error)}`
7807
+ );
7808
+ }
7809
+ }
7810
+ function registerAgentUpdateCommand(parent) {
7811
+ 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");
7812
+ addAuthOptions(cmd);
7813
+ addFilterOptions(cmd);
7814
+ addProfileOptions(cmd);
7815
+ cmd.action(async (agentId, options) => {
7816
+ await executeUpdate(agentId, options);
7817
+ });
7818
+ }
7819
+ async function executeToggleHidden(agentId, options, hide) {
7820
+ const verb = hide ? "Hiding" : "Unhiding";
7821
+ const past = hide ? "hidden" : "unhidden";
7822
+ try {
7823
+ const auth = await resolveAuth(options);
7824
+ console.log(chalk4.blue(`${verb} agent ${agentId}...`));
7825
+ const result = hide ? await auth.client.hideAgent(auth.sessionToken, agentId) : await auth.client.unhideAgent(auth.sessionToken, agentId);
7826
+ if (!result.success) {
7827
+ exitWithError(result.error || `Failed to ${hide ? "hide" : "unhide"} agent`);
7828
+ }
7829
+ console.log(chalk4.green(`Agent ${past} successfully.`));
7830
+ } catch (error) {
7831
+ exitWithError(
7832
+ `Failed to ${hide ? "hide" : "unhide"} agent: ${error instanceof Error ? error.message : String(error)}`
7833
+ );
7834
+ }
7835
+ }
7836
+ function registerAgentHideCommand(parent) {
7837
+ const cmd = parent.command("hide <agentId>").description("Hide an agent (soft-delete)");
7838
+ addAuthOptions(cmd).action(async (agentId, options) => {
7839
+ await executeToggleHidden(agentId, options, true);
7840
+ });
7841
+ }
7842
+ function registerAgentUnhideCommand(parent) {
7843
+ const cmd = parent.command("unhide <agentId>").description("Unhide a previously hidden agent");
7844
+ addAuthOptions(cmd).action(async (agentId, options) => {
7845
+ await executeToggleHidden(agentId, options, false);
7846
+ });
7847
+ }
7848
+ async function executeRun(agentId, options) {
7849
+ try {
7850
+ const auth = await resolveAuth(options);
7851
+ const mode = parseRunMode(options.mode);
7852
+ console.log(chalk4.blue(`Running agent ${agentId} (mode: ${mode})...`));
7853
+ const result = await auth.client.runAgent({
7854
+ sessionToken: auth.sessionToken,
7855
+ agentId,
7856
+ mode
7857
+ });
7858
+ if (options.json) {
7859
+ console.log(jsonStringify(result));
7860
+ return;
7861
+ }
7862
+ if (!result.success) {
7863
+ exitWithError(result.error || "Agent run failed");
7864
+ }
7865
+ console.log(chalk4.green(`Agent run complete: ${result.action}`));
7866
+ if (result.summary) {
7867
+ console.log(` Summary: ${result.summary}`);
7868
+ }
7869
+ if (result.actions.length > 0) {
7870
+ console.log(" Actions:");
7871
+ for (const action of result.actions) {
7872
+ const textPreview = action.text.length > 60 ? `${action.text.slice(0, 60)}...` : action.text;
7873
+ console.log(` - ${action.type} in ${action.topic}: "${textPreview}"`);
7874
+ console.log(` tx: ${action.transactionHash}`);
7875
+ }
7876
+ }
7877
+ if (result.autoFunded) {
7878
+ console.log(
7879
+ chalk4.blue(
7880
+ ` Auto-funded: $${result.autoFunded.amountUsd.toFixed(4)} (${result.autoFunded.amountEth} ETH)`
7881
+ )
7882
+ );
7883
+ }
7884
+ if (result.agentBalanceUsd !== void 0) {
7885
+ console.log(` Agent balance: $${result.agentBalanceUsd.toFixed(4)}`);
7886
+ }
7887
+ if (result.mainBalanceUsd !== void 0) {
7888
+ console.log(` Credits balance: $${result.mainBalanceUsd.toFixed(4)}`);
7889
+ }
7890
+ } catch (error) {
7891
+ exitWithError(
7892
+ `Failed to run agent: ${error instanceof Error ? error.message : String(error)}`
7893
+ );
7894
+ }
7895
+ }
7896
+ function registerAgentRunCommand(parent) {
7897
+ 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");
7898
+ addAuthOptions(cmd).action(async (agentId, options) => {
7899
+ await executeRun(agentId, options);
7900
+ });
7901
+ }
7902
+ async function executeInfo(agentId, options) {
7903
+ try {
7904
+ const auth = await resolveAuth(options);
7905
+ const agent = await auth.client.getAgent(auth.sessionToken, agentId);
7906
+ if (!agent) {
7907
+ exitWithError(`Agent not found: ${agentId}`);
7908
+ return;
7909
+ }
7910
+ if (options.json) {
7911
+ console.log(jsonStringify(agent));
7912
+ return;
7913
+ }
7914
+ const { config, walletAddress } = agent;
7915
+ const schedule = config.runIntervalMinutes ? `every ${config.runIntervalMinutes} minutes` : "manual only";
7916
+ console.log(
7917
+ chalk4.bold(config.name) + (config.hidden ? chalk4.gray(" [hidden]") : "")
7918
+ );
7919
+ console.log();
7920
+ console.log(` ID: ${config.id}`);
7921
+ console.log(` Wallet: ${walletAddress}`);
7922
+ console.log(` Owner: ${config.ownerAddress}`);
7923
+ console.log(` Schedule: ${schedule}`);
7924
+ console.log(` Created: ${new Date(config.createdAt).toLocaleString()}`);
7925
+ console.log(` Updated: ${new Date(config.updatedAt).toLocaleString()}`);
7926
+ console.log();
7927
+ console.log(" System Prompt:");
7928
+ console.log(` ${config.systemPrompt}`);
7929
+ if (config.filters) {
7930
+ console.log();
7931
+ console.log(" Filters:");
7932
+ if (config.filters.includeFeedPatterns?.length) {
7933
+ console.log(` Include feeds: ${config.filters.includeFeedPatterns.join(", ")}`);
7934
+ }
7935
+ if (config.filters.excludeFeedPatterns?.length) {
7936
+ console.log(` Exclude feeds: ${config.filters.excludeFeedPatterns.join(", ")}`);
7937
+ }
7938
+ if (config.filters.preferredFeedPatterns?.length) {
7939
+ console.log(
7940
+ ` Preferred feeds: ${config.filters.preferredFeedPatterns.join(", ")}`
7941
+ );
7942
+ }
7943
+ if (config.filters.preferredChatTopics?.length) {
7944
+ console.log(` Chat topics: ${config.filters.preferredChatTopics.join(", ")}`);
7945
+ }
7946
+ }
7947
+ } catch (error) {
7948
+ exitWithError(
7949
+ `Failed to get agent info: ${error instanceof Error ? error.message : String(error)}`
7950
+ );
7951
+ }
7952
+ }
7953
+ function registerAgentInfoCommand(parent) {
7954
+ const cmd = parent.command("info <agentId>").description("Show detailed agent information").option("--json", "Output as JSON");
7955
+ addAuthOptions(cmd).action(async (agentId, options) => {
7956
+ await executeInfo(agentId, options);
7957
+ });
7958
+ }
7959
+ async function executeDm(agentAddress, message, options) {
7960
+ try {
7961
+ if (!isAddress(agentAddress)) {
7962
+ exitWithError(`Invalid agent address: ${agentAddress}`);
7963
+ }
7964
+ if (options.topicSignature && !options.topic) {
7965
+ exitWithError(
7966
+ "--topic-signature requires --topic (the topic the signature authorizes)"
7967
+ );
7968
+ }
7969
+ const usingSessionToken = !!(options.sessionToken || process.env.NET_SESSION_TOKEN);
7970
+ if (usingSessionToken && (!options.topic || !options.topicSignature)) {
7971
+ exitWithError(
7972
+ "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>"
7973
+ );
7974
+ }
7975
+ const auth = await resolveAuth(options);
7976
+ const topic = options.topic ?? generateAgentChatTopic(agentAddress);
7977
+ const isNewConversation = !options.topic;
7978
+ console.log(
7979
+ chalk4.blue(
7980
+ isNewConversation ? `Starting new conversation with ${agentAddress}...` : `Continuing conversation ${topic}...`
7981
+ )
7982
+ );
7983
+ const result = await auth.client.sendMessage(
7984
+ {
7985
+ sessionToken: auth.sessionToken,
7986
+ agentAddress,
7987
+ topic,
7988
+ message,
7989
+ userSignature: options.topicSignature
7990
+ },
7991
+ auth.account
7992
+ );
7993
+ if (options.json) {
7994
+ console.log(jsonStringify(result));
7995
+ return;
7996
+ }
7997
+ console.log();
7998
+ console.log(chalk4.cyan("You: ") + message);
7999
+ console.log(chalk4.green("Agent: ") + result.aiMessage);
8000
+ console.log();
8001
+ console.log(chalk4.gray(` Topic: ${result.topic}`));
8002
+ console.log(chalk4.gray(` TX: ${result.transactionHash}`));
8003
+ if (isNewConversation) {
8004
+ console.log(
8005
+ chalk4.gray(` (Use --topic ${result.topic} to continue this conversation)`)
8006
+ );
8007
+ }
8008
+ } catch (error) {
8009
+ exitWithError(
8010
+ `Failed to send DM: ${error instanceof Error ? error.message : String(error)}`
8011
+ );
8012
+ }
8013
+ }
8014
+ function registerAgentDmCommand(parent) {
8015
+ const cmd = parent.command("dm <agentAddress> <message>").description("Send a DM to an onchain agent").option("--topic <topic>", "Continue an existing conversation").option(
8016
+ "--topic-signature <hex>",
8017
+ "Pre-signed ConversationAuth signature (requires --topic). Obtain via `agent dm-auth-encode` + external signer."
8018
+ ).option("--json", "Output as JSON");
8019
+ addAuthOptions(cmd).action(async (agentAddress, message, options) => {
8020
+ await executeDm(agentAddress, message, options);
8021
+ });
8022
+ }
8023
+ async function executeDmList(options) {
8024
+ try {
8025
+ const { chainId, apiUrl, operator } = resolveReadOnly(options);
8026
+ if (!operator) {
8027
+ exitWithError(
8028
+ "--operator <address> is required (user wallet address to list conversations for)"
8029
+ );
8030
+ }
8031
+ const client = new AgentClient({ apiUrl, chainId });
8032
+ console.log(chalk4.blue("Loading conversations..."));
8033
+ const conversations = await client.listConversations(operator, {
8034
+ limit: options.limit
8035
+ });
8036
+ const agentConversations = conversations.filter((c) => isAgentChatTopic(c.topic));
8037
+ if (options.json) {
8038
+ console.log(jsonStringify(agentConversations));
8039
+ return;
8040
+ }
8041
+ if (agentConversations.length === 0) {
8042
+ console.log(chalk4.yellow("No agent conversations found."));
8043
+ return;
8044
+ }
8045
+ console.log(chalk4.bold(`Agent Conversations (${agentConversations.length}):
8046
+ `));
8047
+ for (const conv of agentConversations) {
8048
+ const agentAddr = parseAgentAddressFromTopic(conv.topic);
8049
+ const encrypted = conv.isEncrypted ? chalk4.yellow(" [encrypted]") : "";
8050
+ const date = new Date(conv.lastMessageTimestamp * 1e3).toLocaleString();
8051
+ const preview = conv.lastMessage ? conv.lastMessage.length > 60 ? `${conv.lastMessage.slice(0, 60)}...` : conv.lastMessage : null;
8052
+ console.log(` ${chalk4.cyan(agentAddr || "unknown")}${encrypted}`);
8053
+ console.log(` Topic: ${conv.topic}`);
8054
+ console.log(` Messages: ${conv.messageCount}`);
8055
+ console.log(` Last: ${date}`);
8056
+ if (preview) console.log(` Preview: ${preview}`);
8057
+ console.log();
8058
+ }
8059
+ } catch (error) {
8060
+ exitWithError(
8061
+ `Failed to list conversations: ${error instanceof Error ? error.message : String(error)}`
8062
+ );
8063
+ }
8064
+ }
8065
+ function registerAgentDmListCommand(parent) {
8066
+ 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");
8067
+ addCommonOptions(cmd).action(async (options) => {
8068
+ await executeDmList(options);
8069
+ });
8070
+ }
8071
+ async function executeDmHistory(topic, options) {
8072
+ try {
8073
+ const { chainId, apiUrl, operator } = resolveReadOnly(options);
8074
+ if (!operator) {
8075
+ exitWithError(
8076
+ "--operator <address> is required (user wallet address for this conversation)"
8077
+ );
8078
+ }
8079
+ const client = new AgentClient({ apiUrl, chainId });
8080
+ console.log(chalk4.blue("Loading conversation history..."));
8081
+ const messages = await client.getConversationHistory(operator, topic, {
8082
+ limit: options.limit
8083
+ });
8084
+ if (options.json) {
8085
+ console.log(jsonStringify(messages));
8086
+ return;
8087
+ }
8088
+ if (messages.length === 0) {
8089
+ console.log(chalk4.yellow("No messages found."));
8090
+ return;
8091
+ }
8092
+ const agentAddr = parseAgentAddressFromTopic(topic);
8093
+ console.log(
8094
+ chalk4.bold(
8095
+ `Conversation with ${agentAddr || topic} (${messages.length} messages):
8096
+ `
8097
+ )
8098
+ );
8099
+ for (const msg of messages) {
8100
+ const date = new Date(msg.timestamp * 1e3).toLocaleString();
8101
+ const prefix = msg.sender === "user" ? chalk4.cyan("You") : chalk4.green("Agent");
8102
+ const encrypted = msg.encrypted ? chalk4.yellow(" [encrypted]") : "";
8103
+ console.log(` ${prefix}${encrypted} (${chalk4.gray(date)}):`);
8104
+ console.log(` ${msg.text}`);
8105
+ console.log();
8106
+ }
8107
+ } catch (error) {
8108
+ exitWithError(
8109
+ `Failed to load history: ${error instanceof Error ? error.message : String(error)}`
8110
+ );
8111
+ }
8112
+ }
8113
+ function registerAgentDmHistoryCommand(parent) {
8114
+ const cmd = parent.command("dm-history <topic>").description("Read DM conversation history (pure chain read)").requiredOption(
8115
+ "--operator <address>",
8116
+ "User wallet address for this conversation"
8117
+ ).option("--limit <n>", "Max recent messages to fetch", (v) => parseInt(v, 10)).option("--json", "Output as JSON");
8118
+ addCommonOptions(cmd).action(async (topic, options) => {
8119
+ await executeDmHistory(topic, options);
8120
+ });
8121
+ }
8122
+ async function executeDmAuthEncode(options) {
8123
+ try {
8124
+ const { chainId } = parseReadOnlyOptionsWithDefault({
8125
+ chainId: options.chainId
8126
+ });
8127
+ let topic = options.topic;
8128
+ if (!topic) {
8129
+ if (!options.agentAddress) {
8130
+ exitWithError(
8131
+ "Must provide either --topic or --agent-address to generate a topic"
8132
+ );
8133
+ }
8134
+ if (!isAddress(options.agentAddress)) {
8135
+ exitWithError(`Invalid agent address: ${options.agentAddress}`);
8136
+ }
8137
+ topic = generateAgentChatTopic(options.agentAddress);
8138
+ }
8139
+ const result = buildConversationAuthTypedData({ topic, chainId });
8140
+ console.log(jsonStringify(result));
8141
+ } catch (error) {
8142
+ exitWithError(
8143
+ `dm-auth-encode failed: ${error instanceof Error ? error.message : String(error)}`
8144
+ );
8145
+ }
8146
+ }
8147
+ function registerAgentDmAuthEncodeCommand(parent) {
8148
+ parent.command("dm-auth-encode").description(
8149
+ "Emit { typedData, topic } for external signing. Pipe .typedData to your signer, pass .topic + the resulting signature to `agent dm --topic ... --topic-signature ...`."
8150
+ ).option(
8151
+ "--topic <topic>",
8152
+ "Existing topic to authorize (e.g. agent-chat-0x...-nanoid). If omitted, a new topic is generated from --agent-address."
8153
+ ).option(
8154
+ "--agent-address <address>",
8155
+ "Agent address \u2014 used to generate a new topic when --topic is omitted"
8156
+ ).option("--chain-id <id>", "Chain ID (default: 8453)", (v) => parseInt(v, 10)).action(async (options) => {
8157
+ await executeDmAuthEncode(options);
8158
+ });
8159
+ }
8160
+ async function executeSessionEncode(options) {
8161
+ try {
8162
+ if (!isAddress(options.operator)) {
8163
+ exitWithError(`Invalid operator address: ${options.operator}`);
8164
+ }
8165
+ const { chainId } = parseReadOnlyOptionsWithDefault({
8166
+ chainId: options.chainId
8167
+ });
8168
+ const result = buildSessionTypedData({
8169
+ operatorAddress: options.operator,
8170
+ chainId,
8171
+ expiresIn: options.expiresIn
8172
+ });
8173
+ console.log(jsonStringify(result));
8174
+ } catch (error) {
8175
+ exitWithError(
8176
+ `session-encode failed: ${error instanceof Error ? error.message : String(error)}`
8177
+ );
8178
+ }
8179
+ }
8180
+ function registerAgentSessionEncodeCommand(parent) {
8181
+ parent.command("session-encode").description(
8182
+ "Emit { typedData, expiresAt } for external signing (e.g., Bankr). Pipe .typedData to your signer, pass .expiresAt + the resulting signature to `agent session-create`."
8183
+ ).requiredOption(
8184
+ "--operator <address>",
8185
+ "Address that will sign the session (must match signer)"
8186
+ ).option("--chain-id <id>", "Chain ID (default: 8453)", (v) => parseInt(v, 10)).option(
8187
+ "--expires-in <seconds>",
8188
+ "Session lifetime in seconds (default: 3600, max: 86400)",
8189
+ (v) => parseInt(v, 10)
8190
+ ).action(async (options) => {
8191
+ await executeSessionEncode(options);
8192
+ });
8193
+ }
8194
+ async function executeSessionCreate(options) {
8195
+ try {
8196
+ if (!isAddress(options.operator)) {
8197
+ exitWithError(`Invalid operator address: ${options.operator}`);
8198
+ }
8199
+ if (!options.signature.startsWith("0x")) {
8200
+ exitWithError("--signature must be a 0x-prefixed hex string");
8201
+ }
8202
+ if (!options.expiresAt || !Number.isFinite(options.expiresAt)) {
8203
+ exitWithError("--expires-at must be a unix timestamp (seconds)");
8204
+ }
8205
+ const { chainId } = parseReadOnlyOptionsWithDefault({
8206
+ chainId: options.chainId
8207
+ });
8208
+ const apiUrl = options.apiUrl || process.env.NET_API_URL || NET_API_URL;
8209
+ const result = await exchangeSessionSignature({
8210
+ apiUrl,
8211
+ chainId,
8212
+ operatorAddress: options.operator,
8213
+ signature: options.signature,
8214
+ expiresAt: options.expiresAt
8215
+ });
8216
+ console.log(jsonStringify(result));
8217
+ } catch (error) {
8218
+ exitWithError(
8219
+ `session-create failed: ${error instanceof Error ? error.message : String(error)}`
8220
+ );
8221
+ }
8222
+ }
8223
+ function registerAgentSessionCreateCommand(parent) {
8224
+ parent.command("session-create").description(
8225
+ "Exchange an externally-produced signature for a session token. Pair with `agent session-encode`."
8226
+ ).requiredOption(
8227
+ "--operator <address>",
8228
+ "Address that produced the signature (must match the ecrecover result)"
8229
+ ).requiredOption("--signature <hex>", "EIP-712 signature over the session typed data").requiredOption(
8230
+ "--expires-at <timestamp>",
8231
+ "expiresAt value from session-encode (unix seconds)",
8232
+ (v) => parseInt(v, 10)
8233
+ ).option("--chain-id <id>", "Chain ID (default: 8453)", (v) => parseInt(v, 10)).option("--api-url <url>", "Net Protocol API URL").action(async (options) => {
8234
+ await executeSessionCreate(options);
8235
+ });
8236
+ }
8237
+
8238
+ // src/commands/agent/index.ts
8239
+ function registerAgentCommand(program2) {
8240
+ const agentCommand = program2.command("agent").description("Onchain agent operations (create, manage, run, DM)");
8241
+ registerAgentCreateCommand(agentCommand);
8242
+ registerAgentListCommand(agentCommand);
8243
+ registerAgentUpdateCommand(agentCommand);
8244
+ registerAgentHideCommand(agentCommand);
8245
+ registerAgentUnhideCommand(agentCommand);
8246
+ registerAgentRunCommand(agentCommand);
8247
+ registerAgentInfoCommand(agentCommand);
8248
+ registerAgentDmCommand(agentCommand);
8249
+ registerAgentDmListCommand(agentCommand);
8250
+ registerAgentDmHistoryCommand(agentCommand);
8251
+ registerAgentSessionEncodeCommand(agentCommand);
8252
+ registerAgentSessionCreateCommand(agentCommand);
8253
+ registerAgentDmAuthEncodeCommand(agentCommand);
8254
+ }
8255
+ var CHAINS = {
8256
+ 8453: base,
8257
+ 84532: baseSepolia
8258
+ };
8259
+ function getApiUrl(chainId) {
8260
+ return chainId === 84532 ? NET_TESTNET_API_URL : NET_API_URL;
8261
+ }
8262
+ function registerRelayCommand(program2) {
8263
+ const relayCommand = program2.command("relay").description("Relay operations (fund credits, check balance)");
8264
+ 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) => {
8265
+ const { privateKey, chainId, rpcUrl } = parseCommonOptionsWithDefault({
8266
+ privateKey: options.privateKey,
8267
+ chainId: options.chainId ? parseInt(options.chainId, 10) : void 0,
8268
+ rpcUrl: options.rpcUrl
8269
+ });
8270
+ const amount = parseFloat(options.amount);
8271
+ if (isNaN(amount) || amount <= 0) {
8272
+ exitWithError("Invalid amount. Must be a positive number.");
8273
+ }
8274
+ const chain = CHAINS[chainId];
8275
+ if (!chain) {
8276
+ exitWithError(
8277
+ `Chain ${chainId} not supported for relay funding. Use Base (8453) or Base Sepolia (84532).`
8278
+ );
8279
+ }
8280
+ const account = privateKeyToAccount(privateKey);
8281
+ const rpcUrls = getChainRpcUrls({ chainId, rpcUrl });
8282
+ const publicClient = createPublicClient({
8283
+ chain,
8284
+ transport: http(rpcUrls[0])
8285
+ });
8286
+ const walletClient = createWalletClient({
8287
+ account,
8288
+ chain,
8289
+ transport: http(rpcUrls[0])
8290
+ });
8291
+ const signer = {
8292
+ address: account.address,
8293
+ signTypedData: (msg) => walletClient.signTypedData(msg),
8294
+ readContract: (args) => publicClient.readContract(args),
8295
+ sendTransaction: (args) => walletClient.sendTransaction(args)
8296
+ };
8297
+ const { x402Client, wrapFetchWithPayment, x402HTTPClient } = await import('@x402/fetch');
8298
+ const { registerExactEvmScheme } = await import('@x402/evm/exact/client');
8299
+ const client = new x402Client();
8300
+ registerExactEvmScheme(client, { signer });
8301
+ const fetchWithPayment = wrapFetchWithPayment(fetch, client);
8302
+ const httpClient = new x402HTTPClient(client);
8303
+ const apiUrl = getApiUrl(chainId);
8304
+ if (!options.json) {
8305
+ console.log(
8306
+ `Funding $${amount.toFixed(2)} USDC to relay on chain ${chainId}...`
8307
+ );
8308
+ }
8309
+ try {
8310
+ const result = await fundBackendWallet({
8311
+ apiUrl,
8312
+ chainId,
8313
+ operatorAddress: account.address,
8314
+ secretKey: RELAY_ACCESS_KEY,
8315
+ fetchWithPayment,
8316
+ httpClient,
8317
+ amount
8318
+ });
8319
+ if (options.json) {
8320
+ console.log(
8321
+ JSON.stringify(
8322
+ {
8323
+ success: true,
8324
+ paymentTxHash: result.paymentTxHash,
8325
+ backendWalletAddress: result.backendWalletAddress,
8326
+ amountUsd: amount
8327
+ },
8328
+ null,
8329
+ 2
8330
+ )
8331
+ );
8332
+ } else {
8333
+ console.log(chalk4.green(`
8334
+ \u2713 Funded $${amount.toFixed(2)} successfully`));
8335
+ console.log(` Payment tx: ${result.paymentTxHash}`);
8336
+ console.log(
8337
+ ` Backend wallet: ${result.backendWalletAddress}`
8338
+ );
8339
+ }
8340
+ } catch (error) {
8341
+ const msg = error instanceof Error ? error.message : String(error);
8342
+ if (options.json) {
8343
+ console.log(JSON.stringify({ success: false, error: msg }, null, 2));
8344
+ process.exit(1);
8345
+ }
8346
+ exitWithError(`Funding failed: ${msg}`);
8347
+ }
8348
+ });
8349
+ 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) => {
8350
+ const { privateKey, chainId } = parseCommonOptionsWithDefault({
8351
+ privateKey: options.privateKey,
8352
+ chainId: options.chainId ? parseInt(options.chainId, 10) : void 0
8353
+ });
8354
+ const account = privateKeyToAccount(privateKey);
8355
+ const apiUrl = getApiUrl(chainId);
8356
+ try {
8357
+ const result = await checkBackendWalletBalance({
8358
+ apiUrl,
8359
+ chainId,
8360
+ operatorAddress: account.address,
8361
+ secretKey: RELAY_ACCESS_KEY
8362
+ });
8363
+ if (options.json) {
8364
+ console.log(JSON.stringify(result, null, 2));
8365
+ } else {
8366
+ console.log(`Relay Balance`);
8367
+ console.log(` Backend wallet: ${result.backendWalletAddress}`);
8368
+ console.log(` Balance: ${result.balanceEth} ETH`);
8369
+ console.log(
8370
+ ` Sufficient: ${result.sufficientBalance ? chalk4.green("Yes") : chalk4.red("No")} (min: ${result.minRequiredEth} ETH)`
8371
+ );
8372
+ }
8373
+ } catch (error) {
8374
+ const msg = error instanceof Error ? error.message : String(error);
8375
+ if (options.json) {
8376
+ console.log(JSON.stringify({ success: false, error: msg }, null, 2));
8377
+ process.exit(1);
8378
+ }
8379
+ exitWithError(`Balance check failed: ${msg}`);
8380
+ }
8381
+ });
8382
+ }
7543
8383
  var CACHE_DIR = join(homedir(), ".netp");
7544
8384
  var CACHE_FILE = join(CACHE_DIR, "update-check.json");
7545
8385
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
@@ -7640,6 +8480,8 @@ registerProfileCommand(program);
7640
8480
  registerBazaarCommand(program);
7641
8481
  registerFeedCommand(program);
7642
8482
  registerUpvoteCommand(program);
8483
+ registerAgentCommand(program);
8484
+ registerRelayCommand(program);
7643
8485
  program.command("update").description("Update netp to the latest version").action(async () => {
7644
8486
  const { execSync } = await import('child_process');
7645
8487
  console.log("Updating @net-protocol/cli...");