dsh-model 0.18.1 → 0.19.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 +9 -2
- package/README.md +9 -2
- package/dist/cli.js +5 -1
- package/dist/commands/self.js +5 -0
- package/dist/commands/sources.js +19 -0
- package/dist/daemon/core.js +89 -1
- package/dist/daemon/failures.js +80 -0
- package/dist/daemon/usage.js +1 -1
- package/dist/dsh/cli.js +6 -2
- package/dist/engine/compat.js +51 -1
- package/dist/integrations/apikeys.js +124 -0
- package/dist/integrations/dshplugin.js +14 -3
- package/dist/ops.js +2 -1
- package/dist/service/admin.js +2 -2
- package/dist/sources.js +9 -0
- package/dist/util/exec.js +11 -0
- package/package.json +4 -1
- package/plugin/client.js +257 -27
- package/plugin/index.js +77 -22
package/plugin/index.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import { spawn } from 'node:child_process'
|
|
11
11
|
import { randomBytes } from 'node:crypto'
|
|
12
|
-
import {
|
|
12
|
+
import { readFileSync } from 'node:fs'
|
|
13
|
+
import { access, readFile, writeFile } from 'node:fs/promises'
|
|
13
14
|
import { homedir } from 'node:os'
|
|
14
15
|
import { join } from 'node:path'
|
|
15
16
|
import { fileURLToPath } from 'node:url'
|
|
@@ -17,6 +18,30 @@ import { fileURLToPath } from 'node:url'
|
|
|
17
18
|
/** 本包根目录:插件就是完整的 dsh-model 包,自带命令行 */
|
|
18
19
|
const PKG_ROOT = fileURLToPath(new URL('..', import.meta.url))
|
|
19
20
|
const VPS_ROOT = '/opt/dsh-vps'
|
|
21
|
+
const ADMIN_PATH_UNIT = '/etc/systemd/system/dsh-model-admin.path'
|
|
22
|
+
|
|
23
|
+
/** 这份代码加载时的版本:插件装了新版但 dsh 没重启时,它和磁盘上的版本不一样 */
|
|
24
|
+
const RUNNING_VERSION = (() => {
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8')).version
|
|
27
|
+
} catch {
|
|
28
|
+
return ''
|
|
29
|
+
}
|
|
30
|
+
})()
|
|
31
|
+
|
|
32
|
+
async function installedVersion() {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(await readFile(join(PKG_ROOT, 'package.json'), 'utf8')).version
|
|
35
|
+
} catch {
|
|
36
|
+
return RUNNING_VERSION
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 页面能不能直接重启 dsh:只在 dsh-vps 上(交给 root 的 dsh-model-admin 执行 systemctl restart dsh-gate);其他环境提示手动重启 */
|
|
41
|
+
async function restartMode() {
|
|
42
|
+
if ((await exists(VPS_ROOT)) && (await exists(ADMIN_PATH_UNIT))) return 'vps'
|
|
43
|
+
return process.versions.electron ? 'desktop' : 'manual'
|
|
44
|
+
}
|
|
20
45
|
|
|
21
46
|
export const name = 'dsh-model'
|
|
22
47
|
export const inject = []
|
|
@@ -36,11 +61,14 @@ const ALLOWED = [
|
|
|
36
61
|
['POST', /^\/keys\/[A-Za-z0-9._-]{1,40}\/reveal$/],
|
|
37
62
|
['GET', /^\/endpoints$/],
|
|
38
63
|
['GET', /^\/self$/],
|
|
64
|
+
['GET', /^\/failures$/],
|
|
65
|
+
['POST', /^\/models\/test$/],
|
|
39
66
|
['POST', /^\/self\/(update|uninstall)$/],
|
|
40
67
|
['GET', /^\/sources\/[a-z-]{2,20}\/models$/],
|
|
41
68
|
['POST', /^\/sources\/[a-z-]{2,20}\/models$/],
|
|
42
69
|
['POST', /^\/sources\/[a-z-]{2,20}\/(enable|disable|logout)$/],
|
|
43
70
|
['POST', /^\/opencode\/key$/],
|
|
71
|
+
['POST', /^\/apikey\/[a-z-]{2,20}$/],
|
|
44
72
|
['GET', /^\/login\/[A-Za-z0-9._-]{1,120}$/],
|
|
45
73
|
['POST', /^\/login\/[A-Za-z0-9._-]{1,120}\/callback$/],
|
|
46
74
|
['DELETE', /^\/login\/[A-Za-z0-9._-]{1,120}$/],
|
|
@@ -98,25 +126,26 @@ function check(req, token, needToken = true) {
|
|
|
98
126
|
const exists = (p) => access(p).then(() => true, () => false)
|
|
99
127
|
|
|
100
128
|
/** 从页面一键执行 setup / repair(只在本机模式:VPS 上要 root,页面只给命令)。同一时间只跑一个 */
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
129
|
+
/** 一键安装 / 修复的进度:页面每秒来取一次新输出 */
|
|
130
|
+
let job = null
|
|
131
|
+
function startCli(action, args) {
|
|
132
|
+
if (job && job.running) return job
|
|
133
|
+
job = { action, running: true, output: '', code: null, startedAt: Date.now() }
|
|
134
|
+
const current = job
|
|
135
|
+
const child = spawn(process.execPath, [join(PKG_ROOT, 'bin/dsh-model.js'), ...args], {
|
|
136
|
+
windowsHide: true,
|
|
137
|
+
// dsh 桌面版可能跑在 Electron 里:让它当普通 Node 用
|
|
138
|
+
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1', NO_COLOR: '1', FORCE_COLOR: '0' },
|
|
139
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
140
|
+
})
|
|
141
|
+
const add = (b) => { current.output = (current.output + b.toString().replace(/\x1b\[[0-9;]*m/g, '')).slice(-20000) }
|
|
142
|
+
child.stdout.on('data', add)
|
|
143
|
+
child.stderr.on('data', add)
|
|
144
|
+
const timer = setTimeout(() => child.kill('SIGTERM'), 10 * 60 * 1000)
|
|
145
|
+
const done = (code) => { clearTimeout(timer); current.running = false; current.code = code }
|
|
146
|
+
child.on('error', (e) => { add(`\n${e.message}`); done(-1) })
|
|
147
|
+
child.on('exit', (code) => done(code ?? -1))
|
|
148
|
+
return job
|
|
120
149
|
}
|
|
121
150
|
|
|
122
151
|
export function apply(ctx) {
|
|
@@ -168,6 +197,30 @@ export function apply(ctx) {
|
|
|
168
197
|
}),
|
|
169
198
|
)
|
|
170
199
|
|
|
200
|
+
// 插件版本与重启:装了新版但 dsh 还在跑旧代码时,页面提示(VPS 上可一键重启)
|
|
201
|
+
track(
|
|
202
|
+
ws.register({
|
|
203
|
+
kind: 'exact',
|
|
204
|
+
path: '/api-dsh-model/host',
|
|
205
|
+
handler: async (req, res) => {
|
|
206
|
+
const bad = check(req, token)
|
|
207
|
+
if (bad) return json(res, bad.code, { error: { code: 'refused', message: bad.error } })
|
|
208
|
+
let body
|
|
209
|
+
try {
|
|
210
|
+
body = JSON.parse((await readBody(req)) || '{}')
|
|
211
|
+
} catch {
|
|
212
|
+
return json(res, 400, { error: { code: 'invalid_json', message: 'invalid JSON' } })
|
|
213
|
+
}
|
|
214
|
+
const mode = await restartMode()
|
|
215
|
+
if (body.action !== 'restart') return json(res, 200, { running: RUNNING_VERSION, installed: await installedVersion(), restart: mode })
|
|
216
|
+
if (mode !== 'vps') return json(res, 409, { error: { code: 'restart_unsupported', message: 'restart dsh manually' } })
|
|
217
|
+
// 写给 root 的 dsh-model-admin:它只认几件固定的事,这里是 restart-dsh
|
|
218
|
+
await writeFile(join(home(), 'admin', 'request'), JSON.stringify({ action: 'restart-dsh' }), { mode: 0o600 })
|
|
219
|
+
return json(res, 200, { restarting: true })
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
)
|
|
223
|
+
|
|
171
224
|
// 页面上的「一键安装 / 修复」
|
|
172
225
|
track(
|
|
173
226
|
ws.register({
|
|
@@ -183,10 +236,12 @@ export function apply(ctx) {
|
|
|
183
236
|
return json(res, 400, { error: { code: 'invalid_json', message: 'invalid JSON' } })
|
|
184
237
|
}
|
|
185
238
|
if (await exists(VPS_ROOT)) return json(res, 200, { vps: true })
|
|
239
|
+
// { poll: true }:只取进度;否则开始(已经在跑就接着看同一个)
|
|
240
|
+
if (body.poll) return json(res, 200, job ? { running: job.running, output: job.output, code: job.code, action: job.action } : { running: false, output: '', code: null })
|
|
186
241
|
const action = body.action === 'repair' ? 'repair' : 'setup'
|
|
187
242
|
const lang = body.lang === 'en' ? 'en' : 'zh'
|
|
188
|
-
const
|
|
189
|
-
return json(res, 200,
|
|
243
|
+
const j = startCli(action, action === 'repair' ? ['repair', '--lang', lang] : ['setup', '--skip-dsh-plugin', '--lang', lang])
|
|
244
|
+
return json(res, 200, { running: j.running, output: j.output, code: j.code, action: j.action })
|
|
190
245
|
},
|
|
191
246
|
}),
|
|
192
247
|
)
|