feat(chat): add toolbar commands (list/add/hide/sort)
Add `dws chat toolbar` command group with shared helpers and 4 basic subcommands for managing conversation shortcut bar visibility and order. - toolbar_helpers.go: shared utilities (hasIntersection, isSystemBusy, parseExtension, toolbarConversationID, toolbarNewSystemBusyError) - toolbar_helpers_test.go: unit tests for shared helpers - toolbar.go: command group entry assembling 7 subcommands - toolbar_list.go: list shortcut entries (read/low) - toolbar_add.go: add entries to visible area (write/low) - toolbar_hide.go: hide entries from visible area (write/low) - toolbar_sort.go: sort entries with intersection validation and SYSTEM_BUSY error handling (write/low) - chat.go: mount newChatToolbarCommand() to chat root
This commit is contained in:
1 parent
a37e6e6847
commit
4dbfaa4cef
8 files changed
+601
-1
No files matched your search
@@ -8164,7 +8164,7 @@ pl_PL, sv_SE, fi_FI, cs_CZ, ar_SA, tl_PH, he_IL, nl_NL, lo_LA, it_IT`,
|
||||
chatCategoryCmd.AddCommand(chatCategoryCreateSmartCmd)
|
||||
chatMessageCmd.AddCommand(chatMessageListDirectCmd, chatMessageSearchCommonCmd, chatMessageCombineForwardCmd, chatMessageForwardTopicCmd, chatMessageSetPinCmd, chatMessageUnsetPinCmd, chatMessageListPinCmd, chatMessageAddFavoriteCmd, chatMessageRemoveFavoriteCmd, chatMessageListFavoritesCmd, chatMessageSetTopMsgCmd, chatMessageUnsetTopMsgCmd, chatMessageListEmotionRepliesCmd)
|
||||
|
||||
root.AddCommand(chatChmodCmd, chatDataAuthCmd, chatGroupCmd, chatSearchCmd, chatSearchCommonCmd, chatMessageCmd, chatFileCmd, newChatMediaGroup(), chatBotCmd, chatMessageListTopConversationsCmd, chatConversationInfoCmd, chatCategoryCmd, chatGroupRoleCmd, chatMuteCmd, chatSetTopCmd, chatGroupMuteCmd, chatGroupMuteMemberCmd, chatHideCmd, chatMuteAtAllCmd, chatMuteRedEnvelopeCmd, chatMarkUnreadCmd, chatClearRedPointCmd, chatClearAllRedPointCmd, chatListAllConversationsCmd, chatClearMessagesCmd, chatMarkReadCmd, chatTextCmd)
|
||||
root.AddCommand(chatChmodCmd, chatDataAuthCmd, chatGroupCmd, chatSearchCmd, chatSearchCommonCmd, chatMessageCmd, chatFileCmd, newChatMediaGroup(), chatBotCmd, chatMessageListTopConversationsCmd, chatConversationInfoCmd, chatCategoryCmd, chatGroupRoleCmd, chatMuteCmd, chatSetTopCmd, chatGroupMuteCmd, chatGroupMuteMemberCmd, chatHideCmd, chatMuteAtAllCmd, chatMuteRedEnvelopeCmd, chatMarkUnreadCmd, chatClearRedPointCmd, chatClearAllRedPointCmd, chatListAllConversationsCmd, chatClearMessagesCmd, chatMarkReadCmd, chatTextCmd, newChatToolbarCommand())
|
||||
|
||||
return root
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 "github.com/spf13/cobra"
|
||||
|
||||
func newChatToolbarCommand() *cobra.Command {
|
||||
toolbarCmd := &cobra.Command{
|
||||
Use: "toolbar",
|
||||
Short: "快捷栏管理",
|
||||
Long: "管理会话快捷栏入口:查询、添加、隐藏、排序及自定义入口 CRUD。",
|
||||
RunE: groupRunE,
|
||||
}
|
||||
toolbarCmd.DisableAutoGenTag = true
|
||||
|
||||
toolbarCmd.AddCommand(
|
||||
newToolbarListCommand(),
|
||||
newToolbarAddCommand(),
|
||||
newToolbarHideCommand(),
|
||||
newToolbarSortCommand(),
|
||||
newToolbarCreateCustomCommand(),
|
||||
newToolbarRemoveCustomCommand(),
|
||||
newToolbarUpdateCustomCommand(),
|
||||
)
|
||||
|
||||
return toolbarCmd
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
||||
)
|
||||
|
||||
func newToolbarAddCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "将入口添加到快捷栏可见区",
|
||||
Long: "将一个或多个快捷入口从隐藏区移至可见区。入口 ID 使用逗号分隔。",
|
||||
Example: ` dws chat toolbar add --conversation-id <cid> --shortcut-ids 101,102
|
||||
# 查询入口 ID: dws chat toolbar list --conversation-id <cid>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cid, err := toolbarConversationID(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateRequiredFlags(cmd, "shortcut-ids"); err != nil {
|
||||
return err
|
||||
}
|
||||
ids, err := parseCSVInt64(mustGetFlag(cmd, "shortcut-ids"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("--shortcut-ids: %w", err)
|
||||
}
|
||||
return callMCPToolOnServer("im", "add_chat_toolbar_shortcuts", map[string]any{
|
||||
"openCid": cid,
|
||||
"shortcutIds": ids,
|
||||
})
|
||||
},
|
||||
}
|
||||
cmd.Flags().String("conversation-id", "", "会话 openConversationId")
|
||||
cmd.Flags().String("shortcut-ids", "", "入口 ID 列表(逗号分隔)")
|
||||
_ = cmd.MarkFlagRequired("conversation-id")
|
||||
_ = cmd.MarkFlagRequired("shortcut-ids")
|
||||
cmd.DisableAutoGenTag = true
|
||||
|
||||
DeclareLeafMetadata(cmd, LeafSpec{
|
||||
Safety: contract.SafetySpec{
|
||||
Effect: "write", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Contract: LeafContract{
|
||||
Identity: contract.ToolIdentitySpec{
|
||||
ProductID: "chat",
|
||||
Name: "add_chat_toolbar_shortcuts",
|
||||
CanonicalPath: "chat.add_chat_toolbar_shortcuts",
|
||||
CLIPath: "chat toolbar add",
|
||||
PrimaryCLIPath: "chat toolbar add",
|
||||
},
|
||||
Description: "将快捷入口添加到会话快捷栏可见区",
|
||||
Interface: &contract.InterfaceSpec{
|
||||
Mode: "mcp",
|
||||
Availability: "available",
|
||||
Ref: &contract.InterfaceRefSpec{ProductID: "im", RPCName: "add_chat_toolbar_shortcuts"},
|
||||
},
|
||||
Selection: contract.SelectionSpec{
|
||||
AgentSummary: "将快捷入口添加到会话快捷栏可见区",
|
||||
UseWhen: []string{"需要将隐藏的快捷入口重新显示在快捷栏中"},
|
||||
AvoidWhen: []string{"创建新的自定义入口应使用 chat toolbar create-custom"},
|
||||
Examples: []string{"dws chat toolbar add --conversation-id <cid> --shortcut-ids 101,102"},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "conversation-id", Property: "openCid", Required: boolPtr(true)},
|
||||
{Name: "shortcut-ids", Property: "shortcutIds", Required: boolPtr(true)},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
)
|
||||
|
||||
func hasIntersection(a, b []int64) bool {
|
||||
if len(a) == 0 || len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
seen := make(map[int64]struct{}, len(a))
|
||||
for _, v := range a {
|
||||
seen[v] = struct{}{}
|
||||
}
|
||||
for _, v := range b {
|
||||
if _, ok := seen[v]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isSystemBusy(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(err.Error(), "SYSTEM_BUSY")
|
||||
}
|
||||
|
||||
func parseExtension(cmd *cobra.Command) (map[string]string, error) {
|
||||
raw, _ := cmd.Flags().GetStringArray("extension")
|
||||
if len(raw) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
result := make(map[string]string, len(raw))
|
||||
for _, pair := range raw {
|
||||
idx := strings.Index(pair, "=")
|
||||
if idx <= 0 {
|
||||
return nil, fmt.Errorf("--extension 格式错误,期望 key=value,实际 %q", pair)
|
||||
}
|
||||
key := strings.TrimSpace(pair[:idx])
|
||||
value := strings.TrimSpace(pair[idx+1:])
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("--extension key 不能为空: %q", pair)
|
||||
}
|
||||
result[key] = value
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func toolbarConversationID(cmd *cobra.Command) (string, error) {
|
||||
cid, _ := cmd.Flags().GetString("conversation-id")
|
||||
cid = strings.TrimSpace(cid)
|
||||
if cid == "" {
|
||||
return "", apperrors.NewValidation(
|
||||
"flag --conversation-id is required",
|
||||
apperrors.WithReason("missing_required_flag"),
|
||||
apperrors.WithHint("使用 --conversation-id 指定会话 openConversationId"),
|
||||
apperrors.WithActions("通过 dws chat search 或 dws chat group list 获取会话 ID"),
|
||||
)
|
||||
}
|
||||
return cid, nil
|
||||
}
|
||||
|
||||
func toolbarNewSystemBusyError() error {
|
||||
return apperrors.NewValidation(
|
||||
"服务端繁忙(SYSTEM_BUSY),请稍后重试",
|
||||
apperrors.WithReason("system_busy"),
|
||||
apperrors.WithHint("服务端当前负载较高,建议等待几秒后重试"),
|
||||
apperrors.WithActions("等待后使用相同参数重试"),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TestHasIntersection(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b []int64
|
||||
want bool
|
||||
}{
|
||||
{"both empty", nil, nil, false},
|
||||
{"a empty", nil, []int64{1}, false},
|
||||
{"b empty", []int64{1}, nil, false},
|
||||
{"no overlap", []int64{1, 2, 3}, []int64{4, 5, 6}, false},
|
||||
{"has overlap", []int64{1, 2, 3}, []int64{3, 4, 5}, true},
|
||||
{"single match", []int64{1}, []int64{1}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := hasIntersection(tt.a, tt.b)
|
||||
if got != tt.want {
|
||||
t.Errorf("hasIntersection(%v, %v) = %v, want %v", tt.a, tt.b, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSystemBusy(t *testing.T) {
|
||||
if isSystemBusy(nil) {
|
||||
t.Error("isSystemBusy(nil) should be false")
|
||||
}
|
||||
if !isSystemBusy(errors.New("error code: SYSTEM_BUSY")) {
|
||||
t.Error("isSystemBusy should detect SYSTEM_BUSY in error message")
|
||||
}
|
||||
if isSystemBusy(errors.New("some other error")) {
|
||||
t.Error("isSystemBusy should return false for non-SYSTEM_BUSY errors")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseExtension(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "test"}
|
||||
cmd.Flags().StringArray("extension", nil, "")
|
||||
|
||||
ext, err := parseExtension(cmd)
|
||||
if err != nil {
|
||||
t.Fatalf("empty extension should not error: %v", err)
|
||||
}
|
||||
if ext != nil {
|
||||
t.Fatalf("empty extension should return nil, got %v", ext)
|
||||
}
|
||||
|
||||
_ = cmd.Flags().Set("extension", "key1=val1")
|
||||
_ = cmd.Flags().Set("extension", "key2=val2")
|
||||
ext, err = parseExtension(cmd)
|
||||
if err != nil {
|
||||
t.Fatalf("valid extension should not error: %v", err)
|
||||
}
|
||||
if ext["key1"] != "val1" || ext["key2"] != "val2" {
|
||||
t.Fatalf("unexpected extension map: %v", ext)
|
||||
}
|
||||
|
||||
cmd2 := &cobra.Command{Use: "test2"}
|
||||
cmd2.Flags().StringArray("extension", nil, "")
|
||||
_ = cmd2.Flags().Set("extension", "badformat")
|
||||
_, err = parseExtension(cmd2)
|
||||
if err == nil {
|
||||
t.Fatal("invalid extension format should error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestToolbarConversationID(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "test"}
|
||||
cmd.Flags().String("conversation-id", "", "")
|
||||
|
||||
_, err := toolbarConversationID(cmd)
|
||||
if err == nil {
|
||||
t.Fatal("missing conversation-id should error")
|
||||
}
|
||||
|
||||
_ = cmd.Flags().Set("conversation-id", "cid123")
|
||||
cid, err := toolbarConversationID(cmd)
|
||||
if err != nil {
|
||||
t.Fatalf("valid conversation-id should not error: %v", err)
|
||||
}
|
||||
if cid != "cid123" {
|
||||
t.Fatalf("expected cid123, got %s", cid)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
||||
)
|
||||
|
||||
func newToolbarHideCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "hide",
|
||||
Short: "将入口从快捷栏可见区隐藏",
|
||||
Long: "将一个或多个快捷入口从可见区移至隐藏区。入口 ID 使用逗号分隔。",
|
||||
Example: ` dws chat toolbar hide --conversation-id <cid> --shortcut-ids 101,102
|
||||
# 查询入口 ID: dws chat toolbar list --conversation-id <cid>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cid, err := toolbarConversationID(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateRequiredFlags(cmd, "shortcut-ids"); err != nil {
|
||||
return err
|
||||
}
|
||||
ids, err := parseCSVInt64(mustGetFlag(cmd, "shortcut-ids"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("--shortcut-ids: %w", err)
|
||||
}
|
||||
return callMCPToolOnServer("im", "hide_chat_toolbar_shortcuts", map[string]any{
|
||||
"openCid": cid,
|
||||
"shortcutIds": ids,
|
||||
})
|
||||
},
|
||||
}
|
||||
cmd.Flags().String("conversation-id", "", "会话 openConversationId")
|
||||
cmd.Flags().String("shortcut-ids", "", "入口 ID 列表(逗号分隔)")
|
||||
_ = cmd.MarkFlagRequired("conversation-id")
|
||||
_ = cmd.MarkFlagRequired("shortcut-ids")
|
||||
cmd.DisableAutoGenTag = true
|
||||
|
||||
DeclareLeafMetadata(cmd, LeafSpec{
|
||||
Safety: contract.SafetySpec{
|
||||
Effect: "write", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Contract: LeafContract{
|
||||
Identity: contract.ToolIdentitySpec{
|
||||
ProductID: "chat",
|
||||
Name: "hide_chat_toolbar_shortcuts",
|
||||
CanonicalPath: "chat.hide_chat_toolbar_shortcuts",
|
||||
CLIPath: "chat toolbar hide",
|
||||
PrimaryCLIPath: "chat toolbar hide",
|
||||
},
|
||||
Description: "将快捷入口从会话快捷栏可见区移至隐藏区",
|
||||
Interface: &contract.InterfaceSpec{
|
||||
Mode: "mcp",
|
||||
Availability: "available",
|
||||
Ref: &contract.InterfaceRefSpec{ProductID: "im", RPCName: "hide_chat_toolbar_shortcuts"},
|
||||
},
|
||||
Selection: contract.SelectionSpec{
|
||||
AgentSummary: "将快捷入口从会话快捷栏可见区隐藏",
|
||||
UseWhen: []string{"需要将快捷栏中的入口隐藏到隐藏区"},
|
||||
AvoidWhen: []string{"永久删除自定义入口应使用 chat toolbar remove-custom"},
|
||||
Examples: []string{"dws chat toolbar hide --conversation-id <cid> --shortcut-ids 101,102"},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "conversation-id", Property: "openCid", Required: boolPtr(true)},
|
||||
{Name: "shortcut-ids", Property: "shortcutIds", Required: boolPtr(true)},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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 (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
||||
)
|
||||
|
||||
func newToolbarListCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "查询会话快捷栏入口列表",
|
||||
Long: "查询指定会话的快捷栏入口列表,包括可见区和隐藏区的所有入口。",
|
||||
Example: ` dws chat toolbar list --conversation-id <cid>
|
||||
# 查询群 ID: dws chat search --query "群名"`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cid, err := toolbarConversationID(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callMCPToolOnServer("im", "list_chat_toolbar_shortcuts", map[string]any{
|
||||
"openCid": cid,
|
||||
})
|
||||
},
|
||||
}
|
||||
cmd.Flags().String("conversation-id", "", "会话 openConversationId")
|
||||
_ = cmd.MarkFlagRequired("conversation-id")
|
||||
cmd.DisableAutoGenTag = true
|
||||
|
||||
DeclareLeafMetadata(cmd, LeafSpec{
|
||||
Safety: contract.SafetySpec{
|
||||
Effect: "read", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Contract: LeafContract{
|
||||
Identity: contract.ToolIdentitySpec{
|
||||
ProductID: "chat",
|
||||
Name: "list_chat_toolbar_shortcuts",
|
||||
CanonicalPath: "chat.list_chat_toolbar_shortcuts",
|
||||
CLIPath: "chat toolbar list",
|
||||
PrimaryCLIPath: "chat toolbar list",
|
||||
},
|
||||
Description: "查询会话快捷栏入口列表",
|
||||
Interface: &contract.InterfaceSpec{
|
||||
Mode: "mcp",
|
||||
Availability: "available",
|
||||
Ref: &contract.InterfaceRefSpec{ProductID: "im", RPCName: "list_chat_toolbar_shortcuts"},
|
||||
},
|
||||
Selection: contract.SelectionSpec{
|
||||
AgentSummary: "查询会话快捷栏入口列表",
|
||||
UseWhen: []string{"需要查看会话快捷栏中有哪些入口、获取入口 ID 时"},
|
||||
AvoidWhen: []string{"修改快捷栏排序使用 chat toolbar sort;创建自定义入口使用 chat toolbar create-custom"},
|
||||
Examples: []string{"dws chat toolbar list --conversation-id <cid>"},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "conversation-id", Property: "openCid", Required: boolPtr(true)},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
)
|
||||
|
||||
func newToolbarSortCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "sort",
|
||||
Short: "排序快捷栏入口",
|
||||
Long: "对会话快捷栏入口进行排序。sorted-ids 指定排序后的可见区入口 ID 列表,unsorted-ids 指定不参与排序放在末尾的入口 ID 列表。两个列表不能有交集。",
|
||||
Example: ` dws chat toolbar sort --conversation-id <cid> --sorted-ids 101,102,103
|
||||
dws chat toolbar sort --conversation-id <cid> --sorted-ids 101,102 --unsorted-ids 103,104
|
||||
# 查询入口 ID: dws chat toolbar list --conversation-id <cid>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cid, err := toolbarConversationID(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateRequiredFlags(cmd, "sorted-ids"); err != nil {
|
||||
return err
|
||||
}
|
||||
sortedIds, err := parseCSVInt64(mustGetFlag(cmd, "sorted-ids"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("--sorted-ids: %w", err)
|
||||
}
|
||||
|
||||
toolArgs := map[string]any{
|
||||
"openCid": cid,
|
||||
"sortedShortcutIds": sortedIds,
|
||||
}
|
||||
|
||||
if unsortedRaw, _ := cmd.Flags().GetString("unsorted-ids"); unsortedRaw != "" {
|
||||
unsortedIds, err := parseCSVInt64(unsortedRaw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("--unsorted-ids: %w", err)
|
||||
}
|
||||
if hasIntersection(sortedIds, unsortedIds) {
|
||||
return apperrors.NewValidation(
|
||||
"--sorted-ids 与 --unsorted-ids 不能有交集",
|
||||
apperrors.WithReason("id_intersection"),
|
||||
apperrors.WithHint("检查两个 ID 列表,确保同一个 ID 不同时出现在两个列表中"),
|
||||
apperrors.WithActions("修正 ID 列表后重试"),
|
||||
)
|
||||
}
|
||||
toolArgs["unsortedShortcutIds"] = unsortedIds
|
||||
}
|
||||
|
||||
err = callMCPToolOnServer("im", "sort_chat_toolbar_shortcuts", toolArgs)
|
||||
if isSystemBusy(err) {
|
||||
return toolbarNewSystemBusyError()
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
cmd.Flags().String("conversation-id", "", "会话 openConversationId")
|
||||
cmd.Flags().String("sorted-ids", "", "排序后的入口 ID 列表(逗号分隔)")
|
||||
cmd.Flags().String("unsorted-ids", "", "不参与排序放在末尾的入口 ID 列表(逗号分隔)")
|
||||
_ = cmd.MarkFlagRequired("conversation-id")
|
||||
_ = cmd.MarkFlagRequired("sorted-ids")
|
||||
cmd.DisableAutoGenTag = true
|
||||
|
||||
DeclareLeafMetadata(cmd, LeafSpec{
|
||||
Safety: contract.SafetySpec{
|
||||
Effect: "write", Risk: "low",
|
||||
Confirmation: "not_required", Idempotency: "idempotent",
|
||||
},
|
||||
Contract: LeafContract{
|
||||
Identity: contract.ToolIdentitySpec{
|
||||
ProductID: "chat",
|
||||
Name: "sort_chat_toolbar_shortcuts",
|
||||
CanonicalPath: "chat.sort_chat_toolbar_shortcuts",
|
||||
CLIPath: "chat toolbar sort",
|
||||
PrimaryCLIPath: "chat toolbar sort",
|
||||
},
|
||||
Description: "对会话快捷栏入口进行排序",
|
||||
Interface: &contract.InterfaceSpec{
|
||||
Mode: "mcp",
|
||||
Availability: "available",
|
||||
Ref: &contract.InterfaceRefSpec{ProductID: "im", RPCName: "sort_chat_toolbar_shortcuts"},
|
||||
},
|
||||
Selection: contract.SelectionSpec{
|
||||
AgentSummary: "对会话快捷栏入口进行排序",
|
||||
UseWhen: []string{"需要调整快捷栏入口的显示顺序"},
|
||||
AvoidWhen: []string{"仅添加或隐藏入口使用 chat toolbar add / hide"},
|
||||
Examples: []string{"dws chat toolbar sort --conversation-id <cid> --sorted-ids 101,102,103"},
|
||||
},
|
||||
Parameters: []contract.ParamDecl{
|
||||
{Name: "conversation-id", Property: "openCid", Required: boolPtr(true)},
|
||||
{Name: "sorted-ids", Property: "sortedShortcutIds", Required: boolPtr(true)},
|
||||
{Name: "unsorted-ids", Property: "unsortedShortcutIds", Required: boolPtr(false)},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
Reference in new issue
Block a user