Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdb461155f | ||
|
|
7e38f9fbba | ||
|
|
067b3e6e51 | ||
|
|
bcac84987f | ||
|
|
8dd87108f1 |
@@ -6,21 +6,6 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and th
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.55-beta.1] - 2026-07-23
|
||||
|
||||
This beta validates MCP Market URL resolution, the supported Wukong local-file
|
||||
send path after retiring the legacy credential-based media upload command from
|
||||
discovery, and reliable message-read rendering for rich content, forwarded
|
||||
records, encrypted messages, and media-download ID aliases.
|
||||
|
||||
### Added
|
||||
|
||||
- **MCP URL resolution** — adds `dws mcp url get <mcpId>` for resolving a DingTalk MCP Market ID to the current user and organization scoped Streamable HTTP URL, while keeping the helper-only `mcp-meta` endpoint out of the public product command surface.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Chat local-file sending** — hides the open-source-only `chat media upload` compatibility command from Help, Schema, and bundled Skills, and removes its legacy AppKey/AppSecret OAPI path. Historical argv still receives an actionable migration error. Send local images and files through `chat message send --msg-type file --file-path`; callers that already hold a mediaId may continue to use `--msg-type image --media-id`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Message-read shortcut projection** (#706) — the message-list shortcuts (`chat +chat-messages` / `+messages-list` / `+messages-list-direct` / `+at-me` / `+search-msg` / `+thread-replies`) now render card and out-of-office rich-content JSON as readable text (without ever rewriting ordinary text that merely embeds a JSON fragment), expand a forwarded chat record's nested `forwardMessages` instead of collapsing to a "[卡片]" summary, and mark undecryptable encrypted card messages as `[加密消息]`; the speaker is read from the bare `sender` key, nested `{name:…}` sender objects yield their display name, and the literal string `"null"` is treated as absent. Shared projection helpers now live in `internal/shortcut/chatmsg`. `chat message download-media` also gains `--msg-id` / `--open-message-id` aliases for its `--message-id` flag so agents copying the `openMessageId`/`msgId` output field no longer hit "unknown flag".
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
// 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 app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
mcpMetaServerID = "mcp-meta"
|
||||
mcpMetaURLTool = "get_mcp_server_url"
|
||||
)
|
||||
|
||||
func newMCPURLGroup(caller edition.ToolCaller) *cobra.Command {
|
||||
group := &cobra.Command{
|
||||
Use: "url",
|
||||
Short: "管理 MCP 服务连接地址",
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cmd.Help()
|
||||
},
|
||||
}
|
||||
group.AddCommand(newMCPURLGetCommand(caller))
|
||||
return group
|
||||
}
|
||||
|
||||
func newMCPURLGetCommand(caller edition.ToolCaller) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get <mcpId>",
|
||||
Short: "按 mcpId 获取 MCP 的 Streamable HTTP 服务地址",
|
||||
Long: "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 " +
|
||||
"Streamable HTTP 服务地址。\n\n" +
|
||||
"安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用," +
|
||||
"请勿分享到群聊、文档、邮件、代码仓库或日志。",
|
||||
Example: " dws mcp url get 2480\n" +
|
||||
" dws mcp url get 2480 --format json",
|
||||
Args: cobra.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if caller == nil {
|
||||
return fmt.Errorf("MCP tool caller is not configured")
|
||||
}
|
||||
mcpID := strings.TrimSpace(args[0])
|
||||
if mcpID == "" {
|
||||
return fmt.Errorf("mcpId 不能为空")
|
||||
}
|
||||
|
||||
result, err := caller.CallTool(cmd.Context(), mcpMetaServerID, mcpMetaURLTool, map[string]any{
|
||||
"mcpId": mcpID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 MCP 服务地址: %w", err)
|
||||
}
|
||||
return writeMCPURLResult(cmd, result)
|
||||
},
|
||||
}
|
||||
cli.AnnotateRuntimePositionals(cmd, cli.RuntimeSchemaPositional{
|
||||
Name: "mcp_id",
|
||||
Type: "string",
|
||||
Description: "钉钉 MCP 市场中的 mcpId",
|
||||
Required: true,
|
||||
Index: 0,
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
func writeMCPURLResult(cmd *cobra.Command, result *edition.ToolResult) error {
|
||||
if result == nil {
|
||||
return fmt.Errorf("MCP 元服务返回空结果")
|
||||
}
|
||||
// get_mcp_server_url returns one JSON document in its first non-empty text
|
||||
// block. Other block types and trailing blocks are intentionally ignored.
|
||||
for _, block := range result.Content {
|
||||
if block.Type != "text" || strings.TrimSpace(block.Text) == "" {
|
||||
continue
|
||||
}
|
||||
if err := apperrors.ClassifyMCPResponseText(block.Text); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var payload any
|
||||
if err := json.Unmarshal([]byte(block.Text), &payload); err != nil {
|
||||
return fmt.Errorf("MCP 元服务返回了无效 JSON: %w", err)
|
||||
}
|
||||
return output.WriteCommandPayload(cmd, payload, output.FormatJSON)
|
||||
}
|
||||
return fmt.Errorf("MCP 元服务返回空结果")
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
// 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 app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type mcpURLTestCaller struct {
|
||||
productID string
|
||||
toolName string
|
||||
args map[string]any
|
||||
result *edition.ToolResult
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *mcpURLTestCaller) CallTool(_ context.Context, productID, toolName string, args map[string]any) (*edition.ToolResult, error) {
|
||||
c.productID = productID
|
||||
c.toolName = toolName
|
||||
c.args = args
|
||||
return c.result, c.err
|
||||
}
|
||||
|
||||
func (*mcpURLTestCaller) Format() string { return "json" }
|
||||
func (*mcpURLTestCaller) DryRun() bool { return false }
|
||||
func (*mcpURLTestCaller) Fields() string { return "" }
|
||||
func (*mcpURLTestCaller) JQ() string { return "" }
|
||||
|
||||
func executeMCPURLCommand(t *testing.T, caller edition.ToolCaller, args ...string) (string, error) {
|
||||
t.Helper()
|
||||
root := &cobra.Command{Use: "mcp", SilenceErrors: true, SilenceUsage: true}
|
||||
root.AddCommand(newMCPURLGroup(caller))
|
||||
var out bytes.Buffer
|
||||
root.SetOut(&out)
|
||||
root.SetErr(&out)
|
||||
root.SetArgs(args)
|
||||
err := root.ExecuteContext(t.Context())
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
func TestMCPURLGetCallsMetaServerAndPreservesResponse(t *testing.T) {
|
||||
const response = `{"result":{"mcpURL":"https://example.test/mcp?key=one&token=two","mcpJSON":{"transport":"streamable-http"},"name":"Example"}}`
|
||||
caller := &mcpURLTestCaller{
|
||||
result: &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: response}}},
|
||||
}
|
||||
|
||||
out, err := executeMCPURLCommand(t, caller, "url", "get", " 10043 ")
|
||||
if err != nil {
|
||||
t.Fatalf("execute mcp url get: %v", err)
|
||||
}
|
||||
if caller.productID != mcpMetaServerID {
|
||||
t.Fatalf("productID = %q, want %q", caller.productID, mcpMetaServerID)
|
||||
}
|
||||
if caller.toolName != mcpMetaURLTool {
|
||||
t.Fatalf("toolName = %q, want %q", caller.toolName, mcpMetaURLTool)
|
||||
}
|
||||
if got := caller.args["mcpId"]; got != "10043" {
|
||||
t.Fatalf("mcpId = %#v, want %q", got, "10043")
|
||||
}
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal([]byte(out), &payload); err != nil {
|
||||
t.Fatalf("output is not JSON: %v\n%s", err, out)
|
||||
}
|
||||
result, ok := payload["result"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("output result = %#v", payload["result"])
|
||||
}
|
||||
if got := result["mcpURL"]; got != "https://example.test/mcp?key=one&token=two" {
|
||||
t.Fatalf("result.mcpURL = %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPURLGetRejectsBlankID(t *testing.T) {
|
||||
_, err := executeMCPURLCommand(t, &mcpURLTestCaller{}, "url", "get", " ")
|
||||
if err == nil || !strings.Contains(err.Error(), "mcpId 不能为空") {
|
||||
t.Fatalf("error = %v, want blank mcpId error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPURLGroupShowsHelp(t *testing.T) {
|
||||
out, err := executeMCPURLCommand(t, nil, "url")
|
||||
if err != nil {
|
||||
t.Fatalf("execute mcp url: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "get") {
|
||||
t.Fatalf("help output does not list get command:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPURLGetRejectsMissingCaller(t *testing.T) {
|
||||
_, err := executeMCPURLCommand(t, nil, "url", "get", "10043")
|
||||
if err == nil || !strings.Contains(err.Error(), "caller is not configured") {
|
||||
t.Fatalf("error = %v, want missing caller error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPURLGetPropagatesCallError(t *testing.T) {
|
||||
caller := &mcpURLTestCaller{err: errors.New("permission denied")}
|
||||
_, err := executeMCPURLCommand(t, caller, "url", "get", "10043")
|
||||
if err == nil || !strings.Contains(err.Error(), "permission denied") {
|
||||
t.Fatalf("error = %v, want call error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPURLGetRejectsInvalidJSON(t *testing.T) {
|
||||
caller := &mcpURLTestCaller{
|
||||
result: &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: "not-json"}}},
|
||||
}
|
||||
_, err := executeMCPURLCommand(t, caller, "url", "get", "10043")
|
||||
if err == nil || !strings.Contains(err.Error(), "无效 JSON") {
|
||||
t.Fatalf("error = %v, want invalid JSON error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPURLGetRejectsEmptyResults(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
result *edition.ToolResult
|
||||
}{
|
||||
{name: "nil result"},
|
||||
{
|
||||
name: "no usable text content",
|
||||
result: &edition.ToolResult{Content: []edition.ContentBlock{
|
||||
{Type: "image", Text: "ignored"},
|
||||
{Type: "text", Text: " "},
|
||||
}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
caller := &mcpURLTestCaller{result: tt.result}
|
||||
_, err := executeMCPURLCommand(t, caller, "url", "get", "10043")
|
||||
if err == nil || !strings.Contains(err.Error(), "返回空结果") {
|
||||
t.Fatalf("error = %v, want empty result error", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCPURLGetClassifiesBusinessError(t *testing.T) {
|
||||
caller := &mcpURLTestCaller{
|
||||
result: &edition.ToolResult{Content: []edition.ContentBlock{{
|
||||
Type: "text",
|
||||
Text: `{"success":false,"errorMsg":"搜索内容不能为空"}`,
|
||||
}}},
|
||||
}
|
||||
_, err := executeMCPURLCommand(t, caller, "url", "get", "10043")
|
||||
if err == nil {
|
||||
t.Fatal("expected classified business error")
|
||||
}
|
||||
var typed *apperrors.Error
|
||||
if !errors.As(err, &typed) || typed.Reason != "business_error" {
|
||||
t.Fatalf("error = %#v, want classified business error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootRegistersMCPURLGet(t *testing.T) {
|
||||
root := NewRootCommand(t.Context())
|
||||
mcp, _, err := root.Find([]string{"mcp"})
|
||||
if err != nil {
|
||||
t.Fatalf("find mcp: %v", err)
|
||||
}
|
||||
if mcp.Hidden {
|
||||
t.Fatal("mcp command must be public when it contains reviewed public helpers")
|
||||
}
|
||||
cmd, _, err := root.Find([]string{"mcp", "url", "get"})
|
||||
if err != nil {
|
||||
t.Fatalf("find mcp url get: %v", err)
|
||||
}
|
||||
if got := cmd.CommandPath(); got != "dws mcp url get" {
|
||||
t.Fatalf("command path = %q, want %q", got, "dws mcp url get")
|
||||
}
|
||||
}
|
||||
@@ -385,16 +385,11 @@ func newRootCommandWithEngine(rootCtx context.Context, engine *pipeline.Engine,
|
||||
|
||||
schemaCmd := newSchemaCommand(loader)
|
||||
mcpCmd := newMCPCommand(rootCtx, loader, runner, engine)
|
||||
// The legacy dynamic MCP surface remains disabled, but reviewed static MCP
|
||||
// helpers registered below are part of the public CLI and Schema surface.
|
||||
mcpCmd.Hidden = false
|
||||
mcpCmd.Short = "管理 MCP 服务连接信息"
|
||||
mcpCmd.Long = "管理经过审核并纳入 Schema 的 MCP 服务连接辅助能力。"
|
||||
mcpCmd.Hidden = true
|
||||
// Wrap the caller so every MCP tool call's shape is recorded to the local
|
||||
// usage log (privacy-preserving; see internal/shortcut/usage). Powers
|
||||
// `dws shortcut stats` and future high-frequency shortcut distillation.
|
||||
patCaller := newRecordingToolCaller(newToolCallerAdapter(runner, flags))
|
||||
mcpCmd.AddCommand(newMCPURLGroup(patCaller))
|
||||
|
||||
utilityCommands := []*cobra.Command{
|
||||
newAuthCommand(patCaller),
|
||||
|
||||
@@ -15,14 +15,11 @@ package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
stderrors "errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -78,14 +75,7 @@ func TestRootKeepsMainBranchChatCompatibilityCommands(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
mediaGroup := mustFindCommand(t, root, "chat", "media")
|
||||
if mediaGroup.Deprecated == "" || mediaGroup.Hidden || !mediaGroup.Runnable() {
|
||||
t.Fatalf("chat media compatibility contract: deprecated=%q hidden=%v runnable=%v", mediaGroup.Deprecated, mediaGroup.Hidden, mediaGroup.Runnable())
|
||||
}
|
||||
mediaUpload := mustFindCommand(t, mediaGroup, "upload")
|
||||
if mediaUpload.Deprecated == "" || mediaUpload.Hidden || !mediaUpload.Runnable() {
|
||||
t.Fatalf("chat media upload compatibility contract: deprecated=%q hidden=%v runnable=%v", mediaUpload.Deprecated, mediaUpload.Hidden, mediaUpload.Runnable())
|
||||
}
|
||||
mediaUpload := mustFindCommand(t, root, "chat", "media", "upload")
|
||||
for _, flag := range []string{"file", "type"} {
|
||||
if mediaUpload.Flags().Lookup(flag) == nil {
|
||||
t.Fatalf("chat media upload missing --%s", flag)
|
||||
@@ -98,113 +88,6 @@ func TestRootKeepsMainBranchChatCompatibilityCommands(t *testing.T) {
|
||||
mustFindCommand(t, root, "conference", "meeting", "reserve")
|
||||
}
|
||||
|
||||
func TestChatHelpAndSchemaHideRetiredMediaUpload(t *testing.T) {
|
||||
for _, args := range [][]string{
|
||||
{"chat", "--help"},
|
||||
{"chat", "media", "--help"},
|
||||
} {
|
||||
root := NewRootCommand()
|
||||
var output bytes.Buffer
|
||||
root.SetOut(&output)
|
||||
root.SetErr(&output)
|
||||
root.SetArgs(args)
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatalf("dws %s: %v\n%s", strings.Join(args, " "), err, output.String())
|
||||
}
|
||||
for _, line := range strings.Split(output.String(), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) > 0 && (fields[0] == "media" || fields[0] == "upload") {
|
||||
t.Fatalf("dws %s exposes retired command in Help line %q:\n%s", strings.Join(args, " "), line, output.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root := NewRootCommand()
|
||||
var output bytes.Buffer
|
||||
root.SetOut(&output)
|
||||
root.SetErr(&output)
|
||||
root.SetArgs([]string{"schema", "--cli-path", "chat media upload", "--format", "json"})
|
||||
err := root.Execute()
|
||||
if err == nil {
|
||||
t.Fatalf("retired chat media upload remains queryable from Schema:\n%s", output.String())
|
||||
}
|
||||
if !strings.Contains(err.Error(), "unknown runtime schema path") {
|
||||
t.Fatalf("retired chat media upload Schema error = %v, want unknown path", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootChatMediaUploadWithoutAppCredentialsReturnsMigrationValidation(t *testing.T) {
|
||||
for _, key := range []string{"DWS_CLIENT_ID", "DWS_CLIENT_SECRET"} {
|
||||
value, existed := os.LookupEnv(key)
|
||||
if err := os.Unsetenv(key); err != nil {
|
||||
t.Fatalf("unset %s: %v", key, err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if existed {
|
||||
_ = os.Setenv(key, value)
|
||||
return
|
||||
}
|
||||
_ = os.Unsetenv(key)
|
||||
})
|
||||
if _, exists := os.LookupEnv(key); exists {
|
||||
t.Fatalf("%s is still set", key)
|
||||
}
|
||||
}
|
||||
t.Setenv("DWS_CONFIG_DIR", filepath.Join(t.TempDir(), "config"))
|
||||
|
||||
filePath := filepath.Join(t.TempDir(), "image.png")
|
||||
if err := os.WriteFile(filePath, []byte("image"), 0o600); err != nil {
|
||||
t.Fatalf("write image fixture: %v", err)
|
||||
}
|
||||
commandArgs := []string{
|
||||
"chat", "media", "upload",
|
||||
"--file", filePath,
|
||||
"--type", "image",
|
||||
}
|
||||
previousArgs := os.Args
|
||||
os.Args = append([]string{"dws"}, commandArgs...)
|
||||
t.Cleanup(func() { os.Args = previousArgs })
|
||||
|
||||
root := NewRootCommand()
|
||||
var output bytes.Buffer
|
||||
root.SetOut(&output)
|
||||
root.SetErr(&output)
|
||||
root.SetArgs(commandArgs)
|
||||
err := root.Execute()
|
||||
if err == nil {
|
||||
t.Fatalf("chat media upload succeeded without app credentials:\n%s", output.String())
|
||||
}
|
||||
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) {
|
||||
t.Fatalf("chat media upload error type = %T, want *errors.Error: %v", err, err)
|
||||
}
|
||||
if typed.Category != apperrors.CategoryValidation {
|
||||
t.Fatalf("chat media upload category = %q, want %q", typed.Category, apperrors.CategoryValidation)
|
||||
}
|
||||
if exitCode := apperrors.ExitCode(err); exitCode != 3 {
|
||||
t.Fatalf("chat media upload exit code = %d, want 3", exitCode)
|
||||
}
|
||||
|
||||
got := output.String() + "\n" + err.Error()
|
||||
for _, want := range []string{"已下线", "chat message send --msg-type file --file-path"} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("chat media upload migration output missing %q:\n%s", want, got)
|
||||
}
|
||||
}
|
||||
for _, forbidden := range []string{
|
||||
"DWS_CLIENT_ID",
|
||||
"DWS_CLIENT_SECRET",
|
||||
"缺少应用凭证",
|
||||
"AppSecret",
|
||||
"clientSecret",
|
||||
} {
|
||||
if strings.Contains(got, forbidden) {
|
||||
t.Fatalf("chat media upload returned credential error %q:\n%s", forbidden, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootKeepsContactWukongCompatibilityCommands(t *testing.T) {
|
||||
root := NewRootCommand()
|
||||
label := mustFindCommand(t, root, "contact", "label")
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:0977e2b3d4af77d348318fd0cd7ce36d325ba9ca4e7c9ed0d6daeb1be0a0c4ec",
|
||||
"surface_hash": "sha256:e85148774f65d34b01d50ebdc702b785afdb8ea78663b6f2eeecc35bc3cb3fd5",
|
||||
"source_hash": "sha256:5df496973c41b3b4f7ae8c856ff0e9e99bcabd4455419b7d41bb23c44df6f1af",
|
||||
"surface_hash": "sha256:7ef588f38052f0104e027c8daff5fefd68698781f2c87715461183d4058288ed",
|
||||
"coverage": {
|
||||
"surface_products": 23,
|
||||
"products_with_metadata": 23,
|
||||
"surface_tools": 573,
|
||||
"tools_with_metadata": 573,
|
||||
"tools_with_agent_summary": 573,
|
||||
"tools_with_use_when": 573,
|
||||
"tools_with_avoid_when": 573,
|
||||
"tools_with_examples": 573,
|
||||
"tools_with_interface_mode": 573,
|
||||
"unmatched_skill_tools": 119,
|
||||
"surface_products": 22,
|
||||
"products_with_metadata": 22,
|
||||
"surface_tools": 572,
|
||||
"tools_with_metadata": 572,
|
||||
"tools_with_agent_summary": 572,
|
||||
"tools_with_use_when": 572,
|
||||
"tools_with_avoid_when": 572,
|
||||
"tools_with_examples": 572,
|
||||
"tools_with_interface_mode": 572,
|
||||
"unmatched_skill_tools": 120,
|
||||
"unreviewed_skill_tools": 7
|
||||
},
|
||||
"products": {
|
||||
@@ -1089,74 +1089,6 @@
|
||||
"查收、搜索、阅读、回复、发送或整理邮件"
|
||||
]
|
||||
},
|
||||
"mcp": {
|
||||
"agent_summary": "解析和管理当前身份可用的 MCP 服务连接信息",
|
||||
"agent_summary_source": "dws-agent-selection/mcp",
|
||||
"avoid_when": [
|
||||
"查询普通钉钉业务数据时使用对应产品命令,不要使用 mcp"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "解析和管理当前身份可用的 MCP 服务连接信息",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "解析和管理当前身份可用的 MCP 服务连接信息",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"查询普通钉钉业务数据时使用对应产品命令,不要使用 mcp"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"查询普通钉钉业务数据时使用对应产品命令,不要使用 mcp"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要把钉钉 MCP 市场中的服务连接到支持 Streamable HTTP 的 Agent 或客户端"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要把钉钉 MCP 市场中的服务连接到支持 Streamable HTTP 的 Agent 或客户端"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"source_refs": [
|
||||
"CommandRegistry:product=mcp",
|
||||
"cobra-help:dws mcp --help",
|
||||
"internal/cli/schema_command_registry.json#mcp",
|
||||
"internal/cli/schema_hints/selection/mcp.json"
|
||||
],
|
||||
"use_when": [
|
||||
"需要把钉钉 MCP 市场中的服务连接到支持 Streamable HTTP 的 Agent 或客户端"
|
||||
]
|
||||
},
|
||||
"minutes": {
|
||||
"agent_summary": "查询和维护钉钉听记的转写、摘要、待办、权限、录音、标签、说话人总结及文件上传会话。",
|
||||
"agent_summary_source": "dws-agent-selection/minutes",
|
||||
@@ -1673,7 +1605,6 @@
|
||||
"event",
|
||||
"live",
|
||||
"mail",
|
||||
"mcp",
|
||||
"minutes",
|
||||
"oa",
|
||||
"pat",
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"product_id": "mcp",
|
||||
"tools": {
|
||||
"mcp url get": {
|
||||
"agent_summary": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"agent_summary_source": "dws-agent-selection/mcp",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "agent-hint",
|
||||
"examples": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
}
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
]
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
}
|
||||
]
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
]
|
||||
},
|
||||
"examples": {
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
}
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "composite",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "composite",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_reason": {
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
|
||||
"candidates": [
|
||||
{
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
|
||||
}
|
||||
]
|
||||
},
|
||||
"reviewed": {
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
}
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"value": "medium",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "medium",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
]
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"idempotency": "idempotent",
|
||||
"interface_mode": "composite",
|
||||
"interface_reason": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"reviewed": true,
|
||||
"risk": "medium",
|
||||
"source_refs": [
|
||||
"cobra-help:dws mcp url get --help",
|
||||
"internal/app/mcp_url_command.go",
|
||||
"internal/cli/schema_command_registry.json#mcp.url_get",
|
||||
"internal/cli/schema_hints/metadata/mcp.json",
|
||||
"internal/cli/schema_hints/selection/mcp.json",
|
||||
"pkg/edition/default.go#openSupplementServers"
|
||||
],
|
||||
"use_when": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:0977e2b3d4af77d348318fd0cd7ce36d325ba9ca4e7c9ed0d6daeb1be0a0c4ec",
|
||||
"surface_hash": "sha256:e85148774f65d34b01d50ebdc702b785afdb8ea78663b6f2eeecc35bc3cb3fd5",
|
||||
"source_files": 152,
|
||||
"hint_files": 48,
|
||||
"hint_products": 40,
|
||||
"hint_tools": 1298,
|
||||
"source_hash": "sha256:5df496973c41b3b4f7ae8c856ff0e9e99bcabd4455419b7d41bb23c44df6f1af",
|
||||
"surface_hash": "sha256:7ef588f38052f0104e027c8daff5fefd68698781f2c87715461183d4058288ed",
|
||||
"source_files": 150,
|
||||
"hint_files": 46,
|
||||
"hint_products": 39,
|
||||
"hint_tools": 1296,
|
||||
"interface_metadata": {
|
||||
"source": "mcp-tools-list+cli-registry",
|
||||
"revision": "4574f7022c32cf4c033e9b7b4156e2fec815fed8",
|
||||
@@ -27,16 +27,16 @@
|
||||
]
|
||||
},
|
||||
"coverage": {
|
||||
"surface_products": 23,
|
||||
"products_with_metadata": 23,
|
||||
"surface_tools": 573,
|
||||
"tools_with_metadata": 573,
|
||||
"tools_with_agent_summary": 573,
|
||||
"tools_with_use_when": 573,
|
||||
"tools_with_avoid_when": 573,
|
||||
"tools_with_examples": 573,
|
||||
"tools_with_interface_mode": 573,
|
||||
"unmatched_skill_tools": 119,
|
||||
"surface_products": 22,
|
||||
"products_with_metadata": 22,
|
||||
"surface_tools": 572,
|
||||
"tools_with_metadata": 572,
|
||||
"tools_with_agent_summary": 572,
|
||||
"tools_with_use_when": 572,
|
||||
"tools_with_avoid_when": 572,
|
||||
"tools_with_examples": 572,
|
||||
"tools_with_interface_mode": 572,
|
||||
"unmatched_skill_tools": 120,
|
||||
"unreviewed_skill_tools": 7
|
||||
},
|
||||
"source_products": [
|
||||
@@ -60,7 +60,6 @@
|
||||
"generate-topic-report",
|
||||
"live",
|
||||
"mail",
|
||||
"mcp",
|
||||
"meeting-followup",
|
||||
"minutes",
|
||||
"oa",
|
||||
@@ -1505,10 +1504,38 @@
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "chat media upload",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 610,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
"chat category create-smart"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "chat media upload",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 611,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
"chat category create-smart"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "chat message list-emotion-replies",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1061,
|
||||
"line": 1086,
|
||||
"candidates": [
|
||||
"chat message add-emoji",
|
||||
"chat message add-favorite",
|
||||
@@ -1522,7 +1549,7 @@
|
||||
{
|
||||
"tool_path": "chat category create",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1416,
|
||||
"line": 1441,
|
||||
"candidates": [
|
||||
"chat group create",
|
||||
"chat category create-smart",
|
||||
@@ -1536,7 +1563,7 @@
|
||||
{
|
||||
"tool_path": "chat category delete",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1441,
|
||||
"line": 1466,
|
||||
"candidates": [
|
||||
"chat category create-smart",
|
||||
"chat category list",
|
||||
@@ -1550,7 +1577,7 @@
|
||||
{
|
||||
"tool_path": "chat category rename",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1452,
|
||||
"line": 1477,
|
||||
"candidates": [
|
||||
"chat group rename",
|
||||
"chat category create-smart",
|
||||
@@ -1564,7 +1591,7 @@
|
||||
{
|
||||
"tool_path": "chat category add-conv",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1464,
|
||||
"line": 1489,
|
||||
"candidates": [
|
||||
"chat category create-smart",
|
||||
"chat category list",
|
||||
@@ -1578,7 +1605,7 @@
|
||||
{
|
||||
"tool_path": "chat category remove-conv",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1477,
|
||||
"line": 1502,
|
||||
"candidates": [
|
||||
"chat category create-smart",
|
||||
"chat category list",
|
||||
@@ -1592,7 +1619,7 @@
|
||||
{
|
||||
"tool_path": "chat text translate",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1492,
|
||||
"line": 1517,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1606,7 +1633,7 @@
|
||||
{
|
||||
"tool_path": "chat text translate",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1493,
|
||||
"line": 1518,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1620,7 +1647,7 @@
|
||||
{
|
||||
"tool_path": "chat text translate",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1494,
|
||||
"line": 1519,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1634,7 +1661,7 @@
|
||||
{
|
||||
"tool_path": "chat hide",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1531,
|
||||
"line": 1556,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1648,7 +1675,7 @@
|
||||
{
|
||||
"tool_path": "chat hide",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1532,
|
||||
"line": 1557,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1662,7 +1689,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-at-all",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1554,
|
||||
"line": 1579,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1676,7 +1703,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-at-all",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1555,
|
||||
"line": 1580,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1690,7 +1717,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-red-envelope",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1577,
|
||||
"line": 1602,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1704,7 +1731,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-red-envelope",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1578,
|
||||
"line": 1603,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1718,7 +1745,7 @@
|
||||
{
|
||||
"tool_path": "chat mark-unread",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1598,
|
||||
"line": 1623,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1732,7 +1759,7 @@
|
||||
{
|
||||
"tool_path": "chat mark-unread",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1599,
|
||||
"line": 1624,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1746,7 +1773,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-red-point",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1617,
|
||||
"line": 1642,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1760,7 +1787,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-red-point",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1618,
|
||||
"line": 1643,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1774,7 +1801,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-all-red-point",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1634,
|
||||
"line": 1659,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1788,7 +1815,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-all-red-point",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1636,
|
||||
"line": 1661,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1802,7 +1829,7 @@
|
||||
{
|
||||
"tool_path": "chat list-all-conversations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1650,
|
||||
"line": 1675,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1816,7 +1843,7 @@
|
||||
{
|
||||
"tool_path": "chat list-all-conversations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1651,
|
||||
"line": 1676,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1830,7 +1857,7 @@
|
||||
{
|
||||
"tool_path": "chat list-all-conversations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1652,
|
||||
"line": 1677,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1844,7 +1871,7 @@
|
||||
{
|
||||
"tool_path": "chat list-all-conversations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1653,
|
||||
"line": 1678,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1858,7 +1885,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-messages",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1673,
|
||||
"line": 1698,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1872,7 +1899,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-messages",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1674,
|
||||
"line": 1699,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1886,7 +1913,7 @@
|
||||
{
|
||||
"tool_path": "chat mark-read",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1692,
|
||||
"line": 1717,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1900,7 +1927,7 @@
|
||||
{
|
||||
"tool_path": "chat mark-read",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1693,
|
||||
"line": 1718,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -1914,7 +1941,7 @@
|
||||
{
|
||||
"tool_path": "chat group list-join-validations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1736,
|
||||
"line": 1761,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -1928,7 +1955,7 @@
|
||||
{
|
||||
"tool_path": "chat group list-join-validations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1737,
|
||||
"line": 1762,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -1942,7 +1969,7 @@
|
||||
{
|
||||
"tool_path": "chat group list-join-validations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1738,
|
||||
"line": 1763,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -1956,7 +1983,7 @@
|
||||
{
|
||||
"tool_path": "chat group audit-join-validation",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1758,
|
||||
"line": 1783,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -1970,7 +1997,7 @@
|
||||
{
|
||||
"tool_path": "chat group audit-join-validation",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1759,
|
||||
"line": 1784,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -1984,7 +2011,7 @@
|
||||
{
|
||||
"tool_path": "chat group members",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1791,
|
||||
"line": 1816,
|
||||
"candidates": [
|
||||
"chat group members add",
|
||||
"chat group members add-bot",
|
||||
@@ -1998,7 +2025,7 @@
|
||||
{
|
||||
"tool_path": "chat group update-alias",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1796,
|
||||
"line": 1821,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -2012,7 +2039,7 @@
|
||||
{
|
||||
"tool_path": "chat group update-nick",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1797,
|
||||
"line": 1822,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -2026,7 +2053,7 @@
|
||||
{
|
||||
"tool_path": "chat group members list-by-ids",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1798,
|
||||
"line": 1823,
|
||||
"candidates": [
|
||||
"chat message list-by-ids",
|
||||
"chat group members add",
|
||||
@@ -2040,7 +2067,7 @@
|
||||
{
|
||||
"tool_path": "chat category create",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1822,
|
||||
"line": 1847,
|
||||
"candidates": [
|
||||
"chat group create",
|
||||
"chat category create-smart",
|
||||
@@ -2054,7 +2081,7 @@
|
||||
{
|
||||
"tool_path": "chat category delete",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1824,
|
||||
"line": 1849,
|
||||
"candidates": [
|
||||
"chat category create-smart",
|
||||
"chat category list",
|
||||
@@ -2068,7 +2095,7 @@
|
||||
{
|
||||
"tool_path": "chat category rename",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1825,
|
||||
"line": 1850,
|
||||
"candidates": [
|
||||
"chat group rename",
|
||||
"chat category create-smart",
|
||||
@@ -2082,7 +2109,7 @@
|
||||
{
|
||||
"tool_path": "chat category add-conv",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1826,
|
||||
"line": 1851,
|
||||
"candidates": [
|
||||
"chat category create-smart",
|
||||
"chat category list",
|
||||
@@ -2096,7 +2123,7 @@
|
||||
{
|
||||
"tool_path": "chat category remove-conv",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1827,
|
||||
"line": 1852,
|
||||
"candidates": [
|
||||
"chat category create-smart",
|
||||
"chat category list",
|
||||
@@ -2110,7 +2137,7 @@
|
||||
{
|
||||
"tool_path": "chat group share-invite",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1838,
|
||||
"line": 1863,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -2124,7 +2151,7 @@
|
||||
{
|
||||
"tool_path": "chat group notice create/edit/get/list",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1839,
|
||||
"line": 1864,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -2138,7 +2165,7 @@
|
||||
{
|
||||
"tool_path": "chat message list-emotion-replies",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1841,
|
||||
"line": 1866,
|
||||
"candidates": [
|
||||
"chat message add-emoji",
|
||||
"chat message add-favorite",
|
||||
@@ -2152,7 +2179,7 @@
|
||||
{
|
||||
"tool_path": "chat hide",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1848,
|
||||
"line": 1873,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2166,7 +2193,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-at-all",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1849,
|
||||
"line": 1874,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2180,7 +2207,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-at-all",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1850,
|
||||
"line": 1875,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2194,7 +2221,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-red-envelope",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1851,
|
||||
"line": 1876,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2208,7 +2235,7 @@
|
||||
{
|
||||
"tool_path": "chat mute-red-envelope",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1852,
|
||||
"line": 1877,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2222,7 +2249,7 @@
|
||||
{
|
||||
"tool_path": "chat mark-unread",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1853,
|
||||
"line": 1878,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2236,7 +2263,7 @@
|
||||
{
|
||||
"tool_path": "chat mark-read",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1854,
|
||||
"line": 1879,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2250,7 +2277,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-red-point",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1855,
|
||||
"line": 1880,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2264,7 +2291,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-all-red-point",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1856,
|
||||
"line": 1881,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2278,7 +2305,7 @@
|
||||
{
|
||||
"tool_path": "chat list-all-conversations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1857,
|
||||
"line": 1882,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2292,7 +2319,7 @@
|
||||
{
|
||||
"tool_path": "chat clear-messages",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1858,
|
||||
"line": 1883,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
@@ -2306,7 +2333,7 @@
|
||||
{
|
||||
"tool_path": "chat group list-join-validations",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1860,
|
||||
"line": 1885,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -2320,7 +2347,7 @@
|
||||
{
|
||||
"tool_path": "chat group audit-join-validation",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1861,
|
||||
"line": 1886,
|
||||
"candidates": [
|
||||
"chat group bots",
|
||||
"chat group create",
|
||||
@@ -2331,10 +2358,24 @@
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "chat media upload",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1893,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
"chat category create-smart"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "chat text translate",
|
||||
"source": "skills/mono/references/products/chat.md",
|
||||
"line": 1881,
|
||||
"line": 1905,
|
||||
"candidates": [
|
||||
"chat bot find",
|
||||
"chat bot search",
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:360114d03cc3e8dc6cef30c6e617cc40f88e5577c29dd5b5946fe1cde91eb9de",
|
||||
"surface_hash": "sha256:e85148774f65d34b01d50ebdc702b785afdb8ea78663b6f2eeecc35bc3cb3fd5",
|
||||
"source_hash": "sha256:14398788381c822f208e8cae059d98cd60da6625023869c74015735650d67e6f",
|
||||
"surface_hash": "sha256:7ef588f38052f0104e027c8daff5fefd68698781f2c87715461183d4058288ed",
|
||||
"catalog": {
|
||||
"agent_metadata": {
|
||||
"products_with_metadata": 23,
|
||||
"products_with_metadata": 22,
|
||||
"source": "embedded-skill-metadata",
|
||||
"source_hash": "sha256:0977e2b3d4af77d348318fd0cd7ce36d325ba9ca4e7c9ed0d6daeb1be0a0c4ec",
|
||||
"surface_hash": "sha256:e85148774f65d34b01d50ebdc702b785afdb8ea78663b6f2eeecc35bc3cb3fd5",
|
||||
"surface_products": 23,
|
||||
"surface_tools": 573,
|
||||
"tools_with_agent_summary": 573,
|
||||
"tools_with_metadata": 573,
|
||||
"unmatched_skill_tools": 119,
|
||||
"source_hash": "sha256:5df496973c41b3b4f7ae8c856ff0e9e99bcabd4455419b7d41bb23c44df6f1af",
|
||||
"surface_hash": "sha256:7ef588f38052f0104e027c8daff5fefd68698781f2c87715461183d4058288ed",
|
||||
"surface_products": 22,
|
||||
"surface_tools": 572,
|
||||
"tools_with_agent_summary": 572,
|
||||
"tools_with_metadata": 572,
|
||||
"unmatched_skill_tools": 120,
|
||||
"version": 1
|
||||
},
|
||||
"count": 23,
|
||||
"count": 22,
|
||||
"interface_metadata": {
|
||||
"coverage": {
|
||||
"aliased_tools": 178,
|
||||
@@ -7660,7 +7660,7 @@
|
||||
"cli_name": "send",
|
||||
"cli_path": "chat message send",
|
||||
"confirmation": "not_required",
|
||||
"description": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n本地图片 / 文件消息:\n 统一使用 --msg-type file --file-path \u003c本地路径\u003e。CLI 会完成上传并按 file 消息发送;\n .png/.jpg 也会显示为可下载的文件附件,不会生成 mediaId 或渲染成内联图片。\n\n旧版内联图片消息:\n 仅当上游已经提供有效 mediaId 时,使用 --msg-type image --media-id。\n 当前 CLI 不提供本地文件到 mediaId 的上传能力。",
|
||||
"description": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n富媒体消息(通过 --msg-type 指定类型):\n image — 发送图片:--msg-type image --media-id(通过 dt_media_upload 上传获得)\n file/audio/video — 发送文件、音频、视频:传本地 --file-path,CLI 会上传后按 file 消息发送",
|
||||
"effect": "write",
|
||||
"group": "message",
|
||||
"idempotency": "unknown",
|
||||
@@ -13476,110 +13476,6 @@
|
||||
"查收、搜索、阅读、回复、发送或整理邮件"
|
||||
]
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_source_refs": [
|
||||
"CommandRegistry:product=mcp",
|
||||
"cobra-help:dws mcp --help",
|
||||
"internal/cli/schema_command_registry.json#mcp",
|
||||
"internal/cli/schema_hints/selection/mcp.json"
|
||||
],
|
||||
"agent_summary": "解析和管理当前身份可用的 MCP 服务连接信息",
|
||||
"agent_summary_source": "dws-agent-selection/mcp",
|
||||
"avoid_when": [
|
||||
"查询普通钉钉业务数据时使用对应产品命令,不要使用 mcp"
|
||||
],
|
||||
"description": "管理 MCP 服务连接信息",
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": "解析和管理当前身份可用的 MCP 服务连接信息"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": "解析和管理当前身份可用的 MCP 服务连接信息"
|
||||
},
|
||||
"avoid_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"查询普通钉钉业务数据时使用对应产品命令,不要使用 mcp"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"查询普通钉钉业务数据时使用对应产品命令,不要使用 mcp"
|
||||
]
|
||||
},
|
||||
"use_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"需要把钉钉 MCP 市场中的服务连接到支持 Streamable HTTP 的 Agent 或客户端"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"需要把钉钉 MCP 市场中的服务连接到支持 Streamable HTTP 的 Agent 或客户端"
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "mcp",
|
||||
"name": "管理 MCP 服务连接信息",
|
||||
"runtime": true,
|
||||
"tool_count": 1,
|
||||
"tools": [
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"agent_summary_source": "dws-agent-selection/mcp",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"canonical_path": "mcp.url_get",
|
||||
"cli_name": "get",
|
||||
"cli_path": "mcp url get",
|
||||
"confirmation": "not_required",
|
||||
"description": "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 Streamable HTTP 服务地址。\n\n安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用,请勿分享到群聊、文档、邮件、代码仓库或日志。",
|
||||
"effect": "read",
|
||||
"group": "url",
|
||||
"idempotency": "idempotent",
|
||||
"interface_mode": "composite",
|
||||
"interface_reason": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"name": "url_get",
|
||||
"primary_cli_path": "mcp url get",
|
||||
"reviewed": true,
|
||||
"risk": "medium",
|
||||
"title": "按 mcpId 获取 MCP 的 Streamable HTTP 服务地址",
|
||||
"use_when": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
]
|
||||
}
|
||||
],
|
||||
"use_when": [
|
||||
"需要把钉钉 MCP 市场中的服务连接到支持 Streamable HTTP 的 Agent 或客户端"
|
||||
]
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_source_refs": [
|
||||
@@ -19257,7 +19153,7 @@
|
||||
}
|
||||
],
|
||||
"source": "embedded-command-catalog",
|
||||
"tool_count": 573
|
||||
"tool_count": 572
|
||||
},
|
||||
"tools": {
|
||||
"aisearch.enterprise_person_search": {
|
||||
@@ -196587,7 +196483,7 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
"description": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n本地图片 / 文件消息:\n 统一使用 --msg-type file --file-path \u003c本地路径\u003e。CLI 会完成上传并按 file 消息发送;\n .png/.jpg 也会显示为可下载的文件附件,不会生成 mediaId 或渲染成内联图片。\n\n旧版内联图片消息:\n 仅当上游已经提供有效 mediaId 时,使用 --msg-type image --media-id。\n 当前 CLI 不提供本地文件到 mediaId 的上传能力。",
|
||||
"description": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n富媒体消息(通过 --msg-type 指定类型):\n image — 发送图片:--msg-type image --media-id(通过 dt_media_upload 上传获得)\n file/audio/video — 发送文件、音频、视频:传本地 --file-path,CLI 会上传后按 file 消息发送",
|
||||
"display": "群聊 / 消息 / 机器人",
|
||||
"effect": "write",
|
||||
"effect_source": "agent-hint",
|
||||
@@ -196704,7 +196600,7 @@
|
||||
"precedence": "cobra_help",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"value": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n本地图片 / 文件消息:\n 统一使用 --msg-type file --file-path \u003c本地路径\u003e。CLI 会完成上传并按 file 消息发送;\n .png/.jpg 也会显示为可下载的文件附件,不会生成 mediaId 或渲染成内联图片。\n\n旧版内联图片消息:\n 仅当上游已经提供有效 mediaId 时,使用 --msg-type image --media-id。\n 当前 CLI 不提供本地文件到 mediaId 的上传能力。"
|
||||
"value": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n富媒体消息(通过 --msg-type 指定类型):\n image — 发送图片:--msg-type image --media-id(通过 dt_media_upload 上传获得)\n file/audio/video — 发送文件、音频、视频:传本地 --file-path,CLI 会上传后按 file 消息发送"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
@@ -196716,7 +196612,7 @@
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"value": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n本地图片 / 文件消息:\n 统一使用 --msg-type file --file-path \u003c本地路径\u003e。CLI 会完成上传并按 file 消息发送;\n .png/.jpg 也会显示为可下载的文件附件,不会生成 mediaId 或渲染成内联图片。\n\n旧版内联图片消息:\n 仅当上游已经提供有效 mediaId 时,使用 --msg-type image --media-id。\n 当前 CLI 不提供本地文件到 mediaId 的上传能力。"
|
||||
"value": "以当前用户身份发送消息。\n\n⚠️ 重要:该接口会真实发送消息到目标会话,不可用于测试或试探性调用。调用前必须确认消息内容和接收对象无误。\n\n目标选择(三选一,必填):\n --group 群聊 openconversation_id\n --user 单聊接收人 userId\n --open-dingtalk-id 单聊接收人 openDingTalkId\n\n纯文本 / Markdown 消息(默认):\n 无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。\n\n富媒体消息(通过 --msg-type 指定类型):\n image — 发送图片:--msg-type image --media-id(通过 dt_media_upload 上传获得)\n file/audio/video — 发送文件、音频、视频:传本地 --file-path,CLI 会上传后按 file 消息发送"
|
||||
},
|
||||
"effect": {
|
||||
"candidates": [
|
||||
@@ -197280,7 +197176,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"file-path": {
|
||||
"description": "本地文件路径(msgType=file/audio/video 时直接上传并按 file 消息发送)",
|
||||
"description": "本地文件路径(msgType=file 时可直接上传发送)",
|
||||
"field_provenance": {
|
||||
"description": {
|
||||
"candidates": [
|
||||
@@ -197288,7 +197184,7 @@
|
||||
"precedence": "cobra_contract",
|
||||
"selected": true,
|
||||
"source": "cobra_usage",
|
||||
"value": "本地文件路径(msgType=file/audio/video 时直接上传并按 file 消息发送)"
|
||||
"value": "本地文件路径(msgType=file 时可直接上传发送)"
|
||||
},
|
||||
{
|
||||
"precedence": "default",
|
||||
@@ -197300,7 +197196,7 @@
|
||||
"precedence": "cobra_contract",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_usage",
|
||||
"value": "本地文件路径(msgType=file/audio/video 时直接上传并按 file 消息发送)"
|
||||
"value": "本地文件路径(msgType=file 时可直接上传发送)"
|
||||
},
|
||||
"format": {
|
||||
"candidates": [
|
||||
@@ -197502,7 +197398,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"media-id": {
|
||||
"description": "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)",
|
||||
"description": "图片 mediaId(通过 dt_media_upload 上传后用 extract_media_id.py 提取,仅 msgType=image)",
|
||||
"example": "@lADP_schema_smoke",
|
||||
"field_provenance": {
|
||||
"description": {
|
||||
@@ -197511,7 +197407,7 @@
|
||||
"precedence": "cobra_contract",
|
||||
"selected": true,
|
||||
"source": "cobra_usage",
|
||||
"value": "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)"
|
||||
"value": "图片 mediaId(通过 dt_media_upload 上传后用 extract_media_id.py 提取,仅 msgType=image)"
|
||||
},
|
||||
{
|
||||
"precedence": "default",
|
||||
@@ -197523,7 +197419,7 @@
|
||||
"precedence": "cobra_contract",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_usage",
|
||||
"value": "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)"
|
||||
"value": "图片 mediaId(通过 dt_media_upload 上传后用 extract_media_id.py 提取,仅 msgType=image)"
|
||||
},
|
||||
"example": {
|
||||
"candidates": [
|
||||
@@ -197621,7 +197517,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"msg-type": {
|
||||
"description": "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)",
|
||||
"description": "富媒体消息类型: image/file/audio/video(audio/video 是 file 别名;纯文本/Markdown 无需指定,直接传内容即可)",
|
||||
"enum": [
|
||||
"image",
|
||||
"file",
|
||||
@@ -197635,7 +197531,7 @@
|
||||
"precedence": "cobra_contract",
|
||||
"selected": true,
|
||||
"source": "cobra_usage",
|
||||
"value": "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)"
|
||||
"value": "富媒体消息类型: image/file/audio/video(audio/video 是 file 别名;纯文本/Markdown 无需指定,直接传内容即可)"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
@@ -197653,7 +197549,7 @@
|
||||
"precedence": "cobra_contract",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_usage",
|
||||
"value": "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)"
|
||||
"value": "富媒体消息类型: image/file/audio/video(audio/video 是 file 别名;纯文本/Markdown 无需指定,直接传内容即可)"
|
||||
},
|
||||
"enum": {
|
||||
"candidates": [
|
||||
@@ -319879,338 +319775,6 @@
|
||||
"已知 templateId 并需要修改名称、主题、正文或收件人时"
|
||||
]
|
||||
},
|
||||
"mcp.url_get": {
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_source_refs": [
|
||||
"cobra-help:dws mcp url get --help",
|
||||
"internal/app/mcp_url_command.go",
|
||||
"internal/cli/schema_command_registry.json#mcp.url_get",
|
||||
"internal/cli/schema_hints/metadata/mcp.json",
|
||||
"internal/cli/schema_hints/selection/mcp.json",
|
||||
"pkg/edition/default.go#openSupplementServers"
|
||||
],
|
||||
"agent_summary": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"agent_summary_source": "dws-agent-selection/mcp",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"canonical_path": "mcp.url_get",
|
||||
"cli_name": "get",
|
||||
"cli_path": "mcp url get",
|
||||
"confirmation": "not_required",
|
||||
"description": "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 Streamable HTTP 服务地址。\n\n安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用,请勿分享到群聊、文档、邮件、代码仓库或日志。",
|
||||
"display": "管理 MCP 服务连接信息",
|
||||
"effect": "read",
|
||||
"effect_source": "agent-hint",
|
||||
"examples": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "available"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "available"
|
||||
},
|
||||
"avoid_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "command_registry",
|
||||
"selected": true,
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "mcp url get",
|
||||
"value": "mcp.url_get"
|
||||
}
|
||||
],
|
||||
"precedence": "command_registry",
|
||||
"resolution": "registry_identity",
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "mcp url get",
|
||||
"value": "mcp.url_get"
|
||||
},
|
||||
"confirmation": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "not_required"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "not_required"
|
||||
},
|
||||
"description": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"value": "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 Streamable HTTP 服务地址。\n\n安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用,请勿分享到群聊、文档、邮件、代码仓库或日志。"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"value": "输入 MCP 市场 mcpId,返回以当前用户和组织身份访问该 MCP 的 Streamable HTTP 服务地址。\n\n安全提示:返回的 mcpURL 和 mcpJSON 可能包含身份凭据,仅限个人使用,请勿分享到群聊、文档、邮件、代码仓库或日志。"
|
||||
},
|
||||
"effect": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "read"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "read"
|
||||
},
|
||||
"examples": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
]
|
||||
},
|
||||
"idempotency": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "idempotent"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "idempotent"
|
||||
},
|
||||
"interface_mode": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "composite"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "composite"
|
||||
},
|
||||
"interface_reason": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata."
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata."
|
||||
},
|
||||
"interface_ref": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": null
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": null
|
||||
},
|
||||
"reviewed": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": true
|
||||
},
|
||||
"risk": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "medium"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output.",
|
||||
"source": "internal/cli/schema_hints/metadata/mcp.json",
|
||||
"value": "medium"
|
||||
},
|
||||
"title": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_help",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"value": "按 mcpId 获取 MCP 的 Streamable HTTP 服务地址"
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"value": "按 mcpId 获取 MCP 的 Streamable HTTP 服务地址"
|
||||
},
|
||||
"use_when": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source": "internal/cli/schema_hints/selection/mcp.json",
|
||||
"value": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
]
|
||||
}
|
||||
},
|
||||
"group": "url",
|
||||
"has_parameters": false,
|
||||
"idempotency": "idempotent",
|
||||
"interface_mode": "composite",
|
||||
"interface_reason": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"is_alias": false,
|
||||
"name": "url_get",
|
||||
"parameter_count": 0,
|
||||
"parameters": {},
|
||||
"path": "mcp.url_get",
|
||||
"positionals": [
|
||||
{
|
||||
"description": "钉钉 MCP 市场中的 mcpId",
|
||||
"index": 0,
|
||||
"name": "mcp_id",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"primary_cli_path": "mcp url get",
|
||||
"product_id": "mcp",
|
||||
"reviewed": true,
|
||||
"risk": "medium",
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "按 mcpId 获取 MCP 的 Streamable HTTP 服务地址",
|
||||
"use_when": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
]
|
||||
},
|
||||
"minutes.add_member_permission": {
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_source_refs": [
|
||||
|
||||
@@ -321,6 +321,7 @@
|
||||
"chat list-all-conversations",
|
||||
"chat mark-read",
|
||||
"chat mark-unread",
|
||||
"chat media upload",
|
||||
"chat message list-emotion-replies",
|
||||
"chat message set-top-msg",
|
||||
"chat message unset-top-msg",
|
||||
|
||||
@@ -1700,15 +1700,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mcp",
|
||||
"tools": [
|
||||
{
|
||||
"canonical_path": "mcp.url_get",
|
||||
"cli_path": "mcp url get"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "minutes",
|
||||
"tools": [
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
"event": "metadata/event.json",
|
||||
"live": "metadata/live.json",
|
||||
"mail": "metadata/mail.json",
|
||||
"mcp": "metadata/mcp.json",
|
||||
"minutes": "metadata/minutes.json",
|
||||
"oa": "metadata/oa.json",
|
||||
"pat": "metadata/pat.json",
|
||||
@@ -48,7 +47,6 @@
|
||||
"event": "selection/event.json",
|
||||
"live": "selection/live.json",
|
||||
"mail": "selection/mail.json",
|
||||
"mcp": "selection/mcp.json",
|
||||
"minutes": "selection/minutes.json",
|
||||
"oa": "selection/oa.json",
|
||||
"pat": "selection/pat.json",
|
||||
@@ -436,6 +434,10 @@
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
},
|
||||
"chat media upload": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
},
|
||||
"chat message list-emotion-replies": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source": {
|
||||
"kind": "explicit",
|
||||
"name": "dws-tool-metadata/mcp",
|
||||
"repository": "DingTalk-Real-AI/dingtalk-workspace-cli",
|
||||
"channel": "open-source",
|
||||
"reviewed": true
|
||||
},
|
||||
"tools": {
|
||||
"mcp.url_get": {
|
||||
"effect": "read",
|
||||
"risk": "medium",
|
||||
"confirmation": "not_required",
|
||||
"idempotency": "idempotent",
|
||||
"interface_mode": "composite",
|
||||
"availability": "available",
|
||||
"interface_reason": "Reviewed unpinned remote adapter: the public CLI wrapper calls the helper-only mcp-meta/get_mcp_server_url endpoint, which is intentionally absent from the public product catalog and pinned MCP metadata.",
|
||||
"runtime_gate": "none",
|
||||
"reviewed": true,
|
||||
"review_reason": "Expose the reviewed MCP URL resolver through the public CLI and Agent Schema while preserving the helper endpoint as a non-product supplemental server. The returned URL contains user- and organization-scoped credentials and must be treated as sensitive output."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,6 +385,10 @@
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
},
|
||||
"chat media upload": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
},
|
||||
"chat message list-emotion-replies": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"channel": "open-source"
|
||||
},
|
||||
"coverage": {
|
||||
"source_tools": 573,
|
||||
"source_tools": 572,
|
||||
"matched_tools": 71
|
||||
},
|
||||
"tools": {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source": {
|
||||
"kind": "explicit",
|
||||
"name": "dws-agent-selection/mcp",
|
||||
"repository": "DingTalk-Real-AI/dingtalk-workspace-cli",
|
||||
"channel": "open-source",
|
||||
"reviewed": true
|
||||
},
|
||||
"tools": {
|
||||
"mcp.url_get": {
|
||||
"agent_summary": "按 MCP 市场 mcpId 获取当前用户和组织可用的 Streamable HTTP 地址",
|
||||
"use_when": [
|
||||
"已知钉钉 MCP 市场 mcpId,需要获得当前身份可用的 Streamable HTTP 连接地址"
|
||||
],
|
||||
"avoid_when": [
|
||||
"只是查询 DWS 已公开命令或参数时使用 dws schema",
|
||||
"用户要求把返回的凭据 URL 发送到群聊、文档、邮件、日志或代码仓库时不要执行或传播"
|
||||
],
|
||||
"examples": [
|
||||
"dws mcp url get 10043 --format json"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "正式公开 MCP URL 解析命令的 Agent 选型文案,并明确凭据 URL 只能返回给当前用户、不得二次传播。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#mcp.url_get",
|
||||
"cobra-help:dws mcp url get --help",
|
||||
"internal/app/mcp_url_command.go",
|
||||
"pkg/edition/default.go#openSupplementServers"
|
||||
]
|
||||
}
|
||||
},
|
||||
"products": {
|
||||
"mcp": {
|
||||
"agent_summary": "解析和管理当前身份可用的 MCP 服务连接信息",
|
||||
"use_when": [
|
||||
"需要把钉钉 MCP 市场中的服务连接到支持 Streamable HTTP 的 Agent 或客户端"
|
||||
],
|
||||
"avoid_when": [
|
||||
"查询普通钉钉业务数据时使用对应产品命令,不要使用 mcp"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "为公开 mcp 命令提供产品级 Agent 路由,并强调连接信息属于敏感凭据。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#mcp",
|
||||
"cobra-help:dws mcp --help",
|
||||
"CommandRegistry:product=mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-16
@@ -1433,25 +1433,19 @@ func newChatCommand() *cobra.Command {
|
||||
纯文本 / Markdown 消息(默认):
|
||||
无需指定 --msg-type,直接传消息内容即可。推荐使用 --text flag 传递内容(尤其当内容含换行、引号等特殊字符时),也支持位置参数。可选 --title 作为消息标题。
|
||||
|
||||
本地图片 / 文件消息:
|
||||
统一使用 --msg-type file --file-path <本地路径>。CLI 会完成上传并按 file 消息发送;
|
||||
.png/.jpg 也会显示为可下载的文件附件,不会生成 mediaId 或渲染成内联图片。
|
||||
|
||||
旧版内联图片消息:
|
||||
仅当上游已经提供有效 mediaId 时,使用 --msg-type image --media-id。
|
||||
当前 CLI 不提供本地文件到 mediaId 的上传能力。`,
|
||||
富媒体消息(通过 --msg-type 指定类型):
|
||||
image — 发送图片:--msg-type image --media-id(通过 dt_media_upload 上传获得)
|
||||
file/audio/video — 发送文件、音频、视频:传本地 --file-path,CLI 会上传后按 file 消息发送`,
|
||||
Example: ` dws chat message send --group <openconversation_id> "hello"
|
||||
dws chat message send --user <userId> "请查收"
|
||||
dws chat message send --open-dingtalk-id <openDingTalkId> "请查收"
|
||||
dws chat message send --group <openconversation_id> --title "周报提醒" "请大家本周五前提交周报"
|
||||
# 发送本地图片或文件(图片会作为可下载的 file 附件发送)
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./screenshot.png
|
||||
# 发送图片
|
||||
dws chat message send --group <openconversation_id> --msg-type image --media-id <mediaId>
|
||||
# 发送本地文件/音频/视频(audio/video 是 file 的语义别名)
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./report.pdf
|
||||
# 发送本地音频/视频(audio/video 是 file 的语义别名)
|
||||
dws chat message send --group <openconversation_id> --msg-type audio --file-path ./recording.mp3
|
||||
dws chat message send --group <openconversation_id> --msg-type video --file-path ./demo.mp4
|
||||
# 旧版内联图片:仅当上游已经持有有效 mediaId 时使用
|
||||
dws chat message send --group <openconversation_id> --msg-type image --media-id <mediaId>
|
||||
# 查询群 ID: dws chat search --query "群名"
|
||||
# 查询用户 ID: dws contact user search --query "姓名"`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
@@ -2406,13 +2400,13 @@ func newChatCommand() *cobra.Command {
|
||||
_ = chatMessageSendCmd.Flags().MarkHidden("markdown")
|
||||
chatMessageSendCmd.Flags().Bool("at-all", false, "@所有人(仅群聊时生效,可选),设置时,消息内容中一定要包含对应的占位符<@all>")
|
||||
chatMessageSendCmd.Flags().String("at-open-dingtalk-ids", "", "@指定成员的 openDingTalkId 列表,逗号分隔(仅群聊时生效,可选),设置--at-open-dingtalk-ids openDingTalkId1,openDingTalkId2时,消息内容中一定要包含对应格式的占位符<@openDingTalkId1> <@openDingTalkId2>")
|
||||
chatMessageSendCmd.Flags().String("media-id", "", "上游已提供的图片 mediaId(仅旧版 msgType=image;CLI 不提供本地上传到 mediaId)")
|
||||
chatMessageSendCmd.Flags().String("msg-type", "", "富媒体消息类型: image/file/audio/video(本地图片/文件推荐 file --file-path;image 仅接受已有 mediaId)")
|
||||
chatMessageSendCmd.Flags().String("media-id", "", "图片 mediaId(通过 dt_media_upload 上传后用 extract_media_id.py 提取,仅 msgType=image)")
|
||||
chatMessageSendCmd.Flags().String("msg-type", "", "富媒体消息类型: image/file/audio/video(audio/video 是 file 别名;纯文本/Markdown 无需指定,直接传内容即可)")
|
||||
chatMessageSendCmd.Flags().Int64("dentry-id", 0, "文件 dentryId(与 --space-id 成对传入时跳过自动上传)")
|
||||
chatMessageSendCmd.Flags().Int64("space-id", 0, "空间 ID(与 --dentry-id 成对传入时跳过自动上传)")
|
||||
chatMessageSendCmd.Flags().String("file-name", "", "文件名")
|
||||
chatMessageSendCmd.Flags().String("file-type", "", "文件类型/扩展名")
|
||||
chatMessageSendCmd.Flags().String("file-path", "", "本地文件路径(msgType=file/audio/video 时直接上传并按 file 消息发送)")
|
||||
chatMessageSendCmd.Flags().String("file-path", "", "本地文件路径(msgType=file 时可直接上传发送)")
|
||||
chatMessageSendCmd.Flags().Int64("file-size", 0, "文件大小,单位字节")
|
||||
_ = chatMessageSendCmd.Flags().MarkHidden("dentry-id")
|
||||
_ = chatMessageSendCmd.Flags().MarkHidden("space-id")
|
||||
@@ -3393,7 +3387,7 @@ flow-status 取值:1=处理中(PROCESSING),2=输入中(INPUTTING),3=完成
|
||||
}
|
||||
iconMediaID := strings.TrimSpace(mustGetFlag(cmd, "icon-media-id"))
|
||||
if iconMediaID == "" {
|
||||
return fmt.Errorf("invalid --icon-media-id: mediaId 不能为空\n hint: 请使用上游媒体上传能力返回的有效 mediaId;DWS CLI 不提供本地文件到 mediaId 的上传命令")
|
||||
return fmt.Errorf("invalid --icon-media-id: mediaId 不能为空\n hint: 先通过媒体上传命令(dt_media_upload)上传图片,使用返回的 mediaId")
|
||||
}
|
||||
return callMCPToolOnServer("im", "update_group_icon", map[string]any{
|
||||
"openConversationId": mustGetFlag(cmd, "group"),
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
)
|
||||
|
||||
type chatFilePathCall struct {
|
||||
server string
|
||||
tool string
|
||||
args map[string]any
|
||||
}
|
||||
|
||||
type chatFilePathCaller struct {
|
||||
sequence []string
|
||||
calls []chatFilePathCall
|
||||
}
|
||||
|
||||
func (c *chatFilePathCaller) CallTool(_ context.Context, server, tool string, args map[string]any) (*edition.ToolResult, error) {
|
||||
c.sequence = append(c.sequence, tool)
|
||||
copied := make(map[string]any, len(args))
|
||||
for key, value := range args {
|
||||
copied[key] = value
|
||||
}
|
||||
c.calls = append(c.calls, chatFilePathCall{server: server, tool: tool, args: copied})
|
||||
|
||||
switch tool {
|
||||
case "init_conversation_file_upload":
|
||||
return textToolResult(`{"resourceUrl":"https://upload.example/file","uploadKey":"upload-key","headers":{"x-upload":"yes"}}`), nil
|
||||
case "commit_conversation_file_upload":
|
||||
return textToolResult(`{"result":{"dentryId":123,"spaceId":456}}`), nil
|
||||
case "send_personal_message":
|
||||
return textToolResult(`{"success":true}`), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected tool call %s/%s", server, tool)
|
||||
}
|
||||
}
|
||||
|
||||
func (*chatFilePathCaller) Format() string { return "json" }
|
||||
func (*chatFilePathCaller) DryRun() bool { return false }
|
||||
func (*chatFilePathCaller) Fields() string { return "" }
|
||||
func (*chatFilePathCaller) JQ() string { return "" }
|
||||
|
||||
func TestChatMessageSendFilePathUsesWukongUploadSequence(t *testing.T) {
|
||||
previousDeps, previousPut, previousArgs := deps, httpPutFile, os.Args
|
||||
t.Cleanup(func() {
|
||||
deps = previousDeps
|
||||
httpPutFile = previousPut
|
||||
os.Args = previousArgs
|
||||
})
|
||||
|
||||
filePath := filepath.Join(t.TempDir(), "image.png")
|
||||
payload := []byte("png payload")
|
||||
if err := os.WriteFile(filePath, payload, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
caller := &chatFilePathCaller{}
|
||||
commandArgs := []string{
|
||||
"message", "send",
|
||||
"--group=cid",
|
||||
"--msg-type=file",
|
||||
"--file-path=" + filePath,
|
||||
}
|
||||
os.Args = append([]string{"dws", "chat"}, commandArgs...)
|
||||
httpPutFile = func(_ context.Context, resourceURL string, headers map[string]string, localPath string, fileSize int64) error {
|
||||
caller.sequence = append(caller.sequence, "HTTP PUT")
|
||||
if resourceURL != "https://upload.example/file" {
|
||||
t.Fatalf("resourceURL = %q", resourceURL)
|
||||
}
|
||||
if headers["x-upload"] != "yes" {
|
||||
t.Fatalf("headers = %#v", headers)
|
||||
}
|
||||
if localPath != filePath || fileSize != int64(len(payload)) {
|
||||
t.Fatalf("upload file = %q (%d), want %q (%d)", localPath, fileSize, filePath, len(payload))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := runChatCoverageCommand(t, caller, commandArgs...)
|
||||
if err != nil {
|
||||
t.Fatalf("chat message send --file-path: %v", err)
|
||||
}
|
||||
|
||||
wantSequence := []string{
|
||||
"init_conversation_file_upload",
|
||||
"HTTP PUT",
|
||||
"commit_conversation_file_upload",
|
||||
"send_personal_message",
|
||||
}
|
||||
if !reflect.DeepEqual(caller.sequence, wantSequence) {
|
||||
t.Fatalf("call sequence = %#v, want %#v", caller.sequence, wantSequence)
|
||||
}
|
||||
if len(caller.calls) != 3 {
|
||||
t.Fatalf("tool calls = %#v, want init, commit, send", caller.calls)
|
||||
}
|
||||
|
||||
fileMD5 := fmt.Sprintf("%x", md5.Sum(payload))
|
||||
wantInit := chatFilePathCall{
|
||||
server: "im",
|
||||
tool: "init_conversation_file_upload",
|
||||
args: map[string]any{
|
||||
"openConversationId": "cid",
|
||||
"fileName": "image.png",
|
||||
"fileSize": int64(len(payload)),
|
||||
"md5": fileMD5,
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(caller.calls[0], wantInit) {
|
||||
t.Fatalf("init call = %#v, want %#v", caller.calls[0], wantInit)
|
||||
}
|
||||
wantCommit := chatFilePathCall{
|
||||
server: "im",
|
||||
tool: "commit_conversation_file_upload",
|
||||
args: map[string]any{
|
||||
"openConversationId": "cid",
|
||||
"uploadKey": "upload-key",
|
||||
"fileName": "image.png",
|
||||
"fileSize": int64(len(payload)),
|
||||
"md5": fileMD5,
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(caller.calls[1], wantCommit) {
|
||||
t.Fatalf("commit call = %#v, want %#v", caller.calls[1], wantCommit)
|
||||
}
|
||||
|
||||
send := caller.calls[len(caller.calls)-1]
|
||||
if send.server != "chat" || send.tool != "send_personal_message" || send.args["msgType"] != "file" {
|
||||
t.Fatalf("send call = %#v", send)
|
||||
}
|
||||
if send.args["openConversationId"] != "cid" {
|
||||
t.Fatalf("send target = %#v", send.args["openConversationId"])
|
||||
}
|
||||
content, ok := send.args["content"].(string)
|
||||
if !ok {
|
||||
t.Fatalf("send content = %#v", send.args["content"])
|
||||
}
|
||||
var parsed struct {
|
||||
DentryID int64 `json:"dentryId"`
|
||||
SpaceID int64 `json:"spaceId"`
|
||||
FileName string `json:"fileName"`
|
||||
FileType string `json:"fileType"`
|
||||
FilePath string `json:"filePath"`
|
||||
FileSize int64 `json:"fileSize"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(content), &parsed); err != nil {
|
||||
t.Fatalf("decode send content %q: %v", content, err)
|
||||
}
|
||||
if parsed.DentryID != 123 || parsed.SpaceID != 456 ||
|
||||
parsed.FileName != "image.png" || parsed.FileType != "png" ||
|
||||
parsed.FilePath != "/image.png" || parsed.FileSize != int64(len(payload)) {
|
||||
t.Fatalf("send content = %#v", parsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatMessageSendFilePathUsesOpenDingTalkIDTarget(t *testing.T) {
|
||||
previousDeps, previousPut, previousArgs := deps, httpPutFile, os.Args
|
||||
t.Cleanup(func() {
|
||||
deps = previousDeps
|
||||
httpPutFile = previousPut
|
||||
os.Args = previousArgs
|
||||
})
|
||||
|
||||
filePath := filepath.Join(t.TempDir(), "report.pdf")
|
||||
payload := []byte("pdf payload")
|
||||
if err := os.WriteFile(filePath, payload, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
caller := &chatFilePathCaller{}
|
||||
commandArgs := []string{
|
||||
"message", "send",
|
||||
"--open-dingtalk-id=D-target",
|
||||
"--msg-type=file",
|
||||
"--file-path=" + filePath,
|
||||
}
|
||||
os.Args = append([]string{"dws", "chat"}, commandArgs...)
|
||||
httpPutFile = func(_ context.Context, resourceURL string, _ map[string]string, localPath string, fileSize int64) error {
|
||||
caller.sequence = append(caller.sequence, "HTTP PUT")
|
||||
if resourceURL != "https://upload.example/file" || localPath != filePath || fileSize != int64(len(payload)) {
|
||||
t.Fatalf("upload = %q, %q (%d)", resourceURL, localPath, fileSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := runChatCoverageCommand(t, caller, commandArgs...); err != nil {
|
||||
t.Fatalf("chat message send --open-dingtalk-id --file-path: %v", err)
|
||||
}
|
||||
if len(caller.calls) != 3 {
|
||||
t.Fatalf("tool calls = %#v, want init, commit, send", caller.calls)
|
||||
}
|
||||
|
||||
fileMD5 := fmt.Sprintf("%x", md5.Sum(payload))
|
||||
wantInit := chatFilePathCall{
|
||||
server: "im",
|
||||
tool: "init_conversation_file_upload",
|
||||
args: map[string]any{
|
||||
"openDingTalkId": "D-target",
|
||||
"fileName": "report.pdf",
|
||||
"fileSize": int64(len(payload)),
|
||||
"md5": fileMD5,
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(caller.calls[0], wantInit) {
|
||||
t.Fatalf("init direct target call = %#v, want %#v", caller.calls[0], wantInit)
|
||||
}
|
||||
wantCommit := chatFilePathCall{
|
||||
server: "im",
|
||||
tool: "commit_conversation_file_upload",
|
||||
args: map[string]any{
|
||||
"openDingTalkId": "D-target",
|
||||
"uploadKey": "upload-key",
|
||||
"fileName": "report.pdf",
|
||||
"fileSize": int64(len(payload)),
|
||||
"md5": fileMD5,
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(caller.calls[1], wantCommit) {
|
||||
t.Fatalf("commit direct target call = %#v, want %#v", caller.calls[1], wantCommit)
|
||||
}
|
||||
|
||||
send := caller.calls[2]
|
||||
if send.server != "chat" || send.tool != "send_personal_message" || send.args["msgType"] != "file" {
|
||||
t.Fatalf("send direct target call = %#v", send)
|
||||
}
|
||||
if send.args["receiverOpenDingTalkId"] != "D-target" {
|
||||
t.Fatalf("send direct target = %#v", send.args)
|
||||
}
|
||||
if _, ok := send.args["openDingTalkId"]; ok {
|
||||
t.Fatalf("send direct target leaked upload target key: %#v", send.args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatMessageSendFilePathRequiresFileMessageType(t *testing.T) {
|
||||
previousDeps := deps
|
||||
t.Cleanup(func() { deps = previousDeps })
|
||||
|
||||
filePath := filepath.Join(t.TempDir(), "image.png")
|
||||
if err := os.WriteFile(filePath, []byte("png"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
caller := &chatFilePathCaller{}
|
||||
err := runChatCoverageCommand(t, caller,
|
||||
"message", "send",
|
||||
"--group=cid",
|
||||
"--file-path="+filePath,
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("bare --file-path succeeded without --msg-type=file")
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("bare --file-path made remote calls: %#v", caller.calls)
|
||||
}
|
||||
}
|
||||
@@ -14,23 +14,38 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const chatMediaUploadReplacement = "dws chat message send --msg-type file --file-path <本地路径>"
|
||||
var (
|
||||
chatMediaResolveAppToken = mediaResolveAppToken
|
||||
chatMediaUploadFile = mediaUploadFile
|
||||
mediaCreateFormFile = func(w *multipart.Writer, field, name string) (io.Writer, error) { return w.CreateFormFile(field, name) }
|
||||
mediaOpenFile = os.Open
|
||||
mediaCopyFile = io.Copy
|
||||
)
|
||||
|
||||
func newChatMediaGroup() *cobra.Command {
|
||||
media := &cobra.Command{
|
||||
Use: "media",
|
||||
Short: "已下线:媒体文件上传兼容入口",
|
||||
Deprecated: "本地图片和文件请改用 " + chatMediaUploadReplacement,
|
||||
Short: "媒体文件管理",
|
||||
Args: cobra.NoArgs,
|
||||
TraverseChildren: true,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(*cobra.Command, []string) error {
|
||||
return chatMediaUploadDownlineError()
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return cmd.Help() },
|
||||
}
|
||||
media.AddCommand(newChatMediaUploadCommand())
|
||||
return media
|
||||
@@ -38,33 +53,160 @@ func newChatMediaGroup() *cobra.Command {
|
||||
|
||||
func newChatMediaUploadCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "upload",
|
||||
Short: "已下线:请通过 chat message send 直接发送本地文件",
|
||||
Deprecated: "请改用 " + chatMediaUploadReplacement,
|
||||
Long: `此命令仅为 1.x 命令行兼容保留,不再读取应用凭证或调用旧版媒体上传接口。
|
||||
|
||||
发送本地图片或文件时,请使用 chat message send --msg-type file --file-path。
|
||||
该路径会把图片作为可下载的 file 消息发送,不会生成 mediaId,也不会渲染成内联 image 消息。
|
||||
如果上游已经提供 mediaId,仍可使用 chat message send --msg-type image --media-id。`,
|
||||
Example: " dws chat message send --group <openConversationId> --msg-type file --file-path ./screenshot.png\n" +
|
||||
" dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file-path ./report.pdf",
|
||||
Use: "upload",
|
||||
Short: "上传图片获取 mediaId(用于 chat message send --msg-type image)",
|
||||
Example: " dws chat media upload --file ./screenshot.png\n" +
|
||||
" dws chat media upload --file ./photo.jpg --type image",
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(*cobra.Command, []string) error {
|
||||
return chatMediaUploadDownlineError()
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
filePath, _ := cmd.Flags().GetString("file")
|
||||
filePath = strings.TrimSpace(filePath)
|
||||
if filePath == "" {
|
||||
return apperrors.NewValidation("--file is required")
|
||||
}
|
||||
fi, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
return apperrors.NewValidation("cannot read file: " + err.Error())
|
||||
}
|
||||
if fi.IsDir() {
|
||||
return apperrors.NewValidation(filePath + " is a directory")
|
||||
}
|
||||
mediaType, _ := cmd.Flags().GetString("type")
|
||||
mediaType = strings.TrimSpace(strings.ToLower(mediaType))
|
||||
if mediaType == "" {
|
||||
mediaType = "image"
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(cmd.Context(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
token, err := chatMediaResolveAppToken(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mediaID, err := chatMediaUploadFile(ctx, token, filePath, mediaType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeCommandPayload(cmd, map[string]any{
|
||||
"success": true,
|
||||
"mediaId": mediaID,
|
||||
})
|
||||
},
|
||||
}
|
||||
// Keep the historical flags so existing argv remains parseable while the
|
||||
// 1.x compatibility command returns an actionable migration error.
|
||||
cmd.Flags().String("file", "", "旧版兼容参数;本地文件请改用 chat message send --file-path")
|
||||
cmd.Flags().String("type", "image", "旧版兼容参数;不再执行媒体上传")
|
||||
cmd.Flags().String("file", "", "本地文件路径 (必填)")
|
||||
cmd.Flags().String("type", "image", "媒体类型: image/voice/video/file")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func chatMediaUploadDownlineError() error {
|
||||
return apperrors.NewValidation(
|
||||
"chat media upload 已下线,当前 CLI 不提供本地文件到 mediaId 的上传能力。" +
|
||||
" 本地图片或文件请改用: " + chatMediaUploadReplacement +
|
||||
";已有 mediaId 时可使用 dws chat message send --msg-type image --media-id <mediaId>。",
|
||||
)
|
||||
func mediaResolveAppToken(ctx context.Context) (string, error) {
|
||||
return mediaResolveAppTokenWithRequest(ctx, http.NewRequestWithContext)
|
||||
}
|
||||
|
||||
type mediaRequestFactory func(context.Context, string, string, io.Reader) (*http.Request, error)
|
||||
|
||||
func mediaResolveAppTokenWithRequest(ctx context.Context, newRequest mediaRequestFactory) (string, error) {
|
||||
appKey := os.Getenv("DWS_CLIENT_ID")
|
||||
appSecret := os.Getenv("DWS_CLIENT_SECRET")
|
||||
if appKey == "" || appSecret == "" {
|
||||
return "", apperrors.NewAuth(
|
||||
"缺少应用凭证。chat media upload 需要 DWS_CLIENT_ID / DWS_CLIENT_SECRET 环境变量。\n" +
|
||||
"请使用 dws auth login --client-id <APP_KEY> --client-secret <APP_SECRET> 登录。")
|
||||
}
|
||||
endpoint := url.URL{Scheme: "https", Host: "oapi.dingtalk.com", Path: "/gettoken"}
|
||||
endpoint.RawQuery = url.Values{
|
||||
"appkey": []string{appKey},
|
||||
"appsecret": []string{appSecret},
|
||||
}.Encode()
|
||||
req, err := newRequest(ctx, http.MethodGet, endpoint.String(), nil)
|
||||
if err != nil {
|
||||
return "", apperrors.NewAuth("构造访问令牌请求失败: " + err.Error())
|
||||
}
|
||||
resp, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
|
||||
if err != nil {
|
||||
return "", apperrors.NewAuth("获取访问令牌失败: " + err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if resp.StatusCode >= 400 {
|
||||
return "", apperrors.NewAuth(fmt.Sprintf("获取访问令牌 HTTP %d: %s", resp.StatusCode, string(raw)))
|
||||
}
|
||||
var parsed struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &parsed); err != nil {
|
||||
return "", apperrors.NewAuth("gettoken 响应解析失败: " + string(raw))
|
||||
}
|
||||
if parsed.ErrCode != 0 || parsed.AccessToken == "" {
|
||||
return "", apperrors.NewAuth(fmt.Sprintf("gettoken errcode=%d errmsg=%s", parsed.ErrCode, parsed.ErrMsg))
|
||||
}
|
||||
return parsed.AccessToken, nil
|
||||
}
|
||||
|
||||
func mediaUploadFile(ctx context.Context, token, filePath, mediaType string) (string, error) {
|
||||
return mediaUploadFileWithRequest(ctx, token, filePath, mediaType, http.NewRequestWithContext)
|
||||
}
|
||||
|
||||
func mediaUploadFileWithRequest(ctx context.Context, token, filePath, mediaType string, newRequest mediaRequestFactory) (string, error) {
|
||||
pr, pw := io.Pipe()
|
||||
writer := multipart.NewWriter(pw)
|
||||
endpoint := url.URL{Scheme: "https", Host: "oapi.dingtalk.com", Path: "/media/upload"}
|
||||
endpoint.RawQuery = url.Values{
|
||||
"access_token": []string{token},
|
||||
"type": []string{mediaType},
|
||||
}.Encode()
|
||||
req, err := newRequest(ctx, http.MethodPost, endpoint.String(), pr)
|
||||
if err != nil {
|
||||
_ = pr.CloseWithError(err)
|
||||
_ = pw.CloseWithError(err)
|
||||
return "", apperrors.NewAPI("construct media upload request: " + err.Error())
|
||||
}
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
go func() {
|
||||
defer pw.Close()
|
||||
defer writer.Close()
|
||||
part, err := mediaCreateFormFile(writer, "media", filepath.Base(filePath))
|
||||
if err != nil {
|
||||
pw.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
f, err := mediaOpenFile(filePath)
|
||||
if err != nil {
|
||||
pw.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := mediaCopyFile(part, f); err != nil {
|
||||
pw.CloseWithError(err)
|
||||
}
|
||||
}()
|
||||
|
||||
resp, err := (&http.Client{Timeout: 2 * time.Minute}).Do(req)
|
||||
if err != nil {
|
||||
return "", apperrors.NewAPI("media upload failed: " + err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if resp.StatusCode >= 400 {
|
||||
return "", apperrors.NewAPI(fmt.Sprintf("media upload HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))))
|
||||
}
|
||||
|
||||
var parsed struct {
|
||||
MediaID string `json:"media_id"`
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &parsed); err != nil {
|
||||
return "", apperrors.NewAPI("media upload 响应解析失败: " + string(body))
|
||||
}
|
||||
if parsed.ErrCode != 0 || strings.TrimSpace(parsed.MediaID) == "" {
|
||||
return "", apperrors.NewAPI(fmt.Sprintf("media upload errcode=%d errmsg=%s body=%s", parsed.ErrCode, parsed.ErrMsg, string(body)))
|
||||
}
|
||||
return strings.TrimSpace(parsed.MediaID), nil
|
||||
}
|
||||
|
||||
@@ -1,36 +1,87 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCrossPlatformCoverageChatMediaUploadIsDeprecatedCompatibilityStub(t *testing.T) {
|
||||
group := newChatMediaGroup()
|
||||
if group.Deprecated == "" || group.Hidden || !group.Runnable() {
|
||||
t.Fatalf("media group compatibility contract: deprecated=%q hidden=%v runnable=%v", group.Deprecated, group.Hidden, group.Runnable())
|
||||
func TestCrossPlatformCoverageChatMediaUploadCommandRemainingCoverage(t *testing.T) {
|
||||
file := filepath.Join(t.TempDir(), "image.png")
|
||||
if err := os.WriteFile(file, []byte("image"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := executeFilterCoverage(t, newChatMediaUploadCommand(), "--file", t.TempDir()); err == nil {
|
||||
t.Fatal("directory upload returned nil")
|
||||
}
|
||||
|
||||
origResolve, origUpload := chatMediaResolveAppToken, chatMediaUploadFile
|
||||
t.Cleanup(func() {
|
||||
chatMediaResolveAppToken = origResolve
|
||||
chatMediaUploadFile = origUpload
|
||||
})
|
||||
chatMediaResolveAppToken = func(context.Context) (string, error) { return "", errors.New("token") }
|
||||
if err := executeFilterCoverage(t, newChatMediaUploadCommand(), "--file", file); err == nil {
|
||||
t.Fatal("token failure returned nil")
|
||||
}
|
||||
chatMediaResolveAppToken = func(context.Context) (string, error) { return "token", nil }
|
||||
chatMediaUploadFile = func(context.Context, string, string, string) (string, error) { return "", errors.New("upload") }
|
||||
if err := executeFilterCoverage(t, newChatMediaUploadCommand(), "--file", file); err == nil {
|
||||
t.Fatal("upload failure returned nil")
|
||||
}
|
||||
var mediaType string
|
||||
chatMediaUploadFile = func(_ context.Context, _, _, typ string) (string, error) {
|
||||
mediaType = typ
|
||||
return "media-id", nil
|
||||
}
|
||||
cmd := newChatMediaUploadCommand()
|
||||
if cmd.Deprecated == "" || cmd.Hidden || !cmd.Runnable() {
|
||||
t.Fatalf("media upload compatibility contract: deprecated=%q hidden=%v runnable=%v", cmd.Deprecated, cmd.Hidden, cmd.Runnable())
|
||||
var out bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"--file", file, "--type", " "})
|
||||
if err := cmd.Execute(); err != nil || mediaType != "image" || !strings.Contains(out.String(), "media-id") {
|
||||
t.Fatalf("success type=%q output=%q err=%v", mediaType, out.String(), err)
|
||||
}
|
||||
for _, flag := range []string{"file", "type"} {
|
||||
if cmd.Flags().Lookup(flag) == nil {
|
||||
t.Fatalf("media upload lost historical --%s flag", flag)
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageMediaUploadMultipartWriterRemainingFailures(t *testing.T) {
|
||||
file := filepath.Join(t.TempDir(), "image.png")
|
||||
if err := os.WriteFile(file, []byte("image"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
origTransport := http.DefaultTransport
|
||||
origCreate, origOpen, origCopy := mediaCreateFormFile, mediaOpenFile, mediaCopyFile
|
||||
t.Cleanup(func() {
|
||||
http.DefaultTransport = origTransport
|
||||
mediaCreateFormFile, mediaOpenFile, mediaCopyFile = origCreate, origOpen, origCopy
|
||||
})
|
||||
http.DefaultTransport = roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
_, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(`{"media_id":"id"}`)), Request: req}, nil
|
||||
})
|
||||
|
||||
mediaCreateFormFile = func(*multipart.Writer, string, string) (io.Writer, error) { return nil, errors.New("part") }
|
||||
if _, err := mediaUploadFile(context.Background(), "token", file, "image"); err == nil {
|
||||
t.Fatal("form part failure returned nil")
|
||||
}
|
||||
|
||||
cmd.SilenceErrors = true
|
||||
cmd.SilenceUsage = true
|
||||
cmd.SetArgs([]string{"--file", "/path/that/does/not/exist.png", "--type", "image"})
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Fatal("deprecated media upload returned nil error")
|
||||
mediaCreateFormFile = origCreate
|
||||
mediaOpenFile = func(string) (*os.File, error) { return nil, errors.New("open") }
|
||||
if _, err := mediaUploadFile(context.Background(), "token", file, "image"); err == nil {
|
||||
t.Fatal("open failure returned nil")
|
||||
}
|
||||
for _, want := range []string{"已下线", "chat message send", "--msg-type file", "--file-path", "--media-id"} {
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Fatalf("media upload migration error missing %q: %v", want, err)
|
||||
}
|
||||
mediaOpenFile = origOpen
|
||||
mediaCopyFile = func(io.Writer, io.Reader) (int64, error) { return 0, errors.New("copy") }
|
||||
if _, err := mediaUploadFile(context.Background(), "token", file, "image"); err == nil {
|
||||
t.Fatal("copy failure returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,14 @@ package helpers
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
@@ -114,7 +117,86 @@ func TestCrossPlatformCoverageMailHTTPTransfersCoverage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageDocVersionsCoverage(t *testing.T) {
|
||||
func TestCrossPlatformCoverageChatMediaHTTPAndDocVersionsCoverage(t *testing.T) {
|
||||
previousTransport := http.DefaultTransport
|
||||
mode := "success"
|
||||
var requestQuery url.Values
|
||||
http.DefaultTransport = roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if mode == "transport-error" {
|
||||
return nil, errors.New("transport")
|
||||
}
|
||||
if req.Body != nil {
|
||||
if _, err := io.ReadAll(req.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
requestQuery = req.URL.Query()
|
||||
status := http.StatusOK
|
||||
body := `{"access_token":"token","errcode":0}`
|
||||
if strings.Contains(req.URL.Path, "media/upload") {
|
||||
body = `{"media_id":"media-id","errcode":0}`
|
||||
}
|
||||
switch mode {
|
||||
case "http-error":
|
||||
status = http.StatusInternalServerError
|
||||
body = `failure`
|
||||
case "invalid-json":
|
||||
body = `{`
|
||||
case "api-error":
|
||||
body = `{"errcode":1,"errmsg":"failed"}`
|
||||
}
|
||||
return &http.Response{StatusCode: status, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(body)), Request: req}, nil
|
||||
})
|
||||
t.Cleanup(func() { http.DefaultTransport = previousTransport })
|
||||
|
||||
t.Setenv("DWS_CLIENT_ID", "")
|
||||
t.Setenv("DWS_CLIENT_SECRET", "")
|
||||
if _, err := mediaResolveAppToken(context.Background()); err == nil {
|
||||
t.Fatal("missing media credentials succeeded")
|
||||
}
|
||||
t.Setenv("DWS_CLIENT_ID", "client")
|
||||
t.Setenv("DWS_CLIENT_SECRET", "secret")
|
||||
for _, current := range []string{"transport-error", "http-error", "invalid-json", "api-error", "success"} {
|
||||
mode = current
|
||||
_, _ = mediaResolveAppToken(context.Background())
|
||||
}
|
||||
t.Setenv("DWS_CLIENT_ID", "client&scope=chat")
|
||||
t.Setenv("DWS_CLIENT_SECRET", "secret=with?reserved")
|
||||
mode = "success"
|
||||
if _, err := mediaResolveAppToken(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if requestQuery.Get("appkey") != "client&scope=chat" || requestQuery.Get("appsecret") != "secret=with?reserved" {
|
||||
t.Fatalf("token query was not encoded: %v", requestQuery)
|
||||
}
|
||||
file := filepath.Join(t.TempDir(), "image.png")
|
||||
if err := os.WriteFile(file, []byte("image"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, current := range []string{"transport-error", "http-error", "invalid-json", "api-error", "success"} {
|
||||
mode = current
|
||||
_, _ = mediaUploadFile(context.Background(), "token", file, "image")
|
||||
}
|
||||
mode = "success"
|
||||
if _, err := mediaUploadFile(context.Background(), "token&scope=chat", file, "image&type=file"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if requestQuery.Get("access_token") != "token&scope=chat" || requestQuery.Get("type") != "image&type=file" {
|
||||
t.Fatalf("upload query was not encoded: %v", requestQuery)
|
||||
}
|
||||
mode = "success"
|
||||
_, _ = mediaUploadFile(context.Background(), "token", filepath.Join(t.TempDir(), "missing"), "image")
|
||||
|
||||
requestFailure := func(context.Context, string, string, io.Reader) (*http.Request, error) {
|
||||
return nil, errors.New("request")
|
||||
}
|
||||
if _, err := mediaResolveAppTokenWithRequest(context.Background(), requestFailure); err == nil {
|
||||
t.Fatal("token request construction failure returned nil")
|
||||
}
|
||||
if _, err := mediaUploadFileWithRequest(context.Background(), "token", file, "image", requestFailure); err == nil {
|
||||
t.Fatal("upload request construction failure returned nil")
|
||||
}
|
||||
|
||||
for _, value := range []any{float64(3), float64(3.5), "3", "bad", jsonNumber("3"), jsonNumber("bad"), true} {
|
||||
_ = docVersionNumberMatches(value, 3)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package chat
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
@@ -130,48 +129,3 @@ func TestCrossPlatformCoverageCompatibilityAliases(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageChatIDHelpers(t *testing.T) {
|
||||
t.Run("recognize open DingTalk IDs", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
value string
|
||||
want bool
|
||||
}{
|
||||
{value: "DingTalk-open-id", want: true},
|
||||
{value: " dingtalk-open-id ", want: true},
|
||||
{value: "user-id", want: false},
|
||||
{value: " ", want: false},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
if got := isOpenID(tc.value); got != tc.want {
|
||||
t.Errorf("isOpenID(%q) = %v, want %v", tc.value, got, tc.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("split mixed IDs", func(t *testing.T) {
|
||||
userIDs, openIDs := splitIDs([]string{" user-1 ", "", "D-open-1", "d-open-2", "user-2"})
|
||||
if want := []string{"user-1", "user-2"}; !reflect.DeepEqual(userIDs, want) {
|
||||
t.Fatalf("user IDs = %#v, want %#v", userIDs, want)
|
||||
}
|
||||
if want := []string{"D-open-1", "d-open-2"}; !reflect.DeepEqual(openIDs, want) {
|
||||
t.Fatalf("open IDs = %#v, want %#v", openIDs, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parse numeric IDs", func(t *testing.T) {
|
||||
got, err := toInt64Slice([]string{" 1 ", "", "-2"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want := []int64{1, -2}; !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("numeric IDs = %#v, want %#v", got, want)
|
||||
}
|
||||
if _, err := toInt64Slice([]string{"not-a-number"}); err == nil {
|
||||
t.Fatal("invalid integer unexpectedly succeeded")
|
||||
}
|
||||
if _, err := toInt64Slice([]string{"", " "}); err == nil {
|
||||
t.Fatal("empty integer list unexpectedly succeeded")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+2
-18
@@ -39,24 +39,8 @@ func defaultHooks() *Hooks {
|
||||
base["claw-type"] = DefaultOSSClawType
|
||||
return base
|
||||
},
|
||||
StaticServers: openStaticServers,
|
||||
SupplementServers: openSupplementServers,
|
||||
VisibleProducts: openVisibleProducts,
|
||||
}
|
||||
}
|
||||
|
||||
// openSupplementServers returns helper-only MCP endpoints owned by the open
|
||||
// CLI. They are callable by explicit server ID but are deliberately excluded
|
||||
// from VisibleProducts, so no top-level product command is generated. They
|
||||
// stay separate from syncdata.StaticServers because only explicitly wired CLI
|
||||
// helpers may call them; they are not public MCP product surfaces.
|
||||
func openSupplementServers() []ServerInfo {
|
||||
return []ServerInfo{
|
||||
{
|
||||
ID: "mcp-meta",
|
||||
Name: "MCP 元服务",
|
||||
Endpoint: "https://mcp-gw.dingtalk.com/server/89833ea5debf30c260a07ffcb5127ffa3bf0c830cd76babadb293d9861485d44",
|
||||
},
|
||||
StaticServers: openStaticServers,
|
||||
VisibleProducts: openVisibleProducts,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,24 +70,4 @@ func TestOpenVisibleProductsExcludesCompatibilityOnlyCommands(t *testing.T) {
|
||||
t.Fatal("conference must remain compatibility-only and not be added to StaticServers")
|
||||
}
|
||||
}
|
||||
if byID["mcp-meta"] {
|
||||
t.Fatal("mcp-meta is helper-only and must not appear in VisibleProducts")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenSupplementServersIncludesMCPMeta(t *testing.T) {
|
||||
servers := openSupplementServers()
|
||||
for _, server := range servers {
|
||||
if server.ID != "mcp-meta" {
|
||||
continue
|
||||
}
|
||||
if server.Endpoint == "" {
|
||||
t.Fatal("mcp-meta has empty endpoint")
|
||||
}
|
||||
if len(server.Prefixes) != 0 {
|
||||
t.Fatal("mcp-meta must remain helper-only without command prefixes")
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatal("openSupplementServers() missing mcp-meta")
|
||||
}
|
||||
|
||||
@@ -273,8 +273,8 @@ alidocs 链接表面长得一样(`https://alidocs.dingtalk.com/i/nodes/{id}`
|
||||
- 已有 userId 时直接使用 `--user`;已有 openDingTalkId 时使用 `--open-dingtalk-id`
|
||||
- 纯文本/Markdown 单聊传 `--user` 时直接走 userId 发送能力,不需要先手动查询 openDingTalkId
|
||||
- 富媒体消息(image/file)单聊优先使用 `--open-dingtalk-id`;传 `--user` 时 CLI 会尝试解析为 openDingTalkId 后发送
|
||||
- "发本地图片/文件/语音/视频到群里" — `dws chat message send ... --msg-type file --file-path <本地路径>`,CLI 内部自动上传并发送;png/jpg/pdf/mp4/zip 等任意扩展名都走这条,接收方看到的是可下载的文件附件。图片不会内联渲染,也不会生成 mediaId
|
||||
- "用已有 mediaId 发内联图片" — 仅当上游已经提供有效 mediaId 时,使用 `dws chat message send ... --msg-type image --media-id <mediaId>`;DWS CLI 不能把本地图片转换成 mediaId
|
||||
- "发文件/语音/视频到群里" — `dws chat message send ... --msg-type file --file-path <本地路径>`,CLI 内部自动上传并发送(png/jpg/pdf/mp4/zip… 任意扩展名都走这条,但**都作为「文件」消息**发出,接收方看到的是可下载的文件条目)
|
||||
- "发张图片/截图(要在聊天里内联渲染成图,不是文件)" — 走图片消息链路:先 `dt_media_upload` 拿 mediaId,再 `dws chat message send ... --msg-type image --media-id <mediaId>`。**注意**:用 `--msg-type file` 发 .png 只会显示为[文件](fileId),不会渲染成图片;要图片效果必须走 `--msg-type image`
|
||||
- "发图片+文字说明" — 不要硬塞进一条命令;先发图片/文件消息再补一条 `--text "..."` 即可
|
||||
|
||||
```bash
|
||||
@@ -282,7 +282,7 @@ dws chat message send --group <openConversationId> --msg-type file --file-path .
|
||||
dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file-path ./report.pdf --format json
|
||||
```
|
||||
|
||||
> ❌ 反模式:先把本地文件转换成 mediaId,或先经钉盘上传再拼装聊天消息。本地图片/文件一律用 `--msg-type file --file-path` 直发;`--msg-type image --media-id` 只接受上游已经提供的有效 mediaId。
|
||||
> ❌ 反模式:调 `dt_media_upload` / `extract_media_id.py` / `drive upload` / `drive download` 等前置工具再 `--msg-type image --media-id`。这是**旧链路**,仅当上游已持有 mediaId 才用;新场景一律 `--file-path` 直发,避免长链路与"空白图"现象。
|
||||
> 富媒体消息单聊优先使用 `--open-dingtalk-id`;传 `--user` 时 CLI 会尝试解析为 openDingTalkId 后发送。
|
||||
|
||||
**用 `chat message send-by-bot` 的场景**:
|
||||
@@ -453,7 +453,7 @@ dws chat message send --group <openConversationId> --msg-type file --file-path <
|
||||
dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file-path <本地路径> --format json
|
||||
```
|
||||
|
||||
支持任意扩展名(`.png/.jpg/.gif/.bmp/.webp/.pdf/.doc/.xls/.zip/.mp3/.wav/.mp4/.avi` …),CLI 自动完成上传与发送,不需要任何前置工具调用。图片文件同样作为可下载的 file 附件发送,不会内联渲染,也不会生成 mediaId。
|
||||
支持任意扩展名(`.png/.jpg/.gif/.bmp/.webp/.pdf/.doc/.xls/.zip/.mp3/.wav/.mp4/.avi` …),CLI 自动识别并处理。**无需** `dt_media_upload` / `extract_media_id.py` / `drive upload` / `drive download` / `chat conversation-info` / `chat file upload` 等任何前置工具调用。
|
||||
|
||||
### 图片/文件 + 文字说明
|
||||
|
||||
@@ -464,9 +464,9 @@ dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file
|
||||
dws chat message send --open-dingtalk-id <openDingTalkId> --text "这是本周数据汇总" --format json
|
||||
```
|
||||
|
||||
### 上游 mediaId — 仅兼容场景
|
||||
### 旧链路(mediaId)— 仅兼容场景
|
||||
|
||||
仅当上游已经提供有效的 `@lQL...` 形式 mediaId 时使用;DWS CLI 不提供本地文件到 mediaId 的转换能力:
|
||||
仅当上游已经通过 `dt_media_upload` 拿到 `@lQL...` 形式的 mediaId 时使用:
|
||||
|
||||
```bash
|
||||
dws chat message send --group <openConversationId> --msg-type image --media-id "@lQLPD4JNnliqBq3NBQDNA8Cw" --format json
|
||||
|
||||
@@ -182,8 +182,8 @@ Flags:
|
||||
--group string 群聊 openConversationId (必填)
|
||||
--icon-media-id string 群头像 mediaId (必填)
|
||||
```
|
||||
> `--icon-media-id` 有本地格式校验:必须是 `@` 开头的、由可信上游提供的媒体 ID,非法格式会在本地直接报错。
|
||||
> ⚠️ 本地格式校验只查前缀。格式合法但不真实存在的 mediaId 服务端仍会静默返回成功,头像并不会真正更新。务必使用真实上游媒体上传能力拿到有效 mediaId;DWS CLI 不提供本地文件到 mediaId 的上传命令。
|
||||
> `--icon-media-id` 有本地格式校验:必须是 `@` 开头的媒体 ID(如 `dt_media_upload` 的返回值),非法格式会在本地直接报错。
|
||||
> ⚠️ 本地格式校验只查前缀。格式合法但不真实存在的 mediaId 服务端仍会静默返回成功,头像并不会真正更新。务必用 `dt_media_upload` / `chat media upload` 上传真实图片拿到的 mediaId。
|
||||
|
||||
#### 更新群设置 — 更新指定群聊的设置项
|
||||
|
||||
@@ -527,17 +527,17 @@ Flags:
|
||||
--group 指定群聊 openConversationId 发群消息;--user 指定用户 userId 发单聊;--open-dingtalk-id 指定用户 openDingTalkId 发单聊。三者只能选其一,不能同时指定。纯文本/Markdown 单聊传 --user 时直接走 userId 发送能力,不需要先手动查询 openDingTalkId。推荐使用 --text flag 传递消息内容(也支持位置参数)。可选 --title 作为消息标题。
|
||||
若用户只提供了数字群号而非 openConversationId,需先调用 `chat group get-by-group-id` 将群号转为 openConversationId,再传入 --group。
|
||||
--群聊时可选 --at-all @所有人,或 --at-open-dingtalk-ids 指定成员(仅群聊时生效)。
|
||||
--本地图片、文件、音频或视频统一用 --msg-type file --file-path;图片会作为可下载的文件附件发送。--msg-type image --media-id 仅用于上游已经提供有效 mediaId 的场景。
|
||||
--富媒体消息:通过 --msg-type 指定类型(image/file/audio/video),audio/video 是 file 的语义别名,发给服务端仍按 file 发送;必须根据文件扩展名判断 msgType 后再发送。
|
||||
```
|
||||
Usage:
|
||||
dws chat message send [flags] [<text>]
|
||||
|
||||
富媒体消息路由:
|
||||
场景 → msgType → 发送参数
|
||||
本地图片/文件/音频/视频 → file → --file-path <本地路径>
|
||||
上游已提供有效 mediaId 的内联图片 → image → --media-id <mediaId>
|
||||
|
||||
注意:本地 .png/.jpg 也按 file 发送,接收方看到的是可下载附件;DWS CLI 不能把本地文件转换成 mediaId。
|
||||
富媒体消息 msgType 决策(必须按此规则判断,不可跳过):
|
||||
文件扩展名 → msgType → 发送参数
|
||||
.jpg/.jpeg/.png/.gif/.bmp/.webp → image → dt_media_upload 上传 → `python scripts/extract_media_id.py <URL>` 提取 mediaId → --msg-type image --media-id
|
||||
.mp3/.wav/.m4a/.aac/.flac → audio(file) → 本地 --file-path 或 conversation-info/drive 上传 → --msg-type audio
|
||||
.mp4/.mov/.avi/.mkv/.webm → video(file) → 本地 --file-path 或 conversation-info/drive 上传 → --msg-type video
|
||||
其他所有(.pdf/.doc/.xls/.zip 等) → file → 本地 --file-path 或 conversation-info/drive 上传 → --msg-type file
|
||||
|
||||
Example:
|
||||
dws chat message send --group <openconversation_id> --text "hello"
|
||||
@@ -549,13 +549,15 @@ Example:
|
||||
dws chat message send --group <openconversation_id> --text "hello" --uuid "unique-id-123"
|
||||
dws chat message send --group <openconversation_id> --at-all "<@all> 请大家注意"
|
||||
dws chat message send --group <openconversation_id> --at-open-dingtalk-ids openDingTalkId1,openDingTalkId2 "<@openDingTalkId1> <@openDingTalkId2> 请查收"
|
||||
# 本地图片/文件/音频/视频统一作为 file 附件发送
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./screenshot.png
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./report.pdf
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./recording.mp3
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./demo.mp4
|
||||
# 仅当上游已有有效 mediaId 时发送内联图片
|
||||
# 发送图片
|
||||
dws chat message send --group <openconversation_id> --msg-type image --media-id <mediaId>
|
||||
# 发送文件/音频/视频(audio/video 是 file 的语义别名)
|
||||
# 先 dws chat conversation-info --group <id> 获取 spaceId(取 newCSpaceIdIM)
|
||||
# 再 dws drive upload --file <文件> --space-id <spaceId> 上传
|
||||
# 再 dws drive info --file-id <fileId> --space-id <spaceId> 获取 dentryId
|
||||
dws chat message send --group <openconversation_id> --msg-type file --dentry-id <dentryId> --space-id 24557356340 --file-name "report.pdf" --file-type "pdf" --file-path "/report.pdf" --file-size 234724
|
||||
dws chat message send --group <openconversation_id> --msg-type audio --file-path ./recording.mp3
|
||||
dws chat message send --group <openconversation_id> --msg-type video --file-path ./demo.mp4
|
||||
Flags:
|
||||
--text string 消息内容(推荐使用,也可用位置参数)
|
||||
--group string 群聊 openconversation_id(群聊时必填)
|
||||
@@ -564,14 +566,14 @@ Flags:
|
||||
--title string 消息标题(可选,默认「消息」)
|
||||
--at-all @所有人(仅群聊时生效,可选,默认 false)
|
||||
--at-open-dingtalk-ids string @指定成员的 openDingTalkId 列表,逗号分隔(仅群聊时生效,可选)
|
||||
--media-id string 上游提供的有效图片 mediaId(仅 msgType=image)
|
||||
--msg-type string 消息类型: image/file/audio/video(本地文件统一使用 file;image 仅配合已有 mediaId)
|
||||
--dentry-id int64 已有钉盘文件 dentryId(兼容参数,非本地文件发送的默认路径)
|
||||
--space-id int64 已有钉盘文件空间 ID(元数据兼容模式)
|
||||
--file-name string 已有钉盘文件名(元数据兼容模式)
|
||||
--file-type string 已有钉盘文件类型/扩展名(元数据兼容模式)
|
||||
--file-path string 本地文件路径(本地图片/文件/音视频配合 msgType=file 使用)
|
||||
--file-size int64 已有钉盘文件大小,单位字节(元数据兼容模式)
|
||||
--media-id string 图片 mediaId(dt_media_upload 上传后用 `python scripts/extract_media_id.py <URL>` 提取,仅 msgType=image)
|
||||
--msg-type string 消息类型: image/file/audio/video(audio/video 是 file 别名;image 用 mediaId,file/audio/video 用钉盘上传)
|
||||
--dentry-id int64 钉盘文件 dentryId(msgType=file 时必填,通过 drive info 获取)
|
||||
--space-id int64 钉盘空间 ID(msgType=file 时必填)
|
||||
--file-name string 文件名(msgType=file 时必填)
|
||||
--file-type string 文件类型/扩展名(msgType=file 时必填)
|
||||
--file-path string 文件路径(msgType=file 时必填)
|
||||
--file-size int64 文件大小,单位字节(msgType=file 时必填)
|
||||
--uuid string 幂等 UUID,相同 uuid 在 24h 内不会重复发送(可选)
|
||||
--ai-tag 消息是否带 AI 发送角标(可选,默认 true)
|
||||
|
||||
@@ -584,13 +586,36 @@ Flags:
|
||||
- **换行符**:消息内容按 Markdown 渲染,换行有两层要求,缺一不可:
|
||||
1. 必须使用**真实换行符**(Unicode `U+000A`),而非字面量字符串 `\n`(反斜杠 + 字母 n)。程序或大模型构造参数时,须确保已正确反转义;否则全部内容会渲染在同一行
|
||||
2. Markdown 规范下**单个换行不产生换行效果**。需要换行时请使用:段落分隔(连续两个真实换行符 `\n\n`)、行尾两个空格 + 真实换行符(硬换行 `<br>`),或直接写 HTML 的 `<br>` 标签
|
||||
- 本地图片、文档、压缩包、音频和视频统一使用 `--msg-type file --file-path <本地路径>`;图片会成为可下载的 file 附件,不会内联渲染,也不会生成 mediaId
|
||||
- `--msg-type image --media-id` 仅接受上游已经提供的有效 mediaId;DWS CLI 不提供本地文件到 mediaId 的上传或转换能力
|
||||
- audio/video 仍是兼容的 file 语义别名,但本地文件的推荐路径保持为 `--msg-type file --file-path`
|
||||
- dentryId/spaceId 等参数仅用于调用方已经持有钉盘文件元数据的兼容场景,不是发送本地文件的前置步骤
|
||||
- 富媒体消息类型与参数对应关系:
|
||||
- image(图片):--msg-type image --media-id
|
||||
- audio/video:file 的语义别名,发给服务端仍是 file;可直接传本地 --file-path,或先 conversation-info 获取 spaceId → drive upload --space-id 上传 → drive info 获取 dentryId → --msg-type audio 或 --msg-type video --dentry-id --space-id --file-name --file-type --file-path --file-size
|
||||
- file(文档/压缩包等其他非图片文件):可直接传本地 --file-path,或先 conversation-info 获取 spaceId → drive upload --space-id 上传 → drive info 获取 dentryId → --msg-type file --dentry-id --space-id --file-name --file-type --file-path --file-size
|
||||
- mediaId 通过 dt_media_upload 上传获得,必须用脚本提取:`python scripts/extract_media_id.py "<URL>"`(输出如 @lQLPxxx,直接用于 --media-id)。禁止手动从 URL 中截取或拼接 mediaId,手动解析会因 URL 格式不稳定导致尺寸后缀残留
|
||||
- --uuid 用于幂等发送,传入相同 uuid 在 24h 内不会重复投递消息(可选,群聊和单聊均支持)
|
||||
- 富媒体消息的单聊优先使用 `--open-dingtalk-id`;传 `--user` 时 CLI 会尝试解析成 openDingTalkId 后发送
|
||||
- 发送文字 + 文件时,先发送 `--msg-type file --file-path` 文件消息,再补一条文本或 Markdown 说明;这是两条独立消息
|
||||
- 发送文件/媒体消息时,必须先根据文件扩展名判断 msgType:图片(.jpg/.png/.gif/.bmp/.webp)→image,音频(.mp3/.wav/.m4a/.aac/.flac)→audio,视频(.mp4/.mov/.avi/.mkv/.webm)→video,其他所有→file;不可跳过此判断步骤。audio/video 是 file 别名,不代表不支持音频/视频
|
||||
- 20MB 降级:图片超过 20MB 时 dt_media_upload 会失败,必须降级走钉盘上传 + Markdown 嵌入方式发送(参见「发送图片+文字消息」章节)。音频/视频/文件走钉盘上传无 20MB 限制
|
||||
- 发送文字 + 文件混合消息时的完整流程:除了将文件以 Markdown 链接内嵌到文字消息中发送一条 md 消息外,还必须额外逐个发送独立的文件消息(--msg-type file),确保接收方可以直接下载原始文件。即:先发一条包含文字和文件链接的 md 消息,再对每个涉及的文件各发一条 --msg-type file 的文件消息
|
||||
```
|
||||
|
||||
### media (上传媒体获取 mediaId)
|
||||
|
||||
#### 上传图片/媒体获取 mediaId — 用于 chat message send --msg-type image 等
|
||||
|
||||
⚠️ 前置条件:本命令需要应用凭证。必须已通过 `dws auth login --client-id <APP_KEY> --client-secret <APP_SECRET>` 登录,或设置环境变量 `DWS_CLIENT_ID` / `DWS_CLIENT_SECRET`;否则报"缺少应用凭证"。这与其他 chat 命令走用户登录态不同。
|
||||
```
|
||||
Usage:
|
||||
dws chat media upload [flags]
|
||||
Example:
|
||||
dws chat media upload --file ./screenshot.png
|
||||
dws chat media upload --file ./photo.jpg --type image
|
||||
Flags:
|
||||
--file string 本地文件路径 (必填)
|
||||
--type string 媒体类型: image/voice/video/file(默认 image)
|
||||
|
||||
注意:
|
||||
- 返回的 mediaId 可直接用于 chat message send --msg-type image --media-id
|
||||
- 发图片+文字时,agent 侧一般用独立的 dt_media_upload 工具;本命令是 dws 内置的等价上传入口
|
||||
```
|
||||
|
||||
### file (会话文件上传,已下线)
|
||||
@@ -1140,7 +1165,7 @@ Flags:
|
||||
|
||||
#### 获取会话基础信息 — 含会话关联的钉盘共享空间 ID
|
||||
|
||||
获取指定会话的基础信息,包含会话关联的钉盘共享空间 ID (newCSpaceIdIM)。该 ID 可用于独立的钉盘存储操作;发送本地图片或文件到聊天不需要先调用本命令,直接使用 `chat message send --msg-type file --file-path`。
|
||||
获取指定会话的基础信息,包含会话关联的钉盘共享空间 ID (newCSpaceIdIM)。发送文件消息前需先调用此命令获取 spaceId,再用 drive upload --space-id 上传文件到共享空间。
|
||||
```
|
||||
Usage:
|
||||
dws chat conversation-info [flags]
|
||||
@@ -1156,8 +1181,8 @@ Flags:
|
||||
注意:
|
||||
- --group、--user、--open-dingtalk-id 互斥,必须且只能指定其一
|
||||
- --group 的别名: --id, --chat, --conversation-id (均可替代 --group)
|
||||
- 返回值中的 newCSpaceIdIM 为会话共享空间 ID,可用于调用方明确需要的钉盘存储流程
|
||||
- 该 ID 不是发送本地聊天附件的前置条件;本地附件直接走 `chat message send --msg-type file --file-path`
|
||||
- 返回值中的 newCSpaceIdIM 为会话共享空间 ID,用于 drive upload --space-id 参数
|
||||
- 上传到共享空间的文件对方才能打开,上传到个人空间的文件对方无法访问
|
||||
```
|
||||
|
||||
#### 引用回复消息 — 引用某条消息并回复文字(单聊/群聊均可)
|
||||
@@ -1865,8 +1890,7 @@ Flags:
|
||||
用户说"转发话题/转发话题消息" → `chat message forward-topic`
|
||||
用户说"置顶消息/把消息置顶" → `chat message set-top-msg`
|
||||
用户说"取消置顶消息/撤销消息置顶" → `chat message unset-top-msg`
|
||||
用户说"发送/上传本地图片或媒体到聊天" → `chat message send --msg-type file --file-path <本地路径>`
|
||||
用户明确只要 mediaId → DWS CLI 当前不提供本地上传入口;仅在上游已有有效 mediaId 时使用 `chat message send --msg-type image --media-id`
|
||||
用户说"上传图片拿mediaId/上传媒体" → `chat media upload`
|
||||
用户说"群机器人列表/群里有哪些机器人/查看群机器人" → `chat group bots`
|
||||
用户说"从群里移除机器人/踢出机器人" → `chat group members remove-bot`
|
||||
用户说"搜索机器人/找机器人/查机器人/帮我找XXX机器人" → `chat bot find`(全部可用机器人,额外返回 botOpenDingTalkId 可发单聊)
|
||||
@@ -1892,7 +1916,7 @@ Flags:
|
||||
- `chat message list-topic-replies` — 拉取群话题的回复消息列表
|
||||
- `chat message list-focused` — 拉取特别关注人的消息,cursor 分页
|
||||
- `chat list-top-conversations` — 拉取置顶会话列表(用户询问"置顶会话"或"置顶消息"时路由到此),cursor 分页
|
||||
- `chat message send` — 以当前用户身份发消息(群聊或单聊),text 为位置参数;本地图片/文件/音视频统一用 `--msg-type file --file-path`,其中图片显示为可下载附件而非内联图片;`--msg-type image --media-id` 只用于上游已经提供有效 mediaId 的场景,DWS CLI 不能从本地文件生成 mediaId
|
||||
- `chat message send` — 以当前用户身份发消息(群聊或单聊),text 为位置参数;支持 --msg-type 发送富媒体消息:image(图片)、file/audio/video(audio/video 是 file 别名),图片的 mediaId 通过 dt_media_upload 上传获得,其他文件可直接用 --file-path 或先获取会话共享空间再上传钉盘
|
||||
- `chat message search` — 按关键词搜索消息内容(跨所有会话,可选指定群)
|
||||
- `chat search-common` — 搜索共同群,查询指定人共同所在的群聊(AND=所有人都在,OR=任一人在)
|
||||
- `chat message send-by-bot` — 以**机器人**身份发消息(群聊或单聊),text 为 --text flag
|
||||
@@ -2049,16 +2073,34 @@ dws chat message send-by-bot --robot-code <robot-code> --group <openconversation
|
||||
```
|
||||
|
||||
|
||||
### 发送图片/文件 + 文字说明(两条消息)
|
||||
### 发送图片+文字 / 文件+文字消息(跨产品: drive → chat)
|
||||
|
||||
本地图片和文件先用 `--msg-type file --file-path` 发送,再补一条文本消息说明;这是两条独立消息,不需要媒体上传或钉盘前置步骤。图片会显示为可下载的文件附件,不会内联渲染。
|
||||
- **图片+文字**:图片**必须**通过 `dt_media_upload` 工具(非 dws 命令,是 agent 可调用的独立 tool)上传获取 mediaId,然后用 Markdown 嵌入方式发送。**禁止**使用钉盘上传图片。
|
||||
- **文件+文字**:文件通过钉盘上传 + Markdown 嵌入方式发送。
|
||||
|
||||
纯发图片/文件(不带文字)的完整流程见 [intent-guide.md](../intent-guide.md) 对应章节。
|
||||
|
||||
```bash
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./screenshot.png --format json
|
||||
dws chat message send --group <openconversation_id> --text "这是本周的数据汇总" --format json
|
||||
```
|
||||
# === 图片+文字 ===
|
||||
# Step 1: 调用 dt_media_upload 工具上传图片(这是一个独立的 tool,不是 dws 命令)
|
||||
# dt_media_upload 会返回 mediaId(如 @lQLPxxx)
|
||||
# 提取 mediaId 可使用脚本: python extract_media_id.py "<返回的URL>"
|
||||
|
||||
如果调用方已经从上游取得有效 mediaId,可以先用 `--msg-type image --media-id` 发送内联图片,再补一条文本消息;DWS CLI 本身不能把本地图片转换成 mediaId。
|
||||
# Step 2: 用 Markdown 语法发送(mediaId 作为图片引用)
|
||||
dws chat message send --group <openconversation_id> \
|
||||
--text " 这是本周的数据汇总" --format json
|
||||
|
||||
# === 文件+文字 ===
|
||||
# Step 1: 上传文件到钉盘
|
||||
dws drive upload --file "报告.pdf" --format json
|
||||
|
||||
# Step 2: 获取下载链接
|
||||
dws drive download --file-id <dentryUuid> --format json
|
||||
|
||||
# Step 3: 用 Markdown 语法发送
|
||||
dws chat message send --group <openconversation_id> \
|
||||
--text "[报告.pdf](下载链接) 这是季度报告" --format json
|
||||
```
|
||||
|
||||
### 发送图片 / 文件(统一一条命令)
|
||||
|
||||
@@ -2080,7 +2122,7 @@ dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file
|
||||
dws chat message send --open-dingtalk-id <openDingTalkId> --text "这是本周数据汇总" --format json
|
||||
```
|
||||
|
||||
**已有 mediaId(仅当上游已经提供有效 mediaId 时才用)**:
|
||||
**旧链路(仅当上游已经持有 `dt_media_upload` 返回的 mediaId 时才用)**:
|
||||
|
||||
```bash
|
||||
dws chat message send --group <openConversationId> --msg-type image --media-id "@lQLPD4JNnliqBq3NBQDNA8Cw" --format json
|
||||
@@ -2140,7 +2182,7 @@ Flags:
|
||||
| `chat message search` | `nextCursor` | 下次 message search 的 --cursor |
|
||||
| `chat message search-advanced` | `nextCursor` | 下次 message search-advanced 的 --cursor |
|
||||
| `chat search-common` | `openConversationId` | message send/list 等的 --group |
|
||||
| `chat conversation-info` | `newCSpaceIdIM` | 独立钉盘存储流程的共享空间 ID;不是发送本地聊天附件的前置条件 |
|
||||
| `chat conversation-info` | `newCSpaceIdIM` | drive upload 的 --space-id(发送文件消息前获取共享空间) |
|
||||
| `chat file upload` | 无(已下线) | 不要调用;常规发图/发文件用 `chat message send --msg-type file --file-path` |
|
||||
| `chat message list` | `openMsgId` | message read-status 的 --message-id |
|
||||
| `chat group-role list` | `openRoleId` | group-role update/remove/set-user/remove-user 的 --role-id |
|
||||
@@ -2148,6 +2190,7 @@ Flags:
|
||||
| `chat category list` | `categoryId` | category list-conversations 的 --category-id |
|
||||
| `chat group get-by-group-id` | `openConversationId` | 同 chat search,将群号转为 openConversationId |
|
||||
| `chat message send-card` | `bizId` | update-card 的 --biz-id |
|
||||
| `drive download` | 下载链接 | message send 的 Markdown 图片/链接语法 |
|
||||
| `chat message list` | `openMessageId` | message reply 的 --ref-msg-id、message forward 的 --msg-id |
|
||||
| `chat search` | `openConversationId` | set-top 的 --conversation-id、group-mute / group-mute-member 的 --group |
|
||||
|
||||
@@ -2164,7 +2207,7 @@ Flags:
|
||||
- 如果不传 `--uuid`,每次调用都视为新消息,重试可能导致消息重复发送
|
||||
- 此参数适用于 `chat message send`(群聊和单聊均支持)
|
||||
- `--group` 为群聊会话 ID (openconversation_id),可从群搜索或群聊信息中获取
|
||||
- `chat message send` 的 text 是位置参数(恰好 1 个),非 flag;群聊用 `--group`,单聊用 `--user`(userId)或 `--open-dingtalk-id`(openDingTalkId),三者互斥;纯文本/Markdown 单聊传 `--user` 时直接走 userId 发送能力;`--at-all`、`--at-open-dingtalk-ids` 仅在 `--group` 群聊时生效;本地图片/文件/音视频统一用 `--msg-type file --file-path`,其中图片是可下载附件;`--msg-type image --media-id` 仅用于上游已经提供有效 mediaId 的内联图片
|
||||
- `chat message send` 的 text 是位置参数(恰好 1 个),非 flag;群聊用 `--group`,单聊用 `--user`(userId)或 `--open-dingtalk-id`(openDingTalkId),三者互斥;纯文本/Markdown 单聊传 `--user` 时直接走 userId 发送能力;`--at-all`、`--at-open-dingtalk-ids` 仅在 `--group` 群聊时生效;富媒体消息通过 `--msg-type` 指定类型(image/file/audio/video),必须显式指定;发送文件/媒体消息时,必须先根据文件扩展名判断 msgType:图片→image,音频→audio,视频→video,其他→file,不可跳过此判断
|
||||
- `chat message list-all` 的四个参数(--start、--end、--limit、--cursor)每次请求都必须传递;翻页时用响应中的 nextCursor 值作为下次 --cursor
|
||||
- `chat message list` 的 `--group`、`--user`、`--open-dingtalk-id` 三者互斥,必须且只能指定其一
|
||||
- `chat message list-by-sender` 不需要指定单聊/群聊,返回结果自带会话类型标识;`--sender-user-id`(userId)与 `--sender-open-dingtalk-id`(openDingTalkId)二选一;时间用 `--start`/`--end`(ISO-8601),分页用 `--limit`/`--cursor`
|
||||
@@ -2185,7 +2228,7 @@ Flags:
|
||||
- `chat group transfer-owner` 转让群主,需传 --group(openConversationId);新群主 userId 用 `--user`,openDingTalkId 用 `--new-owner`
|
||||
- `chat group invite-url` 获取群邀请链接,需传 --group(openConversationId),可选 --expires-seconds 指定有效期(秒,0=永久)
|
||||
- `chat group quit` 退出群聊,需传 --group(openConversationId)
|
||||
- `chat group update-icon` 更新群头像,需传 --group(openConversationId)和由可信上游提供的有效 --icon-media-id(mediaId);DWS CLI 不能从本地图片生成该 ID
|
||||
- `chat group update-icon` 更新群头像,需传 --group(openConversationId)和 --icon-media-id(mediaId)
|
||||
- `chat group update-settings` 更新群设置,需传 --group(openConversationId)、--setting-key(设置项 key)、--status(0=关闭 1=开启)
|
||||
- `chat message send-card` 创建并推送流式卡片,群聊传 --group,单聊传 --receiver,二者互斥;不传 content,后续通过 update-card 更新内容
|
||||
- `chat message update-card` 流式更新卡片内容,需传 --biz-id(创建卡片返回的业务 ID)、--content、--flow-status
|
||||
@@ -2215,8 +2258,9 @@ Flags:
|
||||
|------|------|------|
|
||||
| [chat_export_messages.py](../../scripts/chat_export_messages.py) | 导出群聊消息到 JSON 文件 | `python chat_export_messages.py --query "项目冲刺" --time "2026-03-10 00:00:00"` |
|
||||
| [chat_history_with_user.py](../../scripts/chat_history_with_user.py) | 查询与某人的单聊聊天记录 | `python chat_history_with_user.py --name "张三" --time "2026-03-10 00:00:00"` |
|
||||
| [extract_media_id.py](../../scripts/extract_media_id.py) | 从 dt_media_upload URL 提取 mediaId | `python extract_media_id.py "<URL>"`(输出如 @lQLPxxx,直接用于 --media-id) |
|
||||
|
||||
## 相关产品
|
||||
|
||||
- [contact](./contact.md) — 搜索同事/好友,获取 userId 用于 --user、send-by-bot --users、send-by-bot --at-user-ids、list-by-sender --sender-user-id;获取 openDingTalkId 用于 message send 的 --at-open-dingtalk-ids、--open-dingtalk-id、send-by-bot --open-dingtalk-ids、send-by-bot --at-open-dingtalk-ids、list-by-sender 的 --sender-open-dingtalk-id
|
||||
- [drive](./drive.md) — 钉盘文件存储与下载;不是发送本地聊天图片/文件的前置步骤
|
||||
- [drive](./drive.md) — 上传文件获取下载链接,用于 Markdown 图片/文件消息
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
从 dt_media_upload 返回的 URL 中提取 mediaId。
|
||||
|
||||
用法:
|
||||
python extract_media_id.py <url>
|
||||
python extract_media_id.py "https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw_960_1280.png"
|
||||
# 输出: @lQLPD4JNnliqBq3NBQDNA8Cw
|
||||
|
||||
依赖: 无(纯 Python 标准库)
|
||||
|
||||
典型工作流(发送图片/语音/视频消息):
|
||||
# 1. 用 dt_media_upload 上传文件,获得 URL
|
||||
# 2. 用本脚本从 URL 提取 mediaId
|
||||
python extract_media_id.py "<dt_media_upload 返回的 URL>"
|
||||
# 输出: @lQLPxxx(直接用于 --media-id 或 --pic-url 参数)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
def extract_media_id(url: str) -> str:
|
||||
"""从 dt_media_upload 返回的 URL 中提取 mediaId。
|
||||
|
||||
支持的 URL 格式示例:
|
||||
https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw_960_1280.png
|
||||
https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw.png
|
||||
https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw
|
||||
|
||||
提取规则:
|
||||
1. 取 URL 路径中 /media/ 之后的部分
|
||||
2. 去除尾部的 _数字_数字.扩展名 后缀(如 _960_1280.png)
|
||||
3. 如果没有尺寸后缀,去除尾部的 .扩展名
|
||||
4. 加上 @ 前缀
|
||||
|
||||
返回: @lQLPD4JNnliqBq3NBQDNA8Cw
|
||||
"""
|
||||
url = url.strip()
|
||||
|
||||
# 提取路径部分
|
||||
parsed = urlparse(url)
|
||||
path = parsed.path # e.g. /media/lQLPxxx_960_1280.png
|
||||
|
||||
# 取 /media/ 之后的部分
|
||||
media_prefix = "/media/"
|
||||
idx = path.find(media_prefix)
|
||||
if idx == -1:
|
||||
print(f"错误:URL 中未找到 /media/ 路径: {url}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
raw = path[idx + len(media_prefix):] # e.g. lQLPxxx_960_1280.png
|
||||
|
||||
if not raw:
|
||||
print(f"错误:/media/ 后无内容: {url}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 尝试去除 _数字_数字.扩展名 后缀(如 _960_1280.png)
|
||||
match = re.match(r'^(.+?)(_\d+_\d+\.\w+)$', raw)
|
||||
if match:
|
||||
media_id = match.group(1)
|
||||
else:
|
||||
# 没有尺寸后缀,尝试去除 .扩展名
|
||||
match2 = re.match(r'^(.+)\.\w+$', raw)
|
||||
if match2:
|
||||
media_id = match2.group(1)
|
||||
else:
|
||||
# 无后缀,直接使用
|
||||
media_id = raw
|
||||
|
||||
return f"@{media_id}"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="从 dt_media_upload 返回的 URL 中提取 mediaId"
|
||||
)
|
||||
parser.add_argument("url", help="dt_media_upload 返回的文件 URL")
|
||||
args = parser.parse_args()
|
||||
|
||||
media_id = extract_media_id(args.url)
|
||||
print(media_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -96,6 +96,6 @@ metadata:
|
||||
## 跨产品协作
|
||||
|
||||
- 收件人是人名 → 先用 `dingtalk-contact` 或 `dingtalk-aisearch` 拿 `openDingTalkId` / `userId`
|
||||
- 要发本地图片/文件 → 直接用 `dws chat message send --msg-type file --file-path <本地路径>`;图片会作为可下载的文件附件发送,不会内联渲染。只有上游已提供有效 mediaId 时才用 `--msg-type image --media-id`,DWS CLI 不能把本地文件转换成 mediaId
|
||||
- 要发图片/文件 → 先 `dt_media_upload` 上传 → `python scripts/extract_media_id.py "<URL>"` 提取 mediaId → 再用 `--media-id`
|
||||
- 紧急升级(应用内/短信/电话)→ 切到 `dingtalk-ding`
|
||||
- 发邮件 → 切到 `dingtalk-mail`
|
||||
|
||||
@@ -184,8 +184,8 @@ Flags:
|
||||
--icon-media-id string 群头像 mediaId (必填)
|
||||
```
|
||||
|
||||
> `--icon-media-id` 必须是 `@` 开头的、由可信上游提供的媒体 ID,非法格式会本地报错。
|
||||
> ⚠️ 本地格式校验只查前缀。格式合法但不真实存在的 mediaId 服务端仍会静默返回成功,头像并不会真正更新。务必使用真实上游媒体上传能力拿到有效 mediaId;DWS CLI 不提供本地文件到 mediaId 的上传命令。
|
||||
> `--icon-media-id` 必须是 `@` 开头的媒体 ID(如 dt_media_upload 返回值),非法格式会本地报错。
|
||||
> ⚠️ 本地格式校验只查前缀。格式合法但不真实存在的 mediaId 服务端仍会静默返回成功,头像并不会真正更新。务必用 `dt_media_upload` / `chat media upload` 上传真实图片拿到的 mediaId。
|
||||
|
||||
#### 更新群设置 — 更新指定群聊的设置项
|
||||
|
||||
@@ -529,17 +529,17 @@ Flags:
|
||||
--group 指定群聊 openConversationId 发群消息;--user 指定用户 userId 发单聊;--open-dingtalk-id 指定用户 openDingTalkId 发单聊。三者只能选其一,不能同时指定。纯文本/Markdown 单聊传 --user 时直接走 userId 发送能力,不需要先手动查询 openDingTalkId。推荐使用 --text flag 传递消息内容(也支持位置参数)。可选 --title 作为消息标题。
|
||||
若用户只提供了数字群号而非 openConversationId,需先调用 `chat group get-by-group-id` 将群号转为 openConversationId,再传入 --group。
|
||||
--群聊时可选 --at-all @所有人,或 --at-open-dingtalk-ids 指定成员(仅群聊时生效)。
|
||||
--本地图片、文件、音频或视频统一用 --msg-type file --file-path;图片会作为可下载的文件附件发送。--msg-type image --media-id 仅用于上游已经提供有效 mediaId 的场景。
|
||||
--富媒体消息:通过 --msg-type 指定类型(image/file/audio/video),audio/video 是 file 的语义别名,发给服务端仍按 file 发送;必须根据文件扩展名判断 msgType 后再发送。
|
||||
```
|
||||
Usage:
|
||||
dws chat message send [flags] [<text>]
|
||||
|
||||
富媒体消息路由:
|
||||
场景 → msgType → 发送参数
|
||||
本地图片/文件/音频/视频 → file → --file-path <本地路径>
|
||||
上游已提供有效 mediaId 的内联图片 → image → --media-id <mediaId>
|
||||
|
||||
注意:本地 .png/.jpg 也按 file 发送,接收方看到的是可下载附件;DWS CLI 不能把本地文件转换成 mediaId。
|
||||
富媒体消息 msgType 决策(必须按此规则判断,不可跳过):
|
||||
文件扩展名 → msgType → 发送参数
|
||||
.jpg/.jpeg/.png/.gif/.bmp/.webp → image → dt_media_upload 上传 → `python scripts/extract_media_id.py <URL>` 提取 mediaId → --msg-type image --media-id
|
||||
.mp3/.wav/.m4a/.aac/.flac → audio(file) → 本地 --file-path 或 conversation-info/drive 上传 → --msg-type audio
|
||||
.mp4/.mov/.avi/.mkv/.webm → video(file) → 本地 --file-path 或 conversation-info/drive 上传 → --msg-type video
|
||||
其他所有(.pdf/.doc/.xls/.zip 等) → file → 本地 --file-path 或 conversation-info/drive 上传 → --msg-type file
|
||||
|
||||
Example:
|
||||
dws chat message send --group <openconversation_id> --text "hello"
|
||||
@@ -551,13 +551,15 @@ Example:
|
||||
dws chat message send --group <openconversation_id> --text "hello" --uuid "unique-id-123"
|
||||
dws chat message send --group <openconversation_id> --at-all "<@all> 请大家注意"
|
||||
dws chat message send --group <openconversation_id> --at-open-dingtalk-ids openDingTalkId1,openDingTalkId2 "<@openDingTalkId1> <@openDingTalkId2> 请查收"
|
||||
# 本地图片/文件/音频/视频统一作为 file 附件发送
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./screenshot.png
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./report.pdf
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./recording.mp3
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./demo.mp4
|
||||
# 仅当上游已有有效 mediaId 时发送内联图片
|
||||
# 发送图片
|
||||
dws chat message send --group <openconversation_id> --msg-type image --media-id <mediaId>
|
||||
# 发送文件/音频/视频(audio/video 是 file 的语义别名)
|
||||
# 先 dws chat conversation-info --group <id> 获取 spaceId(取 newCSpaceIdIM)
|
||||
# 再 dws drive upload --file <文件> --space-id <spaceId> 上传
|
||||
# 再 dws drive info --file-id <fileId> --space-id <spaceId> 获取 dentryId
|
||||
dws chat message send --group <openconversation_id> --msg-type file --dentry-id <dentryId> --space-id 24557356340 --file-name "report.pdf" --file-type "pdf" --file-path "/report.pdf" --file-size 234724
|
||||
dws chat message send --group <openconversation_id> --msg-type audio --file-path ./recording.mp3
|
||||
dws chat message send --group <openconversation_id> --msg-type video --file-path ./demo.mp4
|
||||
Flags:
|
||||
--text string 消息内容(推荐使用,也可用位置参数)
|
||||
--group string 群聊 openconversation_id(群聊时必填)
|
||||
@@ -566,14 +568,14 @@ Flags:
|
||||
--title string 消息标题(可选,默认「消息」)
|
||||
--at-all @所有人(仅群聊时生效,可选,默认 false)
|
||||
--at-open-dingtalk-ids string @指定成员的 openDingTalkId 列表,逗号分隔(仅群聊时生效,可选)
|
||||
--media-id string 上游提供的有效图片 mediaId(仅 msgType=image)
|
||||
--msg-type string 消息类型: image/file/audio/video(本地文件统一使用 file;image 仅配合已有 mediaId)
|
||||
--dentry-id int64 已有钉盘文件 dentryId(兼容参数,非本地文件发送的默认路径)
|
||||
--space-id int64 已有钉盘文件空间 ID(元数据兼容模式)
|
||||
--file-name string 已有钉盘文件名(元数据兼容模式)
|
||||
--file-type string 已有钉盘文件类型/扩展名(元数据兼容模式)
|
||||
--file-path string 本地文件路径(本地图片/文件/音视频配合 msgType=file 使用)
|
||||
--file-size int64 已有钉盘文件大小,单位字节(元数据兼容模式)
|
||||
--media-id string 图片 mediaId(dt_media_upload 上传后用 `python scripts/extract_media_id.py <URL>` 提取,仅 msgType=image)
|
||||
--msg-type string 消息类型: image/file/audio/video(audio/video 是 file 别名;image 用 mediaId,file/audio/video 用钉盘上传)
|
||||
--dentry-id int64 钉盘文件 dentryId(msgType=file 时必填,通过 drive info 获取)
|
||||
--space-id int64 钉盘空间 ID(msgType=file 时必填)
|
||||
--file-name string 文件名(msgType=file 时必填)
|
||||
--file-type string 文件类型/扩展名(msgType=file 时必填)
|
||||
--file-path string 文件路径(msgType=file 时必填)
|
||||
--file-size int64 文件大小,单位字节(msgType=file 时必填)
|
||||
--uuid string 幂等 UUID,相同 uuid 在 24h 内不会重复发送(可选)
|
||||
--ai-tag 消息是否带 AI 发送角标(可选,默认 true)
|
||||
|
||||
@@ -586,13 +588,16 @@ Flags:
|
||||
- **换行符**:消息内容按 Markdown 渲染,换行有两层要求,缺一不可:
|
||||
1. 必须使用**真实换行符**(Unicode `U+000A`),而非字面量字符串 `\n`(反斜杠 + 字母 n)。程序或大模型构造参数时,须确保已正确反转义;否则全部内容会渲染在同一行
|
||||
2. Markdown 规范下**单个换行不产生换行效果**。需要换行时请使用:段落分隔(连续两个真实换行符 `\n\n`)、行尾两个空格 + 真实换行符(硬换行 `<br>`),或直接写 HTML 的 `<br>` 标签
|
||||
- 本地图片、文档、压缩包、音频和视频统一使用 `--msg-type file --file-path <本地路径>`;图片会成为可下载的 file 附件,不会内联渲染,也不会生成 mediaId
|
||||
- `--msg-type image --media-id` 仅接受上游已经提供的有效 mediaId;DWS CLI 不提供本地文件到 mediaId 的上传或转换能力
|
||||
- audio/video 仍是兼容的 file 语义别名,但本地文件的推荐路径保持为 `--msg-type file --file-path`
|
||||
- dentryId/spaceId 等参数仅用于调用方已经持有钉盘文件元数据的兼容场景,不是发送本地文件的前置步骤
|
||||
- 富媒体消息类型与参数对应关系:
|
||||
- image(图片):--msg-type image --media-id
|
||||
- audio/video:file 的语义别名,发给服务端仍是 file;可直接传本地 --file-path,或先 conversation-info 获取 spaceId → drive upload --space-id 上传 → drive info 获取 dentryId → --msg-type audio 或 --msg-type video --dentry-id --space-id --file-name --file-type --file-path --file-size
|
||||
- file(文档/压缩包等其他非图片文件):可直接传本地 --file-path,或先 conversation-info 获取 spaceId → drive upload --space-id 上传 → drive info 获取 dentryId → --msg-type file --dentry-id --space-id --file-name --file-type --file-path --file-size
|
||||
- mediaId 通过 dt_media_upload 上传获得,必须用脚本提取:`python scripts/extract_media_id.py "<URL>"`(输出如 @lQLPxxx,直接用于 --media-id)。禁止手动从 URL 中截取或拼接 mediaId,手动解析会因 URL 格式不稳定导致尺寸后缀残留
|
||||
- --uuid 用于幂等发送,传入相同 uuid 在 24h 内不会重复投递消息(可选,群聊和单聊均支持)
|
||||
- 富媒体消息的单聊优先使用 `--open-dingtalk-id`;传 `--user` 时 CLI 会尝试解析成 openDingTalkId 后发送
|
||||
- 发送文字 + 文件时,先发送 `--msg-type file --file-path` 文件消息,再补一条文本或 Markdown 说明;这是两条独立消息
|
||||
- 发送文件/媒体消息时,必须先根据文件扩展名判断 msgType:图片(.jpg/.png/.gif/.bmp/.webp)→image,音频(.mp3/.wav/.m4a/.aac/.flac)→audio,视频(.mp4/.mov/.avi/.mkv/.webm)→video,其他所有→file;不可跳过此判断步骤。audio/video 是 file 别名,不代表不支持音频/视频
|
||||
- 20MB 降级:图片超过 20MB 时 dt_media_upload 会失败,必须降级走钉盘上传 + Markdown 嵌入方式发送(参见「发送图片+文字消息」章节)。音频/视频/文件走钉盘上传无 20MB 限制
|
||||
- 发送文字 + 文件混合消息时的完整流程:除了将文件以 Markdown 链接内嵌到文字消息中发送一条 md 消息外,还必须额外逐个发送独立的文件消息(--msg-type file),确保接收方可以直接下载原始文件。即:先发一条包含文字和文件链接的 md 消息,再对每个涉及的文件各发一条 --msg-type file 的文件消息
|
||||
```
|
||||
|
||||
#### 查询消息发送状态 — 查询以当前用户身份发送的消息的发送状态
|
||||
@@ -1125,7 +1130,7 @@ Flags:
|
||||
|
||||
#### 获取会话基础信息 — 含会话关联的钉盘共享空间 ID
|
||||
|
||||
获取指定会话的基础信息,包含会话关联的钉盘共享空间 ID (newCSpaceIdIM)。该 ID 可用于独立的钉盘存储操作;发送本地图片或文件到聊天不需要先调用本命令,直接使用 `chat message send --msg-type file --file-path`。
|
||||
获取指定会话的基础信息,包含会话关联的钉盘共享空间 ID (newCSpaceIdIM)。发送文件消息前需先调用此命令获取 spaceId,再用 drive upload --space-id 上传文件到共享空间。
|
||||
```
|
||||
Usage:
|
||||
dws chat conversation-info [flags]
|
||||
@@ -1141,8 +1146,8 @@ Flags:
|
||||
注意:
|
||||
- --group、--user、--open-dingtalk-id 互斥,必须且只能指定其一
|
||||
- --group 的别名: --id, --chat, --conversation-id (均可替代 --group)
|
||||
- 返回值中的 newCSpaceIdIM 为会话共享空间 ID,可用于调用方明确需要的钉盘存储流程
|
||||
- 该 ID 不是发送本地聊天附件的前置条件;本地附件直接走 `chat message send --msg-type file --file-path`
|
||||
- 返回值中的 newCSpaceIdIM 为会话共享空间 ID,用于 drive upload --space-id 参数
|
||||
- 上传到共享空间的文件对方才能打开,上传到个人空间的文件对方无法访问
|
||||
```
|
||||
|
||||
#### 引用回复消息 — 引用某条消息并回复文字(单聊/群聊均可)
|
||||
@@ -1506,6 +1511,25 @@ Flags:
|
||||
- 支持单聊和群聊,openConversationId 可通过 chat search(群聊)或 chat conversation-info(单聊)获取
|
||||
```
|
||||
|
||||
### media (上传媒体获取 mediaId)
|
||||
|
||||
#### 上传图片/媒体获取 mediaId — 用于 chat message send --msg-type image 等
|
||||
|
||||
⚠️ 前置条件:本命令需要应用凭证。必须已通过 `dws auth login --client-id <APP_KEY> --client-secret <APP_SECRET>` 登录,或设置环境变量 `DWS_CLIENT_ID` / `DWS_CLIENT_SECRET`;否则报"缺少应用凭证"。这与其他 chat 命令走用户登录态不同。
|
||||
```
|
||||
Usage:
|
||||
dws chat media upload [flags]
|
||||
Example:
|
||||
dws chat media upload --file ./screenshot.png
|
||||
dws chat media upload --file ./photo.jpg --type image
|
||||
Flags:
|
||||
--file string 本地文件路径 (必填)
|
||||
--type string 媒体类型: image/voice/video/file(默认 image)
|
||||
|
||||
注意:
|
||||
- 返回的 mediaId 可直接用于 chat message send --msg-type image --media-id
|
||||
```
|
||||
|
||||
### hide (隐藏会话)
|
||||
|
||||
#### 隐藏会话 — 在会话列表中隐藏指定会话(支持单聊/群聊),收到新消息时会重新出现
|
||||
@@ -1831,8 +1855,7 @@ Flags:
|
||||
用户说"转发话题/转发话题消息" → `chat message forward-topic`
|
||||
用户说"置顶消息/把消息置顶" → `chat message set-top-msg`
|
||||
用户说"取消置顶消息/撤销消息置顶" → `chat message unset-top-msg`
|
||||
用户说"发送/上传本地图片或媒体到聊天" → `chat message send --msg-type file --file-path <本地路径>`
|
||||
用户明确只要 mediaId → DWS CLI 当前不提供本地上传入口;仅在上游已有有效 mediaId 时使用 `chat message send --msg-type image --media-id`
|
||||
用户说"上传图片拿mediaId/上传媒体" → `chat media upload`
|
||||
用户说"群机器人列表/群里有哪些机器人/查看群机器人" → `chat group bots`
|
||||
用户说"从群里移除机器人/踢出机器人" → `chat group members remove-bot`
|
||||
用户说"搜索机器人/找机器人/查机器人/帮我找XXX机器人" → `chat bot find`(全部可用机器人,额外返回 botOpenDingTalkId 可发单聊)
|
||||
@@ -1858,7 +1881,7 @@ Flags:
|
||||
- `chat message list-topic-replies` — 拉取群话题的回复消息列表
|
||||
- `chat message list-focused` — 拉取特别关注人的消息,cursor 分页
|
||||
- `chat list-top-conversations` — 拉取置顶会话列表(用户询问"置顶会话"或"置顶消息"时路由到此),cursor 分页
|
||||
- `chat message send` — 以当前用户身份发消息(群聊或单聊),text 为位置参数;本地图片/文件/音视频统一用 `--msg-type file --file-path`,其中图片显示为可下载附件而非内联图片;`--msg-type image --media-id` 只用于上游已经提供有效 mediaId 的场景,DWS CLI 不能从本地文件生成 mediaId
|
||||
- `chat message send` — 以当前用户身份发消息(群聊或单聊),text 为位置参数;支持 --msg-type 发送富媒体消息:image(图片)、file/audio/video(audio/video 是 file 别名),图片的 mediaId 通过 dt_media_upload 上传获得,其他文件可直接用 --file-path 或先获取会话共享空间再上传钉盘
|
||||
- `chat message search` — 按关键词搜索消息内容(跨所有会话,可选指定群)
|
||||
- `chat search-common` — 搜索共同群,查询指定人共同所在的群聊(AND=所有人都在,OR=任一人在)
|
||||
- `chat message send-by-bot` — 以**机器人**身份发消息(群聊或单聊),text 为 --text flag
|
||||
@@ -2005,16 +2028,34 @@ dws chat message send-by-bot --robot-code <robot-code> --group <openconversation
|
||||
```
|
||||
|
||||
|
||||
### 发送图片/文件 + 文字说明(两条消息)
|
||||
### 发送图片+文字 / 文件+文字消息(跨产品: drive → chat)
|
||||
|
||||
本地图片和文件先用 `--msg-type file --file-path` 发送,再补一条文本消息说明;这是两条独立消息,不需要媒体上传或钉盘前置步骤。图片会显示为可下载的文件附件,不会内联渲染。
|
||||
- **图片+文字**:图片**必须**通过 `dt_media_upload` 工具(非 dws 命令,是 agent 可调用的独立 tool)上传获取 mediaId,然后用 Markdown 嵌入方式发送。**禁止**使用钉盘上传图片。
|
||||
- **文件+文字**:文件通过钉盘上传 + Markdown 嵌入方式发送。
|
||||
|
||||
纯发图片/文件(不带文字)的完整流程见 [intent-guide.md](./01-messaging.md) 对应章节。
|
||||
|
||||
```bash
|
||||
dws chat message send --group <openconversation_id> --msg-type file --file-path ./screenshot.png --format json
|
||||
dws chat message send --group <openconversation_id> --text "这是本周的数据汇总" --format json
|
||||
```
|
||||
# === 图片+文字 ===
|
||||
# Step 1: 调用 dt_media_upload 工具上传图片(这是一个独立的 tool,不是 dws 命令)
|
||||
# dt_media_upload 会返回 mediaId(如 @lQLPxxx)
|
||||
# 提取 mediaId 可使用脚本: python extract_media_id.py "<返回的URL>"
|
||||
|
||||
如果调用方已经从上游取得有效 mediaId,可以先用 `--msg-type image --media-id` 发送内联图片,再补一条文本消息;DWS CLI 本身不能把本地图片转换成 mediaId。
|
||||
# Step 2: 用 Markdown 语法发送(mediaId 作为图片引用)
|
||||
dws chat message send --group <openconversation_id> \
|
||||
--text " 这是本周的数据汇总" --format json
|
||||
|
||||
# === 文件+文字 ===
|
||||
# Step 1: 上传文件到钉盘
|
||||
dws drive upload --file "报告.pdf" --format json
|
||||
|
||||
# Step 2: 获取下载链接
|
||||
dws drive download --file-id <dentryUuid> --format json
|
||||
|
||||
# Step 3: 用 Markdown 语法发送
|
||||
dws chat message send --group <openconversation_id> \
|
||||
--text "[报告.pdf](下载链接) 这是季度报告" --format json
|
||||
```
|
||||
|
||||
#### 创建并推送流式卡片 — 向群聊或单聊发送流式卡片消息
|
||||
|
||||
@@ -2070,13 +2111,14 @@ Flags:
|
||||
| `chat message search` | `nextCursor` | 下次 message search 的 --cursor |
|
||||
| `chat message search-advanced` | `nextCursor` | 下次 message search-advanced 的 --cursor |
|
||||
| `chat search-common` | `openConversationId` | message send/list 等的 --group |
|
||||
| `chat conversation-info` | `newCSpaceIdIM` | 独立钉盘存储流程的共享空间 ID;不是发送本地聊天附件的前置条件 |
|
||||
| `chat conversation-info` | `newCSpaceIdIM` | drive upload 的 --space-id(发送文件消息前获取共享空间) |
|
||||
| `chat message list` | `openMsgId` | message read-status 的 --message-id |
|
||||
| `chat group-role list` | `openRoleId` | group-role update/remove/set-user/remove-user 的 --role-id |
|
||||
| `chat message create-text-emotion` | `emotionId` | add-text-emotion 的 --emotion-id |
|
||||
| `chat category list` | `categoryId` | category list-conversations 的 --category-id |
|
||||
| `chat group get-by-group-id` | `openConversationId` | 同 chat search,将群号转为 openConversationId |
|
||||
| `chat message send-card` | `bizId` | update-card 的 --biz-id |
|
||||
| `drive download` | 下载链接 | message send 的 Markdown 图片/链接语法 |
|
||||
| `chat message list` | `openMessageId` | message reply 的 --ref-msg-id、message forward 的 --msg-id |
|
||||
| `chat search` | `openConversationId` | set-top 的 --conversation-id、group-mute / group-mute-member 的 --group |
|
||||
|
||||
@@ -2093,7 +2135,7 @@ Flags:
|
||||
- 如果不传 `--uuid`,每次调用都视为新消息,重试可能导致消息重复发送
|
||||
- 此参数适用于 `chat message send`(群聊和单聊均支持)
|
||||
- `--group` 为群聊会话 ID (openconversation_id),可从群搜索或群聊信息中获取
|
||||
- `chat message send` 的 text 是位置参数(恰好 1 个),非 flag;群聊用 `--group`,单聊用 `--user`(userId)或 `--open-dingtalk-id`(openDingTalkId),三者互斥;纯文本/Markdown 单聊传 `--user` 时直接走 userId 发送能力;`--at-all`、`--at-open-dingtalk-ids` 仅在 `--group` 群聊时生效;本地图片/文件/音视频统一用 `--msg-type file --file-path`,其中图片是可下载附件;`--msg-type image --media-id` 仅用于上游已经提供有效 mediaId 的内联图片
|
||||
- `chat message send` 的 text 是位置参数(恰好 1 个),非 flag;群聊用 `--group`,单聊用 `--user`(userId)或 `--open-dingtalk-id`(openDingTalkId),三者互斥;纯文本/Markdown 单聊传 `--user` 时直接走 userId 发送能力;`--at-all`、`--at-open-dingtalk-ids` 仅在 `--group` 群聊时生效;富媒体消息通过 `--msg-type` 指定类型(image/file/audio/video),必须显式指定;发送文件/媒体消息时,必须先根据文件扩展名判断 msgType:图片→image,音频→audio,视频→video,其他→file,不可跳过此判断
|
||||
- `chat message list-all` 的四个参数(--start、--end、--limit、--cursor)每次请求都必须传递;翻页时用响应中的 nextCursor 值作为下次 --cursor
|
||||
- `chat message list` 的 `--group`、`--user`、`--open-dingtalk-id` 三者互斥,必须且只能指定其一
|
||||
- `chat message list-by-sender` 不需要指定单聊/群聊,返回结果自带会话类型标识;`--sender-user-id`(userId)与 `--sender-open-dingtalk-id`(openDingTalkId)二选一;时间用 `--start`/`--end`(ISO-8601),分页用 `--limit`/`--cursor`
|
||||
@@ -2114,7 +2156,7 @@ Flags:
|
||||
- `chat group transfer-owner` 转让群主,需传 --group(openConversationId);新群主 userId 用 `--user`,openDingTalkId 用 `--new-owner`
|
||||
- `chat group invite-url` 获取群邀请链接,需传 --group(openConversationId),可选 --expires-seconds 指定有效期(秒,0=永久)
|
||||
- `chat group quit` 退出群聊,需传 --group(openConversationId)
|
||||
- `chat group update-icon` 更新群头像,需传 --group(openConversationId)和由可信上游提供的有效 --icon-media-id(mediaId);DWS CLI 不能从本地图片生成该 ID
|
||||
- `chat group update-icon` 更新群头像,需传 --group(openConversationId)和 --icon-media-id(mediaId)
|
||||
- `chat group update-settings` 更新群设置,需传 --group(openConversationId)、--setting-key(设置项 key)、--status(0=关闭 1=开启)
|
||||
- `chat message send-card` 创建并推送流式卡片,群聊传 --group,单聊传 --receiver,二者互斥;不传 content,后续通过 update-card 更新内容
|
||||
- `chat message update-card` 流式更新卡片内容,需传 --biz-id(创建卡片返回的业务 ID)、--content、--flow-status
|
||||
@@ -2141,8 +2183,9 @@ Flags:
|
||||
|------|------|------|
|
||||
| [chat_export_messages.py](../scripts/chat_export_messages.py) | 导出群聊消息到 JSON 文件 | `python chat_export_messages.py --query "项目冲刺" --time "2026-03-10 00:00:00"` |
|
||||
| [chat_history_with_user.py](../scripts/chat_history_with_user.py) | 查询与某人的单聊聊天记录 | `python chat_history_with_user.py --name "张三" --time "2026-03-10 00:00:00"` |
|
||||
| [extract_media_id.py](../scripts/extract_media_id.py) | 从 dt_media_upload URL 提取 mediaId | `python extract_media_id.py "<URL>"`(输出如 @lQLPxxx,直接用于 --media-id) |
|
||||
|
||||
## 相关产品
|
||||
|
||||
- [contact](../../dingtalk-contact/references/contact.md) — 搜索同事/好友,获取 userId 用于 --user、send-by-bot --users、send-by-bot --at-user-ids、list-by-sender --sender-user-id;获取 openDingTalkId 用于 message send 的 --at-open-dingtalk-ids、--open-dingtalk-id、send-by-bot --open-dingtalk-ids、send-by-bot --at-open-dingtalk-ids、list-by-sender 的 --sender-open-dingtalk-id
|
||||
- [drive](../../dingtalk-drive/references/drive.md) — 钉盘文件存储与下载;不是发送本地聊天图片/文件的前置步骤
|
||||
- [drive](../../dingtalk-drive/references/drive.md) — 上传文件获取下载链接,用于 Markdown 图片/文件消息
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
从 dt_media_upload 返回的 URL 中提取 mediaId。
|
||||
|
||||
用法:
|
||||
python extract_media_id.py <url>
|
||||
python extract_media_id.py "https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw_960_1280.png"
|
||||
# 输出: @lQLPD4JNnliqBq3NBQDNA8Cw
|
||||
|
||||
依赖: 无(纯 Python 标准库)
|
||||
|
||||
典型工作流(发送图片/语音/视频消息):
|
||||
# 1. 用 dt_media_upload 上传文件,获得 URL
|
||||
# 2. 用本脚本从 URL 提取 mediaId
|
||||
python extract_media_id.py "<dt_media_upload 返回的 URL>"
|
||||
# 输出: @lQLPxxx(直接用于 --media-id 或 --pic-url 参数)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
def extract_media_id(url: str) -> str:
|
||||
"""从 dt_media_upload 返回的 URL 中提取 mediaId。
|
||||
|
||||
支持的 URL 格式示例:
|
||||
https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw_960_1280.png
|
||||
https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw.png
|
||||
https://down.dingtalk.com/media/lQLPD4JNnliqBq3NBQDNA8Cw
|
||||
|
||||
提取规则:
|
||||
1. 取 URL 路径中 /media/ 之后的部分
|
||||
2. 去除尾部的 _数字_数字.扩展名 后缀(如 _960_1280.png)
|
||||
3. 如果没有尺寸后缀,去除尾部的 .扩展名
|
||||
4. 加上 @ 前缀
|
||||
|
||||
返回: @lQLPD4JNnliqBq3NBQDNA8Cw
|
||||
"""
|
||||
url = url.strip()
|
||||
|
||||
# 提取路径部分
|
||||
parsed = urlparse(url)
|
||||
path = parsed.path # e.g. /media/lQLPxxx_960_1280.png
|
||||
|
||||
# 取 /media/ 之后的部分
|
||||
media_prefix = "/media/"
|
||||
idx = path.find(media_prefix)
|
||||
if idx == -1:
|
||||
print(f"错误:URL 中未找到 /media/ 路径: {url}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
raw = path[idx + len(media_prefix):] # e.g. lQLPxxx_960_1280.png
|
||||
|
||||
if not raw:
|
||||
print(f"错误:/media/ 后无内容: {url}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 尝试去除 _数字_数字.扩展名 后缀(如 _960_1280.png)
|
||||
match = re.match(r'^(.+?)(_\d+_\d+\.\w+)$', raw)
|
||||
if match:
|
||||
media_id = match.group(1)
|
||||
else:
|
||||
# 没有尺寸后缀,尝试去除 .扩展名
|
||||
match2 = re.match(r'^(.+)\.\w+$', raw)
|
||||
if match2:
|
||||
media_id = match2.group(1)
|
||||
else:
|
||||
# 无后缀,直接使用
|
||||
media_id = raw
|
||||
|
||||
return f"@{media_id}"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="从 dt_media_upload 返回的 URL 中提取 mediaId"
|
||||
)
|
||||
parser.add_argument("url", help="dt_media_upload 返回的文件 URL")
|
||||
args = parser.parse_args()
|
||||
|
||||
media_id = extract_media_id(args.url)
|
||||
print(media_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// MCP architecture, products are discovered dynamically from MCP servers,
|
||||
// and their availability depends on the test environment's fixture data.
|
||||
|
||||
func TestPublicMCPHelpShowsReviewedURLCommand(t *testing.T) {
|
||||
func TestHiddenMCPHelpIsReachable(t *testing.T) {
|
||||
cmd := app.NewRootCommand()
|
||||
var out strings.Builder
|
||||
cmd.SetOut(&out)
|
||||
@@ -25,8 +25,7 @@ func TestPublicMCPHelpShowsReviewedURLCommand(t *testing.T) {
|
||||
}
|
||||
|
||||
got := out.String()
|
||||
if !strings.Contains(got, "管理经过审核并纳入 Schema 的 MCP 服务连接辅助能力") ||
|
||||
!strings.Contains(got, "url") {
|
||||
if !strings.Contains(got, "canonical MCP command surface is disabled") {
|
||||
t.Fatalf("mcp help missing expected text:\n%s", got)
|
||||
}
|
||||
}
|
||||
|
||||
+15
-59
@@ -1,7 +1,7 @@
|
||||
[root]
|
||||
runnable: true
|
||||
hidden: false
|
||||
commands: agoal, aisearch, aitable, api, attendance, audit, auth, calendar, chat, completion, config, contact, dev, devapp, devdoc, ding, doc, doctor, drive, event, help, live, mail, mcp, minutes, oa, pat, plugin, profile, recovery, report, schema, sheet, skill, todo, upgrade, version, wiki
|
||||
commands: agoal, aisearch, aitable, api, attendance, audit, auth, calendar, chat, completion, config, contact, dev, devapp, devdoc, ding, doc, doctor, drive, event, help, live, mail, minutes, oa, pat, plugin, profile, recovery, report, schema, sheet, skill, todo, upgrade, version, wiki
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=persistent, --client-secret:string|required=false|hidden=false|no-opt=""|scope=persistent, --debug:bool|required=false|hidden=false|no-opt="true"|scope=persistent, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=persistent, --fields:string|required=false|hidden=false|no-opt=""|scope=persistent, -f/--format:string|required=false|hidden=false|no-opt=""|scope=persistent, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=persistent, --mock:bool|required=false|hidden=false|no-opt="true"|scope=persistent, -o/--output:string|required=false|hidden=true|no-opt=""|scope=persistent, --profile:string|required=false|hidden=false|no-opt=""|scope=persistent, --timeout:int|required=false|hidden=false|no-opt=""|scope=persistent, --token:string|required=false|hidden=true|no-opt=""|scope=persistent, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=persistent, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=persistent
|
||||
|
||||
[agoal]
|
||||
@@ -1738,7 +1738,7 @@
|
||||
[chat.+bot-find]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --cursor:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --keyword:string|required=false|hidden=true|no-opt=""|scope=local, --limit:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --query:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --cursor:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --limit:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --query:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.+bot-search]
|
||||
runnable: true
|
||||
@@ -1748,7 +1748,7 @@
|
||||
[chat.+broadcast]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --ai-tag:bool|required=false|hidden=false|no-opt="true"|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --text:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --to:stringSlice|required=false|hidden=false|no-opt=""|scope=local, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --text:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --to:stringSlice|required=false|hidden=false|no-opt=""|scope=local, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.+category-create]
|
||||
runnable: true
|
||||
@@ -1833,7 +1833,7 @@
|
||||
[chat.+chat-search]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --cursor:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --exclude-muted:bool|required=false|hidden=false|no-opt="true"|scope=local, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --keyword:string|required=false|hidden=true|no-opt=""|scope=local, --limit:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --query:string|required=false|hidden=false|no-opt=""|scope=local, --size:int|required=false|hidden=true|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --cursor:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --exclude-muted:bool|required=false|hidden=false|no-opt="true"|scope=local, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --limit:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --query:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.+chat-set-admin]
|
||||
runnable: true
|
||||
@@ -1878,7 +1878,7 @@
|
||||
[chat.+dm]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --ai-tag:bool|required=false|hidden=false|no-opt="true"|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --text:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --to:string|required=false|hidden=false|no-opt=""|scope=local, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --text:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --to:string|required=false|hidden=false|no-opt=""|scope=local, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.+group-members]
|
||||
runnable: true
|
||||
@@ -1888,7 +1888,7 @@
|
||||
[chat.+messages-list-direct]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, --forward:bool|required=false|hidden=false|no-opt="true"|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --limit:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --open-dingtalk-id:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --size:int|required=false|hidden=true|no-opt=""|scope=local, --time:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --user:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, --forward:bool|required=false|hidden=false|no-opt="true"|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --limit:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --open-dingtalk-id:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --time:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --user:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.+messages-list-pin]
|
||||
runnable: true
|
||||
@@ -1913,7 +1913,7 @@
|
||||
[chat.+messages-read-status]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --conversation-id:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, --group:string|required=false|hidden=true|no-opt=""|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --id:string|required=false|hidden=true|no-opt=""|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --message-id:string|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --users:stringSlice|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --conversation-id:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --message-id:string|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --users:stringSlice|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.+messages-send-by-webhook]
|
||||
runnable: true
|
||||
@@ -1933,7 +1933,7 @@
|
||||
[chat.+send-to-group]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --ai-tag:bool|required=false|hidden=false|no-opt="true"|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, --group:string|required=false|hidden=false|no-opt=""|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --text:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, --group:string|required=false|hidden=false|no-opt=""|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --text:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.+unread-chats]
|
||||
runnable: true
|
||||
@@ -2317,7 +2317,7 @@
|
||||
[chat.message.download-media]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --message-id:string|required=true|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --msg-id:string|required=false|hidden=true|no-opt=""|scope=local, --open-conversation-id:string|required=true|hidden=false|no-opt=""|scope=local, --open-message-id:string|required=false|hidden=true|no-opt=""|scope=local, --output:string|required=true|hidden=false|no-opt=""|scope=local, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --resource-id:string|required=true|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --type:string|required=true|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --message-id:string|required=true|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --open-conversation-id:string|required=true|hidden=false|no-opt=""|scope=local, --output:string|required=true|hidden=false|no-opt=""|scope=local, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --resource-id:string|required=true|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --type:string|required=true|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[chat.message.forward]
|
||||
runnable: true
|
||||
@@ -2539,7 +2539,7 @@
|
||||
[contact]
|
||||
runnable: true
|
||||
hidden: false
|
||||
commands: +by-mobile, +dept-members, +list-dept-members, +list-followings, +list-role-members, +list-roles, +list-sub-depts, +lookup, +me, +org, +resolve-dept, +search-mobile, +search-user, +team, account, dept, label, org, relation, user
|
||||
commands: +by-mobile, +dept-members, +list-dept-members, +list-followings, +list-role-members, +list-roles, +list-sub-depts, +lookup, +me, +org, +resolve-dept, +search-mobile, +search-user, +team, dept, label, relation, user
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.+by-mobile]
|
||||
@@ -2612,17 +2612,6 @@
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --name:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.account]
|
||||
runnable: true
|
||||
hidden: false
|
||||
commands: create
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.account.create]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dept-ids:string|required=false|hidden=false|no-opt=""|scope=local, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --email:string|required=false|hidden=false|no-opt=""|scope=local, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --login-id:string|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --org-user-mobile:string|required=false|hidden=false|no-opt=""|scope=local, --org-user-name:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --send-pwd-via-sms:bool|required=false|hidden=false|no-opt="true"|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.dept]
|
||||
runnable: true
|
||||
hidden: false
|
||||
@@ -2671,17 +2660,6 @@
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --id:string|required=false|hidden=false|no-opt=""|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --label-id:string|required=false|hidden=true|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --role-id:string|required=false|hidden=true|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.org]
|
||||
runnable: true
|
||||
hidden: false
|
||||
commands: create
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.org.create]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --creator-username:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --org-name:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.relation]
|
||||
runnable: true
|
||||
hidden: false
|
||||
@@ -2696,7 +2674,7 @@
|
||||
[contact.user]
|
||||
runnable: true
|
||||
hidden: false
|
||||
commands: dismission, get, get-self, invite, profile, search, search-mobile
|
||||
commands: dismission, get, get-self, profile, search, search-mobile
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.user.dismission]
|
||||
@@ -2721,11 +2699,6 @@
|
||||
aliases: current, me, self, whoami
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.user.invite]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --depts:string|required=false|hidden=false|no-opt=""|scope=local, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --org-user-mobile:string|required=false|hidden=false|no-opt=""|scope=local, --org-user-name:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[contact.user.profile]
|
||||
runnable: true
|
||||
hidden: false
|
||||
@@ -2819,7 +2792,7 @@
|
||||
[dev.app.get]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --app-key:string|required=false|hidden=false|no-opt=""|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --unified-app-id:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --unified-app-id:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[dev.app.list]
|
||||
runnable: true
|
||||
@@ -3259,7 +3232,7 @@
|
||||
[doc.+share-doc]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --ai-tag:bool|required=false|hidden=false|no-opt="true"|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --note:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --to:string|required=false|hidden=false|no-opt=""|scope=local, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --url:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --note:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --to:string|required=false|hidden=false|no-opt=""|scope=local, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --url:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[doc.+template-list]
|
||||
runnable: true
|
||||
@@ -3664,7 +3637,7 @@
|
||||
[event.consume]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --as:string|required=false|hidden=true|no-opt=""|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --compact:bool|required=false|hidden=false|no-opt="true"|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --debug-raw-events:bool|required=false|hidden=false|no-opt="true"|scope=local, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=local, --duration:duration|required=false|hidden=false|no-opt=""|scope=local, --ephemeral:bool|required=false|hidden=false|no-opt="true"|scope=local, --event-types:stringSlice|required=false|hidden=false|no-opt=""|scope=local, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, --filter:string|required=false|hidden=false|no-opt=""|scope=local, --filter-json:string|required=false|hidden=false|no-opt=""|scope=local, --flatten:bool|required=false|hidden=false|no-opt="true"|scope=local, --force:bool|required=false|hidden=false|no-opt="true"|scope=local, --foreground:bool|required=false|hidden=false|no-opt="true"|scope=local, -f/--format:string|required=false|hidden=false|no-opt=""|scope=local, --group:string|required=false|hidden=false|no-opt=""|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --max-events:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --name:string|required=false|hidden=false|no-opt=""|scope=local, --open-dingtalk-id:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --output-dir:string|required=false|hidden=false|no-opt=""|scope=local, --personal-event-base-url:string|required=false|hidden=false|no-opt=""|scope=local, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --query:string|required=false|hidden=false|no-opt=""|scope=local, --quiet:bool|required=false|hidden=false|no-opt="true"|scope=local, --route:stringArray|required=false|hidden=false|no-opt=""|scope=local, --rule:string|required=false|hidden=false|no-opt=""|scope=local, --stream-source-id:string|required=false|hidden=false|no-opt=""|scope=local, --stream-ticket-mode:string|required=false|hidden=false|no-opt=""|scope=local, --stream-ticket-url:string|required=false|hidden=false|no-opt=""|scope=local, --subscribe-id:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --ttl:duration|required=false|hidden=false|no-opt=""|scope=local, --user:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --as:string|required=false|hidden=true|no-opt=""|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --compact:bool|required=false|hidden=false|no-opt="true"|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --debug-raw-events:bool|required=false|hidden=false|no-opt="true"|scope=local, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=local, --duration:duration|required=false|hidden=false|no-opt=""|scope=local, --ephemeral:bool|required=false|hidden=false|no-opt="true"|scope=local, --event-types:stringSlice|required=false|hidden=false|no-opt=""|scope=local, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, --filter:string|required=false|hidden=false|no-opt=""|scope=local, --filter-json:string|required=false|hidden=false|no-opt=""|scope=local, --force:bool|required=false|hidden=false|no-opt="true"|scope=local, --foreground:bool|required=false|hidden=false|no-opt="true"|scope=local, -f/--format:string|required=false|hidden=false|no-opt=""|scope=local, --group:string|required=false|hidden=false|no-opt=""|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --max-events:int|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --name:string|required=false|hidden=false|no-opt=""|scope=local, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --output-dir:string|required=false|hidden=false|no-opt=""|scope=local, --personal-event-base-url:string|required=false|hidden=false|no-opt=""|scope=local, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --query:string|required=false|hidden=false|no-opt=""|scope=local, --quiet:bool|required=false|hidden=false|no-opt="true"|scope=local, --route:stringArray|required=false|hidden=false|no-opt=""|scope=local, --rule:string|required=false|hidden=false|no-opt=""|scope=local, --stream-source-id:string|required=false|hidden=false|no-opt=""|scope=local, --stream-ticket-mode:string|required=false|hidden=false|no-opt=""|scope=local, --stream-ticket-url:string|required=false|hidden=false|no-opt=""|scope=local, --subscribe-id:string|required=false|hidden=false|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, --ttl:duration|required=false|hidden=false|no-opt=""|scope=local, --user:string|required=false|hidden=false|no-opt=""|scope=local, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[event.list]
|
||||
runnable: true
|
||||
@@ -3674,7 +3647,7 @@
|
||||
[event.schema]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --as:string|required=false|hidden=true|no-opt=""|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, --flatten:bool|required=false|hidden=false|no-opt="true"|scope=local, -f/--format:string|required=false|hidden=false|no-opt=""|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
flags: --as:string|required=false|hidden=true|no-opt=""|scope=local, --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=local, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[event.status]
|
||||
runnable: true
|
||||
@@ -4144,23 +4117,6 @@
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --cursor:string|required=false|hidden=false|no-opt=""|scope=local, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --email:string|required=false|hidden=false|no-opt=""|scope=local, --employee-no:string|required=false|hidden=false|no-opt=""|scope=local, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --keyword:string|required=false|hidden=false|no-opt=""|scope=local, --limit:string|required=false|hidden=false|no-opt=""|scope=local, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --size:string|required=false|hidden=true|no-opt=""|scope=local, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[mcp]
|
||||
runnable: true
|
||||
hidden: false
|
||||
commands: url
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[mcp.url]
|
||||
runnable: true
|
||||
hidden: false
|
||||
commands: get
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[mcp.url.get]
|
||||
runnable: true
|
||||
hidden: false
|
||||
flags: --client-id:string|required=false|hidden=false|no-opt=""|scope=inherited, --client-secret:string|required=false|hidden=false|no-opt=""|scope=inherited, --debug:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --dry-run:bool|required=false|hidden=false|no-opt="true"|scope=inherited, --fields:string|required=false|hidden=false|no-opt=""|scope=inherited, -f/--format:string|required=false|hidden=false|no-opt=""|scope=inherited, -h/--help:bool|required=false|hidden=false|no-opt="true"|scope=local, --jq:string|required=false|hidden=false|no-opt=""|scope=inherited, --mock:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -o/--output:string|required=false|hidden=true|no-opt=""|scope=inherited, --profile:string|required=false|hidden=false|no-opt=""|scope=inherited, --timeout:int|required=false|hidden=false|no-opt=""|scope=inherited, --token:string|required=false|hidden=true|no-opt=""|scope=inherited, -v/--verbose:bool|required=false|hidden=false|no-opt="true"|scope=inherited, -y/--yes:bool|required=false|hidden=false|no-opt="true"|scope=inherited
|
||||
|
||||
[minutes]
|
||||
runnable: true
|
||||
hidden: false
|
||||
|
||||
Reference in New Issue
Block a user