@covalenthq/goldrush-cli 3.0.1 → 3.0.2

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/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@covalenthq/goldrush-cli",
6
- version: "3.0.1",
6
+ version: "3.0.2",
7
7
  description: "Terminal-first blockchain data & algorithmic trading CLI powered by GoldRush API",
8
8
  type: "module",
9
9
  bin: {
@@ -45,8 +45,8 @@ var package_default = {
45
45
  homepage: "https://goldrush.dev/docs/",
46
46
  repository: {
47
47
  type: "git",
48
- url: "https://github.com/covalenthq/covalent-api-sdk-ts.git",
49
- directory: "services/mcp-server"
48
+ url: "https://github.com/covalenthq/goldrush-services-monorepo.git",
49
+ directory: "services/goldrush-cli"
50
50
  },
51
51
  dependencies: {
52
52
  "@covalenthq/client-sdk": "workspace:^",
@@ -100,6 +100,9 @@ async function getKeytar() {
100
100
  return null;
101
101
  }
102
102
  }
103
+ async function isKeychainAvailable() {
104
+ return await getKeytar() !== null;
105
+ }
103
106
  async function getApiKey() {
104
107
  const keytar = await getKeytar();
105
108
  if (keytar) {
@@ -186,10 +189,7 @@ import { GoldRushClient } from "@covalenthq/client-sdk";
186
189
  async function validateApiKey(key) {
187
190
  try {
188
191
  const client = new GoldRushClient(key);
189
- const response = await client.BalanceService.getTokenBalancesForWalletAddress(
190
- "eth-mainnet",
191
- "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
192
- );
192
+ const response = await client.BaseService.getAllChains();
193
193
  if (response.error) {
194
194
  return {
195
195
  valid: false,
@@ -242,6 +242,14 @@ function infoBox(title, message) {
242
242
  borderStyle: "round"
243
243
  });
244
244
  }
245
+ function warningBox(title, message) {
246
+ return boxen(message, {
247
+ title: chalk.hex("#FFE66D").bold(title),
248
+ borderColor: "yellow",
249
+ padding: 1,
250
+ borderStyle: "round"
251
+ });
252
+ }
245
253
 
246
254
  // src/ui/spinner.ts
247
255
  import ora from "ora";
@@ -299,14 +307,34 @@ var authCommand = new Command("auth").description("Set up your GoldRush API key"
299
307
  const result = await validateApiKey(apiKey.trim());
300
308
  if (result.valid) {
301
309
  await setApiKey(apiKey.trim());
310
+ const keychainAvailable = await isKeychainAvailable();
302
311
  spinner.succeed("API key validated and stored securely");
303
- console.log(
304
- successBox(
305
- "Authentication Complete",
306
- `API key stored in OS keychain
312
+ if (keychainAvailable) {
313
+ console.log(
314
+ successBox(
315
+ "Authentication Complete",
316
+ `API key stored in OS keychain
307
317
  Key: ${colors.gold(apiKey.trim().slice(0, 6) + "..." + apiKey.trim().slice(-4))}`
308
- )
309
- );
318
+ )
319
+ );
320
+ } else {
321
+ console.log(
322
+ successBox(
323
+ "Authentication Complete",
324
+ `Key: ${colors.gold(apiKey.trim().slice(0, 6) + "..." + apiKey.trim().slice(-4))}`
325
+ )
326
+ );
327
+ console.log(
328
+ warningBox(
329
+ "Plaintext Storage Warning",
330
+ `The OS keychain (keytar) is not available on this system.
331
+ Your API key is stored in plaintext in the local config file.
332
+
333
+ To use secure storage, install the keytar native module or set
334
+ the ${colors.accent("GOLDRUSH_API_KEY")} environment variable instead.`
335
+ )
336
+ );
337
+ }
310
338
  } else {
311
339
  spinner.fail("Invalid API key");
312
340
  console.log(
@@ -4961,9 +4989,15 @@ var gasCommand = new Command5("gas").description("Get real-time gas price estima
4961
4989
  if (!eventType) {
4962
4990
  console.log(
4963
4991
  errorBox(
4964
- "Invalid Type",
4965
- `Unknown transaction type: ${options.type}
4966
- Valid types: erc20, native, swap`
4992
+ "Invalid Transaction Type",
4993
+ `"${colors.accent(options.type)}" is not a valid transaction type.
4994
+
4995
+ Valid values:
4996
+ ${colors.gold("erc20")} \u2014 ERC-20 token transfer gas estimates
4997
+ ${colors.gold("native")} \u2014 Native token transfer gas estimates
4998
+ ${colors.gold("swap")} \u2014 Uniswap v3 swap gas estimates
4999
+
5000
+ Example: ${colors.accent(`goldrush gas eth-mainnet --type swap`)}`
4967
5001
  )
4968
5002
  );
4969
5003
  return;
@@ -5580,7 +5614,6 @@ async function subscribeToWalletActivity(chainName, walletAddresses, callback) {
5580
5614
  },
5581
5615
  {
5582
5616
  next: (events) => {
5583
- infoBox(JSON.stringify(events, null, 2), "Wallet Activity");
5584
5617
  for (const event of events) {
5585
5618
  const decodedType = parseDecodedType(event.decoded_type);
5586
5619
  const { summary, quoteUsd, tokenSymbol } = buildDetailsSummary(decodedType, event.decoded_details);
@@ -5615,9 +5648,39 @@ async function subscribeToWalletActivity(chainName, walletAddresses, callback) {
5615
5648
  return unsubscribe;
5616
5649
  }
5617
5650
 
5651
+ // src/utils/clipboard.ts
5652
+ import { execSync } from "child_process";
5653
+ function copyToClipboard(text) {
5654
+ const platform = process.platform;
5655
+ try {
5656
+ if (platform === "darwin") {
5657
+ execSync("pbcopy", { input: text });
5658
+ return true;
5659
+ }
5660
+ if (platform === "win32") {
5661
+ execSync(
5662
+ `powershell -NoProfile -Command "$input | Set-Clipboard"`,
5663
+ { input: text }
5664
+ );
5665
+ return true;
5666
+ }
5667
+ if (platform === "linux") {
5668
+ try {
5669
+ execSync("xclip -selection clipboard", { input: text });
5670
+ return true;
5671
+ } catch {
5672
+ execSync("xsel --clipboard --input", { input: text });
5673
+ return true;
5674
+ }
5675
+ }
5676
+ return false;
5677
+ } catch {
5678
+ return false;
5679
+ }
5680
+ }
5681
+
5618
5682
  // src/commands/newpairs.ts
5619
5683
  import { Command as Command9 } from "commander";
5620
- import { execSync } from "child_process";
5621
5684
  var newpairsCommand = new Command9("new_pairs").description("Fetch the latest new DEX liquidity pairs").argument("[chain]", "Blockchain name (e.g. eth-mainnet)").argument(
5622
5685
  "[protocols...]",
5623
5686
  "Filter by DEX protocol(s) (e.g. uniswap-v2 raydium-amm)"
@@ -5715,14 +5778,6 @@ Supported protocols:
5715
5778
  function getPairsArray() {
5716
5779
  return [...pairMap.values()].reverse().slice(0, limit);
5717
5780
  }
5718
- function copyToClipboard(text) {
5719
- try {
5720
- execSync("pbcopy", { input: text });
5721
- return true;
5722
- } catch {
5723
- return false;
5724
- }
5725
- }
5726
5781
  function rerender() {
5727
5782
  const pairs = getPairsArray();
5728
5783
  if (selectedRow >= pairs.length) {
@@ -6184,12 +6239,19 @@ var asciiArt = [
6184
6239
  " \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Terminal-first blockchain data & trading \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
6185
6240
  " \u2550\u2550\u2550\u2550\u2550\u2550 powered by Covalent \u2550\u2550\u2550\u2550\u2550\u2550"
6186
6241
  ];
6187
- function printLogo() {
6242
+ function printLogo(version) {
6188
6243
  console.log();
6189
6244
  for (let i = 0; i < asciiArt.length; i++) {
6190
6245
  const colorIdx = Math.min(i, gradientStops.length - 1);
6191
6246
  console.log(chalk5.hex(gradientStops[colorIdx])(asciiArt[i]));
6192
6247
  }
6248
+ if (version) {
6249
+ console.log(
6250
+ chalk5.hex(gradientStops[gradientStops.length - 1])(
6251
+ ` v${version}`
6252
+ )
6253
+ );
6254
+ }
6193
6255
  console.log();
6194
6256
  }
6195
6257
 
@@ -6413,8 +6475,11 @@ async function startProxyServer(port) {
6413
6475
  );
6414
6476
  process.exit(1);
6415
6477
  }
6416
- const sessionToken = crypto.randomBytes(32).toString("hex");
6417
- await setSessionToken(sessionToken);
6478
+ let sessionToken = await getSessionToken();
6479
+ if (!sessionToken) {
6480
+ sessionToken = crypto.randomBytes(32).toString("hex");
6481
+ await setSessionToken(sessionToken);
6482
+ }
6418
6483
  const app = express();
6419
6484
  app.use(express.json());
6420
6485
  app.use(bodySizeLimit);
@@ -6424,7 +6489,7 @@ async function startProxyServer(port) {
6424
6489
  app.get("/tools", toolsHandler);
6425
6490
  app.post("/call", callHandler);
6426
6491
  const server = app.listen(serverPort, () => {
6427
- printLogo();
6492
+ printLogo(package_default.version);
6428
6493
  console.log(
6429
6494
  successBox(
6430
6495
  "Proxy Server Running",
@@ -6461,11 +6526,19 @@ var proxyCommand = new Command11("proxy").description("Launch the GoldRush proxy
6461
6526
  });
6462
6527
 
6463
6528
  // src/api/search.ts
6529
+ var SearchTimeoutError = class extends Error {
6530
+ constructor() {
6531
+ super(
6532
+ "Search request timed out. The streaming service did not respond within 15 seconds."
6533
+ );
6534
+ this.name = "SearchTimeoutError";
6535
+ }
6536
+ };
6464
6537
  var SEARCH_TOKEN_QUERY = `
6465
6538
  query SearchToken($query: String!) {
6466
6539
  searchToken(query: $query) {
6467
6540
  pair_address
6468
- chain
6541
+ chain_name
6469
6542
  quote_rate
6470
6543
  quote_rate_usd
6471
6544
  volume
@@ -6493,11 +6566,11 @@ async function searchTokens(query) {
6493
6566
  { query },
6494
6567
  {
6495
6568
  next: (data) => {
6496
- const items = data?.searchToken ?? data ?? [];
6569
+ const items = data?.data?.searchToken ?? data?.searchToken ?? data;
6497
6570
  const list = Array.isArray(items) ? items : [];
6498
6571
  results = list.map((item) => ({
6499
6572
  pairAddress: item.pair_address ?? "",
6500
- chain: item.chain ?? "",
6573
+ chain: item.chain_name ?? item.chain ?? "",
6501
6574
  quoteRate: item.quote_rate ?? 0,
6502
6575
  quoteRateUsd: item.quote_rate_usd ?? 0,
6503
6576
  volume: item.volume ?? 0,
@@ -6524,7 +6597,13 @@ async function searchTokens(query) {
6524
6597
  }
6525
6598
  }
6526
6599
  );
6527
- setTimeout(() => resolve2(results), 15e3);
6600
+ setTimeout(() => {
6601
+ if (results.length === 0) {
6602
+ reject(new SearchTimeoutError());
6603
+ } else {
6604
+ resolve2(results);
6605
+ }
6606
+ }, 15e3);
6528
6607
  });
6529
6608
  }
6530
6609
 
@@ -6573,8 +6652,21 @@ Results: ${colors.gold(String(results.length))} (sorted by volume)`
6573
6652
  );
6574
6653
  renderSearchResultsTable(results);
6575
6654
  } catch (err) {
6576
- spinner.fail("Search failed");
6577
- console.log(errorBox("Error", err.message));
6655
+ if (err instanceof SearchTimeoutError) {
6656
+ spinner.warn("Request timed out");
6657
+ console.log(
6658
+ warningBox(
6659
+ "Search Timed Out",
6660
+ `No response from the streaming service within 15 seconds.
6661
+
6662
+ Try again or check your connection.
6663
+ Run ${colors.accent("goldrush status")} to verify your API key.`
6664
+ )
6665
+ );
6666
+ } else {
6667
+ spinner.fail("Search failed");
6668
+ console.log(errorBox("Error", err.message));
6669
+ }
6578
6670
  }
6579
6671
  });
6580
6672
 
@@ -6632,6 +6724,14 @@ Proxy Port: ${colors.accent(String(port))}`
6632
6724
  });
6633
6725
 
6634
6726
  // src/api/traders.ts
6727
+ var TradersTimeoutError = class extends Error {
6728
+ constructor() {
6729
+ super(
6730
+ "Traders request timed out. The streaming service did not respond within 15 seconds."
6731
+ );
6732
+ this.name = "TradersTimeoutError";
6733
+ }
6734
+ };
6635
6735
  var UPNL_FOR_TOKEN_QUERY = `
6636
6736
  query UpnlForToken($chain_name: ChainName!, $token_address: String!) {
6637
6737
  upnlForToken(chain_name: $chain_name, token_address: $token_address) {
@@ -6691,7 +6791,13 @@ async function getTopTraders(chainName, tokenAddress) {
6691
6791
  }
6692
6792
  }
6693
6793
  );
6694
- setTimeout(() => resolve2(results), 15e3);
6794
+ setTimeout(() => {
6795
+ if (results.length === 0) {
6796
+ reject(new TradersTimeoutError());
6797
+ } else {
6798
+ resolve2(results);
6799
+ }
6800
+ }, 15e3);
6695
6801
  });
6696
6802
  }
6697
6803
 
@@ -6758,8 +6864,21 @@ Traders: ${colors.gold(String(traders.length))}`
6758
6864
  );
6759
6865
  renderTopTradersTable(traders);
6760
6866
  } catch (err) {
6761
- spinner.fail("Failed to fetch top traders");
6762
- console.log(errorBox("Error", err.message));
6867
+ if (err instanceof TradersTimeoutError) {
6868
+ spinner.warn("Request timed out");
6869
+ console.log(
6870
+ warningBox(
6871
+ "Traders Query Timed Out",
6872
+ `No response from the streaming service within 15 seconds.
6873
+
6874
+ Make sure the token address is on a supported streaming chain.
6875
+ Run ${colors.accent("goldrush status")} to verify your API key.`
6876
+ )
6877
+ );
6878
+ } else {
6879
+ spinner.fail("Failed to fetch top traders");
6880
+ console.log(errorBox("Error", err.message));
6881
+ }
6763
6882
  }
6764
6883
  }
6765
6884
  );
@@ -6861,7 +6980,6 @@ Showing: ${colors.gold(String(transfers.length))} most recent`
6861
6980
  // src/commands/watch.ts
6862
6981
  import { input as input7 } from "@inquirer/prompts";
6863
6982
  import { Command as Command16 } from "commander";
6864
- import { execSync as execSync2 } from "child_process";
6865
6983
  var watchCommand = new Command16("watch").description("Stream live wallet activity (transactions, transfers, swaps)").argument("[address]", "Wallet address to watch").argument("[chain]", "Blockchain name (e.g. eth-mainnet)").option("-l, --limit <number>", "Max rows to display", "20").action(
6866
6984
  async (address, chain, opts) => {
6867
6985
  if (!await isAuthenticated()) {
@@ -6876,8 +6994,17 @@ var watchCommand = new Command16("watch").description("Stream live wallet activi
6876
6994
  let walletAddress = address;
6877
6995
  if (!walletAddress) {
6878
6996
  walletAddress = await input7({
6879
- message: colors.primary("Wallet address to watch:"),
6880
- validate: (val) => /^0x[a-fA-F0-9]{40}$/.test(val.trim()) ? true : "Enter a valid EVM address (0x...)"
6997
+ message: colors.primary(
6998
+ "Wallet address or ENS name to watch:"
6999
+ ),
7000
+ validate: (val) => {
7001
+ const v = val.trim();
7002
+ if (!v) return "Please enter an address or ENS name.";
7003
+ if (/^0x[a-fA-F0-9]{40}$/.test(v) || /^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.eth$/.test(v)) {
7004
+ return true;
7005
+ }
7006
+ return "Enter a valid EVM address (0x\u202640 hex chars) or ENS name (e.g. vitalik.eth).";
7007
+ }
6881
7008
  });
6882
7009
  walletAddress = walletAddress.trim();
6883
7010
  }
@@ -6942,14 +7069,6 @@ Run ${colors.accent("goldrush chains")} for details.`
6942
7069
  function getDisplayActivities() {
6943
7070
  return activities.slice(0, limit);
6944
7071
  }
6945
- function copyToClipboard(text) {
6946
- try {
6947
- execSync2("pbcopy", { input: text });
6948
- return true;
6949
- } catch {
6950
- return false;
6951
- }
6952
- }
6953
7072
  function rerender() {
6954
7073
  const display = getDisplayActivities();
6955
7074
  if (selectedRow >= display.length) {
@@ -7121,7 +7240,7 @@ program.addCommand(mcpCommand);
7121
7240
  program.addCommand(chainsCommand);
7122
7241
  program.addCommand(watchCommand);
7123
7242
  program.action(async () => {
7124
- printLogo();
7243
+ printLogo(package_default.version);
7125
7244
  const action = await select3({
7126
7245
  message: colors.primary("What would you like to do?"),
7127
7246
  choices: [