@covalenthq/goldrush-cli 3.0.0 → 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/README.md CHANGED
@@ -15,7 +15,7 @@ goldrush auth
15
15
  goldrush balances eth-mainnet 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
16
16
 
17
17
  # Stream new DEX pairs
18
- goldrush newpairs eth-mainnet
18
+ goldrush new_pairs eth-mainnet
19
19
 
20
20
  # Launch proxy server (for MCP bridge)
21
21
  goldrush proxy
@@ -32,7 +32,7 @@ goldrush install
32
32
  | `goldrush auth` | Set up API key (OS keychain) |
33
33
  | `goldrush balances <chain> <address>` | Token balances with rich table |
34
34
  | `goldrush transfers <chain> <address>` | Transfer history |
35
- | `goldrush newpairs <chain>` | Stream new DEX pairs |
35
+ | `goldrush new_pairs <chain>` | Stream new DEX pairs |
36
36
  | `goldrush ohlcv <chain>` | OHLCV pairs stream |
37
37
  | `goldrush search <query>` | Token search |
38
38
  | `goldrush traders <chain>` | Top traders |
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.0",
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,13 +189,19 @@ 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
- );
193
- return !response.error;
194
- } catch {
195
- return false;
192
+ const response = await client.BaseService.getAllChains();
193
+ if (response.error) {
194
+ return {
195
+ valid: false,
196
+ error_message: response.error_message
197
+ };
198
+ }
199
+ return { valid: true };
200
+ } catch (error) {
201
+ return {
202
+ valid: false,
203
+ error_message: error?.message || "The API key could not be validated.\nGet a key at https://goldrush.dev/platform/apikey/"
204
+ };
196
205
  }
197
206
  }
198
207
  async function getMaskedApiKey() {
@@ -233,6 +242,14 @@ function infoBox(title, message) {
233
242
  borderStyle: "round"
234
243
  });
235
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
+ }
236
253
 
237
254
  // src/ui/spinner.ts
238
255
  import ora from "ora";
@@ -287,23 +304,43 @@ var authCommand = new Command("auth").description("Set up your GoldRush API key"
287
304
  }
288
305
  const spinner = createSpinner("Validating API key...");
289
306
  spinner.start();
290
- const isValid = await validateApiKey(apiKey.trim());
291
- if (isValid) {
307
+ const result = await validateApiKey(apiKey.trim());
308
+ if (result.valid) {
292
309
  await setApiKey(apiKey.trim());
310
+ const keychainAvailable = await isKeychainAvailable();
293
311
  spinner.succeed("API key validated and stored securely");
294
- console.log(
295
- successBox(
296
- "Authentication Complete",
297
- `API key stored in OS keychain
312
+ if (keychainAvailable) {
313
+ console.log(
314
+ successBox(
315
+ "Authentication Complete",
316
+ `API key stored in OS keychain
298
317
  Key: ${colors.gold(apiKey.trim().slice(0, 6) + "..." + apiKey.trim().slice(-4))}`
299
- )
300
- );
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
+ }
301
338
  } else {
302
339
  spinner.fail("Invalid API key");
303
340
  console.log(
304
341
  errorBox(
305
342
  "Authentication Failed",
306
- "The API key could not be validated.\nGet a key at https://goldrush.dev/platform/apikey/"
343
+ result.error_message || "The API key could not be validated.\nGet a key at https://goldrush.dev/platform/apikey/"
307
344
  )
308
345
  );
309
346
  }
@@ -4816,7 +4853,7 @@ var configCommand = new Command4("config").description("View or update GoldRush
4816
4853
  console.log(
4817
4854
  infoBox(
4818
4855
  "Current Settings",
4819
- `Port: ${colors.gold(String(config2.port))}
4856
+ `Proxy Port: ${colors.gold(String(config2.port))}
4820
4857
  Default Chain: ${colors.accent(config2.defaultChain)}
4821
4858
  Quote Currency: ${colors.gold(config2.quoteCurrency)}`
4822
4859
  )
@@ -4824,7 +4861,7 @@ Quote Currency: ${colors.gold(config2.quoteCurrency)}`
4824
4861
  const action = await select({
4825
4862
  message: "What would you like to update?",
4826
4863
  choices: [
4827
- { name: `Port (${config2.port})`, value: "port" },
4864
+ { name: `Proxy Port (${config2.port})`, value: "port" },
4828
4865
  {
4829
4866
  name: `Default Chain (${config2.defaultChain})`,
4830
4867
  value: "chain"
@@ -4952,9 +4989,15 @@ var gasCommand = new Command5("gas").description("Get real-time gas price estima
4952
4989
  if (!eventType) {
4953
4990
  console.log(
4954
4991
  errorBox(
4955
- "Invalid Type",
4956
- `Unknown transaction type: ${options.type}
4957
- 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`)}`
4958
5001
  )
4959
5002
  );
4960
5003
  return;
@@ -5571,7 +5614,6 @@ async function subscribeToWalletActivity(chainName, walletAddresses, callback) {
5571
5614
  },
5572
5615
  {
5573
5616
  next: (events) => {
5574
- infoBox(JSON.stringify(events, null, 2), "Wallet Activity");
5575
5617
  for (const event of events) {
5576
5618
  const decodedType = parseDecodedType(event.decoded_type);
5577
5619
  const { summary, quoteUsd, tokenSymbol } = buildDetailsSummary(decodedType, event.decoded_details);
@@ -5606,9 +5648,39 @@ async function subscribeToWalletActivity(chainName, walletAddresses, callback) {
5606
5648
  return unsubscribe;
5607
5649
  }
5608
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
+
5609
5682
  // src/commands/newpairs.ts
5610
5683
  import { Command as Command9 } from "commander";
5611
- import { execSync } from "child_process";
5612
5684
  var newpairsCommand = new Command9("new_pairs").description("Fetch the latest new DEX liquidity pairs").argument("[chain]", "Blockchain name (e.g. eth-mainnet)").argument(
5613
5685
  "[protocols...]",
5614
5686
  "Filter by DEX protocol(s) (e.g. uniswap-v2 raydium-amm)"
@@ -5706,14 +5778,6 @@ Supported protocols:
5706
5778
  function getPairsArray() {
5707
5779
  return [...pairMap.values()].reverse().slice(0, limit);
5708
5780
  }
5709
- function copyToClipboard(text) {
5710
- try {
5711
- execSync("pbcopy", { input: text });
5712
- return true;
5713
- } catch {
5714
- return false;
5715
- }
5716
- }
5717
5781
  function rerender() {
5718
5782
  const pairs = getPairsArray();
5719
5783
  if (selectedRow >= pairs.length) {
@@ -6175,12 +6239,19 @@ var asciiArt = [
6175
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",
6176
6240
  " \u2550\u2550\u2550\u2550\u2550\u2550 powered by Covalent \u2550\u2550\u2550\u2550\u2550\u2550"
6177
6241
  ];
6178
- function printLogo() {
6242
+ function printLogo(version) {
6179
6243
  console.log();
6180
6244
  for (let i = 0; i < asciiArt.length; i++) {
6181
6245
  const colorIdx = Math.min(i, gradientStops.length - 1);
6182
6246
  console.log(chalk5.hex(gradientStops[colorIdx])(asciiArt[i]));
6183
6247
  }
6248
+ if (version) {
6249
+ console.log(
6250
+ chalk5.hex(gradientStops[gradientStops.length - 1])(
6251
+ ` v${version}`
6252
+ )
6253
+ );
6254
+ }
6184
6255
  console.log();
6185
6256
  }
6186
6257
 
@@ -6404,8 +6475,11 @@ async function startProxyServer(port) {
6404
6475
  );
6405
6476
  process.exit(1);
6406
6477
  }
6407
- const sessionToken = crypto.randomBytes(32).toString("hex");
6408
- await setSessionToken(sessionToken);
6478
+ let sessionToken = await getSessionToken();
6479
+ if (!sessionToken) {
6480
+ sessionToken = crypto.randomBytes(32).toString("hex");
6481
+ await setSessionToken(sessionToken);
6482
+ }
6409
6483
  const app = express();
6410
6484
  app.use(express.json());
6411
6485
  app.use(bodySizeLimit);
@@ -6415,7 +6489,7 @@ async function startProxyServer(port) {
6415
6489
  app.get("/tools", toolsHandler);
6416
6490
  app.post("/call", callHandler);
6417
6491
  const server = app.listen(serverPort, () => {
6418
- printLogo();
6492
+ printLogo(package_default.version);
6419
6493
  console.log(
6420
6494
  successBox(
6421
6495
  "Proxy Server Running",
@@ -6452,11 +6526,19 @@ var proxyCommand = new Command11("proxy").description("Launch the GoldRush proxy
6452
6526
  });
6453
6527
 
6454
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
+ };
6455
6537
  var SEARCH_TOKEN_QUERY = `
6456
6538
  query SearchToken($query: String!) {
6457
6539
  searchToken(query: $query) {
6458
6540
  pair_address
6459
- chain
6541
+ chain_name
6460
6542
  quote_rate
6461
6543
  quote_rate_usd
6462
6544
  volume
@@ -6484,11 +6566,11 @@ async function searchTokens(query) {
6484
6566
  { query },
6485
6567
  {
6486
6568
  next: (data) => {
6487
- const items = data?.searchToken ?? data ?? [];
6569
+ const items = data?.data?.searchToken ?? data?.searchToken ?? data;
6488
6570
  const list = Array.isArray(items) ? items : [];
6489
6571
  results = list.map((item) => ({
6490
6572
  pairAddress: item.pair_address ?? "",
6491
- chain: item.chain ?? "",
6573
+ chain: item.chain_name ?? item.chain ?? "",
6492
6574
  quoteRate: item.quote_rate ?? 0,
6493
6575
  quoteRateUsd: item.quote_rate_usd ?? 0,
6494
6576
  volume: item.volume ?? 0,
@@ -6515,7 +6597,13 @@ async function searchTokens(query) {
6515
6597
  }
6516
6598
  }
6517
6599
  );
6518
- setTimeout(() => resolve2(results), 15e3);
6600
+ setTimeout(() => {
6601
+ if (results.length === 0) {
6602
+ reject(new SearchTimeoutError());
6603
+ } else {
6604
+ resolve2(results);
6605
+ }
6606
+ }, 15e3);
6519
6607
  });
6520
6608
  }
6521
6609
 
@@ -6564,8 +6652,21 @@ Results: ${colors.gold(String(results.length))} (sorted by volume)`
6564
6652
  );
6565
6653
  renderSearchResultsTable(results);
6566
6654
  } catch (err) {
6567
- spinner.fail("Search failed");
6568
- 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
+ }
6569
6670
  }
6570
6671
  });
6571
6672
 
@@ -6592,11 +6693,13 @@ Proxy Port: ${colors.accent(String(port))}`
6592
6693
  } else {
6593
6694
  const spinner = createSpinner("Validating API key...");
6594
6695
  spinner.start();
6595
- const valid = await validateApiKey(apiKey);
6596
- if (valid) {
6696
+ const result = await validateApiKey(apiKey);
6697
+ if (result.valid) {
6597
6698
  spinner.succeed("API key is valid");
6598
6699
  } else {
6599
- spinner.fail("API key is invalid or expired");
6700
+ spinner.fail(
6701
+ `API key is invalid or expired${result.error_message ? `: ${result.error_message}` : ""}`
6702
+ );
6600
6703
  }
6601
6704
  }
6602
6705
  const proxySpinner = createSpinner(
@@ -6615,12 +6718,20 @@ Proxy Port: ${colors.accent(String(port))}`
6615
6718
  }
6616
6719
  } catch {
6617
6720
  proxySpinner.warn(
6618
- `Proxy server not reachable on port ${port}. Run \`goldrush start\` to launch it.`
6721
+ `Proxy server not reachable on port ${port}. Run \`goldrush proxy\` to launch it.`
6619
6722
  );
6620
6723
  }
6621
6724
  });
6622
6725
 
6623
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
+ };
6624
6735
  var UPNL_FOR_TOKEN_QUERY = `
6625
6736
  query UpnlForToken($chain_name: ChainName!, $token_address: String!) {
6626
6737
  upnlForToken(chain_name: $chain_name, token_address: $token_address) {
@@ -6680,7 +6791,13 @@ async function getTopTraders(chainName, tokenAddress) {
6680
6791
  }
6681
6792
  }
6682
6793
  );
6683
- setTimeout(() => resolve2(results), 15e3);
6794
+ setTimeout(() => {
6795
+ if (results.length === 0) {
6796
+ reject(new TradersTimeoutError());
6797
+ } else {
6798
+ resolve2(results);
6799
+ }
6800
+ }, 15e3);
6684
6801
  });
6685
6802
  }
6686
6803
 
@@ -6747,8 +6864,21 @@ Traders: ${colors.gold(String(traders.length))}`
6747
6864
  );
6748
6865
  renderTopTradersTable(traders);
6749
6866
  } catch (err) {
6750
- spinner.fail("Failed to fetch top traders");
6751
- 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
+ }
6752
6882
  }
6753
6883
  }
6754
6884
  );
@@ -6850,7 +6980,6 @@ Showing: ${colors.gold(String(transfers.length))} most recent`
6850
6980
  // src/commands/watch.ts
6851
6981
  import { input as input7 } from "@inquirer/prompts";
6852
6982
  import { Command as Command16 } from "commander";
6853
- import { execSync as execSync2 } from "child_process";
6854
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(
6855
6984
  async (address, chain, opts) => {
6856
6985
  if (!await isAuthenticated()) {
@@ -6865,8 +6994,17 @@ var watchCommand = new Command16("watch").description("Stream live wallet activi
6865
6994
  let walletAddress = address;
6866
6995
  if (!walletAddress) {
6867
6996
  walletAddress = await input7({
6868
- message: colors.primary("Wallet address to watch:"),
6869
- 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
+ }
6870
7008
  });
6871
7009
  walletAddress = walletAddress.trim();
6872
7010
  }
@@ -6931,14 +7069,6 @@ Run ${colors.accent("goldrush chains")} for details.`
6931
7069
  function getDisplayActivities() {
6932
7070
  return activities.slice(0, limit);
6933
7071
  }
6934
- function copyToClipboard(text) {
6935
- try {
6936
- execSync2("pbcopy", { input: text });
6937
- return true;
6938
- } catch {
6939
- return false;
6940
- }
6941
- }
6942
7072
  function rerender() {
6943
7073
  const display = getDisplayActivities();
6944
7074
  if (selectedRow >= display.length) {
@@ -7110,7 +7240,7 @@ program.addCommand(mcpCommand);
7110
7240
  program.addCommand(chainsCommand);
7111
7241
  program.addCommand(watchCommand);
7112
7242
  program.action(async () => {
7113
- printLogo();
7243
+ printLogo(package_default.version);
7114
7244
  const action = await select3({
7115
7245
  message: colors.primary("What would you like to do?"),
7116
7246
  choices: [