dsh-plugin 1.1.1 → 1.1.2

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 (64) hide show
  1. package/README.en.md +5 -5
  2. package/README.md +5 -5
  3. package/client/client.js +242 -153
  4. package/client/client.js.map +1 -1
  5. package/lib/http/routes.js +15 -8
  6. package/lib/index.js +4 -0
  7. package/lib/services/install-types.js +8 -2
  8. package/lib/services/install.js +1 -1
  9. package/lib/services/installed-versions.js +5 -1
  10. package/lib/services/loader.js +4 -0
  11. package/lib/services/npm-resolve.js +4 -0
  12. package/lib/services/pending-restart.js +1 -1
  13. package/lib/services/preflight.js +9 -4
  14. package/lib/services/profile.js +30 -3
  15. package/lib/services/progress.js +4 -0
  16. package/lib/services/task-queue.js +13 -7
  17. package/lib/types/index.d.ts +4 -0
  18. package/lib/types/services/install-types.d.ts +10 -4
  19. package/lib/types/services/install.d.ts +5 -1
  20. package/lib/types/services/loader.d.ts +4 -0
  21. package/lib/types/services/pending-restart.d.ts +8 -2
  22. package/lib/types/services/profile.d.ts +4 -2
  23. package/lib/types/services/progress.d.ts +4 -0
  24. package/package.json +1 -1
  25. package/src/client/components/AboutModal.tsx +4 -0
  26. package/src/client/components/CatalogControls.tsx +4 -0
  27. package/src/client/components/CatalogHeader.tsx +4 -0
  28. package/src/client/components/CatalogList.tsx +4 -0
  29. package/src/client/components/CategoryTabs.tsx +4 -0
  30. package/src/client/components/Dropdown.tsx +4 -0
  31. package/src/client/components/HubUpdateModal.tsx +4 -0
  32. package/src/client/components/NotificationsModal.tsx +4 -0
  33. package/src/client/components/PluginCard.tsx +5 -1
  34. package/src/client/components/PluginHubSection.tsx +5 -1
  35. package/src/client/components/ProgressView.tsx +4 -0
  36. package/src/client/components/icons.tsx +4 -0
  37. package/src/client/components/modals.tsx +4 -0
  38. package/src/client/hooks/useCatalog.ts +9 -5
  39. package/src/client/hooks/useTaskQueue.ts +13 -9
  40. package/src/client/index.tsx +4 -0
  41. package/src/client/lib/catalog.ts +27 -6
  42. package/src/client/lib/env.ts +4 -0
  43. package/src/client/lib/failures.ts +5 -1
  44. package/src/client/lib/format.ts +4 -0
  45. package/src/client/lib/renderMarkdown.ts +4 -0
  46. package/src/client/locales.ts +7 -1
  47. package/src/client/styles/Dropdown.module.css +5 -1
  48. package/src/client/styles/Header.module.css +5 -1
  49. package/src/client/styles/List.module.css +5 -1
  50. package/src/client/styles/Modal.module.css +5 -1
  51. package/src/client/styles/tokens.module.css +5 -1
  52. package/src/client/types.ts +5 -1
  53. package/src/server/http/routes.ts +14 -8
  54. package/src/server/index.ts +4 -0
  55. package/src/server/services/install-types.ts +10 -4
  56. package/src/server/services/install.ts +5 -1
  57. package/src/server/services/installed-versions.ts +5 -1
  58. package/src/server/services/loader.ts +4 -0
  59. package/src/server/services/npm-resolve.ts +4 -0
  60. package/src/server/services/pending-restart.ts +8 -2
  61. package/src/server/services/preflight.ts +9 -4
  62. package/src/server/services/profile.ts +27 -3
  63. package/src/server/services/progress.ts +4 -0
  64. package/src/server/services/task-queue.ts +13 -7
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Local HTTP routes exposing real installs to the in-app Plugin Hub UI.
3
7
  * The client fetches the same-origin `/dsh-plugin-hub/*` endpoints; the
4
8
  * install handler validates the target, then spawns the official dsh CLI
@@ -8,7 +12,7 @@ import { readFileSync } from 'node:fs';
8
12
  import { homedir, release } from 'node:os';
9
13
  import { dirname, join, resolve } from 'node:path';
10
14
  import { spawn } from 'node:child_process';
11
- import { activeTask, cancelTask, dumpLoaderEntries, getTask, githubTarget, hasQueuedTarget, listPendingRestarts, readProfileArg, startPluginMutation, validPackageName } from '../services/install.js';
15
+ import { activeTask, cancelTask, dumpLoaderEntries, getTask, githubRepoOf, githubTarget, hasQueuedTarget, listPendingRestarts, readProfileArg, startPluginMutation, validPackageName } from '../services/install.js';
12
16
  import { recordInstalledVersion, recordResolvedNpmPackage, readInstalledVersions, removeInstalledVersion } from '../services/installed-versions.js';
13
17
  import { resolveNpmPackage } from '../services/npm-resolve.js';
14
18
  import { preflightTarget } from '../services/preflight.js';
@@ -64,11 +68,14 @@ function hostEnv(profile) {
64
68
  dshHome: process.env.DSH_HOME ?? join(homedir(), '.dsh'),
65
69
  };
66
70
  }
67
- /** Match an install target (`github:<owner>/<repo>`) against the installed table; returns the package name when already installed. */
71
+ /** Match a GitHub install target against the installed table; returns the package name when already installed. */
68
72
  function installedByRepo(installed, target) {
69
- const needle = target.toLowerCase();
73
+ const repo = githubRepoOf(target);
74
+ if (repo === null)
75
+ return null;
76
+ const needle = repo.toLowerCase();
70
77
  for (const [name, spec] of Object.entries(installed)) {
71
- if (spec.toLowerCase().includes(needle))
78
+ if (githubRepoOf(spec)?.toLowerCase() === needle)
72
79
  return name;
73
80
  }
74
81
  return null;
@@ -164,7 +171,7 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
164
171
  const displayRepo = typeof body === 'object' && body !== null && typeof body.display === 'string'
165
172
  ? body.display.trim()
166
173
  : rawRepo;
167
- // 目标语法两态:owner/repo → github 源(走装前预检);npm 包名(@scope/name 或 name)→ 信任 registry 直接安装
174
+ // 目标语法两态:owner/repo → 显式 HTTPS Git 源(走装前预检);npm 包名(@scope/name 或 name)→ 信任 registry 直接安装
168
175
  const repoTarget = githubTarget(rawRepo);
169
176
  let target = repoTarget ?? (repoTarget === null && validPackageName(rawRepo) ? rawRepo : null);
170
177
  if (target === null) {
@@ -195,7 +202,7 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
195
202
  // 重复安装防护:非更新请求命中已安装目标时直接拒绝,不重复跑 CLI
196
203
  // (否则 CLI 会因「已存在」失败,且用户看到的是莫名其妙的报错);
197
204
  // 更新请求(mode: 'update')放行,CLI 的 add 对已存在依赖是 pnpm 原位覆盖重装。
198
- // 命中判定双通道:npm 包名按 dependencies 键直接命中,github: 目标按 spec 值匹配。
205
+ // 命中判定双通道:npm 包名按 dependencies 键直接命中,GitHub 目标按仓库身份匹配。
199
206
  const installed = readInstalled(profile);
200
207
  const already = installedByRepo(installed, target) ?? (installed[target] !== undefined ? target : null);
201
208
  if (already !== null && mode !== 'update') {
@@ -236,7 +243,7 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
236
243
  profile,
237
244
  target,
238
245
  // npm 安装时 target 是包名:displayTarget 固定展示仓库名,待重启行/前端恢复不受通道变化影响
239
- displayTarget: githubTarget(displayRepo) ?? undefined,
246
+ displayTarget: githubRepoOf(displayRepo) ?? undefined,
240
247
  // 已尝试的安装方式(npm 反查):实际执行命令由 spawn 时追加,失败 issue 一并展示
241
248
  attempts,
242
249
  timeoutMs: COMMAND_TIMEOUT_MS,
@@ -283,7 +290,7 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
283
290
  profile,
284
291
  target: name,
285
292
  // 卸载的 mutation 目标是 npm 包名;待重启行要显示 owner/repo(与安装行一致),非法/缺失时回退包名
286
- displayTarget: githubTarget(repo) ?? undefined,
293
+ displayTarget: githubRepoOf(repo) ?? undefined,
287
294
  // 运行中 loader:卸载成功后主动移除条目,立即生效、无需重启
288
295
  uninstallLoader: loader,
289
296
  timeoutMs: COMMAND_TIMEOUT_MS,
package/lib/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * dsh-plugin host entry: mounts the Plugin Hub HTTP routes once the host
3
7
  * web server service is available. The browser bundle (src/client) talks to
4
8
  * these same-origin routes to perform real installs from inside the app.
@@ -1,5 +1,11 @@
1
- /** 安装/卸载运行时的公共类型与常量(无依赖,供各服务模块复用)。 */
2
- /** Grammar of an accepted `github:<owner>/<repo>` install target. */
1
+ /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
6
+ * 安装/卸载运行时的公共类型与常量(无依赖,供各服务模块复用)。
7
+ */
8
+ /** Grammar of a catalog GitHub repository identity (`owner/repo`). */
3
9
  export const REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
4
10
  /** Grammar of an npm package name (used for uninstall targets). */
5
11
  export const PACKAGE_RE = /^(?:@[a-z0-9._-]+\/)?[A-Za-z0-9._-]+$/;
@@ -1,4 +1,4 @@
1
1
  export { addPendingRestart, clearPendingRestart, listPendingRestarts } from './pending-restart.js';
2
2
  export { dumpLoaderEntries, removeLoadedEntry } from './loader.js';
3
- export { addAllowBuildsKey, githubTarget, parseAllowBuildsKey, readProfileArg, validPackageName } from './profile.js';
3
+ export { addAllowBuildsKey, githubRepoOf, githubTarget, parseAllowBuildsKey, readProfileArg, validPackageName } from './profile.js';
4
4
  export { activeTask, cancelTask, getTask, hasQueuedTarget, hasRunningTask, runPluginMutation, startPluginMutation } from './task-queue.js';
@@ -1,8 +1,12 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Installed-plugin version tracking.
3
7
  *
4
8
  * The installed table (`readInstalled` in routes.ts) only knows the manifest
5
- * spec of each dependency (`github:<owner>/<repo>`), which carries no version.
9
+ * spec of each Git dependency, which carries no version.
6
10
  * To let the Plugin Hub tell "an update is available", we record the catalog
7
11
  * signal present at install time into a small per-profile JSON file, then
8
12
  * compare it against the catalog's current signal later.
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * 运行中 loader 的读取与停用:卸载成功后对匹配条目做 live-disable,
3
7
  * 使卸载立即生效(刷新页面不再加载已卸载的 client bundle,也无需重启)。
4
8
  */
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * npm 包反查:git 分发不完整(缺构建产物/子模块)导致安装失败时,用 npm
3
7
  * registry 的搜索接口按 repository 地址反查该仓库对应的官方 npm 包。
4
8
  *
@@ -2,7 +2,7 @@ const pendingRestarts = [];
2
2
  /**
3
3
  * 登记一条待重启记录(同一目标去重)。
4
4
  * - 卸载:目标刚装完未重启(已有待重启记录)→ 宿主从未加载过它,卸完磁盘已干净、
5
- * loader 无残留,无需再提醒重启,直接移除该条;否则(此前已被宿主加载过)登记「卸载后待重启清理」。
5
+ * loader 无残留,无需再提醒重启,直接移除该条;否则(已被宿主加载过)登记「卸载后待重启清理」。
6
6
  * - 安装:已有记录则刷新为安装语义(如卸载后未重启又重装),否则新登记。
7
7
  */
8
8
  export function addPendingRestart(target, kind) {
@@ -1,5 +1,9 @@
1
1
  /**
2
- * 安装前预检:对 `github:` 源的插件,检查其分发(codeload tarball)里是否
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
6
+ * 安装前预检:对 GitHub 源的插件,检查其分发(codeload tarball)里是否
3
7
  * 真的包含 package.json 声明的入口文件(main / exports["."].default)。
4
8
  * git 分发常不提交构建产物(lib/ 等),这类残缺包装完会让宿主重启加载插件树时
5
9
  * ERR_MODULE_NOT_FOUND 直接崩溃 —— 预检在动手安装前就把它们拦下,避免白装一次。
@@ -13,12 +17,13 @@ import { tmpdir } from 'node:os';
13
17
  import { join } from 'node:path';
14
18
  import { pipeline } from 'node:stream/promises';
15
19
  import { promisify } from 'node:util';
20
+ import { githubRepoOf } from './profile.js';
16
21
  const execFileAsync = promisify(execFile);
17
22
  export async function preflightTarget(target) {
18
- const m = /^github:([^/]+)\/([^/]+)$/.exec(target);
19
- if (m === null)
23
+ const source = githubRepoOf(target);
24
+ if (source === null)
20
25
  return { ok: true, missing: null };
21
- const [, owner, repo] = m;
26
+ const [owner, repo] = source.split('/');
22
27
  let commit = '';
23
28
  try {
24
29
  const { stdout } = await execFileAsync('git', ['ls-remote', `https://github.com/${owner}/${repo}.git`, 'HEAD']);
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * 宿主 profile 与目标校验工具:解析当前 profile、目录定位、安装目标语法校验,
3
7
  * 以及 pnpm allowBuilds 白名单的写入(失败恢复路径)。
4
8
  */
@@ -16,11 +20,34 @@ export function readProfileArg(fallback = 'web') {
16
20
  export function profileDirectory(profile) {
17
21
  return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'profiles', profile);
18
22
  }
19
- /** Build a safe `github:<owner>/<repo>` target, or null when the repo is unsafe. */
23
+ /** Build a safe explicit HTTPS Git target, or null when the repo is unsafe. */
20
24
  export function githubTarget(repo) {
21
25
  if (typeof repo !== 'string' || !REPO_RE.test(repo))
22
26
  return null;
23
- return `github:${repo}`;
27
+ return `git+https://github.com/${repo}.git`;
28
+ }
29
+ /** Extract an owner/repo identity from a catalog value or an installed Git spec. */
30
+ export function githubRepoOf(value) {
31
+ if (typeof value !== 'string')
32
+ return null;
33
+ const input = value.trim();
34
+ if (REPO_RE.test(input))
35
+ return input;
36
+ const patterns = [
37
+ /^github:([^/]+)\/([^/]+)$/i,
38
+ /^(?:git\+)?https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
39
+ /^(?:git\+)?ssh:\/\/(?:git@)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
40
+ /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
41
+ ];
42
+ for (const pattern of patterns) {
43
+ const match = pattern.exec(input);
44
+ if (match === null)
45
+ continue;
46
+ const repo = `${match[1]}/${match[2]}`;
47
+ if (REPO_RE.test(repo))
48
+ return repo;
49
+ }
50
+ return null;
24
51
  }
25
52
  /** Validate an npm package name (uninstall target grammar). */
26
53
  export function validPackageName(name) {
@@ -28,7 +55,7 @@ export function validPackageName(name) {
28
55
  }
29
56
  /**
30
57
  * The exact `allowBuilds` key pnpm printed in its
31
- * `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` hint. For `github:` installs pnpm
58
+ * `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` hint. For Git installs pnpm
32
59
  * matches against `name@<resolved fetch URL>` — the URL it actually fetches,
33
60
  * e.g. `https://codeload.github.com/<owner>/<repo>/tar.gz/<commit>` — not the
34
61
  * `git+ssh://`/branch spec. A bare name or a `git+` spec does not match, so
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Pure progress-estimation helpers shared by the install runner.
3
7
  *
4
8
  * The dsh CLI wraps pnpm, which refreshes its `Progress: …` line in place
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * 后台安装/卸载任务队列:内存任务注册表 + FIFO 串行 worker + 子进程生命周期管理。
3
7
  * 每个任务一次 `dsh plugin --profile <profile> <action> <target>` 子进程,
4
8
  * 输出流式收集到任务上;卸载成功时尝试从运行中 loader 即时停用(见 loader.ts)。
@@ -10,7 +14,7 @@ import { CAPTURE_LIMIT_BYTES, MAX_TASKS, MAX_TASK_LINES } from './install-types.
10
14
  import { cleanLine, estimateProgress } from './progress.js';
11
15
  import { removeLoadedEntry } from './loader.js';
12
16
  import { addPendingRestart, clearPendingRestart } from './pending-restart.js';
13
- import { addAllowBuildsKey, parseAllowBuildsKey, profileDirectory } from './profile.js';
17
+ import { addAllowBuildsKey, githubRepoOf, parseAllowBuildsKey, profileDirectory } from './profile.js';
14
18
  /** In-memory task registry, keyed by task id. */
15
19
  const tasks = new Map();
16
20
  let nextTaskId = 1;
@@ -185,7 +189,7 @@ function spawnMutation(options) {
185
189
  const { action, profile, target, timeoutMs = 5 * 60 * 1000, env, task, displayTarget, uninstallLoader } = options;
186
190
  const invocation = cliInvocation();
187
191
  const args = [...invocation.prefixArgs, 'plugin', '--profile', profile, action, target];
188
- // 记录实际执行的命令(npm 通道显示包名,git 通道显示 github: 源):失败提 Issue 时
192
+ // 记录实际执行的命令(npm 通道显示包名,git 通道显示 HTTPS 源):失败提 Issue 时
189
193
  // 「已尝试的安装方式」如实展示完整命令链,作者一眼看清我们跑过什么
190
194
  const executed = `dsh plugin --profile ${profile} ${action} ${target}`;
191
195
  if (task && !task.attempts.includes(executed)) {
@@ -275,8 +279,7 @@ function spawnMutation(options) {
275
279
  child.stderr?.on('data', (chunk) => collect('stderr', chunk));
276
280
  child.once('exit', () => {
277
281
  // dsh 内部会 fork pnpm:pnpm 退出后仍攥着 stdout/stderr 管道时,
278
- // close 事件永不到来(历史队列卡死根因——取消路径曾因此卡死,用整组 SIGKILL 解决;
279
- // 自然失败路径没有兜底)。exit 后给 close 一段宽限,未按时触发则整组清理强制结束,
282
+ // close 事件不会到来。exit 后给 close 一段宽限,未按时触发则整组清理强制结束,
280
283
  // 保证任务必然进入终态、后续排队任务自动启动。
281
284
  exitWatch = setTimeout(() => {
282
285
  if (settled)
@@ -360,8 +363,11 @@ export function verifyInstalledEntry(profile, target) {
360
363
  return { name: null, missing: null };
361
364
  }
362
365
  const deps = (profilePkg?.dependencies ?? {});
363
- // target 自身是依赖名(npm 包),或通过 github: specifier 反查依赖名
364
- const name = deps[target] !== undefined ? target : Object.keys(deps).find((k) => deps[k] === target);
366
+ // target 自身是依赖名(npm 包),或通过 Git specifier 反查依赖名
367
+ const targetRepo = githubRepoOf(target)?.toLowerCase();
368
+ const name = targetRepo !== undefined
369
+ ? Object.keys(deps).find((k) => githubRepoOf(deps[k])?.toLowerCase() === targetRepo)
370
+ : deps[target] !== undefined ? target : Object.keys(deps).find((k) => deps[k] === target);
365
371
  if (!name)
366
372
  return { name: null, missing: null };
367
373
  const pkgDir = join(profileDirectory(profile), 'node_modules', name);
@@ -428,7 +434,7 @@ export async function runPluginMutation(options) {
428
434
  // 输出解析不到时回退用目标名(插件自身构建脚本被拦的 npm 场景)。
429
435
  if (output.includes('ERR_PNPM_IGNORED_BUILDS')) {
430
436
  const keys = ignoredBuildKeys(output);
431
- if (keys.length === 0 && !options.target.startsWith('github:'))
437
+ if (keys.length === 0 && githubRepoOf(options.target) === null)
432
438
  keys.push(options.target);
433
439
  if (keys.length > 0) {
434
440
  if (options.task) {
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * dsh-plugin host entry: mounts the Plugin Hub HTTP routes once the host
3
7
  * web server service is available. The browser bundle (src/client) talks to
4
8
  * these same-origin routes to perform real installs from inside the app.
@@ -1,4 +1,10 @@
1
- /** 安装/卸载运行时的公共类型与常量(无依赖,供各服务模块复用)。 */
1
+ /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
6
+ * 安装/卸载运行时的公共类型与常量(无依赖,供各服务模块复用)。
7
+ */
2
8
  /** 一个 pnpm 安装子进程的结果快照。 */
3
9
  export interface InstallResult {
4
10
  exitCode: number | null;
@@ -9,7 +15,7 @@ export interface InstallResult {
9
15
  }
10
16
  export interface InstallTask {
11
17
  id: number;
12
- /** 操作目标:`github:<owner>/<repo>`(安装)或 npm 包名(卸载),冲突/恢复时需要展示给用户 */
18
+ /** 操作目标:显式 HTTPS Git URL(安装)或 npm 包名(卸载),冲突/恢复时需要展示给用户 */
13
19
  target: string;
14
20
  /** 展示用目标(owner/repo):npm 安装时 target 是包名,前端恢复用此字段显示仓库名,保持用户无感知 */
15
21
  displayTarget?: string;
@@ -52,14 +58,14 @@ export interface QueueItem {
52
58
  * 它自然清空,正好等于「重启后提醒消失」的语义,无需任何重启探测。
53
59
  */
54
60
  export interface PendingRestart {
55
- /** `github:<owner>/<repo>` 安装目标(与任务一致;卸载时为展示用的 owner/repo) */
61
+ /** Git 安装目标(与任务一致;卸载时为展示用的 owner/repo) */
56
62
  target: string;
57
63
  /** 待重启语义:install 装完等挂载生效 / uninstall 卸完等清理 loader 残留 */
58
64
  kind: 'install' | 'uninstall';
59
65
  /** 任务完成时刻(epoch ms) */
60
66
  at: number;
61
67
  }
62
- /** Grammar of an accepted `github:<owner>/<repo>` install target. */
68
+ /** Grammar of a catalog GitHub repository identity (`owner/repo`). */
63
69
  export declare const REPO_RE: RegExp;
64
70
  /** Grammar of an npm package name (used for uninstall targets). */
65
71
  export declare const PACKAGE_RE: RegExp;
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Install runner 的统一出口:执行真实插件变更(spawn 官方 dsh CLI 子进程)。
3
7
  * 实现按职责拆分在同目录子模块,本文件只做公共 API 的 re-export,保持外部
4
8
  * import 面稳定(routes.ts / index.ts 仍从 `services/install.ts` 引入)。
@@ -14,5 +18,5 @@ export type { InstallResult, InstallTask, Invocation, PendingRestart, QueueItem
14
18
  export type { LoaderHandle } from './loader.ts';
15
19
  export { addPendingRestart, clearPendingRestart, listPendingRestarts } from './pending-restart.ts';
16
20
  export { dumpLoaderEntries, removeLoadedEntry } from './loader.ts';
17
- export { addAllowBuildsKey, githubTarget, parseAllowBuildsKey, readProfileArg, validPackageName } from './profile.ts';
21
+ export { addAllowBuildsKey, githubRepoOf, githubTarget, parseAllowBuildsKey, readProfileArg, validPackageName } from './profile.ts';
18
22
  export { activeTask, cancelTask, getTask, hasQueuedTarget, hasRunningTask, runPluginMutation, startPluginMutation } from './task-queue.ts';
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * 运行中 loader 的读取与停用:卸载成功后对匹配条目做 live-disable,
3
7
  * 使卸载立即生效(刷新页面不再加载已卸载的 client bundle,也无需重启)。
4
8
  */
@@ -1,9 +1,15 @@
1
- /** 待重启列表的内存态管理(宿主进程一重启自然清空)。 */
1
+ /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
6
+ * 待重启列表的内存态管理(宿主进程一重启自然清空)。
7
+ */
2
8
  import type { PendingRestart } from './install-types.ts';
3
9
  /**
4
10
  * 登记一条待重启记录(同一目标去重)。
5
11
  * - 卸载:目标刚装完未重启(已有待重启记录)→ 宿主从未加载过它,卸完磁盘已干净、
6
- * loader 无残留,无需再提醒重启,直接移除该条;否则(此前已被宿主加载过)登记「卸载后待重启清理」。
12
+ * loader 无残留,无需再提醒重启,直接移除该条;否则(已被宿主加载过)登记「卸载后待重启清理」。
7
13
  * - 安装:已有记录则刷新为安装语义(如卸载后未重启又重装),否则新登记。
8
14
  */
9
15
  export declare function addPendingRestart(target: string, kind: 'install' | 'uninstall'): void;
@@ -2,13 +2,15 @@
2
2
  export declare function readProfileArg(fallback?: string): string;
3
3
  /** Resolve a profile directory (`DSH_HOME` or `~/.dsh`). */
4
4
  export declare function profileDirectory(profile: string): string;
5
- /** Build a safe `github:<owner>/<repo>` target, or null when the repo is unsafe. */
5
+ /** Build a safe explicit HTTPS Git target, or null when the repo is unsafe. */
6
6
  export declare function githubTarget(repo: string): string | null;
7
+ /** Extract an owner/repo identity from a catalog value or an installed Git spec. */
8
+ export declare function githubRepoOf(value: string): string | null;
7
9
  /** Validate an npm package name (uninstall target grammar). */
8
10
  export declare function validPackageName(name: string): boolean;
9
11
  /**
10
12
  * The exact `allowBuilds` key pnpm printed in its
11
- * `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` hint. For `github:` installs pnpm
13
+ * `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` hint. For Git installs pnpm
12
14
  * matches against `name@<resolved fetch URL>` — the URL it actually fetches,
13
15
  * e.g. `https://codeload.github.com/<owner>/<repo>/tar.gz/<commit>` — not the
14
16
  * `git+ssh://`/branch spec. A bare name or a `git+` spec does not match, so
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Pure progress-estimation helpers shared by the install runner.
3
7
  *
4
8
  * The dsh CLI wraps pnpm, which refreshes its `Progress: …` line in place
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "A community plugin marketplace for DeepSeek Harness, built to the official plugin spec — browse, search and install 4000+ human-curated community plugins without leaving the app. · DeepSeek Harness 社区插件市场(遵循官方开发规范):4000+ 人工精选社区插件,每日更新。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * 头部「关注我们」弹窗:介绍 DSH-Plugin Hub 是干什么的 + 用户反馈群二维码。
3
7
  * 内容以 Markdown 形式由 dsh-update Worker 的 /about 接口下发(key = hub:about),
4
8
  * 作者想怎么写就怎么写、随写随推,客户端只负责渲染 —— 非写死。
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Toolbar above the list: search input + sort dropdown + installed / not
3
7
  * installed filter buttons + the notifications entry. All buttons share the
4
8
  * same size (24px high, like the notification entry button).
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Section header: brand title row (H1 + open-site button) + tagline, followed
3
7
  * by the purple ad banner that promotes the catalog stats.
4
8
  */
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Catalog list: the scrollable container with the loading / failed / empty
3
7
  * states, the plugin cards, and the count footer. Resets scroll position when
4
8
  * the category or install-status filter changes so the replaced content is
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Category tabs: an "all" chip followed by one chip per catalog category.
3
7
  * The "all" chip carries the total plugin count; the per-category chips
4
8
  * carry no counts so their widths stay uniform.
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Custom dropdown (native <select> replacement).
3
7
  *
4
8
  * macOS/浏览器原生下拉无法跟随宿主主题,这里自绘:触发器按钮 + 绝对定位面板,
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Hub 版本信息 / 自我更新说明弹窗:
3
7
  * - 有更新(hasUpdate=true):标题「有新版本」,展示新版本号、发布时间与 Worker 下发的
4
8
  * Markdown 变更记录(renderMarkdown 渲染,双语言按界面语言取 {zh,en} 对象),
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Notification-center dialog: a persistent log of every settled install /
3
7
  * remove task — successes and failures alike.
4
8
  *
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * One plugin card in the catalog list: name/category/verified badge,
3
7
  * description + topics, star/fork/date stats, and the detail/install/
4
8
  * uninstall actions.
@@ -65,7 +69,7 @@ export function PluginCard({ plugin: p, copied, installedName, installedVersion,
65
69
  // 查看详情:未安装/已安装都始终显示,跳转官网详情页
66
70
  h('a', {
67
71
  className: styles.detailBtn,
68
- // 两级路径:/plugins/{ownerSlug}/{slug};旧数据缺 ownerSlug 时从 repo 推导
72
+ // 两级路径:/plugins/{ownerSlug}/{slug};缺 ownerSlug 时从 repo 推导
69
73
  href: pluginDetailUrl(p, langPath),
70
74
  target: '_blank',
71
75
  rel: 'noopener noreferrer',
@@ -1,8 +1,12 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Plugin Hub section: wires the catalog data pipeline, the background task
3
7
  * queue and the feedback state together. Data/queue logic lives in hooks/,
4
8
  * rendering is delegated to the small presentational components in this
5
- * folder; only the dialogs/toast and the section-level copy actions remain.
9
+ * folder; it hosts the dialogs/toast and the section-level copy actions.
6
10
  */
7
11
  import { createElement as h, useEffect, useState } from 'react'
8
12
  import styles from '../styles/Header.module.css'
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Inline progress block shared by the install and uninstall dialogs:
3
7
  * a real percentage bar (server-estimated, client-polled). No live terminal
4
8
  * window — pnpm emits nothing during git clone/build, so a log pane would be
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Inline SVG icons used across the client. Icons inherit currentColor so
3
7
  * they adapt to themes and disabled states automatically.
4
8
  */
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Dialog layer for the Plugin Hub: the install-confirm dialog, the uninstall
3
7
  * dialog and the global toast. Both dialogs lock themselves while a mutation
4
8
  * or restart is running, then switch to a result view offering an immediate
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Catalog data + view state for the Plugin Hub section.
3
7
  *
4
8
  * Owns the online-data pipeline (live fetch from dsh-plugin.org), the local
@@ -7,7 +11,7 @@
7
11
  */
8
12
  import { useEffect, useMemo, useState } from 'react'
9
13
  import type { HubPlugin, LocaleId } from '../types.ts'
10
- import { HUB_ABOUT_URL, HUB_REPO, HUB_UPDATE_URL, normalize, PLUGIN_VERSION } from '../lib/catalog.ts'
14
+ import { HUB_ABOUT_URL, HUB_REPO, HUB_UPDATE_URL, normalize, PLUGIN_VERSION, repoFromInstallTarget } from '../lib/catalog.ts'
11
15
  import type { HubAboutInfo, HubUpdateInfo } from '../lib/catalog.ts'
12
16
  import type { SortKey } from '../lib/catalog.ts'
13
17
 
@@ -59,7 +63,7 @@ export function useCatalog(lang: LocaleId) {
59
63
  const list = (Array.isArray(data) ? data : []).map((item) => normalize(item as Record<string, unknown>))
60
64
  // 插件市场不显示自己:DSH-Plugin Hub 从目录里排除自身条目,
61
65
  // 防止「自己出现在自己的插件列表里、还能自己安装自己」;统计计数同步减 1 保持与列表一致。
62
- // 主站数据构建侧已同步防止(registry/API 不再含自身),此处为已发布旧数据/CDN 缓存期的兜底。
66
+ // 主站数据已同步排除自身,此处为 CDN 缓存期的兜底。
63
67
  const hadSelf = list.some((p) => p.source?.repo === SELF_REPO)
64
68
  setPlugins(list.filter((p) => p.compatibility?.status === 'verified' && p.source?.repo !== SELF_REPO))
65
69
  const stats = s as { total?: number; verified?: number }
@@ -129,13 +133,13 @@ export function useCatalog(lang: LocaleId) {
129
133
  return () => { cancelled = true }
130
134
  }, [reloadKey])
131
135
 
132
- /** 插件是否已安装:匹配 installed spec 中的 `github:<owner>/<repo>`,或 npm 通道安装的依赖包名;命中返回 npm 包名。 */
136
+ /** 插件是否已安装:匹配 Git spec 中的 owner/repo,或 npm 通道安装的依赖包名;命中返回 npm 包名。 */
133
137
  const installedName = (p: HubPlugin): string | null => {
134
138
  const repo = p.source?.repo
135
139
  if (!repo) return null
136
- const needle = `github:${repo.toLowerCase()}`
140
+ const needle = repo.toLowerCase()
137
141
  for (const [name, spec] of Object.entries(installed)) {
138
- if (spec.toLowerCase().includes(needle)) return name
142
+ if (repoFromInstallTarget(spec).toLowerCase() === needle) return name
139
143
  }
140
144
  // npm 通道安装:profile 依赖 key 直接是 npm 包名。包名来源两处——目录数据(npmPackage),
141
145
  // 或服务端 npm 优先反查持久化的映射(versions[repo].npmPackage,覆盖组织 scope 与 GitHub