httpcat-cli 0.0.27 → 0.1.1-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/dependabot.yml +2 -0
- package/.github/workflows/README.md +37 -4
- package/.github/workflows/ci.yml +31 -20
- package/.github/workflows/homebrew-tap.yml +1 -1
- package/.github/workflows/publish-switch.yml +41 -0
- package/.github/workflows/rc-publish.yml +196 -0
- package/.github/workflows/release.yml +267 -85
- package/README.md +107 -108
- 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 +743 -35
- 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 +143 -38
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +31 -27
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/positions.d.ts.map +1 -1
- package/dist/commands/positions.js +178 -106
- package/dist/commands/positions.js.map +1 -1
- package/dist/commands/sell.d.ts.map +1 -1
- package/dist/commands/sell.js +720 -28
- package/dist/commands/sell.js.map +1 -1
- package/dist/index.js +321 -104
- 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 +70 -68
- 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/tests/README.md +0 -1
- 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,18 +334,19 @@ 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
|
-
httpcat buy 0x1234567890123456789012345678901234567890 0.20
|
|
339
344
|
httpcat buy abc123-4567-89ab-cdef-0123456789ab 0.20
|
|
340
345
|
httpcat buy "My Token" 0.10
|
|
341
|
-
httpcat buy MTK
|
|
342
|
-
httpcat
|
|
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
|
|
349
|
+
httpcat --json buy abc123-4567-89ab-cdef-0123456789ab 0.10
|
|
343
350
|
httpcat --private-key 0x... buy "PurrfecTT" 0.20
|
|
344
351
|
httpcat buy MTK 0.10 --repeat 10
|
|
345
352
|
httpcat buy MTK 0.10 --repeat 10 --delay 500
|
|
@@ -365,6 +372,27 @@ Examples:
|
|
|
365
372
|
const client = await HttpcatClient.create(privateKey);
|
|
366
373
|
const isTestMode = client.getNetwork().includes("sepolia");
|
|
367
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
|
+
}
|
|
368
396
|
// If repeat is specified, execute multiple buys
|
|
369
397
|
if (repeatCount && repeatCount > 0) {
|
|
370
398
|
const results = [];
|
|
@@ -377,7 +405,7 @@ Examples:
|
|
|
377
405
|
if (!globalOpts.json && !globalOpts.quiet) {
|
|
378
406
|
console.log(chalk.dim(`Buy ${i}/${repeatCount}...`));
|
|
379
407
|
}
|
|
380
|
-
const result = await withLoading(() => buyToken(client, tokenId,
|
|
408
|
+
const result = await withLoading(() => buyToken(client, tokenId, finalAmount, isTestMode, silent || i > 1, privateKey), {
|
|
381
409
|
message: `Buying tokens (${i}/${repeatCount})...`,
|
|
382
410
|
json: globalOpts.json,
|
|
383
411
|
quiet: globalOpts.quiet || i > 1,
|
|
@@ -428,7 +456,7 @@ Examples:
|
|
|
428
456
|
const lastResult = results[results.length - 1];
|
|
429
457
|
const graduationStatus = lastResult.graduationReached
|
|
430
458
|
? "✅ GRADUATED!"
|
|
431
|
-
: `${lastResult.graduationProgress.toFixed(2)}%`;
|
|
459
|
+
: `${(lastResult.graduationProgress || 0).toFixed(2)}%`;
|
|
432
460
|
console.log();
|
|
433
461
|
console.log(chalk.cyan.bold("📊 Repeat Buy Summary"));
|
|
434
462
|
console.log(chalk.dim("─".repeat(50)));
|
|
@@ -446,7 +474,7 @@ Examples:
|
|
|
446
474
|
}
|
|
447
475
|
else {
|
|
448
476
|
// Normal single buy execution
|
|
449
|
-
const result = await withLoading(() => buyToken(client, tokenId,
|
|
477
|
+
const result = await withLoading(() => buyToken(client, tokenId, finalAmount, isTestMode, silent, privateKey), {
|
|
450
478
|
message: "Buying tokens...",
|
|
451
479
|
json: globalOpts.json,
|
|
452
480
|
quiet: globalOpts.quiet,
|
|
@@ -475,16 +503,15 @@ Examples:
|
|
|
475
503
|
// Sell command
|
|
476
504
|
program
|
|
477
505
|
.command("sell")
|
|
478
|
-
.description("Sell tokens
|
|
479
|
-
.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)')
|
|
480
508
|
.argument("<amount>", 'Amount: number (e.g., 1000), percentage (e.g., 50%), or "all"')
|
|
481
509
|
.addHelpText("after", `
|
|
482
510
|
Examples:
|
|
483
|
-
httpcat sell 0x1234567890123456789012345678901234567890 1000
|
|
484
511
|
httpcat sell abc123-4567-89ab-cdef-0123456789ab 1000
|
|
485
512
|
httpcat sell "My Token" 50%
|
|
486
513
|
httpcat sell MTK all
|
|
487
|
-
httpcat --json sell
|
|
514
|
+
httpcat --json sell abc123-4567-89ab-cdef-0123456789ab 25%
|
|
488
515
|
`)
|
|
489
516
|
.action(async (tokenId, amountInput, command) => {
|
|
490
517
|
try {
|
|
@@ -507,19 +534,99 @@ Examples:
|
|
|
507
534
|
// Derive user address from private key
|
|
508
535
|
const account = privateKeyToAccount(privateKey);
|
|
509
536
|
const userAddress = account.address;
|
|
510
|
-
//
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (actualBalance === "0") {
|
|
518
626
|
throw new Error("You do not own any of this token");
|
|
519
627
|
}
|
|
520
|
-
const tokenAmount = parseTokenAmount(amountInput,
|
|
521
|
-
|
|
522
|
-
const result = await withLoading(() => sellToken(client, tokenId, tokenAmount, silent), {
|
|
628
|
+
const tokenAmount = parseTokenAmount(amountInput, actualBalance);
|
|
629
|
+
const result = await withLoading(() => sellToken(client, tokenId, tokenAmount, silent, privateKey), {
|
|
523
630
|
message: "Selling tokens...",
|
|
524
631
|
json: globalOpts.json,
|
|
525
632
|
quiet: globalOpts.quiet,
|
|
@@ -544,18 +651,95 @@ Examples:
|
|
|
544
651
|
process.exit(getExitCode(error));
|
|
545
652
|
}
|
|
546
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
|
+
});
|
|
547
732
|
// Info command
|
|
548
733
|
program
|
|
549
734
|
.command("info")
|
|
550
735
|
.description("Get detailed information about a token")
|
|
551
|
-
.argument("<identifier>", '
|
|
736
|
+
.argument("<identifier>", 'Address, name, or symbol (e.g., 0x1234..., "My Token", or MTK)')
|
|
552
737
|
.addHelpText("after", `
|
|
553
738
|
Examples:
|
|
554
|
-
httpcat info 0x1234567890123456789012345678901234567890
|
|
555
739
|
httpcat info abc123-4567-89ab-cdef-0123456789ab
|
|
556
740
|
httpcat info "My Token"
|
|
557
741
|
httpcat info MTK
|
|
558
|
-
httpcat --json info
|
|
742
|
+
httpcat --json info "PurrfecTT"
|
|
559
743
|
`)
|
|
560
744
|
.action(async (tokenId, command) => {
|
|
561
745
|
try {
|
|
@@ -588,7 +772,7 @@ Examples:
|
|
|
588
772
|
outputJson("token_info", result);
|
|
589
773
|
}
|
|
590
774
|
else if (!globalOpts.quiet) {
|
|
591
|
-
displayTokenInfo(result);
|
|
775
|
+
await displayTokenInfo(result, userAddress);
|
|
592
776
|
}
|
|
593
777
|
process.exit(0);
|
|
594
778
|
}
|
|
@@ -607,9 +791,9 @@ Examples:
|
|
|
607
791
|
program
|
|
608
792
|
.command("list")
|
|
609
793
|
.description("List all tokens with pagination and sorting")
|
|
610
|
-
.option("--page <number>", "Page number (default: 1)", "1")
|
|
611
|
-
.option("--limit <number>", "Items per page (default: 20, max: 100)", "20")
|
|
612
|
-
.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")
|
|
613
797
|
.addHelpText("after", `
|
|
614
798
|
Examples:
|
|
615
799
|
httpcat list
|
|
@@ -662,18 +846,18 @@ Examples:
|
|
|
662
846
|
// Transactions command
|
|
663
847
|
program
|
|
664
848
|
.command("transactions")
|
|
665
|
-
.description("Get paginated list of transactions
|
|
666
|
-
.option("--user <address>", "Filter by user address")
|
|
667
|
-
.option("--token <tokenId>", "Filter by token ID")
|
|
668
|
-
.option("--type <type>", "Filter by type: buy, sell, or airdrop")
|
|
669
|
-
.option("--limit <number>", "Number of results (default: 50, max: 100)", "50")
|
|
670
|
-
.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")
|
|
671
855
|
.addHelpText("after", `
|
|
672
856
|
Examples:
|
|
673
|
-
httpcat transactions
|
|
674
|
-
httpcat transactions --user 0x1234...
|
|
675
|
-
httpcat transactions --token abc123-...
|
|
676
|
-
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
|
|
677
861
|
httpcat --json transactions --user 0x1234... --offset 50
|
|
678
862
|
`)
|
|
679
863
|
.action(async (options, command) => {
|
|
@@ -693,9 +877,37 @@ Examples:
|
|
|
693
877
|
privateKey = await promptForPrivateKey();
|
|
694
878
|
}
|
|
695
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
|
+
}
|
|
696
902
|
const input = {};
|
|
697
|
-
|
|
903
|
+
// Use selected account's address by default, unless --user is explicitly provided
|
|
904
|
+
if (options.user) {
|
|
698
905
|
input.userAddress = options.user;
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
// Default to selected account's address
|
|
909
|
+
input.userAddress = userAddress;
|
|
910
|
+
}
|
|
699
911
|
if (options.token)
|
|
700
912
|
input.tokenId = options.token;
|
|
701
913
|
if (options.type) {
|
|
@@ -737,9 +949,13 @@ Examples:
|
|
|
737
949
|
program
|
|
738
950
|
.command("positions")
|
|
739
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")
|
|
740
954
|
.addHelpText("after", `
|
|
741
955
|
Examples:
|
|
742
956
|
httpcat positions
|
|
957
|
+
httpcat positions --active
|
|
958
|
+
httpcat positions --graduated
|
|
743
959
|
httpcat --json positions
|
|
744
960
|
httpcat --private-key 0x... positions
|
|
745
961
|
`)
|
|
@@ -765,7 +981,9 @@ Examples:
|
|
|
765
981
|
const userAddress = account.address;
|
|
766
982
|
// Show which account is being used and verify it matches (if not quiet and not json)
|
|
767
983
|
if (!globalOpts.quiet && !globalOpts.json) {
|
|
768
|
-
const activeIndex = accountIndex !== undefined
|
|
984
|
+
const activeIndex = accountIndex !== undefined
|
|
985
|
+
? accountIndex
|
|
986
|
+
: config.getActiveAccountIndex();
|
|
769
987
|
const accounts = config.getAllAccounts();
|
|
770
988
|
const accountInfo = accounts.find((acc) => acc.index === activeIndex);
|
|
771
989
|
if (accountInfo) {
|
|
@@ -780,17 +998,33 @@ Examples:
|
|
|
780
998
|
console.log();
|
|
781
999
|
}
|
|
782
1000
|
}
|
|
783
|
-
|
|
1001
|
+
let result = await withLoading(() => getPositions(client, userAddress), {
|
|
784
1002
|
message: "Fetching positions...",
|
|
785
1003
|
json: globalOpts.json,
|
|
786
1004
|
quiet: globalOpts.quiet,
|
|
787
1005
|
spinner: "cat",
|
|
788
1006
|
});
|
|
1007
|
+
const filter = command.active
|
|
1008
|
+
? "active"
|
|
1009
|
+
: command.graduated
|
|
1010
|
+
? "graduated"
|
|
1011
|
+
: "all";
|
|
789
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
|
+
}
|
|
790
1024
|
outputJson("positions", result);
|
|
791
1025
|
}
|
|
792
1026
|
else if (!globalOpts.quiet) {
|
|
793
|
-
displayPositions(result);
|
|
1027
|
+
displayPositions(result, filter);
|
|
794
1028
|
}
|
|
795
1029
|
process.exit(0);
|
|
796
1030
|
}
|
|
@@ -849,15 +1083,15 @@ Examples:
|
|
|
849
1083
|
process.exit(getExitCode(error));
|
|
850
1084
|
}
|
|
851
1085
|
});
|
|
852
|
-
//
|
|
1086
|
+
// Balances command
|
|
853
1087
|
program
|
|
854
|
-
.command("
|
|
855
|
-
.description("Check wallet
|
|
1088
|
+
.command("balances")
|
|
1089
|
+
.description("Check wallet balances (ETH and USDC)")
|
|
856
1090
|
.addHelpText("after", `
|
|
857
1091
|
Examples:
|
|
858
|
-
httpcat
|
|
859
|
-
httpcat
|
|
860
|
-
httpcat --json
|
|
1092
|
+
httpcat balances
|
|
1093
|
+
httpcat balances --private-key 0x...
|
|
1094
|
+
httpcat --json balances
|
|
861
1095
|
`)
|
|
862
1096
|
.action(async (command) => {
|
|
863
1097
|
try {
|
|
@@ -876,13 +1110,13 @@ Examples:
|
|
|
876
1110
|
privateKey = undefined;
|
|
877
1111
|
}
|
|
878
1112
|
const result = await withLoading(() => checkBalance(privateKey), {
|
|
879
|
-
message: "Checking
|
|
1113
|
+
message: "Checking balances...",
|
|
880
1114
|
json: globalOpts.json,
|
|
881
1115
|
quiet: globalOpts.quiet,
|
|
882
1116
|
spinner: "cat",
|
|
883
1117
|
});
|
|
884
1118
|
if (globalOpts.json) {
|
|
885
|
-
outputJson("
|
|
1119
|
+
outputJson("balances", result);
|
|
886
1120
|
}
|
|
887
1121
|
else if (!globalOpts.quiet) {
|
|
888
1122
|
displayBalance(result);
|
|
@@ -892,7 +1126,7 @@ Examples:
|
|
|
892
1126
|
catch (error) {
|
|
893
1127
|
const globalOpts = command.parent?.opts() || {};
|
|
894
1128
|
if (globalOpts.json) {
|
|
895
|
-
outputError("
|
|
1129
|
+
outputError("balances", error, getExitCode(error));
|
|
896
1130
|
}
|
|
897
1131
|
else {
|
|
898
1132
|
handleError(error, globalOpts.verbose);
|
|
@@ -928,7 +1162,7 @@ Examples:
|
|
|
928
1162
|
outputJson("account", result);
|
|
929
1163
|
}
|
|
930
1164
|
else if (!globalOpts.quiet) {
|
|
931
|
-
displayAccountInfo(result);
|
|
1165
|
+
await displayAccountInfo(result);
|
|
932
1166
|
}
|
|
933
1167
|
process.exit(0);
|
|
934
1168
|
}
|
|
@@ -982,31 +1216,6 @@ accountCommand
|
|
|
982
1216
|
process.exit(getExitCode(error));
|
|
983
1217
|
}
|
|
984
1218
|
});
|
|
985
|
-
accountCommand
|
|
986
|
-
.command("add")
|
|
987
|
-
.description("Add a new seed-derived account")
|
|
988
|
-
.addHelpText("after", `
|
|
989
|
-
Examples:
|
|
990
|
-
httpcat account add # Add a new account from existing seed phrase
|
|
991
|
-
httpcat account add # If no seed phrase, prompts to generate/import one
|
|
992
|
-
`)
|
|
993
|
-
.action(async () => {
|
|
994
|
-
try {
|
|
995
|
-
const { addAccount } = await import("./commands/account.js");
|
|
996
|
-
await addAccount();
|
|
997
|
-
process.exit(0);
|
|
998
|
-
}
|
|
999
|
-
catch (error) {
|
|
1000
|
-
const globalOpts = accountCommand.parent?.opts() || {};
|
|
1001
|
-
if (globalOpts.json) {
|
|
1002
|
-
outputError("account_add", error, getExitCode(error));
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
handleError(error, globalOpts.verbose);
|
|
1006
|
-
}
|
|
1007
|
-
process.exit(getExitCode(error));
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1010
1219
|
accountCommand
|
|
1011
1220
|
.command("add")
|
|
1012
1221
|
.description("Add a new seed-derived account")
|
|
@@ -1035,16 +1244,16 @@ Examples:
|
|
|
1035
1244
|
program
|
|
1036
1245
|
.command("chat")
|
|
1037
1246
|
.description("Start streaming chat ($0.01 to join, $0.0001 per message, 10 min lease)")
|
|
1038
|
-
.argument("[token]", "Optional: Token
|
|
1039
|
-
.option("--input-format <format>", 'Input format (only works with --json): "text" (default), or "stream-json" (realtime streaming input)', "text")
|
|
1247
|
+
.argument("[token]", "Optional: Token symbol, name, or address for token-specific chat")
|
|
1248
|
+
.option("-f, --input-format <format>", 'Input format (only works with --json): "text" (default), or "stream-json" (realtime streaming input)', "text")
|
|
1040
1249
|
.addHelpText("after", `
|
|
1041
1250
|
Examples:
|
|
1042
1251
|
httpcat chat # Join general chat
|
|
1043
|
-
httpcat chat 0x1234567890123456789012345678901234567890 # Join chat for token at address
|
|
1044
1252
|
httpcat chat MTK # Join chat for token with symbol "MTK"
|
|
1045
1253
|
httpcat chat "My Token" # Join chat for token with name "My Token"
|
|
1254
|
+
httpcat chat 0x1234... # Join chat for token at address
|
|
1046
1255
|
httpcat --private-key 0x... chat
|
|
1047
|
-
httpcat --json chat
|
|
1256
|
+
httpcat --json chat MTK
|
|
1048
1257
|
httpcat --json --input-format stream-json chat MTK # Structured JSON input
|
|
1049
1258
|
`)
|
|
1050
1259
|
.action(async (tokenIdentifier, options, command) => {
|
|
@@ -1110,6 +1319,14 @@ Configuration:
|
|
|
1110
1319
|
.action(async () => {
|
|
1111
1320
|
await runMcpServer();
|
|
1112
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
|
+
});
|
|
1113
1330
|
// Interactive shell (default when no command)
|
|
1114
1331
|
program.action(async (options) => {
|
|
1115
1332
|
try {
|