dsh-plugin 1.6.11 → 2.0.0

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 (128) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/client/client.js +705 -803
  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/package-entry.js +6 -11
  20. package/lib/services/install/registry-check.js +5 -16
  21. package/lib/services/install/release-target.js +8 -9
  22. package/lib/services/install/task-queue.js +74 -57
  23. package/lib/services/npm-release-age.js +119 -0
  24. package/lib/services/probe.js +4 -4
  25. package/lib/services/profile/installed-versions.js +15 -21
  26. package/lib/services/profile/profile.js +16 -44
  27. package/lib/services/profile/progress.js +8 -11
  28. package/lib/services/settings.js +2 -5
  29. package/lib/types/http/catalog.d.ts +1 -1
  30. package/lib/types/http/install.d.ts +2 -2
  31. package/lib/types/http/npm-release-age.d.ts +2 -0
  32. package/lib/types/http/routes.d.ts +10 -12
  33. package/lib/types/http/shared.d.ts +5 -6
  34. package/lib/types/index.d.ts +7 -8
  35. package/lib/types/services/install/install-types.d.ts +12 -13
  36. package/lib/types/services/install/install.d.ts +1 -1
  37. package/lib/types/services/install/npm-resolve.d.ts +4 -16
  38. package/lib/types/services/install/package-entry.d.ts +6 -11
  39. package/lib/types/services/install/release-target.d.ts +7 -8
  40. package/lib/types/services/install/task-queue.d.ts +32 -31
  41. package/lib/types/services/npm-release-age.d.ts +15 -0
  42. package/lib/types/services/probe.d.ts +4 -4
  43. package/lib/types/services/profile/installed-versions.d.ts +5 -7
  44. package/lib/types/services/profile/profile.d.ts +14 -18
  45. package/lib/types/services/profile/progress.d.ts +7 -10
  46. package/lib/types/services/settings.d.ts +0 -4
  47. package/package.json +1 -1
  48. package/src/client/components/PluginHubSection.tsx +23 -27
  49. package/src/client/components/catalog/CatalogList.tsx +2 -4
  50. package/src/client/components/catalog/PluginCard.tsx +2 -3
  51. package/src/client/components/layout/CatalogControls.tsx +5 -6
  52. package/src/client/components/layout/CatalogHeader.tsx +1 -2
  53. package/src/client/components/layout/CategoryTabs.tsx +2 -3
  54. package/src/client/components/layout/SectionTabs.tsx +3 -5
  55. package/src/client/components/modals/ConfirmDialog.tsx +4 -6
  56. package/src/client/components/modals/InstalledDetailModal.tsx +3 -6
  57. package/src/client/components/modals/LogsModal.tsx +7 -12
  58. package/src/client/components/modals/NotificationsModal.tsx +52 -24
  59. package/src/client/components/modals/ProgressView.tsx +3 -4
  60. package/src/client/components/modals/modals.tsx +58 -30
  61. package/src/client/components/ui/Dropdown.tsx +4 -6
  62. package/src/client/components/ui/Toggle.tsx +2 -3
  63. package/src/client/components/ui/icons.tsx +1 -2
  64. package/src/client/components/views/CustomInstallView.tsx +20 -178
  65. package/src/client/components/views/DiagnosticsView.tsx +3 -6
  66. package/src/client/components/views/InstalledView.tsx +6 -11
  67. package/src/client/components/views/MarketView.tsx +2 -4
  68. package/src/client/components/views/SettingsView.tsx +9 -32
  69. package/src/client/css-modules.d.ts +1 -1
  70. package/src/client/data/catalog.ts +2 -3
  71. package/src/client/data/host.ts +26 -3
  72. package/src/client/data/hub.ts +2 -3
  73. package/src/client/hooks/useCatalog.ts +79 -24
  74. package/src/client/hooks/useIncrementalList.ts +3 -5
  75. package/src/client/hooks/useLanguage.ts +4 -6
  76. package/src/client/hooks/useSettings.ts +4 -11
  77. package/src/client/hooks/useTaskQueue.ts +36 -42
  78. package/src/client/index.tsx +2 -3
  79. package/src/client/locales.ts +96 -123
  80. package/src/client/logic/constants.ts +1 -2
  81. package/src/client/logic/ensure-plugin-css.ts +8 -12
  82. package/src/client/logic/failures.ts +41 -28
  83. package/src/client/logic/format.ts +1 -1
  84. package/src/client/logic/install-command.ts +14 -35
  85. package/src/client/logic/installed.ts +15 -10
  86. package/src/client/logic/normalize.ts +3 -3
  87. package/src/client/logic/release-age.ts +19 -0
  88. package/src/client/logic/urls.ts +55 -50
  89. package/src/client/styles/CustomInstallView.module.css +3 -30
  90. package/src/client/styles/DiagnosticsView.module.css +3 -5
  91. package/src/client/styles/Dropdown.module.css +2 -4
  92. package/src/client/styles/Header.module.css +6 -7
  93. package/src/client/styles/InstalledView.module.css +4 -7
  94. package/src/client/styles/List.module.css +4 -5
  95. package/src/client/styles/LogsView.module.css +2 -3
  96. package/src/client/styles/Modal.module.css +4 -5
  97. package/src/client/styles/SectionTabs.module.css +2 -4
  98. package/src/client/styles/SettingsView.module.css +3 -5
  99. package/src/client/styles/Toggle.module.css +2 -4
  100. package/src/client/styles/tokens.module.css +14 -18
  101. package/src/client/types.ts +11 -11
  102. package/src/server/http/catalog.ts +2 -3
  103. package/src/server/http/diagnostics.ts +6 -7
  104. package/src/server/http/install.ts +47 -208
  105. package/src/server/http/logs.ts +1 -2
  106. package/src/server/http/npm-release-age.ts +42 -0
  107. package/src/server/http/routes.ts +12 -12
  108. package/src/server/http/settings.ts +2 -2
  109. package/src/server/http/shared.ts +5 -6
  110. package/src/server/http/task.ts +1 -2
  111. package/src/server/index.ts +7 -8
  112. package/src/server/services/install/install-types.ts +12 -13
  113. package/src/server/services/install/install.ts +0 -1
  114. package/src/server/services/install/npm-check.ts +3 -2
  115. package/src/server/services/install/npm-resolve.ts +9 -99
  116. package/src/server/services/install/package-entry.ts +6 -11
  117. package/src/server/services/install/registry-check.ts +5 -16
  118. package/src/server/services/install/release-target.ts +8 -9
  119. package/src/server/services/install/task-queue.ts +79 -63
  120. package/src/server/services/npm-release-age.ts +107 -0
  121. package/src/server/services/probe.ts +4 -4
  122. package/src/server/services/profile/installed-versions.ts +16 -22
  123. package/src/server/services/profile/profile.ts +16 -42
  124. package/src/server/services/profile/progress.ts +8 -11
  125. package/src/server/services/settings.ts +2 -9
  126. package/lib/services/install/preflight.js +0 -178
  127. package/lib/types/services/install/preflight.d.ts +0 -23
  128. package/src/server/services/install/preflight.ts +0 -184
@@ -3,18 +3,12 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Custom install view: a standalone top-level section for manually
7
- * installing npm packages or GitHub sources outside the catalog. Three
8
- * installer cards, one per channel — npm package (bare name or a full
9
- * npm/pnpm install command, including the official global `npm install -g`),
10
- * GitHub source and the official `dsh plugin ... add <target>` command —
11
- * each with a placeholder example above the input and its own format check.
12
- * The cards stay separate so a user never has to guess which format goes
13
- * where. Finished installs surface in the Installed view, marked as custom.
6
+ * 自定义安装视图:独立一级板块,用于手动安装目录之外的 npm 包。安装卡片接受裸包名或完整的
7
+ * npm/pnpm 安装命令(含官方全局 `npm install -g`),输入框上方带示例并自带格式校验。
8
+ * 装完的插件出现在「已安装」视图,标记为自定义安装。
14
9
  *
15
- * Pure presentational: owns only the three local input/error states; the
16
- * actual install (custom source, gated by the security toggles) bubbles up
17
- * via onInstallCustom.
10
+ * 纯展示组件:只持有本地输入/错误状态;真正的安装(custom 源,受安全开关控制)经
11
+ * onInstallCustom 上抛。
18
12
  */
19
13
  import { createElement as h, useState } from 'react'
20
14
  import type { FormEvent, MouseEvent } from 'react'
@@ -23,27 +17,14 @@ import modal from '../../styles/Modal.module.css'
23
17
  import type { InstallChannel, Translate } from '../../types.ts'
24
18
  import { CloseIcon, HelpIcon } from '../ui/icons.tsx'
25
19
 
26
- /* —— 自定义安装输入预检 ——
27
- * 提交前校验「输入是否符合该通道格式」,识别不了的输入不发请求,就地提示推荐格式。
28
- * 三个通道严格分开:npm 框认裸包名或完整 npm/pnpm 安装命令(含全局 `npm install -g`),
29
- * git 框只认 GitHub 地址,命令框只认官方 `dsh plugin --profile <name> <add|update> <target>`
30
- * (--profile 必填,无 -p 简写;update = 更新已安装目标到最新)。
20
+ /* —— 自定义安装输入校验 ——
21
+ * 提交前校验「输入是否符合格式」,识别不了的输入不发请求,就地提示推荐格式。
22
+ * npm 框认裸包名或完整 npm/pnpm 安装命令(含全局 `npm install -g`)。
31
23
  * 这里只做格式校验,真正归一化/安装由服务端完成。 */
32
24
 
33
- /** GitHub 地址(带前缀形态):github:、https://github.com/、git+https://github.com/、git@github.com: */
34
- const GITHUB_URL_RE = /^(?:github:|https?:\/\/github\.com\/|git\+https?:\/\/github\.com\/|git@github\.com:)([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?$/
35
- /** 裸 owner/repo(npm 包名只有 @scope/pkg 才带斜杠,无 @ 前缀的 a/b 一定是 GitHub 仓库) */
36
- const GITHUB_BARE_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/
37
25
  /** npm 包名(含 @scope/pkg) */
38
26
  const NPM_PACKAGE_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/
39
27
 
40
- /** DSH 命令行前缀(官方唯一形式 `dsh plugin --profile <name> <add|update> <target>`)—— 提取其中的动作与目标。
41
- * 官方 CLI(apps/cli/src/args.ts,commander):--profile 为必填长选项(无 -p 简写,省略即报错),
42
- * 支持 `--profile=<name>` 等号形式;命令大小写不敏感。update = 已安装目标覆盖更新到最新;
43
- * remove 不走这里(卸载由「已安装」列表的卸载按钮执行,命令框不认卸载动词)。
44
- * 与 server 端 installTargetOf 口径一致。 */
45
- const DSH_PLUGIN_CMD_RE = /^dsh\s+plugin\s+--profile(?:\s+|=)\S+\s+(add|update)\s+(.+)$/i
46
-
47
28
  /** npm 全局安装命令(官方 README 格式,如 `npm install -g @deepseek-ai/dsh @deepseek-harness-tui/dsh-tui`)——
48
29
  * 提取 -g 之后的包列表;任一包名非法返回 null(交给格式错误提示)。与 server 端 globalNpmPackagesOf 口径一致。 */
49
30
  const NPM_GLOBAL_CMD_RE = /^npm\s+(?:install|i)\s+(?:-g|--global)\s+(.+)$/i
@@ -52,14 +33,6 @@ const NPM_GLOBAL_CMD_RE = /^npm\s+(?:install|i)\s+(?:-g|--global)\s+(.+)$/i
52
33
  * 提取命令后的包列表;包列表在 splitPackages 里逐个校验。 */
53
34
  const NPM_INSTALL_CMD_RE = /^(?:npm\s+(?:install|i)|pnpm\s+(?:add|i|install))\s+(.+)$/i
54
35
 
55
- /** 解析 DSH 命令:返回 { action, target };非官方命令返回 null(交给格式错误提示)。 */
56
- function parseDshCommand(raw: string): { action: 'add' | 'update'; target: string } | null {
57
- const input = raw.trim()
58
- const match = DSH_PLUGIN_CMD_RE.exec(input)
59
- if (match === null) return null
60
- return { action: match[1].toLowerCase() as 'add' | 'update', target: match[2].trim() }
61
- }
62
-
63
36
  /** 空格分隔的包列表:逐个按 npm 包名语法校验 + 拒绝以 - 开头的 token(npm 会把 --xxx 当参数而非包名),
64
37
  * 任一非法返回 null(交给格式错误提示)。与 server 端 globalNpmPackagesOf 同口径。 */
65
38
  function splitPackages(raw: string): string[] | null {
@@ -96,46 +69,28 @@ function parseNpmInput(raw: string): NpmInputParse | null {
96
69
  return null
97
70
  }
98
71
 
99
- function isGitHubInput(raw: string): boolean {
100
- const input = raw.trim()
101
- return GITHUB_URL_RE.test(input) || GITHUB_BARE_RE.test(input)
102
- }
103
-
104
- /** 卡片标题旁的「帮助」弹窗:三块卡片各自的标题与格式清单语言包 key。 */
72
+ /** 卡片标题旁的「帮助」弹窗:该卡片的标题与格式清单语言包 key。 */
105
73
  const HELP_CARDS = {
106
74
  npm: { titleKey: 'npmInstallLabel', contentKey: 'npmInstallHelp' },
107
- git: { titleKey: 'gitInstallLabel', contentKey: 'gitInstallHelp' },
108
- cmd: { titleKey: 'dshCmdLabel', contentKey: 'dshCmdHelp' },
109
75
  } as const
110
76
 
111
77
  type HelpTarget = keyof typeof HELP_CARDS
112
78
 
113
- export function CustomInstallView({ t, onInstallCustom, enableNpm, enableGit, enableDsh, onOpenSettings, profile }: {
79
+ export function CustomInstallView({ t, onInstallCustom, enableNpm, onOpenSettings }: {
114
80
  t: Translate
115
- /** 命令行安装:输入 npm 包名 / npm、pnpm 安装命令 / GitHub 地址 / dsh plugin 命令即装
116
- * (custom 源,受安全设置开关控制)。installChannel 必填 = 入口渠道(三张卡片之一):
117
- * 服务端日志/报错提示据此溯源「从哪个入口发起」;opts.globalNpm 表示官方 npm 全局安装
118
- * 命令(npm install -g),附带解析出的包列表(仍属 NPM 包入口)。 */
81
+ /** 命令行安装:输入 npm 包名 / npm、pnpm 安装命令即装(custom 源,受安全设置开关控制)。
82
+ * installChannel 必填 = 入口渠道(NPM 包卡片):服务端日志/报错提示据此溯源「从哪个入口发起」;
83
+ * opts.globalNpm 表示官方 npm 全局安装命令(npm install -g),附带解析出的包列表。 */
119
84
  onInstallCustom: (raw: string, opts: { installChannel: InstallChannel; globalNpm?: string[] }) => void
120
- /** 安全信任三通道开关:关掉后对应输入卡片禁用(不提交),并引导去设置打开 */
85
+ /** 安全信任开关:关掉后输入卡片禁用(不提交),并引导去设置打开 */
121
86
  enableNpm: boolean
122
- enableGit: boolean
123
- enableDsh: boolean
124
87
  /** 引导去「设置 → 安全信任」打开被关闭的通道开关 */
125
88
  onOpenSettings: () => void
126
- /** 当前 dsh profile 名(宿主 /env 快照):一键插入的更新命令用它拼 --profile */
127
- profile: string
128
89
  }) {
129
90
  /** 自定义安装:NPM 包输入与格式错误('' = 无错误) */
130
91
  const [npmQuery, setNpmQuery] = useState('')
131
92
  const [npmError, setNpmError] = useState('')
132
- /** 自定义安装:GitHub 源码输入与格式错误('' = 无错误) */
133
- const [gitQuery, setGitQuery] = useState('')
134
- const [gitError, setGitError] = useState('')
135
- /** 自定义安装:DSH 命令输入与格式错误('' = 无错误) */
136
- const [cmdQuery, setCmdQuery] = useState('')
137
- const [cmdError, setCmdError] = useState('')
138
- /** 卡片标题旁「帮助」弹窗:null = 未打开;'npm' | 'git' | 'cmd' = 打开对应卡片的格式清单 */
93
+ /** 卡片标题旁「帮助」弹窗:null = 未打开;'npm' = 打开该卡片的格式清单 */
139
94
  const [helpFor, setHelpFor] = useState<HelpTarget | null>(null)
140
95
 
141
96
  /** 通道关闭提示行:开关关掉后替代示例与输入行,引导去设置打开 */
@@ -148,9 +103,8 @@ export function CustomInstallView({ t, onInstallCustom, enableNpm, enableGit, en
148
103
  }, t('goToSettings')),
149
104
  )
150
105
 
151
- // 三个通道分开校验 —— 识别不了的输入不发请求,就地提示推荐写法。
152
- // NPM 框认裸包名或完整 npm/pnpm 命令(含全局 npm install -g,走 globalNpm 通道);
153
- // 命令框只认官方 dsh plugin 命令,提交时剥成裸目标发给服务端。
106
+ // 只做格式校验 —— 识别不了的输入不发请求,就地提示推荐写法。
107
+ // NPM 框认裸包名或完整 npm/pnpm 命令(含全局 npm install -g,走 globalNpm 通道)。
154
108
  const submitNpm = () => {
155
109
  const raw = npmQuery.trim()
156
110
  if (!raw) return
@@ -164,36 +118,12 @@ export function CustomInstallView({ t, onInstallCustom, enableNpm, enableGit, en
164
118
  setNpmQuery('')
165
119
  setNpmError('')
166
120
  }
167
- const submitGit = () => {
168
- const target = gitQuery.trim()
169
- if (!target) return
170
- if (!isGitHubInput(target)) { setGitError(t('gitInstallInvalid')); return }
171
- onInstallCustom(target, { installChannel: 'git' })
172
- setGitQuery('')
173
- setGitError('')
174
- }
175
- const submitCmd = () => {
176
- const raw = cmdQuery.trim()
177
- if (!raw) return
178
- const parsed = parseDshCommand(raw)
179
- if (parsed === null) { setCmdError(t('dshCmdInvalid', { profile })); return }
180
- // add / update 统一走安装流程:目标已安装时弹窗自动转「更新」覆盖重装(服务端 mode=update 放行),
181
- // 未安装时即为普通安装 —— 粘贴 `dsh plugin --profile web update dsh-plugin` 这类命令即可更新到最新
182
- onInstallCustom(parsed.target, { installChannel: 'dsh' })
183
- setCmdQuery('')
184
- setCmdError('')
185
- }
186
- /** 一键插入 DSH Plugin Hub 自身的更新命令:按当前 profile 拼官方命令填进输入框,用户确认后提交 */
187
- const insertHubUpdate = () => {
188
- setCmdQuery(`dsh plugin --profile ${profile} update dsh-plugin`)
189
- setCmdError('')
190
- }
191
121
 
192
122
  return h('div', { className: styles.root },
193
123
  // 页面说明:独立一级导航,交代「装完去哪看」
194
124
  h('p', { className: styles.desc }, t('customViewDesc')),
195
- // 三块卡片(一行一个):label + 文本框上方示例 + 输入行 + 错误区;
196
- // 安全信任开关关掉对应通道时,卡片禁用并显示「前往设置」提示行
125
+ // NPM 包卡片:label + 文本框上方示例 + 输入行 + 错误区;
126
+ // 安全信任开关关掉时,卡片禁用并显示「前往设置」提示行
197
127
  h('div', { className: styles.installCards },
198
128
  // NPM 包:输入包名或原生 npm/pnpm 安装命令即装(含官方全局 npm install -g)
199
129
  h('div', { className: enableNpm ? styles.installCard : `${styles.installCard} ${styles.installCardDisabled}` },
@@ -239,94 +169,6 @@ export function CustomInstallView({ t, onInstallCustom, enableNpm, enableGit, en
239
169
  npmError ? h('div', { className: styles.installError }, npmError) : null,
240
170
  ],
241
171
  ),
242
- // GitHub 源码:输入仓库地址即装
243
- h('div', { className: enableGit ? styles.installCard : `${styles.installCard} ${styles.installCardDisabled}` },
244
- h('div', { className: styles.installCardHead },
245
- h('span', { className: styles.installLabel }, t('gitInstallLabel')),
246
- h('button', {
247
- type: 'button',
248
- className: styles.installHelpBtn,
249
- title: t('installHelp'),
250
- 'aria-label': t('installHelp'),
251
- onClick: () => setHelpFor('git'),
252
- }, h(HelpIcon)),
253
- ),
254
- !enableGit
255
- ? channelOffRow
256
- : [
257
- h('div', { className: styles.installExample }, t('gitInstallExample')),
258
- h('div', { className: styles.installRow },
259
- h('input', {
260
- className: gitError ? `${styles.installInput} ${styles.installInputError}` : styles.installInput,
261
- type: 'text',
262
- placeholder: t('gitInstallPlaceholder'),
263
- value: gitQuery,
264
- spellCheck: false,
265
- onInput: (e: FormEvent<HTMLInputElement>) => {
266
- setGitQuery((e.target as HTMLInputElement).value)
267
- setGitError('')
268
- },
269
- onKeyDown: (e: KeyboardEvent) => { if (e.key === 'Enter') submitGit() },
270
- }),
271
- h('button', {
272
- className: styles.installBtn,
273
- type: 'button',
274
- disabled: gitQuery.trim() === '',
275
- onClick: submitGit,
276
- }, t('installCliBtn')),
277
- ),
278
- gitError ? h('div', { className: styles.installError }, gitError) : null,
279
- ],
280
- ),
281
- // 命令卡片:粘贴官方 dsh plugin 命令即装/即更新 —— 只认完整命令
282
- // (--profile 必填,可含 = 等号,动词 add/update,剥成裸目标走安装流程)
283
- h('div', { className: enableDsh ? styles.installCard : `${styles.installCard} ${styles.installCardDisabled}` },
284
- h('div', { className: styles.installCardHead },
285
- h('span', { className: styles.installLabel }, t('dshCmdLabel')),
286
- h('button', {
287
- type: 'button',
288
- className: styles.installHelpBtn,
289
- title: t('installHelp'),
290
- 'aria-label': t('installHelp'),
291
- onClick: () => setHelpFor('cmd'),
292
- }, h(HelpIcon)),
293
- // 一键插入 Hub 自身的更新命令:按当前 profile 拼 `dsh plugin --profile <p> update dsh-plugin`
294
- // 填入输入框(命令卡片被安全开关禁用时不显示,插入后也提交不了)
295
- enableDsh
296
- ? h('button', {
297
- type: 'button',
298
- className: styles.installInsertBtn,
299
- onClick: insertHubUpdate,
300
- }, t('dshCmdInsertHubUpdate'))
301
- : null,
302
- ),
303
- !enableDsh
304
- ? channelOffRow
305
- : [
306
- h('div', { className: styles.installExample }, t('dshCmdExample', { profile })),
307
- h('div', { className: styles.installRow },
308
- h('input', {
309
- className: cmdError ? `${styles.installInput} ${styles.installInputError}` : styles.installInput,
310
- type: 'text',
311
- placeholder: t('dshCmdPlaceholder'),
312
- value: cmdQuery,
313
- spellCheck: false,
314
- onInput: (e: FormEvent<HTMLInputElement>) => {
315
- setCmdQuery((e.target as HTMLInputElement).value)
316
- setCmdError('')
317
- },
318
- onKeyDown: (e: KeyboardEvent) => { if (e.key === 'Enter') submitCmd() },
319
- }),
320
- h('button', {
321
- className: styles.installBtn,
322
- type: 'button',
323
- disabled: cmdQuery.trim() === '',
324
- onClick: submitCmd,
325
- }, t('installCliBtn')),
326
- ),
327
- cmdError ? h('div', { className: styles.installError }, cmdError) : null,
328
- ],
329
- ),
330
172
  ),
331
173
  // 帮助弹窗:点卡片标题旁的问号打开 —— 宽窗(720px)把该通道支持的命令格式整行展示,防止折行
332
174
  helpFor !== null
@@ -346,7 +188,7 @@ export function CustomInstallView({ t, onInstallCustom, enableNpm, enableGit, en
346
188
  h('div', { className: modal.modalBody },
347
189
  // 格式清单:一行一条;`命令\t说明` 的说明跟在命令后,纯说明行以 \t 开头
348
190
  h('div', { className: styles.helpBody },
349
- t(HELP_CARDS[helpFor].contentKey, { profile }).split('\n').map((line, i) => {
191
+ t(HELP_CARDS[helpFor].contentKey).split('\n').map((line, i) => {
350
192
  const [cmd, note] = line.split('\t')
351
193
  return h('div', { key: i, className: styles.helpLine },
352
194
  cmd !== '' ? h('span', { className: styles.helpCmd }, cmd) : null,
@@ -3,12 +3,9 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * System diagnostics list: one row per connectivity channel (npm registry /
7
- * GitHub API / catalog site), each with a live status badge — 200 OK in
8
- * green, Unreachable in red, Checking… while probing. The list runs
9
- * automatically when the page opens; every row is a button that re-probes
10
- * just that channel, so a click always yields visible feedback
11
- * (row pulses → badge flips → latency refreshes).
6
+ * 系统诊断列表:每个连通性通道(npm registry / GitHub API / 目录站点)一行,各带实时
7
+ * 状态徽标 —— 200 OK 绿、不可达红、探测中显示「检测中…」。页面打开时自动跑一轮;
8
+ * 每行都是按钮,点击只重测该通道,因此点击总有可见反馈(行脉冲 → 徽标翻转 → 延迟刷新)。
12
9
  */
13
10
  import { createElement as h, useCallback, useEffect, useRef, useState } from 'react'
14
11
  import styles from '../../styles/DiagnosticsView.module.css'
@@ -3,18 +3,13 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Installed view: manages the plugins installed into the current profile.
7
- * Follows the mainstream extension-manager pattern (VSCode Installed tab) —
8
- * a searchable, sortable list of installed entries that mixes catalog
9
- * plugins (full metadata) and custom command installs (outside the catalog,
10
- * only runtime info, tagged with a custom badge). The toolbar carries a
11
- * search box plus two segmented radio-button groups: source filter
12
- * (all / catalog / custom) and sort (name / recently installed / stars / forks).
13
- * Each row carries restart (when pending), update and uninstall actions
14
- * and opens a detail modal with the full install metadata.
6
+ * 已安装视图:管理安装进当前 profile 的插件。沿用主流扩展管理器模式(VSCode 已安装 tab)——
7
+ * 可搜索、可排序的已安装清单,混排目录插件(完整元数据)与命令行自定义安装(目录之外,
8
+ * 只有运行时信息,打「自定义」徽标)。工具栏含搜索框与两组分段单选:来源筛选
9
+ * (全部/目录/自定义)与排序(名称/最近安装/Star/Fork)。每行带重启(待重启时)、更新与
10
+ * 卸载动作,并打开含完整安装元数据的详情弹窗。
15
11
  *
16
- * Pure presentational: owns only local search/sort state; the InstalledItem
17
- * model arrives from the catalog hook and actions bubble up via callbacks.
12
+ * 纯展示组件:只持有本地搜索/排序状态;InstalledItem 模型来自目录 hook,动作经回调上抛。
18
13
  */
19
14
  import { createElement as h, useMemo, useState } from 'react'
20
15
  import type { FormEvent, MouseEvent, ReactNode } from 'react'
@@ -3,10 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Market view: the browse experience for the catalog — category tabs,
7
- * search/sort/install-state controls and the plugin list. Owns nothing;
8
- * it wires catalog state (query/sort/filter/category) to the small
9
- * presentational components so PluginHubSection stays free of view internals.
6
+ * 市场视图:目录的浏览体验 —— 分类 Tab、搜索/排序/安装状态控件与插件列表。自身不持有状态,
7
+ * 只把目录状态(query/sort/filter/category)接到小型展示组件上,让 PluginHubSection 不掺视图细节。
10
8
  */
11
9
  import { createElement as h, Fragment } from 'react'
12
10
  import type { HubPlugin, LocaleId } from '../../types.ts'
@@ -3,17 +3,14 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Settings view: desktop-settings navigation — a fixed left sidebar lists
7
- * the sections (icon + label, click to switch), the right pane shows the
8
- * active section's rows. New settings only add rows, new areas only add a
9
- * sidebar entry.
6
+ * 设置视图:桌面设置式导航 —— 固定左侧栏列出各分组(图标 + 名称,点击切换),
7
+ * 右侧面板展示当前分组的设置行。新增设置只加行,新增区域只加一条侧栏项。
10
8
  *
11
- * Sections mirror what the local server actually enforces:
12
- * Updates & Sources — startup update check / npm mirror / HTTP proxy
13
- * (all wired into the install command path)
14
- * Security & Trust — restricted custom installs (server 403 gate)
15
- * Diagnostics — live connectivity probe + environment snapshot
16
- * Logs — system log viewer + storage location
9
+ * 各分组对应本地服务端实际强制的行为:
10
+ * 更新与源 —— 启动检查更新 / npm 镜像 / HTTP 代理(都接进安装命令链路)
11
+ * 安全与信任 —— 受限的自定义安装(服务端 403 门禁)
12
+ * 诊断 —— 实时连通性探测 + 环境快照
13
+ * 日志 —— 系统日志查看器 + 存放位置
17
14
  */
18
15
  import { createElement as h, useEffect, useRef, useState } from 'react'
19
16
  import type { ChangeEvent, MouseEvent, ReactNode } from 'react'
@@ -207,10 +204,8 @@ export function SettingsView({ t, settings, update, reset, env, onCopy, openSect
207
204
  )
208
205
 
209
206
  const securityCard = h('div', { className: styles.card },
210
- // 安全与信任:命令行安装的三个通道开关(NPM 包 / GitHub 源码 / DSH 命令)——
211
- // 服务端按通道门禁 403 拦截(npm 目标吃 enableNpmInstall、git 目标吃 enableGitInstall),
212
- // DSH 命令框为纯客户端通道:关掉后命令输入卡片禁用并引导去设置打开;
213
- // 目录内收录的插件安装不受影响(默认全部开启)
207
+ // 安全与信任:命令行安装的开关(仅 NPM 包)—— 服务端按通道门禁 403 拦截
208
+ //(npm 目标吃 enableNpmInstall);目录内收录的插件安装不受影响(默认开启)
214
209
  h(SettingRow, {
215
210
  title: t('settingsEnableNpm'),
216
211
  desc: t('settingsEnableNpmDesc'),
@@ -220,24 +215,6 @@ export function SettingsView({ t, settings, update, reset, env, onCopy, openSect
220
215
  title: t('settingsEnableNpm'),
221
216
  }),
222
217
  }),
223
- h(SettingRow, {
224
- title: t('settingsEnableGit'),
225
- desc: t('settingsEnableGitDesc'),
226
- children: h(Toggle, {
227
- checked: settings.enableGitInstall,
228
- onChange: (v) => update({ enableGitInstall: v }),
229
- title: t('settingsEnableGit'),
230
- }),
231
- }),
232
- h(SettingRow, {
233
- title: t('settingsEnableDsh'),
234
- desc: t('settingsEnableDshDesc'),
235
- children: h(Toggle, {
236
- checked: settings.enableDshInstall,
237
- onChange: (v) => update({ enableDshInstall: v }),
238
- title: t('settingsEnableDsh'),
239
- }),
240
- }),
241
218
  )
242
219
 
243
220
  const diagnosticsCard = h('div', { className: styles.card },
@@ -1,4 +1,4 @@
1
- /** Type declaration for CSS Modules consumed by the browser client. */
1
+ /** 浏览器客户端使用的 CSS Modules 类型声明。 */
2
2
  declare module '*.module.css' {
3
3
  const classes: Record<string, string>
4
4
  export default classes
@@ -3,9 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Catalog repository: fetches the plugin directory and the collection stats
7
- * from the online API and normalizes them. Components never see URLs or
8
- * fetch — the repository is the only place that knows where data lives.
6
+ * 目录数据仓库:从在线 API 拉取插件目录与收录统计并做归一化。
7
+ * 组件不接触 URL 与 fetch —— 这里是唯一知道数据所在地的地方。
9
8
  */
10
9
  import type { HubPlugin, LocaleId } from '../types.ts'
11
10
  import { normalize } from '../logic/normalize.ts'
@@ -3,9 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Host repository: the local routes served by the DSH harness (installed
7
- * plugin table + environment snapshot). A host without the plugin's server
8
- * routes degrades to null / empty tables instead of throwing.
6
+ * 宿主数据仓库:DSH 宿主提供的本地路由(已装插件表 + 环境快照)。
7
+ * 宿主未挂载本插件服务端路由时降级为 null / 空表,而不是抛错。
9
8
  */
10
9
  import type { EnvInfo } from '../types.ts'
11
10
  import type { InstalledVersionSignal } from '../logic/installed.ts'
@@ -45,6 +44,30 @@ export async function fetchInstalled(): Promise<HostInstalled | null> {
45
44
  }
46
45
  }
47
46
 
47
+ /** npm 包最新版本的版本号与真实发布时间(服务端经 registry 查询)。 */
48
+ export interface NpmReleaseInfo {
49
+ version: string
50
+ publishedAt: string
51
+ }
52
+
53
+ /** 批量查询已安装插件 npm 包的最新版本发布时间(供应链门禁用);失败返回 null(调用方按原逻辑照常提示)。 */
54
+ export async function fetchNpmReleaseAge(packages: string[]): Promise<Record<string, NpmReleaseInfo | null> | null> {
55
+ if (packages.length === 0) return {}
56
+ try {
57
+ const res = await fetch('/dsh-plugin-hub/npm-release-age', {
58
+ method: 'POST',
59
+ headers: { 'content-type': 'application/json' },
60
+ body: JSON.stringify({ packages }),
61
+ cache: 'no-store',
62
+ })
63
+ if (!res.ok) return null
64
+ const data = await res.json() as { packages?: Record<string, NpmReleaseInfo | null> }
65
+ return data.packages ?? null
66
+ } catch {
67
+ return null
68
+ }
69
+ }
70
+
48
71
  let envPromise: Promise<EnvInfo | null> | null = null
49
72
 
50
73
  /** 在系统文件管理器里定位并打开已安装插件的目录(服务端 spawn open,跨平台);失败返回 false。 */
@@ -3,9 +3,8 @@
3
3
  * Website: https://dsh-plugin.org
4
4
  * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
5
  *
6
- * Hub meta repository: the Cloudflare Worker endpoints behind the self-update
7
- * check and the "About us" content. Failures degrade to null — the caller
8
- * decides how to fall back (no badge, placeholder copy), never throws.
6
+ * Hub 元数据仓库:自我更新检查与「关注我们」内容背后的 Cloudflare Worker 接口。
7
+ * 失败降级为 null —— 由调用方决定如何兜底(不显示徽标、占位文案),绝不抛错。
9
8
  */
10
9
  import type { HubAboutInfo, HubUpdateInfo } from '../types.ts'
11
10
  import { HUB_ABOUT_URL, HUB_UPDATE_URL } from '../logic/constants.ts'