feat(doctor): add repair/force flows

This commit is contained in:
Peter Steinberger
2026-01-08 21:47:35 +01:00
parent 712a7dddf6
commit 2d4ec35e1c
7 changed files with 80 additions and 8 deletions

View File

@@ -167,10 +167,31 @@ export async function maybeRepairGatewayServiceConfig(
"Gateway service config",
);
const repair = await prompter.confirmSkipInNonInteractive({
message: "Update gateway service config to the recommended defaults now?",
initialValue: true,
});
const aggressiveIssues = audit.issues.filter(
(issue) => issue.level === "aggressive",
);
const recommendedIssues = audit.issues.filter(
(issue) => issue.level !== "aggressive",
);
const needsAggressive = aggressiveIssues.length > 0;
if (needsAggressive && !prompter.shouldForce) {
note(
"Custom or unexpected service edits detected. Rerun with --force to overwrite.",
"Gateway service config",
);
}
const repair = needsAggressive
? await prompter.confirmAggressive({
message:
"Overwrite gateway service config with current defaults now?",
initialValue: Boolean(prompter.shouldForce),
})
: await prompter.confirmRepair({
message: "Update gateway service config to the recommended defaults now?",
initialValue: true,
});
if (!repair) return;
const devMode =

View File

@@ -8,14 +8,20 @@ export type DoctorOptions = {
yes?: boolean;
nonInteractive?: boolean;
deep?: boolean;
repair?: boolean;
force?: boolean;
};
export type DoctorPrompter = {
confirm: (params: Parameters<typeof confirm>[0]) => Promise<boolean>;
confirmRepair: (params: Parameters<typeof confirm>[0]) => Promise<boolean>;
confirmAggressive: (params: Parameters<typeof confirm>[0]) => Promise<boolean>;
confirmSkipInNonInteractive: (
params: Parameters<typeof confirm>[0],
) => Promise<boolean>;
select: <T>(params: Parameters<typeof select>[0], fallback: T) => Promise<T>;
shouldRepair: boolean;
shouldForce: boolean;
};
export function createDoctorPrompter(params: {
@@ -24,24 +30,42 @@ export function createDoctorPrompter(params: {
}): DoctorPrompter {
const yes = params.options.yes === true;
const requestedNonInteractive = params.options.nonInteractive === true;
const shouldRepair = params.options.repair === true || yes;
const shouldForce = params.options.force === true;
const isTty = Boolean(process.stdin.isTTY);
const nonInteractive = requestedNonInteractive || (!isTty && !yes);
const canPrompt = isTty && !yes && !nonInteractive;
const confirmDefault = async (p: Parameters<typeof confirm>[0]) => {
if (nonInteractive) return false;
if (shouldRepair) return true;
if (!canPrompt) return Boolean(p.initialValue ?? false);
return guardCancel(await confirm(p), params.runtime) === true;
};
return {
confirm: confirmDefault,
confirmSkipInNonInteractive: async (p) => {
confirmRepair: async (p) => {
if (nonInteractive) return false;
return confirmDefault(p);
},
confirmAggressive: async (p) => {
if (nonInteractive) return false;
if (shouldRepair && shouldForce) return true;
if (shouldRepair && !shouldForce) return false;
if (!canPrompt) return Boolean(p.initialValue ?? false);
return guardCancel(await confirm(p), params.runtime) === true;
},
confirmSkipInNonInteractive: async (p) => {
if (nonInteractive) return false;
if (shouldRepair) return true;
return confirmDefault(p);
},
select: async <T>(p: Parameters<typeof select>[0], fallback: T) => {
if (!canPrompt) return fallback;
if (!canPrompt || shouldRepair) return fallback;
return guardCancel(await select(p), params.runtime) as T;
},
shouldRepair,
shouldForce,
};
}