nansen-cli 1.17.0 → 1.18.0
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/CHANGELOG.md +29 -0
- package/package.json +2 -1
- package/skills/nansen-alerts/SKILL.md +137 -0
- package/skills/nansen-alpha-discovery/SKILL.md +43 -0
- package/skills/nansen-batch-wallet/SKILL.md +26 -0
- package/skills/nansen-cross-chain-flow/SKILL.md +27 -0
- package/skills/nansen-dca-watch/SKILL.md +38 -0
- package/skills/nansen-defi-exposure/SKILL.md +37 -0
- package/skills/nansen-exit-signal/SKILL.md +39 -0
- package/skills/nansen-fund-watch/SKILL.md +35 -0
- package/skills/nansen-holder-quality/SKILL.md +38 -0
- package/skills/nansen-perp-scan/SKILL.md +32 -0
- package/skills/nansen-perp-trader/SKILL.md +39 -0
- package/skills/nansen-pm-deep-dive/SKILL.md +50 -0
- package/skills/nansen-pm-insider-scan/SKILL.md +62 -0
- package/skills/nansen-polymarket-trader/SKILL.md +43 -0
- package/skills/nansen-portfolio-history/SKILL.md +36 -0
- package/skills/nansen-prediction-market/SKILL.md +47 -0
- package/skills/nansen-profiler/SKILL.md +98 -0
- package/skills/nansen-search/SKILL.md +34 -0
- package/skills/nansen-sm-trend/SKILL.md +30 -0
- package/skills/nansen-smart-money/SKILL.md +71 -0
- package/skills/nansen-token/SKILL.md +90 -0
- package/skills/nansen-token-discovery/SKILL.md +54 -0
- package/skills/nansen-token-forensics/SKILL.md +40 -0
- package/skills/nansen-trade/SKILL.md +100 -0
- package/skills/nansen-wallet/SKILL.md +140 -0
- package/skills/nansen-wallet-analysis/SKILL.md +45 -0
- package/skills/nansen-wallet-attribution/REFERENCE.md +43 -0
- package/skills/nansen-wallet-attribution/SKILL.md +46 -0
- package/skills/nansen-wallet-migration/SKILL.md +183 -0
- package/skills/nansen-web-fetch/SKILL.md +50 -0
- package/skills/nansen-web-search/SKILL.md +39 -0
- package/src/api.js +76 -3
- package/src/cli.js +176 -14
- package/src/schema.json +164 -1
- package/src/telemetry.js +237 -0
- package/src/update-check.js +2 -2
package/src/cli.js
CHANGED
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
import { NansenAPI, NansenError, ErrorCode, saveConfig, deleteConfig, getConfigFile, clearCache, getCacheDir, validateAddress, sleep } from './api.js';
|
|
7
7
|
import { buildWalletCommands } from './wallet.js';
|
|
8
8
|
import { buildTradingCommands } from './trading.js';
|
|
9
|
+
import { formatAlertsTable, buildAlertsCommands } from './commands/alerts.js';
|
|
9
10
|
import { resolveAddress, isEnsName } from './ens.js';
|
|
10
11
|
import fs from 'fs';
|
|
11
12
|
import { getUpdateNotification, getUpgradeNotice, scheduleUpdateCheck } from './update-check.js';
|
|
13
|
+
import { trackCommandSucceeded, trackCommandFailed } from './telemetry.js';
|
|
12
14
|
import { createRequire } from 'module';
|
|
13
15
|
import * as readline from 'readline';
|
|
14
16
|
|
|
@@ -163,19 +165,29 @@ export function parseArgs(args) {
|
|
|
163
165
|
const key = arg.slice(2);
|
|
164
166
|
const next = args[i + 1];
|
|
165
167
|
|
|
166
|
-
if (key === 'pretty' || key === 'help' || key === 'version' || key === 'table' || key === 'no-retry' || key === 'cache' || key === 'no-cache' || key === 'stream' || key === 'enrich' || key === 'full' || key === 'human') {
|
|
168
|
+
if (key === 'pretty' || key === 'help' || key === 'version' || key === 'table' || key === 'no-retry' || key === 'cache' || key === 'no-cache' || key === 'stream' || key === 'enrich' || key === 'full' || key === 'human' || key === 'enabled' || key === 'disabled') {
|
|
167
169
|
result.flags[key] = true;
|
|
168
|
-
} else if (next && !next.startsWith('-')) {
|
|
170
|
+
} else if (next && (!next.startsWith('-') || /^-\d/.test(next))) {
|
|
169
171
|
// Try to parse as JSON first (for objects/arrays/booleans),
|
|
170
172
|
// but keep numeric strings as strings to avoid precision loss
|
|
171
173
|
// and scientific notation for large integers (e.g. 1e+21).
|
|
174
|
+
let parsedValue;
|
|
172
175
|
try {
|
|
173
176
|
const parsed = JSON.parse(next);
|
|
174
|
-
|
|
177
|
+
parsedValue = typeof parsed === 'number' ? next : parsed;
|
|
175
178
|
} catch {
|
|
176
|
-
|
|
179
|
+
parsedValue = next;
|
|
177
180
|
}
|
|
178
181
|
i++;
|
|
182
|
+
// Accumulate repeated options into arrays (supports repeatable flags like --token, --subject)
|
|
183
|
+
if (key in result.options) {
|
|
184
|
+
if (!Array.isArray(result.options[key])) {
|
|
185
|
+
result.options[key] = [result.options[key]];
|
|
186
|
+
}
|
|
187
|
+
result.options[key].push(parsedValue);
|
|
188
|
+
} else {
|
|
189
|
+
result.options[key] = parsedValue;
|
|
190
|
+
}
|
|
179
191
|
} else {
|
|
180
192
|
result.flags[key] = true;
|
|
181
193
|
}
|
|
@@ -226,7 +238,7 @@ export function formatTable(data) {
|
|
|
226
238
|
// Get columns from first record, prioritize common useful fields
|
|
227
239
|
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'];
|
|
228
240
|
const allKeys = [...new Set(records.flatMap(r => Object.keys(r)))];
|
|
229
|
-
|
|
241
|
+
|
|
230
242
|
// Sort: priority fields first, then alphabetically
|
|
231
243
|
const columns = allKeys.sort((a, b) => {
|
|
232
244
|
const aIdx = priorityFields.indexOf(a);
|
|
@@ -250,12 +262,12 @@ export function formatTable(data) {
|
|
|
250
262
|
// Build table
|
|
251
263
|
const separator = '─';
|
|
252
264
|
const lines = [];
|
|
253
|
-
|
|
265
|
+
|
|
254
266
|
// Header
|
|
255
267
|
const header = columns.map((col, i) => col.padEnd(widths[i])).join(' │ ');
|
|
256
268
|
lines.push(header);
|
|
257
269
|
lines.push(widths.map(w => separator.repeat(w)).join('─┼─'));
|
|
258
|
-
|
|
270
|
+
|
|
259
271
|
// Rows
|
|
260
272
|
for (const record of records.slice(0, 50)) { // Limit to 50 rows
|
|
261
273
|
const row = columns.map((col, i) => {
|
|
@@ -670,6 +682,8 @@ COMMANDS:
|
|
|
670
682
|
research smart-money, profiler, token, search, perp, portfolio, points
|
|
671
683
|
trade quote, execute
|
|
672
684
|
wallet create, list, show, export, default, delete, forget-password
|
|
685
|
+
alerts list, create, update, toggle, delete
|
|
686
|
+
web search, fetch
|
|
673
687
|
account Show API key status, plan, and remaining credits
|
|
674
688
|
login Save API key (--api-key <key> or NANSEN_API_KEY env var)
|
|
675
689
|
logout Remove saved API key
|
|
@@ -693,6 +707,8 @@ Labels: Fund, Smart Trader, 30D/90D/180D Smart Trader, Smart HL Perps Trader
|
|
|
693
707
|
|
|
694
708
|
Docs: https://docs.nansen.ai
|
|
695
709
|
Skills: npx skills add nansen-ai/nansen-cli (agent-optimised docs per command group)
|
|
710
|
+
|
|
711
|
+
Telemetry: anonymous usage stats collected. Disable: DO_NOT_TRACK=1
|
|
696
712
|
`;
|
|
697
713
|
|
|
698
714
|
// Helper to prompt for input (exported for mocking)
|
|
@@ -762,6 +778,76 @@ export function buildCommands(deps = {}) {
|
|
|
762
778
|
return apiInstance.getAccount();
|
|
763
779
|
},
|
|
764
780
|
|
|
781
|
+
'web': async (args, apiInstance, flags, options) => {
|
|
782
|
+
const subcommand = args[0] || 'help';
|
|
783
|
+
const subArgs = args.slice(1);
|
|
784
|
+
|
|
785
|
+
const handlers = {
|
|
786
|
+
'search': async () => {
|
|
787
|
+
// Accept queries as positional args or --query (repeated)
|
|
788
|
+
let queries = subArgs.length > 0 ? subArgs : [];
|
|
789
|
+
if (options.query) {
|
|
790
|
+
const fromOption = Array.isArray(options.query) ? options.query : [options.query];
|
|
791
|
+
queries = queries.concat(fromOption);
|
|
792
|
+
}
|
|
793
|
+
queries = queries.filter(q => q.trim());
|
|
794
|
+
if (queries.length === 0) {
|
|
795
|
+
throw new NansenError('At least one query is required. Usage: nansen web search "bitcoin price" --num-results 5', ErrorCode.MISSING_PARAM);
|
|
796
|
+
}
|
|
797
|
+
let numResults;
|
|
798
|
+
if (options['num-results'] !== undefined) {
|
|
799
|
+
const numResultsRaw = parseInt(options['num-results'], 10);
|
|
800
|
+
if (Number.isNaN(numResultsRaw)) {
|
|
801
|
+
// Non-numeric — fall back to API default
|
|
802
|
+
numResults = undefined;
|
|
803
|
+
} else if (numResultsRaw >= 1 && numResultsRaw <= 20) {
|
|
804
|
+
numResults = numResultsRaw;
|
|
805
|
+
} else {
|
|
806
|
+
throw new NansenError('--num-results must be between 1 and 20', ErrorCode.INVALID_PARAMS);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return apiInstance.webSearch({ queries, numResults });
|
|
810
|
+
},
|
|
811
|
+
|
|
812
|
+
'fetch': async () => {
|
|
813
|
+
// Accept URLs as positional args or --url (repeated)
|
|
814
|
+
let urls = subArgs.length > 0 ? subArgs : [];
|
|
815
|
+
if (options.url) {
|
|
816
|
+
const fromOption = Array.isArray(options.url) ? options.url : [options.url];
|
|
817
|
+
urls = urls.concat(fromOption);
|
|
818
|
+
}
|
|
819
|
+
if (urls.length === 0) {
|
|
820
|
+
throw new NansenError('At least one URL is required. Usage: nansen web fetch https://example.com --question "What is this about?"', ErrorCode.MISSING_PARAM);
|
|
821
|
+
}
|
|
822
|
+
for (const u of urls) {
|
|
823
|
+
try { new URL(u); } catch {
|
|
824
|
+
throw new NansenError(`Invalid URL: "${u}". URLs must include a scheme, e.g. https://example.com`, ErrorCode.INVALID_PARAMS);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
if (!options.question || !options.question.trim()) {
|
|
828
|
+
throw new NansenError('--question is required and cannot be blank. Usage: nansen web fetch https://example.com --question "What is this about?"', ErrorCode.MISSING_PARAM);
|
|
829
|
+
}
|
|
830
|
+
return apiInstance.webFetch({ urls, question: options.question });
|
|
831
|
+
},
|
|
832
|
+
|
|
833
|
+
'help': async () => ({
|
|
834
|
+
subcommands: ['search', 'fetch'],
|
|
835
|
+
description: 'Web search and fetch commands',
|
|
836
|
+
examples: [
|
|
837
|
+
'nansen web search "bitcoin price"',
|
|
838
|
+
'nansen web search "solana news" --num-results 5',
|
|
839
|
+
'nansen web fetch https://nansen.ai --question "What does Nansen do?"',
|
|
840
|
+
],
|
|
841
|
+
}),
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
if (!handlers[subcommand]) {
|
|
845
|
+
throw new NansenError(`Unknown web subcommand: ${subcommand}. Available: search, fetch`, ErrorCode.UNKNOWN);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
return handlers[subcommand]();
|
|
849
|
+
},
|
|
850
|
+
|
|
765
851
|
'login': async (args, apiInstance, flags, options) => {
|
|
766
852
|
if (flags.help || flags.h) {
|
|
767
853
|
log('nansen login - Save your Nansen API key\n');
|
|
@@ -811,13 +897,48 @@ export function buildCommands(deps = {}) {
|
|
|
811
897
|
return;
|
|
812
898
|
}
|
|
813
899
|
|
|
900
|
+
// Verify API key before saving
|
|
901
|
+
const NansenAPIClass = _NansenAPIClass;
|
|
902
|
+
const testApi = new NansenAPIClass(apiKey.trim(), undefined, {
|
|
903
|
+
retry: { maxRetries: 2 },
|
|
904
|
+
cache: { enabled: false }
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
let accountInfo;
|
|
908
|
+
try {
|
|
909
|
+
accountInfo = await testApi.getAccount();
|
|
910
|
+
} catch (error) {
|
|
911
|
+
if (error.code === ErrorCode.UNAUTHORIZED) {
|
|
912
|
+
log(JSON.stringify({
|
|
913
|
+
error: 'INVALID_API_KEY',
|
|
914
|
+
message: 'The API key is not valid.',
|
|
915
|
+
resolution: ['Check your key at https://app.nansen.ai/api']
|
|
916
|
+
}));
|
|
917
|
+
} else {
|
|
918
|
+
log(JSON.stringify({
|
|
919
|
+
error: 'VERIFICATION_FAILED',
|
|
920
|
+
message: `Could not verify API key: ${error.message}`,
|
|
921
|
+
resolution: ['Check your internet connection', 'Try again']
|
|
922
|
+
}));
|
|
923
|
+
}
|
|
924
|
+
exit(1);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Key is valid - now save
|
|
814
929
|
saveConfigFn({
|
|
815
930
|
apiKey: apiKey.trim(),
|
|
816
931
|
baseUrl: 'https://api.nansen.ai'
|
|
817
932
|
});
|
|
818
933
|
|
|
819
934
|
log(`✓ Saved to ${getConfigFileFn()}\n`);
|
|
820
|
-
|
|
935
|
+
if (accountInfo?.plan) {
|
|
936
|
+
log(`Plan: ${accountInfo.plan}`);
|
|
937
|
+
}
|
|
938
|
+
if (accountInfo?.credits_remaining !== undefined) {
|
|
939
|
+
log(`Credits remaining: ${accountInfo.credits_remaining}`);
|
|
940
|
+
}
|
|
941
|
+
log('\nYou can now use the Nansen CLI. Try:');
|
|
821
942
|
log(' nansen research token screener --chain solana --pretty');
|
|
822
943
|
},
|
|
823
944
|
|
|
@@ -1448,7 +1569,7 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1448
1569
|
if (updateNotification) errorOutput(updateNotification);
|
|
1449
1570
|
};
|
|
1450
1571
|
|
|
1451
|
-
const commands = { ...buildCommands(deps), ...buildWalletCommands(deps), ...buildTradingCommands(deps), ...commandOverrides };
|
|
1572
|
+
const commands = { ...buildCommands(deps), ...buildWalletCommands(deps), ...buildTradingCommands(deps), ...buildAlertsCommands(deps), ...commandOverrides };
|
|
1452
1573
|
|
|
1453
1574
|
if (flags.version || flags.v) {
|
|
1454
1575
|
output(VERSION);
|
|
@@ -1485,8 +1606,8 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1485
1606
|
}
|
|
1486
1607
|
}
|
|
1487
1608
|
// First try subcommand help
|
|
1488
|
-
// Skip for 'trade' —
|
|
1489
|
-
if (command && subcommand && command !== 'trade') {
|
|
1609
|
+
// Skip for 'trade'/'alerts' — their handlers show their own rich usage
|
|
1610
|
+
if (command && subcommand && command !== 'trade' && command !== 'alerts') {
|
|
1490
1611
|
const subHelp = generateSubcommandHelp(command, subcommand);
|
|
1491
1612
|
if (subHelp) {
|
|
1492
1613
|
output(subHelp);
|
|
@@ -1495,8 +1616,8 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1495
1616
|
}
|
|
1496
1617
|
}
|
|
1497
1618
|
// Then try command-level help (list subcommands)
|
|
1498
|
-
// Skip for 'trade' — let the handler show its own usage
|
|
1499
|
-
const cmdSchemaLookup = command !== 'trade' && (SCHEMA.commands[command] || SCHEMA.commands.research.subcommands[command]);
|
|
1619
|
+
// Skip for 'trade'/'alerts' — let the handler show its own usage
|
|
1620
|
+
const cmdSchemaLookup = command !== 'trade' && command !== 'alerts' && (SCHEMA.commands[command] || SCHEMA.commands.research.subcommands[command]);
|
|
1500
1621
|
if (command && cmdSchemaLookup) {
|
|
1501
1622
|
const cmdSchema = cmdSchemaLookup;
|
|
1502
1623
|
const lines = [`${command} — ${cmdSchema.description}`];
|
|
@@ -1504,6 +1625,19 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1504
1625
|
lines.push('Subcommands: ' + Object.keys(cmdSchema.subcommands).join(', '));
|
|
1505
1626
|
lines.push(`Use: nansen ${command} <subcommand> --help`);
|
|
1506
1627
|
}
|
|
1628
|
+
if (cmdSchema.options) {
|
|
1629
|
+
const params = Object.entries(cmdSchema.options).map(([name, opt]) => {
|
|
1630
|
+
const parts = [`--${name}`];
|
|
1631
|
+
if (opt.required) parts[0] += '*';
|
|
1632
|
+
if (opt.default !== undefined) parts.push(`(default: ${opt.default})`);
|
|
1633
|
+
if (opt.description) parts.push(`— ${opt.description}`);
|
|
1634
|
+
return parts.join(' ');
|
|
1635
|
+
});
|
|
1636
|
+
lines.push(`\nOptions (* required):\n ${params.join('\n ')}`);
|
|
1637
|
+
}
|
|
1638
|
+
if (cmdSchema.examples?.length) {
|
|
1639
|
+
lines.push(`\nExamples:\n ${cmdSchema.examples.join('\n ')}`);
|
|
1640
|
+
}
|
|
1507
1641
|
output(lines.join('\n'));
|
|
1508
1642
|
notify();
|
|
1509
1643
|
return { type: 'command-help', command };
|
|
@@ -1531,13 +1665,22 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1531
1665
|
}
|
|
1532
1666
|
}
|
|
1533
1667
|
|
|
1668
|
+
// ── Telemetry setup ──
|
|
1669
|
+
const startTime = Date.now();
|
|
1670
|
+
const fullCommand = subcommand ? `${command} ${subcommand}` : command;
|
|
1671
|
+
const flagNames = Object.keys(flags).filter(k => flags[k]).map(k => `--${k}`);
|
|
1672
|
+
const optionNames = Object.keys(options).map(k => `--${k}`);
|
|
1673
|
+
const usedFlags = [...flagNames, ...optionNames];
|
|
1674
|
+
const chain = options.chain || null;
|
|
1675
|
+
|
|
1534
1676
|
if (!commands[command]) {
|
|
1535
|
-
const errorData = {
|
|
1677
|
+
const errorData = {
|
|
1536
1678
|
error: `Unknown command: ${command}`,
|
|
1537
1679
|
available: Object.keys(commands)
|
|
1538
1680
|
};
|
|
1539
1681
|
const formatted = formatOutput(errorData, { pretty, table });
|
|
1540
1682
|
output(formatted.text);
|
|
1683
|
+
trackCommandFailed({ command: fullCommand, duration_ms: Date.now() - startTime, error_code: 'UNKNOWN_COMMAND', flags: usedFlags, chain });
|
|
1541
1684
|
notify();
|
|
1542
1685
|
exit(1);
|
|
1543
1686
|
return { type: 'error', data: errorData };
|
|
@@ -1565,6 +1708,7 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1565
1708
|
|
|
1566
1709
|
// Commands that handle their own output return undefined
|
|
1567
1710
|
if (result === undefined) {
|
|
1711
|
+
trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, flags: usedFlags, chain });
|
|
1568
1712
|
notify();
|
|
1569
1713
|
return { type: 'no-output', command };
|
|
1570
1714
|
}
|
|
@@ -1573,6 +1717,7 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1573
1717
|
if (command === 'schema') {
|
|
1574
1718
|
const formatted = formatOutput(result, { pretty, table: false });
|
|
1575
1719
|
output(formatted.text);
|
|
1720
|
+
trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, flags: usedFlags, chain });
|
|
1576
1721
|
notify();
|
|
1577
1722
|
return { type: 'schema', data: result };
|
|
1578
1723
|
}
|
|
@@ -1583,6 +1728,13 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1583
1728
|
result = filterFields(result, fields);
|
|
1584
1729
|
}
|
|
1585
1730
|
|
|
1731
|
+
// Alerts list with --table uses custom table format
|
|
1732
|
+
if (command === 'alerts' && subcommand === 'list' && table) {
|
|
1733
|
+
output(formatAlertsTable(result));
|
|
1734
|
+
notify();
|
|
1735
|
+
return { type: 'success', data: result };
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1586
1738
|
// Output in requested format
|
|
1587
1739
|
if (stream) {
|
|
1588
1740
|
// Stream mode: output each record as a JSON line (NDJSON)
|
|
@@ -1590,6 +1742,7 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1590
1742
|
if (streamOutput) {
|
|
1591
1743
|
output(streamOutput);
|
|
1592
1744
|
}
|
|
1745
|
+
trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, from_cache: !!result?.fromCache, flags: usedFlags, chain });
|
|
1593
1746
|
notify();
|
|
1594
1747
|
return { type: 'stream', data: result };
|
|
1595
1748
|
}
|
|
@@ -1597,12 +1750,21 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1597
1750
|
const successData = { success: true, data: result };
|
|
1598
1751
|
const formatted = formatOutput(successData, { pretty, table, csv });
|
|
1599
1752
|
output(formatted.text);
|
|
1753
|
+
trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, from_cache: !!result?.fromCache, flags: usedFlags, chain });
|
|
1600
1754
|
notify();
|
|
1601
1755
|
return { type: csv ? 'csv' : 'success', data: result };
|
|
1602
1756
|
} catch (error) {
|
|
1603
1757
|
const errorData = formatError(error);
|
|
1604
1758
|
const formatted = formatOutput(errorData, { pretty, table, csv });
|
|
1605
1759
|
output(formatted.text);
|
|
1760
|
+
trackCommandFailed({
|
|
1761
|
+
command: fullCommand,
|
|
1762
|
+
duration_ms: Date.now() - startTime,
|
|
1763
|
+
error_code: error.code || 'UNKNOWN',
|
|
1764
|
+
status: error.status || null,
|
|
1765
|
+
flags: usedFlags,
|
|
1766
|
+
chain,
|
|
1767
|
+
});
|
|
1606
1768
|
notify();
|
|
1607
1769
|
exit(1);
|
|
1608
1770
|
return { type: 'error', data: errorData };
|
package/src/schema.json
CHANGED
|
@@ -568,6 +568,129 @@
|
|
|
568
568
|
}
|
|
569
569
|
}
|
|
570
570
|
},
|
|
571
|
+
"alerts": {
|
|
572
|
+
"description": "Smart alert management \u2014 create, update, toggle, delete alerts",
|
|
573
|
+
"subcommands": {
|
|
574
|
+
"list": {
|
|
575
|
+
"description": "List all alerts",
|
|
576
|
+
"returns": [
|
|
577
|
+
"id",
|
|
578
|
+
"name",
|
|
579
|
+
"type",
|
|
580
|
+
"timeWindow",
|
|
581
|
+
"isEnabled",
|
|
582
|
+
"channels",
|
|
583
|
+
"data",
|
|
584
|
+
"description"
|
|
585
|
+
]
|
|
586
|
+
},
|
|
587
|
+
"create": {
|
|
588
|
+
"description": "Create a new alert",
|
|
589
|
+
"options": {
|
|
590
|
+
"name": {
|
|
591
|
+
"type": "string",
|
|
592
|
+
"required": true,
|
|
593
|
+
"description": "Alert name"
|
|
594
|
+
},
|
|
595
|
+
"type": {
|
|
596
|
+
"type": "string",
|
|
597
|
+
"required": true,
|
|
598
|
+
"description": "Alert type (e.g. sm-token-flows, common-token-transfer)"
|
|
599
|
+
},
|
|
600
|
+
"chains": {
|
|
601
|
+
"type": "string",
|
|
602
|
+
"description": "Comma-separated chains (e.g. ethereum,solana). Merged into data."
|
|
603
|
+
},
|
|
604
|
+
"telegram": {
|
|
605
|
+
"type": "string",
|
|
606
|
+
"description": "Telegram chat ID for notifications"
|
|
607
|
+
},
|
|
608
|
+
"slack": {
|
|
609
|
+
"type": "string",
|
|
610
|
+
"description": "Slack webhook URL for notifications"
|
|
611
|
+
},
|
|
612
|
+
"discord": {
|
|
613
|
+
"type": "string",
|
|
614
|
+
"description": "Discord webhook URL for notifications"
|
|
615
|
+
},
|
|
616
|
+
"data": {
|
|
617
|
+
"type": "string",
|
|
618
|
+
"description": "Alert config JSON. --chains is merged on top."
|
|
619
|
+
},
|
|
620
|
+
"description": {
|
|
621
|
+
"type": "string",
|
|
622
|
+
"description": "Optional description"
|
|
623
|
+
},
|
|
624
|
+
"disabled": {
|
|
625
|
+
"type": "boolean",
|
|
626
|
+
"description": "Create alert in disabled state"
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
"update": {
|
|
631
|
+
"description": "Update an existing alert. Usage: nansen alerts update <id> [options]",
|
|
632
|
+
"options": {
|
|
633
|
+
"name": {
|
|
634
|
+
"type": "string",
|
|
635
|
+
"description": "Alert name"
|
|
636
|
+
},
|
|
637
|
+
"type": {
|
|
638
|
+
"type": "string",
|
|
639
|
+
"description": "Alert type"
|
|
640
|
+
},
|
|
641
|
+
"chains": {
|
|
642
|
+
"type": "string",
|
|
643
|
+
"description": "Comma-separated chains. Merged into data."
|
|
644
|
+
},
|
|
645
|
+
"telegram": {
|
|
646
|
+
"type": "string",
|
|
647
|
+
"description": "Telegram chat ID"
|
|
648
|
+
},
|
|
649
|
+
"slack": {
|
|
650
|
+
"type": "string",
|
|
651
|
+
"description": "Slack webhook URL"
|
|
652
|
+
},
|
|
653
|
+
"discord": {
|
|
654
|
+
"type": "string",
|
|
655
|
+
"description": "Discord webhook URL"
|
|
656
|
+
},
|
|
657
|
+
"data": {
|
|
658
|
+
"type": "string",
|
|
659
|
+
"description": "Alert config JSON. --chains merged on top."
|
|
660
|
+
},
|
|
661
|
+
"description": {
|
|
662
|
+
"type": "string",
|
|
663
|
+
"description": "Description"
|
|
664
|
+
},
|
|
665
|
+
"enabled": {
|
|
666
|
+
"type": "boolean",
|
|
667
|
+
"description": "Enable alert"
|
|
668
|
+
},
|
|
669
|
+
"disabled": {
|
|
670
|
+
"type": "boolean",
|
|
671
|
+
"description": "Disable alert"
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
"toggle": {
|
|
676
|
+
"description": "Enable or disable an alert. Usage: nansen alerts toggle <id> --enabled|--disabled",
|
|
677
|
+
"options": {
|
|
678
|
+
"enabled": {
|
|
679
|
+
"type": "boolean",
|
|
680
|
+
"description": "Enable alert"
|
|
681
|
+
},
|
|
682
|
+
"disabled": {
|
|
683
|
+
"type": "boolean",
|
|
684
|
+
"description": "Disable alert"
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
},
|
|
688
|
+
"delete": {
|
|
689
|
+
"description": "Delete an alert. Usage: nansen alerts delete <id>",
|
|
690
|
+
"options": {}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
},
|
|
571
694
|
"trade": {
|
|
572
695
|
"description": "DEX trading commands",
|
|
573
696
|
"subcommands": {
|
|
@@ -596,7 +719,7 @@
|
|
|
596
719
|
},
|
|
597
720
|
"wallet": {
|
|
598
721
|
"type": "string",
|
|
599
|
-
"description": "Wallet name (or \"walletconnect\"/\"wc\" for WalletConnect, EVM only). A configured wallet is required
|
|
722
|
+
"description": "Wallet name (or \"walletconnect\"/\"wc\" for WalletConnect, EVM only). A configured wallet is required \u2014 run `nansen wallet create` if you haven't set one up yet."
|
|
600
723
|
}
|
|
601
724
|
},
|
|
602
725
|
"prerequisites": [
|
|
@@ -663,6 +786,46 @@
|
|
|
663
786
|
},
|
|
664
787
|
"account": {
|
|
665
788
|
"description": "Show API key status, plan, and remaining credits. Does not consume credits."
|
|
789
|
+
},
|
|
790
|
+
"web": {
|
|
791
|
+
"description": "Web search and fetch commands",
|
|
792
|
+
"subcommands": {
|
|
793
|
+
"search": {
|
|
794
|
+
"description": "Search the web for one or more queries in parallel",
|
|
795
|
+
"options": {
|
|
796
|
+
"query": {
|
|
797
|
+
"description": "Search query (can be repeated for multiple queries)"
|
|
798
|
+
},
|
|
799
|
+
"num-results": {
|
|
800
|
+
"description": "Number of results per query (1-20, default 10)",
|
|
801
|
+
"default": 10
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
"examples": [
|
|
805
|
+
"nansen web search \"bitcoin price\"",
|
|
806
|
+
"nansen web search \"solana news\" --num-results 5",
|
|
807
|
+
"nansen web search --query \"btc\" --query \"eth\"",
|
|
808
|
+
"nansen web search \"btc\" --query \"eth\" # positional + flag merged"
|
|
809
|
+
]
|
|
810
|
+
},
|
|
811
|
+
"fetch": {
|
|
812
|
+
"description": "Fetch and analyze content from one or more URLs using AI",
|
|
813
|
+
"options": {
|
|
814
|
+
"url": {
|
|
815
|
+
"description": "URL to fetch (can be repeated for multiple URLs)"
|
|
816
|
+
},
|
|
817
|
+
"question": {
|
|
818
|
+
"description": "Question to answer about the URL content",
|
|
819
|
+
"required": true
|
|
820
|
+
}
|
|
821
|
+
},
|
|
822
|
+
"examples": [
|
|
823
|
+
"nansen web fetch https://nansen.ai --question \"What does Nansen do?\"",
|
|
824
|
+
"nansen web fetch --url https://a.com --url https://b.com --question \"Compare these\"",
|
|
825
|
+
"nansen web fetch https://a.com --url https://b.com --question \"Diff?\" # positional + flag merged"
|
|
826
|
+
]
|
|
827
|
+
}
|
|
828
|
+
}
|
|
666
829
|
}
|
|
667
830
|
},
|
|
668
831
|
"globalOptions": {
|