@gehennawu/dsh-service 2.0.3 → 2.0.5
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 +6 -7
- package/README.md +6 -7
- package/backup-routes.js +1 -1
- package/client.js +1 -1
- package/index.js +57 -30
- package/package.json +2 -3
- package/quota-routes.js +1 -1
- package/session-routes.js +1 -1
- package/skill-routes.js +1 -1
- package/subagent-routes.js +1 -1
- package/plugin-compat.js +0 -611
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
4
4
|
import { createHash, createHmac, randomBytes, randomUUID } from 'node:crypto'
|
|
5
5
|
import { Buffer } from 'node:buffer'
|
|
6
|
-
import { constants as fsConstants, existsSync, readFileSync } from 'node:fs'
|
|
6
|
+
import { constants as fsConstants, existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
7
7
|
import { access, chmod, cp, link, lstat, mkdir, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from 'node:fs/promises'
|
|
8
8
|
import { homedir } from 'node:os'
|
|
9
9
|
import { basename, dirname, join, relative, resolve, sep } from 'node:path'
|
|
@@ -20,7 +20,6 @@ import { createSessionsRoutes } from './session-routes.js'
|
|
|
20
20
|
import { createQuotaRoutes } from './quota-routes.js'
|
|
21
21
|
import { createSubagentRoutes } from './subagent-routes.js'
|
|
22
22
|
import { createBackupRoutes } from './backup-routes.js'
|
|
23
|
-
import { collectPluginCompat, pluginCompatCheckItem } from './plugin-compat.js'
|
|
24
23
|
import { collectPluginHealth, pluginCheckItem, restartPluginEntry } from './plugin-health.js'
|
|
25
24
|
import {
|
|
26
25
|
buildCliproxyAccountPlan as buildCliproxyAccountPlanAdapter,
|
|
@@ -285,18 +284,60 @@ const FETCH_TIMEOUT_OVERRIDE = '--config.fetchTimeout=600000'
|
|
|
285
284
|
// 当前正在运行的插件源码目录:定位「本插件由哪个 profile 挂载」时与磁盘副本做 realpath 匹配。
|
|
286
285
|
const loadedPluginDir = dirname(fileURLToPath(import.meta.url))
|
|
287
286
|
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
287
|
+
// 运行中宿主版本的探测。DSH 包由宿主安装,不作为插件依赖打包进来,插件只能自己找。
|
|
288
|
+
// 候选按「离真正在跑的进程有多近」排序(2026-10-03 修复:旧实现只有后两条,
|
|
289
|
+
// 非全局前缀安装——npx / `npm i --prefix` / 隔离 profile——会静默读到机器上另一份安装):
|
|
290
|
+
// ① 当前进程入口 `process.argv[1]`(先 realpath 穿透 `.bin` 软链)逐级向上找
|
|
291
|
+
// `name === '@deepseek-ai/dsh'` 的 package.json —— 这是真正在跑的那份;
|
|
292
|
+
// ② 从插件自身路径 require(历史行为,profile 与宿主同树时命中);
|
|
293
|
+
// ③ 镜像/容器里的历史全局路径(兼容既有部署)。
|
|
294
|
+
// 任一候选的版本串都要过 BACKUP_VERSION_RE(含数字、长度封顶)才采纳,否则继续回落;
|
|
295
|
+
// 全部失败返回 'unknown',绝不让坏值进版本卡、更新判定与备份文件名。
|
|
296
|
+
const LEGACY_GLOBAL_DSH_MANIFEST = '/usr/local/lib/node_modules/@deepseek-ai/dsh/package.json'
|
|
297
|
+
|
|
298
|
+
function readDshVersion(options = {}) {
|
|
299
|
+
const readText = options.readFileSync ?? readFileSync
|
|
300
|
+
const resolveReal = options.realpathSync ?? realpathSync
|
|
301
|
+
const loadManifest = options.requireManifest ?? ((path) => require(path))
|
|
302
|
+
const argv1 = options.argv1 ?? process.argv[1]
|
|
303
|
+
const accepted = (value) => (typeof value === 'string' && BACKUP_VERSION_RE.test(value) ? value : null)
|
|
304
|
+
const entryManifestVersion = (path) => {
|
|
305
|
+
try {
|
|
306
|
+
const manifest = JSON.parse(readText(path, 'utf8'))
|
|
307
|
+
if (manifest === null || typeof manifest !== 'object' || manifest.name !== DSH_PACKAGE) return null
|
|
308
|
+
return accepted(manifest.version)
|
|
309
|
+
} catch (_) {
|
|
310
|
+
return null
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (typeof argv1 === 'string' && argv1 !== '') {
|
|
314
|
+
let entry = argv1
|
|
315
|
+
try { entry = resolveReal(argv1) } catch (_) {}
|
|
316
|
+
// 目录链深度设上限:拿不到 realpath 或入口在异常位置时不至于无限上溯。
|
|
317
|
+
let directory = dirname(entry)
|
|
318
|
+
for (let depth = 0; depth < 12; depth += 1) {
|
|
319
|
+
const version = entryManifestVersion(join(directory, 'package.json'))
|
|
320
|
+
if (version !== null) return version
|
|
321
|
+
const parent = dirname(directory)
|
|
322
|
+
if (parent === directory) break
|
|
323
|
+
directory = parent
|
|
324
|
+
}
|
|
325
|
+
}
|
|
295
326
|
try {
|
|
296
|
-
|
|
297
|
-
|
|
327
|
+
const version = accepted(loadManifest(`${DSH_PACKAGE}/package.json`)?.version)
|
|
328
|
+
if (version !== null) return version
|
|
329
|
+
} catch (_) {}
|
|
330
|
+
try {
|
|
331
|
+
const version = accepted(loadManifest(LEGACY_GLOBAL_DSH_MANIFEST)?.version)
|
|
332
|
+
if (version !== null) return version
|
|
333
|
+
} catch (_) {}
|
|
334
|
+
return 'unknown'
|
|
298
335
|
}
|
|
299
336
|
|
|
337
|
+
let dshVersion = readDshVersion()
|
|
338
|
+
let pluginVersion = 'unknown'
|
|
339
|
+
try { pluginVersion = require('./package.json').version } catch (_) {}
|
|
340
|
+
|
|
300
341
|
// Node 最低要求只读 package.json engines 一处(与版本号同源),启动时解析一次。
|
|
301
342
|
let requiredNodeMajor = 22
|
|
302
343
|
try {
|
|
@@ -3121,19 +3162,6 @@ async function collectDiagnostics(ctx, dshHome, runtimeEnv, options = {}) {
|
|
|
3121
3162
|
checks.push(pluginCheck)
|
|
3122
3163
|
}
|
|
3123
3164
|
|
|
3124
|
-
// v1.3 插件兼容性:对照已核实的 alpha 破坏面清单(client-runtime 供应商移除、SQLite
|
|
3125
|
-
// persistence 移除、聊天/统计条 CSS 哈希漂移、data-time-hover-root 删除)扫描启用插件的
|
|
3126
|
-
// 清单与入口代码;loader 缺席降级为 info 检查项、不带扫描结果。退役接口(severity info)
|
|
3127
|
-
// 单列 soft,只提示不拉高 overall。
|
|
3128
|
-
let compatReport
|
|
3129
|
-
try {
|
|
3130
|
-
compatReport = await collectPluginCompat(ctx)
|
|
3131
|
-
} catch (error) {
|
|
3132
|
-
compatReport = { available: false, scanned: 0, issues: [], soft: [], declaredOnly: [], unknown: [] }
|
|
3133
|
-
}
|
|
3134
|
-
if (!compatReport.available) add('plugin-compat', 'info', 'unavailable')
|
|
3135
|
-
else checks.push(pluginCompatCheckItem(compatReport))
|
|
3136
|
-
|
|
3137
3165
|
// 使用统计索引(工作区实现,未发布):统计链路自身的健康度。单会话折读失败会让用量静默少算,
|
|
3138
3166
|
// 此前只在模型统计页的全局警告里可见,诊断清单没有任何一项覆盖它。
|
|
3139
3167
|
// 功能关闭时宿主根本不刷新索引(usage/usage-refresh 均受 modelUsage 门控),报「失败」
|
|
@@ -3148,10 +3176,10 @@ async function collectDiagnostics(ctx, dshHome, runtimeEnv, options = {}) {
|
|
|
3148
3176
|
const indexed = index.sessions !== null && typeof index.sessions === 'object' ? Object.keys(index.sessions).length : 0
|
|
3149
3177
|
const updatedAt = Number.isFinite(Number(index.updatedAt)) ? Number(index.updatedAt) : 0
|
|
3150
3178
|
// updatedAt=0 = 从未建立索引(用户还没打开过模型统计页):不是故障,报 info。
|
|
3151
|
-
//
|
|
3152
|
-
//
|
|
3179
|
+
// 失败会话是「部分成功」语义:报 info 提示而非 warning——统计仍可用,
|
|
3180
|
+
// 只是少算了这些会话,不拉高 overall 为 warning,不点亮诊断标签 ⚠,不进概览可行动项。
|
|
3153
3181
|
if (updatedAt === 0) add('usage-index', 'info', 'never')
|
|
3154
|
-
else add('usage-index', failed > 0 ? '
|
|
3182
|
+
else add('usage-index', failed > 0 ? 'info' : 'ok', `${failed}:${indexed}:${updatedAt}`)
|
|
3155
3183
|
}
|
|
3156
3184
|
}
|
|
3157
3185
|
|
|
@@ -3164,9 +3192,6 @@ async function collectDiagnostics(ctx, dshHome, runtimeEnv, options = {}) {
|
|
|
3164
3192
|
checkedAt: Date.now(),
|
|
3165
3193
|
checks,
|
|
3166
3194
|
...(pluginReport.available ? { pluginIssues: pluginReport.issues } : {}),
|
|
3167
|
-
...(compatReport.available
|
|
3168
|
-
? { pluginCompat: { scanned: compatReport.scanned, issues: compatReport.issues, soft: compatReport.soft, declaredOnly: compatReport.declaredOnly, unknown: compatReport.unknown } }
|
|
3169
|
-
: {}),
|
|
3170
3195
|
}
|
|
3171
3196
|
}
|
|
3172
3197
|
|
|
@@ -6185,6 +6210,7 @@ export {
|
|
|
6185
6210
|
quotaEndpointFor,
|
|
6186
6211
|
quotaErrorCode,
|
|
6187
6212
|
quotaProviderUnusable,
|
|
6213
|
+
readDshVersion,
|
|
6188
6214
|
readLlmProviders,
|
|
6189
6215
|
readLlmProvidersFromDescribe,
|
|
6190
6216
|
normalizeLlmProviderEntries,
|
|
@@ -6280,6 +6306,7 @@ export default {
|
|
|
6280
6306
|
quotaEndpointFor,
|
|
6281
6307
|
quotaErrorCode,
|
|
6282
6308
|
quotaProviderUnusable,
|
|
6309
|
+
readDshVersion,
|
|
6283
6310
|
readLlmProviders,
|
|
6284
6311
|
readLlmProvidersFromDescribe,
|
|
6285
6312
|
normalizeLlmProviderEntries,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gehennawu/dsh-service",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "DSH Web 运维面板:安全重启、健康监控、备份和 Linux 权限维护。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
"subagent-routes.js",
|
|
27
27
|
"backup-routes.js",
|
|
28
28
|
"plugin-health.js",
|
|
29
|
-
"plugin-compat.js",
|
|
30
29
|
"client.js",
|
|
31
30
|
"cordis.patch.yml",
|
|
32
31
|
"README.md",
|
|
@@ -53,7 +52,7 @@
|
|
|
53
52
|
"homepage": "https://github.com/gehennawu/dsh-service#readme",
|
|
54
53
|
"engines": {
|
|
55
54
|
"node": ">=22",
|
|
56
|
-
"dsh": ">=0.1.1-rc.2 <0.2.
|
|
55
|
+
"dsh": ">=0.1.1-rc.2 <0.2.1-alpha.2"
|
|
57
56
|
},
|
|
58
57
|
"dependencies": {
|
|
59
58
|
"@deepseek-ai/schemastery": "^3.18.3"
|
package/quota-routes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 额度查询(quota)的 RPC 端点:从 apply 的端点表按功能域拆出。
|
|
2
2
|
// 依赖显式注入(工厂解构名单即本模块完整依赖面);handler 体与拆分前逐字一致。
|
|
3
3
|
// 注意:客户端半有单产物约束,宿主半无此约束——兄弟 ESM 模块是本仓库既有惯例
|
|
4
|
-
// (quota-adapters.js / backup-integrity.js / plugin-health.js
|
|
4
|
+
// (quota-adapters.js / backup-integrity.js / plugin-health.js)。
|
|
5
5
|
|
|
6
6
|
import { randomBytes } from 'node:crypto'
|
|
7
7
|
import { resolve } from 'node:path'
|
package/session-routes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 会话管理(sessions)的 RPC 端点:从 apply 的端点表按功能域拆出。
|
|
2
2
|
// 依赖显式注入(工厂解构名单即本模块完整依赖面);handler 体与拆分前逐字一致。
|
|
3
3
|
// 注意:客户端半有单产物约束,宿主半无此约束——兄弟 ESM 模块是本仓库既有惯例
|
|
4
|
-
// (quota-adapters.js / backup-integrity.js / plugin-health.js
|
|
4
|
+
// (quota-adapters.js / backup-integrity.js / plugin-health.js)。
|
|
5
5
|
|
|
6
6
|
import { randomUUID } from 'node:crypto'
|
|
7
7
|
import { rm } from 'node:fs/promises'
|
package/skill-routes.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// 依赖显式注入(工厂解构名单即本模块完整依赖面);handler 体与拆分前一致,
|
|
3
3
|
// 例外:可重赋值的 let(skillsBatch/skillsIndexPromise)以 { current } 箱体注入保活绑定。
|
|
4
4
|
// 注意:客户端半有单产物约束,宿主半无此约束——兄弟 ESM 模块是本仓库既有惯例
|
|
5
|
-
// (quota-adapters.js / backup-integrity.js / plugin-health.js
|
|
5
|
+
// (quota-adapters.js / backup-integrity.js / plugin-health.js)。
|
|
6
6
|
|
|
7
7
|
import { randomUUID } from 'node:crypto'
|
|
8
8
|
import { readFile } from 'node:fs/promises'
|
package/subagent-routes.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// 依赖显式注入(工厂解构名单即本模块完整依赖面);handler 体与拆分前一致,
|
|
3
3
|
// 例外:可重赋值的 let(subagentRouteConfig/subagentSeamInstalled)以 { current } 箱体注入保活绑定。
|
|
4
4
|
// 注意:客户端半有单产物约束,宿主半无此约束——兄弟 ESM 模块是本仓库既有惯例
|
|
5
|
-
// (quota-adapters.js / backup-integrity.js / plugin-health.js
|
|
5
|
+
// (quota-adapters.js / backup-integrity.js / plugin-health.js)。
|
|
6
6
|
|
|
7
7
|
export function createSubagentRoutes({
|
|
8
8
|
ctx,
|