@nanmicoder/dsh-skills-hub 0.0.2 → 0.0.3
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/CONTRIBUTING.md +6 -2
- package/README.md +8 -7
- package/README_EN.md +8 -7
- package/README_ZH.md +1 -1
- package/assets/readme/marketplace.png +0 -0
- package/compatibility.json +1 -1
- package/docs/CATALOG.md +39 -0
- package/docs/COMPATIBILITY.md +12 -0
- package/docs/CONTRACT.md +14 -9
- package/docs/RELEASE.md +4 -4
- package/lib/catalog/catalog.js +89 -0
- package/lib/catalog/skills.json +8673 -0
- package/lib/client.js +2549 -1819
- package/lib/client.js.map +1 -1
- package/lib/market/clawhub-provider.js +139 -22
- package/lib/market/install-service.js +19 -8
- package/lib/market/market-service.js +88 -9
- package/lib/market/skillhub-provider.js +6 -2
- package/lib/skills/installed.js +5 -0
- package/lib/types/catalog/catalog.d.ts +73 -0
- package/lib/types/client/api.d.ts +12 -3
- package/lib/types/client/components/CategoryBar.d.ts +13 -0
- package/lib/types/client/components/FilterBar.d.ts +4 -0
- package/lib/types/client/components/InstallConfirmDialog.d.ts +14 -11
- package/lib/types/client/components/InstalledSkillDetail.d.ts +1 -1
- package/lib/types/client/components/InstalledSkills.d.ts +1 -1
- package/lib/types/client/components/MarketSkillDetail.d.ts +34 -0
- package/lib/types/client/components/SkillDetailShell.d.ts +66 -43
- package/lib/types/client/locales.d.ts +64 -0
- package/lib/types/client/skill-insights.d.ts +30 -0
- package/lib/types/client/state.d.ts +23 -3
- package/lib/types/market/clawhub-provider.d.ts +15 -0
- package/lib/types/market/install-service.d.ts +1 -0
- package/lib/types/market/market-service.d.ts +25 -3
- package/lib/types/market/types.d.ts +29 -0
- package/lib/types/skills/installed.d.ts +4 -0
- package/lib/types/web-routes.d.ts +1 -1
- package/lib/web-routes.js +44 -5
- package/package.json +1 -1
- package/release-notes/v0.0.3.md +12 -0
- package/lib/types/client/components/InstallStateBadge.d.ts +0 -11
- package/lib/types/client/components/SecurityBadge.d.ts +0 -16
- 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
|
|
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";
|
|
@@ -248,6 +249,30 @@ function enumQuery(url, name, allowed, fallback) {
|
|
|
248
249
|
return raw;
|
|
249
250
|
throw new RouteError(400, 'BAD_REQUEST', `Invalid ${name} filter: ${raw}`);
|
|
250
251
|
}
|
|
252
|
+
/** Shape of a catalogue category key. */
|
|
253
|
+
const CATEGORY_KEY_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
254
|
+
/** `category` query parameter: a catalogue category key, shape-checked only. */
|
|
255
|
+
function parseCategory(url) {
|
|
256
|
+
const raw = url.searchParams.get('category');
|
|
257
|
+
if (raw === null || raw === '' || raw === 'all')
|
|
258
|
+
return undefined;
|
|
259
|
+
if (!CATEGORY_KEY_PATTERN.test(raw))
|
|
260
|
+
throw new RouteError(400, 'BAD_REQUEST', `Invalid category: ${raw}`);
|
|
261
|
+
return raw;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Optional registry owner. ClawHub slugs are shared across authors, so the
|
|
265
|
+
* panel names the author of the card it opened; a malformed owner is refused
|
|
266
|
+
* rather than silently dropped (that would fall back to guessing).
|
|
267
|
+
*/
|
|
268
|
+
function parseOwner(raw) {
|
|
269
|
+
if (raw === undefined || raw === null || raw === '')
|
|
270
|
+
return undefined;
|
|
271
|
+
if (typeof raw !== 'string' || !CLAWHUB_OWNER_PATTERN.test(raw)) {
|
|
272
|
+
throw new RouteError(400, 'BAD_REQUEST', `Invalid owner: ${String(raw)}`);
|
|
273
|
+
}
|
|
274
|
+
return raw;
|
|
275
|
+
}
|
|
251
276
|
/** `limit` query parameter, clamped into the documented range. */
|
|
252
277
|
function parseLimit(url) {
|
|
253
278
|
const raw = url.searchParams.get('limit');
|
|
@@ -293,6 +318,8 @@ async function handleStats(res) {
|
|
|
293
318
|
async function handleList(url, res) {
|
|
294
319
|
const params = {
|
|
295
320
|
q: optionalQuery(url, 'q', MAX_QUERY_LENGTH),
|
|
321
|
+
scope: enumQuery(url, 'scope', ['catalog', 'market'], 'catalog'),
|
|
322
|
+
category: parseCategory(url),
|
|
296
323
|
source: enumQuery(url, 'source', ['all', ...MARKET_SOURCES], 'all'),
|
|
297
324
|
security: enumQuery(url, 'security', ['all', ...SECURITY_FILTERS], 'all'),
|
|
298
325
|
installed: enumQuery(url, 'installed', INSTALLED_FILTERS, 'all'),
|
|
@@ -303,15 +330,20 @@ async function handleList(url, res) {
|
|
|
303
330
|
const result = await listMarketSkills(params);
|
|
304
331
|
sendJson(res, 200, result);
|
|
305
332
|
}
|
|
333
|
+
async function handleCategories(res) {
|
|
334
|
+
sendJson(res, 200, listMarketCategories());
|
|
335
|
+
}
|
|
306
336
|
async function handleDetail(source, slug, url, res) {
|
|
307
|
-
|
|
337
|
+
const owner = parseOwner(url.searchParams.get('owner'));
|
|
338
|
+
sendJson(res, 200, await getMarketSkillDetail(source, slug, { force: parseRefresh(url), owner }));
|
|
308
339
|
}
|
|
309
340
|
async function handleFile(source, slug, url, res) {
|
|
310
341
|
const filePath = url.searchParams.get('path') ?? '';
|
|
311
342
|
if (!isValidMarketFilePath(filePath)) {
|
|
312
343
|
throw new RouteError(400, 'BAD_REQUEST', `Invalid file path: ${filePath}`);
|
|
313
344
|
}
|
|
314
|
-
|
|
345
|
+
const owner = parseOwner(url.searchParams.get('owner'));
|
|
346
|
+
sendJson(res, 200, { file: await getMarketFileContent(source, slug, filePath, owner) });
|
|
315
347
|
}
|
|
316
348
|
/**
|
|
317
349
|
* List installed skills from disk.
|
|
@@ -326,10 +358,12 @@ async function handleInstalled(deps, res) {
|
|
|
326
358
|
sendJson(res, 200, { items: (await scanInstalledSkills(roots)).map(item => ({ ...item, removable: deps.allowUninstall() })) });
|
|
327
359
|
}
|
|
328
360
|
async function handleInstall(req, deps, res) {
|
|
329
|
-
const
|
|
361
|
+
const body = await readJsonBody(req);
|
|
362
|
+
const { source, slug } = parseBodyId(body);
|
|
330
363
|
const result = await installMarketSkill(source, slug, {
|
|
331
364
|
skillsRoot: await deps.skillsRoot(),
|
|
332
365
|
allowUninstall: deps.allowUninstall(),
|
|
366
|
+
owner: parseOwner(body['owner']),
|
|
333
367
|
});
|
|
334
368
|
// Refresh before answering, so the next /skills or /installed call is correct.
|
|
335
369
|
await deps.rescan().catch(() => undefined);
|
|
@@ -368,6 +402,11 @@ async function handle(req, res, gate, deps) {
|
|
|
368
402
|
await handleStatus(res);
|
|
369
403
|
return;
|
|
370
404
|
}
|
|
405
|
+
if (head === 'categories' && segments.length === 1) {
|
|
406
|
+
requireMethod(method, 'GET');
|
|
407
|
+
await handleCategories(res);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
371
410
|
if (head === 'stats' && segments.length === 1) {
|
|
372
411
|
requireMethod(method, 'GET');
|
|
373
412
|
await handleStats(res);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanmicoder/dsh-skills-hub",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
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",
|
|
@@ -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).
|
|
@@ -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;
|