@agentproto/runtime 5.1.0 → 5.3.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 +18 -0
- package/dist/bundles.d.ts +67 -0
- package/dist/bundles.mjs +173 -0
- package/dist/bundles.mjs.map +1 -0
- package/dist/catalog-models.d.ts +2 -3
- package/dist/config-DcESQDmZ.d.ts +748 -0
- package/dist/config-schema.d.ts +9 -3
- package/dist/config-schema.mjs +198 -47
- package/dist/config-schema.mjs.map +1 -1
- package/dist/config.d.ts +2 -589
- package/dist/config.mjs +205 -49
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.ts +3774 -9286
- package/dist/index.mjs +12070 -4494
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-imports.d.ts +73 -1
- package/dist/mcp-imports.mjs +146 -4
- package/dist/mcp-imports.mjs.map +1 -1
- package/dist/resume-strategies.mjs.map +1 -1
- package/dist/{session-config-ByfVeEQJ.d.ts → session-config-DxIIuJFY.d.ts} +1 -1
- package/dist/session-presence.mjs +196 -46
- package/dist/session-presence.mjs.map +1 -1
- package/dist/session-story.mjs +1 -0
- package/dist/session-story.mjs.map +1 -1
- package/dist/sessions-DKm6Wqro.d.ts +6866 -0
- package/dist/{spawn-defaults-wQV9AeiG.d.ts → spawn-defaults-Cp9SGZex.d.ts} +103 -2
- package/dist/user-presets.d.ts +8 -99
- package/dist/user-presets.mjs +35 -4
- package/dist/user-presets.mjs.map +1 -1
- package/package.json +49 -34
- package/dist/context-continuity-Bn99UtVC.d.ts +0 -104
package/dist/mcp-imports.d.ts
CHANGED
|
@@ -91,12 +91,63 @@ interface DiscoveredMcp {
|
|
|
91
91
|
|
|
92
92
|
declare const IMPORTED_MCPS_PATH: () => string;
|
|
93
93
|
declare const IMPORTED_MCPS_VERSION: 1;
|
|
94
|
+
/** Where an import came from. `plugin` (written by plugin-local-browser) and
|
|
95
|
+
* `manual` have no discovery source; `kind` otherwise mirrors
|
|
96
|
+
* `DiscoveredMcp["source"]`. */
|
|
97
|
+
interface ImportedMcpOrigin {
|
|
98
|
+
kind: DiscoveredMcp["source"] | "plugin" | "manual";
|
|
99
|
+
scope: string;
|
|
100
|
+
name: string;
|
|
101
|
+
}
|
|
102
|
+
/** `live` = re-read the source harness config at connect time (the snapshot is
|
|
103
|
+
* only a fallback); `snapshot` = the stored copy is authoritative. */
|
|
104
|
+
type ImportedMcpResolve = "live" | "snapshot";
|
|
105
|
+
/** Opaque secret refs (resolved via `resolveMcpSecret`), keyed by the header
|
|
106
|
+
* / env key they fill. Values are refs, NEVER secret material. */
|
|
107
|
+
interface ImportedMcpSecretRefs {
|
|
108
|
+
headers?: Record<string, string>;
|
|
109
|
+
env?: Record<string, string>;
|
|
110
|
+
}
|
|
94
111
|
interface ImportedMcpEntry {
|
|
95
112
|
id: string;
|
|
96
113
|
alias: string;
|
|
97
114
|
addedAt: string;
|
|
98
115
|
snapshot: DiscoveredMcp;
|
|
116
|
+
/** Additive (no file-version bump): link back to the source config. */
|
|
117
|
+
origin?: ImportedMcpOrigin;
|
|
118
|
+
resolve?: ImportedMcpResolve;
|
|
119
|
+
secretRefs?: ImportedMcpSecretRefs;
|
|
120
|
+
}
|
|
121
|
+
/** Value written into `snapshot.headers[K]` / `snapshot.env[K]` in place of a
|
|
122
|
+
* literal secret that now lives behind `secretRefs`. */
|
|
123
|
+
declare const SECRET_REF_MARKER = "<secretRef>";
|
|
124
|
+
/** `live` only when discovery can actually re-find the entry: a discovery
|
|
125
|
+
* source AND a scope discovery scans (`global` / `project:*` / `workspace:*`).
|
|
126
|
+
* claude-code `local`-scope and plugin/manual entries stay `snapshot`. */
|
|
127
|
+
declare function deriveResolve(snapshot: DiscoveredMcp): ImportedMcpResolve;
|
|
128
|
+
/** Keychain path + account for one secret slot of an import. Ref format
|
|
129
|
+
* `agentproto/mcp-import/<importId>#<header|env>:<KEY>` (split on LAST `#`). */
|
|
130
|
+
declare function mcpSecretRef(importId: string, kind: "header" | "env", key: string): string;
|
|
131
|
+
/** A value worth moving to the keychain: non-empty, not already the marker,
|
|
132
|
+
* and not a `${VAR}` placeholder (those expand at connect, no secret at rest). */
|
|
133
|
+
declare function isLiteralSecretValue(v: unknown): v is string;
|
|
134
|
+
interface MoveSecretsResult {
|
|
135
|
+
entry: ImportedMcpEntry;
|
|
136
|
+
/** Header/env KEY names moved this call (never values). */
|
|
137
|
+
moved: string[];
|
|
138
|
+
/** Human warnings (key names only). */
|
|
139
|
+
warnings: string[];
|
|
99
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Move every literal header/env value of `entry.snapshot` behind a keychain
|
|
143
|
+
* ref. Keychain write happens FIRST per key; only on success is the snapshot
|
|
144
|
+
* value replaced by the marker (a failed write keeps the literal + warns).
|
|
145
|
+
* Read-back verification when `resolve` is supplied.
|
|
146
|
+
*/
|
|
147
|
+
declare function moveEntrySecrets(entry: ImportedMcpEntry, hooks: {
|
|
148
|
+
storeMcpSecret?: (ref: string, value: string) => Promise<void>;
|
|
149
|
+
resolveMcpSecret?: (ref: string) => Promise<string | undefined>;
|
|
150
|
+
}): Promise<MoveSecretsResult>;
|
|
100
151
|
interface ImportedMcpsConfig {
|
|
101
152
|
version: typeof IMPORTED_MCPS_VERSION;
|
|
102
153
|
imports: ImportedMcpEntry[];
|
|
@@ -112,7 +163,28 @@ declare function addImport(config: ImportedMcpsConfig, input: {
|
|
|
112
163
|
snapshot: DiscoveredMcp;
|
|
113
164
|
alias?: string;
|
|
114
165
|
}): ImportedMcpsConfig;
|
|
166
|
+
/**
|
|
167
|
+
* `addImport` + move literal header/env values behind keychain refs (via the
|
|
168
|
+
* `storeMcpSecret` hook). Hook absent → literals kept and a warning returned.
|
|
169
|
+
* Callers persist `config` and surface `warnings` in their reply.
|
|
170
|
+
*/
|
|
171
|
+
declare function addImportWithSecrets(config: ImportedMcpsConfig, input: {
|
|
172
|
+
snapshot: DiscoveredMcp;
|
|
173
|
+
alias?: string;
|
|
174
|
+
}, hooks: {
|
|
175
|
+
storeMcpSecret?: (ref: string, value: string) => Promise<void>;
|
|
176
|
+
resolveMcpSecret?: (ref: string) => Promise<string | undefined>;
|
|
177
|
+
}): Promise<{
|
|
178
|
+
config: ImportedMcpsConfig;
|
|
179
|
+
entry: ImportedMcpEntry;
|
|
180
|
+
warnings: string[];
|
|
181
|
+
}>;
|
|
115
182
|
declare function removeImport(config: ImportedMcpsConfig, id: string): ImportedMcpsConfig;
|
|
116
183
|
declare function findImport(config: ImportedMcpsConfig, id: string): ImportedMcpEntry | undefined;
|
|
184
|
+
/** KEY names only (never refs' targets or values) — for status surfaces. */
|
|
185
|
+
declare function secretRefKeys(entry: Pick<ImportedMcpEntry, "secretRefs">): {
|
|
186
|
+
headers?: string[];
|
|
187
|
+
env?: string[];
|
|
188
|
+
} | undefined;
|
|
117
189
|
|
|
118
|
-
export { IMPORTED_MCPS_PATH, IMPORTED_MCPS_VERSION, type ImportedMcpEntry, type ImportedMcpsConfig, addImport, findImport, loadImportedMcps, removeImport, saveImportedMcps };
|
|
190
|
+
export { IMPORTED_MCPS_PATH, IMPORTED_MCPS_VERSION, type ImportedMcpEntry, type ImportedMcpOrigin, type ImportedMcpResolve, type ImportedMcpSecretRefs, type ImportedMcpsConfig, type MoveSecretsResult, SECRET_REF_MARKER, addImport, addImportWithSecrets, deriveResolve, findImport, isLiteralSecretValue, loadImportedMcps, mcpSecretRef, moveEntrySecrets, removeImport, saveImportedMcps, secretRefKeys };
|
package/dist/mcp-imports.mjs
CHANGED
|
@@ -9,6 +9,86 @@ import { resolve, dirname } from 'path';
|
|
|
9
9
|
|
|
10
10
|
var IMPORTED_MCPS_PATH = () => resolve(homedir(), ".agentproto", "imported-mcps.json");
|
|
11
11
|
var IMPORTED_MCPS_VERSION = 1;
|
|
12
|
+
var SECRET_REF_MARKER = "<secretRef>";
|
|
13
|
+
var LIVE_SOURCES = /* @__PURE__ */ new Set([
|
|
14
|
+
"claude-code",
|
|
15
|
+
"cursor",
|
|
16
|
+
"codex",
|
|
17
|
+
"workspace"
|
|
18
|
+
]);
|
|
19
|
+
function deriveResolve(snapshot) {
|
|
20
|
+
if (!LIVE_SOURCES.has(snapshot.source)) return "snapshot";
|
|
21
|
+
const s = snapshot.scope;
|
|
22
|
+
return s === "global" || s.startsWith("project:") || s.startsWith("workspace:") ? "live" : "snapshot";
|
|
23
|
+
}
|
|
24
|
+
function mcpSecretRef(importId, kind, key) {
|
|
25
|
+
return `agentproto/mcp-import/${importId}#${kind}:${key}`;
|
|
26
|
+
}
|
|
27
|
+
var PLACEHOLDER_RE = /\$\{[A-Z_][A-Z0-9_]*(?::-[^}]*)?\}/i;
|
|
28
|
+
function isLiteralSecretValue(v) {
|
|
29
|
+
return typeof v === "string" && v.length > 0 && v !== SECRET_REF_MARKER && !PLACEHOLDER_RE.test(v);
|
|
30
|
+
}
|
|
31
|
+
async function moveEntrySecrets(entry, hooks) {
|
|
32
|
+
if (!hooks.storeMcpSecret) {
|
|
33
|
+
const has = Object.values(entry.snapshot.headers ?? {}).some(isLiteralSecretValue) || Object.values(entry.snapshot.env ?? {}).some(isLiteralSecretValue);
|
|
34
|
+
return {
|
|
35
|
+
entry,
|
|
36
|
+
moved: [],
|
|
37
|
+
warnings: has ? [
|
|
38
|
+
"no secret store available: header/env values were kept as literals in imported-mcps.json (mode 0600)"
|
|
39
|
+
] : []
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const moved = [];
|
|
43
|
+
const warnings = [];
|
|
44
|
+
const secretRefs = {
|
|
45
|
+
...entry.secretRefs?.headers ? { headers: { ...entry.secretRefs.headers } } : {},
|
|
46
|
+
...entry.secretRefs?.env ? { env: { ...entry.secretRefs.env } } : {}
|
|
47
|
+
};
|
|
48
|
+
const headers = entry.snapshot.headers ? { ...entry.snapshot.headers } : void 0;
|
|
49
|
+
const env = entry.snapshot.env ? { ...entry.snapshot.env } : void 0;
|
|
50
|
+
const passes = [
|
|
51
|
+
["header", headers],
|
|
52
|
+
["env", env]
|
|
53
|
+
];
|
|
54
|
+
for (const [kind, map] of passes) {
|
|
55
|
+
if (!map) continue;
|
|
56
|
+
for (const [key, value] of Object.entries(map)) {
|
|
57
|
+
if (!isLiteralSecretValue(value)) continue;
|
|
58
|
+
const ref = mcpSecretRef(entry.id, kind, key);
|
|
59
|
+
try {
|
|
60
|
+
await hooks.storeMcpSecret(ref, value);
|
|
61
|
+
if (hooks.resolveMcpSecret) {
|
|
62
|
+
const back = await hooks.resolveMcpSecret(ref);
|
|
63
|
+
if (back !== value) throw new Error("read-back mismatch");
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
warnings.push(
|
|
67
|
+
`could not store ${kind} "${key}" in the secret store (${err instanceof Error ? err.message : "error"}); kept as a literal`
|
|
68
|
+
);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
map[key] = SECRET_REF_MARKER;
|
|
72
|
+
const bucket = kind === "header" ? "headers" : "env";
|
|
73
|
+
secretRefs[bucket] = { ...secretRefs[bucket] ?? {}, [key]: ref };
|
|
74
|
+
moved.push(`${kind}:${key}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (moved.length === 0) return { entry, moved, warnings };
|
|
78
|
+
return {
|
|
79
|
+
entry: {
|
|
80
|
+
...entry,
|
|
81
|
+
snapshot: {
|
|
82
|
+
...entry.snapshot,
|
|
83
|
+
...headers ? { headers } : {},
|
|
84
|
+
...env ? { env } : {}
|
|
85
|
+
},
|
|
86
|
+
secretRefs
|
|
87
|
+
},
|
|
88
|
+
moved,
|
|
89
|
+
warnings
|
|
90
|
+
};
|
|
91
|
+
}
|
|
12
92
|
var EMPTY = {
|
|
13
93
|
version: IMPORTED_MCPS_VERSION,
|
|
14
94
|
imports: []
|
|
@@ -34,7 +114,10 @@ async function loadImportedMcps(path = IMPORTED_MCPS_PATH()) {
|
|
|
34
114
|
async function saveImportedMcps(config, path = IMPORTED_MCPS_PATH()) {
|
|
35
115
|
await promises.mkdir(dirname(path), { recursive: true });
|
|
36
116
|
const tmp = `${path}.tmp.${process.pid}`;
|
|
37
|
-
await promises.writeFile(tmp, JSON.stringify(config, null, 2) + "\n",
|
|
117
|
+
await promises.writeFile(tmp, JSON.stringify(config, null, 2) + "\n", {
|
|
118
|
+
encoding: "utf8",
|
|
119
|
+
mode: 384
|
|
120
|
+
});
|
|
38
121
|
await promises.rename(tmp, path);
|
|
39
122
|
}
|
|
40
123
|
function addImport(config, input) {
|
|
@@ -47,11 +130,30 @@ function addImport(config, input) {
|
|
|
47
130
|
id: input.snapshot.id,
|
|
48
131
|
alias,
|
|
49
132
|
addedAt,
|
|
50
|
-
snapshot: input.snapshot
|
|
133
|
+
snapshot: input.snapshot,
|
|
134
|
+
origin: {
|
|
135
|
+
kind: input.snapshot.source,
|
|
136
|
+
scope: input.snapshot.scope,
|
|
137
|
+
name: input.snapshot.name
|
|
138
|
+
},
|
|
139
|
+
resolve: deriveResolve(input.snapshot)
|
|
51
140
|
};
|
|
52
141
|
const others = config.imports.filter((e) => e.id !== input.snapshot.id);
|
|
53
142
|
return { ...config, imports: [...others, next] };
|
|
54
143
|
}
|
|
144
|
+
async function addImportWithSecrets(config, input, hooks) {
|
|
145
|
+
const added = addImport(config, input);
|
|
146
|
+
const entry = added.imports.find((e) => e.id === input.snapshot.id);
|
|
147
|
+
const moved = await moveEntrySecrets(entry, hooks);
|
|
148
|
+
return {
|
|
149
|
+
config: {
|
|
150
|
+
...added,
|
|
151
|
+
imports: added.imports.map((e) => e.id === entry.id ? moved.entry : e)
|
|
152
|
+
},
|
|
153
|
+
entry: moved.entry,
|
|
154
|
+
warnings: moved.warnings
|
|
155
|
+
};
|
|
156
|
+
}
|
|
55
157
|
function removeImport(config, id) {
|
|
56
158
|
return { ...config, imports: config.imports.filter((e) => e.id !== id) };
|
|
57
159
|
}
|
|
@@ -71,12 +173,52 @@ function normalize(parsed) {
|
|
|
71
173
|
const addedAt = typeof e.addedAt === "string" ? e.addedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
72
174
|
const snapshot = e.snapshot;
|
|
73
175
|
if (!id || !snapshot) continue;
|
|
74
|
-
|
|
176
|
+
const extra = {};
|
|
177
|
+
const origin = parseOrigin(e.origin);
|
|
178
|
+
if (origin) extra.origin = origin;
|
|
179
|
+
if (e.resolve === "live" || e.resolve === "snapshot") extra.resolve = e.resolve;
|
|
180
|
+
const secretRefs = parseSecretRefs(e.secretRefs);
|
|
181
|
+
if (secretRefs) extra.secretRefs = secretRefs;
|
|
182
|
+
imports.push({ id, alias, addedAt, snapshot, ...extra });
|
|
75
183
|
}
|
|
76
184
|
}
|
|
77
185
|
return { version: IMPORTED_MCPS_VERSION, imports };
|
|
78
186
|
}
|
|
187
|
+
function parseOrigin(v) {
|
|
188
|
+
if (!v || typeof v !== "object") return void 0;
|
|
189
|
+
const o = v;
|
|
190
|
+
if (typeof o.kind !== "string" || typeof o.scope !== "string" || typeof o.name !== "string")
|
|
191
|
+
return void 0;
|
|
192
|
+
return { kind: o.kind, scope: o.scope, name: o.name };
|
|
193
|
+
}
|
|
194
|
+
function parseStringMap(v) {
|
|
195
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
|
|
196
|
+
const out = {};
|
|
197
|
+
for (const [k, val] of Object.entries(v)) {
|
|
198
|
+
if (typeof val === "string") out[k] = val;
|
|
199
|
+
}
|
|
200
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
201
|
+
}
|
|
202
|
+
function parseSecretRefs(v) {
|
|
203
|
+
if (!v || typeof v !== "object") return void 0;
|
|
204
|
+
const o = v;
|
|
205
|
+
const headers = parseStringMap(o.headers);
|
|
206
|
+
const env = parseStringMap(o.env);
|
|
207
|
+
if (!headers && !env) return void 0;
|
|
208
|
+
return { ...headers ? { headers } : {}, ...env ? { env } : {} };
|
|
209
|
+
}
|
|
210
|
+
function secretRefKeys(entry) {
|
|
211
|
+
const r = entry.secretRefs;
|
|
212
|
+
if (!r) return void 0;
|
|
213
|
+
const headers = r.headers ? Object.keys(r.headers) : [];
|
|
214
|
+
const env = r.env ? Object.keys(r.env) : [];
|
|
215
|
+
if (headers.length === 0 && env.length === 0) return void 0;
|
|
216
|
+
return {
|
|
217
|
+
...headers.length ? { headers } : {},
|
|
218
|
+
...env.length ? { env } : {}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
79
221
|
|
|
80
|
-
export { IMPORTED_MCPS_PATH, IMPORTED_MCPS_VERSION, addImport, findImport, loadImportedMcps, removeImport, saveImportedMcps };
|
|
222
|
+
export { IMPORTED_MCPS_PATH, IMPORTED_MCPS_VERSION, SECRET_REF_MARKER, addImport, addImportWithSecrets, deriveResolve, findImport, isLiteralSecretValue, loadImportedMcps, mcpSecretRef, moveEntrySecrets, removeImport, saveImportedMcps, secretRefKeys };
|
|
81
223
|
//# sourceMappingURL=mcp-imports.mjs.map
|
|
82
224
|
//# sourceMappingURL=mcp-imports.mjs.map
|
package/dist/mcp-imports.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp-imports.ts"],"names":["resolvePath","fs"],"mappings":"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","file":"mcp-imports.mjs","sourcesContent":["/**\n * `~/.agentproto/imported-mcps.json` — the user's curated set of\n * discovered MCPs they want the daemon to know about. v1 is just\n * persistence + read-side; the actual MCP-proxy implementation\n * (where the daemon's /mcp endpoint aggregates the imported\n * servers' tools under namespace prefixes) is v2 work.\n *\n * Purpose: today, \"I see you have chrome-devtools in claude\" is\n * read-only. Once a user imports it, the operator agent can refer\n * to it (\"the user said it's enabled — call it\") and a future\n * proxy layer can actually expose it. We persist the choice now so\n * the data is in place when the proxy lands.\n *\n * File shape:\n * {\n * \"version\": 1,\n * \"imports\": [\n * {\n * \"id\": \"claude-code:project:/path:chrome-devtools\",\n * \"alias\": \"chrome-devtools\",\n * \"addedAt\": \"2026-05-10T...\",\n * \"snapshot\": { ...the DiscoveredMcp at import time }\n * }\n * ]\n * }\n *\n * Snapshotting at import time keeps the daemon resilient to the\n * source config getting deleted (user runs `claude mcp remove ...`).\n * The imported entry stays usable; only the next discovery pass\n * stops listing it as \"available to import.\"\n */\n\nimport { promises as fs } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { dirname, resolve as resolvePath } from \"node:path\"\nimport type { DiscoveredMcp } from \"./mcp-discovery.js\"\n\nexport const IMPORTED_MCPS_PATH = (): string =>\n resolvePath(homedir(), \".agentproto\", \"imported-mcps.json\")\n\nexport const IMPORTED_MCPS_VERSION = 1 as const\n\nexport interface ImportedMcpEntry {\n id: string\n alias: string\n addedAt: string\n snapshot: DiscoveredMcp\n}\n\nexport interface ImportedMcpsConfig {\n version: typeof IMPORTED_MCPS_VERSION\n imports: ImportedMcpEntry[]\n}\n\nconst EMPTY: ImportedMcpsConfig = {\n version: IMPORTED_MCPS_VERSION,\n imports: [],\n}\n\nexport async function loadImportedMcps(\n path: string = IMPORTED_MCPS_PATH()\n): Promise<ImportedMcpsConfig> {\n let raw: string\n try {\n raw = await fs.readFile(path, \"utf8\")\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return EMPTY\n throw err\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (err) {\n throw new Error(\n `agentproto: ${path} is not valid JSON (${\n err instanceof Error ? err.message : String(err)\n }). Delete or fix manually.`\n )\n }\n return normalize(parsed)\n}\n\nexport async function saveImportedMcps(\n config: ImportedMcpsConfig,\n path: string = IMPORTED_MCPS_PATH()\n): Promise<void> {\n await fs.mkdir(dirname(path), { recursive: true })\n const tmp = `${path}.tmp.${process.pid}`\n await fs.writeFile(tmp, JSON.stringify(config, null, 2) + \"\\n\", \"utf8\")\n await fs.rename(tmp, path)\n}\n\n/**\n * Add (or replace) an import. Snapshots the discovered MCP at\n * import time so the entry stays usable when the source config\n * disappears.\n */\nexport function addImport(\n config: ImportedMcpsConfig,\n input: { snapshot: DiscoveredMcp; alias?: string }\n): ImportedMcpsConfig {\n const alias = (input.alias ?? input.snapshot.name).trim()\n if (!alias) {\n throw new Error(\"addImport: alias resolves to empty string\")\n }\n const addedAt = new Date().toISOString()\n const next: ImportedMcpEntry = {\n id: input.snapshot.id,\n alias,\n addedAt,\n snapshot: input.snapshot,\n }\n const others = config.imports.filter(e => e.id !== input.snapshot.id)\n return { ...config, imports: [...others, next] }\n}\n\nexport function removeImport(\n config: ImportedMcpsConfig,\n id: string\n): ImportedMcpsConfig {\n return { ...config, imports: config.imports.filter(e => e.id !== id) }\n}\n\nexport function findImport(\n config: ImportedMcpsConfig,\n id: string\n): ImportedMcpEntry | undefined {\n return config.imports.find(e => e.id === id)\n}\n\nfunction normalize(parsed: unknown): ImportedMcpsConfig {\n if (!parsed || typeof parsed !== \"object\") return EMPTY\n const obj = parsed as Record<string, unknown>\n const imports: ImportedMcpEntry[] = []\n if (Array.isArray(obj.imports)) {\n for (const entry of obj.imports) {\n if (!entry || typeof entry !== \"object\") continue\n const e = entry as Record<string, unknown>\n const id = typeof e.id === \"string\" ? e.id : \"\"\n const alias = typeof e.alias === \"string\" ? e.alias : id\n const addedAt =\n typeof e.addedAt === \"string\" ? e.addedAt : new Date().toISOString()\n const snapshot = e.snapshot as DiscoveredMcp | undefined\n if (!id || !snapshot) continue\n imports.push({ id, alias, addedAt, snapshot })\n }\n }\n return { version: IMPORTED_MCPS_VERSION, imports }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp-imports.ts"],"names":["resolvePath","fs"],"mappings":"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","file":"mcp-imports.mjs","sourcesContent":["/**\n * `~/.agentproto/imported-mcps.json` — the user's curated set of\n * discovered MCPs they want the daemon to know about. v1 is just\n * persistence + read-side; the actual MCP-proxy implementation\n * (where the daemon's /mcp endpoint aggregates the imported\n * servers' tools under namespace prefixes) is v2 work.\n *\n * Purpose: today, \"I see you have chrome-devtools in claude\" is\n * read-only. Once a user imports it, the operator agent can refer\n * to it (\"the user said it's enabled — call it\") and a future\n * proxy layer can actually expose it. We persist the choice now so\n * the data is in place when the proxy lands.\n *\n * File shape:\n * {\n * \"version\": 1,\n * \"imports\": [\n * {\n * \"id\": \"claude-code:project:/path:chrome-devtools\",\n * \"alias\": \"chrome-devtools\",\n * \"addedAt\": \"2026-05-10T...\",\n * \"snapshot\": { ...the DiscoveredMcp at import time }\n * }\n * ]\n * }\n *\n * Snapshotting at import time keeps the daemon resilient to the\n * source config getting deleted (user runs `claude mcp remove ...`).\n * The imported entry stays usable; only the next discovery pass\n * stops listing it as \"available to import.\"\n */\n\nimport { promises as fs } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { dirname, resolve as resolvePath } from \"node:path\"\nimport type { DiscoveredMcp } from \"./mcp-discovery.js\"\n\nexport const IMPORTED_MCPS_PATH = (): string =>\n resolvePath(homedir(), \".agentproto\", \"imported-mcps.json\")\n\nexport const IMPORTED_MCPS_VERSION = 1 as const\n\n/** Where an import came from. `plugin` (written by plugin-local-browser) and\n * `manual` have no discovery source; `kind` otherwise mirrors\n * `DiscoveredMcp[\"source\"]`. */\nexport interface ImportedMcpOrigin {\n kind: DiscoveredMcp[\"source\"] | \"plugin\" | \"manual\"\n scope: string\n name: string\n}\n\n/** `live` = re-read the source harness config at connect time (the snapshot is\n * only a fallback); `snapshot` = the stored copy is authoritative. */\nexport type ImportedMcpResolve = \"live\" | \"snapshot\"\n\n/** Opaque secret refs (resolved via `resolveMcpSecret`), keyed by the header\n * / env key they fill. Values are refs, NEVER secret material. */\nexport interface ImportedMcpSecretRefs {\n headers?: Record<string, string>\n env?: Record<string, string>\n}\n\nexport interface ImportedMcpEntry {\n id: string\n alias: string\n addedAt: string\n snapshot: DiscoveredMcp\n /** Additive (no file-version bump): link back to the source config. */\n origin?: ImportedMcpOrigin\n resolve?: ImportedMcpResolve\n secretRefs?: ImportedMcpSecretRefs\n}\n\n/** Value written into `snapshot.headers[K]` / `snapshot.env[K]` in place of a\n * literal secret that now lives behind `secretRefs`. */\nexport const SECRET_REF_MARKER = \"<secretRef>\"\n\n/** Discovery sources whose config can be re-read live. */\nconst LIVE_SOURCES: ReadonlySet<string> = new Set([\n \"claude-code\",\n \"cursor\",\n \"codex\",\n \"workspace\",\n])\n\n/** `live` only when discovery can actually re-find the entry: a discovery\n * source AND a scope discovery scans (`global` / `project:*` / `workspace:*`).\n * claude-code `local`-scope and plugin/manual entries stay `snapshot`. */\nexport function deriveResolve(snapshot: DiscoveredMcp): ImportedMcpResolve {\n if (!LIVE_SOURCES.has(snapshot.source)) return \"snapshot\"\n const s = snapshot.scope\n return s === \"global\" || s.startsWith(\"project:\") || s.startsWith(\"workspace:\")\n ? \"live\"\n : \"snapshot\"\n}\n\n/** Keychain path + account for one secret slot of an import. Ref format\n * `agentproto/mcp-import/<importId>#<header|env>:<KEY>` (split on LAST `#`). */\nexport function mcpSecretRef(\n importId: string,\n kind: \"header\" | \"env\",\n key: string\n): string {\n return `agentproto/mcp-import/${importId}#${kind}:${key}`\n}\n\nconst PLACEHOLDER_RE = /\\$\\{[A-Z_][A-Z0-9_]*(?::-[^}]*)?\\}/i\n\n/** A value worth moving to the keychain: non-empty, not already the marker,\n * and not a `${VAR}` placeholder (those expand at connect, no secret at rest). */\nexport function isLiteralSecretValue(v: unknown): v is string {\n return (\n typeof v === \"string\" &&\n v.length > 0 &&\n v !== SECRET_REF_MARKER &&\n !PLACEHOLDER_RE.test(v)\n )\n}\n\nexport interface MoveSecretsResult {\n entry: ImportedMcpEntry\n /** Header/env KEY names moved this call (never values). */\n moved: string[]\n /** Human warnings (key names only). */\n warnings: string[]\n}\n\n/**\n * Move every literal header/env value of `entry.snapshot` behind a keychain\n * ref. Keychain write happens FIRST per key; only on success is the snapshot\n * value replaced by the marker (a failed write keeps the literal + warns).\n * Read-back verification when `resolve` is supplied.\n */\nexport async function moveEntrySecrets(\n entry: ImportedMcpEntry,\n hooks: {\n storeMcpSecret?: (ref: string, value: string) => Promise<void>\n resolveMcpSecret?: (ref: string) => Promise<string | undefined>\n }\n): Promise<MoveSecretsResult> {\n if (!hooks.storeMcpSecret) {\n const has =\n Object.values(entry.snapshot.headers ?? {}).some(isLiteralSecretValue) ||\n Object.values(entry.snapshot.env ?? {}).some(isLiteralSecretValue)\n return {\n entry,\n moved: [],\n warnings: has\n ? [\n \"no secret store available: header/env values were kept as literals in imported-mcps.json (mode 0600)\",\n ]\n : [],\n }\n }\n const moved: string[] = []\n const warnings: string[] = []\n const secretRefs: ImportedMcpSecretRefs = {\n ...(entry.secretRefs?.headers ? { headers: { ...entry.secretRefs.headers } } : {}),\n ...(entry.secretRefs?.env ? { env: { ...entry.secretRefs.env } } : {}),\n }\n const headers = entry.snapshot.headers ? { ...entry.snapshot.headers } : undefined\n const env = entry.snapshot.env ? { ...entry.snapshot.env } : undefined\n const passes: Array<[\"header\" | \"env\", Record<string, string> | undefined]> = [\n [\"header\", headers],\n [\"env\", env],\n ]\n for (const [kind, map] of passes) {\n if (!map) continue\n for (const [key, value] of Object.entries(map)) {\n if (!isLiteralSecretValue(value)) continue\n const ref = mcpSecretRef(entry.id, kind, key)\n try {\n await hooks.storeMcpSecret(ref, value)\n if (hooks.resolveMcpSecret) {\n const back = await hooks.resolveMcpSecret(ref)\n if (back !== value) throw new Error(\"read-back mismatch\")\n }\n } catch (err) {\n warnings.push(\n `could not store ${kind} \"${key}\" in the secret store (${\n err instanceof Error ? err.message : \"error\"\n }); kept as a literal`\n )\n continue\n }\n map[key] = SECRET_REF_MARKER\n const bucket = kind === \"header\" ? \"headers\" : \"env\"\n secretRefs[bucket] = { ...(secretRefs[bucket] ?? {}), [key]: ref }\n moved.push(`${kind}:${key}`)\n }\n }\n if (moved.length === 0) return { entry, moved, warnings }\n return {\n entry: {\n ...entry,\n snapshot: {\n ...entry.snapshot,\n ...(headers ? { headers } : {}),\n ...(env ? { env } : {}),\n },\n secretRefs,\n },\n moved,\n warnings,\n }\n}\n\nexport interface ImportedMcpsConfig {\n version: typeof IMPORTED_MCPS_VERSION\n imports: ImportedMcpEntry[]\n}\n\nconst EMPTY: ImportedMcpsConfig = {\n version: IMPORTED_MCPS_VERSION,\n imports: [],\n}\n\nexport async function loadImportedMcps(\n path: string = IMPORTED_MCPS_PATH()\n): Promise<ImportedMcpsConfig> {\n let raw: string\n try {\n raw = await fs.readFile(path, \"utf8\")\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return EMPTY\n throw err\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (err) {\n throw new Error(\n `agentproto: ${path} is not valid JSON (${\n err instanceof Error ? err.message : String(err)\n }). Delete or fix manually.`\n )\n }\n return normalize(parsed)\n}\n\nexport async function saveImportedMcps(\n config: ImportedMcpsConfig,\n path: string = IMPORTED_MCPS_PATH()\n): Promise<void> {\n await fs.mkdir(dirname(path), { recursive: true })\n const tmp = `${path}.tmp.${process.pid}`\n // 0600: entries may hold literal upstream credentials (parity with bundles.ts).\n await fs.writeFile(tmp, JSON.stringify(config, null, 2) + \"\\n\", {\n encoding: \"utf8\",\n mode: 0o600,\n })\n await fs.rename(tmp, path)\n}\n\n/**\n * Add (or replace) an import. Snapshots the discovered MCP at\n * import time so the entry stays usable when the source config\n * disappears.\n */\nexport function addImport(\n config: ImportedMcpsConfig,\n input: { snapshot: DiscoveredMcp; alias?: string }\n): ImportedMcpsConfig {\n const alias = (input.alias ?? input.snapshot.name).trim()\n if (!alias) {\n throw new Error(\"addImport: alias resolves to empty string\")\n }\n const addedAt = new Date().toISOString()\n const next: ImportedMcpEntry = {\n id: input.snapshot.id,\n alias,\n addedAt,\n snapshot: input.snapshot,\n origin: {\n kind: input.snapshot.source,\n scope: input.snapshot.scope,\n name: input.snapshot.name,\n },\n resolve: deriveResolve(input.snapshot),\n }\n const others = config.imports.filter(e => e.id !== input.snapshot.id)\n return { ...config, imports: [...others, next] }\n}\n\n/**\n * `addImport` + move literal header/env values behind keychain refs (via the\n * `storeMcpSecret` hook). Hook absent → literals kept and a warning returned.\n * Callers persist `config` and surface `warnings` in their reply.\n */\nexport async function addImportWithSecrets(\n config: ImportedMcpsConfig,\n input: { snapshot: DiscoveredMcp; alias?: string },\n hooks: {\n storeMcpSecret?: (ref: string, value: string) => Promise<void>\n resolveMcpSecret?: (ref: string) => Promise<string | undefined>\n }\n): Promise<{ config: ImportedMcpsConfig; entry: ImportedMcpEntry; warnings: string[] }> {\n const added = addImport(config, input)\n const entry = added.imports.find(e => e.id === input.snapshot.id)!\n const moved = await moveEntrySecrets(entry, hooks)\n return {\n config: {\n ...added,\n imports: added.imports.map(e => (e.id === entry.id ? moved.entry : e)),\n },\n entry: moved.entry,\n warnings: moved.warnings,\n }\n}\n\nexport function removeImport(\n config: ImportedMcpsConfig,\n id: string\n): ImportedMcpsConfig {\n return { ...config, imports: config.imports.filter(e => e.id !== id) }\n}\n\nexport function findImport(\n config: ImportedMcpsConfig,\n id: string\n): ImportedMcpEntry | undefined {\n return config.imports.find(e => e.id === id)\n}\n\nfunction normalize(parsed: unknown): ImportedMcpsConfig {\n if (!parsed || typeof parsed !== \"object\") return EMPTY\n const obj = parsed as Record<string, unknown>\n const imports: ImportedMcpEntry[] = []\n if (Array.isArray(obj.imports)) {\n for (const entry of obj.imports) {\n if (!entry || typeof entry !== \"object\") continue\n const e = entry as Record<string, unknown>\n const id = typeof e.id === \"string\" ? e.id : \"\"\n const alias = typeof e.alias === \"string\" ? e.alias : id\n const addedAt =\n typeof e.addedAt === \"string\" ? e.addedAt : new Date().toISOString()\n const snapshot = e.snapshot as DiscoveredMcp | undefined\n if (!id || !snapshot) continue\n const extra: Partial<ImportedMcpEntry> = {}\n const origin = parseOrigin(e.origin)\n if (origin) extra.origin = origin\n if (e.resolve === \"live\" || e.resolve === \"snapshot\") extra.resolve = e.resolve\n const secretRefs = parseSecretRefs(e.secretRefs)\n if (secretRefs) extra.secretRefs = secretRefs\n imports.push({ id, alias, addedAt, snapshot, ...extra })\n }\n }\n return { version: IMPORTED_MCPS_VERSION, imports }\n}\n\nfunction parseOrigin(v: unknown): ImportedMcpOrigin | undefined {\n if (!v || typeof v !== \"object\") return undefined\n const o = v as Record<string, unknown>\n if (\n typeof o.kind !== \"string\" ||\n typeof o.scope !== \"string\" ||\n typeof o.name !== \"string\"\n )\n return undefined\n return { kind: o.kind as ImportedMcpOrigin[\"kind\"], scope: o.scope, name: o.name }\n}\n\nfunction parseStringMap(v: unknown): Record<string, string> | undefined {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) return undefined\n const out: Record<string, string> = {}\n for (const [k, val] of Object.entries(v as Record<string, unknown>)) {\n if (typeof val === \"string\") out[k] = val\n }\n return Object.keys(out).length > 0 ? out : undefined\n}\n\nfunction parseSecretRefs(v: unknown): ImportedMcpSecretRefs | undefined {\n if (!v || typeof v !== \"object\") return undefined\n const o = v as Record<string, unknown>\n const headers = parseStringMap(o.headers)\n const env = parseStringMap(o.env)\n if (!headers && !env) return undefined\n return { ...(headers ? { headers } : {}), ...(env ? { env } : {}) }\n}\n\n/** KEY names only (never refs' targets or values) — for status surfaces. */\nexport function secretRefKeys(\n entry: Pick<ImportedMcpEntry, \"secretRefs\">\n): { headers?: string[]; env?: string[] } | undefined {\n const r = entry.secretRefs\n if (!r) return undefined\n const headers = r.headers ? Object.keys(r.headers) : []\n const env = r.env ? Object.keys(r.env) : []\n if (headers.length === 0 && env.length === 0) return undefined\n return {\n ...(headers.length ? { headers } : {}),\n ...(env.length ? { env } : {}),\n }\n}\n"]}
|