@khorsheed/dsh-capability-catalog 0.1.97 → 0.1.99
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/CHANGELOG.md +17 -0
- package/README.en.md +31 -7
- package/README.i18n.yaml +2 -2
- package/README.md +31 -7
- package/cordis.patch.yml +1 -1
- package/lib/client.js +14790 -902
- package/lib/index.js +760 -265
- package/lib/typert.host.js +614 -295
- package/lib/typert.remote-client.d.ts +27 -4
- package/lib/typert.remote-client.js +599 -288
- package/lib/types/client/CapabilityCatalogCard.js +20 -5
- package/lib/types/client/SkillDetailModal.d.ts +13 -5
- package/lib/types/client/SkillDetailModal.js +9 -3
- package/lib/types/client/locales.d.ts +0 -4
- package/lib/types/client/locales.js +4 -8
- package/lib/types/envHint.d.ts +7 -1
- package/lib/types/envHint.js +52 -18
- package/lib/types/evidence.d.ts +142 -0
- package/lib/types/evidence.js +179 -0
- package/lib/types/frontmatter.d.ts +78 -0
- package/lib/types/frontmatter.js +186 -0
- package/lib/types/import.js +10 -19
- package/lib/types/index.d.ts +16 -4
- package/lib/types/index.js +32 -53
- package/lib/types/mcpStore.d.ts +6 -8
- package/lib/types/mcpStore.js +18 -43
- package/lib/types/mcps.d.ts +1 -6
- package/lib/types/mcps.js +0 -9
- package/lib/types/namespace.d.ts +2 -3
- package/lib/types/namespace.js +2 -3
- package/lib/types/remote.d.ts +11 -2
- package/lib/types/remote.js +4 -1
- package/lib/types/scoped-delivery.d.ts +36 -24
- package/lib/types/scoped-delivery.js +136 -100
- package/lib/types/scoped-edits.js +2 -1
- package/lib/types/shellEnv.d.ts +8 -1
- package/lib/types/shellEnv.js +37 -16
- package/lib/types/skills.d.ts +41 -0
- package/lib/types/skills.js +51 -9
- package/lib/types/tool-face.d.ts +68 -0
- package/lib/types/tool-face.js +82 -0
- package/lib/types/types.d.ts +40 -19
- package/package.json +20 -18
- package/lib/invariant.js +0 -25
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -23
package/lib/types/index.js
CHANGED
|
@@ -51,6 +51,7 @@ import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
|
51
51
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
52
52
|
import { catalogAddSkill, catalogDetail, catalogListDirSkills, catalogReadSkillFile, catalogSetCredential, catalogSnapshot, catalogDeleteSkill, catalogPickDirectory } from './remote.js';
|
|
53
53
|
import { resolveServices } from './skills.js';
|
|
54
|
+
import { toolSchemasFacing, toolOriginsFacing, mcpServerNamesFacing } from './tool-face.js';
|
|
54
55
|
import { definitionFor, scanManagedSkills, ScopedSkillDelivery, scopedSkillsRoot, } from './scoped-delivery.js';
|
|
55
56
|
import { adoptManagedSkill, releaseManagedSkill, setManagedPresetScope, } from './scoped-edits.js';
|
|
56
57
|
import { installSkillEnvInjection } from './shellEnv.js';
|
|
@@ -58,8 +59,6 @@ import { McpStore } from './mcpStore.js';
|
|
|
58
59
|
import { reconcileRegisteredMcpTools, desiredMcpTools } from './mcpTools.js';
|
|
59
60
|
import { maskConfig } from './mcps.js';
|
|
60
61
|
import { installSkillEnvHint } from './envHint.js';
|
|
61
|
-
import { MCP_TOOL_PREFIX } from './channels.js';
|
|
62
|
-
import { toolOrigin, isValidOrigin } from './tool-origin.js';
|
|
63
62
|
import { CAPABILITY_CATALOG_NS } from './namespace.js';
|
|
64
63
|
import { CapabilityCatalogSettingsSchema } from './settings.js';
|
|
65
64
|
import { capsTag, hashOf } from './capabilities.js';
|
|
@@ -72,12 +71,20 @@ export { resolveSkillNameFromContent } from './import.js';
|
|
|
72
71
|
export { resolvePresetScope } from './preset-scope.js';
|
|
73
72
|
// The mode view: the roster a picker offers and one face per mode.
|
|
74
73
|
export { modeOptions, readModeFaces } from './modes.js';
|
|
75
|
-
export { ScopedSkillDelivery, scopedSkillsRoot, scanManagedSkills, managedSkillFrom,
|
|
74
|
+
export { ScopedSkillDelivery, scopedSkillsRoot, scanManagedSkills, managedSkillFrom, defaultSkillRoots, detectConflicts, SCOPED_PROVIDER_NAME, MANAGED_SKILL_RANK, } from './scoped-delivery.js';
|
|
76
75
|
export { adoptManagedSkill, releaseManagedSkill, setManagedPresetScope, withPresetScope, findSkillSource, managedLocation, isDirectory, } from './scoped-edits.js';
|
|
77
76
|
// The capability fingerprint: the canonical form, its digest, and the tag.
|
|
78
77
|
// Exported from the package root so a host reader can hash a snapshot it
|
|
79
78
|
// already holds without a second Remote round-trip.
|
|
80
79
|
export { canonicalCapabilities, canonicalJson, capsTag, hashOf, hashSkillBody, CAPS_TAG_PREFIX, } from './capabilities.js';
|
|
80
|
+
// Capability-composition EVIDENCE: the pure-data production face a target
|
|
81
|
+
// side runs where the composition lives. It reuses this package's own
|
|
82
|
+
// snapshot/digest algorithms (parity by construction), mounts no standing
|
|
83
|
+
// scope, registers no service, and returns plain JSON-safe data: the digest,
|
|
84
|
+
// the per-section completeness, the measurement object, and the caller's
|
|
85
|
+
// coverage declaration. Consumers never recompute the digest and never treat
|
|
86
|
+
// a partial/unavailable section as verified.
|
|
87
|
+
export { produceCapabilityEvidence } from './evidence.js';
|
|
81
88
|
/** The remote namespace name (matches the generated remote-client type). */
|
|
82
89
|
export const REMOTE_NS = 'capabilityCatalog';
|
|
83
90
|
/** Custom inject: subscribe no extra service beyond the two we probe. */
|
|
@@ -107,7 +114,6 @@ let CapabilityCatalogService = (() => {
|
|
|
107
114
|
let _addSkill_decorators;
|
|
108
115
|
let _deleteSkill_decorators;
|
|
109
116
|
let _pickDirectory_decorators;
|
|
110
|
-
let _mcpList_decorators;
|
|
111
117
|
let _mcpAdd_decorators;
|
|
112
118
|
let _mcpRemove_decorators;
|
|
113
119
|
let _mcpSetEnabled_decorators;
|
|
@@ -134,7 +140,6 @@ let CapabilityCatalogService = (() => {
|
|
|
134
140
|
_addSkill_decorators = [Remote('addSkill')];
|
|
135
141
|
_deleteSkill_decorators = [Remote('deleteSkill')];
|
|
136
142
|
_pickDirectory_decorators = [Remote('pickDirectory')];
|
|
137
|
-
_mcpList_decorators = [Remote('mcpList')];
|
|
138
143
|
_mcpAdd_decorators = [Remote('mcpAdd')];
|
|
139
144
|
_mcpRemove_decorators = [Remote('mcpRemove')];
|
|
140
145
|
_mcpSetEnabled_decorators = [Remote('mcpSetEnabled')];
|
|
@@ -158,7 +163,6 @@ let CapabilityCatalogService = (() => {
|
|
|
158
163
|
__esDecorate(this, null, _addSkill_decorators, { kind: "method", name: "addSkill", static: false, private: false, access: { has: obj => "addSkill" in obj, get: obj => obj.addSkill }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
159
164
|
__esDecorate(this, null, _deleteSkill_decorators, { kind: "method", name: "deleteSkill", static: false, private: false, access: { has: obj => "deleteSkill" in obj, get: obj => obj.deleteSkill }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
160
165
|
__esDecorate(this, null, _pickDirectory_decorators, { kind: "method", name: "pickDirectory", static: false, private: false, access: { has: obj => "pickDirectory" in obj, get: obj => obj.pickDirectory }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
161
|
-
__esDecorate(this, null, _mcpList_decorators, { kind: "method", name: "mcpList", static: false, private: false, access: { has: obj => "mcpList" in obj, get: obj => obj.mcpList }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
162
166
|
__esDecorate(this, null, _mcpAdd_decorators, { kind: "method", name: "mcpAdd", static: false, private: false, access: { has: obj => "mcpAdd" in obj, get: obj => obj.mcpAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
163
167
|
__esDecorate(this, null, _mcpRemove_decorators, { kind: "method", name: "mcpRemove", static: false, private: false, access: { has: obj => "mcpRemove" in obj, get: obj => obj.mcpRemove }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
164
168
|
__esDecorate(this, null, _mcpSetEnabled_decorators, { kind: "method", name: "mcpSetEnabled", static: false, private: false, access: { has: obj => "mcpSetEnabled" in obj, get: obj => obj.mcpSetEnabled }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -278,7 +282,7 @@ let CapabilityCatalogService = (() => {
|
|
|
278
282
|
// inject so the env-injection runs once shellEnv/credentials/skills are
|
|
279
283
|
// registered (at the catalog's apply they may not be composed yet).
|
|
280
284
|
ctx.inject(['shellEnv', 'credentials', 'skills'], () => {
|
|
281
|
-
installSkillEnvInjection(ctx, () => this.catalogScope());
|
|
285
|
+
installSkillEnvInjection(ctx, () => this.catalogScope(), () => this.dshHome());
|
|
282
286
|
});
|
|
283
287
|
// Runtime companion hint (generic): when the skill tool loads a skill,
|
|
284
288
|
// tell the model the configured credential env mappings so it uses the
|
|
@@ -402,18 +406,26 @@ let CapabilityCatalogService = (() => {
|
|
|
402
406
|
/** Record tools that appeared after the apply-time baseline (reads the live
|
|
403
407
|
* registry each time, so a change event never sees a stale/undefined ref). */
|
|
404
408
|
markNewTools() {
|
|
405
|
-
const tools = this.
|
|
409
|
+
const tools = this.toolsRegistry();
|
|
406
410
|
const current = new Set(tools?.schemas().map(t => t.name) ?? []);
|
|
407
411
|
for (const name of current) {
|
|
408
412
|
if (!this.baseline.has(name))
|
|
409
413
|
this.appearedAfterApply.add(name);
|
|
410
414
|
}
|
|
411
415
|
}
|
|
412
|
-
/**
|
|
416
|
+
/**
|
|
417
|
+
* The managed skills root under the harness home. Mirrors the official
|
|
418
|
+
* precedence (`resolveDshHome` in `@deepseek-ai/dsh-home-paths`):
|
|
419
|
+
* the settings service's home, then a non-blank `$DSH_HOME`, then the
|
|
420
|
+
* `~/.dsh` installation default. The fallback was once hardcoded to
|
|
421
|
+
* `.dsh-official`, which silently pointed a desktop instance (no settings
|
|
422
|
+
* home, no env) at ANOTHER instance's managed skills (2026-09-30).
|
|
423
|
+
*/
|
|
413
424
|
dshHome() {
|
|
414
425
|
const settings = this.ctx.get?.('settings');
|
|
415
|
-
const
|
|
416
|
-
|
|
426
|
+
const fromEnv = process.env.DSH_HOME;
|
|
427
|
+
const envHome = fromEnv !== undefined && fromEnv.trim().length > 0 ? fromEnv : undefined;
|
|
428
|
+
return settings?.home ?? envHome ?? join(homedir(), '.dsh');
|
|
417
429
|
}
|
|
418
430
|
/**
|
|
419
431
|
* The standing skill view scope of ONE preset. In the web bundle the
|
|
@@ -602,10 +614,6 @@ let CapabilityCatalogService = (() => {
|
|
|
602
614
|
async pickDirectory() {
|
|
603
615
|
return catalogPickDirectory(this.ctx);
|
|
604
616
|
}
|
|
605
|
-
async mcpList() {
|
|
606
|
-
// Masked DTO: never surface secretRef markers (or the secret key names) to the browser.
|
|
607
|
-
return this.mcp.list().map(maskConfig);
|
|
608
|
-
}
|
|
609
617
|
async mcpAdd(config) {
|
|
610
618
|
if (config.serverName.trim() === '')
|
|
611
619
|
return false;
|
|
@@ -642,57 +650,28 @@ let CapabilityCatalogService = (() => {
|
|
|
642
650
|
credentials: this.mcp.credentials(),
|
|
643
651
|
};
|
|
644
652
|
}
|
|
653
|
+
/** The live tools registry, when the composition provides one. */
|
|
654
|
+
toolsRegistry() {
|
|
655
|
+
return this.ctx.get?.('tools');
|
|
656
|
+
}
|
|
645
657
|
/** The visible tool schemas in the standing scope (the set the model sees),
|
|
646
658
|
* or the global layer when no preset standing key resolves. Skills already
|
|
647
659
|
* enumerate through the same scope; tools must use it too so the
|
|
648
660
|
* settings reader sees the official/plugin/MCP tools the model actually has. */
|
|
649
661
|
toolSchemasIn(scope) {
|
|
650
|
-
|
|
651
|
-
if (tools?.schemas === undefined)
|
|
652
|
-
return [];
|
|
653
|
-
return scope === undefined ? tools.schemas() : tools.schemas(scope);
|
|
662
|
+
return toolSchemasFacing(this.toolsRegistry(), scope);
|
|
654
663
|
}
|
|
655
664
|
/** tool name → author-declared origin, read via `ctx.tools.get(name)` so the
|
|
656
665
|
* `Symbol.for('dsh.tool.origin')` tag on the retained definition survives (the
|
|
657
666
|
* system-prompt assembly strips it, but `get()` returns the full definition). */
|
|
658
667
|
toolOriginsMap(scope) {
|
|
659
|
-
const tools = this.
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
const schemas = scope === undefined ? tools.schemas() : tools.schemas(scope);
|
|
663
|
-
const out = new Map();
|
|
664
|
-
for (const schema of schemas) {
|
|
665
|
-
const def = scope === undefined ? tools.get(schema.name) : tools.get(schema.name, scope);
|
|
666
|
-
if (def === undefined) {
|
|
667
|
-
// `schemas()` and `get()` disagree for this scope — the author's origin
|
|
668
|
-
// tag (if any) cannot be read, so the tool falls back to heuristics.
|
|
669
|
-
this.ctx.logger.warn(`capability-catalog: tool "${schema.name}" is visible but ctx.tools.get() is undefined in this scope — its origin tag cannot be read; it will be classified by heuristics`);
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
const origin = toolOrigin(def);
|
|
673
|
-
if (origin !== undefined) {
|
|
674
|
-
if (!isValidOrigin(origin)) {
|
|
675
|
-
this.ctx.logger.warn(`capability-catalog: tool "${schema.name}" origin channel "${String(origin.channel)}" is not a known channel (plugin/builtin/mcp) — ignoring the tag, falling back to heuristics`);
|
|
676
|
-
continue;
|
|
677
|
-
}
|
|
678
|
-
out.set(schema.name, origin);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
return out;
|
|
668
|
+
const tools = this.toolsRegistry();
|
|
669
|
+
const { origins } = toolOriginsFacing(tools, toolSchemasFacing(tools, scope), scope, (message) => this.ctx.logger.warn(message));
|
|
670
|
+
return origins;
|
|
682
671
|
}
|
|
683
672
|
/** MCP server names from live `mcp__`-prefixed tools (prefix-derived baseline). */
|
|
684
673
|
mcpServerNamesIn(scope) {
|
|
685
|
-
|
|
686
|
-
for (const schema of this.toolSchemasIn(scope)) {
|
|
687
|
-
if (!schema.name.startsWith(MCP_TOOL_PREFIX))
|
|
688
|
-
continue;
|
|
689
|
-
const rest = schema.name.slice(MCP_TOOL_PREFIX.length);
|
|
690
|
-
const sep = rest.indexOf('__');
|
|
691
|
-
const server = sep > 0 ? rest.slice(0, sep) : rest;
|
|
692
|
-
if (server.length > 0 && !names.includes(server))
|
|
693
|
-
names.push(server);
|
|
694
|
-
}
|
|
695
|
-
return names;
|
|
674
|
+
return mcpServerNamesFacing(this.toolSchemasIn(scope));
|
|
696
675
|
}
|
|
697
676
|
/**
|
|
698
677
|
* Reconcile which discovered MCP tools are registered on `ctx.tools` so the
|
package/lib/types/mcpStore.d.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
import type { CatalogMcpCredentialDecl, CatalogMcpServerConfig, CatalogMcpTool } from './types.ts';
|
|
9
9
|
/** One tool's persisted record: the user's toggle choice only. Descriptions and
|
|
10
10
|
* parameter schemas are discovered at connect time and deliberately NOT persisted
|
|
11
|
-
* (they bloat settings.yaml and go stale);
|
|
11
|
+
* (they bloat settings.yaml and go stale); the service re-fills them at boot by
|
|
12
|
+
* re-connecting the enabled servers. */
|
|
12
13
|
export interface PersistedMcpTool {
|
|
13
14
|
readonly name: string;
|
|
14
15
|
readonly enabled: boolean;
|
|
@@ -50,15 +51,12 @@ export declare class McpStore {
|
|
|
50
51
|
setToolEnabled(serverName: string, toolName: string, enabled: boolean): void;
|
|
51
52
|
/** Connect + discover tools for a server; caches the list (connect errors surface). */
|
|
52
53
|
discover(serverName: string): Promise<readonly CatalogMcpTool[]>;
|
|
53
|
-
/** Re-connect + re-discover every enabled server — boot-time refill of the
|
|
54
|
-
* discovered descriptions/parameters (the persisted block stores only
|
|
55
|
-
* name+enabled). Preserves per-tool enable flags; per-server errors are
|
|
56
|
-
* recorded and do not stop the others. */
|
|
57
|
-
reconnectAll(): Promise<void>;
|
|
58
|
-
/** A server's most recent connection/dev error. */
|
|
59
|
-
errorOf(serverName: string): string | undefined;
|
|
60
54
|
/** Forward one tool call to a server (connect → call → close). Resolves secrets. */
|
|
61
55
|
callTool(serverName: string, rawName: string, args: Record<string, unknown>): Promise<unknown>;
|
|
56
|
+
/** The `resolveConfigSecrets` callback for one server: a bare secret ref
|
|
57
|
+
* resolves to the stored credential under the server's env/header/query
|
|
58
|
+
* namespace (the same namespaces {@link credentialRefsOf} writes). */
|
|
59
|
+
private secretResolver;
|
|
62
60
|
/** Extract credential reference paths from a config (env/header/url-query).
|
|
63
61
|
* Namespaced `mcp.<serverName>.env.<KEY>` so multiple servers don't collide. */
|
|
64
62
|
private credentialRefsOf;
|
package/lib/types/mcpStore.js
CHANGED
|
@@ -32,7 +32,6 @@ export class McpStore {
|
|
|
32
32
|
config: server.config,
|
|
33
33
|
credentials: existing?.credentials ?? new Map(),
|
|
34
34
|
tools,
|
|
35
|
-
error: undefined,
|
|
36
35
|
});
|
|
37
36
|
}
|
|
38
37
|
for (const [ref, value] of Object.entries(state.credentials ?? {})) {
|
|
@@ -83,7 +82,6 @@ export class McpStore {
|
|
|
83
82
|
config,
|
|
84
83
|
credentials: existing?.credentials ?? new Map(),
|
|
85
84
|
tools: existing?.tools ?? [],
|
|
86
|
-
error: existing?.error,
|
|
87
85
|
});
|
|
88
86
|
this.persist();
|
|
89
87
|
}
|
|
@@ -130,56 +128,41 @@ export class McpStore {
|
|
|
130
128
|
const { connectMcpServer } = await import('./mcpConnector.js');
|
|
131
129
|
// Resolve secretRef markers to the stored credential values before connecting,
|
|
132
130
|
// so a configured API key is sent as its real value (never the secretRef).
|
|
133
|
-
const resolved = await resolveConfigSecrets(s.config,
|
|
134
|
-
const candidates = [
|
|
135
|
-
`mcp.${serverName}.env.${ref}`,
|
|
136
|
-
`mcp.${serverName}.header.${ref}`,
|
|
137
|
-
`mcp.${serverName}.query.${ref}`,
|
|
138
|
-
];
|
|
139
|
-
for (const candidate of candidates) {
|
|
140
|
-
const value = s.credentials.get(candidate);
|
|
141
|
-
if (value !== undefined)
|
|
142
|
-
return value;
|
|
143
|
-
}
|
|
144
|
-
return undefined;
|
|
145
|
-
});
|
|
131
|
+
const resolved = await resolveConfigSecrets(s.config, this.secretResolver(serverName, s));
|
|
146
132
|
const connection = await connectMcpServer(resolved);
|
|
147
133
|
// Preserve the user's per-tool enable flags — re-discovering refreshes
|
|
148
134
|
// descriptions/parameters without resetting toggles (new tools default on).
|
|
149
135
|
const prevEnabled = new Map(s.tools.map(t => [t.name, t.enabled]));
|
|
150
136
|
s.tools = connection.tools.map(t => ({ ...t, enabled: prevEnabled.get(t.name) ?? true }));
|
|
151
|
-
s.error = undefined;
|
|
152
137
|
await connection.close();
|
|
153
138
|
this.persist();
|
|
154
139
|
return s.tools;
|
|
155
140
|
}
|
|
156
|
-
catch
|
|
157
|
-
s.error = String(error);
|
|
141
|
+
catch {
|
|
158
142
|
this.persist();
|
|
159
143
|
return s.tools;
|
|
160
144
|
}
|
|
161
145
|
}
|
|
162
|
-
/** Re-connect + re-discover every enabled server — boot-time refill of the
|
|
163
|
-
* discovered descriptions/parameters (the persisted block stores only
|
|
164
|
-
* name+enabled). Preserves per-tool enable flags; per-server errors are
|
|
165
|
-
* recorded and do not stop the others. */
|
|
166
|
-
async reconnectAll() {
|
|
167
|
-
for (const server of this.list()) {
|
|
168
|
-
if (!server.enabled)
|
|
169
|
-
continue;
|
|
170
|
-
await this.discover(server.serverName);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/** A server's most recent connection/dev error. */
|
|
174
|
-
errorOf(serverName) {
|
|
175
|
-
return this.servers.get(serverName)?.error;
|
|
176
|
-
}
|
|
177
146
|
/** Forward one tool call to a server (connect → call → close). Resolves secrets. */
|
|
178
147
|
async callTool(serverName, rawName, args) {
|
|
179
148
|
const s = this.servers.get(serverName);
|
|
180
149
|
if (s === undefined)
|
|
181
150
|
throw new Error(`unknown MCP server: ${serverName}`);
|
|
182
|
-
const resolved = await resolveConfigSecrets(s.config,
|
|
151
|
+
const resolved = await resolveConfigSecrets(s.config, this.secretResolver(serverName, s));
|
|
152
|
+
const { connectMcpServer } = await import('./mcpConnector.js');
|
|
153
|
+
const connection = await connectMcpServer(resolved);
|
|
154
|
+
try {
|
|
155
|
+
return await connection.call(rawName, args);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
await connection.close();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** The `resolveConfigSecrets` callback for one server: a bare secret ref
|
|
162
|
+
* resolves to the stored credential under the server's env/header/query
|
|
163
|
+
* namespace (the same namespaces {@link credentialRefsOf} writes). */
|
|
164
|
+
secretResolver(serverName, s) {
|
|
165
|
+
return async (ref) => {
|
|
183
166
|
const candidates = [
|
|
184
167
|
`mcp.${serverName}.env.${ref}`,
|
|
185
168
|
`mcp.${serverName}.header.${ref}`,
|
|
@@ -191,15 +174,7 @@ export class McpStore {
|
|
|
191
174
|
return value;
|
|
192
175
|
}
|
|
193
176
|
return undefined;
|
|
194
|
-
}
|
|
195
|
-
const { connectMcpServer } = await import('./mcpConnector.js');
|
|
196
|
-
const connection = await connectMcpServer(resolved);
|
|
197
|
-
try {
|
|
198
|
-
return await connection.call(rawName, args);
|
|
199
|
-
}
|
|
200
|
-
finally {
|
|
201
|
-
await connection.close();
|
|
202
|
-
}
|
|
177
|
+
};
|
|
203
178
|
}
|
|
204
179
|
/** Extract credential reference paths from a config (env/header/url-query).
|
|
205
180
|
* Namespaced `mcp.<serverName>.env.<KEY>` so multiple servers don't collide. */
|
package/lib/types/mcps.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* the catalog's own thin client).
|
|
11
11
|
* @module @khorsheed/dsh-capability-catalog/mcps
|
|
12
12
|
*/
|
|
13
|
-
import type { CatalogJsonValue,
|
|
13
|
+
import type { CatalogJsonValue, CatalogMcpServerConfig, McpTransport } from './types.ts';
|
|
14
14
|
/** Secret marker put in a config field whose value lives in the credential store. */
|
|
15
15
|
export declare const SECRET_REF_PREFIX = "secretRef:";
|
|
16
16
|
/** A parsed server from a pasted mcp.json server entry. */
|
|
@@ -41,15 +41,11 @@ export declare function maskSecret(value: string): string;
|
|
|
41
41
|
* marker (bare or scheme-prefixed) with `·secretRef·`, so neither the secret key
|
|
42
42
|
* name nor the marker is exposed to the browser. */
|
|
43
43
|
export declare function maskConfig(config: CatalogMcpServerConfig): CatalogMcpServerConfig;
|
|
44
|
-
/** Whether the config still has an unfilled credential (= prompts for it). */
|
|
45
|
-
export declare function hasUnconfiguredCredentials(credentials: readonly CatalogMcpCredentialDecl[]): boolean;
|
|
46
44
|
/** Resolve `secretRef:` markers in a config into plaintext before connecting
|
|
47
45
|
* (the catalog resolves via the stored credential values, then connects).
|
|
48
46
|
* Replaces embedded markers too, so a scheme-prefixed value (`Bearer secretRef:a`)
|
|
49
47
|
* resolves to `Bearer <value>`. */
|
|
50
48
|
export declare function resolveConfigSecrets(config: CatalogMcpServerConfig, resolve: (ref: string) => Promise<string | undefined>): Promise<CatalogMcpServerConfig>;
|
|
51
|
-
/** Sort tools by name and default each to enabled. */
|
|
52
|
-
export declare function finalizeTools(tools: readonly CatalogMcpTool[]): CatalogMcpTool[];
|
|
53
49
|
/**
|
|
54
50
|
* Map each bare secret config key (env/header name, or a query param) to its
|
|
55
51
|
* catalog-stored, namespaced credential ref (`mcp.<server>.<kind>.<key>`). The
|
|
@@ -59,6 +55,5 @@ export declare function finalizeTools(tools: readonly CatalogMcpTool[]): Catalog
|
|
|
59
55
|
* next snapshot round-trips the namespaced refs.
|
|
60
56
|
*/
|
|
61
57
|
export declare function credentialStoredRefs(serverName: string, config: CatalogMcpServerConfig): Readonly<Record<string, string>>;
|
|
62
|
-
export { SECRET_REF_PREFIX as _SECRET_REF_PREFIX };
|
|
63
58
|
export type { CatalogJsonValue };
|
|
64
59
|
//# sourceMappingURL=mcps.d.ts.map
|
package/lib/types/mcps.js
CHANGED
|
@@ -92,10 +92,6 @@ export function maskConfig(config) {
|
|
|
92
92
|
...(url !== undefined ? { url } : {}),
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
|
-
/** Whether the config still has an unfilled credential (= prompts for it). */
|
|
96
|
-
export function hasUnconfiguredCredentials(credentials) {
|
|
97
|
-
return credentials.some(c => !c.configured);
|
|
98
|
-
}
|
|
99
95
|
/** Resolve `secretRef:` markers in a config into plaintext before connecting
|
|
100
96
|
* (the catalog resolves via the stored credential values, then connects).
|
|
101
97
|
* Replaces embedded markers too, so a scheme-prefixed value (`Bearer secretRef:a`)
|
|
@@ -124,10 +120,6 @@ export async function resolveConfigSecrets(config, resolve) {
|
|
|
124
120
|
...(url !== undefined ? { url } : {}),
|
|
125
121
|
};
|
|
126
122
|
}
|
|
127
|
-
/** Sort tools by name and default each to enabled. */
|
|
128
|
-
export function finalizeTools(tools) {
|
|
129
|
-
return [...tools].sort((a, b) => a.name.localeCompare(b.name));
|
|
130
|
-
}
|
|
131
123
|
/**
|
|
132
124
|
* Map each bare secret config key (env/header name, or a query param) to its
|
|
133
125
|
* catalog-stored, namespaced credential ref (`mcp.<server>.<kind>.<key>`). The
|
|
@@ -154,4 +146,3 @@ export function credentialStoredRefs(serverName, config) {
|
|
|
154
146
|
}
|
|
155
147
|
return out;
|
|
156
148
|
}
|
|
157
|
-
export { SECRET_REF_PREFIX as _SECRET_REF_PREFIX }; // re-export for tests if needed
|
package/lib/types/namespace.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The settings namespace owned by the capability-catalog plugin. Plain string
|
|
3
|
-
* so both the host half (settings registration) and the browser half
|
|
4
|
-
*
|
|
5
|
-
* dependencies into the client bundle.
|
|
3
|
+
* so both the host half (settings registration) and the browser half import
|
|
4
|
+
* it without dragging host-only dependencies into the client bundle.
|
|
6
5
|
*/
|
|
7
6
|
/** Settings namespace owned by the capability-catalog plugin. */
|
|
8
7
|
export declare const CAPABILITY_CATALOG_NS = "capability-catalog";
|
package/lib/types/namespace.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The settings namespace owned by the capability-catalog plugin. Plain string
|
|
3
|
-
* so both the host half (settings registration) and the browser half
|
|
4
|
-
*
|
|
5
|
-
* dependencies into the client bundle.
|
|
3
|
+
* so both the host half (settings registration) and the browser half import
|
|
4
|
+
* it without dragging host-only dependencies into the client bundle.
|
|
6
5
|
*/
|
|
7
6
|
/** Settings namespace owned by the capability-catalog plugin. */
|
|
8
7
|
export const CAPABILITY_CATALOG_NS = 'capability-catalog';
|
package/lib/types/remote.d.ts
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { Context } from '@deepseek-ai/cordis';
|
|
8
8
|
import type { CapabilityCatalogSnapshot, CatalogAddSkillRequest, CatalogAddSkillResult, CatalogCredentialSetRequest, CatalogDeleteSkillResult, CatalogDirSkillInfo, CatalogJsonValue, CatalogSkillDetail, CatalogSkillFileRead, CatalogToolRow } from './types.ts';
|
|
9
|
-
import { type CredentialsSlice, type RegistrySlice, type SkillDefinitionLike } from './skills.ts';
|
|
9
|
+
import { type CredentialsSlice, type RegistrySlice, type SkillDefinitionLike, type SkillsCollectionReport } from './skills.ts';
|
|
10
10
|
import type { ToolOrigin } from './tool-origin.ts';
|
|
11
11
|
import { resolveSkillNameFromContent } from './import.ts';
|
|
12
|
-
export type { CredentialsSlice, RegistrySlice };
|
|
12
|
+
export type { CredentialsSlice, RegistrySlice, SkillsCollectionReport };
|
|
13
13
|
/** Project visible tools onto catalog rows with channel attribution. */
|
|
14
14
|
export declare function projectTools(schemas: readonly ToolSchemaLike[], mcpServers: readonly string[], appearedAfterApply: Set<string>, toolOrigins?: ReadonlyMap<string, ToolOrigin>): CatalogToolRow[];
|
|
15
15
|
/** Minimal ToolSchema shape (model-facing name/description/parameters). */
|
|
@@ -18,6 +18,14 @@ interface ToolSchemaLike {
|
|
|
18
18
|
readonly description?: string;
|
|
19
19
|
readonly parameters?: CatalogJsonValue;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Out-bag a snapshot caller may pass to receive the collection health the
|
|
23
|
+
* rows were gathered with. Evidence production reads it to build the
|
|
24
|
+
* per-section completeness descriptors; the Remote verbs never pass one.
|
|
25
|
+
*/
|
|
26
|
+
export interface CatalogSnapshotReport {
|
|
27
|
+
skills?: SkillsCollectionReport;
|
|
28
|
+
}
|
|
21
29
|
/**
|
|
22
30
|
* Build the full catalog snapshot (skills + tools).
|
|
23
31
|
*
|
|
@@ -29,6 +37,7 @@ interface ToolSchemaLike {
|
|
|
29
37
|
export declare function catalogSnapshot(workdir: string | undefined, registry: RegistrySlice, toolsSchemas: readonly ToolSchemaLike[], mcpServers: readonly string[], appearedAfterApply: ReadonlySet<string>, scopes?: readonly unknown[], toolOrigins?: ReadonlyMap<string, ToolOrigin>, options?: {
|
|
30
38
|
fingerprint?: boolean;
|
|
31
39
|
preset?: string;
|
|
40
|
+
report?: CatalogSnapshotReport;
|
|
32
41
|
}): Promise<CapabilityCatalogSnapshot>;
|
|
33
42
|
/** Load one skill detail. */
|
|
34
43
|
export declare function catalogDetail(ctx: Context, registry: RegistrySlice, name: string, workdir: string | undefined, scope?: unknown, fallback?: SkillDefinitionLike): Promise<CatalogSkillDetail | undefined>;
|
package/lib/types/remote.js
CHANGED
|
@@ -41,7 +41,10 @@ export function projectTools(schemas, mcpServers, appearedAfterApply, toolOrigin
|
|
|
41
41
|
*/
|
|
42
42
|
export async function catalogSnapshot(workdir, registry, toolsSchemas, mcpServers, appearedAfterApply, scopes = [undefined], toolOrigins = new Map(), options = {}) {
|
|
43
43
|
const fingerprint = options.fingerprint === true;
|
|
44
|
-
const
|
|
44
|
+
const skillsReport = { attemptedScopes: 0, failedScopes: 0, incompleteReads: 0, unreadableBodies: 0 };
|
|
45
|
+
const base = await collectSkills(registry, workdir, scopes, { bodySha: fingerprint, report: skillsReport });
|
|
46
|
+
if (options.report !== undefined)
|
|
47
|
+
options.report.skills = skillsReport;
|
|
45
48
|
const tools = projectTools(toolsSchemas, mcpServers, appearedAfterApply, toolOrigins);
|
|
46
49
|
const snapshot = {
|
|
47
50
|
skills: base.skills,
|
|
@@ -57,6 +57,8 @@ export interface ManagedSkill {
|
|
|
57
57
|
readonly userInvocable: boolean;
|
|
58
58
|
/** Preset ids this skill is delivered to; absent/empty means "not scoped". */
|
|
59
59
|
readonly presetScope: readonly string[] | undefined;
|
|
60
|
+
/** The frontmatter's full `metadata:` block, preserved verbatim when parseable. */
|
|
61
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
60
62
|
/** The skill's own directory (its `resourceBase`). */
|
|
61
63
|
readonly dir: string;
|
|
62
64
|
/** Absolute `SKILL.md` path. */
|
|
@@ -115,31 +117,15 @@ export interface ScopedDeliveryDeps {
|
|
|
115
117
|
export interface ScopedDeliveryRegistry {
|
|
116
118
|
readonly registerProvider?: (create: (control: SkillProviderControl) => SkillProvider) => () => void;
|
|
117
119
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Read the `presetScope` list out of a frontmatter block.
|
|
120
|
-
*
|
|
121
|
-
* Accepted forms, at any indentation (the canonical one is
|
|
122
|
-
* `metadata.presetScope`):
|
|
123
|
-
*
|
|
124
|
-
* ```yaml
|
|
125
|
-
* metadata:
|
|
126
|
-
* presetScope: [dsh-writing, dev]
|
|
127
|
-
* presetScope:
|
|
128
|
-
* - dsh-writing
|
|
129
|
-
* ```
|
|
130
|
-
* @param front - the frontmatter text without its `---` fences.
|
|
131
|
-
* @returns the declared ids, or undefined when the key is absent.
|
|
132
|
-
*/
|
|
133
|
-
export declare function parsePresetScopeFrontmatter(front: string): readonly string[] | undefined;
|
|
134
|
-
/** Whether a string is a usable skill / preset id. */
|
|
135
|
-
export declare function isKebabId(value: string): boolean;
|
|
136
|
-
/** Split a SKILL.md into its frontmatter block and body. */
|
|
137
|
-
export declare function splitFrontmatter(content: string): {
|
|
138
|
-
front: string;
|
|
139
|
-
body: string;
|
|
140
|
-
} | undefined;
|
|
141
120
|
/**
|
|
142
121
|
* Parse one managed `SKILL.md` into its delivery record.
|
|
122
|
+
*
|
|
123
|
+
* The frontmatter is ONE real-YAML parse (see `./frontmatter.ts`): name and
|
|
124
|
+
* description read top-level only, the invocation flags accept the host
|
|
125
|
+
* loader's boolean variants, and the scope list comes from the canonical
|
|
126
|
+
* `metadata.presetScope` (falling back to a legacy top-level key). A malformed
|
|
127
|
+
* `metadata:` block degrades to no-metadata — the skill still delivers —
|
|
128
|
+
* rather than rejecting a file the rest of the block could still serve.
|
|
143
129
|
* @param content - the file text.
|
|
144
130
|
* @param dir - the skill's directory (its resource base).
|
|
145
131
|
* @param path - the absolute `SKILL.md` path.
|
|
@@ -194,6 +180,9 @@ export declare class ScopedSkillDelivery {
|
|
|
194
180
|
private failure;
|
|
195
181
|
private started;
|
|
196
182
|
private disposed;
|
|
183
|
+
/** Reconcile gate: one pass in flight, one queued rerun (see reconcile). */
|
|
184
|
+
private reconcileInFlight;
|
|
185
|
+
private reconcileQueued;
|
|
197
186
|
/** The probed registry's mount list (undefined until start()'s probe resolves). */
|
|
198
187
|
private presetMounts;
|
|
199
188
|
/**
|
|
@@ -203,8 +192,22 @@ export declare class ScopedSkillDelivery {
|
|
|
203
192
|
constructor(ctx: Context, deps: ScopedDeliveryDeps);
|
|
204
193
|
/** Start watching the managed root and deliver what it currently holds. */
|
|
205
194
|
start(): Promise<void>;
|
|
206
|
-
/**
|
|
195
|
+
/**
|
|
196
|
+
* Re-read policy and files, then converge the delivery registrations.
|
|
197
|
+
*
|
|
198
|
+
* Triggers arrive concurrently — the filesystem watcher, the
|
|
199
|
+
* `agent-preset/selected` event, and the management Remotes — and two
|
|
200
|
+
* overlapping passes can both decide a preset has no registration and each
|
|
201
|
+
* register one, leaving two same-named providers serving one preset. The
|
|
202
|
+
* gate allows ONE pass in flight and coalesces every trigger that arrives
|
|
203
|
+
* during it into ONE queued rerun (the pass re-reads policy and files from
|
|
204
|
+
* scratch, so a rerun loses no update). A caller that finds a pass in
|
|
205
|
+
* flight waits for the chain its flag provoked, so an awaited reconcile
|
|
206
|
+
* still reflects the write that triggered it.
|
|
207
|
+
*/
|
|
207
208
|
reconcile(): Promise<void>;
|
|
209
|
+
/** One reconcile pass (call only through {@link reconcile}'s gate). */
|
|
210
|
+
private reconcileNow;
|
|
208
211
|
/** The current status, for the settings surface and the model-facing tool. */
|
|
209
212
|
status(): ScopedDeliveryStatus;
|
|
210
213
|
/** Stop watching and dispose every delivery registration. */
|
|
@@ -233,6 +236,13 @@ export declare class ScopedSkillDelivery {
|
|
|
233
236
|
private providerFor;
|
|
234
237
|
/** The managed skills one delivery currently serves. */
|
|
235
238
|
private skillsFor;
|
|
239
|
+
/** The managed skills readable through one preset's reader: its own scoped
|
|
240
|
+
* delivery plus the unscoped ones every preset sees through the global layer. */
|
|
241
|
+
private readableFor;
|
|
242
|
+
/** The read-only bundle-file reader served with one preset's delivery. */
|
|
243
|
+
private bundleFileTool;
|
|
244
|
+
/** Serve one bundle file (or the bundle's file list) to a delivered skill's caller. */
|
|
245
|
+
private readBundleFile;
|
|
236
246
|
/** Invalidate every registration's completed catalogs. */
|
|
237
247
|
private invalidateAll;
|
|
238
248
|
/** Dispose every registration. */
|
|
@@ -240,4 +250,6 @@ export declare class ScopedSkillDelivery {
|
|
|
240
250
|
/** Dispose one registration and forget it. */
|
|
241
251
|
private dropEntry;
|
|
242
252
|
}
|
|
253
|
+
/** The scoped bundle-file reader's tool name. */
|
|
254
|
+
export declare const READ_SKILL_FILE_TOOL = "read_skill_file";
|
|
243
255
|
//# sourceMappingURL=scoped-delivery.d.ts.map
|