@mintlify/cli 4.0.1654 → 4.0.1656

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 (54) hide show
  1. package/bin/cli.js +1 -7
  2. package/bin/tsconfig.build.tsbuildinfo +1 -1
  3. package/package.json +9 -13
  4. package/src/cli.tsx +1 -12
  5. package/__test__/mintTestFilePreview.test.ts +0 -35
  6. package/__test__/mintTestOutput.test.ts +0 -256
  7. package/__test__/runHistory.test.ts +0 -240
  8. package/bin/agent-harness/agentPreflight.js +0 -24
  9. package/bin/agent-harness/buildTaskPrompt.js +0 -86
  10. package/bin/agent-harness/countReport.js +0 -10
  11. package/bin/agent-harness/discoverCodeBlocks.js +0 -101
  12. package/bin/agent-harness/executeCodeBlocks.js +0 -263
  13. package/bin/agent-harness/filterSelectedPages.js +0 -10
  14. package/bin/agent-harness/generateTestCode.js +0 -337
  15. package/bin/agent-harness/index.js +0 -95
  16. package/bin/agent-harness/loadAgentSdk.js +0 -46
  17. package/bin/agent-harness/manifest.js +0 -113
  18. package/bin/agent-harness/mapWithConcurrency.js +0 -26
  19. package/bin/agent-harness/runHistory.js +0 -256
  20. package/bin/agent-harness/runSave.js +0 -47
  21. package/bin/agent-harness/setupFolders.js +0 -73
  22. package/bin/agent-harness/tasks/checkTestability.js +0 -228
  23. package/bin/agent-harness/types.js +0 -66
  24. package/bin/mintTest.js +0 -122
  25. package/bin/mintTestFilePreview.js +0 -39
  26. package/bin/mintTestNav.js +0 -35
  27. package/bin/mintTestTelemetry.js +0 -63
  28. package/bin/mintTestText.js +0 -31
  29. package/bin/mintTestTree.js +0 -94
  30. package/bin/mintTestUi.js +0 -818
  31. package/src/agent-harness/MINT_TEST_SYSTEM_DESIGN.md +0 -64
  32. package/src/agent-harness/agentPreflight.ts +0 -13
  33. package/src/agent-harness/buildTaskPrompt.ts +0 -93
  34. package/src/agent-harness/countReport.ts +0 -17
  35. package/src/agent-harness/discoverCodeBlocks.ts +0 -90
  36. package/src/agent-harness/executeCodeBlocks.ts +0 -351
  37. package/src/agent-harness/filterSelectedPages.ts +0 -14
  38. package/src/agent-harness/generateTestCode.ts +0 -396
  39. package/src/agent-harness/index.ts +0 -127
  40. package/src/agent-harness/loadAgentSdk.ts +0 -35
  41. package/src/agent-harness/manifest.ts +0 -116
  42. package/src/agent-harness/mapWithConcurrency.ts +0 -21
  43. package/src/agent-harness/runHistory.ts +0 -255
  44. package/src/agent-harness/runSave.ts +0 -38
  45. package/src/agent-harness/setupFolders.ts +0 -87
  46. package/src/agent-harness/tasks/checkTestability.ts +0 -256
  47. package/src/agent-harness/types.ts +0 -239
  48. package/src/mintTest.tsx +0 -138
  49. package/src/mintTestFilePreview.ts +0 -32
  50. package/src/mintTestNav.ts +0 -31
  51. package/src/mintTestTelemetry.ts +0 -101
  52. package/src/mintTestText.ts +0 -35
  53. package/src/mintTestTree.ts +0 -134
  54. package/src/mintTestUi.tsx +0 -1378
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mintlify/cli",
3
- "version": "4.0.1654",
3
+ "version": "4.0.1656",
4
4
  "description": "The Mintlify CLI",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -45,13 +45,13 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@inquirer/prompts": "7.9.0",
48
- "@mintlify/common": "1.0.1251",
49
- "@mintlify/link-rot": "3.0.1472",
48
+ "@mintlify/common": "1.0.1252",
49
+ "@mintlify/link-rot": "3.0.1473",
50
50
  "@mintlify/models": "0.0.375",
51
- "@mintlify/og": "0.0.49",
52
- "@mintlify/prebuild": "1.0.1416",
53
- "@mintlify/previewing": "4.0.1492",
54
- "@mintlify/validation": "0.1.919",
51
+ "@mintlify/og": "0.0.50",
52
+ "@mintlify/prebuild": "1.0.1417",
53
+ "@mintlify/previewing": "4.0.1493",
54
+ "@mintlify/validation": "0.1.920",
55
55
  "adm-zip": "0.6.0",
56
56
  "chalk": "5.2.0",
57
57
  "color": "4.2.3",
@@ -79,14 +79,10 @@
79
79
  "zod": "4.3.6"
80
80
  },
81
81
  "optionalDependencies": {
82
- "@anthropic-ai/claude-agent-sdk": "0.3.241",
83
- "@anthropic-ai/sdk": "0.120.0",
84
- "@modelcontextprotocol/sdk": "1.30.0",
85
- "@openai/codex-sdk": "0.149.1",
86
82
  "keytar": "7.9.0"
87
83
  },
88
84
  "devDependencies": {
89
- "@mintlify/editor": "0.0.510",
85
+ "@mintlify/editor": "0.0.511",
90
86
  "@mintlify/ts-config": "2.0.2",
91
87
  "@tsconfig/recommended": "1.0.2",
92
88
  "@types/detect-port": "1.3.2",
@@ -105,5 +101,5 @@
105
101
  "vitest": "2.1.9",
106
102
  "vitest-mock-process": "1.0.4"
107
103
  },
108
- "gitHead": "eb7262d426ad3af6bed4bfb3bdcca1f01d7be5a1"
104
+ "gitHead": "ae1817e9de70256651892e22a38627b5b2a26ee8"
109
105
  }
package/src/cli.tsx CHANGED
@@ -44,7 +44,6 @@ import { login } from './login.js';
44
44
  import { logout } from './logout.js';
45
45
  import { mdxLinter } from './mdxLinter.js';
46
46
  import { createTelemetryMiddleware } from './middlewares/telemetryMiddleware.js';
47
- import { mintTestHandler } from './mintTest.js';
48
47
  import { ogCommand } from './og.js';
49
48
  import {
50
49
  buildOverlayRegistryForCheck,
@@ -97,9 +96,7 @@ export const cli = ({ packageName = 'mint' }: { packageName?: string }) => {
97
96
  }
98
97
 
99
98
  const telemetryMiddleware = createTelemetryMiddleware();
100
- if (commandArguments.find((argument) => !argument.startsWith('-')) !== 'test') {
101
- render(<Logs />);
102
- }
99
+ render(<Logs />);
103
100
 
104
101
  return (
105
102
  yargs(commandArguments)
@@ -267,14 +264,6 @@ export const cli = ({ packageName = 'mint' }: { packageName?: string }) => {
267
264
  );
268
265
  }
269
266
  )
270
- .command(
271
- 'test',
272
- 'Test runnable code blocks with a local coding agent',
273
- (yargs) => yargs.usage('usage: mint test'),
274
- async () => {
275
- await mintTestHandler();
276
- }
277
- )
278
267
  .command(
279
268
  'export',
280
269
  'Export a static site for air-gapped deployment',
@@ -1,35 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { PREVIEW_BYTE_LIMIT, readFilePreview } from '../src/mintTestFilePreview.js';
6
-
7
- let temporaryDirectory: string;
8
-
9
- beforeEach(async () => {
10
- temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-preview-'));
11
- });
12
-
13
- afterEach(async () => {
14
- await fs.rm(temporaryDirectory, { recursive: true });
15
- });
16
-
17
- it('reads at most the preview limit plus the truncation marker byte', async () => {
18
- const filePath = path.join(temporaryDirectory, 'large.txt');
19
- await fs.writeFile(filePath, 'a'.repeat(PREVIEW_BYTE_LIMIT + 10_000));
20
-
21
- const preview = await readFilePreview(filePath);
22
-
23
- expect(preview).toEqual({
24
- binary: false,
25
- text: 'a'.repeat(PREVIEW_BYTE_LIMIT),
26
- truncated: true,
27
- });
28
- });
29
-
30
- it('rejects binary previews', async () => {
31
- const filePath = path.join(temporaryDirectory, 'binary.dat');
32
- await fs.writeFile(filePath, new Uint8Array([1, 0, 2]));
33
-
34
- await expect(readFilePreview(filePath)).resolves.toEqual({ binary: true });
35
- });
@@ -1,256 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { executeCodeBlocks } from '../src/agent-harness/executeCodeBlocks.js';
6
- import { generateTestCode } from '../src/agent-harness/generateTestCode.js';
7
- import type { GeneratedTask, TaskUpdate } from '../src/agent-harness/types.js';
8
-
9
- const agentMocks = vi.hoisted(() => ({ codexRun: vi.fn() }));
10
-
11
- vi.mock('../src/agent-harness/loadAgentSdk.js', () => ({
12
- loadCodexSdk: vi.fn(async () => ({
13
- Codex: class {
14
- startThread() {
15
- return { run: agentMocks.codexRun };
16
- }
17
- },
18
- })),
19
- }));
20
-
21
- const temporaryDirectories: string[] = [];
22
-
23
- afterEach(async () => {
24
- vi.clearAllMocks();
25
- await Promise.all(
26
- temporaryDirectories.splice(0).map((directory) => fs.rm(directory, { recursive: true }))
27
- );
28
- });
29
-
30
- it('includes absolute generated file paths in updates and results', async () => {
31
- const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-output-'));
32
- temporaryDirectories.push(directory);
33
- const generatedFile = path.join(directory, 'tests', 'generated.test.ts');
34
- await fs.mkdir(path.dirname(generatedFile), { recursive: true });
35
- await fs.writeFile(generatedFile, 'export const generated = true;\n');
36
- const task: GeneratedTask = {
37
- id: 'claude:guide.mdx',
38
- agent: 'claude',
39
- docsRoot: directory,
40
- file: 'guide.mdx',
41
- directory,
42
- attempts: 1,
43
- tokens: 10,
44
- testable: true,
45
- manifest: {
46
- version: 1,
47
- generatedFiles: ['tests/generated.test.ts'],
48
- setupCommands: [],
49
- testCommands: [],
50
- },
51
- };
52
- const updates: TaskUpdate[] = [];
53
-
54
- const results = await executeCodeBlocks({
55
- tasks: [task],
56
- docsRoot: directory,
57
- concurrency: 1,
58
- commandTimeoutMs: 1_000,
59
- signal: new AbortController().signal,
60
- onTaskUpdate: (update) => updates.push(update),
61
- });
62
-
63
- expect(results).toHaveLength(1);
64
- expect(results[0]?.generatedFiles).toEqual([generatedFile]);
65
- expect(updates.map((update) => update.generatedFiles)).toEqual([
66
- [generatedFile],
67
- [generatedFile],
68
- ]);
69
- });
70
-
71
- it('includes partial generated files when generation fails', async () => {
72
- const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-output-'));
73
- temporaryDirectories.push(directory);
74
- const generatedFile = path.join(directory, 'tests', 'partial.test.ts');
75
- await fs.mkdir(path.dirname(generatedFile), { recursive: true });
76
- await fs.writeFile(path.join(directory, 'package.json'), '{}\n');
77
- agentMocks.codexRun.mockImplementation(async () => {
78
- await fs.writeFile(generatedFile, 'export const partial = true;\n');
79
- throw new Error('generation failed');
80
- });
81
- const updates: TaskUpdate[] = [];
82
- const onGenerationResult = vi.fn();
83
-
84
- const generated = await generateTestCode({
85
- tasks: [generatedTask(directory)],
86
- docsRoot: directory,
87
- model: 'unused',
88
- concurrency: 1,
89
- signal: new AbortController().signal,
90
- onTaskUpdate: (update) => updates.push(update),
91
- onGenerationResult,
92
- });
93
-
94
- expect(updates.at(-1)).toMatchObject({
95
- phase: 'agent_error',
96
- generatedFiles: [generatedFile],
97
- });
98
- expect(onGenerationResult).toHaveBeenCalledWith({
99
- status: 'failed',
100
- agent: 'codex',
101
- model: 'unused',
102
- attemptCount: 2,
103
- tokenCount: 0,
104
- durationMs: expect.any(Number),
105
- generatedFileCount: 1,
106
- });
107
- const results = await executeCodeBlocks({
108
- tasks: generated,
109
- docsRoot: directory,
110
- concurrency: 1,
111
- commandTimeoutMs: 1_000,
112
- signal: new AbortController().signal,
113
- onTaskUpdate: (update) => updates.push(update),
114
- });
115
- expect(results[0]?.generatedFiles).toEqual([generatedFile]);
116
- expect(updates.at(-1)).toMatchObject({
117
- phase: 'agent_error',
118
- generatedFiles: [generatedFile],
119
- });
120
- });
121
-
122
- it('reports a successful generation run', async () => {
123
- const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-output-'));
124
- temporaryDirectories.push(directory);
125
- const generatedFile = path.join(directory, 'tests', 'generated.test.ts');
126
- await fs.mkdir(path.dirname(generatedFile), { recursive: true });
127
- await fs.writeFile(path.join(directory, 'package.json'), '{}\n');
128
- agentMocks.codexRun.mockImplementation(async () => {
129
- await fs.writeFile(generatedFile, 'export const generated = true;\n');
130
- await fs.writeFile(
131
- path.join(directory, 'mint-test.json'),
132
- `${JSON.stringify({
133
- version: 1,
134
- generatedFiles: ['tests/generated.test.ts'],
135
- setupCommands: [],
136
- testCommands: [],
137
- })}\n`
138
- );
139
- return {
140
- finalResponse: 'done',
141
- usage: {
142
- input_tokens: 100,
143
- cached_input_tokens: 25,
144
- cache_write_input_tokens: 10,
145
- output_tokens: 50,
146
- reasoning_output_tokens: 15,
147
- },
148
- };
149
- });
150
- const onGenerationResult = vi.fn();
151
-
152
- await generateTestCode({
153
- tasks: [generatedTask(directory)],
154
- docsRoot: directory,
155
- model: 'gpt-test',
156
- concurrency: 1,
157
- signal: new AbortController().signal,
158
- onTaskUpdate: () => {},
159
- onGenerationResult,
160
- });
161
-
162
- expect(onGenerationResult).toHaveBeenCalledWith({
163
- status: 'succeeded',
164
- agent: 'codex',
165
- model: 'gpt-test',
166
- attemptCount: 1,
167
- tokenCount: 200,
168
- durationMs: expect.any(Number),
169
- generatedFileCount: 1,
170
- });
171
- });
172
-
173
- it('includes partial generated files when generation is cancelled', async () => {
174
- const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-output-'));
175
- temporaryDirectories.push(directory);
176
- const generatedFile = path.join(directory, 'tests', 'partial.test.ts');
177
- await fs.mkdir(path.dirname(generatedFile), { recursive: true });
178
- const controller = new AbortController();
179
- agentMocks.codexRun.mockImplementation(async () => {
180
- await fs.writeFile(generatedFile, 'export const partial = true;\n');
181
- controller.abort();
182
- throw new Error('cancelled');
183
- });
184
- const updates: TaskUpdate[] = [];
185
-
186
- const generated = await generateTestCode({
187
- tasks: [generatedTask(directory)],
188
- docsRoot: directory,
189
- model: 'unused',
190
- concurrency: 1,
191
- signal: controller.signal,
192
- onTaskUpdate: (update) => updates.push(update),
193
- });
194
-
195
- expect(updates.at(-1)).toMatchObject({
196
- phase: 'cancelled',
197
- generatedFiles: [generatedFile],
198
- });
199
- const results = await executeCodeBlocks({
200
- tasks: generated,
201
- docsRoot: directory,
202
- concurrency: 1,
203
- commandTimeoutMs: 1_000,
204
- signal: controller.signal,
205
- onTaskUpdate: (update) => updates.push(update),
206
- });
207
- expect(results[0]?.generatedFiles).toEqual([generatedFile]);
208
- expect(updates.at(-1)).toMatchObject({
209
- phase: 'cancelled',
210
- generatedFiles: [generatedFile],
211
- });
212
- });
213
-
214
- it.each([
215
- { outcome: 'fails', aborted: false, status: 'agent_error', error: 'generation failed' },
216
- { outcome: 'is cancelled', aborted: true, status: 'cancelled', error: 'run cancelled' },
217
- ] as const)(
218
- 'forwards partial generated files through execution when generation $outcome',
219
- async ({ aborted, status, error }) => {
220
- const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-output-'));
221
- temporaryDirectories.push(directory);
222
- const generatedFile = path.join(directory, 'tests', 'partial.test.ts');
223
- const controller = new AbortController();
224
- if (aborted) controller.abort();
225
- const updates: TaskUpdate[] = [];
226
-
227
- const results = await executeCodeBlocks({
228
- tasks: [{ ...generatedTask(directory), error, generatedFiles: [generatedFile] }],
229
- docsRoot: directory,
230
- concurrency: 1,
231
- commandTimeoutMs: 1_000,
232
- signal: controller.signal,
233
- onTaskUpdate: (update) => updates.push(update),
234
- });
235
-
236
- expect(results).toEqual([
237
- expect.objectContaining({ status, error, generatedFiles: [generatedFile], commands: [] }),
238
- ]);
239
- expect(updates).toEqual([
240
- expect.objectContaining({ phase: status, generatedFiles: [generatedFile] }),
241
- ]);
242
- }
243
- );
244
-
245
- function generatedTask(directory: string): GeneratedTask {
246
- return {
247
- id: 'codex:guide.mdx',
248
- agent: 'codex',
249
- docsRoot: directory,
250
- file: 'guide.mdx',
251
- directory,
252
- attempts: 0,
253
- tokens: 0,
254
- testable: true,
255
- };
256
- }
@@ -1,240 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
-
5
- import {
6
- loadLatestTestRun,
7
- loadTestRuns,
8
- recordTestRun,
9
- testRunReportPath,
10
- testRunsIndexPath,
11
- writeTestRunHistory,
12
- } from '../src/agent-harness/runHistory.js';
13
- import type { CodeTestReport } from '../src/agent-harness/types.js';
14
-
15
- const temporaryDirectories: string[] = [];
16
-
17
- afterEach(async () => {
18
- vi.restoreAllMocks();
19
- vi.unstubAllGlobals();
20
- await Promise.all(
21
- temporaryDirectories.splice(0).map((directory) => fs.rm(directory, { recursive: true }))
22
- );
23
- });
24
-
25
- function makeReport(docsRoot: string, runId: string, completedAt: string): CodeTestReport {
26
- return {
27
- version: 1,
28
- runId,
29
- status: 'passed',
30
- docsRoot,
31
- outputDirectory: path.join(docsRoot, 'tests', 'mint-test', runId),
32
- reportPath: testRunReportPath(docsRoot, runId),
33
- agents: ['claude'],
34
- model: 'claude-opus-4-8',
35
- selectedFiles: ['guide.mdx'],
36
- startedAt: '2026-09-01T12:00:00.000Z',
37
- completedAt,
38
- durationMs: 1_000,
39
- tasks: [
40
- {
41
- id: 'claude:guide.mdx',
42
- agent: 'claude',
43
- file: 'guide.mdx',
44
- directory: path.join(docsRoot, 'tests', 'mint-test', runId, 'claude', '001-guide'),
45
- generatedFiles: [
46
- path.join(
47
- docsRoot,
48
- 'tests',
49
- 'mint-test',
50
- runId,
51
- 'claude',
52
- '001-guide',
53
- 'tests',
54
- 'guide.test.ts'
55
- ),
56
- ],
57
- testable: true,
58
- attempts: 1,
59
- tokens: 100,
60
- status: 'passed',
61
- commands: [],
62
- },
63
- ],
64
- };
65
- }
66
-
67
- it('stores each result and tracks every run in newest-first order', async () => {
68
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
69
- temporaryDirectories.push(docsRoot);
70
- const first = makeReport(docsRoot, '20260901120000-first', '2026-09-01T12:00:01.000Z');
71
- const second = makeReport(docsRoot, '20260901130000-second', '2026-09-01T13:00:01.000Z');
72
-
73
- await writeTestRunHistory(docsRoot, first);
74
- await writeTestRunHistory(docsRoot, second);
75
-
76
- expect(JSON.parse(await fs.readFile(testRunReportPath(docsRoot, first.runId), 'utf8'))).toEqual(
77
- first
78
- );
79
- const index = await loadTestRuns(docsRoot);
80
- expect(index.runs.map((run) => run.runId)).toEqual([second.runId, first.runId]);
81
- expect(index.runs[0]?.counts).toEqual({
82
- passed: 1,
83
- failed: 0,
84
- cancelled: 0,
85
- agentErrors: 0,
86
- });
87
- await expect(loadLatestTestRun(docsRoot)).resolves.toEqual(second);
88
- });
89
-
90
- it('rebuilds testRuns.json data from saved run results', async () => {
91
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
92
- temporaryDirectories.push(docsRoot);
93
- const report = makeReport(docsRoot, '20260901120000-rebuild', '2026-09-01T12:00:01.000Z');
94
- await writeTestRunHistory(docsRoot, report);
95
- await fs.unlink(testRunsIndexPath(docsRoot));
96
-
97
- const rebuilt = await loadTestRuns(docsRoot);
98
-
99
- expect(rebuilt.runs).toHaveLength(1);
100
- expect(rebuilt.runs[0]?.runId).toBe(report.runId);
101
- });
102
-
103
- it('keeps every run when history writes overlap', async () => {
104
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
105
- temporaryDirectories.push(docsRoot);
106
- const reports = Array.from({ length: 12 }, (_, index) =>
107
- makeReport(
108
- docsRoot,
109
- `20260901120000-concurrent-${index}`,
110
- `2026-09-01T12:00:${String(index).padStart(2, '0')}.000Z`
111
- )
112
- );
113
-
114
- await Promise.all(reports.map((report) => writeTestRunHistory(docsRoot, report)));
115
-
116
- const index = JSON.parse(await fs.readFile(testRunsIndexPath(docsRoot), 'utf8')) as {
117
- runs: Array<{ runId: string }>;
118
- };
119
- expect(index.runs.map((run) => run.runId).sort()).toEqual(
120
- reports.map((report) => report.runId).sort()
121
- );
122
- }, 60_000);
123
-
124
- it('retries a temporarily unavailable Windows history lock', async () => {
125
- vi.stubGlobal('process', { ...process, platform: 'win32' });
126
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
127
- temporaryDirectories.push(docsRoot);
128
- const report = makeReport(docsRoot, 'windows-retry', '2026-09-01T12:00:01.000Z');
129
- const open = vi
130
- .spyOn(fs, 'open')
131
- .mockRejectedValueOnce(Object.assign(new Error('Lock deletion pending'), { code: 'EPERM' }));
132
-
133
- await writeTestRunHistory(docsRoot, report);
134
-
135
- expect(open).toHaveBeenCalledTimes(2);
136
- expect(open).toHaveBeenNthCalledWith(
137
- 2,
138
- path.join(docsRoot, '.mintlify/test/testRuns.lock'),
139
- 'wx'
140
- );
141
- expect(JSON.parse(await fs.readFile(testRunsIndexPath(docsRoot), 'utf8'))).toMatchObject({
142
- runs: [{ runId: report.runId }],
143
- });
144
- await expect(
145
- fs.access(path.join(docsRoot, '.mintlify/test/testRuns.lock'))
146
- ).rejects.toMatchObject({
147
- code: 'ENOENT',
148
- });
149
- });
150
-
151
- it.each([
152
- ['linux', 'EPERM'],
153
- ['win32', 'EACCES'],
154
- ])('propagates %s lock acquisition errors with code %s', async (platform, code) => {
155
- vi.stubGlobal('process', { ...process, platform });
156
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
157
- temporaryDirectories.push(docsRoot);
158
- const report = makeReport(docsRoot, 'denied', '2026-09-01T12:00:01.000Z');
159
- const error = Object.assign(new Error('Permission denied'), { code });
160
- const open = vi.spyOn(fs, 'open').mockRejectedValue(error);
161
-
162
- await expect(writeTestRunHistory(docsRoot, report)).rejects.toBe(error);
163
- expect(open).toHaveBeenCalledTimes(1);
164
- });
165
-
166
- it('stops retrying a Windows lock at the acquisition deadline', async () => {
167
- vi.stubGlobal('process', { ...process, platform: 'win32' });
168
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
169
- temporaryDirectories.push(docsRoot);
170
- const report = makeReport(docsRoot, 'timeout', '2026-09-01T12:00:01.000Z');
171
- vi.spyOn(fs, 'open').mockRejectedValue(
172
- Object.assign(new Error('Permission denied'), { code: 'EPERM' })
173
- );
174
- vi.spyOn(Date, 'now').mockReturnValueOnce(0).mockReturnValueOnce(0).mockReturnValue(40_000);
175
-
176
- await expect(writeTestRunHistory(docsRoot, report)).rejects.toThrow(
177
- 'Timed out updating mint test run history'
178
- );
179
- });
180
-
181
- it('does not retry a token write failure after acquiring the Windows lock', async () => {
182
- vi.stubGlobal('process', { ...process, platform: 'win32' });
183
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
184
- temporaryDirectories.push(docsRoot);
185
- const report = makeReport(docsRoot, 'token-failure', '2026-09-01T12:00:01.000Z');
186
- const error = Object.assign(new Error('Cannot write token'), { code: 'EPERM' });
187
- const realOpen = fs.open;
188
- const open = vi.spyOn(fs, 'open').mockImplementationOnce(async (...args) => {
189
- const handle = await realOpen(...args);
190
- vi.spyOn(handle, 'writeFile').mockRejectedValueOnce(error);
191
- return handle;
192
- });
193
-
194
- await expect(writeTestRunHistory(docsRoot, report)).rejects.toBe(error);
195
- expect(open).toHaveBeenCalledTimes(1);
196
- });
197
-
198
- it('recovers a stale history lock left by an interrupted process', async () => {
199
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
200
- temporaryDirectories.push(docsRoot);
201
- const historyDirectory = path.join(docsRoot, '.mintlify', 'test');
202
- const lockPath = path.join(historyDirectory, 'testRuns.lock');
203
- await fs.mkdir(historyDirectory, { recursive: true });
204
- await fs.writeFile(lockPath, 'interrupted-process');
205
- const staleTime = new Date(Date.now() - 31_000);
206
- await fs.utimes(lockPath, staleTime, staleTime);
207
- const report = makeReport(docsRoot, '20260901120000-after-crash', '2026-09-01T12:00:01.000Z');
208
-
209
- await writeTestRunHistory(docsRoot, report);
210
-
211
- await expect(fs.access(lockPath)).rejects.toMatchObject({ code: 'ENOENT' });
212
- await expect(loadLatestTestRun(docsRoot)).resolves.toEqual(report);
213
- });
214
-
215
- it('returns the report as-is when history is saved', async () => {
216
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
217
- temporaryDirectories.push(docsRoot);
218
- const report = makeReport(docsRoot, '20260901120000-saved', '2026-09-01T12:00:01.000Z');
219
- const fallbackReportPath = path.join(report.outputDirectory, 'report.json');
220
-
221
- await expect(recordTestRun(docsRoot, report, fallbackReportPath)).resolves.toBe(report);
222
- await expect(loadLatestTestRun(docsRoot)).resolves.toEqual(report);
223
- });
224
-
225
- it('keeps a finished run usable when history cannot be saved', async () => {
226
- const docsRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'mint-test-history-'));
227
- temporaryDirectories.push(docsRoot);
228
- await fs.mkdir(path.join(docsRoot, '.mintlify'), { recursive: true });
229
- await fs.writeFile(path.join(docsRoot, '.mintlify', 'test'), 'blocks the history directory\n');
230
- const report = makeReport(docsRoot, '20260901120000-unsaved', '2026-09-01T12:00:01.000Z');
231
- const fallbackReportPath = path.join(report.outputDirectory, 'report.json');
232
-
233
- const recorded = await recordTestRun(docsRoot, report, fallbackReportPath);
234
-
235
- expect(recorded).toEqual({
236
- ...report,
237
- reportPath: fallbackReportPath,
238
- historyError: expect.stringContaining('run history could not be saved: '),
239
- });
240
- });
@@ -1,24 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { loadClaudeAgentSdk, loadCodexSdk } from './loadAgentSdk.js';
11
- /**
12
- * Fail fast, before any page is checked, when an agent SDK is not installed. Without this every
13
- * page reports the same missing-dependency error minutes later as "test could not be generated".
14
- */
15
- export function ensureAgentSdks(agents) {
16
- return __awaiter(this, void 0, void 0, function* () {
17
- for (const agent of new Set(agents)) {
18
- if (agent === 'claude')
19
- yield loadClaudeAgentSdk();
20
- else
21
- yield loadCodexSdk();
22
- }
23
- });
24
- }