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.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/client/client.js +705 -803
- 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/package-entry.js +6 -11
- package/lib/services/install/registry-check.js +5 -16
- package/lib/services/install/release-target.js +8 -9
- package/lib/services/install/task-queue.js +74 -57
- 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/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 +32 -31
- 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 +23 -27
- 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 +52 -24
- package/src/client/components/modals/ProgressView.tsx +3 -4
- package/src/client/components/modals/modals.tsx +58 -30
- 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 +96 -123
- package/src/client/logic/constants.ts +1 -2
- package/src/client/logic/ensure-plugin-css.ts +8 -12
- package/src/client/logic/failures.ts +41 -28
- 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 +55 -50
- 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 +4 -5
- 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/package-entry.ts +6 -11
- package/src/server/services/install/registry-check.ts +5 -16
- package/src/server/services/install/release-target.ts +8 -9
- package/src/server/services/install/task-queue.ts +79 -63
- 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,13 +3,10 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* install/remove notifications.
|
|
6
|
+
* 失败归类、Issue 正文摘要与持久化的安装/卸载通知。
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* notification center — so a result is never lost even when the dialog
|
|
12
|
-
* was dismissed or nobody was watching the progress strip.
|
|
8
|
+
* 每个结算后的任务都会双重留痕:服务端系统日志(hub.log)与馈送通知中心的客户端
|
|
9
|
+
* localStorage 记录 —— 即使弹窗被关闭、无人盯着进度条,结果也不会丢失。
|
|
13
10
|
*/
|
|
14
11
|
export interface NotificationRecord {
|
|
15
12
|
id: number
|
|
@@ -27,7 +24,7 @@ export interface NotificationRecord {
|
|
|
27
24
|
version?: string
|
|
28
25
|
/** 实际执行的安装/卸载命令(issue 预填时如实展示);历史记录可能缺失 */
|
|
29
26
|
command?: string
|
|
30
|
-
/**
|
|
27
|
+
/** 尝试过的安装方式(实际执行过的命令,按先后顺序):失败提 Issue 时贴给作者,便于反推正确的 npm 包名 */
|
|
31
28
|
attempts?: string[]
|
|
32
29
|
/** 结束时间(epoch ms) */
|
|
33
30
|
at: number
|
|
@@ -151,9 +148,28 @@ export function removeNotification(id: number): NotificationRecord[] {
|
|
|
151
148
|
return next
|
|
152
149
|
}
|
|
153
150
|
|
|
154
|
-
export type FailureKind = 'npmTooOld' | 'dshMissing' | 'gitMissing' | 'pnpmMissing' | 'npmMissing' | 'pnpmStore' | 'pnpmWorkspace' | 'originRejected' | 'pnpmPolicy' | 'pnpmUnusedPatch' | 'pnpmMissingDep' | 'pluginMissingDep' | 'registryVersionStale' | 'pnpmLinkedDirMissing' | 'fileLocked' | 'accessDenied' | 'fsUnavailable' | 'pnpmIgnoredBuild' | 'pluginPrepare' | 'network' | 'hubState' | 'timeout' | 'profileLock' | 'hub'
|
|
151
|
+
export type FailureKind = 'npmTooOld' | 'dshMissing' | 'gitMissing' | 'pnpmMissing' | 'npmMissing' | 'pnpmStore' | 'pnpmWorkspace' | 'originRejected' | 'pnpmPolicy' | 'pnpmUnusedPatch' | 'pnpmMissingDep' | 'pluginMissingDep' | 'registryVersionStale' | 'pnpmLinkedDirMissing' | 'fileLocked' | 'accessDenied' | 'fsUnavailable' | 'pnpmIgnoredBuild' | 'pluginPrepare' | 'network' | 'hostCliSpawn' | 'hubState' | 'timeout' | 'profileLock' | 'hub'
|
|
155
152
|
|
|
156
|
-
/**
|
|
153
|
+
/** 失败动作口径:标题、上报正文与提示文案按安装/更新/卸载取词,不写死「安装」。 */
|
|
154
|
+
export type FailureAction = 'install' | 'update' | 'uninstall'
|
|
155
|
+
|
|
156
|
+
/** 动作词(英文):名词短语,标题与句子里都成立。 */
|
|
157
|
+
export function actionWordEn(action: FailureAction): string {
|
|
158
|
+
return action === 'uninstall' ? 'removal' : action === 'update' ? 'update' : 'install'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** 动作词(中文):安装 / 更新 / 卸载。 */
|
|
162
|
+
export function actionWordZh(action: FailureAction): string {
|
|
163
|
+
return action === 'uninstall' ? '卸载' : action === 'update' ? '更新' : '安装'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** 记录的动作:老记录只有 kind,按 kind 兜底。 */
|
|
167
|
+
export function actionOfRecord(rec: { kind: NotificationRecord['kind']; action?: FailureAction }): FailureAction {
|
|
168
|
+
if (rec.action !== undefined) return rec.action
|
|
169
|
+
return rec.kind === 'uninstall' ? 'uninstall' : rec.kind === 'update' ? 'update' : 'install'
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** 只匹配 pnpm 的错误码,不匹配泛泛出现的 checksum / integrity 字样。 */
|
|
157
173
|
function missingTarballIntegrity(message: string): boolean {
|
|
158
174
|
return /\bERR_PNPM_MISSING_TARBALL_INTEGRITY\b/.test(message)
|
|
159
175
|
}
|
|
@@ -168,9 +184,9 @@ export function releaseAgeRetryAfter(message: string): string | null {
|
|
|
168
184
|
}
|
|
169
185
|
|
|
170
186
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
187
|
+
* 同一策略子类型也用于选择实时/已保存失败的提示文案:minimumReleaseAge 只有在能算出
|
|
188
|
+
* 可重试时刻时才走专属文案(给出确切的「何时可再试」),算不出时退回通用文案,
|
|
189
|
+
* 避免出现「预计可重试时间:」后接空值的残缺句子。
|
|
174
190
|
*/
|
|
175
191
|
export function pnpmPolicyHintOf(message: string): 'failPnpmTarballIntegrityHint' | 'failPnpmReleaseAgeHint' | 'failPnpmPolicyHint' {
|
|
176
192
|
if (missingTarballIntegrity(message)) return 'failPnpmTarballIntegrityHint'
|
|
@@ -226,14 +242,12 @@ export function classifyFailure(message: string): FailureKind {
|
|
|
226
242
|
// 构建脚本被 allowBuilds 拦截。必须在 network 之前判:日志尾部混重试残留的连接失败特征
|
|
227
243
|
if (/ERR_PNPM_IGNORED_BUILDS|Ignored build scripts:|ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED/i.test(message)) return 'pnpmIgnoredBuild'
|
|
228
244
|
// 网络/代理/源配置问题。必须在 prepare 之前判:git fetch 失败常被外层包成 "Command failed"
|
|
229
|
-
|
|
230
|
-
|
|
245
|
+
// tarball 下载失败的括号里可能是数字码(pnpm 自己的)或符号码(undici 的 UND_ERR_*),两种都要认
|
|
246
|
+
if (/\[network\]|ERR_PNPM_GIT_FETCH_FAILED|ETIMEDOUT|ECONNREFUSED|ECONNRESET|ENOTFOUND|EAI_AGAIN|EPIPE|EHOSTUNREACH|ENETUNREACH|UND_ERR_[A-Z0-9_]+|getaddrinfo|Could not connect|Could not resolve host|Network unreachable|Failed to connect|socket hang up|CERT_HAS_EXPIRED|SSL certificate problem|\bTLS\b|\bSSL\b|fetch failed|failed to fetch|\bGET https?:\/\/\S+\.tgz\s+error \([A-Za-z0-9_]+\)/i.test(message)) return 'network'
|
|
247
|
+
// 装后校验发现入口文件缺失([packaging])= 分发物缺构建产物
|
|
231
248
|
if (/\[packaging\]|entry file missing/i.test(message)) return 'pluginPrepare'
|
|
232
249
|
// registry 上不存在的依赖(404),profile 任何安装都会卡在这条。必须在 pluginPrepare 之前判
|
|
233
|
-
//
|
|
234
|
-
// 该包来自插件的依赖树,属**作者侧**问题(依赖没发布/已被删除)。标记由服务端联网复查后注入。
|
|
235
|
-
// 必须排在 pnpmMissingDep 之前 —— 日志里同样含 ERR_PNPM_FETCH_404,靠标记区分「作者的锅」
|
|
236
|
-
// 与「本机 profile 残留依赖(#73)/ 镜像源滞后」。
|
|
250
|
+
// [registry-missing](服务端官方源复核后注入)= 作者侧缺依赖,须先于 pnpmMissingDep(同样含 404 文本)
|
|
237
251
|
if (/\[registry-missing\]/i.test(message)) return 'pluginMissingDep'
|
|
238
252
|
if (/ERR_PNPM_FETCH_404|404 Not Found|Not Found - 404/i.test(message)) return 'pnpmMissingDep'
|
|
239
253
|
// 要装的版本在实际安装源里没有(镜像滞后或版本写错)。必须在 pluginPrepare 之前判
|
|
@@ -246,7 +260,7 @@ export function classifyFailure(message: string): FailureKind {
|
|
|
246
260
|
// 安装器自身产生的状态类消息(未安装 / 不在此处卸载 / 已在队列中 / 已安装):这几句由本服务的
|
|
247
261
|
// 守卫发出,不是 CLI 输出、也不是插件问题,而是插件中心的已安装列表与实读快照不同步。
|
|
248
262
|
// 单独归类,让前端给「刷新后重试」这类可行动提示,而不是当作未知失败引导去提 Issue。
|
|
249
|
-
// already installed
|
|
263
|
+
// already installed 只认「整行就是它」的短形态;带「——」说明的
|
|
250
264
|
// 包名冲突另有处置,不能按状态不同步提示。
|
|
251
265
|
if (/plugin is not installed|plugin cannot be uninstalled here|already queued:|^already installed: \S+$/im.test(message)) return 'hubState'
|
|
252
266
|
// 安装器自己的超时标记(命令跑满上限被强杀):既不是插件问题、也不是用户环境问题,而且很可能是
|
|
@@ -257,12 +271,11 @@ export function classifyFailure(message: string): FailureKind {
|
|
|
257
271
|
// dsh 进程或安装任务在写,锁一直没释放。既不是插件问题、也不是用户环境坏了 —— 把并发收掉重试即可。
|
|
258
272
|
// 摆放位置同样在一切错误码分支之后:日志里真有 pnpm 报错时仍以真实报错为准。
|
|
259
273
|
if (/atomic-write: timed out waiting for the writer lock|timed out waiting for the writer lock/i.test(message)) return 'profileLock'
|
|
260
|
-
//
|
|
261
|
-
// 终端里只留这一句「git-hosted plugins build … add the exact key … then re-run」,日志里因此
|
|
262
|
-
// 一条原始 ERR_PNPM_* 码都没有 —— 只能靠这句话识别为「构建脚本被白名单拦截」。
|
|
263
|
-
// 摆放位置在一切错误码分支之后:日志里真有 ERR_PNPM_PREPARE_PACKAGE / ERR_PNPM_IGNORED_BUILDS
|
|
264
|
-
// 时(构建确实跑挂、或不带摘要的旧宿主),仍以原始报错为准,不会被这句摘要改写。
|
|
274
|
+
// 宿主自己的 allowBuilds 摘要行(pnpm 原文只写进 diagnostics 日志文件,终端只剩这一句)
|
|
265
275
|
if (/git-hosted plugins build on install via their prepare script|add the exact key pnpm printed above under allowBuilds/i.test(message)) return 'pnpmIgnoredBuild'
|
|
276
|
+
// 宿主自身入口起不来(服务端 [host-spawn] 标记):环境问题,给提示、不引导提 Issue。
|
|
277
|
+
// 不匹配 `spawn … ENOENT` 文本 —— 那个形态任何 spawn(curl/git/taskkill…)都会产出
|
|
278
|
+
if (/\[host-spawn\]/i.test(message)) return 'hostCliSpawn'
|
|
266
279
|
// 其余未知失败:引导到插件中心仓库排查
|
|
267
280
|
return 'hub'
|
|
268
281
|
}
|
|
@@ -330,8 +343,8 @@ export function coreErrorCode(message: string): string | null {
|
|
|
330
343
|
}
|
|
331
344
|
|
|
332
345
|
/** 「包在 registry 不存在」失败的提示变体:服务端用官方源复核过(会注入 `[registry-mirror-lag]`)
|
|
333
|
-
* 时,说明包在官方源上存在、是本机镜像源未同步 →
|
|
334
|
-
*
|
|
346
|
+
* 时,说明包在官方源上存在、是本机镜像源未同步 → 提示换源;否则按「profile 残留依赖」提示
|
|
347
|
+
* (删掉 profile 里那条依赖即可)。 */
|
|
335
348
|
export function registryMissingHintOf(message: string): 'failPnpmMissingDepHint' | 'failRegistryMirrorLagHint' {
|
|
336
349
|
return /\[registry-mirror-lag\]/i.test(message) ? 'failRegistryMirrorLagHint' : 'failPnpmMissingDepHint'
|
|
337
350
|
}
|
|
@@ -350,12 +363,12 @@ export function missingRegistryPackageOf(message: string): string | null {
|
|
|
350
363
|
|
|
351
364
|
/**
|
|
352
365
|
* 从网络类失败消息里提取「具体连不上的地址」,供弹窗精准提示(你的网络无法访问什么)。
|
|
353
|
-
* 优先取服务端 [network]
|
|
366
|
+
* 优先取服务端 [network] 网络探测消息括号里的探测地址(中文「(…)」/ 英文「(…)」),
|
|
354
367
|
* 否则取消息里出现的第一个 URL(git fetch 的仓库地址、registry 域名等)。
|
|
355
368
|
* 提取不到返回 null —— 调用方据此跳过地址行。
|
|
356
369
|
*/
|
|
357
370
|
export function unreachableTargetOf(message: string): string | null {
|
|
358
|
-
// [network]
|
|
371
|
+
// [network] 网络探测消息:`无法连接到 GitHub(https://github.com/)` / `cannot reach the npm registry (https://registry.npmjs.org/)`
|
|
359
372
|
const paren = message.match(/[((](https?:\/\/[^\s'"`<>()+]+)[))]/)
|
|
360
373
|
if (paren) return paren[1]
|
|
361
374
|
// 其余网络失败(git fetch / npm 连接失败):取第一个 URL,去掉行尾标点
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* vs prebuilt GitHub release vs explicit-HTTPS GitHub), build the display
|
|
8
|
-
* command and normalize a raw install spec back to its owner/repo identity.
|
|
6
|
+
* 安装目标/命令工具:为目录条目取 npm 包名、构造展示命令,并把原始安装 spec 归一化回 owner/repo 身份。
|
|
9
7
|
*/
|
|
10
8
|
import type { HubPlugin } from '../types.ts'
|
|
11
9
|
import { githubReleaseTarget } from '../../server/services/install/release-target.ts'
|
|
12
10
|
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
11
|
+
/** 后端 attempts 按时间先后排列:取最后一次实际执行的命令,取不到再回退到展示用命令。
|
|
12
|
+
* 这里匹配的是任务队列记录的指令形态,不是要在此执行的命令。
|
|
15
13
|
*/
|
|
16
14
|
export function executedCommandOf(command?: string, attempts?: string[]): string | undefined {
|
|
17
15
|
if (attempts === undefined) return command
|
|
@@ -21,44 +19,25 @@ export function executedCommandOf(command?: string, attempts?: string[]): string
|
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
|
-
*
|
|
25
|
-
* (走 npm registry tarball
|
|
26
|
-
*
|
|
27
|
-
* 都没有 → git 直装。
|
|
28
|
-
* 返回值 target 即传给后端 /install 的安装目标(npm 包名 / release URL / owner/repo)。
|
|
22
|
+
* 目录条目的安装目标(用户无感知):目录条目必带 npm 包名,直接用该包名安装
|
|
23
|
+
* (走 npm registry tarball)。
|
|
24
|
+
* 返回值即传给后端 /install 的安装目标(npm 包名)。
|
|
29
25
|
*/
|
|
30
|
-
export function installTargetOf(p: HubPlugin):
|
|
31
|
-
|
|
32
|
-
const repo = (p.source?.repo ?? '').trim()
|
|
33
|
-
if (pkg && repo) return { target: pkg, via: 'npm' }
|
|
34
|
-
// 目录下发的权威命令若是「同仓库」的固定 release 直链,改走该预构建包;
|
|
35
|
-
// 跨仓 / 非法 release 目标不采信,退回 git 直装。
|
|
36
|
-
const release = githubReleaseTarget(p.install?.githubCommand ?? '')
|
|
37
|
-
if (release !== null && repo !== '' && release.repo.toLowerCase() === repo.toLowerCase()) {
|
|
38
|
-
return { target: release.target, via: 'release' }
|
|
39
|
-
}
|
|
40
|
-
return { target: repo, via: 'github' }
|
|
26
|
+
export function installTargetOf(p: HubPlugin): string {
|
|
27
|
+
return (p.source?.npmPackage ?? '').trim()
|
|
41
28
|
}
|
|
42
29
|
|
|
43
30
|
/** 展示用安装命令(复制/弹窗):目录下发的权威命令优先(CLI-only 插件如 dsh-tui
|
|
44
|
-
* 需专属 profile(--profile dsh-tui),无法从
|
|
45
|
-
*
|
|
31
|
+
* 需专属 profile(--profile dsh-tui),无法从 npm 包名推断,必须用官方命令);
|
|
32
|
+
* 常规插件无目录命令时用 npm 包名回退生成。 */
|
|
46
33
|
export function installCommandOf(p: HubPlugin, withProfile = false): string {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
const cmd = p.install?.command
|
|
50
|
-
if (cmd) return cmd
|
|
51
|
-
} else {
|
|
52
|
-
// release 与 git 通道共用目录下发的 githubCommand(git+ 显式 HTTPS 或 release 直链)
|
|
53
|
-
const cmd = p.install?.githubCommand
|
|
54
|
-
if (cmd) return cmd
|
|
55
|
-
}
|
|
34
|
+
const cmd = p.install?.command
|
|
35
|
+
if (cmd) return cmd
|
|
56
36
|
const prefix = `dsh plugin${withProfile ? ' --profile web' : ''} add `
|
|
57
|
-
|
|
58
|
-
return `${prefix}git+https://github.com/${target}.git`
|
|
37
|
+
return `${prefix}${installTargetOf(p)}`
|
|
59
38
|
}
|
|
60
39
|
|
|
61
|
-
/**
|
|
40
|
+
/** 把任务/安装目标归一化为 owner/repo 展示身份。 */
|
|
62
41
|
export function repoFromInstallTarget(value: string): string {
|
|
63
42
|
const input = value.trim()
|
|
64
43
|
// release 直链(预构建 .tgz)走同一条身份归一化:解析出所属仓库,已装态/更新信号才能
|
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* 已安装插件模型(纯逻辑,不依赖 React/UI)。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* install matching / update detection used by the market view, so both
|
|
12
|
-
* views share one source of truth.
|
|
8
|
+
* 把宿主 `/installed` 路由下发的本地已装表(npm 包名 → manifest spec)与目录投影合并成
|
|
9
|
+
* 统一的 `InstalledItem` 视图模型,供「已安装」视图消费;同时承载市场视图用的安装匹配
|
|
10
|
+
* 与更新检测,让两个视图共享唯一数据源。
|
|
13
11
|
*/
|
|
14
12
|
import type { HubPlugin } from '../types.ts'
|
|
15
13
|
import { HUB_REPO } from './constants.ts'
|
|
@@ -56,7 +54,7 @@ function signalOf(repo: string | null, versions: Record<string, InstalledVersion
|
|
|
56
54
|
return versions[repo.toLowerCase()] ?? null
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
/** 插件是否已安装:匹配 Git spec 中的 owner/repo,或 npm
|
|
57
|
+
/** 插件是否已安装:匹配 Git spec 中的 owner/repo,或 npm 安装的依赖包名;命中返回 npm 包名。 */
|
|
60
58
|
export function installedNameOf(
|
|
61
59
|
plugin: HubPlugin,
|
|
62
60
|
installed: Record<string, string>,
|
|
@@ -68,8 +66,8 @@ export function installedNameOf(
|
|
|
68
66
|
for (const [name, spec] of Object.entries(installed)) {
|
|
69
67
|
if (repoFromInstallTarget(spec).toLowerCase() === needle) return name
|
|
70
68
|
}
|
|
71
|
-
// npm
|
|
72
|
-
//
|
|
69
|
+
// npm 安装:profile 依赖 key 直接是 npm 包名。包名来源两处——目录数据(npmPackage),
|
|
70
|
+
// 或服务端安装时持久化的映射(versions[repo].npmPackage,覆盖组织 scope 与 GitHub
|
|
73
71
|
// 用户名不一致、目录未下发包名的场景);命中任一并依赖 key 真实存在即视为已安装。
|
|
74
72
|
// 只认 npm 安装的依赖(spec 无法解析出仓库身份):git/本地安装的依赖身份由仓库匹配决定,
|
|
75
73
|
// 包名同名不代表同仓库——否则「git 装 A 仓库」会被「npm 包名同名但仓库不同」的目录条目
|
|
@@ -91,6 +89,13 @@ export function installedVersionOf(plugin: HubPlugin, versions: Record<string, I
|
|
|
91
89
|
return versions[repo.toLowerCase()]?.version ?? null
|
|
92
90
|
}
|
|
93
91
|
|
|
92
|
+
/** 该插件对应的 npm 包名(目录下发,或安装时持久化的映射);无则 null。决定更新是否走 npm registry。 */
|
|
93
|
+
export function npmPackageOf(plugin: HubPlugin, versions: Record<string, InstalledVersionSignal>): string | null {
|
|
94
|
+
const repo = plugin.source?.repo
|
|
95
|
+
const pkg = (plugin.source?.npmPackage || (repo ? versions[repo.toLowerCase()]?.npmPackage : undefined) || '').trim()
|
|
96
|
+
return pkg === '' ? null : pkg
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
/**
|
|
95
100
|
* 是否有更新:仅对已安装插件有意义。
|
|
96
101
|
* 双信号判定——有 release 版本的比版本;无版本(repo 不打 tag)的比仓库最近更新时间
|
|
@@ -174,7 +179,7 @@ function isHubSelf(name: string, spec: string): boolean {
|
|
|
174
179
|
if (spec.startsWith('file:') || spec.startsWith('link:')) return true
|
|
175
180
|
const repo = repoFromInstallTarget(spec)
|
|
176
181
|
if (isRepoLike(repo) && repo.toLowerCase() === HUB_REPO.toLowerCase()) return true
|
|
177
|
-
// npm
|
|
182
|
+
// npm 覆盖重装 hub 自身后,file:/link: 本地链接会被替换成 registry 版本号/范围
|
|
178
183
|
// (如 1.3.0、^1.3.0)——依赖 key `dsh-plugin` 在 npm 上唯一属于本插件(不存在第三方
|
|
179
184
|
// 同名包),版本号形态与仓库身份同样视为 hub 自身:命令窗口「自己装自己」成功后,
|
|
180
185
|
// 条目仍不进已安装列表(卸载接口对它的 400 拒绝是服务端最后一道防线)。
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* 归一化器:把在线 API 的短 key 载荷(s/o/n/vr/c/t/f/d/r/v/u/a/sg/fk)映射为
|
|
7
|
+
* HubPlugin,让渲染层只面对一种数据形态。
|
|
8
8
|
*/
|
|
9
9
|
import type { HubPlugin } from '../types.ts'
|
|
10
10
|
|
|
@@ -30,7 +30,7 @@ export function normalize(raw: Record<string, unknown>): HubPlugin {
|
|
|
30
30
|
: undefined,
|
|
31
31
|
// 安装信息:wi 仅在 false 时下发(缺省视为 true 可一键安装);
|
|
32
32
|
// mi 仅在 true 时下发(人工干预:网页端替不了终端,只引导用户自己去终端装);
|
|
33
|
-
// ic/igc = 目录下发的权威安装命令(
|
|
33
|
+
// ic/igc = 目录下发的权威安装命令(CLI-only 插件如 dsh-tui 必须用)
|
|
34
34
|
install: raw.wi === false || raw.mi === true || typeof raw.ic === 'string' || typeof raw.igc === 'string'
|
|
35
35
|
? {
|
|
36
36
|
webInstallable: raw.wi === false ? false : undefined,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
6
|
+
* 供应链门禁的时间判定(纯函数):pnpm 会静默回退发布未满门槛的版本,
|
|
7
|
+
* 故发布太新的版本不该提示更新。Hub 自身更新与插件更新共用同一门槛。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** pnpm 供应链门禁:dsh profile 默认 minimumReleaseAge=1440(分钟);+30 分钟缓冲抵消网络/CDN 边缘时间偏差。 */
|
|
11
|
+
export const MIN_RELEASE_AGE_MS = 24 * 60 * 60 * 1000 + 30 * 60 * 1000
|
|
12
|
+
|
|
13
|
+
/** 发布时间是否仍在门禁窗口内(太新 → 暂缓提示);无值/解析不出返回 false(不拦真实更新)。 */
|
|
14
|
+
export function isReleaseTooFresh(publishedAt: string | null | undefined, now: number): boolean {
|
|
15
|
+
if (typeof publishedAt !== 'string' || publishedAt === '') return false
|
|
16
|
+
const ms = Date.parse(publishedAt)
|
|
17
|
+
if (Number.isNaN(ms)) return false
|
|
18
|
+
return now - ms < MIN_RELEASE_AGE_MS
|
|
19
|
+
}
|
package/src/client/logic/urls.ts
CHANGED
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
* URL
|
|
7
|
-
*
|
|
8
|
-
* URL stays within GitHub's 8192-byte request limit.
|
|
6
|
+
* URL 构造:目录详情页、插件官网链接,以及失败弹窗用的预填 GitHub Issue 链接。
|
|
7
|
+
* 均为纯函数,保证 issue URL 不超过 GitHub 8192 字节的请求上限。
|
|
9
8
|
*/
|
|
10
9
|
import type { EnvInfo, HubPlugin, LocaleId } from '../types.ts'
|
|
11
10
|
import { GITHUB_URL, HUB_REPO, PLUGIN_VERSION, SITE_URL } from './constants.ts'
|
|
12
|
-
import { MAX_CORE_CHARS, classifyFailure, coreErrorCode, pnpmPolicyHintOf, summarizeError } from './failures.ts'
|
|
11
|
+
import { MAX_CORE_CHARS, actionWordEn, actionWordZh, classifyFailure, coreErrorCode, pnpmPolicyHintOf, summarizeError } from './failures.ts'
|
|
13
12
|
import { executedCommandOf } from './install-command.ts'
|
|
14
|
-
import type { FailureKind } from './failures.ts'
|
|
13
|
+
import type { FailureAction, FailureKind } from './failures.ts'
|
|
15
14
|
|
|
16
15
|
/** dsh-plugin.org 中文页挂在 /zh/ 前缀下,英文在根路径。 */
|
|
17
16
|
export function langPathOf(lang: LocaleId): string {
|
|
@@ -41,18 +40,18 @@ export function pluginSiteUrl(repo: string): string {
|
|
|
41
40
|
return siteUrl(`plugins/${repo}`)
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
/**
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
/** 错误原因标题(对外英文):点明问题出在哪一侧,便于 issue 列表分流;
|
|
44
|
+
* 动作级原因(未分类/超时…)带动作词,卸载失败不写成 install failed。 */
|
|
45
|
+
function reasonTitleOf(kind: FailureKind, action: FailureAction): string {
|
|
46
|
+
const word = actionWordEn(action)
|
|
48
47
|
switch (kind) {
|
|
49
|
-
case 'npmTooOld': return
|
|
48
|
+
case 'npmTooOld': return `npm too old for this ${word}`
|
|
50
49
|
case 'dshMissing': return 'dsh command not found on the user machine'
|
|
51
50
|
case 'pnpmMissing': return 'pnpm command not found on the user machine'
|
|
52
51
|
case 'pnpmStore': return 'pnpm store version mismatch on the user machine'
|
|
53
|
-
case 'pnpmWorkspace': return
|
|
52
|
+
case 'pnpmWorkspace': return `pnpm workspace-root check blocked the ${word} on the user machine`
|
|
54
53
|
case 'originRejected': return 'request origin rejected by the local service on the user machine'
|
|
55
|
-
case 'pnpmPolicy': return
|
|
54
|
+
case 'pnpmPolicy': return `pnpm supply-chain policy blocked the ${word} on the user machine`
|
|
56
55
|
case 'pnpmUnusedPatch': return 'stale pnpm patch entry on the user machine'
|
|
57
56
|
case 'pnpmMissingDep': return 'dependency not found on the npm registry on the user machine'
|
|
58
57
|
case 'registryVersionStale': return 'requested version not available on the npm registry in use on the user machine'
|
|
@@ -63,12 +62,13 @@ function reasonTitleOf(kind: FailureKind): string {
|
|
|
63
62
|
case 'pnpmIgnoredBuild': return 'build scripts blocked by pnpm allowlist'
|
|
64
63
|
case 'pluginPrepare': return 'plugin distribution incomplete'
|
|
65
64
|
case 'network': return 'network failure on the user side'
|
|
66
|
-
case '
|
|
67
|
-
case '
|
|
65
|
+
case 'hostCliSpawn': return 'the DSH host could not be launched on the user machine'
|
|
66
|
+
case 'hubState': return `stale ${word} state in the plugin hub`
|
|
67
|
+
case 'timeout': return `${word} timed out`
|
|
68
68
|
case 'profileLock': return 'DSH profile is locked by another process'
|
|
69
69
|
case 'pluginMissingDep': return 'a dependency of the plugin is not published on npm'
|
|
70
|
-
case 'hub': return
|
|
71
|
-
default: return
|
|
70
|
+
case 'hub': return `${word} failed (cause unclassified)`
|
|
71
|
+
default: return `plugin ${word} failed`
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -78,19 +78,21 @@ function reasonTitleOf(kind: FailureKind): string {
|
|
|
78
78
|
* 错误核心摘要(完整日志太长,塞进 URL 会被 GitHub 以「request URL too long」拒绝,
|
|
79
79
|
* 故只收集重点)。错误弹窗、失败记录共用此逻辑。
|
|
80
80
|
* env 取不到时为 null,链接照常生成、只是少环境段。
|
|
81
|
-
* attempts
|
|
81
|
+
* attempts(尝试过的安装方式,实际执行过的命令):作者看到我们试过的命令,
|
|
82
82
|
* 组织 scope 与 GitHub 用户名不一致时也能直接指认正确的 npm 包名。
|
|
83
83
|
*/
|
|
84
|
-
export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | null, command?: string, attempts?: string[]): string {
|
|
84
|
+
export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | null, command?: string, attempts?: string[], action: FailureAction = 'install'): string {
|
|
85
85
|
// 原因判定:按失败类型归类,便于作者判断问题是否出在插件侧。
|
|
86
|
-
// [packaging]
|
|
87
|
-
//
|
|
86
|
+
// [packaging](装后校验拦截)单独列:分发物不完整(package.json 声明的文件
|
|
87
|
+
// 在发布物里缺失),作者照此补齐即可。
|
|
88
88
|
const kind = classifyFailure(message)
|
|
89
89
|
const executed = executedCommandOf(command, attempts)
|
|
90
|
+
// 动作词:标题与正文的「安装/更新/卸载」统一取自这里
|
|
91
|
+
const zhWord = actionWordZh(action)
|
|
90
92
|
// 标题 = 错误原因(非动作词),让 issue 列表一眼可分流
|
|
91
|
-
const title = `[dsh-plugin.org | dsh-plugin-hub] ${reasonTitleOf(kind)}: ${repo}`
|
|
93
|
+
const title = `[dsh-plugin.org | dsh-plugin-hub] ${reasonTitleOf(kind, action)}: ${repo}`
|
|
92
94
|
const reason = /\[packaging\]/i.test(message)
|
|
93
|
-
? 'plugin distribution is incomplete — a file the package needs (its entry from main / exports, an entry a bundled patch mounts, or the patch declared by dsh.bundle.patch) is missing from the published
|
|
95
|
+
? 'plugin distribution is incomplete — a file the package needs (its entry from main / exports, an entry a bundled patch mounts, or the patch declared by dsh.bundle.patch) is missing from the published npm package; please publish a complete package'
|
|
94
96
|
: /\[registry-missing\]/i.test(message)
|
|
95
97
|
// 服务端用官方 registry 复核过:缺的包在官方源上也不存在,且不是用户 profile 的残留依赖
|
|
96
98
|
? 'a dependency declared by the plugin is not published on the public npm registry (missing, removed, or never released); please publish or correct that dependency, or ship it with the plugin'
|
|
@@ -123,23 +125,22 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
|
|
|
123
125
|
`- Cause: ${reason}`,
|
|
124
126
|
...(code ? [`- Key error: \`${code}\``] : []),
|
|
125
127
|
'',
|
|
126
|
-
// 来源说明:标题(链官网)+ 一句来源(链仓库)+
|
|
127
|
-
`## [DSH Plugin 插件市场](${SITE_URL}) ·
|
|
128
|
-
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL})
|
|
129
|
-
`-
|
|
130
|
-
`-
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
// 查过/试过的命令就能直接指认
|
|
128
|
+
// 来源说明:标题(链官网)+ 一句来源(链仓库)+ 实际执行的命令(含 --profile)与执行结果,链接由常量动态拼接
|
|
129
|
+
`## [DSH Plugin 插件市场](${SITE_URL}) · ${zhWord} Plugin 失败错误信息`,
|
|
130
|
+
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件市场的安装程序自动生成,随本次${zhWord}失败一并提交。`,
|
|
131
|
+
`- 实际执行的${zhWord}命令:${executed ? `\`${executed}\`` : 'unknown (no executed command recorded)'}`,
|
|
132
|
+
`- 执行结果:${zhWord}失败,未能${zhWord}该插件。`,
|
|
133
|
+
// 尝试过的安装方式(实际执行过的命令,按先后顺序):作者据此指认正确的 npm 包名。
|
|
134
|
+
// 不写「安装」:卸载失败也走这条正文。
|
|
134
135
|
...(attempts && attempts.length > 0
|
|
135
|
-
? ['', '## Attempted
|
|
136
|
+
? ['', '## Attempted channels(已尝试的方式)', ...attempts.map((a) => `- ${a}`)]
|
|
136
137
|
: []),
|
|
137
|
-
// [packaging]
|
|
138
|
+
// [packaging] 场景:说明当前插件的 npm 分发物不完整
|
|
138
139
|
...(/\[packaging\]/i.test(message)
|
|
139
140
|
? [
|
|
140
141
|
'',
|
|
141
142
|
'## 安装方式说明',
|
|
142
|
-
'
|
|
143
|
+
'当前插件的 npm 分发物缺少它需要的文件(`main` / `exports` 声明的入口、补丁按本包名 mount 的入口,或 `dsh.bundle.patch` 声明的补丁)。请发布包含完整构建产物的 npm 包后重试。',
|
|
143
144
|
]
|
|
144
145
|
: []),
|
|
145
146
|
'',
|
|
@@ -192,14 +193,14 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
|
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
/**
|
|
195
|
-
* 插件中心自身仓库的预填 Issue
|
|
196
|
-
*
|
|
196
|
+
* 插件中心自身仓库的预填 Issue 链接:用于「非插件侧」失败(无法识别的未知原因,
|
|
197
|
+
* 以及判定由插件中心自己产出、可能误报时的次要反馈入口)。正文带上目标插件与完整
|
|
197
198
|
* 错误摘要,供插件中心维护者分诊 —— 不再把环境问题/插件中心自身问题误投给插件作者。
|
|
198
199
|
*/
|
|
199
200
|
export type HubIssueVariant =
|
|
200
|
-
/**
|
|
201
|
+
/** 缺省:安装器主动把失败送到插件中心分诊(未分类兜底) */
|
|
201
202
|
| 'triage'
|
|
202
|
-
/**
|
|
203
|
+
/** 从判定由插件中心自己产出的分支(插件打包问题 / allowBuilds 拦截)的次要入口提交:用户怀疑是误报 */
|
|
203
204
|
| 'misjudge'
|
|
204
205
|
|
|
205
206
|
export function hubIssueUrl(
|
|
@@ -210,38 +211,42 @@ export function hubIssueUrl(
|
|
|
210
211
|
attempts?: string[],
|
|
211
212
|
/** 见 HubIssueVariant;缺省 'triage',既有正文保持不变 */
|
|
212
213
|
variant: HubIssueVariant = 'triage',
|
|
214
|
+
/** 上报动作:标题与正文按它取词 */
|
|
215
|
+
action: FailureAction = 'install',
|
|
213
216
|
): string {
|
|
214
217
|
const kind = classifyFailure(message)
|
|
215
218
|
const executed = executedCommandOf(command, attempts)
|
|
219
|
+
const zhWord = actionWordZh(action)
|
|
216
220
|
const repo = pluginRepo && pluginRepo.trim() !== '' ? pluginRepo.trim() : 'unknown'
|
|
217
221
|
const title = variant === 'misjudge'
|
|
218
222
|
// 标题就点明「可能是误判」,维护者一眼能区分它和未分类兜底
|
|
219
223
|
? `[dsh-plugin-hub] possible misjudgment: ${repo}`
|
|
220
|
-
: `[dsh-plugin-hub] ${reasonTitleOf(kind)}: ${repo}`
|
|
224
|
+
: `[dsh-plugin-hub] ${reasonTitleOf(kind, action)}: ${repo}`
|
|
221
225
|
const code = coreErrorCode(message)
|
|
222
226
|
const build = (coreChars: number): string => {
|
|
223
227
|
const body = [
|
|
224
228
|
'## Summary',
|
|
225
|
-
`- Auto-classified cause: ${reasonTitleOf(kind)}`,
|
|
229
|
+
`- Auto-classified cause: ${reasonTitleOf(kind, action)}`,
|
|
226
230
|
`- Target plugin: \`${repo}\``,
|
|
227
231
|
...(code ? [`- Key error: \`${code}\``] : []),
|
|
228
|
-
//
|
|
232
|
+
// 为什么投递到这里而非插件仓库:只有确有证据是插件自身构建/分发失败(pluginPrepare、
|
|
233
|
+
// 或自动放行 allowBuilds 重试后仍失败的构建拦截)才投插件仓库
|
|
229
234
|
...(variant === 'misjudge'
|
|
230
|
-
//
|
|
231
|
-
//
|
|
235
|
+
// 这些分支的判据由插件中心自己产出、或掺了我们的动作(自动放行 allowBuilds)——
|
|
236
|
+
// 一旦我们自己误报,用户就被送去作者仓库、我们永远收不到反馈。
|
|
232
237
|
// 正文写清它来自哪条分支、判据是谁给的,维护者才不会把它当成未分类兜底。
|
|
233
238
|
? [
|
|
234
|
-
'- Filed from:
|
|
235
|
-
'- Source of that verdict: produced by dsh-plugin-hub\'s own
|
|
239
|
+
'- Filed from: a branch that normally routes to the plugin repo — filed here because that verdict may be our own misjudgment.',
|
|
240
|
+
'- Source of that verdict: produced by dsh-plugin-hub\'s own check or its auto-allow retry, not by plain pnpm output.',
|
|
236
241
|
]
|
|
237
|
-
: ['- Why filed here: the installer only routes a failure to the plugin repo when
|
|
242
|
+
: ['- Why filed here: the installer only routes a failure to the plugin repo when the cause is demonstrably the plugin\'s own build/distribution (pluginPrepare, or build scripts pnpm blocks that even an auto-allow retry cannot get past). This failure does not match that, so it is routed here for triage.']),
|
|
238
243
|
'',
|
|
239
|
-
`## [DSH Plugin 插件市场](${SITE_URL}) ·
|
|
240
|
-
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL})
|
|
241
|
-
`-
|
|
242
|
-
`-
|
|
244
|
+
`## [DSH Plugin 插件市场](${SITE_URL}) · ${zhWord} Plugin 失败错误信息`,
|
|
245
|
+
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件市场的安装程序自动生成,随本次${zhWord}失败一并提交。`,
|
|
246
|
+
`- 实际执行的${zhWord}命令:${executed ? `\`${executed}\`` : 'unknown (no executed command recorded)'}`,
|
|
247
|
+
`- 执行结果:${zhWord}失败,未能${zhWord}该插件。`,
|
|
243
248
|
...(attempts && attempts.length > 0
|
|
244
|
-
? ['', '## Attempted
|
|
249
|
+
? ['', '## Attempted channels(已尝试的方式)', ...attempts.map((a) => `- ${a}`)]
|
|
245
250
|
: []),
|
|
246
251
|
'',
|
|
247
252
|
'## Environment',
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* label + example hint above the input + input row (input + install button)
|
|
9
|
-
* + optional format-error note. Colors resolve through the --hub-* tokens.
|
|
5
|
+
* 自定义安装视图:独立一级板块,用于手动安装目录之外的 npm 包。安装卡片逐行堆叠:
|
|
6
|
+
* 渠道标签 + 输入框上方的示例提示 + 输入行(输入框 + 安装按钮)+ 可选的格式错误提示。
|
|
7
|
+
* 颜色经 --hub-* 变量解析。
|
|
10
8
|
*/
|
|
11
9
|
|
|
12
10
|
.root {
|
|
@@ -84,31 +82,6 @@
|
|
|
84
82
|
border-color: var(--hub-brand-border-soft);
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
/* 命令卡片标题旁的「一键插入更新命令」:右对齐小号描边按钮,弱化但可点,hover 提亮品牌色 */
|
|
88
|
-
.installInsertBtn {
|
|
89
|
-
flex-shrink: 0;
|
|
90
|
-
margin-left: auto;
|
|
91
|
-
font-family: inherit;
|
|
92
|
-
font-size: 11px;
|
|
93
|
-
line-height: 16px;
|
|
94
|
-
font-weight: 500;
|
|
95
|
-
color: var(--hub-text-secondary);
|
|
96
|
-
background: transparent;
|
|
97
|
-
border: 1px solid var(--hub-border-2);
|
|
98
|
-
border-radius: 6px;
|
|
99
|
-
padding: 2px 9px;
|
|
100
|
-
cursor: pointer;
|
|
101
|
-
white-space: nowrap;
|
|
102
|
-
user-select: none;
|
|
103
|
-
transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.installInsertBtn:hover {
|
|
107
|
-
color: var(--hub-brand);
|
|
108
|
-
border-color: var(--hub-brand-border-soft);
|
|
109
|
-
background: var(--hub-brand-tint);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
85
|
/* 文本框上方的示例:灰字小字,告诉用户该通道的推荐写法 */
|
|
113
86
|
.installExample {
|
|
114
87
|
font-size: 11px;
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* a button — click to re-probe just that channel with a visible
|
|
9
|
-
* running→result feedback loop. Colors resolve through the --hub-* tokens. */
|
|
5
|
+
* 系统诊断列表:每个连通性探测一行 —— 名称、目标地址、延迟与实时状态徽标
|
|
6
|
+
* (200 OK / 不可达 / 检测中…)。页面打开时自动跑一轮;每行都是按钮 ——
|
|
7
|
+
* 点击只重测该通道,形成「运行→结果」的可见反馈闭环。颜色经 --hub-* 变量解析。 */
|
|
10
8
|
|
|
11
9
|
.panel {
|
|
12
10
|
display: flex;
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* a floating menu with hover fills and a brand-highlighted active option.
|
|
8
|
-
* Colors resolve through the --hub-* design tokens. */
|
|
5
|
+
* 按钮式下拉框:触发器呈现为紧凑工具栏按钮(细边框 + 淡填充 + 当前值 + 箭头),
|
|
6
|
+
* 面板是浮层菜单,选项有 hover 填充、激活项品牌高亮。颜色经 --hub-* 设计变量解析。 */
|
|
9
7
|
|
|
10
8
|
.dropdown {
|
|
11
9
|
position: relative;
|