@caplets/core 0.13.1 → 0.14.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/dist/index.js +504 -104
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -3720,7 +3720,7 @@ const allowsEval = /* @__PURE__ */ cached(() => {
|
|
|
3720
3720
|
return false;
|
|
3721
3721
|
}
|
|
3722
3722
|
});
|
|
3723
|
-
function isPlainObject$
|
|
3723
|
+
function isPlainObject$8(o) {
|
|
3724
3724
|
if (isObject(o) === false) return false;
|
|
3725
3725
|
const ctor = o.constructor;
|
|
3726
3726
|
if (ctor === void 0) return true;
|
|
@@ -3731,7 +3731,7 @@ function isPlainObject$7(o) {
|
|
|
3731
3731
|
return true;
|
|
3732
3732
|
}
|
|
3733
3733
|
function shallowClone(o) {
|
|
3734
|
-
if (isPlainObject$
|
|
3734
|
+
if (isPlainObject$8(o)) return { ...o };
|
|
3735
3735
|
if (Array.isArray(o)) return [...o];
|
|
3736
3736
|
if (o instanceof Map) return new Map(o);
|
|
3737
3737
|
if (o instanceof Set) return new Set(o);
|
|
@@ -3814,7 +3814,7 @@ function omit(schema, mask) {
|
|
|
3814
3814
|
}));
|
|
3815
3815
|
}
|
|
3816
3816
|
function extend(schema, shape) {
|
|
3817
|
-
if (!isPlainObject$
|
|
3817
|
+
if (!isPlainObject$8(shape)) throw new Error("Invalid input to extend: expected a plain object");
|
|
3818
3818
|
const checks = schema._zod.def.checks;
|
|
3819
3819
|
if (checks && checks.length > 0) {
|
|
3820
3820
|
const existingShape = schema._zod.def.shape;
|
|
@@ -3830,7 +3830,7 @@ function extend(schema, shape) {
|
|
|
3830
3830
|
} }));
|
|
3831
3831
|
}
|
|
3832
3832
|
function safeExtend(schema, shape) {
|
|
3833
|
-
if (!isPlainObject$
|
|
3833
|
+
if (!isPlainObject$8(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
3834
3834
|
return clone(schema, mergeDefs(schema._zod.def, { get shape() {
|
|
3835
3835
|
const _shape = {
|
|
3836
3836
|
...schema._zod.def.shape,
|
|
@@ -5444,7 +5444,7 @@ function mergeValues(a, b) {
|
|
|
5444
5444
|
valid: true,
|
|
5445
5445
|
data: a
|
|
5446
5446
|
};
|
|
5447
|
-
if (isPlainObject$
|
|
5447
|
+
if (isPlainObject$8(a) && isPlainObject$8(b)) {
|
|
5448
5448
|
const bKeys = Object.keys(b);
|
|
5449
5449
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
5450
5450
|
const newObj = {
|
|
@@ -5520,7 +5520,7 @@ const $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
5520
5520
|
$ZodType.init(inst, def);
|
|
5521
5521
|
inst._zod.parse = (payload, ctx) => {
|
|
5522
5522
|
const input = payload.value;
|
|
5523
|
-
if (!isPlainObject$
|
|
5523
|
+
if (!isPlainObject$8(input)) {
|
|
5524
5524
|
payload.issues.push({
|
|
5525
5525
|
expected: "record",
|
|
5526
5526
|
code: "invalid_type",
|
|
@@ -12186,7 +12186,7 @@ var Protocol = class {
|
|
|
12186
12186
|
};
|
|
12187
12187
|
}
|
|
12188
12188
|
};
|
|
12189
|
-
function isPlainObject$
|
|
12189
|
+
function isPlainObject$7(value) {
|
|
12190
12190
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12191
12191
|
}
|
|
12192
12192
|
function mergeCapabilities(base, additional) {
|
|
@@ -12196,7 +12196,7 @@ function mergeCapabilities(base, additional) {
|
|
|
12196
12196
|
const addValue = additional[k];
|
|
12197
12197
|
if (addValue === void 0) continue;
|
|
12198
12198
|
const baseValue = result[k];
|
|
12199
|
-
if (isPlainObject$
|
|
12199
|
+
if (isPlainObject$7(baseValue) && isPlainObject$7(addValue)) result[k] = {
|
|
12200
12200
|
...baseValue,
|
|
12201
12201
|
...addValue
|
|
12202
12202
|
};
|
|
@@ -19848,7 +19848,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
|
|
|
19848
19848
|
} };
|
|
19849
19849
|
//#endregion
|
|
19850
19850
|
//#region package.json
|
|
19851
|
-
var version = "0.
|
|
19851
|
+
var version = "0.14.0";
|
|
19852
19852
|
//#endregion
|
|
19853
19853
|
//#region ../../node_modules/.pnpm/unist-util-stringify-position@4.0.0/node_modules/unist-util-stringify-position/lib/index.js
|
|
19854
19854
|
/**
|
|
@@ -27795,19 +27795,19 @@ function loadCapletFilesWithPaths(root) {
|
|
|
27795
27795
|
if (servers[candidate.id] || openapiEndpoints[candidate.id] || graphqlEndpoints[candidate.id] || httpApis[candidate.id] || cliTools[candidate.id] || capletSets[candidate.id]) throw new CapletsError("CONFIG_INVALID", `Duplicate Caplet ID ${candidate.id} under ${root}`);
|
|
27796
27796
|
paths[candidate.id] = candidate.path;
|
|
27797
27797
|
const config = readCapletFile(candidate.path);
|
|
27798
|
-
if (isPlainObject$
|
|
27798
|
+
if (isPlainObject$6(config) && config.backend === "openapi") {
|
|
27799
27799
|
const { backend: _backend, ...endpoint } = config;
|
|
27800
27800
|
openapiEndpoints[candidate.id] = endpoint;
|
|
27801
|
-
} else if (isPlainObject$
|
|
27801
|
+
} else if (isPlainObject$6(config) && config.backend === "graphql") {
|
|
27802
27802
|
const { backend: _backend, ...endpoint } = config;
|
|
27803
27803
|
graphqlEndpoints[candidate.id] = endpoint;
|
|
27804
|
-
} else if (isPlainObject$
|
|
27804
|
+
} else if (isPlainObject$6(config) && config.backend === "http") {
|
|
27805
27805
|
const { backend: _backend, ...endpoint } = config;
|
|
27806
27806
|
httpApis[candidate.id] = endpoint;
|
|
27807
|
-
} else if (isPlainObject$
|
|
27807
|
+
} else if (isPlainObject$6(config) && config.backend === "cli") {
|
|
27808
27808
|
const { backend: _backend, ...endpoint } = config;
|
|
27809
27809
|
cliTools[candidate.id] = endpoint;
|
|
27810
|
-
} else if (isPlainObject$
|
|
27810
|
+
} else if (isPlainObject$6(config) && config.backend === "caplets") {
|
|
27811
27811
|
const { backend: _backend, ...endpoint } = config;
|
|
27812
27812
|
capletSets[candidate.id] = endpoint;
|
|
27813
27813
|
} else servers[candidate.id] = config;
|
|
@@ -27961,7 +27961,7 @@ function parseFrontmatter(text, path) {
|
|
|
27961
27961
|
value: text
|
|
27962
27962
|
});
|
|
27963
27963
|
matter(file, { strip: true });
|
|
27964
|
-
if (!isPlainObject$
|
|
27964
|
+
if (!isPlainObject$6(file.data.matter) || Object.keys(file.data.matter).length === 0) throw new Error("empty frontmatter");
|
|
27965
27965
|
return {
|
|
27966
27966
|
frontmatter: file.data.matter,
|
|
27967
27967
|
body: String(file)
|
|
@@ -27970,7 +27970,7 @@ function parseFrontmatter(text, path) {
|
|
|
27970
27970
|
throw new CapletsError("CONFIG_INVALID", `Caplet file at ${path} has invalid YAML frontmatter`, redactSecrets(error));
|
|
27971
27971
|
}
|
|
27972
27972
|
}
|
|
27973
|
-
function isPlainObject$
|
|
27973
|
+
function isPlainObject$6(value) {
|
|
27974
27974
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
27975
27975
|
}
|
|
27976
27976
|
function validateCapletId(id, path) {
|
|
@@ -28591,7 +28591,7 @@ function normalizeLocalPaths(input, baseDir) {
|
|
|
28591
28591
|
}
|
|
28592
28592
|
function normalizeEndpointPaths(endpoints, baseDir, normalize) {
|
|
28593
28593
|
if (!endpoints) return;
|
|
28594
|
-
return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$
|
|
28594
|
+
return Object.fromEntries(Object.entries(endpoints).map(([id, endpoint]) => [id, isPlainObject$5(endpoint) ? normalize(endpoint, baseDir) : endpoint]));
|
|
28595
28595
|
}
|
|
28596
28596
|
function normalizeOpenApiPath(endpoint, baseDir) {
|
|
28597
28597
|
return {
|
|
@@ -28600,7 +28600,7 @@ function normalizeOpenApiPath(endpoint, baseDir) {
|
|
|
28600
28600
|
};
|
|
28601
28601
|
}
|
|
28602
28602
|
function normalizeGraphQlPath(endpoint, baseDir) {
|
|
28603
|
-
const operations = isPlainObject$
|
|
28603
|
+
const operations = isPlainObject$5(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$5(operation) ? {
|
|
28604
28604
|
...operation,
|
|
28605
28605
|
documentPath: normalizeLocalPath(operation.documentPath, baseDir)
|
|
28606
28606
|
} : operation])) : endpoint.operations;
|
|
@@ -28611,7 +28611,7 @@ function normalizeGraphQlPath(endpoint, baseDir) {
|
|
|
28611
28611
|
};
|
|
28612
28612
|
}
|
|
28613
28613
|
function normalizeCliToolsPaths(endpoint, baseDir) {
|
|
28614
|
-
const actions = isPlainObject$
|
|
28614
|
+
const actions = isPlainObject$5(endpoint.actions) ? Object.fromEntries(Object.entries(endpoint.actions).map(([name, action]) => [name, isPlainObject$5(action) ? {
|
|
28615
28615
|
...action,
|
|
28616
28616
|
cwd: normalizeLocalPath(action.cwd, baseDir)
|
|
28617
28617
|
} : action])) : endpoint.actions;
|
|
@@ -28814,7 +28814,7 @@ function isPublicMetadataPath(path) {
|
|
|
28814
28814
|
if (path.length < 3 || path[0] !== "mcpServers" && path[0] !== "openapiEndpoints" && path[0] !== "graphqlEndpoints" && path[0] !== "httpApis" && path[0] !== "cliTools" && path[0] !== "capletSets") return false;
|
|
28815
28815
|
return NON_INTERPOLATED_SERVER_FIELDS.has(path[2] ?? "");
|
|
28816
28816
|
}
|
|
28817
|
-
function isPlainObject$
|
|
28817
|
+
function isPlainObject$5(value) {
|
|
28818
28818
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
28819
28819
|
}
|
|
28820
28820
|
function hasEnvReference(value) {
|
|
@@ -28984,9 +28984,9 @@ function validateInput(action, input) {
|
|
|
28984
28984
|
if (!schema) return;
|
|
28985
28985
|
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
28986
28986
|
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}`);
|
|
28987
|
-
const properties = isPlainObject$
|
|
28987
|
+
const properties = isPlainObject$4(schema.properties) ? schema.properties : {};
|
|
28988
28988
|
for (const [key, property] of Object.entries(properties)) {
|
|
28989
|
-
if (input[key] === void 0 || !isPlainObject$
|
|
28989
|
+
if (input[key] === void 0 || !isPlainObject$4(property) || typeof property.type !== "string") continue;
|
|
28990
28990
|
if (!matchesJsonType(input[key], property.type)) throw new CapletsError("REQUEST_INVALID", `CLI tool ${action.name} input ${key} must be ${property.type}`);
|
|
28991
28991
|
}
|
|
28992
28992
|
}
|
|
@@ -28996,7 +28996,7 @@ function matchesJsonType(value, type) {
|
|
|
28996
28996
|
case "number":
|
|
28997
28997
|
case "integer": return typeof value === "number" && (type === "number" || Number.isInteger(value));
|
|
28998
28998
|
case "boolean": return typeof value === "boolean";
|
|
28999
|
-
case "object": return isPlainObject$
|
|
28999
|
+
case "object": return isPlainObject$4(value);
|
|
29000
29000
|
case "array": return Array.isArray(value);
|
|
29001
29001
|
case "null": return value === null;
|
|
29002
29002
|
default: return true;
|
|
@@ -29087,7 +29087,7 @@ function isExecutable(path) {
|
|
|
29087
29087
|
function isAbortError$1(error) {
|
|
29088
29088
|
return error instanceof Error && error.name === "AbortError";
|
|
29089
29089
|
}
|
|
29090
|
-
function isPlainObject$
|
|
29090
|
+
function isPlainObject$4(value) {
|
|
29091
29091
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
29092
29092
|
}
|
|
29093
29093
|
//#endregion
|
|
@@ -48370,7 +48370,7 @@ function resolveMapping(mapping, input) {
|
|
|
48370
48370
|
function resolveMappingToRecord(mapping, input, name) {
|
|
48371
48371
|
if (mapping === void 0) return {};
|
|
48372
48372
|
const resolved = resolveMapping(mapping, input);
|
|
48373
|
-
if (!isPlainObject$
|
|
48373
|
+
if (!isPlainObject$3(resolved)) throw new CapletsError("REQUEST_INVALID", `HTTP action ${name} mapping must resolve to an object`);
|
|
48374
48374
|
return resolved;
|
|
48375
48375
|
}
|
|
48376
48376
|
function valueAtPath(input, path) {
|
|
@@ -48465,9 +48465,9 @@ function buildActionUrl(base, actionPath, options = {}) {
|
|
|
48465
48465
|
return baseUrl;
|
|
48466
48466
|
}
|
|
48467
48467
|
function asRecord$1(value) {
|
|
48468
|
-
return isPlainObject$
|
|
48468
|
+
return isPlainObject$3(value) ? value : {};
|
|
48469
48469
|
}
|
|
48470
|
-
function isPlainObject$
|
|
48470
|
+
function isPlainObject$3(value) {
|
|
48471
48471
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
48472
48472
|
}
|
|
48473
48473
|
//#endregion
|
|
@@ -58400,14 +58400,12 @@ function openApiCacheKey(endpoint) {
|
|
|
58400
58400
|
//#endregion
|
|
58401
58401
|
//#region src/capability-description.ts
|
|
58402
58402
|
function capabilityDescription(server) {
|
|
58403
|
-
const backendName = server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : server.backend === "http" ? "HTTP API" : server.backend === "cli" ? "CLI tools" : server.backend === "caplets" ? "nested Caplets" : "backend";
|
|
58404
|
-
const checkOperation = server.backend === "mcp" ? "check_mcp_server" : "check_backend";
|
|
58405
58403
|
const hint = [
|
|
58406
|
-
`Use this Caplet to inspect and call tools from its ${
|
|
58404
|
+
`Use this Caplet to inspect and call tools from its ${server.backend === "mcp" ? "MCP server" : server.backend === "openapi" ? "OpenAPI endpoint" : server.backend === "graphql" ? "GraphQL endpoint" : server.backend === "http" ? "HTTP API" : server.backend === "cli" ? "CLI tools" : server.backend === "caplets" ? "nested Caplets" : "backend"} backend.`,
|
|
58407
58405
|
"",
|
|
58408
58406
|
"Recommended flow:",
|
|
58409
58407
|
"- Read the full Caplet card: {\"operation\":\"get_caplet\"}",
|
|
58410
|
-
|
|
58408
|
+
"- Check the backend: {\"operation\":\"check_backend\"}",
|
|
58411
58409
|
"- Discover tools: {\"operation\":\"list_tools\"} or {\"operation\":\"search_tools\",\"query\":\"<what you need>\"}",
|
|
58412
58410
|
"- Read one tool schema: {\"operation\":\"get_tool\",\"tool\":\"<tool name>\"}",
|
|
58413
58411
|
"- Invoke one downstream tool: {\"operation\":\"call_tool\",\"tool\":\"<tool name>\",\"arguments\":{...}}",
|
|
@@ -58544,7 +58542,6 @@ function graphQlSource(server) {
|
|
|
58544
58542
|
const operations = [
|
|
58545
58543
|
"get_caplet",
|
|
58546
58544
|
"check_backend",
|
|
58547
|
-
"check_mcp_server",
|
|
58548
58545
|
"list_tools",
|
|
58549
58546
|
"search_tools",
|
|
58550
58547
|
"get_tool",
|
|
@@ -58553,7 +58550,7 @@ const operations = [
|
|
|
58553
58550
|
const generatedToolInputDescriptions = {
|
|
58554
58551
|
operation: [
|
|
58555
58552
|
"Caplets wrapper operation to perform for this configured Caplet backend.",
|
|
58556
|
-
"Use get_caplet to read the full Caplet card, check_backend to check
|
|
58553
|
+
"Use get_caplet to read the full Caplet card, check_backend to check backend availability, list_tools or search_tools to discover downstream tools, get_tool to read a downstream input schema, and call_tool to run one downstream tool, operation, action, CLI command, or child Caplet.",
|
|
58557
58554
|
"For call_tool, pass downstream inputs only inside the top-level \"arguments\" object."
|
|
58558
58555
|
].join(" "),
|
|
58559
58556
|
query: "Required only for search_tools. Example: {\"operation\":\"search_tools\",\"query\":\"web search\",\"limit\":5}. Do not use query for call_tool; put downstream query values under arguments.query.",
|
|
@@ -58606,7 +58603,7 @@ function generatedToolInputJsonSchema() {
|
|
|
58606
58603
|
//#region src/field-selection.ts
|
|
58607
58604
|
function projectStructuredContent(value, outputSchema, fields) {
|
|
58608
58605
|
validateFieldSelection(outputSchema, fields);
|
|
58609
|
-
if (!isPlainObject$
|
|
58606
|
+
if (!isPlainObject$2(value)) throwInvalid("Field selection requires object structured content");
|
|
58610
58607
|
const result = createJsonObject();
|
|
58611
58608
|
for (const field of fields) {
|
|
58612
58609
|
const projected = projectPath(value, outputSchema, field.split("."));
|
|
@@ -58615,7 +58612,7 @@ function projectStructuredContent(value, outputSchema, fields) {
|
|
|
58615
58612
|
return result;
|
|
58616
58613
|
}
|
|
58617
58614
|
function validateFieldSelection(outputSchema, fields) {
|
|
58618
|
-
if (!isPlainObject$
|
|
58615
|
+
if (!isPlainObject$2(outputSchema)) throwInvalid("Field selection requires an output schema");
|
|
58619
58616
|
if (!Array.isArray(fields) || fields.some((field) => typeof field !== "string")) throwInvalid("Field selection requires an array of field paths");
|
|
58620
58617
|
for (const field of fields) validateSchemaPath(outputSchema, field.split("."), field);
|
|
58621
58618
|
}
|
|
@@ -58639,7 +58636,7 @@ function projectPath(value, schema, path) {
|
|
|
58639
58636
|
return value.map((item) => projectPath(item, itemSchema, path) ?? {});
|
|
58640
58637
|
}
|
|
58641
58638
|
const segment = path[0];
|
|
58642
|
-
if (!isPlainObject$
|
|
58639
|
+
if (!isPlainObject$2(value) || !Object.prototype.hasOwnProperty.call(value, segment)) return;
|
|
58643
58640
|
const rest = path.slice(1);
|
|
58644
58641
|
const propertySchema = getSchemaProperty(schema, segment);
|
|
58645
58642
|
const projected = projectPath(value[segment], propertySchema, rest);
|
|
@@ -58651,24 +58648,24 @@ function pruneToSchema(value, schema) {
|
|
|
58651
58648
|
const itemSchema = arrayItemSchema(schema);
|
|
58652
58649
|
return value.map((item) => pruneToSchema(item, itemSchema));
|
|
58653
58650
|
}
|
|
58654
|
-
if (!isPlainObject$
|
|
58655
|
-
const properties = isPlainObject$
|
|
58656
|
-
if (!isPlainObject$
|
|
58651
|
+
if (!isPlainObject$2(value)) return cloneJsonValue(value);
|
|
58652
|
+
const properties = isPlainObject$2(schema) ? schema.properties : void 0;
|
|
58653
|
+
if (!isPlainObject$2(properties)) return cloneJsonValue(value);
|
|
58657
58654
|
const result = createJsonObject();
|
|
58658
58655
|
for (const [key, nestedSchema] of Object.entries(properties)) if (isSupportedSegment(key) && Object.prototype.hasOwnProperty.call(value, key)) result[key] = pruneToSchema(value[key], nestedSchema);
|
|
58659
58656
|
return result;
|
|
58660
58657
|
}
|
|
58661
58658
|
function getSchemaProperty(schema, segment) {
|
|
58662
|
-
const properties = isPlainObject$
|
|
58659
|
+
const properties = isPlainObject$2(schema) ? schema.properties : void 0;
|
|
58663
58660
|
if (!properties || !Object.prototype.hasOwnProperty.call(properties, segment)) return;
|
|
58664
58661
|
return properties[segment];
|
|
58665
58662
|
}
|
|
58666
58663
|
function arrayItemSchema(schema) {
|
|
58667
|
-
if (!isPlainObject$
|
|
58664
|
+
if (!isPlainObject$2(schema) || Array.isArray(schema.items)) return;
|
|
58668
58665
|
return schema.items;
|
|
58669
58666
|
}
|
|
58670
58667
|
function mergeValue(target, value) {
|
|
58671
|
-
if (!isPlainObject$
|
|
58668
|
+
if (!isPlainObject$2(value)) return;
|
|
58672
58669
|
for (const [key, nested] of Object.entries(value)) {
|
|
58673
58670
|
if (!isSupportedSegment(key)) continue;
|
|
58674
58671
|
target[key] = mergeNested(target[key], nested);
|
|
@@ -58677,7 +58674,7 @@ function mergeValue(target, value) {
|
|
|
58677
58674
|
function mergeNested(existing, next) {
|
|
58678
58675
|
if (next === void 0) return existing;
|
|
58679
58676
|
if (Array.isArray(existing) && Array.isArray(next)) return Array.from({ length: Math.max(existing.length, next.length) }, (_, index) => mergeNested(existing[index], next[index]));
|
|
58680
|
-
if (isPlainObject$
|
|
58677
|
+
if (isPlainObject$2(existing) && isPlainObject$2(next)) {
|
|
58681
58678
|
const merged = Object.assign(createJsonObject(), existing);
|
|
58682
58679
|
mergeValue(merged, next);
|
|
58683
58680
|
return merged;
|
|
@@ -58687,7 +58684,7 @@ function mergeNested(existing, next) {
|
|
|
58687
58684
|
function isSupportedSegment(segment) {
|
|
58688
58685
|
return segment !== "" && segment !== "*" && segment !== "__proto__" && segment !== "prototype" && segment !== "constructor" && !/^\d+$/.test(segment);
|
|
58689
58686
|
}
|
|
58690
|
-
function isPlainObject$
|
|
58687
|
+
function isPlainObject$2(value) {
|
|
58691
58688
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
58692
58689
|
}
|
|
58693
58690
|
function createJsonObject() {
|
|
@@ -58695,7 +58692,7 @@ function createJsonObject() {
|
|
|
58695
58692
|
}
|
|
58696
58693
|
function cloneJsonValue(value) {
|
|
58697
58694
|
if (Array.isArray(value)) return value.map(cloneJsonValue);
|
|
58698
|
-
if (isPlainObject$
|
|
58695
|
+
if (isPlainObject$2(value)) {
|
|
58699
58696
|
const result = createJsonObject();
|
|
58700
58697
|
for (const [key, nested] of Object.entries(value)) if (isSupportedSegment(key)) result[key] = cloneJsonValue(nested);
|
|
58701
58698
|
return result;
|
|
@@ -58718,9 +58715,6 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
58718
58715
|
switch (parsed.operation) {
|
|
58719
58716
|
case "get_caplet": return jsonResult(registry.detail(server));
|
|
58720
58717
|
case "check_backend": return jsonResult(await backendFor(server, downstream, openapi, graphql, http, cli, caplets).check(server));
|
|
58721
|
-
case "check_mcp_server":
|
|
58722
|
-
if (server.backend !== "mcp") throw new CapletsError("REQUEST_INVALID", "check_mcp_server is only valid for MCP-backed Caplets; use check_backend");
|
|
58723
|
-
return jsonResult(await downstream.checkServer(server));
|
|
58724
58718
|
case "list_tools": {
|
|
58725
58719
|
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
|
|
58726
58720
|
const tools = await backend.listTools(server);
|
|
@@ -58771,7 +58765,6 @@ function validateOperationRequest(request, maxSearchLimit) {
|
|
|
58771
58765
|
switch (value.operation) {
|
|
58772
58766
|
case "get_caplet":
|
|
58773
58767
|
case "check_backend":
|
|
58774
|
-
case "check_mcp_server":
|
|
58775
58768
|
case "list_tools":
|
|
58776
58769
|
allowed([]);
|
|
58777
58770
|
return { operation: value.operation };
|
|
@@ -58801,7 +58794,7 @@ function validateOperationRequest(request, maxSearchLimit) {
|
|
|
58801
58794
|
"fields"
|
|
58802
58795
|
]);
|
|
58803
58796
|
if (!value.tool) throw new CapletsError("REQUEST_INVALID", "call_tool requires tool");
|
|
58804
|
-
if (!isPlainObject(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
|
|
58797
|
+
if (!isPlainObject$1(value.arguments)) throw new CapletsError("REQUEST_INVALID", "call_tool.arguments must be a JSON object");
|
|
58805
58798
|
return value.fields === void 0 ? {
|
|
58806
58799
|
operation: "call_tool",
|
|
58807
58800
|
tool: value.tool,
|
|
@@ -58822,7 +58815,7 @@ function jsonResult(value) {
|
|
|
58822
58815
|
return {
|
|
58823
58816
|
content: [{
|
|
58824
58817
|
type: "text",
|
|
58825
|
-
text:
|
|
58818
|
+
text: "Result available in structuredContent.result."
|
|
58826
58819
|
}],
|
|
58827
58820
|
structuredContent: { result: value }
|
|
58828
58821
|
};
|
|
@@ -58830,7 +58823,7 @@ function jsonResult(value) {
|
|
|
58830
58823
|
function projectCallToolResult(result, outputSchema, fields) {
|
|
58831
58824
|
if (result.isError === true) return result;
|
|
58832
58825
|
const structuredContent = result.structuredContent;
|
|
58833
|
-
if (!isPlainObject(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
|
|
58826
|
+
if (!isPlainObject$1(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
|
|
58834
58827
|
const projected = projectStructuredContent(structuredContent, outputSchema, fields);
|
|
58835
58828
|
return {
|
|
58836
58829
|
...result,
|
|
@@ -58841,7 +58834,7 @@ function projectCallToolResult(result, outputSchema, fields) {
|
|
|
58841
58834
|
structuredContent: projected
|
|
58842
58835
|
};
|
|
58843
58836
|
}
|
|
58844
|
-
function isPlainObject(value) {
|
|
58837
|
+
function isPlainObject$1(value) {
|
|
58845
58838
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
58846
58839
|
}
|
|
58847
58840
|
function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
|
|
@@ -62937,7 +62930,7 @@ async function loginAuth(serverId, options) {
|
|
|
62937
62930
|
};
|
|
62938
62931
|
if (server.backend === "mcp") await runOAuthFlow(server, flowOptions);
|
|
62939
62932
|
else await runGenericOAuthFlow(server, flowOptions);
|
|
62940
|
-
options.writeOut(`Authenticated
|
|
62933
|
+
options.writeOut(`Authenticated \`${serverId}\`.\n`);
|
|
62941
62934
|
} catch (error) {
|
|
62942
62935
|
options.writeErr(`${JSON.stringify(toSafeError(error, "AUTH_FAILED"), null, 2)}\n`);
|
|
62943
62936
|
process.exitCode = 1;
|
|
@@ -62945,25 +62938,47 @@ async function loginAuth(serverId, options) {
|
|
|
62945
62938
|
}
|
|
62946
62939
|
function logoutAuth(serverId, options) {
|
|
62947
62940
|
assertLoginTarget(findAuthTarget(serverId, loadConfig(options.configPath)), serverId);
|
|
62948
|
-
if (deleteTokenBundle(serverId, options.authDir)) options.writeOut(`Deleted OAuth credentials for
|
|
62949
|
-
else options.writeOut(`No OAuth credentials found for
|
|
62941
|
+
if (deleteTokenBundle(serverId, options.authDir)) options.writeOut(`Deleted OAuth credentials for \`${serverId}\`.\n`);
|
|
62942
|
+
else options.writeOut(`No OAuth credentials found for \`${serverId}\`.\n`);
|
|
62950
62943
|
}
|
|
62951
62944
|
function listAuth(options) {
|
|
62952
62945
|
const servers = authTargets(loadConfig(options.configPath)).sort((left, right) => left.server.localeCompare(right.server));
|
|
62946
|
+
const format = options.format ?? "plain";
|
|
62947
|
+
if (format === "json") {
|
|
62948
|
+
const rows = servers.map((server) => {
|
|
62949
|
+
const bundle = readTokenBundle(server.server, options.authDir);
|
|
62950
|
+
const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
|
|
62951
|
+
return {
|
|
62952
|
+
server: server.server,
|
|
62953
|
+
status,
|
|
62954
|
+
...bundle?.expiresAt ? { expiresAt: bundle.expiresAt } : {},
|
|
62955
|
+
...bundle?.scope ? { scope: bundle.scope } : {}
|
|
62956
|
+
};
|
|
62957
|
+
});
|
|
62958
|
+
options.writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
62959
|
+
return;
|
|
62960
|
+
}
|
|
62953
62961
|
if (servers.length === 0) {
|
|
62954
|
-
options.writeOut("No configured remote OAuth servers found.\n");
|
|
62962
|
+
options.writeOut(format === "markdown" ? "## OAuth credentials\n\nNo configured remote OAuth servers found.\n" : "No configured remote OAuth servers found.\n");
|
|
62955
62963
|
return;
|
|
62956
62964
|
}
|
|
62965
|
+
if (format === "markdown") options.writeOut("## OAuth credentials\n\n");
|
|
62966
|
+
else options.writeOut("OAuth credentials\n\n");
|
|
62957
62967
|
for (const server of servers) {
|
|
62958
62968
|
const bundle = readTokenBundle(server.server, options.authDir);
|
|
62959
62969
|
const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
|
|
62970
|
+
const details = [bundle?.expiresAt ? `expires ${bundle.expiresAt}` : void 0, bundle?.scope ? `scope ${bundle.scope}` : void 0].filter(Boolean).join("; ");
|
|
62971
|
+
if (format === "markdown") {
|
|
62972
|
+
options.writeOut(`- \`${server.server}\` — ${status}${details ? ` (${details})` : ""}\n`);
|
|
62973
|
+
continue;
|
|
62974
|
+
}
|
|
62960
62975
|
options.writeOut([
|
|
62961
62976
|
server.server,
|
|
62962
|
-
status
|
|
62963
|
-
bundle?.expiresAt ? `
|
|
62964
|
-
bundle?.scope ? `
|
|
62965
|
-
].
|
|
62966
|
-
options.writeOut("\n");
|
|
62977
|
+
` Status: ${status}`,
|
|
62978
|
+
...bundle?.expiresAt ? [` Expires: ${bundle.expiresAt}`] : [],
|
|
62979
|
+
...bundle?.scope ? [` Scope: ${bundle.scope}`] : []
|
|
62980
|
+
].join("\n"));
|
|
62981
|
+
options.writeOut("\n\n");
|
|
62967
62982
|
}
|
|
62968
62983
|
}
|
|
62969
62984
|
function findAuthTarget(serverId, config = loadConfig()) {
|
|
@@ -63063,24 +63078,49 @@ function allCaplets(config) {
|
|
|
63063
63078
|
...Object.values(config.cliTools)
|
|
63064
63079
|
];
|
|
63065
63080
|
}
|
|
63066
|
-
function formatCapletList(rows) {
|
|
63081
|
+
function formatCapletList(rows, format = "plain") {
|
|
63082
|
+
return format === "markdown" ? formatCapletListMarkdown(rows) : formatCapletListPlain(rows);
|
|
63083
|
+
}
|
|
63084
|
+
function formatCapletListMarkdown(rows) {
|
|
63085
|
+
if (rows.length === 0) return "## Configured Caplets\n\nNo configured Caplets found.\n";
|
|
63086
|
+
const heading = [
|
|
63087
|
+
"## Configured Caplets",
|
|
63088
|
+
"",
|
|
63089
|
+
`${rows.length} ${rows.length === 1 ? "Caplet" : "Caplets"} shown.`,
|
|
63090
|
+
""
|
|
63091
|
+
];
|
|
63092
|
+
const entries = rows.flatMap((row) => [
|
|
63093
|
+
`- \`${row.server}\` — ${row.name}`,
|
|
63094
|
+
` - Backend: ${row.backend}`,
|
|
63095
|
+
` - Status: ${row.status}`,
|
|
63096
|
+
` - Source: ${row.source}`,
|
|
63097
|
+
...row.disabled ? [" - Disabled: true"] : [],
|
|
63098
|
+
...row.path ? [` - Path: ${row.path}`] : []
|
|
63099
|
+
]);
|
|
63100
|
+
const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
|
|
63101
|
+
if (warnings.length === 0) return `${[...heading, ...entries].join("\n")}\n`;
|
|
63102
|
+
return `${[
|
|
63103
|
+
...heading,
|
|
63104
|
+
...entries,
|
|
63105
|
+
"",
|
|
63106
|
+
"Warnings:",
|
|
63107
|
+
...warnings.map((warning) => `- ${warning}`)
|
|
63108
|
+
].join("\n")}\n`;
|
|
63109
|
+
}
|
|
63110
|
+
function formatCapletListPlain(rows) {
|
|
63067
63111
|
if (rows.length === 0) return "No configured Caplets found.\n";
|
|
63068
|
-
const
|
|
63069
|
-
"server",
|
|
63070
|
-
"backend",
|
|
63071
|
-
"status",
|
|
63072
|
-
"source",
|
|
63073
|
-
"name"
|
|
63074
|
-
], ...rows.map((row) => [
|
|
63112
|
+
const entries = rows.map((row) => [
|
|
63075
63113
|
row.server,
|
|
63076
|
-
row.
|
|
63077
|
-
row.
|
|
63078
|
-
row.
|
|
63079
|
-
row.
|
|
63080
|
-
|
|
63114
|
+
` Name: ${row.name}`,
|
|
63115
|
+
` Backend: ${row.backend}`,
|
|
63116
|
+
` Status: ${row.status}`,
|
|
63117
|
+
` Source: ${row.source}`,
|
|
63118
|
+
...row.disabled ? [" Disabled: true"] : [],
|
|
63119
|
+
...row.path ? [` Path: ${row.path}`] : []
|
|
63120
|
+
].join("\n")).join("\n\n");
|
|
63081
63121
|
const warnings = rows.flatMap((row) => row.shadows.map((shadow) => `Warning: ${formatSourceKind(row.source)} Caplet ${row.server} shadows ${formatSourceKind(shadow.kind)} Caplet at ${shadow.path}`));
|
|
63082
|
-
if (warnings.length === 0) return
|
|
63083
|
-
return
|
|
63122
|
+
if (warnings.length === 0) return `Configured Caplets (${rows.length})\n\n${entries}\n`;
|
|
63123
|
+
return `Configured Caplets (${rows.length})\n\n${entries}\n\n${warnings.join("\n")}\n`;
|
|
63084
63124
|
}
|
|
63085
63125
|
function formatSourceKind(kind) {
|
|
63086
63126
|
if (kind.startsWith("project")) return "project";
|
|
@@ -63100,28 +63140,35 @@ function resolveCliConfigPaths(envConfigPath, authDir) {
|
|
|
63100
63140
|
envConfig: envConfigPath ?? null
|
|
63101
63141
|
};
|
|
63102
63142
|
}
|
|
63103
|
-
function formatConfigPaths(paths) {
|
|
63143
|
+
function formatConfigPaths(paths, format = "plain") {
|
|
63144
|
+
if (format === "markdown") return formatConfigPathsMarkdown(paths);
|
|
63145
|
+
return formatConfigPathsPlain(paths);
|
|
63146
|
+
}
|
|
63147
|
+
function formatConfigPathsMarkdown(paths) {
|
|
63104
63148
|
return [
|
|
63105
|
-
|
|
63106
|
-
|
|
63107
|
-
|
|
63108
|
-
|
|
63109
|
-
|
|
63110
|
-
|
|
63111
|
-
|
|
63149
|
+
"## Caplets paths",
|
|
63150
|
+
"",
|
|
63151
|
+
`- User config: ${paths.userConfig}`,
|
|
63152
|
+
`- Project config: ${paths.projectConfig}`,
|
|
63153
|
+
`- User Caplets root: ${paths.userRoot}`,
|
|
63154
|
+
`- State root: ${paths.stateRoot}`,
|
|
63155
|
+
`- Project Caplets root: ${paths.projectRoot}`,
|
|
63156
|
+
`- Auth directory: ${paths.authDir}`,
|
|
63157
|
+
`- CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
|
|
63112
63158
|
].join("\n") + "\n";
|
|
63113
63159
|
}
|
|
63114
|
-
function
|
|
63115
|
-
|
|
63116
|
-
|
|
63117
|
-
|
|
63118
|
-
|
|
63119
|
-
}
|
|
63120
|
-
|
|
63121
|
-
|
|
63122
|
-
|
|
63123
|
-
|
|
63124
|
-
|
|
63160
|
+
function formatConfigPathsPlain(paths) {
|
|
63161
|
+
return [
|
|
63162
|
+
"Caplets paths",
|
|
63163
|
+
"",
|
|
63164
|
+
`User config: ${paths.userConfig}`,
|
|
63165
|
+
`Project config: ${paths.projectConfig}`,
|
|
63166
|
+
`User root: ${paths.userRoot}`,
|
|
63167
|
+
`State root: ${paths.stateRoot}`,
|
|
63168
|
+
`Project root: ${paths.projectRoot}`,
|
|
63169
|
+
`Auth directory: ${paths.authDir}`,
|
|
63170
|
+
`CAPLETS_CONFIG: ${paths.envConfig ?? "unset"}`
|
|
63171
|
+
].join("\n") + "\n";
|
|
63125
63172
|
}
|
|
63126
63173
|
//#endregion
|
|
63127
63174
|
//#region src/cli/install.ts
|
|
@@ -63370,7 +63417,7 @@ async function runCli(args, io = {}) {
|
|
|
63370
63417
|
]);
|
|
63371
63418
|
} catch (error) {
|
|
63372
63419
|
if (error instanceof CommanderError) {
|
|
63373
|
-
if (error.code === "commander.helpDisplayed" || error.code === "commander.version") return;
|
|
63420
|
+
if (error.code === "commander.helpDisplayed" || error.code === "commander.version" || error.message === "(outputHelp)") return;
|
|
63374
63421
|
throw new CapletsError("REQUEST_INVALID", error.message);
|
|
63375
63422
|
}
|
|
63376
63423
|
throw error;
|
|
@@ -63379,6 +63426,9 @@ async function runCli(args, io = {}) {
|
|
|
63379
63426
|
function createProgram(io = {}) {
|
|
63380
63427
|
const writeOut = io.writeOut ?? ((value) => process.stdout.write(value));
|
|
63381
63428
|
const writeErr = io.writeErr ?? ((value) => process.stderr.write(value));
|
|
63429
|
+
const setExitCode = io.setExitCode ?? ((code) => {
|
|
63430
|
+
process.exitCode = code;
|
|
63431
|
+
});
|
|
63382
63432
|
const program = new Command();
|
|
63383
63433
|
program.name("caplets").description("Progressive-disclosure gateway for MCP servers.").version(io.version ?? version).exitOverride().configureOutput({
|
|
63384
63434
|
writeOut,
|
|
@@ -63392,13 +63442,13 @@ function createProgram(io = {}) {
|
|
|
63392
63442
|
force: Boolean(options.force)
|
|
63393
63443
|
})}\n`);
|
|
63394
63444
|
});
|
|
63395
|
-
program.command("list").description("List configured Caplets.").option("--all", "include disabled Caplets").option("--json", "print JSON output").action((options) => {
|
|
63445
|
+
program.command("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((options) => {
|
|
63396
63446
|
const rows = listCaplets(loadConfigWithSources(envConfigPath()), { includeDisabled: Boolean(options.all) });
|
|
63397
|
-
if (options.json) {
|
|
63447
|
+
if (options.json || options.format === "json") {
|
|
63398
63448
|
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
63399
63449
|
return;
|
|
63400
63450
|
}
|
|
63401
|
-
writeOut(formatCapletList(rows));
|
|
63451
|
+
writeOut(formatCapletList(rows, options.format ?? "plain"));
|
|
63402
63452
|
});
|
|
63403
63453
|
program.command("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((repo, capletIds, options) => {
|
|
63404
63454
|
const result = installCaplets(repo, {
|
|
@@ -63444,17 +63494,88 @@ function createProgram(io = {}) {
|
|
|
63444
63494
|
destinationRoot: addDestinationRoot(options)
|
|
63445
63495
|
}));
|
|
63446
63496
|
});
|
|
63497
|
+
program.command("get-caplet").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) => {
|
|
63498
|
+
await executeOperation(caplet, { operation: "get_caplet" }, {
|
|
63499
|
+
writeOut,
|
|
63500
|
+
writeErr,
|
|
63501
|
+
setExitCode,
|
|
63502
|
+
authDir: io.authDir,
|
|
63503
|
+
format: options.format
|
|
63504
|
+
});
|
|
63505
|
+
});
|
|
63506
|
+
program.command("check-backend").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) => {
|
|
63507
|
+
await executeOperation(caplet, { operation: "check_backend" }, {
|
|
63508
|
+
writeOut,
|
|
63509
|
+
writeErr,
|
|
63510
|
+
setExitCode,
|
|
63511
|
+
authDir: io.authDir,
|
|
63512
|
+
format: options.format
|
|
63513
|
+
});
|
|
63514
|
+
});
|
|
63515
|
+
program.command("list-tools").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) => {
|
|
63516
|
+
await executeOperation(caplet, { operation: "list_tools" }, {
|
|
63517
|
+
writeOut,
|
|
63518
|
+
writeErr,
|
|
63519
|
+
setExitCode,
|
|
63520
|
+
authDir: io.authDir,
|
|
63521
|
+
format: options.format
|
|
63522
|
+
});
|
|
63523
|
+
});
|
|
63524
|
+
program.command("search-tools").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) => {
|
|
63525
|
+
await executeOperation(caplet, options.limit === void 0 ? {
|
|
63526
|
+
operation: "search_tools",
|
|
63527
|
+
query
|
|
63528
|
+
} : {
|
|
63529
|
+
operation: "search_tools",
|
|
63530
|
+
query,
|
|
63531
|
+
limit: options.limit
|
|
63532
|
+
}, {
|
|
63533
|
+
writeOut,
|
|
63534
|
+
writeErr,
|
|
63535
|
+
setExitCode,
|
|
63536
|
+
authDir: io.authDir,
|
|
63537
|
+
format: options.format
|
|
63538
|
+
});
|
|
63539
|
+
});
|
|
63540
|
+
program.command("get-tool").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) => {
|
|
63541
|
+
const { caplet, tool } = parseQualifiedTarget(target);
|
|
63542
|
+
await executeOperation(caplet, {
|
|
63543
|
+
operation: "get_tool",
|
|
63544
|
+
tool
|
|
63545
|
+
}, {
|
|
63546
|
+
writeOut,
|
|
63547
|
+
writeErr,
|
|
63548
|
+
setExitCode,
|
|
63549
|
+
authDir: io.authDir,
|
|
63550
|
+
format: options.format
|
|
63551
|
+
});
|
|
63552
|
+
});
|
|
63553
|
+
program.command("call-tool").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) => {
|
|
63554
|
+
const { caplet, tool } = parseQualifiedTarget(target);
|
|
63555
|
+
await executeOperation(caplet, {
|
|
63556
|
+
operation: "call_tool",
|
|
63557
|
+
tool,
|
|
63558
|
+
arguments: parseCallToolArgs(options.args),
|
|
63559
|
+
...options.field && options.field.length > 0 ? { fields: options.field } : {}
|
|
63560
|
+
}, {
|
|
63561
|
+
writeOut,
|
|
63562
|
+
writeErr,
|
|
63563
|
+
setExitCode,
|
|
63564
|
+
authDir: io.authDir,
|
|
63565
|
+
format: options.format
|
|
63566
|
+
});
|
|
63567
|
+
});
|
|
63447
63568
|
const config = program.command("config").description("Inspect Caplets config locations.");
|
|
63448
63569
|
config.command("path").description("Print the effective user config path.").action(() => {
|
|
63449
63570
|
writeOut(`${resolveConfigPath(envConfigPath())}\n`);
|
|
63450
63571
|
});
|
|
63451
|
-
config.command("paths").description("Print resolved Caplets config, root, and auth paths.").option("--json", "print JSON output").action((options) => {
|
|
63572
|
+
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) => {
|
|
63452
63573
|
const paths = resolveCliConfigPaths(envConfigPath(), io.authDir);
|
|
63453
|
-
if (options.json) {
|
|
63574
|
+
if (options.json || options.format === "json") {
|
|
63454
63575
|
writeOut(`${JSON.stringify(paths, null, 2)}\n`);
|
|
63455
63576
|
return;
|
|
63456
63577
|
}
|
|
63457
|
-
writeOut(formatConfigPaths(paths));
|
|
63578
|
+
writeOut(formatConfigPaths(paths, options.format ?? "plain"));
|
|
63458
63579
|
});
|
|
63459
63580
|
const auth = program.command("auth").description("Manage OAuth credentials for remote servers.");
|
|
63460
63581
|
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) => {
|
|
@@ -63475,10 +63596,11 @@ function createProgram(io = {}) {
|
|
|
63475
63596
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
63476
63597
|
});
|
|
63477
63598
|
});
|
|
63478
|
-
auth.command("list").description("List servers with stored OAuth credentials.").action(() => {
|
|
63599
|
+
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) => {
|
|
63479
63600
|
const configPath = envConfigPath();
|
|
63480
63601
|
listAuth({
|
|
63481
63602
|
writeOut,
|
|
63603
|
+
format: options.json || options.format === "json" ? "json" : options.format ?? "plain",
|
|
63482
63604
|
...configPath ? { configPath } : {},
|
|
63483
63605
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
63484
63606
|
});
|
|
@@ -63492,6 +63614,284 @@ function collect(value, previous) {
|
|
|
63492
63614
|
previous.push(value);
|
|
63493
63615
|
return previous;
|
|
63494
63616
|
}
|
|
63617
|
+
function parsePositiveInteger(value) {
|
|
63618
|
+
const parsed = Number(value);
|
|
63619
|
+
if (!Number.isInteger(parsed) || parsed <= 0) throw new CapletsError("REQUEST_INVALID", `Expected a positive integer, got ${value}`);
|
|
63620
|
+
return parsed;
|
|
63621
|
+
}
|
|
63622
|
+
function parseOutputFormat(value) {
|
|
63623
|
+
switch (value.toLocaleLowerCase()) {
|
|
63624
|
+
case "markdown":
|
|
63625
|
+
case "md": return "markdown";
|
|
63626
|
+
case "plain": return "plain";
|
|
63627
|
+
case "json": return "json";
|
|
63628
|
+
default: throw new CapletsError("REQUEST_INVALID", `Expected output format markdown, md, plain, or json; got ${value}`);
|
|
63629
|
+
}
|
|
63630
|
+
}
|
|
63631
|
+
function parseQualifiedTarget(target) {
|
|
63632
|
+
const dot = target.indexOf(".");
|
|
63633
|
+
if (dot <= 0 || dot === target.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected qualified target in the form <caplet>.<tool>");
|
|
63634
|
+
return {
|
|
63635
|
+
caplet: target.slice(0, dot),
|
|
63636
|
+
tool: target.slice(dot + 1)
|
|
63637
|
+
};
|
|
63638
|
+
}
|
|
63639
|
+
function parseCallToolArgs(value) {
|
|
63640
|
+
if (value === void 0) return {};
|
|
63641
|
+
let parsed;
|
|
63642
|
+
try {
|
|
63643
|
+
parsed = JSON.parse(value);
|
|
63644
|
+
} catch (error) {
|
|
63645
|
+
throw new CapletsError("REQUEST_INVALID", "call-tool --args must be valid JSON", error);
|
|
63646
|
+
}
|
|
63647
|
+
if (!isPlainObject(parsed)) throw new CapletsError("REQUEST_INVALID", "call-tool --args must be a JSON object");
|
|
63648
|
+
return parsed;
|
|
63649
|
+
}
|
|
63650
|
+
function isPlainObject(value) {
|
|
63651
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63652
|
+
}
|
|
63653
|
+
async function executeOperation(caplet, request, io) {
|
|
63654
|
+
const configPath = envConfigPath();
|
|
63655
|
+
const engine = new CapletsEngine({
|
|
63656
|
+
...configPath ? { configPath } : {},
|
|
63657
|
+
...io.authDir ? { authDir: io.authDir } : {},
|
|
63658
|
+
watch: false,
|
|
63659
|
+
writeErr: io.writeErr
|
|
63660
|
+
});
|
|
63661
|
+
try {
|
|
63662
|
+
const result = await engine.execute(caplet, request);
|
|
63663
|
+
const output = cliOutputForOperation(result, {
|
|
63664
|
+
...request,
|
|
63665
|
+
caplet
|
|
63666
|
+
}, io.format ?? "markdown");
|
|
63667
|
+
io.writeOut(typeof output === "string" ? `${output}\n` : `${JSON.stringify(output, null, 2)}\n`);
|
|
63668
|
+
if (isPlainObject(result) && result.isError === true) io.setExitCode(1);
|
|
63669
|
+
} finally {
|
|
63670
|
+
await engine.close();
|
|
63671
|
+
}
|
|
63672
|
+
}
|
|
63673
|
+
function cliOutputForOperation(result, request, format) {
|
|
63674
|
+
if (format === "json" || !isPlainObject(result)) return jsonPayloadForOperation(result, request.operation);
|
|
63675
|
+
return format === "markdown" ? markdownSummaryForOperation(result, request) : plainSummaryForOperation(result, request);
|
|
63676
|
+
}
|
|
63677
|
+
function jsonPayloadForOperation(result, operation) {
|
|
63678
|
+
if (operation === "call_tool" || !isPlainObject(result)) return result;
|
|
63679
|
+
const structuredContent = result.structuredContent;
|
|
63680
|
+
if (!isPlainObject(structuredContent) || !("result" in structuredContent)) return result;
|
|
63681
|
+
return structuredContent.result;
|
|
63682
|
+
}
|
|
63683
|
+
function markdownSummaryForOperation(result, request) {
|
|
63684
|
+
const operation = request.operation;
|
|
63685
|
+
const payload = jsonPayloadForOperation(result, operation);
|
|
63686
|
+
if (!isPlainObject(payload)) return String(payload);
|
|
63687
|
+
switch (operation) {
|
|
63688
|
+
case "get_caplet": return [
|
|
63689
|
+
`## Caplet \`${String(payload.caplet ?? "unknown")}\``,
|
|
63690
|
+
"",
|
|
63691
|
+
`**Name:** ${String(payload.name ?? "Unnamed")}`,
|
|
63692
|
+
`**Description:** ${String(payload.description ?? "No description.")}`,
|
|
63693
|
+
payload.backend ? `**Backend:** ${backendType(payload.backend)}` : void 0,
|
|
63694
|
+
"",
|
|
63695
|
+
"Next:",
|
|
63696
|
+
`- List tools: \`caplets list-tools ${String(payload.caplet ?? "<caplet>")}\``,
|
|
63697
|
+
`- Search tools: \`caplets search-tools ${String(payload.caplet ?? "<caplet>")} <query>\``
|
|
63698
|
+
].filter((line) => line !== void 0).join("\n");
|
|
63699
|
+
case "check_backend": return [
|
|
63700
|
+
`## Backend \`${String(payload.server ?? "caplet")}\``,
|
|
63701
|
+
"",
|
|
63702
|
+
`- Status: ${String(payload.status ?? "unknown")}`,
|
|
63703
|
+
typeof payload.toolCount === "number" ? `- Tools: ${payload.toolCount}` : void 0,
|
|
63704
|
+
typeof payload.elapsedMs === "number" ? `- Elapsed: ${payload.elapsedMs}ms` : void 0,
|
|
63705
|
+
"",
|
|
63706
|
+
"Next:",
|
|
63707
|
+
`- List tools: \`caplets list-tools ${String(payload.server ?? "<caplet>")}\``
|
|
63708
|
+
].filter((line) => line !== void 0).join("\n");
|
|
63709
|
+
case "list_tools": {
|
|
63710
|
+
const tools = Array.isArray(payload.tools) ? payload.tools : [];
|
|
63711
|
+
return [
|
|
63712
|
+
`## Tools for \`${String(payload.server ?? "caplet")}\``,
|
|
63713
|
+
"",
|
|
63714
|
+
`${tools.length} ${tools.length === 1 ? "tool" : "tools"} found.`,
|
|
63715
|
+
"",
|
|
63716
|
+
...formatToolLines(tools, "markdown"),
|
|
63717
|
+
"",
|
|
63718
|
+
"Next:",
|
|
63719
|
+
`- Inspect a tool: \`caplets get-tool ${String(payload.server ?? "<caplet>")}.<tool>\``,
|
|
63720
|
+
`- Call a tool: \`caplets call-tool ${String(payload.server ?? "<caplet>")}.<tool> --args '{...}'\``,
|
|
63721
|
+
"- Machine output: add `--format json`"
|
|
63722
|
+
].join("\n");
|
|
63723
|
+
}
|
|
63724
|
+
case "search_tools": {
|
|
63725
|
+
const tools = Array.isArray(payload.tools) ? payload.tools : [];
|
|
63726
|
+
return [
|
|
63727
|
+
`## Matches for ${JSON.stringify(String(payload.query ?? ""))} in \`${String(payload.server ?? "caplet")}\``,
|
|
63728
|
+
"",
|
|
63729
|
+
`${tools.length} ${tools.length === 1 ? "match" : "matches"} found.`,
|
|
63730
|
+
"",
|
|
63731
|
+
...formatToolLines(tools, "markdown"),
|
|
63732
|
+
"",
|
|
63733
|
+
"Next:",
|
|
63734
|
+
tools.length > 0 ? `- Inspect the first match: \`caplets get-tool ${String(payload.server ?? "<caplet>")}.${firstToolName(tools) ?? "<tool>"}\`` : `- Try a broader query or list tools: \`caplets list-tools ${String(payload.server ?? "<caplet>")}\``
|
|
63735
|
+
].join("\n");
|
|
63736
|
+
}
|
|
63737
|
+
case "get_tool": {
|
|
63738
|
+
const tool = isPlainObject(payload.tool) ? payload.tool : {};
|
|
63739
|
+
const target = `${String(payload.server ?? "<caplet>")}.${String(tool.name ?? "<tool>")}`;
|
|
63740
|
+
return [
|
|
63741
|
+
`## Tool \`${target}\``,
|
|
63742
|
+
"",
|
|
63743
|
+
tool.description ? compactDescription(String(tool.description)) : void 0,
|
|
63744
|
+
"",
|
|
63745
|
+
"Input:",
|
|
63746
|
+
`- ${schemaSummary(tool.inputSchema)}`,
|
|
63747
|
+
"",
|
|
63748
|
+
"Output:",
|
|
63749
|
+
`- ${tool.outputSchema ? schemaSummary(tool.outputSchema) : "not declared"}`,
|
|
63750
|
+
"",
|
|
63751
|
+
"Next:",
|
|
63752
|
+
`- Call: \`caplets call-tool ${target} --args '{...}'\``,
|
|
63753
|
+
"- Full schema: add `--format json`"
|
|
63754
|
+
].filter((line) => line !== void 0).join("\n");
|
|
63755
|
+
}
|
|
63756
|
+
case "call_tool": return [
|
|
63757
|
+
`## Call \`${`${String(request.caplet ?? "<caplet>")}.${String(request.tool ?? "unknown")}`}\``,
|
|
63758
|
+
"",
|
|
63759
|
+
`- Status: ${payload.isError === true ? "failed" : "succeeded"}`,
|
|
63760
|
+
callStatusLine(payload) ? `- ${callStatusLine(payload)}` : void 0,
|
|
63761
|
+
`- Result: ${summarizeCallResult(payload)}`,
|
|
63762
|
+
"",
|
|
63763
|
+
"Use `--format json` to inspect the full structured result."
|
|
63764
|
+
].filter((line) => line !== void 0).join("\n");
|
|
63765
|
+
default: return JSON.stringify(payload, null, 2);
|
|
63766
|
+
}
|
|
63767
|
+
}
|
|
63768
|
+
function plainSummaryForOperation(result, request) {
|
|
63769
|
+
const operation = request.operation;
|
|
63770
|
+
const payload = jsonPayloadForOperation(result, operation);
|
|
63771
|
+
if (!isPlainObject(payload)) return String(payload);
|
|
63772
|
+
switch (operation) {
|
|
63773
|
+
case "get_caplet": return [
|
|
63774
|
+
`Caplet: ${String(payload.caplet ?? "unknown")}`,
|
|
63775
|
+
`Name: ${String(payload.name ?? "Unnamed")}`,
|
|
63776
|
+
`Description: ${String(payload.description ?? "No description.")}`,
|
|
63777
|
+
payload.backend ? `Backend: ${backendType(payload.backend)}` : void 0,
|
|
63778
|
+
`Next: caplets list-tools ${String(payload.caplet ?? "<caplet>")} or caplets search-tools ${String(payload.caplet ?? "<caplet>")} <query>`
|
|
63779
|
+
].filter((line) => Boolean(line)).join("\n");
|
|
63780
|
+
case "check_backend": return [
|
|
63781
|
+
`Backend: ${String(payload.server ?? "caplet")} is ${String(payload.status ?? "unknown")}`,
|
|
63782
|
+
typeof payload.toolCount === "number" ? `Tools: ${payload.toolCount}` : void 0,
|
|
63783
|
+
typeof payload.elapsedMs === "number" ? `Elapsed: ${payload.elapsedMs}ms` : void 0,
|
|
63784
|
+
`Next: caplets list-tools ${String(payload.server ?? "<caplet>")}`
|
|
63785
|
+
].filter((line) => Boolean(line)).join("\n");
|
|
63786
|
+
case "list_tools": {
|
|
63787
|
+
const tools = Array.isArray(payload.tools) ? payload.tools : [];
|
|
63788
|
+
return [
|
|
63789
|
+
`Tools for ${String(payload.server ?? "caplet")} (${tools.length}):`,
|
|
63790
|
+
...formatToolLines(tools, "plain"),
|
|
63791
|
+
`Next: caplets get-tool ${String(payload.server ?? "<caplet>")}.<tool> or caplets call-tool ${String(payload.server ?? "<caplet>")}.<tool> --args '{...}'`
|
|
63792
|
+
].join("\n");
|
|
63793
|
+
}
|
|
63794
|
+
case "search_tools": {
|
|
63795
|
+
const tools = Array.isArray(payload.tools) ? payload.tools : [];
|
|
63796
|
+
return [
|
|
63797
|
+
`Matches for ${JSON.stringify(String(payload.query ?? ""))} in ${String(payload.server ?? "caplet")} (${tools.length}):`,
|
|
63798
|
+
...formatToolLines(tools, "plain"),
|
|
63799
|
+
tools.length > 0 ? `Next: caplets get-tool ${String(payload.server ?? "<caplet>")}.${firstToolName(tools) ?? "<tool>"}` : `Next: try caplets list-tools ${String(payload.server ?? "<caplet>")} or a broader query.`
|
|
63800
|
+
].join("\n");
|
|
63801
|
+
}
|
|
63802
|
+
case "get_tool": {
|
|
63803
|
+
const tool = isPlainObject(payload.tool) ? payload.tool : {};
|
|
63804
|
+
const target = `${String(payload.server ?? "<caplet>")}.${String(tool.name ?? "<tool>")}`;
|
|
63805
|
+
return [
|
|
63806
|
+
`Tool: ${target}`,
|
|
63807
|
+
tool.description ? `Description: ${compactDescription(String(tool.description))}` : void 0,
|
|
63808
|
+
`Input: ${schemaSummary(tool.inputSchema)}`,
|
|
63809
|
+
`Output: ${tool.outputSchema ? schemaSummary(tool.outputSchema) : "not declared"}`,
|
|
63810
|
+
`Next: caplets call-tool ${target} --args '{...}'`,
|
|
63811
|
+
"Use --format json to inspect full schemas and descriptions."
|
|
63812
|
+
].filter((line) => Boolean(line)).join("\n");
|
|
63813
|
+
}
|
|
63814
|
+
case "call_tool": return [
|
|
63815
|
+
`Call ${`${String(request.caplet ?? "<caplet>")}.${String(request.tool ?? "unknown")}`} ${payload.isError === true ? "failed" : "succeeded"}.`,
|
|
63816
|
+
callStatusLine(payload),
|
|
63817
|
+
`Result: ${summarizeCallResult(payload)}`,
|
|
63818
|
+
"Use --format json to inspect the full structured result."
|
|
63819
|
+
].filter((line) => Boolean(line)).join("\n");
|
|
63820
|
+
default: return JSON.stringify(payload, null, 2);
|
|
63821
|
+
}
|
|
63822
|
+
}
|
|
63823
|
+
function formatToolLines(tools, format) {
|
|
63824
|
+
if (tools.length === 0) return ["- none"];
|
|
63825
|
+
return tools.map((tool) => {
|
|
63826
|
+
if (!isPlainObject(tool)) return `- ${String(tool)}`;
|
|
63827
|
+
const name = String(tool.tool ?? tool.name ?? "unknown");
|
|
63828
|
+
const displayName = format === "markdown" ? `\`${name}\`` : name;
|
|
63829
|
+
const flags = [tool.hasInputSchema ? "input" : void 0, tool.hasOutputSchema ? "output" : void 0].filter(Boolean).join(", ");
|
|
63830
|
+
return `- ${displayName}${flags ? ` (${flags})` : ""}${tool.description ? ` — ${compactDescription(String(tool.description))}` : ""}`;
|
|
63831
|
+
});
|
|
63832
|
+
}
|
|
63833
|
+
function compactDescription(value) {
|
|
63834
|
+
const firstParagraph = value.trim().split(/\n\s*\n/u)[0] ?? "";
|
|
63835
|
+
const collapsed = (firstParagraph.match(/^.*?(?:[.!?](?=\s|$)|$)/u)?.[0] ?? firstParagraph).replace(/\s+/gu, " ").trim();
|
|
63836
|
+
return collapsed.length > 140 ? `${collapsed.slice(0, 137).trimEnd()}...` : collapsed;
|
|
63837
|
+
}
|
|
63838
|
+
function firstToolName(tools) {
|
|
63839
|
+
const first = tools[0];
|
|
63840
|
+
return isPlainObject(first) && typeof first.tool === "string" ? first.tool : void 0;
|
|
63841
|
+
}
|
|
63842
|
+
function backendType(value) {
|
|
63843
|
+
return isPlainObject(value) && typeof value.type === "string" ? value.type : "unknown";
|
|
63844
|
+
}
|
|
63845
|
+
function callStatusLine(payload) {
|
|
63846
|
+
const structured = isPlainObject(payload.structuredContent) ? payload.structuredContent : payload;
|
|
63847
|
+
return typeof structured.exitCode === "number" ? `Exit code: ${structured.exitCode}` : void 0;
|
|
63848
|
+
}
|
|
63849
|
+
function summarizeCallResult(payload) {
|
|
63850
|
+
const structured = isPlainObject(payload.structuredContent) ? payload.structuredContent : payload;
|
|
63851
|
+
const preview = previewValue(preferredPreviewValue(structured));
|
|
63852
|
+
if (preview) return preview;
|
|
63853
|
+
const keys = Object.keys(structured).filter((key) => key !== "elapsedMs");
|
|
63854
|
+
return keys.length > 0 ? `structured keys: ${keys.join(", ")}` : "no structured content";
|
|
63855
|
+
}
|
|
63856
|
+
function preferredPreviewValue(value) {
|
|
63857
|
+
if (!isPlainObject(value)) return value;
|
|
63858
|
+
if ("result" in value) return value.result;
|
|
63859
|
+
if ("json" in value) return value.json;
|
|
63860
|
+
if (typeof value.text === "string") return value.text;
|
|
63861
|
+
if (typeof value.stdout === "string" && value.stdout.trim()) return value.stdout.trim();
|
|
63862
|
+
return value;
|
|
63863
|
+
}
|
|
63864
|
+
function previewValue(value) {
|
|
63865
|
+
if (typeof value === "string") return truncatePreview(value);
|
|
63866
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
|
|
63867
|
+
if (Array.isArray(value)) return truncatePreview(JSON.stringify(value));
|
|
63868
|
+
if (isPlainObject(value)) {
|
|
63869
|
+
const entries = Object.entries(value).slice(0, 4);
|
|
63870
|
+
if (entries.length === 0) return "empty object";
|
|
63871
|
+
return truncatePreview(entries.map(([key, entryValue]) => `${key}: ${previewScalar(entryValue)}`).join(", "));
|
|
63872
|
+
}
|
|
63873
|
+
}
|
|
63874
|
+
function previewScalar(value) {
|
|
63875
|
+
if (typeof value === "string") return JSON.stringify(truncatePreview(value, 80));
|
|
63876
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null) return String(value);
|
|
63877
|
+
if (Array.isArray(value)) return `[${value.length} item${value.length === 1 ? "" : "s"}]`;
|
|
63878
|
+
if (isPlainObject(value)) return `{${Object.keys(value).slice(0, 3).join(", ")}${Object.keys(value).length > 3 ? ", ..." : ""}}`;
|
|
63879
|
+
return typeof value;
|
|
63880
|
+
}
|
|
63881
|
+
function truncatePreview(value, maxLength = 180) {
|
|
63882
|
+
const collapsed = value.replace(/\s+/gu, " ").trim();
|
|
63883
|
+
return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 3).trimEnd()}...` : collapsed;
|
|
63884
|
+
}
|
|
63885
|
+
function schemaSummary(schema) {
|
|
63886
|
+
if (!isPlainObject(schema)) return "not declared";
|
|
63887
|
+
const properties = isPlainObject(schema.properties) ? Object.keys(schema.properties) : [];
|
|
63888
|
+
const required = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
|
|
63889
|
+
return [
|
|
63890
|
+
typeof schema.type === "string" ? `type ${schema.type}` : void 0,
|
|
63891
|
+
properties.length > 0 ? `properties ${properties.join(", ")}` : "no declared properties",
|
|
63892
|
+
required.length > 0 ? `required ${required.join(", ")}` : "no required fields"
|
|
63893
|
+
].filter((part) => Boolean(part)).join("; ");
|
|
63894
|
+
}
|
|
63495
63895
|
function addDestinationRoot(options) {
|
|
63496
63896
|
return options.global ? resolveCapletsRoot(resolveConfigPath(envConfigPath())) : resolveProjectCapletsRoot();
|
|
63497
63897
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caplets/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Core runtime library for Caplets progressive disclosure gateways.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"caplets",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^25.7.0",
|
|
47
|
+
"@typescript/native-preview": "7.0.0-dev.20260515.1",
|
|
47
48
|
"rolldown": "^1.0.0",
|
|
48
49
|
"typescript": "^6.0.3",
|
|
49
50
|
"vitest": "^4.1.6"
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"scripts": {
|
|
55
56
|
"build": "rm -rf dist && rolldown -c",
|
|
56
57
|
"build:watch": "rolldown -c --watch",
|
|
57
|
-
"typecheck": "
|
|
58
|
+
"typecheck": "tsgo --noEmit",
|
|
58
59
|
"test": "vitest run"
|
|
59
60
|
}
|
|
60
61
|
}
|