mirror of
https://github.com/larksuite/cli.git
synced 2026-07-04 23:15:25 +08:00
Compare commits
5 Commits
v1.0.10
...
fix/format
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67ee0defab | ||
|
|
2a301246f9 | ||
|
|
abc374f1a3 | ||
|
|
2910cde73a | ||
|
|
7fdc162ff7 |
26
CHANGELOG.md
26
CHANGELOG.md
@@ -2,6 +2,31 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [v1.0.10] - 2026-04-13
|
||||
|
||||
### Features
|
||||
|
||||
- **im**: Support im oapi range download for large files (#283)
|
||||
- **sheets**: Add filter view and condition shortcuts (#422)
|
||||
- **wiki**: Add wiki move shortcut with async task polling (#436)
|
||||
- **drive**: Add drive `+create-shortcut` shortcut (#432)
|
||||
- **drive**: Add drive files patch metadata API (#444)
|
||||
- **task**: Support `--section-guid` flag in tasklist-task-add shortcut (#430)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **base**: Support large base attachment uploads (#441)
|
||||
- **config**: Clarify init copy for TTY, preserve original for AI (#448)
|
||||
- **im**: Reject `--user-id` under bot identity for chat-messages-list (#340)
|
||||
- **mail**: Add missing scopes for mail `+watch` shortcut (#357)
|
||||
- **mail**: Restrict `--output-dir` to current working directory (#376)
|
||||
|
||||
### Documentation
|
||||
|
||||
- **wiki**: Add wiki member operations to lark-wiki skill (#417)
|
||||
- **task**: Document sections API resources, permissions, and URL parsing (#430)
|
||||
- **doc**: Clarify when markdown escaping is needed (#312)
|
||||
|
||||
## [v1.0.9] - 2026-04-11
|
||||
|
||||
### Features
|
||||
@@ -303,6 +328,7 @@ Bundled AI agent skills for intelligent assistance:
|
||||
- Bilingual documentation (English & Chinese).
|
||||
- CI/CD pipelines: linting, testing, coverage reporting, and automated releases.
|
||||
|
||||
[v1.0.10]: https://github.com/larksuite/cli/releases/tag/v1.0.10
|
||||
[v1.0.9]: https://github.com/larksuite/cli/releases/tag/v1.0.9
|
||||
[v1.0.8]: https://github.com/larksuite/cli/releases/tag/v1.0.8
|
||||
[v1.0.7]: https://github.com/larksuite/cli/releases/tag/v1.0.7
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
[中文版](./README.zh.md) | [English](./README.md)
|
||||
|
||||
The official [Lark/Feishu](https://www.larksuite.com/) CLI tool, maintained by the [larksuite](https://github.com/larksuite) team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Slides, Calendar, Mail, Tasks, Meetings, and more, with 200+ commands and 21 AI Agent [Skills](./skills/).
|
||||
The official [Lark/Feishu](https://www.larksuite.com/) CLI tool, maintained by the [larksuite](https://github.com/larksuite) team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Slides, Calendar, Mail, Tasks, Meetings, and more, with 200+ commands and 22 AI Agent [Skills](./skills/).
|
||||
|
||||
[Install](#installation--quick-start) · [AI Agent Skills](#agent-skills) · [Auth](#authentication) · [Commands](#three-layer-command-system) · [Advanced](#advanced-usage) · [Security](#security--risk-warnings-read-before-use) · [Contributing](#contributing)
|
||||
|
||||
## Why lark-cli?
|
||||
|
||||
- **Agent-Native Design** — 21 structured [Skills](./skills/) out of the box, compatible with popular AI tools — Agents can operate Lark with zero extra setup
|
||||
- **Wide Coverage** — 13 business domains, 200+ curated commands, 21 AI Agent [Skills](./skills/)
|
||||
- **Agent-Native Design** — 22 structured [Skills](./skills/) out of the box, compatible with popular AI tools — Agents can operate Lark with zero extra setup
|
||||
- **Wide Coverage** — 14 business domains, 200+ curated commands, 22 AI Agent [Skills](./skills/)
|
||||
- **AI-Friendly & Optimized** — Every command is tested with real Agents, featuring concise parameters, smart defaults, and structured output to maximize Agent call success rates
|
||||
- **Open Source, Zero Barriers** — MIT license, ready to use, just `npm install`
|
||||
- **Up and Running in 3 Minutes** — One-click app creation, interactive login, from install to first API call in just 3 steps
|
||||
@@ -36,6 +36,7 @@ The official [Lark/Feishu](https://www.larksuite.com/) CLI tool, maintained by t
|
||||
| 👤 Contact | Search users by name/email/phone, get user profiles |
|
||||
| 📧 Mail | Browse, search, read emails, send, reply, forward, manage drafts, watch new mail |
|
||||
| 🎥 Meetings | Search meeting records, query meeting minutes & recordings |
|
||||
| 🕐 Attendance | Query personal attendance check-in records |
|
||||
| ✍️ Approval | Query approval tasks, approve/reject/transfer tasks, cancel and CC instances |
|
||||
|
||||
## Installation & Quick Start
|
||||
@@ -149,6 +150,7 @@ lark-cli auth status
|
||||
| `lark-minutes` | Minutes metadata & AI artifacts (summary, todos, chapters) |
|
||||
| `lark-openapi-explorer` | Explore underlying APIs from official docs |
|
||||
| `lark-skill-maker` | Custom skill creation framework |
|
||||
| `lark-attendance` | Query personal attendance check-in records |
|
||||
| `lark-approval` | Query approval tasks, approve/reject/transfer tasks, cancel and CC instances |
|
||||
| `lark-workflow-meeting-summary` | Workflow: meeting minutes aggregation & structured report |
|
||||
| `lark-workflow-standup-report` | Workflow: agenda & todo summary |
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
[中文版](./README.zh.md) | [English](./README.md)
|
||||
|
||||
飞书官方 CLI 工具,由 [larksuite](https://github.com/larksuite) 团队维护 — 让人类和 AI Agent 都能在终端中操作飞书。覆盖消息、文档、多维表格、电子表格、幻灯片、日历、邮箱、任务、会议等核心业务域,提供 200+ 命令及 21 个 AI Agent [Skills](./skills/)。
|
||||
飞书官方 CLI 工具,由 [larksuite](https://github.com/larksuite) 团队维护 — 让人类和 AI Agent 都能在终端中操作飞书。覆盖消息、文档、多维表格、电子表格、幻灯片、日历、邮箱、任务、会议等核心业务域,提供 200+ 命令及 22 个 AI Agent [Skills](./skills/)。
|
||||
|
||||
[安装](#安装与快速开始) · [AI Agent Skills](#agent-skills) · [认证](#认证) · [命令](#三层命令调用) · [进阶用法](#进阶用法) · [安全](#安全与风险提示使用前必读) · [贡献](#贡献)
|
||||
|
||||
## 为什么选 lark-cli?
|
||||
|
||||
- **为 Agent 原生设计** — 21 个 [Skills](./skills/) 开箱即用,适配主流 AI 工具,Agent 无需额外适配即可操作飞书
|
||||
- **覆盖面广** — 13 大业务域、200+ 精选命令、21 个 AI Agent [Skills](./skills/)
|
||||
- **为 Agent 原生设计** — 22 个 [Skills](./skills/) 开箱即用,适配主流 AI 工具,Agent 无需额外适配即可操作飞书
|
||||
- **覆盖面广** — 14 大业务域、200+ 精选命令、22 个 AI Agent [Skills](./skills/)
|
||||
- **AI 友好调优** — 每条命令经过 Agent 实测验证,提供更友好的参数、智能默认值和结构化输出,大幅提升 Agent 调用成功率
|
||||
- **开源零门槛** — MIT 协议,开箱即用,`npm install` 即可使用
|
||||
- **三分钟上手** — 一键创建应用、交互式登录授权,从安装到第一次 API 调用只需三步
|
||||
@@ -36,6 +36,7 @@
|
||||
| 👤 通讯录 | 按姓名/邮箱/手机号搜索用户、获取用户信息 |
|
||||
| 📧 邮箱 | 浏览、搜索、阅读邮件,发送、回复、转发邮件,管理草稿,监听新邮件 |
|
||||
| 🎥 视频会议 | 搜索会议记录、查询会议纪要与录制 |
|
||||
| 🕐 考勤打卡 | 查询个人考勤打卡记录 |
|
||||
| ✍️ 审批 | 查询审批任务、同意/拒绝/转交审批任务、撤回与抄送审批实例 |
|
||||
|
||||
## 安装与快速开始
|
||||
@@ -150,6 +151,7 @@ lark-cli auth status
|
||||
| `lark-minutes` | 妙记元数据与 AI 产物(总结、待办、章节) |
|
||||
| `lark-openapi-explorer` | 从官方文档探索底层 API |
|
||||
| `lark-skill-maker` | 自定义 skill 创建框架 |
|
||||
| `lark-attendance` | 查询个人考勤打卡记录 |
|
||||
| `lark-approval` | 审批任务查询、同意/拒绝/转交审批任务、撤回与抄送审批实例 |
|
||||
| `lark-workflow-meeting-summary` | 工作流:会议纪要汇总与结构化报告 |
|
||||
| `lark-workflow-standup-report` | 工作流:日程待办摘要 |
|
||||
|
||||
@@ -184,27 +184,6 @@ func runInteractiveLogin(ios *cmdutil.IOStreams, lang string, msg *loginMsg) (*i
|
||||
}
|
||||
fmt.Fprintf(ios.ErrOut, msg.SummaryScopes, len(scopes), scopePreview)
|
||||
|
||||
// Phase 2: confirmation
|
||||
var confirmed bool
|
||||
form2 := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
Title(msg.ConfirmAuth).
|
||||
Value(&confirmed),
|
||||
),
|
||||
).WithTheme(cmdutil.ThemeFeishu())
|
||||
|
||||
if err := form2.Run(); err != nil {
|
||||
if err == huh.ErrUserAborted {
|
||||
return nil, output.ErrBare(1)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !confirmed {
|
||||
return nil, output.ErrBare(1)
|
||||
}
|
||||
|
||||
return &interactiveResult{
|
||||
Domains: selectedDomains,
|
||||
ScopeLevel: permLevel,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
@@ -17,13 +18,39 @@ var knownArrayFields = []string{
|
||||
"members", "departments", "calendar_list", "acl_list", "freebusy_list",
|
||||
}
|
||||
|
||||
// isSliceLike reports whether v is any kind of slice (e.g. []interface{},
|
||||
// []map[string]interface{}, []string, etc.), using reflect so that the
|
||||
// check is not limited to a single concrete slice type.
|
||||
func isSliceLike(v interface{}) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
return reflect.TypeOf(v).Kind() == reflect.Slice
|
||||
}
|
||||
|
||||
// toGenericSlice converts any slice type to []interface{} by re-boxing each
|
||||
// element. This only changes the outer container type; individual elements
|
||||
// retain their original dynamic type (e.g. map[string]interface{} stays as-is).
|
||||
// Returns nil if v is not a slice.
|
||||
func toGenericSlice(v interface{}) []interface{} {
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return nil
|
||||
}
|
||||
out := make([]interface{}, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
out[i] = rv.Index(i).Interface()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// FindArrayField finds the primary array field in a response's data object.
|
||||
// It first checks knownArrayFields in priority order, then falls back to
|
||||
// the lexicographically smallest unknown array field for deterministic results.
|
||||
func FindArrayField(data map[string]interface{}) string {
|
||||
for _, name := range knownArrayFields {
|
||||
if arr, ok := data[name]; ok {
|
||||
if _, isArr := arr.([]interface{}); isArr {
|
||||
if isSliceLike(arr) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
@@ -31,7 +58,7 @@ func FindArrayField(data map[string]interface{}) string {
|
||||
// Fallback: lexicographically first array field (deterministic)
|
||||
var candidates []string
|
||||
for k, v := range data {
|
||||
if _, isArr := v.([]interface{}); isArr {
|
||||
if isSliceLike(v) {
|
||||
candidates = append(candidates, k)
|
||||
}
|
||||
}
|
||||
@@ -81,7 +108,7 @@ func ExtractItems(data interface{}) []interface{} {
|
||||
// Strategy 1: Lark API envelope — result["data"][arrayField]
|
||||
if dataObj, ok := resultMap["data"].(map[string]interface{}); ok {
|
||||
if field := FindArrayField(dataObj); field != "" {
|
||||
if items, ok := dataObj[field].([]interface{}); ok {
|
||||
if items := toGenericSlice(dataObj[field]); items != nil {
|
||||
return items
|
||||
}
|
||||
}
|
||||
@@ -90,7 +117,7 @@ func ExtractItems(data interface{}) []interface{} {
|
||||
// Strategy 2: direct map — result[arrayField]
|
||||
// Covers shortcut-level data like {"members":[…], "total":5, "has_more":false}
|
||||
if field := FindArrayField(resultMap); field != "" {
|
||||
if items, ok := resultMap[field].([]interface{}); ok {
|
||||
if items := toGenericSlice(resultMap[field]); items != nil {
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +266,129 @@ func TestExtractItems(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Typed-slice regression tests ---
|
||||
// These cover the scenario where shortcut code uses []map[string]interface{}
|
||||
// (or other typed slices) instead of []interface{} in outData.
|
||||
|
||||
func TestExtractItems_TypedMapSlice(t *testing.T) {
|
||||
// Simulates shortcut pattern: outData["chats"] = []map[string]interface{}{...}
|
||||
data := map[string]interface{}{
|
||||
"chats": []map[string]interface{}{
|
||||
{"chat_id": "oc_abc", "name": "Test Chat"},
|
||||
{"chat_id": "oc_def", "name": "Dev Chat"},
|
||||
},
|
||||
"total": 2,
|
||||
"has_more": false,
|
||||
}
|
||||
items := ExtractItems(data)
|
||||
if len(items) != 2 {
|
||||
t.Fatalf("expected 2 items from typed map slice, got %d", len(items))
|
||||
}
|
||||
// Verify elements are still map[string]interface{} (flattenItem can handle them)
|
||||
for i, item := range items {
|
||||
if _, ok := item.(map[string]interface{}); !ok {
|
||||
t.Errorf("item[%d] should be map[string]interface{}, got %T", i, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractItems_TypedMapSlice_InEnvelope(t *testing.T) {
|
||||
// Typed slice inside a Lark API envelope: result["data"]["items"] = []map[string]interface{}{...}
|
||||
data := map[string]interface{}{
|
||||
"code": float64(0),
|
||||
"data": map[string]interface{}{
|
||||
"items": []map[string]interface{}{
|
||||
{"id": "1", "name": "Alice"},
|
||||
},
|
||||
"has_more": false,
|
||||
},
|
||||
}
|
||||
items := ExtractItems(data)
|
||||
if len(items) != 1 {
|
||||
t.Fatalf("expected 1 item from typed slice in envelope, got %d", len(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatValue_Table_TypedMapSlice(t *testing.T) {
|
||||
// The core bug: --format table with []map[string]interface{} should render
|
||||
// multi-column table, not a key-value two-column fallback.
|
||||
data := map[string]interface{}{
|
||||
"chats": []map[string]interface{}{
|
||||
{"chat_id": "oc_abc", "name": "Lark Dev"},
|
||||
},
|
||||
"total": 1,
|
||||
"has_more": false,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
FormatValue(&buf, data, FormatTable)
|
||||
out := buf.String()
|
||||
|
||||
// Should have column headers from the data fields
|
||||
if !strings.Contains(out, "chat_id") {
|
||||
t.Errorf("table should contain 'chat_id' column header, got:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "name") {
|
||||
t.Errorf("table should contain 'name' column header, got:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "Lark Dev") {
|
||||
t.Errorf("table should contain data value 'Lark Dev', got:\n%s", out)
|
||||
}
|
||||
// Should NOT render as key-value fallback (metadata as rows)
|
||||
if strings.Contains(out, "has_more") {
|
||||
t.Errorf("table should not contain metadata 'has_more' as a row, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatValue_CSV_TypedMapSlice(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"messages": []map[string]interface{}{
|
||||
{"message_id": "om_abc", "content": "hello"},
|
||||
{"message_id": "om_def", "content": "world"},
|
||||
},
|
||||
"total": 2,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
FormatValue(&buf, data, FormatCSV)
|
||||
lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
|
||||
|
||||
if len(lines) != 3 {
|
||||
t.Fatalf("CSV should have header + 2 rows, got %d lines:\n%s", len(lines), buf.String())
|
||||
}
|
||||
// Header should contain data field names, not top-level map keys
|
||||
header := lines[0]
|
||||
if !strings.Contains(header, "message_id") {
|
||||
t.Errorf("CSV header should contain 'message_id', got: %s", header)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatValue_NDJSON_TypedMapSlice(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"tasks": []map[string]interface{}{
|
||||
{"guid": "t1", "url": "https://example.com/t1"},
|
||||
{"guid": "t2", "url": "https://example.com/t2"},
|
||||
},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
FormatValue(&buf, data, FormatNDJSON)
|
||||
lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
|
||||
|
||||
if len(lines) != 2 {
|
||||
t.Fatalf("NDJSON should output 2 lines, got %d:\n%s", len(lines), buf.String())
|
||||
}
|
||||
for i, line := range lines {
|
||||
var obj map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(line), &obj); err != nil {
|
||||
t.Errorf("NDJSON line %d should be valid JSON: %s", i, line)
|
||||
}
|
||||
if _, ok := obj["guid"]; !ok {
|
||||
t.Errorf("NDJSON line %d should contain 'guid' field, got: %s", i, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatValue_LegacyFormats(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@larksuite/cli",
|
||||
"version": "1.0.9",
|
||||
"version": "1.0.10",
|
||||
"description": "The official CLI for Lark/Feishu open platform",
|
||||
"bin": {
|
||||
"lark-cli": "scripts/run.js"
|
||||
|
||||
@@ -126,4 +126,38 @@ lark-cli sheets spreadsheet.sheet.filters update \
|
||||
|
||||
**常见错误:**
|
||||
- `Wrong Filter Value`:筛选已存在,需要先 delete 再 create
|
||||
- `Excess Limit`:update 时重复添加同一列条件
|
||||
- `Excess Limit`:update 时重复添加同一列条件
|
||||
|
||||
### 单元格数据类型
|
||||
|
||||
接受二维数组的 shortcut(`+write`/`+append` 的 `--values`、`+create` 的 `--data`)中,每个单元格值支持以下类型。**公式、带文本链接、@人、@文档、下拉列表必须使用对象格式**,直接传字符串会被当作纯文本存储。
|
||||
|
||||
| 类型 | 写入格式 | 示例 |
|
||||
|------|---------|------|
|
||||
| 字符串 | `"文本"` | `"hello"` |
|
||||
| 数字 | `数字` | `123`、`3.14` |
|
||||
| 日期 | `数字`(自 1899-12-30 起的天数,需先设单元格日期格式) | `42101` |
|
||||
| 链接(纯 URL) | `"URL 字符串"` | `"https://example.com"` |
|
||||
| 链接(带文本) | `{"type":"url","text":"显示文本","link":"URL"}` | `{"type":"url","text":"飞书","link":"https://www.feishu.cn"}` |
|
||||
| 邮箱 | `"邮箱字符串"` | `"user@example.com"` |
|
||||
| **公式** | `{"type":"formula","text":"=公式"}` | `{"type":"formula","text":"=SUM(A1:A10)"}` |
|
||||
| @人 | `{"type":"mention","text":"标识","textType":"email\|openId\|unionId","notify":false}` | `{"type":"mention","text":"user@example.com","textType":"email","notify":false}`(notify 可选,默认 false;仅在用户明确要求通知时设为 true) |
|
||||
| @文档 | `{"type":"mention","textType":"fileToken","text":"token","objType":"类型"}` | `{"type":"mention","textType":"fileToken","text":"shtXXX","objType":"sheet"}` |
|
||||
| 下拉列表 | `{"type":"multipleValue","values":[值1,值2]}` | `{"type":"multipleValue","values":["选项A","选项B"]}` |
|
||||
|
||||
**写入公式示例**:
|
||||
|
||||
```bash
|
||||
# ✅ 正确:使用对象格式
|
||||
lark-cli sheets +write --url "URL" --sheet-id "sheetId" --range "C6" \
|
||||
--values '[[{"type":"formula","text":"=SUM(C2:C5)"}]]'
|
||||
|
||||
# ❌ 错误:直接传字符串,会被存为纯文本
|
||||
lark-cli sheets +write --url "URL" --sheet-id "sheetId" --range "C6" \
|
||||
--values '[["=SUM(C2:C5)"]]'
|
||||
```
|
||||
|
||||
**限制**:
|
||||
- 公式不支持跨表引用(IMPORTRANGE)
|
||||
- @人仅支持同租户用户,单次最多 50 人
|
||||
- 下拉列表需先调用设置下拉列表接口,值中的字符串不能包含逗号
|
||||
|
||||
@@ -141,6 +141,40 @@ lark-cli sheets spreadsheet.sheet.filters update \
|
||||
- `Wrong Filter Value`:筛选已存在,需要先 delete 再 create
|
||||
- `Excess Limit`:update 时重复添加同一列条件
|
||||
|
||||
### 单元格数据类型
|
||||
|
||||
接受二维数组的 shortcut(`+write`/`+append` 的 `--values`、`+create` 的 `--data`)中,每个单元格值支持以下类型。**公式、带文本链接、@人、@文档、下拉列表必须使用对象格式**,直接传字符串会被当作纯文本存储。
|
||||
|
||||
| 类型 | 写入格式 | 示例 |
|
||||
|------|---------|------|
|
||||
| 字符串 | `"文本"` | `"hello"` |
|
||||
| 数字 | `数字` | `123`、`3.14` |
|
||||
| 日期 | `数字`(自 1899-12-30 起的天数,需先设单元格日期格式) | `42101` |
|
||||
| 链接(纯 URL) | `"URL 字符串"` | `"https://example.com"` |
|
||||
| 链接(带文本) | `{"type":"url","text":"显示文本","link":"URL"}` | `{"type":"url","text":"飞书","link":"https://www.feishu.cn"}` |
|
||||
| 邮箱 | `"邮箱字符串"` | `"user@example.com"` |
|
||||
| **公式** | `{"type":"formula","text":"=公式"}` | `{"type":"formula","text":"=SUM(A1:A10)"}` |
|
||||
| @人 | `{"type":"mention","text":"标识","textType":"email\|openId\|unionId","notify":false}` | `{"type":"mention","text":"user@example.com","textType":"email","notify":false}`(notify 可选,默认 false;仅在用户明确要求通知时设为 true) |
|
||||
| @文档 | `{"type":"mention","textType":"fileToken","text":"token","objType":"类型"}` | `{"type":"mention","textType":"fileToken","text":"shtXXX","objType":"sheet"}` |
|
||||
| 下拉列表 | `{"type":"multipleValue","values":[值1,值2]}` | `{"type":"multipleValue","values":["选项A","选项B"]}` |
|
||||
|
||||
**写入公式示例**:
|
||||
|
||||
```bash
|
||||
# ✅ 正确:使用对象格式
|
||||
lark-cli sheets +write --url "URL" --sheet-id "sheetId" --range "C6" \
|
||||
--values '[[{"type":"formula","text":"=SUM(C2:C5)"}]]'
|
||||
|
||||
# ❌ 错误:直接传字符串,会被存为纯文本
|
||||
lark-cli sheets +write --url "URL" --sheet-id "sheetId" --range "C6" \
|
||||
--values '[["=SUM(C2:C5)"]]'
|
||||
```
|
||||
|
||||
**限制**:
|
||||
- 公式不支持跨表引用(IMPORTRANGE)
|
||||
- @人仅支持同租户用户,单次最多 50 人
|
||||
- 下拉列表需先调用设置下拉列表接口,值中的字符串不能包含逗号
|
||||
|
||||
## Shortcuts(推荐优先使用)
|
||||
|
||||
Shortcut 是对常用操作的高级封装(`lark-cli sheets +<verb> [flags]`)。有 Shortcut 的操作优先使用。
|
||||
|
||||
Reference in New Issue
Block a user