@larksuite/openclaw-lark 2026.5.13 → 2026.5.20
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/package.json +3 -3
- package/src/card/reply-dispatcher.js +9 -3
- package/src/channel/interactive-dispatch.js +2 -1
- package/src/core/card-action-operator.d.ts +33 -0
- package/src/core/card-action-operator.js +30 -0
- package/src/core/lark-client.js +6 -3
- package/src/messaging/converters/video-chat.js +6 -2
- package/src/messaging/outbound/actions.js +10 -1
- package/src/tools/ask-user-question.js +2 -1
- package/src/tools/auto-auth.js +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larksuite/openclaw-lark",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.20",
|
|
4
4
|
"description": "OpenClaw Lark/Feishu channel plugin",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"node": ">=22"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@larksuiteoapi/node-sdk": "^1.
|
|
27
|
-
"@sinclair/typebox": "0.34.
|
|
26
|
+
"@larksuiteoapi/node-sdk": "^1.64.0",
|
|
27
|
+
"@sinclair/typebox": "0.34.49",
|
|
28
28
|
"image-size": "^2.0.2",
|
|
29
29
|
"undici-types": "^8.1.0",
|
|
30
30
|
"zod": "^4.3.6"
|
|
@@ -196,12 +196,13 @@ function createFeishuReplyDispatcher(params) {
|
|
|
196
196
|
}
|
|
197
197
|
// 提取文本和媒体 URL
|
|
198
198
|
const text = getVisiblePayloadText(payload);
|
|
199
|
+
const reasoningText = payload.isReasoning === true ? (payload.text ?? '') : '';
|
|
199
200
|
const payloadMediaUrls = payload.mediaUrls?.length
|
|
200
201
|
? payload.mediaUrls
|
|
201
202
|
: payload.mediaUrl
|
|
202
203
|
? [payload.mediaUrl]
|
|
203
204
|
: [];
|
|
204
|
-
if (!text.trim() && payloadMediaUrls.length === 0) {
|
|
205
|
+
if (!text.trim() && !reasoningText.trim() && payloadMediaUrls.length === 0) {
|
|
205
206
|
log.debug('deliver: empty text and no media, skipping');
|
|
206
207
|
return;
|
|
207
208
|
}
|
|
@@ -211,12 +212,17 @@ function createFeishuReplyDispatcher(params) {
|
|
|
211
212
|
await controller.onToolPayload(payload);
|
|
212
213
|
return;
|
|
213
214
|
}
|
|
214
|
-
|
|
215
|
+
const controllerText = reasoningText.trim() ? reasoningText : text;
|
|
216
|
+
if (controllerText.trim()) {
|
|
215
217
|
await controller.ensureCardCreated();
|
|
216
218
|
if (controller.isTerminated)
|
|
217
219
|
return;
|
|
218
220
|
if (controller.cardMessageId) {
|
|
219
|
-
|
|
221
|
+
if (payload.isReasoning === true) {
|
|
222
|
+
await controller.onReasoningStream({ ...payload, text: controllerText });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
await controller.onDeliver({ ...payload, text: controllerText });
|
|
220
226
|
return;
|
|
221
227
|
}
|
|
222
228
|
// Card creation failed — fall through to static delivery
|
|
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
16
16
|
exports.dispatchFeishuPluginInteractiveHandler = dispatchFeishuPluginInteractiveHandler;
|
|
17
17
|
// NOTE: This is the SDK-standard interactive pipeline.
|
|
18
18
|
const plugin_runtime_1 = require("openclaw/plugin-sdk/plugin-runtime");
|
|
19
|
+
const card_action_operator_1 = require("../core/card-action-operator.js");
|
|
19
20
|
const lark_logger_1 = require("../core/lark-logger.js");
|
|
20
21
|
const send_1 = require("../messaging/outbound/send.js");
|
|
21
22
|
const log = (0, lark_logger_1.larkLogger)('channel/interactive-dispatch');
|
|
@@ -29,7 +30,7 @@ function extractBasics(data) {
|
|
|
29
30
|
const openMessageId = ev.open_message_id ?? ev.context?.open_message_id;
|
|
30
31
|
return {
|
|
31
32
|
action: action.trim(),
|
|
32
|
-
senderOpenId: ev.operator
|
|
33
|
+
senderOpenId: (0, card_action_operator_1.resolveCardCallbackOperatorId)(ev.operator),
|
|
33
34
|
openChatId,
|
|
34
35
|
openMessageId,
|
|
35
36
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Card callback operator identity extraction.
|
|
6
|
+
*
|
|
7
|
+
* Feishu Schema 2 card callbacks may carry the operator identity under
|
|
8
|
+
* either `operator.open_id` (Schema 1 / default) or `operator.user_id`
|
|
9
|
+
* (Schema 2 when the user has no open_id in the app's tenant).
|
|
10
|
+
*
|
|
11
|
+
* This helper provides a single, consistent extraction point so that
|
|
12
|
+
* every card callback handler resolves the operator identity the same
|
|
13
|
+
* way. See openclaw/openclaw#71670 for the upstream Schema 2 change.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Minimal shape of the `operator` object in a Feishu card callback event.
|
|
17
|
+
* Both fields are optional because Schema 2 may omit `open_id` entirely.
|
|
18
|
+
*/
|
|
19
|
+
export interface CardCallbackOperator {
|
|
20
|
+
open_id?: string;
|
|
21
|
+
user_id?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extract the operator's identity from a Feishu card callback event.
|
|
25
|
+
*
|
|
26
|
+
* Prefers `open_id` (the stable per-app user identifier) and falls back
|
|
27
|
+
* to `user_id` when `open_id` is absent or empty — this is the Schema 2 path.
|
|
28
|
+
*
|
|
29
|
+
* @param operator - The `operator` field from the card callback payload.
|
|
30
|
+
* @returns The resolved operator identifier, or `undefined` when neither
|
|
31
|
+
* field is present.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveCardCallbackOperatorId(operator: CardCallbackOperator | undefined): string | undefined;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*
|
|
6
|
+
* Card callback operator identity extraction.
|
|
7
|
+
*
|
|
8
|
+
* Feishu Schema 2 card callbacks may carry the operator identity under
|
|
9
|
+
* either `operator.open_id` (Schema 1 / default) or `operator.user_id`
|
|
10
|
+
* (Schema 2 when the user has no open_id in the app's tenant).
|
|
11
|
+
*
|
|
12
|
+
* This helper provides a single, consistent extraction point so that
|
|
13
|
+
* every card callback handler resolves the operator identity the same
|
|
14
|
+
* way. See openclaw/openclaw#71670 for the upstream Schema 2 change.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.resolveCardCallbackOperatorId = resolveCardCallbackOperatorId;
|
|
18
|
+
/**
|
|
19
|
+
* Extract the operator's identity from a Feishu card callback event.
|
|
20
|
+
*
|
|
21
|
+
* Prefers `open_id` (the stable per-app user identifier) and falls back
|
|
22
|
+
* to `user_id` when `open_id` is absent or empty — this is the Schema 2 path.
|
|
23
|
+
*
|
|
24
|
+
* @param operator - The `operator` field from the card callback payload.
|
|
25
|
+
* @returns The resolved operator identifier, or `undefined` when neither
|
|
26
|
+
* field is present.
|
|
27
|
+
*/
|
|
28
|
+
function resolveCardCallbackOperatorId(operator) {
|
|
29
|
+
return operator?.open_id || operator?.user_id;
|
|
30
|
+
}
|
package/src/core/lark-client.js
CHANGED
|
@@ -59,16 +59,19 @@ const runtime_store_1 = require("./runtime-store.js");
|
|
|
59
59
|
const version_1 = require("./version.js");
|
|
60
60
|
const log = (0, lark_logger_1.larkLogger)('core/lark-client');
|
|
61
61
|
// ---------------------------------------------------------------------------
|
|
62
|
-
//
|
|
62
|
+
// Inject User-Agent into all Feishu SDK requests
|
|
63
63
|
// ---------------------------------------------------------------------------
|
|
64
64
|
const GLOBAL_LARK_USER_AGENT_KEY = 'LARK_USER_AGENT';
|
|
65
65
|
function installGlobalUserAgent() {
|
|
66
|
-
// node-sdk
|
|
66
|
+
// node-sdk's built-in interceptor reads global.LARK_USER_AGENT to override User-Agent
|
|
67
67
|
globalThis[GLOBAL_LARK_USER_AGENT_KEY] = (0, version_1.getUserAgent)();
|
|
68
68
|
}
|
|
69
69
|
installGlobalUserAgent();
|
|
70
|
+
// Disable axios auto-proxy to prevent HTTP_PROXY env vars from corrupting request URLs.
|
|
71
|
+
// Proxy routing is managed centrally by OpenClaw core's global-agent.
|
|
72
|
+
Lark.defaultHttpInstance.defaults.proxy = false;
|
|
70
73
|
Lark.defaultHttpInstance.interceptors.request.handlers = [];
|
|
71
|
-
//
|
|
74
|
+
// Inject User-Agent header into all HTTP requests via interceptor
|
|
72
75
|
Lark.defaultHttpInstance.interceptors.request.use((req) => {
|
|
73
76
|
if (req.headers) {
|
|
74
77
|
req.headers['User-Agent'] = (0, version_1.getUserAgent)();
|
|
@@ -11,12 +11,16 @@ const utils_1 = require("./utils.js");
|
|
|
11
11
|
const convertVideoChat = (raw) => {
|
|
12
12
|
const parsed = (0, utils_1.safeParse)(raw);
|
|
13
13
|
const topic = parsed?.topic ?? '';
|
|
14
|
+
const meetingNo = parsed?.meet_number?.trim() ?? '';
|
|
14
15
|
const parts = [];
|
|
15
16
|
if (topic) {
|
|
16
|
-
parts.push(
|
|
17
|
+
parts.push(`Topic: ${topic}`);
|
|
17
18
|
}
|
|
18
19
|
if (parsed?.start_time) {
|
|
19
|
-
parts.push(
|
|
20
|
+
parts.push(`Start time: ${(0, utils_1.millisToDatetime)(parsed.start_time)}`);
|
|
21
|
+
}
|
|
22
|
+
if (meetingNo) {
|
|
23
|
+
parts.push(`Meeting number: ${meetingNo}`);
|
|
20
24
|
}
|
|
21
25
|
const inner = parts.join('\n') || '[video chat]';
|
|
22
26
|
return {
|
|
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
exports.feishuMessageActions = void 0;
|
|
18
18
|
const tool_send_1 = require("openclaw/plugin-sdk/tool-send");
|
|
19
19
|
const param_readers_1 = require("openclaw/plugin-sdk/param-readers");
|
|
20
|
+
const typebox_1 = require("@sinclair/typebox");
|
|
20
21
|
const sdk_compat_1 = require("../../core/sdk-compat.js");
|
|
21
22
|
const lark_client_1 = require("../../core/lark-client.js");
|
|
22
23
|
const accounts_1 = require("../../core/accounts.js");
|
|
@@ -25,6 +26,14 @@ const reactions_1 = require("./reactions.js");
|
|
|
25
26
|
const deliver_1 = require("./deliver.js");
|
|
26
27
|
const media_1 = require("./media.js");
|
|
27
28
|
const log = (0, lark_logger_1.larkLogger)('outbound/actions');
|
|
29
|
+
const FEISHU_SEND_TEXT_DESCRIPTION = 'Text to send as a separate Feishu message. During a normal Feishu streaming-card reply, do not call send just to repeat or finalize the same answer; return the final answer normally so the active card can be completed by the reply dispatcher. Use send only when the user explicitly needs an additional separate message.';
|
|
30
|
+
const FEISHU_MESSAGE_TOOL_SCHEMA = {
|
|
31
|
+
properties: {
|
|
32
|
+
message: typebox_1.Type.Optional(typebox_1.Type.String({ description: FEISHU_SEND_TEXT_DESCRIPTION })),
|
|
33
|
+
text: typebox_1.Type.Optional(typebox_1.Type.String({ description: FEISHU_SEND_TEXT_DESCRIPTION })),
|
|
34
|
+
},
|
|
35
|
+
visibility: 'current-channel',
|
|
36
|
+
};
|
|
28
37
|
// ---------------------------------------------------------------------------
|
|
29
38
|
// Helpers
|
|
30
39
|
// ---------------------------------------------------------------------------
|
|
@@ -131,7 +140,7 @@ exports.feishuMessageActions = {
|
|
|
131
140
|
return {
|
|
132
141
|
actions: Array.from(SUPPORTED_ACTIONS),
|
|
133
142
|
capabilities: ['cards'],
|
|
134
|
-
schema:
|
|
143
|
+
schema: FEISHU_MESSAGE_TOOL_SCHEMA,
|
|
135
144
|
};
|
|
136
145
|
},
|
|
137
146
|
supportsAction: ({ action }) => SUPPORTED_ACTIONS.has(action),
|
|
@@ -26,6 +26,7 @@ exports.registerAskUserQuestionTool = registerAskUserQuestionTool;
|
|
|
26
26
|
const node_crypto_1 = require("node:crypto");
|
|
27
27
|
const typebox_1 = require("@sinclair/typebox");
|
|
28
28
|
const lark_ticket_1 = require("../core/lark-ticket.js");
|
|
29
|
+
const card_action_operator_1 = require("../core/card-action-operator.js");
|
|
29
30
|
const lark_logger_1 = require("../core/lark-logger.js");
|
|
30
31
|
const cardkit_1 = require("../card/cardkit.js");
|
|
31
32
|
const chat_queue_1 = require("../channel/chat-queue.js");
|
|
@@ -164,7 +165,7 @@ function handleAskUserAction(data, _cfg, accountId) {
|
|
|
164
165
|
let openChatId;
|
|
165
166
|
try {
|
|
166
167
|
const event = data;
|
|
167
|
-
senderOpenId = event.operator
|
|
168
|
+
senderOpenId = (0, card_action_operator_1.resolveCardCallbackOperatorId)(event.operator);
|
|
168
169
|
// open_chat_id may be at top level or inside context (form submit callbacks use context)
|
|
169
170
|
openChatId = event.open_chat_id ?? event.context?.open_chat_id;
|
|
170
171
|
const actionTag = event.action?.tag;
|
package/src/tools/auto-auth.js
CHANGED
|
@@ -33,6 +33,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
33
33
|
exports.handleCardAction = handleCardAction;
|
|
34
34
|
exports.handleInvokeErrorWithAutoAuth = handleInvokeErrorWithAutoAuth;
|
|
35
35
|
const lark_ticket_1 = require("../core/lark-ticket.js");
|
|
36
|
+
const card_action_operator_1 = require("../core/card-action-operator.js");
|
|
36
37
|
const lark_logger_1 = require("../core/lark-logger.js");
|
|
37
38
|
const log = (0, lark_logger_1.larkLogger)('tools/auto-auth');
|
|
38
39
|
const api_error_1 = require("../core/api-error.js");
|
|
@@ -591,7 +592,7 @@ async function handleCardAction(data, cfg, accountId) {
|
|
|
591
592
|
const event = data;
|
|
592
593
|
action = event.action?.value?.action;
|
|
593
594
|
operationId = event.action?.value?.operation_id;
|
|
594
|
-
senderOpenId = event.operator
|
|
595
|
+
senderOpenId = (0, card_action_operator_1.resolveCardCallbackOperatorId)(event.operator);
|
|
595
596
|
}
|
|
596
597
|
catch {
|
|
597
598
|
return;
|