nansen-cli 1.4.0 → 1.5.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 +4 -4
- package/src/api.js +15 -8
- package/src/cli.js +17 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nansen-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Command-line interface for Nansen API - designed for AI agents",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "git+https://github.com/
|
|
37
|
+
"url": "git+https://github.com/nansen-ai/nansen-cli.git"
|
|
38
38
|
},
|
|
39
39
|
"bugs": {
|
|
40
|
-
"url": "https://github.com/
|
|
40
|
+
"url": "https://github.com/nansen-ai/nansen-cli/issues"
|
|
41
41
|
},
|
|
42
|
-
"homepage": "https://github.com/
|
|
42
|
+
"homepage": "https://github.com/nansen-ai/nansen-cli#readme",
|
|
43
43
|
"engines": {
|
|
44
44
|
"node": ">=18.0.0"
|
|
45
45
|
},
|
package/src/api.js
CHANGED
|
@@ -19,6 +19,7 @@ export const ErrorCode = {
|
|
|
19
19
|
UNAUTHORIZED: 'UNAUTHORIZED', // 401 - Invalid or missing API key
|
|
20
20
|
FORBIDDEN: 'FORBIDDEN', // 403 - Valid key but insufficient permissions
|
|
21
21
|
CREDITS_EXHAUSTED: 'CREDITS_EXHAUSTED', // 403 - Insufficient API credits
|
|
22
|
+
PAYMENT_REQUIRED: 'PAYMENT_REQUIRED', // 402 - x402 payment required
|
|
22
23
|
|
|
23
24
|
// Rate Limiting
|
|
24
25
|
RATE_LIMITED: 'RATE_LIMITED', // 429 - Too many requests
|
|
@@ -87,6 +88,8 @@ function statusToErrorCode(status, data = {}) {
|
|
|
87
88
|
return ErrorCode.INVALID_PARAMS;
|
|
88
89
|
case 401:
|
|
89
90
|
return ErrorCode.UNAUTHORIZED;
|
|
91
|
+
case 402:
|
|
92
|
+
return ErrorCode.PAYMENT_REQUIRED;
|
|
90
93
|
case 403:
|
|
91
94
|
if (messageLower.includes('credit') || messageLower.includes('insufficient')) return ErrorCode.CREDITS_EXHAUSTED;
|
|
92
95
|
return ErrorCode.FORBIDDEN;
|
|
@@ -382,13 +385,7 @@ function parseRetryAfter(headerValue) {
|
|
|
382
385
|
|
|
383
386
|
export class NansenAPI {
|
|
384
387
|
constructor(apiKey = config.apiKey, baseUrl = config.baseUrl, options = {}) {
|
|
385
|
-
|
|
386
|
-
throw new NansenError(
|
|
387
|
-
'API key required. Run `nansen login` or set NANSEN_API_KEY environment variable.',
|
|
388
|
-
ErrorCode.UNAUTHORIZED
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
this.apiKey = apiKey;
|
|
388
|
+
this.apiKey = apiKey || null;
|
|
392
389
|
this.baseUrl = baseUrl;
|
|
393
390
|
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...options.retry };
|
|
394
391
|
this.cacheOptions = {
|
|
@@ -431,7 +428,7 @@ export class NansenAPI {
|
|
|
431
428
|
method: 'POST',
|
|
432
429
|
headers: {
|
|
433
430
|
'Content-Type': 'application/json',
|
|
434
|
-
'apikey': this.apiKey,
|
|
431
|
+
...(this.apiKey ? { 'apikey': this.apiKey } : {}),
|
|
435
432
|
...options.headers
|
|
436
433
|
},
|
|
437
434
|
body: JSON.stringify(NansenAPI.cleanBody(body))
|
|
@@ -484,6 +481,16 @@ export class NansenAPI {
|
|
|
484
481
|
message = message.replace(/\.+$/, '') + '. This filter is not supported for this token/chain combination. Do not retry.';
|
|
485
482
|
} else if (code === ErrorCode.CREDITS_EXHAUSTED) {
|
|
486
483
|
message = message.replace(/\.+$/, '') + '. No retry will help. Check your Nansen dashboard for credit balance.';
|
|
484
|
+
} else if (code === ErrorCode.PAYMENT_REQUIRED) {
|
|
485
|
+
message = 'Payment required (x402). This endpoint requires on-chain payment.';
|
|
486
|
+
const paymentHeader = response.headers.get('payment-required');
|
|
487
|
+
if (paymentHeader) {
|
|
488
|
+
try {
|
|
489
|
+
data.paymentRequirements = JSON.parse(atob(paymentHeader));
|
|
490
|
+
} catch {
|
|
491
|
+
data.paymentRequiredRaw = paymentHeader;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
487
494
|
}
|
|
488
495
|
|
|
489
496
|
lastError = new NansenError(message, code, response.status, {
|
package/src/cli.js
CHANGED
|
@@ -1181,16 +1181,26 @@ export function buildCommands(deps = {}) {
|
|
|
1181
1181
|
const handlers = {
|
|
1182
1182
|
'info': () => apiInstance.tokenInformation({ tokenAddress, chain, timeframe }),
|
|
1183
1183
|
'screener': async () => {
|
|
1184
|
-
const result = await apiInstance.tokenScreener({ chains, timeframe, filters, orderBy, pagination });
|
|
1185
|
-
// Client-side search filter (API doesn't support server-side search)
|
|
1186
1184
|
const search = options.search;
|
|
1187
|
-
|
|
1185
|
+
// When searching, fetch more results to filter from (API has no server-side search)
|
|
1186
|
+
const searchPagination = search
|
|
1187
|
+
? { page: 1, per_page: Math.max(500, pagination?.per_page || 0) }
|
|
1188
|
+
: pagination;
|
|
1189
|
+
const result = await apiInstance.tokenScreener({ chains, timeframe, filters, orderBy, pagination: searchPagination });
|
|
1190
|
+
if (search) {
|
|
1188
1191
|
const q = search.toLowerCase();
|
|
1189
|
-
const
|
|
1192
|
+
const requestedLimit = pagination?.per_page || 100;
|
|
1193
|
+
const filterArr = (arr) => arr.filter(t =>
|
|
1190
1194
|
(t.token_symbol && t.token_symbol.toLowerCase().includes(q)) ||
|
|
1191
|
-
(t.token_name && t.token_name.toLowerCase().includes(q))
|
|
1192
|
-
|
|
1193
|
-
|
|
1195
|
+
(t.token_name && t.token_name.toLowerCase().includes(q)) ||
|
|
1196
|
+
(t.token_address && t.token_address.toLowerCase() === q)
|
|
1197
|
+
).slice(0, requestedLimit);
|
|
1198
|
+
// Handle nested response shapes: {data: [...]} or {data: {data: [...]}}
|
|
1199
|
+
if (Array.isArray(result?.data)) {
|
|
1200
|
+
return { ...result, data: filterArr(result.data) };
|
|
1201
|
+
} else if (result?.data?.data && Array.isArray(result.data.data)) {
|
|
1202
|
+
return { ...result, data: { ...result.data, data: filterArr(result.data.data) } };
|
|
1203
|
+
}
|
|
1194
1204
|
}
|
|
1195
1205
|
return result;
|
|
1196
1206
|
},
|