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.
Files changed (322) hide show
  1. package/.github/workflows/ci.yml +3 -0
  2. package/.github/workflows/rc-publish.yml +6 -0
  3. package/.github/workflows/release.yml +102 -0
  4. package/.github/workflows/sync-version.yml +31 -2
  5. package/README.md +1408 -109
  6. package/additions.txt +3 -0
  7. package/bun.lock +260 -25
  8. package/dist/agent/autonomous-trader.d.ts.map +1 -0
  9. package/dist/agent/autonomous-trader.js +362 -0
  10. package/dist/agent/autonomous-trader.js.map +1 -0
  11. package/dist/agent/ax-agent.d.ts.map +1 -1
  12. package/dist/agent/ax-agent.js +356 -18
  13. package/dist/agent/ax-agent.js.map +1 -1
  14. package/dist/agent/event-client.d.ts.map +1 -0
  15. package/dist/agent/event-client.js +82 -0
  16. package/dist/agent/event-client.js.map +1 -0
  17. package/dist/agent/log-stream.d.ts.map +1 -0
  18. package/dist/agent/log-stream.js +95 -0
  19. package/dist/agent/log-stream.js.map +1 -0
  20. package/dist/agent/memory/conversation-session.d.ts.map +1 -0
  21. package/dist/agent/memory/conversation-session.js +232 -0
  22. package/dist/agent/memory/conversation-session.js.map +1 -0
  23. package/dist/agent/memory/conversation-store.d.ts.map +1 -0
  24. package/dist/agent/memory/conversation-store.js +214 -0
  25. package/dist/agent/memory/conversation-store.js.map +1 -0
  26. package/dist/agent/memory/database-schema.d.ts.map +1 -0
  27. package/dist/agent/memory/database-schema.js +355 -0
  28. package/dist/agent/memory/database-schema.js.map +1 -0
  29. package/dist/agent/memory/decision-tracker.d.ts.map +1 -0
  30. package/dist/agent/memory/decision-tracker.js +274 -0
  31. package/dist/agent/memory/decision-tracker.js.map +1 -0
  32. package/dist/agent/memory/memory-manager.d.ts.map +1 -0
  33. package/dist/agent/memory/memory-manager.js +187 -0
  34. package/dist/agent/memory/memory-manager.js.map +1 -0
  35. package/dist/agent/memory/types.d.ts.map +1 -0
  36. package/dist/agent/memory/types.js +5 -0
  37. package/dist/agent/memory/types.js.map +1 -0
  38. package/dist/agent/message-formatter.d.ts.map +1 -0
  39. package/dist/agent/message-formatter.js +76 -0
  40. package/dist/agent/message-formatter.js.map +1 -0
  41. package/dist/agent/position-db.d.ts.map +1 -0
  42. package/dist/agent/position-db.js +154 -0
  43. package/dist/agent/position-db.js.map +1 -0
  44. package/dist/agent/simple-chat-ui-static.d.ts.map +1 -0
  45. package/dist/agent/simple-chat-ui-static.js +129 -0
  46. package/dist/agent/simple-chat-ui-static.js.map +1 -0
  47. package/dist/agent/simple-chat-ui.d.ts.map +1 -0
  48. package/dist/agent/simple-chat-ui.js +90 -0
  49. package/dist/agent/simple-chat-ui.js.map +1 -0
  50. package/dist/agent/tools.d.ts.map +1 -1
  51. package/dist/agent/tools.js +297 -4
  52. package/dist/agent/tools.js.map +1 -1
  53. package/dist/agent/ui.d.ts.map +1 -0
  54. package/dist/agent/ui.js +84 -0
  55. package/dist/agent/ui.js.map +1 -0
  56. package/dist/agent/unified-runtime.d.ts.map +1 -0
  57. package/dist/agent/unified-runtime.js +397 -0
  58. package/dist/agent/unified-runtime.js.map +1 -0
  59. package/dist/client.d.ts.map +1 -1
  60. package/dist/client.js +272 -21
  61. package/dist/client.js.map +1 -1
  62. package/dist/commands/account.d.ts.map +1 -1
  63. package/dist/commands/account.js +187 -33
  64. package/dist/commands/account.js.map +1 -1
  65. package/dist/commands/agent.d.ts.map +1 -0
  66. package/dist/commands/agent.js +125 -0
  67. package/dist/commands/agent.js.map +1 -0
  68. package/dist/commands/approve.d.ts.map +1 -0
  69. package/dist/commands/approve.js +505 -0
  70. package/dist/commands/approve.js.map +1 -0
  71. package/dist/commands/automation.d.ts.map +1 -0
  72. package/dist/commands/automation.js +346 -0
  73. package/dist/commands/automation.js.map +1 -0
  74. package/dist/commands/balances.d.ts.map +1 -1
  75. package/dist/commands/balances.js +226 -73
  76. package/dist/commands/balances.js.map +1 -1
  77. package/dist/commands/buy.d.ts.map +1 -1
  78. package/dist/commands/buy.js +149 -146
  79. package/dist/commands/buy.js.map +1 -1
  80. package/dist/commands/call.d.ts.map +1 -0
  81. package/dist/commands/call.js +51 -0
  82. package/dist/commands/call.js.map +1 -0
  83. package/dist/commands/cex.d.ts.map +1 -0
  84. package/dist/commands/cex.js +958 -0
  85. package/dist/commands/cex.js.map +1 -0
  86. package/dist/commands/chat.d.ts.map +1 -1
  87. package/dist/commands/chat.js +169 -411
  88. package/dist/commands/chat.js.map +1 -1
  89. package/dist/commands/claim.d.ts.map +1 -1
  90. package/dist/commands/claim.js +313 -29
  91. package/dist/commands/claim.js.map +1 -1
  92. package/dist/commands/create.d.ts.map +1 -1
  93. package/dist/commands/create.js +151 -43
  94. package/dist/commands/create.js.map +1 -1
  95. package/dist/commands/gasless-swap.d.ts.map +1 -0
  96. package/dist/commands/gasless-swap.js +232 -0
  97. package/dist/commands/gasless-swap.js.map +1 -0
  98. package/dist/commands/health.d.ts.map +1 -1
  99. package/dist/commands/health.js +63 -7
  100. package/dist/commands/health.js.map +1 -1
  101. package/dist/commands/info.d.ts.map +1 -1
  102. package/dist/commands/info.js +131 -47
  103. package/dist/commands/info.js.map +1 -1
  104. package/dist/commands/launchpad.d.ts.map +1 -0
  105. package/dist/commands/launchpad.js +708 -0
  106. package/dist/commands/launchpad.js.map +1 -0
  107. package/dist/commands/list.d.ts.map +1 -1
  108. package/dist/commands/list.js +57 -23
  109. package/dist/commands/list.js.map +1 -1
  110. package/dist/commands/market.d.ts.map +1 -0
  111. package/dist/commands/market.js +960 -0
  112. package/dist/commands/market.js.map +1 -0
  113. package/dist/commands/mcp-install.d.ts.map +1 -0
  114. package/dist/commands/mcp-install.js +387 -0
  115. package/dist/commands/mcp-install.js.map +1 -0
  116. package/dist/commands/opps.d.ts.map +1 -0
  117. package/dist/commands/opps.js +409 -0
  118. package/dist/commands/opps.js.map +1 -0
  119. package/dist/commands/perps.d.ts.map +1 -0
  120. package/dist/commands/perps.js +248 -0
  121. package/dist/commands/perps.js.map +1 -0
  122. package/dist/commands/portfolio.d.ts.map +1 -0
  123. package/dist/commands/portfolio.js +679 -0
  124. package/dist/commands/portfolio.js.map +1 -0
  125. package/dist/commands/positions.d.ts.map +1 -1
  126. package/dist/commands/positions.js +76 -47
  127. package/dist/commands/positions.js.map +1 -1
  128. package/dist/commands/predict.d.ts.map +1 -0
  129. package/dist/commands/predict.js +280 -0
  130. package/dist/commands/predict.js.map +1 -0
  131. package/dist/commands/predictions.d.ts.map +1 -0
  132. package/dist/commands/predictions.js +486 -0
  133. package/dist/commands/predictions.js.map +1 -0
  134. package/dist/commands/risk.d.ts.map +1 -0
  135. package/dist/commands/risk.js +225 -0
  136. package/dist/commands/risk.js.map +1 -0
  137. package/dist/commands/security.d.ts.map +1 -0
  138. package/dist/commands/security.js +244 -0
  139. package/dist/commands/security.js.map +1 -0
  140. package/dist/commands/sell.d.ts.map +1 -1
  141. package/dist/commands/sell.js +67 -34
  142. package/dist/commands/sell.js.map +1 -1
  143. package/dist/commands/send.d.ts.map +1 -0
  144. package/dist/commands/send.js +733 -0
  145. package/dist/commands/send.js.map +1 -0
  146. package/dist/commands/sign.d.ts.map +1 -0
  147. package/dist/commands/sign.js +1048 -0
  148. package/dist/commands/sign.js.map +1 -0
  149. package/dist/commands/swap.d.ts.map +1 -0
  150. package/dist/commands/swap.js +744 -0
  151. package/dist/commands/swap.js.map +1 -0
  152. package/dist/commands/system.d.ts.map +1 -0
  153. package/dist/commands/system.js +417 -0
  154. package/dist/commands/system.js.map +1 -0
  155. package/dist/commands/tools/index.d.ts.map +1 -0
  156. package/dist/commands/tools/index.js +2040 -0
  157. package/dist/commands/tools/index.js.map +1 -0
  158. package/dist/commands/trade.d.ts.map +1 -0
  159. package/dist/commands/trade.js +237 -0
  160. package/dist/commands/trade.js.map +1 -0
  161. package/dist/commands/transactions.d.ts.map +1 -1
  162. package/dist/commands/transactions.js +29 -17
  163. package/dist/commands/transactions.js.map +1 -1
  164. package/dist/commands/update.d.ts.map +1 -0
  165. package/dist/commands/update.js +429 -0
  166. package/dist/commands/update.js.map +1 -0
  167. package/dist/config.d.ts.map +1 -1
  168. package/dist/config.js +351 -40
  169. package/dist/config.js.map +1 -1
  170. package/dist/index.js +4524 -924
  171. package/dist/index.js.map +1 -1
  172. package/dist/interactive/art.d.ts.map +1 -1
  173. package/dist/interactive/art.js +33 -1
  174. package/dist/interactive/art.js.map +1 -1
  175. package/dist/interactive/shell.d.ts.map +1 -1
  176. package/dist/interactive/shell.js +467 -2652
  177. package/dist/interactive/shell.js.map +1 -1
  178. package/dist/mcp/context.d.ts.map +1 -0
  179. package/dist/mcp/context.js +211 -0
  180. package/dist/mcp/context.js.map +1 -0
  181. package/dist/mcp/onboarding.d.ts.map +1 -0
  182. package/dist/mcp/onboarding.js +266 -0
  183. package/dist/mcp/onboarding.js.map +1 -0
  184. package/dist/mcp/resources.d.ts.map +1 -0
  185. package/dist/mcp/resources.js +222 -0
  186. package/dist/mcp/resources.js.map +1 -0
  187. package/dist/mcp/server.d.ts.map +1 -1
  188. package/dist/mcp/server.js +51 -1
  189. package/dist/mcp/server.js.map +1 -1
  190. package/dist/mcp/tools.d.ts.map +1 -1
  191. package/dist/mcp/tools.js +4119 -169
  192. package/dist/mcp/tools.js.map +1 -1
  193. package/dist/mcp/types.d.ts.map +1 -1
  194. package/dist/types/agent-info.d.ts.map +1 -0
  195. package/dist/types/agent-info.js +11 -0
  196. package/dist/types/agent-info.js.map +1 -0
  197. package/dist/ui/components/ScrollableList.d.ts.map +1 -0
  198. package/dist/ui/components/ScrollableList.js +72 -0
  199. package/dist/ui/components/ScrollableList.js.map +1 -0
  200. package/dist/ui/components/ThemeProvider.d.ts.map +1 -0
  201. package/dist/ui/components/ThemeProvider.js +87 -0
  202. package/dist/ui/components/ThemeProvider.js.map +1 -0
  203. package/dist/ui/components/ThemedBox.d.ts.map +1 -0
  204. package/dist/ui/components/ThemedBox.js +24 -0
  205. package/dist/ui/components/ThemedBox.js.map +1 -0
  206. package/dist/ui/components/agent/ChatHeader.d.ts.map +1 -0
  207. package/dist/ui/components/agent/ChatHeader.js +39 -0
  208. package/dist/ui/components/agent/ChatHeader.js.map +1 -0
  209. package/dist/ui/components/agent/Header.d.ts.map +1 -0
  210. package/dist/ui/components/agent/Header.js +14 -0
  211. package/dist/ui/components/agent/Header.js.map +1 -0
  212. package/dist/ui/components/agent/Input.d.ts.map +1 -0
  213. package/dist/ui/components/agent/Input.js +23 -0
  214. package/dist/ui/components/agent/Input.js.map +1 -0
  215. package/dist/ui/components/agent/Output.d.ts.map +1 -0
  216. package/dist/ui/components/agent/Output.js +23 -0
  217. package/dist/ui/components/agent/Output.js.map +1 -0
  218. package/dist/ui/components/chat/TokenChatUI.d.ts.map +1 -0
  219. package/dist/ui/components/chat/TokenChatUI.js +133 -0
  220. package/dist/ui/components/chat/TokenChatUI.js.map +1 -0
  221. package/dist/ui/components/shell/ShellHeader.d.ts.map +1 -0
  222. package/dist/ui/components/shell/ShellHeader.js +31 -0
  223. package/dist/ui/components/shell/ShellHeader.js.map +1 -0
  224. package/dist/ui/components/shell/ShellInput.d.ts.map +1 -0
  225. package/dist/ui/components/shell/ShellInput.js +151 -0
  226. package/dist/ui/components/shell/ShellInput.js.map +1 -0
  227. package/dist/ui/components/shell/ShellOutput.d.ts.map +1 -0
  228. package/dist/ui/components/shell/ShellOutput.js +8 -0
  229. package/dist/ui/components/shell/ShellOutput.js.map +1 -0
  230. package/dist/ui/hooks/useChatWebSocket.d.ts.map +1 -0
  231. package/dist/ui/hooks/useChatWebSocket.js +76 -0
  232. package/dist/ui/hooks/useChatWebSocket.js.map +1 -0
  233. package/dist/ui/hooks/useCommandHistory.d.ts.map +1 -0
  234. package/dist/ui/hooks/useCommandHistory.js +70 -0
  235. package/dist/ui/hooks/useCommandHistory.js.map +1 -0
  236. package/dist/ui/hooks/useDebounce.d.ts.map +1 -0
  237. package/dist/ui/hooks/useDebounce.js +17 -0
  238. package/dist/ui/hooks/useDebounce.js.map +1 -0
  239. package/dist/ui/hooks/useLogStream.d.ts.map +1 -0
  240. package/dist/ui/hooks/useLogStream.js +20 -0
  241. package/dist/ui/hooks/useLogStream.js.map +1 -0
  242. package/dist/ui/hooks/useVirtualScroll.d.ts.map +1 -0
  243. package/dist/ui/hooks/useVirtualScroll.js +70 -0
  244. package/dist/ui/hooks/useVirtualScroll.js.map +1 -0
  245. package/dist/utils/admin.d.ts.map +1 -0
  246. package/dist/utils/admin.js +144 -0
  247. package/dist/utils/admin.js.map +1 -0
  248. package/dist/utils/autoSetup.d.ts.map +1 -0
  249. package/dist/utils/autoSetup.js +252 -0
  250. package/dist/utils/autoSetup.js.map +1 -0
  251. package/dist/utils/build-constants.d.ts.map +1 -0
  252. package/dist/utils/build-constants.js +10 -0
  253. package/dist/utils/build-constants.js.map +1 -0
  254. package/dist/utils/constants.d.ts.map +1 -1
  255. package/dist/utils/errors.d.ts.map +1 -1
  256. package/dist/utils/errors.js +10 -1
  257. package/dist/utils/errors.js.map +1 -1
  258. package/dist/utils/formatting.d.ts.map +1 -1
  259. package/dist/utils/formatting.js +46 -9
  260. package/dist/utils/formatting.js.map +1 -1
  261. package/dist/utils/llm-cli-config.d.ts.map +1 -0
  262. package/dist/utils/llm-cli-config.js +963 -0
  263. package/dist/utils/llm-cli-config.js.map +1 -0
  264. package/dist/utils/llm-cli-detector.d.ts.map +1 -0
  265. package/dist/utils/llm-cli-detector.js +202 -0
  266. package/dist/utils/llm-cli-detector.js.map +1 -0
  267. package/dist/utils/loading.d.ts.map +1 -1
  268. package/dist/utils/loading.js +25 -3
  269. package/dist/utils/loading.js.map +1 -1
  270. package/dist/utils/maintenance.d.ts.map +1 -0
  271. package/dist/utils/maintenance.js +17 -0
  272. package/dist/utils/maintenance.js.map +1 -0
  273. package/dist/utils/mcp-config.d.ts.map +1 -0
  274. package/dist/utils/mcp-config.js +77 -0
  275. package/dist/utils/mcp-config.js.map +1 -0
  276. package/dist/utils/privateKeyPrompt.d.ts.map +1 -1
  277. package/dist/utils/privateKeyPrompt.js +308 -129
  278. package/dist/utils/privateKeyPrompt.js.map +1 -1
  279. package/dist/utils/process-cleanup.d.ts.map +1 -0
  280. package/dist/utils/process-cleanup.js +136 -0
  281. package/dist/utils/process-cleanup.js.map +1 -0
  282. package/dist/utils/retry.d.ts.map +1 -0
  283. package/dist/utils/retry.js +56 -0
  284. package/dist/utils/retry.js.map +1 -0
  285. package/dist/utils/rpc-helpers.d.ts.map +1 -0
  286. package/dist/utils/rpc-helpers.js +70 -0
  287. package/dist/utils/rpc-helpers.js.map +1 -0
  288. package/dist/utils/rpc-transport.d.ts.map +1 -0
  289. package/dist/utils/rpc-transport.js +87 -0
  290. package/dist/utils/rpc-transport.js.map +1 -0
  291. package/dist/utils/shell-setup.d.ts.map +1 -0
  292. package/dist/utils/shell-setup.js +531 -0
  293. package/dist/utils/shell-setup.js.map +1 -0
  294. package/dist/utils/status.d.ts.map +1 -1
  295. package/dist/utils/status.js +34 -5
  296. package/dist/utils/status.js.map +1 -1
  297. package/dist/utils/token-resolver.d.ts.map +1 -1
  298. package/dist/utils/token-resolver.js +51 -8
  299. package/dist/utils/token-resolver.js.map +1 -1
  300. package/dist/utils/x402-caller.d.ts.map +1 -0
  301. package/dist/utils/x402-caller.js +17 -0
  302. package/dist/utils/x402-caller.js.map +1 -0
  303. package/docs/README.md +28 -0
  304. package/docs/agent/README.md +18 -0
  305. package/docs/api/README.md +41 -0
  306. package/docs/cli/README.md +42 -0
  307. package/docs/guides/README.md +26 -0
  308. package/docs/implementation/README.md +18 -0
  309. package/docs/planning/README.md +19 -0
  310. package/docs/testing/README.md +15 -0
  311. package/docs/ux/README.md +16 -0
  312. package/issues.txt +2 -0
  313. package/package.json +24 -9
  314. package/scripts/cat-spin.sh +417 -0
  315. package/scripts/deprecate-rc-versions.js +58 -0
  316. package/scripts/inject-build-constants.js +43 -0
  317. package/scripts/monitor-foobar.js +117 -0
  318. package/swap.logs +61 -0
  319. package/swapping.txt +108 -0
  320. package/test.txt +12 -0
  321. package/tests/fixtures/test-data.json +16 -0
  322. package/Screenshot 2025-12-21 at 8.56.02/342/200/257PM.png +0 -0
@@ -0,0 +1,2040 @@
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, formatAddress, formatTokenAmount } 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
+ import { callX402Endpoint, displayCallResult } from "../call.js";
11
+ // ============================================================================
12
+ // Shared Utilities
13
+ // ============================================================================
14
+ function getPrivateKey(cliPrivateKey, accountIndex) {
15
+ if (cliPrivateKey)
16
+ return cliPrivateKey;
17
+ const envKey = process.env.HTTPCAT_PRIVATE_KEY;
18
+ if (envKey)
19
+ return envKey;
20
+ try {
21
+ const index = accountIndex !== undefined
22
+ ? accountIndex
23
+ : config.getActiveAccountIndex();
24
+ return config.getAccountPrivateKey(index);
25
+ }
26
+ catch (error) {
27
+ return config.get("privateKey");
28
+ }
29
+ }
30
+ function isConfigured(cliPrivateKey) {
31
+ if (cliPrivateKey)
32
+ return true;
33
+ return config.isConfigured();
34
+ }
35
+ async function ensureWalletUnlocked() {
36
+ const password = config.getPassword();
37
+ if (!password) {
38
+ config.ensureSessionValid();
39
+ return;
40
+ }
41
+ if (!config.isSessionValid()) {
42
+ const inquirer = (await import("inquirer")).default;
43
+ const answers = await inquirer.prompt([
44
+ {
45
+ type: "password",
46
+ name: "password",
47
+ message: "Enter password to unlock wallet:",
48
+ mask: "•",
49
+ },
50
+ ]);
51
+ await config.unlockSession(answers.password);
52
+ }
53
+ }
54
+ // ============================================================================
55
+ // Tools Command Group
56
+ // ============================================================================
57
+ export function createToolsCommand() {
58
+ const toolsCommand = new Command("tools")
59
+ .description("EVM development & interaction tools")
60
+ .addHelpText("after", `
61
+ Examples:
62
+ httpcat tools address validate 0x...
63
+ httpcat tools abi encode <abi> transfer <args>
64
+ httpcat tools tx build <tx-json>
65
+ httpcat tools signature eip712 <domain> <types> <message>
66
+ `);
67
+ // ============================================================================
68
+ // Address Utilities
69
+ // ============================================================================
70
+ const addressCommand = toolsCommand
71
+ .command("address")
72
+ .description("Address utilities");
73
+ addressCommand
74
+ .command("validate")
75
+ .description("Validate Ethereum address format ($0.001)")
76
+ .argument("<address>", "Address to validate")
77
+ .action(async (address, options, command) => {
78
+ try {
79
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
80
+ const accountIndex = globalOpts.account;
81
+ await ensureWalletUnlocked();
82
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
83
+ if (!isConfigured(globalOpts.privateKey)) {
84
+ if (globalOpts.json) {
85
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
86
+ process.exit(2);
87
+ }
88
+ privateKey = await promptForPrivateKey();
89
+ }
90
+ const client = await HttpcatClient.create(privateKey);
91
+ const result = await withLoading(async () => {
92
+ const { data } = await client.invoke("tools/address/validate", {
93
+ address,
94
+ });
95
+ return data;
96
+ }, {
97
+ message: "Validating address...",
98
+ json: globalOpts.json,
99
+ quiet: globalOpts.quiet,
100
+ spinner: "cat",
101
+ clearOnSuccess: true,
102
+ });
103
+ if (globalOpts.json) {
104
+ outputJson("address_validate", result);
105
+ }
106
+ else if (!globalOpts.quiet) {
107
+ console.log();
108
+ console.log(chalk.magenta.bold("✅ Address Validation"));
109
+ console.log();
110
+ const boxData = {
111
+ Address: chalk.cyan(address),
112
+ Valid: result.valid
113
+ ? chalk.green.bold("Yes")
114
+ : chalk.red.bold("No"),
115
+ };
116
+ if (result.checksummed) {
117
+ boxData["Checksummed"] = chalk.yellow(result.checksummed);
118
+ }
119
+ printBox("Validation Result", boxData);
120
+ console.log();
121
+ }
122
+ process.exit(0);
123
+ }
124
+ catch (error) {
125
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
126
+ if (globalOpts.json) {
127
+ outputError("address_validate", error, getExitCode(error));
128
+ }
129
+ else {
130
+ handleError(error, globalOpts.verbose);
131
+ }
132
+ process.exit(getExitCode(error));
133
+ }
134
+ });
135
+ addressCommand
136
+ .command("checksum")
137
+ .description("Convert address to EIP-55 checksum format ($0.001)")
138
+ .argument("<address>", "Address to checksum")
139
+ .action(async (address, options, command) => {
140
+ try {
141
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
142
+ const accountIndex = globalOpts.account;
143
+ await ensureWalletUnlocked();
144
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
145
+ if (!isConfigured(globalOpts.privateKey)) {
146
+ if (globalOpts.json) {
147
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
148
+ process.exit(2);
149
+ }
150
+ privateKey = await promptForPrivateKey();
151
+ }
152
+ const client = await HttpcatClient.create(privateKey);
153
+ const result = await withLoading(async () => {
154
+ const { data } = await client.invoke("tools/address/checksum", {
155
+ address,
156
+ });
157
+ return data;
158
+ }, {
159
+ message: "Converting to checksum...",
160
+ json: globalOpts.json,
161
+ quiet: globalOpts.quiet,
162
+ spinner: "cat",
163
+ clearOnSuccess: true,
164
+ });
165
+ if (globalOpts.json) {
166
+ outputJson("address_checksum", result);
167
+ }
168
+ else if (!globalOpts.quiet) {
169
+ console.log();
170
+ console.log(chalk.magenta.bold("🔤 Address Checksum"));
171
+ console.log();
172
+ printBox("Checksum Result", {
173
+ Original: chalk.dim(result.original),
174
+ Checksummed: chalk.green.bold(result.address),
175
+ });
176
+ console.log();
177
+ }
178
+ process.exit(0);
179
+ }
180
+ catch (error) {
181
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
182
+ if (globalOpts.json) {
183
+ outputError("address_checksum", error, getExitCode(error));
184
+ }
185
+ else {
186
+ handleError(error, globalOpts.verbose);
187
+ }
188
+ process.exit(getExitCode(error));
189
+ }
190
+ });
191
+ addressCommand
192
+ .command("derive")
193
+ .description("Derive address from private key ($0.001)")
194
+ .argument("<privateKey>", "Private key (0x-prefixed hex)")
195
+ .action(async (privateKey, options, command) => {
196
+ try {
197
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
198
+ const accountIndex = globalOpts.account;
199
+ await ensureWalletUnlocked();
200
+ let pk = getPrivateKey(globalOpts.privateKey, accountIndex) || privateKey;
201
+ if (!isConfigured(globalOpts.privateKey) && !privateKey) {
202
+ if (globalOpts.json) {
203
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
204
+ process.exit(2);
205
+ }
206
+ pk = await promptForPrivateKey();
207
+ }
208
+ const client = await HttpcatClient.create(pk);
209
+ const result = await withLoading(async () => {
210
+ const { data } = await client.invoke("tools/address/derive", {
211
+ privateKey: pk,
212
+ });
213
+ return data;
214
+ }, {
215
+ message: "Deriving address...",
216
+ json: globalOpts.json,
217
+ quiet: globalOpts.quiet,
218
+ spinner: "cat",
219
+ clearOnSuccess: true,
220
+ });
221
+ if (globalOpts.json) {
222
+ outputJson("address_derive", result);
223
+ }
224
+ else if (!globalOpts.quiet) {
225
+ console.log();
226
+ console.log(chalk.magenta.bold("🔑 Address Derivation"));
227
+ console.log();
228
+ printBox("Derived Address", {
229
+ Address: chalk.green.bold(result.address),
230
+ });
231
+ console.log();
232
+ }
233
+ process.exit(0);
234
+ }
235
+ catch (error) {
236
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
237
+ if (globalOpts.json) {
238
+ outputError("address_derive", error, getExitCode(error));
239
+ }
240
+ else {
241
+ handleError(error, globalOpts.verbose);
242
+ }
243
+ process.exit(getExitCode(error));
244
+ }
245
+ });
246
+ addressCommand
247
+ .command("balance")
248
+ .description("Get address balance (ETH + tokens) ($0.001)")
249
+ .argument("<address>", "Address (0x format)")
250
+ .option("--tokens <tokens>", "Comma-separated token addresses")
251
+ .action(async (address, options, command) => {
252
+ try {
253
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
254
+ const accountIndex = globalOpts.account;
255
+ await ensureWalletUnlocked();
256
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
257
+ if (!isConfigured(globalOpts.privateKey)) {
258
+ if (globalOpts.json) {
259
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
260
+ process.exit(2);
261
+ }
262
+ privateKey = await promptForPrivateKey();
263
+ }
264
+ const client = await HttpcatClient.create(privateKey);
265
+ const input = { address };
266
+ if (options.tokens) {
267
+ input.tokens = options.tokens.split(",").map((t) => t.trim());
268
+ }
269
+ const result = await withLoading(async () => {
270
+ const { data } = await client.invoke("tools/address/balance", input);
271
+ return data;
272
+ }, {
273
+ message: "Fetching balance...",
274
+ json: globalOpts.json,
275
+ quiet: globalOpts.quiet,
276
+ spinner: "cat",
277
+ clearOnSuccess: true,
278
+ });
279
+ if (globalOpts.json) {
280
+ outputJson("address_balance", result);
281
+ }
282
+ else if (!globalOpts.quiet) {
283
+ console.log();
284
+ console.log(chalk.magenta.bold("💰 Address Balance"));
285
+ console.log();
286
+ const boxData = {
287
+ Address: chalk.cyan(address),
288
+ "ETH Balance": chalk.green.bold(result.ethBalanceFormatted || formatTokenAmount(result.ethBalance)),
289
+ };
290
+ printBox("Balance", boxData);
291
+ if (result.tokens && result.tokens.length > 0) {
292
+ console.log();
293
+ console.log(chalk.cyan.bold("Token Balances:"));
294
+ for (const token of result.tokens) {
295
+ console.log(` ${formatAddress(token.address, 10)}: ${chalk.green(token.balanceFormatted || formatTokenAmount(token.balance))}`);
296
+ }
297
+ }
298
+ console.log();
299
+ }
300
+ process.exit(0);
301
+ }
302
+ catch (error) {
303
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
304
+ if (globalOpts.json) {
305
+ outputError("address_balance", error, getExitCode(error));
306
+ }
307
+ else {
308
+ handleError(error, globalOpts.verbose);
309
+ }
310
+ process.exit(getExitCode(error));
311
+ }
312
+ });
313
+ // ============================================================================
314
+ // ABI Utilities
315
+ // ============================================================================
316
+ const abiCommand = toolsCommand
317
+ .command("abi")
318
+ .description("ABI encoding/decoding utilities");
319
+ abiCommand
320
+ .command("encode")
321
+ .description("Encode function call from ABI and arguments ($0.001)")
322
+ .requiredOption("--abi <abi>", "ABI JSON (as string or file path)")
323
+ .requiredOption("--function <name>", "Function name")
324
+ .requiredOption("--args <args>", "Arguments as JSON array")
325
+ .action(async (options, command) => {
326
+ try {
327
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
328
+ const accountIndex = globalOpts.account;
329
+ await ensureWalletUnlocked();
330
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
331
+ if (!isConfigured(globalOpts.privateKey)) {
332
+ if (globalOpts.json) {
333
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
334
+ process.exit(2);
335
+ }
336
+ privateKey = await promptForPrivateKey();
337
+ }
338
+ const client = await HttpcatClient.create(privateKey);
339
+ // Parse ABI (could be file path or JSON string)
340
+ let abi;
341
+ try {
342
+ abi = JSON.parse(options.abi);
343
+ }
344
+ catch {
345
+ // Try as file path
346
+ const fs = await import("fs");
347
+ if (fs.existsSync(options.abi)) {
348
+ abi = JSON.parse(fs.readFileSync(options.abi, "utf-8"));
349
+ }
350
+ else {
351
+ throw new Error("Invalid ABI: must be JSON string or file path");
352
+ }
353
+ }
354
+ // Parse args
355
+ let args;
356
+ try {
357
+ args = JSON.parse(options.args);
358
+ }
359
+ catch {
360
+ throw new Error("Invalid args: must be JSON array");
361
+ }
362
+ const result = await withLoading(async () => {
363
+ const { data } = await client.invoke("tools/abi/encode", {
364
+ abi,
365
+ functionName: options.function,
366
+ args,
367
+ });
368
+ return data;
369
+ }, {
370
+ message: "Encoding function call...",
371
+ json: globalOpts.json,
372
+ quiet: globalOpts.quiet,
373
+ spinner: "cat",
374
+ clearOnSuccess: true,
375
+ });
376
+ if (globalOpts.json) {
377
+ outputJson("abi_encode", result);
378
+ }
379
+ else if (!globalOpts.quiet) {
380
+ console.log();
381
+ console.log(chalk.magenta.bold("📝 ABI Encode"));
382
+ console.log();
383
+ printBox("Encoded Data", {
384
+ Data: chalk.green.bold(result.data),
385
+ });
386
+ console.log();
387
+ }
388
+ process.exit(0);
389
+ }
390
+ catch (error) {
391
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
392
+ if (globalOpts.json) {
393
+ outputError("abi_encode", error, getExitCode(error));
394
+ }
395
+ else {
396
+ handleError(error, globalOpts.verbose);
397
+ }
398
+ process.exit(getExitCode(error));
399
+ }
400
+ });
401
+ abiCommand
402
+ .command("decode")
403
+ .description("Decode function result or event log ($0.001)")
404
+ .requiredOption("--abi <abi>", "ABI JSON (as string or file path)")
405
+ .requiredOption("--data <data>", "Data to decode (0x-prefixed hex)")
406
+ .requiredOption("--type <type>", "Type: function or event")
407
+ .action(async (options, command) => {
408
+ try {
409
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
410
+ const accountIndex = globalOpts.account;
411
+ await ensureWalletUnlocked();
412
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
413
+ if (!isConfigured(globalOpts.privateKey)) {
414
+ if (globalOpts.json) {
415
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
416
+ process.exit(2);
417
+ }
418
+ privateKey = await promptForPrivateKey();
419
+ }
420
+ const client = await HttpcatClient.create(privateKey);
421
+ // Parse ABI
422
+ let abi;
423
+ try {
424
+ abi = JSON.parse(options.abi);
425
+ }
426
+ catch {
427
+ const fs = await import("fs");
428
+ if (fs.existsSync(options.abi)) {
429
+ abi = JSON.parse(fs.readFileSync(options.abi, "utf-8"));
430
+ }
431
+ else {
432
+ throw new Error("Invalid ABI: must be JSON string or file path");
433
+ }
434
+ }
435
+ const result = await withLoading(async () => {
436
+ const { data } = await client.invoke("tools/abi/decode", {
437
+ abi,
438
+ data: options.data,
439
+ type: options.type,
440
+ });
441
+ return data;
442
+ }, {
443
+ message: "Decoding data...",
444
+ json: globalOpts.json,
445
+ quiet: globalOpts.quiet,
446
+ spinner: "cat",
447
+ clearOnSuccess: true,
448
+ });
449
+ if (globalOpts.json) {
450
+ outputJson("abi_decode", result);
451
+ }
452
+ else if (!globalOpts.quiet) {
453
+ console.log();
454
+ console.log(chalk.magenta.bold("🔍 ABI Decode"));
455
+ console.log();
456
+ console.log(chalk.cyan("Decoded:"));
457
+ console.log(JSON.stringify(result.decoded, null, 2));
458
+ console.log();
459
+ }
460
+ process.exit(0);
461
+ }
462
+ catch (error) {
463
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
464
+ if (globalOpts.json) {
465
+ outputError("abi_decode", error, getExitCode(error));
466
+ }
467
+ else {
468
+ handleError(error, globalOpts.verbose);
469
+ }
470
+ process.exit(getExitCode(error));
471
+ }
472
+ });
473
+ abiCommand
474
+ .command("parse")
475
+ .description("Parse ABI JSON and return structured format ($0.001)")
476
+ .requiredOption("--abi <abi>", "ABI JSON (as string or file path)")
477
+ .action(async (options, command) => {
478
+ try {
479
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
480
+ const accountIndex = globalOpts.account;
481
+ await ensureWalletUnlocked();
482
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
483
+ if (!isConfigured(globalOpts.privateKey)) {
484
+ if (globalOpts.json) {
485
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
486
+ process.exit(2);
487
+ }
488
+ privateKey = await promptForPrivateKey();
489
+ }
490
+ const client = await HttpcatClient.create(privateKey);
491
+ // Parse ABI
492
+ let abi;
493
+ try {
494
+ abi = JSON.parse(options.abi);
495
+ }
496
+ catch {
497
+ const fs = await import("fs");
498
+ if (fs.existsSync(options.abi)) {
499
+ abi = JSON.parse(fs.readFileSync(options.abi, "utf-8"));
500
+ }
501
+ else {
502
+ throw new Error("Invalid ABI: must be JSON string or file path");
503
+ }
504
+ }
505
+ const result = await withLoading(async () => {
506
+ const { data } = await client.invoke("tools/abi/parse", { abi });
507
+ return data;
508
+ }, {
509
+ message: "Parsing ABI...",
510
+ json: globalOpts.json,
511
+ quiet: globalOpts.quiet,
512
+ spinner: "cat",
513
+ clearOnSuccess: true,
514
+ });
515
+ if (globalOpts.json) {
516
+ outputJson("abi_parse", result);
517
+ }
518
+ else if (!globalOpts.quiet) {
519
+ console.log();
520
+ console.log(chalk.magenta.bold("📋 ABI Parse"));
521
+ console.log();
522
+ const boxData = {
523
+ Functions: chalk.cyan((result.functions?.length || 0).toString()),
524
+ Events: chalk.cyan((result.events?.length || 0).toString()),
525
+ Errors: chalk.cyan((result.errors?.length || 0).toString()),
526
+ };
527
+ printBox("ABI Structure", boxData);
528
+ console.log();
529
+ }
530
+ process.exit(0);
531
+ }
532
+ catch (error) {
533
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
534
+ if (globalOpts.json) {
535
+ outputError("abi_parse", error, getExitCode(error));
536
+ }
537
+ else {
538
+ handleError(error, globalOpts.verbose);
539
+ }
540
+ process.exit(getExitCode(error));
541
+ }
542
+ });
543
+ // ============================================================================
544
+ // Transaction Building
545
+ // ============================================================================
546
+ const txCommand = toolsCommand
547
+ .command("tx")
548
+ .description("Transaction utilities");
549
+ txCommand
550
+ .command("build")
551
+ .description("Build complete transaction with auto-filled fields ($0.001)")
552
+ .requiredOption("--to <address>", "To address (0x format)")
553
+ .option("--data <data>", "Transaction data (0x-prefixed hex)")
554
+ .option("--value <value>", "Value in wei")
555
+ .option("--from <address>", "From address (0x format)")
556
+ .option("--gas-limit <gas>", "Gas limit")
557
+ .option("--gas-price <price>", "Gas price in wei")
558
+ .option("--nonce <nonce>", "Nonce")
559
+ .action(async (options, command) => {
560
+ try {
561
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
562
+ const accountIndex = globalOpts.account;
563
+ await ensureWalletUnlocked();
564
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
565
+ if (!isConfigured(globalOpts.privateKey)) {
566
+ if (globalOpts.json) {
567
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
568
+ process.exit(2);
569
+ }
570
+ privateKey = await promptForPrivateKey();
571
+ }
572
+ const client = await HttpcatClient.create(privateKey);
573
+ const input = { to: options.to };
574
+ if (options.data)
575
+ input.data = options.data;
576
+ if (options.value)
577
+ input.value = options.value;
578
+ if (options.from)
579
+ input.from = options.from;
580
+ if (options.gasLimit)
581
+ input.gasLimit = options.gasLimit;
582
+ if (options.gasPrice)
583
+ input.gasPrice = options.gasPrice;
584
+ if (options.nonce)
585
+ input.nonce = parseInt(options.nonce);
586
+ const result = await withLoading(async () => {
587
+ const { data } = await client.invoke("tools/tx/build", input);
588
+ return data;
589
+ }, {
590
+ message: "Building transaction...",
591
+ json: globalOpts.json,
592
+ quiet: globalOpts.quiet,
593
+ spinner: "cat",
594
+ clearOnSuccess: true,
595
+ });
596
+ if (globalOpts.json) {
597
+ outputJson("tx_build", result);
598
+ }
599
+ else if (!globalOpts.quiet) {
600
+ console.log();
601
+ console.log(chalk.magenta.bold("🔨 Transaction Build"));
602
+ console.log();
603
+ console.log(JSON.stringify(result.transaction, null, 2));
604
+ console.log();
605
+ }
606
+ process.exit(0);
607
+ }
608
+ catch (error) {
609
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
610
+ if (globalOpts.json) {
611
+ outputError("tx_build", error, getExitCode(error));
612
+ }
613
+ else {
614
+ handleError(error, globalOpts.verbose);
615
+ }
616
+ process.exit(getExitCode(error));
617
+ }
618
+ });
619
+ txCommand
620
+ .command("encode")
621
+ .description("Encode transaction to RLP format ($0.001)")
622
+ .requiredOption("--tx <tx>", "Transaction JSON (as string or file path)")
623
+ .action(async (options, command) => {
624
+ try {
625
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
626
+ const accountIndex = globalOpts.account;
627
+ await ensureWalletUnlocked();
628
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
629
+ if (!isConfigured(globalOpts.privateKey)) {
630
+ if (globalOpts.json) {
631
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
632
+ process.exit(2);
633
+ }
634
+ privateKey = await promptForPrivateKey();
635
+ }
636
+ const client = await HttpcatClient.create(privateKey);
637
+ // Parse transaction
638
+ let transaction;
639
+ try {
640
+ transaction = JSON.parse(options.tx);
641
+ }
642
+ catch {
643
+ const fs = await import("fs");
644
+ if (fs.existsSync(options.tx)) {
645
+ transaction = JSON.parse(fs.readFileSync(options.tx, "utf-8"));
646
+ }
647
+ else {
648
+ throw new Error("Invalid transaction: must be JSON string or file path");
649
+ }
650
+ }
651
+ const result = await withLoading(async () => {
652
+ const { data } = await client.invoke("tools/tx/encode", {
653
+ transaction,
654
+ });
655
+ return data;
656
+ }, {
657
+ message: "Encoding transaction...",
658
+ json: globalOpts.json,
659
+ quiet: globalOpts.quiet,
660
+ spinner: "cat",
661
+ clearOnSuccess: true,
662
+ });
663
+ if (globalOpts.json) {
664
+ outputJson("tx_encode", result);
665
+ }
666
+ else if (!globalOpts.quiet) {
667
+ console.log();
668
+ console.log(chalk.magenta.bold("📦 Transaction Encode"));
669
+ console.log();
670
+ printBox("RLP Encoded", {
671
+ RLP: chalk.green.bold(result.rlp),
672
+ });
673
+ console.log();
674
+ }
675
+ process.exit(0);
676
+ }
677
+ catch (error) {
678
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
679
+ if (globalOpts.json) {
680
+ outputError("tx_encode", error, getExitCode(error));
681
+ }
682
+ else {
683
+ handleError(error, globalOpts.verbose);
684
+ }
685
+ process.exit(getExitCode(error));
686
+ }
687
+ });
688
+ txCommand
689
+ .command("decode")
690
+ .description("Decode RLP transaction ($0.001)")
691
+ .requiredOption("--rlp <rlp>", "RLP-encoded transaction (0x-prefixed hex)")
692
+ .action(async (options, command) => {
693
+ try {
694
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
695
+ const accountIndex = globalOpts.account;
696
+ await ensureWalletUnlocked();
697
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
698
+ if (!isConfigured(globalOpts.privateKey)) {
699
+ if (globalOpts.json) {
700
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
701
+ process.exit(2);
702
+ }
703
+ privateKey = await promptForPrivateKey();
704
+ }
705
+ const client = await HttpcatClient.create(privateKey);
706
+ const result = await withLoading(async () => {
707
+ const { data } = await client.invoke("tools/tx/decode", {
708
+ rlp: options.rlp,
709
+ });
710
+ return data;
711
+ }, {
712
+ message: "Decoding transaction...",
713
+ json: globalOpts.json,
714
+ quiet: globalOpts.quiet,
715
+ spinner: "cat",
716
+ clearOnSuccess: true,
717
+ });
718
+ if (globalOpts.json) {
719
+ outputJson("tx_decode", result);
720
+ }
721
+ else if (!globalOpts.quiet) {
722
+ console.log();
723
+ console.log(chalk.magenta.bold("🔍 Transaction Decode"));
724
+ console.log();
725
+ console.log(JSON.stringify(result.transaction, null, 2));
726
+ console.log();
727
+ }
728
+ process.exit(0);
729
+ }
730
+ catch (error) {
731
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
732
+ if (globalOpts.json) {
733
+ outputError("tx_decode", error, getExitCode(error));
734
+ }
735
+ else {
736
+ handleError(error, globalOpts.verbose);
737
+ }
738
+ process.exit(getExitCode(error));
739
+ }
740
+ });
741
+ txCommand
742
+ .command("simulate")
743
+ .description("Enhanced transaction simulation with state analysis ($0.001)")
744
+ .requiredOption("--to <address>", "To address (0x format)")
745
+ .requiredOption("--data <data>", "Transaction data (0x-prefixed hex)")
746
+ .requiredOption("--value <value>", "Value in wei")
747
+ .option("--from <address>", "From address (0x format)")
748
+ .action(async (options, command) => {
749
+ try {
750
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
751
+ const accountIndex = globalOpts.account;
752
+ await ensureWalletUnlocked();
753
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
754
+ if (!isConfigured(globalOpts.privateKey)) {
755
+ if (globalOpts.json) {
756
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
757
+ process.exit(2);
758
+ }
759
+ privateKey = await promptForPrivateKey();
760
+ }
761
+ const client = await HttpcatClient.create(privateKey);
762
+ const input = {
763
+ transaction: {
764
+ to: options.to,
765
+ data: options.data,
766
+ value: options.value,
767
+ },
768
+ };
769
+ if (options.from)
770
+ input.from = options.from;
771
+ const result = await withLoading(async () => {
772
+ const { data } = await client.invoke("tools/tx/simulate", input);
773
+ return data;
774
+ }, {
775
+ message: "Simulating transaction...",
776
+ json: globalOpts.json,
777
+ quiet: globalOpts.quiet,
778
+ spinner: "cat",
779
+ clearOnSuccess: true,
780
+ });
781
+ if (globalOpts.json) {
782
+ outputJson("tx_simulate", result);
783
+ }
784
+ else if (!globalOpts.quiet) {
785
+ console.log();
786
+ console.log(chalk.magenta.bold("🎮 Transaction Simulation"));
787
+ console.log();
788
+ const boxData = {
789
+ Success: result.success
790
+ ? chalk.green.bold("Yes")
791
+ : chalk.red.bold("No"),
792
+ "Gas Used": chalk.cyan(result.gasUsed || "N/A"),
793
+ };
794
+ printBox("Simulation Result", boxData);
795
+ if (result.logs && result.logs.length > 0) {
796
+ console.log();
797
+ console.log(chalk.cyan(`Logs (${result.logs.length}):`));
798
+ console.log(JSON.stringify(result.logs, null, 2));
799
+ }
800
+ console.log();
801
+ }
802
+ process.exit(0);
803
+ }
804
+ catch (error) {
805
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
806
+ if (globalOpts.json) {
807
+ outputError("tx_simulate", error, getExitCode(error));
808
+ }
809
+ else {
810
+ handleError(error, globalOpts.verbose);
811
+ }
812
+ process.exit(getExitCode(error));
813
+ }
814
+ });
815
+ txCommand
816
+ .command("estimate-gas")
817
+ .description("Estimate gas for transaction ($0.001)")
818
+ .requiredOption("--to <address>", "To address (0x format)")
819
+ .requiredOption("--data <data>", "Transaction data (0x-prefixed hex)")
820
+ .requiredOption("--value <value>", "Value in wei")
821
+ .option("--from <address>", "From address (0x format)")
822
+ .action(async (options, command) => {
823
+ try {
824
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
825
+ const accountIndex = globalOpts.account;
826
+ await ensureWalletUnlocked();
827
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
828
+ if (!isConfigured(globalOpts.privateKey)) {
829
+ if (globalOpts.json) {
830
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
831
+ process.exit(2);
832
+ }
833
+ privateKey = await promptForPrivateKey();
834
+ }
835
+ const client = await HttpcatClient.create(privateKey);
836
+ const input = {
837
+ transaction: {
838
+ to: options.to,
839
+ data: options.data,
840
+ value: options.value,
841
+ },
842
+ };
843
+ if (options.from)
844
+ input.from = options.from;
845
+ const result = await withLoading(async () => {
846
+ const { data } = await client.invoke("tools/tx/estimate-gas", input);
847
+ return data;
848
+ }, {
849
+ message: "Estimating gas...",
850
+ json: globalOpts.json,
851
+ quiet: globalOpts.quiet,
852
+ spinner: "cat",
853
+ clearOnSuccess: true,
854
+ });
855
+ if (globalOpts.json) {
856
+ outputJson("tx_estimate_gas", result);
857
+ }
858
+ else if (!globalOpts.quiet) {
859
+ console.log();
860
+ console.log(chalk.magenta.bold("⛽ Gas Estimation"));
861
+ console.log();
862
+ printBox("Gas Estimate", {
863
+ "Gas Estimate": chalk.green.bold(result.gasEstimate),
864
+ "Gas Limit": chalk.yellow(result.gasLimit || result.gasEstimate),
865
+ });
866
+ console.log();
867
+ }
868
+ process.exit(0);
869
+ }
870
+ catch (error) {
871
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
872
+ if (globalOpts.json) {
873
+ outputError("tx_estimate_gas", error, getExitCode(error));
874
+ }
875
+ else {
876
+ handleError(error, globalOpts.verbose);
877
+ }
878
+ process.exit(getExitCode(error));
879
+ }
880
+ });
881
+ txCommand
882
+ .command("prepare")
883
+ .description("Prepare transaction for signing ($0.001)")
884
+ .requiredOption("--to <address>", "To address (0x format)")
885
+ .requiredOption("--from <address>", "From address (0x format)")
886
+ .option("--data <data>", "Transaction data (0x-prefixed hex)")
887
+ .option("--value <value>", "Value in wei")
888
+ .action(async (options, command) => {
889
+ try {
890
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
891
+ const accountIndex = globalOpts.account;
892
+ await ensureWalletUnlocked();
893
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
894
+ if (!isConfigured(globalOpts.privateKey)) {
895
+ if (globalOpts.json) {
896
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
897
+ process.exit(2);
898
+ }
899
+ privateKey = await promptForPrivateKey();
900
+ }
901
+ const client = await HttpcatClient.create(privateKey);
902
+ const input = {
903
+ to: options.to,
904
+ from: options.from,
905
+ };
906
+ if (options.data)
907
+ input.data = options.data;
908
+ if (options.value)
909
+ input.value = options.value;
910
+ const result = await withLoading(async () => {
911
+ const { data } = await client.invoke("tools/tx/prepare", input);
912
+ return data;
913
+ }, {
914
+ message: "Preparing transaction...",
915
+ json: globalOpts.json,
916
+ quiet: globalOpts.quiet,
917
+ spinner: "cat",
918
+ clearOnSuccess: true,
919
+ });
920
+ if (globalOpts.json) {
921
+ outputJson("tx_prepare", result);
922
+ }
923
+ else if (!globalOpts.quiet) {
924
+ console.log();
925
+ console.log(chalk.magenta.bold("✍️ Transaction Prepare"));
926
+ console.log();
927
+ console.log(JSON.stringify(result.transaction, null, 2));
928
+ if (result.messageHash) {
929
+ console.log();
930
+ console.log(chalk.cyan("Message Hash:") + " " + chalk.yellow(result.messageHash));
931
+ }
932
+ console.log();
933
+ }
934
+ process.exit(0);
935
+ }
936
+ catch (error) {
937
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
938
+ if (globalOpts.json) {
939
+ outputError("tx_prepare", error, getExitCode(error));
940
+ }
941
+ else {
942
+ handleError(error, globalOpts.verbose);
943
+ }
944
+ process.exit(getExitCode(error));
945
+ }
946
+ });
947
+ // ============================================================================
948
+ // Signature Helpers
949
+ // ============================================================================
950
+ const signatureCommand = toolsCommand
951
+ .command("signature")
952
+ .description("Signature utilities");
953
+ signatureCommand
954
+ .command("eip191")
955
+ .description("Prepare EIP-191 personal sign message ($0.001)")
956
+ .requiredOption("--message <message>", "Message to sign")
957
+ .option("--address <address>", "Address for verification")
958
+ .action(async (options, command) => {
959
+ try {
960
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
961
+ const accountIndex = globalOpts.account;
962
+ await ensureWalletUnlocked();
963
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
964
+ if (!isConfigured(globalOpts.privateKey)) {
965
+ if (globalOpts.json) {
966
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
967
+ process.exit(2);
968
+ }
969
+ privateKey = await promptForPrivateKey();
970
+ }
971
+ const client = await HttpcatClient.create(privateKey);
972
+ const input = { message: options.message };
973
+ if (options.address)
974
+ input.address = options.address;
975
+ const result = await withLoading(async () => {
976
+ const { data } = await client.invoke("tools/signature/eip191", input);
977
+ return data;
978
+ }, {
979
+ message: "Preparing EIP-191 signature...",
980
+ json: globalOpts.json,
981
+ quiet: globalOpts.quiet,
982
+ spinner: "cat",
983
+ clearOnSuccess: true,
984
+ });
985
+ if (globalOpts.json) {
986
+ outputJson("signature_eip191", result);
987
+ }
988
+ else if (!globalOpts.quiet) {
989
+ console.log();
990
+ console.log(chalk.magenta.bold("✍️ EIP-191 Signature"));
991
+ console.log();
992
+ printBox("Signature Data", {
993
+ Message: chalk.cyan(result.message),
994
+ "Message Hash": chalk.green.bold(result.messageHash),
995
+ Signature: result.signature
996
+ ? chalk.yellow(result.signature)
997
+ : chalk.dim("Not signed"),
998
+ });
999
+ console.log();
1000
+ }
1001
+ process.exit(0);
1002
+ }
1003
+ catch (error) {
1004
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1005
+ if (globalOpts.json) {
1006
+ outputError("signature_eip191", error, getExitCode(error));
1007
+ }
1008
+ else {
1009
+ handleError(error, globalOpts.verbose);
1010
+ }
1011
+ process.exit(getExitCode(error));
1012
+ }
1013
+ });
1014
+ signatureCommand
1015
+ .command("eip712")
1016
+ .description("Prepare EIP-712 structured data signature ($0.001)")
1017
+ .requiredOption("--domain <domain>", "Domain JSON (as string or file path)")
1018
+ .requiredOption("--types <types>", "Types JSON (as string or file path)")
1019
+ .requiredOption("--message <message>", "Message JSON (as string or file path)")
1020
+ .action(async (options, command) => {
1021
+ try {
1022
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1023
+ const accountIndex = globalOpts.account;
1024
+ await ensureWalletUnlocked();
1025
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1026
+ if (!isConfigured(globalOpts.privateKey)) {
1027
+ if (globalOpts.json) {
1028
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1029
+ process.exit(2);
1030
+ }
1031
+ privateKey = await promptForPrivateKey();
1032
+ }
1033
+ const client = await HttpcatClient.create(privateKey);
1034
+ // Parse JSON inputs
1035
+ const parseJson = (str) => {
1036
+ try {
1037
+ return JSON.parse(str);
1038
+ }
1039
+ catch {
1040
+ const fs = require("fs");
1041
+ if (fs.existsSync(str)) {
1042
+ return JSON.parse(fs.readFileSync(str, "utf-8"));
1043
+ }
1044
+ throw new Error(`Invalid JSON: ${str}`);
1045
+ }
1046
+ };
1047
+ const domain = parseJson(options.domain);
1048
+ const types = parseJson(options.types);
1049
+ const message = parseJson(options.message);
1050
+ const result = await withLoading(async () => {
1051
+ const { data } = await client.invoke("tools/signature/eip712", {
1052
+ domain,
1053
+ types,
1054
+ message,
1055
+ });
1056
+ return data;
1057
+ }, {
1058
+ message: "Preparing EIP-712 signature...",
1059
+ json: globalOpts.json,
1060
+ quiet: globalOpts.quiet,
1061
+ spinner: "cat",
1062
+ clearOnSuccess: true,
1063
+ });
1064
+ if (globalOpts.json) {
1065
+ outputJson("signature_eip712", result);
1066
+ }
1067
+ else if (!globalOpts.quiet) {
1068
+ console.log();
1069
+ console.log(chalk.magenta.bold("✍️ EIP-712 Signature"));
1070
+ console.log();
1071
+ printBox("Signature Data", {
1072
+ "Message Hash": chalk.green.bold(result.messageHash),
1073
+ });
1074
+ console.log();
1075
+ }
1076
+ process.exit(0);
1077
+ }
1078
+ catch (error) {
1079
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1080
+ if (globalOpts.json) {
1081
+ outputError("signature_eip712", error, getExitCode(error));
1082
+ }
1083
+ else {
1084
+ handleError(error, globalOpts.verbose);
1085
+ }
1086
+ process.exit(getExitCode(error));
1087
+ }
1088
+ });
1089
+ signatureCommand
1090
+ .command("permit2")
1091
+ .description("Prepare Permit2 signature message ($0.001)")
1092
+ .requiredOption("--token <address>", "Token address (0x format)")
1093
+ .requiredOption("--amount <amount>", "Amount in wei")
1094
+ .requiredOption("--spender <address>", "Spender address (0x format)")
1095
+ .requiredOption("--nonce <nonce>", "Nonce")
1096
+ .requiredOption("--deadline <deadline>", "Deadline (Unix timestamp)")
1097
+ .action(async (options, command) => {
1098
+ try {
1099
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1100
+ const accountIndex = globalOpts.account;
1101
+ await ensureWalletUnlocked();
1102
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1103
+ if (!isConfigured(globalOpts.privateKey)) {
1104
+ if (globalOpts.json) {
1105
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1106
+ process.exit(2);
1107
+ }
1108
+ privateKey = await promptForPrivateKey();
1109
+ }
1110
+ const client = await HttpcatClient.create(privateKey);
1111
+ const result = await withLoading(async () => {
1112
+ const { data } = await client.invoke("tools/signature/permit2", {
1113
+ token: options.token,
1114
+ amount: options.amount,
1115
+ spender: options.spender,
1116
+ nonce: parseInt(options.nonce),
1117
+ deadline: parseInt(options.deadline),
1118
+ });
1119
+ return data;
1120
+ }, {
1121
+ message: "Preparing Permit2 signature...",
1122
+ json: globalOpts.json,
1123
+ quiet: globalOpts.quiet,
1124
+ spinner: "cat",
1125
+ clearOnSuccess: true,
1126
+ });
1127
+ if (globalOpts.json) {
1128
+ outputJson("signature_permit2", result);
1129
+ }
1130
+ else if (!globalOpts.quiet) {
1131
+ console.log();
1132
+ console.log(chalk.magenta.bold("✍️ Permit2 Signature"));
1133
+ console.log();
1134
+ printBox("Signature Data", {
1135
+ "Message Hash": chalk.green.bold(result.messageHash),
1136
+ });
1137
+ console.log();
1138
+ }
1139
+ process.exit(0);
1140
+ }
1141
+ catch (error) {
1142
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1143
+ if (globalOpts.json) {
1144
+ outputError("signature_permit2", error, getExitCode(error));
1145
+ }
1146
+ else {
1147
+ handleError(error, globalOpts.verbose);
1148
+ }
1149
+ process.exit(getExitCode(error));
1150
+ }
1151
+ });
1152
+ // ============================================================================
1153
+ // Multicall Operations
1154
+ // ============================================================================
1155
+ const multicallCommand = toolsCommand
1156
+ .command("multicall")
1157
+ .description("Multicall operations");
1158
+ multicallCommand
1159
+ .command("encode")
1160
+ .description("Encode multicall operation ($0.001)")
1161
+ .requiredOption("--calls <calls>", "Calls JSON array (as string or file path)")
1162
+ .action(async (options, command) => {
1163
+ try {
1164
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1165
+ const accountIndex = globalOpts.account;
1166
+ await ensureWalletUnlocked();
1167
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1168
+ if (!isConfigured(globalOpts.privateKey)) {
1169
+ if (globalOpts.json) {
1170
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1171
+ process.exit(2);
1172
+ }
1173
+ privateKey = await promptForPrivateKey();
1174
+ }
1175
+ const client = await HttpcatClient.create(privateKey);
1176
+ // Parse calls
1177
+ let calls;
1178
+ try {
1179
+ calls = JSON.parse(options.calls);
1180
+ }
1181
+ catch {
1182
+ const fs = await import("fs");
1183
+ if (fs.existsSync(options.calls)) {
1184
+ calls = JSON.parse(fs.readFileSync(options.calls, "utf-8"));
1185
+ }
1186
+ else {
1187
+ throw new Error("Invalid calls: must be JSON array string or file path");
1188
+ }
1189
+ }
1190
+ const result = await withLoading(async () => {
1191
+ const { data } = await client.invoke("tools/multicall/encode", {
1192
+ calls,
1193
+ });
1194
+ return data;
1195
+ }, {
1196
+ message: "Encoding multicall...",
1197
+ json: globalOpts.json,
1198
+ quiet: globalOpts.quiet,
1199
+ spinner: "cat",
1200
+ clearOnSuccess: true,
1201
+ });
1202
+ if (globalOpts.json) {
1203
+ outputJson("multicall_encode", result);
1204
+ }
1205
+ else if (!globalOpts.quiet) {
1206
+ console.log();
1207
+ console.log(chalk.magenta.bold("📦 Multicall Encode"));
1208
+ console.log();
1209
+ printBox("Encoded Data", {
1210
+ Data: chalk.green.bold(result.data),
1211
+ });
1212
+ console.log();
1213
+ }
1214
+ process.exit(0);
1215
+ }
1216
+ catch (error) {
1217
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1218
+ if (globalOpts.json) {
1219
+ outputError("multicall_encode", error, getExitCode(error));
1220
+ }
1221
+ else {
1222
+ handleError(error, globalOpts.verbose);
1223
+ }
1224
+ process.exit(getExitCode(error));
1225
+ }
1226
+ });
1227
+ multicallCommand
1228
+ .command("decode")
1229
+ .description("Decode multicall results ($0.001)")
1230
+ .requiredOption("--data <data>", "Multicall result data (0x-prefixed hex)")
1231
+ .requiredOption("--calls <calls>", "Original calls JSON array (as string or file path)")
1232
+ .action(async (options, command) => {
1233
+ try {
1234
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1235
+ const accountIndex = globalOpts.account;
1236
+ await ensureWalletUnlocked();
1237
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1238
+ if (!isConfigured(globalOpts.privateKey)) {
1239
+ if (globalOpts.json) {
1240
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1241
+ process.exit(2);
1242
+ }
1243
+ privateKey = await promptForPrivateKey();
1244
+ }
1245
+ const client = await HttpcatClient.create(privateKey);
1246
+ // Parse calls
1247
+ let calls;
1248
+ try {
1249
+ calls = JSON.parse(options.calls);
1250
+ }
1251
+ catch {
1252
+ const fs = await import("fs");
1253
+ if (fs.existsSync(options.calls)) {
1254
+ calls = JSON.parse(fs.readFileSync(options.calls, "utf-8"));
1255
+ }
1256
+ else {
1257
+ throw new Error("Invalid calls: must be JSON array string or file path");
1258
+ }
1259
+ }
1260
+ const result = await withLoading(async () => {
1261
+ const { data } = await client.invoke("tools/multicall/decode", {
1262
+ data: options.data,
1263
+ calls,
1264
+ });
1265
+ return data;
1266
+ }, {
1267
+ message: "Decoding multicall results...",
1268
+ json: globalOpts.json,
1269
+ quiet: globalOpts.quiet,
1270
+ spinner: "cat",
1271
+ clearOnSuccess: true,
1272
+ });
1273
+ if (globalOpts.json) {
1274
+ outputJson("multicall_decode", result);
1275
+ }
1276
+ else if (!globalOpts.quiet) {
1277
+ console.log();
1278
+ console.log(chalk.magenta.bold("🔍 Multicall Decode"));
1279
+ console.log();
1280
+ console.log(JSON.stringify(result.results, null, 2));
1281
+ console.log();
1282
+ }
1283
+ process.exit(0);
1284
+ }
1285
+ catch (error) {
1286
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1287
+ if (globalOpts.json) {
1288
+ outputError("multicall_decode", error, getExitCode(error));
1289
+ }
1290
+ else {
1291
+ handleError(error, globalOpts.verbose);
1292
+ }
1293
+ process.exit(getExitCode(error));
1294
+ }
1295
+ });
1296
+ // ============================================================================
1297
+ // Nonce Management
1298
+ // ============================================================================
1299
+ const nonceCommand = toolsCommand
1300
+ .command("nonce")
1301
+ .description("Nonce management");
1302
+ nonceCommand
1303
+ .command("get")
1304
+ .description("Get current nonce for address ($0.001)")
1305
+ .argument("<address>", "Address (0x format)")
1306
+ .action(async (address, options, command) => {
1307
+ try {
1308
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1309
+ const accountIndex = globalOpts.account;
1310
+ await ensureWalletUnlocked();
1311
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1312
+ if (!isConfigured(globalOpts.privateKey)) {
1313
+ if (globalOpts.json) {
1314
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1315
+ process.exit(2);
1316
+ }
1317
+ privateKey = await promptForPrivateKey();
1318
+ }
1319
+ const client = await HttpcatClient.create(privateKey);
1320
+ const result = await withLoading(async () => {
1321
+ const { data } = await client.invoke("tools/nonce/get", { address });
1322
+ return data;
1323
+ }, {
1324
+ message: "Fetching nonce...",
1325
+ json: globalOpts.json,
1326
+ quiet: globalOpts.quiet,
1327
+ spinner: "cat",
1328
+ clearOnSuccess: true,
1329
+ });
1330
+ if (globalOpts.json) {
1331
+ outputJson("nonce_get", result);
1332
+ }
1333
+ else if (!globalOpts.quiet) {
1334
+ console.log();
1335
+ console.log(chalk.magenta.bold("🔢 Nonce"));
1336
+ console.log();
1337
+ printBox("Nonce Info", {
1338
+ Address: chalk.cyan(address),
1339
+ "Current Nonce": chalk.green.bold(result.nonce?.toString() || "N/A"),
1340
+ "Pending Nonce": chalk.yellow(result.pendingNonce?.toString() || "N/A"),
1341
+ });
1342
+ console.log();
1343
+ }
1344
+ process.exit(0);
1345
+ }
1346
+ catch (error) {
1347
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1348
+ if (globalOpts.json) {
1349
+ outputError("nonce_get", error, getExitCode(error));
1350
+ }
1351
+ else {
1352
+ handleError(error, globalOpts.verbose);
1353
+ }
1354
+ process.exit(getExitCode(error));
1355
+ }
1356
+ });
1357
+ nonceCommand
1358
+ .command("predict")
1359
+ .description("Predict next nonce accounting for pending transactions ($0.001)")
1360
+ .argument("<address>", "Address (0x format)")
1361
+ .action(async (address, options, command) => {
1362
+ try {
1363
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1364
+ const accountIndex = globalOpts.account;
1365
+ await ensureWalletUnlocked();
1366
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1367
+ if (!isConfigured(globalOpts.privateKey)) {
1368
+ if (globalOpts.json) {
1369
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1370
+ process.exit(2);
1371
+ }
1372
+ privateKey = await promptForPrivateKey();
1373
+ }
1374
+ const client = await HttpcatClient.create(privateKey);
1375
+ const result = await withLoading(async () => {
1376
+ const { data } = await client.invoke("tools/nonce/predict", {
1377
+ address,
1378
+ });
1379
+ return data;
1380
+ }, {
1381
+ message: "Predicting nonce...",
1382
+ json: globalOpts.json,
1383
+ quiet: globalOpts.quiet,
1384
+ spinner: "cat",
1385
+ clearOnSuccess: true,
1386
+ });
1387
+ if (globalOpts.json) {
1388
+ outputJson("nonce_predict", result);
1389
+ }
1390
+ else if (!globalOpts.quiet) {
1391
+ console.log();
1392
+ console.log(chalk.magenta.bold("🔮 Nonce Prediction"));
1393
+ console.log();
1394
+ printBox("Nonce Prediction", {
1395
+ Address: chalk.cyan(address),
1396
+ "Current Nonce": chalk.green(result.currentNonce?.toString() || "N/A"),
1397
+ "Pending Count": chalk.yellow(result.pendingCount?.toString() || "0"),
1398
+ "Predicted Nonce": chalk.green.bold(result.predictedNonce?.toString() || "N/A"),
1399
+ });
1400
+ console.log();
1401
+ }
1402
+ process.exit(0);
1403
+ }
1404
+ catch (error) {
1405
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1406
+ if (globalOpts.json) {
1407
+ outputError("nonce_predict", error, getExitCode(error));
1408
+ }
1409
+ else {
1410
+ handleError(error, globalOpts.verbose);
1411
+ }
1412
+ process.exit(getExitCode(error));
1413
+ }
1414
+ });
1415
+ // ============================================================================
1416
+ // Gas Utilities
1417
+ // ============================================================================
1418
+ const gasCommand = toolsCommand
1419
+ .command("gas")
1420
+ .description("Gas utilities");
1421
+ gasCommand
1422
+ .command("price")
1423
+ .description("Get current gas price ($0.001)")
1424
+ .action(async (options, command) => {
1425
+ try {
1426
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1427
+ const accountIndex = globalOpts.account;
1428
+ await ensureWalletUnlocked();
1429
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1430
+ if (!isConfigured(globalOpts.privateKey)) {
1431
+ if (globalOpts.json) {
1432
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1433
+ process.exit(2);
1434
+ }
1435
+ privateKey = await promptForPrivateKey();
1436
+ }
1437
+ const client = await HttpcatClient.create(privateKey);
1438
+ const result = await withLoading(async () => {
1439
+ const { data } = await client.invoke("tools/gas/price", {});
1440
+ return data;
1441
+ }, {
1442
+ message: "Fetching gas price...",
1443
+ json: globalOpts.json,
1444
+ quiet: globalOpts.quiet,
1445
+ spinner: "cat",
1446
+ clearOnSuccess: true,
1447
+ });
1448
+ if (globalOpts.json) {
1449
+ outputJson("gas_price", result);
1450
+ }
1451
+ else if (!globalOpts.quiet) {
1452
+ console.log();
1453
+ console.log(chalk.magenta.bold("⛽ Gas Price"));
1454
+ console.log();
1455
+ printBox("Gas Prices", {
1456
+ "Gas Price": chalk.green(result.gasPrice || "N/A"),
1457
+ "Max Fee Per Gas": chalk.yellow(result.maxFeePerGas || "N/A"),
1458
+ "Max Priority Fee Per Gas": chalk.cyan(result.maxPriorityFeePerGas || "N/A"),
1459
+ });
1460
+ console.log();
1461
+ }
1462
+ process.exit(0);
1463
+ }
1464
+ catch (error) {
1465
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1466
+ if (globalOpts.json) {
1467
+ outputError("gas_price", error, getExitCode(error));
1468
+ }
1469
+ else {
1470
+ handleError(error, globalOpts.verbose);
1471
+ }
1472
+ process.exit(getExitCode(error));
1473
+ }
1474
+ });
1475
+ gasCommand
1476
+ .command("estimate")
1477
+ .description("Estimate gas for operation ($0.001)")
1478
+ .requiredOption("--to <address>", "To address (0x format)")
1479
+ .requiredOption("--data <data>", "Transaction data (0x-prefixed hex)")
1480
+ .requiredOption("--value <value>", "Value in wei")
1481
+ .action(async (options, command) => {
1482
+ try {
1483
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1484
+ const accountIndex = globalOpts.account;
1485
+ await ensureWalletUnlocked();
1486
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1487
+ if (!isConfigured(globalOpts.privateKey)) {
1488
+ if (globalOpts.json) {
1489
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1490
+ process.exit(2);
1491
+ }
1492
+ privateKey = await promptForPrivateKey();
1493
+ }
1494
+ const client = await HttpcatClient.create(privateKey);
1495
+ const result = await withLoading(async () => {
1496
+ const { data } = await client.invoke("tools/gas/estimate", {
1497
+ transaction: {
1498
+ to: options.to,
1499
+ data: options.data,
1500
+ value: options.value,
1501
+ },
1502
+ });
1503
+ return data;
1504
+ }, {
1505
+ message: "Estimating gas...",
1506
+ json: globalOpts.json,
1507
+ quiet: globalOpts.quiet,
1508
+ spinner: "cat",
1509
+ clearOnSuccess: true,
1510
+ });
1511
+ if (globalOpts.json) {
1512
+ outputJson("gas_estimate", result);
1513
+ }
1514
+ else if (!globalOpts.quiet) {
1515
+ console.log();
1516
+ console.log(chalk.magenta.bold("⛽ Gas Estimate"));
1517
+ console.log();
1518
+ printBox("Gas Estimate", {
1519
+ "Gas Estimate": chalk.green.bold(result.gasEstimate),
1520
+ });
1521
+ console.log();
1522
+ }
1523
+ process.exit(0);
1524
+ }
1525
+ catch (error) {
1526
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1527
+ if (globalOpts.json) {
1528
+ outputError("gas_estimate", error, getExitCode(error));
1529
+ }
1530
+ else {
1531
+ handleError(error, globalOpts.verbose);
1532
+ }
1533
+ process.exit(getExitCode(error));
1534
+ }
1535
+ });
1536
+ gasCommand
1537
+ .command("optimize")
1538
+ .description("Optimize gas settings for transaction ($0.001)")
1539
+ .requiredOption("--to <address>", "To address (0x format)")
1540
+ .requiredOption("--data <data>", "Transaction data (0x-prefixed hex)")
1541
+ .requiredOption("--value <value>", "Value in wei")
1542
+ .option("--priority <priority>", "Priority: low, medium, high", "medium")
1543
+ .action(async (options, command) => {
1544
+ try {
1545
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1546
+ const accountIndex = globalOpts.account;
1547
+ await ensureWalletUnlocked();
1548
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1549
+ if (!isConfigured(globalOpts.privateKey)) {
1550
+ if (globalOpts.json) {
1551
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1552
+ process.exit(2);
1553
+ }
1554
+ privateKey = await promptForPrivateKey();
1555
+ }
1556
+ const client = await HttpcatClient.create(privateKey);
1557
+ const input = {
1558
+ transaction: {
1559
+ to: options.to,
1560
+ data: options.data,
1561
+ value: options.value,
1562
+ },
1563
+ };
1564
+ if (options.priority)
1565
+ input.priority = options.priority;
1566
+ const result = await withLoading(async () => {
1567
+ const { data } = await client.invoke("tools/gas/optimize", input);
1568
+ return data;
1569
+ }, {
1570
+ message: "Optimizing gas settings...",
1571
+ json: globalOpts.json,
1572
+ quiet: globalOpts.quiet,
1573
+ spinner: "cat",
1574
+ clearOnSuccess: true,
1575
+ });
1576
+ if (globalOpts.json) {
1577
+ outputJson("gas_optimize", result);
1578
+ }
1579
+ else if (!globalOpts.quiet) {
1580
+ console.log();
1581
+ console.log(chalk.magenta.bold("⚡ Gas Optimization"));
1582
+ console.log();
1583
+ printBox("Optimized Gas", {
1584
+ "Gas Price": chalk.green(result.gasPrice || "N/A"),
1585
+ "Max Fee Per Gas": chalk.yellow(result.maxFeePerGas || "N/A"),
1586
+ "Max Priority Fee Per Gas": chalk.cyan(result.maxPriorityFeePerGas || "N/A"),
1587
+ "Estimated Cost": chalk.magenta(result.estimatedCost || "N/A"),
1588
+ });
1589
+ console.log();
1590
+ }
1591
+ process.exit(0);
1592
+ }
1593
+ catch (error) {
1594
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1595
+ if (globalOpts.json) {
1596
+ outputError("gas_optimize", error, getExitCode(error));
1597
+ }
1598
+ else {
1599
+ handleError(error, globalOpts.verbose);
1600
+ }
1601
+ process.exit(getExitCode(error));
1602
+ }
1603
+ });
1604
+ // ============================================================================
1605
+ // Key Utilities
1606
+ // ============================================================================
1607
+ const keyCommand = toolsCommand
1608
+ .command("key")
1609
+ .description("Key utilities");
1610
+ keyCommand
1611
+ .command("derive")
1612
+ .description("Derive address from private key ($0.001)")
1613
+ .argument("<privateKey>", "Private key (0x-prefixed hex)")
1614
+ .action(async (privateKey, options, command) => {
1615
+ try {
1616
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1617
+ const accountIndex = globalOpts.account;
1618
+ await ensureWalletUnlocked();
1619
+ let pk = getPrivateKey(globalOpts.privateKey, accountIndex) || privateKey;
1620
+ if (!isConfigured(globalOpts.privateKey) && !privateKey) {
1621
+ if (globalOpts.json) {
1622
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1623
+ process.exit(2);
1624
+ }
1625
+ pk = await promptForPrivateKey();
1626
+ }
1627
+ const client = await HttpcatClient.create(pk);
1628
+ const result = await withLoading(async () => {
1629
+ const { data } = await client.invoke("tools/key/derive", {
1630
+ privateKey: pk,
1631
+ });
1632
+ return data;
1633
+ }, {
1634
+ message: "Deriving address...",
1635
+ json: globalOpts.json,
1636
+ quiet: globalOpts.quiet,
1637
+ spinner: "cat",
1638
+ clearOnSuccess: true,
1639
+ });
1640
+ if (globalOpts.json) {
1641
+ outputJson("key_derive", result);
1642
+ }
1643
+ else if (!globalOpts.quiet) {
1644
+ console.log();
1645
+ console.log(chalk.magenta.bold("🔑 Key Derivation"));
1646
+ console.log();
1647
+ printBox("Derived Address", {
1648
+ Address: chalk.green.bold(result.address),
1649
+ });
1650
+ console.log();
1651
+ }
1652
+ process.exit(0);
1653
+ }
1654
+ catch (error) {
1655
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1656
+ if (globalOpts.json) {
1657
+ outputError("key_derive", error, getExitCode(error));
1658
+ }
1659
+ else {
1660
+ handleError(error, globalOpts.verbose);
1661
+ }
1662
+ process.exit(getExitCode(error));
1663
+ }
1664
+ });
1665
+ keyCommand
1666
+ .command("validate")
1667
+ .description("Validate private key format ($0.001)")
1668
+ .argument("<privateKey>", "Private key")
1669
+ .action(async (privateKey, options, command) => {
1670
+ try {
1671
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1672
+ const accountIndex = globalOpts.account;
1673
+ await ensureWalletUnlocked();
1674
+ let privateKeyToUse = getPrivateKey(globalOpts.privateKey, accountIndex) || privateKey;
1675
+ if (!isConfigured(globalOpts.privateKey) && !privateKey) {
1676
+ if (globalOpts.json) {
1677
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1678
+ process.exit(2);
1679
+ }
1680
+ privateKeyToUse = await promptForPrivateKey();
1681
+ }
1682
+ const client = await HttpcatClient.create(privateKeyToUse);
1683
+ const result = await withLoading(async () => {
1684
+ const { data } = await client.invoke("tools/key/validate", {
1685
+ privateKey: privateKeyToUse,
1686
+ });
1687
+ return data;
1688
+ }, {
1689
+ message: "Validating private key...",
1690
+ json: globalOpts.json,
1691
+ quiet: globalOpts.quiet,
1692
+ spinner: "cat",
1693
+ clearOnSuccess: true,
1694
+ });
1695
+ if (globalOpts.json) {
1696
+ outputJson("key_validate", result);
1697
+ }
1698
+ else if (!globalOpts.quiet) {
1699
+ console.log();
1700
+ console.log(chalk.magenta.bold("✅ Key Validation"));
1701
+ console.log();
1702
+ printBox("Validation Result", {
1703
+ Valid: result.valid
1704
+ ? chalk.green.bold("Yes")
1705
+ : chalk.red.bold("No"),
1706
+ Format: result.format ? chalk.cyan(result.format) : chalk.dim("N/A"),
1707
+ });
1708
+ console.log();
1709
+ }
1710
+ process.exit(0);
1711
+ }
1712
+ catch (error) {
1713
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1714
+ if (globalOpts.json) {
1715
+ outputError("key_validate", error, getExitCode(error));
1716
+ }
1717
+ else {
1718
+ handleError(error, globalOpts.verbose);
1719
+ }
1720
+ process.exit(getExitCode(error));
1721
+ }
1722
+ });
1723
+ // ============================================================================
1724
+ // RPC Utilities
1725
+ // ============================================================================
1726
+ const rpcCommand = toolsCommand
1727
+ .command("rpc")
1728
+ .description("RPC utilities");
1729
+ rpcCommand
1730
+ .command("proxy")
1731
+ .description("Enhanced JSON-RPC proxy ($0.001)")
1732
+ .requiredOption("--method <method>", "RPC method (e.g., 'eth_getBalance')")
1733
+ .requiredOption("--params <params>", "Params as JSON array")
1734
+ .action(async (options, command) => {
1735
+ try {
1736
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1737
+ const accountIndex = globalOpts.account;
1738
+ await ensureWalletUnlocked();
1739
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1740
+ if (!isConfigured(globalOpts.privateKey)) {
1741
+ if (globalOpts.json) {
1742
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1743
+ process.exit(2);
1744
+ }
1745
+ privateKey = await promptForPrivateKey();
1746
+ }
1747
+ const client = await HttpcatClient.create(privateKey);
1748
+ // Parse params
1749
+ let params;
1750
+ try {
1751
+ params = JSON.parse(options.params);
1752
+ }
1753
+ catch {
1754
+ throw new Error("Invalid params: must be JSON array");
1755
+ }
1756
+ const result = await withLoading(async () => {
1757
+ const { data } = await client.invoke("tools/rpc/proxy", {
1758
+ method: options.method,
1759
+ params,
1760
+ });
1761
+ return data;
1762
+ }, {
1763
+ message: "Calling RPC...",
1764
+ json: globalOpts.json,
1765
+ quiet: globalOpts.quiet,
1766
+ spinner: "cat",
1767
+ clearOnSuccess: true,
1768
+ });
1769
+ if (globalOpts.json) {
1770
+ outputJson("rpc_proxy", result);
1771
+ }
1772
+ else if (!globalOpts.quiet) {
1773
+ console.log();
1774
+ console.log(chalk.magenta.bold("🌐 RPC Proxy"));
1775
+ console.log();
1776
+ console.log(JSON.stringify(result, null, 2));
1777
+ console.log();
1778
+ }
1779
+ process.exit(0);
1780
+ }
1781
+ catch (error) {
1782
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1783
+ if (globalOpts.json) {
1784
+ outputError("rpc_proxy", error, getExitCode(error));
1785
+ }
1786
+ else {
1787
+ handleError(error, globalOpts.verbose);
1788
+ }
1789
+ process.exit(getExitCode(error));
1790
+ }
1791
+ });
1792
+ rpcCommand
1793
+ .command("batch")
1794
+ .description("Batch multiple RPC calls ($0.001)")
1795
+ .requiredOption("--requests <requests>", "Requests JSON array (as string or file path)")
1796
+ .action(async (options, command) => {
1797
+ try {
1798
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1799
+ const accountIndex = globalOpts.account;
1800
+ await ensureWalletUnlocked();
1801
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1802
+ if (!isConfigured(globalOpts.privateKey)) {
1803
+ if (globalOpts.json) {
1804
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1805
+ process.exit(2);
1806
+ }
1807
+ privateKey = await promptForPrivateKey();
1808
+ }
1809
+ const client = await HttpcatClient.create(privateKey);
1810
+ // Parse requests
1811
+ let requests;
1812
+ try {
1813
+ requests = JSON.parse(options.requests);
1814
+ }
1815
+ catch {
1816
+ const fs = await import("fs");
1817
+ if (fs.existsSync(options.requests)) {
1818
+ requests = JSON.parse(fs.readFileSync(options.requests, "utf-8"));
1819
+ }
1820
+ else {
1821
+ throw new Error("Invalid requests: must be JSON array string or file path");
1822
+ }
1823
+ }
1824
+ const result = await withLoading(async () => {
1825
+ const { data } = await client.invoke("tools/rpc/batch", {
1826
+ requests,
1827
+ });
1828
+ return data;
1829
+ }, {
1830
+ message: "Batching RPC calls...",
1831
+ json: globalOpts.json,
1832
+ quiet: globalOpts.quiet,
1833
+ spinner: "cat",
1834
+ clearOnSuccess: true,
1835
+ });
1836
+ if (globalOpts.json) {
1837
+ outputJson("rpc_batch", result);
1838
+ }
1839
+ else if (!globalOpts.quiet) {
1840
+ console.log();
1841
+ console.log(chalk.magenta.bold("📦 RPC Batch"));
1842
+ console.log();
1843
+ console.log(JSON.stringify(result.results, null, 2));
1844
+ console.log();
1845
+ }
1846
+ process.exit(0);
1847
+ }
1848
+ catch (error) {
1849
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1850
+ if (globalOpts.json) {
1851
+ outputError("rpc_batch", error, getExitCode(error));
1852
+ }
1853
+ else {
1854
+ handleError(error, globalOpts.verbose);
1855
+ }
1856
+ process.exit(getExitCode(error));
1857
+ }
1858
+ });
1859
+ rpcCommand
1860
+ .command("health")
1861
+ .description("Check RPC endpoint health ($0.001)")
1862
+ .action(async (options, command) => {
1863
+ try {
1864
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1865
+ const accountIndex = globalOpts.account;
1866
+ await ensureWalletUnlocked();
1867
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1868
+ if (!isConfigured(globalOpts.privateKey)) {
1869
+ if (globalOpts.json) {
1870
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1871
+ process.exit(2);
1872
+ }
1873
+ privateKey = await promptForPrivateKey();
1874
+ }
1875
+ const client = await HttpcatClient.create(privateKey);
1876
+ const result = await withLoading(async () => {
1877
+ const { data } = await client.invoke("tools/rpc/health", {});
1878
+ return data;
1879
+ }, {
1880
+ message: "Checking RPC health...",
1881
+ json: globalOpts.json,
1882
+ quiet: globalOpts.quiet,
1883
+ spinner: "cat",
1884
+ clearOnSuccess: true,
1885
+ });
1886
+ if (globalOpts.json) {
1887
+ outputJson("rpc_health", result);
1888
+ }
1889
+ else if (!globalOpts.quiet) {
1890
+ console.log();
1891
+ console.log(chalk.magenta.bold("🏥 RPC Health"));
1892
+ console.log();
1893
+ printBox("Health Status", {
1894
+ Healthy: result.healthy
1895
+ ? chalk.green.bold("Yes")
1896
+ : chalk.red.bold("No"),
1897
+ Latency: result.latency
1898
+ ? chalk.cyan(`${result.latency}ms`)
1899
+ : "N/A",
1900
+ "Chain ID": result.chainId
1901
+ ? chalk.yellow(result.chainId.toString())
1902
+ : "N/A",
1903
+ "Block Number": result.blockNumber
1904
+ ? chalk.cyan(result.blockNumber.toString())
1905
+ : "N/A",
1906
+ });
1907
+ console.log();
1908
+ }
1909
+ process.exit(0);
1910
+ }
1911
+ catch (error) {
1912
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1913
+ if (globalOpts.json) {
1914
+ outputError("rpc_health", error, getExitCode(error));
1915
+ }
1916
+ else {
1917
+ handleError(error, globalOpts.verbose);
1918
+ }
1919
+ process.exit(getExitCode(error));
1920
+ }
1921
+ });
1922
+ // ============================================================================
1923
+ // x402 Endpoint Call
1924
+ // ============================================================================
1925
+ toolsCommand
1926
+ .command("call")
1927
+ .description("Call an arbitrary x402-protected HTTP endpoint with automatic payment handling")
1928
+ .argument("<url>", "The x402 endpoint URL")
1929
+ .option("-m, --method <method>", "HTTP method (GET, POST, PUT, DELETE, PATCH)", "POST")
1930
+ .option("-b, --body <body>", "Request body as JSON string (or '-' to read from stdin)")
1931
+ .option("-H, --header <header>", "Additional HTTP headers (format: 'Key: Value')", (val, prev) => {
1932
+ const headers = prev || [];
1933
+ headers.push(val);
1934
+ return headers;
1935
+ })
1936
+ .addHelpText("after", `
1937
+ Examples:
1938
+ httpcat tools call https://example.com/api/endpoint --method POST --body '{"key": "value"}'
1939
+ httpcat tools call https://example.com/api/endpoint --method GET
1940
+ echo '{"data": "test"}' | httpcat tools call https://example.com/api/endpoint --body -
1941
+ httpcat tools call https://example.com/api/endpoint --header "X-Custom-Header: value" --header "Authorization: Bearer token"
1942
+ `)
1943
+ .action(async (url, options, command) => {
1944
+ try {
1945
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
1946
+ const accountIndex = globalOpts.account;
1947
+ // Ensure wallet is unlocked
1948
+ await ensureWalletUnlocked();
1949
+ let privateKey = getPrivateKey(globalOpts.privateKey, accountIndex);
1950
+ // If not configured and not in JSON mode, prompt interactively
1951
+ if (!isConfigured(globalOpts.privateKey)) {
1952
+ if (globalOpts.json) {
1953
+ console.error('❌ Not configured. Run "httpcat config" first or use --private-key flag.');
1954
+ process.exit(2);
1955
+ }
1956
+ privateKey = await promptForPrivateKey();
1957
+ }
1958
+ const client = await HttpcatClient.create(privateKey);
1959
+ // Parse body
1960
+ let body = undefined;
1961
+ if (options.body !== undefined) {
1962
+ if (options.body === "-") {
1963
+ // Read from stdin
1964
+ const { readFileSync } = await import("fs");
1965
+ const stdin = readFileSync(0, "utf-8").trim();
1966
+ if (stdin) {
1967
+ try {
1968
+ body = JSON.parse(stdin);
1969
+ }
1970
+ catch {
1971
+ body = stdin;
1972
+ }
1973
+ }
1974
+ }
1975
+ else {
1976
+ try {
1977
+ body = JSON.parse(options.body);
1978
+ }
1979
+ catch {
1980
+ body = options.body;
1981
+ }
1982
+ }
1983
+ }
1984
+ // Parse headers
1985
+ const headers = {};
1986
+ if (options.header && Array.isArray(options.header)) {
1987
+ for (const header of options.header) {
1988
+ const colonIndex = header.indexOf(":");
1989
+ if (colonIndex === -1) {
1990
+ throw new Error(`Invalid header format: ${header}. Expected format: "Key: Value"`);
1991
+ }
1992
+ const key = header.substring(0, colonIndex).trim();
1993
+ const value = header.substring(colonIndex + 1).trim();
1994
+ headers[key] = value;
1995
+ }
1996
+ }
1997
+ else if (options.header) {
1998
+ // Single header
1999
+ const colonIndex = options.header.indexOf(":");
2000
+ if (colonIndex === -1) {
2001
+ throw new Error(`Invalid header format: ${options.header}. Expected format: "Key: Value"`);
2002
+ }
2003
+ const key = options.header.substring(0, colonIndex).trim();
2004
+ const value = options.header.substring(colonIndex + 1).trim();
2005
+ headers[key] = value;
2006
+ }
2007
+ const result = await withLoading(() => callX402Endpoint(client, url, {
2008
+ method: options.method,
2009
+ body,
2010
+ headers,
2011
+ }), {
2012
+ message: "Calling x402 endpoint...",
2013
+ json: globalOpts.json,
2014
+ quiet: globalOpts.quiet,
2015
+ spinner: "cat",
2016
+ clearOnSuccess: true,
2017
+ });
2018
+ if (globalOpts.json) {
2019
+ outputJson("call", result);
2020
+ }
2021
+ else if (!globalOpts.quiet) {
2022
+ displayCallResult(result);
2023
+ }
2024
+ // Exit with non-zero code if status is error
2025
+ process.exit(result.status >= 400 ? 1 : 0);
2026
+ }
2027
+ catch (error) {
2028
+ const globalOpts = command.parent?.parent?.parent?.opts() || {};
2029
+ if (globalOpts.json) {
2030
+ outputError("call", error, getExitCode(error));
2031
+ }
2032
+ else {
2033
+ handleError(error, globalOpts.verbose);
2034
+ }
2035
+ process.exit(getExitCode(error));
2036
+ }
2037
+ });
2038
+ return toolsCommand;
2039
+ }
2040
+ //# sourceMappingURL=index.js.map