dsh-sensitive-guard 0.0.0-stage → 0.3.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,91 @@
1
+ # 变更记录
2
+
3
+ ## 0.3.2
4
+
5
+ - `exports` 增加 `./package.json`,修掉 `require('dsh-sensitive-guard/package.json')` 报 `ERR_PACKAGE_PATH_NOT_EXPORTED`。
6
+ - README 安装章节按 npm 发版更新:给出从 npm 装的版本号写法,`link:` 降为本地开发用。
7
+
8
+ ## 0.3.1
9
+
10
+ - 设置页不再只显示「载入失败」:被拒时展示说明卡,含当前访问地址、数据目录路径,以及**按来源算好的** `allowCidrs` 配置(IPv4 取 /24,IPv6 取 /64),可直接照抄。
11
+ - 未授权来源从"一律 403"改为**只读降级**:`/guard/vault` 返回条目结构(占位符、规则、时间、命中数)但**去掉 8 位预览与审计**;揭示与暂停仍然 403,并附带修复建议。
12
+ - 只读模式下小锁置灰,提示「需在服务所在的本机打开才能揭示」。
13
+ - `/guard/vault` 新增 `dataDir`,便于用户直接定位 `rules.json`。
14
+ - 自测 47 → 56 项,覆盖降级返回结构、预览被剔除、审计被清空、建议网段、拒绝仍带修复建议。
15
+
16
+ ## 0.3.0
17
+
18
+ - 新增 `allowCidrs`:默认仍**只允许本机回环**,显式配置网段后才允许局域网/远程访问三个 HTTP 接口(用于 `dsh web --host 192.168.x.x` 这类部署)。
19
+ - 非回环访问额外三道检查:`Host` 必须是 IP 字面量(挡 DNS rebinding)、POST 必须 `application/json`(强制 CORS 预检,挡跨站表单)、`Origin` 与 `Host` 同源。
20
+ - CIDR 支持 IPv4 / IPv6,兼容 `::ffff:` v4-mapped 地址;`/0` 直接拒绝并写入 `rules.error`,避免手滑全网开放。
21
+ - `/guard/vault` 新增 `access: { level, allowCidrs }`,设置页在局域网访问时显示橙色提示。
22
+ - 自测 34 → 47 项,覆盖网段放行、`/32` 不越界、rebinding、content-type 与 `/0` 拒绝。
23
+
24
+ ## 0.2.9
25
+
26
+ - 文档重构:原 README 把 9 个版本的 changelog 穿插在正文中间,识别范围与安装被切碎,安装章节掉到文件末尾。拆出本文件,README 按「快速开始 → 怎么用 → 原理 → 识别范围 → 配置 → 接口 → 边界 → 恢复 → 开发」重排。
27
+ - 补齐暂停开关、对话内小锁、模型取用凭据三段使用说明。
28
+
29
+ ## 0.2.8
30
+
31
+ - 新增设置页「暂停保护」:30 分钟 / 2 小时 / 到重启,切换即时生效,暂停期间三个钩子全部透传(不脱敏、不入库、不补洗)。
32
+ - `untilRestart` **不持久化**——重启即恢复保护,避免“忘了开回来”。
33
+ - 限时暂停写到 `pause.json`,重启后若未到期仍然有效;时长上限夹到 24 小时,防止手滑关一周。
34
+ - 新增 `GET/POST /guard/settings`(仅回环),暂停/恢复都记审计。
35
+ - 自测 28 → 34 项,覆盖暂停生效、恢复、回环限制。
36
+
37
+ ## 0.2.7
38
+
39
+ - 新增 `cli-password`:`mysql -uroot -pabc12345`、`redis-cli -a xxx` 这类命令行密码。规则限定在确实用 `-p`/`-a` 表示密码的客户端,`docker run -p 8080:80` 有专门用例保证不误伤。
40
+ - 新增可选熵值检测:`rules.json` 里 `"entropy": true` 开启,抓无前缀的随机串。默认关闭,限制见 README「识别范围」。
41
+ - 自测 21 → 24 项,新增命令行密码、`docker -p` 负向用例、性能基准(300k 字符扫描 / 2000 个密钥替换)、熵值开关。
42
+ - 补 `LICENSE`、`.gitignore`、GitHub Actions(ubuntu + windows × node 18/20/22)。
43
+ - host 端 `apply` 与正则预编译加兜底,避免半注册状态。
44
+
45
+ ## 0.2.6
46
+
47
+ - 小锁按类型上色:密码/口令→红、token/JWT/Bearer→紫、api key/密钥→绿、其它→蓝,展开时统一转橙。颜色依据 vault 里新增的 `label` 字段(命中的关键词),而不是规则 id——因为 `keyword-prose` 一条规则同时覆盖密码和 token,只看 id 分不出类型。
48
+ - 视觉收紧:去掉文字与按钮间的空隙,按钮缩到 18px,整体更像一个胶囊。
49
+ - 新增 `/guard/vault` 元数据缓存,客户端首次渲染时按需拉取一次(并发去重),给胶囊取色。
50
+
51
+ ## 0.2.5
52
+
53
+ - 修复对话内小锁「一次有、一次没有」的竞态:原来用 `ctx.get('uiConversation')` 只探测一次,冷启动时 conversation 包可能还没 provide 该服务,取到 `undefined` 后就直接放弃,整个会话都不会再出现锁。改为 `ctx.get` 快速路径 + `ctx.inject(['uiConversation'], …)` 声明式等待,服务就绪(或重建)时自动补注册。
54
+ - `inline reveal:` 新增 `waiting` 状态,表示正在等服务就绪。
55
+
56
+ ## 0.2.4
57
+
58
+ - `keyword-prose` 支持关键词后接下划线后缀:`SECRET_KEY=`、`DB_PASSWORD_HASH=` 这类环境变量名此前整条漏掉(`secret` 被 `_KEY` 粘住,边界和分隔符都对不上)。
59
+ - 新增 `bearer-token`:`Authorization: Bearer <不透明令牌>` 此前不命中(既无厂商前缀,也没有 `key: value` 结构)。
60
+
61
+ ## 0.2.3
62
+
63
+ - 保留厂商前缀:`sk-abc123` 遮成 `sk-<SG_…>`,`AKIA…` 遮成 `AKIA<SG_…>`。上下文里能看出是哪个服务的凭据,阅读不被打断;真正会认证的尾部仍然不出现。
64
+ - `/guard/reveal` 返回三个字段:`plaintext`(完整值,设置页与导出用)、`body`(去掉前缀的尾部,对话内胶囊用)、`prefix`。这样对话里点开不会变成 `sk-sk-abc123`。
65
+ - 修复对话内小锁不显示的真正原因:keyed slot 用 `key` 匹配,之前写成了 `id`,宿主按“无渲染器”静默退回原文。同时改为官方要求的 generator 注入(`function*` + `yield`)。
66
+ - 设置页底部新增 `inline reveal:` 状态行,不开 DevTools 也能定位。
67
+
68
+ ## 0.2.2
69
+
70
+ - 修复致命问题:客户端 `apply` 里访问 `ctx.uiConversation`(未在 `inject` 声明)会抛 `cannot get property … without inject`,被宿主 fail-fast 判为 entry 未激活,导致**整个应用启动失败**。现在只用 `ctx.get('uiConversation')` 且逐层 try/catch,`apply` 整体兜底。
71
+ - 规则新增 `keyword-prose`:支持 `我的 api key 是 sk-…` / `密码是 …` 这类自然语言写法(中文关键词与「是/为/就是/is」分隔符),此前只认 `=` 和 `:`,整句都被漏掉。
72
+
73
+ ## 0.2.1
74
+
75
+ - 新增:用户消息里的 `<SG_…>` 直接渲染为可点锁形胶囊,点击就地揭示明文(浏览器直连 `/guard/reveal`,不进模型上下文)。
76
+ - 修正 0.2.0 的错误结论:宿主确实提供 `ctx.uiConversation.userText` 注册 matcher、`conversation.user.text.node` 注册渲染器,且节点可自带 actions。
77
+
78
+ ## 0.2.0
79
+
80
+ - 修复 `llm/stream` 用 `async function` 返回 `Promise`,导致整轮崩在 `yield* ... is not async iterable`;改为 async generator,且只洗文本增量。
81
+ - 修复 `tools/post-execute` 一直在洗 `decision.content`(默认决策 `{kind:"accept"}` 没有该字段)导致脱敏空转;改为洗 `result.content`。
82
+ - `sensitive_guard_status` 不再返回 8 位预览,避免明文片段进入模型上下文。
83
+ - `/guard/vault`、`/guard/reveal` 增加回环来源校验(`0.0.0.0` 绑定时不再对局域网暴露)。
84
+ - 导出文件 15 分钟 TTL;保管库 500 条上限;写盘改为 250ms 合并。
85
+ - `webServer` 改为可选依赖,无 Web 服务时脱敏与工具照常工作。
86
+ - `vault.key` 在 Windows 上用 `icacls` 收紧 ACL;`loadKey` 并发创建改为复用胜者而不是 fail-open。
87
+ - 占位符偏移改用 `d` 标志的 `match.indices`,修复 URL 中用户名/密码相同时遮错位置。
88
+
89
+ ## 0.1.0
90
+
91
+ - 首个可用版本:三个钩子入口(`agent/pre-step`、`tools/post-execute`、`llm/stream`)、AES-256-GCM 保管库、设置页解锁、导出工具、两条回环路由。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wangxinyu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,225 @@
1
- # Temporary Holding Version
1
+ # dsh-sensitive-guard
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DeepSeek Harness(DSH)的插件,只做一件事:把密钥、令牌、密码挡在模型上下文之外。用户消息和工具结果里的凭证,在进入上下文之前就被换成 `<SG_xxxxxxxxxxxx>` 占位符,明文加密留在本机。模型拿到的是代号,你点一下能看到真值。
4
+
5
+ 代号里那 12 位十六进制是 `sha256(明文)` 的前缀,不可逆,只用来保证同一个密钥每次都是同一个代号。
6
+
7
+ ## 它往宿主里注册了什么
8
+
9
+ DSH 插件分 host 端和客户端两半,这个插件两半都占:host 端抓数据、管保管库、开接口;客户端加设置页和对话里的胶囊。
10
+
11
+ | 注册点 | 端 | 作用 |
12
+ | --- | --- | --- |
13
+ | `agent/pre-step` | host 钩子 | 用户输入进上下文前脱敏 |
14
+ | `tools/post-execute` | host 钩子 | 工具结果进上下文前脱敏 |
15
+ | `llm/stream` | host 钩子 | 出网的流式文本兜底清洗 |
16
+ | `sensitive_guard_status`、`sensitive_guard_export` | host 工具 | 查状态;把明文导出成 0600 临时文件 |
17
+ | `/guard/vault`、`/guard/reveal`、`/guard/settings` | HTTP 路由 | 设置页取数、揭示明文、暂停开关 |
18
+ | `settings.section` | 客户端 slot | 设置页里的「敏感保护」 |
19
+ | `conversation.user.text.node` | 客户端 slot | 对话里的锁形胶囊 |
20
+
21
+ 前提只要 `tools` 一个。`webServer` 不存在时脱敏照常工作,只是设置页和揭示用不了。
22
+
23
+ ## 长什么样
24
+
25
+ 遮住的时候,对话里是 `sk-` 后面跟一个锁:
26
+
27
+ ![对话里被遮住的凭证](https://raw.githubusercontent.com/wangxy496/dsh-sensitive-guard/main/docs/chat-masked.png)
28
+
29
+ 点一下锁,就地显示明文,20 秒后自动遮回:
30
+
31
+ ![点开后显示明文](https://raw.githubusercontent.com/wangxy496/dsh-sensitive-guard/main/docs/chat-revealed.png)
32
+
33
+ 设置页列出保管库里全部条目,行尾的锁同样能揭示:
34
+
35
+ ![设置页](https://raw.githubusercontent.com/wangxy496/dsh-sensitive-guard/main/docs/settings.png)
36
+
37
+ ## 安装
38
+
39
+ 要求 dsh 0.2.0 以上、Node 18 以上。Windows 11 和 Ubuntu 22.04 都实测过,macOS 没测。
40
+
41
+ 三种装法,按场景选。
42
+
43
+ **从 npm 装**(给使用者)。编辑 `~/.dsh/profiles/<profile>/package.json`:
44
+
45
+ ```json
46
+ {
47
+ "dependencies": {
48
+ "dsh-sensitive-guard": "0.3.2"
49
+ },
50
+ "dsh": {
51
+ "profile": {
52
+ "bundles": ["dsh-sensitive-guard"]
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ `bundles` 里保留原有条目,只追加这一项。然后在同目录跑 `pnpm install`,重启 dsh。
59
+
60
+ **让 dsh 自己装**(不想手改文件的场合)。在会话里说「安装这个目录下的插件:/绝对/路径」,它走 `install_bundle`。
61
+
62
+ **本地改代码用 link**。把版本号换成 `"link:/absolute/path/to/sensitive-guard"`,源码改动立刻生效,但 host 端的改动还是要重启进程。
63
+
64
+ ## 确认装好了
65
+
66
+ 发一句:
67
+
68
+ ```
69
+ 我的 api key 是 sk-proj-abcdefghijklmnopqrstuvwx
70
+ ```
71
+
72
+ 应该显示成 `我的 api key 是 sk-` 加一个锁形胶囊。点开能看到明文,20 秒后自动遮回。
73
+
74
+ 再看「设置 → 敏感保护」,最下面一行 `inline reveal:` 显示 `ok` 就正常,显示别的就翻到文末「卸载和故障」。
75
+
76
+ ## 日常怎么用
77
+
78
+ **对话里看明文。** 用户消息里的占位符会渲染成可点的胶囊,颜色按类型分:密码红、token 紫、key 绿、其它蓝,点开后统一变橙。点击走本机接口,明文不经过模型。
79
+
80
+ 只有用户消息能点。AI 回复和工具结果走另一套节点渲染,宿主没留「包一层」的口子,要接就得整体替换助手的消息渲染,宿主升级会白屏,所以没做。那些位置到设置页看。
81
+
82
+ **设置页。** 顶部是暂停开关,下面是保管库条目,每条都能点锁查看,揭示会记审计。暂停有三档:30 分钟、2 小时、到重启。连不上数据库、模型读不到密码的时候点一下就行,到期自动恢复。「到重启」不写盘,重启就恢复。
83
+
84
+ **模型怎么用凭据。** 它拿不到明文。要连库或者调接口时让它导出:
85
+
86
+ ```
87
+ sensitive_guard_export(<SG_xxx>) 明文写到 0600 文件,返回路径
88
+ 命令里用 $(cat /那个/路径) 明文不进上下文
89
+ ```
90
+
91
+ 工具只有两个。`sensitive_guard_status` 看状态和条目元数据,不含明文,连前 8 位预览都不返回。`sensitive_guard_export` 负责导出,文件 15 分钟后过期。
92
+
93
+ 常被当成 bug 的一点:模型读 `application.yml` 时看到的是占位符,拿它去连库必然失败。要么让它先导出,要么临时点个暂停。
94
+
95
+ ## 数据在哪
96
+
97
+ 三处是分开的。
98
+
99
+ | | 内容 |
100
+ | --- | --- |
101
+ | 模型上下文 | `<SG_1e62c5fe7944>`,哈希前缀,不可逆 |
102
+ | 磁盘 | `v1:iv:tag:密文`,AES-256-GCM,密钥在 `vault.key` |
103
+ | 屏幕 | 代号加锁,点开才向本机要明文 |
104
+
105
+ 拦截点有两个:用户输入走 `agent/pre-step`,工具结果走 `tools/post-execute`。另外 `llm/stream` 会洗一遍出网的流式文本做兜底,只处理 `text-delta` 和 `reasoning-delta`,`tool-call-delta` 放过,否则工具参数会变成占位符。
106
+
107
+ ## 认得出什么
108
+
109
+ 厂商前缀:`sk-`、`sk_`、`ghp_`、`glpat-`、`npm_`、`hf_`、`AKIA`、`ASIA`、`xox*-`、`AIza`、`whsec_`、`SG.`。PEM 私钥块、JWT、`Bearer` 令牌。
110
+
111
+ 赋值和环境变量:`password=`、`secret:`、`api_key:`、`SECRET_KEY=`、`DB_PASSWORD=`。
112
+
113
+ 中文说法:`数据库密码是…`、`我的 token 是…`。
114
+
115
+ 连接串:`mysql://user:pass@host`、`postgresql://…`、JDBC 参数里的 `password=`、`X-Api-Key:`。
116
+
117
+ 命令行:`mysql -uroot -pabc12345`、`redis-cli -a xxx`。只认这几个客户端,`docker run -p 8080:80` 不会被误伤。
118
+
119
+ **可选的熵值检测,默认关闭。** `rules.json` 里加 `"entropy": true` 才会开。它抓的是没有前缀的随机串,但只对 base64 或混合字符集有用(熵约 4.8 到 5.0 bits/char)。纯 hex 串的上限就是 4.0,永远够不到 4.5 的默认阈值,所以 `8f3a9b2c…` 这种开了也抓不到;把阈值降到 3.9 又会误伤英文长句。另外 base64 图片、commit hash、UUID 都会被判成高熵。
120
+
121
+ 抓不到的三类:自研格式的凭据,得自己往 `extraRules` 加正则;二进制和图片里的;被 base64 或 hex 二次编码搬运过的。
122
+
123
+ ## 配置
124
+
125
+ `<数据目录>/rules.json`,存盘后两秒内生效,不用重启。Windows 下是 `%USERPROFILE%\.dsh\sensitive-guard\`,Linux 下是 `~/.dsh/sensitive-guard/`。
126
+
127
+ ```json
128
+ {
129
+ "extraRules": [
130
+ { "id": "my-internal", "pattern": "X-WX-Token:\\s*([A-Za-z0-9]{20,})", "flags": "i", "group": 1 }
131
+ ],
132
+ "disabled": ["jwt"],
133
+ "outboundScrub": true,
134
+ "entropy": false,
135
+ "entropyThreshold": 4.5,
136
+ "allowCidrs": ["192.168.0.0/16"]
137
+ }
138
+ ```
139
+
140
+ `group` 是要遮挡的捕获组序号,0 表示整个匹配。自定义正则的性能自己负责,别写 `(a+)+`、`(.*)*` 这类结构,它跑在同步钩子里,回溯爆炸会卡住宿主。
141
+
142
+ `disabled` 里写规则 id 可以关掉单条内置规则。`outboundScrub: false` 会关掉出网补洗。
143
+
144
+ `allowCidrs` 默认是空数组,意思是只认本机。它管的是 HTTP 接口,下一节细说。
145
+
146
+ ## 从别的机器访问
147
+
148
+ 桌面版不用管这节。三个 HTTP 接口默认只接受本机回环,浏览器和 dsh 在同一台机器上,天然满足。
149
+
150
+ 只有一种情况会碰到:dsh 以 `--host 0.0.0.0` 或 `--host 192.168.x.x` 启动,你又从另一台机器的浏览器打开。这时来源不是回环,接口会拒。两个办法:
151
+
152
+ 一是在服务所在那台机器的浏览器里打开 `127.0.0.1:3080`,不用改任何配置。
153
+
154
+ 二是放行网段,在数据目录的 `rules.json` 里加一行:
155
+
156
+ ```json
157
+ { "allowCidrs": ["192.168.0.0/16"] }
158
+ ```
159
+
160
+ 存盘两秒生效。设置页顶栏会出现橙色提示,说明当前是从局域网进来的。
161
+
162
+ 没配的时候页面不会白屏,会降级成只读:能看到有哪些条目、什么类型、什么时候入库、命中几次,看不到值,也看不到 8 位预览。顶部给一张说明卡,写着当前地址和可以直接抄的配置,网段按你的来源地址算好了。
163
+
164
+ 放行之后除了网段匹配,还有三道检查,都不用配。Host 头必须是 IP 字面量,防 DNS rebinding,因为 rebinding 需要一个域名,而 Origin 和 Host 会一起被伪造,只靠同源校验分不出来。POST 必须是 `application/json`,这样跨站的 `text/plain` 表单发不进来,服务端也不应答 CORS 预检。最后 Origin 必须和 Host 同源。
165
+
166
+ 开放网段意味着这个网段里任何设备都能读到完整明文,不只是预览。只在可信内网开,用完把这行删掉。没做登录是因为加一套认证会让点小锁多两步,不如把网段收窄。
167
+
168
+ ## HTTP 接口
169
+
170
+ 四个接口,默认只接受本机回环。
171
+
172
+ | 方法 | 路径 | 说明 |
173
+ | --- | --- | --- |
174
+ | GET | `/guard/vault` | 面板数据,含 8 位预览和 `access` 当前级别;未授权来源返回 `restricted: true` 的只读版,不含预览和审计 |
175
+ | POST | `/guard/reveal` | `{placeholder}` 换 `{plaintext, body, prefix}`;未授权 403 |
176
+ | GET | `/guard/settings` | `{paused, untilRestart, until, remainingMs}`;未授权 403 |
177
+ | POST | `/guard/settings` | `{action:"pause", minutes:30}`、`{action:"pause", untilRestart:true}`、`{action:"resume"}`;未授权 403 |
178
+
179
+ ## 已知限制
180
+
181
+ 1. 对话里的胶囊只覆盖用户消息,原因见「日常怎么用」。
182
+ 2. 不做出口改写。平台的 `tools/pre-execute` 只有 allow、deny、cancel、ask 四个动作,没有改参数的挂点,所以用凭据只能走导出文件。
183
+ 3. 保管库防的是误读和落盘明文,不防已经拿到你账户权限的人。加密是 AES-256-GCM,密钥文件在 Windows 上用 `icacls` 收紧到当前账户。
184
+ 4. 历史会话不受保护。`pre-step` 在宿主把用户原文写进日志之后才执行,`session/event` 又只是个观察者,插件没有机会在落盘前改。旧会话里的明文还在磁盘上,那批密钥该轮换。
185
+ 5. `exports/` 下是唯一落盘的明文,15 分钟后在下次导出时清理。
186
+ 6. 保管库最多留 500 条,超出按最后命中时间淘汰。
187
+ 7. 保管库坏掉时会放行,保证对话不中断,但会计数,在 `sensitive_guard_status` 里能看到 `failOpen`。
188
+ 8. 单条文本只扫前 200000 字符。钩子是同步跑的,防止自定义正则的回溯拖死宿主,代价是超长文本尾部不遮挡。
189
+ 9. `allowCidrs` 建立在「网段可信」这个假设上。它挡 CSRF 和 DNS rebinding,不挡网段内的主动攻击者。别写 `0.0.0.0/0`,那个值会被拒绝并记进 `rules.error`。
190
+ 10. 未授权来源仍然能读到元数据:有哪些条目、什么规则、什么时候入库、命中几次。这是为了让设置页能自我解释,代价是同网段的人能推断出「这里有 12 条凭据、分别是钥匙密码令牌」,但拿不到值。
191
+ 11. 宿主是 fail-fast 的,客户端 entry 在模块顶层抛错会让整个应用起不来。`apply` 已经整体兜底,顶层风险消不掉。
192
+
193
+ ## 卸载和故障
194
+
195
+ 正常卸载:编辑 `~/.dsh/profiles/<profile>/package.json`,删掉 `dependencies` 里的 `"dsh-sensitive-guard": "link:…"`,以及 `dsh.profile.bundles` 数组里的 `"dsh-sensitive-guard"`,重开应用。
196
+
197
+ 如果应用打不开了,日志里是 `web boot: N entry did not activate` 加 `dsh-sensitive-guard: failed`,先结束残留进程:
198
+
199
+ ```
200
+ taskkill /F /IM "DeepSeek Harness.exe"
201
+ ```
202
+
203
+ 再按上面的步骤摘掉依赖。崩溃日志在 `%APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-*-web-boot.log`。
204
+
205
+ `inline reveal:` 那行的取值:`ok` 正常,`waiting` 还在等 `uiConversation` 服务,`no-service` 拿不到服务,`matcher-failed` 规则注册被拒,`renderer-failed` 渲染器注册被拒。
206
+
207
+ 数据目录在 `$DSH_PROFILE_DIR/sensitive-guard/`,包含 `vault.json`、`vault.key`、`pause.json`、`rules.json`、`exports/`。没设这个变量时回落到 `$DSH_HOME` 下。
208
+
209
+ ## 开发
210
+
211
+ ```bash
212
+ node test.mjs
213
+ ```
214
+
215
+ 56 项离线自测,用临时目录,不碰真实保管库。
216
+
217
+ | 文件 | 作用 |
218
+ | --- | --- |
219
+ | `index.js` | host 端:三条钩子、保管库、两个工具、三条路由 |
220
+ | `client.js` | 客户端:设置页、对话内胶囊、暂停开关 |
221
+ | `test.mjs` | 自测 |
222
+ | `CHANGELOG.md` | 变更记录 |
223
+ | `locale/` | 插件市场用的中英元数据 |
224
+
225
+ GitHub Actions 在 ubuntu 和 windows 上跑 node 18、20、22 三个版本。许可证 MIT。