@neus/sdk 1.1.7 → 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/README.md +17 -4
- package/cjs/cli-commands.cjs +101 -0
- package/cjs/index.cjs +612 -0
- 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/cli-commands.js +75 -0
- package/index.js +60 -2
- package/mcp-hosts.js +54 -12
- package/package.json +17 -2
- package/runtime-adapters.js +214 -0
- package/runtime-mount.js +522 -0
- package/types.d.ts +89 -0
package/mcp-hosts.js
CHANGED
|
@@ -3,11 +3,40 @@
|
|
|
3
3
|
* Browser-safe: no Node-only APIs except the Buffer fallback for non-browser tests.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import {
|
|
7
|
+
NEUS_AUTH_CLI,
|
|
8
|
+
NEUS_INSTALL_CLI,
|
|
9
|
+
NEUS_NPX,
|
|
10
|
+
NEUS_SETUP_NPX,
|
|
11
|
+
neusCmd,
|
|
12
|
+
} from './cli-commands.js';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
NEUS_PKG,
|
|
16
|
+
NEUS_INSTALL_CLI,
|
|
17
|
+
NEUS_NPX,
|
|
18
|
+
NEUS_SETUP_CLI,
|
|
19
|
+
NEUS_SETUP_NPX,
|
|
20
|
+
NEUS_AUTH_CLI,
|
|
21
|
+
NEUS_CHECK_CLI,
|
|
22
|
+
NEUS_DOCTOR_CLI,
|
|
23
|
+
NEUS_EXAMPLES_CLI,
|
|
24
|
+
NEUS_CHECK_NPX,
|
|
25
|
+
NEUS_AUTH_NPX,
|
|
26
|
+
NEUS_DOCTOR_NPX,
|
|
27
|
+
NEUS_EXAMPLES_NPX,
|
|
28
|
+
NEUS_QUICKSTART_INSTALLED,
|
|
29
|
+
NEUS_QUICKSTART_NPX,
|
|
30
|
+
NEUS_MOUNT_WORKFLOW,
|
|
31
|
+
neusMountApply,
|
|
32
|
+
neusMountApplyNpx,
|
|
33
|
+
neusCmd,
|
|
34
|
+
neusNpx,
|
|
35
|
+
} from './cli-commands.js';
|
|
36
|
+
|
|
6
37
|
export const NEUS_MCP_SERVER_NAME = 'neus';
|
|
7
38
|
export const NEUS_MCP_URL = 'https://mcp.neus.network/mcp';
|
|
8
|
-
export const
|
|
9
|
-
export const NEUS_AUTH_CLI = 'npx -y -p @neus/sdk neus auth';
|
|
10
|
-
export const NEUS_MCP_SETUP_DOCS_URL = 'https://docs.neus.network/mcp/ide-plugin';
|
|
39
|
+
export const NEUS_MCP_SETUP_DOCS_URL = 'https://docs.neus.network/mcp/setup';
|
|
11
40
|
|
|
12
41
|
/** CLI `neus setup --client` values. */
|
|
13
42
|
export const MCP_INSTALL_CLIENTS = ['claude', 'codex', 'cursor', 'vscode'];
|
|
@@ -18,13 +47,13 @@ export const MCP_INSTALL_HOSTS = ['cursor', 'claude', 'codex'];
|
|
|
18
47
|
export const IDE_HOST_LABELS = {
|
|
19
48
|
cursor: 'Cursor',
|
|
20
49
|
claude: 'Claude Code',
|
|
21
|
-
codex: 'Codex'
|
|
50
|
+
codex: 'Codex',
|
|
22
51
|
};
|
|
23
52
|
|
|
24
53
|
export const IDE_HOST_BRAND_LOGOS = {
|
|
25
54
|
cursor: '/images/brandLogos/cursor.svg',
|
|
26
55
|
claude: '/images/brandLogos/anthropic.svg',
|
|
27
|
-
codex: '/images/brandLogos/openai.svg'
|
|
56
|
+
codex: '/images/brandLogos/openai.svg',
|
|
28
57
|
};
|
|
29
58
|
|
|
30
59
|
/**
|
|
@@ -36,7 +65,7 @@ export function buildNeusMcpHttpConfig(accessKey) {
|
|
|
36
65
|
return {
|
|
37
66
|
type: 'http',
|
|
38
67
|
url: NEUS_MCP_URL,
|
|
39
|
-
...(key ? { headers: { Authorization: `Bearer ${key}` } } : {})
|
|
68
|
+
...(key ? { headers: { Authorization: `Bearer ${key}` } } : {}),
|
|
40
69
|
};
|
|
41
70
|
}
|
|
42
71
|
|
|
@@ -69,7 +98,7 @@ export function buildCursorMcpInstallUrl(accessKey) {
|
|
|
69
98
|
export function buildVsCodeMcpInstallUrl(accessKey) {
|
|
70
99
|
const payload = {
|
|
71
100
|
name: NEUS_MCP_SERVER_NAME,
|
|
72
|
-
...buildNeusMcpHttpConfig(accessKey)
|
|
101
|
+
...buildNeusMcpHttpConfig(accessKey),
|
|
73
102
|
};
|
|
74
103
|
return `vscode:mcp/install?${encodeURIComponent(JSON.stringify(payload))}`;
|
|
75
104
|
}
|
|
@@ -80,12 +109,13 @@ export function buildVsCodeMcpInstallUrl(accessKey) {
|
|
|
80
109
|
*/
|
|
81
110
|
export function buildAuthCommandForClient(client) {
|
|
82
111
|
if (client === 'codex') {
|
|
83
|
-
return
|
|
112
|
+
return neusCmd(`auth --client codex`);
|
|
84
113
|
}
|
|
85
114
|
return NEUS_AUTH_CLI;
|
|
86
115
|
}
|
|
87
116
|
|
|
88
117
|
/**
|
|
118
|
+
* Copy-paste block for Profile / IDE onboarding (install + setup + auth).
|
|
89
119
|
* @param {'claude' | 'codex' | 'cursor' | 'vscode'} client
|
|
90
120
|
* @param {string | null | undefined} accessKey
|
|
91
121
|
* @returns {string}
|
|
@@ -93,10 +123,13 @@ export function buildAuthCommandForClient(client) {
|
|
|
93
123
|
export function buildSetupCommandForClient(client, accessKey) {
|
|
94
124
|
const key = String(accessKey || '').trim();
|
|
95
125
|
const setup = key
|
|
96
|
-
?
|
|
97
|
-
:
|
|
98
|
-
if (key)
|
|
99
|
-
|
|
126
|
+
? neusCmd(`setup --client ${client} --access-key ${key}`)
|
|
127
|
+
: neusCmd(`setup --client ${client}`);
|
|
128
|
+
if (key) {
|
|
129
|
+
return `${NEUS_INSTALL_CLI}\n${setup}`;
|
|
130
|
+
}
|
|
131
|
+
const auth = buildAuthCommandForClient(client);
|
|
132
|
+
return `${NEUS_INSTALL_CLI}\n${setup}\n${auth}`;
|
|
100
133
|
}
|
|
101
134
|
|
|
102
135
|
/**
|
|
@@ -108,6 +141,15 @@ export function buildSetupCommandForHost(host, accessKey) {
|
|
|
108
141
|
return buildSetupCommandForClient(host, accessKey);
|
|
109
142
|
}
|
|
110
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Zero-install one-liner for landing pages and copy buttons.
|
|
146
|
+
* @param {'claude' | 'codex' | 'cursor' | 'vscode'} [client]
|
|
147
|
+
*/
|
|
148
|
+
export function buildSetupNpxOneLiner(client) {
|
|
149
|
+
if (!client) return NEUS_SETUP_NPX;
|
|
150
|
+
return `${NEUS_NPX} setup --client ${client}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
111
153
|
/**
|
|
112
154
|
* Cursor supports MCP install deeplinks; Codex uses CLI, not VS Code deeplinks.
|
|
113
155
|
* @param {'cursor' | 'claude' | 'codex'} host
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neus/sdk",
|
|
3
|
-
"version": "1.1
|
|
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"
|
|
@@ -31,10 +31,22 @@
|
|
|
31
31
|
"import": "./gates.js",
|
|
32
32
|
"require": "./cjs/gates.cjs"
|
|
33
33
|
},
|
|
34
|
+
"./cli-commands": {
|
|
35
|
+
"import": "./cli-commands.js",
|
|
36
|
+
"require": "./cjs/cli-commands.cjs"
|
|
37
|
+
},
|
|
34
38
|
"./mcp-hosts": {
|
|
35
39
|
"import": "./mcp-hosts.js",
|
|
36
40
|
"require": "./cjs/mcp-hosts.cjs"
|
|
37
41
|
},
|
|
42
|
+
"./runtime-mount": {
|
|
43
|
+
"import": "./runtime-mount.js",
|
|
44
|
+
"require": "./cjs/runtime-mount.cjs"
|
|
45
|
+
},
|
|
46
|
+
"./runtime-adapters": {
|
|
47
|
+
"import": "./runtime-adapters.js",
|
|
48
|
+
"require": "./cjs/runtime-adapters.cjs"
|
|
49
|
+
},
|
|
38
50
|
"./widgets": {
|
|
39
51
|
"types": "./types.d.ts",
|
|
40
52
|
"import": "./widgets/index.js",
|
|
@@ -54,7 +66,7 @@
|
|
|
54
66
|
"format": "prettier --write \"**/*.js\"",
|
|
55
67
|
"build": "npm run build:widgets && npm run build:cjs",
|
|
56
68
|
"build:widgets": "npx esbuild widgets/verify-gate/VerifyGate.jsx widgets/verify-gate/ProofBadge.jsx --bundle --platform=browser --format=esm --outdir=widgets/verify-gate/dist --jsx=automatic --legal-comments=none --external:react --external:react-dom --external:react/jsx-runtime --external:@neus/sdk/client",
|
|
57
|
-
"build:cjs": "npx esbuild index.js client.js utils.js errors.js gates.js mcp-hosts.js --bundle --platform=node --format=cjs --outdir=cjs --out-extension:.js=.cjs --legal-comments=none --external:ethers --external:@zkpassport/sdk --external:react --external:react-dom --external:react/jsx-runtime",
|
|
69
|
+
"build:cjs": "npx esbuild index.js client.js utils.js errors.js gates.js cli-commands.js mcp-hosts.js runtime-mount.js runtime-adapters.js --bundle --platform=node --format=cjs --outdir=cjs --out-extension:.js=.cjs --legal-comments=none --external:ethers --external:@zkpassport/sdk --external:react --external:react-dom --external:react/jsx-runtime",
|
|
58
70
|
"prepack": "npm run build",
|
|
59
71
|
"prepublishOnly": "npm run lint && npm test && npm run build"
|
|
60
72
|
},
|
|
@@ -129,6 +141,7 @@
|
|
|
129
141
|
},
|
|
130
142
|
"files": [
|
|
131
143
|
"cli/neus.mjs",
|
|
144
|
+
"cli-commands.js",
|
|
132
145
|
"mcp-hosts.js",
|
|
133
146
|
"index.js",
|
|
134
147
|
"client.js",
|
|
@@ -136,6 +149,8 @@
|
|
|
136
149
|
"errors.js",
|
|
137
150
|
"gates.js",
|
|
138
151
|
"sponsor.js",
|
|
152
|
+
"runtime-mount.js",
|
|
153
|
+
"runtime-adapters.js",
|
|
139
154
|
"cjs/**",
|
|
140
155
|
"widgets.cjs",
|
|
141
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
|
+
}
|