@agent-inspect/vitest 2.1.0
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/LICENSE +21 -0
- package/dist/index.cjs +332 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +135 -0
- package/dist/index.d.ts +135 -0
- package/dist/index.mjs +325 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AgentInspect contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var promises = require('fs/promises');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var reporters = require('agent-inspect/reporters');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
10
|
+
|
|
11
|
+
// packages/vitest/src/index.ts
|
|
12
|
+
var PACKAGE_NAME = "@agent-inspect/vitest";
|
|
13
|
+
var DEFAULT_ARTIFACT_DIR = ".agent-inspect/vitest-artifacts";
|
|
14
|
+
var DEFAULT_SUCCESS_LIMIT = 20;
|
|
15
|
+
var MAX_SUCCESS_LIMIT = 100;
|
|
16
|
+
var MAX_TEXT = 180;
|
|
17
|
+
var DEFAULT_GENERATED_AT = "1970-01-01T00:00:00.000Z";
|
|
18
|
+
function createAgentInspectVitestReporter(options = {}) {
|
|
19
|
+
const diagnostics = [];
|
|
20
|
+
const artifacts = [];
|
|
21
|
+
const seenObjects = /* @__PURE__ */ new WeakSet();
|
|
22
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
23
|
+
const artifactNames = /* @__PURE__ */ new Map();
|
|
24
|
+
const successLimit = normalizeSuccessLimit(options);
|
|
25
|
+
let retainedSuccesses = 0;
|
|
26
|
+
const reporter = {
|
|
27
|
+
async onTestCaseResult(test) {
|
|
28
|
+
await handleTest(test);
|
|
29
|
+
},
|
|
30
|
+
async onTaskUpdate(tasks) {
|
|
31
|
+
for (const test of collectTasks(tasks)) {
|
|
32
|
+
await handleTest(test);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
async onFinished(files) {
|
|
36
|
+
for (const test of collectTasks(files)) {
|
|
37
|
+
await handleTest(test);
|
|
38
|
+
}
|
|
39
|
+
await appendGithubSummary();
|
|
40
|
+
},
|
|
41
|
+
getDiagnostics() {
|
|
42
|
+
return diagnostics.slice();
|
|
43
|
+
},
|
|
44
|
+
getArtifacts() {
|
|
45
|
+
return artifacts.slice();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
async function handleTest(test) {
|
|
49
|
+
if (!isObject(test)) return;
|
|
50
|
+
if (seenObjects.has(test)) return;
|
|
51
|
+
const status = readStatus(test);
|
|
52
|
+
if (status === void 0 || status === "skipped") return;
|
|
53
|
+
const identity = readIdentity(test);
|
|
54
|
+
const seenKey = `${identity.id}:${status}`;
|
|
55
|
+
if (seenKeys.has(seenKey)) return;
|
|
56
|
+
const association = resolveAssociation(test, options);
|
|
57
|
+
if (association === void 0) return;
|
|
58
|
+
if (status === "passed") {
|
|
59
|
+
if (retainedSuccesses >= successLimit) return;
|
|
60
|
+
retainedSuccesses += 1;
|
|
61
|
+
}
|
|
62
|
+
seenObjects.add(test);
|
|
63
|
+
seenKeys.add(seenKey);
|
|
64
|
+
try {
|
|
65
|
+
const artifact = await writeSafeArtifact({
|
|
66
|
+
artifactDir: options.artifactDir ?? DEFAULT_ARTIFACT_DIR,
|
|
67
|
+
association,
|
|
68
|
+
generatedAt: options.generatedAt ?? DEFAULT_GENERATED_AT,
|
|
69
|
+
identity,
|
|
70
|
+
reserveArtifactName,
|
|
71
|
+
redactionProfile: options.redactionProfile ?? "share",
|
|
72
|
+
status
|
|
73
|
+
});
|
|
74
|
+
artifacts.push(artifact);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
reportDiagnostic({
|
|
77
|
+
code: "artifact-write-failed",
|
|
78
|
+
message: `Could not write AgentInspect Vitest artifact: ${errorMessage(error)}`,
|
|
79
|
+
testId: identity.id
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function reserveArtifactName(seed) {
|
|
84
|
+
const base = safeSegment(seed);
|
|
85
|
+
const next = (artifactNames.get(base) ?? 0) + 1;
|
|
86
|
+
artifactNames.set(base, next);
|
|
87
|
+
return next === 1 ? base : `${base}-${next}`;
|
|
88
|
+
}
|
|
89
|
+
function reportDiagnostic(diagnostic) {
|
|
90
|
+
diagnostics.push(diagnostic);
|
|
91
|
+
if (options.onDiagnostic === void 0) return;
|
|
92
|
+
try {
|
|
93
|
+
options.onDiagnostic(diagnostic);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
diagnostics.push({
|
|
96
|
+
code: "on-diagnostic-failed",
|
|
97
|
+
message: `AgentInspect Vitest diagnostic callback failed: ${errorMessage(error)}`,
|
|
98
|
+
testId: diagnostic.testId
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function appendGithubSummary() {
|
|
103
|
+
if (options.githubSummary === void 0 || artifacts.length === 0) return;
|
|
104
|
+
const failed = artifacts.filter((artifact) => artifact.status === "failed").length;
|
|
105
|
+
const passed = artifacts.filter((artifact) => artifact.status === "passed").length;
|
|
106
|
+
const lines = [
|
|
107
|
+
"",
|
|
108
|
+
"## AgentInspect Vitest artifacts",
|
|
109
|
+
"",
|
|
110
|
+
`- Failed test artifacts: ${failed}`,
|
|
111
|
+
`- Passing test artifacts retained: ${passed}`,
|
|
112
|
+
`- Artifact directory: ${boundText(path__default.default.basename(options.artifactDir ?? DEFAULT_ARTIFACT_DIR))}`,
|
|
113
|
+
""
|
|
114
|
+
];
|
|
115
|
+
try {
|
|
116
|
+
await promises.writeFile(options.githubSummary, `${lines.join("\n")}
|
|
117
|
+
`, { flag: "a" });
|
|
118
|
+
} catch (error) {
|
|
119
|
+
reportDiagnostic({
|
|
120
|
+
code: "github-summary-write-failed",
|
|
121
|
+
message: `Could not append AgentInspect Vitest summary: ${errorMessage(error)}`
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return reporter;
|
|
126
|
+
}
|
|
127
|
+
var agentInspectVitestReporter = createAgentInspectVitestReporter;
|
|
128
|
+
function normalizeSuccessLimit(options) {
|
|
129
|
+
const cap = clampCount(options.maxSuccessfulTraces, DEFAULT_SUCCESS_LIMIT);
|
|
130
|
+
if (options.retainSuccessful === true) return cap;
|
|
131
|
+
if (typeof options.retainSuccessful === "number") {
|
|
132
|
+
return Math.min(clampCount(options.retainSuccessful, 0), cap);
|
|
133
|
+
}
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
function clampCount(value, fallback) {
|
|
137
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
138
|
+
return Math.max(0, Math.min(MAX_SUCCESS_LIMIT, Math.floor(value)));
|
|
139
|
+
}
|
|
140
|
+
function resolveAssociation(test, options) {
|
|
141
|
+
const resolved = options.resolveTrace?.(test);
|
|
142
|
+
if (isAssociation(resolved)) return resolved;
|
|
143
|
+
const task = test;
|
|
144
|
+
const result = isObject(task.result) ? task.result : void 0;
|
|
145
|
+
return readAssociationFromMeta(task.meta) ?? readAssociationFromContext(task.context) ?? readAssociationFromMeta(result?.meta);
|
|
146
|
+
}
|
|
147
|
+
function readAssociationFromContext(context) {
|
|
148
|
+
if (!isObject(context)) return void 0;
|
|
149
|
+
const maybeMeta = readProp(context, "meta");
|
|
150
|
+
return readAssociationFromMeta(maybeMeta);
|
|
151
|
+
}
|
|
152
|
+
function readAssociationFromMeta(meta) {
|
|
153
|
+
if (!isObject(meta)) return void 0;
|
|
154
|
+
const direct = readProp(meta, "agentInspect");
|
|
155
|
+
const hyphenated = readProp(meta, "agent-inspect");
|
|
156
|
+
const trace = readProp(meta, "trace");
|
|
157
|
+
for (const candidate of [direct, hyphenated, trace]) {
|
|
158
|
+
if (isAssociation(candidate)) return candidate;
|
|
159
|
+
}
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
function isAssociation(value) {
|
|
163
|
+
if (!isObject(value)) return false;
|
|
164
|
+
return readOptionalString(value, "runId") !== void 0 || readOptionalString(value, "tracePath") !== void 0 || readOptionalString(value, "artifactLabel") !== void 0;
|
|
165
|
+
}
|
|
166
|
+
function readStatus(test) {
|
|
167
|
+
const task = test;
|
|
168
|
+
const result = isObject(task.result) ? task.result : void 0;
|
|
169
|
+
const raw = readOptionalString(result, "state") ?? readOptionalString(result, "status") ?? readOptionalString(test, "state") ?? readOptionalString(test, "status");
|
|
170
|
+
if (raw === void 0) return void 0;
|
|
171
|
+
const normalized = raw.toLowerCase();
|
|
172
|
+
if (normalized === "fail" || normalized === "failed") return "failed";
|
|
173
|
+
if (normalized === "pass" || normalized === "passed") return "passed";
|
|
174
|
+
if (normalized === "skip" || normalized === "skipped") return "skipped";
|
|
175
|
+
return void 0;
|
|
176
|
+
}
|
|
177
|
+
function readIdentity(test) {
|
|
178
|
+
const task = test;
|
|
179
|
+
const id = readOptionalString(test, "id") ?? readOptionalString(test, "taskId");
|
|
180
|
+
const name = readOptionalString(test, "fullName") ?? readOptionalString(test, "name") ?? readOptionalString(task.suite, "name") ?? id ?? "unknown-test";
|
|
181
|
+
const file = readOptionalString(test, "file") ?? readOptionalString(test, "filepath") ?? readOptionalString(task.suite, "file");
|
|
182
|
+
return {
|
|
183
|
+
file: file === void 0 ? void 0 : path__default.default.basename(file),
|
|
184
|
+
id: boundText(redactText(id ?? name)),
|
|
185
|
+
name: boundText(redactText(name))
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async function writeSafeArtifact(input) {
|
|
189
|
+
const artifactSeed = input.association.artifactLabel ?? input.identity.id ?? input.identity.name ?? input.status;
|
|
190
|
+
const artifactName = input.reserveArtifactName(artifactSeed);
|
|
191
|
+
const manifestPathResult = reporters.createReporterArtifactPath({
|
|
192
|
+
outputDir: input.artifactDir,
|
|
193
|
+
testId: artifactName,
|
|
194
|
+
name: input.identity.name,
|
|
195
|
+
file: input.identity.file,
|
|
196
|
+
kind: "report",
|
|
197
|
+
format: "json"
|
|
198
|
+
});
|
|
199
|
+
const summaryPathResult = reporters.createReporterArtifactPath({
|
|
200
|
+
outputDir: input.artifactDir,
|
|
201
|
+
testId: artifactName,
|
|
202
|
+
name: input.identity.name,
|
|
203
|
+
file: input.identity.file,
|
|
204
|
+
kind: "summary",
|
|
205
|
+
format: "md"
|
|
206
|
+
});
|
|
207
|
+
if (!manifestPathResult.ok || manifestPathResult.absolutePath === void 0 || manifestPathResult.relativePath === void 0) {
|
|
208
|
+
throw new Error(formatPathDiagnostics(manifestPathResult.diagnostics));
|
|
209
|
+
}
|
|
210
|
+
if (!summaryPathResult.ok || summaryPathResult.absolutePath === void 0 || summaryPathResult.relativePath === void 0) {
|
|
211
|
+
throw new Error(formatPathDiagnostics(summaryPathResult.diagnostics));
|
|
212
|
+
}
|
|
213
|
+
const directory = path__default.default.dirname(manifestPathResult.absolutePath);
|
|
214
|
+
await promises.mkdir(directory, { recursive: true });
|
|
215
|
+
const traceFile = input.association.tracePath === void 0 ? void 0 : path__default.default.basename(input.association.tracePath);
|
|
216
|
+
const manifestArtifact = {
|
|
217
|
+
kind: "report",
|
|
218
|
+
path: manifestPathResult.relativePath,
|
|
219
|
+
format: "json",
|
|
220
|
+
redactionProfile: input.redactionProfile
|
|
221
|
+
};
|
|
222
|
+
const summaryArtifact = {
|
|
223
|
+
kind: "summary",
|
|
224
|
+
path: summaryPathResult.relativePath,
|
|
225
|
+
format: "md",
|
|
226
|
+
redactionProfile: input.redactionProfile
|
|
227
|
+
};
|
|
228
|
+
const manifest = reporters.createTraceArtifactManifest({
|
|
229
|
+
framework: "vitest",
|
|
230
|
+
generatedAt: input.generatedAt,
|
|
231
|
+
results: [
|
|
232
|
+
{
|
|
233
|
+
testId: input.identity.id,
|
|
234
|
+
name: input.identity.name,
|
|
235
|
+
...input.identity.file === void 0 ? {} : { file: input.identity.file },
|
|
236
|
+
status: input.status,
|
|
237
|
+
tracePath: safeOptional(traceFile),
|
|
238
|
+
artifacts: [manifestArtifact, summaryArtifact],
|
|
239
|
+
diagnostics: []
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
artifacts: [manifestArtifact, summaryArtifact],
|
|
243
|
+
diagnostics: []
|
|
244
|
+
});
|
|
245
|
+
const trace = {
|
|
246
|
+
runId: safeOptional(input.association.runId),
|
|
247
|
+
file: safeOptional(traceFile)
|
|
248
|
+
};
|
|
249
|
+
await promises.writeFile(
|
|
250
|
+
manifestPathResult.absolutePath,
|
|
251
|
+
`${JSON.stringify({ package: PACKAGE_NAME, manifest, trace }, null, 2)}
|
|
252
|
+
`
|
|
253
|
+
);
|
|
254
|
+
const summaryPath = summaryPathResult.absolutePath;
|
|
255
|
+
await promises.writeFile(summaryPath, renderSummary(manifest, trace), "utf-8");
|
|
256
|
+
return {
|
|
257
|
+
directory,
|
|
258
|
+
manifest,
|
|
259
|
+
manifestPath: manifestPathResult.absolutePath,
|
|
260
|
+
status: input.status,
|
|
261
|
+
summaryPath,
|
|
262
|
+
testId: input.identity.id
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function renderSummary(manifest, trace) {
|
|
266
|
+
const result = manifest.results[0];
|
|
267
|
+
return [
|
|
268
|
+
"# AgentInspect Vitest Artifact",
|
|
269
|
+
"",
|
|
270
|
+
`- Test: ${result?.name ?? "unknown"}`,
|
|
271
|
+
`- Test id: ${result?.testId ?? "unknown"}`,
|
|
272
|
+
`- Status: ${result?.status ?? "unknown"}`,
|
|
273
|
+
`- File: ${result?.file ?? "unknown"}`,
|
|
274
|
+
`- Trace run: ${trace.runId ?? "unknown"}`,
|
|
275
|
+
`- Trace file: ${result?.tracePath ?? "unknown"}`,
|
|
276
|
+
`- Manifest schema: ${manifest.schemaVersion}`,
|
|
277
|
+
"",
|
|
278
|
+
"Trace contents are intentionally not embedded in this artifact.",
|
|
279
|
+
""
|
|
280
|
+
].join("\n");
|
|
281
|
+
}
|
|
282
|
+
function formatPathDiagnostics(diagnostics) {
|
|
283
|
+
if (diagnostics.length === 0) return "Unsafe AgentInspect Vitest artifact path.";
|
|
284
|
+
return diagnostics.map((diagnostic) => diagnostic.message).join("; ");
|
|
285
|
+
}
|
|
286
|
+
function* collectTasks(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
287
|
+
if (Array.isArray(input)) {
|
|
288
|
+
for (const item of input) yield* collectTasks(item, seen);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (!isObject(input) || seen.has(input)) return;
|
|
292
|
+
seen.add(input);
|
|
293
|
+
yield input;
|
|
294
|
+
const task = input;
|
|
295
|
+
for (const childList of [task.tasks, task.children]) {
|
|
296
|
+
if (Array.isArray(childList)) yield* collectTasks(childList, seen);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function safeOptional(value) {
|
|
300
|
+
return value === void 0 ? void 0 : boundText(redactText(value));
|
|
301
|
+
}
|
|
302
|
+
function safeSegment(value) {
|
|
303
|
+
const sanitized = redactText(value).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
304
|
+
return sanitized.length > 0 ? sanitized : "trace";
|
|
305
|
+
}
|
|
306
|
+
function boundText(value) {
|
|
307
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
308
|
+
if (compact.length <= MAX_TEXT) return compact;
|
|
309
|
+
return `${compact.slice(0, MAX_TEXT - 12)}...[truncated]`;
|
|
310
|
+
}
|
|
311
|
+
function redactText(value) {
|
|
312
|
+
return value.replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\b(?:api[_-]?key|authorization|token|secret|password)\s*[:=]\s*[^,\s]+/gi, "$1=[REDACTED]");
|
|
313
|
+
}
|
|
314
|
+
function readOptionalString(value, key) {
|
|
315
|
+
if (!isObject(value)) return void 0;
|
|
316
|
+
const property = readProp(value, key);
|
|
317
|
+
return typeof property === "string" && property.length > 0 ? property : void 0;
|
|
318
|
+
}
|
|
319
|
+
function readProp(value, key) {
|
|
320
|
+
return value[key];
|
|
321
|
+
}
|
|
322
|
+
function isObject(value) {
|
|
323
|
+
return typeof value === "object" && value !== null;
|
|
324
|
+
}
|
|
325
|
+
function errorMessage(error) {
|
|
326
|
+
return error instanceof Error ? error.message : String(error);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
exports.agentInspectVitestReporter = agentInspectVitestReporter;
|
|
330
|
+
exports.createAgentInspectVitestReporter = createAgentInspectVitestReporter;
|
|
331
|
+
//# sourceMappingURL=index.cjs.map
|
|
332
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["path","writeFile","createReporterArtifactPath","mkdir","createTraceArtifactManifest"],"mappings":"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","file":"index.cjs","sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n createReporterArtifactPath,\n createTraceArtifactManifest,\n type TraceArtifact,\n type TraceArtifactManifest,\n type TraceArtifactRedactionProfile,\n type TraceReporterDiagnostic,\n} from \"agent-inspect/reporters\";\n\nexport type AgentInspectVitestStatus = \"failed\" | \"passed\" | \"skipped\";\n\n/**\n * Experimental trace association read by `@agent-inspect/vitest`.\n *\n * Attach this object to Vitest task metadata, or return it from\n * `resolveTrace`. Associations are explicit only; the reporter never guesses\n * by timestamps.\n */\nexport interface AgentInspectVitestTraceAssociation {\n /** AgentInspect run/span identifier to show in safe artifacts. */\n readonly runId?: string;\n /** Local trace file path. Only the basename is written to artifacts. */\n readonly tracePath?: string;\n /** Optional stable label for the artifact directory. */\n readonly artifactLabel?: string;\n}\n\n/**\n * Experimental diagnostic emitted when reporter work cannot be completed.\n *\n * Diagnostics are reported instead of throwing so Vitest's original test\n * result remains authoritative.\n */\nexport interface AgentInspectVitestDiagnostic {\n readonly code:\n | \"artifact-write-failed\"\n | \"github-summary-write-failed\"\n | \"on-diagnostic-failed\"\n | \"unsafe-artifact-path\";\n readonly message: string;\n readonly testId?: string;\n}\n\n/**\n * Experimental options for the AgentInspect Vitest reporter.\n *\n * The reporter is local-only: it performs no network I/O and writes bounded,\n * structural artifacts without reading trace contents.\n */\nexport interface AgentInspectVitestReporterOptions {\n /** Directory for safe Vitest artifacts. Defaults to `.agent-inspect/vitest-artifacts`. */\n readonly artifactDir?: string;\n /** Optional GitHub step-summary path. Only bounded structural lines are appended. */\n readonly githubSummary?: string;\n /**\n * Keep artifacts for passing tests. `false`/undefined keeps none; `true`\n * keeps up to `maxSuccessfulTraces`; a number keeps up to that many.\n */\n readonly retainSuccessful?: boolean | number;\n /** Upper bound for retained passing-test artifacts. Defaults to 20. */\n readonly maxSuccessfulTraces?: number;\n /** Redaction profile recorded on generated artifacts. Defaults to `share`. */\n readonly redactionProfile?: TraceArtifactRedactionProfile;\n /** Deterministic manifest timestamp. Defaults to the Unix epoch. */\n readonly generatedAt?: string;\n /** Resolve explicit test-to-trace associations from a Vitest task/result. */\n readonly resolveTrace?: (\n test: unknown,\n ) => AgentInspectVitestTraceAssociation | undefined;\n /** Observe non-fatal reporter diagnostics. */\n readonly onDiagnostic?: (diagnostic: AgentInspectVitestDiagnostic) => void;\n}\n\nexport interface AgentInspectVitestArtifact {\n readonly directory: string;\n readonly manifestPath: string;\n readonly summaryPath: string;\n readonly status: AgentInspectVitestStatus;\n readonly testId: string;\n readonly manifest: TraceArtifactManifest;\n}\n\n/**\n * Experimental Vitest reporter facade.\n *\n * The shape is intentionally structural so the package stays tolerant of\n * Vitest reporter API drift while still exposing the hooks Vitest calls.\n */\nexport interface AgentInspectVitestReporter {\n onTestCaseResult(test: unknown): Promise<void>;\n onTaskUpdate(tasks: unknown): Promise<void>;\n onFinished(files?: unknown): Promise<void>;\n getDiagnostics(): readonly AgentInspectVitestDiagnostic[];\n getArtifacts(): readonly AgentInspectVitestArtifact[];\n}\n\ntype TestIdentity = {\n readonly id: string;\n readonly name: string;\n readonly file?: string;\n};\n\ntype TestLike = {\n readonly id?: unknown;\n readonly name?: unknown;\n readonly suite?: unknown;\n readonly file?: unknown;\n readonly filepath?: unknown;\n readonly result?: unknown;\n readonly meta?: unknown;\n readonly context?: unknown;\n readonly tasks?: unknown;\n readonly children?: unknown;\n};\n\ntype ResultLike = {\n readonly state?: unknown;\n readonly status?: unknown;\n readonly meta?: unknown;\n};\n\nconst PACKAGE_NAME = \"@agent-inspect/vitest\";\nconst DEFAULT_ARTIFACT_DIR = \".agent-inspect/vitest-artifacts\";\nconst DEFAULT_SUCCESS_LIMIT = 20;\nconst MAX_SUCCESS_LIMIT = 100;\nconst MAX_TEXT = 180;\nconst DEFAULT_GENERATED_AT = \"1970-01-01T00:00:00.000Z\";\n\n/**\n * Create the experimental AgentInspect Vitest reporter.\n */\nexport function createAgentInspectVitestReporter(\n options: AgentInspectVitestReporterOptions = {},\n): AgentInspectVitestReporter {\n const diagnostics: AgentInspectVitestDiagnostic[] = [];\n const artifacts: AgentInspectVitestArtifact[] = [];\n const seenObjects = new WeakSet<object>();\n const seenKeys = new Set<string>();\n const artifactNames = new Map<string, number>();\n const successLimit = normalizeSuccessLimit(options);\n let retainedSuccesses = 0;\n\n const reporter: AgentInspectVitestReporter = {\n async onTestCaseResult(test: unknown): Promise<void> {\n await handleTest(test);\n },\n async onTaskUpdate(tasks: unknown): Promise<void> {\n for (const test of collectTasks(tasks)) {\n await handleTest(test);\n }\n },\n async onFinished(files?: unknown): Promise<void> {\n for (const test of collectTasks(files)) {\n await handleTest(test);\n }\n await appendGithubSummary();\n },\n getDiagnostics(): readonly AgentInspectVitestDiagnostic[] {\n return diagnostics.slice();\n },\n getArtifacts(): readonly AgentInspectVitestArtifact[] {\n return artifacts.slice();\n },\n };\n\n async function handleTest(test: unknown): Promise<void> {\n if (!isObject(test)) return;\n if (seenObjects.has(test)) return;\n\n const status = readStatus(test);\n if (status === undefined || status === \"skipped\") return;\n\n const identity = readIdentity(test);\n const seenKey = `${identity.id}:${status}`;\n if (seenKeys.has(seenKey)) return;\n\n const association = resolveAssociation(test, options);\n if (association === undefined) return;\n\n if (status === \"passed\") {\n if (retainedSuccesses >= successLimit) return;\n retainedSuccesses += 1;\n }\n\n seenObjects.add(test);\n seenKeys.add(seenKey);\n\n try {\n const artifact = await writeSafeArtifact({\n artifactDir: options.artifactDir ?? DEFAULT_ARTIFACT_DIR,\n association,\n generatedAt: options.generatedAt ?? DEFAULT_GENERATED_AT,\n identity,\n reserveArtifactName,\n redactionProfile: options.redactionProfile ?? \"share\",\n status,\n });\n artifacts.push(artifact);\n } catch (error) {\n reportDiagnostic({\n code: \"artifact-write-failed\",\n message: `Could not write AgentInspect Vitest artifact: ${errorMessage(error)}`,\n testId: identity.id,\n });\n }\n }\n\n function reserveArtifactName(seed: string): string {\n const base = safeSegment(seed);\n const next = (artifactNames.get(base) ?? 0) + 1;\n artifactNames.set(base, next);\n return next === 1 ? base : `${base}-${next}`;\n }\n\n function reportDiagnostic(diagnostic: AgentInspectVitestDiagnostic): void {\n diagnostics.push(diagnostic);\n if (options.onDiagnostic === undefined) return;\n try {\n options.onDiagnostic(diagnostic);\n } catch (error) {\n diagnostics.push({\n code: \"on-diagnostic-failed\",\n message: `AgentInspect Vitest diagnostic callback failed: ${errorMessage(error)}`,\n testId: diagnostic.testId,\n });\n }\n }\n\n async function appendGithubSummary(): Promise<void> {\n if (options.githubSummary === undefined || artifacts.length === 0) return;\n const failed = artifacts.filter((artifact) => artifact.status === \"failed\").length;\n const passed = artifacts.filter((artifact) => artifact.status === \"passed\").length;\n const lines = [\n \"\",\n \"## AgentInspect Vitest artifacts\",\n \"\",\n `- Failed test artifacts: ${failed}`,\n `- Passing test artifacts retained: ${passed}`,\n `- Artifact directory: ${boundText(path.basename(options.artifactDir ?? DEFAULT_ARTIFACT_DIR))}`,\n \"\",\n ];\n try {\n await writeFile(options.githubSummary, `${lines.join(\"\\n\")}\\n`, { flag: \"a\" });\n } catch (error) {\n reportDiagnostic({\n code: \"github-summary-write-failed\",\n message: `Could not append AgentInspect Vitest summary: ${errorMessage(error)}`,\n });\n }\n }\n\n return reporter;\n}\n\nexport const agentInspectVitestReporter = createAgentInspectVitestReporter;\n\nfunction normalizeSuccessLimit(options: AgentInspectVitestReporterOptions): number {\n const cap = clampCount(options.maxSuccessfulTraces, DEFAULT_SUCCESS_LIMIT);\n if (options.retainSuccessful === true) return cap;\n if (typeof options.retainSuccessful === \"number\") {\n return Math.min(clampCount(options.retainSuccessful, 0), cap);\n }\n return 0;\n}\n\nfunction clampCount(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value)) return fallback;\n return Math.max(0, Math.min(MAX_SUCCESS_LIMIT, Math.floor(value)));\n}\n\nfunction resolveAssociation(\n test: object,\n options: AgentInspectVitestReporterOptions,\n): AgentInspectVitestTraceAssociation | undefined {\n const resolved = options.resolveTrace?.(test);\n if (isAssociation(resolved)) return resolved;\n\n const task = test as TestLike;\n const result = isObject(task.result) ? (task.result as ResultLike) : undefined;\n return (\n readAssociationFromMeta(task.meta) ??\n readAssociationFromContext(task.context) ??\n readAssociationFromMeta(result?.meta)\n );\n}\n\nfunction readAssociationFromContext(\n context: unknown,\n): AgentInspectVitestTraceAssociation | undefined {\n if (!isObject(context)) return undefined;\n const maybeMeta = readProp(context, \"meta\");\n return readAssociationFromMeta(maybeMeta);\n}\n\nfunction readAssociationFromMeta(\n meta: unknown,\n): AgentInspectVitestTraceAssociation | undefined {\n if (!isObject(meta)) return undefined;\n const direct = readProp(meta, \"agentInspect\");\n const hyphenated = readProp(meta, \"agent-inspect\");\n const trace = readProp(meta, \"trace\");\n for (const candidate of [direct, hyphenated, trace]) {\n if (isAssociation(candidate)) return candidate;\n }\n return undefined;\n}\n\nfunction isAssociation(\n value: unknown,\n): value is AgentInspectVitestTraceAssociation {\n if (!isObject(value)) return false;\n return (\n readOptionalString(value, \"runId\") !== undefined ||\n readOptionalString(value, \"tracePath\") !== undefined ||\n readOptionalString(value, \"artifactLabel\") !== undefined\n );\n}\n\nfunction readStatus(test: object): AgentInspectVitestStatus | undefined {\n const task = test as TestLike;\n const result = isObject(task.result) ? (task.result as ResultLike) : undefined;\n const raw =\n readOptionalString(result, \"state\") ??\n readOptionalString(result, \"status\") ??\n readOptionalString(test, \"state\") ??\n readOptionalString(test, \"status\");\n if (raw === undefined) return undefined;\n\n const normalized = raw.toLowerCase();\n if (normalized === \"fail\" || normalized === \"failed\") return \"failed\";\n if (normalized === \"pass\" || normalized === \"passed\") return \"passed\";\n if (normalized === \"skip\" || normalized === \"skipped\") return \"skipped\";\n return undefined;\n}\n\nfunction readIdentity(test: object): TestIdentity {\n const task = test as TestLike;\n const id = readOptionalString(test, \"id\") ?? readOptionalString(test, \"taskId\");\n const name =\n readOptionalString(test, \"fullName\") ??\n readOptionalString(test, \"name\") ??\n readOptionalString(task.suite, \"name\") ??\n id ??\n \"unknown-test\";\n const file =\n readOptionalString(test, \"file\") ??\n readOptionalString(test, \"filepath\") ??\n readOptionalString(task.suite, \"file\");\n return {\n file: file === undefined ? undefined : path.basename(file),\n id: boundText(redactText(id ?? name)),\n name: boundText(redactText(name)),\n };\n}\n\nasync function writeSafeArtifact(input: {\n readonly artifactDir: string;\n readonly association: AgentInspectVitestTraceAssociation;\n readonly generatedAt: string;\n readonly identity: TestIdentity;\n readonly redactionProfile: TraceArtifactRedactionProfile;\n readonly reserveArtifactName: (seed: string) => string;\n readonly status: AgentInspectVitestStatus;\n}): Promise<AgentInspectVitestArtifact> {\n const artifactSeed =\n input.association.artifactLabel ??\n input.identity.id ??\n input.identity.name ??\n input.status;\n const artifactName = input.reserveArtifactName(artifactSeed);\n const manifestPathResult = createReporterArtifactPath({\n outputDir: input.artifactDir,\n testId: artifactName,\n name: input.identity.name,\n file: input.identity.file,\n kind: \"report\",\n format: \"json\",\n });\n const summaryPathResult = createReporterArtifactPath({\n outputDir: input.artifactDir,\n testId: artifactName,\n name: input.identity.name,\n file: input.identity.file,\n kind: \"summary\",\n format: \"md\",\n });\n\n if (\n !manifestPathResult.ok ||\n manifestPathResult.absolutePath === undefined ||\n manifestPathResult.relativePath === undefined\n ) {\n throw new Error(formatPathDiagnostics(manifestPathResult.diagnostics));\n }\n if (\n !summaryPathResult.ok ||\n summaryPathResult.absolutePath === undefined ||\n summaryPathResult.relativePath === undefined\n ) {\n throw new Error(formatPathDiagnostics(summaryPathResult.diagnostics));\n }\n\n const directory = path.dirname(manifestPathResult.absolutePath);\n await mkdir(directory, { recursive: true });\n\n const traceFile =\n input.association.tracePath === undefined\n ? undefined\n : path.basename(input.association.tracePath);\n const manifestArtifact: TraceArtifact = {\n kind: \"report\",\n path: manifestPathResult.relativePath,\n format: \"json\",\n redactionProfile: input.redactionProfile,\n };\n const summaryArtifact: TraceArtifact = {\n kind: \"summary\",\n path: summaryPathResult.relativePath,\n format: \"md\",\n redactionProfile: input.redactionProfile,\n };\n const manifest = createTraceArtifactManifest({\n framework: \"vitest\",\n generatedAt: input.generatedAt,\n results: [\n {\n testId: input.identity.id,\n name: input.identity.name,\n ...(input.identity.file === undefined ? {} : { file: input.identity.file }),\n status: input.status,\n tracePath: safeOptional(traceFile),\n artifacts: [manifestArtifact, summaryArtifact],\n diagnostics: [],\n },\n ],\n artifacts: [manifestArtifact, summaryArtifact],\n diagnostics: [],\n });\n const trace = {\n runId: safeOptional(input.association.runId),\n file: safeOptional(traceFile),\n };\n\n await writeFile(\n manifestPathResult.absolutePath,\n `${JSON.stringify({ package: PACKAGE_NAME, manifest, trace }, null, 2)}\\n`,\n );\n const summaryPath = summaryPathResult.absolutePath;\n await writeFile(summaryPath, renderSummary(manifest, trace), \"utf-8\");\n\n return {\n directory,\n manifest,\n manifestPath: manifestPathResult.absolutePath,\n status: input.status,\n summaryPath,\n testId: input.identity.id,\n };\n}\n\nfunction renderSummary(\n manifest: TraceArtifactManifest,\n trace: { readonly runId?: string; readonly file?: string },\n): string {\n const result = manifest.results[0];\n return [\n \"# AgentInspect Vitest Artifact\",\n \"\",\n `- Test: ${result?.name ?? \"unknown\"}`,\n `- Test id: ${result?.testId ?? \"unknown\"}`,\n `- Status: ${result?.status ?? \"unknown\"}`,\n `- File: ${result?.file ?? \"unknown\"}`,\n `- Trace run: ${trace.runId ?? \"unknown\"}`,\n `- Trace file: ${result?.tracePath ?? \"unknown\"}`,\n `- Manifest schema: ${manifest.schemaVersion}`,\n \"\",\n \"Trace contents are intentionally not embedded in this artifact.\",\n \"\",\n ].join(\"\\n\");\n}\n\nfunction formatPathDiagnostics(\n diagnostics: readonly TraceReporterDiagnostic[],\n): string {\n if (diagnostics.length === 0) return \"Unsafe AgentInspect Vitest artifact path.\";\n return diagnostics.map((diagnostic) => diagnostic.message).join(\"; \");\n}\n\nfunction* collectTasks(input: unknown, seen = new WeakSet<object>()): Iterable<unknown> {\n if (Array.isArray(input)) {\n for (const item of input) yield* collectTasks(item, seen);\n return;\n }\n if (!isObject(input) || seen.has(input)) return;\n\n seen.add(input);\n yield input;\n\n const task = input as TestLike;\n for (const childList of [task.tasks, task.children]) {\n if (Array.isArray(childList)) yield* collectTasks(childList, seen);\n }\n}\n\nfunction safeOptional(value: string | undefined): string | undefined {\n return value === undefined ? undefined : boundText(redactText(value));\n}\n\nfunction safeSegment(value: string): string {\n const sanitized = redactText(value)\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 80);\n return sanitized.length > 0 ? sanitized : \"trace\";\n}\n\nfunction boundText(value: string): string {\n const compact = value.replace(/\\s+/g, \" \").trim();\n if (compact.length <= MAX_TEXT) return compact;\n return `${compact.slice(0, MAX_TEXT - 12)}...[truncated]`;\n}\n\nfunction redactText(value: string): string {\n return value\n .replace(/\\bsk-[A-Za-z0-9_-]{8,}\\b/g, \"[REDACTED]\")\n .replace(/\\b(?:api[_-]?key|authorization|token|secret|password)\\s*[:=]\\s*[^,\\s]+/gi, \"$1=[REDACTED]\");\n}\n\nfunction readOptionalString(value: unknown, key: string): string | undefined {\n if (!isObject(value)) return undefined;\n const property = readProp(value, key);\n return typeof property === \"string\" && property.length > 0 ? property : undefined;\n}\n\nfunction readProp(value: object, key: string): unknown {\n return (value as Record<string, unknown>)[key];\n}\n\nfunction isObject(value: unknown): value is object {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
declare const TRACE_ARTIFACT_MANIFEST_SCHEMA_VERSION: "0.1";
|
|
2
|
+
/** Experimental reporter framework identifier for local CI artifacts. */
|
|
3
|
+
type TraceReporterFramework = "vitest" | "jest" | "manual";
|
|
4
|
+
/** Experimental reporter test status normalized across supported test runners. */
|
|
5
|
+
type TraceTestStatus = "passed" | "failed" | "skipped" | "todo";
|
|
6
|
+
/** Experimental reporter artifact kind used in artifact manifests. */
|
|
7
|
+
type TraceArtifactKind = "trace" | "report" | "eval" | "redaction" | "summary";
|
|
8
|
+
/** Experimental reporter artifact file format used in artifact manifests. */
|
|
9
|
+
type TraceArtifactFormat = "json" | "jsonl" | "md" | "html";
|
|
10
|
+
/** Experimental redaction profile marker recorded with reporter artifacts. */
|
|
11
|
+
type TraceArtifactRedactionProfile = "local" | "share" | "strict";
|
|
12
|
+
/** Experimental reporter diagnostic severity. */
|
|
13
|
+
type TraceReporterDiagnosticSeverity = "info" | "warning" | "error";
|
|
14
|
+
/** Experimental reporter diagnostic code for safe, non-throwing reporter helpers. */
|
|
15
|
+
type TraceReporterDiagnosticCode = "invalid_artifact_path" | "artifact_path_escape" | "artifact_path_empty" | "artifact_path_absolute" | "reporter_failure";
|
|
16
|
+
/** Experimental diagnostic emitted by reporter helpers instead of throwing. */
|
|
17
|
+
interface TraceReporterDiagnostic {
|
|
18
|
+
code: TraceReporterDiagnosticCode;
|
|
19
|
+
severity: TraceReporterDiagnosticSeverity;
|
|
20
|
+
message: string;
|
|
21
|
+
target?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Experimental manifest artifact entry shared by framework reporters. */
|
|
24
|
+
interface TraceArtifact {
|
|
25
|
+
kind: TraceArtifactKind;
|
|
26
|
+
path: string;
|
|
27
|
+
format: TraceArtifactFormat;
|
|
28
|
+
redactionProfile: TraceArtifactRedactionProfile;
|
|
29
|
+
title?: string;
|
|
30
|
+
sizeBytes?: number;
|
|
31
|
+
diagnostics?: TraceReporterDiagnostic[];
|
|
32
|
+
}
|
|
33
|
+
/** Experimental normalized test result entry shared by framework reporters. */
|
|
34
|
+
interface TraceTestResult {
|
|
35
|
+
testId: string;
|
|
36
|
+
name: string;
|
|
37
|
+
file?: string;
|
|
38
|
+
status: TraceTestStatus;
|
|
39
|
+
durationMs?: number;
|
|
40
|
+
tracePath?: string;
|
|
41
|
+
artifacts?: TraceArtifact[];
|
|
42
|
+
diagnostics?: TraceReporterDiagnostic[];
|
|
43
|
+
}
|
|
44
|
+
/** Experimental reporter artifact manifest shared by framework reporters. */
|
|
45
|
+
interface TraceArtifactManifest {
|
|
46
|
+
schemaVersion: typeof TRACE_ARTIFACT_MANIFEST_SCHEMA_VERSION;
|
|
47
|
+
generatedAt: string;
|
|
48
|
+
framework: TraceReporterFramework;
|
|
49
|
+
results: TraceTestResult[];
|
|
50
|
+
artifacts: TraceArtifact[];
|
|
51
|
+
diagnostics: TraceReporterDiagnostic[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type AgentInspectVitestStatus = "failed" | "passed" | "skipped";
|
|
55
|
+
/**
|
|
56
|
+
* Experimental trace association read by `@agent-inspect/vitest`.
|
|
57
|
+
*
|
|
58
|
+
* Attach this object to Vitest task metadata, or return it from
|
|
59
|
+
* `resolveTrace`. Associations are explicit only; the reporter never guesses
|
|
60
|
+
* by timestamps.
|
|
61
|
+
*/
|
|
62
|
+
interface AgentInspectVitestTraceAssociation {
|
|
63
|
+
/** AgentInspect run/span identifier to show in safe artifacts. */
|
|
64
|
+
readonly runId?: string;
|
|
65
|
+
/** Local trace file path. Only the basename is written to artifacts. */
|
|
66
|
+
readonly tracePath?: string;
|
|
67
|
+
/** Optional stable label for the artifact directory. */
|
|
68
|
+
readonly artifactLabel?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Experimental diagnostic emitted when reporter work cannot be completed.
|
|
72
|
+
*
|
|
73
|
+
* Diagnostics are reported instead of throwing so Vitest's original test
|
|
74
|
+
* result remains authoritative.
|
|
75
|
+
*/
|
|
76
|
+
interface AgentInspectVitestDiagnostic {
|
|
77
|
+
readonly code: "artifact-write-failed" | "github-summary-write-failed" | "on-diagnostic-failed" | "unsafe-artifact-path";
|
|
78
|
+
readonly message: string;
|
|
79
|
+
readonly testId?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Experimental options for the AgentInspect Vitest reporter.
|
|
83
|
+
*
|
|
84
|
+
* The reporter is local-only: it performs no network I/O and writes bounded,
|
|
85
|
+
* structural artifacts without reading trace contents.
|
|
86
|
+
*/
|
|
87
|
+
interface AgentInspectVitestReporterOptions {
|
|
88
|
+
/** Directory for safe Vitest artifacts. Defaults to `.agent-inspect/vitest-artifacts`. */
|
|
89
|
+
readonly artifactDir?: string;
|
|
90
|
+
/** Optional GitHub step-summary path. Only bounded structural lines are appended. */
|
|
91
|
+
readonly githubSummary?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Keep artifacts for passing tests. `false`/undefined keeps none; `true`
|
|
94
|
+
* keeps up to `maxSuccessfulTraces`; a number keeps up to that many.
|
|
95
|
+
*/
|
|
96
|
+
readonly retainSuccessful?: boolean | number;
|
|
97
|
+
/** Upper bound for retained passing-test artifacts. Defaults to 20. */
|
|
98
|
+
readonly maxSuccessfulTraces?: number;
|
|
99
|
+
/** Redaction profile recorded on generated artifacts. Defaults to `share`. */
|
|
100
|
+
readonly redactionProfile?: TraceArtifactRedactionProfile;
|
|
101
|
+
/** Deterministic manifest timestamp. Defaults to the Unix epoch. */
|
|
102
|
+
readonly generatedAt?: string;
|
|
103
|
+
/** Resolve explicit test-to-trace associations from a Vitest task/result. */
|
|
104
|
+
readonly resolveTrace?: (test: unknown) => AgentInspectVitestTraceAssociation | undefined;
|
|
105
|
+
/** Observe non-fatal reporter diagnostics. */
|
|
106
|
+
readonly onDiagnostic?: (diagnostic: AgentInspectVitestDiagnostic) => void;
|
|
107
|
+
}
|
|
108
|
+
interface AgentInspectVitestArtifact {
|
|
109
|
+
readonly directory: string;
|
|
110
|
+
readonly manifestPath: string;
|
|
111
|
+
readonly summaryPath: string;
|
|
112
|
+
readonly status: AgentInspectVitestStatus;
|
|
113
|
+
readonly testId: string;
|
|
114
|
+
readonly manifest: TraceArtifactManifest;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Experimental Vitest reporter facade.
|
|
118
|
+
*
|
|
119
|
+
* The shape is intentionally structural so the package stays tolerant of
|
|
120
|
+
* Vitest reporter API drift while still exposing the hooks Vitest calls.
|
|
121
|
+
*/
|
|
122
|
+
interface AgentInspectVitestReporter {
|
|
123
|
+
onTestCaseResult(test: unknown): Promise<void>;
|
|
124
|
+
onTaskUpdate(tasks: unknown): Promise<void>;
|
|
125
|
+
onFinished(files?: unknown): Promise<void>;
|
|
126
|
+
getDiagnostics(): readonly AgentInspectVitestDiagnostic[];
|
|
127
|
+
getArtifacts(): readonly AgentInspectVitestArtifact[];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create the experimental AgentInspect Vitest reporter.
|
|
131
|
+
*/
|
|
132
|
+
declare function createAgentInspectVitestReporter(options?: AgentInspectVitestReporterOptions): AgentInspectVitestReporter;
|
|
133
|
+
declare const agentInspectVitestReporter: typeof createAgentInspectVitestReporter;
|
|
134
|
+
|
|
135
|
+
export { type AgentInspectVitestArtifact, type AgentInspectVitestDiagnostic, type AgentInspectVitestReporter, type AgentInspectVitestReporterOptions, type AgentInspectVitestStatus, type AgentInspectVitestTraceAssociation, agentInspectVitestReporter, createAgentInspectVitestReporter };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
declare const TRACE_ARTIFACT_MANIFEST_SCHEMA_VERSION: "0.1";
|
|
2
|
+
/** Experimental reporter framework identifier for local CI artifacts. */
|
|
3
|
+
type TraceReporterFramework = "vitest" | "jest" | "manual";
|
|
4
|
+
/** Experimental reporter test status normalized across supported test runners. */
|
|
5
|
+
type TraceTestStatus = "passed" | "failed" | "skipped" | "todo";
|
|
6
|
+
/** Experimental reporter artifact kind used in artifact manifests. */
|
|
7
|
+
type TraceArtifactKind = "trace" | "report" | "eval" | "redaction" | "summary";
|
|
8
|
+
/** Experimental reporter artifact file format used in artifact manifests. */
|
|
9
|
+
type TraceArtifactFormat = "json" | "jsonl" | "md" | "html";
|
|
10
|
+
/** Experimental redaction profile marker recorded with reporter artifacts. */
|
|
11
|
+
type TraceArtifactRedactionProfile = "local" | "share" | "strict";
|
|
12
|
+
/** Experimental reporter diagnostic severity. */
|
|
13
|
+
type TraceReporterDiagnosticSeverity = "info" | "warning" | "error";
|
|
14
|
+
/** Experimental reporter diagnostic code for safe, non-throwing reporter helpers. */
|
|
15
|
+
type TraceReporterDiagnosticCode = "invalid_artifact_path" | "artifact_path_escape" | "artifact_path_empty" | "artifact_path_absolute" | "reporter_failure";
|
|
16
|
+
/** Experimental diagnostic emitted by reporter helpers instead of throwing. */
|
|
17
|
+
interface TraceReporterDiagnostic {
|
|
18
|
+
code: TraceReporterDiagnosticCode;
|
|
19
|
+
severity: TraceReporterDiagnosticSeverity;
|
|
20
|
+
message: string;
|
|
21
|
+
target?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Experimental manifest artifact entry shared by framework reporters. */
|
|
24
|
+
interface TraceArtifact {
|
|
25
|
+
kind: TraceArtifactKind;
|
|
26
|
+
path: string;
|
|
27
|
+
format: TraceArtifactFormat;
|
|
28
|
+
redactionProfile: TraceArtifactRedactionProfile;
|
|
29
|
+
title?: string;
|
|
30
|
+
sizeBytes?: number;
|
|
31
|
+
diagnostics?: TraceReporterDiagnostic[];
|
|
32
|
+
}
|
|
33
|
+
/** Experimental normalized test result entry shared by framework reporters. */
|
|
34
|
+
interface TraceTestResult {
|
|
35
|
+
testId: string;
|
|
36
|
+
name: string;
|
|
37
|
+
file?: string;
|
|
38
|
+
status: TraceTestStatus;
|
|
39
|
+
durationMs?: number;
|
|
40
|
+
tracePath?: string;
|
|
41
|
+
artifacts?: TraceArtifact[];
|
|
42
|
+
diagnostics?: TraceReporterDiagnostic[];
|
|
43
|
+
}
|
|
44
|
+
/** Experimental reporter artifact manifest shared by framework reporters. */
|
|
45
|
+
interface TraceArtifactManifest {
|
|
46
|
+
schemaVersion: typeof TRACE_ARTIFACT_MANIFEST_SCHEMA_VERSION;
|
|
47
|
+
generatedAt: string;
|
|
48
|
+
framework: TraceReporterFramework;
|
|
49
|
+
results: TraceTestResult[];
|
|
50
|
+
artifacts: TraceArtifact[];
|
|
51
|
+
diagnostics: TraceReporterDiagnostic[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type AgentInspectVitestStatus = "failed" | "passed" | "skipped";
|
|
55
|
+
/**
|
|
56
|
+
* Experimental trace association read by `@agent-inspect/vitest`.
|
|
57
|
+
*
|
|
58
|
+
* Attach this object to Vitest task metadata, or return it from
|
|
59
|
+
* `resolveTrace`. Associations are explicit only; the reporter never guesses
|
|
60
|
+
* by timestamps.
|
|
61
|
+
*/
|
|
62
|
+
interface AgentInspectVitestTraceAssociation {
|
|
63
|
+
/** AgentInspect run/span identifier to show in safe artifacts. */
|
|
64
|
+
readonly runId?: string;
|
|
65
|
+
/** Local trace file path. Only the basename is written to artifacts. */
|
|
66
|
+
readonly tracePath?: string;
|
|
67
|
+
/** Optional stable label for the artifact directory. */
|
|
68
|
+
readonly artifactLabel?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Experimental diagnostic emitted when reporter work cannot be completed.
|
|
72
|
+
*
|
|
73
|
+
* Diagnostics are reported instead of throwing so Vitest's original test
|
|
74
|
+
* result remains authoritative.
|
|
75
|
+
*/
|
|
76
|
+
interface AgentInspectVitestDiagnostic {
|
|
77
|
+
readonly code: "artifact-write-failed" | "github-summary-write-failed" | "on-diagnostic-failed" | "unsafe-artifact-path";
|
|
78
|
+
readonly message: string;
|
|
79
|
+
readonly testId?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Experimental options for the AgentInspect Vitest reporter.
|
|
83
|
+
*
|
|
84
|
+
* The reporter is local-only: it performs no network I/O and writes bounded,
|
|
85
|
+
* structural artifacts without reading trace contents.
|
|
86
|
+
*/
|
|
87
|
+
interface AgentInspectVitestReporterOptions {
|
|
88
|
+
/** Directory for safe Vitest artifacts. Defaults to `.agent-inspect/vitest-artifacts`. */
|
|
89
|
+
readonly artifactDir?: string;
|
|
90
|
+
/** Optional GitHub step-summary path. Only bounded structural lines are appended. */
|
|
91
|
+
readonly githubSummary?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Keep artifacts for passing tests. `false`/undefined keeps none; `true`
|
|
94
|
+
* keeps up to `maxSuccessfulTraces`; a number keeps up to that many.
|
|
95
|
+
*/
|
|
96
|
+
readonly retainSuccessful?: boolean | number;
|
|
97
|
+
/** Upper bound for retained passing-test artifacts. Defaults to 20. */
|
|
98
|
+
readonly maxSuccessfulTraces?: number;
|
|
99
|
+
/** Redaction profile recorded on generated artifacts. Defaults to `share`. */
|
|
100
|
+
readonly redactionProfile?: TraceArtifactRedactionProfile;
|
|
101
|
+
/** Deterministic manifest timestamp. Defaults to the Unix epoch. */
|
|
102
|
+
readonly generatedAt?: string;
|
|
103
|
+
/** Resolve explicit test-to-trace associations from a Vitest task/result. */
|
|
104
|
+
readonly resolveTrace?: (test: unknown) => AgentInspectVitestTraceAssociation | undefined;
|
|
105
|
+
/** Observe non-fatal reporter diagnostics. */
|
|
106
|
+
readonly onDiagnostic?: (diagnostic: AgentInspectVitestDiagnostic) => void;
|
|
107
|
+
}
|
|
108
|
+
interface AgentInspectVitestArtifact {
|
|
109
|
+
readonly directory: string;
|
|
110
|
+
readonly manifestPath: string;
|
|
111
|
+
readonly summaryPath: string;
|
|
112
|
+
readonly status: AgentInspectVitestStatus;
|
|
113
|
+
readonly testId: string;
|
|
114
|
+
readonly manifest: TraceArtifactManifest;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Experimental Vitest reporter facade.
|
|
118
|
+
*
|
|
119
|
+
* The shape is intentionally structural so the package stays tolerant of
|
|
120
|
+
* Vitest reporter API drift while still exposing the hooks Vitest calls.
|
|
121
|
+
*/
|
|
122
|
+
interface AgentInspectVitestReporter {
|
|
123
|
+
onTestCaseResult(test: unknown): Promise<void>;
|
|
124
|
+
onTaskUpdate(tasks: unknown): Promise<void>;
|
|
125
|
+
onFinished(files?: unknown): Promise<void>;
|
|
126
|
+
getDiagnostics(): readonly AgentInspectVitestDiagnostic[];
|
|
127
|
+
getArtifacts(): readonly AgentInspectVitestArtifact[];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create the experimental AgentInspect Vitest reporter.
|
|
131
|
+
*/
|
|
132
|
+
declare function createAgentInspectVitestReporter(options?: AgentInspectVitestReporterOptions): AgentInspectVitestReporter;
|
|
133
|
+
declare const agentInspectVitestReporter: typeof createAgentInspectVitestReporter;
|
|
134
|
+
|
|
135
|
+
export { type AgentInspectVitestArtifact, type AgentInspectVitestDiagnostic, type AgentInspectVitestReporter, type AgentInspectVitestReporterOptions, type AgentInspectVitestStatus, type AgentInspectVitestTraceAssociation, agentInspectVitestReporter, createAgentInspectVitestReporter };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { writeFile, mkdir } from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createReporterArtifactPath, createTraceArtifactManifest } from 'agent-inspect/reporters';
|
|
4
|
+
|
|
5
|
+
// packages/vitest/src/index.ts
|
|
6
|
+
var PACKAGE_NAME = "@agent-inspect/vitest";
|
|
7
|
+
var DEFAULT_ARTIFACT_DIR = ".agent-inspect/vitest-artifacts";
|
|
8
|
+
var DEFAULT_SUCCESS_LIMIT = 20;
|
|
9
|
+
var MAX_SUCCESS_LIMIT = 100;
|
|
10
|
+
var MAX_TEXT = 180;
|
|
11
|
+
var DEFAULT_GENERATED_AT = "1970-01-01T00:00:00.000Z";
|
|
12
|
+
function createAgentInspectVitestReporter(options = {}) {
|
|
13
|
+
const diagnostics = [];
|
|
14
|
+
const artifacts = [];
|
|
15
|
+
const seenObjects = /* @__PURE__ */ new WeakSet();
|
|
16
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
17
|
+
const artifactNames = /* @__PURE__ */ new Map();
|
|
18
|
+
const successLimit = normalizeSuccessLimit(options);
|
|
19
|
+
let retainedSuccesses = 0;
|
|
20
|
+
const reporter = {
|
|
21
|
+
async onTestCaseResult(test) {
|
|
22
|
+
await handleTest(test);
|
|
23
|
+
},
|
|
24
|
+
async onTaskUpdate(tasks) {
|
|
25
|
+
for (const test of collectTasks(tasks)) {
|
|
26
|
+
await handleTest(test);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async onFinished(files) {
|
|
30
|
+
for (const test of collectTasks(files)) {
|
|
31
|
+
await handleTest(test);
|
|
32
|
+
}
|
|
33
|
+
await appendGithubSummary();
|
|
34
|
+
},
|
|
35
|
+
getDiagnostics() {
|
|
36
|
+
return diagnostics.slice();
|
|
37
|
+
},
|
|
38
|
+
getArtifacts() {
|
|
39
|
+
return artifacts.slice();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
async function handleTest(test) {
|
|
43
|
+
if (!isObject(test)) return;
|
|
44
|
+
if (seenObjects.has(test)) return;
|
|
45
|
+
const status = readStatus(test);
|
|
46
|
+
if (status === void 0 || status === "skipped") return;
|
|
47
|
+
const identity = readIdentity(test);
|
|
48
|
+
const seenKey = `${identity.id}:${status}`;
|
|
49
|
+
if (seenKeys.has(seenKey)) return;
|
|
50
|
+
const association = resolveAssociation(test, options);
|
|
51
|
+
if (association === void 0) return;
|
|
52
|
+
if (status === "passed") {
|
|
53
|
+
if (retainedSuccesses >= successLimit) return;
|
|
54
|
+
retainedSuccesses += 1;
|
|
55
|
+
}
|
|
56
|
+
seenObjects.add(test);
|
|
57
|
+
seenKeys.add(seenKey);
|
|
58
|
+
try {
|
|
59
|
+
const artifact = await writeSafeArtifact({
|
|
60
|
+
artifactDir: options.artifactDir ?? DEFAULT_ARTIFACT_DIR,
|
|
61
|
+
association,
|
|
62
|
+
generatedAt: options.generatedAt ?? DEFAULT_GENERATED_AT,
|
|
63
|
+
identity,
|
|
64
|
+
reserveArtifactName,
|
|
65
|
+
redactionProfile: options.redactionProfile ?? "share",
|
|
66
|
+
status
|
|
67
|
+
});
|
|
68
|
+
artifacts.push(artifact);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
reportDiagnostic({
|
|
71
|
+
code: "artifact-write-failed",
|
|
72
|
+
message: `Could not write AgentInspect Vitest artifact: ${errorMessage(error)}`,
|
|
73
|
+
testId: identity.id
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function reserveArtifactName(seed) {
|
|
78
|
+
const base = safeSegment(seed);
|
|
79
|
+
const next = (artifactNames.get(base) ?? 0) + 1;
|
|
80
|
+
artifactNames.set(base, next);
|
|
81
|
+
return next === 1 ? base : `${base}-${next}`;
|
|
82
|
+
}
|
|
83
|
+
function reportDiagnostic(diagnostic) {
|
|
84
|
+
diagnostics.push(diagnostic);
|
|
85
|
+
if (options.onDiagnostic === void 0) return;
|
|
86
|
+
try {
|
|
87
|
+
options.onDiagnostic(diagnostic);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
diagnostics.push({
|
|
90
|
+
code: "on-diagnostic-failed",
|
|
91
|
+
message: `AgentInspect Vitest diagnostic callback failed: ${errorMessage(error)}`,
|
|
92
|
+
testId: diagnostic.testId
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function appendGithubSummary() {
|
|
97
|
+
if (options.githubSummary === void 0 || artifacts.length === 0) return;
|
|
98
|
+
const failed = artifacts.filter((artifact) => artifact.status === "failed").length;
|
|
99
|
+
const passed = artifacts.filter((artifact) => artifact.status === "passed").length;
|
|
100
|
+
const lines = [
|
|
101
|
+
"",
|
|
102
|
+
"## AgentInspect Vitest artifacts",
|
|
103
|
+
"",
|
|
104
|
+
`- Failed test artifacts: ${failed}`,
|
|
105
|
+
`- Passing test artifacts retained: ${passed}`,
|
|
106
|
+
`- Artifact directory: ${boundText(path.basename(options.artifactDir ?? DEFAULT_ARTIFACT_DIR))}`,
|
|
107
|
+
""
|
|
108
|
+
];
|
|
109
|
+
try {
|
|
110
|
+
await writeFile(options.githubSummary, `${lines.join("\n")}
|
|
111
|
+
`, { flag: "a" });
|
|
112
|
+
} catch (error) {
|
|
113
|
+
reportDiagnostic({
|
|
114
|
+
code: "github-summary-write-failed",
|
|
115
|
+
message: `Could not append AgentInspect Vitest summary: ${errorMessage(error)}`
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return reporter;
|
|
120
|
+
}
|
|
121
|
+
var agentInspectVitestReporter = createAgentInspectVitestReporter;
|
|
122
|
+
function normalizeSuccessLimit(options) {
|
|
123
|
+
const cap = clampCount(options.maxSuccessfulTraces, DEFAULT_SUCCESS_LIMIT);
|
|
124
|
+
if (options.retainSuccessful === true) return cap;
|
|
125
|
+
if (typeof options.retainSuccessful === "number") {
|
|
126
|
+
return Math.min(clampCount(options.retainSuccessful, 0), cap);
|
|
127
|
+
}
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
function clampCount(value, fallback) {
|
|
131
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
132
|
+
return Math.max(0, Math.min(MAX_SUCCESS_LIMIT, Math.floor(value)));
|
|
133
|
+
}
|
|
134
|
+
function resolveAssociation(test, options) {
|
|
135
|
+
const resolved = options.resolveTrace?.(test);
|
|
136
|
+
if (isAssociation(resolved)) return resolved;
|
|
137
|
+
const task = test;
|
|
138
|
+
const result = isObject(task.result) ? task.result : void 0;
|
|
139
|
+
return readAssociationFromMeta(task.meta) ?? readAssociationFromContext(task.context) ?? readAssociationFromMeta(result?.meta);
|
|
140
|
+
}
|
|
141
|
+
function readAssociationFromContext(context) {
|
|
142
|
+
if (!isObject(context)) return void 0;
|
|
143
|
+
const maybeMeta = readProp(context, "meta");
|
|
144
|
+
return readAssociationFromMeta(maybeMeta);
|
|
145
|
+
}
|
|
146
|
+
function readAssociationFromMeta(meta) {
|
|
147
|
+
if (!isObject(meta)) return void 0;
|
|
148
|
+
const direct = readProp(meta, "agentInspect");
|
|
149
|
+
const hyphenated = readProp(meta, "agent-inspect");
|
|
150
|
+
const trace = readProp(meta, "trace");
|
|
151
|
+
for (const candidate of [direct, hyphenated, trace]) {
|
|
152
|
+
if (isAssociation(candidate)) return candidate;
|
|
153
|
+
}
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
156
|
+
function isAssociation(value) {
|
|
157
|
+
if (!isObject(value)) return false;
|
|
158
|
+
return readOptionalString(value, "runId") !== void 0 || readOptionalString(value, "tracePath") !== void 0 || readOptionalString(value, "artifactLabel") !== void 0;
|
|
159
|
+
}
|
|
160
|
+
function readStatus(test) {
|
|
161
|
+
const task = test;
|
|
162
|
+
const result = isObject(task.result) ? task.result : void 0;
|
|
163
|
+
const raw = readOptionalString(result, "state") ?? readOptionalString(result, "status") ?? readOptionalString(test, "state") ?? readOptionalString(test, "status");
|
|
164
|
+
if (raw === void 0) return void 0;
|
|
165
|
+
const normalized = raw.toLowerCase();
|
|
166
|
+
if (normalized === "fail" || normalized === "failed") return "failed";
|
|
167
|
+
if (normalized === "pass" || normalized === "passed") return "passed";
|
|
168
|
+
if (normalized === "skip" || normalized === "skipped") return "skipped";
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
function readIdentity(test) {
|
|
172
|
+
const task = test;
|
|
173
|
+
const id = readOptionalString(test, "id") ?? readOptionalString(test, "taskId");
|
|
174
|
+
const name = readOptionalString(test, "fullName") ?? readOptionalString(test, "name") ?? readOptionalString(task.suite, "name") ?? id ?? "unknown-test";
|
|
175
|
+
const file = readOptionalString(test, "file") ?? readOptionalString(test, "filepath") ?? readOptionalString(task.suite, "file");
|
|
176
|
+
return {
|
|
177
|
+
file: file === void 0 ? void 0 : path.basename(file),
|
|
178
|
+
id: boundText(redactText(id ?? name)),
|
|
179
|
+
name: boundText(redactText(name))
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
async function writeSafeArtifact(input) {
|
|
183
|
+
const artifactSeed = input.association.artifactLabel ?? input.identity.id ?? input.identity.name ?? input.status;
|
|
184
|
+
const artifactName = input.reserveArtifactName(artifactSeed);
|
|
185
|
+
const manifestPathResult = createReporterArtifactPath({
|
|
186
|
+
outputDir: input.artifactDir,
|
|
187
|
+
testId: artifactName,
|
|
188
|
+
name: input.identity.name,
|
|
189
|
+
file: input.identity.file,
|
|
190
|
+
kind: "report",
|
|
191
|
+
format: "json"
|
|
192
|
+
});
|
|
193
|
+
const summaryPathResult = createReporterArtifactPath({
|
|
194
|
+
outputDir: input.artifactDir,
|
|
195
|
+
testId: artifactName,
|
|
196
|
+
name: input.identity.name,
|
|
197
|
+
file: input.identity.file,
|
|
198
|
+
kind: "summary",
|
|
199
|
+
format: "md"
|
|
200
|
+
});
|
|
201
|
+
if (!manifestPathResult.ok || manifestPathResult.absolutePath === void 0 || manifestPathResult.relativePath === void 0) {
|
|
202
|
+
throw new Error(formatPathDiagnostics(manifestPathResult.diagnostics));
|
|
203
|
+
}
|
|
204
|
+
if (!summaryPathResult.ok || summaryPathResult.absolutePath === void 0 || summaryPathResult.relativePath === void 0) {
|
|
205
|
+
throw new Error(formatPathDiagnostics(summaryPathResult.diagnostics));
|
|
206
|
+
}
|
|
207
|
+
const directory = path.dirname(manifestPathResult.absolutePath);
|
|
208
|
+
await mkdir(directory, { recursive: true });
|
|
209
|
+
const traceFile = input.association.tracePath === void 0 ? void 0 : path.basename(input.association.tracePath);
|
|
210
|
+
const manifestArtifact = {
|
|
211
|
+
kind: "report",
|
|
212
|
+
path: manifestPathResult.relativePath,
|
|
213
|
+
format: "json",
|
|
214
|
+
redactionProfile: input.redactionProfile
|
|
215
|
+
};
|
|
216
|
+
const summaryArtifact = {
|
|
217
|
+
kind: "summary",
|
|
218
|
+
path: summaryPathResult.relativePath,
|
|
219
|
+
format: "md",
|
|
220
|
+
redactionProfile: input.redactionProfile
|
|
221
|
+
};
|
|
222
|
+
const manifest = createTraceArtifactManifest({
|
|
223
|
+
framework: "vitest",
|
|
224
|
+
generatedAt: input.generatedAt,
|
|
225
|
+
results: [
|
|
226
|
+
{
|
|
227
|
+
testId: input.identity.id,
|
|
228
|
+
name: input.identity.name,
|
|
229
|
+
...input.identity.file === void 0 ? {} : { file: input.identity.file },
|
|
230
|
+
status: input.status,
|
|
231
|
+
tracePath: safeOptional(traceFile),
|
|
232
|
+
artifacts: [manifestArtifact, summaryArtifact],
|
|
233
|
+
diagnostics: []
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
artifacts: [manifestArtifact, summaryArtifact],
|
|
237
|
+
diagnostics: []
|
|
238
|
+
});
|
|
239
|
+
const trace = {
|
|
240
|
+
runId: safeOptional(input.association.runId),
|
|
241
|
+
file: safeOptional(traceFile)
|
|
242
|
+
};
|
|
243
|
+
await writeFile(
|
|
244
|
+
manifestPathResult.absolutePath,
|
|
245
|
+
`${JSON.stringify({ package: PACKAGE_NAME, manifest, trace }, null, 2)}
|
|
246
|
+
`
|
|
247
|
+
);
|
|
248
|
+
const summaryPath = summaryPathResult.absolutePath;
|
|
249
|
+
await writeFile(summaryPath, renderSummary(manifest, trace), "utf-8");
|
|
250
|
+
return {
|
|
251
|
+
directory,
|
|
252
|
+
manifest,
|
|
253
|
+
manifestPath: manifestPathResult.absolutePath,
|
|
254
|
+
status: input.status,
|
|
255
|
+
summaryPath,
|
|
256
|
+
testId: input.identity.id
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function renderSummary(manifest, trace) {
|
|
260
|
+
const result = manifest.results[0];
|
|
261
|
+
return [
|
|
262
|
+
"# AgentInspect Vitest Artifact",
|
|
263
|
+
"",
|
|
264
|
+
`- Test: ${result?.name ?? "unknown"}`,
|
|
265
|
+
`- Test id: ${result?.testId ?? "unknown"}`,
|
|
266
|
+
`- Status: ${result?.status ?? "unknown"}`,
|
|
267
|
+
`- File: ${result?.file ?? "unknown"}`,
|
|
268
|
+
`- Trace run: ${trace.runId ?? "unknown"}`,
|
|
269
|
+
`- Trace file: ${result?.tracePath ?? "unknown"}`,
|
|
270
|
+
`- Manifest schema: ${manifest.schemaVersion}`,
|
|
271
|
+
"",
|
|
272
|
+
"Trace contents are intentionally not embedded in this artifact.",
|
|
273
|
+
""
|
|
274
|
+
].join("\n");
|
|
275
|
+
}
|
|
276
|
+
function formatPathDiagnostics(diagnostics) {
|
|
277
|
+
if (diagnostics.length === 0) return "Unsafe AgentInspect Vitest artifact path.";
|
|
278
|
+
return diagnostics.map((diagnostic) => diagnostic.message).join("; ");
|
|
279
|
+
}
|
|
280
|
+
function* collectTasks(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
281
|
+
if (Array.isArray(input)) {
|
|
282
|
+
for (const item of input) yield* collectTasks(item, seen);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!isObject(input) || seen.has(input)) return;
|
|
286
|
+
seen.add(input);
|
|
287
|
+
yield input;
|
|
288
|
+
const task = input;
|
|
289
|
+
for (const childList of [task.tasks, task.children]) {
|
|
290
|
+
if (Array.isArray(childList)) yield* collectTasks(childList, seen);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function safeOptional(value) {
|
|
294
|
+
return value === void 0 ? void 0 : boundText(redactText(value));
|
|
295
|
+
}
|
|
296
|
+
function safeSegment(value) {
|
|
297
|
+
const sanitized = redactText(value).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
298
|
+
return sanitized.length > 0 ? sanitized : "trace";
|
|
299
|
+
}
|
|
300
|
+
function boundText(value) {
|
|
301
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
302
|
+
if (compact.length <= MAX_TEXT) return compact;
|
|
303
|
+
return `${compact.slice(0, MAX_TEXT - 12)}...[truncated]`;
|
|
304
|
+
}
|
|
305
|
+
function redactText(value) {
|
|
306
|
+
return value.replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\b(?:api[_-]?key|authorization|token|secret|password)\s*[:=]\s*[^,\s]+/gi, "$1=[REDACTED]");
|
|
307
|
+
}
|
|
308
|
+
function readOptionalString(value, key) {
|
|
309
|
+
if (!isObject(value)) return void 0;
|
|
310
|
+
const property = readProp(value, key);
|
|
311
|
+
return typeof property === "string" && property.length > 0 ? property : void 0;
|
|
312
|
+
}
|
|
313
|
+
function readProp(value, key) {
|
|
314
|
+
return value[key];
|
|
315
|
+
}
|
|
316
|
+
function isObject(value) {
|
|
317
|
+
return typeof value === "object" && value !== null;
|
|
318
|
+
}
|
|
319
|
+
function errorMessage(error) {
|
|
320
|
+
return error instanceof Error ? error.message : String(error);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export { agentInspectVitestReporter, createAgentInspectVitestReporter };
|
|
324
|
+
//# sourceMappingURL=index.mjs.map
|
|
325
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n createReporterArtifactPath,\n createTraceArtifactManifest,\n type TraceArtifact,\n type TraceArtifactManifest,\n type TraceArtifactRedactionProfile,\n type TraceReporterDiagnostic,\n} from \"agent-inspect/reporters\";\n\nexport type AgentInspectVitestStatus = \"failed\" | \"passed\" | \"skipped\";\n\n/**\n * Experimental trace association read by `@agent-inspect/vitest`.\n *\n * Attach this object to Vitest task metadata, or return it from\n * `resolveTrace`. Associations are explicit only; the reporter never guesses\n * by timestamps.\n */\nexport interface AgentInspectVitestTraceAssociation {\n /** AgentInspect run/span identifier to show in safe artifacts. */\n readonly runId?: string;\n /** Local trace file path. Only the basename is written to artifacts. */\n readonly tracePath?: string;\n /** Optional stable label for the artifact directory. */\n readonly artifactLabel?: string;\n}\n\n/**\n * Experimental diagnostic emitted when reporter work cannot be completed.\n *\n * Diagnostics are reported instead of throwing so Vitest's original test\n * result remains authoritative.\n */\nexport interface AgentInspectVitestDiagnostic {\n readonly code:\n | \"artifact-write-failed\"\n | \"github-summary-write-failed\"\n | \"on-diagnostic-failed\"\n | \"unsafe-artifact-path\";\n readonly message: string;\n readonly testId?: string;\n}\n\n/**\n * Experimental options for the AgentInspect Vitest reporter.\n *\n * The reporter is local-only: it performs no network I/O and writes bounded,\n * structural artifacts without reading trace contents.\n */\nexport interface AgentInspectVitestReporterOptions {\n /** Directory for safe Vitest artifacts. Defaults to `.agent-inspect/vitest-artifacts`. */\n readonly artifactDir?: string;\n /** Optional GitHub step-summary path. Only bounded structural lines are appended. */\n readonly githubSummary?: string;\n /**\n * Keep artifacts for passing tests. `false`/undefined keeps none; `true`\n * keeps up to `maxSuccessfulTraces`; a number keeps up to that many.\n */\n readonly retainSuccessful?: boolean | number;\n /** Upper bound for retained passing-test artifacts. Defaults to 20. */\n readonly maxSuccessfulTraces?: number;\n /** Redaction profile recorded on generated artifacts. Defaults to `share`. */\n readonly redactionProfile?: TraceArtifactRedactionProfile;\n /** Deterministic manifest timestamp. Defaults to the Unix epoch. */\n readonly generatedAt?: string;\n /** Resolve explicit test-to-trace associations from a Vitest task/result. */\n readonly resolveTrace?: (\n test: unknown,\n ) => AgentInspectVitestTraceAssociation | undefined;\n /** Observe non-fatal reporter diagnostics. */\n readonly onDiagnostic?: (diagnostic: AgentInspectVitestDiagnostic) => void;\n}\n\nexport interface AgentInspectVitestArtifact {\n readonly directory: string;\n readonly manifestPath: string;\n readonly summaryPath: string;\n readonly status: AgentInspectVitestStatus;\n readonly testId: string;\n readonly manifest: TraceArtifactManifest;\n}\n\n/**\n * Experimental Vitest reporter facade.\n *\n * The shape is intentionally structural so the package stays tolerant of\n * Vitest reporter API drift while still exposing the hooks Vitest calls.\n */\nexport interface AgentInspectVitestReporter {\n onTestCaseResult(test: unknown): Promise<void>;\n onTaskUpdate(tasks: unknown): Promise<void>;\n onFinished(files?: unknown): Promise<void>;\n getDiagnostics(): readonly AgentInspectVitestDiagnostic[];\n getArtifacts(): readonly AgentInspectVitestArtifact[];\n}\n\ntype TestIdentity = {\n readonly id: string;\n readonly name: string;\n readonly file?: string;\n};\n\ntype TestLike = {\n readonly id?: unknown;\n readonly name?: unknown;\n readonly suite?: unknown;\n readonly file?: unknown;\n readonly filepath?: unknown;\n readonly result?: unknown;\n readonly meta?: unknown;\n readonly context?: unknown;\n readonly tasks?: unknown;\n readonly children?: unknown;\n};\n\ntype ResultLike = {\n readonly state?: unknown;\n readonly status?: unknown;\n readonly meta?: unknown;\n};\n\nconst PACKAGE_NAME = \"@agent-inspect/vitest\";\nconst DEFAULT_ARTIFACT_DIR = \".agent-inspect/vitest-artifacts\";\nconst DEFAULT_SUCCESS_LIMIT = 20;\nconst MAX_SUCCESS_LIMIT = 100;\nconst MAX_TEXT = 180;\nconst DEFAULT_GENERATED_AT = \"1970-01-01T00:00:00.000Z\";\n\n/**\n * Create the experimental AgentInspect Vitest reporter.\n */\nexport function createAgentInspectVitestReporter(\n options: AgentInspectVitestReporterOptions = {},\n): AgentInspectVitestReporter {\n const diagnostics: AgentInspectVitestDiagnostic[] = [];\n const artifacts: AgentInspectVitestArtifact[] = [];\n const seenObjects = new WeakSet<object>();\n const seenKeys = new Set<string>();\n const artifactNames = new Map<string, number>();\n const successLimit = normalizeSuccessLimit(options);\n let retainedSuccesses = 0;\n\n const reporter: AgentInspectVitestReporter = {\n async onTestCaseResult(test: unknown): Promise<void> {\n await handleTest(test);\n },\n async onTaskUpdate(tasks: unknown): Promise<void> {\n for (const test of collectTasks(tasks)) {\n await handleTest(test);\n }\n },\n async onFinished(files?: unknown): Promise<void> {\n for (const test of collectTasks(files)) {\n await handleTest(test);\n }\n await appendGithubSummary();\n },\n getDiagnostics(): readonly AgentInspectVitestDiagnostic[] {\n return diagnostics.slice();\n },\n getArtifacts(): readonly AgentInspectVitestArtifact[] {\n return artifacts.slice();\n },\n };\n\n async function handleTest(test: unknown): Promise<void> {\n if (!isObject(test)) return;\n if (seenObjects.has(test)) return;\n\n const status = readStatus(test);\n if (status === undefined || status === \"skipped\") return;\n\n const identity = readIdentity(test);\n const seenKey = `${identity.id}:${status}`;\n if (seenKeys.has(seenKey)) return;\n\n const association = resolveAssociation(test, options);\n if (association === undefined) return;\n\n if (status === \"passed\") {\n if (retainedSuccesses >= successLimit) return;\n retainedSuccesses += 1;\n }\n\n seenObjects.add(test);\n seenKeys.add(seenKey);\n\n try {\n const artifact = await writeSafeArtifact({\n artifactDir: options.artifactDir ?? DEFAULT_ARTIFACT_DIR,\n association,\n generatedAt: options.generatedAt ?? DEFAULT_GENERATED_AT,\n identity,\n reserveArtifactName,\n redactionProfile: options.redactionProfile ?? \"share\",\n status,\n });\n artifacts.push(artifact);\n } catch (error) {\n reportDiagnostic({\n code: \"artifact-write-failed\",\n message: `Could not write AgentInspect Vitest artifact: ${errorMessage(error)}`,\n testId: identity.id,\n });\n }\n }\n\n function reserveArtifactName(seed: string): string {\n const base = safeSegment(seed);\n const next = (artifactNames.get(base) ?? 0) + 1;\n artifactNames.set(base, next);\n return next === 1 ? base : `${base}-${next}`;\n }\n\n function reportDiagnostic(diagnostic: AgentInspectVitestDiagnostic): void {\n diagnostics.push(diagnostic);\n if (options.onDiagnostic === undefined) return;\n try {\n options.onDiagnostic(diagnostic);\n } catch (error) {\n diagnostics.push({\n code: \"on-diagnostic-failed\",\n message: `AgentInspect Vitest diagnostic callback failed: ${errorMessage(error)}`,\n testId: diagnostic.testId,\n });\n }\n }\n\n async function appendGithubSummary(): Promise<void> {\n if (options.githubSummary === undefined || artifacts.length === 0) return;\n const failed = artifacts.filter((artifact) => artifact.status === \"failed\").length;\n const passed = artifacts.filter((artifact) => artifact.status === \"passed\").length;\n const lines = [\n \"\",\n \"## AgentInspect Vitest artifacts\",\n \"\",\n `- Failed test artifacts: ${failed}`,\n `- Passing test artifacts retained: ${passed}`,\n `- Artifact directory: ${boundText(path.basename(options.artifactDir ?? DEFAULT_ARTIFACT_DIR))}`,\n \"\",\n ];\n try {\n await writeFile(options.githubSummary, `${lines.join(\"\\n\")}\\n`, { flag: \"a\" });\n } catch (error) {\n reportDiagnostic({\n code: \"github-summary-write-failed\",\n message: `Could not append AgentInspect Vitest summary: ${errorMessage(error)}`,\n });\n }\n }\n\n return reporter;\n}\n\nexport const agentInspectVitestReporter = createAgentInspectVitestReporter;\n\nfunction normalizeSuccessLimit(options: AgentInspectVitestReporterOptions): number {\n const cap = clampCount(options.maxSuccessfulTraces, DEFAULT_SUCCESS_LIMIT);\n if (options.retainSuccessful === true) return cap;\n if (typeof options.retainSuccessful === \"number\") {\n return Math.min(clampCount(options.retainSuccessful, 0), cap);\n }\n return 0;\n}\n\nfunction clampCount(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value)) return fallback;\n return Math.max(0, Math.min(MAX_SUCCESS_LIMIT, Math.floor(value)));\n}\n\nfunction resolveAssociation(\n test: object,\n options: AgentInspectVitestReporterOptions,\n): AgentInspectVitestTraceAssociation | undefined {\n const resolved = options.resolveTrace?.(test);\n if (isAssociation(resolved)) return resolved;\n\n const task = test as TestLike;\n const result = isObject(task.result) ? (task.result as ResultLike) : undefined;\n return (\n readAssociationFromMeta(task.meta) ??\n readAssociationFromContext(task.context) ??\n readAssociationFromMeta(result?.meta)\n );\n}\n\nfunction readAssociationFromContext(\n context: unknown,\n): AgentInspectVitestTraceAssociation | undefined {\n if (!isObject(context)) return undefined;\n const maybeMeta = readProp(context, \"meta\");\n return readAssociationFromMeta(maybeMeta);\n}\n\nfunction readAssociationFromMeta(\n meta: unknown,\n): AgentInspectVitestTraceAssociation | undefined {\n if (!isObject(meta)) return undefined;\n const direct = readProp(meta, \"agentInspect\");\n const hyphenated = readProp(meta, \"agent-inspect\");\n const trace = readProp(meta, \"trace\");\n for (const candidate of [direct, hyphenated, trace]) {\n if (isAssociation(candidate)) return candidate;\n }\n return undefined;\n}\n\nfunction isAssociation(\n value: unknown,\n): value is AgentInspectVitestTraceAssociation {\n if (!isObject(value)) return false;\n return (\n readOptionalString(value, \"runId\") !== undefined ||\n readOptionalString(value, \"tracePath\") !== undefined ||\n readOptionalString(value, \"artifactLabel\") !== undefined\n );\n}\n\nfunction readStatus(test: object): AgentInspectVitestStatus | undefined {\n const task = test as TestLike;\n const result = isObject(task.result) ? (task.result as ResultLike) : undefined;\n const raw =\n readOptionalString(result, \"state\") ??\n readOptionalString(result, \"status\") ??\n readOptionalString(test, \"state\") ??\n readOptionalString(test, \"status\");\n if (raw === undefined) return undefined;\n\n const normalized = raw.toLowerCase();\n if (normalized === \"fail\" || normalized === \"failed\") return \"failed\";\n if (normalized === \"pass\" || normalized === \"passed\") return \"passed\";\n if (normalized === \"skip\" || normalized === \"skipped\") return \"skipped\";\n return undefined;\n}\n\nfunction readIdentity(test: object): TestIdentity {\n const task = test as TestLike;\n const id = readOptionalString(test, \"id\") ?? readOptionalString(test, \"taskId\");\n const name =\n readOptionalString(test, \"fullName\") ??\n readOptionalString(test, \"name\") ??\n readOptionalString(task.suite, \"name\") ??\n id ??\n \"unknown-test\";\n const file =\n readOptionalString(test, \"file\") ??\n readOptionalString(test, \"filepath\") ??\n readOptionalString(task.suite, \"file\");\n return {\n file: file === undefined ? undefined : path.basename(file),\n id: boundText(redactText(id ?? name)),\n name: boundText(redactText(name)),\n };\n}\n\nasync function writeSafeArtifact(input: {\n readonly artifactDir: string;\n readonly association: AgentInspectVitestTraceAssociation;\n readonly generatedAt: string;\n readonly identity: TestIdentity;\n readonly redactionProfile: TraceArtifactRedactionProfile;\n readonly reserveArtifactName: (seed: string) => string;\n readonly status: AgentInspectVitestStatus;\n}): Promise<AgentInspectVitestArtifact> {\n const artifactSeed =\n input.association.artifactLabel ??\n input.identity.id ??\n input.identity.name ??\n input.status;\n const artifactName = input.reserveArtifactName(artifactSeed);\n const manifestPathResult = createReporterArtifactPath({\n outputDir: input.artifactDir,\n testId: artifactName,\n name: input.identity.name,\n file: input.identity.file,\n kind: \"report\",\n format: \"json\",\n });\n const summaryPathResult = createReporterArtifactPath({\n outputDir: input.artifactDir,\n testId: artifactName,\n name: input.identity.name,\n file: input.identity.file,\n kind: \"summary\",\n format: \"md\",\n });\n\n if (\n !manifestPathResult.ok ||\n manifestPathResult.absolutePath === undefined ||\n manifestPathResult.relativePath === undefined\n ) {\n throw new Error(formatPathDiagnostics(manifestPathResult.diagnostics));\n }\n if (\n !summaryPathResult.ok ||\n summaryPathResult.absolutePath === undefined ||\n summaryPathResult.relativePath === undefined\n ) {\n throw new Error(formatPathDiagnostics(summaryPathResult.diagnostics));\n }\n\n const directory = path.dirname(manifestPathResult.absolutePath);\n await mkdir(directory, { recursive: true });\n\n const traceFile =\n input.association.tracePath === undefined\n ? undefined\n : path.basename(input.association.tracePath);\n const manifestArtifact: TraceArtifact = {\n kind: \"report\",\n path: manifestPathResult.relativePath,\n format: \"json\",\n redactionProfile: input.redactionProfile,\n };\n const summaryArtifact: TraceArtifact = {\n kind: \"summary\",\n path: summaryPathResult.relativePath,\n format: \"md\",\n redactionProfile: input.redactionProfile,\n };\n const manifest = createTraceArtifactManifest({\n framework: \"vitest\",\n generatedAt: input.generatedAt,\n results: [\n {\n testId: input.identity.id,\n name: input.identity.name,\n ...(input.identity.file === undefined ? {} : { file: input.identity.file }),\n status: input.status,\n tracePath: safeOptional(traceFile),\n artifacts: [manifestArtifact, summaryArtifact],\n diagnostics: [],\n },\n ],\n artifacts: [manifestArtifact, summaryArtifact],\n diagnostics: [],\n });\n const trace = {\n runId: safeOptional(input.association.runId),\n file: safeOptional(traceFile),\n };\n\n await writeFile(\n manifestPathResult.absolutePath,\n `${JSON.stringify({ package: PACKAGE_NAME, manifest, trace }, null, 2)}\\n`,\n );\n const summaryPath = summaryPathResult.absolutePath;\n await writeFile(summaryPath, renderSummary(manifest, trace), \"utf-8\");\n\n return {\n directory,\n manifest,\n manifestPath: manifestPathResult.absolutePath,\n status: input.status,\n summaryPath,\n testId: input.identity.id,\n };\n}\n\nfunction renderSummary(\n manifest: TraceArtifactManifest,\n trace: { readonly runId?: string; readonly file?: string },\n): string {\n const result = manifest.results[0];\n return [\n \"# AgentInspect Vitest Artifact\",\n \"\",\n `- Test: ${result?.name ?? \"unknown\"}`,\n `- Test id: ${result?.testId ?? \"unknown\"}`,\n `- Status: ${result?.status ?? \"unknown\"}`,\n `- File: ${result?.file ?? \"unknown\"}`,\n `- Trace run: ${trace.runId ?? \"unknown\"}`,\n `- Trace file: ${result?.tracePath ?? \"unknown\"}`,\n `- Manifest schema: ${manifest.schemaVersion}`,\n \"\",\n \"Trace contents are intentionally not embedded in this artifact.\",\n \"\",\n ].join(\"\\n\");\n}\n\nfunction formatPathDiagnostics(\n diagnostics: readonly TraceReporterDiagnostic[],\n): string {\n if (diagnostics.length === 0) return \"Unsafe AgentInspect Vitest artifact path.\";\n return diagnostics.map((diagnostic) => diagnostic.message).join(\"; \");\n}\n\nfunction* collectTasks(input: unknown, seen = new WeakSet<object>()): Iterable<unknown> {\n if (Array.isArray(input)) {\n for (const item of input) yield* collectTasks(item, seen);\n return;\n }\n if (!isObject(input) || seen.has(input)) return;\n\n seen.add(input);\n yield input;\n\n const task = input as TestLike;\n for (const childList of [task.tasks, task.children]) {\n if (Array.isArray(childList)) yield* collectTasks(childList, seen);\n }\n}\n\nfunction safeOptional(value: string | undefined): string | undefined {\n return value === undefined ? undefined : boundText(redactText(value));\n}\n\nfunction safeSegment(value: string): string {\n const sanitized = redactText(value)\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 80);\n return sanitized.length > 0 ? sanitized : \"trace\";\n}\n\nfunction boundText(value: string): string {\n const compact = value.replace(/\\s+/g, \" \").trim();\n if (compact.length <= MAX_TEXT) return compact;\n return `${compact.slice(0, MAX_TEXT - 12)}...[truncated]`;\n}\n\nfunction redactText(value: string): string {\n return value\n .replace(/\\bsk-[A-Za-z0-9_-]{8,}\\b/g, \"[REDACTED]\")\n .replace(/\\b(?:api[_-]?key|authorization|token|secret|password)\\s*[:=]\\s*[^,\\s]+/gi, \"$1=[REDACTED]\");\n}\n\nfunction readOptionalString(value: unknown, key: string): string | undefined {\n if (!isObject(value)) return undefined;\n const property = readProp(value, key);\n return typeof property === \"string\" && property.length > 0 ? property : undefined;\n}\n\nfunction readProp(value: object, key: string): unknown {\n return (value as Record<string, unknown>)[key];\n}\n\nfunction isObject(value: unknown): value is object {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-inspect/vitest",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Optional Vitest reporter for AgentInspect local trace artifacts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rajudandigam/agent-inspect.git",
|
|
10
|
+
"directory": "packages/vitest"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/rajudandigam/agent-inspect/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/rajudandigam/agent-inspect#readme",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./dist/index.d.cts",
|
|
28
|
+
"default": "./dist/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"vitest": "^2.1.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"agent-inspect": "2.1.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"vitest": "^2.1.8"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "pnpm --workspace-root exec tsup --config tsup.vitest.config.ts",
|
|
49
|
+
"test": "vitest run -c ../../vitest.config.ts"
|
|
50
|
+
}
|
|
51
|
+
}
|