@khorsheed/dsh-capability-catalog 0.1.95
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +286 -0
- package/README.zh.md +138 -0
- package/cordis.patch.yml +8 -0
- package/lib/client.js +10100 -0
- package/lib/index.js +4022 -0
- package/lib/invariant.js +25 -0
- package/lib/mcpConnector-C2TbkPBY.js +69 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1417 -0
- package/lib/typert.remote-client.d.ts +68 -0
- package/lib/typert.remote-client.js +1136 -0
- package/lib/types/capabilities.d.ts +103 -0
- package/lib/types/capabilities.js +116 -0
- package/lib/types/channels.d.ts +41 -0
- package/lib/types/channels.js +75 -0
- package/lib/types/client/AddMcpDialog.d.ts +13 -0
- package/lib/types/client/AddMcpDialog.js +79 -0
- package/lib/types/client/AddSkillModal.d.ts +17 -0
- package/lib/types/client/AddSkillModal.js +138 -0
- package/lib/types/client/BundleFileTree.d.ts +7 -0
- package/lib/types/client/BundleFileTree.js +74 -0
- package/lib/types/client/CapabilityCatalogCard.d.ts +57 -0
- package/lib/types/client/CapabilityCatalogCard.js +479 -0
- package/lib/types/client/CredentialField.d.ts +14 -0
- package/lib/types/client/CredentialField.js +9 -0
- package/lib/types/client/McpCards.d.ts +13 -0
- package/lib/types/client/McpCards.js +12 -0
- package/lib/types/client/McpServerManageModal.d.ts +18 -0
- package/lib/types/client/McpServerManageModal.js +63 -0
- package/lib/types/client/MetadataRow.d.ts +10 -0
- package/lib/types/client/MetadataRow.js +6 -0
- package/lib/types/client/ModalShell.d.ts +16 -0
- package/lib/types/client/ModalShell.js +27 -0
- package/lib/types/client/ModeChips.d.ts +38 -0
- package/lib/types/client/ModeChips.js +42 -0
- package/lib/types/client/SchemaView.d.ts +11 -0
- package/lib/types/client/SchemaView.js +143 -0
- package/lib/types/client/SkillCards.d.ts +28 -0
- package/lib/types/client/SkillCards.js +43 -0
- package/lib/types/client/SkillDetailModal.d.ts +69 -0
- package/lib/types/client/SkillDetailModal.js +167 -0
- package/lib/types/client/ToolCards.d.ts +28 -0
- package/lib/types/client/ToolCards.js +52 -0
- package/lib/types/client/ToolDetailModal.d.ts +12 -0
- package/lib/types/client/ToolDetailModal.js +29 -0
- package/lib/types/client/icons.d.ts +26 -0
- package/lib/types/client/icons.js +10 -0
- package/lib/types/client/index.d.ts +21 -0
- package/lib/types/client/index.js +196 -0
- package/lib/types/client/locales.d.ts +392 -0
- package/lib/types/client/locales.js +395 -0
- package/lib/types/client/mcp-model.d.ts +24 -0
- package/lib/types/client/mcp-model.js +87 -0
- package/lib/types/client/mode-model.d.ts +108 -0
- package/lib/types/client/mode-model.js +140 -0
- package/lib/types/client/preset-display.d.ts +21 -0
- package/lib/types/client/preset-display.js +20 -0
- package/lib/types/client/settle.d.ts +40 -0
- package/lib/types/client/settle.js +57 -0
- package/lib/types/client/slots.d.ts +86 -0
- package/lib/types/client/slots.js +1 -0
- package/lib/types/envHint.d.ts +22 -0
- package/lib/types/envHint.js +76 -0
- package/lib/types/import.d.ts +106 -0
- package/lib/types/import.js +635 -0
- package/lib/types/index.d.ts +216 -0
- package/lib/types/index.js +739 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.js +23 -0
- package/lib/types/mcpConnector.d.ts +24 -0
- package/lib/types/mcpConnector.js +73 -0
- package/lib/types/mcpStore.d.ts +68 -0
- package/lib/types/mcpStore.js +235 -0
- package/lib/types/mcpTools.d.ts +45 -0
- package/lib/types/mcpTools.js +125 -0
- package/lib/types/mcps.d.ts +64 -0
- package/lib/types/mcps.js +157 -0
- package/lib/types/modes.d.ts +65 -0
- package/lib/types/modes.js +97 -0
- package/lib/types/namespace.d.ts +9 -0
- package/lib/types/namespace.js +8 -0
- package/lib/types/official-tools.d.ts +3 -0
- package/lib/types/official-tools.js +44 -0
- package/lib/types/preset-scope.d.ts +105 -0
- package/lib/types/preset-scope.js +117 -0
- package/lib/types/remote.d.ts +48 -0
- package/lib/types/remote.js +120 -0
- package/lib/types/scoped-delivery.d.ts +243 -0
- package/lib/types/scoped-delivery.js +686 -0
- package/lib/types/scoped-edits.d.ts +90 -0
- package/lib/types/scoped-edits.js +234 -0
- package/lib/types/settings.d.ts +28 -0
- package/lib/types/settings.js +28 -0
- package/lib/types/shellEnv.d.ts +65 -0
- package/lib/types/shellEnv.js +232 -0
- package/lib/types/skills.d.ts +103 -0
- package/lib/types/skills.js +303 -0
- package/lib/types/tool-origin.d.ts +29 -0
- package/lib/types/tool-origin.js +30 -0
- package/lib/types/types.d.ts +315 -0
- package/lib/types/types.js +8 -0
- package/lib/types/zip.d.ts +24 -0
- package/lib/types/zip.js +80 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +119 -0
- package/scripts/gen-official-tools.mts +52 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cross-mode comparison model: ONE grid built from every mode's face.
|
|
3
|
+
*
|
|
4
|
+
* The section answers two different questions with one grid. A mode-selected
|
|
5
|
+
* view shows what that mode loads; the comparison shows, for every capability
|
|
6
|
+
* the instance knows, the modes that load it — which is the question "这个工具
|
|
7
|
+
* 会在哪些模式下被加载?" and the reason the mode control exists at all.
|
|
8
|
+
*
|
|
9
|
+
* Two properties make the comparison trustworthy:
|
|
10
|
+
*
|
|
11
|
+
* - A capability's ROW is taken from the deployment default's face when the
|
|
12
|
+
* default mode also has it. Every mode registers its own instance of a tool
|
|
13
|
+
* (its own description, its own channel attribution), and picking whichever
|
|
14
|
+
* mode happened to be read first would make the card's prose depend on roster
|
|
15
|
+
* order. The default mode is the one the human sees everywhere else in the
|
|
16
|
+
* panel, so it wins whenever it can answer.
|
|
17
|
+
* - A mode that could not be read contributes NO modes-list and no rows; it is
|
|
18
|
+
* reported in `unavailable` instead. "Not loaded in this mode" and "this mode
|
|
19
|
+
* could not be read" are different claims, and a union that merged them would
|
|
20
|
+
* quietly understate where a capability is available.
|
|
21
|
+
*
|
|
22
|
+
* Pure over its input, so the unit tests need no renderer.
|
|
23
|
+
* @module @khorsheed/dsh-capability-catalog/client/mode-model
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the mode ids a capability was found in into chips, in the order the
|
|
27
|
+
* ids arrive (the roster's own read order). An id the roster no longer supplies
|
|
28
|
+
* still renders — its own id is the only label left — because dropping it would
|
|
29
|
+
* hide a mode the comparison actually read.
|
|
30
|
+
* @param ids - mode ids, in roster order.
|
|
31
|
+
* @param options - the roster, for display names and the default marker.
|
|
32
|
+
* @returns one chip per id.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveModeChips(ids, options) {
|
|
35
|
+
const byId = new Map(options.map(option => [option.id, option]));
|
|
36
|
+
return ids.map((id) => {
|
|
37
|
+
const option = byId.get(id);
|
|
38
|
+
return { id, label: option?.name ?? id, isDefault: option?.isDefault === true };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Find the managed skills no readable mode can show.
|
|
43
|
+
*
|
|
44
|
+
* The judgement needs no faces: the delivery status already knows which preset
|
|
45
|
+
* ids a skill declares, which presets the roster supplies, and which of those
|
|
46
|
+
* failed to resolve a standing scope. A skill whose declared ids are ALL
|
|
47
|
+
* unreachable is in no mode's face by construction — and a conflicted one is
|
|
48
|
+
* excluded from delivery entirely, so its managed copy is in no face either.
|
|
49
|
+
*
|
|
50
|
+
* An UNSCOPED managed skill (no declared ids) is deliberately NOT an orphan: the
|
|
51
|
+
* delivery serves it to every preset through the global layer, so every mode
|
|
52
|
+
* shows it. Treating it as one would put a permanent, wrong diagnostic on screen.
|
|
53
|
+
* @param skills - the managed rows (`presetScopeStatus().skills`).
|
|
54
|
+
* @param presets - every preset the roster supplies.
|
|
55
|
+
* @param failedPresets - preset ids whose standing scope could not be resolved.
|
|
56
|
+
* @returns the orphan rows, name-sorted.
|
|
57
|
+
*/
|
|
58
|
+
export function orphanManagedSkills(skills, presets, failedPresets) {
|
|
59
|
+
const known = new Set(presets.map(option => option.id));
|
|
60
|
+
const failed = new Set(failedPresets);
|
|
61
|
+
const orphans = [];
|
|
62
|
+
for (const skill of skills) {
|
|
63
|
+
if (skill.conflict !== undefined) {
|
|
64
|
+
orphans.push({
|
|
65
|
+
name: skill.name,
|
|
66
|
+
description: skill.description,
|
|
67
|
+
modelInvocable: skill.modelInvocable,
|
|
68
|
+
userInvocable: skill.userInvocable,
|
|
69
|
+
declared: skill.presets,
|
|
70
|
+
unreachable: [...skill.presets],
|
|
71
|
+
conflict: skill.conflict,
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (skill.presets.length === 0)
|
|
76
|
+
continue;
|
|
77
|
+
const unreachable = skill.presets.filter(id => !known.has(id) || failed.has(id));
|
|
78
|
+
if (unreachable.length < skill.presets.length)
|
|
79
|
+
continue;
|
|
80
|
+
orphans.push({
|
|
81
|
+
name: skill.name,
|
|
82
|
+
description: skill.description,
|
|
83
|
+
modelInvocable: skill.modelInvocable,
|
|
84
|
+
userInvocable: skill.userInvocable,
|
|
85
|
+
declared: skill.presets,
|
|
86
|
+
unreachable,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return orphans.sort((a, b) => a.name.localeCompare(b.name));
|
|
90
|
+
}
|
|
91
|
+
/** Add one capability to the union, keeping the default mode's row when it has one. */
|
|
92
|
+
function claim(byName, modes, row, preset, isDefault) {
|
|
93
|
+
const claimed = byName.get(row.name);
|
|
94
|
+
if (claimed === undefined)
|
|
95
|
+
byName.set(row.name, { row, fromDefault: isDefault });
|
|
96
|
+
else if (isDefault && !claimed.fromDefault)
|
|
97
|
+
byName.set(row.name, { row, fromDefault: true });
|
|
98
|
+
const listed = modes.get(row.name);
|
|
99
|
+
if (listed === undefined)
|
|
100
|
+
modes.set(row.name, [preset]);
|
|
101
|
+
else if (!listed.includes(preset))
|
|
102
|
+
listed.push(preset);
|
|
103
|
+
}
|
|
104
|
+
/** Union rows as a name-sorted list (the caller sorts again after filtering). */
|
|
105
|
+
function ordered(byName) {
|
|
106
|
+
return [...byName.values()].map(claimed => claimed.row).sort((a, b) => a.name.localeCompare(b.name));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Fold every mode's face into one comparison grid.
|
|
110
|
+
* @param faces - the faces the host read, in roster order.
|
|
111
|
+
* @returns the union rows, the per-capability mode lists, and the unread modes.
|
|
112
|
+
*/
|
|
113
|
+
export function buildModeComparison(faces) {
|
|
114
|
+
const skills = new Map();
|
|
115
|
+
const tools = new Map();
|
|
116
|
+
const skillModes = new Map();
|
|
117
|
+
const toolModes = new Map();
|
|
118
|
+
const unavailable = [];
|
|
119
|
+
let modes = 0;
|
|
120
|
+
for (const face of faces) {
|
|
121
|
+
const label = face.name ?? face.preset;
|
|
122
|
+
if (face.unavailable !== undefined) {
|
|
123
|
+
unavailable.push({ preset: face.preset, label, reason: face.unavailable });
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
modes += 1;
|
|
127
|
+
for (const skill of face.skills)
|
|
128
|
+
claim(skills, skillModes, skill, face.preset, face.isDefault);
|
|
129
|
+
for (const tool of face.tools)
|
|
130
|
+
claim(tools, toolModes, tool, face.preset, face.isDefault);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
skills: ordered(skills),
|
|
134
|
+
tools: ordered(tools),
|
|
135
|
+
skillModes,
|
|
136
|
+
toolModes,
|
|
137
|
+
modes,
|
|
138
|
+
unavailable,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in preset display names, localized through the host's own copy keys.
|
|
3
|
+
*
|
|
4
|
+
* The four shipped presets (standard/ptc/minimal/cordis) publish NO name of
|
|
5
|
+
* their own — surfaces are expected to resolve their copy through
|
|
6
|
+
* `presetDisplayText` from `@deepseek-ai/dsh-agent-preset-registry/display`,
|
|
7
|
+
* the pure fold the host ships exactly so browser bundles inline it. Rendering
|
|
8
|
+
* `name ?? id` leaks the raw English ids; this helper routes every roster row
|
|
9
|
+
* through the fold with this card's own dictionary (the eight preset* keys),
|
|
10
|
+
* while user-authored presets keep their published names untouched.
|
|
11
|
+
* @module @khorsheed/dsh-capability-catalog/client/preset-display
|
|
12
|
+
*/
|
|
13
|
+
import type { CapabilityCatalogKey } from './locales.ts';
|
|
14
|
+
/** One roster row or face's identity, as display copy resolution needs it. */
|
|
15
|
+
export interface PresetLike {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly name?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
/** The row's display name: localized built-in copy, else its published name (or id). */
|
|
20
|
+
export declare function presetName(preset: PresetLike, t: (key: CapabilityCatalogKey) => string): string;
|
|
21
|
+
//# sourceMappingURL=preset-display.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in preset display names, localized through the host's own copy keys.
|
|
3
|
+
*
|
|
4
|
+
* The four shipped presets (standard/ptc/minimal/cordis) publish NO name of
|
|
5
|
+
* their own — surfaces are expected to resolve their copy through
|
|
6
|
+
* `presetDisplayText` from `@deepseek-ai/dsh-agent-preset-registry/display`,
|
|
7
|
+
* the pure fold the host ships exactly so browser bundles inline it. Rendering
|
|
8
|
+
* `name ?? id` leaks the raw English ids; this helper routes every roster row
|
|
9
|
+
* through the fold with this card's own dictionary (the eight preset* keys),
|
|
10
|
+
* while user-authored presets keep their published names untouched.
|
|
11
|
+
* @module @khorsheed/dsh-capability-catalog/client/preset-display
|
|
12
|
+
*/
|
|
13
|
+
import { presetDisplayText } from '@deepseek-ai/dsh-agent-preset-registry/display';
|
|
14
|
+
/** The row's display name: localized built-in copy, else its published name (or id). */
|
|
15
|
+
export function presetName(preset, t) {
|
|
16
|
+
// exactOptionalPropertyTypes: the upstream source type spells `name?: string`,
|
|
17
|
+
// so an explicit undefined must collapse into an absent property instead.
|
|
18
|
+
const source = preset.name === undefined ? { id: preset.id } : { id: preset.id, name: preset.name };
|
|
19
|
+
return presetDisplayText(source, key => t(key)).name;
|
|
20
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-query the catalog until a mutation is visible in it.
|
|
3
|
+
*
|
|
4
|
+
* The host skill registry is not invalidated by our own writes. The catalog
|
|
5
|
+
* installs and deletes skills with `node:fs`, while the host's only synchronous
|
|
6
|
+
* invalidation path is the `fs/observed` event, which the model-facing
|
|
7
|
+
* filesystem tools raise — a plugin write raises nothing. The registry therefore
|
|
8
|
+
* catches up when its watcher notices the change (chokidar, 200 ms stability
|
|
9
|
+
* threshold), a moment AFTER the write resolves. A single refresh fired right
|
|
10
|
+
* after an install or delete thus returns the pre-change snapshot, and the grid
|
|
11
|
+
* keeps its stale rows until the panel is reopened.
|
|
12
|
+
* @module @khorsheed/dsh-capability-catalog/client/settle
|
|
13
|
+
*/
|
|
14
|
+
/** Delay schedule and sleep seam of {@link refreshUntilSettled}. */
|
|
15
|
+
export interface SettleOptions {
|
|
16
|
+
/** One attempt per entry, plus the immediate one. */
|
|
17
|
+
readonly delaysMs?: readonly number[];
|
|
18
|
+
/** How to wait between attempts. */
|
|
19
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Refresh once immediately, then keep re-querying until `settled` accepts the
|
|
23
|
+
* snapshot or the schedule runs out. The immediate attempt is deliberate: it
|
|
24
|
+
* preserves the single-refresh behaviour for every mutation the registry
|
|
25
|
+
* already knows about, and it is the only attempt that can beat the watcher.
|
|
26
|
+
* @param read - the newest snapshot, or undefined before one lands.
|
|
27
|
+
* @param refresh - one query; resolves once the store holds its result.
|
|
28
|
+
* @param settled - accepts the snapshot when the mutation is visible in it.
|
|
29
|
+
* @param options - delay schedule and sleep seam.
|
|
30
|
+
* @returns whether the snapshot settled before the schedule ran out.
|
|
31
|
+
*/
|
|
32
|
+
export declare function refreshUntilSettled<T>(read: () => T | undefined, refresh: () => Promise<void>, settled: (value: T) => boolean, options?: SettleOptions): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* The skill names an add-skill result reported. The host joins a multi-skill
|
|
35
|
+
* install with `', '` (e.g. `--skill '*'`), so the caller cannot treat it as one
|
|
36
|
+
* name.
|
|
37
|
+
* @param name - the result's `name` field.
|
|
38
|
+
*/
|
|
39
|
+
export declare function installedNames(name: string | undefined): readonly string[];
|
|
40
|
+
//# sourceMappingURL=settle.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-query the catalog until a mutation is visible in it.
|
|
3
|
+
*
|
|
4
|
+
* The host skill registry is not invalidated by our own writes. The catalog
|
|
5
|
+
* installs and deletes skills with `node:fs`, while the host's only synchronous
|
|
6
|
+
* invalidation path is the `fs/observed` event, which the model-facing
|
|
7
|
+
* filesystem tools raise — a plugin write raises nothing. The registry therefore
|
|
8
|
+
* catches up when its watcher notices the change (chokidar, 200 ms stability
|
|
9
|
+
* threshold), a moment AFTER the write resolves. A single refresh fired right
|
|
10
|
+
* after an install or delete thus returns the pre-change snapshot, and the grid
|
|
11
|
+
* keeps its stale rows until the panel is reopened.
|
|
12
|
+
* @module @khorsheed/dsh-capability-catalog/client/settle
|
|
13
|
+
*/
|
|
14
|
+
/** Delays between attempts: past the watcher's ~200 ms settle, bounded so the UI never spins. */
|
|
15
|
+
const DEFAULT_DELAYS_MS = [250, 500, 1000, 1500];
|
|
16
|
+
/** Await a real delay; tests inject their own so they never touch the clock. */
|
|
17
|
+
const realSleep = async (ms) => {
|
|
18
|
+
await new Promise((resolve) => { setTimeout(resolve, ms); });
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Refresh once immediately, then keep re-querying until `settled` accepts the
|
|
22
|
+
* snapshot or the schedule runs out. The immediate attempt is deliberate: it
|
|
23
|
+
* preserves the single-refresh behaviour for every mutation the registry
|
|
24
|
+
* already knows about, and it is the only attempt that can beat the watcher.
|
|
25
|
+
* @param read - the newest snapshot, or undefined before one lands.
|
|
26
|
+
* @param refresh - one query; resolves once the store holds its result.
|
|
27
|
+
* @param settled - accepts the snapshot when the mutation is visible in it.
|
|
28
|
+
* @param options - delay schedule and sleep seam.
|
|
29
|
+
* @returns whether the snapshot settled before the schedule ran out.
|
|
30
|
+
*/
|
|
31
|
+
export async function refreshUntilSettled(read, refresh, settled, options = {}) {
|
|
32
|
+
const delays = options.delaysMs ?? DEFAULT_DELAYS_MS;
|
|
33
|
+
const sleep = options.sleep ?? realSleep;
|
|
34
|
+
const accepts = () => {
|
|
35
|
+
const value = read();
|
|
36
|
+
return value !== undefined && settled(value);
|
|
37
|
+
};
|
|
38
|
+
await refresh();
|
|
39
|
+
if (accepts())
|
|
40
|
+
return true;
|
|
41
|
+
for (const delay of delays) {
|
|
42
|
+
await sleep(delay);
|
|
43
|
+
await refresh();
|
|
44
|
+
if (accepts())
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The skill names an add-skill result reported. The host joins a multi-skill
|
|
51
|
+
* install with `', '` (e.g. `--skill '*'`), so the caller cannot treat it as one
|
|
52
|
+
* name.
|
|
53
|
+
* @param name - the result's `name` field.
|
|
54
|
+
*/
|
|
55
|
+
export function installedNames(name) {
|
|
56
|
+
return (name ?? '').split(',').map(part => part.trim()).filter(part => part !== '');
|
|
57
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot-facing types of the capability-catalog client half: the settings
|
|
3
|
+
* section (a standalone nav tab) and its injected data face.
|
|
4
|
+
*/
|
|
5
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
6
|
+
import type { CapabilityCatalogSnapshot, CatalogAddSkillRequest, CatalogDirSkillInfo, CatalogDeleteSkillResult, CatalogMcpServerConfig, CatalogMcpSnapshot, CatalogMcpTool, CatalogModeFace, CatalogPresetOption, CatalogPresetScopeEditResult, CatalogPresetScopeStatus, CatalogSkillDetail, CatalogSkillFileRead } from '@khorsheed/dsh-capability-catalog/types';
|
|
7
|
+
/** A tiny external store holding the last catalog snapshot (re-created on change). */
|
|
8
|
+
export interface CatalogHook {
|
|
9
|
+
readonly getSnapshot: () => CapabilityCatalogSnapshot | undefined;
|
|
10
|
+
readonly subscribe: (listener: () => void) => () => void;
|
|
11
|
+
/** Re-read one mode's face; `presetId` omitted reads the deployment default. */
|
|
12
|
+
readonly refresh: (presetId?: string) => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/** Injected data face of the settings section. */
|
|
15
|
+
export interface CapabilityCatalogInjected {
|
|
16
|
+
hooks: {
|
|
17
|
+
/** The catalog snapshot store; the section binds `useCatalog` to it. */
|
|
18
|
+
catalog: CatalogHook;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Re-fetch the catalog snapshot after a mutation (e.g. add-skill). `presetId`
|
|
22
|
+
* names the mode the grid is showing, so a mutation never drags the grid back
|
|
23
|
+
* to the default mode's face.
|
|
24
|
+
*/
|
|
25
|
+
refresh: (presetId?: string) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Re-fetch until `settled` accepts the snapshot, on a bounded backoff. Use it
|
|
28
|
+
* for mutations whose visibility depends on the host's skill watcher (adding
|
|
29
|
+
* or deleting a skill): the write lands before the registry is invalidated, so
|
|
30
|
+
* one immediate refresh returns the pre-change snapshot.
|
|
31
|
+
*/
|
|
32
|
+
refreshSettled: (settled: (snapshot: CapabilityCatalogSnapshot) => boolean, presetId?: string) => Promise<boolean>;
|
|
33
|
+
/** Load one skill's full detail (content + metadata + credentials) at a mode's scope. */
|
|
34
|
+
detail: (name: string, presetId?: string) => Promise<CatalogSkillDetail | undefined>;
|
|
35
|
+
/** Read one skill-bundle file's text content on demand. */
|
|
36
|
+
readSkillFile: (name: string, path: string, presetId?: string) => Promise<CatalogSkillFileRead | undefined>;
|
|
37
|
+
/** List the skills inside a local container dir (add-skill chooser). */
|
|
38
|
+
listDirSkills: (dirPath: string) => Promise<readonly CatalogDirSkillInfo[]>;
|
|
39
|
+
/** Set one declared credential value. */
|
|
40
|
+
setCredential: (key: string, value: string) => Promise<boolean>;
|
|
41
|
+
/** Add a skill via a full request (upload zip/text or clone-from-source). */
|
|
42
|
+
addSkill: (request: CatalogAddSkillRequest) => Promise<{
|
|
43
|
+
ok: boolean;
|
|
44
|
+
error?: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
exists?: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
/** Delete a catalog-owned file skill (rejects built-in/plugin-provided). */
|
|
49
|
+
deleteSkill: (name: string, presetId?: string) => Promise<CatalogDeleteSkillResult>;
|
|
50
|
+
/** Open the host's native directory chooser (the workspace "add" dialog). */
|
|
51
|
+
pickDirectory: () => Promise<string | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Every mode's capability face, for the cross-mode comparison view and for a
|
|
54
|
+
* detail modal asking where a non-writable skill actually loads. Read once per
|
|
55
|
+
* session behind a single-flight cache; `force` re-reads after a mutation that
|
|
56
|
+
* can change a face.
|
|
57
|
+
*/
|
|
58
|
+
modeFaces: (force?: boolean) => Promise<readonly CatalogModeFace[]>;
|
|
59
|
+
/** MCP: full management snapshot (servers + tools + credential state). */
|
|
60
|
+
mcpSnapshot: () => Promise<CatalogMcpSnapshot>;
|
|
61
|
+
/** MCP: add/replace a server config. */
|
|
62
|
+
mcpAdd: (config: CatalogMcpServerConfig) => Promise<boolean>;
|
|
63
|
+
/** MCP: remove a server. */
|
|
64
|
+
mcpRemove: (serverName: string) => Promise<boolean>;
|
|
65
|
+
/** MCP: set server-level enable flag. */
|
|
66
|
+
mcpSetEnabled: (serverName: string, enabled: boolean) => Promise<void>;
|
|
67
|
+
/** MCP: set one credential value. */
|
|
68
|
+
mcpSetCredential: (ref: string, value: string) => Promise<boolean>;
|
|
69
|
+
/** MCP: set one tool's enable flag. */
|
|
70
|
+
mcpSetToolEnabled: (serverName: string, tool: string, enabled: boolean) => Promise<void>;
|
|
71
|
+
/** MCP: connect + discover a server's tools. */
|
|
72
|
+
mcpDiscover: (serverName: string) => Promise<readonly CatalogMcpTool[]>;
|
|
73
|
+
/** Preset-scoped delivery status of the plugin's managed skill root. */
|
|
74
|
+
presetScopeStatus: () => Promise<CatalogPresetScopeStatus | undefined>;
|
|
75
|
+
/** Every preset the roster supplies, for the scope picker. */
|
|
76
|
+
presetScopeRoster: () => Promise<readonly CatalogPresetOption[]>;
|
|
77
|
+
/** Declare which presets one managed skill is delivered to. */
|
|
78
|
+
presetScopeSet: (name: string, presets: readonly string[]) => Promise<CatalogPresetScopeEditResult>;
|
|
79
|
+
/** Move an installed skill into the managed root with a preset scope. */
|
|
80
|
+
presetScopeAdopt: (name: string, presets: readonly string[]) => Promise<CatalogPresetScopeEditResult>;
|
|
81
|
+
/** Move a managed skill back to the user skill root. */
|
|
82
|
+
presetScopeRelease: (name: string) => Promise<CatalogPresetScopeEditResult>;
|
|
83
|
+
}
|
|
84
|
+
/** Full props of the settings.section entry (a standalone nav tab). */
|
|
85
|
+
export type CapabilityCatalogCardProps = PropsRuntime<'settings.section'> & InjectFace<CapabilityCatalogInjected> & PropsLocale<'capability-catalog'>;
|
|
86
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill env hint via `tools/post-execute` — a runtime companion context that
|
|
3
|
+
* tells the model about any skill's configured credential env mappings.
|
|
4
|
+
*
|
|
5
|
+
* The catalog injects credentials as trusted per-execution `DSH_<KEY>` env
|
|
6
|
+
* vars (see shellEnv.ts), but a skill's own text references the original
|
|
7
|
+
* `$KEY` name and the model does not derive the alias on its own. This module
|
|
8
|
+
* observes `tools/post-execute` when the `skill` tool loads a skill and appends
|
|
9
|
+
* a generic, per-skill note listing each configured mapping, so the model can
|
|
10
|
+
* use the value without us modifying the user's SKILL.md.
|
|
11
|
+
*
|
|
12
|
+
* Generic: it derives the mappings from the loaded skill's own env-decl keys
|
|
13
|
+
* (decodeEnvDecls) + the credential store, so any future key / skill is
|
|
14
|
+
* covered. It never touches the skill file or the provider's returned content.
|
|
15
|
+
* @module @khorsheed/dsh-capability-catalog/envHint
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
/** Subscribe to `tools/post-execute`; appends an env-mapping hint for the
|
|
19
|
+
* loaded skill. Returns a disposer. Degrades silently when tools/registry/
|
|
20
|
+
* credentials are absent or the tool is not the skills loader. */
|
|
21
|
+
export declare function installSkillEnvHint(ctx: Context, getScope: () => Promise<unknown | undefined>): void;
|
|
22
|
+
//# sourceMappingURL=envHint.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill env hint via `tools/post-execute` — a runtime companion context that
|
|
3
|
+
* tells the model about any skill's configured credential env mappings.
|
|
4
|
+
*
|
|
5
|
+
* The catalog injects credentials as trusted per-execution `DSH_<KEY>` env
|
|
6
|
+
* vars (see shellEnv.ts), but a skill's own text references the original
|
|
7
|
+
* `$KEY` name and the model does not derive the alias on its own. This module
|
|
8
|
+
* observes `tools/post-execute` when the `skill` tool loads a skill and appends
|
|
9
|
+
* a generic, per-skill note listing each configured mapping, so the model can
|
|
10
|
+
* use the value without us modifying the user's SKILL.md.
|
|
11
|
+
*
|
|
12
|
+
* Generic: it derives the mappings from the loaded skill's own env-decl keys
|
|
13
|
+
* (decodeEnvDecls) + the credential store, so any future key / skill is
|
|
14
|
+
* covered. It never touches the skill file or the provider's returned content.
|
|
15
|
+
* @module @khorsheed/dsh-capability-catalog/envHint
|
|
16
|
+
*/
|
|
17
|
+
import { decodeCredentialDecls, decodeEnvDecls, mergeCredentialDecls, resolveServices } from './skills.js';
|
|
18
|
+
const DSH_PREFIX = 'DSH_';
|
|
19
|
+
/** Subscribe to `tools/post-execute`; appends an env-mapping hint for the
|
|
20
|
+
* loaded skill. Returns a disposer. Degrades silently when tools/registry/
|
|
21
|
+
* credentials are absent or the tool is not the skills loader. */
|
|
22
|
+
export function installSkillEnvHint(ctx, getScope) {
|
|
23
|
+
// Subscribe inside the DEFERRED tools inject — a one-shot ctx.get probe loses
|
|
24
|
+
// the hint if tools mounts after this plugin (silently degrading it with no
|
|
25
|
+
// error), while inject fires when the registry appears and never in a
|
|
26
|
+
// composition without one (dropping only the hint, never the boot).
|
|
27
|
+
ctx.inject(['tools'], () => {
|
|
28
|
+
ctx.on('tools/post-execute', (async (exec, _result, next) => {
|
|
29
|
+
if (exec?.name !== 'skill')
|
|
30
|
+
return next();
|
|
31
|
+
const skillName = exec.arguments?.name;
|
|
32
|
+
if (typeof skillName !== 'string' || skillName.length === 0)
|
|
33
|
+
return next();
|
|
34
|
+
const scope = await getScope().catch(() => undefined);
|
|
35
|
+
const { registry, credentials } = resolveServices(ctx);
|
|
36
|
+
if (registry === undefined || credentials === undefined)
|
|
37
|
+
return next();
|
|
38
|
+
const lookup = scope === undefined ? {} : { scope };
|
|
39
|
+
let def;
|
|
40
|
+
try {
|
|
41
|
+
def = await registry.get(skillName, lookup);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return next();
|
|
45
|
+
}
|
|
46
|
+
if (def === undefined)
|
|
47
|
+
return next();
|
|
48
|
+
const decls = mergeCredentialDecls(decodeCredentialDecls(def.metadata), decodeEnvDecls(def.content ?? ''));
|
|
49
|
+
const configured = [];
|
|
50
|
+
for (const decl of decls) {
|
|
51
|
+
const info = await credentials.describe(decl.key).catch(() => undefined);
|
|
52
|
+
if (info?.configured === true)
|
|
53
|
+
configured.push(decl.key);
|
|
54
|
+
}
|
|
55
|
+
if (configured.length === 0)
|
|
56
|
+
return next();
|
|
57
|
+
const lines = [
|
|
58
|
+
`Runtime credential mapping for ${skillName}:`,
|
|
59
|
+
...configured.map(k => `- ${k} is configured; in the model shell it is available as ${DSH_PREFIX}${k}. Use it as ${k}="${DSH_PREFIX}${k}" <command>.`),
|
|
60
|
+
`Do not search dotenv files or ask the user for this credential, and do not print either variable.`,
|
|
61
|
+
];
|
|
62
|
+
const hint = lines.join('\n');
|
|
63
|
+
return {
|
|
64
|
+
kind: 'accept',
|
|
65
|
+
additionalContexts: [{
|
|
66
|
+
id: `capability-catalog-env-hint-${skillName}`,
|
|
67
|
+
role: 'user',
|
|
68
|
+
content: [{ type: 'text', text: hint }],
|
|
69
|
+
// Producer-owned kind: the retired {kind: 'plugin'} wrapper fails the
|
|
70
|
+
// rc.1 native source admission at the durable write (turn-killing).
|
|
71
|
+
source: { kind: 'capability-catalog', plugin: 'capability-catalog', form: 'env-hint' },
|
|
72
|
+
}],
|
|
73
|
+
};
|
|
74
|
+
}));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add-skill import: write a skill from a pasted SKILL.md payload or an uploaded
|
|
3
|
+
* zip archive into the managed skills root and extract its name/frontmatter.
|
|
4
|
+
*
|
|
5
|
+
* A skill is a directory bundle (`<name>/SKILL.md` plus optional
|
|
6
|
+
* `references`/`scripts`/`assets`) or a single flat file, discovered by the
|
|
7
|
+
* skill-filesystem watcher from one of the managed roots. So "installing" a
|
|
8
|
+
* skill is just placing it under the root; nothing executes the payload.
|
|
9
|
+
*
|
|
10
|
+
* The zip path reads the archive with the dependency-free zip reader
|
|
11
|
+
* (`./zip.ts`, node:zlib only), locates the `SKILL.md`, derives the kebab-case
|
|
12
|
+
* `name` from frontmatter, and extracts the bundle under `<root>/<name>/`
|
|
13
|
+
* (strip the archive's own wrapping folder if present). The clone path resolves
|
|
14
|
+
* a repo spec — including a whole `npx skills add <repo> --skill <name>` command
|
|
15
|
+
* copied from a skill's README — clones it into a scratch directory, and lifts
|
|
16
|
+
* the selected skill bundle(s) into the root.
|
|
17
|
+
* @module @khorsheed/dsh-capability-catalog/import
|
|
18
|
+
*/
|
|
19
|
+
import type { CatalogAddSkillRequest, CatalogAddSkillResult, CatalogDirSkillInfo } from './types.ts';
|
|
20
|
+
/** Resolve the managed root directory. */
|
|
21
|
+
export declare function managedRoot(root: 'user' | 'project', dshHome: string): string;
|
|
22
|
+
/** Extract the kebab-case `name` and `description` from a SKILL.md body. */
|
|
23
|
+
export declare function resolveSkillNameFromContent(content: string): {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
} | undefined;
|
|
27
|
+
/** Read the frontmatter `disable-model-invocation` flag (default model-invocable true). */
|
|
28
|
+
export declare function inferModelInvocable(content: string): boolean;
|
|
29
|
+
/** True when a base64 payload decodes to a ZIP archive (magic bytes). */
|
|
30
|
+
export declare function isZipPayload(payload: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Add a skill from a base64 SKILL.md text payload into the managed root.
|
|
33
|
+
* @param request - the add-skill request.
|
|
34
|
+
* @param dshHome - the dsh home root.
|
|
35
|
+
*/
|
|
36
|
+
export declare function addSkillFromText(request: CatalogAddSkillRequest, dshHome: string): Promise<CatalogAddSkillResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Add a skill from a base64 ZIP archive payload into the managed root.
|
|
39
|
+
*
|
|
40
|
+
* The archive should contain a skill bundle. If the `SKILL.md` sits directly at
|
|
41
|
+
* the archive root it is extracted as-is; if it sits under a single wrapping
|
|
42
|
+
* folder that folder is stripped so the bundle lands at `<root>/<name>/`. Path
|
|
43
|
+
* traversal entries are skipped; the frontmatter name drives the target folder.
|
|
44
|
+
* @param request - the add-skill request.
|
|
45
|
+
* @param dshHome - the dsh home root.
|
|
46
|
+
*/
|
|
47
|
+
export declare function addSkillFromZip(request: CatalogAddSkillRequest, dshHome: string): Promise<CatalogAddSkillResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Add a skill from a base64 payload into the managed root, auto-detecting a
|
|
50
|
+
* ZIP archive (magic bytes) from a raw SKILL.md text payload.
|
|
51
|
+
* @param request - the add-skill request.
|
|
52
|
+
* @param dshHome - the dsh home root.
|
|
53
|
+
*/
|
|
54
|
+
export declare function addSkillFromPayload(request: CatalogAddSkillRequest, dshHome: string): Promise<CatalogAddSkillResult>;
|
|
55
|
+
/** A parsed `command`-channel spec: the source reference plus its `--skill` selections. */
|
|
56
|
+
interface RepoSpec {
|
|
57
|
+
/** owner/repo or a git URL (a local path is copied before this parse runs). */
|
|
58
|
+
readonly ref: string;
|
|
59
|
+
/** Sub-skill names from `--skill`/`-s`, matching the CLI's own greedy values. */
|
|
60
|
+
readonly skills: readonly string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse a `command`-channel spec — `owner/repo`, a git URL, or a whole
|
|
64
|
+
* `npx skills add <repo> [--skill <name>]… [-g]` command — into its source
|
|
65
|
+
* reference and `--skill` selections.
|
|
66
|
+
*
|
|
67
|
+
* Every flag token is consumed here, never left in the string: the spec used to
|
|
68
|
+
* be handed to `git clone` after only a prefix and a trailing `-g` were
|
|
69
|
+
* stripped, so the README form `npx skills add owner/repo --skill name` became
|
|
70
|
+
* the clone URL `https://github.com/owner/repo --skill name`, which git rejects
|
|
71
|
+
* as a malformed URL. The recognized set mirrors the `skills` CLI's own `add`
|
|
72
|
+
* parser (vercel-labs/skills) so a command copied from a skill's README installs
|
|
73
|
+
* what it says: a flag's value is consumed with it, never glued onto the
|
|
74
|
+
* reference.
|
|
75
|
+
* @param spec - the raw spec from the add-skill command channel.
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseRepoSpec(spec: string): RepoSpec | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Install a skill by cloning a source repo (owner/repo, a git URL, a host-prefixed
|
|
80
|
+
* `github.com/owner/repo[/path]`, a copied browser URL, or a whole
|
|
81
|
+
* `npx skills add <repo> [--skill <name>]` command) into the managed root. This
|
|
82
|
+
* is the dsh-native "install from source" — a plain `npx skills add` writes into
|
|
83
|
+
* an external skills dir the dsh filesystem watcher does not scan.
|
|
84
|
+
*
|
|
85
|
+
* The clone lands in a scratch directory; the chosen skill bundle (the repo's
|
|
86
|
+
* only one, `request.skills`, or the command's `--skill` values) is then lifted
|
|
87
|
+
* to `<managed root>/<frontmatter name>/`. A repo carrying several skills and no
|
|
88
|
+
* selection is refused with the list rather than installing the shallowest one.
|
|
89
|
+
* A spec naming a path inside the repo (`…/skills/<name>`) searches only that
|
|
90
|
+
* path.
|
|
91
|
+
* @param request - the add-skill request (channel 'command', repo carries the spec).
|
|
92
|
+
* @param dshHome - the dsh home root.
|
|
93
|
+
*/
|
|
94
|
+
export declare function commandInstall(request: CatalogAddSkillRequest, dshHome: string): Promise<CatalogAddSkillResult>;
|
|
95
|
+
/**
|
|
96
|
+
* List the skills inside a local dir for the add-skill chooser. If the dir is a
|
|
97
|
+
* **skill container** (a set of `<name>/SKILL.md` sub-dirs) the sub-skills are
|
|
98
|
+
* listed as `kind:'child'`; if the dir is **itself a single skill bundle** (a
|
|
99
|
+
* `SKILL.md` at its root) it is listed as one `kind:'self'` entry so the user can
|
|
100
|
+
* install the directory as-is. Both shapes are returned when a dir has a root
|
|
101
|
+
* `SKILL.md` AND child skill dirs.
|
|
102
|
+
* @param dirPath - the local directory path (may be `~`-prefixed).
|
|
103
|
+
*/
|
|
104
|
+
export declare function listDirSkills(dirPath: string): Promise<readonly CatalogDirSkillInfo[]>;
|
|
105
|
+
export {};
|
|
106
|
+
//# sourceMappingURL=import.d.ts.map
|