nansen-cli 1.19.0 → 1.20.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 +10 -0
- package/README.md +5 -3
- package/package.json +3 -1
- package/scripts/postinstall.js +179 -0
- package/skills/nansen-wallet-manager/SKILL.md +48 -2
- package/src/api.js +1 -1
- package/src/cli.js +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#302](https://github.com/nansen-ai/nansen-cli/pull/302) [`3f0a5ab`](https://github.com/nansen-ai/nansen-cli/commit/3f0a5abad463c0386122efbe746809913aa823ba) Thanks [@arein](https://github.com/arein)! - Add post-install onboarding that interactively offers to install the Nansen AI coding skill and run a test query after `npm install -g nansen-cli`. Non-interactive environments (CI, piped stdin) receive a one-liner tip and are never blocked.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#313](https://github.com/nansen-ai/nansen-cli/pull/313) [`bb4d9e4`](https://github.com/nansen-ai/nansen-cli/commit/bb4d9e475158147645cae9b8bdd2555568a1e515) Thanks [@0xlaveen](https://github.com/0xlaveen)! - Update API key setup URL from app.nansen.ai/api to app.nansen.ai/auth/agent-setup across CLI help text, error messages, README, and postinstall script.
|
|
12
|
+
|
|
3
13
|
## 1.19.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -15,11 +15,13 @@ npx skills add nansen-ai/nansen-cli # load agent skill files
|
|
|
15
15
|
## Auth
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
nansen login
|
|
19
|
-
|
|
18
|
+
nansen login --api-key <key> # save key to ~/.nansen/config.json
|
|
19
|
+
nansen login --human # interactive prompt
|
|
20
|
+
export NANSEN_API_KEY=... # env var (highest priority)
|
|
21
|
+
nansen logout # remove saved key
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
Get your API key at [app.nansen.ai/
|
|
24
|
+
Get your API key at [app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup).
|
|
23
25
|
|
|
24
26
|
## Commands
|
|
25
27
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nansen-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Command-line interface for Nansen API - designed for AI agents",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
"src/**/*.json",
|
|
13
13
|
"!src/__tests__/**",
|
|
14
14
|
"skills/**",
|
|
15
|
+
"scripts/postinstall.js",
|
|
15
16
|
"CHANGELOG.md"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|
|
19
|
+
"postinstall": "node scripts/postinstall.js",
|
|
18
20
|
"start": "node src/index.js",
|
|
19
21
|
"pretest": "node scripts/check-changeset.js",
|
|
20
22
|
"test": "vitest run",
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-install onboarding for nansen-cli.
|
|
5
|
+
*
|
|
6
|
+
* Runs after `npm install -g nansen-cli` and offers two optional steps:
|
|
7
|
+
* 1. Install the Nansen AI coding skill (`npx skills add nansen-ai/nansen-cli`)
|
|
8
|
+
* 2. Check account status to verify the API key works (0 credits)
|
|
9
|
+
*
|
|
10
|
+
* Non-interactive environments (CI, piped stdin) get a one-liner tip instead.
|
|
11
|
+
* Always exits 0 — onboarding failures must never break installation.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createInterface } from "readline";
|
|
15
|
+
import { execFileSync, spawn } from "child_process";
|
|
16
|
+
import { existsSync, readFileSync } from "fs";
|
|
17
|
+
import { join, dirname } from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
|
|
22
|
+
const BOLD = "\x1b[1m";
|
|
23
|
+
const DIM = "\x1b[2m";
|
|
24
|
+
const GREEN = "\x1b[32m";
|
|
25
|
+
const YELLOW = "\x1b[33m";
|
|
26
|
+
const CYAN = "\x1b[36m";
|
|
27
|
+
const RESET = "\x1b[0m";
|
|
28
|
+
|
|
29
|
+
const SKILL_REPO = "nansen-ai/nansen-cli";
|
|
30
|
+
const TEST_QUERY = ["account"];
|
|
31
|
+
const TEST_QUERY_DISPLAY = "nansen account";
|
|
32
|
+
|
|
33
|
+
// Path to the CLI entry point (works even if `nansen` bin isn't linked yet)
|
|
34
|
+
const CLI_ENTRY = join(__dirname, "..", "src", "index.js");
|
|
35
|
+
|
|
36
|
+
function log(msg = "") {
|
|
37
|
+
process.stderr.write(` ${msg}\n`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function hasTTY() {
|
|
41
|
+
return process.stdin.isTTY && process.stderr.isTTY;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hasNpx() {
|
|
45
|
+
try {
|
|
46
|
+
execFileSync("npx", ["--version"], { stdio: "ignore", shell: process.platform === "win32" });
|
|
47
|
+
return true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isLoggedIn() {
|
|
54
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
55
|
+
const configFile = join(home, ".nansen", "config.json");
|
|
56
|
+
if (!existsSync(configFile)) return false;
|
|
57
|
+
try {
|
|
58
|
+
const config = JSON.parse(readFileSync(configFile, "utf8"));
|
|
59
|
+
return !!(config.apiKey || config.api_key);
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isSkillInstalled() {
|
|
66
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
67
|
+
const locations = [
|
|
68
|
+
join(home, ".claude", "skills", "nansen-cli"),
|
|
69
|
+
join(home, ".claude", "skills", "nansen-ai--nansen-cli"),
|
|
70
|
+
];
|
|
71
|
+
return locations.some((loc) => existsSync(loc));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function prompt(question) {
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
77
|
+
let answered = false;
|
|
78
|
+
rl.on("close", () => { if (!answered) resolve(""); });
|
|
79
|
+
rl.question(question, (answer) => {
|
|
80
|
+
answered = true;
|
|
81
|
+
rl.close();
|
|
82
|
+
resolve(answer.trim());
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function runCommand(cmd, args) {
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
const child = spawn(cmd, args, { stdio: "inherit", shell: process.platform === "win32" });
|
|
90
|
+
child.on("close", (code) => resolve(code === 0));
|
|
91
|
+
child.on("error", () => resolve(false));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function installSkill() {
|
|
96
|
+
if (isSkillInstalled()) {
|
|
97
|
+
log(`${GREEN}✓${RESET} Nansen skill already installed.`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!hasNpx()) {
|
|
102
|
+
log(`${DIM}Tip: Run 'npx skills add ${SKILL_REPO}' to install the Nansen AI coding skill.${RESET}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
log(`The Nansen skill lets AI coding agents (Cursor, Claude Code, etc.) query`);
|
|
107
|
+
log(`on-chain data, track smart money, and analyze tokens on your behalf.`);
|
|
108
|
+
const answer = await prompt(` Install Nansen skill for your AI coding agent? [Y/n] `);
|
|
109
|
+
|
|
110
|
+
if (/^n/i.test(answer)) {
|
|
111
|
+
log(`Skipped. You can install it later with: ${CYAN}npx skills add ${SKILL_REPO}${RESET}`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
log(`Installing Nansen skill...`);
|
|
116
|
+
const ok = await runCommand("npx", ["-y", "skills", "add", SKILL_REPO]);
|
|
117
|
+
if (!ok) {
|
|
118
|
+
log(`${YELLOW}Skill installation failed. You can retry with: npx skills add ${SKILL_REPO}${RESET}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function testQuery() {
|
|
123
|
+
if (!isLoggedIn()) {
|
|
124
|
+
log();
|
|
125
|
+
log(`Not logged in yet. Run ${CYAN}nansen login --api-key <key>${RESET} to authenticate.`);
|
|
126
|
+
log(`Get your API key at: ${CYAN}https://app.nansen.ai/auth/agent-setup${RESET}`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
log();
|
|
131
|
+
log(`Your API key is configured. Let's verify it works.`);
|
|
132
|
+
const answer = await prompt(` Check account status? (${DIM}${TEST_QUERY_DISPLAY}${RESET}) [Y/n] `);
|
|
133
|
+
|
|
134
|
+
if (/^n/i.test(answer)) {
|
|
135
|
+
log(`Skipped. You're all set! Try: ${CYAN}nansen research smart-money netflow --chain solana${RESET}`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
log(`Running: ${DIM}${TEST_QUERY_DISPLAY}${RESET}`);
|
|
140
|
+
log();
|
|
141
|
+
// Use process.execPath + CLI_ENTRY so it works even if `nansen` bin isn't linked yet
|
|
142
|
+
const ok = await runCommand(process.execPath, [CLI_ENTRY, ...TEST_QUERY, "--pretty"]);
|
|
143
|
+
if (ok) {
|
|
144
|
+
log();
|
|
145
|
+
log(`${GREEN}✓${RESET} All set! Run ${CYAN}nansen help${RESET} to see all available commands.`);
|
|
146
|
+
} else {
|
|
147
|
+
log();
|
|
148
|
+
log(`${YELLOW}Query failed. Check your API key with: nansen login --api-key <key>${RESET}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function main() {
|
|
153
|
+
// Only run for global installs; skip local npm install / npm ci
|
|
154
|
+
if (process.env.npm_lifecycle_event === "postinstall" && process.env.npm_config_global !== "true") {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
log();
|
|
159
|
+
|
|
160
|
+
if (!hasTTY()) {
|
|
161
|
+
log(`${BOLD}Nansen CLI installed!${RESET}`);
|
|
162
|
+
log();
|
|
163
|
+
log(`Tip: Run '${CYAN}npx skills add ${SKILL_REPO}${RESET}' to install the Nansen AI coding skill.`);
|
|
164
|
+
log(`Tip: Run '${CYAN}nansen login --api-key <key>${RESET}' to authenticate.`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
log(`${BOLD}Nansen CLI installed!${RESET}`);
|
|
169
|
+
log();
|
|
170
|
+
|
|
171
|
+
await installSkill();
|
|
172
|
+
await testQuery();
|
|
173
|
+
|
|
174
|
+
log();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
main().catch(() => {
|
|
178
|
+
// Never fail installation due to onboarding errors
|
|
179
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nansen-wallet-manager
|
|
3
|
-
description: Wallet management — create, list, show, export, send, delete. Use when creating wallets, checking balances, or sending tokens.
|
|
3
|
+
description: Wallet management — create (local or Privy server-side), list, show, export, send, delete. Use when creating wallets, checking balances, or sending tokens.
|
|
4
4
|
metadata:
|
|
5
5
|
openclaw:
|
|
6
6
|
requires:
|
|
@@ -30,7 +30,49 @@ NANSEN_API_KEY=<key> nansen login
|
|
|
30
30
|
nansen research profiler labels --address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --chain ethereum
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
## Wallet
|
|
33
|
+
## Wallet Providers
|
|
34
|
+
|
|
35
|
+
The CLI supports two wallet providers:
|
|
36
|
+
|
|
37
|
+
| | **Local** (default) | **Privy** (server-side) |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| Key storage | Encrypted on disk | Server-side via Privy API |
|
|
40
|
+
| Password required | Yes (min 12 chars) | No |
|
|
41
|
+
| Export private keys | Yes (`wallet export`) | No — keys are managed by Privy |
|
|
42
|
+
| Best for | Human users, manual trading | Agents, automated workflows |
|
|
43
|
+
| Flag | `--provider local` (default) | `--provider privy` |
|
|
44
|
+
| Required env vars | `NANSEN_WALLET_PASSWORD` | `PRIVY_APP_ID` + `PRIVY_APP_SECRET` |
|
|
45
|
+
|
|
46
|
+
## Privy Wallet Creation
|
|
47
|
+
|
|
48
|
+
Privy wallets are server-side wallets managed by the Privy API. No password is needed — keys never touch the local machine.
|
|
49
|
+
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
The following environment variables must be set:
|
|
53
|
+
|
|
54
|
+
| Var | Purpose |
|
|
55
|
+
|-----|---------|
|
|
56
|
+
| `PRIVY_APP_ID` | Privy application ID |
|
|
57
|
+
| `PRIVY_APP_SECRET` | Privy application secret |
|
|
58
|
+
|
|
59
|
+
### Create a Privy wallet
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
nansen wallet create --provider privy
|
|
63
|
+
# Or with a custom name:
|
|
64
|
+
nansen wallet create --name agent-wallet --provider privy
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Critical rules for agents (Privy)
|
|
68
|
+
|
|
69
|
+
- **No password needed** — Privy manages keys server-side
|
|
70
|
+
- **Cannot export keys** — `wallet export` only works for local wallets
|
|
71
|
+
- All other operations (`list`, `show`, `send`, `delete`, `default`) work identically for both providers
|
|
72
|
+
|
|
73
|
+
## Local Wallet Creation (Two-Step Agent Flow)
|
|
74
|
+
|
|
75
|
+
> This section covers **local** wallet creation. For Privy server-side wallets, see the [Privy Wallet Creation](#privy-wallet-creation) section above — no password is needed.
|
|
34
76
|
|
|
35
77
|
Wallet creation requires a password from the **human user**. The agent must NOT generate or store the password itself.
|
|
36
78
|
|
|
@@ -127,6 +169,7 @@ For detailed migration steps (from `~/.nansen/.env`, `.credentials`, or env-var-
|
|
|
127
169
|
| `--chain` | `evm` or `solana` |
|
|
128
170
|
| `--max` | Send entire balance |
|
|
129
171
|
| `--dry-run` | Preview without broadcasting |
|
|
172
|
+
| `--provider` | Wallet provider: `local` (default, encrypted on disk) or `privy` (server-side via Privy API) |
|
|
130
173
|
| `--human` | Enable interactive prompts (human terminal use only — agents must NOT use this) |
|
|
131
174
|
| `--unsafe-no-password` | Skip encryption (keys stored in plaintext — NOT recommended) |
|
|
132
175
|
|
|
@@ -136,5 +179,8 @@ For detailed migration steps (from `~/.nansen/.env`, `.credentials`, or env-var-
|
|
|
136
179
|
|-----|---------|
|
|
137
180
|
| `NANSEN_WALLET_PASSWORD` | Wallet encryption password — only needed for initial `wallet create`. After that, the OS keychain handles it. |
|
|
138
181
|
| `NANSEN_API_KEY` | API key (also set via `nansen login --api-key <key>`) |
|
|
182
|
+
| `PRIVY_APP_ID` | Privy application ID (required for `--provider privy`) |
|
|
183
|
+
| `PRIVY_APP_SECRET` | Privy application secret (required for `--provider privy`) |
|
|
184
|
+
| `NANSEN_WALLET_PROVIDER` | Default provider for wallet create — `local` or `privy` |
|
|
139
185
|
| `NANSEN_EVM_RPC` | Custom EVM RPC endpoint |
|
|
140
186
|
| `NANSEN_SOLANA_RPC` | Custom Solana RPC endpoint |
|
package/src/api.js
CHANGED
|
@@ -630,7 +630,7 @@ export class NansenAPI {
|
|
|
630
630
|
} catch (x402Err) {
|
|
631
631
|
if (!this.apiKey) {
|
|
632
632
|
message = 'No API key configured. Two ways to authenticate:\n' +
|
|
633
|
-
' 1. API key: nansen login --api-key <key> (get key at https://app.nansen.ai/
|
|
633
|
+
' 1. API key: nansen login --api-key <key> (get key at https://app.nansen.ai/auth/agent-setup)\n' +
|
|
634
634
|
' 2. x402 micropayment: nansen wallet create + fund with USDC (no API key needed)';
|
|
635
635
|
} else {
|
|
636
636
|
message = `x402 auto-payment failed: ${x402Err.message}`;
|
package/src/cli.js
CHANGED
|
@@ -685,7 +685,7 @@ COMMANDS:
|
|
|
685
685
|
alerts list, create, update, toggle, delete
|
|
686
686
|
web search, fetch
|
|
687
687
|
account Show API key status, plan, and remaining credits
|
|
688
|
-
login Save API key (--api-key <key
|
|
688
|
+
login Save API key (--api-key <key>, --human, or NANSEN_API_KEY env var)
|
|
689
689
|
logout Remove saved API key
|
|
690
690
|
schema JSON schema for all commands (use "nansen schema <cmd>" for one)
|
|
691
691
|
cache clear
|
|
@@ -859,7 +859,7 @@ export function buildCommands(deps = {}) {
|
|
|
859
859
|
log(' --api-key <key> Your Nansen API key');
|
|
860
860
|
log(' --human Enable interactive prompt');
|
|
861
861
|
log(' --help Show this help\n');
|
|
862
|
-
log('Get your API key at: https://app.nansen.ai/
|
|
862
|
+
log('Get your API key at: https://app.nansen.ai/auth/agent-setup');
|
|
863
863
|
return;
|
|
864
864
|
}
|
|
865
865
|
|
|
@@ -879,7 +879,7 @@ export function buildCommands(deps = {}) {
|
|
|
879
879
|
return;
|
|
880
880
|
}
|
|
881
881
|
log('Nansen CLI Login\n');
|
|
882
|
-
log('Get your API key at: https://app.nansen.ai/
|
|
882
|
+
log('Get your API key at: https://app.nansen.ai/auth/agent-setup\n');
|
|
883
883
|
apiKey = await promptFn('Enter your API key: ', true);
|
|
884
884
|
}
|
|
885
885
|
|
|
@@ -890,7 +890,7 @@ export function buildCommands(deps = {}) {
|
|
|
890
890
|
resolution: [
|
|
891
891
|
'Run: nansen login --api-key <key>',
|
|
892
892
|
'Or set NANSEN_API_KEY environment variable',
|
|
893
|
-
'Get your API key at: https://app.nansen.ai/
|
|
893
|
+
'Get your API key at: https://app.nansen.ai/auth/agent-setup',
|
|
894
894
|
],
|
|
895
895
|
}));
|
|
896
896
|
exit(1);
|
|
@@ -912,7 +912,7 @@ export function buildCommands(deps = {}) {
|
|
|
912
912
|
log(JSON.stringify({
|
|
913
913
|
error: 'INVALID_API_KEY',
|
|
914
914
|
message: 'The API key is not valid.',
|
|
915
|
-
resolution: ['Check your key at https://app.nansen.ai/
|
|
915
|
+
resolution: ['Check your key at https://app.nansen.ai/auth/agent-setup']
|
|
916
916
|
}));
|
|
917
917
|
} else {
|
|
918
918
|
log(JSON.stringify({
|