nansen-cli 1.20.0 → 1.22.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 CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#336](https://github.com/nansen-ai/nansen-cli/pull/336) [`c3b1fbd`](https://github.com/nansen-ai/nansen-cli/commit/c3b1fbdee46c15326a1656bf27a314f6c55dddf8) Thanks [@kome12](https://github.com/kome12)! - Add --label option to `token flows` command to filter by holder segment (top_100_holders, smart_money, public_figure, whale, exchange).
8
+
9
+ - [#334](https://github.com/nansen-ai/nansen-cli/pull/334) [`83244c6`](https://github.com/nansen-ai/nansen-cli/commit/83244c658d4ece2072dea0c6ed405a088c98aa4f) Thanks [@kome12](https://github.com/kome12)! - Add `--include-stablecoins` flag to `token screener` command. Pass `--include-stablecoins false` to exclude stablecoins from screener results (API default is `true`). Supports combined usage with `--smart-money`.
10
+
11
+ - [#339](https://github.com/nansen-ai/nansen-cli/pull/339) [`27ebcfc`](https://github.com/nansen-ai/nansen-cli/commit/27ebcfc2a3afd836db595df6d5a2a5f9242b624c) Thanks [@TimNooren](https://github.com/TimNooren)! - Add --amount-unit token flag to trade quote for human-readable amounts
12
+
13
+ ## 1.21.0
14
+
15
+ ### Minor Changes
16
+
17
+ - [#315](https://github.com/nansen-ai/nansen-cli/pull/315) [`908fa0c`](https://github.com/nansen-ai/nansen-cli/commit/908fa0ccdf18cb79b7efb58b9b31f66e0afedff6) Thanks [@TimNooren](https://github.com/TimNooren)! - Add `nansen agent` command for the Nansen AI research agent with fast/expert modes, SSE streaming, conversation continuation, and JSON output.
18
+
19
+ ### Patch Changes
20
+
21
+ - [#326](https://github.com/nansen-ai/nansen-cli/pull/326) [`1532ba4`](https://github.com/nansen-ai/nansen-cli/commit/1532ba420174ed7635a42641f0c1a2802077fdc0) Thanks [@TimNooren](https://github.com/TimNooren)! - Show API credit cost in research subcommand help text (fetched from OpenAPI spec, cached 24h).
22
+
23
+ - [#330](https://github.com/nansen-ai/nansen-cli/pull/330) [`a6b9b8f`](https://github.com/nansen-ai/nansen-cli/commit/a6b9b8fc7d291ee7375941bb275c224939338161) Thanks [@0xlaveen](https://github.com/0xlaveen)! - Suppress misleading PASSWORD_REQUIRED error when `--provider privy` is specified. Privy wallets don't need a password — only the Privy-specific credentials error is now shown when PRIVY_APP_ID/PRIVY_APP_SECRET are missing.
24
+
25
+ - [#329](https://github.com/nansen-ai/nansen-cli/pull/329) [`f047833`](https://github.com/nansen-ai/nansen-cli/commit/f047833c7e6fb55cb713e0b69f82282fe87d4566) Thanks [@TimNooren](https://github.com/TimNooren)! - Limit deprecation warnings and update notices to help output only, keeping stdout/stderr clean for programmatic usage.
26
+
27
+ - [#332](https://github.com/nansen-ai/nansen-cli/pull/332) [`e9b6de1`](https://github.com/nansen-ai/nansen-cli/commit/e9b6de17fdba3f46733dff026c2495c095bfbf35) Thanks [@0xlaveen](https://github.com/0xlaveen)! - docs: add trading examples and Privy wallet setup to README
28
+
3
29
  ## 1.20.0
4
30
 
5
31
  ### Minor Changes
package/README.md CHANGED
@@ -36,10 +36,35 @@ nansen schema [command] [--pretty] # full command reference (no API key neede
36
36
 
37
37
  **Trade:** `quote`, `execute` — DEX swaps on Solana and Base.
38
38
 
39
- **Wallet:** `create`, `list`, `show`, `export`, `default`, `delete`, `send` — local keypairs (EVM + Solana).
39
+ **Wallet:** `create`, `list`, `show`, `export`, `default`, `delete`, `send` — local or Privy server-side wallets (EVM + Solana).
40
40
 
41
41
  Run `nansen schema --pretty` for the full subcommand and field reference.
42
42
 
43
+ ## Trading
44
+
45
+ DEX swaps on `solana` and `base`. Two-step: quote then execute.
46
+
47
+ ```bash
48
+ nansen trade quote --chain solana --from SOL --to USDC --amount 1000000000
49
+ nansen trade execute --quote <quoteId>
50
+ ```
51
+
52
+ Amounts are in base units (lamports, wei). Common symbols (`SOL`, `ETH`, `USDC`, `USDT`) resolve automatically. A wallet is required — set one with `nansen wallet default <name>`.
53
+
54
+ ## Wallet
55
+
56
+ ```bash
57
+ nansen wallet create --name my-wallet # local keypair (EVM + Solana)
58
+ nansen wallet create --name my-wallet --provider privy # server-side via Privy
59
+ nansen wallet list
60
+ nansen wallet default <name>
61
+ nansen wallet send --wallet <name> --to <addr> --amount <n> --chain <chain>
62
+ ```
63
+
64
+ **Local wallets** are password-encrypted. Set `NANSEN_WALLET_PASSWORD` to skip the prompt.
65
+
66
+ **Privy wallets** are server-side — no password, no local key storage. Requires `PRIVY_APP_ID` and `PRIVY_APP_SECRET` env vars. Get credentials at [dashboard.privy.io](https://dashboard.privy.io).
67
+
43
68
  ## Key Options
44
69
 
45
70
  | Option | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nansen-cli",
3
- "version": "1.20.0",
3
+ "version": "1.22.0",
4
4
  "description": "Command-line interface for Nansen API - designed for AI agents",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -58,7 +58,16 @@ nansen trade execute --quote "$quote_id"
58
58
  | ETH | Base | `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee` |
59
59
  | USDC | Base | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
60
60
 
61
- ## Amounts are in base units
61
+ ## Amounts
62
+
63
+ By default, `--amount` accepts **integer base units** (lamports, wei, etc). Use `--amount-unit token` to specify human-readable token amounts instead — the CLI resolves decimals locally and sends base units to the API.
64
+
65
+ ```bash
66
+ # Base units (default)
67
+ nansen trade quote --chain solana --from SOL --to USDC --amount 1000000000
68
+ # Token units (0.5 SOL = 500000000 lamports, resolved automatically)
69
+ nansen trade quote --chain solana --from SOL --to USDC --amount 0.5 --amount-unit token
70
+ ```
62
71
 
63
72
  | Token | Decimals | 1 token = |
64
73
  |-------|----------|-----------|
@@ -66,6 +75,8 @@ nansen trade execute --quote "$quote_id"
66
75
  | ETH | 18 | `1000000000000000000` |
67
76
  | USDC | 6 | `1000000` |
68
77
 
78
+ If the user says "$20 worth of X", you must convert USD → token amount, then either pass base units or use `--amount-unit token`. For example, to buy $20 of SOL at $150/SOL: $20 ÷ $150 = 0.1333 SOL → `--amount 0.1333 --amount-unit token`. Use a price lookup (e.g. `nansen research token info`) to get the current price first.
79
+
69
80
  ## Flags
70
81
 
71
82
  | Flag | Purpose |
@@ -73,7 +84,8 @@ nansen trade execute --quote "$quote_id"
73
84
  | `--chain` | `solana` or `base` |
74
85
  | `--from` | Source token (symbol or address) |
75
86
  | `--to` | Destination token (symbol or address) |
76
- | `--amount` | Amount in base units (integer) |
87
+ | `--amount` | Amount in base units (integer), or token units with `--amount-unit token` |
88
+ | `--amount-unit` | Set to `token` to specify amount in token units (e.g. 0.5 SOL) |
77
89
  | `--wallet` | Wallet name (default: default wallet) |
78
90
  | `--slippage` | Slippage tolerance as decimal (e.g. 0.03) |
79
91
  | `--quote` | Quote ID for execute |
@@ -109,6 +109,18 @@ If the `.credentials` file fallback is used, the CLI prints a warning on every o
109
109
 
110
110
  ## Create
111
111
 
112
+ ### Privy (server-side, no password)
113
+
114
+ ```bash
115
+ nansen wallet create --provider privy
116
+ # Or with a custom name:
117
+ nansen wallet create --name trading --provider privy
118
+ ```
119
+
120
+ Requires `PRIVY_APP_ID` + `PRIVY_APP_SECRET` env vars. No password needed.
121
+
122
+ ### Local (encrypted on disk, password required)
123
+
112
124
  ```bash
113
125
  # Ask the user for a password first, then:
114
126
  NANSEN_WALLET_PASSWORD="<password_from_user>" nansen wallet create
package/src/api.js CHANGED
@@ -11,12 +11,12 @@ import { getAnonymousId, TELEMETRY_DISABLED } from './telemetry.js';
11
11
 
12
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
13
 
14
- function telemetryHeaders() {
14
+ export function telemetryHeaders() {
15
15
  if (TELEMETRY_DISABLED) return {};
16
16
  return { 'X-Anonymous-Id': getAnonymousId() };
17
17
  }
18
18
 
19
- const { version: packageVersion } = JSON.parse(
19
+ export const { version: packageVersion } = JSON.parse(
20
20
  fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
21
21
  );
22
22
 
@@ -85,7 +85,7 @@ export class NansenError extends Error {
85
85
  /**
86
86
  * Map HTTP status codes to error codes
87
87
  */
88
- function statusToErrorCode(status, data = {}) {
88
+ export function statusToErrorCode(status, data = {}) {
89
89
  const message = data?.message || data?.error || '';
90
90
  const messageLower = message.toLowerCase();
91
91
 
@@ -486,7 +486,7 @@ export class NansenAPI {
486
486
  return cached;
487
487
  }
488
488
  }
489
-
489
+
490
490
  let lastError;
491
491
 
492
492
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
@@ -974,7 +974,7 @@ export class NansenAPI {
974
974
  }
975
975
 
976
976
  async tokenFlows(params = {}) {
977
- const { tokenAddress, chain = 'solana', filters = {}, orderBy, pagination, days = 30, date } = params;
977
+ const { tokenAddress, chain = 'solana', label, filters = {}, orderBy, pagination, days = 30, date } = params;
978
978
  if (tokenAddress) {
979
979
  const validation = validateTokenAddress(tokenAddress, chain);
980
980
  if (!validation.valid) throw new NansenError(validation.error, validation.code);
@@ -984,6 +984,7 @@ export class NansenAPI {
984
984
  token_address: tokenAddress,
985
985
  chain,
986
986
  date: dateRange,
987
+ label,
987
988
  filters,
988
989
  order_by: orderBy,
989
990
  pagination
package/src/cli.js CHANGED
@@ -7,9 +7,11 @@ import { NansenAPI, NansenError, ErrorCode, saveConfig, deleteConfig, getConfigF
7
7
  import { buildWalletCommands } from './wallet.js';
8
8
  import { buildTradingCommands } from './trading.js';
9
9
  import { formatAlertsTable, buildAlertsCommands } from './commands/alerts.js';
10
+ import { buildAgentCommands } from './commands/agent.js';
10
11
  import { resolveAddress, isEnsName } from './ens.js';
11
12
  import fs from 'fs';
12
13
  import { getUpdateNotification, getUpgradeNotice, scheduleUpdateCheck } from './update-check.js';
14
+ import { refreshCostMapIfStale, getCostForEndpoint } from './cost-cache.js';
13
15
  import { trackCommandSucceeded, trackCommandFailed } from './telemetry.js';
14
16
  import { createRequire } from 'module';
15
17
  import * as readline from 'readline';
@@ -165,7 +167,7 @@ export function parseArgs(args) {
165
167
  const key = arg.slice(2);
166
168
  const next = args[i + 1];
167
169
 
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') {
170
+ 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' || key === 'expert' || key === 'json') {
169
171
  result.flags[key] = true;
170
172
  } else if (next && (!next.startsWith('-') || /^-\d/.test(next))) {
171
173
  // Try to parse as JSON first (for objects/arrays/booleans),
@@ -682,6 +684,7 @@ COMMANDS:
682
684
  research smart-money, profiler, token, search, perp, portfolio, points
683
685
  trade quote, execute
684
686
  wallet create, list, show, export, default, delete, forget-password
687
+ agent Ask the Nansen AI research agent (fast/expert modes)
685
688
  alerts list, create, update, toggle, delete
686
689
  web search, fetch
687
690
  account Show API key status, plan, and remaining credits
@@ -701,6 +704,10 @@ EXAMPLES:
701
704
  nansen research profiler balance --address 0x... --chain ethereum
702
705
  nansen trade quote --chain base --from ETH --to USDC --amount 1000000000000000000
703
706
 
707
+ DEPRECATED ALIASES (still work, will be removed in a future version):
708
+ smart-money, profiler, token, search, perp, portfolio, points → use "nansen research <command>"
709
+ quote, execute → use "nansen trade <command>"
710
+
704
711
  Research chains: ethereum, solana, base, bnb, arbitrum, polygon, optimism, avalanche, linea, scroll, mantle, ronin, sei, plasma, sonic, monad, hyperevm, iotaevm
705
712
  Trade chains: solana, base
706
713
  Labels: Fund, Smart Trader, 30D/90D/180D Smart Trader, Smart HL Perps Trader
@@ -1193,10 +1200,15 @@ export function buildCommands(deps = {}) {
1193
1200
  // Convenience filter for smart money only
1194
1201
  const onlySmartMoney = options['smart-money'] || flags['smart-money'] || false;
1195
1202
  if (onlySmartMoney) {
1196
- filters.include_smart_money_labels = filters.include_smart_money_labels ||
1203
+ filters.include_smart_money_labels = filters.include_smart_money_labels ||
1197
1204
  ['Fund', 'Smart Trader', '30D Smart Trader', '90D Smart Trader', '180D Smart Trader'];
1198
1205
  }
1199
1206
 
1207
+ const includeStablecoins = options['include-stablecoins'] ?? flags['include-stablecoins'];
1208
+ if (includeStablecoins !== undefined) {
1209
+ filters.include_stablecoins = includeStablecoins;
1210
+ }
1211
+
1200
1212
  const handlers = {
1201
1213
  'indicators': () => apiInstance.tokenIndicators({ tokenAddress, chain }),
1202
1214
  'ohlcv': () => apiInstance.tokenOhlcv({ tokenAddress, chain, timeframe: options.timeframe || '1d' }),
@@ -1228,7 +1240,8 @@ export function buildCommands(deps = {}) {
1228
1240
  'holders': () => apiInstance.tokenHolders({ tokenAddress, chain, labelType: onlySmartMoney ? 'smart_money' : 'all_holders', filters, orderBy, pagination }),
1229
1241
  'flows': () => {
1230
1242
  const date = parseDateOption(options.date, days);
1231
- return apiInstance.tokenFlows({ tokenAddress, chain, filters, orderBy, pagination, days, date });
1243
+ const label = options.label;
1244
+ return apiInstance.tokenFlows({ tokenAddress, chain, label, filters, orderBy, pagination, days, date });
1232
1245
  },
1233
1246
  'dex-trades': () => apiInstance.tokenDexTrades({ tokenAddress, chain, onlySmartMoney, filters, orderBy, pagination, days }),
1234
1247
  'pnl': () => apiInstance.tokenPnlLeaderboard({ tokenAddress, chain, filters, orderBy, pagination, days }),
@@ -1250,7 +1263,7 @@ export function buildCommands(deps = {}) {
1250
1263
  'help': () => ({
1251
1264
  commands: ['info', 'ohlcv', 'screener', 'holders', 'flows', 'dex-trades', 'pnl', 'who-bought-sold', 'flow-intelligence', 'transfers', 'jup-dca', 'perp-trades', 'perp-positions', 'perp-pnl-leaderboard'],
1252
1265
  description: 'Token God Mode endpoints',
1253
- example: 'nansen token screener --chain solana --timeframe 24h --smart-money'
1266
+ example: 'nansen token screener --chain solana --timeframe 24h --smart-money --include-stablecoins false'
1254
1267
  })
1255
1268
  };
1256
1269
 
@@ -1509,6 +1522,11 @@ export function generateSubcommandHelp(command, subcommand, prefix = null) {
1509
1522
  lines.push(`Params (* required): ${params.join(', ')}`);
1510
1523
  }
1511
1524
 
1525
+ if (subSchema.endpoint) {
1526
+ const cost = getCostForEndpoint(subSchema.endpoint);
1527
+ if (cost) lines.push(`Cost: ${cost.free} credit${cost.free === 1 ? '' : 's'} (Free tier) / ${cost.pro} credit${cost.pro === 1 ? '' : 's'} (Pro tier)`);
1528
+ }
1529
+
1512
1530
  if (subSchema.returns?.length) {
1513
1531
  lines.push(`Returns: ${subSchema.returns.join(', ')}`);
1514
1532
  }
@@ -1548,12 +1566,7 @@ export async function runCLI(rawArgs, deps = {}) {
1548
1566
  const subArgs = positional.slice(1);
1549
1567
  const subcommand = subArgs[0];
1550
1568
 
1551
- // Deprecation warnings for commands that moved under 'research' or 'trade'
1552
- if (DEPRECATED_TO_RESEARCH.has(command)) {
1553
- errorOutput(`Warning: "nansen ${command}" is deprecated. Use "nansen research ${command}" instead.`);
1554
- } else if (DEPRECATED_TO_TRADE.has(command)) {
1555
- errorOutput(`Warning: "nansen ${command}" is deprecated. Use "nansen trade ${command}" instead.`);
1556
- }
1569
+
1557
1570
 
1558
1571
  const pretty = flags.pretty || flags.p;
1559
1572
  const table = flags.table || flags.t;
@@ -1569,7 +1582,14 @@ export async function runCLI(rawArgs, deps = {}) {
1569
1582
  if (updateNotification) errorOutput(updateNotification);
1570
1583
  };
1571
1584
 
1572
- const commands = { ...buildCommands(deps), ...buildWalletCommands(deps), ...buildTradingCommands(deps), ...buildAlertsCommands(deps), ...commandOverrides };
1585
+ // Deprecation note for help output
1586
+ const deprecationNote = (cmd) => {
1587
+ if (DEPRECATED_TO_RESEARCH.has(cmd)) return `Note: "nansen ${cmd}" is deprecated. Use "nansen research ${cmd}" instead.\n\n`;
1588
+ if (DEPRECATED_TO_TRADE.has(cmd)) return `Note: "nansen ${cmd}" is deprecated. Use "nansen trade ${cmd}" instead.\n\n`;
1589
+ return '';
1590
+ };
1591
+
1592
+ const commands = { ...buildCommands(deps), ...buildWalletCommands(deps), ...buildTradingCommands(deps), ...buildAlertsCommands(deps), ...buildAgentCommands(deps), ...commandOverrides };
1573
1593
 
1574
1594
  if (flags.version || flags.v) {
1575
1595
  output(VERSION);
@@ -1577,6 +1597,7 @@ export async function runCLI(rawArgs, deps = {}) {
1577
1597
  }
1578
1598
 
1579
1599
  if (command === 'help' || flags.help || flags.h) {
1600
+ await refreshCostMapIfStale();
1580
1601
  // Check for subcommand-specific help: nansen <command> <subcommand> --help
1581
1602
  if (flags.help || flags.h) {
1582
1603
  // Handle 'research <category> <sub> --help' (3-level)
@@ -1607,17 +1628,17 @@ export async function runCLI(rawArgs, deps = {}) {
1607
1628
  }
1608
1629
  // First try subcommand help
1609
1630
  // Skip for 'trade'/'alerts' — their handlers show their own rich usage
1610
- if (command && subcommand && command !== 'trade' && command !== 'alerts') {
1631
+ if (command && subcommand && command !== 'trade' && command !== 'alerts' && command !== 'agent') {
1611
1632
  const subHelp = generateSubcommandHelp(command, subcommand);
1612
1633
  if (subHelp) {
1613
- output(subHelp);
1634
+ output(deprecationNote(command) + subHelp);
1614
1635
  notify();
1615
1636
  return { type: 'subcommand-help', command, subcommand };
1616
1637
  }
1617
1638
  }
1618
1639
  // Then try command-level help (list subcommands)
1619
1640
  // 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]);
1641
+ const cmdSchemaLookup = command !== 'trade' && command !== 'alerts' && command !== 'agent' && (SCHEMA.commands[command] || SCHEMA.commands.research.subcommands[command]);
1621
1642
  if (command && cmdSchemaLookup) {
1622
1643
  const cmdSchema = cmdSchemaLookup;
1623
1644
  const lines = [`${command} — ${cmdSchema.description}`];
@@ -1638,7 +1659,7 @@ export async function runCLI(rawArgs, deps = {}) {
1638
1659
  if (cmdSchema.examples?.length) {
1639
1660
  lines.push(`\nExamples:\n ${cmdSchema.examples.join('\n ')}`);
1640
1661
  }
1641
- output(lines.join('\n'));
1662
+ output(deprecationNote(command) + lines.join('\n'));
1642
1663
  notify();
1643
1664
  return { type: 'command-help', command };
1644
1665
  }
@@ -1681,7 +1702,6 @@ export async function runCLI(rawArgs, deps = {}) {
1681
1702
  const formatted = formatOutput(errorData, { pretty, table });
1682
1703
  output(formatted.text);
1683
1704
  trackCommandFailed({ command: fullCommand, duration_ms: Date.now() - startTime, error_code: 'UNKNOWN_COMMAND', flags: usedFlags, chain });
1684
- notify();
1685
1705
  exit(1);
1686
1706
  return { type: 'error', data: errorData };
1687
1707
  }
@@ -1709,7 +1729,6 @@ export async function runCLI(rawArgs, deps = {}) {
1709
1729
  // Commands that handle their own output return undefined
1710
1730
  if (result === undefined) {
1711
1731
  trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, flags: usedFlags, chain });
1712
- notify();
1713
1732
  return { type: 'no-output', command };
1714
1733
  }
1715
1734
 
@@ -1718,7 +1737,6 @@ export async function runCLI(rawArgs, deps = {}) {
1718
1737
  const formatted = formatOutput(result, { pretty, table: false });
1719
1738
  output(formatted.text);
1720
1739
  trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, flags: usedFlags, chain });
1721
- notify();
1722
1740
  return { type: 'schema', data: result };
1723
1741
  }
1724
1742
 
@@ -1731,7 +1749,6 @@ export async function runCLI(rawArgs, deps = {}) {
1731
1749
  // Alerts list with --table uses custom table format
1732
1750
  if (command === 'alerts' && subcommand === 'list' && table) {
1733
1751
  output(formatAlertsTable(result));
1734
- notify();
1735
1752
  return { type: 'success', data: result };
1736
1753
  }
1737
1754
 
@@ -1743,7 +1760,6 @@ export async function runCLI(rawArgs, deps = {}) {
1743
1760
  output(streamOutput);
1744
1761
  }
1745
1762
  trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, from_cache: !!result?.fromCache, flags: usedFlags, chain });
1746
- notify();
1747
1763
  return { type: 'stream', data: result };
1748
1764
  }
1749
1765
 
@@ -1751,7 +1767,6 @@ export async function runCLI(rawArgs, deps = {}) {
1751
1767
  const formatted = formatOutput(successData, { pretty, table, csv });
1752
1768
  output(formatted.text);
1753
1769
  trackCommandSucceeded({ command: fullCommand, duration_ms: Date.now() - startTime, from_cache: !!result?.fromCache, flags: usedFlags, chain });
1754
- notify();
1755
1770
  return { type: csv ? 'csv' : 'success', data: result };
1756
1771
  } catch (error) {
1757
1772
  const errorData = formatError(error);
@@ -1765,7 +1780,6 @@ export async function runCLI(rawArgs, deps = {}) {
1765
1780
  flags: usedFlags,
1766
1781
  chain,
1767
1782
  });
1768
- notify();
1769
1783
  exit(1);
1770
1784
  return { type: 'error', data: errorData };
1771
1785
  }