fix(cli): guide migration and setup errors

This commit is contained in:
Vincent Koc
2026-05-09 08:13:12 +08:00
parent 24662e9f67
commit 56916bc8bf
4 changed files with 38 additions and 16 deletions

View File

@@ -11,6 +11,7 @@ import {
} from "../agents/auth-profiles.js";
import { resolveAuthStorePath } from "../agents/auth-profiles/paths.js";
import { loadPersistedAuthProfileStore } from "../agents/auth-profiles/persisted.js";
import { formatCliCommand } from "../cli/command-format.js";
import { commitConfigWithPendingPluginInstalls } from "../cli/plugins-install-record-commit.js";
import { logConfigUpdated } from "../config/logging.js";
import { pathExists } from "../infra/fs-safe.js";
@@ -97,7 +98,7 @@ export async function agentsAddCommand(
if (nonInteractive && !workspaceFlag) {
runtime.error(
"Non-interactive mode requires --workspace. Re-run without flags to use the wizard.",
`Non-interactive agent creation requires --workspace. Re-run ${formatCliCommand("openclaw agents add <id> --workspace <path>")} or omit flags to use the wizard.`,
);
runtime.exit(1);
return;
@@ -105,20 +106,24 @@ export async function agentsAddCommand(
if (nonInteractive) {
if (!nameInput) {
runtime.error("Agent name is required in non-interactive mode.");
runtime.error(
`Agent name is required in non-interactive mode. Run ${formatCliCommand("openclaw agents add <id> --workspace <path>")}.`,
);
runtime.exit(1);
return;
}
if (!workspaceFlag) {
runtime.error(
"Non-interactive mode requires --workspace. Re-run without flags to use the wizard.",
`Non-interactive agent creation requires --workspace. Re-run ${formatCliCommand("openclaw agents add <id> --workspace <path>")} or omit flags to use the wizard.`,
);
runtime.exit(1);
return;
}
const agentId = normalizeAgentId(nameInput);
if (agentId === DEFAULT_AGENT_ID) {
runtime.error(`"${DEFAULT_AGENT_ID}" is reserved. Choose another name.`);
runtime.error(
`"${DEFAULT_AGENT_ID}" is reserved. Choose another name, or run ${formatCliCommand("openclaw agents list")} to inspect the default agent.`,
);
runtime.exit(1);
return;
}
@@ -126,7 +131,9 @@ export async function agentsAddCommand(
runtime.log(`Normalized agent id to "${agentId}".`);
}
if (findAgentEntryIndex(listAgentEntries(cfg), agentId) >= 0) {
runtime.error(`Agent "${agentId}" already exists.`);
runtime.error(
`Agent "${agentId}" already exists. Run ${formatCliCommand("openclaw agents list")} to inspect configured agents.`,
);
runtime.exit(1);
return;
}

View File

@@ -1,3 +1,4 @@
import { formatCliCommand } from "../cli/command-format.js";
import { applyAuthChoiceLoadedPluginProvider } from "../plugins/provider-auth-choice.js";
import type { ApplyAuthChoiceParams, ApplyAuthChoiceResult } from "./auth-choice.apply.types.js";
import type { AuthChoice } from "./onboard-types.js";
@@ -56,7 +57,7 @@ async function formatDeprecatedProviderChoiceError(
if (!deprecatedChoice) {
return undefined;
}
return `Auth choice ${JSON.stringify(authChoice)} is no longer supported. Use ${JSON.stringify(deprecatedChoice.choiceId)} instead.`;
return `Auth choice ${JSON.stringify(authChoice)} is no longer supported. Use ${JSON.stringify(deprecatedChoice.choiceId)} instead, or run ${formatCliCommand("openclaw onboard")} to choose interactively.`;
}
export async function applyAuthChoice(
@@ -95,14 +96,14 @@ export async function applyAuthChoice(
throw new Error(
[
`Auth choice "${normalizedParams.authChoice}" was not matched to a provider setup flow.`,
'For Anthropic legacy token auth, use "setup-token" with tokenProvider="anthropic" or choose the Anthropic setup-token entry explicitly.',
`Run ${formatCliCommand("openclaw models auth login --provider <provider>")} for provider auth, or rerun ${formatCliCommand("openclaw onboard")} to choose interactively.`,
].join("\n"),
);
}
if (normalizedParams.authChoice === "oauth") {
throw new Error(
'Auth choice "oauth" is no longer supported directly. Use "setup-token" for Anthropic legacy token auth or a provider-specific OAuth entry.',
`Auth choice "oauth" is no longer supported directly. Use a provider-specific auth entry, or run ${formatCliCommand("openclaw models auth login --provider <provider>")}.`,
);
}

View File

@@ -4,6 +4,7 @@ import type {
ChannelResolveResult,
} from "../../channels/plugins/types.adapters.js";
import { resolveCommandConfigWithSecrets } from "../../cli/command-config-resolution.js";
import { formatCliCommand } from "../../cli/command-format.js";
import { getChannelsCommandSecretTargetIds } from "../../cli/command-secret-targets.js";
import { commitConfigWithPendingPluginInstalls } from "../../cli/plugins-install-record-commit.js";
import { refreshPluginRegistryAfterConfigMutation } from "../../cli/plugins-registry-refresh.js";
@@ -130,7 +131,9 @@ export async function channelsResolveCommand(opts: ChannelsResolveOptions, runti
});
const entries = (opts.entries ?? []).map((entry) => entry.trim()).filter(Boolean);
if (entries.length === 0) {
throw new Error("At least one entry is required.");
throw new Error(
`At least one entry is required. Example: ${formatCliCommand("openclaw channels resolve --channel discord <name-or-id>")}.`,
);
}
const explicitChannel = opts.channel?.trim();
@@ -145,7 +148,7 @@ export async function channelsResolveCommand(opts: ChannelsResolveOptions, runti
: null;
if (explicitChannel && resolvedExplicit?.catalogEntry && !resolvedExplicit.plugin) {
throw new Error(
`Channel plugin "${resolvedExplicit.catalogEntry.id}" is not installed. Run "openclaw channels add --channel ${resolvedExplicit.catalogEntry.id}" first.`,
`Channel plugin "${resolvedExplicit.catalogEntry.id}" is not installed. Run ${formatCliCommand(`openclaw channels add --channel ${resolvedExplicit.catalogEntry.id}`)} first.`,
);
}
if (resolvedExplicit?.configChanged) {
@@ -193,7 +196,9 @@ export async function channelsResolveCommand(opts: ChannelsResolveOptions, runti
(selection.channel ? getChannelPlugin(selection.channel) : undefined);
if (!plugin?.resolver?.resolveTargets) {
const channelText = selection.channel ?? explicitChannel ?? "";
throw new Error(`Channel ${channelText} does not support resolve.`);
throw new Error(
`Channel ${channelText} does not support resolve. Run ${formatCliCommand("openclaw channels capabilities --channel " + channelText)} to inspect supported actions.`,
);
}
const preferredKind = resolvePreferredKind(opts.kind);

View File

@@ -1,4 +1,5 @@
import { cancel, isCancel } from "@clack/prompts";
import { formatCliCommand } from "../cli/command-format.js";
import { promptYesNo } from "../cli/prompt.js";
import { getRuntimeConfig } from "../config/config.js";
import { redactMigrationPlan } from "../plugin-sdk/migration.js";
@@ -119,7 +120,9 @@ export async function migrateListCommand(runtime: RuntimeEnv, opts: { json?: boo
return;
}
if (providers.length === 0) {
runtime.log("No migration providers found.");
runtime.log(
`No migration providers found. Run ${formatCliCommand("openclaw plugins list")} to verify provider plugins are installed and enabled.`,
);
return;
}
runtime.log(
@@ -139,7 +142,9 @@ export async function migratePlanCommand(
): Promise<MigrationPlan> {
const providerId = opts.provider?.trim();
if (!providerId) {
throw new Error("Migration provider is required.");
throw new Error(
`Migration provider is required. Run ${formatCliCommand("openclaw migrate list")} to choose one.`,
);
}
const plan = selectMigrationItems(
await createMigrationPlan(runtime, { ...opts, provider: providerId }),
@@ -167,13 +172,17 @@ export async function migrateApplyCommand(
): Promise<MigrationApplyResult | MigrationPlan> {
const providerId = opts.provider?.trim();
if (!providerId) {
throw new Error("Migration provider is required.");
throw new Error(
`Migration provider is required. Run ${formatCliCommand("openclaw migrate list")} to choose one.`,
);
}
if (opts.noBackup && !opts.force) {
throw new Error("--no-backup requires --force.");
throw new Error("--no-backup requires --force because it skips the automatic rollback copy.");
}
if (!opts.yes && !process.stdin.isTTY) {
throw new Error("openclaw migrate apply requires --yes in non-interactive mode.");
throw new Error(
`openclaw migrate apply requires --yes in non-interactive mode. Preview first with ${formatCliCommand("openclaw migrate plan --provider <provider>")}.`,
);
}
const provider = resolveMigrationProvider(providerId);
if (!opts.yes) {