@nanmicoder/dsh-skills-hub 0.0.2 → 0.0.4

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 (44) hide show
  1. package/CONTRIBUTING.md +7 -3
  2. package/README.md +24 -12
  3. package/README_EN.md +24 -12
  4. package/README_ZH.md +1 -1
  5. package/assets/readme/marketplace.png +0 -0
  6. package/compatibility.json +6 -6
  7. package/docs/CATALOG.md +39 -0
  8. package/docs/COMPATIBILITY.md +31 -1
  9. package/docs/CONTRACT.md +15 -10
  10. package/docs/RELEASE.md +4 -4
  11. package/lib/catalog/catalog.js +89 -0
  12. package/lib/catalog/skills.json +8673 -0
  13. package/lib/client.js +2549 -1819
  14. package/lib/client.js.map +1 -1
  15. package/lib/market/clawhub-provider.js +139 -22
  16. package/lib/market/install-service.js +19 -8
  17. package/lib/market/market-service.js +88 -9
  18. package/lib/market/skillhub-provider.js +6 -2
  19. package/lib/skills/installed.js +5 -0
  20. package/lib/types/catalog/catalog.d.ts +73 -0
  21. package/lib/types/client/api.d.ts +12 -3
  22. package/lib/types/client/components/CategoryBar.d.ts +13 -0
  23. package/lib/types/client/components/FilterBar.d.ts +4 -0
  24. package/lib/types/client/components/InstallConfirmDialog.d.ts +14 -11
  25. package/lib/types/client/components/InstalledSkillDetail.d.ts +1 -1
  26. package/lib/types/client/components/InstalledSkills.d.ts +1 -1
  27. package/lib/types/client/components/MarketSkillDetail.d.ts +34 -0
  28. package/lib/types/client/components/SkillDetailShell.d.ts +66 -43
  29. package/lib/types/client/locales.d.ts +64 -0
  30. package/lib/types/client/skill-insights.d.ts +30 -0
  31. package/lib/types/client/state.d.ts +23 -3
  32. package/lib/types/market/clawhub-provider.d.ts +15 -0
  33. package/lib/types/market/install-service.d.ts +1 -0
  34. package/lib/types/market/market-service.d.ts +25 -3
  35. package/lib/types/market/types.d.ts +29 -0
  36. package/lib/types/skills/installed.d.ts +4 -0
  37. package/lib/types/web-routes.d.ts +4 -13
  38. package/lib/web-routes.js +48 -19
  39. package/package.json +26 -27
  40. package/release-notes/v0.0.3.md +12 -0
  41. package/release-notes/v0.0.4.md +11 -0
  42. package/lib/types/client/components/InstallStateBadge.d.ts +0 -11
  43. package/lib/types/client/components/SecurityBadge.d.ts +0 -16
  44. package/lib/types/client/components/SkillDetailView.d.ts +0 -19
package/lib/web-routes.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Skills Hub HTTP surface.
3
3
  *
4
- * One `prefix` route (`/api/skills-hub`) owns the seven endpoints the panel
4
+ * One `prefix` route (`/api/skills-hub`) owns the endpoints the panel
5
5
  * needs; every response is JSON with `Cache-Control: no-store`, because the
6
6
  * panel polls and every payload is either live upstream data or live disk state.
7
7
  *
@@ -16,8 +16,9 @@
16
16
  * otherwise be reachable by a cross-site request or a DNS-rebound page.
17
17
  */
18
18
  import { installMarketSkill, MarketInstallError, uninstallMarketSkill, } from "./market/install-service.js";
19
- import { getMarketFileContent, getMarketSkillDetail, getMarketStatus, isValidMarketFilePath, listMarketSkills, } from "./market/market-service.js";
19
+ import { getMarketFileContent, getMarketSkillDetail, getMarketStatus, isValidMarketFilePath, listMarketCategories, listMarketSkills, } from "./market/market-service.js";
20
20
  import { getMarketStats } from "./market/cache.js";
21
+ import { CLAWHUB_OWNER_PATTERN } from "./market/clawhub-provider.js";
21
22
  import { readInstalledFile, readInstalledSkill, removeInstalledSkill } from "./skills/management.js";
22
23
  import { scanInstalledSkills } from "./skills/installed.js";
23
24
  import { resolveSkillsScanRoots } from "./skills/root.js";
@@ -116,19 +117,9 @@ function sendFailure(res, error) {
116
117
  const failure = toHttpFailure(error);
117
118
  sendJson(res, failure.status, errorPayload(failure.code, failure.message), failure.allow === undefined ? undefined : { allow: failure.allow });
118
119
  }
119
- /**
120
- * Run the browser-trust gate and return the rejection status, if any.
121
- *
122
- * The frozen gate type says `boolean`, the real service answers `401 | 403 |
123
- * undefined`; both are accepted so the envelope can report the true status when
124
- * the plugin is wired to the real Connection service, while a plain predicate
125
- * still works.
126
- */
127
- function evaluateGate(gate, req, res) {
128
- const outcome = gate.requestRejection(req, res);
129
- if (outcome === undefined || outcome === false || outcome === null)
130
- return undefined;
131
- return typeof outcome === 'number' ? outcome : 403;
120
+ /** Preserve the host's authentication (401) and origin (403) rejections. */
121
+ function evaluateGate(gate, req) {
122
+ return gate.requestRejection(req);
132
123
  }
133
124
  /**
134
125
  * Read a bounded JSON object body.
@@ -248,6 +239,30 @@ function enumQuery(url, name, allowed, fallback) {
248
239
  return raw;
249
240
  throw new RouteError(400, 'BAD_REQUEST', `Invalid ${name} filter: ${raw}`);
250
241
  }
242
+ /** Shape of a catalogue category key. */
243
+ const CATEGORY_KEY_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
244
+ /** `category` query parameter: a catalogue category key, shape-checked only. */
245
+ function parseCategory(url) {
246
+ const raw = url.searchParams.get('category');
247
+ if (raw === null || raw === '' || raw === 'all')
248
+ return undefined;
249
+ if (!CATEGORY_KEY_PATTERN.test(raw))
250
+ throw new RouteError(400, 'BAD_REQUEST', `Invalid category: ${raw}`);
251
+ return raw;
252
+ }
253
+ /**
254
+ * Optional registry owner. ClawHub slugs are shared across authors, so the
255
+ * panel names the author of the card it opened; a malformed owner is refused
256
+ * rather than silently dropped (that would fall back to guessing).
257
+ */
258
+ function parseOwner(raw) {
259
+ if (raw === undefined || raw === null || raw === '')
260
+ return undefined;
261
+ if (typeof raw !== 'string' || !CLAWHUB_OWNER_PATTERN.test(raw)) {
262
+ throw new RouteError(400, 'BAD_REQUEST', `Invalid owner: ${String(raw)}`);
263
+ }
264
+ return raw;
265
+ }
251
266
  /** `limit` query parameter, clamped into the documented range. */
252
267
  function parseLimit(url) {
253
268
  const raw = url.searchParams.get('limit');
@@ -293,6 +308,8 @@ async function handleStats(res) {
293
308
  async function handleList(url, res) {
294
309
  const params = {
295
310
  q: optionalQuery(url, 'q', MAX_QUERY_LENGTH),
311
+ scope: enumQuery(url, 'scope', ['catalog', 'market'], 'catalog'),
312
+ category: parseCategory(url),
296
313
  source: enumQuery(url, 'source', ['all', ...MARKET_SOURCES], 'all'),
297
314
  security: enumQuery(url, 'security', ['all', ...SECURITY_FILTERS], 'all'),
298
315
  installed: enumQuery(url, 'installed', INSTALLED_FILTERS, 'all'),
@@ -303,15 +320,20 @@ async function handleList(url, res) {
303
320
  const result = await listMarketSkills(params);
304
321
  sendJson(res, 200, result);
305
322
  }
323
+ async function handleCategories(res) {
324
+ sendJson(res, 200, listMarketCategories());
325
+ }
306
326
  async function handleDetail(source, slug, url, res) {
307
- sendJson(res, 200, await getMarketSkillDetail(source, slug, { force: parseRefresh(url) }));
327
+ const owner = parseOwner(url.searchParams.get('owner'));
328
+ sendJson(res, 200, await getMarketSkillDetail(source, slug, { force: parseRefresh(url), owner }));
308
329
  }
309
330
  async function handleFile(source, slug, url, res) {
310
331
  const filePath = url.searchParams.get('path') ?? '';
311
332
  if (!isValidMarketFilePath(filePath)) {
312
333
  throw new RouteError(400, 'BAD_REQUEST', `Invalid file path: ${filePath}`);
313
334
  }
314
- sendJson(res, 200, { file: await getMarketFileContent(source, slug, filePath) });
335
+ const owner = parseOwner(url.searchParams.get('owner'));
336
+ sendJson(res, 200, { file: await getMarketFileContent(source, slug, filePath, owner) });
315
337
  }
316
338
  /**
317
339
  * List installed skills from disk.
@@ -326,10 +348,12 @@ async function handleInstalled(deps, res) {
326
348
  sendJson(res, 200, { items: (await scanInstalledSkills(roots)).map(item => ({ ...item, removable: deps.allowUninstall() })) });
327
349
  }
328
350
  async function handleInstall(req, deps, res) {
329
- const { source, slug } = parseBodyId(await readJsonBody(req));
351
+ const body = await readJsonBody(req);
352
+ const { source, slug } = parseBodyId(body);
330
353
  const result = await installMarketSkill(source, slug, {
331
354
  skillsRoot: await deps.skillsRoot(),
332
355
  allowUninstall: deps.allowUninstall(),
356
+ owner: parseOwner(body['owner']),
333
357
  });
334
358
  // Refresh before answering, so the next /skills or /installed call is correct.
335
359
  await deps.rescan().catch(() => undefined);
@@ -352,7 +376,7 @@ async function handle(req, res, gate, deps) {
352
376
  // leaving the whole marketplace readable and installable from any web page.
353
377
  const resolvedGate = gate();
354
378
  if (resolvedGate !== undefined) {
355
- const rejection = evaluateGate(resolvedGate, req, res);
379
+ const rejection = evaluateGate(resolvedGate, req);
356
380
  if (rejection !== undefined) {
357
381
  sendJson(res, rejection, errorPayload(rejection === 401 ? 'UNAUTHORIZED' : 'FORBIDDEN', rejection === 401 ? 'unauthorized' : 'forbidden'));
358
382
  return;
@@ -368,6 +392,11 @@ async function handle(req, res, gate, deps) {
368
392
  await handleStatus(res);
369
393
  return;
370
394
  }
395
+ if (head === 'categories' && segments.length === 1) {
396
+ requireMethod(method, 'GET');
397
+ await handleCategories(res);
398
+ return;
399
+ }
371
400
  if (head === 'stats' && segments.length === 1) {
372
401
  requireMethod(method, 'GET');
373
402
  await handleStats(res);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanmicoder/dsh-skills-hub",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Skills Hub for DeepSeek Harness: browse, preview, install and remove third-party skills from ClawHub and SkillHub inside the DSH Web GUI.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -62,14 +62,14 @@
62
62
  "pack:check": "pnpm check && node scripts/pack-check.mjs"
63
63
  },
64
64
  "peerDependencies": {
65
- "@deepseek-ai/cordis": "^4.0.2",
66
- "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
67
- "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
68
- "@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.2",
69
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
70
- "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
71
- "@deepseek-ai/dsh-host-webserver": "0.2.0-rc.2",
72
- "@deepseek-ai/schemastery": "^3.18.2",
65
+ "@deepseek-ai/cordis": "~4.0.5",
66
+ "@deepseek-ai/dsh-client-connection": "0.2.1-rc.1",
67
+ "@deepseek-ai/dsh-client-locale": "0.2.1-rc.1",
68
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.1-rc.1",
69
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.1",
70
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.1-rc.1",
71
+ "@deepseek-ai/dsh-host-webserver": "0.2.1-rc.1",
72
+ "@deepseek-ai/schemastery": "~3.18.5",
73
73
  "react": "^18.2.0"
74
74
  },
75
75
  "peerDependenciesMeta": {
@@ -102,24 +102,23 @@
102
102
  }
103
103
  },
104
104
  "devDependencies": {
105
- "@deepseek-ai/cordis": "4.0.4",
106
- "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
107
- "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
108
- "@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
109
- "@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.2",
110
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
111
- "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
112
- "@deepseek-ai/dsh-fs": "0.2.0-rc.2",
113
- "@deepseek-ai/dsh-home-paths": "0.2.0-rc.2",
114
- "@deepseek-ai/dsh-host-webserver": "0.2.0-rc.2",
115
- "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
116
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
117
- "@deepseek-ai/dsh-sandbox": "0.2.0-rc.2",
118
- "@deepseek-ai/dsh-scope": "0.2.0-rc.2",
119
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
120
- "@deepseek-ai/dsh-skill": "0.2.0-rc.2",
121
- "@deepseek-ai/dsh-skill-filesystem": "0.2.0-rc.2",
122
- "@deepseek-ai/schemastery": "3.18.4",
105
+ "@deepseek-ai/cordis": "4.0.5",
106
+ "@deepseek-ai/dsh-client-connection": "0.2.1-rc.1",
107
+ "@deepseek-ai/dsh-client-locale": "0.2.1-rc.1",
108
+ "@deepseek-ai/dsh-client-store": "0.2.1-rc.1",
109
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.1-rc.1",
110
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.1",
111
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.1-rc.1",
112
+ "@deepseek-ai/dsh-fs": "0.2.1-rc.1",
113
+ "@deepseek-ai/dsh-home-paths": "0.2.1-rc.1",
114
+ "@deepseek-ai/dsh-host-webserver": "0.2.1-rc.1",
115
+ "@deepseek-ai/dsh-llm": "0.2.1-rc.1",
116
+ "@deepseek-ai/dsh-sandbox": "0.2.1-rc.1",
117
+ "@deepseek-ai/dsh-scope": "0.2.1-rc.1",
118
+ "@deepseek-ai/dsh-session": "0.2.1-rc.1",
119
+ "@deepseek-ai/dsh-skill": "0.2.1-rc.1",
120
+ "@deepseek-ai/dsh-skill-filesystem": "0.2.1-rc.1",
121
+ "@deepseek-ai/schemastery": "3.18.5",
123
122
  "@types/node": "^24.13.3",
124
123
  "@types/react": "~18.3.1",
125
124
  "@types/react-dom": "^18.3.7",
@@ -0,0 +1,12 @@
1
+ # Skills Hub 0.0.3
2
+
3
+ `@nanmicoder/dsh-skills-hub` for DeepSeek Harness **0.2.0-rc.2**.
4
+
5
+ - **Curated catalogue.** The home page now opens on 398 hand-picked skills from ClawHub and SkillHub, grouped into 13 categories (agents, research, development, office, productivity, content, media, data, marketing, finance, professional, life, security) with editor's picks first. The list ships with the plugin, so it renders instantly and offline; details, files and installs are still read live from upstream and cached. Searching switches to live results from both marketplaces.
6
+ - **Redesigned detail page.** Header with downloads, installs, stars and file count; Overview, Files, Security and Changelog tabs; a rule-based "what this skill will do" panel (shell commands, hooks, secrets, network hosts, workspace writes, required binaries) and "when it triggers" list, both derived from the skill's own files; per-scanner security reports from upstream.
7
+ - **Install confirmation.** The dialog lists what the skill will be able to do, where it will be installed, and requires an explicit acknowledgement for skills that are unscanned or flagged by upstream scanners.
8
+ - **Same-name skills resolved by author.** ClawHub slugs can be shared by several authors. Details, file previews and installs are now pinned to the author shown on the card, live search shows one entry per slug, and a mirror is only folded into an original by the same author. Installed skills record their author.
9
+ - **Unified look.** Marketplace home, detail pages and Installed share one visual system, and fit the docked 720px panel in light and dark themes.
10
+ - Copy now states plainly that Skills Hub is a community plugin, not a DeepSeek product, and that the curated list is a recommendation rather than a security guarantee.
11
+
12
+ Install in **DeepSeek Harness desktop → Plugins → Add plugin** using `@nanmicoder/dsh-skills-hub@0.0.3`, then enable the plugin. Existing skills and cache settings remain in place. Catalogue policy is documented in [CATALOG.md](../docs/CATALOG.md).
@@ -0,0 +1,11 @@
1
+ # Skills Hub 0.0.4
2
+
3
+ `@nanmicoder/dsh-skills-hub` for DeepSeek Harness **0.2.1-rc.1**.
4
+
5
+ - Move the exact Harness dependency and peer cohort to `0.2.1-rc.1`, with Cordis 4.0.5 and Schemastery 3.18.5. Remove the retired `dsh-invariants` development dependency.
6
+ - Type the HTTP trust gate against the host connection service, preserving authentication (401) and origin (403) rejection statuses without an unchecked boolean adapter.
7
+ - Retain the existing sidebar, curated catalogue, Markdown previews, persistent cache and skill installation/removal flows.
8
+
9
+ This release targets the exact candidate host version; it does not claim compatibility with an unreleased stable 0.2.1. See [COMPATIBILITY.md](../docs/COMPATIBILITY.md) for validation evidence and limitations.
10
+
11
+ After publication, install `@nanmicoder/dsh-skills-hub@0.0.4` from the host plugin manager. Release preparation does not publish the package.
@@ -1,11 +0,0 @@
1
- import type { InstallState } from '../../market/types.ts';
2
- /**
3
- * Local install state chip.
4
- *
5
- * The three labels come from dictionary keys the contract already fixes —
6
- * `installed`, `installed.installable` and `notInstallable` — so this badge
7
- * adds no vocabulary of its own.
8
- */
9
- export declare function InstallStateBadge(props: {
10
- state: InstallState;
11
- }): JSX.Element;
@@ -1,16 +0,0 @@
1
- import type { SecurityReport, SecurityStatus } from '../../market/types.ts';
2
- /**
3
- * Audit verdict chip.
4
- *
5
- * Shows a shield for every verdict except `flagged`, which uses the warning
6
- * triangle: a scan that found something must not look like a scan that merely
7
- * ran, and shape reads faster than color alone.
8
- *
9
- * `reports` supplies the upstream status line for the hover title, so the chip
10
- * can stay a two-word summary without hiding what the vendor actually said.
11
- */
12
- export declare function SecurityBadge(props: {
13
- status: SecurityStatus;
14
- reports?: SecurityReport[];
15
- compact?: boolean;
16
- }): JSX.Element;
@@ -1,19 +0,0 @@
1
- import type { NormalizedSkillDetail } from '../../market/types.ts';
2
- import type { MarketController, MarketState } from '../state.ts';
3
- /**
4
- * Market skill detail page.
5
- *
6
- * Like the home page this component owns no data: the tab, the selected file
7
- * and the file contents all live in `MarketState`, so switching tabs or
8
- * re-opening a skill does not lose its place. The reading surface itself — back
9
- * affordance, header, tabs, facts rail — is `SkillDetailShell`, which the local
10
- * installed page uses too.
11
- *
12
- * The bundled GFM renderer handles catalogue Markdown without relying on a
13
- * host renderer delegate. Raw HTML is disabled and URLs use its safe default.
14
- */
15
- export declare function SkillDetailView(props: {
16
- detail: NormalizedSkillDetail;
17
- state: MarketState;
18
- controller: MarketController;
19
- }): JSX.Element;