@neus/sdk 1.0.6 → 1.0.8
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 +0 -23
- package/SECURITY.md +38 -38
- package/cli/neus.mjs +100 -15
- package/client.js +1837 -1837
- package/package.json +136 -136
- package/types.d.ts +915 -915
- package/widgets/README.md +45 -45
- package/widgets/verify-gate/dist/VerifyGate.js +88 -21
package/README.md
CHANGED
|
@@ -10,29 +10,6 @@
|
|
|
10
10
|
npm install @neus/sdk
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
## One-command onboarding
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npx -y -p @neus/sdk neus init
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Configures supported MCP clients automatically. By default the command installs NEUS into user-level Claude Code, Cursor, and VS Code MCP config when those clients are detected.
|
|
20
|
-
|
|
21
|
-
## CLI
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# Autopilot setup for detected clients
|
|
25
|
-
npx -y -p @neus/sdk neus init
|
|
26
|
-
|
|
27
|
-
# Enable personal account tools such as neus_me and private reads
|
|
28
|
-
npx -y -p @neus/sdk neus auth --access-key <npk_...>
|
|
29
|
-
|
|
30
|
-
# Inspect current NEUS MCP setup
|
|
31
|
-
npx -y -p @neus/sdk neus status --json
|
|
32
|
-
```
|
|
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.
|
|
35
|
-
|
|
36
13
|
## Minimal working example
|
|
37
14
|
|
|
38
15
|
```javascript
|
package/SECURITY.md
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
# NEUS SDK security notes
|
|
2
|
-
|
|
3
|
-
Treat **wallet signatures** and **API keys** as secrets. Do not log them, expose them to clients, or store them in analytics.
|
|
4
|
-
|
|
5
|
-
## Authentication model
|
|
6
|
-
|
|
7
|
-
- **Verification requests** are authenticated with a wallet signature over the **CAIP-380 Portable Proof** six-line signing string. Never roll your own message format in production—use the SDK or the hosted preparation step documented for HTTP integrations.
|
|
8
|
-
- **Proof lookups by `proofId`** are safe for public proofs. Private proofs return a minimal payload unless the caller proves ownership (authenticated owner or signed request).
|
|
9
|
-
- **Owner-only reads** of private proof payloads require an extra owner-signed request. The SDK attaches the required signed headers for you.
|
|
10
|
-
|
|
11
|
-
## Do not
|
|
12
|
-
|
|
13
|
-
- Do not treat proof signatures as bearer tokens (they are request-bound).
|
|
14
|
-
- Do not embed API keys in browser apps. Keep API keys server-side only.
|
|
15
|
-
- Do not log or persist proof signatures, API keys, or third-party auth credentials (if your integration uses them).
|
|
16
|
-
|
|
17
|
-
## Privacy defaults
|
|
18
|
-
|
|
19
|
-
**`client.verify()`** defaults to **private**.
|
|
20
|
-
|
|
21
|
-
**`VerifyGate`** create mode also defaults to **private**.
|
|
22
|
-
|
|
23
|
-
Use public visibility only when you intentionally need proof reuse without owner-authenticated access:
|
|
24
|
-
|
|
25
|
-
- unlisted public: `privacyLevel: 'public'`, `publicDisplay: false`
|
|
26
|
-
- listed public: `privacyLevel: 'public'`, `publicDisplay: true`
|
|
27
|
-
|
|
28
|
-
Do not treat unlisted public proofs as secret.
|
|
29
|
-
|
|
30
|
-
`storeOriginalContent` is an advanced storage control. Most integrations should leave the default as-is.
|
|
31
|
-
|
|
32
|
-
Controls:
|
|
33
|
-
|
|
34
|
-
- `privacyLevel` - private by default; switch to public only for intentional public reuse
|
|
35
|
-
- `publicDisplay` - discovery vs unlisted
|
|
36
|
-
- `storeOriginalContent` - advanced content-storage control
|
|
37
|
-
|
|
38
|
-
Discoverable listings require **`privacyLevel: 'public'`** and **`publicDisplay: true`**.
|
|
1
|
+
# NEUS SDK security notes
|
|
2
|
+
|
|
3
|
+
Treat **wallet signatures** and **API keys** as secrets. Do not log them, expose them to clients, or store them in analytics.
|
|
4
|
+
|
|
5
|
+
## Authentication model
|
|
6
|
+
|
|
7
|
+
- **Verification requests** are authenticated with a wallet signature over the **CAIP-380 Portable Proof** six-line signing string. Never roll your own message format in production—use the SDK or the hosted preparation step documented for HTTP integrations.
|
|
8
|
+
- **Proof lookups by `proofId`** are safe for public proofs. Private proofs return a minimal payload unless the caller proves ownership (authenticated owner or signed request).
|
|
9
|
+
- **Owner-only reads** of private proof payloads require an extra owner-signed request. The SDK attaches the required signed headers for you.
|
|
10
|
+
|
|
11
|
+
## Do not
|
|
12
|
+
|
|
13
|
+
- Do not treat proof signatures as bearer tokens (they are request-bound).
|
|
14
|
+
- Do not embed API keys in browser apps. Keep API keys server-side only.
|
|
15
|
+
- Do not log or persist proof signatures, API keys, or third-party auth credentials (if your integration uses them).
|
|
16
|
+
|
|
17
|
+
## Privacy defaults
|
|
18
|
+
|
|
19
|
+
**`client.verify()`** defaults to **private**.
|
|
20
|
+
|
|
21
|
+
**`VerifyGate`** create mode also defaults to **private**.
|
|
22
|
+
|
|
23
|
+
Use public visibility only when you intentionally need proof reuse without owner-authenticated access:
|
|
24
|
+
|
|
25
|
+
- unlisted public: `privacyLevel: 'public'`, `publicDisplay: false`
|
|
26
|
+
- listed public: `privacyLevel: 'public'`, `publicDisplay: true`
|
|
27
|
+
|
|
28
|
+
Do not treat unlisted public proofs as secret.
|
|
29
|
+
|
|
30
|
+
`storeOriginalContent` is an advanced storage control. Most integrations should leave the default as-is.
|
|
31
|
+
|
|
32
|
+
Controls:
|
|
33
|
+
|
|
34
|
+
- `privacyLevel` - private by default; switch to public only for intentional public reuse
|
|
35
|
+
- `publicDisplay` - discovery vs unlisted
|
|
36
|
+
- `storeOriginalContent` - advanced content-storage control
|
|
37
|
+
|
|
38
|
+
Discoverable listings require **`privacyLevel: 'public'`** and **`publicDisplay: true`**.
|
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
|
-
|
|
29
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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) {
|