feat(devdoc): support cursor pagination
This commit is contained in:
1 parent
0a8de62041
commit
1921e5e37e
13 files changed
+552
-32
No files matched your search
@@ -492,7 +492,7 @@ dws chat message send-by-bot --robot-code BOT_CODE --group GROUP_ID \
|
||||
| Mail | `mail` | 18 | `mailbox` `message` `draft` `folder` `tag` `thread` `attachment` `user` | List mailboxes, KQL message search, read & send messages, drafts, folders, tags, threads, attachments, address-book user search |
|
||||
| Sheet | `sheet` | 23 | `range` `filter-view` (top-level: `create` `new` `list` `info` `read` `get` `update` `find` `replace` `append` `merge-cells` `unmerge-cells` `add-dimension` `insert-dimension` `delete-dimension` `move-dimension` `update-dimension` `write-image`) | Online spreadsheet (`contentType=ALIDOC`, `extension=axls`): worksheet CRUD, range read / write / append, dimension ops, cell merge / unmerge, find / replace, named filter views + sheet-level filters, image write |
|
||||
| Wiki | `wiki` | 21 | `space` `member` `node` `doc` `file` | Knowledge base management: spaces (`create` / `get` / `list` / `search`), members (`add` / `list` / `update`), node tree, docs & files |
|
||||
| DevDoc | `devdoc` | 1 | `article` | Search the DingTalk Open Platform documentation |
|
||||
| DevDoc | `devdoc` | 2 | `article` `error` | Search the DingTalk Open Platform documentation and diagnose API errors |
|
||||
| AI Search | `aisearch` | 3 | `person` | Enterprise people search by name / department / position / duty / supervisor / subordinate / phone / job-number (single command, multi-dimension filter) |
|
||||
| Live | `live` | 1 | `stream` | DingTalk live streaming: list my lives |
|
||||
| Raw API | `api` | 1 | — | Call any DingTalk OpenAPI directly (api / oapi dual-form), with automatic app-level token management |
|
||||
|
||||
+1
-1
@@ -488,7 +488,7 @@ dws chat message send-by-bot --robot-code BOT_CODE --group GROUP_ID \
|
||||
| 邮箱 | `mail` | 18 | `mailbox` `message` `draft` `folder` `tag` `thread` `attachment` `user` | 邮箱地址列表、KQL 邮件搜索、读取与发送邮件、草稿、文件夹、标签、会话、附件、通讯录用户搜索 |
|
||||
| 在线电子表格 | `sheet` | 23 | `range` `filter-view`(顶层:`create` `new` `list` `info` `read` `get` `update` `find` `replace` `append` `merge-cells` `unmerge-cells` `add-dimension` `insert-dimension` `delete-dimension` `move-dimension` `update-dimension` `write-image`) | 在线电子表格(`contentType=ALIDOC`、`extension=axls`):工作表 CRUD、区域读写/追加、行列操作、合并/取消合并、查找替换、命名筛选视图 + 表级筛选、写入图片 |
|
||||
| 知识库 | `wiki` | 21 | `space` `member` `node` `doc` `file` | 知识库管理:空间(`create` / `get` / `list` / `search`)、成员(`add` / `list` / `update`)、节点树、文档与文件 |
|
||||
| 开发者文档 | `devdoc` | 1 | `article` | 搜索钉钉开放平台文档 |
|
||||
| 开发者文档 | `devdoc` | 2 | `article` `error` | 搜索钉钉开放平台文档并排查 API 调用错误 |
|
||||
| AI 搜问 | `aisearch` | 3 | `person` | 企业人员搜索:按姓名 / 部门 / 职位 / 职责 / 上级 / 下级 / 手机号 / 工号 多维度过滤(单命令) |
|
||||
| 直播 | `live` | 1 | `stream` | 钉钉直播:查看我的直播列表 |
|
||||
| Raw API | `api` | 1 | — | 直接调用任意钉钉 OpenAPI(api / oapi 双形态),自动管理应用级 Token |
|
||||
|
||||
@@ -36,7 +36,7 @@ Every command inherits these flags (documented here once, not repeated per comma
|
||||
- [`dws calendar` — Calendar](#dws-calendar) · 14 commands
|
||||
- [`dws chat` — Group Chat / IM](#dws-chat) · 23 commands
|
||||
- [`dws contact` — Contact Directory](#dws-contact) · 6 commands
|
||||
- [`dws devdoc` — Open Platform Docs](#dws-devdoc) · 1 commands
|
||||
- [`dws devdoc` — Open Platform Docs](#dws-devdoc) · 2 commands
|
||||
- [`dws ding` — DING Messages](#dws-ding) · 2 commands
|
||||
- [`dws doc` — DingTalk Doc](#dws-doc) · 21 commands
|
||||
- [`dws drive` — DingTalk Drive](#dws-drive) · 6 commands
|
||||
@@ -182,11 +182,12 @@ _Users, departments, and directory lookups._
|
||||
|
||||
_Search the DingTalk Open Platform documentation._
|
||||
|
||||
**1 commands**
|
||||
**2 commands**
|
||||
|
||||
| Command | Description | When to use |
|
||||
|---|---|---|
|
||||
| `dws devdoc article search` | Search the DingTalk Open Platform documentation by keyword. | When the agent needs authoritative API reference or guides to answer a developer question. |
|
||||
| `dws devdoc error diagnose` | Diagnose DingTalk Open Platform API errors by error code, requestId, or context. | When the agent needs troubleshooting material for a failed OpenAPI call. |
|
||||
|
||||
## `dws ding` — DING Messages
|
||||
|
||||
@@ -320,4 +321,3 @@ _Personal todo task management._
|
||||
| `dws todo task get` | Retrieve the full details of a todo item by ID. | When the agent inspects a specific todo's content, due date, and executors. |
|
||||
| `dws todo task list` | List todos for the current user within the current organization. | When the agent surfaces the user's outstanding tasks or builds a daily focus list. |
|
||||
| `dws todo task update` | Update a todo's title, description, due time, or executors. | When the agent edits an existing todo after new information comes in. |
|
||||
|
||||
+192
-5
@@ -14,6 +14,8 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cobracmd"
|
||||
@@ -22,6 +24,12 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
devdocArticleSearchTool = "search_open_platform_docs_rag"
|
||||
devdocArticleSearchLegacyTool = "search_open_platform_docs"
|
||||
devdocErrorDiagnoseTool = "search_open_error_code_rag"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterPublic(func() Handler {
|
||||
return devdocHandler{}
|
||||
@@ -57,6 +65,18 @@ func (devdocHandler) Command(runner executor.Runner) *cobra.Command {
|
||||
}
|
||||
article.AddCommand(newDevdocArticleSearchCommand(runner))
|
||||
root.AddCommand(article)
|
||||
errs := &cobra.Command{
|
||||
Use: "error",
|
||||
Short: "错误排查",
|
||||
Args: cobra.NoArgs,
|
||||
TraverseChildren: true,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cmd.Help()
|
||||
},
|
||||
}
|
||||
errs.AddCommand(newDevdocErrorDiagnoseCommand(runner))
|
||||
root.AddCommand(errs)
|
||||
return root
|
||||
}
|
||||
|
||||
@@ -82,22 +102,144 @@ func newDevdocArticleSearchCommand(runner executor.Runner) *cobra.Command {
|
||||
if size < 1 {
|
||||
size = 10
|
||||
}
|
||||
return runDevdocTool(cmd, runner, "search_open_platform_docs", map[string]any{
|
||||
"keyword": keyword,
|
||||
"page": page,
|
||||
"size": size,
|
||||
})
|
||||
cursor := devdocFlagOrFallback(cmd, "cursor")
|
||||
return runDevdocToolWithFallback(cmd, runner, devdocArticleSearchTool,
|
||||
devdocSearchParams(keyword, page, size, cursor),
|
||||
devdocArticleSearchLegacyTool,
|
||||
devdocLegacySearchParams(keyword, page, size, cursor))
|
||||
},
|
||||
}
|
||||
preferLegacyLeaf(cmd)
|
||||
cmd.Flags().String("query", "", "搜索关键词 (必填)")
|
||||
addDevdocHiddenStringFlag(cmd, "keyword", "--query 的悟空兼容别名")
|
||||
cmd.Flags().Int("page", 1, "分页页码 (从 1 开始,默认 1)")
|
||||
cmd.Flags().String("cursor", "", "分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page")
|
||||
cmd.Flags().Int("size", 10, "分页大小 (默认 10)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newDevdocErrorDiagnoseCommand(runner executor.Runner) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "diagnose",
|
||||
Aliases: []string{"troubleshoot"},
|
||||
Short: "排查开放平台调用错误",
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
errorCode := devdocFlagOrFallback(cmd, "error-code")
|
||||
errorMessage := devdocFlagOrFallback(cmd, "error-message")
|
||||
api := devdocFlagOrFallback(cmd, "api")
|
||||
contextText := devdocFlagOrFallback(cmd, "context")
|
||||
requestID := devdocFlagOrFallback(cmd, "request-id")
|
||||
query := devdocFlagOrFallback(cmd, "query")
|
||||
|
||||
parts := make([]string, 0, 4)
|
||||
for _, part := range []string{query, errorMessage, api, contextText} {
|
||||
if trimmed := strings.TrimSpace(part); trimmed != "" {
|
||||
parts = append(parts, trimmed)
|
||||
}
|
||||
}
|
||||
query = strings.Join(parts, " ")
|
||||
if errorCode == "" && requestID == "" && query == "" {
|
||||
return apperrors.NewValidation("--query, --error-code, or --request-id is required")
|
||||
}
|
||||
page, _ := cmd.Flags().GetInt("page")
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
size, _ := cmd.Flags().GetInt("size")
|
||||
if size < 1 {
|
||||
size = 10
|
||||
}
|
||||
cursor := devdocFlagOrFallback(cmd, "cursor")
|
||||
|
||||
params := map[string]any{
|
||||
"size": size,
|
||||
}
|
||||
if cursor != "" {
|
||||
params["cursor"] = cursor
|
||||
} else {
|
||||
params["page"] = page
|
||||
}
|
||||
if errorCode != "" {
|
||||
params["errorCode"] = errorCode
|
||||
}
|
||||
if query != "" {
|
||||
params["query"] = query
|
||||
}
|
||||
if requestID != "" {
|
||||
params["requestId"] = requestID
|
||||
}
|
||||
|
||||
fallbackQuery := strings.TrimSpace(strings.Join([]string{errorCode, query, requestID}, " "))
|
||||
return runDevdocToolWithFallbacks(cmd, runner, devdocErrorDiagnoseTool, params, []devdocFallbackTool{
|
||||
{tool: devdocArticleSearchTool, params: devdocSearchParams(fallbackQuery, page, size, cursor)},
|
||||
{tool: devdocArticleSearchLegacyTool, params: devdocLegacySearchParams(fallbackQuery, page, size, cursor)},
|
||||
})
|
||||
},
|
||||
}
|
||||
cmd.Flags().String("query", "", "原始排查问题")
|
||||
cmd.Flags().String("error-code", "", "错误码")
|
||||
cmd.Flags().String("error-message", "", "错误描述,会合并进原始问题")
|
||||
cmd.Flags().String("api", "", "API 名称,会合并进原始问题作为补充检索词")
|
||||
cmd.Flags().String("context", "", "额外排查上下文,会合并进原始问题")
|
||||
cmd.Flags().String("request-id", "", "开放平台 requestId")
|
||||
cmd.Flags().Int("page", 1, "分页页码 (从 1 开始,默认 1)")
|
||||
cmd.Flags().String("cursor", "", "分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page")
|
||||
cmd.Flags().Int("size", 10, "分页大小 (默认 10)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func devdocSearchParams(keyword string, page int, size int, cursor string) map[string]any {
|
||||
params := map[string]any{
|
||||
"keyword": keyword,
|
||||
"size": size,
|
||||
}
|
||||
if cursor != "" {
|
||||
params["cursor"] = cursor
|
||||
} else {
|
||||
params["page"] = page
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func devdocLegacySearchParams(keyword string, page int, size int, cursor string) map[string]any {
|
||||
return map[string]any{
|
||||
"keyword": keyword,
|
||||
"page": devdocPageFromCursor(page, cursor),
|
||||
"size": size,
|
||||
}
|
||||
}
|
||||
|
||||
func devdocPageFromCursor(page int, cursor string) int {
|
||||
if cursor == "" {
|
||||
return page
|
||||
}
|
||||
cursorPage, err := strconv.Atoi(cursor)
|
||||
if err == nil && cursorPage > 0 {
|
||||
return cursorPage
|
||||
}
|
||||
return page
|
||||
}
|
||||
|
||||
func runDevdocTool(cmd *cobra.Command, runner executor.Runner, tool string, params map[string]any) error {
|
||||
return runDevdocToolWithFallback(cmd, runner, tool, params, "", nil)
|
||||
}
|
||||
|
||||
type devdocFallbackTool struct {
|
||||
tool string
|
||||
params map[string]any
|
||||
}
|
||||
|
||||
func runDevdocToolWithFallback(cmd *cobra.Command, runner executor.Runner, tool string, params map[string]any, fallbackTool string, fallbackParams map[string]any) error {
|
||||
var fallbacks []devdocFallbackTool
|
||||
if fallbackTool != "" {
|
||||
fallbacks = append(fallbacks, devdocFallbackTool{tool: fallbackTool, params: fallbackParams})
|
||||
}
|
||||
return runDevdocToolWithFallbacks(cmd, runner, tool, params, fallbacks)
|
||||
}
|
||||
|
||||
func runDevdocToolWithFallbacks(cmd *cobra.Command, runner executor.Runner, tool string, params map[string]any, fallbacks []devdocFallbackTool) error {
|
||||
invocation := executor.NewHelperInvocation(
|
||||
cobracmd.LegacyCommandPath(cmd),
|
||||
"devdoc",
|
||||
@@ -108,12 +250,57 @@ func runDevdocTool(cmd *cobra.Command, runner executor.Runner, tool string, para
|
||||
return writeCommandPayload(cmd, invocation)
|
||||
}
|
||||
result, err := runner.Run(cmd.Context(), invocation)
|
||||
for _, fallback := range fallbacks {
|
||||
if err == nil || !isDevdocToolNotFoundError(err) {
|
||||
break
|
||||
}
|
||||
if fallback.tool == "" {
|
||||
continue
|
||||
}
|
||||
fallbackParams := fallback.params
|
||||
if fallbackParams == nil {
|
||||
fallbackParams = params
|
||||
}
|
||||
fallbackInvocation := executor.NewHelperInvocation(
|
||||
cobracmd.LegacyCommandPath(cmd),
|
||||
"devdoc",
|
||||
fallback.tool,
|
||||
fallbackParams,
|
||||
)
|
||||
result, err = runner.Run(cmd.Context(), fallbackInvocation)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeCommandPayload(cmd, result)
|
||||
}
|
||||
|
||||
func isDevdocToolNotFoundError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
msg := normalizedDevdocErrorText(err)
|
||||
return strings.Contains(msg, "tool not found") ||
|
||||
strings.Contains(msg, "unknown tool") ||
|
||||
strings.Contains(msg, "mcp_tool_not_found") ||
|
||||
strings.Contains(msg, "tool metadata api error") ||
|
||||
strings.Contains(msg, "未找到指定工具")
|
||||
}
|
||||
|
||||
func normalizedDevdocErrorText(err error) string {
|
||||
parts := []string{strings.ToLower(err.Error())}
|
||||
var typed *apperrors.Error
|
||||
if stderrors.As(err, &typed) && typed != nil {
|
||||
parts = append(parts,
|
||||
strings.ToLower(typed.Reason),
|
||||
strings.ToLower(typed.ServerDiag.ServerErrorCode),
|
||||
strings.ToLower(typed.ServerDiag.TechnicalDetail),
|
||||
strings.ToLower(typed.Hint),
|
||||
)
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
func devdocFlagOrFallback(cmd *cobra.Command, primary string, aliases ...string) string {
|
||||
names := append([]string{primary}, aliases...)
|
||||
for _, name := range names {
|
||||
|
||||
@@ -16,17 +16,27 @@ package helpers
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor"
|
||||
)
|
||||
|
||||
type devdocCommandRunner struct {
|
||||
last executor.Invocation
|
||||
last executor.Invocation
|
||||
calls []executor.Invocation
|
||||
errors map[string]error
|
||||
}
|
||||
|
||||
func (r *devdocCommandRunner) Run(_ context.Context, invocation executor.Invocation) (executor.Result, error) {
|
||||
r.last = invocation
|
||||
r.calls = append(r.calls, invocation)
|
||||
if r.errors != nil {
|
||||
if err := r.errors[invocation.Tool]; err != nil {
|
||||
return executor.Result{}, err
|
||||
}
|
||||
}
|
||||
return executor.Result{Invocation: invocation}, nil
|
||||
}
|
||||
|
||||
@@ -43,8 +53,8 @@ func TestDevdocArticleSearchAcceptsWukongKeywordAlias(t *testing.T) {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("Execute() error = %v\nstderr:\n%s", err, errOut.String())
|
||||
}
|
||||
if runner.last.Tool != "search_open_platform_docs" {
|
||||
t.Fatalf("tool = %q, want search_open_platform_docs", runner.last.Tool)
|
||||
if runner.last.Tool != devdocArticleSearchTool {
|
||||
t.Fatalf("tool = %q, want %s", runner.last.Tool, devdocArticleSearchTool)
|
||||
}
|
||||
if got := runner.last.Params["keyword"]; got != "openConversationId" {
|
||||
t.Fatalf("keyword = %#v, want openConversationId", got)
|
||||
@@ -57,6 +67,161 @@ func TestDevdocArticleSearchAcceptsWukongKeywordAlias(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevdocArticleSearchPassesCursor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runner := &devdocCommandRunner{}
|
||||
cmd := devdocHandler{}.Command(runner)
|
||||
var out, errOut bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&errOut)
|
||||
cmd.SetArgs([]string{"article", "search", "--query", "Webhook", "--cursor", "3", "--size", "5"})
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("Execute() error = %v\nstderr:\n%s", err, errOut.String())
|
||||
}
|
||||
if runner.last.Tool != devdocArticleSearchTool {
|
||||
t.Fatalf("tool = %q, want %s", runner.last.Tool, devdocArticleSearchTool)
|
||||
}
|
||||
if got := runner.last.Params["cursor"]; got != "3" {
|
||||
t.Fatalf("cursor = %#v, want 3", got)
|
||||
}
|
||||
if _, ok := runner.last.Params["page"]; ok {
|
||||
t.Fatalf("page must be omitted when cursor is set: %#v", runner.last.Params)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevdocErrorDiagnoseBuildsErrorSearchQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runner := &devdocCommandRunner{}
|
||||
cmd := devdocHandler{}.Command(runner)
|
||||
var out, errOut bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&errOut)
|
||||
cmd.SetArgs([]string{
|
||||
"error", "diagnose",
|
||||
"--error-code", "40014",
|
||||
"--error-message", "不合法的access_token",
|
||||
"--api", "获取用户信息",
|
||||
"--cursor", "2",
|
||||
"--page", "2",
|
||||
"--size", "3",
|
||||
})
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("Execute() error = %v\nstderr:\n%s", err, errOut.String())
|
||||
}
|
||||
if runner.last.Tool != devdocErrorDiagnoseTool {
|
||||
t.Fatalf("tool = %q, want %s", runner.last.Tool, devdocErrorDiagnoseTool)
|
||||
}
|
||||
if got := runner.last.Params["errorCode"]; got != "40014" {
|
||||
t.Fatalf("errorCode = %#v, want 40014", got)
|
||||
}
|
||||
if got := runner.last.Params["query"]; got != "不合法的access_token 获取用户信息" {
|
||||
t.Fatalf("query = %#v, want merged message and api", got)
|
||||
}
|
||||
if _, ok := runner.last.Params["page"]; ok {
|
||||
t.Fatalf("page must be omitted when cursor is set: %#v", runner.last.Params)
|
||||
}
|
||||
if got := runner.last.Params["cursor"]; got != "2" {
|
||||
t.Fatalf("cursor = %#v, want 2", got)
|
||||
}
|
||||
if got := runner.last.Params["size"]; got != 3 {
|
||||
t.Fatalf("size = %#v, want 3", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevdocErrorDiagnoseFallsBackToArticleSearchTools(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runner := &devdocCommandRunner{
|
||||
errors: map[string]error{
|
||||
devdocErrorDiagnoseTool: errors.New("PARAM_ERROR - 未找到指定工具"),
|
||||
devdocArticleSearchTool: errors.New("unknown tool"),
|
||||
},
|
||||
}
|
||||
cmd := devdocHandler{}.Command(runner)
|
||||
var out, errOut bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&errOut)
|
||||
cmd.SetArgs([]string{
|
||||
"error", "diagnose",
|
||||
"--error-code", "40014",
|
||||
"--query", "获取用户信息",
|
||||
"--request-id", "req-1",
|
||||
"--cursor", "2",
|
||||
})
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("Execute() error = %v\nstderr:\n%s", err, errOut.String())
|
||||
}
|
||||
if len(runner.calls) != 3 {
|
||||
t.Fatalf("calls = %d, want 3", len(runner.calls))
|
||||
}
|
||||
if got := runner.calls[0].Tool; got != devdocErrorDiagnoseTool {
|
||||
t.Fatalf("first tool = %q, want %s", got, devdocErrorDiagnoseTool)
|
||||
}
|
||||
if got := runner.calls[1].Tool; got != devdocArticleSearchTool {
|
||||
t.Fatalf("second tool = %q, want %s", got, devdocArticleSearchTool)
|
||||
}
|
||||
if got := runner.calls[1].Params["cursor"]; got != "2" {
|
||||
t.Fatalf("RAG fallback cursor = %#v, want 2", got)
|
||||
}
|
||||
if _, ok := runner.calls[1].Params["page"]; ok {
|
||||
t.Fatalf("RAG fallback page must be omitted when cursor is set: %#v", runner.calls[1].Params)
|
||||
}
|
||||
if got := runner.calls[2].Tool; got != devdocArticleSearchLegacyTool {
|
||||
t.Fatalf("third tool = %q, want %s", got, devdocArticleSearchLegacyTool)
|
||||
}
|
||||
if got := runner.last.Params["keyword"]; got != "40014 获取用户信息 req-1" {
|
||||
t.Fatalf("fallback keyword = %#v, want merged diagnostic keyword", got)
|
||||
}
|
||||
if _, ok := runner.last.Params["cursor"]; ok {
|
||||
t.Fatalf("legacy fallback params must not include cursor: %#v", runner.last.Params)
|
||||
}
|
||||
if got := runner.last.Params["page"]; got != 2 {
|
||||
t.Fatalf("legacy fallback page = %#v, want cursor-derived page 2", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevdocArticleSearchFallsBackOnStructuredToolMetadataError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runner := &devdocCommandRunner{
|
||||
errors: map[string]error{
|
||||
devdocArticleSearchTool: apperrors.NewAPI("business error: success=false",
|
||||
apperrors.WithReason("business_error"),
|
||||
apperrors.WithServerDiag(apperrors.ServerDiagnostics{
|
||||
ServerErrorCode: "PARAM_ERROR",
|
||||
TechnicalDetail: "Tool metadata API error: PARAM_ERROR - 未找到指定工具",
|
||||
}),
|
||||
),
|
||||
},
|
||||
}
|
||||
cmd := devdocHandler{}.Command(runner)
|
||||
var out, errOut bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&errOut)
|
||||
cmd.SetArgs([]string{"article", "search", "--query", "Webhook", "--cursor", "3"})
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("Execute() error = %v\nstderr:\n%s", err, errOut.String())
|
||||
}
|
||||
if len(runner.calls) != 2 {
|
||||
t.Fatalf("calls = %d, want 2", len(runner.calls))
|
||||
}
|
||||
if got := runner.calls[0].Tool; got != devdocArticleSearchTool {
|
||||
t.Fatalf("first tool = %q, want %s", got, devdocArticleSearchTool)
|
||||
}
|
||||
if got := runner.calls[1].Tool; got != devdocArticleSearchLegacyTool {
|
||||
t.Fatalf("second tool = %q, want %s", got, devdocArticleSearchLegacyTool)
|
||||
}
|
||||
if got := runner.calls[1].Params["page"]; got != 3 {
|
||||
t.Fatalf("legacy fallback page = %#v, want cursor-derived page 3", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevdocArticleSearchAcceptsPositionalKeyword(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -13,21 +13,45 @@ Example:
|
||||
dws devdoc article search --query "OAuth2 接入"
|
||||
dws devdoc article search --keyword "机器人" --size 10
|
||||
dws devdoc article search --query "消息卡片" --page 2 --size 5
|
||||
dws devdoc article search --query "消息卡片" --cursor "<NEXT_CURSOR>" --size 5
|
||||
Flags:
|
||||
--query string 搜索关键词 (必填)
|
||||
--keyword string 搜索关键词 (--query 的别名)
|
||||
--page int 分页页码 (从 1 开始,默认 1)
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
--size int 分页大小 (默认 10)
|
||||
```
|
||||
|
||||
### 排查调用错误
|
||||
```
|
||||
Usage:
|
||||
dws devdoc error diagnose [flags]
|
||||
Example:
|
||||
dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token"
|
||||
dws devdoc error diagnose --request-id "<REQUEST_ID>" --query "接口调用失败"
|
||||
dws devdoc error troubleshoot --error-message "token is illegal" --api "获取用户信息"
|
||||
Flags:
|
||||
--query string 原始排查问题
|
||||
--error-code string 错误码
|
||||
--error-message string 错误描述,会合并进原始问题
|
||||
--api string API 名称,会合并进原始问题
|
||||
--context string 额外排查上下文
|
||||
--request-id string 开放平台 requestId
|
||||
--page int 分页页码 (从 1 开始,默认 1)
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
--size int 分页大小 (默认 10)
|
||||
```
|
||||
|
||||
## 意图判断
|
||||
|
||||
用户问开放平台 API / 字段 / 错误码 / SDK / 鉴权 / 回调 / 配额相关的技术细节:
|
||||
- 走 `devdoc article search`,把用户问的关键短语作为位置参数或 `--query`
|
||||
- 用户已经给出错误码、错误描述或 requestId:
|
||||
- 先走 `devdoc error diagnose`;若后端返回 `PARAM_ERROR - 未找到指定工具` / `unknown tool`,降级 `devdoc article search`,同时把该结果标记为 `needs_gateway_tool_registration`
|
||||
|
||||
关键区分:
|
||||
- devdoc(钉钉**开放平台**开发者文档,面向研发) vs doc(钉钉在线文档,面向普通用户内容)
|
||||
- devdoc 只做搜索,不做读取;命中条目返回标题、摘要、文档链接,由 Agent 引用链接或进一步浏览
|
||||
- devdoc 只做搜索/诊断,不做读取;命中条目返回标题、摘要、文档链接,由 Agent 引用链接或进一步浏览
|
||||
|
||||
## 核心工作流
|
||||
|
||||
@@ -41,13 +65,38 @@ dws devdoc article search "MCP" --format json
|
||||
# 命中结果多时翻页
|
||||
dws devdoc article search --query "消息卡片" --page 2 --size 5 --format json
|
||||
|
||||
# 推荐动态翻页:优先使用上一页返回的 nextCursor
|
||||
dws devdoc article search --query "消息卡片" --cursor "<NEXT_CURSOR>" --size 5 --format json
|
||||
|
||||
# 查错误码 / 字段含义
|
||||
dws devdoc article search --query "errcode 40078" --format json
|
||||
|
||||
# 有明确错误码时优先走错误诊断
|
||||
dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token" --format json
|
||||
|
||||
# 有 requestId 时尝试链路排查;不是所有接口都会返回 requestId
|
||||
dws devdoc error diagnose --request-id "<REQUEST_ID>" --query "接口调用失败" --format json
|
||||
|
||||
# 错误排查结果继续翻页
|
||||
dws devdoc error diagnose --error-code "40014" --query "access_token" --cursor "<NEXT_CURSOR>" --format json
|
||||
```
|
||||
|
||||
## 上线/注入验收
|
||||
|
||||
```bash
|
||||
dws devdoc --help
|
||||
dws devdoc article search --query "OAuth2 接入" --dry-run --format json
|
||||
dws devdoc error diagnose --error-code "40014" --query "access_token" --dry-run --format json
|
||||
dws schema devdoc.search_open_platform_docs_rag --format json || true
|
||||
dws schema devdoc.search_open_error_code_rag --format json || true
|
||||
```
|
||||
|
||||
- `--help` / `--dry-run` 只能证明 CLI 命令面和映射存在,不能证明后端工具已注册
|
||||
- 真实调用若返回 `PARAM_ERROR - 未找到指定工具`,不是用户参数问题;记录为网关/工具注册待闭环,并降级到 article search 或本地已挂载 Markdown
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 关键词必填;可用位置参数、`--query` 或兼容别名 `--keyword`。建议传用户原话里的关键名词(API 名、错误码、能力名),不要过度改写
|
||||
- 返回按相关性排序,默认 `--size 10`;要拿更多结果时先翻页,再考虑换关键词
|
||||
- 返回按相关性排序,默认 `--size 10`;响应有 `hasMore=true` 时,用 `nextCursor` 传给 `--cursor` 继续翻页,不要手写猜测下一页
|
||||
- 命中结果里的链接是钉钉开放平台公开文档,可直接给用户做参考
|
||||
- 不要把 devdoc 用来查业务数据(那是 aitable / doc / report 的事);devdoc 只查**官方开发者文档**
|
||||
@@ -15,9 +15,27 @@ Example:
|
||||
Flags:
|
||||
--query string 搜索关键词 (必填)
|
||||
--page string 页码 (默认 1)
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
--size string 每页数量 (默认 10)
|
||||
```
|
||||
|
||||
### 排查开放平台调用错误
|
||||
```
|
||||
Usage:
|
||||
dws devdoc error diagnose [flags]
|
||||
Example:
|
||||
dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token" --format json
|
||||
dws devdoc error diagnose --request-id "<REQUEST_ID>" --query "接口调用失败" --format json
|
||||
Flags:
|
||||
--query string 原始排查问题
|
||||
--error-code string 错误码
|
||||
--error-message string 错误描述
|
||||
--api string API 名称
|
||||
--context string 额外排查上下文
|
||||
--request-id string 开放平台 requestId
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## oa — 审批
|
||||
@@ -99,6 +117,7 @@ Example:
|
||||
## 意图判断
|
||||
|
||||
- 用户说"开发文档/API 文档/接口文档" → `devdoc article search`
|
||||
- 用户说"调用报错/错误码/requestId" → `devdoc error diagnose`;若返回 `PARAM_ERROR - 未找到指定工具`,降级 `devdoc article search` 并标记后端工具注册待闭环
|
||||
- 用户说"审批/请假/报销/出差" → `oa approval`
|
||||
- 用户说"同意审批/批准" → `oa approval approve`
|
||||
- 用户说"拒绝审批/驳回" → `oa approval reject`
|
||||
@@ -110,7 +129,8 @@ Example:
|
||||
|
||||
| 操作 | 从返回中提取 | 用于 |
|
||||
|------|-------------|------|
|
||||
| `devdoc article search` | 文档链接 | 直接展示给用户 |
|
||||
| `devdoc article search` | 文档链接、nextCursor | 直接展示给用户;hasMore=true 时用 --cursor 翻页 |
|
||||
| `devdoc error diagnose` | 诊断材料/文档链接、nextCursor | 排查错误码;hasMore=true 时用 --cursor 翻页;不可用时退回文档搜索 |
|
||||
| `oa approval list-forms` | processCode | detail / records 等 |
|
||||
| `oa approval tasks` | taskId, instanceId | approve / reject |
|
||||
| `oa approval list-pending` | instanceId | detail / approve / reject |
|
||||
|
||||
@@ -13,21 +13,45 @@ Example:
|
||||
dws devdoc article search --query "OAuth2 接入"
|
||||
dws devdoc article search --keyword "机器人" --size 10
|
||||
dws devdoc article search --query "消息卡片" --page 2 --size 5
|
||||
dws devdoc article search --query "消息卡片" --cursor "<NEXT_CURSOR>" --size 5
|
||||
Flags:
|
||||
--query string 搜索关键词 (必填)
|
||||
--keyword string 搜索关键词 (--query 的别名)
|
||||
--page int 分页页码 (从 1 开始,默认 1)
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
--size int 分页大小 (默认 10)
|
||||
```
|
||||
|
||||
### 排查调用错误
|
||||
```
|
||||
Usage:
|
||||
dws devdoc error diagnose [flags]
|
||||
Example:
|
||||
dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token"
|
||||
dws devdoc error diagnose --request-id "<REQUEST_ID>" --query "接口调用失败"
|
||||
dws devdoc error troubleshoot --error-message "token is illegal" --api "获取用户信息"
|
||||
Flags:
|
||||
--query string 原始排查问题
|
||||
--error-code string 错误码
|
||||
--error-message string 错误描述,会合并进原始问题
|
||||
--api string API 名称,会合并进原始问题
|
||||
--context string 额外排查上下文
|
||||
--request-id string 开放平台 requestId
|
||||
--page int 分页页码 (从 1 开始,默认 1)
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
--size int 分页大小 (默认 10)
|
||||
```
|
||||
|
||||
## 意图判断
|
||||
|
||||
用户问开放平台 API / 字段 / 错误码 / SDK / 鉴权 / 回调 / 配额相关的技术细节:
|
||||
- 走 `devdoc article search`,把用户问的关键短语作为位置参数或 `--query`
|
||||
- 用户已经给出错误码、错误描述或 requestId:
|
||||
- 先走 `devdoc error diagnose`;若后端返回 `PARAM_ERROR - 未找到指定工具` / `unknown tool`,降级 `devdoc article search`,同时把该结果标记为 `needs_gateway_tool_registration`
|
||||
|
||||
关键区分:
|
||||
- devdoc(钉钉**开放平台**开发者文档,面向研发) vs doc(钉钉在线文档,面向普通用户内容)
|
||||
- devdoc 只做搜索,不做读取;命中条目返回标题、摘要、文档链接,由 Agent 引用链接或进一步浏览
|
||||
- devdoc 只做搜索/诊断,不做读取;命中条目返回标题、摘要、文档链接,由 Agent 引用链接或进一步浏览
|
||||
|
||||
## 核心工作流
|
||||
|
||||
@@ -41,13 +65,38 @@ dws devdoc article search "MCP" --format json
|
||||
# 命中结果多时翻页
|
||||
dws devdoc article search --query "消息卡片" --page 2 --size 5 --format json
|
||||
|
||||
# 推荐动态翻页:优先使用上一页返回的 nextCursor
|
||||
dws devdoc article search --query "消息卡片" --cursor "<NEXT_CURSOR>" --size 5 --format json
|
||||
|
||||
# 查错误码 / 字段含义
|
||||
dws devdoc article search --query "errcode 40078" --format json
|
||||
|
||||
# 有明确错误码时优先走错误诊断
|
||||
dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token" --format json
|
||||
|
||||
# 有 requestId 时尝试链路排查;不是所有接口都会返回 requestId
|
||||
dws devdoc error diagnose --request-id "<REQUEST_ID>" --query "接口调用失败" --format json
|
||||
|
||||
# 错误排查结果继续翻页
|
||||
dws devdoc error diagnose --error-code "40014" --query "access_token" --cursor "<NEXT_CURSOR>" --format json
|
||||
```
|
||||
|
||||
## 上线/注入验收
|
||||
|
||||
```bash
|
||||
dws devdoc --help
|
||||
dws devdoc article search --query "OAuth2 接入" --dry-run --format json
|
||||
dws devdoc error diagnose --error-code "40014" --query "access_token" --dry-run --format json
|
||||
dws schema devdoc.search_open_platform_docs_rag --format json || true
|
||||
dws schema devdoc.search_open_error_code_rag --format json || true
|
||||
```
|
||||
|
||||
- `--help` / `--dry-run` 只能证明 CLI 命令面和映射存在,不能证明后端工具已注册
|
||||
- 真实调用若返回 `PARAM_ERROR - 未找到指定工具`,不是用户参数问题;记录为网关/工具注册待闭环,并降级到 article search 或本地已挂载 Markdown
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 关键词必填;可用位置参数、`--query` 或兼容别名 `--keyword`。建议传用户原话里的关键名词(API 名、错误码、能力名),不要过度改写
|
||||
- 返回按相关性排序,默认 `--size 10`;要拿更多结果时先翻页,再考虑换关键词
|
||||
- 返回按相关性排序,默认 `--size 10`;响应有 `hasMore=true` 时,用 `nextCursor` 传给 `--cursor` 继续翻页,不要手写猜测下一页
|
||||
- 命中结果里的链接是钉钉开放平台公开文档,可直接给用户做参考
|
||||
- 不要把 devdoc 用来查业务数据(那是 aitable / doc / report 的事);devdoc 只查**官方开发者文档**
|
||||
@@ -15,9 +15,27 @@ Example:
|
||||
Flags:
|
||||
--query string 搜索关键词 (必填)
|
||||
--page string 页码 (默认 1)
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
--size string 每页数量 (默认 10)
|
||||
```
|
||||
|
||||
### 排查开放平台调用错误
|
||||
```
|
||||
Usage:
|
||||
dws devdoc error diagnose [flags]
|
||||
Example:
|
||||
dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token" --format json
|
||||
dws devdoc error diagnose --request-id "<REQUEST_ID>" --query "接口调用失败" --format json
|
||||
Flags:
|
||||
--query string 原始排查问题
|
||||
--error-code string 错误码
|
||||
--error-message string 错误描述
|
||||
--api string API 名称
|
||||
--context string 额外排查上下文
|
||||
--request-id string 开放平台 requestId
|
||||
--cursor string 分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page
|
||||
```
|
||||
|
||||
## live — 直播
|
||||
|
||||
### 查看我的直播列表
|
||||
@@ -91,7 +109,8 @@ Args:
|
||||
|
||||
## 意图判断
|
||||
|
||||
- 用户说"开发文档/API 文档/接口文档/调用报错" → `devdoc article search`
|
||||
- 用户说"开发文档/API 文档/接口文档" → `devdoc article search`
|
||||
- 用户说"调用报错/错误码/requestId" → `devdoc error diagnose`;若返回 `PARAM_ERROR - 未找到指定工具`,降级 `devdoc article search` 并标记后端工具注册待闭环
|
||||
- 用户说"直播/我的直播" → `live stream list`
|
||||
- 用户说"搜索技能/找技能/安装技能/技能市场" → `skill search` / `skill install`(按步骤衔接)
|
||||
|
||||
@@ -99,7 +118,8 @@ Args:
|
||||
|
||||
| 操作 | 从返回中提取 | 用于 |
|
||||
|------|-------------|------|
|
||||
| `devdoc article search` | 文档链接 | 直接展示给用户 |
|
||||
| `devdoc article search` | 文档链接、nextCursor | 直接展示给用户;hasMore=true 时用 --cursor 翻页 |
|
||||
| `devdoc error diagnose` | 诊断材料/文档链接、nextCursor | 排查错误码;hasMore=true 时用 --cursor 翻页;不可用时退回文档搜索 |
|
||||
| `skill search` | `skillId`、名称、描述 | 用户选型后传给 `skill install <skillId> <target>` |
|
||||
| `skill install` | 安装成功/失败信息 | 确认目标 Agent 目录已注册 |
|
||||
| `skill publish` | 发布结果(成功或错误信息) | 确认企业技能库已更新 |
|
||||
|
||||
@@ -13,7 +13,7 @@ func TestDevdocArticleSearch_should_call_correct_tool(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertToolName(t, cap, "search_open_platform_docs")
|
||||
assertToolName(t, cap, "search_open_platform_docs_rag")
|
||||
}
|
||||
|
||||
func TestDevdocArticleSearch_should_pass_keyword(t *testing.T) {
|
||||
@@ -25,6 +25,16 @@ func TestDevdocArticleSearch_should_pass_keyword(t *testing.T) {
|
||||
assertToolArg(t, cap, "keyword", "openConversationId")
|
||||
}
|
||||
|
||||
func TestDevdocArticleSearch_should_pass_cursor(t *testing.T) {
|
||||
cap := setupTestDeps(t, "devdoc")
|
||||
root := buildRoot()
|
||||
_ = execCmd(t, root, []string{"devdoc", "article", "search"}, map[string]string{
|
||||
"keyword": "Webhook", "cursor": "3", "size": "10",
|
||||
})
|
||||
assertToolArg(t, cap, "cursor", "3")
|
||||
assertArgNotPresent(t, cap, "page")
|
||||
}
|
||||
|
||||
func TestDevdocArticleSearch_should_not_call_when_dry_run(t *testing.T) {
|
||||
cap := setupTestDepsWithDryRun(t, "devdoc")
|
||||
root := buildRoot()
|
||||
|
||||
@@ -126,13 +126,9 @@ func execCmdWithArgs(t *testing.T, root *cobra.Command, path []string, flags map
|
||||
|
||||
cap := getCapture(t)
|
||||
|
||||
cliArgs := []string{"-f", "json"}
|
||||
cliArgs := []string{"-f", "json", "--dry-run"}
|
||||
cliArgs = append(cliArgs, path...)
|
||||
|
||||
// Add dry-run if capture says so
|
||||
if cap != nil && cap.dryRun {
|
||||
cliArgs = append(cliArgs, "--dry-run")
|
||||
}
|
||||
// Add --yes if auto-confirm
|
||||
if cap != nil && cap.confirm {
|
||||
cliArgs = append(cliArgs, "--yes")
|
||||
@@ -165,11 +161,22 @@ func execCmdWithArgs(t *testing.T, root *cobra.Command, path []string, flags map
|
||||
} `json:"invocation"`
|
||||
}
|
||||
if cap != nil {
|
||||
recordDryRunPreview := !cap.dryRun
|
||||
if jsonErr := json.Unmarshal(out.Bytes(), &inv); jsonErr == nil && inv.Invocation.Tool != "" {
|
||||
if !inv.Invocation.DryRun {
|
||||
if !inv.Invocation.DryRun || recordDryRunPreview {
|
||||
cap.record(inv.Invocation.Tool, inv.Invocation.Params, "")
|
||||
}
|
||||
// For dry-run: don't record (matches old behavior: assertCallCount == 0)
|
||||
return nil
|
||||
}
|
||||
var directInv struct {
|
||||
Tool string `json:"tool"`
|
||||
Params map[string]any `json:"params"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
}
|
||||
if jsonErr := json.Unmarshal(out.Bytes(), &directInv); jsonErr == nil && directInv.Tool != "" {
|
||||
if recordDryRunPreview {
|
||||
cap.record(directInv.Tool, directInv.Params, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+6
-1
@@ -1168,7 +1168,7 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然
|
||||
|
||||
---
|
||||
|
||||
### devdoc(7 条)
|
||||
### devdoc(8 条)
|
||||
|
||||
#### `dws devdoc article search`
|
||||
|
||||
@@ -1207,6 +1207,11 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然
|
||||
- Expected: `dws devdoc article search --keyword "机器人发消息接口" --format json`
|
||||
- Flags: `--keyword` = `机器人发消息接口`
|
||||
|
||||
**devdoc_devdoc_article_search_008**
|
||||
- Prompt: 继续搜索开放平台文档中关于消息卡片的下一页,游标 NEXT_CURSOR
|
||||
- Expected: `dws devdoc article search --keyword 消息卡片 --cursor NEXT_CURSOR --format json`
|
||||
- Flags: `--keyword` = `消息卡片`, `--cursor` = `NEXT_CURSOR`
|
||||
|
||||
---
|
||||
|
||||
### routing(3 条)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
## Summary
|
||||
|
||||
- **Total Test Cases**: 234
|
||||
- **Passed**: 234
|
||||
- **Total Test Cases**: 235
|
||||
- **Passed**: 235
|
||||
- **Failed**: 0
|
||||
- **Pass Rate**: 100.0%
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
| chat | 31 | 31 | 0 | 100.0% |
|
||||
| contact | 14 | 14 | 0 | 100.0% |
|
||||
| devapp | 25 | 25 | 0 | 100.0% |
|
||||
| devdoc | 7 | 7 | 0 | 100.0% |
|
||||
| devdoc | 8 | 8 | 0 | 100.0% |
|
||||
| ding | 5 | 5 | 0 | 100.0% |
|
||||
| report | 26 | 26 | 0 | 100.0% |
|
||||
| routing | 3 | 3 | 0 | 100.0% |
|
||||
@@ -1353,6 +1353,14 @@
|
||||
- Command path: PASS (devdoc article search)
|
||||
- Flags: PASS (1 flags validated)
|
||||
|
||||
**devdoc_devdoc_article_search_008** ✅ PASS
|
||||
|
||||
- Prompt: 继续搜索开放平台文档中关于消息卡片的下一页,游标 NEXT_CURSOR
|
||||
- Expected: `dws devdoc article search --keyword 消息卡片 --cursor NEXT_CURSOR --format json`
|
||||
- Skill Reference: references/products/devdoc.md
|
||||
- Command path: PASS (devdoc article search)
|
||||
- Flags: PASS (2 flags validated)
|
||||
|
||||
### ding
|
||||
|
||||
**ding_ding_message_recall_001** ✅ PASS
|
||||
|
||||
Reference in new issue
Block a user