@openclaw/feishu 2026.9.1-beta.1 → 2026.9.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-RwSyseKr.js → accounts-cCMNFBKg.js} +1 -1
- package/dist/api.js +222 -284
- package/dist/{app-registration-BpfUzS9m.js → app-registration-BI99e_JI.js} +12 -9
- package/dist/{channel-Dpgf8wLD.js → channel-VytDDz_B.js} +244 -582
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-BIEERwaT.js → channel.runtime-CAD74kBO.js} +15 -19
- package/dist/{chat-C1nuW5UY.js → chat-DDhIcPQy.js} +13 -13
- package/dist/{client-DalLmpMf.js → client-DMbIL3UH.js} +3 -6
- package/dist/contract-api.js +2 -2
- package/dist/doctor-contract-api.js +1 -1
- package/dist/doctor-contract-w6FpMypv.js +465 -0
- package/dist/index.js +11 -42
- package/dist/{json-response-CheljwGr.js → json-response-DXajw_FU.js} +1 -1
- package/dist/{monitor.startup-D-gL82OO.js → monitor-rPJ_Mz4M.js} +62 -2
- package/dist/{monitor.account-DhhotI_Q.js → monitor.account-v0XIDyfs.js} +270 -299
- package/dist/{probe-CfhhR_0a.js → probe-D_SatdZl.js} +3 -3
- package/dist/{presentation-card-C8Z7Tgru.js → reply-delivery-result-CYaKkbUN.js} +334 -240
- package/dist/{security-audit-D6Fz2h6p.js → security-audit-DKfR4Cv3.js} +1 -1
- package/dist/{security-audit-shared-BgpY7AiJ.js → security-audit-shared-CK5rVR-1.js} +2 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/{subagent-hooks-KUi-7vUy.js → subagent-hooks-Buadpie_.js} +3 -6
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{targets-BUjQ1TcA.js → targets-BTQCYRZQ.js} +4 -4
- package/dist/{thread-bindings-CptM_YU6.js → thread-bindings-BiL1wGqK.js} +20 -9
- package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/@larksuiteoapi/node-sdk/node_modules/ws/package.json +1 -1
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
- package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
- package/node_modules/typebox/build/system/memory/create.mjs +3 -4
- package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
- package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
- package/node_modules/typebox/build/system/memory/update.mjs +3 -2
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +45 -238
- package/package.json +6 -5
- package/dist/doctor-contract-bEQXIXyP.js +0 -158
- package/dist/monitor-BRg3sHH7.js +0 -62
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as feishuPlugin } from "./channel-
|
|
1
|
+
import { t as feishuPlugin } from "./channel-VytDDz_B.js";
|
|
2
2
|
export { feishuPlugin };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-
|
|
2
|
-
import { c as listFeishuDirectoryPeers, o as feishuOutbound, s as listFeishuDirectoryGroups } from "./channel-
|
|
3
|
-
import {
|
|
4
|
-
import { r as createFeishuClient } from "./client-
|
|
5
|
-
import { a as getFeishuMemberInfo, i as getChatMembers, n as buildFeishuDirectChatMembers, r as getChatInfo, t as assertFeishuChatMember } from "./chat-
|
|
6
|
-
import { t as probeFeishu } from "./probe-
|
|
1
|
+
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-cCMNFBKg.js";
|
|
2
|
+
import { c as listFeishuDirectoryPeers, o as feishuOutbound, s as listFeishuDirectoryGroups } from "./channel-VytDDz_B.js";
|
|
3
|
+
import { C as editMessageFeishu, D as sendMessageFeishu, E as sendCardFeishu, I as assertFeishuApiSuccess, b as sendStickerFeishu, w as getMessageFeishu } from "./reply-delivery-result-CYaKkbUN.js";
|
|
4
|
+
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
5
|
+
import { a as getFeishuMemberInfo, i as getChatMembers, n as buildFeishuDirectChatMembers, r as getChatInfo, t as assertFeishuChatMember } from "./chat-DDhIcPQy.js";
|
|
6
|
+
import { t as probeFeishu } from "./probe-D_SatdZl.js";
|
|
7
7
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
8
|
//#region extensions/feishu/src/directory.ts
|
|
9
9
|
const MAX_FEISHU_DIRECTORY_PAGES = 100;
|
|
@@ -84,9 +84,6 @@ async function listFeishuDirectoryGroupsLive(params) {
|
|
|
84
84
|
}
|
|
85
85
|
//#endregion
|
|
86
86
|
//#region extensions/feishu/src/pins.ts
|
|
87
|
-
function assertFeishuPinApiSuccess(response, action) {
|
|
88
|
-
if (response.code !== 0) throw new Error(`Feishu ${action} failed: ${response.msg || `code ${response.code}`}`);
|
|
89
|
-
}
|
|
90
87
|
function normalizePin(pin) {
|
|
91
88
|
return {
|
|
92
89
|
messageId: pin.message_id,
|
|
@@ -103,7 +100,7 @@ async function createPinFeishu(params) {
|
|
|
103
100
|
});
|
|
104
101
|
if (!account.configured) throw new Error(`Feishu account "${account.accountId}" not configured`);
|
|
105
102
|
const response = await createFeishuClient(account).im.pin.create({ data: { message_id: params.messageId } });
|
|
106
|
-
|
|
103
|
+
assertFeishuApiSuccess(response, "Feishu pin create failed");
|
|
107
104
|
return response.data?.pin ? normalizePin(response.data.pin) : null;
|
|
108
105
|
}
|
|
109
106
|
async function removePinFeishu(params) {
|
|
@@ -112,7 +109,8 @@ async function removePinFeishu(params) {
|
|
|
112
109
|
accountId: params.accountId
|
|
113
110
|
});
|
|
114
111
|
if (!account.configured) throw new Error(`Feishu account "${account.accountId}" not configured`);
|
|
115
|
-
|
|
112
|
+
const response = await createFeishuClient(account).im.pin.delete({ path: { message_id: params.messageId } });
|
|
113
|
+
assertFeishuApiSuccess(response, "Feishu pin delete failed");
|
|
116
114
|
}
|
|
117
115
|
async function listPinsFeishu(params) {
|
|
118
116
|
const account = resolveFeishuRuntimeAccount({
|
|
@@ -127,7 +125,7 @@ async function listPinsFeishu(params) {
|
|
|
127
125
|
...typeof params.pageSize === "number" ? { page_size: Math.max(1, Math.min(100, Math.floor(params.pageSize))) } : {},
|
|
128
126
|
...params.pageToken ? { page_token: params.pageToken } : {}
|
|
129
127
|
} });
|
|
130
|
-
|
|
128
|
+
assertFeishuApiSuccess(response, "Feishu pin list failed");
|
|
131
129
|
return {
|
|
132
130
|
chatId: params.chatId,
|
|
133
131
|
pins: (response.data?.items ?? []).map(normalizePin),
|
|
@@ -142,9 +140,6 @@ function resolveConfiguredFeishuClient(params) {
|
|
|
142
140
|
if (!account.configured) throw new Error(`Feishu account "${account.accountId}" not configured`);
|
|
143
141
|
return createFeishuClient(account);
|
|
144
142
|
}
|
|
145
|
-
function assertFeishuReactionApiSuccess(response, action) {
|
|
146
|
-
if (response.code !== 0) throw new Error(`Feishu ${action} failed: ${response.msg || `code ${response.code}`}`);
|
|
147
|
-
}
|
|
148
143
|
/**
|
|
149
144
|
* Add a reaction (emoji) to a message.
|
|
150
145
|
* @param emojiType - Feishu emoji type, e.g., "SMILE", "THUMBSUP", "HEART"
|
|
@@ -159,7 +154,7 @@ async function addReactionFeishu(params) {
|
|
|
159
154
|
path: { message_id: messageId },
|
|
160
155
|
data: { reaction_type: { emoji_type: emojiType } }
|
|
161
156
|
});
|
|
162
|
-
|
|
157
|
+
assertFeishuApiSuccess(response, "Feishu add reaction failed");
|
|
163
158
|
const reactionId = response.data?.reaction_id;
|
|
164
159
|
if (!reactionId) throw new Error("Feishu add reaction failed: no reaction_id returned");
|
|
165
160
|
return { reactionId };
|
|
@@ -169,13 +164,14 @@ async function addReactionFeishu(params) {
|
|
|
169
164
|
*/
|
|
170
165
|
async function removeReactionFeishu(params) {
|
|
171
166
|
const { cfg, messageId, reactionId, accountId } = params;
|
|
172
|
-
|
|
167
|
+
const response = await resolveConfiguredFeishuClient({
|
|
173
168
|
cfg,
|
|
174
169
|
accountId
|
|
175
170
|
}).im.messageReaction.delete({ path: {
|
|
176
171
|
message_id: messageId,
|
|
177
172
|
reaction_id: reactionId
|
|
178
|
-
} })
|
|
173
|
+
} });
|
|
174
|
+
assertFeishuApiSuccess(response, "Feishu remove reaction failed");
|
|
179
175
|
}
|
|
180
176
|
/**
|
|
181
177
|
* List all reactions for a message.
|
|
@@ -197,7 +193,7 @@ async function listReactionsFeishu(params) {
|
|
|
197
193
|
...pageToken ? { page_token: pageToken } : {}
|
|
198
194
|
} : void 0
|
|
199
195
|
});
|
|
200
|
-
|
|
196
|
+
assertFeishuApiSuccess(response, "Feishu list reactions failed");
|
|
201
197
|
for (const item of response.data?.items ?? []) reactions.push({
|
|
202
198
|
reactionId: item.reaction_id ?? "",
|
|
203
199
|
emojiType: item.reaction_type?.emoji_type ?? "",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { r as createFeishuClient } from "./client-DalLmpMf.js";
|
|
1
|
+
import { Et as resolveFeishuChatType, H as feishuExternalToolResult, K as resolveAnyEnabledFeishuToolsConfig, Q as formatFeishuApiError, dt as authorizeFeishuChatMemberRead, gt as resolveFeishuChatReadPreliminaryAuthorization, q as resolveFeishuToolAccount, ut as assertFeishuChatReadAllowed } from "./reply-delivery-result-CYaKkbUN.js";
|
|
2
|
+
import { r as createFeishuClient } from "./client-DMbIL3UH.js";
|
|
4
3
|
import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
|
|
5
4
|
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
|
6
5
|
import { Type } from "typebox";
|
|
@@ -192,9 +191,7 @@ async function getFeishuMemberInfo(client, memberId, memberIdType = "open_id") {
|
|
|
192
191
|
function registerFeishuChatTools(api) {
|
|
193
192
|
if (!api.config) return;
|
|
194
193
|
const cfg = api.config;
|
|
195
|
-
|
|
196
|
-
if (accounts.length === 0) return;
|
|
197
|
-
if (!resolveAnyEnabledFeishuToolsConfig(accounts).chat) return;
|
|
194
|
+
if (!resolveAnyEnabledFeishuToolsConfig(cfg).chat) return;
|
|
198
195
|
api.registerTool((toolContext) => ({
|
|
199
196
|
name: "feishu_chat",
|
|
200
197
|
resultContentSource: "network",
|
|
@@ -238,13 +235,16 @@ function registerFeishuChatTools(api) {
|
|
|
238
235
|
return feishuExternalToolResult(await getChatMembers(client, p.chat_id, readChatPageSize(rawParams), p.page_token, p.member_id_type));
|
|
239
236
|
case "info":
|
|
240
237
|
if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action info" });
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
238
|
+
{
|
|
239
|
+
const chat = await getAuthorizedFeishuChatInfo({
|
|
240
|
+
client,
|
|
241
|
+
cfg,
|
|
242
|
+
account,
|
|
243
|
+
chatId: p.chat_id,
|
|
244
|
+
ctx: toolContext
|
|
245
|
+
});
|
|
246
|
+
return feishuExternalToolResult(chat);
|
|
247
|
+
}
|
|
248
248
|
case "member_info":
|
|
249
249
|
if (!p.member_id) return feishuExternalToolResult({ error: "member_id is required for action member_info" });
|
|
250
250
|
if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action member_info" });
|
|
@@ -2,6 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
4
4
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
5
|
+
import { bufferToBlobPart } from "openclaw/plugin-sdk/blob-runtime";
|
|
5
6
|
import { isRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
6
7
|
import { readPluginPackageVersion, resolveAmbientNodeProxyAgent } from "openclaw/plugin-sdk/extension-shared";
|
|
7
8
|
//#region extensions/feishu/src/client-timeout.ts
|
|
@@ -32,7 +33,8 @@ var client_exports = /* @__PURE__ */ __exportAll({
|
|
|
32
33
|
createFeishuWSClient: () => createFeishuWSClient,
|
|
33
34
|
getFeishuUserAgent: () => getFeishuUserAgent
|
|
34
35
|
});
|
|
35
|
-
const
|
|
36
|
+
const require = createRequire(import.meta.url);
|
|
37
|
+
const FEISHU_USER_AGENT = `openclaw-feishu-builtin/${readPluginPackageVersion({ require })}/${process.platform}`;
|
|
36
38
|
const FEISHU_SDK_ORIGIN = "https://open.feishu.cn";
|
|
37
39
|
const FEISHU_WS_CONFIG = { pingTimeout: 3 };
|
|
38
40
|
/** User-Agent header value for all Feishu API requests. */
|
|
@@ -99,11 +101,6 @@ function stringifyMultipartFieldValue(value) {
|
|
|
99
101
|
default: return;
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
|
-
function bufferToBlobPart(value) {
|
|
103
|
-
const bytes = new Uint8Array(value.byteLength);
|
|
104
|
-
bytes.set(value);
|
|
105
|
-
return bytes;
|
|
106
|
-
}
|
|
107
104
|
function normalizeMultipartUploadData(opts) {
|
|
108
105
|
if (!isMultipartFormRequest(opts) || !isRecord(opts.data) || !isFeishuMessageMediaUploadRequest(opts, opts.data)) return opts;
|
|
109
106
|
const form = new FormData();
|
package/dist/contract-api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as parseFeishuTargetId, i as parseFeishuDirectConversationId, r as parseFeishuConversationId, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
|
|
2
|
-
import { t as messageActionTargetAliases } from "./security-audit-
|
|
3
|
-
import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-
|
|
2
|
+
import { t as messageActionTargetAliases } from "./security-audit-DKfR4Cv3.js";
|
|
3
|
+
import { t as collectFeishuSecurityAuditFindings } from "./security-audit-shared-CK5rVR-1.js";
|
|
4
4
|
export { buildFeishuConversationId, collectFeishuSecurityAuditFindings, messageActionTargetAliases, parseFeishuConversationId, parseFeishuDirectConversationId, parseFeishuTargetId };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-w6FpMypv.js";
|
|
2
2
|
export { legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
2
|
+
import { ContextVisibilityModeSchema, DmPolicySchema, GroupPolicySchema, ReplyToModeSchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$1 } from "openclaw/plugin-sdk/secret-input";
|
|
5
|
+
import { asObjectRecord, defineChannelAliasMigration, defineKeyMoveMigration, defineStrayPluginEntryConfigMigration, hasLegacyAccountStreamingAliases, normalizeChannelConfigEntries } from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
6
|
+
//#region extensions/feishu/src/external-keys.ts
|
|
7
|
+
const FEISHU_EXTERNAL_KEY_PATTERN = /^(?!\s)(?![\s\S]*\s$)(?![\s\S]*\.\.)[^\p{Cc}\p{Cs}/\\]{1,512}$/u;
|
|
8
|
+
function normalizeFeishuExternalKey(value) {
|
|
9
|
+
if (typeof value !== "string") return;
|
|
10
|
+
const normalized = value.trim();
|
|
11
|
+
return FEISHU_EXTERNAL_KEY_PATTERN.test(normalized) ? normalized : void 0;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region extensions/feishu/src/webhook-path.ts
|
|
15
|
+
const DEFAULT_FEISHU_WEBHOOK_PATH = "/feishu/events";
|
|
16
|
+
/** Normalize trusted configuration only; incoming request targets must remain unmodified. */
|
|
17
|
+
function normalizeFeishuWebhookPath(value) {
|
|
18
|
+
const configured = value?.trim();
|
|
19
|
+
if (!configured) return DEFAULT_FEISHU_WEBHOOK_PATH;
|
|
20
|
+
try {
|
|
21
|
+
const parsed = new URL(configured, "http://localhost");
|
|
22
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
23
|
+
const emptyQuery = !parsed.search && parsed.href.endsWith("?") && !configured.includes("#") ? "?" : "";
|
|
24
|
+
return `${parsed.pathname}${parsed.search}${emptyQuery}`;
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region extensions/feishu/src/config-schema.ts
|
|
31
|
+
const ChannelActionsSchema = z.object({
|
|
32
|
+
reactions: z.boolean().optional(),
|
|
33
|
+
sticker: z.boolean().optional()
|
|
34
|
+
}).strict().optional();
|
|
35
|
+
const MAX_STICKER_SETS = 32;
|
|
36
|
+
const MAX_STICKERS_PER_SET = 256;
|
|
37
|
+
function canonicalTextPattern(maxLength) {
|
|
38
|
+
return new RegExp(`^(?!\\s)(?![\\s\\S]*\\s$)[^\\p{Cs}]{1,${maxLength}}$`, "u");
|
|
39
|
+
}
|
|
40
|
+
const FeishuStickerSetSchema = z.record(z.string().regex(FEISHU_EXTERNAL_KEY_PATTERN), z.array(z.string().regex(canonicalTextPattern(64))).min(1).max(8)).refine((set) => Object.keys(set).length <= MAX_STICKERS_PER_SET, { message: `At most ${MAX_STICKERS_PER_SET} stickers per bot set are allowed` }).meta({ maxProperties: MAX_STICKERS_PER_SET });
|
|
41
|
+
const FeishuStickerSetsSchema = z.record(z.string().regex(canonicalTextPattern(128)), FeishuStickerSetSchema).refine((sets) => Object.keys(sets).length <= MAX_STICKER_SETS, { message: `At most ${MAX_STICKER_SETS} bot sticker sets are allowed` }).meta({ maxProperties: MAX_STICKER_SETS });
|
|
42
|
+
const FeishuGroupPolicySchema = z.union([GroupPolicySchema, z.literal("allowall").transform(() => "open")]);
|
|
43
|
+
const FeishuDomainSchema = z.union([z.enum(["feishu", "lark"]), z.string().regex(/^[Hh][Tt][Tt][Pp][Ss]:\/\//).url()]);
|
|
44
|
+
const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]);
|
|
45
|
+
const FeishuWebhookPathSchema = z.string().refine((value) => normalizeFeishuWebhookPath(value) === value, { message: "webhookPath must be a canonical HTTP request path; run \"openclaw doctor --fix\" to repair it" });
|
|
46
|
+
const TtsOverrideSchema = z.object({
|
|
47
|
+
auto: z.enum([
|
|
48
|
+
"off",
|
|
49
|
+
"always",
|
|
50
|
+
"inbound",
|
|
51
|
+
"tagged"
|
|
52
|
+
]).optional(),
|
|
53
|
+
enabled: z.boolean().optional(),
|
|
54
|
+
mode: z.enum(["final", "all"]).optional(),
|
|
55
|
+
provider: z.string().optional(),
|
|
56
|
+
persona: z.string().optional(),
|
|
57
|
+
personas: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
58
|
+
summaryModel: z.string().optional(),
|
|
59
|
+
modelOverrides: z.record(z.string(), z.unknown()).optional(),
|
|
60
|
+
providers: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
61
|
+
prefsPath: z.string().optional(),
|
|
62
|
+
maxTextLength: z.number().int().min(1).optional(),
|
|
63
|
+
timeoutMs: z.number().int().min(1e3).max(12e4).optional()
|
|
64
|
+
}).strict().optional();
|
|
65
|
+
const ToolPolicySchema = z.object({
|
|
66
|
+
allow: z.array(z.string()).optional(),
|
|
67
|
+
deny: z.array(z.string()).optional()
|
|
68
|
+
}).strict().optional();
|
|
69
|
+
const DmConfigSchema = z.object({
|
|
70
|
+
enabled: z.boolean().optional(),
|
|
71
|
+
systemPrompt: z.string().optional()
|
|
72
|
+
}).strict().optional();
|
|
73
|
+
const MarkdownConfigSchema = z.object({
|
|
74
|
+
mode: z.enum([
|
|
75
|
+
"native",
|
|
76
|
+
"escape",
|
|
77
|
+
"strip"
|
|
78
|
+
]).optional(),
|
|
79
|
+
tableMode: z.enum([
|
|
80
|
+
"native",
|
|
81
|
+
"ascii",
|
|
82
|
+
"simple"
|
|
83
|
+
]).optional()
|
|
84
|
+
}).strict().optional();
|
|
85
|
+
const RenderModeSchema = z.enum([
|
|
86
|
+
"auto",
|
|
87
|
+
"raw",
|
|
88
|
+
"card"
|
|
89
|
+
]).optional();
|
|
90
|
+
const BlockStreamingCoalesceSchema = z.object({
|
|
91
|
+
minChars: z.number().int().positive().optional(),
|
|
92
|
+
maxChars: z.number().int().positive().optional(),
|
|
93
|
+
idleMs: z.number().int().nonnegative().optional()
|
|
94
|
+
}).strict().optional();
|
|
95
|
+
const FeishuStreamingSchema = z.object({
|
|
96
|
+
mode: z.enum(["off", "partial"]).optional(),
|
|
97
|
+
chunkMode: z.enum(["length", "newline"]).optional(),
|
|
98
|
+
block: z.object({
|
|
99
|
+
enabled: z.boolean().optional(),
|
|
100
|
+
coalesce: BlockStreamingCoalesceSchema
|
|
101
|
+
}).strict().optional()
|
|
102
|
+
}).strict().optional();
|
|
103
|
+
const ChannelHeartbeatVisibilitySchema = z.object({
|
|
104
|
+
visibility: z.enum(["visible", "hidden"]).optional(),
|
|
105
|
+
intervalMs: z.number().int().positive().optional()
|
|
106
|
+
}).strict().optional();
|
|
107
|
+
/**
|
|
108
|
+
* Dynamic agent creation configuration.
|
|
109
|
+
* When enabled, a new agent is created for each unique DM user.
|
|
110
|
+
*/
|
|
111
|
+
const DynamicAgentCreationSchema = z.object({
|
|
112
|
+
enabled: z.boolean().optional(),
|
|
113
|
+
workspaceTemplate: z.string().optional(),
|
|
114
|
+
agentDirTemplate: z.string().optional(),
|
|
115
|
+
maxAgents: z.number().int().positive().optional()
|
|
116
|
+
}).strict().optional();
|
|
117
|
+
/**
|
|
118
|
+
* Feishu tools configuration.
|
|
119
|
+
* Controls which tool categories are enabled.
|
|
120
|
+
*
|
|
121
|
+
* Dependencies:
|
|
122
|
+
* - wiki requires doc (wiki content is edited via doc tools)
|
|
123
|
+
* - perm can work independently but is typically used with drive
|
|
124
|
+
*/
|
|
125
|
+
const FeishuToolsConfigSchema = z.object({
|
|
126
|
+
doc: z.boolean().optional(),
|
|
127
|
+
chat: z.boolean().optional(),
|
|
128
|
+
wiki: z.boolean().optional(),
|
|
129
|
+
drive: z.boolean().optional(),
|
|
130
|
+
perm: z.boolean().optional(),
|
|
131
|
+
scopes: z.boolean().optional(),
|
|
132
|
+
bitable: z.boolean().optional()
|
|
133
|
+
}).strict().optional();
|
|
134
|
+
/**
|
|
135
|
+
* Group session scope for routing Feishu group messages.
|
|
136
|
+
* - "group" (default): one session per group chat
|
|
137
|
+
* - "group_sender": one session per (group + sender)
|
|
138
|
+
* - "group_topic": one session per group topic thread (falls back to group if no topic)
|
|
139
|
+
* - "group_topic_sender": one session per (group + topic thread + sender),
|
|
140
|
+
* falls back to (group + sender) if no topic
|
|
141
|
+
*/
|
|
142
|
+
const GroupSessionScopeSchema = z.enum([
|
|
143
|
+
"group",
|
|
144
|
+
"group_sender",
|
|
145
|
+
"group_topic",
|
|
146
|
+
"group_topic_sender"
|
|
147
|
+
]).optional();
|
|
148
|
+
/**
|
|
149
|
+
* @deprecated Use groupSessionScope instead.
|
|
150
|
+
*
|
|
151
|
+
* Topic session isolation mode for group chats.
|
|
152
|
+
* - "disabled" (default): All messages in a group share one session
|
|
153
|
+
* - "enabled": Messages in different topics get separate sessions
|
|
154
|
+
*
|
|
155
|
+
* Topic routing uses Feishu topic-group `thread_id` when the event identifies a
|
|
156
|
+
* native topic group, and keeps `root_id` precedence for normal groups so
|
|
157
|
+
* reply-created threads stay on the initiating message session.
|
|
158
|
+
*/
|
|
159
|
+
const TopicSessionModeSchema = z.enum(["disabled", "enabled"]).optional();
|
|
160
|
+
const ReactionNotificationModeSchema = z.enum([
|
|
161
|
+
"off",
|
|
162
|
+
"own",
|
|
163
|
+
"all"
|
|
164
|
+
]).optional();
|
|
165
|
+
/**
|
|
166
|
+
* Reply-in-thread mode for group chats.
|
|
167
|
+
* - "disabled" (default): Bot replies are normal inline replies
|
|
168
|
+
* - "enabled": Bot replies create or continue a Feishu topic thread
|
|
169
|
+
*
|
|
170
|
+
* When enabled, the Feishu reply API is called with `reply_in_thread: true`,
|
|
171
|
+
* causing the reply to appear as a topic (话题) under the original message.
|
|
172
|
+
*/
|
|
173
|
+
const ReplyInThreadSchema = z.enum(["disabled", "enabled"]).optional();
|
|
174
|
+
const FeishuGroupSchema = buildGroupEntrySchema({
|
|
175
|
+
tools: ToolPolicySchema,
|
|
176
|
+
groupSessionScope: GroupSessionScopeSchema,
|
|
177
|
+
topicSessionMode: TopicSessionModeSchema,
|
|
178
|
+
replyInThread: ReplyInThreadSchema
|
|
179
|
+
}).omit({ toolsBySender: true });
|
|
180
|
+
const FeishuSharedConfigShape = {
|
|
181
|
+
webhookHost: z.string().optional(),
|
|
182
|
+
webhookPort: z.number().int().positive().optional(),
|
|
183
|
+
capabilities: z.array(z.string()).optional(),
|
|
184
|
+
markdown: MarkdownConfigSchema,
|
|
185
|
+
configWrites: z.boolean().optional(),
|
|
186
|
+
contextVisibility: ContextVisibilityModeSchema.optional(),
|
|
187
|
+
replyToMode: ReplyToModeSchema.optional(),
|
|
188
|
+
responsePrefix: z.string().optional(),
|
|
189
|
+
dmPolicy: DmPolicySchema.optional(),
|
|
190
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
191
|
+
groupPolicy: FeishuGroupPolicySchema.optional(),
|
|
192
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
193
|
+
groupSenderAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
194
|
+
requireMention: z.boolean().optional(),
|
|
195
|
+
groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
|
|
196
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
197
|
+
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
198
|
+
dms: z.record(z.string(), DmConfigSchema).optional(),
|
|
199
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
200
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
201
|
+
httpTimeoutMs: z.number().int().positive().max(3e5).optional(),
|
|
202
|
+
heartbeatVisibility: ChannelHeartbeatVisibilitySchema,
|
|
203
|
+
renderMode: RenderModeSchema,
|
|
204
|
+
streaming: FeishuStreamingSchema,
|
|
205
|
+
tools: FeishuToolsConfigSchema,
|
|
206
|
+
actions: ChannelActionsSchema,
|
|
207
|
+
replyInThread: ReplyInThreadSchema,
|
|
208
|
+
reactionNotifications: ReactionNotificationModeSchema,
|
|
209
|
+
typingIndicator: z.boolean().optional(),
|
|
210
|
+
resolveSenderNames: z.boolean().optional(),
|
|
211
|
+
allowBots: z.boolean().optional(),
|
|
212
|
+
vcAutoJoin: z.boolean().optional(),
|
|
213
|
+
tts: TtsOverrideSchema
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Per-account configuration.
|
|
217
|
+
* All fields are optional - missing fields inherit from top-level config.
|
|
218
|
+
*/
|
|
219
|
+
const FeishuAccountConfigSchema = z.object({
|
|
220
|
+
enabled: z.boolean().optional(),
|
|
221
|
+
name: z.string().optional(),
|
|
222
|
+
appId: z.string().optional(),
|
|
223
|
+
appSecret: buildSecretInputSchema().optional(),
|
|
224
|
+
encryptKey: buildSecretInputSchema().optional(),
|
|
225
|
+
verificationToken: buildSecretInputSchema().optional(),
|
|
226
|
+
domain: FeishuDomainSchema.optional(),
|
|
227
|
+
connectionMode: FeishuConnectionModeSchema.optional(),
|
|
228
|
+
webhookPath: FeishuWebhookPathSchema.optional(),
|
|
229
|
+
...FeishuSharedConfigShape,
|
|
230
|
+
groupSessionScope: GroupSessionScopeSchema,
|
|
231
|
+
topicSessionMode: TopicSessionModeSchema
|
|
232
|
+
}).strict();
|
|
233
|
+
const FeishuConfigSchemaBase = z.object({
|
|
234
|
+
enabled: z.boolean().optional(),
|
|
235
|
+
defaultAccount: z.string().optional(),
|
|
236
|
+
stickerSets: FeishuStickerSetsSchema.optional(),
|
|
237
|
+
appId: z.string().optional(),
|
|
238
|
+
appSecret: buildSecretInputSchema().optional(),
|
|
239
|
+
encryptKey: buildSecretInputSchema().optional(),
|
|
240
|
+
verificationToken: buildSecretInputSchema().optional(),
|
|
241
|
+
domain: FeishuDomainSchema.optional().default("feishu"),
|
|
242
|
+
connectionMode: FeishuConnectionModeSchema.optional().default("websocket"),
|
|
243
|
+
webhookPath: FeishuWebhookPathSchema.optional().default(DEFAULT_FEISHU_WEBHOOK_PATH),
|
|
244
|
+
...FeishuSharedConfigShape,
|
|
245
|
+
dmPolicy: DmPolicySchema.optional().default("pairing"),
|
|
246
|
+
reactionNotifications: ReactionNotificationModeSchema.optional().default("own"),
|
|
247
|
+
groupPolicy: FeishuGroupPolicySchema.optional().default("allowlist"),
|
|
248
|
+
requireMention: z.boolean().optional(),
|
|
249
|
+
groupSessionScope: GroupSessionScopeSchema,
|
|
250
|
+
topicSessionMode: TopicSessionModeSchema,
|
|
251
|
+
dynamicAgentCreation: DynamicAgentCreationSchema,
|
|
252
|
+
typingIndicator: z.boolean().optional().default(true),
|
|
253
|
+
resolveSenderNames: z.boolean().optional().default(true)
|
|
254
|
+
}).strict();
|
|
255
|
+
const FeishuConfigSchema = buildMultiAccountChannelSchema(FeishuConfigSchemaBase, {
|
|
256
|
+
accountSchema: FeishuAccountConfigSchema,
|
|
257
|
+
optionalAccount: true
|
|
258
|
+
}).superRefine((value, ctx) => {
|
|
259
|
+
const defaultAccount = value.defaultAccount?.trim();
|
|
260
|
+
if (defaultAccount && value.accounts && Object.keys(value.accounts).length > 0) {
|
|
261
|
+
const normalizedDefaultAccount = normalizeAccountId(defaultAccount);
|
|
262
|
+
if (!Object.hasOwn(value.accounts, normalizedDefaultAccount)) ctx.addIssue({
|
|
263
|
+
code: z.ZodIssueCode.custom,
|
|
264
|
+
path: ["defaultAccount"],
|
|
265
|
+
message: `channels.feishu.defaultAccount="${defaultAccount}" does not match a configured account key`
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const defaultConnectionMode = value.connectionMode ?? "websocket";
|
|
269
|
+
const defaultVerificationTokenConfigured = hasConfiguredSecretInput$1(value.verificationToken);
|
|
270
|
+
const defaultEncryptKeyConfigured = hasConfiguredSecretInput$1(value.encryptKey);
|
|
271
|
+
if (defaultConnectionMode === "webhook") {
|
|
272
|
+
if (!defaultVerificationTokenConfigured) ctx.addIssue({
|
|
273
|
+
code: z.ZodIssueCode.custom,
|
|
274
|
+
path: ["verificationToken"],
|
|
275
|
+
message: "channels.feishu.connectionMode=\"webhook\" requires channels.feishu.verificationToken"
|
|
276
|
+
});
|
|
277
|
+
if (!defaultEncryptKeyConfigured) ctx.addIssue({
|
|
278
|
+
code: z.ZodIssueCode.custom,
|
|
279
|
+
path: ["encryptKey"],
|
|
280
|
+
message: "channels.feishu.connectionMode=\"webhook\" requires channels.feishu.encryptKey"
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
for (const [accountId, account] of Object.entries(value.accounts ?? {})) {
|
|
284
|
+
if (!account) continue;
|
|
285
|
+
if ((account.connectionMode ?? defaultConnectionMode) !== "webhook") continue;
|
|
286
|
+
const accountVerificationTokenConfigured = hasConfiguredSecretInput$1(account.verificationToken) || defaultVerificationTokenConfigured;
|
|
287
|
+
const accountEncryptKeyConfigured = hasConfiguredSecretInput$1(account.encryptKey) || defaultEncryptKeyConfigured;
|
|
288
|
+
if (!accountVerificationTokenConfigured) ctx.addIssue({
|
|
289
|
+
code: z.ZodIssueCode.custom,
|
|
290
|
+
path: [
|
|
291
|
+
"accounts",
|
|
292
|
+
accountId,
|
|
293
|
+
"verificationToken"
|
|
294
|
+
],
|
|
295
|
+
message: `channels.feishu.accounts.${accountId}.connectionMode="webhook" requires a verificationToken (account-level or top-level)`
|
|
296
|
+
});
|
|
297
|
+
if (!accountEncryptKeyConfigured) ctx.addIssue({
|
|
298
|
+
code: z.ZodIssueCode.custom,
|
|
299
|
+
path: [
|
|
300
|
+
"accounts",
|
|
301
|
+
accountId,
|
|
302
|
+
"encryptKey"
|
|
303
|
+
],
|
|
304
|
+
message: `channels.feishu.accounts.${accountId}.connectionMode="webhook" requires an encryptKey (account-level or top-level)`
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (value.dmPolicy === "open") {
|
|
308
|
+
if (!(value.allowFrom ?? []).some((entry) => String(entry).trim() === "*")) ctx.addIssue({
|
|
309
|
+
code: z.ZodIssueCode.custom,
|
|
310
|
+
path: ["allowFrom"],
|
|
311
|
+
message: "channels.feishu.dmPolicy=\"open\" requires channels.feishu.allowFrom to include \"*\""
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
const FeishuChannelConfigSchema = buildChannelConfigSchema(FeishuConfigSchema, { jsonSchemaMode: "input" });
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region extensions/feishu/src/doctor-contract.ts
|
|
318
|
+
const streamingAliasMigration = defineChannelAliasMigration({
|
|
319
|
+
channelId: "feishu",
|
|
320
|
+
streaming: { defaultMode: "partial" },
|
|
321
|
+
accountStreamingReplacesRoot: true
|
|
322
|
+
});
|
|
323
|
+
const LEGACY_COALESCE_FIELDS = [
|
|
324
|
+
"enabled",
|
|
325
|
+
"minDelayMs",
|
|
326
|
+
"maxDelayMs"
|
|
327
|
+
];
|
|
328
|
+
const LEGACY_HEARTBEAT_FIELDS = ["visibility", "intervalMs"];
|
|
329
|
+
const toolsBaseMigration = defineKeyMoveMigration({
|
|
330
|
+
from: ["tools", "base"],
|
|
331
|
+
to: ["tools", "bitable"],
|
|
332
|
+
match: (value) => typeof value === "boolean",
|
|
333
|
+
sourceOwn: false
|
|
334
|
+
});
|
|
335
|
+
function sanitizeLegacyHeartbeatFields(params) {
|
|
336
|
+
const heartbeat = asObjectRecord(params.entry.heartbeat);
|
|
337
|
+
if (!heartbeat || Object.keys(heartbeat).length > 0 && !LEGACY_HEARTBEAT_FIELDS.some((field) => Object.hasOwn(heartbeat, field))) return {
|
|
338
|
+
entry: params.entry,
|
|
339
|
+
changed: false
|
|
340
|
+
};
|
|
341
|
+
const next = { ...params.entry };
|
|
342
|
+
delete next.heartbeat;
|
|
343
|
+
params.changes.push(`Removed ${params.pathPrefix}.heartbeat (legacy Feishu fields were never read by runtime).`);
|
|
344
|
+
return {
|
|
345
|
+
entry: next,
|
|
346
|
+
changed: true
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function sanitizeLegacyCoalesceFields(params) {
|
|
350
|
+
const streaming = asObjectRecord(params.entry.streaming);
|
|
351
|
+
const block = asObjectRecord(streaming?.block);
|
|
352
|
+
const coalesce = asObjectRecord(block?.coalesce);
|
|
353
|
+
if (!streaming || !block || !coalesce) return {
|
|
354
|
+
entry: params.entry,
|
|
355
|
+
changed: false
|
|
356
|
+
};
|
|
357
|
+
const removed = LEGACY_COALESCE_FIELDS.filter((field) => coalesce[field] !== void 0);
|
|
358
|
+
if (removed.length === 0) return {
|
|
359
|
+
entry: params.entry,
|
|
360
|
+
changed: false
|
|
361
|
+
};
|
|
362
|
+
const nextCoalesce = { ...coalesce };
|
|
363
|
+
for (const field of removed) delete nextCoalesce[field];
|
|
364
|
+
params.changes.push(`Removed ${params.pathPrefix}.streaming.block.coalesce.{${removed.join(",")}} (legacy Feishu-only fields; block delivery reads minChars/maxChars/idleMs).`);
|
|
365
|
+
return {
|
|
366
|
+
entry: {
|
|
367
|
+
...params.entry,
|
|
368
|
+
streaming: {
|
|
369
|
+
...streaming,
|
|
370
|
+
block: {
|
|
371
|
+
...block,
|
|
372
|
+
coalesce: nextCoalesce
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
changed: true
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function hasLegacyWebhookPath(value) {
|
|
380
|
+
const path = asObjectRecord(value)?.webhookPath;
|
|
381
|
+
return typeof path === "string" && normalizeFeishuWebhookPath(path) !== path;
|
|
382
|
+
}
|
|
383
|
+
function normalizeLegacyWebhookPath(params) {
|
|
384
|
+
const path = params.entry.webhookPath;
|
|
385
|
+
if (typeof path !== "string") return {
|
|
386
|
+
entry: params.entry,
|
|
387
|
+
changed: false
|
|
388
|
+
};
|
|
389
|
+
const normalized = normalizeFeishuWebhookPath(path);
|
|
390
|
+
const canonical = normalized ?? "/feishu/events";
|
|
391
|
+
if (canonical === path) return {
|
|
392
|
+
entry: params.entry,
|
|
393
|
+
changed: false
|
|
394
|
+
};
|
|
395
|
+
params.changes.push(normalized === null ? `Reset invalid ${params.pathPrefix}.webhookPath to ${DEFAULT_FEISHU_WEBHOOK_PATH}.` : `Normalized ${params.pathPrefix}.webhookPath to its HTTP request path.`);
|
|
396
|
+
return {
|
|
397
|
+
entry: {
|
|
398
|
+
...params.entry,
|
|
399
|
+
webhookPath: canonical
|
|
400
|
+
},
|
|
401
|
+
changed: true
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function normalizeFeishuLegacyConfigEntries(cfg, changes) {
|
|
405
|
+
return normalizeChannelConfigEntries({
|
|
406
|
+
cfg,
|
|
407
|
+
channelId: "feishu",
|
|
408
|
+
changes,
|
|
409
|
+
normalizeEntry: (params) => {
|
|
410
|
+
const tools = toolsBaseMigration.normalize(params);
|
|
411
|
+
const coalesce = sanitizeLegacyCoalesceFields({
|
|
412
|
+
...params,
|
|
413
|
+
entry: tools.entry
|
|
414
|
+
});
|
|
415
|
+
const heartbeat = sanitizeLegacyHeartbeatFields({
|
|
416
|
+
...params,
|
|
417
|
+
entry: coalesce.entry
|
|
418
|
+
});
|
|
419
|
+
const webhook = normalizeLegacyWebhookPath({
|
|
420
|
+
...params,
|
|
421
|
+
entry: heartbeat.entry
|
|
422
|
+
});
|
|
423
|
+
return {
|
|
424
|
+
entry: webhook.entry,
|
|
425
|
+
changed: tools.changed || coalesce.changed || heartbeat.changed || webhook.changed
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
}).config;
|
|
429
|
+
}
|
|
430
|
+
const feishuStrayEntryConfigMigration = defineStrayPluginEntryConfigMigration({
|
|
431
|
+
pluginId: "feishu",
|
|
432
|
+
channelId: "feishu",
|
|
433
|
+
validateMergedChannelConfig: (merged) => FeishuConfigSchema.safeParse(merged).success
|
|
434
|
+
});
|
|
435
|
+
const legacyConfigRules = [
|
|
436
|
+
...streamingAliasMigration.legacyConfigRules,
|
|
437
|
+
feishuStrayEntryConfigMigration.legacyConfigRule,
|
|
438
|
+
{
|
|
439
|
+
path: ["channels", "feishu"],
|
|
440
|
+
message: "channels.feishu[.accounts.<id>].webhookPath must be a canonical HTTP request path; run \"openclaw doctor --fix\".",
|
|
441
|
+
match: (value) => {
|
|
442
|
+
const entry = asObjectRecord(value);
|
|
443
|
+
return hasLegacyWebhookPath(entry) || hasLegacyAccountStreamingAliases(entry?.accounts, hasLegacyWebhookPath);
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
path: ["channels", "feishu"],
|
|
448
|
+
message: "channels.feishu[.accounts.<id>].tools.base is legacy; use tools.bitable. Run \"openclaw doctor --fix\".",
|
|
449
|
+
match: (value) => {
|
|
450
|
+
const entry = asObjectRecord(value);
|
|
451
|
+
return toolsBaseMigration.hasLegacy(entry) || hasLegacyAccountStreamingAliases(entry?.accounts, toolsBaseMigration.hasLegacy);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
];
|
|
455
|
+
function normalizeCompatibilityConfig({ cfg }) {
|
|
456
|
+
const aliases = streamingAliasMigration.normalizeChannelConfig({ cfg });
|
|
457
|
+
const entries = normalizeFeishuLegacyConfigEntries(aliases.config, aliases.changes);
|
|
458
|
+
const stray = feishuStrayEntryConfigMigration.normalizeConfig({ cfg: entries });
|
|
459
|
+
return {
|
|
460
|
+
config: stray.config,
|
|
461
|
+
changes: [...aliases.changes, ...stray.changes]
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
465
|
+
export { normalizeFeishuWebhookPath as a, DEFAULT_FEISHU_WEBHOOK_PATH as i, normalizeCompatibilityConfig as n, normalizeFeishuExternalKey as o, FeishuChannelConfigSchema as r, legacyConfigRules as t };
|