@neus/sdk 1.2.0 → 1.2.1
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/index.cjs +10 -188
- package/cjs/runtime-mount.cjs +2 -2
- package/cli-commands.js +75 -0
- package/index.js +1 -10
- package/package.json +4 -1
- package/runtime-adapters.js +214 -0
- package/runtime-mount.js +522 -0
package/cjs/index.cjs
CHANGED
|
@@ -430,9 +430,9 @@ function deriveDid(address, chainIdOrChain) {
|
|
|
430
430
|
async function resolveDID(params, options = {}) {
|
|
431
431
|
const endpointPath = options.endpoint || "/api/v1/profile/did/resolve";
|
|
432
432
|
const apiUrl = typeof options.apiUrl === "string" ? options.apiUrl.trim() : "";
|
|
433
|
-
const resolveEndpoint = (
|
|
434
|
-
if (!
|
|
435
|
-
const trimmedPath =
|
|
433
|
+
const resolveEndpoint = (path) => {
|
|
434
|
+
if (!path || typeof path !== "string") return null;
|
|
435
|
+
const trimmedPath = path.trim();
|
|
436
436
|
if (!trimmedPath) return null;
|
|
437
437
|
if (/^https?:\/\//i.test(trimmedPath)) return trimmedPath;
|
|
438
438
|
if (trimmedPath.startsWith("/")) {
|
|
@@ -491,9 +491,9 @@ async function resolveDID(params, options = {}) {
|
|
|
491
491
|
async function standardizeVerificationRequest(params, options = {}) {
|
|
492
492
|
const endpointPath = options.endpoint || "/api/v1/verification/standardize";
|
|
493
493
|
const apiUrl = typeof options.apiUrl === "string" ? options.apiUrl.trim() : "";
|
|
494
|
-
const resolveEndpoint = (
|
|
495
|
-
if (!
|
|
496
|
-
const trimmedPath =
|
|
494
|
+
const resolveEndpoint = (path) => {
|
|
495
|
+
if (!path || typeof path !== "string") return null;
|
|
496
|
+
const trimmedPath = path.trim();
|
|
497
497
|
if (!trimmedPath) return null;
|
|
498
498
|
if (/^https?:\/\//i.test(trimmedPath)) return trimmedPath;
|
|
499
499
|
if (trimmedPath.startsWith("/")) {
|
|
@@ -3004,7 +3004,6 @@ __export(index_exports, {
|
|
|
3004
3004
|
MCP_INSTALL_CLIENTS: () => MCP_INSTALL_CLIENTS,
|
|
3005
3005
|
MCP_INSTALL_HOSTS: () => MCP_INSTALL_HOSTS,
|
|
3006
3006
|
MONTH: () => MONTH,
|
|
3007
|
-
MOUNT_MANIFEST_RELATIVE: () => MOUNT_MANIFEST_RELATIVE,
|
|
3008
3007
|
NEUS_AUTH_CLI: () => NEUS_AUTH_CLI,
|
|
3009
3008
|
NEUS_AUTH_NPX: () => NEUS_AUTH_NPX,
|
|
3010
3009
|
NEUS_CHECK_CLI: () => NEUS_CHECK_CLI,
|
|
@@ -3035,7 +3034,6 @@ __export(index_exports, {
|
|
|
3035
3034
|
VerificationError: () => VerificationError,
|
|
3036
3035
|
WEEK: () => WEEK,
|
|
3037
3036
|
YEAR: () => YEAR,
|
|
3038
|
-
applyRuntimeBundle: () => applyRuntimeBundle,
|
|
3039
3037
|
buildAuthCommandForClient: () => buildAuthCommandForClient,
|
|
3040
3038
|
buildCursorMcpInstallUrl: () => buildCursorMcpInstallUrl,
|
|
3041
3039
|
buildNeusMcpHttpConfig: () => buildNeusMcpHttpConfig,
|
|
@@ -3045,9 +3043,6 @@ __export(index_exports, {
|
|
|
3045
3043
|
buildSetupNpxOneLiner: () => buildSetupNpxOneLiner,
|
|
3046
3044
|
buildVerificationRequest: () => buildVerificationRequest,
|
|
3047
3045
|
buildVsCodeMcpInstallUrl: () => buildVsCodeMcpInstallUrl,
|
|
3048
|
-
bundleToClaudeMd: () => bundleToClaudeMd,
|
|
3049
|
-
bundleToCodexJson: () => bundleToCodexJson,
|
|
3050
|
-
bundleToCursorRules: () => bundleToCursorRules,
|
|
3051
3046
|
combineGates: () => combineGates,
|
|
3052
3047
|
computeContentHash: () => computeContentHash,
|
|
3053
3048
|
constructVerificationMessage: () => constructVerificationMessage,
|
|
@@ -3077,12 +3072,10 @@ __export(index_exports, {
|
|
|
3077
3072
|
pickActiveDelegation: () => pickActiveDelegation,
|
|
3078
3073
|
pickIdentity: () => pickIdentity,
|
|
3079
3074
|
profileAgentToIdentitySeed: () => profileAgentToIdentitySeed,
|
|
3080
|
-
readMountManifest: () => readMountManifest,
|
|
3081
3075
|
resolveDID: () => resolveDID,
|
|
3082
3076
|
resolveEffectiveRuntime: () => resolveEffectiveRuntime,
|
|
3083
3077
|
resolveRuntimeBundleFromMcp: () => resolveRuntimeBundleFromMcp,
|
|
3084
3078
|
resolveZkPassportConfig: () => resolveZkPassportConfig,
|
|
3085
|
-
sanitizeAgentIdForFilename: () => sanitizeAgentIdForFilename,
|
|
3086
3079
|
signMessage: () => signMessage,
|
|
3087
3080
|
standardizeVerificationRequest: () => standardizeVerificationRequest,
|
|
3088
3081
|
supportsMcpInstallDeeplink: () => supportsMcpInstallDeeplink,
|
|
@@ -3094,8 +3087,7 @@ __export(index_exports, {
|
|
|
3094
3087
|
validateUniversalAddress: () => validateUniversalAddress,
|
|
3095
3088
|
validateVerifierPayload: () => validateVerifierPayload,
|
|
3096
3089
|
validateWalletAddress: () => validateWalletAddress,
|
|
3097
|
-
withRetry: () => withRetry
|
|
3098
|
-
writeMountManifest: () => writeMountManifest
|
|
3090
|
+
withRetry: () => withRetry
|
|
3099
3091
|
});
|
|
3100
3092
|
module.exports = __toCommonJS(index_exports);
|
|
3101
3093
|
init_client();
|
|
@@ -3164,7 +3156,7 @@ function capabilitiesToArray(caps) {
|
|
|
3164
3156
|
return Object.entries(caps).filter(([, enabled]) => enabled === true).map(([key]) => String(key).trim()).filter(Boolean);
|
|
3165
3157
|
}
|
|
3166
3158
|
function isDelegationExpired(expiresAt) {
|
|
3167
|
-
if (expiresAt === null || expiresAt ===
|
|
3159
|
+
if (expiresAt === null || expiresAt === 0) return false;
|
|
3168
3160
|
const ms = Number(expiresAt);
|
|
3169
3161
|
return Number.isFinite(ms) && ms > 0 && ms <= Date.now();
|
|
3170
3162
|
}
|
|
@@ -3261,7 +3253,7 @@ function extractAgentContextFromProofs(proofs) {
|
|
|
3261
3253
|
runtimePolicy: vvData.runtimePolicy && typeof vvData.runtimePolicy === "object" ? vvData.runtimePolicy : void 0,
|
|
3262
3254
|
expiresAt: vvData.expiresAt ?? null,
|
|
3263
3255
|
isExpired: isDelegationExpired(vvData.expiresAt),
|
|
3264
|
-
maxSpend: vvData.maxSpend !== null
|
|
3256
|
+
maxSpend: vvData.maxSpend !== null ? String(vvData.maxSpend) : void 0,
|
|
3265
3257
|
instructions: vvData.instructions || null,
|
|
3266
3258
|
skills: Array.isArray(vvData.skills) ? vvData.skills : [],
|
|
3267
3259
|
provider: vvData.provider || vvData.modelProvider || null,
|
|
@@ -3546,168 +3538,6 @@ function evaluateMountFileHealth(manifest) {
|
|
|
3546
3538
|
};
|
|
3547
3539
|
}
|
|
3548
3540
|
|
|
3549
|
-
// runtime-adapters.js
|
|
3550
|
-
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
3551
|
-
var import_node_path = __toESM(require("node:path"), 1);
|
|
3552
|
-
var MOUNT_MANIFEST_RELATIVE = import_node_path.default.join(".neus", "mount.json");
|
|
3553
|
-
function sanitizeAgentIdForFilename(agentId) {
|
|
3554
|
-
return String(agentId || "agent").trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "agent";
|
|
3555
|
-
}
|
|
3556
|
-
function bundleToCursorRules(bundle) {
|
|
3557
|
-
const id = bundle.identity.agentId;
|
|
3558
|
-
const label = bundle.identity.agentLabel || id;
|
|
3559
|
-
const skillsBlock = (bundle.identity.skills || []).map((skill) => {
|
|
3560
|
-
if (typeof skill === "string") return `- ${skill}`;
|
|
3561
|
-
const labelText = skill.label || skill.id || "skill";
|
|
3562
|
-
const kind = skill.kind || "skill";
|
|
3563
|
-
const provider = skill.provider ? ` / ${skill.provider}` : "";
|
|
3564
|
-
return `- ${labelText} (${kind}${provider})`;
|
|
3565
|
-
}).join("\n");
|
|
3566
|
-
const denied = (bundle.enforce.deniedActions || []).map((action) => `- ${action}`).join("\n");
|
|
3567
|
-
const capabilities = (bundle.identity.capabilities || []).map((cap) => `- ${cap}`).join("\n");
|
|
3568
|
-
const services = (bundle.identity.services || []).map((svc) => `- ${svc.name}: ${svc.endpoint}${svc.version ? ` (v${svc.version})` : ""}`).join("\n");
|
|
3569
|
-
return `---
|
|
3570
|
-
description: NEUS proof-backed agent \u2014 ${label}
|
|
3571
|
-
globs:
|
|
3572
|
-
alwaysApply: true
|
|
3573
|
-
---
|
|
3574
|
-
|
|
3575
|
-
# NEUS Agent \u2014 ${label}
|
|
3576
|
-
|
|
3577
|
-
You are **${label}** (\`${id}\`). This project mounted trust context from NEUS.
|
|
3578
|
-
|
|
3579
|
-
## Identity
|
|
3580
|
-
${bundle.identity.description || bundle.identity.instructions || "Follow the agent instructions below."}
|
|
3581
|
-
|
|
3582
|
-
## Instructions
|
|
3583
|
-
${bundle.identity.instructions || "Use NEUS MCP for trust checks before sensitive actions."}
|
|
3584
|
-
|
|
3585
|
-
## Capabilities
|
|
3586
|
-
${capabilities || "- General purpose"}
|
|
3587
|
-
|
|
3588
|
-
## Skills
|
|
3589
|
-
${skillsBlock || "- None configured"}
|
|
3590
|
-
|
|
3591
|
-
## Services
|
|
3592
|
-
${services || "- None configured"}
|
|
3593
|
-
|
|
3594
|
-
## Scoped policy
|
|
3595
|
-
${denied ? `Denied actions (do not perform without new approval):
|
|
3596
|
-
${denied}` : "- Follow delegation on file via NEUS MCP."}
|
|
3597
|
-
|
|
3598
|
-
## Trust workflow
|
|
3599
|
-
1. Call \`neus_context\` once per session when NEUS MCP is available.
|
|
3600
|
-
2. Trust before action: \`neus_proofs_check\` then \`neus_verify_or_guide\`.
|
|
3601
|
-
3. Do not invent qHashes, wallets, or receipt fields.
|
|
3602
|
-
4. Summarize NEUS outcomes as Trust Result \u2014 never dump raw tool JSON.
|
|
3603
|
-
|
|
3604
|
-
## Proof references
|
|
3605
|
-
- Identity: ${bundle.trust.identityProofUrl}
|
|
3606
|
-
${bundle.trust.delegationProofUrl ? `- Delegation: ${bundle.trust.delegationProofUrl}` : "- Delegation: not on file \u2014 call `neus_agent_link` before acting as this agent."}
|
|
3607
|
-
`;
|
|
3608
|
-
}
|
|
3609
|
-
function bundleToClaudeMd(bundle) {
|
|
3610
|
-
const id = bundle.identity.agentId;
|
|
3611
|
-
const label = bundle.identity.agentLabel || id;
|
|
3612
|
-
return `# NEUS Agent \u2014 ${label}
|
|
3613
|
-
|
|
3614
|
-
Mounted from NEUS Runtime Mount (\`${RUNTIME_MOUNT_SCHEMA}\`).
|
|
3615
|
-
|
|
3616
|
-
## Identity
|
|
3617
|
-
- **Agent ID:** ${id}
|
|
3618
|
-
- **Label:** ${label}
|
|
3619
|
-
|
|
3620
|
-
## Description
|
|
3621
|
-
${bundle.identity.description || "Proof-backed agent on NEUS Network."}
|
|
3622
|
-
|
|
3623
|
-
## Instructions
|
|
3624
|
-
${bundle.identity.instructions || "Use NEUS MCP before sensitive actions."}
|
|
3625
|
-
|
|
3626
|
-
## Trust receipts
|
|
3627
|
-
- Identity: \`${bundle.trust.identityQHash}\` \u2014 ${bundle.trust.identityProofUrl}
|
|
3628
|
-
${bundle.trust.delegationQHash ? `- Delegation: \`${bundle.trust.delegationQHash}\` \u2014 ${bundle.trust.delegationProofUrl}` : ""}
|
|
3629
|
-
|
|
3630
|
-
## Policy
|
|
3631
|
-
- Do not invent qHashes or verifier outcomes.
|
|
3632
|
-
- Call \`neus_context\` once; use profile context when signed in.
|
|
3633
|
-
`;
|
|
3634
|
-
}
|
|
3635
|
-
function bundleToCodexJson(bundle) {
|
|
3636
|
-
return JSON.stringify(
|
|
3637
|
-
{
|
|
3638
|
-
schema: RUNTIME_MOUNT_SCHEMA,
|
|
3639
|
-
name: bundle.identity.agentLabel,
|
|
3640
|
-
agentId: bundle.identity.agentId,
|
|
3641
|
-
agentWallet: bundle.identity.agentWallet,
|
|
3642
|
-
description: bundle.identity.description,
|
|
3643
|
-
instructions: bundle.identity.instructions,
|
|
3644
|
-
capabilities: bundle.identity.capabilities,
|
|
3645
|
-
skills: bundle.identity.skills,
|
|
3646
|
-
services: bundle.identity.services,
|
|
3647
|
-
effectiveRuntime: bundle.effectiveRuntime,
|
|
3648
|
-
enforce: bundle.enforce,
|
|
3649
|
-
trust: bundle.trust,
|
|
3650
|
-
mountedAt: bundle.mountedAt
|
|
3651
|
-
},
|
|
3652
|
-
null,
|
|
3653
|
-
2
|
|
3654
|
-
);
|
|
3655
|
-
}
|
|
3656
|
-
function readMountManifest(cwd) {
|
|
3657
|
-
const manifestPath = import_node_path.default.join(cwd, MOUNT_MANIFEST_RELATIVE);
|
|
3658
|
-
if (!import_node_fs.default.existsSync(manifestPath)) return null;
|
|
3659
|
-
try {
|
|
3660
|
-
const parsed = JSON.parse(import_node_fs.default.readFileSync(manifestPath, "utf8"));
|
|
3661
|
-
return parsed?.schema === RUNTIME_MOUNT_SCHEMA ? parsed : null;
|
|
3662
|
-
} catch {
|
|
3663
|
-
return null;
|
|
3664
|
-
}
|
|
3665
|
-
}
|
|
3666
|
-
function writeMountManifest(bundle, cwd) {
|
|
3667
|
-
const dir = import_node_path.default.join(cwd, ".neus");
|
|
3668
|
-
import_node_fs.default.mkdirSync(dir, { recursive: true });
|
|
3669
|
-
const manifestPath = import_node_path.default.join(dir, "mount.json");
|
|
3670
|
-
import_node_fs.default.writeFileSync(manifestPath, `${JSON.stringify(bundle, null, 2)}
|
|
3671
|
-
`, "utf8");
|
|
3672
|
-
return manifestPath;
|
|
3673
|
-
}
|
|
3674
|
-
function applyRuntimeBundle(flavor, bundle, cwd, options = {}) {
|
|
3675
|
-
const dryRun = Boolean(options.dryRun);
|
|
3676
|
-
const safeId = sanitizeAgentIdForFilename(bundle.identity.agentId);
|
|
3677
|
-
const written = [];
|
|
3678
|
-
const manifestPath = dryRun ? import_node_path.default.join(cwd, MOUNT_MANIFEST_RELATIVE) : writeMountManifest(bundle, cwd);
|
|
3679
|
-
written.push(manifestPath);
|
|
3680
|
-
if (flavor === "cursor") {
|
|
3681
|
-
const rulesDir = import_node_path.default.join(cwd, ".cursor", "rules");
|
|
3682
|
-
const rulesPath = import_node_path.default.join(rulesDir, `neus-agent-${safeId}.mdc`);
|
|
3683
|
-
if (!dryRun) {
|
|
3684
|
-
import_node_fs.default.mkdirSync(rulesDir, { recursive: true });
|
|
3685
|
-
import_node_fs.default.writeFileSync(rulesPath, bundleToCursorRules(bundle), "utf8");
|
|
3686
|
-
}
|
|
3687
|
-
written.push(rulesPath);
|
|
3688
|
-
return { flavor, written, primary: rulesPath, manifestPath };
|
|
3689
|
-
}
|
|
3690
|
-
if (flavor === "claude") {
|
|
3691
|
-
const claudePath = import_node_path.default.join(cwd, ".claude", "NEUS_AGENT.md");
|
|
3692
|
-
if (!dryRun) {
|
|
3693
|
-
import_node_fs.default.mkdirSync(import_node_path.default.join(cwd, ".claude"), { recursive: true });
|
|
3694
|
-
import_node_fs.default.writeFileSync(claudePath, bundleToClaudeMd(bundle), "utf8");
|
|
3695
|
-
}
|
|
3696
|
-
written.push(claudePath);
|
|
3697
|
-
return { flavor, written, primary: claudePath, manifestPath };
|
|
3698
|
-
}
|
|
3699
|
-
if (flavor === "codex") {
|
|
3700
|
-
const codexPath = import_node_path.default.join(cwd, ".neus", `codex-agent-${safeId}.json`);
|
|
3701
|
-
if (!dryRun) {
|
|
3702
|
-
import_node_fs.default.mkdirSync(import_node_path.default.join(cwd, ".neus"), { recursive: true });
|
|
3703
|
-
import_node_fs.default.writeFileSync(codexPath, bundleToCodexJson(bundle), "utf8");
|
|
3704
|
-
}
|
|
3705
|
-
written.push(codexPath);
|
|
3706
|
-
return { flavor, written, primary: codexPath, manifestPath };
|
|
3707
|
-
}
|
|
3708
|
-
throw new Error(`Unsupported runtime adapter: ${flavor}`);
|
|
3709
|
-
}
|
|
3710
|
-
|
|
3711
3541
|
// index.js
|
|
3712
3542
|
init_errors();
|
|
3713
3543
|
|
|
@@ -3850,7 +3680,6 @@ var index_default = {
|
|
|
3850
3680
|
MCP_INSTALL_CLIENTS,
|
|
3851
3681
|
MCP_INSTALL_HOSTS,
|
|
3852
3682
|
MONTH,
|
|
3853
|
-
MOUNT_MANIFEST_RELATIVE,
|
|
3854
3683
|
NEUS_AUTH_CLI,
|
|
3855
3684
|
NEUS_AUTH_NPX,
|
|
3856
3685
|
NEUS_CHECK_CLI,
|
|
@@ -3881,7 +3710,6 @@ var index_default = {
|
|
|
3881
3710
|
VerificationError,
|
|
3882
3711
|
WEEK,
|
|
3883
3712
|
YEAR,
|
|
3884
|
-
applyRuntimeBundle,
|
|
3885
3713
|
buildAuthCommandForClient,
|
|
3886
3714
|
buildCursorMcpInstallUrl,
|
|
3887
3715
|
buildNeusMcpHttpConfig,
|
|
@@ -3891,9 +3719,6 @@ var index_default = {
|
|
|
3891
3719
|
buildSetupNpxOneLiner,
|
|
3892
3720
|
buildVerificationRequest,
|
|
3893
3721
|
buildVsCodeMcpInstallUrl,
|
|
3894
|
-
bundleToClaudeMd,
|
|
3895
|
-
bundleToCodexJson,
|
|
3896
|
-
bundleToCursorRules,
|
|
3897
3722
|
combineGates,
|
|
3898
3723
|
computeContentHash,
|
|
3899
3724
|
constructVerificationMessage,
|
|
@@ -3922,12 +3747,10 @@ var index_default = {
|
|
|
3922
3747
|
pickActiveDelegation,
|
|
3923
3748
|
pickIdentity,
|
|
3924
3749
|
profileAgentToIdentitySeed,
|
|
3925
|
-
readMountManifest,
|
|
3926
3750
|
resolveDID,
|
|
3927
3751
|
resolveEffectiveRuntime,
|
|
3928
3752
|
resolveRuntimeBundleFromMcp,
|
|
3929
3753
|
resolveZkPassportConfig,
|
|
3930
|
-
sanitizeAgentIdForFilename,
|
|
3931
3754
|
signMessage,
|
|
3932
3755
|
standardizeVerificationRequest,
|
|
3933
3756
|
supportsMcpInstallDeeplink,
|
|
@@ -3939,6 +3762,5 @@ var index_default = {
|
|
|
3939
3762
|
validateUniversalAddress,
|
|
3940
3763
|
validateVerifierPayload,
|
|
3941
3764
|
validateWalletAddress,
|
|
3942
|
-
withRetry
|
|
3943
|
-
writeMountManifest
|
|
3765
|
+
withRetry
|
|
3944
3766
|
});
|
package/cjs/runtime-mount.cjs
CHANGED
|
@@ -54,7 +54,7 @@ function capabilitiesToArray(caps) {
|
|
|
54
54
|
return Object.entries(caps).filter(([, enabled]) => enabled === true).map(([key]) => String(key).trim()).filter(Boolean);
|
|
55
55
|
}
|
|
56
56
|
function isDelegationExpired(expiresAt) {
|
|
57
|
-
if (expiresAt === null || expiresAt ===
|
|
57
|
+
if (expiresAt === null || expiresAt === 0) return false;
|
|
58
58
|
const ms = Number(expiresAt);
|
|
59
59
|
return Number.isFinite(ms) && ms > 0 && ms <= Date.now();
|
|
60
60
|
}
|
|
@@ -151,7 +151,7 @@ function extractAgentContextFromProofs(proofs) {
|
|
|
151
151
|
runtimePolicy: vvData.runtimePolicy && typeof vvData.runtimePolicy === "object" ? vvData.runtimePolicy : void 0,
|
|
152
152
|
expiresAt: vvData.expiresAt ?? null,
|
|
153
153
|
isExpired: isDelegationExpired(vvData.expiresAt),
|
|
154
|
-
maxSpend: vvData.maxSpend !== null
|
|
154
|
+
maxSpend: vvData.maxSpend !== null ? String(vvData.maxSpend) : void 0,
|
|
155
155
|
instructions: vvData.instructions || null,
|
|
156
156
|
skills: Array.isArray(vvData.skills) ? vvData.skills : [],
|
|
157
157
|
provider: vvData.provider || vvData.modelProvider || null,
|
package/cli-commands.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NEUS CLI command strings — SSOT for docs, MCP context, product UI, and skills.
|
|
3
|
+
*
|
|
4
|
+
* Pattern (industry default):
|
|
5
|
+
* - Install once: `npm i -g @neus/sdk`
|
|
6
|
+
* - Daily use: `neus <command>` (short)
|
|
7
|
+
* - Zero-install try: `npx @neus/sdk <command>` (no global install)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const NEUS_PKG = '@neus/sdk';
|
|
11
|
+
|
|
12
|
+
/** Recommended one-time install for builders using the CLI regularly. */
|
|
13
|
+
export const NEUS_INSTALL_CLI = `npm i -g ${NEUS_PKG}`;
|
|
14
|
+
|
|
15
|
+
/** Zero-install prefix — works without global install. */
|
|
16
|
+
export const NEUS_NPX = `npx ${NEUS_PKG}`;
|
|
17
|
+
|
|
18
|
+
/** Short commands (after `NEUS_INSTALL_CLI`). */
|
|
19
|
+
export const NEUS_SETUP_CLI = 'neus setup';
|
|
20
|
+
export const NEUS_AUTH_CLI = 'neus auth';
|
|
21
|
+
export const NEUS_CHECK_CLI = 'neus check';
|
|
22
|
+
export const NEUS_DOCTOR_CLI = 'neus doctor --live';
|
|
23
|
+
export const NEUS_EXAMPLES_CLI = 'neus examples';
|
|
24
|
+
|
|
25
|
+
/** One-shot copy-paste (no global install required). */
|
|
26
|
+
export const NEUS_SETUP_NPX = `${NEUS_NPX} setup`;
|
|
27
|
+
export const NEUS_AUTH_NPX = `${NEUS_NPX} auth`;
|
|
28
|
+
export const NEUS_CHECK_NPX = `${NEUS_NPX} check`;
|
|
29
|
+
export const NEUS_DOCTOR_NPX = `${NEUS_NPX} doctor --live`;
|
|
30
|
+
export const NEUS_EXAMPLES_NPX = `${NEUS_NPX} examples`;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {string} agentId
|
|
34
|
+
* @param {'cursor' | 'claude' | 'codex'} [host]
|
|
35
|
+
*/
|
|
36
|
+
export function neusMountApply(agentId, host = 'cursor') {
|
|
37
|
+
const id = String(agentId || '').trim();
|
|
38
|
+
return `neus mount ${id} --apply ${host}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {string} agentId
|
|
43
|
+
* @param {'cursor' | 'claude' | 'codex'} [host]
|
|
44
|
+
*/
|
|
45
|
+
export function neusMountApplyNpx(agentId, host = 'cursor') {
|
|
46
|
+
const id = String(agentId || '').trim();
|
|
47
|
+
return `${NEUS_NPX} mount ${id} --apply ${host}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Docs / landing quick start (installed path). */
|
|
51
|
+
export const NEUS_QUICKSTART_INSTALLED = `${NEUS_INSTALL_CLI}
|
|
52
|
+
${NEUS_SETUP_CLI}
|
|
53
|
+
${NEUS_AUTH_CLI}`;
|
|
54
|
+
|
|
55
|
+
/** Docs quick try (zero-install). */
|
|
56
|
+
export const NEUS_QUICKSTART_NPX = NEUS_SETUP_NPX;
|
|
57
|
+
|
|
58
|
+
/** Per-repo agent bind (after auth on the machine). */
|
|
59
|
+
export const NEUS_MOUNT_WORKFLOW = `${NEUS_AUTH_CLI}
|
|
60
|
+
neus mount <agentId> --apply cursor
|
|
61
|
+
${NEUS_DOCTOR_CLI}`;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} subcommand
|
|
65
|
+
*/
|
|
66
|
+
export function neusCmd(subcommand) {
|
|
67
|
+
return `neus ${String(subcommand || '').trim()}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} subcommand
|
|
72
|
+
*/
|
|
73
|
+
export function neusNpx(subcommand) {
|
|
74
|
+
return `${NEUS_NPX} ${String(subcommand || '').trim()}`;
|
|
75
|
+
}
|
package/index.js
CHANGED
|
@@ -72,16 +72,7 @@ export {
|
|
|
72
72
|
evaluateMountFileHealth
|
|
73
73
|
} from './runtime-mount.js';
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
MOUNT_MANIFEST_RELATIVE,
|
|
77
|
-
sanitizeAgentIdForFilename,
|
|
78
|
-
bundleToCursorRules,
|
|
79
|
-
bundleToClaudeMd,
|
|
80
|
-
bundleToCodexJson,
|
|
81
|
-
readMountManifest,
|
|
82
|
-
writeMountManifest,
|
|
83
|
-
applyRuntimeBundle
|
|
84
|
-
} from './runtime-adapters.js';
|
|
75
|
+
// Node-only adapters (fs/path): import `@neus/sdk/runtime-adapters` — not re-exported here (Next/webpack safe).
|
|
85
76
|
|
|
86
77
|
export {
|
|
87
78
|
SDKError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neus/sdk",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "NEUS makes trust portable across the internet — so people, apps, and AI agents can prove what is real before access, payout, or execution.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"neus": "cli/neus.mjs"
|
|
@@ -141,6 +141,7 @@
|
|
|
141
141
|
},
|
|
142
142
|
"files": [
|
|
143
143
|
"cli/neus.mjs",
|
|
144
|
+
"cli-commands.js",
|
|
144
145
|
"mcp-hosts.js",
|
|
145
146
|
"index.js",
|
|
146
147
|
"client.js",
|
|
@@ -148,6 +149,8 @@
|
|
|
148
149
|
"errors.js",
|
|
149
150
|
"gates.js",
|
|
150
151
|
"sponsor.js",
|
|
152
|
+
"runtime-mount.js",
|
|
153
|
+
"runtime-adapters.js",
|
|
151
154
|
"cjs/**",
|
|
152
155
|
"widgets.cjs",
|
|
153
156
|
"types.d.ts",
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Mount adapters — apply proof-backed bundles to host workspaces.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { RUNTIME_MOUNT_SCHEMA } from './runtime-mount.js';
|
|
8
|
+
|
|
9
|
+
export const MOUNT_MANIFEST_RELATIVE = path.join('.neus', 'mount.json');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} agentId
|
|
13
|
+
*/
|
|
14
|
+
export function sanitizeAgentIdForFilename(agentId) {
|
|
15
|
+
return String(agentId || 'agent')
|
|
16
|
+
.trim()
|
|
17
|
+
.toLowerCase()
|
|
18
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
19
|
+
.replace(/^-+|-+$/g, '')
|
|
20
|
+
.slice(0, 64) || 'agent';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {import('./runtime-mount.js').RuntimeBundle} bundle
|
|
25
|
+
*/
|
|
26
|
+
export function bundleToCursorRules(bundle) {
|
|
27
|
+
const id = bundle.identity.agentId;
|
|
28
|
+
const label = bundle.identity.agentLabel || id;
|
|
29
|
+
const skillsBlock = (bundle.identity.skills || [])
|
|
30
|
+
.map(skill => {
|
|
31
|
+
if (typeof skill === 'string') return `- ${skill}`;
|
|
32
|
+
const labelText = skill.label || skill.id || 'skill';
|
|
33
|
+
const kind = skill.kind || 'skill';
|
|
34
|
+
const provider = skill.provider ? ` / ${skill.provider}` : '';
|
|
35
|
+
return `- ${labelText} (${kind}${provider})`;
|
|
36
|
+
})
|
|
37
|
+
.join('\n');
|
|
38
|
+
|
|
39
|
+
const denied = (bundle.enforce.deniedActions || []).map(action => `- ${action}`).join('\n');
|
|
40
|
+
const capabilities = (bundle.identity.capabilities || []).map(cap => `- ${cap}`).join('\n');
|
|
41
|
+
const services = (bundle.identity.services || [])
|
|
42
|
+
.map(svc => `- ${svc.name}: ${svc.endpoint}${svc.version ? ` (v${svc.version})` : ''}`)
|
|
43
|
+
.join('\n');
|
|
44
|
+
|
|
45
|
+
return `---
|
|
46
|
+
description: NEUS proof-backed agent — ${label}
|
|
47
|
+
globs:
|
|
48
|
+
alwaysApply: true
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# NEUS Agent — ${label}
|
|
52
|
+
|
|
53
|
+
You are **${label}** (\`${id}\`). This project mounted trust context from NEUS.
|
|
54
|
+
|
|
55
|
+
## Identity
|
|
56
|
+
${bundle.identity.description || bundle.identity.instructions || 'Follow the agent instructions below.'}
|
|
57
|
+
|
|
58
|
+
## Instructions
|
|
59
|
+
${bundle.identity.instructions || 'Use NEUS MCP for trust checks before sensitive actions.'}
|
|
60
|
+
|
|
61
|
+
## Capabilities
|
|
62
|
+
${capabilities || '- General purpose'}
|
|
63
|
+
|
|
64
|
+
## Skills
|
|
65
|
+
${skillsBlock || '- None configured'}
|
|
66
|
+
|
|
67
|
+
## Services
|
|
68
|
+
${services || '- None configured'}
|
|
69
|
+
|
|
70
|
+
## Scoped policy
|
|
71
|
+
${denied ? `Denied actions (do not perform without new approval):\n${denied}` : '- Follow delegation on file via NEUS MCP.'}
|
|
72
|
+
|
|
73
|
+
## Trust workflow
|
|
74
|
+
1. Call \`neus_context\` once per session when NEUS MCP is available.
|
|
75
|
+
2. Trust before action: \`neus_proofs_check\` then \`neus_verify_or_guide\`.
|
|
76
|
+
3. Do not invent qHashes, wallets, or receipt fields.
|
|
77
|
+
4. Summarize NEUS outcomes as Trust Result — never dump raw tool JSON.
|
|
78
|
+
|
|
79
|
+
## Proof references
|
|
80
|
+
- Identity: ${bundle.trust.identityProofUrl}
|
|
81
|
+
${bundle.trust.delegationProofUrl ? `- Delegation: ${bundle.trust.delegationProofUrl}` : '- Delegation: not on file — call `neus_agent_link` before acting as this agent.'}
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {import('./runtime-mount.js').RuntimeBundle} bundle
|
|
87
|
+
*/
|
|
88
|
+
export function bundleToClaudeMd(bundle) {
|
|
89
|
+
const id = bundle.identity.agentId;
|
|
90
|
+
const label = bundle.identity.agentLabel || id;
|
|
91
|
+
return `# NEUS Agent — ${label}
|
|
92
|
+
|
|
93
|
+
Mounted from NEUS Runtime Mount (\`${RUNTIME_MOUNT_SCHEMA}\`).
|
|
94
|
+
|
|
95
|
+
## Identity
|
|
96
|
+
- **Agent ID:** ${id}
|
|
97
|
+
- **Label:** ${label}
|
|
98
|
+
|
|
99
|
+
## Description
|
|
100
|
+
${bundle.identity.description || 'Proof-backed agent on NEUS Network.'}
|
|
101
|
+
|
|
102
|
+
## Instructions
|
|
103
|
+
${bundle.identity.instructions || 'Use NEUS MCP before sensitive actions.'}
|
|
104
|
+
|
|
105
|
+
## Trust receipts
|
|
106
|
+
- Identity: \`${bundle.trust.identityQHash}\` — ${bundle.trust.identityProofUrl}
|
|
107
|
+
${bundle.trust.delegationQHash ? `- Delegation: \`${bundle.trust.delegationQHash}\` — ${bundle.trust.delegationProofUrl}` : ''}
|
|
108
|
+
|
|
109
|
+
## Policy
|
|
110
|
+
- Do not invent qHashes or verifier outcomes.
|
|
111
|
+
- Call \`neus_context\` once; use profile context when signed in.
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {import('./runtime-mount.js').RuntimeBundle} bundle
|
|
117
|
+
*/
|
|
118
|
+
export function bundleToCodexJson(bundle) {
|
|
119
|
+
return JSON.stringify(
|
|
120
|
+
{
|
|
121
|
+
schema: RUNTIME_MOUNT_SCHEMA,
|
|
122
|
+
name: bundle.identity.agentLabel,
|
|
123
|
+
agentId: bundle.identity.agentId,
|
|
124
|
+
agentWallet: bundle.identity.agentWallet,
|
|
125
|
+
description: bundle.identity.description,
|
|
126
|
+
instructions: bundle.identity.instructions,
|
|
127
|
+
capabilities: bundle.identity.capabilities,
|
|
128
|
+
skills: bundle.identity.skills,
|
|
129
|
+
services: bundle.identity.services,
|
|
130
|
+
effectiveRuntime: bundle.effectiveRuntime,
|
|
131
|
+
enforce: bundle.enforce,
|
|
132
|
+
trust: bundle.trust,
|
|
133
|
+
mountedAt: bundle.mountedAt
|
|
134
|
+
},
|
|
135
|
+
null,
|
|
136
|
+
2
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {string} cwd
|
|
142
|
+
*/
|
|
143
|
+
export function readMountManifest(cwd) {
|
|
144
|
+
const manifestPath = path.join(cwd, MOUNT_MANIFEST_RELATIVE);
|
|
145
|
+
if (!fs.existsSync(manifestPath)) return null;
|
|
146
|
+
try {
|
|
147
|
+
const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
148
|
+
return parsed?.schema === RUNTIME_MOUNT_SCHEMA ? parsed : null;
|
|
149
|
+
} catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {import('./runtime-mount.js').RuntimeBundle} bundle
|
|
156
|
+
* @param {string} cwd
|
|
157
|
+
*/
|
|
158
|
+
export function writeMountManifest(bundle, cwd) {
|
|
159
|
+
const dir = path.join(cwd, '.neus');
|
|
160
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
161
|
+
const manifestPath = path.join(dir, 'mount.json');
|
|
162
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(bundle, null, 2)}\n`, 'utf8');
|
|
163
|
+
return manifestPath;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {'cursor' | 'claude' | 'codex'} flavor
|
|
168
|
+
* @param {import('./runtime-mount.js').RuntimeBundle} bundle
|
|
169
|
+
* @param {string} cwd
|
|
170
|
+
* @param {{ dryRun?: boolean }} [options]
|
|
171
|
+
*/
|
|
172
|
+
export function applyRuntimeBundle(flavor, bundle, cwd, options = {}) {
|
|
173
|
+
const dryRun = Boolean(options.dryRun);
|
|
174
|
+
const safeId = sanitizeAgentIdForFilename(bundle.identity.agentId);
|
|
175
|
+
const written = [];
|
|
176
|
+
|
|
177
|
+
const manifestPath = dryRun
|
|
178
|
+
? path.join(cwd, MOUNT_MANIFEST_RELATIVE)
|
|
179
|
+
: writeMountManifest(bundle, cwd);
|
|
180
|
+
written.push(manifestPath);
|
|
181
|
+
|
|
182
|
+
if (flavor === 'cursor') {
|
|
183
|
+
const rulesDir = path.join(cwd, '.cursor', 'rules');
|
|
184
|
+
const rulesPath = path.join(rulesDir, `neus-agent-${safeId}.mdc`);
|
|
185
|
+
if (!dryRun) {
|
|
186
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
187
|
+
fs.writeFileSync(rulesPath, bundleToCursorRules(bundle), 'utf8');
|
|
188
|
+
}
|
|
189
|
+
written.push(rulesPath);
|
|
190
|
+
return { flavor, written, primary: rulesPath, manifestPath };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (flavor === 'claude') {
|
|
194
|
+
const claudePath = path.join(cwd, '.claude', 'NEUS_AGENT.md');
|
|
195
|
+
if (!dryRun) {
|
|
196
|
+
fs.mkdirSync(path.join(cwd, '.claude'), { recursive: true });
|
|
197
|
+
fs.writeFileSync(claudePath, bundleToClaudeMd(bundle), 'utf8');
|
|
198
|
+
}
|
|
199
|
+
written.push(claudePath);
|
|
200
|
+
return { flavor, written, primary: claudePath, manifestPath };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (flavor === 'codex') {
|
|
204
|
+
const codexPath = path.join(cwd, '.neus', `codex-agent-${safeId}.json`);
|
|
205
|
+
if (!dryRun) {
|
|
206
|
+
fs.mkdirSync(path.join(cwd, '.neus'), { recursive: true });
|
|
207
|
+
fs.writeFileSync(codexPath, bundleToCodexJson(bundle), 'utf8');
|
|
208
|
+
}
|
|
209
|
+
written.push(codexPath);
|
|
210
|
+
return { flavor, written, primary: codexPath, manifestPath };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
throw new Error(`Unsupported runtime adapter: ${flavor}`);
|
|
214
|
+
}
|
package/runtime-mount.js
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Mount — proof-backed agent context bundle (neus.runtime-mount.v1).
|
|
3
|
+
* SSOT for CLI, integrators, and protocol neus_agent_mount response shape.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const RUNTIME_MOUNT_SCHEMA = 'neus.runtime-mount.v1';
|
|
7
|
+
|
|
8
|
+
const PROOF_URL_BASE = 'https://neus.network/proof/';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string | null | undefined} value
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeWallet(value) {
|
|
14
|
+
const wallet = String(value || '').trim().toLowerCase();
|
|
15
|
+
return /^0x[a-f0-9]{40}$/.test(wallet) ? wallet : '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {unknown} value
|
|
20
|
+
*/
|
|
21
|
+
function asString(value) {
|
|
22
|
+
const trimmed = String(value ?? '').trim();
|
|
23
|
+
return trimmed.length > 0 ? trimmed : '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {unknown} value
|
|
28
|
+
* @returns {string[]}
|
|
29
|
+
*/
|
|
30
|
+
function asStringArray(value) {
|
|
31
|
+
if (!Array.isArray(value)) return [];
|
|
32
|
+
return value.map(item => String(item || '').trim()).filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {Record<string, unknown> | null | undefined} caps
|
|
37
|
+
*/
|
|
38
|
+
function capabilitiesToArray(caps) {
|
|
39
|
+
if (Array.isArray(caps)) return asStringArray(caps);
|
|
40
|
+
if (!caps || typeof caps !== 'object') return [];
|
|
41
|
+
return Object.entries(caps)
|
|
42
|
+
.filter(([, enabled]) => enabled === true)
|
|
43
|
+
.map(([key]) => String(key).trim())
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {number | null | undefined} expiresAt
|
|
49
|
+
*/
|
|
50
|
+
export function isDelegationExpired(expiresAt) {
|
|
51
|
+
if (expiresAt === null || expiresAt === 0) return false;
|
|
52
|
+
const ms = Number(expiresAt);
|
|
53
|
+
return Number.isFinite(ms) && ms > 0 && ms <= Date.now();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {Array<Record<string, unknown>>} identities
|
|
58
|
+
* @param {{ agentId?: string, agentWallet?: string, identityQHash?: string }} selector
|
|
59
|
+
*/
|
|
60
|
+
export function pickIdentity(identities, selector) {
|
|
61
|
+
const list = Array.isArray(identities) ? identities : [];
|
|
62
|
+
const qHash = asString(selector.identityQHash).toLowerCase();
|
|
63
|
+
if (qHash) {
|
|
64
|
+
return list.find(row => asString(row.qHash).toLowerCase() === qHash) || null;
|
|
65
|
+
}
|
|
66
|
+
const agentId = asString(selector.agentId).toLowerCase();
|
|
67
|
+
const agentWallet = normalizeWallet(selector.agentWallet);
|
|
68
|
+
if (agentId) {
|
|
69
|
+
const byId = list.filter(row => asString(row.agentId).toLowerCase() === agentId);
|
|
70
|
+
if (agentWallet) {
|
|
71
|
+
return byId.find(row => normalizeWallet(row.agentWallet) === agentWallet) || byId[0] || null;
|
|
72
|
+
}
|
|
73
|
+
return byId[0] || null;
|
|
74
|
+
}
|
|
75
|
+
if (agentWallet) {
|
|
76
|
+
return list.find(row => normalizeWallet(row.agentWallet) === agentWallet) || null;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {Array<Record<string, unknown>>} delegations
|
|
83
|
+
* @param {string} controllerWallet
|
|
84
|
+
* @param {string} agentWallet
|
|
85
|
+
* @param {string} agentId
|
|
86
|
+
*/
|
|
87
|
+
export function pickActiveDelegation(delegations, controllerWallet, agentWallet, agentId) {
|
|
88
|
+
const list = Array.isArray(delegations) ? delegations : [];
|
|
89
|
+
const controller = normalizeWallet(controllerWallet);
|
|
90
|
+
const agent = normalizeWallet(agentWallet);
|
|
91
|
+
const id = asString(agentId).toLowerCase();
|
|
92
|
+
const candidates = list.filter(row => {
|
|
93
|
+
if (isDelegationExpired(row.expiresAt)) return false;
|
|
94
|
+
const rowAgent = normalizeWallet(row.agentWallet);
|
|
95
|
+
const rowController = normalizeWallet(row.controllerWallet);
|
|
96
|
+
if (agent && rowAgent && rowAgent !== agent) return false;
|
|
97
|
+
if (controller && rowController && rowController !== controller) return false;
|
|
98
|
+
if (id && row.agentId && asString(row.agentId).toLowerCase() !== id) return false;
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
return candidates[0] || null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {Record<string, unknown> | null | undefined} identity
|
|
106
|
+
* @param {Record<string, unknown> | null | undefined} delegation
|
|
107
|
+
*/
|
|
108
|
+
export function resolveEffectiveRuntime(identity, delegation) {
|
|
109
|
+
const delProvider = asString(delegation?.provider);
|
|
110
|
+
const delModel = asString(delegation?.model);
|
|
111
|
+
if (delProvider || delModel) {
|
|
112
|
+
return {
|
|
113
|
+
provider: delProvider || 'openai',
|
|
114
|
+
model: delModel || ''
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const defaultRuntime =
|
|
118
|
+
identity?.defaultRuntime && typeof identity.defaultRuntime === 'object'
|
|
119
|
+
? identity.defaultRuntime
|
|
120
|
+
: null;
|
|
121
|
+
const idProvider = asString(defaultRuntime?.provider);
|
|
122
|
+
const idModel = asString(defaultRuntime?.model);
|
|
123
|
+
if (idProvider || idModel) {
|
|
124
|
+
return {
|
|
125
|
+
provider: idProvider || 'openai',
|
|
126
|
+
model: idModel || ''
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {unknown} proof
|
|
134
|
+
*/
|
|
135
|
+
export function extractAgentContextFromProofs(proofs) {
|
|
136
|
+
const identities = [];
|
|
137
|
+
const delegations = [];
|
|
138
|
+
const list = Array.isArray(proofs) ? proofs : [];
|
|
139
|
+
|
|
140
|
+
for (const proof of list) {
|
|
141
|
+
const qHash = asString(proof?.qHash);
|
|
142
|
+
const verifiedVerifiers = Array.isArray(proof?.verifiedVerifiers) ? proof.verifiedVerifiers : [];
|
|
143
|
+
for (const vv of verifiedVerifiers) {
|
|
144
|
+
const verifierId = asString(vv?.verifierId);
|
|
145
|
+
const vvData = vv?.data && typeof vv.data === 'object' ? vv.data : {};
|
|
146
|
+
if (verifierId === 'agent-identity') {
|
|
147
|
+
identities.push({
|
|
148
|
+
qHash,
|
|
149
|
+
agentId: vvData.agentId || null,
|
|
150
|
+
agentWallet: vvData.agentWallet || null,
|
|
151
|
+
agentLabel: vvData.agentLabel || vvData.agentId || 'Agent',
|
|
152
|
+
agentType: vvData.agentType || 'agent',
|
|
153
|
+
description: vvData.description || null,
|
|
154
|
+
capabilities: capabilitiesToArray(vvData.capabilities),
|
|
155
|
+
skills: Array.isArray(vvData.skills) ? vvData.skills : [],
|
|
156
|
+
instructions: vvData.instructions || null,
|
|
157
|
+
services: Array.isArray(vvData.services) ? vvData.services : [],
|
|
158
|
+
defaultRuntime:
|
|
159
|
+
vvData.defaultRuntime && typeof vvData.defaultRuntime === 'object'
|
|
160
|
+
? vvData.defaultRuntime
|
|
161
|
+
: undefined
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (verifierId === 'agent-delegation') {
|
|
165
|
+
delegations.push({
|
|
166
|
+
qHash,
|
|
167
|
+
controllerWallet: vvData.controllerWallet || null,
|
|
168
|
+
agentWallet: vvData.agentWallet || null,
|
|
169
|
+
agentId: vvData.agentId || null,
|
|
170
|
+
scope: vvData.scope || 'global',
|
|
171
|
+
allowedActions: asStringArray(vvData.allowedActions),
|
|
172
|
+
deniedActions: asStringArray(vvData.deniedActions),
|
|
173
|
+
runtimePolicy:
|
|
174
|
+
vvData.runtimePolicy && typeof vvData.runtimePolicy === 'object'
|
|
175
|
+
? vvData.runtimePolicy
|
|
176
|
+
: undefined,
|
|
177
|
+
expiresAt: vvData.expiresAt ?? null,
|
|
178
|
+
isExpired: isDelegationExpired(vvData.expiresAt),
|
|
179
|
+
maxSpend: vvData.maxSpend !== null ? String(vvData.maxSpend) : undefined,
|
|
180
|
+
instructions: vvData.instructions || null,
|
|
181
|
+
skills: Array.isArray(vvData.skills) ? vvData.skills : [],
|
|
182
|
+
provider: vvData.provider || vvData.modelProvider || null,
|
|
183
|
+
model: vvData.model || null
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return { identities, delegations };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {Record<string, unknown>} input
|
|
194
|
+
*/
|
|
195
|
+
export function buildRuntimeBundle(input) {
|
|
196
|
+
const identity = input.identity || {};
|
|
197
|
+
const delegation = input.delegation || null;
|
|
198
|
+
const identityQHash = asString(input.identityQHash || identity.qHash);
|
|
199
|
+
const delegationQHash = delegation ? asString(input.delegationQHash || delegation.qHash) : null;
|
|
200
|
+
const agentId = asString(identity.agentId);
|
|
201
|
+
const agentWallet = normalizeWallet(identity.agentWallet);
|
|
202
|
+
|
|
203
|
+
if (!identityQHash || !agentId || !agentWallet) {
|
|
204
|
+
throw new Error('Runtime mount requires verified agent identity (agentId, agentWallet, identityQHash).');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const effectiveRuntime = resolveEffectiveRuntime(identity, delegation);
|
|
208
|
+
const deniedActions = delegation ? asStringArray(delegation.deniedActions) : [];
|
|
209
|
+
const allowedActions = delegation ? asStringArray(delegation.allowedActions) : undefined;
|
|
210
|
+
const requiresHumanApproval =
|
|
211
|
+
delegation?.runtimePolicy &&
|
|
212
|
+
typeof delegation.runtimePolicy === 'object' &&
|
|
213
|
+
delegation.runtimePolicy.requiresHumanApproval === true;
|
|
214
|
+
|
|
215
|
+
const capabilities = asStringArray(identity.capabilities);
|
|
216
|
+
const skills = Array.isArray(identity.skills) ? identity.skills : [];
|
|
217
|
+
const skillIds = skills
|
|
218
|
+
.map(skill =>
|
|
219
|
+
typeof skill === 'string' ? skill : asString(skill?.id || skill?.label)
|
|
220
|
+
)
|
|
221
|
+
.filter(Boolean);
|
|
222
|
+
|
|
223
|
+
const delegations = delegation ? [delegation] : [];
|
|
224
|
+
const activeDelegations = delegations.filter(row => !row.isExpired).length;
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
schema: RUNTIME_MOUNT_SCHEMA,
|
|
228
|
+
mountedAt: new Date().toISOString(),
|
|
229
|
+
trust: {
|
|
230
|
+
identityQHash,
|
|
231
|
+
delegationQHash: delegationQHash || null,
|
|
232
|
+
identityProofUrl: `${PROOF_URL_BASE}${identityQHash}`,
|
|
233
|
+
delegationProofUrl: delegationQHash ? `${PROOF_URL_BASE}${delegationQHash}` : null
|
|
234
|
+
},
|
|
235
|
+
identity: {
|
|
236
|
+
agentId,
|
|
237
|
+
agentWallet,
|
|
238
|
+
agentLabel: asString(identity.agentLabel) || agentId,
|
|
239
|
+
agentType: asString(identity.agentType) || 'agent',
|
|
240
|
+
description: asString(identity.description) || undefined,
|
|
241
|
+
instructions: asString(identity.instructions) || undefined,
|
|
242
|
+
capabilities,
|
|
243
|
+
skills,
|
|
244
|
+
services: Array.isArray(identity.services) ? identity.services : undefined,
|
|
245
|
+
defaultRuntime:
|
|
246
|
+
identity.defaultRuntime && typeof identity.defaultRuntime === 'object'
|
|
247
|
+
? identity.defaultRuntime
|
|
248
|
+
: undefined
|
|
249
|
+
},
|
|
250
|
+
delegation: delegation
|
|
251
|
+
? {
|
|
252
|
+
controllerWallet: normalizeWallet(delegation.controllerWallet) || asString(delegation.controllerWallet),
|
|
253
|
+
scope: asString(delegation.scope) || undefined,
|
|
254
|
+
allowedActions,
|
|
255
|
+
deniedActions,
|
|
256
|
+
runtimePolicy: delegation.runtimePolicy,
|
|
257
|
+
expiresAt: delegation.expiresAt ?? null,
|
|
258
|
+
isExpired: Boolean(delegation.isExpired),
|
|
259
|
+
maxSpend: delegation.maxSpend,
|
|
260
|
+
instructions: asString(delegation.instructions) || undefined,
|
|
261
|
+
skills: Array.isArray(delegation.skills) ? delegation.skills : undefined,
|
|
262
|
+
provider: asString(delegation.provider) || undefined,
|
|
263
|
+
model: asString(delegation.model) || undefined
|
|
264
|
+
}
|
|
265
|
+
: null,
|
|
266
|
+
effectiveRuntime,
|
|
267
|
+
tools: Array.isArray(input.tools) ? input.tools : [],
|
|
268
|
+
secretBindings: Array.isArray(input.secretBindings) ? input.secretBindings : [],
|
|
269
|
+
memoryRefs: Array.isArray(input.memoryRefs) ? input.memoryRefs : undefined,
|
|
270
|
+
enforce: {
|
|
271
|
+
deniedActions,
|
|
272
|
+
...(allowedActions?.length ? { allowedActions } : {}),
|
|
273
|
+
...(requiresHumanApproval ? { requiresHumanApproval: true } : {})
|
|
274
|
+
},
|
|
275
|
+
contextPack: {
|
|
276
|
+
identityCount: 1,
|
|
277
|
+
delegationCount: delegations.length,
|
|
278
|
+
activeDelegations,
|
|
279
|
+
capabilitiesSummary: capabilities.slice(0, 32),
|
|
280
|
+
skillsSummary: skillIds.slice(0, 32)
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @param {Record<string, unknown>} profileAgent
|
|
287
|
+
*/
|
|
288
|
+
export function profileAgentToIdentitySeed(profileAgent) {
|
|
289
|
+
return {
|
|
290
|
+
agentId: profileAgent.agentId,
|
|
291
|
+
agentWallet: profileAgent.agentWallet,
|
|
292
|
+
agentLabel: profileAgent.agentLabel || profileAgent.name,
|
|
293
|
+
agentType: profileAgent.agentType || profileAgent.typeLabel,
|
|
294
|
+
description: profileAgent.description,
|
|
295
|
+
instructions: profileAgent.instructions,
|
|
296
|
+
capabilities: capabilitiesToArray(profileAgent.capabilities),
|
|
297
|
+
skills: Array.isArray(profileAgent.skills) ? profileAgent.skills : [],
|
|
298
|
+
services: Array.isArray(profileAgent.services) ? profileAgent.services : [],
|
|
299
|
+
identityQHash: profileAgent.identityQHash || profileAgent.qHash
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @param {import('./runtime-mount.js').RuntimeBundle | Record<string, unknown>} value
|
|
305
|
+
*/
|
|
306
|
+
export function isRuntimeBundle(value) {
|
|
307
|
+
return Boolean(value && typeof value === 'object' && value.schema === RUNTIME_MOUNT_SCHEMA);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Resolve mount via MCP (server tool first, then client assembly).
|
|
312
|
+
*
|
|
313
|
+
* @param {{
|
|
314
|
+
* callMcpTool: (args: { name: string, args?: Record<string, unknown>, accessKey?: string, sessionId?: string, signal?: AbortSignal }) => Promise<{ ok: boolean, payload?: unknown, error?: string }>,
|
|
315
|
+
* initializeMcp?: () => Promise<{ sessionId: string }>,
|
|
316
|
+
* accessKey: string,
|
|
317
|
+
* agentId?: string,
|
|
318
|
+
* agentWallet?: string,
|
|
319
|
+
* identityQHash?: string,
|
|
320
|
+
* signal?: AbortSignal
|
|
321
|
+
* }} input
|
|
322
|
+
*/
|
|
323
|
+
export async function resolveRuntimeBundleFromMcp(input) {
|
|
324
|
+
const accessKey = asString(input.accessKey);
|
|
325
|
+
if (!accessKey) {
|
|
326
|
+
throw new Error('NEUS access key or authenticated MCP session is required for runtime mount.');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const selector = {
|
|
330
|
+
agentId: input.agentId,
|
|
331
|
+
agentWallet: input.agentWallet,
|
|
332
|
+
identityQHash: input.identityQHash
|
|
333
|
+
};
|
|
334
|
+
if (!selector.agentId && !selector.agentWallet && !selector.identityQHash) {
|
|
335
|
+
throw new Error('Provide agentId, agentWallet, or identityQHash.');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
let sessionId = '';
|
|
339
|
+
if (input.initializeMcp) {
|
|
340
|
+
const init = await input.initializeMcp();
|
|
341
|
+
sessionId = init.sessionId || '';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const mountArgs = {
|
|
345
|
+
...(selector.agentId ? { agentId: selector.agentId } : {}),
|
|
346
|
+
...(selector.agentWallet ? { agentWallet: selector.agentWallet } : {}),
|
|
347
|
+
...(selector.identityQHash ? { identityQHash: selector.identityQHash } : {})
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const serverMount = await input.callMcpTool({
|
|
351
|
+
name: 'neus_agent_mount',
|
|
352
|
+
args: mountArgs,
|
|
353
|
+
accessKey,
|
|
354
|
+
sessionId,
|
|
355
|
+
signal: input.signal
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (serverMount.ok) {
|
|
359
|
+
const payload = serverMount.payload;
|
|
360
|
+
if (isRuntimeBundle(payload)) {
|
|
361
|
+
return /** @type {import('./runtime-mount.js').RuntimeBundle} */ (payload);
|
|
362
|
+
}
|
|
363
|
+
if (payload && typeof payload === 'object' && isRuntimeBundle(payload.data)) {
|
|
364
|
+
return /** @type {import('./runtime-mount.js').RuntimeBundle} */ (payload.data);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const me = await input.callMcpTool({
|
|
369
|
+
name: 'neus_me',
|
|
370
|
+
args: {},
|
|
371
|
+
accessKey,
|
|
372
|
+
sessionId,
|
|
373
|
+
signal: input.signal
|
|
374
|
+
});
|
|
375
|
+
if (!me.ok) {
|
|
376
|
+
throw new Error(me.error || 'Could not load profile context. Run `neus auth` and retry.');
|
|
377
|
+
}
|
|
378
|
+
const mePayload = /** @type {Record<string, unknown>} */ (me.payload || {});
|
|
379
|
+
if (mePayload.status === 'auth_required') {
|
|
380
|
+
throw new Error('Profile authentication required. Run `neus auth` or set NEUS_ACCESS_KEY.');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const principal = /** @type {Record<string, unknown>} */ (mePayload.principal || {});
|
|
384
|
+
const controllerWallet = normalizeWallet(principal.primaryAccount);
|
|
385
|
+
const profileAgents = Array.isArray(mePayload.agents) ? mePayload.agents : [];
|
|
386
|
+
|
|
387
|
+
let agentWallet = normalizeWallet(selector.agentWallet);
|
|
388
|
+
let agentId = asString(selector.agentId);
|
|
389
|
+
|
|
390
|
+
if (!agentWallet && agentId) {
|
|
391
|
+
const row = profileAgents.find(
|
|
392
|
+
row => asString(row.agentId).toLowerCase() === agentId.toLowerCase()
|
|
393
|
+
);
|
|
394
|
+
if (row) {
|
|
395
|
+
agentWallet = normalizeWallet(row.agentWallet);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (!agentId && agentWallet) {
|
|
399
|
+
const row = profileAgents.find(row => normalizeWallet(row.agentWallet) === agentWallet);
|
|
400
|
+
if (row) agentId = asString(row.agentId);
|
|
401
|
+
}
|
|
402
|
+
if (!agentWallet && selector.identityQHash) {
|
|
403
|
+
const idProof = await input.callMcpTool({
|
|
404
|
+
name: 'neus_proofs_get',
|
|
405
|
+
args: { qHash: selector.identityQHash, verifierId: 'agent-identity' },
|
|
406
|
+
accessKey,
|
|
407
|
+
sessionId,
|
|
408
|
+
signal: input.signal
|
|
409
|
+
});
|
|
410
|
+
if (idProof.ok) {
|
|
411
|
+
const data = /** @type {Record<string, unknown>} */ (idProof.payload?.data || idProof.payload || {});
|
|
412
|
+
const proofs = Array.isArray(data.proofs) ? data.proofs : [];
|
|
413
|
+
const extracted = extractAgentContextFromProofs(proofs);
|
|
414
|
+
const identity = pickIdentity(extracted.identities, selector);
|
|
415
|
+
if (identity) {
|
|
416
|
+
agentWallet = normalizeWallet(identity.agentWallet);
|
|
417
|
+
agentId = asString(identity.agentId);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (!agentWallet) {
|
|
423
|
+
throw new Error('Could not resolve agent wallet. Check agentId or link the agent on your profile.');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const [identityPage, delegationPage] = await Promise.all([
|
|
427
|
+
input.callMcpTool({
|
|
428
|
+
name: 'neus_proofs_get',
|
|
429
|
+
args: { identifier: agentWallet, verifierId: 'agent-identity', limit: 25 },
|
|
430
|
+
accessKey,
|
|
431
|
+
sessionId,
|
|
432
|
+
signal: input.signal
|
|
433
|
+
}),
|
|
434
|
+
controllerWallet
|
|
435
|
+
? input.callMcpTool({
|
|
436
|
+
name: 'neus_proofs_get',
|
|
437
|
+
args: { identifier: controllerWallet, verifierId: 'agent-delegation', limit: 50 },
|
|
438
|
+
accessKey,
|
|
439
|
+
sessionId,
|
|
440
|
+
signal: input.signal
|
|
441
|
+
})
|
|
442
|
+
: Promise.resolve({ ok: false })
|
|
443
|
+
]);
|
|
444
|
+
|
|
445
|
+
const identityProofs = identityPage.ok
|
|
446
|
+
? /** @type {unknown[]} */ (
|
|
447
|
+
identityPage.payload?.data?.proofs || identityPage.payload?.proofs || []
|
|
448
|
+
)
|
|
449
|
+
: [];
|
|
450
|
+
const delegationProofs = delegationPage.ok
|
|
451
|
+
? /** @type {unknown[]} */ (
|
|
452
|
+
delegationPage.payload?.data?.proofs || delegationPage.payload?.proofs || []
|
|
453
|
+
)
|
|
454
|
+
: [];
|
|
455
|
+
|
|
456
|
+
const idCtx = extractAgentContextFromProofs(identityProofs);
|
|
457
|
+
const delCtx = extractAgentContextFromProofs(delegationProofs);
|
|
458
|
+
|
|
459
|
+
let identity = pickIdentity(idCtx.identities, { ...selector, agentId, agentWallet });
|
|
460
|
+
if (!identity && profileAgents.length > 0) {
|
|
461
|
+
const row = profileAgents.find(
|
|
462
|
+
a =>
|
|
463
|
+
asString(a.agentId).toLowerCase() === agentId.toLowerCase() ||
|
|
464
|
+
normalizeWallet(a.agentWallet) === agentWallet
|
|
465
|
+
);
|
|
466
|
+
if (row) {
|
|
467
|
+
identity = { ...profileAgentToIdentitySeed(row), agentWallet, agentId: agentId || asString(row.agentId) };
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (!identity) {
|
|
471
|
+
throw new Error('Agent identity proof not found. Complete agent setup on neus.network first.');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const delegation = pickActiveDelegation(
|
|
475
|
+
delCtx.delegations,
|
|
476
|
+
controllerWallet,
|
|
477
|
+
agentWallet,
|
|
478
|
+
agentId || asString(identity.agentId)
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
return buildRuntimeBundle({
|
|
482
|
+
identity,
|
|
483
|
+
delegation,
|
|
484
|
+
identityQHash: asString(identity.qHash || selector.identityQHash),
|
|
485
|
+
delegationQHash: delegation ? asString(delegation.qHash) : null,
|
|
486
|
+
tools: [],
|
|
487
|
+
secretBindings: []
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @param {import('./runtime-mount.js').RuntimeBundle | Record<string, unknown> | null | undefined} manifest
|
|
493
|
+
*/
|
|
494
|
+
export function evaluateMountFileHealth(manifest) {
|
|
495
|
+
if (!manifest || manifest.schema !== RUNTIME_MOUNT_SCHEMA) {
|
|
496
|
+
return {
|
|
497
|
+
mountFileValid: false,
|
|
498
|
+
missingDelegation: true,
|
|
499
|
+
delegationExpired: false,
|
|
500
|
+
needsRefresh: true,
|
|
501
|
+
reason: 'missing_or_invalid'
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const delegationQHash = asString(manifest.trust?.delegationQHash);
|
|
506
|
+
const missingDelegation = !delegationQHash;
|
|
507
|
+
const expiresAt = manifest.delegation?.expiresAt;
|
|
508
|
+
const delegationExpired =
|
|
509
|
+
Boolean(manifest.delegation?.isExpired) || isDelegationExpired(expiresAt);
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
mountFileValid: true,
|
|
513
|
+
missingDelegation,
|
|
514
|
+
delegationExpired,
|
|
515
|
+
needsRefresh: missingDelegation || delegationExpired,
|
|
516
|
+
reason: delegationExpired
|
|
517
|
+
? 'delegation_expired'
|
|
518
|
+
: missingDelegation
|
|
519
|
+
? 'delegation_missing'
|
|
520
|
+
: null
|
|
521
|
+
};
|
|
522
|
+
}
|