httpcat-cli 0.3.0 → 0.3.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/ci.yml +3 -0
- package/.github/workflows/rc-publish.yml +6 -0
- package/.github/workflows/release.yml +102 -0
- package/.github/workflows/sync-version.yml +31 -2
- package/README.md +1408 -109
- package/additions.txt +3 -0
- package/bun.lock +260 -25
- package/dist/agent/autonomous-trader.d.ts.map +1 -0
- package/dist/agent/autonomous-trader.js +362 -0
- package/dist/agent/autonomous-trader.js.map +1 -0
- package/dist/agent/ax-agent.d.ts.map +1 -1
- package/dist/agent/ax-agent.js +356 -18
- package/dist/agent/ax-agent.js.map +1 -1
- package/dist/agent/event-client.d.ts.map +1 -0
- package/dist/agent/event-client.js +82 -0
- package/dist/agent/event-client.js.map +1 -0
- package/dist/agent/log-stream.d.ts.map +1 -0
- package/dist/agent/log-stream.js +95 -0
- package/dist/agent/log-stream.js.map +1 -0
- package/dist/agent/memory/conversation-session.d.ts.map +1 -0
- package/dist/agent/memory/conversation-session.js +232 -0
- package/dist/agent/memory/conversation-session.js.map +1 -0
- package/dist/agent/memory/conversation-store.d.ts.map +1 -0
- package/dist/agent/memory/conversation-store.js +214 -0
- package/dist/agent/memory/conversation-store.js.map +1 -0
- package/dist/agent/memory/database-schema.d.ts.map +1 -0
- package/dist/agent/memory/database-schema.js +355 -0
- package/dist/agent/memory/database-schema.js.map +1 -0
- package/dist/agent/memory/decision-tracker.d.ts.map +1 -0
- package/dist/agent/memory/decision-tracker.js +274 -0
- package/dist/agent/memory/decision-tracker.js.map +1 -0
- package/dist/agent/memory/memory-manager.d.ts.map +1 -0
- package/dist/agent/memory/memory-manager.js +187 -0
- package/dist/agent/memory/memory-manager.js.map +1 -0
- package/dist/agent/memory/types.d.ts.map +1 -0
- package/dist/agent/memory/types.js +5 -0
- package/dist/agent/memory/types.js.map +1 -0
- package/dist/agent/message-formatter.d.ts.map +1 -0
- package/dist/agent/message-formatter.js +76 -0
- package/dist/agent/message-formatter.js.map +1 -0
- package/dist/agent/position-db.d.ts.map +1 -0
- package/dist/agent/position-db.js +154 -0
- package/dist/agent/position-db.js.map +1 -0
- package/dist/agent/simple-chat-ui-static.d.ts.map +1 -0
- package/dist/agent/simple-chat-ui-static.js +129 -0
- package/dist/agent/simple-chat-ui-static.js.map +1 -0
- package/dist/agent/simple-chat-ui.d.ts.map +1 -0
- package/dist/agent/simple-chat-ui.js +90 -0
- package/dist/agent/simple-chat-ui.js.map +1 -0
- package/dist/agent/tools.d.ts.map +1 -1
- package/dist/agent/tools.js +297 -4
- package/dist/agent/tools.js.map +1 -1
- package/dist/agent/ui.d.ts.map +1 -0
- package/dist/agent/ui.js +84 -0
- package/dist/agent/ui.js.map +1 -0
- package/dist/agent/unified-runtime.d.ts.map +1 -0
- package/dist/agent/unified-runtime.js +397 -0
- package/dist/agent/unified-runtime.js.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +272 -21
- package/dist/client.js.map +1 -1
- package/dist/commands/account.d.ts.map +1 -1
- package/dist/commands/account.js +187 -33
- package/dist/commands/account.js.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +125 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/approve.d.ts.map +1 -0
- package/dist/commands/approve.js +505 -0
- package/dist/commands/approve.js.map +1 -0
- package/dist/commands/automation.d.ts.map +1 -0
- package/dist/commands/automation.js +346 -0
- package/dist/commands/automation.js.map +1 -0
- package/dist/commands/balances.d.ts.map +1 -1
- package/dist/commands/balances.js +226 -73
- package/dist/commands/balances.js.map +1 -1
- package/dist/commands/buy.d.ts.map +1 -1
- package/dist/commands/buy.js +149 -146
- package/dist/commands/buy.js.map +1 -1
- package/dist/commands/call.d.ts.map +1 -0
- package/dist/commands/call.js +51 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/cex.d.ts.map +1 -0
- package/dist/commands/cex.js +958 -0
- package/dist/commands/cex.js.map +1 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +169 -411
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/claim.d.ts.map +1 -1
- package/dist/commands/claim.js +313 -29
- package/dist/commands/claim.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +151 -43
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/gasless-swap.d.ts.map +1 -0
- package/dist/commands/gasless-swap.js +232 -0
- package/dist/commands/gasless-swap.js.map +1 -0
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +63 -7
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +131 -47
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/launchpad.d.ts.map +1 -0
- package/dist/commands/launchpad.js +708 -0
- package/dist/commands/launchpad.js.map +1 -0
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +57 -23
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/market.d.ts.map +1 -0
- package/dist/commands/market.js +960 -0
- package/dist/commands/market.js.map +1 -0
- package/dist/commands/mcp-install.d.ts.map +1 -0
- package/dist/commands/mcp-install.js +387 -0
- package/dist/commands/mcp-install.js.map +1 -0
- package/dist/commands/opps.d.ts.map +1 -0
- package/dist/commands/opps.js +409 -0
- package/dist/commands/opps.js.map +1 -0
- package/dist/commands/perps.d.ts.map +1 -0
- package/dist/commands/perps.js +248 -0
- package/dist/commands/perps.js.map +1 -0
- package/dist/commands/portfolio.d.ts.map +1 -0
- package/dist/commands/portfolio.js +679 -0
- package/dist/commands/portfolio.js.map +1 -0
- package/dist/commands/positions.d.ts.map +1 -1
- package/dist/commands/positions.js +76 -47
- package/dist/commands/positions.js.map +1 -1
- package/dist/commands/predict.d.ts.map +1 -0
- package/dist/commands/predict.js +280 -0
- package/dist/commands/predict.js.map +1 -0
- package/dist/commands/predictions.d.ts.map +1 -0
- package/dist/commands/predictions.js +486 -0
- package/dist/commands/predictions.js.map +1 -0
- package/dist/commands/risk.d.ts.map +1 -0
- package/dist/commands/risk.js +225 -0
- package/dist/commands/risk.js.map +1 -0
- package/dist/commands/security.d.ts.map +1 -0
- package/dist/commands/security.js +244 -0
- package/dist/commands/security.js.map +1 -0
- package/dist/commands/sell.d.ts.map +1 -1
- package/dist/commands/sell.js +67 -34
- package/dist/commands/sell.js.map +1 -1
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +733 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/sign.d.ts.map +1 -0
- package/dist/commands/sign.js +1048 -0
- package/dist/commands/sign.js.map +1 -0
- package/dist/commands/swap.d.ts.map +1 -0
- package/dist/commands/swap.js +744 -0
- package/dist/commands/swap.js.map +1 -0
- package/dist/commands/system.d.ts.map +1 -0
- package/dist/commands/system.js +417 -0
- package/dist/commands/system.js.map +1 -0
- package/dist/commands/tools/index.d.ts.map +1 -0
- package/dist/commands/tools/index.js +2040 -0
- package/dist/commands/tools/index.js.map +1 -0
- package/dist/commands/trade.d.ts.map +1 -0
- package/dist/commands/trade.js +237 -0
- package/dist/commands/trade.js.map +1 -0
- package/dist/commands/transactions.d.ts.map +1 -1
- package/dist/commands/transactions.js +29 -17
- package/dist/commands/transactions.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +429 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +351 -40
- package/dist/config.js.map +1 -1
- package/dist/index.js +4524 -924
- package/dist/index.js.map +1 -1
- package/dist/interactive/art.d.ts.map +1 -1
- package/dist/interactive/art.js +33 -1
- package/dist/interactive/art.js.map +1 -1
- package/dist/interactive/shell.d.ts.map +1 -1
- package/dist/interactive/shell.js +467 -2652
- package/dist/interactive/shell.js.map +1 -1
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/context.js +211 -0
- package/dist/mcp/context.js.map +1 -0
- package/dist/mcp/onboarding.d.ts.map +1 -0
- package/dist/mcp/onboarding.js +266 -0
- package/dist/mcp/onboarding.js.map +1 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +222 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +51 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +4119 -169
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/types/agent-info.d.ts.map +1 -0
- package/dist/types/agent-info.js +11 -0
- package/dist/types/agent-info.js.map +1 -0
- package/dist/ui/components/ScrollableList.d.ts.map +1 -0
- package/dist/ui/components/ScrollableList.js +72 -0
- package/dist/ui/components/ScrollableList.js.map +1 -0
- package/dist/ui/components/ThemeProvider.d.ts.map +1 -0
- package/dist/ui/components/ThemeProvider.js +87 -0
- package/dist/ui/components/ThemeProvider.js.map +1 -0
- package/dist/ui/components/ThemedBox.d.ts.map +1 -0
- package/dist/ui/components/ThemedBox.js +24 -0
- package/dist/ui/components/ThemedBox.js.map +1 -0
- package/dist/ui/components/agent/ChatHeader.d.ts.map +1 -0
- package/dist/ui/components/agent/ChatHeader.js +39 -0
- package/dist/ui/components/agent/ChatHeader.js.map +1 -0
- package/dist/ui/components/agent/Header.d.ts.map +1 -0
- package/dist/ui/components/agent/Header.js +14 -0
- package/dist/ui/components/agent/Header.js.map +1 -0
- package/dist/ui/components/agent/Input.d.ts.map +1 -0
- package/dist/ui/components/agent/Input.js +23 -0
- package/dist/ui/components/agent/Input.js.map +1 -0
- package/dist/ui/components/agent/Output.d.ts.map +1 -0
- package/dist/ui/components/agent/Output.js +23 -0
- package/dist/ui/components/agent/Output.js.map +1 -0
- package/dist/ui/components/chat/TokenChatUI.d.ts.map +1 -0
- package/dist/ui/components/chat/TokenChatUI.js +133 -0
- package/dist/ui/components/chat/TokenChatUI.js.map +1 -0
- package/dist/ui/components/shell/ShellHeader.d.ts.map +1 -0
- package/dist/ui/components/shell/ShellHeader.js +31 -0
- package/dist/ui/components/shell/ShellHeader.js.map +1 -0
- package/dist/ui/components/shell/ShellInput.d.ts.map +1 -0
- package/dist/ui/components/shell/ShellInput.js +151 -0
- package/dist/ui/components/shell/ShellInput.js.map +1 -0
- package/dist/ui/components/shell/ShellOutput.d.ts.map +1 -0
- package/dist/ui/components/shell/ShellOutput.js +8 -0
- package/dist/ui/components/shell/ShellOutput.js.map +1 -0
- package/dist/ui/hooks/useChatWebSocket.d.ts.map +1 -0
- package/dist/ui/hooks/useChatWebSocket.js +76 -0
- package/dist/ui/hooks/useChatWebSocket.js.map +1 -0
- package/dist/ui/hooks/useCommandHistory.d.ts.map +1 -0
- package/dist/ui/hooks/useCommandHistory.js +70 -0
- package/dist/ui/hooks/useCommandHistory.js.map +1 -0
- package/dist/ui/hooks/useDebounce.d.ts.map +1 -0
- package/dist/ui/hooks/useDebounce.js +17 -0
- package/dist/ui/hooks/useDebounce.js.map +1 -0
- package/dist/ui/hooks/useLogStream.d.ts.map +1 -0
- package/dist/ui/hooks/useLogStream.js +20 -0
- package/dist/ui/hooks/useLogStream.js.map +1 -0
- package/dist/ui/hooks/useVirtualScroll.d.ts.map +1 -0
- package/dist/ui/hooks/useVirtualScroll.js +70 -0
- package/dist/ui/hooks/useVirtualScroll.js.map +1 -0
- package/dist/utils/admin.d.ts.map +1 -0
- package/dist/utils/admin.js +144 -0
- package/dist/utils/admin.js.map +1 -0
- package/dist/utils/autoSetup.d.ts.map +1 -0
- package/dist/utils/autoSetup.js +252 -0
- package/dist/utils/autoSetup.js.map +1 -0
- package/dist/utils/build-constants.d.ts.map +1 -0
- package/dist/utils/build-constants.js +10 -0
- package/dist/utils/build-constants.js.map +1 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +10 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/formatting.js +46 -9
- package/dist/utils/formatting.js.map +1 -1
- package/dist/utils/llm-cli-config.d.ts.map +1 -0
- package/dist/utils/llm-cli-config.js +963 -0
- package/dist/utils/llm-cli-config.js.map +1 -0
- package/dist/utils/llm-cli-detector.d.ts.map +1 -0
- package/dist/utils/llm-cli-detector.js +202 -0
- package/dist/utils/llm-cli-detector.js.map +1 -0
- package/dist/utils/loading.d.ts.map +1 -1
- package/dist/utils/loading.js +25 -3
- package/dist/utils/loading.js.map +1 -1
- package/dist/utils/maintenance.d.ts.map +1 -0
- package/dist/utils/maintenance.js +17 -0
- package/dist/utils/maintenance.js.map +1 -0
- package/dist/utils/mcp-config.d.ts.map +1 -0
- package/dist/utils/mcp-config.js +77 -0
- package/dist/utils/mcp-config.js.map +1 -0
- package/dist/utils/privateKeyPrompt.d.ts.map +1 -1
- package/dist/utils/privateKeyPrompt.js +308 -129
- package/dist/utils/privateKeyPrompt.js.map +1 -1
- package/dist/utils/process-cleanup.d.ts.map +1 -0
- package/dist/utils/process-cleanup.js +136 -0
- package/dist/utils/process-cleanup.js.map +1 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +56 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/rpc-helpers.d.ts.map +1 -0
- package/dist/utils/rpc-helpers.js +70 -0
- package/dist/utils/rpc-helpers.js.map +1 -0
- package/dist/utils/rpc-transport.d.ts.map +1 -0
- package/dist/utils/rpc-transport.js +87 -0
- package/dist/utils/rpc-transport.js.map +1 -0
- package/dist/utils/shell-setup.d.ts.map +1 -0
- package/dist/utils/shell-setup.js +531 -0
- package/dist/utils/shell-setup.js.map +1 -0
- package/dist/utils/status.d.ts.map +1 -1
- package/dist/utils/status.js +34 -5
- package/dist/utils/status.js.map +1 -1
- package/dist/utils/token-resolver.d.ts.map +1 -1
- package/dist/utils/token-resolver.js +51 -8
- package/dist/utils/token-resolver.js.map +1 -1
- package/dist/utils/x402-caller.d.ts.map +1 -0
- package/dist/utils/x402-caller.js +17 -0
- package/dist/utils/x402-caller.js.map +1 -0
- package/docs/README.md +28 -0
- package/docs/agent/README.md +18 -0
- package/docs/api/README.md +41 -0
- package/docs/cli/README.md +42 -0
- package/docs/guides/README.md +26 -0
- package/docs/implementation/README.md +18 -0
- package/docs/planning/README.md +19 -0
- package/docs/testing/README.md +15 -0
- package/docs/ux/README.md +16 -0
- package/issues.txt +2 -0
- package/package.json +24 -9
- package/scripts/cat-spin.sh +417 -0
- package/scripts/deprecate-rc-versions.js +58 -0
- package/scripts/inject-build-constants.js +43 -0
- package/scripts/monitor-foobar.js +117 -0
- package/swap.logs +61 -0
- package/swapping.txt +108 -0
- package/test.txt +12 -0
- package/tests/fixtures/test-data.json +16 -0
- package/Screenshot 2025-12-21 at 8.56.02/342/200/257PM.png +0 -0
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { HttpcatClient } from "../client.js";
|
|
3
|
+
import { config } from "../config.js";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { printBox, formatCurrency, formatTokenAmount, createTable, } from "../utils/formatting.js";
|
|
6
|
+
import { withLoading } from "../utils/loading.js";
|
|
7
|
+
import { outputJson, outputError } from "../headless/json-output.js";
|
|
8
|
+
import { handleError, getExitCode } from "../utils/errors.js";
|
|
9
|
+
import { promptForPrivateKey } from "../utils/privateKeyPrompt.js";
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Shared Utilities
|
|
12
|
+
// ============================================================================
|
|
13
|
+
function getPrivateKey(cliPrivateKey, accountIndex) {
|
|
14
|
+
if (cliPrivateKey)
|
|
15
|
+
return cliPrivateKey;
|
|
16
|
+
const envKey = process.env.HTTPCAT_PRIVATE_KEY;
|
|
17
|
+
if (envKey)
|
|
18
|
+
return envKey;
|
|
19
|
+
try {
|
|
20
|
+
const index = accountIndex !== undefined
|
|
21
|
+
? accountIndex
|
|
22
|
+
: config.getActiveAccountIndex();
|
|
23
|
+
return config.getAccountPrivateKey(index);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return config.get("privateKey");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function isConfigured(cliPrivateKey) {
|
|
30
|
+
if (cliPrivateKey)
|
|
31
|
+
return true;
|
|
32
|
+
return config.isConfigured();
|
|
33
|
+
}
|
|
34
|
+
async function ensureWalletUnlocked() {
|
|
35
|
+
const password = config.getPassword();
|
|
36
|
+
if (!password) {
|
|
37
|
+
config.ensureSessionValid();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!config.isSessionValid()) {
|
|
41
|
+
const inquirer = (await import("inquirer")).default;
|
|
42
|
+
const answers = await inquirer.prompt([
|
|
43
|
+
{
|
|
44
|
+
type: "password",
|
|
45
|
+
name: "password",
|
|
46
|
+
message: "Enter password to unlock wallet:",
|
|
47
|
+
mask: "•",
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
await config.unlockSession(answers.password);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Normalize symbol format (BTC/USD, BTC-USD, BTCUSD -> BTC/USD)
|
|
54
|
+
function normalizeSymbol(symbol) {
|
|
55
|
+
// Remove spaces
|
|
56
|
+
symbol = symbol.trim().toUpperCase();
|
|
57
|
+
// If already has /, return as is
|
|
58
|
+
if (symbol.includes("/")) {
|
|
59
|
+
return symbol;
|
|
60
|
+
}
|
|
61
|
+
// If has -, replace with /
|
|
62
|
+
if (symbol.includes("-")) {
|
|
63
|
+
return symbol.replace("-", "/");
|
|
64
|
+
}
|
|
65
|
+
// If it's like BTCUSD, try to split (assume 3-4 char base, rest is quote)
|
|
66
|
+
// This is a heuristic - common pairs are BTC/USD, ETH/USD, etc.
|
|
67
|
+
const commonBases = ["BTC", "ETH", "USDT", "USDC", "BNB", "SOL", "ADA", "DOT", "MATIC", "AVAX"];
|
|
68
|
+
for (const base of commonBases) {
|
|
69
|
+
if (symbol.startsWith(base)) {
|
|
70
|
+
const quote = symbol.substring(base.length);
|
|
71
|
+
if (quote.length >= 2) {
|
|
72
|
+
return `${base}/${quote}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Default: assume it's already correct or return as is
|
|
77
|
+
return symbol;
|
|
78
|
+
}
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// CEX.IO Command Group
|
|
81
|
+
// ============================================================================
|
|
82
|
+
export function createCexCommand() {
|
|
83
|
+
const cexCommand = new Command("cex")
|
|
84
|
+
.description("CEX.IO exchange trading & market data")
|
|
85
|
+
.addHelpText("after", `
|
|
86
|
+
Examples:
|
|
87
|
+
httpcat cex markets
|
|
88
|
+
httpcat cex ticker BTC/USD
|
|
89
|
+
httpcat cex balance
|
|
90
|
+
httpcat cex orderbook BTC/USD --depth 20
|
|
91
|
+
httpcat cex orders
|
|
92
|
+
httpcat cex order-history --status completed
|
|
93
|
+
httpcat cex trade-history --symbol BTC/USD
|
|
94
|
+
httpcat cex place-order BTC/USD buy limit 0.1 --price 50000
|
|
95
|
+
httpcat cex cancel-order 12345
|
|
96
|
+
`);
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// 1. Markets Command
|
|
99
|
+
// ============================================================================
|
|
100
|
+
cexCommand
|
|
101
|
+
.command("markets")
|
|
102
|
+
.description("Get all available trading pairs on CEX.IO (FREE)")
|
|
103
|
+
.option("--base <currency>", "Filter by base currency (e.g., BTC)")
|
|
104
|
+
.option("--quote <currency>", "Filter by quote currency (e.g., USD)")
|
|
105
|
+
.action(async (options, command) => {
|
|
106
|
+
try {
|
|
107
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
108
|
+
const accountIndex = globalOpts.account;
|
|
109
|
+
await ensureWalletUnlocked();
|
|
110
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
111
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
112
|
+
if (globalOpts.json) {
|
|
113
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
114
|
+
process.exit(2);
|
|
115
|
+
}
|
|
116
|
+
privateKey = await promptForPrivateKey();
|
|
117
|
+
}
|
|
118
|
+
const client = await HttpcatClient.create(privateKey);
|
|
119
|
+
const result = await withLoading(async () => {
|
|
120
|
+
const { data } = await client.invoke("cex/markets", {
|
|
121
|
+
baseCurrency: options.base,
|
|
122
|
+
quoteCurrency: options.quote,
|
|
123
|
+
});
|
|
124
|
+
return data;
|
|
125
|
+
}, {
|
|
126
|
+
message: "Fetching markets...",
|
|
127
|
+
json: globalOpts.json,
|
|
128
|
+
quiet: globalOpts.quiet,
|
|
129
|
+
spinner: "cat",
|
|
130
|
+
clearOnSuccess: true,
|
|
131
|
+
});
|
|
132
|
+
if (globalOpts.json) {
|
|
133
|
+
outputJson("cex_markets", {
|
|
134
|
+
command: "cex_markets",
|
|
135
|
+
data: result,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else if (!globalOpts.quiet) {
|
|
139
|
+
console.log();
|
|
140
|
+
const markets = result.markets || result.pairs || [];
|
|
141
|
+
console.log(chalk.magenta.bold(`📈 CEX.IO Markets (${markets.length} pairs)`));
|
|
142
|
+
console.log();
|
|
143
|
+
if (markets.length > 0) {
|
|
144
|
+
const table = createTable({
|
|
145
|
+
head: ["Pair", "Last Price", "24h Change", "24h Volume"],
|
|
146
|
+
colWidths: [15, 15, 15, 20],
|
|
147
|
+
});
|
|
148
|
+
for (const market of markets.slice(0, 50)) {
|
|
149
|
+
const pair = market.symbol || market.pair || "";
|
|
150
|
+
const lastPrice = market.lastPrice || market.price || 0;
|
|
151
|
+
const change24h = market.change24h || market.priceChange24h || 0;
|
|
152
|
+
const volume24h = market.volume24h || market.totalVolume24h || 0;
|
|
153
|
+
const changeColor = change24h >= 0 ? chalk.green : chalk.red;
|
|
154
|
+
const changeEmoji = change24h >= 0 ? "🟢" : "🔴";
|
|
155
|
+
table.push([
|
|
156
|
+
chalk.cyan(pair),
|
|
157
|
+
formatCurrency(lastPrice),
|
|
158
|
+
changeColor(`${change24h >= 0 ? "+" : ""}${change24h.toFixed(2)}% ${changeEmoji}`),
|
|
159
|
+
formatCurrency(volume24h),
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
console.log(table.toString());
|
|
163
|
+
if (markets.length > 50) {
|
|
164
|
+
console.log(chalk.dim(`... and ${markets.length - 50} more markets`));
|
|
165
|
+
}
|
|
166
|
+
console.log();
|
|
167
|
+
console.log(chalk.dim(`Total: ${markets.length} markets`));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
console.log(chalk.dim("No markets found."));
|
|
171
|
+
}
|
|
172
|
+
console.log();
|
|
173
|
+
}
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
178
|
+
if (globalOpts.json) {
|
|
179
|
+
outputError("cex_markets", error, getExitCode(error));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
handleError(error, globalOpts.verbose);
|
|
183
|
+
}
|
|
184
|
+
process.exit(getExitCode(error));
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// 2. Ticker Command
|
|
189
|
+
// ============================================================================
|
|
190
|
+
cexCommand
|
|
191
|
+
.command("ticker")
|
|
192
|
+
.description("Get detailed ticker information for a trading pair (FREE)")
|
|
193
|
+
.argument("<symbol>", "Trading pair (supports BTC/USD, BTC-USD, or BTCUSD)")
|
|
194
|
+
.action(async (symbol, options, command) => {
|
|
195
|
+
try {
|
|
196
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
197
|
+
const accountIndex = globalOpts.account;
|
|
198
|
+
await ensureWalletUnlocked();
|
|
199
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
200
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
201
|
+
if (globalOpts.json) {
|
|
202
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
203
|
+
process.exit(2);
|
|
204
|
+
}
|
|
205
|
+
privateKey = await promptForPrivateKey();
|
|
206
|
+
}
|
|
207
|
+
const client = await HttpcatClient.create(privateKey);
|
|
208
|
+
const normalizedSymbol = normalizeSymbol(symbol);
|
|
209
|
+
const result = await withLoading(async () => {
|
|
210
|
+
const { data } = await client.invoke("cex/ticker", {
|
|
211
|
+
symbol: normalizedSymbol,
|
|
212
|
+
});
|
|
213
|
+
return data;
|
|
214
|
+
}, {
|
|
215
|
+
message: "Fetching ticker...",
|
|
216
|
+
json: globalOpts.json,
|
|
217
|
+
quiet: globalOpts.quiet,
|
|
218
|
+
spinner: "cat",
|
|
219
|
+
clearOnSuccess: true,
|
|
220
|
+
});
|
|
221
|
+
if (globalOpts.json) {
|
|
222
|
+
outputJson("cex_ticker", {
|
|
223
|
+
command: "cex_ticker",
|
|
224
|
+
data: result,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
else if (!globalOpts.quiet) {
|
|
228
|
+
console.log();
|
|
229
|
+
console.log(chalk.magenta.bold(`📊 Ticker: ${normalizedSymbol}`));
|
|
230
|
+
console.log();
|
|
231
|
+
const lastPrice = result.lastPrice || result.price || 0;
|
|
232
|
+
const change24h = result.change24h || result.priceChange24h || 0;
|
|
233
|
+
const changeAmount = result.changeAmount24h || 0;
|
|
234
|
+
const bid = result.bid || 0;
|
|
235
|
+
const ask = result.ask || 0;
|
|
236
|
+
const spread = ask - bid;
|
|
237
|
+
const spreadPercent = lastPrice > 0 ? (spread / lastPrice) * 100 : 0;
|
|
238
|
+
const high24h = result.high24h || result.high || 0;
|
|
239
|
+
const low24h = result.low24h || result.low || 0;
|
|
240
|
+
const volume24h = result.volume24h || result.totalVolume24h || 0;
|
|
241
|
+
const changeColor = change24h >= 0 ? chalk.green : chalk.red;
|
|
242
|
+
const changeEmoji = change24h >= 0 ? "🟢" : "🔴";
|
|
243
|
+
const boxData = {
|
|
244
|
+
"Last Price": chalk.green.bold(formatCurrency(lastPrice)),
|
|
245
|
+
"24h Change": changeColor(`${changeAmount >= 0 ? "+" : ""}${formatCurrency(Math.abs(changeAmount))} (${change24h >= 0 ? "+" : ""}${change24h.toFixed(2)}%) ${changeEmoji}`),
|
|
246
|
+
Bid: formatCurrency(bid),
|
|
247
|
+
Ask: formatCurrency(ask),
|
|
248
|
+
Spread: `${formatCurrency(spread)} (${spreadPercent.toFixed(2)}%)`,
|
|
249
|
+
"24h High": formatCurrency(high24h),
|
|
250
|
+
"24h Low": formatCurrency(low24h),
|
|
251
|
+
"24h Volume": formatCurrency(volume24h),
|
|
252
|
+
};
|
|
253
|
+
printBox("Ticker Data", boxData);
|
|
254
|
+
console.log();
|
|
255
|
+
}
|
|
256
|
+
process.exit(0);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
260
|
+
if (globalOpts.json) {
|
|
261
|
+
outputError("cex_ticker", error, getExitCode(error));
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
handleError(error, globalOpts.verbose);
|
|
265
|
+
}
|
|
266
|
+
process.exit(getExitCode(error));
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// 3. Balance Command
|
|
271
|
+
// ============================================================================
|
|
272
|
+
cexCommand
|
|
273
|
+
.command("balance")
|
|
274
|
+
.description("Get all asset balances in CEX.IO account ($0.001)")
|
|
275
|
+
.option("--currency <currency>", "Filter by specific currency (e.g., BTC, USD)")
|
|
276
|
+
.action(async (options, command) => {
|
|
277
|
+
try {
|
|
278
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
279
|
+
const accountIndex = globalOpts.account;
|
|
280
|
+
await ensureWalletUnlocked();
|
|
281
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
282
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
283
|
+
if (globalOpts.json) {
|
|
284
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
285
|
+
process.exit(2);
|
|
286
|
+
}
|
|
287
|
+
privateKey = await promptForPrivateKey();
|
|
288
|
+
}
|
|
289
|
+
const client = await HttpcatClient.create(privateKey);
|
|
290
|
+
const result = await withLoading(async () => {
|
|
291
|
+
const { data } = await client.invoke("cex/balance", {
|
|
292
|
+
currency: options.currency,
|
|
293
|
+
});
|
|
294
|
+
return data;
|
|
295
|
+
}, {
|
|
296
|
+
message: "Fetching balance ($0.001)...",
|
|
297
|
+
json: globalOpts.json,
|
|
298
|
+
quiet: globalOpts.quiet,
|
|
299
|
+
spinner: "cat",
|
|
300
|
+
clearOnSuccess: true,
|
|
301
|
+
});
|
|
302
|
+
if (globalOpts.json) {
|
|
303
|
+
outputJson("cex_balance", {
|
|
304
|
+
command: "cex_balance",
|
|
305
|
+
data: result,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
else if (!globalOpts.quiet) {
|
|
309
|
+
console.log();
|
|
310
|
+
console.log(chalk.magenta.bold("💰 CEX.IO Balance"));
|
|
311
|
+
console.log();
|
|
312
|
+
const balances = result.balances || result.assets || [];
|
|
313
|
+
const totalValue = result.totalValue || result.totalPortfolioValue || 0;
|
|
314
|
+
if (totalValue > 0) {
|
|
315
|
+
console.log(chalk.bold(`Total Portfolio Value: ${formatCurrency(totalValue)}`));
|
|
316
|
+
console.log();
|
|
317
|
+
}
|
|
318
|
+
if (balances.length > 0) {
|
|
319
|
+
const table = createTable({
|
|
320
|
+
head: ["Currency", "Available", "Locked in Orders", "Total"],
|
|
321
|
+
colWidths: [12, 18, 20, 18],
|
|
322
|
+
});
|
|
323
|
+
for (const balance of balances) {
|
|
324
|
+
const currency = balance.currency || balance.symbol || "";
|
|
325
|
+
const available = parseFloat(balance.available?.toString() || "0");
|
|
326
|
+
const locked = parseFloat(balance.locked?.toString() || "0");
|
|
327
|
+
const total = parseFloat(balance.total?.toString() || "0");
|
|
328
|
+
table.push([
|
|
329
|
+
chalk.cyan(currency.toUpperCase()),
|
|
330
|
+
formatTokenAmount(available.toString()),
|
|
331
|
+
formatTokenAmount(locked.toString()),
|
|
332
|
+
formatTokenAmount(total.toString()),
|
|
333
|
+
]);
|
|
334
|
+
}
|
|
335
|
+
console.log(table.toString());
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
console.log(chalk.dim("No balances found."));
|
|
339
|
+
}
|
|
340
|
+
console.log();
|
|
341
|
+
}
|
|
342
|
+
process.exit(0);
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
346
|
+
if (globalOpts.json) {
|
|
347
|
+
outputError("cex_balance", error, getExitCode(error));
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
handleError(error, globalOpts.verbose);
|
|
351
|
+
}
|
|
352
|
+
process.exit(getExitCode(error));
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
// ============================================================================
|
|
356
|
+
// 4. Orderbook Command
|
|
357
|
+
// ============================================================================
|
|
358
|
+
cexCommand
|
|
359
|
+
.command("orderbook")
|
|
360
|
+
.description("Get current order book for a trading pair ($0.001)")
|
|
361
|
+
.argument("<symbol>", "Trading pair (e.g., BTC/USD)")
|
|
362
|
+
.option("--depth <depth>", "Order book depth", "20")
|
|
363
|
+
.action(async (symbol, options, command) => {
|
|
364
|
+
try {
|
|
365
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
366
|
+
const accountIndex = globalOpts.account;
|
|
367
|
+
await ensureWalletUnlocked();
|
|
368
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
369
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
370
|
+
if (globalOpts.json) {
|
|
371
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
372
|
+
process.exit(2);
|
|
373
|
+
}
|
|
374
|
+
privateKey = await promptForPrivateKey();
|
|
375
|
+
}
|
|
376
|
+
const client = await HttpcatClient.create(privateKey);
|
|
377
|
+
const normalizedSymbol = normalizeSymbol(symbol);
|
|
378
|
+
const depth = Math.min(parseInt(options.depth || "20"), 100);
|
|
379
|
+
const result = await withLoading(async () => {
|
|
380
|
+
const { data } = await client.invoke("cex/orderbook", {
|
|
381
|
+
symbol: normalizedSymbol,
|
|
382
|
+
depth,
|
|
383
|
+
});
|
|
384
|
+
return data;
|
|
385
|
+
}, {
|
|
386
|
+
message: "Fetching orderbook ($0.001)...",
|
|
387
|
+
json: globalOpts.json,
|
|
388
|
+
quiet: globalOpts.quiet,
|
|
389
|
+
spinner: "cat",
|
|
390
|
+
clearOnSuccess: true,
|
|
391
|
+
});
|
|
392
|
+
if (globalOpts.json) {
|
|
393
|
+
outputJson("cex_orderbook", {
|
|
394
|
+
command: "cex_orderbook",
|
|
395
|
+
data: result,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
else if (!globalOpts.quiet) {
|
|
399
|
+
console.log();
|
|
400
|
+
console.log(chalk.magenta.bold(`📖 Orderbook: ${normalizedSymbol} (Depth: ${depth})`));
|
|
401
|
+
console.log();
|
|
402
|
+
const asks = result.asks || [];
|
|
403
|
+
const bids = result.bids || [];
|
|
404
|
+
const bestAsk = asks[0]?.[0] || 0;
|
|
405
|
+
const bestBid = bids[0]?.[0] || 0;
|
|
406
|
+
const spread = bestAsk - bestBid;
|
|
407
|
+
const spreadPercent = bestAsk > 0 ? (spread / bestAsk) * 100 : 0;
|
|
408
|
+
console.log(chalk.dim(`Spread: ${formatCurrency(spread)} (${spreadPercent.toFixed(2)}%)`));
|
|
409
|
+
console.log();
|
|
410
|
+
// Create side-by-side table
|
|
411
|
+
const maxRows = Math.max(asks.length, bids.length);
|
|
412
|
+
const table = createTable({
|
|
413
|
+
head: [
|
|
414
|
+
"ASKS (Sell Orders)",
|
|
415
|
+
"",
|
|
416
|
+
"│",
|
|
417
|
+
"BIDS (Buy Orders)",
|
|
418
|
+
"",
|
|
419
|
+
],
|
|
420
|
+
colWidths: [15, 15, 3, 15, 15],
|
|
421
|
+
});
|
|
422
|
+
for (let i = 0; i < Math.min(maxRows, depth); i++) {
|
|
423
|
+
const ask = asks[i] || [];
|
|
424
|
+
const bid = bids[i] || [];
|
|
425
|
+
table.push([
|
|
426
|
+
ask[0] ? formatCurrency(ask[0]) : "",
|
|
427
|
+
ask[1] ? formatTokenAmount(ask[1].toString()) : "",
|
|
428
|
+
"│",
|
|
429
|
+
bid[0] ? formatCurrency(bid[0]) : "",
|
|
430
|
+
bid[1] ? formatTokenAmount(bid[1].toString()) : "",
|
|
431
|
+
]);
|
|
432
|
+
}
|
|
433
|
+
console.log(table.toString());
|
|
434
|
+
console.log();
|
|
435
|
+
}
|
|
436
|
+
process.exit(0);
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
440
|
+
if (globalOpts.json) {
|
|
441
|
+
outputError("cex_orderbook", error, getExitCode(error));
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
handleError(error, globalOpts.verbose);
|
|
445
|
+
}
|
|
446
|
+
process.exit(getExitCode(error));
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
// ============================================================================
|
|
450
|
+
// 5. Orders Command (Open Orders)
|
|
451
|
+
// ============================================================================
|
|
452
|
+
cexCommand
|
|
453
|
+
.command("orders")
|
|
454
|
+
.description("Get all open (active) orders for the account ($0.001)")
|
|
455
|
+
.option("--symbol <symbol>", "Filter by trading pair (e.g., BTC/USD)")
|
|
456
|
+
.option("--limit <limit>", "Maximum orders", "100")
|
|
457
|
+
.action(async (options, command) => {
|
|
458
|
+
try {
|
|
459
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
460
|
+
const accountIndex = globalOpts.account;
|
|
461
|
+
await ensureWalletUnlocked();
|
|
462
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
463
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
464
|
+
if (globalOpts.json) {
|
|
465
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
466
|
+
process.exit(2);
|
|
467
|
+
}
|
|
468
|
+
privateKey = await promptForPrivateKey();
|
|
469
|
+
}
|
|
470
|
+
const client = await HttpcatClient.create(privateKey);
|
|
471
|
+
const symbol = options.symbol ? normalizeSymbol(options.symbol) : undefined;
|
|
472
|
+
const result = await withLoading(async () => {
|
|
473
|
+
const { data } = await client.invoke("cex/orders", {
|
|
474
|
+
symbol,
|
|
475
|
+
limit: parseInt(options.limit || "100"),
|
|
476
|
+
});
|
|
477
|
+
return data;
|
|
478
|
+
}, {
|
|
479
|
+
message: "Fetching open orders ($0.001)...",
|
|
480
|
+
json: globalOpts.json,
|
|
481
|
+
quiet: globalOpts.quiet,
|
|
482
|
+
spinner: "cat",
|
|
483
|
+
clearOnSuccess: true,
|
|
484
|
+
});
|
|
485
|
+
if (globalOpts.json) {
|
|
486
|
+
outputJson("cex_orders", {
|
|
487
|
+
command: "cex_orders",
|
|
488
|
+
data: result,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
else if (!globalOpts.quiet) {
|
|
492
|
+
console.log();
|
|
493
|
+
const orders = result.orders || [];
|
|
494
|
+
console.log(chalk.magenta.bold(`📋 Open Orders (${orders.length} orders)`));
|
|
495
|
+
console.log();
|
|
496
|
+
if (orders.length > 0) {
|
|
497
|
+
const table = createTable({
|
|
498
|
+
head: ["ID", "Pair", "Type", "Order Type", "Price", "Amount"],
|
|
499
|
+
colWidths: [10, 12, 8, 12, 15, 15],
|
|
500
|
+
});
|
|
501
|
+
for (const order of orders) {
|
|
502
|
+
const orderId = order.id || order.orderId || "";
|
|
503
|
+
const pair = order.symbol || order.pair || "";
|
|
504
|
+
const type = order.type || order.side || "";
|
|
505
|
+
const orderType = order.orderType || order.type || "Limit";
|
|
506
|
+
const price = order.price || "-";
|
|
507
|
+
const amount = order.amount || order.quantity || 0;
|
|
508
|
+
const typeColor = type.toLowerCase() === "buy" ? chalk.green : chalk.red;
|
|
509
|
+
table.push([
|
|
510
|
+
chalk.cyan(orderId.toString()),
|
|
511
|
+
pair,
|
|
512
|
+
typeColor(type),
|
|
513
|
+
orderType,
|
|
514
|
+
price === "-" ? chalk.dim("-") : formatCurrency(price),
|
|
515
|
+
formatTokenAmount(amount.toString()),
|
|
516
|
+
]);
|
|
517
|
+
}
|
|
518
|
+
console.log(table.toString());
|
|
519
|
+
console.log();
|
|
520
|
+
console.log(chalk.dim(`Total: ${orders.length} open orders`));
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
console.log(chalk.dim("No open orders."));
|
|
524
|
+
}
|
|
525
|
+
console.log();
|
|
526
|
+
}
|
|
527
|
+
process.exit(0);
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
531
|
+
if (globalOpts.json) {
|
|
532
|
+
outputError("cex_orders", error, getExitCode(error));
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
handleError(error, globalOpts.verbose);
|
|
536
|
+
}
|
|
537
|
+
process.exit(getExitCode(error));
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
// ============================================================================
|
|
541
|
+
// 6. Order History Command
|
|
542
|
+
// ============================================================================
|
|
543
|
+
cexCommand
|
|
544
|
+
.command("order-history")
|
|
545
|
+
.description("Get historical orders (completed, canceled, failed) ($0.01)")
|
|
546
|
+
.option("--symbol <symbol>", "Filter by trading pair")
|
|
547
|
+
.option("--status <status>", "Filter by status: completed, canceled, failed, or all", "all")
|
|
548
|
+
.option("--limit <limit>", "Maximum orders", "50")
|
|
549
|
+
.option("--since <timestamp>", "ISO timestamp for filtering orders since this time")
|
|
550
|
+
.action(async (options, command) => {
|
|
551
|
+
try {
|
|
552
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
553
|
+
const accountIndex = globalOpts.account;
|
|
554
|
+
await ensureWalletUnlocked();
|
|
555
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
556
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
557
|
+
if (globalOpts.json) {
|
|
558
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
559
|
+
process.exit(2);
|
|
560
|
+
}
|
|
561
|
+
privateKey = await promptForPrivateKey();
|
|
562
|
+
}
|
|
563
|
+
const client = await HttpcatClient.create(privateKey);
|
|
564
|
+
const symbol = options.symbol ? normalizeSymbol(options.symbol) : undefined;
|
|
565
|
+
const limit = Math.min(parseInt(options.limit || "50"), 100);
|
|
566
|
+
const result = await withLoading(async () => {
|
|
567
|
+
const { data } = await client.invoke("cex/order-history", {
|
|
568
|
+
symbol,
|
|
569
|
+
status: options.status === "all" ? undefined : options.status,
|
|
570
|
+
limit,
|
|
571
|
+
since: options.since,
|
|
572
|
+
});
|
|
573
|
+
return data;
|
|
574
|
+
}, {
|
|
575
|
+
message: "Fetching order history ($0.01)...",
|
|
576
|
+
json: globalOpts.json,
|
|
577
|
+
quiet: globalOpts.quiet,
|
|
578
|
+
spinner: "cat",
|
|
579
|
+
clearOnSuccess: true,
|
|
580
|
+
});
|
|
581
|
+
if (globalOpts.json) {
|
|
582
|
+
outputJson("cex_order_history", {
|
|
583
|
+
command: "cex_order_history",
|
|
584
|
+
data: result,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
else if (!globalOpts.quiet) {
|
|
588
|
+
console.log();
|
|
589
|
+
const orders = result.orders || [];
|
|
590
|
+
console.log(chalk.magenta.bold(`📜 Order History (${orders.length} orders)`));
|
|
591
|
+
console.log();
|
|
592
|
+
if (orders.length > 0) {
|
|
593
|
+
const table = createTable({
|
|
594
|
+
head: ["ID", "Pair", "Type", "Status", "Price", "Amount"],
|
|
595
|
+
colWidths: [10, 12, 8, 12, 15, 15],
|
|
596
|
+
});
|
|
597
|
+
for (const order of orders) {
|
|
598
|
+
const orderId = order.id || order.orderId || "";
|
|
599
|
+
const pair = order.symbol || order.pair || "";
|
|
600
|
+
const type = order.type || order.side || "";
|
|
601
|
+
const status = order.status || "";
|
|
602
|
+
const price = order.price || 0;
|
|
603
|
+
const amount = order.amount || order.quantity || 0;
|
|
604
|
+
const typeColor = type.toLowerCase() === "buy" ? chalk.green : chalk.red;
|
|
605
|
+
const statusColor = status.toLowerCase() === "completed"
|
|
606
|
+
? chalk.green
|
|
607
|
+
: status.toLowerCase() === "canceled"
|
|
608
|
+
? chalk.yellow
|
|
609
|
+
: chalk.red;
|
|
610
|
+
table.push([
|
|
611
|
+
chalk.cyan(orderId.toString()),
|
|
612
|
+
pair,
|
|
613
|
+
typeColor(type),
|
|
614
|
+
statusColor(status),
|
|
615
|
+
formatCurrency(price),
|
|
616
|
+
formatTokenAmount(amount.toString()),
|
|
617
|
+
]);
|
|
618
|
+
}
|
|
619
|
+
console.log(table.toString());
|
|
620
|
+
console.log();
|
|
621
|
+
const hasMore = result.hasMore || false;
|
|
622
|
+
console.log(chalk.dim(`Total: ${orders.length} orders${hasMore ? " (has more: true)" : ""}`));
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
console.log(chalk.dim("No order history found."));
|
|
626
|
+
}
|
|
627
|
+
console.log();
|
|
628
|
+
}
|
|
629
|
+
process.exit(0);
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
633
|
+
if (globalOpts.json) {
|
|
634
|
+
outputError("cex_order_history", error, getExitCode(error));
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
handleError(error, globalOpts.verbose);
|
|
638
|
+
}
|
|
639
|
+
process.exit(getExitCode(error));
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
// ============================================================================
|
|
643
|
+
// 7. Trade History Command
|
|
644
|
+
// ============================================================================
|
|
645
|
+
cexCommand
|
|
646
|
+
.command("trade-history")
|
|
647
|
+
.description("Get history of executed trades (fills) ($0.01)")
|
|
648
|
+
.option("--symbol <symbol>", "Filter by trading pair")
|
|
649
|
+
.option("--limit <limit>", "Maximum trades", "50")
|
|
650
|
+
.option("--since <timestamp>", "ISO timestamp for filtering trades since this time")
|
|
651
|
+
.option("--until <timestamp>", "ISO timestamp for filtering trades until this time")
|
|
652
|
+
.action(async (options, command) => {
|
|
653
|
+
try {
|
|
654
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
655
|
+
const accountIndex = globalOpts.account;
|
|
656
|
+
await ensureWalletUnlocked();
|
|
657
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
658
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
659
|
+
if (globalOpts.json) {
|
|
660
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
661
|
+
process.exit(2);
|
|
662
|
+
}
|
|
663
|
+
privateKey = await promptForPrivateKey();
|
|
664
|
+
}
|
|
665
|
+
const client = await HttpcatClient.create(privateKey);
|
|
666
|
+
const symbol = options.symbol ? normalizeSymbol(options.symbol) : undefined;
|
|
667
|
+
const limit = Math.min(parseInt(options.limit || "50"), 100);
|
|
668
|
+
const result = await withLoading(async () => {
|
|
669
|
+
const { data } = await client.invoke("cex/trade-history", {
|
|
670
|
+
symbol,
|
|
671
|
+
limit,
|
|
672
|
+
since: options.since,
|
|
673
|
+
until: options.until,
|
|
674
|
+
});
|
|
675
|
+
return data;
|
|
676
|
+
}, {
|
|
677
|
+
message: "Fetching trade history ($0.01)...",
|
|
678
|
+
json: globalOpts.json,
|
|
679
|
+
quiet: globalOpts.quiet,
|
|
680
|
+
spinner: "cat",
|
|
681
|
+
clearOnSuccess: true,
|
|
682
|
+
});
|
|
683
|
+
if (globalOpts.json) {
|
|
684
|
+
outputJson("cex_trade_history", {
|
|
685
|
+
command: "cex_trade_history",
|
|
686
|
+
data: result,
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
else if (!globalOpts.quiet) {
|
|
690
|
+
console.log();
|
|
691
|
+
const trades = result.trades || [];
|
|
692
|
+
console.log(chalk.magenta.bold(`💱 Trade History (${trades.length} trades)`));
|
|
693
|
+
console.log();
|
|
694
|
+
if (trades.length > 0) {
|
|
695
|
+
const table = createTable({
|
|
696
|
+
head: ["ID", "Order ID", "Pair", "Type", "Price", "Amount"],
|
|
697
|
+
colWidths: [10, 12, 12, 8, 15, 15],
|
|
698
|
+
});
|
|
699
|
+
for (const trade of trades) {
|
|
700
|
+
const tradeId = trade.id || trade.tradeId || "";
|
|
701
|
+
const orderId = trade.orderId || "";
|
|
702
|
+
const pair = trade.symbol || trade.pair || "";
|
|
703
|
+
const type = trade.type || trade.side || "";
|
|
704
|
+
const price = trade.price || 0;
|
|
705
|
+
const amount = trade.amount || trade.quantity || 0;
|
|
706
|
+
const typeColor = type.toLowerCase() === "buy" ? chalk.green : chalk.red;
|
|
707
|
+
table.push([
|
|
708
|
+
chalk.cyan(tradeId.toString()),
|
|
709
|
+
orderId.toString(),
|
|
710
|
+
pair,
|
|
711
|
+
typeColor(type),
|
|
712
|
+
formatCurrency(price),
|
|
713
|
+
formatTokenAmount(amount.toString()),
|
|
714
|
+
]);
|
|
715
|
+
}
|
|
716
|
+
console.log(table.toString());
|
|
717
|
+
console.log();
|
|
718
|
+
const hasMore = result.hasMore || false;
|
|
719
|
+
console.log(chalk.dim(`Total: ${trades.length} trades${hasMore ? " (has more: true)" : ""}`));
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
console.log(chalk.dim("No trade history found."));
|
|
723
|
+
}
|
|
724
|
+
console.log();
|
|
725
|
+
}
|
|
726
|
+
process.exit(0);
|
|
727
|
+
}
|
|
728
|
+
catch (error) {
|
|
729
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
730
|
+
if (globalOpts.json) {
|
|
731
|
+
outputError("cex_trade_history", error, getExitCode(error));
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
handleError(error, globalOpts.verbose);
|
|
735
|
+
}
|
|
736
|
+
process.exit(getExitCode(error));
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
// ============================================================================
|
|
740
|
+
// 8. Place Order Command (HIGH RISK - requires confirmation)
|
|
741
|
+
// ============================================================================
|
|
742
|
+
cexCommand
|
|
743
|
+
.command("place-order")
|
|
744
|
+
.description("Place a new buy or sell order ($0.05) - HIGH RISK: executes real trades")
|
|
745
|
+
.argument("<symbol>", "Trading pair (e.g., BTC/USD)")
|
|
746
|
+
.argument("<type>", "Order type: buy or sell")
|
|
747
|
+
.argument("<orderType>", "Order type detail: market, limit, or stop-limit")
|
|
748
|
+
.argument("<amount>", "Order amount (decimal, positive)")
|
|
749
|
+
.option("--price <price>", "Order price (required for limit/stop-limit)")
|
|
750
|
+
.option("--stop-price <price>", "Stop price (for stop-limit orders)")
|
|
751
|
+
.option("--confirm", "Skip confirmation prompt (use with caution)")
|
|
752
|
+
.action(async (symbol, type, orderType, amount, options, command) => {
|
|
753
|
+
try {
|
|
754
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
755
|
+
const accountIndex = globalOpts.account;
|
|
756
|
+
await ensureWalletUnlocked();
|
|
757
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
758
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
759
|
+
if (globalOpts.json) {
|
|
760
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
761
|
+
process.exit(2);
|
|
762
|
+
}
|
|
763
|
+
privateKey = await promptForPrivateKey();
|
|
764
|
+
}
|
|
765
|
+
// Validate inputs
|
|
766
|
+
const amountNum = parseFloat(amount);
|
|
767
|
+
if (isNaN(amountNum) || amountNum <= 0) {
|
|
768
|
+
throw new Error("Amount must be a positive number");
|
|
769
|
+
}
|
|
770
|
+
if ((orderType === "limit" || orderType === "stop-limit") &&
|
|
771
|
+
!options.price) {
|
|
772
|
+
throw new Error("Price is required for limit and stop-limit orders");
|
|
773
|
+
}
|
|
774
|
+
if (orderType === "stop-limit" && !options.stopPrice) {
|
|
775
|
+
throw new Error("Stop price is required for stop-limit orders");
|
|
776
|
+
}
|
|
777
|
+
if (type !== "buy" && type !== "sell") {
|
|
778
|
+
throw new Error("Type must be 'buy' or 'sell'");
|
|
779
|
+
}
|
|
780
|
+
if (orderType !== "market" &&
|
|
781
|
+
orderType !== "limit" &&
|
|
782
|
+
orderType !== "stop-limit") {
|
|
783
|
+
throw new Error("Order type must be 'market', 'limit', or 'stop-limit'");
|
|
784
|
+
}
|
|
785
|
+
const client = await HttpcatClient.create(privateKey);
|
|
786
|
+
const normalizedSymbol = normalizeSymbol(symbol);
|
|
787
|
+
// Show warning and confirmation (unless --confirm flag)
|
|
788
|
+
if (!options.confirm && !globalOpts.json && !globalOpts.quiet) {
|
|
789
|
+
console.log();
|
|
790
|
+
console.log(chalk.red.bold("⚠️ WARNING: This will place a REAL order on CEX.IO"));
|
|
791
|
+
console.log();
|
|
792
|
+
const priceNum = options.price ? parseFloat(options.price) : 0;
|
|
793
|
+
const estimatedCost = priceNum > 0 ? priceNum * amountNum : 0;
|
|
794
|
+
const orderSummary = {
|
|
795
|
+
Pair: chalk.cyan(normalizedSymbol),
|
|
796
|
+
Type: type === "buy" ? chalk.green(type) : chalk.red(type),
|
|
797
|
+
"Order Type": orderType,
|
|
798
|
+
Amount: formatTokenAmount(amount),
|
|
799
|
+
};
|
|
800
|
+
if (priceNum > 0) {
|
|
801
|
+
orderSummary["Price"] = formatCurrency(priceNum);
|
|
802
|
+
orderSummary["Estimated Cost"] = formatCurrency(estimatedCost);
|
|
803
|
+
}
|
|
804
|
+
if (options.stopPrice) {
|
|
805
|
+
orderSummary["Stop Price"] = formatCurrency(parseFloat(options.stopPrice));
|
|
806
|
+
}
|
|
807
|
+
printBox("Order Details", orderSummary);
|
|
808
|
+
console.log();
|
|
809
|
+
const inquirer = (await import("inquirer")).default;
|
|
810
|
+
const { confirmed } = await inquirer.prompt([
|
|
811
|
+
{
|
|
812
|
+
type: "confirm",
|
|
813
|
+
name: "confirmed",
|
|
814
|
+
message: "Are you sure you want to proceed?",
|
|
815
|
+
default: false,
|
|
816
|
+
},
|
|
817
|
+
]);
|
|
818
|
+
if (!confirmed) {
|
|
819
|
+
console.log(chalk.yellow("Order canceled by user."));
|
|
820
|
+
process.exit(0);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
const result = await withLoading(async () => {
|
|
824
|
+
const orderData = {
|
|
825
|
+
symbol: normalizedSymbol,
|
|
826
|
+
type,
|
|
827
|
+
orderType,
|
|
828
|
+
amount: amountNum,
|
|
829
|
+
};
|
|
830
|
+
if (options.price) {
|
|
831
|
+
orderData.price = parseFloat(options.price);
|
|
832
|
+
}
|
|
833
|
+
if (options.stopPrice) {
|
|
834
|
+
orderData.stopPrice = parseFloat(options.stopPrice);
|
|
835
|
+
}
|
|
836
|
+
const { data } = await client.invoke("cex/place-order", orderData);
|
|
837
|
+
return data;
|
|
838
|
+
}, {
|
|
839
|
+
message: "Placing order ($0.05)...",
|
|
840
|
+
json: globalOpts.json,
|
|
841
|
+
quiet: globalOpts.quiet,
|
|
842
|
+
spinner: "cat",
|
|
843
|
+
clearOnSuccess: true,
|
|
844
|
+
});
|
|
845
|
+
if (globalOpts.json) {
|
|
846
|
+
outputJson("cex_place_order", {
|
|
847
|
+
command: "cex_place_order",
|
|
848
|
+
data: result,
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
else if (!globalOpts.quiet) {
|
|
852
|
+
console.log();
|
|
853
|
+
console.log(chalk.green.bold("✅ Order Placed Successfully"));
|
|
854
|
+
console.log();
|
|
855
|
+
const orderId = result.id || result.orderId || "";
|
|
856
|
+
const status = result.status || "";
|
|
857
|
+
const pair = result.symbol || normalizedSymbol;
|
|
858
|
+
const orderType = result.orderType || "";
|
|
859
|
+
const amount = result.amount || amountNum;
|
|
860
|
+
const price = result.price || options.price || "-";
|
|
861
|
+
const filled = result.filled || result.filledAmount || 0;
|
|
862
|
+
const remaining = result.remaining || result.remainingAmount || amountNum - filled;
|
|
863
|
+
const boxData = {
|
|
864
|
+
"Order ID": chalk.cyan(orderId.toString()),
|
|
865
|
+
Status: status === "open" ? chalk.green(status) : status,
|
|
866
|
+
Pair: pair,
|
|
867
|
+
Type: `${result.type || type} (${orderType})`,
|
|
868
|
+
Amount: formatTokenAmount(amount.toString()),
|
|
869
|
+
Price: price === "-" ? chalk.dim("-") : formatCurrency(price),
|
|
870
|
+
Filled: formatTokenAmount(filled.toString()),
|
|
871
|
+
Remaining: formatTokenAmount(remaining.toString()),
|
|
872
|
+
};
|
|
873
|
+
printBox("Order Details", boxData);
|
|
874
|
+
console.log();
|
|
875
|
+
}
|
|
876
|
+
process.exit(0);
|
|
877
|
+
}
|
|
878
|
+
catch (error) {
|
|
879
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
880
|
+
if (globalOpts.json) {
|
|
881
|
+
outputError("cex_place_order", error, getExitCode(error));
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
handleError(error, globalOpts.verbose);
|
|
885
|
+
}
|
|
886
|
+
process.exit(getExitCode(error));
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
// ============================================================================
|
|
890
|
+
// 9. Cancel Order Command
|
|
891
|
+
// ============================================================================
|
|
892
|
+
cexCommand
|
|
893
|
+
.command("cancel-order")
|
|
894
|
+
.description("Cancel an open order by ID ($0.01)")
|
|
895
|
+
.argument("<orderId>", "Order ID to cancel")
|
|
896
|
+
.action(async (orderId, options, command) => {
|
|
897
|
+
try {
|
|
898
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
899
|
+
const accountIndex = globalOpts.account;
|
|
900
|
+
await ensureWalletUnlocked();
|
|
901
|
+
let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
|
|
902
|
+
if (!isConfigured(globalOpts.privateKey)) {
|
|
903
|
+
if (globalOpts.json) {
|
|
904
|
+
console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
|
|
905
|
+
process.exit(2);
|
|
906
|
+
}
|
|
907
|
+
privateKey = await promptForPrivateKey();
|
|
908
|
+
}
|
|
909
|
+
const client = await HttpcatClient.create(privateKey);
|
|
910
|
+
const result = await withLoading(async () => {
|
|
911
|
+
const { data } = await client.invoke("cex/cancel-order", {
|
|
912
|
+
orderId: orderId.toString(),
|
|
913
|
+
});
|
|
914
|
+
return data;
|
|
915
|
+
}, {
|
|
916
|
+
message: "Canceling order ($0.01)...",
|
|
917
|
+
json: globalOpts.json,
|
|
918
|
+
quiet: globalOpts.quiet,
|
|
919
|
+
spinner: "cat",
|
|
920
|
+
clearOnSuccess: true,
|
|
921
|
+
});
|
|
922
|
+
if (globalOpts.json) {
|
|
923
|
+
outputJson("cex_cancel_order", {
|
|
924
|
+
command: "cex_cancel_order",
|
|
925
|
+
data: result,
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
else if (!globalOpts.quiet) {
|
|
929
|
+
console.log();
|
|
930
|
+
console.log(chalk.green.bold("✅ Order Canceled"));
|
|
931
|
+
console.log();
|
|
932
|
+
const canceledOrderId = result.id || result.orderId || orderId;
|
|
933
|
+
const status = result.status || "canceled";
|
|
934
|
+
const message = result.message || "Order canceled successfully";
|
|
935
|
+
const boxData = {
|
|
936
|
+
"Order ID": chalk.cyan(canceledOrderId.toString()),
|
|
937
|
+
Status: status === "canceled" ? chalk.yellow(status) : status,
|
|
938
|
+
Message: message,
|
|
939
|
+
};
|
|
940
|
+
printBox("Cancel Result", boxData);
|
|
941
|
+
console.log();
|
|
942
|
+
}
|
|
943
|
+
process.exit(0);
|
|
944
|
+
}
|
|
945
|
+
catch (error) {
|
|
946
|
+
const globalOpts = command.parent?.parent?.opts() || {};
|
|
947
|
+
if (globalOpts.json) {
|
|
948
|
+
outputError("cex_cancel_order", error, getExitCode(error));
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
handleError(error, globalOpts.verbose);
|
|
952
|
+
}
|
|
953
|
+
process.exit(getExitCode(error));
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
return cexCommand;
|
|
957
|
+
}
|
|
958
|
+
//# sourceMappingURL=cex.js.map
|