@neus/sdk 1.1.6 → 1.2.0
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 +17 -4
- package/cjs/cli-commands.cjs +101 -0
- package/cjs/client.cjs +3 -4
- package/cjs/index.cjs +801 -12
- package/cjs/mcp-hosts.cjs +91 -8
- package/cjs/runtime-adapters.cjs +218 -0
- package/cjs/runtime-mount.cjs +452 -0
- package/cli/neus.mjs +337 -86
- package/client.js +2121 -2122
- package/index.js +69 -2
- package/mcp-hosts.js +54 -12
- package/package.json +17 -2
- package/types.d.ts +90 -1
package/cli/neus.mjs
CHANGED
|
@@ -10,6 +10,13 @@ import {
|
|
|
10
10
|
NEUS_MCP_URL,
|
|
11
11
|
buildNeusMcpHttpConfig
|
|
12
12
|
} from '../mcp-hosts.js';
|
|
13
|
+
import {
|
|
14
|
+
resolveRuntimeBundleFromMcp,
|
|
15
|
+
RUNTIME_MOUNT_SCHEMA,
|
|
16
|
+
normalizeWallet,
|
|
17
|
+
evaluateMountFileHealth
|
|
18
|
+
} from '../runtime-mount.js';
|
|
19
|
+
import { applyRuntimeBundle, readMountManifest } from '../runtime-adapters.js';
|
|
13
20
|
|
|
14
21
|
const __cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
15
22
|
const CLI_PACKAGE_VERSION = (() => {
|
|
@@ -590,7 +597,10 @@ function parseArgs(argv) {
|
|
|
590
597
|
json: false,
|
|
591
598
|
dryRun: false,
|
|
592
599
|
project: false,
|
|
593
|
-
oauth: false
|
|
600
|
+
oauth: false,
|
|
601
|
+
agent: '',
|
|
602
|
+
apply: '',
|
|
603
|
+
agentTarget: ''
|
|
594
604
|
};
|
|
595
605
|
|
|
596
606
|
for (let index = 1; index < argv.length; index += 1) {
|
|
@@ -650,6 +660,24 @@ function parseArgs(argv) {
|
|
|
650
660
|
options.oauth = true;
|
|
651
661
|
continue;
|
|
652
662
|
}
|
|
663
|
+
if (token === '--agent') {
|
|
664
|
+
const value = argv[index + 1];
|
|
665
|
+
if (!value) throw new Error('--agent requires a value');
|
|
666
|
+
options.agent = value.trim();
|
|
667
|
+
index += 1;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (token === '--apply') {
|
|
671
|
+
const value = argv[index + 1];
|
|
672
|
+
if (!value) throw new Error('--apply requires a value (cursor, claude, or codex)');
|
|
673
|
+
options.apply = value.trim().toLowerCase();
|
|
674
|
+
index += 1;
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
if (command === 'mount' && !token.startsWith('-') && !options.agentTarget) {
|
|
678
|
+
options.agentTarget = token;
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
653
681
|
if (token === '--help' || token === '-h') {
|
|
654
682
|
return { command: 'help', options };
|
|
655
683
|
}
|
|
@@ -675,6 +703,7 @@ function printUsage(exitCode = 0) {
|
|
|
675
703
|
' check Confirm setup and live NEUS connection (alias for doctor --live)',
|
|
676
704
|
' examples Show assistant prompts to try after install',
|
|
677
705
|
' doctor Deep check: config status, profile connection, and live MCP context',
|
|
706
|
+
' mount Mount proof-backed agent context for any runtime',
|
|
678
707
|
' import Detect and package supported assistant context for NEUS portability',
|
|
679
708
|
' export Export the latest local NEUS portable agent manifest',
|
|
680
709
|
' help Show this message',
|
|
@@ -688,6 +717,8 @@ function printUsage(exitCode = 0) {
|
|
|
688
717
|
' --to <format> Export format: manifest or json',
|
|
689
718
|
' --output <path> Write exported manifest to a specific path',
|
|
690
719
|
' --live Run live MCP checks (uses IDE credential or --access-key)',
|
|
720
|
+
' --agent <agentId> Agent id for mount (also: neus mount <agentId>)',
|
|
721
|
+
' --apply <cursor|claude|codex> Write mounted agent rules to the current project',
|
|
691
722
|
' --json Print JSON output',
|
|
692
723
|
' --dry-run Preview changes without writing files'
|
|
693
724
|
];
|
|
@@ -1378,6 +1409,151 @@ async function callMcpTool({ name, args, accessKey, sessionId, signal }) {
|
|
|
1378
1409
|
};
|
|
1379
1410
|
}
|
|
1380
1411
|
|
|
1412
|
+
async function initializeMcpSession(accessKey, signal) {
|
|
1413
|
+
const init = await postMcpJsonRpc({
|
|
1414
|
+
id: 1,
|
|
1415
|
+
method: 'initialize',
|
|
1416
|
+
params: {
|
|
1417
|
+
protocolVersion: '2025-11-25',
|
|
1418
|
+
capabilities: {},
|
|
1419
|
+
clientInfo: { name: 'neus-cli', version: CLI_PACKAGE_VERSION }
|
|
1420
|
+
},
|
|
1421
|
+
accessKey,
|
|
1422
|
+
signal
|
|
1423
|
+
});
|
|
1424
|
+
if (!init.response.ok || init.json?.error) {
|
|
1425
|
+
throw new Error(init.json?.error?.message || 'MCP initialize failed');
|
|
1426
|
+
}
|
|
1427
|
+
return { sessionId: init.sessionId || '' };
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
async function evaluateAgentMountDoctor(accessKey, cwd, signal) {
|
|
1431
|
+
const manifest = readMountManifest(cwd);
|
|
1432
|
+
const fileHealth = evaluateMountFileHealth(manifest);
|
|
1433
|
+
const out = {
|
|
1434
|
+
mountFilePresent: Boolean(manifest),
|
|
1435
|
+
mountFileValid: fileHealth.mountFileValid,
|
|
1436
|
+
mountNeedsRefresh: fileHealth.needsRefresh,
|
|
1437
|
+
mountRefreshReason: fileHealth.reason,
|
|
1438
|
+
missingDelegation: fileHealth.missingDelegation,
|
|
1439
|
+
delegationExpired: fileHealth.delegationExpired,
|
|
1440
|
+
mountAgentId: manifest?.identity?.agentId || null,
|
|
1441
|
+
agentVerified: false,
|
|
1442
|
+
agentLinkStatus: null
|
|
1443
|
+
};
|
|
1444
|
+
if (!accessKey) return out;
|
|
1445
|
+
|
|
1446
|
+
let sessionId = '';
|
|
1447
|
+
try {
|
|
1448
|
+
const init = await initializeMcpSession(accessKey, signal);
|
|
1449
|
+
sessionId = init.sessionId;
|
|
1450
|
+
} catch {
|
|
1451
|
+
return out;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
const agentId = out.mountAgentId || manifest?.identity?.agentId;
|
|
1455
|
+
const agentWallet = manifest?.identity?.agentWallet;
|
|
1456
|
+
if (agentWallet) {
|
|
1457
|
+
const link = await callMcpTool({
|
|
1458
|
+
name: 'neus_agent_link',
|
|
1459
|
+
args: { agentWallet },
|
|
1460
|
+
accessKey,
|
|
1461
|
+
sessionId,
|
|
1462
|
+
signal
|
|
1463
|
+
});
|
|
1464
|
+
if (link.ok) {
|
|
1465
|
+
out.agentLinkStatus = link.payload?.status || (link.payload?.linked ? 'ok' : 'link_required');
|
|
1466
|
+
out.agentVerified = Boolean(link.payload?.linked);
|
|
1467
|
+
}
|
|
1468
|
+
} else if (agentId) {
|
|
1469
|
+
try {
|
|
1470
|
+
const bundle = await resolveRuntimeBundleFromMcp({
|
|
1471
|
+
callMcpTool: args => callMcpTool({ ...args, accessKey, sessionId, signal }),
|
|
1472
|
+
accessKey,
|
|
1473
|
+
agentId,
|
|
1474
|
+
signal
|
|
1475
|
+
});
|
|
1476
|
+
out.agentVerified = Boolean(bundle?.trust?.identityQHash && bundle?.delegation);
|
|
1477
|
+
out.mountAgentId = bundle.identity?.agentId || agentId;
|
|
1478
|
+
} catch {
|
|
1479
|
+
out.agentVerified = false;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return out;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
async function runMount(options) {
|
|
1486
|
+
const cwd = process.cwd();
|
|
1487
|
+
const scope = resolveScope(options);
|
|
1488
|
+
const accessKey = resolveLiveAccessKey(options, scope, cwd);
|
|
1489
|
+
const agentTarget = String(options.agentTarget || options.agent || '').trim();
|
|
1490
|
+
if (!agentTarget) {
|
|
1491
|
+
throw new Error('Usage: neus mount <agentId> [--apply cursor|claude|codex]');
|
|
1492
|
+
}
|
|
1493
|
+
if (!accessKey) {
|
|
1494
|
+
throw new Error('Credential required. Run `neus auth` or pass --access-key.');
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
const controller = new AbortController();
|
|
1498
|
+
const timeout = setTimeout(() => controller.abort(), 30000);
|
|
1499
|
+
try {
|
|
1500
|
+
const bundle = await resolveRuntimeBundleFromMcp({
|
|
1501
|
+
callMcpTool: args => callMcpTool({ ...args, accessKey, signal: controller.signal }),
|
|
1502
|
+
initializeMcp: () => initializeMcpSession(accessKey, controller.signal),
|
|
1503
|
+
accessKey,
|
|
1504
|
+
agentId: agentTarget,
|
|
1505
|
+
signal: controller.signal
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
const applyFlavor = String(options.apply || '').trim().toLowerCase();
|
|
1509
|
+
let applyResult = null;
|
|
1510
|
+
if (applyFlavor) {
|
|
1511
|
+
if (!['cursor', 'claude', 'codex'].includes(applyFlavor)) {
|
|
1512
|
+
throw new Error('--apply must be cursor, claude, or codex');
|
|
1513
|
+
}
|
|
1514
|
+
applyResult = applyRuntimeBundle(applyFlavor, bundle, cwd, { dryRun: options.dryRun });
|
|
1515
|
+
} else if (!options.json) {
|
|
1516
|
+
applyRuntimeBundle('cursor', bundle, cwd, { dryRun: options.dryRun });
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
const payload = {
|
|
1520
|
+
command: 'mount',
|
|
1521
|
+
schema: RUNTIME_MOUNT_SCHEMA,
|
|
1522
|
+
agentId: bundle.identity.agentId,
|
|
1523
|
+
bundle,
|
|
1524
|
+
applied: applyResult,
|
|
1525
|
+
dryRun: Boolean(options.dryRun)
|
|
1526
|
+
};
|
|
1527
|
+
|
|
1528
|
+
if (options.json) {
|
|
1529
|
+
printJson(payload);
|
|
1530
|
+
return payload;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
emitCliBanner(options);
|
|
1534
|
+
writeCliLine(paint('mount', 'green'));
|
|
1535
|
+
logStep('ok', 'agent', bundle.identity.agentLabel || bundle.identity.agentId);
|
|
1536
|
+
writeGuidanceLine(`Identity receipt: ${bundle.trust.identityProofUrl}`);
|
|
1537
|
+
if (bundle.trust.delegationProofUrl) {
|
|
1538
|
+
writeGuidanceLine(`Delegation receipt: ${bundle.trust.delegationProofUrl}`);
|
|
1539
|
+
} else {
|
|
1540
|
+
writeGuidanceLine('Delegation not on file — run agent setup on neus.network before scoped actions.');
|
|
1541
|
+
}
|
|
1542
|
+
if (applyResult) {
|
|
1543
|
+
for (const filePath of applyResult.written) {
|
|
1544
|
+
logStep('ok', 'wrote', filePath);
|
|
1545
|
+
}
|
|
1546
|
+
} else if (!options.dryRun) {
|
|
1547
|
+
logStep('ok', 'wrote', path.join(cwd, '.neus', 'mount.json'));
|
|
1548
|
+
}
|
|
1549
|
+
writeGuidanceLine('Start a new Agent chat so mounted rules load. Use NEUS Verify before sensitive actions.');
|
|
1550
|
+
writeCliLine('');
|
|
1551
|
+
return payload;
|
|
1552
|
+
} finally {
|
|
1553
|
+
clearTimeout(timeout);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1381
1557
|
async function runLiveMcpDiagnostics(accessKey) {
|
|
1382
1558
|
if (!accessKey) {
|
|
1383
1559
|
return {
|
|
@@ -1603,96 +1779,108 @@ async function runAuthBrowser(options) {
|
|
|
1603
1779
|
const codeChallenge = deriveCodeChallenge(codeVerifier);
|
|
1604
1780
|
|
|
1605
1781
|
return new Promise((resolve, reject) => {
|
|
1782
|
+
let settled = false;
|
|
1783
|
+
function finish(error, value) {
|
|
1784
|
+
if (settled) return;
|
|
1785
|
+
settled = true;
|
|
1786
|
+
server.close();
|
|
1787
|
+
if (error) reject(error);
|
|
1788
|
+
else resolve(value);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1606
1791
|
const server = createServer((req, res) => {
|
|
1607
1792
|
const url = new URL(req.url, `http://127.0.0.1:${server.address().port}`);
|
|
1608
|
-
if (url.pathname === '/callback') {
|
|
1609
|
-
const returnedState = url.searchParams.get('state');
|
|
1610
|
-
if (!returnedState || returnedState !== csrfState) {
|
|
1611
|
-
res.writeHead(403, { 'Content-Type': 'text/html' });
|
|
1612
|
-
res.end('<html><body><h2>Security check failed</h2><p>Invalid request. Try again.</p></body></html>');
|
|
1613
|
-
server.close();
|
|
1614
|
-
reject(new Error('CSRF state mismatch'));
|
|
1615
|
-
return;
|
|
1616
|
-
}
|
|
1617
1793
|
|
|
1618
|
-
|
|
1619
|
-
|
|
1794
|
+
// Ignore browser noise; keep the server alive for the real callback.
|
|
1795
|
+
if (url.pathname === '/favicon.ico') {
|
|
1796
|
+
res.writeHead(204);
|
|
1797
|
+
res.end();
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1620
1800
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
return;
|
|
1627
|
-
}
|
|
1801
|
+
if (url.pathname !== '/callback') {
|
|
1802
|
+
res.writeHead(404);
|
|
1803
|
+
res.end();
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1628
1806
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1807
|
+
const returnedState = url.searchParams.get('state');
|
|
1808
|
+
if (!returnedState || returnedState !== csrfState) {
|
|
1809
|
+
res.writeHead(403, { 'Content-Type': 'text/html' });
|
|
1810
|
+
res.end('<html><body><h2>Security check failed</h2><p>Invalid request. Try again.</p></body></html>');
|
|
1811
|
+
finish(new Error('CSRF state mismatch'));
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1636
1814
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
params.set('grant_type', 'authorization_code');
|
|
1640
|
-
params.set('code', code);
|
|
1641
|
-
params.set('redirect_uri', redirectUri);
|
|
1642
|
-
params.set('client_id', NEUS_OAUTH_CLIENT_ID);
|
|
1643
|
-
params.set('code_verifier', codeVerifier);
|
|
1644
|
-
params.set('resource', NEUS_MCP_RESOURCE);
|
|
1815
|
+
const code = url.searchParams.get('code');
|
|
1816
|
+
const error = url.searchParams.get('error');
|
|
1645
1817
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
})
|
|
1652
|
-
.then(tokenResp => tokenResp.json())
|
|
1653
|
-
.then(tokenJson => {
|
|
1654
|
-
if (!tokenJson.access_token) {
|
|
1655
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1656
|
-
res.end('<html><body><h2>Token exchange failed</h2><p>Please try again.</p></body></html>');
|
|
1657
|
-
server.close();
|
|
1658
|
-
reject(new Error(tokenJson.error_description || tokenJson.error || 'Token exchange failed'));
|
|
1659
|
-
return;
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
|
-
const accessToken = tokenJson.access_token;
|
|
1663
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1664
|
-
res.end('<html><body><h2>Authenticated</h2><p>You can close this tab and return to your terminal.</p></body></html>');
|
|
1665
|
-
server.close();
|
|
1666
|
-
|
|
1667
|
-
const results = runClientOperations(browserManagedClients, scope, cwd, options.dryRun, client =>
|
|
1668
|
-
installClient(client, scope, accessToken, options.dryRun, cwd)
|
|
1669
|
-
);
|
|
1670
|
-
results.push(
|
|
1671
|
-
...runClientOperations(hostManagedClients, scope, cwd, options.dryRun, () =>
|
|
1672
|
-
authCodex(scope, options.dryRun, cwd, options)
|
|
1673
|
-
)
|
|
1674
|
-
);
|
|
1675
|
-
const payload = {
|
|
1676
|
-
command: 'auth',
|
|
1677
|
-
scope,
|
|
1678
|
-
clients,
|
|
1679
|
-
accessKeyConfigured: true,
|
|
1680
|
-
authMethod: 'browser',
|
|
1681
|
-
results,
|
|
1682
|
-
hasErrors: results.some(result => result.error)
|
|
1683
|
-
};
|
|
1684
|
-
resolve(payload);
|
|
1685
|
-
})
|
|
1686
|
-
.catch(err => {
|
|
1687
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1688
|
-
res.end('<html><body><h2>Connection error</h2><p>Please try again.</p></body></html>');
|
|
1689
|
-
server.close();
|
|
1690
|
-
reject(err);
|
|
1691
|
-
});
|
|
1692
|
-
} else {
|
|
1693
|
-
res.writeHead(404);
|
|
1694
|
-
res.end();
|
|
1818
|
+
if (error) {
|
|
1819
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1820
|
+
res.end('<html><body><h2>Authentication failed</h2><p>You can close this tab and try again.</p></body></html>');
|
|
1821
|
+
finish(new Error(`Authentication failed: ${error}`));
|
|
1822
|
+
return;
|
|
1695
1823
|
}
|
|
1824
|
+
|
|
1825
|
+
if (!code) {
|
|
1826
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1827
|
+
res.end('<html><body><h2>Missing auth code</h2><p>You can close this tab and try again.</p></body></html>');
|
|
1828
|
+
finish(new Error('No auth code received from callback'));
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
const redirectUri = `http://127.0.0.1:${server.address().port}/callback`;
|
|
1833
|
+
const params = new URLSearchParams();
|
|
1834
|
+
params.set('grant_type', 'authorization_code');
|
|
1835
|
+
params.set('code', code);
|
|
1836
|
+
params.set('redirect_uri', redirectUri);
|
|
1837
|
+
params.set('client_id', NEUS_OAUTH_CLIENT_ID);
|
|
1838
|
+
params.set('code_verifier', codeVerifier);
|
|
1839
|
+
params.set('resource', NEUS_MCP_RESOURCE);
|
|
1840
|
+
|
|
1841
|
+
fetch(NEUS_TOKEN_ENDPOINT, {
|
|
1842
|
+
method: 'POST',
|
|
1843
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
|
|
1844
|
+
body: params.toString(),
|
|
1845
|
+
signal: AbortSignal.timeout(15_000),
|
|
1846
|
+
})
|
|
1847
|
+
.then(tokenResp => tokenResp.json())
|
|
1848
|
+
.then(tokenJson => {
|
|
1849
|
+
if (!tokenJson.access_token) {
|
|
1850
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1851
|
+
res.end('<html><body><h2>Token exchange failed</h2><p>Please try again.</p></body></html>');
|
|
1852
|
+
finish(new Error(tokenJson.error_description || tokenJson.error || 'Token exchange failed'));
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
const accessToken = tokenJson.access_token;
|
|
1857
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1858
|
+
res.end('<html><body><h2>Authenticated</h2><p>You can close this tab and return to your terminal.</p></body></html>');
|
|
1859
|
+
|
|
1860
|
+
const results = runClientOperations(browserManagedClients, scope, cwd, options.dryRun, client =>
|
|
1861
|
+
installClient(client, scope, accessToken, options.dryRun, cwd)
|
|
1862
|
+
);
|
|
1863
|
+
results.push(
|
|
1864
|
+
...runClientOperations(hostManagedClients, scope, cwd, options.dryRun, () =>
|
|
1865
|
+
authCodex(scope, options.dryRun, cwd, options)
|
|
1866
|
+
)
|
|
1867
|
+
);
|
|
1868
|
+
const payload = {
|
|
1869
|
+
command: 'auth',
|
|
1870
|
+
scope,
|
|
1871
|
+
clients,
|
|
1872
|
+
accessKeyConfigured: true,
|
|
1873
|
+
authMethod: 'browser',
|
|
1874
|
+
results,
|
|
1875
|
+
hasErrors: results.some(result => result.error)
|
|
1876
|
+
};
|
|
1877
|
+
finish(null, payload);
|
|
1878
|
+
})
|
|
1879
|
+
.catch(err => {
|
|
1880
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1881
|
+
res.end('<html><body><h2>Connection error</h2><p>Please try again.</p></body></html>');
|
|
1882
|
+
finish(err);
|
|
1883
|
+
});
|
|
1696
1884
|
});
|
|
1697
1885
|
|
|
1698
1886
|
server.listen(0, '127.0.0.1', () => {
|
|
@@ -1728,10 +1916,13 @@ async function runAuthBrowser(options) {
|
|
|
1728
1916
|
});
|
|
1729
1917
|
|
|
1730
1918
|
// Timeout after 5 minutes
|
|
1731
|
-
setTimeout(() => {
|
|
1732
|
-
|
|
1733
|
-
reject(new Error('Authentication timed out after 5 minutes. Try again.'));
|
|
1919
|
+
const timeout = setTimeout(() => {
|
|
1920
|
+
finish(new Error('Authentication timed out after 5 minutes. Try again.'));
|
|
1734
1921
|
}, 5 * 60 * 1000);
|
|
1922
|
+
|
|
1923
|
+
server.on('close', () => {
|
|
1924
|
+
clearTimeout(timeout);
|
|
1925
|
+
});
|
|
1735
1926
|
});
|
|
1736
1927
|
}
|
|
1737
1928
|
|
|
@@ -1864,6 +2055,19 @@ async function runSetup(options) {
|
|
|
1864
2055
|
return authResult || payload;
|
|
1865
2056
|
}
|
|
1866
2057
|
|
|
2058
|
+
if (options.agent && !options.dryRun) {
|
|
2059
|
+
const mountKey = resolveLiveAccessKey(options, scope, cwd);
|
|
2060
|
+
if (mountKey) {
|
|
2061
|
+
await runMount({
|
|
2062
|
+
...options,
|
|
2063
|
+
agentTarget: options.agent,
|
|
2064
|
+
apply: options.apply || 'cursor',
|
|
2065
|
+
json: false,
|
|
2066
|
+
live: true
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
|
|
1867
2071
|
return payload;
|
|
1868
2072
|
}
|
|
1869
2073
|
|
|
@@ -1945,6 +2149,7 @@ const ASSISTANT_EXAMPLE_PROMPTS = [
|
|
|
1945
2149
|
'Use NEUS Verify before taking sensitive actions.',
|
|
1946
2150
|
'Check whether I already have the required trust receipt.',
|
|
1947
2151
|
'Verify this agent is trusted before it runs tools.',
|
|
2152
|
+
'Mount my NEUS agent context with neus_agent_mount, then follow its scoped policy.',
|
|
1948
2153
|
'Use NEUS Vault before storing or using secrets.',
|
|
1949
2154
|
'Show the receipt for this verification.'
|
|
1950
2155
|
];
|
|
@@ -2004,7 +2209,29 @@ async function runDoctor(options) {
|
|
|
2004
2209
|
payload.profileConnectable = Boolean(payload.mcp.authenticated);
|
|
2005
2210
|
payload.hasErrors =
|
|
2006
2211
|
payload.hasErrors || !payload.mcp.reachable || !payload.mcp.authenticated;
|
|
2212
|
+
try {
|
|
2213
|
+
const agentDoctor = await evaluateAgentMountDoctor(
|
|
2214
|
+
liveAccessKey,
|
|
2215
|
+
cwd,
|
|
2216
|
+
AbortSignal.timeout(20000)
|
|
2217
|
+
);
|
|
2218
|
+
payload.agentVerified = agentDoctor.agentVerified;
|
|
2219
|
+
payload.mountFilePresent = agentDoctor.mountFilePresent;
|
|
2220
|
+
payload.mountFileValid = agentDoctor.mountFileValid;
|
|
2221
|
+
payload.mountNeedsRefresh = agentDoctor.mountNeedsRefresh;
|
|
2222
|
+
payload.mountRefreshReason = agentDoctor.mountRefreshReason;
|
|
2223
|
+
payload.mountAgentId = agentDoctor.mountAgentId;
|
|
2224
|
+
payload.agentLinkStatus = agentDoctor.agentLinkStatus;
|
|
2225
|
+
payload.delegationExpired = agentDoctor.delegationExpired;
|
|
2226
|
+
payload.missingDelegation = agentDoctor.missingDelegation;
|
|
2227
|
+
} catch {
|
|
2228
|
+
payload.agentVerified = false;
|
|
2229
|
+
}
|
|
2007
2230
|
}
|
|
2231
|
+
} else {
|
|
2232
|
+
const manifest = readMountManifest(cwd);
|
|
2233
|
+
payload.mountFilePresent = Boolean(manifest);
|
|
2234
|
+
payload.mountAgentId = manifest?.identity?.agentId || null;
|
|
2008
2235
|
}
|
|
2009
2236
|
|
|
2010
2237
|
if (options.json) {
|
|
@@ -2053,6 +2280,26 @@ async function runDoctor(options) {
|
|
|
2053
2280
|
logStep('ok', 'profile', `connected${handle}${receipts}`);
|
|
2054
2281
|
writeGuidanceLine('NEUS Verify is ready. Ask your assistant to verify trust before sensitive actions.');
|
|
2055
2282
|
writeGuidanceLine('Run `npx -y -p @neus/sdk neus examples` for starter prompts.');
|
|
2283
|
+
if (payload.mountFilePresent) {
|
|
2284
|
+
logStep('ok', 'mount', payload.mountAgentId ? `project mount: ${payload.mountAgentId}` : 'project mount on file');
|
|
2285
|
+
}
|
|
2286
|
+
if (payload.mountNeedsRefresh) {
|
|
2287
|
+
const reason =
|
|
2288
|
+
payload.delegationExpired
|
|
2289
|
+
? 'delegation expired'
|
|
2290
|
+
: payload.missingDelegation
|
|
2291
|
+
? 'delegation missing on file'
|
|
2292
|
+
: 'mount stale';
|
|
2293
|
+
logStep('warn', 'mount', `${reason} — run \`neus mount ${payload.mountAgentId || '<agentId>'} --apply cursor\``);
|
|
2294
|
+
payload.hasErrors = true;
|
|
2295
|
+
} else if (payload.agentVerified) {
|
|
2296
|
+
logStep('ok', 'agent', 'identity and delegation on file');
|
|
2297
|
+
} else if (payload.mountAgentId || payload.mountFilePresent) {
|
|
2298
|
+
writeGuidanceLine(
|
|
2299
|
+
`Mounted agent is not fully linked yet. Run \`neus mount ${payload.mountAgentId || '<agentId>'} --apply cursor\` after auth.`
|
|
2300
|
+
);
|
|
2301
|
+
payload.hasErrors = true;
|
|
2302
|
+
}
|
|
2056
2303
|
} else {
|
|
2057
2304
|
logStep('warn', 'profile', 'live connection was not confirmed — run `neus auth`');
|
|
2058
2305
|
}
|
|
@@ -2196,6 +2443,10 @@ async function main() {
|
|
|
2196
2443
|
await runDoctor(options);
|
|
2197
2444
|
return;
|
|
2198
2445
|
}
|
|
2446
|
+
if (command === 'mount') {
|
|
2447
|
+
await runMount(options);
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2199
2450
|
if (command === 'examples') {
|
|
2200
2451
|
runExamples(options);
|
|
2201
2452
|
return;
|