dsh-harness-manager 0.1.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.md +124 -0
- package/cordis.patch.yml +18 -0
- package/lib/index.js +149 -0
- package/lib/panel.js +113 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# dsh-harness-manager
|
|
2
|
+
|
|
3
|
+
在 DSH 的 Web 界面左下角加一个「管理器」胶囊按钮,一键打开桌面版
|
|
4
|
+
[DeepSeek Harness Manager](https://github.com/sss-1012/DeepSeek-Harness-Manager)。
|
|
5
|
+
|
|
6
|
+
管理器是本仓库提供的 Windows 控制中心:环境检测与安装、插件搜索/安装/启停、
|
|
7
|
+
多 profile 启停、诊断中心、余额查询、版本更新与回滚。
|
|
8
|
+
|
|
9
|
+
- 已安装管理器 → 显示**绿点**,点击直接启动(管理器是单实例,已在运行会前置窗口)。
|
|
10
|
+
- 未检测到 → 显示**黄点**,点击前往 Releases 下载页。
|
|
11
|
+
- 胶囊可关闭,偏好记录在 `localStorage`(键 `dshm.panel.hidden`)。
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
dsh plugin --profile web add dsh-harness-manager
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
安装后重启 `dsh web`(或在管理器里重启该 profile)生效。
|
|
20
|
+
|
|
21
|
+
本地开发安装(不发布 npm,直接链接目录):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
dsh plugin --profile web add link:E:\Work\DeepSeek-Harness-Manager\plugin
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
卸载:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
dsh plugin --profile web remove dsh-harness-manager
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 管理器程序是怎么找到的
|
|
34
|
+
|
|
35
|
+
按以下顺序探测,任一路径存在即可用:
|
|
36
|
+
|
|
37
|
+
1. 环境变量 `DSH_MANAGER_EXE`(指向 `DeepSeek-Harness-Manager.exe`)
|
|
38
|
+
2. `~/.dsh-manager/install.json` 中的 `exe` 字段 —— 管理器每次启动时自注册,
|
|
39
|
+
同时写入 `version` 与 `updatedAt`
|
|
40
|
+
3. 常见安装位置:
|
|
41
|
+
- `%LOCALAPPDATA%\Programs\DeepSeek-Harness-Manager\DeepSeek-Harness-Manager.exe`
|
|
42
|
+
- `%LOCALAPPDATA%\DeepSeek-Harness-Manager\DeepSeek-Harness-Manager.exe`
|
|
43
|
+
- `%ProgramFiles%\DeepSeek-Harness-Manager\DeepSeek-Harness-Manager.exe`
|
|
44
|
+
|
|
45
|
+
未安装也不影响 DSH 本身:插件只提供一个入口,不含任何业务逻辑。
|
|
46
|
+
|
|
47
|
+
## 提供的路由
|
|
48
|
+
|
|
49
|
+
宿主侧通过 `ctx.webServer` 注册三个只读/本地路由,不会向外发请求:
|
|
50
|
+
|
|
51
|
+
| 路径 | 方法 | 说明 |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `/dsh-manager/status.json` | GET | `{ installed, exe, version, source, downloadUrl }` |
|
|
54
|
+
| `/dsh-manager/launch` | POST | 以 detached 方式启动管理器进程 |
|
|
55
|
+
| `/dsh-manager/panel.js` | GET | 左下角入口的客户端脚本 |
|
|
56
|
+
|
|
57
|
+
## 目录结构
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
plugin/
|
|
61
|
+
package.json # dsh.bundle 声明 + keywords(dsh-plugin)
|
|
62
|
+
cordis.patch.yml # 把插件挂进 profile 配置树
|
|
63
|
+
lib/index.js # 宿主侧:路由 + tapIndex 注入
|
|
64
|
+
lib/panel.js # 客户端:左下角胶囊按钮
|
|
65
|
+
test/host.test.js # 无依赖自检(mock ctx,13 项)
|
|
66
|
+
test/boot-check.mjs # 真实启动自检(隔离环境,9 项)
|
|
67
|
+
test/resolve-check.mjs # 只读:bundle 解析 + 依赖树完整度
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 自检
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
node plugin/test/host.test.js # 快:mock 宿主上下文,校验路由/注入/分支
|
|
74
|
+
node plugin/test/boot-check.mjs # 慢(约 1 分钟):真实 dsh web 端到端
|
|
75
|
+
node plugin/test/resolve-check.mjs web --deep # 只读:检查某 profile 的 bundle 是否都能解析、依赖树是否完整
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`boot-check.mjs` 会在 `.dshm-plugin-check/` 下**用独立的 DSH_HOME 与非默认端口**(默认 3099)
|
|
79
|
+
创建测试 profile,不读写真实的 `~/.dsh`、`~/.dsh-manager`,也不影响正在运行的服务;
|
|
80
|
+
结束时会自动清理(若目录仍被刚退出的子进程占用,会交给后台进程稍后删除)。
|
|
81
|
+
|
|
82
|
+
## 发布到 npm(可选)
|
|
83
|
+
|
|
84
|
+
发布后即可 `dsh plugin --profile web add dsh-harness-manager`,不必再用 `link:`。
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd plugin
|
|
88
|
+
npm login
|
|
89
|
+
npm publish --access public
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
若账号开启了 2FA,npm 会要求一次性验证码,否则报
|
|
93
|
+
`E403 ... Two-factor authentication or granular access token with bypass 2fa enabled is required`。两种解法:
|
|
94
|
+
|
|
95
|
+
1. 带验证码发布:`npm publish --access public --otp=<验证器里的 6 位码>`;
|
|
96
|
+
若提示需要 web 认证,先执行 `npm login --auth-type=web` 再发布。
|
|
97
|
+
2. 生成**勾选 Bypass 2FA** 的 granular token(包权限 Read and write),设置
|
|
98
|
+
`npm config set //registry.npmjs.org/:_authToken <token>` 后再发布。
|
|
99
|
+
该 token 等同免 2FA 的发布权限,不要提交进仓库,用完可撤销。
|
|
100
|
+
|
|
101
|
+
## 提交到 awesome-dsh-plugin
|
|
102
|
+
|
|
103
|
+
上游要求(见其 `contributing.md`):`package.json` 声明 `dsh.bundle` 且同级有 `cordis.patch.yml`、
|
|
104
|
+
仓库创建满 1 天、仓库带 [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic、描述必须与代码相符。
|
|
105
|
+
本插件位于子目录,按「monorepo 子包」形式提交:`url` 指向子目录,`name` 用 `owner/repo#subname`,
|
|
106
|
+
文件名 `owner__repo--<子路径>.yml`。
|
|
107
|
+
|
|
108
|
+
在 [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) 添加**一个文件**
|
|
109
|
+
`data/plugins/sss-1012__DeepSeek-Harness-Manager--plugin.yml`:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
url: https://github.com/sss-1012/DeepSeek-Harness-Manager/tree/main/plugin
|
|
113
|
+
name: sss-1012/DeepSeek-Harness-Manager#plugin
|
|
114
|
+
category: dev
|
|
115
|
+
description:
|
|
116
|
+
en: "Launcher pill in the DSH web UI that opens DeepSeek Harness Manager, a Windows desktop app which installs Node/pnpm/dsh, manages plugins and profiles, diagnoses failures, and updates or rolls back the dsh CLI."
|
|
117
|
+
zh: "DSH 界面左下角的入口胶囊,一键打开 DeepSeek Harness Manager —— Windows 控制中心:装环境(Node/pnpm/dsh)、管插件与 profile、诊断故障,以及 dsh CLI 的更新与回滚。"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
不要手改生成出来的两个 README;`category` 只能取上游列出的固定值之一(`dev` 为同类启动器常用的分类)。
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# dsh-harness-manager —— DSH bundle patch
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# 挂载声明:把「打开 DSH Manager」入口插入 Web profile 的配置树。
|
|
5
|
+
#
|
|
6
|
+
# 安装:
|
|
7
|
+
# dsh plugin --profile web add dsh-harness-manager
|
|
8
|
+
# 本地/开发安装(直接链接本目录):
|
|
9
|
+
# dsh plugin --profile web add link:<本目录绝对路径>
|
|
10
|
+
# 安装后重启 dsh web 生效。
|
|
11
|
+
#
|
|
12
|
+
# 卸载:
|
|
13
|
+
# dsh plugin --profile web remove dsh-harness-manager
|
|
14
|
+
# ============================================================================
|
|
15
|
+
|
|
16
|
+
- insert:
|
|
17
|
+
- id: dsh-harness-manager
|
|
18
|
+
name: dsh-harness-manager
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { spawn } from 'node:child_process'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
// dsh-harness-manager —— 在 DSH 界面里一键打开 DeepSeek Harness Manager。
|
|
8
|
+
// 由 manager 仓库提供;安装:
|
|
9
|
+
// dsh plugin --profile web add dsh-harness-manager
|
|
10
|
+
// dsh plugin --profile web add link:<repo>/plugin (本地开发)
|
|
11
|
+
const name = 'dsh-harness-manager'
|
|
12
|
+
const inject = ['webServer']
|
|
13
|
+
|
|
14
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
15
|
+
const MANAGER_HOME = process.env.DSH_MANAGER_HOME || path.join(os.homedir(), '.dsh-manager')
|
|
16
|
+
const EXE_NAME = 'DeepSeek-Harness-Manager.exe'
|
|
17
|
+
const DOWNLOAD_URL = 'https://github.com/sss-1012/DeepSeek-Harness-Manager/releases/latest'
|
|
18
|
+
|
|
19
|
+
const JSON_HEADERS = {
|
|
20
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
21
|
+
'Cache-Control': 'no-store',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function candidatePaths() {
|
|
25
|
+
const list = []
|
|
26
|
+
if (process.env.DSH_MANAGER_EXE) list.push(process.env.DSH_MANAGER_EXE)
|
|
27
|
+
const local = process.env.LOCALAPPDATA || ''
|
|
28
|
+
const pf = process.env.ProgramFiles || 'C:\\Program Files'
|
|
29
|
+
const pf86 = process.env['ProgramFiles(x86)'] || ''
|
|
30
|
+
if (local) {
|
|
31
|
+
list.push(path.join(local, 'Programs', 'DeepSeek-Harness-Manager', EXE_NAME))
|
|
32
|
+
list.push(path.join(local, 'DeepSeek-Harness-Manager', EXE_NAME))
|
|
33
|
+
}
|
|
34
|
+
list.push(path.join(pf, 'DeepSeek-Harness-Manager', EXE_NAME))
|
|
35
|
+
if (pf86) list.push(path.join(pf86, 'DeepSeek-Harness-Manager', EXE_NAME))
|
|
36
|
+
return list
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// manager 启动时会写 install.json(自注册),优先采用;否则回退到常见安装路径
|
|
40
|
+
function locateManager() {
|
|
41
|
+
try {
|
|
42
|
+
const raw = fs.readFileSync(path.join(MANAGER_HOME, 'install.json'), 'utf8')
|
|
43
|
+
const info = JSON.parse(raw.replace(/^\uFEFF/, ''))
|
|
44
|
+
if (info && typeof info.exe === 'string' && fs.existsSync(info.exe)) {
|
|
45
|
+
return { exe: info.exe, version: info.version || null, source: 'install.json' }
|
|
46
|
+
}
|
|
47
|
+
} catch { /* 未自注册 */ }
|
|
48
|
+
for (const p of candidatePaths()) {
|
|
49
|
+
try { if (p && fs.existsSync(p)) return { exe: p, version: null, source: 'path' } } catch { /* 忽略 */ }
|
|
50
|
+
}
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readBody(req, limit = 64 * 1024) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
let size = 0
|
|
57
|
+
const chunks = []
|
|
58
|
+
req.on('data', (c) => {
|
|
59
|
+
size += c.length
|
|
60
|
+
if (size > limit) { reject(new Error('body too large')); req.destroy(); return }
|
|
61
|
+
chunks.push(c)
|
|
62
|
+
})
|
|
63
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
|
|
64
|
+
req.on('error', reject)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function apply(ctx) {
|
|
69
|
+
const disposers = []
|
|
70
|
+
if (!ctx || !ctx.webServer) return
|
|
71
|
+
|
|
72
|
+
const panelJs = fs.readFileSync(path.join(PACKAGE_ROOT, 'lib', 'panel.js'), 'utf8')
|
|
73
|
+
|
|
74
|
+
// 状态:是否已安装、版本、下载地址
|
|
75
|
+
disposers.push(ctx.webServer.register({
|
|
76
|
+
kind: 'exact',
|
|
77
|
+
path: '/dsh-manager/status.json',
|
|
78
|
+
handler: (req, res) => {
|
|
79
|
+
const found = locateManager()
|
|
80
|
+
res.writeHead(200, JSON_HEADERS)
|
|
81
|
+
res.end(JSON.stringify({
|
|
82
|
+
ok: true,
|
|
83
|
+
installed: Boolean(found),
|
|
84
|
+
exe: found ? found.exe : null,
|
|
85
|
+
version: found ? found.version : null,
|
|
86
|
+
source: found ? found.source : null,
|
|
87
|
+
downloadUrl: DOWNLOAD_URL,
|
|
88
|
+
}))
|
|
89
|
+
},
|
|
90
|
+
}))
|
|
91
|
+
|
|
92
|
+
// 启动管理器(单实例锁会让已在运行的窗口前置)
|
|
93
|
+
disposers.push(ctx.webServer.register({
|
|
94
|
+
kind: 'exact',
|
|
95
|
+
path: '/dsh-manager/launch',
|
|
96
|
+
handler: async (req, res) => {
|
|
97
|
+
if (req.method !== 'POST') {
|
|
98
|
+
res.writeHead(405, JSON_HEADERS)
|
|
99
|
+
res.end(JSON.stringify({ ok: false, error: 'POST only' }))
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
try { await readBody(req) } catch { /* 忽略 body */ }
|
|
103
|
+
const found = locateManager()
|
|
104
|
+
if (!found) {
|
|
105
|
+
res.writeHead(200, JSON_HEADERS)
|
|
106
|
+
res.end(JSON.stringify({ ok: false, code: 'NOT_INSTALLED', downloadUrl: DOWNLOAD_URL }))
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const child = spawn(found.exe, [], { detached: true, stdio: 'ignore', windowsHide: false })
|
|
111
|
+
child.unref()
|
|
112
|
+
res.writeHead(200, JSON_HEADERS)
|
|
113
|
+
res.end(JSON.stringify({ ok: true, exe: found.exe }))
|
|
114
|
+
} catch (err) {
|
|
115
|
+
res.writeHead(200, JSON_HEADERS)
|
|
116
|
+
res.end(JSON.stringify({ ok: false, code: 'LAUNCH_FAILED', error: String((err && err.message) || err).slice(0, 200) }))
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
}))
|
|
120
|
+
|
|
121
|
+
// 客户端脚本
|
|
122
|
+
disposers.push(ctx.webServer.register({
|
|
123
|
+
kind: 'exact',
|
|
124
|
+
path: '/dsh-manager/panel.js',
|
|
125
|
+
handler: (req, res) => {
|
|
126
|
+
res.writeHead(200, {
|
|
127
|
+
'Content-Type': 'application/javascript; charset=utf-8',
|
|
128
|
+
'Cache-Control': 'no-store',
|
|
129
|
+
})
|
|
130
|
+
res.end(panelJs)
|
|
131
|
+
},
|
|
132
|
+
}))
|
|
133
|
+
|
|
134
|
+
// 注入到 Web UI
|
|
135
|
+
disposers.push(ctx.webServer.tapIndex((html) => {
|
|
136
|
+
if (html.indexOf('/dsh-manager/panel.js') !== -1) return html
|
|
137
|
+
const tag = '<script defer src="/dsh-manager/panel.js"></script>'
|
|
138
|
+
if (html.indexOf('</body>') !== -1) return html.replace('</body>', tag + '</body>')
|
|
139
|
+
return html + tag
|
|
140
|
+
}))
|
|
141
|
+
|
|
142
|
+
ctx.effect(() => () => {
|
|
143
|
+
for (const d of disposers) {
|
|
144
|
+
try { d() } catch { /* 忽略 */ }
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export { name, inject, apply }
|
package/lib/panel.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// dsh-harness-manager 客户端脚本:在 DSH Web UI 左下角放一个管理器入口。
|
|
2
|
+
// 纯浏览器脚本,由宿主以 /dsh-manager/panel.js 提供。
|
|
3
|
+
(function () {
|
|
4
|
+
if (window.__dshManagerPanel) return
|
|
5
|
+
window.__dshManagerPanel = true
|
|
6
|
+
|
|
7
|
+
var HIDE_KEY = 'dshm.panel.hidden'
|
|
8
|
+
var API = '/dsh-manager'
|
|
9
|
+
var state = null
|
|
10
|
+
|
|
11
|
+
function el(tag, style, text) {
|
|
12
|
+
var node = document.createElement(tag)
|
|
13
|
+
if (style) node.setAttribute('style', style)
|
|
14
|
+
if (text != null) node.textContent = text
|
|
15
|
+
return node
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function toast(message) {
|
|
19
|
+
var box = el('div',
|
|
20
|
+
'position:fixed;left:18px;bottom:74px;z-index:2147482000;max-width:320px;' +
|
|
21
|
+
'padding:10px 14px;border-radius:10px;font:13px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif;' +
|
|
22
|
+
'background:rgba(17,24,39,.94);color:#f9fafb;box-shadow:0 8px 24px rgba(0,0,0,.28);' +
|
|
23
|
+
'opacity:0;transition:opacity .18s ease;pointer-events:none;')
|
|
24
|
+
box.textContent = message
|
|
25
|
+
document.body.appendChild(box)
|
|
26
|
+
requestAnimationFrame(function () { box.style.opacity = '1' })
|
|
27
|
+
setTimeout(function () {
|
|
28
|
+
box.style.opacity = '0'
|
|
29
|
+
setTimeout(function () { if (box.parentNode) box.parentNode.removeChild(box) }, 220)
|
|
30
|
+
}, 2600)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function build(status) {
|
|
34
|
+
var wrap = el('div',
|
|
35
|
+
'position:fixed;left:18px;bottom:18px;z-index:2147482000;display:flex;align-items:center;gap:8px;')
|
|
36
|
+
wrap.id = 'dshm-panel'
|
|
37
|
+
|
|
38
|
+
var btn = el('button',
|
|
39
|
+
'display:inline-flex;align-items:center;gap:8px;padding:9px 14px;' +
|
|
40
|
+
'border:1px solid rgba(148,163,184,.45);border-radius:999px;cursor:pointer;' +
|
|
41
|
+
'font:600 13px/1 system-ui,-apple-system,"Segoe UI",sans-serif;' +
|
|
42
|
+
'background:rgba(255,255,255,.94);color:#111827;backdrop-filter:blur(8px);' +
|
|
43
|
+
'box-shadow:0 6px 18px rgba(15,23,42,.16);')
|
|
44
|
+
var dot = el('span',
|
|
45
|
+
'width:8px;height:8px;border-radius:50%;flex:0 0 auto;background:' +
|
|
46
|
+
(status.installed ? '#16a34a' : '#f59e0b') + ';')
|
|
47
|
+
var text = el('span', null, status.installed ? '管理器' : '安装管理器')
|
|
48
|
+
btn.appendChild(dot)
|
|
49
|
+
btn.appendChild(text)
|
|
50
|
+
|
|
51
|
+
btn.addEventListener('mouseenter', function () { btn.style.boxShadow = '0 10px 24px rgba(15,23,42,.24)' })
|
|
52
|
+
btn.addEventListener('mouseleave', function () { btn.style.boxShadow = '0 6px 18px rgba(15,23,42,.16)' })
|
|
53
|
+
|
|
54
|
+
btn.title = status.installed
|
|
55
|
+
? '打开 DeepSeek Harness Manager' + (status.version ? ' v' + status.version : '') + '\n' + (status.exe || '')
|
|
56
|
+
: '尚未检测到管理器,点击前往下载'
|
|
57
|
+
|
|
58
|
+
btn.addEventListener('click', function () {
|
|
59
|
+
if (!state || !state.installed) {
|
|
60
|
+
window.open((state && state.downloadUrl) || 'https://github.com/sss-1012/DeepSeek-Harness-Manager/releases/latest', '_blank', 'noopener')
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
btn.disabled = true
|
|
64
|
+
fetch(API + '/launch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
|
|
65
|
+
.then(function (r) { return r.json() })
|
|
66
|
+
.then(function (data) {
|
|
67
|
+
if (data && data.ok) { toast('DeepSeek Harness Manager 已启动'); return }
|
|
68
|
+
if (data && data.code === 'NOT_INSTALLED') {
|
|
69
|
+
state.installed = false
|
|
70
|
+
toast('未找到管理器程序,正在打开下载页')
|
|
71
|
+
window.open((data && data.downloadUrl) || (state && state.downloadUrl) || '', '_blank', 'noopener')
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
toast('启动失败:' + ((data && data.error) || '未知错误'))
|
|
75
|
+
})
|
|
76
|
+
.catch(function (err) { toast('启动失败:' + (err && err.message ? err.message : err)) })
|
|
77
|
+
.then(function () { btn.disabled = false })
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
var close = el('button',
|
|
81
|
+
'width:26px;height:26px;line-height:1;padding:0;border:1px solid rgba(148,163,184,.45);' +
|
|
82
|
+
'border-radius:50%;cursor:pointer;background:rgba(255,255,255,.9);color:#64748b;' +
|
|
83
|
+
'font:14px/1 system-ui,sans-serif;box-shadow:0 4px 12px rgba(15,23,42,.12);',
|
|
84
|
+
'×')
|
|
85
|
+
close.title = '隐藏(在浏览器控制台执行 localStorage.removeItem("' + HIDE_KEY + '") 可恢复)'
|
|
86
|
+
close.addEventListener('click', function () {
|
|
87
|
+
try { localStorage.setItem(HIDE_KEY, '1') } catch (e) { /* 忽略 */ }
|
|
88
|
+
if (wrap.parentNode) wrap.parentNode.removeChild(wrap)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
wrap.appendChild(btn)
|
|
92
|
+
wrap.appendChild(close)
|
|
93
|
+
return wrap
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function mount(status) {
|
|
97
|
+
state = status
|
|
98
|
+
var old = document.getElementById('dshm-panel')
|
|
99
|
+
if (old && old.parentNode) old.parentNode.removeChild(old)
|
|
100
|
+
document.body.appendChild(build(status))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function boot() {
|
|
104
|
+
try { if (localStorage.getItem(HIDE_KEY) === '1') return } catch (e) { /* 忽略 */ }
|
|
105
|
+
fetch(API + '/status.json', { headers: { Accept: 'application/json' } })
|
|
106
|
+
.then(function (r) { return r.json() })
|
|
107
|
+
.then(function (data) { mount(data || { installed: false }) })
|
|
108
|
+
.catch(function () { mount({ installed: false, downloadUrl: 'https://github.com/sss-1012/DeepSeek-Harness-Manager/releases/latest' }) })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot)
|
|
112
|
+
else boot()
|
|
113
|
+
})()
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-harness-manager",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "在 DSH 界面里一键打开 DeepSeek Harness Manager(Windows 控制中心:环境/插件/诊断/更新回滚)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dsh",
|
|
7
|
+
"dsh-plugin",
|
|
8
|
+
"deepseek-harness",
|
|
9
|
+
"manager",
|
|
10
|
+
"windows",
|
|
11
|
+
"diagnostics"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"default": "./lib/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"dsh": {
|
|
23
|
+
"bundle": {
|
|
24
|
+
"patch": "./cordis.patch.yml"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"lib",
|
|
29
|
+
"cordis.patch.yml",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/sss-1012/DeepSeek-Harness-Manager.git",
|
|
35
|
+
"directory": "plugin"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/sss-1012/DeepSeek-Harness-Manager/tree/main/plugin",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/sss-1012/DeepSeek-Harness-Manager/issues"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT"
|
|
45
|
+
}
|