nansen-cli 1.17.0 → 1.19.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 +41 -0
- package/package.json +5 -3
- package/skills/nansen-dca-tracker/SKILL.md +38 -0
- package/skills/nansen-defi-positions/SKILL.md +37 -0
- package/skills/nansen-exit-signals/SKILL.md +39 -0
- package/skills/nansen-fund-tracker/SKILL.md +35 -0
- package/skills/nansen-general-search/SKILL.md +34 -0
- package/skills/nansen-holder-analysis/SKILL.md +38 -0
- package/skills/nansen-perp-screener/SKILL.md +32 -0
- package/skills/nansen-perp-trader-profile/SKILL.md +39 -0
- package/skills/nansen-polymarket-deep-dive/SKILL.md +50 -0
- package/skills/nansen-polymarket-insider-scan/SKILL.md +62 -0
- package/skills/nansen-polymarket-trader-profile/SKILL.md +43 -0
- package/skills/nansen-portfolio-tracker/SKILL.md +36 -0
- package/skills/nansen-prediction-markets/SKILL.md +47 -0
- package/skills/nansen-sm-cross-chain-flows/SKILL.md +27 -0
- package/skills/nansen-smart-alerts/SKILL.md +137 -0
- package/skills/nansen-smart-money-alpha/SKILL.md +43 -0
- package/skills/nansen-smart-money-tracker/SKILL.md +71 -0
- package/skills/nansen-smart-money-trend/SKILL.md +30 -0
- package/skills/nansen-token-research/SKILL.md +90 -0
- package/skills/nansen-token-screener/SKILL.md +54 -0
- package/skills/nansen-token-transfer-analysis/SKILL.md +40 -0
- package/skills/nansen-trading/SKILL.md +100 -0
- package/skills/nansen-wallet-batch/SKILL.md +26 -0
- package/skills/nansen-wallet-clustering/REFERENCE.md +43 -0
- package/skills/nansen-wallet-clustering/SKILL.md +46 -0
- package/skills/nansen-wallet-deep-dive/SKILL.md +45 -0
- package/skills/nansen-wallet-keychain-migration/SKILL.md +183 -0
- package/skills/nansen-wallet-manager/SKILL.md +140 -0
- package/skills/nansen-wallet-profiler/SKILL.md +98 -0
- package/skills/nansen-web-fetcher/SKILL.md +50 -0
- package/skills/nansen-web-searcher/SKILL.md +39 -0
- package/src/api.js +76 -3
- package/src/cli.js +176 -14
- package/src/commands/alerts.js +717 -0
- package/src/schema.json +164 -1
- package/src/telemetry.js +237 -0
- package/src/update-check.js +2 -2
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": {
|
package/src/telemetry.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight CLI telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Sends anonymous usage events so we can understand which commands are used,
|
|
5
|
+
* how long they take, and where errors occur. Events are fire-and-forget —
|
|
6
|
+
* failures are silently ignored and never block the CLI.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import crypto from 'crypto';
|
|
13
|
+
import os from 'os';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
|
|
18
|
+
const { version: cliVersion } = JSON.parse(
|
|
19
|
+
fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const TELEMETRY_URL =
|
|
23
|
+
'https://bi-data-sources.nansen.ai/events-service-68ifmnpsx2uq7cgab8dw/v2/event';
|
|
24
|
+
|
|
25
|
+
const TIMEOUT_MS = 2000;
|
|
26
|
+
|
|
27
|
+
// ─── opt-out ──────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
export const TELEMETRY_DISABLED =
|
|
30
|
+
process.env.DO_NOT_TRACK === '1' || process.env.NANSEN_NO_TELEMETRY === '1';
|
|
31
|
+
|
|
32
|
+
// ─── environment ──────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Infer prod vs dev from NANSEN_BASE_URL env var.
|
|
36
|
+
* Only engineers pointing at a local/staging API will have this set.
|
|
37
|
+
*/
|
|
38
|
+
function getEventSource() {
|
|
39
|
+
const baseUrl = process.env.NANSEN_BASE_URL || '';
|
|
40
|
+
return baseUrl && !baseUrl.includes('api.nansen.ai') ? 'cli_dev' : 'cli_prod';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ─── system info ──────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
const SYSTEM_NAMES = { Darwin: 'macos', Linux: 'linux', Windows_NT: 'windows' };
|
|
46
|
+
|
|
47
|
+
function getSystemName() {
|
|
48
|
+
return SYSTEM_NAMES[os.type()] || os.type().toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ─── identity ──────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
const TELEMETRY_ID_FILE = path.join(
|
|
54
|
+
process.env.HOME || process.env.USERPROFILE || '',
|
|
55
|
+
'.nansen',
|
|
56
|
+
'telemetry-id'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get or create a persistent random anonymous_id stored in ~/.nansen/telemetry-id.
|
|
61
|
+
*/
|
|
62
|
+
let _anonymousId;
|
|
63
|
+
export function getAnonymousId() {
|
|
64
|
+
if (_anonymousId === undefined) {
|
|
65
|
+
try {
|
|
66
|
+
_anonymousId = fs.readFileSync(TELEMETRY_ID_FILE, 'utf8').trim();
|
|
67
|
+
} catch {
|
|
68
|
+
_anonymousId = crypto.randomUUID();
|
|
69
|
+
try {
|
|
70
|
+
fs.mkdirSync(path.dirname(TELEMETRY_ID_FILE), { recursive: true });
|
|
71
|
+
fs.writeFileSync(TELEMETRY_ID_FILE, _anonymousId, 'utf8');
|
|
72
|
+
} catch { /* best-effort persist */ }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return _anonymousId;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─── session ───────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
const SESSION_FILE = path.join(
|
|
81
|
+
process.env.HOME || process.env.USERPROFILE || '',
|
|
82
|
+
'.nansen',
|
|
83
|
+
'session'
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get or create a session ID. The session rotates after 30 min of inactivity.
|
|
90
|
+
* Callers can override via NANSEN_SESSION_ID env var.
|
|
91
|
+
*/
|
|
92
|
+
let _sessionId;
|
|
93
|
+
export function getSessionId() {
|
|
94
|
+
if (_sessionId !== undefined) return _sessionId;
|
|
95
|
+
|
|
96
|
+
if (process.env.NANSEN_SESSION_ID) {
|
|
97
|
+
_sessionId = process.env.NANSEN_SESSION_ID;
|
|
98
|
+
return _sessionId;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const now = Date.now();
|
|
102
|
+
try {
|
|
103
|
+
const raw = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf8'));
|
|
104
|
+
if (raw.id && raw.ts && now - raw.ts < SESSION_TIMEOUT_MS) {
|
|
105
|
+
_sessionId = raw.id;
|
|
106
|
+
// touch timestamp, but only if >1 min elapsed to reduce writes
|
|
107
|
+
if (now - raw.ts > 60_000) {
|
|
108
|
+
try { fs.writeFileSync(SESSION_FILE, JSON.stringify({ id: _sessionId, ts: now }), 'utf8'); } catch { /* best-effort touch */ }
|
|
109
|
+
}
|
|
110
|
+
return _sessionId;
|
|
111
|
+
}
|
|
112
|
+
} catch { /* missing or corrupt → new session */ }
|
|
113
|
+
|
|
114
|
+
_sessionId = crypto.randomUUID();
|
|
115
|
+
try {
|
|
116
|
+
fs.mkdirSync(path.dirname(SESSION_FILE), { recursive: true });
|
|
117
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify({ id: _sessionId, ts: now }), 'utf8');
|
|
118
|
+
} catch { /* best-effort */ }
|
|
119
|
+
return _sessionId;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ─── send ──────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Send a telemetry event. Fire-and-forget — never throws.
|
|
126
|
+
*/
|
|
127
|
+
function sendEvent(event) {
|
|
128
|
+
if (TELEMETRY_DISABLED) return;
|
|
129
|
+
const controller = new AbortController();
|
|
130
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
131
|
+
timer.unref();
|
|
132
|
+
|
|
133
|
+
fetch(TELEMETRY_URL, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: { 'Content-Type': 'application/json' },
|
|
136
|
+
body: JSON.stringify(event),
|
|
137
|
+
signal: controller.signal,
|
|
138
|
+
})
|
|
139
|
+
.catch(() => {}) // swallow errors
|
|
140
|
+
.finally(() => clearTimeout(timer));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── context ───────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
function buildContext() {
|
|
146
|
+
return {
|
|
147
|
+
client_type: 'nansen-cli',
|
|
148
|
+
client_version: cliVersion,
|
|
149
|
+
system_name: getSystemName(),
|
|
150
|
+
system_version: os.release(),
|
|
151
|
+
node_version: process.version,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ─── public API ────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Convert a command string like "smart-money netflow" to a path like "/smart-money/netflow".
|
|
159
|
+
*/
|
|
160
|
+
function commandToPath(command) {
|
|
161
|
+
return '/' + command.replace(/\s+/g, '/');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Track a CLI command that completed successfully.
|
|
166
|
+
*
|
|
167
|
+
* @param {object} opts
|
|
168
|
+
* @param {string} opts.command - Full command string, e.g. "smart-money netflow"
|
|
169
|
+
* @param {number} opts.duration_ms - Wall-clock execution time
|
|
170
|
+
* @param {boolean} [opts.from_cache] - Whether result was served from cache
|
|
171
|
+
* @param {string[]} [opts.flags] - Flag names used (no values), e.g. ["--chain", "--pretty"]
|
|
172
|
+
* @param {string|null} [opts.chain] - Chain name if specified, e.g. "ethereum", "solana"
|
|
173
|
+
*/
|
|
174
|
+
export function trackCommandSucceeded({
|
|
175
|
+
command,
|
|
176
|
+
duration_ms,
|
|
177
|
+
from_cache = false,
|
|
178
|
+
flags = [],
|
|
179
|
+
chain = null,
|
|
180
|
+
}) {
|
|
181
|
+
sendEvent({
|
|
182
|
+
event: 'cli_command_succeeded',
|
|
183
|
+
event_source: getEventSource(),
|
|
184
|
+
event_id: crypto.randomUUID(),
|
|
185
|
+
user_id: null,
|
|
186
|
+
anonymous_id: getAnonymousId(),
|
|
187
|
+
session_id: getSessionId(),
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
path: commandToPath(command),
|
|
190
|
+
properties: {
|
|
191
|
+
latency: duration_ms / 1000,
|
|
192
|
+
from_cache,
|
|
193
|
+
flags,
|
|
194
|
+
...(chain ? { chain } : {}),
|
|
195
|
+
},
|
|
196
|
+
context: buildContext(),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Track a CLI command that failed.
|
|
202
|
+
*
|
|
203
|
+
* @param {object} opts
|
|
204
|
+
* @param {string} opts.command - Full command string
|
|
205
|
+
* @param {number} opts.duration_ms - Wall-clock execution time
|
|
206
|
+
* @param {string} opts.error_code - Structured error code (from ErrorCode or custom)
|
|
207
|
+
* @param {number|null} [opts.status] - HTTP status if the error came from the API
|
|
208
|
+
* @param {string[]} [opts.flags] - Flag names used
|
|
209
|
+
* @param {string|null} [opts.chain] - Chain name if specified
|
|
210
|
+
*/
|
|
211
|
+
export function trackCommandFailed({
|
|
212
|
+
command,
|
|
213
|
+
duration_ms,
|
|
214
|
+
error_code,
|
|
215
|
+
status = null,
|
|
216
|
+
flags = [],
|
|
217
|
+
chain = null,
|
|
218
|
+
}) {
|
|
219
|
+
sendEvent({
|
|
220
|
+
event: 'cli_command_failed',
|
|
221
|
+
event_source: getEventSource(),
|
|
222
|
+
event_id: crypto.randomUUID(),
|
|
223
|
+
user_id: null,
|
|
224
|
+
anonymous_id: getAnonymousId(),
|
|
225
|
+
session_id: getSessionId(),
|
|
226
|
+
timestamp: new Date().toISOString(),
|
|
227
|
+
path: commandToPath(command),
|
|
228
|
+
properties: {
|
|
229
|
+
latency: duration_ms / 1000,
|
|
230
|
+
error_code,
|
|
231
|
+
status,
|
|
232
|
+
flags,
|
|
233
|
+
...(chain ? { chain } : {}),
|
|
234
|
+
},
|
|
235
|
+
context: buildContext(),
|
|
236
|
+
});
|
|
237
|
+
}
|
package/src/update-check.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import path from 'path';
|
|
10
|
-
import
|
|
10
|
+
import childProcess from 'child_process';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -119,7 +119,7 @@ export function scheduleUpdateCheck() {
|
|
|
119
119
|
req.setTimeout(5000, () => req.destroy());
|
|
120
120
|
`;
|
|
121
121
|
|
|
122
|
-
const child = spawn(process.execPath, ['-e', script], {
|
|
122
|
+
const child = childProcess.spawn(process.execPath, ['-e', script], {
|
|
123
123
|
detached: true,
|
|
124
124
|
stdio: 'ignore'
|
|
125
125
|
});
|