mirror of
https://github.com/larksuite/cli.git
synced 2026-07-08 02:00:19 +08:00
Compare commits
1 Commits
main
...
fix/skills
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4472a1d6b |
@@ -76,4 +76,4 @@ CLI 提供三种互斥的 scope 表达方式:
|
||||
## 不在本 skill 范围
|
||||
|
||||
- OpenAPI spec 全量导出、实时日志 tail、Webhook 消费、多鉴权方式:本期不支持。
|
||||
- 身份选择、权限不足处理(`permission_violations`→`console_url`)、exit-10 审批、通用"禁输出密钥"红线、高风险操作通用框架:见 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),不在此重复。
|
||||
- 身份选择、权限不足处理(`missing_scopes`→`console_url`)、exit-10 审批、通用"禁输出密钥"红线、高风险操作通用框架:见 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),不在此重复。
|
||||
|
||||
@@ -85,7 +85,7 @@ metadata:
|
||||
## 身份与权限降级
|
||||
|
||||
- 默认显式使用 `--as user` 操作用户资源;只有用户明确要求应用身份时,才直接用 `--as bot`。
|
||||
- user 身份报 scope/授权不足,或错误中包含 `permission_violations` / `hint`,先转 `lark-shared` 做用户授权恢复,不要直接降级 bot。
|
||||
- user 身份报 scope/授权不足,或错误中包含 `missing_scopes` / `hint`,先转 `lark-shared` 做用户授权恢复,不要直接降级 bot。
|
||||
- user 身份报资源级无访问且无授权恢复提示时,才可用 `--as bot` 重试一次;bot 仍失败就停止重试并按权限错误处理。
|
||||
- `91403` 或明确不可访问错误不要循环换身份重试。
|
||||
- `+base-create` / `+base-copy` 若用 bot 身份执行,关注返回中的 `permission_grant`,并把用户是否可打开新 Base 告知用户。
|
||||
|
||||
@@ -98,16 +98,35 @@ lark-cli base +dashboard-block-get \
|
||||
|
||||
## 返回结构总览
|
||||
|
||||
服务端响应外层仍然是标准 OpenAPI 包装:
|
||||
CLI 输出标准成功信封(判断成功用 `ok == true` 或退出码 0,不要找 `code == 0`):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"dimensions": [...],
|
||||
"measures": [...],
|
||||
"main_data": [...]
|
||||
"dimensions": [
|
||||
{
|
||||
"field_name": "地区",
|
||||
"alias": "dim_region"
|
||||
}
|
||||
],
|
||||
"measures": [
|
||||
{
|
||||
"field_name": "销售额",
|
||||
"alias": "me_sales"
|
||||
}
|
||||
],
|
||||
"main_data": [
|
||||
{
|
||||
"dim_region": {
|
||||
"value": "华东"
|
||||
},
|
||||
"me_sales": {
|
||||
"value": 12345
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -347,28 +347,51 @@ value 使用预定义关键字机制,第一个元素为字符串常量名称
|
||||
|------|------|------|------|
|
||||
| `format` | string | 是 | 固定为 `"flat"`,表示返回扁平化的对象数组 |
|
||||
|
||||
## API 出参详情
|
||||
## CLI 出参详情
|
||||
|
||||
**成功时:**
|
||||
**成功时**(stdout,判断成功用 `ok == true` 或退出码 0):
|
||||
|
||||
```json
|
||||
{"code": 0, "data": {"main_data": [{"dim_city": {"value": "北京"}, "total_amount": {"value": 12345.00}}, ...]}, "msg": ""}
|
||||
{
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"main_data": [
|
||||
{
|
||||
"dim_city": {
|
||||
"value": "北京"
|
||||
},
|
||||
"total_amount": {
|
||||
"value": 12345.00
|
||||
}
|
||||
},
|
||||
{
|
||||
"dim_city": {
|
||||
"value": "上海"
|
||||
},
|
||||
"total_amount": {
|
||||
"value": 6789.00
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**失败时:**
|
||||
**失败时**(stderr 类型化错误信封,非零退出码;`error.code` 是上游 API 错误码):
|
||||
|
||||
```json
|
||||
{"code": 800004006, "data": {"error": {"code": 800004006, ...}}, "msg": "DSL validation failed"}
|
||||
{"ok": false, "identity": "user", "error": {"type": "api", "subtype": "...", "code": 800004006, "message": "DSL validation failed", "hint": "..."}}
|
||||
```
|
||||
|
||||
**Response 字段:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `code` | int | 状态码,0 为成功 |
|
||||
| `msg` | string | 错误信息 |
|
||||
| `ok` | bool | 是否成功 |
|
||||
| `data.main_data` | []object | 查询结果数组,每个元素为一行数据 |
|
||||
| `data.error` | object | 失败时的错误详情 |
|
||||
| `error.code` | int | 失败时的上游 API 错误码 |
|
||||
| `error.message` / `error.hint` | string | 失败原因与建议的恢复动作 |
|
||||
|
||||
每行数据的字段值封装在 CellValue 中:
|
||||
|
||||
@@ -387,7 +410,7 @@ value 使用预定义关键字机制,第一个元素为字符串常量名称
|
||||
|
||||
## 返回值
|
||||
|
||||
命令成功后输出 `data` 字段的内容:
|
||||
命令成功后,成功信封的 `data` 字段即查询结果:
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ lark-cli drive +member-add \
|
||||
}
|
||||
```
|
||||
|
||||
批量部分失败时,`partial` 为 `true`,CLI 以非零退出码返回 `error.type=partial_failure`。检查 `error.detail` 中的 `requested_count`、`succeeded_count`、`members`、`missing_member_ids` 和可选的 `mismatched_member_ids`。响应顺序不影响匹配结果。
|
||||
批量部分失败时,`partial` 为 `true`,同一份结果以 `ok:false` 部分失败信封写到 **stdout**(stderr 不再输出单独的错误信封),CLI 以非零退出码结束。检查 `data` 中的 `requested_count`、`succeeded_count`、`members`、`missing_member_ids` 和可选的 `mismatched_member_ids`。响应顺序不影响匹配结果。
|
||||
|
||||
## 行为说明
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
| `summary.deleted_local` | 启用 `--delete-local --yes` 时删除的本地文件数 |
|
||||
| `items[]` | 每个文件的明细(`rel_path` / `file_token` / `source_id` / `action` / 失败时的 `error`) |
|
||||
|
||||
`summary.failed > 0` 时命令以 **非零状态码**(`exit=1`,`error.type=partial_failure`)退出,且同一份 `summary + items` 会在 `error.detail` 里返回;脚本/agent 直接通过 exit code 判断成败即可,不需要再去解 `summary.failed`。
|
||||
`summary.failed > 0` 时命令以 **非零状态码**(`exit=1`)退出:同一份 `summary + items` 会以 `ok:false` 部分失败信封写到 **stdout**(字段在 `data.summary` / `data.items`,另附 `data.note` 说明失败情况),stderr 不再输出单独的错误信封;脚本/agent 直接通过 exit code 判断成败即可,不需要再去解 `summary.failed`。
|
||||
|
||||
## 远端同名文件冲突
|
||||
|
||||
如果 Drive 中多个条目映射到同一个 `rel_path`,默认直接失败(`error.type=duplicate_remote_path`),且不会下载、覆盖或删除任何本地文件。只有“多个 `type=file` 同名”的场景支持显式策略;`file-folder` 这类异构冲突始终直接失败。
|
||||
如果 Drive 中多个条目映射到同一个 `rel_path`,默认直接失败(stderr 类型化错误信封:`error.type=validation`、`error.subtype=failed_precondition`,`error.params[]` 逐条列出冲突的 `rel_path` 及碰撞条目),且不会下载、覆盖或删除任何本地文件。只有“多个 `type=file` 同名”的场景支持显式策略;`file-folder` 这类异构冲突始终直接失败。
|
||||
|
||||
| 策略 | 行为 |
|
||||
|------|------|
|
||||
@@ -80,7 +80,7 @@ lark-cli drive +pull --local-dir ./repo --folder-token fldcnxxxxxxxxx \
|
||||
|
||||
- `--delete-local`(无 `--yes`)→ Validate 直接报错:`--delete-local requires --yes`,没有任何下载、列表请求或删除发生。
|
||||
- `--delete-local --yes`,**且下载阶段全部成功** → 扫一遍 `--local-dir` 下所有常规文件,把不在云端清单里的逐个 `os.Remove`。**只删常规文件,不删目录**:远端文件夹被删除后,对应本地目录会保留空壳。
|
||||
- `--delete-local --yes`,**但下载阶段有任何条目失败** → **跳过整个删除阶段**,命令以 `partial_failure` 非零退出。设计意图:避免出现"前面下载失败、后面继续删本地文件"的半同步状态;操作者修好下载错误后再重跑即可。
|
||||
- `--delete-local --yes`,**但下载阶段有任何条目失败** → **跳过整个删除阶段**,命令以 `ok:false` 部分失败结果非零退出。设计意图:避免出现"前面下载失败、后面继续删本地文件"的半同步状态;操作者修好下载错误后再重跑即可。
|
||||
- 远端同名文件冲突且使用默认 `fail` → 在下载阶段前失败,删除阶段不会运行。
|
||||
- 不传 `--delete-local` → `summary.deleted_local` 永远是 0;命令对本地"多余"文件视而不见。
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
## 远端同名文件冲突
|
||||
|
||||
如果 Drive 中多个条目映射到同一个 `rel_path`,默认直接失败(`error.type=duplicate_remote_path`),且不会上传、覆盖或进入 `--delete-remote` 删除阶段。只有“多个 `type=file` 同名”的场景支持显式策略;`file-folder` 这类异构冲突始终直接失败。
|
||||
如果 Drive 中多个条目映射到同一个 `rel_path`,默认直接失败(stderr 类型化错误信封:`error.type=validation`、`error.subtype=failed_precondition`,`error.params[]` 逐条列出冲突的 `rel_path` 及碰撞条目),且不会上传、覆盖或进入 `--delete-remote` 删除阶段。只有“多个 `type=file` 同名”的场景支持显式策略;`file-folder` 这类异构冲突始终直接失败。
|
||||
|
||||
| 策略 | 行为 |
|
||||
|------|------|
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
## 远端同名文件冲突
|
||||
|
||||
如果 Drive 中多个条目映射到同一个 `rel_path`,`+status` 会在下载/hash 前直接失败,返回 `error.type=duplicate_remote_path`,并在 `error.detail.duplicates_remote[]` 中列出该路径下所有冲突条目的 `file_token`、`type`、名称、大小和时间字段;其中 `created_time`、`modified_time` 缺失时会省略,`size` 在缺失或为 `0` 时都可能被省略。不要把这种情况当成普通 `modified`;它表示同步域本身有歧义,需要先整理云端结构,或在 `+pull` / `+push` 中仅对“duplicate file”场景显式选择冲突策略。
|
||||
如果 Drive 中多个条目映射到同一个 `rel_path`,`+status` 会在下载/hash 前直接失败,在 stderr 返回类型化错误信封(`error.type=validation`、`error.subtype=failed_precondition`);`error.params[]` 每条的 `name` 是冲突的 `rel_path`,`reason` 枚举该路径下所有碰撞条目(`type` + `file_token`)。不要把这种情况当成普通 `modified`;它表示同步域本身有歧义,需要先整理云端结构,或在 `+pull` / `+push` 中仅对“duplicate file”场景显式选择冲突策略(`error.hint` 也给出了同样的恢复选项)。
|
||||
|
||||
## 命令
|
||||
|
||||
@@ -76,20 +76,18 @@ lark-cli drive +status \
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"identity": "user",
|
||||
"error": {
|
||||
"type": "duplicate_remote_path",
|
||||
"message": "multiple Drive entries map to the same rel_path",
|
||||
"detail": {
|
||||
"duplicates_remote": [
|
||||
{
|
||||
"rel_path": "dup.txt",
|
||||
"entries": [
|
||||
{"file_token": "<full_file_token>", "type": "file", "name": "dup.txt", "size": 5, "created_time": "1730000000", "modified_time": "1730000000"},
|
||||
{"file_token": "<folder_token>", "type": "folder", "name": "dup.txt", "created_time": "1730000060", "modified_time": "1730000060"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"type": "validation",
|
||||
"subtype": "failed_precondition",
|
||||
"message": "1 rel_path(s) map to multiple Drive entries",
|
||||
"hint": "resolve the duplicate remote files first: re-run +pull with --on-duplicate-remote=rename (downloads each with a hashed suffix), or use --on-duplicate-remote=newest|oldest (supported by +pull/+sync/+push) to pick one, or delete the extra remote files; a plain retry will not help",
|
||||
"params": [
|
||||
{
|
||||
"name": "dup.txt",
|
||||
"reason": "2 Drive entries collide here: file <full_file_token>, folder <folder_token>"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -120,9 +120,9 @@ lark-cli minutes +todo --minute-token <token> --as user --todos '[
|
||||
|
||||
**更新 / 删除前**:先用 `minutes +detail --minute-tokens <token> --todo` 读取 `todos[].todo_id`(按 `content` 匹配目标条目;列表顺序不保证稳定,**不要**用"第 2 条"代替 `todo_id`)。
|
||||
|
||||
**无编辑权限**:若 CLI 返回 `error.type=no_edit_permission`,表示对**这条妙记**没有编辑权,应请所有者授权;**不要**误走 `auth login --scope`。
|
||||
**无编辑权限**:若 CLI 返回稳定字段 `error.subtype=permission_denied`,且 `error.code` 为 `40005`(`+todo` / `+word-replace` 等编辑接口)或 `2091005`(如标题更新),表示对**这条妙记**没有编辑权,应请所有者授权;**不要**误走 `auth login --scope`。`error.message` 只作人读说明,不要用它做分支判断。
|
||||
|
||||
**逐字稿关键词替换无命中**:`minutes +word-replace` 时,若 CLI 返回 `error.type=words_not_found`,表示传入的 `source_word` 在该妙记逐字稿中**一个都没匹配到**,未做任何替换。这是**参数问题不是权限问题**:先用 `minutes +detail --minute-tokens <token> --transcript` 读取当前逐字稿,核对 `source_word` 的精确写法与大小写后重试。
|
||||
**逐字稿关键词替换无命中**:`minutes +word-replace` 时,若 CLI 返回稳定字段 `error.code=40001` 且 `error.subtype=not_found`,表示传入的 `source_word` 在该妙记逐字稿中**一个都没匹配到**,未做任何替换。这是**参数问题不是权限问题**:先用 `minutes +detail --minute-tokens <token> --transcript` 读取当前逐字稿,核对 `source_word` 的精确写法与大小写后重试。`error.message` 只作人读说明,不要用它做分支判断。
|
||||
|
||||
**替换 AI 总结全文**:见 [minutes +summary](references/lark-minutes-summary.md)。
|
||||
|
||||
|
||||
@@ -126,8 +126,8 @@ lark-cli minutes +todo --minute-token obcnxxxxxxxxxxxxxxxxxxxx --operation add -
|
||||
| 未指定操作 | 单条模式传 `--operation`,或批量传 `--todos` |
|
||||
| `--todos` 与单条 flags 冲突 | 二选一 |
|
||||
| `todos[i]` 校验失败 | 检查该条 `operation` 与字段组合 |
|
||||
| `error.type` = `no_edit_permission` | **妙记资源无编辑权**:向妙记所有者申请该妙记的编辑/协作权限;**不要**走 `auth login --scope` |
|
||||
| 缺少 OAuth scope(`permission_violations` 含 `minutes:minutes:update`) | `lark-cli auth login --scope "minutes:minutes:update"` |
|
||||
| `error.code=40005` 且 `error.subtype=permission_denied` | **妙记资源无编辑权**:向妙记所有者申请该妙记的编辑/协作权限;**不要**走 `auth login --scope`;`error.message` 只作人读说明 |
|
||||
| 缺少 OAuth scope(`error.missing_scopes` 含 `minutes:minutes:update`) | `lark-cli auth login --scope "minutes:minutes:update"` |
|
||||
|
||||
## 参考
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ LARKSUITE_CLI_NO_UPDATE_NOTIFIER=1 LARKSUITE_CLI_NO_SKILLS_NOTIFIER=1 lark-cli a
|
||||
遇到权限相关错误时,**根据当前身份类型采取不同解决方案**。
|
||||
|
||||
错误响应中包含关键信息:
|
||||
- `permission_violations`:列出缺失的 scope (N选1)
|
||||
- `missing_scopes`:列出缺失的 scope (N选1)
|
||||
- `console_url`:飞书开发者后台的权限配置链接
|
||||
- `hint`:建议的修复命令
|
||||
|
||||
@@ -178,22 +178,22 @@ lark-cli 对高风险写操作(`risk: "high-risk-write"`)有强制确认门
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"identity": "bot",
|
||||
"error": {
|
||||
"type": "confirmation_required",
|
||||
"type": "confirmation",
|
||||
"subtype": "confirmation_required",
|
||||
"message": "drive +delete requires confirmation",
|
||||
"hint": "add --yes to confirm",
|
||||
"risk": {
|
||||
"level": "high-risk-write",
|
||||
"action": "drive +delete"
|
||||
}
|
||||
"risk": "high-risk-write",
|
||||
"action": "drive +delete"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**遇到这种情况,不要当普通错误放弃。** 按以下流程处理:
|
||||
|
||||
1. **识别**:看到子进程 exit code = `10` 且 stderr JSON 里 `error.type == "confirmation_required"`
|
||||
2. **向用户确认**:把 `error.risk.action` 和关键参数展示给用户,明确告知"这是高风险操作",等待用户显式同意
|
||||
1. **识别**:看到子进程 exit code = `10` 且 stderr JSON 里 `error.type == "confirmation"`、`error.subtype == "confirmation_required"`
|
||||
2. **向用户确认**:把 `error.action`、`error.risk` 和关键参数展示给用户,明确告知"这是高风险操作",等待用户显式同意
|
||||
3. **用户同意** → 在你**原始 argv 的末尾追加 `--yes`** 后重试
|
||||
4. **用户拒绝** → 终止流程,不要擅自改写参数或跳过门禁
|
||||
|
||||
|
||||
@@ -65,15 +65,15 @@ lark-cli slides xml_presentations get --as user --params '{
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"xml_presentation": {
|
||||
"presentation_id": "slides_example_presentation_id",
|
||||
"revision_id": 3,
|
||||
"content": "<presentation xmlns=\"http://www.larkoffice.com/sml/2.0\" height=\"540\" width=\"960\">...</presentation>"
|
||||
}
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -94,12 +94,12 @@ lark-cli slides xml_presentation.slide create --as user --params '{
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"slide_id": "slide_example_id",
|
||||
"revision_id": 100
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -116,11 +116,11 @@ lark-cli slides xml_presentation.slide delete --as user --params '{
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"revision_id": 101
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ lark-cli slides +screenshot --as user \
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"xml_presentation_id": "slides_example_presentation_id",
|
||||
"output_dir": ".lark-slides/screenshots",
|
||||
@@ -79,8 +80,7 @@ lark-cli slides +screenshot --as user \
|
||||
"size": 12345
|
||||
}
|
||||
]
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -141,12 +141,12 @@ lark-cli slides xml_presentation.slide create --as user \
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"slide_id": "slide_example_id",
|
||||
"revision_id": 100
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -61,11 +61,11 @@ lark-cli slides xml_presentation.slide delete --as user --params '{"xml_presenta
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"revision_id": 100
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -65,15 +65,15 @@ lark-cli slides xml_presentation.slide get --as user --params '{
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"slide": {
|
||||
"slide_id": "slide_example_id",
|
||||
"content": "<slide id=\"slide_example_id\"><style/><data>...</data></slide>"
|
||||
},
|
||||
"revision_id": 100
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -130,24 +130,28 @@ lark-cli slides xml_presentation.slide replace --as user --params '{
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"revision_id": 105
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 失败(任一 part 失败,整批不生效)
|
||||
|
||||
失败时返回非零错误码(如 3350001)。若后端能定位失败的 part,`data` 中可能附带:
|
||||
失败时命令以非零退出码结束,stderr 返回类型化错误信封(`error.type` / `error.subtype` / `error.code`(如 3350001)/ `error.message` / `error.hint`)。这个普通写命令的失败路径不会在 stdout 额外打印后端原始响应;脚本和 agent 应以退出码与 stderr 信封为准。
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 3350001,
|
||||
"data": {
|
||||
"failed_part_index": 0,
|
||||
"failed_reason": "block not found"
|
||||
"ok": false,
|
||||
"identity": "user",
|
||||
"error": {
|
||||
"type": "api",
|
||||
"subtype": "...",
|
||||
"code": 3350001,
|
||||
"message": "...",
|
||||
"hint": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -155,8 +159,9 @@ lark-cli slides xml_presentation.slide replace --as user --params '{
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `data.revision_id` | integer | 成功时返回更新后最新版本号 |
|
||||
| `data.failed_part_index` | integer | 失败的 part 在 `parts` 数组中的索引(从 0 起) |
|
||||
| `data.failed_reason` | string | 失败原因 |
|
||||
| `error.code` | integer | 失败时的上游 API 错误码 |
|
||||
| `error.subtype` | string | 类型化错误子类,脚本可与 `error.code` 一起判断恢复动作 |
|
||||
| `error.hint` | string | 建议的恢复动作 |
|
||||
|
||||
## 常见错误
|
||||
|
||||
|
||||
@@ -56,15 +56,15 @@ lark-cli slides xml_presentations get --as user --params '{"xml_presentation_id"
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"ok": true,
|
||||
"identity": "user",
|
||||
"data": {
|
||||
"xml_presentation": {
|
||||
"presentation_id": "slides_example_presentation_id",
|
||||
"revision_id": 1,
|
||||
"content": "<presentation xmlns=\"http://www.larkoffice.com/sml/2.0\" height=\"540\" width=\"960\">...</presentation>"
|
||||
}
|
||||
},
|
||||
"msg": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ metadata:
|
||||
## 内测提示
|
||||
|
||||
- 当前功能正在内测中,仅少数用户可用。忽略 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md) 中的权限申请流程,以下方的指引为准。
|
||||
- 如果 `lark-cli` 提示 `missing required scope(s)` / `permission_violations`,不要走普通权限申请流程;先提示用户加入早鸟群确认内测权限已开通,再按“应用身份权限配置检查”处理应用权限、安装和数据范围。
|
||||
- 如果 `lark-cli` 提示 `missing required scope(s)` / `missing_scopes`,不要走普通权限申请流程;先提示用户加入早鸟群确认内测权限已开通,再按“应用身份权限配置检查”处理应用权限、安装和数据范围。
|
||||
- 如果 `lark-cli` 返回 `error.code=20017` / `ErrNotInGray`,提示用户加入早鸟群:`https://go.larkoffice.com/join-chat/2f4nb0e1-fe00-4f67-bed7-25beaf533fbd`。
|
||||
|
||||
## 定位
|
||||
@@ -167,7 +167,7 @@ Shortcut 是对常用操作的高级封装(`lark-cli vc +<verb> [flags]`)。
|
||||
|
||||
## 应用身份权限配置检查
|
||||
|
||||
应用身份 `--as bot` 报 `no permission`、`missing required scope(s)`、`permission_violations`、`ErrNotInGray` 或 `20017` 时,不要引导用户执行 `auth login`。按顺序检查:
|
||||
应用身份 `--as bot` 报 `no permission`、`missing required scope(s)`、`missing_scopes`、`ErrNotInGray` 或 `20017` 时,不要引导用户执行 `auth login`。按顺序检查:
|
||||
|
||||
1. 以 CLI 返回的 metadata / error envelope 为准,确认提示的 VC Agent 相关权限已开通。常见读取 active meeting / events 需要会中事件读取权限;应用机器人入会 / 离会需要 bot 入会写权限。
|
||||
2. 应用已发布并安装到当前租户。
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- TestDrive_FilesCreateFolderWorkflow: proves `drive files create_folder` in `create_folder as bot`; helper asserts the returned folder token and registers best-effort cleanup via `drive files delete`.
|
||||
- TestDrive_StatusWorkflow: proves `drive +status` against a real Drive folder. Seeds the remote side via `drive +upload` (`unchanged.txt`, `modified.txt`, `remote-only.txt`), seeds local files with the matching/diverging contents, and asserts every output bucket (`unchanged`, `modified`, `new_local`, `new_remote`) holds exactly the expected `rel_path` and `file_token`. Cleans up uploaded files and the parent folder via best-effort cleanup hooks.
|
||||
- TestDrive_UploadWorkflow: proves `drive +upload` against the real backend in both create and overwrite modes. First uploads a fresh file into a temporary Drive folder, then re-uploads new bytes with `--file-token` against the returned token, asserts the overwrite keeps the token stable, and finally downloads the file to confirm the remote content changed.
|
||||
- TestDrive_DuplicateRemoteWorkflow: proves the duplicate-remote workflows against the real backend. One subtest uploads two same-name files into the same Drive folder and asserts `drive +status` and default `drive +pull` both fail with `duplicate_remote_path`, while `drive +pull --on-duplicate-remote=rename` succeeds, downloads both files, and writes a hashed renamed sibling locally. The other subtest uploads duplicate remote files, runs `drive +push --on-duplicate-remote=newest --if-exists=overwrite --delete-remote --yes`, and then re-runs `drive +status` to prove the mirror converged to a single unchanged `dup.txt`.
|
||||
- TestDrive_DuplicateRemoteWorkflow: proves the duplicate-remote workflows against the real backend. One subtest uploads two same-name files into the same Drive folder and asserts `drive +status` and default `drive +pull` both fail with a typed validation error for the duplicate rel_path, while `drive +pull --on-duplicate-remote=rename` succeeds, downloads both files, and writes a hashed renamed sibling locally. The other subtest uploads duplicate remote files, runs `drive +push --on-duplicate-remote=newest --if-exists=overwrite --delete-remote --yes`, and then re-runs `drive +status` to prove the mirror converged to a single unchanged `dup.txt`.
|
||||
- TestDrive_ApplyPermissionDryRun / TestDrive_ApplyPermissionDryRunRejectsFullAccess: dry-run coverage for `drive +apply-permission`; asserts URL→type inference for docx/sheet/slides, explicit `--type` overriding URL inference when both a recognized URL and `--type` are supplied, bare-token + explicit `--type` path, request method/URL/type-query/perm/remark body shape, optional `remark` omission when unset, and client-side rejection of `--perm full_access`. Runs without hitting the live API.
|
||||
- TestDriveAddCommentDryRun_File / TestDriveAddCommentDryRun_Base: dry-run coverage for `drive +add-comment` on supported Drive file and Base targets; pins the `metas.batch_query -> files/:token/new_comments` file chain, Base `file_type=bitable`, and Base anchor fields.
|
||||
- TestDriveAddCommentMarkdownFileWorkflow: opt-in live workflow skeleton for the same path, gated by `LARK_DRIVE_MD_COMMENT_E2E=1`.
|
||||
|
||||
Reference in New Issue
Block a user