mirror of
https://github.com/larksuite/cli.git
synced 2026-07-06 16:18:05 +08:00
Compare commits
3 Commits
feat-histo
...
docs/lark-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bc6d30eeb | ||
|
|
72c294712c | ||
|
|
37f4f899b2 |
@@ -20,9 +20,9 @@ import (
|
||||
)
|
||||
|
||||
// driveSearchErrUserNotVisible is the Lark service code returned by
|
||||
// doc_wiki/search when an open_id referenced in --creator-ids / --sharer-ids
|
||||
// falls outside the app's user-visibility scope (different from the
|
||||
// search:docs:read API scope).
|
||||
// doc_wiki/search when an open_id referenced in an identity filter falls
|
||||
// outside the app's user-visibility scope (different from the search:docs:read
|
||||
// API scope).
|
||||
const driveSearchErrUserNotVisible = 99992351
|
||||
|
||||
// open_time has a server-side cap of 3 months per request. Rather than
|
||||
@@ -79,6 +79,8 @@ var DriveSearch = common.Shortcut{
|
||||
|
||||
{Name: "mine", Type: "bool", Desc: "restrict to docs I own (server-side owner semantic, NOT original creator; uses current user's open_id)"},
|
||||
{Name: "creator-ids", Desc: "comma-separated owner open_ids (API field is creator_ids but matched by owner); mutually exclusive with --mine"},
|
||||
{Name: "created-by-me", Type: "bool", Desc: "restrict to docs originally created by me (uses current user's open_id as original_creator_ids)"},
|
||||
{Name: "original-creator-ids", Desc: "comma-separated original creator open_ids; mutually exclusive with --created-by-me"},
|
||||
|
||||
{Name: "edited-since", Desc: "start of [my edited] time window (e.g. 7d, 1m, 1y, 2026-04-01, RFC3339, unix seconds)"},
|
||||
{Name: "edited-until", Desc: "end of [my edited] time window"},
|
||||
@@ -108,7 +110,7 @@ var DriveSearch = common.Shortcut{
|
||||
Tips: []string{
|
||||
"Time flags accept relative (e.g. 7d, 1m, 1y), absolute (2026-04-01, RFC3339), or unix seconds.",
|
||||
"my_edit_time and my_comment_time are hour-aggregated server-side; sub-hour inputs are snapped and a notice is printed to stderr.",
|
||||
"Use --mine for a quick \"docs I own\" filter (owner semantic, not original creator). For other people, use --creator-ids ou_xxx,ou_yyy.",
|
||||
"Use --created-by-me for \"docs I created\". Use --mine for \"docs I own\" (owner semantic).",
|
||||
"--folder-tokens limits to doc-only search; --space-ids limits to wiki-only. They cannot be combined.",
|
||||
},
|
||||
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
|
||||
@@ -164,6 +166,9 @@ type driveSearchSpec struct {
|
||||
Mine bool
|
||||
CreatorIDs []string
|
||||
|
||||
CreatedByMe bool
|
||||
OriginalCreatorIDs []string
|
||||
|
||||
EditedSince string
|
||||
EditedUntil string
|
||||
CommentedSince string
|
||||
@@ -193,6 +198,9 @@ func readDriveSearchSpec(runtime *common.RuntimeContext) driveSearchSpec {
|
||||
Mine: runtime.Bool("mine"),
|
||||
CreatorIDs: common.SplitCSV(runtime.Str("creator-ids")),
|
||||
|
||||
CreatedByMe: runtime.Bool("created-by-me"),
|
||||
OriginalCreatorIDs: common.SplitCSV(runtime.Str("original-creator-ids")),
|
||||
|
||||
EditedSince: runtime.Str("edited-since"),
|
||||
EditedUntil: runtime.Str("edited-until"),
|
||||
CommentedSince: runtime.Str("commented-since"),
|
||||
@@ -221,12 +229,18 @@ func buildDriveSearchRequest(spec driveSearchSpec, userOpenID string, now time.T
|
||||
if spec.Mine && len(spec.CreatorIDs) > 0 {
|
||||
return nil, nil, errs.NewValidationError(errs.SubtypeInvalidArgument, "cannot combine --mine and --creator-ids")
|
||||
}
|
||||
if spec.CreatedByMe && len(spec.OriginalCreatorIDs) > 0 {
|
||||
return nil, nil, errs.NewValidationError(errs.SubtypeInvalidArgument, "cannot combine --created-by-me and --original-creator-ids")
|
||||
}
|
||||
if len(spec.FolderTokens) > 0 && len(spec.SpaceIDs) > 0 {
|
||||
return nil, nil, errs.NewValidationError(errs.SubtypeInvalidArgument, "cannot combine --folder-tokens and --space-ids; doc and wiki scoped search cannot be combined")
|
||||
}
|
||||
if spec.Mine && userOpenID == "" {
|
||||
return nil, nil, errs.NewValidationError(errs.SubtypeInvalidArgument, "--mine requires a logged-in user open_id, but none is configured; run `lark-cli auth login` or set user open_id in config").WithParam("--mine")
|
||||
}
|
||||
if spec.CreatedByMe && userOpenID == "" {
|
||||
return nil, nil, errs.NewValidationError(errs.SubtypeInvalidArgument, "--created-by-me requires a logged-in user open_id, but none is configured; run `lark-cli auth login` or set user open_id in config").WithParam("--created-by-me")
|
||||
}
|
||||
|
||||
if err := validateDocTypes(spec.DocTypes); err != nil {
|
||||
return nil, nil, err
|
||||
@@ -256,13 +270,20 @@ func buildDriveSearchRequest(spec driveSearchSpec, userOpenID string, now time.T
|
||||
notices = append(notices, n)
|
||||
}
|
||||
|
||||
// Creator identity.
|
||||
// Identity filters. creator_ids is owner; original_creator_ids is the
|
||||
// immutable document creator.
|
||||
switch {
|
||||
case spec.Mine:
|
||||
filter["creator_ids"] = []string{userOpenID}
|
||||
case len(spec.CreatorIDs) > 0:
|
||||
filter["creator_ids"] = spec.CreatorIDs
|
||||
}
|
||||
switch {
|
||||
case spec.CreatedByMe:
|
||||
filter["original_creator_ids"] = []string{userOpenID}
|
||||
case len(spec.OriginalCreatorIDs) > 0:
|
||||
filter["original_creator_ids"] = spec.OriginalCreatorIDs
|
||||
}
|
||||
|
||||
// Time dimensions — each fills at most one filter key; hour-aggregated ones
|
||||
// also contribute notices.
|
||||
@@ -358,6 +379,11 @@ func validateDriveSearchIDs(spec driveSearchSpec) error {
|
||||
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--creator-ids %q: %s", id, err).WithParam("--creator-ids")
|
||||
}
|
||||
}
|
||||
for _, id := range spec.OriginalCreatorIDs {
|
||||
if _, err := common.ValidateUserIDTyped("--original-creator-ids", id); err != nil {
|
||||
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--original-creator-ids %q: %s", id, err).WithParam("--original-creator-ids")
|
||||
}
|
||||
}
|
||||
if n := len(spec.ChatIDs); n > driveSearchMaxChatIDs {
|
||||
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--chat-ids: max %d values per request, got %d", driveSearchMaxChatIDs, n).WithParam("--chat-ids")
|
||||
}
|
||||
@@ -635,7 +661,7 @@ func enrichDriveSearchError(err error) error {
|
||||
if !ok || p.Code != driveSearchErrUserNotVisible {
|
||||
return err
|
||||
}
|
||||
p.Hint = "one or more open_ids in --creator-ids / --sharer-ids are outside this app's user-visibility scope (this is the app's contact visibility, not the search:docs:read API scope); ask an admin to grant the app visibility to those users in the developer console, or drop the unreachable open_ids"
|
||||
p.Hint = "one or more open_ids in --creator-ids / --original-creator-ids / --sharer-ids are outside this app's user-visibility scope (this is the app's contact visibility, not the search:docs:read API scope); ask an admin to grant the app visibility to those users in the developer console, or drop the unreachable open_ids"
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -245,9 +245,10 @@ func TestValidateDriveSearchIDs(t *testing.T) {
|
||||
t.Run("all valid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
spec := driveSearchSpec{
|
||||
CreatorIDs: []string{"ou_aaa"},
|
||||
ChatIDs: []string{"oc_xxx"},
|
||||
SharerIDs: []string{"ou_bbb"},
|
||||
CreatorIDs: []string{"ou_aaa"},
|
||||
OriginalCreatorIDs: []string{"ou_ccc"},
|
||||
ChatIDs: []string{"oc_xxx"},
|
||||
SharerIDs: []string{"ou_bbb"},
|
||||
}
|
||||
if err := validateDriveSearchIDs(spec); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
@@ -275,6 +276,24 @@ func TestValidateDriveSearchIDs(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("bad original creator id format", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := validateDriveSearchIDs(driveSearchSpec{OriginalCreatorIDs: []string{"u_bad"}})
|
||||
if err == nil || !strings.Contains(err.Error(), "--original-creator-ids") {
|
||||
t.Fatalf("expected --original-creator-ids error, got: %v", err)
|
||||
}
|
||||
var vErr *errs.ValidationError
|
||||
if !errors.As(err, &vErr) {
|
||||
t.Fatalf("expected *errs.ValidationError, got %T", err)
|
||||
}
|
||||
if vErr.Subtype != errs.SubtypeInvalidArgument {
|
||||
t.Fatalf("Subtype = %q, want %q", vErr.Subtype, errs.SubtypeInvalidArgument)
|
||||
}
|
||||
if vErr.Param != "--original-creator-ids" {
|
||||
t.Fatalf("Param = %q, want --original-creator-ids", vErr.Param)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("bad chat id format", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := validateDriveSearchIDs(driveSearchSpec{ChatIDs: []string{"chat_bad"}})
|
||||
@@ -727,6 +746,33 @@ func TestBuildDriveSearchRequest(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("--created-by-me fills original_creator_ids from userOpenID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
req, _, err := buildDriveSearchRequest(driveSearchSpec{CreatedByMe: true}, userOpenID, now)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
got := req["doc_filter"].(map[string]interface{})["original_creator_ids"].([]string)
|
||||
if len(got) != 1 || got[0] != userOpenID {
|
||||
t.Fatalf("expected [userOpenID], got %v", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("--original-creator-ids fills original_creator_ids", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
spec := driveSearchSpec{OriginalCreatorIDs: []string{"ou_a", "ou_b"}}
|
||||
req, _, err := buildDriveSearchRequest(spec, userOpenID, now)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
for _, filterKey := range []string{"doc_filter", "wiki_filter"} {
|
||||
got := req[filterKey].(map[string]interface{})["original_creator_ids"].([]string)
|
||||
if !reflect.DeepEqual(got, []string{"ou_a", "ou_b"}) {
|
||||
t.Fatalf("%s: expected explicit original creator ids, got %v", filterKey, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("--mine without userOpenID errors", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, _, err := buildDriveSearchRequest(driveSearchSpec{Mine: true}, "", now)
|
||||
@@ -735,6 +781,14 @@ func TestBuildDriveSearchRequest(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("--created-by-me without userOpenID errors", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, _, err := buildDriveSearchRequest(driveSearchSpec{CreatedByMe: true}, "", now)
|
||||
if err == nil || !strings.Contains(err.Error(), "--created-by-me") {
|
||||
t.Fatalf("expected --created-by-me error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("--mine + --creator-ids mutually exclusive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
spec := driveSearchSpec{Mine: true, CreatorIDs: []string{"ou_x"}}
|
||||
@@ -756,6 +810,15 @@ func TestBuildDriveSearchRequest(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("--created-by-me + --original-creator-ids mutually exclusive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
spec := driveSearchSpec{CreatedByMe: true, OriginalCreatorIDs: []string{"ou_x"}}
|
||||
_, _, err := buildDriveSearchRequest(spec, userOpenID, now)
|
||||
if err == nil || !strings.Contains(err.Error(), "--created-by-me") {
|
||||
t.Fatalf("expected exclusion error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("--folder-tokens + --space-ids mutually exclusive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
spec := driveSearchSpec{
|
||||
|
||||
@@ -33,6 +33,7 @@ lark-cli docs +update --api-version v2 --doc "文档URL或token" --command appen
|
||||
> **格式选择规则(全局):**
|
||||
> - **创建 / 导入场景**(`docs +create`,或 `docs +update --command append/overwrite` 的整段写入):XML 和 Markdown 都可以。用户提供 `.md` 本地文件、或明确说"导入 Markdown"时,直接用 Markdown;否则默认 XML(可用 callout、grid、checkbox 等富 block)。
|
||||
> - **精准编辑场景**(`docs +update` 的 `str_replace` / `block_insert_after` / `block_replace` / `block_delete` / `block_move_after` 等局部精修指令):优先使用 XML(`--doc-format xml`,即默认值)。XML 能稳定表达 block 结构和样式,局部精修更可控;不要因为 Markdown 更简单就自行切换。
|
||||
> - **读取理解场景**(总结、分析、问答、提取信息、快速浏览):如果不需要 block ID、资源 token 或精确样式结构,优先使用 Markdown(`--doc-format markdown`),减少结构噪音。
|
||||
|
||||
## 快速决策
|
||||
- 用户需要“某个 block 的直达链接 / 锚点链接”时:返回 `文档基础 URL#block_id`。如果当前只有文档 URL 没有 block_id,先用 `docs +fetch --detail with-ids` 拿到目标 block 的 id
|
||||
|
||||
@@ -19,7 +19,7 @@ metadata:
|
||||
## 快速决策
|
||||
|
||||
- 用户要**整理云盘 / 文件夹 / 文档库 / 知识库 / 个人文档库**,或要“盘点目录结构、找出未归档/临时/重复/空目录、生成整理方案”,必须先阅读 [`references/lark-drive-workflow-knowledge-organize.md`](references/lark-drive-workflow-knowledge-organize.md)。默认只生成方案;创建目录、移动资源、申请权限都必须单独确认。
|
||||
- 用户要**搜文档 / Wiki / 电子表格 / 多维表格 / 云空间(云盘/云存储)对象**,优先使用 `lark-cli drive +search`。自然语言里"最近我编辑过的"、"我创建的"(→ `--mine`,实为 owner 语义)、"最近一周我打开过的 xxx"、"某人 owner 的 docx" 等直接映射到扁平 flag,避免手写嵌套 JSON。
|
||||
- 用户要**搜文档 / 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。
|
||||
- 用户要把本地 `.xlsx` / `.csv` / `.base` 导入成 Base / 多维表格 / bitable,第一步必须使用 `lark-cli drive +import --type bitable`。
|
||||
@@ -110,7 +110,7 @@ Shortcut 是对常用操作的高级封装(`lark-cli drive +<verb> [flags]`)
|
||||
|
||||
| Shortcut | 说明 |
|
||||
|----------|----------|
|
||||
| [`+search`](references/lark-drive-search.md) | 搜索文档、Wiki、表格、文件夹等云空间对象;支持 `--edited-since`、`--mine`、`--doc-types` 等扁平 flag。 |
|
||||
| [`+search`](references/lark-drive-search.md) | 搜索文档、Wiki、表格、文件夹等云空间对象;支持 `--edited-since`、`--created-by-me`、`--mine`、`--doc-types` 等扁平 flag;区分 original creator 与 owner 语义。 |
|
||||
| [`+upload`](references/lark-drive-upload.md) | 上传本地文件到 Drive 文件夹或 wiki 节点。 |
|
||||
| [`+create-folder`](references/lark-drive-create-folder.md) | 新建 Drive 文件夹,支持父文件夹与 bot 创建后自动授权。 |
|
||||
| [`+download`](references/lark-drive-download.md) | 下载 Drive 文件到本地。 |
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
核心特性:
|
||||
|
||||
- 把常用过滤条件全部**扁平化为独立 flag**(`--edited-since`、`--mine`、`--doc-types`、`--folder-tokens` 等),不再要求用户或 AI 手写嵌套 `--filter` JSON
|
||||
- 把常用过滤条件全部**扁平化为独立 flag**(`--edited-since`、`--created-by-me`、`--mine`、`--doc-types`、`--folder-tokens` 等),不再要求用户或 AI 手写嵌套 `--filter` JSON
|
||||
- 额外暴露了 4 个"我"维度:`my_edit_time`(我编辑过)、`my_comment_time`(我评论过)、`open_time`(我打开过)、`create_time`(文档创建时间)——直接对应用户自然语言里的"最近我编辑过的"、"我评论过的"等表达
|
||||
- 自动处理 `my_edit_time` / `my_comment_time` 的小时级聚合(服务端存储粒度):亚小时输入会向整点 snap,并在 stderr 打出提示
|
||||
- `--mine` 一键从当前登录用户的 open_id 填 `creator_ids`,不必再先去查 contact(注意 `creator_ids` 服务端按 **owner / 文档归属人** 语义匹配,不是“最初创建人”,详见下文「身份维度」)
|
||||
- `--created-by-me` 一键从当前登录用户的 open_id 填 `original_creator_ids`,匹配“我最初创建的”;`--mine` 仍填 `creator_ids`,匹配 owner / 文档归属人
|
||||
|
||||
> **资源发现入口统一**:`drive +search` 同样返回 `SHEET` / `Base` / `FOLDER` 等全部云空间(云盘/云存储)对象,不只是文档 / Wiki。用户说"找一个表格"、"找报表"、"最近打开的表格"时,也从这里开始;定位后再切到对应业务 skill(如 `lark-sheets`)做对象内部操作。
|
||||
|
||||
@@ -21,18 +21,19 @@
|
||||
> 错误:`lark-cli drive +search 方案`
|
||||
> `+search` 不接受位置参数;空 `--query` 或省略 `--query` 表示纯靠 filter 浏览(合法)。
|
||||
>
|
||||
> **列表型请求不要硬塞关键词**:如果用户只是要求"我这月创建的所有文档"、"最近半年我编辑过的文档"、"按类型分类统计"这类范围浏览 / 汇总请求,且没有给出标题片段或业务关键词,应使用 `--query ""` 搭配 `--mine`、`--created-*`、`--edited-*`、`--doc-types` 等过滤条件。不要把"查找"、"所有文档"、"最近更新过"、"按类型分类统计"这类动作词或统计意图放进 `--query`,否则会把本来应靠 filter 命中的结果过度收窄。
|
||||
> **列表型请求不要硬塞关键词**:如果用户只是要求"我这月创建的所有文档"、"最近半年我编辑过的文档"、"按类型分类统计"这类范围浏览 / 汇总请求,且没有给出标题片段或业务关键词,应使用 `--query ""` 搭配 `--created-by-me`、`--mine`、`--created-*`、`--edited-*`、`--doc-types` 等过滤条件。不要把"查找"、"所有文档"、"最近更新过"、"按类型分类统计"这类动作词或统计意图放进 `--query`,否则会把本来应靠 filter 命中的结果过度收窄。
|
||||
|
||||
### 自然语言 → 命令映射速查
|
||||
|
||||
| 用户说 | 命令 |
|
||||
|---|---|
|
||||
| 我这月创建的所有文档,按类型分类统计 | `lark-cli drive +search --query "" --mine --created-since "<YYYY-MM-DD>" --created-until "<YYYY-MM-DD>"` |
|
||||
| 我这月创建的所有文档,按类型分类统计 | `lark-cli drive +search --query "" --created-by-me --created-since "<YYYY-MM-DD>" --created-until "<YYYY-MM-DD>"` |
|
||||
| 最近半年我编辑过的文档,看看哪些最近更新过 | `lark-cli drive +search --query "" --edited-since 6m --sort edit_time` |
|
||||
| 最近一个月我编辑过的文档 | `lark-cli drive +search --query "" --edited-since 1m` |
|
||||
| 最近一个月我编辑过 且 我评论过的 | `lark-cli drive +search --query "" --edited-since 1m --commented-since 1m` |
|
||||
| 最近一周我打开过的表格 | `lark-cli drive +search --query "" --opened-since 7d --doc-types sheet` |
|
||||
| 我 owner 的所有文档(owner 语义,非"我最初创建") | `lark-cli drive +search --query "" --mine` |
|
||||
| 我最初创建、后来转给王五 owner 的文档 | `lark-cli drive +search --query "" --created-by-me --creator-ids ou_wangwu` |
|
||||
| 我 owner、30-60 天前创建的文档(粗略"上个月",按 30 天滑窗算;`--mine` 是 owner,`--created-*` 才是文档创建时间) | `lark-cli drive +search --query "" --mine --created-since 2m --created-until 1m` |
|
||||
| 我 owner、2026 年 3 月创建的文档(精确日历月;同上,owner + 创建时间窗两个维度) | `lark-cli drive +search --query "" --mine --created-since 2026-03-01 --created-until 2026-04-01` |
|
||||
| 关键词"预算",最近一周我打开过,按编辑时间降序 | `lark-cli drive +search --query 预算 --opened-since 7d --sort edit_time` |
|
||||
@@ -77,7 +78,7 @@ lark-cli drive +search --query 方案 --page-token '<PAGE_TOKEN>'
|
||||
|
||||
对"所有文档"、"按类型分类统计"、"最近更新过"这类请求,不要只跑一次搜索后直接回答。标准流程:
|
||||
|
||||
1. 先把自然语言拆成过滤条件:所有权(`--mine` / `--creator-ids`)、时间维度(`--created-*` / `--edited-*` / `--opened-*` / `--commented-*`)、类型(`--doc-types`)、空间或文件夹范围。
|
||||
1. 先把自然语言拆成过滤条件:原始创建者(`--created-by-me` / `--original-creator-ids`)、所有权(`--mine` / `--creator-ids`)、时间维度(`--created-*` / `--edited-*` / `--opened-*` / `--commented-*`)、类型(`--doc-types`)、空间或文件夹范围。
|
||||
2. 没有真实业务关键词时保持 `--query ""`;不要把"所有文档"、"统计"、"最近更新"放进 query。
|
||||
3. 检查返回结果的 `doc_type` / `result_meta.doc_types`、创建/编辑时间和 URL/token 是否与过滤目标一致;明显不符合的结果不要计入答案。
|
||||
4. 用户要求"所有 / 全量 / 统计"时按 `has_more` 翻页并累积去重;不要只用第一页推断总量。返回体里的 `total` 不可靠,统计要以实际去重后的结果为准。
|
||||
@@ -103,14 +104,16 @@ lark-cli drive +search --query 方案 --page-token '<PAGE_TOKEN>'
|
||||
| `--page-token <token>` | 否 | 上一次响应里的 `page_token`,用于翻页 |
|
||||
| `--format` | 否 | `json`(默认)/ `pretty` |
|
||||
|
||||
### 身份(owner 维度,API 字段名 `creator_ids`)
|
||||
### 身份维度
|
||||
|
||||
> **语义说明(重要)**:`creator_ids`(含 `--mine` / `--creator-ids`)虽然 OpenAPI 字段名是 “creator”,但服务端实际按 **owner(文档归属人 / 负责人)** 语义匹配,**不是“最初创建人”**:我创建后转交他人的文档不会命中,他人创建后转给我(我成为 owner)的会命中。用户说“我的 / 我创建的 / 我负责的”文档都路由到 `--mine`,但要清楚它返回的是“我 owner 的”。
|
||||
> **语义说明(重要)**:`creator_ids`(含 `--mine` / `--creator-ids`)虽然字段名是 “creator”,但服务端实际按 **owner(文档归属人 / 负责人)** 语义匹配,**不是“最初创建人”**。真正的原始创建者使用 `original_creator_ids`(CLI 为 `--created-by-me` / `--original-creator-ids`)。
|
||||
|
||||
| 参数 | 映射 | 说明 |
|
||||
|---|---|---|
|
||||
| `--mine` | `creator_ids = [当前用户 open_id]` | bool。一键“我 owner 的”(**不是**“我最初创建的”);从当前登录用户身份(`runtime.UserOpenId()`)解析 open_id,取不到直接报错(提示运行 `lark-cli auth login`) |
|
||||
| `--creator-ids ou_x,ou_y` | `creator_ids = [...]` | 显式 open_id 列表,逗号分隔,按 **owner** 匹配;**与 `--mine` 互斥** |
|
||||
| `--created-by-me` | `original_creator_ids = [当前用户 open_id]` | bool。一键“我最初创建的”;从当前登录用户身份解析 open_id,取不到直接报错 |
|
||||
| `--original-creator-ids ou_x,ou_y` | `original_creator_ids = [...]` | 显式 open_id 列表,逗号分隔,按**原始创建者**匹配;**与 `--created-by-me` 互斥** |
|
||||
|
||||
### 时间维度(每个维度一对 since/until)
|
||||
|
||||
@@ -187,7 +190,7 @@ stdout 的 JSON 输出不受影响。`open_time` / `create_time` 不做 snap。
|
||||
|
||||
## 决策规则
|
||||
|
||||
- **身份快捷方式**:用户说“我的 / 我创建的 / 我负责的”文档,直接 `--mine` 即可,不需要先查 contact 拿 open_id。注意 `--mine` 是 **owner** 语义(我归属/负责的),不是“我最初创建的”——转交出去的不算、转交给我的算。
|
||||
- **身份快捷方式**:用户说“我创建的 / 我新建的 / 我最初创建的”文档,用 `--created-by-me`;用户说“我的 / 我负责的 / 我 owner 的”文档,用 `--mine`。`--mine` 是 owner 语义:转交出去的不算、转交给我的算。
|
||||
- **时间维度选择**:
|
||||
- "我编辑的"、"我修改的" → `--edited-since` / `--edited-until`
|
||||
- "我评论的"、"我回复过的" → `--commented-since` / `--commented-until`
|
||||
@@ -197,10 +200,10 @@ stdout 的 JSON 输出不受影响。`open_time` / `create_time` 不做 snap。
|
||||
- "某个文件夹下" → `--folder-tokens`(doc-only)
|
||||
- "某个 wiki 空间下" → `--space-ids`(wiki-only)
|
||||
- 两者不能同时使用,混用会报错
|
||||
- **身份 flag 互斥**:`--mine` 和 `--creator-ids` 不要同时传,会直接报错。“我和张三的”(owner)用 `--creator-ids ou_me,ou_zhangsan`(需要先拿到自己 open_id,但这种场景少见)。
|
||||
- **身份 flag 互斥**:`--mine` 和 `--creator-ids` 不要同时传;`--created-by-me` 和 `--original-creator-ids` 不要同时传。owner 维度与原始创建者维度可以组合,例如“我创建后转给王五 owner”用 `--created-by-me --creator-ids ou_wangwu`。
|
||||
- **实体补全**:
|
||||
- 用户说"某个群里",先用 `lark-im` 查 `chat_id`
|
||||
- 用户说“某人的 / 某人分享的”(非自己;`--creator-ids` 按 owner 匹配),先用 `lark-contact` 查 open_id,再填 `--creator-ids` / `--sharer-ids`
|
||||
- 用户说“某人负责/owner 的 / 某人创建的 / 某人分享的”(非自己),先用 `lark-contact` 查 open_id,再按语义填 `--creator-ids` / `--original-creator-ids` / `--sharer-ids`
|
||||
- **查询语义下推**:`--query` 支持的服务端高级语法(`intitle:`、`""`、`OR`、`-`)优先使用,不要先模糊搜再在客户端二次过滤。
|
||||
- **query 填写边界**:只有标题片段、业务名词、项目名、会议名、文件内容关键词才应进入 `--query`。仅描述动作、时间范围、所有权、统计方式的词不算关键词,保持 `--query ""` 并依赖 filters。
|
||||
- **证据核验**:列表/统计类答案必须来自搜索结果中的实际 URL/token 和类型/时间字段;内容问答必须能指出使用了哪些非污染候选。没有可验证候选时先扩大 query 或翻页,不要直接编总结。
|
||||
@@ -222,7 +225,7 @@ stdout 的 JSON 输出不受影响。`open_time` / `create_time` 不做 snap。
|
||||
|
||||
| code | 含义 | 处理 |
|
||||
|---|---|---|
|
||||
| `99992351` | `--creator-ids` / `--sharer-ids` 里有 open_id 超出**应用的通讯录可见范围**,服务端拒绝识别 | 让管理员在开发者后台把这些用户加进应用的"通讯录可见性"授权里;或把超出范围的 open_id 从参数里去掉。这和 `search:docs:read` scope 不是一回事 —— 是"应用能看见哪些人"而不是"应用能调用哪个接口" |
|
||||
| `99992351` | `--creator-ids` / `--original-creator-ids` / `--sharer-ids` 里有 open_id 超出**应用的通讯录可见范围**,服务端拒绝识别 | 让管理员在开发者后台把这些用户加进应用的"通讯录可见性"授权里;或把超出范围的 open_id 从参数里去掉。这和 `search:docs:read` scope 不是一回事 —— 是"应用能看见哪些人"而不是"应用能调用哪个接口" |
|
||||
|
||||
## 时间范围自动裁剪(`--opened-*` 专有)
|
||||
|
||||
|
||||
@@ -222,11 +222,27 @@ lark-cli im +messages-reply --message-id om_xxx --text "Let me take a look at th
|
||||
|
||||
The reply appears in the target message's thread and does not show up in the main chat stream.
|
||||
|
||||
## @Mention Format (text / post)
|
||||
## @Mention Format
|
||||
|
||||
- Recommended format: `<at user_id="ou_xxx">name</at>`
|
||||
The `<at>` syntax differs by message type. The shortcut only normalizes mentions for `text` and `post`; `interactive` card content is passed through verbatim, so cards must use the card-native syntax below.
|
||||
|
||||
### `text`
|
||||
|
||||
- `<at user_id="ou_xxx">name</at>` — the inner text is the mentioned user's display name and is optional (`<at user_id="ou_xxx"></at>` also works)
|
||||
- @all: `<at user_id="all"></at>`
|
||||
- The shortcut normalizes common variants like `<at id=...>` and `<at open_id=...>` into `user_id`, but `user_id` remains the recommended documented form
|
||||
|
||||
### `post`
|
||||
|
||||
- Inside a `text` or `md` element, the same inline form as `text` works: `<at user_id="ou_xxx">name</at>`
|
||||
- Or use a dedicated `at` element node: `{"tag":"at","user_id":"ou_xxx"}` (use `"all"` to mention everyone)
|
||||
|
||||
### `interactive` (card)
|
||||
|
||||
Card content is **not** normalized — use the card-native `<at>` syntax inside a `lark_md` / `markdown` element:
|
||||
|
||||
- single user by open_id: `<at id=ou_xxx></at>`
|
||||
- multiple users: `<at ids=ou_xxx1,ou_xxx2></at>`
|
||||
- by email: `<at email=user@example.com></at>`
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -224,11 +224,27 @@ lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Test\n\nhello' --dry
|
||||
}
|
||||
```
|
||||
|
||||
## @Mention Format (text / post)
|
||||
## @Mention Format
|
||||
|
||||
- Recommended format: `<at user_id="ou_xxx">name</at>`
|
||||
The `<at>` syntax differs by message type. The shortcut only normalizes mentions for `text` and `post`; `interactive` card content is passed through verbatim, so cards must use the card-native syntax below.
|
||||
|
||||
### `text`
|
||||
|
||||
- `<at user_id="ou_xxx">name</at>` — the inner text is the mentioned user's display name and is optional (`<at user_id="ou_xxx"></at>` also works)
|
||||
- @all: `<at user_id="all"></at>`
|
||||
- The shortcut normalizes common variants like `<at id=...>` and `<at open_id=...>` into `user_id`, but you should still document examples with `user_id`
|
||||
|
||||
### `post`
|
||||
|
||||
- Inside a `text` or `md` element, the same inline form as `text` works: `<at user_id="ou_xxx">name</at>`
|
||||
- Or use a dedicated `at` element node: `{"tag":"at","user_id":"ou_xxx"}` (use `"all"` to mention everyone)
|
||||
|
||||
### `interactive` (card)
|
||||
|
||||
Card content is **not** normalized — use the card-native `<at>` syntax inside a `lark_md` / `markdown` element:
|
||||
|
||||
- single user by open_id: `<at id=ou_xxx></at>`
|
||||
- multiple users: `<at ids=ou_xxx1,ou_xxx2></at>`
|
||||
- by email: `<at email=user@example.com></at>`
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user