pi-remote-feishu 0.1.0
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/ARCHITECTURE.md +837 -0
- package/ARCHITECTURE.zh-CN.md +704 -0
- package/README.md +118 -0
- package/dist/attachments/mime.d.ts +11 -0
- package/dist/attachments/mime.js +27 -0
- package/dist/attachments/mime.js.map +1 -0
- package/dist/attachments/processor.d.ts +29 -0
- package/dist/attachments/processor.js +48 -0
- package/dist/attachments/processor.js.map +1 -0
- package/dist/attachments/temp-files.d.ts +16 -0
- package/dist/attachments/temp-files.js +32 -0
- package/dist/attachments/temp-files.js.map +1 -0
- package/dist/bin/pi-remote-feishu.d.ts +2 -0
- package/dist/bin/pi-remote-feishu.js +359 -0
- package/dist/bin/pi-remote-feishu.js.map +1 -0
- package/dist/bridge/card-actions.d.ts +13 -0
- package/dist/bridge/card-actions.js +156 -0
- package/dist/bridge/card-actions.js.map +1 -0
- package/dist/bridge/conversation-router.d.ts +21 -0
- package/dist/bridge/conversation-router.js +65 -0
- package/dist/bridge/conversation-router.js.map +1 -0
- package/dist/bridge/message-handler.d.ts +22 -0
- package/dist/bridge/message-handler.js +218 -0
- package/dist/bridge/message-handler.js.map +1 -0
- package/dist/bridge/message-normalizer.d.ts +17 -0
- package/dist/bridge/message-normalizer.js +81 -0
- package/dist/bridge/message-normalizer.js.map +1 -0
- package/dist/bridge/prompt-queue.d.ts +20 -0
- package/dist/bridge/prompt-queue.js +39 -0
- package/dist/bridge/prompt-queue.js.map +1 -0
- package/dist/bridge/runtime-host.d.ts +45 -0
- package/dist/bridge/runtime-host.js +133 -0
- package/dist/bridge/runtime-host.js.map +1 -0
- package/dist/bridge/session-host-manager.d.ts +59 -0
- package/dist/bridge/session-host-manager.js +143 -0
- package/dist/bridge/session-host-manager.js.map +1 -0
- package/dist/bridge/stream-renderer.d.ts +50 -0
- package/dist/bridge/stream-renderer.js +108 -0
- package/dist/bridge/stream-renderer.js.map +1 -0
- package/dist/bridge/ui-context.d.ts +19 -0
- package/dist/bridge/ui-context.js +150 -0
- package/dist/bridge/ui-context.js.map +1 -0
- package/dist/cards/common.d.ts +33 -0
- package/dist/cards/common.js +84 -0
- package/dist/cards/common.js.map +1 -0
- package/dist/cards/help.d.ts +11 -0
- package/dist/cards/help.js +31 -0
- package/dist/cards/help.js.map +1 -0
- package/dist/cards/models.d.ts +24 -0
- package/dist/cards/models.js +46 -0
- package/dist/cards/models.js.map +1 -0
- package/dist/cards/permission.d.ts +9 -0
- package/dist/cards/permission.js +17 -0
- package/dist/cards/permission.js.map +1 -0
- package/dist/cards/sessions.d.ts +11 -0
- package/dist/cards/sessions.js +54 -0
- package/dist/cards/sessions.js.map +1 -0
- package/dist/cards/status.d.ts +15 -0
- package/dist/cards/status.js +20 -0
- package/dist/cards/status.js.map +1 -0
- package/dist/cards/stop.d.ts +16 -0
- package/dist/cards/stop.js +24 -0
- package/dist/cards/stop.js.map +1 -0
- package/dist/config/load-config.d.ts +19 -0
- package/dist/config/load-config.js +51 -0
- package/dist/config/load-config.js.map +1 -0
- package/dist/config/schema.d.ts +52 -0
- package/dist/config/schema.js +260 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/extensions/index.d.ts +7 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/lark-cli-guard.d.ts +12 -0
- package/dist/extensions/lark-cli-guard.js +75 -0
- package/dist/extensions/lark-cli-guard.js.map +1 -0
- package/dist/feishu/channel.d.ts +28 -0
- package/dist/feishu/channel.js +191 -0
- package/dist/feishu/channel.js.map +1 -0
- package/dist/feishu/context.d.ts +5 -0
- package/dist/feishu/context.js +19 -0
- package/dist/feishu/context.js.map +1 -0
- package/dist/feishu/webhook.d.ts +5 -0
- package/dist/feishu/webhook.js +8 -0
- package/dist/feishu/webhook.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/store/json-store.d.ts +25 -0
- package/dist/store/json-store.js +105 -0
- package/dist/store/json-store.js.map +1 -0
- package/dist/store/store.d.ts +7 -0
- package/dist/store/store.js +15 -0
- package/dist/store/store.js.map +1 -0
- package/dist/tools/send-file-to-chat.d.ts +26 -0
- package/dist/tools/send-file-to-chat.js +85 -0
- package/dist/tools/send-file-to-chat.js.map +1 -0
- package/dist/types.d.ts +300 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
- package/skills/lark-doc-cli/SKILL.md +56 -0
- package/skills/lark-im-readonly/SKILL.md +49 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================
|
|
4
|
+
* lark-cli 护栏
|
|
5
|
+
* ------------------------------------------------------------
|
|
6
|
+
* 防止模型通过 bash 使用 lark-cli 的 IM 写/收命令
|
|
7
|
+
* (发送、回复、转发、删除、更新、接收、监听等),
|
|
8
|
+
* 确保飞书会话内的消息回发走 pi-remote-feishu 自己的传输通道。
|
|
9
|
+
*
|
|
10
|
+
* 允许保留的能力:
|
|
11
|
+
* - lark-doc-cli:飞书文档读写
|
|
12
|
+
* - lark-im-readonly:只读的 IM 查询(查聊天、列成员、搜消息、下资源)
|
|
13
|
+
* ============================================================
|
|
14
|
+
*/
|
|
15
|
+
/** 被拦截的 lark-cli im 动作(+xxx 子命令名) */
|
|
16
|
+
const BLOCKED_LARK_IM_ACTIONS = [
|
|
17
|
+
"messages-send",
|
|
18
|
+
"messages-reply",
|
|
19
|
+
"messages-forward",
|
|
20
|
+
"messages-delete",
|
|
21
|
+
"messages-update",
|
|
22
|
+
"messages-receive",
|
|
23
|
+
"messages-listen",
|
|
24
|
+
"events-receive",
|
|
25
|
+
"events-listen",
|
|
26
|
+
];
|
|
27
|
+
/** 检测本机是否安装了 lark-cli(运行 `lark-cli --version`) */
|
|
28
|
+
export function isLarkCliInstalled() {
|
|
29
|
+
const result = spawnSync("lark-cli", ["--version"], {
|
|
30
|
+
stdio: "ignore",
|
|
31
|
+
shell: process.platform === "win32",
|
|
32
|
+
});
|
|
33
|
+
return result.status === 0;
|
|
34
|
+
}
|
|
35
|
+
/** 命令里是否包含 lark-cli im(含 npx 前缀的写法) */
|
|
36
|
+
function hasLarkImCommand(command) {
|
|
37
|
+
return /\blark-cli\s+im\b/.test(command) || /\bnpx\s+lark-cli\s+im\b/.test(command);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 判断一条 bash 命令是否需要被拦截:
|
|
41
|
+
* 仅当它调用 lark-cli im 且包含被禁动作时才拦截。
|
|
42
|
+
* 动作匹配使用 +action 或 action 两种写法均可(正则做了转义)。
|
|
43
|
+
*/
|
|
44
|
+
export function isBlockedLarkImCommand(command) {
|
|
45
|
+
if (!hasLarkImCommand(command))
|
|
46
|
+
return false;
|
|
47
|
+
return BLOCKED_LARK_IM_ACTIONS.some((action) => {
|
|
48
|
+
const escaped = action.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
49
|
+
return new RegExp(`(?:\\+|\\b)${escaped}\\b`).test(command);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** 系统提示词护栏:检测到 lark-cli 时注入(允许文档/只读 IM,禁止 IM 写/收) */
|
|
53
|
+
export const FEISHU_CLI_PROMPT_GUARD = `
|
|
54
|
+
## Feishu CLI Boundary
|
|
55
|
+
|
|
56
|
+
This session may expose Lark CLI skills for Feishu documents and read-only IM inspection.
|
|
57
|
+
|
|
58
|
+
- Use lark-doc-cli for Feishu document tasks such as fetching, creating, updating, and inserting media into docs.
|
|
59
|
+
- Use lark-im-readonly only for read-only IM tasks such as looking up chats, listing members, searching existing messages, or downloading message resources.
|
|
60
|
+
- Do not use lark-cli IM commands to send, reply, forward, delete, update, receive, or listen for messages.
|
|
61
|
+
- In a Feishu-triggered run, the pi-remote-feishu transport is the only normal reply channel back to the current chat.
|
|
62
|
+
- To return generated local files to the current Feishu chat, use send_file_to_chat instead of manually sending IM messages with lark-cli.
|
|
63
|
+
`;
|
|
64
|
+
/** 系统提示词护栏:未检测到 lark-cli 时注入 */
|
|
65
|
+
export const FEISHU_NO_CLI_PROMPT_GUARD = `
|
|
66
|
+
## Feishu CLI Boundary
|
|
67
|
+
|
|
68
|
+
The lark-cli executable was not detected when pi-remote-feishu loaded.
|
|
69
|
+
|
|
70
|
+
- Do not attempt to use lark-cli commands unless the user installs and authenticates lark-cli, then reloads Pi.
|
|
71
|
+
- Use pi-remote-feishu transport features for the current Feishu chat.
|
|
72
|
+
- To return generated local files to the current Feishu chat, use send_file_to_chat.
|
|
73
|
+
- If the user asks to read or edit a Feishu document and no SDK document tool is available yet, ask for the document link or token and explain that SDK document tooling is not implemented in this extension version.
|
|
74
|
+
`;
|
|
75
|
+
//# sourceMappingURL=lark-cli-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lark-cli-guard.js","sourceRoot":"","sources":["../../src/extensions/lark-cli-guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C;;;;;;;;;;;;GAYG;AAEH,qCAAqC;AACrC,MAAM,uBAAuB,GAAG;IAC9B,eAAe;IACf,gBAAgB;IAChB,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,kBAAkB;IAClB,iBAAiB;IACjB,gBAAgB;IAChB,eAAe;CACP,CAAC;AAEX,kDAAkD;AAClD,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE;QAClD,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;KACpC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED,uCAAuC;AACvC,SAAS,gBAAgB,CAAC,OAAe;IACvC,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACtF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;QAChE,OAAO,IAAI,MAAM,CAAC,cAAc,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;CAUtC,CAAC;AAEF,gCAAgC;AAChC,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;CASzC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FeishuChannel } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================
|
|
4
|
+
* 飞书通道实现
|
|
5
|
+
* ------------------------------------------------------------
|
|
6
|
+
* 封装 @larksuiteoapi/node-sdk 的 WebSocket 通道,向业务层暴露
|
|
7
|
+
* 统一、友好的 FeishuChannel 接口:
|
|
8
|
+
*
|
|
9
|
+
* - SDK 事件 -> 归一化后的 FeishuMessage / FeishuCardAction
|
|
10
|
+
* - 各种 send/stream/update/download 方法 -> Promise 化
|
|
11
|
+
*
|
|
12
|
+
* 注意:这里用 as unknown as RawLarkChannel 做窄接口化,
|
|
13
|
+
* 只声明我们实际用到的 SDK 能力,方便替换实现。
|
|
14
|
+
* ============================================================
|
|
15
|
+
*/
|
|
16
|
+
/** 创建飞书通道的选项 */
|
|
17
|
+
export interface CreateFeishuChannelOptions {
|
|
18
|
+
appId: string;
|
|
19
|
+
appSecret: string;
|
|
20
|
+
logLevel?: string;
|
|
21
|
+
/** 群聊是否要求 @机器人(由 SDK 侧 policy 处理) */
|
|
22
|
+
requireMention: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 创建飞书通道(FeishuChannel 的唯一实现)。
|
|
26
|
+
* 通过 createLarkChannel 建立 WebSocket 长连接,并包一层统一接口。
|
|
27
|
+
*/
|
|
28
|
+
export declare function createFeishuChannel(options: CreateFeishuChannelOptions): FeishuChannel;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
import { createLarkChannel, LoggerLevel } from "@larksuiteoapi/node-sdk";
|
|
4
|
+
/** 日志级别映射:字符串 -> SDK LoggerLevel */
|
|
5
|
+
const LOG_LEVELS = {
|
|
6
|
+
fatal: LoggerLevel.fatal,
|
|
7
|
+
error: LoggerLevel.error,
|
|
8
|
+
warn: LoggerLevel.warn,
|
|
9
|
+
info: LoggerLevel.info,
|
|
10
|
+
debug: LoggerLevel.debug,
|
|
11
|
+
trace: LoggerLevel.trace,
|
|
12
|
+
};
|
|
13
|
+
/* ---------- 字段读取辅助(兼容多种命名风格) ---------- */
|
|
14
|
+
function asRecord(value) {
|
|
15
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
16
|
+
}
|
|
17
|
+
function readString(record, key) {
|
|
18
|
+
const value = record[key];
|
|
19
|
+
return typeof value === "string" ? value : undefined;
|
|
20
|
+
}
|
|
21
|
+
function readBoolean(record, key) {
|
|
22
|
+
const value = record[key];
|
|
23
|
+
return typeof value === "boolean" ? value : undefined;
|
|
24
|
+
}
|
|
25
|
+
/* ---------- 事件归一化 ---------- */
|
|
26
|
+
/** 归一化消息资源列表:只保留有 fileKey 的资源 */
|
|
27
|
+
function normalizeResources(value) {
|
|
28
|
+
if (!Array.isArray(value))
|
|
29
|
+
return [];
|
|
30
|
+
return value.map((entry) => {
|
|
31
|
+
const record = asRecord(entry);
|
|
32
|
+
const fileName = readString(record, "fileName") ?? readString(record, "file_name");
|
|
33
|
+
return {
|
|
34
|
+
type: readString(record, "type") ?? "file",
|
|
35
|
+
fileKey: readString(record, "fileKey") ?? readString(record, "file_key") ?? "",
|
|
36
|
+
...(fileName ? { fileName } : {}),
|
|
37
|
+
};
|
|
38
|
+
}).filter((resource) => resource.fileKey.length > 0);
|
|
39
|
+
}
|
|
40
|
+
/** 把 SDK 原始消息事件归一化成 FeishuMessage */
|
|
41
|
+
function normalizeMessage(value) {
|
|
42
|
+
const record = asRecord(value);
|
|
43
|
+
const rawContentType = readString(record, "rawContentType") ?? readString(record, "messageType");
|
|
44
|
+
const chatTypeText = readString(record, "chatType") ?? readString(record, "chat_type") ?? rawContentType;
|
|
45
|
+
// p2p / private 视为私聊,其余(p2p_group 等)视为群聊
|
|
46
|
+
const chatType = chatTypeText === "p2p" || chatTypeText === "private" ? "private" : "group";
|
|
47
|
+
const sender = asRecord(record.sender);
|
|
48
|
+
const senderId = asRecord(sender.sender_id);
|
|
49
|
+
const mentions = Array.isArray(record.mentions) ? record.mentions : [];
|
|
50
|
+
const mentionedBot = readBoolean(record, "mentionedBot") ?? mentions.length > 0;
|
|
51
|
+
const senderName = readString(record, "senderName") ?? readString(sender, "sender_name");
|
|
52
|
+
const tenantKey = readString(record, "tenantKey") ?? readString(record, "tenant_key");
|
|
53
|
+
return {
|
|
54
|
+
messageId: readString(record, "messageId") ?? readString(record, "message_id") ?? "",
|
|
55
|
+
chatId: readString(record, "chatId") ?? readString(record, "chat_id") ?? "",
|
|
56
|
+
chatType,
|
|
57
|
+
userOpenId: readString(record, "userOpenId") ??
|
|
58
|
+
readString(record, "openId") ??
|
|
59
|
+
readString(senderId, "open_id") ??
|
|
60
|
+
readString(senderId, "user_id") ??
|
|
61
|
+
"unknown",
|
|
62
|
+
...(senderName ? { senderName } : {}),
|
|
63
|
+
...(tenantKey ? { tenantKey } : {}),
|
|
64
|
+
text: readString(record, "content") ?? readString(record, "text") ?? "",
|
|
65
|
+
mentionedBot,
|
|
66
|
+
resources: normalizeResources(record.resources),
|
|
67
|
+
raw: value,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** 把 SDK 原始卡片事件归一化成 FeishuCardAction */
|
|
71
|
+
function normalizeCardAction(value) {
|
|
72
|
+
const record = asRecord(value);
|
|
73
|
+
const event = asRecord(record.event);
|
|
74
|
+
const action = asRecord(record.action);
|
|
75
|
+
const operator = asRecord(record.operator);
|
|
76
|
+
const operatorId = asRecord(operator.operator_id);
|
|
77
|
+
const actionValue = asRecord(action.value ?? record.value);
|
|
78
|
+
const messageId = readString(record, "messageId") ?? readString(event, "message_id");
|
|
79
|
+
const userOpenId = readString(record, "userOpenId") ?? readString(operatorId, "open_id");
|
|
80
|
+
const token = readString(record, "token") ?? readString(event, "token");
|
|
81
|
+
return {
|
|
82
|
+
...(messageId ? { messageId } : {}),
|
|
83
|
+
chatId: readString(record, "chatId") ?? readString(event, "chat_id") ?? "",
|
|
84
|
+
...(userOpenId ? { userOpenId } : {}),
|
|
85
|
+
actionValue,
|
|
86
|
+
...(token ? { token } : {}),
|
|
87
|
+
raw: value,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** 归一化发送结果 */
|
|
91
|
+
function normalizeSendResult(result) {
|
|
92
|
+
return { messageId: result.messageId ?? result.message_id ?? "" };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 创建飞书通道(FeishuChannel 的唯一实现)。
|
|
96
|
+
* 通过 createLarkChannel 建立 WebSocket 长连接,并包一层统一接口。
|
|
97
|
+
*/
|
|
98
|
+
export function createFeishuChannel(options) {
|
|
99
|
+
const loggerLevel = LOG_LEVELS[options.logLevel?.toLowerCase() ?? ""] ?? LoggerLevel.warn;
|
|
100
|
+
const raw = createLarkChannel({
|
|
101
|
+
appId: options.appId,
|
|
102
|
+
appSecret: options.appSecret,
|
|
103
|
+
loggerLevel,
|
|
104
|
+
policy: { requireMention: options.requireMention, dmMode: "open" },
|
|
105
|
+
includeRawEvent: true,
|
|
106
|
+
safety: { chatQueue: { enabled: false } },
|
|
107
|
+
});
|
|
108
|
+
let connected = false;
|
|
109
|
+
return {
|
|
110
|
+
async connect() {
|
|
111
|
+
await raw.connect();
|
|
112
|
+
connected = true;
|
|
113
|
+
},
|
|
114
|
+
async disconnect() {
|
|
115
|
+
await raw.disconnect();
|
|
116
|
+
connected = false;
|
|
117
|
+
},
|
|
118
|
+
/** 注册消息回调(自动归一化) */
|
|
119
|
+
onMessage(handler) {
|
|
120
|
+
raw.on("message", (message) => handler(normalizeMessage(message)));
|
|
121
|
+
},
|
|
122
|
+
/** 注册卡片事件回调(自动归一化) */
|
|
123
|
+
onCardAction(handler) {
|
|
124
|
+
raw.on("cardAction", (event) => handler(normalizeCardAction(event)));
|
|
125
|
+
},
|
|
126
|
+
onError(handler) {
|
|
127
|
+
raw.on("error", (error) => handler(error instanceof Error ? error : new Error(String(error))));
|
|
128
|
+
},
|
|
129
|
+
async sendText(chatId, text, replyOptions) {
|
|
130
|
+
return normalizeSendResult(await raw.send(chatId, { text }, replyOptions));
|
|
131
|
+
},
|
|
132
|
+
async sendMarkdown(chatId, markdown, replyOptions) {
|
|
133
|
+
return normalizeSendResult(await raw.send(chatId, { markdown }, replyOptions));
|
|
134
|
+
},
|
|
135
|
+
async sendCard(chatId, card, replyOptions) {
|
|
136
|
+
return normalizeSendResult(await raw.send(chatId, { card }, replyOptions));
|
|
137
|
+
},
|
|
138
|
+
/** 流式发送 Markdown:producer 里不断 append 增量 */
|
|
139
|
+
async streamMarkdown(chatId, producer, replyOptions) {
|
|
140
|
+
await raw.stream(chatId, { markdown: producer }, replyOptions);
|
|
141
|
+
},
|
|
142
|
+
async updateCard(messageId, card) {
|
|
143
|
+
await raw.updateCard(messageId, card);
|
|
144
|
+
},
|
|
145
|
+
/** 用 token 更新卡片(token 来自卡片事件,无需 messageId) */
|
|
146
|
+
async updateCardByToken(token, card) {
|
|
147
|
+
await raw.rawClient.request({
|
|
148
|
+
url: "/open-apis/interactive/v1/card/update",
|
|
149
|
+
method: "POST",
|
|
150
|
+
data: { token, card },
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
/** 发送本地文件(发送前校验路径确实是文件) */
|
|
154
|
+
async sendFile(chatId, filePath, fileName) {
|
|
155
|
+
const statResult = await stat(filePath);
|
|
156
|
+
if (!statResult.isFile()) {
|
|
157
|
+
throw new Error("Path is not a file");
|
|
158
|
+
}
|
|
159
|
+
await raw.send(chatId, {
|
|
160
|
+
file: { source: filePath, fileName: fileName ?? basename(filePath) },
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
/** 发送本地图片 */
|
|
164
|
+
async sendImage(chatId, imagePath) {
|
|
165
|
+
const statResult = await stat(imagePath);
|
|
166
|
+
if (!statResult.isFile()) {
|
|
167
|
+
throw new Error("Path is not a file");
|
|
168
|
+
}
|
|
169
|
+
await raw.send(chatId, { image: { source: imagePath } });
|
|
170
|
+
},
|
|
171
|
+
/** 下载消息资源:分块读取可读流并拼成完整 Buffer */
|
|
172
|
+
async downloadResource(messageId, fileKey, type) {
|
|
173
|
+
const response = await raw.rawClient.im.v1.messageResource.get({
|
|
174
|
+
path: { message_id: messageId, file_key: fileKey },
|
|
175
|
+
params: { type },
|
|
176
|
+
});
|
|
177
|
+
const chunks = [];
|
|
178
|
+
for await (const chunk of response.getReadableStream()) {
|
|
179
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
180
|
+
}
|
|
181
|
+
return Buffer.concat(chunks);
|
|
182
|
+
},
|
|
183
|
+
get connected() {
|
|
184
|
+
return connected;
|
|
185
|
+
},
|
|
186
|
+
get botName() {
|
|
187
|
+
return raw.botIdentity?.name;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/feishu/channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FeishuRunContext } from "../types.js";
|
|
2
|
+
/** 读取当前调用链中的飞书上下文;不在飞书请求中则返回 undefined */
|
|
3
|
+
export declare function getFeishuContext(): FeishuRunContext | undefined;
|
|
4
|
+
/** 在指定上下文里执行 run(),整个 run 及其子异步调用都能读到该上下文 */
|
|
5
|
+
export declare function runWithFeishuContext<T>(context: FeishuRunContext, run: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
/**
|
|
3
|
+
* 飞书运行上下文存储:
|
|
4
|
+
* 使用 AsyncLocalStorage 在"一次飞书请求 -> Pi 生成"的整个异步调用链中
|
|
5
|
+
* 传递 FeishuRunContext(channel、chatId、sessionKey、config 等)。
|
|
6
|
+
*
|
|
7
|
+
* 这样 send_file_to_chat、ui.select 等能力可以在任意深度拿到
|
|
8
|
+
* 当前请求对应的飞书通道,而不必手动层层传参。
|
|
9
|
+
*/
|
|
10
|
+
const storage = new AsyncLocalStorage();
|
|
11
|
+
/** 读取当前调用链中的飞书上下文;不在飞书请求中则返回 undefined */
|
|
12
|
+
export function getFeishuContext() {
|
|
13
|
+
return storage.getStore();
|
|
14
|
+
}
|
|
15
|
+
/** 在指定上下文里执行 run(),整个 run 及其子异步调用都能读到该上下文 */
|
|
16
|
+
export async function runWithFeishuContext(context, run) {
|
|
17
|
+
return await storage.run(context, run);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/feishu/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAoB,CAAC;AAE1D,0CAA0C;AAC1C,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAI,OAAyB,EAAE,GAAqB;IAC5F,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/feishu/webhook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,gCAAgC;IAC9C,OAAO,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;AAChG,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 公共入口:对外导出所有可供外部复用的 API。
|
|
3
|
+
* 当其他项目以 npm 包方式引入 pi-remote-feishu 时,从这里 import。
|
|
4
|
+
*/
|
|
5
|
+
/** 卡片事件处理(stop 停止、model 切换模型、session 会话管理等) */
|
|
6
|
+
export { handleCardAction } from "./bridge/card-actions.js";
|
|
7
|
+
/** 会话路由:判断消息是否被接受,并计算 sessionKey */
|
|
8
|
+
export { routeConversation, buildSessionKey } from "./bridge/conversation-router.js";
|
|
9
|
+
/** 飞书消息总入口:路由 -> 附件处理 -> 归一化 -> 命令/生成 */
|
|
10
|
+
export { handleFeishuMessage } from "./bridge/message-handler.js";
|
|
11
|
+
/** 把飞书消息归一化成给 Pi 的输入(含群聊上下文、附件说明) */
|
|
12
|
+
export { normalizeMessage } from "./bridge/message-normalizer.js";
|
|
13
|
+
/** 提示词队列:保证同一会话的消息串行处理 */
|
|
14
|
+
export { PromptQueue } from "./bridge/prompt-queue.js";
|
|
15
|
+
/** 会话宿主管理器:按 sessionKey 管理 Pi 运行时实例 */
|
|
16
|
+
export { SessionHostManager } from "./bridge/session-host-manager.js";
|
|
17
|
+
/** 飞书 UI 桥接:把 Pi 的 ui.select/confirm 转成飞书权限卡片 */
|
|
18
|
+
export { createFeishuUIContext, resolvePermissionCardAction } from "./bridge/ui-context.js";
|
|
19
|
+
/** 配置加载:合并 环境变量 + 配置文件 + CLI 参数 */
|
|
20
|
+
export { loadConfig } from "./config/load-config.js";
|
|
21
|
+
/** 创建飞书通道(封装飞书 WebSocket SDK) */
|
|
22
|
+
export { createFeishuChannel } from "./feishu/channel.js";
|
|
23
|
+
/** 飞书运行上下文(AsyncLocalStorage 存取) */
|
|
24
|
+
export { getFeishuContext, runWithFeishuContext } from "./feishu/context.js";
|
|
25
|
+
/** JSON 文件存储:会话映射持久化 */
|
|
26
|
+
export { JsonFeishuStore } from "./store/json-store.js";
|
|
27
|
+
/** 生成 send_file_to_chat 工具,让 Pi 可以把本地文件回传到当前飞书聊天 */
|
|
28
|
+
export { createSendFileToChatTool } from "./tools/send-file-to-chat.js";
|
|
29
|
+
/** 导出全部公共类型 */
|
|
30
|
+
export type * from "./types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 公共入口:对外导出所有可供外部复用的 API。
|
|
3
|
+
* 当其他项目以 npm 包方式引入 pi-remote-feishu 时,从这里 import。
|
|
4
|
+
*/
|
|
5
|
+
/** 卡片事件处理(stop 停止、model 切换模型、session 会话管理等) */
|
|
6
|
+
export { handleCardAction } from "./bridge/card-actions.js";
|
|
7
|
+
/** 会话路由:判断消息是否被接受,并计算 sessionKey */
|
|
8
|
+
export { routeConversation, buildSessionKey } from "./bridge/conversation-router.js";
|
|
9
|
+
/** 飞书消息总入口:路由 -> 附件处理 -> 归一化 -> 命令/生成 */
|
|
10
|
+
export { handleFeishuMessage } from "./bridge/message-handler.js";
|
|
11
|
+
/** 把飞书消息归一化成给 Pi 的输入(含群聊上下文、附件说明) */
|
|
12
|
+
export { normalizeMessage } from "./bridge/message-normalizer.js";
|
|
13
|
+
/** 提示词队列:保证同一会话的消息串行处理 */
|
|
14
|
+
export { PromptQueue } from "./bridge/prompt-queue.js";
|
|
15
|
+
/** 会话宿主管理器:按 sessionKey 管理 Pi 运行时实例 */
|
|
16
|
+
export { SessionHostManager } from "./bridge/session-host-manager.js";
|
|
17
|
+
/** 飞书 UI 桥接:把 Pi 的 ui.select/confirm 转成飞书权限卡片 */
|
|
18
|
+
export { createFeishuUIContext, resolvePermissionCardAction } from "./bridge/ui-context.js";
|
|
19
|
+
/** 配置加载:合并 环境变量 + 配置文件 + CLI 参数 */
|
|
20
|
+
export { loadConfig } from "./config/load-config.js";
|
|
21
|
+
/** 创建飞书通道(封装飞书 WebSocket SDK) */
|
|
22
|
+
export { createFeishuChannel } from "./feishu/channel.js";
|
|
23
|
+
/** 飞书运行上下文(AsyncLocalStorage 存取) */
|
|
24
|
+
export { getFeishuContext, runWithFeishuContext } from "./feishu/context.js";
|
|
25
|
+
/** JSON 文件存储:会话映射持久化 */
|
|
26
|
+
export { JsonFeishuStore } from "./store/json-store.js";
|
|
27
|
+
/** 生成 send_file_to_chat 工具,让 Pi 可以把本地文件回传到当前飞书聊天 */
|
|
28
|
+
export { createSendFileToChatTool } from "./tools/send-file-to-chat.js";
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+CAA+C;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,oCAAoC;AACpC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACrF,yCAAyC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,qCAAqC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,0BAA0B;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,uCAAuC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,iDAAiD;AACjD,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAC5F,mCAAmC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,iCAAiC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,oCAAoC;AACpC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,wBAAwB;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,oDAAoD;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FeishuStore, SessionFilter, SessionMapping } from "../types.js";
|
|
2
|
+
export declare class JsonFeishuStore implements FeishuStore {
|
|
3
|
+
private readonly filePath;
|
|
4
|
+
/** 写操作队列尾:保证 set/delete 串行执行 */
|
|
5
|
+
private writeTail;
|
|
6
|
+
constructor(filePath: string);
|
|
7
|
+
/** 按 sessionKey 查询映射 */
|
|
8
|
+
getSessionMapping(sessionKey: string): Promise<SessionMapping | undefined>;
|
|
9
|
+
/** 写入(或覆盖)一条映射:已存在则替换,否则追加 */
|
|
10
|
+
setSessionMapping(mapping: SessionMapping): Promise<void>;
|
|
11
|
+
/** 删除一条映射 */
|
|
12
|
+
deleteSessionMapping(sessionKey: string): Promise<void>;
|
|
13
|
+
/** 列出全部映射(支持按 chatId / userId / chatType 过滤) */
|
|
14
|
+
listSessionMappings(filter?: SessionFilter): Promise<SessionMapping[]>;
|
|
15
|
+
/**
|
|
16
|
+
* 写操作互斥锁:
|
|
17
|
+
* 把本次操作挂到 writeTail 链尾,前一个写完成后再执行,
|
|
18
|
+
* 防止并发 set/delete 造成文件内容丢失。
|
|
19
|
+
*/
|
|
20
|
+
private withWriteLock;
|
|
21
|
+
/** 读取存储文件;文件不存在或结构非法时按空映射处理 */
|
|
22
|
+
private readStore;
|
|
23
|
+
/** 写存储文件(自动创建目录,美化 JSON 格式) */
|
|
24
|
+
private writeStore;
|
|
25
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
/** 判断映射是否满足过滤条件(未指定字段视为不限制) */
|
|
4
|
+
function matchesFilter(mapping, filter) {
|
|
5
|
+
if (!filter)
|
|
6
|
+
return true;
|
|
7
|
+
if (filter.chatId !== undefined && mapping.chatId !== filter.chatId)
|
|
8
|
+
return false;
|
|
9
|
+
if (filter.userId !== undefined && mapping.userId !== filter.userId)
|
|
10
|
+
return false;
|
|
11
|
+
if (filter.chatType !== undefined && mapping.chatType !== filter.chatType)
|
|
12
|
+
return false;
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
/** 校验读到的数据是否为合法 StoreFile 结构 */
|
|
16
|
+
function isStoreFile(value) {
|
|
17
|
+
if (typeof value !== "object" || value === null || !("mappings" in value))
|
|
18
|
+
return false;
|
|
19
|
+
const mappings = value.mappings;
|
|
20
|
+
return Array.isArray(mappings);
|
|
21
|
+
}
|
|
22
|
+
export class JsonFeishuStore {
|
|
23
|
+
filePath;
|
|
24
|
+
/** 写操作队列尾:保证 set/delete 串行执行 */
|
|
25
|
+
writeTail = Promise.resolve();
|
|
26
|
+
constructor(filePath) {
|
|
27
|
+
this.filePath = filePath;
|
|
28
|
+
}
|
|
29
|
+
/** 按 sessionKey 查询映射 */
|
|
30
|
+
async getSessionMapping(sessionKey) {
|
|
31
|
+
const file = await this.readStore();
|
|
32
|
+
return file.mappings.find((mapping) => mapping.sessionKey === sessionKey);
|
|
33
|
+
}
|
|
34
|
+
/** 写入(或覆盖)一条映射:已存在则替换,否则追加 */
|
|
35
|
+
async setSessionMapping(mapping) {
|
|
36
|
+
await this.withWriteLock(async () => {
|
|
37
|
+
const file = await this.readStore();
|
|
38
|
+
const index = file.mappings.findIndex((entry) => entry.sessionKey === mapping.sessionKey);
|
|
39
|
+
if (index === -1) {
|
|
40
|
+
file.mappings.push(mapping);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
file.mappings[index] = mapping;
|
|
44
|
+
}
|
|
45
|
+
await this.writeStore(file);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** 删除一条映射 */
|
|
49
|
+
async deleteSessionMapping(sessionKey) {
|
|
50
|
+
await this.withWriteLock(async () => {
|
|
51
|
+
const file = await this.readStore();
|
|
52
|
+
await this.writeStore({
|
|
53
|
+
mappings: file.mappings.filter((mapping) => mapping.sessionKey !== sessionKey),
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** 列出全部映射(支持按 chatId / userId / chatType 过滤) */
|
|
58
|
+
async listSessionMappings(filter) {
|
|
59
|
+
const file = await this.readStore();
|
|
60
|
+
return file.mappings.filter((mapping) => matchesFilter(mapping, filter));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 写操作互斥锁:
|
|
64
|
+
* 把本次操作挂到 writeTail 链尾,前一个写完成后再执行,
|
|
65
|
+
* 防止并发 set/delete 造成文件内容丢失。
|
|
66
|
+
*/
|
|
67
|
+
async withWriteLock(run) {
|
|
68
|
+
const previous = this.writeTail;
|
|
69
|
+
let release = () => { };
|
|
70
|
+
this.writeTail = new Promise((resolve) => {
|
|
71
|
+
release = resolve;
|
|
72
|
+
});
|
|
73
|
+
await previous;
|
|
74
|
+
try {
|
|
75
|
+
await run();
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
release();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** 读取存储文件;文件不存在或结构非法时按空映射处理 */
|
|
82
|
+
async readStore() {
|
|
83
|
+
try {
|
|
84
|
+
const raw = await readFile(this.filePath, "utf-8");
|
|
85
|
+
const parsed = JSON.parse(raw);
|
|
86
|
+
if (isStoreFile(parsed)) {
|
|
87
|
+
return { mappings: parsed.mappings };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
// 文件不存在:返回空(首次运行时)
|
|
92
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
93
|
+
return { mappings: [] };
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
return { mappings: [] };
|
|
98
|
+
}
|
|
99
|
+
/** 写存储文件(自动创建目录,美化 JSON 格式) */
|
|
100
|
+
async writeStore(file) {
|
|
101
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
102
|
+
await writeFile(this.filePath, `${JSON.stringify(file, null, 2)}\n`, "utf-8");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=json-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-store.js","sourceRoot":"","sources":["../../src/store/json-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { JsonFeishuStore } from "./json-store.js";
|
|
3
|
+
/**
|
|
4
|
+
* 存储工厂:根据配置创建对应的 FeishuStore 实现。
|
|
5
|
+
* 目前只支持 json(sqlite 预留但未实现)。
|
|
6
|
+
* 会话映射文件放在 files.tempDir 下的 sessions.json。
|
|
7
|
+
*/
|
|
8
|
+
export function createStore(config) {
|
|
9
|
+
if (config.sessions.store === "sqlite") {
|
|
10
|
+
throw new Error('SQLite store is not implemented yet; use sessions.store = "json"');
|
|
11
|
+
}
|
|
12
|
+
const baseDir = config.files.tempDir ?? ".";
|
|
13
|
+
return new JsonFeishuStore(join(baseDir, "sessions.json"));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/store/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC;IAC5C,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import type { FeishuConfig } from "../types.js";
|
|
4
|
+
/**
|
|
5
|
+
* ============================================================
|
|
6
|
+
* send_file_to_chat 工具
|
|
7
|
+
* ------------------------------------------------------------
|
|
8
|
+
* 供 Pi 在生成过程中调用:把本地生成的文件回传到当前飞书聊天。
|
|
9
|
+
*
|
|
10
|
+
* 安全校验:
|
|
11
|
+
* - 必须在飞书请求上下文内(getFeishuContext)才可用
|
|
12
|
+
* - 文件路径必须在 allowedOutputDirs 白名单内
|
|
13
|
+
* - 必须是文件且大小不超过 maxUploadBytes
|
|
14
|
+
* ============================================================
|
|
15
|
+
*/
|
|
16
|
+
/** 工具参数 schema(typebox 描述,Pi 会据此生成函数签名给模型看) */
|
|
17
|
+
declare const sendFileSchema: Type.TObject<{
|
|
18
|
+
filePath: Type.TString;
|
|
19
|
+
fileName: Type.TOptional<Type.TString>;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* 创建 send_file_to_chat 工具。
|
|
23
|
+
* 在 Pi 的运行时中注册后,模型生成完文件即可调用它把文件发回飞书。
|
|
24
|
+
*/
|
|
25
|
+
export declare function createSendFileToChatTool(config: FeishuConfig): ToolDefinition<typeof sendFileSchema, undefined>;
|
|
26
|
+
export {};
|