lark-cli 飞书操作
用 lark-cli(官方 CLI)控制飞书:文档、云空间、电子表格、多维表格、消息、日历、邮件、任务、审批、OKR、妙记、视频会议等 18 大业务域。
前置检查(每次会话首次使用时执行一次)
1. 检查安装与版本
# 确认命令可用;没有则安装
Get-Command lark-cli -ErrorAction SilentlyContinue
# 未找到时(NODE_OPTIONS 会干扰全局安装,先清掉):
$env:NODE_OPTIONS=$null; npm install -g @larksuite/cli
# 装完仍找不到 -> 去 npm 全局目录里找实际路径
$npmPrefix = (npm prefix -g)
Get-ChildItem "$env:APPDATA\npm\lark-cli*", "$npmPrefix\lark-cli*" -ErrorAction SilentlyContinue
lark-cli --version
lark-cli doctor
doctor输出各项检查。cli_update为 warn 时建议先升级:lark-cli update- 若提示 "Check the installed lark-doc skill first; if it is not the v2 skill, run
lark-cli update",先执行lark-cli update(升级 CLI,同时更新配套 lark-doc 等 agent skills)
1b. 检查应用配置(与授权是两回事)
lark-cli config show
判据:输出里出现 appId 才算已配置——不要用退出码判断(配置缺失时它未必非零)。
- 有
appId→ 应用配置就绪,去看 §2 授权状态 - 没有
appId→ 需要初始化应用配置,但只能用非交互方式(见 §5 禁止命令)
⚠️ 应用配置完成 ≠ 用户已登录。
config show通过只说明应用凭证在,个人资源(用户文档、日历、邮件、任务、聊天记录)还需要 §3 的用户授权。两步要连着做完,不要停在申请配置这一步。
2. 检查授权状态
lark-cli auth status
auth status 关键字段的读法:
| 字段 | 含义 | 处理 |
|---|---|---|
identity |
当前默认身份(user / 无用户时才会回退 bot) |
要读个人资源必须是 user |
tokenStatus |
valid / needs_refresh / expired |
needs_refresh 会在下次调用时自动刷新,正常;expired 才要重新登录 |
expiresAt / refreshExpiresAt |
access token / refresh token 到期时间 | 看 refreshExpiresAt 判断要不要重新走 Device Flow |
scope |
已授权的 scope 列表 | 权限报错时先来这儿对照缺哪个 scope |
token expired→ 重新登录(见下)no token→ 首次登录
3. 登录(Device Flow)
关键点:auth login 是阻塞式命令,必须用 --no-wait 先拿到授权 URL,再交给用户完成浏览器授权。
# 步骤 A:发起授权,立即返回
lark-cli auth login --no-wait --json --domain docs,drive,im,calendar
返回 JSON 中含 verification_uri、user_code。把这两个信息告诉用户:
请打开 {verification_uri},输入验证码 {user_code} 完成授权。
# 步骤 B:用户在浏览器完成授权后,用 device-code 完成登录
lark-cli auth login --device-code <DEVICE_CODE> --json
scope 选择(--domain 参数,可逗号分隔多个):
- 只操作文档:
docs,drive - 操作表格:
sheets - 操作多维表格:
base - 发消息:
im - 日历:
calendar - 邮件:
mail - 任务:
task - 全部:
all(不推荐,scope 越多审批越慢)
4. 常用授权管理命令
lark-cli auth status # 查看当前用户/token状态
lark-cli auth list # 列出所有已登录用户
lark-cli auth scopes # 查看应用已开通的 scopes
lark-cli auth check --scope docs:document:read # 检查是否具备某 scope
lark-cli auth logout # 登出
lark-cli config show # 查看当前应用配置
5. 禁止执行的命令(会挂住会话)
以下命令任何情况下都不要跑——它们需要 TTY,非交互环境下会输出坏掉的二维码并一直阻塞:
| 禁止命令 | 原因 |
|---|---|
lark-cli config init --new |
交互式,需 TTY |
lark-cli config init |
同上(不带 --new 也会进交互) |
lark-cli config set-default |
交互式选择,需 TTY |
要初始化应用配置时,走非交互路径;不要用上面三条试探。
6. Device Flow 硬化规则(超时、重试、复用)
auth login --no-wait 拿到 device_code 后,轮询阶段有三条硬规则:
authorization_pending是正常状态,不是错误。它在 HTTP 层可能表现为 400,含义是「用户还没在浏览器点完授权」。继续等,不要重新发起。- 轮询命令的存活时间必须长于 device code 有效期。device code 常见有效期为 10 分钟,所以轮询要跑 ≥11 分钟(工具超时给 700000 ms 以上),或干脆放到后台跑。
- 轮询被工具超时打断、但 device code 还没过期时,复用同一个
device_code继续轮询,不要重新--no-wait生成新码。只有出现expired_token、invalid_grant,或确认已过有效期,才重新发起一次登录。
同时:同一时刻只跑一个轮询。上一个轮询还在 authorization_pending 时,不要并行开新的 setup 或 login 流程。
7. 首次连接的用户提示语
首次使用需要两步连接时,先跟用户说清楚,避免他以为在重复授权:
第一次使用飞书套件需要完成两步连接:先初始化飞书 CLI 应用配置,再授权访问你有权限的飞书数据。两步完成后我会自动继续当前任务;这不是重复授权。
**补充授权(不是重连)**的话术——飞书权限按能力拆分,跑更具体的操作(文档搜索、云盘检索、导出等)时可能要追加 scope:
当前任务需要补充授权:飞书文档搜索权限
search:docs:read。授权后,后续搜索飞书文档不会再次要求这个权限。
补充授权时用显式 scope 登录,且 --scope 不能与 --domain、--recommend 同时使用(三者互斥)。除非有意收窄 token,否则不要用一小撮显式 scope 替换掉推荐范围登录。
常用命令速查
写法分两类:shortcut(
+xxx,高层封装,优先用)与 原始 API(lark-cli <domain> <resource> <method>)。用原始 API 前先跑一次lark-cli schema <domain>.<resource>.<method>看准参数结构,别照记忆拼--data。
域与 shortcut 速览(本机 lark-cli 1.0.31 实测)
| 域 | 用途 | 实测 shortcut |
|---|---|---|
im |
消息、群、聊天记录、附件、话题、书签 | +messages-send、+messages-search、+messages-reply、+messages-resources-download、+chat-list、+chat-create、+chat-search、+chat-messages-list、+threads-messages-list、+flag-list |
docs |
新版文档读写、搜索、媒体 | +fetch、+create、+update、+search、+media-insert、+media-download、+whiteboard-update |
drive |
云空间文件、导入导出、权限、评论、目录镜像 | +upload、+download、+search、+export、+export-download、+import、+create-folder、+add-comment、+push、+pull、+status、+task_result |
sheets |
电子表格读写、查找替换、行列增删、样式、浮动图片 | +read、+write、+append、+create、+find、+replace、+info、+insert-dimension、+delete-dimension、+export、+set-style |
base |
多维表格记录/字段/视图/仪表盘/工作流/表单 | +base-create、+table-list、+field-list、+record-list、+record-search、+record-batch-create、+record-batch-update、+record-upsert、+record-upload-attachment、+data-query、+view-list、+workflow-list |
calendar |
日程、空闲、会议时间建议、会议室 | +agenda、+create、+update、+freebusy、+suggestion、+rsvp、+room-find |
task |
任务、清单、提醒、评论、附件 | +create、+get-my-tasks、+get-related-tasks、+search、+complete、+update、+comment、+reminder、+tasklist-create、+tasklist-search |
mail |
收发、草稿、线程、全文检索 | +send、+draft-create、+reply、+reply-all、+forward、+message、+thread、+triage、+watch |
wiki |
知识库节点、移动、空间删除 | +node-create、+move、+delete-space |
vc |
会议记录、录制、纪要、bot 入会 | +search、+notes、+recording、+meeting-join、+meeting-events、+meeting-leave |
contact |
用户搜索与资料(需 --as user) |
+search-user、+get-user |
minutes |
妙记搜索、媒体下载、上传生成 | +search、+download、+upload |
whiteboard |
画板查询 / 用 mermaid·plantuml·DSL 更新 | +query、+update |
⚠️ shortcut 名随版本变化:网上某些移植文档写的是
+calendars-list、+events-list、+spreadsheets-read、+tables-records-list、+documents-create这类名字,在本机 1.0.31 上不存在(calendar 实际是+agenda/+freebusy等,sheets 是+read,base 是+record-list)。一律以lark-cli <domain> --help的实际输出为准,不要照抄外部文档。
常用实体 ID
| 实体 | ID 形态 |
|---|---|
| 用户 | open_id(ou_xxx)/ user_id / email |
| 群聊 | chat_id(oc_xxx) |
| 消息 | message_id(om_xxx) |
| 话题 | thread_id |
| 文档 | document_id(docx token) |
| 文件 | file_key / file_id |
| 多维表格 | base_id(app_xxx)+ table_id(tbl_xxx) |
| 日程 | event_id |
文档(docs)
| 操作 | 命令 |
|---|---|
| 读取文档 | lark-cli docs +fetch --api-version v2 --doc "<URL或token>" |
| 创建文档 | lark-cli docs +create --api-version v2 --title "标题" --markdown "<内容或@file>" |
| 追加内容 | lark-cli docs +update --api-version v2 --doc "<token>" --mode append --markdown "<内容>" |
| 覆盖全文 | lark-cli docs +update --api-version v2 --doc "<token>" --mode overwrite --markdown "<内容>" |
| 替换某段 | lark-cli docs +update --api-version v2 --doc "<token>" --mode replace_range --selection-by-title "## 小节" --markdown "<新内容>" |
| 更新标题 | lark-cli docs +update --api-version v2 --doc "<token>" --new-title "新标题" |
| 删除范围 | lark-cli docs +update --api-version v2 --doc "<token>" --mode delete_range --selection-by-title "## 小节" |
| 搜索文档 | lark-cli docs +search --query "关键词" |
| 插入图片 | lark-cli docs +media-insert --doc "<token>" --file "D:\path\img.png" |
文档 token 提取:URL https://xxx.feishu.cn/docx/I005dwk5poSRtExG0RpcRG5Knrf 中的 I005dwk5poSRtExG0RpcRG5Knrf 就是 doc token,可直接传给 --doc。
内容格式:
--markdown支持 Lark-flavored Markdown,也支持@文件路径(读取本地文件)或-(stdin)- 也可用
--doc-format xml使用 XML 格式获得更强的结构控制(表格/画板/图表等) - ⚠️ 已知问题:v2
+create --title T --content @file.md可能静默丢弃 title 和 content(GitHub issue #827),返回 ok 但文档是空的。建议创建时用--markdown而非--content,创建后立即+fetch验证非空。 - 版本差异(实测 lark-cli 1.0.31):
docs +update --api-version v2的实际 flag 是--command append|overwrite|replace_range|...、--content、--doc-format xml|markdown,没有--mode/--markdown(上表是新版 CLI 写法;用前先lark-cli docs +update --api-version v2 --help确认)。+update不支持--format。表格行列级编辑请直接用下方块级 API。
云空间(drive)
| 操作 | 命令 |
|---|---|
| 上传文件 | lark-cli drive +upload --file "D:\path\file.pdf" --folder-token <token> |
| 下载文件 | lark-cli drive +download --token <file_token> --output "D:\path\save.pdf" |
| 搜索文件 | lark-cli drive +search --query "关键词" |
| 创建文件夹 | lark-cli drive +create-folder --name "新文件夹" --folder-token <父token> |
| 移动文件 | lark-cli drive +move --token <file_token> --folder-token <目标token> |
| 删除文件 | lark-cli drive +delete --token <file_token> |
| 导出文档 | lark-cli drive +export --token <doc_token> --type docx |
| 添加评论 | lark-cli drive +add-comment --token <doc_token> --content "评论内容" |
电子表格(sheets)
| 操作 | 命令 |
|---|---|
| 创建表格 | lark-cli sheets +create --title "表名" --header "A,B,C" |
| 读取数据 | lark-cli sheets +read --spreadsheet-token <token> --range "Sheet1!A1:C10" |
| 写入数据 | lark-cli sheets +write --spreadsheet-token <token> --range "Sheet1!A1" --values "[[1,2,3]]" |
| 追加数据 | lark-cli sheets +append --spreadsheet-token <token> --values "[[1,2,3]]" |
| 查找替换 | lark-cli sheets +find --spreadsheet-token <token> --find "关键词" |
| 查看信息 | lark-cli sheets +info --spreadsheet-token <token> |
多维表格(base)
| 操作 | 命令 |
|---|---|
| 创建 | lark-cli base +base-create --name "表名" |
| 查看字段 | lark-cli base +field-list --app-token <token> --table-id <table_id> |
| 读取记录 | lark-cli base +record-list --app-token <token> --table-id <table_id> |
| 搜索记录 | lark-cli base +record-search --app-token <token> --table-id <table_id> --query "关键词" |
消息(im)
| 操作 | 命令 |
|---|---|
| 发消息 | lark-cli im +message-send --receive-id <open_id> --receive-id-type open_id --msg-type text --content "内容" |
| 搜索消息 | lark-cli im +message-search --query "关键词" |
| 列出群聊 | lark-cli im +chat-list |
通用 API(兜底,透传飞书开放平台 OpenAPI)
# GET(无查询参数)
lark-cli api GET /open-apis/docx/v1/documents/<doc>/raw_content
# GET(带查询参数)—— PowerShell 会吃掉 --params 里的双引号,必须"单引号 + 反斜杠转义":
lark-cli api GET "/open-apis/docx/v1/documents/<doc>/blocks" --params '{\"page_size\":500}'
# PATCH/POST(带请求体)—— 复杂 JSON 先写 body.json(UTF-8 无 BOM),再 --data @file:
[IO.File]::WriteAllText("$PWD\body.json", $json, (New-Object System.Text.UTF8Encoding($false)))
lark-cli api PATCH "/open-apis/docx/v1/documents/<doc>/blocks/<block_id>" --data "@body.json"
实测三个坑:
- HTTP 动词必须严格照官方文档:docx「更新块」是
PATCH;用 PUT/POST 调它得到的是HTTP 404: 404 page not found(不是 405),容易误判成接口不存在 --params报invalid format, expected JSON object= 双引号被 PowerShell 剥掉了,改用'{\"k\":v}'写法schema子命令只覆盖 approval/calendar/drive/im/mail/sheets/wiki 等,不含 docx(报Unknown service: docx),docx 参数直接查官方文档(链接见下节)
官方块级 API(docx v1:表格行列级、块级精细编辑)
docs +update 只能整段 Markdown 追加/覆盖,表格加行、单格改字、删行列这类结构级编辑它做不到,要直接调官方块级 OpenAPI(lark-cli api 透传)。官方文档入口:
| 接口 | 方法与 URL | 官方文档 |
|---|---|---|
| 获取所有块 | GET /open-apis/docx/v1/documents/<doc>/blocks?page_size=500 |
文档概述(块级接口总览) |
| 获取单块 | GET .../blocks/<block_id> |
同上 |
| 更新块 | PATCH .../blocks/<block_id> |
更新块 |
| 批量更新块 | PATCH .../blocks/batch_update |
批量更新块 |
| 创建块 | POST .../blocks/<parent_id>/children,body {"children":[...],"index":n} |
文档概述 |
| 创建嵌套块(一次建出表格+单元格) | POST .../blocks/<parent_id>/descendant |
文档概述 |
| 删除块 | DELETE .../blocks/<block_id>/children/batch_delete,body {"start_index":0,"end_index":2} |
文档概述 |
权限:创建及编辑新版文档 或 编辑新版文档(scope 里有 docx:document:write_only 即可)。
块模型(GET blocks 实测确认的 block_type)
| block_type | 含义 | 识别特征 |
|---|---|---|
| 1 | 页面(根块,page 的 block_id = document_id) | children 是文档顶层块序列 |
| 2 | 文本段落 | text.elements[].text_run |
| 3–11 | 标题 1–9 级 | heading1 / heading2 … |
| 14 | 代码块 | code.elements,style.language |
| 31 | 表格 | table.property.{row_size,column_size,header_row};table.cells 为 cell 的 block_id 按行主序排列(每行 column_size 个) |
| 32 | 单元格 | table_cell,children 通常是一个 block_type 2 的段落 |
更新块(PATCH /blocks/)请求体键
一次 PATCH 只做一个操作,请求体取以下键之一(标 ✋ 的仅 Table 块可用):
| 键 | 用途 |
|---|---|
update_text_elements |
重写段落文本。elements 数组支持 text_run / mention_user / mention_doc / equation;样式放 text_element_style(bold/italic/underline/strikethrough/inline_code/background_color/text_color/link) |
update_text_style |
只改段落样式(align/folded),不动内容 |
update_table_property ✋ |
表格属性(列宽 column_width 等) |
insert_table_row ✋ |
{"row_index":n}:n=-1 插在最前,n=row_size 追加末尾;响应直接返回新行各 cell 的 block_id |
insert_table_column ✋ |
{"column_index":n} |
delete_table_rows / delete_table_columns ✋ |
批量删行/列(行/列索引数组) |
merge_table_cells / unmerge_table_cells ✋ |
合并/取消合并(row/column_start_index、row/column_end_index) |
批量更新块(PATCH /blocks/batch_update)
请求体 requests 为数组,每项 = block_id + 上面任意一个操作键。一次调用改多个段落:
{ "requests": [
{ "block_id": "<段落block1>", "update_text_elements": { "elements": [
{ "text_run": { "content": "加粗文字", "text_element_style": { "bold": true } } } ] } },
{ "block_id": "<段落block2>", "update_text_elements": { "elements": [
{ "text_run": { "content": "https://example.com",
"text_element_style": { "link": { "url": "https%3A%2F%2Fexample.com" } } } } ] } }
] }
⚠️ link.url 必须 percent-encode(https:// → https%3A%2F%2F),写原始 URL 会得到坏链接。响应带 document_revision_id;可传 client_token 幂等。
表格追加一行:完整调用序列(实测 5 步)
GET .../blocks→ 找block_type:31的 table 块,记block_id与property.row_sizePATCH .../blocks/<table_block_id>+{"insert_table_row":{"row_index":<row_size>}}→ 末尾加空行,响应返回新行 3 个 cell 的 block_id- 再
GET .../blocks→ 用 cell id 反查各 cell 的 children,拿到新行的文本段落 block id(insert 响应里只有 cell id,没有段落 id) PATCH .../blocks/batch_update→ 对三个段落 block 分别update_text_elements填入各列内容(名称 bold、纯文本、link 三件套)docs +fetch回读 → 确认行数 +1、新行内容与样式正确
工作流程
场景 A:读取飞书文档
- 用户给 URL → 提取 token(
/docx/<token>或/wiki/<token>) lark-cli docs +fetch --api-version v2 --doc "<token>" --format json- 输出是 JSON,含 markdown 格式正文;如需落盘:
lark-cli docs +fetch --api-version v2 --doc "<token>" | Out-File -Encoding utf8 doc.md - wiki 文档可能需要先通过
lark-cli wiki解析 node → 实际 obj_token,再 fetch
场景 B:编辑飞书文档
- 先
+fetch读取现状(拿到 markdown + block 结构) - 确认要改动的位置:追加到末尾用
--mode append;整篇替换用--mode overwrite;只改某一节用--mode replace_range --selection-by-title "## 章节" - 写入临时 md 文件,用
--markdown @temp.md传入,避免命令行转义问题 - 执行
+update,检查返回 JSON 的code字段是否为 0 - 再次
+fetch验证修改生效
场景 C:创建飞书文档
- 准备 markdown 内容(写入本地临时文件最稳妥)
lark-cli docs +create --api-version v2 --title "标题" --markdown @temp.md- 检查返回 JSON 中的
url/document_id - 立即 fetch 回读验证内容非空(见上文 issue #827 的坑)
场景 D:批量文档处理
lark-cli docs +search --query "关键词" --page-size 20拿到一批文档- 逐个提取 token,循环执行 fetch / update
- 有失败要单独记录,不要让单条失败中断整批
输出处理技巧
# 用 --jq 过滤 JSON 字段
lark-cli docs +fetch --api-version v2 --doc <token> -q '.data.content'
lark-cli docs +search --query "周报" -q '.data.items[].url'
# 输出格式选择
--format json # 默认,结构化
--format pretty # 人类可读
--format table # 表格
--format ndjson # 每行一个JSON,便于逐行处理
# 分页
--page-all # 自动翻页拿全量
--page-size 50 # 每页条数
常见错误与排查
| 错误 | 原因 | 处理 |
|---|---|---|
token expired |
登录过期 | 重新 auth login --no-wait --domain ... |
permission denied / forbidden |
缺少 scope 或文档未授权给当前用户 | 检查 auth scopes;请文档所有者在飞书客户端里把文档分享给当前登录用户 |
InvalidParam |
参数格式错误 | 检查 token 是否取对、JSON 是否合法 |
not exist |
token 错误或文档被删 | 确认 URL 是否最新 |
Rate limit |
触发限流 | 加 Start-Sleep -Milliseconds 500 重试 |
| 输出中文乱码 | PowerShell 编码问题 | 命令前先执行 [Console]::OutputEncoding=[Text.Encoding]::UTF8 |
| v2 create 内容为空 | issue #827 | 用 --markdown 而非 --content,创建后 fetch 验证 |
api 透传返回 404 page not found |
HTTP 动词不对(docx 更新块是 PATCH,用 PUT/POST 就 404)或路径拼错(如 /all-blocks 不存在) |
逐字对照官方文档的方法+URL |
--params invalid format, expected JSON object |
PowerShell 剥掉了内层双引号 | 写成 --params '{\"page_size\":500}' |
--file/--content must be a relative path |
@file 只接受当前目录内的相对路径 |
临时文件写进工作区,用 @./file.json |
Unknown service: docx(schema 命令) |
schema 不覆盖 docx | docx 参数查官方文档(「官方块级 API」一节有链接) |
表格要加行/改单元格,+update 做不到 |
CLI 只有整段 Markdown 粒度 | 走官方块级 PATCH/batch_update(见「官方块级 API」一节) |
need_user_authorization / No user logged in / failed to get access token |
用户未登录或 token 过期 | 停止重试业务接口,走 device flow 登录并复用同一 device_code 轮询,再 auth status 确认 |
forbidden(配 --as bot) |
bot/应用不是该资源协作者 | 停止用 bot 重试;改用 --as user,或请用户把应用加为文档/资源协作者 |
App scope not enabled / required scope ... |
应用没在开放平台开通该 scope,用户授权也补不上 | 停止重试;告知用户/管理员需在开发者后台开通的具体 scope 名,开通后重授权再重试一次 |
authorization_pending |
用户还没在浏览器点完授权 | 保持当前轮询,不要开新流程 |
expired_token / invalid_grant |
device code 过期 | 重新发起一次登录,把新 URL 给用户 |
熔断规则(fail-fast):业务接口报授权/权限错误后,只允许诊断一次,然后切到对应的授权步骤。不要反复换 --as user / --as bot / --format 等无关变体重试同一接口。
注意事项
- 身份选择:
- 默认
--as auto:有登录用户就用用户身份,否则回退到应用(bot)身份。 - 个人资源(用户能打开的文档、日历、邮件、任务、聊天记录)在
auth status有效时优先显式--as user。 --as bot只用于应用自身操作,或 bot 明确是成员/协作者的资源;不是协作者时给它--as bot只会拿到forbidden。- 文档归属:用户要的文档/表格默认用
--as user创建,这样归属用户本人;只有用户明确要应用归属内容时才用--as bot。 - bot 建的文档要分享给用户,需要
docs:permission.member:create;该 scope 未开通时停下来找管理员开通,不要绕路。
- 默认
- Risk 标记:
+fetch/+search是 read;+create/+update/+delete是 write。写操作前确认目标正确。 - dry-run:加
--dry-run只打印请求不执行,可用来验证参数。 - 大文件/长内容:优先走
@file方式传内容,避免命令行长度限制和转义坑。 - Windows 路径:
@D:\path\to\file.md反斜杠在 PowerShell 里没问题,但如果内容含中文建议文件存 UTF-8(无 BOM 更稳)。 - 不要把 token/app-secret 写进 skill 或脚本:凭证由
lark-cli config/auth管理,OS keychain 存储。 - CLI 升级:
lark-cli update --json;升级后 agent skills(lark-doc 等)会同步更新到 v2。