caplets 0.17.1 → 0.17.3
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 +19 -6
- package/dist/index.js +129 -36
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -77,16 +77,18 @@ You can also invoke configured Caplets directly from the CLI for agent-friendly
|
|
|
77
77
|
```sh
|
|
78
78
|
caplets get-caplet context7
|
|
79
79
|
caplets list-tools context7
|
|
80
|
-
caplets get-tool context7
|
|
81
|
-
caplets call-tool context7
|
|
82
|
-
caplets call-tool context7
|
|
80
|
+
caplets get-tool context7 resolve-library-id
|
|
81
|
+
caplets call-tool context7 resolve-library-id --args '{"libraryName":"react"}'
|
|
82
|
+
caplets call-tool context7 resolve-library-id --args '{"libraryName":"react"}' --field result.id --format json
|
|
83
83
|
caplets list-resources docs
|
|
84
84
|
caplets read-resource docs file:///repo/README.md
|
|
85
85
|
caplets list-prompts linear
|
|
86
|
-
caplets get-prompt linear
|
|
86
|
+
caplets get-prompt linear review_issue --args '{"issueId":"CAP-123"}'
|
|
87
87
|
caplets complete docs --resource-template 'file:///repo/{path}' --argument path --value src/
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
The older qualified form, such as `caplets call-tool context7.resolve-library-id` or `caplets get-prompt linear.review_issue`, remains supported for scripts and existing usage.
|
|
91
|
+
|
|
90
92
|
Direct CLI operation commands print Markdown summaries by default. Add `--format plain` for plain text or `--format json` for machine-readable JSON (`md` is accepted as an alias for `markdown`). If a downstream tool returns `isError: true`, Caplets still exits with status code 1.
|
|
91
93
|
|
|
92
94
|
### Shell completions
|
|
@@ -117,7 +119,7 @@ caplets completion cmd > %USERPROFILE%\caplets-completion.cmd
|
|
|
117
119
|
%USERPROFILE%\caplets-completion.cmd
|
|
118
120
|
```
|
|
119
121
|
|
|
120
|
-
Completions include command names, options, common enum values, configured Caplet IDs, and cache-backed downstream names for qualified targets such as `caplets call-tool repo.<TAB>`. Downstream discovery is bounded by the `completion` config timeouts and a platform-native cache directory. Generated shell scripts suppress completion stderr; run the underlying CLI command directly when debugging completion behavior.
|
|
122
|
+
Completions include command names, options, common enum values, configured Caplet IDs, and cache-backed downstream names for split targets such as `caplets call-tool repo <TAB>` and qualified targets such as `caplets call-tool repo.<TAB>`. Downstream discovery is bounded by the `completion` config timeouts and a platform-native cache directory. Generated shell scripts suppress completion stderr; run the underlying CLI command directly when debugging completion behavior.
|
|
121
123
|
|
|
122
124
|
Backends that require OAuth or token auth may need `caplets auth login <server>` before live downstream completions can return richer results. Completion never starts interactive login flows.
|
|
123
125
|
|
|
@@ -546,7 +548,18 @@ This repository includes polished working examples under [`caplets/`](caplets/):
|
|
|
546
548
|
- `linear`: Linear's hosted OAuth MCP endpoint.
|
|
547
549
|
- `context7`: Context7 documentation lookup through `@upstash/context7-mcp`.
|
|
548
550
|
- `repo-cli`: Read-oriented repository CLI workflows through `git` and package scripts.
|
|
549
|
-
- `
|
|
551
|
+
- `ast-grep`: Structural code search, scan, rewrite, rule testing, and scaffolding through the `ast-grep` CLI.
|
|
552
|
+
- `osv`: OSV.dev vulnerability lookups through explicit read-only HTTP actions.
|
|
553
|
+
- `npm`: npm registry operations through npm's published OpenAPI spec URL.
|
|
554
|
+
- `pypi`: PyPI project metadata, release metadata, and Simple API JSON through a curated OpenAPI spec.
|
|
555
|
+
- `deepwiki`: Repository-focused documentation and architecture context through DeepWiki MCP.
|
|
556
|
+
- `sourcegraph`: Cross-repository code search and navigation through Sourcegraph MCP.
|
|
557
|
+
- `playwright`: Headless browser automation for frontend inspection and testing through Playwright MCP.
|
|
558
|
+
- `coding-agent-toolkit`: A CapletSet that bundles high-value coding-agent examples; source children are symlinks to canonical top-level examples and installed copies are materialized as self-contained files/directories.
|
|
559
|
+
- `github-cli`: Pre-existing secondary read-oriented GitHub workflows through the `gh` CLI; prefer the canonical `github` MCP example for the polished GitHub integration.
|
|
560
|
+
|
|
561
|
+
GraphQL is intentionally skipped in this showcase batch so the examples can focus on HTTP,
|
|
562
|
+
OpenAPI, MCP, CLI, and CapletSet coverage without duplicating GitHub or GitLab surfaces.
|
|
550
563
|
|
|
551
564
|
Install every example from a repo's `caplets/` directory into the current project's
|
|
552
565
|
`./.caplets` directory:
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import { accessSync, chmodSync, closeSync, constants, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
|
|
4
|
-
import minpath, { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, win32 } from "node:path";
|
|
3
|
+
import { accessSync, chmodSync, closeSync, constants, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync, writeSync } from "node:fs";
|
|
4
|
+
import minpath, { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, sep, win32 } from "node:path";
|
|
5
5
|
import { execFileSync, spawn } from "node:child_process";
|
|
6
6
|
import process$1, { stdin, stdout } from "node:process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -4709,7 +4709,7 @@ function generatedToolInputJsonSchema() {
|
|
|
4709
4709
|
return generatedToolInputJsonSchemaForCaplet({ backend: "tool" });
|
|
4710
4710
|
}
|
|
4711
4711
|
//#endregion
|
|
4712
|
-
//#region ../core/dist/options-
|
|
4712
|
+
//#region ../core/dist/options-j9p3L3r1.js
|
|
4713
4713
|
var __create = Object.create;
|
|
4714
4714
|
var __defProp = Object.defineProperty;
|
|
4715
4715
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -56439,8 +56439,21 @@ async function loadOpenApiSource(endpoint, authDir) {
|
|
|
56439
56439
|
if (endpoint.specPath) return endpoint.specPath;
|
|
56440
56440
|
if (!endpoint.specUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} is missing OpenAPI spec source`);
|
|
56441
56441
|
if (!endpoint.baseUrl) throw new CapletsError("CONFIG_INVALID", `${endpoint.server} must configure baseUrl when using remote specUrl`);
|
|
56442
|
-
|
|
56443
|
-
|
|
56442
|
+
return parseOpenApiSourceText(await fetchWithLimit(endpoint.specUrl, endpoint.requestTimeoutMs, shouldSendSpecAuth(endpoint) ? authHeaders(endpoint, authDir) : {}));
|
|
56443
|
+
}
|
|
56444
|
+
function parseOpenApiSourceText(source) {
|
|
56445
|
+
let parsed;
|
|
56446
|
+
try {
|
|
56447
|
+
parsed = JSON.parse(source);
|
|
56448
|
+
} catch (jsonError) {
|
|
56449
|
+
try {
|
|
56450
|
+
parsed = (0, import_dist$1.parse)(source);
|
|
56451
|
+
} catch {
|
|
56452
|
+
throw jsonError instanceof Error ? jsonError : new Error(String(jsonError));
|
|
56453
|
+
}
|
|
56454
|
+
}
|
|
56455
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("OpenAPI source must parse to an object");
|
|
56456
|
+
return parsed;
|
|
56444
56457
|
}
|
|
56445
56458
|
function extractOperations(endpoint, document) {
|
|
56446
56459
|
const operations = [];
|
|
@@ -56458,6 +56471,7 @@ function extractOperations(endpoint, document) {
|
|
|
56458
56471
|
const requestBody = requestBodyFor(operation);
|
|
56459
56472
|
const outputSchema = outputSchemaFor(operation);
|
|
56460
56473
|
const baseUrl = endpoint.baseUrl ?? firstServerUrl(document);
|
|
56474
|
+
const staticHeaders = staticHeaderDefaultsFor(endpoint, parameters);
|
|
56461
56475
|
validateOperationBaseUrl(endpoint, baseUrl);
|
|
56462
56476
|
operations.push({
|
|
56463
56477
|
name,
|
|
@@ -56465,15 +56479,32 @@ function extractOperations(endpoint, document) {
|
|
|
56465
56479
|
path,
|
|
56466
56480
|
...typeof operation.summary === "string" ? { summary: operation.summary } : {},
|
|
56467
56481
|
...typeof operation.description === "string" ? { description: operation.description } : {},
|
|
56468
|
-
inputSchema: inputSchemaFor(parameters, requestBody),
|
|
56482
|
+
inputSchema: inputSchemaFor(parameters, requestBody, staticHeaders),
|
|
56469
56483
|
...outputSchema ? { outputSchema } : {},
|
|
56470
56484
|
...requestBody?.contentType ? { requestBodyContentType: requestBody.contentType } : {},
|
|
56471
|
-
...baseUrl ? { baseUrl } : {}
|
|
56485
|
+
...baseUrl ? { baseUrl } : {},
|
|
56486
|
+
...Object.keys(staticHeaders).length ? { staticHeaders } : {}
|
|
56472
56487
|
});
|
|
56473
56488
|
}
|
|
56474
56489
|
}
|
|
56475
56490
|
return operations.sort((left, right) => left.name.localeCompare(right.name));
|
|
56476
56491
|
}
|
|
56492
|
+
function staticHeaderDefaultsFor(endpoint, parameters) {
|
|
56493
|
+
const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
|
|
56494
|
+
const headers = {};
|
|
56495
|
+
for (const parameter of parameters) {
|
|
56496
|
+
if (parameter?.in !== "header" || typeof parameter.name !== "string") continue;
|
|
56497
|
+
const normalized = parameter.name.toLowerCase();
|
|
56498
|
+
if (configuredHeaderNames.has(normalized) || FORBIDDEN_ARGUMENT_HEADERS.has(normalized) && normalized !== "accept") continue;
|
|
56499
|
+
const defaultValue = parameter.schema?.default;
|
|
56500
|
+
if ([
|
|
56501
|
+
"string",
|
|
56502
|
+
"number",
|
|
56503
|
+
"boolean"
|
|
56504
|
+
].includes(typeof defaultValue)) headers[parameter.name] = String(defaultValue);
|
|
56505
|
+
}
|
|
56506
|
+
return headers;
|
|
56507
|
+
}
|
|
56477
56508
|
function requestBodyFor(operation) {
|
|
56478
56509
|
const requestBody = operation.requestBody;
|
|
56479
56510
|
if (!requestBody || typeof requestBody !== "object") return;
|
|
@@ -56534,19 +56565,20 @@ function structuredOutputSchema(bodySchema) {
|
|
|
56534
56565
|
}
|
|
56535
56566
|
};
|
|
56536
56567
|
}
|
|
56537
|
-
function inputSchemaFor(parameters, requestBody) {
|
|
56568
|
+
function inputSchemaFor(parameters, requestBody, staticHeaders = {}) {
|
|
56538
56569
|
const schema = {
|
|
56539
56570
|
type: "object",
|
|
56540
56571
|
additionalProperties: false,
|
|
56541
56572
|
properties: {}
|
|
56542
56573
|
};
|
|
56543
56574
|
const required = [];
|
|
56575
|
+
const protectedStaticHeaders = new Set(Object.keys(staticHeaders).map((key) => key.toLowerCase()).filter((key) => FORBIDDEN_ARGUMENT_HEADERS.has(key)));
|
|
56544
56576
|
for (const location of [
|
|
56545
56577
|
"path",
|
|
56546
56578
|
"query",
|
|
56547
56579
|
"header"
|
|
56548
56580
|
]) {
|
|
56549
|
-
const locationParameters = parameters.filter((parameter) => parameter?.in === location);
|
|
56581
|
+
const locationParameters = parameters.filter((parameter) => parameter?.in === location && !(location === "header" && protectedStaticHeaders.has(parameter.name?.toLowerCase())));
|
|
56550
56582
|
if (locationParameters.length === 0) continue;
|
|
56551
56583
|
const nestedRequired = locationParameters.filter((parameter) => parameter.required === true || location === "path").map((parameter) => parameter.name);
|
|
56552
56584
|
schema.properties[location] = {
|
|
@@ -56585,7 +56617,8 @@ function buildRequest(endpoint, operation, args, authDir) {
|
|
|
56585
56617
|
for (const [key, value] of Object.entries(asRecord(args.query))) if (value !== void 0 && value !== null) url.searchParams.append(key, serializeHttpValue("query", key, value));
|
|
56586
56618
|
const headers = new Headers();
|
|
56587
56619
|
applyAuth(headers, endpoint, authDir);
|
|
56588
|
-
const configuredHeaderNames =
|
|
56620
|
+
const configuredHeaderNames = configuredAuthHeaderNames(endpoint);
|
|
56621
|
+
for (const [key, value] of Object.entries(operation.staticHeaders ?? {})) if (!headers.has(key) && !configuredHeaderNames.has(key.toLowerCase())) headers.set(key, value);
|
|
56589
56622
|
for (const [key, value] of Object.entries(asRecord(args.header))) if (value !== void 0 && value !== null) {
|
|
56590
56623
|
const normalized = key.toLowerCase();
|
|
56591
56624
|
if (FORBIDDEN_ARGUMENT_HEADERS.has(normalized) || configuredHeaderNames.has(normalized)) throw new CapletsError("REQUEST_INVALID", `Header ${key} cannot be supplied by arguments`);
|
|
@@ -56631,6 +56664,9 @@ function asRecord(value) {
|
|
|
56631
56664
|
function applyAuth(headers, endpoint, authDir) {
|
|
56632
56665
|
for (const [key, value] of Object.entries(authHeaders(endpoint, authDir))) headers.set(key, value);
|
|
56633
56666
|
}
|
|
56667
|
+
function configuredAuthHeaderNames(endpoint) {
|
|
56668
|
+
return endpoint.auth.type === "headers" ? new Set(Object.keys(endpoint.auth.headers).map((key) => key.toLowerCase())) : /* @__PURE__ */ new Set();
|
|
56669
|
+
}
|
|
56634
56670
|
function authHeaders(endpoint, authDir) {
|
|
56635
56671
|
switch (endpoint.auth.type) {
|
|
56636
56672
|
case "none": return {};
|
|
@@ -57625,7 +57661,7 @@ var CapletsEngine = class {
|
|
|
57625
57661
|
}
|
|
57626
57662
|
}
|
|
57627
57663
|
async completeCliWords(words) {
|
|
57628
|
-
const { completeCliWords } = await Promise.resolve().then(() =>
|
|
57664
|
+
const { completeCliWords } = await Promise.resolve().then(() => completion_Cq1z7ci2_exports).then((n) => n.r);
|
|
57629
57665
|
return await completeCliWords(words, {
|
|
57630
57666
|
config: this.registry.config,
|
|
57631
57667
|
managers: {
|
|
@@ -57963,8 +57999,8 @@ function hasEnv$1(value) {
|
|
|
57963
57999
|
return value !== void 0 && value.trim() !== "";
|
|
57964
58000
|
}
|
|
57965
58001
|
//#endregion
|
|
57966
|
-
//#region ../core/dist/completion-
|
|
57967
|
-
var
|
|
58002
|
+
//#region ../core/dist/completion-Cq1z7ci2.js
|
|
58003
|
+
var completion_Cq1z7ci2_exports = /* @__PURE__ */ __exportAll$1({
|
|
57968
58004
|
a: () => formatCapletList,
|
|
57969
58005
|
c: () => resolveCliConfigPaths,
|
|
57970
58006
|
i: () => trailingSpaceCompletionToken,
|
|
@@ -58434,9 +58470,17 @@ async function completeCliWords(words, options = {}) {
|
|
|
58434
58470
|
if (normalized.length === 1) return prefixFilter([...topLevelCommandNames], current);
|
|
58435
58471
|
if (normalized.length === 2 && command in cliSubcommands) return prefixFilter(cliSubcommands[command], current);
|
|
58436
58472
|
if (normalized.length === 2 && capletIdCommands.has(command)) return prefixFilter(promptResourceCommands.has(command) ? configuredCapletIds(options, { backend: "mcp" }) : configuredCapletIds(options), current);
|
|
58437
|
-
if (
|
|
58438
|
-
|
|
58439
|
-
|
|
58473
|
+
if (qualifiedToolCommands.has(command) || qualifiedPromptCommands.has(command)) {
|
|
58474
|
+
const kind = qualifiedToolCommands.has(command) ? "tools" : "prompts";
|
|
58475
|
+
const idFilter = qualifiedPromptCommands.has(command) ? { backend: "mcp" } : void 0;
|
|
58476
|
+
if (normalized.length === 2) {
|
|
58477
|
+
if (current.includes(".")) return prefixFilter((await discoverCompletionCandidates(current.slice(0, current.indexOf(".")), kind, discoveryOptions(options))).map((candidate) => candidate.value), current);
|
|
58478
|
+
return prefixFilter(configuredCapletIds(options, idFilter), current);
|
|
58479
|
+
}
|
|
58480
|
+
if (normalized.length === 3 && subcommand && !subcommand.includes(".")) {
|
|
58481
|
+
if (current.startsWith("-")) return [];
|
|
58482
|
+
return prefixFilter((await discoverCompletionCandidates(subcommand, kind, discoveryOptions(options))).map((candidate) => candidate.value.replace(`${subcommand}.`, "")), current);
|
|
58483
|
+
}
|
|
58440
58484
|
}
|
|
58441
58485
|
if (command === cliCommands.readResource && normalized.length === 3) return prefixFilter((await discoverCompletionCandidates(subcommand, "resources", discoveryOptions(options))).map((candidate) => candidate.value), current);
|
|
58442
58486
|
if (command === cliCommands.auth && ["login", "logout"].includes(subcommand) && normalized.length === 3) return prefixFilter(configuredCapletIds(options), current);
|
|
@@ -59819,7 +59863,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
|
|
|
59819
59863
|
values: [],
|
|
59820
59864
|
hasMore: false
|
|
59821
59865
|
} };
|
|
59822
|
-
var version$1 = "0.18.
|
|
59866
|
+
var version$1 = "0.18.3";
|
|
59823
59867
|
var CapletsMcpSession = class {
|
|
59824
59868
|
engine;
|
|
59825
59869
|
server;
|
|
@@ -63618,12 +63662,14 @@ function rejectCrossKindDestinationCollision(plan, destinationRoot) {
|
|
|
63618
63662
|
function installPlan(caplet, options) {
|
|
63619
63663
|
const isDirectory = basename(caplet.path) === "CAPLET.md";
|
|
63620
63664
|
const sourcePath = isDirectory ? dirname(caplet.path) : caplet.path;
|
|
63665
|
+
const sourceBoundary = dirname(sourcePath);
|
|
63621
63666
|
const sourcePathRelative = relative(options.repoRoot, sourcePath);
|
|
63622
63667
|
const destination = isDirectory ? join(options.destinationRoot, caplet.id) : join(options.destinationRoot, `${caplet.id}.md`);
|
|
63623
63668
|
return {
|
|
63624
63669
|
id: caplet.id,
|
|
63625
63670
|
source: `${options.sourceId}#${sourcePathRelative}`,
|
|
63626
63671
|
sourcePath,
|
|
63672
|
+
sourceBoundary,
|
|
63627
63673
|
destination,
|
|
63628
63674
|
kind: isDirectory ? "directory" : "file"
|
|
63629
63675
|
};
|
|
@@ -63685,16 +63731,40 @@ function removeInstallPath(path, label, force) {
|
|
|
63685
63731
|
}
|
|
63686
63732
|
function copyInstallPath(plan) {
|
|
63687
63733
|
try {
|
|
63734
|
+
if (plan.kind === "directory") {
|
|
63735
|
+
copyDirectoryCaplet(plan.sourcePath, plan.destination, realpathSync(plan.sourceBoundary));
|
|
63736
|
+
return;
|
|
63737
|
+
}
|
|
63688
63738
|
cpSync(plan.sourcePath, plan.destination, {
|
|
63689
|
-
recursive:
|
|
63739
|
+
recursive: false,
|
|
63690
63740
|
force: false,
|
|
63691
63741
|
errorOnExist: true
|
|
63692
63742
|
});
|
|
63693
63743
|
} catch (error) {
|
|
63744
|
+
if (error instanceof CapletsError) throw error;
|
|
63694
63745
|
if (isFsError(error, "EEXIST") || isFsError(error, "EISDIR")) throw new CapletsError("CONFIG_EXISTS", `Caplet ${plan.id} already exists at ${plan.destination}; pass --force to overwrite it`, toSafeError(error));
|
|
63695
63746
|
throw new CapletsError("CONFIG_INVALID", `Could not install Caplet ${plan.id} to ${plan.destination}`, toSafeError(error));
|
|
63696
63747
|
}
|
|
63697
63748
|
}
|
|
63749
|
+
function copyDirectoryCaplet(source, destination, sourceBoundary, seenDirectories = /* @__PURE__ */ new Set()) {
|
|
63750
|
+
const resolvedSource = lstatSync(source).isSymbolicLink() ? resolveDirectoryCapletSymlink(source, sourceBoundary) : source;
|
|
63751
|
+
if (statSync(resolvedSource).isDirectory()) {
|
|
63752
|
+
const realDirectory = realpathSync(resolvedSource);
|
|
63753
|
+
if (seenDirectories.has(realDirectory)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} creates a copy cycle`);
|
|
63754
|
+
const childSeenDirectories = new Set(seenDirectories);
|
|
63755
|
+
childSeenDirectories.add(realDirectory);
|
|
63756
|
+
mkdirSync(destination);
|
|
63757
|
+
for (const entry of readdirSync(resolvedSource)) copyDirectoryCaplet(join(resolvedSource, entry), join(destination, entry), sourceBoundary, childSeenDirectories);
|
|
63758
|
+
return;
|
|
63759
|
+
}
|
|
63760
|
+
copyFileSync(resolvedSource, destination);
|
|
63761
|
+
}
|
|
63762
|
+
function resolveDirectoryCapletSymlink(source, sourceBoundary) {
|
|
63763
|
+
const target = readlinkSync(source);
|
|
63764
|
+
const resolvedTarget = realpathSync(isAbsolute(target) ? target : resolve(dirname(source), target));
|
|
63765
|
+
if (resolvedTarget !== sourceBoundary && !resolvedTarget.startsWith(`${sourceBoundary}${sep}`)) throw new CapletsError("CONFIG_INVALID", `Directory Caplet symlink ${source} resolves outside source Caplets boundary`);
|
|
63766
|
+
return resolvedTarget;
|
|
63767
|
+
}
|
|
63698
63768
|
function isFsError(error, code) {
|
|
63699
63769
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
63700
63770
|
}
|
|
@@ -67491,7 +67561,7 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
67491
67561
|
}
|
|
67492
67562
|
});
|
|
67493
67563
|
}
|
|
67494
|
-
return c.json(await dispatchRemoteCliRequest(request, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name))));
|
|
67564
|
+
return c.json(await dispatchRemoteCliRequest(request, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.publicOrigin, options.trustProxy, (name) => c.req.header(name))));
|
|
67495
67565
|
});
|
|
67496
67566
|
app.get(routePath(paths.control, "auth/callback/:flowId"), async (c) => {
|
|
67497
67567
|
const flowId = c.req.param("flowId");
|
|
@@ -67501,7 +67571,7 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
67501
67571
|
flowId,
|
|
67502
67572
|
callbackUrl: c.req.url
|
|
67503
67573
|
}
|
|
67504
|
-
}, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.trustProxy, (name) => c.req.header(name)));
|
|
67574
|
+
}, controlContext(io, writeErr, authFlowStore, c.req.url, paths.control, options.publicOrigin, options.trustProxy, (name) => c.req.header(name)));
|
|
67505
67575
|
if (!result.ok) writeErr(`Caplets authentication failed for flow ${flowId}: ${result.error.message}\n`);
|
|
67506
67576
|
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);
|
|
67507
67577
|
});
|
|
@@ -67515,12 +67585,12 @@ function createHttpServeApp(options, engine, io = {}) {
|
|
|
67515
67585
|
if (options.warnUnauthenticatedNetwork) writeErr(`Warning: Caplets MCP HTTP server is listening on ${options.host} without authentication.\n`);
|
|
67516
67586
|
return app;
|
|
67517
67587
|
}
|
|
67518
|
-
function controlContext(io, writeErr, authFlowStore, requestUrl, controlPath, trustProxy, header) {
|
|
67588
|
+
function controlContext(io, writeErr, authFlowStore, requestUrl, controlPath, publicOrigin, trustProxy, header) {
|
|
67519
67589
|
return {
|
|
67520
67590
|
...io.control,
|
|
67521
67591
|
projectCapletsRoot: io.control?.projectCapletsRoot ?? resolveProjectCapletsRoot(),
|
|
67522
67592
|
authFlowStore,
|
|
67523
|
-
controlCallbackBaseUrl: new URL(controlPath, publicRequestOrigin(requestUrl, trustProxy, header)).toString(),
|
|
67593
|
+
controlCallbackBaseUrl: new URL(controlPath, publicOrigin ?? publicRequestOrigin(requestUrl, trustProxy, header)).toString(),
|
|
67524
67594
|
writeErr
|
|
67525
67595
|
};
|
|
67526
67596
|
}
|
|
@@ -67685,6 +67755,7 @@ function resolveServeOptions(raw, env = process.env) {
|
|
|
67685
67755
|
host,
|
|
67686
67756
|
port,
|
|
67687
67757
|
path,
|
|
67758
|
+
...serverUrl ? { publicOrigin: serverUrl.origin } : {},
|
|
67688
67759
|
auth,
|
|
67689
67760
|
warnUnauthenticatedNetwork: !loopback && !auth.enabled,
|
|
67690
67761
|
loopback,
|
|
@@ -67874,9 +67945,12 @@ function createProgram(io = {}) {
|
|
|
67874
67945
|
suggestions = remote ? await remote.request("complete_cli", {
|
|
67875
67946
|
shell,
|
|
67876
67947
|
words: completionWords
|
|
67877
|
-
}) : await
|
|
67948
|
+
}) : await completeCliWordsLocally(completionWords, {
|
|
67949
|
+
...configPath ? { configPath } : {},
|
|
67950
|
+
...io.authDir ? { authDir: io.authDir } : {}
|
|
67951
|
+
});
|
|
67878
67952
|
} catch {
|
|
67879
|
-
suggestions = [];
|
|
67953
|
+
suggestions = remote ? [] : await completeCliWords(completionWords, configPath ? { configPath } : {});
|
|
67880
67954
|
}
|
|
67881
67955
|
if (suggestions.length > 0) writeOut(`${suggestions.join("\n")}\n`);
|
|
67882
67956
|
});
|
|
@@ -68070,8 +68144,8 @@ function createProgram(io = {}) {
|
|
|
68070
68144
|
format: options.format
|
|
68071
68145
|
});
|
|
68072
68146
|
});
|
|
68073
|
-
program.command(cliCommands.getTool).description("Print one downstream tool schema.").argument("<caplet
|
|
68074
|
-
const { caplet, tool } = parseQualifiedTarget(
|
|
68147
|
+
program.command(cliCommands.getTool).description("Print one downstream tool schema.").argument("<caplet-or-target>", "Caplet ID or qualified <caplet.tool> target").argument("[tool]", "downstream tool name when caplet is provided separately").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (capletOrTarget, toolArgument, options) => {
|
|
68148
|
+
const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
|
|
68075
68149
|
await executeOperation(caplet, {
|
|
68076
68150
|
operation: "get_tool",
|
|
68077
68151
|
tool
|
|
@@ -68085,8 +68159,8 @@ function createProgram(io = {}) {
|
|
|
68085
68159
|
format: options.format
|
|
68086
68160
|
});
|
|
68087
68161
|
});
|
|
68088
|
-
program.command(cliCommands.callTool).description("Call one downstream tool.").argument("<caplet
|
|
68089
|
-
const { caplet, tool } = parseQualifiedTarget(
|
|
68162
|
+
program.command(cliCommands.callTool).description("Call one downstream tool.").argument("<caplet-or-target>", "Caplet ID or qualified <caplet.tool> target").argument("[tool]", "downstream tool name when caplet is provided separately").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 (capletOrTarget, toolArgument, options) => {
|
|
68163
|
+
const { caplet, tool } = parseQualifiedTarget(capletOrTarget, toolArgument);
|
|
68090
68164
|
await executeOperation(caplet, {
|
|
68091
68165
|
operation: "call_tool",
|
|
68092
68166
|
tool,
|
|
@@ -68182,8 +68256,8 @@ function createProgram(io = {}) {
|
|
|
68182
68256
|
remote: remoteClientForCli(io),
|
|
68183
68257
|
format: options.format
|
|
68184
68258
|
}));
|
|
68185
|
-
program.command(cliCommands.getPrompt).description("Get one MCP prompt by name.").argument("<caplet
|
|
68186
|
-
const { caplet, tool: prompt } = parseQualifiedTarget(
|
|
68259
|
+
program.command(cliCommands.getPrompt).description("Get one MCP prompt by name.").argument("<caplet-or-target>", "MCP Caplet ID or qualified <caplet.prompt> target").argument("[prompt]", "prompt name when caplet is provided separately").option("--args <json-object>", "JSON object of prompt arguments").option("--format <format>", "output format: markdown, md, plain, or json", parseOutputFormat).action(async (capletOrTarget, promptArgument, options) => {
|
|
68260
|
+
const { caplet, tool: prompt } = parseQualifiedTarget(capletOrTarget, promptArgument);
|
|
68187
68261
|
await executeOperation(caplet, {
|
|
68188
68262
|
operation: "get_prompt",
|
|
68189
68263
|
prompt,
|
|
@@ -68348,14 +68422,33 @@ function parseOutputFormat(value) {
|
|
|
68348
68422
|
default: throw new CapletsError("REQUEST_INVALID", `Expected output format markdown, md, plain, or json; got ${value}`);
|
|
68349
68423
|
}
|
|
68350
68424
|
}
|
|
68351
|
-
function parseQualifiedTarget(
|
|
68352
|
-
|
|
68353
|
-
|
|
68425
|
+
function parseQualifiedTarget(capletOrTarget, toolArgument) {
|
|
68426
|
+
if (toolArgument !== void 0) {
|
|
68427
|
+
if (capletOrTarget.length === 0 || toolArgument.length === 0) throw new CapletsError("REQUEST_INVALID", "Expected target in the form <caplet> <tool> or <caplet>.<tool>");
|
|
68428
|
+
return {
|
|
68429
|
+
caplet: capletOrTarget,
|
|
68430
|
+
tool: toolArgument
|
|
68431
|
+
};
|
|
68432
|
+
}
|
|
68433
|
+
const dot = capletOrTarget.indexOf(".");
|
|
68434
|
+
if (dot <= 0 || dot === capletOrTarget.length - 1) throw new CapletsError("REQUEST_INVALID", "Expected target in the form <caplet> <tool> or <caplet>.<tool>");
|
|
68354
68435
|
return {
|
|
68355
|
-
caplet:
|
|
68356
|
-
tool:
|
|
68436
|
+
caplet: capletOrTarget.slice(0, dot),
|
|
68437
|
+
tool: capletOrTarget.slice(dot + 1)
|
|
68357
68438
|
};
|
|
68358
68439
|
}
|
|
68440
|
+
async function completeCliWordsLocally(words, options) {
|
|
68441
|
+
const engine = new CapletsEngine({
|
|
68442
|
+
...options.configPath ? { configPath: options.configPath } : {},
|
|
68443
|
+
...options.authDir ? { authDir: options.authDir } : {},
|
|
68444
|
+
watch: false
|
|
68445
|
+
});
|
|
68446
|
+
try {
|
|
68447
|
+
return await engine.completeCliWords(words);
|
|
68448
|
+
} finally {
|
|
68449
|
+
await engine.close();
|
|
68450
|
+
}
|
|
68451
|
+
}
|
|
68359
68452
|
function parseCallToolArgs(value) {
|
|
68360
68453
|
if (value === void 0) return {};
|
|
68361
68454
|
let parsed;
|
|
@@ -68780,7 +68873,7 @@ function writeAddResult(writeOut, label, result) {
|
|
|
68780
68873
|
}
|
|
68781
68874
|
//#endregion
|
|
68782
68875
|
//#region package.json
|
|
68783
|
-
var version = "0.17.
|
|
68876
|
+
var version = "0.17.3";
|
|
68784
68877
|
//#endregion
|
|
68785
68878
|
//#region src/index.ts
|
|
68786
68879
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "caplets",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"description": "Progressive disclosure gateway CLI for MCP servers and native Caplets adapters.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"caplets",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
|
-
"@caplets/core": "0.18.
|
|
37
|
+
"@caplets/core": "0.18.3"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^25.9.1",
|