@openclaw/feishu 2026.5.2 → 2026.5.3-beta.1
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
package/src/perm-schema.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { Type, type Static } from "typebox";
|
|
2
|
-
|
|
3
|
-
const TokenType = Type.Union([
|
|
4
|
-
Type.Literal("doc"),
|
|
5
|
-
Type.Literal("docx"),
|
|
6
|
-
Type.Literal("sheet"),
|
|
7
|
-
Type.Literal("bitable"),
|
|
8
|
-
Type.Literal("folder"),
|
|
9
|
-
Type.Literal("file"),
|
|
10
|
-
Type.Literal("wiki"),
|
|
11
|
-
Type.Literal("mindnote"),
|
|
12
|
-
]);
|
|
13
|
-
|
|
14
|
-
const MemberType = Type.Union([
|
|
15
|
-
Type.Literal("email"),
|
|
16
|
-
Type.Literal("openid"),
|
|
17
|
-
Type.Literal("userid"),
|
|
18
|
-
Type.Literal("unionid"),
|
|
19
|
-
Type.Literal("openchat"),
|
|
20
|
-
Type.Literal("opendepartmentid"),
|
|
21
|
-
]);
|
|
22
|
-
|
|
23
|
-
const Permission = Type.Union([
|
|
24
|
-
Type.Literal("view"),
|
|
25
|
-
Type.Literal("edit"),
|
|
26
|
-
Type.Literal("full_access"),
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
export const FeishuPermSchema = Type.Union([
|
|
30
|
-
Type.Object({
|
|
31
|
-
action: Type.Literal("list"),
|
|
32
|
-
token: Type.String({ description: "File token" }),
|
|
33
|
-
type: TokenType,
|
|
34
|
-
}),
|
|
35
|
-
Type.Object({
|
|
36
|
-
action: Type.Literal("add"),
|
|
37
|
-
token: Type.String({ description: "File token" }),
|
|
38
|
-
type: TokenType,
|
|
39
|
-
member_type: MemberType,
|
|
40
|
-
member_id: Type.String({ description: "Member ID (email, open_id, user_id, etc.)" }),
|
|
41
|
-
perm: Permission,
|
|
42
|
-
}),
|
|
43
|
-
Type.Object({
|
|
44
|
-
action: Type.Literal("remove"),
|
|
45
|
-
token: Type.String({ description: "File token" }),
|
|
46
|
-
type: TokenType,
|
|
47
|
-
member_type: MemberType,
|
|
48
|
-
member_id: Type.String({ description: "Member ID to remove" }),
|
|
49
|
-
}),
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
export type FeishuPermParams = Static<typeof FeishuPermSchema>;
|
package/src/perm.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
2
|
-
import type { OpenClawPluginApi } from "../runtime-api.js";
|
|
3
|
-
import { listEnabledFeishuAccounts } from "./accounts.js";
|
|
4
|
-
import { FeishuPermSchema, type FeishuPermParams } from "./perm-schema.js";
|
|
5
|
-
import { createFeishuToolClient, resolveAnyEnabledFeishuToolsConfig } from "./tool-account.js";
|
|
6
|
-
import {
|
|
7
|
-
jsonToolResult,
|
|
8
|
-
toolExecutionErrorResult,
|
|
9
|
-
unknownToolActionResult,
|
|
10
|
-
} from "./tool-result.js";
|
|
11
|
-
|
|
12
|
-
type ListTokenType =
|
|
13
|
-
| "doc"
|
|
14
|
-
| "sheet"
|
|
15
|
-
| "file"
|
|
16
|
-
| "wiki"
|
|
17
|
-
| "bitable"
|
|
18
|
-
| "docx"
|
|
19
|
-
| "mindnote"
|
|
20
|
-
| "minutes"
|
|
21
|
-
| "slides";
|
|
22
|
-
type CreateTokenType =
|
|
23
|
-
| "doc"
|
|
24
|
-
| "sheet"
|
|
25
|
-
| "file"
|
|
26
|
-
| "wiki"
|
|
27
|
-
| "bitable"
|
|
28
|
-
| "docx"
|
|
29
|
-
| "folder"
|
|
30
|
-
| "mindnote"
|
|
31
|
-
| "minutes"
|
|
32
|
-
| "slides";
|
|
33
|
-
type MemberType =
|
|
34
|
-
| "email"
|
|
35
|
-
| "openid"
|
|
36
|
-
| "unionid"
|
|
37
|
-
| "openchat"
|
|
38
|
-
| "opendepartmentid"
|
|
39
|
-
| "userid"
|
|
40
|
-
| "groupid"
|
|
41
|
-
| "wikispaceid";
|
|
42
|
-
type PermType = "view" | "edit" | "full_access";
|
|
43
|
-
|
|
44
|
-
// ============ Actions ============
|
|
45
|
-
|
|
46
|
-
async function listMembers(client: Lark.Client, token: string, type: string) {
|
|
47
|
-
const res = await client.drive.permissionMember.list({
|
|
48
|
-
path: { token },
|
|
49
|
-
params: { type: type as ListTokenType },
|
|
50
|
-
});
|
|
51
|
-
if (res.code !== 0) {
|
|
52
|
-
throw new Error(res.msg);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
members:
|
|
57
|
-
res.data?.items?.map((m) => ({
|
|
58
|
-
member_type: m.member_type,
|
|
59
|
-
member_id: m.member_id,
|
|
60
|
-
perm: m.perm,
|
|
61
|
-
name: m.name,
|
|
62
|
-
})) ?? [],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function addMember(
|
|
67
|
-
client: Lark.Client,
|
|
68
|
-
token: string,
|
|
69
|
-
type: string,
|
|
70
|
-
memberType: string,
|
|
71
|
-
memberId: string,
|
|
72
|
-
perm: string,
|
|
73
|
-
) {
|
|
74
|
-
const res = await client.drive.permissionMember.create({
|
|
75
|
-
path: { token },
|
|
76
|
-
params: { type: type as CreateTokenType, need_notification: false },
|
|
77
|
-
data: {
|
|
78
|
-
member_type: memberType as MemberType,
|
|
79
|
-
member_id: memberId,
|
|
80
|
-
perm: perm as PermType,
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
if (res.code !== 0) {
|
|
84
|
-
throw new Error(res.msg);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
success: true,
|
|
89
|
-
member: res.data?.member,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function removeMember(
|
|
94
|
-
client: Lark.Client,
|
|
95
|
-
token: string,
|
|
96
|
-
type: string,
|
|
97
|
-
memberType: string,
|
|
98
|
-
memberId: string,
|
|
99
|
-
) {
|
|
100
|
-
const res = await client.drive.permissionMember.delete({
|
|
101
|
-
path: { token, member_id: memberId },
|
|
102
|
-
params: { type: type as CreateTokenType, member_type: memberType as MemberType },
|
|
103
|
-
});
|
|
104
|
-
if (res.code !== 0) {
|
|
105
|
-
throw new Error(res.msg);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
success: true,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ============ Tool Registration ============
|
|
114
|
-
|
|
115
|
-
export function registerFeishuPermTools(api: OpenClawPluginApi) {
|
|
116
|
-
if (!api.config) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const accounts = listEnabledFeishuAccounts(api.config);
|
|
121
|
-
if (accounts.length === 0) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
|
|
126
|
-
if (!toolsCfg.perm) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
type FeishuPermExecuteParams = FeishuPermParams & { accountId?: string };
|
|
131
|
-
|
|
132
|
-
api.registerTool(
|
|
133
|
-
(ctx) => {
|
|
134
|
-
const defaultAccountId = ctx.agentAccountId;
|
|
135
|
-
return {
|
|
136
|
-
name: "feishu_perm",
|
|
137
|
-
label: "Feishu Perm",
|
|
138
|
-
description: "Feishu permission management. Actions: list, add, remove",
|
|
139
|
-
parameters: FeishuPermSchema,
|
|
140
|
-
async execute(_toolCallId, params) {
|
|
141
|
-
const p = params as FeishuPermExecuteParams;
|
|
142
|
-
try {
|
|
143
|
-
const client = createFeishuToolClient({
|
|
144
|
-
api,
|
|
145
|
-
executeParams: p,
|
|
146
|
-
defaultAccountId,
|
|
147
|
-
});
|
|
148
|
-
switch (p.action) {
|
|
149
|
-
case "list":
|
|
150
|
-
return jsonToolResult(await listMembers(client, p.token, p.type));
|
|
151
|
-
case "add":
|
|
152
|
-
return jsonToolResult(
|
|
153
|
-
await addMember(client, p.token, p.type, p.member_type, p.member_id, p.perm),
|
|
154
|
-
);
|
|
155
|
-
case "remove":
|
|
156
|
-
return jsonToolResult(
|
|
157
|
-
await removeMember(client, p.token, p.type, p.member_type, p.member_id),
|
|
158
|
-
);
|
|
159
|
-
default:
|
|
160
|
-
return unknownToolActionResult((p as { action?: unknown }).action);
|
|
161
|
-
}
|
|
162
|
-
} catch (err) {
|
|
163
|
-
return toolExecutionErrorResult(err);
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
},
|
|
168
|
-
{ name: "feishu_perm" },
|
|
169
|
-
);
|
|
170
|
-
}
|
package/src/pins.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { ClawdbotConfig } from "../runtime-api.js";
|
|
2
|
-
import { resolveFeishuRuntimeAccount } from "./accounts.js";
|
|
3
|
-
import { createFeishuClient } from "./client.js";
|
|
4
|
-
|
|
5
|
-
type FeishuPin = {
|
|
6
|
-
messageId: string;
|
|
7
|
-
chatId?: string;
|
|
8
|
-
operatorId?: string;
|
|
9
|
-
operatorIdType?: string;
|
|
10
|
-
createTime?: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function assertFeishuPinApiSuccess(response: { code?: number; msg?: string }, action: string) {
|
|
14
|
-
if (response.code !== 0) {
|
|
15
|
-
throw new Error(`Feishu ${action} failed: ${response.msg || `code ${response.code}`}`);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function normalizePin(pin: {
|
|
20
|
-
message_id: string;
|
|
21
|
-
chat_id?: string;
|
|
22
|
-
operator_id?: string;
|
|
23
|
-
operator_id_type?: string;
|
|
24
|
-
create_time?: string;
|
|
25
|
-
}): FeishuPin {
|
|
26
|
-
return {
|
|
27
|
-
messageId: pin.message_id,
|
|
28
|
-
chatId: pin.chat_id,
|
|
29
|
-
operatorId: pin.operator_id,
|
|
30
|
-
operatorIdType: pin.operator_id_type,
|
|
31
|
-
createTime: pin.create_time,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function createPinFeishu(params: {
|
|
36
|
-
cfg: ClawdbotConfig;
|
|
37
|
-
messageId: string;
|
|
38
|
-
accountId?: string;
|
|
39
|
-
}): Promise<FeishuPin | null> {
|
|
40
|
-
const account = resolveFeishuRuntimeAccount({ cfg: params.cfg, accountId: params.accountId });
|
|
41
|
-
if (!account.configured) {
|
|
42
|
-
throw new Error(`Feishu account "${account.accountId}" not configured`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const client = createFeishuClient(account);
|
|
46
|
-
const response = await client.im.pin.create({
|
|
47
|
-
data: {
|
|
48
|
-
message_id: params.messageId,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
assertFeishuPinApiSuccess(response, "pin create");
|
|
52
|
-
return response.data?.pin ? normalizePin(response.data.pin) : null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function removePinFeishu(params: {
|
|
56
|
-
cfg: ClawdbotConfig;
|
|
57
|
-
messageId: string;
|
|
58
|
-
accountId?: string;
|
|
59
|
-
}): Promise<void> {
|
|
60
|
-
const account = resolveFeishuRuntimeAccount({ cfg: params.cfg, accountId: params.accountId });
|
|
61
|
-
if (!account.configured) {
|
|
62
|
-
throw new Error(`Feishu account "${account.accountId}" not configured`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const client = createFeishuClient(account);
|
|
66
|
-
const response = await client.im.pin.delete({
|
|
67
|
-
path: {
|
|
68
|
-
message_id: params.messageId,
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
assertFeishuPinApiSuccess(response, "pin delete");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export async function listPinsFeishu(params: {
|
|
75
|
-
cfg: ClawdbotConfig;
|
|
76
|
-
chatId: string;
|
|
77
|
-
startTime?: string;
|
|
78
|
-
endTime?: string;
|
|
79
|
-
pageSize?: number;
|
|
80
|
-
pageToken?: string;
|
|
81
|
-
accountId?: string;
|
|
82
|
-
}): Promise<{ chatId: string; pins: FeishuPin[]; hasMore: boolean; pageToken?: string }> {
|
|
83
|
-
const account = resolveFeishuRuntimeAccount({ cfg: params.cfg, accountId: params.accountId });
|
|
84
|
-
if (!account.configured) {
|
|
85
|
-
throw new Error(`Feishu account "${account.accountId}" not configured`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const client = createFeishuClient(account);
|
|
89
|
-
const response = await client.im.pin.list({
|
|
90
|
-
params: {
|
|
91
|
-
chat_id: params.chatId,
|
|
92
|
-
...(params.startTime ? { start_time: params.startTime } : {}),
|
|
93
|
-
...(params.endTime ? { end_time: params.endTime } : {}),
|
|
94
|
-
...(typeof params.pageSize === "number"
|
|
95
|
-
? { page_size: Math.max(1, Math.min(100, Math.floor(params.pageSize))) }
|
|
96
|
-
: {}),
|
|
97
|
-
...(params.pageToken ? { page_token: params.pageToken } : {}),
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
assertFeishuPinApiSuccess(response, "pin list");
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
chatId: params.chatId,
|
|
104
|
-
pins: (response.data?.items ?? []).map(normalizePin),
|
|
105
|
-
hasMore: response.data?.has_more === true,
|
|
106
|
-
pageToken: response.data?.page_token,
|
|
107
|
-
};
|
|
108
|
-
}
|
package/src/policy.test.ts
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { FeishuConfigSchema } from "./config-schema.js";
|
|
4
|
-
import {
|
|
5
|
-
hasExplicitFeishuGroupConfig,
|
|
6
|
-
isFeishuGroupAllowed,
|
|
7
|
-
resolveFeishuAllowlistMatch,
|
|
8
|
-
resolveFeishuGroupConfig,
|
|
9
|
-
resolveFeishuReplyPolicy,
|
|
10
|
-
} from "./policy.js";
|
|
11
|
-
import type { FeishuConfig } from "./types.js";
|
|
12
|
-
|
|
13
|
-
function createCfg(feishu: Record<string, unknown>): OpenClawConfig {
|
|
14
|
-
return {
|
|
15
|
-
channels: {
|
|
16
|
-
feishu,
|
|
17
|
-
},
|
|
18
|
-
} as OpenClawConfig;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function createFeishuConfig(overrides: Partial<FeishuConfig>): FeishuConfig {
|
|
22
|
-
return FeishuConfigSchema.parse(overrides);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe("resolveFeishuReplyPolicy", () => {
|
|
26
|
-
it("defaults open groups to no mention when unset", () => {
|
|
27
|
-
expect(
|
|
28
|
-
resolveFeishuReplyPolicy({
|
|
29
|
-
isDirectMessage: false,
|
|
30
|
-
cfg: createCfg({ groupPolicy: "open" }),
|
|
31
|
-
groupPolicy: "open",
|
|
32
|
-
groupId: "oc_1",
|
|
33
|
-
}),
|
|
34
|
-
).toEqual({ requireMention: false });
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("keeps explicit top-level mention gating in open groups", () => {
|
|
38
|
-
expect(
|
|
39
|
-
resolveFeishuReplyPolicy({
|
|
40
|
-
isDirectMessage: false,
|
|
41
|
-
cfg: createCfg({ groupPolicy: "open", requireMention: true }),
|
|
42
|
-
groupPolicy: "open",
|
|
43
|
-
groupId: "oc_1",
|
|
44
|
-
}),
|
|
45
|
-
).toEqual({ requireMention: true });
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("keeps explicit account mention gating in open groups", () => {
|
|
49
|
-
expect(
|
|
50
|
-
resolveFeishuReplyPolicy({
|
|
51
|
-
isDirectMessage: false,
|
|
52
|
-
cfg: createCfg({
|
|
53
|
-
groupPolicy: "allowlist",
|
|
54
|
-
requireMention: false,
|
|
55
|
-
accounts: {
|
|
56
|
-
work: {
|
|
57
|
-
groupPolicy: "open",
|
|
58
|
-
requireMention: true,
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
}),
|
|
62
|
-
accountId: "work",
|
|
63
|
-
groupPolicy: "open",
|
|
64
|
-
groupId: "oc_1",
|
|
65
|
-
}),
|
|
66
|
-
).toEqual({ requireMention: true });
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("keeps explicit per-group mention gating in open groups", () => {
|
|
70
|
-
expect(
|
|
71
|
-
resolveFeishuReplyPolicy({
|
|
72
|
-
isDirectMessage: false,
|
|
73
|
-
cfg: createCfg({
|
|
74
|
-
groupPolicy: "open",
|
|
75
|
-
groups: { oc_1: { requireMention: true } },
|
|
76
|
-
}),
|
|
77
|
-
groupPolicy: "open",
|
|
78
|
-
groupId: "oc_1",
|
|
79
|
-
}),
|
|
80
|
-
).toEqual({ requireMention: true });
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("defaults allowlist groups to require mentions", () => {
|
|
84
|
-
expect(
|
|
85
|
-
resolveFeishuReplyPolicy({
|
|
86
|
-
isDirectMessage: false,
|
|
87
|
-
cfg: createCfg({ groupPolicy: "allowlist" }),
|
|
88
|
-
groupPolicy: "allowlist",
|
|
89
|
-
groupId: "oc_1",
|
|
90
|
-
}),
|
|
91
|
-
).toEqual({ requireMention: true });
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe("resolveFeishuGroupConfig", () => {
|
|
96
|
-
it("falls back to wildcard group config when direct match is missing", () => {
|
|
97
|
-
const cfg = createFeishuConfig({
|
|
98
|
-
groups: {
|
|
99
|
-
"*": { requireMention: false },
|
|
100
|
-
"oc-explicit": { requireMention: true },
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const resolved = resolveFeishuGroupConfig({
|
|
105
|
-
cfg,
|
|
106
|
-
groupId: "oc-missing",
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
expect(resolved).toEqual({ requireMention: false });
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("prefers exact group config over wildcard", () => {
|
|
113
|
-
const cfg = createFeishuConfig({
|
|
114
|
-
groups: {
|
|
115
|
-
"*": { requireMention: false },
|
|
116
|
-
"oc-explicit": { requireMention: true },
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const resolved = resolveFeishuGroupConfig({
|
|
121
|
-
cfg,
|
|
122
|
-
groupId: "oc-explicit",
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
expect(resolved).toEqual({ requireMention: true });
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("keeps case-insensitive matching for explicit group ids", () => {
|
|
129
|
-
const cfg = createFeishuConfig({
|
|
130
|
-
groups: {
|
|
131
|
-
"*": { requireMention: false },
|
|
132
|
-
OC_UPPER: { requireMention: true },
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const resolved = resolveFeishuGroupConfig({
|
|
137
|
-
cfg,
|
|
138
|
-
groupId: "oc_upper",
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
expect(resolved).toEqual({ requireMention: true });
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe("hasExplicitFeishuGroupConfig", () => {
|
|
146
|
-
it("matches direct and case-insensitive group ids", () => {
|
|
147
|
-
const cfg = createFeishuConfig({
|
|
148
|
-
groups: {
|
|
149
|
-
OC_UPPER: { requireMention: true },
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
expect(hasExplicitFeishuGroupConfig({ cfg, groupId: "OC_UPPER" })).toBe(true);
|
|
154
|
-
expect(hasExplicitFeishuGroupConfig({ cfg, groupId: "oc_upper" })).toBe(true);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("does not treat wildcard group defaults as explicit admission", () => {
|
|
158
|
-
const cfg = createFeishuConfig({
|
|
159
|
-
groups: {
|
|
160
|
-
"*": { requireMention: false },
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
expect(hasExplicitFeishuGroupConfig({ cfg, groupId: "oc_any" })).toBe(false);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe("resolveFeishuAllowlistMatch", () => {
|
|
169
|
-
it("allows wildcard", () => {
|
|
170
|
-
expect(
|
|
171
|
-
resolveFeishuAllowlistMatch({
|
|
172
|
-
allowFrom: ["*"],
|
|
173
|
-
senderId: "ou-attacker",
|
|
174
|
-
}),
|
|
175
|
-
).toEqual({ allowed: true, matchKey: "*", matchSource: "wildcard" });
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("allows provider-prefixed wildcard entries", () => {
|
|
179
|
-
expect(
|
|
180
|
-
resolveFeishuAllowlistMatch({
|
|
181
|
-
allowFrom: ["feishu:*", "lark:*"],
|
|
182
|
-
senderId: "ou_anyone",
|
|
183
|
-
}),
|
|
184
|
-
).toEqual({ allowed: true, matchKey: "*", matchSource: "wildcard" });
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("treats typed wildcard aliases as bare wildcards", () => {
|
|
188
|
-
for (const wildcard of [
|
|
189
|
-
"chat:*",
|
|
190
|
-
"group:*",
|
|
191
|
-
"channel:*",
|
|
192
|
-
"user:*",
|
|
193
|
-
"dm:*",
|
|
194
|
-
"open_id:*",
|
|
195
|
-
"feishu:user:*",
|
|
196
|
-
]) {
|
|
197
|
-
expect(
|
|
198
|
-
resolveFeishuAllowlistMatch({
|
|
199
|
-
allowFrom: [wildcard],
|
|
200
|
-
senderId: "ou_anyone",
|
|
201
|
-
}),
|
|
202
|
-
).toEqual({ allowed: true, matchKey: "*", matchSource: "wildcard" });
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("matches normalized ID entries", () => {
|
|
207
|
-
expect(
|
|
208
|
-
resolveFeishuAllowlistMatch({
|
|
209
|
-
allowFrom: ["feishu:user:ou_ALLOWED"],
|
|
210
|
-
senderId: "ou_ALLOWED",
|
|
211
|
-
}),
|
|
212
|
-
).toEqual({ allowed: true, matchKey: "user:ou_ALLOWED", matchSource: "id" });
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("accepts repeated provider prefixes for legacy allowlist entries", () => {
|
|
216
|
-
expect(
|
|
217
|
-
resolveFeishuAllowlistMatch({
|
|
218
|
-
allowFrom: ["feishu:feishu:user:ou_ALLOWED"],
|
|
219
|
-
senderId: "ou_ALLOWED",
|
|
220
|
-
}),
|
|
221
|
-
).toEqual({ allowed: true, matchKey: "user:ou_ALLOWED", matchSource: "id" });
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("does not fold opaque IDs to lowercase", () => {
|
|
225
|
-
expect(
|
|
226
|
-
resolveFeishuAllowlistMatch({
|
|
227
|
-
allowFrom: ["user:OU_ALLOWED"],
|
|
228
|
-
senderId: "ou_ALLOWED",
|
|
229
|
-
}),
|
|
230
|
-
).toEqual({ allowed: false });
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("keeps user and chat allowlist namespaces distinct", () => {
|
|
234
|
-
expect(
|
|
235
|
-
resolveFeishuAllowlistMatch({
|
|
236
|
-
allowFrom: ["user:oc_group_123"],
|
|
237
|
-
senderId: "oc_group_123",
|
|
238
|
-
}),
|
|
239
|
-
).toEqual({ allowed: false });
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("supports user_id as an additional immutable sender candidate", () => {
|
|
243
|
-
expect(
|
|
244
|
-
resolveFeishuAllowlistMatch({
|
|
245
|
-
allowFrom: ["on_user_123"],
|
|
246
|
-
senderId: "ou_other",
|
|
247
|
-
senderIds: ["on_user_123"],
|
|
248
|
-
}),
|
|
249
|
-
).toEqual({ allowed: true, matchKey: "user:on_user_123", matchSource: "id" });
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("auto-detects bare open_id entries as user allowlist matches", () => {
|
|
253
|
-
expect(
|
|
254
|
-
resolveFeishuAllowlistMatch({
|
|
255
|
-
allowFrom: ["ou_BARE"],
|
|
256
|
-
senderId: "ou_BARE",
|
|
257
|
-
}),
|
|
258
|
-
).toEqual({ allowed: true, matchKey: "user:ou_BARE", matchSource: "id" });
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("auto-detects bare chat_id entries as chat allowlist matches", () => {
|
|
262
|
-
expect(
|
|
263
|
-
resolveFeishuAllowlistMatch({
|
|
264
|
-
allowFrom: ["oc_group_123"],
|
|
265
|
-
senderId: "oc_group_123",
|
|
266
|
-
}),
|
|
267
|
-
).toEqual({ allowed: true, matchKey: "chat:oc_group_123", matchSource: "id" });
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it("does not authorize based on display-name collision", () => {
|
|
271
|
-
const victimOpenId = "ou_4f4ec5aa111122223333444455556666";
|
|
272
|
-
|
|
273
|
-
expect(
|
|
274
|
-
resolveFeishuAllowlistMatch({
|
|
275
|
-
allowFrom: [victimOpenId],
|
|
276
|
-
senderId: "ou_attacker_real_open_id",
|
|
277
|
-
senderIds: ["on_attacker_user_id"],
|
|
278
|
-
senderName: victimOpenId,
|
|
279
|
-
}),
|
|
280
|
-
).toEqual({ allowed: false });
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
describe("isFeishuGroupAllowed", () => {
|
|
285
|
-
it("matches group IDs with chat: prefix", () => {
|
|
286
|
-
expect(
|
|
287
|
-
isFeishuGroupAllowed({
|
|
288
|
-
groupPolicy: "allowlist",
|
|
289
|
-
allowFrom: ["chat:oc_group_123"],
|
|
290
|
-
senderId: "oc_group_123",
|
|
291
|
-
}),
|
|
292
|
-
).toBe(true);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it("allows group when groupPolicy is 'open'", () => {
|
|
296
|
-
expect(
|
|
297
|
-
isFeishuGroupAllowed({
|
|
298
|
-
groupPolicy: "open",
|
|
299
|
-
allowFrom: [],
|
|
300
|
-
senderId: "oc_group_999",
|
|
301
|
-
}),
|
|
302
|
-
).toBe(true);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it("treats 'allowall' as equivalent to 'open'", () => {
|
|
306
|
-
expect(
|
|
307
|
-
isFeishuGroupAllowed({
|
|
308
|
-
groupPolicy: "allowall",
|
|
309
|
-
allowFrom: [],
|
|
310
|
-
senderId: "oc_group_999",
|
|
311
|
-
}),
|
|
312
|
-
).toBe(true);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it("rejects group when groupPolicy is 'disabled'", () => {
|
|
316
|
-
expect(
|
|
317
|
-
isFeishuGroupAllowed({
|
|
318
|
-
groupPolicy: "disabled",
|
|
319
|
-
allowFrom: ["oc_group_999"],
|
|
320
|
-
senderId: "oc_group_999",
|
|
321
|
-
}),
|
|
322
|
-
).toBe(false);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it("rejects group when groupPolicy is 'allowlist' and allowFrom is empty", () => {
|
|
326
|
-
expect(
|
|
327
|
-
isFeishuGroupAllowed({
|
|
328
|
-
groupPolicy: "allowlist",
|
|
329
|
-
allowFrom: [],
|
|
330
|
-
senderId: "oc_group_999",
|
|
331
|
-
}),
|
|
332
|
-
).toBe(false);
|
|
333
|
-
});
|
|
334
|
-
});
|