476 lines
21 KiB
Go
476 lines
21 KiB
Go
package helpers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
sheetCSVReadAll = io.ReadAll
|
|
sheetCSVReadFile = os.ReadFile
|
|
)
|
|
|
|
func newDataCmds() []*cobra.Command {
|
|
findCmd := &cobra.Command{
|
|
Use: "find",
|
|
Short: "在工作表中搜索单元格内容",
|
|
Long: `在钉钉电子表格的指定工作表中查找匹配指定文本的所有单元格,返回匹配的单元格地址和值。
|
|
|
|
通过 --node 定位电子表格文档,通过 --sheet-id 定位工作表,通过 --query 指定查找内容(--find 为其别名)。
|
|
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
|
|
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。
|
|
|
|
支持多种查找模式:
|
|
默认模式 在单元格值中进行子字符串匹配(区分大小写)
|
|
忽略大小写 通过 --match-case=false 实现
|
|
完整单元格匹配 通过 --match-entire-cell 实现
|
|
正则表达式 通过 --use-regexp 实现,--query 作为正则表达式匹配
|
|
搜索公式文本 通过 --match-formula 实现,在公式文本中查找而非计算结果
|
|
包含隐藏单元格 通过 --include-hidden 实现
|
|
|
|
可通过 --range 限定搜索范围(A1 表示法),不传时搜索整个工作表。`,
|
|
Example: ` # 基本搜索
|
|
dws sheet find --node NODE_ID --sheet-id SHEET_ID --query "销售额"
|
|
|
|
# 在指定范围内搜索
|
|
dws sheet find --node NODE_ID --sheet-id SHEET_ID --query "合计" --range "A1:D100"
|
|
|
|
# 正则表达式搜索(不区分大小写)
|
|
dws sheet find --node NODE_ID --sheet-id SHEET_ID --query "^total" --use-regexp --match-case=false
|
|
|
|
# 精确匹配整个单元格内容
|
|
dws sheet find --node NODE_ID --sheet-id SHEET_ID --query "完成" --match-entire-cell
|
|
|
|
# 搜索公式文本(使用 --find 别名)
|
|
dws sheet find --node NODE_ID --sheet-id SHEET_ID --find "SUM" --match-formula`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
text, err := mustFlagOrFallback(cmd, "query", "find")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
toolArgs := map[string]any{
|
|
"nodeId": mustGetFlag(cmd, "node"),
|
|
"sheetId": mustGetFlag(cmd, "sheet-id"),
|
|
"text": text,
|
|
}
|
|
if v, _ := cmd.Flags().GetString("range"); v != "" {
|
|
toolArgs["range"] = v
|
|
}
|
|
matchCase, _ := cmd.Flags().GetBool("match-case")
|
|
toolArgs["matchCase"] = matchCase
|
|
matchEntireCell, _ := cmd.Flags().GetBool("match-entire-cell")
|
|
toolArgs["matchEntireCell"] = matchEntireCell
|
|
useRegExp, _ := cmd.Flags().GetBool("use-regexp")
|
|
toolArgs["useRegExp"] = useRegExp
|
|
matchFormula, _ := cmd.Flags().GetBool("match-formula")
|
|
toolArgs["matchFormulaText"] = matchFormula
|
|
includeHidden, _ := cmd.Flags().GetBool("include-hidden")
|
|
toolArgs["includeHidden"] = includeHidden
|
|
return callMCPTool("find_cells", toolArgs)
|
|
},
|
|
}
|
|
DeclareLeafMetadata(findCmd, LeafSpec{
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: LeafContract{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "sheet",
|
|
Name: "find_cells",
|
|
CanonicalPath: "sheet.find_cells",
|
|
CLIPath: "sheet find",
|
|
PrimaryCLIPath: "sheet find",
|
|
},
|
|
Description: "在工作表中搜索单元格(支持精确匹配/正则/搜公式)。",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "mcp",
|
|
Availability: "available",
|
|
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "find_cells"},
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "在工作表中搜索单元格(支持精确匹配/正则/搜公式)。",
|
|
UseWhen: []string{"要查找包含某文本或公式的单元格位置时,必须用服务端 find"},
|
|
AvoidWhen: []string{"不要用 range read 全量下载后客户端过滤;要替换文本用 replace"},
|
|
Examples: []string{"dws sheet find --node <NODE_ID> --sheet-id <SHEET_ID> --find \"销售额\""},
|
|
},
|
|
Parameters: []contract.ParamDecl{
|
|
{Name: "match-formula", Property: "matchFormulaText"},
|
|
{Name: "node", Property: "nodeId"},
|
|
{Name: "query", Property: "text"},
|
|
{Name: "use-regexp", Property: "useRegExp"},
|
|
},
|
|
},
|
|
})
|
|
findCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
|
|
findCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
|
|
findCmd.Flags().String("query", "", "搜索文本 (必填,别名: --find)")
|
|
findCmd.Flags().String("find", "", "--query 的别名")
|
|
_ = findCmd.Flags().MarkHidden("find")
|
|
findCmd.Flags().String("range", "", "搜索范围,A1 表示法 (如 A1:D10)")
|
|
findCmd.Flags().Bool("match-case", true, "区分大小写 (默认 true)")
|
|
findCmd.Flags().Bool("match-entire-cell", false, "精确匹配整个单元格内容")
|
|
findCmd.Flags().Bool("use-regexp", false, "启用正则表达式搜索")
|
|
findCmd.Flags().Bool("match-formula", false, "搜索公式文本而非显示值")
|
|
findCmd.Flags().Bool("include-hidden", false, "包含隐藏单元格")
|
|
|
|
replaceCmd := &cobra.Command{
|
|
Use: "replace",
|
|
Short: "查找替换/批量替换/精确匹配替换/正则替换文本",
|
|
Long: `在钉钉表格的指定工作表中,全局查找并替换文本内容。
|
|
|
|
支持以下替换选项:
|
|
--match-case 区分大小写(默认 false)
|
|
--match-entire-cell 完整单元格匹配(默认 false)
|
|
--use-regexp 使用正则表达式匹配(默认 false)
|
|
--match-formula 在公式文本中查找替换(默认 false,替换公式源码而非显示值)
|
|
--include-hidden 包含隐藏行/列(默认 false)
|
|
--range 限定替换范围,A1 表示法(不传时在整个工作表中替换)
|
|
|
|
返回被替换的单元格数量。
|
|
|
|
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
|
|
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。`,
|
|
Example: ` # 基本替换
|
|
dws sheet replace --node NODE_ID --sheet-id SHEET_ID --find "旧文本" --replacement "新文本"
|
|
|
|
# 精确匹配整个单元格后替换
|
|
dws sheet replace --node NODE_ID --sheet-id SHEET_ID --find "待处理" --replacement "已完成" --match-entire-cell
|
|
|
|
# 正则表达式替换
|
|
dws sheet replace --node NODE_ID --sheet-id SHEET_ID --find "\\d{4}" --replacement "****" --use-regexp
|
|
|
|
# 在指定范围内替换
|
|
dws sheet replace --node NODE_ID --sheet-id SHEET_ID --find "旧" --replacement "新" --range "A1:D100"
|
|
|
|
# 删除匹配内容(替换为空)
|
|
dws sheet replace --node NODE_ID --sheet-id SHEET_ID --find "临时" --replacement ""`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
toolArgs := map[string]any{
|
|
"nodeId": mustGetFlag(cmd, "node"),
|
|
"sheetId": mustGetFlag(cmd, "sheet-id"),
|
|
"text": mustGetFlag(cmd, "find"),
|
|
"replaceText": mustGetFlag(cmd, "replacement"),
|
|
}
|
|
if v, _ := cmd.Flags().GetString("range"); v != "" {
|
|
toolArgs["range"] = v
|
|
}
|
|
matchCase, _ := cmd.Flags().GetBool("match-case")
|
|
toolArgs["matchCase"] = matchCase
|
|
matchEntireCell, _ := cmd.Flags().GetBool("match-entire-cell")
|
|
toolArgs["matchEntireCell"] = matchEntireCell
|
|
useRegExp, _ := cmd.Flags().GetBool("use-regexp")
|
|
toolArgs["useRegExp"] = useRegExp
|
|
matchFormula, _ := cmd.Flags().GetBool("match-formula")
|
|
toolArgs["matchFormulaText"] = matchFormula
|
|
includeHidden, _ := cmd.Flags().GetBool("include-hidden")
|
|
toolArgs["includeHidden"] = includeHidden
|
|
return callMCPTool("replace_all", toolArgs)
|
|
},
|
|
}
|
|
DeclareLeafMetadata(replaceCmd, LeafSpec{
|
|
Safety: contract.SafetySpec{
|
|
Effect: "write", Risk: "medium",
|
|
Confirmation: "not_required", Idempotency: "unknown",
|
|
},
|
|
Contract: LeafContract{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "sheet",
|
|
Name: "replace_all",
|
|
CanonicalPath: "sheet.replace_all",
|
|
CLIPath: "sheet replace",
|
|
PrimaryCLIPath: "sheet replace",
|
|
},
|
|
Description: "全局查找替换文本(服务端原子操作)。",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "mcp",
|
|
Availability: "available",
|
|
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "replace_all"},
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "全局查找替换文本(服务端原子操作)。",
|
|
UseWhen: []string{"要把工作表中匹配文本批量替换为另一文本时"},
|
|
AvoidWhen: []string{"不要用 find + range update 组合模拟;只查找不替换用 find"},
|
|
Examples: []string{"dws sheet replace --node <NODE_ID> --sheet-id <SHEET_ID> --find \"旧文本\" --replacement \"新文本\""},
|
|
},
|
|
Parameters: []contract.ParamDecl{
|
|
{Name: "find", Property: "text"},
|
|
{Name: "match-formula", Property: "matchFormulaText"},
|
|
{Name: "node", Property: "nodeId"},
|
|
{Name: "replacement", Property: "replaceText"},
|
|
{Name: "use-regexp", Property: "useRegExp"},
|
|
},
|
|
},
|
|
})
|
|
replaceCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
|
|
replaceCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
|
|
replaceCmd.Flags().String("find", "", "查找文本 (必填)")
|
|
replaceCmd.Flags().String("replacement", "", "替换文本 (必填,可为空字符串表示删除)")
|
|
replaceCmd.Flags().String("range", "", "替换范围,A1 表示法 (如 A1:D100)")
|
|
replaceCmd.Flags().Bool("match-case", false, "区分大小写 (默认 false)")
|
|
replaceCmd.Flags().Bool("match-entire-cell", false, "完整单元格匹配")
|
|
replaceCmd.Flags().Bool("use-regexp", false, "启用正则表达式匹配")
|
|
replaceCmd.Flags().Bool("match-formula", false, "在公式文本中查找替换(默认 false)")
|
|
replaceCmd.Flags().Bool("include-hidden", false, "包含隐藏行/列")
|
|
|
|
appendCmd := &cobra.Command{
|
|
Use: "append",
|
|
Short: "在工作表末尾追加数据",
|
|
Long: `在钉钉电子表格的指定工作表末尾追加若干行数据。
|
|
|
|
系统会自动定位到工作表中最后一行有数据的位置,在其下方插入新行数据。
|
|
如果工作表为空,数据将从第一行开始写入。
|
|
|
|
--values 为二维 JSON 数组,外层数组的每个元素代表一行,内层数组的每个元素代表该行中的一个单元格值。
|
|
追加的数据列数应与工作表已有数据的列数保持一致,以确保数据对齐。
|
|
|
|
nodeId 支持传入文档链接 URL 或文档 ID(dentryUuid),系统自动识别。
|
|
sheetId 支持传入工作表 ID 或工作表名称,可通过 sheet list 获取。`,
|
|
Example: ` dws sheet append --node NODE_ID --sheet-id SHEET_ID --values '[["张三","销售部",50000]]'
|
|
dws sheet append --node NODE_ID --sheet-id SHEET_ID \
|
|
--values '[["李四","市场部",38000],["王五","销售部",62000]]'`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := validateRequiredFlags(cmd, "node", "sheet-id", "values"); err != nil {
|
|
return err
|
|
}
|
|
var values [][]any
|
|
if err := json.Unmarshal([]byte(mustGetFlag(cmd, "values")), &values); err != nil {
|
|
return fmt.Errorf("--values JSON 解析失败: %w", err)
|
|
}
|
|
return callMCPTool("append_rows", map[string]any{
|
|
"nodeId": mustGetFlag(cmd, "node"),
|
|
"sheetId": mustGetFlag(cmd, "sheet-id"),
|
|
"values": values,
|
|
})
|
|
},
|
|
}
|
|
DeclareLeafMetadata(appendCmd, LeafSpec{
|
|
Safety: contract.SafetySpec{
|
|
Effect: "write", Risk: "medium",
|
|
Confirmation: "not_required", Idempotency: "unknown",
|
|
},
|
|
Contract: LeafContract{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "sheet",
|
|
Name: "append_rows",
|
|
CanonicalPath: "sheet.append_rows",
|
|
CLIPath: "sheet append",
|
|
PrimaryCLIPath: "sheet append",
|
|
},
|
|
Description: "在工作表数据末尾追加带值的数据行。",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "mcp",
|
|
Availability: "available",
|
|
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "append_rows"},
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "在工作表数据末尾追加带值的数据行。",
|
|
UseWhen: []string{"要在已有数据下方追加记录行(带 values)时"},
|
|
AvoidWhen: []string{"末尾追加空行/空列用 add-dimension;中间插入空行用 insert-dimension;覆盖已有区域用 range update/csv-put"},
|
|
Examples: []string{"dws sheet append --node <NODE_ID> --sheet-id <SHEET_ID> --values '[[\"张三\",\"销售部\",50000]]'"},
|
|
},
|
|
Parameters: []contract.ParamDecl{
|
|
{Name: "node", Property: "nodeId"},
|
|
},
|
|
},
|
|
})
|
|
appendCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
|
|
appendCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
|
|
appendCmd.Flags().String("values", "", "追加数据,二维 JSON 数组 (必填)")
|
|
|
|
csvPutCmd := &cobra.Command{
|
|
Use: "csv-put",
|
|
Short: "将 CSV 数据写入表格指定位置(支持公式,自动扩容)",
|
|
Long: `将 RFC 4180 格式的 CSV 文本写入指定工作表的指定单元格位置。
|
|
写入值和公式,不支持样式/批注。字段值以 = 开头时默认按公式解析;
|
|
如需写入以 = 开头的字面文本,在字段值前加单引号(例如 "'=1+1")。自动扩容行列。
|
|
目标区域如含合并单元格,csv-put 会打散合并并覆盖所有单元格;这不同于
|
|
range update 与合并区域冲突时返回 MERGED_CELLS_CONFLICT 的行为。
|
|
|
|
--csv 支持三种输入:直接传文本、@filepath 从文件读取、- 从 stdin 读取。
|
|
--allow-overwrite 默认 false,目标区域有数据时需显式传 --allow-overwrite 才能覆盖。`,
|
|
Example: ` dws sheet csv-put --node NODE_ID --sheet-id SHEET_ID --start-cell A1 \
|
|
--csv "=1+1,'=1+1"
|
|
|
|
dws sheet csv-put --node NODE_ID --sheet-id SHEET_ID --start-cell B2 \
|
|
--csv @data.csv --allow-overwrite
|
|
|
|
cat data.csv | dws sheet csv-put --node NODE_ID --sheet-id SHEET_ID \
|
|
--start-cell A1 --csv -
|
|
|
|
dws sheet csv-put --node NODE_ID --sheet-id SHEET_ID --start-cell A1 \
|
|
--csv @data.csv --dry-run`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := validateRequiredFlags(cmd, "node", "sheet-id", "csv", "start-cell"); err != nil {
|
|
return err
|
|
}
|
|
csvContent := mustGetFlag(cmd, "csv")
|
|
switch {
|
|
case csvContent == "-":
|
|
data, err := sheetCSVReadAll(os.Stdin)
|
|
if err != nil {
|
|
return fmt.Errorf("读取 stdin 失败: %w", err)
|
|
}
|
|
csvContent = string(data)
|
|
case strings.HasPrefix(csvContent, "@"):
|
|
data, err := sheetCSVReadFile(strings.TrimPrefix(csvContent, "@"))
|
|
if err != nil {
|
|
return fmt.Errorf("读取 CSV 文件失败: %w", err)
|
|
}
|
|
csvContent = string(data)
|
|
}
|
|
csvContent = strings.ReplaceAll(csvContent, "\r", "")
|
|
csvContent = strings.TrimPrefix(csvContent, "\xef\xbb\xbf")
|
|
toolArgs := map[string]any{
|
|
"nodeId": mustGetFlag(cmd, "node"),
|
|
"sheetId": mustGetFlag(cmd, "sheet-id"),
|
|
"csv": csvContent,
|
|
"startCell": mustGetFlag(cmd, "start-cell"),
|
|
}
|
|
if cmd.Flags().Changed("allow-overwrite") {
|
|
v, _ := cmd.Flags().GetBool("allow-overwrite")
|
|
toolArgs["allowOverwrite"] = v
|
|
}
|
|
return callMCPTool("set_range_from_csv", toolArgs)
|
|
},
|
|
}
|
|
DeclareLeafMetadata(csvPutCmd, LeafSpec{
|
|
Safety: contract.SafetySpec{
|
|
Effect: "write", Risk: "medium",
|
|
Confirmation: "not_required", Idempotency: "unknown",
|
|
},
|
|
Contract: LeafContract{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "sheet",
|
|
Name: "set_range_from_csv",
|
|
CanonicalPath: "sheet.set_range_from_csv",
|
|
CLIPath: "sheet csv-put",
|
|
PrimaryCLIPath: "sheet csv-put",
|
|
},
|
|
Description: "将 CSV 值或公式写入起始单元格(= 开头按公式;可自动扩容)。",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "mcp",
|
|
Availability: "available",
|
|
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "set_range_from_csv"},
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "将 CSV 值或公式写入起始单元格(= 开头按公式;可自动扩容)。",
|
|
UseWhen: []string{"需要从 CSV/表格文本批量写入值或以 = 开头的公式时优先使用"},
|
|
AvoidWhen: []string{"需要超链接/富文本/样式/数据验证用 range update;在末尾追加数据行用 append;覆盖已有数据需 --allow-overwrite;要把 = 开头内容写成字面文本需前加单引号"},
|
|
Examples: []string{
|
|
"dws sheet csv-put --node <NODE_ID> --sheet-id <SHEET_ID> --start-cell A1 --csv @data.csv --allow-overwrite",
|
|
"dws sheet csv-put --node <NODE_ID> --sheet-id <SHEET_ID> --start-cell A1 --csv \"=1+1,'=1+1\"",
|
|
},
|
|
},
|
|
Parameters: []contract.ParamDecl{
|
|
{Name: "node", Property: "nodeId"},
|
|
},
|
|
},
|
|
})
|
|
csvPutCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
|
|
csvPutCmd.Flags().String("sheet-id", "", "工作表 ID 或名称 (必填)")
|
|
csvPutCmd.Flags().String("csv", "", "CSV 文本、@文件路径 或 - 表示 stdin (必填)")
|
|
csvPutCmd.Flags().String("start-cell", "", "起始单元格,A1 表示法 (必填)")
|
|
csvPutCmd.Flags().Bool("allow-overwrite", false, "允许覆盖已有数据 (默认 false)")
|
|
|
|
csvGetCmd := &cobra.Command{
|
|
Use: "csv-get",
|
|
Short: "以 CSV 格式读取工作表数据",
|
|
Long: `以 RFC 4180 CSV 格式读取钉钉电子表格数据。
|
|
|
|
返回内容:
|
|
csv 带 [row=N] 行号前缀的 CSV 文本
|
|
colIndices 列字母映射数组(定位列用 colIndices[j],禁止手数逗号)
|
|
rowIndices 行号映射数组
|
|
hasMore 目标范围是否还有未返回数据
|
|
truncationReasons 部分返回原因:max_cells / max_chars
|
|
resolvedRange 未传 --range 时底层解析出的完整目标范围
|
|
returnedRange 本次实际完整返回的范围
|
|
|
|
取值模式(--value-render-option):
|
|
formatted_value 格式化后的展示值(默认),如 ¥1,000.00、2025-06-01
|
|
raw_value 原始值,如 1000、45808
|
|
formula 公式文本,如 =SUM(A1:A10),无公式时回退原始值
|
|
|
|
与 range read 的区别:
|
|
- CSV 格式 token 消耗约为 JSON 的 1/3
|
|
- 支持选择取值模式
|
|
- 自动防爆(30,000 单元格 / max-chars 上限 + hasMore 标志)
|
|
- [row=N] 前缀防止行号计算错误
|
|
|
|
当 hasMore=true 时本命令不会自动续读;请结合目标范围和
|
|
returnedRange 从下一行显式传 --range。max_cells 不能通过增大
|
|
--max-chars 解决。收到 forbidden.document.sizeOverLimit 表示工作簿整体
|
|
无法装载,应创建更小副本或拆分工作簿,缩小 --range 不能解决。
|
|
|
|
注意:csv-get 不返回合并单元格结构。查看合并范围请使用
|
|
dws sheet info --node NODE_ID --sheet-id SHEET_ID --format json,并读取 mergedRanges。`,
|
|
Example: ` dws sheet csv-get --node NODE_ID
|
|
dws sheet csv-get --node NODE_ID --sheet-id SHEET_ID --range "A1:D10"
|
|
dws sheet csv-get --node NODE_ID --range "A1:Z500" --value-render-option raw_value
|
|
dws sheet csv-get --node NODE_ID --range "A1:D10" --max-chars 50000`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
toolArgs := map[string]any{
|
|
"nodeId": mustGetFlag(cmd, "node"),
|
|
}
|
|
if v, _ := cmd.Flags().GetString("sheet-id"); v != "" {
|
|
toolArgs["sheetId"] = v
|
|
}
|
|
if v, _ := cmd.Flags().GetString("range"); v != "" {
|
|
toolArgs["range"] = v
|
|
}
|
|
if v, _ := cmd.Flags().GetString("value-render-option"); v != "" {
|
|
toolArgs["valueRenderOption"] = v
|
|
}
|
|
if cmd.Flags().Changed("max-chars") {
|
|
v, _ := cmd.Flags().GetInt("max-chars")
|
|
toolArgs["maxChars"] = v
|
|
}
|
|
return callMCPTool("get_range_as_csv", toolArgs)
|
|
},
|
|
}
|
|
DeclareLeafMetadata(csvGetCmd, LeafSpec{
|
|
Safety: contract.SafetySpec{
|
|
Effect: "read", Risk: "low",
|
|
Confirmation: "not_required", Idempotency: "idempotent",
|
|
},
|
|
Contract: LeafContract{
|
|
Identity: contract.ToolIdentitySpec{
|
|
ProductID: "sheet",
|
|
Name: "get_range_as_csv",
|
|
CanonicalPath: "sheet.get_range_as_csv",
|
|
CLIPath: "sheet csv-get",
|
|
PrimaryCLIPath: "sheet csv-get",
|
|
},
|
|
Description: "以 CSV 文本读取指定区域。",
|
|
Interface: &contract.InterfaceSpec{
|
|
Mode: "mcp",
|
|
Availability: "available",
|
|
Ref: &contract.InterfaceRefSpec{ProductID: "sheet", RPCName: "get_range_as_csv"},
|
|
},
|
|
Selection: contract.SelectionSpec{
|
|
AgentSummary: "以 CSV 文本读取指定区域。",
|
|
UseWhen: []string{"需要把区域导出为 CSV 文本便于管道处理时"},
|
|
AvoidWhen: []string{"需要结构化 per-cell(样式/超链接/公式)用 range read;导出整份 xlsx 用 sheet export"},
|
|
Examples: []string{"dws sheet csv-get --node <NODE_ID> --sheet-id <SHEET_ID> --range \"A1:D20\""},
|
|
},
|
|
Parameters: []contract.ParamDecl{
|
|
{Name: "node", Property: "nodeId"},
|
|
},
|
|
},
|
|
})
|
|
csvGetCmd.Flags().String("node", "", "表格文档 ID 或 URL (必填)")
|
|
csvGetCmd.Flags().String("sheet-id", "", "工作表 ID 或名称")
|
|
csvGetCmd.Flags().String("range", "", "读取范围,A1 表示法 (不传则读取全部非空数据)")
|
|
csvGetCmd.Flags().String("value-render-option", "", "取值模式: formatted_value | raw_value | formula")
|
|
csvGetCmd.Flags().Int("max-chars", 0, "CSV 最大字符数 (默认 200000)")
|
|
|
|
return []*cobra.Command{findCmd, replaceCmd, appendCmd, csvPutCmd, csvGetCmd}
|
|
}
|