dsh-calibguard 0.3.5 → 0.3.6
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 -7
- package/README.md +7 -5
- package/docs/configuration.md +6 -3
- package/docs/troubleshooting.md +1 -0
- package/lib/client.js +26 -6
- package/lib/engine.js +66 -17
- package/lib/routes.js +10 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -4,7 +4,7 @@ English | [中文](README.md)
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
7
|
-

|
|
8
8
|
[](https://www.npmjs.com/package/dsh-calibguard)
|
|
9
9
|
|
|
10
10
|
Plugs the [CalibGuard](https://github.com/P-436/CG) file-security auditing engine into
|
|
@@ -228,10 +228,11 @@ After pressing Save:
|
|
|
228
228
|
- **the AI uses the same values** (precedence: plugin config < shared settings < per-call options);
|
|
229
229
|
- changes take effect immediately, no restart;
|
|
230
230
|
- the API key goes into the DSH credential store (`ctx.credentials`), not into that settings file;
|
|
231
|
-
- the **credential name** is only the name of the storage slot (not the secret
|
|
232
|
-
|
|
233
|
-
(`credentialRef`) and the value
|
|
234
|
-
plugin
|
|
231
|
+
- the **credential name** is only the name of the storage slot (not the secret; pre-filled as
|
|
232
|
+
`DEEPSEEK_API_KEY`): fill in the API key and save; the name is remembered in shared settings
|
|
233
|
+
(`credentialRef`) and the value goes to the DSH credential store first. **If the current DSH version
|
|
234
|
+
cannot be written from a plugin, the key is stored in the local `~/.dsh/calibguard-credentials.json`
|
|
235
|
+
(0600, this machine only)** and the panel tells you where it landed;
|
|
235
236
|
- **audit results**: every audit writes JSON + Markdown into the engine's `reports/` directory
|
|
236
237
|
(default `<engineRoot>/reports`, `engineRoot` defaults to `~/.dsh/calibguard-engine`). The panel shows
|
|
237
238
|
that path, has a "clear results" button, and each item links to its Markdown report.
|
|
@@ -277,7 +278,8 @@ Common keys of the loader row `config` (full table: [docs/configuration.md](docs
|
|
|
277
278
|
| First audit is slow | Normal: ~475 MB of weights are loaded once (then the process stays resident) |
|
|
278
279
|
| Big file takes tens of seconds | Normal: full-text sliding-window inference; see [docs/performance.md](docs/performance.md) |
|
|
279
280
|
| Gray zone always goes to manual review | No credential configured: save "credential name + API key" in the panel, or set `credentialRef`, or configure it in DSH Models |
|
|
280
|
-
| **Panel says "credentialRef required" when saving the API key** | Upgrade to ≥ **0.3.3**: the panel has a "credential name" field and remembers it
|
|
281
|
+
| **Panel says "credentialRef required" when saving the API key** | Upgrade to ≥ **0.3.3**: the panel has a "credential name" field (defaults to `DEEPSEEK_API_KEY`) and remembers it; older versions require `credentialRef` in the plugin config |
|
|
282
|
+
| **"This environment does not support writing the DSH credential store"** | Since 0.3.6 the key automatically falls back to the local `~/.dsh/calibguard-credentials.json` (0600) and the panel shows the storage location; you can also configure it in the DSH Models page, or use plugin config `llmApiKey` (not recommended) |
|
|
281
283
|
| **Where are audit results saved / how to clear them** | In the engine's `reports/` directory (default `<engineRoot>/reports`): JSON + Markdown per audit. The panel shows the path and has a "clear results" button; AI tools return `report_md_path` / `report_json_path` |
|
|
282
284
|
| **"First audit will load the model" hint shows every time** | Since 0.3.3 it is only shown while the model is not loaded yet; afterwards the button just says "auditing…" and the header shows "model loaded" |
|
|
283
285
|
| **NVIDIA GPU (e.g. RTX 2060) but the engine keeps using CPU** | The installed torch is the CPU build (old installer behavior). Upgrade to ≥0.3.3 (the panel will tell you) and run `node setup.mjs --reinstall-torch` (~2.5 GB) in the plugin directory, then restart DSH |
|
|
@@ -299,7 +301,7 @@ dsh-CalibGuard/
|
|
|
299
301
|
├── engine/ Bundled engine core copied from CG_GUI (code / tokenizer / calibration, no weights)
|
|
300
302
|
├── cordis.patch.yml Bundle patch (inserts the loader row)
|
|
301
303
|
├── scripts/ link-dsh (dev-time junction), bridge-selftest (engine self-check)
|
|
302
|
-
├── test/ node --test (
|
|
304
|
+
├── test/ node --test (105 cases; auto-skip when the engine env is missing)
|
|
303
305
|
├── .github/workflows/ publish.yml: npm trusted publishing (OIDC, optional)
|
|
304
306
|
└── docs/ configuration, performance, troubleshooting, plugin contract, design notes
|
|
305
307
|
```
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
7
|
-

|
|
8
8
|
[](https://www.npmjs.com/package/dsh-calibguard)
|
|
9
9
|
|
|
10
10
|
把 [CalibGuard](https://github.com/P-436/CG) 的文件安全审计引擎接进 **DeepSeek Harness(DSH)**:
|
|
@@ -217,8 +217,9 @@ AI 能看到完整四档结论与证据;若你说「只审高危文件」,
|
|
|
217
217
|
- **AI 调用同样使用这份设置**(优先级:插件配置 < 共享设置 < 本次调用参数);
|
|
218
218
|
- 改完立即生效,**不用重启**;
|
|
219
219
|
- API Key 写入 DSH 凭据库(`ctx.credentials`),不落入该设置文件;
|
|
220
|
-
- **凭据名**只是"保存位置"
|
|
221
|
-
|
|
220
|
+
- **凭据名**只是"保存位置"的名字(不是密钥本身,面板默认预填 `DEEPSEEK_API_KEY`):填 API Key 保存后,
|
|
221
|
+
名字记进共享设置的 `credentialRef`,值优先写入 DSH 凭据库;**当前 DSH 版本不支持从插件写凭据库时,
|
|
222
|
+
自动保存到本机 `~/.dsh/calibguard-credentials.json`(0600,仅本机)**,面板会明确提示存储位置;
|
|
222
223
|
- **审计结果**:每次审计在引擎的 `reports/` 目录生成 JSON + Markdown(默认 `<engineRoot>/reports`,
|
|
223
224
|
`engineRoot` 默认 `~/.dsh/calibguard-engine`)。面板结果区会显示该路径并可「清空结果」,
|
|
224
225
|
条目里的「查看 Markdown 报告」可直接打开。
|
|
@@ -262,7 +263,8 @@ AI 能看到完整四档结论与证据;若你说「只审高危文件」,
|
|
|
262
263
|
| 侧边栏没有图标 / 面板报 `not found` | 客户端与路由在宿主启动时加载:**重启 DSH** |
|
|
263
264
|
| **首次运行要等十几分钟** | 正常:首次下载模型(约 442 MB,自动镜像回退)并安装依赖(1–3 GB);面板顶部有进度卡片,只需一次 |
|
|
264
265
|
| 首次审计很慢 | 正常:要加载约 475 MB 权重(之后走常驻进程) |
|
|
265
|
-
| **面板保存 API Key 提示"需要 credentialRef"** | 升级到 ≥ **0.3.3
|
|
266
|
+
| **面板保存 API Key 提示"需要 credentialRef"** | 升级到 ≥ **0.3.3**:面板「凭据名」可直接填写并记住(默认预填 `DEEPSEEK_API_KEY`);旧版本请把 `credentialRef` 写进插件配置 |
|
|
267
|
+
| **提示"当前环境不支持写入 DSH 凭据库"** | ≥0.3.6 起会自动回退到本机凭据文件 `~/.dsh/calibguard-credentials.json`(0600),面板会显示存储位置;想改回凭据库时在 DSH Models 页配置即可。也可用插件配置 `llmApiKey`(不推荐) |
|
|
266
268
|
| **审计结果保存在哪 / 想清空** | 保存在引擎 `reports/` 目录(默认 `<engineRoot>/reports`),每次审计生成 JSON + Markdown;面板结果区显示路径并带「清空结果」按钮;AI 工具返回 `report_md_path` / `report_json_path` |
|
|
267
269
|
| **审计时总提示"首次会加载模型"** | 0.3.3 起只在模型尚未加载时提示;加载后按钮只显示「审计中…」,顶部状态显示「模型 已加载」 |
|
|
268
270
|
| **有 NVIDIA 显卡(如 RTX 2060)但一直显示 CPU** | 说明装的是 CPU 版 torch(旧版安装器行为)。升级到 ≥0.3.3 后面板会给出提示;在插件目录运行 `node setup.mjs --reinstall-torch`(约 2.5 GB)装上 CUDA 版即可,之后重启 DSH |
|
|
@@ -286,7 +288,7 @@ dsh-CalibGuard/
|
|
|
286
288
|
├── engine/ 从 CG_GUI 复制来的引擎核心(代码/词表/校准参数,不含 475 MB 权重)
|
|
287
289
|
├── cordis.patch.yml 组合包 patch(把插件行插进 profile 配置树)
|
|
288
290
|
├── scripts/ link-dsh(开发期挂包)、bridge-selftest(引擎自检)
|
|
289
|
-
├── test/ node --test(
|
|
291
|
+
├── test/ node --test(105 个用例;缺引擎环境时自动 skip)
|
|
290
292
|
├── .github/workflows/ publish.yml:npm 可信发布(OIDC,可选)
|
|
291
293
|
└── docs/ 配置全表、性能、排错、插件契约、实现决策
|
|
292
294
|
```
|
package/docs/configuration.md
CHANGED
|
@@ -46,11 +46,14 @@
|
|
|
46
46
|
|
|
47
47
|
## 三、云端复核的 Key 从哪来(不硬编码、不隐式继承)
|
|
48
48
|
|
|
49
|
-
1. `credentialRef
|
|
50
|
-
**继承环境 > `~/.dsh/.credentials.yaml` > `<cwd>/.env` > `$DSH_HOME/.env`**;
|
|
49
|
+
1. `credentialRef`(推荐;插件配置或面板「凭据名」两者取其一,面板默认 `DEEPSEEK_API_KEY`)→
|
|
50
|
+
`ctx.credentials.resolve(ref)`,优先级 **继承环境 > `~/.dsh/.credentials.yaml` > `<cwd>/.env` > `$DSH_HOME/.env`**;
|
|
51
51
|
解析到的值随请求走 **stdin** 下发,**不进 argv、不进子进程环境变量**,且每次操作重新解析(换 Key 下一次审计即生效)。
|
|
52
52
|
2. `llmApiKey`(字面值,次选)。
|
|
53
|
-
3.
|
|
53
|
+
3. 同名环境变量(名字来自 `credentialRef`);
|
|
54
|
+
4. 本地凭据回退文件 `$DSH_HOME/calibguard-credentials.json`(面板保存时若 DSH 版本不支持写凭据库,
|
|
55
|
+
会自动写到这里;仅本机、权限 0600,值不落共享设置文件);
|
|
56
|
+
5. `CG_GUI/gui-settings.json` 里的 `deepseek_api_key`(`useGuiSettings: true` 时生效)。
|
|
54
57
|
|
|
55
58
|
三者都没有 → 云端复核不可用、灰区转人工(安全降级)。桥接子进程**不会**去读宿主的
|
|
56
59
|
`DEEPSEEK_API_KEY`:它的环境已按 DSH 的 `scrubbedParentEnv` 清洗(`*KEY*` / `*PASSWORD*` /
|
package/docs/troubleshooting.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
| 每份文件都 `manual_review` + `CUDA error: no kernel image` | 设备定档失效:确认 `device` 未被强制成 `cuda` |
|
|
10
10
|
| 灰区全部转人工、`llm.status=api_error` | 没配凭据(预期降级)→ 设 `credentialRef`(推荐)或在 DSH Models 页配置 |
|
|
11
11
|
| `凭据引用 X 未配置` 告警 | `credentialRef` 指向的引用在 DSH 凭据库里没有值;在 Models 页填 Key 或用 `llmApiKey` |
|
|
12
|
+
| **提示"当前环境不支持写入 DSH 凭据库"** | ≥0.3.6 会自动回退到 `~/.dsh/calibguard-credentials.json`(0600,仅本机),面板会显示存储位置;清空即删该文件或到 DSH Models 页重新配置 |
|
|
12
13
|
| **面板保存 API Key 报「需要 credentialRef」** | 升级到 ≥0.3.3:面板「凭据名」直接填(如 `DEEPSEEK_API_KEY`)并保存,会记进共享设置;旧版本只能把 `credentialRef` 写进插件配置 |
|
|
13
14
|
| **审计结果在哪里 / 怎么清空** | 引擎 `reports/` 目录(默认 `<engineRoot>/reports`):每次审计生成 JSON + Markdown;面板结果区显示路径并有「清空结果」,工具返回 `report_md_path` / `report_json_path` |
|
|
14
15
|
| **每次审计都提示"首次会加载模型"** | 0.3.3 起仅在模型未加载时提示;加载后不再出现,顶部状态显示「模型 已加载」 |
|
package/lib/client.js
CHANGED
|
@@ -406,7 +406,7 @@ window.__ModuleLoader__.load({
|
|
|
406
406
|
writable: Boolean(payload && payload.credentialWritable),
|
|
407
407
|
configured: Boolean(llm.configured),
|
|
408
408
|
})
|
|
409
|
-
setCredName(function (prev) { return prev || (payload && payload.credentialRef) || '' })
|
|
409
|
+
setCredName(function (prev) { return prev || (payload && payload.credentialRef) || 'DEEPSEEK_API_KEY' })
|
|
410
410
|
setForm(function (current) {
|
|
411
411
|
var next = update(current, 'llmEnabled', llm.enabled !== false)
|
|
412
412
|
next = update(next, 'allowThreshold', gate.allowThreshold !== undefined ? gate.allowThreshold : current.allowThreshold)
|
|
@@ -490,7 +490,11 @@ window.__ModuleLoader__.load({
|
|
|
490
490
|
}).then(function (outcome) {
|
|
491
491
|
setApiKey('')
|
|
492
492
|
if (outcome && outcome.credentialRef) setCredName(outcome.credentialRef)
|
|
493
|
-
|
|
493
|
+
var storage = outcome && outcome.credentialStorage
|
|
494
|
+
setSettingsMsg(storage === 'local-file'
|
|
495
|
+
? '已保存 ✓ 参数共用;当前 DSH 版本不支持从插件写入凭据库,API Key 已保存到本机文件 '
|
|
496
|
+
+ '(~/.dsh/calibguard-credentials.json,仅本机可见)'
|
|
497
|
+
: '已保存 ✓ 参数共用;API Key 已写入 DSH 凭据库(' + ((outcome && outcome.credentialRef) || credentialName) + ')')
|
|
494
498
|
refreshStatus()
|
|
495
499
|
loadSettings()
|
|
496
500
|
}).catch(function (err) {
|
|
@@ -631,6 +635,7 @@ window.__ModuleLoader__.load({
|
|
|
631
635
|
|
|
632
636
|
var envInfo = status && status.environment ? status.environment : null
|
|
633
637
|
var engineInfo = status && status.status ? status.status : null
|
|
638
|
+
var envPreparing = Boolean(envInfo) && !engineInfo && envInfo.state !== 'failed'
|
|
634
639
|
var reportDir = (engineInfo && engineInfo.reportDir)
|
|
635
640
|
|| (status && status.settings && status.settings.reportDir)
|
|
636
641
|
|| (result && result.config && result.config.reportDir)
|
|
@@ -657,8 +662,16 @@ window.__ModuleLoader__.load({
|
|
|
657
662
|
+ ' | 云端复核 ' + (engineInfo.llm.enabled ? (engineInfo.llm.configured ? '可用' : '已启用但无凭据') : '关闭')
|
|
658
663
|
: (envFailed ? '环境准备失败' : (envBusy ? '首次运行:正在准备引擎与依赖…' : '状态读取中…'))
|
|
659
664
|
|
|
660
|
-
|
|
661
|
-
|
|
665
|
+
// 环境准备中自动刷新(装完后无需手动点「刷新状态」)
|
|
666
|
+
useEffect(function () {
|
|
667
|
+
if (!envPreparing) return undefined
|
|
668
|
+
var timer = setInterval(refreshStatus, 4000)
|
|
669
|
+
return function () { clearInterval(timer) }
|
|
670
|
+
}, [envPreparing])
|
|
671
|
+
|
|
672
|
+
function renderPage() {
|
|
673
|
+
return jsx('div', {
|
|
674
|
+
style: S.page,
|
|
662
675
|
children: jsxs(Fragment, {
|
|
663
676
|
children: [
|
|
664
677
|
// ---------- 顶部 ----------
|
|
@@ -706,7 +719,10 @@ window.__ModuleLoader__.load({
|
|
|
706
719
|
],
|
|
707
720
|
}),
|
|
708
721
|
torchState === 'done'
|
|
709
|
-
? jsx('div', {
|
|
722
|
+
? jsx('div', {
|
|
723
|
+
style: { color: C.allow, fontSize: 12, marginTop: 6 },
|
|
724
|
+
children: 'torch 重装完成(面板已自动刷新;若顶部仍显示 CPU,点「刷新状态」或重启 DSH 即可)',
|
|
725
|
+
})
|
|
710
726
|
: null,
|
|
711
727
|
torchState === 'failed'
|
|
712
728
|
? jsx('div', {
|
|
@@ -1023,7 +1039,8 @@ window.__ModuleLoader__.load({
|
|
|
1023
1039
|
style: S.note,
|
|
1024
1040
|
children: '设置保存在本地 ' + (settingsFile || '$DSH_HOME/calibguard-settings.json')
|
|
1025
1041
|
+ '(GUI 与 AI 工具共用);API Key 写入 DSH 凭据库,不落此文件。'
|
|
1026
|
-
+ '
|
|
1042
|
+
+ '「凭据名」只是保存位置的名字(默认 DEEPSEEK_API_KEY,也会记住你填的名字);'
|
|
1043
|
+
+ 'API Key 优先写 DSH 凭据库,当前 DSH 版本不支持时自动保存到本机文件。'
|
|
1027
1044
|
+ '审计结果(JSON + Markdown)保存在引擎的 reports 目录,见下方「审计结果」。',
|
|
1028
1045
|
}),
|
|
1029
1046
|
jsxs('div', {
|
|
@@ -1195,6 +1212,9 @@ window.__ModuleLoader__.load({
|
|
|
1195
1212
|
],
|
|
1196
1213
|
}),
|
|
1197
1214
|
})
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
return renderPage()
|
|
1198
1218
|
}
|
|
1199
1219
|
|
|
1200
1220
|
// ============================== 插件入口 ==============================
|
package/lib/engine.js
CHANGED
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
* stdin 管道关闭 → 桥接读到 EOF 自行退出,不留孤儿进程。
|
|
23
23
|
*/
|
|
24
24
|
import { spawn } from 'node:child_process'
|
|
25
|
-
import { existsSync } from 'node:fs'
|
|
26
|
-
import { join } from 'node:path'
|
|
25
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
26
|
+
import { dirname, join } from 'node:path'
|
|
27
27
|
import { fileURLToPath } from 'node:url'
|
|
28
28
|
|
|
29
29
|
import { buildChildEnv } from './env.js'
|
|
30
30
|
import { toBridgeOptions } from './config.js'
|
|
31
31
|
import { EnvironmentBootstrap, venvPythonPath } from './bootstrap.js'
|
|
32
|
+
import { dshHome } from './settings.js'
|
|
32
33
|
|
|
33
34
|
/** 桥接脚本相对本文件的位置(spawn 需要真实路径,不能直接把 URL 对象交给子进程)。 */
|
|
34
35
|
const BRIDGE_SCRIPT = fileURLToPath(new URL('../bridge/calibguard_bridge.py', import.meta.url))
|
|
@@ -44,6 +45,38 @@ const ENV_WAIT_MS = 60_000
|
|
|
44
45
|
/** stderr 环形缓冲行数:崩溃时把最后几行回给调用方,便于定位。 */
|
|
45
46
|
const STDERR_TAIL_LINES = 20
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* 本地凭据回退文件:某些 DSH 版本的 ctx.credentials 没有 「set」(无法从插件写入凭据库),
|
|
50
|
+
* 此时把 Key 写到 $DSH_HOME/calibguard-credentials.json(仅本机、权限尽量收紧),
|
|
51
|
+
* 避免「面板根本配不了 Key」。优先仍走 DSH 凭据库;文件只是兼容兜底。
|
|
52
|
+
*/
|
|
53
|
+
function localCredentialFile() {
|
|
54
|
+
return join(dshHome(), 'calibguard-credentials.json')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readLocalCredentials() {
|
|
58
|
+
try {
|
|
59
|
+
const raw = JSON.parse(readFileSync(localCredentialFile(), 'utf8'))
|
|
60
|
+
return raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {}
|
|
61
|
+
} catch {
|
|
62
|
+
return {}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readLocalCredential(ref) {
|
|
67
|
+
const value = readLocalCredentials()[ref]
|
|
68
|
+
return typeof value === 'string' && value ? value : undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function writeLocalCredential(ref, value) {
|
|
72
|
+
const file = localCredentialFile()
|
|
73
|
+
const data = { ...readLocalCredentials(), [ref]: value }
|
|
74
|
+
mkdirSync(dirname(file), { recursive: true })
|
|
75
|
+
writeFileSync(file, JSON.stringify(data, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 })
|
|
76
|
+
try { chmodSync(file, 0o600) } catch { /* Windows 上可能不支持,忽略 */ }
|
|
77
|
+
return file
|
|
78
|
+
}
|
|
79
|
+
|
|
47
80
|
export class CalibGuardEngine {
|
|
48
81
|
/**
|
|
49
82
|
* @param {object} options
|
|
@@ -161,22 +194,19 @@ export class CalibGuardEngine {
|
|
|
161
194
|
const ref = this.effectiveCredentialRef()
|
|
162
195
|
if (!ref) {
|
|
163
196
|
this.lastCredential = { source: 'none', configured: false }
|
|
164
|
-
/** 面板「重装 torch」任务状态(CPU 版 → GPU 版一键替换用)。 */
|
|
165
|
-
this.torchJob = { state: 'idle', step: '', message: '', tail: [], startedAt: null, finishedAt: null, error: null }
|
|
166
|
-
this.torchChild = null
|
|
167
197
|
return { key: undefined, source: 'none' }
|
|
168
198
|
}
|
|
169
199
|
|
|
200
|
+
let lookedUpInStore = false
|
|
170
201
|
if (this.credentials?.resolve) {
|
|
202
|
+
lookedUpInStore = true
|
|
171
203
|
try {
|
|
172
204
|
const resolved = await this.credentials.resolve(ref)
|
|
173
205
|
if (resolved?.value) {
|
|
174
206
|
this.lastCredential = { source: `credentials:${resolved.source ?? 'unknown'}`, configured: true }
|
|
175
207
|
return { key: resolved.value, source: this.lastCredential.source }
|
|
176
208
|
}
|
|
177
|
-
this.logger?.warn?.(`[calibguard] 凭据引用 ${ref}
|
|
178
|
-
this.lastCredential = { source: 'credentials:missing', configured: false }
|
|
179
|
-
return { key: undefined, source: this.lastCredential.source }
|
|
209
|
+
this.logger?.warn?.(`[calibguard] 凭据引用 ${ref} 未在 DSH 凭据库中找到,继续尝试环境变量/本地凭据文件`)
|
|
180
210
|
} catch (error) {
|
|
181
211
|
this.logger?.warn?.(`[calibguard] 解析凭据 ${ref} 失败:${error?.message ?? error}`)
|
|
182
212
|
}
|
|
@@ -188,8 +218,16 @@ export class CalibGuardEngine {
|
|
|
188
218
|
this.lastCredential = { source: `env:${ref}`, configured: true }
|
|
189
219
|
return { key: fromEnv, source: this.lastCredential.source }
|
|
190
220
|
}
|
|
191
|
-
|
|
192
|
-
|
|
221
|
+
|
|
222
|
+
// 最后兜底:插件自己写的本地凭据文件(DSH 版本不支持从插件写凭据库时用)
|
|
223
|
+
const fromLocal = readLocalCredential(ref)
|
|
224
|
+
if (fromLocal) {
|
|
225
|
+
this.lastCredential = { source: 'local-file', configured: true }
|
|
226
|
+
return { key: fromLocal, source: 'local-file' }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
this.lastCredential = { source: lookedUpInStore ? 'credentials:missing' : 'unavailable', configured: false }
|
|
230
|
+
return { key: undefined, source: this.lastCredential.source }
|
|
193
231
|
}
|
|
194
232
|
|
|
195
233
|
/**
|
|
@@ -212,10 +250,20 @@ export class CalibGuardEngine {
|
|
|
212
250
|
throw new Error('还没有凭据名:请在面板「凭据名」里填一个(如 DEEPSEEK_API_KEY),'
|
|
213
251
|
+ '或在插件配置里设置 credentialRef。凭据名不是密钥本身,只是"保存位置"的名字。')
|
|
214
252
|
}
|
|
215
|
-
|
|
216
|
-
|
|
253
|
+
// 优先写入 DSH 凭据库;当前 DSH 版本不支持时,退化为本地凭据文件(仅本机、0600)
|
|
254
|
+
let storage = 'dsh-credentials'
|
|
255
|
+
if (typeof this.credentials?.set === 'function') {
|
|
256
|
+
try {
|
|
257
|
+
await this.credentials.set(ref, value.trim())
|
|
258
|
+
} catch (error) {
|
|
259
|
+
this.logger?.warn?.(`[calibguard] 写入 DSH 凭据库失败(${error?.message ?? error}),改用本地凭据文件`)
|
|
260
|
+
storage = 'local-file'
|
|
261
|
+
writeLocalCredential(ref, value.trim())
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
storage = 'local-file'
|
|
265
|
+
writeLocalCredential(ref, value.trim())
|
|
217
266
|
}
|
|
218
|
-
await this.credentials.set(ref, value.trim())
|
|
219
267
|
// 记住这个名字(只存名字,不存密钥),下次启动后审计仍能找到它
|
|
220
268
|
if (this.settingsStore) {
|
|
221
269
|
try {
|
|
@@ -224,9 +272,9 @@ export class CalibGuardEngine {
|
|
|
224
272
|
this.logger?.warn?.(`[calibguard] 记住凭据名失败(不影响本次写入):${error?.message ?? error}`)
|
|
225
273
|
}
|
|
226
274
|
}
|
|
227
|
-
this.lastCredential = { source:
|
|
228
|
-
this.logger?.info?.(`[calibguard] 已写入凭据引用 ${ref}
|
|
229
|
-
return { ref, saved: true }
|
|
275
|
+
this.lastCredential = { source: storage, configured: true }
|
|
276
|
+
this.logger?.info?.(`[calibguard] 已写入凭据引用 ${ref}(存储:${storage},值不落日志)`)
|
|
277
|
+
return { ref, saved: true, storage }
|
|
230
278
|
}
|
|
231
279
|
|
|
232
280
|
/**
|
|
@@ -624,7 +672,8 @@ export class CalibGuardEngine {
|
|
|
624
672
|
lastExit: this.lastExit,
|
|
625
673
|
credential: { ...this.lastCredential },
|
|
626
674
|
credentialRef: this.effectiveCredentialRef() || null,
|
|
627
|
-
credentialWritable:
|
|
675
|
+
credentialWritable: true,
|
|
676
|
+
credentialStorage: typeof this.credentials?.set === 'function' ? 'dsh-credentials' : 'local-file',
|
|
628
677
|
environment: this.bootstrap.snapshot(),
|
|
629
678
|
torchJob: this.torchJobSnapshot(),
|
|
630
679
|
gpuTorch: this.gpuTorchStatus(),
|
package/lib/routes.js
CHANGED
|
@@ -148,6 +148,7 @@ export function createCalibGuardRoutes({ engine, config, logger, settingsStore }
|
|
|
148
148
|
credential: diagnostics.credential,
|
|
149
149
|
credentialRef: diagnostics.credentialRef ?? null,
|
|
150
150
|
credentialWritable: Boolean(diagnostics.credentialWritable),
|
|
151
|
+
credentialStorage: diagnostics.credentialStorage ?? null,
|
|
151
152
|
setupScript,
|
|
152
153
|
torchJob: typeof engine.torchJobSnapshot === 'function' ? engine.torchJobSnapshot() : null,
|
|
153
154
|
gpuTorch: typeof engine.gpuTorchStatus === 'function' ? engine.gpuTorchStatus() : null,
|
|
@@ -164,6 +165,7 @@ export function createCalibGuardRoutes({ engine, config, logger, settingsStore }
|
|
|
164
165
|
credential: diagnostics.credential,
|
|
165
166
|
credentialRef: diagnostics.credentialRef ?? null,
|
|
166
167
|
credentialWritable: Boolean(diagnostics.credentialWritable),
|
|
168
|
+
credentialStorage: diagnostics.credentialStorage ?? null,
|
|
167
169
|
setupScript,
|
|
168
170
|
settings: engine.settings(),
|
|
169
171
|
settingsFile: settingsStore?.file ?? null,
|
|
@@ -181,6 +183,7 @@ export function createCalibGuardRoutes({ engine, config, logger, settingsStore }
|
|
|
181
183
|
credential: diagnostics.credential,
|
|
182
184
|
credentialRef: diagnostics.credentialRef ?? null,
|
|
183
185
|
credentialWritable: Boolean(diagnostics.credentialWritable),
|
|
186
|
+
credentialStorage: diagnostics.credentialStorage ?? null,
|
|
184
187
|
setupScript,
|
|
185
188
|
torchJob: typeof engine.torchJobSnapshot === 'function' ? engine.torchJobSnapshot() : null,
|
|
186
189
|
gpuTorch: typeof engine.gpuTorchStatus === 'function' ? engine.gpuTorchStatus() : null,
|
|
@@ -213,7 +216,13 @@ export function createCalibGuardRoutes({ engine, config, logger, settingsStore }
|
|
|
213
216
|
}
|
|
214
217
|
const result = await engine.setCredential(apiKey, name || undefined)
|
|
215
218
|
const ref = result && result.ref ? result.ref : null
|
|
216
|
-
sendJson(res, 200, {
|
|
219
|
+
sendJson(res, 200, {
|
|
220
|
+
ok: true,
|
|
221
|
+
...result,
|
|
222
|
+
credentialRef: ref,
|
|
223
|
+
credentialStorage: result && result.storage ? result.storage : null,
|
|
224
|
+
settings: engine.settings(),
|
|
225
|
+
})
|
|
217
226
|
sendJson(res, 200, { ok: true, ...result })
|
|
218
227
|
} catch (error) {
|
|
219
228
|
sendJson(res, 400, { error: String(error?.message ?? error) })
|