caplets 0.15.0 → 0.17.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 +110 -14
- package/dist/index.js +2890 -775
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,441 +9,25 @@ import { homedir, tmpdir } from "node:os";
|
|
|
9
9
|
import { PassThrough } from "node:stream";
|
|
10
10
|
import { createServer } from "node:http";
|
|
11
11
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
12
|
+
import { Buffer as Buffer$1 } from "node:buffer";
|
|
12
13
|
import { createInterface } from "node:readline/promises";
|
|
13
14
|
import { createServer as createServer$1 } from "http";
|
|
14
15
|
import { Http2ServerRequest, constants as constants$1 } from "http2";
|
|
15
16
|
import { Readable } from "stream";
|
|
16
17
|
import crypto$1 from "crypto";
|
|
17
|
-
//#region
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
operation: "Wrapper operation: get_caplet, check_backend, list_tools, search_tools, get_tool, or call_tool.",
|
|
28
|
-
query: "Required for search_tools only.",
|
|
29
|
-
limit: "Optional search_tools result limit.",
|
|
30
|
-
tool: "Exact downstream tool name for get_tool or call_tool.",
|
|
31
|
-
arguments: "Required JSON object for call_tool arguments/downstream inputs.",
|
|
32
|
-
fields: "Optional call_tool structured output paths when outputSchema allows it."
|
|
18
|
+
//#region \0rolldown/runtime.js
|
|
19
|
+
var __defProp$1 = Object.defineProperty;
|
|
20
|
+
var __exportAll$1 = (all, no_symbols) => {
|
|
21
|
+
let target = {};
|
|
22
|
+
for (var name in all) __defProp$1(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true
|
|
25
|
+
});
|
|
26
|
+
if (!no_symbols) __defProp$1(target, Symbol.toStringTag, { value: "Module" });
|
|
27
|
+
return target;
|
|
33
28
|
};
|
|
34
|
-
function generatedToolInputJsonSchema() {
|
|
35
|
-
return {
|
|
36
|
-
type: "object",
|
|
37
|
-
properties: {
|
|
38
|
-
operation: {
|
|
39
|
-
type: "string",
|
|
40
|
-
enum: operations,
|
|
41
|
-
description: generatedToolInputDescriptions.operation
|
|
42
|
-
},
|
|
43
|
-
query: {
|
|
44
|
-
type: "string",
|
|
45
|
-
description: generatedToolInputDescriptions.query
|
|
46
|
-
},
|
|
47
|
-
limit: {
|
|
48
|
-
type: "integer",
|
|
49
|
-
minimum: 1,
|
|
50
|
-
description: generatedToolInputDescriptions.limit
|
|
51
|
-
},
|
|
52
|
-
tool: {
|
|
53
|
-
type: "string",
|
|
54
|
-
description: generatedToolInputDescriptions.tool
|
|
55
|
-
},
|
|
56
|
-
arguments: {
|
|
57
|
-
type: "object",
|
|
58
|
-
description: generatedToolInputDescriptions.arguments
|
|
59
|
-
},
|
|
60
|
-
fields: {
|
|
61
|
-
type: "array",
|
|
62
|
-
items: {
|
|
63
|
-
type: "string",
|
|
64
|
-
minLength: 1
|
|
65
|
-
},
|
|
66
|
-
minItems: 1,
|
|
67
|
-
description: generatedToolInputDescriptions.fields
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
required: ["operation"],
|
|
71
|
-
additionalProperties: false
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
29
|
//#endregion
|
|
75
|
-
//#region ../core/dist/
|
|
76
|
-
var __create = Object.create;
|
|
77
|
-
var __defProp = Object.defineProperty;
|
|
78
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
79
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
80
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
81
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
82
|
-
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
83
|
-
var __copyProps = (to, from, except, desc) => {
|
|
84
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
85
|
-
key = keys[i];
|
|
86
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
87
|
-
get: ((k) => from[k]).bind(null, key),
|
|
88
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
return to;
|
|
92
|
-
};
|
|
93
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
94
|
-
value: mod,
|
|
95
|
-
enumerable: true
|
|
96
|
-
}) : target, mod));
|
|
97
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
98
|
-
var CapletsError = class extends Error {
|
|
99
|
-
code;
|
|
100
|
-
details;
|
|
101
|
-
constructor(code, message, details) {
|
|
102
|
-
super(message);
|
|
103
|
-
this.name = "CapletsError";
|
|
104
|
-
this.code = code;
|
|
105
|
-
this.details = details;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
const SECRET_KEY_PATTERN = /(token|secret|authorization|auth|api[-_]?key|password|credential|clientsecret|client_secret|code|refresh)/i;
|
|
109
|
-
const SECRET_VALUE_PATTERN = /(bearer\s+)[a-z0-9._~+/=-]+|([?&](?:access_token|refresh_token|token|code)=)[^&\s]+/gi;
|
|
110
|
-
function redactSecrets(value) {
|
|
111
|
-
if (typeof value === "string") return value.replace(SECRET_VALUE_PATTERN, "$1$2[REDACTED]");
|
|
112
|
-
if (Array.isArray(value)) return value.map((item) => redactSecrets(item));
|
|
113
|
-
if (value && typeof value === "object") {
|
|
114
|
-
const redacted = {};
|
|
115
|
-
for (const [key, nested] of Object.entries(value)) redacted[key] = SECRET_KEY_PATTERN.test(key) ? "[REDACTED]" : redactSecrets(nested);
|
|
116
|
-
return redacted;
|
|
117
|
-
}
|
|
118
|
-
return value;
|
|
119
|
-
}
|
|
120
|
-
function toSafeError(error, fallback = "INTERNAL_ERROR") {
|
|
121
|
-
if (error instanceof CapletsError) return {
|
|
122
|
-
code: error.code,
|
|
123
|
-
message: String(redactSecrets(error.message)),
|
|
124
|
-
...error.details === void 0 ? {} : { details: redactSecrets(error.details) }
|
|
125
|
-
};
|
|
126
|
-
if (error instanceof Error) return {
|
|
127
|
-
code: fallback,
|
|
128
|
-
message: String(redactSecrets(error.message))
|
|
129
|
-
};
|
|
130
|
-
return {
|
|
131
|
-
code: fallback,
|
|
132
|
-
message: String(redactSecrets(error))
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function errorResult(error, fallback) {
|
|
136
|
-
const safe = toSafeError(error, fallback);
|
|
137
|
-
return {
|
|
138
|
-
isError: true,
|
|
139
|
-
content: [{
|
|
140
|
-
type: "text",
|
|
141
|
-
text: `${safe.code}: ${safe.message}`
|
|
142
|
-
}],
|
|
143
|
-
structuredContent: { error: safe }
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
function textContent(text) {
|
|
147
|
-
return text ? [{
|
|
148
|
-
type: "text",
|
|
149
|
-
text
|
|
150
|
-
}] : [];
|
|
151
|
-
}
|
|
152
|
-
function compactJsonText(value, maxLength = 600) {
|
|
153
|
-
return compactText(JSON.stringify(value) ?? String(value), maxLength);
|
|
154
|
-
}
|
|
155
|
-
function compactText(value, maxLength = 600) {
|
|
156
|
-
const collapsed = value.replace(/\s+/gu, " ").trim();
|
|
157
|
-
return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 1).trimEnd()}…` : collapsed;
|
|
158
|
-
}
|
|
159
|
-
function resultKeys(value) {
|
|
160
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return "scalar result";
|
|
161
|
-
const keys = Object.keys(value).filter((key) => key !== "elapsedMs");
|
|
162
|
-
return keys.length > 0 ? `structured keys: ${keys.join(", ")}` : "empty structured result";
|
|
163
|
-
}
|
|
164
|
-
function statusSummary(value) {
|
|
165
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return compactJsonText(value);
|
|
166
|
-
const record = value;
|
|
167
|
-
return [
|
|
168
|
-
typeof record.status === "number" ? `status ${record.status}` : void 0,
|
|
169
|
-
typeof record.statusText === "string" && record.statusText ? record.statusText : void 0,
|
|
170
|
-
typeof record.exitCode === "number" ? `exit ${record.exitCode}` : void 0,
|
|
171
|
-
"body" in record ? "body" : void 0,
|
|
172
|
-
"json" in record ? "json" : void 0,
|
|
173
|
-
typeof record.stdout === "string" && record.stdout ? "stdout" : void 0,
|
|
174
|
-
typeof record.stderr === "string" && record.stderr ? "stderr" : void 0
|
|
175
|
-
].filter((part) => Boolean(part)).join("; ") || resultKeys(record);
|
|
176
|
-
}
|
|
177
|
-
function compactStructuredContent(value) {
|
|
178
|
-
return textContent(statusSummary(value));
|
|
179
|
-
}
|
|
180
|
-
function searchToolList(tools, query, limit, compact) {
|
|
181
|
-
const tokens = query.toLocaleLowerCase().split(/\s+/).filter(Boolean);
|
|
182
|
-
return tools.filter((tool) => {
|
|
183
|
-
const haystack = `${tool.name}\n${tool.description ?? ""}`.toLocaleLowerCase();
|
|
184
|
-
return tokens.some((token) => haystack.includes(token));
|
|
185
|
-
}).sort((left, right) => left.name.localeCompare(right.name)).slice(0, limit).map(compact);
|
|
186
|
-
}
|
|
187
|
-
const DEFAULT_INPUT_SCHEMA$1 = {
|
|
188
|
-
type: "object",
|
|
189
|
-
additionalProperties: true
|
|
190
|
-
};
|
|
191
|
-
var CliToolsManager = class {
|
|
192
|
-
registry;
|
|
193
|
-
constructor(registry) {
|
|
194
|
-
this.registry = registry;
|
|
195
|
-
}
|
|
196
|
-
updateRegistry(registry) {
|
|
197
|
-
this.registry = registry;
|
|
198
|
-
}
|
|
199
|
-
invalidate(_serverId) {}
|
|
200
|
-
async checkTools(config) {
|
|
201
|
-
const startedAt = Date.now();
|
|
202
|
-
try {
|
|
203
|
-
for (const action of actionsFor(config)) {
|
|
204
|
-
const cwdTemplate = action.cwd ?? config.cwd;
|
|
205
|
-
if (cwdTemplate && !cwdTemplate.includes("$input")) {
|
|
206
|
-
if (!existsSync(interpolateRequiredString(cwdTemplate, {}, "cwd"))) throw new CapletsError("CONFIG_INVALID", `CLI cwd does not exist for ${config.server}/${action.name}`);
|
|
207
|
-
}
|
|
208
|
-
if (!action.command.includes("$input")) resolveCommandPath(action.command);
|
|
209
|
-
}
|
|
210
|
-
this.registry.setStatus(config.server, "available");
|
|
211
|
-
return {
|
|
212
|
-
id: config.server,
|
|
213
|
-
status: "available",
|
|
214
|
-
toolCount: Object.keys(config.actions).length,
|
|
215
|
-
elapsedMs: Date.now() - startedAt
|
|
216
|
-
};
|
|
217
|
-
} catch (error) {
|
|
218
|
-
const safe = toSafeError(error, "SERVER_UNAVAILABLE");
|
|
219
|
-
this.registry.setStatus(config.server, "unavailable", safe);
|
|
220
|
-
return {
|
|
221
|
-
id: config.server,
|
|
222
|
-
status: "unavailable",
|
|
223
|
-
elapsedMs: Date.now() - startedAt,
|
|
224
|
-
error: safe
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
async listTools(config) {
|
|
229
|
-
return actionsFor(config).map((action) => this.toTool(action));
|
|
230
|
-
}
|
|
231
|
-
async getTool(config, toolName) {
|
|
232
|
-
return this.toTool(getAction(config, toolName));
|
|
233
|
-
}
|
|
234
|
-
async callTool(config, toolName, args) {
|
|
235
|
-
const action = getAction(config, toolName);
|
|
236
|
-
validateInput(action, args);
|
|
237
|
-
const execution = resolveExecution(config, action, args);
|
|
238
|
-
const startedAt = Date.now();
|
|
239
|
-
const controller = new AbortController();
|
|
240
|
-
const timeout = setTimeout(() => controller.abort(), execution.timeoutMs);
|
|
241
|
-
try {
|
|
242
|
-
const result = await spawnCommand(execution, controller.signal, () => Date.now() - startedAt);
|
|
243
|
-
const structured = parseStructuredResult(action, result, result.exitCode !== 0);
|
|
244
|
-
return {
|
|
245
|
-
content: compactStructuredContent(structured),
|
|
246
|
-
structuredContent: structured,
|
|
247
|
-
isError: result.exitCode !== 0
|
|
248
|
-
};
|
|
249
|
-
} catch (error) {
|
|
250
|
-
if (isAbortError$1(error)) throw new CapletsError("TOOL_CALL_TIMEOUT", `CLI tool timed out for ${config.server}/${toolName}`);
|
|
251
|
-
if (error instanceof CapletsError) throw error;
|
|
252
|
-
throw new CapletsError("DOWNSTREAM_TOOL_ERROR", `CLI tool failed for ${config.server}/${toolName}`, toSafeError(error));
|
|
253
|
-
} finally {
|
|
254
|
-
clearTimeout(timeout);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
compact(config, tool) {
|
|
258
|
-
return {
|
|
259
|
-
id: config.server,
|
|
260
|
-
tool: tool.name,
|
|
261
|
-
...tool.description ? { description: tool.description } : {},
|
|
262
|
-
hasInputSchema: Boolean(tool.inputSchema),
|
|
263
|
-
hasOutputSchema: Boolean(tool.outputSchema)
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
search(config, tools, query, limit) {
|
|
267
|
-
return searchToolList(tools, query, limit, (tool) => this.compact(config, tool));
|
|
268
|
-
}
|
|
269
|
-
toTool(action) {
|
|
270
|
-
return {
|
|
271
|
-
name: action.name,
|
|
272
|
-
...action.description ? { description: action.description } : {},
|
|
273
|
-
inputSchema: action.inputSchema ?? DEFAULT_INPUT_SCHEMA$1,
|
|
274
|
-
...action.outputSchema ? { outputSchema: action.outputSchema } : {},
|
|
275
|
-
...action.annotations ? { annotations: action.annotations } : {}
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
function actionsFor(config) {
|
|
280
|
-
return Object.entries(config.actions).map(([name, action]) => ({
|
|
281
|
-
name,
|
|
282
|
-
...action
|
|
283
|
-
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
284
|
-
}
|
|
285
|
-
function getAction(config, toolName) {
|
|
286
|
-
const actions = actionsFor(config);
|
|
287
|
-
const action = actions.find((candidate) => candidate.name === toolName);
|
|
288
|
-
if (!action) throw new CapletsError("TOOL_NOT_FOUND", `Tool ${toolName} was not found on ${config.server}`, {
|
|
289
|
-
server: config.server,
|
|
290
|
-
tool: toolName,
|
|
291
|
-
suggestions: actions.map((candidate) => candidate.name).filter((name) => name.toLocaleLowerCase().includes(toolName.toLocaleLowerCase()[0] ?? "")).slice(0, 5)
|
|
292
|
-
});
|
|
293
|
-
return action;
|
|
294
|
-
}
|
|
295
|
-
function resolveExecution(config, action, input) {
|
|
296
|
-
const cwd = interpolateString(action.cwd ?? config.cwd, input, "cwd");
|
|
297
|
-
if (cwd && !existsSync(cwd)) throw new CapletsError("CONFIG_INVALID", `CLI cwd does not exist for ${config.server}/${action.name}`);
|
|
298
|
-
const env = {
|
|
299
|
-
...process.env,
|
|
300
|
-
...resolveEnv(config.env, input),
|
|
301
|
-
...resolveEnv(action.env, input)
|
|
302
|
-
};
|
|
303
|
-
return {
|
|
304
|
-
command: interpolateString(action.command, input, "command") ?? action.command,
|
|
305
|
-
args: (action.args ?? []).map((arg, index) => interpolateRequiredString(arg, input, `args.${index}`)),
|
|
306
|
-
...cwd ? { cwd } : {},
|
|
307
|
-
env,
|
|
308
|
-
timeoutMs: action.timeoutMs ?? config.timeoutMs,
|
|
309
|
-
maxOutputBytes: action.maxOutputBytes ?? config.maxOutputBytes
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
function resolveEnv(env, input) {
|
|
313
|
-
if (!env) return {};
|
|
314
|
-
return Object.fromEntries(Object.entries(env).map(([key, value]) => [key, interpolateRequiredString(value, input, `env.${key}`)]));
|
|
315
|
-
}
|
|
316
|
-
function interpolateString(value, input, field) {
|
|
317
|
-
return value === void 0 ? void 0 : interpolateRequiredString(value, input, field);
|
|
318
|
-
}
|
|
319
|
-
function interpolateRequiredString(value, input, field) {
|
|
320
|
-
return value.replace(/\$input(?:\.([A-Za-z0-9_.-]+))?/g, (_match, path) => {
|
|
321
|
-
if (!path) throw new CapletsError("REQUEST_INVALID", `CLI ${field} cannot interpolate $input directly`);
|
|
322
|
-
const selected = valueAtPath$1(input, path);
|
|
323
|
-
if (selected === void 0 || selected === null) throw new CapletsError("REQUEST_INVALID", `CLI ${field} references missing input ${path}`);
|
|
324
|
-
if (typeof selected !== "string" && typeof selected !== "number" && typeof selected !== "boolean") throw new CapletsError("REQUEST_INVALID", `CLI ${field} input ${path} must be a string, number, or boolean`);
|
|
325
|
-
return String(selected);
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
function valueAtPath$1(input, path) {
|
|
329
|
-
let current = input;
|
|
330
|
-
for (const segment of path.split(".")) {
|
|
331
|
-
if (!current || typeof current !== "object" || Array.isArray(current)) return;
|
|
332
|
-
current = current[segment];
|
|
333
|
-
}
|
|
334
|
-
return current;
|
|
335
|
-
}
|
|
336
|
-
function validateInput(action, input) {
|
|
337
|
-
const schema = action.inputSchema;
|
|
338
|
-
if (!schema) return;
|
|
339
|
-
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
340
|
-
for (const key of required) if (typeof key === "string" && (input[key] === void 0 || input[key] === null)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} requires input ${key}`);
|
|
341
|
-
const properties = isPlainObject$7(schema.properties) ? schema.properties : {};
|
|
342
|
-
for (const [key, property] of Object.entries(properties)) {
|
|
343
|
-
if (input[key] === void 0 || !isPlainObject$7(property) || typeof property.type !== "string") continue;
|
|
344
|
-
if (!matchesJsonType(input[key], property.type)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} input ${key} must be ${property.type}`);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
function matchesJsonType(value, type) {
|
|
348
|
-
switch (type) {
|
|
349
|
-
case "string": return typeof value === "string";
|
|
350
|
-
case "number":
|
|
351
|
-
case "integer": return typeof value === "number" && (type === "number" || Number.isInteger(value));
|
|
352
|
-
case "boolean": return typeof value === "boolean";
|
|
353
|
-
case "object": return isPlainObject$7(value);
|
|
354
|
-
case "array": return Array.isArray(value);
|
|
355
|
-
case "null": return value === null;
|
|
356
|
-
default: return true;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function spawnCommand(execution, signal, elapsedMs) {
|
|
360
|
-
return new Promise((resolve, reject) => {
|
|
361
|
-
let stdout = "";
|
|
362
|
-
let stderr = "";
|
|
363
|
-
let outputBytes = 0;
|
|
364
|
-
const child = spawn(execution.command, execution.args, {
|
|
365
|
-
cwd: execution.cwd,
|
|
366
|
-
env: execution.env,
|
|
367
|
-
shell: false,
|
|
368
|
-
signal,
|
|
369
|
-
windowsHide: true
|
|
370
|
-
});
|
|
371
|
-
child.on("error", reject);
|
|
372
|
-
const append = (stream, chunk) => {
|
|
373
|
-
outputBytes += chunk.byteLength;
|
|
374
|
-
if (outputBytes > execution.maxOutputBytes) {
|
|
375
|
-
child.kill();
|
|
376
|
-
reject(new CapletsError("DOWNSTREAM_TOOL_ERROR", "CLI tool output exceeded byte limit"));
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
if (stream === "stdout") stdout += chunk.toString("utf8");
|
|
380
|
-
else stderr += chunk.toString("utf8");
|
|
381
|
-
};
|
|
382
|
-
child.stdout?.on("data", (chunk) => append("stdout", chunk));
|
|
383
|
-
child.stderr?.on("data", (chunk) => append("stderr", chunk));
|
|
384
|
-
child.on("close", (exitCode, childSignal) => {
|
|
385
|
-
resolve({
|
|
386
|
-
exitCode,
|
|
387
|
-
signal: childSignal,
|
|
388
|
-
stdout,
|
|
389
|
-
stderr,
|
|
390
|
-
elapsedMs: elapsedMs()
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
function parseStructuredResult(action, result, tolerateInvalidJson = false) {
|
|
396
|
-
const structured = {
|
|
397
|
-
exitCode: result.exitCode,
|
|
398
|
-
stdout: result.stdout,
|
|
399
|
-
stderr: result.stderr,
|
|
400
|
-
elapsedMs: result.elapsedMs,
|
|
401
|
-
...result.signal ? { signal: result.signal } : {}
|
|
402
|
-
};
|
|
403
|
-
if (action.output?.type === "json" && result.stdout.trim()) try {
|
|
404
|
-
structured.json = JSON.parse(result.stdout);
|
|
405
|
-
} catch (error) {
|
|
406
|
-
if (tolerateInvalidJson) {
|
|
407
|
-
structured.jsonParseError = toSafeError(error);
|
|
408
|
-
return structured;
|
|
409
|
-
}
|
|
410
|
-
throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", `CLI tool ${action.name} stdout was not valid JSON`, toSafeError(error));
|
|
411
|
-
}
|
|
412
|
-
return structured;
|
|
413
|
-
}
|
|
414
|
-
function resolveCommandPath(command) {
|
|
415
|
-
if (isAbsolute(command) || /[\\/]/.test(command)) {
|
|
416
|
-
assertExecutable(command);
|
|
417
|
-
return command;
|
|
418
|
-
}
|
|
419
|
-
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
420
|
-
if (!directory) continue;
|
|
421
|
-
const candidate = join(directory, command);
|
|
422
|
-
if (isExecutable(candidate)) return candidate;
|
|
423
|
-
if (process.platform === "win32") for (const ext of (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")) {
|
|
424
|
-
const windowsCandidate = join(directory, `${command}${ext.toLowerCase()}`);
|
|
425
|
-
if (isExecutable(windowsCandidate)) return windowsCandidate;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
throw new CapletsError("SERVER_UNAVAILABLE", `CLI command ${command} was not found on PATH`);
|
|
429
|
-
}
|
|
430
|
-
function assertExecutable(path) {
|
|
431
|
-
if (!isExecutable(path)) throw new CapletsError("SERVER_UNAVAILABLE", `CLI command ${path} is not executable`);
|
|
432
|
-
}
|
|
433
|
-
function isExecutable(path) {
|
|
434
|
-
try {
|
|
435
|
-
accessSync(path, constants.X_OK);
|
|
436
|
-
return true;
|
|
437
|
-
} catch {
|
|
438
|
-
return false;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
function isAbortError$1(error) {
|
|
442
|
-
return error instanceof Error && error.name === "AbortError";
|
|
443
|
-
}
|
|
444
|
-
function isPlainObject$7(value) {
|
|
445
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
446
|
-
}
|
|
30
|
+
//#region ../core/dist/generated-tool-input-schema-B6rce396.js
|
|
447
31
|
var _a$1;
|
|
448
32
|
/** A special constant with type `never` */
|
|
449
33
|
const NEVER = /* @__PURE__ */ Object.freeze({ status: "aborted" });
|
|
@@ -587,7 +171,7 @@ const allowsEval = /* @__PURE__ */ cached(() => {
|
|
|
587
171
|
return false;
|
|
588
172
|
}
|
|
589
173
|
});
|
|
590
|
-
function isPlainObject$
|
|
174
|
+
function isPlainObject$8(o) {
|
|
591
175
|
if (isObject(o) === false) return false;
|
|
592
176
|
const ctor = o.constructor;
|
|
593
177
|
if (ctor === void 0) return true;
|
|
@@ -598,7 +182,7 @@ function isPlainObject$6(o) {
|
|
|
598
182
|
return true;
|
|
599
183
|
}
|
|
600
184
|
function shallowClone(o) {
|
|
601
|
-
if (isPlainObject$
|
|
185
|
+
if (isPlainObject$8(o)) return { ...o };
|
|
602
186
|
if (Array.isArray(o)) return [...o];
|
|
603
187
|
if (o instanceof Map) return new Map(o);
|
|
604
188
|
if (o instanceof Set) return new Set(o);
|
|
@@ -681,7 +265,7 @@ function omit(schema, mask) {
|
|
|
681
265
|
}));
|
|
682
266
|
}
|
|
683
267
|
function extend(schema, shape) {
|
|
684
|
-
if (!isPlainObject$
|
|
268
|
+
if (!isPlainObject$8(shape)) throw new Error("Invalid input to extend: expected a plain object");
|
|
685
269
|
const checks = schema._zod.def.checks;
|
|
686
270
|
if (checks && checks.length > 0) {
|
|
687
271
|
const existingShape = schema._zod.def.shape;
|
|
@@ -697,7 +281,7 @@ function extend(schema, shape) {
|
|
|
697
281
|
} }));
|
|
698
282
|
}
|
|
699
283
|
function safeExtend(schema, shape) {
|
|
700
|
-
if (!isPlainObject$
|
|
284
|
+
if (!isPlainObject$8(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
701
285
|
return clone(schema, mergeDefs(schema._zod.def, { get shape() {
|
|
702
286
|
const _shape = {
|
|
703
287
|
...schema._zod.def.shape,
|
|
@@ -888,7 +472,7 @@ const _parse = (_Err) => (schema, value, _ctx, _params) => {
|
|
|
888
472
|
}
|
|
889
473
|
return result.value;
|
|
890
474
|
};
|
|
891
|
-
const parse$
|
|
475
|
+
const parse$1 = /* @__PURE__ */ _parse($ZodRealError);
|
|
892
476
|
const _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
|
|
893
477
|
const ctx = _ctx ? {
|
|
894
478
|
..._ctx,
|
|
@@ -925,7 +509,7 @@ const _safeParse = (_Err) => (schema, value, _ctx) => {
|
|
|
925
509
|
data: result.value
|
|
926
510
|
};
|
|
927
511
|
};
|
|
928
|
-
const safeParse$
|
|
512
|
+
const safeParse$1 = /* @__PURE__ */ _safeParse($ZodRealError);
|
|
929
513
|
const _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
|
|
930
514
|
const ctx = _ctx ? {
|
|
931
515
|
..._ctx,
|
|
@@ -944,7 +528,7 @@ const _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
|
|
|
944
528
|
data: result.value
|
|
945
529
|
};
|
|
946
530
|
};
|
|
947
|
-
const safeParseAsync$
|
|
531
|
+
const safeParseAsync$1 = /* @__PURE__ */ _safeParseAsync($ZodRealError);
|
|
948
532
|
const _encode = (_Err) => (schema, value, _ctx) => {
|
|
949
533
|
const ctx = _ctx ? {
|
|
950
534
|
..._ctx,
|
|
@@ -1043,7 +627,7 @@ const string$1 = (params) => {
|
|
|
1043
627
|
return new RegExp(`^${regex}$`);
|
|
1044
628
|
};
|
|
1045
629
|
const integer = /^-?\d+$/;
|
|
1046
|
-
const number$
|
|
630
|
+
const number$1 = /^-?\d+(?:\.\d+)?$/;
|
|
1047
631
|
const boolean$1 = /^(?:true|false)$/i;
|
|
1048
632
|
const _null$2 = /^null$/i;
|
|
1049
633
|
const lowercase = /^[^A-Z]*$/;
|
|
@@ -1517,10 +1101,10 @@ const $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
|
|
|
1517
1101
|
defineLazy(inst, "~standard", () => ({
|
|
1518
1102
|
validate: (value) => {
|
|
1519
1103
|
try {
|
|
1520
|
-
const r = safeParse$
|
|
1104
|
+
const r = safeParse$1(inst, value);
|
|
1521
1105
|
return r.success ? { value: r.data } : { issues: r.error?.issues };
|
|
1522
1106
|
} catch (_) {
|
|
1523
|
-
return safeParseAsync$
|
|
1107
|
+
return safeParseAsync$1(inst, value).then((r) => r.success ? { value: r.data } : { issues: r.error?.issues });
|
|
1524
1108
|
}
|
|
1525
1109
|
},
|
|
1526
1110
|
vendor: "zod",
|
|
@@ -1810,7 +1394,7 @@ const $ZodJWT = /* @__PURE__ */ $constructor("$ZodJWT", (inst, def) => {
|
|
|
1810
1394
|
});
|
|
1811
1395
|
const $ZodNumber = /* @__PURE__ */ $constructor("$ZodNumber", (inst, def) => {
|
|
1812
1396
|
$ZodType.init(inst, def);
|
|
1813
|
-
inst._zod.pattern = inst._zod.bag.pattern ?? number$
|
|
1397
|
+
inst._zod.pattern = inst._zod.bag.pattern ?? number$1;
|
|
1814
1398
|
inst._zod.parse = (payload, _ctx) => {
|
|
1815
1399
|
if (def.coerce) try {
|
|
1816
1400
|
payload.value = Number(payload.value);
|
|
@@ -2297,7 +1881,7 @@ function mergeValues$1(a, b) {
|
|
|
2297
1881
|
valid: true,
|
|
2298
1882
|
data: a
|
|
2299
1883
|
};
|
|
2300
|
-
if (isPlainObject$
|
|
1884
|
+
if (isPlainObject$8(a) && isPlainObject$8(b)) {
|
|
2301
1885
|
const bKeys = Object.keys(b);
|
|
2302
1886
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
2303
1887
|
const newObj = {
|
|
@@ -2373,7 +1957,7 @@ const $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
2373
1957
|
$ZodType.init(inst, def);
|
|
2374
1958
|
inst._zod.parse = (payload, ctx) => {
|
|
2375
1959
|
const input = payload.value;
|
|
2376
|
-
if (!isPlainObject$
|
|
1960
|
+
if (!isPlainObject$8(input)) {
|
|
2377
1961
|
payload.issues.push({
|
|
2378
1962
|
expected: "record",
|
|
2379
1963
|
code: "invalid_type",
|
|
@@ -2440,7 +2024,7 @@ const $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
2440
2024
|
issues: []
|
|
2441
2025
|
}, ctx);
|
|
2442
2026
|
if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
|
|
2443
|
-
if (typeof key === "string" && number$
|
|
2027
|
+
if (typeof key === "string" && number$1.test(key) && keyResult.issues.length) {
|
|
2444
2028
|
const retryResult = def.keyType._zod.run({
|
|
2445
2029
|
value: Number(key),
|
|
2446
2030
|
issues: []
|
|
@@ -4115,8 +3699,8 @@ const initializer = (inst, issues) => {
|
|
|
4115
3699
|
const ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer, { Parent: Error });
|
|
4116
3700
|
const parse$2 = /* @__PURE__ */ _parse(ZodRealError);
|
|
4117
3701
|
const parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
4118
|
-
const safeParse$
|
|
4119
|
-
const safeParseAsync$
|
|
3702
|
+
const safeParse$2 = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
3703
|
+
const safeParseAsync$2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
4120
3704
|
const encode = /* @__PURE__ */ _encode(ZodRealError);
|
|
4121
3705
|
const decode = /* @__PURE__ */ _decode(ZodRealError);
|
|
4122
3706
|
const encodeAsync = /* @__PURE__ */ _encodeAsync(ZodRealError);
|
|
@@ -4172,9 +3756,9 @@ const ZodType$1 = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
|
|
4172
3756
|
inst.type = def.type;
|
|
4173
3757
|
Object.defineProperty(inst, "_def", { value: def });
|
|
4174
3758
|
inst.parse = (data, params) => parse$2(inst, data, params, { callee: inst.parse });
|
|
4175
|
-
inst.safeParse = (data, params) => safeParse$
|
|
3759
|
+
inst.safeParse = (data, params) => safeParse$2(inst, data, params);
|
|
4176
3760
|
inst.parseAsync = async (data, params) => parseAsync(inst, data, params, { callee: inst.parseAsync });
|
|
4177
|
-
inst.safeParseAsync = async (data, params) => safeParseAsync$
|
|
3761
|
+
inst.safeParseAsync = async (data, params) => safeParseAsync$2(inst, data, params);
|
|
4178
3762
|
inst.spa = inst.safeParseAsync;
|
|
4179
3763
|
inst.encode = (data, params) => encode(inst, data, params);
|
|
4180
3764
|
inst.decode = (data, params) => decode(inst, data, params);
|
|
@@ -4523,7 +4107,7 @@ const ZodNumber$1 = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => {
|
|
|
4523
4107
|
inst.isFinite = true;
|
|
4524
4108
|
inst.format = bag.format ?? null;
|
|
4525
4109
|
});
|
|
4526
|
-
function number$
|
|
4110
|
+
function number$2(params) {
|
|
4527
4111
|
return /* @__PURE__ */ _number(ZodNumber$1, params);
|
|
4528
4112
|
}
|
|
4529
4113
|
const ZodNumberFormat = /* @__PURE__ */ $constructor("ZodNumberFormat", (inst, def) => {
|
|
@@ -4969,6 +4553,571 @@ function preprocess(fn, schema) {
|
|
|
4969
4553
|
out: schema
|
|
4970
4554
|
});
|
|
4971
4555
|
}
|
|
4556
|
+
const operations = [
|
|
4557
|
+
"get_caplet",
|
|
4558
|
+
"check_backend",
|
|
4559
|
+
"list_tools",
|
|
4560
|
+
"search_tools",
|
|
4561
|
+
"get_tool",
|
|
4562
|
+
"call_tool"
|
|
4563
|
+
];
|
|
4564
|
+
const mcpOperations = [
|
|
4565
|
+
...operations,
|
|
4566
|
+
"list_resources",
|
|
4567
|
+
"search_resources",
|
|
4568
|
+
"list_resource_templates",
|
|
4569
|
+
"read_resource",
|
|
4570
|
+
"list_prompts",
|
|
4571
|
+
"search_prompts",
|
|
4572
|
+
"get_prompt",
|
|
4573
|
+
"complete"
|
|
4574
|
+
];
|
|
4575
|
+
const generatedToolInputDescriptions = {
|
|
4576
|
+
operation: "Wrapper operation: get_caplet, check_backend, list_tools, search_tools, get_tool, call_tool. MCP Caplets also expose resources, prompts, and completions.",
|
|
4577
|
+
query: "Required for search operations only.",
|
|
4578
|
+
limit: "Optional list/search result limit.",
|
|
4579
|
+
tool: "Exact downstream tool name for get_tool or call_tool.",
|
|
4580
|
+
arguments: "JSON object for call_tool arguments/downstream inputs or get_prompt arguments.",
|
|
4581
|
+
fields: "Optional call_tool structured output paths when outputSchema allows it.",
|
|
4582
|
+
uri: "Exact downstream resource URI for read_resource.",
|
|
4583
|
+
prompt: "Exact downstream prompt name for get_prompt.",
|
|
4584
|
+
ref: "Completion target reference for complete.",
|
|
4585
|
+
argument: "Completion argument object for complete."
|
|
4586
|
+
};
|
|
4587
|
+
const completionRefSchema = union([object$1({
|
|
4588
|
+
type: literal("prompt"),
|
|
4589
|
+
name: string().min(1)
|
|
4590
|
+
}).strict(), object$1({
|
|
4591
|
+
type: literal("resourceTemplate"),
|
|
4592
|
+
uri: string().min(1)
|
|
4593
|
+
}).strict()]);
|
|
4594
|
+
const completionArgumentSchema = object$1({
|
|
4595
|
+
name: string().min(1),
|
|
4596
|
+
value: string()
|
|
4597
|
+
}).strict();
|
|
4598
|
+
const baseShape = {
|
|
4599
|
+
query: string().optional().describe(generatedToolInputDescriptions.query),
|
|
4600
|
+
limit: number$2().int().positive().optional().describe(generatedToolInputDescriptions.limit),
|
|
4601
|
+
tool: string().optional().describe(generatedToolInputDescriptions.tool),
|
|
4602
|
+
arguments: object$1({}).catchall(any()).optional().describe(generatedToolInputDescriptions.arguments),
|
|
4603
|
+
fields: array(string().min(1)).min(1).optional().describe(generatedToolInputDescriptions.fields)
|
|
4604
|
+
};
|
|
4605
|
+
function generatedToolInputSchemaForCaplet(caplet) {
|
|
4606
|
+
return object$1({
|
|
4607
|
+
operation: (caplet.backend === "mcp" ? _enum(mcpOperations) : _enum(operations)).describe(generatedToolInputDescriptions.operation),
|
|
4608
|
+
...baseShape,
|
|
4609
|
+
...caplet.backend === "mcp" ? {
|
|
4610
|
+
uri: string().optional().describe(generatedToolInputDescriptions.uri),
|
|
4611
|
+
prompt: string().optional().describe(generatedToolInputDescriptions.prompt),
|
|
4612
|
+
ref: completionRefSchema.optional().describe(generatedToolInputDescriptions.ref),
|
|
4613
|
+
argument: completionArgumentSchema.optional().describe(generatedToolInputDescriptions.argument)
|
|
4614
|
+
} : {}
|
|
4615
|
+
}).strict();
|
|
4616
|
+
}
|
|
4617
|
+
object$1({
|
|
4618
|
+
operation: _enum(operations).describe(generatedToolInputDescriptions.operation),
|
|
4619
|
+
...baseShape
|
|
4620
|
+
}).strict();
|
|
4621
|
+
function generatedToolInputJsonSchemaForCaplet(caplet) {
|
|
4622
|
+
const mcp = caplet.backend === "mcp";
|
|
4623
|
+
return {
|
|
4624
|
+
type: "object",
|
|
4625
|
+
properties: {
|
|
4626
|
+
operation: {
|
|
4627
|
+
type: "string",
|
|
4628
|
+
enum: mcp ? mcpOperations : operations,
|
|
4629
|
+
description: generatedToolInputDescriptions.operation
|
|
4630
|
+
},
|
|
4631
|
+
query: {
|
|
4632
|
+
type: "string",
|
|
4633
|
+
description: generatedToolInputDescriptions.query
|
|
4634
|
+
},
|
|
4635
|
+
limit: {
|
|
4636
|
+
type: "integer",
|
|
4637
|
+
minimum: 1,
|
|
4638
|
+
description: generatedToolInputDescriptions.limit
|
|
4639
|
+
},
|
|
4640
|
+
tool: {
|
|
4641
|
+
type: "string",
|
|
4642
|
+
description: generatedToolInputDescriptions.tool
|
|
4643
|
+
},
|
|
4644
|
+
arguments: {
|
|
4645
|
+
type: "object",
|
|
4646
|
+
description: generatedToolInputDescriptions.arguments
|
|
4647
|
+
},
|
|
4648
|
+
fields: {
|
|
4649
|
+
type: "array",
|
|
4650
|
+
items: {
|
|
4651
|
+
type: "string",
|
|
4652
|
+
minLength: 1
|
|
4653
|
+
},
|
|
4654
|
+
minItems: 1,
|
|
4655
|
+
description: generatedToolInputDescriptions.fields
|
|
4656
|
+
},
|
|
4657
|
+
...mcp ? {
|
|
4658
|
+
uri: {
|
|
4659
|
+
type: "string",
|
|
4660
|
+
description: generatedToolInputDescriptions.uri
|
|
4661
|
+
},
|
|
4662
|
+
prompt: {
|
|
4663
|
+
type: "string",
|
|
4664
|
+
description: generatedToolInputDescriptions.prompt
|
|
4665
|
+
},
|
|
4666
|
+
ref: {
|
|
4667
|
+
oneOf: [{
|
|
4668
|
+
type: "object",
|
|
4669
|
+
properties: {
|
|
4670
|
+
type: { const: "prompt" },
|
|
4671
|
+
name: {
|
|
4672
|
+
type: "string",
|
|
4673
|
+
minLength: 1
|
|
4674
|
+
}
|
|
4675
|
+
},
|
|
4676
|
+
required: ["type", "name"],
|
|
4677
|
+
additionalProperties: false
|
|
4678
|
+
}, {
|
|
4679
|
+
type: "object",
|
|
4680
|
+
properties: {
|
|
4681
|
+
type: { const: "resourceTemplate" },
|
|
4682
|
+
uri: {
|
|
4683
|
+
type: "string",
|
|
4684
|
+
minLength: 1
|
|
4685
|
+
}
|
|
4686
|
+
},
|
|
4687
|
+
required: ["type", "uri"],
|
|
4688
|
+
additionalProperties: false
|
|
4689
|
+
}],
|
|
4690
|
+
description: generatedToolInputDescriptions.ref
|
|
4691
|
+
},
|
|
4692
|
+
argument: {
|
|
4693
|
+
type: "object",
|
|
4694
|
+
properties: {
|
|
4695
|
+
name: {
|
|
4696
|
+
type: "string",
|
|
4697
|
+
minLength: 1
|
|
4698
|
+
},
|
|
4699
|
+
value: { type: "string" }
|
|
4700
|
+
},
|
|
4701
|
+
required: ["name", "value"],
|
|
4702
|
+
additionalProperties: false,
|
|
4703
|
+
description: generatedToolInputDescriptions.argument
|
|
4704
|
+
}
|
|
4705
|
+
} : {}
|
|
4706
|
+
},
|
|
4707
|
+
required: ["operation"],
|
|
4708
|
+
additionalProperties: false
|
|
4709
|
+
};
|
|
4710
|
+
}
|
|
4711
|
+
function generatedToolInputJsonSchema() {
|
|
4712
|
+
return generatedToolInputJsonSchemaForCaplet({ backend: "tool" });
|
|
4713
|
+
}
|
|
4714
|
+
//#endregion
|
|
4715
|
+
//#region ../core/dist/options-DM1cMRcp.js
|
|
4716
|
+
var __create = Object.create;
|
|
4717
|
+
var __defProp = Object.defineProperty;
|
|
4718
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4719
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4720
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4721
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4722
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
4723
|
+
var __exportAll = (all, no_symbols) => {
|
|
4724
|
+
let target = {};
|
|
4725
|
+
for (var name in all) __defProp(target, name, {
|
|
4726
|
+
get: all[name],
|
|
4727
|
+
enumerable: true
|
|
4728
|
+
});
|
|
4729
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
4730
|
+
return target;
|
|
4731
|
+
};
|
|
4732
|
+
var __copyProps = (to, from, except, desc) => {
|
|
4733
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
4734
|
+
key = keys[i];
|
|
4735
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
4736
|
+
get: ((k) => from[k]).bind(null, key),
|
|
4737
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
4738
|
+
});
|
|
4739
|
+
}
|
|
4740
|
+
return to;
|
|
4741
|
+
};
|
|
4742
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
4743
|
+
value: mod,
|
|
4744
|
+
enumerable: true
|
|
4745
|
+
}) : target, mod));
|
|
4746
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4747
|
+
const CAPLETS_ERROR_CODES = [
|
|
4748
|
+
"CONFIG_NOT_FOUND",
|
|
4749
|
+
"CONFIG_EXISTS",
|
|
4750
|
+
"CONFIG_INVALID",
|
|
4751
|
+
"REQUEST_INVALID",
|
|
4752
|
+
"SERVER_NOT_FOUND",
|
|
4753
|
+
"SERVER_UNAVAILABLE",
|
|
4754
|
+
"SERVER_START_TIMEOUT",
|
|
4755
|
+
"UNKNOWN_OPERATION",
|
|
4756
|
+
"TOOL_NOT_FOUND",
|
|
4757
|
+
"TOOL_CALL_TIMEOUT",
|
|
4758
|
+
"AUTH_REQUIRED",
|
|
4759
|
+
"AUTH_FAILED",
|
|
4760
|
+
"AUTH_REFRESH_FAILED",
|
|
4761
|
+
"DOWNSTREAM_PROTOCOL_ERROR",
|
|
4762
|
+
"DOWNSTREAM_TOOL_ERROR",
|
|
4763
|
+
"UNSUPPORTED_OPERATION",
|
|
4764
|
+
"UNSUPPORTED_CAPABILITY",
|
|
4765
|
+
"PROMPT_NOT_FOUND",
|
|
4766
|
+
"DOWNSTREAM_RESOURCE_ERROR",
|
|
4767
|
+
"DOWNSTREAM_PROMPT_ERROR",
|
|
4768
|
+
"DOWNSTREAM_COMPLETION_ERROR",
|
|
4769
|
+
"UNSUPPORTED_TRANSPORT",
|
|
4770
|
+
"INTERNAL_ERROR"
|
|
4771
|
+
];
|
|
4772
|
+
var CapletsError = class extends Error {
|
|
4773
|
+
code;
|
|
4774
|
+
details;
|
|
4775
|
+
constructor(code, message, details) {
|
|
4776
|
+
super(message);
|
|
4777
|
+
this.name = "CapletsError";
|
|
4778
|
+
this.code = code;
|
|
4779
|
+
this.details = details;
|
|
4780
|
+
}
|
|
4781
|
+
};
|
|
4782
|
+
const SECRET_KEY_PATTERN = /(token|secret|authorization|auth|api[-_]?key|password|credential|clientsecret|client_secret|code|refresh)/i;
|
|
4783
|
+
const SECRET_VALUE_PATTERN = /(bearer\s+)[a-z0-9._~+/=-]+|([?&](?:access_token|refresh_token|token|code)=)[^&\s]+/gi;
|
|
4784
|
+
function redactSecrets(value) {
|
|
4785
|
+
if (typeof value === "string") return value.replace(SECRET_VALUE_PATTERN, "$1$2[REDACTED]");
|
|
4786
|
+
if (Array.isArray(value)) return value.map((item) => redactSecrets(item));
|
|
4787
|
+
if (value && typeof value === "object") {
|
|
4788
|
+
const redacted = {};
|
|
4789
|
+
for (const [key, nested] of Object.entries(value)) redacted[key] = SECRET_KEY_PATTERN.test(key) ? "[REDACTED]" : redactSecrets(nested);
|
|
4790
|
+
return redacted;
|
|
4791
|
+
}
|
|
4792
|
+
return value;
|
|
4793
|
+
}
|
|
4794
|
+
function toSafeError(error, fallback = "INTERNAL_ERROR") {
|
|
4795
|
+
if (error instanceof CapletsError) return {
|
|
4796
|
+
code: error.code,
|
|
4797
|
+
message: String(redactSecrets(error.message)),
|
|
4798
|
+
...error.details === void 0 ? {} : { details: redactSecrets(error.details) }
|
|
4799
|
+
};
|
|
4800
|
+
if (error instanceof Error) return {
|
|
4801
|
+
code: fallback,
|
|
4802
|
+
message: String(redactSecrets(error.message))
|
|
4803
|
+
};
|
|
4804
|
+
return {
|
|
4805
|
+
code: fallback,
|
|
4806
|
+
message: String(redactSecrets(error))
|
|
4807
|
+
};
|
|
4808
|
+
}
|
|
4809
|
+
function errorResult(error, fallback) {
|
|
4810
|
+
const safe = toSafeError(error, fallback);
|
|
4811
|
+
return {
|
|
4812
|
+
isError: true,
|
|
4813
|
+
content: [{
|
|
4814
|
+
type: "text",
|
|
4815
|
+
text: `${safe.code}: ${safe.message}`
|
|
4816
|
+
}],
|
|
4817
|
+
structuredContent: { error: safe }
|
|
4818
|
+
};
|
|
4819
|
+
}
|
|
4820
|
+
function textContent(text) {
|
|
4821
|
+
return text ? [{
|
|
4822
|
+
type: "text",
|
|
4823
|
+
text
|
|
4824
|
+
}] : [];
|
|
4825
|
+
}
|
|
4826
|
+
function compactJsonText(value, maxLength = 600) {
|
|
4827
|
+
return compactText(JSON.stringify(value) ?? String(value), maxLength);
|
|
4828
|
+
}
|
|
4829
|
+
function compactText(value, maxLength = 600) {
|
|
4830
|
+
const collapsed = value.replace(/\s+/gu, " ").trim();
|
|
4831
|
+
return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 1).trimEnd()}…` : collapsed;
|
|
4832
|
+
}
|
|
4833
|
+
function resultKeys(value) {
|
|
4834
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return "scalar result";
|
|
4835
|
+
const keys = Object.keys(value).filter((key) => key !== "elapsedMs");
|
|
4836
|
+
return keys.length > 0 ? `structured keys: ${keys.join(", ")}` : "empty structured result";
|
|
4837
|
+
}
|
|
4838
|
+
function statusSummary(value) {
|
|
4839
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return compactJsonText(value);
|
|
4840
|
+
const record = value;
|
|
4841
|
+
return [
|
|
4842
|
+
typeof record.status === "number" ? `status ${record.status}` : void 0,
|
|
4843
|
+
typeof record.statusText === "string" && record.statusText ? record.statusText : void 0,
|
|
4844
|
+
typeof record.exitCode === "number" ? `exit ${record.exitCode}` : void 0,
|
|
4845
|
+
"body" in record ? "body" : void 0,
|
|
4846
|
+
"json" in record ? "json" : void 0,
|
|
4847
|
+
typeof record.stdout === "string" && record.stdout ? "stdout" : void 0,
|
|
4848
|
+
typeof record.stderr === "string" && record.stderr ? "stderr" : void 0
|
|
4849
|
+
].filter((part) => Boolean(part)).join("; ") || resultKeys(record);
|
|
4850
|
+
}
|
|
4851
|
+
function compactStructuredContent(value) {
|
|
4852
|
+
return textContent(statusSummary(value));
|
|
4853
|
+
}
|
|
4854
|
+
function searchToolList(tools, query, limit, compact) {
|
|
4855
|
+
const tokens = query.toLocaleLowerCase().split(/\s+/).filter(Boolean);
|
|
4856
|
+
return tools.filter((tool) => {
|
|
4857
|
+
const haystack = `${tool.name}\n${tool.description ?? ""}`.toLocaleLowerCase();
|
|
4858
|
+
return tokens.some((token) => haystack.includes(token));
|
|
4859
|
+
}).sort((left, right) => left.name.localeCompare(right.name)).slice(0, limit).map(compact);
|
|
4860
|
+
}
|
|
4861
|
+
const DEFAULT_INPUT_SCHEMA$1 = {
|
|
4862
|
+
type: "object",
|
|
4863
|
+
additionalProperties: true
|
|
4864
|
+
};
|
|
4865
|
+
var CliToolsManager = class {
|
|
4866
|
+
registry;
|
|
4867
|
+
constructor(registry) {
|
|
4868
|
+
this.registry = registry;
|
|
4869
|
+
}
|
|
4870
|
+
updateRegistry(registry) {
|
|
4871
|
+
this.registry = registry;
|
|
4872
|
+
}
|
|
4873
|
+
invalidate(_serverId) {}
|
|
4874
|
+
async checkTools(config) {
|
|
4875
|
+
const startedAt = Date.now();
|
|
4876
|
+
try {
|
|
4877
|
+
for (const action of actionsFor(config)) {
|
|
4878
|
+
const cwdTemplate = action.cwd ?? config.cwd;
|
|
4879
|
+
if (cwdTemplate && !cwdTemplate.includes("$input")) {
|
|
4880
|
+
if (!existsSync(interpolateRequiredString(cwdTemplate, {}, "cwd"))) throw new CapletsError("CONFIG_INVALID", `CLI cwd does not exist for ${config.server}/${action.name}`);
|
|
4881
|
+
}
|
|
4882
|
+
if (!action.command.includes("$input")) resolveCommandPath(action.command);
|
|
4883
|
+
}
|
|
4884
|
+
this.registry.setStatus(config.server, "available");
|
|
4885
|
+
return {
|
|
4886
|
+
id: config.server,
|
|
4887
|
+
status: "available",
|
|
4888
|
+
toolCount: Object.keys(config.actions).length,
|
|
4889
|
+
elapsedMs: Date.now() - startedAt
|
|
4890
|
+
};
|
|
4891
|
+
} catch (error) {
|
|
4892
|
+
const safe = toSafeError(error, "SERVER_UNAVAILABLE");
|
|
4893
|
+
this.registry.setStatus(config.server, "unavailable", safe);
|
|
4894
|
+
return {
|
|
4895
|
+
id: config.server,
|
|
4896
|
+
status: "unavailable",
|
|
4897
|
+
elapsedMs: Date.now() - startedAt,
|
|
4898
|
+
error: safe
|
|
4899
|
+
};
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
async listTools(config) {
|
|
4903
|
+
return actionsFor(config).map((action) => this.toTool(action));
|
|
4904
|
+
}
|
|
4905
|
+
async getTool(config, toolName) {
|
|
4906
|
+
return this.toTool(getAction(config, toolName));
|
|
4907
|
+
}
|
|
4908
|
+
async callTool(config, toolName, args) {
|
|
4909
|
+
const action = getAction(config, toolName);
|
|
4910
|
+
validateInput(action, args);
|
|
4911
|
+
const execution = resolveExecution(config, action, args);
|
|
4912
|
+
const startedAt = Date.now();
|
|
4913
|
+
const controller = new AbortController();
|
|
4914
|
+
const timeout = setTimeout(() => controller.abort(), execution.timeoutMs);
|
|
4915
|
+
try {
|
|
4916
|
+
const result = await spawnCommand(execution, controller.signal, () => Date.now() - startedAt);
|
|
4917
|
+
const structured = parseStructuredResult(action, result, result.exitCode !== 0);
|
|
4918
|
+
return {
|
|
4919
|
+
content: compactStructuredContent(structured),
|
|
4920
|
+
structuredContent: structured,
|
|
4921
|
+
isError: result.exitCode !== 0
|
|
4922
|
+
};
|
|
4923
|
+
} catch (error) {
|
|
4924
|
+
if (isAbortError$1(error)) throw new CapletsError("TOOL_CALL_TIMEOUT", `CLI tool timed out for ${config.server}/${toolName}`);
|
|
4925
|
+
if (error instanceof CapletsError) throw error;
|
|
4926
|
+
throw new CapletsError("DOWNSTREAM_TOOL_ERROR", `CLI tool failed for ${config.server}/${toolName}`, toSafeError(error));
|
|
4927
|
+
} finally {
|
|
4928
|
+
clearTimeout(timeout);
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
compact(config, tool) {
|
|
4932
|
+
return {
|
|
4933
|
+
id: config.server,
|
|
4934
|
+
tool: tool.name,
|
|
4935
|
+
...tool.description ? { description: tool.description } : {},
|
|
4936
|
+
hasInputSchema: Boolean(tool.inputSchema),
|
|
4937
|
+
hasOutputSchema: Boolean(tool.outputSchema)
|
|
4938
|
+
};
|
|
4939
|
+
}
|
|
4940
|
+
search(config, tools, query, limit) {
|
|
4941
|
+
return searchToolList(tools, query, limit, (tool) => this.compact(config, tool));
|
|
4942
|
+
}
|
|
4943
|
+
toTool(action) {
|
|
4944
|
+
return {
|
|
4945
|
+
name: action.name,
|
|
4946
|
+
...action.description ? { description: action.description } : {},
|
|
4947
|
+
inputSchema: action.inputSchema ?? DEFAULT_INPUT_SCHEMA$1,
|
|
4948
|
+
...action.outputSchema ? { outputSchema: action.outputSchema } : {},
|
|
4949
|
+
...action.annotations ? { annotations: action.annotations } : {}
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
};
|
|
4953
|
+
function actionsFor(config) {
|
|
4954
|
+
return Object.entries(config.actions).map(([name, action]) => ({
|
|
4955
|
+
name,
|
|
4956
|
+
...action
|
|
4957
|
+
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
4958
|
+
}
|
|
4959
|
+
function getAction(config, toolName) {
|
|
4960
|
+
const actions = actionsFor(config);
|
|
4961
|
+
const action = actions.find((candidate) => candidate.name === toolName);
|
|
4962
|
+
if (!action) throw new CapletsError("TOOL_NOT_FOUND", `Tool ${toolName} was not found on ${config.server}`, {
|
|
4963
|
+
server: config.server,
|
|
4964
|
+
tool: toolName,
|
|
4965
|
+
suggestions: actions.map((candidate) => candidate.name).filter((name) => name.toLocaleLowerCase().includes(toolName.toLocaleLowerCase()[0] ?? "")).slice(0, 5)
|
|
4966
|
+
});
|
|
4967
|
+
return action;
|
|
4968
|
+
}
|
|
4969
|
+
function resolveExecution(config, action, input) {
|
|
4970
|
+
const cwd = interpolateString(action.cwd ?? config.cwd, input, "cwd");
|
|
4971
|
+
if (cwd && !existsSync(cwd)) throw new CapletsError("CONFIG_INVALID", `CLI cwd does not exist for ${config.server}/${action.name}`);
|
|
4972
|
+
const env = {
|
|
4973
|
+
...process.env,
|
|
4974
|
+
...resolveEnv(config.env, input),
|
|
4975
|
+
...resolveEnv(action.env, input)
|
|
4976
|
+
};
|
|
4977
|
+
return {
|
|
4978
|
+
command: interpolateString(action.command, input, "command") ?? action.command,
|
|
4979
|
+
args: (action.args ?? []).map((arg, index) => interpolateRequiredString(arg, input, `args.${index}`)),
|
|
4980
|
+
...cwd ? { cwd } : {},
|
|
4981
|
+
env,
|
|
4982
|
+
timeoutMs: action.timeoutMs ?? config.timeoutMs,
|
|
4983
|
+
maxOutputBytes: action.maxOutputBytes ?? config.maxOutputBytes
|
|
4984
|
+
};
|
|
4985
|
+
}
|
|
4986
|
+
function resolveEnv(env, input) {
|
|
4987
|
+
if (!env) return {};
|
|
4988
|
+
return Object.fromEntries(Object.entries(env).map(([key, value]) => [key, interpolateRequiredString(value, input, `env.${key}`)]));
|
|
4989
|
+
}
|
|
4990
|
+
function interpolateString(value, input, field) {
|
|
4991
|
+
return value === void 0 ? void 0 : interpolateRequiredString(value, input, field);
|
|
4992
|
+
}
|
|
4993
|
+
function interpolateRequiredString(value, input, field) {
|
|
4994
|
+
return value.replace(/\$input(?:\.([A-Za-z0-9_.-]+))?/g, (_match, path) => {
|
|
4995
|
+
if (!path) throw new CapletsError("REQUEST_INVALID", `CLI ${field} cannot interpolate $input directly`);
|
|
4996
|
+
const selected = valueAtPath$1(input, path);
|
|
4997
|
+
if (selected === void 0 || selected === null) throw new CapletsError("REQUEST_INVALID", `CLI ${field} references missing input ${path}`);
|
|
4998
|
+
if (typeof selected !== "string" && typeof selected !== "number" && typeof selected !== "boolean") throw new CapletsError("REQUEST_INVALID", `CLI ${field} input ${path} must be a string, number, or boolean`);
|
|
4999
|
+
return String(selected);
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
5002
|
+
function valueAtPath$1(input, path) {
|
|
5003
|
+
let current = input;
|
|
5004
|
+
for (const segment of path.split(".")) {
|
|
5005
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) return;
|
|
5006
|
+
current = current[segment];
|
|
5007
|
+
}
|
|
5008
|
+
return current;
|
|
5009
|
+
}
|
|
5010
|
+
function validateInput(action, input) {
|
|
5011
|
+
const schema = action.inputSchema;
|
|
5012
|
+
if (!schema) return;
|
|
5013
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
5014
|
+
for (const key of required) if (typeof key === "string" && (input[key] === void 0 || input[key] === null)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} requires input ${key}`);
|
|
5015
|
+
const properties = isPlainObject$6(schema.properties) ? schema.properties : {};
|
|
5016
|
+
for (const [key, property] of Object.entries(properties)) {
|
|
5017
|
+
if (input[key] === void 0 || !isPlainObject$6(property) || typeof property.type !== "string") continue;
|
|
5018
|
+
if (!matchesJsonType(input[key], property.type)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} input ${key} must be ${property.type}`);
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
function matchesJsonType(value, type) {
|
|
5022
|
+
switch (type) {
|
|
5023
|
+
case "string": return typeof value === "string";
|
|
5024
|
+
case "number":
|
|
5025
|
+
case "integer": return typeof value === "number" && (type === "number" || Number.isInteger(value));
|
|
5026
|
+
case "boolean": return typeof value === "boolean";
|
|
5027
|
+
case "object": return isPlainObject$6(value);
|
|
5028
|
+
case "array": return Array.isArray(value);
|
|
5029
|
+
case "null": return value === null;
|
|
5030
|
+
default: return true;
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
function spawnCommand(execution, signal, elapsedMs) {
|
|
5034
|
+
return new Promise((resolve, reject) => {
|
|
5035
|
+
let stdout = "";
|
|
5036
|
+
let stderr = "";
|
|
5037
|
+
let outputBytes = 0;
|
|
5038
|
+
const child = spawn(execution.command, execution.args, {
|
|
5039
|
+
cwd: execution.cwd,
|
|
5040
|
+
env: execution.env,
|
|
5041
|
+
shell: false,
|
|
5042
|
+
signal,
|
|
5043
|
+
windowsHide: true
|
|
5044
|
+
});
|
|
5045
|
+
child.on("error", reject);
|
|
5046
|
+
const append = (stream, chunk) => {
|
|
5047
|
+
outputBytes += chunk.byteLength;
|
|
5048
|
+
if (outputBytes > execution.maxOutputBytes) {
|
|
5049
|
+
child.kill();
|
|
5050
|
+
reject(new CapletsError("DOWNSTREAM_TOOL_ERROR", "CLI tool output exceeded byte limit"));
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
if (stream === "stdout") stdout += chunk.toString("utf8");
|
|
5054
|
+
else stderr += chunk.toString("utf8");
|
|
5055
|
+
};
|
|
5056
|
+
child.stdout?.on("data", (chunk) => append("stdout", chunk));
|
|
5057
|
+
child.stderr?.on("data", (chunk) => append("stderr", chunk));
|
|
5058
|
+
child.on("close", (exitCode, childSignal) => {
|
|
5059
|
+
resolve({
|
|
5060
|
+
exitCode,
|
|
5061
|
+
signal: childSignal,
|
|
5062
|
+
stdout,
|
|
5063
|
+
stderr,
|
|
5064
|
+
elapsedMs: elapsedMs()
|
|
5065
|
+
});
|
|
5066
|
+
});
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5069
|
+
function parseStructuredResult(action, result, tolerateInvalidJson = false) {
|
|
5070
|
+
const structured = {
|
|
5071
|
+
exitCode: result.exitCode,
|
|
5072
|
+
stdout: result.stdout,
|
|
5073
|
+
stderr: result.stderr,
|
|
5074
|
+
elapsedMs: result.elapsedMs,
|
|
5075
|
+
...result.signal ? { signal: result.signal } : {}
|
|
5076
|
+
};
|
|
5077
|
+
if (action.output?.type === "json" && result.stdout.trim()) try {
|
|
5078
|
+
structured.json = JSON.parse(result.stdout);
|
|
5079
|
+
} catch (error) {
|
|
5080
|
+
if (tolerateInvalidJson) {
|
|
5081
|
+
structured.jsonParseError = toSafeError(error);
|
|
5082
|
+
return structured;
|
|
5083
|
+
}
|
|
5084
|
+
throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", `CLI tool ${action.name} stdout was not valid JSON`, toSafeError(error));
|
|
5085
|
+
}
|
|
5086
|
+
return structured;
|
|
5087
|
+
}
|
|
5088
|
+
function resolveCommandPath(command) {
|
|
5089
|
+
if (isAbsolute(command) || /[\\/]/.test(command)) {
|
|
5090
|
+
assertExecutable(command);
|
|
5091
|
+
return command;
|
|
5092
|
+
}
|
|
5093
|
+
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
5094
|
+
if (!directory) continue;
|
|
5095
|
+
const candidate = join(directory, command);
|
|
5096
|
+
if (isExecutable(candidate)) return candidate;
|
|
5097
|
+
if (process.platform === "win32") for (const ext of (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")) {
|
|
5098
|
+
const windowsCandidate = join(directory, `${command}${ext.toLowerCase()}`);
|
|
5099
|
+
if (isExecutable(windowsCandidate)) return windowsCandidate;
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
throw new CapletsError("SERVER_UNAVAILABLE", `CLI command ${command} was not found on PATH`);
|
|
5103
|
+
}
|
|
5104
|
+
function assertExecutable(path) {
|
|
5105
|
+
if (!isExecutable(path)) throw new CapletsError("SERVER_UNAVAILABLE", `CLI command ${path} is not executable`);
|
|
5106
|
+
}
|
|
5107
|
+
function isExecutable(path) {
|
|
5108
|
+
try {
|
|
5109
|
+
accessSync(path, constants.X_OK);
|
|
5110
|
+
return true;
|
|
5111
|
+
} catch {
|
|
5112
|
+
return false;
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
5115
|
+
function isAbortError$1(error) {
|
|
5116
|
+
return error instanceof Error && error.name === "AbortError";
|
|
5117
|
+
}
|
|
5118
|
+
function isPlainObject$6(value) {
|
|
5119
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5120
|
+
}
|
|
4972
5121
|
/** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
|
|
4973
5122
|
const ZodIssueCode$1 = {
|
|
4974
5123
|
invalid_type: "invalid_type",
|
|
@@ -12489,9 +12638,9 @@ const capletMcpServerSchema = object$1({
|
|
|
12489
12638
|
cwd: string().min(1).optional().describe("Working directory for stdio servers."),
|
|
12490
12639
|
url: string().min(1).optional().describe("Remote MCP server URL for http or sse transport."),
|
|
12491
12640
|
auth: capletRemoteAuthSchema.optional(),
|
|
12492
|
-
startupTimeoutMs: number$
|
|
12493
|
-
callTimeoutMs: number$
|
|
12494
|
-
toolCacheTtlMs: number$
|
|
12641
|
+
startupTimeoutMs: number$2().int().positive().optional().describe("Timeout in milliseconds for starting or checking a downstream server."),
|
|
12642
|
+
callTimeoutMs: number$2().int().positive().optional().describe("Timeout in milliseconds for downstream tool calls."),
|
|
12643
|
+
toolCacheTtlMs: number$2().int().nonnegative().optional().describe("Milliseconds downstream tool metadata stays fresh. Set 0 to refresh every time."),
|
|
12495
12644
|
disabled: boolean().optional().describe("When true, omit this Caplet from discovery and do not start its MCP server.")
|
|
12496
12645
|
}).strict().superRefine((server, ctx) => {
|
|
12497
12646
|
const effectiveTransport = server.transport ?? (server.command ? "stdio" : void 0);
|
|
@@ -12546,8 +12695,8 @@ const capletOpenApiEndpointSchema = object$1({
|
|
|
12546
12695
|
specUrl: string().min(1).optional().describe("Remote OpenAPI specification URL."),
|
|
12547
12696
|
baseUrl: string().min(1).optional().describe("Override base URL for OpenAPI requests."),
|
|
12548
12697
|
auth: capletEndpointAuthSchema.describe("Explicit OpenAPI request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
12549
|
-
requestTimeoutMs: number$
|
|
12550
|
-
operationCacheTtlMs: number$
|
|
12698
|
+
requestTimeoutMs: number$2().int().positive().optional().describe("Timeout in milliseconds for OpenAPI HTTP requests."),
|
|
12699
|
+
operationCacheTtlMs: number$2().int().nonnegative().optional().describe("Milliseconds OpenAPI operation metadata stays fresh. Set 0 to refresh every time."),
|
|
12551
12700
|
disabled: boolean().optional().describe("When true, omit this Caplet from discovery.")
|
|
12552
12701
|
}).strict().superRefine((endpoint, ctx) => {
|
|
12553
12702
|
if (Boolean(endpoint.specPath) === Boolean(endpoint.specUrl)) ctx.addIssue({
|
|
@@ -12584,9 +12733,9 @@ const capletGraphQlEndpointSchema = object$1({
|
|
|
12584
12733
|
introspection: literal(true).optional().describe("Load schema through endpoint introspection."),
|
|
12585
12734
|
operations: record(string().regex(SERVER_ID_PATTERN), capletGraphQlOperationSchema).optional().describe("Configured GraphQL operations keyed by stable tool name."),
|
|
12586
12735
|
auth: capletEndpointAuthSchema.describe("Explicit GraphQL request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
12587
|
-
requestTimeoutMs: number$
|
|
12588
|
-
operationCacheTtlMs: number$
|
|
12589
|
-
selectionDepth: number$
|
|
12736
|
+
requestTimeoutMs: number$2().int().positive().optional().describe("Timeout in milliseconds for GraphQL HTTP requests."),
|
|
12737
|
+
operationCacheTtlMs: number$2().int().nonnegative().optional().describe("Milliseconds GraphQL operation metadata stays fresh. Set 0 to refresh every time."),
|
|
12738
|
+
selectionDepth: number$2().int().positive().max(5).optional().describe("Maximum depth for auto-generated GraphQL selection sets."),
|
|
12590
12739
|
disabled: boolean().optional().describe("When true, omit this Caplet from discovery.")
|
|
12591
12740
|
}).strict().superRefine((endpoint, ctx) => {
|
|
12592
12741
|
if (Number(Boolean(endpoint.schemaPath)) + Number(Boolean(endpoint.schemaUrl)) + Number(endpoint.introspection === true) !== 1) ctx.addIssue({
|
|
@@ -12607,7 +12756,7 @@ const capletGraphQlEndpointSchema = object$1({
|
|
|
12607
12756
|
});
|
|
12608
12757
|
const httpScalarMappingSchema$1 = record(string(), union([
|
|
12609
12758
|
string(),
|
|
12610
|
-
number$
|
|
12759
|
+
number$2(),
|
|
12611
12760
|
boolean()
|
|
12612
12761
|
]));
|
|
12613
12762
|
const capletHttpActionSchema = object$1({
|
|
@@ -12635,8 +12784,8 @@ const capletHttpApiSchema = object$1({
|
|
|
12635
12784
|
baseUrl: string().min(1).regex(HTTP_BASE_URL_PATTERN, "HTTP API baseUrl must not include credentials, query, or fragment").describe("Base URL for HTTP action requests."),
|
|
12636
12785
|
auth: capletEndpointAuthSchema.describe("Explicit HTTP API request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
12637
12786
|
actions: record(string().regex(SERVER_ID_PATTERN), capletHttpActionSchema).refine((actions) => Object.keys(actions).length > 0, "HTTP API must define at least one action").describe("Configured HTTP actions keyed by stable tool name."),
|
|
12638
|
-
requestTimeoutMs: number$
|
|
12639
|
-
maxResponseBytes: number$
|
|
12787
|
+
requestTimeoutMs: number$2().int().positive().optional().describe("Timeout in milliseconds for HTTP action requests."),
|
|
12788
|
+
maxResponseBytes: number$2().int().positive().optional().describe("Maximum HTTP action response body bytes to read."),
|
|
12640
12789
|
disabled: boolean().optional().describe("When true, omit this Caplet from discovery.")
|
|
12641
12790
|
}).strict().superRefine((api, ctx) => {
|
|
12642
12791
|
if (api.baseUrl && !hasEnvReference$1(api.baseUrl) && !isAllowedHttpBaseUrl(api.baseUrl)) ctx.addIssue({
|
|
@@ -12666,8 +12815,8 @@ const capletCliToolActionSchema = object$1({
|
|
|
12666
12815
|
args: array(string()).optional().describe("Arguments passed to the command."),
|
|
12667
12816
|
env: record(string(), string()).optional().describe("Additional environment variables."),
|
|
12668
12817
|
cwd: string().min(1).optional().describe("Working directory for this action."),
|
|
12669
|
-
timeoutMs: number$
|
|
12670
|
-
maxOutputBytes: number$
|
|
12818
|
+
timeoutMs: number$2().int().positive().optional(),
|
|
12819
|
+
maxOutputBytes: number$2().int().positive().optional(),
|
|
12671
12820
|
output: capletCliToolOutputSchema.optional(),
|
|
12672
12821
|
annotations: capletCliToolAnnotationsSchema.optional()
|
|
12673
12822
|
}).strict();
|
|
@@ -12675,16 +12824,16 @@ const capletCliToolsSchema = object$1({
|
|
|
12675
12824
|
actions: record(string().regex(SERVER_ID_PATTERN), capletCliToolActionSchema).refine((actions) => Object.keys(actions).length > 0, "CLI tools backend must define at least one action").describe("Configured CLI actions keyed by stable tool name."),
|
|
12676
12825
|
cwd: string().min(1).optional().describe("Default working directory for CLI actions."),
|
|
12677
12826
|
env: record(string(), string()).optional().describe("Default environment variables."),
|
|
12678
|
-
timeoutMs: number$
|
|
12679
|
-
maxOutputBytes: number$
|
|
12827
|
+
timeoutMs: number$2().int().positive().optional(),
|
|
12828
|
+
maxOutputBytes: number$2().int().positive().optional(),
|
|
12680
12829
|
disabled: boolean().optional().describe("When true, omit this Caplet from discovery.")
|
|
12681
12830
|
}).strict();
|
|
12682
12831
|
const capletSetSchema = object$1({
|
|
12683
12832
|
configPath: string().min(1).optional().describe("Child Caplets config.json path."),
|
|
12684
12833
|
capletsRoot: string().min(1).optional().describe("Child Markdown Caplets root directory."),
|
|
12685
|
-
defaultSearchLimit: number$
|
|
12686
|
-
maxSearchLimit: number$
|
|
12687
|
-
toolCacheTtlMs: number$
|
|
12834
|
+
defaultSearchLimit: number$2().int().positive().optional(),
|
|
12835
|
+
maxSearchLimit: number$2().int().positive().max(50).optional(),
|
|
12836
|
+
toolCacheTtlMs: number$2().int().nonnegative().optional(),
|
|
12688
12837
|
disabled: boolean().optional().describe("When true, omit this Caplet from discovery.")
|
|
12689
12838
|
}).strict().superRefine((set, ctx) => {
|
|
12690
12839
|
if (!set.configPath && !set.capletsRoot) ctx.addIssue({
|
|
@@ -12922,14 +13071,24 @@ function defaultStateBaseDir(env = process.env, home = homedir(), platform = pro
|
|
|
12922
13071
|
if (platform === "win32") return env.LOCALAPPDATA && win32.isAbsolute(env.LOCALAPPDATA) ? env.LOCALAPPDATA : win32.join(home, "AppData", "Local");
|
|
12923
13072
|
return env.XDG_STATE_HOME && posix.isAbsolute(env.XDG_STATE_HOME) ? env.XDG_STATE_HOME : posix.join(home, ".local", "state");
|
|
12924
13073
|
}
|
|
13074
|
+
function defaultCacheBaseDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
13075
|
+
if (platform === "win32") return env.LOCALAPPDATA && win32.isAbsolute(env.LOCALAPPDATA) ? env.LOCALAPPDATA : win32.join(home, "AppData", "Local");
|
|
13076
|
+
if (platform === "darwin") return posix.join(home, "Library", "Caches");
|
|
13077
|
+
return env.XDG_CACHE_HOME && posix.isAbsolute(env.XDG_CACHE_HOME) ? env.XDG_CACHE_HOME : posix.join(home, ".cache");
|
|
13078
|
+
}
|
|
12925
13079
|
function defaultConfigPath(env = process.env, home = homedir(), platform = process.platform) {
|
|
12926
13080
|
return (platform === "win32" ? win32.join : posix.join)(defaultConfigBaseDir(env, home, platform), "caplets", "config.json");
|
|
12927
13081
|
}
|
|
12928
13082
|
function defaultAuthDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
12929
13083
|
return (platform === "win32" ? win32.join : posix.join)(defaultStateBaseDir(env, home, platform), "caplets", "auth");
|
|
12930
13084
|
}
|
|
13085
|
+
function defaultCompletionCacheDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
13086
|
+
const pathJoin = platform === "win32" ? win32.join : posix.join;
|
|
13087
|
+
return platform === "win32" ? pathJoin(defaultCacheBaseDir(env, home, platform), "caplets", "cache", "completions") : pathJoin(defaultCacheBaseDir(env, home, platform), "caplets", "completions");
|
|
13088
|
+
}
|
|
12931
13089
|
const DEFAULT_CONFIG_PATH = defaultConfigPath();
|
|
12932
13090
|
const DEFAULT_AUTH_DIR = defaultAuthDir();
|
|
13091
|
+
const DEFAULT_COMPLETION_CACHE_DIR = defaultCompletionCacheDir();
|
|
12933
13092
|
const PROJECT_CONFIG_FILE = join(".caplets", "config.json");
|
|
12934
13093
|
function resolveConfigPath(path) {
|
|
12935
13094
|
return path ?? DEFAULT_CONFIG_PATH;
|
|
@@ -13042,9 +13201,9 @@ const publicServerSchema = object$1({
|
|
|
13042
13201
|
url: string().url().optional().describe("Remote MCP server URL for http or sse transport."),
|
|
13043
13202
|
auth: remoteAuthSchema.optional(),
|
|
13044
13203
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
13045
|
-
startupTimeoutMs: number$
|
|
13046
|
-
callTimeoutMs: number$
|
|
13047
|
-
toolCacheTtlMs: number$
|
|
13204
|
+
startupTimeoutMs: number$2().int().positive().default(1e4).describe("Timeout in milliseconds for starting or checking a downstream server."),
|
|
13205
|
+
callTimeoutMs: number$2().int().positive().default(6e4).describe("Timeout in milliseconds for downstream tool calls."),
|
|
13206
|
+
toolCacheTtlMs: number$2().int().nonnegative().default(3e4).describe("Milliseconds downstream tool metadata stays fresh. Set 0 to refresh every time."),
|
|
13048
13207
|
disabled: boolean().default(false).describe("When true, omit this server from Caplets discovery and do not start it.")
|
|
13049
13208
|
}).strict();
|
|
13050
13209
|
const normalizedServerSchema = publicServerSchema.extend({ body: string().optional() });
|
|
@@ -13056,8 +13215,8 @@ const publicOpenApiEndpointSchema = object$1({
|
|
|
13056
13215
|
baseUrl: string().url().optional().describe("Override base URL for OpenAPI requests."),
|
|
13057
13216
|
auth: openApiAuthSchema.describe("Explicit OpenAPI request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
13058
13217
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
13059
|
-
requestTimeoutMs: number$
|
|
13060
|
-
operationCacheTtlMs: number$
|
|
13218
|
+
requestTimeoutMs: number$2().int().positive().default(6e4).describe("Timeout in milliseconds for OpenAPI HTTP requests."),
|
|
13219
|
+
operationCacheTtlMs: number$2().int().nonnegative().default(3e4).describe("Milliseconds OpenAPI operation metadata stays fresh. Set 0 to refresh every time."),
|
|
13061
13220
|
disabled: boolean().default(false).describe("When true, omit this OpenAPI Caplet from discovery.")
|
|
13062
13221
|
}).strict();
|
|
13063
13222
|
const normalizedOpenApiEndpointSchema = publicOpenApiEndpointSchema.extend({ body: string().optional() });
|
|
@@ -13082,9 +13241,9 @@ const publicGraphQlEndpointSchema = object$1({
|
|
|
13082
13241
|
operations: record(string().regex(SERVER_ID_PATTERN), graphQlOperationSchema).optional().describe("Configured GraphQL operations keyed by stable tool name."),
|
|
13083
13242
|
auth: openApiAuthSchema.describe("Explicit GraphQL request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
13084
13243
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
13085
|
-
requestTimeoutMs: number$
|
|
13086
|
-
operationCacheTtlMs: number$
|
|
13087
|
-
selectionDepth: number$
|
|
13244
|
+
requestTimeoutMs: number$2().int().positive().default(6e4).describe("Timeout in milliseconds for GraphQL HTTP requests."),
|
|
13245
|
+
operationCacheTtlMs: number$2().int().nonnegative().default(3e4).describe("Milliseconds GraphQL operation metadata stays fresh. Set 0 to refresh every time."),
|
|
13246
|
+
selectionDepth: number$2().int().positive().max(5).default(2).describe("Maximum depth for auto-generated GraphQL selection sets."),
|
|
13088
13247
|
disabled: boolean().default(false).describe("When true, omit this GraphQL Caplet.")
|
|
13089
13248
|
}).strict().superRefine((endpoint, ctx) => {
|
|
13090
13249
|
if (Number(Boolean(endpoint.schemaPath)) + Number(Boolean(endpoint.schemaUrl)) + Number(endpoint.introspection === true) !== 1) ctx.addIssue({
|
|
@@ -13095,7 +13254,7 @@ const publicGraphQlEndpointSchema = object$1({
|
|
|
13095
13254
|
const normalizedGraphQlEndpointSchema = publicGraphQlEndpointSchema.extend({ body: string().optional() });
|
|
13096
13255
|
const httpScalarMappingSchema = record(string(), union([
|
|
13097
13256
|
string(),
|
|
13098
|
-
number$
|
|
13257
|
+
number$2(),
|
|
13099
13258
|
boolean()
|
|
13100
13259
|
]));
|
|
13101
13260
|
const httpActionSchema = object$1({
|
|
@@ -13127,8 +13286,8 @@ const publicHttpApiSchema = object$1({
|
|
|
13127
13286
|
auth: openApiAuthSchema.describe("Explicit HTTP API request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
13128
13287
|
actions: record(string().regex(SERVER_ID_PATTERN), httpActionSchema).refine((actions) => Object.keys(actions).length > 0, "HTTP API must define at least one action").describe("Configured HTTP actions keyed by stable tool name."),
|
|
13129
13288
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
13130
|
-
requestTimeoutMs: number$
|
|
13131
|
-
maxResponseBytes: number$
|
|
13289
|
+
requestTimeoutMs: number$2().int().positive().default(6e4).describe("Timeout in milliseconds for HTTP action requests."),
|
|
13290
|
+
maxResponseBytes: number$2().int().positive().default(2e5).describe("Maximum HTTP action response body bytes to read."),
|
|
13132
13291
|
disabled: boolean().default(false).describe("When true, omit this HTTP API Caplet.")
|
|
13133
13292
|
}).strict();
|
|
13134
13293
|
const normalizedHttpApiSchema = publicHttpApiSchema.extend({ body: string().optional() });
|
|
@@ -13147,8 +13306,8 @@ const cliToolActionSchema = object$1({
|
|
|
13147
13306
|
args: array(string()).optional().describe("Arguments passed to the command."),
|
|
13148
13307
|
env: record(string(), string()).optional().describe("Additional environment variables for the command."),
|
|
13149
13308
|
cwd: string().min(1).optional().describe("Working directory for this action."),
|
|
13150
|
-
timeoutMs: number$
|
|
13151
|
-
maxOutputBytes: number$
|
|
13309
|
+
timeoutMs: number$2().int().positive().optional().describe("Command timeout in milliseconds."),
|
|
13310
|
+
maxOutputBytes: number$2().int().positive().optional().describe("Maximum combined stdout and stderr bytes to keep."),
|
|
13152
13311
|
output: cliToolOutputSchema.optional(),
|
|
13153
13312
|
annotations: cliToolAnnotationsSchema.optional()
|
|
13154
13313
|
}).strict();
|
|
@@ -13159,8 +13318,8 @@ const publicCliToolsSchema = object$1({
|
|
|
13159
13318
|
cwd: string().min(1).optional().describe("Default working directory for CLI actions."),
|
|
13160
13319
|
env: record(string(), string()).optional().describe("Default environment variables for CLI actions."),
|
|
13161
13320
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
13162
|
-
timeoutMs: number$
|
|
13163
|
-
maxOutputBytes: number$
|
|
13321
|
+
timeoutMs: number$2().int().positive().default(6e4).describe("Default timeout in milliseconds for CLI actions."),
|
|
13322
|
+
maxOutputBytes: number$2().int().positive().default(2e5).describe("Default maximum combined stdout and stderr bytes to keep."),
|
|
13164
13323
|
disabled: boolean().default(false).describe("When true, omit this CLI tools Caplet.")
|
|
13165
13324
|
}).strict();
|
|
13166
13325
|
const normalizedCliToolsSchema = publicCliToolsSchema.extend({ body: string().optional() });
|
|
@@ -13169,9 +13328,9 @@ const publicCapletSetSchema = object$1({
|
|
|
13169
13328
|
description: string().describe("Capability description shown before child Caplets are disclosed.").refine((value) => value.trim().length >= 10, "description must contain at least 10 non-whitespace characters").refine((value) => value.length <= 1500, "description must be at most 1500 characters"),
|
|
13170
13329
|
configPath: string().min(1).optional().describe("Child Caplets config.json path."),
|
|
13171
13330
|
capletsRoot: string().min(1).optional().describe("Child Markdown Caplets root directory."),
|
|
13172
|
-
defaultSearchLimit: number$
|
|
13173
|
-
maxSearchLimit: number$
|
|
13174
|
-
toolCacheTtlMs: number$
|
|
13331
|
+
defaultSearchLimit: number$2().int().positive().default(20).describe("Default maximum number of child Caplet search results."),
|
|
13332
|
+
maxSearchLimit: number$2().int().positive().max(50).default(50).describe("Maximum accepted child Caplet search result limit."),
|
|
13333
|
+
toolCacheTtlMs: number$2().int().nonnegative().default(3e4).describe("Milliseconds child Caplet metadata stays fresh. Set 0 to refresh every time."),
|
|
13175
13334
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
13176
13335
|
disabled: boolean().default(false).describe("When true, omit this Caplet set.")
|
|
13177
13336
|
}).strict().superRefine((set, ctx) => {
|
|
@@ -13190,8 +13349,19 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
13190
13349
|
return object$1({
|
|
13191
13350
|
$schema: string().url().optional().describe("Optional JSON Schema URL for editor validation."),
|
|
13192
13351
|
version: literal(1).default(1).describe("Caplets config schema version."),
|
|
13193
|
-
defaultSearchLimit: number$
|
|
13194
|
-
maxSearchLimit: number$
|
|
13352
|
+
defaultSearchLimit: number$2().int().positive().default(20).describe("Default maximum number of same-server search results."),
|
|
13353
|
+
maxSearchLimit: number$2().int().positive().max(50).default(50).describe("Maximum accepted search_tools limit."),
|
|
13354
|
+
completion: object$1({
|
|
13355
|
+
discoveryTimeoutMs: number$2().int().positive().default(750),
|
|
13356
|
+
overallTimeoutMs: number$2().int().positive().default(1500),
|
|
13357
|
+
cacheTtlMs: number$2().int().nonnegative().default(3e5),
|
|
13358
|
+
negativeCacheTtlMs: number$2().int().nonnegative().default(3e4)
|
|
13359
|
+
}).strict().default({
|
|
13360
|
+
discoveryTimeoutMs: 750,
|
|
13361
|
+
overallTimeoutMs: 1500,
|
|
13362
|
+
cacheTtlMs: 3e5,
|
|
13363
|
+
negativeCacheTtlMs: 3e4
|
|
13364
|
+
}).describe("Shell completion discovery timeout and cache settings."),
|
|
13195
13365
|
mcpServers: record(string().regex(SERVER_ID_PATTERN), serverValueSchema).default({}).describe("Downstream MCP servers keyed by stable server ID."),
|
|
13196
13366
|
openapiEndpoints: record(string().regex(SERVER_ID_PATTERN), openApiEndpointValueSchema).default({}).describe("OpenAPI endpoints keyed by stable Caplet ID."),
|
|
13197
13367
|
graphqlEndpoints: record(string().regex(SERVER_ID_PATTERN), graphQlEndpointValueSchema).default({}).describe("GraphQL endpoints keyed by stable Caplet ID."),
|
|
@@ -13442,7 +13612,7 @@ function loadConfigWithSources(path = resolveConfigPath(), projectPath = resolve
|
|
|
13442
13612
|
const userConfig = hasUserConfig ? readPublicConfigInput(path) : void 0;
|
|
13443
13613
|
const userCaplets = loadCapletFilesWithPaths(resolveCapletsRoot(path));
|
|
13444
13614
|
const projectConfig = hasProjectConfig ? rejectProjectConfigExecutableBackendMaps(readPublicConfigInput(projectPath), projectPath) : void 0;
|
|
13445
|
-
const projectCapletsRoot = resolveProjectCapletsRootForConfigPath(projectPath);
|
|
13615
|
+
const projectCapletsRoot = resolveProjectCapletsRootForConfigPath$1(projectPath);
|
|
13446
13616
|
const projectCaplets = projectCapletsRoot ? loadCapletFilesWithPaths(projectCapletsRoot) : void 0;
|
|
13447
13617
|
if (!hasUserConfig && !hasProjectConfig && !userCaplets && !projectCaplets) throw new CapletsError("CONFIG_NOT_FOUND", `Caplets config not found at ${path} or ${projectPath}`);
|
|
13448
13618
|
try {
|
|
@@ -13496,7 +13666,7 @@ function loadIsolatedConfig(options) {
|
|
|
13496
13666
|
if (Object.keys(config.mcpServers).length === 0 && Object.keys(config.openapiEndpoints).length === 0 && Object.keys(config.graphqlEndpoints).length === 0 && Object.keys(config.httpApis).length === 0 && Object.keys(config.cliTools).length === 0 && Object.keys(config.capletSets).length === 0) throw new CapletsError("CONFIG_INVALID", "Nested Caplet set must define at least one Caplet");
|
|
13497
13667
|
return config;
|
|
13498
13668
|
}
|
|
13499
|
-
function resolveProjectCapletsRootForConfigPath(projectPath) {
|
|
13669
|
+
function resolveProjectCapletsRootForConfigPath$1(projectPath) {
|
|
13500
13670
|
const root = dirname(projectPath);
|
|
13501
13671
|
return basename(root) === ".caplets" && basename(projectPath) === "config.json" ? root : void 0;
|
|
13502
13672
|
}
|
|
@@ -13706,7 +13876,8 @@ function parseConfig(input) {
|
|
|
13706
13876
|
version: parsed.data.version,
|
|
13707
13877
|
options: {
|
|
13708
13878
|
defaultSearchLimit: parsed.data.defaultSearchLimit,
|
|
13709
|
-
maxSearchLimit: parsed.data.maxSearchLimit
|
|
13879
|
+
maxSearchLimit: parsed.data.maxSearchLimit,
|
|
13880
|
+
completion: parsed.data.completion
|
|
13710
13881
|
},
|
|
13711
13882
|
mcpServers: servers,
|
|
13712
13883
|
openapiEndpoints,
|
|
@@ -17286,10 +17457,10 @@ const ZodMiniType = /* @__PURE__ */ $constructor("ZodMiniType", (inst, def) => {
|
|
|
17286
17457
|
$ZodType.init(inst, def);
|
|
17287
17458
|
inst.def = def;
|
|
17288
17459
|
inst.type = def.type;
|
|
17289
|
-
inst.parse = (data, params) => parse$
|
|
17290
|
-
inst.safeParse = (data, params) => safeParse$
|
|
17460
|
+
inst.parse = (data, params) => parse$1(inst, data, params, { callee: inst.parse });
|
|
17461
|
+
inst.safeParse = (data, params) => safeParse$1(inst, data, params);
|
|
17291
17462
|
inst.parseAsync = async (data, params) => parseAsync$1(inst, data, params, { callee: inst.parseAsync });
|
|
17292
|
-
inst.safeParseAsync = async (data, params) => safeParseAsync$
|
|
17463
|
+
inst.safeParseAsync = async (data, params) => safeParseAsync$1(inst, data, params);
|
|
17293
17464
|
inst.check = (...checks) => {
|
|
17294
17465
|
return inst.clone({
|
|
17295
17466
|
...def,
|
|
@@ -17335,11 +17506,11 @@ function objectFromShape(shape) {
|
|
|
17335
17506
|
throw new Error("Mixed Zod versions detected in object shape.");
|
|
17336
17507
|
}
|
|
17337
17508
|
function safeParse(schema, data) {
|
|
17338
|
-
if (isZ4Schema(schema)) return safeParse$
|
|
17509
|
+
if (isZ4Schema(schema)) return safeParse$1(schema, data);
|
|
17339
17510
|
return schema.safeParse(data);
|
|
17340
17511
|
}
|
|
17341
17512
|
async function safeParseAsync(schema, data) {
|
|
17342
|
-
if (isZ4Schema(schema)) return await safeParseAsync$
|
|
17513
|
+
if (isZ4Schema(schema)) return await safeParseAsync$1(schema, data);
|
|
17343
17514
|
return await schema.safeParseAsync(data);
|
|
17344
17515
|
}
|
|
17345
17516
|
function getObjectShape(schema) {
|
|
@@ -17453,7 +17624,7 @@ const AssertObjectSchema = custom((v) => v !== null && (typeof v === "object" ||
|
|
|
17453
17624
|
/**
|
|
17454
17625
|
* A progress token, used to associate progress notifications with the original request.
|
|
17455
17626
|
*/
|
|
17456
|
-
const ProgressTokenSchema = union([string(), number$
|
|
17627
|
+
const ProgressTokenSchema = union([string(), number$2().int()]);
|
|
17457
17628
|
/**
|
|
17458
17629
|
* An opaque token used to represent a cursor for pagination.
|
|
17459
17630
|
*/
|
|
@@ -17462,13 +17633,13 @@ looseObject({
|
|
|
17462
17633
|
/**
|
|
17463
17634
|
* Requested duration in milliseconds to retain task from creation.
|
|
17464
17635
|
*/
|
|
17465
|
-
ttl: number$
|
|
17636
|
+
ttl: number$2().optional(),
|
|
17466
17637
|
/**
|
|
17467
17638
|
* Time in milliseconds to wait between task status requests.
|
|
17468
17639
|
*/
|
|
17469
|
-
pollInterval: number$
|
|
17640
|
+
pollInterval: number$2().optional()
|
|
17470
17641
|
});
|
|
17471
|
-
const TaskMetadataSchema = object$1({ ttl: number$
|
|
17642
|
+
const TaskMetadataSchema = object$1({ ttl: number$2().optional() });
|
|
17472
17643
|
/**
|
|
17473
17644
|
* Metadata for associating messages with a task.
|
|
17474
17645
|
* Include this in the `_meta` field under the key `io.modelcontextprotocol/related-task`.
|
|
@@ -17535,7 +17706,7 @@ _meta: RequestMetaSchema.optional() });
|
|
|
17535
17706
|
/**
|
|
17536
17707
|
* A uniquely identifying ID for a request in JSON-RPC.
|
|
17537
17708
|
*/
|
|
17538
|
-
const RequestIdSchema = union([string(), number$
|
|
17709
|
+
const RequestIdSchema = union([string(), number$2().int()]);
|
|
17539
17710
|
/**
|
|
17540
17711
|
* A request that expects a response.
|
|
17541
17712
|
*/
|
|
@@ -17592,7 +17763,7 @@ const JSONRPCErrorResponseSchema = object$1({
|
|
|
17592
17763
|
/**
|
|
17593
17764
|
* The error type that occurred.
|
|
17594
17765
|
*/
|
|
17595
|
-
code: number$
|
|
17766
|
+
code: number$2().int(),
|
|
17596
17767
|
/**
|
|
17597
17768
|
* A short description of the error. The message SHOULD be limited to a concise single sentence.
|
|
17598
17769
|
*/
|
|
@@ -17928,11 +18099,11 @@ const ProgressSchema = object$1({
|
|
|
17928
18099
|
/**
|
|
17929
18100
|
* The progress thus far. This should increase every time progress is made, even if the total is unknown.
|
|
17930
18101
|
*/
|
|
17931
|
-
progress: number$
|
|
18102
|
+
progress: number$2(),
|
|
17932
18103
|
/**
|
|
17933
18104
|
* Total number of items to process (or total progress required), if known.
|
|
17934
18105
|
*/
|
|
17935
|
-
total: optional(number$
|
|
18106
|
+
total: optional(number$2()),
|
|
17936
18107
|
/**
|
|
17937
18108
|
* An optional message describing the current progress.
|
|
17938
18109
|
*/
|
|
@@ -17988,7 +18159,7 @@ const TaskSchema = object$1({
|
|
|
17988
18159
|
* Time in milliseconds to keep task results available after completion.
|
|
17989
18160
|
* If null, the task has unlimited lifetime until manually cleaned up.
|
|
17990
18161
|
*/
|
|
17991
|
-
ttl: union([number$
|
|
18162
|
+
ttl: union([number$2(), _null()]),
|
|
17992
18163
|
/**
|
|
17993
18164
|
* ISO 8601 timestamp when the task was created.
|
|
17994
18165
|
*/
|
|
@@ -17997,7 +18168,7 @@ const TaskSchema = object$1({
|
|
|
17997
18168
|
* ISO 8601 timestamp when the task was last updated.
|
|
17998
18169
|
*/
|
|
17999
18170
|
lastUpdatedAt: string(),
|
|
18000
|
-
pollInterval: optional(number$
|
|
18171
|
+
pollInterval: optional(number$2()),
|
|
18001
18172
|
/**
|
|
18002
18173
|
* Optional diagnostic message for failed tasks or other status information.
|
|
18003
18174
|
*/
|
|
@@ -18112,7 +18283,7 @@ const AnnotationsSchema = object$1({
|
|
|
18112
18283
|
/**
|
|
18113
18284
|
* Importance hint for the resource, from 0 (least) to 1 (most).
|
|
18114
18285
|
*/
|
|
18115
|
-
priority: number$
|
|
18286
|
+
priority: number$2().min(0).max(1).optional(),
|
|
18116
18287
|
/**
|
|
18117
18288
|
* ISO 8601 timestamp for the most recent modification.
|
|
18118
18289
|
*/
|
|
@@ -18143,7 +18314,7 @@ const ResourceSchema = object$1({
|
|
|
18143
18314
|
*
|
|
18144
18315
|
* This can be used by Hosts to display file sizes and estimate context window usage.
|
|
18145
18316
|
*/
|
|
18146
|
-
size: optional(number$
|
|
18317
|
+
size: optional(number$2()),
|
|
18147
18318
|
/**
|
|
18148
18319
|
* Optional annotations for the client.
|
|
18149
18320
|
*/
|
|
@@ -18670,7 +18841,7 @@ const ListChangedOptionsBaseSchema = object$1({
|
|
|
18670
18841
|
*
|
|
18671
18842
|
* @default 300
|
|
18672
18843
|
*/
|
|
18673
|
-
debounceMs: number$
|
|
18844
|
+
debounceMs: number$2().int().nonnegative().default(300)
|
|
18674
18845
|
});
|
|
18675
18846
|
/**
|
|
18676
18847
|
* The severity of a log message.
|
|
@@ -18739,15 +18910,15 @@ name: string().optional() })).optional(),
|
|
|
18739
18910
|
/**
|
|
18740
18911
|
* How much to prioritize cost when selecting a model.
|
|
18741
18912
|
*/
|
|
18742
|
-
costPriority: number$
|
|
18913
|
+
costPriority: number$2().min(0).max(1).optional(),
|
|
18743
18914
|
/**
|
|
18744
18915
|
* How much to prioritize sampling speed (latency) when selecting a model.
|
|
18745
18916
|
*/
|
|
18746
|
-
speedPriority: number$
|
|
18917
|
+
speedPriority: number$2().min(0).max(1).optional(),
|
|
18747
18918
|
/**
|
|
18748
18919
|
* How much to prioritize intelligence and capabilities when selecting a model.
|
|
18749
18920
|
*/
|
|
18750
|
-
intelligencePriority: number$
|
|
18921
|
+
intelligencePriority: number$2().min(0).max(1).optional()
|
|
18751
18922
|
});
|
|
18752
18923
|
/**
|
|
18753
18924
|
* Controls tool usage behavior in sampling requests.
|
|
@@ -18837,13 +19008,13 @@ const CreateMessageRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend
|
|
|
18837
19008
|
"thisServer",
|
|
18838
19009
|
"allServers"
|
|
18839
19010
|
]).optional(),
|
|
18840
|
-
temperature: number$
|
|
19011
|
+
temperature: number$2().optional(),
|
|
18841
19012
|
/**
|
|
18842
19013
|
* The requested maximum number of tokens to sample (to prevent runaway completions).
|
|
18843
19014
|
*
|
|
18844
19015
|
* The client MAY choose to sample fewer tokens than the requested maximum.
|
|
18845
19016
|
*/
|
|
18846
|
-
maxTokens: number$
|
|
19017
|
+
maxTokens: number$2().int(),
|
|
18847
19018
|
stopSequences: array(string()).optional(),
|
|
18848
19019
|
/**
|
|
18849
19020
|
* Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.
|
|
@@ -18947,8 +19118,8 @@ const StringSchemaSchema = object$1({
|
|
|
18947
19118
|
type: literal("string"),
|
|
18948
19119
|
title: string().optional(),
|
|
18949
19120
|
description: string().optional(),
|
|
18950
|
-
minLength: number$
|
|
18951
|
-
maxLength: number$
|
|
19121
|
+
minLength: number$2().optional(),
|
|
19122
|
+
maxLength: number$2().optional(),
|
|
18952
19123
|
format: _enum([
|
|
18953
19124
|
"email",
|
|
18954
19125
|
"uri",
|
|
@@ -18964,9 +19135,9 @@ const NumberSchemaSchema = object$1({
|
|
|
18964
19135
|
type: _enum(["number", "integer"]),
|
|
18965
19136
|
title: string().optional(),
|
|
18966
19137
|
description: string().optional(),
|
|
18967
|
-
minimum: number$
|
|
18968
|
-
maximum: number$
|
|
18969
|
-
default: number$
|
|
19138
|
+
minimum: number$2().optional(),
|
|
19139
|
+
maximum: number$2().optional(),
|
|
19140
|
+
default: number$2().optional()
|
|
18970
19141
|
});
|
|
18971
19142
|
/**
|
|
18972
19143
|
* Schema for single-selection enumeration without display titles for options.
|
|
@@ -19009,8 +19180,8 @@ const PrimitiveSchemaDefinitionSchema = union([
|
|
|
19009
19180
|
type: literal("array"),
|
|
19010
19181
|
title: string().optional(),
|
|
19011
19182
|
description: string().optional(),
|
|
19012
|
-
minItems: number$
|
|
19013
|
-
maxItems: number$
|
|
19183
|
+
minItems: number$2().optional(),
|
|
19184
|
+
maxItems: number$2().optional(),
|
|
19014
19185
|
items: object$1({
|
|
19015
19186
|
type: literal("string"),
|
|
19016
19187
|
enum: array(string())
|
|
@@ -19020,8 +19191,8 @@ const PrimitiveSchemaDefinitionSchema = union([
|
|
|
19020
19191
|
type: literal("array"),
|
|
19021
19192
|
title: string().optional(),
|
|
19022
19193
|
description: string().optional(),
|
|
19023
|
-
minItems: number$
|
|
19024
|
-
maxItems: number$
|
|
19194
|
+
minItems: number$2().optional(),
|
|
19195
|
+
maxItems: number$2().optional(),
|
|
19025
19196
|
items: object$1({ anyOf: array(object$1({
|
|
19026
19197
|
const: string(),
|
|
19027
19198
|
title: string()
|
|
@@ -19126,7 +19297,7 @@ const ElicitResultSchema = ResultSchema.extend({
|
|
|
19126
19297
|
*/
|
|
19127
19298
|
content: preprocess((val) => val === null ? void 0 : val, record(string(), union([
|
|
19128
19299
|
string(),
|
|
19129
|
-
number$
|
|
19300
|
+
number$2(),
|
|
19130
19301
|
boolean(),
|
|
19131
19302
|
array(string())
|
|
19132
19303
|
])).optional())
|
|
@@ -19199,7 +19370,7 @@ const CompleteResultSchema = ResultSchema.extend({ completion: looseObject({
|
|
|
19199
19370
|
/**
|
|
19200
19371
|
* The total number of completion options available. This can exceed the number of values actually sent in the response.
|
|
19201
19372
|
*/
|
|
19202
|
-
total: optional(number$
|
|
19373
|
+
total: optional(number$2().int()),
|
|
19203
19374
|
/**
|
|
19204
19375
|
* Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
|
|
19205
19376
|
*/
|
|
@@ -28871,8 +29042,8 @@ const OAuthClientMetadataSchema = object$1({
|
|
|
28871
29042
|
const OAuthClientInformationSchema = object$1({
|
|
28872
29043
|
client_id: string(),
|
|
28873
29044
|
client_secret: string().optional(),
|
|
28874
|
-
client_id_issued_at: number$
|
|
28875
|
-
client_secret_expires_at: number$
|
|
29045
|
+
client_id_issued_at: number$2().optional(),
|
|
29046
|
+
client_secret_expires_at: number$2().optional()
|
|
28876
29047
|
}).strip();
|
|
28877
29048
|
/**
|
|
28878
29049
|
* RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
|
|
@@ -30743,8 +30914,45 @@ var FileOAuthProvider = class {
|
|
|
30743
30914
|
headers.set("content-type", "application/x-www-form-urlencoded");
|
|
30744
30915
|
};
|
|
30745
30916
|
};
|
|
30746
|
-
async function
|
|
30917
|
+
async function startOAuthFlow(server, options) {
|
|
30747
30918
|
if (server.transport === "stdio" || !server.url || server.auth?.type !== "oauth2" && server.auth?.type !== "oidc") throw new CapletsError("REQUEST_INVALID", `${server.server} is not a configured OAuth remote server`);
|
|
30919
|
+
let redirectUrl;
|
|
30920
|
+
const provider = new FileOAuthProvider(server, options.redirectUri, (url) => {
|
|
30921
|
+
redirectUrl = url;
|
|
30922
|
+
options.print?.(`Open this URL to authorize ${server.server}:\n${url.toString()}`);
|
|
30923
|
+
}, options.authDir);
|
|
30924
|
+
const scope = scopesFor(server.auth);
|
|
30925
|
+
try {
|
|
30926
|
+
if (await auth(provider, {
|
|
30927
|
+
serverUrl: server.url,
|
|
30928
|
+
...scope ? { scope } : {}
|
|
30929
|
+
}) === "AUTHORIZED") return {
|
|
30930
|
+
authorizationUrl: "",
|
|
30931
|
+
complete: async () => {}
|
|
30932
|
+
};
|
|
30933
|
+
} catch (error) {
|
|
30934
|
+
throw normalizeMcpOAuthError(server, error);
|
|
30935
|
+
}
|
|
30936
|
+
if (!redirectUrl) throw new CapletsError("AUTH_FAILED", "OAuth authorization URL was not provided");
|
|
30937
|
+
return {
|
|
30938
|
+
authorizationUrl: redirectUrl.toString(),
|
|
30939
|
+
complete: async (callbackUrl) => {
|
|
30940
|
+
assertNoOAuthCallbackError(server, callbackUrl);
|
|
30941
|
+
const completion = extractCompletion(callbackUrl);
|
|
30942
|
+
if (completion.state !== provider.state()) throw new CapletsError("AUTH_FAILED", "OAuth callback state did not match");
|
|
30943
|
+
try {
|
|
30944
|
+
await auth(provider, {
|
|
30945
|
+
serverUrl: server.url,
|
|
30946
|
+
authorizationCode: completion.code,
|
|
30947
|
+
...scope ? { scope } : {}
|
|
30948
|
+
});
|
|
30949
|
+
} catch (error) {
|
|
30950
|
+
throw normalizeMcpOAuthError(server, error);
|
|
30951
|
+
}
|
|
30952
|
+
}
|
|
30953
|
+
};
|
|
30954
|
+
}
|
|
30955
|
+
async function runOAuthFlow(server, options = {}) {
|
|
30748
30956
|
let callbackCode;
|
|
30749
30957
|
let callbackState;
|
|
30750
30958
|
const callback = await createLoopbackCallback((url) => {
|
|
@@ -30752,37 +30960,43 @@ async function runOAuthFlow(server, options = {}) {
|
|
|
30752
30960
|
callbackCode = url.searchParams.get("code") ?? void 0;
|
|
30753
30961
|
callbackState = url.searchParams.get("state") ?? void 0;
|
|
30754
30962
|
});
|
|
30755
|
-
let redirectUrl;
|
|
30756
|
-
const provider = new FileOAuthProvider(server, callback.redirectUri, (url) => {
|
|
30757
|
-
redirectUrl = url;
|
|
30758
|
-
options.print?.(`Open this URL to authorize ${server.server}:\n${url.toString()}`);
|
|
30759
|
-
}, options.authDir);
|
|
30760
30963
|
try {
|
|
30761
|
-
const
|
|
30762
|
-
|
|
30763
|
-
|
|
30764
|
-
...
|
|
30964
|
+
const started = await startOAuthFlow(server, {
|
|
30965
|
+
redirectUri: callback.redirectUri,
|
|
30966
|
+
...options.authDir ? { authDir: options.authDir } : {},
|
|
30967
|
+
...options.print ? { print: options.print } : {}
|
|
30765
30968
|
});
|
|
30766
|
-
if (
|
|
30767
|
-
if (!options.noOpen
|
|
30969
|
+
if (!started.authorizationUrl) return "AUTHORIZED";
|
|
30970
|
+
if (!options.noOpen) await (options.open ? options.open(started.authorizationUrl) : openBrowser$1(started.authorizationUrl));
|
|
30768
30971
|
const manualInput = options.manualInput ?? (options.noOpen ? await options.readManualInput?.() : void 0);
|
|
30769
30972
|
const completion = manualInput ? extractCompletion(manualInput) : await callback.waitForCode(() => callbackCode ? {
|
|
30770
30973
|
code: callbackCode,
|
|
30771
30974
|
...callbackState ? { state: callbackState } : {}
|
|
30772
30975
|
} : void 0);
|
|
30773
|
-
|
|
30774
|
-
|
|
30775
|
-
return await auth(provider, {
|
|
30776
|
-
serverUrl: server.url,
|
|
30777
|
-
authorizationCode: completion.code,
|
|
30778
|
-
...scope ? { scope } : {}
|
|
30779
|
-
});
|
|
30976
|
+
await started.complete(completion.state ? `${callback.redirectUri}?code=${encodeURIComponent(completion.code)}&state=${encodeURIComponent(completion.state)}` : `${callback.redirectUri}?code=${encodeURIComponent(completion.code)}`);
|
|
30977
|
+
return "AUTHORIZED";
|
|
30780
30978
|
} catch (error) {
|
|
30781
30979
|
throw normalizeMcpOAuthError(server, error);
|
|
30782
30980
|
} finally {
|
|
30783
30981
|
await callback.close();
|
|
30784
30982
|
}
|
|
30785
30983
|
}
|
|
30984
|
+
function assertNoOAuthCallbackError(target, callbackUrl) {
|
|
30985
|
+
let url;
|
|
30986
|
+
try {
|
|
30987
|
+
url = new URL(callbackUrl);
|
|
30988
|
+
} catch {
|
|
30989
|
+
return;
|
|
30990
|
+
}
|
|
30991
|
+
const error = url.searchParams.get("error");
|
|
30992
|
+
if (!error) return;
|
|
30993
|
+
const description = url.searchParams.get("error_description");
|
|
30994
|
+
throw new CapletsError("AUTH_FAILED", description ? `OAuth provider returned an error: ${description}` : "OAuth provider returned an error", redactSecrets({
|
|
30995
|
+
server: target.server,
|
|
30996
|
+
error,
|
|
30997
|
+
error_description: description ?? void 0
|
|
30998
|
+
}));
|
|
30999
|
+
}
|
|
30786
31000
|
function normalizeMcpOAuthError(server, error) {
|
|
30787
31001
|
if ((server.auth?.type === "oauth2" || server.auth?.type === "oidc") && !server.auth.clientId && !server.auth.clientMetadataUrl && error instanceof Error && error.message.includes("does not support dynamic client registration")) return new CapletsError("AUTH_FAILED", "OAuth is not available for this server without a host-specific OAuth app or PAT auth", {
|
|
30788
31002
|
server: server.server,
|
|
@@ -30790,6 +31004,75 @@ function normalizeMcpOAuthError(server, error) {
|
|
|
30790
31004
|
});
|
|
30791
31005
|
return error;
|
|
30792
31006
|
}
|
|
31007
|
+
async function startGenericOAuthFlow(target, options) {
|
|
31008
|
+
if (target.auth?.type !== "oauth2" && target.auth?.type !== "oidc") throw new CapletsError("REQUEST_INVALID", `${target.server} is not configured for OAuth`);
|
|
31009
|
+
const authConfig = target.auth;
|
|
31010
|
+
const redirectUri = authConfig.redirectUri ?? options.redirectUri;
|
|
31011
|
+
const verifier = base64url(randomBytes(32));
|
|
31012
|
+
const state = base64url(randomBytes(24));
|
|
31013
|
+
const allowLoopbackHttp = isLoopbackDevelopmentTarget(target, authConfig);
|
|
31014
|
+
const metadata = await discoverAuthorizationServer(target, authConfig, allowLoopbackHttp);
|
|
31015
|
+
const authorizationEndpoint = authConfig.authorizationUrl ?? metadata.authorization_endpoint;
|
|
31016
|
+
const tokenEndpoint = authConfig.tokenUrl ?? metadata.token_endpoint;
|
|
31017
|
+
if (!authorizationEndpoint || !tokenEndpoint) throw new CapletsError("AUTH_FAILED", "OAuth metadata is missing endpoints", { server: target.server });
|
|
31018
|
+
assertAllowedAuthUrl(authorizationEndpoint, "authorization endpoint", allowLoopbackHttp);
|
|
31019
|
+
assertAllowedAuthUrl(tokenEndpoint, "token endpoint", allowLoopbackHttp);
|
|
31020
|
+
const client = await resolveGenericClient(target, authConfig, metadata, redirectUri, allowLoopbackHttp);
|
|
31021
|
+
const scope = scopesFor(authConfig);
|
|
31022
|
+
const authorizationUrl = new URL(authorizationEndpoint);
|
|
31023
|
+
authorizationUrl.searchParams.set("response_type", "code");
|
|
31024
|
+
authorizationUrl.searchParams.set("client_id", client.clientId);
|
|
31025
|
+
authorizationUrl.searchParams.set("redirect_uri", redirectUri);
|
|
31026
|
+
authorizationUrl.searchParams.set("code_challenge", pkceChallenge(verifier));
|
|
31027
|
+
authorizationUrl.searchParams.set("code_challenge_method", "S256");
|
|
31028
|
+
authorizationUrl.searchParams.set("state", state);
|
|
31029
|
+
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
|
31030
|
+
options.print?.(`Open this URL to authorize ${target.server}:\n${authorizationUrl.toString()}`);
|
|
31031
|
+
return {
|
|
31032
|
+
authorizationUrl: authorizationUrl.toString(),
|
|
31033
|
+
complete: async (callbackUrl) => {
|
|
31034
|
+
assertNoOAuthCallbackError(target, callbackUrl);
|
|
31035
|
+
const completion = extractCompletion(callbackUrl);
|
|
31036
|
+
if (completion.state !== state) throw new CapletsError("AUTH_FAILED", "OAuth callback state did not match");
|
|
31037
|
+
const params = new URLSearchParams({
|
|
31038
|
+
grant_type: "authorization_code",
|
|
31039
|
+
code: completion.code,
|
|
31040
|
+
redirect_uri: redirectUri,
|
|
31041
|
+
client_id: client.clientId,
|
|
31042
|
+
code_verifier: verifier
|
|
31043
|
+
});
|
|
31044
|
+
if (client.clientSecret) params.set("client_secret", client.clientSecret);
|
|
31045
|
+
const tokenResponse = await fetchJson(tokenEndpoint, target.requestTimeoutMs, {
|
|
31046
|
+
method: "POST",
|
|
31047
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
31048
|
+
body: params.toString()
|
|
31049
|
+
}, allowLoopbackHttp);
|
|
31050
|
+
const idToken = asString(tokenResponse.id_token);
|
|
31051
|
+
const idClaims = parseJwtPayload(idToken);
|
|
31052
|
+
validateOidcToken(authConfig, metadata, idToken, idClaims, client.clientId);
|
|
31053
|
+
writeTokenBundle(stripUndefined({
|
|
31054
|
+
server: target.server,
|
|
31055
|
+
authType: authConfig.type,
|
|
31056
|
+
accessToken: requireString(tokenResponse.access_token, "access_token"),
|
|
31057
|
+
refreshToken: asString(tokenResponse.refresh_token),
|
|
31058
|
+
tokenType: asString(tokenResponse.token_type),
|
|
31059
|
+
expiresAt: typeof tokenResponse.expires_in === "number" ? new Date(Date.now() + tokenResponse.expires_in * 1e3).toISOString() : void 0,
|
|
31060
|
+
scope: asString(tokenResponse.scope) ?? scope,
|
|
31061
|
+
idToken,
|
|
31062
|
+
issuer: asString(idClaims?.iss) ?? metadata.issuer ?? authConfig.issuer,
|
|
31063
|
+
subject: asString(idClaims?.sub),
|
|
31064
|
+
clientId: client.clientId,
|
|
31065
|
+
clientSecret: client.clientSecret,
|
|
31066
|
+
protectedResourceOrigin: protectedResourceOrigin(target, authConfig),
|
|
31067
|
+
metadata: redactSecrets({
|
|
31068
|
+
protectedResource: target.url ?? target.baseUrl ?? target.specUrl,
|
|
31069
|
+
authorizationServer: metadata,
|
|
31070
|
+
dynamicClient: client.dynamic ? { client_id: client.clientId } : void 0
|
|
31071
|
+
})
|
|
31072
|
+
}), options.authDir);
|
|
31073
|
+
}
|
|
31074
|
+
};
|
|
31075
|
+
}
|
|
30793
31076
|
async function runGenericOAuthFlow(target, options = {}) {
|
|
30794
31077
|
if (target.auth?.type !== "oauth2" && target.auth?.type !== "oidc") throw new CapletsError("REQUEST_INVALID", `${target.server} is not configured for OAuth`);
|
|
30795
31078
|
const authConfig = target.auth;
|
|
@@ -30822,7 +31105,7 @@ async function runGenericOAuthFlow(target, options = {}) {
|
|
|
30822
31105
|
authorizationUrl.searchParams.set("state", state);
|
|
30823
31106
|
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
|
30824
31107
|
options.print?.(`Open this URL to authorize ${target.server}:\n${authorizationUrl.toString()}`);
|
|
30825
|
-
if (!options.noOpen) await (options.open ? options.open(authorizationUrl.toString()) : openBrowser(authorizationUrl.toString()));
|
|
31108
|
+
if (!options.noOpen) await (options.open ? options.open(authorizationUrl.toString()) : openBrowser$1(authorizationUrl.toString()));
|
|
30826
31109
|
const manualInput = options.manualInput ?? (options.noOpen ? await options.readManualInput?.() : void 0);
|
|
30827
31110
|
const completion = manualInput ? extractCompletion(manualInput) : await callback.waitForCode(() => callbackCode ? {
|
|
30828
31111
|
code: callbackCode,
|
|
@@ -30925,7 +31208,7 @@ async function createLoopbackCallback(onCallback) {
|
|
|
30925
31208
|
close: () => new Promise((resolve) => server.close(() => resolve()))
|
|
30926
31209
|
};
|
|
30927
31210
|
}
|
|
30928
|
-
async function openBrowser(url) {
|
|
31211
|
+
async function openBrowser$1(url) {
|
|
30929
31212
|
const { spawn } = await import("node:child_process");
|
|
30930
31213
|
spawn(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open", process.platform === "win32" ? [
|
|
30931
31214
|
"/c",
|
|
@@ -31135,14 +31418,28 @@ var DownstreamManager = class {
|
|
|
31135
31418
|
async checkServer(server) {
|
|
31136
31419
|
const startedAt = Date.now();
|
|
31137
31420
|
try {
|
|
31421
|
+
const capabilities = (await this.connect(server)).client.getServerCapabilities() ?? {};
|
|
31138
31422
|
const tools = await this.refreshTools(server, true);
|
|
31139
31423
|
this.registry.setStatus(server.server, "available");
|
|
31140
|
-
|
|
31424
|
+
const result = {
|
|
31141
31425
|
id: server.server,
|
|
31142
31426
|
status: "available",
|
|
31427
|
+
capabilities: {
|
|
31428
|
+
tools: Boolean(capabilities.tools),
|
|
31429
|
+
resources: Boolean(capabilities.resources),
|
|
31430
|
+
resourceTemplates: Boolean(capabilities.resources),
|
|
31431
|
+
prompts: Boolean(capabilities.prompts),
|
|
31432
|
+
completions: Boolean(capabilities.completions)
|
|
31433
|
+
},
|
|
31143
31434
|
toolCount: tools.length,
|
|
31144
31435
|
elapsedMs: Date.now() - startedAt
|
|
31145
31436
|
};
|
|
31437
|
+
if (capabilities.resources) Object.assign(result, {
|
|
31438
|
+
resourceCount: (await this.listResources(server, true)).length,
|
|
31439
|
+
resourceTemplateCount: (await this.listResourceTemplates(server, true)).length
|
|
31440
|
+
});
|
|
31441
|
+
if (capabilities.prompts) Object.assign(result, { promptCount: (await this.listPrompts(server, true)).length });
|
|
31442
|
+
return result;
|
|
31146
31443
|
} catch (error) {
|
|
31147
31444
|
const safe = toSafeError(error, "SERVER_UNAVAILABLE");
|
|
31148
31445
|
this.registry.setStatus(server.server, "unavailable", safe);
|
|
@@ -31184,6 +31481,86 @@ var DownstreamManager = class {
|
|
|
31184
31481
|
throw new CapletsError("DOWNSTREAM_TOOL_ERROR", `Downstream tool failed for ${server.server}/${toolName}`, toSafeError(error));
|
|
31185
31482
|
}
|
|
31186
31483
|
}
|
|
31484
|
+
async listResources(server, force = false) {
|
|
31485
|
+
const connection = await this.assertCapability(server, "resources");
|
|
31486
|
+
if (!force && connection.resources && this.isCacheFresh(connection.resourcesFetchedAt, server.toolCacheTtlMs)) return connection.resources;
|
|
31487
|
+
const resources = [];
|
|
31488
|
+
let cursor;
|
|
31489
|
+
do {
|
|
31490
|
+
const result = await connection.client.listResources(cursor ? { cursor } : void 0, { timeout: server.startupTimeoutMs });
|
|
31491
|
+
resources.push(...result.resources ?? []);
|
|
31492
|
+
cursor = result.nextCursor;
|
|
31493
|
+
} while (cursor);
|
|
31494
|
+
connection.resources = resources;
|
|
31495
|
+
connection.resourcesFetchedAt = Date.now();
|
|
31496
|
+
return resources;
|
|
31497
|
+
}
|
|
31498
|
+
async listResourceTemplates(server, force = false) {
|
|
31499
|
+
const connection = await this.assertCapability(server, "resources");
|
|
31500
|
+
if (!force && connection.resourceTemplates && this.isCacheFresh(connection.resourceTemplatesFetchedAt, server.toolCacheTtlMs)) return connection.resourceTemplates;
|
|
31501
|
+
const resourceTemplates = [];
|
|
31502
|
+
let cursor;
|
|
31503
|
+
do {
|
|
31504
|
+
const result = await connection.client.listResourceTemplates(cursor ? { cursor } : void 0, { timeout: server.startupTimeoutMs });
|
|
31505
|
+
resourceTemplates.push(...result.resourceTemplates ?? []);
|
|
31506
|
+
cursor = result.nextCursor;
|
|
31507
|
+
} while (cursor);
|
|
31508
|
+
connection.resourceTemplates = resourceTemplates;
|
|
31509
|
+
connection.resourceTemplatesFetchedAt = Date.now();
|
|
31510
|
+
return resourceTemplates;
|
|
31511
|
+
}
|
|
31512
|
+
async readResource(server, uri) {
|
|
31513
|
+
const connection = await this.assertCapability(server, "resources");
|
|
31514
|
+
try {
|
|
31515
|
+
return await connection.client.readResource({ uri }, { timeout: server.callTimeoutMs });
|
|
31516
|
+
} catch (error) {
|
|
31517
|
+
throw new CapletsError("DOWNSTREAM_RESOURCE_ERROR", `Downstream resource read failed for ${server.server}/${uri}`, toSafeError(error));
|
|
31518
|
+
}
|
|
31519
|
+
}
|
|
31520
|
+
async listPrompts(server, force = false) {
|
|
31521
|
+
const connection = await this.assertCapability(server, "prompts");
|
|
31522
|
+
if (!force && connection.prompts && this.isCacheFresh(connection.promptsFetchedAt, server.toolCacheTtlMs)) return connection.prompts;
|
|
31523
|
+
const prompts = [];
|
|
31524
|
+
let cursor;
|
|
31525
|
+
do {
|
|
31526
|
+
const result = await connection.client.listPrompts(cursor ? { cursor } : void 0, { timeout: server.startupTimeoutMs });
|
|
31527
|
+
prompts.push(...result.prompts ?? []);
|
|
31528
|
+
cursor = result.nextCursor;
|
|
31529
|
+
} while (cursor);
|
|
31530
|
+
connection.prompts = prompts;
|
|
31531
|
+
connection.promptsFetchedAt = Date.now();
|
|
31532
|
+
return prompts;
|
|
31533
|
+
}
|
|
31534
|
+
async getPrompt(server, promptName, args) {
|
|
31535
|
+
if (!(await this.listPrompts(server)).some((prompt) => prompt.name === promptName)) throw new CapletsError("PROMPT_NOT_FOUND", `Prompt ${promptName} was not found on ${server.server}`);
|
|
31536
|
+
const connection = await this.connect(server);
|
|
31537
|
+
try {
|
|
31538
|
+
return await connection.client.getPrompt({
|
|
31539
|
+
name: promptName,
|
|
31540
|
+
arguments: stringifyPromptArgs(args)
|
|
31541
|
+
}, { timeout: server.callTimeoutMs });
|
|
31542
|
+
} catch (error) {
|
|
31543
|
+
throw new CapletsError("DOWNSTREAM_PROMPT_ERROR", `Downstream prompt failed for ${server.server}/${promptName}`, toSafeError(error));
|
|
31544
|
+
}
|
|
31545
|
+
}
|
|
31546
|
+
async complete(server, request) {
|
|
31547
|
+
const connection = await this.assertCapability(server, "completions");
|
|
31548
|
+
const params = {
|
|
31549
|
+
ref: request.ref.type === "prompt" ? {
|
|
31550
|
+
type: "ref/prompt",
|
|
31551
|
+
name: request.ref.name
|
|
31552
|
+
} : {
|
|
31553
|
+
type: "ref/resource",
|
|
31554
|
+
uri: request.ref.uri
|
|
31555
|
+
},
|
|
31556
|
+
argument: request.argument
|
|
31557
|
+
};
|
|
31558
|
+
try {
|
|
31559
|
+
return await connection.client.complete(params, { timeout: server.callTimeoutMs });
|
|
31560
|
+
} catch (error) {
|
|
31561
|
+
throw new CapletsError("DOWNSTREAM_COMPLETION_ERROR", `Downstream completion failed for ${server.server}`, toSafeError(error));
|
|
31562
|
+
}
|
|
31563
|
+
}
|
|
31187
31564
|
compact(server, tool) {
|
|
31188
31565
|
return {
|
|
31189
31566
|
id: server.server,
|
|
@@ -31193,9 +31570,75 @@ var DownstreamManager = class {
|
|
|
31193
31570
|
hasOutputSchema: Boolean(tool.outputSchema)
|
|
31194
31571
|
};
|
|
31195
31572
|
}
|
|
31573
|
+
compactResource(server, resource) {
|
|
31574
|
+
return {
|
|
31575
|
+
id: server.server,
|
|
31576
|
+
kind: "resource",
|
|
31577
|
+
uri: resource.uri,
|
|
31578
|
+
...resource.name ? { name: resource.name } : {},
|
|
31579
|
+
...resource.description ? { description: resource.description } : {},
|
|
31580
|
+
...resource.mimeType ? { mimeType: resource.mimeType } : {},
|
|
31581
|
+
...typeof resource.size === "number" ? { size: resource.size } : {}
|
|
31582
|
+
};
|
|
31583
|
+
}
|
|
31584
|
+
compactResourceTemplate(server, template) {
|
|
31585
|
+
return {
|
|
31586
|
+
id: server.server,
|
|
31587
|
+
kind: "resourceTemplate",
|
|
31588
|
+
uriTemplate: template.uriTemplate,
|
|
31589
|
+
...template.name ? { name: template.name } : {},
|
|
31590
|
+
...template.description ? { description: template.description } : {},
|
|
31591
|
+
...template.mimeType ? { mimeType: template.mimeType } : {}
|
|
31592
|
+
};
|
|
31593
|
+
}
|
|
31594
|
+
compactPrompt(server, prompt) {
|
|
31595
|
+
return {
|
|
31596
|
+
id: server.server,
|
|
31597
|
+
prompt: prompt.name,
|
|
31598
|
+
...prompt.description ? { description: prompt.description } : {},
|
|
31599
|
+
...prompt.arguments ? { arguments: prompt.arguments } : {}
|
|
31600
|
+
};
|
|
31601
|
+
}
|
|
31602
|
+
searchResources(server, resources, query, limit) {
|
|
31603
|
+
const lower = query.toLocaleLowerCase();
|
|
31604
|
+
return resources.map((resource) => this.compactResource(server, resource)).filter((resource) => [
|
|
31605
|
+
resource.uri,
|
|
31606
|
+
resource.name,
|
|
31607
|
+
resource.description,
|
|
31608
|
+
resource.mimeType
|
|
31609
|
+
].some((value) => value?.toLocaleLowerCase().includes(lower))).slice(0, limit);
|
|
31610
|
+
}
|
|
31611
|
+
searchResourceTemplates(server, templates, query, limit) {
|
|
31612
|
+
const lower = query.toLocaleLowerCase();
|
|
31613
|
+
return templates.map((template) => this.compactResourceTemplate(server, template)).filter((template) => [
|
|
31614
|
+
template.uriTemplate,
|
|
31615
|
+
template.name,
|
|
31616
|
+
template.description,
|
|
31617
|
+
template.mimeType
|
|
31618
|
+
].some((value) => value?.toLocaleLowerCase().includes(lower))).slice(0, limit);
|
|
31619
|
+
}
|
|
31620
|
+
searchPrompts(server, prompts, query, limit) {
|
|
31621
|
+
const lower = query.toLocaleLowerCase();
|
|
31622
|
+
return prompts.map((prompt) => this.compactPrompt(server, prompt)).filter((prompt) => [
|
|
31623
|
+
prompt.prompt,
|
|
31624
|
+
prompt.description,
|
|
31625
|
+
...(prompt.arguments ?? []).flatMap((arg) => [arg.name, arg.description])
|
|
31626
|
+
].some((value) => value?.toLocaleLowerCase().includes(lower))).slice(0, limit);
|
|
31627
|
+
}
|
|
31196
31628
|
search(server, tools, query, limit) {
|
|
31197
31629
|
return searchToolList(tools, query, limit, (tool) => this.compact(server, tool));
|
|
31198
31630
|
}
|
|
31631
|
+
async assertCapability(server, capability) {
|
|
31632
|
+
const connection = await this.connect(server);
|
|
31633
|
+
if (!connection.client.getServerCapabilities()?.[capability]) throw new CapletsError("UNSUPPORTED_CAPABILITY", `${server.server} does not advertise MCP ${capability}`, {
|
|
31634
|
+
server: server.server,
|
|
31635
|
+
capability
|
|
31636
|
+
});
|
|
31637
|
+
return connection;
|
|
31638
|
+
}
|
|
31639
|
+
isCacheFresh(fetchedAt, ttlMs) {
|
|
31640
|
+
return fetchedAt !== void 0 && ttlMs > 0 && Date.now() - fetchedAt <= ttlMs;
|
|
31641
|
+
}
|
|
31199
31642
|
async refreshTools(server, force) {
|
|
31200
31643
|
const connection = await this.connect(server);
|
|
31201
31644
|
const now = Date.now();
|
|
@@ -31239,6 +31682,20 @@ var DownstreamManager = class {
|
|
|
31239
31682
|
transport,
|
|
31240
31683
|
configFingerprint: expectedFingerprint
|
|
31241
31684
|
};
|
|
31685
|
+
client.setNotificationHandler(ToolListChangedNotificationSchema, () => {
|
|
31686
|
+
connection.tools = void 0;
|
|
31687
|
+
connection.toolsFetchedAt = void 0;
|
|
31688
|
+
});
|
|
31689
|
+
client.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
|
|
31690
|
+
connection.resources = void 0;
|
|
31691
|
+
connection.resourcesFetchedAt = void 0;
|
|
31692
|
+
connection.resourceTemplates = void 0;
|
|
31693
|
+
connection.resourceTemplatesFetchedAt = void 0;
|
|
31694
|
+
});
|
|
31695
|
+
client.setNotificationHandler(PromptListChangedNotificationSchema, () => {
|
|
31696
|
+
connection.prompts = void 0;
|
|
31697
|
+
connection.promptsFetchedAt = void 0;
|
|
31698
|
+
});
|
|
31242
31699
|
pendingConnection = connection;
|
|
31243
31700
|
this.connecting.set(server.server, connection);
|
|
31244
31701
|
transport.onclose = () => {
|
|
@@ -31369,6 +31826,18 @@ function nearbyToolNames(tools, needle) {
|
|
|
31369
31826
|
function isTimeoutLike(error) {
|
|
31370
31827
|
return error instanceof Error && /timeout|timed out|aborted/i.test(error.message);
|
|
31371
31828
|
}
|
|
31829
|
+
function stringifyPromptArgs(args) {
|
|
31830
|
+
const stringified = {};
|
|
31831
|
+
for (const [key, value] of Object.entries(args)) {
|
|
31832
|
+
if (typeof value === "string") {
|
|
31833
|
+
stringified[key] = value;
|
|
31834
|
+
continue;
|
|
31835
|
+
}
|
|
31836
|
+
const serialized = JSON.stringify(value);
|
|
31837
|
+
if (typeof serialized === "string") stringified[key] = serialized;
|
|
31838
|
+
}
|
|
31839
|
+
return stringified;
|
|
31840
|
+
}
|
|
31372
31841
|
function isAuthRemediationError(error) {
|
|
31373
31842
|
return error instanceof CapletsError && (error.code === "AUTH_REQUIRED" || error.code === "AUTH_FAILED");
|
|
31374
31843
|
}
|
|
@@ -56230,7 +56699,7 @@ function capabilityDescription(server) {
|
|
|
56230
56699
|
return [
|
|
56231
56700
|
`${server.name} Caplet.`,
|
|
56232
56701
|
server.description,
|
|
56233
|
-
"Use get_caplet for details when needed; use search_tools or list_tools to discover downstream operations."
|
|
56702
|
+
server.backend === "mcp" ? "Use get_caplet for details when needed; use tools for actions, resources for readable context, prompts for reusable workflows, and complete for prompt/resource-template arguments." : "Use get_caplet for details when needed; use search_tools or list_tools to discover downstream operations."
|
|
56234
56703
|
].filter(Boolean).join(" ");
|
|
56235
56704
|
}
|
|
56236
56705
|
var ServerRegistry = class {
|
|
@@ -56446,17 +56915,9 @@ function cloneJsonValue(value) {
|
|
|
56446
56915
|
function throwInvalid(message) {
|
|
56447
56916
|
throw new CapletsError("REQUEST_INVALID", message);
|
|
56448
56917
|
}
|
|
56449
|
-
const generatedToolInputSchema = object$1({
|
|
56450
|
-
operation: _enum(operations).describe(generatedToolInputDescriptions.operation),
|
|
56451
|
-
query: string().optional().describe(generatedToolInputDescriptions.query),
|
|
56452
|
-
limit: number$1().int().positive().optional().describe(generatedToolInputDescriptions.limit),
|
|
56453
|
-
tool: string().optional().describe(generatedToolInputDescriptions.tool),
|
|
56454
|
-
arguments: record(string(), unknown()).optional().describe(generatedToolInputDescriptions.arguments),
|
|
56455
|
-
fields: array(string().min(1)).min(1).optional().describe(generatedToolInputDescriptions.fields)
|
|
56456
|
-
}).strict();
|
|
56457
56918
|
async function handleServerTool(server, request, registry, downstream, openapi, graphql, http, cli, caplets) {
|
|
56458
56919
|
const startedAt = Date.now();
|
|
56459
|
-
const parsed = validateOperationRequest(request, registry.config.options.maxSearchLimit);
|
|
56920
|
+
const parsed = validateOperationRequest(request, registry.config.options.maxSearchLimit, server.backend);
|
|
56460
56921
|
switch (parsed.operation) {
|
|
56461
56922
|
case "get_caplet": return jsonResult(registry.detail(server), metadataFor(server, "get_caplet", void 0, startedAt));
|
|
56462
56923
|
case "check_backend": return jsonResult(await backendFor(server, downstream, openapi, graphql, http, cli, caplets).check(server), metadataFor(server, "check_backend", void 0, startedAt));
|
|
@@ -56497,11 +56958,75 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
56497
56958
|
validateFieldSelection(tool.outputSchema, parsed.fields);
|
|
56498
56959
|
return annotateCallToolResult(projectCallToolResult(await backend.callTool(server, parsed.tool, parsed.arguments), tool.outputSchema, parsed.fields), metadataFor(server, "call_tool", parsed.tool, startedAt));
|
|
56499
56960
|
}
|
|
56961
|
+
case "list_resources": {
|
|
56962
|
+
const backend = mcpBackendFor(server, downstream);
|
|
56963
|
+
const resources = await backend.listResources(server);
|
|
56964
|
+
const templates = await backend.listResourceTemplates(server);
|
|
56965
|
+
const limit = parsed.limit ?? resources.length + templates.length;
|
|
56966
|
+
return jsonResult({
|
|
56967
|
+
id: server.server,
|
|
56968
|
+
name: server.name,
|
|
56969
|
+
resources: resources.slice(0, limit).map((resource) => backend.compactResource(server, resource)),
|
|
56970
|
+
resourceTemplates: templates.slice(0, Math.max(0, limit - resources.length)).map((template) => backend.compactResourceTemplate(server, template))
|
|
56971
|
+
}, metadataFor(server, "list_resources", void 0, startedAt));
|
|
56972
|
+
}
|
|
56973
|
+
case "search_resources": {
|
|
56974
|
+
const backend = mcpBackendFor(server, downstream);
|
|
56975
|
+
const resources = await backend.listResources(server);
|
|
56976
|
+
const templates = await backend.listResourceTemplates(server);
|
|
56977
|
+
const limit = parsed.limit ?? registry.config.options.defaultSearchLimit;
|
|
56978
|
+
const resourceMatches = backend.searchResources(server, resources, parsed.query, limit);
|
|
56979
|
+
const templateMatches = backend.searchResourceTemplates(server, templates, parsed.query, Math.max(0, limit - resourceMatches.length));
|
|
56980
|
+
return jsonResult({
|
|
56981
|
+
id: server.server,
|
|
56982
|
+
name: server.name,
|
|
56983
|
+
query: parsed.query,
|
|
56984
|
+
matches: [...resourceMatches, ...templateMatches]
|
|
56985
|
+
}, metadataFor(server, "search_resources", void 0, startedAt));
|
|
56986
|
+
}
|
|
56987
|
+
case "list_resource_templates": {
|
|
56988
|
+
const backend = mcpBackendFor(server, downstream);
|
|
56989
|
+
const templates = await backend.listResourceTemplates(server);
|
|
56990
|
+
const limit = parsed.limit ?? templates.length;
|
|
56991
|
+
return jsonResult({
|
|
56992
|
+
id: server.server,
|
|
56993
|
+
name: server.name,
|
|
56994
|
+
resourceTemplates: templates.slice(0, limit).map((template) => backend.compactResourceTemplate(server, template))
|
|
56995
|
+
}, metadataFor(server, "list_resource_templates", void 0, startedAt));
|
|
56996
|
+
}
|
|
56997
|
+
case "read_resource": return annotateMcpResult(await mcpBackendFor(server, downstream).readResource(server, parsed.uri), metadataFor(server, "read_resource", { uri: parsed.uri }, startedAt));
|
|
56998
|
+
case "list_prompts": {
|
|
56999
|
+
const backend = mcpBackendFor(server, downstream);
|
|
57000
|
+
const prompts = await backend.listPrompts(server);
|
|
57001
|
+
const limit = parsed.limit ?? prompts.length;
|
|
57002
|
+
return jsonResult({
|
|
57003
|
+
id: server.server,
|
|
57004
|
+
name: server.name,
|
|
57005
|
+
prompts: prompts.slice(0, limit).map((prompt) => backend.compactPrompt(server, prompt))
|
|
57006
|
+
}, metadataFor(server, "list_prompts", void 0, startedAt));
|
|
57007
|
+
}
|
|
57008
|
+
case "search_prompts": {
|
|
57009
|
+
const backend = mcpBackendFor(server, downstream);
|
|
57010
|
+
const prompts = await backend.listPrompts(server);
|
|
57011
|
+
const limit = parsed.limit ?? registry.config.options.defaultSearchLimit;
|
|
57012
|
+
return jsonResult({
|
|
57013
|
+
id: server.server,
|
|
57014
|
+
name: server.name,
|
|
57015
|
+
query: parsed.query,
|
|
57016
|
+
prompts: backend.searchPrompts(server, prompts, parsed.query, limit)
|
|
57017
|
+
}, metadataFor(server, "search_prompts", void 0, startedAt));
|
|
57018
|
+
}
|
|
57019
|
+
case "get_prompt": return annotateMcpResult(await mcpBackendFor(server, downstream).getPrompt(server, parsed.prompt, parsed.arguments), metadataFor(server, "get_prompt", { prompt: parsed.prompt }, startedAt));
|
|
57020
|
+
case "complete": return annotateMcpResult(await mcpBackendFor(server, downstream).complete(server, {
|
|
57021
|
+
ref: parsed.ref,
|
|
57022
|
+
argument: parsed.argument
|
|
57023
|
+
}), metadataFor(server, "complete", void 0, startedAt));
|
|
56500
57024
|
}
|
|
56501
57025
|
}
|
|
56502
|
-
function validateOperationRequest(request, maxSearchLimit) {
|
|
56503
|
-
|
|
56504
|
-
|
|
57026
|
+
function validateOperationRequest(request, maxSearchLimit, backend = "tool") {
|
|
57027
|
+
const result = generatedToolInputSchemaForCaplet({ backend }).safeParse(request);
|
|
57028
|
+
if (request && typeof request === "object" && "operation" in request && typeof request.operation === "string" && !mcpOperations.includes(request.operation)) throw new CapletsError("UNKNOWN_OPERATION", `Unknown operation: ${request.operation}`);
|
|
57029
|
+
if (request && typeof request === "object" && "operation" in request && typeof request.operation === "string" && backend !== "mcp" && mcpOperations.includes(request.operation) && !operations.includes(request.operation)) throw new CapletsError("UNSUPPORTED_OPERATION", `${request.operation} is only available for MCP-backed Caplets`);
|
|
56505
57030
|
if (!result.success) throw new CapletsError("REQUEST_INVALID", "Generated server tool request is invalid", result.error.issues);
|
|
56506
57031
|
const value = result.data;
|
|
56507
57032
|
const keys = Object.keys(value).sort();
|
|
@@ -56548,7 +57073,7 @@ function validateOperationRequest(request, maxSearchLimit) {
|
|
|
56548
57073
|
"fields"
|
|
56549
57074
|
]);
|
|
56550
57075
|
if (!value.tool) throw new CapletsError("REQUEST_INVALID", "call_tool requires tool");
|
|
56551
|
-
if (!isPlainObject$
|
|
57076
|
+
if (!isPlainObject$7(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
|
|
56552
57077
|
return value.fields === void 0 ? {
|
|
56553
57078
|
operation: "call_tool",
|
|
56554
57079
|
tool: value.tool,
|
|
@@ -56559,23 +57084,82 @@ function validateOperationRequest(request, maxSearchLimit) {
|
|
|
56559
57084
|
arguments: value.arguments,
|
|
56560
57085
|
fields: value.fields
|
|
56561
57086
|
};
|
|
57087
|
+
case "list_resources":
|
|
57088
|
+
case "list_resource_templates":
|
|
57089
|
+
case "list_prompts":
|
|
57090
|
+
allowed(["limit"]);
|
|
57091
|
+
if (value.limit !== void 0 && value.limit > maxSearchLimit) throw new CapletsError("REQUEST_INVALID", `${value.operation} limit must be <= ${maxSearchLimit}`);
|
|
57092
|
+
return value.limit === void 0 ? { operation: value.operation } : {
|
|
57093
|
+
operation: value.operation,
|
|
57094
|
+
limit: value.limit
|
|
57095
|
+
};
|
|
57096
|
+
case "search_resources":
|
|
57097
|
+
case "search_prompts":
|
|
57098
|
+
allowed(["query", "limit"]);
|
|
57099
|
+
if (!value.query) throw new CapletsError("REQUEST_INVALID", `${value.operation} requires query`);
|
|
57100
|
+
if (value.limit !== void 0 && value.limit > maxSearchLimit) throw new CapletsError("REQUEST_INVALID", `${value.operation} limit must be <= ${maxSearchLimit}`);
|
|
57101
|
+
return value.limit === void 0 ? {
|
|
57102
|
+
operation: value.operation,
|
|
57103
|
+
query: value.query
|
|
57104
|
+
} : {
|
|
57105
|
+
operation: value.operation,
|
|
57106
|
+
query: value.query,
|
|
57107
|
+
limit: value.limit
|
|
57108
|
+
};
|
|
57109
|
+
case "read_resource":
|
|
57110
|
+
allowed(["uri"]);
|
|
57111
|
+
if (!value.uri) throw new CapletsError("REQUEST_INVALID", "read_resource requires uri");
|
|
57112
|
+
return {
|
|
57113
|
+
operation: "read_resource",
|
|
57114
|
+
uri: value.uri
|
|
57115
|
+
};
|
|
57116
|
+
case "get_prompt":
|
|
57117
|
+
allowed(["prompt", "arguments"]);
|
|
57118
|
+
if (!value.prompt) throw new CapletsError("REQUEST_INVALID", "get_prompt requires prompt");
|
|
57119
|
+
if (value.arguments !== void 0 && !isPlainObject$7(value.arguments)) throw new CapletsError("REQUEST_INVALID", "get_prompt.arguments must be a JSON object");
|
|
57120
|
+
return {
|
|
57121
|
+
operation: "get_prompt",
|
|
57122
|
+
prompt: value.prompt,
|
|
57123
|
+
arguments: value.arguments ?? {}
|
|
57124
|
+
};
|
|
57125
|
+
case "complete":
|
|
57126
|
+
allowed(["ref", "argument"]);
|
|
57127
|
+
if (!value.ref) throw new CapletsError("REQUEST_INVALID", "complete requires ref");
|
|
57128
|
+
if (!value.argument) throw new CapletsError("REQUEST_INVALID", "complete requires argument");
|
|
57129
|
+
return {
|
|
57130
|
+
operation: "complete",
|
|
57131
|
+
ref: value.ref,
|
|
57132
|
+
argument: value.argument
|
|
57133
|
+
};
|
|
56562
57134
|
}
|
|
56563
|
-
|
|
57135
|
+
throw new CapletsError("INTERNAL_ERROR", "Unhandled operation");
|
|
56564
57136
|
}
|
|
56565
|
-
function
|
|
56566
|
-
throw new CapletsError("
|
|
57137
|
+
function mcpBackendFor(server, downstream) {
|
|
57138
|
+
if (server.backend !== "mcp") throw new CapletsError("UNSUPPORTED_OPERATION", "MCP resource, prompt, and completion operations require an MCP-backed Caplet");
|
|
57139
|
+
return downstream;
|
|
56567
57140
|
}
|
|
56568
|
-
function metadataFor(server, operation,
|
|
57141
|
+
function metadataFor(server, operation, target, startedAt) {
|
|
57142
|
+
const targetFields = typeof target === "string" ? { tool: target } : target ?? {};
|
|
56569
57143
|
return {
|
|
56570
57144
|
id: server.server,
|
|
56571
57145
|
name: server.name,
|
|
56572
57146
|
backend: server.backend,
|
|
56573
57147
|
operation,
|
|
56574
|
-
...
|
|
57148
|
+
...targetFields,
|
|
56575
57149
|
status: "ok",
|
|
56576
57150
|
...startedAt === void 0 ? {} : { elapsedMs: Date.now() - startedAt }
|
|
56577
57151
|
};
|
|
56578
57152
|
}
|
|
57153
|
+
function annotateMcpResult(result, metadata) {
|
|
57154
|
+
const existingMeta = result._meta;
|
|
57155
|
+
return {
|
|
57156
|
+
...result,
|
|
57157
|
+
_meta: {
|
|
57158
|
+
...isPlainObject$7(existingMeta) ? existingMeta : {},
|
|
57159
|
+
caplets: metadata
|
|
57160
|
+
}
|
|
57161
|
+
};
|
|
57162
|
+
}
|
|
56579
57163
|
function jsonResult(value, metadata) {
|
|
56580
57164
|
return {
|
|
56581
57165
|
content: [{
|
|
@@ -56599,7 +57183,7 @@ function annotateCallToolResult(result, metadata) {
|
|
|
56599
57183
|
return {
|
|
56600
57184
|
...result,
|
|
56601
57185
|
_meta: {
|
|
56602
|
-
...isPlainObject$
|
|
57186
|
+
...isPlainObject$7(existingMeta) ? existingMeta : {},
|
|
56603
57187
|
caplets: annotatedMetadata
|
|
56604
57188
|
}
|
|
56605
57189
|
};
|
|
@@ -56607,7 +57191,7 @@ function annotateCallToolResult(result, metadata) {
|
|
|
56607
57191
|
function projectCallToolResult(result, outputSchema, fields) {
|
|
56608
57192
|
if (result.isError === true) return result;
|
|
56609
57193
|
const structuredContent = result.structuredContent;
|
|
56610
|
-
if (!isPlainObject$
|
|
57194
|
+
if (!isPlainObject$7(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
|
|
56611
57195
|
const projected = projectStructuredContent(structuredContent, outputSchema, fields);
|
|
56612
57196
|
return {
|
|
56613
57197
|
...result,
|
|
@@ -56616,11 +57200,11 @@ function projectCallToolResult(result, outputSchema, fields) {
|
|
|
56616
57200
|
};
|
|
56617
57201
|
}
|
|
56618
57202
|
function extractArtifacts(result) {
|
|
56619
|
-
if (!isPlainObject$
|
|
57203
|
+
if (!isPlainObject$7(result) || !Array.isArray(result.content)) return [];
|
|
56620
57204
|
const artifacts = [];
|
|
56621
57205
|
const seen = /* @__PURE__ */ new Set();
|
|
56622
57206
|
for (const item of result.content) {
|
|
56623
|
-
if (!isPlainObject$
|
|
57207
|
+
if (!isPlainObject$7(item) || item.type !== "text" || typeof item.text !== "string") continue;
|
|
56624
57208
|
const text = item.text;
|
|
56625
57209
|
for (const link of parseMarkdownLinks(text)) {
|
|
56626
57210
|
const label = link.label;
|
|
@@ -56735,7 +57319,7 @@ function artifactKindFromText(text) {
|
|
|
56735
57319
|
if (/network[-_ ]?(?:log)?|har\b/.test(text)) return "network-log";
|
|
56736
57320
|
return "file";
|
|
56737
57321
|
}
|
|
56738
|
-
function isPlainObject$
|
|
57322
|
+
function isPlainObject$7(value) {
|
|
56739
57323
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
56740
57324
|
}
|
|
56741
57325
|
function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
|
|
@@ -57043,6 +57627,38 @@ var CapletsEngine = class {
|
|
|
57043
57627
|
return errorResult(error);
|
|
57044
57628
|
}
|
|
57045
57629
|
}
|
|
57630
|
+
async completeCliWords(words) {
|
|
57631
|
+
const { completeCliWords } = await Promise.resolve().then(() => completion_CxGG6ae3_exports).then((n) => n.r);
|
|
57632
|
+
return await completeCliWords(words, {
|
|
57633
|
+
config: this.registry.config,
|
|
57634
|
+
managers: {
|
|
57635
|
+
listTools: async (server) => this.listCompletionTools(server),
|
|
57636
|
+
listPrompts: async (server) => {
|
|
57637
|
+
if (server.backend !== "mcp") return [];
|
|
57638
|
+
return (await this.downstream.listPrompts(server)).map((prompt) => ({
|
|
57639
|
+
name: prompt.name,
|
|
57640
|
+
...prompt.description ? { description: prompt.description } : {}
|
|
57641
|
+
}));
|
|
57642
|
+
},
|
|
57643
|
+
listResources: async (server) => {
|
|
57644
|
+
if (server.backend !== "mcp") return [];
|
|
57645
|
+
return (await this.downstream.listResources(server)).map((resource) => ({
|
|
57646
|
+
uri: resource.uri,
|
|
57647
|
+
...resource.name ? { name: resource.name } : {},
|
|
57648
|
+
...resource.description ? { description: resource.description } : {}
|
|
57649
|
+
}));
|
|
57650
|
+
},
|
|
57651
|
+
listResourceTemplates: async (server) => {
|
|
57652
|
+
if (server.backend !== "mcp") return [];
|
|
57653
|
+
return (await this.downstream.listResourceTemplates(server)).map((template) => ({
|
|
57654
|
+
uriTemplate: template.uriTemplate,
|
|
57655
|
+
...template.name ? { name: template.name } : {},
|
|
57656
|
+
...template.description ? { description: template.description } : {}
|
|
57657
|
+
}));
|
|
57658
|
+
}
|
|
57659
|
+
}
|
|
57660
|
+
});
|
|
57661
|
+
}
|
|
57046
57662
|
async close() {
|
|
57047
57663
|
this.closed = true;
|
|
57048
57664
|
try {
|
|
@@ -57062,6 +57678,12 @@ var CapletsEngine = class {
|
|
|
57062
57678
|
this.reloadListeners.clear();
|
|
57063
57679
|
}
|
|
57064
57680
|
}
|
|
57681
|
+
async listCompletionTools(server) {
|
|
57682
|
+
return (server.backend === "mcp" ? await this.downstream.listTools(server) : server.backend === "openapi" ? await this.openapi.listTools(server) : server.backend === "graphql" ? await this.graphql.listTools(server) : server.backend === "http" ? await this.http.listTools(server) : server.backend === "cli" ? await this.cli.listTools(server) : await this.capletSets.listTools(server)).map((tool) => ({
|
|
57683
|
+
name: tool.name,
|
|
57684
|
+
...tool.description ? { description: tool.description } : {}
|
|
57685
|
+
}));
|
|
57686
|
+
}
|
|
57065
57687
|
async reloadOnce() {
|
|
57066
57688
|
if (this.closed) return false;
|
|
57067
57689
|
let nextConfig;
|
|
@@ -57259,6 +57881,649 @@ function isDirectory(path) {
|
|
|
57259
57881
|
return false;
|
|
57260
57882
|
}
|
|
57261
57883
|
}
|
|
57884
|
+
const DEFAULT_SERVER_USER = "caplets";
|
|
57885
|
+
function resolveCapletsMode(input = {}, env = process.env) {
|
|
57886
|
+
const mode = parseCapletsMode(input.mode ?? env.CAPLETS_MODE ?? "auto");
|
|
57887
|
+
if (mode === "local") return { mode: "local" };
|
|
57888
|
+
const rawUrl = nonEmpty$1(input.serverUrl, "serverUrl") ?? nonEmpty$1(env.CAPLETS_SERVER_URL, "CAPLETS_SERVER_URL");
|
|
57889
|
+
if (mode === "remote") {
|
|
57890
|
+
if (rawUrl === void 0) throw new CapletsError("REQUEST_INVALID", "CAPLETS_MODE=remote requires CAPLETS_SERVER_URL or serverUrl.");
|
|
57891
|
+
return { mode: "remote" };
|
|
57892
|
+
}
|
|
57893
|
+
return rawUrl === void 0 ? { mode: "local" } : { mode: "remote" };
|
|
57894
|
+
}
|
|
57895
|
+
function resolveCapletsServer(input = {}, env = process.env) {
|
|
57896
|
+
const rawUrl = nonEmpty$1(input.url, "url") ?? nonEmpty$1(env.CAPLETS_SERVER_URL, "CAPLETS_SERVER_URL");
|
|
57897
|
+
if (rawUrl === void 0) throw new CapletsError("REQUEST_INVALID", "CAPLETS_SERVER_URL or url is required.");
|
|
57898
|
+
const baseUrl = parseServerBaseUrl(rawUrl);
|
|
57899
|
+
const userWasExplicit = input.user !== void 0 || hasEnv$1(env.CAPLETS_SERVER_USER);
|
|
57900
|
+
const user = nonEmpty$1(input.user, "user") ?? nonEmpty$1(env.CAPLETS_SERVER_USER, "CAPLETS_SERVER_USER") ?? DEFAULT_SERVER_USER;
|
|
57901
|
+
const password = nonEmpty$1(input.password, "password") ?? nonEmpty$1(env.CAPLETS_SERVER_PASSWORD, "CAPLETS_SERVER_PASSWORD");
|
|
57902
|
+
if (userWasExplicit && password === void 0) throw new CapletsError("REQUEST_INVALID", "Caplets server Basic Auth requires a password; set CAPLETS_SERVER_PASSWORD or password.");
|
|
57903
|
+
const auth = password === void 0 ? {
|
|
57904
|
+
enabled: false,
|
|
57905
|
+
user
|
|
57906
|
+
} : {
|
|
57907
|
+
enabled: true,
|
|
57908
|
+
user,
|
|
57909
|
+
password
|
|
57910
|
+
};
|
|
57911
|
+
const requestInit = auth.enabled ? { headers: { Authorization: basicAuthHeader(auth.user, auth.password) } } : {};
|
|
57912
|
+
return {
|
|
57913
|
+
baseUrl,
|
|
57914
|
+
mcpUrl: mcpUrlForBase(baseUrl),
|
|
57915
|
+
controlUrl: controlUrlForBase(baseUrl),
|
|
57916
|
+
healthUrl: healthUrlForBase(baseUrl),
|
|
57917
|
+
auth,
|
|
57918
|
+
requestInit,
|
|
57919
|
+
...input.fetch ? { fetch: input.fetch } : {}
|
|
57920
|
+
};
|
|
57921
|
+
}
|
|
57922
|
+
function mcpUrlForBase(baseUrl) {
|
|
57923
|
+
return appendBasePath(baseUrl, "mcp");
|
|
57924
|
+
}
|
|
57925
|
+
function controlUrlForBase(baseUrl) {
|
|
57926
|
+
return appendBasePath(baseUrl, "control");
|
|
57927
|
+
}
|
|
57928
|
+
function healthUrlForBase(baseUrl) {
|
|
57929
|
+
return appendBasePath(baseUrl, "healthz");
|
|
57930
|
+
}
|
|
57931
|
+
function appendBasePath(baseUrl, path) {
|
|
57932
|
+
const url = new URL(baseUrl.href);
|
|
57933
|
+
url.pathname = `${url.pathname === "/" ? "" : url.pathname}/${path}`;
|
|
57934
|
+
return url;
|
|
57935
|
+
}
|
|
57936
|
+
function parseServerBaseUrl(value) {
|
|
57937
|
+
let url;
|
|
57938
|
+
try {
|
|
57939
|
+
url = new URL(value);
|
|
57940
|
+
} catch {
|
|
57941
|
+
throw new CapletsError("REQUEST_INVALID", "Invalid Caplets server URL.");
|
|
57942
|
+
}
|
|
57943
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") throw new CapletsError("REQUEST_INVALID", "Caplets server URL must not include username, password, query string, or fragment.");
|
|
57944
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopbackHost$1(url.hostname))) throw new CapletsError("REQUEST_INVALID", "Caplets server URL must use https except loopback development URLs.");
|
|
57945
|
+
url.pathname = url.pathname === "/" ? "/" : url.pathname.replace(/\/+$/u, "");
|
|
57946
|
+
return url;
|
|
57947
|
+
}
|
|
57948
|
+
function isLoopbackHost$1(host) {
|
|
57949
|
+
const normalized = host.toLocaleLowerCase();
|
|
57950
|
+
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
|
|
57951
|
+
}
|
|
57952
|
+
function parseCapletsMode(value) {
|
|
57953
|
+
if (value === "auto" || value === "local" || value === "remote") return value;
|
|
57954
|
+
throw new CapletsError("REQUEST_INVALID", `Expected CAPLETS_MODE to be auto, local, or remote, got ${value}`);
|
|
57955
|
+
}
|
|
57956
|
+
function basicAuthHeader(user, password) {
|
|
57957
|
+
return `Basic ${Buffer$1.from(`${user}:${password}`).toString("base64")}`;
|
|
57958
|
+
}
|
|
57959
|
+
function nonEmpty$1(value, label) {
|
|
57960
|
+
if (value === void 0) return;
|
|
57961
|
+
const trimmed = value.trim();
|
|
57962
|
+
if (!trimmed) throw new CapletsError("REQUEST_INVALID", `${label} must not be empty`);
|
|
57963
|
+
return trimmed;
|
|
57964
|
+
}
|
|
57965
|
+
function hasEnv$1(value) {
|
|
57966
|
+
return value !== void 0 && value.trim() !== "";
|
|
57967
|
+
}
|
|
57968
|
+
//#endregion
|
|
57969
|
+
//#region ../core/dist/completion-CxGG6ae3.js
|
|
57970
|
+
var completion_CxGG6ae3_exports = /* @__PURE__ */ __exportAll$1({
|
|
57971
|
+
a: () => formatCapletList,
|
|
57972
|
+
c: () => resolveCliConfigPaths,
|
|
57973
|
+
i: () => trailingSpaceCompletionToken,
|
|
57974
|
+
l: () => cliCommands,
|
|
57975
|
+
n: () => completionScript,
|
|
57976
|
+
o: () => formatConfigPaths,
|
|
57977
|
+
r: () => completion_exports,
|
|
57978
|
+
s: () => listCaplets,
|
|
57979
|
+
t: () => completeCliWords,
|
|
57980
|
+
u: () => completionShells
|
|
57981
|
+
});
|
|
57982
|
+
const completionShells = [
|
|
57983
|
+
"bash",
|
|
57984
|
+
"zsh",
|
|
57985
|
+
"fish",
|
|
57986
|
+
"powershell",
|
|
57987
|
+
"cmd"
|
|
57988
|
+
];
|
|
57989
|
+
const cliCommands = {
|
|
57990
|
+
completion: "completion",
|
|
57991
|
+
completeHidden: "__complete",
|
|
57992
|
+
serve: "serve",
|
|
57993
|
+
init: "init",
|
|
57994
|
+
list: "list",
|
|
57995
|
+
install: "install",
|
|
57996
|
+
add: "add",
|
|
57997
|
+
getCaplet: "get-caplet",
|
|
57998
|
+
checkBackend: "check-backend",
|
|
57999
|
+
listTools: "list-tools",
|
|
58000
|
+
searchTools: "search-tools",
|
|
58001
|
+
getTool: "get-tool",
|
|
58002
|
+
callTool: "call-tool",
|
|
58003
|
+
listResources: "list-resources",
|
|
58004
|
+
searchResources: "search-resources",
|
|
58005
|
+
listResourceTemplates: "list-resource-templates",
|
|
58006
|
+
readResource: "read-resource",
|
|
58007
|
+
listPrompts: "list-prompts",
|
|
58008
|
+
searchPrompts: "search-prompts",
|
|
58009
|
+
getPrompt: "get-prompt",
|
|
58010
|
+
complete: "complete",
|
|
58011
|
+
config: "config",
|
|
58012
|
+
auth: "auth"
|
|
58013
|
+
};
|
|
58014
|
+
const topLevelCommandNames = [
|
|
58015
|
+
cliCommands.serve,
|
|
58016
|
+
cliCommands.init,
|
|
58017
|
+
cliCommands.list,
|
|
58018
|
+
cliCommands.install,
|
|
58019
|
+
cliCommands.add,
|
|
58020
|
+
cliCommands.getCaplet,
|
|
58021
|
+
cliCommands.checkBackend,
|
|
58022
|
+
cliCommands.listTools,
|
|
58023
|
+
cliCommands.searchTools,
|
|
58024
|
+
cliCommands.getTool,
|
|
58025
|
+
cliCommands.callTool,
|
|
58026
|
+
cliCommands.listResources,
|
|
58027
|
+
cliCommands.searchResources,
|
|
58028
|
+
cliCommands.listResourceTemplates,
|
|
58029
|
+
cliCommands.readResource,
|
|
58030
|
+
cliCommands.listPrompts,
|
|
58031
|
+
cliCommands.searchPrompts,
|
|
58032
|
+
cliCommands.getPrompt,
|
|
58033
|
+
cliCommands.complete,
|
|
58034
|
+
cliCommands.config,
|
|
58035
|
+
cliCommands.auth,
|
|
58036
|
+
cliCommands.completion
|
|
58037
|
+
];
|
|
58038
|
+
const cliSubcommands = {
|
|
58039
|
+
[cliCommands.add]: [
|
|
58040
|
+
"cli",
|
|
58041
|
+
"mcp",
|
|
58042
|
+
"openapi",
|
|
58043
|
+
"graphql",
|
|
58044
|
+
"http"
|
|
58045
|
+
],
|
|
58046
|
+
[cliCommands.auth]: [
|
|
58047
|
+
"login",
|
|
58048
|
+
"logout",
|
|
58049
|
+
"list"
|
|
58050
|
+
],
|
|
58051
|
+
[cliCommands.completion]: [...completionShells],
|
|
58052
|
+
[cliCommands.config]: ["path", "paths"]
|
|
58053
|
+
};
|
|
58054
|
+
const capletIdCommands = new Set([
|
|
58055
|
+
cliCommands.getCaplet,
|
|
58056
|
+
cliCommands.checkBackend,
|
|
58057
|
+
cliCommands.listTools,
|
|
58058
|
+
cliCommands.searchTools,
|
|
58059
|
+
cliCommands.listResources,
|
|
58060
|
+
cliCommands.searchResources,
|
|
58061
|
+
cliCommands.listResourceTemplates,
|
|
58062
|
+
cliCommands.readResource,
|
|
58063
|
+
cliCommands.listPrompts,
|
|
58064
|
+
cliCommands.searchPrompts,
|
|
58065
|
+
cliCommands.complete
|
|
58066
|
+
]);
|
|
58067
|
+
const qualifiedToolCommands = new Set([cliCommands.getTool, cliCommands.callTool]);
|
|
58068
|
+
const qualifiedPromptCommands = new Set([cliCommands.getPrompt]);
|
|
58069
|
+
function listCaplets(configWithSources, options) {
|
|
58070
|
+
const { config, sources, shadows } = configWithSources;
|
|
58071
|
+
return allCaplets(config).filter((server) => options.includeDisabled || !server.disabled).map((server) => ({
|
|
58072
|
+
server: server.server,
|
|
58073
|
+
backend: server.backend,
|
|
58074
|
+
name: server.name,
|
|
58075
|
+
description: server.description,
|
|
58076
|
+
disabled: server.disabled,
|
|
58077
|
+
status: initialServerStatus(server),
|
|
58078
|
+
source: sources[server.server]?.kind ?? "unknown",
|
|
58079
|
+
path: sources[server.server]?.path ?? null,
|
|
58080
|
+
shadows: shadows[server.server] ?? []
|
|
58081
|
+
})).sort((left, right) => left.server.localeCompare(right.server));
|
|
58082
|
+
}
|
|
58083
|
+
function initialServerStatus(server) {
|
|
58084
|
+
return server.disabled ? "disabled" : "not_started";
|
|
58085
|
+
}
|
|
58086
|
+
function allCaplets(config) {
|
|
58087
|
+
return [
|
|
58088
|
+
...Object.values(config.mcpServers),
|
|
58089
|
+
...Object.values(config.openapiEndpoints),
|
|
58090
|
+
...Object.values(config.graphqlEndpoints),
|
|
58091
|
+
...Object.values(config.httpApis),
|
|
58092
|
+
...Object.values(config.cliTools)
|
|
58093
|
+
];
|
|
58094
|
+
}
|
|
58095
|
+
function formatCapletList(rows, format = "plain") {
|
|
58096
|
+
return format === "markdown" ? formatCapletListMarkdown(rows) : formatCapletListPlain(rows);
|
|
58097
|
+
}
|
|
58098
|
+
function formatCapletListMarkdown(rows) {
|
|
58099
|
+
if (rows.length === 0) return "## Configured Caplets\n\nNo configured Caplets found.\n";
|
|
58100
|
+
const heading = [
|
|
58101
|
+
"## Configured Caplets",
|
|
58102
|
+
"",
|
|
58103
|
+
`${rows.length} ${rows.length === 1 ? "Caplet" : "Caplets"} shown.`,
|
|
58104
|
+
""
|
|
58105
|
+
];
|
|
58106
|
+
const entries = rows.flatMap((row) => [
|
|
58107
|
+
`- \`${row.server}\` — ${row.name}`,
|
|
58108
|
+
` - Backend: ${row.backend}`,
|
|
58109
|
+
` - Status: ${row.status}`,
|
|
58110
|
+
` - Source: ${row.source}`,
|
|
58111
|
+
...row.disabled ? [" - Disabled: true"] : [],
|
|
58112
|
+
...row.path ? [` - Path: ${row.path}`] : []
|
|
58113
|
+
]);
|
|
58114
|
+
const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
|
|
58115
|
+
if (warnings.length === 0) return `${[...heading, ...entries].join("\n")}\n`;
|
|
58116
|
+
return `${[
|
|
58117
|
+
...heading,
|
|
58118
|
+
...entries,
|
|
58119
|
+
"",
|
|
58120
|
+
"Warnings:",
|
|
58121
|
+
...warnings.map((warning) => `- ${warning}`)
|
|
58122
|
+
].join("\n")}\n`;
|
|
58123
|
+
}
|
|
58124
|
+
function formatCapletListPlain(rows) {
|
|
58125
|
+
if (rows.length === 0) return "No configured Caplets found.\n";
|
|
58126
|
+
const entries = rows.map((row) => [
|
|
58127
|
+
row.server,
|
|
58128
|
+
` Name: ${row.name}`,
|
|
58129
|
+
` Backend: ${row.backend}`,
|
|
58130
|
+
` Status: ${row.status}`,
|
|
58131
|
+
` Source: ${row.source}`,
|
|
58132
|
+
...row.disabled ? [" Disabled: true"] : [],
|
|
58133
|
+
...row.path ? [` Path: ${row.path}`] : []
|
|
58134
|
+
].join("\n")).join("\n\n");
|
|
58135
|
+
const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
|
|
58136
|
+
if (warnings.length === 0) return `Configured Caplets (${rows.length})\n\n${entries}\n`;
|
|
58137
|
+
return `Configured Caplets (${rows.length})\n\n${entries}\n\n${warnings.join("\n")}\n`;
|
|
58138
|
+
}
|
|
58139
|
+
function formatSourceKind(kind) {
|
|
58140
|
+
if (kind.startsWith("project")) return "project";
|
|
58141
|
+
if (kind.startsWith("global")) return "global";
|
|
58142
|
+
return kind;
|
|
58143
|
+
}
|
|
58144
|
+
function resolveCliConfigPaths(envConfigPath, authDir) {
|
|
58145
|
+
const configPath = resolveConfigPath(envConfigPath);
|
|
58146
|
+
const effectiveAuthDir = authDir ?? DEFAULT_AUTH_DIR;
|
|
58147
|
+
return {
|
|
58148
|
+
userConfig: configPath,
|
|
58149
|
+
projectConfig: resolveProjectConfigPath(),
|
|
58150
|
+
userRoot: resolveCapletsRoot(configPath),
|
|
58151
|
+
stateRoot: dirname(effectiveAuthDir),
|
|
58152
|
+
projectRoot: resolveProjectCapletsRoot(),
|
|
58153
|
+
authDir: effectiveAuthDir,
|
|
58154
|
+
envConfig: envConfigPath ?? null
|
|
58155
|
+
};
|
|
58156
|
+
}
|
|
58157
|
+
function formatConfigPaths(paths, format = "plain") {
|
|
58158
|
+
if (format === "markdown") return formatConfigPathsMarkdown(paths);
|
|
58159
|
+
return formatConfigPathsPlain(paths);
|
|
58160
|
+
}
|
|
58161
|
+
function formatConfigPathsMarkdown(paths) {
|
|
58162
|
+
return [
|
|
58163
|
+
"## Caplets paths",
|
|
58164
|
+
"",
|
|
58165
|
+
`- User config: ${paths.userConfig}`,
|
|
58166
|
+
`- Project config: ${paths.projectConfig}`,
|
|
58167
|
+
`- User Caplets root: ${paths.userRoot}`,
|
|
58168
|
+
`- State root: ${paths.stateRoot}`,
|
|
58169
|
+
`- Project Caplets root: ${paths.projectRoot}`,
|
|
58170
|
+
`- Auth directory: ${paths.authDir}`,
|
|
58171
|
+
`- CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
|
|
58172
|
+
].join("\n") + "\n";
|
|
58173
|
+
}
|
|
58174
|
+
function formatConfigPathsPlain(paths) {
|
|
58175
|
+
return [
|
|
58176
|
+
"Caplets paths",
|
|
58177
|
+
"",
|
|
58178
|
+
`User config: ${paths.userConfig}`,
|
|
58179
|
+
`Project config: ${paths.projectConfig}`,
|
|
58180
|
+
`User root: ${paths.userRoot}`,
|
|
58181
|
+
`State root: ${paths.stateRoot}`,
|
|
58182
|
+
`Project root: ${paths.projectRoot}`,
|
|
58183
|
+
`Auth directory: ${paths.authDir}`,
|
|
58184
|
+
`CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
|
|
58185
|
+
].join("\n") + "\n";
|
|
58186
|
+
}
|
|
58187
|
+
function completionCacheKey(input) {
|
|
58188
|
+
return createHash("sha256").update(JSON.stringify(input)).digest("hex");
|
|
58189
|
+
}
|
|
58190
|
+
function readCompletionCacheEntry(cacheDir, key, now = Date.now()) {
|
|
58191
|
+
try {
|
|
58192
|
+
const parsed = JSON.parse(readFileSync(cachePath(cacheDir, key), "utf8"));
|
|
58193
|
+
if (parsed.status === "positive" && Array.isArray(parsed.candidates)) return {
|
|
58194
|
+
...parsed,
|
|
58195
|
+
fresh: now <= parsed.expiresAt
|
|
58196
|
+
};
|
|
58197
|
+
if (parsed.status === "negative" && typeof parsed.reason === "string") return {
|
|
58198
|
+
...parsed,
|
|
58199
|
+
fresh: now <= parsed.expiresAt
|
|
58200
|
+
};
|
|
58201
|
+
} catch {
|
|
58202
|
+
return;
|
|
58203
|
+
}
|
|
58204
|
+
}
|
|
58205
|
+
function writeCompletionCacheEntry(cacheDir, key, entry) {
|
|
58206
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
58207
|
+
const path = cachePath(cacheDir, key);
|
|
58208
|
+
const tempPath = `${path}.${process.pid}.tmp`;
|
|
58209
|
+
writeFileSync(tempPath, JSON.stringify(entry), { mode: 384 });
|
|
58210
|
+
renameSync(tempPath, path);
|
|
58211
|
+
}
|
|
58212
|
+
function cachePath(cacheDir, key) {
|
|
58213
|
+
return join(cacheDir, `${key}.json`);
|
|
58214
|
+
}
|
|
58215
|
+
async function discoverCompletionCandidates(serverId, kind, options) {
|
|
58216
|
+
const server = enabledServer(serverId, options.config);
|
|
58217
|
+
if (!server) return [];
|
|
58218
|
+
const completion = options.completion ?? options.config.options.completion;
|
|
58219
|
+
const now = options.now ?? Date.now();
|
|
58220
|
+
const configCandidates = configDefinedCandidates(serverId, kind, options.config);
|
|
58221
|
+
const cacheDir = options.cacheDir ?? DEFAULT_COMPLETION_CACHE_DIR;
|
|
58222
|
+
const key = completionCacheKey({
|
|
58223
|
+
server: server.server,
|
|
58224
|
+
backend: server.backend,
|
|
58225
|
+
kind,
|
|
58226
|
+
fingerprint: completionFingerprint(server, kind, completion)
|
|
58227
|
+
});
|
|
58228
|
+
const cached = readCompletionCacheEntry(cacheDir, key, now);
|
|
58229
|
+
if (cached?.status === "positive" && cached.fresh) return cached.candidates;
|
|
58230
|
+
if (cached?.status === "negative" && cached.fresh) return cached.candidates ?? configCandidates;
|
|
58231
|
+
try {
|
|
58232
|
+
const live = await withTimeout(liveCandidates(server, kind, options.managers), Math.min(completion.discoveryTimeoutMs, completion.overallTimeoutMs));
|
|
58233
|
+
const candidates = dedupeCandidates([...configCandidates, ...live]);
|
|
58234
|
+
writeCompletionCacheEntry(cacheDir, key, {
|
|
58235
|
+
status: "positive",
|
|
58236
|
+
fetchedAt: now,
|
|
58237
|
+
expiresAt: now + completion.cacheTtlMs,
|
|
58238
|
+
candidates
|
|
58239
|
+
});
|
|
58240
|
+
return candidates;
|
|
58241
|
+
} catch (error) {
|
|
58242
|
+
writeCompletionCacheEntry(cacheDir, key, {
|
|
58243
|
+
status: "negative",
|
|
58244
|
+
fetchedAt: now,
|
|
58245
|
+
expiresAt: now + completion.negativeCacheTtlMs,
|
|
58246
|
+
reason: negativeReason(error),
|
|
58247
|
+
...cached?.status === "positive" ? { candidates: cached.candidates } : {}
|
|
58248
|
+
});
|
|
58249
|
+
if (cached?.status === "positive") return cached.candidates;
|
|
58250
|
+
return configCandidates;
|
|
58251
|
+
}
|
|
58252
|
+
}
|
|
58253
|
+
function configDefinedCandidates(serverId, kind, config) {
|
|
58254
|
+
if (kind !== "tools") return [];
|
|
58255
|
+
const cli = config.cliTools[serverId];
|
|
58256
|
+
if (cli && !cli.disabled) return Object.keys(cli.actions).map((name) => ({ value: `${serverId}.${name}` }));
|
|
58257
|
+
const http = config.httpApis[serverId];
|
|
58258
|
+
if (http && !http.disabled) return Object.keys(http.actions).map((name) => ({ value: `${serverId}.${name}` }));
|
|
58259
|
+
const graphql = config.graphqlEndpoints[serverId];
|
|
58260
|
+
if (graphql && !graphql.disabled && graphql.operations) return Object.keys(graphql.operations).map((name) => ({ value: `${serverId}.${name}` }));
|
|
58261
|
+
return [];
|
|
58262
|
+
}
|
|
58263
|
+
async function liveCandidates(server, kind, managers) {
|
|
58264
|
+
if (kind === "tools" && managers?.listTools) return (await managers.listTools(server)).map((tool) => ({
|
|
58265
|
+
value: `${server.server}.${tool.name}`,
|
|
58266
|
+
description: tool.description
|
|
58267
|
+
}));
|
|
58268
|
+
if (kind === "tools") return [];
|
|
58269
|
+
if (server.backend !== "mcp") return [];
|
|
58270
|
+
if (kind === "prompts" && managers?.listPrompts) return (await managers.listPrompts(server)).map((prompt) => ({
|
|
58271
|
+
value: `${server.server}.${prompt.name}`,
|
|
58272
|
+
description: prompt.description
|
|
58273
|
+
}));
|
|
58274
|
+
if (kind === "resources" && managers?.listResources) return (await managers.listResources(server)).map((resource) => ({
|
|
58275
|
+
value: resource.uri,
|
|
58276
|
+
label: resource.name,
|
|
58277
|
+
description: resource.description
|
|
58278
|
+
}));
|
|
58279
|
+
if (kind === "resourceTemplates" && managers?.listResourceTemplates) return (await managers.listResourceTemplates(server)).map((template) => ({
|
|
58280
|
+
value: template.uriTemplate,
|
|
58281
|
+
label: template.name,
|
|
58282
|
+
description: template.description
|
|
58283
|
+
}));
|
|
58284
|
+
throw new CapletsError("UNSUPPORTED_CAPABILITY", `Completion discovery is unsupported for ${kind}`);
|
|
58285
|
+
}
|
|
58286
|
+
function completionFingerprint(server, kind, completion) {
|
|
58287
|
+
return JSON.stringify({
|
|
58288
|
+
kind,
|
|
58289
|
+
completion: {
|
|
58290
|
+
discoveryTimeoutMs: completion.discoveryTimeoutMs,
|
|
58291
|
+
cacheTtlMs: completion.cacheTtlMs,
|
|
58292
|
+
negativeCacheTtlMs: completion.negativeCacheTtlMs
|
|
58293
|
+
},
|
|
58294
|
+
server: secretFreeServerShape(server)
|
|
58295
|
+
});
|
|
58296
|
+
}
|
|
58297
|
+
function secretFreeServerShape(server) {
|
|
58298
|
+
const base = {
|
|
58299
|
+
server: server.server,
|
|
58300
|
+
backend: server.backend,
|
|
58301
|
+
name: server.name,
|
|
58302
|
+
description: server.description,
|
|
58303
|
+
tags: server.tags,
|
|
58304
|
+
disabled: server.disabled
|
|
58305
|
+
};
|
|
58306
|
+
switch (server.backend) {
|
|
58307
|
+
case "mcp": return {
|
|
58308
|
+
...base,
|
|
58309
|
+
transport: server.transport,
|
|
58310
|
+
command: server.command,
|
|
58311
|
+
args: server.args,
|
|
58312
|
+
cwd: server.cwd,
|
|
58313
|
+
url: server.url,
|
|
58314
|
+
authType: server.auth?.type,
|
|
58315
|
+
startupTimeoutMs: server.startupTimeoutMs,
|
|
58316
|
+
callTimeoutMs: server.callTimeoutMs
|
|
58317
|
+
};
|
|
58318
|
+
case "openapi": return {
|
|
58319
|
+
...base,
|
|
58320
|
+
specPath: server.specPath,
|
|
58321
|
+
specUrl: server.specUrl,
|
|
58322
|
+
baseUrl: server.baseUrl,
|
|
58323
|
+
authType: server.auth.type,
|
|
58324
|
+
requestTimeoutMs: server.requestTimeoutMs
|
|
58325
|
+
};
|
|
58326
|
+
case "graphql": return {
|
|
58327
|
+
...base,
|
|
58328
|
+
endpointUrl: server.endpointUrl,
|
|
58329
|
+
schemaPath: server.schemaPath,
|
|
58330
|
+
schemaUrl: server.schemaUrl,
|
|
58331
|
+
authType: server.auth.type,
|
|
58332
|
+
operationNames: server.operations ? Object.keys(server.operations) : void 0
|
|
58333
|
+
};
|
|
58334
|
+
case "http": return {
|
|
58335
|
+
...base,
|
|
58336
|
+
baseUrl: server.baseUrl,
|
|
58337
|
+
authType: server.auth.type,
|
|
58338
|
+
actions: Object.fromEntries(Object.entries(server.actions).map(([name, action]) => [name, {
|
|
58339
|
+
method: action.method,
|
|
58340
|
+
path: action.path
|
|
58341
|
+
}])),
|
|
58342
|
+
requestTimeoutMs: server.requestTimeoutMs
|
|
58343
|
+
};
|
|
58344
|
+
case "cli": return {
|
|
58345
|
+
...base,
|
|
58346
|
+
cwd: server.cwd,
|
|
58347
|
+
actions: Object.fromEntries(Object.entries(server.actions).map(([name, action]) => [name, {
|
|
58348
|
+
command: action.command,
|
|
58349
|
+
args: action.args,
|
|
58350
|
+
cwd: action.cwd
|
|
58351
|
+
}])),
|
|
58352
|
+
timeoutMs: server.timeoutMs,
|
|
58353
|
+
maxOutputBytes: server.maxOutputBytes
|
|
58354
|
+
};
|
|
58355
|
+
case "caplets": return {
|
|
58356
|
+
...base,
|
|
58357
|
+
configPath: server.configPath,
|
|
58358
|
+
capletsRoot: server.capletsRoot,
|
|
58359
|
+
defaultSearchLimit: server.defaultSearchLimit,
|
|
58360
|
+
maxSearchLimit: server.maxSearchLimit
|
|
58361
|
+
};
|
|
58362
|
+
}
|
|
58363
|
+
}
|
|
58364
|
+
function negativeReason(error) {
|
|
58365
|
+
if (error instanceof CapletsError) {
|
|
58366
|
+
if (error.code === "AUTH_REQUIRED" || error.code === "AUTH_FAILED" || error.code === "AUTH_REFRESH_FAILED") return "auth_required";
|
|
58367
|
+
if (error.code === "SERVER_UNAVAILABLE" || error.code === "SERVER_START_TIMEOUT") return "unavailable";
|
|
58368
|
+
if (error.code === "UNSUPPORTED_CAPABILITY" || error.code === "UNSUPPORTED_OPERATION") return "unsupported";
|
|
58369
|
+
if (error.code === "TOOL_CALL_TIMEOUT" || error.code === "DOWNSTREAM_COMPLETION_ERROR") return "timeout";
|
|
58370
|
+
}
|
|
58371
|
+
return error instanceof Error && error.message.includes("timeout") ? "timeout" : "error";
|
|
58372
|
+
}
|
|
58373
|
+
async function withTimeout(promise, timeoutMs) {
|
|
58374
|
+
let timeout;
|
|
58375
|
+
try {
|
|
58376
|
+
return await Promise.race([promise, new Promise((_, reject) => {
|
|
58377
|
+
timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("completion discovery timeout")), timeoutMs);
|
|
58378
|
+
})]);
|
|
58379
|
+
} finally {
|
|
58380
|
+
if (timeout) clearTimeout(timeout);
|
|
58381
|
+
}
|
|
58382
|
+
}
|
|
58383
|
+
function enabledServer(serverId, config) {
|
|
58384
|
+
const server = config.mcpServers[serverId] ?? config.openapiEndpoints[serverId] ?? config.graphqlEndpoints[serverId] ?? config.httpApis[serverId] ?? config.cliTools[serverId] ?? config.capletSets[serverId];
|
|
58385
|
+
return server && !server.disabled ? server : void 0;
|
|
58386
|
+
}
|
|
58387
|
+
function dedupeCandidates(candidates) {
|
|
58388
|
+
const seen = /* @__PURE__ */ new Set();
|
|
58389
|
+
return candidates.filter((candidate) => {
|
|
58390
|
+
if (seen.has(candidate.value)) return false;
|
|
58391
|
+
seen.add(candidate.value);
|
|
58392
|
+
return true;
|
|
58393
|
+
});
|
|
58394
|
+
}
|
|
58395
|
+
var completion_exports = /* @__PURE__ */ __exportAll({
|
|
58396
|
+
completeCliWords: () => completeCliWords,
|
|
58397
|
+
completionScript: () => completionScript,
|
|
58398
|
+
trailingSpaceCompletionToken: () => trailingSpaceCompletionToken
|
|
58399
|
+
});
|
|
58400
|
+
const trailingSpaceCompletionToken = "__CAPLETS_TRAILING_SPACE__";
|
|
58401
|
+
const optionValueSuggestions = {
|
|
58402
|
+
"*": { "--format": [
|
|
58403
|
+
"markdown",
|
|
58404
|
+
"md",
|
|
58405
|
+
"plain",
|
|
58406
|
+
"json"
|
|
58407
|
+
] },
|
|
58408
|
+
serve: { "--transport": ["stdio", "http"] },
|
|
58409
|
+
"add:mcp": { "--transport": ["http", "sse"] },
|
|
58410
|
+
"add:cli": { "--include": [
|
|
58411
|
+
"git",
|
|
58412
|
+
"gh",
|
|
58413
|
+
"package"
|
|
58414
|
+
] }
|
|
58415
|
+
};
|
|
58416
|
+
function completionScript(shell) {
|
|
58417
|
+
switch (shell) {
|
|
58418
|
+
case "bash": return bashCompletionScript();
|
|
58419
|
+
case "zsh": return zshCompletionScript();
|
|
58420
|
+
case "fish": return fishCompletionScript();
|
|
58421
|
+
case "powershell": return powershellCompletionScript();
|
|
58422
|
+
case "cmd": return cmdCompletionScript();
|
|
58423
|
+
default: throw new CapletsError("REQUEST_INVALID", "completion shell must be bash, zsh, fish, powershell, or cmd");
|
|
58424
|
+
}
|
|
58425
|
+
}
|
|
58426
|
+
async function completeCliWords(words, options = {}) {
|
|
58427
|
+
try {
|
|
58428
|
+
const normalized = words.length === 0 ? [""] : words;
|
|
58429
|
+
const current = normalized.at(-1) ?? "";
|
|
58430
|
+
const previous = normalized.at(-2);
|
|
58431
|
+
const command = normalized[0] ?? "";
|
|
58432
|
+
const subcommand = normalized[1] ?? "";
|
|
58433
|
+
if (command === cliCommands.complete && previous === "--prompt" && subcommand) return prefixFilter((await discoverCompletionCandidates(subcommand, "prompts", discoveryOptions(options))).map((candidate) => candidate.value.replace(`${subcommand}.`, "")), current);
|
|
58434
|
+
if (command === cliCommands.complete && previous === "--resource-template" && subcommand) return prefixFilter((await discoverCompletionCandidates(subcommand, "resourceTemplates", discoveryOptions(options))).map((candidate) => candidate.value), current);
|
|
58435
|
+
const optionValues = suggestionsForOptionValue(command, subcommand, previous);
|
|
58436
|
+
if (optionValues) return prefixFilter(optionValues, current);
|
|
58437
|
+
if (normalized.length === 1) return prefixFilter([...topLevelCommandNames], current);
|
|
58438
|
+
if (normalized.length === 2 && command in cliSubcommands) return prefixFilter(cliSubcommands[command], current);
|
|
58439
|
+
if (normalized.length === 2 && capletIdCommands.has(command)) return prefixFilter(promptResourceCommands.has(command) ? configuredCapletIds(options, { backend: "mcp" }) : configuredCapletIds(options), current);
|
|
58440
|
+
if (normalized.length === 2 && (qualifiedToolCommands.has(command) || qualifiedPromptCommands.has(command))) {
|
|
58441
|
+
if (current.includes(".")) return prefixFilter((await discoverCompletionCandidates(current.slice(0, current.indexOf(".")), qualifiedToolCommands.has(command) ? "tools" : "prompts", discoveryOptions(options))).map((candidate) => candidate.value), current);
|
|
58442
|
+
return prefixFilter(configuredCapletIds(options, qualifiedPromptCommands.has(command) ? { backend: "mcp" } : void 0).map((id) => `${id}.`), current);
|
|
58443
|
+
}
|
|
58444
|
+
if (command === cliCommands.readResource && normalized.length === 3) return prefixFilter((await discoverCompletionCandidates(subcommand, "resources", discoveryOptions(options))).map((candidate) => candidate.value), current);
|
|
58445
|
+
if (command === cliCommands.auth && ["login", "logout"].includes(subcommand) && normalized.length === 3) return prefixFilter(configuredCapletIds(options), current);
|
|
58446
|
+
return [];
|
|
58447
|
+
} catch {
|
|
58448
|
+
return [];
|
|
58449
|
+
}
|
|
58450
|
+
}
|
|
58451
|
+
function suggestionsForOptionValue(command, subcommand, previous) {
|
|
58452
|
+
if (!previous) return void 0;
|
|
58453
|
+
return optionValueSuggestions[`${command}:${subcommand}`]?.[previous] ?? optionValueSuggestions[command]?.[previous] ?? optionValueSuggestions["*"]?.[previous];
|
|
58454
|
+
}
|
|
58455
|
+
const promptResourceCommands = new Set([
|
|
58456
|
+
cliCommands.getPrompt,
|
|
58457
|
+
cliCommands.readResource,
|
|
58458
|
+
cliCommands.complete
|
|
58459
|
+
]);
|
|
58460
|
+
function configuredCapletIds(options, filter = {}) {
|
|
58461
|
+
return listCaplets(options.config ? {
|
|
58462
|
+
config: options.config,
|
|
58463
|
+
sources: {},
|
|
58464
|
+
shadows: {}
|
|
58465
|
+
} : loadConfigWithSources(options.configPath, options.projectConfigPath), { includeDisabled: false }).filter((row) => !filter.backend || row.backend === filter.backend).map((row) => row.server);
|
|
58466
|
+
}
|
|
58467
|
+
function discoveryOptions(options) {
|
|
58468
|
+
return {
|
|
58469
|
+
config: options.config ?? loadConfigWithSources(options.configPath, options.projectConfigPath).config,
|
|
58470
|
+
completion: options.completion,
|
|
58471
|
+
cacheDir: options.cacheDir,
|
|
58472
|
+
managers: options.managers
|
|
58473
|
+
};
|
|
58474
|
+
}
|
|
58475
|
+
function prefixFilter(values, prefix) {
|
|
58476
|
+
return values.filter((value) => value.startsWith(prefix));
|
|
58477
|
+
}
|
|
58478
|
+
function bashCompletionScript() {
|
|
58479
|
+
return `# caplets bash completion
|
|
58480
|
+
_caplets_completions() {
|
|
58481
|
+
local IFS=$'\n'
|
|
58482
|
+
COMPREPLY=( $(caplets __complete --shell bash -- "\${COMP_WORDS[@]:1}" 2>/dev/null) )
|
|
58483
|
+
}
|
|
58484
|
+
complete -o default -F _caplets_completions caplets
|
|
58485
|
+
`;
|
|
58486
|
+
}
|
|
58487
|
+
function zshCompletionScript() {
|
|
58488
|
+
return `#compdef caplets
|
|
58489
|
+
_caplets() {
|
|
58490
|
+
local -a suggestions
|
|
58491
|
+
suggestions=("\${(@f)$(caplets __complete --shell zsh -- "\${words[@]:1}" 2>/dev/null)}")
|
|
58492
|
+
compadd -- $suggestions
|
|
58493
|
+
}
|
|
58494
|
+
_caplets "$@"
|
|
58495
|
+
`;
|
|
58496
|
+
}
|
|
58497
|
+
function fishCompletionScript() {
|
|
58498
|
+
return `# caplets fish completion
|
|
58499
|
+
function __caplets_complete
|
|
58500
|
+
set -l tokens (commandline -opc)
|
|
58501
|
+
set -l current (commandline -ct)
|
|
58502
|
+
caplets __complete --shell fish -- $tokens[2..-1] $current 2>/dev/null
|
|
58503
|
+
end
|
|
58504
|
+
complete -c caplets -f -a '(__caplets_complete)'
|
|
58505
|
+
`;
|
|
58506
|
+
}
|
|
58507
|
+
function powershellCompletionScript() {
|
|
58508
|
+
return `# caplets PowerShell completion
|
|
58509
|
+
Register-ArgumentCompleter -Native -CommandName caplets -ScriptBlock {
|
|
58510
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
58511
|
+
$tokens = @($commandAst.CommandElements | Select-Object -Skip 1 | ForEach-Object { $_.ToString() })
|
|
58512
|
+
if ($tokens.Count -eq 0 -or $commandAst.Extent.Text.EndsWith(' ')) { $tokens += '${trailingSpaceCompletionToken}' }
|
|
58513
|
+
caplets __complete --shell powershell -- @tokens 2>$null | ForEach-Object {
|
|
58514
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
58515
|
+
}
|
|
58516
|
+
}
|
|
58517
|
+
`;
|
|
58518
|
+
}
|
|
58519
|
+
function cmdCompletionScript() {
|
|
58520
|
+
return `@echo off
|
|
58521
|
+
REM caplets cmd completion helper
|
|
58522
|
+
REM cmd.exe has no native programmable completion API. This doskey macro prints suggestions for the current words.
|
|
58523
|
+
doskey caplets-complete=caplets __complete --shell cmd -- $* 2^>nul
|
|
58524
|
+
REM Usage: caplets-complete get-caplet
|
|
58525
|
+
`;
|
|
58526
|
+
}
|
|
57262
58527
|
//#endregion
|
|
57263
58528
|
//#region ../core/dist/index.js
|
|
57264
58529
|
/**
|
|
@@ -58557,7 +59822,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
|
|
|
58557
59822
|
values: [],
|
|
58558
59823
|
hasMore: false
|
|
58559
59824
|
} };
|
|
58560
|
-
var version$1 = "0.
|
|
59825
|
+
var version$1 = "0.18.0";
|
|
58561
59826
|
var CapletsMcpSession = class {
|
|
58562
59827
|
engine;
|
|
58563
59828
|
server;
|
|
@@ -58599,6 +59864,7 @@ var CapletsMcpSession = class {
|
|
|
58599
59864
|
if (!previousCaplet || serializeCaplet(previousCaplet) !== serializeCaplet(caplet)) tool.update({
|
|
58600
59865
|
title: caplet.name,
|
|
58601
59866
|
description: capabilityDescription(caplet),
|
|
59867
|
+
paramsSchema: generatedToolInputSchemaForCaplet(caplet).shape,
|
|
58602
59868
|
callback: async (request) => this.handleTool(serverId, request),
|
|
58603
59869
|
enabled: true
|
|
58604
59870
|
});
|
|
@@ -58612,7 +59878,7 @@ var CapletsMcpSession = class {
|
|
|
58612
59878
|
return this.server.registerTool(caplet.server, {
|
|
58613
59879
|
title: caplet.name,
|
|
58614
59880
|
description: capabilityDescription(caplet),
|
|
58615
|
-
inputSchema:
|
|
59881
|
+
inputSchema: generatedToolInputSchemaForCaplet(caplet).shape
|
|
58616
59882
|
}, async (request) => this.handleTool(caplet.server, request));
|
|
58617
59883
|
}
|
|
58618
59884
|
async handleTool(serverId, request) {
|
|
@@ -62090,49 +63356,56 @@ async function loginAuth(serverId, options) {
|
|
|
62090
63356
|
}
|
|
62091
63357
|
}
|
|
62092
63358
|
function logoutAuth(serverId, options) {
|
|
62093
|
-
|
|
62094
|
-
if (deleteTokenBundle(serverId, options.authDir)) options.writeOut(`Deleted OAuth credentials for \`${serverId}\`.\n`);
|
|
63359
|
+
if (logoutAuthResult(serverId, options).deleted) options.writeOut(`Deleted OAuth credentials for \`${serverId}\`.\n`);
|
|
62095
63360
|
else options.writeOut(`No OAuth credentials found for \`${serverId}\`.\n`);
|
|
62096
63361
|
}
|
|
63362
|
+
function logoutAuthResult(serverId, options) {
|
|
63363
|
+
assertLoginTarget(findAuthTarget(serverId, loadConfig(options.configPath)), serverId);
|
|
63364
|
+
return {
|
|
63365
|
+
server: serverId,
|
|
63366
|
+
deleted: deleteTokenBundle(serverId, options.authDir)
|
|
63367
|
+
};
|
|
63368
|
+
}
|
|
62097
63369
|
function listAuth(options) {
|
|
62098
|
-
const
|
|
63370
|
+
const rows = listAuthRows(options);
|
|
62099
63371
|
const format = options.format ?? "plain";
|
|
62100
63372
|
if (format === "json") {
|
|
62101
|
-
const rows = servers.map((server) => {
|
|
62102
|
-
const bundle = readTokenBundle(server.server, options.authDir);
|
|
62103
|
-
const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
|
|
62104
|
-
return {
|
|
62105
|
-
server: server.server,
|
|
62106
|
-
status,
|
|
62107
|
-
...bundle?.expiresAt ? { expiresAt: bundle.expiresAt } : {},
|
|
62108
|
-
...bundle?.scope ? { scope: bundle.scope } : {}
|
|
62109
|
-
};
|
|
62110
|
-
});
|
|
62111
63373
|
options.writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
62112
63374
|
return;
|
|
62113
63375
|
}
|
|
62114
|
-
|
|
62115
|
-
|
|
62116
|
-
|
|
62117
|
-
|
|
62118
|
-
if (format === "markdown") options.writeOut("## OAuth credentials\n\n");
|
|
62119
|
-
else options.writeOut("OAuth credentials\n\n");
|
|
62120
|
-
for (const server of servers) {
|
|
63376
|
+
options.writeOut(formatAuthRows(rows, format));
|
|
63377
|
+
}
|
|
63378
|
+
function listAuthRows(options) {
|
|
63379
|
+
return authTargets(loadConfig(options.configPath)).sort((left, right) => left.server.localeCompare(right.server)).map((server) => {
|
|
62121
63380
|
const bundle = readTokenBundle(server.server, options.authDir);
|
|
62122
63381
|
const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
|
|
62123
|
-
|
|
63382
|
+
return {
|
|
63383
|
+
server: server.server,
|
|
63384
|
+
status,
|
|
63385
|
+
...bundle?.expiresAt ? { expiresAt: bundle.expiresAt } : {},
|
|
63386
|
+
...bundle?.scope ? { scope: bundle.scope } : {}
|
|
63387
|
+
};
|
|
63388
|
+
});
|
|
63389
|
+
}
|
|
63390
|
+
function formatAuthRows(rows, format) {
|
|
63391
|
+
if (rows.length === 0) return format === "markdown" ? "## OAuth credentials\n\nNo configured remote OAuth servers found.\n" : "No configured remote OAuth servers found.\n";
|
|
63392
|
+
let output = "";
|
|
63393
|
+
if (format === "markdown") output += "## OAuth credentials\n\n";
|
|
63394
|
+
else output += "OAuth credentials\n\n";
|
|
63395
|
+
for (const row of rows) {
|
|
63396
|
+
const details = [row.expiresAt ? `expires ${row.expiresAt}` : void 0, row.scope ? `scope ${row.scope}` : void 0].filter(Boolean).join("; ");
|
|
62124
63397
|
if (format === "markdown") {
|
|
62125
|
-
|
|
63398
|
+
output += `- \`${row.server}\` — ${row.status}${details ? ` (${details})` : ""}\n`;
|
|
62126
63399
|
continue;
|
|
62127
63400
|
}
|
|
62128
|
-
|
|
62129
|
-
|
|
62130
|
-
` Status: ${status}`,
|
|
62131
|
-
...
|
|
62132
|
-
...
|
|
62133
|
-
].join("\n")
|
|
62134
|
-
options.writeOut("\n\n");
|
|
63401
|
+
output += [
|
|
63402
|
+
row.server,
|
|
63403
|
+
` Status: ${row.status}`,
|
|
63404
|
+
...row.expiresAt ? [` Expires: ${row.expiresAt}`] : [],
|
|
63405
|
+
...row.scope ? [` Scope: ${row.scope}`] : []
|
|
63406
|
+
].join("\n") + "\n\n";
|
|
62135
63407
|
}
|
|
63408
|
+
return output;
|
|
62136
63409
|
}
|
|
62137
63410
|
function findAuthTarget(serverId, config = loadConfig()) {
|
|
62138
63411
|
return authTargets(config).find((server) => server.server === serverId);
|
|
@@ -62201,124 +63474,6 @@ function starterConfig() {
|
|
|
62201
63474
|
} }
|
|
62202
63475
|
}, null, 2);
|
|
62203
63476
|
}
|
|
62204
|
-
function listCaplets(configWithSources, options) {
|
|
62205
|
-
const { config, sources, shadows } = configWithSources;
|
|
62206
|
-
return allCaplets(config).filter((server) => options.includeDisabled || !server.disabled).map((server) => ({
|
|
62207
|
-
server: server.server,
|
|
62208
|
-
backend: server.backend,
|
|
62209
|
-
name: server.name,
|
|
62210
|
-
description: server.description,
|
|
62211
|
-
disabled: server.disabled,
|
|
62212
|
-
status: initialServerStatus(server),
|
|
62213
|
-
source: sources[server.server]?.kind ?? "unknown",
|
|
62214
|
-
path: sources[server.server]?.path ?? null,
|
|
62215
|
-
shadows: shadows[server.server] ?? []
|
|
62216
|
-
})).sort((left, right) => left.server.localeCompare(right.server));
|
|
62217
|
-
}
|
|
62218
|
-
function initialServerStatus(server) {
|
|
62219
|
-
return server.disabled ? "disabled" : "not_started";
|
|
62220
|
-
}
|
|
62221
|
-
function allCaplets(config) {
|
|
62222
|
-
return [
|
|
62223
|
-
...Object.values(config.mcpServers),
|
|
62224
|
-
...Object.values(config.openapiEndpoints),
|
|
62225
|
-
...Object.values(config.graphqlEndpoints),
|
|
62226
|
-
...Object.values(config.httpApis),
|
|
62227
|
-
...Object.values(config.cliTools)
|
|
62228
|
-
];
|
|
62229
|
-
}
|
|
62230
|
-
function formatCapletList(rows, format = "plain") {
|
|
62231
|
-
return format === "markdown" ? formatCapletListMarkdown(rows) : formatCapletListPlain(rows);
|
|
62232
|
-
}
|
|
62233
|
-
function formatCapletListMarkdown(rows) {
|
|
62234
|
-
if (rows.length === 0) return "## Configured Caplets\n\nNo configured Caplets found.\n";
|
|
62235
|
-
const heading = [
|
|
62236
|
-
"## Configured Caplets",
|
|
62237
|
-
"",
|
|
62238
|
-
`${rows.length} ${rows.length === 1 ? "Caplet" : "Caplets"} shown.`,
|
|
62239
|
-
""
|
|
62240
|
-
];
|
|
62241
|
-
const entries = rows.flatMap((row) => [
|
|
62242
|
-
`- \`${row.server}\` — ${row.name}`,
|
|
62243
|
-
` - Backend: ${row.backend}`,
|
|
62244
|
-
` - Status: ${row.status}`,
|
|
62245
|
-
` - Source: ${row.source}`,
|
|
62246
|
-
...row.disabled ? [" - Disabled: true"] : [],
|
|
62247
|
-
...row.path ? [` - Path: ${row.path}`] : []
|
|
62248
|
-
]);
|
|
62249
|
-
const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
|
|
62250
|
-
if (warnings.length === 0) return `${[...heading, ...entries].join("\n")}\n`;
|
|
62251
|
-
return `${[
|
|
62252
|
-
...heading,
|
|
62253
|
-
...entries,
|
|
62254
|
-
"",
|
|
62255
|
-
"Warnings:",
|
|
62256
|
-
...warnings.map((warning) => `- ${warning}`)
|
|
62257
|
-
].join("\n")}\n`;
|
|
62258
|
-
}
|
|
62259
|
-
function formatCapletListPlain(rows) {
|
|
62260
|
-
if (rows.length === 0) return "No configured Caplets found.\n";
|
|
62261
|
-
const entries = rows.map((row) => [
|
|
62262
|
-
row.server,
|
|
62263
|
-
` Name: ${row.name}`,
|
|
62264
|
-
` Backend: ${row.backend}`,
|
|
62265
|
-
` Status: ${row.status}`,
|
|
62266
|
-
` Source: ${row.source}`,
|
|
62267
|
-
...row.disabled ? [" Disabled: true"] : [],
|
|
62268
|
-
...row.path ? [` Path: ${row.path}`] : []
|
|
62269
|
-
].join("\n")).join("\n\n");
|
|
62270
|
-
const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
|
|
62271
|
-
if (warnings.length === 0) return `Configured Caplets (${rows.length})\n\n${entries}\n`;
|
|
62272
|
-
return `Configured Caplets (${rows.length})\n\n${entries}\n\n${warnings.join("\n")}\n`;
|
|
62273
|
-
}
|
|
62274
|
-
function formatSourceKind(kind) {
|
|
62275
|
-
if (kind.startsWith("project")) return "project";
|
|
62276
|
-
if (kind.startsWith("global")) return "global";
|
|
62277
|
-
return kind;
|
|
62278
|
-
}
|
|
62279
|
-
function resolveCliConfigPaths(envConfigPath, authDir) {
|
|
62280
|
-
const configPath = resolveConfigPath(envConfigPath);
|
|
62281
|
-
const effectiveAuthDir = authDir ?? DEFAULT_AUTH_DIR;
|
|
62282
|
-
return {
|
|
62283
|
-
userConfig: configPath,
|
|
62284
|
-
projectConfig: resolveProjectConfigPath(),
|
|
62285
|
-
userRoot: resolveCapletsRoot(configPath),
|
|
62286
|
-
stateRoot: dirname(effectiveAuthDir),
|
|
62287
|
-
projectRoot: resolveProjectCapletsRoot(),
|
|
62288
|
-
authDir: effectiveAuthDir,
|
|
62289
|
-
envConfig: envConfigPath ?? null
|
|
62290
|
-
};
|
|
62291
|
-
}
|
|
62292
|
-
function formatConfigPaths(paths, format = "plain") {
|
|
62293
|
-
if (format === "markdown") return formatConfigPathsMarkdown(paths);
|
|
62294
|
-
return formatConfigPathsPlain(paths);
|
|
62295
|
-
}
|
|
62296
|
-
function formatConfigPathsMarkdown(paths) {
|
|
62297
|
-
return [
|
|
62298
|
-
"## Caplets paths",
|
|
62299
|
-
"",
|
|
62300
|
-
`- User config: ${paths.userConfig}`,
|
|
62301
|
-
`- Project config: ${paths.projectConfig}`,
|
|
62302
|
-
`- User Caplets root: ${paths.userRoot}`,
|
|
62303
|
-
`- State root: ${paths.stateRoot}`,
|
|
62304
|
-
`- Project Caplets root: ${paths.projectRoot}`,
|
|
62305
|
-
`- Auth directory: ${paths.authDir}`,
|
|
62306
|
-
`- CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
|
|
62307
|
-
].join("\n") + "\n";
|
|
62308
|
-
}
|
|
62309
|
-
function formatConfigPathsPlain(paths) {
|
|
62310
|
-
return [
|
|
62311
|
-
"Caplets paths",
|
|
62312
|
-
"",
|
|
62313
|
-
`User config: ${paths.userConfig}`,
|
|
62314
|
-
`Project config: ${paths.projectConfig}`,
|
|
62315
|
-
`User root: ${paths.userRoot}`,
|
|
62316
|
-
`State root: ${paths.stateRoot}`,
|
|
62317
|
-
`Project root: ${paths.projectRoot}`,
|
|
62318
|
-
`Auth directory: ${paths.authDir}`,
|
|
62319
|
-
`CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
|
|
62320
|
-
].join("\n") + "\n";
|
|
62321
|
-
}
|
|
62322
63477
|
function installCaplets(repo, options = {}) {
|
|
62323
63478
|
const source = resolveInstallSource(repo);
|
|
62324
63479
|
try {
|
|
@@ -62552,6 +63707,94 @@ function nearestExistingParent(path) {
|
|
|
62552
63707
|
if (parent === path) return parent;
|
|
62553
63708
|
return nearestExistingParent(parent);
|
|
62554
63709
|
}
|
|
63710
|
+
var RemoteControlClient = class {
|
|
63711
|
+
#baseUrl;
|
|
63712
|
+
#requestInit;
|
|
63713
|
+
#fetch;
|
|
63714
|
+
constructor(options) {
|
|
63715
|
+
this.#baseUrl = options.baseUrl;
|
|
63716
|
+
this.#requestInit = options.requestInit;
|
|
63717
|
+
this.#fetch = options.fetch ?? fetch;
|
|
63718
|
+
}
|
|
63719
|
+
async request(command, args) {
|
|
63720
|
+
const controlUrl = controlUrlForBase(this.#baseUrl);
|
|
63721
|
+
let response;
|
|
63722
|
+
try {
|
|
63723
|
+
response = await this.#fetch(controlUrl, {
|
|
63724
|
+
...this.#requestInit,
|
|
63725
|
+
method: "POST",
|
|
63726
|
+
headers: mergeJsonHeaders(this.#requestInit.headers),
|
|
63727
|
+
body: JSON.stringify({
|
|
63728
|
+
command,
|
|
63729
|
+
arguments: args
|
|
63730
|
+
})
|
|
63731
|
+
});
|
|
63732
|
+
} catch (error) {
|
|
63733
|
+
throw new CapletsError("SERVER_UNAVAILABLE", `Could not connect to Caplets server at ${safeBaseUrl(this.#baseUrl)}.`, toSafeError(error, "SERVER_UNAVAILABLE"));
|
|
63734
|
+
}
|
|
63735
|
+
if (response.status === 401 || response.status === 403) throw new CapletsError("AUTH_FAILED", "Caplets server authentication failed. Check CAPLETS_SERVER_USER and CAPLETS_SERVER_PASSWORD.");
|
|
63736
|
+
if (!response.ok) throw new CapletsError("SERVER_UNAVAILABLE", `Caplets server at ${safeBaseUrl(this.#baseUrl)} returned HTTP ${response.status}.`);
|
|
63737
|
+
const payload = await parseRemoteCliResponse(response);
|
|
63738
|
+
if (!payload.ok) throw new CapletsError(payload.error.code, redactRemoteMessage(payload.error.message), payload.error.nextAction === void 0 ? void 0 : { nextAction: payload.error.nextAction });
|
|
63739
|
+
return payload.result;
|
|
63740
|
+
}
|
|
63741
|
+
};
|
|
63742
|
+
function mergeJsonHeaders(headers) {
|
|
63743
|
+
const merged = new Headers(headers);
|
|
63744
|
+
merged.set("content-type", "application/json");
|
|
63745
|
+
return merged;
|
|
63746
|
+
}
|
|
63747
|
+
function safeBaseUrl(baseUrl) {
|
|
63748
|
+
const safe = new URL(baseUrl.href);
|
|
63749
|
+
safe.username = "";
|
|
63750
|
+
safe.password = "";
|
|
63751
|
+
safe.search = "";
|
|
63752
|
+
safe.hash = "";
|
|
63753
|
+
return safe.toString();
|
|
63754
|
+
}
|
|
63755
|
+
async function parseRemoteCliResponse(response) {
|
|
63756
|
+
let payload;
|
|
63757
|
+
try {
|
|
63758
|
+
payload = await response.json();
|
|
63759
|
+
} catch (error) {
|
|
63760
|
+
throw invalidRemoteControlResponse(error);
|
|
63761
|
+
}
|
|
63762
|
+
if (!isRecord(payload)) throw invalidRemoteControlResponse();
|
|
63763
|
+
if (payload.ok === true) {
|
|
63764
|
+
if (!("result" in payload)) throw invalidRemoteControlResponse();
|
|
63765
|
+
return {
|
|
63766
|
+
ok: true,
|
|
63767
|
+
result: payload.result
|
|
63768
|
+
};
|
|
63769
|
+
}
|
|
63770
|
+
if (payload.ok === false) {
|
|
63771
|
+
const error = payload.error;
|
|
63772
|
+
if (!isRecord(error) || typeof error.code !== "string" || typeof error.message !== "string") throw invalidRemoteControlResponse();
|
|
63773
|
+
if ("nextAction" in error && error.nextAction !== void 0 && typeof error.nextAction !== "string") throw invalidRemoteControlResponse();
|
|
63774
|
+
const errorResponse = {
|
|
63775
|
+
ok: false,
|
|
63776
|
+
error: {
|
|
63777
|
+
code: isCapletsErrorCode(error.code) ? error.code : "DOWNSTREAM_TOOL_ERROR",
|
|
63778
|
+
message: error.message
|
|
63779
|
+
}
|
|
63780
|
+
};
|
|
63781
|
+
if (typeof error.nextAction === "string") errorResponse.error.nextAction = error.nextAction;
|
|
63782
|
+
return errorResponse;
|
|
63783
|
+
}
|
|
63784
|
+
throw invalidRemoteControlResponse();
|
|
63785
|
+
}
|
|
63786
|
+
function invalidRemoteControlResponse(cause) {
|
|
63787
|
+
return new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Caplets server returned an invalid remote control response.", cause === void 0 ? void 0 : toSafeError(cause, "DOWNSTREAM_PROTOCOL_ERROR"));
|
|
63788
|
+
}
|
|
63789
|
+
function isRecord(value) {
|
|
63790
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63791
|
+
}
|
|
63792
|
+
function isCapletsErrorCode(value) {
|
|
63793
|
+
return CAPLETS_ERROR_CODES.includes(value);
|
|
63794
|
+
}
|
|
63795
|
+
function redactRemoteMessage(message) {
|
|
63796
|
+
return String(redactSecrets(message)).replace(/\b(authorization\s*:\s*(?:basic|bearer)\s+)[^\s,;]+/giu, "$1[REDACTED]").replace(/\b((?:access_)?token=)[^\s,;]+/giu, "$1[REDACTED]").replace(/\b((?:token|secret|authorization|auth|api[-_]?key|password|credential|clientsecret|client_secret|code|refresh(?:_token)?)\s*[=:]\s*)[^\s,;]+/giu, "$1[REDACTED]");
|
|
63797
|
+
}
|
|
62555
63798
|
var compose = (middleware, onError, onNotFound) => {
|
|
62556
63799
|
return (context, next) => {
|
|
62557
63800
|
let index = -1;
|
|
@@ -65421,29 +66664,367 @@ var logger = (fn = console.log) => {
|
|
|
65421
66664
|
await log(fn, "-->", method, path, c.res.status, time(start));
|
|
65422
66665
|
};
|
|
65423
66666
|
};
|
|
66667
|
+
const ENGINE_COMMANDS = new Set([
|
|
66668
|
+
"get_caplet",
|
|
66669
|
+
"check_backend",
|
|
66670
|
+
"list_tools",
|
|
66671
|
+
"search_tools",
|
|
66672
|
+
"get_tool",
|
|
66673
|
+
"call_tool",
|
|
66674
|
+
"list_resources",
|
|
66675
|
+
"search_resources",
|
|
66676
|
+
"list_resource_templates",
|
|
66677
|
+
"read_resource",
|
|
66678
|
+
"list_prompts",
|
|
66679
|
+
"search_prompts",
|
|
66680
|
+
"get_prompt",
|
|
66681
|
+
"complete"
|
|
66682
|
+
]);
|
|
66683
|
+
async function dispatchRemoteCliRequest(request, context) {
|
|
66684
|
+
try {
|
|
66685
|
+
return {
|
|
66686
|
+
ok: true,
|
|
66687
|
+
result: await dispatch(request, context)
|
|
66688
|
+
};
|
|
66689
|
+
} catch (error) {
|
|
66690
|
+
const safe = toSafeError(error);
|
|
66691
|
+
const action = nextAction(safe.details);
|
|
66692
|
+
return {
|
|
66693
|
+
ok: false,
|
|
66694
|
+
error: {
|
|
66695
|
+
code: safe.code,
|
|
66696
|
+
message: redactControlErrorMessage(safe.message),
|
|
66697
|
+
...action ? { nextAction: action } : {}
|
|
66698
|
+
}
|
|
66699
|
+
};
|
|
66700
|
+
}
|
|
66701
|
+
}
|
|
66702
|
+
async function dispatch(request, context) {
|
|
66703
|
+
assertObject(request, "remote control request");
|
|
66704
|
+
assertObject(request.arguments, "remote control request arguments");
|
|
66705
|
+
if (request.command === "list") return listCaplets(loadConfigWithSources(context.configPath, context.projectConfigPath), { includeDisabled: optionalBoolean(request.arguments, "includeDisabled") ?? false });
|
|
66706
|
+
if (ENGINE_COMMANDS.has(request.command)) {
|
|
66707
|
+
const caplet = requiredString(request.arguments, "caplet");
|
|
66708
|
+
const toolRequest = requiredEngineRequest(request.arguments, request.command);
|
|
66709
|
+
const engine = new CapletsEngine(context);
|
|
66710
|
+
try {
|
|
66711
|
+
return await engine.execute(caplet, toolRequest);
|
|
66712
|
+
} finally {
|
|
66713
|
+
await engine.close();
|
|
66714
|
+
}
|
|
66715
|
+
}
|
|
66716
|
+
if (request.command === "init") return {
|
|
66717
|
+
remote: true,
|
|
66718
|
+
path: initConfig({
|
|
66719
|
+
...optionalProp("path", context.configPath),
|
|
66720
|
+
...optionalProp("force", optionalBoolean(request.arguments, "force"))
|
|
66721
|
+
})
|
|
66722
|
+
};
|
|
66723
|
+
if (request.command === "add") return dispatchAdd(request.arguments, context);
|
|
66724
|
+
if (request.command === "install") return {
|
|
66725
|
+
remote: true,
|
|
66726
|
+
...installCaplets(requiredString(request.arguments, "repo"), {
|
|
66727
|
+
...optionalProp("capletIds", optionalStringArray(request.arguments, "capletIds")),
|
|
66728
|
+
destinationRoot: context.projectCapletsRoot,
|
|
66729
|
+
...optionalProp("force", optionalBoolean(request.arguments, "force"))
|
|
66730
|
+
})
|
|
66731
|
+
};
|
|
66732
|
+
if (request.command === "complete_cli") {
|
|
66733
|
+
const shell = optionalString(request.arguments, "shell") ?? "bash";
|
|
66734
|
+
if (!completionShells.includes(shell)) return [];
|
|
66735
|
+
const engine = new CapletsEngine(context);
|
|
66736
|
+
try {
|
|
66737
|
+
return await engine.completeCliWords(optionalStringArray(request.arguments, "words") ?? [""]);
|
|
66738
|
+
} finally {
|
|
66739
|
+
await engine.close();
|
|
66740
|
+
}
|
|
66741
|
+
}
|
|
66742
|
+
if (request.command === "auth_list") return listAuthRows({
|
|
66743
|
+
...optionalProp("configPath", context.configPath),
|
|
66744
|
+
...optionalProp("authDir", context.authDir)
|
|
66745
|
+
});
|
|
66746
|
+
if (request.command === "auth_logout") return logoutAuthResult(requiredString(request.arguments, "server"), {
|
|
66747
|
+
...optionalProp("configPath", context.configPath),
|
|
66748
|
+
...optionalProp("authDir", context.authDir)
|
|
66749
|
+
});
|
|
66750
|
+
if (request.command === "auth_login_start") return startRemoteAuthLogin(requiredString(request.arguments, "server"), context);
|
|
66751
|
+
if (request.command === "auth_login_complete") return completeRemoteAuthLogin(requiredString(request.arguments, "flowId"), requiredString(request.arguments, "callbackUrl"), context);
|
|
66752
|
+
throw new CapletsError("UNKNOWN_OPERATION", `Unsupported remote control command ${request.command}`);
|
|
66753
|
+
}
|
|
66754
|
+
async function startRemoteAuthLogin(serverId, context) {
|
|
66755
|
+
if (!context.authFlowStore || !context.controlCallbackBaseUrl) throw new CapletsError("REQUEST_INVALID", "Remote auth login is not available on this server");
|
|
66756
|
+
const config = loadConfigWithSources(context.configPath, context.projectConfigPath).config;
|
|
66757
|
+
const target = findAuthTarget(serverId, config);
|
|
66758
|
+
assertLoginTarget(target, serverId);
|
|
66759
|
+
const flowId = randomUUID();
|
|
66760
|
+
const baseUrl = context.controlCallbackBaseUrl.endsWith("/") ? context.controlCallbackBaseUrl : `${context.controlCallbackBaseUrl}/`;
|
|
66761
|
+
const redirectUri = new URL(`auth/callback/${flowId}`, baseUrl).toString();
|
|
66762
|
+
const started = target.backend === "mcp" ? await startOAuthFlow(target, {
|
|
66763
|
+
redirectUri,
|
|
66764
|
+
...optionalProp("authDir", context.authDir)
|
|
66765
|
+
}) : await startGenericOAuthFlow(target, {
|
|
66766
|
+
redirectUri,
|
|
66767
|
+
...optionalProp("authDir", context.authDir)
|
|
66768
|
+
});
|
|
66769
|
+
if (!started.authorizationUrl) return {
|
|
66770
|
+
server: serverId,
|
|
66771
|
+
authenticated: true
|
|
66772
|
+
};
|
|
66773
|
+
const flow = context.authFlowStore.create({
|
|
66774
|
+
server: serverId,
|
|
66775
|
+
authorizationUrl: started.authorizationUrl,
|
|
66776
|
+
complete: started.complete
|
|
66777
|
+
}, flowId);
|
|
66778
|
+
return {
|
|
66779
|
+
server: serverId,
|
|
66780
|
+
flowId: flow.id,
|
|
66781
|
+
authorizationUrl: flow.authorizationUrl
|
|
66782
|
+
};
|
|
66783
|
+
}
|
|
66784
|
+
async function completeRemoteAuthLogin(flowId, callbackUrl, context) {
|
|
66785
|
+
const flow = context.authFlowStore?.get(flowId);
|
|
66786
|
+
if (!flow) throw new CapletsError("REQUEST_INVALID", `Unknown auth flow ${flowId}`);
|
|
66787
|
+
context.authFlowStore?.delete(flowId);
|
|
66788
|
+
await flow.complete(callbackUrl);
|
|
66789
|
+
return {
|
|
66790
|
+
server: flow.server,
|
|
66791
|
+
authenticated: true
|
|
66792
|
+
};
|
|
66793
|
+
}
|
|
66794
|
+
function dispatchAdd(args, context) {
|
|
66795
|
+
const kind = requiredString(args, "kind");
|
|
66796
|
+
const id = requiredString(args, "id");
|
|
66797
|
+
const options = remoteAddOptions$1(kind, optionalObject(args, "options"));
|
|
66798
|
+
switch (kind) {
|
|
66799
|
+
case "cli": return {
|
|
66800
|
+
remote: true,
|
|
66801
|
+
label: "CLI",
|
|
66802
|
+
...addCliCaplet(id, {
|
|
66803
|
+
...options,
|
|
66804
|
+
destinationRoot: context.projectCapletsRoot,
|
|
66805
|
+
print: false
|
|
66806
|
+
})
|
|
66807
|
+
};
|
|
66808
|
+
case "mcp": return {
|
|
66809
|
+
remote: true,
|
|
66810
|
+
label: "MCP",
|
|
66811
|
+
...addMcpCaplet(id, {
|
|
66812
|
+
...options,
|
|
66813
|
+
destinationRoot: context.projectCapletsRoot,
|
|
66814
|
+
print: false
|
|
66815
|
+
})
|
|
66816
|
+
};
|
|
66817
|
+
case "openapi": return {
|
|
66818
|
+
remote: true,
|
|
66819
|
+
label: "OpenAPI",
|
|
66820
|
+
...addOpenApiCaplet(id, {
|
|
66821
|
+
...options,
|
|
66822
|
+
destinationRoot: context.projectCapletsRoot,
|
|
66823
|
+
print: false
|
|
66824
|
+
})
|
|
66825
|
+
};
|
|
66826
|
+
case "graphql": return {
|
|
66827
|
+
remote: true,
|
|
66828
|
+
label: "GraphQL",
|
|
66829
|
+
...addGraphqlCaplet(id, {
|
|
66830
|
+
...options,
|
|
66831
|
+
destinationRoot: context.projectCapletsRoot,
|
|
66832
|
+
print: false
|
|
66833
|
+
})
|
|
66834
|
+
};
|
|
66835
|
+
case "http": return {
|
|
66836
|
+
remote: true,
|
|
66837
|
+
label: "HTTP",
|
|
66838
|
+
...addHttpCaplet(id, {
|
|
66839
|
+
...options,
|
|
66840
|
+
destinationRoot: context.projectCapletsRoot,
|
|
66841
|
+
print: false
|
|
66842
|
+
})
|
|
66843
|
+
};
|
|
66844
|
+
default: throw new CapletsError("REQUEST_INVALID", "add.kind must be cli, mcp, openapi, graphql, or http");
|
|
66845
|
+
}
|
|
66846
|
+
}
|
|
66847
|
+
function optionalProp(key, value) {
|
|
66848
|
+
return value === void 0 ? {} : { [key]: value };
|
|
66849
|
+
}
|
|
66850
|
+
function assertObject(value, label) {
|
|
66851
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new CapletsError("REQUEST_INVALID", `${label} must be an object`);
|
|
66852
|
+
}
|
|
66853
|
+
function requiredString(args, key) {
|
|
66854
|
+
const value = args[key];
|
|
66855
|
+
if (typeof value !== "string" || value.length === 0) throw new CapletsError("REQUEST_INVALID", `${key} must be a non-empty string`);
|
|
66856
|
+
return value;
|
|
66857
|
+
}
|
|
66858
|
+
function optionalString(args, key) {
|
|
66859
|
+
const value = args[key];
|
|
66860
|
+
if (value === void 0) return;
|
|
66861
|
+
if (typeof value !== "string") throw new CapletsError("REQUEST_INVALID", `${key} must be a string`);
|
|
66862
|
+
return value;
|
|
66863
|
+
}
|
|
66864
|
+
function optionalObject(args, key) {
|
|
66865
|
+
const value = args[key];
|
|
66866
|
+
if (value === void 0) return {};
|
|
66867
|
+
assertObject(value, key);
|
|
66868
|
+
return value;
|
|
66869
|
+
}
|
|
66870
|
+
function requiredEngineRequest(args, command) {
|
|
66871
|
+
const toolRequest = optionalObject(args, "request");
|
|
66872
|
+
if (typeof toolRequest.operation !== "string") throw new CapletsError("REQUEST_INVALID", "request.operation must be a string");
|
|
66873
|
+
if (toolRequest.operation !== command) throw new CapletsError("REQUEST_INVALID", `request.operation must match remote command ${command}`);
|
|
66874
|
+
return toolRequest;
|
|
66875
|
+
}
|
|
66876
|
+
function remoteAddOptions$1(kind, options) {
|
|
66877
|
+
rejectServerOwnedAddOptions(options);
|
|
66878
|
+
switch (kind) {
|
|
66879
|
+
case "cli": return pickOptions(options, {
|
|
66880
|
+
repo: "string",
|
|
66881
|
+
include: "string",
|
|
66882
|
+
command: "string",
|
|
66883
|
+
force: "boolean"
|
|
66884
|
+
});
|
|
66885
|
+
case "mcp": return pickOptions(options, {
|
|
66886
|
+
command: "string",
|
|
66887
|
+
arg: "string-array",
|
|
66888
|
+
cwd: "string",
|
|
66889
|
+
env: "string-array",
|
|
66890
|
+
url: "string",
|
|
66891
|
+
transport: "string",
|
|
66892
|
+
tokenEnv: "string",
|
|
66893
|
+
force: "boolean"
|
|
66894
|
+
});
|
|
66895
|
+
case "openapi": return pickOptions(options, {
|
|
66896
|
+
spec: "string",
|
|
66897
|
+
baseUrl: "string",
|
|
66898
|
+
tokenEnv: "string",
|
|
66899
|
+
force: "boolean"
|
|
66900
|
+
});
|
|
66901
|
+
case "graphql": return pickOptions(options, {
|
|
66902
|
+
endpointUrl: "string",
|
|
66903
|
+
schema: "string",
|
|
66904
|
+
introspection: "boolean",
|
|
66905
|
+
tokenEnv: "string",
|
|
66906
|
+
force: "boolean"
|
|
66907
|
+
});
|
|
66908
|
+
case "http": return pickOptions(options, {
|
|
66909
|
+
baseUrl: "string",
|
|
66910
|
+
action: "string-array",
|
|
66911
|
+
tokenEnv: "string",
|
|
66912
|
+
force: "boolean"
|
|
66913
|
+
});
|
|
66914
|
+
default: return options;
|
|
66915
|
+
}
|
|
66916
|
+
}
|
|
66917
|
+
function pickOptions(options, schema) {
|
|
66918
|
+
const next = {};
|
|
66919
|
+
for (const [key, type] of Object.entries(schema)) {
|
|
66920
|
+
const value = options[key];
|
|
66921
|
+
if (value === void 0) continue;
|
|
66922
|
+
validateOptionType(key, value, type);
|
|
66923
|
+
next[key] = value;
|
|
66924
|
+
}
|
|
66925
|
+
return next;
|
|
66926
|
+
}
|
|
66927
|
+
function rejectServerOwnedAddOptions(options) {
|
|
66928
|
+
if ("output" in options) throw new CapletsError("REQUEST_INVALID", "Remote add output is not supported remotely; the server owns destinationRoot and output path selection");
|
|
66929
|
+
for (const key of ["destinationRoot", "print"]) if (key in options) throw new CapletsError("REQUEST_INVALID", `Remote add ${key} is not supported remotely; the server owns destinationRoot and print behavior`);
|
|
66930
|
+
}
|
|
66931
|
+
function validateOptionType(key, value, type) {
|
|
66932
|
+
if (type === "string" && typeof value !== "string") throw new CapletsError("REQUEST_INVALID", `add.options.${key} must be a string`);
|
|
66933
|
+
if (type === "boolean" && typeof value !== "boolean") throw new CapletsError("REQUEST_INVALID", `add.options.${key} must be a boolean`);
|
|
66934
|
+
if (type === "string-array") {
|
|
66935
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) throw new CapletsError("REQUEST_INVALID", `add.options.${key} must be an array of strings`);
|
|
66936
|
+
}
|
|
66937
|
+
}
|
|
66938
|
+
function optionalBoolean(args, key) {
|
|
66939
|
+
const value = args[key];
|
|
66940
|
+
if (value === void 0) return;
|
|
66941
|
+
if (typeof value !== "boolean") throw new CapletsError("REQUEST_INVALID", `${key} must be a boolean`);
|
|
66942
|
+
return value;
|
|
66943
|
+
}
|
|
66944
|
+
function optionalStringArray(args, key) {
|
|
66945
|
+
const value = args[key];
|
|
66946
|
+
if (value === void 0) return;
|
|
66947
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) throw new CapletsError("REQUEST_INVALID", `${key} must be an array of strings`);
|
|
66948
|
+
return value;
|
|
66949
|
+
}
|
|
66950
|
+
function nextAction(details) {
|
|
66951
|
+
if (details && typeof details === "object" && "nextAction" in details) {
|
|
66952
|
+
const value = details.nextAction;
|
|
66953
|
+
return typeof value === "string" ? value : void 0;
|
|
66954
|
+
}
|
|
66955
|
+
}
|
|
66956
|
+
function redactControlErrorMessage(message) {
|
|
66957
|
+
return message.replace(/(["'])(authorization|(?:access[_-]?)?token|refresh(?:[_-]?token)?|password|client[_-]?secret|clientsecret|api[-_]?key|apikey|secret|credential|code)\1\s*:\s*(["'])(?:\\.|[^\\])*?\3/giu, "$1$2$1:$3[REDACTED]$3").replace(/\b(authorization\s*:\s*(?:basic|bearer)\s+)[^\s,;]+/giu, "$1[REDACTED]").replace(/\b((?:access[_-]?)?token|refresh(?:[_-]?token)?|password|client[_-]?secret|clientsecret|api[-_]?key|apikey|secret|credential|code)(\s*[=:]\s*)[^\s,;]+/giu, "$1$2[REDACTED]");
|
|
66958
|
+
}
|
|
66959
|
+
const DEFAULT_AUTH_FLOW_TTL_MS = 600 * 1e3;
|
|
66960
|
+
var RemoteAuthFlowStore = class {
|
|
66961
|
+
options;
|
|
66962
|
+
flows = /* @__PURE__ */ new Map();
|
|
66963
|
+
constructor(options = {}) {
|
|
66964
|
+
this.options = options;
|
|
66965
|
+
}
|
|
66966
|
+
create(flow, id = randomUUID()) {
|
|
66967
|
+
this.pruneExpired();
|
|
66968
|
+
const created = {
|
|
66969
|
+
id,
|
|
66970
|
+
createdAt: this.now(),
|
|
66971
|
+
...flow
|
|
66972
|
+
};
|
|
66973
|
+
this.flows.set(created.id, created);
|
|
66974
|
+
return { ...created };
|
|
66975
|
+
}
|
|
66976
|
+
get(id) {
|
|
66977
|
+
this.pruneExpired();
|
|
66978
|
+
const flow = this.flows.get(id);
|
|
66979
|
+
if (flow && this.isExpired(flow)) {
|
|
66980
|
+
this.flows.delete(id);
|
|
66981
|
+
return;
|
|
66982
|
+
}
|
|
66983
|
+
return flow;
|
|
66984
|
+
}
|
|
66985
|
+
delete(id) {
|
|
66986
|
+
this.flows.delete(id);
|
|
66987
|
+
}
|
|
66988
|
+
pruneExpired() {
|
|
66989
|
+
for (const [id, flow] of this.flows) if (this.isExpired(flow)) this.flows.delete(id);
|
|
66990
|
+
}
|
|
66991
|
+
isExpired(flow) {
|
|
66992
|
+
return this.now() - flow.createdAt > (this.options.ttlMs ?? DEFAULT_AUTH_FLOW_TTL_MS);
|
|
66993
|
+
}
|
|
66994
|
+
now() {
|
|
66995
|
+
return this.options.now?.() ?? Date.now();
|
|
66996
|
+
}
|
|
66997
|
+
};
|
|
65424
66998
|
function createHttpServeApp(options, engine, io = {}) {
|
|
65425
66999
|
const app = new Hono();
|
|
65426
67000
|
const sessions = /* @__PURE__ */ new Map();
|
|
65427
67001
|
const writeErr = io.writeErr ?? process.stderr.write.bind(process.stderr);
|
|
67002
|
+
const paths = servicePaths(options.path);
|
|
67003
|
+
const authFlowStore = io.authFlowStore ?? new RemoteAuthFlowStore();
|
|
65428
67004
|
app.use("*", logger((message, ...rest) => {
|
|
65429
67005
|
writeErr(`${[message, ...rest].join(" ")}\n`);
|
|
65430
67006
|
}));
|
|
65431
|
-
app.get(
|
|
67007
|
+
app.get(paths.base, (c) => c.json({
|
|
65432
67008
|
name: "caplets",
|
|
65433
67009
|
transport: "http",
|
|
65434
|
-
|
|
65435
|
-
|
|
67010
|
+
base: paths.base,
|
|
67011
|
+
mcp: paths.mcp,
|
|
67012
|
+
control: paths.control,
|
|
67013
|
+
health: paths.health,
|
|
65436
67014
|
auth: {
|
|
65437
67015
|
type: "basic",
|
|
65438
67016
|
enabled: options.auth.enabled
|
|
65439
67017
|
}
|
|
65440
67018
|
}));
|
|
65441
|
-
app.get(
|
|
67019
|
+
app.get(paths.health, (c) => c.json({
|
|
65442
67020
|
status: "ok",
|
|
65443
67021
|
transport: "http",
|
|
65444
|
-
|
|
67022
|
+
base: paths.base,
|
|
67023
|
+
mcpPath: paths.mcp,
|
|
67024
|
+
controlPath: paths.control,
|
|
67025
|
+
healthPath: paths.health
|
|
65445
67026
|
}));
|
|
65446
|
-
app.all(
|
|
67027
|
+
app.all(paths.mcp, basicAuth(options.auth), async (c) => {
|
|
65447
67028
|
const sessionId = c.req.header("mcp-session-id");
|
|
65448
67029
|
if (sessionId) {
|
|
65449
67030
|
const existing = sessions.get(sessionId);
|
|
@@ -65474,6 +67055,36 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
65474
67055
|
sessions.set(nextSessionId, session);
|
|
65475
67056
|
return session.transport.handleRequest(c);
|
|
65476
67057
|
});
|
|
67058
|
+
app.post(paths.control, basicAuth(options.auth), async (c) => {
|
|
67059
|
+
let request;
|
|
67060
|
+
try {
|
|
67061
|
+
const parsed = await c.req.json();
|
|
67062
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new CapletsError("REQUEST_INVALID", "Control request JSON must be an object");
|
|
67063
|
+
request = parsed;
|
|
67064
|
+
} catch (error) {
|
|
67065
|
+
const safe = toSafeError(error instanceof CapletsError ? error : new CapletsError("REQUEST_INVALID", "Control request body must be valid JSON", error), "REQUEST_INVALID");
|
|
67066
|
+
return c.json({
|
|
67067
|
+
ok: false,
|
|
67068
|
+
error: {
|
|
67069
|
+
code: safe.code,
|
|
67070
|
+
message: safe.message
|
|
67071
|
+
}
|
|
67072
|
+
});
|
|
67073
|
+
}
|
|
67074
|
+
return c.json(await dispatchRemoteCliRequest(request, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name))));
|
|
67075
|
+
});
|
|
67076
|
+
app.get(routePath(paths.control, "auth/callback/:flowId"), async (c) => {
|
|
67077
|
+
const flowId = c.req.param("flowId");
|
|
67078
|
+
const result = await dispatchRemoteCliRequest({
|
|
67079
|
+
command: "auth_login_complete",
|
|
67080
|
+
arguments: {
|
|
67081
|
+
flowId,
|
|
67082
|
+
callbackUrl: c.req.url
|
|
67083
|
+
}
|
|
67084
|
+
}, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name)));
|
|
67085
|
+
if (!result.ok) writeErr(`Caplets authentication failed for flow ${flowId}: ${result.error.message}\n`);
|
|
67086
|
+
return result.ok ? c.text("Caplets authentication complete. You can return to your terminal.") : c.text("Caplets authentication failed. Check server logs for details.", 400);
|
|
67087
|
+
});
|
|
65477
67088
|
app.notFound((c) => c.json({ error: "not_found" }, 404));
|
|
65478
67089
|
app.closeCapletsSessions = async () => {
|
|
65479
67090
|
await Promise.allSettled([...sessions.values()].map(async (session) => {
|
|
@@ -65484,19 +67095,66 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
65484
67095
|
if (options.warnUnauthenticatedNetwork) writeErr(`Warning: Caplets MCP HTTP server is listening on ${options.host} without authentication.\n`);
|
|
65485
67096
|
return app;
|
|
65486
67097
|
}
|
|
67098
|
+
function controlContext(io, writeErr, authFlowStore, requestUrl, controlPath, trustProxy, header) {
|
|
67099
|
+
return {
|
|
67100
|
+
...io.control,
|
|
67101
|
+
projectCapletsRoot: io.control?.projectCapletsRoot ?? resolveProjectCapletsRoot(),
|
|
67102
|
+
authFlowStore,
|
|
67103
|
+
controlCallbackBaseUrl: new URL(controlPath, publicRequestOrigin(requestUrl, trustProxy, header)).toString(),
|
|
67104
|
+
writeErr
|
|
67105
|
+
};
|
|
67106
|
+
}
|
|
67107
|
+
function publicRequestOrigin(requestUrl, trustProxy, header) {
|
|
67108
|
+
const url = new URL(requestUrl);
|
|
67109
|
+
if (!trustProxy) return `${url.protocol.slice(0, -1)}://${header("host") ?? url.host}`;
|
|
67110
|
+
const forwardedProto = firstForwardedValue(header("x-forwarded-proto"));
|
|
67111
|
+
const forwardedHost = firstForwardedValue(header("x-forwarded-host"));
|
|
67112
|
+
return `${forwardedProto === "http" || forwardedProto === "https" ? forwardedProto : url.protocol.slice(0, -1)}://${forwardedHost ?? header("host") ?? url.host}`;
|
|
67113
|
+
}
|
|
67114
|
+
function firstForwardedValue(value) {
|
|
67115
|
+
return value?.split(",", 1)[0]?.trim() || void 0;
|
|
67116
|
+
}
|
|
65487
67117
|
async function serveHttp(options, engineOptions = {}, writeErr = (value) => process.stderr.write(value)) {
|
|
65488
67118
|
const engine = new CapletsEngine(engineOptions);
|
|
65489
|
-
const app = createHttpServeApp(options, engine, {
|
|
67119
|
+
const app = createHttpServeApp(options, engine, {
|
|
67120
|
+
writeErr,
|
|
67121
|
+
control: {
|
|
67122
|
+
...engineOptions,
|
|
67123
|
+
projectCapletsRoot: projectCapletsRootForEngineOptions(engineOptions)
|
|
67124
|
+
}
|
|
67125
|
+
});
|
|
67126
|
+
const paths = servicePaths(options.path);
|
|
67127
|
+
const origin = `http://${formatHost(options.host)}:${options.port}`;
|
|
67128
|
+
const baseUrl = `${origin}${paths.base === "/" ? "" : paths.base}`;
|
|
65490
67129
|
installHttpSignalHandlers(serve({
|
|
65491
67130
|
fetch: app.fetch,
|
|
65492
67131
|
hostname: options.host,
|
|
65493
67132
|
port: options.port
|
|
65494
67133
|
}, () => {
|
|
65495
|
-
writeErr(`Caplets
|
|
65496
|
-
writeErr(`
|
|
67134
|
+
writeErr(`Caplets HTTP service listening on ${baseUrl}\n`);
|
|
67135
|
+
writeErr(`MCP endpoint: ${origin}${paths.mcp}\n`);
|
|
67136
|
+
writeErr(`Control endpoint: ${origin}${paths.control}\n`);
|
|
67137
|
+
writeErr(`Health check: ${origin}${paths.health}\n`);
|
|
65497
67138
|
writeErr(`Basic Auth: ${options.auth.enabled ? `enabled (user: ${options.auth.user})` : "disabled"}\n`);
|
|
65498
67139
|
}), app, engine, writeErr);
|
|
65499
67140
|
}
|
|
67141
|
+
function projectCapletsRootForEngineOptions(engineOptions) {
|
|
67142
|
+
return engineOptions.projectConfigPath ? resolveProjectCapletsRootForConfigPath(engineOptions.projectConfigPath) : resolveProjectCapletsRoot();
|
|
67143
|
+
}
|
|
67144
|
+
function resolveProjectCapletsRootForConfigPath(projectConfigPath) {
|
|
67145
|
+
return dirname(projectConfigPath);
|
|
67146
|
+
}
|
|
67147
|
+
function routePath(base, path) {
|
|
67148
|
+
return base === "/" ? `/${path}` : `${base}/${path}`;
|
|
67149
|
+
}
|
|
67150
|
+
function servicePaths(base) {
|
|
67151
|
+
return {
|
|
67152
|
+
base,
|
|
67153
|
+
mcp: routePath(base, "mcp"),
|
|
67154
|
+
control: routePath(base, "control"),
|
|
67155
|
+
health: routePath(base, "healthz")
|
|
67156
|
+
};
|
|
67157
|
+
}
|
|
65500
67158
|
async function createHttpSession(engine, sessionId, options, onClose) {
|
|
65501
67159
|
const transport = new StreamableHTTPTransport({
|
|
65502
67160
|
sessionIdGenerator: () => sessionId,
|
|
@@ -65574,7 +67232,8 @@ const HTTP_ONLY_OPTIONS = [
|
|
|
65574
67232
|
"path",
|
|
65575
67233
|
"user",
|
|
65576
67234
|
"password",
|
|
65577
|
-
"allowUnauthenticatedHttp"
|
|
67235
|
+
"allowUnauthenticatedHttp",
|
|
67236
|
+
"trustProxy"
|
|
65578
67237
|
];
|
|
65579
67238
|
function resolveServeOptions(raw, env = process.env) {
|
|
65580
67239
|
const transport = parseTransport(raw.transport ?? "stdio");
|
|
@@ -65583,9 +67242,10 @@ function resolveServeOptions(raw, env = process.env) {
|
|
|
65583
67242
|
if (invalid.length > 0) throw new CapletsError("REQUEST_INVALID", `${invalid.map((key) => `--${key}`).join(", ")} ${invalid.length === 1 ? "is" : "are"} only valid with --transport http`);
|
|
65584
67243
|
return { transport };
|
|
65585
67244
|
}
|
|
65586
|
-
const
|
|
65587
|
-
const
|
|
65588
|
-
const
|
|
67245
|
+
const serverUrl = env.CAPLETS_SERVER_URL ? parseServeServerUrl(nonEmpty(env.CAPLETS_SERVER_URL, "CAPLETS_SERVER_URL")) : void 0;
|
|
67246
|
+
const host = nonEmpty(raw.host, "--host") ?? serverUrlHost(serverUrl) ?? "127.0.0.1";
|
|
67247
|
+
const port = parsePort(raw.port ?? (serverUrl?.port ? Number(serverUrl.port) : 5387));
|
|
67248
|
+
const path = normalizeHttpPath(raw.path ?? serverUrl?.pathname ?? "/");
|
|
65589
67249
|
const userWasExplicit = raw.user !== void 0 || hasEnv(env.CAPLETS_SERVER_USER);
|
|
65590
67250
|
const user = nonEmpty(raw.user, "--user") ?? nonEmpty(env.CAPLETS_SERVER_USER, "CAPLETS_SERVER_USER") ?? "caplets";
|
|
65591
67251
|
const password = nonEmpty(raw.password, "--password") ?? nonEmpty(env.CAPLETS_SERVER_PASSWORD, "CAPLETS_SERVER_PASSWORD");
|
|
@@ -65607,13 +67267,22 @@ function resolveServeOptions(raw, env = process.env) {
|
|
|
65607
67267
|
path,
|
|
65608
67268
|
auth,
|
|
65609
67269
|
warnUnauthenticatedNetwork: !loopback && !auth.enabled,
|
|
65610
|
-
loopback
|
|
67270
|
+
loopback,
|
|
67271
|
+
trustProxy: raw.trustProxy === true
|
|
65611
67272
|
};
|
|
65612
67273
|
}
|
|
65613
67274
|
function isLoopbackHost(host) {
|
|
65614
67275
|
const normalized = host.toLocaleLowerCase();
|
|
65615
67276
|
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
|
|
65616
67277
|
}
|
|
67278
|
+
function parseServeServerUrl(value) {
|
|
67279
|
+
try {
|
|
67280
|
+
return parseServerBaseUrl(value);
|
|
67281
|
+
} catch (error) {
|
|
67282
|
+
if (error instanceof CapletsError && error.message.includes("must use https except loopback development URLs")) throw new CapletsError("REQUEST_INVALID", "CAPLETS_SERVER_URL must use https except loopback development URLs; use --host, --port, and --path separately for non-loopback HTTP bind addresses.");
|
|
67283
|
+
throw error;
|
|
67284
|
+
}
|
|
67285
|
+
}
|
|
65617
67286
|
function parseTransport(value) {
|
|
65618
67287
|
if (value === "stdio" || value === "http") return value;
|
|
65619
67288
|
throw new CapletsError("REQUEST_INVALID", `Expected --transport to be stdio or http, got ${value}`);
|
|
@@ -65628,6 +67297,9 @@ function normalizeHttpPath(value) {
|
|
|
65628
67297
|
if (value.includes("?") || value.includes("#")) throw new CapletsError("REQUEST_INVALID", "HTTP --path must not include a query string or fragment");
|
|
65629
67298
|
return value === "/" ? value : value.replace(/\/+$/u, "");
|
|
65630
67299
|
}
|
|
67300
|
+
function serverUrlHost(url) {
|
|
67301
|
+
return url?.hostname.replace(/^\[(.*)\]$/u, "$1");
|
|
67302
|
+
}
|
|
65631
67303
|
function nonEmpty(value, label) {
|
|
65632
67304
|
if (value === void 0) return;
|
|
65633
67305
|
const trimmed = value.trim();
|
|
@@ -65751,9 +67423,14 @@ async function runCli(args, io = {}) {
|
|
|
65751
67423
|
throw error;
|
|
65752
67424
|
}
|
|
65753
67425
|
}
|
|
67426
|
+
function normalizeCompletionWords(words) {
|
|
67427
|
+
return words.map((word) => word === "__CAPLETS_TRAILING_SPACE__" ? "" : word);
|
|
67428
|
+
}
|
|
65754
67429
|
function createProgram(io = {}) {
|
|
65755
67430
|
const writeOut = io.writeOut ?? ((value) => process.stdout.write(value));
|
|
65756
67431
|
const writeErr = io.writeErr ?? ((value) => process.stderr.write(value));
|
|
67432
|
+
const env = io.env ?? process.env;
|
|
67433
|
+
const currentConfigPath = () => envConfigPath(env);
|
|
65757
67434
|
const setExitCode = io.setExitCode ?? ((code) => {
|
|
65758
67435
|
process.exitCode = code;
|
|
65759
67436
|
});
|
|
@@ -65763,42 +67440,98 @@ function createProgram(io = {}) {
|
|
|
65763
67440
|
writeErr,
|
|
65764
67441
|
outputError: (value, write) => write(value)
|
|
65765
67442
|
});
|
|
65766
|
-
program.command(
|
|
67443
|
+
program.command(cliCommands.completion).description("Print a shell completion script.").argument("<shell>", "completion shell: bash, zsh, fish, powershell, or cmd").action((shell) => {
|
|
67444
|
+
if (!completionShells.includes(shell)) throw new CapletsError("REQUEST_INVALID", "completion shell must be bash, zsh, fish, powershell, or cmd");
|
|
67445
|
+
writeOut(completionScript(shell));
|
|
67446
|
+
});
|
|
67447
|
+
program.command(cliCommands.completeHidden, { hidden: true }).description("Internal shell completion endpoint.").option("--shell <shell>", "completion shell").allowUnknownOption(true).argument("[words...]", "words to complete").action(async (words, options) => {
|
|
67448
|
+
const shell = completionShells.includes(options.shell) ? options.shell : "bash";
|
|
67449
|
+
const remote = remoteClientForCli(io);
|
|
67450
|
+
const configPath = currentConfigPath();
|
|
67451
|
+
const completionWords = normalizeCompletionWords(words);
|
|
67452
|
+
let suggestions = [];
|
|
67453
|
+
try {
|
|
67454
|
+
suggestions = remote ? await remote.request("complete_cli", {
|
|
67455
|
+
shell,
|
|
67456
|
+
words: completionWords
|
|
67457
|
+
}) : await completeCliWords(completionWords, configPath ? { configPath } : {});
|
|
67458
|
+
} catch {
|
|
67459
|
+
suggestions = [];
|
|
67460
|
+
}
|
|
67461
|
+
if (suggestions.length > 0) writeOut(`${suggestions.join("\n")}\n`);
|
|
67462
|
+
});
|
|
67463
|
+
program.command(cliCommands.serve).description("Serve configured Caplets as an MCP server.").option("--transport <transport>", "server transport: stdio or http").option("--host <host>", "HTTP bind host").option("--port <port>", "HTTP bind port").option("--path <path>", "HTTP service base path").option("--user <user>", "HTTP Basic Auth username").option("--password <password>", "HTTP Basic Auth password").option("--allow-unauthenticated-http", "allow unauthenticated HTTP serving on non-loopback hosts").option("--trust-proxy", "trust X-Forwarded-* headers from a reverse proxy").action(async (options) => {
|
|
65767
67464
|
const resolved = resolveServeOptions(options);
|
|
65768
|
-
const configPath =
|
|
67465
|
+
const configPath = currentConfigPath();
|
|
65769
67466
|
await (io.serve ?? ((serveOptions) => serveResolvedCaplets(serveOptions, {
|
|
65770
67467
|
...configPath ? { configPath } : {},
|
|
65771
67468
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
65772
67469
|
}, writeErr)))(resolved);
|
|
65773
67470
|
});
|
|
65774
|
-
program.command(
|
|
65775
|
-
const
|
|
67471
|
+
program.command(cliCommands.init).description("Create a starter Caplets config file.").option("--force", "overwrite an existing config file").action(async (options) => {
|
|
67472
|
+
const remote = remoteClientForCli(io);
|
|
67473
|
+
if (remote) {
|
|
67474
|
+
writeOut(`Created remote Caplets config at ${(await remote.request("init", { force: Boolean(options.force) })).path}\n`);
|
|
67475
|
+
return;
|
|
67476
|
+
}
|
|
67477
|
+
const configPath = currentConfigPath();
|
|
65776
67478
|
writeOut(`Created Caplets config at ${initConfig({
|
|
65777
67479
|
...configPath ? { path: configPath } : {},
|
|
65778
67480
|
force: Boolean(options.force)
|
|
65779
67481
|
})}\n`);
|
|
65780
67482
|
});
|
|
65781
|
-
program.command(
|
|
65782
|
-
const
|
|
67483
|
+
program.command(cliCommands.list).description("List configured Caplets.").option("--all", "include disabled Caplets").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action(async (options) => {
|
|
67484
|
+
const includeDisabled = Boolean(options.all);
|
|
67485
|
+
const remote = remoteClientForCli(io);
|
|
67486
|
+
if (remote) {
|
|
67487
|
+
const rows = await remote.request("list", { includeDisabled });
|
|
67488
|
+
if (options.json || options.format === "json") {
|
|
67489
|
+
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
67490
|
+
return;
|
|
67491
|
+
}
|
|
67492
|
+
writeOut(formatCapletList(rows, options.format ?? "plain"));
|
|
67493
|
+
return;
|
|
67494
|
+
}
|
|
67495
|
+
const rows = listCaplets(loadConfigWithSources(currentConfigPath()), { includeDisabled });
|
|
65783
67496
|
if (options.json || options.format === "json") {
|
|
65784
67497
|
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
65785
67498
|
return;
|
|
65786
67499
|
}
|
|
65787
67500
|
writeOut(formatCapletList(rows, options.format ?? "plain"));
|
|
65788
67501
|
});
|
|
65789
|
-
program.command(
|
|
67502
|
+
program.command(cliCommands.install).description("Install Caplets from a repo's caplets directory.").argument("<repo>", "local repo path, Git URL, or GitHub owner/repo").argument("[caplets...]", "optional Caplet IDs to install").option("-g, --global", "install to the user Caplets root").option("--force", "overwrite installed Caplets").action(async (repo, capletIds, options) => {
|
|
67503
|
+
const remote = remoteClientForCli(io);
|
|
67504
|
+
if (remote) {
|
|
67505
|
+
if (options.global) writeErr("Warning: --global is not supported in remote mode; the server controls the installation destination.\n");
|
|
67506
|
+
const result = await remote.request("install", {
|
|
67507
|
+
repo,
|
|
67508
|
+
capletIds,
|
|
67509
|
+
force: Boolean(options.force)
|
|
67510
|
+
});
|
|
67511
|
+
for (const caplet of result.installed) writeOut(`Installed ${caplet.id} to remote ${caplet.destination}\n`);
|
|
67512
|
+
return;
|
|
67513
|
+
}
|
|
65790
67514
|
const result = installCaplets(repo, {
|
|
65791
67515
|
capletIds,
|
|
65792
67516
|
force: Boolean(options.force),
|
|
65793
|
-
destinationRoot: options.global ? resolveCapletsRoot(resolveConfigPath(
|
|
67517
|
+
destinationRoot: options.global ? resolveCapletsRoot(resolveConfigPath(currentConfigPath())) : resolveProjectCapletsRoot()
|
|
65794
67518
|
});
|
|
65795
67519
|
for (const caplet of result.installed) writeOut(`Installed ${caplet.id} to ${caplet.destination}\n`);
|
|
65796
67520
|
});
|
|
65797
|
-
const add = program.command(
|
|
65798
|
-
add.command("cli").description("Add a CLI tools Caplet.").argument("<id>", "Caplet ID/display seed").option("--repo <path>", "repository path to inspect").option("--include <items>", "comma-separated generators to include: git,gh,package").option("--command <name>", "single CLI command template to generate").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action((id, options) => {
|
|
67521
|
+
const add = program.command(cliCommands.add).description("Add generated Caplet files.");
|
|
67522
|
+
add.command("cli").description("Add a CLI tools Caplet.").argument("<id>", "Caplet ID/display seed").option("--repo <path>", "repository path to inspect").option("--include <items>", "comma-separated generators to include: git,gh,package").option("--command <name>", "single CLI command template to generate").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action(async (id, options) => {
|
|
67523
|
+
const remote = remoteClientForCli(io);
|
|
67524
|
+
if (remote) {
|
|
67525
|
+
writeAddResult(writeOut, "CLI", await remote.request("add", {
|
|
67526
|
+
kind: "cli",
|
|
67527
|
+
id,
|
|
67528
|
+
options: remoteAddOptions(options)
|
|
67529
|
+
}));
|
|
67530
|
+
return;
|
|
67531
|
+
}
|
|
65799
67532
|
const result = addCliCaplet(id, {
|
|
65800
67533
|
...options,
|
|
65801
|
-
destinationRoot: options.global ? resolveCapletsRoot(resolveConfigPath(
|
|
67534
|
+
destinationRoot: options.global ? resolveCapletsRoot(resolveConfigPath(currentConfigPath())) : resolveProjectCapletsRoot()
|
|
65802
67535
|
});
|
|
65803
67536
|
if (result.path) {
|
|
65804
67537
|
writeOut(`Wrote CLI Caplet to ${result.path}\n`);
|
|
@@ -65806,58 +67539,100 @@ function createProgram(io = {}) {
|
|
|
65806
67539
|
}
|
|
65807
67540
|
writeOut(result.text);
|
|
65808
67541
|
});
|
|
65809
|
-
add.command("mcp").description("Add an MCP backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--command <name>", "stdio command").option("--arg <value>", "stdio command argument", collect, []).option("--cwd <path>", "stdio working directory").option("--env <KEY=VALUE>", "stdio environment variable", collect, []).option("--url <url>", "remote MCP server URL").option("--transport <transport>", "remote transport: http or sse").option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action((id, options) => {
|
|
67542
|
+
add.command("mcp").description("Add an MCP backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--command <name>", "stdio command").option("--arg <value>", "stdio command argument", collect, []).option("--cwd <path>", "stdio working directory").option("--env <KEY=VALUE>", "stdio environment variable", collect, []).option("--url <url>", "remote MCP server URL").option("--transport <transport>", "remote transport: http or sse").option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action(async (id, options) => {
|
|
67543
|
+
const remote = remoteClientForCli(io);
|
|
67544
|
+
if (remote) {
|
|
67545
|
+
writeAddResult(writeOut, "MCP", await remote.request("add", {
|
|
67546
|
+
kind: "mcp",
|
|
67547
|
+
id,
|
|
67548
|
+
options: remoteAddOptions(options)
|
|
67549
|
+
}));
|
|
67550
|
+
return;
|
|
67551
|
+
}
|
|
65810
67552
|
writeAddResult(writeOut, "MCP", addMcpCaplet(id, {
|
|
65811
67553
|
...options,
|
|
65812
|
-
destinationRoot: addDestinationRoot(options)
|
|
67554
|
+
destinationRoot: addDestinationRoot(options, currentConfigPath())
|
|
65813
67555
|
}));
|
|
65814
67556
|
});
|
|
65815
|
-
add.command("openapi").description("Add an OpenAPI backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--spec <path-or-url>", "OpenAPI spec path or URL").option("--base-url <url>", "request base URL override").option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action((id, options) => {
|
|
67557
|
+
add.command("openapi").description("Add an OpenAPI backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--spec <path-or-url>", "OpenAPI spec path or URL").option("--base-url <url>", "request base URL override").option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action(async (id, options) => {
|
|
67558
|
+
const remote = remoteClientForCli(io);
|
|
67559
|
+
if (remote) {
|
|
67560
|
+
writeAddResult(writeOut, "OpenAPI", await remote.request("add", {
|
|
67561
|
+
kind: "openapi",
|
|
67562
|
+
id,
|
|
67563
|
+
options: remoteAddOptions(options)
|
|
67564
|
+
}));
|
|
67565
|
+
return;
|
|
67566
|
+
}
|
|
65816
67567
|
writeAddResult(writeOut, "OpenAPI", addOpenApiCaplet(id, {
|
|
65817
67568
|
...options,
|
|
65818
|
-
destinationRoot: addDestinationRoot(options)
|
|
67569
|
+
destinationRoot: addDestinationRoot(options, currentConfigPath())
|
|
65819
67570
|
}));
|
|
65820
67571
|
});
|
|
65821
|
-
add.command("graphql").description("Add a GraphQL backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--endpoint-url <url>", "GraphQL endpoint URL").option("--schema <path-or-url>", "GraphQL schema path or URL").option("--introspection", "load schema through endpoint introspection").option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action((id, options) => {
|
|
67572
|
+
add.command("graphql").description("Add a GraphQL backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--endpoint-url <url>", "GraphQL endpoint URL").option("--schema <path-or-url>", "GraphQL schema path or URL").option("--introspection", "load schema through endpoint introspection").option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action(async (id, options) => {
|
|
67573
|
+
const remote = remoteClientForCli(io);
|
|
67574
|
+
if (remote) {
|
|
67575
|
+
writeAddResult(writeOut, "GraphQL", await remote.request("add", {
|
|
67576
|
+
kind: "graphql",
|
|
67577
|
+
id,
|
|
67578
|
+
options: remoteAddOptions(options)
|
|
67579
|
+
}));
|
|
67580
|
+
return;
|
|
67581
|
+
}
|
|
65822
67582
|
writeAddResult(writeOut, "GraphQL", addGraphqlCaplet(id, {
|
|
65823
67583
|
...options,
|
|
65824
|
-
destinationRoot: addDestinationRoot(options)
|
|
67584
|
+
destinationRoot: addDestinationRoot(options, currentConfigPath())
|
|
65825
67585
|
}));
|
|
65826
67586
|
});
|
|
65827
|
-
add.command("http").description("Add an HTTP actions backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--base-url <url>", "HTTP API base URL").option("--action <name:METHOD:/path>", "HTTP action", collect, []).option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action((id, options) => {
|
|
67587
|
+
add.command("http").description("Add an HTTP actions backend Caplet.").argument("<id>", "Caplet ID/display seed").option("--base-url <url>", "HTTP API base URL").option("--action <name:METHOD:/path>", "HTTP action", collect, []).option("--token-env <ENV>", "bearer token environment variable reference").option("-g, --global", "write to the user Caplets root").option("--print", "print generated Caplet text without writing a file").option("--output <path>", "output path").option("--force", "overwrite an existing destination file").action(async (id, options) => {
|
|
67588
|
+
const remote = remoteClientForCli(io);
|
|
67589
|
+
if (remote) {
|
|
67590
|
+
writeAddResult(writeOut, "HTTP", await remote.request("add", {
|
|
67591
|
+
kind: "http",
|
|
67592
|
+
id,
|
|
67593
|
+
options: remoteAddOptions(options)
|
|
67594
|
+
}));
|
|
67595
|
+
return;
|
|
67596
|
+
}
|
|
65828
67597
|
writeAddResult(writeOut, "HTTP", addHttpCaplet(id, {
|
|
65829
67598
|
...options,
|
|
65830
|
-
destinationRoot: addDestinationRoot(options)
|
|
67599
|
+
destinationRoot: addDestinationRoot(options, currentConfigPath())
|
|
65831
67600
|
}));
|
|
65832
67601
|
});
|
|
65833
|
-
program.command(
|
|
67602
|
+
program.command(cliCommands.getCaplet).description("Print a configured Caplet card.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
|
|
65834
67603
|
await executeOperation(caplet, { operation: "get_caplet" }, {
|
|
65835
67604
|
writeOut,
|
|
65836
67605
|
writeErr,
|
|
65837
67606
|
setExitCode,
|
|
65838
67607
|
authDir: io.authDir,
|
|
67608
|
+
env,
|
|
67609
|
+
remote: remoteClientForCli(io),
|
|
65839
67610
|
format: options.format
|
|
65840
67611
|
});
|
|
65841
67612
|
});
|
|
65842
|
-
program.command(
|
|
67613
|
+
program.command(cliCommands.checkBackend).description("Check backend availability for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
|
|
65843
67614
|
await executeOperation(caplet, { operation: "check_backend" }, {
|
|
65844
67615
|
writeOut,
|
|
65845
67616
|
writeErr,
|
|
65846
67617
|
setExitCode,
|
|
65847
67618
|
authDir: io.authDir,
|
|
67619
|
+
env,
|
|
67620
|
+
remote: remoteClientForCli(io),
|
|
65848
67621
|
format: options.format
|
|
65849
67622
|
});
|
|
65850
67623
|
});
|
|
65851
|
-
program.command(
|
|
67624
|
+
program.command(cliCommands.listTools).description("List downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => {
|
|
65852
67625
|
await executeOperation(caplet, { operation: "list_tools" }, {
|
|
65853
67626
|
writeOut,
|
|
65854
67627
|
writeErr,
|
|
65855
67628
|
setExitCode,
|
|
65856
67629
|
authDir: io.authDir,
|
|
67630
|
+
env,
|
|
67631
|
+
remote: remoteClientForCli(io),
|
|
65857
67632
|
format: options.format
|
|
65858
67633
|
});
|
|
65859
67634
|
});
|
|
65860
|
-
program.command(
|
|
67635
|
+
program.command(cliCommands.searchTools).description("Search downstream tools for a configured Caplet.").argument("<caplet>", "configured Caplet ID").argument("<query>", "search query").option("--limit <n>", "maximum number of tools to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => {
|
|
65861
67636
|
await executeOperation(caplet, options.limit === void 0 ? {
|
|
65862
67637
|
operation: "search_tools",
|
|
65863
67638
|
query
|
|
@@ -65870,10 +67645,12 @@ function createProgram(io = {}) {
|
|
|
65870
67645
|
writeErr,
|
|
65871
67646
|
setExitCode,
|
|
65872
67647
|
authDir: io.authDir,
|
|
67648
|
+
env,
|
|
67649
|
+
remote: remoteClientForCli(io),
|
|
65873
67650
|
format: options.format
|
|
65874
67651
|
});
|
|
65875
67652
|
});
|
|
65876
|
-
program.command(
|
|
67653
|
+
program.command(cliCommands.getTool).description("Print one downstream tool schema.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
|
|
65877
67654
|
const { caplet, tool } = parseQualifiedTarget(target);
|
|
65878
67655
|
await executeOperation(caplet, {
|
|
65879
67656
|
operation: "get_tool",
|
|
@@ -65883,10 +67660,12 @@ function createProgram(io = {}) {
|
|
|
65883
67660
|
writeErr,
|
|
65884
67661
|
setExitCode,
|
|
65885
67662
|
authDir: io.authDir,
|
|
67663
|
+
env,
|
|
67664
|
+
remote: remoteClientForCli(io),
|
|
65886
67665
|
format: options.format
|
|
65887
67666
|
});
|
|
65888
67667
|
});
|
|
65889
|
-
program.command(
|
|
67668
|
+
program.command(cliCommands.callTool).description("Call one downstream tool.").argument("<caplet.tool>", "qualified target, split on the first dot").option("--args <json-object>", "JSON object of downstream tool arguments").option("--field <path>", "project a field from structured output", collect, []).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
|
|
65890
67669
|
const { caplet, tool } = parseQualifiedTarget(target);
|
|
65891
67670
|
await executeOperation(caplet, {
|
|
65892
67671
|
operation: "call_tool",
|
|
@@ -65898,24 +67677,150 @@ function createProgram(io = {}) {
|
|
|
65898
67677
|
writeErr,
|
|
65899
67678
|
setExitCode,
|
|
65900
67679
|
authDir: io.authDir,
|
|
67680
|
+
env,
|
|
67681
|
+
remote: remoteClientForCli(io),
|
|
65901
67682
|
format: options.format
|
|
65902
67683
|
});
|
|
65903
67684
|
});
|
|
65904
|
-
|
|
67685
|
+
program.command(cliCommands.listResources).description("List MCP resources for a configured MCP Caplet.").argument("<caplet>").option("--limit <n>", "maximum number of resources to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, options.limit === void 0 ? { operation: "list_resources" } : {
|
|
67686
|
+
operation: "list_resources",
|
|
67687
|
+
limit: options.limit
|
|
67688
|
+
}, {
|
|
67689
|
+
writeOut,
|
|
67690
|
+
writeErr,
|
|
67691
|
+
setExitCode,
|
|
67692
|
+
authDir: io.authDir,
|
|
67693
|
+
env,
|
|
67694
|
+
remote: remoteClientForCli(io),
|
|
67695
|
+
format: options.format
|
|
67696
|
+
}));
|
|
67697
|
+
program.command(cliCommands.searchResources).description("Search MCP resources and resource templates for a configured MCP Caplet.").argument("<caplet>").argument("<query>").option("--limit <n>", "maximum number of matches to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => executeOperation(caplet, options.limit === void 0 ? {
|
|
67698
|
+
operation: "search_resources",
|
|
67699
|
+
query
|
|
67700
|
+
} : {
|
|
67701
|
+
operation: "search_resources",
|
|
67702
|
+
query,
|
|
67703
|
+
limit: options.limit
|
|
67704
|
+
}, {
|
|
67705
|
+
writeOut,
|
|
67706
|
+
writeErr,
|
|
67707
|
+
setExitCode,
|
|
67708
|
+
authDir: io.authDir,
|
|
67709
|
+
env,
|
|
67710
|
+
remote: remoteClientForCli(io),
|
|
67711
|
+
format: options.format
|
|
67712
|
+
}));
|
|
67713
|
+
program.command(cliCommands.listResourceTemplates).description("List MCP resource templates for a configured MCP Caplet.").argument("<caplet>").option("--limit <n>", "maximum number of templates to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, options.limit === void 0 ? { operation: "list_resource_templates" } : {
|
|
67714
|
+
operation: "list_resource_templates",
|
|
67715
|
+
limit: options.limit
|
|
67716
|
+
}, {
|
|
67717
|
+
writeOut,
|
|
67718
|
+
writeErr,
|
|
67719
|
+
setExitCode,
|
|
67720
|
+
authDir: io.authDir,
|
|
67721
|
+
env,
|
|
67722
|
+
remote: remoteClientForCli(io),
|
|
67723
|
+
format: options.format
|
|
67724
|
+
}));
|
|
67725
|
+
program.command(cliCommands.readResource).description("Read one MCP resource by URI.").argument("<caplet>").argument("<uri>").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, uri, options) => executeOperation(caplet, {
|
|
67726
|
+
operation: "read_resource",
|
|
67727
|
+
uri
|
|
67728
|
+
}, {
|
|
67729
|
+
writeOut,
|
|
67730
|
+
writeErr,
|
|
67731
|
+
setExitCode,
|
|
67732
|
+
authDir: io.authDir,
|
|
67733
|
+
env,
|
|
67734
|
+
remote: remoteClientForCli(io),
|
|
67735
|
+
format: options.format
|
|
67736
|
+
}));
|
|
67737
|
+
program.command(cliCommands.listPrompts).description("List MCP prompts for a configured MCP Caplet.").argument("<caplet>").option("--limit <n>", "maximum number of prompts to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, options.limit === void 0 ? { operation: "list_prompts" } : {
|
|
67738
|
+
operation: "list_prompts",
|
|
67739
|
+
limit: options.limit
|
|
67740
|
+
}, {
|
|
67741
|
+
writeOut,
|
|
67742
|
+
writeErr,
|
|
67743
|
+
setExitCode,
|
|
67744
|
+
authDir: io.authDir,
|
|
67745
|
+
env,
|
|
67746
|
+
remote: remoteClientForCli(io),
|
|
67747
|
+
format: options.format
|
|
67748
|
+
}));
|
|
67749
|
+
program.command(cliCommands.searchPrompts).description("Search MCP prompts for a configured MCP Caplet.").argument("<caplet>").argument("<query>").option("--limit <n>", "maximum number of prompts to return", parsePositiveInteger).option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, query, options) => executeOperation(caplet, options.limit === void 0 ? {
|
|
67750
|
+
operation: "search_prompts",
|
|
67751
|
+
query
|
|
67752
|
+
} : {
|
|
67753
|
+
operation: "search_prompts",
|
|
67754
|
+
query,
|
|
67755
|
+
limit: options.limit
|
|
67756
|
+
}, {
|
|
67757
|
+
writeOut,
|
|
67758
|
+
writeErr,
|
|
67759
|
+
setExitCode,
|
|
67760
|
+
authDir: io.authDir,
|
|
67761
|
+
env,
|
|
67762
|
+
remote: remoteClientForCli(io),
|
|
67763
|
+
format: options.format
|
|
67764
|
+
}));
|
|
67765
|
+
program.command(cliCommands.getPrompt).description("Get one MCP prompt by name.").argument("<caplet.prompt>", "qualified target, split on the first dot").option("--args <json-object>", "JSON object of prompt arguments").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (target, options) => {
|
|
67766
|
+
const { caplet, tool: prompt } = parseQualifiedTarget(target);
|
|
67767
|
+
await executeOperation(caplet, {
|
|
67768
|
+
operation: "get_prompt",
|
|
67769
|
+
prompt,
|
|
67770
|
+
arguments: parseJsonObjectOption(options.args, "get-prompt --args")
|
|
67771
|
+
}, {
|
|
67772
|
+
writeOut,
|
|
67773
|
+
writeErr,
|
|
67774
|
+
setExitCode,
|
|
67775
|
+
authDir: io.authDir,
|
|
67776
|
+
env,
|
|
67777
|
+
remote: remoteClientForCli(io),
|
|
67778
|
+
format: options.format
|
|
67779
|
+
});
|
|
67780
|
+
});
|
|
67781
|
+
program.command(cliCommands.complete).description("Complete an MCP prompt or resource-template argument.").argument("<caplet>").requiredOption("--argument <name>", "argument name").option("--value <value>", "argument prefix", "").option("--prompt <name>", "prompt name to complete").option("--resource-template <uri-template>", "resource template URI to complete").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (caplet, options) => executeOperation(caplet, {
|
|
67782
|
+
operation: "complete",
|
|
67783
|
+
ref: completionRefFromOptions(options),
|
|
67784
|
+
argument: {
|
|
67785
|
+
name: options.argument,
|
|
67786
|
+
value: options.value
|
|
67787
|
+
}
|
|
67788
|
+
}, {
|
|
67789
|
+
writeOut,
|
|
67790
|
+
writeErr,
|
|
67791
|
+
setExitCode,
|
|
67792
|
+
authDir: io.authDir,
|
|
67793
|
+
env,
|
|
67794
|
+
remote: remoteClientForCli(io),
|
|
67795
|
+
format: options.format
|
|
67796
|
+
}));
|
|
67797
|
+
const config = program.command(cliCommands.config).description("Inspect Caplets config locations.");
|
|
65905
67798
|
config.command("path").description("Print the effective user config path.").action(() => {
|
|
65906
|
-
writeOut(`${resolveConfigPath(
|
|
67799
|
+
writeOut(`${resolveConfigPath(currentConfigPath())}\n`);
|
|
65907
67800
|
});
|
|
65908
67801
|
config.command("paths").description("Print resolved Caplets config, root, and auth paths.").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
|
|
65909
|
-
const paths = resolveCliConfigPaths(
|
|
67802
|
+
const paths = resolveCliConfigPaths(currentConfigPath(), io.authDir);
|
|
65910
67803
|
if (options.json || options.format === "json") {
|
|
65911
67804
|
writeOut(`${JSON.stringify(paths, null, 2)}\n`);
|
|
65912
67805
|
return;
|
|
65913
67806
|
}
|
|
65914
67807
|
writeOut(formatConfigPaths(paths, options.format ?? "plain"));
|
|
65915
67808
|
});
|
|
65916
|
-
const auth = program.command(
|
|
67809
|
+
const auth = program.command(cliCommands.auth).description("Manage OAuth credentials for remote servers.");
|
|
65917
67810
|
auth.command("login").description("Authenticate a configured remote OAuth server.").argument("<server>", "configured server ID").option("--no-open", "print the authorization URL without opening a browser").action(async (serverId, options) => {
|
|
65918
|
-
const
|
|
67811
|
+
const remote = remoteClientForCli(io);
|
|
67812
|
+
if (remote) {
|
|
67813
|
+
const started = await remote.request("auth_login_start", { server: serverId });
|
|
67814
|
+
if (started.authorizationUrl) {
|
|
67815
|
+
writeOut(`Open this URL to authorize ${serverId}:\n${started.authorizationUrl}\n`);
|
|
67816
|
+
if (options.open !== false) await openBrowser(started.authorizationUrl);
|
|
67817
|
+
writeOut("Complete authentication in your browser. The server callback will store credentials.\n");
|
|
67818
|
+
return;
|
|
67819
|
+
}
|
|
67820
|
+
if (started.authenticated) writeOut(`Authenticated \`${serverId}\`.\n`);
|
|
67821
|
+
return;
|
|
67822
|
+
}
|
|
67823
|
+
const configPath = currentConfigPath();
|
|
65919
67824
|
await loginAuth(serverId, {
|
|
65920
67825
|
noOpen: options.open === false,
|
|
65921
67826
|
writeOut,
|
|
@@ -65924,27 +67829,86 @@ function createProgram(io = {}) {
|
|
|
65924
67829
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
65925
67830
|
});
|
|
65926
67831
|
});
|
|
65927
|
-
auth.command("logout").description("Delete stored OAuth credentials for a server.").argument("<server>", "configured server ID").action((serverId) => {
|
|
65928
|
-
const
|
|
67832
|
+
auth.command("logout").description("Delete stored OAuth credentials for a server.").argument("<server>", "configured server ID").action(async (serverId) => {
|
|
67833
|
+
const remote = remoteClientForCli(io);
|
|
67834
|
+
if (remote) {
|
|
67835
|
+
writeOut((await remote.request("auth_logout", { server: serverId })).deleted ? `Deleted remote OAuth credentials for \`${serverId}\`.\n` : `No remote OAuth credentials found for \`${serverId}\`.\n`);
|
|
67836
|
+
return;
|
|
67837
|
+
}
|
|
67838
|
+
const configPath = currentConfigPath();
|
|
65929
67839
|
logoutAuth(serverId, {
|
|
65930
67840
|
writeOut,
|
|
65931
67841
|
...configPath ? { configPath } : {},
|
|
65932
67842
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
65933
67843
|
});
|
|
65934
67844
|
});
|
|
65935
|
-
auth.command("list").description("List servers with stored OAuth credentials.").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action((options) => {
|
|
65936
|
-
const configPath =
|
|
67845
|
+
auth.command("list").description("List servers with stored OAuth credentials.").option("--json", "print JSON output").option("--format <format>", "output format: plain, markdown, md, or json", parseOutputFormat).action(async (options) => {
|
|
67846
|
+
const configPath = currentConfigPath();
|
|
67847
|
+
const format = options.json || options.format === "json" ? "json" : options.format ?? "plain";
|
|
67848
|
+
const remote = remoteClientForCli(io);
|
|
67849
|
+
if (remote) {
|
|
67850
|
+
const rows = await remote.request("auth_list", {});
|
|
67851
|
+
if (format === "json") {
|
|
67852
|
+
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
67853
|
+
return;
|
|
67854
|
+
}
|
|
67855
|
+
writeOut(formatAuthRows(rows, format));
|
|
67856
|
+
return;
|
|
67857
|
+
}
|
|
65937
67858
|
listAuth({
|
|
65938
67859
|
writeOut,
|
|
65939
|
-
format
|
|
67860
|
+
format,
|
|
65940
67861
|
...configPath ? { configPath } : {},
|
|
65941
67862
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
65942
67863
|
});
|
|
65943
67864
|
});
|
|
65944
67865
|
return program;
|
|
65945
67866
|
}
|
|
65946
|
-
function envConfigPath() {
|
|
65947
|
-
return
|
|
67867
|
+
function envConfigPath(env) {
|
|
67868
|
+
return env.CAPLETS_CONFIG?.trim() || void 0;
|
|
67869
|
+
}
|
|
67870
|
+
function remoteClientForCli(io) {
|
|
67871
|
+
const env = io.env ?? process.env;
|
|
67872
|
+
if (resolveCapletsMode({}, env).mode !== "remote") return;
|
|
67873
|
+
return new RemoteControlClient(resolveCapletsServer(io.fetch ? { fetch: io.fetch } : {}, env));
|
|
67874
|
+
}
|
|
67875
|
+
async function openBrowser(url) {
|
|
67876
|
+
const { spawn } = await import("node:child_process");
|
|
67877
|
+
spawn(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open", process.platform === "win32" ? [
|
|
67878
|
+
"/c",
|
|
67879
|
+
"start",
|
|
67880
|
+
"",
|
|
67881
|
+
url
|
|
67882
|
+
] : [url], {
|
|
67883
|
+
stdio: "ignore",
|
|
67884
|
+
detached: true
|
|
67885
|
+
}).unref();
|
|
67886
|
+
}
|
|
67887
|
+
function remoteCommandForOperation(operation) {
|
|
67888
|
+
switch (operation) {
|
|
67889
|
+
case "get_caplet":
|
|
67890
|
+
case "check_backend":
|
|
67891
|
+
case "list_tools":
|
|
67892
|
+
case "search_tools":
|
|
67893
|
+
case "get_tool":
|
|
67894
|
+
case "call_tool":
|
|
67895
|
+
case "list_resources":
|
|
67896
|
+
case "search_resources":
|
|
67897
|
+
case "list_resource_templates":
|
|
67898
|
+
case "read_resource":
|
|
67899
|
+
case "list_prompts":
|
|
67900
|
+
case "search_prompts":
|
|
67901
|
+
case "get_prompt":
|
|
67902
|
+
case "complete": return operation;
|
|
67903
|
+
default: return;
|
|
67904
|
+
}
|
|
67905
|
+
}
|
|
67906
|
+
function remoteAddOptions(options) {
|
|
67907
|
+
const { output, print, global, destinationRoot, ...remoteOptions } = options;
|
|
67908
|
+
if (global) throw new CapletsError("REQUEST_INVALID", "--global is not supported in remote mode; the server controls the add destination.");
|
|
67909
|
+
if (print) throw new CapletsError("REQUEST_INVALID", "--print is not supported in remote mode; the server controls add output.");
|
|
67910
|
+
if (output !== void 0) throw new CapletsError("REQUEST_INVALID", "--output is not supported in remote mode; the server controls the add destination.");
|
|
67911
|
+
return remoteOptions;
|
|
65948
67912
|
}
|
|
65949
67913
|
function collect(value, previous) {
|
|
65950
67914
|
previous.push(value);
|
|
@@ -65983,11 +67947,48 @@ function parseCallToolArgs(value) {
|
|
|
65983
67947
|
if (!isPlainObject(parsed)) throw new CapletsError("REQUEST_INVALID", "call-tool --args must be a JSON object");
|
|
65984
67948
|
return parsed;
|
|
65985
67949
|
}
|
|
67950
|
+
function parseJsonObjectOption(value, label) {
|
|
67951
|
+
if (value === void 0) return {};
|
|
67952
|
+
let parsed;
|
|
67953
|
+
try {
|
|
67954
|
+
parsed = JSON.parse(value);
|
|
67955
|
+
} catch (error) {
|
|
67956
|
+
throw new CapletsError("REQUEST_INVALID", `${label} must be valid JSON`, error);
|
|
67957
|
+
}
|
|
67958
|
+
if (!isPlainObject(parsed)) throw new CapletsError("REQUEST_INVALID", `${label} must be a JSON object`);
|
|
67959
|
+
return parsed;
|
|
67960
|
+
}
|
|
67961
|
+
function completionRefFromOptions(options) {
|
|
67962
|
+
if (options.prompt && options.resourceTemplate) throw new CapletsError("REQUEST_INVALID", "complete accepts either --prompt or --resource-template, not both");
|
|
67963
|
+
if (options.prompt) return {
|
|
67964
|
+
type: "prompt",
|
|
67965
|
+
name: options.prompt
|
|
67966
|
+
};
|
|
67967
|
+
if (options.resourceTemplate) return {
|
|
67968
|
+
type: "resourceTemplate",
|
|
67969
|
+
uri: options.resourceTemplate
|
|
67970
|
+
};
|
|
67971
|
+
throw new CapletsError("REQUEST_INVALID", "complete requires --prompt or --resource-template");
|
|
67972
|
+
}
|
|
65986
67973
|
function isPlainObject(value) {
|
|
65987
67974
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
65988
67975
|
}
|
|
65989
67976
|
async function executeOperation(caplet, request, io) {
|
|
65990
|
-
const
|
|
67977
|
+
const command = remoteCommandForOperation(request.operation);
|
|
67978
|
+
if (io.remote && command) {
|
|
67979
|
+
const result = await io.remote.request(command, {
|
|
67980
|
+
caplet,
|
|
67981
|
+
request
|
|
67982
|
+
});
|
|
67983
|
+
const output = cliOutputForOperation(result, {
|
|
67984
|
+
...request,
|
|
67985
|
+
caplet
|
|
67986
|
+
}, io.format ?? "markdown");
|
|
67987
|
+
io.writeOut(typeof output === "string" ? `${output}\n` : `${JSON.stringify(output, null, 2)}\n`);
|
|
67988
|
+
if (isPlainObject(result) && result.isError === true) io.setExitCode(1);
|
|
67989
|
+
return;
|
|
67990
|
+
}
|
|
67991
|
+
const configPath = envConfigPath(io.env ?? process.env);
|
|
65991
67992
|
const engine = new CapletsEngine({
|
|
65992
67993
|
...configPath ? { configPath } : {},
|
|
65993
67994
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
@@ -66099,6 +68100,55 @@ function markdownSummaryForOperation(result, request) {
|
|
|
66099
68100
|
"",
|
|
66100
68101
|
"Use `--format json` to inspect the full structured result."
|
|
66101
68102
|
].filter((line) => line !== void 0).join("\n");
|
|
68103
|
+
case "list_resources":
|
|
68104
|
+
case "search_resources": {
|
|
68105
|
+
const resources = Array.isArray(payload.resources) ? payload.resources : [];
|
|
68106
|
+
const templates = Array.isArray(payload.resourceTemplates) ? payload.resourceTemplates : [];
|
|
68107
|
+
const matches = Array.isArray(payload.matches) ? payload.matches : [...resources, ...templates];
|
|
68108
|
+
return [
|
|
68109
|
+
`## MCP resources for \`${id}\``,
|
|
68110
|
+
"",
|
|
68111
|
+
`${matches.length} item${matches.length === 1 ? "" : "s"} found.`,
|
|
68112
|
+
"",
|
|
68113
|
+
...formatResourceLines(matches, "markdown")
|
|
68114
|
+
].join("\n");
|
|
68115
|
+
}
|
|
68116
|
+
case "list_resource_templates": {
|
|
68117
|
+
const templates = Array.isArray(payload.resourceTemplates) ? payload.resourceTemplates : [];
|
|
68118
|
+
return [
|
|
68119
|
+
`## MCP resource templates for \`${id}\``,
|
|
68120
|
+
"",
|
|
68121
|
+
...formatResourceLines(templates, "markdown")
|
|
68122
|
+
].join("\n");
|
|
68123
|
+
}
|
|
68124
|
+
case "read_resource": return [
|
|
68125
|
+
`## Resource \`${String(request.uri ?? "")}\``,
|
|
68126
|
+
"",
|
|
68127
|
+
summarizeResourceRead(payload),
|
|
68128
|
+
"",
|
|
68129
|
+
"Use `--format json` to inspect all contents."
|
|
68130
|
+
].join("\n");
|
|
68131
|
+
case "list_prompts":
|
|
68132
|
+
case "search_prompts": {
|
|
68133
|
+
const prompts = Array.isArray(payload.prompts) ? payload.prompts : [];
|
|
68134
|
+
return [
|
|
68135
|
+
`## MCP prompts for \`${id}\``,
|
|
68136
|
+
"",
|
|
68137
|
+
...formatPromptLines(prompts, "markdown")
|
|
68138
|
+
].join("\n");
|
|
68139
|
+
}
|
|
68140
|
+
case "get_prompt": return [
|
|
68141
|
+
`## Prompt \`${String(request.caplet)}.${String(request.prompt)}\``,
|
|
68142
|
+
"",
|
|
68143
|
+
summarizePromptResult(payload),
|
|
68144
|
+
"",
|
|
68145
|
+
"Use `--format json` to inspect all messages."
|
|
68146
|
+
].join("\n");
|
|
68147
|
+
case "complete": return [
|
|
68148
|
+
`## Completion for \`${id}\``,
|
|
68149
|
+
"",
|
|
68150
|
+
summarizeCompletionResult(payload)
|
|
68151
|
+
].join("\n");
|
|
66102
68152
|
default: return JSON.stringify(payload, null, 2);
|
|
66103
68153
|
}
|
|
66104
68154
|
}
|
|
@@ -66155,6 +68205,33 @@ function plainSummaryForOperation(result, request) {
|
|
|
66155
68205
|
`Result: ${summarizeCallResult(payload)}`,
|
|
66156
68206
|
"Use --format json to inspect the full structured result."
|
|
66157
68207
|
].filter((line) => Boolean(line)).join("\n");
|
|
68208
|
+
case "list_resources":
|
|
68209
|
+
case "search_resources": {
|
|
68210
|
+
const resources = Array.isArray(payload.resources) ? payload.resources : [];
|
|
68211
|
+
const templates = Array.isArray(payload.resourceTemplates) ? payload.resourceTemplates : [];
|
|
68212
|
+
const matches = Array.isArray(payload.matches) ? payload.matches : [...resources, ...templates];
|
|
68213
|
+
return [`MCP resources for ${id} (${matches.length}):`, ...formatResourceLines(matches, "plain")].join("\n");
|
|
68214
|
+
}
|
|
68215
|
+
case "list_resource_templates": {
|
|
68216
|
+
const templates = Array.isArray(payload.resourceTemplates) ? payload.resourceTemplates : [];
|
|
68217
|
+
return [`MCP resource templates for ${id}:`, ...formatResourceLines(templates, "plain")].join("\n");
|
|
68218
|
+
}
|
|
68219
|
+
case "read_resource": return [
|
|
68220
|
+
`Resource ${String(request.uri ?? "")}`,
|
|
68221
|
+
summarizeResourceRead(payload),
|
|
68222
|
+
"Use --format json to inspect all contents."
|
|
68223
|
+
].join("\n");
|
|
68224
|
+
case "list_prompts":
|
|
68225
|
+
case "search_prompts": {
|
|
68226
|
+
const prompts = Array.isArray(payload.prompts) ? payload.prompts : [];
|
|
68227
|
+
return [`MCP prompts for ${id}:`, ...formatPromptLines(prompts, "plain")].join("\n");
|
|
68228
|
+
}
|
|
68229
|
+
case "get_prompt": return [
|
|
68230
|
+
`Prompt ${String(request.caplet)}.${String(request.prompt)}`,
|
|
68231
|
+
summarizePromptResult(payload),
|
|
68232
|
+
"Use --format json to inspect all messages."
|
|
68233
|
+
].join("\n");
|
|
68234
|
+
case "complete": return [`Completion for ${id}`, summarizeCompletionResult(payload)].join("\n");
|
|
66158
68235
|
default: return JSON.stringify(payload, null, 2);
|
|
66159
68236
|
}
|
|
66160
68237
|
}
|
|
@@ -66171,6 +68248,44 @@ function formatToolLines(tools, format) {
|
|
|
66171
68248
|
return `- ${displayName}${flags ? ` (${flags})` : ""}${tool.description ? ` — ${compactDescription(String(tool.description))}` : ""}`;
|
|
66172
68249
|
});
|
|
66173
68250
|
}
|
|
68251
|
+
function formatResourceLines(resources, format) {
|
|
68252
|
+
if (resources.length === 0) return ["- none"];
|
|
68253
|
+
return resources.map((resource) => {
|
|
68254
|
+
if (!isPlainObject(resource)) return `- ${String(resource)}`;
|
|
68255
|
+
const name = String(resource.uri ?? resource.uriTemplate ?? "unknown");
|
|
68256
|
+
const displayName = format === "markdown" ? `\`${name}\`` : name;
|
|
68257
|
+
const label = typeof resource.name === "string" ? ` (${resource.name})` : "";
|
|
68258
|
+
return `- ${typeof resource.kind === "string" ? `${resource.kind}: ` : ""}${displayName}${label}${resource.description ? ` — ${compactDescription(String(resource.description))}` : ""}`;
|
|
68259
|
+
});
|
|
68260
|
+
}
|
|
68261
|
+
function formatPromptLines(prompts, format) {
|
|
68262
|
+
if (prompts.length === 0) return ["- none"];
|
|
68263
|
+
return prompts.map((prompt) => {
|
|
68264
|
+
if (!isPlainObject(prompt)) return `- ${String(prompt)}`;
|
|
68265
|
+
const name = String(prompt.prompt ?? prompt.name ?? "unknown");
|
|
68266
|
+
return `- ${format === "markdown" ? `\`${name}\`` : name}${Array.isArray(prompt.arguments) ? ` (${prompt.arguments.length} args)` : ""}${prompt.description ? ` — ${compactDescription(String(prompt.description))}` : ""}`;
|
|
68267
|
+
});
|
|
68268
|
+
}
|
|
68269
|
+
function summarizeResourceRead(payload) {
|
|
68270
|
+
const contents = Array.isArray(payload.contents) ? payload.contents : [];
|
|
68271
|
+
if (contents.length === 0) return "No contents returned.";
|
|
68272
|
+
const first = contents.find(isPlainObject);
|
|
68273
|
+
if (!first) return `${contents.length} content item${contents.length === 1 ? "" : "s"} returned.`;
|
|
68274
|
+
return previewValue(typeof first.text === "string" ? first.text : first.blob) ?? `${contents.length} content item${contents.length === 1 ? "" : "s"} returned.`;
|
|
68275
|
+
}
|
|
68276
|
+
function summarizePromptResult(payload) {
|
|
68277
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
68278
|
+
if (messages.length === 0) return "No messages returned.";
|
|
68279
|
+
const first = messages.find(isPlainObject);
|
|
68280
|
+
if (!first) return `${messages.length} message${messages.length === 1 ? "" : "s"} returned.`;
|
|
68281
|
+
return previewValue((isPlainObject(first.content) ? first.content : void 0)?.text ?? first.content) ?? `${messages.length} message${messages.length === 1 ? "" : "s"} returned.`;
|
|
68282
|
+
}
|
|
68283
|
+
function summarizeCompletionResult(payload) {
|
|
68284
|
+
const completion = isPlainObject(payload.completion) ? payload.completion : void 0;
|
|
68285
|
+
const values = Array.isArray(completion?.values) ? completion.values : [];
|
|
68286
|
+
if (values.length > 0) return values.map((value) => `- ${String(value)}`).join("\n");
|
|
68287
|
+
return previewValue(payload) ?? "No completions returned.";
|
|
68288
|
+
}
|
|
66174
68289
|
function compactDescription(value) {
|
|
66175
68290
|
const firstParagraph = value.trim().split(/\n\s*\n/u)[0] ?? "";
|
|
66176
68291
|
const collapsed = (firstParagraph.match(/^.*?(?:[.!?](?=\s|$)|$)/u)?.[0] ?? firstParagraph).replace(/\s+/gu, " ").trim();
|
|
@@ -66233,19 +68348,19 @@ function schemaSummary(schema) {
|
|
|
66233
68348
|
required.length > 0 ? `required ${required.join(", ")}` : "no required fields"
|
|
66234
68349
|
].filter((part) => Boolean(part)).join("; ");
|
|
66235
68350
|
}
|
|
66236
|
-
function addDestinationRoot(options) {
|
|
66237
|
-
return options.global ? resolveCapletsRoot(resolveConfigPath(
|
|
68351
|
+
function addDestinationRoot(options, configPath) {
|
|
68352
|
+
return options.global ? resolveCapletsRoot(resolveConfigPath(configPath)) : resolveProjectCapletsRoot();
|
|
66238
68353
|
}
|
|
66239
68354
|
function writeAddResult(writeOut, label, result) {
|
|
66240
68355
|
if (result.path) {
|
|
66241
|
-
writeOut(`Wrote ${label} Caplet to ${result.path}\n`);
|
|
68356
|
+
writeOut(`Wrote ${result.remote ? "remote " : ""}${label} Caplet to ${result.path}\n`);
|
|
66242
68357
|
return;
|
|
66243
68358
|
}
|
|
66244
68359
|
writeOut(result.text);
|
|
66245
68360
|
}
|
|
66246
68361
|
//#endregion
|
|
66247
68362
|
//#region package.json
|
|
66248
|
-
var version = "0.
|
|
68363
|
+
var version = "0.17.0";
|
|
66249
68364
|
//#endregion
|
|
66250
68365
|
//#region src/index.ts
|
|
66251
68366
|
async function main() {
|