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.
- package/README.en.md +13 -13
- package/README.md +13 -13
- package/client/client.js +791 -761
- package/client/client.js.map +1 -1
- package/lib/http/catalog.js +2 -3
- package/lib/http/diagnostics.js +6 -7
- package/lib/http/install.js +47 -213
- package/lib/http/logs.js +1 -2
- package/lib/http/npm-release-age.js +42 -0
- package/lib/http/routes.js +8 -7
- package/lib/http/settings.js +2 -2
- package/lib/http/shared.js +2 -2
- package/lib/http/task.js +1 -2
- package/lib/index.js +5 -6
- package/lib/services/install/install-types.js +4 -4
- package/lib/services/install/install.js +1 -1
- package/lib/services/install/npm-check.js +3 -2
- package/lib/services/install/npm-resolve.js +9 -96
- package/lib/services/install/operation-log.js +53 -1
- package/lib/services/install/package-entry.js +6 -11
- package/lib/services/install/registry-check.js +2 -2
- package/lib/services/install/release-target.js +8 -9
- package/lib/services/install/task-queue.js +44 -48
- package/lib/services/npm-release-age.js +119 -0
- package/lib/services/probe.js +4 -4
- package/lib/services/profile/installed-versions.js +15 -21
- package/lib/services/profile/profile.js +16 -44
- package/lib/services/profile/progress.js +8 -11
- package/lib/services/settings.js +2 -5
- package/lib/types/http/catalog.d.ts +1 -1
- package/lib/types/http/install.d.ts +2 -2
- package/lib/types/http/npm-release-age.d.ts +2 -0
- package/lib/types/http/routes.d.ts +10 -12
- package/lib/types/http/shared.d.ts +5 -6
- package/lib/types/index.d.ts +7 -8
- package/lib/types/services/install/install-types.d.ts +12 -13
- package/lib/types/services/install/install.d.ts +1 -1
- package/lib/types/services/install/npm-resolve.d.ts +4 -16
- package/lib/types/services/install/operation-log.d.ts +9 -0
- package/lib/types/services/install/package-entry.d.ts +6 -11
- package/lib/types/services/install/release-target.d.ts +7 -8
- package/lib/types/services/install/task-queue.d.ts +25 -30
- package/lib/types/services/npm-release-age.d.ts +15 -0
- package/lib/types/services/probe.d.ts +4 -4
- package/lib/types/services/profile/installed-versions.d.ts +5 -7
- package/lib/types/services/profile/profile.d.ts +14 -18
- package/lib/types/services/profile/progress.d.ts +7 -10
- package/lib/types/services/settings.d.ts +0 -4
- package/package.json +1 -1
- package/src/client/components/PluginHubSection.tsx +26 -22
- package/src/client/components/catalog/CatalogList.tsx +2 -4
- package/src/client/components/catalog/PluginCard.tsx +2 -3
- package/src/client/components/layout/CatalogControls.tsx +5 -6
- package/src/client/components/layout/CatalogHeader.tsx +1 -2
- package/src/client/components/layout/CategoryTabs.tsx +2 -3
- package/src/client/components/layout/SectionTabs.tsx +3 -5
- package/src/client/components/modals/ConfirmDialog.tsx +4 -6
- package/src/client/components/modals/InstalledDetailModal.tsx +3 -6
- package/src/client/components/modals/LogsModal.tsx +7 -12
- package/src/client/components/modals/NotificationsModal.tsx +74 -24
- package/src/client/components/modals/ProgressView.tsx +3 -4
- package/src/client/components/modals/modals.tsx +75 -26
- package/src/client/components/ui/Dropdown.tsx +4 -6
- package/src/client/components/ui/Toggle.tsx +2 -3
- package/src/client/components/ui/icons.tsx +1 -2
- package/src/client/components/views/CustomInstallView.tsx +20 -178
- package/src/client/components/views/DiagnosticsView.tsx +3 -6
- package/src/client/components/views/InstalledView.tsx +6 -11
- package/src/client/components/views/MarketView.tsx +2 -4
- package/src/client/components/views/SettingsView.tsx +9 -32
- package/src/client/css-modules.d.ts +1 -1
- package/src/client/data/catalog.ts +2 -3
- package/src/client/data/host.ts +26 -3
- package/src/client/data/hub.ts +2 -3
- package/src/client/hooks/useCatalog.ts +79 -24
- package/src/client/hooks/useIncrementalList.ts +3 -5
- package/src/client/hooks/useLanguage.ts +4 -6
- package/src/client/hooks/useSettings.ts +4 -11
- package/src/client/hooks/useTaskQueue.ts +36 -42
- package/src/client/index.tsx +2 -3
- package/src/client/locales.ts +187 -114
- package/src/client/logic/constants.ts +1 -2
- package/src/client/logic/ensure-plugin-css.ts +8 -12
- package/src/client/logic/failures.ts +20 -19
- package/src/client/logic/format.ts +1 -1
- package/src/client/logic/install-command.ts +14 -35
- package/src/client/logic/installed.ts +15 -10
- package/src/client/logic/normalize.ts +3 -3
- package/src/client/logic/release-age.ts +19 -0
- package/src/client/logic/urls.ts +24 -21
- package/src/client/styles/CustomInstallView.module.css +3 -30
- package/src/client/styles/DiagnosticsView.module.css +3 -5
- package/src/client/styles/Dropdown.module.css +2 -4
- package/src/client/styles/Header.module.css +6 -7
- package/src/client/styles/InstalledView.module.css +4 -7
- package/src/client/styles/List.module.css +4 -5
- package/src/client/styles/LogsView.module.css +2 -3
- package/src/client/styles/Modal.module.css +46 -6
- package/src/client/styles/SectionTabs.module.css +2 -4
- package/src/client/styles/SettingsView.module.css +3 -5
- package/src/client/styles/Toggle.module.css +2 -4
- package/src/client/styles/tokens.module.css +14 -18
- package/src/client/types.ts +11 -11
- package/src/server/http/catalog.ts +2 -3
- package/src/server/http/diagnostics.ts +6 -7
- package/src/server/http/install.ts +47 -208
- package/src/server/http/logs.ts +1 -2
- package/src/server/http/npm-release-age.ts +42 -0
- package/src/server/http/routes.ts +12 -12
- package/src/server/http/settings.ts +2 -2
- package/src/server/http/shared.ts +5 -6
- package/src/server/http/task.ts +1 -2
- package/src/server/index.ts +7 -8
- package/src/server/services/install/install-types.ts +12 -13
- package/src/server/services/install/install.ts +0 -1
- package/src/server/services/install/npm-check.ts +3 -2
- package/src/server/services/install/npm-resolve.ts +9 -99
- package/src/server/services/install/operation-log.ts +47 -1
- package/src/server/services/install/package-entry.ts +6 -11
- package/src/server/services/install/registry-check.ts +2 -2
- package/src/server/services/install/release-target.ts +8 -9
- package/src/server/services/install/task-queue.ts +51 -54
- package/src/server/services/npm-release-age.ts +107 -0
- package/src/server/services/probe.ts +4 -4
- package/src/server/services/profile/installed-versions.ts +16 -22
- package/src/server/services/profile/profile.ts +16 -42
- package/src/server/services/profile/progress.ts +8 -11
- package/src/server/services/settings.ts +2 -9
- package/lib/services/install/preflight.js +0 -178
- package/lib/types/services/install/preflight.d.ts +0 -23
- 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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
*
|
|
16
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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,
|
|
79
|
+
export function CustomInstallView({ t, onInstallCustom, enableNpm, onOpenSettings }: {
|
|
114
80
|
t: Translate
|
|
115
|
-
/** 命令行安装:输入 npm 包名 / npm、pnpm
|
|
116
|
-
*
|
|
117
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
//
|
|
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
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* active section's rows. New settings only add rows, new areas only add a
|
|
9
|
-
* sidebar entry.
|
|
6
|
+
* 设置视图:桌面设置式导航 —— 固定左侧栏列出各分组(图标 + 名称,点击切换),
|
|
7
|
+
* 右侧面板展示当前分组的设置行。新增设置只加行,新增区域只加一条侧栏项。
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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
|
-
//
|
|
211
|
-
|
|
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 },
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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'
|
package/src/client/data/host.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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。 */
|
package/src/client/data/hub.ts
CHANGED
|
@@ -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
|
|
7
|
-
*
|
|
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'
|