mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-05 21:22:56 +00:00
fix: surface disabled codex plugin routes in doctor lint
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resetCoreHealthChecksForTest } from "../flows/doctor-core-checks.js";
|
||||
import { clearHealthChecksForTest } from "../flows/health-check-registry.js";
|
||||
import { runDoctorLintCli } from "./doctor-lint.js";
|
||||
@@ -141,6 +142,55 @@ describe("runDoctorLintCli", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("reports disabled Codex plugin routes through doctor lint", async () => {
|
||||
mocks.readConfigFileSnapshot.mockResolvedValue({
|
||||
exists: true,
|
||||
valid: true,
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
codex: { enabled: false },
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "gpt-5.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig,
|
||||
path: "/tmp/openclaw.json",
|
||||
});
|
||||
|
||||
const stdout = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
||||
try {
|
||||
const exitCode = await runDoctorLintCli(runtime, {
|
||||
json: true,
|
||||
onlyIds: ["core/doctor/codex-session-routes"],
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
const payload = JSON.parse(String(stdout.mock.calls.at(-1)?.[0]));
|
||||
expect(payload).toMatchObject({
|
||||
ok: false,
|
||||
checksRun: 1,
|
||||
findings: [
|
||||
{
|
||||
checkId: "core/doctor/codex-session-routes",
|
||||
severity: "warning",
|
||||
path: "agents.defaults.model.primary",
|
||||
target: "openai/gpt-5.5",
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(payload.findings[0].message).toContain("Codex plugin is disabled by config");
|
||||
expect(payload.findings[0].fixHint).toContain("openclaw doctor --fix");
|
||||
} finally {
|
||||
stdout.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects invalid severity thresholds", async () => {
|
||||
await expect(runDoctorLintCli(runtime, { severityMin: "warnng" })).rejects.toThrow(
|
||||
"Invalid --severity-min value",
|
||||
|
||||
@@ -43,6 +43,12 @@ type DisabledCodexPluginRouteHit = {
|
||||
modelRef: string;
|
||||
canonicalModel: string;
|
||||
};
|
||||
export type DisabledCodexPluginRouteIssue = {
|
||||
path: string;
|
||||
modelRef: string;
|
||||
canonicalModel: string;
|
||||
blockedOutsideEntry: boolean;
|
||||
};
|
||||
type SharedDefaultCompactionOverrideConsumers = Record<CompactionOverrideKey, boolean>;
|
||||
|
||||
type MutableRecord = Record<string, unknown>;
|
||||
@@ -1173,6 +1179,18 @@ function collectDisabledCodexPluginRouteHits(cfg: OpenClawConfig): DisabledCodex
|
||||
return hits;
|
||||
}
|
||||
|
||||
export function collectDisabledCodexPluginRouteIssues(
|
||||
cfg: OpenClawConfig,
|
||||
): DisabledCodexPluginRouteIssue[] {
|
||||
const blockedOutsideEntry = codexPluginIsBlockedOutsideEntry(cfg);
|
||||
return collectDisabledCodexPluginRouteHits(cfg).map((hit) => ({
|
||||
path: hit.path,
|
||||
modelRef: hit.modelRef,
|
||||
canonicalModel: hit.canonicalModel,
|
||||
blockedOutsideEntry,
|
||||
}));
|
||||
}
|
||||
|
||||
function enableCodexPluginForRequiredRoutes(params: {
|
||||
cfg: OpenClawConfig;
|
||||
routeHits: DisabledCodexPluginRouteHit[];
|
||||
|
||||
@@ -327,6 +327,45 @@ describe("registerCoreHealthChecks", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("reports disabled Codex plugin routes as core health findings", async () => {
|
||||
const check = getCheck(
|
||||
createCoreHealthChecks(createDeps()),
|
||||
"core/doctor/codex-session-routes",
|
||||
);
|
||||
|
||||
const findings = await check.detect({
|
||||
mode: "lint",
|
||||
runtime,
|
||||
cfg: {
|
||||
plugins: {
|
||||
entries: {
|
||||
codex: { enabled: false },
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "gpt-5.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig,
|
||||
});
|
||||
|
||||
expect(findings).toStrictEqual([
|
||||
expect.objectContaining({
|
||||
checkId: "core/doctor/codex-session-routes",
|
||||
severity: "warning",
|
||||
path: "agents.defaults.model.primary",
|
||||
target: "openai/gpt-5.5",
|
||||
requirement: "Codex plugin enabled for routes that use the Codex runtime.",
|
||||
fixHint:
|
||||
"Run `openclaw doctor --fix`: it enables plugins.entries.codex, or set the affected OpenAI models to an OpenClaw runtime policy.",
|
||||
}),
|
||||
]);
|
||||
expect(findings[0]?.message).toContain("Codex plugin is disabled by config");
|
||||
});
|
||||
|
||||
it("uses the read-only model catalog for hooks.gmail.model checks", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: {
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
uiProtocolFreshnessIssueToHealthFinding,
|
||||
uiProtocolFreshnessIssueToRepairEffects,
|
||||
} from "../commands/doctor-ui.js";
|
||||
import { collectDisabledCodexPluginRouteIssues } from "../commands/doctor/shared/codex-route-warnings.js";
|
||||
import type { ConfigValidationIssue, OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveSecretInputRef, type SecretRef } from "../config/types.secrets.js";
|
||||
import { hasAmbiguousGatewayAuthModeConfig } from "../gateway/auth-mode-policy.js";
|
||||
@@ -29,6 +30,7 @@ import { registerHealthCheck } from "./health-check-registry.js";
|
||||
import type { HealthCheck, HealthCheckContext, HealthFinding } from "./health-checks.js";
|
||||
|
||||
const BROWSER_CLAWD_PROFILE_RESIDUE_CHECK_ID = "core/doctor/browser-clawd-profile-residue";
|
||||
const CODEX_SESSION_ROUTES_CHECK_ID = "core/doctor/codex-session-routes";
|
||||
const FINAL_CONFIG_VALIDATION_CHECK_ID = "core/doctor/final-config-validation";
|
||||
|
||||
const loadDoctorCoreChecksRuntimeModule = async () =>
|
||||
@@ -616,6 +618,37 @@ const legacyWhatsAppCrontabCheck: HealthCheck = {
|
||||
},
|
||||
};
|
||||
|
||||
const codexSessionRoutesCheck: HealthCheck = {
|
||||
id: CODEX_SESSION_ROUTES_CHECK_ID,
|
||||
kind: "core",
|
||||
description: "Codex runtime routes have a registered Codex plugin harness before sessions run.",
|
||||
source: "doctor",
|
||||
async detect(ctx) {
|
||||
return collectDisabledCodexPluginRouteIssues(ctx.cfg).map(
|
||||
(issue): HealthFinding => ({
|
||||
checkId: CODEX_SESSION_ROUTES_CHECK_ID,
|
||||
severity: "warning",
|
||||
message: [
|
||||
`${issue.path} routes ${issue.modelRef} to ${issue.canonicalModel}`,
|
||||
"with Codex runtime, but the Codex plugin is disabled by config.",
|
||||
].join(" "),
|
||||
path: issue.path,
|
||||
target: issue.canonicalModel,
|
||||
requirement: "Codex plugin enabled for routes that use the Codex runtime.",
|
||||
fixHint: issue.blockedOutsideEntry
|
||||
? [
|
||||
"Enable plugin loading and remove codex from plugins.deny,",
|
||||
"or set the affected OpenAI models to an OpenClaw runtime policy.",
|
||||
].join(" ")
|
||||
: [
|
||||
"Run `openclaw doctor --fix`: it enables plugins.entries.codex,",
|
||||
"or set the affected OpenAI models to an OpenClaw runtime policy.",
|
||||
].join(" "),
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const gatewayPlatformNotesCheck: HealthCheck = {
|
||||
id: "core/doctor/gateway-services/platform-notes",
|
||||
kind: "core",
|
||||
@@ -913,6 +946,7 @@ function createConvertedWorkflowChecks(deps: CoreHealthCheckDeps): readonly Heal
|
||||
gatewayAuthCheck,
|
||||
legacyStateCheck,
|
||||
legacyWhatsAppCrontabCheck,
|
||||
codexSessionRoutesCheck,
|
||||
shellCompletionCheck,
|
||||
uiProtocolFreshnessCheck,
|
||||
gatewayPlatformNotesCheck,
|
||||
|
||||
Reference in New Issue
Block a user