mirror of https://github.com/openclaw/openclaw.git
feishu: fix schema 2.0 card config in interactive card UX functions (#53395)
Merged via squash.
Prepared head SHA: 31f2396404
Co-authored-by: drvoss <3031622+drvoss@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
parent
375bd73ce1
commit
6e28bd2eb6
|
|
@ -201,6 +201,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Mattermost/slash commands: harden native slash-command callback token validation to use constant-time secret comparison, matching the existing interaction-token path.
|
||||
- Agents/scheduling: route delayed follow-up requests toward cron only when cron is actually available, while keeping background `exec`/`process` guidance scoped to work that starts now. (#60811) Thanks @vincentkoc.
|
||||
- Cron/security: reject unsafe custom `sessionTarget: "session:..."` IDs earlier during cron add, update, and execution so malformed custom session keys fail closed with clear errors.
|
||||
- Feishu/cards: replace the legacy `wide_screen_mode` schema 1.x config with schema 2.0 `width_mode: "fill"` in interactive approval, launcher, markdown, and structured card builders so Feishu card sends stop failing with parse-card errors while preserving wide-card rendering. (#53395) Thanks @drvoss
|
||||
|
||||
## 2026.4.1
|
||||
|
||||
|
|
|
|||
|
|
@ -195,6 +195,9 @@ describe("Feishu Card Action Handler", () => {
|
|||
to: "chat:chat1",
|
||||
accountId: "main",
|
||||
card: expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
width_mode: "fill",
|
||||
}),
|
||||
header: expect.objectContaining({
|
||||
title: expect.objectContaining({ content: "Confirm action" }),
|
||||
}),
|
||||
|
|
@ -220,6 +223,21 @@ describe("Feishu Card Action Handler", () => {
|
|||
}),
|
||||
}),
|
||||
);
|
||||
const firstSendArg = (sendCardFeishuMock.mock.calls as unknown[][]).at(0)?.[0] as
|
||||
| {
|
||||
card?: {
|
||||
config?: {
|
||||
width_mode?: string;
|
||||
wide_screen_mode?: boolean;
|
||||
enable_forward?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
| undefined;
|
||||
const sentCard = firstSendArg?.card;
|
||||
expect(sentCard).toBeDefined();
|
||||
expect(sentCard?.config?.wide_screen_mode).toBeUndefined();
|
||||
expect(sentCard?.config?.enable_forward).toBeUndefined();
|
||||
expect(handleFeishuMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export function createApprovalCard(params: {
|
|||
return {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
wide_screen_mode: true,
|
||||
width_mode: "fill",
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ describe("feishu quick-action launcher", () => {
|
|||
expiresAt: 123,
|
||||
sessionKey: "agent:codex:feishu:chat:chat1",
|
||||
}) as {
|
||||
config: {
|
||||
width_mode?: string;
|
||||
enable_forward?: boolean;
|
||||
wide_screen_mode?: boolean;
|
||||
};
|
||||
body: {
|
||||
elements: Array<{
|
||||
tag: string;
|
||||
|
|
@ -40,6 +45,9 @@ describe("feishu quick-action launcher", () => {
|
|||
};
|
||||
};
|
||||
|
||||
expect(card.config.width_mode).toBe("fill");
|
||||
expect(card.config.enable_forward).toBeUndefined();
|
||||
expect(card.config.wide_screen_mode).toBeUndefined();
|
||||
const actionBlock = card.body.elements.find((entry) => entry.tag === "action");
|
||||
expect(actionBlock?.actions).toHaveLength(3);
|
||||
expect(actionBlock?.actions?.[0]?.value?.oc).toBe("ocf1");
|
||||
|
|
@ -64,6 +72,9 @@ describe("feishu quick-action launcher", () => {
|
|||
to: "user:u123",
|
||||
accountId: "main",
|
||||
card: expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
width_mode: "fill",
|
||||
}),
|
||||
body: expect.objectContaining({
|
||||
elements: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
|
@ -83,6 +94,21 @@ describe("feishu quick-action launcher", () => {
|
|||
}),
|
||||
}),
|
||||
);
|
||||
const firstSendArg = (sendCardFeishuMock.mock.calls as unknown[][]).at(0)?.[0] as
|
||||
| {
|
||||
card?: {
|
||||
config?: {
|
||||
width_mode?: string;
|
||||
wide_screen_mode?: boolean;
|
||||
enable_forward?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
| undefined;
|
||||
const sentCard = firstSendArg?.card;
|
||||
expect(sentCard).toBeDefined();
|
||||
expect(sentCard?.config?.wide_screen_mode).toBeUndefined();
|
||||
expect(sentCard?.config?.enable_forward).toBeUndefined();
|
||||
});
|
||||
|
||||
it("falls back to legacy menu handling when launcher send fails", async () => {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export function createQuickActionLauncherCard(params: {
|
|||
return {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
wide_screen_mode: true,
|
||||
width_mode: "fill",
|
||||
},
|
||||
header: {
|
||||
title: {
|
||||
|
|
|
|||
|
|
@ -143,6 +143,9 @@ describe("Feishu bot menu handler", () => {
|
|||
expect.objectContaining({
|
||||
to: "user:ou_user1",
|
||||
card: expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
width_mode: "fill",
|
||||
}),
|
||||
header: expect.objectContaining({
|
||||
title: expect.objectContaining({ content: "Quick actions" }),
|
||||
}),
|
||||
|
|
@ -212,5 +215,20 @@ describe("Feishu bot menu handler", () => {
|
|||
}),
|
||||
);
|
||||
});
|
||||
const firstSendArg = (sendCardFeishuMock.mock.calls as unknown[][]).at(0)?.[0] as
|
||||
| {
|
||||
card?: {
|
||||
config?: {
|
||||
width_mode?: string;
|
||||
wide_screen_mode?: boolean;
|
||||
enable_forward?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
| undefined;
|
||||
const sentCard = firstSendArg?.card;
|
||||
expect(sentCard).toBeDefined();
|
||||
expect(sentCard?.config?.wide_screen_mode).toBeUndefined();
|
||||
expect(sentCard?.config?.enable_forward).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ClawdbotConfig } from "../runtime-api.js";
|
||||
import { buildMarkdownCard } from "./send.js";
|
||||
|
||||
const {
|
||||
mockConvertMarkdownTables,
|
||||
|
|
@ -406,6 +407,20 @@ describe("resolveFeishuCardTemplate", () => {
|
|||
});
|
||||
|
||||
describe("buildStructuredCard", () => {
|
||||
it("uses schema-2.0 width config instead of legacy wide screen mode", () => {
|
||||
const card = buildStructuredCard("hello") as {
|
||||
config: {
|
||||
width_mode?: string;
|
||||
enable_forward?: boolean;
|
||||
wide_screen_mode?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
expect(card.config.width_mode).toBe("fill");
|
||||
expect(card.config.enable_forward).toBeUndefined();
|
||||
expect(card.config.wide_screen_mode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("falls back to blue when the header template is unsupported", () => {
|
||||
const card = buildStructuredCard("hello", {
|
||||
header: {
|
||||
|
|
@ -424,3 +439,19 @@ describe("buildStructuredCard", () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildMarkdownCard", () => {
|
||||
it("uses schema-2.0 width config instead of legacy wide screen mode", () => {
|
||||
const card = buildMarkdownCard("hello") as {
|
||||
config: {
|
||||
width_mode?: string;
|
||||
enable_forward?: boolean;
|
||||
wide_screen_mode?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
expect(card.config.width_mode).toBe("fill");
|
||||
expect(card.config.enable_forward).toBeUndefined();
|
||||
expect(card.config.wide_screen_mode).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -587,7 +587,7 @@ export function buildMarkdownCard(text: string): Record<string, unknown> {
|
|||
return {
|
||||
schema: "2.0",
|
||||
config: {
|
||||
wide_screen_mode: true,
|
||||
width_mode: "fill",
|
||||
},
|
||||
body: {
|
||||
elements: [
|
||||
|
|
@ -634,7 +634,7 @@ export function buildStructuredCard(
|
|||
}
|
||||
const card: Record<string, unknown> = {
|
||||
schema: "2.0",
|
||||
config: { wide_screen_mode: true },
|
||||
config: { width_mode: "fill" },
|
||||
body: { elements },
|
||||
};
|
||||
if (options?.header) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue