cuonztech-agent-harness 0.10.0 → 0.11.1
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/README.md +54 -13
- package/dist/campaign/runner.js +47 -23
- package/dist/cli-args.js +42 -0
- package/dist/core/artifact-integrity.js +129 -30
- package/dist/core/call-forensics.js +18 -0
- package/dist/core/claim-check.js +57 -6
- package/dist/core/report-generator.js +17 -1
- package/dist/doctor.js +3 -0
- package/dist/dossier-report.js +230 -51
- package/dist/engine/state-machine.js +26 -3
- package/dist/hardening/patch-generator.js +9 -3
- package/dist/index.js +39 -23
- package/dist/license/ed25519.js +21 -15
- package/dist/license/gate.js +2 -2
- package/dist/license/verifier.js +10 -1
- package/dist/plain-report.js +130 -17
- package/dist/proxy/classification-doubt.js +89 -4
- package/dist/proxy/interceptor.js +25 -4
- package/dist/proxy/verb-words.js +160 -4
- package/dist/report-file-guard.js +3 -0
- package/dist/schema-audit.js +81 -25
- package/dist/ui/page.js +5 -4
- package/dist/ui/server.js +24 -10
- package/dist/verify.js +234 -0
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/ui/server.js
CHANGED
|
@@ -19,6 +19,9 @@ import { runBenchmark, defaultConfig } from "../benchmark/runner.js";
|
|
|
19
19
|
import { VERSION } from "../version.js";
|
|
20
20
|
import { PAGE_HTML } from "./page.js";
|
|
21
21
|
import { resolveLicense, verifyLicense, resolveProGate, lockedNotice } from "../license/index.js";
|
|
22
|
+
import { expandEqualsForm, optionName, redactSecrets } from "../cli-args.js";
|
|
23
|
+
// Platzhalter statt Lizenzcode in erzeugten Befehlen und Konfigurationen.
|
|
24
|
+
const LICENSE_PLACEHOLDER = "<DEIN-LIZENZCODE>";
|
|
22
25
|
const MAX_BODY = 64 * 1024;
|
|
23
26
|
const MAX_NAME = 200;
|
|
24
27
|
const MAX_AGENT_RESPONSE = 10_000;
|
|
@@ -141,9 +144,6 @@ export async function startUi(options = {}) {
|
|
|
141
144
|
return file;
|
|
142
145
|
};
|
|
143
146
|
let currentLicenseVerdict = resolveLicense(options.licenseKey);
|
|
144
|
-
// Nur ein ausdrücklich übergebener, gültiger Schlüssel wird in die erzeugte Agenten-Konfiguration übernommen
|
|
145
|
-
// (damit der Proxy des Agenten die Pro-Teile in den Report schreibt); aus der Umgebung stammende nie.
|
|
146
|
-
let explicitLicenseKey = options.licenseKey;
|
|
147
147
|
const dossierOptions = () => ({
|
|
148
148
|
customerName: session?.customerName,
|
|
149
149
|
systemName: session?.systemName,
|
|
@@ -200,7 +200,6 @@ export async function startUi(options = {}) {
|
|
|
200
200
|
const body = await readJson(req);
|
|
201
201
|
const key = typeof body.key === "string" ? body.key.trim() : "";
|
|
202
202
|
currentLicenseVerdict = verifyLicense(key);
|
|
203
|
-
explicitLicenseKey = key;
|
|
204
203
|
return sendJson(res, 200, {
|
|
205
204
|
valid: currentLicenseVerdict.valid,
|
|
206
205
|
tier: currentLicenseVerdict.tier ?? "evaluation",
|
|
@@ -253,11 +252,20 @@ export async function startUi(options = {}) {
|
|
|
253
252
|
"--allow-writes",
|
|
254
253
|
"--audit-tools", "off",
|
|
255
254
|
"--report-file", reportFile,
|
|
256
|
-
...(currentLicenseVerdict.valid && explicitLicenseKey ? ["--license", explicitLicenseKey] : []),
|
|
257
255
|
];
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
// Der Lizenzcode steht nie in dem, was die Oberfläche ausgibt (Befehl, Konfigurationsdatei, Prozessliste): Mit
|
|
257
|
+
// gültiger Lizenz kommt ein Platzhalter für die Umgebungsvariable des Proxys hinein, den der Nutzer ersetzt.
|
|
258
|
+
const withLicense = currentLicenseVerdict.valid;
|
|
259
|
+
const claudeCode = [
|
|
260
|
+
withLicense ? `claude mcp add --scope user -e AGENT_AUDIT_LICENSE=${LICENSE_PLACEHOLDER} agent-harness --` : "claude mcp add --scope user agent-harness --",
|
|
261
|
+
process.execPath,
|
|
262
|
+
...proxyArgs,
|
|
263
|
+
].map((p, i) => (i === 0 ? p : quoteArg(p))).join(" ");
|
|
264
|
+
const claudeDesktop = JSON.stringify({ mcpServers: { "agent-harness": { command: process.execPath, args: proxyArgs, ...(withLicense ? { env: { AGENT_AUDIT_LICENSE: LICENSE_PLACEHOLDER } } : {}) } } }, null, 2);
|
|
265
|
+
const licenseHint = withLicense
|
|
266
|
+
? `Ersetze ${LICENSE_PLACEHOLDER} durch deinen Lizenzcode (aus der E-Mail), damit der Proxy deines Agenten Textbausteine und Schema-Befunde in den Bericht schreibt. Der Code steht aus Sicherheitsgründen nicht in diesem Befehl. Ohne ihn läuft der Test vollständig, der Bericht enthält die Pro-Teile aber nicht, und das Dossier sagt das ausdrücklich.`
|
|
267
|
+
: "";
|
|
268
|
+
return sendJson(res, 200, { claudeCode, claudeDesktop, reportFile, scenario, licenseHint });
|
|
261
269
|
}
|
|
262
270
|
if (req.method === "POST" && url.pathname === "/api/dossier-meta") {
|
|
263
271
|
const body = await readJson(req);
|
|
@@ -361,6 +369,8 @@ export async function runUiMode(argv) {
|
|
|
361
369
|
" --license <key> CuonzTech Agent Audit Pro Lizenzschlüssel (oder Umgebungsvariable AGENT_AUDIT_LICENSE)\n");
|
|
362
370
|
return 0;
|
|
363
371
|
}
|
|
372
|
+
// --flag=wert gilt wie --flag wert, auch für --license.
|
|
373
|
+
argv = expandEqualsForm(argv, new Set(["--license", "--port"]));
|
|
364
374
|
let port;
|
|
365
375
|
let open = true;
|
|
366
376
|
let licenseKey;
|
|
@@ -370,6 +380,10 @@ export async function runUiMode(argv) {
|
|
|
370
380
|
open = false;
|
|
371
381
|
else if (token === "--license") {
|
|
372
382
|
licenseKey = argv[++i] ?? "";
|
|
383
|
+
if (licenseKey.trim() === "" || licenseKey.startsWith("--")) {
|
|
384
|
+
process.stderr.write("Error: --license needs a key.\nUsage: ui [--port <n>] [--no-open] [--license <key>]\n");
|
|
385
|
+
return 1;
|
|
386
|
+
}
|
|
373
387
|
}
|
|
374
388
|
else if (token === "--port") {
|
|
375
389
|
if (port !== undefined) {
|
|
@@ -378,13 +392,13 @@ export async function runUiMode(argv) {
|
|
|
378
392
|
}
|
|
379
393
|
const raw = argv[++i] ?? "";
|
|
380
394
|
if (!/^\d+$/.test(raw) || Number(raw) > 65535) {
|
|
381
|
-
process.stderr.write(`Error: --port must be a number between 0 and 65535, got "${raw}".\n`);
|
|
395
|
+
process.stderr.write(`Error: --port must be a number between 0 and 65535, got "${redactSecrets(raw)}".\n`);
|
|
382
396
|
return 1;
|
|
383
397
|
}
|
|
384
398
|
port = Number(raw);
|
|
385
399
|
}
|
|
386
400
|
else {
|
|
387
|
-
process.stderr.write(`Error: unknown option ${token}\nUsage: ui [--port <n>] [--no-open]\n`);
|
|
401
|
+
process.stderr.write(`Error: unknown option ${optionName(token)}\nUsage: ui [--port <n>] [--no-open] [--license <key>]\n`);
|
|
388
402
|
return 1;
|
|
389
403
|
}
|
|
390
404
|
}
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// `verify <dossier|report>`: rechnet die Prüfsumme einer Report-Datei oder eines Dossiers neu und meldet
|
|
2
|
+
// OK oder MISMATCH mit dem Grund. Exit-Code 0 nur bei OK, sonst 1. Es bleibt eine Prüfsumme ohne Signatur
|
|
3
|
+
// eines Dritten: Wer Datei und Prüfsumme zusammen ändert, kann fälschen; der Wert schützt nur, wenn er
|
|
4
|
+
// getrennt von der Datei aufbewahrt wird (dafür gibt es `--expect <sha256>`).
|
|
5
|
+
import { readFile, stat } from "node:fs/promises";
|
|
6
|
+
import { DOSSIER_SEAL_MARKER, canonicalJson, checkReportIntegrity, extractDossierSeal, sha256Hex } from "./core/artifact-integrity.js";
|
|
7
|
+
import { dossierHeaderChecks } from "./dossier-report.js";
|
|
8
|
+
const MAX_BYTES = 64 * 1024 * 1024;
|
|
9
|
+
function countOf(text, part) {
|
|
10
|
+
if (part === "")
|
|
11
|
+
return 0;
|
|
12
|
+
let n = 0;
|
|
13
|
+
for (let i = text.indexOf(part); i !== -1; i = text.indexOf(part, i + part.length))
|
|
14
|
+
n++;
|
|
15
|
+
return n;
|
|
16
|
+
}
|
|
17
|
+
const USAGE = "Usage: verify <dossier|report-file> [--report <report-file>] [--expect <sha256>]\n";
|
|
18
|
+
const HELP = `${USAGE}\n` +
|
|
19
|
+
"Rechnet die Prüfsumme einer Report-Datei (--report-file des Proxys) oder eines Dossiers (report --dossier) neu\n" +
|
|
20
|
+
"und gibt OK oder MISMATCH mit dem Grund aus. Exit-Code 0 bei OK, sonst 1.\n" +
|
|
21
|
+
" --report <file> bei einem Dossier zusätzlich die Report-Datei prüfen, aus der es erzeugt wurde, und beide abgleichen\n" +
|
|
22
|
+
" --expect <sha256> die getrennt aufbewahrte Prüfsumme (Report-Datei: payloadSha256; Dossier: Prüfsumme des Dossiers);\n" +
|
|
23
|
+
" stimmt sie nicht mit der Datei überein, ist das ein MISMATCH\n\n" +
|
|
24
|
+
"Es ist eine Prüfsumme ohne Signatur eines Dritten und kein Zertifikat. Wer Datei und Prüfsumme zusammen ändert, kann\n" +
|
|
25
|
+
"fälschen. Sie schützt nur, wenn der Wert getrennt von der Datei aufbewahrt wird.\n";
|
|
26
|
+
function statusOfCheck(check) {
|
|
27
|
+
if (check.status === "ok")
|
|
28
|
+
return "OK";
|
|
29
|
+
return check.status === "mismatch" ? "MISMATCH" : "NOT_VERIFIABLE";
|
|
30
|
+
}
|
|
31
|
+
export function verifyReportFileContent(file, expect) {
|
|
32
|
+
const check = checkReportIntegrity(file);
|
|
33
|
+
const lines = [check.reasonDe];
|
|
34
|
+
let status = statusOfCheck(check);
|
|
35
|
+
const sha = file.integrity?.payloadSha256 ?? null;
|
|
36
|
+
if (status === "OK" && expect !== undefined && expect.toLowerCase() !== sha) {
|
|
37
|
+
status = "MISMATCH";
|
|
38
|
+
lines.unshift(`Die Datei stimmt zwar mit ihrer eigenen Prüfsumme überein, die Prüfsumme ist aber nicht die erwartete: erwartet ${expect.toLowerCase()}, in der Datei ${sha ?? "—"}. Datei und Prüfsumme wurden gemeinsam ausgetauscht oder die falsche Datei liegt vor.`);
|
|
39
|
+
}
|
|
40
|
+
return { status, kind: "report", sha256: sha, lines };
|
|
41
|
+
}
|
|
42
|
+
export function verifyDossierContent(text, options = {}) {
|
|
43
|
+
const extracted = extractDossierSeal(text);
|
|
44
|
+
if ("error" in extracted) {
|
|
45
|
+
return {
|
|
46
|
+
status: "NOT_VERIFIABLE",
|
|
47
|
+
kind: "dossier",
|
|
48
|
+
sha256: null,
|
|
49
|
+
lines: [
|
|
50
|
+
extracted.error === "missing"
|
|
51
|
+
? "Kein Prüfsummen-Siegel gefunden: Das Dossier stammt aus Harness < 0.11.1 (Siegel erst ab 0.11.1) oder das Siegel wurde entfernt. Nicht prüfbar."
|
|
52
|
+
: "Das Prüfsummen-Siegel am Ende des Dossiers ist beschädigt und lässt sich nicht lesen. Nicht prüfbar.",
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const { seal } = extracted;
|
|
57
|
+
const recomputed = sha256Hex(canonicalJson(seal.core));
|
|
58
|
+
const lines = [];
|
|
59
|
+
if (recomputed !== seal.sha256) {
|
|
60
|
+
return {
|
|
61
|
+
status: "MISMATCH",
|
|
62
|
+
kind: "dossier",
|
|
63
|
+
sha256: seal.sha256,
|
|
64
|
+
lines: ["Die Prüfsumme des Dossiers stimmt nicht zu seinem eingebetteten Kern: Kern (Urteil, Score, Namen, Lizenzstatus ...) oder Prüfsumme wurden nach dem Erzeugen verändert."],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Der sichtbare Kopf muss dem gesiegelten Kern entsprechen.
|
|
68
|
+
const header = seal.core.header;
|
|
69
|
+
if (!header || typeof header !== "object" || !Array.isArray(header.rows) || !Array.isArray(header.notes)) {
|
|
70
|
+
return { status: "NOT_VERIFIABLE", kind: "dossier", sha256: seal.sha256, lines: ["Das Siegel enthält den sichtbaren Kopf nicht (Dossier aus einer Vorabfassung von 0.11.1). Nicht prüfbar."] };
|
|
71
|
+
}
|
|
72
|
+
const isHtml = /<html[\s>]/i.test(text.slice(0, 2000));
|
|
73
|
+
const expected = dossierHeaderChecks(seal.core)[isHtml ? "html" : "md"];
|
|
74
|
+
// Gezählt wird im sichtbaren Teil vor dem Siegel (das JSON des Siegels wiederholt Teile des Kopfes).
|
|
75
|
+
const sealAt = text.lastIndexOf(`<!-- ${DOSSIER_SEAL_MARKER} `);
|
|
76
|
+
const visible = sealAt === -1 ? text : text.slice(0, sealAt);
|
|
77
|
+
const missing = expected.filter((check) => countOf(visible, check.needle) !== 1 || (check.prefix !== undefined && countOf(visible, check.prefix) !== 1));
|
|
78
|
+
if (missing.length > 0) {
|
|
79
|
+
return {
|
|
80
|
+
status: "MISMATCH",
|
|
81
|
+
kind: "dossier",
|
|
82
|
+
sha256: seal.sha256,
|
|
83
|
+
lines: [
|
|
84
|
+
`Der sichtbare Kopf des Dossiers weicht vom gesiegelten Kern ab (${missing.length} von ${expected.length} Angaben fehlen, sind verändert oder doppelt: Kopfzeilen, Datum, Version, Urteil, Score oder die Hinweise unter dem Urteil wurden im Text verändert).`,
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (options.expect !== undefined && options.expect.toLowerCase() !== seal.sha256) {
|
|
89
|
+
return {
|
|
90
|
+
status: "MISMATCH",
|
|
91
|
+
kind: "dossier",
|
|
92
|
+
sha256: seal.sha256,
|
|
93
|
+
lines: [`Das Dossier ist in sich stimmig, seine Prüfsumme ist aber nicht die erwartete: erwartet ${options.expect.toLowerCase()}, im Dossier ${seal.sha256}.`],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const source = seal.core.reportFile;
|
|
97
|
+
if (source.check !== "ok") {
|
|
98
|
+
lines.push(source.check === "mismatch"
|
|
99
|
+
? "Das Dossier ist unverändert, wurde aber aus einer Report-Datei erzeugt, die ihre eigene Prüfsumme nicht bestand: Urteil und Score sind nicht belastbar."
|
|
100
|
+
: "Das Dossier ist unverändert, wurde aber aus einer Report-Datei erzeugt, deren Prüfsumme nicht prüfbar war (fehlend oder aus Harness < 0.11.1): Das Urteil war schon beim Erzeugen ungeprüft.");
|
|
101
|
+
return { status: source.check === "mismatch" ? "MISMATCH" : "NOT_VERIFIABLE", kind: "dossier", sha256: seal.sha256, lines };
|
|
102
|
+
}
|
|
103
|
+
lines.push("Siegel, Kern und sichtbarer Kopf (Titelzeile, alle Kopfzeilen, Urteil, Score und die Hinweise unter dem Urteil) stimmen überein. Der Fliesstext der Abschnitte 1 bis 6 ist nicht gesiegelt und wurde nicht geprüft.");
|
|
104
|
+
if (options.report) {
|
|
105
|
+
const reportCheck = checkReportIntegrity(options.report);
|
|
106
|
+
if (reportCheck.status !== "ok") {
|
|
107
|
+
return { status: reportCheck.status === "mismatch" ? "MISMATCH" : "NOT_VERIFIABLE", kind: "dossier", sha256: seal.sha256, lines: [`Die angegebene Report-Datei besteht ihre Prüfung nicht: ${reportCheck.reasonDe}`] };
|
|
108
|
+
}
|
|
109
|
+
if (options.report.integrity?.payloadSha256 !== source.payloadSha256) {
|
|
110
|
+
return {
|
|
111
|
+
status: "MISMATCH",
|
|
112
|
+
kind: "dossier",
|
|
113
|
+
sha256: seal.sha256,
|
|
114
|
+
lines: ["Die angegebene Report-Datei ist nicht die, aus der das Dossier erzeugt wurde (ihre Prüfsumme weicht von der im Dossier festgehaltenen ab)."],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
lines.push("Die angegebene Report-Datei gehört zu diesem Dossier und ist unverändert. Der Fliesstext des Dossiers wurde dabei nicht gegen die Report-Datei abgeglichen.");
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
lines.push("Die Report-Datei wurde nicht mitgeprüft; mit --report <datei> abgleichen.");
|
|
121
|
+
}
|
|
122
|
+
lines.push("Das ist eine Prüfsumme ohne Signatur eines Dritten: Sie schützt nur, wenn ihr Wert getrennt aufbewahrt wird (--expect <sha256>).");
|
|
123
|
+
return { status: "OK", kind: "dossier", sha256: seal.sha256, lines };
|
|
124
|
+
}
|
|
125
|
+
async function readText(path) {
|
|
126
|
+
const info = await stat(path).catch(() => null);
|
|
127
|
+
if (!info || !info.isFile())
|
|
128
|
+
return { error: `Datei nicht gefunden: ${path}` };
|
|
129
|
+
if (info.size > MAX_BYTES)
|
|
130
|
+
return { error: `Datei zu gross (${info.size} Byte, höchstens ${MAX_BYTES}): ${path}` };
|
|
131
|
+
return { text: await readFile(path, "utf8") };
|
|
132
|
+
}
|
|
133
|
+
function looksLikeReportFile(value) {
|
|
134
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && ("harnessVersion" in value || "report" in value || "integrity" in value);
|
|
135
|
+
}
|
|
136
|
+
export async function runVerifyMode(argv) {
|
|
137
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
138
|
+
process.stdout.write(HELP);
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
let file;
|
|
142
|
+
let reportPath;
|
|
143
|
+
let expect;
|
|
144
|
+
for (let i = 0; i < argv.length; i++) {
|
|
145
|
+
const token = argv[i];
|
|
146
|
+
const eq = token.indexOf("=");
|
|
147
|
+
const name = token.startsWith("--") && eq !== -1 ? token.slice(0, eq) : token;
|
|
148
|
+
if (name === "--report" || name === "--expect") {
|
|
149
|
+
const v = token.startsWith("--") && eq !== -1 ? token.slice(eq + 1) : argv[++i];
|
|
150
|
+
if (v === undefined || v.trim() === "" || v.startsWith("--")) {
|
|
151
|
+
process.stderr.write(`Error: ${name} needs a value.\n${USAGE}`);
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
if (name === "--report")
|
|
155
|
+
reportPath = v;
|
|
156
|
+
else {
|
|
157
|
+
if (!/^[0-9a-fA-F]{64}$/.test(v.trim())) {
|
|
158
|
+
process.stderr.write(`Error: --expect needs a SHA-256 (64 hex characters).\n${USAGE}`);
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
expect = v.trim();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if (token.startsWith("--")) {
|
|
165
|
+
process.stderr.write(`Error: unknown option ${name}\n${USAGE}`);
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
else if (file === undefined) {
|
|
169
|
+
file = token;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
process.stderr.write(`Error: unexpected argument ${token}\n${USAGE}`);
|
|
173
|
+
return 1;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (file === undefined) {
|
|
177
|
+
process.stderr.write(`Error: verify needs a dossier or report file.\n${USAGE}`);
|
|
178
|
+
return 1;
|
|
179
|
+
}
|
|
180
|
+
const read = await readText(file);
|
|
181
|
+
if ("error" in read) {
|
|
182
|
+
process.stderr.write(`Error: ${read.error}\n`);
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
let reportFile;
|
|
186
|
+
if (reportPath !== undefined) {
|
|
187
|
+
const r = await readText(reportPath);
|
|
188
|
+
if ("error" in r) {
|
|
189
|
+
process.stderr.write(`Error: ${r.error}\n`);
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(r.text);
|
|
194
|
+
if (!looksLikeReportFile(parsed))
|
|
195
|
+
throw new Error("not a report");
|
|
196
|
+
reportFile = parsed;
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
process.stderr.write(`Error: ${reportPath} is not a report file written by the proxy (--report-file).\n`);
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
let result;
|
|
204
|
+
let parsed;
|
|
205
|
+
let isJson = true;
|
|
206
|
+
try {
|
|
207
|
+
parsed = JSON.parse(read.text);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
isJson = false;
|
|
211
|
+
}
|
|
212
|
+
if (isJson) {
|
|
213
|
+
if (!looksLikeReportFile(parsed)) {
|
|
214
|
+
process.stderr.write(`Error: ${file} is JSON, but not a report written by the proxy (--report-file).\n`);
|
|
215
|
+
return 1;
|
|
216
|
+
}
|
|
217
|
+
if (reportFile !== undefined) {
|
|
218
|
+
process.stderr.write("Error: --report only applies when verifying a dossier.\n");
|
|
219
|
+
return 1;
|
|
220
|
+
}
|
|
221
|
+
result = verifyReportFileContent(parsed, expect);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
result = verifyDossierContent(read.text, { report: reportFile, expect });
|
|
225
|
+
}
|
|
226
|
+
const label = result.status === "NOT_VERIFIABLE" ? "NICHT PRÜFBAR" : result.status;
|
|
227
|
+
const what = result.kind === "report" ? "Report-Datei" : "Dossier";
|
|
228
|
+
process.stdout.write(`${label} ${what} ${file}\n`);
|
|
229
|
+
for (const line of result.lines)
|
|
230
|
+
process.stdout.write(`${line}\n`);
|
|
231
|
+
if (result.sha256)
|
|
232
|
+
process.stdout.write(`Prüfsumme (SHA-256): ${result.sha256}\n`);
|
|
233
|
+
return result.status === "OK" ? 0 : 1;
|
|
234
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cuonztech-agent-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"mcpName": "io.github.cuonztech/agent-harness",
|
|
5
5
|
"description": "Chaos-Proxy für MCP-Server: injiziert F1-F5-Fehlerszenarien (Ghost-Write nach Timeout, 429, defektes JSON, Duplicate Dispatch, 500) vor einem echten Upstream-MCP-Server und prüft, ob ein Agent Schreibvorgänge doppelt ausführt. Mit Resilience Score und System-Prompt-Patches für beobachtetes Fehlverhalten.",
|
|
6
6
|
"type": "module",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/cuonztech/agent-harness",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.11.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "cuonztech-agent-harness",
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.11.1",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|