dsh-plugin 1.1.2 → 1.2.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/client/client.js +4469 -1445
- package/client/client.js.map +1 -1
- package/lib/http/routes.js +438 -22
- package/lib/index.js +1 -1
- package/lib/services/install/install.js +4 -0
- package/lib/services/install/npm-check.js +55 -0
- package/lib/services/{npm-resolve.js → install/npm-resolve.js} +7 -5
- package/lib/services/{preflight.js → install/preflight.js} +15 -4
- package/lib/services/{task-queue.js → install/task-queue.js} +155 -66
- package/lib/services/loader.js +56 -1
- package/lib/services/log.js +138 -0
- package/lib/services/probe.js +157 -0
- package/lib/services/{profile.js → profile/profile.js} +29 -1
- package/lib/services/settings.js +60 -0
- package/lib/types/http/routes.d.ts +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/services/{install-types.d.ts → install/install-types.d.ts} +3 -0
- package/lib/types/services/{install.d.ts → install/install.d.ts} +5 -5
- package/lib/types/services/install/npm-check.d.ts +12 -0
- package/lib/types/services/{npm-resolve.d.ts → install/npm-resolve.d.ts} +2 -1
- package/lib/types/services/{task-queue.d.ts → install/task-queue.d.ts} +5 -1
- package/lib/types/services/loader.d.ts +14 -8
- package/lib/types/services/log.d.ts +49 -0
- package/lib/types/services/probe.d.ts +30 -0
- package/lib/types/services/{pending-restart.d.ts → profile/pending-restart.d.ts} +1 -1
- package/lib/types/services/{profile.d.ts → profile/profile.d.ts} +2 -0
- package/lib/types/services/settings.d.ts +24 -0
- package/package.json +1 -1
- package/src/client/components/PluginHubSection.tsx +341 -120
- package/src/client/components/{CatalogList.tsx → catalog/CatalogList.tsx} +11 -7
- package/src/client/components/{PluginCard.tsx → catalog/PluginCard.tsx} +5 -4
- package/src/client/components/layout/CatalogControls.tsx +80 -0
- package/src/client/components/{CatalogHeader.tsx → layout/CatalogHeader.tsx} +4 -4
- package/src/client/components/{CategoryTabs.tsx → layout/CategoryTabs.tsx} +3 -3
- package/src/client/components/layout/SectionTabs.tsx +60 -0
- package/src/client/components/{AboutModal.tsx → modals/AboutModal.tsx} +7 -7
- package/src/client/components/{HubUpdateModal.tsx → modals/HubUpdateModal.tsx} +5 -5
- package/src/client/components/modals/InstalledDetailModal.tsx +193 -0
- package/src/client/components/modals/LogsModal.tsx +301 -0
- package/src/client/components/modals/LogsPathModal.tsx +161 -0
- package/src/client/components/{NotificationsModal.tsx → modals/NotificationsModal.tsx} +47 -20
- package/src/client/components/{ProgressView.tsx → modals/ProgressView.tsx} +2 -2
- package/src/client/components/{modals.tsx → modals/modals.tsx} +117 -40
- package/src/client/components/{Dropdown.tsx → ui/Dropdown.tsx} +14 -6
- package/src/client/components/ui/Toggle.tsx +28 -0
- package/src/client/components/ui/icons.tsx +343 -0
- package/src/client/components/views/CustomInstallView.tsx +250 -0
- package/src/client/components/views/DiagnosticsView.tsx +172 -0
- package/src/client/components/views/InstalledView.tsx +328 -0
- package/src/client/components/views/LogsView.tsx +150 -0
- package/src/client/components/views/MarketView.tsx +66 -0
- package/src/client/components/views/SettingsView.tsx +273 -0
- package/src/client/data/catalog.ts +38 -0
- package/src/client/data/host.ts +107 -0
- package/src/client/data/hub.ts +41 -0
- package/src/client/hooks/useCatalog.ts +71 -115
- package/src/client/hooks/useIncrementalList.ts +46 -0
- package/src/client/hooks/useLanguage.ts +40 -0
- package/src/client/hooks/useSettings.ts +90 -0
- package/src/client/hooks/useTaskQueue.ts +156 -39
- package/src/client/locales.ts +358 -38
- package/src/client/logic/constants.ts +87 -0
- package/src/client/{lib → logic}/failures.ts +95 -74
- package/src/client/logic/install-command.ts +56 -0
- package/src/client/logic/installed.ts +207 -0
- package/src/client/logic/normalize.ts +52 -0
- package/src/client/logic/urls.ts +125 -0
- package/src/client/styles/CustomInstallView.module.css +191 -0
- package/src/client/styles/DiagnosticsView.module.css +231 -0
- package/src/client/styles/Dropdown.module.css +42 -41
- package/src/client/styles/Header.module.css +100 -132
- package/src/client/styles/InstalledView.module.css +447 -0
- package/src/client/styles/List.module.css +6 -0
- package/src/client/styles/LogsView.module.css +503 -0
- package/src/client/styles/Modal.module.css +352 -70
- package/src/client/styles/SectionTabs.module.css +142 -0
- package/src/client/styles/SettingsView.module.css +232 -0
- package/src/client/styles/Toggle.module.css +60 -0
- package/src/client/styles/tokens.module.css +1 -0
- package/src/client/types.ts +23 -3
- package/src/server/http/routes.ts +414 -22
- package/src/server/index.ts +1 -1
- package/src/server/services/{install-types.ts → install/install-types.ts} +3 -1
- package/src/server/services/{install.ts → install/install.ts} +5 -5
- package/src/server/services/install/npm-check.ts +53 -0
- package/src/server/services/{npm-resolve.ts → install/npm-resolve.ts} +7 -5
- package/src/server/services/{preflight.ts → install/preflight.ts} +16 -4
- package/src/server/services/{task-queue.ts → install/task-queue.ts} +157 -63
- package/src/server/services/loader.ts +55 -1
- package/src/server/services/log.ts +167 -0
- package/src/server/services/probe.ts +157 -0
- package/src/server/services/{pending-restart.ts → profile/pending-restart.ts} +1 -1
- package/src/server/services/{profile.ts → profile/profile.ts} +31 -1
- package/src/server/services/settings.ts +79 -0
- package/lib/services/install.js +0 -4
- package/src/client/components/CatalogControls.tsx +0 -108
- package/src/client/components/icons.tsx +0 -129
- package/src/client/lib/catalog.ts +0 -293
- package/src/client/lib/env.ts +0 -20
- /package/lib/services/{install-types.js → install/install-types.js} +0 -0
- /package/lib/services/{installed-versions.js → profile/installed-versions.js} +0 -0
- /package/lib/services/{pending-restart.js → profile/pending-restart.js} +0 -0
- /package/lib/services/{progress.js → profile/progress.js} +0 -0
- /package/lib/types/services/{preflight.d.ts → install/preflight.d.ts} +0 -0
- /package/lib/types/services/{installed-versions.d.ts → profile/installed-versions.d.ts} +0 -0
- /package/lib/types/services/{progress.d.ts → profile/progress.d.ts} +0 -0
- /package/src/client/{lib → logic}/format.ts +0 -0
- /package/src/client/{lib → logic}/renderMarkdown.ts +0 -0
- /package/src/server/services/{installed-versions.ts → profile/installed-versions.ts} +0 -0
- /package/src/server/services/{progress.ts → profile/progress.ts} +0 -0
|
@@ -1,129 +0,0 @@
|
|
|
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
|
-
* Inline SVG icons used across the client. Icons inherit currentColor so
|
|
7
|
-
* they adapt to themes and disabled states automatically.
|
|
8
|
-
*/
|
|
9
|
-
import { createElement as h } from 'react'
|
|
10
|
-
import headerStyles from '../styles/Header.module.css'
|
|
11
|
-
import modalStyles from '../styles/Modal.module.css'
|
|
12
|
-
|
|
13
|
-
/** 弹窗右上角关闭按钮图标:内联 SVG 十字(stroke 继承 currentColor),随按钮禁用态一起变淡。 */
|
|
14
|
-
export function CloseIcon() {
|
|
15
|
-
return h('svg', {
|
|
16
|
-
className: modalStyles.modalCloseIcon,
|
|
17
|
-
viewBox: '0 0 16 16',
|
|
18
|
-
width: 14,
|
|
19
|
-
height: 14,
|
|
20
|
-
fill: 'none',
|
|
21
|
-
'aria-hidden': 'true',
|
|
22
|
-
}, h('path', {
|
|
23
|
-
d: 'M3 3l10 10M13 3L3 13',
|
|
24
|
-
stroke: 'currentColor',
|
|
25
|
-
strokeWidth: 1.6,
|
|
26
|
-
strokeLinecap: 'round',
|
|
27
|
-
}))
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** 来源链接图标:链节(stroke 继承 currentColor),弹窗里官网收录地址旁提示「可点击跳转」。 */
|
|
31
|
-
export function LinkIcon() {
|
|
32
|
-
return h('svg', {
|
|
33
|
-
className: modalStyles.linkIcon,
|
|
34
|
-
viewBox: '0 0 24 24',
|
|
35
|
-
width: 13,
|
|
36
|
-
height: 13,
|
|
37
|
-
fill: 'none',
|
|
38
|
-
'aria-hidden': 'true',
|
|
39
|
-
},
|
|
40
|
-
h('path', {
|
|
41
|
-
d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71',
|
|
42
|
-
stroke: 'currentColor',
|
|
43
|
-
strokeWidth: 1.8,
|
|
44
|
-
strokeLinecap: 'round',
|
|
45
|
-
strokeLinejoin: 'round',
|
|
46
|
-
}),
|
|
47
|
-
h('path', {
|
|
48
|
-
d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',
|
|
49
|
-
stroke: 'currentColor',
|
|
50
|
-
strokeWidth: 1.8,
|
|
51
|
-
strokeLinecap: 'round',
|
|
52
|
-
strokeLinejoin: 'round',
|
|
53
|
-
}))
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** 品牌 logo 图标:蓝紫渐变圆角方块 + 白色拼图块(2x2 错落),标题左侧的品牌标识。 */
|
|
57
|
-
export function LogoIcon() {
|
|
58
|
-
return h('svg', {
|
|
59
|
-
className: headerStyles.logoIcon,
|
|
60
|
-
viewBox: '0 0 24 24',
|
|
61
|
-
width: 20,
|
|
62
|
-
height: 20,
|
|
63
|
-
'aria-hidden': 'true',
|
|
64
|
-
},
|
|
65
|
-
h('defs', null,
|
|
66
|
-
h('linearGradient', { id: 'dshLogoGrad', x1: '0', y1: '0', x2: '1', y2: '1' },
|
|
67
|
-
h('stop', { offset: '0%', stopColor: '#4f46e5' }),
|
|
68
|
-
h('stop', { offset: '100%', stopColor: '#7c3aed' }),
|
|
69
|
-
),
|
|
70
|
-
),
|
|
71
|
-
h('rect', { x: 1, y: 1, width: 22, height: 22, rx: 6, fill: 'url(#dshLogoGrad)' }),
|
|
72
|
-
h('rect', { x: 5.5, y: 5.5, width: 5.5, height: 5.5, rx: 1.5, fill: '#ffffff' }),
|
|
73
|
-
h('rect', { x: 13, y: 5.5, width: 5.5, height: 5.5, rx: 1.5, fill: '#ffffff', opacity: 0.55 }),
|
|
74
|
-
h('rect', { x: 5.5, y: 13, width: 5.5, height: 5.5, rx: 1.5, fill: '#ffffff', opacity: 0.55 }),
|
|
75
|
-
h('rect', { x: 13, y: 13, width: 5.5, height: 5.5, rx: 1.5, fill: '#ffffff' }),
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** GitHub 图标:官方 GitHub Mark(octocat),fill 继承 currentColor,头部右上角源码链接用。 */
|
|
80
|
-
export function GitHubIcon() {
|
|
81
|
-
return h('svg', {
|
|
82
|
-
className: headerStyles.githubIcon,
|
|
83
|
-
viewBox: '0 0 24 24',
|
|
84
|
-
width: 18,
|
|
85
|
-
height: 18,
|
|
86
|
-
fill: 'currentColor',
|
|
87
|
-
'aria-hidden': 'true',
|
|
88
|
-
}, h('path', {
|
|
89
|
-
d: 'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
|
|
90
|
-
}))
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** 复制图标:双层矩形(stroke 继承 currentColor),手动命令旁的复制按钮用。 */
|
|
94
|
-
export function CopyIcon() {
|
|
95
|
-
return h('svg', {
|
|
96
|
-
className: headerStyles.copyIcon,
|
|
97
|
-
viewBox: '0 0 16 16',
|
|
98
|
-
width: 12,
|
|
99
|
-
height: 12,
|
|
100
|
-
fill: 'none',
|
|
101
|
-
'aria-hidden': 'true',
|
|
102
|
-
},
|
|
103
|
-
h('rect', {
|
|
104
|
-
x: 5.5, y: 5.5, width: 7, height: 7, rx: 1.5,
|
|
105
|
-
stroke: 'currentColor', strokeWidth: 1.4,
|
|
106
|
-
}),
|
|
107
|
-
h('path', {
|
|
108
|
-
d: 'M10.5 5.5v-2a1.5 1.5 0 0 0-1.5-1.5H4.5A1.5 1.5 0 0 0 3 3.5v4A1.5 1.5 0 0 0 4.5 9h1',
|
|
109
|
-
stroke: 'currentColor', strokeWidth: 1.4, strokeLinecap: 'round',
|
|
110
|
-
}),
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** 下拉箭头图标:向下 V 形(stroke 继承 currentColor),下拉框触发器右侧提示可展开。 */
|
|
115
|
-
export function ChevronDownIcon() {
|
|
116
|
-
return h('svg', {
|
|
117
|
-
viewBox: '0 0 16 16',
|
|
118
|
-
width: 12,
|
|
119
|
-
height: 12,
|
|
120
|
-
fill: 'none',
|
|
121
|
-
'aria-hidden': 'true',
|
|
122
|
-
}, h('path', {
|
|
123
|
-
d: 'M3.5 5.5L8 10l4.5-4.5',
|
|
124
|
-
stroke: 'currentColor',
|
|
125
|
-
strokeWidth: 1.6,
|
|
126
|
-
strokeLinecap: 'round',
|
|
127
|
-
strokeLinejoin: 'round',
|
|
128
|
-
}))
|
|
129
|
-
}
|
|
@@ -1,293 +0,0 @@
|
|
|
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
|
-
* Catalog constants and normalizers: site URLs, category metadata, sort keys
|
|
7
|
-
* and the mapping from the online API's short-key payloads to HubPlugin.
|
|
8
|
-
*/
|
|
9
|
-
import type { EnvInfo, HubPlugin, LocaleId } from '../types.ts'
|
|
10
|
-
import { MAX_CORE_CHARS, classifyFailure, coreErrorCode, summarizeError } from './failures.ts'
|
|
11
|
-
|
|
12
|
-
/** 插件中心官网地址。 */
|
|
13
|
-
export const SITE_URL = 'https://dsh-plugin.org/'
|
|
14
|
-
|
|
15
|
-
/** 插件中心源码仓库:头部右上角 GitHub 图标的跳转地址 */
|
|
16
|
-
export const GITHUB_URL = 'https://github.com/dshplugin/dsh-plugin-hub'
|
|
17
|
-
|
|
18
|
-
/** Hub 自身在目录数据里的仓库标识:头部版本号旁的「可更新」徽标按它定位自己。 */
|
|
19
|
-
export const HUB_REPO = 'dshplugin/dsh-plugin-hub'
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Hub 自我更新版本控制中心(Cloudflare Worker:dsh-update)。
|
|
23
|
-
* 发新版后向 Worker 管理接口写入最新版本 + Markdown 变更记录(见
|
|
24
|
-
* dsh-update-worker 的 POST /admin/hub),所有已装用户的「可更新」徽标即时出现,
|
|
25
|
-
* 不再依赖主站目录数据管道(每日刷新 + 重建站点)。
|
|
26
|
-
* 响应:{ version: string | null, publishedAt: string | null, notes: string | {zh,en} | null }
|
|
27
|
-
*/
|
|
28
|
-
export const HUB_UPDATE_URL = 'https://hub.dsh-plugin.org/hub'
|
|
29
|
-
|
|
30
|
-
/** Worker 版本控制中心返回的 Hub 自我更新信息(notes 为 Markdown 变更记录)。 */
|
|
31
|
-
export interface HubUpdateInfo {
|
|
32
|
-
version: string
|
|
33
|
-
publishedAt?: string | null
|
|
34
|
-
/** Markdown 变更记录;可传字符串,或 { zh, en } 双语言对象按界面语言取 */
|
|
35
|
-
notes?: string | Record<string, string> | null
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Worker「关注我们」内容接口(同款 Worker,key = hub:about,Markdown 推送非写死)。 */
|
|
39
|
-
export const HUB_ABOUT_URL = 'https://hub.dsh-plugin.org/about'
|
|
40
|
-
|
|
41
|
-
/** Worker 返回的「关注我们」内容(content 为 Markdown,可字符串或 {zh,en} 双语言对象)。 */
|
|
42
|
-
export interface HubAboutInfo {
|
|
43
|
-
/** Markdown 平台介绍 + 反馈群二维码;可传字符串,或 { zh, en } 双语言对象按界面语言取 */
|
|
44
|
-
content?: string | Record<string, string> | null
|
|
45
|
-
updatedAt?: string | null
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** 构建时由 tsdown 从 package.json 注入的插件版本号(见 tsdown.config.ts define)。 */
|
|
49
|
-
declare const __PLUGIN_VERSION__: string
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* 插件当前版本号,供头部标题展示「DSH-Plugin Hub v0.1.1」。
|
|
53
|
-
* tsdown 构建时用 define 把 __PLUGIN_VERSION__ 替换成 package.json 的版本号;
|
|
54
|
-
* node --test 直接 import 本模块时该标识符不存在,typeof 守卫兜底为空串。
|
|
55
|
-
*/
|
|
56
|
-
export const PLUGIN_VERSION: string =
|
|
57
|
-
typeof __PLUGIN_VERSION__ === 'string' ? __PLUGIN_VERSION__ : ''
|
|
58
|
-
|
|
59
|
-
/** dsh-plugin.org 中文页挂在 /zh/ 前缀下,英文在根路径。 */
|
|
60
|
-
export function langPathOf(lang: LocaleId): string {
|
|
61
|
-
return lang === 'zh' ? 'zh/' : ''
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* 官网详情页两级路径:/plugins/{ownerSlug}/{slug};
|
|
66
|
-
* 缺 ownerSlug 时从 repo 推导(卡片详情按钮与弹窗来源行共用)。
|
|
67
|
-
*/
|
|
68
|
-
export function pluginDetailUrl(plugin: HubPlugin, langPath: string): string {
|
|
69
|
-
const repo = plugin.source?.repo ?? ''
|
|
70
|
-
return `${SITE_URL}${langPath}plugins/${plugin.ownerSlug ?? repo.split('/')[0]?.toLowerCase() ?? ''}/${plugin.slug}`
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* 失败弹窗里仓库地址的跳转目标:官网详情页(含插件收录信息),
|
|
75
|
-
* 与 issue 正文的 Catalog 链接同一形式(单级 /plugins/{repo})。
|
|
76
|
-
*/
|
|
77
|
-
export function pluginSiteUrl(repo: string): string {
|
|
78
|
-
return `${SITE_URL}plugins/${repo}`
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 一键反馈 GitHub Issue 的预填链接:标题带「来自 dsh-plugin.org」标识,正文只带
|
|
83
|
-
* 核心信息 —— 原因判定 + 关键错误代码 + 尝试过的安装方式 + 宿主机器环境快照 +
|
|
84
|
-
* 错误核心摘要(完整日志太长,塞进 URL 会被 GitHub 以「request URL too long」拒绝,
|
|
85
|
-
* 故只收集重点)。错误弹窗、失败记录共用此逻辑。
|
|
86
|
-
* env 取不到时为 null,链接照常生成、只是少环境段。
|
|
87
|
-
* attempts(尝试过的安装方式,npm 反查 + 实际执行命令):作者看到我们查过/试过的命令,
|
|
88
|
-
* 组织 scope 与 GitHub 用户名不一致时也能直接指认正确的 npm 包名。
|
|
89
|
-
*/
|
|
90
|
-
export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | null, command?: string, attempts?: string[]): string {
|
|
91
|
-
const title = `[dsh-plugin.org | dsh-plugin-hub] Install/Remove failed: ${repo}`
|
|
92
|
-
// 原因判定:让作者一眼知道这是不是自己插件的问题。
|
|
93
|
-
// [packaging](预检/装后校验拦截)单独列:无论走 npm 还是 git 通道,都是分发物不完整
|
|
94
|
-
// (package.json 声明的入口文件在发布物里缺失),作者照此补齐即可。
|
|
95
|
-
const kind = classifyFailure(message)
|
|
96
|
-
const reason = /\[packaging\]/i.test(message)
|
|
97
|
-
? 'plugin distribution is incomplete — the entry file declared in package.json is missing from the published package (github tarball or npm package); please commit build output or publish a complete package'
|
|
98
|
-
: kind === 'pluginPrepare'
|
|
99
|
-
? 'plugin prepare/build script failed during install (packaging/distribution issue)'
|
|
100
|
-
: kind === 'pnpmIgnoredBuild'
|
|
101
|
-
? 'plugin depends on a native module whose build script pnpm blocks by default (use a prebuilt variant)'
|
|
102
|
-
: 'plugin-side install failure'
|
|
103
|
-
const code = coreErrorCode(message)
|
|
104
|
-
// 按给定核心摘要预算构建完整预填 URL(含 URL 编码);预算可调,供超长时逐档缩小
|
|
105
|
-
const build = (coreChars: number): string => {
|
|
106
|
-
const body = [
|
|
107
|
-
'## Summary',
|
|
108
|
-
`- Cause: ${reason}`,
|
|
109
|
-
...(code ? [`- Key error: \`${code}\``] : []),
|
|
110
|
-
'',
|
|
111
|
-
// 来源说明:标题(链官网)+ 一句来源(链仓库)+ 实际执行的安装命令(含 --profile)与执行结果,链接由常量动态拼接
|
|
112
|
-
`## [DSH-Plugin 插件中心](${SITE_URL}) · 安装 Plugin 失败错误信息`,
|
|
113
|
-
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件中心的安装程序自动生成,随本次安装失败一并提交。`,
|
|
114
|
-
`- 实际执行的安装命令:\`${command ?? `dsh plugin${env?.profile ? ` --profile ${env.profile}` : ''} add git+https://github.com/${repo}.git`}\``,
|
|
115
|
-
`- 执行结果:安装失败,未能安装该插件。`,
|
|
116
|
-
// 尝试过的安装方式(npm 反查 + 实际执行命令,按先后顺序):作者据此反推正确的
|
|
117
|
-
// npm 包名 —— 组织 scope 与 GitHub 用户名不一致时仅凭仓库名猜不到,作者看到我们
|
|
118
|
-
// 查过/试过的命令就能直接指认
|
|
119
|
-
...(attempts && attempts.length > 0
|
|
120
|
-
? ['', '## Attempted install channels(已尝试的安装方式)', ...attempts.map((a) => `- ${a}`)]
|
|
121
|
-
: []),
|
|
122
|
-
// [packaging] 场景:说明两种官方安装通道(npm 包 / git 直装),当前插件在对应通道下分发不完整
|
|
123
|
-
...(/\[packaging\]/i.test(message)
|
|
124
|
-
? [
|
|
125
|
-
'',
|
|
126
|
-
'## 安装方式说明',
|
|
127
|
-
'DSH 插件支持两种官方安装通道:`dsh plugin add <npm-package>`(npm 分发,需发布完整构建产物)与 `dsh plugin add git+https://github.com/owner/repo.git`(Git 直装,仓库需提交构建产物或在 package.json 提供 `prepare` 脚本)。当前插件的分发物缺少 package.json 声明的入口文件,请按所用通道补齐后重新发布。',
|
|
128
|
-
]
|
|
129
|
-
: []),
|
|
130
|
-
'',
|
|
131
|
-
'## Environment',
|
|
132
|
-
`- Plugin: \`${repo}\``,
|
|
133
|
-
// 宿主机器环境快照:便于作者本地复现;某字段缺失时如实写 unknown,不编造
|
|
134
|
-
...(env
|
|
135
|
-
? [
|
|
136
|
-
`- DSH: ${env.dshVersion ? `v${env.dshVersion}` : 'unknown'}`,
|
|
137
|
-
`- Plugin Hub: v${PLUGIN_VERSION}`,
|
|
138
|
-
`- Node: ${env.nodeVersion}`,
|
|
139
|
-
`- OS: ${env.platform} ${env.arch} (${env.release})`,
|
|
140
|
-
`- Profile: \`${env.profile}\``,
|
|
141
|
-
`- DSH Home: \`${env.dshHome}\``,
|
|
142
|
-
]
|
|
143
|
-
: []),
|
|
144
|
-
// 该插件在插件中心的收录位置(详情页链接)
|
|
145
|
-
`- Catalog: [${pluginSiteUrl(repo)}](${pluginSiteUrl(repo)})`,
|
|
146
|
-
'',
|
|
147
|
-
'## Error (core)',
|
|
148
|
-
'',
|
|
149
|
-
'```',
|
|
150
|
-
// 核心错误收集器:只挑关键行(错误代码/构建失败/退出信息),去重截断,URL 才不至于超长
|
|
151
|
-
summarizeError(message, coreChars),
|
|
152
|
-
'```',
|
|
153
|
-
'',
|
|
154
|
-
'---',
|
|
155
|
-
// 底部署名:报错来自内置在 DeepSeek Harness 里的 dsh-plugin-hub 安装程序
|
|
156
|
-
`This error was produced by the [dsh-plugin-hub](${GITHUB_URL}) installer bundled with DeepSeek Harness.`,
|
|
157
|
-
].join('\n')
|
|
158
|
-
return `https://github.com/${repo}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`
|
|
159
|
-
}
|
|
160
|
-
// GitHub 请求行上限 8192 字节:先用宽松预算(最多 ~5K 核心错误)构建,超长再逐档缩小,保证最终 URL 不超限
|
|
161
|
-
const MAX_URL_CHARS = 7600
|
|
162
|
-
let url = build(MAX_CORE_CHARS)
|
|
163
|
-
if (url.length > MAX_URL_CHARS) {
|
|
164
|
-
for (const budget of [4000, 2500, 1400, 800, 400]) {
|
|
165
|
-
url = build(budget)
|
|
166
|
-
if (url.length <= MAX_URL_CHARS) break
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return url
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export const CATEGORY_ORDER = [
|
|
173
|
-
'interface', 'session', 'memory', 'tools', 'agent', 'workflow',
|
|
174
|
-
'integration', 'model', 'dev', 'knowledge', 'fun',
|
|
175
|
-
]
|
|
176
|
-
|
|
177
|
-
/** 分类标签按界面语言取词;未知 key 原样返回。 */
|
|
178
|
-
export function categoryLabel(
|
|
179
|
-
map: Record<string, { zh: string; en: string }>,
|
|
180
|
-
key: string,
|
|
181
|
-
lang: LocaleId,
|
|
182
|
-
): string {
|
|
183
|
-
return map[key]?.[lang] ?? key
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* 归一化在线 API(dsh-plugin.org/api/plugins.{lang}.json)返回的短 key 结构
|
|
188
|
-
* (s/o/n/vr/c/t/f/d/r/v/u/a/sg/fk),统一为 HubPlugin,保证渲染逻辑只认一种结构。
|
|
189
|
-
*/
|
|
190
|
-
export function normalize(raw: Record<string, unknown>): HubPlugin {
|
|
191
|
-
if (typeof raw.s === 'string') {
|
|
192
|
-
return {
|
|
193
|
-
slug: raw.s,
|
|
194
|
-
ownerSlug: typeof raw.o === 'string' ? raw.o : undefined,
|
|
195
|
-
displayName: typeof raw.n === 'string' ? raw.n : undefined,
|
|
196
|
-
version: typeof raw.vr === 'string' ? raw.vr : undefined,
|
|
197
|
-
category: typeof raw.c === 'string' ? raw.c : undefined,
|
|
198
|
-
topics: Array.isArray(raw.t) ? (raw.t as string[]) : undefined,
|
|
199
|
-
features: Array.isArray(raw.f) ? (raw.f as string[]) : undefined,
|
|
200
|
-
description: typeof raw.d === 'string' ? raw.d : undefined,
|
|
201
|
-
// source 双形态:字符串 repo,或 { repo, npmPackage } 对象
|
|
202
|
-
source: typeof raw.r === 'string'
|
|
203
|
-
? { repo: raw.r }
|
|
204
|
-
: raw.r !== null && typeof raw.r === 'object'
|
|
205
|
-
? {
|
|
206
|
-
repo: typeof (raw.r as { repo?: unknown }).repo === 'string' ? (raw.r as { repo: string }).repo : undefined,
|
|
207
|
-
npmPackage: typeof (raw.r as { npmPackage?: unknown }).npmPackage === 'string' ? (raw.r as { npmPackage: string }).npmPackage : undefined,
|
|
208
|
-
}
|
|
209
|
-
: undefined,
|
|
210
|
-
// 能否网页一键安装:API 仅在 false 时下发 wi(缺省视为 true),据此禁用一键安装、只提示命令行
|
|
211
|
-
install: raw.wi === false ? { webInstallable: false } : undefined,
|
|
212
|
-
compatibility: typeof raw.v === 'string' ? { status: raw.v } : undefined,
|
|
213
|
-
dates: {
|
|
214
|
-
repoUpdatedAt: typeof raw.u === 'string' ? raw.u : undefined,
|
|
215
|
-
addedAt: typeof raw.a === 'string' ? raw.a : undefined,
|
|
216
|
-
},
|
|
217
|
-
stats: {
|
|
218
|
-
stargazers_count: typeof raw.sg === 'number' ? raw.sg : undefined,
|
|
219
|
-
forks_count: typeof raw.fk === 'number' ? raw.fk : undefined,
|
|
220
|
-
},
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return raw as unknown as HubPlugin
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* 安装通道决策(用户无感知):目录探测到 npm 包名 → 用 npm 包名安装
|
|
228
|
-
* (走 npm registry tarball,更快、与 GitHub 网络无关);无 npm 包名 → git 直装。
|
|
229
|
-
* 返回值 target 即传给后端 /install 的安装目标(npm 包名 或 owner/repo)。
|
|
230
|
-
*/
|
|
231
|
-
export function installTargetOf(p: HubPlugin): { target: string; via: 'npm' | 'github' } {
|
|
232
|
-
const pkg = (p.source?.npmPackage ?? '').trim()
|
|
233
|
-
const repo = (p.source?.repo ?? '').trim()
|
|
234
|
-
if (pkg && repo) return { target: pkg, via: 'npm' }
|
|
235
|
-
return { target: repo, via: 'github' }
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** 展示用安装命令(复制/弹窗):npm 通道显示包名,git 通道显示显式 HTTPS URL。 */
|
|
239
|
-
export function installCommandOf(p: HubPlugin, withProfile = false): string {
|
|
240
|
-
const { target, via } = installTargetOf(p)
|
|
241
|
-
return via === 'npm'
|
|
242
|
-
? `dsh plugin${withProfile ? ' --profile web' : ''} add ${target}`
|
|
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
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
export const CATEGORY_LABELS: Record<string, { zh: string; en: string }> = {
|
|
264
|
-
interface: { zh: '界面与体验', en: 'UI & Experience' },
|
|
265
|
-
session: { zh: '会话与消息', en: 'Sessions & Messages' },
|
|
266
|
-
memory: { zh: '记忆与上下文', en: 'Memory & Context' },
|
|
267
|
-
tools: { zh: '工具与能力', en: 'Tools & Capabilities' },
|
|
268
|
-
agent: { zh: '技能与智能体', en: 'Skills & Agents' },
|
|
269
|
-
workflow: { zh: '工作流与自动化', en: 'Workflow & Automation' },
|
|
270
|
-
integration: { zh: '集成与连接', en: 'Integrations & Connections' },
|
|
271
|
-
model: { zh: '模型与推理', en: 'Models & Reasoning' },
|
|
272
|
-
dev: { zh: '开发与运维', en: 'Development & Operations' },
|
|
273
|
-
knowledge: { zh: '数据与知识', en: 'Data & Knowledge' },
|
|
274
|
-
fun: { zh: '娱乐', en: 'Entertainment' },
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/** 分类 Tab 的紧凑标签(英文模式空间受限时使用)。 */
|
|
278
|
-
export const CATEGORY_SHORT_LABELS: Record<string, { zh: string; en: string }> = {
|
|
279
|
-
interface: { zh: '界面体验', en: 'UI Exp' },
|
|
280
|
-
session: { zh: '会话消息', en: 'Sessions' },
|
|
281
|
-
memory: { zh: '记忆上下文', en: 'Memory' },
|
|
282
|
-
tools: { zh: '工具能力', en: 'Tools' },
|
|
283
|
-
agent: { zh: '技能智能体', en: 'Agents' },
|
|
284
|
-
workflow: { zh: '工作流', en: 'Workflow' },
|
|
285
|
-
integration: { zh: '集成连接', en: 'Integrations' },
|
|
286
|
-
model: { zh: '模型推理', en: 'Models' },
|
|
287
|
-
dev: { zh: '开发运维', en: 'Dev' },
|
|
288
|
-
knowledge: { zh: '数据知识', en: 'Knowledge' },
|
|
289
|
-
fun: { zh: '娱乐', en: 'Fun' },
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export const SORTS = ['sortStars', 'sortForks', 'sortUpdated', 'sortNewest'] as const
|
|
293
|
-
export type SortKey = (typeof SORTS)[number]
|
package/src/client/lib/env.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
* One-shot host environment snapshot for bug reports. Fetched lazily and
|
|
7
|
-
* cached at module level: issue URLs embed this info, and the environment
|
|
8
|
-
* does not change during a session. A failed fetch degrades to null so the
|
|
9
|
-
* issue links still work — they just ship without the environment block.
|
|
10
|
-
*/
|
|
11
|
-
import type { EnvInfo } from '../types.ts'
|
|
12
|
-
|
|
13
|
-
let envPromise: Promise<EnvInfo | null> | null = null
|
|
14
|
-
|
|
15
|
-
export function getEnv(): Promise<EnvInfo | null> {
|
|
16
|
-
envPromise ??= fetch('/dsh-plugin-hub/env', { cache: 'no-store' })
|
|
17
|
-
.then((res) => (res.ok ? (res.json() as Promise<EnvInfo>) : null))
|
|
18
|
-
.catch(() => null)
|
|
19
|
-
return envPromise
|
|
20
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|