将 test/pre-mcp-discovery 分支上多位同学的能力改动整体合入 main, 服务发现端点保持生产 https://mcp.dingtalk.com,未携带分支上的预发改动。 主要内容: - 修雨域(chat / report / todo / contact / mail)对齐 wukong 基线 (#355) - DWS 内容生产能力增强 (#387)、文档导出与 OA 对齐 (#362 #388 #390) - envelope 注册 cli.Aliases 为 cobra aliases、命令结构与 JSON 解析优化 - skills 多包同步 wukong 对齐内容(attendance/calendar/minutes/oa/sheet 等) - product/oa 进一步对齐 wukong 排除项:未携带 566b4e1(test: point service discovery to pre-mcp.dingtalk.com); 以下 4 个常量保持生产:skill_command.go / auth/endpoints.go / cli/loader.go / market/registry.go。 Co-authored-by: john <32427341+wqyenjoy@users.noreply.github.com> Co-authored-by: wxianfeng <wang.fl1429@gmail.com>
484 lines
15 KiB
Go
484 lines
15 KiB
Go
// 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 helpers
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
|
||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor"
|
||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/i18n"
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
func newAitableFormCommand(runner executor.Runner) *cobra.Command {
|
||
form := newAitableFormGroup("form", "表单管理")
|
||
form.Hidden = true
|
||
field := newAitableFormGroup("field", "表单字段管理")
|
||
share := newAitableFormGroup("share", "表单分享管理")
|
||
questions := newAitableFormGroup("questions", "表单题目管理(等价于 field create / delete)")
|
||
|
||
field.AddCommand(
|
||
newAitableFormFieldListCommand(runner),
|
||
newAitableFormFieldUpdateCommand(runner),
|
||
newAitableFormFieldHideCommand(runner),
|
||
)
|
||
share.AddCommand(
|
||
newAitableFormShareGetCommand(runner),
|
||
newAitableFormShareUpdateCommand(runner),
|
||
)
|
||
questions.AddCommand(
|
||
newAitableFormQuestionsCreateCommand(runner),
|
||
newAitableFormQuestionsDeleteCommand(runner),
|
||
)
|
||
form.AddCommand(
|
||
newAitableFormListCommand(runner),
|
||
newAitableFormGetCommand(runner),
|
||
newAitableFormCreateCommand(runner),
|
||
newAitableFormUpdateCommand(runner),
|
||
newAitableFormDeleteCommand(runner),
|
||
field,
|
||
share,
|
||
questions,
|
||
)
|
||
return form
|
||
}
|
||
|
||
func newAitableFormGroup(use, short string) *cobra.Command {
|
||
return &cobra.Command{
|
||
Use: use,
|
||
Short: i18n.T(short),
|
||
Args: cobra.NoArgs,
|
||
TraverseChildren: true,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
return cmd.Help()
|
||
},
|
||
}
|
||
}
|
||
|
||
func newAitableFormListCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "list",
|
||
Short: i18n.T("列出表单视图"),
|
||
Example: " dws aitable form list --base-id BASE_ID --table-id TABLE_ID",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, err := requiredAitableBaseTable(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return runAitableFormTool(cmd, runner, "list_form_views", map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
})
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableFlags(cmd)
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormGetCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "get",
|
||
Short: i18n.T("获取单个表单视图详情"),
|
||
Example: " dws aitable form get --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, err := requiredAitableBaseTable(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
viewID, err := aitableRequiredFlag(cmd, "view-id")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return runAitableFormTool(cmd, runner, "list_form_views", map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewIds": []string{viewID},
|
||
})
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableFlags(cmd)
|
||
cmd.Flags().String("view-id", "", i18n.T("View ID (必填)"))
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormCreateCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "create",
|
||
Short: i18n.T("创建表单视图"),
|
||
Example: " dws aitable form create --base-id BASE_ID --table-id TABLE_ID --name 员工信息收集",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, err := requiredAitableBaseTable(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
name, err := aitableRequiredFlag(cmd, "name")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
params := map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewType": "FormDesigner",
|
||
"viewName": name,
|
||
}
|
||
// create_view does not currently declare a description parameter.
|
||
// Keep the flag for Wukong CLI compatibility, but do not send it.
|
||
return runAitableTool(cmd, runner, "create_view", params)
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableFlags(cmd)
|
||
cmd.Flags().String("name", "", i18n.T("表单名称 (必填)"))
|
||
cmd.Flags().String("description", "", i18n.T("表单描述(兼容保留)"))
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormUpdateCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "update",
|
||
Short: i18n.T("更新表单配置"),
|
||
Example: " dws aitable form update --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID --title 新标题",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, err := requiredAitableBaseTable(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
viewID, err := aitableRequiredFlag(cmd, "view-id")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
title := aitableFlagOrFallback(cmd, "title", "name")
|
||
description := aitableStringFlag(cmd, "description")
|
||
if title == "" && description == "" {
|
||
return apperrors.NewValidation("--title (or --name) and --description must specify at least one")
|
||
}
|
||
params := map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewId": viewID,
|
||
}
|
||
if title != "" {
|
||
params["title"] = title
|
||
}
|
||
if description != "" {
|
||
params["description"] = description
|
||
}
|
||
return runAitableFormTool(cmd, runner, "update_form_info", params)
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableFlags(cmd)
|
||
cmd.Flags().String("view-id", "", i18n.T("View ID (必填)"))
|
||
cmd.Flags().String("title", "", i18n.T("表单标题"))
|
||
cmd.Flags().String("name", "", i18n.T("--title 的别名"))
|
||
cmd.Flags().String("description", "", i18n.T("表单描述"))
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormDeleteCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "delete",
|
||
Short: i18n.T("删除表单"),
|
||
Example: " dws aitable form delete --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID --yes",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, err := requiredAitableBaseTable(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
viewID, err := aitableRequiredFlag(cmd, "view-id")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if !confirmDeletePrompt(cmd, i18n.T("表单"), viewID) {
|
||
return nil
|
||
}
|
||
return runAitableFormTool(cmd, runner, "delete_form_view", map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewId": viewID,
|
||
})
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableFlags(cmd)
|
||
cmd.Flags().String("view-id", "", i18n.T("View ID (必填)"))
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormQuestionsCreateCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := newAitableFieldCreateCommand(runner)
|
||
cmd.Use = "create"
|
||
cmd.Short = i18n.T("向表单添加题目(等价于 field create)")
|
||
cmd.Example = " dws aitable form questions create --base-id BASE_ID --table-id TABLE_ID --name 电话 --type text"
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormQuestionsDeleteCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := newAitableFieldDeleteCommand(runner)
|
||
cmd.Use = "delete"
|
||
cmd.Short = i18n.T("从表单删除题目(等价于 field delete)")
|
||
cmd.Example = " dws aitable form questions delete --base-id BASE_ID --table-id TABLE_ID --field-id FIELD_ID --yes"
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormFieldListCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "list",
|
||
Short: i18n.T("列出表单字段"),
|
||
Example: " dws aitable form field list --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, viewID, err := requiredAitableBaseTableView(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return runAitableFormTool(cmd, runner, "list_form_fields", map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewId": viewID,
|
||
})
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableViewFlags(cmd)
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormFieldUpdateCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "update",
|
||
Short: i18n.T("更新表单字段"),
|
||
Example: " dws aitable form field update --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID --field-id FIELD_ID --required true",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, viewID, err := requiredAitableBaseTableView(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
fieldID, err := aitableRequiredFlag(cmd, "field-id")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
params := map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewId": viewID,
|
||
"fieldId": fieldID,
|
||
}
|
||
if required, ok, err := optionalAitableBoolStringFlag(cmd, "required"); err != nil {
|
||
return err
|
||
} else if ok {
|
||
params["required"] = required
|
||
}
|
||
if description := aitableStringFlag(cmd, "field-description"); description != "" {
|
||
params["fieldDescription"] = description
|
||
}
|
||
if _, hasRequired := params["required"]; !hasRequired {
|
||
if _, hasDescription := params["fieldDescription"]; !hasDescription {
|
||
return apperrors.NewValidation("at least one of --required or --field-description is required")
|
||
}
|
||
}
|
||
return runAitableFormTool(cmd, runner, "update_form_field", params)
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableViewFlags(cmd)
|
||
cmd.Flags().String("field-id", "", i18n.T("Field ID (必填)"))
|
||
cmd.Flags().String("required", "", i18n.T("是否必填: true/false"))
|
||
cmd.Flags().String("field-description", "", i18n.T("字段描述"))
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormFieldHideCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "hide",
|
||
Short: i18n.T("切换表单字段隐藏"),
|
||
Example: " dws aitable form field hide --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID --field-id FIELD_ID --hidden true",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, viewID, err := requiredAitableBaseTableView(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
fieldID, err := aitableRequiredFlag(cmd, "field-id")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
hidden, err := requiredAitableBoolStringFlag(cmd, "hidden")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return runAitableFormTool(cmd, runner, "update_form_field_hidden", map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewId": viewID,
|
||
"fieldId": fieldID,
|
||
"hidden": hidden,
|
||
})
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableViewFlags(cmd)
|
||
cmd.Flags().String("field-id", "", i18n.T("Field ID (必填)"))
|
||
cmd.Flags().String("hidden", "", i18n.T("是否隐藏: true/false (必填)"))
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormShareGetCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "get",
|
||
Short: i18n.T("获取表单分享配置"),
|
||
Example: " dws aitable form share get --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, viewID, err := requiredAitableBaseTableView(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return runAitableFormTool(cmd, runner, "get_share_form_config", map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewId": viewID,
|
||
})
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableViewFlags(cmd)
|
||
return cmd
|
||
}
|
||
|
||
func newAitableFormShareUpdateCommand(runner executor.Runner) *cobra.Command {
|
||
cmd := &cobra.Command{
|
||
Use: "update",
|
||
Short: i18n.T("开启/关闭分享表单"),
|
||
Example: " dws aitable form share update --base-id BASE_ID --table-id TABLE_ID --view-id VIEW_ID --enabled true",
|
||
Args: cobra.NoArgs,
|
||
DisableAutoGenTag: true,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
baseID, tableID, viewID, err := requiredAitableBaseTableView(cmd)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
enabledRaw, err := requiredAitableBoolStringFlagRaw(cmd, "enabled")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return runAitableFormTool(cmd, runner, "update_share_form", map[string]any{
|
||
"baseId": baseID,
|
||
"tableId": tableID,
|
||
"viewId": viewID,
|
||
"enabled": enabledRaw,
|
||
})
|
||
},
|
||
}
|
||
preferLegacyLeaf(cmd)
|
||
addAitableBaseTableViewFlags(cmd)
|
||
cmd.Flags().String("enabled", "", i18n.T("是否开启分享: true/false (必填)"))
|
||
return cmd
|
||
}
|
||
|
||
func addAitableBaseTableFlags(cmd *cobra.Command) {
|
||
cmd.Flags().String("base-id", "", i18n.T("Base ID (必填)"))
|
||
addAitableHiddenStringFlag(cmd, "base", "--base-id 的兼容别名")
|
||
cmd.Flags().String("table-id", "", i18n.T("Table ID (必填)"))
|
||
}
|
||
|
||
func addAitableBaseTableViewFlags(cmd *cobra.Command) {
|
||
addAitableBaseTableFlags(cmd)
|
||
cmd.Flags().String("view-id", "", i18n.T("View ID (必填)"))
|
||
}
|
||
|
||
func requiredAitableBaseTable(cmd *cobra.Command) (string, string, error) {
|
||
baseID, err := aitableRequiredFlagOrFallback(cmd, "base-id", "base")
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
tableID, err := aitableRequiredFlag(cmd, "table-id")
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
return baseID, tableID, nil
|
||
}
|
||
|
||
func requiredAitableBaseTableView(cmd *cobra.Command) (string, string, string, error) {
|
||
baseID, tableID, err := requiredAitableBaseTable(cmd)
|
||
if err != nil {
|
||
return "", "", "", err
|
||
}
|
||
viewID, err := aitableRequiredFlag(cmd, "view-id")
|
||
if err != nil {
|
||
return "", "", "", err
|
||
}
|
||
return baseID, tableID, viewID, nil
|
||
}
|
||
|
||
func optionalAitableBoolStringFlag(cmd *cobra.Command, name string) (bool, bool, error) {
|
||
raw := aitableStringFlag(cmd, name)
|
||
if raw == "" {
|
||
return false, false, nil
|
||
}
|
||
value, err := parseAitableBoolString(raw, name)
|
||
if err != nil {
|
||
return false, false, err
|
||
}
|
||
return value, true, nil
|
||
}
|
||
|
||
func requiredAitableBoolStringFlag(cmd *cobra.Command, name string) (bool, error) {
|
||
raw, err := requiredAitableBoolStringFlagRaw(cmd, name)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
return parseAitableBoolString(raw, name)
|
||
}
|
||
|
||
func requiredAitableBoolStringFlagRaw(cmd *cobra.Command, name string) (string, error) {
|
||
raw := aitableStringFlag(cmd, name)
|
||
if raw == "" {
|
||
return "", apperrors.NewValidation(fmt.Sprintf("--%s is required", name))
|
||
}
|
||
if _, err := parseAitableBoolString(raw, name); err != nil {
|
||
return "", err
|
||
}
|
||
return strings.ToLower(strings.TrimSpace(raw)), nil
|
||
}
|
||
|
||
func parseAitableBoolString(raw, name string) (bool, error) {
|
||
value, err := strconv.ParseBool(strings.ToLower(strings.TrimSpace(raw)))
|
||
if err != nil {
|
||
return false, apperrors.NewValidation(fmt.Sprintf("--%s must be true or false", name))
|
||
}
|
||
return value, nil
|
||
}
|