dsh-plugin 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +5 -5
- package/README.md +5 -5
- package/client/client.js +242 -153
- package/client/client.js.map +1 -1
- package/lib/http/routes.js +15 -8
- package/lib/index.js +4 -0
- package/lib/services/install-types.js +8 -2
- package/lib/services/install.js +1 -1
- package/lib/services/installed-versions.js +5 -1
- package/lib/services/loader.js +4 -0
- package/lib/services/npm-resolve.js +4 -0
- package/lib/services/pending-restart.js +1 -1
- package/lib/services/preflight.js +9 -4
- package/lib/services/profile.js +30 -3
- package/lib/services/progress.js +4 -0
- package/lib/services/task-queue.js +13 -7
- package/lib/types/index.d.ts +4 -0
- package/lib/types/services/install-types.d.ts +10 -4
- package/lib/types/services/install.d.ts +5 -1
- package/lib/types/services/loader.d.ts +4 -0
- package/lib/types/services/pending-restart.d.ts +8 -2
- package/lib/types/services/profile.d.ts +4 -2
- package/lib/types/services/progress.d.ts +4 -0
- package/package.json +1 -1
- package/src/client/components/AboutModal.tsx +4 -0
- package/src/client/components/CatalogControls.tsx +4 -0
- package/src/client/components/CatalogHeader.tsx +4 -0
- package/src/client/components/CatalogList.tsx +4 -0
- package/src/client/components/CategoryTabs.tsx +4 -0
- package/src/client/components/Dropdown.tsx +4 -0
- package/src/client/components/HubUpdateModal.tsx +4 -0
- package/src/client/components/NotificationsModal.tsx +4 -0
- package/src/client/components/PluginCard.tsx +5 -1
- package/src/client/components/PluginHubSection.tsx +5 -1
- package/src/client/components/ProgressView.tsx +4 -0
- package/src/client/components/icons.tsx +4 -0
- package/src/client/components/modals.tsx +4 -0
- package/src/client/hooks/useCatalog.ts +9 -5
- package/src/client/hooks/useTaskQueue.ts +13 -9
- package/src/client/index.tsx +4 -0
- package/src/client/lib/catalog.ts +27 -6
- package/src/client/lib/env.ts +4 -0
- package/src/client/lib/failures.ts +5 -1
- package/src/client/lib/format.ts +4 -0
- package/src/client/lib/renderMarkdown.ts +4 -0
- package/src/client/locales.ts +7 -1
- package/src/client/styles/Dropdown.module.css +5 -1
- package/src/client/styles/Header.module.css +5 -1
- package/src/client/styles/List.module.css +5 -1
- package/src/client/styles/Modal.module.css +5 -1
- package/src/client/styles/tokens.module.css +5 -1
- package/src/client/types.ts +5 -1
- package/src/server/http/routes.ts +14 -8
- package/src/server/index.ts +4 -0
- package/src/server/services/install-types.ts +10 -4
- package/src/server/services/install.ts +5 -1
- package/src/server/services/installed-versions.ts +5 -1
- package/src/server/services/loader.ts +4 -0
- package/src/server/services/npm-resolve.ts +4 -0
- package/src/server/services/pending-restart.ts +8 -2
- package/src/server/services/preflight.ts +9 -4
- package/src/server/services/profile.ts +27 -3
- package/src/server/services/progress.ts +4 -0
- package/src/server/services/task-queue.ts +13 -7
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Background install/remove task queue for the Plugin Hub.
|
|
3
7
|
*
|
|
4
8
|
* Mirrors the server-side FIFO active queue by polling /active, settles
|
|
@@ -7,7 +11,7 @@
|
|
|
7
11
|
*/
|
|
8
12
|
import { useEffect, useRef, useState } from 'react'
|
|
9
13
|
import type { HubPlugin, Translate } from '../types.ts'
|
|
10
|
-
import { installCommandOf, installTargetOf } from '../lib/catalog.ts'
|
|
14
|
+
import { installCommandOf, installTargetOf, repoFromInstallTarget } from '../lib/catalog.ts'
|
|
11
15
|
|
|
12
16
|
/** 客户端任务队列项:镜像服务端 active 任务(running 在前、pending 在后)。 */
|
|
13
17
|
export interface QueueTask {
|
|
@@ -41,7 +45,7 @@ export interface QueueTask {
|
|
|
41
45
|
|
|
42
46
|
/** 客户端待重启项:镜像服务端内存列表 + 本地补齐的展示信息(简介/版本)。 */
|
|
43
47
|
export interface PendingRestart {
|
|
44
|
-
/** owner/repo
|
|
48
|
+
/** owner/repo(展示用,已从安装目标归一化) */
|
|
45
49
|
target: string
|
|
46
50
|
/** 待重启语义:install 装完等挂载 / uninstall 卸完等清理 */
|
|
47
51
|
kind: 'install' | 'uninstall'
|
|
@@ -120,7 +124,7 @@ export function useTaskQueue(opts: TaskQueueOptions) {
|
|
|
120
124
|
setQueue(next)
|
|
121
125
|
}
|
|
122
126
|
|
|
123
|
-
/**
|
|
127
|
+
/** 解析服务端待重启列表(安装目标归一化为 owner/repo,与安装任务对齐)。 */
|
|
124
128
|
const parsePendingRestarts = (value: unknown): PendingRestart[] => {
|
|
125
129
|
if (!Array.isArray(value)) return []
|
|
126
130
|
const out: PendingRestart[] = []
|
|
@@ -130,7 +134,7 @@ export function useTaskQueue(opts: TaskQueueOptions) {
|
|
|
130
134
|
if (typeof target !== 'string' || target === '') continue
|
|
131
135
|
const at = (x as { at?: unknown }).at
|
|
132
136
|
out.push({
|
|
133
|
-
target: target
|
|
137
|
+
target: repoFromInstallTarget(target),
|
|
134
138
|
kind: (x as { kind?: unknown }).kind === 'uninstall' ? 'uninstall' : 'install',
|
|
135
139
|
at: typeof at === 'number' ? at : 0,
|
|
136
140
|
})
|
|
@@ -280,9 +284,9 @@ export function useTaskQueue(opts: TaskQueueOptions) {
|
|
|
280
284
|
next.push({
|
|
281
285
|
id,
|
|
282
286
|
kind: isRemove ? 'uninstall' : 'install',
|
|
283
|
-
target: typeof a.target === 'string' ? a.target
|
|
287
|
+
target: typeof a.target === 'string' ? repoFromInstallTarget(a.target) : (prevTask?.target ?? ''),
|
|
284
288
|
desc: prevTask?.desc,
|
|
285
|
-
repo: prevTask?.repo ?? (isRemove ? null : (typeof a.target === 'string' ? a.target
|
|
289
|
+
repo: prevTask?.repo ?? (isRemove ? null : (typeof a.target === 'string' ? repoFromInstallTarget(a.target) : null)),
|
|
286
290
|
// 安装时快照的目录信号:轮询合并时保留,任务成功后记录到本地
|
|
287
291
|
version: prevTask?.version,
|
|
288
292
|
updatedAt: prevTask?.updatedAt,
|
|
@@ -341,11 +345,11 @@ export function useTaskQueue(opts: TaskQueueOptions) {
|
|
|
341
345
|
.filter((a) => typeof a.id === 'number')
|
|
342
346
|
.map((a) => {
|
|
343
347
|
const isRemove = a.action === 'remove'
|
|
344
|
-
// 服务端任务的 target
|
|
348
|
+
// 服务端任务的 target:显式 HTTPS Git URL(安装)/ npm 包名(npm 安装/卸载)。
|
|
345
349
|
// 恢复展示统一用 displayTarget(owner/repo,服务端固定传):npm 安装时 target 是包名,
|
|
346
350
|
// 直接展示会让用户看到「安装方式」的内部细节,违背无感知原则;卸载保持包名(匹配已装表)。
|
|
347
|
-
const display = typeof (a as { displayTarget?: unknown }).displayTarget === 'string' ? (a as { displayTarget: string }).displayTarget
|
|
348
|
-
const fallback = typeof a.target === 'string' ? a.target
|
|
351
|
+
const display = typeof (a as { displayTarget?: unknown }).displayTarget === 'string' ? repoFromInstallTarget((a as { displayTarget: string }).displayTarget) : ''
|
|
352
|
+
const fallback = typeof a.target === 'string' ? repoFromInstallTarget(a.target) : ''
|
|
349
353
|
return {
|
|
350
354
|
id: a.id as number,
|
|
351
355
|
kind: (isRemove ? 'uninstall' : 'install') as 'install' | 'uninstall',
|
package/src/client/index.tsx
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* dsh-plugin client entry: wires the Plugin Hub into the host settings via
|
|
3
7
|
* the module-loader bundle (see tsdown.config.ts). All UI logic lives in
|
|
4
8
|
* PluginHubSection; this file only performs the cordis apply wiring.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Catalog constants and normalizers: site URLs, category metadata, sort keys
|
|
3
7
|
* and the mapping from the online API's short-key payloads to HubPlugin.
|
|
4
8
|
*/
|
|
@@ -59,7 +63,7 @@ export function langPathOf(lang: LocaleId): string {
|
|
|
59
63
|
|
|
60
64
|
/**
|
|
61
65
|
* 官网详情页两级路径:/plugins/{ownerSlug}/{slug};
|
|
62
|
-
*
|
|
66
|
+
* 缺 ownerSlug 时从 repo 推导(卡片详情按钮与弹窗来源行共用)。
|
|
63
67
|
*/
|
|
64
68
|
export function pluginDetailUrl(plugin: HubPlugin, langPath: string): string {
|
|
65
69
|
const repo = plugin.source?.repo ?? ''
|
|
@@ -107,7 +111,7 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
|
|
|
107
111
|
// 来源说明:标题(链官网)+ 一句来源(链仓库)+ 实际执行的安装命令(含 --profile)与执行结果,链接由常量动态拼接
|
|
108
112
|
`## [DSH-Plugin 插件中心](${SITE_URL}) · 安装 Plugin 失败错误信息`,
|
|
109
113
|
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件中心的安装程序自动生成,随本次安装失败一并提交。`,
|
|
110
|
-
`- 实际执行的安装命令:\`${command ?? `dsh plugin${env?.profile ? ` --profile ${env.profile}` : ''} add github
|
|
114
|
+
`- 实际执行的安装命令:\`${command ?? `dsh plugin${env?.profile ? ` --profile ${env.profile}` : ''} add git+https://github.com/${repo}.git`}\``,
|
|
111
115
|
`- 执行结果:安装失败,未能安装该插件。`,
|
|
112
116
|
// 尝试过的安装方式(npm 反查 + 实际执行命令,按先后顺序):作者据此反推正确的
|
|
113
117
|
// npm 包名 —— 组织 scope 与 GitHub 用户名不一致时仅凭仓库名猜不到,作者看到我们
|
|
@@ -120,7 +124,7 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
|
|
|
120
124
|
? [
|
|
121
125
|
'',
|
|
122
126
|
'## 安装方式说明',
|
|
123
|
-
'DSH 插件支持两种官方安装通道:`dsh plugin add <npm-package>`(npm 分发,需发布完整构建产物)与 `dsh plugin add github
|
|
127
|
+
'DSH 插件支持两种官方安装通道:`dsh plugin add <npm-package>`(npm 分发,需发布完整构建产物)与 `dsh plugin add git+https://github.com/owner/repo.git`(Git 直装,仓库需提交构建产物或在 package.json 提供 `prepare` 脚本)。当前插件的分发物缺少 package.json 声明的入口文件,请按所用通道补齐后重新发布。',
|
|
124
128
|
]
|
|
125
129
|
: []),
|
|
126
130
|
'',
|
|
@@ -194,7 +198,7 @@ export function normalize(raw: Record<string, unknown>): HubPlugin {
|
|
|
194
198
|
topics: Array.isArray(raw.t) ? (raw.t as string[]) : undefined,
|
|
195
199
|
features: Array.isArray(raw.f) ? (raw.f as string[]) : undefined,
|
|
196
200
|
description: typeof raw.d === 'string' ? raw.d : undefined,
|
|
197
|
-
//
|
|
201
|
+
// source 双形态:字符串 repo,或 { repo, npmPackage } 对象
|
|
198
202
|
source: typeof raw.r === 'string'
|
|
199
203
|
? { repo: raw.r }
|
|
200
204
|
: raw.r !== null && typeof raw.r === 'object'
|
|
@@ -231,12 +235,29 @@ export function installTargetOf(p: HubPlugin): { target: string; via: 'npm' | 'g
|
|
|
231
235
|
return { target: repo, via: 'github' }
|
|
232
236
|
}
|
|
233
237
|
|
|
234
|
-
/** 展示用安装命令(复制/弹窗):npm 通道显示包名,git
|
|
238
|
+
/** 展示用安装命令(复制/弹窗):npm 通道显示包名,git 通道显示显式 HTTPS URL。 */
|
|
235
239
|
export function installCommandOf(p: HubPlugin, withProfile = false): string {
|
|
236
240
|
const { target, via } = installTargetOf(p)
|
|
237
241
|
return via === 'npm'
|
|
238
242
|
? `dsh plugin${withProfile ? ' --profile web' : ''} add ${target}`
|
|
239
|
-
: `dsh plugin${withProfile ? ' --profile web' : ''} add github
|
|
243
|
+
: `dsh plugin${withProfile ? ' --profile web' : ''} add git+https://github.com/${target}.git`
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Normalize a task/install target to its owner/repo display identity. */
|
|
247
|
+
export function repoFromInstallTarget(value: string): string {
|
|
248
|
+
const input = value.trim()
|
|
249
|
+
if (/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/.test(input)) return input
|
|
250
|
+
const patterns = [
|
|
251
|
+
/^github:([^/]+)\/([^/]+)$/i,
|
|
252
|
+
/^(?:git\+)?https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
|
|
253
|
+
/^(?:git\+)?ssh:\/\/(?:git@)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
|
|
254
|
+
/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
|
|
255
|
+
]
|
|
256
|
+
for (const pattern of patterns) {
|
|
257
|
+
const match = pattern.exec(input)
|
|
258
|
+
if (match !== null) return `${match[1]}/${match[2]}`
|
|
259
|
+
}
|
|
260
|
+
return value
|
|
240
261
|
}
|
|
241
262
|
|
|
242
263
|
export const CATEGORY_LABELS: Record<string, { zh: string; en: string }> = {
|
package/src/client/lib/env.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* One-shot host environment snapshot for bug reports. Fetched lazily and
|
|
3
7
|
* cached at module level: issue URLs embed this info, and the environment
|
|
4
8
|
* does not change during a session. A failed fetch degrades to null so the
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Persistent install/remove notifications.
|
|
3
7
|
*
|
|
4
8
|
* Every settled task — success or failure — is appended to localStorage the
|
|
@@ -114,7 +118,7 @@ export function loadNotifications(): NotificationRecord[] {
|
|
|
114
118
|
return list
|
|
115
119
|
.filter((r): r is NotificationRecord =>
|
|
116
120
|
!!r && typeof r === 'object' && typeof (r as { message?: unknown }).message === 'string')
|
|
117
|
-
//
|
|
121
|
+
// ok 字段缺省时按失败渲染
|
|
118
122
|
.map((r) => ({ ...r, ok: r.ok === true }))
|
|
119
123
|
} catch {
|
|
120
124
|
return []
|
package/src/client/lib/format.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Presentation formatters shared across the catalog list and the cards.
|
|
3
7
|
*/
|
|
4
8
|
import type { Translate } from '../types.ts'
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* 轻量 Markdown 渲染器:仅用于 Hub 自我更新的变更记录(Worker 下发的 notes)。
|
|
3
7
|
*
|
|
4
8
|
* 内容由插件作者自己编写、属可信内容,但渲染端仍先整体 HTML 转义,再按
|
package/src/client/locales.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
6
|
+
* zh/en dictionaries for the DSH-Plugin Hub settings section.
|
|
7
|
+
*/
|
|
2
8
|
export const zh = {
|
|
3
9
|
nav: '插件中心',
|
|
4
10
|
title: 'DSH-Plugin Hub',
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
2
|
+
* Website: https://dsh-plugin.org
|
|
3
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
|
+
*
|
|
5
|
+
* 自绘下拉(替换原生 <select>)。颜色跟随宿主主题:经 --hub-* 设计令牌解析,
|
|
2
6
|
* 与 Header 的控件(排序/分段)保持一致的无边框 + 圆角语言。 */
|
|
3
7
|
.dropdown {
|
|
4
8
|
position: relative;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/* Plugin Hub
|
|
1
|
+
/* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
2
|
+
* Website: https://dsh-plugin.org
|
|
3
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
|
+
*
|
|
5
|
+
* Plugin Hub section header: brand row, tagline, ad banner, search input,
|
|
2
6
|
* filter toolbar (installed / not installed, failures entry) and category
|
|
3
7
|
* tabs. Colors resolve through the --hub-* design tokens. */
|
|
4
8
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/* Plugin Hub
|
|
1
|
+
/* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
2
|
+
* Website: https://dsh-plugin.org
|
|
3
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
|
+
*
|
|
5
|
+
* Plugin Hub catalog list: the scrollable body, plugin cards (name, badges,
|
|
2
6
|
* description, topics, stats, actions), loading / empty states and the
|
|
3
7
|
* browse-all footer. Colors resolve through the --hub-* design tokens. */
|
|
4
8
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/* Plugin Hub
|
|
1
|
+
/* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
2
|
+
* Website: https://dsh-plugin.org
|
|
3
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
|
+
*
|
|
5
|
+
* Plugin Hub dialogs and overlays: install / uninstall modals, error dialog,
|
|
2
6
|
* failure-record dialog, task-queue dialog, progress strip, inline progress
|
|
3
7
|
* and the global toast. Colors resolve through the --hub-* design tokens. */
|
|
4
8
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/* Plugin Hub
|
|
1
|
+
/* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
2
|
+
* Website: https://dsh-plugin.org
|
|
3
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
|
+
*
|
|
5
|
+
* Plugin Hub design tokens.
|
|
2
6
|
*
|
|
3
7
|
* Semantic aliases over the host's --dsw-alias-* theme tokens, each with the
|
|
4
8
|
* original light-mode literal as fallback for hosts that do not define the
|
package/src/client/types.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Shared types for the dsh-plugin browser client. Kept dependency-free so
|
|
3
7
|
* any client module can import them without pulling in components or styles.
|
|
4
8
|
*/
|
|
@@ -36,7 +40,7 @@ export interface SectionProps {
|
|
|
36
40
|
|
|
37
41
|
/** Slim projection of the site's DirectoryPlugin entries. */
|
|
38
42
|
export interface HubPlugin {
|
|
39
|
-
/** 作者小写 key,与 slug 共同构成唯一身份(两级路径 /plugins/{ownerSlug}/{slug}
|
|
43
|
+
/** 作者小写 key,与 slug 共同构成唯一身份(两级路径 /plugins/{ownerSlug}/{slug});可能缺省 */
|
|
40
44
|
ownerSlug?: string
|
|
41
45
|
slug: string
|
|
42
46
|
displayName?: string
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Local HTTP routes exposing real installs to the in-app Plugin Hub UI.
|
|
3
7
|
* The client fetches the same-origin `/dsh-plugin-hub/*` endpoints; the
|
|
4
8
|
* install handler validates the target, then spawns the official dsh CLI
|
|
@@ -9,7 +13,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
|
9
13
|
import { homedir, release } from 'node:os'
|
|
10
14
|
import { dirname, join, resolve } from 'node:path'
|
|
11
15
|
import { spawn } from 'node:child_process'
|
|
12
|
-
import { activeTask, cancelTask, dumpLoaderEntries, getTask, githubTarget, hasQueuedTarget, listPendingRestarts, readProfileArg, startPluginMutation, validPackageName, type LoaderHandle } from '../services/install.ts'
|
|
16
|
+
import { activeTask, cancelTask, dumpLoaderEntries, getTask, githubRepoOf, githubTarget, hasQueuedTarget, listPendingRestarts, readProfileArg, startPluginMutation, validPackageName, type LoaderHandle } from '../services/install.ts'
|
|
13
17
|
import { recordInstalledVersion, recordResolvedNpmPackage, readInstalledVersions, removeInstalledVersion } from '../services/installed-versions.ts'
|
|
14
18
|
import { resolveNpmPackage } from '../services/npm-resolve.ts'
|
|
15
19
|
import { preflightTarget } from '../services/preflight.ts'
|
|
@@ -82,11 +86,13 @@ function hostEnv(profile: string): Record<string, string | null> {
|
|
|
82
86
|
}
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
/** Match
|
|
89
|
+
/** Match a GitHub install target against the installed table; returns the package name when already installed. */
|
|
86
90
|
function installedByRepo(installed: Record<string, string>, target: string): string | null {
|
|
87
|
-
const
|
|
91
|
+
const repo = githubRepoOf(target)
|
|
92
|
+
if (repo === null) return null
|
|
93
|
+
const needle = repo.toLowerCase()
|
|
88
94
|
for (const [name, spec] of Object.entries(installed)) {
|
|
89
|
-
if (spec
|
|
95
|
+
if (githubRepoOf(spec)?.toLowerCase() === needle) return name
|
|
90
96
|
}
|
|
91
97
|
return null
|
|
92
98
|
}
|
|
@@ -178,7 +184,7 @@ export function mountPluginHubRoutes(webServer: WebServerService, profile: strin
|
|
|
178
184
|
const displayRepo = typeof body === 'object' && body !== null && typeof (body as { display?: unknown }).display === 'string'
|
|
179
185
|
? (body as { display: string }).display.trim()
|
|
180
186
|
: rawRepo
|
|
181
|
-
// 目标语法两态:owner/repo →
|
|
187
|
+
// 目标语法两态:owner/repo → 显式 HTTPS Git 源(走装前预检);npm 包名(@scope/name 或 name)→ 信任 registry 直接安装
|
|
182
188
|
const repoTarget = githubTarget(rawRepo)
|
|
183
189
|
let target: string | null = repoTarget ?? (repoTarget === null && validPackageName(rawRepo) ? rawRepo : null)
|
|
184
190
|
if (target === null) {
|
|
@@ -208,7 +214,7 @@ export function mountPluginHubRoutes(webServer: WebServerService, profile: strin
|
|
|
208
214
|
// 重复安装防护:非更新请求命中已安装目标时直接拒绝,不重复跑 CLI
|
|
209
215
|
// (否则 CLI 会因「已存在」失败,且用户看到的是莫名其妙的报错);
|
|
210
216
|
// 更新请求(mode: 'update')放行,CLI 的 add 对已存在依赖是 pnpm 原位覆盖重装。
|
|
211
|
-
// 命中判定双通道:npm 包名按 dependencies 键直接命中,
|
|
217
|
+
// 命中判定双通道:npm 包名按 dependencies 键直接命中,GitHub 目标按仓库身份匹配。
|
|
212
218
|
const installed = readInstalled(profile)
|
|
213
219
|
const already = installedByRepo(installed, target) ?? (installed[target] !== undefined ? target : null)
|
|
214
220
|
if (already !== null && mode !== 'update') {
|
|
@@ -249,7 +255,7 @@ export function mountPluginHubRoutes(webServer: WebServerService, profile: strin
|
|
|
249
255
|
profile,
|
|
250
256
|
target,
|
|
251
257
|
// npm 安装时 target 是包名:displayTarget 固定展示仓库名,待重启行/前端恢复不受通道变化影响
|
|
252
|
-
displayTarget:
|
|
258
|
+
displayTarget: githubRepoOf(displayRepo) ?? undefined,
|
|
253
259
|
// 已尝试的安装方式(npm 反查):实际执行命令由 spawn 时追加,失败 issue 一并展示
|
|
254
260
|
attempts,
|
|
255
261
|
timeoutMs: COMMAND_TIMEOUT_MS,
|
|
@@ -294,7 +300,7 @@ export function mountPluginHubRoutes(webServer: WebServerService, profile: strin
|
|
|
294
300
|
profile,
|
|
295
301
|
target: name,
|
|
296
302
|
// 卸载的 mutation 目标是 npm 包名;待重启行要显示 owner/repo(与安装行一致),非法/缺失时回退包名
|
|
297
|
-
displayTarget:
|
|
303
|
+
displayTarget: githubRepoOf(repo) ?? undefined,
|
|
298
304
|
// 运行中 loader:卸载成功后主动移除条目,立即生效、无需重启
|
|
299
305
|
uninstallLoader: loader,
|
|
300
306
|
timeoutMs: COMMAND_TIMEOUT_MS,
|
package/src/server/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* dsh-plugin host entry: mounts the Plugin Hub HTTP routes once the host
|
|
3
7
|
* web server service is available. The browser bundle (src/client) talks to
|
|
4
8
|
* these same-origin routes to perform real installs from inside the app.
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
6
|
+
* 安装/卸载运行时的公共类型与常量(无依赖,供各服务模块复用)。
|
|
7
|
+
*/
|
|
2
8
|
|
|
3
9
|
/** 一个 pnpm 安装子进程的结果快照。 */
|
|
4
10
|
export interface InstallResult {
|
|
@@ -11,7 +17,7 @@ export interface InstallResult {
|
|
|
11
17
|
|
|
12
18
|
export interface InstallTask {
|
|
13
19
|
id: number
|
|
14
|
-
/**
|
|
20
|
+
/** 操作目标:显式 HTTPS Git URL(安装)或 npm 包名(卸载),冲突/恢复时需要展示给用户 */
|
|
15
21
|
target: string
|
|
16
22
|
/** 展示用目标(owner/repo):npm 安装时 target 是包名,前端恢复用此字段显示仓库名,保持用户无感知 */
|
|
17
23
|
displayTarget?: string
|
|
@@ -51,7 +57,7 @@ export interface QueueItem {
|
|
|
51
57
|
* 它自然清空,正好等于「重启后提醒消失」的语义,无需任何重启探测。
|
|
52
58
|
*/
|
|
53
59
|
export interface PendingRestart {
|
|
54
|
-
/**
|
|
60
|
+
/** Git 安装目标(与任务一致;卸载时为展示用的 owner/repo) */
|
|
55
61
|
target: string
|
|
56
62
|
/** 待重启语义:install 装完等挂载生效 / uninstall 卸完等清理 loader 残留 */
|
|
57
63
|
kind: 'install' | 'uninstall'
|
|
@@ -59,7 +65,7 @@ export interface PendingRestart {
|
|
|
59
65
|
at: number
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
/** Grammar of
|
|
68
|
+
/** Grammar of a catalog GitHub repository identity (`owner/repo`). */
|
|
63
69
|
export const REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/
|
|
64
70
|
/** Grammar of an npm package name (used for uninstall targets). */
|
|
65
71
|
export const PACKAGE_RE = /^(?:@[a-z0-9._-]+\/)?[A-Za-z0-9._-]+$/
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Install runner 的统一出口:执行真实插件变更(spawn 官方 dsh CLI 子进程)。
|
|
3
7
|
* 实现按职责拆分在同目录子模块,本文件只做公共 API 的 re-export,保持外部
|
|
4
8
|
* import 面稳定(routes.ts / index.ts 仍从 `services/install.ts` 引入)。
|
|
@@ -14,5 +18,5 @@ export type { InstallResult, InstallTask, Invocation, PendingRestart, QueueItem
|
|
|
14
18
|
export type { LoaderHandle } from './loader.ts'
|
|
15
19
|
export { addPendingRestart, clearPendingRestart, listPendingRestarts } from './pending-restart.ts'
|
|
16
20
|
export { dumpLoaderEntries, removeLoadedEntry } from './loader.ts'
|
|
17
|
-
export { addAllowBuildsKey, githubTarget, parseAllowBuildsKey, readProfileArg, validPackageName } from './profile.ts'
|
|
21
|
+
export { addAllowBuildsKey, githubRepoOf, githubTarget, parseAllowBuildsKey, readProfileArg, validPackageName } from './profile.ts'
|
|
18
22
|
export { activeTask, cancelTask, getTask, hasQueuedTarget, hasRunningTask, runPluginMutation, startPluginMutation } from './task-queue.ts'
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Installed-plugin version tracking.
|
|
3
7
|
*
|
|
4
8
|
* The installed table (`readInstalled` in routes.ts) only knows the manifest
|
|
5
|
-
* spec of each dependency
|
|
9
|
+
* spec of each Git dependency, which carries no version.
|
|
6
10
|
* To let the Plugin Hub tell "an update is available", we record the catalog
|
|
7
11
|
* signal present at install time into a small per-profile JSON file, then
|
|
8
12
|
* compare it against the catalog's current signal later.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* 运行中 loader 的读取与停用:卸载成功后对匹配条目做 live-disable,
|
|
3
7
|
* 使卸载立即生效(刷新页面不再加载已卸载的 client bundle,也无需重启)。
|
|
4
8
|
*/
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* npm 包反查:git 分发不完整(缺构建产物/子模块)导致安装失败时,用 npm
|
|
3
7
|
* registry 的搜索接口按 repository 地址反查该仓库对应的官方 npm 包。
|
|
4
8
|
*
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
6
|
+
* 待重启列表的内存态管理(宿主进程一重启自然清空)。
|
|
7
|
+
*/
|
|
2
8
|
import type { PendingRestart } from './install-types.ts'
|
|
3
9
|
|
|
4
10
|
const pendingRestarts: PendingRestart[] = []
|
|
@@ -6,7 +12,7 @@ const pendingRestarts: PendingRestart[] = []
|
|
|
6
12
|
/**
|
|
7
13
|
* 登记一条待重启记录(同一目标去重)。
|
|
8
14
|
* - 卸载:目标刚装完未重启(已有待重启记录)→ 宿主从未加载过它,卸完磁盘已干净、
|
|
9
|
-
* loader
|
|
15
|
+
* loader 无残留,无需再提醒重启,直接移除该条;否则(已被宿主加载过)登记「卸载后待重启清理」。
|
|
10
16
|
* - 安装:已有记录则刷新为安装语义(如卸载后未重启又重装),否则新登记。
|
|
11
17
|
*/
|
|
12
18
|
export function addPendingRestart(target: string, kind: 'install' | 'uninstall'): void {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
6
|
+
* 安装前预检:对 GitHub 源的插件,检查其分发(codeload tarball)里是否
|
|
3
7
|
* 真的包含 package.json 声明的入口文件(main / exports["."].default)。
|
|
4
8
|
* git 分发常不提交构建产物(lib/ 等),这类残缺包装完会让宿主重启加载插件树时
|
|
5
9
|
* ERR_MODULE_NOT_FOUND 直接崩溃 —— 预检在动手安装前就把它们拦下,避免白装一次。
|
|
@@ -13,6 +17,7 @@ import { tmpdir } from 'node:os'
|
|
|
13
17
|
import { join } from 'node:path'
|
|
14
18
|
import { pipeline } from 'node:stream/promises'
|
|
15
19
|
import { promisify } from 'node:util'
|
|
20
|
+
import { githubRepoOf } from './profile.ts'
|
|
16
21
|
|
|
17
22
|
const execFileAsync = promisify(execFile)
|
|
18
23
|
|
|
@@ -23,9 +28,9 @@ export interface PreflightResult {
|
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
export async function preflightTarget(target: string): Promise<PreflightResult> {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
const [
|
|
31
|
+
const source = githubRepoOf(target)
|
|
32
|
+
if (source === null) return { ok: true, missing: null }
|
|
33
|
+
const [owner, repo] = source.split('/')
|
|
29
34
|
let commit = ''
|
|
30
35
|
try {
|
|
31
36
|
const { stdout } = await execFileAsync('git', ['ls-remote', `https://github.com/${owner}/${repo}.git`, 'HEAD'])
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* 宿主 profile 与目标校验工具:解析当前 profile、目录定位、安装目标语法校验,
|
|
3
7
|
* 以及 pnpm allowBuilds 白名单的写入(失败恢复路径)。
|
|
4
8
|
*/
|
|
@@ -19,10 +23,30 @@ export function profileDirectory(profile: string): string {
|
|
|
19
23
|
return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'profiles', profile)
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
/** Build a safe
|
|
26
|
+
/** Build a safe explicit HTTPS Git target, or null when the repo is unsafe. */
|
|
23
27
|
export function githubTarget(repo: string): string | null {
|
|
24
28
|
if (typeof repo !== 'string' || !REPO_RE.test(repo)) return null
|
|
25
|
-
return `github
|
|
29
|
+
return `git+https://github.com/${repo}.git`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Extract an owner/repo identity from a catalog value or an installed Git spec. */
|
|
33
|
+
export function githubRepoOf(value: string): string | null {
|
|
34
|
+
if (typeof value !== 'string') return null
|
|
35
|
+
const input = value.trim()
|
|
36
|
+
if (REPO_RE.test(input)) return input
|
|
37
|
+
const patterns = [
|
|
38
|
+
/^github:([^/]+)\/([^/]+)$/i,
|
|
39
|
+
/^(?:git\+)?https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
|
|
40
|
+
/^(?:git\+)?ssh:\/\/(?:git@)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
|
|
41
|
+
/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
|
|
42
|
+
]
|
|
43
|
+
for (const pattern of patterns) {
|
|
44
|
+
const match = pattern.exec(input)
|
|
45
|
+
if (match === null) continue
|
|
46
|
+
const repo = `${match[1]}/${match[2]}`
|
|
47
|
+
if (REPO_RE.test(repo)) return repo
|
|
48
|
+
}
|
|
49
|
+
return null
|
|
26
50
|
}
|
|
27
51
|
|
|
28
52
|
/** Validate an npm package name (uninstall target grammar). */
|
|
@@ -32,7 +56,7 @@ export function validPackageName(name: string): boolean {
|
|
|
32
56
|
|
|
33
57
|
/**
|
|
34
58
|
* The exact `allowBuilds` key pnpm printed in its
|
|
35
|
-
* `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` hint. For
|
|
59
|
+
* `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` hint. For Git installs pnpm
|
|
36
60
|
* matches against `name@<resolved fetch URL>` — the URL it actually fetches,
|
|
37
61
|
* e.g. `https://codeload.github.com/<owner>/<repo>/tar.gz/<commit>` — not the
|
|
38
62
|
* `git+ssh://`/branch spec. A bare name or a `git+` spec does not match, so
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
2
6
|
* Pure progress-estimation helpers shared by the install runner.
|
|
3
7
|
*
|
|
4
8
|
* The dsh CLI wraps pnpm, which refreshes its `Progress: …` line in place
|