nansen-cli 1.0.0 → 1.0.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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +99 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nansen-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Command-line interface for Nansen API - designed for AI agents",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -23,7 +23,7 @@ function parseArgs(args) {
23
23
  const key = arg.slice(2);
24
24
  const next = args[i + 1];
25
25
 
26
- if (key === 'pretty' || key === 'help') {
26
+ if (key === 'pretty' || key === 'help' || key === 'table') {
27
27
  result.flags[key] = true;
28
28
  } else if (next && !next.startsWith('-')) {
29
29
  // Try to parse as JSON first
@@ -46,9 +46,98 @@ function parseArgs(args) {
46
46
  return result;
47
47
  }
48
48
 
49
+ // Table formatter for human-readable output
50
+ function formatTable(data) {
51
+ // Extract array of records from various response shapes
52
+ let records = [];
53
+ if (Array.isArray(data)) {
54
+ records = data;
55
+ } else if (data?.data && Array.isArray(data.data)) {
56
+ records = data.data;
57
+ } else if (data?.results && Array.isArray(data.results)) {
58
+ records = data.results;
59
+ } else if (data?.data?.results && Array.isArray(data.data.results)) {
60
+ records = data.data.results;
61
+ } else if (typeof data === 'object' && data !== null) {
62
+ // Single object - convert to array
63
+ records = [data];
64
+ }
65
+
66
+ if (records.length === 0) {
67
+ return 'No data';
68
+ }
69
+
70
+ // Get columns from first record, prioritize common useful fields
71
+ const priorityFields = ['token_symbol', 'token_name', 'symbol', 'name', 'address', 'label', 'chain', 'value_usd', 'amount', 'pnl_usd', 'price_usd', 'volume_usd', 'net_flow_usd', 'timestamp', 'block_timestamp'];
72
+ const allKeys = [...new Set(records.flatMap(r => Object.keys(r)))];
73
+
74
+ // Sort: priority fields first, then alphabetically
75
+ const columns = allKeys.sort((a, b) => {
76
+ const aIdx = priorityFields.indexOf(a);
77
+ const bIdx = priorityFields.indexOf(b);
78
+ if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx;
79
+ if (aIdx !== -1) return -1;
80
+ if (bIdx !== -1) return 1;
81
+ return a.localeCompare(b);
82
+ }).slice(0, 8); // Limit to 8 columns for readability
83
+
84
+ // Calculate column widths
85
+ const widths = columns.map(col => {
86
+ const headerLen = col.length;
87
+ const maxDataLen = Math.max(...records.map(r => {
88
+ const val = formatValue(r[col]);
89
+ return val.length;
90
+ }));
91
+ return Math.min(Math.max(headerLen, maxDataLen), 30); // Cap at 30 chars
92
+ });
93
+
94
+ // Build table
95
+ const separator = '─';
96
+ const lines = [];
97
+
98
+ // Header
99
+ const header = columns.map((col, i) => col.padEnd(widths[i])).join(' │ ');
100
+ lines.push(header);
101
+ lines.push(widths.map(w => separator.repeat(w)).join('─┼─'));
102
+
103
+ // Rows
104
+ for (const record of records.slice(0, 50)) { // Limit to 50 rows
105
+ const row = columns.map((col, i) => {
106
+ const val = formatValue(record[col]);
107
+ return val.slice(0, widths[i]).padEnd(widths[i]);
108
+ }).join(' │ ');
109
+ lines.push(row);
110
+ }
111
+
112
+ if (records.length > 50) {
113
+ lines.push(`... and ${records.length - 50} more rows`);
114
+ }
115
+
116
+ return lines.join('\n');
117
+ }
118
+
119
+ function formatValue(val) {
120
+ if (val === null || val === undefined) return '';
121
+ if (typeof val === 'number') {
122
+ if (Math.abs(val) >= 1000000) return (val / 1000000).toFixed(2) + 'M';
123
+ if (Math.abs(val) >= 1000) return (val / 1000).toFixed(2) + 'K';
124
+ if (Number.isInteger(val)) return val.toString();
125
+ return val.toFixed(2);
126
+ }
127
+ if (typeof val === 'object') return JSON.stringify(val);
128
+ return String(val);
129
+ }
130
+
49
131
  // Output helper
50
- function output(data, pretty = false) {
51
- if (pretty) {
132
+ function output(data, pretty = false, table = false) {
133
+ if (table) {
134
+ if (data.success === false) {
135
+ console.error(`Error: ${data.error}`);
136
+ } else {
137
+ const tableData = data.data || data;
138
+ console.log(formatTable(tableData));
139
+ }
140
+ } else if (pretty) {
52
141
  console.log(JSON.stringify(data, null, 2));
53
142
  } else {
54
143
  console.log(JSON.stringify(data));
@@ -56,14 +145,14 @@ function output(data, pretty = false) {
56
145
  }
57
146
 
58
147
  // Error output
59
- function errorOutput(error, pretty = false) {
148
+ function errorOutput(error, pretty = false, table = false) {
60
149
  const errorData = {
61
150
  success: false,
62
151
  error: error.message,
63
152
  status: error.status,
64
153
  details: error.data
65
154
  };
66
- output(errorData, pretty);
155
+ output(errorData, pretty, table);
67
156
  process.exit(1);
68
157
  }
69
158
 
@@ -86,6 +175,7 @@ COMMANDS:
86
175
 
87
176
  GLOBAL OPTIONS:
88
177
  --pretty Format JSON output for readability
178
+ --table Format output as human-readable table
89
179
  --chain Blockchain to query (ethereum, solana, base, etc.)
90
180
  --chains Multiple chains as JSON array
91
181
  --limit Number of results (shorthand for pagination)
@@ -370,6 +460,7 @@ async function main() {
370
460
  const command = positional[0] || 'help';
371
461
  const subArgs = positional.slice(1);
372
462
  const pretty = flags.pretty || flags.p;
463
+ const table = flags.table || flags.t;
373
464
 
374
465
  if (command === 'help' || flags.help || flags.h) {
375
466
  console.log(HELP);
@@ -380,7 +471,7 @@ async function main() {
380
471
  output({
381
472
  error: `Unknown command: ${command}`,
382
473
  available: Object.keys(commands)
383
- }, pretty);
474
+ }, pretty, table);
384
475
  process.exit(1);
385
476
  }
386
477
 
@@ -395,9 +486,9 @@ async function main() {
395
486
  try {
396
487
  const api = new NansenAPI();
397
488
  const result = await commands[command](subArgs, api, flags, options);
398
- output({ success: true, data: result }, pretty);
489
+ output({ success: true, data: result }, pretty, table);
399
490
  } catch (error) {
400
- errorOutput(error, pretty);
491
+ errorOutput(error, pretty, table);
401
492
  }
402
493
  }
403
494