@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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +286 -0
  4. package/README.zh.md +138 -0
  5. package/cordis.patch.yml +8 -0
  6. package/lib/client.js +10100 -0
  7. package/lib/index.js +4022 -0
  8. package/lib/invariant.js +25 -0
  9. package/lib/mcpConnector-C2TbkPBY.js +69 -0
  10. package/lib/typert.host.d.ts +3 -0
  11. package/lib/typert.host.js +1417 -0
  12. package/lib/typert.remote-client.d.ts +68 -0
  13. package/lib/typert.remote-client.js +1136 -0
  14. package/lib/types/capabilities.d.ts +103 -0
  15. package/lib/types/capabilities.js +116 -0
  16. package/lib/types/channels.d.ts +41 -0
  17. package/lib/types/channels.js +75 -0
  18. package/lib/types/client/AddMcpDialog.d.ts +13 -0
  19. package/lib/types/client/AddMcpDialog.js +79 -0
  20. package/lib/types/client/AddSkillModal.d.ts +17 -0
  21. package/lib/types/client/AddSkillModal.js +138 -0
  22. package/lib/types/client/BundleFileTree.d.ts +7 -0
  23. package/lib/types/client/BundleFileTree.js +74 -0
  24. package/lib/types/client/CapabilityCatalogCard.d.ts +57 -0
  25. package/lib/types/client/CapabilityCatalogCard.js +479 -0
  26. package/lib/types/client/CredentialField.d.ts +14 -0
  27. package/lib/types/client/CredentialField.js +9 -0
  28. package/lib/types/client/McpCards.d.ts +13 -0
  29. package/lib/types/client/McpCards.js +12 -0
  30. package/lib/types/client/McpServerManageModal.d.ts +18 -0
  31. package/lib/types/client/McpServerManageModal.js +63 -0
  32. package/lib/types/client/MetadataRow.d.ts +10 -0
  33. package/lib/types/client/MetadataRow.js +6 -0
  34. package/lib/types/client/ModalShell.d.ts +16 -0
  35. package/lib/types/client/ModalShell.js +27 -0
  36. package/lib/types/client/ModeChips.d.ts +38 -0
  37. package/lib/types/client/ModeChips.js +42 -0
  38. package/lib/types/client/SchemaView.d.ts +11 -0
  39. package/lib/types/client/SchemaView.js +143 -0
  40. package/lib/types/client/SkillCards.d.ts +28 -0
  41. package/lib/types/client/SkillCards.js +43 -0
  42. package/lib/types/client/SkillDetailModal.d.ts +69 -0
  43. package/lib/types/client/SkillDetailModal.js +167 -0
  44. package/lib/types/client/ToolCards.d.ts +28 -0
  45. package/lib/types/client/ToolCards.js +52 -0
  46. package/lib/types/client/ToolDetailModal.d.ts +12 -0
  47. package/lib/types/client/ToolDetailModal.js +29 -0
  48. package/lib/types/client/icons.d.ts +26 -0
  49. package/lib/types/client/icons.js +10 -0
  50. package/lib/types/client/index.d.ts +21 -0
  51. package/lib/types/client/index.js +196 -0
  52. package/lib/types/client/locales.d.ts +392 -0
  53. package/lib/types/client/locales.js +395 -0
  54. package/lib/types/client/mcp-model.d.ts +24 -0
  55. package/lib/types/client/mcp-model.js +87 -0
  56. package/lib/types/client/mode-model.d.ts +108 -0
  57. package/lib/types/client/mode-model.js +140 -0
  58. package/lib/types/client/preset-display.d.ts +21 -0
  59. package/lib/types/client/preset-display.js +20 -0
  60. package/lib/types/client/settle.d.ts +40 -0
  61. package/lib/types/client/settle.js +57 -0
  62. package/lib/types/client/slots.d.ts +86 -0
  63. package/lib/types/client/slots.js +1 -0
  64. package/lib/types/envHint.d.ts +22 -0
  65. package/lib/types/envHint.js +76 -0
  66. package/lib/types/import.d.ts +106 -0
  67. package/lib/types/import.js +635 -0
  68. package/lib/types/index.d.ts +216 -0
  69. package/lib/types/index.js +739 -0
  70. package/lib/types/invariant.d.ts +16 -0
  71. package/lib/types/invariant.js +23 -0
  72. package/lib/types/mcpConnector.d.ts +24 -0
  73. package/lib/types/mcpConnector.js +73 -0
  74. package/lib/types/mcpStore.d.ts +68 -0
  75. package/lib/types/mcpStore.js +235 -0
  76. package/lib/types/mcpTools.d.ts +45 -0
  77. package/lib/types/mcpTools.js +125 -0
  78. package/lib/types/mcps.d.ts +64 -0
  79. package/lib/types/mcps.js +157 -0
  80. package/lib/types/modes.d.ts +65 -0
  81. package/lib/types/modes.js +97 -0
  82. package/lib/types/namespace.d.ts +9 -0
  83. package/lib/types/namespace.js +8 -0
  84. package/lib/types/official-tools.d.ts +3 -0
  85. package/lib/types/official-tools.js +44 -0
  86. package/lib/types/preset-scope.d.ts +105 -0
  87. package/lib/types/preset-scope.js +117 -0
  88. package/lib/types/remote.d.ts +48 -0
  89. package/lib/types/remote.js +120 -0
  90. package/lib/types/scoped-delivery.d.ts +243 -0
  91. package/lib/types/scoped-delivery.js +686 -0
  92. package/lib/types/scoped-edits.d.ts +90 -0
  93. package/lib/types/scoped-edits.js +234 -0
  94. package/lib/types/settings.d.ts +28 -0
  95. package/lib/types/settings.js +28 -0
  96. package/lib/types/shellEnv.d.ts +65 -0
  97. package/lib/types/shellEnv.js +232 -0
  98. package/lib/types/skills.d.ts +103 -0
  99. package/lib/types/skills.js +303 -0
  100. package/lib/types/tool-origin.d.ts +29 -0
  101. package/lib/types/tool-origin.js +30 -0
  102. package/lib/types/types.d.ts +315 -0
  103. package/lib/types/types.js +8 -0
  104. package/lib/types/zip.d.ts +24 -0
  105. package/lib/types/zip.js +80 -0
  106. package/locale/en.json +6 -0
  107. package/locale/zh.json +6 -0
  108. package/package.json +119 -0
  109. package/scripts/gen-official-tools.mts +52 -0
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Wire types of the capability catalog: the snapshot the host half collects,
3
+ * the model-facing tool output, and the Remote data contract. The browser
4
+ * card parses the tool's JSON text result or the Remote response back into
5
+ * these shapes, so they stay plain JSON-safe objects with no class identity.
6
+ * @module @khorsheed/dsh-capability-catalog/types
7
+ */
8
+ /** Arbitrary JSON value — a locally-declared recursive type the typert Remote
9
+ * boundary accepts (arbitrary tool parameter schemas are unconstrained JSON). */
10
+ export type CatalogJsonValue = null | boolean | number | string | readonly CatalogJsonValue[] | {
11
+ readonly [key: string]: CatalogJsonValue;
12
+ };
13
+ /** One skill row in the catalog (summary layer — no body / metadata / rank). */
14
+ export interface CatalogSkillRow {
15
+ readonly name: string;
16
+ /** Short routing description. */
17
+ readonly description: string;
18
+ /** Registration source bucket: project-dsh / project-agents / runtime / user-dsh / user-agents / custom / bundled. */
19
+ readonly source: string;
20
+ /** Provider that owns the skill body (e.g. `filesystem`, `skill-badge`, plugin name). */
21
+ readonly provider: string;
22
+ /** Whether the model may load it (in catalog). */
23
+ readonly modelInvocable: boolean;
24
+ /** Whether a user may invoke it via `/name`. */
25
+ readonly userInvocable: boolean;
26
+ /** Optional extra routing guidance. */
27
+ readonly whenToUse?: string;
28
+ /** Last-modified time (epoch ms) of the skill body, when a local bundle exists. */
29
+ readonly updatedAt?: number;
30
+ /**
31
+ * sha256 of the skill's SKILL.md body — the one part of a skill that IS a
32
+ * capability (the procedure the model executes), carried so the capability
33
+ * fingerprint can be taken from a snapshot alone. Absent when the body
34
+ * could not be read (a remote/opaque skill, or a registry that declines
35
+ * the load).
36
+ */
37
+ readonly bodySha?: string;
38
+ }
39
+ /** Credential declared by a skill's metadata (the plugin-defined convention). */
40
+ export interface CatalogCredentialDecl {
41
+ /** credential key, e.g. `wechat-reading/api-key`. */
42
+ readonly key: string;
43
+ /** Human label shown in the config block. */
44
+ readonly label?: string;
45
+ }
46
+ /** Credential config state for one declared credential. */
47
+ export interface CatalogCredentialState {
48
+ readonly key: string;
49
+ readonly label?: string;
50
+ /** Whether a value has been configured (never the value itself). */
51
+ readonly configured: boolean;
52
+ }
53
+ /** Channel attribution for one tool row. */
54
+ export type CatalogToolChannel = 'builtin' | 'plugin' | 'mcp' | 'unknown';
55
+ /** One tool row in the catalog. */
56
+ export interface CatalogToolRow {
57
+ /** Wire tool name as registered. */
58
+ readonly name: string;
59
+ /** Model-facing description. */
60
+ readonly description: string;
61
+ /** Attribution result. */
62
+ readonly channel: CatalogToolChannel;
63
+ /** Attribution confidence: exact for prefix/whitelist hits, inferred for baseline-diff hits. */
64
+ readonly confidence: 'exact' | 'inferred';
65
+ /** MCP server name when channel === 'mcp' (longest-match resolved). */
66
+ readonly serverName?: string;
67
+ /** Model-facing input parameters (JSON value), for the detail view. */
68
+ readonly parameters?: CatalogJsonValue;
69
+ /** Registrant package/owner when known. */
70
+ readonly owner?: string;
71
+ }
72
+ /** One MCP server observed in the profile patch layers. */
73
+ /** MCP transport kind (matches dsh-mcp-client). */
74
+ export type McpTransport = 'stdio' | 'streamable-http';
75
+ /** One detected credential declared by an MCP server config (env/header/query). */
76
+ export interface CatalogMcpCredentialDecl {
77
+ /** Config key that holds the secret (env var name, header name, or 'query:key'). */
78
+ readonly ref: string;
79
+ /** Human label (e.g. the env var / header / query param name). */
80
+ readonly label: string;
81
+ /** Whether the credential has been configured (never the value). */
82
+ readonly configured: boolean;
83
+ }
84
+ /** A configured MCP server (the catalog's persisted form). */
85
+ export interface CatalogMcpServerConfig {
86
+ readonly serverName: string;
87
+ readonly transport: McpTransport;
88
+ /** stdio */
89
+ readonly command?: string;
90
+ readonly args?: readonly string[];
91
+ readonly cwd?: string;
92
+ /** streamable-http */
93
+ readonly url?: string;
94
+ readonly headers?: readonly [string, string][];
95
+ /** env (value is a secretRef marker if it came from a secret). */
96
+ readonly env?: readonly [string, string][];
97
+ /** Whether the server is enabled (connected + injected). */
98
+ readonly enabled: boolean;
99
+ }
100
+ /** One discovered tool of an MCP server. */
101
+ export interface CatalogMcpTool {
102
+ readonly name: string;
103
+ readonly description: string;
104
+ readonly parameters?: CatalogJsonValue;
105
+ /** Per-tool enable/disable. */
106
+ readonly enabled: boolean;
107
+ }
108
+ /** The catalog's own MCP server management snapshot. */
109
+ export interface CatalogMcpSnapshot {
110
+ readonly servers: readonly CatalogMcpServerConfig[];
111
+ readonly tools: Readonly<Record<string, readonly CatalogMcpTool[]>>;
112
+ readonly credentials: readonly CatalogMcpCredentialDecl[];
113
+ }
114
+ export interface CatalogMcpServerRow {
115
+ readonly name: string;
116
+ readonly toolCount: number;
117
+ }
118
+ /** Channel-count summary. */
119
+ export interface CatalogChannelSummary {
120
+ readonly channel: string;
121
+ readonly count: number;
122
+ }
123
+ /** Full catalog snapshot. */
124
+ export interface CapabilityCatalogSnapshot {
125
+ readonly skills: readonly CatalogSkillRow[];
126
+ readonly tools: readonly CatalogToolRow[];
127
+ readonly mcpServers: readonly CatalogMcpServerRow[];
128
+ readonly channels: readonly CatalogChannelSummary[];
129
+ /**
130
+ * The capability hash of this snapshot (`hashOf`), filled by the host half.
131
+ * It is NOT part of the canonical form it digests — a snapshot carrying it
132
+ * and the same snapshot without it hash alike.
133
+ */
134
+ readonly sha?: string;
135
+ /** The agent preset this snapshot was taken under; absent when none resolved. */
136
+ readonly preset?: string;
137
+ }
138
+ /** Fresh detail of one skill (body + metadata), loaded on demand. */
139
+ export interface CatalogSkillDetail {
140
+ readonly name: string;
141
+ readonly description: string;
142
+ readonly source: string;
143
+ readonly provider: string;
144
+ readonly modelInvocable: boolean;
145
+ readonly userInvocable: boolean;
146
+ readonly path?: string;
147
+ readonly whenToUse?: string;
148
+ /** Full SKILL.md body. */
149
+ readonly content: string;
150
+ /** Parsed frontmatter metadata, serialized to JSON text (typert boundary is strict JSON). */
151
+ readonly metadataText?: string;
152
+ /** Declared credentials + their configured state (metadata.credentials only). */
153
+ readonly credentials?: readonly CatalogCredentialState[];
154
+ /** Env-var references detected in the skill body (read-only, informational —
155
+ * typically provided by the runtime/caller, NOT user-configurable here). */
156
+ readonly environmentRefs?: readonly string[];
157
+ /** Bundle-relative file paths (SKILL.md + scripts/assets/references) for a directory side. */
158
+ readonly files?: readonly string[];
159
+ }
160
+ /** One skill bundle file's text content, read on demand. */
161
+ export interface CatalogSkillFileRead {
162
+ readonly content: string;
163
+ }
164
+ /** One skill found inside a local skill container dir.
165
+ * `kind:'self'` is the picked directory itself (a single skill bundle), so it is
166
+ * installed with `repo=<dir>` and no `skills`; `'child'` is a sub-directory of a
167
+ * container, installed with `repo=<dir>` + `skills:[name]`. */
168
+ export interface CatalogDirSkillInfo {
169
+ readonly name: string;
170
+ readonly description: string;
171
+ readonly kind?: 'self' | 'child';
172
+ }
173
+ /** One managed skill's preset-scoped delivery state. */
174
+ export interface CatalogScopedSkillRow {
175
+ readonly name: string;
176
+ readonly description: string;
177
+ readonly modelInvocable: boolean;
178
+ readonly userInvocable: boolean;
179
+ /** The managed `SKILL.md` path. */
180
+ readonly path: string;
181
+ /** The preset ids its frontmatter declares; empty means "every preset". */
182
+ readonly presets: readonly string[];
183
+ /** Whether a delivery provider currently serves it in at least one preset. */
184
+ readonly delivered: boolean;
185
+ /** The default-root path already supplying this name, when delivery is refused. */
186
+ readonly conflict?: string;
187
+ }
188
+ /** One preset's scoped-delivery state. */
189
+ export interface CatalogScopedPresetRow {
190
+ readonly presetId: string;
191
+ readonly skills: readonly string[];
192
+ /** Why this preset's standing scope could not be used. */
193
+ readonly error?: string;
194
+ }
195
+ /** Preset-scoped skill delivery status, as the settings surface reads it.
196
+ * This is a discovery policy for one instance, not an authorization boundary. */
197
+ export interface CatalogPresetScopeStatus {
198
+ /** Whether any preset is currently served. */
199
+ readonly enabled: boolean;
200
+ /** Why delivery is off, or reduced — absent when fully operational. */
201
+ readonly reason?: string;
202
+ /** The plugin-owned root the delivered skills live in. */
203
+ readonly root: string;
204
+ /** Whether the managed root is currently watched for changes. */
205
+ readonly watching: boolean;
206
+ readonly skills: readonly CatalogScopedSkillRow[];
207
+ readonly presets: readonly CatalogScopedPresetRow[];
208
+ /** True because custom skill roots are configured in host compositions this
209
+ * plugin cannot read: a duplicate there would not be detected. */
210
+ readonly customRootsUnverifiable: boolean;
211
+ }
212
+ /** One preset a scope picker may choose. */
213
+ export interface CatalogPresetOption {
214
+ readonly id: string;
215
+ readonly name?: string;
216
+ readonly description?: string;
217
+ /** Why this preset cannot compose a session, when it cannot. */
218
+ readonly broken?: string;
219
+ /** Whether a session naming no preset composes this one (the deployment default). */
220
+ readonly isDefault?: boolean;
221
+ }
222
+ /**
223
+ * One mode's capability face: what the model sees when a session runs this
224
+ * agent preset. The rows are the same shape the default read returns, so a
225
+ * viewer renders a mode card with the grid it already has.
226
+ */
227
+ export interface CatalogModeFace {
228
+ /** The preset id this face was read at. */
229
+ readonly preset: string;
230
+ /** The preset's published display name, when it has one. */
231
+ readonly name?: string;
232
+ /** One sentence on what the preset is for, when it publishes one. */
233
+ readonly description?: string;
234
+ /** Whether this is the deployment's default mode. */
235
+ readonly isDefault: boolean;
236
+ readonly skills: readonly CatalogSkillRow[];
237
+ readonly tools: readonly CatalogToolRow[];
238
+ /**
239
+ * Why this mode has no face; when set, both lists are empty. Present for a
240
+ * preset discovery already refused and for one whose standing scope would
241
+ * not resolve — neither is evidence that the mode loads nothing.
242
+ */
243
+ readonly unavailable?: string;
244
+ }
245
+ /** Request to declare which presets one skill is delivered to. */
246
+ export interface CatalogPresetScopeSetRequest {
247
+ readonly name: string;
248
+ readonly presets: readonly string[];
249
+ }
250
+ /** Request to adopt an installed skill into the managed, preset-scoped root. */
251
+ export interface CatalogPresetScopeAdoptRequest {
252
+ readonly name: string;
253
+ readonly presets: readonly string[];
254
+ readonly workdir?: string;
255
+ }
256
+ /** Outcome of one preset-scope write; `error` is present exactly when it failed. */
257
+ export interface CatalogPresetScopeEditResult {
258
+ readonly ok: boolean;
259
+ readonly error?: string;
260
+ }
261
+ /** Add-skill channel. */
262
+ export type AddSkillChannel = 'zip' | 'github' | 'command';
263
+ /** Request to add a skill (upload a zip or clone a GitHub repo). */
264
+ export interface CatalogAddSkillRequest {
265
+ readonly channel: AddSkillChannel;
266
+ /** Base64-encoded zip/tgz bytes for `zip`; opaque for `github`. */
267
+ readonly payload: string;
268
+ /** owner/repo, a git URL (a host-prefixed or pasted link and an in-repo subpath resolve to the same clone), or a local dir path for `command`. */
269
+ readonly repo?: string;
270
+ /** Selected skill sub-directory names when `repo` is a multi-skill container dir. */
271
+ readonly skills?: readonly string[];
272
+ /** Whether the added skill is model-invocable (written as disable-model-invocation inversely). */
273
+ readonly modelInvocable: boolean;
274
+ /** Target root: user (`$DSH_HOME/skills`) or project (`.agents/skills`). */
275
+ readonly root: 'user' | 'project';
276
+ /** When true, an existing same-name skill in the target root is replaced instead of prompting. */
277
+ readonly overwrite?: boolean;
278
+ }
279
+ /** Add-skill result. */
280
+ export interface CatalogAddSkillResult {
281
+ readonly ok: boolean;
282
+ readonly error?: string;
283
+ readonly name?: string;
284
+ /** Set on a soft "skill already exists" refusal (ok:false) so the UI can offer overwrite. */
285
+ readonly exists?: boolean;
286
+ }
287
+ /** Delete-skill result (delete only applies to catalog-owned file skills). */
288
+ export interface CatalogDeleteSkillResult {
289
+ readonly ok: boolean;
290
+ readonly error?: string;
291
+ }
292
+ /** Wire form of one credential's configured state. */
293
+ export interface CatalogCredentialSetRequest {
294
+ readonly key: string;
295
+ readonly value: string;
296
+ }
297
+ /** Remote surface the browser card binds through. */
298
+ export interface CapabilityCatalogRemote {
299
+ readonly snapshot: (workdir?: string) => Promise<CapabilityCatalogSnapshot>;
300
+ /** The capability face of one preset, with its `sha` (see the host half). */
301
+ readonly snapshotFor: (presetId?: string, workdir?: string) => Promise<CapabilityCatalogSnapshot>;
302
+ /** The capability face of one mode as a LISTING (no fingerprint work). */
303
+ readonly snapshotAt: (presetId?: string, workdir?: string) => Promise<CapabilityCatalogSnapshot>;
304
+ /** Every mode's face in one call, for the cross-mode comparison view. */
305
+ readonly modeFaces: (workdir?: string) => Promise<readonly CatalogModeFace[]>;
306
+ /** Read one capability at a mode's scope, or the default mode when omitted. */
307
+ readonly detail: (name: string, workdir?: string, presetId?: string) => Promise<CatalogSkillDetail | undefined>;
308
+ readonly readSkillFile: (name: string, filePath: string, workdir?: string, presetId?: string) => Promise<CatalogSkillFileRead | undefined>;
309
+ readonly listDirSkills: (dirPath: string) => Promise<readonly CatalogDirSkillInfo[]>;
310
+ readonly setCredential: (request: CatalogCredentialSetRequest) => Promise<boolean>;
311
+ readonly addSkill: (request: CatalogAddSkillRequest) => Promise<CatalogAddSkillResult>;
312
+ readonly deleteSkill: (name: string, workdir?: string, presetId?: string) => Promise<CatalogDeleteSkillResult>;
313
+ readonly pickDirectory: () => Promise<string | null>;
314
+ }
315
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Wire types of the capability catalog: the snapshot the host half collects,
3
+ * the model-facing tool output, and the Remote data contract. The browser
4
+ * card parses the tool's JSON text result or the Remote response back into
5
+ * these shapes, so they stay plain JSON-safe objects with no class identity.
6
+ * @module @khorsheed/dsh-capability-catalog/types
7
+ */
8
+ export {};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Minimal, dependency-free ZIP reader: enough to unpack a skill archive
3
+ * (dirs, stored and deflated entries) into `{ path, data }` pairs.
4
+ *
5
+ * Why hand-rolled: the host face is analyzed by the typert generator against a
6
+ * scratch overlay that only resolves `@deepseek-ai/*` source-plane packages and
7
+ * the harness's own node_modules — a third-party zip library resolves there but
8
+ * not at publish time, and no host edit is allowed. Using only node:zlib keeps
9
+ * the boundary to node built-ins, which the overlay resolves natively, and
10
+ * keeps the published package free of a zip runtime dependency.
11
+ *
12
+ * Skips directories; throws on a malformed or unsupported (e.g. zip64, encrypted,
13
+ * or data-descriptor-only) archive. Skill zips are small and conventional, so
14
+ * the classic (non-zip64) central directory is the only layout needed.
15
+ * @module @khorsheed/dsh-capability-catalog/zip
16
+ */
17
+ /** One extracted file entry: a relative path and its contents. */
18
+ export interface ZipEntry {
19
+ readonly path: string;
20
+ readonly data: Buffer;
21
+ }
22
+ /** Unpack a zip archive into its non-directory entries. */
23
+ export declare function extractZip(buf: Buffer): ZipEntry[];
24
+ //# sourceMappingURL=zip.d.ts.map
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Minimal, dependency-free ZIP reader: enough to unpack a skill archive
3
+ * (dirs, stored and deflated entries) into `{ path, data }` pairs.
4
+ *
5
+ * Why hand-rolled: the host face is analyzed by the typert generator against a
6
+ * scratch overlay that only resolves `@deepseek-ai/*` source-plane packages and
7
+ * the harness's own node_modules — a third-party zip library resolves there but
8
+ * not at publish time, and no host edit is allowed. Using only node:zlib keeps
9
+ * the boundary to node built-ins, which the overlay resolves natively, and
10
+ * keeps the published package free of a zip runtime dependency.
11
+ *
12
+ * Skips directories; throws on a malformed or unsupported (e.g. zip64, encrypted,
13
+ * or data-descriptor-only) archive. Skill zips are small and conventional, so
14
+ * the classic (non-zip64) central directory is the only layout needed.
15
+ * @module @khorsheed/dsh-capability-catalog/zip
16
+ */
17
+ import { inflateRawSync } from 'node:zlib';
18
+ const SIG_LOCAL = 0x04034b50; // PK\03\04
19
+ const SIG_CENTRAL = 0x02014b50; // PK\01\02
20
+ const SIG_EOCD = 0x06054b50; // PK\05\06
21
+ /** Unpack a zip archive into its non-directory entries. */
22
+ export function extractZip(buf) {
23
+ const eocd = findEocd(buf);
24
+ if (eocd === undefined)
25
+ throw new Error('end of central directory not found');
26
+ const count = buf.readUInt16LE(eocd + 10);
27
+ if (count === 0xffff)
28
+ throw new Error('zip64 archives are not supported');
29
+ let offset = buf.readUInt32LE(eocd + 16);
30
+ const entries = [];
31
+ for (let i = 0; i < count; i++) {
32
+ offset = readCentralDirEntry(buf, offset, entries);
33
+ }
34
+ return entries;
35
+ }
36
+ /** Walk one central-directory entry, appending it when it is a file. */
37
+ function readCentralDirEntry(buf, offset, entries) {
38
+ if (buf.readUInt32LE(offset) !== SIG_CENTRAL)
39
+ throw new Error('bad central directory entry');
40
+ const method = buf.readUInt16LE(offset + 10);
41
+ const compSize = buf.readUInt32LE(offset + 20);
42
+ if (compSize === 0xffffffff)
43
+ throw new Error('zip64 entries are not supported');
44
+ const nameLen = buf.readUInt16LE(offset + 28);
45
+ const extraLen = buf.readUInt16LE(offset + 30);
46
+ const commentLen = buf.readUInt16LE(offset + 32);
47
+ const localOffset = buf.readUInt32LE(offset + 42);
48
+ const name = buf.toString('utf8', offset + 46, offset + 46 + nameLen);
49
+ if (!name.endsWith('/')) {
50
+ const { dataOffset } = readLocalHeader(buf, localOffset);
51
+ entries.push({ path: name, data: inflateEntry(buf, dataOffset, method, compSize) });
52
+ }
53
+ return offset + 46 + nameLen + extraLen + commentLen;
54
+ }
55
+ /** Read a local file header, returning the offset where the data begins. */
56
+ function readLocalHeader(buf, offset) {
57
+ if (buf.readUInt32LE(offset) !== SIG_LOCAL)
58
+ throw new Error('bad local file header');
59
+ const nameLen = buf.readUInt16LE(offset + 26);
60
+ const extraLen = buf.readUInt16LE(offset + 28);
61
+ return { dataOffset: offset + 30 + nameLen + extraLen };
62
+ }
63
+ /** Decompress one entry: stored (0) or raw-deflated (8). */
64
+ function inflateEntry(buf, dataOffset, method, compSize) {
65
+ const compressed = buf.subarray(dataOffset, dataOffset + compSize);
66
+ if (method === 0)
67
+ return Buffer.from(compressed);
68
+ if (method === 8)
69
+ return inflateRawSync(compressed);
70
+ throw new Error(`unsupported compression method ${method}`);
71
+ }
72
+ /** Locate the end-of-central-directory record (last 64 KiB window). */
73
+ function findEocd(buf) {
74
+ const min = Math.max(0, buf.length - 22 - 0xffff);
75
+ for (let i = buf.length - 22; i >= min; i--) {
76
+ if (buf.readUInt32LE(i) === SIG_EOCD)
77
+ return i;
78
+ }
79
+ return undefined;
80
+ }
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Capability Catalog",
4
+ "description": "Enumerate every skill and tool with its registration channel."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "能力目录",
4
+ "description": "列出实例中所有技能与工具,标注各自的注册渠道。"
5
+ }
6
+ }
package/package.json ADDED
@@ -0,0 +1,119 @@
1
+ {
2
+ "name": "@khorsheed/dsh-capability-catalog",
3
+ "description": "Capability catalog: enumerate every skill and tool in the running instance with its registration channel (official builtin, project, user, custom, plugin runtime). A standalone settings tab (工具与技能) lists skills as a three-column preview grid — click for a modal with full detail, SKILL.md source, metadata, and per-credential config (e.g. an API key). An add-skill modal installs a skill from an uploaded zip or a pasted SKILL.md into the user or project skill root (the skill-filesystem watcher discovers it). Tools are listed by channel attribution.",
4
+ "keywords": [
5
+ "dsh",
6
+ "dsh-plugin",
7
+ "cordis",
8
+ "capabilities",
9
+ "catalog",
10
+ "skills"
11
+ ],
12
+ "version": "0.1.95",
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./client": {
26
+ "types": "./lib/types/client/index.d.ts",
27
+ "default": "./lib/client.js"
28
+ },
29
+ "./types": {
30
+ "types": "./lib/types/types.d.ts",
31
+ "default": "./lib/types/types.js"
32
+ },
33
+ "./typert": {
34
+ "types": "./lib/typert.host.d.ts",
35
+ "default": "./lib/typert.host.js"
36
+ },
37
+ "./remote": {
38
+ "types": "./lib/typert.remote-client.d.ts",
39
+ "default": "./lib/typert.remote-client.js"
40
+ },
41
+ "./src/*": "./src/*",
42
+ "./package.json": "./package.json",
43
+ "./locale/*.json": "./locale/*.json"
44
+ },
45
+ "dsh": {
46
+ "bundle": {
47
+ "patch": "./cordis.patch.yml"
48
+ },
49
+ "client": {
50
+ "inject": [
51
+ "@deepseek-ai/dsh-client-locale",
52
+ "@deepseek-ai/dsh-client-ui-primitives",
53
+ "@deepseek-ai/dsh-client-ui-settings",
54
+ "@deepseek-ai/dsh-client-ui-slots"
55
+ ],
56
+ "platform": "web"
57
+ },
58
+ "compat": {
59
+ "minHost": "0.1.5-rc.1",
60
+ "notes": "0.1.5-rc.1 full-line boot-verified 2026-09-25 (42 packages including capture) through the three compat layers: preset-registry dual-name probe, dual-shape typert codecs, and typert faces carrying zod@4; 0.1.7-rc.1 is the 3080 production-verified line",
61
+ "verifiedHost": "0.1.7-rc.1"
62
+ }
63
+ },
64
+ "license": "MIT",
65
+ "dependencies": {
66
+ "zod": "^4.4.3"
67
+ },
68
+ "peerDependencies": {
69
+ "@deepseek-ai/cordis": "^4.0.1",
70
+ "@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6",
71
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
72
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
73
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
74
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.6",
75
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
76
+ "@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
77
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
78
+ "@deepseek-ai/dsh-skill": "^0.1.0-rc.6",
79
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
80
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6",
81
+ "react": "^18.2.0"
82
+ },
83
+ "peerDependenciesMeta": {
84
+ "@deepseek-ai/dsh-agent-presets": {
85
+ "optional": true
86
+ },
87
+ "@deepseek-ai/dsh-client-ui-primitives": {
88
+ "optional": true
89
+ },
90
+ "@deepseek-ai/dsh-client-ui-settings": {
91
+ "optional": true
92
+ },
93
+ "@deepseek-ai/dsh-client-ui-settings-plugins": {
94
+ "optional": true
95
+ },
96
+ "@deepseek-ai/dsh-client-ui-slots": {
97
+ "optional": true
98
+ },
99
+ "@deepseek-ai/dsh-credentials": {
100
+ "optional": true
101
+ },
102
+ "@deepseek-ai/dsh-skill": {
103
+ "optional": true
104
+ }
105
+ },
106
+ "files": [
107
+ "lib",
108
+ "cordis.patch.yml",
109
+ "locale/*.json",
110
+ "lib/typert.host.d.ts",
111
+ "lib/typert.host.js",
112
+ "lib/typert.remote-client.d.ts",
113
+ "lib/typert.remote-client.js",
114
+ "scripts/gen-official-tools.mts",
115
+ "README.md",
116
+ "README.zh.md",
117
+ "LICENSE"
118
+ ]
119
+ }
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ /** Generate official-tools.ts by scanning the pinned harness checkout. */
3
+ import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
4
+ import { homedir } from 'node:os'
5
+ import { join } from 'node:path'
6
+
7
+ const HARNESS = process.env.DSH_HARNESS ?? join(homedir(), 'code/deepseek-harness')
8
+ const PACKAGES = join(HARNESS, 'packages')
9
+ const OUT = new URL('../src/official-tools.ts', import.meta.url)
10
+
11
+ function toolNamesFromSource(source: string): string[] {
12
+ const names: string[] = []
13
+ // Allow marker wrappers between register( and defineTool( — 0.1.2 registers
14
+ // send_message as register(markAdjacentAgentSendMessageTool(defineTool({…}).
15
+ const re = /ctx\.tools\.register\(\s*(?:[A-Za-z_$][\w$]*\(\s*)*defineTool\(\s*\{/g
16
+ let m: RegExpExecArray | null
17
+ while ((m = re.exec(source)) !== null) {
18
+ const start = m.index + m[0].length
19
+ let depth = 1
20
+ let i = start
21
+ for (; i < source.length && depth > 0; i++) {
22
+ const ch = source[i]
23
+ if (ch === '{') depth++
24
+ else if (ch === '}') depth--
25
+ }
26
+ const block = source.slice(start, i)
27
+ const nm = /name:\s*'([^']+)'/.exec(block)
28
+ if (nm?.[1]) names.push(nm[1])
29
+ }
30
+ return names
31
+ }
32
+
33
+ function collect(dir: string): string[] {
34
+ const out: string[] = []
35
+ for (const entry of readdirSync(dir)) {
36
+ if (entry === 'node_modules' || entry === 'lib' || entry === 'tests') continue
37
+ const full = join(dir, entry)
38
+ const st = statSync(full)
39
+ if (st.isDirectory()) out.push(...collect(full))
40
+ else if (entry.endsWith('.ts') || entry.endsWith('.tsx')) out.push(full)
41
+ }
42
+ return out
43
+ }
44
+
45
+ const names = new Set<string>()
46
+ for (const p of collect(PACKAGES)) {
47
+ for (const n of toolNamesFromSource(readFileSync(p, 'utf8'))) names.add(n)
48
+ }
49
+ const sorted = [...names].sort()
50
+ const content = '/** Generated from the pinned harness checkout — do not edit by hand. Run scripts/gen-official-tools.mts. */\nexport const OFFICIAL_TOOLS: readonly string[] = ' + JSON.stringify(sorted, null, 2) + '\n'
51
+ writeFileSync(OUT, content)
52
+ console.log(`official-tools: ${sorted.length} names -> ${OUT.pathname}`)