@neus/sdk 1.2.0 → 1.2.2

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.
@@ -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
- export {
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/mcp-hosts.js CHANGED
@@ -57,11 +57,36 @@ export const IDE_HOST_BRAND_LOGOS = {
57
57
  };
58
58
 
59
59
  /**
60
+ * Build the MCP HTTP server config for an IDE/client.
61
+ *
62
+ * Two paths, one session model — same NEUS Profile/Account either way:
63
+ *
64
+ * - `npk_…` Profile access keys are durable (never expire). Written as a static
65
+ * `Authorization: Bearer npk_…` header. Used for operator IDEs, servers, CI,
66
+ * and automation where browser OAuth is unavailable.
67
+ * - OAuth (default for Cursor, VS Code, Claude Code, Codex): we return a URL-only
68
+ * config (no `headers`). The IDE MCP client discovers OAuth metadata from the
69
+ * server's `401 + WWW-Authenticate` challenge, then runs its own DCR + PKCE +
70
+ * silent-refresh lifecycle (matching Linear, GitHub, Notion). The access token
71
+ * is a short-lived JWT refreshed silently by the host for up to 30 days via the
72
+ * `offline_access` refresh token — the session is long-lived, the access token
73
+ * is not
74
+ *
75
+ * A raw OAuth access token (JWT) is never written as a static Bearer header: IDE
76
+ * MCP clients cannot refresh a static header, and writing one would create a
77
+ * session that dies when the access token expires. URL-only config is the correct
78
+ * OAuth path and is what `neus setup`/`neus auth` produce for browser-OAuth clients.
79
+ *
60
80
  * @param {string | null | undefined} accessKey
61
81
  * @returns {{ type: 'http'; url: string; headers?: { Authorization: string } }}
62
82
  */
63
83
  export function buildNeusMcpHttpConfig(accessKey) {
64
84
  const key = String(accessKey || '').trim();
85
+ // OAuth access tokens are JWTs (three dot-separated base64url segments). Never write
86
+ // them as a static Bearer header — return URL-only so the IDE runs OAuth itself.
87
+ if (key && !key.startsWith('npk_') && key.split('.').length === 3) {
88
+ return { type: 'http', url: NEUS_MCP_URL };
89
+ }
65
90
  return {
66
91
  type: 'http',
67
92
  url: NEUS_MCP_URL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neus/sdk",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
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
+ }