dsh-jev-guard 0.5.2 → 0.5.3

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.
@@ -7,11 +7,24 @@
7
7
  * (pre-execute → guards → execute → post-execute → result),看最终给出的
8
8
  * `ToolExecutionResult` 是不是我们要的拒绝/放行。
9
9
  *
10
- * 运行方式(必须在 deepseek-harness 目录树内运行,否则解析不到 @deepseek-ai/*):
10
+ * 运行方式:这个测试只依赖两件事,而它们必须**同时**成立
11
+ * ① 裸 `@deepseek-ai/*` 能解析 —— 注意 ESM 是按**文件自身所在目录**向上找 node_modules 的(不是
12
+ * cwd),所以在 pnpm 工作区检出里必须让本文件待在**包目录**里(如 `apps/cli`);
13
+ * ② `./packages/util/values/lib/index.js` 存在(第 6 项拿 DSH 自己的 `snapshotJsonValue` 校验
14
+ * notice 形状,那是 `Session.append` 之前的一步)—— 这一条按 **cwd** 算,只有检出**根**满足。
15
+ * 检出的两个目录各满足一条,所以做法是搭一个临时目录把两者接过来(2026-09-23 实测:6/6;带密钥 7/7):
11
16
  *
12
- * cp tools/smoke-dsh-pipeline.mjs /home/user/deepseek-harness/.tmp-guard-pipeline.mjs
13
- * cd /home/user/deepseek-harness && node .tmp-guard-pipeline.mjs
14
- * rm /home/user/deepseek-harness/.tmp-guard-pipeline.mjs
17
+ * H=<DSH 检出>; T=/tmp/jev-guard-pipeline; rm -rf $T; mkdir -p $T/node_modules/@deepseek-ai
18
+ * for p in cordis dsh-tools dsh-session dsh-system-prompt; do
19
+ * ln -s $H/apps/cli/node_modules/@deepseek-ai/$p $T/node_modules/@deepseek-ai/$p; done
20
+ * ln -s $H/packages $T/packages
21
+ * cp <本文件> $T/ && cd $T && JEV_GUARD_ROOT=/mnt/t/jev-guard node smoke-dsh-pipeline.mjs
22
+ * # 加 TYPESAFE_API_KEY 则第 4 项会真的走一次联网判定(7/7)
23
+ *
24
+ * 三个**别照抄**的旧配方:从检出根直接跑会以 `ERR_MODULE_NOT_FOUND` 崩在下面的 import 上;
25
+ * 从 `packages/core/agent-loop` 跑则第 6 项会假报 FAIL(那个 cwd 下没有 `./packages/...`);
26
+ * 用绝对路径跑本文件(不复制)同样在 import 那一步就崩 —— 裸说明符不认 cwd。
27
+ * 测试自身不写真实 `~/.jev-guard/`:日志与降级状态都钉在临时目录里(与 `smoke-dsh-adapter` 同规矩)。
15
28
  *
16
29
  * @module jev-guard/tools/smoke-dsh-pipeline
17
30
  */
@@ -20,11 +33,17 @@ import { Context } from '@deepseek-ai/cordis'
20
33
  import ToolRuntime, { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
21
34
  import SessionStore from '@deepseek-ai/dsh-session'
22
35
  import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
36
+ import { mkdtemp, rm } from 'node:fs/promises'
37
+ import { tmpdir } from 'node:os'
38
+ import { join } from 'node:path'
23
39
  import { pathToFileURL } from 'node:url'
24
40
 
25
41
  const ROOT = process.env.JEV_GUARD_ROOT ?? '/mnt/t/dsh-jev-guard'
26
42
  const mod = await import(`${ROOT}/adapters/dsh/index.js`)
27
43
 
44
+ /** 审计日志与降级状态都落在这里 —— 跑测试不该改动本机真实的阀门状态。 */
45
+ const SANDBOX = await mkdtemp(join(tmpdir(), 'jev-guard-pipeline-'))
46
+
28
47
  let failures = 0
29
48
  let checks = 0
30
49
 
@@ -86,9 +105,11 @@ function expect(label, actual, wantError, needles = []) {
86
105
 
87
106
  async function main() {
88
107
  const hasKey = Boolean(process.env.TYPESAFE_API_KEY)
89
- process.stdout.write(`DSH 工具管线集成测试 root=${ROOT} ${hasKey ? '(含联网)' : '(无密钥)'}\n\n`)
108
+ process.stdout.write(`DSH 工具管线集成测试 root=${ROOT} sandbox=${SANDBOX} ${hasKey ? '(含联网)' : '(无密钥)'}\n\n`)
90
109
 
91
- const ctx = await harness({ tools: ['bash'], inlineScripts: false })
110
+ // 日志/状态钉在临时目录:真实 `~/.jev-guard/` 不该因为跑一次测试而被写入(与 smoke-dsh-adapter 同规矩)。
111
+ const paths = { logPath: join(SANDBOX, 'guard.log'), degradedPath: join(SANDBOX, 'degraded.json') }
112
+ const ctx = await harness({ tools: ['bash'], inlineScripts: false, ...paths })
92
113
 
93
114
  // 1. 只读命令:预筛放行 → 假工具真的被执行
94
115
  expect('ls -la → 真的执行(管线走通)', await run(ctx, 'ls -la'), false, ['RAN: ls -la'])
@@ -115,7 +136,7 @@ async function main() {
115
136
  }
116
137
 
117
138
  // 5. 非目标工具不受影响
118
- const ctx2 = await harness({ tools: ['read'], inlineScripts: false })
139
+ const ctx2 = await harness({ tools: ['read'], inlineScripts: false, ...paths })
119
140
  expect('tools 不匹配 → 放行', await run(ctx2, 'ls -la'), false, ['RAN: ls -la'])
120
141
 
121
142
  // 6. 会话内 notice 的形状:交给 DSH 自己的 JSON 快照校验(真实 Session.append 之前那一步)
@@ -139,6 +160,17 @@ async function main() {
139
160
  if (!noticeOk) failures += 1
140
161
  process.stdout.write(`${noticeOk ? 'ok ' : 'FAIL'} notice 通过 DSH 自己的 snapshotJsonValue(Session.append 之前那一步)\n ${noticeWhy}\n`)
141
162
 
163
+ // 收尾与 smoke-dsh-adapter 同规矩:先等审计队列落盘(record() 是 fire-and-forget,
164
+ // 不等它 process.exit 会丢尾部记录),再把整个临时目录删掉。
165
+ // 审计模块从 ROOT 动态导入 —— 本文件可能被复制到临时目录里运行,相对路径在那里是无效的。
166
+ try {
167
+ const audit = await import(`${ROOT}/lib/audit.js`)
168
+ await audit.flush()
169
+ } catch {
170
+ // 清理失败不该改变测试结论
171
+ }
172
+ await rm(SANDBOX, { recursive: true, force: true }).catch(() => {})
173
+
142
174
  process.stdout.write(`\n${failures === 0 ? `全部通过(${checks} 组断言)` : `${failures} 组失败 / 共 ${checks} 组`}\n`)
143
175
  process.exit(failures === 0 ? 0 : 1)
144
176
  }