dsh-plugin 1.6.12 → 2.0.1

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 (131) hide show
  1. package/README.en.md +13 -13
  2. package/README.md +13 -13
  3. package/client/client.js +791 -761
  4. package/client/client.js.map +1 -1
  5. package/lib/http/catalog.js +2 -3
  6. package/lib/http/diagnostics.js +6 -7
  7. package/lib/http/install.js +47 -213
  8. package/lib/http/logs.js +1 -2
  9. package/lib/http/npm-release-age.js +42 -0
  10. package/lib/http/routes.js +8 -7
  11. package/lib/http/settings.js +2 -2
  12. package/lib/http/shared.js +2 -2
  13. package/lib/http/task.js +1 -2
  14. package/lib/index.js +5 -6
  15. package/lib/services/install/install-types.js +4 -4
  16. package/lib/services/install/install.js +1 -1
  17. package/lib/services/install/npm-check.js +3 -2
  18. package/lib/services/install/npm-resolve.js +9 -96
  19. package/lib/services/install/operation-log.js +53 -1
  20. package/lib/services/install/package-entry.js +6 -11
  21. package/lib/services/install/registry-check.js +2 -2
  22. package/lib/services/install/release-target.js +8 -9
  23. package/lib/services/install/task-queue.js +44 -48
  24. package/lib/services/npm-release-age.js +119 -0
  25. package/lib/services/probe.js +4 -4
  26. package/lib/services/profile/installed-versions.js +15 -21
  27. package/lib/services/profile/profile.js +16 -44
  28. package/lib/services/profile/progress.js +8 -11
  29. package/lib/services/settings.js +2 -5
  30. package/lib/types/http/catalog.d.ts +1 -1
  31. package/lib/types/http/install.d.ts +2 -2
  32. package/lib/types/http/npm-release-age.d.ts +2 -0
  33. package/lib/types/http/routes.d.ts +10 -12
  34. package/lib/types/http/shared.d.ts +5 -6
  35. package/lib/types/index.d.ts +7 -8
  36. package/lib/types/services/install/install-types.d.ts +12 -13
  37. package/lib/types/services/install/install.d.ts +1 -1
  38. package/lib/types/services/install/npm-resolve.d.ts +4 -16
  39. package/lib/types/services/install/operation-log.d.ts +9 -0
  40. package/lib/types/services/install/package-entry.d.ts +6 -11
  41. package/lib/types/services/install/release-target.d.ts +7 -8
  42. package/lib/types/services/install/task-queue.d.ts +25 -30
  43. package/lib/types/services/npm-release-age.d.ts +15 -0
  44. package/lib/types/services/probe.d.ts +4 -4
  45. package/lib/types/services/profile/installed-versions.d.ts +5 -7
  46. package/lib/types/services/profile/profile.d.ts +14 -18
  47. package/lib/types/services/profile/progress.d.ts +7 -10
  48. package/lib/types/services/settings.d.ts +0 -4
  49. package/package.json +1 -1
  50. package/src/client/components/PluginHubSection.tsx +26 -22
  51. package/src/client/components/catalog/CatalogList.tsx +2 -4
  52. package/src/client/components/catalog/PluginCard.tsx +2 -3
  53. package/src/client/components/layout/CatalogControls.tsx +5 -6
  54. package/src/client/components/layout/CatalogHeader.tsx +1 -2
  55. package/src/client/components/layout/CategoryTabs.tsx +2 -3
  56. package/src/client/components/layout/SectionTabs.tsx +3 -5
  57. package/src/client/components/modals/ConfirmDialog.tsx +4 -6
  58. package/src/client/components/modals/InstalledDetailModal.tsx +3 -6
  59. package/src/client/components/modals/LogsModal.tsx +7 -12
  60. package/src/client/components/modals/NotificationsModal.tsx +74 -24
  61. package/src/client/components/modals/ProgressView.tsx +3 -4
  62. package/src/client/components/modals/modals.tsx +75 -26
  63. package/src/client/components/ui/Dropdown.tsx +4 -6
  64. package/src/client/components/ui/Toggle.tsx +2 -3
  65. package/src/client/components/ui/icons.tsx +1 -2
  66. package/src/client/components/views/CustomInstallView.tsx +20 -178
  67. package/src/client/components/views/DiagnosticsView.tsx +3 -6
  68. package/src/client/components/views/InstalledView.tsx +6 -11
  69. package/src/client/components/views/MarketView.tsx +2 -4
  70. package/src/client/components/views/SettingsView.tsx +9 -32
  71. package/src/client/css-modules.d.ts +1 -1
  72. package/src/client/data/catalog.ts +2 -3
  73. package/src/client/data/host.ts +26 -3
  74. package/src/client/data/hub.ts +2 -3
  75. package/src/client/hooks/useCatalog.ts +79 -24
  76. package/src/client/hooks/useIncrementalList.ts +3 -5
  77. package/src/client/hooks/useLanguage.ts +4 -6
  78. package/src/client/hooks/useSettings.ts +4 -11
  79. package/src/client/hooks/useTaskQueue.ts +36 -42
  80. package/src/client/index.tsx +2 -3
  81. package/src/client/locales.ts +187 -114
  82. package/src/client/logic/constants.ts +1 -2
  83. package/src/client/logic/ensure-plugin-css.ts +8 -12
  84. package/src/client/logic/failures.ts +20 -19
  85. package/src/client/logic/format.ts +1 -1
  86. package/src/client/logic/install-command.ts +14 -35
  87. package/src/client/logic/installed.ts +15 -10
  88. package/src/client/logic/normalize.ts +3 -3
  89. package/src/client/logic/release-age.ts +19 -0
  90. package/src/client/logic/urls.ts +24 -21
  91. package/src/client/styles/CustomInstallView.module.css +3 -30
  92. package/src/client/styles/DiagnosticsView.module.css +3 -5
  93. package/src/client/styles/Dropdown.module.css +2 -4
  94. package/src/client/styles/Header.module.css +6 -7
  95. package/src/client/styles/InstalledView.module.css +4 -7
  96. package/src/client/styles/List.module.css +4 -5
  97. package/src/client/styles/LogsView.module.css +2 -3
  98. package/src/client/styles/Modal.module.css +46 -6
  99. package/src/client/styles/SectionTabs.module.css +2 -4
  100. package/src/client/styles/SettingsView.module.css +3 -5
  101. package/src/client/styles/Toggle.module.css +2 -4
  102. package/src/client/styles/tokens.module.css +14 -18
  103. package/src/client/types.ts +11 -11
  104. package/src/server/http/catalog.ts +2 -3
  105. package/src/server/http/diagnostics.ts +6 -7
  106. package/src/server/http/install.ts +47 -208
  107. package/src/server/http/logs.ts +1 -2
  108. package/src/server/http/npm-release-age.ts +42 -0
  109. package/src/server/http/routes.ts +12 -12
  110. package/src/server/http/settings.ts +2 -2
  111. package/src/server/http/shared.ts +5 -6
  112. package/src/server/http/task.ts +1 -2
  113. package/src/server/index.ts +7 -8
  114. package/src/server/services/install/install-types.ts +12 -13
  115. package/src/server/services/install/install.ts +0 -1
  116. package/src/server/services/install/npm-check.ts +3 -2
  117. package/src/server/services/install/npm-resolve.ts +9 -99
  118. package/src/server/services/install/operation-log.ts +47 -1
  119. package/src/server/services/install/package-entry.ts +6 -11
  120. package/src/server/services/install/registry-check.ts +2 -2
  121. package/src/server/services/install/release-target.ts +8 -9
  122. package/src/server/services/install/task-queue.ts +51 -54
  123. package/src/server/services/npm-release-age.ts +107 -0
  124. package/src/server/services/probe.ts +4 -4
  125. package/src/server/services/profile/installed-versions.ts +16 -22
  126. package/src/server/services/profile/profile.ts +16 -42
  127. package/src/server/services/profile/progress.ts +8 -11
  128. package/src/server/services/settings.ts +2 -9
  129. package/lib/services/install/preflight.js +0 -178
  130. package/lib/types/services/install/preflight.d.ts +0 -23
  131. package/src/server/services/install/preflight.ts +0 -184
@@ -1,6 +1,6 @@
1
1
  import type { InstallResult, InstallTask, Invocation } from './install-types.ts';
2
2
  import type { LoaderHandle } from '../loader.ts';
3
- /** Snapshot of a task (keeps the live object untouched by consumers). */
3
+ /** 任务快照(避免消费方改动实时对象)。 */
4
4
  export declare function getTask(id: number): InstallTask | undefined;
5
5
  export interface ActiveTaskInfo {
6
6
  id: number;
@@ -9,28 +9,26 @@ export interface ActiveTaskInfo {
9
9
  status: 'pending' | 'running';
10
10
  progress: number;
11
11
  lines: string[];
12
- /** 尝试过的安装方式(npm 反查 + 实际执行命令):失败时前端 issue 预填需要 */
12
+ /** 尝试过的安装方式(实际执行过的命令):失败时前端 issue 预填需要 */
13
13
  attempts: string[];
14
- /** 自定义安装入口渠道(NPM 包 / GitHub 源码 / DSH 命令卡片):前端刷新恢复后仍能精准溯源 */
15
- installChannel?: 'npm' | 'git' | 'dsh';
14
+ /** 自定义安装入口渠道(NPM 包卡片):前端刷新恢复后仍能精准溯源 */
15
+ installChannel?: 'npm';
16
16
  }
17
- /** All non-terminal tasks in queue order (running first, then pending). Lets the client resume a queue after a page reload. */
17
+ /** 全部未终态任务,按队列顺序(running 在前、pending 在后)。供客户端刷新页面后恢复队列。 */
18
18
  export declare function activeTask(): ActiveTaskInfo[];
19
19
  /**
20
- * True while a mutation child process is still alive. The concurrent slot is
21
- * keyed off the live child set rather than task.status: cancelling a running
22
- * task flips its status immediately, but the child may still be shutting down
23
- * (the close event lands later) — the slot must stay held until then, otherwise
24
- * a new task would start while the old pnpm still runs in the same profile dir.
20
+ * 变更子进程仍存活时为 true。并发占位以存活子进程集合为准,而非 task.status:
21
+ * 取消运行中任务会立即翻转其状态,但子进程可能仍在退出中(close 事件稍后才到)——
22
+ * 占位必须维持到那时,否则旧 pnpm 还在同一 profile 目录跑,新任务就开始了。
25
23
  */
26
24
  export declare function hasRunningTask(): boolean;
27
- /** Whether the target already has a non-terminal task in the queue (install/uninstall dedupe). */
25
+ /** 目标在队列里是否已有未终态任务(安装/卸载去重)。 */
28
26
  export declare function hasQueuedTarget(target: string): boolean;
29
27
  /**
30
- * Cancel a queued or running task.
31
- * - pending: removed from the queue immediately (never spawned).
32
- * - running: kills the child process; the queue worker then picks the next one.
33
- * Returns false when the task is unknown or already finished.
28
+ * 取消排队中或运行中的任务。
29
+ * - pending:立即移出队列(从未 spawn)。
30
+ * - running:杀掉子进程;队列 worker 随后取下一个。
31
+ * 任务不存在或已结束时返回 false。
34
32
  */
35
33
  export declare function cancelTask(id: number): boolean;
36
34
  /**
@@ -41,9 +39,8 @@ export declare function cancelTask(id: number): boolean;
41
39
  */
42
40
  export declare function desktopCliBootstrap(entry: string): string | null;
43
41
  /**
44
- * Enqueue a plugin mutation and return its task. Tasks run strictly serially:
45
- * the queue worker starts the next one only after the previous finishes.
46
- * Progress is visible through `getTask(id)` / `activeTask()` until done.
42
+ * 插件变更入队并返回其任务。任务严格串行:队列 worker 只在上一个结束后才启动下一个。
43
+ * 完成前可通过 `getTask(id)` / `activeTask()` 查看进度。
47
44
  */
48
45
  export declare function startPluginMutation(options: {
49
46
  action: 'add' | 'remove' | 'update';
@@ -58,17 +55,17 @@ export declare function startPluginMutation(options: {
58
55
  /** 运行中 loader:安装成功后热挂新条目、卸载成功后停用旧条目,两类操作都尽量立即生效
59
56
  * (缺失时安装/卸载仍需重启) */
60
57
  loader?: LoaderHandle;
61
- /** 入队前已尝试的安装方式(npm registry 反查等):失败提 Issue 时如实展示;实际执行的命令由 spawn 时追加 */
58
+ /** 入队前已尝试的安装方式:失败提 Issue 时如实展示;实际执行的命令由 spawn 时追加 */
62
59
  attempts?: string[];
63
60
  /** 更新已安装的 npm 包(mode=update):命令显式带 @latest,让 pnpm 真正解析最新版本 */
64
61
  updateNpm?: boolean;
65
- /** 自定义安装入口渠道(客户端三张卡片:NPM 包 / GitHub 源码 / DSH 命令):
66
- * 任务输出行与系统日志据此溯源「从哪个入口发起」;目录插件安装不传 */
67
- installChannel?: 'npm' | 'git' | 'dsh';
62
+ /** 自定义安装入口渠道(NPM 包卡片):任务输出行与系统日志据此溯源「从哪个入口发起」;
63
+ * 目录插件安装不传 */
64
+ installChannel?: 'npm';
68
65
  }): InstallTask;
69
66
  /**
70
67
  * 安装后校验已装包里 package.json 声明的文件(main / exports["."].default 与
71
- * dsh.bundle.patch)是否真实存在。git: 分发常不提交构建产物(lib/ 等),pnpm 装完没有
68
+ * dsh.bundle.patch)是否真实存在。npm 包也可能漏发构建产物(lib/ 等),pnpm 装完没有
72
69
  * 报错,但宿主重启加载插件树时会 ERR_MODULE_NOT_FOUND 直接崩溃、网页打不开 —— 这里在
73
70
  * 登记「待重启」前就把这类残缺包拦下。没有声明过的文件不在这里追究:没声明入口的
74
71
  * bundle 包(只挂技能)本来就不需要入口,宿主读的是它的 package.json 与声明的补丁。
@@ -85,13 +82,11 @@ export declare function verifyInstalledEntry(profile: string, target: string): {
85
82
  */
86
83
  export declare function hostSpawnMarker(invocation: Invocation, error: unknown): string | null;
87
84
  /**
88
- * Run a plugin mutation with one recovery path: when `add` fails because
89
- * pnpm's allowBuilds gate blocks a build script — either the git-hosted
90
- * package's own `prepare` (`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`, key from
91
- * the printed key@url hint) or a dependency's native build
92
- * (`ERR_PNPM_IGNORED_BUILDS`, key = the listed name@version) — write the
93
- * exact keys into the profile's pnpm-workspace.yaml and retry once.
94
- * Other failures fall through to the single-spawn result.
85
+ * 执行插件变更,带一条恢复路径:当 `add` 因 pnpm 的 allowBuilds 门禁拦下构建脚本而失败 ——
86
+ * 或是 git 源包自己的 `prepare`(`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`,key 取自打印的
87
+ * key@url 提示),或是某依赖的原生构建(`ERR_PNPM_IGNORED_BUILDS`,key = 列出的 name@version)——
88
+ * 就把确切的 key 写进 profile 的 pnpm-workspace.yaml 并重试一次。
89
+ * 其它失败一律落到单次 spawn 的结果。
95
90
  */
96
91
  export declare function runPluginMutation(options: {
97
92
  action: 'add' | 'remove' | 'update';
@@ -0,0 +1,15 @@
1
+ export interface NpmRelease {
2
+ version: string;
3
+ publishedAt: string;
4
+ }
5
+ /** 请求体包名数量上限:只为已安装插件按需查询,几十个足矣,封顶防滥用。 */
6
+ export declare const MAX_RELEASE_AGE_PACKAGES = 50;
7
+ /** 单包查询超时(ms):慢网络下失败即返回 null,不拖住整个响应。 */
8
+ export declare const RELEASE_AGE_TIMEOUT_MS = 8000;
9
+ export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
10
+ /** 从完整 packument 取 dist-tags.latest 对应版本及其发布时间;缺任一字段返回 null。 */
11
+ export declare function latestReleaseOf(packument: unknown): NpmRelease | null;
12
+ /** 请求体 → 去重后的合法包名列表;非数组 / 超量 / 含非法包名一律 null(拒绝非法输入)。 */
13
+ export declare function parseReleaseAgeRequest(body: unknown): string[] | null;
14
+ /** 查询一个包的最新版本与发布时间(成功结果缓存,同会话内同包不重复请求);registry 为空回退官方源,失败返回 null。 */
15
+ export declare function lookupReleaseAge(packageName: string, registry: string, proxy: string, timeoutMs?: number): Promise<NpmRelease | null>;
@@ -33,7 +33,7 @@ export declare function tlsRevocationBlocked(stderr: string): boolean;
33
33
  * `http=host:port;https=host:port`(按协议分别配置)。
34
34
  * 字符类必须同时排除 `/` 与 `=`:只排除 `=` 时,带 scheme 的值会让「主机名」吞掉 `//`,
35
35
  * 拼出 `http:////127.0.0.1:10793` 这种立刻失败的非法地址 —— 代理本来是通的,却把诊断、
36
- * 目录拉取、npm/git 预检全判成「不可达」。
36
+ * 目录拉取、安装前网络探测全判成「不可达」。
37
37
  */
38
38
  export declare function parseWinProxyServer(stdout: string): string | null;
39
39
  /**
@@ -49,13 +49,13 @@ export declare function systemProxy(): string | null;
49
49
  */
50
50
  export declare function probeUrl(url: string, proxy: string, timeoutMs: number): Promise<ProbeResult>;
51
51
  /**
52
- * git 通道真实克隆握手探测:spawn git ls-remote(https 传输,与 pnpm 克隆前的
52
+ * git 真实克隆握手探测:spawn git ls-remote(https 传输,与 pnpm 克隆前的
53
53
  * ref 握手一致),注入代理 env,GIT_TERMINAL_PROMPT=0 防凭据提示挂起。
54
54
  *
55
55
  * 为什么要真实 git 而非 curl 打网页:网页「能打开」和 git「能克隆」是两码事 ——
56
56
  * 防火墙/代理常按端口与协议区分,HTTP 页可达不代表 git 传输可达。这里测的就是
57
- * 克隆握手本身,回答「github:owner/repo 装不装得动」。
58
- * 供系统诊断 GitHub 通道使用;探测目标用 dshplugin/hello-dsh 小仓库(秒级完成,
57
+ * 克隆握手本身,回答「能不能连上 github.com」。
58
+ * 供系统诊断 GitHub 可达性使用;探测目标用 dshplugin/hello-dsh 小仓库(秒级完成,
59
59
  * 不打 17MB 的 dsh-plugin-hub 主页)。
60
60
  */
61
61
  export declare function gitLsRemote(url: string, proxy: string, timeoutMs: number): Promise<ProbeResult>;
@@ -4,21 +4,19 @@ export interface InstalledVersionRecord {
4
4
  /** 安装时的仓库最近更新时间(ISO,GitHub pushed_at);无 release 仓库的更新依据 */
5
5
  updatedAt: string;
6
6
  installedAt: string;
7
- /** npm 优先通道反查命中的官方 npm 包名(repo → 包名映射):目录数据可能未下发
7
+ /** 安装时记录在案的官方 npm 包名(repo → 包名映射):目录数据可能未下发
8
8
  * npmPackage(组织 scope 与 GitHub 用户名不一致),客户端据此把依赖 key 匹配回仓库 */
9
9
  npmPackage?: string;
10
10
  }
11
11
  export type InstalledVersionTable = Record<string, InstalledVersionRecord>;
12
12
  export declare function versionsFilePath(profile: string): string;
13
13
  export declare function readInstalledVersions(profile: string): InstalledVersionTable;
14
- /** Record the catalog signals (version + repo updated time) present at install time for one repo. */
14
+ /** 记录某仓库安装时的目录信号(版本 + 仓库更新时间)。 */
15
15
  export declare function recordInstalledVersion(profile: string, repo: string, version: string, updatedAt: string): void;
16
16
  /**
17
- * Record the npm package name resolved for a repo (npm-first install channel).
18
- * Called at `/install` time so the mapping exists even before the task finishes;
19
- * harmless when the repo is not (yet) installed — the client only treats a repo
20
- * as installed when its dependency key actually exists.
17
+ * 记录某仓库解析出的 npm 包名。在 `/install` 时调用,让映射在任务完成前就存在;
18
+ * 仓库尚未安装时也无害 —— 客户端只在依赖 key 真实存在时才把仓库当作已安装。
21
19
  */
22
20
  export declare function recordResolvedNpmPackage(profile: string, repo: string, npmPackage: string): void;
23
- /** Forget the recorded version for one repo (called on uninstall). */
21
+ /** 清除某仓库记录的版本(卸载时调用)。 */
24
22
  export declare function removeInstalledVersion(profile: string, repo: string): void;
@@ -1,4 +1,4 @@
1
- /** Resolve the active profile from the booted CLI args, falling back to `web`. */
1
+ /** 从启动参数解析当前 profile,取不到落回 `web`。 */
2
2
  export declare function readProfileArg(fallback?: string): string;
3
3
  /**
4
4
  * 从启动参数解析当前 profile,两种宿主形态都认:
@@ -10,32 +10,28 @@ export declare function readProfileArg(fallback?: string): string;
10
10
  * 位置参数按「父目录名为 profiles」精确识别 profile 目录,避免误取其它位置参数。
11
11
  */
12
12
  export declare function profileFromArgv(argv: readonly string[], fallback?: string): string;
13
- /** Resolve a profile directory (`DSH_HOME` or `~/.dsh`). */
13
+ /** 解析 profile 目录(`DSH_HOME` 或 `~/.dsh`)。 */
14
14
  export declare function profileDirectory(profile: string): string;
15
- /** Build a safe explicit HTTPS Git target, or null when the repo is unsafe.
16
- * When `ref` is given it is appended as a Git ref (`#tag` / `#commit`) so an
17
- * explicitly pinned revision reaches the installer instead of being dropped. */
15
+ /** 把仓库身份转成规范的 GitHub 目标(git+https),仓库不合法时返回 null。
16
+ * 带 ref 时按 Git ref(`#tag` / `#commit`)拼接;安装通道已不再使用 git 目标,此函数仅做身份规范化。 */
18
17
  export declare function githubTarget(repo: string, ref?: string | null): string | null;
19
- export declare function githubRefOf(value: string): string | null;
20
18
  export declare function installTargetOf(value: string): string;
21
19
  export declare function globalNpmPackagesOf(value: string): string[] | null;
22
- /** Extract an owner/repo identity from a catalog value or an installed Git spec. */
20
+ /** 从目录值或已装 Git spec 中提取 owner/repo 身份。 */
23
21
  export declare function githubRepoOf(value: string): string | null;
24
- /** Validate an npm package name (uninstall target grammar). */
22
+ /** 校验 npm 包名(卸载目标的语法)。 */
25
23
  export declare function validPackageName(name: string): boolean;
26
24
  /**
27
- * The exact `allowBuilds` key pnpm printed in its
28
- * `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` hint. For Git installs pnpm
29
- * matches against `name@<resolved fetch URL>` — the URL it actually fetches,
30
- * e.g. `https://codeload.github.com/<owner>/<repo>/tar.gz/<commit>` — not the
31
- * `git+ssh://`/branch spec. A bare name or a `git+` spec does not match, so
32
- * the only reliable key is the one pnpm prints verbatim. Null when absent.
25
+ * 取 pnpm 在 `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` 提示里原样打印的 `allowBuilds` key。
26
+ * Git 安装时 pnpm 按 `name@<实际 fetch URL>`(如
27
+ * `https://codeload.github.com/<owner>/<repo>/tar.gz/<commit>`)匹配,而不是 `git+ssh://`
28
+ * 或分支 spec —— 裸包名与 `git+` spec 都匹配不上,唯一可靠的 key 就是 pnpm 原样输出的那个。
29
+ * 无则返回 null。
33
30
  */
34
31
  export declare function parseAllowBuildsKey(output: string): string | null;
35
32
  /**
36
- * Add one `allowBuilds` entry to the profile's pnpm-workspace.yaml, merging
37
- * with existing entries and leaving the rest of the yaml intact. Broken
38
- * placeholder entries (pnpm's failed-install bug writes a literal
39
- * `set this to true or false` value) are dropped on rewrite.
33
+ * 向 profile 的 pnpm-workspace.yaml 追加一条 `allowBuilds` 记录:与已有条目合并,
34
+ * 其余 yaml 保持不变;重写时丢弃损坏的占位条目(pnpm 安装失败的 bug 会写入
35
+ * 字面量 `set this to true or false`)。
40
36
  */
41
37
  export declare function addAllowBuildsKey(profile: string, key: string): void;
@@ -3,19 +3,16 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Pure progress-estimation helpers shared by the install runner.
6
+ * 安装执行器共用的纯进度估算工具。
7
7
  *
8
- * The dsh CLI wraps pnpm, which refreshes its `Progress: …` line in place
9
- * with carriage returns, so raw output arrives as a stream of partial
10
- * fragments. These helpers normalize one fragment into a clean terminal
11
- * line and map it onto a 0-100 estimate; keeping them free of I/O makes the
12
- * estimation logic directly unit-testable (see tests/progress.test.ts).
8
+ * dsh CLI 包装了 pnpm,pnpm 用回车原地刷新 `Progress: …` 行,因此原始输出是一串碎片。
9
+ * 这些工具把单个碎片归一化成干净的终端行,并映射到 0-100 估算值;不碰 I/O
10
+ * 让估算逻辑可直接单测(见 tests/progress.test.ts)。
13
11
  */
14
- /** Strip the `[out]` / `[err]` source prefix and ANSI escape sequences. */
12
+ /** 剥掉 `[out]` / `[err]` 来源前缀与 ANSI 转义序列。 */
15
13
  export declare function cleanLine(raw: string): string;
16
14
  /**
17
- * Estimate 0-100 progress from one CLI output line. pnpm emits
18
- * `Progress: resolved N, reused X, downloaded Y, added Z` during the fetch
19
- * phase; resolution/install phase lines bump the estimate towards done.
15
+ * 从单行 CLI 输出估算 0-100 进度。pnpm 在 fetch 阶段输出
16
+ * `Progress: resolved N, reused X, downloaded Y, added Z`;解析/安装阶段的行把估算值推向完成。
20
17
  */
21
18
  export declare function estimateProgress(line: string): number;
@@ -7,10 +7,6 @@ export interface HubSettings {
7
7
  npmRegistry: string;
8
8
  /** 启用命令行安装 NPM 包(默认开;关掉后输入 npm 包名安装被拦截) */
9
9
  enableNpmInstall: boolean;
10
- /** 启用命令行安装 GitHub 源码(默认开;关掉后输入 GitHub 地址安装被拦截) */
11
- enableGitInstall: boolean;
12
- /** 启用命令行粘贴 dsh plugin 命令安装(默认开;关掉后命令输入被禁用) */
13
- enableDshInstall: boolean;
14
10
  /** 日志存放位置覆盖(空串 = 默认 ~/.dsh/profiles/<profile>/hub.log;可填目录或 .log 文件路径) */
15
11
  logPath: string;
16
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin",
3
- "version": "1.6.12",
3
+ "version": "2.0.1",
4
4
  "description": "A community plugin marketplace for DeepSeek Harness, built to the official plugin spec — browse, search and install 10000+ human-curated community plugins without leaving the app. · DeepSeek Harness 社区插件市场(遵循官方开发规范):10000+ 人工精选社区插件,每日更新。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -3,10 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Plugin Hub section: wires the catalog data pipeline, the background task
7
- * queue and the feedback state together. Data/queue logic lives in hooks/,
8
- * rendering is delegated to the small presentational components in this
9
- * folder; it hosts the dialogs/toast and the section-level copy actions.
6
+ * 插件中心板块:把目录数据管道、后台任务队列与反馈状态接在一起。数据/队列逻辑在 hooks/,
7
+ * 渲染交给本目录下的小型展示组件;本文件托管各弹窗/Toast 与板块级复制动作。
10
8
  */
11
9
  import { createElement as h, useEffect, useRef, useState } from 'react'
12
10
  import type { MouseEvent } from 'react'
@@ -78,10 +76,10 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
78
76
  const [confirmPlugin, setConfirmPlugin] = useState<HubPlugin | null>(null)
79
77
  /** 弹窗动作是否为「更新」:已安装插件点「更新」→ 走同一条 add 命令原位覆盖重装,文案区分安装/更新 */
80
78
  const [confirmIsUpdate, setConfirmIsUpdate] = useState(false)
81
- /** 命令行安装确认弹窗:记录待安装的裸目标(npm 包名 / GitHub 地址,DSH 命令已剥成裸目标)——
79
+ /** 命令行安装确认弹窗:记录待安装的裸目标(npm 包名)——
82
80
  * 与应用商店同一套确认/进度/结果弹窗,提供时 plugin 传 null 走 customTarget 模式 */
83
81
  const [confirmCustomTarget, setConfirmCustomTarget] = useState<string | null>(null)
84
- /** 自定义安装入口渠道(三张卡片之一):随确认弹窗记录,提交时上报服务端,
82
+ /** 自定义安装入口渠道(NPM 包卡片):随确认弹窗记录,提交时上报服务端,
85
83
  * 报错/日志据此精准溯源「从哪个入口发起」(npm 全局安装固定为 'npm') */
86
84
  const [confirmCustomChannel, setConfirmCustomChannel] = useState<InstallChannel | null>(null)
87
85
  /** 全局 npm 安装确认弹窗(官方 `npm install -g <pkgs>`):记录原始命令 + 解析出的包列表,
@@ -142,7 +140,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
142
140
 
143
141
  const queue = useTaskQueue({
144
142
  t,
145
- // 界面语言带给服务端:网络预检等错误消息按用户语言提示(中文界面给中文,英文界面给英文)
143
+ // 界面语言带给服务端:安装前网络探测等错误消息按用户语言提示(中文界面给中文,英文界面给英文)
146
144
  langKey,
147
145
  refreshInstalled: catalog.refreshInstalled,
148
146
  onInstallDone: (viaModal, repo, needsRestart, update, needsReload) => {
@@ -170,6 +168,15 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
170
168
  setNotifications(addSuccess({ kind: 'uninstall', repo: repo ?? '' }))
171
169
  },
172
170
  onError: (message, repo, kind, command, attempts, update) => {
171
+ // 失败只由错误弹窗呈现:先关掉发起本次操作的确认/进度弹窗
172
+ setInstallDone(false)
173
+ setUninstallDone(false)
174
+ setConfirmPlugin(null)
175
+ setConfirmCustomTarget(null)
176
+ setConfirmCustomChannel(null)
177
+ setConfirmGlobalNpm(null)
178
+ setUninstallPlugin(null)
179
+ setUninstallItem(null)
173
180
  // 提 Issue 目标先反查仓库身份:npm 包名能在已安装表/目录里反查到 owner/repo 才保留,
174
181
  // 反查不到置 null —— 错误弹窗据此不显示「一键提 Issue」按钮(防提到错误仓库)
175
182
  const issueRepo = issueRepoOf(repo ?? '', catalog.installedItems, catalog.plugins)
@@ -224,10 +231,12 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
224
231
  useEffect(() => {
225
232
  if (startupCheckedRef.current) return
226
233
  if (!settingsReady || catalog.plugins === null || catalog.failed) return
234
+ // 供应链门禁结果未就绪时先等:否则刚发布的插件会先写进通知中心,冒出装不上的更新提醒
235
+ if (!catalog.releaseAgeReady) return
227
236
  startupCheckedRef.current = true
228
237
  if (!hubSettings.checkUpdatesOnStart) return
229
238
  syncUpdateNotices()
230
- }, [settingsReady, hubSettings, catalog.plugins, catalog.failed, catalog.installedItems, queue])
239
+ }, [settingsReady, hubSettings, catalog.plugins, catalog.failed, catalog.installedItems, catalog.releaseAgeReady, queue])
231
240
 
232
241
  // 信任/卸载/更新说明弹窗打开时按 Esc 关闭;任务进入后台队列后可随时关闭(任务继续)
233
242
  useEffect(() => {
@@ -265,8 +274,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
265
274
  await navigator.clipboard.writeText(text)
266
275
  return true
267
276
  } catch {
268
- // Clipboard API unavailable (permissions/iframe) — fall back to the
269
- // legacy hidden-textarea trick, which works in any context.
277
+ // Clipboard API 不可用(权限/iframe)—— 退回旧的隐藏 textarea 方案,任何环境都可用。
270
278
  const ta = document.createElement('textarea')
271
279
  ta.value = text
272
280
  ta.style.position = 'fixed'
@@ -306,7 +314,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
306
314
  Object.keys(catalog.installed).some((k) => k.toLowerCase() === pkg)
307
315
  }
308
316
 
309
- /** 弹窗动作一:复制安装命令到剪贴板,引导去终端粘贴执行(npm 通道显示包名命令)。 */
317
+ /** 弹窗动作一:复制安装命令到剪贴板,引导去终端粘贴执行(npm 安装显示包名命令)。 */
310
318
  const copyCommand = async (p: HubPlugin) => {
311
319
  const repo = p.source?.repo ?? ''
312
320
  const ok = await doCopy(installCommandOf(p))
@@ -417,8 +425,8 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
417
425
  noticeCount: notifications.length + queue.queue.length + queue.pendingRestarts.length,
418
426
  onOpenNotifications: () => {
419
427
  // 打开弹窗时补齐更新提醒(受「启动时检查更新」开关约束):同一版本只一条,
420
- // 清空/删除后重开仍恢复,直到插件更新完成才消失
421
- if (hubSettings.checkUpdatesOnStart) syncUpdateNotices()
428
+ // 清空/删除后重开仍恢复,直到插件更新完成才消失;门禁结果未就绪时先不写,避免提示装不上的更新
429
+ if (hubSettings.checkUpdatesOnStart && catalog.releaseAgeReady) syncUpdateNotices()
422
430
  setShowNotifications(true)
423
431
  },
424
432
  }),
@@ -489,17 +497,13 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
489
497
  : view === 'custom'
490
498
  ? h(CustomInstallView, {
491
499
  t,
492
- // 命令行安装:与应用商店同一套确认/进度/结果弹窗 —— 输入 npm 包名 / GitHub 地址 /
493
- // dsh plugin 命令即装(custom 源,受安全设置三开关控制)。点安装先弹确认窗,
500
+ // 命令行安装:与应用商店同一套确认/进度/结果弹窗 —— 输入 npm 包名 / npm、pnpm
501
+ // 安装命令即装(custom 源,受安全设置开关控制)。点安装先弹确认窗,
494
502
  // 确认后走队列安装(实时进度 + 成功结果视图);目标已安装 → 弹窗转「更新」覆盖重装,
495
503
  // 不会撞「already installed」报错。
496
- // 安全信任开关关掉对应通道 → 卡片禁用并提示去设置打开(onOpenSettings 跳到设置页)。
504
+ // 安全信任开关关掉 → 卡片禁用并提示去设置打开(onOpenSettings 跳到设置页)。
497
505
  enableNpm: hubSettings.enableNpmInstall,
498
- enableGit: hubSettings.enableGitInstall,
499
- enableDsh: hubSettings.enableDshInstall,
500
506
  onOpenSettings: () => setView('settings'),
501
- // 当前 profile:一键插入的 Hub 更新命令用它拼 --profile
502
- profile: env?.profile ?? 'web',
503
507
  onInstallCustom: (raw, opts) => {
504
508
  const target = raw.trim()
505
509
  if (!target) return
@@ -516,7 +520,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
516
520
  return
517
521
  }
518
522
  setConfirmGlobalNpm(null)
519
- // 记录入口渠道:报错/日志据此精准提示「从哪张卡片发起的安装」(NPM 包 / GitHub 源码 / DSH 命令)
523
+ // 记录入口渠道:报错/日志据此精准提示「从哪张卡片发起的安装」
520
524
  setConfirmCustomChannel(opts.installChannel)
521
525
  setConfirmIsUpdate(customTargetInstalled(target))
522
526
  setConfirmCustomTarget(target)
@@ -696,7 +700,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
696
700
  },
697
701
  onInstall: () => void queue.installCustom(confirmCustomTarget, {
698
702
  update: confirmIsUpdate,
699
- // 入口渠道(NPM 包 / GitHub 源码 / DSH 命令卡片):上报服务端,报错/日志据此溯源
703
+ // 入口渠道(NPM 包卡片):上报服务端,报错/日志据此溯源
700
704
  channel: confirmCustomChannel ?? 'npm',
701
705
  }),
702
706
  onRestart: () => setShowRestartConfirm(true),
@@ -3,10 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Catalog list: the scrollable container with the loading / failed / empty
7
- * states, the plugin cards, and the count footer. Resets scroll position when
8
- * the category or install-status filter changes so the replaced content is
9
- * not mistaken for a no-op update.
6
+ * 目录列表:承载加载/失败/空三态、插件卡片与计数页脚的滚动容器。分类或安装状态
7
+ * 筛选变化时重置滚动位置,避免替换后的内容被误当成「没变化」。
10
8
  */
11
9
  import { createElement as h, useEffect, useRef } from 'react'
12
10
  import styles from '../../styles/List.module.css'
@@ -3,9 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * One plugin card in the catalog list: name/category/verified badge,
7
- * description + topics, star/fork/date stats, and the detail/install/
8
- * uninstall actions.
6
+ * 目录列表里的单张插件卡片:名称/分类/verified 徽标、描述 + 标签、Star/Fork/日期
7
+ * 统计,以及详情/安装/卸载动作。
9
8
  */
10
9
  import { createElement as h } from 'react'
11
10
  import styles from '../../styles/List.module.css'
@@ -3,12 +3,11 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Toolbar above the market list:
7
- * row 1 — full-width search input;
8
- * row 2 — result count on the left, sort segmented control on the right
9
- * (mirrors the Installed view: clicking the same sort toggles
10
- * ascending/descending, a new sort uses its default order; the active
11
- * button shows an up/down arrow for the current direction).
6
+ * 市场列表上方的工具栏:
7
+ * 第一行 —— 整行宽的搜索框;
8
+ * 第二行 —— 左侧结果计数,右侧排序分段控件
9
+ * (与已安装视图一致:点同一排序切换正/倒序,点新排序用其默认方向;
10
+ * 激活按钮显示当前方向的上/下箭头)。
12
11
  */
13
12
  import { createElement as h, Fragment } from 'react'
14
13
  import type { FormEvent, ReactNode } from 'react'
@@ -3,8 +3,7 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Section header: brand title row (H1 + open-site button) + tagline, followed
7
- * by the purple ad banner that promotes the catalog stats.
6
+ * 板块头部:品牌标题行(H1 + 官网入口按钮)+ 副标题,其后是宣传目录统计的紫色广告条。
8
7
  */
9
8
  import { createElement as h, Fragment } from 'react'
10
9
  import styles from '../../styles/Header.module.css'
@@ -3,9 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Category tabs: an "all" chip followed by one chip per catalog category.
7
- * The "all" chip carries the total plugin count; the per-category chips
8
- * carry no counts so their widths stay uniform.
6
+ * 分类 Tab:先是「全部」chip,其后每个目录分类一个 chip。「全部」chip 带插件总数,
7
+ * 各分类 chip 不带计数以保持宽度一致。
9
8
  */
10
9
  import { createElement as h } from 'react'
11
10
  import styles from '../../styles/Header.module.css'
@@ -3,11 +3,9 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Primary navigation: a compact segmented control switching between the
7
- * four top-level sections — Market (browse), Installed (manage), Custom
8
- * (manual command installs) and Settings (network sources & preferences).
9
- * Kept visually distinct from the category chips so the hierarchy reads:
10
- * section → category → plugin.
6
+ * 一级导航:紧凑分段控件,在四个顶层板块间切换 —— 市场(浏览)、已安装(管理)、
7
+ * 自定义(手动命令安装)与设置(网络源与偏好)。与分类 chip 视觉上区分开,
8
+ * 让层级清晰:板块 → 分类 → 插件。
11
9
  */
12
10
  import { createElement as h } from 'react'
13
11
  import styles from '../../styles/SectionTabs.module.css'
@@ -3,13 +3,11 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Generic confirm dialog — the single entry point for every "are you sure?"
7
- * question in the app. Rule: any question must be a modal dialog, never an
8
- * in-button double-click.
6
+ * 通用确认弹窗 —— 应用里所有「确定吗?」问题的唯一入口。规则:任何询问都必须是
7
+ * 弹窗,绝不用按钮内双击。
9
8
  *
10
- * The icon in the middle tells the type at a glance: a blue question mark
11
- * for routine confirmations (install / update / restart / ignore), a red
12
- * action glyph for destructive ones (clear / delete / uninstall / reset).
9
+ * 中间的图标一眼说明类型:常规确认(安装/更新/重启/忽略)用蓝色问号,
10
+ * 危险操作(清空/删除/卸载/重置)用红色动作图形。
13
11
  */
14
12
  import { createElement as h } from 'react'
15
13
  import type { MouseEvent } from 'react'
@@ -3,12 +3,9 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Installed-plugin detail dialog: opens from the Installed view (row click
7
- * or the detail button) and shows everything we know about an installed
8
- * entry — the catalog metadata (category, catalog entry, stars/forks,
9
- * latest version) merged with the host runtime facts (install path, installed
10
- * version, install time, last update). Update / uninstall actions are
11
- * offered from here too, mirroring the row actions.
6
+ * 已安装插件详情弹窗:从「已安装」视图打开(行点击或详情按钮),展示我们对某个已安装项
7
+ * 掌握的全部信息 —— 目录元数据(分类、收录入口、Star/Fork、最新版本)与宿主运行时事实
8
+ * (安装路径、已装版本、安装时间、最后更新)合并呈现。此处的更新/卸载动作与行内动作一致。
12
9
  */
13
10
  import { createElement as h } from 'react'
14
11
  import type { MouseEvent } from 'react'
@@ -3,20 +3,15 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Built-in log viewer dialog: no external software needed — the app opens
7
- * its own window and loads the local system log (JSONL at
8
- * ~/.dsh/profiles/<profile>/hub.log) straight from the server, one page at
9
- * a time.
6
+ * 内置日志查看器弹窗:无需外部软件 —— 应用自己开窗,从服务端直接分页加载本地系统日志
7
+ * (JSONL,位于 ~/.dsh/profiles/<profile>/hub.log)。
10
8
  *
11
- * Every line is categorized twice: a function area (install / uninstall /
12
- * update / diagnostics / settings / system) and a severity level (debug /
13
- * info / success / warn / error). Filtering on both axes lets you target,
14
- * say, only install errors — so the exact incompatible step of an install
15
- * (or uninstall) is easy to spot. Search narrows by event code or message.
9
+ * 每行双重归类:功能域(安装/卸载/更新/诊断/设置/系统)与严重级别(debug/info/success/
10
+ * warn/error)。两轴筛选可只盯某类,比如只看安装错误 —— 便于定位安装(或卸载)中真正
11
+ * 卡住的那一步;搜索按事件码或消息缩小范围。
16
12
  *
17
- * Server pagination: GET /dsh-plugin-hub/logs?offset=&limit=&category=
18
- * &level=&query= → { entries, total, hasMore, path }. The footer shows the
19
- * real file location and reveals it in the system file manager.
13
+ * 服务端分页:GET /dsh-plugin-hub/logs?offset=&limit=&category=&level=&query=
14
+ * → { entries, total, hasMore, path }。底部展示日志真实路径并可在系统文件管理器中定位。
20
15
  */
21
16
  import { createElement as h, useCallback, useEffect, useRef, useState } from 'react'
22
17
  import type { MouseEvent } from 'react'