Files
dingtalk-workspace-cli/internal/shortcut/smart/list_tables.go
T

181 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package smart
import (
"strconv"
"strings"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
)
// ListTables: list every data table (数据表) inside one multi-dimensional
// table (base) in a single step.
//
// This is a read-only convenience wrapper over get_tables. It mirrors helpers
// tableGetCmd exactly: it calls the "aitable" server tool "get_tables" with a
// single argument, baseId ← --base (no tableIds → the server returns all tables
// in the base). The response's table list is then defensively projected down to
// {tableId, tableName} and printed via rt.Output so it honours --format/--jq/
// --fields. When no recognisable table list is found it falls back to printing
// the raw payload.
//
// dws aitable +list-tables --base B
var ListTables = shortcut.Shortcut{
Service: "aitable",
Command: "+list-tables",
Product: "aitable",
Description: "列出某个多维表(base)里的所有数据表(只读,投影 tableId/tableName)",
Intent: "当你已经知道某个多维表(base)的 baseId、想一步看清这个 base 下都有哪些数据表(table)、" +
"拿到它们的 tableId 和 tableName 以便后续查记录或改结构,却不想手动翻 base get 的完整目录时使用;" +
"内部直接调用 get_tables,只传 baseId(不带 tableIds,因此返回该 base 下的全部数据表)," +
"再在本地把每张表投影成 tableId、tableName 两个关键字段打印出来。" +
"这是纯只读操作,只做列举与本地投影,不会创建、修改或删除任何表。",
Risk: shortcut.RiskRead,
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Contract: corecmd.ContractDecl{
Identity: contract.ToolIdentitySpec{
ProductID: "aitable",
Name: "shortcut_list_tables",
CanonicalPath: "aitable.shortcut_list_tables",
CLIPath: "aitable +list-tables",
PrimaryCLIPath: "aitable +list-tables",
},
Description: "列出某个多维表(base)里的所有数据表(只读,投影 tableId/tableName)",
Interface: &contract.InterfaceSpec{
Mode: "composite",
Availability: "available",
Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.",
},
Selection: contract.SelectionSpec{
AgentSummary: "列出某个多维表(base)里的所有数据表(只读,投影 tableId/tableName)",
UseWhen: []string{"当你已经知道某个多维表(base)的 baseId、想一步看清这个 base 下都有哪些数据表(table)、拿到它们的 tableId 和 tableName 以便后续查记录或改结构,却不想手动翻 base get 的完整目录时使用;内部直接调用 get_tables,只传 baseId(不带 tableIds,因此返回该 base 下的全部数据表),再在本地把每张表投影成 tableId、tableName 两个关键字段打印出来。这是纯只读操作,只做列举与本地投影,不会创建、修改或删除任何表。"},
AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"},
Examples: []string{"dws aitable +list-tables --base B"},
},
},
Flags: []shortcut.Flag{
{Name: "base", Type: shortcut.FlagString, Desc: "Base ID(要列出数据表的多维表)", Required: true},
},
Tips: []string{
`dws aitable +list-tables --base B`,
},
Execute: func(rt *shortcut.RuntimeContext) error {
// baseId mirrors helpers tableGetCmd (get_tables). Omitting tableIds
// asks the server for every table in the base.
data, err := rt.CallMCPData("aitable", "get_tables", map[string]any{
"baseId": rt.Str("base"),
})
if err != nil {
return err
}
// Project each table down to {tableId, tableName}; fall back to the raw
// payload when we cannot locate a recognisable table list.
items := listTablesItems(data)
if len(items) == 0 {
return rt.Output(data)
}
results := make([]map[string]any, 0, len(items))
for _, t := range items {
results = append(results, map[string]any{
"tableId": listTablesID(t),
"tableName": listTablesName(t),
})
}
return rt.Output(map[string]any{"tables": results})
},
}
// listTablesItems locates the tables list inside a get_tables response, probing
// common container keys at the top level and nested under "data"/"result".
// Returns nil when no list is found.
func listTablesItems(data map[string]any) []map[string]any {
if data == nil {
return nil
}
keys := []string{"tables", "list", "items", "records", "data", "result"}
for _, key := range keys {
if arr, ok := data[key].([]any); ok {
return listTablesToMaps(arr)
}
if inner, ok := data[key].(map[string]any); ok {
for _, k2 := range []string{"tables", "list", "items", "records"} {
if arr, ok := inner[k2].([]any); ok {
return listTablesToMaps(arr)
}
}
}
}
return nil
}
func listTablesToMaps(arr []any) []map[string]any {
out := make([]map[string]any, 0, len(arr))
for _, it := range arr {
if m, ok := it.(map[string]any); ok {
out = append(out, m)
}
}
return out
}
// listTablesID reads a table's id, tolerating the common id key variants.
func listTablesID(t map[string]any) any {
for _, key := range []string{"tableId", "table_id", "id"} {
if v := listTablesString(t[key]); v != "" {
return v
}
}
return nil
}
// listTablesName reads a table's display name, tolerating the common name key
// variants.
func listTablesName(t map[string]any) any {
for _, key := range []string{"tableName", "table_name", "name", "title"} {
if v := listTablesString(t[key]); v != "" {
return v
}
}
return nil
}
// listTablesString coerces a scalar JSON value to a trimmed string, returning ""
// for nil / non-scalar / empty values.
func listTablesString(v any) string {
switch typed := v.(type) {
case string:
return strings.TrimSpace(typed)
case float64:
return strconv.FormatFloat(typed, 'f', -1, 64)
case int:
return strconv.Itoa(typed)
case int64:
return strconv.FormatInt(typed, 10)
default:
return ""
}
}
func init() {
shortcut.Register(ListTables)
}