codex-plugin-doctor 1.55.0 → 1.57.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 +2 -2
- package/dist/core/doctor-size.d.ts +20 -0
- package/dist/core/doctor-size.js +198 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/run-cli.js +45 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -480,9 +480,9 @@ jobs:
|
|
|
480
480
|
runs-on: ubuntu-latest
|
|
481
481
|
steps:
|
|
482
482
|
- uses: actions/checkout@v5
|
|
483
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
483
|
+
- uses: Esquetta/CodexPluginDoctor@v1.57.0
|
|
484
484
|
with:
|
|
485
|
-
version: "1.
|
|
485
|
+
version: "1.57.0"
|
|
486
486
|
path: .
|
|
487
487
|
runtime: "true"
|
|
488
488
|
policy: codex-publish
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DoctorSizeReport {
|
|
2
|
+
targetPath: string;
|
|
3
|
+
status: "pass" | "warn" | "fail";
|
|
4
|
+
totalSize: number;
|
|
5
|
+
totalSizeHuman: string;
|
|
6
|
+
fileCount: number;
|
|
7
|
+
largeFiles: Array<{
|
|
8
|
+
path: string;
|
|
9
|
+
size: number;
|
|
10
|
+
sizeHuman: string;
|
|
11
|
+
}>;
|
|
12
|
+
warnings: string[];
|
|
13
|
+
npmPackSize?: number;
|
|
14
|
+
npmPackSizeHuman?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function buildDoctorSize(targetPath: string, options?: {
|
|
17
|
+
npmPack?: boolean;
|
|
18
|
+
}): Promise<DoctorSizeReport>;
|
|
19
|
+
export declare function renderDoctorSize(report: DoctorSizeReport): string;
|
|
20
|
+
export declare function renderDoctorSizeJson(report: DoctorSizeReport): string;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const skippedDirs = new Set([
|
|
5
|
+
"node_modules",
|
|
6
|
+
".git",
|
|
7
|
+
".cache",
|
|
8
|
+
"dist",
|
|
9
|
+
"coverage",
|
|
10
|
+
"__pycache__",
|
|
11
|
+
".turbo",
|
|
12
|
+
".next",
|
|
13
|
+
".nuxt"
|
|
14
|
+
]);
|
|
15
|
+
const largeFileThreshold = 1024 * 1024;
|
|
16
|
+
const packageSizeWarnThreshold = 10 * 1024 * 1024;
|
|
17
|
+
const packageSizeFailThreshold = 50 * 1024 * 1024;
|
|
18
|
+
function formatSize(bytes) {
|
|
19
|
+
if (bytes < 1024)
|
|
20
|
+
return `${bytes} B`;
|
|
21
|
+
if (bytes < 1024 * 1024)
|
|
22
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
23
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
24
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
25
|
+
return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GB`;
|
|
26
|
+
}
|
|
27
|
+
async function scanDirectory(rootPath, currentPath = rootPath) {
|
|
28
|
+
let totalSize = 0;
|
|
29
|
+
let fileCount = 0;
|
|
30
|
+
const largeFiles = [];
|
|
31
|
+
let entries;
|
|
32
|
+
try {
|
|
33
|
+
entries = await readdir(currentPath, { withFileTypes: true });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { totalSize: 0, fileCount: 0, largeFiles: [] };
|
|
37
|
+
}
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const entryPath = path.join(currentPath, entry.name);
|
|
40
|
+
if (entry.isDirectory()) {
|
|
41
|
+
if (skippedDirs.has(entry.name)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const subResult = await scanDirectory(rootPath, entryPath);
|
|
45
|
+
totalSize += subResult.totalSize;
|
|
46
|
+
fileCount += subResult.fileCount;
|
|
47
|
+
largeFiles.push(...subResult.largeFiles);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!entry.isFile()) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
let details;
|
|
54
|
+
try {
|
|
55
|
+
details = await stat(entryPath);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
totalSize += details.size;
|
|
61
|
+
fileCount += 1;
|
|
62
|
+
if (details.size >= largeFileThreshold) {
|
|
63
|
+
largeFiles.push({
|
|
64
|
+
path: path.relative(rootPath, entryPath).replace(/\\/g, "/"),
|
|
65
|
+
size: details.size,
|
|
66
|
+
sizeHuman: formatSize(details.size)
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { totalSize, fileCount, largeFiles };
|
|
71
|
+
}
|
|
72
|
+
export async function buildDoctorSize(targetPath, options = {}) {
|
|
73
|
+
const resolvedPath = path.resolve(targetPath);
|
|
74
|
+
const { totalSize, fileCount, largeFiles } = await scanDirectory(resolvedPath);
|
|
75
|
+
const warnings = [];
|
|
76
|
+
let status = "pass";
|
|
77
|
+
let npmPackSize;
|
|
78
|
+
let npmPackSizeHuman;
|
|
79
|
+
if (options.npmPack) {
|
|
80
|
+
try {
|
|
81
|
+
const packOutput = await new Promise((resolve, reject) => {
|
|
82
|
+
execFile("npm", ["pack", "--dry-run"], { cwd: resolvedPath, timeout: 30000, shell: process.platform === "win32" }, (error, stdout, stderr) => {
|
|
83
|
+
if (error) {
|
|
84
|
+
reject(new Error(stderr.trim() || error.message));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
resolve(stdout);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
const tarballLine = packOutput.split("\n").find((line) => line.includes("total files") || line.includes("package size"));
|
|
91
|
+
if (tarballLine) {
|
|
92
|
+
const totalMatch = tarballLine.match(/(\d[\d.]*)\s*([KMG]?B)/i);
|
|
93
|
+
if (totalMatch) {
|
|
94
|
+
const value = Number.parseFloat(totalMatch[1]);
|
|
95
|
+
const unit = totalMatch[2].toUpperCase();
|
|
96
|
+
switch (unit) {
|
|
97
|
+
case "KB":
|
|
98
|
+
npmPackSize = Math.round(value * 1024);
|
|
99
|
+
break;
|
|
100
|
+
case "MB":
|
|
101
|
+
npmPackSize = Math.round(value * 1024 * 1024);
|
|
102
|
+
break;
|
|
103
|
+
case "GB":
|
|
104
|
+
npmPackSize = Math.round(value * 1024 * 1024 * 1024);
|
|
105
|
+
break;
|
|
106
|
+
default:
|
|
107
|
+
npmPackSize = Math.round(value);
|
|
108
|
+
}
|
|
109
|
+
npmPackSizeHuman = formatSize(npmPackSize);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (npmPackSize === undefined) {
|
|
113
|
+
warnings.push("Could not determine npm pack size from output.");
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const diffPercent = totalSize > 0
|
|
117
|
+
? Math.abs(Math.round(((npmPackSize - totalSize) / totalSize) * 100))
|
|
118
|
+
: 0;
|
|
119
|
+
if (diffPercent > 50 && npmPackSize > totalSize) {
|
|
120
|
+
warnings.push(`npm pack size (${npmPackSizeHuman}) differs from local size (${formatSize(totalSize)}) by ${diffPercent}%.`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
warnings.push(`npm pack failed: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (totalSize >= packageSizeFailThreshold) {
|
|
129
|
+
status = "fail";
|
|
130
|
+
warnings.push(`Package size (${formatSize(totalSize)}) exceeds ${formatSize(packageSizeFailThreshold)}.`);
|
|
131
|
+
}
|
|
132
|
+
else if (totalSize >= packageSizeWarnThreshold) {
|
|
133
|
+
status = "warn";
|
|
134
|
+
warnings.push(`Package size (${formatSize(totalSize)}) exceeds ${formatSize(packageSizeWarnThreshold)}.`);
|
|
135
|
+
}
|
|
136
|
+
if (largeFiles.length > 0) {
|
|
137
|
+
if (status === "pass")
|
|
138
|
+
status = "warn";
|
|
139
|
+
warnings.push(`${largeFiles.length} file(s) exceed ${formatSize(largeFileThreshold)}.`);
|
|
140
|
+
}
|
|
141
|
+
largeFiles.sort((a, b) => b.size - a.size);
|
|
142
|
+
return {
|
|
143
|
+
targetPath: resolvedPath,
|
|
144
|
+
status,
|
|
145
|
+
totalSize,
|
|
146
|
+
totalSizeHuman: formatSize(totalSize),
|
|
147
|
+
fileCount,
|
|
148
|
+
largeFiles: largeFiles.slice(0, 10),
|
|
149
|
+
warnings,
|
|
150
|
+
npmPackSize,
|
|
151
|
+
npmPackSizeHuman
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function renderDoctorSize(report) {
|
|
155
|
+
const lines = [
|
|
156
|
+
"Codex Plugin Doctor Size Analysis",
|
|
157
|
+
"=================================",
|
|
158
|
+
`Path: ${report.targetPath}`,
|
|
159
|
+
`Status: ${report.status.toUpperCase()}`,
|
|
160
|
+
`Total size: ${report.totalSizeHuman} (${report.fileCount} files)`,
|
|
161
|
+
report.npmPackSizeHuman ? `npm pack size: ${report.npmPackSizeHuman}` : "",
|
|
162
|
+
""
|
|
163
|
+
];
|
|
164
|
+
if (report.warnings.length > 0) {
|
|
165
|
+
lines.push("Warnings", "--------");
|
|
166
|
+
for (const warning of report.warnings) {
|
|
167
|
+
lines.push(` - ${warning}`);
|
|
168
|
+
}
|
|
169
|
+
lines.push("");
|
|
170
|
+
}
|
|
171
|
+
if (report.largeFiles.length > 0) {
|
|
172
|
+
lines.push("Large Files", "-----------");
|
|
173
|
+
for (const file of report.largeFiles) {
|
|
174
|
+
lines.push(` ${file.sizeHuman.padEnd(8)} ${file.path}`);
|
|
175
|
+
}
|
|
176
|
+
lines.push("");
|
|
177
|
+
}
|
|
178
|
+
if (report.status === "pass") {
|
|
179
|
+
lines.push("Package size within acceptable limits.");
|
|
180
|
+
}
|
|
181
|
+
return lines.join("\n");
|
|
182
|
+
}
|
|
183
|
+
export function renderDoctorSizeJson(report) {
|
|
184
|
+
return JSON.stringify({
|
|
185
|
+
schemaVersion: "1.0.0",
|
|
186
|
+
targetPath: report.targetPath,
|
|
187
|
+
status: report.status,
|
|
188
|
+
totalSize: report.totalSize,
|
|
189
|
+
totalSizeHuman: report.totalSizeHuman,
|
|
190
|
+
fileCount: report.fileCount,
|
|
191
|
+
largeFiles: report.largeFiles,
|
|
192
|
+
warnings: report.warnings,
|
|
193
|
+
...(report.npmPackSize !== undefined ? {
|
|
194
|
+
npmPackSize: report.npmPackSize,
|
|
195
|
+
npmPackSizeHuman: report.npmPackSizeHuman
|
|
196
|
+
} : {})
|
|
197
|
+
}, null, 2);
|
|
198
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CheckOptions, CheckResult } from "./domain/types.js";
|
|
2
|
+
export { buildDoctorSize, renderDoctorSize, renderDoctorSizeJson, type DoctorSizeReport } from "./core/doctor-size.js";
|
|
2
3
|
export { applyDoctorConfig, loadDoctorConfig, type DoctorConfig } from "./core/doctor-config.js";
|
|
3
4
|
export { readRawDoctorConfig, resolveDoctorConfigPath, writeRawDoctorConfig, type LoadedDoctorConfig, type RawDoctorConfig } from "./core/doctor-config-store.js";
|
|
4
5
|
export { classifySuppressionRecord, isValidSuppressionFingerprint, isSuppressionExpired, validateSuppressionRecord, type ClassifiedSuppressionRecord, type InvalidSuppressionRecord, type SuppressionRecordField, type ValidSuppressionRecord } from "./core/suppression-record.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { validatePlugin } from "./core/validate-plugin.js";
|
|
2
|
+
export { buildDoctorSize, renderDoctorSize, renderDoctorSizeJson } from "./core/doctor-size.js";
|
|
2
3
|
export { applyDoctorConfig, loadDoctorConfig } from "./core/doctor-config.js";
|
|
3
4
|
export { readRawDoctorConfig, resolveDoctorConfigPath, writeRawDoctorConfig } from "./core/doctor-config-store.js";
|
|
4
5
|
export { classifySuppressionRecord, isValidSuppressionFingerprint, isSuppressionExpired, validateSuppressionRecord } from "./core/suppression-record.js";
|
package/dist/run-cli.js
CHANGED
|
@@ -34,6 +34,7 @@ import { buildDoctorRiskDiffReport, renderDoctorRiskDiffReport, renderDoctorRisk
|
|
|
34
34
|
import { buildDoctorInspectorReport, renderDoctorInspectorReport, renderDoctorInspectorReportJson } from "./core/inspector-bridge.js";
|
|
35
35
|
import { applyFixPlan, buildFixPlan, renderApplyFixResult, renderFixPlanJsonReport, renderFixPlan } from "./core/fix-plan.js";
|
|
36
36
|
import { renderClientDoctor, renderEnvironmentDoctor, renderEnvironmentDoctorJson } from "./core/environment-doctor.js";
|
|
37
|
+
import { buildDoctorSize, renderDoctorSize, renderDoctorSizeJson } from "./core/doctor-size.js";
|
|
37
38
|
import { initCiWorkflow } from "./core/init-ci.js";
|
|
38
39
|
import { watchPlugin } from "./core/watch-plugin.js";
|
|
39
40
|
import { buildDepAudit, renderDepAudit, renderDepAuditJson, renderDepAuditSarif } from "./core/dep-audit.js";
|
|
@@ -168,7 +169,7 @@ function parseRemoteNetworkFlags(flags, runtime) {
|
|
|
168
169
|
return { allowNetwork, allowLocalNetwork, allowSessionLifecycle, requireRemoteReliability };
|
|
169
170
|
}
|
|
170
171
|
function printUsage(io) {
|
|
171
|
-
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>]\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");
|
|
172
|
+
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");
|
|
172
173
|
io.writeStderr("Registry readiness: codex-plugin-doctor registry check <server.json|directory> [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
173
174
|
+ " codex-plugin-doctor registry inspect <server-name> --allow-network [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
174
175
|
+ "Registry publication preflight: codex-plugin-doctor registry preflight <server.json|directory> [--allow-network] [--json] [--output <path>] [--require-publish-ready]");
|
|
@@ -2101,6 +2102,20 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2101
2102
|
io.writeStdout(await renderClientDoctor(terminalContext));
|
|
2102
2103
|
return 0;
|
|
2103
2104
|
}
|
|
2105
|
+
if (maybePath === "size") {
|
|
2106
|
+
const sizeTarget = remainingArgs[0] && !remainingArgs[0].startsWith("--") ? remainingArgs[0] : ".";
|
|
2107
|
+
const sizeFlags = remainingArgs[0] && remainingArgs[0].startsWith("--")
|
|
2108
|
+
? remainingArgs
|
|
2109
|
+
: remainingArgs.slice(1);
|
|
2110
|
+
const jsonOutput = sizeFlags.includes("--json");
|
|
2111
|
+
const npmPack = sizeFlags.includes("--npm");
|
|
2112
|
+
const report = await buildDoctorSize(sizeTarget, { npmPack });
|
|
2113
|
+
const output = jsonOutput
|
|
2114
|
+
? renderDoctorSizeJson(report)
|
|
2115
|
+
: renderDoctorSize(report);
|
|
2116
|
+
io.writeStdout(output);
|
|
2117
|
+
return report.status === "fail" ? 1 : 0;
|
|
2118
|
+
}
|
|
2104
2119
|
io.writeStdout(maybePath === "--json"
|
|
2105
2120
|
? await renderEnvironmentDoctorJson(terminalContext)
|
|
2106
2121
|
: await renderEnvironmentDoctor(terminalContext));
|
|
@@ -2891,6 +2906,13 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2891
2906
|
: normalizedFlags[runtimeApprovalDigestIndex + 1];
|
|
2892
2907
|
const changedSinceIndex = normalizedFlags.indexOf("--changed-since");
|
|
2893
2908
|
const changedSinceRef = changedSinceIndex === -1 ? null : normalizedFlags[changedSinceIndex + 1];
|
|
2909
|
+
const failOnRules = [];
|
|
2910
|
+
for (let i = 0; i < normalizedFlags.length; i += 1) {
|
|
2911
|
+
if (normalizedFlags[i] === "--fail-on" && normalizedFlags[i + 1] && !normalizedFlags[i + 1].startsWith("--")) {
|
|
2912
|
+
failOnRules.push(normalizedFlags[i + 1]);
|
|
2913
|
+
i += 1;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2894
2916
|
const baselineIndex = normalizedFlags.indexOf("--baseline");
|
|
2895
2917
|
const baselinePath = baselineIndex === -1 ? null : normalizedFlags[baselineIndex + 1];
|
|
2896
2918
|
if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
|
|
@@ -3136,6 +3158,28 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
3136
3158
|
failOnWarnings: doctorConfig.failOnWarnings
|
|
3137
3159
|
})
|
|
3138
3160
|
: configuredResult;
|
|
3161
|
+
if (failOnRules.length > 0) {
|
|
3162
|
+
result.findings = result.findings.map((finding) => {
|
|
3163
|
+
if (finding.severity === "warn" && failOnRules.includes(finding.id)) {
|
|
3164
|
+
return { ...finding, severity: "fail" };
|
|
3165
|
+
}
|
|
3166
|
+
return finding;
|
|
3167
|
+
});
|
|
3168
|
+
const hasFail = result.findings.some((f) => f.severity === "fail");
|
|
3169
|
+
const hasWarn = result.findings.some((f) => f.severity === "warn");
|
|
3170
|
+
if (hasFail) {
|
|
3171
|
+
result.status = "fail";
|
|
3172
|
+
result.exitCode = 1;
|
|
3173
|
+
}
|
|
3174
|
+
else if (hasWarn) {
|
|
3175
|
+
result.status = "warn";
|
|
3176
|
+
result.exitCode = 0;
|
|
3177
|
+
}
|
|
3178
|
+
else {
|
|
3179
|
+
result.status = "pass";
|
|
3180
|
+
result.exitCode = 0;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3139
3183
|
if (renderer) {
|
|
3140
3184
|
if (result.status === "fail") {
|
|
3141
3185
|
renderer.stopFailure("Validation failed");
|
package/package.json
CHANGED