@agent-inspect/harness 3.0.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 +543 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +101 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.mjs +533 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +45 -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,543 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var promises = require('fs/promises');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var process = require('process');
|
|
6
|
+
var agentInspect = require('agent-inspect');
|
|
7
|
+
var advanced = require('agent-inspect/advanced');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
12
|
+
var process__default = /*#__PURE__*/_interopDefault(process);
|
|
13
|
+
|
|
14
|
+
// packages/harness/src/index.ts
|
|
15
|
+
var HarnessError = class extends Error {
|
|
16
|
+
code;
|
|
17
|
+
targetName;
|
|
18
|
+
constructor(code, message, options) {
|
|
19
|
+
super(message, { cause: options?.cause });
|
|
20
|
+
this.name = "HarnessError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.targetName = options?.targetName;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var HarnessTargetNotFoundError = class extends HarnessError {
|
|
26
|
+
constructor(targetName) {
|
|
27
|
+
super("target_not_found", `Harness target "${targetName}" was not found.`, {
|
|
28
|
+
targetName
|
|
29
|
+
});
|
|
30
|
+
this.name = "HarnessTargetNotFoundError";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function defineTarget(definition) {
|
|
34
|
+
return definition;
|
|
35
|
+
}
|
|
36
|
+
function createFixtureRunner(options) {
|
|
37
|
+
return new DefaultFixtureRunner(options);
|
|
38
|
+
}
|
|
39
|
+
var DefaultFixtureRunner = class {
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
this.runnerName = normalizeRunnerName(options.name);
|
|
43
|
+
}
|
|
44
|
+
options;
|
|
45
|
+
diagnostics = [];
|
|
46
|
+
runnerName;
|
|
47
|
+
listTargets() {
|
|
48
|
+
return Object.entries(this.options.targets).map(([name, definition]) => {
|
|
49
|
+
const info = { name };
|
|
50
|
+
if (definition.description !== void 0) info.description = definition.description;
|
|
51
|
+
if (definition.metadata !== void 0) info.metadata = definition.metadata;
|
|
52
|
+
return info;
|
|
53
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
54
|
+
}
|
|
55
|
+
getDiagnostics() {
|
|
56
|
+
return [...this.diagnostics];
|
|
57
|
+
}
|
|
58
|
+
async runTarget(targetName, input, options) {
|
|
59
|
+
return await this.runTargetInternal(targetName, input, options);
|
|
60
|
+
}
|
|
61
|
+
async runFromArgv(argv = process__default.default.argv.slice(2), io = {}) {
|
|
62
|
+
const parsed = this.parseArgv(argv);
|
|
63
|
+
const stdout = io.stdout ?? ((chunk) => process__default.default.stdout.write(chunk));
|
|
64
|
+
const stderr = io.stderr ?? ((chunk) => process__default.default.stderr.write(chunk));
|
|
65
|
+
if (!parsed.ok) {
|
|
66
|
+
return this.writeArgvFailure(parsed.message, "cli_usage_error", stdout, stderr);
|
|
67
|
+
}
|
|
68
|
+
if (parsed.list) {
|
|
69
|
+
const result = {
|
|
70
|
+
ok: true,
|
|
71
|
+
exitCode: 0,
|
|
72
|
+
listed: true,
|
|
73
|
+
output: { targets: this.listTargets() },
|
|
74
|
+
diagnostics: this.getDiagnostics()
|
|
75
|
+
};
|
|
76
|
+
writeJsonLine(stdout, { targets: this.listTargets() });
|
|
77
|
+
stderr(`agent-inspect harness: listed ${this.listTargets().length} target(s)
|
|
78
|
+
`);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
if (parsed.targetName === void 0) {
|
|
82
|
+
return this.writeArgvFailure(
|
|
83
|
+
"Missing target name. Pass --list or a target name.",
|
|
84
|
+
"cli_usage_error",
|
|
85
|
+
stdout,
|
|
86
|
+
stderr
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (parsed.fixturePath !== void 0 && parsed.readStdin) {
|
|
90
|
+
return this.writeArgvFailure(
|
|
91
|
+
"Pass either --fixture or --stdin, not both.",
|
|
92
|
+
"cli_usage_error",
|
|
93
|
+
stdout,
|
|
94
|
+
stderr,
|
|
95
|
+
parsed.targetName
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const cwd = io.cwd ?? process__default.default.cwd();
|
|
99
|
+
const inputResult = await this.readInput(parsed, io, cwd);
|
|
100
|
+
if (!inputResult.ok) {
|
|
101
|
+
return this.writeArgvFailure(
|
|
102
|
+
inputResult.message,
|
|
103
|
+
inputResult.code,
|
|
104
|
+
stdout,
|
|
105
|
+
stderr,
|
|
106
|
+
parsed.targetName,
|
|
107
|
+
inputResult.error
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const output = await this.runTargetInternal(parsed.targetName, inputResult.value, {
|
|
112
|
+
trace: parsed.trace
|
|
113
|
+
});
|
|
114
|
+
const expectedResult = await this.compareExpectedOutput(
|
|
115
|
+
parsed,
|
|
116
|
+
output,
|
|
117
|
+
cwd
|
|
118
|
+
);
|
|
119
|
+
if (!expectedResult.ok) {
|
|
120
|
+
return this.writeArgvFailure(
|
|
121
|
+
expectedResult.message,
|
|
122
|
+
expectedResult.code,
|
|
123
|
+
stdout,
|
|
124
|
+
stderr,
|
|
125
|
+
parsed.targetName,
|
|
126
|
+
expectedResult.error,
|
|
127
|
+
output,
|
|
128
|
+
false
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const matchedExpected = expectedResult.compared ? true : void 0;
|
|
132
|
+
const result = {
|
|
133
|
+
ok: true,
|
|
134
|
+
exitCode: 0,
|
|
135
|
+
targetName: parsed.targetName,
|
|
136
|
+
output,
|
|
137
|
+
diagnostics: this.getDiagnostics(),
|
|
138
|
+
...matchedExpected !== void 0 ? { matchedExpected } : {}
|
|
139
|
+
};
|
|
140
|
+
writeJsonLine(stdout, {
|
|
141
|
+
ok: true,
|
|
142
|
+
target: parsed.targetName,
|
|
143
|
+
output,
|
|
144
|
+
...matchedExpected !== void 0 ? { matchedExpected } : {}
|
|
145
|
+
});
|
|
146
|
+
stderr(`agent-inspect harness: ok ${parsed.targetName}
|
|
147
|
+
`);
|
|
148
|
+
return result;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return this.writeArgvFailure(
|
|
151
|
+
`Harness target "${parsed.targetName}" failed.`,
|
|
152
|
+
"invoke_failed",
|
|
153
|
+
stdout,
|
|
154
|
+
stderr,
|
|
155
|
+
parsed.targetName,
|
|
156
|
+
error
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async runTargetInternal(targetName, input, options) {
|
|
161
|
+
const definition = this.options.targets[targetName];
|
|
162
|
+
let app;
|
|
163
|
+
const context = this.createContext(targetName, () => app);
|
|
164
|
+
if (definition === void 0) {
|
|
165
|
+
const error = new HarnessTargetNotFoundError(targetName);
|
|
166
|
+
this.addDiagnostic({
|
|
167
|
+
code: "target_not_found",
|
|
168
|
+
severity: "error",
|
|
169
|
+
message: error.message,
|
|
170
|
+
targetName,
|
|
171
|
+
error: serializeError(error)
|
|
172
|
+
});
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
let primaryError;
|
|
176
|
+
try {
|
|
177
|
+
app = await this.bootstrap(context);
|
|
178
|
+
const target = await this.resolveTarget(definition, app, context, targetName);
|
|
179
|
+
return await this.invokeTarget(definition, target, input, context, targetName, options);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
primaryError = error;
|
|
182
|
+
throw error;
|
|
183
|
+
} finally {
|
|
184
|
+
await this.shutdown(app, context, targetName, primaryError);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
createContext(targetName, getApp) {
|
|
188
|
+
return {
|
|
189
|
+
runnerName: this.runnerName,
|
|
190
|
+
targetName,
|
|
191
|
+
get app() {
|
|
192
|
+
return getApp();
|
|
193
|
+
},
|
|
194
|
+
addDiagnostic: (diagnostic) => this.addDiagnostic(diagnostic),
|
|
195
|
+
getDiagnostics: () => this.getDiagnostics()
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
addDiagnostic(input) {
|
|
199
|
+
this.diagnostics.push({
|
|
200
|
+
...input,
|
|
201
|
+
timestamp: input.timestamp ?? Date.now()
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
parseArgv(argv) {
|
|
205
|
+
const parsed = {
|
|
206
|
+
ok: true,
|
|
207
|
+
list: false,
|
|
208
|
+
readStdin: false,
|
|
209
|
+
trace: {}
|
|
210
|
+
};
|
|
211
|
+
const positional = [];
|
|
212
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
213
|
+
const arg = argv[i];
|
|
214
|
+
switch (arg) {
|
|
215
|
+
case "--list":
|
|
216
|
+
parsed.list = true;
|
|
217
|
+
break;
|
|
218
|
+
case "--fixture": {
|
|
219
|
+
const value = argv[i + 1];
|
|
220
|
+
if (value === void 0) return usageError("--fixture requires a path.");
|
|
221
|
+
parsed.fixturePath = value;
|
|
222
|
+
i += 1;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case "--stdin":
|
|
226
|
+
parsed.readStdin = true;
|
|
227
|
+
break;
|
|
228
|
+
case "--expected":
|
|
229
|
+
case "--expected-output": {
|
|
230
|
+
const value = argv[i + 1];
|
|
231
|
+
if (value === void 0) return usageError(`${arg} requires a path.`);
|
|
232
|
+
parsed.expectedPath = value;
|
|
233
|
+
i += 1;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
case "--trace":
|
|
237
|
+
parsed.trace.mode = "run";
|
|
238
|
+
break;
|
|
239
|
+
case "--no-trace":
|
|
240
|
+
parsed.trace.mode = "off";
|
|
241
|
+
break;
|
|
242
|
+
case "--trace-dir": {
|
|
243
|
+
const value = argv[i + 1];
|
|
244
|
+
if (value === void 0) return usageError("--trace-dir requires a path.");
|
|
245
|
+
parsed.trace.traceDir = value;
|
|
246
|
+
i += 1;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case "--trace-mode": {
|
|
250
|
+
const value = argv[i + 1];
|
|
251
|
+
if (!isHarnessTraceMode(value)) {
|
|
252
|
+
return usageError(
|
|
253
|
+
"--trace-mode must be one of: off, run, run-if-enabled, observe."
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
parsed.trace.mode = value;
|
|
257
|
+
i += 1;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
default:
|
|
261
|
+
if (arg?.startsWith("--")) {
|
|
262
|
+
return usageError(`Unknown harness option: ${arg}`);
|
|
263
|
+
}
|
|
264
|
+
if (arg !== void 0) positional.push(arg);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (positional.length > 1) {
|
|
268
|
+
return usageError("Pass at most one target name.");
|
|
269
|
+
}
|
|
270
|
+
parsed.targetName = positional[0];
|
|
271
|
+
return parsed;
|
|
272
|
+
}
|
|
273
|
+
async readInput(parsed, io, cwd) {
|
|
274
|
+
if (parsed.fixturePath !== void 0) {
|
|
275
|
+
try {
|
|
276
|
+
return {
|
|
277
|
+
ok: true,
|
|
278
|
+
value: parseJsonPayload(
|
|
279
|
+
await promises.readFile(resolvePath(cwd, parsed.fixturePath), "utf8"),
|
|
280
|
+
parsed.fixturePath
|
|
281
|
+
)
|
|
282
|
+
};
|
|
283
|
+
} catch (error) {
|
|
284
|
+
return {
|
|
285
|
+
ok: false,
|
|
286
|
+
code: "fixture_read_failed",
|
|
287
|
+
message: `Failed to read fixture: ${parsed.fixturePath}`,
|
|
288
|
+
error
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (parsed.readStdin) {
|
|
293
|
+
try {
|
|
294
|
+
const raw = io.stdin === void 0 ? await readProcessStdin() : await io.stdin();
|
|
295
|
+
return { ok: true, value: parseJsonPayload(raw, "stdin") };
|
|
296
|
+
} catch (error) {
|
|
297
|
+
return {
|
|
298
|
+
ok: false,
|
|
299
|
+
code: "stdin_read_failed",
|
|
300
|
+
message: "Failed to read JSON from stdin.",
|
|
301
|
+
error
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { ok: true, value: void 0 };
|
|
306
|
+
}
|
|
307
|
+
async compareExpectedOutput(parsed, output, cwd) {
|
|
308
|
+
if (parsed.expectedPath === void 0) {
|
|
309
|
+
return { ok: true, compared: false };
|
|
310
|
+
}
|
|
311
|
+
let expected;
|
|
312
|
+
try {
|
|
313
|
+
expected = parseJsonPayload(
|
|
314
|
+
await promises.readFile(resolvePath(cwd, parsed.expectedPath), "utf8"),
|
|
315
|
+
parsed.expectedPath
|
|
316
|
+
);
|
|
317
|
+
} catch (error) {
|
|
318
|
+
return {
|
|
319
|
+
ok: false,
|
|
320
|
+
compared: true,
|
|
321
|
+
code: "expected_read_failed",
|
|
322
|
+
message: `Failed to read expected output: ${parsed.expectedPath}`,
|
|
323
|
+
error
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
if (stableJson(expected) !== stableJson(output)) {
|
|
327
|
+
return {
|
|
328
|
+
ok: false,
|
|
329
|
+
compared: true,
|
|
330
|
+
code: "expected_output_mismatch",
|
|
331
|
+
message: `Expected output mismatch: ${parsed.expectedPath}`,
|
|
332
|
+
error: new Error("Expected output mismatch.")
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
return { ok: true, compared: true };
|
|
336
|
+
}
|
|
337
|
+
writeArgvFailure(message, code, stdout, stderr, targetName, error, output, matchedExpected) {
|
|
338
|
+
const diagnostic = {
|
|
339
|
+
code,
|
|
340
|
+
severity: "error",
|
|
341
|
+
message,
|
|
342
|
+
...targetName !== void 0 ? { targetName } : {},
|
|
343
|
+
...error !== void 0 ? { error: serializeError(error) } : {}
|
|
344
|
+
};
|
|
345
|
+
this.addDiagnostic(diagnostic);
|
|
346
|
+
const result = {
|
|
347
|
+
ok: false,
|
|
348
|
+
exitCode: 1,
|
|
349
|
+
...targetName !== void 0 ? { targetName } : {},
|
|
350
|
+
...output !== void 0 ? { output } : {},
|
|
351
|
+
...matchedExpected !== void 0 ? { matchedExpected } : {},
|
|
352
|
+
...error !== void 0 ? { error: serializeError(error) } : {},
|
|
353
|
+
diagnostics: this.getDiagnostics()
|
|
354
|
+
};
|
|
355
|
+
writeJsonLine(stdout, {
|
|
356
|
+
ok: false,
|
|
357
|
+
...targetName !== void 0 ? { target: targetName } : {},
|
|
358
|
+
...output !== void 0 ? { output } : {},
|
|
359
|
+
...matchedExpected !== void 0 ? { matchedExpected } : {},
|
|
360
|
+
...error !== void 0 ? { error: serializeError(error) } : {},
|
|
361
|
+
diagnostics: this.getDiagnostics()
|
|
362
|
+
});
|
|
363
|
+
stderr(`agent-inspect harness: ${message}
|
|
364
|
+
`);
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
async bootstrap(context) {
|
|
368
|
+
try {
|
|
369
|
+
if (this.options.bootstrap === void 0) {
|
|
370
|
+
return void 0;
|
|
371
|
+
}
|
|
372
|
+
return await this.options.bootstrap(context);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
this.addDiagnostic({
|
|
375
|
+
code: "bootstrap_failed",
|
|
376
|
+
severity: "error",
|
|
377
|
+
message: "Harness bootstrap failed.",
|
|
378
|
+
targetName: context.targetName,
|
|
379
|
+
error: serializeError(error)
|
|
380
|
+
});
|
|
381
|
+
throw new HarnessError("bootstrap_failed", "Harness bootstrap failed.", {
|
|
382
|
+
targetName: context.targetName,
|
|
383
|
+
cause: error
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async resolveTarget(definition, app, context, targetName) {
|
|
388
|
+
try {
|
|
389
|
+
return await definition.resolve(app, context);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
this.addDiagnostic({
|
|
392
|
+
code: "resolve_failed",
|
|
393
|
+
severity: "error",
|
|
394
|
+
message: `Harness target "${targetName}" failed to resolve.`,
|
|
395
|
+
targetName,
|
|
396
|
+
error: serializeError(error)
|
|
397
|
+
});
|
|
398
|
+
throw new HarnessError(
|
|
399
|
+
"resolve_failed",
|
|
400
|
+
`Harness target "${targetName}" failed to resolve.`,
|
|
401
|
+
{ targetName, cause: error }
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async invokeTarget(definition, target, input, context, targetName, options) {
|
|
406
|
+
const trace = mergeTraceOptions(this.options.trace, options?.trace);
|
|
407
|
+
const runName = resolveRunName(this.runnerName, targetName, trace.runName);
|
|
408
|
+
const traceOptions = toInspectRunOptions(trace);
|
|
409
|
+
const invoke = () => definition.invoke(target, input, context);
|
|
410
|
+
try {
|
|
411
|
+
switch (trace.mode ?? "run-if-enabled") {
|
|
412
|
+
case "off":
|
|
413
|
+
return await invoke();
|
|
414
|
+
case "run":
|
|
415
|
+
return await agentInspect.inspectRun(runName, invoke, {
|
|
416
|
+
...traceOptions,
|
|
417
|
+
enabled: traceOptions.enabled ?? true
|
|
418
|
+
});
|
|
419
|
+
case "observe": {
|
|
420
|
+
const enabled = traceOptions.enabled ?? advanced.isAgentInspectEnabled(process__default.default.env.AGENT_INSPECT);
|
|
421
|
+
const observedTarget = agentInspect.observe(target, {
|
|
422
|
+
...traceOptions,
|
|
423
|
+
enabled
|
|
424
|
+
});
|
|
425
|
+
return await definition.invoke(observedTarget, input, context);
|
|
426
|
+
}
|
|
427
|
+
case "run-if-enabled":
|
|
428
|
+
return await agentInspect.maybeInspectRun(runName, invoke, traceOptions);
|
|
429
|
+
}
|
|
430
|
+
} catch (error) {
|
|
431
|
+
this.addDiagnostic({
|
|
432
|
+
code: "invoke_failed",
|
|
433
|
+
severity: "error",
|
|
434
|
+
message: `Harness target "${targetName}" failed during invocation.`,
|
|
435
|
+
targetName,
|
|
436
|
+
error: serializeError(error)
|
|
437
|
+
});
|
|
438
|
+
throw error;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async shutdown(app, context, targetName, primaryError) {
|
|
442
|
+
if (this.options.shutdown === void 0) return;
|
|
443
|
+
try {
|
|
444
|
+
await this.options.shutdown(app, context);
|
|
445
|
+
} catch (error) {
|
|
446
|
+
this.addDiagnostic({
|
|
447
|
+
code: "shutdown_failed",
|
|
448
|
+
severity: "error",
|
|
449
|
+
message: "Harness shutdown failed.",
|
|
450
|
+
targetName,
|
|
451
|
+
error: serializeError(error)
|
|
452
|
+
});
|
|
453
|
+
if (primaryError === void 0) {
|
|
454
|
+
throw new HarnessError("shutdown_failed", "Harness shutdown failed.", {
|
|
455
|
+
targetName,
|
|
456
|
+
cause: error
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
function normalizeRunnerName(name) {
|
|
463
|
+
const trimmed = name?.trim();
|
|
464
|
+
return trimmed === void 0 || trimmed === "" ? "agent-inspect-harness" : trimmed;
|
|
465
|
+
}
|
|
466
|
+
function resolveRunName(runnerName, targetName, configured) {
|
|
467
|
+
if (typeof configured === "function") return configured(targetName);
|
|
468
|
+
if (typeof configured === "string" && configured.trim() !== "") return configured.trim();
|
|
469
|
+
return `${runnerName}:${targetName}`;
|
|
470
|
+
}
|
|
471
|
+
function mergeTraceOptions(base, override) {
|
|
472
|
+
return { ...base ?? {}, ...override ?? {} };
|
|
473
|
+
}
|
|
474
|
+
function toInspectRunOptions(options) {
|
|
475
|
+
const { mode: _mode, runName: _runName, ...inspectOptions } = options;
|
|
476
|
+
return inspectOptions;
|
|
477
|
+
}
|
|
478
|
+
function serializeError(error) {
|
|
479
|
+
if (error instanceof Error) {
|
|
480
|
+
return {
|
|
481
|
+
name: error.name,
|
|
482
|
+
message: error.message,
|
|
483
|
+
stack: error.stack
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
message: String(error)
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function usageError(message) {
|
|
491
|
+
return { ok: false, message };
|
|
492
|
+
}
|
|
493
|
+
function isHarnessTraceMode(value) {
|
|
494
|
+
return value === "off" || value === "run" || value === "run-if-enabled" || value === "observe";
|
|
495
|
+
}
|
|
496
|
+
function resolvePath(cwd, inputPath) {
|
|
497
|
+
return path__default.default.isAbsolute(inputPath) ? inputPath : path__default.default.resolve(cwd, inputPath);
|
|
498
|
+
}
|
|
499
|
+
function parseJsonPayload(raw, source) {
|
|
500
|
+
const trimmed = raw.trim();
|
|
501
|
+
if (trimmed === "") {
|
|
502
|
+
throw new Error(`Empty JSON payload from ${source}.`);
|
|
503
|
+
}
|
|
504
|
+
return JSON.parse(trimmed);
|
|
505
|
+
}
|
|
506
|
+
async function readProcessStdin() {
|
|
507
|
+
process__default.default.stdin.setEncoding("utf8");
|
|
508
|
+
let raw = "";
|
|
509
|
+
for await (const chunk of process__default.default.stdin) {
|
|
510
|
+
raw += chunk;
|
|
511
|
+
}
|
|
512
|
+
return raw;
|
|
513
|
+
}
|
|
514
|
+
function writeJsonLine(writer, value) {
|
|
515
|
+
writer(`${stableJson(value)}
|
|
516
|
+
`);
|
|
517
|
+
}
|
|
518
|
+
function stableJson(value) {
|
|
519
|
+
return JSON.stringify(sortJson(value)) ?? "null";
|
|
520
|
+
}
|
|
521
|
+
function sortJson(value) {
|
|
522
|
+
if (Array.isArray(value)) {
|
|
523
|
+
return value.map((item) => sortJson(item));
|
|
524
|
+
}
|
|
525
|
+
if (isPlainRecord(value)) {
|
|
526
|
+
const sorted = {};
|
|
527
|
+
for (const key of Object.keys(value).sort()) {
|
|
528
|
+
sorted[key] = sortJson(value[key]);
|
|
529
|
+
}
|
|
530
|
+
return sorted;
|
|
531
|
+
}
|
|
532
|
+
return value === void 0 ? null : value;
|
|
533
|
+
}
|
|
534
|
+
function isPlainRecord(value) {
|
|
535
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
exports.HarnessError = HarnessError;
|
|
539
|
+
exports.HarnessTargetNotFoundError = HarnessTargetNotFoundError;
|
|
540
|
+
exports.createFixtureRunner = createFixtureRunner;
|
|
541
|
+
exports.defineTarget = defineTarget;
|
|
542
|
+
//# sourceMappingURL=index.cjs.map
|
|
543
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["process","readFile","inspectRun","isAgentInspectEnabled","observe","maybeInspectRun","path"],"mappings":"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","file":"index.cjs","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport process from \"node:process\";\n\nimport {\n inspectRun,\n maybeInspectRun,\n observe,\n} from \"agent-inspect\";\nimport type { InspectRunOptions } from \"agent-inspect\";\nimport { isAgentInspectEnabled } from \"agent-inspect/advanced\";\n\nexport type HarnessDiagnosticSeverity = \"warning\" | \"error\";\n\nexport type HarnessDiagnosticCode =\n | \"target_not_found\"\n | \"bootstrap_failed\"\n | \"resolve_failed\"\n | \"invoke_failed\"\n | \"shutdown_failed\"\n | \"cli_usage_error\"\n | \"fixture_read_failed\"\n | \"stdin_read_failed\"\n | \"expected_read_failed\"\n | \"expected_output_mismatch\";\n\nexport interface HarnessDiagnosticError {\n name?: string;\n message: string;\n stack?: string;\n}\n\nexport interface HarnessDiagnostic {\n code: HarnessDiagnosticCode;\n severity: HarnessDiagnosticSeverity;\n message: string;\n targetName?: string;\n timestamp: number;\n error?: HarnessDiagnosticError;\n}\n\nexport type HarnessDiagnosticInput = Omit<HarnessDiagnostic, \"timestamp\"> & {\n timestamp?: number;\n};\n\nexport interface FixtureRunnerContext<TApp = unknown> {\n readonly runnerName: string;\n readonly targetName: string;\n readonly app: TApp | undefined;\n addDiagnostic(diagnostic: HarnessDiagnosticInput): void;\n getDiagnostics(): readonly HarnessDiagnostic[];\n}\n\nexport interface TargetDefinition<TApp, TTarget, TInput, TOutput> {\n /** Human-readable label for target listing. Experimental during v1.x. */\n description?: string;\n /** Safe, bounded metadata for local tooling and recipes. Experimental during v1.x. */\n metadata?: Record<string, unknown>;\n /** Resolve a framework-specific object or function from the bootstrapped app. */\n resolve(\n app: TApp,\n context: FixtureRunnerContext<TApp>,\n ): TTarget | Promise<TTarget>;\n /** Invoke the resolved target with fixture input. */\n invoke(\n target: TTarget,\n input: TInput,\n context: FixtureRunnerContext<TApp>,\n ): TOutput | Promise<TOutput>;\n}\n\nexport type FixtureTargets<TApp = unknown> = Record<\n string,\n TargetDefinition<TApp, unknown, unknown, unknown>\n>;\n\nexport type TargetInput<TDefinition> =\n TDefinition extends TargetDefinition<infer _TApp, infer _TTarget, infer TInput, infer _TOutput>\n ? TInput\n : never;\n\nexport type TargetOutput<TDefinition> =\n TDefinition extends TargetDefinition<infer _TApp, infer _TTarget, infer _TInput, infer TOutput>\n ? TOutput\n : never;\n\nexport type HarnessTraceMode = \"off\" | \"run\" | \"run-if-enabled\" | \"observe\";\n\nexport interface HarnessTraceOptions extends InspectRunOptions {\n /**\n * `run-if-enabled` uses `maybeInspectRun()` and is the default.\n * `observe` proxies resolved targets and traces `run` / `execute` / `invoke` methods when enabled.\n */\n mode?: HarnessTraceMode;\n runName?: string | ((targetName: string) => string);\n}\n\nexport interface FixtureRunnerOptions<TApp, TTargets extends FixtureTargets<TApp>> {\n name?: string;\n targets: TTargets;\n trace?: HarnessTraceOptions;\n bootstrap?(\n context: FixtureRunnerContext<TApp>,\n ): TApp | Promise<TApp>;\n shutdown?(\n app: TApp | undefined,\n context: FixtureRunnerContext<TApp>,\n ): void | Promise<void>;\n}\n\nexport interface FixtureRunOptions {\n trace?: HarnessTraceOptions;\n}\n\nexport interface HarnessArgvIo {\n cwd?: string;\n stdin?(): string | Promise<string>;\n stdout?(chunk: string): void;\n stderr?(chunk: string): void;\n}\n\nexport interface HarnessArgvResult {\n ok: boolean;\n exitCode: 0 | 1;\n targetName?: string;\n listed?: boolean;\n matchedExpected?: boolean;\n output?: unknown;\n error?: HarnessDiagnosticError;\n diagnostics: readonly HarnessDiagnostic[];\n}\n\nexport interface HarnessTargetInfo {\n name: string;\n description?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface FixtureRunner<TApp, TTargets extends FixtureTargets<TApp>> {\n listTargets(): HarnessTargetInfo[];\n getDiagnostics(): readonly HarnessDiagnostic[];\n runTarget<TName extends Extract<keyof TTargets, string>>(\n targetName: TName,\n input: TargetInput<TTargets[TName]>,\n options?: FixtureRunOptions,\n ): Promise<TargetOutput<TTargets[TName]>>;\n runFromArgv(argv?: readonly string[], io?: HarnessArgvIo): Promise<HarnessArgvResult>;\n}\n\nexport class HarnessError extends Error {\n readonly code: HarnessDiagnosticCode;\n readonly targetName?: string;\n\n constructor(\n code: HarnessDiagnosticCode,\n message: string,\n options?: { targetName?: string; cause?: unknown },\n ) {\n super(message, { cause: options?.cause });\n this.name = \"HarnessError\";\n this.code = code;\n this.targetName = options?.targetName;\n }\n}\n\nexport class HarnessTargetNotFoundError extends HarnessError {\n constructor(targetName: string) {\n super(\"target_not_found\", `Harness target \"${targetName}\" was not found.`, {\n targetName,\n });\n this.name = \"HarnessTargetNotFoundError\";\n }\n}\n\nexport function defineTarget<TApp, TTarget, TInput, TOutput>(\n definition: TargetDefinition<TApp, TTarget, TInput, TOutput>,\n): TargetDefinition<TApp, TTarget, TInput, TOutput> {\n return definition;\n}\n\nexport function createFixtureRunner<\n TApp,\n const TTargets extends FixtureTargets<TApp>,\n>(options: FixtureRunnerOptions<TApp, TTargets>): FixtureRunner<TApp, TTargets> {\n return new DefaultFixtureRunner(options);\n}\n\nclass DefaultFixtureRunner<TApp, TTargets extends FixtureTargets<TApp>>\n implements FixtureRunner<TApp, TTargets>\n{\n private readonly diagnostics: HarnessDiagnostic[] = [];\n private readonly runnerName: string;\n\n constructor(private readonly options: FixtureRunnerOptions<TApp, TTargets>) {\n this.runnerName = normalizeRunnerName(options.name);\n }\n\n listTargets(): HarnessTargetInfo[] {\n return Object.entries(this.options.targets)\n .map(([name, definition]) => {\n const info: HarnessTargetInfo = { name };\n if (definition.description !== undefined) info.description = definition.description;\n if (definition.metadata !== undefined) info.metadata = definition.metadata;\n return info;\n })\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n getDiagnostics(): readonly HarnessDiagnostic[] {\n return [...this.diagnostics];\n }\n\n async runTarget<TName extends Extract<keyof TTargets, string>>(\n targetName: TName,\n input: TargetInput<TTargets[TName]>,\n options?: FixtureRunOptions,\n ): Promise<TargetOutput<TTargets[TName]>> {\n return await this.runTargetInternal(targetName, input, options) as TargetOutput<\n TTargets[TName]\n >;\n }\n\n async runFromArgv(\n argv: readonly string[] = process.argv.slice(2),\n io: HarnessArgvIo = {},\n ): Promise<HarnessArgvResult> {\n const parsed = this.parseArgv(argv);\n const stdout = io.stdout ?? ((chunk: string) => process.stdout.write(chunk));\n const stderr = io.stderr ?? ((chunk: string) => process.stderr.write(chunk));\n\n if (!parsed.ok) {\n return this.writeArgvFailure(parsed.message, \"cli_usage_error\", stdout, stderr);\n }\n\n if (parsed.list) {\n const result: HarnessArgvResult = {\n ok: true,\n exitCode: 0,\n listed: true,\n output: { targets: this.listTargets() },\n diagnostics: this.getDiagnostics(),\n };\n writeJsonLine(stdout, { targets: this.listTargets() });\n stderr(`agent-inspect harness: listed ${this.listTargets().length} target(s)\\n`);\n return result;\n }\n\n if (parsed.targetName === undefined) {\n return this.writeArgvFailure(\n \"Missing target name. Pass --list or a target name.\",\n \"cli_usage_error\",\n stdout,\n stderr,\n );\n }\n\n if (parsed.fixturePath !== undefined && parsed.readStdin) {\n return this.writeArgvFailure(\n \"Pass either --fixture or --stdin, not both.\",\n \"cli_usage_error\",\n stdout,\n stderr,\n parsed.targetName,\n );\n }\n\n const cwd = io.cwd ?? process.cwd();\n const inputResult = await this.readInput(parsed, io, cwd);\n if (!inputResult.ok) {\n return this.writeArgvFailure(\n inputResult.message,\n inputResult.code,\n stdout,\n stderr,\n parsed.targetName,\n inputResult.error,\n );\n }\n\n try {\n const output = await this.runTargetInternal(parsed.targetName, inputResult.value, {\n trace: parsed.trace,\n });\n const expectedResult = await this.compareExpectedOutput(\n parsed,\n output,\n cwd,\n );\n if (!expectedResult.ok) {\n return this.writeArgvFailure(\n expectedResult.message,\n expectedResult.code,\n stdout,\n stderr,\n parsed.targetName,\n expectedResult.error,\n output,\n false,\n );\n }\n\n const matchedExpected = expectedResult.compared ? true : undefined;\n const result: HarnessArgvResult = {\n ok: true,\n exitCode: 0,\n targetName: parsed.targetName,\n output,\n diagnostics: this.getDiagnostics(),\n ...(matchedExpected !== undefined ? { matchedExpected } : {}),\n };\n writeJsonLine(stdout, {\n ok: true,\n target: parsed.targetName,\n output,\n ...(matchedExpected !== undefined ? { matchedExpected } : {}),\n });\n stderr(`agent-inspect harness: ok ${parsed.targetName}\\n`);\n return result;\n } catch (error) {\n return this.writeArgvFailure(\n `Harness target \"${parsed.targetName}\" failed.`,\n \"invoke_failed\",\n stdout,\n stderr,\n parsed.targetName,\n error,\n );\n }\n }\n\n private async runTargetInternal(\n targetName: string,\n input: unknown,\n options?: FixtureRunOptions,\n ): Promise<unknown> {\n const definition = this.options.targets[targetName] as\n | TargetDefinition<TApp, unknown, unknown, unknown>\n | undefined;\n let app: TApp | undefined;\n const context = this.createContext(targetName, () => app);\n\n if (definition === undefined) {\n const error = new HarnessTargetNotFoundError(targetName);\n this.addDiagnostic({\n code: \"target_not_found\",\n severity: \"error\",\n message: error.message,\n targetName,\n error: serializeError(error),\n });\n throw error;\n }\n\n let primaryError: unknown;\n try {\n app = await this.bootstrap(context);\n const target = await this.resolveTarget(definition, app, context, targetName);\n return await this.invokeTarget(definition, target, input, context, targetName, options);\n } catch (error) {\n primaryError = error;\n throw error;\n } finally {\n await this.shutdown(app, context, targetName, primaryError);\n }\n }\n\n private createContext(\n targetName: string,\n getApp: () => TApp | undefined,\n ): FixtureRunnerContext<TApp> {\n return {\n runnerName: this.runnerName,\n targetName,\n get app() {\n return getApp();\n },\n addDiagnostic: (diagnostic) => this.addDiagnostic(diagnostic),\n getDiagnostics: () => this.getDiagnostics(),\n };\n }\n\n private addDiagnostic(input: HarnessDiagnosticInput): void {\n this.diagnostics.push({\n ...input,\n timestamp: input.timestamp ?? Date.now(),\n });\n }\n\n private parseArgv(argv: readonly string[]): ParsedArgv {\n const parsed: ParsedArgvSuccess = {\n ok: true,\n list: false,\n readStdin: false,\n trace: {},\n };\n const positional: string[] = [];\n\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n switch (arg) {\n case \"--list\":\n parsed.list = true;\n break;\n case \"--fixture\": {\n const value = argv[i + 1];\n if (value === undefined) return usageError(\"--fixture requires a path.\");\n parsed.fixturePath = value;\n i += 1;\n break;\n }\n case \"--stdin\":\n parsed.readStdin = true;\n break;\n case \"--expected\":\n case \"--expected-output\": {\n const value = argv[i + 1];\n if (value === undefined) return usageError(`${arg} requires a path.`);\n parsed.expectedPath = value;\n i += 1;\n break;\n }\n case \"--trace\":\n parsed.trace.mode = \"run\";\n break;\n case \"--no-trace\":\n parsed.trace.mode = \"off\";\n break;\n case \"--trace-dir\": {\n const value = argv[i + 1];\n if (value === undefined) return usageError(\"--trace-dir requires a path.\");\n parsed.trace.traceDir = value;\n i += 1;\n break;\n }\n case \"--trace-mode\": {\n const value = argv[i + 1];\n if (!isHarnessTraceMode(value)) {\n return usageError(\n \"--trace-mode must be one of: off, run, run-if-enabled, observe.\",\n );\n }\n parsed.trace.mode = value;\n i += 1;\n break;\n }\n default:\n if (arg?.startsWith(\"--\")) {\n return usageError(`Unknown harness option: ${arg}`);\n }\n if (arg !== undefined) positional.push(arg);\n }\n }\n\n if (positional.length > 1) {\n return usageError(\"Pass at most one target name.\");\n }\n parsed.targetName = positional[0];\n return parsed;\n }\n\n private async readInput(\n parsed: ParsedArgvSuccess,\n io: HarnessArgvIo,\n cwd: string,\n ): Promise<InputReadResult> {\n if (parsed.fixturePath !== undefined) {\n try {\n return {\n ok: true,\n value: parseJsonPayload(\n await readFile(resolvePath(cwd, parsed.fixturePath), \"utf8\"),\n parsed.fixturePath,\n ),\n };\n } catch (error) {\n return {\n ok: false,\n code: \"fixture_read_failed\",\n message: `Failed to read fixture: ${parsed.fixturePath}`,\n error,\n };\n }\n }\n\n if (parsed.readStdin) {\n try {\n const raw = io.stdin === undefined ? await readProcessStdin() : await io.stdin();\n return { ok: true, value: parseJsonPayload(raw, \"stdin\") };\n } catch (error) {\n return {\n ok: false,\n code: \"stdin_read_failed\",\n message: \"Failed to read JSON from stdin.\",\n error,\n };\n }\n }\n\n return { ok: true, value: undefined };\n }\n\n private async compareExpectedOutput(\n parsed: ParsedArgvSuccess,\n output: unknown,\n cwd: string,\n ): Promise<ExpectedCompareResult> {\n if (parsed.expectedPath === undefined) {\n return { ok: true, compared: false };\n }\n\n let expected: unknown;\n try {\n expected = parseJsonPayload(\n await readFile(resolvePath(cwd, parsed.expectedPath), \"utf8\"),\n parsed.expectedPath,\n );\n } catch (error) {\n return {\n ok: false,\n compared: true,\n code: \"expected_read_failed\",\n message: `Failed to read expected output: ${parsed.expectedPath}`,\n error,\n };\n }\n\n if (stableJson(expected) !== stableJson(output)) {\n return {\n ok: false,\n compared: true,\n code: \"expected_output_mismatch\",\n message: `Expected output mismatch: ${parsed.expectedPath}`,\n error: new Error(\"Expected output mismatch.\"),\n };\n }\n\n return { ok: true, compared: true };\n }\n\n private writeArgvFailure(\n message: string,\n code: HarnessDiagnosticCode,\n stdout: (chunk: string) => void,\n stderr: (chunk: string) => void,\n targetName?: string,\n error?: unknown,\n output?: unknown,\n matchedExpected?: boolean,\n ): HarnessArgvResult {\n const diagnostic: HarnessDiagnosticInput = {\n code,\n severity: \"error\",\n message,\n ...(targetName !== undefined ? { targetName } : {}),\n ...(error !== undefined ? { error: serializeError(error) } : {}),\n };\n this.addDiagnostic(diagnostic);\n const result: HarnessArgvResult = {\n ok: false,\n exitCode: 1,\n ...(targetName !== undefined ? { targetName } : {}),\n ...(output !== undefined ? { output } : {}),\n ...(matchedExpected !== undefined ? { matchedExpected } : {}),\n ...(error !== undefined ? { error: serializeError(error) } : {}),\n diagnostics: this.getDiagnostics(),\n };\n writeJsonLine(stdout, {\n ok: false,\n ...(targetName !== undefined ? { target: targetName } : {}),\n ...(output !== undefined ? { output } : {}),\n ...(matchedExpected !== undefined ? { matchedExpected } : {}),\n ...(error !== undefined ? { error: serializeError(error) } : {}),\n diagnostics: this.getDiagnostics(),\n });\n stderr(`agent-inspect harness: ${message}\\n`);\n return result;\n }\n\n private async bootstrap(context: FixtureRunnerContext<TApp>): Promise<TApp> {\n try {\n if (this.options.bootstrap === undefined) {\n return undefined as TApp;\n }\n return await this.options.bootstrap(context);\n } catch (error) {\n this.addDiagnostic({\n code: \"bootstrap_failed\",\n severity: \"error\",\n message: \"Harness bootstrap failed.\",\n targetName: context.targetName,\n error: serializeError(error),\n });\n throw new HarnessError(\"bootstrap_failed\", \"Harness bootstrap failed.\", {\n targetName: context.targetName,\n cause: error,\n });\n }\n }\n\n private async resolveTarget<TOutput>(\n definition: TargetDefinition<TApp, unknown, unknown, TOutput>,\n app: TApp,\n context: FixtureRunnerContext<TApp>,\n targetName: string,\n ): Promise<unknown> {\n try {\n return await definition.resolve(app, context);\n } catch (error) {\n this.addDiagnostic({\n code: \"resolve_failed\",\n severity: \"error\",\n message: `Harness target \"${targetName}\" failed to resolve.`,\n targetName,\n error: serializeError(error),\n });\n throw new HarnessError(\n \"resolve_failed\",\n `Harness target \"${targetName}\" failed to resolve.`,\n { targetName, cause: error },\n );\n }\n }\n\n private async invokeTarget<TOutput>(\n definition: TargetDefinition<TApp, unknown, unknown, TOutput>,\n target: unknown,\n input: unknown,\n context: FixtureRunnerContext<TApp>,\n targetName: string,\n options: FixtureRunOptions | undefined,\n ): Promise<TOutput> {\n const trace = mergeTraceOptions(this.options.trace, options?.trace);\n const runName = resolveRunName(this.runnerName, targetName, trace.runName);\n const traceOptions = toInspectRunOptions(trace);\n const invoke = () => definition.invoke(target, input, context);\n\n try {\n switch (trace.mode ?? \"run-if-enabled\") {\n case \"off\":\n return await invoke();\n case \"run\":\n return await inspectRun(runName, invoke, {\n ...traceOptions,\n enabled: traceOptions.enabled ?? true,\n });\n case \"observe\": {\n const enabled =\n traceOptions.enabled ?? isAgentInspectEnabled(process.env.AGENT_INSPECT);\n const observedTarget = observe(target, {\n ...traceOptions,\n enabled,\n });\n return await definition.invoke(observedTarget, input, context);\n }\n case \"run-if-enabled\":\n return await maybeInspectRun(runName, invoke, traceOptions);\n }\n } catch (error) {\n this.addDiagnostic({\n code: \"invoke_failed\",\n severity: \"error\",\n message: `Harness target \"${targetName}\" failed during invocation.`,\n targetName,\n error: serializeError(error),\n });\n throw error;\n }\n }\n\n private async shutdown(\n app: TApp | undefined,\n context: FixtureRunnerContext<TApp>,\n targetName: string,\n primaryError: unknown,\n ): Promise<void> {\n if (this.options.shutdown === undefined) return;\n\n try {\n await this.options.shutdown(app, context);\n } catch (error) {\n this.addDiagnostic({\n code: \"shutdown_failed\",\n severity: \"error\",\n message: \"Harness shutdown failed.\",\n targetName,\n error: serializeError(error),\n });\n if (primaryError === undefined) {\n throw new HarnessError(\"shutdown_failed\", \"Harness shutdown failed.\", {\n targetName,\n cause: error,\n });\n }\n }\n }\n}\n\nfunction normalizeRunnerName(name: string | undefined): string {\n const trimmed = name?.trim();\n return trimmed === undefined || trimmed === \"\" ? \"agent-inspect-harness\" : trimmed;\n}\n\nfunction resolveRunName(\n runnerName: string,\n targetName: string,\n configured: HarnessTraceOptions[\"runName\"],\n): string {\n if (typeof configured === \"function\") return configured(targetName);\n if (typeof configured === \"string\" && configured.trim() !== \"\") return configured.trim();\n return `${runnerName}:${targetName}`;\n}\n\nfunction mergeTraceOptions(\n base: HarnessTraceOptions | undefined,\n override: HarnessTraceOptions | undefined,\n): HarnessTraceOptions {\n return { ...(base ?? {}), ...(override ?? {}) };\n}\n\nfunction toInspectRunOptions(options: HarnessTraceOptions): InspectRunOptions {\n const { mode: _mode, runName: _runName, ...inspectOptions } = options;\n return inspectOptions;\n}\n\nfunction serializeError(error: unknown): HarnessDiagnosticError {\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack,\n };\n }\n return {\n message: String(error),\n };\n}\n\ntype ParsedArgv = ParsedArgvSuccess | ParsedArgvError;\n\ninterface ParsedArgvSuccess {\n ok: true;\n list: boolean;\n readStdin: boolean;\n targetName?: string;\n fixturePath?: string;\n expectedPath?: string;\n trace: HarnessTraceOptions;\n}\n\ninterface ParsedArgvError {\n ok: false;\n message: string;\n}\n\ntype InputReadResult =\n | { ok: true; value: unknown }\n | {\n ok: false;\n code: \"fixture_read_failed\" | \"stdin_read_failed\";\n message: string;\n error: unknown;\n };\n\ntype ExpectedCompareResult =\n | { ok: true; compared: boolean }\n | {\n ok: false;\n compared: true;\n code: \"expected_read_failed\" | \"expected_output_mismatch\";\n message: string;\n error: unknown;\n };\n\nfunction usageError(message: string): ParsedArgvError {\n return { ok: false, message };\n}\n\nfunction isHarnessTraceMode(value: unknown): value is HarnessTraceMode {\n return (\n value === \"off\" ||\n value === \"run\" ||\n value === \"run-if-enabled\" ||\n value === \"observe\"\n );\n}\n\nfunction resolvePath(cwd: string, inputPath: string): string {\n return path.isAbsolute(inputPath) ? inputPath : path.resolve(cwd, inputPath);\n}\n\nfunction parseJsonPayload(raw: string, source: string): unknown {\n const trimmed = raw.trim();\n if (trimmed === \"\") {\n throw new Error(`Empty JSON payload from ${source}.`);\n }\n return JSON.parse(trimmed) as unknown;\n}\n\nasync function readProcessStdin(): Promise<string> {\n process.stdin.setEncoding(\"utf8\");\n let raw = \"\";\n for await (const chunk of process.stdin) {\n raw += chunk;\n }\n return raw;\n}\n\nfunction writeJsonLine(writer: (chunk: string) => void, value: unknown): void {\n writer(`${stableJson(value)}\\n`);\n}\n\nfunction stableJson(value: unknown): string {\n return JSON.stringify(sortJson(value)) ?? \"null\";\n}\n\nfunction sortJson(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => sortJson(item));\n }\n if (isPlainRecord(value)) {\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(value).sort()) {\n sorted[key] = sortJson(value[key]);\n }\n return sorted;\n }\n return value === undefined ? null : value;\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
|