@openclaw/feishu 2026.7.2-beta.7 → 2026.8.1-beta.3

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.
Files changed (35) hide show
  1. package/dist/accounts-CCCdMen2.js +203 -0
  2. package/dist/api.js +61 -60
  3. package/dist/{channel-CScE82zY.js → channel-Ceq80_zP.js} +71 -42
  4. package/dist/channel-plugin-api.js +1 -1
  5. package/dist/{channel.runtime-B21e1E0r.js → channel.runtime-DnRyzGoU.js} +201 -152
  6. package/dist/{client-Dcbs6vml.js → client-Hp7uo_cl.js} +27 -16
  7. package/dist/contract-api.js +2 -2
  8. package/dist/{conversation-id-BeJL-wq7.js → conversation-id-VYgGQ-GX.js} +11 -15
  9. package/dist/doctor-contract-api.js +1 -1
  10. package/dist/doctor-contract-bEQXIXyP.js +158 -0
  11. package/dist/{drive-hSi_Utp0.js → drive-BVYM_Y4k.js} +325 -44
  12. package/dist/{media-CEUraFOR.js → media-BDouVaaM.js} +298 -182
  13. package/dist/{monitor-DngbaA6a.js → monitor-CZ8cDXrq.js} +3 -3
  14. package/dist/{monitor.account-BnTWCw55.js → monitor.account-Dq_voVMB.js} +263 -179
  15. package/dist/{monitor.startup-CqH2tiJq.js → monitor.startup-BGErejNH.js} +1 -1
  16. package/dist/{probe-p3POS2RN.js → probe-DVpy58s0.js} +2 -2
  17. package/dist/security-audit-D6Fz2h6p.js +23 -0
  18. package/dist/{send-result-B9_BpUPx.js → send-result-DEAycmOk.js} +18 -9
  19. package/dist/session-binding-contract-api.js +2 -2
  20. package/dist/{session-conversation-BksWrfzm.js → session-conversation-DFCIvQK-.js} +1 -1
  21. package/dist/session-key-api.js +1 -1
  22. package/dist/setup-api.js +1 -1
  23. package/dist/{subagent-hooks-Cx1cX7rW.js → subagent-hooks-KUi-7vUy.js} +4 -64
  24. package/dist/subagent-hooks-api.js +1 -1
  25. package/dist/{thread-bindings-N3wkgkIN.js → thread-bindings-CptM_YU6.js} +7 -9
  26. package/openclaw.plugin.json +143 -1
  27. package/package.json +4 -4
  28. package/skills/feishu-doc/SKILL.md +20 -195
  29. package/skills/feishu-doc/references/block-types.md +9 -14
  30. package/skills/feishu-drive/SKILL.md +16 -102
  31. package/skills/feishu-perm/SKILL.md +10 -110
  32. package/skills/feishu-wiki/SKILL.md +13 -109
  33. package/dist/accounts-u9X5Wsan.js +0 -469
  34. package/dist/doctor-contract-BiD9tyIv.js +0 -102
  35. package/dist/security-audit-D7WK_BHh.js +0 -11
@@ -1,122 +1,26 @@
1
1
  ---
2
2
  name: feishu-wiki
3
3
  description: |
4
- Feishu knowledge base navigation. Activate when user mentions knowledge base, wiki, or wiki links.
4
+ Feishu knowledge-base navigation workflows. Activate when the user mentions a knowledge base, wiki, or wiki link.
5
5
  ---
6
6
 
7
- # Feishu Wiki Tool
7
+ # Feishu wiki
8
8
 
9
- Single tool `feishu_wiki` for knowledge base operations.
9
+ Use the single `feishu_wiki` tool and its current action schema.
10
10
 
11
- Wiki `space_id` values are opaque strings. Always keep them quoted in tool calls, even when they contain only digits; passing a long numeric-looking ID as a number can corrupt the suffix due to JavaScript number precision limits.
11
+ From `https://example.feishu.cn/wiki/ABC123def`, use `ABC123def` as `token`. Treat every `space_id` as an opaque quoted string, even when it contains only digits.
12
12
 
13
- ## Token Extraction
13
+ ## Navigate
14
14
 
15
- From URL `https://xxx.feishu.cn/wiki/ABC123def` → `token` = `ABC123def`
15
+ - Use `spaces` to enumerate accessible knowledge spaces and `nodes` for a space or parent node.
16
+ - Continue pagination with the returned `page_token` while `has_more` is true, keeping the same space and parent.
17
+ - Use `search` when the user provides a query but not an exact node.
18
+ - Use `get` to resolve a wiki token to its `node_token`, `obj_token`, and `obj_type`.
16
19
 
17
- ## Actions
20
+ ## Create and organize
18
21
 
19
- ### List Knowledge Spaces
22
+ Wiki creation supports only `docx`, `sheet`, and `bitable`; `docx` is the default. Resolve an exact space and parent before create, move, or rename operations, and confirm ambiguous or destructive reorganizations.
20
23
 
21
- ```json
22
- { "action": "spaces" }
23
- ```
24
+ ## Wiki content workflow
24
25
 
25
- Returns one page of accessible wiki spaces plus `has_more` and `page_token`.
26
- Continue with the returned `page_token` while `has_more` is true:
27
-
28
- ```json
29
- { "action": "spaces", "page_token": "next-page-token" }
30
- ```
31
-
32
- ### List Nodes
33
-
34
- ```json
35
- { "action": "nodes", "space_id": "7xxx" }
36
- ```
37
-
38
- With parent:
39
-
40
- ```json
41
- { "action": "nodes", "space_id": "7xxx", "parent_node_token": "wikcnXXX" }
42
- ```
43
-
44
- Returns one page of nodes plus `has_more` and `page_token`. Continue with the
45
- same `space_id` and `parent_node_token`, adding the returned `page_token`, while
46
- `has_more` is true. Both list actions accept optional `page_size` from 1 to 50.
47
-
48
- ### Get Node Details
49
-
50
- ```json
51
- { "action": "get", "token": "ABC123def" }
52
- ```
53
-
54
- Returns: `node_token`, `obj_token`, `obj_type`, etc. Use `obj_token` with `feishu_doc` to read/write the document.
55
-
56
- ### Create Node
57
-
58
- ```json
59
- { "action": "create", "space_id": "7xxx", "title": "New Page" }
60
- ```
61
-
62
- With type and parent:
63
-
64
- ```json
65
- {
66
- "action": "create",
67
- "space_id": "7xxx",
68
- "title": "Sheet",
69
- "obj_type": "sheet",
70
- "parent_node_token": "wikcnXXX"
71
- }
72
- ```
73
-
74
- `obj_type`: `docx` (default), `sheet`, `bitable`, `mindnote`, `file`, `doc`, `slides`
75
-
76
- ### Move Node
77
-
78
- ```json
79
- { "action": "move", "space_id": "7xxx", "node_token": "wikcnXXX" }
80
- ```
81
-
82
- To different location:
83
-
84
- ```json
85
- {
86
- "action": "move",
87
- "space_id": "7xxx",
88
- "node_token": "wikcnXXX",
89
- "target_space_id": "7yyy",
90
- "target_parent_token": "wikcnYYY"
91
- }
92
- ```
93
-
94
- ### Rename Node
95
-
96
- ```json
97
- { "action": "rename", "space_id": "7xxx", "node_token": "wikcnXXX", "title": "New Title" }
98
- ```
99
-
100
- ## Wiki-Doc Workflow
101
-
102
- To edit a wiki page:
103
-
104
- 1. Get node: `{ "action": "get", "token": "wiki_token" }` → returns `obj_token`
105
- 2. Read doc: `feishu_doc { "action": "read", "doc_token": "obj_token" }`
106
- 3. Write doc: `feishu_doc { "action": "write", "doc_token": "obj_token", "content": "..." }`
107
-
108
- ## Configuration
109
-
110
- ```yaml
111
- channels:
112
- feishu:
113
- tools:
114
- wiki: true # default: true
115
- doc: true # required - wiki content uses feishu_doc
116
- ```
117
-
118
- **Dependency:** This tool requires `feishu_doc` to be enabled. Wiki pages are documents - use `feishu_wiki` to navigate, then `feishu_doc` to read/edit content.
119
-
120
- ## Permissions
121
-
122
- Required: `wiki:wiki` or `wiki:wiki:readonly`
26
+ Wiki navigation is independent of the document tool. When `get` returns a `docx` object, use its `obj_token` as `doc_token` with `feishu_doc` to read or edit the page. Other object types require a currently available tool that supports that type; do not treat them as documents.
@@ -1,469 +0,0 @@
1
- import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { isRecord, normalizeOptionalString, normalizeStringEntries, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
3
- import { DEFAULT_ACCOUNT_ID, createAccountListHelpers, hasConfiguredAccountValue, normalizeAccountId, normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-resolution";
4
- import { coerceSecretRef } from "openclaw/plugin-sdk/provider-auth";
5
- import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
6
- //#region extensions/feishu/src/comment-target.ts
7
- const FEISHU_COMMENT_FILE_TYPES = [
8
- "doc",
9
- "docx",
10
- "file",
11
- "sheet",
12
- "slides"
13
- ];
14
- function normalizeCommentFileType(value) {
15
- return typeof value === "string" && FEISHU_COMMENT_FILE_TYPES.includes(value) ? value : void 0;
16
- }
17
- function buildFeishuCommentTarget(params) {
18
- return `comment:${params.fileType}:${params.fileToken}:${params.commentId}`;
19
- }
20
- function parseFeishuCommentTarget(raw) {
21
- const trimmed = raw?.trim();
22
- if (!trimmed?.startsWith("comment:")) return null;
23
- const parts = trimmed.split(":");
24
- if (parts.length !== 4) return null;
25
- const fileType = normalizeCommentFileType(parts[1]);
26
- const fileToken = parts[2]?.trim();
27
- const commentId = parts[3]?.trim();
28
- if (!fileType || !fileToken || !commentId) return null;
29
- return {
30
- fileType,
31
- fileToken,
32
- commentId
33
- };
34
- }
35
- //#endregion
36
- //#region extensions/feishu/src/send-rate-limit.ts
37
- const FEISHU_SEND_RATE_LIMIT_CODES = /* @__PURE__ */ new Set([230020, 11232]);
38
- function getFeishuSendRateLimitCode(error) {
39
- if (!isRecord(error)) return;
40
- const response = isRecord(error.response) ? error.response : void 0;
41
- if (response?.status === 429) return 429;
42
- const code = (isRecord(response?.data) ? response.data : void 0)?.code;
43
- return typeof code === "number" && FEISHU_SEND_RATE_LIMIT_CODES.has(code) ? code : void 0;
44
- }
45
- function getFeishuSendRateLimitCodeFromResponse(response) {
46
- if (!isRecord(response)) return;
47
- const code = response.code;
48
- return typeof code === "number" && FEISHU_SEND_RATE_LIMIT_CODES.has(code) ? code : void 0;
49
- }
50
- //#endregion
51
- //#region extensions/feishu/src/comment-shared.ts
52
- function encodeQuery(params) {
53
- const query = new URLSearchParams();
54
- for (const [key, value] of Object.entries(params)) {
55
- const trimmed = value?.trim();
56
- if (trimmed) query.set(key, trimmed);
57
- }
58
- const queryString = query.toString();
59
- return queryString ? `?${queryString}` : "";
60
- }
61
- const readString = readStringValue;
62
- const normalizeString = normalizeOptionalString;
63
- const isRecord$1 = isRecord;
64
- function formatFeishuApiError(error, options = {}) {
65
- if (!isRecord$1(error)) return typeof error === "string" ? error : JSON.stringify(error);
66
- const config = isRecord$1(error.config) ? error.config : void 0;
67
- const response = isRecord$1(error.response) ? error.response : void 0;
68
- const responseData = isRecord$1(response?.data) ? response?.data : void 0;
69
- const feishuLogId = readString(responseData?.log_id) || (options.includeNestedErrorLogId ? readString(isRecord$1(responseData?.error) ? responseData.error.log_id : void 0) : void 0);
70
- const nestedError = isRecord$1(responseData?.error) ? responseData.error : void 0;
71
- return JSON.stringify({
72
- message: typeof error.message === "string" ? error.message : typeof error === "string" ? error : JSON.stringify(error),
73
- code: readString(error.code),
74
- method: readString(config?.method),
75
- url: readString(config?.url),
76
- ...options.includeConfigParams ? { params: config?.params } : {},
77
- http_status: typeof response?.status === "number" ? response.status : void 0,
78
- feishu_code: typeof responseData?.code === "number" ? responseData.code : readString(responseData?.code),
79
- feishu_msg: readString(responseData?.msg),
80
- feishu_log_id: feishuLogId,
81
- feishu_troubleshooter: readString(responseData?.troubleshooter) || readString(nestedError?.troubleshooter)
82
- });
83
- }
84
- function formatFeishuApiFailure(error, errorPrefix, options = {}) {
85
- return `${errorPrefix}: ${formatFeishuApiError(error, options) || "unknown error"}`;
86
- }
87
- function createFeishuApiError(error, errorPrefix, options = {}) {
88
- return new Error(formatFeishuApiFailure(error, errorPrefix, options), { cause: error });
89
- }
90
- const FEISHU_SEND_RETRY_BASE_MS = 500;
91
- async function requestFeishuApi(request, errorPrefix, options = {}) {
92
- try {
93
- return await retryAsync(async () => {
94
- const result = await request();
95
- const fulfilledRateLimit = getFeishuSendRateLimitCodeFromResponse(result);
96
- if (fulfilledRateLimit !== void 0) throw Object.assign(/* @__PURE__ */ new Error(`Request fulfilled with rate-limit code ${fulfilledRateLimit}`), { response: {
97
- status: 200,
98
- data: result
99
- } });
100
- return result;
101
- }, {
102
- attempts: 3,
103
- minDelayMs: options.retryDelayMs ?? FEISHU_SEND_RETRY_BASE_MS,
104
- shouldRetry: (error) => getFeishuSendRateLimitCode(error) !== void 0
105
- });
106
- } catch (error) {
107
- throw createFeishuApiError(error, errorPrefix, options);
108
- }
109
- }
110
- function readDocsLinkUrl(element) {
111
- const docsLink = isRecord$1(element.docs_link) ? element.docs_link : void 0;
112
- return normalizeString(docsLink?.url) || normalizeString(docsLink?.link) || normalizeString(element.url) || normalizeString(element.link) || void 0;
113
- }
114
- function readMentionUserId(element) {
115
- const mention = isRecord$1(element.mention) ? element.mention : void 0;
116
- const person = isRecord$1(element.person) ? element.person : void 0;
117
- return normalizeString(person?.user_id) || normalizeString(mention?.user_id) || normalizeString(mention?.open_id) || normalizeString(element.mention_user) || normalizeString(element.user_id) || void 0;
118
- }
119
- function readMentionDisplayText(element, userId) {
120
- const mention = isRecord$1(element.mention) ? element.mention : void 0;
121
- const mentionName = normalizeString(mention?.name) || normalizeString(mention?.display_name) || normalizeString(element.name);
122
- return mentionName ? `@${mentionName}` : `@${userId}`;
123
- }
124
- function normalizeCommentText(parts) {
125
- return parts.join("").trim() || void 0;
126
- }
127
- function normalizeCommentSemanticText(parts) {
128
- return parts.join("").replace(/\s+/g, " ").trim() || void 0;
129
- }
130
- function readElementTextPreservingWhitespace(element) {
131
- return (isRecord$1(element.text_run) ? readString(element.text_run.content) || readString(element.text_run.text) : void 0) || readString(element.text) || readString(element.content) || readString(element.name) || void 0;
132
- }
133
- const FEISHU_LINK_TOKEN_MIN_LENGTH = 22;
134
- const FEISHU_LINK_TOKEN_MAX_LENGTH = 28;
135
- const COMMENT_LINK_KIND_ALIASES = /* @__PURE__ */ new Map([
136
- ["doc", "doc"],
137
- ["docs", "doc"],
138
- ["docx", "docx"],
139
- ["sheet", "sheet"],
140
- ["sheets", "sheet"],
141
- ["slide", "slides"],
142
- ["slides", "slides"],
143
- ["file", "file"],
144
- ["files", "file"],
145
- ["wiki", "wiki"],
146
- ["mindnote", "mindnote"],
147
- ["mindnotes", "mindnote"],
148
- ["bitable", "bitable"],
149
- ["base", "base"]
150
- ]);
151
- function isCommentFileType(value) {
152
- return typeof value === "string" && FEISHU_COMMENT_FILE_TYPES.includes(value);
153
- }
154
- function isReasonableFeishuLinkToken(token) {
155
- return typeof token === "string" && token.length >= FEISHU_LINK_TOKEN_MIN_LENGTH && token.length <= FEISHU_LINK_TOKEN_MAX_LENGTH;
156
- }
157
- function parseCommentLinkedDocumentPath(pathname) {
158
- const segments = normalizeStringEntries(pathname.split("/"));
159
- const offset = segments[0]?.toLowerCase() === "space" ? 1 : 0;
160
- const kind = COMMENT_LINK_KIND_ALIASES.get(segments[offset]?.toLowerCase() ?? "");
161
- const token = normalizeString(segments[offset + 1]);
162
- if (!kind || !isReasonableFeishuLinkToken(token)) return null;
163
- return {
164
- urlKind: kind,
165
- token
166
- };
167
- }
168
- function hasResolvedLinkedDocumentReference(link) {
169
- return link.urlKind !== "unknown" && (Boolean(link.resolvedObjToken) || Boolean(link.wikiNodeToken));
170
- }
171
- function resolveCommentLinkedDocumentFromUrl(params) {
172
- const link = {
173
- rawUrl: params.rawUrl,
174
- urlKind: "unknown"
175
- };
176
- try {
177
- const parsedPath = parseCommentLinkedDocumentPath(new URL(params.rawUrl).pathname);
178
- if (!parsedPath) return link;
179
- const { urlKind, token } = parsedPath;
180
- link.urlKind = urlKind;
181
- if (urlKind === "wiki") {
182
- link.urlKind = "wiki";
183
- link.wikiNodeToken = token;
184
- } else {
185
- link.resolvedObjType = urlKind;
186
- link.resolvedObjToken = token;
187
- }
188
- if (link.resolvedObjType && link.resolvedObjToken && isCommentFileType(link.resolvedObjType) && params.currentDocument?.fileType === link.resolvedObjType && params.currentDocument.fileToken === link.resolvedObjToken) link.isCurrentDocument = true;
189
- else if (link.resolvedObjType && link.resolvedObjToken && isCommentFileType(link.resolvedObjType)) link.isCurrentDocument = false;
190
- } catch {
191
- return link;
192
- }
193
- return link;
194
- }
195
- function parseCommentContentElements(params) {
196
- const elements = Array.isArray(params.elements) ? params.elements : [];
197
- const plainTextParts = [];
198
- const semanticTextParts = [];
199
- const mentions = [];
200
- const linkedDocuments = [];
201
- const botIds = new Set(Array.from(params.botOpenIds ?? []).map((value) => normalizeString(value)).filter((value) => Boolean(value)));
202
- const linkedDocumentKeys = /* @__PURE__ */ new Set();
203
- let botMentioned = false;
204
- for (const rawElement of elements) {
205
- if (!isRecord$1(rawElement)) continue;
206
- const element = rawElement;
207
- const type = normalizeString(element.type);
208
- const text = (type === "text_run" ? readElementTextPreservingWhitespace(element) : void 0) || (type === "text" ? readElementTextPreservingWhitespace(element) : void 0) || (type === "docs_link" || type === "link" ? readDocsLinkUrl(element) : void 0) || (type === "mention" || type === "mention_user" || type === "person" ? (() => {
209
- const userId = readMentionUserId(element);
210
- return userId ? readMentionDisplayText(element, userId) : void 0;
211
- })() : void 0) || readElementTextPreservingWhitespace(element) || void 0;
212
- if (type === "mention" || type === "mention_user" || type === "person") {
213
- const userId = readMentionUserId(element);
214
- if (userId) {
215
- const displayText = readMentionDisplayText(element, userId);
216
- const isBotMention = botIds.has(userId);
217
- mentions.push({
218
- userId,
219
- displayText,
220
- isBotMention
221
- });
222
- plainTextParts.push(displayText);
223
- if (!isBotMention) semanticTextParts.push(displayText);
224
- else botMentioned = true;
225
- continue;
226
- }
227
- }
228
- if (type === "docs_link" || type === "link") {
229
- const rawUrl = readDocsLinkUrl(element);
230
- if (rawUrl) {
231
- plainTextParts.push(rawUrl);
232
- semanticTextParts.push(rawUrl);
233
- const linkedDocument = resolveCommentLinkedDocumentFromUrl({
234
- rawUrl,
235
- currentDocument: params.currentDocument
236
- });
237
- if (hasResolvedLinkedDocumentReference(linkedDocument)) {
238
- const key = [
239
- linkedDocument.rawUrl,
240
- linkedDocument.urlKind,
241
- linkedDocument.resolvedObjType,
242
- linkedDocument.resolvedObjToken,
243
- linkedDocument.wikiNodeToken
244
- ].join(":");
245
- if (!linkedDocumentKeys.has(key)) {
246
- linkedDocumentKeys.add(key);
247
- linkedDocuments.push(linkedDocument);
248
- }
249
- }
250
- continue;
251
- }
252
- }
253
- if (text) {
254
- plainTextParts.push(text);
255
- semanticTextParts.push(text);
256
- }
257
- }
258
- return {
259
- plainText: normalizeCommentText(plainTextParts),
260
- semanticText: normalizeCommentSemanticText(semanticTextParts),
261
- mentions,
262
- linkedDocuments,
263
- botMentioned
264
- };
265
- }
266
- function extractReplyText(reply) {
267
- if (!reply || !isRecord$1(reply.content)) return;
268
- return parseCommentContentElements({ elements: Array.isArray(reply.content.elements) ? reply.content.elements : [] }).plainText;
269
- }
270
- //#endregion
271
- //#region extensions/feishu/src/accounts.ts
272
- var accounts_exports = /* @__PURE__ */ __exportAll({
273
- FeishuSecretRefUnavailableError: () => FeishuSecretRefUnavailableError,
274
- inspectFeishuCredentials: () => inspectFeishuCredentials,
275
- listEnabledFeishuAccounts: () => listEnabledFeishuAccounts,
276
- listFeishuAccountIds: () => listFeishuAccountIds,
277
- resolveDefaultFeishuAccountId: () => resolveDefaultFeishuAccountId,
278
- resolveDefaultFeishuAccountSelection: () => resolveDefaultFeishuAccountSelection,
279
- resolveFeishuAccount: () => resolveFeishuAccount,
280
- resolveFeishuCredentials: () => resolveFeishuCredentials,
281
- resolveFeishuRuntimeAccount: () => resolveFeishuRuntimeAccount
282
- });
283
- const { listAccountIds: listFeishuAccountIds, resolveDefaultAccountId, resolveAccountConfig: resolveMergedFeishuAccountConfig } = createAccountListHelpers("feishu", {
284
- allowUnlistedDefaultAccount: true,
285
- omitKeys: ["defaultAccount"],
286
- nestedObjectKeys: ["tools"],
287
- hasImplicitDefaultAccount: (cfg) => {
288
- const feishu = cfg.channels?.feishu;
289
- return hasConfiguredAccountValue(feishu?.appId) && hasConfiguredAccountValue(feishu?.appSecret);
290
- }
291
- });
292
- function formatSecretRefLabel(ref) {
293
- return `${ref.source}:${ref.provider}:${ref.id}`;
294
- }
295
- var FeishuSecretRefUnavailableError = class extends Error {
296
- constructor(path, ref) {
297
- super(`${path}: unresolved SecretRef "${formatSecretRefLabel(ref)}". Resolve this command against an active gateway runtime snapshot before reading it.`);
298
- this.name = "FeishuSecretRefUnavailableError";
299
- this.path = path;
300
- }
301
- };
302
- function resolveFeishuSecretLike(params) {
303
- const asString = normalizeString(params.value);
304
- if (asString) return asString;
305
- const ref = coerceSecretRef(params.value);
306
- if (!ref) return;
307
- if (params.mode === "inspect") {
308
- if (params.allowEnvSecretRefRead && ref.source === "env") {
309
- const envValue = normalizeString(process.env[ref.id]);
310
- if (envValue) return envValue;
311
- }
312
- return;
313
- }
314
- throw new FeishuSecretRefUnavailableError(params.path, ref);
315
- }
316
- function resolveFeishuBaseCredentials(cfg, mode) {
317
- const appId = resolveFeishuSecretLike({
318
- value: cfg?.appId,
319
- path: "channels.feishu.appId",
320
- mode,
321
- allowEnvSecretRefRead: true
322
- });
323
- const appSecret = resolveFeishuSecretLike({
324
- value: cfg?.appSecret,
325
- path: "channels.feishu.appSecret",
326
- mode,
327
- allowEnvSecretRefRead: true
328
- });
329
- if (!appId || !appSecret) return null;
330
- return {
331
- appId,
332
- appSecret,
333
- domain: cfg?.domain ?? "feishu"
334
- };
335
- }
336
- function resolveFeishuEventSecrets(cfg, mode) {
337
- return {
338
- encryptKey: (cfg?.connectionMode ?? "websocket") === "webhook" ? resolveFeishuSecretLike({
339
- value: cfg?.encryptKey,
340
- path: "channels.feishu.encryptKey",
341
- mode,
342
- allowEnvSecretRefRead: true
343
- }) : normalizeString(cfg?.encryptKey),
344
- verificationToken: resolveFeishuSecretLike({
345
- value: cfg?.verificationToken,
346
- path: "channels.feishu.verificationToken",
347
- mode,
348
- allowEnvSecretRefRead: true
349
- })
350
- };
351
- }
352
- /**
353
- * Resolve the default account selection and its source.
354
- */
355
- function resolveDefaultFeishuAccountSelection(cfg) {
356
- const preferred = normalizeOptionalAccountId((cfg.channels?.feishu)?.defaultAccount);
357
- if (preferred) return {
358
- accountId: preferred,
359
- source: "explicit-default"
360
- };
361
- const ids = listFeishuAccountIds(cfg);
362
- if (ids.includes(DEFAULT_ACCOUNT_ID)) return {
363
- accountId: DEFAULT_ACCOUNT_ID,
364
- source: "mapped-default"
365
- };
366
- return {
367
- accountId: ids[0] ?? DEFAULT_ACCOUNT_ID,
368
- source: "fallback"
369
- };
370
- }
371
- /**
372
- * Resolve the default account ID.
373
- */
374
- function resolveDefaultFeishuAccountId(cfg) {
375
- return resolveDefaultAccountId(cfg);
376
- }
377
- /**
378
- * Merge top-level config with account-specific config.
379
- * Account-specific fields override top-level fields.
380
- */
381
- function mergeFeishuAccountConfig(cfg, accountId) {
382
- const feishuCfg = cfg.channels?.feishu;
383
- const merged = resolveMergedFeishuAccountConfig(cfg, accountId);
384
- const topTools = feishuCfg?.tools;
385
- if (merged.tools === void 0 && topTools !== void 0) return {
386
- ...merged,
387
- tools: topTools
388
- };
389
- if (topTools?.bitable === false) return {
390
- ...merged,
391
- tools: {
392
- ...merged.tools,
393
- bitable: false
394
- }
395
- };
396
- return merged;
397
- }
398
- function resolveFeishuCredentials(cfg, options) {
399
- const mode = options?.mode ?? (options?.allowUnresolvedSecretRef ? "inspect" : "strict");
400
- const base = resolveFeishuBaseCredentials(cfg, mode);
401
- if (!base) return null;
402
- const eventSecrets = resolveFeishuEventSecrets(cfg, mode);
403
- return {
404
- ...base,
405
- ...eventSecrets
406
- };
407
- }
408
- function inspectFeishuCredentials(cfg) {
409
- return resolveFeishuCredentials(cfg, { mode: "inspect" });
410
- }
411
- function buildResolvedFeishuAccount(params) {
412
- const hasExplicitAccountId = typeof params.accountId === "string" && params.accountId.trim() !== "";
413
- const defaultSelection = hasExplicitAccountId ? null : resolveDefaultFeishuAccountSelection(params.cfg);
414
- const accountId = hasExplicitAccountId ? normalizeAccountId(params.accountId) : defaultSelection?.accountId ?? DEFAULT_ACCOUNT_ID;
415
- const selectionSource = hasExplicitAccountId ? "explicit" : defaultSelection?.source ?? "fallback";
416
- const baseEnabled = (params.cfg.channels?.feishu)?.enabled !== false;
417
- const merged = mergeFeishuAccountConfig(params.cfg, accountId);
418
- const accountEnabled = merged.enabled !== false;
419
- const enabled = baseEnabled && accountEnabled;
420
- const baseCreds = resolveFeishuBaseCredentials(merged, params.baseMode);
421
- const eventSecrets = resolveFeishuEventSecrets(merged, params.eventSecretMode);
422
- const accountName = merged.name;
423
- return {
424
- accountId,
425
- selectionSource,
426
- enabled,
427
- configured: Boolean(baseCreds),
428
- name: typeof accountName === "string" ? accountName.trim() || void 0 : void 0,
429
- appId: baseCreds?.appId,
430
- appSecret: baseCreds?.appSecret,
431
- encryptKey: eventSecrets.encryptKey,
432
- verificationToken: eventSecrets.verificationToken,
433
- domain: baseCreds?.domain ?? "feishu",
434
- config: merged
435
- };
436
- }
437
- /**
438
- * Resolve a read-only Feishu account snapshot for CLI/config surfaces.
439
- * Unresolved SecretRefs are treated as unavailable instead of throwing.
440
- */
441
- function resolveFeishuAccount(params) {
442
- return buildResolvedFeishuAccount({
443
- ...params,
444
- baseMode: "inspect",
445
- eventSecretMode: "inspect"
446
- });
447
- }
448
- /**
449
- * Resolve a runtime Feishu account.
450
- * Required app credentials stay strict; event-only secrets can be required by callers.
451
- */
452
- function resolveFeishuRuntimeAccount(params, options) {
453
- return buildResolvedFeishuAccount({
454
- ...params,
455
- baseMode: "strict",
456
- eventSecretMode: options?.requireEventSecrets ? "strict" : "inspect"
457
- });
458
- }
459
- /**
460
- * List all enabled and configured accounts.
461
- */
462
- function listEnabledFeishuAccounts(cfg) {
463
- return listFeishuAccountIds(cfg).map((accountId) => resolveFeishuAccount({
464
- cfg,
465
- accountId
466
- })).filter((account) => account.enabled && account.configured);
467
- }
468
- //#endregion
469
- export { normalizeCommentFileType as _, resolveDefaultFeishuAccountId as a, encodeQuery as c, isRecord$1 as d, normalizeString as f, buildFeishuCommentTarget as g, requestFeishuApi as h, listFeishuAccountIds as i, extractReplyText as l, readString as m, inspectFeishuCredentials as n, resolveFeishuAccount as o, parseCommentContentElements as p, listEnabledFeishuAccounts as r, resolveFeishuRuntimeAccount as s, accounts_exports as t, formatFeishuApiError as u, parseFeishuCommentTarget as v };