codex-plugin-doctor 1.59.0 → 1.61.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 +40 -19
- package/dist/core/bounded-http-client.js +1 -0
- package/dist/core/mcp-discovery.d.ts +23 -0
- package/dist/core/mcp-discovery.js +148 -0
- package/dist/core/output-contract.js +62 -0
- package/dist/core/shell-completion.js +23 -0
- package/dist/core/submission-archive-preflight.d.ts +24 -0
- package/dist/core/submission-archive-preflight.js +259 -0
- package/dist/core/submission-archive-reader.d.ts +21 -0
- package/dist/core/submission-archive-reader.js +624 -0
- package/dist/core/submission-archive-ruleset.d.ts +58 -0
- package/dist/core/submission-archive-ruleset.js +61 -0
- package/dist/core/submission-assets.d.ts +3 -1
- package/dist/core/submission-assets.js +52 -31
- package/dist/core/submission-package-reader.d.ts +15 -0
- package/dist/core/submission-package-reader.js +274 -0
- package/dist/core/submission-preflight.d.ts +2 -0
- package/dist/core/submission-preflight.js +2 -2
- package/dist/core/submission-skill-metadata.d.ts +5 -3
- package/dist/core/submission-skill-metadata.js +117 -136
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/reporting/render-mcp-discovery-report.d.ts +2 -0
- package/dist/reporting/render-mcp-discovery-report.js +14 -0
- package/dist/reporting/render-submission-archive-report.d.ts +4 -0
- package/dist/reporting/render-submission-archive-report.js +52 -0
- package/dist/run-cli.d.ts +2 -0
- package/dist/run-cli.js +139 -1
- package/package.json +5 -2
package/dist/run-cli.js
CHANGED
|
@@ -19,7 +19,10 @@ import { buildDoctorRecommendations, renderDoctorRecommendations, renderDoctorRe
|
|
|
19
19
|
import { buildDoctorExportBundle, renderDoctorExportBundle, renderDoctorExportBundleJson } from "./core/doctor-export-bundle.js";
|
|
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
|
+
import { buildSubmissionArchivePreflight, submissionArchiveExitCode } from "./core/submission-archive-preflight.js";
|
|
22
23
|
import { buildSubmissionPreflight } from "./core/submission-preflight.js";
|
|
24
|
+
import { discoverMcpServer } from "./core/mcp-discovery.js";
|
|
25
|
+
import { renderMcpDiscoveryReport } from "./reporting/render-mcp-discovery-report.js";
|
|
23
26
|
import { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
|
|
24
27
|
import { buildExternalValidationCorpusReport, ExternalCorpusManifestError } from "./core/external-validation-corpus.js";
|
|
25
28
|
import { buildCorpusQualityMetricsDiffReport, buildCorpusQualityMetricsReport, CorpusMetricsDiffError, CorpusMetricsManifestError, renderCorpusQualityMetricsDiffJson, renderCorpusQualityMetricsDiffMarkdown, renderCorpusQualityMetricsDiffText, renderCorpusQualityMetricsJson, renderCorpusQualityMetricsMarkdown, renderCorpusQualityMetricsText } from "./core/corpus-quality-metrics.js";
|
|
@@ -58,6 +61,7 @@ import { renderSuppressionList, renderSuppressionListJson, renderSuppressionMuta
|
|
|
58
61
|
import { renderRuleExplanation } from "./reporting/render-rule-explanation.js";
|
|
59
62
|
import { renderSarifReport } from "./reporting/render-sarif-report.js";
|
|
60
63
|
import { renderTextReport } from "./reporting/render-text-report.js";
|
|
64
|
+
import { renderSubmissionArchiveJson, renderSubmissionArchiveMarkdown, renderSubmissionArchiveText } from "./reporting/render-submission-archive-report.js";
|
|
61
65
|
import { renderSubmissionPreflightJson, renderSubmissionPreflightMarkdown, renderSubmissionPreflightText, submissionPreflightExitCode } from "./reporting/render-submission-report.js";
|
|
62
66
|
import { applyPolicyToDepAudit, applyPolicyToDoctorConfig, applyPolicyToSecurityAudit, parsePolicyPack, policyEnablesRuntime, policyFailsOnWarnings, policyPackNames } from "./policy/policy-packs.js";
|
|
63
67
|
import { findRuleDefinition } from "./rules/rule-catalog.js";
|
|
@@ -97,6 +101,7 @@ function writeExactStdout(io, message) {
|
|
|
97
101
|
}
|
|
98
102
|
class CliUsageError extends Error {
|
|
99
103
|
}
|
|
104
|
+
const discoveryUsage = "Usage: codex-plugin-doctor doctor discover <url> --allow-network [--allow-local-network] [--json]";
|
|
100
105
|
function parseRuntimeSandbox(flags, options = { requireRuntime: true }) {
|
|
101
106
|
const index = flags.indexOf("--sandbox");
|
|
102
107
|
if (index === -1) {
|
|
@@ -179,6 +184,7 @@ function parseRemoteNetworkFlags(flags, runtime) {
|
|
|
179
184
|
}
|
|
180
185
|
function printUsage(io) {
|
|
181
186
|
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 [--allow-network [--allow-local-network]]] [--sandbox docker] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>] [--fail-on <rule-id>]\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> [--runtime [--allow-network [--allow-local-network]]] [--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> [--sandbox docker] [--json|--markdown] [--output <path>]|runtime-policy <path> [--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 [--allow-network [--allow-local-network]]] [--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 [--runtime [--allow-network [--allow-local-network]]] [--sandbox docker] [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>] [--upload]|mcp <path> [--runtime [--allow-network [--allow-local-network]]]|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 [--allow-network [--allow-local-network]]] [--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");
|
|
187
|
+
io.writeStderr(discoveryUsage);
|
|
182
188
|
io.writeStderr("Registry readiness: codex-plugin-doctor registry check <server.json|directory> [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
183
189
|
+ " codex-plugin-doctor registry inspect <server-name> --allow-network [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
184
190
|
+ "Registry publication preflight: codex-plugin-doctor registry preflight <server.json|directory> [--allow-network] [--json] [--output <path>] [--require-publish-ready]");
|
|
@@ -186,7 +192,8 @@ function printUsage(io) {
|
|
|
186
192
|
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]");
|
|
187
193
|
io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
|
|
188
194
|
io.writeStderr("Remote MCP runtime flags (check, mcp, release check, and doctor release-evidence): --runtime --allow-network [--allow-local-network] [--allow-session-lifecycle] [--require-remote-reliability]\n --allow-session-lifecycle requires --runtime --allow-network and can terminate a remote session.\n --require-remote-reliability requires --runtime --allow-network, blocks non-pass reliability, and does not grant network consent.");
|
|
189
|
-
io.writeStderr(" codex-plugin-doctor doctor submission <path> [--json|--markdown] [--output <path>] [--require-ready]"
|
|
195
|
+
io.writeStderr(" codex-plugin-doctor doctor submission <path> [--json|--markdown] [--output <path>] [--require-ready]"
|
|
196
|
+
+ "\n codex-plugin-doctor doctor submission archive <zip> [--json|--markdown] [--output <path>] [--require-ready]");
|
|
190
197
|
}
|
|
191
198
|
const suppressUsageText = [
|
|
192
199
|
"Usage:",
|
|
@@ -1048,6 +1055,90 @@ function parseSubmissionCommandArgs(args) {
|
|
|
1048
1055
|
return new CliUsageError("Use either --json or --markdown, not both.");
|
|
1049
1056
|
return { targetPath, jsonOutput, markdownOutput, outputPath, requireReady };
|
|
1050
1057
|
}
|
|
1058
|
+
function parseSubmissionArchiveCommandArgs(args) {
|
|
1059
|
+
let targetPath = null;
|
|
1060
|
+
let jsonOutput = false;
|
|
1061
|
+
let markdownOutput = false;
|
|
1062
|
+
let outputPath = null;
|
|
1063
|
+
let requireReady = false;
|
|
1064
|
+
let optionsEnded = false;
|
|
1065
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1066
|
+
const argument = args[index];
|
|
1067
|
+
if (argument === "--" && !optionsEnded)
|
|
1068
|
+
optionsEnded = true;
|
|
1069
|
+
else if (optionsEnded) {
|
|
1070
|
+
if (targetPath === null)
|
|
1071
|
+
targetPath = argument;
|
|
1072
|
+
else
|
|
1073
|
+
return new CliUsageError(`Unexpected submission archive argument: ${argument}.`);
|
|
1074
|
+
}
|
|
1075
|
+
else if (argument === "--json") {
|
|
1076
|
+
if (jsonOutput)
|
|
1077
|
+
return new CliUsageError("Duplicate submission archive flag: --json.");
|
|
1078
|
+
jsonOutput = true;
|
|
1079
|
+
}
|
|
1080
|
+
else if (argument === "--markdown") {
|
|
1081
|
+
if (markdownOutput)
|
|
1082
|
+
return new CliUsageError("Duplicate submission archive flag: --markdown.");
|
|
1083
|
+
markdownOutput = true;
|
|
1084
|
+
}
|
|
1085
|
+
else if (argument === "--require-ready") {
|
|
1086
|
+
if (requireReady)
|
|
1087
|
+
return new CliUsageError("Duplicate submission archive flag: --require-ready.");
|
|
1088
|
+
requireReady = true;
|
|
1089
|
+
}
|
|
1090
|
+
else if (argument === "--output" || argument.startsWith("--output=")) {
|
|
1091
|
+
if (outputPath !== null)
|
|
1092
|
+
return new CliUsageError("Duplicate submission archive flag: --output.");
|
|
1093
|
+
const value = argument === "--output" ? args[index + 1] : argument.slice("--output=".length);
|
|
1094
|
+
if (!value || (argument === "--output" && value.startsWith("--")))
|
|
1095
|
+
return new CliUsageError("Missing path after --output.");
|
|
1096
|
+
outputPath = value;
|
|
1097
|
+
if (argument === "--output")
|
|
1098
|
+
index += 1;
|
|
1099
|
+
}
|
|
1100
|
+
else if (argument.startsWith("--"))
|
|
1101
|
+
return new CliUsageError(`Unknown submission archive flag: ${argument}.`);
|
|
1102
|
+
else if (targetPath === null)
|
|
1103
|
+
targetPath = argument;
|
|
1104
|
+
else
|
|
1105
|
+
return new CliUsageError(`Unexpected submission archive argument: ${argument}.`);
|
|
1106
|
+
}
|
|
1107
|
+
if (targetPath === null)
|
|
1108
|
+
return new CliUsageError("Missing archive path for submission.");
|
|
1109
|
+
if (jsonOutput && markdownOutput)
|
|
1110
|
+
return new CliUsageError("Use either --json or --markdown, not both.");
|
|
1111
|
+
return { targetPath, jsonOutput, markdownOutput, outputPath, requireReady };
|
|
1112
|
+
}
|
|
1113
|
+
function hasArchiveSubcommandTarget(args) {
|
|
1114
|
+
let optionsEnded = false;
|
|
1115
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1116
|
+
const argument = args[index];
|
|
1117
|
+
if (optionsEnded)
|
|
1118
|
+
return true;
|
|
1119
|
+
if (argument === "--") {
|
|
1120
|
+
optionsEnded = true;
|
|
1121
|
+
}
|
|
1122
|
+
else if (argument === "--output") {
|
|
1123
|
+
index += 1;
|
|
1124
|
+
}
|
|
1125
|
+
else if (!argument.startsWith("--")) {
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
async function submissionTargetExists(targetPath) {
|
|
1132
|
+
try {
|
|
1133
|
+
const { stat } = await import("node:fs/promises");
|
|
1134
|
+
await stat(targetPath);
|
|
1135
|
+
return true;
|
|
1136
|
+
}
|
|
1137
|
+
catch (error) {
|
|
1138
|
+
return !(typeof error === "object" && error !== null && "code" in error
|
|
1139
|
+
&& error.code === "ENOENT");
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1051
1142
|
export async function runCli(args, io = defaultIo, options = {}) {
|
|
1052
1143
|
const [command, maybePath, ...remainingArgs] = args;
|
|
1053
1144
|
const runtimeSandboxSupported = (command === "check" &&
|
|
@@ -1104,6 +1195,34 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1104
1195
|
return 0;
|
|
1105
1196
|
}
|
|
1106
1197
|
if (command === "doctor") {
|
|
1198
|
+
if (maybePath === "discover") {
|
|
1199
|
+
if (remainingArgs.length === 1 && remainingArgs[0] === "--help") {
|
|
1200
|
+
io.writeStdout(`${discoveryUsage}\nHTTP discovery only; runtime behavior is not tested.`);
|
|
1201
|
+
return 0;
|
|
1202
|
+
}
|
|
1203
|
+
const [url, ...flags] = remainingArgs;
|
|
1204
|
+
if (!url || url.startsWith("-")) {
|
|
1205
|
+
io.writeStderr(discoveryUsage);
|
|
1206
|
+
return 2;
|
|
1207
|
+
}
|
|
1208
|
+
const allowedFlags = new Set(["--allow-network", "--allow-local-network", "--json"]);
|
|
1209
|
+
if (flags.some((flag) => !allowedFlags.has(flag)) || new Set(flags).size !== flags.length) {
|
|
1210
|
+
io.writeStderr(`Invalid or duplicate discovery arguments. ${discoveryUsage}`);
|
|
1211
|
+
return 2;
|
|
1212
|
+
}
|
|
1213
|
+
if (!flags.includes("--allow-network")) {
|
|
1214
|
+
io.writeStderr("doctor discover requires explicit --allow-network consent.");
|
|
1215
|
+
return 2;
|
|
1216
|
+
}
|
|
1217
|
+
const report = await (options.discoverMcpServerImpl ?? discoverMcpServer)(url, {
|
|
1218
|
+
allowNetwork: true,
|
|
1219
|
+
allowLocalNetwork: flags.includes("--allow-local-network")
|
|
1220
|
+
});
|
|
1221
|
+
io.writeStdout(flags.includes("--json")
|
|
1222
|
+
? JSON.stringify(report, null, 2)
|
|
1223
|
+
: renderMcpDiscoveryReport(report));
|
|
1224
|
+
return report.status === "discovered" ? 0 : report.status === "unsupported" ? 1 : 2;
|
|
1225
|
+
}
|
|
1107
1226
|
const doctorFlags = maybePath?.startsWith("--")
|
|
1108
1227
|
? [maybePath, ...remainingArgs]
|
|
1109
1228
|
: remainingArgs;
|
|
@@ -1158,6 +1277,25 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1158
1277
|
return 0;
|
|
1159
1278
|
}
|
|
1160
1279
|
if (maybePath === "submission") {
|
|
1280
|
+
if (remainingArgs[0] === "archive") {
|
|
1281
|
+
const useLegacyDirectoryTarget = !hasArchiveSubcommandTarget(remainingArgs.slice(1))
|
|
1282
|
+
&& await submissionTargetExists("archive");
|
|
1283
|
+
if (!useLegacyDirectoryTarget) {
|
|
1284
|
+
const parsedArchiveArgs = parseSubmissionArchiveCommandArgs(remainingArgs.slice(1));
|
|
1285
|
+
if (parsedArchiveArgs instanceof CliUsageError) {
|
|
1286
|
+
io.writeStderr(parsedArchiveArgs.message);
|
|
1287
|
+
return 2;
|
|
1288
|
+
}
|
|
1289
|
+
const report = await buildSubmissionArchivePreflight(parsedArchiveArgs.targetPath);
|
|
1290
|
+
const renderedReport = parsedArchiveArgs.jsonOutput
|
|
1291
|
+
? renderSubmissionArchiveJson(report)
|
|
1292
|
+
: parsedArchiveArgs.markdownOutput ? renderSubmissionArchiveMarkdown(report) : renderSubmissionArchiveText(report);
|
|
1293
|
+
if (parsedArchiveArgs.outputPath)
|
|
1294
|
+
await writeFile(parsedArchiveArgs.outputPath, renderedReport, "utf8");
|
|
1295
|
+
writeExactStdout(io, renderedReport);
|
|
1296
|
+
return submissionArchiveExitCode(report, parsedArchiveArgs.requireReady);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1161
1299
|
const parsedSubmissionArgs = parseSubmissionCommandArgs(remainingArgs);
|
|
1162
1300
|
if (parsedSubmissionArgs instanceof CliUsageError) {
|
|
1163
1301
|
io.writeStderr(parsedSubmissionArgs.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-plugin-doctor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.61.0",
|
|
4
4
|
"description": "CLI-first validator for Codex plugins, skills, and MCP package surfaces with runtime MCP protocol validation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -57,12 +57,15 @@
|
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/node": "^24.2.1",
|
|
60
|
+
"@types/yauzl": "^3.4.0",
|
|
60
61
|
"tsx": "^4.20.4",
|
|
61
62
|
"typescript": "^5.9.2",
|
|
62
63
|
"vitest": "^3.2.4"
|
|
63
64
|
},
|
|
64
65
|
"dependencies": {
|
|
65
66
|
"fast-xml-parser": "^5.11.0",
|
|
66
|
-
"
|
|
67
|
+
"iconv-lite": "^0.7.3",
|
|
68
|
+
"yaml": "^2.9.0",
|
|
69
|
+
"yauzl": "^3.4.0"
|
|
67
70
|
}
|
|
68
71
|
}
|