@m1heng-clawd/feishu 0.1.9 → 0.1.11
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/README.md +199 -31
- package/index.ts +3 -1
- package/package.json +4 -4
- package/skills/feishu-doc/SKILL.md +24 -0
- package/src/bitable-tools/actions.ts +199 -0
- package/src/bitable-tools/common.ts +90 -0
- package/src/bitable-tools/meta.ts +80 -0
- package/src/bitable-tools/register.ts +195 -0
- package/src/bitable-tools/schemas.ts +221 -0
- package/src/bitable.ts +1 -441
- package/src/bot.ts +130 -68
- package/src/channel.ts +6 -8
- package/src/config-schema.ts +7 -0
- package/src/dedup.ts +31 -0
- package/src/doc-schema.ts +8 -0
- package/src/doc-write-service.ts +711 -0
- package/src/docx.ts +83 -287
- package/src/drive-schema.ts +20 -0
- package/src/drive.ts +57 -27
- package/src/dynamic-agent.ts +8 -4
- package/src/media.ts +25 -40
- package/src/monitor.ts +29 -2
- package/src/onboarding.ts +196 -105
- package/src/perm.ts +23 -24
- package/src/probe.ts +108 -4
- package/src/reply-dispatcher.ts +137 -73
- package/src/streaming-card.ts +211 -0
- package/src/targets.ts +1 -1
- package/src/task-tools/actions.ts +137 -0
- package/src/task-tools/common.ts +18 -0
- package/src/task-tools/register.ts +101 -0
- package/src/task-tools/schemas.ts +138 -0
- package/src/task.ts +1 -0
- package/src/tools-common/feishu-api.ts +184 -0
- package/src/tools-common/tool-context.ts +23 -0
- package/src/tools-common/tool-exec.ts +73 -0
- package/src/tools-config.ts +2 -1
- package/src/types.ts +1 -0
- package/src/wiki.ts +42 -43
package/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
Feishu/Lark (飞书) channel plugin for [OpenClaw](https://github.com/openclaw/openclaw).
|
|
4
4
|
|
|
5
5
|
> **中文社区资料** - 配置教程、常见问题、使用技巧:[Wiki](https://github.com/m1heng/clawdbot-feishu/wiki)
|
|
6
|
+
>
|
|
7
|
+
> **Contributing / 贡献指南**: [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
6
8
|
|
|
7
9
|
[English](#english) | [中文](#中文)
|
|
8
10
|
|
|
@@ -16,6 +18,19 @@ Feishu/Lark (飞书) channel plugin for [OpenClaw](https://github.com/openclaw/o
|
|
|
16
18
|
openclaw plugins install @m1heng-clawd/feishu
|
|
17
19
|
```
|
|
18
20
|
|
|
21
|
+
> [!IMPORTANT]
|
|
22
|
+
> **Windows Troubleshooting (`spawn npm ENOENT`)**
|
|
23
|
+
>
|
|
24
|
+
> If `openclaw plugins install` fails, install manually:
|
|
25
|
+
>
|
|
26
|
+
> ```bash
|
|
27
|
+
> # 1. Download the package
|
|
28
|
+
> curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz
|
|
29
|
+
>
|
|
30
|
+
> # 2. Install from local file
|
|
31
|
+
> openclaw plugins install ./feishu-0.1.3.tgz
|
|
32
|
+
> ```
|
|
33
|
+
|
|
19
34
|
### Upgrade
|
|
20
35
|
|
|
21
36
|
```bash
|
|
@@ -61,16 +76,20 @@ openclaw plugins update feishu
|
|
|
61
76
|
| `drive:drive:readonly` | `feishu_drive` | List folders, get file info |
|
|
62
77
|
| `wiki:wiki:readonly` | `feishu_wiki` | List spaces, list nodes, get node info, search |
|
|
63
78
|
| `bitable:app:readonly` | `feishu_bitable` | Read bitable records and fields |
|
|
79
|
+
| `task:task:read` | `feishu_task_get` | Get task details |
|
|
64
80
|
|
|
65
81
|
**Read-write** (optional, for create/edit/delete operations):
|
|
66
82
|
|
|
67
83
|
| Permission | Tool | Description |
|
|
68
84
|
|------------|------|-------------|
|
|
69
85
|
| `docx:document` | `feishu_doc` | Create/edit documents |
|
|
70
|
-
| `docx:document.block:convert` | `feishu_doc` | Markdown to blocks conversion (required for write/append) |
|
|
86
|
+
| `docx:document.block:convert` | `feishu_doc` | Markdown to blocks conversion (required for write/append/create_and_write; also used by `feishu_drive.import_document`) |
|
|
71
87
|
| `drive:drive` | `feishu_doc`, `feishu_drive` | Upload images to documents, create folders, move/delete files |
|
|
72
88
|
| `wiki:wiki` | `feishu_wiki` | Create/move/rename wiki nodes |
|
|
73
|
-
| `bitable:app` | `feishu_bitable` | Create/update bitable records |
|
|
89
|
+
| `bitable:app` | `feishu_bitable` | Create/update/delete bitable records and manage fields |
|
|
90
|
+
| `task:task:write` | `feishu_task_create`, `feishu_task_update`, `feishu_task_delete` | Create/update/delete tasks |
|
|
91
|
+
|
|
92
|
+
> Task scope names may vary slightly in Feishu console UI. If needed, search for Task-related permissions and grant read/write accordingly.
|
|
74
93
|
|
|
75
94
|
#### Drive Access ⚠️
|
|
76
95
|
|
|
@@ -149,6 +168,8 @@ channels:
|
|
|
149
168
|
connectionMode: "websocket"
|
|
150
169
|
# DM policy: "pairing" | "open" | "allowlist"
|
|
151
170
|
dmPolicy: "pairing"
|
|
171
|
+
# DM allowlist (open_id/user_id). Include "*" when dmPolicy="open"
|
|
172
|
+
allowFrom: []
|
|
152
173
|
# Group policy: "open" | "allowlist" | "disabled"
|
|
153
174
|
groupPolicy: "allowlist"
|
|
154
175
|
# Require @mention in groups
|
|
@@ -159,6 +180,81 @@ channels:
|
|
|
159
180
|
renderMode: "auto"
|
|
160
181
|
```
|
|
161
182
|
|
|
183
|
+
#### DM Policy & Access Control
|
|
184
|
+
|
|
185
|
+
`dmPolicy` controls who can interact with the bot in direct messages (DM).
|
|
186
|
+
In multi-account mode, this is resolved per account (`channels.feishu.accounts.<accountId>`).
|
|
187
|
+
|
|
188
|
+
| `dmPolicy` | Who can send DM | How to grant access to a user |
|
|
189
|
+
|------------|------------------|--------------------------------|
|
|
190
|
+
| `pairing` | Users in `allowFrom`, or users approved through pairing | User sends a DM and gets a pairing code; bot owner runs `openclaw pairing approve feishu <code>`. |
|
|
191
|
+
| `open` | Everyone | Set `allowFrom: ["*"]` so all users are treated as allowed. |
|
|
192
|
+
| `allowlist` | Only users in `allowFrom` | Add the user's `open_id`/`user_id` to `allowFrom`, then reload config. |
|
|
193
|
+
|
|
194
|
+
Notes:
|
|
195
|
+
- `allowFrom` accepts Feishu user IDs (`open_id` recommended, `user_id` also supported).
|
|
196
|
+
- If `dmPolicy: "open"`, use `allowFrom: ["*"]`. This is required by top-level schema validation and keeps access behavior explicit.
|
|
197
|
+
- `pairing` and `allowlist` can both pre-authorize users with `allowFrom`.
|
|
198
|
+
|
|
199
|
+
Pairing flow (owner approval):
|
|
200
|
+
1. User sends any DM to the bot.
|
|
201
|
+
2. Bot replies with a pairing code (for example `H9ZEHY8R`).
|
|
202
|
+
3. Bot owner approves:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
openclaw pairing approve feishu H9ZEHY8R
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
4. The user is added to the allow store and can chat immediately.
|
|
209
|
+
|
|
210
|
+
Example: open to everyone
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
channels:
|
|
214
|
+
feishu:
|
|
215
|
+
dmPolicy: "open"
|
|
216
|
+
allowFrom: ["*"]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Example: controlled rollout (pairing + pre-approved users)
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
channels:
|
|
223
|
+
feishu:
|
|
224
|
+
dmPolicy: "pairing"
|
|
225
|
+
allowFrom:
|
|
226
|
+
- "ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Example: strict allowlist
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
channels:
|
|
233
|
+
feishu:
|
|
234
|
+
dmPolicy: "allowlist"
|
|
235
|
+
allowFrom:
|
|
236
|
+
- "ou_alice"
|
|
237
|
+
- "ou_bob"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Example: account-level isolation
|
|
241
|
+
|
|
242
|
+
```yaml
|
|
243
|
+
channels:
|
|
244
|
+
feishu:
|
|
245
|
+
accounts:
|
|
246
|
+
lobster-1:
|
|
247
|
+
dmPolicy: "open"
|
|
248
|
+
allowFrom: ["*"]
|
|
249
|
+
lobster-5:
|
|
250
|
+
dmPolicy: "pairing"
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Top-level `channels.feishu.dmPolicy` / `channels.feishu.allowFrom` are fallback defaults for accounts that do not override them.
|
|
254
|
+
|
|
255
|
+
> `dmPolicy` only controls who can trigger the bot.
|
|
256
|
+
> To actually read/write docs or files, you still need: (1) correct Feishu app scopes, and (2) sharing the target resources (Drive/Wiki/Bitable) with the bot.
|
|
257
|
+
|
|
162
258
|
#### Connection Mode
|
|
163
259
|
|
|
164
260
|
Two connection modes are available for receiving events from Feishu:
|
|
@@ -257,10 +353,11 @@ session:
|
|
|
257
353
|
- Pairing flow for DM approval
|
|
258
354
|
- User and group directory lookup
|
|
259
355
|
- **Card render mode**: Optional markdown rendering with syntax highlighting
|
|
260
|
-
- **Document tools**: Read, create, and write Feishu documents with markdown
|
|
356
|
+
- **Document tools**: Read, create, and write Feishu documents with markdown, including atomic `create_and_write` / `import_document` flows for reliable create+content write
|
|
261
357
|
- **Wiki tools**: Navigate knowledge bases, list spaces, get node details, search, create/move/rename nodes
|
|
262
358
|
- **Drive tools**: List folders, get file info, create folders, move/delete files
|
|
263
|
-
- **Bitable tools**:
|
|
359
|
+
- **Bitable tools**: Manage bitable (多维表格) fields and records (read/create/update/delete), supports both `/base/` and `/wiki/` URLs
|
|
360
|
+
- **Task tools**: Create, get details, update, and delete tasks via Feishu Task v2 API
|
|
264
361
|
- **@mention forwarding**: When you @mention someone in your message, the bot's reply will automatically @mention them too
|
|
265
362
|
- **Permission error notification**: When the bot encounters a Feishu API permission error, it automatically notifies the user with the permission grant URL
|
|
266
363
|
- **Dynamic agent creation**: Each DM user can have their own isolated agent instance with dedicated workspace (optional)
|
|
@@ -299,18 +396,6 @@ Send `/new` command in the chat.
|
|
|
299
396
|
|
|
300
397
|
Feishu API has rate limits. Streaming updates can easily trigger throttling. We use complete-then-send approach for stability.
|
|
301
398
|
|
|
302
|
-
#### Windows install error `spawn npm ENOENT`
|
|
303
|
-
|
|
304
|
-
If `openclaw plugins install` fails, install manually:
|
|
305
|
-
|
|
306
|
-
```bash
|
|
307
|
-
# 1. Download the package
|
|
308
|
-
curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz
|
|
309
|
-
|
|
310
|
-
# 2. Install from local file
|
|
311
|
-
openclaw plugins install ./feishu-0.1.3.tgz
|
|
312
|
-
```
|
|
313
|
-
|
|
314
399
|
#### Cannot find the bot in Feishu
|
|
315
400
|
|
|
316
401
|
1. Ensure the app is published (at least to test version)
|
|
@@ -327,6 +412,19 @@ openclaw plugins install ./feishu-0.1.3.tgz
|
|
|
327
412
|
openclaw plugins install @m1heng-clawd/feishu
|
|
328
413
|
```
|
|
329
414
|
|
|
415
|
+
> [!IMPORTANT]
|
|
416
|
+
> **Windows 排错(`spawn npm ENOENT`)**
|
|
417
|
+
>
|
|
418
|
+
> 如果 `openclaw plugins install` 失败,可以手动安装:
|
|
419
|
+
>
|
|
420
|
+
> ```bash
|
|
421
|
+
> # 1. 下载插件包
|
|
422
|
+
> curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz
|
|
423
|
+
>
|
|
424
|
+
> # 2. 从本地安装
|
|
425
|
+
> openclaw plugins install ./feishu-0.1.3.tgz
|
|
426
|
+
> ```
|
|
427
|
+
|
|
330
428
|
### 升级
|
|
331
429
|
|
|
332
430
|
```bash
|
|
@@ -372,16 +470,20 @@ openclaw plugins update feishu
|
|
|
372
470
|
| `drive:drive:readonly` | `feishu_drive` | 列出文件夹、获取文件信息 |
|
|
373
471
|
| `wiki:wiki:readonly` | `feishu_wiki` | 列出空间、列出节点、获取节点详情、搜索 |
|
|
374
472
|
| `bitable:app:readonly` | `feishu_bitable` | 读取多维表格记录和字段 |
|
|
473
|
+
| `task:task:read` | `feishu_task_get` | 获取任务详情 |
|
|
375
474
|
|
|
376
475
|
**读写权限**(可选,用于创建/编辑/删除操作):
|
|
377
476
|
|
|
378
477
|
| 权限 | 工具 | 说明 |
|
|
379
478
|
|------|------|------|
|
|
380
479
|
| `docx:document` | `feishu_doc` | 创建/编辑文档 |
|
|
381
|
-
| `docx:document.block:convert` | `feishu_doc` | Markdown 转 blocks(write/append
|
|
480
|
+
| `docx:document.block:convert` | `feishu_doc` | Markdown 转 blocks(write/append/create_and_write 必需,`feishu_drive.import_document` 也会用到) |
|
|
382
481
|
| `drive:drive` | `feishu_doc`, `feishu_drive` | 上传图片到文档、创建文件夹、移动/删除文件 |
|
|
383
482
|
| `wiki:wiki` | `feishu_wiki` | 创建/移动/重命名知识库节点 |
|
|
384
|
-
| `bitable:app` | `feishu_bitable` |
|
|
483
|
+
| `bitable:app` | `feishu_bitable` | 创建/更新/删除多维表格记录并管理字段 |
|
|
484
|
+
| `task:task:write` | `feishu_task_create`, `feishu_task_update`, `feishu_task_delete` | 创建/更新/删除任务 |
|
|
485
|
+
|
|
486
|
+
> 飞书控制台中任务权限的显示名称可能略有差异,必要时可按关键字 `task` 搜索并授予对应读写权限。
|
|
385
487
|
|
|
386
488
|
#### 云空间访问权限 ⚠️
|
|
387
489
|
|
|
@@ -460,6 +562,8 @@ channels:
|
|
|
460
562
|
connectionMode: "websocket"
|
|
461
563
|
# 私聊策略: "pairing" | "open" | "allowlist"
|
|
462
564
|
dmPolicy: "pairing"
|
|
565
|
+
# 私聊白名单(open_id/user_id);当 dmPolicy="open" 时请包含 "*"
|
|
566
|
+
allowFrom: []
|
|
463
567
|
# 群聊策略: "open" | "allowlist" | "disabled"
|
|
464
568
|
groupPolicy: "allowlist"
|
|
465
569
|
# 群聊是否需要 @机器人
|
|
@@ -470,6 +574,81 @@ channels:
|
|
|
470
574
|
renderMode: "auto"
|
|
471
575
|
```
|
|
472
576
|
|
|
577
|
+
#### 私聊策略(dmPolicy)与访问授权
|
|
578
|
+
|
|
579
|
+
`dmPolicy` 控制的是“谁可以在私聊里触发机器人”。
|
|
580
|
+
在多账号模式下,它按账号生效(`channels.feishu.accounts.<accountId>`)。
|
|
581
|
+
|
|
582
|
+
| `dmPolicy` | 谁能私聊触发机器人 | 如何给用户开通 |
|
|
583
|
+
|------------|------------------|----------------|
|
|
584
|
+
| `pairing` | `allowFrom` 中的用户,或已通过配对审批的用户 | 用户先私聊机器人拿到配对码;管理员执行 `openclaw pairing approve feishu <code>`。 |
|
|
585
|
+
| `open` | 所有人 | 配置 `allowFrom: ["*"]`,表示全部放开。 |
|
|
586
|
+
| `allowlist` | 仅 `allowFrom` 中的用户 | 将用户 `open_id`/`user_id` 加入 `allowFrom`,然后重载配置。 |
|
|
587
|
+
|
|
588
|
+
说明:
|
|
589
|
+
- `allowFrom` 支持飞书用户 ID(推荐 `open_id`,也支持 `user_id`)。
|
|
590
|
+
- 当 `dmPolicy: "open"` 时,建议固定写 `allowFrom: ["*"]`,语义最清晰,也满足顶层配置校验要求。
|
|
591
|
+
- `pairing` 和 `allowlist` 都可以先通过 `allowFrom` 预授权部分用户。
|
|
592
|
+
|
|
593
|
+
配对审批流程(pairing):
|
|
594
|
+
1. 用户先给机器人发一条私聊消息。
|
|
595
|
+
2. 机器人返回配对码(例如 `H9ZEHY8R`)。
|
|
596
|
+
3. 管理员执行审批命令:
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
openclaw pairing approve feishu H9ZEHY8R
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
4. 审批后该用户立即可用。
|
|
603
|
+
|
|
604
|
+
示例:全部放开
|
|
605
|
+
|
|
606
|
+
```yaml
|
|
607
|
+
channels:
|
|
608
|
+
feishu:
|
|
609
|
+
dmPolicy: "open"
|
|
610
|
+
allowFrom: ["*"]
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
示例:灰度放开(pairing + 预授权)
|
|
614
|
+
|
|
615
|
+
```yaml
|
|
616
|
+
channels:
|
|
617
|
+
feishu:
|
|
618
|
+
dmPolicy: "pairing"
|
|
619
|
+
allowFrom:
|
|
620
|
+
- "ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
示例:严格白名单
|
|
624
|
+
|
|
625
|
+
```yaml
|
|
626
|
+
channels:
|
|
627
|
+
feishu:
|
|
628
|
+
dmPolicy: "allowlist"
|
|
629
|
+
allowFrom:
|
|
630
|
+
- "ou_alice"
|
|
631
|
+
- "ou_bob"
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
示例:按账号隔离配置
|
|
635
|
+
|
|
636
|
+
```yaml
|
|
637
|
+
channels:
|
|
638
|
+
feishu:
|
|
639
|
+
accounts:
|
|
640
|
+
lobster-1:
|
|
641
|
+
dmPolicy: "open"
|
|
642
|
+
allowFrom: ["*"]
|
|
643
|
+
lobster-5:
|
|
644
|
+
dmPolicy: "pairing"
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
`channels.feishu.dmPolicy` / `channels.feishu.allowFrom` 是“默认值”;账号下未覆盖时才会继承。
|
|
648
|
+
|
|
649
|
+
> `dmPolicy` 只控制“是否允许触发机器人”。
|
|
650
|
+
> 真正执行文档/云盘/知识库/多维表格操作,还需要两层权限:1)应用 API 权限(scopes);2)把目标资源分享给机器人。
|
|
651
|
+
|
|
473
652
|
#### 连接模式
|
|
474
653
|
|
|
475
654
|
支持两种从飞书接收事件的连接模式:
|
|
@@ -571,7 +750,8 @@ session:
|
|
|
571
750
|
- **文档工具**:读取、创建、用 Markdown 写入飞书文档(表格因 API 限制不支持)
|
|
572
751
|
- **知识库工具**:浏览知识库、列出空间、获取节点详情、搜索、创建/移动/重命名节点
|
|
573
752
|
- **云空间工具**:列出文件夹、获取文件信息、创建文件夹、移动/删除文件
|
|
574
|
-
-
|
|
753
|
+
- **多维表格工具**:支持多维表格字段与记录的读取/创建/更新/删除,支持 `/base/` 和 `/wiki/` 两种链接格式
|
|
754
|
+
- **任务工具**:基于 Task v2 API 支持任务创建、获取详情、更新和删除
|
|
575
755
|
- **@ 转发功能**:在消息中 @ 某人,机器人的回复会自动 @ 该用户
|
|
576
756
|
- **权限错误提示**:当机器人遇到飞书 API 权限错误时,会自动通知用户并提供权限授权链接
|
|
577
757
|
- **动态 Agent 创建**:每个私聊用户可拥有独立的 agent 实例和专属 workspace(可选)
|
|
@@ -610,18 +790,6 @@ session:
|
|
|
610
790
|
|
|
611
791
|
飞书 API 有请求频率限制,流式更新消息很容易触发限流。当前采用完整回复后一次性发送的方式,以保证稳定性。
|
|
612
792
|
|
|
613
|
-
#### Windows 安装报错 `spawn npm ENOENT`
|
|
614
|
-
|
|
615
|
-
如果 `openclaw plugins install` 失败,可以手动安装:
|
|
616
|
-
|
|
617
|
-
```bash
|
|
618
|
-
# 1. 下载插件包
|
|
619
|
-
curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz
|
|
620
|
-
|
|
621
|
-
# 2. 从本地安装
|
|
622
|
-
openclaw plugins install ./feishu-0.1.3.tgz
|
|
623
|
-
```
|
|
624
|
-
|
|
625
793
|
#### 在飞书里找不到机器人
|
|
626
794
|
|
|
627
795
|
1. 确保应用已发布(至少发布到测试版本)
|
package/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { registerFeishuWikiTools } from "./src/wiki.js";
|
|
|
7
7
|
import { registerFeishuDriveTools } from "./src/drive.js";
|
|
8
8
|
import { registerFeishuPermTools } from "./src/perm.js";
|
|
9
9
|
import { registerFeishuBitableTools } from "./src/bitable.js";
|
|
10
|
+
import { registerFeishuTaskTools } from "./src/task.js";
|
|
10
11
|
|
|
11
12
|
export { monitorFeishuProvider } from "./src/monitor.js";
|
|
12
13
|
export {
|
|
@@ -23,7 +24,7 @@ export {
|
|
|
23
24
|
sendFileFeishu,
|
|
24
25
|
sendMediaFeishu,
|
|
25
26
|
} from "./src/media.js";
|
|
26
|
-
export { probeFeishu } from "./src/probe.js";
|
|
27
|
+
export { probeFeishu, clearProbeCache } from "./src/probe.js";
|
|
27
28
|
export {
|
|
28
29
|
addReactionFeishu,
|
|
29
30
|
removeReactionFeishu,
|
|
@@ -57,6 +58,7 @@ const plugin = {
|
|
|
57
58
|
registerFeishuDriveTools(api);
|
|
58
59
|
registerFeishuPermTools(api);
|
|
59
60
|
registerFeishuBitableTools(api);
|
|
61
|
+
registerFeishuTaskTools(api);
|
|
60
62
|
},
|
|
61
63
|
};
|
|
62
64
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m1heng-clawd/feishu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw Feishu/Lark channel plugin",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,17 +46,17 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@larksuiteoapi/node-sdk": "^1.
|
|
49
|
+
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
50
50
|
"@sinclair/typebox": "0.34.48",
|
|
51
51
|
"zod": "^4.3.6"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^25.0.10",
|
|
55
|
-
"openclaw": "2026.
|
|
55
|
+
"openclaw": "2026.2.13",
|
|
56
56
|
"tsx": "^4.21.0",
|
|
57
57
|
"typescript": "^5.7.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"openclaw": ">=2026.
|
|
60
|
+
"openclaw": ">=2026.2.13"
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -32,6 +32,28 @@ Replaces entire document with markdown content. Supports: headings, lists, code
|
|
|
32
32
|
|
|
33
33
|
**Limitation:** Markdown tables are NOT supported.
|
|
34
34
|
|
|
35
|
+
### Create + Write (Atomic, Recommended)
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"action": "create_and_write",
|
|
40
|
+
"title": "New Document",
|
|
41
|
+
"content": "# Title\n\nMarkdown content..."
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
With folder:
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"action": "create_and_write",
|
|
49
|
+
"title": "New Document",
|
|
50
|
+
"content": "# Title\n\nMarkdown content...",
|
|
51
|
+
"folder_token": "fldcnXXX"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Creates the document and writes content in one call. Prefer this over separate `create` + `write`.
|
|
56
|
+
|
|
35
57
|
### Append Content
|
|
36
58
|
|
|
37
59
|
```json
|
|
@@ -51,6 +73,8 @@ With folder:
|
|
|
51
73
|
{ "action": "create", "title": "New Document", "folder_token": "fldcnXXX" }
|
|
52
74
|
```
|
|
53
75
|
|
|
76
|
+
Creates an empty document (title only).
|
|
77
|
+
|
|
54
78
|
### List Blocks
|
|
55
79
|
|
|
56
80
|
```json
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BitableClient,
|
|
3
|
+
BitableFieldCreateData,
|
|
4
|
+
BitableFieldUpdateData,
|
|
5
|
+
} from "./common.js";
|
|
6
|
+
import { formatField, runBitableApiCall } from "./common.js";
|
|
7
|
+
|
|
8
|
+
// -------- Field operations --------
|
|
9
|
+
|
|
10
|
+
export async function listFields(client: BitableClient, appToken: string, tableId: string) {
|
|
11
|
+
const res = await runBitableApiCall("bitable.appTableField.list", () =>
|
|
12
|
+
client.bitable.appTableField.list({
|
|
13
|
+
path: { app_token: appToken, table_id: tableId },
|
|
14
|
+
}),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const fields = res.data?.items ?? [];
|
|
18
|
+
return {
|
|
19
|
+
fields: fields.map((f) => formatField(f)),
|
|
20
|
+
total: fields.length,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function createField(
|
|
25
|
+
client: BitableClient,
|
|
26
|
+
appToken: string,
|
|
27
|
+
tableId: string,
|
|
28
|
+
field: BitableFieldCreateData,
|
|
29
|
+
) {
|
|
30
|
+
const res = await runBitableApiCall("bitable.appTableField.create", () =>
|
|
31
|
+
client.bitable.appTableField.create({
|
|
32
|
+
path: { app_token: appToken, table_id: tableId },
|
|
33
|
+
data: field,
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
field: res.data?.field ? formatField(res.data.field) : undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function updateField(
|
|
43
|
+
client: BitableClient,
|
|
44
|
+
appToken: string,
|
|
45
|
+
tableId: string,
|
|
46
|
+
fieldId: string,
|
|
47
|
+
field: BitableFieldUpdateData,
|
|
48
|
+
) {
|
|
49
|
+
const res = await runBitableApiCall("bitable.appTableField.update", () =>
|
|
50
|
+
client.bitable.appTableField.update({
|
|
51
|
+
path: { app_token: appToken, table_id: tableId, field_id: fieldId },
|
|
52
|
+
data: field,
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
field: res.data?.field ? formatField(res.data.field) : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function deleteField(
|
|
62
|
+
client: BitableClient,
|
|
63
|
+
appToken: string,
|
|
64
|
+
tableId: string,
|
|
65
|
+
fieldId: string,
|
|
66
|
+
) {
|
|
67
|
+
const res = await runBitableApiCall("bitable.appTableField.delete", () =>
|
|
68
|
+
client.bitable.appTableField.delete({
|
|
69
|
+
path: { app_token: appToken, table_id: tableId, field_id: fieldId },
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
success: res.data?.deleted ?? true,
|
|
75
|
+
field_id: res.data?.field_id ?? fieldId,
|
|
76
|
+
deleted: res.data?.deleted ?? true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// -------- Record operations --------
|
|
81
|
+
|
|
82
|
+
export async function listRecords(
|
|
83
|
+
client: BitableClient,
|
|
84
|
+
appToken: string,
|
|
85
|
+
tableId: string,
|
|
86
|
+
pageSize?: number,
|
|
87
|
+
pageToken?: string,
|
|
88
|
+
) {
|
|
89
|
+
const res = await runBitableApiCall("bitable.appTableRecord.list", () =>
|
|
90
|
+
client.bitable.appTableRecord.list({
|
|
91
|
+
path: { app_token: appToken, table_id: tableId },
|
|
92
|
+
params: {
|
|
93
|
+
page_size: pageSize ?? 100,
|
|
94
|
+
...(pageToken && { page_token: pageToken }),
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
records: res.data?.items ?? [],
|
|
101
|
+
has_more: res.data?.has_more ?? false,
|
|
102
|
+
page_token: res.data?.page_token,
|
|
103
|
+
total: res.data?.total,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function getRecord(
|
|
108
|
+
client: BitableClient,
|
|
109
|
+
appToken: string,
|
|
110
|
+
tableId: string,
|
|
111
|
+
recordId: string,
|
|
112
|
+
) {
|
|
113
|
+
const res = await runBitableApiCall("bitable.appTableRecord.get", () =>
|
|
114
|
+
client.bitable.appTableRecord.get({
|
|
115
|
+
path: { app_token: appToken, table_id: tableId, record_id: recordId },
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
record: res.data?.record,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function createRecord(
|
|
125
|
+
client: BitableClient,
|
|
126
|
+
appToken: string,
|
|
127
|
+
tableId: string,
|
|
128
|
+
fields: Record<string, unknown>,
|
|
129
|
+
) {
|
|
130
|
+
const res = await runBitableApiCall("bitable.appTableRecord.create", () =>
|
|
131
|
+
client.bitable.appTableRecord.create({
|
|
132
|
+
path: { app_token: appToken, table_id: tableId },
|
|
133
|
+
data: { fields },
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
record: res.data?.record,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function updateRecord(
|
|
143
|
+
client: BitableClient,
|
|
144
|
+
appToken: string,
|
|
145
|
+
tableId: string,
|
|
146
|
+
recordId: string,
|
|
147
|
+
fields: Record<string, unknown>,
|
|
148
|
+
) {
|
|
149
|
+
const res = await runBitableApiCall("bitable.appTableRecord.update", () =>
|
|
150
|
+
client.bitable.appTableRecord.update({
|
|
151
|
+
path: { app_token: appToken, table_id: tableId, record_id: recordId },
|
|
152
|
+
data: { fields },
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
record: res.data?.record,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function deleteRecord(
|
|
162
|
+
client: BitableClient,
|
|
163
|
+
appToken: string,
|
|
164
|
+
tableId: string,
|
|
165
|
+
recordId: string,
|
|
166
|
+
) {
|
|
167
|
+
const res = await runBitableApiCall("bitable.appTableRecord.delete", () =>
|
|
168
|
+
client.bitable.appTableRecord.delete({
|
|
169
|
+
path: { app_token: appToken, table_id: tableId, record_id: recordId },
|
|
170
|
+
}),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
success: res.data?.deleted ?? true,
|
|
175
|
+
record_id: res.data?.record_id ?? recordId,
|
|
176
|
+
deleted: res.data?.deleted ?? true,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function batchDeleteRecords(
|
|
181
|
+
client: BitableClient,
|
|
182
|
+
appToken: string,
|
|
183
|
+
tableId: string,
|
|
184
|
+
recordIds: string[],
|
|
185
|
+
) {
|
|
186
|
+
const res = await runBitableApiCall("bitable.appTableRecord.batchDelete", () =>
|
|
187
|
+
client.bitable.appTableRecord.batchDelete({
|
|
188
|
+
path: { app_token: appToken, table_id: tableId },
|
|
189
|
+
data: { records: recordIds },
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const results = res.data?.records ?? [];
|
|
194
|
+
return {
|
|
195
|
+
results,
|
|
196
|
+
requested: recordIds.length,
|
|
197
|
+
deleted: results.filter((r) => r.deleted).length,
|
|
198
|
+
};
|
|
199
|
+
}
|