@neus/sdk 1.0.6 → 1.0.7

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/cli/neus.mjs +100 -15
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -31,7 +31,7 @@ npx -y -p @neus/sdk neus auth --access-key <npk_...>
31
31
  npx -y -p @neus/sdk neus status --json
32
32
  ```
33
33
 
34
- Use `neus init --project` when you want shared repo config instead of personal user-scope setup. Access keys stay user-scope only so secrets do not land in checked-in config.
34
+ Use `neus init --project` when you want shared repo config instead of personal user-scope setup. Access keys stay user-scope only so secrets do not land in checked-in config. Use `--client claude`, `--client cursor`, or `--client vscode` when you want to target one editor directly.
35
35
 
36
36
  ## Minimal working example
37
37
 
package/cli/neus.mjs CHANGED
@@ -24,9 +24,17 @@ function jsonStringify(value) {
24
24
 
25
25
  function readJsonFile(targetPath, fallback) {
26
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;
27
+ const raw = fs.readFileSync(targetPath, 'utf8').trim();
28
+ if (!raw) return fallback;
29
+ try {
30
+ const parsed = JSON.parse(raw);
31
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : fallback;
32
+ } catch (error) {
33
+ if (error instanceof SyntaxError) {
34
+ throw new Error(`Invalid JSON in ${targetPath}`);
35
+ }
36
+ throw error;
37
+ }
30
38
  }
31
39
 
32
40
  function writeJsonFile(targetPath, nextValue, dryRun) {
@@ -306,6 +314,7 @@ function installCursor(scope, accessKey, dryRun, cwd) {
306
314
  targetPath,
307
315
  backupPath: writeResult.backupPath,
308
316
  dryRun,
317
+ error: null,
309
318
  };
310
319
  }
311
320
 
@@ -329,6 +338,7 @@ function installVsCode(scope, accessKey, dryRun, cwd) {
329
338
  targetPath,
330
339
  backupPath: writeResult.backupPath,
331
340
  dryRun,
341
+ error: null,
332
342
  };
333
343
  }
334
344
 
@@ -352,6 +362,7 @@ function installClaudeProject(scope, accessKey, dryRun, cwd) {
352
362
  targetPath,
353
363
  backupPath: writeResult.backupPath,
354
364
  dryRun,
365
+ error: null,
355
366
  };
356
367
  }
357
368
 
@@ -387,6 +398,7 @@ function installClaudeUser(scope, accessKey, dryRun, cwd) {
387
398
  targetPath: '~/.claude.json',
388
399
  backupPath: null,
389
400
  dryRun,
401
+ error: null,
390
402
  };
391
403
  }
392
404
 
@@ -407,7 +419,7 @@ function installClient(client, scope, accessKey, dryRun, cwd) {
407
419
  function inspectCursor(scope, cwd) {
408
420
  const targetPath = cursorConfigPath(scope, cwd);
409
421
  if (!fileExists(targetPath)) {
410
- return { client: 'cursor', scope, configured: false, authConfigured: false, targetPath };
422
+ return { client: 'cursor', scope, configured: false, authConfigured: false, targetPath, error: null };
411
423
  }
412
424
  const doc = readJsonFile(targetPath, {});
413
425
  const server = doc.mcpServers?.[NEUS_SERVER_NAME];
@@ -417,13 +429,14 @@ function inspectCursor(scope, cwd) {
417
429
  configured: Boolean(server && server.url === NEUS_MCP_URL),
418
430
  authConfigured: Boolean(server?.headers?.Authorization),
419
431
  targetPath,
432
+ error: null,
420
433
  };
421
434
  }
422
435
 
423
436
  function inspectVsCode(scope, cwd) {
424
437
  const targetPath = vscodeConfigPath(scope, cwd);
425
438
  if (!fileExists(targetPath)) {
426
- return { client: 'vscode', scope, configured: false, authConfigured: false, targetPath };
439
+ return { client: 'vscode', scope, configured: false, authConfigured: false, targetPath, error: null };
427
440
  }
428
441
  const doc = readJsonFile(targetPath, {});
429
442
  const server = doc.servers?.[NEUS_SERVER_NAME];
@@ -433,6 +446,7 @@ function inspectVsCode(scope, cwd) {
433
446
  configured: Boolean(server && server.url === NEUS_MCP_URL),
434
447
  authConfigured: Boolean(server?.headers?.Authorization),
435
448
  targetPath,
449
+ error: null,
436
450
  };
437
451
  }
438
452
 
@@ -440,7 +454,7 @@ function inspectClaude(scope, cwd) {
440
454
  if (scope === 'project') {
441
455
  const targetPath = claudeProjectConfigPath(cwd);
442
456
  if (!fileExists(targetPath)) {
443
- return { client: 'claude', scope, configured: false, authConfigured: false, targetPath };
457
+ return { client: 'claude', scope, configured: false, authConfigured: false, targetPath, error: null };
444
458
  }
445
459
  const doc = readJsonFile(targetPath, {});
446
460
  const server = doc.mcpServers?.[NEUS_SERVER_NAME];
@@ -450,11 +464,12 @@ function inspectClaude(scope, cwd) {
450
464
  configured: Boolean(server && server.url === NEUS_MCP_URL),
451
465
  authConfigured: Boolean(server?.headers?.Authorization),
452
466
  targetPath,
467
+ error: null,
453
468
  };
454
469
  }
455
470
 
456
471
  if (!commandExists('claude')) {
457
- return { client: 'claude', scope, configured: false, authConfigured: null, targetPath: '~/.claude.json' };
472
+ return { client: 'claude', scope, configured: false, authConfigured: null, targetPath: '~/.claude.json', error: null };
458
473
  }
459
474
 
460
475
  const result = runCommand('claude', ['mcp', 'list'], cwd, true);
@@ -465,6 +480,7 @@ function inspectClaude(scope, cwd) {
465
480
  configured,
466
481
  authConfigured: null,
467
482
  targetPath: '~/.claude.json',
483
+ error: null,
468
484
  };
469
485
  }
470
486
 
@@ -479,9 +495,47 @@ function printJson(payload) {
479
495
  process.stdout.write(jsonStringify(payload));
480
496
  }
481
497
 
498
+ function clientTargetPath(client, scope, cwd) {
499
+ if (client === 'cursor') return cursorConfigPath(scope, cwd);
500
+ if (client === 'vscode') return vscodeConfigPath(scope, cwd);
501
+ if (client === 'claude') {
502
+ return scope === 'project' ? claudeProjectConfigPath(cwd) : '~/.claude.json';
503
+ }
504
+ return null;
505
+ }
506
+
507
+ function errorMessage(error) {
508
+ return error instanceof Error ? error.message : String(error || 'Unknown error');
509
+ }
510
+
511
+ function buildClientFailure(client, scope, cwd, dryRun, error) {
512
+ return {
513
+ client,
514
+ scope,
515
+ configured: false,
516
+ authConfigured: false,
517
+ changed: false,
518
+ targetPath: clientTargetPath(client, scope, cwd),
519
+ backupPath: null,
520
+ dryRun,
521
+ error: errorMessage(error),
522
+ };
523
+ }
524
+
525
+ function runClientOperations(clients, scope, cwd, dryRun, runner) {
526
+ return clients.map((client) => {
527
+ try {
528
+ return runner(client);
529
+ } catch (error) {
530
+ return buildClientFailure(client, scope, cwd, dryRun, error);
531
+ }
532
+ });
533
+ }
534
+
482
535
  function printResultSummary(command, scope, results, accessKey) {
483
536
  const changedCount = results.filter((result) => result.changed).length;
484
- const configuredClients = results.map((result) => result.client).join(', ');
537
+ const configuredClients = results.filter((result) => result.configured).map((result) => result.client).join(', ');
538
+ const failures = results.filter((result) => result.error);
485
539
  const lines = [
486
540
  `NEUS ${command} completed for ${results.length} client${results.length === 1 ? '' : 's'} in ${scope} scope.`,
487
541
  `Configured: ${configuredClients || 'none'}.`,
@@ -501,6 +555,9 @@ function printResultSummary(command, scope, results, accessKey) {
501
555
  const enabled = results.filter((result) => result.configured).map((result) => result.client);
502
556
  lines.push(`Active: ${enabled.length > 0 ? enabled.join(', ') : 'none'}.`);
503
557
  }
558
+ if (failures.length > 0) {
559
+ lines.push(`Issues: ${failures.map((result) => `${result.client}: ${result.error}`).join(' | ')}`);
560
+ }
504
561
 
505
562
  process.stdout.write(`${lines.join('\n')}\n`);
506
563
  }
@@ -508,11 +565,18 @@ function printResultSummary(command, scope, results, accessKey) {
508
565
  function runInit(options) {
509
566
  const scope = resolveScope(options);
510
567
  ensureSafeAuth('init', scope, options.accessKey);
568
+ const cwd = process.cwd();
511
569
 
512
570
  const clients = resolveClients(scope, options.clients);
513
571
  ensureClientSelection(scope, clients);
514
572
 
515
- const results = clients.map((client) => installClient(client, scope, options.accessKey, options.dryRun, process.cwd()));
573
+ const results = runClientOperations(
574
+ clients,
575
+ scope,
576
+ cwd,
577
+ options.dryRun,
578
+ (client) => installClient(client, scope, options.accessKey, options.dryRun, cwd),
579
+ );
516
580
  const payload = {
517
581
  command: 'init',
518
582
  scope,
@@ -520,18 +584,24 @@ function runInit(options) {
520
584
  clients,
521
585
  accessKeyConfigured: Boolean(options.accessKey),
522
586
  results,
587
+ hasErrors: results.some((result) => result.error),
523
588
  };
524
589
 
525
590
  if (options.json) {
526
591
  printJson(payload);
527
- return;
592
+ } else {
593
+ printResultSummary('init', scope, results, options.accessKey);
594
+ }
595
+
596
+ if (payload.hasErrors) {
597
+ process.exitCode = 1;
528
598
  }
529
- printResultSummary('init', scope, results, options.accessKey);
530
599
  }
531
600
 
532
601
  function runAuth(options) {
533
602
  const scope = resolveScope(options);
534
603
  ensureSafeAuth('auth', scope, options.accessKey);
604
+ const cwd = process.cwd();
535
605
  if (!options.accessKey) {
536
606
  throw new Error(`Missing access key. Create one at ${NEUS_ACCESS_KEYS_URL} and rerun neus auth --access-key <npk_...>.`);
537
607
  }
@@ -539,32 +609,47 @@ function runAuth(options) {
539
609
  const clients = resolveClients(scope, options.clients);
540
610
  ensureClientSelection(scope, clients);
541
611
 
542
- const results = clients.map((client) => installClient(client, scope, options.accessKey, options.dryRun, process.cwd()));
612
+ const results = runClientOperations(
613
+ clients,
614
+ scope,
615
+ cwd,
616
+ options.dryRun,
617
+ (client) => installClient(client, scope, options.accessKey, options.dryRun, cwd),
618
+ );
543
619
  const payload = {
544
620
  command: 'auth',
545
621
  scope,
546
622
  clients,
547
623
  accessKeyConfigured: true,
548
624
  results,
625
+ hasErrors: results.some((result) => result.error),
549
626
  };
550
627
 
551
628
  if (options.json) {
552
629
  printJson(payload);
553
- return;
630
+ } else {
631
+ printResultSummary('auth', scope, results, options.accessKey);
632
+ }
633
+
634
+ if (payload.hasErrors) {
635
+ process.exitCode = 1;
554
636
  }
555
- printResultSummary('auth', scope, results, options.accessKey);
556
637
  }
557
638
 
558
639
  function runStatus(options) {
559
640
  const scope = resolveScope(options);
641
+ const cwd = process.cwd();
560
642
  const clients = resolveClients(scope, options.clients);
561
643
  ensureClientSelection(scope, clients);
562
644
 
563
- const inspected = clients.map((client) => inspectClient(client, scope, process.cwd()));
645
+ const inspected = runClientOperations(clients, scope, cwd, options.dryRun, (client) =>
646
+ inspectClient(client, scope, cwd),
647
+ );
564
648
  const payload = {
565
649
  command: 'status',
566
650
  scope,
567
651
  clients: inspected,
652
+ hasErrors: inspected.some((result) => result.error),
568
653
  };
569
654
 
570
655
  if (options.json) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neus/sdk",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Portable trust for people, apps, and agents. Store a proof ID; check gates across surfaces.",
5
5
  "bin": {
6
6
  "neus": "cli/neus.mjs"
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "sideEffects": false,
46
46
  "scripts": {
47
- "test": "vitest run",
47
+ "test": "npm run build:cjs && vitest run",
48
48
  "test:coverage": "vitest run --coverage",
49
49
  "lint": "eslint . --ignore-pattern widgets/verify-gate/dist/**",
50
50
  "format": "prettier --write \"**/*.js\"",