@fantasticit/sumi 0.7.4 → 0.7.5

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.
Files changed (61) hide show
  1. package/dist/adapters.js +1 -1
  2. package/dist/adapters.js.map +1 -1
  3. package/dist/agent-hooks.d.ts +15 -0
  4. package/dist/agent-hooks.js +93 -5
  5. package/dist/agent-hooks.js.map +1 -1
  6. package/node_modules/@sumi/create/package.json +1 -1
  7. package/node_modules/@sumi/kernel/dist/config.d.ts +5 -0
  8. package/node_modules/@sumi/kernel/dist/config.js +16 -0
  9. package/node_modules/@sumi/kernel/dist/config.js.map +1 -1
  10. package/node_modules/@sumi/kernel/dist/engine.js +1 -0
  11. package/node_modules/@sumi/kernel/dist/engine.js.map +1 -1
  12. package/node_modules/@sumi/kernel/dist/registry.d.ts +2 -0
  13. package/node_modules/@sumi/kernel/dist/registry.js +15 -1
  14. package/node_modules/@sumi/kernel/dist/registry.js.map +1 -1
  15. package/node_modules/@sumi/kernel/package.json +2 -2
  16. package/node_modules/@sumi/plugin-sdk/README.md +23 -16
  17. package/node_modules/@sumi/plugin-sdk/dist/protocol.d.ts +9 -0
  18. package/node_modules/@sumi/plugin-sdk/dist/protocol.js +17 -0
  19. package/node_modules/@sumi/plugin-sdk/dist/protocol.js.map +1 -1
  20. package/node_modules/@sumi/plugin-sdk/package.json +1 -1
  21. package/node_modules/@sumi/plugins-core/README.md +14 -12
  22. package/node_modules/@sumi/plugins-core/dist/plugins/exec.js +7 -2
  23. package/node_modules/@sumi/plugins-core/dist/plugins/exec.js.map +1 -1
  24. package/node_modules/@sumi/plugins-core/dist/plugins/test-affected.js +12 -3
  25. package/node_modules/@sumi/plugins-core/dist/plugins/test-affected.js.map +1 -1
  26. package/node_modules/@sumi/plugins-core/dist/test-scope.d.ts +1 -1
  27. package/node_modules/@sumi/plugins-core/dist/test-scope.js +12 -6
  28. package/node_modules/@sumi/plugins-core/dist/test-scope.js.map +1 -1
  29. package/node_modules/@sumi/plugins-core/dist/vitest-runner.js +129 -5
  30. package/node_modules/@sumi/plugins-core/dist/vitest-runner.js.map +1 -1
  31. package/node_modules/@sumi/plugins-core/package.json +4 -4
  32. package/node_modules/@sumi/plugins-governance/README.md +10 -9
  33. package/node_modules/@sumi/plugins-governance/dist/checkout.d.ts +12 -2
  34. package/node_modules/@sumi/plugins-governance/dist/checkout.js +40 -3
  35. package/node_modules/@sumi/plugins-governance/dist/checkout.js.map +1 -1
  36. package/node_modules/@sumi/plugins-governance/dist/commands/gate.js +6 -2
  37. package/node_modules/@sumi/plugins-governance/dist/commands/gate.js.map +1 -1
  38. package/node_modules/@sumi/plugins-governance/dist/commands/pr-report.js +1 -1
  39. package/node_modules/@sumi/plugins-governance/dist/commands/pr-report.js.map +1 -1
  40. package/node_modules/@sumi/plugins-governance/dist/gate-cache.d.ts +25 -0
  41. package/node_modules/@sumi/plugins-governance/dist/gate-cache.js +83 -0
  42. package/node_modules/@sumi/plugins-governance/dist/gate-cache.js.map +1 -0
  43. package/node_modules/@sumi/plugins-governance/dist/protected.d.ts +1 -0
  44. package/node_modules/@sumi/plugins-governance/instructions/push.md +1 -1
  45. package/node_modules/@sumi/plugins-governance/package.json +2 -2
  46. package/node_modules/@sumi/plugins-openspec/package.json +2 -2
  47. package/node_modules/@sumi/plugins-openspec/sumi-plugins.yaml +2 -0
  48. package/node_modules/@sumi/plugins-precedent/README.md +2 -0
  49. package/node_modules/@sumi/plugins-precedent/dist/after-edit-context.d.ts +26 -0
  50. package/node_modules/@sumi/plugins-precedent/dist/after-edit-context.js +192 -0
  51. package/node_modules/@sumi/plugins-precedent/dist/after-edit-context.js.map +1 -0
  52. package/node_modules/@sumi/plugins-precedent/dist/commands/after-edit-context.d.ts +1 -0
  53. package/node_modules/@sumi/plugins-precedent/dist/commands/after-edit-context.js +13 -0
  54. package/node_modules/@sumi/plugins-precedent/dist/commands/after-edit-context.js.map +1 -0
  55. package/node_modules/@sumi/plugins-precedent/package.json +3 -3
  56. package/node_modules/@sumi/plugins-precedent/sumi-plugins.yaml +5 -0
  57. package/node_modules/@sumi/plugins-review/package.json +3 -3
  58. package/package.json +10 -10
  59. package/node_modules/@sumi/kernel/test/fixtures/fake-plugin.mjs +0 -44
  60. package/node_modules/@sumi/kernel/test/fixtures/py-plugin.py +0 -3
  61. package/node_modules/@sumi/plugin-sdk/test/isolate-tmpdir.ts +0 -27
@@ -0,0 +1,192 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
4
+ import { configFile, PROJECT_SUMI, spawnCommandSync } from '@sumi/plugin-sdk';
5
+ import { parse } from 'yaml';
6
+ const LIMIT = 2500;
7
+ const QUERY_TIMEOUT = 8000;
8
+ function inside(root, path) {
9
+ const canonical = (value) => {
10
+ let parent = value;
11
+ for (;;) {
12
+ try {
13
+ return resolve(realpathSync(parent), relative(parent, value));
14
+ }
15
+ catch {
16
+ const next = dirname(parent);
17
+ if (next === parent)
18
+ throw new Error('无法解析编辑路径');
19
+ parent = next;
20
+ }
21
+ }
22
+ };
23
+ const rel = relative(realpathSync(root), canonical(resolve(root, path))).replaceAll('\\', '/');
24
+ return rel && rel !== '..' && !rel.startsWith('../') && !isAbsolute(rel) ? rel : null;
25
+ }
26
+ /** 只接受编辑工具的明确路径,不猜测任意 shell 命令可能写入了什么。 */
27
+ export function editedPaths(root, input) {
28
+ try {
29
+ const payload = JSON.parse(input);
30
+ const tool = payload.tool_input;
31
+ if (!tool || typeof tool !== 'object')
32
+ return [];
33
+ const base = typeof payload.cwd === 'string' ? resolve(root, payload.cwd) : root;
34
+ const candidates = [tool.file_path, tool.notebook_path];
35
+ if (payload.tool_name === 'apply_patch') {
36
+ const patch = typeof tool.command === 'string' ? tool.command : tool.patch;
37
+ if (typeof patch === 'string') {
38
+ for (const match of patch.matchAll(/^\*\*\* (?:Add|Update|Delete|Move) File: (.+)$/gm))
39
+ candidates.push(match[1]);
40
+ for (const match of patch.matchAll(/^\*\*\* Move to: (.+)$/gm))
41
+ candidates.push(match[1]);
42
+ }
43
+ }
44
+ return [
45
+ ...new Set(candidates
46
+ .filter((v) => typeof v === 'string')
47
+ .map((v) => inside(root, resolve(base, v)))
48
+ .filter((v) => !!v)),
49
+ ];
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ }
55
+ function sessionId(input) {
56
+ try {
57
+ const value = JSON.parse(input).session_id;
58
+ return typeof value === 'string' && value ? value : null;
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ }
64
+ function enabled(root) {
65
+ const file = configFile(root);
66
+ if (!file)
67
+ return false;
68
+ const data = parse(readFileSync(join(root, file), 'utf8'));
69
+ return data?.agentContext?.afterEdit !== false;
70
+ }
71
+ export function queryContext(root, paths, timeout = QUERY_TIMEOUT) {
72
+ const launcher = process.env.SUMI_BIN || join(root, 'control/bin/sumi.cjs');
73
+ const result = spawnCommandSync(process.execPath, [launcher, 'context', ...paths, '--json'], {
74
+ cwd: root,
75
+ encoding: 'utf8',
76
+ timeout,
77
+ maxBuffer: 1024 * 1024,
78
+ windowsHide: true,
79
+ });
80
+ if (result.status !== 0 || result.error)
81
+ return null;
82
+ const value = JSON.parse(result.stdout);
83
+ return Array.isArray(value.plugins) && Array.isArray(value.items) && Array.isArray(value.rules) && Array.isArray(value.generalRules)
84
+ ? value
85
+ : null;
86
+ }
87
+ function stateFile(root, session) {
88
+ const key = createHash('sha256').update(session).digest('hex');
89
+ return join(root, '.sumi/after-edit-context', `${key}.json`);
90
+ }
91
+ function previous(root, session) {
92
+ if (!session)
93
+ return new Set();
94
+ try {
95
+ const value = JSON.parse(readFileSync(stateFile(root, session), 'utf8'));
96
+ return new Set(Array.isArray(value) ? value.filter((v) => typeof v === 'string') : []);
97
+ }
98
+ catch {
99
+ return new Set();
100
+ }
101
+ }
102
+ function save(root, session, keys) {
103
+ try {
104
+ const file = stateFile(root, session);
105
+ const dir = join(root, '.sumi/after-edit-context');
106
+ mkdirSync(dir, { recursive: true });
107
+ const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
108
+ for (const name of readdirSync(dir)) {
109
+ if (!name.endsWith('.json'))
110
+ continue;
111
+ const stale = join(dir, name);
112
+ try {
113
+ if (statSync(stale).mtimeMs < cutoff)
114
+ unlinkSync(stale);
115
+ }
116
+ catch {
117
+ /* 去重状态清理是尽力而为 */
118
+ }
119
+ }
120
+ const temporary = `${file}.${randomUUID()}.tmp`;
121
+ writeFileSync(temporary, JSON.stringify([...new Set([...previous(root, session), ...keys])]));
122
+ renameSync(temporary, file);
123
+ }
124
+ catch {
125
+ // 会话去重只是体验优化;状态不可写时仍放行编辑。
126
+ }
127
+ }
128
+ export function acknowledgeContext(root, input) {
129
+ try {
130
+ const value = JSON.parse(input);
131
+ const session = sessionId(value.originalInput ?? '');
132
+ if (session && Array.isArray(value.keys) && value.keys.every((key) => typeof key === 'string'))
133
+ save(root, session, value.keys);
134
+ }
135
+ catch {
136
+ /* 送达回执不可影响编辑操作 */
137
+ }
138
+ }
139
+ /** 插件层的提示策略;查询函数可替换以测试失败、超时和异常。 */
140
+ export function afterEditContext(root, input, query = (paths) => queryContext(root, paths)) {
141
+ try {
142
+ if (!enabled(root))
143
+ return null;
144
+ const paths = editedPaths(root, input);
145
+ if (!paths.length)
146
+ return null;
147
+ const context = query(paths);
148
+ if (!context)
149
+ return null;
150
+ const session = sessionId(input);
151
+ const seen = previous(root, session);
152
+ const itemRow = (i) => ({
153
+ key: `item:${i.ref}`,
154
+ text: `- [${i.ref}] ${i.text}${i.source ? `(来源:${i.source})` : ''}`,
155
+ });
156
+ const ruleRow = (r) => ({ key: `rule:${r.file}:${r.line}`, text: `- ${r.file}:${r.line} ${r.text}` });
157
+ const pluginRow = (p) => ({
158
+ key: `plugin:${p.id}`,
159
+ text: `- 机械门 ${p.id}${p.description ? `:${p.description}` : ''}`,
160
+ });
161
+ const rows = [
162
+ ...context.items.filter((i) => !i.global).map(itemRow),
163
+ ...context.rules.map(ruleRow),
164
+ ...context.plugins.filter((p) => !p.global).map(pluginRow),
165
+ ]
166
+ .map((row) => ({ ...row, text: row.text.length > 220 ? `${row.text.slice(0, 219)}…` : row.text }))
167
+ .filter((row) => !seen.has(row.key));
168
+ if (!rows.length)
169
+ return null;
170
+ const footer = `\n原文:${PROJECT_SUMI} context ${paths.map((p) => JSON.stringify(p)).join(' ')}`;
171
+ let body = '本次编辑命中的项目约束(请核对原文):';
172
+ let count = 0;
173
+ const deliveryKeys = [];
174
+ for (const row of rows) {
175
+ if (body.length + row.text.length + footer.length + 20 > LIMIT)
176
+ break;
177
+ body += `\n${row.text}`;
178
+ deliveryKeys.push(row.key);
179
+ count += 1;
180
+ }
181
+ if (!count)
182
+ return { additionalContext: `${body}\n内容过长,已截断。${footer}`.slice(0, LIMIT) };
183
+ if (count < rows.length)
184
+ body += '\n其余条目已截断;';
185
+ body += footer;
186
+ return session ? { additionalContext: body.slice(0, LIMIT), deliveryKeys } : { additionalContext: body.slice(0, LIMIT) };
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }
192
+ //# sourceMappingURL=after-edit-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"after-edit-context.js","sourceRoot":"","sources":["../src/after-edit-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { acknowledgeContext, afterEditContext } from '../after-edit-context.js';
2
+ let input = '';
3
+ for await (const chunk of process.stdin)
4
+ input += chunk;
5
+ const root = process.env.SUMI_ROOT ?? process.cwd();
6
+ if (process.env.SUMI_AFTER_EDIT_DELIVERED === '1')
7
+ acknowledgeContext(root, input);
8
+ else {
9
+ const result = afterEditContext(root, input);
10
+ if (result)
11
+ process.stdout.write(JSON.stringify(result));
12
+ }
13
+ //# sourceMappingURL=after-edit-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"after-edit-context.js","sourceRoot":"","sources":["../../src/commands/after-edit-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,IAAI,KAAK,GAAG,EAAE,CAAC;AACf,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;IAAE,KAAK,IAAI,KAAK,CAAC;AACxD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;AACpD,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG;IAAE,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;KAC9E,CAAC;IACJ,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7C,IAAI,MAAM;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sumi/plugins-precedent",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "判例插件",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,10 +13,10 @@
13
13
  "./package.json": "./package.json"
14
14
  },
15
15
  "dependencies": {
16
- "@sumi/plugin-sdk": "0.7.4",
16
+ "@sumi/plugin-sdk": "0.7.5",
17
17
  "yaml": "2.9.1",
18
18
  "picomatch": "4.0.7",
19
- "@sumi/plugins-governance": "0.7.4"
19
+ "@sumi/plugins-governance": "0.7.5"
20
20
  },
21
21
  "files": [
22
22
  "dist",
@@ -1,4 +1,6 @@
1
1
  # 判例插件:判例 = 一句约束 + 执行者 + 来源。规划器把命中的判例接线成执行义务,自检保证登记了就可执行。
2
+ pathHints:
3
+ testNeutral: ['precedents/**']
2
4
  plugins:
3
5
  - id: prec/planner
4
6
  version: 0.1.0
@@ -7,8 +9,11 @@ plugins:
7
9
  command: [node, ./dist/plugins/planner.js]
8
10
  timeout: 60
9
11
  context: context-precedents
12
+ hooks:
13
+ agent: { afterEdit: context-after-edit }
10
14
  commands:
11
15
  - { name: context-precedents, argv: [node, ./dist/commands/context.js], description: 列出给定路径上生效的判例 }
16
+ - { name: context-after-edit, argv: [node, ./dist/commands/after-edit-context.js], description: 编辑后提示新命中的约束 }
12
17
  - id: prec/checker
13
18
  version: 0.1.0
14
19
  description: 运行判例的检查器
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sumi/plugins-review",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "评审插件",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,10 +13,10 @@
13
13
  "./package.json": "./package.json"
14
14
  },
15
15
  "dependencies": {
16
- "@sumi/plugin-sdk": "0.7.4",
16
+ "@sumi/plugin-sdk": "0.7.5",
17
17
  "yaml": "2.9.1",
18
18
  "picomatch": "4.0.7",
19
- "@sumi/plugins-governance": "0.7.4"
19
+ "@sumi/plugins-governance": "0.7.5"
20
20
  },
21
21
  "files": [
22
22
  "dist",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fantasticit/sumi",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "sumi 命令行",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,14 +13,14 @@
13
13
  "./package.json": "./package.json"
14
14
  },
15
15
  "dependencies": {
16
- "@sumi/create": "0.7.4",
17
- "@sumi/kernel": "0.7.4",
18
- "@sumi/plugin-sdk": "0.7.4",
19
- "@sumi/plugins-core": "0.7.4",
20
- "@sumi/plugins-governance": "0.7.4",
21
- "@sumi/plugins-openspec": "0.7.4",
22
- "@sumi/plugins-precedent": "0.7.4",
23
- "@sumi/plugins-review": "0.7.4",
16
+ "@sumi/create": "0.7.5",
17
+ "@sumi/kernel": "0.7.5",
18
+ "@sumi/plugin-sdk": "0.7.5",
19
+ "@sumi/plugins-core": "0.7.5",
20
+ "@sumi/plugins-governance": "0.7.5",
21
+ "@sumi/plugins-openspec": "0.7.5",
22
+ "@sumi/plugins-precedent": "0.7.5",
23
+ "@sumi/plugins-review": "0.7.5",
24
24
  "tar": "7.5.22",
25
25
  "yaml": "2.9.1"
26
26
  },
@@ -42,5 +42,5 @@
42
42
  "tar",
43
43
  "yaml"
44
44
  ],
45
- "sumiBuild": "sha256:01d06e4e207af63305b2f17d9a3ac49e01e99107e27d93b392941ebcd8c90ae5"
45
+ "sumiBuild": "sha256:1ca7ee9572526f5e6bf9dfaedc8544a0c34dbc8b16e6b28676f12cbe80678328"
46
46
  }
@@ -1,44 +0,0 @@
1
- // 测试用插件:行为由步骤配置 with 决定。
2
- import { spawn } from 'node:child_process';
3
- import { writeFileSync } from 'node:fs';
4
-
5
- let input = '';
6
- for await (const chunk of process.stdin) input += chunk;
7
- const ctx = JSON.parse(input);
8
- const c = ctx.config;
9
- if (c.touch) writeFileSync(`${ctx.repoRoot}/${c.touch}`, String(Date.now()));
10
- if (c.assertNoGitEnv) {
11
- // hook 中 git 导出的变量指向当前仓库,插件看到它们就可能把操作写进错误的仓库。
12
- const leaked = ['GIT_DIR', 'GIT_INDEX_FILE', 'GIT_WORK_TREE'].filter((v) => process.env[v] !== undefined);
13
- if (leaked.length) {
14
- c.status = 'fail';
15
- c.summary = `泄漏:${leaked.join(',')}`;
16
- }
17
- }
18
- if (c.orphan) {
19
- const child = spawn(process.execPath, ['-e', 'setTimeout(() => {}, 30000)'], { stdio: 'ignore' });
20
- writeFileSync(`${ctx.evidenceDir}/orphan.pid`, String(child.pid));
21
- child.unref();
22
- }
23
- if (c.sleep) await new Promise((r) => setTimeout(r, c.sleep * 1000));
24
- if (c.raw !== undefined) {
25
- process.stdout.write(c.raw);
26
- process.exit(c.exit ?? 0);
27
- }
28
- const status = c.status ?? 'pass';
29
- const result = {
30
- status,
31
- summary: c.summary ?? `${ctx.stepId} ${status}`,
32
- findings: c.findings ?? [],
33
- checked: ctx.files,
34
- evidence: [],
35
- next: c.next,
36
- metrics: c.metrics,
37
- plan: c.plan,
38
- report: c.report,
39
- obligations: ctx.obligations.map((o) => ({ id: o.id, outcome: (c.receipts ?? {})[o.id] ?? 'passed' })),
40
- };
41
- if (c.echoFiles) result.summary = ctx.files.join(',');
42
- process.stdout.write(JSON.stringify(result));
43
- process.exitCode = c.exit ?? (status === 'pass' ? 0 : status === 'fail' ? 1 : 2);
44
- if (c.signal) process.kill(process.pid, c.signal);
@@ -1,3 +0,0 @@
1
- import json, sys
2
- ctx = json.load(sys.stdin)
3
- print(json.dumps({"status": "pass", "summary": "python " + ctx["stepId"], "findings": [], "checked": ctx["files"], "evidence": []}), end="")
@@ -1,27 +0,0 @@
1
- /**
2
- * 各包 vitest 配置共用的 setupFiles:每个测试文件运行前把系统临时目录(TMPDIR,Windows 为 TEMP/TMP)指向
3
- * 一个专用目录,测试文件结束后整体删除。
4
- *
5
- * 测试创建的仓库、门禁检出(失败时会保留)、自检与命令的临时文件都经 os.tmpdir() 落在这里;
6
- * 测试启动的子进程(sumi、git hook、门禁)继承该环境,同样写入这里。直接用 vitest CLI、`pnpm test`,
7
- * 或由 core/test-affected(vitest Node API)与 core/coverage 经根配置的 projects 运行时都会加载本文件。
8
- */
9
- import { mkdtempSync, realpathSync, rmSync } from 'node:fs';
10
- import { tmpdir } from 'node:os';
11
- import { join } from 'node:path';
12
- import { afterAll } from 'vitest';
13
-
14
- const NAMES = ['TMPDIR', 'TMP', 'TEMP'] as const;
15
- const saved = Object.fromEntries(NAMES.map((name) => [name, process.env[name]]));
16
- // 取真实路径:macOS 的 /var 是 /private/var 的符号链接,git 与子进程报告的路径是后者
17
- const dir = realpathSync(mkdtempSync(join(tmpdir(), 'sumi-test-')));
18
- for (const name of NAMES) process.env[name] = dir;
19
-
20
- // 本文件的 afterAll 先于测试文件注册,按 vitest 默认的 stack 顺序最后运行,测试文件自己的清理先完成
21
- afterAll(() => {
22
- for (const name of NAMES) {
23
- if (saved[name] === undefined) delete process.env[name];
24
- else process.env[name] = saved[name];
25
- }
26
- rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
27
- });