乐于分享
好东西不私藏

组织效率工具:lark-cli 使用文档

组织效率工具:lark-cli 使用文档

飞书官方命令行工具完整上手手册,补充截至 2026-08-16 的最新版本、能力范围与使用建议。

lark-cli 使用文档

这是一篇适合直接收藏的 lark-cli 长文手册。 我在原始技术文档基础上,补了截至 2026-08-16 的最新版本信息、官方能力概览和几张速览图,方便公众号阅读。

图 1:GitHub 仓库的 Open Graph 预览图。来源:github.com/larksuite/cli

先看结论

  • lark-cli 是飞书官方开源 CLI,定位很明确:让人和 AI Agent 都能在终端里操作飞书
  • 截至 2026-08-16,GitHub 官方仓库显示它覆盖 18 个业务域、200+ 命令、26 个 AI Agent Skills
  • 截至 2026-08-13,最新 release 已经是 v1.0.87,比很多旧文档里提到的 1.0.85 更新。
  • 如果你只想尽快上手,先看本文里的 图 2 / 图 3 和第二节身份模型,再回头查具体命令。

图 2:飞书官网文章里引用的 lark-cli GitHub README 页面截图。来源:feishu.cn

图 3:一篇公开教程里的 Skills 安装界面截图,可直观看到 npx skills add larksuite/cli -y -g 的效果。来源:腾讯云开发者社区。

最新动态(截至 2026-08-16)

  • 官方仓库:larksuite/cli
  • 最新 release:v1.0.87
  • 发布时间:2026-08-13
  • 官方 README 当前描述:18 个业务域、200+ 命令、26 个 AI Agent Skills
  • 推荐安装命令:npx @larksuite/cli@latest install
  • 推荐升级命令:lark-cli update

飞书(Lark)开放平台的命令行工具,覆盖云文档、知识库、消息、日历、邮件、表格、多维表格、任务、审批、会议等能力。

  • 版本:本文原稿基于 1.0.78 编写;截至 2026-08-16,官方最新 release 为 v1.0.87
  • 可执行文件路径:安装后请用 which lark-cli 以本机实际结果为准
  • 配置文件:~/.lark-cli/config.json

文档中所有 token、空间 ID、用户 ID、域名均为占位符,实际使用时替换成自己的值。


零、这是什么

lark-cli 把飞书开放平台的 API 包装成了命令行。原本要写代码调接口、处理 token 刷新、翻分页、轮询异步任务的活儿,现在一条命令就能完成。

它解决的核心问题是把飞书里的数据变成可以用管道处理的文本流。举几个实际例子:

# 我拥有的文档有多少篇、都是什么lark-cli drive +search --mine --page-size 20 --as user \  --jq '.data.results[] | .result_meta.token'# 今天的日程lark-cli calendar +agenda --as user# 分配给我的任务lark-cli task +get-my-tasks --as user# 某篇文档被谁看过lark-cli drive file.view_records list --file-token <token> --file-type docx --as user

输出都是 JSON,可以直接接 jqawksort 做统计,也可以写成脚本定时跑。这是它和网页版飞书最本质的区别 —— 网页版适合看单篇,命令行适合批量和聚合。

能做什么

类别
领域
典型场景
云文档
drivedocssheetsbasewiki
搜文档、批量导出、读写正文、表格数据、知识库遍历
沟通协作
immailcalendartaskapproval
群消息、邮件收发、日程安排、任务管理、审批
会议
vcminutesnote
会议记录、妙记转写、会议笔记
组织
contactattendanceokr
通讯录、考勤、OKR
其他
appswhiteboardslidesmindnotesevent
妙搭应用、画板、幻灯片、思维笔记、事件订阅

适合与不适合

适合:批量处理(几百篇文档的导出、归档、权限梳理)、跨文档聚合统计、定时任务、把飞书数据接进其他工具链。

不适合:需要精细排版的文档编辑(虽然能写,但所见即所得还是网页版顺手)、一次性的简单查看。

上手路径

如果你是第一次用,按这个顺序看最快:

  1. 先读第二节身份模型。这是最容易出错的地方,--as 用错会拿到空结果而且不报错
  2. 再读第一节命令模型,学会用 --help 和 schema 自己查命令,不用记。
  3. 然后读第四节输出与解析,尤其是 ok 字段的判断规则。
  4. 需要动写操作前,读第五节安全门禁
  5. 剩下的按需查阅。第十二节的踩坑记录建议通读一遍,都是实际踩过的。

最省事的自查命令:

lark-cli doctor    # 一次性检查配置、认证、连通性lark-cli whoami# 看当前是什么身份在操作

一、命令模型

1.1 三层结构

命令按抽象程度分三层,优先用高层的

层级
形式
特点
快捷命令
lark-cli drive +search
+ 前缀。一条命令完成一个完整任务,自动处理 URL 解析、分页、异步任务轮询、多步编排。首选
类型化命令
lark-cli drive file.view_records list
对应单个 API 方法,参数有校验,风险等级明确
原始转义
lark-cli api GET /open-apis/...
直接按 HTTP 路径调用。只在前两层都没覆盖时用(例如新发布的预览版接口)

快捷命令不只是省字数。以 drive +export 为例,它内部完成「发起导出任务 → 轮询任务状态 → 下载结果文件」三步;docs +media-insert 更是四步编排并带自动回滚。用类型化命令自己拼这些流程容易漏掉轮询和错误处理。

1.2 自我发现

不用记命令,靠这四条把命令面摸清:

lark-cli --help# 列出全部领域lark-cli drive --help# 列出该领域的快捷命令 + API 资源lark-cli drive +search --help# 单个命令的全部参数、风险等级、使用提示lark-cli schema drive.metas.batch_query   # API 的入参、出参、所需权限、文档链接

schema 的输出里有几个关键字段:

  • inputSchema 每个参数对应的 flag 名、类型、枚举值、默认值、上下限
  • outputSchema 返回结构,写 --jq 表达式前先看这里
  • _meta.scopes 所需权限
  • _meta.risk 风险等级
  • _meta.doc_url 官方 API Explorer 链接

命令的 --help 底部常有 Tips 段落,写了参数互斥关系和易错点,值得一读。

1.3 参数形式

领域命令(drivedocswikiim 等)的参数一律走标志,没有位置参数:

# 错误:会报 positional arguments are not supportedlark-cli drive +inspect "https://example.feishu.cn/docx/<token>"# 正确lark-cli drive +inspect --url "https://example.feishu.cn/docx/<token>"

只有三个工具类命令接受位置参数:api <METHOD> <PATH>schema <service.resource.method>skills read <name>


二、身份模型

这是最容易出错的地方,务必先理解。

2.1 两种身份

身份
标志
授权方式
能访问什么
用户身份
--as user
后台开通 scope 用户 auth login 授权,两层都要满足
用户自己的日历、云空间、邮箱、任务等个人资源
应用身份
--as bot
只需在开发者后台开通 scope,无需 login
应用级资源、bot 自己的资源

每次命令输出里的 identity 字段表示当前生效身份。

2.2 为什么必须显式指定

bot 和 user 的行为差异很大,用错身份不会报错,只会返回空结果或错数据:

  • bot 看不到用户的个人资源。 用 --as bot 查日程,返回的是 bot 自己那个空日历,不是你的日程。查云空间同理。
  • bot 无法代表用户操作。 用 bot 发消息是以应用名义发出,用 bot 创建文档归属于 bot 而不是你。

所以凡是查「我的」东西,一律加 --as user。本文所有示例都显式带上,避免依赖默认值。

2.3 查看当前身份

lark-cli whoami# 最快,看实际生效的身份lark-cli auth status --json --verify  # 详细:登录态、token 有效期、已授权 scope 全表

whoami 的输出:

{"profile":"cli_xxxxxxxxxxxx","appId":"cli_xxxxxxxxxxxx","brand":"feishu","defaultAs":"auto","identity":"user","identitySource":"auto_detect","available":true,"tokenStatus":"needs_refresh","onBehalfOf":{"userName":"<你的名字>","openId":"ou_xxxxxxxx"}}

tokenStatus 是 needs_refresh 不影响使用,下次调用会自动刷新。但 auth status 里的 refreshExpiresAt 一旦过期,就必须重新 login。


三、认证与授权

3.1 首次配置

lark-cli config init --new    # 会阻塞,等待你打开链接完成应用配置

命令输出里的 verification_url 可以转成二维码方便手机扫:

lark-cli auth qrcode <url> --output ./qr.png   # PNGlark-cli auth qrcode <url> --ascii             # 终端里直接显示

URL 要当作不可修改的字符串,别做 URL 编码、别重新拼 query,否则会失效。

3.2 授权

auth login必须指定范围,三种方式可叠加:

lark-cli auth login --domain all                      # 全部权限lark-cli auth login --domain docs --domain drive      # 按业务域,可重复或逗号分隔lark-cli auth login --scope "drive:file:view_record:readonly"# 按具体 scope,最小权限,推荐lark-cli auth login --recommend                       # 只申请推荐的自动批准 scopelark-cli auth login --scope "..." --exclude drive:file:download  # 排除某些 scope

多次 login 的 scope 会累积(增量授权),不会互相覆盖。

3.3 权限不足怎么办

错误信封会直接告诉你缺什么以及怎么修:

{"ok":false,"identity":"user","error":{"type":"authorization","subtype":"missing_scope","message":"missing required scope(s): contact:user.basic_profile:readonly","hint":"run `lark-cli auth login --scope \"contact:user.basic_profile:readonly\"` ...","missing_scopes":["contact:user.basic_profile:readonly"]}}

照 hint 执行即可。注意区分身份:

  • user 身份缺权限 → 跑 auth login --scope "<缺失的 scope>"
  • bot 身份缺权限 → 不要执行 auth login,bot 没有 login 流程。要去开发者后台开通,错误里的 console_url 就是入口

3.4 权限自查

lark-cli auth check --scope "drive:file:view_record:readonly"# 多个用空格分隔lark-cli auth scopes    # 应用侧已开通的全部权限

auth check 返回 granted 和 missing 两个数组,比在 auth status 那一大串 scope 里肉眼找要快:

{"granted":["drive:file:view_record:readonly"],"missing":null,"ok":true}

3.5 退出与撤销

lark-cli auth logout# 只清本机登录态,返回 loggedOut:true

auth logout不会撤销服务端的授权,也无法单独撤销某一个 scope。要彻底取消授权,得由用户到飞书的授权管理页面操作。

3.6 多账号

lark-cli profile list           # 列出全部 profilelark-cli profile add <name>     # 新增lark-cli profile use <name>     # 切换,用 - 可以切回上一个lark-cli profile rename <old> <new>lark-cli profile remove <name>

一个 profile 对应一套 appId 加登录态,多租户或多应用场景用它隔离。


四、输出与解析

4.1 JSON 信封

成功和失败的结构不同,且走不同的流:

成功 → stdout,退出码 0:

{"ok":true,"identity":"user","data":{ ... },"meta":{"count":1}}

失败 → stderr,退出码非 0:

{"ok":false,"identity":"user","error":{"type":"...","subtype":"...","code":1069604,"message":"...","hint":"..."}}

判断成功必须用 ok == true 或进程退出码,不要用 code == 0 成功信封里根本没有顶层 code 字段,code 只出现在错误信封的 error 内部,含义是上游 OpenAPI 的数字错误码。如果按飞书 OpenAPI 老格式 {"code":0,"msg":"ok"} 去判断,所有成功调用都会被误判成失败。写入类操作里这个误判很危险,可能绕过幂等逻辑造成重复创建。

因为错误走 stderr,脚本里写 2>/dev/null 会把错误信息一起丢掉。调试阶段建议保留 stderr。

4.2 去掉 _notice 噪音

每次调用返回里都可能带 _notice,提示新版本或技能版本脱节,会干扰 jq 解析和阅读。两个环境变量可以关掉:

LARKSUITE_CLI_NO_UPDATE_NOTIFIER=1 LARKSUITE_CLI_NO_SKILLS_NOTIFIER=1 lark-cli whoami

脚本里建议直接 export 一次。或者用 jq 'del(._notice)' 事后删掉。

4.3 过滤与格式

--jq '<表达式>'# 内置 jq 过滤--format json|pretty|table|ndjson|csv--json            # --format json 的简写

内置 --jq不支持 --arg,只接受表达式本身。需要注入外部变量时改用系统 jq:

lark-cli drive file.view_records list --file-token "$tk" --file-type docx --as user | \  jq -r --arg n "$title"'.data.items[]? | [$n, .name, .last_view_time] | @tsv'

写 --jq 之前先用 schema 看 outputSchema,或者先跑一次不带过滤的命令看真实结构。不同接口的返回字段名不统一(详见第九节)。

4.4 分页

--page-size N      # 每页条数,各接口上限不同,常见 50 / 200--page-all         # 自动翻页--page-limit N     # 配合 --page-all 的最大页数,默认 10,0 表示不限--page-token TOKEN # 手动指定起始页,传了它就只取单页

大数据量务必配 --page-limit,默认只翻 10 页容易以为数据不全,设成 0 又可能跑很久。


五、安全与风险门禁

5.1 风险三级

每条命令的 --help 顶部会标注,schema 输出里对应 _meta.risk

等级
含义
要求
read
只读
write
写入
无强制门禁,但仍应确认意图
high-risk-write
高危写入(删除、清空、覆盖等)
必须加 --yes

5.2 exit 10 确认门禁

高危命令不带 --yes 时,CLI 会以退出码 10 拒绝执行,并返回结构化提示:

{"ok":false,"identity":"user","error":{"type":"confirmation","subtype":"confirmation_required","message":"drive +delete requires confirmation","hint":"add --yes to confirm","risk":"high-risk-write","action":"drive +delete"}}

这不是普通报错,是设计好的确认环节。正确处理方式:把 actionrisk 和关键参数摆给使用者看,明确说明这是高危操作,得到同意后再在原命令末尾追加 --yes 重试。被拒绝就终止,不要改参数绕过。

自动化脚本里尤其不要「见到 exit 10 就自动补 --yes 重试」,那等于把这道门禁彻底废掉。

5.3 用 dry-run 预览

lark-cli drive +delete --file-token <token> --type docx --dry-run --as user

--dry-run不触发确认门禁,退出码 0,会打印完整的请求详情(method、url、params、body)。危险操作执行前先用它看一眼实际会发什么请求。

5.4 路径限制

--file--output--output-dir@file 这类路径参数只接受当前目录下的相对路径,传绝对路径会被拒:

unsafe output path: --output must be a relative path within the current directory,got "/tmp/abs_test.bin" (hint: use a relative path like ./filename; ...)

所以下载前要先 cd 到目标目录,再用 --output ./name.ext。需要读取工作目录之外的文件时,支持 stdin 的参数可以用 - 代替路径。大 JSON 体也建议走 stdin,省去转义麻烦。

5.5 其他

  • 不要把 appSecret、accessToken 打印到终端。config show 已自动把 secret 显示为 ****
  • 写入和删除前确认意图,这一条比什么门禁都重要。

六、云空间与云文档(drive)

6.1 先拿 token

绝大多数命令的入口参数是 token。URL 路径本身就能看出 token 类型:

URL 形式
token 含义
/docx/<token>
/doc/<token>/sheets/<token>
可直接当file_token 用
/drive/folder/<token>
folder_token
/wiki/<token>
node token,不能直接当 file_token,必须解包
/page/<token>
妙搭应用 token,file_type=apps

知识库链接必须先解包:

lark-cli drive +inspect --url "https://example.feishu.cn/wiki/<node_token>" --as user

返回 typetitletokenurl;wiki 场景会额外返回 wiki_node 子对象,含 space_idnode_tokenobj_tokenobj_type。传裸 token 而非完整 URL 时需要额外指定 --type

什么时候该用 +inspect:URL 是 wiki 链接、或者 token 来源不明、或者需要顺带拿到标题和规范化 URL。如果 URL 路径已经明确是 /docx//sheets/ 这类,token 可以直接用,不必把 +inspect 当固定前置步骤。

文件类型枚举:doc(旧版文档)、docx(新版文档)、sheetbitablefilefolderwikimindnoteminutesslides+search --doc-types 另外还支持 catalog 和 shortcut

注意 bitable 是多维表格的内部代号,产品名叫 Base。参数里一律填 bitable,填 base 会失败。

6.2 搜索

最省事的入口,比遍历目录快得多:

# 我拥有的全部文档lark-cli drive +search --mine --page-size 20 --as user \  --jq '.data.results[] | [.title_highlighted, .result_meta.token] | @tsv'# 关键词 + 类型 + 时间窗lark-cli drive +search --query "评测" --doc-types docx,sheet --edited-since 7d --as user# 限定在某个知识库空间内搜lark-cli drive +search --mine --space-ids <space_id> --as user

要点:

  • --mine 是「我拥有的」(服务端按 owner 语义匹配),--created-by-me 是「我创建的」(original creator 语义)。文档转手后两者结果不同。注意底层字段名叫 creator_ids,但实际匹配的是 owner,名字有误导性。--mine 与 --creator-ids 互斥。
  • 返回结构是 .data.results[],元数据在 .result_meta 子对象下,不是.data.items[]。这与其他接口不一致,是最常见的踩坑点。
  • title_highlighted 含高亮标签,实测形如 <h>评测</h>脚本和代码。当标题用(尤其是拿来做文件名)必须先剥掉 <h><hb> 标签。
  • --page-size 默认 15、最大 20,比其他接口小得多。传超限值不会报错,而是静默截断成 20(实测传 25,返回的 page_token 里 size 仍为 20)。想拿更多结果只能翻页。
  • --query 上限 30 个 Unicode 码点(中文按 1 个算),超了服务端直接报 99992402 field validation failed,是报错而非截断
  • 时间参数接受相对值(7d1m1y)、绝对日期(2026-04-01)、RFC3339、Unix 秒。注意 1m 是固定 30 天,不是日历月。my_edit_timemy_comment_time 在服务端按小时聚合,传入亚小时精度会被 snap 并在 stderr 给出提示。
  • --folder-tokens 限定为只搜云空间文档,--space-ids 限定为只搜知识库,两者互斥不能同时用。
  • 返回的 total 官方确认不准。 要统计数量得按实际 results 去重累加,别拿 total 当结论。

6.3 列目录

lark-cli drive files list --page-size 200 --order-by EditedTime --as user          # 根目录lark-cli drive files list --folder-token <folder_token> --page-size 200 --as user  # 指定文件夹

要点:

  • 不填 --folder-token 时返回云空间根目录清单,此时不支持分页,也不返回快捷方式。不要拿根目录的返回条数去推断文档总量。
  • --page-size 上限 200,建议直接给 200。
  • --order-by 可选 EditedTime 或 CreatedTime,配 --direction ASC|DESC
  • 这是原生命令,不递归。要遍历文件夹树得自己对 type=folder 的条目下钻,每个目录维护自己的 page_token,按 has_more=false 收尾。
  • 别把 --page-all 的输出直接喂给 JSON 解析器(多页会拼成多个 JSON 对象),也别 2>&1 之后再解析。

按关键词、时间、归属找文档用 +search;只有在需要完整目录树结构时才用 files list 递归。

6.4 上传下载与导入导出

lark-cli drive +upload --help# 上传本地文件lark-cli drive +download --help# 下载文件lark-cli drive +export --help# 云文档导出为本地文件(docx/sheet/bitable/slides/wiki)lark-cli drive +import --help# 本地文件导入为云文档lark-cli drive +export-download --help# 按 file_token 下载已导出的文件lark-cli drive +task_result --help# 轮询导入/导出/移动/删除等异步任务

导入导出都是异步任务。+export 内部固定最多轮询 10 次、间隔 5 秒;超时返回 ticket 和 timed_out=true 并不代表失败,拿 ticket 继续查即可:

lark-cli drive +task_result --scenario export --ticket <ticket> --file-token <token> --as user

导出格式有硬限制:

格式
适用类型
markdown
只支持 docx
csv
只支持 sheet / bitable,且必须带--sub-id 指定子表
base
只支持 bitable
--only-schema
仅 bitable 可用

导出错误码:1069914 token 与 type 不匹配、1069902 无权限、99991679 缺 scope。

导入的注意事项:并发导入同一位置会冲突,报 232140101 / 232140100 / 233523001,必须串行执行,失败重试不超过 3 次。PPTX 单文件上限 500MB。

另外两个容易绕远路的场景:复制在线文档直接用 drive files copy,不要 +export 再 +import;本地 Excel / CSV 导入成多维表格,第一步是 drive +import --type bitable,不是切到 base 域。改标题用 drive files patch 的 new_title

6.5 本地与云端同步

四个命令方向不同,选错会丢数据,务必看清:

命令
方向
说明
+status
只读比对
输出new_local / new_remote / modified / unchanged 四类差异,不改动任何东西
+pull
云端 → 本地
单向镜像
+push
本地 → 云端
单向镜像,会镜像本地空目录
+sync
双向
两边合并,不删除任何一端多余文件

共同限制:四者都只处理 type=file 的二进制文件,会跳过在线文档(docx、sheet 等)和快捷方式。--local-dir 必须是当前目录下的相对路径,软链接也会被拒。

各自的默认行为差异很大,是数据安全的关键:

  • +status 默认 detection=exact,会下载远端字节流算 SHA-256,流量约等于双端共有文件的总大小。只想快速看差异就加 --quick,只比修改时间。
  • +pull --if-exists 默认 overwrite。重复增量同步推荐改成 smart,也可以用 skip
  • +push --if-exists 默认是 skip 而非 overwrite。原因是 overwrite 依赖灰度中的 version 字段,未开通该能力的租户上会导致整批失败。
  • 重名处理:+pull --on-duplicate-remote 支持 fail(默认)/ rename / newest / oldest+push 只有 fail / newest / oldest没有 rename
  • +sync 的冲突项按 --on-conflict=remote-wins|local-wins|keep-both|ask 处理。

危险参数+pull --delete-local 和 +push --delete-remote 都是 high-risk-write,必须配 --yes,否则在校验阶段就被拒。--delete-remote --yes 还会预检 space:document:delete 权限。有一个传输条目失败时,整个删除阶段会被跳过(这是保护机制)。

动手前先跑 +status --quick 看差异,确认无误再执行。也可以更细粒度地处理:对 new_remote 单独 +download,对 new_local 单独 +upload

6.6 权限与协作者

lark-cli drive +member-add --help# 添加协作者lark-cli drive +apply-permission --help# 向文档所有者申请权限lark-cli drive permission.public get --help# 查看公开链接设置lark-cli drive permission.members auth --help# 校验某人是否具备某权限lark-cli drive permission.members transfer_owner --help# 转移所有者

+member-add 的枚举值(填错会直接报参数校验失败):

参数
可选值
--member-typeemail
 / openid / unionid / openchat / opendepartmentid / userid / groupid / wikispaceid / appid
--permview
 / edit / full_access
--perm-typecontainer
(默认,当前页加子页)/ single_page

一次最多传 10 个 --member-id,它们共享同一组 --member-type--perm--perm-type。真实写入需要 --yes。用 --member-type=wikispaceid 时必须额外给 --member-kind

公开链接设置 permission.public patch 的枚举:

  • link_share_entitytenant_readable / tenant_editable / anyone_readable / anyone_editable / closed
  • share_entityanyone / same_tenant / only_full_access
  • security_entitycomment_entityanyone_can_view / anyone_can_edit(前者另有 only_full_access

加人之前先把 ID 解析出来,别靠试错反推类型:用户用 contact +search-user 取 open_id,群用 im +chat-search 取 chat_id,部门用 api POST /open-apis/contact/v3/departments/search 取 open_department_id

公开权限相关的错误码要单独识别,不要归类成缺 scope

含义
91009
租户策略管控
91010
文档未开启对外分享
91011
 / 91012
密级管控

transfer_owner 不可逆,执行前务必确认。另外「部门 + bot 身份」是已知不支持的组合,--member-type=opendepartmentid 必须配 --as user

6.7 评论

lark-cli drive +list-comments --help# 列出评论,支持 URL 解析和 wiki 解包lark-cli drive +add-comment --help# 添加评论

6.8 版本历史

lark-cli drive +version-history --help# 列出历史版本lark-cli drive +version-get --help# 下载某个版本lark-cli drive +version-revert --help# 回滚到某个版本lark-cli drive +version-delete --help# 删除某个历史版本(高危)

七、知识库(wiki)

7.1 token 体系

知识库是最容易搞混 token 的地方,三种 token 各有用途:

token
指向
用途
space_id
知识库空间
+node-list
 的入口
node_token
空间里的节点
继续向下钻子节点
obj_token
节点背后的文档实体
查内容、查访问记录、查元数据

同一个节点的 node_token 和 obj_token 不同,别混用。 向下遍历用 node_token,一切文档级操作用 obj_token

知识库文档的链接形如 /wiki/<node_token>,直接拿这个 token 去调文档接口会失败,得先用 drive +inspect --url 解包成 obj_token

7.2 常用命令

lark-cli wiki +space-list --page-all --as user                    # 列出可访问的空间,拿数字 space_idlark-cli wiki +node-list --space-id <space_id> --page-all --as user  # 列出空间根节点lark-cli wiki +node-list --space-id <space_id> --parent-node-token <node_token> --as user  # 下钻lark-cli wiki +node-get --help# 按 node_token / obj_token / URL 查节点详情lark-cli wiki +node-create --help# 创建节点lark-cli wiki +move --help# 移动节点,或把云空间文档移入知识库lark-cli wiki +move-to-drive --help# 把知识库节点移出到云空间lark-cli wiki +member-list --help# 空间成员lark-cli wiki +member-add --help# 加空间成员,--member-role 只有 admin / member

要点:

  • 知识库操作一律显式加 --as user--as 默认值是 auto,实测常被解析成 bot,而 bot 看不到你的知识库。my_library 配 bot 身份会直接被拒。
  • +node-list 默认只取一页,--page-size上限 50,超限会直接报 --page-size must be between 1 and 50(与 +search 的静默截断不同,这里是硬校验)。--page-all 还受 --page-limit(默认 10)约束,深目录务必把 --page-limit 设成 0。
  • +member-list 同样默认只返回一页。
  • 空间成员角色只有 admin 和 member 两种,且不支持一步切换角色,要先 +member-remove 再 +member-add
  • 节点移出知识库到云空间用 wiki +move-to-drive,不是 wiki +move 也不是 drive +move
  • 「我的文档库 / 个人知识库 / my_library」属于知识库的个人库,不等于云空间根目录,两者是不同的存储位置。
  • 删除知识空间前必须先解析出真实 space_id,且无论匹配到几个都要把候选列给使用者确认,不能因为「只命中一条」就自动删。

上传文件到知识库节点下仍然走 drive 域:drive +upload --wiki-token <node_token>

7.3 遍历整棵树

+node-list不递归,只返回一层。要遍历整棵树,得靠返回里的 has_child 字段自己递归下钻:

walk() {local parent="$1" depth="$2"  (( depth > 8 )) && return# 加个深度上限防意外local args=(wiki +node-list --space-id "$SID" --page-all --page-limit 0 --as user)  [[ -n "$parent" ]] && args+=(--parent-node-token "$parent")  lark-cli "${args[@]}" \    --jq '.data.nodes[]? | [.node_token, .obj_token, .obj_type, .has_child, .title] | @tsv' \    2>/dev/null | grep -v '^Found' | \while IFS=$'\t'read -r ntoken otoken otype haschild title; doprintf'%s\t%s\t%s\n'"$otoken""$otype""$title" >> "$OUT"    [[ "$haschild" == "true" ]] && walk "$ntoken" $((depth + 1))done}walk "" 0

但先想清楚要不要这么做。 大知识库动辄上千节点,递归遍历再逐个查元数据要跑几分钟。如果目标只是「找我在这个空间里的文档」,一条 drive +search --mine --space-ids <space_id> 就够了,快得多。递归遍历只在需要完整目录树结构时才值得。

--space-id my_library 是个人文档库的别名,只能配合 --as user


八、文档内容读写(docs)

本节的所有命令和输出都实测跑过。开始前先明确一点:官方在 docs --help 里要求写 --content 之前必须先读内置指南,因为 XML 语法有不少硬规则:

lark-cli skills read lark-doc                              # 总览与决策树lark-cli skills read lark-doc references/lark-doc-xml.md   # XML 语法(写入前必读)lark-cli skills read lark-doc references/lark-doc-fetch.md # 读取策略

8.1 读取:先选范围,再选详细度

+fetch 有两个正交的维度,组合使用。

--scope 决定读多少

用途
必要参数
full
默认,读整篇
outline
只看目录,先探结构
--max-depth
 限层级
section
读某个标题下的整节
--start-block-id
range
已知精确起止
--start-block-id
 / --end-block-id-1 表示到末尾)
keyword
只有模糊关键词
--keyword
,用 | 分隔多词,OR 语义

--detail 决定每块多细

用途
simple
默认。纯内容,不含 block id,适合阅读和摘要
with-ids
带 block id,后续要做局部编辑或拼直达链接时用
full
带 block id + 样式属性 + 引用元数据,保真改写时用

原则是局部读优于全量读。 大文档直接读整篇既慢又占空间。

实测示例,以一篇有三个一级标题的文档为例:

D=<文档token># 看目录,先摸结构lark-cli docs +fetch --doc $D --scope outline --max-depth 3 --as user \  --jq '.data.document.content'

输出(标题 id 可直接用于下一步):

<fragmentmode="outline"><outline><h1id="doxcnWhJ...">第一节 概述</h1><h1id="doxcnUL7...">第二节 待办</h1><h1id="doxcnWOT...">第三节 代码</h1></outline></fragment>
# 拿上一步的标题 id 精读某一节lark-cli docs +fetch --doc $D --scope section \  --start-block-id doxcnUL7... --detail with-ids --as user \  --jq '.data.document.content'
<fragmentmode="section"requested-start="doxcnUL7..."><h1id="doxcnUL7...">第二节 待办</h1><ul><liid="doxcndbG...">验证 str_replace</li><liid="doxcnEYO...">验证 block_insert_after</li></ul></fragment>
# 只有关键词时,多词 OR 一次召回lark-cli docs +fetch --doc $D --scope keyword --keyword "部署|发布|上线" --as user# 要给人看的话,markdown 更清爽lark-cli docs +fetch --doc $D --doc-format markdown --as user \  --jq '.data.document.content'

--doc-format 三个值:xml(默认,保留结构和 block id)、markdown(纯导出,适合阅读)、im-markdown(发飞书消息用)。

局部读取的输出结构要看懂。 设了 --scope 后内容被 <fragment> 包裹,里面可能出现 <excerpt top-block-id="..." parent-block-path="...">看到 <excerpt> 就意味着这是节选,不代表你看到了那个块的全貌。 想看全貌就拿 top-block-id 再用 section 或 range 拉一次。

表格默认会瘦身,只返回表头加命中行。要整张表得用 range --start-block-id <table-id> --end-block-id <table-id>

8.2 写入:六种指令

+update --command 的可选值:

指令
作用
必要参数
str_replace
按文本替换,--content 留空即删除匹配内容
--pattern
append
追加到文档末尾
--content
overwrite
整篇覆盖
--content
block_insert_after
在指定块后插入
--block-id
--content
block_replace
替换指定块
--block-id
--content
block_delete
删除块,多个用逗号分隔
--block-id
block_move_after
移动块
--block-id
--src-block-ids
block_copy_insert_after
复制块
--block-id
--src-block-ids

实测示例:

# 改错别字,最简单的写法,不需要 block idlark-cli docs +update --doc $D --command str_replace \  --pattern "临时文档" --content "测试文档" --as user# 在某个标题后插一个高亮框lark-cli docs +update --doc $D --command block_insert_after \  --block-id doxcnWhJ... \  --content '<callout emoji="bulb"><p>这段是插入的提示。</p></callout>' --as user# 用 markdown 追加一段(append / overwrite 场景 markdown 很方便)lark-cli docs +update --doc $D --command append --doc-format markdown \  --content '## 附录- 条目一- 条目二' --as user# 删除若干块lark-cli docs +update --doc $D --command block_delete \  --block-id "doxcnEYO...,doxcndbG..." --as user

格式怎么选:整段写入appendoverwrite+create)XML 和 Markdown 都行,用户给的是 .md 就用 Markdown;局部精修str_replaceblock_*)坚持用 XML,因为它能稳定表达块结构和样式。

8.3 新建文档

lark-cli docs +create --as user \  --content '<title>项目周报</title><p>本周进展如下。</p><h1>已完成</h1><ul><li>接口联调</li><li>压测报告</li></ul><pre lang="bash"><code>make deploy</code></pre>'

返回 document_idrevision_idurl

8.4 XML 语法要点

完整规则见 references/lark-doc-xml.md,以下是最容易出错的几条。

标准 HTML 标签(ph1-h9ulollitableblockquoteprecodebema 等)语义不变。扩展标签常用的有:

标签
说明
<title>
文档标题,每篇唯一
<checkbox done="true|false">
待办项
<callout emoji="bulb">
高亮框,子块只能是文本块、标题、列表、待办、引用,不能放表格、图片、代码块
<grid>
 + <column width-ratio="...">
分栏,各列比例之和为 1
<pre lang="bash"><code>...</code></pre>
代码块
<cite type="user" user-id="ou_xxx">
@人,user-id 必填且必须是 open_id
<bookmark name="标题" href="...">
书签,两个属性都必填

几条硬规则:

  • 转义只针对文本内容,标签本身禁止转义。 正确:<p>A &amp; B,1 &lt; 2</p>;错误:&lt;p&gt;内容&lt;/p&gt;
  • 代码必须放在 <pre> 里层的 <code> 中,不能直接写在 <pre> 下。
  • 列表项必须包在 <ul> 或 <ol> 里,不能裸写 <li>
  • 行内样式标签有固定嵌套顺序(外到内):<a> → <b> → <em> → <del> → <u> → <code> → <span> → 文本,闭合顺序严格反转。
  • 只有纯文本人名、没有 open_id 时,先用 contact +search-user --query "名字" --as user 反查,再写 <cite>

8.5 两个必须知道的陷阱

陷阱一:写入失败时 ok 仍然是 true

这是 docs +update 最容易踩的坑。str_replace 没匹配到内容、或者用了已失效的 block id,返回是这样的:

{"ok":true,"data":{"document":{"revision_id":7},"result":"failed","warnings":["degrade_code=1011,msg=Instruction produced no document changes. ..."]}}

外层 ok: true退出码也是 0(实测确认),但 data.result 是 "failed",文档实际没有任何改动。所以判断写入是否真正生效,必须检查 data.result == "success",光看 ok 会以为改成功了。脚本里务必这样写:

res=$(lark-cli docs +update --doc $D --command str_replace \  --pattern "旧文本" --content "新文本" --as user --jq '.data.result')[[ "$res" == "success" ]] || echo"写入未生效,检查 pattern 是否匹配"

陷阱二:block id 会失效。

overwriteblock_replaceblock_delete 之后,受影响的旧 block id 就不能再用了;插入和复制产生的新块,得重新 fetch --detail with-ids 才能拿到 id。连续做多个写操作时不要复用上一轮的 id,否则就会命中陷阱一那种静默失败。

8.6 其他命令

lark-cli docs +media-insert --help# 插入本地图片/附件,四步编排且失败自动回滚lark-cli docs +media-insert --from-clipboard --doc $D --as user  # 剪贴板截图直接插入lark-cli docs +media-download --help# 下载文档里的媒体lark-cli docs +media-preview --help# 预览媒体lark-cli docs +history-list --help# 历史版本lark-cli docs +history-revert --help# 回滚到某个版本lark-cli docs +resource-update --type cover --help# 换封面图lark-cli docs +whiteboard-update --help# 用 mermaid / plantuml / DSL 更新画板

截图已在剪贴板时优先用 --from-clipboard,比先存盘再 --file 省一步。

8.7 跨域跳转

docs 只管文档正文。读到内容里有这些标签时,要提取 token 切到对应领域:

标签
提取
去哪
<sheet token="..." sheet-id="...">
token、sheet-id
sheets
 域
<bitable token="..." table-id="...">
token、table-id
base
 域
<vc-transcribe-tab vc-node-id="...">
vc-node-id 当 note_id
note +detail
<synced_reference src-token="...">
src-token
docs +fetch 读源文档

另外几个容易走弯路的:文档评论走 drive +list-comments / +add-comment;复制文档用 drive files copy,别用 +fetch 加 +create 重建;画板细节看 lark-whiteboard 指南。


九、常用查询实例

9.1 文档被谁看过

lark-cli drive file.view_records list \  --file-token <token> --file-type docx --page-size 50 --as user

返回 .data.items[],每项含 name(访问者姓名)、viewer_idavatar_urllast_view_time(Unix 秒)。--viewer-id-type 可选 open_id(默认)、user_idunion_id

关键限制:返回的是每个人的最近一次访问时间,不是完整流水。 所以「某人今天有没有看过」判断准确(看过就会刷新成当天),但「同一个人今天看了几次」拿不到。

需要 drive:file:view_record:readonly 和 contact:user.base:readonly 权限,且飞书侧仅付费版本开放访问记录能力。

9.2 只要聚合数字

lark-cli drive file.statistics get --file-token <token> --file-type docx --as user

返回 pvuvpv_todayuv_todaylike_countlike_count_today。判断「今天有没有人看过」,看 uv_today 比逐篇翻访问记录快得多。

9.3 批量查文档归属

一次最多 200 个 token:

lark-cli drive metas batch_query --as user \  --data '{"request_docs":[{"doc_token":"<token>","doc_type":"docx"}],"with_url":false}' \  --jq '.data.metas[]? | [.doc_token, .owner_id, .title, .latest_modify_time] | @tsv'

返回里还有 create_timelatest_modify_usersec_label_name(密级标签)。失败的 token 单独放在 .data.failed_list,错误码含义:

含义
970002
文档类型不支持
970003
无权限获取该文件元数据
970005
token 与 doc_type 不匹配,或文件不存在

注意返回顺序与请求顺序不一致,需要按 doc_token 自己对齐。


十、其他领域速查

10.1 消息与群聊(im)

lark-cli im +chat-list --as user           # 我加入的群,默认只返回群聊lark-cli im +chat-list --types p2p,group --as user   # 含单聊(仅 user 身份)lark-cli im +chat-search --query "<群名>" --as user   # 按群名找 chat_idlark-cli im +chat-messages-list --help# 拉取会话消息,支持时间范围lark-cli im +chat-members-list --help# 群成员,users[] 与 bots[] 分开返回lark-cli im +chat-create --help# 建群lark-cli im +feed-group-list --as user     # 消息流分组(标签)

10.2 日历(calendar)

lark-cli calendar +agenda --as user        # 日程,默认今天lark-cli calendar +search-event --help# 按关键词、时间、参与人搜日程lark-cli calendar +create --help# 建日程并邀请参与人lark-cli calendar +update --help# 改日程,增删参与人lark-cli calendar +rsvp --help# 接受 / 拒绝 / 待定lark-cli calendar +freebusy --help# 查忙闲lark-cli calendar +suggestion --help# 模糊时间范围里智能推荐空档lark-cli calendar +room-find --help# 找可用会议室

10.3 任务(task)

lark-cli task +get-my-tasks --as user      # 分配给我的任务lark-cli task +get-related-tasks --as user # 与我相关的任务lark-cli task +search --helplark-cli task +create --helplark-cli task +complete --help / +reopen --helplark-cli task +assign --help / +followers --help / +reminder --helplark-cli task +tasklist-create --help

飞书任务和审批待办是两回事,审批待办走 approval 域。

10.4 会议与妙记(vc / minutes / note)

lark-cli vc +search --help# 搜会议记录,至少要给一个过滤条件lark-cli vc +detail --help# 会议详情,含 note_id 和 minute_tokenlark-cli vc +recording --help# 按会议 ID 或日程 ID 查 minute_tokenlark-cli minutes +search --help# 搜妙记lark-cli minutes +detail --help# 妙记详情,可选摘要/待办/章节/转写/关键词lark-cli minutes +download --help# 下载音视频lark-cli note +transcript --help# 会议笔记的统一转写,存到文件

10.5 表格与多维表格(sheets / base)

lark-cli sheets +cells-get --help# 读区域,含值、公式、样式、评论lark-cli sheets +cells-set --help# 写值 / 公式 / 样式 / 数据校验 / 嵌入图片lark-cli sheets +cells-search --help / +cells-replace --helplark-cli sheets +batch-update --help# 多个写操作打包成一个原子请求,失败回滚lark-cli base +base-create --help# 建多维表格lark-cli base +base-get --helplark-cli base +dashboard-block-create --helplark-cli base +advperm-enable --help# 开启高级权限

sheets +cells-clear+cells-batch-clear 不可逆,属于高危。

10.6 邮件(mail)

lark-cli mail +messages --help# 批量读邮件正文,自动按 20 个一批lark-cli mail +draft-create --help# 新建草稿(不用于回复/转发)lark-cli mail +reply --help / +reply-all --help / +forward --helplark-cli mail +draft-send --help# 发送已有草稿lark-cli mail +lint-html --help# 检查邮件 HTML 兼容性,只读不发lark-cli mail +message-trash --help# 软删除,需要 --yes

回复和转发默认只存草稿不发送,要立即发送得加 --confirm-send。这个默认值很友好,能避免误发。读多封邮件用 +messages 而不是循环调 +message

10.7 通讯录、审批、OKR、考勤

lark-cli contact +get-user --as user       # 不传 user_id 就是查自己lark-cli contact +search-user --help# 搜人,需要 user 身份lark-cli approval --help# 审批实例与待办lark-cli okr +cycle-list --help / +cycle-detail --helplark-cli attendance --help# 考勤打卡记录lark-cli apps --help# 妙搭应用开发与托管lark-cli event --help# 实时事件订阅

十一、内置技能文档

CLI 自带 27 份领域指南,内容比 --help 详实得多,包含完整工作流、字段说明和可复制示例。

lark-cli skills list              # 列出全部技能及适用场景lark-cli skills read lark-drive   # 读某一份

清单:lark-approvallark-appslark-attendancelark-baselark-calendarlark-contactlark-doclark-drivelark-eventlark-imlark-maillark-markdownlark-minuteslark-notelark-okrlark-openapi-explorerlark-sharedlark-sheetslark-skill-makerlark-slideslark-tasklark-vclark-vc-agentlark-whiteboardlark-wikilark-workflow-meeting-summarylark-workflow-standup-report

其中 lark-shared 是跨领域的公共约定(身份、认证、JSON 契约、安全规则),建议第一份就读它。

技能内容随 CLI 版本发布,要用 skills read 获取,不要去翻本地的 SKILL.md 文件,避免版本不一致。lark-cli update 会同时更新二进制和技能。


十二、踩坑记录

以下都是实际使用中验证过的。

JSON 前有干扰行。 部分命令(wiki +space-list+node-list 等)会在 JSON 之前打印一行 Found N item(s),直接管道给系统 jq 会报解析错误。用内置 --jq,或者加 | grep -v '^Found'

返回字段名不统一。 多数接口是 .data.items[],但 drive +search 是 .data.results[] 且元数据在 .result_meta 下,wiki +node-list 是 .data.nodes[]wiki +space-list 是 .data.spaces[]drive files list 是 .data.files[]metas batch_query 是 .data.metas[]。写 jq 前先跑一次看结构。

搜索结果的标题带高亮标签。title_highlightedsummary_highlighted 里含 <h> / <hb> 标签,实测形如 <h>评测</h>脚本和代码。直接拿去做文件名或字符串比对都会出错,先 gsub("</?hb?>"; "") 剥掉。

total 字段不可信。drive +search 返回的 total 官方明确说不准,统计数量要按实际 results 去重累加。

--page-size 超限的行为不统一。drive +search 会静默截断(传 25 实际按 20 取,不报错),wiki +node-list 则直接报 --page-size must be between 1 and 50。前者更危险:以为取了 25 条其实只有 20 条,容易误判「数据就这么多」。各接口上限不同,先用 schema 看 maximum 字段确认。

--as 默认是 auto,容易落到 bot。 查个人资源(知识库、云空间、日历、邮箱)时 bot 身份会返回空结果而不报错,非常难察觉。所有涉及「我的」数据的命令都显式写 --as user

metas batch_query 的返回顺序与请求顺序不一致。 要按 doc_token 自己对齐,不能按下标取。

不能用 +inspect 判断文档是否存在。 实测文档删除后,+inspect 依然正常返回 title 和 tokenok: true),只有 docs +fetch 才会报 3380003 Document page has been deleted。要确认文档还在不在,用 +fetch 或 file.statistics get 试探,别拿 +inspect 当存在性检查。

评论内容不能含尖括号。< 和 > 需转义成 &lt; / &gt;。用 +add-comment 会自动兜底,直接调原生 API 得自己处理。

--jq 不支持 --arg 要注入变量就改用系统 jq,见 4.3。

错误走 stderr。 脚本里图省事加 2>/dev/null,会把错误信封连带 missing_scopeshint 一起丢掉,排查时反而更慢。

别用 code == 0 判断成功。 见 4.1,这是会导致重复写入的隐患。

docs +update 写入失败时 ok 仍然是 true 这条单独拎出来说,因为它和上面那条方向相反、危害更隐蔽。str_replace 没匹配到、或用了失效的 block id,返回是 ok: true 且退出码 0,但 data.result 是 "failed",文档实际没改动。判断写入是否生效必须查 data.result == "success",详见 8.5。

绝对路径被拒。--output 之类只收相对路径,见 5.4。

macOS 自带 bash 是 3.2,不支持 wait -n 写并发脚本时 while (( $(jobs -rp | wc -l) >= 5 )); do wait -n; done 会直接报 invalid option,导致限流完全失效、所有请求一起冲出去。改用分批 wait:

i=0for x in ...; do  query "$x" &  i=$((i+1))if (( i % 6 == 0 )); thenwaitfi# 每 6 个等一批donewait

管道子 shell 里的后台任务等不到。cat list | while read x; do cmd & done; wait 这种写法,while 跑在子 shell 中,外层 wait 等不到里面的后台任务,于是统计跑在任务完成之前,拿到的是中间结果。改成 while ... done < file 的重定向形式。

并发写同一文件时别急着统计。 后台任务还在追加内容,此时跑 wc -luniq -c 得到的是快照,前后两次结果会互相矛盾。等 wait 真正返回后再汇总。

时间戳都是 Unix 秒。 macOS 用 date -r <秒> +"%m-%d %H:%M",Linux 用 date -d @<秒>

统计中文姓名前先设 LC_ALL=C 否则 sortuniq 在某些 locale 下行为异常。


十三、实用片段

13.1 查今天谁看过我的文档

#!/bin/bashexport LARKSUITE_CLI_NO_UPDATE_NOTIFIER=1 LARKSUITE_CLI_NO_SKILLS_NOTIFIER=1CLI=~/.local/bin/lark-cliTODAY=$(date -j -f "%Y-%m-%d %H:%M:%S""$(date +%Y-%m-%d) 00:00:00" +%s)$CLI drive +search --mine --doc-types docx --page-size 20 --as user 2>/dev/null | \  jq -r '.data.results[] | [.result_meta.token, .title_highlighted] | @tsv' | \while IFS=$'\t'read -r token title; do$CLI drive file.view_records list \    --file-token "$token" --file-type docx --page-size 50 --as user 2>/dev/null | \  jq -r --arg n "$title" --arg d "$TODAY" \'.data.items[]? | select((.last_view_time|tonumber) >= ($d|tonumber))     | [$n, .name, .last_view_time] | @tsv'done

13.2 读者排行

输入为上面产出的 TSV(第二列是访问者姓名):

export LC_ALL=CME="<你的名字>"awk -F'\t' -v me="$ME"'$2 != me {print $2}' views.tsv | sort | uniq -c | sort -rn | head -15

13.3 时间戳转可读时间

awk -F'\t''{cmd="date -r "$3" +\"%m-%d %H:%M\""; cmd|getline t; close(cmd); printf "%s  %-9s %s\n", t, $2, $1}' views.tsv

13.4 批量导出文档为本地文件

#!/bin/bashexport LARKSUITE_CLI_NO_UPDATE_NOTIFIER=1 LARKSUITE_CLI_NO_SKILLS_NOTIFIER=1CLI=~/.local/bin/lark-climkdir -p ./exports && cd ./exports || exit 1   # 路径参数只收相对路径,必须先 cd# 注意 --page-size 上限 20;title_highlighted 含 <h> 标签,用 gsub 剥掉再当文件名$CLI drive +search --mine --doc-types docx --page-size 20 --as user 2>/dev/null | \  jq -r '.data.results[] | [.result_meta.token, (.title_highlighted | gsub("</?hb?>"; ""))] | @tsv' | \while IFS=$'\t'read -r token title; do  safe=$(printf'%s'"$title" | tr'/:''__')$CLI drive +export --file-token "$token" --type docx --output "./${safe}.docx" --as user# 若返回 timed_out=true,用 +task_result --scenario export --ticket <ticket> 继续查done

导出整个文件夹(含子目录)的完整流程:

drive files list --folder-token <token> --page-size 200   # 对 type=folder 递归下钻  → drive +export --url <每个文档的 url> --file-extension <fmt>  → 若 ready=false:drive +task_result --scenario export --ticket <T> --file-token <token>  → drive +export-download --file-token <导出后的 token>