mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 22:24:31 +08:00
Compare commits
1 Commits
docs/drive
...
feat/summa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58466a3d94 |
73
shortcuts/minutes/minutes_summary.go
Normal file
73
shortcuts/minutes/minutes_summary.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package minutes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/larksuite/cli/internal/output"
|
||||
"github.com/larksuite/cli/internal/validate"
|
||||
"github.com/larksuite/cli/shortcuts/common"
|
||||
)
|
||||
|
||||
const minutesSummaryMarkdownTip = "Summary accepts any text; unsupported Markdown is saved but may display as literal raw text in Minutes. For best rendering, prefer plain text, line breaks, headings (#, ##, ###), bold (**text**), and lists (-, *, or 1.)."
|
||||
|
||||
// MinutesSummary replaces the AI summary of a minute.
|
||||
var MinutesSummary = common.Shortcut{
|
||||
Service: "minutes",
|
||||
Command: "+summary",
|
||||
Description: "Replace the AI summary of a minute",
|
||||
Risk: "write",
|
||||
Scopes: []string{"minutes:minutes:update"},
|
||||
AuthTypes: []string{"user"},
|
||||
HasFormat: true,
|
||||
Flags: []common.Flag{
|
||||
{Name: "minute-token", Desc: "minute token", Required: true},
|
||||
{Name: "summary", Desc: "replacement summary text (Markdown subset renders best in Minutes)", Required: true, Input: []string{common.File, common.Stdin}},
|
||||
},
|
||||
Tips: []string{
|
||||
minutesSummaryMarkdownTip,
|
||||
"Use `lark-cli vc +notes --minute-tokens <token>` to read the current summary before replacing it.",
|
||||
},
|
||||
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
||||
minuteToken := runtime.Str("minute-token")
|
||||
if minuteToken == "" {
|
||||
return output.ErrValidation("--minute-token is required")
|
||||
}
|
||||
if err := validate.ResourceName(minuteToken, "--minute-token"); err != nil {
|
||||
return output.ErrValidation("%s", err)
|
||||
}
|
||||
summary := strings.TrimSpace(runtime.Str("summary"))
|
||||
if summary == "" {
|
||||
return output.ErrValidation("--summary is required")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
|
||||
return common.NewDryRunAPI().
|
||||
PUT(fmt.Sprintf("/open-apis/minutes/v1/minutes/%s/summary", validate.EncodePathSegment(runtime.Str("minute-token")))).
|
||||
Body(map[string]interface{}{"summary": "<summary markdown>"})
|
||||
},
|
||||
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
||||
minuteToken := runtime.Str("minute-token")
|
||||
summary := strings.TrimSpace(runtime.Str("summary"))
|
||||
|
||||
path := fmt.Sprintf("/open-apis/minutes/v1/minutes/%s/summary", validate.EncodePathSegment(minuteToken))
|
||||
body := map[string]interface{}{
|
||||
"summary": summary,
|
||||
}
|
||||
if _, err := runtime.CallAPI(http.MethodPut, path, nil, body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runtime.OutFormat(map[string]interface{}{
|
||||
"minute_token": minuteToken,
|
||||
"updated": true,
|
||||
}, nil, nil)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
221
shortcuts/minutes/minutes_summary_todo_test.go
Normal file
221
shortcuts/minutes/minutes_summary_todo_test.go
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package minutes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/larksuite/cli/internal/cmdutil"
|
||||
"github.com/larksuite/cli/internal/httpmock"
|
||||
)
|
||||
|
||||
func todoStub(token string) *httpmock.Stub {
|
||||
return &httpmock.Stub{
|
||||
Method: "PUT",
|
||||
URL: "/open-apis/minutes/v1/minutes/" + token + "/todo",
|
||||
Body: map[string]interface{}{"code": 0, "msg": "ok", "data": map[string]interface{}{}},
|
||||
}
|
||||
}
|
||||
|
||||
func firstTodoItem(t *testing.T, raw []byte) map[string]any {
|
||||
t.Helper()
|
||||
if len(raw) == 0 {
|
||||
t.Fatal("request body was not captured")
|
||||
}
|
||||
var body map[string]any
|
||||
if err := json.Unmarshal(raw, &body); err != nil {
|
||||
t.Fatalf("failed to parse captured body: %v", err)
|
||||
}
|
||||
items, _ := body["todo_items"].([]any)
|
||||
if len(items) != 1 {
|
||||
t.Fatalf("todo_items: want 1 item, got %d (%v)", len(items), body["todo_items"])
|
||||
}
|
||||
item, _ := items[0].(map[string]any)
|
||||
return item
|
||||
}
|
||||
|
||||
func TestMinutesSummary_DryRun(t *testing.T) {
|
||||
f, stdout, _, _ := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
|
||||
err := mountAndRun(t, MinutesSummary, []string{
|
||||
"+summary",
|
||||
"--minute-token", "obcn123456789",
|
||||
"--summary", "**Weekly sync**\n- follow up",
|
||||
"--dry-run",
|
||||
"--as", "user",
|
||||
}, f, stdout)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
out := stdout.String()
|
||||
if !strings.Contains(out, "PUT") || !strings.Contains(out, "/open-apis/minutes/v1/minutes/obcn123456789/summary") {
|
||||
t.Fatalf("dry-run output = %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesTodo_DryRun(t *testing.T) {
|
||||
f, stdout, _, _ := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
|
||||
err := mountAndRun(t, MinutesTodo, []string{
|
||||
"+todo",
|
||||
"--minute-token", "obcn123456789",
|
||||
"--todo", "- finish deck",
|
||||
"--is-done",
|
||||
"--dry-run",
|
||||
"--as", "user",
|
||||
}, f, stdout)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
out := stdout.String()
|
||||
if !strings.Contains(out, "PUT") || !strings.Contains(out, "/open-apis/minutes/v1/minutes/obcn123456789/todo") {
|
||||
t.Fatalf("dry-run output = %q", out)
|
||||
}
|
||||
if !strings.Contains(out, "todo_items") {
|
||||
t.Fatalf("dry-run output should contain todo_items, got %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesTodo_RequiresIsDone(t *testing.T) {
|
||||
f, _, stderr, _ := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
|
||||
err := mountAndRun(t, MinutesTodo, []string{
|
||||
"+todo",
|
||||
"--minute-token", "obcn123456789",
|
||||
"--todo", "finish deck",
|
||||
"--as", "user",
|
||||
}, f, stderr)
|
||||
if err == nil {
|
||||
t.Fatal("expected validation error for missing --is-done")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "is-done") && !strings.Contains(err.Error(), "todo-list") {
|
||||
t.Fatalf("error = %q, want message mentioning is-done or todo-list", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesTodo_Add_RequestBody(t *testing.T) {
|
||||
f, stdout, _, reg := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
stub := todoStub("obcn123456789")
|
||||
reg.Register(stub)
|
||||
|
||||
err := mountAndRun(t, MinutesTodo, []string{
|
||||
"+todo", "--minute-token", "obcn123456789",
|
||||
"--todo", "finish deck", "--is-done=false", "--as", "user",
|
||||
}, f, stdout)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
item := firstTodoItem(t, stub.CapturedBody)
|
||||
if item["content"] != "finish deck" {
|
||||
t.Errorf("content = %v, want finish deck", item["content"])
|
||||
}
|
||||
if item["is_done"] != false {
|
||||
t.Errorf("is_done = %v, want false", item["is_done"])
|
||||
}
|
||||
if _, ok := item["todo_id"]; ok {
|
||||
t.Errorf("add should not send todo_id, got %v", item["todo_id"])
|
||||
}
|
||||
if !strings.Contains(stdout.String(), "add") {
|
||||
t.Errorf("output should report add operation, got %q", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesTodo_Update_RequestBody(t *testing.T) {
|
||||
f, stdout, _, reg := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
stub := todoStub("obcn123456789")
|
||||
reg.Register(stub)
|
||||
|
||||
err := mountAndRun(t, MinutesTodo, []string{
|
||||
"+todo", "--minute-token", "obcn123456789",
|
||||
"--todo-id", "99", "--todo", "updated deck", "--is-done", "--as", "user",
|
||||
}, f, stdout)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
item := firstTodoItem(t, stub.CapturedBody)
|
||||
if item["todo_id"] != "99" {
|
||||
t.Errorf("todo_id = %v, want 99", item["todo_id"])
|
||||
}
|
||||
if item["content"] != "updated deck" {
|
||||
t.Errorf("content = %v, want updated deck", item["content"])
|
||||
}
|
||||
if item["is_done"] != true {
|
||||
t.Errorf("is_done = %v, want true", item["is_done"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesTodo_Delete_RequestBody(t *testing.T) {
|
||||
f, stdout, _, reg := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
stub := todoStub("obcn123456789")
|
||||
reg.Register(stub)
|
||||
|
||||
err := mountAndRun(t, MinutesTodo, []string{
|
||||
"+todo", "--minute-token", "obcn123456789",
|
||||
"--todo-id", "88", "--as", "user",
|
||||
}, f, stdout)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
item := firstTodoItem(t, stub.CapturedBody)
|
||||
if item["todo_id"] != "88" {
|
||||
t.Errorf("todo_id = %v, want 88", item["todo_id"])
|
||||
}
|
||||
if _, ok := item["content"]; ok {
|
||||
t.Errorf("delete should not send content, got %v", item["content"])
|
||||
}
|
||||
if _, ok := item["is_done"]; ok {
|
||||
t.Errorf("delete should not send is_done, got %v", item["is_done"])
|
||||
}
|
||||
// the todo id must never be surfaced to the user in the command output
|
||||
if strings.Contains(stdout.String(), "88") {
|
||||
t.Errorf("output must not expose the todo id, got %q", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesTodo_DeleteRejectsIsDone(t *testing.T) {
|
||||
f, _, _, _ := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
|
||||
err := mountAndRun(t, MinutesTodo, []string{
|
||||
"+todo", "--minute-token", "obcn123456789",
|
||||
"--todo-id", "88", "--is-done", "--as", "user",
|
||||
}, f, nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected validation error when --is-done is used to delete")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesTodo_RequiresAnyInput(t *testing.T) {
|
||||
f, _, _, _ := cmdutil.TestFactory(t, defaultConfig())
|
||||
warmTokenCache(t)
|
||||
|
||||
err := mountAndRun(t, MinutesTodo, []string{
|
||||
"+todo", "--minute-token", "obcn123456789", "--as", "user",
|
||||
}, f, nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected validation error when neither --todo nor --todo-id is provided")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinutesSummaryAndTodo_HelpMetadata(t *testing.T) {
|
||||
for _, tip := range MinutesSummary.Tips {
|
||||
if strings.Contains(tip, "raw text") {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatal("MinutesSummary tips should mention unsupported markdown display behavior")
|
||||
}
|
||||
123
shortcuts/minutes/minutes_todo.go
Normal file
123
shortcuts/minutes/minutes_todo.go
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package minutes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/larksuite/cli/internal/output"
|
||||
"github.com/larksuite/cli/internal/validate"
|
||||
"github.com/larksuite/cli/shortcuts/common"
|
||||
)
|
||||
|
||||
// minuteTodoOp describes the resolved single-todo operation derived from flags.
|
||||
type minuteTodoOp struct {
|
||||
operation string // add | update | delete
|
||||
item map[string]interface{} // the single todo_items entry sent to the API
|
||||
}
|
||||
|
||||
// MinutesTodo adds, updates, or deletes a single todo item on a minute.
|
||||
var MinutesTodo = common.Shortcut{
|
||||
Service: "minutes",
|
||||
Command: "+todo",
|
||||
Description: "Add, update, or delete a single todo item on a minute",
|
||||
Risk: "write",
|
||||
Scopes: []string{"minutes:minutes:update"},
|
||||
AuthTypes: []string{"user"},
|
||||
HasFormat: true,
|
||||
Flags: []common.Flag{
|
||||
{Name: "minute-token", Desc: "minute token (required)", Required: true},
|
||||
{Name: "todo", Desc: "todo plain-text content; required (with --is-done) to add or update", Input: []string{common.File, common.Stdin}},
|
||||
{Name: "is-done", Type: "bool", Desc: "completion flag; required together with --todo"},
|
||||
{Name: "todo-id", Desc: "id of an existing todo; provide to update (with --todo) or delete (without --todo); omit to add"},
|
||||
},
|
||||
Tips: []string{
|
||||
"Add a todo: `--todo \"...\" --is-done=false`.",
|
||||
"Update a todo: `--todo-id <id> --todo \"...\" --is-done`.",
|
||||
"Delete a todo: `--todo-id <id>` (omit --todo).",
|
||||
"`content` is plain text only; markdown formatting is not supported.",
|
||||
"Use `lark-cli vc +notes --minute-tokens <token>` to read current todos before writing.",
|
||||
},
|
||||
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
||||
minuteToken := runtime.Str("minute-token")
|
||||
if minuteToken == "" {
|
||||
return output.ErrValidation("--minute-token is required")
|
||||
}
|
||||
if err := validate.ResourceName(minuteToken, "--minute-token"); err != nil {
|
||||
return output.ErrValidation("%s", err)
|
||||
}
|
||||
if _, err := resolveMinuteTodoOp(runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
|
||||
return common.NewDryRunAPI().
|
||||
PUT(fmt.Sprintf("/open-apis/minutes/v1/minutes/%s/todo", validate.EncodePathSegment(runtime.Str("minute-token")))).
|
||||
Body(map[string]interface{}{
|
||||
"todo_items": "<todo_items array>",
|
||||
})
|
||||
},
|
||||
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
||||
minuteToken := runtime.Str("minute-token")
|
||||
op, err := resolveMinuteTodoOp(runtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/open-apis/minutes/v1/minutes/%s/todo", validate.EncodePathSegment(minuteToken))
|
||||
body := map[string]interface{}{
|
||||
"todo_items": []interface{}{op.item},
|
||||
}
|
||||
if _, err := runtime.CallAPI(http.MethodPut, path, nil, body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Intentionally omit the todo id from the output: users never see it.
|
||||
runtime.OutFormat(map[string]interface{}{
|
||||
"minute_token": minuteToken,
|
||||
"operation": op.operation,
|
||||
"updated": true,
|
||||
}, nil, nil)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func resolveMinuteTodoOp(runtime *common.RuntimeContext) (*minuteTodoOp, error) {
|
||||
todo := strings.TrimSpace(runtime.Str("todo"))
|
||||
todoID := strings.TrimSpace(runtime.Str("todo-id"))
|
||||
hasTodo := todo != ""
|
||||
hasTodoID := todoID != ""
|
||||
hasIsDone := runtime.Changed("is-done")
|
||||
|
||||
item := map[string]interface{}{}
|
||||
if hasTodoID {
|
||||
item["todo_id"] = todoID
|
||||
}
|
||||
|
||||
switch {
|
||||
case hasTodo:
|
||||
// add or update: content and is_done must appear together
|
||||
if !hasIsDone {
|
||||
return nil, output.ErrValidation("--todo requires --is-done")
|
||||
}
|
||||
item["content"] = todo
|
||||
item["is_done"] = runtime.Bool("is-done")
|
||||
if hasTodoID {
|
||||
return &minuteTodoOp{operation: "update", item: item}, nil
|
||||
}
|
||||
return &minuteTodoOp{operation: "add", item: item}, nil
|
||||
case hasTodoID:
|
||||
// delete: only the id is needed; content/is_done are not allowed
|
||||
if hasIsDone {
|
||||
return nil, output.ErrValidation("--is-done cannot be used to delete a todo (omit it, and provide only --todo-id)")
|
||||
}
|
||||
return &minuteTodoOp{operation: "delete", item: item}, nil
|
||||
default:
|
||||
return nil, output.ErrValidation("provide --todo (with --is-done) to add/update, or --todo-id alone to delete")
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,7 @@ func Shortcuts() []common.Shortcut {
|
||||
MinutesSearch,
|
||||
MinutesDownload,
|
||||
MinutesUpload,
|
||||
MinutesSummary,
|
||||
MinutesTodo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: lark-drive
|
||||
version: 1.0.0
|
||||
description: "飞书云空间(云盘/云存储):管理云空间(云盘/云存储)中的文件和文件夹。上传和下载文件、创建文件夹、复制/移动/删除文件、查看文件元数据、管理文档评论、管理文档权限、订阅用户评论变更事件、修改文件标题(docx、sheet、bitable、file、folder、wiki);也负责把本地 Word/Markdown/Excel/CSV/PPTX 以及 Base 快照(.base)导入为飞书在线云文档(docx、sheet、bitable、slides)。当用户需要上传或下载文件、整理云空间(云盘/云存储)目录、查看文件详情、管理评论、管理文档权限、修改文件标题、订阅用户评论变更事件,或要把本地文件导入成新版文档、电子表格、多维表格/Base/幻灯片时使用。涉及云空间/知识库/文档库的知识资产盘点、整理、治理,也从本 skill 进入。\"云空间\"、\"云盘\"和\"云存储\"是同一概念,用户说\"云盘\"、\"云存储\"、\"网盘\"、\"我的空间\"时均路由到本 skill。"
|
||||
description: "飞书云空间(云盘/云存储):管理云空间(云盘/云存储)中的文件和文件夹。上传和下载文件、创建文件夹、复制/移动/删除文件、查看文件元数据、管理文档评论、管理文档权限、订阅用户评论变更事件、修改文件标题(docx、sheet、bitable、file、folder、wiki);也负责把本地 Word/Markdown/Excel/CSV/PPTX 以及 Base 快照(.base)导入为飞书在线云文档(docx、sheet、bitable、slides)。当用户需要上传或下载文件、整理云空间(云盘/云存储)目录、查看文件详情、管理评论、管理文档权限、修改文件标题、订阅用户评论变更事件,或要把本地文件导入成新版文档、电子表格、多维表格/Base/幻灯片 时使用。\"云空间\"、\"云盘\"和\"云存储\"是同一概念,用户说\"云盘\"、\"云存储\"、\"网盘\"、\"我的空间\"时均路由到本 skill。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["lark-cli"]
|
||||
@@ -18,8 +18,7 @@ metadata:
|
||||
|
||||
## 快速决策
|
||||
|
||||
- 用户要**搜文档 / 知识库 / 电子表格 / 多维表格 / 云空间(云盘/云存储)对象**,优先使用 `lark-cli drive +search`。自然语言里"最近我编辑过的"、"我创建的"(→ `--mine`,实为 owner 语义)、"最近一周我打开过的 xxx"、"某人 owner 的 docx" 等直接映射到扁平 flag,避免手写嵌套 JSON。
|
||||
- 用户要**盘点/整理/治理云空间、知识库或文档库资产**,读取 [`lark-drive-knowledge-overview.md`](references/lark-drive-knowledge-overview.md) 做范围和 recipe 路由。
|
||||
- 用户要**搜文档 / Wiki / 电子表格 / 多维表格 / 云空间(云盘/云存储)对象**,优先使用 `lark-cli drive +search`。自然语言里"最近我编辑过的"、"我创建的"(→ `--mine`,实为 owner 语义)、"最近一周我打开过的 xxx"、"某人 owner 的 docx" 等直接映射到扁平 flag,避免手写嵌套 JSON。
|
||||
- 用户要把本地 `.xlsx` / `.csv` / `.base` 导入成 Base / 多维表格 / bitable,第一步必须使用 `lark-cli drive +import --type bitable`。
|
||||
- 用户要把本地 `.md` / `.docx` / `.doc` / `.txt` / `.html` 导入成在线文档,使用 `lark-cli drive +import --type docx`。
|
||||
- 用户要把本地 `.pptx` 导入成飞书幻灯片,使用 `lark-cli drive +import --type slides`;当前 PPTX 导入上限是 500MB。
|
||||
@@ -28,19 +27,9 @@ metadata:
|
||||
- 用户要查看、下载、回滚或删除文件的**历史版本**,使用 `drive +version-history`、`drive +version-get`、`drive +version-revert`、`drive +version-delete`;这组命令同时支持 `--as user` 和 `--as bot`,自动化场景优先 `--as bot`。
|
||||
- 用户要把本地 `.xlsx` / `.xls` / `.csv` 导入成电子表格,使用 `lark-cli drive +import --type sheet`。
|
||||
- 用户要在云空间(云盘/云存储)里新建文件夹,优先使用 `lark-cli drive +create-folder`。
|
||||
- 用户要把本地文件上传到知识库 / 文档库里的某个节点下时,仍然使用 `lark-cli drive +upload --wiki-token <wiki_token>`;不要误切到 `wiki` 域命令。
|
||||
- 用户要把本地文件上传到知识库 / 文档库里的某个 wiki 节点下时,仍然使用 `lark-cli drive +upload --wiki-token <wiki_token>`;不要误切到 `wiki` 域命令。
|
||||
- `lark-base` 只负责导入完成后的 Base 内部操作(表、字段、记录、视图),不要在“本地文件 -> Base”这一步提前切到 `lark-base`。
|
||||
|
||||
## 知识资产整理 Workflow
|
||||
|
||||
触发上面的知识资产场景后,按以下顺序加载 reference;不要把所有跨域细节塞进当前上下文:
|
||||
|
||||
1. 先阅读 [`lark-drive-knowledge-overview.md`](references/lark-drive-knowledge-overview.md) 判断目标范围和 recipe。
|
||||
2. 需要标准中间产物时阅读 [`lark-drive-knowledge-artifacts.md`](references/lark-drive-knowledge-artifacts.md)。
|
||||
3. 涉及任何写操作或权限治理时阅读 [`lark-drive-knowledge-safety.md`](references/lark-drive-knowledge-safety.md)。
|
||||
4. 涉及知识库节点树、知识库成员、文档库 / my_library 时,切到 [`lark-wiki`](../lark-wiki/SKILL.md) 读取具体命令规则。
|
||||
5. 涉及文档正文读取或报告创建时,切到 [`lark-doc`](../lark-doc/SKILL.md);涉及 Sheets 台账时,切到 [`lark-sheets`](../lark-sheets/SKILL.md)。
|
||||
|
||||
## 修改标题
|
||||
- 使用 `drive files patch` 命令,通过new_title字段可以修改标题,支持 docx、sheet、bitable、file、wiki、folder 类型
|
||||
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
# 知识资产整理 Artifact 协议
|
||||
|
||||
> 用途:让 inventory、organize、permission-audit 等 workflow 可串联、可恢复、可评测。字段保持最小稳定;缺失信息用空值、`warnings` 或 `unsupported_checks` 表达,不要编造。
|
||||
|
||||
## 目录约定
|
||||
|
||||
```text
|
||||
./lark-drive-knowledge/<run-id>/
|
||||
scope.json
|
||||
inventory.json
|
||||
organize-plan.json
|
||||
permission-audit.json
|
||||
execution-log.json
|
||||
report.md
|
||||
```
|
||||
|
||||
`run-id` 建议使用 `YYYYMMDD-HHMMSS-<short-scope>`,例如 `20260526-143000-wiki-space`.
|
||||
|
||||
## scope.json
|
||||
|
||||
记录用户目标和本次实际处理范围。
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "",
|
||||
"requested_by_user": "",
|
||||
"scope_type": "drive_folder|wiki_space|wiki_node|my_library|mixed",
|
||||
"root": {
|
||||
"url": "",
|
||||
"space_id": "",
|
||||
"folder_token": "",
|
||||
"node_token": ""
|
||||
},
|
||||
"limits": {
|
||||
"max_depth": -1,
|
||||
"page_limit": 0,
|
||||
"content_read": "none|outline|targeted"
|
||||
},
|
||||
"generated_at": ""
|
||||
}
|
||||
```
|
||||
|
||||
## inventory.json
|
||||
|
||||
记录事实清单。所有后续分析优先消费它。
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "",
|
||||
"summary": {
|
||||
"total": 0,
|
||||
"by_source": {},
|
||||
"by_type": {},
|
||||
"warnings_count": 0
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"source": "drive|wiki|my_library",
|
||||
"title": "",
|
||||
"path": "",
|
||||
"url": "",
|
||||
"token": "",
|
||||
"type": "folder|docx|doc|sheet|bitable|file|slides|mindnote|wiki|shortcut",
|
||||
"space_id": "",
|
||||
"node_token": "",
|
||||
"obj_token": "",
|
||||
"obj_type": "",
|
||||
"folder_token": "",
|
||||
"parent_token": "",
|
||||
"depth": 0,
|
||||
"has_child": false,
|
||||
"owner": "",
|
||||
"created_time": "",
|
||||
"modified_time": "",
|
||||
"evidence": []
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"generated_at": ""
|
||||
}
|
||||
```
|
||||
|
||||
## organize-plan.json
|
||||
|
||||
只表达计划,不代表已经执行。
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "",
|
||||
"mode": "plan",
|
||||
"summary": {
|
||||
"actions_count": 0,
|
||||
"requires_confirmation_count": 0
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"id": "act-001",
|
||||
"action": "create_drive_folder|create_wiki_node|move_drive|move_wiki_node|create_drive_shortcut|create_wiki_shortcut",
|
||||
"source": {},
|
||||
"target": {},
|
||||
"reason": "",
|
||||
"evidence": [],
|
||||
"risk": "read|write|high-risk-write",
|
||||
"requires_confirmation": true,
|
||||
"dry_run_command": "",
|
||||
"execute_command": ""
|
||||
}
|
||||
],
|
||||
"blocked": [],
|
||||
"warnings": [],
|
||||
"generated_at": ""
|
||||
}
|
||||
```
|
||||
|
||||
第一版不生成 delete、overwrite、permission patch、owner transfer 动作。
|
||||
|
||||
## permission-audit.json
|
||||
|
||||
记录权限审计事实、推断和能力边界。
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "",
|
||||
"summary": {
|
||||
"audited_items": 0,
|
||||
"risk_findings": 0,
|
||||
"unsupported_checks": []
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"title": "",
|
||||
"path": "",
|
||||
"url": "",
|
||||
"token": "",
|
||||
"type": "",
|
||||
"wiki_space_members": [],
|
||||
"public_permission": {},
|
||||
"risk_findings": [
|
||||
{
|
||||
"type": "",
|
||||
"severity": "low|medium|high",
|
||||
"fact": "",
|
||||
"inference": "",
|
||||
"suggestion": "",
|
||||
"evidence": [],
|
||||
"requires_confirmation": true
|
||||
}
|
||||
],
|
||||
"unsupported_checks": []
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"generated_at": ""
|
||||
}
|
||||
```
|
||||
|
||||
## execution-log.json
|
||||
|
||||
仅在用户明确确认执行 organize plan 后生成。
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "",
|
||||
"plan_file": "",
|
||||
"results": [
|
||||
{
|
||||
"action_id": "act-001",
|
||||
"status": "success|failed|skipped",
|
||||
"command": "",
|
||||
"result": {},
|
||||
"error": ""
|
||||
}
|
||||
],
|
||||
"generated_at": ""
|
||||
}
|
||||
```
|
||||
@@ -1,115 +0,0 @@
|
||||
# 知识资产盘点 Workflow
|
||||
|
||||
> 前置条件:先读 [`lark-drive-knowledge-overview.md`](lark-drive-knowledge-overview.md) 和 [`lark-drive-knowledge-artifacts.md`](lark-drive-knowledge-artifacts.md)。涉及 Wiki 或文档库时再读 [`../../lark-wiki/SKILL.md`](../../lark-wiki/SKILL.md)。
|
||||
|
||||
## 目标
|
||||
|
||||
对云空间文件夹、知识库、知识库子树或文档库做结构化盘点,生成 `inventory.json`,作为整理、权限审计和报告的事实底座。
|
||||
|
||||
## Step 1: 归一化范围
|
||||
|
||||
- 云空间文件夹 URL:提取 `folder_token`,或用 `drive +inspect` 确认类型。
|
||||
- 知识库 URL:用 `wiki +node-get` 或 `drive +inspect` 获取 `space_id`、`node_token`、`obj_type`、`obj_token`。
|
||||
- `my_library` / 文档库:固定走 `wiki +node-list --space-id my_library --as user`。
|
||||
|
||||
记录到 `scope.json`。
|
||||
|
||||
## Step 2: 盘点云空间文件夹
|
||||
|
||||
先查看 schema:
|
||||
|
||||
```bash
|
||||
lark-cli schema drive.files.list --format json
|
||||
```
|
||||
|
||||
读取直接子项:
|
||||
|
||||
```bash
|
||||
lark-cli drive files list \
|
||||
--params '{"folder_token":"<folder_token>","page_size":200}' \
|
||||
--page-all --page-limit 0 --format json --as user
|
||||
```
|
||||
|
||||
对返回的 `type=folder` 子项递归调用同一命令。记录字段:
|
||||
|
||||
- `name` -> `title`
|
||||
- `token`
|
||||
- `type`
|
||||
- `url`
|
||||
- `parent_token`
|
||||
- `owner_id`
|
||||
- `created_time`
|
||||
- `modified_time`
|
||||
|
||||
如需按关键词或时间补充范围,可用 `drive +search`,但目录树以 `drive files list` 为准。
|
||||
|
||||
## Step 3: 盘点知识库或文档库
|
||||
|
||||
读取根层:
|
||||
|
||||
```bash
|
||||
lark-cli wiki +node-list \
|
||||
--space-id "<space_id_or_my_library>" \
|
||||
--page-all --page-limit 0 --format json --as user
|
||||
```
|
||||
|
||||
对 `has_child=true` 的节点递归:
|
||||
|
||||
```bash
|
||||
lark-cli wiki +node-list \
|
||||
--space-id "<space_id_or_my_library>" \
|
||||
--parent-node-token "<node_token>" \
|
||||
--page-all --page-limit 0 --format json --as user
|
||||
```
|
||||
|
||||
必要时补节点详情:
|
||||
|
||||
```bash
|
||||
lark-cli wiki +node-get --node-token "<node_token>" --format json --as user
|
||||
```
|
||||
|
||||
记录字段:
|
||||
|
||||
- `title`
|
||||
- `node_token`
|
||||
- `obj_token`
|
||||
- `obj_type`
|
||||
- `node_type`
|
||||
- `parent_node_token`
|
||||
- `has_child`
|
||||
- `space_id`
|
||||
- `owner`(如果详情返回)
|
||||
|
||||
## Step 4: 可选读取内容结构
|
||||
|
||||
只有用户需要“按内容归类”“识别主题”“生成导航页”时才读取正文结构。优先读 outline,不全量读正文:
|
||||
|
||||
```bash
|
||||
lark-cli docs +fetch \
|
||||
--api-version v2 \
|
||||
--doc "<url_or_token>" \
|
||||
--scope outline \
|
||||
--max-depth 3 \
|
||||
--doc-format markdown \
|
||||
--format json --as user
|
||||
```
|
||||
|
||||
失败时保留结构盘点结果,并在 `warnings` 中标记 `content_outline_failed`。
|
||||
|
||||
## Step 5: 输出 inventory.json
|
||||
|
||||
把所有条目写入 `./lark-drive-knowledge/<run-id>/inventory.json`。聊天回复只输出摘要:
|
||||
|
||||
- 总数
|
||||
- 按 source/type 统计
|
||||
- 空标题数量
|
||||
- 重复标题候选数量
|
||||
- 未读取或失败的节点数量
|
||||
- artifact 路径
|
||||
|
||||
## 停止条件
|
||||
|
||||
- 没有权限读取根节点或根文件夹:停止并给出授权建议。
|
||||
- 分页失败或 cursor 不前进:保留已读结果,写入 `warnings`。
|
||||
- 结果规模过大:停止深挖正文,只输出结构清单并提示用户缩小范围。
|
||||
- 知识库和云空间混合范围不清楚:先让用户确认是否都处理。
|
||||
@@ -1,107 +0,0 @@
|
||||
# 散乱知识整理 Workflow
|
||||
|
||||
> 前置条件:先有 `inventory.json`,并阅读 [`lark-drive-knowledge-safety.md`](lark-drive-knowledge-safety.md)。第一版默认只生成整理计划,不直接执行。
|
||||
|
||||
## 目标
|
||||
|
||||
帮助用户把云空间文件夹、知识库或文档库下的散乱文档组织成更清晰的目录结构。输出 `organize-plan.json`。
|
||||
|
||||
## 输入
|
||||
|
||||
- 必需:`inventory.json`
|
||||
- 可选:用户给出的目标分类规则,例如“按项目”“按业务线”“按系统”“按年份”“按文档类型”
|
||||
- 可选:outline 读取结果,用于按内容主题分类
|
||||
|
||||
## 分析规则
|
||||
|
||||
优先基于事实字段:
|
||||
|
||||
- 路径层级过深或过平。
|
||||
- 空标题、重复标题、相似标题。
|
||||
- 同一主题散落在多个目录。
|
||||
- 云空间文件夹中在线文档和普通文件混放。
|
||||
- 知识库 shortcut 复用或源文档散落。
|
||||
- 文档标题包含“旧版”“废弃”“草稿”“临时”等治理信号。
|
||||
|
||||
模型推断必须写入 `reason` 和 `evidence`,不能把推断当事实。
|
||||
|
||||
## 生成计划
|
||||
|
||||
允许生成的 action:
|
||||
|
||||
| action | 说明 |
|
||||
|-|-|
|
||||
| `create_drive_folder` | 在 Drive 中创建目标文件夹 |
|
||||
| `create_wiki_node` | 在知识库或文档库 / `my_library` 中创建目录节点 |
|
||||
| `move_drive` | 移动 Drive 文件/文件夹 |
|
||||
| `move_wiki_node` | 移动知识库节点 |
|
||||
| `create_drive_shortcut` | 在 Drive 目标文件夹创建快捷方式 |
|
||||
| `create_wiki_shortcut` | 在知识库中创建 shortcut 节点 |
|
||||
|
||||
禁止生成的 action:
|
||||
|
||||
- delete
|
||||
- overwrite
|
||||
- permission patch
|
||||
- member remove
|
||||
- owner transfer
|
||||
|
||||
每个 action 必须包含:
|
||||
|
||||
- source
|
||||
- target
|
||||
- reason
|
||||
- evidence
|
||||
- `requires_confirmation=true`
|
||||
- dry-run command
|
||||
- execute command
|
||||
|
||||
## 命令模板
|
||||
|
||||
Drive 创建文件夹:
|
||||
|
||||
```bash
|
||||
lark-cli drive +create-folder --name "<folder_name>" --folder-token "<parent_folder_token>" --dry-run --as user
|
||||
```
|
||||
|
||||
Drive 移动:
|
||||
|
||||
```bash
|
||||
lark-cli drive +move --file-token "<token>" --type "<type>" --folder-token "<target_folder_token>" --dry-run --as user
|
||||
```
|
||||
|
||||
Drive 快捷方式:
|
||||
|
||||
```bash
|
||||
lark-cli drive +create-shortcut --file-token "<token>" --type "<type>" --folder-token "<target_folder_token>" --dry-run --as user
|
||||
```
|
||||
|
||||
Wiki 创建节点:
|
||||
|
||||
```bash
|
||||
lark-cli wiki +node-create --space-id "<space_id_or_my_library>" --parent-node-token "<parent_node_token>" --title "<title>" --obj-type docx --dry-run --as user
|
||||
```
|
||||
|
||||
Wiki 移动节点:
|
||||
|
||||
```bash
|
||||
lark-cli wiki +move --node-token "<node_token>" --target-parent-token "<target_parent_node_token>" --dry-run --as user
|
||||
```
|
||||
|
||||
Wiki shortcut:
|
||||
|
||||
```bash
|
||||
lark-cli wiki +node-create --space-id "<space_id>" --parent-node-token "<parent_node_token>" --node-type shortcut --origin-node-token "<source_node_token>" --title "<title>" --dry-run --as user
|
||||
```
|
||||
|
||||
## 执行与验收
|
||||
|
||||
只有用户明确确认某个 `organize-plan.json` 后,才逐条执行。执行后写 `execution-log.json`,并重新跑 inventory 验证目标目录结构。
|
||||
|
||||
聊天回复要给出:
|
||||
|
||||
- 计划文件路径
|
||||
- action 数量
|
||||
- 需要确认的高影响动作
|
||||
- 被阻塞的动作和原因
|
||||
- 下一步确认方式
|
||||
@@ -1,59 +0,0 @@
|
||||
# 知识资产整理 Workflow 总览
|
||||
|
||||
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 盘点某个云空间文件夹、知识库、知识库节点或文档库下的文档和目录。
|
||||
- 整理散乱文档:归类、生成目录结构、移动计划、快捷方式计划、导航页建议。
|
||||
- 治理知识资产:重复、空标题、孤立、未归档、命名混乱、内容缺失或过期。
|
||||
- 审计权限风险:Wiki 空间成员、文档公开链接、组织外访问、分享/复制/下载设置。
|
||||
|
||||
## 目标范围模型
|
||||
|
||||
| 用户目标 | 视作 | 主入口 |
|
||||
|-|-|-|
|
||||
| 云空间文件夹 | Drive folder tree | `drive files list` / `drive +search` |
|
||||
| 知识库 / 知识库节点 | Wiki space/node tree | `wiki +node-list` / `wiki +node-get` |
|
||||
| 文档库 / my_library | Wiki personal library | `wiki +node-list --space-id my_library --as user` |
|
||||
| 文档正文或标题结构 | Docs content | `docs +fetch --api-version v2` |
|
||||
| 报告或台账 | Docs / Sheets | `docs +create` / `sheets +create` |
|
||||
|
||||
不要把“文档库”当成 Drive 根目录;它应走 Wiki personal library。
|
||||
|
||||
## Recipe 路由
|
||||
|
||||
| 用户意图 | 读取文件 | 产物 |
|
||||
|-|-|-|
|
||||
| 盘点、梳理、导出清单、看目录结构 | [`lark-drive-knowledge-inventory.md`](lark-drive-knowledge-inventory.md) | `inventory.json` |
|
||||
| 整理、归类、组织目录、生成移动计划 | [`lark-drive-knowledge-organize.md`](lark-drive-knowledge-organize.md) | `organize-plan.json` |
|
||||
| 权限风险、公开链接、组织外访问、成员过多 | [`lark-drive-knowledge-permission-audit.md`](lark-drive-knowledge-permission-audit.md) | `permission-audit.json` |
|
||||
|
||||
复合意图按链路执行:
|
||||
|
||||
```text
|
||||
盘点并整理 -> inventory -> organize
|
||||
盘点并审计权限 -> inventory -> permission-audit
|
||||
整理并输出报告 -> inventory -> organize -> report artifact
|
||||
```
|
||||
|
||||
## 执行原则
|
||||
|
||||
- 大范围结果默认写入本地 artifact,不把完整清单塞进聊天。
|
||||
- 每次 run 使用独立目录:`./lark-drive-knowledge/<run-id>/`。
|
||||
- 工作流之间通过 artifact 串联,优先复用已有 `inventory.json`,不要无故重复爬取。
|
||||
- 所有判断必须区分事实、推断、建议;没有证据的内容写入 `warnings` 或 `unsupported_checks`。
|
||||
- 原生 API 调用前必须先运行 `lark-cli schema <service>.<resource>.<method>` 校验参数结构。
|
||||
- 写操作和权限治理必须遵守 [`lark-drive-knowledge-safety.md`](lark-drive-knowledge-safety.md)。
|
||||
|
||||
## 当前能力边界
|
||||
|
||||
| 能力 | 状态 | 说明 |
|
||||
|-|-|-|
|
||||
| 知识库节点盘点 | 支持 | 递归 `wiki +node-list` |
|
||||
| 云空间文件夹盘点 | 支持 | 递归 `drive files list` |
|
||||
| 文档标题结构读取 | 支持 | `docs +fetch --scope outline` |
|
||||
| Wiki 空间成员审计 | 支持 | `wiki +member-list` |
|
||||
| 文档公开权限审计 | 支持 | `drive permission.public get` |
|
||||
| 单文档协作者全量枚举 | 暂不支持 | 当前 Drive permission members 只有 `auth/create/transfer_owner` |
|
||||
| 自动删除、覆盖、降权、改权限 | 第一版不执行 | 只输出计划和建议 |
|
||||
@@ -1,109 +0,0 @@
|
||||
# 权限风险审计 Workflow
|
||||
|
||||
> 前置条件:先读 [`lark-drive-knowledge-overview.md`](lark-drive-knowledge-overview.md)、[`lark-drive-knowledge-artifacts.md`](lark-drive-knowledge-artifacts.md) 和 [`lark-drive-knowledge-safety.md`](lark-drive-knowledge-safety.md)。第一版只审计和建议,不自动改权限。
|
||||
|
||||
## 目标
|
||||
|
||||
审计 云空间 / 知识库 / 文档库范围内的权限风险,重点覆盖:
|
||||
|
||||
- Wiki 空间成员和角色。
|
||||
- 文档公开权限、外部访问、链接分享、复制/下载/打印限制。
|
||||
- 当前能力无法验证的权限项。
|
||||
|
||||
## 当前能力边界
|
||||
|
||||
| 检查项 | 状态 | 命令 |
|
||||
|-|-|-|
|
||||
| Wiki 空间成员 | 支持 | `wiki +member-list` |
|
||||
| 文档公开权限 | 支持 | `drive permission.public get`,仅限 schema 支持的文档类型 |
|
||||
| Drive 文件夹公开权限 | 暂不支持 | `drive.permission.public.get` 不支持 `type=folder` |
|
||||
| 当前用户/应用是否具备某权限 | 支持 | `drive permission.members auth` |
|
||||
| 单文档显式协作者全量枚举 | 暂不支持 | 当前无 `drive.permission.members.list` |
|
||||
| 自动关闭公开权限或降权 | 第一版不执行 | 只输出建议 |
|
||||
|
||||
## Step 1: 输入范围
|
||||
|
||||
优先消费 `inventory.json`。如果用户只给一个 URL,先按 [`lark-drive-knowledge-inventory.md`](lark-drive-knowledge-inventory.md) 做最小盘点。
|
||||
|
||||
## Step 2: Wiki 空间成员审计
|
||||
|
||||
涉及知识库空间或文档库 / `my_library` 时:
|
||||
|
||||
```bash
|
||||
lark-cli wiki +member-list \
|
||||
--space-id "<space_id_or_my_library>" \
|
||||
--page-all --page-limit 0 --format json --as user
|
||||
```
|
||||
|
||||
审计信号:
|
||||
|
||||
- admin 数量异常多。
|
||||
- 成员包含部门、群或开放范围较大的 member_type。
|
||||
- 文档库成员结果缺失或不适用时写入 `warnings`,不要推断。
|
||||
|
||||
## Step 3: 文档公开权限审计
|
||||
|
||||
先查看 schema:
|
||||
|
||||
```bash
|
||||
lark-cli schema drive.permission.public.get --format json
|
||||
```
|
||||
|
||||
只对 inventory 中 `type` 属于以下集合的条目查询:
|
||||
|
||||
```text
|
||||
doc, docx, sheet, bitable, file, wiki, mindnote, minutes, slides
|
||||
```
|
||||
|
||||
`type=folder` 的 Drive 文件夹不要调用 `drive.permission.public get`。将该 item 写入 `unsupported_checks` 或 `warnings`,例如 `drive_folder_public_permission_unsupported`,并继续审计其他条目。
|
||||
|
||||
```bash
|
||||
lark-cli drive permission.public get \
|
||||
--params '{"token":"<token>","type":"<type>"}' \
|
||||
--format json --as user
|
||||
```
|
||||
|
||||
Token/type 选择:
|
||||
|
||||
- 云空间文件:用 Drive `token` 和 `type=file`。
|
||||
- 云空间文件夹:不执行 `permission.public.get`,记录为未覆盖检查。
|
||||
- 知识库节点权限:优先用 `node_token` 和 `type=wiki`。
|
||||
- 底层文档权限:用 `obj_token` 和 `obj_type`。
|
||||
|
||||
如果某类 token 查询失败,不要中断全局审计;写入该 item 的 `warnings`。如果是 `type=folder`,不要把它当作 API 失败,而是写入 `unsupported_checks`。
|
||||
|
||||
## 风险规则
|
||||
|
||||
| 字段 | 高风险 | 中风险 |
|
||||
|-|-|-|
|
||||
| `link_share_entity` | `anyone_editable`, `anyone_readable` | `tenant_editable` |
|
||||
| `external_access` | `true` 且链接或分享范围较宽 | `true` |
|
||||
| `share_entity` | `anyone` | `same_tenant` |
|
||||
| `security_entity` | `anyone_can_view` | `anyone_can_edit` |
|
||||
| Wiki member role | admin 过多 | 部门/群成员需确认 |
|
||||
|
||||
每条风险都必须写清楚:
|
||||
|
||||
- `fact`:API 返回字段。
|
||||
- `inference`:为什么可能有风险。
|
||||
- `suggestion`:建议 owner 或管理员确认的动作。
|
||||
- `evidence`:token、URL、path、字段名和值。
|
||||
- `requires_confirmation=true`。
|
||||
|
||||
## 输出
|
||||
|
||||
写入 `permission-audit.json`,聊天回复只给摘要:
|
||||
|
||||
- 审计对象数量。
|
||||
- 高/中/低风险数量。
|
||||
- 公开权限风险 Top N。
|
||||
- Wiki 成员风险摘要。
|
||||
- 未覆盖检查:必须包含 `explicit_collaborator_list`,说明当前 CLI 无法枚举单文档显式协作者列表。
|
||||
- 如果 inventory 中包含 `type=folder`,未覆盖检查必须包含 `drive_folder_public_permission`,说明 `drive.permission.public.get` 不支持 Drive 文件夹。
|
||||
|
||||
## 禁止事项
|
||||
|
||||
- 不自动调用 `permission.public.patch`。
|
||||
- 不自动调用 `permission.members.create`、`transfer_owner` 或任何成员删除/降权接口。
|
||||
- 不把“无法枚举协作者”说成“没有协作者风险”。
|
||||
- 不把模型推断写成事实。
|
||||
@@ -1,50 +0,0 @@
|
||||
# 知识资产整理安全策略
|
||||
|
||||
> 默认安全级别:只读或计划优先。任何会改变用户文档、目录或权限的操作,都必须先生成计划并让用户明确确认。
|
||||
|
||||
## 动作分级
|
||||
|
||||
| 分级 | 示例 | 策略 |
|
||||
|-|-|-|
|
||||
| Read-only | 列目录、查元数据、读 outline、读公开权限 | 可直接执行 |
|
||||
| Plan-only | 生成整理计划、权限治理建议、报告草稿 | 可直接执行 |
|
||||
| Confirmed write | 创建文件夹、创建知识库节点、移动文档、创建快捷方式、新建报告 | 必须先展示计划,用户确认后执行 |
|
||||
| High-risk write | 删除、覆盖、改权限、转移 owner、移除成员、公开权限 patch | 第一版 workflow 不执行 |
|
||||
| Unsupported | 单文档协作者全量枚举 | 明确说明当前能力无法验证 |
|
||||
|
||||
## 强制规则
|
||||
|
||||
- 盘点、整理建议、权限审计默认不修改任何资源。
|
||||
- `organize-plan.json` 不是执行授权;只有用户明确说执行某个 plan,才进入执行阶段。
|
||||
- 执行前必须展示 action 列表,包括 source、target、reason、risk、dry-run command。
|
||||
- 执行写操作前先跑对应 `--dry-run`;dry-run 通过不等于用户已确认真实执行。
|
||||
- 不自动执行 delete、overwrite、permission patch、member remove、owner transfer。
|
||||
- 权限治理第一版只输出风险和建议,不自动收敛权限。
|
||||
- 对无法确认的风险,写成 `unsupported_checks` 或 `requires_confirmation=true`,不要当作事实。
|
||||
|
||||
## 需要停下来问用户的情况
|
||||
|
||||
- 目标范围不明确,例如同时给出多个 folder/wiki URL 但未说明是否都处理。
|
||||
- 计划包含跨空间移动、跨 Drive/Wiki 移动或大量移动。
|
||||
- 目标目录已有同名节点/文件夹,无法判断是否复用。
|
||||
- 用户要求“直接整理好”,但计划还没有展示和确认。
|
||||
- 用户要求“治理权限”,但动作涉及改公开权限、移除成员、降权或转移 owner。
|
||||
- 节点数量或文件数量超出上下文可处理范围,需要改为文件产物或缩小范围。
|
||||
|
||||
## 权限风险表述
|
||||
|
||||
表述必须区分:
|
||||
|
||||
- 事实:API 返回的字段,例如 `external_access=true`。
|
||||
- 推断:基于规则得到的风险,例如“可能允许组织外传播”。
|
||||
- 建议:下一步动作,例如“建议 owner 人工确认是否关闭外部访问”。
|
||||
- 未覆盖:当前 API/CLI 不能验证的项,例如“无法枚举单文档显式协作者列表”。
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
事实:link_share_entity=anyone_editable。
|
||||
推断:互联网获得链接的人可能可编辑,属于高风险公开权限。
|
||||
建议:请 owner 确认是否需要关闭链接分享或收敛为 tenant_readable。
|
||||
未覆盖:未检查显式协作者列表,因为当前 CLI 没有 permission.members.list。
|
||||
```
|
||||
@@ -99,6 +99,9 @@ Minutes (妙记) ← minute_token 标识
|
||||
> - 用户说"通过文件生成妙记 / 把音视频转妙记" → 先上传获取 `file_token`,然后使用 `minutes +upload`
|
||||
> - 用户说"把音视频文件转成纪要 / 逐字稿 / 文字稿 / 撰写文字 / 总结 / 待办 / 章节" → 先上传获取 `file_token`,调用 `minutes +upload` 生成 `minute_url`,再提取 `minute_token` 走 `vc +notes --minute-tokens`
|
||||
|
||||
> - 用户说"替换 / 更新这个妙记的总结" → [`minutes +summary`](references/lark-minutes-summary.md)
|
||||
> - 用户说"新增 / 更新 / 删除这个妙记的某条待办" → [`minutes +todo`](references/lark-minutes-todo.md)
|
||||
|
||||
## Shortcuts(推荐优先使用)
|
||||
|
||||
Shortcut 是对常用操作的高级封装(`lark-cli minutes +<verb> [flags]`)。有 Shortcut 的操作优先使用。
|
||||
@@ -108,10 +111,14 @@ Shortcut 是对常用操作的高级封装(`lark-cli minutes +<verb> [flags]`
|
||||
| [`+search`](references/lark-minutes-search.md) | Search minutes by keyword, owners, participants, and time range |
|
||||
| [`+download`](references/lark-minutes-download.md) | Download audio/video media file of a minute |
|
||||
| [`+upload`](references/lark-minutes-upload.md) | Upload a media file token to generate a minute |
|
||||
| [`+summary`](references/lark-minutes-summary.md) | Replace the AI summary of a minute |
|
||||
| [`+todo`](references/lark-minutes-todo.md) | Add, update, or delete a single todo item |
|
||||
|
||||
- 使用 `+search` 命令时,必须阅读 [references/lark-minutes-search.md](references/lark-minutes-search.md),了解搜索参数和返回值结构。
|
||||
- 使用 `+download` 命令时,必须阅读 [references/lark-minutes-download.md](references/lark-minutes-download.md),了解下载参数和返回值结构。
|
||||
- 使用 `+upload` 命令时,必须阅读 [references/lark-minutes-upload.md](references/lark-minutes-upload.md),了解生成参数和返回值结构。
|
||||
- 使用 `+summary` 时,必须阅读 [references/lark-minutes-summary.md](references/lark-minutes-summary.md);妙记端通常可良好展示:一级/二级/三级标题(`#` / `##` / `###`)、加粗(`**text**`)、无序列表(`-` / `*`)、有序列表(`1.`),以及纯文本与换行;不支持的 Markdown 语法会按原始文本展示,接口不会因此拒绝,Agent 写入时应优先使用可展示子集。
|
||||
- 使用 `+todo` 时,必须阅读 [references/lark-minutes-todo.md](references/lark-minutes-todo.md);对单条待办做增删改:新增传 `--todo`(纯文本)+`--is-done`,更新再加 `--todo-id`,删除只传 `--todo-id`。待办 id 通过 `vc +notes` 读取,不向用户展示。
|
||||
|
||||
<!-- AUTO-GENERATED-START — gen-skills.py 管理,勿手动编辑 -->
|
||||
|
||||
|
||||
122
skills/lark-minutes/references/lark-minutes-summary.md
Normal file
122
skills/lark-minutes/references/lark-minutes-summary.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# minutes +summary
|
||||
|
||||
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
|
||||
替换妙记的 AI 总结内容。写操作,会覆盖当前总结。
|
||||
|
||||
本 skill 对应 shortcut:`lark-cli minutes +summary`(调用 `PUT /open-apis/minutes/v1/minutes/{minute_token}/summary`)。
|
||||
|
||||
## 典型触发表达
|
||||
|
||||
- "把这条妙记的总结改成……"
|
||||
- "更新 / 替换妙记的 AI 总结"
|
||||
- "修正总结内容后写回妙记"
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 直接传入总结内容(Markdown 子集)
|
||||
lark-cli minutes +summary --minute-token obcnxxxxxxxxxxxxxxxxxxxx --summary "**会议结论**\n- 方案 A 通过\n- 下周跟进排期"
|
||||
|
||||
# 从文件读取总结内容
|
||||
lark-cli minutes +summary --minute-token obcnxxxxxxxxxxxxxxxxxxxx --summary @summary.md
|
||||
|
||||
# 从 stdin 读取
|
||||
echo "**结论**" | lark-cli minutes +summary --minute-token obcnxxxxxxxxxxxxxxxxxxxx --summary @-
|
||||
|
||||
# 预览 API 调用
|
||||
lark-cli minutes +summary --minute-token obcnxxxxxxxxxxxxxxxxxxxx --summary @summary.md --dry-run
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--minute-token <token>` | 是 | 妙记 Token |
|
||||
| `--summary <text>` | 是 | 替换后的总结内容,支持 `@file` / `@-`(stdin) |
|
||||
| `--dry-run` | 否 | 预览 API 调用,不执行 |
|
||||
|
||||
## 核心约束
|
||||
|
||||
### 1. 先读后写
|
||||
|
||||
替换前建议先用 `lark-cli vc +notes --minute-tokens <token>` 读取当前总结,确认 `minute_token` 与待替换内容无误。
|
||||
|
||||
### 2. Markdown 展示说明
|
||||
|
||||
接口接受任意总结文本,**不会因 Markdown 格式校验失败而拒绝请求**。妙记客户端通常只能良好渲染以下 Markdown 子集;不支持的语法(如链接、代码块、四级标题等)会**按原始文本展示**(保留 Markdown 标记字符,不会渲染成对应样式)。Agent 写入时应优先使用可展示语法,避免用户在妙记里看到字面量的 `[链接](url)`、`` `code` `` 等:
|
||||
|
||||
| 支持 | 写法 | 示例 |
|
||||
|------|------|------|
|
||||
| 纯文本 | 普通段落 | `本次会议讨论了 Q2 预算` |
|
||||
| 换行 | `\n` 或空行 | 分段落书写 |
|
||||
| 一级标题 | `# ` + 标题文字 | `# 会议结论` |
|
||||
| 二级标题 | `## ` + 标题文字 | `## 行动项` |
|
||||
| 三级标题 | `### ` + 标题文字 | `### 跟进事项` |
|
||||
| 加粗 | `**文字**` | `**重点结论**` |
|
||||
| 无序列表 | `- ` 或 `* ` | `- 跟进预算审批` |
|
||||
| 有序列表 | `1. ` | `1. 确认需求` |
|
||||
|
||||
> 标题语法建议:`#` 后保留空格,并优先使用 1~3 级(`#` / `##` / `###`)。四级及以上(`####`)无法渲染,会以原始文本形式展示。
|
||||
|
||||
**不建议使用**(会按原始文本展示):链接、图片、代码块、表格、引用块、斜体、删除线、四级及以上标题等。
|
||||
|
||||
合法示例:
|
||||
|
||||
```markdown
|
||||
# 会议结论
|
||||
|
||||
## 核心讨论
|
||||
|
||||
**方案 A 通过**,下周启动排期。
|
||||
|
||||
### 待跟进
|
||||
- 预算审批
|
||||
- 排期确认
|
||||
|
||||
1. 张三负责预算
|
||||
2. 李四负责排期
|
||||
```
|
||||
|
||||
### 3. 所需权限
|
||||
|
||||
| 身份 | 所需权限 |
|
||||
|------|---------|
|
||||
| user | `minutes:minutes:update` |
|
||||
|
||||
## 输出结果
|
||||
|
||||
```json
|
||||
{
|
||||
"minute_token": "obcnxxxxxxxxxxxxxxxxxxxx",
|
||||
"updated": true
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `minute_token` | 妙记 Token |
|
||||
| `updated` | 是否已成功更新 |
|
||||
|
||||
## 如何获取 minute_token
|
||||
|
||||
| 来源 | 获取方式 |
|
||||
|------|---------|
|
||||
| 妙记 URL | 从 URL 末尾提取,如 `https://sample.feishu.cn/minutes/obcnxxxxxxxxxxxxxxxxxxxx` |
|
||||
| 妙记搜索 | `lark-cli minutes +search --query "关键词"` |
|
||||
| 会议产物查询 | `lark-cli vc +notes --minute-tokens <token>` |
|
||||
|
||||
## 常见错误与排查
|
||||
|
||||
| 错误现象 | 错误码 | 根本原因 | 解决方案 |
|
||||
|---------|--------|---------|---------|
|
||||
| 总结展示为原始 Markdown 文本 | — | 总结含链接、四级标题等妙记端无法渲染的语法 | 改用标题(#~###)、加粗、列表等可展示格式;接口不会因此报错 |
|
||||
| 参数无效 | — | `minute_token` 缺失或格式错误 | 检查 token 是否完整 |
|
||||
| 权限不足 | — | 缺少 `minutes:minutes:update` | 运行 `auth login --scope "minutes:minutes:update"` |
|
||||
|
||||
## 参考
|
||||
|
||||
- [lark-minutes](../SKILL.md) — 妙记全部命令
|
||||
- [minutes +todo](lark-minutes-todo.md) — 替换待办项
|
||||
- [lark-vc-notes](../../lark-vc/references/lark-vc-notes.md) — 读取总结、待办等 AI 产物
|
||||
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数
|
||||
122
skills/lark-minutes/references/lark-minutes-todo.md
Normal file
122
skills/lark-minutes/references/lark-minutes-todo.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# minutes +todo
|
||||
|
||||
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
|
||||
对妙记中的**单条**待办做新增 / 更新 / 删除。写操作。
|
||||
|
||||
本 skill 对应 shortcut:`lark-cli minutes +todo`(调用 `PUT /open-apis/minutes/v1/minutes/{minute_token}/todo`)。
|
||||
|
||||
## 典型触发表达
|
||||
|
||||
- "给这条妙记加一条待办"
|
||||
- "把某条待办改成……"
|
||||
- "标记某条待办为已完成 / 取消完成"
|
||||
- "删除某条待办"
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 新增一条待办(不带 id,content 与 is_done 成对)
|
||||
lark-cli minutes +todo --minute-token obcnxxxxxxxxxxxxxxxxxxxx --todo "跟进预算审批" --is-done=false
|
||||
|
||||
# 更新已有待办(带 id,覆盖内容与完成状态)
|
||||
lark-cli minutes +todo --minute-token obcnxxxxxxxxxxxxxxxxxxxx --todo-id 1234567890 --todo "整理会议纪要" --is-done
|
||||
|
||||
# 删除已有待办(只带 id,不带 --todo)
|
||||
lark-cli minutes +todo --minute-token obcnxxxxxxxxxxxxxxxxxxxx --todo-id 1234567890
|
||||
|
||||
# 预览 API 调用
|
||||
lark-cli minutes +todo --minute-token obcnxxxxxxxxxxxxxxxxxxxx --todo "新待办" --is-done --dry-run
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--minute-token <token>` | 是 | 妙记 Token |
|
||||
| `--todo <text>` | 视操作 | 待办纯文本;新增 / 更新时必填,且必须与 `--is-done` 成对出现;删除时不传 |
|
||||
| `--is-done` | 视操作 | 完成状态布尔值;传 `--is-done` 表示 `true`,传 `--is-done=false` 表示 `false`;仅在带 `--todo` 时使用 |
|
||||
| `--todo-id <id>` | 视操作 | 已有待办的 id;更新 / 删除时必填,新增时不传 |
|
||||
| `--dry-run` | 否 | 预览 API 调用,不执行 |
|
||||
|
||||
## 三种操作的判定
|
||||
|
||||
| `--todo-id` | `--todo` | 行为 |
|
||||
|-------------|----------|------|
|
||||
| 不传 | 有内容 | **新增**一条待办(需 `--is-done`) |
|
||||
| 传 | 有内容 | **更新** id 对应的待办(需 `--is-done`) |
|
||||
| 传 | 不传 | **删除** id 对应的待办 |
|
||||
|
||||
## 核心约束
|
||||
|
||||
### 1. 先读后写,待办 id 如何获取
|
||||
|
||||
更新 / 删除前先用 `lark-cli vc +notes --minute-tokens <token>` 读取当前待办。返回的每条待办带 `todo_id` 字段,用作 `--todo-id` 的取值。
|
||||
|
||||
> 待办 id 仅用于程序内部定位某条待办,不必展示给用户;本命令的输出也不会回显 id。
|
||||
|
||||
读取与写入均使用 `is_done` 布尔字段。已删除的待办不会出现在读取结果中。
|
||||
|
||||
### 2. 待办内容为纯文本
|
||||
|
||||
`content` **不是 Markdown**,请直接传入待办描述文字。
|
||||
|
||||
- 不要写 `# 标题`、`**加粗**`、`- 列表` 等 Markdown 语法
|
||||
- 如需多行内容,可直接使用换行;但不会被渲染为 Markdown 格式
|
||||
|
||||
### 3. 请求体字段
|
||||
|
||||
请求体 `todo_items` 始终只包含**一条**待办:
|
||||
|
||||
| CLI | JSON 字段 | 说明 |
|
||||
|-----|-----------|------|
|
||||
| `--todo` | `content` | 纯文本待办描述(新增 / 更新必填;删除不传) |
|
||||
| `--is-done` | `is_done` | 是否已完成(新增 / 更新必填;删除不传) |
|
||||
| `--todo-id` | `todo_id` | 已有待办 id(更新 / 删除必填;新增不传) |
|
||||
|
||||
### 4. 所需权限
|
||||
|
||||
| 身份 | 所需权限 |
|
||||
|------|---------|
|
||||
| user | `minutes:minutes:update` |
|
||||
|
||||
## 输出结果
|
||||
|
||||
```json
|
||||
{
|
||||
"minute_token": "obcnxxxxxxxxxxxxxxxxxxxx",
|
||||
"operation": "add",
|
||||
"updated": true
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `minute_token` | 妙记 Token |
|
||||
| `operation` | 本次操作类型:`add` / `update` / `delete` |
|
||||
| `updated` | 是否已成功提交 |
|
||||
|
||||
## 如何获取 minute_token
|
||||
|
||||
| 来源 | 获取方式 |
|
||||
|------|---------|
|
||||
| 妙记 URL | 从 URL 末尾提取,如 `https://sample.feishu.cn/minutes/obcnxxxxxxxxxxxxxxxxxxxx` |
|
||||
| 妙记搜索 | `lark-cli minutes +search --query "关键词"` |
|
||||
| 会议产物查询 | `lark-cli vc +notes --minute-tokens <token>` |
|
||||
|
||||
## 常见错误与排查
|
||||
|
||||
| 错误现象 | 根本原因 | 解决方案 |
|
||||
|---------|---------|---------|
|
||||
| 参数无效 | `minute_token` 缺失 | 检查 token |
|
||||
| 未指定操作 | 既没传 `--todo` 也没传 `--todo-id` | 新增 / 更新需 `--todo`,删除需 `--todo-id` |
|
||||
| 缺少 `is_done` | 传了 `--todo` 未传 `--is-done` | `--todo` 与 `--is-done` 必须成对出现 |
|
||||
| 删除时多传了 `--is-done` | 删除只需 `--todo-id` | 删除时不要传 `--todo` / `--is-done` |
|
||||
| 权限不足 | 缺少 `minutes:minutes:update` | 运行 `auth login --scope "minutes:minutes:update"` |
|
||||
|
||||
## 参考
|
||||
|
||||
- [lark-minutes](../SKILL.md) — 妙记全部命令
|
||||
- [minutes +summary](lark-minutes-summary.md) — 替换 AI 总结(不支持的 Markdown 会按原始文本展示,详见该文档)
|
||||
- [lark-vc-notes](../../lark-vc/references/lark-vc-notes.md) — 读取总结、待办等 AI 产物
|
||||
- [lark-shared](../../lark-shared/SKILL.md) — 认证和全局参数
|
||||
@@ -91,7 +91,7 @@ lark-cli vc +notes --meeting-ids 69xxxxxxxxxxxxx28 --dry-run
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `artifacts.summary` | AI 总结(JSON 内联) |
|
||||
| `artifacts.todos` | 待办事项(JSON 内联) |
|
||||
| `artifacts.todos` | 待办事项(JSON 内联);每条含 `content`、`is_done` 及 `todo_id`。`todo_id` 仅供 `minutes +todo` 更新/删除单条待办时使用,不必展示给用户 |
|
||||
| `artifacts.chapters` | 章节纪要(JSON 内联) |
|
||||
| `artifacts.transcript_file` | 逐字稿本地文件路径。默认落到 `./minutes/{minute_token}/transcript.txt`(与 `minutes +download` 聚合);显式 `--output-dir` 时走旧布局 `./{output-dir}/artifact-{title}-{token}/transcript.txt` |
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: lark-wiki
|
||||
version: 1.0.0
|
||||
description: "飞书知识库:管理知识空间、空间成员和文档节点。创建和查询知识空间、查看和管理空间成员、管理节点层级结构、在知识库中组织文档和快捷方式。当用户需要在知识库中查找或创建文档、浏览知识空间结构、查看或管理空间成员、移动或复制节点时使用。知识库 / 文档库的文档和目录盘点、整理、治理 workflow 统一从 lark-drive 进入;本 skill 只处理知识空间、成员和节点操作。"
|
||||
description: "飞书知识库:管理知识空间、空间成员和文档节点。创建和查询知识空间、查看和管理空间成员、管理节点层级结构、在知识库中组织文档和快捷方式。当用户需要在知识库中查找或创建文档、浏览知识空间结构、查看或管理空间成员、移动或复制节点时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["lark-cli"]
|
||||
@@ -24,7 +24,6 @@ metadata:
|
||||
|
||||
## 快速决策
|
||||
|
||||
- 用户要**盘点、整理、治理知识库 / 文档库中的文档和目录**,不要在本 skill 里展开 workflow;切到 [`lark-drive`](../lark-drive/SKILL.md),按其 `lark-drive-knowledge-overview.md` 入口编排。
|
||||
- 用户给的是知识库 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`。
|
||||
|
||||
Reference in New Issue
Block a user