diff --git a/shortcuts/drive/drive_export.go b/shortcuts/drive/drive_export.go index 271e07637..2cc461d13 100644 --- a/shortcuts/drive/drive_export.go +++ b/shortcuts/drive/drive_export.go @@ -31,7 +31,7 @@ var DriveExport = common.Shortcut{ Flags: []common.Flag{ {Name: "token", Desc: "source document token", Required: true}, {Name: "doc-type", Desc: "source document type: doc | docx | sheet | bitable", Required: true, Enum: []string{"doc", "docx", "sheet", "bitable"}}, - {Name: "file-extension", Desc: "export format: docx | pdf | xlsx | csv | markdown", Required: true, Enum: []string{"docx", "pdf", "xlsx", "csv", "markdown"}}, + {Name: "file-extension", Desc: "export format: docx | pdf | xlsx | csv | markdown | base (bitable only)", Required: true, Enum: []string{"docx", "pdf", "xlsx", "csv", "markdown", "base"}}, {Name: "sub-id", Desc: "sub-table/sheet ID, required when exporting sheet/bitable as csv"}, {Name: "output-dir", Default: ".", Desc: "local output directory (default: current directory)"}, {Name: "overwrite", Type: "bool", Desc: "overwrite existing output file"}, diff --git a/shortcuts/drive/drive_export_common.go b/shortcuts/drive/drive_export_common.go index cc73f82af..a9382c66f 100644 --- a/shortcuts/drive/drive_export_common.go +++ b/shortcuts/drive/drive_export_common.go @@ -137,15 +137,19 @@ func validateDriveExportSpec(spec driveExportSpec) error { } switch spec.FileExtension { - case "docx", "pdf", "xlsx", "csv", "markdown": + case "docx", "pdf", "xlsx", "csv", "markdown", "base": default: - return output.ErrValidation("invalid --file-extension %q: allowed values are docx, pdf, xlsx, csv, markdown", spec.FileExtension) + return output.ErrValidation("invalid --file-extension %q: allowed values are docx, pdf, xlsx, csv, markdown, base", spec.FileExtension) } if spec.FileExtension == "markdown" && spec.DocType != "docx" { return output.ErrValidation("--file-extension markdown only supports --doc-type docx") } + if spec.FileExtension == "base" && spec.DocType != "bitable" { + return output.ErrValidation("--file-extension base only supports --doc-type bitable") + } + if strings.TrimSpace(spec.SubID) != "" { if spec.FileExtension != "csv" || (spec.DocType != "sheet" && spec.DocType != "bitable") { return output.ErrValidation("--sub-id is only used when exporting sheet/bitable as csv") @@ -367,6 +371,8 @@ func exportFileSuffix(fileExtension string) string { return ".xlsx" case "csv": return ".csv" + case "base": + return ".base" default: return "" } diff --git a/shortcuts/drive/drive_export_common_test.go b/shortcuts/drive/drive_export_common_test.go index 39258cf64..0ca7b4f09 100644 --- a/shortcuts/drive/drive_export_common_test.go +++ b/shortcuts/drive/drive_export_common_test.go @@ -64,4 +64,10 @@ func TestSanitizeExportFileNameAndEnsureExtension(t *testing.T) { if got := ensureExportFileExtension("report.pdf", "pdf"); got != "report.pdf" { t.Fatalf("ensureExportFileExtension() should preserve suffix, got %q", got) } + if got := ensureExportFileExtension("crm", "base"); got != "crm.base" { + t.Fatalf("ensureExportFileExtension() = %q, want %q", got, "crm.base") + } + if got := exportFileSuffix("base"); got != ".base" { + t.Fatalf("exportFileSuffix(base) = %q, want %q", got, ".base") + } } diff --git a/shortcuts/drive/drive_export_test.go b/shortcuts/drive/drive_export_test.go index c80c9eb1e..296c09af1 100644 --- a/shortcuts/drive/drive_export_test.go +++ b/shortcuts/drive/drive_export_test.go @@ -5,6 +5,7 @@ package drive import ( "bytes" + "encoding/json" "errors" "net/http" "os" @@ -45,6 +46,20 @@ func TestValidateDriveExportSpec(t *testing.T) { spec: driveExportSpec{Token: "docx123", DocType: "docx", FileExtension: "pdf", SubID: "tbl_1"}, wantErr: "--sub-id is only used", }, + { + name: "base bitable ok", + spec: driveExportSpec{Token: "base123", DocType: "bitable", FileExtension: "base"}, + }, + { + name: "base non bitable rejected", + spec: driveExportSpec{Token: "sheet123", DocType: "sheet", FileExtension: "base"}, + wantErr: "only supports --doc-type bitable", + }, + { + name: "unknown file extension rejected", + spec: driveExportSpec{Token: "docx123", DocType: "docx", FileExtension: "rtf"}, + wantErr: "invalid --file-extension", + }, } for _, tt := range tests { @@ -185,6 +200,88 @@ func TestDriveExportAsyncSuccess(t *testing.T) { } } +func TestDriveExportBitableBaseAsyncSuccess(t *testing.T) { + f, stdout, _, reg := cmdutil.TestFactory(t, driveTestConfig()) + createStub := &httpmock.Stub{ + Method: "POST", + URL: "/open-apis/drive/v1/export_tasks", + Body: map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{"ticket": "tk_base"}, + }, + } + reg.Register(createStub) + reg.Register(&httpmock.Stub{ + Method: "GET", + URL: "/open-apis/drive/v1/export_tasks/tk_base", + Body: map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{ + "result": map[string]interface{}{ + "job_status": 0, + "file_token": "box_base", + "file_name": "crm", + "file_extension": "base", + "type": "bitable", + "file_size": 8, + }, + }, + }, + }) + reg.Register(&httpmock.Stub{ + Method: "GET", + URL: "/open-apis/drive/v1/export_tasks/file/box_base/download", + Status: 200, + RawBody: []byte("snapshot"), + Headers: http.Header{ + "Content-Type": []string{"application/octet-stream"}, + "Content-Disposition": []string{`attachment; filename="crm.base"`}, + }, + }) + + tmpDir := t.TempDir() + withDriveWorkingDir(t, tmpDir) + + prevAttempts, prevInterval := driveExportPollAttempts, driveExportPollInterval + driveExportPollAttempts, driveExportPollInterval = 1, 0 + t.Cleanup(func() { + driveExportPollAttempts, driveExportPollInterval = prevAttempts, prevInterval + }) + + err := mountAndRunDrive(t, DriveExport, []string{ + "+export", + "--token", "bitable123", + "--doc-type", "bitable", + "--file-extension", "base", + "--as", "bot", + }, f, stdout) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + var createBody map[string]interface{} + if err := json.Unmarshal(createStub.CapturedBody, &createBody); err != nil { + t.Fatalf("unmarshal export_tasks body: %v", err) + } + if createBody["file_extension"] != "base" { + t.Fatalf("export_tasks body file_extension = %v, want %q", createBody["file_extension"], "base") + } + if createBody["type"] != "bitable" { + t.Fatalf("export_tasks body type = %v, want %q", createBody["type"], "bitable") + } + + data, err := os.ReadFile(filepath.Join(tmpDir, "crm.base")) + if err != nil { + t.Fatalf("ReadFile() error: %v", err) + } + if string(data) != "snapshot" { + t.Fatalf("downloaded content = %q", string(data)) + } + if !strings.Contains(stdout.String(), `"file_extension": "base"`) { + t.Fatalf("stdout missing base file_extension: %s", stdout.String()) + } +} + func TestDriveExportReadyDownloadFailureIncludesRecoveryHint(t *testing.T) { f, _, _, reg := cmdutil.TestFactory(t, driveTestConfig()) reg.Register(&httpmock.Stub{ diff --git a/shortcuts/drive/drive_import.go b/shortcuts/drive/drive_import.go index aff527315..7dbe7fd8d 100644 --- a/shortcuts/drive/drive_import.go +++ b/shortcuts/drive/drive_import.go @@ -27,7 +27,7 @@ var DriveImport = common.Shortcut{ }, AuthTypes: []string{"user", "bot"}, Flags: []common.Flag{ - {Name: "file", Desc: "local file path (e.g. .docx, .xlsx, .md; large files auto use multipart upload)", Required: true}, + {Name: "file", Desc: "local file path (e.g. .docx, .xlsx, .md, .base; large files auto use multipart upload; .base is capped at 20MB)", Required: true}, {Name: "type", Desc: "target document type (docx, sheet, bitable)", Required: true}, {Name: "folder-token", Desc: "target folder token (omit for root folder; API accepts empty mount_key as root)"}, {Name: "name", Desc: "imported file name (default: local file name without extension)"}, diff --git a/shortcuts/drive/drive_import_common.go b/shortcuts/drive/drive_import_common.go index cfde7cfe3..210bfb394 100644 --- a/shortcuts/drive/drive_import_common.go +++ b/shortcuts/drive/drive_import_common.go @@ -42,6 +42,7 @@ var driveImportExtToDocTypes = map[string][]string{ "xlsx": {"sheet", "bitable"}, "xls": {"sheet"}, "csv": {"sheet", "bitable"}, + "base": {"bitable"}, } // driveImportSpec contains the user-facing import inputs after normalization. @@ -143,7 +144,7 @@ func driveImportFileSizeLimit(filePath, docType string) (int64, bool) { switch strings.TrimPrefix(strings.ToLower(filepath.Ext(filePath)), ".") { case "docx", "doc": return driveImport600MBFileSizeLimit, true - case "txt", "md", "mark", "markdown", "html", "xls": + case "txt", "md", "mark", "markdown", "html", "xls", "base": return driveImport20MBFileSizeLimit, true case "xlsx": return driveImport800MBFileSizeLimit, true @@ -198,7 +199,7 @@ func validateDriveImportSpec(spec driveImportSpec) error { supportedTypes, ok := driveImportExtToDocTypes[ext] if !ok { - return output.ErrValidation("unsupported file extension: %s. Supported extensions are: docx, doc, txt, md, mark, markdown, html, xlsx, xls, csv", ext) + return output.ErrValidation("unsupported file extension: %s. Supported extensions are: docx, doc, txt, md, mark, markdown, html, xlsx, xls, csv, base", ext) } typeAllowed := false @@ -217,6 +218,8 @@ func validateDriveImportSpec(spec driveImportSpec) error { hint = fmt.Sprintf(".%s files can only be imported as 'sheet' or 'bitable', not '%s'", ext, spec.DocType) case "xls": hint = fmt.Sprintf(".xls files can only be imported as 'sheet', not '%s'", spec.DocType) + case "base": + hint = fmt.Sprintf(".base files can only be imported as 'bitable', not '%s'", spec.DocType) default: hint = fmt.Sprintf(".%s files can only be imported as 'docx', not '%s'", ext, spec.DocType) } diff --git a/shortcuts/drive/drive_import_common_test.go b/shortcuts/drive/drive_import_common_test.go index 69daba4e2..674b22832 100644 --- a/shortcuts/drive/drive_import_common_test.go +++ b/shortcuts/drive/drive_import_common_test.go @@ -13,27 +13,54 @@ import ( "github.com/larksuite/cli/internal/httpmock" ) -func TestValidateDriveImportSpecRejectsMismatchedType(t *testing.T) { +func TestValidateDriveImportSpec(t *testing.T) { t.Parallel() - err := validateDriveImportSpec(driveImportSpec{ - FilePath: "./data.xlsx", - DocType: "docx", - }) - if err == nil || !strings.Contains(err.Error(), "file type mismatch") { - t.Fatalf("expected file type mismatch error, got %v", err) + tests := []struct { + name string + spec driveImportSpec + wantErr string + }{ + { + name: "xlsx as docx rejected", + spec: driveImportSpec{FilePath: "./data.xlsx", DocType: "docx"}, + wantErr: "file type mismatch", + }, + { + name: "xls bitable rejected", + spec: driveImportSpec{FilePath: "./data.xls", DocType: "bitable"}, + wantErr: ".xls files can only be imported as 'sheet'", + }, + { + name: "base bitable ok", + spec: driveImportSpec{FilePath: "./snapshot.base", DocType: "bitable"}, + }, + { + name: "base non bitable rejected", + spec: driveImportSpec{FilePath: "./snapshot.base", DocType: "sheet"}, + wantErr: ".base files can only be imported as 'bitable'", + }, + { + name: "unknown extension rejected", + spec: driveImportSpec{FilePath: "./data.rtf", DocType: "docx"}, + wantErr: "unsupported file extension", + }, } -} - -func TestValidateDriveImportSpecRejectsXlsBitable(t *testing.T) { - t.Parallel() - err := validateDriveImportSpec(driveImportSpec{ - FilePath: "./data.xls", - DocType: "bitable", - }) - if err == nil || !strings.Contains(err.Error(), ".xls files can only be imported as 'sheet'") { - t.Fatalf("expected xls-only-sheet validation error, got %v", err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + err := validateDriveImportSpec(tt.spec) + if tt.wantErr == "" { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + return + } + if err == nil || !strings.Contains(err.Error(), tt.wantErr) { + t.Fatalf("expected error containing %q, got %v", tt.wantErr, err) + } + }) } } @@ -74,6 +101,19 @@ func TestValidateDriveImportFileSize(t *testing.T) { docType: "sheet", fileSize: driveImport800MBFileSizeLimit, }, + { + name: "base exceeds 20mb limit", + filePath: "./snapshot.base", + docType: "bitable", + fileSize: driveImport20MBFileSizeLimit + 1, + wantText: "exceeds 20.0 MB import limit for .base", + }, + { + name: "base within 20mb limit", + filePath: "./snapshot.base", + docType: "bitable", + fileSize: driveImport20MBFileSizeLimit, + }, } for _, tt := range tests { @@ -222,6 +262,27 @@ func TestDriveImportRejectsOversizedFileByImportLimit(t *testing.T) { } } +func TestDriveImportRejectsOversizedBaseFile(t *testing.T) { + f, _, _, _ := cmdutil.TestFactory(t, driveTestConfig()) + + tmpDir := t.TempDir() + withDriveWorkingDir(t, tmpDir) + writeSizedDriveImportFile(t, "too-large.base", driveImport20MBFileSizeLimit+1) + + err := mountAndRunDrive(t, DriveImport, []string{ + "+import", + "--file", "too-large.base", + "--type", "bitable", + "--as", "bot", + }, f, nil) + if err == nil { + t.Fatal("expected size limit error, got nil") + } + if !strings.Contains(err.Error(), "exceeds 20.0 MB import limit for .base") { + t.Fatalf("unexpected error: %v", err) + } +} + func writeSizedDriveImportFile(t *testing.T, name string, size int64) { t.Helper() diff --git a/skill-template/domains/base.md b/skill-template/domains/base.md index 79c5acb42..f3f3fffab 100644 --- a/skill-template/domains/base.md +++ b/skill-template/domains/base.md @@ -13,7 +13,7 @@ 6. **批量上限 500 条/次** — 同一表建议串行写入,并在批次间延迟 0.5–1 秒 7. **改名和删除按明确意图执行** — 视图重命名这类低风险改名操作,目标和新名称明确时可直接执行;删除记录 / 字段 / 表时,只要用户已经明确要求删除且目标明确,也可直接执行,不需要再补一次确认 8. **不要走旧 bitable 路径** — Base 场景不要调用 `lark-cli api GET /open-apis/bitable/v1/...`;即使 wiki 解析结果是 `obj_type=bitable`,后续也应继续使用 `lark-cli base ...` -9. **不要把本地文件导入误判成 Base 表内操作** — 如果目标是“把 Excel / CSV 导入成 Base / 多维表格”,必须先走 `lark-cli drive +import --type bitable`;只有导入完成后,才回到 `lark-cli base ...` +9. **不要把本地文件导入误判成 Base 表内操作** — 如果目标是“把 Excel / CSV / `.base` 快照导入成 Base / 多维表格”,必须先走 `lark-cli drive +import --type bitable`;只有导入完成后,才回到 `lark-cli base ...` ## 意图 → 命令索引 @@ -22,7 +22,7 @@ | 查表字段 | `table.fields list` | 写记录 / 更新前必调 | | 查记录 | `table.records list` | GET,简单列表,可附带 `view_id` | | 按视图筛选查询 | `view.filter update` + `table.records list` | 当前 `base/v3` 没有独立 `search` | -| 把本地文件导入为 Base / 多维表格 | `lark-cli drive +import --type bitable` | 导入阶段属于 `drive`,不是 `base` | +| 把本地 Excel / CSV / `.base` 导入为 Base / 多维表格 | `lark-cli drive +import --type bitable` | 导入阶段属于 `drive`,不是 `base` | | 新增单条记录 | `table.records create` | 少量数据 | | 更新记录 | `table.records patch` | 只传需要变更的字段 | | 删除记录 | `table.records delete` | 单条删除 | diff --git a/skill-template/domains/drive.md b/skill-template/domains/drive.md index 521e78f37..f45b8f5c7 100644 --- a/skill-template/domains/drive.md +++ b/skill-template/domains/drive.md @@ -1,9 +1,9 @@ -> **导入分流规则:** 如果用户要把本地 Excel / CSV 导入成 Base / 多维表格 / bitable,必须优先使用 `lark-cli drive +import --type bitable`。不要先切到 `lark-base`;`lark-base` 只负责导入完成后的表内操作。 +> **导入分流规则:** 如果用户要把本地 Excel / CSV / `.base` 快照导入成 Base / 多维表格 / bitable,必须优先使用 `lark-cli drive +import --type bitable`。不要先切到 `lark-base`;`lark-base` 只负责导入完成后的表内操作。 ## 快速决策 -- 用户要把本地 `.xlsx` / `.csv` 导入成 Base / 多维表格 / bitable,第一步必须使用 `lark-cli drive +import --type bitable`。 +- 用户要把本地 `.xlsx` / `.csv` / `.base` 导入成 Base / 多维表格 / bitable,第一步必须使用 `lark-cli drive +import --type bitable`。 - 用户要把本地 `.md` / `.docx` / `.doc` / `.txt` / `.html` 导入成在线文档,使用 `lark-cli drive +import --type docx`。 - 用户要把本地 `.xlsx` / `.xls` / `.csv` 导入成电子表格,使用 `lark-cli drive +import --type sheet`。 - 用户要在云空间里新建文件夹,优先使用 `lark-cli drive +create-folder`。 diff --git a/skills/lark-base/SKILL.md b/skills/lark-base/SKILL.md index 5d6ecad92..031423b11 100644 --- a/skills/lark-base/SKILL.md +++ b/skills/lark-base/SKILL.md @@ -40,7 +40,7 @@ metadata: 1. 先阅读 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md)。 2. Base 业务命令仅使用 `lark-cli base +...` 形式的 shortcut 命令;如果输入是 Wiki 链接,可先调用 `lark-cli wiki spaces get_node` 解析真实 token。 3. 定位到命令后,先读该命令对应的 reference,再执行命令。 -4. 如果用户要把本地 Excel / CSV 导入成 Base / 多维表格 / bitable,第一步不是 `base`,而是 `lark-cli drive +import --type bitable`;导入完成后再回到 `lark-cli base +...` 做表内操作。 +4. 如果用户要把本地 Excel / CSV / `.base` 快照导入成 Base / 多维表格 / bitable,第一步不是 `base`,而是 `lark-cli drive +import --type bitable`;导入完成后再回到 `lark-cli base +...` 做表内操作。 5. 不要在 Base 场景改走 `lark-cli api /open-apis/bitable/v1/...`。 ## 2. 模块与命令导航 @@ -222,7 +222,7 @@ metadata: | 上传附件到记录 | `+record-upload-attachment` | 不要用 `+record-upsert` / `+record-batch-*` 伪造附件值 | | 下载记录里的附件文件 | `lark-cli docs +media-download --token --output ` | `file_token` 从 `+record-get` 返回的附件字段里取;用法见 [`../lark-doc/references/lark-doc-media-download.md`](../lark-doc/references/lark-doc-media-download.md) | | 基于视图做筛选读取 | `+view-set-filter` + `+record-list` | 不要跳过视图筛选直接猜条件 | -| 本地 Excel / CSV 导入为 Base | `lark-cli drive +import --type bitable` | 不要误走 `+base-create`、`+table-create` 或 `+record-upsert` | +| 本地 Excel / CSV / `.base` 导入为 Base | `lark-cli drive +import --type bitable` | 不要误走 `+base-create`、`+table-create` 或 `+record-upsert` | ### 3.3 表名、字段名与表达式引用 diff --git a/skills/lark-drive/SKILL.md b/skills/lark-drive/SKILL.md index 712031eca..d51495188 100644 --- a/skills/lark-drive/SKILL.md +++ b/skills/lark-drive/SKILL.md @@ -1,7 +1,7 @@ --- name: lark-drive version: 1.0.0 -description: "飞书云空间:管理云空间中的文件和文件夹。上传和下载文件、创建文件夹、复制/移动/删除文件、查看文件元数据、管理文档评论、管理文档权限、订阅用户评论变更事件、修改文件标题(docx、sheet、bitable、file、folder、wiki);也负责把本地 Word/Markdown/Excel/CSV 导入为飞书在线云文档(docx、sheet、bitable)。当用户需要上传或下载文件、整理云空间目录、查看文件详情、管理评论、管理文档权限、修改文件标题、订阅用户评论变更事件,或要把本地文件导入成新版文档、电子表格、多维表格/Base 时使用。" +description: "飞书云空间:管理云空间中的文件和文件夹。上传和下载文件、创建文件夹、复制/移动/删除文件、查看文件元数据、管理文档评论、管理文档权限、订阅用户评论变更事件、修改文件标题(docx、sheet、bitable、file、folder、wiki);也负责把本地 Word/Markdown/Excel/CSV 以及 Base 快照(.base)导入为飞书在线云文档(docx、sheet、bitable)。当用户需要上传或下载文件、整理云空间目录、查看文件详情、管理评论、管理文档权限、修改文件标题、订阅用户评论变更事件,或要把本地文件导入成新版文档、电子表格、多维表格/Base 时使用。" metadata: requires: bins: ["lark-cli"] @@ -12,11 +12,11 @@ metadata: **CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理** -> **导入分流规则:** 如果用户要把本地 Excel / CSV 导入成 Base / 多维表格 / bitable,必须优先使用 `lark-cli drive +import --type bitable`。不要先切到 `lark-base`;`lark-base` 只负责导入完成后的表内操作。 +> **导入分流规则:** 如果用户要把本地 Excel / CSV / `.base` 快照导入成 Base / 多维表格 / bitable,必须优先使用 `lark-cli drive +import --type bitable`。不要先切到 `lark-base`;`lark-base` 只负责导入完成后的表内操作。 ## 快速决策 -- 用户要把本地 `.xlsx` / `.csv` 导入成 Base / 多维表格 / bitable,第一步必须使用 `lark-cli drive +import --type bitable`。 +- 用户要把本地 `.xlsx` / `.csv` / `.base` 导入成 Base / 多维表格 / bitable,第一步必须使用 `lark-cli drive +import --type bitable`。 - 用户要把本地 `.md` / `.docx` / `.doc` / `.txt` / `.html` 导入成在线文档,使用 `lark-cli drive +import --type docx`。 - 用户要把本地 `.xlsx` / `.xls` / `.csv` 导入成电子表格,使用 `lark-cli drive +import --type sheet`。 - 用户要在云空间里新建文件夹,优先使用 `lark-cli drive +create-folder`。 diff --git a/skills/lark-drive/references/lark-drive-export.md b/skills/lark-drive/references/lark-drive-export.md index f60917c00..9aa4b379d 100644 --- a/skills/lark-drive/references/lark-drive-export.md +++ b/skills/lark-drive/references/lark-drive-export.md @@ -47,6 +47,13 @@ lark-cli drive +export \ --sub-id "" \ --output-dir ./exports +# 导出多维表格为 .base 快照(只支持 bitable) +lark-cli drive +export \ + --token "" \ + --doc-type bitable \ + --file-extension base \ + --output-dir ./exports + # 允许覆盖已存在文件 lark-cli drive +export \ --token "" \ @@ -61,7 +68,7 @@ lark-cli drive +export \ |------|------|------| | `--token` | 是 | 源文档 token | | `--doc-type` | 是 | 源文档类型:`doc` / `docx` / `sheet` / `bitable` | -| `--file-extension` | 是 | 导出格式:`docx` / `pdf` / `xlsx` / `csv` / `markdown` | +| `--file-extension` | 是 | 导出格式:`docx` / `pdf` / `xlsx` / `csv` / `markdown` / `base` | | `--sub-id` | 条件必填 | 当 `sheet` / `bitable` 导出为 `csv` 时必填 | | `--output-dir` | 否 | 本地输出目录,默认当前目录 | | `--overwrite` | 否 | 覆盖已存在文件 | @@ -69,6 +76,7 @@ lark-cli drive +export \ ## 关键约束 - `markdown` 只支持 `docx` +- `base` 只支持 `bitable` - `sheet` / `bitable` 导出为 `csv` 时必须带 `--sub-id` - shortcut 内部固定有限轮询:最多 10 次,每次间隔 5 秒 - 轮询超时不是失败;会返回 `ticket`、`timed_out=true` 和 `next_command`,供后续继续查询 diff --git a/skills/lark-drive/references/lark-drive-import.md b/skills/lark-drive/references/lark-drive-import.md index 23d1c4fe5..4fb3049fe 100644 --- a/skills/lark-drive/references/lark-drive-import.md +++ b/skills/lark-drive/references/lark-drive-import.md @@ -5,7 +5,7 @@ 将本地文件(如 Word、TXT、Markdown、Excel 等)导入并转换为飞书在线云文档(docx、sheet、bitable)。底层统一通过 `POST /open-apis/drive/v1/import_tasks` 接口创建导入任务,并在 shortcut 内做有限次数轮询 `GET /open-apis/drive/v1/import_tasks/:ticket`。 > [!IMPORTANT] -> 当用户说“把本地 Excel / CSV 导入成 Base / 多维表格 / bitable 文档”时,第一步必须使用 `drive +import --type bitable`。 +> 当用户说“把本地 Excel / CSV / `.base` 快照导入成 Base / 多维表格 / bitable 文档”时,第一步必须使用 `drive +import --type bitable`。 > 这是 Drive 导入场景,不是 `lark-base` 的建表 / 写记录场景。 > 只有导入完成并拿到新文档的 `token` / `url` 后,后续字段、记录、视图等表内操作才切换到 `lark-cli base +...`。 @@ -21,6 +21,9 @@ lark-cli drive +import --file ./data.xlsx --type sheet # 导入 Excel 为多维表格 / Base (bitable) lark-cli drive +import --file ./crm.xlsx --type bitable --name "客户台账" +# 导入 .base 快照为多维表格 / Base (bitable)(文件不能超过 20MB) +lark-cli drive +import --file ./snapshot.base --type bitable --name "快照还原" + # 导入到指定文件夹,并指定导入后的文件名 lark-cli drive +import --file ./data.csv --type bitable --folder-token --name "导入数据表" @@ -60,6 +63,7 @@ lark-cli drive +import --file ./README.md --type docx --dry-run | `.xlsx` | `sheet`, `bitable` | Microsoft Excel 表格 | | `.xls` | `sheet` | Microsoft Excel 97-2003 表格 | | `.csv` | `sheet`, `bitable` | CSV 数据文件 | +| `.base` | `bitable` | 多维表格快照文件 | > [!IMPORTANT] > 用户口头说的 “Base” / “多维表格” / “bitable”,在命令里统一对应 `--type bitable`。 @@ -68,6 +72,7 @@ lark-cli drive +import --file ./README.md --type docx --dry-run > - 文档类文件(.docx, .doc, .txt, .md, .html)**只能**导入为 `docx` > - `.xlsx` / `.csv` 文件**只能**导入为 `sheet` 或 `bitable` > - `.xls` 文件**只能**导入为 `sheet` +> - `.base` 文件**只能**导入为 `bitable` > - 例如:`.csv` 文件不能导入为 `docx`,`.md` 文件不能导入为 `sheet` > [!IMPORTANT] @@ -100,6 +105,7 @@ lark-cli drive +import --file ./README.md --type docx --dry-run | `.csv` | `sheet` | 20MB | | `.csv` | `bitable` | 100MB | | `.xls` | `sheet` | 20MB | +| `.base` | `bitable` | 20MB | - 如果文件超出对应上限,shortcut 会在真正上传前直接返回验证错误。 - “超过 20MB 自动切换分片上传”只表示上传链路会切到 multipart,不代表所有格式都允许导入超过 20MB 的文件。