


























@@ -56,6 +56,16 @@ const hasKeyInEnv = vi.hoisted(() =>
5656const listConfiguredWebSearchProviders = vi.hoisted(() =>
5757vi.fn<(params?: { config?: OpenClawConfig }) => PluginWebSearchProviderEntry[]>(() => []),
5858);
59+const hasAuthProfileForProvider = vi.hoisted(() =>
60+vi.fn<
61+(params: {
62+provider: string;
63+agentDir?: string;
64+includeExternalCli?: boolean;
65+type?: string;
66+}) => boolean
67+>(() => false),
68+);
59696070vi.mock("../commands/onboard-helpers.js", () => ({
6171detectBrowserOpenSupport: vi.fn(async () => ({ ok: false })),
@@ -99,6 +109,10 @@ vi.mock("../commands/onboard-search.js", () => ({
99109 resolveExistingKey,
100110}));
101111112+vi.mock("../agents/tools/model-config.helpers.js", () => ({
113+ hasAuthProfileForProvider,
114+}));
115+102116vi.mock("../web-search/runtime.js", () => ({
103117 listConfiguredWebSearchProviders,
104118}));
@@ -165,7 +179,14 @@ function createRuntime(): RuntimeEnv {
165179function createWebSearchProviderEntry(
166180provider: Pick<
167181PluginWebSearchProviderEntry,
168-"id" | "label" | "hint" | "envVars" | "placeholder" | "signupUrl" | "credentialPath"
182+| "id"
183+| "label"
184+| "hint"
185+| "envVars"
186+| "authProviderId"
187+| "placeholder"
188+| "signupUrl"
189+| "credentialPath"
169190>,
170191): PluginWebSearchProviderEntry {
171192return {
@@ -297,6 +318,8 @@ describe("finalizeSetupWizard", () => {
297318hasKeyInEnv.mockReturnValue(false);
298319listConfiguredWebSearchProviders.mockReset();
299320listConfiguredWebSearchProviders.mockReturnValue([]);
321+hasAuthProfileForProvider.mockReset();
322+hasAuthProfileForProvider.mockReturnValue(false);
300323});
301324302325it("resolves gateway password SecretRef for probe but omits auth from TUI hatch", async () => {
@@ -717,6 +740,67 @@ describe("finalizeSetupWizard", () => {
717740);
718741});
719742743+it("reports OAuth-backed web search as enabled without an API key", async () => {
744+listConfiguredWebSearchProviders.mockReturnValue([
745+createWebSearchProviderEntry({
746+id: "grok",
747+label: "Grok (xAI)",
748+hint: "Uses xAI OAuth or API key",
749+envVars: ["XAI_API_KEY"],
750+authProviderId: "xai",
751+placeholder: "xai-...",
752+signupUrl: "https://console.x.ai/",
753+credentialPath: "plugins.entries.xai.config.webSearch.apiKey",
754+}),
755+]);
756+hasAuthProfileForProvider.mockImplementation(
757+({ provider, type }) => provider === "xai" && (!type || type === "oauth"),
758+);
759+760+const prompter = createLaterPrompter();
761+762+await finalizeSetupWizard(
763+createAdvancedFinalizeArgs({
764+nextConfig: {
765+tools: {
766+web: {
767+search: {
768+provider: "grok",
769+enabled: true,
770+},
771+},
772+},
773+},
774+ prompter,
775+}),
776+);
777+778+expectNoteContains(
779+prompter,
780+"Web search is enabled, so your agent can look things up online when needed.",
781+"Web search",
782+);
783+expectNoteContains(prompter, "Credential: existing xAI OAuth sign-in.", "Web search");
784+expect(
785+vi
786+.mocked(prompter.note)
787+.mock.calls.some(
788+([message, title]) => title === "Web search" && message.includes("no API key"),
789+),
790+).toBe(false);
791+expect(hasAuthProfileForProvider).toHaveBeenCalledWith(
792+expect.objectContaining({
793+provider: "xai",
794+}),
795+);
796+expect(hasAuthProfileForProvider).toHaveBeenCalledWith(
797+expect.objectContaining({
798+provider: "xai",
799+type: "oauth",
800+}),
801+);
802+});
803+720804it("uses the setup token for health checks to avoid local env token drift", async () => {
721805vi.stubEnv("OPENCLAW_GATEWAY_TOKEN", "env-token");
722806const prompter = createLaterPrompter();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。