@openclaw/feishu 2026.7.2-beta.7 → 2026.8.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accounts-CCCdMen2.js +203 -0
- package/dist/api.js +52 -51
- package/dist/{channel-CScE82zY.js → channel-Dzb5jv3i.js} +53 -30
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-B21e1E0r.js → channel.runtime-CoPaHQQ6.js} +181 -142
- package/dist/{client-Dcbs6vml.js → client-Hp7uo_cl.js} +27 -16
- package/dist/contract-api.js +2 -2
- package/dist/{conversation-id-BeJL-wq7.js → conversation-id-VYgGQ-GX.js} +11 -15
- package/dist/doctor-contract-api.js +1 -1
- package/dist/doctor-contract-bEQXIXyP.js +158 -0
- package/dist/{drive-hSi_Utp0.js → drive-B0JkoiRj.js} +318 -41
- package/dist/{media-CEUraFOR.js → media-BjSy8fiy.js} +298 -182
- package/dist/{monitor-DngbaA6a.js → monitor-CeHCfROt.js} +3 -3
- package/dist/{monitor.account-BnTWCw55.js → monitor.account-DwdqexLU.js} +237 -158
- package/dist/{monitor.startup-CqH2tiJq.js → monitor.startup-BGErejNH.js} +1 -1
- package/dist/{probe-p3POS2RN.js → probe-DVpy58s0.js} +2 -2
- package/dist/security-audit-D6Fz2h6p.js +23 -0
- package/dist/{send-result-B9_BpUPx.js → send-result-DEAycmOk.js} +18 -9
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/{session-conversation-BksWrfzm.js → session-conversation-DFCIvQK-.js} +1 -1
- package/dist/session-key-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/{subagent-hooks-Cx1cX7rW.js → subagent-hooks-B867acTt.js} +2 -2
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{thread-bindings-N3wkgkIN.js → thread-bindings-Itvfmx6_.js} +6 -3
- package/openclaw.plugin.json +143 -1
- package/package.json +4 -4
- package/skills/feishu-doc/SKILL.md +20 -195
- package/skills/feishu-doc/references/block-types.md +9 -14
- package/skills/feishu-drive/SKILL.md +16 -102
- package/skills/feishu-perm/SKILL.md +10 -110
- package/skills/feishu-wiki/SKILL.md +13 -109
- package/dist/accounts-u9X5Wsan.js +0 -469
- package/dist/doctor-contract-BiD9tyIv.js +0 -102
- package/dist/security-audit-D7WK_BHh.js +0 -11
|
@@ -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-bEQXIXyP.js";
|
|
2
2
|
export { legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { asObjectRecord, defineChannelAliasMigration, defineKeyMoveMigration, hasLegacyAccountStreamingAliases, normalizeChannelConfigEntries } from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
2
|
+
//#region extensions/feishu/src/webhook-path.ts
|
|
3
|
+
const DEFAULT_FEISHU_WEBHOOK_PATH = "/feishu/events";
|
|
4
|
+
/** Normalize trusted configuration only; incoming request targets must remain unmodified. */
|
|
5
|
+
function normalizeFeishuWebhookPath(value) {
|
|
6
|
+
const configured = value?.trim();
|
|
7
|
+
if (!configured) return DEFAULT_FEISHU_WEBHOOK_PATH;
|
|
8
|
+
try {
|
|
9
|
+
const parsed = new URL(configured, "http://localhost");
|
|
10
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
11
|
+
const emptyQuery = !parsed.search && parsed.href.endsWith("?") && !configured.includes("#") ? "?" : "";
|
|
12
|
+
return `${parsed.pathname}${parsed.search}${emptyQuery}`;
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region extensions/feishu/src/doctor-contract.ts
|
|
19
|
+
const streamingAliasMigration = defineChannelAliasMigration({
|
|
20
|
+
channelId: "feishu",
|
|
21
|
+
streaming: { defaultMode: "partial" },
|
|
22
|
+
accountStreamingReplacesRoot: true
|
|
23
|
+
});
|
|
24
|
+
const LEGACY_COALESCE_FIELDS = [
|
|
25
|
+
"enabled",
|
|
26
|
+
"minDelayMs",
|
|
27
|
+
"maxDelayMs"
|
|
28
|
+
];
|
|
29
|
+
const LEGACY_HEARTBEAT_FIELDS = ["visibility", "intervalMs"];
|
|
30
|
+
const toolsBaseMigration = defineKeyMoveMigration({
|
|
31
|
+
from: ["tools", "base"],
|
|
32
|
+
to: ["tools", "bitable"],
|
|
33
|
+
match: (value) => typeof value === "boolean",
|
|
34
|
+
sourceOwn: false
|
|
35
|
+
});
|
|
36
|
+
function sanitizeLegacyHeartbeatFields(params) {
|
|
37
|
+
const heartbeat = asObjectRecord(params.entry.heartbeat);
|
|
38
|
+
if (!heartbeat || Object.keys(heartbeat).length > 0 && !LEGACY_HEARTBEAT_FIELDS.some((field) => Object.hasOwn(heartbeat, field))) return {
|
|
39
|
+
entry: params.entry,
|
|
40
|
+
changed: false
|
|
41
|
+
};
|
|
42
|
+
const next = { ...params.entry };
|
|
43
|
+
delete next.heartbeat;
|
|
44
|
+
params.changes.push(`Removed ${params.pathPrefix}.heartbeat (legacy Feishu fields were never read by runtime).`);
|
|
45
|
+
return {
|
|
46
|
+
entry: next,
|
|
47
|
+
changed: true
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function sanitizeLegacyCoalesceFields(params) {
|
|
51
|
+
const streaming = asObjectRecord(params.entry.streaming);
|
|
52
|
+
const block = asObjectRecord(streaming?.block);
|
|
53
|
+
const coalesce = asObjectRecord(block?.coalesce);
|
|
54
|
+
if (!streaming || !block || !coalesce) return {
|
|
55
|
+
entry: params.entry,
|
|
56
|
+
changed: false
|
|
57
|
+
};
|
|
58
|
+
const removed = LEGACY_COALESCE_FIELDS.filter((field) => coalesce[field] !== void 0);
|
|
59
|
+
if (removed.length === 0) return {
|
|
60
|
+
entry: params.entry,
|
|
61
|
+
changed: false
|
|
62
|
+
};
|
|
63
|
+
const nextCoalesce = { ...coalesce };
|
|
64
|
+
for (const field of removed) delete nextCoalesce[field];
|
|
65
|
+
params.changes.push(`Removed ${params.pathPrefix}.streaming.block.coalesce.{${removed.join(",")}} (legacy Feishu-only fields; block delivery reads minChars/maxChars/idleMs).`);
|
|
66
|
+
return {
|
|
67
|
+
entry: {
|
|
68
|
+
...params.entry,
|
|
69
|
+
streaming: {
|
|
70
|
+
...streaming,
|
|
71
|
+
block: {
|
|
72
|
+
...block,
|
|
73
|
+
coalesce: nextCoalesce
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
changed: true
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function hasLegacyWebhookPath(value) {
|
|
81
|
+
const path = asObjectRecord(value)?.webhookPath;
|
|
82
|
+
return typeof path === "string" && normalizeFeishuWebhookPath(path) !== path;
|
|
83
|
+
}
|
|
84
|
+
function normalizeLegacyWebhookPath(params) {
|
|
85
|
+
const path = params.entry.webhookPath;
|
|
86
|
+
if (typeof path !== "string") return {
|
|
87
|
+
entry: params.entry,
|
|
88
|
+
changed: false
|
|
89
|
+
};
|
|
90
|
+
const normalized = normalizeFeishuWebhookPath(path);
|
|
91
|
+
const canonical = normalized ?? "/feishu/events";
|
|
92
|
+
if (canonical === path) return {
|
|
93
|
+
entry: params.entry,
|
|
94
|
+
changed: false
|
|
95
|
+
};
|
|
96
|
+
params.changes.push(normalized === null ? `Reset invalid ${params.pathPrefix}.webhookPath to ${DEFAULT_FEISHU_WEBHOOK_PATH}.` : `Normalized ${params.pathPrefix}.webhookPath to its HTTP request path.`);
|
|
97
|
+
return {
|
|
98
|
+
entry: {
|
|
99
|
+
...params.entry,
|
|
100
|
+
webhookPath: canonical
|
|
101
|
+
},
|
|
102
|
+
changed: true
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function normalizeFeishuLegacyConfigEntries(cfg, changes) {
|
|
106
|
+
return normalizeChannelConfigEntries({
|
|
107
|
+
cfg,
|
|
108
|
+
channelId: "feishu",
|
|
109
|
+
changes,
|
|
110
|
+
normalizeEntry: (params) => {
|
|
111
|
+
const tools = toolsBaseMigration.normalize(params);
|
|
112
|
+
const coalesce = sanitizeLegacyCoalesceFields({
|
|
113
|
+
...params,
|
|
114
|
+
entry: tools.entry
|
|
115
|
+
});
|
|
116
|
+
const heartbeat = sanitizeLegacyHeartbeatFields({
|
|
117
|
+
...params,
|
|
118
|
+
entry: coalesce.entry
|
|
119
|
+
});
|
|
120
|
+
const webhook = normalizeLegacyWebhookPath({
|
|
121
|
+
...params,
|
|
122
|
+
entry: heartbeat.entry
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
entry: webhook.entry,
|
|
126
|
+
changed: tools.changed || coalesce.changed || heartbeat.changed || webhook.changed
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}).config;
|
|
130
|
+
}
|
|
131
|
+
const legacyConfigRules = [
|
|
132
|
+
...streamingAliasMigration.legacyConfigRules,
|
|
133
|
+
{
|
|
134
|
+
path: ["channels", "feishu"],
|
|
135
|
+
message: "channels.feishu[.accounts.<id>].webhookPath must be a canonical HTTP request path; run \"openclaw doctor --fix\".",
|
|
136
|
+
match: (value) => {
|
|
137
|
+
const entry = asObjectRecord(value);
|
|
138
|
+
return hasLegacyWebhookPath(entry) || hasLegacyAccountStreamingAliases(entry?.accounts, hasLegacyWebhookPath);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
path: ["channels", "feishu"],
|
|
143
|
+
message: "channels.feishu[.accounts.<id>].tools.base is legacy; use tools.bitable. Run \"openclaw doctor --fix\".",
|
|
144
|
+
match: (value) => {
|
|
145
|
+
const entry = asObjectRecord(value);
|
|
146
|
+
return toolsBaseMigration.hasLegacy(entry) || hasLegacyAccountStreamingAliases(entry?.accounts, toolsBaseMigration.hasLegacy);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
];
|
|
150
|
+
function normalizeCompatibilityConfig({ cfg }) {
|
|
151
|
+
const aliases = streamingAliasMigration.normalizeChannelConfig({ cfg });
|
|
152
|
+
return {
|
|
153
|
+
config: normalizeFeishuLegacyConfigEntries(aliases.config, aliases.changes),
|
|
154
|
+
changes: aliases.changes
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
export { normalizeFeishuWebhookPath as i, normalizeCompatibilityConfig as n, DEFAULT_FEISHU_WEBHOOK_PATH as r, legacyConfigRules as t };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { a as resolveDefaultFeishuAccountId,
|
|
2
|
-
import { A as resolveFeishuChatType, b as resolveFeishuChatReadPreliminaryAuthorization, h as authorizeFeishuChatMemberRead, m as assertFeishuChatReadAllowed } from "./send-result-
|
|
3
|
-
import { r as createFeishuClient } from "./client-
|
|
1
|
+
import { a as resolveDefaultFeishuAccountId, i as listFeishuAccountIds, o as resolveFeishuAccount, r as listEnabledFeishuAccounts, s as resolveFeishuRuntimeAccount } from "./accounts-CCCdMen2.js";
|
|
2
|
+
import { A as resolveFeishuChatType, b as resolveFeishuChatReadPreliminaryAuthorization, h as authorizeFeishuChatMemberRead, m as assertFeishuChatReadAllowed } from "./send-result-DEAycmOk.js";
|
|
3
|
+
import { r as createFeishuClient } from "./client-Hp7uo_cl.js";
|
|
4
4
|
import { optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
|
|
5
|
-
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
-
import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
7
5
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
8
|
-
import {
|
|
6
|
+
import { isRecord, normalizeOptionalString, normalizeStringEntries, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
|
+
import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
9
8
|
import { Type } from "typebox";
|
|
9
|
+
import { wrapExternalContent } from "openclaw/plugin-sdk/security-runtime";
|
|
10
|
+
import { jsonResult } from "openclaw/plugin-sdk/tool-results";
|
|
10
11
|
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
|
12
|
+
import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
|
|
11
13
|
//#region extensions/feishu/src/tools-config.ts
|
|
12
14
|
/**
|
|
13
15
|
* Default tool configuration.
|
|
@@ -99,6 +101,26 @@ function resolveAnyEnabledFeishuToolsConfig(accounts) {
|
|
|
99
101
|
}
|
|
100
102
|
return merged;
|
|
101
103
|
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region extensions/feishu/src/tool-result.ts
|
|
106
|
+
function feishuExternalToolResult(details) {
|
|
107
|
+
return {
|
|
108
|
+
content: [{
|
|
109
|
+
type: "text",
|
|
110
|
+
text: wrapExternalContent(JSON.stringify(details, null, 2), {
|
|
111
|
+
source: "api",
|
|
112
|
+
includeWarning: false
|
|
113
|
+
})
|
|
114
|
+
}],
|
|
115
|
+
details
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function unknownToolActionResult(action) {
|
|
119
|
+
return jsonResult({ error: `Unknown action: ${String(action)}` });
|
|
120
|
+
}
|
|
121
|
+
function toolExecutionErrorResult(error) {
|
|
122
|
+
return feishuExternalToolResult({ error: formatErrorMessage(error) });
|
|
123
|
+
}
|
|
102
124
|
const FeishuChatSchema = Type.Object({
|
|
103
125
|
action: Type.Enum([
|
|
104
126
|
"members",
|
|
@@ -125,6 +147,267 @@ const FeishuChatSchema = Type.Object({
|
|
|
125
147
|
}))
|
|
126
148
|
});
|
|
127
149
|
//#endregion
|
|
150
|
+
//#region extensions/feishu/src/comment-target.ts
|
|
151
|
+
const FEISHU_COMMENT_FILE_TYPES = [
|
|
152
|
+
"doc",
|
|
153
|
+
"docx",
|
|
154
|
+
"file",
|
|
155
|
+
"sheet",
|
|
156
|
+
"slides"
|
|
157
|
+
];
|
|
158
|
+
function normalizeCommentFileType(value) {
|
|
159
|
+
return typeof value === "string" && FEISHU_COMMENT_FILE_TYPES.includes(value) ? value : void 0;
|
|
160
|
+
}
|
|
161
|
+
function buildFeishuCommentTarget(params) {
|
|
162
|
+
return `comment:${params.fileType}:${params.fileToken}:${params.commentId}`;
|
|
163
|
+
}
|
|
164
|
+
function parseFeishuCommentTarget(raw) {
|
|
165
|
+
const trimmed = raw?.trim();
|
|
166
|
+
if (!trimmed?.startsWith("comment:")) return null;
|
|
167
|
+
const parts = trimmed.split(":");
|
|
168
|
+
if (parts.length !== 4) return null;
|
|
169
|
+
const fileType = normalizeCommentFileType(parts[1]);
|
|
170
|
+
const fileToken = parts[2]?.trim();
|
|
171
|
+
const commentId = parts[3]?.trim();
|
|
172
|
+
if (!fileType || !fileToken || !commentId) return null;
|
|
173
|
+
return {
|
|
174
|
+
fileType,
|
|
175
|
+
fileToken,
|
|
176
|
+
commentId
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region extensions/feishu/src/send-rate-limit.ts
|
|
181
|
+
const FEISHU_SEND_RATE_LIMIT_CODES = /* @__PURE__ */ new Set([230020, 11232]);
|
|
182
|
+
function getFeishuSendRateLimitCode(error) {
|
|
183
|
+
if (!isRecord(error)) return;
|
|
184
|
+
const response = isRecord(error.response) ? error.response : void 0;
|
|
185
|
+
if (response?.status === 429) return 429;
|
|
186
|
+
const code = (isRecord(response?.data) ? response.data : void 0)?.code;
|
|
187
|
+
return typeof code === "number" && FEISHU_SEND_RATE_LIMIT_CODES.has(code) ? code : void 0;
|
|
188
|
+
}
|
|
189
|
+
function getFeishuSendRateLimitCodeFromResponse(response) {
|
|
190
|
+
if (!isRecord(response)) return;
|
|
191
|
+
const code = response.code;
|
|
192
|
+
return typeof code === "number" && FEISHU_SEND_RATE_LIMIT_CODES.has(code) ? code : void 0;
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region extensions/feishu/src/comment-shared.ts
|
|
196
|
+
function encodeQuery(params) {
|
|
197
|
+
const query = new URLSearchParams();
|
|
198
|
+
for (const [key, value] of Object.entries(params)) {
|
|
199
|
+
const trimmed = value?.trim();
|
|
200
|
+
if (trimmed) query.set(key, trimmed);
|
|
201
|
+
}
|
|
202
|
+
const queryString = query.toString();
|
|
203
|
+
return queryString ? `?${queryString}` : "";
|
|
204
|
+
}
|
|
205
|
+
function formatFeishuApiError(error, options = {}) {
|
|
206
|
+
if (!isRecord(error)) return typeof error === "string" ? error : JSON.stringify(error);
|
|
207
|
+
const config = isRecord(error.config) ? error.config : void 0;
|
|
208
|
+
const response = isRecord(error.response) ? error.response : void 0;
|
|
209
|
+
const responseData = isRecord(response?.data) ? response?.data : void 0;
|
|
210
|
+
const feishuLogId = readStringValue(responseData?.log_id) || (options.includeNestedErrorLogId ? readStringValue(isRecord(responseData?.error) ? responseData.error.log_id : void 0) : void 0);
|
|
211
|
+
const nestedError = isRecord(responseData?.error) ? responseData.error : void 0;
|
|
212
|
+
return JSON.stringify({
|
|
213
|
+
message: typeof error.message === "string" ? error.message : typeof error === "string" ? error : JSON.stringify(error),
|
|
214
|
+
code: readStringValue(error.code),
|
|
215
|
+
method: readStringValue(config?.method),
|
|
216
|
+
url: readStringValue(config?.url),
|
|
217
|
+
...options.includeConfigParams ? { params: config?.params } : {},
|
|
218
|
+
http_status: typeof response?.status === "number" ? response.status : void 0,
|
|
219
|
+
feishu_code: typeof responseData?.code === "number" ? responseData.code : readStringValue(responseData?.code),
|
|
220
|
+
feishu_msg: readStringValue(responseData?.msg),
|
|
221
|
+
feishu_log_id: feishuLogId,
|
|
222
|
+
feishu_troubleshooter: readStringValue(responseData?.troubleshooter) || readStringValue(nestedError?.troubleshooter)
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function formatFeishuApiFailure(error, errorPrefix, options = {}) {
|
|
226
|
+
return `${errorPrefix}: ${formatFeishuApiError(error, options) || "unknown error"}`;
|
|
227
|
+
}
|
|
228
|
+
function createFeishuApiError(error, errorPrefix, options = {}) {
|
|
229
|
+
return new Error(formatFeishuApiFailure(error, errorPrefix, options), { cause: error });
|
|
230
|
+
}
|
|
231
|
+
const FEISHU_SEND_RETRY_BASE_MS = 500;
|
|
232
|
+
async function requestFeishuApi(request, errorPrefix, options = {}) {
|
|
233
|
+
try {
|
|
234
|
+
return await retryAsync(async () => {
|
|
235
|
+
const result = await request();
|
|
236
|
+
const fulfilledRateLimit = getFeishuSendRateLimitCodeFromResponse(result);
|
|
237
|
+
if (fulfilledRateLimit !== void 0) throw Object.assign(/* @__PURE__ */ new Error(`Request fulfilled with rate-limit code ${fulfilledRateLimit}`), { response: {
|
|
238
|
+
status: 200,
|
|
239
|
+
data: result
|
|
240
|
+
} });
|
|
241
|
+
return result;
|
|
242
|
+
}, {
|
|
243
|
+
attempts: 3,
|
|
244
|
+
minDelayMs: options.retryDelayMs ?? FEISHU_SEND_RETRY_BASE_MS,
|
|
245
|
+
shouldRetry: (error) => getFeishuSendRateLimitCode(error) !== void 0
|
|
246
|
+
});
|
|
247
|
+
} catch (error) {
|
|
248
|
+
throw createFeishuApiError(error, errorPrefix, options);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function readDocsLinkUrl(element) {
|
|
252
|
+
const docsLink = isRecord(element.docs_link) ? element.docs_link : void 0;
|
|
253
|
+
return normalizeOptionalString(docsLink?.url) || normalizeOptionalString(docsLink?.link) || normalizeOptionalString(element.url) || normalizeOptionalString(element.link) || void 0;
|
|
254
|
+
}
|
|
255
|
+
function readMentionUserId(element) {
|
|
256
|
+
const mention = isRecord(element.mention) ? element.mention : void 0;
|
|
257
|
+
return normalizeOptionalString((isRecord(element.person) ? element.person : void 0)?.user_id) || normalizeOptionalString(mention?.user_id) || normalizeOptionalString(mention?.open_id) || normalizeOptionalString(element.mention_user) || normalizeOptionalString(element.user_id) || void 0;
|
|
258
|
+
}
|
|
259
|
+
function readMentionDisplayText(element, userId) {
|
|
260
|
+
const mention = isRecord(element.mention) ? element.mention : void 0;
|
|
261
|
+
const mentionName = normalizeOptionalString(mention?.name) || normalizeOptionalString(mention?.display_name) || normalizeOptionalString(element.name);
|
|
262
|
+
return mentionName ? `@${mentionName}` : `@${userId}`;
|
|
263
|
+
}
|
|
264
|
+
function normalizeCommentText(parts) {
|
|
265
|
+
return parts.join("").trim() || void 0;
|
|
266
|
+
}
|
|
267
|
+
function normalizeCommentSemanticText(parts) {
|
|
268
|
+
return parts.join("").replace(/\s+/g, " ").trim() || void 0;
|
|
269
|
+
}
|
|
270
|
+
function readElementTextPreservingWhitespace(element) {
|
|
271
|
+
return (isRecord(element.text_run) ? readStringValue(element.text_run.content) || readStringValue(element.text_run.text) : void 0) || readStringValue(element.text) || readStringValue(element.content) || readStringValue(element.name) || void 0;
|
|
272
|
+
}
|
|
273
|
+
const FEISHU_LINK_TOKEN_MIN_LENGTH = 22;
|
|
274
|
+
const FEISHU_LINK_TOKEN_MAX_LENGTH = 28;
|
|
275
|
+
const COMMENT_LINK_KIND_ALIASES = /* @__PURE__ */ new Map([
|
|
276
|
+
["doc", "doc"],
|
|
277
|
+
["docs", "doc"],
|
|
278
|
+
["docx", "docx"],
|
|
279
|
+
["sheet", "sheet"],
|
|
280
|
+
["sheets", "sheet"],
|
|
281
|
+
["slide", "slides"],
|
|
282
|
+
["slides", "slides"],
|
|
283
|
+
["file", "file"],
|
|
284
|
+
["files", "file"],
|
|
285
|
+
["wiki", "wiki"],
|
|
286
|
+
["mindnote", "mindnote"],
|
|
287
|
+
["mindnotes", "mindnote"],
|
|
288
|
+
["bitable", "bitable"],
|
|
289
|
+
["base", "base"]
|
|
290
|
+
]);
|
|
291
|
+
function isCommentFileType(value) {
|
|
292
|
+
return typeof value === "string" && FEISHU_COMMENT_FILE_TYPES.includes(value);
|
|
293
|
+
}
|
|
294
|
+
function isReasonableFeishuLinkToken(token) {
|
|
295
|
+
return typeof token === "string" && token.length >= FEISHU_LINK_TOKEN_MIN_LENGTH && token.length <= FEISHU_LINK_TOKEN_MAX_LENGTH;
|
|
296
|
+
}
|
|
297
|
+
function parseCommentLinkedDocumentPath(pathname) {
|
|
298
|
+
const segments = normalizeStringEntries(pathname.split("/"));
|
|
299
|
+
const offset = segments[0]?.toLowerCase() === "space" ? 1 : 0;
|
|
300
|
+
const kind = COMMENT_LINK_KIND_ALIASES.get(segments[offset]?.toLowerCase() ?? "");
|
|
301
|
+
const token = normalizeOptionalString(segments[offset + 1]);
|
|
302
|
+
if (!kind || !isReasonableFeishuLinkToken(token)) return null;
|
|
303
|
+
return {
|
|
304
|
+
urlKind: kind,
|
|
305
|
+
token
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function hasResolvedLinkedDocumentReference(link) {
|
|
309
|
+
return link.urlKind !== "unknown" && (Boolean(link.resolvedObjToken) || Boolean(link.wikiNodeToken));
|
|
310
|
+
}
|
|
311
|
+
function resolveCommentLinkedDocumentFromUrl(params) {
|
|
312
|
+
const link = {
|
|
313
|
+
rawUrl: params.rawUrl,
|
|
314
|
+
urlKind: "unknown"
|
|
315
|
+
};
|
|
316
|
+
try {
|
|
317
|
+
const parsedPath = parseCommentLinkedDocumentPath(new URL(params.rawUrl).pathname);
|
|
318
|
+
if (!parsedPath) return link;
|
|
319
|
+
const { urlKind, token } = parsedPath;
|
|
320
|
+
link.urlKind = urlKind;
|
|
321
|
+
if (urlKind === "wiki") {
|
|
322
|
+
link.urlKind = "wiki";
|
|
323
|
+
link.wikiNodeToken = token;
|
|
324
|
+
} else {
|
|
325
|
+
link.resolvedObjType = urlKind;
|
|
326
|
+
link.resolvedObjToken = token;
|
|
327
|
+
}
|
|
328
|
+
if (link.resolvedObjType && link.resolvedObjToken && isCommentFileType(link.resolvedObjType) && params.currentDocument?.fileType === link.resolvedObjType && params.currentDocument.fileToken === link.resolvedObjToken) link.isCurrentDocument = true;
|
|
329
|
+
else if (link.resolvedObjType && link.resolvedObjToken && isCommentFileType(link.resolvedObjType)) link.isCurrentDocument = false;
|
|
330
|
+
} catch {
|
|
331
|
+
return link;
|
|
332
|
+
}
|
|
333
|
+
return link;
|
|
334
|
+
}
|
|
335
|
+
function parseCommentContentElements(params) {
|
|
336
|
+
const elements = Array.isArray(params.elements) ? params.elements : [];
|
|
337
|
+
const plainTextParts = [];
|
|
338
|
+
const semanticTextParts = [];
|
|
339
|
+
const mentions = [];
|
|
340
|
+
const linkedDocuments = [];
|
|
341
|
+
const botIds = new Set(Array.from(params.botOpenIds ?? []).map((value) => normalizeOptionalString(value)).filter((value) => Boolean(value)));
|
|
342
|
+
const linkedDocumentKeys = /* @__PURE__ */ new Set();
|
|
343
|
+
let botMentioned = false;
|
|
344
|
+
for (const rawElement of elements) {
|
|
345
|
+
if (!isRecord(rawElement)) continue;
|
|
346
|
+
const element = rawElement;
|
|
347
|
+
const type = normalizeOptionalString(element.type);
|
|
348
|
+
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" ? (() => {
|
|
349
|
+
const userId = readMentionUserId(element);
|
|
350
|
+
return userId ? readMentionDisplayText(element, userId) : void 0;
|
|
351
|
+
})() : void 0) || readElementTextPreservingWhitespace(element) || void 0;
|
|
352
|
+
if (type === "mention" || type === "mention_user" || type === "person") {
|
|
353
|
+
const userId = readMentionUserId(element);
|
|
354
|
+
if (userId) {
|
|
355
|
+
const displayText = readMentionDisplayText(element, userId);
|
|
356
|
+
const isBotMention = botIds.has(userId);
|
|
357
|
+
mentions.push({
|
|
358
|
+
userId,
|
|
359
|
+
displayText,
|
|
360
|
+
isBotMention
|
|
361
|
+
});
|
|
362
|
+
plainTextParts.push(displayText);
|
|
363
|
+
if (!isBotMention) semanticTextParts.push(displayText);
|
|
364
|
+
else botMentioned = true;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (type === "docs_link" || type === "link") {
|
|
369
|
+
const rawUrl = readDocsLinkUrl(element);
|
|
370
|
+
if (rawUrl) {
|
|
371
|
+
plainTextParts.push(rawUrl);
|
|
372
|
+
semanticTextParts.push(rawUrl);
|
|
373
|
+
const linkedDocument = resolveCommentLinkedDocumentFromUrl({
|
|
374
|
+
rawUrl,
|
|
375
|
+
currentDocument: params.currentDocument
|
|
376
|
+
});
|
|
377
|
+
if (hasResolvedLinkedDocumentReference(linkedDocument)) {
|
|
378
|
+
const key = [
|
|
379
|
+
linkedDocument.rawUrl,
|
|
380
|
+
linkedDocument.urlKind,
|
|
381
|
+
linkedDocument.resolvedObjType,
|
|
382
|
+
linkedDocument.resolvedObjToken,
|
|
383
|
+
linkedDocument.wikiNodeToken
|
|
384
|
+
].join(":");
|
|
385
|
+
if (!linkedDocumentKeys.has(key)) {
|
|
386
|
+
linkedDocumentKeys.add(key);
|
|
387
|
+
linkedDocuments.push(linkedDocument);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (text) {
|
|
394
|
+
plainTextParts.push(text);
|
|
395
|
+
semanticTextParts.push(text);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
plainText: normalizeCommentText(plainTextParts),
|
|
400
|
+
semanticText: normalizeCommentSemanticText(semanticTextParts),
|
|
401
|
+
mentions,
|
|
402
|
+
linkedDocuments,
|
|
403
|
+
botMentioned
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function extractReplyText(reply) {
|
|
407
|
+
if (!reply || !isRecord(reply.content)) return;
|
|
408
|
+
return parseCommentContentElements({ elements: Array.isArray(reply.content.elements) ? reply.content.elements : [] }).plainText;
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
128
411
|
//#region extensions/feishu/src/chat.ts
|
|
129
412
|
function readChatPageSize(params) {
|
|
130
413
|
return readPositiveIntegerParam(params, "page_size", {
|
|
@@ -292,6 +575,7 @@ function registerFeishuChatTools(api) {
|
|
|
292
575
|
if (!resolveAnyEnabledFeishuToolsConfig(accounts).chat) return;
|
|
293
576
|
api.registerTool((toolContext) => ({
|
|
294
577
|
name: "feishu_chat",
|
|
578
|
+
resultContentSource: "network",
|
|
295
579
|
label: "Feishu Chat",
|
|
296
580
|
description: "Feishu chat operations. Actions: members, info, member_info",
|
|
297
581
|
parameters: FeishuChatSchema,
|
|
@@ -310,7 +594,7 @@ function registerFeishuChatTools(api) {
|
|
|
310
594
|
const client = createFeishuClient(account);
|
|
311
595
|
switch (p.action) {
|
|
312
596
|
case "members":
|
|
313
|
-
if (!p.chat_id) return
|
|
597
|
+
if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action members" });
|
|
314
598
|
{
|
|
315
599
|
const chat = await getAuthorizedFeishuChatInfo({
|
|
316
600
|
client,
|
|
@@ -327,12 +611,12 @@ function registerFeishuChatTools(api) {
|
|
|
327
611
|
ctx: toolContext,
|
|
328
612
|
memberIdType: p.member_id_type
|
|
329
613
|
});
|
|
330
|
-
if (authorization.kind === "direct") return
|
|
614
|
+
if (authorization.kind === "direct") return feishuExternalToolResult(buildFeishuDirectChatMembers(authorization));
|
|
331
615
|
}
|
|
332
|
-
return
|
|
616
|
+
return feishuExternalToolResult(await getChatMembers(client, p.chat_id, readChatPageSize(rawParams), p.page_token, p.member_id_type));
|
|
333
617
|
case "info":
|
|
334
|
-
if (!p.chat_id) return
|
|
335
|
-
return
|
|
618
|
+
if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action info" });
|
|
619
|
+
return feishuExternalToolResult(await getAuthorizedFeishuChatInfo({
|
|
336
620
|
client,
|
|
337
621
|
cfg,
|
|
338
622
|
account,
|
|
@@ -340,8 +624,8 @@ function registerFeishuChatTools(api) {
|
|
|
340
624
|
ctx: toolContext
|
|
341
625
|
}));
|
|
342
626
|
case "member_info":
|
|
343
|
-
if (!p.member_id) return
|
|
344
|
-
if (!p.chat_id) return
|
|
627
|
+
if (!p.member_id) return feishuExternalToolResult({ error: "member_id is required for action member_info" });
|
|
628
|
+
if (!p.chat_id) return feishuExternalToolResult({ error: "chat_id is required for action member_info" });
|
|
345
629
|
{
|
|
346
630
|
const chat = await getAuthorizedFeishuChatInfo({
|
|
347
631
|
client,
|
|
@@ -362,27 +646,19 @@ function registerFeishuChatTools(api) {
|
|
|
362
646
|
if (authorization.kind === "group") {
|
|
363
647
|
const memberIdType = p.member_id_type ?? "open_id";
|
|
364
648
|
await assertFeishuChatMember(client, p.chat_id, p.member_id, memberIdType);
|
|
365
|
-
return
|
|
649
|
+
return feishuExternalToolResult(await getFeishuMemberInfo(client, p.member_id, memberIdType));
|
|
366
650
|
}
|
|
367
|
-
return
|
|
651
|
+
return feishuExternalToolResult(await getFeishuMemberInfo(client, authorization.memberId, authorization.memberIdType));
|
|
368
652
|
}
|
|
369
|
-
default: return
|
|
653
|
+
default: return feishuExternalToolResult({ error: `Unknown action: ${String(p.action)}` });
|
|
370
654
|
}
|
|
371
655
|
} catch (err) {
|
|
372
|
-
return
|
|
656
|
+
return feishuExternalToolResult({ error: formatFeishuApiError(err, { includeNestedErrorLogId: true }) });
|
|
373
657
|
}
|
|
374
658
|
}
|
|
375
659
|
}), { name: "feishu_chat" });
|
|
376
660
|
}
|
|
377
661
|
//#endregion
|
|
378
|
-
//#region extensions/feishu/src/tool-result.ts
|
|
379
|
-
function unknownToolActionResult(action) {
|
|
380
|
-
return jsonResult({ error: `Unknown action: ${String(action)}` });
|
|
381
|
-
}
|
|
382
|
-
function toolExecutionErrorResult(error) {
|
|
383
|
-
return jsonResult({ error: formatErrorMessage(error) });
|
|
384
|
-
}
|
|
385
|
-
//#endregion
|
|
386
662
|
//#region extensions/feishu/src/comment-reaction.ts
|
|
387
663
|
const COMMENT_TYPING_REACTION_TYPE = "Typing";
|
|
388
664
|
const COMMENT_REACTION_TIMEOUT_MS = 3e4;
|
|
@@ -726,15 +1002,15 @@ function formatDriveApiError(error) {
|
|
|
726
1002
|
return formatFeishuApiError(error, { includeConfigParams: true });
|
|
727
1003
|
}
|
|
728
1004
|
function extractDriveApiErrorMeta(error) {
|
|
729
|
-
if (!isRecord
|
|
730
|
-
const response = isRecord
|
|
731
|
-
const responseData = isRecord
|
|
1005
|
+
if (!isRecord(error)) return { message: typeof error === "string" ? error : JSON.stringify(error) };
|
|
1006
|
+
const response = isRecord(error.response) ? error.response : void 0;
|
|
1007
|
+
const responseData = isRecord(response?.data) ? response?.data : void 0;
|
|
732
1008
|
return {
|
|
733
1009
|
message: typeof error.message === "string" ? error.message : typeof error === "string" ? error : JSON.stringify(error),
|
|
734
1010
|
httpStatus: typeof response?.status === "number" ? response.status : void 0,
|
|
735
|
-
feishuCode: typeof responseData?.code === "number" ? responseData.code :
|
|
736
|
-
feishuMsg:
|
|
737
|
-
feishuLogId:
|
|
1011
|
+
feishuCode: typeof responseData?.code === "number" ? responseData.code : readStringValue(responseData?.code),
|
|
1012
|
+
feishuMsg: readStringValue(responseData?.msg),
|
|
1013
|
+
feishuLogId: readStringValue(responseData?.log_id)
|
|
738
1014
|
};
|
|
739
1015
|
}
|
|
740
1016
|
function isReplyNotAllowedError(error) {
|
|
@@ -1013,6 +1289,7 @@ function registerFeishuDriveTools(api) {
|
|
|
1013
1289
|
const defaultAccountId = ctx.agentAccountId;
|
|
1014
1290
|
return {
|
|
1015
1291
|
name: "feishu_drive",
|
|
1292
|
+
resultContentSource: "network",
|
|
1016
1293
|
label: "Feishu Drive",
|
|
1017
1294
|
description: "Feishu cloud storage operations. Actions: list, info, create_folder, move, delete, list_comments, list_comment_replies, add_comment, reply_comment",
|
|
1018
1295
|
parameters: FeishuDriveSchema,
|
|
@@ -1029,21 +1306,21 @@ function registerFeishuDriveTools(api) {
|
|
|
1029
1306
|
}
|
|
1030
1307
|
});
|
|
1031
1308
|
switch (p.action) {
|
|
1032
|
-
case "list": return
|
|
1309
|
+
case "list": return feishuExternalToolResult(await listFolder(client, {
|
|
1033
1310
|
folder_token: p.folder_token,
|
|
1034
1311
|
page_size: p.page_size,
|
|
1035
1312
|
page_token: p.page_token
|
|
1036
1313
|
}));
|
|
1037
|
-
case "info": return
|
|
1038
|
-
case "create_folder": return
|
|
1039
|
-
case "move": return
|
|
1040
|
-
case "delete": return
|
|
1041
|
-
case "list_comments": return
|
|
1042
|
-
case "list_comment_replies": return
|
|
1314
|
+
case "info": return feishuExternalToolResult(await getFileInfo(client, p.file_token, p.type));
|
|
1315
|
+
case "create_folder": return feishuExternalToolResult(await createFolder(client, p.name, p.folder_token));
|
|
1316
|
+
case "move": return feishuExternalToolResult(await moveFile(client, p.file_token, p.type, p.folder_token));
|
|
1317
|
+
case "delete": return feishuExternalToolResult(await deleteFile(client, p.file_token, p.type));
|
|
1318
|
+
case "list_comments": return feishuExternalToolResult(await listComments(client, applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "list_comments")));
|
|
1319
|
+
case "list_comment_replies": return feishuExternalToolResult(await listCommentReplies(client, applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "list_comment_replies")));
|
|
1043
1320
|
case "add_comment": {
|
|
1044
1321
|
const resolved = applyAddCommentDefaults(applyAddCommentAmbientDefaults(p, ctx));
|
|
1045
1322
|
try {
|
|
1046
|
-
return
|
|
1323
|
+
return feishuExternalToolResult(await addComment(client, resolved));
|
|
1047
1324
|
} finally {
|
|
1048
1325
|
cleanupAmbientCommentTypingReaction({
|
|
1049
1326
|
client: getDriveInternalClient(client),
|
|
@@ -1054,7 +1331,7 @@ function registerFeishuDriveTools(api) {
|
|
|
1054
1331
|
case "reply_comment": {
|
|
1055
1332
|
const resolved = applyCommentFileTypeDefault(applyAmbientCommentDefaults(p, ctx), "reply_comment");
|
|
1056
1333
|
try {
|
|
1057
|
-
return
|
|
1334
|
+
return feishuExternalToolResult(await deliverCommentThreadText(client, resolved));
|
|
1058
1335
|
} finally {
|
|
1059
1336
|
cleanupAmbientCommentTypingReaction({
|
|
1060
1337
|
client: getDriveInternalClient(client),
|
|
@@ -1072,4 +1349,4 @@ function registerFeishuDriveTools(api) {
|
|
|
1072
1349
|
}, { name: "feishu_drive" });
|
|
1073
1350
|
}
|
|
1074
1351
|
//#endregion
|
|
1075
|
-
export {
|
|
1352
|
+
export { resolveFeishuToolAccount as C, resolveAnyEnabledFeishuToolsConfig as S, parseFeishuCommentTarget as _, assertFeishuChatMember as a, unknownToolActionResult as b, getChatMembers as c, encodeQuery as d, extractReplyText as f, normalizeCommentFileType as g, buildFeishuCommentTarget as h, createCommentTypingReactionLifecycle as i, getFeishuMemberInfo as l, requestFeishuApi as m, registerFeishuDriveTools as n, buildFeishuDirectChatMembers as o, parseCommentContentElements as p, cleanupAmbientCommentTypingReaction as r, getChatInfo as s, deliverCommentThreadText as t, registerFeishuChatTools as u, feishuExternalToolResult as v, resolveToolsConfig as w, createFeishuToolClient as x, toolExecutionErrorResult as y };
|