@neus/sdk 1.1.4 → 1.1.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 +11 -10
- package/cjs/client.cjs +100 -20
- package/cjs/index.cjs +100 -20
- package/cli/neus.mjs +106 -32
- package/client.js +105 -20
- package/package.json +147 -147
- package/types.d.ts +1154 -1011
- package/widgets/verify-gate/dist/ProofBadge.js +1 -1
- package/widgets/verify-gate/dist/VerifyGate.js +1 -1
package/cli/neus.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { exec, spawnSync } from 'node:child_process';
|
|
3
3
|
import { createHash, randomBytes } from 'node:crypto';
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import os from 'node:os';
|
|
@@ -12,6 +12,13 @@ import {
|
|
|
12
12
|
} from '../mcp-hosts.js';
|
|
13
13
|
|
|
14
14
|
const __cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const CLI_PACKAGE_VERSION = (() => {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(fs.readFileSync(path.join(__cliDir, '..', 'package.json'), 'utf8')).version;
|
|
18
|
+
} catch {
|
|
19
|
+
return '0.0.0';
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
15
22
|
|
|
16
23
|
const NEUS_APP_URL = 'https://neus.network';
|
|
17
24
|
const NEUS_TOKEN_ENDPOINT = 'https://neus.network/api/v1/auth/mcp/token';
|
|
@@ -100,7 +107,7 @@ function emitCliBanner(cliOptions = {}) {
|
|
|
100
107
|
if (!shouldEmitCliBanner(cliOptions)) return;
|
|
101
108
|
const version = readCliVersion();
|
|
102
109
|
const title = paint('NEUS', 'green');
|
|
103
|
-
const meta = `${paint(`v${version}`, 'dim')}${paint(' | trust
|
|
110
|
+
const meta = `${paint(`v${version}`, 'dim')}${paint(' | trust that travels', 'dim')}`;
|
|
104
111
|
writeCliLine('');
|
|
105
112
|
writeCliLine(` ${title} ${meta}`);
|
|
106
113
|
writeCliLine('');
|
|
@@ -137,21 +144,20 @@ function describeClientResult(command, result) {
|
|
|
137
144
|
}
|
|
138
145
|
if (result.changed) return 'updated';
|
|
139
146
|
if (result.authConfigured) return 'signed in';
|
|
147
|
+
if (result.configured) return 'ready';
|
|
140
148
|
return 'ready';
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
function printBuilderGuidance(command, results) {
|
|
144
|
-
if (!['setup', 'auth'].includes(command)) return;
|
|
152
|
+
if (!['setup', 'auth', 'check'].includes(command)) return;
|
|
145
153
|
const hasCodex = results.some(result => result.client === 'codex');
|
|
146
154
|
writeCliLine('');
|
|
147
|
-
writeCliLine(paint('
|
|
148
|
-
writeGuidanceLine('
|
|
155
|
+
writeCliLine(paint('Next steps', 'cyan'));
|
|
156
|
+
writeGuidanceLine('Run `npx -y -p @neus/sdk neus examples` for assistant prompts.');
|
|
149
157
|
if (hasCodex) {
|
|
150
|
-
writeGuidanceLine('Codex
|
|
158
|
+
writeGuidanceLine('Codex OAuth: `neus auth --client codex` or `codex mcp login neus`.');
|
|
151
159
|
}
|
|
152
|
-
writeGuidanceLine(
|
|
153
|
-
'Claude plugin commands run inside Claude Code chat, not as `claude install`: `/plugin marketplace add https://github.com/neus/network`.'
|
|
154
|
-
);
|
|
160
|
+
writeGuidanceLine('Ask your assistant: "Use NEUS Verify before taking sensitive actions."');
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
function selectedClientNames(results) {
|
|
@@ -178,7 +184,7 @@ function printStatusGuidance(results) {
|
|
|
178
184
|
writeGuidanceLine(NEUS_MCP_URL);
|
|
179
185
|
writeCliLine(paint('Profile connection', 'cyan'));
|
|
180
186
|
if (results.some(result => result.configured)) {
|
|
181
|
-
writeGuidanceLine('Saved config found. Run `npx -y -p @neus/sdk neus
|
|
187
|
+
writeGuidanceLine('Saved config found. Run `npx -y -p @neus/sdk neus check` to confirm live connection.');
|
|
182
188
|
} else {
|
|
183
189
|
writeGuidanceLine(`No selected MCP host is configured yet. Run \`${preferredSetupCommand(results)}\`.`);
|
|
184
190
|
}
|
|
@@ -298,6 +304,7 @@ function envAccessKey() {
|
|
|
298
304
|
|
|
299
305
|
/** --access-key flag, else NEUS_ACCESS_KEY from the environment, else browser sign-in. */
|
|
300
306
|
function resolveAccessKey(options) {
|
|
307
|
+
if (options?.oauth) return '';
|
|
301
308
|
const explicit = String(options.accessKey || '').trim();
|
|
302
309
|
if (explicit) return explicit;
|
|
303
310
|
return envAccessKey();
|
|
@@ -309,6 +316,7 @@ function resolveLiveAccessKey(options, scope, cwd) {
|
|
|
309
316
|
if (explicit) return explicit;
|
|
310
317
|
const installed = readInstalledAccessKey(scope, cwd);
|
|
311
318
|
if (installed) return installed;
|
|
319
|
+
if (options?.oauth) return '';
|
|
312
320
|
return envAccessKey();
|
|
313
321
|
}
|
|
314
322
|
|
|
@@ -581,7 +589,8 @@ function parseArgs(argv) {
|
|
|
581
589
|
live: false,
|
|
582
590
|
json: false,
|
|
583
591
|
dryRun: false,
|
|
584
|
-
project: false
|
|
592
|
+
project: false,
|
|
593
|
+
oauth: false
|
|
585
594
|
};
|
|
586
595
|
|
|
587
596
|
for (let index = 1; index < argv.length; index += 1) {
|
|
@@ -637,6 +646,10 @@ function parseArgs(argv) {
|
|
|
637
646
|
index += 1;
|
|
638
647
|
continue;
|
|
639
648
|
}
|
|
649
|
+
if (token === '--oauth') {
|
|
650
|
+
options.oauth = true;
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
640
653
|
if (token === '--help' || token === '-h') {
|
|
641
654
|
return { command: 'help', options };
|
|
642
655
|
}
|
|
@@ -659,6 +672,8 @@ function printUsage(exitCode = 0) {
|
|
|
659
672
|
' auth Sign in (browser, or NEUS_ACCESS_KEY / --access-key when set)',
|
|
660
673
|
' disconnect Disconnect NEUS MCP (revoke the stored OAuth token or access key)',
|
|
661
674
|
' status Show current NEUS MCP setup',
|
|
675
|
+
' check Confirm setup and live NEUS connection (alias for doctor --live)',
|
|
676
|
+
' examples Show assistant prompts to try after install',
|
|
662
677
|
' doctor Deep check: config status, profile connection, and live MCP context',
|
|
663
678
|
' import Detect and package supported assistant context for NEUS portability',
|
|
664
679
|
' export Export the latest local NEUS portable agent manifest',
|
|
@@ -668,6 +683,7 @@ function printUsage(exitCode = 0) {
|
|
|
668
683
|
' --client <name[,name]> Limit setup to claude, codex, cursor, or vscode',
|
|
669
684
|
' --project Write shared project config instead of user config',
|
|
670
685
|
' --access-key <npk_...> Override profile access key (else uses NEUS_ACCESS_KEY if set)',
|
|
686
|
+
' --oauth Force browser OAuth (ignore NEUS_ACCESS_KEY in the environment)',
|
|
671
687
|
' --from <source> Import source: auto, cursor, claude-code, or claude-desktop',
|
|
672
688
|
' --to <format> Export format: manifest or json',
|
|
673
689
|
' --output <path> Write exported manifest to a specific path',
|
|
@@ -1105,7 +1121,7 @@ function createEmptyManifest(source) {
|
|
|
1105
1121
|
proofHints: {
|
|
1106
1122
|
status: 'not-issued',
|
|
1107
1123
|
qHashes: [],
|
|
1108
|
-
next: ['neus setup', 'neus auth', 'neus
|
|
1124
|
+
next: ['neus setup', 'neus auth', 'neus check']
|
|
1109
1125
|
}
|
|
1110
1126
|
};
|
|
1111
1127
|
}
|
|
@@ -1383,7 +1399,7 @@ async function runLiveMcpDiagnostics(accessKey) {
|
|
|
1383
1399
|
params: {
|
|
1384
1400
|
protocolVersion: '2025-11-25',
|
|
1385
1401
|
capabilities: {},
|
|
1386
|
-
clientInfo: { name: 'neus-cli', version:
|
|
1402
|
+
clientInfo: { name: 'neus-cli', version: CLI_PACKAGE_VERSION }
|
|
1387
1403
|
},
|
|
1388
1404
|
accessKey,
|
|
1389
1405
|
signal: controller.signal
|
|
@@ -1424,6 +1440,9 @@ async function runLiveMcpDiagnostics(accessKey) {
|
|
|
1424
1440
|
signal: controller.signal
|
|
1425
1441
|
});
|
|
1426
1442
|
const mode = context.ok ? context.payload?.mode?.current || context.payload?.mode || '' : '';
|
|
1443
|
+
const profileCtx = context.ok ? context.payload?.profileContext : null;
|
|
1444
|
+
const principal = profileCtx?.principal || null;
|
|
1445
|
+
const proofsTotal = profileCtx?.profileSummary?.proofsSummary?.total;
|
|
1427
1446
|
return {
|
|
1428
1447
|
live: true,
|
|
1429
1448
|
reachable: true,
|
|
@@ -1431,6 +1450,9 @@ async function runLiveMcpDiagnostics(accessKey) {
|
|
|
1431
1450
|
toolsCount: toolNames.length,
|
|
1432
1451
|
tools: toolNames,
|
|
1433
1452
|
contextMode: mode,
|
|
1453
|
+
sessionWallet: context.ok ? context.payload?.sessionWallet || principal?.primaryAccount || null : null,
|
|
1454
|
+
profileHandle: principal?.handle || null,
|
|
1455
|
+
proofsTotal: Number.isFinite(Number(proofsTotal)) ? Number(proofsTotal) : null,
|
|
1434
1456
|
checks: [
|
|
1435
1457
|
{
|
|
1436
1458
|
name: 'initialize',
|
|
@@ -1693,9 +1715,12 @@ async function runAuthBrowser(options) {
|
|
|
1693
1715
|
logStep('next', 'wait', 'finish sign-in in the browser');
|
|
1694
1716
|
}
|
|
1695
1717
|
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
|
|
1718
|
+
const openCommand = process.platform === 'win32'
|
|
1719
|
+
? `cmd /c start "" "${authUrl.replace(/"/g, '\\"')}"`
|
|
1720
|
+
: process.platform === 'darwin'
|
|
1721
|
+
? `open "${authUrl.replace(/"/g, '\\"')}"`
|
|
1722
|
+
: `xdg-open "${authUrl.replace(/"/g, '\\"')}"`;
|
|
1723
|
+
exec(openCommand, { shell: true }, err => {
|
|
1699
1724
|
if (err && !options.json) {
|
|
1700
1725
|
logStep('warn', 'browser', 'open the URL above manually');
|
|
1701
1726
|
}
|
|
@@ -1810,12 +1835,18 @@ async function runSetup(options) {
|
|
|
1810
1835
|
}
|
|
1811
1836
|
|
|
1812
1837
|
if (options.json) {
|
|
1838
|
+
payload.authRequired = !accessKey && !options.dryRun;
|
|
1839
|
+
if (payload.authRequired) {
|
|
1840
|
+
payload.nextCommand = clients.length === 1 && clients[0] === 'codex'
|
|
1841
|
+
? 'neus auth --client codex'
|
|
1842
|
+
: 'neus auth';
|
|
1843
|
+
}
|
|
1813
1844
|
printJson(payload);
|
|
1814
1845
|
return payload;
|
|
1815
1846
|
}
|
|
1816
1847
|
|
|
1817
1848
|
printFlowSummary('setup', scope, initResults, {
|
|
1818
|
-
nextStep: accessKey ? '
|
|
1849
|
+
nextStep: accessKey ? 'Run `neus examples`, then ask your assistant to use NEUS Verify.' : '',
|
|
1819
1850
|
cliOptions: options
|
|
1820
1851
|
});
|
|
1821
1852
|
|
|
@@ -1823,7 +1854,7 @@ async function runSetup(options) {
|
|
|
1823
1854
|
const authResult = await runAuth(options);
|
|
1824
1855
|
if (authResult && !authResult.hasErrors) {
|
|
1825
1856
|
printFlowSummary('auth', authResult.scope, authResult.results, {
|
|
1826
|
-
nextStep: '
|
|
1857
|
+
nextStep: 'Run `neus examples`, then ask your assistant to use NEUS Verify.',
|
|
1827
1858
|
cliOptions: options
|
|
1828
1859
|
});
|
|
1829
1860
|
}
|
|
@@ -1910,7 +1941,39 @@ function runExport(options) {
|
|
|
1910
1941
|
printExportSummary(payload, options);
|
|
1911
1942
|
}
|
|
1912
1943
|
|
|
1944
|
+
const ASSISTANT_EXAMPLE_PROMPTS = [
|
|
1945
|
+
'Use NEUS Verify before taking sensitive actions.',
|
|
1946
|
+
'Check whether I already have the required trust receipt.',
|
|
1947
|
+
'Verify this agent is trusted before it runs tools.',
|
|
1948
|
+
'Use NEUS Vault before storing or using secrets.',
|
|
1949
|
+
'Show the receipt for this verification.'
|
|
1950
|
+
];
|
|
1951
|
+
|
|
1952
|
+
function runExamples(options) {
|
|
1953
|
+
const payload = {
|
|
1954
|
+
command: 'examples',
|
|
1955
|
+
intro: 'Try this in your assistant:',
|
|
1956
|
+
prompts: ASSISTANT_EXAMPLE_PROMPTS
|
|
1957
|
+
};
|
|
1958
|
+
|
|
1959
|
+
if (options.json) {
|
|
1960
|
+
printJson(payload);
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
emitCliBanner(options);
|
|
1965
|
+
writeCliLine(paint('examples', 'green'));
|
|
1966
|
+
writeCliLine('');
|
|
1967
|
+
writeCliLine(` ${paint(payload.intro, 'dim')}`);
|
|
1968
|
+
writeCliLine('');
|
|
1969
|
+
ASSISTANT_EXAMPLE_PROMPTS.forEach((prompt, index) => {
|
|
1970
|
+
writeCliLine(` ${paint(String(index + 1) + '.', 'cyan')} ${prompt}`);
|
|
1971
|
+
});
|
|
1972
|
+
writeCliLine('');
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1913
1975
|
async function runDoctor(options) {
|
|
1976
|
+
const displayCommand = options.displayCommand || 'doctor';
|
|
1914
1977
|
const scope = resolveScope(options);
|
|
1915
1978
|
const cwd = process.cwd();
|
|
1916
1979
|
const clients = resolveClients(scope, options.clients);
|
|
@@ -1922,7 +1985,7 @@ async function runDoctor(options) {
|
|
|
1922
1985
|
const configuredClients = inspected.filter(r => r.configured);
|
|
1923
1986
|
const liveAccessKey = resolveLiveAccessKey(options, scope, cwd);
|
|
1924
1987
|
const payload = {
|
|
1925
|
-
command:
|
|
1988
|
+
command: displayCommand,
|
|
1926
1989
|
scope,
|
|
1927
1990
|
clients: inspected,
|
|
1928
1991
|
configuredCount: configuredClients.length,
|
|
@@ -1951,7 +2014,7 @@ async function runDoctor(options) {
|
|
|
1951
2014
|
|
|
1952
2015
|
if (configuredClients.length === 0) {
|
|
1953
2016
|
emitCliBanner(options);
|
|
1954
|
-
writeCliLine(paint(
|
|
2017
|
+
writeCliLine(paint(displayCommand, 'green'));
|
|
1955
2018
|
for (const result of inspected) {
|
|
1956
2019
|
if (result.error) {
|
|
1957
2020
|
logStep('warn', result.client, result.error);
|
|
@@ -1966,13 +2029,13 @@ async function runDoctor(options) {
|
|
|
1966
2029
|
writeGuidanceLine(NEUS_MCP_URL);
|
|
1967
2030
|
writeCliLine(paint('Profile connection', 'cyan'));
|
|
1968
2031
|
writeGuidanceLine(`No selected MCP host is configured yet. Run \`${preferredSetupCommand(inspected)}\`.`);
|
|
1969
|
-
writeGuidanceLine(`Then run \`${preferredAuthCommand(inspected)}\` and re-check with \`npx -y -p @neus/sdk neus
|
|
2032
|
+
writeGuidanceLine(`Then run \`${preferredAuthCommand(inspected)}\` and re-check with \`npx -y -p @neus/sdk neus check\`.`);
|
|
1970
2033
|
writeCliLine('');
|
|
1971
2034
|
process.exitCode = 1;
|
|
1972
2035
|
return;
|
|
1973
2036
|
}
|
|
1974
2037
|
|
|
1975
|
-
printFlowSummary(
|
|
2038
|
+
printFlowSummary(displayCommand, scope, inspected, { cliOptions: options });
|
|
1976
2039
|
const hasCodex = inspected.some(result => result.client === 'codex');
|
|
1977
2040
|
writeCliLine(paint('Profile connection', 'cyan'));
|
|
1978
2041
|
if (options.live && payload.mcp) {
|
|
@@ -1983,16 +2046,19 @@ async function runDoctor(options) {
|
|
|
1983
2046
|
: 'No account credential found for the configured MCP clients. Run `neus auth`.'
|
|
1984
2047
|
);
|
|
1985
2048
|
} else {
|
|
1986
|
-
|
|
1987
|
-
payload.mcp.
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2049
|
+
if (payload.mcp.authenticated) {
|
|
2050
|
+
const handle = payload.mcp.profileHandle ? ` as ${payload.mcp.profileHandle}` : '';
|
|
2051
|
+
const receipts =
|
|
2052
|
+
payload.mcp.proofsTotal != null ? ` · ${payload.mcp.proofsTotal} trust receipts on file` : '';
|
|
2053
|
+
logStep('ok', 'profile', `connected${handle}${receipts}`);
|
|
2054
|
+
writeGuidanceLine('NEUS Verify is ready. Ask your assistant to verify trust before sensitive actions.');
|
|
2055
|
+
writeGuidanceLine('Run `npx -y -p @neus/sdk neus examples` for starter prompts.');
|
|
2056
|
+
} else {
|
|
2057
|
+
logStep('warn', 'profile', 'live connection was not confirmed — run `neus auth`');
|
|
2058
|
+
}
|
|
1993
2059
|
}
|
|
1994
2060
|
} else if (liveAccessKey) {
|
|
1995
|
-
writeGuidanceLine('Saved credential found. Run `neus
|
|
2061
|
+
writeGuidanceLine('Saved credential found. Run `neus check` to confirm live connection.');
|
|
1996
2062
|
} else {
|
|
1997
2063
|
writeGuidanceLine(
|
|
1998
2064
|
hasCodex
|
|
@@ -2096,12 +2162,12 @@ async function main() {
|
|
|
2096
2162
|
printJson(result);
|
|
2097
2163
|
} else if (result.authMethod !== 'browser') {
|
|
2098
2164
|
printFlowSummary('auth', result.scope, result.results, {
|
|
2099
|
-
nextStep: '
|
|
2165
|
+
nextStep: 'Run `neus examples`, then ask your assistant to use NEUS Verify.',
|
|
2100
2166
|
cliOptions: options
|
|
2101
2167
|
});
|
|
2102
2168
|
} else {
|
|
2103
2169
|
printFlowSummary('auth', result.scope, result.results, {
|
|
2104
|
-
nextStep: '
|
|
2170
|
+
nextStep: 'Run `neus examples`, then ask your assistant to use NEUS Verify.',
|
|
2105
2171
|
cliOptions: options
|
|
2106
2172
|
});
|
|
2107
2173
|
}
|
|
@@ -2122,10 +2188,18 @@ async function main() {
|
|
|
2122
2188
|
}
|
|
2123
2189
|
return;
|
|
2124
2190
|
}
|
|
2191
|
+
if (command === 'check') {
|
|
2192
|
+
await runDoctor({ ...options, live: true, displayCommand: 'check' });
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2125
2195
|
if (command === 'doctor') {
|
|
2126
2196
|
await runDoctor(options);
|
|
2127
2197
|
return;
|
|
2128
2198
|
}
|
|
2199
|
+
if (command === 'examples') {
|
|
2200
|
+
runExamples(options);
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2129
2203
|
if (command === 'import') {
|
|
2130
2204
|
runImport(options);
|
|
2131
2205
|
return;
|
package/client.js
CHANGED
|
@@ -11,19 +11,19 @@ import {
|
|
|
11
11
|
|
|
12
12
|
const FALLBACK_PUBLIC_VERIFIER_CATALOG = {
|
|
13
13
|
'ownership-basic': { supportsDirectApi: true },
|
|
14
|
+
'ownership-social': { supportsDirectApi: false },
|
|
14
15
|
'ownership-pseudonym': { supportsDirectApi: true },
|
|
15
16
|
'ownership-dns-txt': { supportsDirectApi: true },
|
|
16
|
-
'ownership-social': { supportsDirectApi: false },
|
|
17
17
|
'ownership-org-oauth': { supportsDirectApi: false },
|
|
18
18
|
'contract-ownership': { supportsDirectApi: true },
|
|
19
|
+
'proof-of-human': { supportsDirectApi: false },
|
|
19
20
|
'nft-ownership': { supportsDirectApi: true },
|
|
20
21
|
'token-holding': { supportsDirectApi: true },
|
|
21
|
-
'wallet-link': { supportsDirectApi: true },
|
|
22
22
|
'wallet-risk': { supportsDirectApi: true },
|
|
23
|
-
'
|
|
23
|
+
'wallet-link': { supportsDirectApi: true },
|
|
24
|
+
'ai-content-moderation': { supportsDirectApi: true },
|
|
24
25
|
'agent-identity': { supportsDirectApi: true },
|
|
25
|
-
'agent-delegation': { supportsDirectApi: true }
|
|
26
|
-
'ai-content-moderation': { supportsDirectApi: true }
|
|
26
|
+
'agent-delegation': { supportsDirectApi: true }
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
|
|
@@ -1204,6 +1204,12 @@ export class NeusClient {
|
|
|
1204
1204
|
typeof options?.storeOriginalContent === 'boolean' ? options.storeOriginalContent : true
|
|
1205
1205
|
};
|
|
1206
1206
|
if (typeof options?.enableIpfs === 'boolean') optionsPayload.enableIpfs = options.enableIpfs;
|
|
1207
|
+
// Receipts persist offchain by default; hub registry anchoring is explicit opt-in.
|
|
1208
|
+
if (options?.publishToHub === true) {
|
|
1209
|
+
optionsPayload.publishToHub = true;
|
|
1210
|
+
} else {
|
|
1211
|
+
delete optionsPayload.publishToHub;
|
|
1212
|
+
}
|
|
1207
1213
|
|
|
1208
1214
|
const requestData = {
|
|
1209
1215
|
verifierIds: normalizedVerifierIds,
|
|
@@ -1471,6 +1477,33 @@ export class NeusClient {
|
|
|
1471
1477
|
return true;
|
|
1472
1478
|
}
|
|
1473
1479
|
|
|
1480
|
+
_buildProofsByWalletQuery(options = {}) {
|
|
1481
|
+
const qs = [];
|
|
1482
|
+
if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
|
|
1483
|
+
const cursorRaw = options.cursor !== null && options.cursor !== undefined ? String(options.cursor).trim() : '';
|
|
1484
|
+
if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
|
|
1485
|
+
else if (options.offset !== undefined && options.offset !== null) {
|
|
1486
|
+
qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
|
|
1487
|
+
}
|
|
1488
|
+
if (options.q) qs.push(`q=${encodeURIComponent(String(options.q))}`);
|
|
1489
|
+
if (options.qHash) qs.push(`qHash=${encodeURIComponent(String(options.qHash).toLowerCase())}`);
|
|
1490
|
+
if (options.verifierId) qs.push(`verifierId=${encodeURIComponent(String(options.verifierId))}`);
|
|
1491
|
+
if (options.verifierIds) qs.push(`verifierIds=${encodeURIComponent(String(options.verifierIds))}`);
|
|
1492
|
+
if (options.tags) qs.push(`tags=${encodeURIComponent(String(options.tags))}`);
|
|
1493
|
+
if (options.tagPrefix) qs.push(`tagPrefix=${encodeURIComponent(String(options.tagPrefix))}`);
|
|
1494
|
+
if (options.tagContains) qs.push(`tagContains=${encodeURIComponent(String(options.tagContains))}`);
|
|
1495
|
+
if (options.tagPrefixesAll) qs.push(`tagPrefixesAll=${encodeURIComponent(String(options.tagPrefixesAll))}`);
|
|
1496
|
+
if (options.status) qs.push(`status=${encodeURIComponent(String(options.status))}`);
|
|
1497
|
+
if (options.appId) qs.push(`appId=${encodeURIComponent(String(options.appId))}`);
|
|
1498
|
+
if (options.chainCoverage) qs.push(`chainCoverage=${encodeURIComponent(String(options.chainCoverage))}`);
|
|
1499
|
+
if (options.privacyLevel) qs.push(`privacyLevel=${encodeURIComponent(String(options.privacyLevel))}`);
|
|
1500
|
+
if (options.includeHistory) qs.push('includeHistory=1');
|
|
1501
|
+
if (options.includeFacets) qs.push(`includeFacets=${encodeURIComponent(String(options.includeFacets))}`);
|
|
1502
|
+
if (options.visibility) qs.push(`visibility=${encodeURIComponent(String(options.visibility))}`);
|
|
1503
|
+
if (options.isPublicRead) qs.push('isPublicRead=1');
|
|
1504
|
+
return qs;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1474
1507
|
async getProofsByWallet(walletAddress, options = {}) {
|
|
1475
1508
|
if (!walletAddress || typeof walletAddress !== 'string') {
|
|
1476
1509
|
throw new ValidationError('walletAddress is required');
|
|
@@ -1479,12 +1512,7 @@ export class NeusClient {
|
|
|
1479
1512
|
const id = walletAddress.trim();
|
|
1480
1513
|
const pathId = /^0x[a-fA-F0-9]{40}$/i.test(id) ? id.toLowerCase() : id;
|
|
1481
1514
|
|
|
1482
|
-
const qs =
|
|
1483
|
-
if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
|
|
1484
|
-
const cursorRaw = options.cursor !== null && options.cursor !== undefined ? String(options.cursor).trim() : '';
|
|
1485
|
-
if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
|
|
1486
|
-
else if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
|
|
1487
|
-
if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
|
|
1515
|
+
const qs = this._buildProofsByWalletQuery(options);
|
|
1488
1516
|
|
|
1489
1517
|
const query = qs.length ? `?${qs.join('&')}` : '';
|
|
1490
1518
|
const response = await this._makeRequest(
|
|
@@ -1500,13 +1528,14 @@ export class NeusClient {
|
|
|
1500
1528
|
return {
|
|
1501
1529
|
success: true,
|
|
1502
1530
|
proofs: Array.isArray(proofs) ? proofs : [],
|
|
1503
|
-
totalCount: response.data?.totalCount
|
|
1531
|
+
totalCount: typeof response.data?.totalCount === 'number' ? response.data.totalCount : null,
|
|
1504
1532
|
hasMore: Boolean(response.data?.hasMore),
|
|
1505
1533
|
nextOffset: response.data?.nextOffset ?? null,
|
|
1506
1534
|
nextCursor:
|
|
1507
1535
|
typeof response.data?.nextCursor === 'string' && response.data.nextCursor.trim()
|
|
1508
1536
|
? response.data.nextCursor.trim()
|
|
1509
|
-
: null
|
|
1537
|
+
: null,
|
|
1538
|
+
facets: response.data?.facets || null,
|
|
1510
1539
|
};
|
|
1511
1540
|
}
|
|
1512
1541
|
|
|
@@ -1569,12 +1598,7 @@ export class NeusClient {
|
|
|
1569
1598
|
throw new ValidationError(`Failed to sign message: ${error.message}`);
|
|
1570
1599
|
}
|
|
1571
1600
|
|
|
1572
|
-
const qs =
|
|
1573
|
-
if (options.limit) qs.push(`limit=${encodeURIComponent(String(options.limit))}`);
|
|
1574
|
-
const cursorRaw = options.cursor !== null && options.cursor !== undefined ? String(options.cursor).trim() : '';
|
|
1575
|
-
if (cursorRaw) qs.push(`cursor=${encodeURIComponent(cursorRaw)}`);
|
|
1576
|
-
else if (options.offset) qs.push(`offset=${encodeURIComponent(String(options.offset))}`);
|
|
1577
|
-
if (options.qHash) qs.push(`qHash=${encodeURIComponent(options.qHash.toLowerCase())}`);
|
|
1601
|
+
const qs = this._buildProofsByWalletQuery(options);
|
|
1578
1602
|
const query = qs.length ? `?${qs.join('&')}` : '';
|
|
1579
1603
|
|
|
1580
1604
|
const response = await this._makeRequest('GET', `/api/v1/proofs/by-wallet/${encodeURIComponent(pathId)}${query}`, null, {
|
|
@@ -1592,7 +1616,7 @@ export class NeusClient {
|
|
|
1592
1616
|
return {
|
|
1593
1617
|
success: true,
|
|
1594
1618
|
proofs: Array.isArray(proofs) ? proofs : [],
|
|
1595
|
-
totalCount: response.data?.totalCount
|
|
1619
|
+
totalCount: typeof response.data?.totalCount === 'number' ? response.data.totalCount : null,
|
|
1596
1620
|
hasMore: Boolean(response.data?.hasMore),
|
|
1597
1621
|
nextOffset: response.data?.nextOffset ?? null,
|
|
1598
1622
|
nextCursor:
|
|
@@ -1730,6 +1754,67 @@ export class NeusClient {
|
|
|
1730
1754
|
return response;
|
|
1731
1755
|
}
|
|
1732
1756
|
|
|
1757
|
+
/**
|
|
1758
|
+
* Get the public snapshot of a published gate: requirements, charge, schedule,
|
|
1759
|
+
* checkout plan, and reward presence. Never returns the secret reward value —
|
|
1760
|
+
* that is delivered post-verify via fulfillGate().
|
|
1761
|
+
*
|
|
1762
|
+
* @param {string} gateId Published gate handle
|
|
1763
|
+
* @returns {Promise<object>} Public gate snapshot
|
|
1764
|
+
*/
|
|
1765
|
+
async getGate(gateId) {
|
|
1766
|
+
const id = String(gateId || '').trim();
|
|
1767
|
+
if (!id || id.length > 80 || !/^[a-zA-Z0-9:_-]+$/.test(id)) {
|
|
1768
|
+
throw new ValidationError('Valid gateId is required');
|
|
1769
|
+
}
|
|
1770
|
+
const response = await this._makeRequest('GET', `/api/v1/gates/${encodeURIComponent(id)}`);
|
|
1771
|
+
if (!response.success || !response.data?.gate) {
|
|
1772
|
+
throw new ApiError(`Gate lookup failed: ${response.error?.message || 'Gate not found'}`, response.error);
|
|
1773
|
+
}
|
|
1774
|
+
return response.data.gate;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
/**
|
|
1778
|
+
* Post-verify reward delivery for hosted gate checkout. Requires a verified
|
|
1779
|
+
* proof (qHash) for the gate; paid gates also require payment evidence
|
|
1780
|
+
* (paymentCheckoutSessionId for card, or paymentTxHash for USDC).
|
|
1781
|
+
*
|
|
1782
|
+
* @param {object} params
|
|
1783
|
+
* @param {string} params.gateId Published gate handle
|
|
1784
|
+
* @param {string} params.qHash Verified proof receipt id
|
|
1785
|
+
* @param {string} [params.walletAddress] Wallet bound to the proof (required without a session cookie)
|
|
1786
|
+
* @param {string} [params.paymentCheckoutSessionId] Stripe checkout session id (card rail)
|
|
1787
|
+
* @param {string} [params.paymentTxHash] USDC payment transaction hash (wallet rail)
|
|
1788
|
+
* @returns {Promise<object>} `{ success, data: { gateId, qHash, fulfillment, successReturnUrl? } }`
|
|
1789
|
+
*/
|
|
1790
|
+
async fulfillGate(params = {}) {
|
|
1791
|
+
const gateId = String(params.gateId || '').trim();
|
|
1792
|
+
if (!gateId || gateId.length > 80 || !/^[a-zA-Z0-9:_-]+$/.test(gateId)) {
|
|
1793
|
+
throw new ValidationError('Valid gateId is required');
|
|
1794
|
+
}
|
|
1795
|
+
const qHash = String(params.qHash || '').trim();
|
|
1796
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(qHash)) {
|
|
1797
|
+
throw new ValidationError('Valid qHash is required');
|
|
1798
|
+
}
|
|
1799
|
+
const body = { qHash };
|
|
1800
|
+
const walletAddress = String(params.walletAddress || '').trim();
|
|
1801
|
+
if (walletAddress) body.walletAddress = walletAddress;
|
|
1802
|
+
const paymentCheckoutSessionId = String(params.paymentCheckoutSessionId || '').trim();
|
|
1803
|
+
if (paymentCheckoutSessionId) body.paymentCheckoutSessionId = paymentCheckoutSessionId;
|
|
1804
|
+
const paymentTxHash = String(params.paymentTxHash || '').trim();
|
|
1805
|
+
if (paymentTxHash) body.paymentTxHash = paymentTxHash;
|
|
1806
|
+
|
|
1807
|
+
const response = await this._makeRequest(
|
|
1808
|
+
'POST',
|
|
1809
|
+
`/api/v1/gates/${encodeURIComponent(gateId)}/fulfill`,
|
|
1810
|
+
body
|
|
1811
|
+
);
|
|
1812
|
+
if (!response.success) {
|
|
1813
|
+
throw new ApiError(`Gate fulfillment failed: ${response.error?.message || 'Unknown error'}`, response.error);
|
|
1814
|
+
}
|
|
1815
|
+
return response;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1733
1818
|
async checkGate(params) {
|
|
1734
1819
|
const { walletAddress, requirements, proofs: preloadedProofs } = params;
|
|
1735
1820
|
|