codex-plugin-doctor 0.18.0 → 0.20.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 +14 -8
- package/dist/core/init-ci.js +7 -0
- package/dist/core/output-contract.js +12 -0
- package/dist/core/validation-corpus.d.ts +57 -0
- package/dist/core/validation-corpus.js +128 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/reporting/render-installed-machine-report.d.ts +37 -0
- package/dist/reporting/render-installed-machine-report.js +56 -0
- package/dist/run-cli.js +34 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ Output formats:
|
|
|
77
77
|
- validation history JSONL and trend summaries
|
|
78
78
|
- deterministic local attestation artifacts
|
|
79
79
|
- output contract and rule catalog freeze metadata
|
|
80
|
+
- bundled validation corpus reports
|
|
80
81
|
- `--output` file writing
|
|
81
82
|
- CI summary and artifact generation
|
|
82
83
|
|
|
@@ -180,6 +181,8 @@ codex-plugin-doctor self-test
|
|
|
180
181
|
codex-plugin-doctor doctor
|
|
181
182
|
codex-plugin-doctor doctor contract
|
|
182
183
|
codex-plugin-doctor doctor contract --json --output output-contract.json
|
|
184
|
+
codex-plugin-doctor doctor corpus
|
|
185
|
+
codex-plugin-doctor doctor corpus --json --output validation-corpus.json
|
|
183
186
|
codex-plugin-doctor doctor npm codex-plugin-doctor
|
|
184
187
|
codex-plugin-doctor doctor npm codex-plugin-doctor --json --output npm-preinstall.json
|
|
185
188
|
codex-plugin-doctor doctor attest .
|
|
@@ -262,7 +265,7 @@ codex-plugin-doctor check . --json --runtime --verbose-runtime
|
|
|
262
265
|
|
|
263
266
|
`self-test` runs the bundled runtime-complete sample through static validation, runtime MCP probes, and the compatibility scorecard. It is the fastest post-install check after `npm install -g codex-plugin-doctor`.
|
|
264
267
|
|
|
265
|
-
`doctor` checks the local environment, including package version, platform, Node version, npm global prefix, Codex home, and Codex plugin cache visibility. The text output also includes recommended next commands for self-test, installed plugin discovery, runtime checks, compatibility scoring, and CI setup. `doctor contract` publishes the machine-readable output contract, including public JSON schema surfaces, stable-through-1.0 compatibility metadata, and a frozen rule catalog digest. Add `--json` for automation or `--output output-contract.json` to write the contract to disk. `doctor npm <package>` runs a preinstall scan by packing the npm package with scripts disabled, extracting the publish tarball, and running validation, security, trust, and recommendation checks against the shipped contents. Add `--json` for automation or `--output npm-preinstall.json` to write the report to disk. `doctor attest <path>` creates a deterministic local attestation with a package fingerprint, report digest, validation/security/compatibility/trust summary, and unsigned verification metadata. Add `--json` for automation or `--output attestation.json` to write the artifact to disk. `doctor inspector <path>` builds a safe MCP Inspector launch command from a packaged `.mcp.json` file without starting the Inspector proxy automatically. Use `--server <name>` when the package contains multiple MCP server entries. `doctor diff --before <path> --after <path>` compares two package roots and reports new findings, resolved findings, trust score delta, and whether risk increased. `doctor recommend <path>` turns validation, security, and compatibility signals into a prioritized action plan with blocker, high, medium, and info actions. Add `--json` for automation or `--output recommendations.json` to write the report to disk. `doctor trust <path>` creates a local trust score from package lifecycle scripts, dependency specs, and MCP security findings. Use it before release when you want supply-chain risks summarized as one score. `doctor perf <path>` profiles the shared package analysis pipeline and reports per-stage durations for validation, config, security, compatibility, trust, recommendations, and total runtime. `doctor export --bundle <path>` creates a redacted operator handoff bundle that includes validation JSON, security scorecard data, compatibility matrix, recommendations, and trust score in one file. `doctor snapshot` creates a redacted diagnostics bundle with environment health, client config readiness, installed plugin metadata, and next commands. Add `--json` for machine-readable output or `--output doctor-snapshot.json` to write the bundle to disk. `doctor clients` reports local Codex, Claude Desktop, Cursor, Cline, and Windsurf config readiness. `doctor --update-check` compares the installed CLI version with the latest npm version and prints the upgrade command when a newer release is available.
|
|
268
|
+
`doctor` checks the local environment, including package version, platform, Node version, npm global prefix, Codex home, and Codex plugin cache visibility. The text output also includes recommended next commands for self-test, installed plugin discovery, runtime checks, compatibility scoring, and CI setup. `doctor contract` publishes the machine-readable output contract, including public JSON schema surfaces, stable-through-1.0 compatibility metadata, and a frozen rule catalog digest. Add `--json` for automation or `--output output-contract.json` to write the contract to disk. `doctor corpus` runs the bundled validation corpus against healthy runtime, risky security, and starter skill packages, then reports whether each case matched its expected outcome. Add `--json` for automation or `--output validation-corpus.json` to write the corpus report to disk. `doctor npm <package>` runs a preinstall scan by packing the npm package with scripts disabled, extracting the publish tarball, and running validation, security, trust, and recommendation checks against the shipped contents. Add `--json` for automation or `--output npm-preinstall.json` to write the report to disk. `doctor attest <path>` creates a deterministic local attestation with a package fingerprint, report digest, validation/security/compatibility/trust summary, and unsigned verification metadata. Add `--json` for automation or `--output attestation.json` to write the artifact to disk. `doctor inspector <path>` builds a safe MCP Inspector launch command from a packaged `.mcp.json` file without starting the Inspector proxy automatically. Use `--server <name>` when the package contains multiple MCP server entries. `doctor diff --before <path> --after <path>` compares two package roots and reports new findings, resolved findings, trust score delta, and whether risk increased. `doctor recommend <path>` turns validation, security, and compatibility signals into a prioritized action plan with blocker, high, medium, and info actions. Add `--json` for automation or `--output recommendations.json` to write the report to disk. `doctor trust <path>` creates a local trust score from package lifecycle scripts, dependency specs, and MCP security findings. Use it before release when you want supply-chain risks summarized as one score. `doctor perf <path>` profiles the shared package analysis pipeline and reports per-stage durations for validation, config, security, compatibility, trust, recommendations, and total runtime. `doctor export --bundle <path>` creates a redacted operator handoff bundle that includes validation JSON, security scorecard data, compatibility matrix, recommendations, and trust score in one file. `doctor snapshot` creates a redacted diagnostics bundle with environment health, client config readiness, installed plugin metadata, and next commands. Add `--json` for machine-readable output or `--output doctor-snapshot.json` to write the bundle to disk. `doctor clients` reports local Codex, Claude Desktop, Cursor, Cline, and Windsurf config readiness. `doctor --update-check` compares the installed CLI version with the latest npm version and prints the upgrade command when a newer release is available.
|
|
266
269
|
|
|
267
270
|
`audit --installed` runs a local ecosystem audit against every discovered Codex plugin in the installed plugin cache. Add `--security` to include security scorecards, `--compat` to include the all-client compatibility matrix, and `--json --output local-audit.json` when you want a shareable machine-readable report. Add `--cache` to reuse unchanged plugin results between runs; add `--changed` to only report plugins whose fingerprint changed since the last cached audit. Use `--cache-file path/to/audit-cache.json` when CI or scripted runs need an explicit cache location.
|
|
268
271
|
|
|
@@ -327,15 +330,18 @@ jobs:
|
|
|
327
330
|
doctor:
|
|
328
331
|
runs-on: ubuntu-latest
|
|
329
332
|
steps:
|
|
330
|
-
- uses: actions/checkout@v4
|
|
331
|
-
- uses: Esquetta/CodexPluginDoctor@v0.
|
|
333
|
+
- uses: actions/checkout@v4
|
|
334
|
+
- uses: Esquetta/CodexPluginDoctor@v0.20.0
|
|
332
335
|
with:
|
|
333
|
-
version: "0.
|
|
336
|
+
version: "0.20.0"
|
|
334
337
|
path: .
|
|
335
|
-
runtime: "
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
338
|
+
runtime: "true"
|
|
339
|
+
policy: codex-publish
|
|
340
|
+
upload-artifact: "true"
|
|
341
|
+
artifact-name: codex-plugin-doctor-reports
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
The action writes `codex-plugin-doctor-summary.md`, `codex-plugin-doctor-report.json`, and optional `codex-plugin-doctor.sarif` files to `codex-plugin-doctor-reports`, appends the Markdown report to the GitHub Actions step summary, uploads the report directory as an artifact, and then returns the real validation exit code. For runtime probing, SARIF output, installed plugin cache checks, CI policy presets, and pinned release examples, see [GitHub Action Usage](./docs/engineering/github-action-usage.md).
|
|
339
345
|
|
|
340
346
|
To self-test this repository after cloning it:
|
|
341
347
|
|
package/dist/core/init-ci.js
CHANGED
|
@@ -21,6 +21,13 @@ function buildWorkflow() {
|
|
|
21
21
|
` version: \"${packageVersion}\"`,
|
|
22
22
|
" path: .",
|
|
23
23
|
" runtime: \"true\"",
|
|
24
|
+
" policy: codex-publish",
|
|
25
|
+
" json: \"true\"",
|
|
26
|
+
" markdown: \"true\"",
|
|
27
|
+
" sarif: \"true\"",
|
|
28
|
+
" upload-artifact: \"true\"",
|
|
29
|
+
" step-summary: \"true\"",
|
|
30
|
+
" artifact-name: codex-plugin-doctor-reports",
|
|
24
31
|
""
|
|
25
32
|
].join("\n");
|
|
26
33
|
}
|
|
@@ -7,6 +7,12 @@ const publicSchemaDefinitions = [
|
|
|
7
7
|
command: "codex-plugin-doctor check <path> --json",
|
|
8
8
|
required: ["schemaVersion", "generatedAt", "summary", "findings"]
|
|
9
9
|
},
|
|
10
|
+
{
|
|
11
|
+
id: "doctor.installed.check.json",
|
|
12
|
+
command: "codex-plugin-doctor check --installed --json",
|
|
13
|
+
outputKind: "doctor.installed.check",
|
|
14
|
+
required: ["schemaVersion", "kind", "generatedAt", "summary", "plugins"]
|
|
15
|
+
},
|
|
10
16
|
{
|
|
11
17
|
id: "doctor.security.json",
|
|
12
18
|
command: "codex-plugin-doctor security <path> --json",
|
|
@@ -42,6 +48,12 @@ const publicSchemaDefinitions = [
|
|
|
42
48
|
command: "codex-plugin-doctor doctor --json",
|
|
43
49
|
required: ["schemaVersion", "generatedAt", "version", "platform", "node", "checks"]
|
|
44
50
|
},
|
|
51
|
+
{
|
|
52
|
+
id: "doctor.validation.corpus.json",
|
|
53
|
+
command: "codex-plugin-doctor doctor corpus --json",
|
|
54
|
+
outputKind: "doctor.validation.corpus",
|
|
55
|
+
required: ["schemaVersion", "kind", "generatedAt", "version", "summary", "cases"]
|
|
56
|
+
},
|
|
45
57
|
{
|
|
46
58
|
id: "doctor.recommendations.json",
|
|
47
59
|
command: "codex-plugin-doctor doctor recommend <path> --json",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type PackageAnalysisOptions } from "./package-analysis.js";
|
|
2
|
+
type ValidationStatus = "pass" | "warn" | "fail";
|
|
3
|
+
export interface ValidationCorpusCaseDefinition {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
profile: string;
|
|
7
|
+
sourceType: "bundled-example";
|
|
8
|
+
relativePath: string;
|
|
9
|
+
runtimeEnabled: boolean;
|
|
10
|
+
expected: {
|
|
11
|
+
validationStatus: ValidationStatus;
|
|
12
|
+
findingIds?: string[];
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ValidationCorpusCaseResult {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
profile: string;
|
|
19
|
+
sourceType: "bundled-example";
|
|
20
|
+
targetPath: string;
|
|
21
|
+
runtimeEnabled: boolean;
|
|
22
|
+
expected: {
|
|
23
|
+
validationStatus: ValidationStatus;
|
|
24
|
+
findingIds: string[];
|
|
25
|
+
};
|
|
26
|
+
actual: {
|
|
27
|
+
validationStatus: ValidationStatus;
|
|
28
|
+
findingIds: string[];
|
|
29
|
+
securityStatus: ValidationStatus;
|
|
30
|
+
trustStatus: ValidationStatus;
|
|
31
|
+
compatibilityFailedClients: string[];
|
|
32
|
+
};
|
|
33
|
+
expectationMatched: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface DoctorValidationCorpusReport {
|
|
36
|
+
schemaVersion: "1.0.0";
|
|
37
|
+
kind: "doctor.validation.corpus";
|
|
38
|
+
generatedAt: string;
|
|
39
|
+
version: string;
|
|
40
|
+
summary: {
|
|
41
|
+
status: "pass" | "fail";
|
|
42
|
+
caseCount: number;
|
|
43
|
+
passedExpectations: number;
|
|
44
|
+
failedExpectations: number;
|
|
45
|
+
runtimeCases: number;
|
|
46
|
+
};
|
|
47
|
+
cases: ValidationCorpusCaseResult[];
|
|
48
|
+
}
|
|
49
|
+
export interface BuildDoctorValidationCorpusOptions {
|
|
50
|
+
environment?: PackageAnalysisOptions["environment"];
|
|
51
|
+
}
|
|
52
|
+
export declare function buildDoctorValidationCorpusReport(options?: BuildDoctorValidationCorpusOptions): Promise<DoctorValidationCorpusReport>;
|
|
53
|
+
export declare function renderDoctorValidationCorpusJson(report: DoctorValidationCorpusReport): string;
|
|
54
|
+
export declare function renderDoctorValidationCorpusReport(report: DoctorValidationCorpusReport, options?: {
|
|
55
|
+
outputPath?: string | null;
|
|
56
|
+
}): string;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { buildPackageAnalysis } from "./package-analysis.js";
|
|
4
|
+
import { validatePlugin } from "./validate-plugin.js";
|
|
5
|
+
import { matrixExitCode } from "../compatibility/compatibility-matrix.js";
|
|
6
|
+
import { packageVersion } from "../version.js";
|
|
7
|
+
const bundledCorpusCases = [
|
|
8
|
+
{
|
|
9
|
+
id: "bundled-runtime-healthy",
|
|
10
|
+
label: "Bundled runtime-complete example",
|
|
11
|
+
profile: "healthy-runtime",
|
|
12
|
+
sourceType: "bundled-example",
|
|
13
|
+
relativePath: "examples/codex-doctor-runtime",
|
|
14
|
+
runtimeEnabled: true,
|
|
15
|
+
expected: {
|
|
16
|
+
validationStatus: "pass"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "bundled-risky-security",
|
|
21
|
+
label: "Bundled risky security example",
|
|
22
|
+
profile: "risky-security",
|
|
23
|
+
sourceType: "bundled-example",
|
|
24
|
+
relativePath: "examples/codex-doctor-risky",
|
|
25
|
+
runtimeEnabled: false,
|
|
26
|
+
expected: {
|
|
27
|
+
validationStatus: "fail",
|
|
28
|
+
findingIds: ["plugin.security.hard_coded_secret"]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "bundled-starter-skill",
|
|
33
|
+
label: "Bundled starter skill-only example",
|
|
34
|
+
profile: "skill-only",
|
|
35
|
+
sourceType: "bundled-example",
|
|
36
|
+
relativePath: "examples/codex-doctor-starter",
|
|
37
|
+
runtimeEnabled: false,
|
|
38
|
+
expected: {
|
|
39
|
+
validationStatus: "pass"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
function resolvePackageRoot() {
|
|
44
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
45
|
+
}
|
|
46
|
+
function includesExpectedFindings(actualFindingIds, expectedFindingIds) {
|
|
47
|
+
return expectedFindingIds.every((findingId) => actualFindingIds.includes(findingId));
|
|
48
|
+
}
|
|
49
|
+
async function runCorpusCase(caseDefinition, options) {
|
|
50
|
+
const targetPath = path.resolve(resolvePackageRoot(), caseDefinition.relativePath);
|
|
51
|
+
const analysis = await buildPackageAnalysis(targetPath, {
|
|
52
|
+
environment: options.environment,
|
|
53
|
+
runCheck: (pathToCheck) => validatePlugin(pathToCheck, {
|
|
54
|
+
runtime: caseDefinition.runtimeEnabled
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
const findingIds = analysis.validation.findings.map((finding) => finding.id).sort();
|
|
58
|
+
const expectedFindingIds = [...(caseDefinition.expected.findingIds ?? [])].sort();
|
|
59
|
+
const compatibilityFailedClients = analysis.compatibility.results
|
|
60
|
+
.filter((result) => result.status === "fail")
|
|
61
|
+
.map((result) => result.client);
|
|
62
|
+
const expectationMatched = analysis.validation.status === caseDefinition.expected.validationStatus &&
|
|
63
|
+
includesExpectedFindings(findingIds, expectedFindingIds);
|
|
64
|
+
return {
|
|
65
|
+
id: caseDefinition.id,
|
|
66
|
+
label: caseDefinition.label,
|
|
67
|
+
profile: caseDefinition.profile,
|
|
68
|
+
sourceType: caseDefinition.sourceType,
|
|
69
|
+
targetPath,
|
|
70
|
+
runtimeEnabled: caseDefinition.runtimeEnabled,
|
|
71
|
+
expected: {
|
|
72
|
+
validationStatus: caseDefinition.expected.validationStatus,
|
|
73
|
+
findingIds: expectedFindingIds
|
|
74
|
+
},
|
|
75
|
+
actual: {
|
|
76
|
+
validationStatus: analysis.validation.status,
|
|
77
|
+
findingIds,
|
|
78
|
+
securityStatus: analysis.security.status,
|
|
79
|
+
trustStatus: analysis.trust.status,
|
|
80
|
+
compatibilityFailedClients: matrixExitCode(analysis.compatibility) === 1
|
|
81
|
+
? compatibilityFailedClients
|
|
82
|
+
: []
|
|
83
|
+
},
|
|
84
|
+
expectationMatched
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export async function buildDoctorValidationCorpusReport(options = {}) {
|
|
88
|
+
const cases = await Promise.all(bundledCorpusCases.map((caseDefinition) => runCorpusCase(caseDefinition, options)));
|
|
89
|
+
const failedExpectations = cases.filter((caseResult) => !caseResult.expectationMatched).length;
|
|
90
|
+
return {
|
|
91
|
+
schemaVersion: "1.0.0",
|
|
92
|
+
kind: "doctor.validation.corpus",
|
|
93
|
+
generatedAt: new Date().toISOString(),
|
|
94
|
+
version: packageVersion,
|
|
95
|
+
summary: {
|
|
96
|
+
status: failedExpectations > 0 ? "fail" : "pass",
|
|
97
|
+
caseCount: cases.length,
|
|
98
|
+
passedExpectations: cases.length - failedExpectations,
|
|
99
|
+
failedExpectations,
|
|
100
|
+
runtimeCases: cases.filter((caseResult) => caseResult.runtimeEnabled).length
|
|
101
|
+
},
|
|
102
|
+
cases
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export function renderDoctorValidationCorpusJson(report) {
|
|
106
|
+
return JSON.stringify(report, null, 2);
|
|
107
|
+
}
|
|
108
|
+
export function renderDoctorValidationCorpusReport(report, options = {}) {
|
|
109
|
+
const lines = [
|
|
110
|
+
"Doctor Validation Corpus",
|
|
111
|
+
"========================",
|
|
112
|
+
`Version: ${report.version}`,
|
|
113
|
+
`Status: ${report.summary.status.toUpperCase()}`,
|
|
114
|
+
`Cases: ${report.summary.caseCount}`,
|
|
115
|
+
`Passed expectations: ${report.summary.passedExpectations}`,
|
|
116
|
+
`Failed expectations: ${report.summary.failedExpectations}`,
|
|
117
|
+
`Runtime cases: ${report.summary.runtimeCases}`
|
|
118
|
+
];
|
|
119
|
+
if (options.outputPath) {
|
|
120
|
+
lines.push(`Output: ${options.outputPath}`);
|
|
121
|
+
}
|
|
122
|
+
lines.push("", "Cases", "-----");
|
|
123
|
+
for (const caseResult of report.cases) {
|
|
124
|
+
lines.push(`${caseResult.id}: ${caseResult.expectationMatched ? "PASS" : "FAIL"} ` +
|
|
125
|
+
`(${caseResult.actual.validationStatus.toUpperCase()}, ${caseResult.actual.findingIds.length} findings)`);
|
|
126
|
+
}
|
|
127
|
+
return lines.join("\n");
|
|
128
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { buildDoctorRecommendations, renderDoctorRecommendations, renderDoctorRe
|
|
|
6
6
|
export { buildDoctorExportBundle, renderDoctorExportBundle, renderDoctorExportBundleJson, type DoctorExportBundle } from "./core/doctor-export-bundle.js";
|
|
7
7
|
export { buildDoctorAttestation, renderDoctorAttestation, renderDoctorAttestationJson, type DoctorAttestation, type Digest, type PackageFingerprint } from "./core/attestation.js";
|
|
8
8
|
export { buildDoctorOutputContract, renderDoctorOutputContract, renderDoctorOutputContractJson, type DoctorOutputContract, type OutputContractRule, type OutputContractSchema } from "./core/output-contract.js";
|
|
9
|
+
export { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport, type BuildDoctorValidationCorpusOptions, type DoctorValidationCorpusReport, type ValidationCorpusCaseDefinition, type ValidationCorpusCaseResult } from "./core/validation-corpus.js";
|
|
9
10
|
export { buildDoctorExportBundleFromAnalysis, buildDoctorRecommendationsFromAnalysis, buildPackageAnalysis, type PackageAnalysis, type PackageAnalysisOptions, type PackageAnalysisStage, type PackageAnalysisTiming } from "./core/package-analysis.js";
|
|
10
11
|
export { buildDoctorPerformanceReport, renderDoctorPerformanceReport, renderDoctorPerformanceReportJson, type BuildDoctorPerformanceReportOptions, type DoctorPerformanceReport, type DoctorPerformanceStage, type DoctorPerformanceStageName } from "./core/performance-report.js";
|
|
11
12
|
export { buildDoctorNpmPackageReport, renderDoctorNpmPackageReport, renderDoctorNpmPackageReportJson, type BuildDoctorNpmPackageReportOptions, type DoctorNpmPackageReport } from "./core/npm-package-doctor.js";
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { buildDoctorRecommendations, renderDoctorRecommendations, renderDoctorRe
|
|
|
6
6
|
export { buildDoctorExportBundle, renderDoctorExportBundle, renderDoctorExportBundleJson } from "./core/doctor-export-bundle.js";
|
|
7
7
|
export { buildDoctorAttestation, renderDoctorAttestation, renderDoctorAttestationJson } from "./core/attestation.js";
|
|
8
8
|
export { buildDoctorOutputContract, renderDoctorOutputContract, renderDoctorOutputContractJson } from "./core/output-contract.js";
|
|
9
|
+
export { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
|
|
9
10
|
export { buildDoctorExportBundleFromAnalysis, buildDoctorRecommendationsFromAnalysis, buildPackageAnalysis } from "./core/package-analysis.js";
|
|
10
11
|
export { buildDoctorPerformanceReport, renderDoctorPerformanceReport, renderDoctorPerformanceReportJson } from "./core/performance-report.js";
|
|
11
12
|
export { buildDoctorNpmPackageReport, renderDoctorNpmPackageReport, renderDoctorNpmPackageReportJson } from "./core/npm-package-doctor.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CompatibilityMatrix } from "../compatibility/compatibility-matrix.js";
|
|
2
|
+
import type { InstalledPlugin } from "../core/discover-installed-plugins.js";
|
|
3
|
+
import type { CheckResult, JsonReport } from "../domain/types.js";
|
|
4
|
+
export interface InstalledCheckItem {
|
|
5
|
+
plugin: InstalledPlugin;
|
|
6
|
+
result: CheckResult;
|
|
7
|
+
compatibilityMatrix?: CompatibilityMatrix;
|
|
8
|
+
}
|
|
9
|
+
export interface InstalledCheckJsonReport {
|
|
10
|
+
schemaVersion: "1.0.0";
|
|
11
|
+
kind: "doctor.installed.check";
|
|
12
|
+
generatedAt: string;
|
|
13
|
+
summary: {
|
|
14
|
+
status: "pass" | "warn" | "fail";
|
|
15
|
+
checked: number;
|
|
16
|
+
pass: number;
|
|
17
|
+
warn: number;
|
|
18
|
+
fail: number;
|
|
19
|
+
};
|
|
20
|
+
plugins: Array<{
|
|
21
|
+
plugin: {
|
|
22
|
+
name: string;
|
|
23
|
+
version?: string;
|
|
24
|
+
rootPath: string;
|
|
25
|
+
relativePath: string;
|
|
26
|
+
};
|
|
27
|
+
report: JsonReport;
|
|
28
|
+
compatibilityMatrix?: CompatibilityMatrix;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
export declare function buildInstalledJsonReport(items: InstalledCheckItem[], options: {
|
|
32
|
+
runtimeProbeEnabled: boolean;
|
|
33
|
+
}): InstalledCheckJsonReport;
|
|
34
|
+
export declare function renderInstalledJsonReport(items: InstalledCheckItem[], options: {
|
|
35
|
+
runtimeProbeEnabled: boolean;
|
|
36
|
+
}): string;
|
|
37
|
+
export declare function renderInstalledSarifReport(items: InstalledCheckItem[]): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { buildJsonReport } from "./render-json-report.js";
|
|
2
|
+
import { renderSarifReport } from "./render-sarif-report.js";
|
|
3
|
+
function summarizeStatus(items) {
|
|
4
|
+
const pass = items.filter((item) => item.result.status === "pass").length;
|
|
5
|
+
const warn = items.filter((item) => item.result.status === "warn").length;
|
|
6
|
+
const fail = items.filter((item) => item.result.status === "fail").length;
|
|
7
|
+
return {
|
|
8
|
+
status: fail > 0 ? "fail" : warn > 0 ? "warn" : "pass",
|
|
9
|
+
checked: items.length,
|
|
10
|
+
pass,
|
|
11
|
+
warn,
|
|
12
|
+
fail
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function buildInstalledJsonReport(items, options) {
|
|
16
|
+
return {
|
|
17
|
+
schemaVersion: "1.0.0",
|
|
18
|
+
kind: "doctor.installed.check",
|
|
19
|
+
generatedAt: new Date().toISOString(),
|
|
20
|
+
summary: summarizeStatus(items),
|
|
21
|
+
plugins: items.map((item) => ({
|
|
22
|
+
plugin: {
|
|
23
|
+
name: item.plugin.name,
|
|
24
|
+
...(item.plugin.version ? { version: item.plugin.version } : {}),
|
|
25
|
+
rootPath: item.plugin.rootPath,
|
|
26
|
+
relativePath: item.plugin.relativePath
|
|
27
|
+
},
|
|
28
|
+
report: buildJsonReport(item.result, options),
|
|
29
|
+
...(item.compatibilityMatrix ? { compatibilityMatrix: item.compatibilityMatrix } : {})
|
|
30
|
+
}))
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function renderInstalledJsonReport(items, options) {
|
|
34
|
+
return JSON.stringify(buildInstalledJsonReport(items, options), null, 2);
|
|
35
|
+
}
|
|
36
|
+
export function renderInstalledSarifReport(items) {
|
|
37
|
+
const runs = items.flatMap((item) => {
|
|
38
|
+
const sarif = JSON.parse(renderSarifReport(item.result));
|
|
39
|
+
return sarif.runs.map((run) => ({
|
|
40
|
+
...run,
|
|
41
|
+
automationDetails: {
|
|
42
|
+
id: item.plugin.name
|
|
43
|
+
},
|
|
44
|
+
properties: {
|
|
45
|
+
pluginName: item.plugin.name,
|
|
46
|
+
...(item.plugin.version ? { pluginVersion: item.plugin.version } : {}),
|
|
47
|
+
pluginPath: item.plugin.rootPath
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
});
|
|
51
|
+
return JSON.stringify({
|
|
52
|
+
version: "2.1.0",
|
|
53
|
+
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
54
|
+
runs
|
|
55
|
+
}, null, 2);
|
|
56
|
+
}
|
package/dist/run-cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { buildDoctorRecommendations, renderDoctorRecommendations, renderDoctorRe
|
|
|
18
18
|
import { buildDoctorExportBundle, renderDoctorExportBundle, renderDoctorExportBundleJson } from "./core/doctor-export-bundle.js";
|
|
19
19
|
import { buildDoctorAttestation, renderDoctorAttestation, renderDoctorAttestationJson } from "./core/attestation.js";
|
|
20
20
|
import { buildDoctorOutputContract, renderDoctorOutputContract, renderDoctorOutputContractJson } from "./core/output-contract.js";
|
|
21
|
+
import { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
|
|
21
22
|
import { buildDoctorPerformanceReport, renderDoctorPerformanceReport, renderDoctorPerformanceReportJson } from "./core/performance-report.js";
|
|
22
23
|
import { buildDoctorNpmPackageReport, renderDoctorNpmPackageReport, renderDoctorNpmPackageReportJson } from "./core/npm-package-doctor.js";
|
|
23
24
|
import { buildDoctorRiskDiffReport, renderDoctorRiskDiffReport, renderDoctorRiskDiffReportJson } from "./core/risk-diff.js";
|
|
@@ -29,6 +30,7 @@ import { initPluginPackage, initPluginTemplates, isInitPluginTemplate } from "./
|
|
|
29
30
|
import { runCheck } from "./index.js";
|
|
30
31
|
import { buildGenericMcpDoctor, renderGenericMcpDoctor, renderGenericMcpDoctorJson } from "./mcp/generic-mcp-doctor.js";
|
|
31
32
|
import { renderInstalledSummary } from "./reporting/render-installed-summary.js";
|
|
33
|
+
import { renderInstalledJsonReport, renderInstalledSarifReport } from "./reporting/render-installed-machine-report.js";
|
|
32
34
|
import { renderBadgeJson, renderBadgeMarkdown } from "./reporting/render-badge-report.js";
|
|
33
35
|
import { renderCompatibilityScorecard } from "./reporting/render-compatibility-scorecard.js";
|
|
34
36
|
import { renderCompatibilityReport } from "./reporting/render-compatibility-report.js";
|
|
@@ -67,7 +69,7 @@ const defaultIo = {
|
|
|
67
69
|
}
|
|
68
70
|
};
|
|
69
71
|
function printUsage(io) {
|
|
70
|
-
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] [--verbose-runtime] [--explain] [--no-animations] [--ascii]\n codex-plugin-doctor audit --installed [filter] [--policy codex-publish|mcp-strict|security] [--security] [--compat] [--json] [--output <path>] [--cache] [--changed]\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 fix <path> (--dry-run|--interactive --backup|--apply --backup)\n codex-plugin-doctor history <history.jsonl> [--json] [--fail-on-regression]\n codex-plugin-doctor doctor [npm <package>|contract|attest <path>|inspector <path>|diff --before <path> --after <path>|recommend <path>|trust <path>|perf <path>|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 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");
|
|
72
|
+
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] [--verbose-runtime] [--explain] [--no-animations] [--ascii]\n codex-plugin-doctor audit --installed [filter] [--policy codex-publish|mcp-strict|security] [--security] [--compat] [--json] [--output <path>] [--cache] [--changed]\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 fix <path> (--dry-run|--interactive --backup|--apply --backup)\n codex-plugin-doctor history <history.jsonl> [--json] [--fail-on-regression]\n codex-plugin-doctor doctor [npm <package>|contract|corpus|attest <path>|inspector <path>|diff --before <path> --after <path>|recommend <path>|trust <path>|perf <path>|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 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");
|
|
71
73
|
}
|
|
72
74
|
function renderInstalledPlugins(plugins) {
|
|
73
75
|
const lines = [
|
|
@@ -250,6 +252,29 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
250
252
|
: renderDoctorOutputContract(contract, { outputPath }));
|
|
251
253
|
return 0;
|
|
252
254
|
}
|
|
255
|
+
if (maybePath === "corpus") {
|
|
256
|
+
const jsonOutput = remainingArgs.includes("--json");
|
|
257
|
+
const outputIndex = remainingArgs.indexOf("--output");
|
|
258
|
+
const outputPath = outputIndex === -1 ? null : remainingArgs[outputIndex + 1];
|
|
259
|
+
if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
|
|
260
|
+
io.writeStderr("Missing path after --output.");
|
|
261
|
+
return 2;
|
|
262
|
+
}
|
|
263
|
+
const report = await buildDoctorValidationCorpusReport({
|
|
264
|
+
environment: {
|
|
265
|
+
env: terminalContext.env,
|
|
266
|
+
platform: terminalContext.platform
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
const reportJson = renderDoctorValidationCorpusJson(report);
|
|
270
|
+
if (outputPath) {
|
|
271
|
+
await writeFile(outputPath, reportJson, "utf8");
|
|
272
|
+
}
|
|
273
|
+
io.writeStdout(jsonOutput
|
|
274
|
+
? reportJson
|
|
275
|
+
: renderDoctorValidationCorpusReport(report, { outputPath }));
|
|
276
|
+
return report.summary.status === "pass" ? 0 : 1;
|
|
277
|
+
}
|
|
253
278
|
if (maybePath === "attest") {
|
|
254
279
|
const targetPath = remainingArgs[0] && !remainingArgs[0].startsWith("--")
|
|
255
280
|
? remainingArgs[0]
|
|
@@ -992,18 +1017,18 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
992
1017
|
}
|
|
993
1018
|
const report = installedSummary
|
|
994
1019
|
? renderInstalledSummary(checkedPlugins)
|
|
995
|
-
:
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
?
|
|
1020
|
+
: sarifOutput
|
|
1021
|
+
? renderInstalledSarifReport(checkedPlugins)
|
|
1022
|
+
: jsonOutput
|
|
1023
|
+
? renderInstalledJsonReport(checkedPlugins, { runtimeProbeEnabled: effectiveRuntimeProbeEnabled })
|
|
1024
|
+
: checkedPlugins
|
|
1025
|
+
.map((item) => markdownOutput
|
|
1026
|
+
? buildMarkdownReport(item.result, { runtimeProbeEnabled: effectiveRuntimeProbeEnabled })
|
|
1002
1027
|
: renderTextReport(item.result, {
|
|
1003
1028
|
ascii: outputPolicy.style === "ascii",
|
|
1004
1029
|
explain: explainFindings
|
|
1005
1030
|
}))
|
|
1006
|
-
|
|
1031
|
+
.join("\n\n");
|
|
1007
1032
|
if (outputPath) {
|
|
1008
1033
|
await writeFile(outputPath, report, "utf8");
|
|
1009
1034
|
}
|
package/package.json
CHANGED