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.
- package/package.json +1 -1
- package/src/index.js +99 -8
package/package.json
CHANGED
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 (
|
|
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
|
|