dsh-plugin 1.6.12 → 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 +615 -741
- 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 +2 -2
- package/lib/services/install/release-target.js +8 -9
- package/lib/services/install/task-queue.js +40 -48
- package/lib/services/npm-release-age.js +119 -0
- package/lib/services/probe.js +4 -4
- package/lib/services/profile/installed-versions.js +15 -21
- package/lib/services/profile/profile.js +16 -44
- package/lib/services/profile/progress.js +8 -11
- package/lib/services/settings.js +2 -5
- package/lib/types/http/catalog.d.ts +1 -1
- package/lib/types/http/install.d.ts +2 -2
- package/lib/types/http/npm-release-age.d.ts +2 -0
- package/lib/types/http/routes.d.ts +10 -12
- package/lib/types/http/shared.d.ts +5 -6
- package/lib/types/index.d.ts +7 -8
- package/lib/types/services/install/install-types.d.ts +12 -13
- package/lib/types/services/install/install.d.ts +1 -1
- package/lib/types/services/install/npm-resolve.d.ts +4 -16
- package/lib/types/services/install/package-entry.d.ts +6 -11
- package/lib/types/services/install/release-target.d.ts +7 -8
- package/lib/types/services/install/task-queue.d.ts +25 -30
- package/lib/types/services/npm-release-age.d.ts +15 -0
- package/lib/types/services/probe.d.ts +4 -4
- package/lib/types/services/profile/installed-versions.d.ts +5 -7
- package/lib/types/services/profile/profile.d.ts +14 -18
- package/lib/types/services/profile/progress.d.ts +7 -10
- package/lib/types/services/settings.d.ts +0 -4
- package/package.json +1 -1
- package/src/client/components/PluginHubSection.tsx +17 -22
- package/src/client/components/catalog/CatalogList.tsx +2 -4
- package/src/client/components/catalog/PluginCard.tsx +2 -3
- package/src/client/components/layout/CatalogControls.tsx +5 -6
- package/src/client/components/layout/CatalogHeader.tsx +1 -2
- package/src/client/components/layout/CategoryTabs.tsx +2 -3
- package/src/client/components/layout/SectionTabs.tsx +3 -5
- package/src/client/components/modals/ConfirmDialog.tsx +4 -6
- package/src/client/components/modals/InstalledDetailModal.tsx +3 -6
- package/src/client/components/modals/LogsModal.tsx +7 -12
- package/src/client/components/modals/NotificationsModal.tsx +35 -16
- package/src/client/components/modals/ProgressView.tsx +3 -4
- package/src/client/components/modals/modals.tsx +35 -15
- 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 +75 -114
- package/src/client/logic/constants.ts +1 -2
- package/src/client/logic/ensure-plugin-css.ts +8 -12
- package/src/client/logic/failures.ts +16 -18
- 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 +20 -20
- 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 +2 -2
- package/src/server/services/install/release-target.ts +8 -9
- package/src/server/services/install/task-queue.ts +47 -54
- package/src/server/services/npm-release-age.ts +107 -0
- package/src/server/services/probe.ts +4 -4
- package/src/server/services/profile/installed-versions.ts +16 -22
- package/src/server/services/profile/profile.ts +16 -42
- package/src/server/services/profile/progress.ts +8 -11
- package/src/server/services/settings.ts +2 -9
- package/lib/services/install/preflight.js +0 -178
- package/lib/types/services/install/preflight.d.ts +0 -23
- package/src/server/services/install/preflight.ts +0 -184
package/lib/http/catalog.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* and profile-level installed-package reads shared with the install/task modules.
|
|
6
|
+
* 目录代理路由:目录/统计载荷的服务端缓存,以及与安装/任务模块共享的 profile 级已装包读取。
|
|
8
7
|
*/
|
|
9
8
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
9
|
import { homedir } from 'node:os';
|
|
@@ -44,7 +43,7 @@ function writeCatalogCache(file, body) {
|
|
|
44
43
|
export function profileDirectory(profile) {
|
|
45
44
|
return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'profiles', profile);
|
|
46
45
|
}
|
|
47
|
-
/**
|
|
46
|
+
/** 读取安装进某 profile 的非官方依赖。 */
|
|
48
47
|
export function readInstalled(profile) {
|
|
49
48
|
try {
|
|
50
49
|
const manifest = JSON.parse(readFileSync(join(profileDirectory(profile), 'package.json'), 'utf8'));
|
package/lib/http/diagnostics.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* and the host environment snapshot shared with the task module's /env route.
|
|
6
|
+
* 诊断路由:流式连通性自检、代理校验,以及与任务模块 /env 路由共享的宿主环境快照。
|
|
8
7
|
*/
|
|
9
8
|
import { readFileSync } from 'node:fs';
|
|
10
9
|
import { homedir, release } from 'node:os';
|
|
@@ -33,7 +32,7 @@ function hostDshVersion() {
|
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
catch {
|
|
36
|
-
/*
|
|
35
|
+
/* 不是包根,继续向上查找 */
|
|
37
36
|
}
|
|
38
37
|
const parent = dirname(dir);
|
|
39
38
|
if (parent === dir)
|
|
@@ -98,8 +97,8 @@ export function registerDiagnosticsRoutes(webServer, profile) {
|
|
|
98
97
|
handler: async (request, response) => {
|
|
99
98
|
if (!requireTrustedPost(request, response))
|
|
100
99
|
return;
|
|
101
|
-
// 连通性自检:npm registry(镜像/官方)/ GitHub
|
|
102
|
-
//
|
|
100
|
+
// 连通性自检:npm registry(镜像/官方)/ GitHub / 目录站点,逐路探测并测速。
|
|
101
|
+
// npm 安装走 registry、目录数据走 dsh-plugin.org、GitHub 那一行用于反映插件运行时对 github.com 的可达性。
|
|
103
102
|
// 流式(NDJSON)逐条上报:每个探测先发命令行,完成后发结果行,客户端以黑窗口终端实时展示。
|
|
104
103
|
// 可选 body { key }:只重测指定通道(页面列表「重测」单项时用),缺省全量串行探测。
|
|
105
104
|
let onlyKey = '';
|
|
@@ -116,8 +115,8 @@ export function registerDiagnosticsRoutes(webServer, profile) {
|
|
|
116
115
|
// npm 通道:设置里配置了镜像源就探测该镜像(安装通道吃同一个 registry);
|
|
117
116
|
// 未配置(空串)时探测官方源作为「跟随本机 npm 配置」的基础连通性参考。
|
|
118
117
|
const registry = settings.npmRegistry.replace(/\/+$/, '') || 'https://registry.npmjs.org';
|
|
119
|
-
//
|
|
120
|
-
//
|
|
118
|
+
// 连通性自检打各通道的轻量资源:npm 源拉包元数据、GitHub 行探小仓库、目录站探
|
|
119
|
+
// 小文件,避免拉取目录全量 JSON。
|
|
121
120
|
// npm 行 display:配置了镜像就显示镜像地址;未配置置空 —— 客户端显示「未配置(跟随本机 npm 配置)」。
|
|
122
121
|
const allChecks = [
|
|
123
122
|
{ key: 'npm', url: `${registry}/dsh-plugin`, display: settings.npmRegistry !== '' ? registry : '', cmd: `npm view dsh-plugin version --registry ${registry}` },
|
package/lib/http/install.js
CHANGED
|
@@ -3,22 +3,18 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* 安装/卸载路由:校验目标后 spawn 官方 dsh CLI(见 services/install/install.ts),
|
|
7
|
+
* 并回传捕获的结果。自定义安装只接受 npm 包名。
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import { githubReleaseTarget } from '../services/install/release-target.js';
|
|
9
|
+
import { githubRepoOf, globalNpmPackagesOf, hasQueuedTarget, installTargetOf, startPluginMutation, validPackageName } from '../services/install/install.js';
|
|
11
10
|
import { recordResolvedNpmPackage } from '../services/profile/installed-versions.js';
|
|
12
|
-
import { isDshNpmPackageForRepo
|
|
13
|
-
import {
|
|
14
|
-
import { gitLsRemote, probeUrl, systemProxy } from '../services/probe.js';
|
|
11
|
+
import { isDshNpmPackageForRepo } from '../services/install/npm-resolve.js';
|
|
12
|
+
import { probeUrl, systemProxy } from '../services/probe.js';
|
|
15
13
|
import { loadSettings } from '../services/settings.js';
|
|
16
|
-
import {
|
|
14
|
+
import { readInstalled } from './catalog.js';
|
|
17
15
|
import { COMMAND_TIMEOUT_MS, readJsonBody, requireTrustedPost, sendJson } from './shared.js';
|
|
18
|
-
/** 安装/卸载 CLI
|
|
19
|
-
*
|
|
20
|
-
* 代理优先级:设置里的代理 → 系统代理(macOS scutil / Windows 注册表)→ 宿主 env 原有值。
|
|
21
|
-
* 使安装通道与诊断使用相同的代理来源。 */
|
|
16
|
+
/** 安装/卸载 CLI 的子进程环境:透传宿主环境并注入 HTTP(S) 代理,
|
|
17
|
+
* 代理来源与诊断一致(设置代理 → 系统代理 → 宿主 env);不注入 npm registry,沿用用户本机 npm 配置。 */
|
|
22
18
|
function mutationEnv(settings) {
|
|
23
19
|
const env = { ...process.env, CI: 'true' };
|
|
24
20
|
const proxy = settings.proxy !== '' ? settings.proxy : (systemProxy() ?? '');
|
|
@@ -30,18 +26,6 @@ function mutationEnv(settings) {
|
|
|
30
26
|
}
|
|
31
27
|
return env;
|
|
32
28
|
}
|
|
33
|
-
/** Match a GitHub install target against the installed table; returns the package name when already installed. */
|
|
34
|
-
function installedByRepo(installed, target) {
|
|
35
|
-
const repo = githubRepoOf(target);
|
|
36
|
-
if (repo === null)
|
|
37
|
-
return null;
|
|
38
|
-
const needle = repo.toLowerCase();
|
|
39
|
-
for (const [name, spec] of Object.entries(installed)) {
|
|
40
|
-
if (githubRepoOf(spec)?.toLowerCase() === needle)
|
|
41
|
-
return name;
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
29
|
export function registerInstallRoutes(webServer, profile, loader) {
|
|
46
30
|
return [
|
|
47
31
|
{
|
|
@@ -54,43 +38,37 @@ export function registerInstallRoutes(webServer, profile, loader) {
|
|
|
54
38
|
try {
|
|
55
39
|
const body = await readJsonBody(request);
|
|
56
40
|
// mode: 'update' = 已安装目标的覆盖更新(放行 add,pnpm 对已存在依赖原位覆盖重装)。
|
|
57
|
-
// 后续「同仓库残留」检测可能把 install 就地升级为 update(见包名冲突检测),故用 let。
|
|
58
41
|
let mode = body !== null && typeof body === 'object' && body.mode === 'update'
|
|
59
42
|
? 'update'
|
|
60
43
|
: 'install';
|
|
61
|
-
// installTargetOf
|
|
62
|
-
// 兼容用户直接粘贴命令行(防输错),真正语法判定交给下面 githubRepoOf / validPackageName。
|
|
44
|
+
// installTargetOf 把完整 DSH 命令剥成裸目标;自定义目标只接受 npm 包名,语法判定交给 validPackageName。
|
|
63
45
|
const rawRepo = typeof body === 'object' && body !== null && typeof body.repo === 'string'
|
|
64
46
|
? installTargetOf(body.repo)
|
|
65
47
|
: '';
|
|
66
|
-
//
|
|
67
|
-
// 待重启行 / 任务恢复时展示仓库名,保证用户无感知(安装通道变化不改变所见目标)
|
|
48
|
+
// 展示用仓库名(owner/repo):npm 安装时 body.repo 是包名,display 供待重启行 / 任务恢复展示仓库名。
|
|
68
49
|
const displayRepo = typeof body === 'object' && body !== null && typeof body.display === 'string'
|
|
69
50
|
? body.display.trim()
|
|
70
51
|
: rawRepo;
|
|
71
|
-
// 请求来源:目录插件安装(catalog)走目录白名单;命令行安装(custom
|
|
52
|
+
// 请求来源:目录插件安装(catalog)走目录白名单;命令行安装(custom)受安全开关控制
|
|
72
53
|
const source = typeof body === 'object' && body !== null && typeof body.source === 'string'
|
|
73
54
|
? body.source
|
|
74
55
|
: '';
|
|
75
|
-
//
|
|
76
|
-
// 客户端显式上报,服务端日志/报错据此精准溯源「从哪个入口发起」;目录插件安装不传。
|
|
56
|
+
// 自定义安装入口渠道(仅 NPM 包):日志/报错据此溯源发起入口。
|
|
77
57
|
const installChannel = (() => {
|
|
78
58
|
const raw = body !== null && typeof body === 'object' ? body.installChannel : undefined;
|
|
79
|
-
return raw === '
|
|
59
|
+
return raw === 'npm' ? 'npm' : undefined;
|
|
80
60
|
})();
|
|
81
61
|
const settings = loadSettings(profile);
|
|
82
62
|
// 界面语言(客户端随请求携带):错误消息按用户当前语言提示(中文界面给中文,英文界面给英文)
|
|
83
63
|
const lang = typeof body === 'object' && body !== null && body.lang === 'en'
|
|
84
64
|
? 'en'
|
|
85
65
|
: 'zh';
|
|
86
|
-
// 全局 npm
|
|
87
|
-
//
|
|
88
|
-
// 包列表优先取 body.globalNpm(客户端命令框解析后显式携带);兜底再从 rawRepo 解析,
|
|
89
|
-
// 保证直接调 API 传完整命令也能识别。
|
|
66
|
+
// 全局 npm 安装(`npm install -g <pkgs>`):不进任何 profile、无需宿主重启。
|
|
67
|
+
// 包列表优先取 body.globalNpm,兜底从 rawRepo 解析,保证直接调 API 传完整命令也能识别。
|
|
90
68
|
const bodyGlobalNpm = Array.isArray(body.globalNpm)
|
|
91
69
|
? body.globalNpm.filter((x) => typeof x === 'string' && x !== '')
|
|
92
70
|
: [];
|
|
93
|
-
|
|
71
|
+
const globalNpm = bodyGlobalNpm.length > 0 ? bodyGlobalNpm : (globalNpmPackagesOf(rawRepo) ?? []);
|
|
94
72
|
if (globalNpm.length > 0) {
|
|
95
73
|
// 包名逐个按 npm 包名语法校验 + 拒绝以 - 开头的 token(npm 会把 --xxx 当参数而非包名),数量封顶防滥用
|
|
96
74
|
if (globalNpm.length > 20 || globalNpm.some((p) => !validPackageName(p) || p.startsWith('-'))) {
|
|
@@ -122,99 +100,35 @@ export function registerInstallRoutes(webServer, profile, loader) {
|
|
|
122
100
|
sendJson(response, 200, { ok: true, task: task.id });
|
|
123
101
|
return;
|
|
124
102
|
}
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// githubRepoOf 先把各种 GitHub 地址归一成 owner/repo,让「输入地址即装」兼容粘贴完整链接。
|
|
129
|
-
const gitRepo = githubRepoOf(rawRepo);
|
|
130
|
-
const release = githubReleaseTarget(rawRepo);
|
|
131
|
-
// 显式 pin 的 Git ref(`#tag` / `#commit`)是用户意图:保留它,且不让 npm 反查
|
|
132
|
-
// 把这个精确修订换成 registry 上的默认版本。
|
|
133
|
-
const pinnedRef = githubRefOf(rawRepo);
|
|
134
|
-
const repoTarget = gitRepo !== null && release === null ? githubTarget(gitRepo, pinnedRef) : null;
|
|
135
|
-
let target = release?.target ?? repoTarget ?? (validPackageName(rawRepo) ? rawRepo : null);
|
|
136
|
-
if (target === null) {
|
|
137
|
-
sendJson(response, 400, { error: 'unsupported install target' });
|
|
103
|
+
// 自定义安装目标只接受 npm 包名(@scope/name 或 name)。
|
|
104
|
+
if (!validPackageName(rawRepo)) {
|
|
105
|
+
sendJson(response, 400, { error: 'unsupported install target: expected an npm package name' });
|
|
138
106
|
return;
|
|
139
107
|
}
|
|
108
|
+
const target = rawRepo;
|
|
109
|
+
// 已尝试的安装方式(供失败提 Issue 预填):保持为空
|
|
140
110
|
const attempts = [];
|
|
141
111
|
// 代理口径与诊断/安装通道一致:设置里的代理 → 系统代理 → 环境变量 → 直连。
|
|
142
|
-
// 供 npm 反查时跟随 GitHub 改名跳转用(Node https 读不到系统代理,交给 curl)。
|
|
143
112
|
const effectiveProxy = settings.proxy !== ''
|
|
144
113
|
? settings.proxy
|
|
145
114
|
: (systemProxy() ?? process.env.HTTPS_PROXY ?? process.env.https_proxy ?? '');
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
// registry metadata confirms it is a DSH package for this repo. Otherwise inspect the
|
|
149
|
-
// repository root package.json and use its package name only when that exact registry
|
|
150
|
-
// package also points back to the repo and identifies as a DSH plugin. This avoids
|
|
151
|
-
// relying on npm search ranking, which is not a repository index and can omit the
|
|
152
|
-
// correct package even with a large result window.
|
|
115
|
+
// 目录插件安装:目录数据可能滞后于打包拆分,确认目录下发的 npm 目标确为该仓库的 DSH 包,
|
|
116
|
+
// 命中则记录 repo → 包名映射,客户端据此把依赖 key 匹配回仓库、列表立即显示「已安装」。
|
|
153
117
|
const catalogRepo = source === 'catalog' ? githubRepoOf(displayRepo) : null;
|
|
154
|
-
if (catalogRepo !== null
|
|
118
|
+
if (catalogRepo !== null) {
|
|
155
119
|
const catalogTargetIsDsh = await isDshNpmPackageForRepo(rawRepo, catalogRepo, settings.npmRegistry, effectiveProxy);
|
|
156
|
-
if (catalogTargetIsDsh)
|
|
120
|
+
if (catalogTargetIsDsh)
|
|
157
121
|
recordResolvedNpmPackage(profile, catalogRepo, rawRepo);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
const catalogGitTarget = githubTarget(catalogRepo);
|
|
161
|
-
if (catalogGitTarget !== null) {
|
|
162
|
-
const root = await preflightTarget(catalogGitTarget);
|
|
163
|
-
const rootName = root.ok && root.name !== null && validPackageName(root.name) ? root.name : null;
|
|
164
|
-
if (rootName !== null && rootName !== rawRepo
|
|
165
|
-
&& await isDshNpmPackageForRepo(rootName, catalogRepo, settings.npmRegistry, effectiveProxy)) {
|
|
166
|
-
attempts.push(`catalog npm target: \`${target}\` → repository root DSH package \`${rootName}\``);
|
|
167
|
-
target = rootName;
|
|
168
|
-
recordResolvedNpmPackage(profile, catalogRepo, rootName);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// npm 优先:git 目标先反查该仓库的官方 npm 包,命中则改走 npm 通道。
|
|
174
|
-
// git 分发常缺构建产物/子模块,npm 包是作者发布的完整产物;未命中或
|
|
175
|
-
// 查询失败保留 github 直装(不阻塞安装,预检仍会拦截缺入口文件的情况)。
|
|
176
|
-
// 通用机制,不针对具体插件。
|
|
177
|
-
// 反查本身计入「已尝试的安装方式」:组织 scope 与 GitHub 用户名不一致时仅凭
|
|
178
|
-
// 仓库名猜不到包名,失败提 Issue 时作者看到我们查过的命令就能直接指认正确包名。
|
|
179
|
-
// 显式 pin 了 Git ref 时跳过 npm 反查:用户要的就是那个精确修订,
|
|
180
|
-
// 不能用 registry 上的默认版本覆盖它。
|
|
181
|
-
if (repoTarget !== null && pinnedRef === null) {
|
|
182
|
-
// 反查/记录统一用归一化后的 owner/repo(gitRepo),保证「输入完整链接」也走同一套 npm 反查
|
|
183
|
-
const repoIdentity = gitRepo ?? rawRepo;
|
|
184
|
-
const npmName = await resolveNpmPackage(repoIdentity, settings.npmRegistry, effectiveProxy);
|
|
185
|
-
if (npmName !== null) {
|
|
186
|
-
target = npmName;
|
|
187
|
-
attempts.push(`npm registry search: \`npm search repository:${repoIdentity}\` → found \`${npmName}\``);
|
|
188
|
-
// 持久化 repo → npm 包名映射:目录数据未下发 npmPackage(组织 scope 与 GitHub
|
|
189
|
-
// 用户名不一致)时,客户端仍能通过 /installed 的 versions 把依赖 key 匹配回仓库,
|
|
190
|
-
// 列表立即显示「已安装」,避免安装成功后仍显示可安装
|
|
191
|
-
recordResolvedNpmPackage(profile, repoIdentity, npmName);
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
attempts.push(`npm registry search: \`npm search repository:${repoIdentity}\` → no matching package found (falling back to git install)`);
|
|
195
|
-
}
|
|
196
122
|
}
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const isGitChannel = release !== null || (repoTarget !== null && target === repoTarget);
|
|
202
|
-
if (isGitChannel && !settings.enableGitInstall) {
|
|
203
|
-
sendJson(response, 403, { error: 'git installs are disabled by the security settings' });
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
if (!isGitChannel && !settings.enableNpmInstall) {
|
|
207
|
-
sendJson(response, 403, { error: 'npm installs are disabled by the security settings' });
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
123
|
+
// 命令行安装(目录外)受「启用 NPM 安装」开关控制;目录插件安装走目录白名单,不受开关影响。
|
|
124
|
+
if (source !== 'catalog' && !settings.enableNpmInstall) {
|
|
125
|
+
sendJson(response, 403, { error: 'npm installs are disabled by the security settings' });
|
|
126
|
+
return;
|
|
210
127
|
}
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
// 「安装」会撞上重复安装守卫,直接覆盖重装比让用户看到一句看不懂的 already installed
|
|
214
|
-
// 报错友好,最终状态也一致(都是已安装)。命中判定双通道:npm 包名按 dependencies
|
|
215
|
-
// 键直接命中,GitHub 目标按仓库身份匹配。
|
|
128
|
+
// 命中已安装目标时按「更新」执行(pnpm add 对已存在依赖即原位覆盖重装):
|
|
129
|
+
// 客户端已装列表可能与服务端不同步,覆盖重装比报错更友好且终态一致。
|
|
216
130
|
const installed = readInstalled(profile);
|
|
217
|
-
const already =
|
|
131
|
+
const already = installed[target] !== undefined ? target : null;
|
|
218
132
|
if (already !== null)
|
|
219
133
|
mode = 'update';
|
|
220
134
|
// 队列级去重:同一目标已在排队/执行中时拒绝,避免客户端防重竞态导致重复入队
|
|
@@ -222,122 +136,42 @@ export function registerInstallRoutes(webServer, profile, loader) {
|
|
|
222
136
|
sendJson(response, 409, { error: `already queued: ${target}` });
|
|
223
137
|
return;
|
|
224
138
|
}
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
// 页 —— 404 表示「目标不存在」而非网络不通,不该被当成网络故障拦截。
|
|
228
|
-
// 仅新安装预检(更新是已信任目标的覆盖重装,跳过);不通直接 400 拦下并打
|
|
229
|
-
// [network] 标记,客户端据此提示「你的网络不通」,而不是把网络失败当成
|
|
230
|
-
// 插件侧问题引导去作者仓库提 Issue。
|
|
231
|
-
// 代理口径与诊断一致:设置里的代理 → 系统代理 → 环境变量 → 直连。
|
|
139
|
+
// 安装前网络探测:探 registry 根而非具体包(404 是「包不存在」而非网络不通);仅新安装执行,更新跳过。
|
|
140
|
+
// 不通则 400 拦下并打 [network] 标记,客户端据此提示网络问题,而非插件问题。
|
|
232
141
|
if (already === null) {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
// npm 通道探 registry 根。两者都是通道的稳定入口,不探具体包/仓库(404 会被误判成网络故障)。
|
|
236
|
-
const httpProbeTarget = release !== null
|
|
237
|
-
? 'https://github.com/'
|
|
238
|
-
: `${(settings.npmRegistry.replace(/\/+$/, '') || 'https://registry.npmjs.org')}/`;
|
|
239
|
-
// git 通道的探针换成克隆握手本身:git ls-remote 走的就是 pnpm 克隆前的同一套
|
|
240
|
-
// https 传输。curl 打 github.com 主页会被按协议/端口区分的防火墙与 TLS 复检
|
|
241
|
-
// 拦截(能 git 克隆、打不开网页),把可装的仓库误判成 [network] 而中止。
|
|
242
|
-
// 握手失败时再用 curl 复核一次主页连通性:只有两者都不通
|
|
243
|
-
// 才判「网络不通」;仅握手失败(仓库不存在、私有仓库、需要凭据)放行,
|
|
244
|
-
// 交给后续预检与真实安装给出准确原因。
|
|
245
|
-
// 探针只握手仓库本身(去掉 git+ 前缀与 #ref 片段):ls-remote 认的是仓库地址,
|
|
246
|
-
// 带 ref 片段会干扰 URL 解析
|
|
247
|
-
const gitProbeTarget = repoTarget === null ? null : repoTarget.replace(/^git\+/, '').replace(/#.*$/, '');
|
|
248
|
-
let net;
|
|
249
|
-
if (gitProbeTarget !== null) {
|
|
250
|
-
net = await gitLsRemote(gitProbeTarget, effectiveProxy, 6000);
|
|
251
|
-
if (!net.ok) {
|
|
252
|
-
const reachable = await probeUrl('https://github.com/', effectiveProxy, 6000);
|
|
253
|
-
if (reachable.ok)
|
|
254
|
-
net = reachable;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
net = await probeUrl(httpProbeTarget, effectiveProxy, 6000);
|
|
259
|
-
}
|
|
260
|
-
const probeTarget = gitProbeTarget ?? httpProbeTarget;
|
|
142
|
+
const probeTarget = `${(settings.npmRegistry.replace(/\/+$/, '') || 'https://registry.npmjs.org')}/`;
|
|
143
|
+
const net = await probeUrl(probeTarget, effectiveProxy, 6000);
|
|
261
144
|
if (!net.ok) {
|
|
262
|
-
//
|
|
263
|
-
// 让用户一眼看懂是网络问题而非插件问题
|
|
264
|
-
const channelName = isGitChannel
|
|
265
|
-
? (lang === 'zh' ? 'GitHub' : 'github.com')
|
|
266
|
-
: (lang === 'zh' ? 'npm 源' : 'the npm registry');
|
|
145
|
+
// 错误消息按界面语言输出,让用户看清是网络问题而非插件问题
|
|
267
146
|
const error = lang === 'zh'
|
|
268
|
-
? `[network] 安装已中止:安装前无法连接到
|
|
269
|
-
: `[network] install aborted: cannot reach
|
|
147
|
+
? `[network] 安装已中止:安装前无法连接到 npm 源(${probeTarget})—— 您的网络似乎不通或被拦截(断网 / DNS 解析失败 / 防火墙 / 代理配置问题)。这不是插件本身的问题,请检查网络连接或代理设置,运行「系统诊断」检测各通道连通性后重试。`
|
|
148
|
+
: `[network] install aborted: cannot reach the npm registry (${probeTarget}) before install — your network connection appears to be down or blocked (DNS / proxy / firewall). This is not a problem with the plugin itself. Check your connection or proxy settings, run the connectivity diagnostic, then retry.`;
|
|
270
149
|
sendJson(response, 400, { error, attempts });
|
|
271
150
|
return;
|
|
272
151
|
}
|
|
273
152
|
}
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
// npm 包信任 registry 直接放行;github 源拦截时标记 [packaging] 前缀,
|
|
277
|
-
// 供客户端归类为「插件分发不完整」并引导去仓库提 Issue
|
|
278
|
-
if (already === null) {
|
|
279
|
-
const preflight = await preflightTarget(target);
|
|
280
|
-
if (!preflight.ok) {
|
|
281
|
-
// 预检拦截(git 分发缺作者声明的文件):同步 400 返回,附上已尝试的安装方式,
|
|
282
|
-
// 客户端失败弹窗提 Issue 时一并贴给作者
|
|
283
|
-
sendJson(response, 400, {
|
|
284
|
-
error: `[packaging] ${target}: plugin distribution is incomplete — the file ${preflight.missing ?? '?'} the package needs (its declared entry, an entry a bundled patch mounts, or the patch itself) is not present in the git distribution (build output not committed). Report to the author.`,
|
|
285
|
-
attempts,
|
|
286
|
-
});
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
// 包名冲突检测(仅 git 通道):git 目标包内声明的 name 若与 profile 已装依赖同名,
|
|
290
|
-
// pnpm 会以该 name 做依赖键撞车,抛出的 CLI 报错晦涩难懂 —— 且该包名在 registry 上
|
|
291
|
-
// 可能被别的仓库占用,npm 反查搜不到,git 直装就会撞上已装的同名依赖。
|
|
292
|
-
// 到这里已装依赖要么来源不同仓库、要么 spec 是版本号解析不出仓库身份。
|
|
293
|
-
// 读 node_modules 里已装包的真实 repository 区分两种情况:
|
|
294
|
-
// - 指向同一仓库 → 是「同仓库残留」(spec 丢了仓库身份),就地转 update 覆盖重装(幂等);
|
|
295
|
-
// - 指向别的仓库 / 读不到 → 跨仓库包名冲突,入队前转成明确 409,指导用户先卸载或改装。
|
|
296
|
-
if (preflight.name !== null && repoTarget !== null && target === repoTarget) {
|
|
297
|
-
const declared = preflight.name;
|
|
298
|
-
if (installed[declared] !== undefined) {
|
|
299
|
-
const installedRepo = installedRepoOf(profile, declared);
|
|
300
|
-
if (installedRepo !== null && gitRepo !== null && installedRepo.toLowerCase() === gitRepo.toLowerCase()) {
|
|
301
|
-
// 同名且同源:已装 spec 丢了仓库身份导致的残留,直接按 update 覆盖重装,不打扰用户
|
|
302
|
-
attempts.push(`same-repo residual: \`${declared}\` resolves to \`${installedRepo}\` → reinstall as update`);
|
|
303
|
-
mode = 'update';
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
attempts.push(`package name conflict: \`${gitRepo}\` declares npm name \`${declared}\`, which is already present in the profile and cannot be matched to this repo (that npm name is likely registered by another repository)`);
|
|
307
|
-
const error = lang === 'zh'
|
|
308
|
-
? `already installed: ${declared} —— ${gitRepo} 声明的 npm 包名「${declared}」已存在于当前 profile,且无法匹配到本仓库(该包名在 npm 上可能被其他仓库占用)。请先移除现有的 ${declared}(dsh plugin remove ${declared}),再重新安装;或改装该包名对应的官方 npm 包。`
|
|
309
|
-
: `already installed: ${declared} — ${gitRepo} declares the npm package name "${declared}", which is already present in the profile and cannot be matched to this repo (that npm name is likely registered by another repository). Remove the existing ${declared} dependency (dsh plugin remove ${declared}) first, then retry; or install the official npm package under that name.`;
|
|
310
|
-
sendJson(response, 409, { error, attempts });
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
// 入队前复检(与上方检查同语义,但必须在同步块内):preflight 的 await 期间并发请求
|
|
317
|
-
// 可能已通过上方检查、任务却尚未入队——此处检查与 startPluginMutation 之间无 await,
|
|
318
|
-
// JS 单线程保证同一目标至多一个任务,杜绝双击/并发产生双任务
|
|
153
|
+
// 入队前复检:网络探测的 await 期间并发请求可能已通过上方检查,而此处到 startPluginMutation
|
|
154
|
+
// 之间无 await,JS 单线程保证同一目标至多一个任务,杜绝双击/并发双任务。
|
|
319
155
|
if (hasQueuedTarget(target)) {
|
|
320
156
|
sendJson(response, 409, { error: `already queued: ${target}` });
|
|
321
157
|
return;
|
|
322
158
|
}
|
|
323
|
-
//
|
|
159
|
+
// 后台启动 CLI;客户端轮询 /status 获取进度
|
|
324
160
|
const task = startPluginMutation({
|
|
325
161
|
// mode=update(已安装目标的覆盖更新)以 update 动作入队:
|
|
326
162
|
// 日志按 update 类别落盘(update.start/done/fail),实际 CLI 执行仍是 add <target>@latest
|
|
327
163
|
action: mode === 'update' ? 'update' : 'add',
|
|
328
164
|
profile,
|
|
329
165
|
target,
|
|
330
|
-
// npm 安装时 target 是包名:displayTarget
|
|
166
|
+
// npm 安装时 target 是包名:displayTarget 固定展示仓库名,待重启行/前端恢复不因安装目标形态而变
|
|
331
167
|
displayTarget: githubRepoOf(displayRepo) ?? undefined,
|
|
332
|
-
//
|
|
333
|
-
attempts,
|
|
334
|
-
timeoutMs: COMMAND_TIMEOUT_MS,
|
|
335
|
-
env: mutationEnv(settings),
|
|
336
|
-
// 覆盖更新:npm 通道命令显式 @latest(pnpm add 无版本对已存在依赖是幂等的,不加版本不会真更新)
|
|
168
|
+
// 覆盖更新:npm 命令显式 @latest(pnpm add 无版本对已存在依赖是幂等的,不加版本不会真更新)
|
|
337
169
|
updateNpm: mode === 'update',
|
|
338
170
|
// 运行中 loader:安装成功后把新插件热挂进 loader,非桌面端 + 纯服务端插件无需重启即生效
|
|
339
171
|
loader,
|
|
340
|
-
|
|
172
|
+
timeoutMs: COMMAND_TIMEOUT_MS,
|
|
173
|
+
env: mutationEnv(settings),
|
|
174
|
+
// 自定义安装入口渠道:日志/报错据此溯源
|
|
341
175
|
installChannel,
|
|
342
176
|
});
|
|
343
177
|
sendJson(response, 200, { ok: true, task: task.id });
|
package/lib/http/logs.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* native folder picker for choosing a log directory.
|
|
6
|
+
* 日志路由:分页日志查看器、清空、在文件管理器中定位,以及选择日志目录的原生文件夹选择器。
|
|
8
7
|
*/
|
|
9
8
|
import { spawn } from 'node:child_process';
|
|
10
9
|
import { dirname } from 'node:path';
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
* npm 包发布时间查询路由:按已安装插件的 npm 包名按需查询最新版本的发布时间,
|
|
7
|
+
* 供客户端判定 pnpm 24h 供应链门禁(发布未满门槛的版本不提示更新)。
|
|
8
|
+
*/
|
|
9
|
+
import { systemProxy } from '../services/probe.js';
|
|
10
|
+
import { lookupReleaseAge, parseReleaseAgeRequest } from '../services/npm-release-age.js';
|
|
11
|
+
import { loadSettings } from '../services/settings.js';
|
|
12
|
+
import { readJsonBody, requireTrustedPost, sendJson } from './shared.js';
|
|
13
|
+
export function registerNpmReleaseAgeRoutes(webServer, profile) {
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
kind: 'exact',
|
|
17
|
+
path: '/dsh-plugin-hub/npm-release-age',
|
|
18
|
+
handler: async (request, response) => {
|
|
19
|
+
if (!requireTrustedPost(request, response))
|
|
20
|
+
return;
|
|
21
|
+
try {
|
|
22
|
+
const packages = parseReleaseAgeRequest(await readJsonBody(request));
|
|
23
|
+
if (packages === null) {
|
|
24
|
+
sendJson(response, 400, { error: 'invalid package list' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const settings = loadSettings(profile);
|
|
28
|
+
// 代理口径与安装/目录一致:设置里的代理 → 系统代理 → 环境变量 → 直连
|
|
29
|
+
const proxy = settings.proxy !== ''
|
|
30
|
+
? settings.proxy
|
|
31
|
+
: (systemProxy() ?? process.env.HTTPS_PROXY ?? process.env.https_proxy ?? '');
|
|
32
|
+
// 并发查询:单包各自带超时且失败即 null,整体不因任一包失败而失败
|
|
33
|
+
const results = await Promise.all(packages.map(async (name) => [name, await lookupReleaseAge(name, settings.npmRegistry, proxy)]));
|
|
34
|
+
sendJson(response, 200, { packages: Object.fromEntries(results) });
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
}
|
package/lib/http/routes.js
CHANGED
|
@@ -6,12 +6,12 @@ import { registerSettingsRoutes } from './settings.js';
|
|
|
6
6
|
import { registerLogsRoutes } from './logs.js';
|
|
7
7
|
import { registerDiagnosticsRoutes } from './diagnostics.js';
|
|
8
8
|
import { registerTaskRoutes } from './task.js';
|
|
9
|
+
import { registerNpmReleaseAgeRoutes } from './npm-release-age.js';
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @param webServer - DSH web server
|
|
12
|
-
* @param profile - profile
|
|
13
|
-
* @param loader -
|
|
14
|
-
* immediately so the page survives a refresh without a host restart.
|
|
11
|
+
* 在宿主 web server 上注册插件中心 API 并返回 dispose 函数。
|
|
12
|
+
* @param webServer - DSH web server 服务。
|
|
13
|
+
* @param profile - 拥有插件变更权限的 profile。
|
|
14
|
+
* @param loader - 运行中 loader(可选):让卸载立即移除条目,页面刷新即生效、无需重启宿主。
|
|
15
15
|
*/
|
|
16
16
|
export function mountPluginHubRoutes(webServer, profile, loader) {
|
|
17
17
|
if (!PROFILE_RE.test(profile))
|
|
@@ -32,6 +32,7 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
|
|
|
32
32
|
...registerLogsRoutes(webServer, profile),
|
|
33
33
|
...registerDiagnosticsRoutes(webServer, profile),
|
|
34
34
|
...registerTaskRoutes(webServer, profile, loader),
|
|
35
|
+
...registerNpmReleaseAgeRoutes(webServer, profile),
|
|
35
36
|
];
|
|
36
37
|
const disposers = routes.map((route) => webServer.register(route));
|
|
37
38
|
return () => {
|
|
@@ -39,7 +40,7 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
|
|
|
39
40
|
dispose();
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
/**
|
|
43
|
+
/** 与客户端路由文档共用的 profile 解析。 */
|
|
43
44
|
export { readProfileArg } from '../services/install/install.js';
|
|
44
|
-
/**
|
|
45
|
+
/** 转出以便 tests/routes.test.ts 继续从本模块引入。 */
|
|
45
46
|
export { isDesktopHost, isSameOrigin } from './shared.js';
|
package/lib/http/settings.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* 设置路由:读写插件中心设置,以及恢复默认。
|
|
7
7
|
*/
|
|
8
8
|
import { mkdirSync } from 'node:fs';
|
|
9
9
|
import { dirname } from 'node:path';
|
|
@@ -28,7 +28,7 @@ export function registerSettingsRoutes(webServer, profile) {
|
|
|
28
28
|
// 白名单字段:只接受已知设置项,防客户端把任意键写进文件
|
|
29
29
|
const patch = {};
|
|
30
30
|
const source = typeof body === 'object' && body !== null ? body : {};
|
|
31
|
-
for (const key of ['checkUpdatesOnStart', 'enableNpmInstall', '
|
|
31
|
+
for (const key of ['checkUpdatesOnStart', 'enableNpmInstall', 'npmRegistry', 'proxy', 'logPath']) {
|
|
32
32
|
if (source[key] !== undefined)
|
|
33
33
|
patch[key] = source[key];
|
|
34
34
|
}
|
package/lib/http/shared.js
CHANGED
|
@@ -108,8 +108,8 @@ function loopbackAuthority(authority) {
|
|
|
108
108
|
return null;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
/** POST
|
|
112
|
-
*
|
|
111
|
+
/** POST 变更只接受来自本地 web server 来源或桌面宿主页面。转出供测试(tests/routes.test.ts)使用 ——
|
|
112
|
+
* 来源校验就是全部的 CSRF 防御。 */
|
|
113
113
|
export function isSameOrigin(request) {
|
|
114
114
|
// 服务只绑定本机回环地址:Host 不是回环(局域网 IP、被改写的域名)一律拒绝,
|
|
115
115
|
// 顺带挡掉 DNS rebinding(Host 是攻击者域名,即便解析到 127.0.0.1)。
|
package/lib/http/task.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* the installed-plugin inventory the client uses to reconcile its list.
|
|
6
|
+
* 任务/杂项路由:任务轮询、宿主重启、loader 条目调试,以及客户端用于对账列表的已装插件清单。
|
|
8
7
|
*/
|
|
9
8
|
import { spawn } from 'node:child_process';
|
|
10
9
|
import { join } from 'node:path';
|
package/lib/index.js
CHANGED
|
@@ -3,17 +3,16 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
* dsh-plugin
|
|
7
|
-
*
|
|
8
|
-
* these same-origin routes to perform real installs from inside the app.
|
|
6
|
+
* dsh-plugin 宿主入口:宿主 webServer 服务就绪后挂载插件中心的 HTTP 路由。
|
|
7
|
+
* 浏览器 bundle(src/client)通过这些同源路由在应用内执行真正的安装。
|
|
9
8
|
*/
|
|
10
9
|
import { readProfileArg } from './services/install/install.js';
|
|
11
10
|
import { mountPluginHubRoutes } from './http/routes.js';
|
|
12
11
|
export const name = 'dsh-plugin';
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @param ctx - cordis
|
|
16
|
-
* @param config -
|
|
13
|
+
* webServer 服务就绪后挂载插件中心路由。
|
|
14
|
+
* @param ctx - cordis 宿主上下文。
|
|
15
|
+
* @param config - 可选的 profile 覆盖。
|
|
17
16
|
*/
|
|
18
17
|
export function apply(ctx, config = {}) {
|
|
19
18
|
const profile = config.profile ?? readProfileArg('web');
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
*
|
|
6
6
|
* 安装/卸载运行时的公共类型与常量(无依赖,供各服务模块复用)。
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
8
|
+
/** 目录 GitHub 仓库身份(`owner/repo`)的语法。 */
|
|
9
9
|
export const REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
10
|
-
/**
|
|
10
|
+
/** npm 包名的语法(用于卸载目标)。 */
|
|
11
11
|
export const PACKAGE_RE = /^(?:@[a-z0-9._-]+\/)?[A-Za-z0-9._-]+$/;
|
|
12
12
|
/** 捕获输出上限:stdout/stderr 各保留尾部 N 字节。 */
|
|
13
13
|
export const CAPTURE_LIMIT_BYTES = 64 * 1024;
|
|
14
|
-
/**
|
|
14
|
+
/** 每个任务保留的最大输出行数(最新优先)。 */
|
|
15
15
|
export const MAX_TASK_LINES = 200;
|
|
16
|
-
/**
|
|
16
|
+
/** 最多跟踪的任务数;超出时先丢弃最早结束的任务。 */
|
|
17
17
|
export const MAX_TASKS = 50;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { addPendingRestart, clearPendingRestart, listPendingRestarts } from '../profile/pending-restart.js';
|
|
2
2
|
export { dumpLoaderEntries, removeLoadedEntry } from '../loader.js';
|
|
3
|
-
export { addAllowBuildsKey,
|
|
3
|
+
export { addAllowBuildsKey, githubRepoOf, githubTarget, globalNpmPackagesOf, installTargetOf, parseAllowBuildsKey, readProfileArg, validPackageName, } from '../profile/profile.js';
|
|
4
4
|
export { activeTask, cancelTask, getTask, hasQueuedTarget, hasRunningTask, runPluginMutation, startPluginMutation } from './task-queue.js';
|