caplets 0.17.3 → 0.17.5
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 +35 -13
- package/dist/index.js +662 -129
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -219,6 +219,12 @@ opencode
|
|
|
219
219
|
|
|
220
220
|
For MCP-backed Codex or Claude Code configs, point the agent's MCP server entry at the derived `/mcp` URL using that agent's supported HTTP MCP configuration. If Basic Auth is needed, use the agent's secure secret or environment interpolation mechanism rather than hardcoding credentials.
|
|
221
221
|
|
|
222
|
+
In `CAPLETS_MODE=remote`, read and execute commands use a merged Caplets view. Remote server rows load first, user-global local Caplets overlay them, and project-local Caplets have the highest priority. A local Caplet with the same ID shadows the remote one, runs locally, and prints a warning such as `Warning: project Caplet shared shadows remote Caplet`; remote-only IDs continue through remote control.
|
|
223
|
+
|
|
224
|
+
Local overlays in remote mode are best-effort. Invalid local config sources warn and are ignored while remaining valid layers still load; invalid Caplet files warn and are skipped individually. For sibling file layouts, `foo/CAPLET.md` wins over `foo.md` and warns that `foo.md` was shadowed; if the winning `CAPLET.md` is invalid, that local ID is skipped instead of falling back to `foo.md`.
|
|
225
|
+
|
|
226
|
+
Mutating commands do not automatically write to the server just because remote mode is active. `caplets init`, `caplets add ...`, and `caplets install ...` write project-local by default, `--project` is the explicit project-local target, `--global` writes user-global local files, and `--remote` sends the mutation to the remote Caplets server. Target flags are mutually exclusive.
|
|
227
|
+
|
|
222
228
|
## Convert Existing Tooling
|
|
223
229
|
|
|
224
230
|
Caplets is designed to convert what you already use into agent-friendly capability domains.
|
|
@@ -299,7 +305,7 @@ the agent chooses that server and asks to search, list, inspect, or call them.
|
|
|
299
305
|
|
|
300
306
|
## Capabilities
|
|
301
307
|
|
|
302
|
-
- Reads downstream MCP server definitions, native OpenAPI endpoint definitions, native GraphQL endpoint definitions, explicit HTTP API action definitions, and curated CLI tool definitions from
|
|
308
|
+
- Reads downstream MCP server definitions, native OpenAPI endpoint definitions, native GraphQL endpoint definitions, explicit HTTP API action definitions, and curated CLI tool definitions from user and project config sources.
|
|
303
309
|
- Registers one generated MCP tool for each enabled MCP server, OpenAPI endpoint, GraphQL endpoint, HTTP API, or CLI tools backend.
|
|
304
310
|
- Uses the configured server ID as the generated tool name.
|
|
305
311
|
- Uses the configured `name` and `description` as the capability card shown to agents.
|
|
@@ -316,12 +322,20 @@ the agent chooses that server and asks to search, list, inspect, or call them.
|
|
|
316
322
|
|
|
317
323
|
## Configure
|
|
318
324
|
|
|
319
|
-
Create a starter
|
|
325
|
+
Create a starter project config at `./.caplets/config.json`:
|
|
320
326
|
|
|
321
327
|
```sh
|
|
322
328
|
caplets init
|
|
323
329
|
```
|
|
324
330
|
|
|
331
|
+
To create a starter user config instead, pass `--global`. The user config path is
|
|
332
|
+
`${XDG_CONFIG_HOME:-~/.config}/caplets/config.json` on Unix-like platforms and
|
|
333
|
+
`%APPDATA%\caplets\config.json` on Windows:
|
|
334
|
+
|
|
335
|
+
```sh
|
|
336
|
+
caplets init --global
|
|
337
|
+
```
|
|
338
|
+
|
|
325
339
|
The generated config includes a disabled example server. Replace it with the MCP servers
|
|
326
340
|
you want Caplets to expose:
|
|
327
341
|
|
|
@@ -406,10 +420,12 @@ you want Caplets to expose:
|
|
|
406
420
|
}
|
|
407
421
|
```
|
|
408
422
|
|
|
409
|
-
The
|
|
423
|
+
The user config path can be overridden with `CAPLETS_CONFIG`; the project config path can be
|
|
424
|
+
overridden with `CAPLETS_PROJECT_CONFIG`:
|
|
410
425
|
|
|
411
426
|
```sh
|
|
412
|
-
|
|
427
|
+
CAPLETS_PROJECT_CONFIG=/path/to/project/.caplets/config.json caplets init
|
|
428
|
+
CAPLETS_CONFIG=/path/to/user/config.json caplets init --global
|
|
413
429
|
CAPLETS_CONFIG=/path/to/config.json caplets serve
|
|
414
430
|
```
|
|
415
431
|
|
|
@@ -422,10 +438,9 @@ caplets config paths
|
|
|
422
438
|
caplets config paths --json
|
|
423
439
|
```
|
|
424
440
|
|
|
425
|
-
Caplets validates
|
|
441
|
+
Caplets validates config files at startup and hot reloads config changes while `caplets serve`
|
|
426
442
|
is running. Invalid edits are ignored until fixed, so the MCP server keeps serving the last
|
|
427
|
-
known-good config instead of dropping every tool because of a transient JSON or validation
|
|
428
|
-
error.
|
|
443
|
+
known-good config instead of dropping every tool because of a transient JSON or validation error.
|
|
429
444
|
|
|
430
445
|
The optional `$schema` field points editors at the generated JSON Schema in
|
|
431
446
|
[`schemas/caplets-config.schema.json`](schemas/caplets-config.schema.json). CI verifies that
|
|
@@ -591,12 +606,14 @@ That means a project-local Caplet can intentionally replace a user-level Caplet
|
|
|
591
606
|
Use `caplets list` to see each Caplet's winning source; when a project Caplet shadows a user-level
|
|
592
607
|
Caplet, the list output includes a warning naming the shadowed path.
|
|
593
608
|
|
|
594
|
-
`caplets init` refuses to overwrite an existing config. To intentionally replace the file:
|
|
609
|
+
`caplets init` refuses to overwrite an existing project config. To intentionally replace the file:
|
|
595
610
|
|
|
596
611
|
```sh
|
|
597
612
|
caplets init --force
|
|
598
613
|
```
|
|
599
614
|
|
|
615
|
+
Use `caplets init --global --force` to replace the user config instead.
|
|
616
|
+
|
|
600
617
|
### Caplet IDs
|
|
601
618
|
|
|
602
619
|
Each key under `mcpServers`, `openapiEndpoints`, `graphqlEndpoints`, `httpApis`, `cliTools`, or `capletSets` is the
|
|
@@ -914,11 +931,16 @@ caplets auth login <server> --no-open
|
|
|
914
931
|
In local mode, OAuth/OIDC tokens are stored under
|
|
915
932
|
`${XDG_STATE_HOME:-~/.local/state}/caplets/auth/<server>.json` on Unix-like platforms and
|
|
916
933
|
`%LOCALAPPDATA%\caplets\auth\<server>.json` on Windows. Token files use owner-only file
|
|
917
|
-
permissions where the platform supports them. In `CAPLETS_MODE=remote`, `caplets auth list
|
|
918
|
-
|
|
919
|
-
server
|
|
920
|
-
|
|
921
|
-
|
|
934
|
+
permissions where the platform supports them. In `CAPLETS_MODE=remote`, `caplets auth list` shows
|
|
935
|
+
local and remote auth targets with a `source` field. `caplets auth login <server>` and
|
|
936
|
+
`caplets auth logout <server>` use the matching scope when it is unambiguous; if the same ID exists
|
|
937
|
+
in multiple scopes, pass `--project`, `--global`, or `--remote`. Remote OAuth/OIDC credentials are
|
|
938
|
+
stored server-side and are not returned to the local client. Caplets supports well-known OAuth/OIDC
|
|
939
|
+
discovery and dynamic client registration when advertised. When a token expires, run
|
|
940
|
+
`caplets auth login <server>` again.
|
|
941
|
+
|
|
942
|
+
Auth target flags are mutually exclusive. `caplets auth list --project`,
|
|
943
|
+
`caplets auth list --global`, and `caplets auth list --remote` filter the listing to one scope.
|
|
922
944
|
|
|
923
945
|
To inspect or remove stored OAuth credentials:
|
|
924
946
|
|
package/dist/index.js
CHANGED
|
@@ -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-BlNyqF_E.js
|
|
4713
4713
|
var __create = Object.create;
|
|
4714
4714
|
var __defProp = Object.defineProperty;
|
|
4715
4715
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -12911,6 +12911,82 @@ function loadCapletFilesWithPaths(root) {
|
|
|
12911
12911
|
paths
|
|
12912
12912
|
} : void 0;
|
|
12913
12913
|
}
|
|
12914
|
+
function loadCapletFilesWithPathsBestEffort(root) {
|
|
12915
|
+
if (!existsSync(root)) return;
|
|
12916
|
+
const warnings = [];
|
|
12917
|
+
return buildCapletFileLoadResult(root, discoverCapletFilesBestEffort(root, warnings), warnings);
|
|
12918
|
+
}
|
|
12919
|
+
function buildCapletFileLoadResult(root, candidates, warnings) {
|
|
12920
|
+
const servers = {};
|
|
12921
|
+
const openapiEndpoints = {};
|
|
12922
|
+
const graphqlEndpoints = {};
|
|
12923
|
+
const httpApis = {};
|
|
12924
|
+
const cliTools = {};
|
|
12925
|
+
const capletSets = {};
|
|
12926
|
+
const paths = {};
|
|
12927
|
+
function hasId(id) {
|
|
12928
|
+
return Boolean(servers[id] || openapiEndpoints[id] || graphqlEndpoints[id] || httpApis[id] || cliTools[id] || capletSets[id]);
|
|
12929
|
+
}
|
|
12930
|
+
for (const candidate of candidates) {
|
|
12931
|
+
if (hasId(candidate.id)) {
|
|
12932
|
+
const message = `Duplicate Caplet ID ${candidate.id} under ${root}`;
|
|
12933
|
+
if (!warnings) throw new CapletsError("CONFIG_INVALID", message);
|
|
12934
|
+
warnings.push({
|
|
12935
|
+
path: candidate.path,
|
|
12936
|
+
message: `${message}; skipping duplicate at ${candidate.path}`
|
|
12937
|
+
});
|
|
12938
|
+
continue;
|
|
12939
|
+
}
|
|
12940
|
+
let config;
|
|
12941
|
+
try {
|
|
12942
|
+
config = readCapletFile(candidate.path);
|
|
12943
|
+
} catch (error) {
|
|
12944
|
+
if (!warnings) throw error;
|
|
12945
|
+
warnings.push({
|
|
12946
|
+
path: candidate.path,
|
|
12947
|
+
message: `Skipping invalid Caplet file at ${candidate.path}: ${errorMessage$1(error)}`
|
|
12948
|
+
});
|
|
12949
|
+
continue;
|
|
12950
|
+
}
|
|
12951
|
+
paths[candidate.id] = candidate.path;
|
|
12952
|
+
if (isPlainObject$5(config) && config.backend === "openapi") {
|
|
12953
|
+
const { backend: _backend, ...endpoint } = config;
|
|
12954
|
+
openapiEndpoints[candidate.id] = endpoint;
|
|
12955
|
+
} else if (isPlainObject$5(config) && config.backend === "graphql") {
|
|
12956
|
+
const { backend: _backend, ...endpoint } = config;
|
|
12957
|
+
graphqlEndpoints[candidate.id] = endpoint;
|
|
12958
|
+
} else if (isPlainObject$5(config) && config.backend === "http") {
|
|
12959
|
+
const { backend: _backend, ...endpoint } = config;
|
|
12960
|
+
httpApis[candidate.id] = endpoint;
|
|
12961
|
+
} else if (isPlainObject$5(config) && config.backend === "cli") {
|
|
12962
|
+
const { backend: _backend, ...endpoint } = config;
|
|
12963
|
+
cliTools[candidate.id] = endpoint;
|
|
12964
|
+
} else if (isPlainObject$5(config) && config.backend === "caplets") {
|
|
12965
|
+
const { backend: _backend, ...endpoint } = config;
|
|
12966
|
+
capletSets[candidate.id] = endpoint;
|
|
12967
|
+
} else servers[candidate.id] = config;
|
|
12968
|
+
}
|
|
12969
|
+
const hasServers = Object.keys(servers).length > 0;
|
|
12970
|
+
const hasOpenApi = Object.keys(openapiEndpoints).length > 0;
|
|
12971
|
+
const hasGraphQl = Object.keys(graphqlEndpoints).length > 0;
|
|
12972
|
+
const hasHttpApis = Object.keys(httpApis).length > 0;
|
|
12973
|
+
const hasCliTools = Object.keys(cliTools).length > 0;
|
|
12974
|
+
const hasCapletSets = Object.keys(capletSets).length > 0;
|
|
12975
|
+
const config = {
|
|
12976
|
+
...hasServers ? { mcpServers: servers } : {},
|
|
12977
|
+
...hasOpenApi ? { openapiEndpoints } : {},
|
|
12978
|
+
...hasGraphQl ? { graphqlEndpoints } : {},
|
|
12979
|
+
...hasHttpApis ? { httpApis } : {},
|
|
12980
|
+
...hasCliTools ? { cliTools } : {},
|
|
12981
|
+
...hasCapletSets ? { capletSets } : {}
|
|
12982
|
+
};
|
|
12983
|
+
if (!(Object.keys(config).length > 0) && warnings?.length === 0) return;
|
|
12984
|
+
return {
|
|
12985
|
+
config,
|
|
12986
|
+
paths,
|
|
12987
|
+
warnings: warnings ?? []
|
|
12988
|
+
};
|
|
12989
|
+
}
|
|
12914
12990
|
function discoverCapletFiles(root) {
|
|
12915
12991
|
const entries = readdirSync(root, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
12916
12992
|
const candidates = [];
|
|
@@ -12935,6 +13011,82 @@ function discoverCapletFiles(root) {
|
|
|
12935
13011
|
}
|
|
12936
13012
|
return candidates;
|
|
12937
13013
|
}
|
|
13014
|
+
function discoverCapletFilesBestEffort(root, warnings) {
|
|
13015
|
+
const entries = readdirSync(root, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
13016
|
+
const byId = /* @__PURE__ */ new Map();
|
|
13017
|
+
const duplicateIds = /* @__PURE__ */ new Set();
|
|
13018
|
+
function addCandidate(id, path, isDirectoryCaplet) {
|
|
13019
|
+
try {
|
|
13020
|
+
validateCapletId(id, path);
|
|
13021
|
+
} catch (error) {
|
|
13022
|
+
warnings.push({
|
|
13023
|
+
path,
|
|
13024
|
+
message: `Skipping invalid Caplet file at ${path}: ${errorMessage$1(error)}`
|
|
13025
|
+
});
|
|
13026
|
+
return;
|
|
13027
|
+
}
|
|
13028
|
+
if (duplicateIds.has(id)) {
|
|
13029
|
+
warnings.push({
|
|
13030
|
+
path,
|
|
13031
|
+
message: `Duplicate Caplet ID ${id} under ${root}; skipping duplicate at ${path}`
|
|
13032
|
+
});
|
|
13033
|
+
return;
|
|
13034
|
+
}
|
|
13035
|
+
const existing = byId.get(id);
|
|
13036
|
+
if (!existing) {
|
|
13037
|
+
byId.set(id, {
|
|
13038
|
+
id,
|
|
13039
|
+
path,
|
|
13040
|
+
isDirectoryCaplet
|
|
13041
|
+
});
|
|
13042
|
+
return;
|
|
13043
|
+
}
|
|
13044
|
+
if (isDirectoryCaplet && !existing.isDirectoryCaplet) {
|
|
13045
|
+
warnings.push({
|
|
13046
|
+
path: existing.path,
|
|
13047
|
+
message: `Caplet file at ${existing.path} was shadowed by ${path}`
|
|
13048
|
+
});
|
|
13049
|
+
byId.set(id, {
|
|
13050
|
+
id,
|
|
13051
|
+
path,
|
|
13052
|
+
isDirectoryCaplet
|
|
13053
|
+
});
|
|
13054
|
+
return;
|
|
13055
|
+
}
|
|
13056
|
+
if (!isDirectoryCaplet && existing.isDirectoryCaplet) {
|
|
13057
|
+
warnings.push({
|
|
13058
|
+
path,
|
|
13059
|
+
message: `Caplet file at ${path} was shadowed by ${existing.path}`
|
|
13060
|
+
});
|
|
13061
|
+
return;
|
|
13062
|
+
}
|
|
13063
|
+
warnings.push({
|
|
13064
|
+
path,
|
|
13065
|
+
message: `Duplicate Caplet ID ${id} under ${root}; skipping ${existing.path} and ${path}`
|
|
13066
|
+
});
|
|
13067
|
+
byId.delete(id);
|
|
13068
|
+
duplicateIds.add(id);
|
|
13069
|
+
}
|
|
13070
|
+
for (const entry of entries) {
|
|
13071
|
+
if (entry.name === "auth" || entry.name === "config.json") continue;
|
|
13072
|
+
const path = join(root, entry.name);
|
|
13073
|
+
if (entry.isFile() && extname(entry.name).toLowerCase() === ".md") {
|
|
13074
|
+
addCandidate(basename(entry.name, extname(entry.name)), path, false);
|
|
13075
|
+
continue;
|
|
13076
|
+
}
|
|
13077
|
+
if (entry.isDirectory()) {
|
|
13078
|
+
const capletPath = join(path, "CAPLET.md");
|
|
13079
|
+
if (existsSync(capletPath) && statSync(capletPath).isFile()) addCandidate(entry.name, capletPath, true);
|
|
13080
|
+
}
|
|
13081
|
+
}
|
|
13082
|
+
return Array.from(byId.values()).map(({ id, path }) => ({
|
|
13083
|
+
id,
|
|
13084
|
+
path
|
|
13085
|
+
}));
|
|
13086
|
+
}
|
|
13087
|
+
function errorMessage$1(error) {
|
|
13088
|
+
return error instanceof Error ? error.message : String(error);
|
|
13089
|
+
}
|
|
12938
13090
|
function readCapletFile(path) {
|
|
12939
13091
|
if (statSync(path).size > MAX_CAPLET_FILE_BYTES) throw new CapletsError("CONFIG_INVALID", `Caplet file at ${path} exceeds the ${MAX_CAPLET_FILE_BYTES} byte limit`);
|
|
12940
13092
|
const { frontmatter, body } = parseFrontmatter(readFileSync(path, "utf8"), path);
|
|
@@ -13611,35 +13763,78 @@ function loadConfigWithSources(path = resolveConfigPath(), projectPath = resolve
|
|
|
13611
13763
|
const projectConfig = hasProjectConfig ? rejectProjectConfigExecutableBackendMaps(readPublicConfigInput(projectPath), projectPath) : void 0;
|
|
13612
13764
|
const projectCapletsRoot = resolveProjectCapletsRootForConfigPath$1(projectPath);
|
|
13613
13765
|
const projectCaplets = projectCapletsRoot ? loadCapletFilesWithPaths(projectCapletsRoot) : void 0;
|
|
13614
|
-
|
|
13615
|
-
|
|
13616
|
-
const { input, sources, shadows } = mergeConfigInputsWithSources({
|
|
13766
|
+
return buildConfigWithSources([
|
|
13767
|
+
{
|
|
13617
13768
|
input: userConfig,
|
|
13618
13769
|
source: {
|
|
13619
13770
|
kind: "global-config",
|
|
13620
13771
|
path
|
|
13621
13772
|
}
|
|
13622
|
-
},
|
|
13773
|
+
},
|
|
13774
|
+
userCaplets ? {
|
|
13623
13775
|
input: userCaplets.config,
|
|
13624
13776
|
source: {
|
|
13625
13777
|
kind: "global-file",
|
|
13626
13778
|
path: userCaplets.paths
|
|
13627
13779
|
}
|
|
13628
|
-
} : void 0,
|
|
13780
|
+
} : void 0,
|
|
13781
|
+
{
|
|
13629
13782
|
input: projectConfig,
|
|
13630
13783
|
source: {
|
|
13631
13784
|
kind: "project-config",
|
|
13632
13785
|
path: projectPath
|
|
13633
13786
|
}
|
|
13634
|
-
},
|
|
13787
|
+
},
|
|
13788
|
+
projectCaplets ? {
|
|
13635
13789
|
input: projectCaplets.config,
|
|
13636
13790
|
source: {
|
|
13637
13791
|
kind: "project-file",
|
|
13638
13792
|
path: projectCaplets.paths
|
|
13639
13793
|
}
|
|
13640
|
-
} : void 0
|
|
13794
|
+
} : void 0
|
|
13795
|
+
], `Caplets config not found at ${path} or ${projectPath}`, "Caplets config must define at least one MCP server, OpenAPI endpoint, GraphQL endpoint, HTTP API, CLI tools backend, or Caplet set");
|
|
13796
|
+
}
|
|
13797
|
+
function loadGlobalConfig(path = resolveConfigPath()) {
|
|
13798
|
+
const userConfig = existsSync(path) ? readPublicConfigInput(path) : void 0;
|
|
13799
|
+
const userCaplets = loadCapletFilesWithPaths(resolveCapletsRoot(path));
|
|
13800
|
+
return buildConfigWithSources([{
|
|
13801
|
+
input: userConfig,
|
|
13802
|
+
source: {
|
|
13803
|
+
kind: "global-config",
|
|
13804
|
+
path
|
|
13805
|
+
}
|
|
13806
|
+
}, userCaplets ? {
|
|
13807
|
+
input: userCaplets.config,
|
|
13808
|
+
source: {
|
|
13809
|
+
kind: "global-file",
|
|
13810
|
+
path: userCaplets.paths
|
|
13811
|
+
}
|
|
13812
|
+
} : void 0], `Caplets user config not found at ${path}`, void 0).config;
|
|
13813
|
+
}
|
|
13814
|
+
function loadProjectConfig(projectPath = resolveProjectConfigPath()) {
|
|
13815
|
+
const projectConfig = existsSync(projectPath) ? rejectProjectConfigExecutableBackendMaps(readPublicConfigInput(projectPath), projectPath) : void 0;
|
|
13816
|
+
const projectCapletsRoot = resolveProjectCapletsRootForConfigPath$1(projectPath);
|
|
13817
|
+
const projectCaplets = projectCapletsRoot ? loadCapletFilesWithPaths(projectCapletsRoot) : void 0;
|
|
13818
|
+
return buildConfigWithSources([{
|
|
13819
|
+
input: projectConfig,
|
|
13820
|
+
source: {
|
|
13821
|
+
kind: "project-config",
|
|
13822
|
+
path: projectPath
|
|
13823
|
+
}
|
|
13824
|
+
}, projectCaplets ? {
|
|
13825
|
+
input: projectCaplets.config,
|
|
13826
|
+
source: {
|
|
13827
|
+
kind: "project-file",
|
|
13828
|
+
path: projectCaplets.paths
|
|
13829
|
+
}
|
|
13830
|
+
} : void 0], `Caplets project config not found at ${projectPath}`, void 0).config;
|
|
13831
|
+
}
|
|
13832
|
+
function buildConfigWithSources(inputs, notFoundMessage, emptyMessage) {
|
|
13833
|
+
if (!inputs.some((entry) => entry?.input !== void 0)) throw new CapletsError("CONFIG_NOT_FOUND", notFoundMessage);
|
|
13834
|
+
try {
|
|
13835
|
+
const { input, sources, shadows } = mergeConfigInputsWithSources(...inputs);
|
|
13641
13836
|
const config = parseConfig(input);
|
|
13642
|
-
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",
|
|
13837
|
+
if (emptyMessage && 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", emptyMessage);
|
|
13643
13838
|
return {
|
|
13644
13839
|
config,
|
|
13645
13840
|
sources,
|
|
@@ -13650,6 +13845,74 @@ function loadConfigWithSources(path = resolveConfigPath(), projectPath = resolve
|
|
|
13650
13845
|
throw new CapletsError("CONFIG_INVALID", "Caplets config is not valid JSON", redactSecrets(error));
|
|
13651
13846
|
}
|
|
13652
13847
|
}
|
|
13848
|
+
function loadLocalOverlayConfigWithSources(path = resolveConfigPath(), projectPath = resolveProjectConfigPath()) {
|
|
13849
|
+
const warnings = [];
|
|
13850
|
+
const userConfig = existsSync(path) ? readBestEffortConfigInput(path, "global-config", warnings) : void 0;
|
|
13851
|
+
const userCaplets = loadBestEffortCapletFiles(resolveCapletsRoot(path), "global-file", warnings);
|
|
13852
|
+
const projectConfig = existsSync(projectPath) ? readBestEffortConfigInput(projectPath, "project-config", warnings, (input) => rejectProjectConfigExecutableBackendMaps(input, projectPath)) : void 0;
|
|
13853
|
+
const projectCapletsRoot = resolveProjectCapletsRootForConfigPath$1(projectPath);
|
|
13854
|
+
const projectCaplets = projectCapletsRoot ? loadBestEffortCapletFiles(projectCapletsRoot, "project-file", warnings) : void 0;
|
|
13855
|
+
const { input, sources, shadows } = mergeConfigInputsWithSources({
|
|
13856
|
+
input: userConfig,
|
|
13857
|
+
source: {
|
|
13858
|
+
kind: "global-config",
|
|
13859
|
+
path
|
|
13860
|
+
}
|
|
13861
|
+
}, userCaplets ? {
|
|
13862
|
+
input: userCaplets.config,
|
|
13863
|
+
source: {
|
|
13864
|
+
kind: "global-file",
|
|
13865
|
+
path: userCaplets.paths
|
|
13866
|
+
}
|
|
13867
|
+
} : void 0, {
|
|
13868
|
+
input: projectConfig,
|
|
13869
|
+
source: {
|
|
13870
|
+
kind: "project-config",
|
|
13871
|
+
path: projectPath
|
|
13872
|
+
}
|
|
13873
|
+
}, projectCaplets ? {
|
|
13874
|
+
input: projectCaplets.config,
|
|
13875
|
+
source: {
|
|
13876
|
+
kind: "project-file",
|
|
13877
|
+
path: projectCaplets.paths
|
|
13878
|
+
}
|
|
13879
|
+
} : void 0);
|
|
13880
|
+
return {
|
|
13881
|
+
config: parseConfig(input),
|
|
13882
|
+
sources,
|
|
13883
|
+
shadows,
|
|
13884
|
+
warnings
|
|
13885
|
+
};
|
|
13886
|
+
}
|
|
13887
|
+
function readBestEffortConfigInput(path, kind, warnings, transform) {
|
|
13888
|
+
try {
|
|
13889
|
+
const input = readPublicConfigInput(path);
|
|
13890
|
+
return transform ? transform(input) : input;
|
|
13891
|
+
} catch (error) {
|
|
13892
|
+
warnings.push({
|
|
13893
|
+
kind,
|
|
13894
|
+
path,
|
|
13895
|
+
message: errorMessage$2(error)
|
|
13896
|
+
});
|
|
13897
|
+
return;
|
|
13898
|
+
}
|
|
13899
|
+
}
|
|
13900
|
+
function loadBestEffortCapletFiles(root, kind, warnings) {
|
|
13901
|
+
const result = loadCapletFilesWithPathsBestEffort(root);
|
|
13902
|
+
if (!result) return;
|
|
13903
|
+
for (const warning of result.warnings) warnings.push({
|
|
13904
|
+
kind,
|
|
13905
|
+
path: warning.path ?? root,
|
|
13906
|
+
message: warning.message
|
|
13907
|
+
});
|
|
13908
|
+
return {
|
|
13909
|
+
config: result.config,
|
|
13910
|
+
paths: result.paths
|
|
13911
|
+
};
|
|
13912
|
+
}
|
|
13913
|
+
function errorMessage$2(error) {
|
|
13914
|
+
return error instanceof Error ? error.message : String(error);
|
|
13915
|
+
}
|
|
13653
13916
|
function loadIsolatedConfig(options) {
|
|
13654
13917
|
if (!options.configPath && !options.capletsRoot) throw new CapletsError("CONFIG_INVALID", "Nested Caplet set must define at least one source: configPath or capletsRoot");
|
|
13655
13918
|
const configInput = options.configPath ? readPublicConfigInput(options.configPath) : void 0;
|
|
@@ -57594,6 +57857,7 @@ var CapletsEngine = class {
|
|
|
57594
57857
|
watchDebounceMs;
|
|
57595
57858
|
watchEnabled;
|
|
57596
57859
|
writeErr;
|
|
57860
|
+
configLoader;
|
|
57597
57861
|
reloadListeners = /* @__PURE__ */ new Set();
|
|
57598
57862
|
watchers = [];
|
|
57599
57863
|
reloadTimer;
|
|
@@ -57606,7 +57870,8 @@ var CapletsEngine = class {
|
|
|
57606
57870
|
configPath: resolveConfigPath(options.configPath),
|
|
57607
57871
|
projectConfigPath: options.projectConfigPath ?? resolveProjectConfigPath()
|
|
57608
57872
|
};
|
|
57609
|
-
|
|
57873
|
+
this.configLoader = options.configLoader ?? loadConfig;
|
|
57874
|
+
const config = this.configLoader(this.paths.configPath, this.paths.projectConfigPath);
|
|
57610
57875
|
this.registry = new ServerRegistry(config);
|
|
57611
57876
|
this.downstream = new DownstreamManager(this.registry, selectAuthOptions(options.authDir));
|
|
57612
57877
|
this.openapi = new OpenApiManager(this.registry, selectAuthOptions(options.authDir));
|
|
@@ -57661,7 +57926,7 @@ var CapletsEngine = class {
|
|
|
57661
57926
|
}
|
|
57662
57927
|
}
|
|
57663
57928
|
async completeCliWords(words) {
|
|
57664
|
-
const { completeCliWords } = await Promise.resolve().then(() =>
|
|
57929
|
+
const { completeCliWords } = await Promise.resolve().then(() => completion_DRPTunQd_exports).then((n) => n.r);
|
|
57665
57930
|
return await completeCliWords(words, {
|
|
57666
57931
|
config: this.registry.config,
|
|
57667
57932
|
managers: {
|
|
@@ -57721,7 +57986,7 @@ var CapletsEngine = class {
|
|
|
57721
57986
|
if (this.closed) return false;
|
|
57722
57987
|
let nextConfig;
|
|
57723
57988
|
try {
|
|
57724
|
-
nextConfig =
|
|
57989
|
+
nextConfig = this.configLoader(this.paths.configPath, this.paths.projectConfigPath);
|
|
57725
57990
|
} catch (error) {
|
|
57726
57991
|
this.writeErr(`Caplets config reload failed; keeping last known-good config.\n`);
|
|
57727
57992
|
this.writeErr(`${JSON.stringify(toSafeError(error, "CONFIG_INVALID"), null, 2)}\n`);
|
|
@@ -57999,8 +58264,8 @@ function hasEnv$1(value) {
|
|
|
57999
58264
|
return value !== void 0 && value.trim() !== "";
|
|
58000
58265
|
}
|
|
58001
58266
|
//#endregion
|
|
58002
|
-
//#region ../core/dist/completion-
|
|
58003
|
-
var
|
|
58267
|
+
//#region ../core/dist/completion-DRPTunQd.js
|
|
58268
|
+
var completion_DRPTunQd_exports = /* @__PURE__ */ __exportAll$1({
|
|
58004
58269
|
a: () => formatCapletList,
|
|
58005
58270
|
c: () => resolveCliConfigPaths,
|
|
58006
58271
|
i: () => trailingSpaceCompletionToken,
|
|
@@ -58122,7 +58387,8 @@ function allCaplets(config) {
|
|
|
58122
58387
|
...Object.values(config.openapiEndpoints),
|
|
58123
58388
|
...Object.values(config.graphqlEndpoints),
|
|
58124
58389
|
...Object.values(config.httpApis),
|
|
58125
|
-
...Object.values(config.cliTools)
|
|
58390
|
+
...Object.values(config.cliTools),
|
|
58391
|
+
...Object.values(config.capletSets)
|
|
58126
58392
|
];
|
|
58127
58393
|
}
|
|
58128
58394
|
function formatCapletList(rows, format = "plain") {
|
|
@@ -58174,15 +58440,15 @@ function formatSourceKind(kind) {
|
|
|
58174
58440
|
if (kind.startsWith("global")) return "global";
|
|
58175
58441
|
return kind;
|
|
58176
58442
|
}
|
|
58177
|
-
function resolveCliConfigPaths(envConfigPath, authDir) {
|
|
58443
|
+
function resolveCliConfigPaths(envConfigPath, projectConfigPath = resolveProjectConfigPath(), authDir) {
|
|
58178
58444
|
const configPath = resolveConfigPath(envConfigPath);
|
|
58179
58445
|
const effectiveAuthDir = authDir ?? DEFAULT_AUTH_DIR;
|
|
58180
58446
|
return {
|
|
58181
58447
|
userConfig: configPath,
|
|
58182
|
-
projectConfig:
|
|
58448
|
+
projectConfig: projectConfigPath,
|
|
58183
58449
|
userRoot: resolveCapletsRoot(configPath),
|
|
58184
58450
|
stateRoot: dirname(effectiveAuthDir),
|
|
58185
|
-
projectRoot:
|
|
58451
|
+
projectRoot: dirname(projectConfigPath),
|
|
58186
58452
|
authDir: effectiveAuthDir,
|
|
58187
58453
|
envConfig: envConfigPath ?? null
|
|
58188
58454
|
};
|
|
@@ -59863,7 +60129,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
|
|
|
59863
60129
|
values: [],
|
|
59864
60130
|
hasMore: false
|
|
59865
60131
|
} };
|
|
59866
|
-
var version$1 = "0.18.
|
|
60132
|
+
var version$1 = "0.18.5";
|
|
59867
60133
|
var CapletsMcpSession = class {
|
|
59868
60134
|
engine;
|
|
59869
60135
|
server;
|
|
@@ -63379,7 +63645,7 @@ function isUrlLike(value) {
|
|
|
63379
63645
|
return /^https?:\/\//i.test(value);
|
|
63380
63646
|
}
|
|
63381
63647
|
async function loginAuth(serverId, options) {
|
|
63382
|
-
const server = findAuthTarget(serverId, loadConfig(options.configPath));
|
|
63648
|
+
const server = findAuthTarget(serverId, options.config ?? loadConfig(options.configPath));
|
|
63383
63649
|
assertLoginTarget(server, serverId);
|
|
63384
63650
|
try {
|
|
63385
63651
|
const flowOptions = {
|
|
@@ -63401,40 +63667,64 @@ function logoutAuth(serverId, options) {
|
|
|
63401
63667
|
else options.writeOut(`No OAuth credentials found for \`${serverId}\`.\n`);
|
|
63402
63668
|
}
|
|
63403
63669
|
function logoutAuthResult(serverId, options) {
|
|
63404
|
-
assertLoginTarget(findAuthTarget(serverId, loadConfig(options.configPath)), serverId);
|
|
63670
|
+
assertLoginTarget(findAuthTarget(serverId, options.config ?? loadConfig(options.configPath)), serverId);
|
|
63405
63671
|
return {
|
|
63406
63672
|
server: serverId,
|
|
63407
63673
|
deleted: deleteTokenBundle(serverId, options.authDir)
|
|
63408
63674
|
};
|
|
63409
63675
|
}
|
|
63410
|
-
function
|
|
63411
|
-
|
|
63412
|
-
|
|
63413
|
-
|
|
63414
|
-
|
|
63415
|
-
|
|
63676
|
+
function listAuthRows(options) {
|
|
63677
|
+
return authRowsForTargets(authTargets(loadConfig(options.configPath)), options.authDir);
|
|
63678
|
+
}
|
|
63679
|
+
function listLocalAuthRows(options) {
|
|
63680
|
+
return authRowsForTargets(localAuthTargets(options), options.authDir);
|
|
63681
|
+
}
|
|
63682
|
+
function localAuthTargets(options) {
|
|
63683
|
+
return [...options.source === "project" ? [] : authTargetsForSource("global", options), ...options.source === "global" ? [] : authTargetsForSource("project", options)].filter((target) => !options.source || target.source === options.source);
|
|
63684
|
+
}
|
|
63685
|
+
function localAuthConfigForTarget(options) {
|
|
63686
|
+
assertLoginTarget(localAuthTargets(options).find((candidate) => candidate.server === options.serverId), options.serverId);
|
|
63687
|
+
return loadConfigForSource(options.source, options);
|
|
63688
|
+
}
|
|
63689
|
+
function authTargetsForSource(source, options) {
|
|
63690
|
+
try {
|
|
63691
|
+
return authTargets(loadConfigForSource(source, options)).map((target) => ({
|
|
63692
|
+
...target,
|
|
63693
|
+
source
|
|
63694
|
+
}));
|
|
63695
|
+
} catch (error) {
|
|
63696
|
+
if (error instanceof CapletsError && error.code === "CONFIG_NOT_FOUND") return [];
|
|
63697
|
+
throw error;
|
|
63416
63698
|
}
|
|
63417
|
-
options.writeOut(formatAuthRows(rows, format));
|
|
63418
63699
|
}
|
|
63419
|
-
function
|
|
63420
|
-
|
|
63421
|
-
|
|
63700
|
+
function loadConfigForSource(source, options) {
|
|
63701
|
+
if (source === "global") return loadGlobalConfig(options.configPath);
|
|
63702
|
+
return loadProjectConfig(options.projectConfigPath);
|
|
63703
|
+
}
|
|
63704
|
+
function authRowsForTargets(targets, authDir) {
|
|
63705
|
+
return targets.sort((left, right) => left.server.localeCompare(right.server)).map((server) => {
|
|
63706
|
+
const bundle = readTokenBundle(server.server, authDir);
|
|
63422
63707
|
const status = !bundle ? "missing" : isTokenBundleExpired(bundle) ? "expired" : "authenticated";
|
|
63423
63708
|
return {
|
|
63424
63709
|
server: server.server,
|
|
63425
63710
|
status,
|
|
63426
63711
|
...bundle?.expiresAt ? { expiresAt: bundle.expiresAt } : {},
|
|
63427
|
-
...bundle?.scope ? { scope: bundle.scope } : {}
|
|
63712
|
+
...bundle?.scope ? { scope: bundle.scope } : {},
|
|
63713
|
+
...server.source ? { source: server.source } : {}
|
|
63428
63714
|
};
|
|
63429
63715
|
});
|
|
63430
63716
|
}
|
|
63431
63717
|
function formatAuthRows(rows, format) {
|
|
63432
|
-
if (rows.length === 0) return format === "markdown" ? "## OAuth credentials\n\nNo configured
|
|
63718
|
+
if (rows.length === 0) return format === "markdown" ? "## OAuth credentials\n\nNo configured OAuth servers found.\n" : "No configured OAuth servers found.\n";
|
|
63433
63719
|
let output = "";
|
|
63434
63720
|
if (format === "markdown") output += "## OAuth credentials\n\n";
|
|
63435
63721
|
else output += "OAuth credentials\n\n";
|
|
63436
63722
|
for (const row of rows) {
|
|
63437
|
-
const details = [
|
|
63723
|
+
const details = [
|
|
63724
|
+
row.source ? `source ${row.source}` : void 0,
|
|
63725
|
+
row.expiresAt ? `expires ${row.expiresAt}` : void 0,
|
|
63726
|
+
row.scope ? `scope ${row.scope}` : void 0
|
|
63727
|
+
].filter(Boolean).join("; ");
|
|
63438
63728
|
if (format === "markdown") {
|
|
63439
63729
|
output += `- \`${row.server}\` — ${row.status}${details ? ` (${details})` : ""}\n`;
|
|
63440
63730
|
continue;
|
|
@@ -63442,6 +63732,7 @@ function formatAuthRows(rows, format) {
|
|
|
63442
63732
|
output += [
|
|
63443
63733
|
row.server,
|
|
63444
63734
|
` Status: ${row.status}`,
|
|
63735
|
+
...row.source ? [` Source: ${row.source}`] : [],
|
|
63445
63736
|
...row.expiresAt ? [` Expires: ${row.expiresAt}`] : [],
|
|
63446
63737
|
...row.scope ? [` Scope: ${row.scope}`] : []
|
|
63447
63738
|
].join("\n") + "\n\n";
|
|
@@ -67942,15 +68233,29 @@ function createProgram(io = {}) {
|
|
|
67942
68233
|
const completionWords = normalizeCompletionWords(words);
|
|
67943
68234
|
let suggestions = [];
|
|
67944
68235
|
try {
|
|
67945
|
-
|
|
67946
|
-
|
|
67947
|
-
|
|
67948
|
-
|
|
68236
|
+
if (remote) {
|
|
68237
|
+
const localOverlay = loadLocalOverlayForCli(io, () => {});
|
|
68238
|
+
const localSuggestions = await completeCliWordsLocally(completionWords, {
|
|
68239
|
+
...configPath ? { configPath } : {},
|
|
68240
|
+
projectConfigPath: envProjectConfigPath(env),
|
|
68241
|
+
...io.authDir ? { authDir: io.authDir } : {},
|
|
68242
|
+
config: localOverlay.config
|
|
68243
|
+
});
|
|
68244
|
+
if (localShadowedCompletionTarget(completionWords, localOverlay.config)) suggestions = localSuggestions;
|
|
68245
|
+
else suggestions = mergeCompletionSuggestions(localSuggestions, await remote.request("complete_cli", {
|
|
68246
|
+
shell,
|
|
68247
|
+
words: completionWords
|
|
68248
|
+
}));
|
|
68249
|
+
} else suggestions = await completeCliWordsLocally(completionWords, {
|
|
67949
68250
|
...configPath ? { configPath } : {},
|
|
68251
|
+
projectConfigPath: envProjectConfigPath(env),
|
|
67950
68252
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
67951
68253
|
});
|
|
67952
68254
|
} catch {
|
|
67953
|
-
suggestions = remote ? [] : await completeCliWords(completionWords,
|
|
68255
|
+
suggestions = remote ? [] : await completeCliWords(completionWords, {
|
|
68256
|
+
...configPath ? { configPath } : {},
|
|
68257
|
+
projectConfigPath: envProjectConfigPath(env)
|
|
68258
|
+
});
|
|
67954
68259
|
}
|
|
67955
68260
|
if (suggestions.length > 0) writeOut(`${suggestions.join("\n")}\n`);
|
|
67956
68261
|
});
|
|
@@ -67962,23 +68267,26 @@ function createProgram(io = {}) {
|
|
|
67962
68267
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
67963
68268
|
}, writeErr)))(resolved);
|
|
67964
68269
|
});
|
|
67965
|
-
program.command(cliCommands.init).description("Create a starter Caplets config file.").option("--force", "overwrite an existing config file").action(async (options) => {
|
|
67966
|
-
const
|
|
67967
|
-
if (remote) {
|
|
67968
|
-
writeOut(`Created remote Caplets config at ${(await
|
|
68270
|
+
program.command(cliCommands.init).description("Create a starter Caplets config file.").option("--project", "create the project Caplets config").option("-g, --global", "create the user Caplets config").option("--remote", "create the remote Caplets config").option("--force", "overwrite an existing config file").action(async (options) => {
|
|
68271
|
+
const target = parseMutationTarget(options);
|
|
68272
|
+
if (target === "remote") {
|
|
68273
|
+
writeOut(`Created remote Caplets config at ${(await requireRemoteClientForTarget(io).request("init", { force: Boolean(options.force) })).path}\n`);
|
|
67969
68274
|
return;
|
|
67970
68275
|
}
|
|
67971
|
-
const
|
|
67972
|
-
|
|
67973
|
-
...configPath ? { path: configPath } : {},
|
|
68276
|
+
const path = initConfig({
|
|
68277
|
+
path: target === "global" ? resolveConfigPath(currentConfigPath()) : envProjectConfigPath(env),
|
|
67974
68278
|
force: Boolean(options.force)
|
|
67975
|
-
})
|
|
68279
|
+
});
|
|
68280
|
+
writeOut(`Created ${localMutationTargetLabel(target, io)}Caplets config at ${path}\n`);
|
|
67976
68281
|
});
|
|
67977
68282
|
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) => {
|
|
67978
68283
|
const includeDisabled = Boolean(options.all);
|
|
67979
68284
|
const remote = remoteClientForCli(io);
|
|
67980
68285
|
if (remote) {
|
|
67981
|
-
const rows = await remote.request("list", { includeDisabled })
|
|
68286
|
+
const rows = mergeRemoteAndLocalRows(await remote.request("list", { includeDisabled }), tryLoadLocalOverlayForCli(io, writeErr), {
|
|
68287
|
+
includeDisabled,
|
|
68288
|
+
writeErr
|
|
68289
|
+
});
|
|
67982
68290
|
if (options.json || options.format === "json") {
|
|
67983
68291
|
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
67984
68292
|
return;
|
|
@@ -67986,18 +68294,17 @@ function createProgram(io = {}) {
|
|
|
67986
68294
|
writeOut(formatCapletList(rows, options.format ?? "plain"));
|
|
67987
68295
|
return;
|
|
67988
68296
|
}
|
|
67989
|
-
const rows = listCaplets(loadConfigWithSources(currentConfigPath()), { includeDisabled });
|
|
68297
|
+
const rows = listCaplets(loadConfigWithSources(currentConfigPath(), envProjectConfigPath(env)), { includeDisabled });
|
|
67990
68298
|
if (options.json || options.format === "json") {
|
|
67991
68299
|
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
67992
68300
|
return;
|
|
67993
68301
|
}
|
|
67994
68302
|
writeOut(formatCapletList(rows, options.format ?? "plain"));
|
|
67995
68303
|
});
|
|
67996
|
-
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) => {
|
|
67997
|
-
const
|
|
67998
|
-
if (remote) {
|
|
67999
|
-
|
|
68000
|
-
const result = await remote.request("install", {
|
|
68304
|
+
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("--project", "install to the project Caplets root").option("-g, --global", "install to the user Caplets root").option("--remote", "install through remote control").option("--force", "overwrite installed Caplets").action(async (repo, capletIds, options) => {
|
|
68305
|
+
const target = parseMutationTarget(options);
|
|
68306
|
+
if (target === "remote") {
|
|
68307
|
+
const result = await requireRemoteClientForTarget(io).request("install", {
|
|
68001
68308
|
repo,
|
|
68002
68309
|
capletIds,
|
|
68003
68310
|
force: Boolean(options.force)
|
|
@@ -68008,15 +68315,15 @@ function createProgram(io = {}) {
|
|
|
68008
68315
|
const result = installCaplets(repo, {
|
|
68009
68316
|
capletIds,
|
|
68010
68317
|
force: Boolean(options.force),
|
|
68011
|
-
destinationRoot:
|
|
68318
|
+
destinationRoot: target === "global" ? resolveCapletsRoot(resolveConfigPath(currentConfigPath())) : envProjectCapletsRoot(env)
|
|
68012
68319
|
});
|
|
68013
|
-
for (const caplet of result.installed) writeOut(`Installed ${caplet.id} to ${caplet.destination}\n`);
|
|
68320
|
+
for (const caplet of result.installed) writeOut(`Installed ${caplet.id} to ${localMutationTargetLabel(target, io)}${caplet.destination}\n`);
|
|
68014
68321
|
});
|
|
68015
68322
|
const add = program.command(cliCommands.add).description("Add generated Caplet files.");
|
|
68016
|
-
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) => {
|
|
68017
|
-
const
|
|
68018
|
-
if (remote) {
|
|
68019
|
-
writeAddResult(writeOut, "CLI", await
|
|
68323
|
+
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("--project", "write to the project Caplets root").option("-g, --global", "write to the user Caplets root").option("--remote", "add through remote control").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) => {
|
|
68324
|
+
const target = parseMutationTarget(options);
|
|
68325
|
+
if (target === "remote") {
|
|
68326
|
+
writeAddResult(writeOut, "CLI", await requireRemoteClientForTarget(io).request("add", {
|
|
68020
68327
|
kind: "cli",
|
|
68021
68328
|
id,
|
|
68022
68329
|
options: remoteAddOptions(options)
|
|
@@ -68025,73 +68332,77 @@ function createProgram(io = {}) {
|
|
|
68025
68332
|
}
|
|
68026
68333
|
const result = addCliCaplet(id, {
|
|
68027
68334
|
...options,
|
|
68028
|
-
destinationRoot:
|
|
68335
|
+
destinationRoot: target === "global" ? resolveCapletsRoot(resolveConfigPath(currentConfigPath())) : envProjectCapletsRoot(env)
|
|
68029
68336
|
});
|
|
68030
68337
|
if (result.path) {
|
|
68031
|
-
writeOut(`Wrote CLI Caplet to ${result.path}\n`);
|
|
68338
|
+
writeOut(`Wrote ${localMutationTargetLabel(target, io)}CLI Caplet to ${result.path}\n`);
|
|
68032
68339
|
return;
|
|
68033
68340
|
}
|
|
68034
68341
|
writeOut(result.text);
|
|
68035
68342
|
});
|
|
68036
|
-
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) => {
|
|
68037
|
-
const
|
|
68038
|
-
if (remote) {
|
|
68039
|
-
writeAddResult(writeOut, "MCP", await
|
|
68343
|
+
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("--project", "write to the project Caplets root").option("-g, --global", "write to the user Caplets root").option("--remote", "add through remote control").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) => {
|
|
68344
|
+
const target = parseMutationTarget(options);
|
|
68345
|
+
if (target === "remote") {
|
|
68346
|
+
writeAddResult(writeOut, "MCP", await requireRemoteClientForTarget(io).request("add", {
|
|
68040
68347
|
kind: "mcp",
|
|
68041
68348
|
id,
|
|
68042
68349
|
options: remoteAddOptions(options)
|
|
68043
68350
|
}));
|
|
68044
68351
|
return;
|
|
68045
68352
|
}
|
|
68046
|
-
|
|
68353
|
+
const result = addMcpCaplet(id, {
|
|
68047
68354
|
...options,
|
|
68048
|
-
destinationRoot: addDestinationRoot(
|
|
68049
|
-
})
|
|
68355
|
+
destinationRoot: addDestinationRoot(target, currentConfigPath(), env)
|
|
68356
|
+
});
|
|
68357
|
+
writeAddResult(writeOut, `${localMutationTargetLabel(target, io)}MCP`, result);
|
|
68050
68358
|
});
|
|
68051
|
-
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) => {
|
|
68052
|
-
const
|
|
68053
|
-
if (remote) {
|
|
68054
|
-
writeAddResult(writeOut, "OpenAPI", await
|
|
68359
|
+
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("--project", "write to the project Caplets root").option("-g, --global", "write to the user Caplets root").option("--remote", "add through remote control").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) => {
|
|
68360
|
+
const target = parseMutationTarget(options);
|
|
68361
|
+
if (target === "remote") {
|
|
68362
|
+
writeAddResult(writeOut, "OpenAPI", await requireRemoteClientForTarget(io).request("add", {
|
|
68055
68363
|
kind: "openapi",
|
|
68056
68364
|
id,
|
|
68057
68365
|
options: remoteAddOptions(options)
|
|
68058
68366
|
}));
|
|
68059
68367
|
return;
|
|
68060
68368
|
}
|
|
68061
|
-
|
|
68369
|
+
const result = addOpenApiCaplet(id, {
|
|
68062
68370
|
...options,
|
|
68063
|
-
destinationRoot: addDestinationRoot(
|
|
68064
|
-
})
|
|
68371
|
+
destinationRoot: addDestinationRoot(target, currentConfigPath(), env)
|
|
68372
|
+
});
|
|
68373
|
+
writeAddResult(writeOut, `${localMutationTargetLabel(target, io)}OpenAPI`, result);
|
|
68065
68374
|
});
|
|
68066
|
-
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) => {
|
|
68067
|
-
const
|
|
68068
|
-
if (remote) {
|
|
68069
|
-
writeAddResult(writeOut, "GraphQL", await
|
|
68375
|
+
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("--project", "write to the project Caplets root").option("-g, --global", "write to the user Caplets root").option("--remote", "add through remote control").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) => {
|
|
68376
|
+
const target = parseMutationTarget(options);
|
|
68377
|
+
if (target === "remote") {
|
|
68378
|
+
writeAddResult(writeOut, "GraphQL", await requireRemoteClientForTarget(io).request("add", {
|
|
68070
68379
|
kind: "graphql",
|
|
68071
68380
|
id,
|
|
68072
68381
|
options: remoteAddOptions(options)
|
|
68073
68382
|
}));
|
|
68074
68383
|
return;
|
|
68075
68384
|
}
|
|
68076
|
-
|
|
68385
|
+
const result = addGraphqlCaplet(id, {
|
|
68077
68386
|
...options,
|
|
68078
|
-
destinationRoot: addDestinationRoot(
|
|
68079
|
-
})
|
|
68387
|
+
destinationRoot: addDestinationRoot(target, currentConfigPath(), env)
|
|
68388
|
+
});
|
|
68389
|
+
writeAddResult(writeOut, `${localMutationTargetLabel(target, io)}GraphQL`, result);
|
|
68080
68390
|
});
|
|
68081
|
-
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) => {
|
|
68082
|
-
const
|
|
68083
|
-
if (remote) {
|
|
68084
|
-
writeAddResult(writeOut, "HTTP", await
|
|
68391
|
+
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("--project", "write to the project Caplets root").option("-g, --global", "write to the user Caplets root").option("--remote", "add through remote control").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) => {
|
|
68392
|
+
const target = parseMutationTarget(options);
|
|
68393
|
+
if (target === "remote") {
|
|
68394
|
+
writeAddResult(writeOut, "HTTP", await requireRemoteClientForTarget(io).request("add", {
|
|
68085
68395
|
kind: "http",
|
|
68086
68396
|
id,
|
|
68087
68397
|
options: remoteAddOptions(options)
|
|
68088
68398
|
}));
|
|
68089
68399
|
return;
|
|
68090
68400
|
}
|
|
68091
|
-
|
|
68401
|
+
const result = addHttpCaplet(id, {
|
|
68092
68402
|
...options,
|
|
68093
|
-
destinationRoot: addDestinationRoot(
|
|
68094
|
-
})
|
|
68403
|
+
destinationRoot: addDestinationRoot(target, currentConfigPath(), env)
|
|
68404
|
+
});
|
|
68405
|
+
writeAddResult(writeOut, `${localMutationTargetLabel(target, io)}HTTP`, result);
|
|
68095
68406
|
});
|
|
68096
68407
|
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) => {
|
|
68097
68408
|
await executeOperation(caplet, { operation: "get_caplet" }, {
|
|
@@ -68293,7 +68604,7 @@ function createProgram(io = {}) {
|
|
|
68293
68604
|
writeOut(`${resolveConfigPath(currentConfigPath())}\n`);
|
|
68294
68605
|
});
|
|
68295
68606
|
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) => {
|
|
68296
|
-
const paths = resolveCliConfigPaths(currentConfigPath(), io.authDir);
|
|
68607
|
+
const paths = resolveCliConfigPaths(currentConfigPath(), envProjectConfigPath(env), io.authDir);
|
|
68297
68608
|
if (options.json || options.format === "json") {
|
|
68298
68609
|
writeOut(`${JSON.stringify(paths, null, 2)}\n`);
|
|
68299
68610
|
return;
|
|
@@ -68301,60 +68612,59 @@ function createProgram(io = {}) {
|
|
|
68301
68612
|
writeOut(formatConfigPaths(paths, options.format ?? "plain"));
|
|
68302
68613
|
});
|
|
68303
68614
|
const auth = program.command(cliCommands.auth).description("Manage OAuth credentials for remote servers.");
|
|
68304
|
-
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) => {
|
|
68305
|
-
const
|
|
68306
|
-
if (remote) {
|
|
68307
|
-
|
|
68308
|
-
if (started.authorizationUrl) {
|
|
68309
|
-
writeOut(`Open this URL to authorize ${serverId}:\n${started.authorizationUrl}\n`);
|
|
68310
|
-
if (options.open !== false) await openBrowser(started.authorizationUrl);
|
|
68311
|
-
writeOut("Complete authentication in your browser. The server callback will store credentials.\n");
|
|
68312
|
-
return;
|
|
68313
|
-
}
|
|
68314
|
-
if (started.authenticated) writeOut(`Authenticated \`${serverId}\`.\n`);
|
|
68615
|
+
auth.command("login").description("Authenticate a configured remote OAuth server.").argument("<server>", "configured server ID").option("--project", "authenticate using the project Caplets config").option("-g, --global", "authenticate using the user Caplets config").option("--remote", "authenticate using the remote server auth store").option("--no-open", "print the authorization URL without opening a browser").action(async (serverId, options) => {
|
|
68616
|
+
const target = await resolveAuthTarget(serverId, options, io);
|
|
68617
|
+
if (target === "remote") {
|
|
68618
|
+
await remoteAuthLogin(requireRemoteClientForTarget(io), serverId, options.open !== false, writeOut);
|
|
68315
68619
|
return;
|
|
68316
68620
|
}
|
|
68317
68621
|
const configPath = currentConfigPath();
|
|
68622
|
+
const projectConfigPath = envProjectConfigPath(env);
|
|
68318
68623
|
await loginAuth(serverId, {
|
|
68319
68624
|
noOpen: options.open === false,
|
|
68320
68625
|
writeOut,
|
|
68321
68626
|
writeErr,
|
|
68322
68627
|
...configPath ? { configPath } : {},
|
|
68628
|
+
...projectConfigPath ? { projectConfigPath } : {},
|
|
68629
|
+
config: localAuthConfigForTarget({
|
|
68630
|
+
serverId,
|
|
68631
|
+
...configPath ? { configPath } : {},
|
|
68632
|
+
...projectConfigPath ? { projectConfigPath } : {},
|
|
68633
|
+
source: target
|
|
68634
|
+
}),
|
|
68323
68635
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
68324
68636
|
});
|
|
68325
68637
|
});
|
|
68326
|
-
auth.command("logout").description("Delete stored OAuth credentials for a server.").argument("<server>", "configured server ID").action(async (serverId) => {
|
|
68327
|
-
const
|
|
68328
|
-
if (remote) {
|
|
68329
|
-
writeOut((await
|
|
68638
|
+
auth.command("logout").description("Delete stored OAuth credentials for a server.").argument("<server>", "configured server ID").option("--project", "delete credentials for the project Caplets config target").option("-g, --global", "delete credentials for the user Caplets config target").option("--remote", "delete credentials from the remote server auth store").action(async (serverId, options) => {
|
|
68639
|
+
const target = await resolveAuthTarget(serverId, options, io);
|
|
68640
|
+
if (target === "remote") {
|
|
68641
|
+
writeOut((await requireRemoteClientForTarget(io).request("auth_logout", { server: serverId })).deleted ? `Deleted remote OAuth credentials for \`${serverId}\`.\n` : `No remote OAuth credentials found for \`${serverId}\`.\n`);
|
|
68330
68642
|
return;
|
|
68331
68643
|
}
|
|
68332
68644
|
const configPath = currentConfigPath();
|
|
68645
|
+
const projectConfigPath = envProjectConfigPath(env);
|
|
68333
68646
|
logoutAuth(serverId, {
|
|
68334
68647
|
writeOut,
|
|
68335
68648
|
...configPath ? { configPath } : {},
|
|
68649
|
+
config: localAuthConfigForTarget({
|
|
68650
|
+
serverId,
|
|
68651
|
+
...configPath ? { configPath } : {},
|
|
68652
|
+
...projectConfigPath ? { projectConfigPath } : {},
|
|
68653
|
+
source: target
|
|
68654
|
+
}),
|
|
68336
68655
|
...io.authDir ? { authDir: io.authDir } : {}
|
|
68337
68656
|
});
|
|
68338
68657
|
});
|
|
68339
|
-
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) => {
|
|
68658
|
+
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).option("--project", "list auth targets from the project Caplets config").option("-g, --global", "list auth targets from the user Caplets config").option("--remote", "list auth targets from the remote server auth store").action(async (options) => {
|
|
68340
68659
|
const configPath = currentConfigPath();
|
|
68660
|
+
const projectConfigPath = envProjectConfigPath(env);
|
|
68341
68661
|
const format = options.json || options.format === "json" ? "json" : options.format ?? "plain";
|
|
68342
|
-
const
|
|
68343
|
-
if (
|
|
68344
|
-
|
|
68345
|
-
if (format === "json") {
|
|
68346
|
-
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
68347
|
-
return;
|
|
68348
|
-
}
|
|
68349
|
-
writeOut(formatAuthRows(rows, format));
|
|
68662
|
+
const rows = await authListRowsForCli(parseAuthFlagTarget(options), io, configPath, projectConfigPath);
|
|
68663
|
+
if (format === "json") {
|
|
68664
|
+
writeOut(`${JSON.stringify(rows, null, 2)}\n`);
|
|
68350
68665
|
return;
|
|
68351
68666
|
}
|
|
68352
|
-
|
|
68353
|
-
writeOut,
|
|
68354
|
-
format,
|
|
68355
|
-
...configPath ? { configPath } : {},
|
|
68356
|
-
...io.authDir ? { authDir: io.authDir } : {}
|
|
68357
|
-
});
|
|
68667
|
+
writeOut(formatAuthRows(rows, format));
|
|
68358
68668
|
});
|
|
68359
68669
|
return program;
|
|
68360
68670
|
}
|
|
@@ -68398,12 +68708,90 @@ function remoteCommandForOperation(operation) {
|
|
|
68398
68708
|
}
|
|
68399
68709
|
}
|
|
68400
68710
|
function remoteAddOptions(options) {
|
|
68401
|
-
const { output, print, global, destinationRoot, ...remoteOptions } = options;
|
|
68402
|
-
if (global) throw new CapletsError("REQUEST_INVALID", "--global is not supported in remote mode; the server controls the add destination.");
|
|
68711
|
+
const { output, print, global, project, remote, destinationRoot, ...remoteOptions } = options;
|
|
68403
68712
|
if (print) throw new CapletsError("REQUEST_INVALID", "--print is not supported in remote mode; the server controls add output.");
|
|
68404
68713
|
if (output !== void 0) throw new CapletsError("REQUEST_INVALID", "--output is not supported in remote mode; the server controls the add destination.");
|
|
68405
68714
|
return remoteOptions;
|
|
68406
68715
|
}
|
|
68716
|
+
function parseMutationTarget(options) {
|
|
68717
|
+
const selected = [
|
|
68718
|
+
options.project ? "--project" : void 0,
|
|
68719
|
+
options.global ? "--global" : void 0,
|
|
68720
|
+
options.remote ? "--remote" : void 0
|
|
68721
|
+
].filter((value) => value !== void 0);
|
|
68722
|
+
if (selected.length > 1) throw new CapletsError("REQUEST_INVALID", `Cannot combine mutation target flags: ${selected.join(", ")}`);
|
|
68723
|
+
if (options.global) return "global";
|
|
68724
|
+
if (options.remote) return "remote";
|
|
68725
|
+
return "project";
|
|
68726
|
+
}
|
|
68727
|
+
function localMutationTargetLabel(target, io) {
|
|
68728
|
+
return remoteClientForCli(io) ? `${target} ` : "";
|
|
68729
|
+
}
|
|
68730
|
+
function parseAuthFlagTarget(options) {
|
|
68731
|
+
const selected = [
|
|
68732
|
+
options.project ? "--project" : void 0,
|
|
68733
|
+
options.global ? "--global" : void 0,
|
|
68734
|
+
options.remote ? "--remote" : void 0
|
|
68735
|
+
].filter((value) => value !== void 0);
|
|
68736
|
+
if (selected.length > 1) throw new CapletsError("REQUEST_INVALID", `Cannot combine auth target flags: ${selected.join(", ")}`);
|
|
68737
|
+
if (options.project) return "project";
|
|
68738
|
+
if (options.global) return "global";
|
|
68739
|
+
if (options.remote) return "remote";
|
|
68740
|
+
}
|
|
68741
|
+
async function resolveAuthTarget(serverId, options, io) {
|
|
68742
|
+
const explicit = parseAuthFlagTarget(options);
|
|
68743
|
+
if (explicit) return explicit;
|
|
68744
|
+
const env = io.env ?? process.env;
|
|
68745
|
+
const configPath = envConfigPath(env);
|
|
68746
|
+
const projectConfigPath = envProjectConfigPath(env);
|
|
68747
|
+
const matches = localAuthTargets({
|
|
68748
|
+
...configPath ? { configPath } : {},
|
|
68749
|
+
...projectConfigPath ? { projectConfigPath } : {}
|
|
68750
|
+
}).filter((target) => target.server === serverId).map((target) => target.source);
|
|
68751
|
+
const remote = remoteClientForCli(io);
|
|
68752
|
+
if (remote) {
|
|
68753
|
+
if (matches.length === 0) matches.push("remote");
|
|
68754
|
+
else if ((await remoteAuthRows(remote)).some((row) => row.server === serverId)) matches.push("remote");
|
|
68755
|
+
}
|
|
68756
|
+
const unique = [...new Set(matches)];
|
|
68757
|
+
if (unique.length === 1) return unique[0];
|
|
68758
|
+
if (unique.length > 1) throw new CapletsError("REQUEST_INVALID", `Auth target \`${serverId}\` exists in multiple scopes. Pass --project, --global, or --remote.`);
|
|
68759
|
+
throw new CapletsError("SERVER_NOT_FOUND", `Server ${serverId} is not configured for OAuth`);
|
|
68760
|
+
}
|
|
68761
|
+
async function authListRowsForCli(target, io, configPath, projectConfigPath) {
|
|
68762
|
+
if (target === "remote") return remoteAuthRows(requireRemoteClientForTarget(io));
|
|
68763
|
+
const localRows = listLocalAuthRows({
|
|
68764
|
+
...configPath ? { configPath } : {},
|
|
68765
|
+
...projectConfigPath ? { projectConfigPath } : {},
|
|
68766
|
+
...io.authDir ? { authDir: io.authDir } : {},
|
|
68767
|
+
...target ? { source: target } : {}
|
|
68768
|
+
});
|
|
68769
|
+
if (target) return localRows;
|
|
68770
|
+
const remote = remoteClientForCli(io);
|
|
68771
|
+
if (!remote) return localRows;
|
|
68772
|
+
return [...localRows, ...await remoteAuthRows(remote)].sort((left, right) => left.server.localeCompare(right.server));
|
|
68773
|
+
}
|
|
68774
|
+
async function remoteAuthRows(remote) {
|
|
68775
|
+
return (await remote.request("auth_list", {})).map((row) => ({
|
|
68776
|
+
...row,
|
|
68777
|
+
source: "remote"
|
|
68778
|
+
}));
|
|
68779
|
+
}
|
|
68780
|
+
async function remoteAuthLogin(remote, serverId, open, writeOut) {
|
|
68781
|
+
const started = await remote.request("auth_login_start", { server: serverId });
|
|
68782
|
+
if (started.authorizationUrl) {
|
|
68783
|
+
writeOut(`Open this URL to authorize ${serverId}:\n${started.authorizationUrl}\n`);
|
|
68784
|
+
if (open) await openBrowser(started.authorizationUrl);
|
|
68785
|
+
writeOut("Complete authentication in your browser. The server callback will store credentials.\n");
|
|
68786
|
+
return;
|
|
68787
|
+
}
|
|
68788
|
+
if (started.authenticated) writeOut(`Authenticated \`${serverId}\`.\n`);
|
|
68789
|
+
}
|
|
68790
|
+
function requireRemoteClientForTarget(io) {
|
|
68791
|
+
const remote = remoteClientForCli(io);
|
|
68792
|
+
if (!remote) throw new CapletsError("REQUEST_INVALID", "--remote requires CAPLETS_MODE=remote and CAPLETS_SERVER_URL");
|
|
68793
|
+
return remote;
|
|
68794
|
+
}
|
|
68407
68795
|
function collect(value, previous) {
|
|
68408
68796
|
previous.push(value);
|
|
68409
68797
|
return previous;
|
|
@@ -68440,8 +68828,10 @@ function parseQualifiedTarget(capletOrTarget, toolArgument) {
|
|
|
68440
68828
|
async function completeCliWordsLocally(words, options) {
|
|
68441
68829
|
const engine = new CapletsEngine({
|
|
68442
68830
|
...options.configPath ? { configPath: options.configPath } : {},
|
|
68831
|
+
...options.projectConfigPath ? { projectConfigPath: options.projectConfigPath } : {},
|
|
68443
68832
|
...options.authDir ? { authDir: options.authDir } : {},
|
|
68444
|
-
watch: false
|
|
68833
|
+
watch: false,
|
|
68834
|
+
...options.config ? { configLoader: () => options.config } : {}
|
|
68445
68835
|
});
|
|
68446
68836
|
try {
|
|
68447
68837
|
return await engine.completeCliWords(words);
|
|
@@ -68449,6 +68839,34 @@ async function completeCliWordsLocally(words, options) {
|
|
|
68449
68839
|
await engine.close();
|
|
68450
68840
|
}
|
|
68451
68841
|
}
|
|
68842
|
+
function mergeCompletionSuggestions(...groups) {
|
|
68843
|
+
return [...new Set(groups.flat())];
|
|
68844
|
+
}
|
|
68845
|
+
function localShadowedCompletionTarget(words, config) {
|
|
68846
|
+
const command = words[0];
|
|
68847
|
+
const target = words[1];
|
|
68848
|
+
if (!command || !target || target.startsWith("-")) return;
|
|
68849
|
+
const qualifiedCommands = new Set([
|
|
68850
|
+
cliCommands.getTool,
|
|
68851
|
+
cliCommands.callTool,
|
|
68852
|
+
cliCommands.getPrompt
|
|
68853
|
+
]);
|
|
68854
|
+
const capletCommands = new Set([
|
|
68855
|
+
cliCommands.getCaplet,
|
|
68856
|
+
cliCommands.checkBackend,
|
|
68857
|
+
cliCommands.listTools,
|
|
68858
|
+
cliCommands.searchTools,
|
|
68859
|
+
cliCommands.listResources,
|
|
68860
|
+
cliCommands.searchResources,
|
|
68861
|
+
cliCommands.listResourceTemplates,
|
|
68862
|
+
cliCommands.readResource,
|
|
68863
|
+
cliCommands.listPrompts,
|
|
68864
|
+
cliCommands.searchPrompts,
|
|
68865
|
+
cliCommands.complete
|
|
68866
|
+
]);
|
|
68867
|
+
const caplet = qualifiedCommands.has(command) ? target.slice(0, target.includes(".") ? target.indexOf(".") : target.length) : capletCommands.has(command) ? target : void 0;
|
|
68868
|
+
return caplet && hasEnabledCaplet(config, caplet) ? caplet : void 0;
|
|
68869
|
+
}
|
|
68452
68870
|
function parseCallToolArgs(value) {
|
|
68453
68871
|
if (value === void 0) return {};
|
|
68454
68872
|
let parsed;
|
|
@@ -68489,6 +68907,11 @@ function isPlainObject(value) {
|
|
|
68489
68907
|
async function executeOperation(caplet, request, io) {
|
|
68490
68908
|
const command = remoteCommandForOperation(request.operation);
|
|
68491
68909
|
if (io.remote && command) {
|
|
68910
|
+
const localOverlay = tryLoadLocalOverlayForCli(io, io.writeErr);
|
|
68911
|
+
if (localOverlay && hasEnabledCaplet(localOverlay.config, caplet)) {
|
|
68912
|
+
await executeLocalOperation(caplet, request, io, localOverlay.config);
|
|
68913
|
+
return;
|
|
68914
|
+
}
|
|
68492
68915
|
const result = await io.remote.request(command, {
|
|
68493
68916
|
caplet,
|
|
68494
68917
|
request
|
|
@@ -68501,12 +68924,119 @@ async function executeOperation(caplet, request, io) {
|
|
|
68501
68924
|
if (isPlainObject(result) && result.isError === true) io.setExitCode(1);
|
|
68502
68925
|
return;
|
|
68503
68926
|
}
|
|
68927
|
+
await executeLocalOperation(caplet, request, io);
|
|
68928
|
+
}
|
|
68929
|
+
function loadLocalOverlayForCli(io, writeErr) {
|
|
68930
|
+
const env = io.env ?? process.env;
|
|
68931
|
+
const overlay = loadLocalOverlayConfigWithSources(resolveConfigPath(envConfigPath(env)), envProjectConfigPath(env));
|
|
68932
|
+
for (const warning of overlay.warnings) writeErr(`Warning: ${warning.kind} at ${warning.path}: ${warning.message}\n`);
|
|
68933
|
+
return overlay;
|
|
68934
|
+
}
|
|
68935
|
+
function tryLoadLocalOverlayForCli(io, writeErr) {
|
|
68936
|
+
try {
|
|
68937
|
+
return loadLocalOverlayForCli(io, writeErr);
|
|
68938
|
+
} catch (error) {
|
|
68939
|
+
writeErr(`Warning: Could not load local Caplets overlay: ${formatErrorMessage(error)}\n`);
|
|
68940
|
+
return loadPartialLocalOverlayForCli(io, writeErr);
|
|
68941
|
+
}
|
|
68942
|
+
}
|
|
68943
|
+
function loadPartialLocalOverlayForCli(io, writeErr) {
|
|
68944
|
+
const env = io.env ?? process.env;
|
|
68945
|
+
const configPath = resolveConfigPath(envConfigPath(env));
|
|
68946
|
+
const projectConfigPath = envProjectConfigPath(env);
|
|
68947
|
+
const absentProjectPath = join(dirname(configPath), ".caplets-overlay-recovery", "config.json");
|
|
68948
|
+
const absentGlobalPath = join(dirname(projectConfigPath), ".caplets-overlay-recovery", "config.json");
|
|
68949
|
+
const globalOverlay = tryLoadPartialOverlayLayer("global", configPath, absentProjectPath, writeErr);
|
|
68950
|
+
const projectOverlay = tryLoadPartialOverlayLayer("project", absentGlobalPath, projectConfigPath, writeErr);
|
|
68951
|
+
if (!globalOverlay) return projectOverlay;
|
|
68952
|
+
if (!projectOverlay) return globalOverlay;
|
|
68953
|
+
return mergePartialLocalOverlays(globalOverlay, projectOverlay);
|
|
68954
|
+
}
|
|
68955
|
+
function tryLoadPartialOverlayLayer(label, configPath, projectConfigPath, writeErr) {
|
|
68956
|
+
try {
|
|
68957
|
+
const overlay = loadLocalOverlayConfigWithSources(configPath, projectConfigPath);
|
|
68958
|
+
for (const warning of overlay.warnings) writeErr(`Warning: ${warning.kind} at ${warning.path}: ${warning.message}\n`);
|
|
68959
|
+
return overlay;
|
|
68960
|
+
} catch (error) {
|
|
68961
|
+
writeErr(`Warning: Could not load ${label} Caplets overlay: ${formatErrorMessage(error)}\n`);
|
|
68962
|
+
return;
|
|
68963
|
+
}
|
|
68964
|
+
}
|
|
68965
|
+
function mergePartialLocalOverlays(globalOverlay, projectOverlay) {
|
|
68966
|
+
const config = { ...globalOverlay.config };
|
|
68967
|
+
const sources = { ...globalOverlay.sources };
|
|
68968
|
+
const shadows = { ...globalOverlay.shadows };
|
|
68969
|
+
for (const kind of capletConfigKinds) config[kind] = { ...globalOverlay.config[kind] };
|
|
68970
|
+
for (const kind of capletConfigKinds) for (const id of Object.keys(projectOverlay.config[kind])) {
|
|
68971
|
+
removeCapletFromPartialOverlay(config, sources, shadows, id);
|
|
68972
|
+
config[kind][id] = projectOverlay.config[kind][id];
|
|
68973
|
+
}
|
|
68974
|
+
for (const [id, source] of Object.entries(projectOverlay.sources)) sources[id] = source;
|
|
68975
|
+
for (const [id, shadowedSources] of Object.entries(projectOverlay.shadows)) shadows[id] = [...shadows[id] ?? [], ...shadowedSources];
|
|
68976
|
+
return {
|
|
68977
|
+
config,
|
|
68978
|
+
sources,
|
|
68979
|
+
shadows,
|
|
68980
|
+
warnings: [...globalOverlay.warnings, ...projectOverlay.warnings]
|
|
68981
|
+
};
|
|
68982
|
+
}
|
|
68983
|
+
const capletConfigKinds = [
|
|
68984
|
+
"mcpServers",
|
|
68985
|
+
"openapiEndpoints",
|
|
68986
|
+
"graphqlEndpoints",
|
|
68987
|
+
"httpApis",
|
|
68988
|
+
"cliTools",
|
|
68989
|
+
"capletSets"
|
|
68990
|
+
];
|
|
68991
|
+
function removeCapletFromPartialOverlay(config, sources, shadows, id) {
|
|
68992
|
+
for (const kind of capletConfigKinds) delete config[kind][id];
|
|
68993
|
+
if (sources[id]) shadows[id] = [...shadows[id] ?? [], sources[id]];
|
|
68994
|
+
delete sources[id];
|
|
68995
|
+
}
|
|
68996
|
+
function formatErrorMessage(error) {
|
|
68997
|
+
return error instanceof Error ? error.message : String(error);
|
|
68998
|
+
}
|
|
68999
|
+
function envProjectConfigPath(env) {
|
|
69000
|
+
return env.CAPLETS_PROJECT_CONFIG?.trim() || resolveProjectConfigPath();
|
|
69001
|
+
}
|
|
69002
|
+
function envProjectCapletsRoot(env) {
|
|
69003
|
+
const projectConfigPath = env.CAPLETS_PROJECT_CONFIG?.trim();
|
|
69004
|
+
return projectConfigPath ? dirname(projectConfigPath) : resolveProjectCapletsRoot();
|
|
69005
|
+
}
|
|
69006
|
+
function mergeRemoteAndLocalRows(remoteRows, localOverlay, options) {
|
|
69007
|
+
const rows = /* @__PURE__ */ new Map();
|
|
69008
|
+
for (const row of remoteRows) rows.set(row.server, {
|
|
69009
|
+
...row,
|
|
69010
|
+
source: "remote"
|
|
69011
|
+
});
|
|
69012
|
+
if (!localOverlay) return [...rows.values()].filter((row) => options.includeDisabled || !row.disabled).sort((left, right) => left.server.localeCompare(right.server));
|
|
69013
|
+
for (const row of listCaplets(localOverlay, { includeDisabled: true })) {
|
|
69014
|
+
if (rows.get(row.server)) {
|
|
69015
|
+
if (row.disabled) continue;
|
|
69016
|
+
options.writeErr(`Warning: ${formatOverlaySource(row.source)} Caplet ${row.server} shadows remote Caplet\n`);
|
|
69017
|
+
}
|
|
69018
|
+
rows.set(row.server, row);
|
|
69019
|
+
}
|
|
69020
|
+
return [...rows.values()].filter((row) => options.includeDisabled || !row.disabled).sort((left, right) => left.server.localeCompare(right.server));
|
|
69021
|
+
}
|
|
69022
|
+
function formatOverlaySource(kind) {
|
|
69023
|
+
if (kind.startsWith("project")) return "project";
|
|
69024
|
+
if (kind.startsWith("global")) return "global";
|
|
69025
|
+
return kind;
|
|
69026
|
+
}
|
|
69027
|
+
function hasEnabledCaplet(config, id) {
|
|
69028
|
+
const caplet = config.mcpServers[id] ?? config.openapiEndpoints[id] ?? config.graphqlEndpoints[id] ?? config.httpApis[id] ?? config.cliTools[id] ?? config.capletSets[id];
|
|
69029
|
+
return Boolean(caplet && !caplet.disabled);
|
|
69030
|
+
}
|
|
69031
|
+
async function executeLocalOperation(caplet, request, io, config) {
|
|
68504
69032
|
const configPath = envConfigPath(io.env ?? process.env);
|
|
68505
69033
|
const engine = new CapletsEngine({
|
|
68506
69034
|
...configPath ? { configPath } : {},
|
|
69035
|
+
projectConfigPath: envProjectConfigPath(io.env ?? process.env),
|
|
68507
69036
|
...io.authDir ? { authDir: io.authDir } : {},
|
|
68508
69037
|
watch: false,
|
|
68509
|
-
writeErr: io.writeErr
|
|
69038
|
+
writeErr: io.writeErr,
|
|
69039
|
+
...config ? { configLoader: () => config } : {}
|
|
68510
69040
|
});
|
|
68511
69041
|
try {
|
|
68512
69042
|
const result = await engine.execute(caplet, request);
|
|
@@ -68861,8 +69391,8 @@ function schemaSummary(schema) {
|
|
|
68861
69391
|
required.length > 0 ? `required ${required.join(", ")}` : "no required fields"
|
|
68862
69392
|
].filter((part) => Boolean(part)).join("; ");
|
|
68863
69393
|
}
|
|
68864
|
-
function addDestinationRoot(
|
|
68865
|
-
return
|
|
69394
|
+
function addDestinationRoot(target, configPath, env) {
|
|
69395
|
+
return target === "global" ? resolveCapletsRoot(resolveConfigPath(configPath)) : envProjectCapletsRoot(env);
|
|
68866
69396
|
}
|
|
68867
69397
|
function writeAddResult(writeOut, label, result) {
|
|
68868
69398
|
if (result.path) {
|
|
@@ -68873,15 +69403,18 @@ function writeAddResult(writeOut, label, result) {
|
|
|
68873
69403
|
}
|
|
68874
69404
|
//#endregion
|
|
68875
69405
|
//#region package.json
|
|
68876
|
-
var version = "0.17.
|
|
69406
|
+
var version = "0.17.5";
|
|
68877
69407
|
//#endregion
|
|
68878
69408
|
//#region src/index.ts
|
|
68879
69409
|
async function main() {
|
|
68880
69410
|
await runCli(process.argv.slice(2), { version });
|
|
68881
69411
|
}
|
|
68882
69412
|
main().catch((error) => {
|
|
68883
|
-
|
|
69413
|
+
process.stderr.write(`${errorMessage(error)}\n`);
|
|
68884
69414
|
process.exit(1);
|
|
68885
69415
|
});
|
|
69416
|
+
function errorMessage(error) {
|
|
69417
|
+
return error instanceof Error ? error.message : String(error);
|
|
69418
|
+
}
|
|
68886
69419
|
//#endregion
|
|
68887
69420
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "caplets",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.5",
|
|
4
4
|
"description": "Progressive disclosure gateway CLI for MCP servers and native Caplets adapters.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"caplets",
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
|
-
"@caplets/core": "0.18.
|
|
37
|
+
"@caplets/core": "0.18.5"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^25.9.1",
|
|
41
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
41
|
+
"@typescript/native-preview": "7.0.0-dev.20260523.1",
|
|
42
42
|
"rolldown": "^1.0.2",
|
|
43
43
|
"typescript": "^6.0.3",
|
|
44
44
|
"vitest": "^4.1.7"
|
|
@@ -47,8 +47,9 @@
|
|
|
47
47
|
"node": ">=22"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
|
-
"
|
|
51
|
-
"build
|
|
50
|
+
"clean": "rm -rf dist",
|
|
51
|
+
"build": "pnpm run clean && rolldown -c",
|
|
52
|
+
"build:watch": "pnpm run clean && rolldown -c --watch",
|
|
52
53
|
"typecheck": "tsgo --noEmit",
|
|
53
54
|
"test": "vitest run"
|
|
54
55
|
}
|