codex-plugin-doctor 1.52.0 → 1.54.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/LICENSE +21 -21
- package/README.md +33 -17
- package/dist/core/bounded-http-client.js +2 -1
- package/dist/core/doctor-export-bundle.js +3 -1
- package/dist/core/mcp-registry.d.ts +57 -0
- package/dist/core/mcp-registry.js +455 -0
- package/dist/core/output-contract.js +73 -0
- package/dist/core/release-check.d.ts +2 -0
- package/dist/core/release-check.js +2 -0
- package/dist/core/release-evidence.d.ts +2 -0
- package/dist/core/release-evidence.js +2 -0
- package/dist/core/remote-mcp-probe.d.ts +1 -0
- package/dist/core/remote-mcp-probe.js +59 -1
- package/dist/core/remote-transport-reliability.d.ts +18 -0
- package/dist/core/remote-transport-reliability.js +243 -0
- package/dist/core/runtime-probe.d.ts +3 -1
- package/dist/core/runtime-probe.js +18 -1
- package/dist/core/sse-observation.d.ts +7 -0
- package/dist/core/sse-observation.js +67 -0
- package/dist/core/validate-plugin.js +4 -3
- package/dist/domain/types.d.ts +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/generic-mcp-doctor.d.ts +2 -0
- package/dist/mcp/generic-mcp-doctor.js +6 -3
- package/dist/reporting/render-markdown-report.js +13 -0
- package/dist/reporting/render-text-report.js +11 -0
- package/dist/rules/rule-catalog.js +108 -0
- package/dist/run-cli.js +143 -7
- package/examples/README.md +94 -94
- package/examples/codex-doctor-generic-mcp/.mcp.json +10 -10
- package/examples/codex-doctor-generic-mcp/server.js +1 -1
- package/examples/codex-doctor-risky/.codex-plugin/plugin.json +7 -7
- package/examples/codex-doctor-risky/.mcp.json +12 -12
- package/examples/codex-doctor-risky/mock-server.js +1 -1
- package/examples/codex-doctor-runtime/.codex-plugin/plugin.json +8 -8
- package/examples/codex-doctor-runtime/.mcp.json +9 -9
- package/examples/codex-doctor-runtime/mock-server.js +237 -237
- package/examples/codex-doctor-runtime/skills/context-check/SKILL.md +7 -7
- package/examples/codex-doctor-starter/.codex-plugin/plugin.json +7 -7
- package/examples/codex-doctor-starter/skills/repo-scan/SKILL.md +7 -7
- package/package.json +1 -1
package/dist/domain/types.d.ts
CHANGED
|
@@ -43,6 +43,8 @@ export interface CheckOptions {
|
|
|
43
43
|
runtime?: boolean;
|
|
44
44
|
allowNetwork?: boolean;
|
|
45
45
|
allowLocalNetwork?: boolean;
|
|
46
|
+
allowSessionLifecycle?: boolean;
|
|
47
|
+
requireRemoteReliability?: boolean;
|
|
46
48
|
runtimeTranscript?: (line: string) => void;
|
|
47
49
|
runtimeStartupTimeoutMs?: number;
|
|
48
50
|
runtimeSandbox?: RuntimeSandboxMode;
|
|
@@ -103,8 +105,19 @@ export interface RemoteRuntimeScorecard {
|
|
|
103
105
|
session: "absent" | "present-valid" | "present-invalid";
|
|
104
106
|
protocolHeaders: RuntimeCapabilityStatus;
|
|
105
107
|
authorization: RuntimeCapabilityStatus;
|
|
108
|
+
reliability?: RemoteTransportReliabilityScorecard;
|
|
106
109
|
overall: "pass" | "warn" | "fail" | "skipped";
|
|
107
110
|
}
|
|
111
|
+
export type RemoteTransportReliabilityStatus = "pass" | "warn" | "fail" | "skipped";
|
|
112
|
+
export interface RemoteTransportReliabilityScorecard {
|
|
113
|
+
getSse: RemoteTransportReliabilityStatus;
|
|
114
|
+
sessionPropagation: RemoteTransportReliabilityStatus;
|
|
115
|
+
resumability: RemoteTransportReliabilityStatus;
|
|
116
|
+
disconnectSafety: RemoteTransportReliabilityStatus;
|
|
117
|
+
sessionRestart: RemoteTransportReliabilityStatus;
|
|
118
|
+
termination: RemoteTransportReliabilityStatus;
|
|
119
|
+
overall: RemoteTransportReliabilityStatus;
|
|
120
|
+
}
|
|
108
121
|
export type McpConformanceProfile = "legacy" | "2025-11-25" | "future-compatible";
|
|
109
122
|
export interface RuntimeConformanceScorecard {
|
|
110
123
|
protocolVersion: string | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export { buildDoctorInspectorReport, renderDoctorInspectorReport, renderDoctorIn
|
|
|
30
30
|
export { buildEcosystemAudit, renderEcosystemAudit, renderEcosystemAuditJson, type EcosystemAuditReport } from "./audit/ecosystem-audit.js";
|
|
31
31
|
export { applyPolicyToDepAudit, applyPolicyToDoctorConfig, applyPolicyToSecurityAudit, parsePolicyPack, policyEnablesRuntime, policyFailsOnWarnings, policyPackNames, type PolicyPackName } from "./policy/policy-packs.js";
|
|
32
32
|
export { buildGenericMcpDoctor, renderGenericMcpDoctor, renderGenericMcpDoctorJson, type GenericMcpDoctorReport } from "./mcp/generic-mcp-doctor.js";
|
|
33
|
+
export { buildMcpRegistryReadiness, inspectMcpRegistryServer, registryReadinessExitCode, renderMcpRegistryReadiness, renderMcpRegistryReadinessJson, type InspectMcpRegistryOptions, type McpRegistryFinding, type McpRegistryInstallability, type McpRegistryReadinessReport, type McpRegistryScorecard } from "./core/mcp-registry.js";
|
|
33
34
|
export { watchPlugin, type WatchPluginOptions, type WatchPluginResult } from "./core/watch-plugin.js";
|
|
34
35
|
export { buildDepAudit, buildDepAuditRecommendations, renderDepAudit, renderDepAuditJson, renderDepAuditSarif, type DepAuditRecommendation, type DepAuditReport, type DepAuditVulnerability } from "./core/dep-audit.js";
|
|
35
36
|
export { initGitHooks, removeGitHooks, type InitGitHooksResult, type RemoveGitHooksResult } from "./core/init-git-hooks.js";
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ export { buildDoctorInspectorReport, renderDoctorInspectorReport, renderDoctorIn
|
|
|
30
30
|
export { buildEcosystemAudit, renderEcosystemAudit, renderEcosystemAuditJson } from "./audit/ecosystem-audit.js";
|
|
31
31
|
export { applyPolicyToDepAudit, applyPolicyToDoctorConfig, applyPolicyToSecurityAudit, parsePolicyPack, policyEnablesRuntime, policyFailsOnWarnings, policyPackNames } from "./policy/policy-packs.js";
|
|
32
32
|
export { buildGenericMcpDoctor, renderGenericMcpDoctor, renderGenericMcpDoctorJson } from "./mcp/generic-mcp-doctor.js";
|
|
33
|
+
export { buildMcpRegistryReadiness, inspectMcpRegistryServer, registryReadinessExitCode, renderMcpRegistryReadiness, renderMcpRegistryReadinessJson } from "./core/mcp-registry.js";
|
|
33
34
|
export { watchPlugin } from "./core/watch-plugin.js";
|
|
34
35
|
export { buildDepAudit, buildDepAuditRecommendations, renderDepAudit, renderDepAuditJson, renderDepAuditSarif } from "./core/dep-audit.js";
|
|
35
36
|
export { initGitHooks, removeGitHooks } from "./core/init-git-hooks.js";
|
|
@@ -17,6 +17,8 @@ export interface GenericMcpDoctorOptions {
|
|
|
17
17
|
runtime?: boolean;
|
|
18
18
|
allowNetwork?: boolean;
|
|
19
19
|
allowLocalNetwork?: boolean;
|
|
20
|
+
allowSessionLifecycle?: boolean;
|
|
21
|
+
requireRemoteReliability?: boolean;
|
|
20
22
|
runtimeStartupTimeoutMs?: number;
|
|
21
23
|
}
|
|
22
24
|
export declare function buildGenericMcpDoctor(targetPath: string, environment?: CompatibilityEnvironment, options?: GenericMcpDoctorOptions): Promise<GenericMcpDoctorReport>;
|
|
@@ -2,7 +2,7 @@ import { realpath, stat } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { buildCompatibilityMatrix, readMcpConfigPath } from "../compatibility/compatibility-matrix.js";
|
|
4
4
|
import { readJsonFile } from "../core/read-json-file.js";
|
|
5
|
-
import { probeRuntimeConfig } from "../core/runtime-probe.js";
|
|
5
|
+
import { probeRuntimeConfig, remoteReliabilityGatePassed } from "../core/runtime-probe.js";
|
|
6
6
|
import { formatFindingFingerprintLine, withFindingFingerprints } from "../reporting/finding-fingerprint.js";
|
|
7
7
|
import { auditMcpServerConfig, buildSecurityAuditFromFindings } from "../security/security-audit.js";
|
|
8
8
|
function buildFinding(severity, id, message, impact, suggestedFix, evidence) {
|
|
@@ -142,11 +142,14 @@ export async function buildGenericMcpDoctor(targetPath, environment = {}, option
|
|
|
142
142
|
? await probeRuntimeConfig(canonicalRootPath, canonicalMcpConfigPath, {
|
|
143
143
|
startupTimeoutMs: options.runtimeStartupTimeoutMs,
|
|
144
144
|
allowNetwork: options.allowNetwork,
|
|
145
|
-
allowLocalNetwork: options.allowLocalNetwork
|
|
145
|
+
allowLocalNetwork: options.allowLocalNetwork,
|
|
146
|
+
allowSessionLifecycle: options.allowSessionLifecycle
|
|
146
147
|
})
|
|
147
148
|
: null;
|
|
148
149
|
const fingerprintedFindings = withFindingFingerprints([...staticFindings, ...(runtimeResult?.findings ?? [])], rootPath);
|
|
149
|
-
const status =
|
|
150
|
+
const status = options.requireRemoteReliability === true && !remoteReliabilityGatePassed(runtimeResult?.scorecard)
|
|
151
|
+
? "fail"
|
|
152
|
+
: mergeReportStatus(fingerprintedFindings, security);
|
|
150
153
|
return {
|
|
151
154
|
targetPath: rootPath,
|
|
152
155
|
status,
|
|
@@ -42,6 +42,19 @@ function appendRuntimeScorecard(lines, result) {
|
|
|
42
42
|
lines.push(`| Protocol headers | ${remote.protocolHeaders.toUpperCase()} |`);
|
|
43
43
|
lines.push(`| Authorization | ${remote.authorization.toUpperCase()} |`);
|
|
44
44
|
lines.push(`| Overall | ${remote.overall.toUpperCase()} |`);
|
|
45
|
+
if (remote.reliability) {
|
|
46
|
+
const reliability = remote.reliability;
|
|
47
|
+
lines.push("", "## Remote Transport Reliability", "");
|
|
48
|
+
lines.push("| Check | Status |");
|
|
49
|
+
lines.push("| --- | --- |");
|
|
50
|
+
lines.push(`| GET SSE | ${reliability.getSse.toUpperCase()} |`);
|
|
51
|
+
lines.push(`| Session propagation | ${reliability.sessionPropagation.toUpperCase()} |`);
|
|
52
|
+
lines.push(`| Resumability | ${reliability.resumability.toUpperCase()} |`);
|
|
53
|
+
lines.push(`| Disconnect safety | ${reliability.disconnectSafety.toUpperCase()} |`);
|
|
54
|
+
lines.push(`| Session restart | ${reliability.sessionRestart.toUpperCase()} |`);
|
|
55
|
+
lines.push(`| Termination | ${reliability.termination.toUpperCase()} |`);
|
|
56
|
+
lines.push(`| Overall | ${reliability.overall.toUpperCase()} |`);
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
59
|
}
|
|
47
60
|
export function buildMarkdownReport(result, options) {
|
|
@@ -59,6 +59,17 @@ function appendRuntimeScorecard(lines, result) {
|
|
|
59
59
|
lines.push(`protocol headers: ${remote.protocolHeaders}`);
|
|
60
60
|
lines.push(`authorization: ${remote.authorization}`);
|
|
61
61
|
lines.push(`overall: ${remote.overall}`);
|
|
62
|
+
if (remote.reliability) {
|
|
63
|
+
const reliability = remote.reliability;
|
|
64
|
+
lines.push("", "Remote Transport Reliability", "----------------------------");
|
|
65
|
+
lines.push(`GET SSE: ${reliability.getSse}`);
|
|
66
|
+
lines.push(`Session propagation: ${reliability.sessionPropagation}`);
|
|
67
|
+
lines.push(`Resumability: ${reliability.resumability}`);
|
|
68
|
+
lines.push(`Disconnect safety: ${reliability.disconnectSafety}`);
|
|
69
|
+
lines.push(`Session restart: ${reliability.sessionRestart}`);
|
|
70
|
+
lines.push(`Termination: ${reliability.termination}`);
|
|
71
|
+
lines.push(`Overall: ${reliability.overall}`);
|
|
72
|
+
}
|
|
62
73
|
}
|
|
63
74
|
}
|
|
64
75
|
export function renderTextReport(result, options = {}) {
|
|
@@ -556,6 +556,114 @@ export const ruleCatalog = [
|
|
|
556
556
|
why: "Authorization readiness cannot be confirmed without bounded metadata responses.",
|
|
557
557
|
fix: "Make protected-resource and authorization-server metadata available over HTTPS.",
|
|
558
558
|
example: "Return application/json discovery metadata."
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
id: "plugin.runtime.remote.reliability.get.status",
|
|
562
|
+
category: "runtime",
|
|
563
|
+
defaultSeverity: "fail",
|
|
564
|
+
summary: "The remote MCP endpoint rejected the SSE transport request.",
|
|
565
|
+
why: "Clients cannot rely on server-to-client streaming when an accepted SSE request does not return the expected status.",
|
|
566
|
+
fix: "Return HTTP 200 with a valid SSE response, or HTTP 405 when GET streaming is not supported.",
|
|
567
|
+
example: "HTTP/1.1 405 Method Not Allowed"
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
id: "plugin.runtime.remote.reliability.get.content_type",
|
|
571
|
+
category: "runtime",
|
|
572
|
+
defaultSeverity: "fail",
|
|
573
|
+
summary: "The remote MCP endpoint returned a non-SSE media type for GET streaming.",
|
|
574
|
+
why: "Clients cannot interpret a successful streaming response unless it is declared as Server-Sent Events.",
|
|
575
|
+
fix: "Return Content-Type: text/event-stream for accepted GET streaming requests.",
|
|
576
|
+
example: "Content-Type: text/event-stream"
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
id: "plugin.runtime.remote.reliability.get.inconclusive",
|
|
580
|
+
category: "runtime",
|
|
581
|
+
defaultSeverity: "warn",
|
|
582
|
+
summary: "The remote MCP GET streaming check was inconclusive.",
|
|
583
|
+
why: "A bounded probe could not observe a complete SSE event, so streaming reliability remains uncertain.",
|
|
584
|
+
fix: "Emit complete SSE event frames promptly when server-to-client streaming is supported.",
|
|
585
|
+
example: "id: event-1\n\ndata: {}\n\n"
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
id: "plugin.runtime.remote.reliability.get.failed",
|
|
589
|
+
category: "runtime",
|
|
590
|
+
defaultSeverity: "fail",
|
|
591
|
+
summary: "The remote MCP GET streaming request could not be completed.",
|
|
592
|
+
why: "Clients cannot rely on server-to-client streaming when a bounded transport request fails.",
|
|
593
|
+
fix: "Keep the Streamable HTTP transport reachable within the configured request bounds.",
|
|
594
|
+
example: "Complete the GET request before the runtime timeout."
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
id: "plugin.runtime.remote.reliability.get.malformed",
|
|
598
|
+
category: "runtime",
|
|
599
|
+
defaultSeverity: "fail",
|
|
600
|
+
summary: "The remote MCP endpoint emitted malformed SSE framing.",
|
|
601
|
+
why: "Malformed event framing prevents clients from safely processing stream progress or reconnecting.",
|
|
602
|
+
fix: "Return complete SSE events with valid id and retry fields.",
|
|
603
|
+
example: "id: event-1\n\nevent: message\n\ndata: {}\n\n"
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
id: "plugin.runtime.remote.reliability.resume.status",
|
|
607
|
+
category: "runtime",
|
|
608
|
+
defaultSeverity: "fail",
|
|
609
|
+
summary: "The remote MCP endpoint rejected the SSE resume request.",
|
|
610
|
+
why: "Clients cannot resume a dropped stream when the reconnect response has an unexpected status.",
|
|
611
|
+
fix: "Return HTTP 200 for an accepted Last-Event-ID reconnect request.",
|
|
612
|
+
example: "HTTP/1.1 200 OK"
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
id: "plugin.runtime.remote.reliability.resume.content_type",
|
|
616
|
+
category: "runtime",
|
|
617
|
+
defaultSeverity: "fail",
|
|
618
|
+
summary: "The remote MCP endpoint returned a non-SSE media type for a resume request.",
|
|
619
|
+
why: "Clients cannot process a resumed stream unless it is declared as Server-Sent Events.",
|
|
620
|
+
fix: "Return Content-Type: text/event-stream after accepting a Last-Event-ID reconnect request.",
|
|
621
|
+
example: "Content-Type: text/event-stream"
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
id: "plugin.runtime.remote.reliability.resume.inconclusive",
|
|
625
|
+
category: "runtime",
|
|
626
|
+
defaultSeverity: "warn",
|
|
627
|
+
summary: "The remote MCP SSE resume check was inconclusive.",
|
|
628
|
+
why: "A bounded probe could not confirm a complete resumed event, so reconnect reliability remains uncertain.",
|
|
629
|
+
fix: "Emit complete SSE event frames after accepting a reconnect.",
|
|
630
|
+
example: "Return one complete SSE event after a Last-Event-ID request."
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
id: "plugin.runtime.remote.reliability.resume.failed",
|
|
634
|
+
category: "runtime",
|
|
635
|
+
defaultSeverity: "fail",
|
|
636
|
+
summary: "The remote MCP SSE resume request could not be completed.",
|
|
637
|
+
why: "Clients cannot recover from a dropped stream when a bounded reconnect fails.",
|
|
638
|
+
fix: "Accept one bounded SSE reconnect using Last-Event-ID.",
|
|
639
|
+
example: "Honor the Last-Event-ID header on one reconnect."
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
id: "plugin.runtime.remote.reliability.resume.malformed",
|
|
643
|
+
category: "runtime",
|
|
644
|
+
defaultSeverity: "fail",
|
|
645
|
+
summary: "The remote MCP endpoint emitted malformed SSE framing after reconnecting.",
|
|
646
|
+
why: "Malformed resumed events prevent clients from safely continuing stream processing.",
|
|
647
|
+
fix: "Return complete SSE events with valid id and retry fields after reconnecting.",
|
|
648
|
+
example: "Return a complete event frame after reconnecting."
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
id: "plugin.runtime.remote.reliability.session_restart.failed",
|
|
652
|
+
category: "runtime",
|
|
653
|
+
defaultSeverity: "fail",
|
|
654
|
+
summary: "The remote MCP session could not be restarted.",
|
|
655
|
+
why: "Clients cannot recover from an expired MCP session when a fresh initialization sequence fails.",
|
|
656
|
+
fix: "Accept a fresh initialize sequence after an expired MCP session.",
|
|
657
|
+
example: "Return a valid initialize response after HTTP 404 for a session-bound request."
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
id: "plugin.runtime.remote.reliability.termination.failed",
|
|
661
|
+
category: "runtime",
|
|
662
|
+
defaultSeverity: "fail",
|
|
663
|
+
summary: "The remote MCP session could not be terminated.",
|
|
664
|
+
why: "A server that advertises session lifecycle support must safely handle the approved termination request.",
|
|
665
|
+
fix: "Return a successful response or HTTP 405 for a bounded MCP session DELETE request.",
|
|
666
|
+
example: "HTTP/1.1 204 No Content"
|
|
559
667
|
}
|
|
560
668
|
];
|
|
561
669
|
export function findRuleDefinition(id) {
|
package/dist/run-cli.js
CHANGED
|
@@ -28,6 +28,7 @@ import { buildDoctorRuntimePolicyReport, renderDoctorRuntimePolicy, renderDoctor
|
|
|
28
28
|
import { buildDoctorReviewBundle, diffDoctorReviewBundles, renderDoctorReviewBundle, renderDoctorReviewBundleDiff, renderDoctorReviewBundleDiffJson, renderDoctorReviewBundleJson, renderDoctorReviewBundleVerification, renderDoctorReviewBundleVerificationJson, verifyDoctorReviewBundle } from "./core/review-bundle.js";
|
|
29
29
|
import { buildDoctorReleaseEvidenceAssetReport, buildDoctorReleaseEvidenceReport, renderDoctorReleaseEvidenceAsset, renderDoctorReleaseEvidenceAssetJson, renderDoctorReleaseEvidence, renderDoctorReleaseEvidenceJson, renderDoctorReleaseEvidenceVerification, renderDoctorReleaseEvidenceVerificationJson, verifyDoctorReleaseEvidence } from "./core/release-evidence.js";
|
|
30
30
|
import { buildDoctorNpmPackageReport, renderDoctorNpmPackageReport, renderDoctorNpmPackageReportJson } from "./core/npm-package-doctor.js";
|
|
31
|
+
import { buildMcpRegistryReadiness, inspectMcpRegistryServer, registryReadinessExitCode, renderMcpRegistryReadiness, renderMcpRegistryReadinessJson } from "./core/mcp-registry.js";
|
|
31
32
|
import { buildDoctorRiskDiffReport, renderDoctorRiskDiffReport, renderDoctorRiskDiffReportJson } from "./core/risk-diff.js";
|
|
32
33
|
import { buildDoctorInspectorReport, renderDoctorInspectorReport, renderDoctorInspectorReportJson } from "./core/inspector-bridge.js";
|
|
33
34
|
import { applyFixPlan, buildFixPlan, renderApplyFixResult, renderFixPlanJsonReport, renderFixPlan } from "./core/fix-plan.js";
|
|
@@ -102,6 +103,8 @@ function parseRuntimeSandbox(flags, options = { requireRuntime: true }) {
|
|
|
102
103
|
function parseRemoteNetworkFlags(flags, runtime) {
|
|
103
104
|
let allowNetwork = false;
|
|
104
105
|
let allowLocalNetwork = false;
|
|
106
|
+
let allowSessionLifecycle = false;
|
|
107
|
+
let requireRemoteReliability = false;
|
|
105
108
|
for (let index = 0; index < flags.length; index += 1) {
|
|
106
109
|
const flag = flags[index];
|
|
107
110
|
if (flag === "--allow-network") {
|
|
@@ -120,23 +123,57 @@ function parseRemoteNetworkFlags(flags, runtime) {
|
|
|
120
123
|
}
|
|
121
124
|
allowLocalNetwork = true;
|
|
122
125
|
}
|
|
123
|
-
else if (flag
|
|
126
|
+
else if (flag === "--allow-session-lifecycle") {
|
|
127
|
+
if (allowSessionLifecycle)
|
|
128
|
+
return new CliUsageError("Duplicate runtime remote flag: --allow-session-lifecycle.");
|
|
129
|
+
if (flags[index + 1] && !flags[index + 1].startsWith("--")) {
|
|
130
|
+
return new CliUsageError("--allow-session-lifecycle does not accept a value.");
|
|
131
|
+
}
|
|
132
|
+
allowSessionLifecycle = true;
|
|
133
|
+
}
|
|
134
|
+
else if (flag === "--require-remote-reliability") {
|
|
135
|
+
if (requireRemoteReliability)
|
|
136
|
+
return new CliUsageError("Duplicate runtime remote flag: --require-remote-reliability.");
|
|
137
|
+
if (flags[index + 1] && !flags[index + 1].startsWith("--")) {
|
|
138
|
+
return new CliUsageError("--require-remote-reliability does not accept a value.");
|
|
139
|
+
}
|
|
140
|
+
requireRemoteReliability = true;
|
|
141
|
+
}
|
|
142
|
+
else if (flag?.startsWith("--allow-network=") ||
|
|
143
|
+
flag?.startsWith("--allow-local-network=") ||
|
|
144
|
+
flag?.startsWith("--allow-session-lifecycle=") ||
|
|
145
|
+
flag?.startsWith("--require-remote-reliability=")) {
|
|
124
146
|
return new CliUsageError(`${flag.split("=", 1)[0]} does not accept a value.`);
|
|
125
147
|
}
|
|
126
148
|
}
|
|
149
|
+
if (allowSessionLifecycle && !runtime) {
|
|
150
|
+
return new CliUsageError("--allow-session-lifecycle requires --runtime.");
|
|
151
|
+
}
|
|
152
|
+
if (requireRemoteReliability && !runtime) {
|
|
153
|
+
return new CliUsageError("--require-remote-reliability requires --runtime and --allow-network.");
|
|
154
|
+
}
|
|
127
155
|
if ((allowNetwork || allowLocalNetwork) && !runtime) {
|
|
128
156
|
return new CliUsageError("--allow-network requires --runtime.");
|
|
129
157
|
}
|
|
130
158
|
if (allowLocalNetwork && !allowNetwork) {
|
|
131
159
|
return new CliUsageError("--allow-local-network requires --allow-network.");
|
|
132
160
|
}
|
|
133
|
-
|
|
161
|
+
if (allowSessionLifecycle && !allowNetwork) {
|
|
162
|
+
return new CliUsageError("--allow-session-lifecycle requires --allow-network.");
|
|
163
|
+
}
|
|
164
|
+
if (requireRemoteReliability && !allowNetwork) {
|
|
165
|
+
return new CliUsageError("--require-remote-reliability requires --runtime and --allow-network.");
|
|
166
|
+
}
|
|
167
|
+
return { allowNetwork, allowLocalNetwork, allowSessionLifecycle, requireRemoteReliability };
|
|
134
168
|
}
|
|
135
169
|
function printUsage(io) {
|
|
136
170
|
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");
|
|
171
|
+
io.writeStderr("Registry readiness: codex-plugin-doctor registry check <server.json|directory> [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
172
|
+
+ " codex-plugin-doctor registry inspect <server-name> --allow-network [--json] [--output <path>] [--require-registry-readiness]");
|
|
137
173
|
io.writeStderr("Corpus quality regression: codex-plugin-doctor doctor corpus metrics diff --before <metrics.json> --after <metrics.json> [--fail-on-regression] [--json|--markdown] [--output <path>]");
|
|
138
174
|
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]");
|
|
139
175
|
io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
|
|
176
|
+
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.");
|
|
140
177
|
}
|
|
141
178
|
const suppressUsageText = [
|
|
142
179
|
"Usage:",
|
|
@@ -687,6 +724,8 @@ function buildGenericMcpDoctorCommandArgs(commandTarget, flags) {
|
|
|
687
724
|
let runtime = false;
|
|
688
725
|
let allowNetwork = false;
|
|
689
726
|
let allowLocalNetwork = false;
|
|
727
|
+
let allowSessionLifecycle = false;
|
|
728
|
+
let requireRemoteReliability = false;
|
|
690
729
|
for (let index = 0; index < flags.length; index += 1) {
|
|
691
730
|
const flag = flags[index];
|
|
692
731
|
if (flag === "--runtime") {
|
|
@@ -715,6 +754,26 @@ function buildGenericMcpDoctorCommandArgs(commandTarget, flags) {
|
|
|
715
754
|
allowLocalNetwork = true;
|
|
716
755
|
continue;
|
|
717
756
|
}
|
|
757
|
+
if (flag === "--allow-session-lifecycle") {
|
|
758
|
+
if (flags[index + 1] && !flags[index + 1].startsWith("--")) {
|
|
759
|
+
return "--allow-session-lifecycle does not accept a value.";
|
|
760
|
+
}
|
|
761
|
+
allowSessionLifecycle = true;
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (flag === "--require-remote-reliability") {
|
|
765
|
+
if (flags[index + 1] && !flags[index + 1].startsWith("--")) {
|
|
766
|
+
return "--require-remote-reliability does not accept a value.";
|
|
767
|
+
}
|
|
768
|
+
requireRemoteReliability = true;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (flag.startsWith("--allow-network=") ||
|
|
772
|
+
flag.startsWith("--allow-local-network=") ||
|
|
773
|
+
flag.startsWith("--allow-session-lifecycle=") ||
|
|
774
|
+
flag.startsWith("--require-remote-reliability=")) {
|
|
775
|
+
return `${flag.split("=", 1)[0]} does not accept a value.`;
|
|
776
|
+
}
|
|
718
777
|
if (flag === "--output") {
|
|
719
778
|
if (outputPath !== null) {
|
|
720
779
|
return "Duplicate MCP flag: --output.";
|
|
@@ -740,8 +799,10 @@ function buildGenericMcpDoctorCommandArgs(commandTarget, flags) {
|
|
|
740
799
|
jsonOutput,
|
|
741
800
|
outputPath,
|
|
742
801
|
runtime,
|
|
743
|
-
allowNetwork,
|
|
744
|
-
allowLocalNetwork
|
|
802
|
+
allowNetwork: remoteNetwork.allowNetwork,
|
|
803
|
+
allowLocalNetwork: remoteNetwork.allowLocalNetwork,
|
|
804
|
+
allowSessionLifecycle,
|
|
805
|
+
requireRemoteReliability
|
|
745
806
|
};
|
|
746
807
|
}
|
|
747
808
|
function parsePerformanceThresholds(flags) {
|
|
@@ -1031,7 +1092,9 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1031
1092
|
}, {
|
|
1032
1093
|
runtime: parsedMcpArgs.runtime,
|
|
1033
1094
|
allowNetwork: parsedMcpArgs.allowNetwork,
|
|
1034
|
-
allowLocalNetwork: parsedMcpArgs.allowLocalNetwork
|
|
1095
|
+
allowLocalNetwork: parsedMcpArgs.allowLocalNetwork,
|
|
1096
|
+
allowSessionLifecycle: parsedMcpArgs.allowSessionLifecycle,
|
|
1097
|
+
requireRemoteReliability: parsedMcpArgs.requireRemoteReliability
|
|
1035
1098
|
});
|
|
1036
1099
|
const renderedReport = parsedMcpArgs.jsonOutput
|
|
1037
1100
|
? renderGenericMcpDoctorJson(report)
|
|
@@ -1344,6 +1407,8 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1344
1407
|
runtime,
|
|
1345
1408
|
...(remoteNetwork.allowNetwork ? { allowNetwork: true } : {}),
|
|
1346
1409
|
...(remoteNetwork.allowLocalNetwork ? { allowLocalNetwork: true } : {}),
|
|
1410
|
+
...(remoteNetwork.allowSessionLifecycle ? { allowSessionLifecycle: true } : {}),
|
|
1411
|
+
...(remoteNetwork.requireRemoteReliability ? { requireRemoteReliability: true } : {}),
|
|
1347
1412
|
...(runtimeSandbox ? { sandbox: runtimeSandbox } : {}),
|
|
1348
1413
|
environment: {
|
|
1349
1414
|
env: terminalContext.env,
|
|
@@ -1507,6 +1572,8 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
1507
1572
|
runtime,
|
|
1508
1573
|
...(remoteNetwork.allowNetwork ? { allowNetwork: true } : {}),
|
|
1509
1574
|
...(remoteNetwork.allowLocalNetwork ? { allowLocalNetwork: true } : {}),
|
|
1575
|
+
...(remoteNetwork.allowSessionLifecycle ? { allowSessionLifecycle: true } : {}),
|
|
1576
|
+
...(remoteNetwork.requireRemoteReliability ? { requireRemoteReliability: true } : {}),
|
|
1510
1577
|
...(runtimeSandbox ? { sandbox: runtimeSandbox } : {}),
|
|
1511
1578
|
environment: {
|
|
1512
1579
|
env: terminalContext.env,
|
|
@@ -2292,6 +2359,65 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2292
2359
|
: renderApplyFixResult(result));
|
|
2293
2360
|
return 0;
|
|
2294
2361
|
}
|
|
2362
|
+
if (command === "registry") {
|
|
2363
|
+
const subcommand = maybePath;
|
|
2364
|
+
const target = remainingArgs[0];
|
|
2365
|
+
const flags = remainingArgs.slice(1);
|
|
2366
|
+
if ((subcommand !== "check" && subcommand !== "inspect") || !target || target.startsWith("--")) {
|
|
2367
|
+
io.writeStderr("Usage: codex-plugin-doctor registry check <server.json|directory> [--json] [--output <path>] [--require-registry-readiness]\n"
|
|
2368
|
+
+ " codex-plugin-doctor registry inspect <server-name> --allow-network [--json] [--output <path>] [--require-registry-readiness]");
|
|
2369
|
+
return 2;
|
|
2370
|
+
}
|
|
2371
|
+
const allowedFlags = new Set([
|
|
2372
|
+
"--json",
|
|
2373
|
+
"--output",
|
|
2374
|
+
"--allow-network",
|
|
2375
|
+
"--require-registry-readiness"
|
|
2376
|
+
]);
|
|
2377
|
+
let outputPath = null;
|
|
2378
|
+
for (let index = 0; index < flags.length; index += 1) {
|
|
2379
|
+
const flag = flags[index];
|
|
2380
|
+
if (!allowedFlags.has(flag)) {
|
|
2381
|
+
io.writeStderr(`Unknown registry flag: ${flag}.`);
|
|
2382
|
+
return 2;
|
|
2383
|
+
}
|
|
2384
|
+
if (flag === "--output") {
|
|
2385
|
+
const value = flags[index + 1];
|
|
2386
|
+
if (!value || value.startsWith("--")) {
|
|
2387
|
+
io.writeStderr("Missing path after --output.");
|
|
2388
|
+
return 2;
|
|
2389
|
+
}
|
|
2390
|
+
outputPath = value;
|
|
2391
|
+
index += 1;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
const allowNetwork = flags.includes("--allow-network");
|
|
2395
|
+
if (subcommand === "check" && allowNetwork) {
|
|
2396
|
+
io.writeStderr("--allow-network is supported only by registry inspect.");
|
|
2397
|
+
return 2;
|
|
2398
|
+
}
|
|
2399
|
+
if (subcommand === "inspect" && !allowNetwork) {
|
|
2400
|
+
io.writeStderr("registry inspect requires explicit --allow-network consent.");
|
|
2401
|
+
return 2;
|
|
2402
|
+
}
|
|
2403
|
+
try {
|
|
2404
|
+
const report = subcommand === "check"
|
|
2405
|
+
? await buildMcpRegistryReadiness(target)
|
|
2406
|
+
: await inspectMcpRegistryServer(target, { allowNetwork: true });
|
|
2407
|
+
const rendered = flags.includes("--json")
|
|
2408
|
+
? renderMcpRegistryReadinessJson(report)
|
|
2409
|
+
: renderMcpRegistryReadiness(report);
|
|
2410
|
+
if (outputPath) {
|
|
2411
|
+
await writeFile(outputPath, rendered, "utf8");
|
|
2412
|
+
}
|
|
2413
|
+
io.writeStdout(rendered);
|
|
2414
|
+
return registryReadinessExitCode(report, flags.includes("--require-registry-readiness"));
|
|
2415
|
+
}
|
|
2416
|
+
catch (error) {
|
|
2417
|
+
io.writeStderr(`Registry inspection failed: ${error.message}`);
|
|
2418
|
+
return 1;
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2295
2421
|
if (command === "security") {
|
|
2296
2422
|
if (!maybePath || maybePath.startsWith("--")) {
|
|
2297
2423
|
io.writeStderr("Missing target path. Usage: codex-plugin-doctor security <path> [--json|--scorecard]");
|
|
@@ -2332,7 +2458,9 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2332
2458
|
}, {
|
|
2333
2459
|
runtime: parsedMcpArgs.runtime,
|
|
2334
2460
|
allowNetwork: parsedMcpArgs.allowNetwork,
|
|
2335
|
-
allowLocalNetwork: parsedMcpArgs.allowLocalNetwork
|
|
2461
|
+
allowLocalNetwork: parsedMcpArgs.allowLocalNetwork,
|
|
2462
|
+
allowSessionLifecycle: parsedMcpArgs.allowSessionLifecycle,
|
|
2463
|
+
requireRemoteReliability: parsedMcpArgs.requireRemoteReliability
|
|
2336
2464
|
});
|
|
2337
2465
|
const renderedReport = parsedMcpArgs.jsonOutput
|
|
2338
2466
|
? renderGenericMcpDoctorJson(report)
|
|
@@ -2653,6 +2781,8 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2653
2781
|
runtime: runtimeProbeEnabled,
|
|
2654
2782
|
...(remoteNetwork.allowNetwork ? { allowNetwork: true } : {}),
|
|
2655
2783
|
...(remoteNetwork.allowLocalNetwork ? { allowLocalNetwork: true } : {}),
|
|
2784
|
+
...(remoteNetwork.allowSessionLifecycle ? { allowSessionLifecycle: true } : {}),
|
|
2785
|
+
...(remoteNetwork.requireRemoteReliability ? { requireRemoteReliability: true } : {}),
|
|
2656
2786
|
...(runtimeSandbox ? { runtimeSandbox } : {}),
|
|
2657
2787
|
runCheck: options.runCheckImpl
|
|
2658
2788
|
});
|
|
@@ -2867,6 +2997,8 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2867
2997
|
runtime: effectiveRuntimeProbeEnabled,
|
|
2868
2998
|
...(remoteNetwork.allowNetwork ? { allowNetwork: true } : {}),
|
|
2869
2999
|
...(remoteNetwork.allowLocalNetwork ? { allowLocalNetwork: true } : {}),
|
|
3000
|
+
...(remoteNetwork.allowSessionLifecycle ? { allowSessionLifecycle: true } : {}),
|
|
3001
|
+
...(remoteNetwork.requireRemoteReliability ? { requireRemoteReliability: true } : {}),
|
|
2870
3002
|
runtimeTranscript: effectiveRuntimeProbeEnabled && verboseRuntime
|
|
2871
3003
|
? (line) => io.writeStderr(line)
|
|
2872
3004
|
: undefined
|
|
@@ -2938,7 +3070,9 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2938
3070
|
const pluginResult = applyDoctorConfig(await runCheckImpl(pluginRoot, {
|
|
2939
3071
|
runtime: effectiveRuntimeProbeEnabled,
|
|
2940
3072
|
...(remoteNetwork.allowNetwork ? { allowNetwork: true } : {}),
|
|
2941
|
-
...(remoteNetwork.allowLocalNetwork ? { allowLocalNetwork: true } : {})
|
|
3073
|
+
...(remoteNetwork.allowLocalNetwork ? { allowLocalNetwork: true } : {}),
|
|
3074
|
+
...(remoteNetwork.allowSessionLifecycle ? { allowSessionLifecycle: true } : {}),
|
|
3075
|
+
...(remoteNetwork.requireRemoteReliability ? { requireRemoteReliability: true } : {})
|
|
2942
3076
|
}), applyPolicyToDoctorConfig(applyCheckProfile(await loadDoctorConfig(pluginRoot, configPath), checkProfile), policy));
|
|
2943
3077
|
io.writeStdout(`\n${pluginResult.targetPath}: ${pluginResult.status.toUpperCase()}`);
|
|
2944
3078
|
validatedCount += 1;
|
|
@@ -2962,6 +3096,8 @@ export async function runCli(args, io = defaultIo, options = {}) {
|
|
|
2962
3096
|
runtime: effectiveRuntimeProbeEnabled,
|
|
2963
3097
|
...(remoteNetwork.allowNetwork ? { allowNetwork: true } : {}),
|
|
2964
3098
|
...(remoteNetwork.allowLocalNetwork ? { allowLocalNetwork: true } : {}),
|
|
3099
|
+
...(remoteNetwork.allowSessionLifecycle ? { allowSessionLifecycle: true } : {}),
|
|
3100
|
+
...(remoteNetwork.requireRemoteReliability ? { requireRemoteReliability: true } : {}),
|
|
2965
3101
|
...(runtimeSandbox ? { runtimeSandbox } : {}),
|
|
2966
3102
|
...(effectiveRuntimeProbeEnabled && verboseRuntime
|
|
2967
3103
|
? { runtimeTranscript: (line) => io.writeStderr(line) }
|