dsh-flakefinder 0.3.0 → 0.3.1

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 CHANGED
@@ -22,7 +22,7 @@ Frameworks: vitest / jest / pytest (setup.cfg / pyproject / pytest.ini / tox.ini
22
22
 
23
23
  ## Compatibility
24
24
 
25
- Verified against `@deepseek-ai/dsh@0.1.1-rc.2` on 2026-08-26. Built for the cordis patch-bundle plugin model (`cordis.patch.yml` + `dsh.bundle.patch`). No runtime imports of `@deepseek-ai/*` internals.
25
+ Verified against source-run `@deepseek-ai/dsh@0.1.2-alpha.4` on 2026-09-02. Built for the cordis patch-bundle plugin model (`cordis.patch.yml` + `dsh.bundle.patch`). No runtime imports of `@deepseek-ai/*` internals.
26
26
 
27
27
  ## Install
28
28
 
@@ -60,4 +60,4 @@ pnpm test
60
60
  pnpm typecheck
61
61
  ```
62
62
 
63
- MIT
63
+ MIT
package/README.md CHANGED
@@ -28,7 +28,7 @@ DeepSeek Harness 测试稳定性插件:支持 vitest / jest / pytest / node:te
28
28
 
29
29
  ## 兼容性
30
30
 
31
- 在 `@deepseek-ai/dsh@0.1.1-rc.2` 上验证(2026-08-26)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
31
+ 在 `@deepseek-ai/dsh@0.1.2-alpha.4` 源码模式下验证(2026-09-02)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
32
32
 
33
33
  ## 安装
34
34
 
@@ -113,4 +113,4 @@ Agent:
113
113
 
114
114
  ## License
115
115
 
116
- MIT
116
+ MIT
package/lib/index.d.ts CHANGED
@@ -19,12 +19,11 @@ export interface FlakePluginContext {
19
19
  spawn: SubprocessSpawnLike;
20
20
  };
21
21
  tools: {
22
- register(definition: FlakeToolDefinition, options?: {
23
- prepend?: boolean;
24
- }): () => void;
22
+ register(definition: FlakeToolDefinition): () => void;
25
23
  };
26
- get?(name: 'approval'): unknown;
27
- on?(event: string, listener: () => void): () => void;
24
+ on?(event: string, listener: (...args: any[]) => unknown, options?: {
25
+ prepend?: boolean;
26
+ }): (() => void) | void;
28
27
  }
29
28
  /**
30
29
  * 插件入口:解析配置、封装执行器与存储、注册五个工具。
package/lib/index.js CHANGED
@@ -31,17 +31,18 @@ export function apply(ctx, config) {
31
31
  const tools = buildFlakeTools(cfg, runner, store);
32
32
  const disposers = [];
33
33
  for (const definition of tools) {
34
- const wrapped = { ...definition };
35
- if (wrapped.gate !== undefined) {
36
- const original = wrapped.gate.bind(wrapped);
37
- wrapped.gate = async (exec, next) => {
38
- const record = (typeof exec === 'object' && exec !== null ? exec : {});
39
- return original({ ...record, approval: ctx.get?.('approval') }, next);
40
- };
41
- }
42
- disposers.push(ctx.tools.register(wrapped, { prepend: true }));
34
+ disposers.push(ctx.tools.register(definition));
43
35
  }
44
36
  if (typeof ctx.on === 'function') {
37
+ if (cfg.writeApproval) {
38
+ ctx.on('tools/pre-execute', async (exec, next) => {
39
+ if (exec.name === 'flaky_quarantine')
40
+ return { kind: 'ask', reason: '把测试用例写入 .flakefinder.json 隔离清单' };
41
+ if (exec.name === 'flaky_clear')
42
+ return { kind: 'ask', reason: '从 .flakefinder.json 隔离清单移除测试用例' };
43
+ return next();
44
+ }, { prepend: true });
45
+ }
45
46
  ctx.on('dispose', () => {
46
47
  for (const dispose of disposers)
47
48
  dispose();
package/lib/tools.d.ts CHANGED
@@ -24,7 +24,6 @@ export interface FlakeToolDefinition {
24
24
  render(args: unknown, value: unknown): ContentBlock[];
25
25
  };
26
26
  execute(args: unknown, exec: unknown): Promise<unknown>;
27
- gate?(exec: unknown, next: () => Promise<unknown>): Promise<unknown>;
28
27
  timeoutMs?: number;
29
28
  }
30
29
  /** 构建五个工具定义。 */
package/lib/tools.js CHANGED
@@ -297,10 +297,6 @@ export function buildFlakeTools(cfg, runner, store) {
297
297
  },
298
298
  timeoutMs: 10000,
299
299
  };
300
- if (cfg.writeApproval) {
301
- quarantine.gate = approvalGate('flaky_quarantine', '把测试用例写入 .flakefinder.json 隔离清单');
302
- clear.gate = approvalGate('flaky_clear', '从 .flakefinder.json 隔离清单移除测试用例');
303
- }
304
300
  const flakyHealth = {
305
301
  name: 'flaky_health',
306
302
  description: 'dsh-flakefinder 自检:验证 Python 解释器与 pytest 是否可用(pytest 框架必需),并汇总默认配置。遇到问题时先运行本工具定位。',
@@ -359,27 +355,3 @@ export function buildFlakeTools(cfg, runner, store) {
359
355
  function runsTimeout(cfg) {
360
356
  return Math.min(10 * 60 * 1000, cfg.maxRuns * cfg.timeoutMs + 15000);
361
357
  }
362
- /** 写操作审批门:复用 dsh-docker 的中文拒绝语义。 */
363
- function approvalGate(toolName, action) {
364
- return async (exec, next) => {
365
- const record = (typeof exec === 'object' && exec !== null ? exec : {});
366
- const approval = record.approval;
367
- if (approval === undefined) {
368
- return { kind: 'deny', reason: toolName + ' 需要确认,但当前环境没有审批通道(如 headless)。如确定安全,可在配置中设置 writeApproval: false。' };
369
- }
370
- const outcome = await approval.request({
371
- agent: record.agent,
372
- toolName: record.toolName,
373
- callId: record.callId,
374
- reason: action,
375
- signal: record.signal,
376
- });
377
- if (outcome === 'allowed-once')
378
- return next();
379
- if (outcome === 'cancelled')
380
- return { kind: 'deny', reason: action + ' 被取消,未执行。' };
381
- if (outcome === 'unavailable')
382
- return { kind: 'deny', reason: action + ' 不可用(没有可用的审批界面),未执行。' };
383
- return { kind: 'deny', reason: action + ' 未获批准:要么你拒绝了,要么当前会话处于 Full Access。若确需直接写入,可设置 writeApproval: false(自行承担风险)。' };
384
- };
385
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-flakefinder",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "DSH 测试稳定性插件:flaky_detect/history/quarantine/clear/report 五工具,支持 vitest/jest/pytest/node:test,重复运行测试并识别不稳定用例,JUnit/TAP 完整解析,JSON 历史与隔离清单,零运行时依赖。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",