@kwonye/mcpx 0.1.105 → 0.1.106
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/dist/adapters/codex.d.ts +2 -0
- package/dist/adapters/index.d.ts +2 -1
- package/dist/adapters/json.d.ts +26 -0
- package/dist/adapters/pi.d.ts +9 -0
- package/dist/chunk-0xt563m1.js +126 -0
- package/dist/{chunk-btbww96n.js → chunk-1mrkz29t.js} +69 -56
- package/dist/{chunk-6mxr34tc.js → chunk-1v66g9d6.js} +4 -3
- package/dist/chunk-1yq3yb66.js +126 -0
- package/dist/chunk-27mq4agf.js +33 -0
- package/dist/{chunk-t4hqf6yh.js → chunk-4aafp444.js} +8136 -8262
- package/dist/chunk-4xym942z.js +36 -0
- package/dist/{chunk-tnhekcpv.js → chunk-56z18csx.js} +1 -1
- package/dist/{chunk-bnbz55g6.js → chunk-65kshh9w.js} +1 -1
- package/dist/{chunk-xydajvzg.js → chunk-7ky3rsw9.js} +1 -1
- package/dist/chunk-7rq88hb3.js +563 -0
- package/dist/chunk-8v13hqrp.js +825 -0
- package/dist/chunk-a2gen8bn.js +730 -0
- package/dist/{chunk-01s7rhx3.js → chunk-brxwa30j.js} +130 -113
- package/dist/chunk-chaj8a8b.js +45 -0
- package/dist/chunk-e96643mj.js +26 -0
- package/dist/{chunk-d2v9rxy0.js → chunk-ew06e41w.js} +12 -6
- package/dist/chunk-ezk8txcy.js +23 -0
- package/dist/{chunk-4y0t0tfj.js → chunk-grkz21sq.js} +29 -3
- package/dist/{chunk-12vgqm89.js → chunk-gvjxpk98.js} +1 -1
- package/dist/chunk-h9kp47rp.js +18 -0
- package/dist/chunk-hahs24dv.js +127 -0
- package/dist/{chunk-y9a1rx7j.js → chunk-jv0s0cp6.js} +5 -3
- package/dist/{chunk-enzmq965.js → chunk-n0462d52.js} +1 -1
- package/dist/chunk-nh6hhv29.js +138 -0
- package/dist/chunk-p7yzyt87.js +19426 -0
- package/dist/chunk-q86c5s6r.js +7 -0
- package/dist/chunk-qf8bj9y7.js +254 -0
- package/dist/chunk-t20f49g3.js +30 -0
- package/dist/{chunk-am903qjk.js → chunk-v041118a.js} +11 -5
- package/dist/{chunk-91vcwk5m.js → chunk-v14bg5yp.js} +1 -1
- package/dist/chunk-wvzap0na.js +18166 -0
- package/dist/chunk-wztmp5b4.js +30479 -0
- package/dist/chunk-xvqr1q9b.js +14 -0
- package/dist/{chunk-h158rkts.js → chunk-yj7mpczw.js} +17 -5
- package/dist/chunk-yr0txkw0.js +31 -0
- package/dist/cli.js +3879 -1061
- package/dist/core/adoption.d.ts +62 -0
- package/dist/core/analytics.d.ts +17 -0
- package/dist/core/consumers.d.ts +8 -0
- package/dist/core/control.d.ts +12 -0
- package/dist/core/daemon.d.ts +2 -2
- package/dist/core/file-state.d.ts +5 -0
- package/dist/core/harnesses.d.ts +25 -0
- package/dist/core/index.d.ts +12 -4
- package/dist/core/index.js +109 -71
- package/dist/core/managed-index-lock.d.ts +1 -1
- package/dist/core/management.d.ts +32 -0
- package/dist/core/native-document.d.ts +4 -0
- package/dist/core/operations.d.ts +52 -0
- package/dist/core/plugin-projections.d.ts +1 -0
- package/dist/core/runtime-entry.d.ts +6 -0
- package/dist/core/runtime-snapshot.d.ts +6 -0
- package/dist/core/secret-keychain.d.ts +31 -0
- package/dist/core/secrets.d.ts +12 -2
- package/dist/core/served-skills.d.ts +62 -0
- package/dist/core/status.d.ts +1 -0
- package/dist/core/sync-plan.d.ts +14 -0
- package/dist/core/sync.d.ts +3 -0
- package/dist/core/telemetry.d.ts +2 -2
- package/dist/core/tui.d.ts +20 -0
- package/dist/core/verify-upstreams.d.ts +4 -0
- package/dist/core/work-limiter.d.ts +10 -0
- package/dist/gateway/server.d.ts +2 -0
- package/dist/types.d.ts +62 -1
- package/dist/util/fs.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/adapters/opencode.d.ts +0 -11
- package/dist/chunk-56whm3hm.js +0 -4055
- package/dist/chunk-fw8dyt89.js +0 -344
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
import {
|
|
2
|
+
adoptionPlanSchema,
|
|
3
|
+
applyAdoption,
|
|
4
|
+
clearOAuthCredentials,
|
|
5
|
+
listHarnesses,
|
|
6
|
+
listOperations,
|
|
7
|
+
markInterruptedOperations,
|
|
8
|
+
mcpV2Headers,
|
|
9
|
+
mcpV2Meta,
|
|
10
|
+
operationEvents,
|
|
11
|
+
planAdoption,
|
|
12
|
+
probeOAuthSupport,
|
|
13
|
+
protectServerSecrets,
|
|
14
|
+
rollbackOperation,
|
|
15
|
+
saveOperation,
|
|
16
|
+
synchronizeClients
|
|
17
|
+
} from "./chunk-wztmp5b4.js";
|
|
18
|
+
import {
|
|
19
|
+
SecretsManager,
|
|
20
|
+
addServer,
|
|
21
|
+
registerProject,
|
|
22
|
+
removeServer,
|
|
23
|
+
setProjectServerEnabled,
|
|
24
|
+
setServerEnabled,
|
|
25
|
+
unregisterProject,
|
|
26
|
+
updateServer
|
|
27
|
+
} from "./chunk-8v13hqrp.js";
|
|
28
|
+
import {
|
|
29
|
+
mutateConfig
|
|
30
|
+
} from "./chunk-hahs24dv.js";
|
|
31
|
+
import {
|
|
32
|
+
processAlive
|
|
33
|
+
} from "./chunk-chaj8a8b.js";
|
|
34
|
+
import {
|
|
35
|
+
listSkills
|
|
36
|
+
} from "./chunk-7rq88hb3.js";
|
|
37
|
+
import {
|
|
38
|
+
loadConfig,
|
|
39
|
+
loadMergedConfig,
|
|
40
|
+
parseCliAddCommand
|
|
41
|
+
} from "./chunk-brxwa30j.js";
|
|
42
|
+
import {
|
|
43
|
+
exports_external
|
|
44
|
+
} from "./chunk-p7yzyt87.js";
|
|
45
|
+
import {
|
|
46
|
+
getConfigPath,
|
|
47
|
+
getRuntimeDir,
|
|
48
|
+
sha256
|
|
49
|
+
} from "./chunk-grkz21sq.js";
|
|
50
|
+
import {
|
|
51
|
+
__require
|
|
52
|
+
} from "./chunk-wj4tjacg.js";
|
|
53
|
+
|
|
54
|
+
// src/core/control.ts
|
|
55
|
+
import fs from "node:fs";
|
|
56
|
+
|
|
57
|
+
// src/core/runtime-snapshot.ts
|
|
58
|
+
var committed;
|
|
59
|
+
function freeze(value) {
|
|
60
|
+
if (value && typeof value === "object") {
|
|
61
|
+
for (const child of Object.values(value))
|
|
62
|
+
freeze(child);
|
|
63
|
+
Object.freeze(value);
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
function compileRuntimeSnapshot() {
|
|
68
|
+
committed = freeze(structuredClone(loadMergedConfig()));
|
|
69
|
+
}
|
|
70
|
+
function clearRuntimeSnapshot() {
|
|
71
|
+
committed = undefined;
|
|
72
|
+
}
|
|
73
|
+
function runtimeConfig() {
|
|
74
|
+
return committed ?? loadMergedConfig();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/core/control.ts
|
|
78
|
+
import path from "node:path";
|
|
79
|
+
import os from "node:os";
|
|
80
|
+
import http from "node:http";
|
|
81
|
+
import crypto from "node:crypto";
|
|
82
|
+
|
|
83
|
+
// src/core/auth-probe.ts
|
|
84
|
+
var JSON_RPC_VERSION = "2.0";
|
|
85
|
+
var DEFAULT_AUTH_PROBE_TIMEOUT_MS = 8000;
|
|
86
|
+
var AUTH_KEYWORDS = [
|
|
87
|
+
"unauthorized",
|
|
88
|
+
"unauthenticated",
|
|
89
|
+
"forbidden",
|
|
90
|
+
"authentication",
|
|
91
|
+
"authorization",
|
|
92
|
+
"api key",
|
|
93
|
+
"api_key",
|
|
94
|
+
"apikey",
|
|
95
|
+
"credentials",
|
|
96
|
+
"access denied",
|
|
97
|
+
"auth required",
|
|
98
|
+
"x-api-key",
|
|
99
|
+
"invalid key",
|
|
100
|
+
"missing header",
|
|
101
|
+
"not authenticated",
|
|
102
|
+
"auth token"
|
|
103
|
+
];
|
|
104
|
+
function isAuthRelatedMessage(message) {
|
|
105
|
+
const lower = message.toLowerCase();
|
|
106
|
+
return AUTH_KEYWORDS.some((kw) => lower.includes(kw));
|
|
107
|
+
}
|
|
108
|
+
function resolveHeaders(spec, secrets) {
|
|
109
|
+
const headers = {
|
|
110
|
+
"content-type": "application/json",
|
|
111
|
+
accept: "application/json"
|
|
112
|
+
};
|
|
113
|
+
for (const [key, value] of Object.entries(spec.headers ?? {})) {
|
|
114
|
+
headers[key] = secrets.resolveMaybeSecret(value);
|
|
115
|
+
}
|
|
116
|
+
return headers;
|
|
117
|
+
}
|
|
118
|
+
function isOAuthLikelyChallenge(wwwAuthenticate) {
|
|
119
|
+
if (!wwwAuthenticate) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const lower = wwwAuthenticate.toLowerCase();
|
|
123
|
+
return lower.includes("bearer") || lower.includes("authorization_uri") || lower.includes("resource_metadata");
|
|
124
|
+
}
|
|
125
|
+
async function probeHttpAuthRequirement(spec, secrets, timeoutMs = DEFAULT_AUTH_PROBE_TIMEOUT_MS, options = {}) {
|
|
126
|
+
const timeoutController = new AbortController;
|
|
127
|
+
const timeoutHandle = setTimeout(() => timeoutController.abort(), timeoutMs);
|
|
128
|
+
const discoverOAuth = options.discoverOAuth ?? true;
|
|
129
|
+
async function withOAuthSupport(result) {
|
|
130
|
+
if (!discoverOAuth) {
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
const support = await probeOAuthSupport(spec.url).catch(() => {
|
|
134
|
+
return;
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
...result,
|
|
138
|
+
oauthSupport: support?.support,
|
|
139
|
+
oauthLikely: result.oauthLikely || support?.support === "supported"
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const headers = mcpV2Headers("tools/list", resolveHeaders(spec, secrets));
|
|
144
|
+
const response = await fetch(spec.url, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers,
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
149
|
+
id: "mcpx-auth-probe",
|
|
150
|
+
method: "tools/list",
|
|
151
|
+
params: {
|
|
152
|
+
_meta: {
|
|
153
|
+
...mcpV2Meta({ name: "mcpx-auth-probe", version: "2.0.0" })
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}),
|
|
157
|
+
signal: timeoutController.signal
|
|
158
|
+
});
|
|
159
|
+
if (response.status === 401 || response.status === 403) {
|
|
160
|
+
const wwwAuthenticate = response.headers.get("www-authenticate");
|
|
161
|
+
return await withOAuthSupport({
|
|
162
|
+
authRequired: true,
|
|
163
|
+
oauthLikely: isOAuthLikelyChallenge(wwwAuthenticate),
|
|
164
|
+
status: response.status,
|
|
165
|
+
wwwAuthenticate: wwwAuthenticate ?? undefined
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (response.status === 200) {
|
|
169
|
+
try {
|
|
170
|
+
const body = await response.json();
|
|
171
|
+
const err = body?.error;
|
|
172
|
+
if (err && typeof err === "object" && !("result" in body)) {
|
|
173
|
+
const message = typeof err.message === "string" ? err.message : "";
|
|
174
|
+
if (message && isAuthRelatedMessage(message)) {
|
|
175
|
+
return await withOAuthSupport({
|
|
176
|
+
authRequired: true,
|
|
177
|
+
status: response.status,
|
|
178
|
+
error: `JSON-RPC error: ${message}`
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} catch {}
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
authRequired: false,
|
|
186
|
+
status: response.status
|
|
187
|
+
};
|
|
188
|
+
} catch (error) {
|
|
189
|
+
const isAbort = error.name === "AbortError";
|
|
190
|
+
return {
|
|
191
|
+
authRequired: false,
|
|
192
|
+
error: isAbort ? `Auth probe timed out after ${timeoutMs}ms.` : error.message
|
|
193
|
+
};
|
|
194
|
+
} finally {
|
|
195
|
+
clearTimeout(timeoutHandle);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/core/management.ts
|
|
200
|
+
var string = exports_external.string().min(1);
|
|
201
|
+
var serverSchema = exports_external.discriminatedUnion("transport", [exports_external.object({ transport: exports_external.literal("http"), url: exports_external.url(), headers: exports_external.record(exports_external.string(), exports_external.string()).optional(), enabled: exports_external.boolean().optional(), scope: exports_external.string().optional() }), exports_external.object({ transport: exports_external.literal("stdio"), command: string, args: exports_external.array(exports_external.string()).optional(), env: exports_external.record(exports_external.string(), exports_external.string()).optional(), cwd: string.optional(), enabled: exports_external.boolean().optional(), scope: exports_external.string().optional() })]);
|
|
202
|
+
async function syncManaged(targets) {
|
|
203
|
+
return synchronizeClients({ targetClients: targets });
|
|
204
|
+
}
|
|
205
|
+
async function managementCommand(method, raw) {
|
|
206
|
+
if (method === "status") {
|
|
207
|
+
const { buildStatusReport } = await import("./chunk-e96643mj.js");
|
|
208
|
+
const { loadManagedIndex } = await import("./chunk-xvqr1q9b.js");
|
|
209
|
+
return buildStatusReport(loadMergedConfig(), loadManagedIndex());
|
|
210
|
+
}
|
|
211
|
+
if (method === "auth.configure") {
|
|
212
|
+
const [input] = exports_external.tuple([exports_external.object({ serverName: string, headerName: string.optional(), envName: string.optional(), authValue: string, secretName: string.optional(), raw: exports_external.boolean().optional() })]).parse(raw);
|
|
213
|
+
const { resolveAuthTarget, defaultAuthSecretName, maybePrefixBearer, applyAuthReference } = await import("./chunk-ezk8txcy.js");
|
|
214
|
+
await mutateConfig((config) => {
|
|
215
|
+
const spec = config.servers[input.serverName];
|
|
216
|
+
if (!spec)
|
|
217
|
+
throw new Error("Unknown server");
|
|
218
|
+
const target = resolveAuthTarget(spec, input.headerName, input.envName);
|
|
219
|
+
const key = input.secretName ?? defaultAuthSecretName(input.serverName, target);
|
|
220
|
+
new SecretsManager().setSecret(key, maybePrefixBearer(target, input.authValue, input.raw ?? false));
|
|
221
|
+
applyAuthReference(spec, target, `secret://${key}`);
|
|
222
|
+
});
|
|
223
|
+
return { configured: true, sync: await syncManaged() };
|
|
224
|
+
}
|
|
225
|
+
if (method === "snapshot") {
|
|
226
|
+
const config = loadConfig();
|
|
227
|
+
return { revision: config.revision ?? 0, capturedAt: new Date().toISOString(), harnesses: listHarnesses(config), operations: listOperations(), servers: Object.entries(config.servers).map(([name, spec]) => ({ name, transport: spec.transport, enabled: spec.enabled !== false, scope: spec.scope })), skills: listSkills().map((skill) => ({ id: skill.id, source: skill.source, readOnly: skill.readOnly })), packages: Object.values(config.packages ?? {}), plugins: Object.values(config.plugins ?? {}).map((plugin) => ({ id: plugin.id, name: plugin.name, version: plugin.version, status: plugin.status, enabled: plugin.enabled })) };
|
|
228
|
+
}
|
|
229
|
+
if (method === "adoption.plan")
|
|
230
|
+
return planAdoption(exports_external.tuple([exports_external.array(string).optional()]).parse(raw)[0]);
|
|
231
|
+
if (method === "adoption.apply") {
|
|
232
|
+
const [plan, selected] = exports_external.tuple([adoptionPlanSchema, exports_external.array(string).optional()]).parse(raw);
|
|
233
|
+
return applyAdoption(plan, selected);
|
|
234
|
+
}
|
|
235
|
+
if (method === "operation.rollback")
|
|
236
|
+
return rollbackOperation(exports_external.tuple([exports_external.uuid()]).parse(raw)[0]);
|
|
237
|
+
if (method === "package.pin") {
|
|
238
|
+
const [id, pinned] = exports_external.tuple([string, exports_external.boolean()]).parse(raw);
|
|
239
|
+
await mutateConfig((config) => {
|
|
240
|
+
if (!config.packages?.[id])
|
|
241
|
+
throw new Error("Unknown native package");
|
|
242
|
+
config.packages[id].pinned = pinned;
|
|
243
|
+
});
|
|
244
|
+
return { id, pinned };
|
|
245
|
+
}
|
|
246
|
+
if (method === "harness.mode") {
|
|
247
|
+
const [id, mode] = exports_external.tuple([string, exports_external.enum(["observe", "selected", "automatic"])]).parse(raw);
|
|
248
|
+
if (!listHarnesses(loadConfig()).some((h) => h.id === id))
|
|
249
|
+
throw new Error("Unknown harness installation");
|
|
250
|
+
await mutateConfig((config) => {
|
|
251
|
+
config.harnesses ??= {};
|
|
252
|
+
config.harnesses[id] = { mode };
|
|
253
|
+
});
|
|
254
|
+
return { id, mode };
|
|
255
|
+
}
|
|
256
|
+
if (method === "sync")
|
|
257
|
+
return syncManaged(exports_external.tuple([exports_external.array(string).optional()]).parse(raw)[0]);
|
|
258
|
+
if (method === "server.add" || method === "server.update" || method === "server.command") {
|
|
259
|
+
const [name, spec, resolvedSecrets] = method === "server.command" ? (() => {
|
|
260
|
+
const [command] = exports_external.tuple([string]).parse(raw);
|
|
261
|
+
const parsed = parseCliAddCommand(command);
|
|
262
|
+
return [parsed.name, parsed.spec, undefined];
|
|
263
|
+
})() : exports_external.tuple([string, serverSchema, exports_external.record(exports_external.string(), exports_external.string()).optional()]).parse(raw);
|
|
264
|
+
const secrets = new SecretsManager;
|
|
265
|
+
for (const [key, value] of Object.entries(resolvedSecrets ?? {}))
|
|
266
|
+
secrets.setSecret(key, value);
|
|
267
|
+
const safe = protectServerSecrets(name, spec, secrets);
|
|
268
|
+
await mutateConfig((config) => {
|
|
269
|
+
if (method === "server.update")
|
|
270
|
+
updateServer(config, name, safe);
|
|
271
|
+
else
|
|
272
|
+
addServer(config, name, safe, true);
|
|
273
|
+
});
|
|
274
|
+
const summary = await syncManaged();
|
|
275
|
+
const probe = safe.transport === "http" ? await probeHttpAuthRequirement(safe, secrets) : undefined;
|
|
276
|
+
return { [method === "server.update" ? "updated" : "added"]: name, sync: summary, ...probe?.authRequired ? { authRequired: true, authStatus: probe.status, oauthLikely: probe.oauthLikely, oauthSupport: probe.oauthSupport } : {} };
|
|
277
|
+
}
|
|
278
|
+
if (method === "server.remove") {
|
|
279
|
+
const [name] = exports_external.tuple([string]).parse(raw);
|
|
280
|
+
await mutateConfig((config) => removeServer(config, name));
|
|
281
|
+
clearOAuthCredentials(name, new SecretsManager);
|
|
282
|
+
return { removed: name, sync: await syncManaged() };
|
|
283
|
+
}
|
|
284
|
+
if (method === "server.enabled") {
|
|
285
|
+
const [name, enabled] = exports_external.tuple([string, exports_external.boolean()]).parse(raw);
|
|
286
|
+
await mutateConfig((config) => setServerEnabled(config, name, enabled));
|
|
287
|
+
return { updated: name, enabled, sync: await syncManaged() };
|
|
288
|
+
}
|
|
289
|
+
if (method === "project.enabled") {
|
|
290
|
+
const [projectPath, name, enabled] = exports_external.tuple([string, string, exports_external.boolean()]).parse(raw);
|
|
291
|
+
const result = await mutateConfig((config) => setProjectServerEnabled(config, projectPath, name, enabled));
|
|
292
|
+
return { updated: name, enabled, ...result, sync: await syncManaged() };
|
|
293
|
+
}
|
|
294
|
+
if (method === "project.add" || method === "project.remove") {
|
|
295
|
+
const [projectPath, name] = exports_external.tuple([string, string.optional()]).parse(raw);
|
|
296
|
+
await mutateConfig((config) => method === "project.add" ? registerProject(config, projectPath, name) : unregisterProject(config, projectPath));
|
|
297
|
+
return { success: true, sync: await syncManaged() };
|
|
298
|
+
}
|
|
299
|
+
if (method.startsWith("skill.")) {
|
|
300
|
+
const skills = await import("./chunk-27mq4agf.js");
|
|
301
|
+
const action = method.slice(6);
|
|
302
|
+
const [id, value] = exports_external.tuple([string, string.optional()]).parse(raw);
|
|
303
|
+
let result;
|
|
304
|
+
switch (action) {
|
|
305
|
+
case "save":
|
|
306
|
+
skills.saveSkill(id, string.parse(value));
|
|
307
|
+
result = { id, success: true };
|
|
308
|
+
break;
|
|
309
|
+
case "delete":
|
|
310
|
+
skills.deleteSkill(id);
|
|
311
|
+
result = { id, success: true };
|
|
312
|
+
break;
|
|
313
|
+
case "install":
|
|
314
|
+
result = await skills.installSkillFromSource(id, value);
|
|
315
|
+
break;
|
|
316
|
+
case "customize":
|
|
317
|
+
result = skills.customizeSkill(id, value);
|
|
318
|
+
break;
|
|
319
|
+
case "pin":
|
|
320
|
+
skills.pinSkill(id);
|
|
321
|
+
result = { id, success: true };
|
|
322
|
+
break;
|
|
323
|
+
case "unpin":
|
|
324
|
+
skills.unpinSkill(id);
|
|
325
|
+
result = { id, success: true };
|
|
326
|
+
break;
|
|
327
|
+
case "update":
|
|
328
|
+
result = await skills.updateSkill(id);
|
|
329
|
+
break;
|
|
330
|
+
case "rollback":
|
|
331
|
+
result = skills.rollbackSkill(id);
|
|
332
|
+
break;
|
|
333
|
+
default:
|
|
334
|
+
throw new Error("Unknown skill operation");
|
|
335
|
+
}
|
|
336
|
+
const sync = await syncManaged();
|
|
337
|
+
return { ...result, sync };
|
|
338
|
+
}
|
|
339
|
+
if (method.startsWith("plugin.")) {
|
|
340
|
+
const plugins = await import("./chunk-v041118a.js");
|
|
341
|
+
const [name, extra, value, project] = exports_external.tuple([string.optional(), exports_external.unknown().optional(), exports_external.unknown().optional(), exports_external.unknown().optional()]).parse(raw);
|
|
342
|
+
const action = method.slice(7);
|
|
343
|
+
const options = exports_external.object({ name: string.optional(), enabled: exports_external.boolean().optional(), components: exports_external.record(exports_external.string(), exports_external.boolean()).optional(), approvals: exports_external.record(exports_external.string(), exports_external.boolean()).optional() });
|
|
344
|
+
switch (action) {
|
|
345
|
+
case "install":
|
|
346
|
+
return plugins.installPlugin(string.parse(name), options.optional().parse(extra));
|
|
347
|
+
case "prepare":
|
|
348
|
+
await plugins.preparePlugin(string.parse(name));
|
|
349
|
+
break;
|
|
350
|
+
case "update":
|
|
351
|
+
return plugins.updatePlugin(string.parse(name));
|
|
352
|
+
case "pin":
|
|
353
|
+
await plugins.pinPlugin(string.parse(name));
|
|
354
|
+
break;
|
|
355
|
+
case "unpin":
|
|
356
|
+
await plugins.unpinPlugin(string.parse(name));
|
|
357
|
+
break;
|
|
358
|
+
case "rollback":
|
|
359
|
+
return plugins.rollbackPlugin(string.parse(name));
|
|
360
|
+
case "uninstall":
|
|
361
|
+
await plugins.uninstallPlugin(string.parse(name), exports_external.object({ keepData: exports_external.boolean().optional() }).optional().parse(extra)?.keepData);
|
|
362
|
+
break;
|
|
363
|
+
case "enable":
|
|
364
|
+
await plugins.enablePlugin(string.parse(name));
|
|
365
|
+
break;
|
|
366
|
+
case "disable":
|
|
367
|
+
await plugins.disablePlugin(string.parse(name));
|
|
368
|
+
break;
|
|
369
|
+
case "approve":
|
|
370
|
+
await plugins.approvePluginComponent(string.parse(name), string.parse(extra));
|
|
371
|
+
break;
|
|
372
|
+
case "project":
|
|
373
|
+
await plugins.setPluginProjectOverride(string.parse(name), string.parse(extra), exports_external.object({ enabled: exports_external.boolean().optional(), components: exports_external.record(exports_external.string(), exports_external.boolean()).optional() }).parse(value));
|
|
374
|
+
break;
|
|
375
|
+
case "resetProject":
|
|
376
|
+
await plugins.resetPluginProjectOverride(string.parse(name), string.parse(extra));
|
|
377
|
+
break;
|
|
378
|
+
case "config":
|
|
379
|
+
await plugins.pluginConfigSet(string.parse(name), string.parse(extra), string.parse(value), string.optional().parse(project));
|
|
380
|
+
break;
|
|
381
|
+
case "sync":
|
|
382
|
+
await plugins.pluginSync();
|
|
383
|
+
break;
|
|
384
|
+
default:
|
|
385
|
+
throw new Error("Unknown plugin operation");
|
|
386
|
+
}
|
|
387
|
+
return { name, success: true };
|
|
388
|
+
}
|
|
389
|
+
if (method.startsWith("marketplace.")) {
|
|
390
|
+
const marketplaces = await import("./chunk-ew06e41w.js");
|
|
391
|
+
const [name, extra] = exports_external.tuple([string, exports_external.unknown().optional()]).parse(raw);
|
|
392
|
+
switch (method.slice(12)) {
|
|
393
|
+
case "add":
|
|
394
|
+
return marketplaces.addMarketplace(name, string.optional().parse(extra));
|
|
395
|
+
case "refresh":
|
|
396
|
+
return marketplaces.refreshMarketplaceWithPlugins(name);
|
|
397
|
+
case "remove":
|
|
398
|
+
await marketplaces.removeMarketplace(name);
|
|
399
|
+
return { name, success: true };
|
|
400
|
+
case "automatic":
|
|
401
|
+
return marketplaces.setMarketplaceAutoUpdate(name, exports_external.boolean().parse(extra));
|
|
402
|
+
case "install":
|
|
403
|
+
return marketplaces.installMarketplacePlugin(name);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (method === "share.import") {
|
|
407
|
+
const [directory, options] = exports_external.tuple([string, exports_external.object({ locked: exports_external.boolean().optional(), dryRun: exports_external.boolean().optional(), inputs: exports_external.record(exports_external.string(), exports_external.string()).optional() }).optional()]).parse(raw);
|
|
408
|
+
const { importEnvironment } = await import("./chunk-h9kp47rp.js");
|
|
409
|
+
const result = await importEnvironment(directory, options);
|
|
410
|
+
if (!options?.dryRun)
|
|
411
|
+
await syncManaged();
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
throw new Error(`Unknown management method: ${method}`);
|
|
415
|
+
}
|
|
416
|
+
async function getManagementSnapshot() {
|
|
417
|
+
return managementCommand("snapshot", []);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/core/control.ts
|
|
421
|
+
var CONTROL_VERSION = 1;
|
|
422
|
+
function controlSocketPath() {
|
|
423
|
+
if (process.platform === "win32")
|
|
424
|
+
return `\\\\.\\pipe\\mcpx-${sha256(`${os.userInfo().username}:${getConfigPath()}`).slice(0, 20)}`;
|
|
425
|
+
const normal = path.join(getRuntimeDir(), "control.sock");
|
|
426
|
+
return Buffer.byteLength(normal) < 96 ? normal : path.join(os.tmpdir(), `mcpx-${process.getuid?.() ?? "user"}-${sha256(getConfigPath()).slice(0, 12)}.sock`);
|
|
427
|
+
}
|
|
428
|
+
function controlKey() {
|
|
429
|
+
const file = path.join(getRuntimeDir(), "control.key");
|
|
430
|
+
fs.mkdirSync(getRuntimeDir(), { recursive: true, mode: 448 });
|
|
431
|
+
if (!fs.existsSync(file)) {
|
|
432
|
+
try {
|
|
433
|
+
fs.writeFileSync(file, crypto.randomBytes(32).toString("base64url"), { mode: 384, flag: "wx" });
|
|
434
|
+
} catch (error) {
|
|
435
|
+
if (error.code !== "EEXIST")
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return fs.readFileSync(file, "utf8").trim();
|
|
440
|
+
}
|
|
441
|
+
function authorized(value) {
|
|
442
|
+
const a = Buffer.from(value ?? ""), b = Buffer.from(`Bearer ${controlKey()}`);
|
|
443
|
+
return a.length === b.length && crypto.timingSafeEqual(a, b);
|
|
444
|
+
}
|
|
445
|
+
var requestSchema = exports_external.object({ version: exports_external.literal(1), method: exports_external.string(), args: exports_external.array(exports_external.unknown()).default([]) });
|
|
446
|
+
var queue = Promise.resolve();
|
|
447
|
+
var activeWriters = 0;
|
|
448
|
+
function coordinate(work) {
|
|
449
|
+
const execute = async () => {
|
|
450
|
+
activeWriters++;
|
|
451
|
+
try {
|
|
452
|
+
return await work();
|
|
453
|
+
} finally {
|
|
454
|
+
try {
|
|
455
|
+
compileRuntimeSnapshot();
|
|
456
|
+
} finally {
|
|
457
|
+
activeWriters--;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
const next = queue.then(execute, execute);
|
|
462
|
+
queue = next.catch(() => {
|
|
463
|
+
return;
|
|
464
|
+
});
|
|
465
|
+
return next;
|
|
466
|
+
}
|
|
467
|
+
async function startControlServer() {
|
|
468
|
+
const socketPath = controlSocketPath();
|
|
469
|
+
controlKey();
|
|
470
|
+
if (process.platform !== "win32" && fs.existsSync(socketPath)) {
|
|
471
|
+
if (!fs.lstatSync(socketPath).isSocket())
|
|
472
|
+
throw new Error("Control socket path is occupied by a non-socket file.");
|
|
473
|
+
try {
|
|
474
|
+
await controlRequest("snapshot");
|
|
475
|
+
throw new Error("Another mcpx control service is already running");
|
|
476
|
+
} catch (error) {
|
|
477
|
+
if (!["ENOENT", "ECONNREFUSED", "FailedToOpenSocket"].includes(error.code ?? ""))
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
fs.unlinkSync(socketPath);
|
|
481
|
+
}
|
|
482
|
+
const clients = new Set;
|
|
483
|
+
const leases = new Map;
|
|
484
|
+
const server = http.createServer(async (request, response) => {
|
|
485
|
+
if (request.headers.origin || !authorized(request.headers.authorization)) {
|
|
486
|
+
response.writeHead(403);
|
|
487
|
+
response.end();
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (request.url === "/v1/events" && request.method === "GET") {
|
|
491
|
+
response.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache" });
|
|
492
|
+
response.write(`data: ${JSON.stringify({ type: "connected", version: CONTROL_VERSION })}
|
|
493
|
+
|
|
494
|
+
`);
|
|
495
|
+
clients.add(response);
|
|
496
|
+
request.on("close", () => clients.delete(response));
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (request.url !== "/v1/command" || request.method !== "POST") {
|
|
500
|
+
response.writeHead(404);
|
|
501
|
+
response.end();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
let body = "";
|
|
506
|
+
for await (const chunk of request) {
|
|
507
|
+
body += chunk;
|
|
508
|
+
if (Buffer.byteLength(body) > 4 * 1024 * 1024)
|
|
509
|
+
throw new Error("Control request is too large");
|
|
510
|
+
}
|
|
511
|
+
const input = requestSchema.parse(JSON.parse(body));
|
|
512
|
+
if (input.method === "service.health") {
|
|
513
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
514
|
+
response.end(JSON.stringify({ result: { pid: process.pid, activeWriters } }));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
if (input.method === "lease.release") {
|
|
518
|
+
const [id, success] = exports_external.tuple([exports_external.uuid(), exports_external.boolean().optional()]).parse(input.args);
|
|
519
|
+
leases.get(id)?.(success);
|
|
520
|
+
leases.delete(id);
|
|
521
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
522
|
+
response.end(JSON.stringify({ result: true }));
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (input.method === "lease.acquire") {
|
|
526
|
+
const [pid, label] = exports_external.tuple([exports_external.number().int().positive(), exports_external.string()]).parse(input.args);
|
|
527
|
+
const id = crypto.randomUUID();
|
|
528
|
+
let ready;
|
|
529
|
+
const started = new Promise((resolve) => {
|
|
530
|
+
ready = resolve;
|
|
531
|
+
});
|
|
532
|
+
coordinate(async () => {
|
|
533
|
+
const operation = { id, label: `CLI ${label}`, phase: "applying", progress: 0, startedAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
|
|
534
|
+
saveOperation(operation);
|
|
535
|
+
let succeeded = true;
|
|
536
|
+
await new Promise((resolve) => {
|
|
537
|
+
const timer = setInterval(() => {
|
|
538
|
+
if (!processAlive(pid))
|
|
539
|
+
finish(false);
|
|
540
|
+
}, 1000);
|
|
541
|
+
const finish = (success = true) => {
|
|
542
|
+
succeeded = success;
|
|
543
|
+
clearInterval(timer);
|
|
544
|
+
leases.delete(id);
|
|
545
|
+
resolve();
|
|
546
|
+
};
|
|
547
|
+
leases.set(id, finish);
|
|
548
|
+
ready();
|
|
549
|
+
});
|
|
550
|
+
operation.phase = succeeded ? "complete" : "failed";
|
|
551
|
+
operation.progress = 1;
|
|
552
|
+
saveOperation(operation);
|
|
553
|
+
});
|
|
554
|
+
await started;
|
|
555
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
556
|
+
response.end(JSON.stringify({ result: id }));
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const work = async () => {
|
|
560
|
+
return managementCommand(input.method, input.args);
|
|
561
|
+
};
|
|
562
|
+
const result = input.method === "snapshot" ? await work() : await coordinate(async () => {
|
|
563
|
+
if (input.method === "adoption.plan")
|
|
564
|
+
return work();
|
|
565
|
+
const operation = { id: crypto.randomUUID(), label: input.method, phase: "applying", progress: 0, startedAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
|
|
566
|
+
saveOperation(operation);
|
|
567
|
+
try {
|
|
568
|
+
const result2 = await work();
|
|
569
|
+
const outcome = exports_external.object({ hasErrors: exports_external.boolean().optional(), sync: exports_external.object({ hasErrors: exports_external.boolean() }).optional() }).safeParse(result2);
|
|
570
|
+
const failed = outcome.success && (outcome.data.hasErrors || outcome.data.sync?.hasErrors);
|
|
571
|
+
operation.phase = failed ? "failed" : "complete";
|
|
572
|
+
operation.progress = 1;
|
|
573
|
+
if (failed)
|
|
574
|
+
operation.message = "The integration was saved, but synchronization needs attention. Review the affected harness diagnostics and retry sync.";
|
|
575
|
+
saveOperation(operation);
|
|
576
|
+
return result2;
|
|
577
|
+
} catch (error) {
|
|
578
|
+
operation.phase = "failed";
|
|
579
|
+
operation.message = "Operation failed. Review the affected integration and retry.";
|
|
580
|
+
saveOperation(operation);
|
|
581
|
+
throw error;
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
585
|
+
response.end(JSON.stringify({ version: CONTROL_VERSION, result }));
|
|
586
|
+
} catch (error) {
|
|
587
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
588
|
+
response.end(JSON.stringify({ error: error instanceof exports_external.ZodError || error instanceof SyntaxError ? "Invalid management input" : error.message }));
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
const notify = (operation) => {
|
|
592
|
+
for (const client of clients)
|
|
593
|
+
client.write(`data: ${JSON.stringify({ type: "operation", operation })}
|
|
594
|
+
|
|
595
|
+
`);
|
|
596
|
+
};
|
|
597
|
+
operationEvents.on("change", notify);
|
|
598
|
+
await new Promise((resolve, reject) => {
|
|
599
|
+
server.once("error", reject);
|
|
600
|
+
server.listen(socketPath, () => {
|
|
601
|
+
server.removeListener("error", reject);
|
|
602
|
+
if (process.platform !== "win32")
|
|
603
|
+
fs.chmodSync(socketPath, 384);
|
|
604
|
+
resolve();
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
markInterruptedOperations();
|
|
608
|
+
compileRuntimeSnapshot();
|
|
609
|
+
let reconciling = false;
|
|
610
|
+
let observedPlan = "";
|
|
611
|
+
const reconcile = setInterval(() => {
|
|
612
|
+
if (reconciling)
|
|
613
|
+
return;
|
|
614
|
+
reconciling = true;
|
|
615
|
+
coordinate(async () => {
|
|
616
|
+
if (listOperations().some((operation) => operation.phase === "recovery-required"))
|
|
617
|
+
return;
|
|
618
|
+
const config = loadConfig();
|
|
619
|
+
const targets = Object.entries(config.harnesses ?? {}).filter(([, settings]) => settings.mode === "automatic").map(([id]) => id);
|
|
620
|
+
if (!targets.length)
|
|
621
|
+
return;
|
|
622
|
+
const plan = planAdoption(targets);
|
|
623
|
+
const fingerprint = sha256(JSON.stringify(plan.items));
|
|
624
|
+
if (fingerprint === observedPlan)
|
|
625
|
+
return;
|
|
626
|
+
observedPlan = fingerprint;
|
|
627
|
+
const safe = plan.items.filter((item) => item.action === "import" || item.action === "reuse");
|
|
628
|
+
if (safe.length)
|
|
629
|
+
await applyAdoption(plan, safe.map((item) => item.id));
|
|
630
|
+
}).catch(() => {
|
|
631
|
+
const time = new Date().toISOString();
|
|
632
|
+
saveOperation({ id: crypto.randomUUID(), label: "Automatic adoption", phase: "failed", startedAt: time, updatedAt: time, progress: 0, message: "An installation could not be adopted. Scan existing installs to review its configuration." });
|
|
633
|
+
}).finally(() => {
|
|
634
|
+
reconciling = false;
|
|
635
|
+
});
|
|
636
|
+
}, 3000);
|
|
637
|
+
reconcile.unref();
|
|
638
|
+
return async () => {
|
|
639
|
+
clearInterval(reconcile);
|
|
640
|
+
clearRuntimeSnapshot();
|
|
641
|
+
operationEvents.off("change", notify);
|
|
642
|
+
for (const client of clients)
|
|
643
|
+
client.end();
|
|
644
|
+
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function controlRequest(method, args = []) {
|
|
648
|
+
return new Promise((resolve, reject) => {
|
|
649
|
+
const supplied = [...args];
|
|
650
|
+
while (supplied.length && supplied.at(-1) === undefined)
|
|
651
|
+
supplied.pop();
|
|
652
|
+
const body = JSON.stringify({ version: CONTROL_VERSION, method, args: supplied });
|
|
653
|
+
const request = http.request({ socketPath: controlSocketPath(), path: "/v1/command", method: "POST", headers: { authorization: `Bearer ${controlKey()}`, "content-type": "application/json", "content-length": Buffer.byteLength(body) } }, (response) => {
|
|
654
|
+
let data = "";
|
|
655
|
+
response.on("data", (chunk) => {
|
|
656
|
+
data += chunk;
|
|
657
|
+
});
|
|
658
|
+
response.on("end", () => {
|
|
659
|
+
try {
|
|
660
|
+
const parsed = JSON.parse(data);
|
|
661
|
+
if (response.statusCode !== 200)
|
|
662
|
+
reject(new Error(parsed.error ?? "Management request failed"));
|
|
663
|
+
else
|
|
664
|
+
resolve(parsed.result);
|
|
665
|
+
} catch {
|
|
666
|
+
reject(new Error("Invalid management response"));
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
request.on("error", reject);
|
|
671
|
+
request.end(body);
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
async function ensureControlService(cliPath) {
|
|
675
|
+
try {
|
|
676
|
+
await controlRequest("snapshot");
|
|
677
|
+
return;
|
|
678
|
+
} catch (error) {
|
|
679
|
+
if (!["ENOENT", "ECONNREFUSED", "FailedToOpenSocket"].includes(error.code ?? ""))
|
|
680
|
+
throw error;
|
|
681
|
+
}
|
|
682
|
+
const { startDaemon } = await import("./chunk-4xym942z.js");
|
|
683
|
+
await startDaemon(loadConfig(), cliPath, new SecretsManager);
|
|
684
|
+
for (let attempt = 0;attempt < 50; attempt++) {
|
|
685
|
+
try {
|
|
686
|
+
await controlRequest("snapshot");
|
|
687
|
+
return;
|
|
688
|
+
} catch {
|
|
689
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
throw new Error("mcpx control service did not become ready");
|
|
693
|
+
}
|
|
694
|
+
function subscribeControlEvents(listener, onError) {
|
|
695
|
+
let closed = false;
|
|
696
|
+
const request = http.get({ socketPath: controlSocketPath(), path: "/v1/events", headers: { authorization: `Bearer ${controlKey()}` } }, (response) => {
|
|
697
|
+
response.on("end", () => {
|
|
698
|
+
if (!closed)
|
|
699
|
+
onError?.(new Error("Local service disconnected"));
|
|
700
|
+
});
|
|
701
|
+
let pending = "";
|
|
702
|
+
response.on("data", (chunk) => {
|
|
703
|
+
pending += chunk;
|
|
704
|
+
let boundary;
|
|
705
|
+
while ((boundary = pending.indexOf(`
|
|
706
|
+
|
|
707
|
+
`)) >= 0) {
|
|
708
|
+
const event = pending.slice(0, boundary);
|
|
709
|
+
pending = pending.slice(boundary + 2);
|
|
710
|
+
if (event.startsWith("data: ")) {
|
|
711
|
+
try {
|
|
712
|
+
listener(JSON.parse(event.slice(6)));
|
|
713
|
+
} catch {}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
request.on("error", (error) => {
|
|
719
|
+
if (!closed)
|
|
720
|
+
onError?.(error);
|
|
721
|
+
});
|
|
722
|
+
return () => {
|
|
723
|
+
closed = true;
|
|
724
|
+
request.destroy();
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
export { runtimeConfig, probeHttpAuthRequirement, getManagementSnapshot, CONTROL_VERSION, controlSocketPath, coordinate, startControlServer, controlRequest, ensureControlService, subscribeControlEvents };
|
|
729
|
+
|
|
730
|
+
//# debugId=32A461FC6536AB7664756E2164756E21
|