@neus/sdk 1.0.4 → 1.0.6
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/README.md +21 -4
- package/SECURITY.md +11 -6
- package/cjs/client.cjs +132 -220
- package/cjs/gates.cjs +10 -33
- package/cjs/index.cjs +143 -252
- package/cjs/utils.cjs +0 -6
- package/cli/neus.mjs +589 -41
- package/client.js +1834 -1955
- package/errors.js +0 -34
- package/gates.js +73 -175
- package/index.js +0 -9
- package/package.json +5 -4
- package/types.d.ts +14 -459
- package/utils.js +1 -265
- package/widgets/README.md +45 -45
- package/widgets/index.js +0 -8
- package/widgets/verify-gate/dist/ProofBadge.js +0 -3
- package/widgets/verify-gate/dist/VerifyGate.js +58 -78
- package/widgets/verify-gate/index.js +0 -4
package/cjs/utils.cjs
CHANGED
|
@@ -287,7 +287,6 @@ function createVerificationData(content, owner, reference = null) {
|
|
|
287
287
|
content,
|
|
288
288
|
owner: validateWalletAddress(owner) ? owner.toLowerCase() : owner,
|
|
289
289
|
reference: reference || {
|
|
290
|
-
// Must be a valid backend enum value; 'content' is not supported.
|
|
291
290
|
type: "other",
|
|
292
291
|
id: stableRefId(content)
|
|
293
292
|
}
|
|
@@ -761,9 +760,7 @@ var StatusPoller = class {
|
|
|
761
760
|
}
|
|
762
761
|
};
|
|
763
762
|
var NEUS_CONSTANTS = {
|
|
764
|
-
/** Default EVM chain id for NEUS protocol signing context (`HUB_CHAIN_ID` name kept for compatibility). */
|
|
765
763
|
HUB_CHAIN_ID: 84532,
|
|
766
|
-
// Supported target chains for cross-chain propagation
|
|
767
764
|
TESTNET_CHAINS: [
|
|
768
765
|
11155111,
|
|
769
766
|
// Ethereum Sepolia
|
|
@@ -774,15 +771,12 @@ var NEUS_CONSTANTS = {
|
|
|
774
771
|
80002
|
|
775
772
|
// Polygon Amoy
|
|
776
773
|
],
|
|
777
|
-
// API endpoints
|
|
778
774
|
API_BASE_URL: "https://api.neus.network",
|
|
779
775
|
API_VERSION: "v1",
|
|
780
|
-
// Timeouts and limits
|
|
781
776
|
SIGNATURE_MAX_AGE_MS: 5 * 60 * 1e3,
|
|
782
777
|
// 5 minutes
|
|
783
778
|
REQUEST_TIMEOUT_MS: 30 * 1e3,
|
|
784
779
|
// 30 seconds
|
|
785
|
-
// Default verifier set for quick starts
|
|
786
780
|
DEFAULT_VERIFIERS: [
|
|
787
781
|
"ownership-basic",
|
|
788
782
|
"nft-ownership",
|
package/cli/neus.mjs
CHANGED
|
@@ -1,58 +1,606 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const NEUS_SERVER_NAME = 'neus';
|
|
8
|
+
const NEUS_MCP_URL = 'https://mcp.neus.network/mcp';
|
|
9
|
+
const NEUS_ACCESS_KEYS_URL = 'https://neus.network/profile?tab=account';
|
|
10
|
+
const SUPPORTED_CLIENTS = ['claude', 'cursor', 'vscode'];
|
|
11
|
+
|
|
12
|
+
function fileExists(targetPath) {
|
|
13
|
+
try {
|
|
14
|
+
fs.accessSync(targetPath);
|
|
15
|
+
return true;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
9
19
|
}
|
|
10
20
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
function jsonStringify(value) {
|
|
22
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function readJsonFile(targetPath, fallback) {
|
|
26
|
+
if (!fileExists(targetPath)) return fallback;
|
|
27
|
+
const raw = fs.readFileSync(targetPath, 'utf8');
|
|
28
|
+
const parsed = JSON.parse(raw);
|
|
29
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : fallback;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function writeJsonFile(targetPath, nextValue, dryRun) {
|
|
33
|
+
const serialized = jsonStringify(nextValue);
|
|
34
|
+
const hadExistingFile = fileExists(targetPath);
|
|
35
|
+
const previous = hadExistingFile ? fs.readFileSync(targetPath, 'utf8') : null;
|
|
36
|
+
const changed = previous !== serialized;
|
|
37
|
+
const backupPath = hadExistingFile && changed ? `${targetPath}.bak` : null;
|
|
38
|
+
|
|
39
|
+
if (!dryRun && changed) {
|
|
40
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
41
|
+
if (backupPath) {
|
|
42
|
+
fs.copyFileSync(targetPath, backupPath);
|
|
43
|
+
}
|
|
44
|
+
fs.writeFileSync(targetPath, serialized, 'utf8');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
changed,
|
|
49
|
+
targetPath,
|
|
50
|
+
backupPath,
|
|
51
|
+
dryRun,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function resolveCommand(command) {
|
|
56
|
+
const checker = process.platform === 'win32' ? 'where' : 'which';
|
|
57
|
+
const result = spawnSync(checker, [command], {
|
|
58
|
+
encoding: 'utf8',
|
|
59
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
60
|
+
});
|
|
61
|
+
if (result.status !== 0) return null;
|
|
62
|
+
const firstMatch = result.stdout
|
|
63
|
+
.split(/\r?\n/)
|
|
64
|
+
.map((line) => line.trim())
|
|
65
|
+
.find(Boolean);
|
|
66
|
+
return firstMatch || null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function runCommand(command, args, cwd, tolerateFailure = false) {
|
|
70
|
+
const resolvedCommand = resolveCommand(command) || command;
|
|
71
|
+
const isWindowsScript = process.platform === 'win32' && /\.(cmd|bat)$/i.test(resolvedCommand);
|
|
72
|
+
const result = isWindowsScript
|
|
73
|
+
? spawnSync(
|
|
74
|
+
process.env.ComSpec || 'cmd.exe',
|
|
75
|
+
['/d', '/s', '/c', resolvedCommand, ...args],
|
|
76
|
+
{
|
|
77
|
+
cwd,
|
|
78
|
+
encoding: 'utf8',
|
|
79
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
80
|
+
},
|
|
81
|
+
)
|
|
82
|
+
: spawnSync(resolvedCommand, args, {
|
|
83
|
+
cwd,
|
|
84
|
+
encoding: 'utf8',
|
|
85
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (result.error && !tolerateFailure) {
|
|
89
|
+
throw result.error;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (result.status !== 0 && !tolerateFailure) {
|
|
93
|
+
const detail = [result.stderr, result.stdout].find((value) => typeof value === 'string' && value.trim()) || '';
|
|
94
|
+
throw new Error(detail.trim() || `Command failed: ${command} ${args.join(' ')}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function commandExists(command) {
|
|
101
|
+
return Boolean(resolveCommand(command));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function cursorInstalled() {
|
|
105
|
+
const homeDir = os.homedir();
|
|
106
|
+
const appData = process.env.APPDATA || '';
|
|
107
|
+
const localAppData = process.env.LOCALAPPDATA || '';
|
|
108
|
+
return [
|
|
109
|
+
path.join(homeDir, '.cursor'),
|
|
110
|
+
path.join(appData, 'Cursor'),
|
|
111
|
+
path.join(localAppData, 'Programs', 'Cursor', 'Cursor.exe'),
|
|
112
|
+
].some(fileExists);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function defaultUserClients() {
|
|
116
|
+
const detected = [];
|
|
117
|
+
if (commandExists('claude')) detected.push('claude');
|
|
118
|
+
if (cursorInstalled()) detected.push('cursor');
|
|
119
|
+
if (commandExists('code') || fileExists(path.join(process.env.APPDATA || '', 'Code'))) detected.push('vscode');
|
|
120
|
+
return detected;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function parseClientOption(raw) {
|
|
124
|
+
return String(raw || '')
|
|
125
|
+
.split(',')
|
|
126
|
+
.map((value) => value.trim().toLowerCase())
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function parseArgs(argv) {
|
|
131
|
+
if (argv.length === 0) {
|
|
132
|
+
return {
|
|
133
|
+
command: 'help',
|
|
134
|
+
options: {
|
|
135
|
+
accessKey: process.env.NEUS_ACCESS_KEY || '',
|
|
136
|
+
clients: [],
|
|
137
|
+
json: false,
|
|
138
|
+
dryRun: false,
|
|
139
|
+
project: false,
|
|
17
140
|
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const command = argv[0];
|
|
145
|
+
const options = {
|
|
146
|
+
accessKey: process.env.NEUS_ACCESS_KEY || '',
|
|
147
|
+
clients: [],
|
|
148
|
+
json: false,
|
|
149
|
+
dryRun: false,
|
|
150
|
+
project: false,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
154
|
+
const token = argv[index];
|
|
155
|
+
if (token === '--json') {
|
|
156
|
+
options.json = true;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (token === '--dry-run') {
|
|
160
|
+
options.dryRun = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (token === '--project') {
|
|
164
|
+
options.project = true;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (token === '--client') {
|
|
168
|
+
const value = argv[index + 1];
|
|
169
|
+
if (!value) throw new Error('--client requires a value');
|
|
170
|
+
options.clients.push(...parseClientOption(value));
|
|
171
|
+
index += 1;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (token === '--access-key') {
|
|
175
|
+
const value = argv[index + 1];
|
|
176
|
+
if (!value) throw new Error('--access-key requires a value');
|
|
177
|
+
options.accessKey = value;
|
|
178
|
+
index += 1;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (token === '--help' || token === '-h') {
|
|
182
|
+
return { command: 'help', options };
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`Unknown option: ${token}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
options.accessKey = String(options.accessKey || '').trim();
|
|
188
|
+
options.clients = [...new Set(options.clients)];
|
|
189
|
+
|
|
190
|
+
return { command, options };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function printUsage(exitCode = 0) {
|
|
194
|
+
const lines = [
|
|
195
|
+
'Usage: neus <command> [options]',
|
|
196
|
+
'',
|
|
197
|
+
'Commands:',
|
|
198
|
+
' init Configure supported MCP clients automatically',
|
|
199
|
+
' auth Add or update a personal access key for NEUS MCP',
|
|
200
|
+
' status Show current NEUS MCP setup',
|
|
201
|
+
' help Show this message',
|
|
202
|
+
'',
|
|
203
|
+
'Options:',
|
|
204
|
+
' --client <name[,name]> Limit setup to claude, cursor, or vscode',
|
|
205
|
+
' --project Write shared project config instead of user config',
|
|
206
|
+
' --access-key <npk_...> Configure Bearer auth for personal account tools',
|
|
207
|
+
' --json Emit machine-readable output',
|
|
208
|
+
' --dry-run Preview changes without writing files',
|
|
209
|
+
];
|
|
210
|
+
const stream = exitCode === 0 ? process.stdout : process.stderr;
|
|
211
|
+
stream.write(`${lines.join('\n')}\n`);
|
|
212
|
+
process.exit(exitCode);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function assertValidClients(clients) {
|
|
216
|
+
for (const client of clients) {
|
|
217
|
+
if (!SUPPORTED_CLIENTS.includes(client)) {
|
|
218
|
+
throw new Error(`Unsupported client: ${client}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function resolveScope(options) {
|
|
224
|
+
return options.project ? 'project' : 'user';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function resolveClients(scope, requestedClients) {
|
|
228
|
+
assertValidClients(requestedClients);
|
|
229
|
+
if (requestedClients.length > 0) return requestedClients;
|
|
230
|
+
if (scope === 'project') return [...SUPPORTED_CLIENTS];
|
|
231
|
+
return defaultUserClients();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function ensureClientSelection(scope, clients) {
|
|
235
|
+
if (clients.length > 0) return;
|
|
236
|
+
if (scope === 'project') return;
|
|
237
|
+
throw new Error('No supported clients detected. Re-run with --project or use --client to target a specific client.');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function ensureSafeAuth(command, scope, accessKey) {
|
|
241
|
+
if (command === 'auth' && scope !== 'user') {
|
|
242
|
+
throw new Error('`neus auth` only supports user scope so access keys never land in shared project config.');
|
|
243
|
+
}
|
|
244
|
+
if (scope === 'project' && accessKey) {
|
|
245
|
+
throw new Error('Access keys are only supported in user scope. Remove --project or omit --access-key.');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function buildCursorServer(accessKey) {
|
|
250
|
+
return {
|
|
251
|
+
type: 'streamableHttp',
|
|
252
|
+
url: NEUS_MCP_URL,
|
|
253
|
+
...(accessKey ? { headers: { Authorization: `Bearer ${accessKey}` } } : {}),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function buildVsCodeServer(accessKey) {
|
|
258
|
+
return {
|
|
259
|
+
type: 'http',
|
|
260
|
+
url: NEUS_MCP_URL,
|
|
261
|
+
...(accessKey ? { headers: { Authorization: `Bearer ${accessKey}` } } : {}),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function buildClaudeServer(accessKey) {
|
|
266
|
+
return {
|
|
267
|
+
type: 'http',
|
|
268
|
+
url: NEUS_MCP_URL,
|
|
269
|
+
...(accessKey ? { headers: { Authorization: `Bearer ${accessKey}` } } : {}),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function cursorConfigPath(scope, cwd) {
|
|
274
|
+
return scope === 'user'
|
|
275
|
+
? path.join(os.homedir(), '.cursor', 'mcp.json')
|
|
276
|
+
: path.join(cwd, '.cursor', 'mcp.json');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function vscodeConfigPath(scope, cwd) {
|
|
280
|
+
return scope === 'user'
|
|
281
|
+
? path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Code', 'User', 'mcp.json')
|
|
282
|
+
: path.join(cwd, '.vscode', 'mcp.json');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function claudeProjectConfigPath(cwd) {
|
|
286
|
+
return path.join(cwd, '.mcp.json');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function installCursor(scope, accessKey, dryRun, cwd) {
|
|
290
|
+
const targetPath = cursorConfigPath(scope, cwd);
|
|
291
|
+
const doc = readJsonFile(targetPath, { mcpServers: {} });
|
|
292
|
+
const next = {
|
|
293
|
+
...doc,
|
|
294
|
+
mcpServers: {
|
|
295
|
+
...(doc.mcpServers && typeof doc.mcpServers === 'object' && !Array.isArray(doc.mcpServers) ? doc.mcpServers : {}),
|
|
296
|
+
[NEUS_SERVER_NAME]: buildCursorServer(accessKey),
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
const writeResult = writeJsonFile(targetPath, next, dryRun);
|
|
300
|
+
return {
|
|
301
|
+
client: 'cursor',
|
|
302
|
+
scope,
|
|
303
|
+
configured: true,
|
|
304
|
+
authConfigured: Boolean(accessKey),
|
|
305
|
+
changed: writeResult.changed,
|
|
306
|
+
targetPath,
|
|
307
|
+
backupPath: writeResult.backupPath,
|
|
308
|
+
dryRun,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function installVsCode(scope, accessKey, dryRun, cwd) {
|
|
313
|
+
const targetPath = vscodeConfigPath(scope, cwd);
|
|
314
|
+
const doc = readJsonFile(targetPath, { servers: {} });
|
|
315
|
+
const next = {
|
|
316
|
+
...doc,
|
|
317
|
+
servers: {
|
|
318
|
+
...(doc.servers && typeof doc.servers === 'object' && !Array.isArray(doc.servers) ? doc.servers : {}),
|
|
319
|
+
[NEUS_SERVER_NAME]: buildVsCodeServer(accessKey),
|
|
18
320
|
},
|
|
19
321
|
};
|
|
322
|
+
const writeResult = writeJsonFile(targetPath, next, dryRun);
|
|
323
|
+
return {
|
|
324
|
+
client: 'vscode',
|
|
325
|
+
scope,
|
|
326
|
+
configured: true,
|
|
327
|
+
authConfigured: Boolean(accessKey),
|
|
328
|
+
changed: writeResult.changed,
|
|
329
|
+
targetPath,
|
|
330
|
+
backupPath: writeResult.backupPath,
|
|
331
|
+
dryRun,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function installClaudeProject(scope, accessKey, dryRun, cwd) {
|
|
336
|
+
const targetPath = claudeProjectConfigPath(cwd);
|
|
337
|
+
const doc = readJsonFile(targetPath, { mcpServers: {} });
|
|
338
|
+
const next = {
|
|
339
|
+
...doc,
|
|
340
|
+
mcpServers: {
|
|
341
|
+
...(doc.mcpServers && typeof doc.mcpServers === 'object' && !Array.isArray(doc.mcpServers) ? doc.mcpServers : {}),
|
|
342
|
+
[NEUS_SERVER_NAME]: buildClaudeServer(accessKey),
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
const writeResult = writeJsonFile(targetPath, next, dryRun);
|
|
346
|
+
return {
|
|
347
|
+
client: 'claude',
|
|
348
|
+
scope,
|
|
349
|
+
configured: true,
|
|
350
|
+
authConfigured: Boolean(accessKey),
|
|
351
|
+
changed: writeResult.changed,
|
|
352
|
+
targetPath,
|
|
353
|
+
backupPath: writeResult.backupPath,
|
|
354
|
+
dryRun,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function installClaudeUser(scope, accessKey, dryRun, cwd) {
|
|
359
|
+
if (!commandExists('claude')) {
|
|
360
|
+
throw new Error('Claude Code CLI is not installed or not on PATH.');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (!dryRun) {
|
|
364
|
+
runCommand('claude', ['mcp', 'remove', '--scope', 'user', NEUS_SERVER_NAME], cwd, true);
|
|
365
|
+
const addArgs = [
|
|
366
|
+
'mcp',
|
|
367
|
+
'add',
|
|
368
|
+
'--transport',
|
|
369
|
+
'http',
|
|
370
|
+
'--scope',
|
|
371
|
+
'user',
|
|
372
|
+
NEUS_SERVER_NAME,
|
|
373
|
+
NEUS_MCP_URL,
|
|
374
|
+
];
|
|
375
|
+
if (accessKey) {
|
|
376
|
+
addArgs.push('--header', `Authorization: Bearer ${accessKey}`);
|
|
377
|
+
}
|
|
378
|
+
runCommand('claude', addArgs, cwd);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
client: 'claude',
|
|
383
|
+
scope,
|
|
384
|
+
configured: true,
|
|
385
|
+
authConfigured: Boolean(accessKey),
|
|
386
|
+
changed: true,
|
|
387
|
+
targetPath: '~/.claude.json',
|
|
388
|
+
backupPath: null,
|
|
389
|
+
dryRun,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function installClaude(scope, accessKey, dryRun, cwd) {
|
|
394
|
+
if (scope === 'project') {
|
|
395
|
+
return installClaudeProject(scope, accessKey, dryRun, cwd);
|
|
396
|
+
}
|
|
397
|
+
return installClaudeUser(scope, accessKey, dryRun, cwd);
|
|
398
|
+
}
|
|
20
399
|
|
|
400
|
+
function installClient(client, scope, accessKey, dryRun, cwd) {
|
|
401
|
+
if (client === 'cursor') return installCursor(scope, accessKey, dryRun, cwd);
|
|
402
|
+
if (client === 'vscode') return installVsCode(scope, accessKey, dryRun, cwd);
|
|
403
|
+
if (client === 'claude') return installClaude(scope, accessKey, dryRun, cwd);
|
|
404
|
+
throw new Error(`Unsupported client: ${client}`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function inspectCursor(scope, cwd) {
|
|
408
|
+
const targetPath = cursorConfigPath(scope, cwd);
|
|
409
|
+
if (!fileExists(targetPath)) {
|
|
410
|
+
return { client: 'cursor', scope, configured: false, authConfigured: false, targetPath };
|
|
411
|
+
}
|
|
412
|
+
const doc = readJsonFile(targetPath, {});
|
|
413
|
+
const server = doc.mcpServers?.[NEUS_SERVER_NAME];
|
|
414
|
+
return {
|
|
415
|
+
client: 'cursor',
|
|
416
|
+
scope,
|
|
417
|
+
configured: Boolean(server && server.url === NEUS_MCP_URL),
|
|
418
|
+
authConfigured: Boolean(server?.headers?.Authorization),
|
|
419
|
+
targetPath,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function inspectVsCode(scope, cwd) {
|
|
424
|
+
const targetPath = vscodeConfigPath(scope, cwd);
|
|
425
|
+
if (!fileExists(targetPath)) {
|
|
426
|
+
return { client: 'vscode', scope, configured: false, authConfigured: false, targetPath };
|
|
427
|
+
}
|
|
428
|
+
const doc = readJsonFile(targetPath, {});
|
|
429
|
+
const server = doc.servers?.[NEUS_SERVER_NAME];
|
|
430
|
+
return {
|
|
431
|
+
client: 'vscode',
|
|
432
|
+
scope,
|
|
433
|
+
configured: Boolean(server && server.url === NEUS_MCP_URL),
|
|
434
|
+
authConfigured: Boolean(server?.headers?.Authorization),
|
|
435
|
+
targetPath,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function inspectClaude(scope, cwd) {
|
|
440
|
+
if (scope === 'project') {
|
|
441
|
+
const targetPath = claudeProjectConfigPath(cwd);
|
|
442
|
+
if (!fileExists(targetPath)) {
|
|
443
|
+
return { client: 'claude', scope, configured: false, authConfigured: false, targetPath };
|
|
444
|
+
}
|
|
445
|
+
const doc = readJsonFile(targetPath, {});
|
|
446
|
+
const server = doc.mcpServers?.[NEUS_SERVER_NAME];
|
|
447
|
+
return {
|
|
448
|
+
client: 'claude',
|
|
449
|
+
scope,
|
|
450
|
+
configured: Boolean(server && server.url === NEUS_MCP_URL),
|
|
451
|
+
authConfigured: Boolean(server?.headers?.Authorization),
|
|
452
|
+
targetPath,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!commandExists('claude')) {
|
|
457
|
+
return { client: 'claude', scope, configured: false, authConfigured: null, targetPath: '~/.claude.json' };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const result = runCommand('claude', ['mcp', 'list'], cwd, true);
|
|
461
|
+
const configured = result.status === 0 && result.stdout.split(/\r?\n/).some((line) => line.trim() === NEUS_SERVER_NAME);
|
|
462
|
+
return {
|
|
463
|
+
client: 'claude',
|
|
464
|
+
scope,
|
|
465
|
+
configured,
|
|
466
|
+
authConfigured: null,
|
|
467
|
+
targetPath: '~/.claude.json',
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function inspectClient(client, scope, cwd) {
|
|
472
|
+
if (client === 'cursor') return inspectCursor(scope, cwd);
|
|
473
|
+
if (client === 'vscode') return inspectVsCode(scope, cwd);
|
|
474
|
+
if (client === 'claude') return inspectClaude(scope, cwd);
|
|
475
|
+
throw new Error(`Unsupported client: ${client}`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function printJson(payload) {
|
|
479
|
+
process.stdout.write(jsonStringify(payload));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function printResultSummary(command, scope, results, accessKey) {
|
|
483
|
+
const changedCount = results.filter((result) => result.changed).length;
|
|
484
|
+
const configuredClients = results.map((result) => result.client).join(', ');
|
|
21
485
|
const lines = [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"## MCP",
|
|
25
|
-
JSON.stringify(mcpBlock, null, 2),
|
|
26
|
-
"",
|
|
27
|
-
"## URLs",
|
|
28
|
-
"MCP: https://mcp.neus.network/mcp",
|
|
29
|
-
"Verify: https://neus.network/verify",
|
|
30
|
-
"Explorer: https://neus.network/hub",
|
|
31
|
-
"",
|
|
32
|
-
"## Docs",
|
|
33
|
-
"MCP setup: https://docs.neus.network/mcp/setup",
|
|
34
|
-
"LLM / assistants: https://docs.neus.network/platform/llm-docs",
|
|
35
|
-
"Agents: https://docs.neus.network/agents/overview",
|
|
36
|
-
"Agent identity: https://docs.neus.network/agents/agent-identity",
|
|
37
|
-
"Agent delegation: https://docs.neus.network/agents/agent-delegation",
|
|
38
|
-
"Integration: https://docs.neus.network/integration",
|
|
39
|
-
"Quickstart: https://docs.neus.network/quickstart",
|
|
40
|
-
"Machine route map: https://neus.network/llms.txt",
|
|
41
|
-
"",
|
|
42
|
-
"This command only prints to stdout; it does not modify files or create accounts.",
|
|
43
|
-
"Use Authorization: Bearer <access key> when a tool returns auth_required (see MCP auth).",
|
|
486
|
+
`NEUS ${command} completed for ${results.length} client${results.length === 1 ? '' : 's'} in ${scope} scope.`,
|
|
487
|
+
`Configured: ${configuredClients || 'none'}.`,
|
|
44
488
|
];
|
|
45
489
|
|
|
46
|
-
|
|
490
|
+
if (changedCount > 0) {
|
|
491
|
+
lines.push(`Updated: ${changedCount} target${changedCount === 1 ? '' : 's'}.`);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (command === 'init' && !accessKey) {
|
|
495
|
+
lines.push(`Account tools stay optional. Add personal auth later with: neus auth --access-key <npk_...>`);
|
|
496
|
+
}
|
|
497
|
+
if ((command === 'init' || command === 'auth') && accessKey) {
|
|
498
|
+
lines.push('Personal account tools are enabled where the client supports user-scope auth setup.');
|
|
499
|
+
}
|
|
500
|
+
if (command === 'status') {
|
|
501
|
+
const enabled = results.filter((result) => result.configured).map((result) => result.client);
|
|
502
|
+
lines.push(`Active: ${enabled.length > 0 ? enabled.join(', ') : 'none'}.`);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
47
506
|
}
|
|
48
507
|
|
|
49
|
-
|
|
50
|
-
|
|
508
|
+
function runInit(options) {
|
|
509
|
+
const scope = resolveScope(options);
|
|
510
|
+
ensureSafeAuth('init', scope, options.accessKey);
|
|
511
|
+
|
|
512
|
+
const clients = resolveClients(scope, options.clients);
|
|
513
|
+
ensureClientSelection(scope, clients);
|
|
514
|
+
|
|
515
|
+
const results = clients.map((client) => installClient(client, scope, options.accessKey, options.dryRun, process.cwd()));
|
|
516
|
+
const payload = {
|
|
517
|
+
command: 'init',
|
|
518
|
+
scope,
|
|
519
|
+
detectedClients: defaultUserClients(),
|
|
520
|
+
clients,
|
|
521
|
+
accessKeyConfigured: Boolean(options.accessKey),
|
|
522
|
+
results,
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
if (options.json) {
|
|
526
|
+
printJson(payload);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
printResultSummary('init', scope, results, options.accessKey);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function runAuth(options) {
|
|
533
|
+
const scope = resolveScope(options);
|
|
534
|
+
ensureSafeAuth('auth', scope, options.accessKey);
|
|
535
|
+
if (!options.accessKey) {
|
|
536
|
+
throw new Error(`Missing access key. Create one at ${NEUS_ACCESS_KEYS_URL} and rerun neus auth --access-key <npk_...>.`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const clients = resolveClients(scope, options.clients);
|
|
540
|
+
ensureClientSelection(scope, clients);
|
|
541
|
+
|
|
542
|
+
const results = clients.map((client) => installClient(client, scope, options.accessKey, options.dryRun, process.cwd()));
|
|
543
|
+
const payload = {
|
|
544
|
+
command: 'auth',
|
|
545
|
+
scope,
|
|
546
|
+
clients,
|
|
547
|
+
accessKeyConfigured: true,
|
|
548
|
+
results,
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
if (options.json) {
|
|
552
|
+
printJson(payload);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
printResultSummary('auth', scope, results, options.accessKey);
|
|
51
556
|
}
|
|
52
557
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
558
|
+
function runStatus(options) {
|
|
559
|
+
const scope = resolveScope(options);
|
|
560
|
+
const clients = resolveClients(scope, options.clients);
|
|
561
|
+
ensureClientSelection(scope, clients);
|
|
562
|
+
|
|
563
|
+
const inspected = clients.map((client) => inspectClient(client, scope, process.cwd()));
|
|
564
|
+
const payload = {
|
|
565
|
+
command: 'status',
|
|
566
|
+
scope,
|
|
567
|
+
clients: inspected,
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
if (options.json) {
|
|
571
|
+
printJson(payload);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
printResultSummary('status', scope, inspected, '');
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function main() {
|
|
578
|
+
try {
|
|
579
|
+
const { command, options } = parseArgs(process.argv.slice(2));
|
|
580
|
+
|
|
581
|
+
if (command === 'help') {
|
|
582
|
+
printUsage(0);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (command === 'init') {
|
|
586
|
+
runInit(options);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (command === 'auth') {
|
|
590
|
+
runAuth(options);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (command === 'status') {
|
|
594
|
+
runStatus(options);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
process.stderr.write(`Unknown subcommand: ${command}\n`);
|
|
599
|
+
printUsage(1);
|
|
600
|
+
} catch (error) {
|
|
601
|
+
process.stderr.write(`${error?.message || 'Unknown error'}\n`);
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
56
604
|
}
|
|
57
605
|
|
|
58
|
-
|
|
606
|
+
main();
|