feat: improve doctor update flow
This commit is contained in:
@@ -454,6 +454,10 @@ export function registerPluginsCli(program: Command) {
|
|||||||
const targets = opts.all ? Object.keys(installs) : id ? [id] : [];
|
const targets = opts.all ? Object.keys(installs) : id ? [id] : [];
|
||||||
|
|
||||||
if (targets.length === 0) {
|
if (targets.length === 0) {
|
||||||
|
if (opts.all) {
|
||||||
|
defaultRuntime.log("No npm-installed plugins to update.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
defaultRuntime.error("Provide a plugin id or use --all.");
|
defaultRuntime.error("Provide a plugin id or use --all.");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -623,7 +623,8 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
|||||||
process.env.CLAWDBOT_UPDATE_IN_PROGRESS = "1";
|
process.env.CLAWDBOT_UPDATE_IN_PROGRESS = "1";
|
||||||
try {
|
try {
|
||||||
const { doctorCommand } = await import("../commands/doctor.js");
|
const { doctorCommand } = await import("../commands/doctor.js");
|
||||||
await doctorCommand(defaultRuntime, { nonInteractive: true });
|
const interactiveDoctor = Boolean(process.stdin.isTTY) && !opts.json && opts.yes !== true;
|
||||||
|
await doctorCommand(defaultRuntime, { nonInteractive: !interactiveDoctor });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
defaultRuntime.log(theme.warn(`Doctor failed: ${String(err)}`));
|
defaultRuntime.log(theme.warn(`Doctor failed: ${String(err)}`));
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -121,10 +121,14 @@ export async function loadAndMaybeMigrateDoctorConfig(params: {
|
|||||||
options: DoctorOptions;
|
options: DoctorOptions;
|
||||||
confirm: (p: { message: string; initialValue: boolean }) => Promise<boolean>;
|
confirm: (p: { message: string; initialValue: boolean }) => Promise<boolean>;
|
||||||
}) {
|
}) {
|
||||||
void params.confirm;
|
|
||||||
const shouldRepair = params.options.repair === true || params.options.yes === true;
|
const shouldRepair = params.options.repair === true || params.options.yes === true;
|
||||||
const snapshot = await readConfigFileSnapshot();
|
const snapshot = await readConfigFileSnapshot();
|
||||||
let cfg: ClawdbotConfig = snapshot.config ?? {};
|
const baseCfg = snapshot.config ?? {};
|
||||||
|
let cfg: ClawdbotConfig = baseCfg;
|
||||||
|
let candidate = structuredClone(baseCfg) as ClawdbotConfig;
|
||||||
|
let pendingChanges = false;
|
||||||
|
let shouldWriteConfig = false;
|
||||||
|
const fixHints: string[] = [];
|
||||||
if (snapshot.exists && !snapshot.valid && snapshot.legacyIssues.length === 0) {
|
if (snapshot.exists && !snapshot.valid && snapshot.legacyIssues.length === 0) {
|
||||||
note("Config invalid; doctor will run with best-effort config.", "Config");
|
note("Config invalid; doctor will run with best-effort config.", "Config");
|
||||||
}
|
}
|
||||||
@@ -139,52 +143,76 @@ export async function loadAndMaybeMigrateDoctorConfig(params: {
|
|||||||
snapshot.legacyIssues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n"),
|
snapshot.legacyIssues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n"),
|
||||||
"Legacy config keys detected",
|
"Legacy config keys detected",
|
||||||
);
|
);
|
||||||
|
const { config: migrated, changes } = migrateLegacyConfig(snapshot.parsed);
|
||||||
|
if (changes.length > 0) {
|
||||||
|
note(changes.join("\n"), "Doctor changes");
|
||||||
|
}
|
||||||
|
if (migrated) {
|
||||||
|
candidate = migrated;
|
||||||
|
pendingChanges = pendingChanges || changes.length > 0;
|
||||||
|
}
|
||||||
if (shouldRepair) {
|
if (shouldRepair) {
|
||||||
// Legacy migration (2026-01-02, commit: 16420e5b) — normalize per-provider allowlists; move WhatsApp gating into channels.whatsapp.allowFrom.
|
// Legacy migration (2026-01-02, commit: 16420e5b) — normalize per-provider allowlists; move WhatsApp gating into channels.whatsapp.allowFrom.
|
||||||
const { config: migrated, changes } = migrateLegacyConfig(snapshot.parsed);
|
|
||||||
if (changes.length > 0) note(changes.join("\n"), "Doctor changes");
|
|
||||||
if (migrated) cfg = migrated;
|
if (migrated) cfg = migrated;
|
||||||
} else {
|
} else {
|
||||||
note(
|
fixHints.push(
|
||||||
`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply legacy migrations.`,
|
`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply legacy migrations.`,
|
||||||
"Doctor",
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalized = normalizeLegacyConfigValues(cfg);
|
const normalized = normalizeLegacyConfigValues(candidate);
|
||||||
if (normalized.changes.length > 0) {
|
if (normalized.changes.length > 0) {
|
||||||
note(normalized.changes.join("\n"), "Doctor changes");
|
note(normalized.changes.join("\n"), "Doctor changes");
|
||||||
|
candidate = normalized.config;
|
||||||
|
pendingChanges = true;
|
||||||
if (shouldRepair) {
|
if (shouldRepair) {
|
||||||
cfg = normalized.config;
|
cfg = normalized.config;
|
||||||
} else {
|
} else {
|
||||||
note(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply these changes.`, "Doctor");
|
fixHints.push(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply these changes.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoEnable = applyPluginAutoEnable({ config: cfg, env: process.env });
|
const autoEnable = applyPluginAutoEnable({ config: candidate, env: process.env });
|
||||||
if (autoEnable.changes.length > 0) {
|
if (autoEnable.changes.length > 0) {
|
||||||
note(autoEnable.changes.join("\n"), "Doctor changes");
|
note(autoEnable.changes.join("\n"), "Doctor changes");
|
||||||
|
candidate = autoEnable.config;
|
||||||
|
pendingChanges = true;
|
||||||
if (shouldRepair) {
|
if (shouldRepair) {
|
||||||
cfg = autoEnable.config;
|
cfg = autoEnable.config;
|
||||||
} else {
|
} else {
|
||||||
note(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply these changes.`, "Doctor");
|
fixHints.push(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply these changes.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unknown = stripUnknownConfigKeys(cfg);
|
const unknown = stripUnknownConfigKeys(candidate);
|
||||||
if (unknown.removed.length > 0) {
|
if (unknown.removed.length > 0) {
|
||||||
const lines = unknown.removed.map((path) => `- ${path}`).join("\n");
|
const lines = unknown.removed.map((path) => `- ${path}`).join("\n");
|
||||||
|
candidate = unknown.config;
|
||||||
|
pendingChanges = true;
|
||||||
if (shouldRepair) {
|
if (shouldRepair) {
|
||||||
cfg = unknown.config;
|
cfg = unknown.config;
|
||||||
note(lines, "Doctor changes");
|
note(lines, "Doctor changes");
|
||||||
} else {
|
} else {
|
||||||
note(lines, "Unknown config keys");
|
note(lines, "Unknown config keys");
|
||||||
note('Run "clawdbot doctor --fix" to remove these keys.', "Doctor");
|
fixHints.push('Run "clawdbot doctor --fix" to remove these keys.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldRepair && pendingChanges) {
|
||||||
|
const shouldApply = await params.confirm({
|
||||||
|
message: "Apply recommended config repairs now?",
|
||||||
|
initialValue: true,
|
||||||
|
});
|
||||||
|
if (shouldApply) {
|
||||||
|
cfg = candidate;
|
||||||
|
shouldWriteConfig = true;
|
||||||
|
} else if (fixHints.length > 0) {
|
||||||
|
note(fixHints.join("\n"), "Doctor");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noteOpencodeProviderOverrides(cfg);
|
noteOpencodeProviderOverrides(cfg);
|
||||||
|
|
||||||
return { cfg, path: snapshot.path ?? CONFIG_PATH_CLAWDBOT };
|
return { cfg, path: snapshot.path ?? CONFIG_PATH_CLAWDBOT, shouldWriteConfig };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,7 +250,8 @@ export async function doctorCommand(
|
|||||||
healthOk,
|
healthOk,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (prompter.shouldRepair) {
|
const shouldWriteConfig = prompter.shouldRepair || configResult.shouldWriteConfig;
|
||||||
|
if (shouldWriteConfig) {
|
||||||
cfg = applyWizardMetadata(cfg, { command: "doctor", mode: resolveMode(cfg) });
|
cfg = applyWizardMetadata(cfg, { command: "doctor", mode: resolveMode(cfg) });
|
||||||
await writeConfigFile(cfg);
|
await writeConfigFile(cfg);
|
||||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user