fix(doctor): warn when discord repair is blocked

This commit is contained in:
Altay
2026-04-01 20:24:09 +03:00
parent 21463240b5
commit 099ba514a1
4 changed files with 104 additions and 4 deletions

View File

@@ -194,6 +194,26 @@ describe("doctor discord provider repairs", () => {
expect(result.config.channels?.discord?.dm?.allowFrom).toEqual(["99"]);
});
it("returns repair warnings when unsafe numeric ids block doctor fix", () => {
const cfg = {
channels: {
discord: {
allowFrom: [106232522769186816],
},
},
} as unknown as OpenClawConfig;
const result = maybeRepairDiscordNumericIds(cfg, {
doctorFixCommand: "openclaw doctor --fix",
});
expect(result.changes).toEqual([]);
expect(result.warnings).toEqual([
expect.stringContaining("could not be auto-repaired"),
expect.stringContaining('rerun "openclaw doctor --fix"'),
]);
});
it("formats numeric id warnings for safe entries", () => {
const warnings = collectDiscordNumericIdWarnings({
hits: [{ path: "channels.discord.allowFrom[0]", entry: 123, safe: true }],

View File

@@ -168,9 +168,46 @@ export function collectDiscordNumericIdWarnings(params: {
return lines;
}
export function maybeRepairDiscordNumericIds(cfg: OpenClawConfig): {
function collectBlockedDiscordNumericIdRepairWarnings(params: {
hits: DiscordNumericIdHit[];
doctorFixCommand: string;
}): string[] {
const hitsByListPath = new Map<string, DiscordNumericIdHit[]>();
for (const hit of params.hits) {
const listPath = hit.path.replace(/\[\d+\]$/, "");
const existing = hitsByListPath.get(listPath);
if (existing) {
existing.push(hit);
continue;
}
hitsByListPath.set(listPath, [hit]);
}
const blockedHits: DiscordNumericIdHit[] = [];
for (const hits of hitsByListPath.values()) {
if (hits.some((hit) => !hit.safe)) {
blockedHits.push(...hits);
}
}
if (blockedHits.length === 0) {
return [];
}
const sample = blockedHits[0];
const samplePath = sanitizeForLog(sample.path);
return [
`- Discord allowlists contain ${blockedHits.length} numeric ${blockedHits.length === 1 ? "entry" : "entries"} in lists that could not be auto-repaired (e.g. ${samplePath}).`,
`- These lists include invalid or precision-losing numeric IDs; manually quote the original values in your config file, then rerun "${params.doctorFixCommand}".`,
];
}
export function maybeRepairDiscordNumericIds(
cfg: OpenClawConfig,
params?: { doctorFixCommand?: string },
): {
config: OpenClawConfig;
changes: string[];
warnings?: string[];
} {
const hits = scanDiscordNumericIdEntries(cfg);
if (hits.length === 0) {
@@ -214,8 +251,16 @@ export function maybeRepairDiscordNumericIds(cfg: OpenClawConfig): {
}
}
if (changes.length === 0) {
const warnings =
params?.doctorFixCommand === undefined
? []
: collectBlockedDiscordNumericIdRepairWarnings({
hits,
doctorFixCommand: params.doctorFixCommand,
});
if (changes.length === 0 && warnings.length === 0) {
return { config: cfg, changes: [] };
}
return { config: next, changes };
return { config: next, changes, warnings };
}

View File

@@ -71,4 +71,35 @@ describe("doctor repair sequencing", () => {
expect(result.warningNotes[0]).not.toContain("\u001B");
expect(result.warningNotes[0]).not.toContain("\r");
});
it("emits Discord warnings when unsafe numeric ids block repair", async () => {
const result = await runDoctorRepairSequence({
state: {
cfg: {
channels: {
discord: {
allowFrom: [106232522769186816],
},
},
} as unknown as OpenClawConfig,
candidate: {
channels: {
discord: {
allowFrom: [106232522769186816],
},
},
} as unknown as OpenClawConfig,
pendingChanges: false,
fixHints: [],
},
doctorFixCommand: "openclaw doctor --fix",
});
expect(result.changeNotes).toEqual([]);
expect(result.warningNotes).toHaveLength(1);
expect(result.warningNotes[0]).toContain("could not be auto-repaired");
expect(result.warningNotes[0]).toContain('rerun "openclaw doctor --fix"');
expect(result.state.pendingChanges).toBe(false);
expect(result.state.candidate.channels?.discord?.allowFrom).toEqual([106232522769186816]);
});
});

View File

@@ -48,7 +48,11 @@ export async function runDoctorRepairSequence(params: {
};
applyMutation(await maybeRepairTelegramAllowFromUsernames(state.candidate));
applyMutation(maybeRepairDiscordNumericIds(state.candidate));
applyMutation(
maybeRepairDiscordNumericIds(state.candidate, {
doctorFixCommand: params.doctorFixCommand,
}),
);
applyMutation(maybeRepairOpenPolicyAllowFrom(state.candidate));
applyMutation(maybeRepairBundledPluginLoadPaths(state.candidate, process.env));
applyMutation(maybeRepairStalePluginConfig(state.candidate, process.env));