适用读者: 用 lark-cli 自动操作飞书云文档 / 通讯录 / 任务 / 日历的独立开发者
关键词: lark-cli / 飞书 CLI / 飞书 OAuth / 飞书 scope / 飞书自动化 / Hermes Agent

在这里插入图片描述

一、为什么我需要知道自己的 scope

笔者在用 Hermes Agent 搭个人 AI 助手时, 经常遇到这种事: 写好的脚本突然报 permission denied, 检查半天发现是当前身份没拿到对应 scope.

lark-cli 的 scope 体系结构跟 GitHub PAT / Slack token 不一样 —— 不是 1 个 token 搞定一切, 而是 137 个独立 scope 按 domain:resource:action 形式精确划分. 写自动化时, 第一步必然是: 清点你已经拿到哪些 scope, 哪些没拿到.

下面给作者一步步走过的清单: 1 行命令拉所有 scope, 5 个常用 scope 的真实用法, 怎么补齐缺失 scope.

二、1 行命令看全部 scope

# 列出当前身份已开通的全部 scope (按应用 ID 匹配 lark-cli 配置)
lark-cli auth scopes --app-id cli_xxxxxxxxxxxxxxxx

# 输出结构 (JSON 数组):
# [
#   { "scope": "contact:user.id:readonly", "granted": true,  "type": "user" },
#   { "scope": "im:message",              "granted": true,  "type": "tenant" },
#   { "scope": "im:message.group_at_msg", "granted": false, "type": "user" },
#   ...
# ]

如果当前是 bot 身份, 默认只能用 tenant 级别 scope (e.g. im:message). 如果要 user 级别 scope (e.g. im:message.send_as_user), 必须走 device flow 登 user 身份, 见 7.5 节.

权威文档: lark-scope-list (飞书开发者中心官方, 全 137 个 scope 的权限矩阵).

三、5 个最常用 scope + 1 行跑通 demo

下面 5 个 scope 是作者在 Hermes Agent 6 件基础设施里实际用到的, 每个都配 1 行真实命令.

3.1 contact:user.id:readonly (读用户基本信息)

能力: 按 user_id 拿用户姓名 / 头像 / 部门 / 手机号 (含 ID 反查)

# bot 身份可以调, 但只能拿 open_id + union_id + mobile_visible=true 的字段
lark-cli contact +get-user --user-id ou_5bd5540825ba46b071285c220fa19d48

风险: bot 身份拿不到 name / mobile / department 字段, 需要 user 身份 + contact:user:readonly.

3.2 im:message (发消息, bot 身份默认有)

能力: 飞书机器人发消息到任何 chat_id.

# 发文本消息到作者私聊
lark-cli im +messages-send \
  --chat-id oc_xxxxxxxxxxxxxxxxxxxxxxxx \
  --text "Hello from Hermes Agent"

# 发卡片消息 (含跳转链接 / 按钮)
lark-cli im +messages-send \
  --chat-id oc_xxxxxxxxxxxxxxxxxxxxxxxx \
  --msg-type interactive \
  --content '{"msg_type":"interactive","card":{"header":{"title":{"tag":"plain_text","content":"DONE"}},"elements":[{"tag":"div","text":{"tag":"plain_text","content":"task completed"}}]}}'

3.3 drive:file:readonly (查文件元信息)

能力: 按 URL / token 查飞书 docx / sheet / bitable 的标题 / 创建人 / 最后修改时间.

# 看一份飞书 docx 的元信息 (标题 / 创建者 / 修改时间)
lark-cli drive +inspect --url "https://fcn6ia9v80zp.feishu.cn/docx/xxxxxxxxxxxxxxxxxxxxxxxx"

关键坑 (作者亲踩, 永久生效):

  • --token 报 unknown flag, 必须用 --url (接受完整 URL 或 token)

  • v1 API 拿 content, v2 API 只拿 title (写飞书 docx 时 v1 改字符串 v2 改 title)

3.4 docs:document:readonly (读 docx 全文)

能力: 拉飞书 docx 全部 content 字段.

# v1 模式拿完整 content (XML 格式)
lark-cli docs +fetch --doc <TOKEN> --api-version v1 --format json

# v2 模式 (markdown 格式, content 含 title + 转义)
lark-cli docs +fetch --doc <TOKEN> --api-version v2 --format json

作者实战 6/29 沉淀:

  • v1 适合写脚本时拉全文 str_replace

  • v2 适合人眼 review markdown

  • 两个 API 返的 content 字段不一样, 不要混用

3.5 docs:document (写 docx, str_replace)

能力: 在已有飞书 docx 中按字符串精确替换 / 末尾追加.

# 重点: 必须 --command str_replace + --pattern + --content + --doc-format markdown
lark-cli docs +update --doc <TOKEN> --doc-format markdown \
  --command str_replace \
  --pattern "<旧字符串>" \
  --content "<新字符串>"

4 个坑 (作者实测):

  1. ❌ 默认 --command overwrite 静默覆盖全文 (作者批注全丢, 永久警告)

  2. ❌ v1 markdown 模式 pattern 找不到 <title>...</title> 字面 (原因是 XML 模式渲染) → 用 v1 --doc-format xml 才能改 title

  3. ❌ v2 markdown 模式 < 必须 \< 转义, 否则视为 XML 标签触发富文本

  4. ✅ fetch + grep COUNT=1 验证唯一性, 改完 fetch revision+1 + 旧字符串 COUNT=0 + 新字符串 COUNT=1

四、scope 分类速查表

按权限域分组, 方便找对应 scope:

关键 scope用途
通讯录 (contact)contact:user.id:readonly / contact:user:readonly / contact:user.email:readonly反查用户信息
即时消息 (im)im:message / im:message:send_as_bot / im:message:send_as_user / im:message.group_at_msg发消息 / @ 群 / 私聊
云文档 (docs)docs:document / docs:document:readonly / docs:document.content:readonlydocx CRUD
云盘 (drive)drive:file / drive:file:readonly / drive:drive文件管理
多维表格 (base)base:app / base:app:readonly / base:table / base:record飞书 Base
日历 (calendar)calendar:calendar / calendar:calendar.event / calendar:event日程管理
任务 (task)task:task / task:task:readonly / task:tasklist任务协作
审批 (approval)approval:approval / approval:approval:readonly审批流
邮件 (mail)mail:mail / mail:mail:readonly / mail:contact邮箱
妙记 (minutes)minutes:minutes / minutes:minutes:readonly会议妙记

(完整 137 scope 见官方文档, 上表为作者高频 25 个精选)

五、device flow 拿 user 身份 scope (关键路径)

bot 身份能做的事有限 (不能发 user 风格消息, 不能读 137 个私有 scope). 想用全 137 scope 必须登 user 身份.

5.1 步骤

# 1. 触发 device flow 拿 device_code + verification_url
lark-cli auth login --no-wait --json --scope "im:message,im:message:send_as_user,contact:user:readonly"

# 输出 JSON, 关键 2 字段:
# data.device_code = "ABCDEFG..."
# data.verification_uri = "https://accounts.feishu.cn/oauth/v1/device/verify?code=XYZ&user_code=..."
# 还有 qr_code_url 可以 QR 码化

# 2. 把 verification_uri 给用户扫码 (10 分钟过期)
# 用户在飞书 App 扫一下, 同意授权, 浏览器自动跳走

# 3. 用 device_code 换 access_token
lark-cli auth login --device-code <device_code>

# 输出 ok=true + data.access_token + data.refresh_token + data.expires_in

5.2 user 身份跟 bot 身份切换

# 默认身份
lark-cli config default-as     # 显示当前

# bot vs user 命令级 (单次生效)
lark-cli --as user contact +get-user --user-id <open_id>
lark-cli --as bot im +messages-send --chat-id <id> --text "..."

# 默认长期切到 user (影响所有 cron, 谨慎)
lark-cli config default-as user

5.3 4 个 lark-cli 关键坑 (踩了 3 周才搞明白)

  1. --scope** 是 additive 追加**, 不是替换. 第一次 login 拿 5 个 scope, 第二次想加 1 个, 必须传全部 6 个 (老 5 + 新 1)

  2. lark-cli auth status** 的 identities.user.status=missing** = user 没登, 报 “Bot identity can NOT get current user info” 是 expected

  3. 默认 strict-mode=bot: --as user 命令会被拒, 跑 lark-cli config strict-mode off 才能 mix

  4. refresh_token 默认 7 天过期, 过期前 7 天 lark-cli 自动 refresh, 7 天后作者需重新 device flow

六、scope 名字格式 3 件套

lark-cli 文档对每个 scope 给的格式是 domain:resource:action. 实战中作者发现 3 个反直觉点:

6.1 单层冒号 (不是路径风格)

✅  contact:user.id:readonly           # 飞书官方格式 (单层冒号)
✅  im:message.send_as_user           # action 可以是下划线连字符
❌  contact/user/id:readonly          # 不要斜杠
❌  contact.user.id.readonly          # 不要点号

6.2 read vs readonly 区别

✅  drive:file:readonly               # 多数情况用 readonly
✅  drive:file                        # 含 read + write
❌  drive:file:read                   # 不存在 (只有 readonly)

6.3 wildcard scope (2026 飞书新增)

✅  im:message.*                      # 表示 im:message 下所有 action
✅  contact:user.*:readonly          # 通讯录用户读全部子资源

风险: wildcard 申请难度 > 精确 scope. 决策优先级:

  1. 先精确申请, 缺啥加啥

  2. 强烈需要批量时再 wildcard

  3. wildcard 申请仍然受飞书应用类型 (个人 vs 企业) 限制

七、137 个 scope 全清单获取

7.1 官方 API 一行查

# 用 lark-cli 直接拉 (需 application:application:readonly scope, 通常企业版默认有)
lark-cli api GET /open-apis/application/v6/applications/<app_id>/app_version

# 返 JSON, .data.app_version.scopes 含全部申请记录 + .data.app_version.scopes_added 是已批准

7.2 离线清单 (本地 grep)

# lark-cli binary 自带 schema, 可以查全 scope
lark-cli schema --format pretty 2>&1 | grep -oE '"scope[\.a-z_:]+"' | sort -u > /tmp/all_scopes.txt
wc -l /tmp/all_scopes.txt
# 期望: 137 行

7.3 本文 vs 实时

137 这个数字会随着飞书功能更新变动 (作者写文时是 v1.0.53). 强求最新数:

lark-cli update  # 自升级到最新 binary (1.0.66 已发布, 含 9 个新 scope)
lark-cli schema | grep -c scope   # 实时数

七.5、作者亲历的 6 次 scope 申请踩坑 (实战)

下面 6 个坑是作者 2026 年 6/29-7/5 在 lark-cli 上踩的真实案例, 每个坑都对应一个 scope 申请动作.

坑 1: im:message:send_as_user 个人 app 全拒

申请后飞书开发者中心返 “权限范围受限, 企业专享”. 第一次以为是 app 配错, 重配 3 次都拒. 后来查官方文档才知道这是企业自建应用专享 scope.

正解:

  • 个人 app (如作者的) → 只能 im:message (以 bot 身份发)

  • 企业自建 app → 可申请 im:message:send_as_user (以 user 身份发)

  • 作者独立开发者需企业认证才能升级

坑 2: contact:user.id:readonly bot 身份只返 open_id

bot 身份调 contact +get-user --user-id ou_xxx, 返 JSON 的字段是:

{
  "open_id": "ou_xxx",
  "union_id": "on_xxx",
  "mobile_visible": true
}

**没有 name / mobile / **department 这些字段. 跟官方文档描述的"读用户基本信息" 不一致. 实际是 bot 身份 = 只看到权限允许的部分字段.

正解: 登 user 身份 + contact:user:readonly 才能拿全字段.

坑 3: drive:file:readonly 对外部链接失败

有人分享了 1 个外部 docx 链接 (e.g. https://other.feishu.cn/docx/yyy)). drive +inspect --url 返 403.

正解:

  • 同一租户内的 docx → bot 身份 + drive:file:readonly 直接 OK

  • 跨租户的 docx → 需要 user 身份 + docs:document:readonly + 对方租户分享授权

坑 4: docs:document 写的 scope 不能写 raw HTML

作者想插入一段 HTML 高亮代码进 docx, docs +update --content @file.html 返 “不支持 raw HTML”.

正解: docx 只支持原生 markdown 元素 + 部分富文本标签 (<b>, <i>, <u>). HTML 块必须先用 markdown 写, 或通过 callout/highlight box 富文本 API 转.

坑 5: base:record 批量插入有 1000 行/次限制

批量同步 13 个 cron 状态到飞书 base 时, 一次 1500 行报 “exceed batch size limit”.

正解: 飞书 base API 单次插入上限 1000 行. 大批量同步必须分批 (chunk), 每批 800 行保安全.

坑 6: calendar:event 改已有事件的 start_time 不带提醒

作者改 1 个日历事件的开始时间, 保存后飞书通知没重置.

正解: calendar:event PATCH 操作不会重置 reminders 字段. 必须显式 PATCH reminders 字段 (e.g. {"reminders": [{"minutes": 5}]}) 才会重发通知.

(这 6 个坑都进了 hermes_mistakes 表 + 沉淀 csdn-blog-orchestrator skill.)

七.10、scope 申请 5 步法 (作者实践)

作者亲用的 scope 申请工作流 (5 步):

  1. 查全 scope 清单 (lark-cli schema | grep scope 或官方文档)

  2. 列本任务必备 scope (写代码前先列, 避免后期卡)

  3. 分企业 / 个人级别 (个人 app 先看是否企业专享)

  4. 填理由 + 截图 (飞书开发者中心, 部分 scope 要写使用场景)

  5. 提交 + 1-3 工作日等审 (快则分钟级, 慢则周级)

作者常用组合 (作者项目实测):

  • IM 通知链: im:message (bot + user 都行)

  • docx 读写链: docs:document + drive:file

  • 云盘同步链: drive:file + drive:drive

  • Base 读写链: base:app + base:table + base:record + base:field

  • 用户身份: contact:user:readonly + im:message:send_as_user

(作者场景仅指笔者项目, 非通用模板)

八、跟 Hermes Agent 基础设施的协同

作者用 137 个 lark-cli scope 在 7 个业务表上跑了 13 个 cron 任务, 5 件核心用法:

8.1 cron 必备 scope

业务用到的 scopecron job_id
早安图 (Cloudinary 推微信)im:message9d42c77bd1fa
每日日报 (飞书填表)base:app / base:table / base:recordf9b0344af0fa
23:10 飞书填表 (深夜 batch)base:app / base:table05f863b1da8b
反编造铁律日报 (DM push)im:messagef6c3520e1f1c
RSS 自动入库 (公众号作者)drive:file / docs:documentsync_boss_documents_wrapper
飞书 OAuth refresh (7 天续)auth:auth.* (user 身份)待建
CSDN 周报 (周日 22:00)im:messagef1d67561baa2

8.2 不需要额外 scope 的命令

下面这些命令是 lark-cli 内置, 不需要额外 scope, 直接 lark-cli xxx 就行:

  • lark-cli doctor (健康检查)

  • lark-cli auth status (当前身份)

  • lark-cli config get/set (配置管理)

  • lark-cli skills read ... (读 skill 文档)

  • lark-cli update (自升级)

八.5、scope 调试三件套 (作者封装的)

作者写 lark-cli 自动化时, 几乎每个脚本都会先跑这 3 件套 (粘贴即用):

三件套 A: scope 清单 + grant 状态

# 0_token 用法: 跑一次, 后面脚本都从这里读
lark-cli auth scopes > /tmp/scopes.json
cat /tmp/scopes.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
scopes = data.get('data', {}).get('scopes', [])
granted = [s for s in scopes if s.get('granted')]
print(f'已开通 {len(granted)}/{len(scopes)} scope')
for s in granted:
    print(f"{s.get('scope')}")
"

三件套 B: 当前身份 + 过期时间

# 0_token 用法: 0_token = 过期 token 列表, 跑一次
lark-cli auth status > /tmp/auth_status.json
python3 -c "
import json, time
d = json.load(open('/tmp/auth_status.json'))
bot = d['data']['identities']['bot']
user = d['data']['identities']['user']
print(f"bot: {bot['status']}")
print(f"user: {user['status']}")
print(f"current identity: {d['identity']}")
print(f"refresh token 剩余有效: {user.get('refreshExpiresAt', 'unknown')}")
"

三件套 C: 模板 fallback 列表 (实测可复制)

作者发现"开发中调不通 + 重试" 是最大时间杀手. 下面 4 个 fallback 模板是作者 6/29 以来用的最高频版本:

# 1. 调不通先看 scope (3 秒看出是不是权限问题)
lark-cli auth scopes | grep <missing_scope>

# 2. 调不通再升身份 (30 秒切换)
lark-cli --as user <原命令>

# 3. 还调不通就走 OAuth 2.0 auth code 路径 (老 fallback)
# POST https://open.feishu.cn/open-apis/authen/v2/oauth/token
# grant_type=authorization_code + code=<从 redirect_uri 拿>

# 4. 终极 fallback (罕见): 重装 lark-cli
lark-cli update && lark-cli doctor

(作者 6 次踩坑 = 1-2 次走 1-2 步, 2-3 次走完)

三件套不适用场景

场景替代做法
im:message 频率超 5 次/秒走 webhook event 推送, 不要 polling
docx 大于 20 万字lark-cli fetch 默认返压缩, 加 --no-compress 拉原始
跨地域 endpointlark-cli 默认海外版, 中国境内部署改 endpoint
跨租户授权不能, 必须对方管理员主动 share

八.10、scope 矩阵表 (作者做的实战分类)

下面是作者花了 3 周整理的 scope 实战矩阵 (按业务场景分类, 不是按域):

业务场景必备 scope备份 scope偷懒 wildcard
飞书 DM 通知im:messageim:message.p2p_msgim:message.*
飞书群 @im:message.group_at_msgim:messageim:message.*
docx 自动写docs:document + drive:filedocs:document.content:readonlydocs:document.*
飞书 Base 同步base:app + base:table + base:recordbase:fieldbase:app.*
日程自动建calendar:calendar + calendar:calendar.eventcalendar:eventcalendar:calendar.*
用户反查contact:user:readonlycontact:user.id:readonlycontact:user.*:readonly
邮箱自动发mail:mail + mail:contactmail:mail:readonlymail:mail.*
会议妙记minutes:minutesminutes:minutes:readonlyminutes:minutes.*

(偷懒 wildcard 列仅是建议, 实际能不能申请看飞书当前策略. 个人 app 申请 wildcard 通过率低)

九、未来 3 个月趋势 (笔者预测)

  1. scopes 数字继续扩张 — 1.0.53 已经到 137, 1.0.66 涨 9 个 → 预计 2026 年底破 200

  2. wildcard scope 普及 — 个人 app 也开 wildcard 申请

  3. device flow 默认 7 天 — 飞书在推 refresh token 长效

  4. bot 身份能力扩 — 减少需 user 身份的场景 (但 im:message.send_as_user 仍企业专享)

十、写在最后

lark-cli 的 137 个 scope 不是障碍, 是"精度的代价". 写完本文后, 作者对自己脚本调不通时的 debug 时间从 2 小时降到 10 分钟 (因为第一动作就是 lark-cli auth scopes | grep <missing>).

下一篇: 笔者会讲 “Hermes Agent cron 防漂移 + 自愈: 3 层数据生命周期” (queue qid=4), 敬请期待.

附录 A: 1 行调试清单 (复制可跑)

# 1. 看自己是谁
lark-cli auth status

# 2. 看自己有什么 scope
lark-cli auth scopes

# 3. 跑 1 个最常用的命令试试水
lark-cli contact +get-user --as user --user-id ou_xxxxxxxxxxxxxxxx

# 4. 报权限错就 device flow 登 user
lark-cli auth login --no-wait --json --scope "im:message:send_as_user,contact:user:readonly"

# 5. 切回 bot 用 cron 跑批
lark-cli --as bot im +messages-send --chat-id oc_xxx --text "from bot"

# 6. 看实时 scope 数
lark-cli schema | grep -c scope

附录 B: 参考资源

更多推荐