From 2fbddce88182308cd02264d6c0c0c2fc8732199b Mon Sep 17 00:00:00 2001 From: zhang-guiping Date: Mon, 1 Jun 2026 02:22:16 +0800 Subject: [PATCH] fix(cli): avoid catalog validation in agents add (#88314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #76284. Thanks @zhangguiping-xydt. Co-authored-by: 张贵萍0668001030 --- .github/workflows/openclaw-npm-release.yml | 3 +- src/commands/agents.add.test.ts | 62 ++++++++++++++++++++++ src/commands/agents.commands.add.ts | 1 + ui/index.html | 2 +- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/.github/workflows/openclaw-npm-release.yml b/.github/workflows/openclaw-npm-release.yml index d7d6cec00286..bbfb6bb3e0f0 100644 --- a/.github/workflows/openclaw-npm-release.yml +++ b/.github/workflows/openclaw-npm-release.yml @@ -257,7 +257,8 @@ jobs: return -1; } - for (let start = input.indexOf("["); start !== -1; start = input.indexOf("[", start + 1)) { + for (const match of input.matchAll(/\[/g)) { + const start = match.index; const end = arrayEndFrom(start); if (end === -1) { continue; diff --git a/src/commands/agents.add.test.ts b/src/commands/agents.add.test.ts index c51c5e6cc940..1efa6d4a9115 100644 --- a/src/commands/agents.add.test.ts +++ b/src/commands/agents.add.test.ts @@ -12,6 +12,12 @@ const writeConfigFileMock = vi.hoisted(() => vi.fn().mockResolvedValue(undefined const replaceConfigFileMock = vi.hoisted(() => vi.fn(async (params: { nextConfig: unknown }) => await writeConfigFileMock(params.nextConfig)), ); +const commitConfigWithPendingPluginInstallsMock = vi.hoisted(() => + vi.fn(async (params: { nextConfig: Record }) => { + await writeConfigFileMock(params.nextConfig); + return { config: params.nextConfig }; + }), +); const transformConfigWithPendingPluginInstallsMock = vi.hoisted(() => vi.fn( async (params: { @@ -56,6 +62,16 @@ const transformConfigWithPendingPluginInstallsMock = vi.hoisted(() => const wizardMocks = vi.hoisted(() => ({ createClackPrompter: vi.fn(), })); +const authChoiceMocks = vi.hoisted(() => ({ + applyAuthChoice: vi.fn(), + warnIfModelConfigLooksOff: vi.fn(async () => {}), +})); +const onboardChannelsMocks = vi.hoisted(() => ({ + setupChannels: vi.fn(async (config: Record) => config), +})); +const onboardHelpersMocks = vi.hoisted(() => ({ + ensureWorkspaceAndSessions: vi.fn(async () => {}), +})); vi.mock("../config/config.js", async () => ({ ...(await vi.importActual("../config/config.js")), @@ -68,6 +84,7 @@ vi.mock("../cli/plugins-install-record-commit.js", async () => ({ ...(await vi.importActual( "../cli/plugins-install-record-commit.js", )), + commitConfigWithPendingPluginInstalls: commitConfigWithPendingPluginInstallsMock, transformConfigWithPendingPluginInstalls: transformConfigWithPendingPluginInstallsMock, })); @@ -75,6 +92,19 @@ vi.mock("../wizard/clack-prompter.js", () => ({ createClackPrompter: wizardMocks.createClackPrompter, })); +vi.mock("./auth-choice.js", () => ({ + applyAuthChoice: authChoiceMocks.applyAuthChoice, + warnIfModelConfigLooksOff: authChoiceMocks.warnIfModelConfigLooksOff, +})); + +vi.mock("./onboard-channels.js", () => ({ + setupChannels: onboardChannelsMocks.setupChannels, +})); + +vi.mock("./onboard-helpers.js", () => ({ + ensureWorkspaceAndSessions: onboardHelpersMocks.ensureWorkspaceAndSessions, +})); + import { WizardCancelledError } from "../wizard/prompts.js"; import { agentsAddCommand, testing } from "./agents.commands.add.js"; @@ -85,8 +115,13 @@ describe("agents add command", () => { readConfigFileSnapshotMock.mockClear(); writeConfigFileMock.mockClear(); replaceConfigFileMock.mockClear(); + commitConfigWithPendingPluginInstallsMock.mockClear(); transformConfigWithPendingPluginInstallsMock.mockClear(); wizardMocks.createClackPrompter.mockClear(); + authChoiceMocks.applyAuthChoice.mockClear(); + authChoiceMocks.warnIfModelConfigLooksOff.mockClear(); + onboardChannelsMocks.setupChannels.mockClear(); + onboardHelpersMocks.ensureWorkspaceAndSessions.mockClear(); runtime.log.mockClear(); runtime.error.mockClear(); runtime.exit.mockClear(); @@ -136,6 +171,33 @@ describe("agents add command", () => { expect(writeConfigFileMock).not.toHaveBeenCalled(); }); + it("skips catalog validation when checking the interactive wizard model config", async () => { + readConfigFileSnapshotMock.mockResolvedValue({ + ...baseConfigSnapshot, + config: { agents: { list: [] } }, + sourceConfig: { agents: { list: [] } }, + }); + wizardMocks.createClackPrompter.mockReturnValue({ + intro: vi.fn(), + text: vi.fn().mockResolvedValueOnce("Jon").mockResolvedValueOnce("/tmp/openclaw-jon"), + confirm: vi.fn().mockResolvedValue(false), + note: vi.fn(), + outro: vi.fn(), + }); + + await agentsAddCommand({}, runtime); + + expect(authChoiceMocks.warnIfModelConfigLooksOff).toHaveBeenCalledOnce(); + expect(authChoiceMocks.warnIfModelConfigLooksOff).toHaveBeenCalledWith( + expect.objectContaining({ agents: expect.any(Object) }), + expect.any(Object), + expect.objectContaining({ + agentId: "jon", + validateCatalog: false, + }), + ); + }); + it("copies only portable auth profiles when seeding a new agent store", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-agents-add-auth-copy-")); try { diff --git a/src/commands/agents.commands.add.ts b/src/commands/agents.commands.add.ts index f3a99164996f..b8da5ae8ed84 100644 --- a/src/commands/agents.commands.add.ts +++ b/src/commands/agents.commands.add.ts @@ -415,6 +415,7 @@ export async function agentsAddCommand( await warnIfModelConfigLooksOff(nextConfig, prompter, { agentId, agentDir, + validateCatalog: false, }); let selection: ChannelChoice[] = []; diff --git a/ui/index.html b/ui/index.html index 9277fa45171e..18a1cadb1d8b 100644 --- a/ui/index.html +++ b/ui/index.html @@ -57,7 +57,7 @@ document.documentElement.setAttribute("data-theme", resolved); document.documentElement.setAttribute( "data-theme-mode", - resolved.indexOf("light") !== -1 ? "light" : "dark", + resolved.includes("light") ? "light" : "dark", ); } catch (e) {} })();