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