mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:20:45 +00:00
fix: keep onboarding setup paths cold
This commit is contained in:
@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Onboarding/setup: keep first-run config reads, plugin compatibility notices, and post-model sanity checks on cold metadata paths unless the user chooses to browse all models, avoiding full plugin/runtime catalog work between prompts. Thanks @shakkernerd.
|
||||||
- Onboarding/models: keep skip-auth and provider-scoped model picker prompts off the full global model catalog path, and cache provider catalog hook resolution so setup no longer stalls after auth on large plugin registries. Thanks @shakkernerd.
|
- Onboarding/models: keep skip-auth and provider-scoped model picker prompts off the full global model catalog path, and cache provider catalog hook resolution so setup no longer stalls after auth on large plugin registries. Thanks @shakkernerd.
|
||||||
- Gateway/Bonjour: suppress known @homebridge/ciao cancellation and network assertion failures through scoped process handlers so malformed mDNS packets or restricted VPS networking disable/restart Bonjour instead of crashing the gateway. Fixes #67578. Thanks @zenassist26-create.
|
- Gateway/Bonjour: suppress known @homebridge/ciao cancellation and network assertion failures through scoped process handlers so malformed mDNS packets or restricted VPS networking disable/restart Bonjour instead of crashing the gateway. Fixes #67578. Thanks @zenassist26-create.
|
||||||
- Discord: keep late clicks on already-resolved exec approval buttons quiet when elevated mode auto-resolved the request, while still surfacing real approval submission failures. Fixes #66906. Thanks @rlerikse.
|
- Discord: keep late clicks on already-resolved exec approval buttons quiet when elevated mode auto-resolved the request, while still surfacing real approval submission failures. Fixes #66906. Thanks @rlerikse.
|
||||||
|
|||||||
74
src/commands/auth-choice.model-check.test.ts
Normal file
74
src/commands/auth-choice.model-check.test.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||||
|
import { warnIfModelConfigLooksOff } from "./auth-choice.model-check.js";
|
||||||
|
import { makePrompter } from "./setup/__tests__/test-utils.js";
|
||||||
|
|
||||||
|
const loadModelCatalog = vi.hoisted(() => vi.fn());
|
||||||
|
vi.mock("../agents/model-catalog.js", () => ({
|
||||||
|
loadModelCatalog,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ensureAuthProfileStore = vi.hoisted(() => vi.fn(() => ({ version: 1, profiles: {} })));
|
||||||
|
const listProfilesForProvider = vi.hoisted(() => vi.fn(() => []));
|
||||||
|
vi.mock("../agents/auth-profiles.js", () => ({
|
||||||
|
ensureAuthProfileStore,
|
||||||
|
listProfilesForProvider,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const resolveEnvApiKey = vi.hoisted(() => vi.fn(() => undefined));
|
||||||
|
const hasUsableCustomProviderApiKey = vi.hoisted(() => vi.fn(() => false));
|
||||||
|
vi.mock("../agents/model-auth.js", () => ({
|
||||||
|
resolveEnvApiKey,
|
||||||
|
hasUsableCustomProviderApiKey,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("warnIfModelConfigLooksOff", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
loadModelCatalog.mockResolvedValue([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips catalog validation when requested while keeping auth checks", async () => {
|
||||||
|
const note = vi.fn(async () => {});
|
||||||
|
const prompter = makePrompter({ note });
|
||||||
|
const config = {
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: "openai-codex/gpt-5.5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
await warnIfModelConfigLooksOff(config, prompter, { validateCatalog: false });
|
||||||
|
|
||||||
|
expect(loadModelCatalog).not.toHaveBeenCalled();
|
||||||
|
expect(ensureAuthProfileStore).toHaveBeenCalledOnce();
|
||||||
|
expect(listProfilesForProvider).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ profiles: {} }),
|
||||||
|
"openai-codex",
|
||||||
|
);
|
||||||
|
expect(note).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('No auth configured for provider "openai-codex"'),
|
||||||
|
"Model check",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps full catalog validation enabled by default", async () => {
|
||||||
|
const note = vi.fn(async () => {});
|
||||||
|
const prompter = makePrompter({ note });
|
||||||
|
const config = {
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: "openai-codex/gpt-5.5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
await warnIfModelConfigLooksOff(config, prompter);
|
||||||
|
|
||||||
|
expect(loadModelCatalog).toHaveBeenCalledWith({
|
||||||
|
config,
|
||||||
|
useCache: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -9,25 +9,27 @@ import { buildProviderAuthRecoveryHint } from "./provider-auth-guidance.js";
|
|||||||
export async function warnIfModelConfigLooksOff(
|
export async function warnIfModelConfigLooksOff(
|
||||||
config: OpenClawConfig,
|
config: OpenClawConfig,
|
||||||
prompter: WizardPrompter,
|
prompter: WizardPrompter,
|
||||||
options?: { agentId?: string; agentDir?: string },
|
options?: { agentId?: string; agentDir?: string; validateCatalog?: boolean },
|
||||||
) {
|
) {
|
||||||
const ref = resolveDefaultModelForAgent({
|
const ref = resolveDefaultModelForAgent({
|
||||||
cfg: config,
|
cfg: config,
|
||||||
agentId: options?.agentId,
|
agentId: options?.agentId,
|
||||||
});
|
});
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
const catalog = await loadModelCatalog({
|
if (options?.validateCatalog !== false) {
|
||||||
config,
|
const catalog = await loadModelCatalog({
|
||||||
useCache: false,
|
config,
|
||||||
});
|
useCache: false,
|
||||||
if (catalog.length > 0) {
|
});
|
||||||
const known = catalog.some(
|
if (catalog.length > 0) {
|
||||||
(entry) => entry.provider === ref.provider && entry.id === ref.model,
|
const known = catalog.some(
|
||||||
);
|
(entry) => entry.provider === ref.provider && entry.id === ref.model,
|
||||||
if (!known) {
|
|
||||||
warnings.push(
|
|
||||||
`Model not found: ${ref.provider}/${ref.model}. Update agents.defaults.model or run /models list.`,
|
|
||||||
);
|
);
|
||||||
|
if (!known) {
|
||||||
|
warnings.push(
|
||||||
|
`Model not found: ${ref.provider}/${ref.model}. Update agents.defaults.model or run /models list.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,87 @@ describe("promptDefaultModel", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps current preferred-provider models cold until browsing is requested", async () => {
|
||||||
|
const select = vi.fn(async (params) => params.initialValue as never);
|
||||||
|
const prompter = makePrompter({ select });
|
||||||
|
const config = {
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: "openai-codex/gpt-5.5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
const result = await promptDefaultModel({
|
||||||
|
config,
|
||||||
|
prompter,
|
||||||
|
allowKeep: true,
|
||||||
|
includeManual: true,
|
||||||
|
ignoreAllowlist: true,
|
||||||
|
preferredProvider: "openai-codex",
|
||||||
|
browseCatalogOnDemand: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({});
|
||||||
|
expect(loadModelCatalog).not.toHaveBeenCalled();
|
||||||
|
expect(select.mock.calls[0]?.[0]).toMatchObject({
|
||||||
|
searchable: false,
|
||||||
|
initialValue: "__keep__",
|
||||||
|
});
|
||||||
|
expect(select.mock.calls[0]?.[0]?.options).toEqual([
|
||||||
|
expect.objectContaining({ value: "__keep__" }),
|
||||||
|
expect.objectContaining({ value: "__manual__" }),
|
||||||
|
expect.objectContaining({ value: "__browse__" }),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loads the full model catalog when the user chooses to browse", async () => {
|
||||||
|
loadModelCatalog.mockResolvedValue([
|
||||||
|
{
|
||||||
|
provider: "openai-codex",
|
||||||
|
id: "gpt-5.5",
|
||||||
|
name: "GPT-5.5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: "openai-codex",
|
||||||
|
id: "gpt-5.5-pro",
|
||||||
|
name: "GPT-5.5 Pro",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const select = vi
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce("__browse__")
|
||||||
|
.mockImplementationOnce(async (params) => {
|
||||||
|
const option = params.options.find(
|
||||||
|
(entry: { value: string }) => entry.value === "openai-codex/gpt-5.5-pro",
|
||||||
|
);
|
||||||
|
return option?.value ?? params.initialValue;
|
||||||
|
});
|
||||||
|
const prompter = makePrompter({ select });
|
||||||
|
const config = {
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: "openai-codex/gpt-5.5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
|
||||||
|
const result = await promptDefaultModel({
|
||||||
|
config,
|
||||||
|
prompter,
|
||||||
|
allowKeep: true,
|
||||||
|
includeManual: true,
|
||||||
|
ignoreAllowlist: true,
|
||||||
|
preferredProvider: "openai-codex",
|
||||||
|
browseCatalogOnDemand: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.model).toBe("openai-codex/gpt-5.5-pro");
|
||||||
|
expect(loadModelCatalog).toHaveBeenCalledOnce();
|
||||||
|
expect(select).toHaveBeenCalledTimes(2);
|
||||||
|
expect(select.mock.calls[1]?.[0]?.searchable).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("supports configuring vLLM during setup", async () => {
|
it("supports configuring vLLM during setup", async () => {
|
||||||
loadModelCatalog.mockResolvedValue([
|
loadModelCatalog.mockResolvedValue([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export { applyPrimaryModel } from "../plugins/provider-model-primary.js";
|
|||||||
|
|
||||||
const KEEP_VALUE = "__keep__";
|
const KEEP_VALUE = "__keep__";
|
||||||
const MANUAL_VALUE = "__manual__";
|
const MANUAL_VALUE = "__manual__";
|
||||||
|
const BROWSE_VALUE = "__browse__";
|
||||||
const PROVIDER_FILTER_THRESHOLD = 30;
|
const PROVIDER_FILTER_THRESHOLD = 30;
|
||||||
|
|
||||||
// Internal router models are valid defaults during auth/setup but not manual API targets.
|
// Internal router models are valid defaults during auth/setup but not manual API targets.
|
||||||
@@ -46,6 +47,7 @@ export type PromptDefaultModelParams = {
|
|||||||
includeProviderPluginSetups?: boolean;
|
includeProviderPluginSetups?: boolean;
|
||||||
ignoreAllowlist?: boolean;
|
ignoreAllowlist?: boolean;
|
||||||
loadCatalog?: boolean;
|
loadCatalog?: boolean;
|
||||||
|
browseCatalogOnDemand?: boolean;
|
||||||
preferredProvider?: string;
|
preferredProvider?: string;
|
||||||
agentDir?: string;
|
agentDir?: string;
|
||||||
workspaceDir?: string;
|
workspaceDir?: string;
|
||||||
@@ -508,20 +510,70 @@ export async function promptDefaultModel(
|
|||||||
const includeManual = params.includeManual ?? true;
|
const includeManual = params.includeManual ?? true;
|
||||||
const includeProviderPluginSetups = params.includeProviderPluginSetups ?? false;
|
const includeProviderPluginSetups = params.includeProviderPluginSetups ?? false;
|
||||||
const loadCatalog = params.loadCatalog ?? true;
|
const loadCatalog = params.loadCatalog ?? true;
|
||||||
|
const browseCatalogOnDemand = params.browseCatalogOnDemand ?? false;
|
||||||
const ignoreAllowlist = params.ignoreAllowlist ?? false;
|
const ignoreAllowlist = params.ignoreAllowlist ?? false;
|
||||||
const preferredProviderRaw = normalizeOptionalString(params.preferredProvider);
|
const preferredProviderRaw = normalizeOptionalString(params.preferredProvider);
|
||||||
const preferredProvider = preferredProviderRaw
|
const preferredProvider = preferredProviderRaw
|
||||||
? normalizeProviderId(preferredProviderRaw)
|
? normalizeProviderId(preferredProviderRaw)
|
||||||
: undefined;
|
: undefined;
|
||||||
const configuredRaw = resolveConfiguredModelRaw(cfg);
|
const configuredRaw = resolveConfiguredModelRaw(cfg);
|
||||||
|
const useStaticModelNormalization = !loadCatalog || browseCatalogOnDemand;
|
||||||
const resolved = resolveConfiguredModelRef({
|
const resolved = resolveConfiguredModelRef({
|
||||||
cfg,
|
cfg,
|
||||||
defaultProvider: DEFAULT_PROVIDER,
|
defaultProvider: DEFAULT_PROVIDER,
|
||||||
defaultModel: DEFAULT_MODEL,
|
defaultModel: DEFAULT_MODEL,
|
||||||
|
allowPluginNormalization: useStaticModelNormalization ? false : undefined,
|
||||||
});
|
});
|
||||||
const resolvedKey = modelKey(resolved.provider, resolved.model);
|
const resolvedKey = modelKey(resolved.provider, resolved.model);
|
||||||
const configuredKey = configuredRaw ? resolvedKey : "";
|
const configuredKey = configuredRaw ? resolvedKey : "";
|
||||||
|
|
||||||
|
if (
|
||||||
|
loadCatalog &&
|
||||||
|
browseCatalogOnDemand &&
|
||||||
|
preferredProvider &&
|
||||||
|
allowKeep &&
|
||||||
|
normalizeProviderId(resolved.provider) === preferredProvider
|
||||||
|
) {
|
||||||
|
const options: WizardSelectOption[] = [
|
||||||
|
{
|
||||||
|
value: KEEP_VALUE,
|
||||||
|
label: configuredRaw
|
||||||
|
? `Keep current (${configuredRaw})`
|
||||||
|
: `Keep current (default: ${resolvedKey})`,
|
||||||
|
hint:
|
||||||
|
configuredRaw && configuredRaw !== resolvedKey ? `resolves to ${resolvedKey}` : undefined,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (includeManual) {
|
||||||
|
options.push({ value: MANUAL_VALUE, label: "Enter model manually" });
|
||||||
|
}
|
||||||
|
options.push({
|
||||||
|
value: BROWSE_VALUE,
|
||||||
|
label: "Browse all models",
|
||||||
|
hint: "loads provider catalogs",
|
||||||
|
});
|
||||||
|
|
||||||
|
const selection = await params.prompter.select({
|
||||||
|
message: params.message ?? "Default model",
|
||||||
|
options,
|
||||||
|
initialValue: KEEP_VALUE,
|
||||||
|
searchable: false,
|
||||||
|
});
|
||||||
|
if (selection === KEEP_VALUE) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (selection === MANUAL_VALUE) {
|
||||||
|
return promptManualModel({
|
||||||
|
prompter: params.prompter,
|
||||||
|
allowBlank: false,
|
||||||
|
initialValue: configuredRaw || resolvedKey || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (selection !== BROWSE_VALUE) {
|
||||||
|
return { model: selection };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!loadCatalog) {
|
if (!loadCatalog) {
|
||||||
const options: WizardSelectOption[] = [];
|
const options: WizardSelectOption[] = [];
|
||||||
if (allowKeep) {
|
if (allowKeep) {
|
||||||
|
|||||||
@@ -118,13 +118,18 @@ const readConfigFileSnapshot = vi.hoisted(() =>
|
|||||||
legacyIssues: [] as Array<{ path: string; message: string }>,
|
legacyIssues: [] as Array<{ path: string; message: string }>,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
const createConfigIO = vi.hoisted(() =>
|
||||||
|
vi.fn(() => ({
|
||||||
|
readConfigFileSnapshot,
|
||||||
|
})),
|
||||||
|
);
|
||||||
const ensureSystemdUserLingerInteractive = vi.hoisted(() => vi.fn(async () => {}));
|
const ensureSystemdUserLingerInteractive = vi.hoisted(() => vi.fn(async () => {}));
|
||||||
const isSystemdUserServiceAvailable = vi.hoisted(() => vi.fn(async () => true));
|
const isSystemdUserServiceAvailable = vi.hoisted(() => vi.fn(async () => true));
|
||||||
const ensureControlUiAssetsBuilt = vi.hoisted(() => vi.fn(async () => ({ ok: true })));
|
const ensureControlUiAssetsBuilt = vi.hoisted(() => vi.fn(async () => ({ ok: true })));
|
||||||
const runTui = vi.hoisted(() => vi.fn(async (_options: unknown) => {}));
|
const runTui = vi.hoisted(() => vi.fn(async (_options: unknown) => {}));
|
||||||
const setupWizardShellCompletion = vi.hoisted(() => vi.fn(async () => {}));
|
const setupWizardShellCompletion = vi.hoisted(() => vi.fn(async () => {}));
|
||||||
const probeGatewayReachable = vi.hoisted(() => vi.fn(async () => ({ ok: true })));
|
const probeGatewayReachable = vi.hoisted(() => vi.fn(async () => ({ ok: true })));
|
||||||
const buildPluginCompatibilityNotices = vi.hoisted(() =>
|
const buildPluginCompatibilitySnapshotNotices = vi.hoisted(() =>
|
||||||
vi.fn((): PluginCompatibilityNotice[] => []),
|
vi.fn((): PluginCompatibilityNotice[] => []),
|
||||||
);
|
);
|
||||||
const formatPluginCompatibilityNotice = vi.hoisted(() =>
|
const formatPluginCompatibilityNotice = vi.hoisted(() =>
|
||||||
@@ -185,8 +190,8 @@ vi.mock("../commands/onboard-hooks.js", () => ({
|
|||||||
|
|
||||||
vi.mock("../config/config.js", () => ({
|
vi.mock("../config/config.js", () => ({
|
||||||
DEFAULT_GATEWAY_PORT: 18789,
|
DEFAULT_GATEWAY_PORT: 18789,
|
||||||
|
createConfigIO,
|
||||||
resolveGatewayPort,
|
resolveGatewayPort,
|
||||||
readConfigFileSnapshot,
|
|
||||||
writeConfigFile,
|
writeConfigFile,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -228,7 +233,7 @@ vi.mock("../infra/control-ui-assets.js", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../plugins/status.js", () => ({
|
vi.mock("../plugins/status.js", () => ({
|
||||||
buildPluginCompatibilityNotices,
|
buildPluginCompatibilitySnapshotNotices,
|
||||||
formatPluginCompatibilityNotice,
|
formatPluginCompatibilityNotice,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -405,6 +410,7 @@ describe("runSetupWizard", () => {
|
|||||||
const multiselect: WizardPrompter["multiselect"] = vi.fn(async () => []);
|
const multiselect: WizardPrompter["multiselect"] = vi.fn(async () => []);
|
||||||
const prompter = buildWizardPrompter({ select, multiselect });
|
const prompter = buildWizardPrompter({ select, multiselect });
|
||||||
const runtime = createRuntime({ throwsOnExit: true });
|
const runtime = createRuntime({ throwsOnExit: true });
|
||||||
|
createConfigIO.mockClear();
|
||||||
ensureAuthProfileStore.mockClear();
|
ensureAuthProfileStore.mockClear();
|
||||||
|
|
||||||
await runSetupWizard(
|
await runSetupWizard(
|
||||||
@@ -423,6 +429,7 @@ describe("runSetupWizard", () => {
|
|||||||
prompter,
|
prompter,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(createConfigIO).toHaveBeenCalledWith({ pluginValidation: "skip" });
|
||||||
expect(select).not.toHaveBeenCalled();
|
expect(select).not.toHaveBeenCalled();
|
||||||
expect(ensureAuthProfileStore).not.toHaveBeenCalled();
|
expect(ensureAuthProfileStore).not.toHaveBeenCalled();
|
||||||
expect(setupChannels).not.toHaveBeenCalled();
|
expect(setupChannels).not.toHaveBeenCalled();
|
||||||
@@ -623,6 +630,7 @@ describe("runSetupWizard", () => {
|
|||||||
|
|
||||||
it("prompts for a model during explicit interactive Ollama setup", async () => {
|
it("prompts for a model during explicit interactive Ollama setup", async () => {
|
||||||
promptDefaultModel.mockClear();
|
promptDefaultModel.mockClear();
|
||||||
|
warnIfModelConfigLooksOff.mockClear();
|
||||||
resolveProviderPluginChoice.mockReturnValue({
|
resolveProviderPluginChoice.mockReturnValue({
|
||||||
provider: {
|
provider: {
|
||||||
id: "ollama",
|
id: "ollama",
|
||||||
@@ -671,8 +679,14 @@ describe("runSetupWizard", () => {
|
|||||||
expect(promptDefaultModel).toHaveBeenCalledWith(
|
expect(promptDefaultModel).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
allowKeep: false,
|
allowKeep: false,
|
||||||
|
browseCatalogOnDemand: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
expect(warnIfModelConfigLooksOff).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
expect.objectContaining({ validateCatalog: false }),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("re-prompts for auth when applyAuthChoice requests retry selection", async () => {
|
it("re-prompts for auth when applyAuthChoice requests retry selection", async () => {
|
||||||
@@ -744,7 +758,7 @@ describe("runSetupWizard", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("shows plugin compatibility notices for an existing valid config", async () => {
|
it("shows plugin compatibility notices for an existing valid config", async () => {
|
||||||
buildPluginCompatibilityNotices.mockReturnValue([
|
buildPluginCompatibilitySnapshotNotices.mockReturnValue([
|
||||||
{
|
{
|
||||||
pluginId: "legacy-plugin",
|
pluginId: "legacy-plugin",
|
||||||
code: "legacy-before-agent-start",
|
code: "legacy-before-agent-start",
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import type {
|
|||||||
OnboardOptions,
|
OnboardOptions,
|
||||||
ResetScope,
|
ResetScope,
|
||||||
} from "../commands/onboard-types.js";
|
} from "../commands/onboard-types.js";
|
||||||
import { readConfigFileSnapshot, resolveGatewayPort, writeConfigFile } from "../config/config.js";
|
import { createConfigIO, resolveGatewayPort, writeConfigFile } from "../config/config.js";
|
||||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||||
import { normalizeSecretInputString } from "../config/types.secrets.js";
|
import { normalizeSecretInputString } from "../config/types.secrets.js";
|
||||||
import { formatErrorMessage } from "../infra/errors.js";
|
import { formatErrorMessage } from "../infra/errors.js";
|
||||||
import {
|
import {
|
||||||
buildPluginCompatibilityNotices,
|
buildPluginCompatibilitySnapshotNotices,
|
||||||
formatPluginCompatibilityNotice,
|
formatPluginCompatibilityNotice,
|
||||||
} from "../plugins/status.js";
|
} from "../plugins/status.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
@@ -61,6 +61,10 @@ async function writeWizardConfigFile(config: OpenClawConfig): Promise<OpenClawCo
|
|||||||
return committed.config;
|
return committed.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function readSetupConfigFileSnapshot() {
|
||||||
|
return await createConfigIO({ pluginValidation: "skip" }).readConfigFileSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
async function resolveAuthChoiceModelSelectionPolicy(params: {
|
async function resolveAuthChoiceModelSelectionPolicy(params: {
|
||||||
authChoice: string;
|
authChoice: string;
|
||||||
config: OpenClawConfig;
|
config: OpenClawConfig;
|
||||||
@@ -146,7 +150,7 @@ export async function runSetupWizard(
|
|||||||
await prompter.intro("OpenClaw setup");
|
await prompter.intro("OpenClaw setup");
|
||||||
await requireRiskAcknowledgement({ opts, prompter });
|
await requireRiskAcknowledgement({ opts, prompter });
|
||||||
|
|
||||||
const snapshot = await readConfigFileSnapshot();
|
const snapshot = await readSetupConfigFileSnapshot();
|
||||||
let baseConfig: OpenClawConfig = snapshot.valid
|
let baseConfig: OpenClawConfig = snapshot.valid
|
||||||
? snapshot.exists
|
? snapshot.exists
|
||||||
? (snapshot.sourceConfig ?? snapshot.config)
|
? (snapshot.sourceConfig ?? snapshot.config)
|
||||||
@@ -173,7 +177,7 @@ export async function runSetupWizard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const compatibilityNotices = snapshot.valid
|
const compatibilityNotices = snapshot.valid
|
||||||
? buildPluginCompatibilityNotices({ config: baseConfig })
|
? buildPluginCompatibilitySnapshotNotices({ config: baseConfig })
|
||||||
: [];
|
: [];
|
||||||
if (compatibilityNotices.length > 0) {
|
if (compatibilityNotices.length > 0) {
|
||||||
await prompter.note(
|
await prompter.note(
|
||||||
@@ -570,7 +574,7 @@ export async function runSetupWizard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { warnIfModelConfigLooksOff } = await loadAuthChoiceModule();
|
const { warnIfModelConfigLooksOff } = await loadAuthChoiceModule();
|
||||||
await warnIfModelConfigLooksOff(nextConfig, prompter);
|
await warnIfModelConfigLooksOff(nextConfig, prompter, { validateCatalog: false });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -617,6 +621,7 @@ export async function runSetupWizard(
|
|||||||
ignoreAllowlist: true,
|
ignoreAllowlist: true,
|
||||||
includeProviderPluginSetups: true,
|
includeProviderPluginSetups: true,
|
||||||
preferredProvider: authChoiceModelSelectionPolicy?.preferredProvider,
|
preferredProvider: authChoiceModelSelectionPolicy?.preferredProvider,
|
||||||
|
browseCatalogOnDemand: true,
|
||||||
workspaceDir,
|
workspaceDir,
|
||||||
runtime,
|
runtime,
|
||||||
});
|
});
|
||||||
@@ -628,7 +633,7 @@ export async function runSetupWizard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await warnIfModelConfigLooksOff(nextConfig, prompter);
|
await warnIfModelConfigLooksOff(nextConfig, prompter, { validateCatalog: false });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user