@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/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
- const argv = process.argv.slice(2);
3
- const sub = argv[0];
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
- function usage() {
6
- process.stderr.write("Usage: neus init\n");
7
- process.stderr.write("Prints hosted MCP config and doc URLs. Does not write files.\n");
8
- process.exit(sub && sub !== "help" && sub !== "--help" && sub !== "-h" ? 1 : 0);
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 printInit() {
12
- const mcpBlock = {
13
- mcpServers: {
14
- neus: {
15
- type: "streamableHttp",
16
- url: "https://mcp.neus.network/mcp",
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
- "# NEUS",
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
- process.stdout.write(`${lines.join("\n")}\n`);
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
- if (!sub || sub === "help" || sub === "--help" || sub === "-h") {
50
- usage();
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
- if (sub === "init") {
54
- printInit();
55
- process.exit(0);
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
- usage();
606
+ main();