codex-plugin-doctor 1.58.0 → 1.60.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/dist/run-cli.js CHANGED
@@ -19,6 +19,8 @@ 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";
23
+ import { buildSubmissionPreflight } from "./core/submission-preflight.js";
22
24
  import { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
23
25
  import { buildExternalValidationCorpusReport, ExternalCorpusManifestError } from "./core/external-validation-corpus.js";
24
26
  import { buildCorpusQualityMetricsDiffReport, buildCorpusQualityMetricsReport, CorpusMetricsDiffError, CorpusMetricsManifestError, renderCorpusQualityMetricsDiffJson, renderCorpusQualityMetricsDiffMarkdown, renderCorpusQualityMetricsDiffText, renderCorpusQualityMetricsJson, renderCorpusQualityMetricsMarkdown, renderCorpusQualityMetricsText } from "./core/corpus-quality-metrics.js";
@@ -57,6 +59,8 @@ import { renderSuppressionList, renderSuppressionListJson, renderSuppressionMuta
57
59
  import { renderRuleExplanation } from "./reporting/render-rule-explanation.js";
58
60
  import { renderSarifReport } from "./reporting/render-sarif-report.js";
59
61
  import { renderTextReport } from "./reporting/render-text-report.js";
62
+ import { renderSubmissionArchiveJson, renderSubmissionArchiveMarkdown, renderSubmissionArchiveText } from "./reporting/render-submission-archive-report.js";
63
+ import { renderSubmissionPreflightJson, renderSubmissionPreflightMarkdown, renderSubmissionPreflightText, submissionPreflightExitCode } from "./reporting/render-submission-report.js";
60
64
  import { applyPolicyToDepAudit, applyPolicyToDoctorConfig, applyPolicyToSecurityAudit, parsePolicyPack, policyEnablesRuntime, policyFailsOnWarnings, policyPackNames } from "./policy/policy-packs.js";
61
65
  import { findRuleDefinition } from "./rules/rule-catalog.js";
62
66
  import { buildSecurityAudit, renderSecurityAuditJson, renderSecurityScorecard } from "./security/security-audit.js";
@@ -86,6 +90,13 @@ const defaultIo = {
86
90
  }
87
91
  }
88
92
  };
93
+ function writeExactStdout(io, message) {
94
+ if (io === defaultIo) {
95
+ process.stdout.write(message);
96
+ return;
97
+ }
98
+ io.writeStdout(message);
99
+ }
89
100
  class CliUsageError extends Error {
90
101
  }
91
102
  function parseRuntimeSandbox(flags, options = { requireRuntime: true }) {
@@ -177,6 +188,8 @@ function printUsage(io) {
177
188
  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]");
178
189
  io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
179
190
  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.");
191
+ io.writeStderr(" codex-plugin-doctor doctor submission <path> [--json|--markdown] [--output <path>] [--require-ready]"
192
+ + "\n codex-plugin-doctor doctor submission archive <zip> [--json|--markdown] [--output <path>] [--require-ready]");
180
193
  }
181
194
  const suppressUsageText = [
182
195
  "Usage:",
@@ -970,6 +983,158 @@ function parseSelectedFixActionIndexes(answer, actionCount) {
970
983
  ? actionIndexes
971
984
  : null;
972
985
  }
986
+ function parseSubmissionCommandArgs(args) {
987
+ let targetPath = null;
988
+ let jsonOutput = false;
989
+ let markdownOutput = false;
990
+ let outputPath = null;
991
+ let requireReady = false;
992
+ let optionsEnded = false;
993
+ for (let index = 0; index < args.length; index += 1) {
994
+ const argument = args[index];
995
+ if (argument === "--" && !optionsEnded) {
996
+ optionsEnded = true;
997
+ }
998
+ else if (optionsEnded) {
999
+ if (targetPath === null) {
1000
+ targetPath = argument;
1001
+ }
1002
+ else {
1003
+ return new CliUsageError(`Unexpected submission argument: ${argument}.`);
1004
+ }
1005
+ }
1006
+ else if (argument === "--json") {
1007
+ if (jsonOutput)
1008
+ return new CliUsageError("Duplicate submission flag: --json.");
1009
+ jsonOutput = true;
1010
+ }
1011
+ else if (argument === "--markdown") {
1012
+ if (markdownOutput)
1013
+ return new CliUsageError("Duplicate submission flag: --markdown.");
1014
+ markdownOutput = true;
1015
+ }
1016
+ else if (argument === "--require-ready") {
1017
+ if (requireReady)
1018
+ return new CliUsageError("Duplicate submission flag: --require-ready.");
1019
+ requireReady = true;
1020
+ }
1021
+ else if (argument === "--output") {
1022
+ if (outputPath !== null)
1023
+ return new CliUsageError("Duplicate submission flag: --output.");
1024
+ const value = args[index + 1];
1025
+ if (!value || value.startsWith("--"))
1026
+ return new CliUsageError("Missing path after --output.");
1027
+ outputPath = value;
1028
+ index += 1;
1029
+ }
1030
+ else if (argument.startsWith("--output=")) {
1031
+ if (outputPath !== null)
1032
+ return new CliUsageError("Duplicate submission flag: --output.");
1033
+ const value = argument.slice("--output=".length);
1034
+ if (!value)
1035
+ return new CliUsageError("Missing path after --output.");
1036
+ outputPath = value;
1037
+ }
1038
+ else if (argument.startsWith("--")) {
1039
+ return new CliUsageError(`Unknown submission flag: ${argument}.`);
1040
+ }
1041
+ else if (targetPath === null) {
1042
+ targetPath = argument;
1043
+ }
1044
+ else {
1045
+ return new CliUsageError(`Unexpected submission argument: ${argument}.`);
1046
+ }
1047
+ }
1048
+ if (targetPath === null)
1049
+ return new CliUsageError("Missing target path for submission.");
1050
+ if (jsonOutput && markdownOutput)
1051
+ return new CliUsageError("Use either --json or --markdown, not both.");
1052
+ return { targetPath, jsonOutput, markdownOutput, outputPath, requireReady };
1053
+ }
1054
+ function parseSubmissionArchiveCommandArgs(args) {
1055
+ let targetPath = null;
1056
+ let jsonOutput = false;
1057
+ let markdownOutput = false;
1058
+ let outputPath = null;
1059
+ let requireReady = false;
1060
+ let optionsEnded = false;
1061
+ for (let index = 0; index < args.length; index += 1) {
1062
+ const argument = args[index];
1063
+ if (argument === "--" && !optionsEnded)
1064
+ optionsEnded = true;
1065
+ else if (optionsEnded) {
1066
+ if (targetPath === null)
1067
+ targetPath = argument;
1068
+ else
1069
+ return new CliUsageError(`Unexpected submission archive argument: ${argument}.`);
1070
+ }
1071
+ else if (argument === "--json") {
1072
+ if (jsonOutput)
1073
+ return new CliUsageError("Duplicate submission archive flag: --json.");
1074
+ jsonOutput = true;
1075
+ }
1076
+ else if (argument === "--markdown") {
1077
+ if (markdownOutput)
1078
+ return new CliUsageError("Duplicate submission archive flag: --markdown.");
1079
+ markdownOutput = true;
1080
+ }
1081
+ else if (argument === "--require-ready") {
1082
+ if (requireReady)
1083
+ return new CliUsageError("Duplicate submission archive flag: --require-ready.");
1084
+ requireReady = true;
1085
+ }
1086
+ else if (argument === "--output" || argument.startsWith("--output=")) {
1087
+ if (outputPath !== null)
1088
+ return new CliUsageError("Duplicate submission archive flag: --output.");
1089
+ const value = argument === "--output" ? args[index + 1] : argument.slice("--output=".length);
1090
+ if (!value || (argument === "--output" && value.startsWith("--")))
1091
+ return new CliUsageError("Missing path after --output.");
1092
+ outputPath = value;
1093
+ if (argument === "--output")
1094
+ index += 1;
1095
+ }
1096
+ else if (argument.startsWith("--"))
1097
+ return new CliUsageError(`Unknown submission archive flag: ${argument}.`);
1098
+ else if (targetPath === null)
1099
+ targetPath = argument;
1100
+ else
1101
+ return new CliUsageError(`Unexpected submission archive argument: ${argument}.`);
1102
+ }
1103
+ if (targetPath === null)
1104
+ return new CliUsageError("Missing archive path for submission.");
1105
+ if (jsonOutput && markdownOutput)
1106
+ return new CliUsageError("Use either --json or --markdown, not both.");
1107
+ return { targetPath, jsonOutput, markdownOutput, outputPath, requireReady };
1108
+ }
1109
+ function hasArchiveSubcommandTarget(args) {
1110
+ let optionsEnded = false;
1111
+ for (let index = 0; index < args.length; index += 1) {
1112
+ const argument = args[index];
1113
+ if (optionsEnded)
1114
+ return true;
1115
+ if (argument === "--") {
1116
+ optionsEnded = true;
1117
+ }
1118
+ else if (argument === "--output") {
1119
+ index += 1;
1120
+ }
1121
+ else if (!argument.startsWith("--")) {
1122
+ return true;
1123
+ }
1124
+ }
1125
+ return false;
1126
+ }
1127
+ async function submissionTargetExists(targetPath) {
1128
+ try {
1129
+ const { stat } = await import("node:fs/promises");
1130
+ await stat(targetPath);
1131
+ return true;
1132
+ }
1133
+ catch (error) {
1134
+ return !(typeof error === "object" && error !== null && "code" in error
1135
+ && error.code === "ENOENT");
1136
+ }
1137
+ }
973
1138
  export async function runCli(args, io = defaultIo, options = {}) {
974
1139
  const [command, maybePath, ...remainingArgs] = args;
975
1140
  const runtimeSandboxSupported = (command === "check" &&
@@ -1079,6 +1244,43 @@ export async function runCli(args, io = defaultIo, options = {}) {
1079
1244
  : renderDoctorOutputContract(contract, { outputPath }));
1080
1245
  return 0;
1081
1246
  }
1247
+ if (maybePath === "submission") {
1248
+ if (remainingArgs[0] === "archive") {
1249
+ const useLegacyDirectoryTarget = !hasArchiveSubcommandTarget(remainingArgs.slice(1))
1250
+ && await submissionTargetExists("archive");
1251
+ if (!useLegacyDirectoryTarget) {
1252
+ const parsedArchiveArgs = parseSubmissionArchiveCommandArgs(remainingArgs.slice(1));
1253
+ if (parsedArchiveArgs instanceof CliUsageError) {
1254
+ io.writeStderr(parsedArchiveArgs.message);
1255
+ return 2;
1256
+ }
1257
+ const report = await buildSubmissionArchivePreflight(parsedArchiveArgs.targetPath);
1258
+ const renderedReport = parsedArchiveArgs.jsonOutput
1259
+ ? renderSubmissionArchiveJson(report)
1260
+ : parsedArchiveArgs.markdownOutput ? renderSubmissionArchiveMarkdown(report) : renderSubmissionArchiveText(report);
1261
+ if (parsedArchiveArgs.outputPath)
1262
+ await writeFile(parsedArchiveArgs.outputPath, renderedReport, "utf8");
1263
+ writeExactStdout(io, renderedReport);
1264
+ return submissionArchiveExitCode(report, parsedArchiveArgs.requireReady);
1265
+ }
1266
+ }
1267
+ const parsedSubmissionArgs = parseSubmissionCommandArgs(remainingArgs);
1268
+ if (parsedSubmissionArgs instanceof CliUsageError) {
1269
+ io.writeStderr(parsedSubmissionArgs.message);
1270
+ return 2;
1271
+ }
1272
+ const report = await buildSubmissionPreflight(parsedSubmissionArgs.targetPath);
1273
+ const renderedReport = parsedSubmissionArgs.jsonOutput
1274
+ ? renderSubmissionPreflightJson(report)
1275
+ : parsedSubmissionArgs.markdownOutput
1276
+ ? renderSubmissionPreflightMarkdown(report)
1277
+ : renderSubmissionPreflightText(report);
1278
+ if (parsedSubmissionArgs.outputPath) {
1279
+ await writeFile(parsedSubmissionArgs.outputPath, renderedReport, "utf8");
1280
+ }
1281
+ writeExactStdout(io, renderedReport);
1282
+ return submissionPreflightExitCode(report, parsedSubmissionArgs.requireReady);
1283
+ }
1082
1284
  if (maybePath === "mcp") {
1083
1285
  const targetPath = remainingArgs[0] && !remainingArgs[0].startsWith("--")
1084
1286
  ? remainingArgs[0]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plugin-doctor",
3
- "version": "1.58.0",
3
+ "version": "1.60.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,8 +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"
64
+ },
65
+ "dependencies": {
66
+ "fast-xml-parser": "^5.11.0",
67
+ "iconv-lite": "^0.7.3",
68
+ "yaml": "^2.9.0",
69
+ "yauzl": "^3.4.0"
63
70
  }
64
71
  }