fix: enforce strict config validation

This commit is contained in:
Peter Steinberger
2026-01-19 03:38:51 +00:00
parent a9fc2ca0ef
commit d1e9490f95
53 changed files with 1025 additions and 821 deletions

View File

@@ -1,65 +1,67 @@
import {
isNixMode,
loadConfig,
migrateLegacyConfig,
readConfigFileSnapshot,
writeConfigFile,
} from "../../config/config.js";
import { danger } from "../../globals.js";
import { autoMigrateLegacyState } from "../../infra/state-migrations.js";
import { readConfigFileSnapshot } from "../../config/config.js";
import { loadAndMaybeMigrateDoctorConfig } from "../../commands/doctor-config-flow.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
import { loadClawdbotPlugins } from "../../plugins/loader.js";
import type { RuntimeEnv } from "../../runtime.js";
const ALLOWED_INVALID_COMMANDS = new Set(["doctor", "logs", "health", "help", "status", "service"]);
function formatConfigIssues(issues: Array<{ path: string; message: string }>): string[] {
return issues.map((issue) => `- ${issue.path || "<root>"}: ${issue.message}`);
}
export async function ensureConfigReady(params: {
runtime: RuntimeEnv;
migrateState?: boolean;
commandPath?: string[];
}): Promise<void> {
await loadAndMaybeMigrateDoctorConfig({
options: { nonInteractive: true },
confirm: async () => false,
});
const snapshot = await readConfigFileSnapshot();
if (snapshot.legacyIssues.length > 0) {
if (isNixMode) {
params.runtime.error(
danger(
"Legacy config entries detected while running in Nix mode. Update your Nix config to the latest schema and retry.",
),
);
params.runtime.exit(1);
return;
}
const migrated = migrateLegacyConfig(snapshot.parsed);
if (migrated.config) {
await writeConfigFile(migrated.config);
if (migrated.changes.length > 0) {
params.runtime.log(
`Migrated legacy config entries:\n${migrated.changes
.map((entry) => `- ${entry}`)
.join("\n")}`,
);
}
} else {
const issues = snapshot.legacyIssues
.map((issue) => `- ${issue.path}: ${issue.message}`)
.join("\n");
params.runtime.error(
danger(
`Legacy config entries detected. Run "clawdbot doctor" (or ask your agent) to migrate.\n${issues}`,
),
);
params.runtime.exit(1);
return;
const command = params.commandPath?.[0];
const allowInvalid = command ? ALLOWED_INVALID_COMMANDS.has(command) : false;
const issues = snapshot.exists && !snapshot.valid ? formatConfigIssues(snapshot.issues) : [];
const legacyIssues =
snapshot.legacyIssues.length > 0
? snapshot.legacyIssues.map((issue) => `- ${issue.path}: ${issue.message}`)
: [];
const pluginIssues: string[] = [];
if (snapshot.valid) {
const workspaceDir = resolveAgentWorkspaceDir(
snapshot.config,
resolveDefaultAgentId(snapshot.config),
);
const registry = loadClawdbotPlugins({
config: snapshot.config,
workspaceDir: workspaceDir ?? undefined,
cache: false,
mode: "validate",
});
for (const diag of registry.diagnostics) {
if (diag.level !== "error") continue;
const id = diag.pluginId ? ` ${diag.pluginId}` : "";
pluginIssues.push(`- plugin${id}: ${diag.message}`);
}
}
if (snapshot.exists && !snapshot.valid) {
params.runtime.error(`Config invalid at ${snapshot.path}.`);
for (const issue of snapshot.issues) {
params.runtime.error(`- ${issue.path || "<root>"}: ${issue.message}`);
}
params.runtime.error("Run `clawdbot doctor` to repair, then retry.");
const invalid = snapshot.exists && (!snapshot.valid || pluginIssues.length > 0);
if (!invalid) return;
params.runtime.error(`Config invalid at ${snapshot.path}.`);
if (issues.length > 0) {
params.runtime.error(issues.join("\n"));
}
if (legacyIssues.length > 0) {
params.runtime.error(`Legacy config keys detected:\n${legacyIssues.join("\n")}`);
}
if (pluginIssues.length > 0) {
params.runtime.error(`Plugin config errors:\n${pluginIssues.join("\n")}`);
}
params.runtime.error("Run `clawdbot doctor --fix` to repair, then retry.");
if (!allowInvalid) {
params.runtime.exit(1);
return;
}
if (params.migrateState !== false) {
const cfg = loadConfig();
await autoMigrateLegacyState({ cfg });
}
}