@openclaw/feishu 2026.5.2 → 2026.5.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accounts-Ba3-WP1z.js +423 -0
- package/dist/api.js +2280 -0
- package/dist/app-registration-B8qc1MCM.js +184 -0
- package/dist/audio-preflight.runtime-BPlzkO3l.js +7 -0
- package/dist/card-interaction-BfRLgvw_.js +96 -0
- package/dist/channel-CSD_Jt8I.js +1668 -0
- package/dist/channel-entry.js +22 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-DYsXcD36.js +700 -0
- package/dist/client-DBVoQL5w.js +157 -0
- package/dist/contract-api.js +9 -0
- package/dist/conversation-id-DWS3Ep2A.js +139 -0
- package/dist/directory.static-f3EeoRJd.js +44 -0
- package/dist/drive-C5eJLJr7.js +883 -0
- package/dist/index.js +68 -0
- package/dist/monitor-CT189QfR.js +60 -0
- package/dist/monitor.account-dJV2jO8C.js +4990 -0
- package/dist/monitor.state-DYM02ipp.js +100 -0
- package/dist/policy-D6c-wMPl.js +118 -0
- package/dist/probe-BNzzU_uR.js +149 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/runtime-CG0DuRCy.js +8 -0
- package/dist/runtime-api.js +14 -0
- package/dist/secret-contract-Dm4Z_zQN.js +119 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-DqJdocrN.js +11 -0
- package/dist/security-audit-shared-ByuMx9cJ.js +38 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/send-DowxxbpH.js +1218 -0
- package/dist/session-conversation-B4nrW-vo.js +27 -0
- package/dist/session-key-api.js +2 -0
- package/dist/setup-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/subagent-hooks-C3UhPVLV.js +227 -0
- package/dist/subagent-hooks-api.js +23 -0
- package/dist/targets-JMFJRKSe.js +48 -0
- package/dist/thread-bindings-BmS6TLes.js +222 -0
- package/package.json +15 -6
- package/api.ts +0 -31
- package/channel-entry.ts +0 -20
- package/channel-plugin-api.ts +0 -1
- package/contract-api.ts +0 -16
- package/index.ts +0 -82
- package/runtime-api.ts +0 -55
- package/secret-contract-api.ts +0 -5
- package/security-contract-api.ts +0 -1
- package/session-key-api.ts +0 -1
- package/setup-api.ts +0 -3
- package/setup-entry.test.ts +0 -14
- package/setup-entry.ts +0 -13
- package/src/accounts.test.ts +0 -459
- package/src/accounts.ts +0 -326
- package/src/app-registration.ts +0 -331
- package/src/approval-auth.test.ts +0 -24
- package/src/approval-auth.ts +0 -25
- package/src/async.test.ts +0 -35
- package/src/async.ts +0 -104
- package/src/audio-preflight.runtime.ts +0 -9
- package/src/bitable.test.ts +0 -131
- package/src/bitable.ts +0 -762
- package/src/bot-content.ts +0 -474
- package/src/bot-group-name.test.ts +0 -108
- package/src/bot-runtime-api.ts +0 -12
- package/src/bot-sender-name.ts +0 -125
- package/src/bot.broadcast.test.ts +0 -463
- package/src/bot.card-action.test.ts +0 -577
- package/src/bot.checkBotMentioned.test.ts +0 -265
- package/src/bot.helpers.test.ts +0 -118
- package/src/bot.stripBotMention.test.ts +0 -126
- package/src/bot.test.ts +0 -3040
- package/src/bot.ts +0 -1559
- package/src/card-action.ts +0 -447
- package/src/card-interaction.test.ts +0 -129
- package/src/card-interaction.ts +0 -159
- package/src/card-test-helpers.ts +0 -47
- package/src/card-ux-approval.ts +0 -65
- package/src/card-ux-launcher.test.ts +0 -99
- package/src/card-ux-launcher.ts +0 -121
- package/src/card-ux-shared.ts +0 -33
- package/src/channel-runtime-api.ts +0 -16
- package/src/channel.runtime.ts +0 -47
- package/src/channel.test.ts +0 -959
- package/src/channel.ts +0 -1313
- package/src/chat-schema.ts +0 -25
- package/src/chat.test.ts +0 -196
- package/src/chat.ts +0 -188
- package/src/client.test.ts +0 -433
- package/src/client.ts +0 -290
- package/src/comment-dispatcher-runtime-api.ts +0 -6
- package/src/comment-dispatcher.test.ts +0 -169
- package/src/comment-dispatcher.ts +0 -107
- package/src/comment-handler-runtime-api.ts +0 -3
- package/src/comment-handler.test.ts +0 -486
- package/src/comment-handler.ts +0 -309
- package/src/comment-reaction.test.ts +0 -166
- package/src/comment-reaction.ts +0 -259
- package/src/comment-shared.test.ts +0 -182
- package/src/comment-shared.ts +0 -406
- package/src/comment-target.ts +0 -44
- package/src/config-schema.test.ts +0 -309
- package/src/config-schema.ts +0 -333
- package/src/conversation-id.test.ts +0 -18
- package/src/conversation-id.ts +0 -199
- package/src/dedup-runtime-api.ts +0 -1
- package/src/dedup.ts +0 -141
- package/src/directory.static.ts +0 -61
- package/src/directory.test.ts +0 -136
- package/src/directory.ts +0 -124
- package/src/doc-schema.ts +0 -182
- package/src/docx-batch-insert.test.ts +0 -91
- package/src/docx-batch-insert.ts +0 -223
- package/src/docx-color-text.ts +0 -154
- package/src/docx-table-ops.test.ts +0 -53
- package/src/docx-table-ops.ts +0 -316
- package/src/docx-types.ts +0 -38
- package/src/docx.account-selection.test.ts +0 -79
- package/src/docx.test.ts +0 -685
- package/src/docx.ts +0 -1616
- package/src/drive-schema.ts +0 -92
- package/src/drive.test.ts +0 -1219
- package/src/drive.ts +0 -829
- package/src/dynamic-agent.ts +0 -137
- package/src/event-types.ts +0 -45
- package/src/external-keys.test.ts +0 -20
- package/src/external-keys.ts +0 -19
- package/src/lifecycle.test-support.ts +0 -220
- package/src/media.test.ts +0 -900
- package/src/media.ts +0 -861
- package/src/mention-target.types.ts +0 -5
- package/src/mention.ts +0 -114
- package/src/message-action-contract.ts +0 -13
- package/src/monitor-state-runtime-api.ts +0 -7
- package/src/monitor-transport-runtime-api.ts +0 -7
- package/src/monitor.account.ts +0 -468
- package/src/monitor.acp-init-failure.lifecycle.test-support.ts +0 -219
- package/src/monitor.bot-identity.ts +0 -86
- package/src/monitor.bot-menu-handler.ts +0 -165
- package/src/monitor.bot-menu.lifecycle.test-support.ts +0 -224
- package/src/monitor.bot-menu.test.ts +0 -178
- package/src/monitor.broadcast.reply-once.lifecycle.test-support.ts +0 -264
- package/src/monitor.card-action.lifecycle.test-support.ts +0 -373
- package/src/monitor.cleanup.test.ts +0 -376
- package/src/monitor.comment-notice-handler.ts +0 -105
- package/src/monitor.comment.test.ts +0 -937
- package/src/monitor.comment.ts +0 -1386
- package/src/monitor.lifecycle.test.ts +0 -4
- package/src/monitor.message-handler.ts +0 -339
- package/src/monitor.reaction.lifecycle.test-support.ts +0 -68
- package/src/monitor.reaction.test.ts +0 -713
- package/src/monitor.startup.test.ts +0 -192
- package/src/monitor.startup.ts +0 -74
- package/src/monitor.state.defaults.test.ts +0 -46
- package/src/monitor.state.ts +0 -170
- package/src/monitor.synthetic-error.ts +0 -18
- package/src/monitor.test-mocks.ts +0 -45
- package/src/monitor.transport.ts +0 -424
- package/src/monitor.ts +0 -100
- package/src/monitor.webhook-e2e.test.ts +0 -272
- package/src/monitor.webhook-security.test.ts +0 -264
- package/src/monitor.webhook.test-helpers.ts +0 -116
- package/src/outbound-runtime-api.ts +0 -1
- package/src/outbound.test.ts +0 -935
- package/src/outbound.ts +0 -718
- package/src/perm-schema.ts +0 -52
- package/src/perm.ts +0 -170
- package/src/pins.ts +0 -108
- package/src/policy.test.ts +0 -334
- package/src/policy.ts +0 -236
- package/src/post.test.ts +0 -105
- package/src/post.ts +0 -275
- package/src/probe.test.ts +0 -275
- package/src/probe.ts +0 -166
- package/src/processing-claims.ts +0 -59
- package/src/qr-terminal.ts +0 -1
- package/src/reactions.ts +0 -123
- package/src/reasoning-preview.test.ts +0 -59
- package/src/reasoning-preview.ts +0 -20
- package/src/reply-dispatcher-runtime-api.ts +0 -7
- package/src/reply-dispatcher.test.ts +0 -1144
- package/src/reply-dispatcher.ts +0 -650
- package/src/runtime.ts +0 -9
- package/src/secret-contract.ts +0 -145
- package/src/secret-input.ts +0 -1
- package/src/security-audit-shared.ts +0 -69
- package/src/security-audit.test.ts +0 -61
- package/src/security-audit.ts +0 -1
- package/src/send-result.ts +0 -29
- package/src/send-target.test.ts +0 -80
- package/src/send-target.ts +0 -35
- package/src/send.reply-fallback.test.ts +0 -292
- package/src/send.test.ts +0 -550
- package/src/send.ts +0 -800
- package/src/sequential-key.test.ts +0 -72
- package/src/sequential-key.ts +0 -28
- package/src/sequential-queue.test.ts +0 -92
- package/src/sequential-queue.ts +0 -16
- package/src/session-conversation.ts +0 -42
- package/src/session-route.ts +0 -48
- package/src/setup-core.ts +0 -51
- package/src/setup-surface.test.ts +0 -174
- package/src/setup-surface.ts +0 -581
- package/src/streaming-card.test.ts +0 -190
- package/src/streaming-card.ts +0 -490
- package/src/subagent-hooks.test.ts +0 -603
- package/src/subagent-hooks.ts +0 -397
- package/src/targets.ts +0 -97
- package/src/test-support/lifecycle-test-support.ts +0 -453
- package/src/thread-bindings.test.ts +0 -143
- package/src/thread-bindings.ts +0 -330
- package/src/tool-account-routing.test.ts +0 -187
- package/src/tool-account.test.ts +0 -44
- package/src/tool-account.ts +0 -93
- package/src/tool-factory-test-harness.ts +0 -79
- package/src/tool-result.test.ts +0 -32
- package/src/tool-result.ts +0 -16
- package/src/tools-config.test.ts +0 -21
- package/src/tools-config.ts +0 -22
- package/src/types.ts +0 -104
- package/src/typing.test.ts +0 -144
- package/src/typing.ts +0 -214
- package/src/wiki-schema.ts +0 -55
- package/src/wiki.ts +0 -227
- package/subagent-hooks-api.ts +0 -31
- package/tsconfig.json +0 -16
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import type { FeishuMessageEvent } from "./bot.js";
|
|
3
|
-
import { getFeishuSequentialKey } from "./sequential-key.js";
|
|
4
|
-
|
|
5
|
-
function createTextEvent(params: {
|
|
6
|
-
text: string;
|
|
7
|
-
messageId?: string;
|
|
8
|
-
chatId?: string;
|
|
9
|
-
}): FeishuMessageEvent {
|
|
10
|
-
return {
|
|
11
|
-
sender: {
|
|
12
|
-
sender_id: {
|
|
13
|
-
open_id: "ou_sender_1",
|
|
14
|
-
user_id: "ou_user_1",
|
|
15
|
-
},
|
|
16
|
-
sender_type: "user",
|
|
17
|
-
},
|
|
18
|
-
message: {
|
|
19
|
-
message_id: params.messageId ?? "om_message_1",
|
|
20
|
-
chat_id: params.chatId ?? "oc_dm_chat",
|
|
21
|
-
chat_type: "p2p",
|
|
22
|
-
message_type: "text",
|
|
23
|
-
content: JSON.stringify({ text: params.text }),
|
|
24
|
-
},
|
|
25
|
-
} as FeishuMessageEvent;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe("getFeishuSequentialKey", () => {
|
|
29
|
-
it.each([
|
|
30
|
-
[createTextEvent({ text: "hello" }), "feishu:default:oc_dm_chat"],
|
|
31
|
-
[createTextEvent({ text: "/status" }), "feishu:default:oc_dm_chat"],
|
|
32
|
-
[createTextEvent({ text: "/stop" }), "feishu:default:oc_dm_chat:control"],
|
|
33
|
-
[createTextEvent({ text: "/btw what changed?" }), "feishu:default:oc_dm_chat:btw"],
|
|
34
|
-
])("resolves sequential key %#", (event, expected) => {
|
|
35
|
-
expect(
|
|
36
|
-
getFeishuSequentialKey({
|
|
37
|
-
accountId: "default",
|
|
38
|
-
event,
|
|
39
|
-
}),
|
|
40
|
-
).toBe(expected);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("keeps /btw on a stable per-chat lane across different message ids", () => {
|
|
44
|
-
const first = createTextEvent({ text: "/btw one", messageId: "om_message_1" });
|
|
45
|
-
const second = createTextEvent({ text: "/btw two", messageId: "om_message_2" });
|
|
46
|
-
|
|
47
|
-
expect(
|
|
48
|
-
getFeishuSequentialKey({
|
|
49
|
-
accountId: "default",
|
|
50
|
-
event: first,
|
|
51
|
-
}),
|
|
52
|
-
).toBe("feishu:default:oc_dm_chat:btw");
|
|
53
|
-
expect(
|
|
54
|
-
getFeishuSequentialKey({
|
|
55
|
-
accountId: "default",
|
|
56
|
-
event: second,
|
|
57
|
-
}),
|
|
58
|
-
).toBe("feishu:default:oc_dm_chat:btw");
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("falls back to a stable btw lane when the message id is unavailable", () => {
|
|
62
|
-
const event = createTextEvent({ text: "/btw what changed?" });
|
|
63
|
-
delete (event.message as { message_id?: string }).message_id;
|
|
64
|
-
|
|
65
|
-
expect(
|
|
66
|
-
getFeishuSequentialKey({
|
|
67
|
-
accountId: "default",
|
|
68
|
-
event,
|
|
69
|
-
}),
|
|
70
|
-
).toBe("feishu:default:oc_dm_chat:btw");
|
|
71
|
-
});
|
|
72
|
-
});
|
package/src/sequential-key.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isAbortRequestText,
|
|
3
|
-
isBtwRequestText,
|
|
4
|
-
} from "openclaw/plugin-sdk/command-primitives-runtime";
|
|
5
|
-
import { parseFeishuMessageEvent, type FeishuMessageEvent } from "./bot.js";
|
|
6
|
-
|
|
7
|
-
export function getFeishuSequentialKey(params: {
|
|
8
|
-
accountId: string;
|
|
9
|
-
event: FeishuMessageEvent;
|
|
10
|
-
botOpenId?: string;
|
|
11
|
-
botName?: string;
|
|
12
|
-
}): string {
|
|
13
|
-
const { accountId, event, botOpenId, botName } = params;
|
|
14
|
-
const chatId = event.message.chat_id?.trim() || "unknown";
|
|
15
|
-
const baseKey = `feishu:${accountId}:${chatId}`;
|
|
16
|
-
const parsed = parseFeishuMessageEvent(event, botOpenId, botName);
|
|
17
|
-
const text = parsed.content.trim();
|
|
18
|
-
|
|
19
|
-
if (isAbortRequestText(text)) {
|
|
20
|
-
return `${baseKey}:control`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (isBtwRequestText(text)) {
|
|
24
|
-
return `${baseKey}:btw`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return baseKey;
|
|
28
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createSequentialQueue } from "./sequential-queue.js";
|
|
3
|
-
|
|
4
|
-
function createDeferred() {
|
|
5
|
-
let resolve!: () => void;
|
|
6
|
-
const promise = new Promise<void>((res) => {
|
|
7
|
-
resolve = res;
|
|
8
|
-
});
|
|
9
|
-
return { promise, resolve };
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
describe("createSequentialQueue", () => {
|
|
13
|
-
it("serializes tasks for the same key", async () => {
|
|
14
|
-
const enqueue = createSequentialQueue();
|
|
15
|
-
const gate = createDeferred();
|
|
16
|
-
const order: string[] = [];
|
|
17
|
-
|
|
18
|
-
const first = enqueue("feishu:default:chat-1", async () => {
|
|
19
|
-
order.push("first:start");
|
|
20
|
-
await gate.promise;
|
|
21
|
-
order.push("first:end");
|
|
22
|
-
});
|
|
23
|
-
const second = enqueue("feishu:default:chat-1", async () => {
|
|
24
|
-
order.push("second:start");
|
|
25
|
-
order.push("second:end");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
await Promise.resolve();
|
|
29
|
-
expect(order).toEqual(["first:start"]);
|
|
30
|
-
|
|
31
|
-
gate.resolve();
|
|
32
|
-
await Promise.all([first, second]);
|
|
33
|
-
|
|
34
|
-
expect(order).toEqual(["first:start", "first:end", "second:start", "second:end"]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("allows different keys to run concurrently", async () => {
|
|
38
|
-
const enqueue = createSequentialQueue();
|
|
39
|
-
const gateA = createDeferred();
|
|
40
|
-
const gateB = createDeferred();
|
|
41
|
-
const order: string[] = [];
|
|
42
|
-
|
|
43
|
-
const first = enqueue("feishu:default:chat-1", async () => {
|
|
44
|
-
order.push("chat-1:start");
|
|
45
|
-
await gateA.promise;
|
|
46
|
-
order.push("chat-1:end");
|
|
47
|
-
});
|
|
48
|
-
const second = enqueue("feishu:default:chat-1:btw:om_2", async () => {
|
|
49
|
-
order.push("btw:start");
|
|
50
|
-
await gateB.promise;
|
|
51
|
-
order.push("btw:end");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
await Promise.resolve();
|
|
55
|
-
expect(order).toEqual(["chat-1:start", "btw:start"]);
|
|
56
|
-
|
|
57
|
-
gateA.resolve();
|
|
58
|
-
gateB.resolve();
|
|
59
|
-
await Promise.all([first, second]);
|
|
60
|
-
|
|
61
|
-
expect(order).toContain("chat-1:end");
|
|
62
|
-
expect(order).toContain("btw:end");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("does not leak unhandled rejections when a queued task fails", async () => {
|
|
66
|
-
const enqueue = createSequentialQueue();
|
|
67
|
-
const unhandled: unknown[] = [];
|
|
68
|
-
const onUnhandledRejection = (reason: unknown) => {
|
|
69
|
-
unhandled.push(reason);
|
|
70
|
-
};
|
|
71
|
-
process.on("unhandledRejection", onUnhandledRejection);
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
await expect(
|
|
75
|
-
enqueue("feishu:default:chat-1", async () => {
|
|
76
|
-
throw new Error("boom");
|
|
77
|
-
}),
|
|
78
|
-
).rejects.toThrow("boom");
|
|
79
|
-
|
|
80
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
81
|
-
expect(unhandled).toEqual([]);
|
|
82
|
-
|
|
83
|
-
await expect(
|
|
84
|
-
enqueue("feishu:default:chat-1", async () => {
|
|
85
|
-
return;
|
|
86
|
-
}),
|
|
87
|
-
).resolves.toBeUndefined();
|
|
88
|
-
} finally {
|
|
89
|
-
process.off("unhandledRejection", onUnhandledRejection);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
});
|
package/src/sequential-queue.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export function createSequentialQueue() {
|
|
2
|
-
const queues = new Map<string, Promise<void>>();
|
|
3
|
-
|
|
4
|
-
return (key: string, task: () => Promise<void>): Promise<void> => {
|
|
5
|
-
const previous = queues.get(key) ?? Promise.resolve();
|
|
6
|
-
const next = previous.then(task, task);
|
|
7
|
-
queues.set(key, next);
|
|
8
|
-
const cleanup = () => {
|
|
9
|
-
if (queues.get(key) === next) {
|
|
10
|
-
queues.delete(key);
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
next.then(cleanup, cleanup);
|
|
14
|
-
return next;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { buildFeishuConversationId, parseFeishuConversationId } from "./conversation-id.js";
|
|
2
|
-
|
|
3
|
-
function resolveFeishuParentConversationCandidates(rawId: string): string[] {
|
|
4
|
-
const parsed = parseFeishuConversationId({ conversationId: rawId });
|
|
5
|
-
if (!parsed) {
|
|
6
|
-
return [];
|
|
7
|
-
}
|
|
8
|
-
switch (parsed.scope) {
|
|
9
|
-
case "group_topic_sender":
|
|
10
|
-
return [
|
|
11
|
-
buildFeishuConversationId({
|
|
12
|
-
chatId: parsed.chatId,
|
|
13
|
-
scope: "group_topic",
|
|
14
|
-
topicId: parsed.topicId,
|
|
15
|
-
}),
|
|
16
|
-
parsed.chatId,
|
|
17
|
-
];
|
|
18
|
-
case "group_topic":
|
|
19
|
-
case "group_sender":
|
|
20
|
-
return [parsed.chatId];
|
|
21
|
-
case "group":
|
|
22
|
-
default:
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function resolveFeishuSessionConversation(params: {
|
|
28
|
-
kind: "group" | "channel";
|
|
29
|
-
rawId: string;
|
|
30
|
-
}) {
|
|
31
|
-
const parsed = parseFeishuConversationId({ conversationId: params.rawId });
|
|
32
|
-
if (!parsed) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
id: parsed.canonicalConversationId,
|
|
37
|
-
baseConversationId: parsed.chatId,
|
|
38
|
-
parentConversationCandidates: resolveFeishuParentConversationCandidates(
|
|
39
|
-
parsed.canonicalConversationId,
|
|
40
|
-
),
|
|
41
|
-
};
|
|
42
|
-
}
|
package/src/session-route.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildChannelOutboundSessionRoute,
|
|
3
|
-
stripChannelTargetPrefix,
|
|
4
|
-
type ChannelOutboundSessionRouteParams,
|
|
5
|
-
} from "openclaw/plugin-sdk/channel-core";
|
|
6
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
7
|
-
|
|
8
|
-
export function resolveFeishuOutboundSessionRoute(params: ChannelOutboundSessionRouteParams) {
|
|
9
|
-
let trimmed = stripChannelTargetPrefix(params.target, "feishu", "lark");
|
|
10
|
-
if (!trimmed) {
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const lower = normalizeLowercaseStringOrEmpty(trimmed);
|
|
15
|
-
let isGroup = false;
|
|
16
|
-
let typeExplicit = false;
|
|
17
|
-
|
|
18
|
-
if (lower.startsWith("group:") || lower.startsWith("chat:") || lower.startsWith("channel:")) {
|
|
19
|
-
trimmed = trimmed.replace(/^(group|chat|channel):/i, "").trim();
|
|
20
|
-
isGroup = true;
|
|
21
|
-
typeExplicit = true;
|
|
22
|
-
} else if (lower.startsWith("user:") || lower.startsWith("dm:")) {
|
|
23
|
-
trimmed = trimmed.replace(/^(user|dm):/i, "").trim();
|
|
24
|
-
isGroup = false;
|
|
25
|
-
typeExplicit = true;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!typeExplicit) {
|
|
29
|
-
const idLower = normalizeLowercaseStringOrEmpty(trimmed);
|
|
30
|
-
if (idLower.startsWith("ou_") || idLower.startsWith("on_")) {
|
|
31
|
-
isGroup = false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return buildChannelOutboundSessionRoute({
|
|
36
|
-
cfg: params.cfg,
|
|
37
|
-
agentId: params.agentId,
|
|
38
|
-
channel: "feishu",
|
|
39
|
-
accountId: params.accountId,
|
|
40
|
-
peer: {
|
|
41
|
-
kind: isGroup ? "group" : "direct",
|
|
42
|
-
id: trimmed,
|
|
43
|
-
},
|
|
44
|
-
chatType: isGroup ? "group" : "direct",
|
|
45
|
-
from: isGroup ? `feishu:group:${trimmed}` : `feishu:${trimmed}`,
|
|
46
|
-
to: trimmed,
|
|
47
|
-
});
|
|
48
|
-
}
|
package/src/setup-core.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_ACCOUNT_ID,
|
|
3
|
-
type ChannelSetupAdapter,
|
|
4
|
-
type OpenClawConfig,
|
|
5
|
-
} from "openclaw/plugin-sdk/setup";
|
|
6
|
-
import { resolveDefaultFeishuAccountId } from "./accounts.js";
|
|
7
|
-
import type { FeishuConfig } from "./types.js";
|
|
8
|
-
|
|
9
|
-
export function setFeishuNamedAccountEnabled(
|
|
10
|
-
cfg: OpenClawConfig,
|
|
11
|
-
accountId: string,
|
|
12
|
-
enabled: boolean,
|
|
13
|
-
): OpenClawConfig {
|
|
14
|
-
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
|
15
|
-
return {
|
|
16
|
-
...cfg,
|
|
17
|
-
channels: {
|
|
18
|
-
...cfg.channels,
|
|
19
|
-
feishu: {
|
|
20
|
-
...feishuCfg,
|
|
21
|
-
accounts: {
|
|
22
|
-
...feishuCfg?.accounts,
|
|
23
|
-
[accountId]: {
|
|
24
|
-
...feishuCfg?.accounts?.[accountId],
|
|
25
|
-
enabled,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const feishuSetupAdapter: ChannelSetupAdapter = {
|
|
34
|
-
resolveAccountId: ({ cfg, accountId }) => accountId?.trim() || resolveDefaultFeishuAccountId(cfg),
|
|
35
|
-
applyAccountConfig: ({ cfg, accountId }) => {
|
|
36
|
-
const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;
|
|
37
|
-
if (isDefault) {
|
|
38
|
-
return {
|
|
39
|
-
...cfg,
|
|
40
|
-
channels: {
|
|
41
|
-
...cfg.channels,
|
|
42
|
-
feishu: {
|
|
43
|
-
...cfg.channels?.feishu,
|
|
44
|
-
enabled: true,
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
return setFeishuNamedAccountEnabled(cfg, accountId, true);
|
|
50
|
-
},
|
|
51
|
-
};
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createNonExitingRuntimeEnv,
|
|
3
|
-
createPluginSetupWizardConfigure,
|
|
4
|
-
createPluginSetupWizardStatus,
|
|
5
|
-
createTestWizardPrompter,
|
|
6
|
-
runSetupWizardConfigure,
|
|
7
|
-
} from "openclaw/plugin-sdk/plugin-test-runtime";
|
|
8
|
-
import { describe, expect, it, vi } from "vitest";
|
|
9
|
-
|
|
10
|
-
vi.mock("./probe.js", () => ({
|
|
11
|
-
probeFeishu: vi.fn(async () => ({ ok: false, error: "mocked" })),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
vi.mock("./app-registration.js", () => ({
|
|
15
|
-
initAppRegistration: vi.fn(async () => {
|
|
16
|
-
throw new Error("mocked: scan-to-create not available");
|
|
17
|
-
}),
|
|
18
|
-
beginAppRegistration: vi.fn(),
|
|
19
|
-
pollAppRegistration: vi.fn(),
|
|
20
|
-
printQrCode: vi.fn(async () => {}),
|
|
21
|
-
getAppOwnerOpenId: vi.fn(async () => undefined),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
import { feishuPlugin } from "./channel.js";
|
|
25
|
-
|
|
26
|
-
const baseStatusContext = {
|
|
27
|
-
accountOverrides: {},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
async function withEnvVars(values: Record<string, string | undefined>, run: () => Promise<void>) {
|
|
31
|
-
const previous = new Map<string, string | undefined>();
|
|
32
|
-
for (const [key, value] of Object.entries(values)) {
|
|
33
|
-
previous.set(key, process.env[key]);
|
|
34
|
-
if (value === undefined) {
|
|
35
|
-
delete process.env[key];
|
|
36
|
-
} else {
|
|
37
|
-
process.env[key] = value;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
await run();
|
|
43
|
-
} finally {
|
|
44
|
-
for (const [key, prior] of previous.entries()) {
|
|
45
|
-
if (prior === undefined) {
|
|
46
|
-
delete process.env[key];
|
|
47
|
-
} else {
|
|
48
|
-
process.env[key] = prior;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function getStatusWithEnvRefs(params: { appIdKey: string; appSecretKey: string }) {
|
|
55
|
-
return await feishuGetStatus({
|
|
56
|
-
cfg: {
|
|
57
|
-
channels: {
|
|
58
|
-
feishu: {
|
|
59
|
-
appId: { source: "env", id: params.appIdKey, provider: "default" },
|
|
60
|
-
appSecret: { source: "env", id: params.appSecretKey, provider: "default" },
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
} as never,
|
|
64
|
-
...baseStatusContext,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const feishuConfigure = createPluginSetupWizardConfigure(feishuPlugin);
|
|
69
|
-
const feishuGetStatus = createPluginSetupWizardStatus(feishuPlugin);
|
|
70
|
-
|
|
71
|
-
describe("feishu setup wizard", () => {
|
|
72
|
-
it("does not throw when config appId/appSecret are SecretRef objects", async () => {
|
|
73
|
-
const text = vi
|
|
74
|
-
.fn()
|
|
75
|
-
.mockResolvedValueOnce("cli_from_prompt")
|
|
76
|
-
.mockResolvedValueOnce("secret_from_prompt");
|
|
77
|
-
const prompter = createTestWizardPrompter({
|
|
78
|
-
text,
|
|
79
|
-
confirm: vi.fn(async () => true),
|
|
80
|
-
select: vi.fn(
|
|
81
|
-
async ({ initialValue }: { initialValue?: string }) => initialValue ?? "bot",
|
|
82
|
-
) as never,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
await expect(
|
|
86
|
-
runSetupWizardConfigure({
|
|
87
|
-
configure: feishuConfigure,
|
|
88
|
-
cfg: {
|
|
89
|
-
channels: {
|
|
90
|
-
feishu: {
|
|
91
|
-
appId: { source: "env", id: "FEISHU_APP_ID", provider: "default" },
|
|
92
|
-
appSecret: { source: "env", id: "FEISHU_APP_SECRET", provider: "default" },
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
} as never,
|
|
96
|
-
prompter,
|
|
97
|
-
runtime: createNonExitingRuntimeEnv(),
|
|
98
|
-
}),
|
|
99
|
-
).resolves.toBeTruthy();
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe("feishu setup wizard status", () => {
|
|
104
|
-
it("treats SecretRef appSecret as configured when appId is present", async () => {
|
|
105
|
-
const status = await feishuGetStatus({
|
|
106
|
-
cfg: {
|
|
107
|
-
channels: {
|
|
108
|
-
feishu: {
|
|
109
|
-
appId: "cli_a123456",
|
|
110
|
-
appSecret: {
|
|
111
|
-
source: "env",
|
|
112
|
-
provider: "default",
|
|
113
|
-
id: "FEISHU_APP_SECRET",
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
} as never,
|
|
118
|
-
accountOverrides: {},
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
expect(status.configured).toBe(true);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("does not fallback to top-level appId when account explicitly sets empty appId", async () => {
|
|
125
|
-
const status = await feishuGetStatus({
|
|
126
|
-
cfg: {
|
|
127
|
-
channels: {
|
|
128
|
-
feishu: {
|
|
129
|
-
appId: "top_level_app",
|
|
130
|
-
accounts: {
|
|
131
|
-
main: {
|
|
132
|
-
appId: "",
|
|
133
|
-
appSecret: "sample-app-credential", // pragma: allowlist secret
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
} as never,
|
|
139
|
-
...baseStatusContext,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
expect(status.configured).toBe(false);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("treats env SecretRef appId as not configured when env var is missing", async () => {
|
|
146
|
-
const appIdKey = "FEISHU_APP_ID_STATUS_MISSING_TEST";
|
|
147
|
-
const appSecretKey = "FEISHU_APP_CREDENTIAL_STATUS_MISSING_TEST"; // pragma: allowlist secret
|
|
148
|
-
await withEnvVars(
|
|
149
|
-
{
|
|
150
|
-
[appIdKey]: undefined,
|
|
151
|
-
[appSecretKey]: "env-credential-456", // pragma: allowlist secret
|
|
152
|
-
},
|
|
153
|
-
async () => {
|
|
154
|
-
const status = await getStatusWithEnvRefs({ appIdKey, appSecretKey });
|
|
155
|
-
expect(status.configured).toBe(false);
|
|
156
|
-
},
|
|
157
|
-
);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("treats env SecretRef appId/appSecret as configured in status", async () => {
|
|
161
|
-
const appIdKey = "FEISHU_APP_ID_STATUS_TEST";
|
|
162
|
-
const appSecretKey = "FEISHU_APP_CREDENTIAL_STATUS_TEST"; // pragma: allowlist secret
|
|
163
|
-
await withEnvVars(
|
|
164
|
-
{
|
|
165
|
-
[appIdKey]: "cli_env_123",
|
|
166
|
-
[appSecretKey]: "env-credential-456", // pragma: allowlist secret
|
|
167
|
-
},
|
|
168
|
-
async () => {
|
|
169
|
-
const status = await getStatusWithEnvRefs({ appIdKey, appSecretKey });
|
|
170
|
-
expect(status.configured).toBe(true);
|
|
171
|
-
},
|
|
172
|
-
);
|
|
173
|
-
});
|
|
174
|
-
});
|