mirror of
https://github.com/larksuite/cli.git
synced 2026-07-10 11:04:26 +08:00
Compare commits
1 Commits
v1.0.68
...
fix/skills
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3faf36809 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -2,23 +2,6 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [v1.0.68] - 2026-07-09
|
||||
|
||||
### Features
|
||||
|
||||
- **drive**: Strengthen lark-drive high-risk write operations and read-only recognition boundaries. (#1801)
|
||||
- **slides**: add slides chart demo reference
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- register and consume --json shorthand for custom-format shortcuts (#1737)
|
||||
- **drive**: abort push on parent sibling limit (#1813)
|
||||
|
||||
### Documentation
|
||||
|
||||
- require native charts in slide planning
|
||||
- register knowledge organize workflow (#1828)
|
||||
|
||||
## [v1.0.67] - 2026-07-08
|
||||
|
||||
### Features
|
||||
@@ -1438,7 +1421,6 @@ Bundled AI agent skills for intelligent assistance:
|
||||
- Bilingual documentation (English & Chinese).
|
||||
- CI/CD pipelines: linting, testing, coverage reporting, and automated releases.
|
||||
|
||||
[v1.0.68]: https://github.com/larksuite/cli/releases/tag/v1.0.68
|
||||
[v1.0.67]: https://github.com/larksuite/cli/releases/tag/v1.0.67
|
||||
[v1.0.66]: https://github.com/larksuite/cli/releases/tag/v1.0.66
|
||||
[v1.0.65]: https://github.com/larksuite/cli/releases/tag/v1.0.65
|
||||
|
||||
@@ -113,7 +113,6 @@ func TestBuildAPIError_ExitCodeMatrix(t *testing.T) {
|
||||
{"230027 user_not_authorized", 230027, errs.CategoryAuthorization, errs.SubtypeUserUnauthorized, 3, "PermissionError"},
|
||||
{"1470403 task_permission_denied", 1470403, errs.CategoryAuthorization, errs.SubtypePermissionDenied, 3, "PermissionError"},
|
||||
{"1470400 task_invalid_params", 1470400, errs.CategoryAPI, errs.SubtypeInvalidParameters, 1, "APIError"},
|
||||
{"1062507 drive_parent_sibling_limit", 1062507, errs.CategoryAPI, errs.SubtypeQuotaExceeded, 1, "APIError"},
|
||||
{"99991400 rate_limit", 99991400, errs.CategoryAPI, errs.SubtypeRateLimit, 1, "APIError"},
|
||||
{"99991661 token_missing", 99991661, errs.CategoryAuthentication, errs.SubtypeTokenMissing, 3, "AuthenticationError"},
|
||||
{"21000 challenge_required", 21000, errs.CategoryPolicy, errs.Subtype("challenge_required"), 6, "SecurityPolicyError"},
|
||||
|
||||
@@ -17,7 +17,6 @@ var driveCodeMeta = map[int]CodeMeta{
|
||||
1061043: {Category: errs.CategoryAPI, Subtype: errs.SubtypeQuotaExceeded}, // file size beyond limit
|
||||
1061044: {Category: errs.CategoryAPI, Subtype: errs.SubtypeNotFound}, // parent folder does not exist (upload)
|
||||
1061101: {Category: errs.CategoryAPI, Subtype: errs.SubtypeQuotaExceeded}, // file quota exceeded
|
||||
1062507: {Category: errs.CategoryAPI, Subtype: errs.SubtypeQuotaExceeded}, // parent folder child count limit exceeded
|
||||
1062009: {Category: errs.CategoryAPI, Subtype: errs.SubtypeInvalidParameters}, // actual size inconsistent with declared size
|
||||
1063001: {Category: errs.CategoryAPI, Subtype: errs.SubtypeInvalidParameters}, // secure label invalid parameter
|
||||
1063002: {Category: errs.CategoryAuthorization, Subtype: errs.SubtypePermissionDenied}, // secure label permission denied
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@larksuite/cli",
|
||||
"version": "1.0.68",
|
||||
"version": "1.0.67",
|
||||
"description": "The official CLI for Lark/Feishu open platform",
|
||||
"bin": {
|
||||
"lark-cli": "scripts/run.js"
|
||||
|
||||
@@ -623,10 +623,6 @@ func driveClassifyBatchFailure(err error) driveBatchFailureDecision {
|
||||
case problem.Subtype == errs.SubtypeRateLimit || problem.Code == 99991400:
|
||||
decision.Class = "rate_limited"
|
||||
decision.Terminal = true
|
||||
case problem.Code == 1062507:
|
||||
decision.Class = "parent_sibling_limit"
|
||||
decision.Terminal = true
|
||||
decision.Hint = "The destination parent folder has reached its child-count limit. Clean up that folder, choose another --folder-token, or split the upload across subfolders before retrying."
|
||||
case problem.Subtype == errs.SubtypeQuotaExceeded || problem.Code == 1061043:
|
||||
decision.Class = "file_size_limit"
|
||||
case problem.Code == 1062009:
|
||||
|
||||
@@ -1334,75 +1334,6 @@ func TestDrivePushAbortsAfterCreateFolderMissingScope(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDrivePushAbortsAfterCreateFolderParentSiblingLimit(t *testing.T) {
|
||||
f, stdout, _, reg := cmdutil.TestFactory(t, driveTestConfig())
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
withDriveWorkingDir(t, tmpDir)
|
||||
if err := os.MkdirAll(filepath.Join("local", "a"), 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll a: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join("local", "b"), 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll b: %v", err)
|
||||
}
|
||||
|
||||
reg.Register(&httpmock.Stub{
|
||||
Method: "GET",
|
||||
URL: "folder_token=folder_root",
|
||||
Body: map[string]interface{}{
|
||||
"code": 0, "msg": "ok",
|
||||
"data": map[string]interface{}{"files": []interface{}{}, "has_more": false},
|
||||
},
|
||||
})
|
||||
reg.Register(&httpmock.Stub{
|
||||
Method: "POST",
|
||||
URL: "/open-apis/drive/v1/files/create_folder",
|
||||
Body: map[string]interface{}{
|
||||
"code": 1062507,
|
||||
"msg": "parent node out of sibling num.",
|
||||
},
|
||||
})
|
||||
|
||||
err := mountAndRunDrive(t, DrivePush, []string{
|
||||
"+push",
|
||||
"--local-dir", "local",
|
||||
"--folder-token", "folder_root",
|
||||
"--as", "bot",
|
||||
}, f, stdout)
|
||||
if err == nil {
|
||||
t.Fatalf("expected partial failure, got nil\nstdout: %s", stdout.String())
|
||||
}
|
||||
var pfErr *output.PartialFailureError
|
||||
if !errors.As(err, &pfErr) {
|
||||
t.Fatalf("expected *output.PartialFailureError, got %T: %v", err, err)
|
||||
}
|
||||
summary, items := splitDrivePushStdout(t, stdout.Bytes())
|
||||
if got := summary["failed"]; got != float64(1) {
|
||||
t.Fatalf("summary.failed = %v, want 1", got)
|
||||
}
|
||||
if got := summary["aborted"]; got != true {
|
||||
t.Fatalf("summary.aborted = %v, want true", got)
|
||||
}
|
||||
if len(items) != 1 {
|
||||
t.Fatalf("items len = %d, want 1; items=%#v", len(items), items)
|
||||
}
|
||||
item := items[0]
|
||||
if item["rel_path"] != "a" || item["phase"] != "create_folder" || item["error_class"] != "parent_sibling_limit" {
|
||||
t.Fatalf("unexpected failed item: %#v", item)
|
||||
}
|
||||
if item["code"] != float64(1062507) || item["subtype"] != "quota_exceeded" || item["retryable"] != false {
|
||||
t.Fatalf("unexpected failure metadata: %#v", item)
|
||||
}
|
||||
if got, _ := item["hint"].(string); !strings.Contains(got, "--folder-token") || !strings.Contains(got, "child-count limit") {
|
||||
t.Fatalf("hint should explain the destination folder child-count limit, got item=%#v", item)
|
||||
}
|
||||
for _, item := range items {
|
||||
if item["rel_path"] == "b" {
|
||||
t.Fatalf("parent sibling limit must abort before b, got items=%#v", items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDrivePushDetectsLocalFileChangedBeforeUpload(t *testing.T) {
|
||||
f, stdout, _, reg := cmdutil.TestFactory(t, driveTestConfig())
|
||||
|
||||
|
||||
@@ -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 告知用户。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: lark-drive
|
||||
version: 1.0.0
|
||||
description: "飞书云空间(云盘/云存储):管理 Drive 文件和文件夹,包含上传/下载、创建文件夹、复制/移动/删除、查看元数据、评论/权限/订阅、标题、版本和本地文件导入。用户需要整理云盘目录、处理云空间资源 URL/token、判断链接类型/真实 token/标题,或导入 Word/Markdown/Excel/CSV/PPTX/.base 为 docx/sheet/bitable/slides 时使用;doubao.com 云空间 URL/token 也按资源路径和 token 路由,不回退 WebFetch。不负责:文档内容编辑(走 lark-doc)、表格/Base 表内数据操作(走 lark-sheets/lark-base)、知识空间节点/成员管理(走 lark-wiki)、原生 Markdown 文件读写/patch/diff(走 lark-markdown)。"
|
||||
description: "飞书云空间(云盘/云存储):管理 Drive 文件和文件夹,包含上传/下载、创建文件夹、复制/移动/删除、查看元数据、评论/权限/订阅、标题、版本和本地文件导入。用户需要整理云盘目录、处理云空间资源 URL/token,或导入 Word/Markdown/Excel/CSV/PPTX/.base 为 docx/sheet/bitable/slides 时使用;doubao.com 云空间 URL/token 也按资源路径和 token 路由,不回退 WebFetch。不负责:文档内容编辑(走 lark-doc)、表格/Base 表内数据操作(走 lark-sheets/lark-base)、知识空间节点/成员管理(走 lark-wiki)、原生 Markdown 文件读写/patch/diff(走 lark-markdown)。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["lark-cli"]
|
||||
@@ -21,10 +21,8 @@ metadata:
|
||||
## 快速决策
|
||||
|
||||
- 用户要**复制文档 / 创建副本 / 另存为副本**时,使用 `lark-cli drive files copy`。先用 `lark-cli schema drive.files.copy --format json` 确认参数;如果来源是 wiki URL/token,先用 `lark-cli drive +inspect` 获取底层 `token` 和 `type`,不要把 wiki token 直接当 `file_token`。`params.file_token` 传源文档 token,`data.folder_token` 传目标文件夹 token,`data.name` 传副本名称,`data.type` 传源文件类型(如 `docx` / `sheet` / `bitable` / `slides`)。示例:`lark-cli drive files copy --params '{"file_token":"<DOC_TOKEN>"}' --data '{"folder_token":"<FOLDER_TOKEN>","name":"<COPY_NAME>","type":"docx"}'`。如返回 `confirmation_required`,按 `lark-shared` 高风险审批协议向用户确认后,在原命令末尾追加 `--yes` 重试。
|
||||
- 用户要**识别飞书 / doubao 云空间 URL 的类型和 token**时,可以先按 URL 路径形态做轻量判断;当路径已明确指向 docx / sheet / bitable / slides / file / folder 等资源时,可直接提取对应 token/type。传入 wiki URL、需要识别标题或 canonical URL、URL/token 有歧义,或后续操作依赖底层真实资源时,再使用 `lark-cli drive +inspect --url '<url>'` 进行识别;具体用法、失败处理和边界见 [`references/lark-drive-inspect.md`](references/lark-drive-inspect.md)。
|
||||
- 高风险写操作(删除、公开权限修改、owner 转移、版本删除/回滚、批量移动/覆盖/同步)必须同时满足三个条件才执行:目标已解析为该操作可直接使用的执行对象,执行细节已明确到可直接调用命令(例如删除的 file-token/type、公开权限修改的共享范围、owner 转移的目标 owner、版本删除/回滚的 version id、移动/覆盖/同步的目标位置和冲突策略),且用户在本轮明确确认执行这些具体目标和执行细节。用户只说“删除没用的文件”“开放/共享给大家”“改成开放”“覆盖/移动这些”只表示目标状态;先只读发现并列出候选、权限档位或执行方案,停止等待用户确认。
|
||||
- 用户要**检查 / 治理文档权限、公开范围、链接分享、外部访问、复制下载权限、密级标签、owner 转移**,或要“权限风险报告、收紧权限、申请查看 / 编辑权限、转移 / 批量转移 owner”,必须先阅读 [`references/lark-drive-workflow.md`](references/lark-drive-workflow.md),再按其中 `Workflow Registry` 进入 [`permission_governance`](references/lark-drive-workflow-permission-governance.md) workflow。
|
||||
- 用户要**整理云盘 / 文件夹 / 文档库 / 知识库 / 个人文档库**,或要“盘点目录结构、找出未归档/临时/重复/空目录、生成整理方案”,必须先阅读 [`references/lark-drive-workflow.md`](references/lark-drive-workflow.md),再按其中 `Workflow Registry` 进入 [`knowledge_organize`](references/lark-drive-workflow-knowledge-organize.md) workflow。默认只生成方案;创建目录、移动资源、申请权限都必须单独确认。
|
||||
- 用户要**整理云盘 / 文件夹 / 文档库 / 知识库 / 个人文档库**,或要“盘点目录结构、找出未归档/临时/重复/空目录、生成整理方案”,必须先阅读 [`references/lark-drive-workflow-knowledge-organize.md`](references/lark-drive-workflow-knowledge-organize.md)。默认只生成方案;创建目录、移动资源、申请权限都必须单独确认。
|
||||
- 用户要**搜文档 / Wiki / 电子表格 / 多维表格 / 云空间(云盘/云存储)对象**,优先使用 `lark-cli drive +search`。自然语言里"最近我编辑过的"、"我创建的"(→ `--created-by-me`,原始创建者语义)、"我负责/owner 的"(→ `--mine`,owner 语义)、"最近一周我打开过的 xxx"、"某人 owner 的 docx" 等直接映射到扁平 flag,避免手写嵌套 JSON。
|
||||
- 用户要**根据文档评论定位正文位置**,例如 根据评论 review 文档、根据评论内容回看文档、区分多处相同引用文本时,对于 docx 类型(`file_type=docx`)的文档支持通过 `need_relation=true` 返回评论位置,其他类型暂不支持,具体用法需要先阅读 [`references/lark-drive-comment-location.md`](references/lark-drive-comment-location.md) 了解。
|
||||
- 用户给出 doubao.com 的云空间资源 URL/token,或明确提到豆包里的 file/folder/docx/sheet/bitable/wiki 资源时,仍按资源类型、URL 路径和 token 路由到本 skill;不要因为域名不是飞书而回退到 WebFetch。
|
||||
@@ -164,7 +162,7 @@ lark-cli drive <resource> <method> [flags] # 调用 API
|
||||
|
||||
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
|
||||
>
|
||||
> **高频原生命令:** 读取 Drive 文件夹清单时使用 `drive files list`,使用前先读 [`references/lark-drive-files-list.md`](references/lark-drive-files-list.md),按模板通过 `--params` 传参并手动处理分页;不要把 `--page-all` 输出直接交给 JSON 解析脚本。
|
||||
> **高频原生命令:** 读取 Drive 文件夹清单时使用 `drive files list`,必须按 [`references/lark-drive-files-list.md`](references/lark-drive-files-list.md) 的模板通过 `--params` 传 `folder_token` / `page_token`,并手动处理分页;不要把 `--page-all` 输出直接交给 JSON 解析脚本。
|
||||
|
||||
### files
|
||||
|
||||
@@ -206,12 +204,10 @@ lark-cli drive <resource> <method> [flags] # 调用 API
|
||||
### file.statistics
|
||||
|
||||
- `get` — 获取文件统计信息
|
||||
- 获取 docx / 文件统计信息时,建议优先使用 typed flags:`lark-cli drive file.statistics get --file-token <token> --file-type <type> --format json`;`--params` JSON 也支持,适合批量拼装或 raw 参数场景。
|
||||
|
||||
### file.view_records
|
||||
|
||||
- `list` — 获取文档的访问者记录
|
||||
- 查看 docx 最近访问记录、返回 open_id、最多 N 条时,建议优先使用 typed flags:`lark-cli drive file.view_records list --file-token <docx_token> --file-type docx --page-size <N> --viewer-id-type open_id --format json`;`--params` JSON 也支持,适合批量拼装、分页续跑或 raw 参数场景。
|
||||
|
||||
### file.comment.reply.reactions
|
||||
|
||||
|
||||
@@ -7,18 +7,6 @@
|
||||
|
||||
> [!CAUTION]
|
||||
> 这是**高风险写操作**。CLI 层要求显式传 `--yes`;如果用户已经明确要求删除且目标明确,直接执行并带上 `--yes`。
|
||||
> “目标明确”表示用户给出了可解析为 `file-token` + `type` 的具体 URL/token,或对你刚列出的可解析资源列表逐项/整批确认删除。按“没用的”“临时的”“疑似重复的”“全部旧文件”等描述搜索出来的候选属于待确认目标;这类请求先列候选、说明筛选依据和影响范围,然后停止等待确认。
|
||||
|
||||
## 删除前门槛
|
||||
|
||||
执行 `drive +delete --yes` 前同时满足:
|
||||
|
||||
| 条件 | 可执行信号 |
|
||||
|------|------------|
|
||||
| 具体目标 | 单个可解析为 `file-token` + `type` 的 URL/token,或用户确认过且可解析的资源列表 |
|
||||
| 执行确认 | 用户在本轮明确说确认删除这些具体目标 |
|
||||
|
||||
若缺少任一条件,使用 `drive +search`、`drive +inspect` 或只读 API 收集候选并回复待确认清单;启发式规则(打开时间、标题模式、owner、文件类型等)只能作为候选筛选依据,不能升级为删除确认。执行 `drive +delete` 时必须使用解析后的 `--file-token` 和 `--type`。
|
||||
|
||||
## 命令
|
||||
|
||||
|
||||
@@ -41,37 +41,12 @@ lark-cli drive files list \
|
||||
|
||||
也可以省略 `folder_token` 字段来请求根目录,但在 Agent 编排中建议显式传空字符串,避免把“忘记传参数”和“确认请求根目录”混在一起。
|
||||
|
||||
## 按时间排序
|
||||
|
||||
默认不要传 `order_by` / `direction`;服务端会按默认顺序返回。只有用户明确要求按创建时间或编辑时间排序时,才使用服务端排序参数。
|
||||
|
||||
按创建时间升序列出当前文件夹直接子项:
|
||||
|
||||
```bash
|
||||
lark-cli drive files list \
|
||||
--params '{"folder_token":"<folder_token>","order_by":"CreatedTime","direction":"ASC","page_size":200}' \
|
||||
--format json
|
||||
```
|
||||
|
||||
按编辑时间降序列出当前文件夹直接子项:
|
||||
|
||||
```bash
|
||||
lark-cli drive files list \
|
||||
--params '{"folder_token":"<folder_token>","order_by":"EditedTime","direction":"DESC","page_size":200}' \
|
||||
--format json
|
||||
```
|
||||
|
||||
以上示例返回排序后的当前页;如果返回 `has_more=true`,保持相同 `folder_token` / `order_by` / `direction` / `page_size`,把 `next_page_token` 放入 `page_token` 继续翻页。
|
||||
|
||||
## 参数规则
|
||||
|
||||
1. `folder_token` 必须放在 `--params` JSON 里;不要使用不存在的 `--folder-token` flag。
|
||||
2. `page_token` 必须放在 `--params` JSON 里;不要依赖 shell 变量拼接不完整的 JSON。
|
||||
3. 默认不要传 `order_by` / `direction`;只有用户明确要求按创建时间 / 编辑时间排序时才使用服务端排序参数。
|
||||
4. 排序参数映射:创建时间 -> `order_by:"CreatedTime"`;编辑时间 / 修改时间 -> `order_by:"EditedTime"`;升序 -> `direction:"ASC"`;降序 -> `direction:"DESC"`。不要省略排序参数后再用 Python / shell 客户端排序替代。
|
||||
5. 排序查询建议带 `page_size:200` 减少翻页;只有用户要求完整分页、递归盘点、大目录全量导出,或当前页返回 `has_more=true` 后继续翻页时,才加入 `page_token`。
|
||||
6. `page_size` 在分页、递归盘点或全量导出时建议显式设置为 `200`。如果服务端或环境返回参数错误,再降级到服务端允许的值,并记录降级原因。
|
||||
7. 调用前如果不确定字段结构,先运行 `lark-cli schema drive.files.list` 查看 `--params` 结构。
|
||||
3. `page_size` 建议显式设置为 `200`。如果服务端或环境返回参数错误,再降级到服务端允许的值,并记录降级原因。
|
||||
4. 调用前如果不确定字段结构,先运行 `lark-cli schema drive.files.list` 查看 `--params` 结构。
|
||||
|
||||
## 返回结构与解析
|
||||
|
||||
|
||||
@@ -47,6 +47,4 @@ JSON 输出包含以下字段:
|
||||
- `--url` 为必填参数
|
||||
- 当 `--url` 是 bare token(非完整 URL)时,`--type` 也是必填的
|
||||
- wiki URL 会自动调用 `get_node` API 解包,输出中 `type` 和 `token` 是底层文档的类型和 token
|
||||
- `+inspect` 只用于识别/消歧;如果任务已能通过 URL 路径形态完成路由判断,不必把它作为所有 Drive 操作的通用前置步骤
|
||||
- `+inspect` 失败后不要自动切到写接口继续尝试,先按错误提示处理权限、scope 或链接问题
|
||||
- 支持 `--dry-run` 查看将调用的 API 步骤
|
||||
|
||||
@@ -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`。响应顺序不影响匹配结果。
|
||||
|
||||
## 行为说明
|
||||
|
||||
|
||||
@@ -10,18 +10,6 @@
|
||||
|
||||
如果用户只是想向文档 owner 申请访问权限,优先使用 [`lark-drive-apply-permission.md`](lark-drive-apply-permission.md)。
|
||||
|
||||
## 公开权限修改前门槛
|
||||
|
||||
公开权限修改是高风险写操作。执行 `drive permission.public patch --yes` 前同时确认:
|
||||
|
||||
| 条件 | 可执行信号 |
|
||||
|------|------------|
|
||||
| 具体目标 | 单个 URL/token,或用户确认过的资源列表 |
|
||||
| 公开范围 | 用户明确选择组织内/互联网、可读/可编辑等具体 `link_share_entity` 档位 |
|
||||
| 执行确认 | 用户在本轮确认按该目标和范围执行 |
|
||||
|
||||
“开放一下”“共享给大家”“让大家能看”只表达目标状态,不包含具体公开范围。先列出可选范围并停止等待用户选择;公开档位必须来自用户选择,CLI 的 `--yes` 只表示已获得用户对该档位的执行确认。
|
||||
|
||||
## 公开权限错误码
|
||||
|
||||
调用 `lark-cli drive permission.public patch` 更新文档公开权限失败时,如果返回以下错误码,按表格给用户明确下一步。不要把这些错误简单归类为缺少 scope;它们通常表示租户、对外分享或文档密级策略拦截。
|
||||
|
||||
@@ -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`),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` 这类异构冲突始终直接失败。
|
||||
|
||||
| 策略 | 行为 |
|
||||
|------|------|
|
||||
@@ -143,7 +143,6 @@ lark-cli drive +push --local-dir ./repo --folder-token fldcnxxxxxxxxx \
|
||||
| `permission_denied` | `1061004` / HTTP 403 | 当前身份无权操作目标资源 | 停止重试,检查目标文件夹权限、身份类型(user / bot)和资源可见性 |
|
||||
| `invalid_api_parameters` | `1061002` | API 参数被服务端拒绝 | 停止重试,检查 `--folder-token`、覆盖模式、`file_token`、文件名和上传参数;不要对同一参数组合批量重试 |
|
||||
| `parent_node_missing` | `1061044` | 上传 / 建目录使用的父文件夹不存在或当前身份不可见 | 停止重试,检查 `--folder-token` 是否仍存在、是否有权限、父目录是否在 push 过程中被删除;不要继续上传同一目录树 |
|
||||
| `parent_sibling_limit` | `1062507` | 目标父文件夹单层子节点数量超过上限 | 停止重试,清理目标目录、换一个 `--folder-token`,或把上传内容拆到多个子目录 |
|
||||
| `rate_limited` | `99991400` | 触发频控 | 停止当前批次,退避后再重试 |
|
||||
| `server_error` | `1061001` / `2200` | Drive 服务端异常 | 停止当前批次,稍后重试;保留 `log_id` 便于排查 |
|
||||
|
||||
|
||||
@@ -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”场景显式选择冲突策略。
|
||||
|
||||
## 命令
|
||||
|
||||
@@ -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>"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
# 知识整理工作流
|
||||
|
||||
Workflow id: `knowledge_organize`
|
||||
|
||||
Risk / Structure: `R2-R3` / `S3`
|
||||
|
||||
This file implements the registered knowledge organization workflow. Before execution, the agent MUST read [`lark-drive-workflow.md`](lark-drive-workflow.md) and [`../../lark-shared/SKILL.md`](../../lark-shared/SKILL.md), and follow the shared execution protocol, Artifact Contract, Workflow Loading rules, authentication rules, and write confirmation rules.
|
||||
|
||||
It defines the workflow-specific state machine and progressive loading map. Stage-specific rules live in phase files and MUST be loaded only when the workflow reaches the corresponding state.
|
||||
This file is the single entry point for the knowledge organization workflow. It defines the global contract, state machine, and progressive loading map. Stage-specific rules live in phase files and MUST be loaded only when the workflow reaches the corresponding state.
|
||||
|
||||
Phase files are references for this workflow, not independent skills. Do not route user requests directly to a phase file.
|
||||
|
||||
@@ -86,7 +80,7 @@ When this workflow is triggered, the agent MUST:
|
||||
|
||||
## Runtime State
|
||||
|
||||
This workflow extends the shared Artifact Contract. Agent MUST maintain these internal fields during one workflow run:
|
||||
Agent MUST maintain these internal fields during one workflow run:
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
@@ -120,24 +114,24 @@ This workflow extends the shared Artifact Contract. Agent MUST maintain these in
|
||||
|
||||
## Execution State Machine
|
||||
|
||||
| State | Protocol Step | Entry Condition | Agent MUST Do | User-Facing Output | wait_for_user | Next State |
|
||||
|-------|---------------|-----------------|---------------|--------------------|---------------|------------|
|
||||
| `PARSE_SCOPE` | `route` / `scope` | Workflow triggered | Load discovery phase; parse target, environment, identity, and target type | Scope confirmation or clarification question | `true` | `INVENTORY` |
|
||||
| `INVENTORY` | `read` | Scope confirmed | Load discovery phase; recursively list resources and build `resource_items` | Inventory progress / summary; continue automatically unless blocked | `false` unless blocked | `CONTENT_READ` |
|
||||
| `CONTENT_READ` | `read` | Inventory complete | Load analysis phase; identify low-confidence items and perform mandatory partial read when needed | Low-confidence read summary | `false` unless auth / permission blocks | `ISSUE_ANALYSIS` |
|
||||
| `ISSUE_ANALYSIS` | `assess` / `plan` | Resource list and partial reads ready | Load analysis phase; detect structure problems, evidence, and organization approach | Inventory result, problems, organization approach, and decision options | `true` | `RULE_GENERATION` |
|
||||
| `RULE_GENERATION` | `assess` / `plan` | User confirms organization approach | Load analysis phase; generate classification rules and `target_tree` | No separate stop; target tree is shown with plan generation | `false` | `PLAN_GENERATION` |
|
||||
| `PLAN_GENERATION` | `assess` / `plan` | Target tree ready | Load planning phase; generate complete internal `plan_items`; show target tree plus plan overview or page | Target tree and plan overview / paginated plan page | `true` | `EXEC_CONFIRM` |
|
||||
| `EXEC_CONFIRM` | `confirm` | User wants execution | Load planning phase; ask user to choose execution scope | Execution options and write-operation summary | `true` | `EXECUTE` or `DONE` |
|
||||
| `EXECUTE` | `execute` | User explicitly confirmed execution scope | Load execution phase; execute only whitelisted write operations for confirmed scope while maintaining internal recovery state | Progress reports for large or long-running execution; if blocked after successful moves, ask whether to try restoring to `整理前的位置` | `false` unless blocked / recovery offered | `VERIFY`, `ROLLBACK_CONFIRM`, or `DONE` |
|
||||
| `VERIFY` | `verify` | Execution finished | Load execution phase; rescan target scope and compare actual path/token against plan | Verification table and final summary; if serious mismatches exist, ask whether to try restoring to `整理前的位置` | `false` unless recovery offered | `DONE` or `ROLLBACK_CONFIRM` |
|
||||
| `ROLLBACK_CONFIRM` | `recovery confirm` | User asks to restore after execution failure / verification mismatch / explicit rollback request | Load rollback phase; generate internal `rollback_plan`; ask whether to execute recovery | Recoverable scope and restore confirmation | `true` | `ROLLBACK` or `DONE` |
|
||||
| `ROLLBACK` | `recovery execute` | User explicitly confirms restore execution | Load rollback phase; execute confirmed reverse moves only | Recovery progress / result | `false` | `ROLLBACK_VERIFY` |
|
||||
| `ROLLBACK_VERIFY` | `recovery verify` | Recovery execution finished | Load rollback phase; verify restored locations and decide whether cleanup candidates exist | Recovery verification result | `false` | `ROLLBACK_CLEANUP_CONFIRM` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP_CONFIRM` | `cleanup confirm` | Cleanup candidates exist after recovery, or user asks to clean workflow-created empty folders / nodes | Load rollback phase; generate cleanup plan and ask for delete confirmation | Cleanup candidates and delete confirmation | `true` | `ROLLBACK_CLEANUP` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP` | `cleanup execute` | User explicitly confirms cleanup deletion | Load rollback phase; delete only confirmed workflow-created safe-empty folders / nodes | Cleanup progress / result | `false` | `ROLLBACK_CLEANUP_VERIFY` |
|
||||
| `ROLLBACK_CLEANUP_VERIFY` | `cleanup verify` | Cleanup deletion finished | Load rollback phase; verify deleted cleanup targets | Cleanup verification result | `false` | `DONE` |
|
||||
| `DONE` | `done` | No more action | Stop | Final answer | `false` | End |
|
||||
| State | Entry Condition | Agent MUST Do | User-Facing Output | wait_for_user | Next State |
|
||||
|-------|-----------------|---------------|--------------------|---------------|------------|
|
||||
| `PARSE_SCOPE` | Workflow triggered | Load discovery phase; parse target, environment, identity, and target type | Scope confirmation or clarification question | `true` | `INVENTORY` |
|
||||
| `INVENTORY` | Scope confirmed | Load discovery phase; recursively list resources and build `resource_items` | Inventory progress / summary; continue automatically unless blocked | `false` unless blocked | `CONTENT_READ` |
|
||||
| `CONTENT_READ` | Inventory complete | Load analysis phase; identify low-confidence items and perform mandatory partial read when needed | Low-confidence read summary | `false` unless auth / permission blocks | `ISSUE_ANALYSIS` |
|
||||
| `ISSUE_ANALYSIS` | Resource list and partial reads ready | Load analysis phase; detect structure problems, evidence, and organization approach | Inventory result, problems, organization approach, and decision options | `true` | `RULE_GENERATION` |
|
||||
| `RULE_GENERATION` | User confirms organization approach | Load analysis phase; generate classification rules and `target_tree` | No separate stop; target tree is shown with plan generation | `false` | `PLAN_GENERATION` |
|
||||
| `PLAN_GENERATION` | Target tree ready | Load planning phase; generate complete internal `plan_items`; show target tree plus plan overview or page | Target tree and plan overview / paginated plan page | `true` | `EXEC_CONFIRM` |
|
||||
| `EXEC_CONFIRM` | User wants execution | Load planning phase; ask user to choose execution scope | Execution options and write-operation summary | `true` | `EXECUTE` or `DONE` |
|
||||
| `EXECUTE` | User explicitly confirmed execution scope | Load execution phase; execute only whitelisted write operations for confirmed scope while maintaining internal recovery state | Progress reports for large or long-running execution; if blocked after successful moves, ask whether to try restoring to `整理前的位置` | `false` unless blocked / recovery offered | `VERIFY`, `ROLLBACK_CONFIRM`, or `DONE` |
|
||||
| `VERIFY` | Execution finished | Load execution phase; rescan target scope and compare actual path/token against plan | Verification table and final summary; if serious mismatches exist, ask whether to try restoring to `整理前的位置` | `false` unless recovery offered | `DONE` or `ROLLBACK_CONFIRM` |
|
||||
| `ROLLBACK_CONFIRM` | User asks to restore after execution failure / verification mismatch / explicit rollback request | Load rollback phase; generate internal `rollback_plan`; ask whether to execute recovery | Recoverable scope and restore confirmation | `true` | `ROLLBACK` or `DONE` |
|
||||
| `ROLLBACK` | User explicitly confirms restore execution | Load rollback phase; execute confirmed reverse moves only | Recovery progress / result | `false` | `ROLLBACK_VERIFY` |
|
||||
| `ROLLBACK_VERIFY` | Recovery execution finished | Load rollback phase; verify restored locations and decide whether cleanup candidates exist | Recovery verification result | `false` | `ROLLBACK_CLEANUP_CONFIRM` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP_CONFIRM` | Cleanup candidates exist after recovery, or user asks to clean workflow-created empty folders / nodes | Load rollback phase; generate cleanup plan and ask for delete confirmation | Cleanup candidates and delete confirmation | `true` | `ROLLBACK_CLEANUP` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP` | User explicitly confirms cleanup deletion | Load rollback phase; delete only confirmed workflow-created safe-empty folders / nodes | Cleanup progress / result | `false` | `ROLLBACK_CLEANUP_VERIFY` |
|
||||
| `ROLLBACK_CLEANUP_VERIFY` | Cleanup deletion finished | Load rollback phase; verify deleted cleanup targets | Cleanup verification result | `false` | `DONE` |
|
||||
| `DONE` | No more action | Stop | Final answer | `false` | End |
|
||||
|
||||
## Progressive Load Map
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ Structure Level:
|
||||
2. Entry file 超过约 300 行时,优先拆 `commands`、`outputs` 或 `artifacts` reference。
|
||||
3. 只有执行、验证、恢复或 rollback 状态链复杂到影响可读性时,才升级到 `S3` phase files。
|
||||
4. 垂直业务包优先作为已有 workflow 的 recipe / policy / template,不默认新增独立 workflow。
|
||||
5. 已有样板:`permission_governance` 是 `R2/S2`;`knowledge_organize` 是 `R2-R3/S3`。
|
||||
5. 已有样板:`permission_governance` 是 `R2/S2`;已发布的独立 `knowledge_organize` 是 `R2-R3/S3`,当前不作为本总框架 registry entry。
|
||||
|
||||
## 加载与拆分边界
|
||||
|
||||
@@ -111,7 +111,6 @@ Structure Level:
|
||||
| Workflow | Status | Risk | Structure | Entry File | Trigger |
|
||||
|----------|--------|------|-----------|------------|---------|
|
||||
| `permission_governance` | Registered | `R2` | `S2` | [`lark-drive-workflow-permission-governance.md`](lark-drive-workflow-permission-governance.md) | 权限审计、公开链接/外部访问、复制/下载/评论/分享设置、权限申请、owner 转移 / 批量 owner 转移、密级标签调整 |
|
||||
| `knowledge_organize` | Registered | `R2-R3` | `S3` | [`lark-drive-workflow-knowledge-organize.md`](lark-drive-workflow-knowledge-organize.md) | 整理云盘 / 文件夹 / 文档库 / 知识库、盘点目录结构、归类资源、生成整理方案,并在用户确认后创建目录或移动资源 |
|
||||
|
||||
## Workflow Loading
|
||||
|
||||
|
||||
@@ -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`,表示对**这条妙记**没有编辑权,应请所有者授权;**不要**误走 `auth login --scope`。
|
||||
|
||||
**逐字稿关键词替换无命中**:`minutes +word-replace` 时,若 CLI 返回 `error.type=words_not_found`,表示传入的 `source_word` 在该妙记逐字稿中**一个都没匹配到**,未做任何替换。这是**参数问题不是权限问题**:先用 `minutes +detail --minute-tokens <token> --transcript` 读取当前逐字稿,核对 `source_word` 的精确写法与大小写后重试。
|
||||
**逐字稿关键词替换无命中**:`minutes +word-replace` 时,若 CLI 返回 `error.subtype=not_found`,表示传入的 `source_word` 在该妙记逐字稿中**一个都没匹配到**,未做任何替换。这是**参数问题不是权限问题**:先用 `minutes +detail --minute-tokens <token> --transcript` 读取当前逐字稿,核对 `source_word` 的精确写法与大小写后重试。
|
||||
|
||||
**替换 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.subtype` = `permission_denied` | **妙记资源无编辑权**:向妙记所有者申请该妙记的编辑/协作权限;**不要**走 `auth login --scope` |
|
||||
| 缺少 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. **用户拒绝** → 终止流程,不要擅自改写参数或跳过门禁
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
## Core Rules
|
||||
|
||||
- `asset_need` is metadata only. It can guide page design, but it must not require web search, local download, media upload, or external tools.
|
||||
- Every planned asset must include a fallback visual plan. The fallback can use native charts, tables, whiteboard diagrams, placeholder regions, or XML shapes, text, and arrows as appropriate.
|
||||
- Every planned asset must include a fallback visual plan so the slide can be generated with XML shapes, text, arrows, tables, simple charts, whiteboard diagrams, or placeholder regions.
|
||||
- Asset needs must serve the page's `key_message` and `visual_focus`. Do not add decorative assets that do not clarify the page.
|
||||
- Prefer a few high-value asset plans over one asset on every page. For a 6-page technical or business deck, plan assets on at least 3 pages when the content allows.
|
||||
- If a real local asset already exists or the user provides one, it can be used through the normal media-upload workflow. Still keep `fallback_if_missing` in the plan.
|
||||
@@ -43,7 +43,7 @@ For a page without a meaningful asset need, use:
|
||||
- `architecture_diagram`: system components, data flow, dependency map, or model structure.
|
||||
- `icon`: small semantic symbol for a concept, step, role, or status.
|
||||
- `logo`: brand, product, team, or customer mark.
|
||||
- `chart`: column, bar, line, area, radar, pie, doughnut/ring, or combo data visual. Note: `<chart>` does not support funnel or scatter — map those to `<whiteboard>` SVG at generation time.
|
||||
- `chart`: line, bar, pie, radar, area, or combo data visual. Note: `<chart>` does not support funnel or scatter — map those to `<whiteboard>` SVG at generation time.
|
||||
- `infographic`: composed visual explanation, usually combining labels, numbers, and simple shapes.
|
||||
- `screenshot`: product UI, terminal output, workflow state, or page capture.
|
||||
- `flow_diagram`: process, sequence, decision tree, or mechanism diagram.
|
||||
@@ -64,23 +64,11 @@ Match asset type to slide role:
|
||||
|
||||
`suggested_query` is only a future lookup hint. Write it as a short phrase a human or later workflow could search, but do not execute the search unless the user separately requests real assets.
|
||||
|
||||
For `asset_type: "chart"`:
|
||||
|
||||
- If the visual is a supported standard data chart — column, bar, line, area, radar, pie, doughnut/ring, or combo — `fallback_if_missing` must still render as a native `<chart>`.
|
||||
- Do not imitate supported standard data visuals with manual drawing primitives or `<whiteboard>`.
|
||||
- Choose the data source explicitly:
|
||||
- `user_provided`: when the user provides concrete values, tables, CSV, or metric lists, use those values and do not replace them with mock data.
|
||||
- `mock_placeholder`: when the user asks for a placeholder, template, example, or chart position to replace later, use mock data in a native `<chart>`.
|
||||
- `mock_required_by_intent`: when the user does not provide concrete values but asks for data expression, charts, trends, comparisons, or distributions, use mock data in a native `<chart>`.
|
||||
- Mock data must be labeled as `模拟数据,仅占位,待替换真实数据` or equivalent. Do not present mock values as facts.
|
||||
- Manual drawing fallbacks are allowed only for unsupported chart types such as scatter, funnel, waterfall-like custom visuals, or decorative non-data visuals.
|
||||
|
||||
`fallback_if_missing` must be concrete enough to turn into XML, for example:
|
||||
|
||||
- "Draw a simplified attention matrix with 5 token labels, semi-transparent cells, and arrows to output token."
|
||||
- "Use three grouped boxes with arrows from client to gateway to service; add small protocol labels."
|
||||
- "Render a native `<chart>` using the user-provided series."
|
||||
- "Render a native `<chart>` with mock placeholder values and label it as `模拟数据,仅占位,待替换真实数据`."
|
||||
- "Render a mini bar chart with 4 bars using shapes and value labels."
|
||||
- "Use a bordered placeholder panel with product area labels, not an empty image."
|
||||
|
||||
Weak fallbacks to avoid:
|
||||
@@ -130,7 +118,7 @@ Business comparison page:
|
||||
When generating XML:
|
||||
|
||||
1. If an asset exists and the workflow supports it, place it in the planned visual region.
|
||||
2. If no asset exists, immediately render `fallback_if_missing` with the planned XML-native element type. Supported standard data visuals still use native `<chart>`; other fallbacks may use shapes, text, lines, arrows, tables, whiteboard diagrams, or placeholder panels.
|
||||
2. If no asset exists, immediately render `fallback_if_missing` with XML-native shapes, text, lines, arrows, tables, whiteboard diagrams, or chart-like elements.
|
||||
3. Size the fallback to satisfy `visual_focus`; it should be a real page element, not a tiny decoration.
|
||||
4. Keep text-density limits. Do not compensate for missing assets by adding long bullet text.
|
||||
5. After creation, fetch the presentation and verify asset pages are not blank and that each planned fallback is visible when no real asset was used.
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
`<whiteboard>` 放在 `<data>` 内,内部可放 **SVG** 或 **Mermaid**,用于绘制流程图、时序图、架构图、散点图、漏斗图、自定义图标、装饰图案等 `<chart>` 和 `<shape>` 难以覆盖的视觉内容。
|
||||
|
||||
普通柱状图、条形图、折线图、面积图、雷达图、饼图 / 环图和组合图应优先使用原生 `<chart>`。除非用户明确要求像素级自定义,或图表类型确实不受 `<chart>` 支持,否则不要用 `<whiteboard>` + SVG / Mermaid 重画这些标准图表。
|
||||
|
||||
> 前置条件:使用本文档前先阅读 [lark-slides SKILL.md](../SKILL.md)。
|
||||
|
||||
---
|
||||
@@ -14,13 +12,13 @@
|
||||
|
||||
| 场景 | 推荐元素 |
|
||||
|------|---------|
|
||||
| 有结构化数据序列的柱/条/折线/面积/雷达/饼/环/组合图 | `<chart>` — 原生渲染,支持 legend / tooltip / 系列配色 |
|
||||
| 散点图、漏斗图(`<chart>` 不支持)或其他非原生数据视觉 | `<whiteboard>` SVG |
|
||||
| 有结构化数据序列的柱/条/折线/面积/雷达/饼/组合图 | `<chart>` — 原生渲染,支持 legend / tooltip / 系列配色 |
|
||||
| 散点图、漏斗图(`<chart>` 不支持) | `<whiteboard>` SVG |
|
||||
| 流程图、时序图、架构图、类图、ER 图等拓扑图 | `<whiteboard>` Mermaid 或 SVG |
|
||||
| 自定义图标、徽标、示意性图形(需要 path/polygon 精确控制) | `<whiteboard>` SVG |
|
||||
| 进度条、波浪背景、装饰图案、像素级自定义可视化 | `<whiteboard>` SVG |
|
||||
|
||||
> 适合 `<chart>` 的内容就用 `<chart>`,不要用 SVG / Mermaid 手绘——原生渲染更省力、结构更稳定,也更容易被回读和后续编辑。
|
||||
> 适合 `<chart>` 的内容就用 `<chart>`,不要用 SVG 手绘——原生渲染更省力且质量更高。
|
||||
|
||||
---
|
||||
|
||||
@@ -41,13 +39,9 @@ SVG 内的坐标相对于 whiteboard 自身左上角(0,0),与 slide 坐标
|
||||
|
||||
## SVG 还是 Mermaid?
|
||||
|
||||
选择分三步:**先排除原生 `<chart>`,再判断 whiteboard 类型,最后看当前模型身份**。
|
||||
选择分两步:**先看图表类型,再看当前模型身份**。
|
||||
|
||||
### 第一步:先确认是否应该使用 `<chart>`
|
||||
|
||||
如果内容是柱状图、条形图、折线图、面积图、雷达图、饼图 / 环图或组合图,返回使用原生 `<chart>`,不要继续套用本文档的 SVG / Mermaid 路径。
|
||||
|
||||
### 第二步:whiteboard 类型优先判断
|
||||
### 第一步:图表类型优先判断
|
||||
|
||||
以下类型**推荐 Mermaid**,自动布局、代码简洁;如需精确匹配品牌配色或自定义节点样式,可改用 SVG:
|
||||
|
||||
@@ -56,19 +50,20 @@ SVG 内的坐标相对于 whiteboard 自身左上角(0,0),与 slide 坐标
|
||||
| 流程图、决策树、架构图 | `flowchart TD` / `flowchart LR` |
|
||||
| 时序图 | `sequenceDiagram` |
|
||||
| 类图 | `classDiagram` |
|
||||
| 饼图 | `pie` |
|
||||
| 甘特图 | `gantt` |
|
||||
| 状态图 | `stateDiagram-v2` |
|
||||
| 思维导图 | `mindmap` |
|
||||
| ER 图 | `erDiagram` |
|
||||
|
||||
### 第三步:非原生图表与装饰元素按模型身份选路径
|
||||
### 第二步:数据图表与装饰元素按模型身份选路径
|
||||
|
||||
上表以外的场景(散点图、漏斗图、进度条、时间线、波浪背景、星点纹理等)需要精确控制坐标和配色,SVG 表达力更强,但各模型生成 SVG 的能力有差异:
|
||||
|
||||
| 模型身份 | 路径 |
|
||||
|----------|------|
|
||||
| Claude / Gemini / GPT / GLM | **SVG** — 精确控制坐标、颜色、透明度 |
|
||||
| Doubao / Seed / Other | **Mermaid** — 用 `gantt`、`flowchart` 等近似表达;确实无法用 Mermaid 表达时才回退到简单 SVG 矩形/线条 |
|
||||
| Doubao / Seed / Other | **Mermaid** — 用 `pie`、`gantt` 等近似表达;确实无法用 Mermaid 表达时才回退到简单 SVG 矩形/线条 |
|
||||
|
||||
> **先自报身份再选路径**:在决定使用 SVG 之前,确认当前模型属于哪一类。不要跳过这一步。
|
||||
|
||||
@@ -78,13 +73,13 @@ SVG 内的坐标相对于 whiteboard 自身左上角(0,0),与 slide 坐标
|
||||
|
||||
### ⚠️ 设计品质要求
|
||||
|
||||
在 slide 里嵌入 `<whiteboard>` 的目的是**表达原生 `<chart>` 或基础 `<shape>` 难以覆盖的视觉关系**,不是把标准数据图表手绘一遍。
|
||||
在 slide 里嵌入 `<whiteboard>` 的目的是**提升视觉质量**,不是把数字堆进去。
|
||||
|
||||
- **不要只用矩形加文字应付**:通篇纯白底色 + 方块 + 黑字等于白做,这是不及格输出
|
||||
- **非原生数据视觉必须有坐标系**:散点、漏斗等仍要有必要的坐标轴、刻度、数值标注或分段说明,不要只画点或色块
|
||||
- **数据图表必须有坐标系**:坐标轴、网格线、数值标注缺一不可,不要只画柱子或点
|
||||
- **字号必须有层级**:标题 ≠ 标签 ≠ 数值,混用同一字号会消灭视觉焦点
|
||||
- **配色要与 slide 主题呼应**:深色 slide 背景下图表用透明底或深色卡片;浅色背景下避免再加纯白底块
|
||||
- **每个 whiteboard 都是设计机会**:主动用圆角、半透明填充、清晰分组、节点状态等细节拉开与默认模板的差距
|
||||
- **每个 whiteboard 都是设计机会**:主动用圆角、半透明填充、折线面积、点装饰等细节拉开与默认模板的差距
|
||||
- **写 SVG 前先判断背景亮度**:背景亮度 < 30% 时,装饰元素"对比不足"比"过强"危害更大,宁重勿轻;
|
||||
- **装饰层次用亮度跳跃,不用线性叠透明度**:`α=0.04→0.08→0.12` 的等差递增在深色底上几乎看不出差异(相邻层亮度差 ≈20);正确做法是非线性跳跃如 `0.10→0.40→0.70→1.0`,相邻层亮度差 ≥60。
|
||||
|
||||
@@ -111,11 +106,11 @@ whiteboard 渲染时以**所有子元素的几何包围盒合并结果**为内
|
||||
|
||||
| 元素 | 说明 | 典型用途 |
|
||||
|------|------|---------|
|
||||
| `<rect>` | 矩形,支持 `rx` 圆角 | 卡片、进度条、分段色块 |
|
||||
| `<rect>` | 矩形,支持 `rx` 圆角 | 柱图、卡片、进度条 |
|
||||
| `<circle>` | 圆 | 节点、装饰点、环形图 |
|
||||
| `<ellipse>` | 椭圆 | 自定义轮廓图形 |
|
||||
| `<line>` | 直线 | 轴线、分隔线、连接线 |
|
||||
| `<path>` | 任意路径(支持 Q/C 曲线) | 波浪、曲线、弧形 |
|
||||
| `<line>` | 直线 | 坐标轴、分隔线 |
|
||||
| `<path>` | 任意路径(支持 Q/C 曲线) | 波浪、折线、弧形 |
|
||||
| `<text>` | 文本,支持中文 | 标签、数值 |
|
||||
| `<polygon>` | 多边形 | 箭头、星形、面积填充 |
|
||||
| `<g>` | 分组 | 批量变换、语义分组 |
|
||||
@@ -128,25 +123,27 @@ whiteboard 渲染时以**所有子元素的几何包围盒合并结果**为内
|
||||
---
|
||||
### 元素计算
|
||||
|
||||
SVG 中只要涉及批量定位、等间距排布或数据映射,**建议额外运行一个 Python 脚本把坐标算出来再填入 SVG**,而不是手动估值。适用范围包括散点、漏斗、装饰性点阵、等间距圆、重复图案等;普通柱状图、折线图、饼图仍应回到原生 `<chart>`。
|
||||
SVG 中只要涉及批量定位、等间距排布或数据映射,**建议额外运行一个 Python 脚本把坐标算出来再填入 SVG**,而不是手动估值。适用范围不限于数据图表——装饰性点阵、等间距圆、重复图案同样适用。
|
||||
|
||||
> **主动去算**:写 SVG 之前先运行脚本,把输出当注释贴在 `<svg>` 开头,再照着填坐标。估值几乎每次都需要反复调整,跳过这步反而更慢。
|
||||
|
||||
**散点图 / 装饰性点阵范式**
|
||||
**数据图表(柱状图范式)**
|
||||
|
||||
```python
|
||||
W, H = 360, 260
|
||||
origin_x, origin_y = 50, 216 # 左下角,SVG Y 轴向下
|
||||
cw, ch = 290, 184
|
||||
|
||||
points = [(12, 40), (28, 80), (45, 65)]
|
||||
x_min, x_max, y_min, y_max = 0, 50, 0, 100
|
||||
for i, (xv, yv) in enumerate(points):
|
||||
x = round(origin_x + (xv - x_min) / (x_max - x_min) * cw)
|
||||
y = round(origin_y - (yv - y_min) / (y_max - y_min) * ch)
|
||||
print(f"point-{i}: cx={x} cy={y}")
|
||||
data, y_max = [120, 160, 90], 200
|
||||
bar_w = int(cw / len(data) * 0.62)
|
||||
for i, v in enumerate(data):
|
||||
cx = round(origin_x + (i + 0.5) * cw / len(data))
|
||||
y = round(origin_y - v / y_max * ch)
|
||||
print(f"bar-{i}: x={cx - bar_w//2} y={y} w={bar_w} h={round(origin_y - y)}")
|
||||
```
|
||||
|
||||
折线图:`x = origin_x + i/(n-1)*cw`,`y = origin_y - (v-y_min)/(y_max-y_min)*ch`。
|
||||
|
||||
**装饰性元素(等间距范式)**
|
||||
|
||||
```python
|
||||
@@ -163,9 +160,9 @@ for i in range(n):
|
||||
```python
|
||||
# 每个元素登记 (x, y, w, h),含 stroke 外扩
|
||||
elements = [
|
||||
(10, 20, 80, 160), # item-0
|
||||
(107, 10, 80, 170), # item-1
|
||||
(204, 40, 80, 140), # item-2
|
||||
(10, 20, 80, 160), # bar-0
|
||||
(107, 10, 80, 170), # bar-1
|
||||
(204, 40, 80, 140), # bar-2
|
||||
(0, 0, 300, 1), # x-axis
|
||||
]
|
||||
|
||||
@@ -264,6 +261,7 @@ print(f"whiteboard width={wb_w} height={wb_h}")
|
||||
| 流程图 | `flowchart TD` / `flowchart LR` | 业务流程、决策树、工作流 |
|
||||
| 时序图 | `sequenceDiagram` | 系统交互、API 调用链 |
|
||||
| 甘特图 | `gantt` | 项目计划、里程碑 |
|
||||
| 饼图 | `pie` | 占比数据 |
|
||||
| 类图 | `classDiagram` | 对象关系、架构设计 |
|
||||
| ER 图 | `erDiagram` | 数据库结构 |
|
||||
| 状态图 | `stateDiagram-v2` | 状态机、生命周期 |
|
||||
@@ -281,6 +279,7 @@ Mermaid 图表会自动撑满 whiteboard 区域。建议:
|
||||
|---------|-----------|------------|
|
||||
| 流程图(5-8 节点) | 720-816 | 300-400 |
|
||||
| 时序图(3-5 参与者) | 720-816 | 320-420 |
|
||||
| 饼图 | 500-600 | 300-360 |
|
||||
| 甘特图 | 816 | 280-360 |
|
||||
| 思维导图 | 816 | 380-480 |
|
||||
|
||||
@@ -308,7 +307,7 @@ Mermaid 语法包含 `[`、`>`、`-->`,不用 CDATA 直接写会破坏 XML 解
|
||||
- [ ] 文字 `y` 坐标为 baseline 位置,最小值 ≥ font-size(避免被裁切)
|
||||
|
||||
**SVG 模式——视觉品质检查:**
|
||||
- [ ] 非原生数据视觉有必要的坐标轴、网格线、数值标注或分段说明,没有"裸点"或无解释色块
|
||||
- [ ] 坐标轴、网格线、数值标注齐全,没有"裸柱子"或"裸折线"
|
||||
- [ ] 字号有层级:标题 > 数值 > 轴标签,非全部相同
|
||||
- [ ] 单一数据系列用同一颜色,多系列用不同颜色且对比充足
|
||||
- [ ] 轴标签与图表元素互不遮挡,留有足够空间
|
||||
|
||||
@@ -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.code`(如 3350001)/ `error.message` / `error.hint`),stdout 不会打印后端原始响应:
|
||||
|
||||
```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,6 @@ 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 | 失败原因 |
|
||||
|
||||
## 常见错误
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -119,34 +119,6 @@ Each slide must include:
|
||||
- `text_density`: `low`, `medium`, or `high`.
|
||||
- `speaker_intent`: why the speaker needs this page and how it advances the story.
|
||||
|
||||
Optional slide fields:
|
||||
|
||||
- `chart_contract`: required when the page plan includes a standard data chart that `<chart>` supports. Use this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"chart_contract": {
|
||||
"required": true,
|
||||
"render_as": "native_chart",
|
||||
"chart_type": "line",
|
||||
"data_source": "mock_placeholder",
|
||||
"data_series_required": true,
|
||||
"placeholder_label_required": true,
|
||||
"manual_shape_fallback_allowed": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When `chart_contract.required == true`, XML generation must produce a `<chart>` element on that slide. A shape, line, polyline, or whiteboard approximation does not satisfy the plan.
|
||||
|
||||
`data_source` must be one of:
|
||||
|
||||
- `user_provided`: the user supplied concrete values, tables, CSV, or metric lists; use them and do not replace them with mock data.
|
||||
- `mock_placeholder`: the user asked for a placeholder, template, example, or later-replaceable chart position; use mock data in native `<chart>`.
|
||||
- `mock_required_by_intent`: the user did not provide concrete values but asked for data expression, charts, trends, comparisons, or distributions; use mock data in native `<chart>`.
|
||||
|
||||
`data_series_required` means the generated XML must include `<chartData>`. It does not require user-provided real-world values. When real values are unavailable but chart expression is part of the user's intent, write mock or placeholder values into native `<chart>` and label them clearly instead of switching to manual drawing primitives or metric blocks.
|
||||
|
||||
## Layout Vocabulary
|
||||
|
||||
Use one of these `layout_type` values unless the user explicitly needs a custom structure:
|
||||
@@ -211,7 +183,6 @@ Use an object for one planned asset, an array for multiple real needs, or `asset
|
||||
- `purpose`: why this asset helps the page's key message.
|
||||
- `suggested_query`: short future lookup hint only; do not execute it unless separately requested.
|
||||
- `fallback_if_missing`: concrete XML-native visual plan using shapes, labels, tables, whiteboard diagrams, or placeholder panels.
|
||||
- `chart_contract`: when `asset_type` is `chart` and the visual is a supported standard data chart, set this optional slide-level field so generation is locked to native `<chart>`.
|
||||
|
||||
For detailed rules and examples, read `asset-planning.md`.
|
||||
|
||||
@@ -219,7 +190,7 @@ Good examples:
|
||||
|
||||
- `{"asset_type":"architecture_diagram","purpose":"Explain component relationships.","suggested_query":"service architecture diagram","fallback_if_missing":"Draw a component diagram with grouped boxes, connector arrows, and short labels."}`
|
||||
- `{"asset_type":"logo","purpose":"Identify the customer context.","suggested_query":"customer logo","fallback_if_missing":"Use a text label in a small badge."}`
|
||||
- `{"asset_type":"chart","purpose":"Show adoption trend.","suggested_query":"monthly adoption trend chart","fallback_if_missing":"Render a native `<chart>` using the provided series when available; otherwise render a native `<chart>` with mock placeholder values and label it as 模拟数据,仅占位,待替换真实数据."}`
|
||||
- `{"asset_type":"chart","purpose":"Show adoption trend.","suggested_query":"monthly adoption trend chart","fallback_if_missing":"Draw a simple trend line chart with axis labels and data points."}`
|
||||
|
||||
## XML Generation Contract
|
||||
|
||||
@@ -230,7 +201,6 @@ Before writing each slide XML, map the plan fields to concrete decisions:
|
||||
- `visual_focus` determines the largest visual region or emphasized object.
|
||||
- `text_density` caps visible text volume.
|
||||
- `asset_need` informs placeholder diagrams, icons, charts, screenshots, or shape-based fallback visuals only. Missing real assets must use `fallback_if_missing`, not blank regions.
|
||||
- `chart_contract` locks supported standard data charts to native `<chart>` output. Manual approximations are allowed only when the planned chart type is unsupported by `<chart>` or when the visual is explicitly non-data/decorative.
|
||||
|
||||
After creating the PPT, fetch the presentation and verify:
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3018,7 +3018,7 @@
|
||||
|
||||
子元素(mermaid 与 svg 二选一):
|
||||
- mermaid: Mermaid 源码文本, 可使用 CDATA 包裹
|
||||
适用场景: 流程图、时序图、思维导图、类图、甘特图、ER 图、用户旅程等结构图
|
||||
适用场景: 流程图、时序图、思维导图、类图、甘特图、饼图等结构化图表
|
||||
特点: 用简短的文本声明描述图表逻辑, 由渲染引擎自动布局, 无需手动计算坐标
|
||||
示例: <mermaid><![CDATA[flowchart TD\n A[开始] --> B[结束]]]></mermaid>
|
||||
- svg: SVG 内容
|
||||
|
||||
@@ -263,56 +263,7 @@
|
||||
- `<chartLegend>`
|
||||
- `<chartTooltip>`
|
||||
|
||||
完整图表类型覆盖示例见 [slides_chart_demo.xml](slides_chart_demo.xml),其中包含柱状、条形、折线、面积、饼 / 环、雷达等原生 `<chart>` 示例,以及散点、气泡、漏斗、帕累托、瀑布等 `<whiteboard>` SVG 图表示例。
|
||||
|
||||
组合图示例(来自 [slides_chart_demo.xml](slides_chart_demo.xml)):
|
||||
|
||||
```xml
|
||||
<chart width="556" height="350" topLeftX="42" topLeftY="132">
|
||||
<chartPlotArea>
|
||||
<chartPlot type="combo">
|
||||
<chartExtra/>
|
||||
<chartSeriesList>
|
||||
<chartSeries index="1" comboType="column"/>
|
||||
<chartSeries index="2" comboType="line" yAxisPosition="right">
|
||||
<chartTooltip format="0%"/>
|
||||
</chartSeries>
|
||||
</chartSeriesList>
|
||||
</chartPlot>
|
||||
<chartAxes>
|
||||
<chartAxis type="x">
|
||||
<chartLabel fontSize="10"/>
|
||||
</chartAxis>
|
||||
<chartAxis type="y" position="left">
|
||||
<chartGridLine color="rgb(226, 232, 240)"/>
|
||||
<chartLabel fontSize="10"/>
|
||||
</chartAxis>
|
||||
<chartAxis type="y" position="right">
|
||||
<chartLabel fontSize="10" format="0%"/>
|
||||
</chartAxis>
|
||||
</chartAxes>
|
||||
</chartPlotArea>
|
||||
<chartLegend position="bottom" fontSize="11"/>
|
||||
<chartData>
|
||||
<dim1>
|
||||
<chartField name="季度">24Q1,24Q2,24Q3,24Q4,25Q1,25Q2,25Q3,25Q4</chartField>
|
||||
</dim1>
|
||||
<dim2>
|
||||
<chartField name="营收">180,195,210,245,220,238,258,296</chartField>
|
||||
<chartField name="增速">0.08,0.12,0.15,0.18,0.22,0.22,0.23,0.21</chartField>
|
||||
</dim2>
|
||||
</chartData>
|
||||
<chartTitle fontSize="12" color="rgba(15, 30, 58, 1)" bold="true">营收(亿美元, 左轴) · 同比增速(%, 右轴)</chartTitle>
|
||||
<chartStyle>
|
||||
<chartBackground color="rgba(0, 0, 0, 0)"/>
|
||||
<chartBorder color="rgb(222, 224, 227)" width="0"/>
|
||||
<chartColorTheme>
|
||||
<color value="rgb(28, 71, 120)"/>
|
||||
<color value="rgb(240, 129, 54)"/>
|
||||
</chartColorTheme>
|
||||
</chartStyle>
|
||||
</chart>
|
||||
```
|
||||
如果要写图表 XML,建议直接以 XSD 为准,不要自行发明更简化的 chart DSL。
|
||||
|
||||
## 样式元素
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ XSD 中的 `title`、`headline`、`sub-headline`、`body`、`caption` 主要出
|
||||
### whiteboard
|
||||
|
||||
```xml
|
||||
<!-- SVG 模式:<chart> 不支持的图表或自定义视觉、装饰元素 -->
|
||||
<!-- SVG 模式:数据图表、装饰元素 -->
|
||||
<whiteboard topLeftX="580" topLeftY="120" width="340" height="280">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="60" y="80" width="40" height="140" rx="3" fill="rgba(59,130,246,0.85)"/>
|
||||
|
||||
@@ -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`。
|
||||
|
||||
## 定位
|
||||
@@ -170,7 +170,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. 应用已发布并安装到当前租户。
|
||||
|
||||
@@ -24,7 +24,7 @@ metadata:
|
||||
|
||||
## 快速决策
|
||||
|
||||
- 用户要**整理 / 盘点 / 归类 / 重构知识库、个人文档库、文档库目录或 Wiki 节点结构**,或要生成整理方案、目标目录树、移动计划时,不要只使用 Wiki 节点 API。必须先阅读 [`../lark-drive/references/lark-drive-workflow.md`](../lark-drive/references/lark-drive-workflow.md),再按其中 `Workflow Registry` 进入 [`knowledge_organize`](../lark-drive/references/lark-drive-workflow-knowledge-organize.md) workflow;该 workflow 负责 Drive / Wiki / 个人文档库的统一入口解析、资源盘点、分类计划、写前确认和结果验证。
|
||||
- 用户要**整理 / 盘点 / 归类 / 重构知识库、个人文档库、文档库目录或 Wiki 节点结构**,或要生成整理方案、目标目录树、移动计划时,不要只使用 Wiki 节点 API。必须先阅读 [`../lark-drive/references/lark-drive-workflow-knowledge-organize.md`](../lark-drive/references/lark-drive-workflow-knowledge-organize.md),该 workflow 负责 Drive / Wiki / 个人文档库的统一入口解析、资源盘点、分类计划、写前确认和结果验证。
|
||||
- 用户给的是知识库 URL(`.../wiki/<token>`),且后续要查成员/加成员/删成员:先调用 `lark-cli wiki spaces get_node --params '{"token":"<wiki_token>"}'` 获取 `space_id`,后续成员接口统一使用 `space_id`。
|
||||
- 用户要**删除**知识空间(`wiki +delete-space`)但只给了名称或 URL:**不能**把名称 / URL 原样传给 `--space-id`,必须先解析出真实 `space_id`。解析方式:
|
||||
- URL(`.../wiki/<token>`):`lark-cli wiki spaces get_node --params '{"token":"<wiki_token>"}' --format json`,读 `data.node.space_id`。
|
||||
|
||||
@@ -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