codex-plugin-doctor 1.46.1 → 1.48.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/README.md +27 -14
- package/dist/core/attestation.d.ts +1 -0
- package/dist/core/attestation.js +1 -1
- package/dist/core/corpus-quality-metrics.d.ts +125 -0
- package/dist/core/corpus-quality-metrics.js +433 -0
- package/dist/core/external-validation-corpus.d.ts +88 -0
- package/dist/core/external-validation-corpus.js +271 -0
- package/dist/core/init-git-hooks.js +43 -4
- package/dist/core/output-contract.js +25 -2
- package/dist/core/release-check.d.ts +2 -1
- package/dist/core/release-check.js +2 -1
- package/dist/core/release-evidence.d.ts +5 -2
- package/dist/core/release-evidence.js +12 -2
- package/dist/core/runtime-plan.d.ts +5 -1
- package/dist/core/runtime-plan.js +13 -1
- package/dist/core/runtime-policy.d.ts +5 -1
- package/dist/core/runtime-policy.js +3 -2
- package/dist/core/runtime-probe.d.ts +2 -1
- package/dist/core/runtime-probe.js +100 -18
- package/dist/core/runtime-sandbox.d.ts +21 -0
- package/dist/core/runtime-sandbox.js +68 -0
- package/dist/core/validate-plugin.js +19 -8
- package/dist/core/validation-corpus.d.ts +4 -2
- package/dist/core/validation-corpus.js +12 -1
- package/dist/domain/types.d.ts +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/reporting/render-json-report.js +3 -0
- package/dist/reporting/render-markdown-report.js +3 -0
- package/dist/reporting/render-text-report.js +3 -0
- package/dist/run-cli.d.ts +2 -0
- package/dist/run-cli.js +214 -19
- package/package.json +1 -1
package/dist/run-cli.js
CHANGED
|
@@ -20,6 +20,8 @@ import { buildDoctorExportBundle, renderDoctorExportBundle, renderDoctorExportBu
|
|
|
20
20
|
import { buildDoctorAttestation, renderDoctorAttestation, renderDoctorAttestationJson, renderDoctorAttestationVerification, renderDoctorAttestationVerificationJson, verifyDoctorAttestation } from "./core/attestation.js";
|
|
21
21
|
import { buildDoctorOutputContract, renderDoctorOutputContract, renderDoctorOutputContractJson } from "./core/output-contract.js";
|
|
22
22
|
import { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
|
|
23
|
+
import { buildExternalValidationCorpusReport, ExternalCorpusManifestError } from "./core/external-validation-corpus.js";
|
|
24
|
+
import { buildCorpusQualityMetricsReport, CorpusMetricsManifestError, renderCorpusQualityMetricsJson, renderCorpusQualityMetricsMarkdown, renderCorpusQualityMetricsText } from "./core/corpus-quality-metrics.js";
|
|
23
25
|
import { buildDoctorPerformanceReport, renderDoctorPerformanceReport, renderDoctorPerformanceReportJson } from "./core/performance-report.js";
|
|
24
26
|
import { buildDoctorRuntimePlan, evaluateRuntimeApproval, renderDoctorRuntimePlan, renderDoctorRuntimePlanMarkdown, renderDoctorRuntimePlanJson, runtimeApprovalPassed } from "./core/runtime-plan.js";
|
|
25
27
|
import { buildDoctorRuntimePolicyReport, renderDoctorRuntimePolicy, renderDoctorRuntimePolicyJson } from "./core/runtime-policy.js";
|
|
@@ -81,8 +83,24 @@ const defaultIo = {
|
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
};
|
|
86
|
+
class CliUsageError extends Error {
|
|
87
|
+
}
|
|
88
|
+
function parseRuntimeSandbox(flags, options = { requireRuntime: true }) {
|
|
89
|
+
const index = flags.indexOf("--sandbox");
|
|
90
|
+
if (index === -1) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const value = flags[index + 1];
|
|
94
|
+
if (value !== "docker") {
|
|
95
|
+
throw new CliUsageError("Expected --sandbox docker.");
|
|
96
|
+
}
|
|
97
|
+
if (options.requireRuntime !== false && !flags.includes("--runtime")) {
|
|
98
|
+
throw new CliUsageError("--sandbox docker requires --runtime.");
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
84
102
|
function printUsage(io) {
|
|
85
|
-
io.writeStderr("Usage: codex-plugin-doctor check <path|--installed> [filter] [--policy codex-publish|mcp-strict|security] [--compat] [--json|--markdown|--badge-json|--badge-markdown] [--output <path>] [--history <path>] [--runtime] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>]\n codex-plugin-doctor audit --installed [filter] [--policy codex-publish|mcp-strict|security] [--security] [--compat] [--json] [--output <path>] [--cache] [--changed]\n codex-plugin-doctor audit deps <path> [--policy codex-publish|mcp-strict|security] [--recommend] [--json|--sarif] [--output <path>]\n codex-plugin-doctor mcp <path> [--json] [--output <path>]\n codex-plugin-doctor security <path> [--policy security] [--json|--scorecard]\n codex-plugin-doctor compat <path> [--all|--client <client>] [--json] [--scorecard] [--output <path>] [--install-preview|--apply --backup]\n codex-plugin-doctor suppress add <path> [--fingerprint <sha256> --reason <text> --expires-at YYYY-MM-DD] [--config <path>] [--json]\n codex-plugin-doctor suppress list <path> [--config <path>] [--json]\n codex-plugin-doctor suppress remove <path> [--fingerprint <sha256>|--index <n>] [--config <path>] [--json]\n codex-plugin-doctor fix <path> (--dry-run|--interactive --backup|--apply --backup)\n codex-plugin-doctor history <history.jsonl> [--json] [--fail-on-regression]\n codex-plugin-doctor watch <path> [--runtime] [--json] [--output <path>] [--debounce-ms <ms>] [--max-iterations <n>] [--fail-fast] [--accumulate-json <path>]\n codex-plugin-doctor doctor [npm <package>|contract|corpus|runtime-plan <path> [--json|--markdown] [--output <path>]|runtime-policy <path> [--json] [--output <path>]|review-bundle <path> --output <dir> --sign-key-env NAME [--json] [--allow-dirty] [--allow-untagged]|review-bundle verify <bundle-dir> --target <path> --sign-key-env NAME [--json] [--output <path>] [--failures-only]|review-bundle diff --before <dir> --after <dir> [--json]|attest <path> [--sign-key-env NAME]|attest verify <attestation.json> --target <path> --sign-key-env NAME|release-evidence <path> --sign-key-env NAME [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>]|release-evidence verify <evidence.json> --target <path> --sign-key-env NAME|release-evidence asset <path> --tag <tag> --output <evidence.json> --sign-key-env NAME [--upload]|mcp <path>|inspector <path>|diff --before <path> --after <path>|recommend <path>|trust <path>|perf <path> [--max-total-ms <ms>] [--max-stage-ms stage=ms]|export --bundle <path>|snapshot|clients|--json|--update-check]\n codex-plugin-doctor init [path] [--template skill-only|mcp-stdio|mcp-http|full-runtime]\n codex-plugin-doctor init-ci [path]\n codex-plugin-doctor init-git-hooks [path] [--force] [--json]\n codex-plugin-doctor init-git-hooks [path] --remove [--json]\n codex-plugin-doctor completion bash|zsh|fish\n codex-plugin-doctor config validate <path> [--json]\n codex-plugin-doctor release check <path> [--json]\n codex-plugin-doctor self-test\n codex-plugin-doctor list --installed\n codex-plugin-doctor explain <finding-id>\n codex-plugin-doctor --version\n\nFirst run:\n codex-plugin-doctor doctor\n codex-plugin-doctor self-test\n codex-plugin-doctor init my-plugin\n codex-plugin-doctor check . --runtime --explain");
|
|
103
|
+
io.writeStderr("Usage: codex-plugin-doctor check <path|--installed> [filter] [--policy codex-publish|mcp-strict|security] [--compat] [--json|--markdown|--badge-json|--badge-markdown] [--output <path>] [--history <path>] [--runtime] [--sandbox docker] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>]\n codex-plugin-doctor audit --installed [filter] [--policy codex-publish|mcp-strict|security] [--security] [--compat] [--json] [--output <path>] [--cache] [--changed]\n codex-plugin-doctor audit deps <path> [--policy codex-publish|mcp-strict|security] [--recommend] [--json|--sarif] [--output <path>]\n codex-plugin-doctor mcp <path> [--json] [--output <path>]\n codex-plugin-doctor security <path> [--policy security] [--json|--scorecard]\n codex-plugin-doctor compat <path> [--all|--client <client>] [--json] [--scorecard] [--output <path>] [--install-preview|--apply --backup]\n codex-plugin-doctor suppress add <path> [--fingerprint <sha256> --reason <text> --expires-at YYYY-MM-DD] [--config <path>] [--json]\n codex-plugin-doctor suppress list <path> [--config <path>] [--json]\n codex-plugin-doctor suppress remove <path> [--fingerprint <sha256>|--index <n>] [--config <path>] [--json]\n codex-plugin-doctor fix <path> (--dry-run|--interactive --backup|--apply --backup)\n codex-plugin-doctor history <history.jsonl> [--json] [--fail-on-regression]\n codex-plugin-doctor watch <path> [--runtime] [--json] [--output <path>] [--debounce-ms <ms>] [--max-iterations <n>] [--fail-fast] [--accumulate-json <path>]\n codex-plugin-doctor doctor [npm <package>|contract|corpus [--manifest <corpus.json>] [--json] [--output <path>]|corpus metrics --manifest <corpus.json> [--json|--markdown] [--output <path>] [--min-precision <0..1>] [--min-recall <0..1>] [--max-false-positive-rate <0..1>]|runtime-plan <path> [--runtime --sandbox docker] [--json|--markdown] [--output <path>]|runtime-policy <path> [--runtime --sandbox docker] [--json] [--output <path>]|review-bundle <path> --output <dir> --sign-key-env NAME [--json] [--allow-dirty] [--allow-untagged]|review-bundle verify <bundle-dir> --target <path> --sign-key-env NAME [--json] [--output <path>] [--failures-only]|review-bundle diff --before <dir> --after <dir> [--json]|attest <path> [--sign-key-env NAME]|attest verify <attestation.json> --target <path> --sign-key-env NAME|release-evidence <path> --sign-key-env NAME [--runtime --sandbox docker] [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>]|release-evidence verify <evidence.json> --target <path> --sign-key-env NAME|release-evidence asset <path> --tag <tag> --output <evidence.json> --sign-key-env NAME [--upload]|mcp <path>|inspector <path>|diff --before <path> --after <path>|recommend <path>|trust <path>|perf <path> [--max-total-ms <ms>] [--max-stage-ms stage=ms]|export --bundle <path>|snapshot|clients|--json|--update-check]\n codex-plugin-doctor init [path] [--template skill-only|mcp-stdio|mcp-http|full-runtime]\n codex-plugin-doctor init-ci [path]\n codex-plugin-doctor init-git-hooks [path] [--force] [--json]\n codex-plugin-doctor init-git-hooks [path] --remove [--json]\n codex-plugin-doctor completion bash|zsh|fish\n codex-plugin-doctor config validate <path> [--json]\n codex-plugin-doctor release check <path> [--json] [--runtime --sandbox docker]\n codex-plugin-doctor self-test\n codex-plugin-doctor list --installed\n codex-plugin-doctor explain <finding-id>\n codex-plugin-doctor --version\n\nFirst run:\n codex-plugin-doctor doctor\n codex-plugin-doctor self-test\n codex-plugin-doctor init my-plugin\n codex-plugin-doctor check . --runtime --explain");
|
|
86
104
|
io.writeStderr("Suppression governance: codex-plugin-doctor suppress list <path> [--fail-on-expired] [--fail-on-invalid] [--warn-expiring-within-days <days>]\n codex-plugin-doctor suppress prune <path> [--apply] [--json]");
|
|
87
105
|
io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
|
|
88
106
|
}
|
|
@@ -805,6 +823,17 @@ function parseSelectedFixActionIndexes(answer, actionCount) {
|
|
|
805
823
|
}
|
|
806
824
|
export async function runCli(args, io = defaultIo, options = {}) {
|
|
807
825
|
const [command, maybePath, ...remainingArgs] = args;
|
|
826
|
+
const runtimeSandboxSupported = (command === "check" &&
|
|
827
|
+
!args.includes("--installed") &&
|
|
828
|
+
!args.includes("--changed-since")) ||
|
|
829
|
+
(command === "release" && maybePath === "check") ||
|
|
830
|
+
(command === "doctor" &&
|
|
831
|
+
(["runtime-plan", "runtime-policy"].includes(maybePath ?? "") ||
|
|
832
|
+
(maybePath === "release-evidence" && remainingArgs[0] !== "verify")));
|
|
833
|
+
if (args.includes("--sandbox") && !runtimeSandboxSupported) {
|
|
834
|
+
io.writeStderr("--sandbox is supported only by single-package check, release check, runtime-plan, runtime-policy, and release-evidence.");
|
|
835
|
+
return 2;
|
|
836
|
+
}
|
|
808
837
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
809
838
|
io.writeStdout(packageVersion);
|
|
810
839
|
return 0;
|
|
@@ -933,6 +962,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
933
962
|
const markdownOutput = runtimePlanFlags.includes("--markdown");
|
|
934
963
|
const outputIndex = runtimePlanFlags.indexOf("--output");
|
|
935
964
|
const outputPath = outputIndex === -1 ? null : runtimePlanFlags[outputIndex + 1];
|
|
965
|
+
let runtimeSandbox;
|
|
966
|
+
try {
|
|
967
|
+
runtimeSandbox = parseRuntimeSandbox(runtimePlanFlags, { requireRuntime: false });
|
|
968
|
+
}
|
|
969
|
+
catch (error) {
|
|
970
|
+
io.writeStderr(error.message);
|
|
971
|
+
return 2;
|
|
972
|
+
}
|
|
936
973
|
if (!targetPath) {
|
|
937
974
|
io.writeStderr("Missing target path for runtime plan.");
|
|
938
975
|
return 2;
|
|
@@ -945,7 +982,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
945
982
|
io.writeStderr("Use either --json or --markdown, not both.");
|
|
946
983
|
return 2;
|
|
947
984
|
}
|
|
948
|
-
const plan = await buildDoctorRuntimePlan(targetPath);
|
|
985
|
+
const plan = await buildDoctorRuntimePlan(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
|
|
949
986
|
const renderedPlan = jsonOutput
|
|
950
987
|
? renderDoctorRuntimePlanJson(plan)
|
|
951
988
|
: markdownOutput
|
|
@@ -965,6 +1002,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
965
1002
|
const jsonOutput = runtimePolicyFlags.includes("--json");
|
|
966
1003
|
const outputIndex = runtimePolicyFlags.indexOf("--output");
|
|
967
1004
|
const outputPath = outputIndex === -1 ? null : runtimePolicyFlags[outputIndex + 1];
|
|
1005
|
+
let runtimeSandbox;
|
|
1006
|
+
try {
|
|
1007
|
+
runtimeSandbox = parseRuntimeSandbox(runtimePolicyFlags, { requireRuntime: false });
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
io.writeStderr(error.message);
|
|
1011
|
+
return 2;
|
|
1012
|
+
}
|
|
968
1013
|
if (!targetPath) {
|
|
969
1014
|
io.writeStderr("Missing target path for runtime policy.");
|
|
970
1015
|
return 2;
|
|
@@ -973,7 +1018,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
973
1018
|
io.writeStderr("Missing path after --output.");
|
|
974
1019
|
return 2;
|
|
975
1020
|
}
|
|
976
|
-
const report = await buildDoctorRuntimePolicyReport(targetPath);
|
|
1021
|
+
const report = await buildDoctorRuntimePolicyReport(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
|
|
977
1022
|
const renderedReport = jsonOutput
|
|
978
1023
|
? renderDoctorRuntimePolicyJson(report)
|
|
979
1024
|
: renderDoctorRuntimePolicy(report);
|
|
@@ -1138,6 +1183,15 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1138
1183
|
const runtimeApprovalDigest = runtimeApprovalDigestIndex === -1
|
|
1139
1184
|
? null
|
|
1140
1185
|
: assetFlags[runtimeApprovalDigestIndex + 1];
|
|
1186
|
+
const runtime = assetFlags.includes("--runtime");
|
|
1187
|
+
let runtimeSandbox;
|
|
1188
|
+
try {
|
|
1189
|
+
runtimeSandbox = parseRuntimeSandbox(assetFlags);
|
|
1190
|
+
}
|
|
1191
|
+
catch (error) {
|
|
1192
|
+
io.writeStderr(error.message);
|
|
1193
|
+
return 2;
|
|
1194
|
+
}
|
|
1141
1195
|
if (!targetPath) {
|
|
1142
1196
|
io.writeStderr("Missing target path for release evidence asset.");
|
|
1143
1197
|
return 2;
|
|
@@ -1193,13 +1247,13 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1193
1247
|
allowUntagged,
|
|
1194
1248
|
requireRuntimeApproval,
|
|
1195
1249
|
runtimeApprovalDigest,
|
|
1250
|
+
runtime,
|
|
1251
|
+
...(runtimeSandbox ? { sandbox: runtimeSandbox } : {}),
|
|
1196
1252
|
environment: {
|
|
1197
1253
|
env: terminalContext.env,
|
|
1198
1254
|
platform: terminalContext.platform
|
|
1199
1255
|
},
|
|
1200
|
-
runCheck: options.runCheckImpl
|
|
1201
|
-
? (pathToCheck) => options.runCheckImpl(pathToCheck)
|
|
1202
|
-
: undefined,
|
|
1256
|
+
runCheck: options.runCheckImpl ?? runCheck,
|
|
1203
1257
|
performanceThresholds: parsedThresholds.thresholds
|
|
1204
1258
|
});
|
|
1205
1259
|
await writeFile(resolvedOutputPath, renderDoctorReleaseEvidenceJson(evidence), "utf8");
|
|
@@ -1298,6 +1352,15 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1298
1352
|
const runtimeApprovalDigest = runtimeApprovalDigestIndex === -1
|
|
1299
1353
|
? null
|
|
1300
1354
|
: evidenceFlags[runtimeApprovalDigestIndex + 1];
|
|
1355
|
+
const runtime = evidenceFlags.includes("--runtime");
|
|
1356
|
+
let runtimeSandbox;
|
|
1357
|
+
try {
|
|
1358
|
+
runtimeSandbox = parseRuntimeSandbox(evidenceFlags);
|
|
1359
|
+
}
|
|
1360
|
+
catch (error) {
|
|
1361
|
+
io.writeStderr(error.message);
|
|
1362
|
+
return 2;
|
|
1363
|
+
}
|
|
1301
1364
|
if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
|
|
1302
1365
|
io.writeStderr("Missing path after --output.");
|
|
1303
1366
|
return 2;
|
|
@@ -1336,13 +1399,13 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1336
1399
|
allowUntagged,
|
|
1337
1400
|
requireRuntimeApproval,
|
|
1338
1401
|
runtimeApprovalDigest,
|
|
1402
|
+
runtime,
|
|
1403
|
+
...(runtimeSandbox ? { sandbox: runtimeSandbox } : {}),
|
|
1339
1404
|
environment: {
|
|
1340
1405
|
env: terminalContext.env,
|
|
1341
1406
|
platform: terminalContext.platform
|
|
1342
1407
|
},
|
|
1343
|
-
runCheck: options.runCheckImpl
|
|
1344
|
-
? (pathToCheck) => options.runCheckImpl(pathToCheck)
|
|
1345
|
-
: undefined,
|
|
1408
|
+
runCheck: options.runCheckImpl ?? runCheck,
|
|
1346
1409
|
performanceThresholds: parsedThresholds.thresholds
|
|
1347
1410
|
});
|
|
1348
1411
|
const reportJson = renderDoctorReleaseEvidenceJson(report);
|
|
@@ -1354,19 +1417,133 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1354
1417
|
return report.exitCode;
|
|
1355
1418
|
}
|
|
1356
1419
|
if (maybePath === "corpus") {
|
|
1420
|
+
if (remainingArgs[0] === "metrics") {
|
|
1421
|
+
const metricsArgs = remainingArgs.slice(1);
|
|
1422
|
+
const jsonOutput = metricsArgs.includes("--json");
|
|
1423
|
+
const markdownOutput = metricsArgs.includes("--markdown");
|
|
1424
|
+
const valueOptions = new Map();
|
|
1425
|
+
const knownValueFlags = new Set([
|
|
1426
|
+
"--manifest",
|
|
1427
|
+
"--output",
|
|
1428
|
+
"--min-precision",
|
|
1429
|
+
"--min-recall",
|
|
1430
|
+
"--max-false-positive-rate"
|
|
1431
|
+
]);
|
|
1432
|
+
for (let index = 0; index < metricsArgs.length; index += 1) {
|
|
1433
|
+
const argument = metricsArgs[index];
|
|
1434
|
+
if (argument === "--json" || argument === "--markdown")
|
|
1435
|
+
continue;
|
|
1436
|
+
if (!knownValueFlags.has(argument)) {
|
|
1437
|
+
io.writeStderr(`Unknown corpus metrics argument: ${argument}.`);
|
|
1438
|
+
return 2;
|
|
1439
|
+
}
|
|
1440
|
+
const value = metricsArgs[index + 1];
|
|
1441
|
+
if (!value || value.startsWith("--")) {
|
|
1442
|
+
const label = argument === "--manifest"
|
|
1443
|
+
? "path after --manifest"
|
|
1444
|
+
: argument === "--output"
|
|
1445
|
+
? "path after --output"
|
|
1446
|
+
: `value after ${argument}`;
|
|
1447
|
+
io.writeStderr(`Missing ${label}.`);
|
|
1448
|
+
return 2;
|
|
1449
|
+
}
|
|
1450
|
+
valueOptions.set(argument, value);
|
|
1451
|
+
index += 1;
|
|
1452
|
+
}
|
|
1453
|
+
const manifestPath = valueOptions.get("--manifest");
|
|
1454
|
+
const outputPath = valueOptions.get("--output") ?? null;
|
|
1455
|
+
if (!manifestPath) {
|
|
1456
|
+
io.writeStderr("Corpus metrics requires --manifest <path>.");
|
|
1457
|
+
return 2;
|
|
1458
|
+
}
|
|
1459
|
+
if (jsonOutput && markdownOutput) {
|
|
1460
|
+
io.writeStderr("Use either --json or --markdown, not both.");
|
|
1461
|
+
return 2;
|
|
1462
|
+
}
|
|
1463
|
+
const thresholds = {};
|
|
1464
|
+
const thresholdFlags = [
|
|
1465
|
+
["--min-precision", "minPrecision"],
|
|
1466
|
+
["--min-recall", "minRecall"],
|
|
1467
|
+
["--max-false-positive-rate", "maxFalsePositiveRate"]
|
|
1468
|
+
];
|
|
1469
|
+
for (const [flag, key] of thresholdFlags) {
|
|
1470
|
+
const raw = valueOptions.get(flag);
|
|
1471
|
+
if (raw === undefined)
|
|
1472
|
+
continue;
|
|
1473
|
+
const value = Number(raw);
|
|
1474
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
1475
|
+
io.writeStderr(`${flag} must be between 0 and 1.`);
|
|
1476
|
+
return 2;
|
|
1477
|
+
}
|
|
1478
|
+
thresholds[key] = value;
|
|
1479
|
+
}
|
|
1480
|
+
let report;
|
|
1481
|
+
try {
|
|
1482
|
+
const builder = options.buildCorpusMetricsReportImpl ?? buildCorpusQualityMetricsReport;
|
|
1483
|
+
const buildOptions = {
|
|
1484
|
+
thresholds,
|
|
1485
|
+
environment: {
|
|
1486
|
+
env: terminalContext.env,
|
|
1487
|
+
platform: terminalContext.platform
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
report = await builder(manifestPath, buildOptions);
|
|
1491
|
+
}
|
|
1492
|
+
catch (error) {
|
|
1493
|
+
if (error instanceof CorpusMetricsManifestError) {
|
|
1494
|
+
io.writeStderr(error.message);
|
|
1495
|
+
return 2;
|
|
1496
|
+
}
|
|
1497
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1498
|
+
io.writeStderr(`Corpus metrics analysis failed: ${message}`);
|
|
1499
|
+
return 2;
|
|
1500
|
+
}
|
|
1501
|
+
const rendered = jsonOutput
|
|
1502
|
+
? renderCorpusQualityMetricsJson(report)
|
|
1503
|
+
: markdownOutput
|
|
1504
|
+
? renderCorpusQualityMetricsMarkdown(report)
|
|
1505
|
+
: renderCorpusQualityMetricsText(report, { outputPath });
|
|
1506
|
+
if (outputPath)
|
|
1507
|
+
await writeFile(outputPath, rendered, "utf8");
|
|
1508
|
+
io.writeStdout(rendered);
|
|
1509
|
+
return report.exitCode;
|
|
1510
|
+
}
|
|
1357
1511
|
const jsonOutput = remainingArgs.includes("--json");
|
|
1358
1512
|
const outputIndex = remainingArgs.indexOf("--output");
|
|
1359
1513
|
const outputPath = outputIndex === -1 ? null : remainingArgs[outputIndex + 1];
|
|
1514
|
+
const manifestIndex = remainingArgs.indexOf("--manifest");
|
|
1515
|
+
const manifestPath = manifestIndex === -1 ? null : remainingArgs[manifestIndex + 1];
|
|
1360
1516
|
if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
|
|
1361
1517
|
io.writeStderr("Missing path after --output.");
|
|
1362
1518
|
return 2;
|
|
1363
1519
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1520
|
+
if (manifestIndex !== -1 && (!manifestPath || manifestPath.startsWith("--"))) {
|
|
1521
|
+
io.writeStderr("Missing path after --manifest.");
|
|
1522
|
+
return 2;
|
|
1523
|
+
}
|
|
1524
|
+
let report;
|
|
1525
|
+
try {
|
|
1526
|
+
report = manifestPath
|
|
1527
|
+
? await buildExternalValidationCorpusReport(manifestPath, {
|
|
1528
|
+
environment: {
|
|
1529
|
+
env: terminalContext.env,
|
|
1530
|
+
platform: terminalContext.platform
|
|
1531
|
+
}
|
|
1532
|
+
})
|
|
1533
|
+
: await buildDoctorValidationCorpusReport({
|
|
1534
|
+
environment: {
|
|
1535
|
+
env: terminalContext.env,
|
|
1536
|
+
platform: terminalContext.platform
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
catch (error) {
|
|
1541
|
+
if (error instanceof ExternalCorpusManifestError) {
|
|
1542
|
+
io.writeStderr(error.message);
|
|
1543
|
+
return 2;
|
|
1368
1544
|
}
|
|
1369
|
-
|
|
1545
|
+
throw error;
|
|
1546
|
+
}
|
|
1370
1547
|
const reportJson = renderDoctorValidationCorpusJson(report);
|
|
1371
1548
|
if (outputPath) {
|
|
1372
1549
|
await writeFile(outputPath, reportJson, "utf8");
|
|
@@ -2262,6 +2439,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2262
2439
|
: remainingArgs.slice(1);
|
|
2263
2440
|
const jsonOutput = releaseFlags.includes("--json");
|
|
2264
2441
|
const runtimeProbeEnabled = releaseFlags.includes("--runtime");
|
|
2442
|
+
let runtimeSandbox;
|
|
2443
|
+
try {
|
|
2444
|
+
runtimeSandbox = parseRuntimeSandbox(releaseFlags);
|
|
2445
|
+
}
|
|
2446
|
+
catch (error) {
|
|
2447
|
+
io.writeStderr(error.message);
|
|
2448
|
+
return 2;
|
|
2449
|
+
}
|
|
2265
2450
|
const requireRuntimeApproval = releaseFlags.includes("--require-runtime-approval");
|
|
2266
2451
|
const runtimeApprovalDigestIndex = releaseFlags.indexOf("--runtime-approval-digest");
|
|
2267
2452
|
const runtimeApprovalDigest = runtimeApprovalDigestIndex === -1
|
|
@@ -2277,7 +2462,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2277
2462
|
return 2;
|
|
2278
2463
|
}
|
|
2279
2464
|
if (requireRuntimeApproval) {
|
|
2280
|
-
const runtimePlan = await buildDoctorRuntimePlan(targetPath);
|
|
2465
|
+
const runtimePlan = await buildDoctorRuntimePlan(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
|
|
2281
2466
|
const approval = evaluateRuntimeApproval(runtimePlan, {
|
|
2282
2467
|
required: true,
|
|
2283
2468
|
approvedDigest: runtimeApprovalDigest
|
|
@@ -2291,6 +2476,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2291
2476
|
env: terminalContext.env,
|
|
2292
2477
|
platform: terminalContext.platform,
|
|
2293
2478
|
runtime: runtimeProbeEnabled,
|
|
2479
|
+
...(runtimeSandbox ? { runtimeSandbox } : {}),
|
|
2294
2480
|
runCheck: options.runCheckImpl
|
|
2295
2481
|
});
|
|
2296
2482
|
const output = jsonOutput
|
|
@@ -2331,6 +2517,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2331
2517
|
: maybePath && maybePath.startsWith("--")
|
|
2332
2518
|
? [maybePath, ...remainingArgs]
|
|
2333
2519
|
: remainingArgs;
|
|
2520
|
+
let runtimeSandbox;
|
|
2521
|
+
try {
|
|
2522
|
+
runtimeSandbox = parseRuntimeSandbox(normalizedFlags);
|
|
2523
|
+
}
|
|
2524
|
+
catch (error) {
|
|
2525
|
+
io.writeStderr(error.message);
|
|
2526
|
+
return 2;
|
|
2527
|
+
}
|
|
2334
2528
|
const jsonOutput = normalizedFlags.includes("--json");
|
|
2335
2529
|
const markdownOutput = normalizedFlags.includes("--markdown");
|
|
2336
2530
|
const badgeJsonOutput = normalizedFlags.includes("--badge-json");
|
|
@@ -2458,7 +2652,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2458
2652
|
});
|
|
2459
2653
|
const runCheckImpl = options.runCheckImpl ?? runCheck;
|
|
2460
2654
|
if (!checkInstalled && effectiveRuntimeProbeEnabled && requireRuntimeApproval) {
|
|
2461
|
-
const runtimePlan = await buildDoctorRuntimePlan(targetPath);
|
|
2655
|
+
const runtimePlan = await buildDoctorRuntimePlan(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
|
|
2462
2656
|
const approval = evaluateRuntimeApproval(runtimePlan, {
|
|
2463
2657
|
required: true,
|
|
2464
2658
|
approvedDigest: runtimeApprovalDigest
|
|
@@ -2580,9 +2774,10 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2580
2774
|
const doctorConfig = applyPolicyToDoctorConfig(applyCheckProfile(await loadDoctorConfig(targetPath, configPath), checkProfile), policy);
|
|
2581
2775
|
const configuredResult = applyDoctorConfig(await runCheckImpl(targetPath, {
|
|
2582
2776
|
runtime: effectiveRuntimeProbeEnabled,
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
:
|
|
2777
|
+
...(runtimeSandbox ? { runtimeSandbox } : {}),
|
|
2778
|
+
...(effectiveRuntimeProbeEnabled && verboseRuntime
|
|
2779
|
+
? { runtimeTranscript: (line) => io.writeStderr(line) }
|
|
2780
|
+
: {})
|
|
2586
2781
|
}), doctorConfig);
|
|
2587
2782
|
const result = baseline
|
|
2588
2783
|
? applyValidationBaseline(configuredResult, baseline, {
|
package/package.json
CHANGED