From 10c9200f759ea3b0b7507ee400e4392c580bae0d Mon Sep 17 00:00:00 2001 From: "clawsweeper[bot]" <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Sun, 3 May 2026 00:19:36 +0000 Subject: [PATCH] [AI-assisted] fix(feishu): probe status with account credentials (#76321) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: - The PR resolves Feishu setup/status probes through the selected/default account, adds a multi-account regression test, and adds an Unreleased changelog fix entry. - Reproducibility: yes. Current main can be checked by calling the Feishu setup-status adapter with a config t ... hannels.feishu.accounts.; the status path passes accountId but Feishu currently ignores it for probing. ClawSweeper fixups: - Included follow-up commit: fix(clawsweeper): address review for automerge-openclaw-openclaw-7406… - Included follow-up commit: [AI-assisted] fix(feishu): probe status with account credentials - Ran the ClawSweeper repair loop before final review. Validation: - ClawSweeper review passed for head 427e8e8b074f1bb7a57339c711c157c64bc41656. - Required merge gates passed before the squash merge. Prepared head SHA: 427e8e8b074f1bb7a57339c711c157c64bc41656 Review: https://github.com/openclaw/openclaw/pull/76321#issuecomment-4364994746 Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: brokemac79 <255583030+brokemac79@users.noreply.github.com> --- CHANGELOG.md | 1 + extensions/feishu/src/setup-surface.test.ts | 55 ++++++++++++++++++++- extensions/feishu/src/setup-surface.ts | 11 ++--- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd7e71311d..fe22816408e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Maintainer workflow: push prepared PR heads through GitHub's verified commit API by default and require an explicit override before git-protocol pushes can publish unsigned commits. Thanks @BunsDev. +- Feishu: resolve setup/status probes through the selected/default account so multi-account configs with account-scoped app credentials show as configured and probeable. Fixes #72930. Thanks @brokemac79. - Gateway/responses: emit every client tool call from `/v1/responses` JSON and SSE responses when the agent invokes multiple client tools in a single turn, so multi-tool plans, graph orchestration calls, and similar batched flows no longer drop every call but the last. Fixes #52288. Thanks @CharZhou and @bonelli. - Slack/reactions: treat missing no_reaction remove responses as idempotent success and route own-reaction cleanup through the remove helper, so concurrent cleanup no longer surfaces Slack race errors. Fixes #50733. (#76304) Thanks @martingarramon and @Hollychou924. - Control UI/Gateway: avoid full session-list reloads for locally applied message-phase session updates, carry known session keys through transcript-file update events, and defer media provider listing when explicit generation model config is present. Refs #76236, #76203, #76188, #76107, and #76166. Thanks @BunsDev. diff --git a/extensions/feishu/src/setup-surface.test.ts b/extensions/feishu/src/setup-surface.test.ts index c8f1a15fdd0..db60f8fada8 100644 --- a/extensions/feishu/src/setup-surface.test.ts +++ b/extensions/feishu/src/setup-surface.test.ts @@ -5,10 +5,18 @@ import { createTestWizardPrompter, runSetupWizardConfigure, } from "openclaw/plugin-sdk/plugin-test-runtime"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { FeishuProbeResult } from "./types.js"; + +const { probeFeishuMock } = vi.hoisted(() => ({ + probeFeishuMock: vi.fn<() => Promise>(async () => ({ + ok: false, + error: "mocked", + })), +})); vi.mock("./probe.js", () => ({ - probeFeishu: vi.fn(async () => ({ ok: false, error: "mocked" })), + probeFeishu: probeFeishuMock, })); vi.mock("./app-registration.js", () => ({ @@ -69,6 +77,11 @@ const feishuConfigure = createPluginSetupWizardConfigure(feishuPlugin); const feishuGetStatus = createPluginSetupWizardStatus(feishuPlugin); describe("feishu setup wizard", () => { + beforeEach(() => { + probeFeishuMock.mockReset(); + probeFeishuMock.mockResolvedValue({ ok: false, error: "mocked" }); + }); + it("does not throw when config appId/appSecret are SecretRef objects", async () => { const text = vi .fn() @@ -101,6 +114,11 @@ describe("feishu setup wizard", () => { }); describe("feishu setup wizard status", () => { + beforeEach(() => { + probeFeishuMock.mockReset(); + probeFeishuMock.mockResolvedValue({ ok: false, error: "mocked" }); + }); + it("treats SecretRef appSecret as configured when appId is present", async () => { const status = await feishuGetStatus({ cfg: { @@ -121,6 +139,39 @@ describe("feishu setup wizard status", () => { expect(status.configured).toBe(true); }); + it("probes the resolved default account in multi-account config", async () => { + probeFeishuMock.mockResolvedValueOnce({ ok: true, botName: "Feishu Main" }); + + const status = await feishuGetStatus({ + cfg: { + channels: { + feishu: { + enabled: true, + defaultAccount: "main-bot", + accounts: { + "main-bot": { + appId: "cli_main", + appSecret: "main-app-secret", // pragma: allowlist secret + connectionMode: "websocket", + }, + }, + }, + }, + } as never, + ...baseStatusContext, + }); + + expect(status.configured).toBe(true); + expect(status.statusLines).toEqual(["Feishu: connected as Feishu Main"]); + expect(probeFeishuMock).toHaveBeenCalledWith( + expect.objectContaining({ + accountId: "main-bot", + appId: "cli_main", + appSecret: "main-app-secret", // pragma: allowlist secret + }), + ); + }); + it("does not fallback to top-level appId when account explicitly sets empty appId", async () => { const status = await feishuGetStatus({ cfg: { diff --git a/extensions/feishu/src/setup-surface.ts b/extensions/feishu/src/setup-surface.ts index ec207dca658..5f4fc8008c3 100644 --- a/extensions/feishu/src/setup-surface.ts +++ b/extensions/feishu/src/setup-surface.ts @@ -12,7 +12,7 @@ import { type OpenClawConfig, type SecretInput, } from "openclaw/plugin-sdk/setup"; -import { inspectFeishuCredentials, resolveDefaultFeishuAccountId } from "./accounts.js"; +import { resolveDefaultFeishuAccountId, resolveFeishuAccount } from "./accounts.js"; import type { AppRegistrationResult } from "./app-registration.js"; import type { FeishuConfig, FeishuDomain } from "./types.js"; @@ -515,14 +515,13 @@ export const feishuSetupWizard: ChannelSetupWizard = { configuredScore: 2, unconfiguredScore: 0, resolveConfigured: ({ cfg }) => isFeishuConfigured(cfg), - resolveStatusLines: async ({ cfg, configured }) => { - const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined; - const resolvedCredentials = inspectFeishuCredentials(feishuCfg); + resolveStatusLines: async ({ cfg, accountId, configured }) => { + const account = resolveFeishuAccount({ cfg, accountId }); let probeResult = null; - if (configured && resolvedCredentials) { + if (configured && account.configured) { try { const { probeFeishu } = await import("./probe.js"); - probeResult = await probeFeishu(resolvedCredentials); + probeResult = await probeFeishu(account); } catch {} } if (!configured) {