dsh-flakefinder 0.3.0 → 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/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
@@ -22,10 +22,6 @@ function compileParameters(spec) {
22
22
  node.description = prop.description;
23
23
  if (typeof prop?.items === 'string')
24
24
  node.items = { type: prop.items };
25
- if (prop?.minItems !== undefined)
26
- node.minItems = prop.minItems;
27
- if (prop?.maxItems !== undefined)
28
- node.maxItems = prop.maxItems;
29
25
  properties[key] = node;
30
26
  }
31
27
  return { type: 'object', properties, ...(required.length > 0 ? { required } : {}) };
@@ -200,7 +196,7 @@ export function buildFlakeTools(cfg, runner, store) {
200
196
  name: 'flaky_quarantine',
201
197
  description: '把确认的 flaky 用例写入项目根 .flakefinder.json 隔离清单(不修改测试源码)。需要 flaky_detect 判定为 flaky 后再使用。',
202
198
  parameters: compileParameters({
203
- tests: { type: 'array', items: 'string', minItems: 1, maxItems: 100, required: true, description: '测试引用数组:文件路径,或 "文件路径 > 用例名"。' },
199
+ tests: { type: 'array', items: 'string', required: true, description: '1-100 条测试引用:文件路径,或 "文件路径 > 用例名"。' },
204
200
  reason: { type: 'string', required: true, description: '隔离原因(会写进清单,例如:定时器竞态,见 issue #12)。' },
205
201
  }),
206
202
  output: {
@@ -227,7 +223,7 @@ export function buildFlakeTools(cfg, runner, store) {
227
223
  name: 'flaky_clear',
228
224
  description: '从隔离清单移除已经恢复稳定的用例。建议先 flaky_detect 验证多轮全部通过后再清除。',
229
225
  parameters: compileParameters({
230
- tests: { type: 'array', items: 'string', minItems: 1, maxItems: 100, required: true, description: '测试引用数组:文件路径,或 "文件路径 > 用例名"。' },
226
+ tests: { type: 'array', items: 'string', required: true, description: '1-100 条测试引用:文件路径,或 "文件路径 > 用例名"。' },
231
227
  }),
232
228
  output: {
233
229
  schema: {
@@ -297,10 +293,6 @@ export function buildFlakeTools(cfg, runner, store) {
297
293
  },
298
294
  timeoutMs: 10000,
299
295
  };
300
- if (cfg.writeApproval) {
301
- quarantine.gate = approvalGate('flaky_quarantine', '把测试用例写入 .flakefinder.json 隔离清单');
302
- clear.gate = approvalGate('flaky_clear', '从 .flakefinder.json 隔离清单移除测试用例');
303
- }
304
296
  const flakyHealth = {
305
297
  name: 'flaky_health',
306
298
  description: 'dsh-flakefinder 自检:验证 Python 解释器与 pytest 是否可用(pytest 框架必需),并汇总默认配置。遇到问题时先运行本工具定位。',
@@ -359,27 +351,3 @@ export function buildFlakeTools(cfg, runner, store) {
359
351
  function runsTimeout(cfg) {
360
352
  return Math.min(10 * 60 * 1000, cfg.maxRuns * cfg.timeoutMs + 15000);
361
353
  }
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.2",
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",