codex-plugin-doctor 1.45.0 → 1.46.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 +19 -13
- package/dist/core/release-check.d.ts +50 -0
- package/dist/core/release-check.js +189 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/run-cli.js +51 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -285,12 +285,16 @@ codex-plugin-doctor check . --badge-markdown
|
|
|
285
285
|
codex-plugin-doctor check . --sarif --output results.sarif
|
|
286
286
|
codex-plugin-doctor check . --ascii
|
|
287
287
|
codex-plugin-doctor check . --no-animations
|
|
288
|
-
codex-plugin-doctor check . --runtime
|
|
289
|
-
codex-plugin-doctor check . --runtime --require-runtime-approval --runtime-approval-digest sha256:<approved-plan-digest>
|
|
288
|
+
codex-plugin-doctor check . --runtime
|
|
289
|
+
codex-plugin-doctor check . --runtime --require-runtime-approval --runtime-approval-digest sha256:<approved-plan-digest>
|
|
290
|
+
codex-plugin-doctor release check .
|
|
291
|
+
codex-plugin-doctor release check . --json
|
|
292
|
+
codex-plugin-doctor release check . --runtime
|
|
293
|
+
codex-plugin-doctor release check . --runtime --require-runtime-approval --runtime-approval-digest sha256:<approved-plan-digest>
|
|
290
294
|
codex-plugin-doctor check . --config .codex-doctor.json
|
|
291
|
-
codex-plugin-doctor baseline create . --output .codex-doctor-baseline.json
|
|
292
|
-
codex-plugin-doctor check . --baseline .codex-doctor-baseline.json
|
|
293
|
-
codex-plugin-doctor check . --history validation-history.jsonl
|
|
295
|
+
codex-plugin-doctor baseline create . --output .codex-doctor-baseline.json
|
|
296
|
+
codex-plugin-doctor check . --baseline .codex-doctor-baseline.json
|
|
297
|
+
codex-plugin-doctor check . --history validation-history.jsonl
|
|
294
298
|
codex-plugin-doctor history validation-history.jsonl
|
|
295
299
|
codex-plugin-doctor history validation-history.jsonl --json
|
|
296
300
|
codex-plugin-doctor history validation-history.jsonl --fail-on-regression
|
|
@@ -336,11 +340,11 @@ codex-plugin-doctor check . --json --runtime --verbose-runtime
|
|
|
336
340
|
|
|
337
341
|
`check --badge-json` emits Shields endpoint-compatible JSON such as `{"schemaVersion":1,"label":"doctor","message":"PASS","color":"brightgreen"}`. `check --badge-markdown` emits a static shields.io Markdown badge for README or release notes. Badge output is intentionally limited to single package checks, not `check --installed`.
|
|
338
342
|
|
|
339
|
-
`check --history <path>` appends a compact JSONL validation snapshot after a single package check. `history <path>` reads the JSONL file and compares the latest run to the previous run, including status, finding-count deltas, and whether the latest run regressed. Add `history --json` for automation output or `history --fail-on-regression` when CI should fail after a worse latest run.
|
|
340
|
-
|
|
341
|
-
`baseline create <path> --output <path>` records the current fingerprinted findings for gradual CI adoption. A later `check <path> --baseline <path>` keeps matched findings visible in baseline metadata but gates only new active findings; reports summarize new, matched, and resolved counts. Commit the baseline for review and regenerate it deliberately after accepted findings change.
|
|
342
|
-
|
|
343
|
-
`fix --dry-run` renders safe automatic fix plans without changing files. `fix --interactive --backup` shows the same numbered plan, then applies everything after `yes` or only selected action numbers such as `1,3`. `fix --apply --backup` applies supported safe fixes, such as manifest defaults and missing skills directories, after creating backups.
|
|
343
|
+
`check --history <path>` appends a compact JSONL validation snapshot after a single package check. `history <path>` reads the JSONL file and compares the latest run to the previous run, including status, finding-count deltas, and whether the latest run regressed. Add `history --json` for automation output or `history --fail-on-regression` when CI should fail after a worse latest run.
|
|
344
|
+
|
|
345
|
+
`baseline create <path> --output <path>` records the current fingerprinted findings for gradual CI adoption. A later `check <path> --baseline <path>` keeps matched findings visible in baseline metadata but gates only new active findings; reports summarize new, matched, and resolved counts. Commit the baseline for review and regenerate it deliberately after accepted findings change.
|
|
346
|
+
|
|
347
|
+
`fix --dry-run` renders safe automatic fix plans without changing files. `fix --interactive --backup` shows the same numbered plan, then applies everything after `yes` or only selected action numbers such as `1,3`. `fix --apply --backup` applies supported safe fixes, such as manifest defaults and missing skills directories, after creating backups.
|
|
344
348
|
|
|
345
349
|
Optional local policy file:
|
|
346
350
|
|
|
@@ -406,9 +410,9 @@ jobs:
|
|
|
406
410
|
runs-on: ubuntu-latest
|
|
407
411
|
steps:
|
|
408
412
|
- uses: actions/checkout@v5
|
|
409
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
413
|
+
- uses: Esquetta/CodexPluginDoctor@v1.46.1
|
|
410
414
|
with:
|
|
411
|
-
version: "1.
|
|
415
|
+
version: "1.46.1"
|
|
412
416
|
path: .
|
|
413
417
|
runtime: "true"
|
|
414
418
|
policy: codex-publish
|
|
@@ -463,7 +467,9 @@ npm run prepare-release
|
|
|
463
467
|
npm run release-check
|
|
464
468
|
```
|
|
465
469
|
|
|
466
|
-
`prepare-release` runs tests, builds the TypeScript output, and performs `npm pack --dry-run`. `release-check` adds release preflight checks for a clean git tree, existing npm versions, existing version tags, tests, build, pack dry-run, fresh temp install audit, and publish dry-run.
|
|
470
|
+
`prepare-release` runs tests, builds the TypeScript output, and performs `npm pack --dry-run`. `release-check` adds release preflight checks for a clean git tree, existing npm versions, existing version tags, package/lockfile/changelog version consistency, tests, build, pack dry-run, fresh temp install audit, and publish dry-run.
|
|
471
|
+
|
|
472
|
+
For plugin packages, `codex-plugin-doctor release check .` performs static readiness checks by default. Runtime probing executes package-local MCP servers and therefore requires an explicit `--runtime`; protected workflows can also require the approved runtime-plan digest.
|
|
467
473
|
|
|
468
474
|
Related docs:
|
|
469
475
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CheckOptions, CheckResult } from "../domain/types.js";
|
|
2
|
+
export interface ReleaseCheckReport {
|
|
3
|
+
targetPath: string;
|
|
4
|
+
status: "pass" | "fail";
|
|
5
|
+
runtimeProbeEnabled?: boolean;
|
|
6
|
+
checks: {
|
|
7
|
+
validation: {
|
|
8
|
+
status: string;
|
|
9
|
+
findings: number;
|
|
10
|
+
exitCode: number;
|
|
11
|
+
};
|
|
12
|
+
security: {
|
|
13
|
+
status: string;
|
|
14
|
+
score: number;
|
|
15
|
+
};
|
|
16
|
+
dependencies: {
|
|
17
|
+
status: string;
|
|
18
|
+
vulnerabilities: number;
|
|
19
|
+
};
|
|
20
|
+
compatibility: {
|
|
21
|
+
status: string;
|
|
22
|
+
score: number;
|
|
23
|
+
overallStatus: string;
|
|
24
|
+
};
|
|
25
|
+
trust: {
|
|
26
|
+
status: string;
|
|
27
|
+
score: number;
|
|
28
|
+
};
|
|
29
|
+
metadata?: ReleaseMetadataCheck;
|
|
30
|
+
};
|
|
31
|
+
ready: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface ReleaseMetadataCheck {
|
|
34
|
+
status: "pass" | "fail" | "skipped";
|
|
35
|
+
packageVersion: string | null;
|
|
36
|
+
lockfileVersion: string | null;
|
|
37
|
+
changelogVersion: boolean | null;
|
|
38
|
+
findings: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface BuildReleaseCheckOptions {
|
|
41
|
+
env?: Record<string, string | undefined>;
|
|
42
|
+
platform?: NodeJS.Platform;
|
|
43
|
+
runtime?: boolean;
|
|
44
|
+
runCheck?: (targetPath: string, options: CheckOptions) => Promise<CheckResult>;
|
|
45
|
+
}
|
|
46
|
+
export declare function buildReleaseMetadataCheck(targetPath: string): Promise<ReleaseMetadataCheck>;
|
|
47
|
+
export declare function assertReleaseMetadataSync(targetPath: string): Promise<void>;
|
|
48
|
+
export declare function buildReleaseCheck(targetPath: string, options?: BuildReleaseCheckOptions): Promise<ReleaseCheckReport>;
|
|
49
|
+
export declare function renderReleaseCheck(report: ReleaseCheckReport): string;
|
|
50
|
+
export declare function renderReleaseCheckJson(report: ReleaseCheckReport): string;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { validatePlugin } from "./validate-plugin.js";
|
|
4
|
+
import { buildSecurityAudit } from "../security/security-audit.js";
|
|
5
|
+
import { buildDepAudit } from "./dep-audit.js";
|
|
6
|
+
import { buildCompatibilityMatrix } from "../compatibility/compatibility-matrix.js";
|
|
7
|
+
import { buildTrustScore } from "../security/trust-score.js";
|
|
8
|
+
async function readOptionalFile(filePath) {
|
|
9
|
+
try {
|
|
10
|
+
return await readFile(filePath, "utf8");
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code === "ENOENT") {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function buildReleaseMetadataCheck(targetPath) {
|
|
20
|
+
const packageContent = await readOptionalFile(path.join(targetPath, "package.json"));
|
|
21
|
+
if (packageContent === null) {
|
|
22
|
+
return {
|
|
23
|
+
status: "skipped",
|
|
24
|
+
packageVersion: null,
|
|
25
|
+
lockfileVersion: null,
|
|
26
|
+
changelogVersion: null,
|
|
27
|
+
findings: []
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const findings = [];
|
|
31
|
+
let packageVersion = null;
|
|
32
|
+
let lockfileVersion = null;
|
|
33
|
+
try {
|
|
34
|
+
const packageJson = JSON.parse(packageContent);
|
|
35
|
+
packageVersion = typeof packageJson.version === "string" ? packageJson.version : null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
findings.push("package.json is not valid JSON.");
|
|
39
|
+
}
|
|
40
|
+
if (!packageVersion) {
|
|
41
|
+
findings.push("package.json must contain a version.");
|
|
42
|
+
}
|
|
43
|
+
const lockContent = await readOptionalFile(path.join(targetPath, "package-lock.json"));
|
|
44
|
+
if (lockContent !== null) {
|
|
45
|
+
try {
|
|
46
|
+
const lock = JSON.parse(lockContent);
|
|
47
|
+
const rootVersion = lock.packages?.[""]?.version ?? lock.version;
|
|
48
|
+
lockfileVersion = typeof rootVersion === "string" ? rootVersion : null;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
findings.push("package-lock.json is not valid JSON.");
|
|
52
|
+
}
|
|
53
|
+
if (!lockfileVersion) {
|
|
54
|
+
findings.push("package-lock.json must contain the root package version.");
|
|
55
|
+
}
|
|
56
|
+
else if (packageVersion && lockfileVersion !== packageVersion) {
|
|
57
|
+
findings.push(`package-lock.json version ${lockfileVersion} does not match package.json ${packageVersion}.`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const changelog = await readOptionalFile(path.join(targetPath, "CHANGELOG.md"));
|
|
61
|
+
const changelogVersion = changelog === null || !packageVersion
|
|
62
|
+
? null
|
|
63
|
+
: new RegExp(`^## \\[${packageVersion.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\](?:\\s|$)`, "m").test(changelog);
|
|
64
|
+
if (changelogVersion === false) {
|
|
65
|
+
findings.push(`CHANGELOG.md does not contain a ${packageVersion} release section.`);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
status: findings.length === 0 ? "pass" : "fail",
|
|
69
|
+
packageVersion,
|
|
70
|
+
lockfileVersion,
|
|
71
|
+
changelogVersion,
|
|
72
|
+
findings
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export async function assertReleaseMetadataSync(targetPath) {
|
|
76
|
+
const metadata = await buildReleaseMetadataCheck(path.resolve(targetPath));
|
|
77
|
+
if (metadata.status === "fail") {
|
|
78
|
+
throw new Error(`Release metadata is inconsistent: ${metadata.findings.join(" ")}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function matrixOverallStatus(matrix) {
|
|
82
|
+
const statuses = matrix.results.map((r) => r.status);
|
|
83
|
+
const passCount = statuses.filter((s) => s === "pass").length;
|
|
84
|
+
const total = statuses.length;
|
|
85
|
+
if (total === 0) {
|
|
86
|
+
return "pass";
|
|
87
|
+
}
|
|
88
|
+
if (passCount === total) {
|
|
89
|
+
return "pass";
|
|
90
|
+
}
|
|
91
|
+
const failCount = statuses.filter((s) => s === "fail").length;
|
|
92
|
+
return failCount > 0 ? "fail" : "warn";
|
|
93
|
+
}
|
|
94
|
+
function matrixScore(matrix) {
|
|
95
|
+
const scoreMap = { pass: 100, warn: 70, fail: 0, skipped: 0 };
|
|
96
|
+
const scores = matrix.results.map((r) => scoreMap[r.status] ?? 0);
|
|
97
|
+
if (scores.length === 0) {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
return Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);
|
|
101
|
+
}
|
|
102
|
+
export async function buildReleaseCheck(targetPath, options = {}) {
|
|
103
|
+
const resolvedPath = path.resolve(targetPath);
|
|
104
|
+
const env = options.env ?? process.env;
|
|
105
|
+
const platform = options.platform ?? process.platform;
|
|
106
|
+
const runtimeProbeEnabled = options.runtime ?? false;
|
|
107
|
+
const validationResult = await (options.runCheck ?? validatePlugin)(resolvedPath, {
|
|
108
|
+
runtime: runtimeProbeEnabled
|
|
109
|
+
});
|
|
110
|
+
const securityResult = await buildSecurityAudit(resolvedPath);
|
|
111
|
+
const depResult = await buildDepAudit(resolvedPath);
|
|
112
|
+
const compatResult = await buildCompatibilityMatrix(resolvedPath, { env, platform });
|
|
113
|
+
const trustResult = await buildTrustScore(resolvedPath);
|
|
114
|
+
const metadataResult = await buildReleaseMetadataCheck(resolvedPath);
|
|
115
|
+
const checks = {
|
|
116
|
+
validation: {
|
|
117
|
+
status: validationResult.status,
|
|
118
|
+
findings: validationResult.findings.length,
|
|
119
|
+
exitCode: validationResult.exitCode
|
|
120
|
+
},
|
|
121
|
+
security: {
|
|
122
|
+
status: securityResult.status,
|
|
123
|
+
score: securityResult.score
|
|
124
|
+
},
|
|
125
|
+
dependencies: {
|
|
126
|
+
status: depResult.status,
|
|
127
|
+
vulnerabilities: depResult.totalVulnerabilities
|
|
128
|
+
},
|
|
129
|
+
compatibility: {
|
|
130
|
+
status: matrixOverallStatus(compatResult),
|
|
131
|
+
score: matrixScore(compatResult),
|
|
132
|
+
overallStatus: matrixOverallStatus(compatResult)
|
|
133
|
+
},
|
|
134
|
+
trust: {
|
|
135
|
+
status: trustResult.status,
|
|
136
|
+
score: trustResult.score
|
|
137
|
+
},
|
|
138
|
+
metadata: metadataResult
|
|
139
|
+
};
|
|
140
|
+
const ready = checks.validation.status === "pass" &&
|
|
141
|
+
checks.security.status === "pass" &&
|
|
142
|
+
checks.dependencies.status !== "fail" &&
|
|
143
|
+
checks.compatibility.status !== "fail" &&
|
|
144
|
+
checks.trust.status === "pass" &&
|
|
145
|
+
checks.metadata.status !== "fail";
|
|
146
|
+
return {
|
|
147
|
+
targetPath: resolvedPath,
|
|
148
|
+
status: ready ? "pass" : "fail",
|
|
149
|
+
runtimeProbeEnabled,
|
|
150
|
+
checks,
|
|
151
|
+
ready
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function renderReleaseCheck(report) {
|
|
155
|
+
const icon = (pass) => pass ? "PASS" : pass === undefined ? "SKIP" : "FAIL";
|
|
156
|
+
const lines = [
|
|
157
|
+
"Codex Plugin Doctor Release Check",
|
|
158
|
+
"==================================",
|
|
159
|
+
`Path: ${report.targetPath}`,
|
|
160
|
+
`Ready: ${report.ready ? "YES" : "NO"}`,
|
|
161
|
+
`Runtime probe: ${report.runtimeProbeEnabled ? "enabled" : "disabled"}`,
|
|
162
|
+
"",
|
|
163
|
+
"Checks",
|
|
164
|
+
"------",
|
|
165
|
+
` ${icon(report.checks.validation.status === "pass")} validation ${report.checks.validation.status} (${report.checks.validation.findings} findings)`,
|
|
166
|
+
` ${icon(report.checks.security.status === "pass")} security ${report.checks.security.status} (score: ${report.checks.security.score})`,
|
|
167
|
+
` ${icon(report.checks.dependencies.status === "pass")} dependencies ${report.checks.dependencies.status} (${report.checks.dependencies.vulnerabilities} vulns)`,
|
|
168
|
+
` ${icon(report.checks.compatibility.status === "pass")} compatibility ${report.checks.compatibility.status} (score: ${report.checks.compatibility.score})`,
|
|
169
|
+
` ${icon(report.checks.trust.status === "pass")} trust ${report.checks.trust.status} (score: ${report.checks.trust.score})`,
|
|
170
|
+
...(report.checks.metadata
|
|
171
|
+
? [` ${icon(report.checks.metadata.status !== "fail")} metadata ${report.checks.metadata.status} (${report.checks.metadata.findings.length} findings)`]
|
|
172
|
+
: []),
|
|
173
|
+
"",
|
|
174
|
+
report.ready
|
|
175
|
+
? "All release checks passed. Ready to publish."
|
|
176
|
+
: "Some checks failed. Fix issues before publishing."
|
|
177
|
+
];
|
|
178
|
+
return lines.join("\n");
|
|
179
|
+
}
|
|
180
|
+
export function renderReleaseCheckJson(report) {
|
|
181
|
+
return JSON.stringify({
|
|
182
|
+
schemaVersion: "1.0.0",
|
|
183
|
+
targetPath: report.targetPath,
|
|
184
|
+
status: report.status,
|
|
185
|
+
runtimeProbeEnabled: report.runtimeProbeEnabled ?? false,
|
|
186
|
+
ready: report.ready,
|
|
187
|
+
checks: report.checks
|
|
188
|
+
}, null, 2);
|
|
189
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -29,4 +29,5 @@ export { buildDepAudit, buildDepAuditRecommendations, renderDepAudit, renderDepA
|
|
|
29
29
|
export { initGitHooks, removeGitHooks, type InitGitHooksResult, type RemoveGitHooksResult } from "./core/init-git-hooks.js";
|
|
30
30
|
export { generateCompletion, type CompletionShell } from "./core/shell-completion.js";
|
|
31
31
|
export { validateConfigFile, renderConfigValidation, renderConfigValidationJson, type ConfigValidationFinding, type ConfigValidationReport } from "./core/config-validate.js";
|
|
32
|
+
export { assertReleaseMetadataSync, buildReleaseCheck, buildReleaseMetadataCheck, renderReleaseCheck, renderReleaseCheckJson, type BuildReleaseCheckOptions, type ReleaseMetadataCheck, type ReleaseCheckReport } from "./core/release-check.js";
|
|
32
33
|
export declare function runCheck(targetPath: string, options?: CheckOptions): Promise<CheckResult>;
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ export { buildDepAudit, buildDepAuditRecommendations, renderDepAudit, renderDepA
|
|
|
29
29
|
export { initGitHooks, removeGitHooks } from "./core/init-git-hooks.js";
|
|
30
30
|
export { generateCompletion } from "./core/shell-completion.js";
|
|
31
31
|
export { validateConfigFile, renderConfigValidation, renderConfigValidationJson } from "./core/config-validate.js";
|
|
32
|
+
export { assertReleaseMetadataSync, buildReleaseCheck, buildReleaseMetadataCheck, renderReleaseCheck, renderReleaseCheckJson } from "./core/release-check.js";
|
|
32
33
|
export async function runCheck(targetPath, options = {}) {
|
|
33
34
|
return validatePlugin(targetPath, options);
|
|
34
35
|
}
|
package/dist/run-cli.js
CHANGED
|
@@ -36,6 +36,7 @@ import { buildDepAudit, renderDepAudit, renderDepAuditJson, renderDepAuditSarif
|
|
|
36
36
|
import { initGitHooks, removeGitHooks } from "./core/init-git-hooks.js";
|
|
37
37
|
import { generateCompletion } from "./core/shell-completion.js";
|
|
38
38
|
import { renderConfigValidation, renderConfigValidationJson, validateConfigFile } from "./core/config-validate.js";
|
|
39
|
+
import { buildReleaseCheck, renderReleaseCheck, renderReleaseCheckJson } from "./core/release-check.js";
|
|
39
40
|
import { initPluginPackage, initPluginTemplates, isInitPluginTemplate } from "./core/init-plugin.js";
|
|
40
41
|
import { runCheck } from "./index.js";
|
|
41
42
|
import { buildGenericMcpDoctor, renderGenericMcpDoctor, renderGenericMcpDoctorJson } from "./mcp/generic-mcp-doctor.js";
|
|
@@ -81,7 +82,7 @@ const defaultIo = {
|
|
|
81
82
|
}
|
|
82
83
|
};
|
|
83
84
|
function printUsage(io) {
|
|
84
|
-
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 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");
|
|
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");
|
|
85
86
|
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]");
|
|
86
87
|
io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
|
|
87
88
|
}
|
|
@@ -2249,6 +2250,55 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2249
2250
|
io.writeStdout(output);
|
|
2250
2251
|
return report.status === "fail" ? 1 : 0;
|
|
2251
2252
|
}
|
|
2253
|
+
if (command === "release") {
|
|
2254
|
+
const subCommand = maybePath;
|
|
2255
|
+
if (subCommand !== "check") {
|
|
2256
|
+
io.writeStderr("Usage: codex-plugin-doctor release check <path> [--json] [--runtime] [--require-runtime-approval --runtime-approval-digest <digest>]");
|
|
2257
|
+
return 2;
|
|
2258
|
+
}
|
|
2259
|
+
const targetPath = remainingArgs[0] && !remainingArgs[0].startsWith("--") ? remainingArgs[0] : ".";
|
|
2260
|
+
const releaseFlags = remainingArgs[0] && remainingArgs[0].startsWith("--")
|
|
2261
|
+
? remainingArgs
|
|
2262
|
+
: remainingArgs.slice(1);
|
|
2263
|
+
const jsonOutput = releaseFlags.includes("--json");
|
|
2264
|
+
const runtimeProbeEnabled = releaseFlags.includes("--runtime");
|
|
2265
|
+
const requireRuntimeApproval = releaseFlags.includes("--require-runtime-approval");
|
|
2266
|
+
const runtimeApprovalDigestIndex = releaseFlags.indexOf("--runtime-approval-digest");
|
|
2267
|
+
const runtimeApprovalDigest = runtimeApprovalDigestIndex === -1
|
|
2268
|
+
? null
|
|
2269
|
+
: releaseFlags[runtimeApprovalDigestIndex + 1];
|
|
2270
|
+
if (requireRuntimeApproval && !runtimeProbeEnabled) {
|
|
2271
|
+
io.writeStderr("Runtime approval requires --runtime.");
|
|
2272
|
+
return 2;
|
|
2273
|
+
}
|
|
2274
|
+
if (runtimeApprovalDigestIndex !== -1 &&
|
|
2275
|
+
(!runtimeApprovalDigest || runtimeApprovalDigest.startsWith("--"))) {
|
|
2276
|
+
io.writeStderr("Missing digest after --runtime-approval-digest.");
|
|
2277
|
+
return 2;
|
|
2278
|
+
}
|
|
2279
|
+
if (requireRuntimeApproval) {
|
|
2280
|
+
const runtimePlan = await buildDoctorRuntimePlan(targetPath);
|
|
2281
|
+
const approval = evaluateRuntimeApproval(runtimePlan, {
|
|
2282
|
+
required: true,
|
|
2283
|
+
approvedDigest: runtimeApprovalDigest
|
|
2284
|
+
});
|
|
2285
|
+
if (!runtimeApprovalPassed(approval)) {
|
|
2286
|
+
io.writeStderr(`${approval.message}\nCurrent runtime plan digest: ${runtimePlan.digest}`);
|
|
2287
|
+
return 1;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
const report = await buildReleaseCheck(targetPath, {
|
|
2291
|
+
env: terminalContext.env,
|
|
2292
|
+
platform: terminalContext.platform,
|
|
2293
|
+
runtime: runtimeProbeEnabled,
|
|
2294
|
+
runCheck: options.runCheckImpl
|
|
2295
|
+
});
|
|
2296
|
+
const output = jsonOutput
|
|
2297
|
+
? renderReleaseCheckJson(report)
|
|
2298
|
+
: renderReleaseCheck(report);
|
|
2299
|
+
io.writeStdout(output);
|
|
2300
|
+
return report.status === "fail" ? 1 : 0;
|
|
2301
|
+
}
|
|
2252
2302
|
if (command === "suppress") {
|
|
2253
2303
|
const parsedSuppressCommand = parseSuppressCommand([maybePath ?? "", ...remainingArgs]);
|
|
2254
2304
|
if ("message" in parsedSuppressCommand) {
|
package/package.json
CHANGED