httpcat-cli 0.0.26 → 0.0.27-rc.1
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/.github/workflows/README.md +19 -2
- package/.github/workflows/ci.yml +31 -20
- package/.github/workflows/homebrew-tap.yml +1 -1
- package/.github/workflows/rc-publish.yml +169 -0
- package/.github/workflows/release.yml +223 -71
- package/README.md +94 -76
- package/bun.lock +2933 -0
- package/dist/commands/account.d.ts.map +1 -1
- package/dist/commands/account.js +14 -7
- package/dist/commands/account.js.map +1 -1
- package/dist/commands/balances.d.ts.map +1 -0
- package/dist/commands/balances.js +171 -0
- package/dist/commands/balances.js.map +1 -0
- package/dist/commands/buy.d.ts.map +1 -1
- package/dist/commands/buy.js +739 -32
- package/dist/commands/buy.js.map +1 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +467 -906
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/claim.d.ts.map +1 -0
- package/dist/commands/claim.js +65 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +0 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +128 -26
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +30 -23
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/positions.d.ts.map +1 -1
- package/dist/commands/positions.js +178 -105
- package/dist/commands/positions.js.map +1 -1
- package/dist/commands/sell.d.ts.map +1 -1
- package/dist/commands/sell.js +713 -24
- package/dist/commands/sell.js.map +1 -1
- package/dist/index.js +315 -99
- package/dist/index.js.map +1 -1
- package/dist/interactive/shell.d.ts.map +1 -1
- package/dist/interactive/shell.js +328 -179
- package/dist/interactive/shell.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +8 -8
- package/dist/mcp/tools.js.map +1 -1
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +66 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/formatting.js +3 -5
- package/dist/utils/formatting.js.map +1 -1
- package/dist/utils/token-resolver.d.ts.map +1 -1
- package/dist/utils/token-resolver.js +71 -40
- package/dist/utils/token-resolver.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +4 -3
- package/dist/utils/validation.js.map +1 -1
- package/jest.config.js +1 -1
- package/package.json +19 -13
- package/.claude/settings.local.json +0 -41
- package/dist/commands/balance.d.ts.map +0 -1
- package/dist/commands/balance.js +0 -112
- package/dist/commands/balance.js.map +0 -1
- package/homebrew-httpcat/Formula/httpcat.rb +0 -18
- package/homebrew-httpcat/README.md +0 -31
- package/homebrew-httpcat/homebrew-httpcat/Formula/httpcat.rb +0 -18
- package/homebrew-httpcat/homebrew-httpcat/README.md +0 -31
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { privateKeyToAccount } from "viem/accounts";
|
|
5
|
+
import { createRequire } from "module";
|
|
5
6
|
import { config } from "./config.js";
|
|
6
7
|
import { formatAddress, formatCurrency } from "./utils/formatting.js";
|
|
7
8
|
import { HttpcatClient, HttpcatError } from "./client.js";
|
|
@@ -18,6 +19,8 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
18
19
|
const __dirname = dirname(__filename);
|
|
19
20
|
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
20
21
|
const VERSION = packageJson.version;
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const { version: PACKAGE_VERSION } = require("../package.json");
|
|
21
24
|
// Import commands
|
|
22
25
|
import { createToken, displayCreateResult } from "./commands/create.js";
|
|
23
26
|
import { buyToken, displayBuyResult, displayBuyResultCompact, } from "./commands/buy.js";
|
|
@@ -27,17 +30,18 @@ import { listTokens, displayTokenList } from "./commands/list.js";
|
|
|
27
30
|
import { getPositions, displayPositions } from "./commands/positions.js";
|
|
28
31
|
import { getTransactions, displayTransactions, } from "./commands/transactions.js";
|
|
29
32
|
import { checkHealth, displayHealthStatus } from "./commands/health.js";
|
|
30
|
-
import { checkBalance, displayBalance } from "./commands/
|
|
33
|
+
import { checkBalance, displayBalance } from "./commands/balances.js";
|
|
31
34
|
import { startChatStream } from "./commands/chat.js";
|
|
32
35
|
import { runMcpServer } from "./commands/mcp-server.js";
|
|
33
|
-
import { getAccountInfo, displayAccountInfo, listAccounts, switchAccount, addAccount } from "./commands/account.js";
|
|
36
|
+
import { getAccountInfo, displayAccountInfo, listAccounts, switchAccount, addAccount, } from "./commands/account.js";
|
|
37
|
+
import { viewFees, claimFees, displayFees, displayClaimResult, } from "./commands/claim.js";
|
|
34
38
|
// Check for --version --json before parsing
|
|
35
39
|
const args = process.argv;
|
|
36
40
|
if (args.includes("--version") || args.includes("-V")) {
|
|
37
41
|
if (args.includes("--json")) {
|
|
38
42
|
console.log(JSON.stringify({
|
|
39
43
|
name: "httpcat-cli",
|
|
40
|
-
version:
|
|
44
|
+
version: PACKAGE_VERSION,
|
|
41
45
|
}, null, 2));
|
|
42
46
|
process.exit(0);
|
|
43
47
|
}
|
|
@@ -46,13 +50,13 @@ const program = new Command();
|
|
|
46
50
|
program
|
|
47
51
|
.name("httpcat")
|
|
48
52
|
.description("CLI tool for interacting with httpcat agent")
|
|
49
|
-
.version(
|
|
50
|
-
.option("--json", "Output in JSON format")
|
|
51
|
-
.option("--quiet", "Minimal output (exit codes only)")
|
|
52
|
-
.option("--verbose", "Verbose error messages")
|
|
53
|
+
.version(PACKAGE_VERSION)
|
|
54
|
+
.option("-j, --json", "Output in JSON format")
|
|
55
|
+
.option("-q, --quiet", "Minimal output (exit codes only)")
|
|
56
|
+
.option("-v, --verbose", "Verbose error messages")
|
|
53
57
|
.option("--no-art", "Disable ASCII art")
|
|
54
|
-
.option("--private-key <key>", "Private key (overrides config and env var)")
|
|
55
|
-
.option("--account <index>", "Account index to use (overrides active account)", (value) => parseInt(value, 10));
|
|
58
|
+
.option("-k, --private-key <key>", "Private key (overrides config and env var)")
|
|
59
|
+
.option("-a, --account <index>", "Account index to use (overrides active account)", (value) => parseInt(value, 10));
|
|
56
60
|
// Helper function to get private key with priority: CLI flag > env var > config
|
|
57
61
|
function getPrivateKey(cliPrivateKey, accountIndex) {
|
|
58
62
|
if (cliPrivateKey)
|
|
@@ -62,7 +66,9 @@ function getPrivateKey(cliPrivateKey, accountIndex) {
|
|
|
62
66
|
return envKey;
|
|
63
67
|
// Use account system
|
|
64
68
|
try {
|
|
65
|
-
const index = accountIndex !== undefined
|
|
69
|
+
const index = accountIndex !== undefined
|
|
70
|
+
? accountIndex
|
|
71
|
+
: config.getActiveAccountIndex();
|
|
66
72
|
return config.getAccountPrivateKey(index);
|
|
67
73
|
}
|
|
68
74
|
catch (error) {
|
|
@@ -186,7 +192,7 @@ envCommand
|
|
|
186
192
|
.description("Add a custom environment")
|
|
187
193
|
.argument("<name>", "Environment name")
|
|
188
194
|
.argument("<agentUrl>", "Agent URL (e.g., http://localhost:8787)")
|
|
189
|
-
.option("--network <network>", "Network (default: base-sepolia)", "base-sepolia")
|
|
195
|
+
.option("-n, --network <network>", "Network (default: base-sepolia)", "base-sepolia")
|
|
190
196
|
.action((name, agentUrl, options) => {
|
|
191
197
|
try {
|
|
192
198
|
config.addEnvironment(name, agentUrl, options.network);
|
|
@@ -207,7 +213,7 @@ envCommand
|
|
|
207
213
|
.description("Update an existing environment")
|
|
208
214
|
.argument("<name>", "Environment name")
|
|
209
215
|
.argument("<agentUrl>", "Agent URL (e.g., http://localhost:8787)")
|
|
210
|
-
.option("--network <network>", "Network (default: base-sepolia)", "base-sepolia")
|
|
216
|
+
.option("-n, --network <network>", "Network (default: base-sepolia)", "base-sepolia")
|
|
211
217
|
.action((name, agentUrl, options) => {
|
|
212
218
|
try {
|
|
213
219
|
config.updateEnvironment(name, agentUrl, options.network);
|
|
@@ -225,9 +231,9 @@ envCommand
|
|
|
225
231
|
program
|
|
226
232
|
.command("config")
|
|
227
233
|
.description("Configure httpcat")
|
|
228
|
-
.option("--show", "Show current configuration")
|
|
229
|
-
.option("--set <key=value>", "Set configuration value")
|
|
230
|
-
.option("--reset", "Reset configuration")
|
|
234
|
+
.option("-s, --show", "Show current configuration")
|
|
235
|
+
.option("-S, --set <key=value>", "Set configuration value")
|
|
236
|
+
.option("-r, --reset", "Reset configuration")
|
|
231
237
|
.addHelpText("after", `
|
|
232
238
|
Examples:
|
|
233
239
|
httpcat config # Run setup wizard
|
|
@@ -267,9 +273,9 @@ program
|
|
|
267
273
|
.description("Create a new token")
|
|
268
274
|
.argument("<name>", 'Token name (e.g., "My Token")')
|
|
269
275
|
.argument("<symbol>", 'Token symbol/ticker (e.g., "MTK", 3-10 characters)')
|
|
270
|
-
.option("--photo <url>", "Photo URL for token logo")
|
|
271
|
-
.option("--banner <url>", "Banner image URL")
|
|
272
|
-
.option("--website <url>", "Website URL")
|
|
276
|
+
.option("-p, --photo <url>", "Photo URL for token logo")
|
|
277
|
+
.option("-b, --banner <url>", "Banner image URL")
|
|
278
|
+
.option("-w, --website <url>", "Website URL")
|
|
273
279
|
.addHelpText("after", `
|
|
274
280
|
Examples:
|
|
275
281
|
httpcat create "My Token" "MTK"
|
|
@@ -328,16 +334,18 @@ Examples:
|
|
|
328
334
|
// Buy command
|
|
329
335
|
program
|
|
330
336
|
.command("buy")
|
|
331
|
-
.description("Buy tokens
|
|
332
|
-
.argument("<identifier>", '
|
|
333
|
-
.argument("<amount>", "Amount in USDC: 0.05, 0.10,
|
|
334
|
-
.option("--repeat <count>", "Number of times to repeat the buy operation", (value) => parseInt(value, 10))
|
|
335
|
-
.option("--delay <ms>", "Delay in milliseconds between repeat operations (default: 0)", (value) => parseInt(value, 10), 0)
|
|
337
|
+
.description("Buy tokens (auto-routes to bonding curve or Uniswap V2)")
|
|
338
|
+
.argument("<identifier>", 'Address, name, or symbol (e.g., 0x1234..., "My Token", or MTK)')
|
|
339
|
+
.argument("<amount>", "Amount in USDC or percentage: 0.05, 0.10, 0.20, 10, 50% (% of your USDC balance)")
|
|
340
|
+
.option("-r, --repeat <count>", "Number of times to repeat the buy operation", (value) => parseInt(value, 10))
|
|
341
|
+
.option("-d, --delay <ms>", "Delay in milliseconds between repeat operations (default: 0)", (value) => parseInt(value, 10), 0)
|
|
336
342
|
.addHelpText("after", `
|
|
337
343
|
Examples:
|
|
338
344
|
httpcat buy abc123-4567-89ab-cdef-0123456789ab 0.20
|
|
339
345
|
httpcat buy "My Token" 0.10
|
|
340
|
-
httpcat buy MTK
|
|
346
|
+
httpcat buy MTK 10 # Buy with $10 USDC
|
|
347
|
+
httpcat buy MTK 50% # Buy with 50% of your USDC balance
|
|
348
|
+
httpcat buy MTK 0.05 --repeat 10
|
|
341
349
|
httpcat --json buy abc123-4567-89ab-cdef-0123456789ab 0.10
|
|
342
350
|
httpcat --private-key 0x... buy "PurrfecTT" 0.20
|
|
343
351
|
httpcat buy MTK 0.10 --repeat 10
|
|
@@ -364,6 +372,27 @@ Examples:
|
|
|
364
372
|
const client = await HttpcatClient.create(privateKey);
|
|
365
373
|
const isTestMode = client.getNetwork().includes("sepolia");
|
|
366
374
|
const silent = globalOpts.json || globalOpts.quiet;
|
|
375
|
+
// Handle percentage amounts for graduated tokens
|
|
376
|
+
let finalAmount = amount;
|
|
377
|
+
if (amount.endsWith("%")) {
|
|
378
|
+
if (!silent) {
|
|
379
|
+
console.log("💰 Checking USDC balance for percentage buy...");
|
|
380
|
+
}
|
|
381
|
+
const balance = await checkBalance(privateKey);
|
|
382
|
+
const usdcBalance = parseFloat(balance.usdcFormatted.replace("$", "").replace(",", ""));
|
|
383
|
+
const percentage = parseFloat(amount.replace("%", ""));
|
|
384
|
+
if (isNaN(percentage) || percentage <= 0 || percentage > 100) {
|
|
385
|
+
throw new Error("Percentage must be between 0 and 100");
|
|
386
|
+
}
|
|
387
|
+
finalAmount = ((usdcBalance * percentage) / 100).toFixed(6);
|
|
388
|
+
if (!silent) {
|
|
389
|
+
console.log(`💵 USDC Balance: $${usdcBalance}`);
|
|
390
|
+
console.log(`📊 Using ${percentage}% = $${finalAmount}`);
|
|
391
|
+
}
|
|
392
|
+
if (parseFloat(finalAmount) === 0) {
|
|
393
|
+
throw new Error("Insufficient USDC balance");
|
|
394
|
+
}
|
|
395
|
+
}
|
|
367
396
|
// If repeat is specified, execute multiple buys
|
|
368
397
|
if (repeatCount && repeatCount > 0) {
|
|
369
398
|
const results = [];
|
|
@@ -376,7 +405,7 @@ Examples:
|
|
|
376
405
|
if (!globalOpts.json && !globalOpts.quiet) {
|
|
377
406
|
console.log(chalk.dim(`Buy ${i}/${repeatCount}...`));
|
|
378
407
|
}
|
|
379
|
-
const result = await withLoading(() => buyToken(client, tokenId,
|
|
408
|
+
const result = await withLoading(() => buyToken(client, tokenId, finalAmount, isTestMode, silent || i > 1, privateKey), {
|
|
380
409
|
message: `Buying tokens (${i}/${repeatCount})...`,
|
|
381
410
|
json: globalOpts.json,
|
|
382
411
|
quiet: globalOpts.quiet || i > 1,
|
|
@@ -427,7 +456,7 @@ Examples:
|
|
|
427
456
|
const lastResult = results[results.length - 1];
|
|
428
457
|
const graduationStatus = lastResult.graduationReached
|
|
429
458
|
? "✅ GRADUATED!"
|
|
430
|
-
: `${lastResult.graduationProgress.toFixed(2)}%`;
|
|
459
|
+
: `${(lastResult.graduationProgress || 0).toFixed(2)}%`;
|
|
431
460
|
console.log();
|
|
432
461
|
console.log(chalk.cyan.bold("📊 Repeat Buy Summary"));
|
|
433
462
|
console.log(chalk.dim("─".repeat(50)));
|
|
@@ -445,7 +474,7 @@ Examples:
|
|
|
445
474
|
}
|
|
446
475
|
else {
|
|
447
476
|
// Normal single buy execution
|
|
448
|
-
const result = await withLoading(() => buyToken(client, tokenId,
|
|
477
|
+
const result = await withLoading(() => buyToken(client, tokenId, finalAmount, isTestMode, silent, privateKey), {
|
|
449
478
|
message: "Buying tokens...",
|
|
450
479
|
json: globalOpts.json,
|
|
451
480
|
quiet: globalOpts.quiet,
|
|
@@ -474,8 +503,8 @@ Examples:
|
|
|
474
503
|
// Sell command
|
|
475
504
|
program
|
|
476
505
|
.command("sell")
|
|
477
|
-
.description("Sell tokens
|
|
478
|
-
.argument("<identifier>", '
|
|
506
|
+
.description("Sell tokens (auto-routes to bonding curve or Uniswap V2)")
|
|
507
|
+
.argument("<identifier>", 'Address, name, or symbol (e.g., 0x1234..., "My Token", or MTK)')
|
|
479
508
|
.argument("<amount>", 'Amount: number (e.g., 1000), percentage (e.g., 50%), or "all"')
|
|
480
509
|
.addHelpText("after", `
|
|
481
510
|
Examples:
|
|
@@ -505,23 +534,99 @@ Examples:
|
|
|
505
534
|
// Derive user address from private key
|
|
506
535
|
const account = privateKeyToAccount(privateKey);
|
|
507
536
|
const userAddress = account.address;
|
|
508
|
-
//
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
537
|
+
// Check if it's a contract address
|
|
538
|
+
const addressRegex = /^0x[0-9a-f]{40}$/i;
|
|
539
|
+
const isContractAddress = addressRegex.test(tokenId);
|
|
540
|
+
let actualBalance = "0";
|
|
541
|
+
let info = null;
|
|
542
|
+
// If it's a contract address, try to look it up in database first
|
|
543
|
+
if (isContractAddress) {
|
|
544
|
+
try {
|
|
545
|
+
// Try to get token info - if it exists in our DB, use normal flow
|
|
546
|
+
info = await withLoading(() => getTokenInfo(client, tokenId, userAddress, silent), {
|
|
547
|
+
message: "Checking token info...",
|
|
548
|
+
json: globalOpts.json,
|
|
549
|
+
quiet: globalOpts.quiet,
|
|
550
|
+
spinner: "cat",
|
|
551
|
+
});
|
|
552
|
+
// Found in database! Continue with normal flow below
|
|
553
|
+
actualBalance = info.userPosition?.tokensOwned || "0";
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
// Not in database - treat as external token
|
|
557
|
+
if (error.message?.includes("not found") ||
|
|
558
|
+
error.message?.includes("Invalid UUID")) {
|
|
559
|
+
if (!silent) {
|
|
560
|
+
console.log("📊 Checking on-chain balance for external token...");
|
|
561
|
+
}
|
|
562
|
+
const { createPublicClient, http, parseAbi } = await import("viem");
|
|
563
|
+
const { baseSepolia } = await import("viem/chains");
|
|
564
|
+
const publicClient = createPublicClient({
|
|
565
|
+
chain: baseSepolia,
|
|
566
|
+
transport: http(),
|
|
567
|
+
});
|
|
568
|
+
const ERC20_ABI = parseAbi([
|
|
569
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
570
|
+
]);
|
|
571
|
+
const balance = await publicClient.readContract({
|
|
572
|
+
address: tokenId,
|
|
573
|
+
abi: ERC20_ABI,
|
|
574
|
+
functionName: "balanceOf",
|
|
575
|
+
args: [userAddress],
|
|
576
|
+
});
|
|
577
|
+
actualBalance = balance.toString();
|
|
578
|
+
if (!silent) {
|
|
579
|
+
const { formatUnits } = await import("viem");
|
|
580
|
+
console.log(`💼 On-chain balance: ${formatUnits(balance, 18)} tokens`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
// Some other error - rethrow
|
|
585
|
+
throw error;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
// UUID token - get token info
|
|
591
|
+
info = await withLoading(() => getTokenInfo(client, tokenId, userAddress, silent), {
|
|
592
|
+
message: "Checking token info...",
|
|
593
|
+
json: globalOpts.json,
|
|
594
|
+
quiet: globalOpts.quiet,
|
|
595
|
+
spinner: "cat",
|
|
596
|
+
});
|
|
597
|
+
actualBalance = info.userPosition?.tokensOwned || "0";
|
|
598
|
+
// For graduated tokens, check actual on-chain balance
|
|
599
|
+
if (info && info.status === "graduated") {
|
|
600
|
+
if (!silent) {
|
|
601
|
+
console.log("📊 Checking on-chain balance for graduated token...");
|
|
602
|
+
}
|
|
603
|
+
const { createPublicClient, http, parseAbi } = await import("viem");
|
|
604
|
+
const { baseSepolia } = await import("viem/chains");
|
|
605
|
+
const publicClient = createPublicClient({
|
|
606
|
+
chain: baseSepolia,
|
|
607
|
+
transport: http(),
|
|
608
|
+
});
|
|
609
|
+
const ERC20_ABI = parseAbi([
|
|
610
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
611
|
+
]);
|
|
612
|
+
const balance = await publicClient.readContract({
|
|
613
|
+
address: info.address,
|
|
614
|
+
abi: ERC20_ABI,
|
|
615
|
+
functionName: "balanceOf",
|
|
616
|
+
args: [userAddress],
|
|
617
|
+
});
|
|
618
|
+
actualBalance = balance.toString();
|
|
619
|
+
if (!silent) {
|
|
620
|
+
const { formatUnits } = await import("viem");
|
|
621
|
+
console.log(`💼 On-chain balance: ${formatUnits(balance, 18)} tokens`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
517
624
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
if (!resolvedTokenId) {
|
|
521
|
-
throw new Error(`Failed to get token ID from token info. Original identifier: ${tokenId}`);
|
|
625
|
+
if (actualBalance === "0") {
|
|
626
|
+
throw new Error("You do not own any of this token");
|
|
522
627
|
}
|
|
523
|
-
const tokenAmount = parseTokenAmount(amountInput,
|
|
524
|
-
const result = await withLoading(() => sellToken(client,
|
|
628
|
+
const tokenAmount = parseTokenAmount(amountInput, actualBalance);
|
|
629
|
+
const result = await withLoading(() => sellToken(client, tokenId, tokenAmount, silent, privateKey), {
|
|
525
630
|
message: "Selling tokens...",
|
|
526
631
|
json: globalOpts.json,
|
|
527
632
|
quiet: globalOpts.quiet,
|
|
@@ -546,11 +651,89 @@ Examples:
|
|
|
546
651
|
process.exit(getExitCode(error));
|
|
547
652
|
}
|
|
548
653
|
});
|
|
654
|
+
// Claim command
|
|
655
|
+
program
|
|
656
|
+
.command("claim")
|
|
657
|
+
.description("View and claim accumulated LP fees for graduated tokens")
|
|
658
|
+
.argument("<identifier>", 'Address, name, or symbol (e.g., 0x1234..., "My Token", or MTK)')
|
|
659
|
+
.option("-e, --execute", "Execute the claim (default: view only)")
|
|
660
|
+
.option("-A, --address <address>", "Caller address (defaults to wallet address)")
|
|
661
|
+
.addHelpText("after", `
|
|
662
|
+
Examples:
|
|
663
|
+
httpcat claim "MyToken" # View accumulated fees
|
|
664
|
+
httpcat claim MTK --execute # Claim fees
|
|
665
|
+
httpcat claim 0x4C64... --execute # Claim using contract address
|
|
666
|
+
httpcat --json claim "MyToken"
|
|
667
|
+
`)
|
|
668
|
+
.action(async (identifier, options, command) => {
|
|
669
|
+
try {
|
|
670
|
+
const globalOpts = command.parent?.opts() || {};
|
|
671
|
+
const accountIndex = globalOpts.account;
|
|
672
|
+
// Ensure wallet is unlocked
|
|
673
|
+
await ensureWalletUnlocked();
|
|
674
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
675
|
+
// If not configured and not in JSON mode, prompt interactively
|
|
676
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
677
|
+
if (globalOpts.json) {
|
|
678
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
679
|
+
process.exit(2);
|
|
680
|
+
}
|
|
681
|
+
// Interactive prompt for private key
|
|
682
|
+
privateKey = await promptForPrivateKey();
|
|
683
|
+
}
|
|
684
|
+
const client = await HttpcatClient.create(privateKey);
|
|
685
|
+
const silent = globalOpts.json || globalOpts.quiet;
|
|
686
|
+
if (options.execute) {
|
|
687
|
+
// Claim fees
|
|
688
|
+
// Derive caller address from private key if not provided
|
|
689
|
+
const account = privateKeyToAccount(privateKey);
|
|
690
|
+
const callerAddress = options.address || account.address;
|
|
691
|
+
const result = await withLoading(() => claimFees(client, identifier, callerAddress, silent), {
|
|
692
|
+
message: "Claiming fees...",
|
|
693
|
+
json: globalOpts.json,
|
|
694
|
+
quiet: globalOpts.quiet,
|
|
695
|
+
spinner: "cat",
|
|
696
|
+
});
|
|
697
|
+
if (globalOpts.json) {
|
|
698
|
+
outputJson("claim_fees", result);
|
|
699
|
+
}
|
|
700
|
+
else if (!globalOpts.quiet) {
|
|
701
|
+
displayClaimResult(result);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
// View fees only
|
|
706
|
+
const result = await withLoading(() => viewFees(client, identifier, silent), {
|
|
707
|
+
message: "Fetching fee information...",
|
|
708
|
+
json: globalOpts.json,
|
|
709
|
+
quiet: globalOpts.quiet,
|
|
710
|
+
spinner: "cat",
|
|
711
|
+
});
|
|
712
|
+
if (globalOpts.json) {
|
|
713
|
+
outputJson("view_fees", result);
|
|
714
|
+
}
|
|
715
|
+
else if (!globalOpts.quiet) {
|
|
716
|
+
displayFees(result);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
process.exit(0);
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
const globalOpts = command.parent?.opts() || {};
|
|
723
|
+
if (globalOpts.json) {
|
|
724
|
+
outputError("claim", error, getExitCode(error));
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
handleError(error, globalOpts.verbose);
|
|
728
|
+
}
|
|
729
|
+
process.exit(getExitCode(error));
|
|
730
|
+
}
|
|
731
|
+
});
|
|
549
732
|
// Info command
|
|
550
733
|
program
|
|
551
734
|
.command("info")
|
|
552
735
|
.description("Get detailed information about a token")
|
|
553
|
-
.argument("<identifier>", '
|
|
736
|
+
.argument("<identifier>", 'Address, name, or symbol (e.g., 0x1234..., "My Token", or MTK)')
|
|
554
737
|
.addHelpText("after", `
|
|
555
738
|
Examples:
|
|
556
739
|
httpcat info abc123-4567-89ab-cdef-0123456789ab
|
|
@@ -589,7 +772,7 @@ Examples:
|
|
|
589
772
|
outputJson("token_info", result);
|
|
590
773
|
}
|
|
591
774
|
else if (!globalOpts.quiet) {
|
|
592
|
-
displayTokenInfo(result);
|
|
775
|
+
await displayTokenInfo(result, userAddress);
|
|
593
776
|
}
|
|
594
777
|
process.exit(0);
|
|
595
778
|
}
|
|
@@ -608,9 +791,9 @@ Examples:
|
|
|
608
791
|
program
|
|
609
792
|
.command("list")
|
|
610
793
|
.description("List all tokens with pagination and sorting")
|
|
611
|
-
.option("--page <number>", "Page number (default: 1)", "1")
|
|
612
|
-
.option("--limit <number>", "Items per page (default: 20, max: 100)", "20")
|
|
613
|
-
.option("--sort <field>", "Sort by: mcap (market cap), created (date), or name (alphabetical)", "mcap")
|
|
794
|
+
.option("-p, --page <number>", "Page number (default: 1)", "1")
|
|
795
|
+
.option("-l, --limit <number>", "Items per page (default: 20, max: 100)", "20")
|
|
796
|
+
.option("-s, --sort <field>", "Sort by: mcap (market cap), created (date), or name (alphabetical)", "mcap")
|
|
614
797
|
.addHelpText("after", `
|
|
615
798
|
Examples:
|
|
616
799
|
httpcat list
|
|
@@ -663,18 +846,18 @@ Examples:
|
|
|
663
846
|
// Transactions command
|
|
664
847
|
program
|
|
665
848
|
.command("transactions")
|
|
666
|
-
.description("Get paginated list of transactions
|
|
667
|
-
.option("--user <address>", "Filter by user address")
|
|
668
|
-
.option("--token <tokenId>", "Filter by token ID")
|
|
669
|
-
.option("--type <type>", "Filter by type: buy, sell, or airdrop")
|
|
670
|
-
.option("--limit <number>", "Number of results (default: 50, max: 100)", "50")
|
|
671
|
-
.option("--offset <number>", "Pagination offset (default: 0)", "0")
|
|
849
|
+
.description("Get paginated list of transactions (defaults to selected account)")
|
|
850
|
+
.option("-u, --user <address>", "Filter by user address (defaults to selected account if not provided)")
|
|
851
|
+
.option("-t, --token <tokenId>", "Filter by token ID")
|
|
852
|
+
.option("-T, --type <type>", "Filter by type: buy, sell, or airdrop")
|
|
853
|
+
.option("-l, --limit <number>", "Number of results (default: 50, max: 100)", "50")
|
|
854
|
+
.option("-o, --offset <number>", "Pagination offset (default: 0)", "0")
|
|
672
855
|
.addHelpText("after", `
|
|
673
856
|
Examples:
|
|
674
|
-
httpcat transactions
|
|
675
|
-
httpcat transactions --user 0x1234...
|
|
676
|
-
httpcat transactions --token abc123-...
|
|
677
|
-
httpcat transactions --type buy --limit 20
|
|
857
|
+
httpcat transactions # Get transactions for selected account
|
|
858
|
+
httpcat transactions --user 0x1234... # Get transactions for specific address
|
|
859
|
+
httpcat transactions --token abc123-... # Get transactions for a token
|
|
860
|
+
httpcat transactions --type buy --limit 20 # Get buy transactions for selected account
|
|
678
861
|
httpcat --json transactions --user 0x1234... --offset 50
|
|
679
862
|
`)
|
|
680
863
|
.action(async (options, command) => {
|
|
@@ -694,9 +877,37 @@ Examples:
|
|
|
694
877
|
privateKey = await promptForPrivateKey();
|
|
695
878
|
}
|
|
696
879
|
const client = await HttpcatClient.create(privateKey);
|
|
880
|
+
// Derive user address from private key (default to selected account if no --user option)
|
|
881
|
+
const account = privateKeyToAccount(privateKey);
|
|
882
|
+
const userAddress = account.address;
|
|
883
|
+
// Show which account is being used (if not quiet and not json)
|
|
884
|
+
if (!globalOpts.quiet && !globalOpts.json) {
|
|
885
|
+
const activeIndex = accountIndex !== undefined
|
|
886
|
+
? accountIndex
|
|
887
|
+
: config.getActiveAccountIndex();
|
|
888
|
+
const accounts = config.getAllAccounts();
|
|
889
|
+
const accountInfo = accounts.find((acc) => acc.index === activeIndex);
|
|
890
|
+
if (accountInfo) {
|
|
891
|
+
// Verify the address matches
|
|
892
|
+
if (accountInfo.address.toLowerCase() !== userAddress.toLowerCase()) {
|
|
893
|
+
console.log(chalk.red(`⚠️ Warning: Account address mismatch!`));
|
|
894
|
+
console.log(chalk.red(` Expected: ${accountInfo.address}`));
|
|
895
|
+
console.log(chalk.red(` Got: ${userAddress}`));
|
|
896
|
+
console.log();
|
|
897
|
+
}
|
|
898
|
+
console.log(chalk.dim(`Using account ${activeIndex} (${accountInfo.type === "custom" ? "Custom" : "Seed-Derived"}): ${formatAddress(userAddress, 12)}`));
|
|
899
|
+
console.log();
|
|
900
|
+
}
|
|
901
|
+
}
|
|
697
902
|
const input = {};
|
|
698
|
-
|
|
903
|
+
// Use selected account's address by default, unless --user is explicitly provided
|
|
904
|
+
if (options.user) {
|
|
699
905
|
input.userAddress = options.user;
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
// Default to selected account's address
|
|
909
|
+
input.userAddress = userAddress;
|
|
910
|
+
}
|
|
700
911
|
if (options.token)
|
|
701
912
|
input.tokenId = options.token;
|
|
702
913
|
if (options.type) {
|
|
@@ -738,9 +949,13 @@ Examples:
|
|
|
738
949
|
program
|
|
739
950
|
.command("positions")
|
|
740
951
|
.description("Get all your positions with comprehensive information")
|
|
952
|
+
.option("-a, --active", "Show only active (non-graduated) positions")
|
|
953
|
+
.option("-g, --graduated", "Show only graduated positions")
|
|
741
954
|
.addHelpText("after", `
|
|
742
955
|
Examples:
|
|
743
956
|
httpcat positions
|
|
957
|
+
httpcat positions --active
|
|
958
|
+
httpcat positions --graduated
|
|
744
959
|
httpcat --json positions
|
|
745
960
|
httpcat --private-key 0x... positions
|
|
746
961
|
`)
|
|
@@ -766,7 +981,9 @@ Examples:
|
|
|
766
981
|
const userAddress = account.address;
|
|
767
982
|
// Show which account is being used and verify it matches (if not quiet and not json)
|
|
768
983
|
if (!globalOpts.quiet && !globalOpts.json) {
|
|
769
|
-
const activeIndex = accountIndex !== undefined
|
|
984
|
+
const activeIndex = accountIndex !== undefined
|
|
985
|
+
? accountIndex
|
|
986
|
+
: config.getActiveAccountIndex();
|
|
770
987
|
const accounts = config.getAllAccounts();
|
|
771
988
|
const accountInfo = accounts.find((acc) => acc.index === activeIndex);
|
|
772
989
|
if (accountInfo) {
|
|
@@ -781,17 +998,33 @@ Examples:
|
|
|
781
998
|
console.log();
|
|
782
999
|
}
|
|
783
1000
|
}
|
|
784
|
-
|
|
1001
|
+
let result = await withLoading(() => getPositions(client, userAddress), {
|
|
785
1002
|
message: "Fetching positions...",
|
|
786
1003
|
json: globalOpts.json,
|
|
787
1004
|
quiet: globalOpts.quiet,
|
|
788
1005
|
spinner: "cat",
|
|
789
1006
|
});
|
|
1007
|
+
const filter = command.active
|
|
1008
|
+
? "active"
|
|
1009
|
+
: command.graduated
|
|
1010
|
+
? "graduated"
|
|
1011
|
+
: "all";
|
|
790
1012
|
if (globalOpts.json) {
|
|
1013
|
+
// For JSON output, filter the positions
|
|
1014
|
+
if (filter !== "all") {
|
|
1015
|
+
const filteredPositions = result.positions.filter((p) => filter === "active"
|
|
1016
|
+
? p.token.status !== "graduated"
|
|
1017
|
+
: p.token.status === "graduated");
|
|
1018
|
+
result = {
|
|
1019
|
+
...result,
|
|
1020
|
+
positions: filteredPositions,
|
|
1021
|
+
total: filteredPositions.length,
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
791
1024
|
outputJson("positions", result);
|
|
792
1025
|
}
|
|
793
1026
|
else if (!globalOpts.quiet) {
|
|
794
|
-
displayPositions(result);
|
|
1027
|
+
displayPositions(result, filter);
|
|
795
1028
|
}
|
|
796
1029
|
process.exit(0);
|
|
797
1030
|
}
|
|
@@ -850,15 +1083,15 @@ Examples:
|
|
|
850
1083
|
process.exit(getExitCode(error));
|
|
851
1084
|
}
|
|
852
1085
|
});
|
|
853
|
-
//
|
|
1086
|
+
// Balances command
|
|
854
1087
|
program
|
|
855
|
-
.command("
|
|
856
|
-
.description("Check wallet
|
|
1088
|
+
.command("balances")
|
|
1089
|
+
.description("Check wallet balances (ETH and USDC)")
|
|
857
1090
|
.addHelpText("after", `
|
|
858
1091
|
Examples:
|
|
859
|
-
httpcat
|
|
860
|
-
httpcat
|
|
861
|
-
httpcat --json
|
|
1092
|
+
httpcat balances
|
|
1093
|
+
httpcat balances --private-key 0x...
|
|
1094
|
+
httpcat --json balances
|
|
862
1095
|
`)
|
|
863
1096
|
.action(async (command) => {
|
|
864
1097
|
try {
|
|
@@ -877,13 +1110,13 @@ Examples:
|
|
|
877
1110
|
privateKey = undefined;
|
|
878
1111
|
}
|
|
879
1112
|
const result = await withLoading(() => checkBalance(privateKey), {
|
|
880
|
-
message: "Checking
|
|
1113
|
+
message: "Checking balances...",
|
|
881
1114
|
json: globalOpts.json,
|
|
882
1115
|
quiet: globalOpts.quiet,
|
|
883
1116
|
spinner: "cat",
|
|
884
1117
|
});
|
|
885
1118
|
if (globalOpts.json) {
|
|
886
|
-
outputJson("
|
|
1119
|
+
outputJson("balances", result);
|
|
887
1120
|
}
|
|
888
1121
|
else if (!globalOpts.quiet) {
|
|
889
1122
|
displayBalance(result);
|
|
@@ -893,7 +1126,7 @@ Examples:
|
|
|
893
1126
|
catch (error) {
|
|
894
1127
|
const globalOpts = command.parent?.opts() || {};
|
|
895
1128
|
if (globalOpts.json) {
|
|
896
|
-
outputError("
|
|
1129
|
+
outputError("balances", error, getExitCode(error));
|
|
897
1130
|
}
|
|
898
1131
|
else {
|
|
899
1132
|
handleError(error, globalOpts.verbose);
|
|
@@ -929,7 +1162,7 @@ Examples:
|
|
|
929
1162
|
outputJson("account", result);
|
|
930
1163
|
}
|
|
931
1164
|
else if (!globalOpts.quiet) {
|
|
932
|
-
displayAccountInfo(result);
|
|
1165
|
+
await displayAccountInfo(result);
|
|
933
1166
|
}
|
|
934
1167
|
process.exit(0);
|
|
935
1168
|
}
|
|
@@ -983,31 +1216,6 @@ accountCommand
|
|
|
983
1216
|
process.exit(getExitCode(error));
|
|
984
1217
|
}
|
|
985
1218
|
});
|
|
986
|
-
accountCommand
|
|
987
|
-
.command("add")
|
|
988
|
-
.description("Add a new seed-derived account")
|
|
989
|
-
.addHelpText("after", `
|
|
990
|
-
Examples:
|
|
991
|
-
httpcat account add # Add a new account from existing seed phrase
|
|
992
|
-
httpcat account add # If no seed phrase, prompts to generate/import one
|
|
993
|
-
`)
|
|
994
|
-
.action(async () => {
|
|
995
|
-
try {
|
|
996
|
-
const { addAccount } = await import("./commands/account.js");
|
|
997
|
-
await addAccount();
|
|
998
|
-
process.exit(0);
|
|
999
|
-
}
|
|
1000
|
-
catch (error) {
|
|
1001
|
-
const globalOpts = accountCommand.parent?.opts() || {};
|
|
1002
|
-
if (globalOpts.json) {
|
|
1003
|
-
outputError("account_add", error, getExitCode(error));
|
|
1004
|
-
}
|
|
1005
|
-
else {
|
|
1006
|
-
handleError(error, globalOpts.verbose);
|
|
1007
|
-
}
|
|
1008
|
-
process.exit(getExitCode(error));
|
|
1009
|
-
}
|
|
1010
|
-
});
|
|
1011
1219
|
accountCommand
|
|
1012
1220
|
.command("add")
|
|
1013
1221
|
.description("Add a new seed-derived account")
|
|
@@ -1037,7 +1245,7 @@ program
|
|
|
1037
1245
|
.command("chat")
|
|
1038
1246
|
.description("Start streaming chat ($0.01 to join, $0.0001 per message, 10 min lease)")
|
|
1039
1247
|
.argument("[token]", "Optional: Token symbol, name, or address for token-specific chat")
|
|
1040
|
-
.option("--input-format <format>", 'Input format (only works with --json): "text" (default), or "stream-json" (realtime streaming input)', "text")
|
|
1248
|
+
.option("-f, --input-format <format>", 'Input format (only works with --json): "text" (default), or "stream-json" (realtime streaming input)', "text")
|
|
1041
1249
|
.addHelpText("after", `
|
|
1042
1250
|
Examples:
|
|
1043
1251
|
httpcat chat # Join general chat
|
|
@@ -1111,6 +1319,14 @@ Configuration:
|
|
|
1111
1319
|
.action(async () => {
|
|
1112
1320
|
await runMcpServer();
|
|
1113
1321
|
});
|
|
1322
|
+
// Help command
|
|
1323
|
+
program
|
|
1324
|
+
.command("help")
|
|
1325
|
+
.description("Display help for httpcat")
|
|
1326
|
+
.action(() => {
|
|
1327
|
+
program.outputHelp();
|
|
1328
|
+
process.exit(0);
|
|
1329
|
+
});
|
|
1114
1330
|
// Interactive shell (default when no command)
|
|
1115
1331
|
program.action(async (options) => {
|
|
1116
1332
|
try {
|