From 3adeffb09daeaecc0b49f5744c9d5f371a529487 Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Fri, 12 Jun 2026 11:33:55 +0800 Subject: [PATCH 01/47] event subscribe --- docs/devapp-yulan-command-routing.md | 47 ++++++----- internal/helpers/devapp.go | 76 ++++++++++++++++- internal/helpers/devapp_test.go | 73 +++++++++++++++- skills/dingtalk-devapp/SKILL.md | 4 +- skills/dingtalk-devapp/references/event.md | 84 +++++++++++++++++++ skills/mono/SKILL.md | 6 +- skills/mono/references/intent-guide.md | 1 + skills/mono/references/products/devapp.md | 24 ++++-- skills/multi/dingtalk-devapp/SKILL.md | 4 +- .../multi/dingtalk-devapp/references/event.md | 84 +++++++++++++++++++ 10 files changed, 372 insertions(+), 31 deletions(-) create mode 100644 skills/dingtalk-devapp/references/event.md create mode 100644 skills/multi/dingtalk-devapp/references/event.md diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index ced85339..3455b635 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -86,8 +86,9 @@ dws devapp permission detail Show one permission's full API coverage permission add Apply permissions into current app version changes permission remove Remove one authorized permission - event list List event subscription config - event config Configure callback URL, token, aesKey, event types + event list List subscribable events and current subscription state + event subscribe Subscribe one event by eventCode + event unsubscribe Unsubscribe one event by eventCode robot create Create a new agent app + robot synchronously robot submit Submit async robot-create task (supports retry by taskId) robot result Query async robot-create task result @@ -104,9 +105,8 @@ dws devapp version status Poll publish and approval status ``` -Robot and version commands are hardcoded helper leaves over the `op-app` MCP -server (same pinned endpoint as the rest of `devapp`, no service discovery). -`event` is still spec-only and reports unsupported at runtime. +Robot, version, and event commands are hardcoded helper leaves over the `op-app` +MCP server (same pinned endpoint as the rest of `devapp`, no service discovery). Open-source examples should use `--format json` for agent consumption: @@ -139,14 +139,14 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Permission list | `devapp permission list/search/detail` | `list_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | | Permission apply | `devapp permission add` | `apply_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | | Permission remove | `devapp permission remove` | `remove_open_dev_app_permission` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | -| Events | `devapp event list/config` | `list/config_open_dev_app_events` | Event facade pending | Pending | Spec only | +| Events | `devapp event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | Implemented | Hardcoded helper | | Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | | Robot config | `devapp robot get/config/update/enable/offline` | `get/create/update_open_dev_app_robot_config`, `enable/offline_open_dev_app_robot` | Implemented | Hardcoded helper | | Version | `devapp version create/list/get/check-approval/publish/status` | `create/list_open_dev_app_versions`, `get_open_dev_app_version_detail/status`, `publish_open_dev_app_version` | Implemented | Hardcoded helper | -P0 for yulan is app CRUD, permissions, credentials, and web app config. Robot -and version are now implemented as hardcoded helper leaves over the same -`op-app` MCP server; events remain a pending backend slice. +P0 for yulan is app CRUD, permissions, credentials, and web app config. Robot, +version, and event subscription are now all implemented as hardcoded helper +leaves over the same `op-app` MCP server. ### 3.1 P0 RPC Contract @@ -1003,12 +1003,20 @@ Rules: Event and version commands are needed for a full application lifecycle but should not be folded into permission add. -Target event tools: +Implemented event tools (verified against the `op-app` MCP `tools/list`). The +real model is "list subscribable events + subscribe/unsubscribe one event by +`eventCode`", not a single callbackUrl/token/aesKey config call: | CLI | MCP tool | Notes | | --- | --- | --- | -| `devapp event list` | `list_open_dev_app_events` | Query current callback and subscribed event types. | -| `devapp event config` | `configure_open_dev_app_event` | Configure callbackUrl, token, aesKey, eventTypes. | +| `devapp event list` | `list_open_dev_app_events` | Args `unifiedAppId`; returns `pushType` + `events[]` (`eventCode`/`eventName`/`subscribed`). Read. | +| `devapp event subscribe` | `subscribe_open_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write (`--dry-run`/`--yes`). | +| `devapp event unsubscribe` | `unsubscribe_open_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write. | + +Note: for gray unified apps, subscribe/unsubscribe are staged into version +metadata and only take effect after `version publish`. The event callback URL +itself is not part of this model; it lives in robot config (`chatBotEventUrl`, +exposed as `robot config --event-url`). Implemented version tools (verified against the `op-app` MCP `tools/list`): @@ -1070,7 +1078,7 @@ Positive `devapp` signals: | `开放平台应用`, `开发者后台应用`, `企业内部应用`, `内部应用` | `devapp` | | `agentId`, `clientId`, `appKey`, `customKey`, `appSecret`, `clientSecret` | `devapp` | | `应用权限`, `权限点`, `API 权限`, `APP/SNS 权限`, `scopeValue` | `devapp permission ...` | -| `事件订阅`, `callbackUrl`, `token`, `aesKey` in an app context | `devapp event ...` | +| `事件订阅`, `订阅事件`, `退订事件`, `eventCode`, `可订阅事件` in an app context | `devapp event ...` | | `应用版本`, `版本发布`, `选审批人`, `发布审核` in developer backend context | `devapp version ...` | | yulan's current `开放平台应用 CLI 化 / dws app` workstream | `devapp` | @@ -1124,8 +1132,8 @@ Intent table: | "申请权限/开通权限" | `permission add` | `apply_open_dev_app_permissions` | | "取消权限/移除权限" | `permission remove` | `remove_open_dev_app_permission` | | "拿 appSecret/clientSecret" | `credentials get` | `get_open_dev_app_credentials` | -| "配置事件订阅" | `event config` | Pending | -| "发布/审核/选审批人" | `version ...` | Pending | +| "查可订阅事件/订阅/退订事件" | `event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | +| "发布/审核/选审批人" | `version ...` | `publish_open_dev_app_version` etc. | Yulan invocation examples: @@ -1135,7 +1143,7 @@ Yulan invocation examples: | "把应用图标换成 ICON_RESOURCE" | `dws devapp update --unified-app-id ... --icon ICON_RESOURCE --dry-run --format json` | Icon change is app base info update and must be guarded. | | "已确认删除这个开放平台应用" | `dws devapp delete --unified-app-id ... --yes --format json` | Destructive write only after unique app resolution and confirmation. | | "手机号权限要申请,虽然需要审核" | `dws devapp permission add --permissions Contact.User.mobile --dry-run/--yes` | `requiredApproval=true` is still appliable; approval moves to version publish. | -| "配置事件订阅回调" | `dws devapp event config ... --dry-run --format json` | Event subscription is app configuration, not permission or docs search. | +| "订阅通讯录用户增加事件" | `dws devapp event list ...` then `dws devapp event subscribe --event-code user_add_org --dry-run --format json` | Pick eventCode from list, then subscribe; gray apps need a version publish to take effect. | | "保存版本并看审核状态" | `version create`, `check-approval`, `publish`, `status` | Version flow owns approver selection and publish audit state. | | "创建 MCP 服务并配置 HSF tool" | OpenDev MCP platform workflow, not `dws devapp create` | MCP connector/tool is a platform artifact, not an enterprise internal app. | @@ -1473,9 +1481,10 @@ the following are true: 4. Permission list/add/remove smoke cases pass for APP and SNS scopes. 5. `requiredApproval=true` permission add stages into version changes instead of being rejected by the Agent. -6. Credential access is handled only by `credentials get`; missing event/version - tools are reported as unsupported/pending, not silently emulated by other - commands. +6. Credential access is handled only by `credentials get`; event subscription + (`event list/subscribe/unsubscribe`) and version flow are implemented over + `op-app`, and any still-missing tool is reported as unsupported, not silently + emulated by other commands. 7. No repository doc, skill, log, or fixture contains MCP gateway keys, cookies, access tokens, full app secrets, or production app data. 8. Existing skill setup and generator tests pass. diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 62025e05..a4c33f42 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -49,6 +49,11 @@ const ( devAppVersionDetailTool = "get_open_dev_app_version_detail" devAppVersionPublishTool = "publish_open_dev_app_version" devAppVersionStatusTool = "get_open_dev_app_version_status" + + // 事件订阅能力(op-app MCP 工具,硬编码不走服务发现)。 + devAppEventListTool = "list_open_dev_app_events" + devAppEventSubscribeTool = "subscribe_open_dev_app_event" + devAppEventUnsubscribeTool = "unsubscribe_open_dev_app_event" ) func init() { @@ -72,7 +77,7 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { Use: "devapp", Aliases: []string{"app"}, Short: "开放平台应用", - Long: "管理开放平台开发者应用:查询、详情、创建、更新、启停、删除、权限、网页应用、成员、安全配置、机器人和版本发布。", + Long: "管理开放平台开发者应用:查询、详情、创建、更新、启停、删除、权限、网页应用、成员、安全配置、机器人、版本发布和事件订阅。", Args: cobra.NoArgs, TraverseChildren: true, DisableAutoGenTag: true, @@ -193,6 +198,22 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { newDevAppVersionStatusCommand(runner), ) + event := &cobra.Command{ + Use: "event", + Short: "开放平台应用事件订阅", + Args: cobra.NoArgs, + TraverseChildren: true, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + event.AddCommand( + newDevAppEventListCommand(runner), + newDevAppEventSubscribeCommand(runner, "subscribe", "订阅单个事件", devAppEventSubscribeTool), + newDevAppEventSubscribeCommand(runner, "unsubscribe", "退订单个事件", devAppEventUnsubscribeTool), + ) + root.AddCommand( newDevAppListCommand(runner), newDevAppGetCommand(runner), @@ -208,6 +229,7 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { security, robot, version, + event, ) return root } @@ -1092,6 +1114,58 @@ func requiredDevAppUnifiedID(cmd *cobra.Command) (string, error) { return appID, nil } +// --------------------------------------------------------------------------- +// 事件订阅能力 +// --------------------------------------------------------------------------- + +func newDevAppEventListCommand(runner executor.Runner) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "查询应用可订阅事件列表及订阅状态", + Example: " dws devapp event list --unified-app-id UNIFIED_APP_ID --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + return runDevAppTool(runner, cmd, devAppEventListTool, map[string]any{"unifiedAppId": appID}) + }, + } + addDevAppUnifiedIDFlag(cmd) + preferLegacyLeaf(cmd) + return cmd +} + +func newDevAppEventSubscribeCommand(runner executor.Runner, use, short, tool string) *cobra.Command { + cmd := &cobra.Command{ + Use: use, + Short: short, + Example: " dws devapp event " + use + " --unified-app-id UNIFIED_APP_ID --event-code user_add_org --dry-run --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := devAppRequireWriteGuard(cmd, "event "+use); err != nil { + return err + } + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + eventCode := devAppStringFlag(cmd, "event-code") + if eventCode == "" { + return apperrors.NewValidation("--event-code is required") + } + return runDevAppTool(runner, cmd, tool, map[string]any{"unifiedAppId": appID, "eventCode": eventCode}) + }, + } + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("event-code", "", "事件码 eventCode,见 event list 返回 (必填)") + preferLegacyLeaf(cmd) + return cmd +} + func registerDevAppMemberMutationFlags(cmd *cobra.Command) { cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") cmd.Flags().String("users", "", "成员 userId 列表,多个用逗号分隔 (必填)") diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index b672d2e3..f027ca4c 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -114,7 +114,7 @@ func TestDevAppCommandHasAppAliasAndCoreCommands(t *testing.T) { if !hasAlias { t.Fatalf("Aliases = %v, want app", root.Aliases) } - for _, name := range []string{"list", "get", "create", "update", "delete", "inactive", "active", "credentials", "webapp", "permission", "member", "security", "robot", "version"} { + for _, name := range []string{"list", "get", "create", "update", "delete", "inactive", "active", "credentials", "webapp", "permission", "member", "security", "robot", "version", "event"} { if _, _, err := root.Find([]string{name}); err != nil { t.Fatalf("missing command %q: %v", name, err) } @@ -290,6 +290,75 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { } } +func TestDevAppEventCommandsBuildToolParams(t *testing.T) { + cases := []struct { + name string + args []string + wantTool string + wantParams map[string]any + }{ + { + name: "list", + args: []string{"event", "list", "--unified-app-id", "u-1"}, + wantTool: "list_open_dev_app_events", + wantParams: map[string]any{"unifiedAppId": "u-1"}, + }, + { + name: "subscribe", + args: []string{"event", "subscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org", "--yes"}, + wantTool: "subscribe_open_dev_app_event", + wantParams: map[string]any{"unifiedAppId": "u-1", "eventCode": "user_add_org"}, + }, + { + name: "unsubscribe", + args: []string{"event", "unsubscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org", "--yes"}, + wantTool: "unsubscribe_open_dev_app_event", + wantParams: map[string]any{"unifiedAppId": "u-1", "eventCode": "user_add_org"}, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs(append([]string{"devapp"}, tc.args...)) + + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + if got := runner.last.Tool; got != tc.wantTool { + t.Fatalf("Tool = %q, want %q", got, tc.wantTool) + } + if !reflect.DeepEqual(runner.last.Params, tc.wantParams) { + t.Fatalf("Params = %#v, want %#v", runner.last.Params, tc.wantParams) + } + }) + } +} + +func TestDevAppEventSubscribeRequiresEventCode(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"devapp", "event", "subscribe", "--unified-app-id", "u-1", "--yes"}) + + err := root.Execute() + if err == nil { + t.Fatal("Execute() error = nil, want --event-code validation") + } + if !strings.Contains(err.Error(), "event-code") { + t.Fatalf("error = %q, want event-code validation", err.Error()) + } + if runner.last.Tool != "" { + t.Fatalf("tool = %q, want no invocation", runner.last.Tool) + } +} + func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { cases := []struct { name string @@ -300,6 +369,8 @@ func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { {name: "robot offline", args: []string{"devapp", "robot", "offline", "--unified-app-id", "u-1"}}, {name: "version create", args: []string{"devapp", "version", "create", "--unified-app-id", "u-1", "--version", "1.0.1"}}, {name: "version publish", args: []string{"devapp", "version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1"}}, + {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org"}}, + {name: "event unsubscribe", args: []string{"devapp", "event", "unsubscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org"}}, } for _, tc := range cases { diff --git a/skills/dingtalk-devapp/SKILL.md b/skills/dingtalk-devapp/SKILL.md index 5019efe1..664f1cf9 100644 --- a/skills/dingtalk-devapp/SKILL.md +++ b/skills/dingtalk-devapp/SKILL.md @@ -1,6 +1,6 @@ --- name: dingtalk-devapp -description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 +description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/事件订阅/订阅事件/退订事件/eventCode。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 cli_version: ">=1.0.15" metadata: category: product @@ -23,6 +23,7 @@ metadata: - `应用权限/权限点/scopeValue/应用成员/安全配置/IP 白名单` - `创建机器人/智能体机器人/机器人配置/机器人回调地址`(→ `robot`) - `应用版本/版本发布/发布审核/选审批人`(→ `version`) +- `事件订阅/订阅事件/退订事件/eventCode/可订阅事件`(→ `event`) 不要路由到 devapp 的:接口文档→`devdoc`、钉钉文档→`doc`、工作台应用→`workbench app`、审批→`oa`。只说 `应用` 无上下文时先追问。 @@ -53,4 +54,5 @@ metadata: | 成员与安全 | [member-security.md](references/member-security.md) | member list / add / remove / security config | | 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / offline | | 版本发布 | [version.md](references/version.md) | version create / list / get / check-approval / publish / status | +| 事件订阅 | [event.md](references/event.md) | event list / subscribe / unsubscribe | | 操作流程 | [workflows.md](references/workflows.md) | 创建应用全流程 / 权限全流程 / 生命周期管理 | diff --git a/skills/dingtalk-devapp/references/event.md b/skills/dingtalk-devapp/references/event.md new file mode 100644 index 00000000..166dbc61 --- /dev/null +++ b/skills/dingtalk-devapp/references/event.md @@ -0,0 +1,84 @@ +# 事件订阅 + +管理开放平台企业内部应用的事件订阅:列出可订阅事件、按事件码订阅/退订。 + +> 所有命令通过 `--unified-app-id` 定位应用;`corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。订阅/退订是写操作,先 `--dry-run`,确认后再 `--yes`。 + +## 可订阅事件列表 + +```bash +dws devapp event list --unified-app-id --format json +``` + +MCP tool: `list_open_dev_app_events`。返回 `pushType`(当前推送类型)和 `events[]`,每项含: + +| 字段 | 含义 | +|------|------| +| `eventCode` | 事件码,订阅/退订时传入,如 `user_add_org` | +| `eventName` | 事件名称,如「通讯录用户增加」 | +| `subscribed` | 当前是否已订阅 | + +常见事件码示例:`user_add_org`(通讯录用户增加) / `user_modify_org`(用户更改) / `user_leave_org`(用户离职) / `org_dept_create`(部门创建) / `org_dept_modify`(部门修改) / `org_dept_remove`(部门删除)。以 `event list` 实际返回为准。 + +## 订阅事件 + +```bash +dws devapp event subscribe --unified-app-id --event-code user_add_org --dry-run --format json +dws devapp event subscribe --unified-app-id --event-code user_add_org --yes --format json +``` + +MCP tool: `subscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 + +## 退订事件 + +```bash +dws devapp event unsubscribe --unified-app-id --event-code user_add_org --dry-run --format json +dws devapp event unsubscribe --unified-app-id --event-code user_add_org --yes --format json +``` + +MCP tool: `unsubscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 + +## 字段映射 + +| CLI | MCP | 必填 | 说明 | +|-----|-----|------|------| +| `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID(兼容隐藏别名 `--app-id`) | +| `--event-code` | `eventCode` | subscribe/unsubscribe 必填 | 事件码,取自 `event list` | + +## 灰度应用需发布版本生效(重要) + +对**灰度统一应用**,`subscribe`/`unsubscribe` 只会把变更**暂存到版本元数据**,不会立即生效,需后续走版本发布链路: + +```text +event subscribe/unsubscribe 订阅变更写入版本元数据 + → version create 创建新版本 + → version check-approval 预检审批 + → version publish 发布后订阅变更才生效 + → version status 轮询发布/审批状态 +``` + +详见 [version.md](version.md)。 + +## 单步流程 + +```text +1. 查看可订阅事件及当前状态 + dws devapp event list --unified-app-id --format json + → 从返回里挑 eventCode + +2. 订阅 + dws devapp event subscribe --unified-app-id --event-code --dry-run --format json + → 确认后加 --yes + +3. 验证 + dws devapp event list --unified-app-id --format json + → 确认对应事件 subscribed=true(灰度应用需先发布版本) +``` + +## 错误处理 + +| 情况 | 处理 | +|------|------| +| `--event-code` 缺失 | CLI 直接报错,先 `event list` 取事件码 | +| 订阅后 `subscribed` 仍为 false | 灰度应用需先 `version publish` 发布版本 | +| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/mono/SKILL.md b/skills/mono/SKILL.md index b46c04be..1f9d4cb6 100644 --- a/skills/mono/SKILL.md +++ b/skills/mono/SKILL.md @@ -49,7 +49,7 @@ cli_version: ">=1.0.15" | `chat` | 群聊与机器人:搜索群/建群/群成员管理/改群名/消息发送(文本/Markdown/图片/文件)/拉取消息/@我/特别关注/机器人群发/单聊/撤回/转发/引用回复/Webhook/机器人搜索 | [chat.md](./references/products/chat.md) | | `contact` | 通讯录:用户查询(当前用户/搜索/详情/手机号)/花名册档案(学历/家庭/银行卡/合同)/离职员工查询(姓名/时间范围/部门)/部门查询(搜索/详情/子部门/成员)/角色查询(主管/管理员/财务/HR 等 label)/特别关注列表 | [contact.md](./references/products/contact.md) | | `devdoc` | 开放平台文档:搜索开发文档 | [devdoc.md](./references/products/devdoc.md) | -| `devapp` | 开放平台应用:企业内部应用查询/详情/创建/修改/删除、成员管理、安全配置、应用权限 APP/SNS 列表与申请取消、凭证读取、机器人创建与配置、版本创建/发布/审批 | [devapp.md](./references/products/devapp.md) | +| `devapp` | 开放平台应用:企业内部应用查询/详情/创建/修改/删除、成员管理、安全配置、应用权限 APP/SNS 列表与申请取消、凭证读取、机器人创建与配置、版本创建/发布/审批、事件订阅/退订 | [devapp.md](./references/products/devapp.md) | | `ding` | DING消息:发送/撤回(应用内/短信/电话) | [ding.md](./references/products/ding.md) | | `doc` | 钉钉文档:搜索/浏览/读写/块级编辑/评论/文件创建/复制/移动/重命名/**删除/导出 docx/权限管理/媒体上传下载**;创建/编辑先按渐进式 doc 子文档与 JSONML 工作流决策 | [doc.md](./references/products/doc.md) | | `drive` | 钉钉云盘:文件列表/元数据/文件夹/上传(两步)/下载 | [drive.md](./references/products/drive.md) | @@ -84,7 +84,7 @@ cli_version: ">=1.0.15" ### 消歧规则 - "应用" 是泛词,不默认等于开放平台应用。只有出现"开放平台应用/开发者后台应用/企业内部应用", - 或伴随 `agentId/clientId/appKey/appSecret/customKey/应用成员/安全配置/IP 白名单/登录重定向/应用权限/权限点/创建机器人/智能体机器人/机器人配置/事件订阅/应用版本/发布审核` + 或伴随 `agentId/clientId/appKey/appSecret/customKey/应用成员/安全配置/IP 白名单/登录重定向/应用权限/权限点/创建机器人/智能体机器人/机器人配置/事件订阅/订阅事件/退订事件/eventCode/应用版本/发布审核` 等开发者后台标识时,才路由到 `devapp`。 - "工作台应用/app001/appXYZ/钉钉工作台上有哪些应用" → `workbench app`,不要走 `devapp`。 - "开放平台接口文档/API 怎么调用/错误码/字段说明" → `devdoc`,不要走 `devapp`。 @@ -104,7 +104,7 @@ cli_version: ">=1.0.15" 用户提到"群聊/建群/群成员/群管理/发消息/发图片消息/发文件消息/发 Markdown 消息/截图发钉钉/转发消息/引用回复/@我/特别关注消息/机器人发消息/Webhook/机器人群发/机器人单聊/通知" → `chat` 用户提到"通讯录/同事/部门/组织架构/子部门/部门多少人/离职员工/离职名单/离职花名册/花名册/员工档案/学历/家庭/银行卡/紧急联系人/合同/角色/主管角色/管理员角色/财务/HR/特别关注/星标联系人" → `contact` 用户提到"开发/API/调用错误 文档" → `devdoc` -用户提到"开放平台应用/开发者后台应用/企业内部应用/应用 agentId/clientId/appKey/appSecret/应用成员/安全配置/IP 白名单/登录重定向/应用权限/权限点/创建机器人/智能体机器人/机器人配置/事件订阅/应用版本发布/审核" → `devapp` +用户提到"开放平台应用/开发者后台应用/企业内部应用/应用 agentId/clientId/appKey/appSecret/应用成员/安全配置/IP 白名单/登录重定向/应用权限/权限点/创建机器人/智能体机器人/机器人配置/事件订阅/订阅事件/退订事件/eventCode/应用版本发布/审核" → `devapp` 用户提到"DING/紧急消息/电话提醒" → `ding` 用户提到"钉钉文档/云文档/读写文档/知识库里的文档/浏览知识库内容/知识库内搜索文档/块级编辑/文档评论/文档复制移动" → `doc` 用户提到"云盘/文件存储/文件上传下载/文件夹" → `drive` diff --git a/skills/mono/references/intent-guide.md b/skills/mono/references/intent-guide.md index bda871ab..6be21977 100644 --- a/skills/mono/references/intent-guide.md +++ b/skills/mono/references/intent-guide.md @@ -56,6 +56,7 @@ - "查 appKey/clientId/agentId/appSecret/clientSecret" — 应用身份和凭证 - "应用权限/权限点/事件订阅/应用版本/发布审核" — 应用配置或发布链路 - "创建机器人/智能体机器人/机器人配置/机器人回调" — 应用机器人能力(`devapp robot`) +- "事件订阅/订阅事件/退订事件/eventCode/可订阅事件" — 应用事件订阅(`devapp event`) **用 `devdoc` 的场景**: - "开放平台 API 怎么调用" — 接口文档 diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 726facd9..cfde383f 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -326,7 +326,24 @@ MCP tools: `create_open_dev_app_version` / `list_open_dev_app_versions` / `get_o --- -## 七、操作流程 +## 七、事件订阅 + +```bash +dws devapp event list --unified-app-id ID --format json +dws devapp event subscribe --unified-app-id ID --event-code user_add_org --dry-run --format json +dws devapp event unsubscribe --unified-app-id ID --event-code user_add_org --dry-run --format json +``` + +MCP tools: `list_open_dev_app_events` / `subscribe_open_dev_app_event` / `unsubscribe_open_dev_app_event`。 + +- `event list` 返回 `pushType` 与 `events[]`(每项 `eventCode`/`eventName`/`subscribed`);订阅/退订只接受 `--event-code`(取自 list 返回),一次一个。 +- 写操作走 `--dry-run`/`--yes` 写保护;`list` 是只读。 +- **灰度统一应用**:`subscribe`/`unsubscribe` 只把变更暂存到版本元数据,需后续 `version create → publish` 发布版本后才生效。订阅后 `event list` 若仍 `subscribed=false`,先发布版本再回读。 +- 回调地址不在此模型内:消息/事件回调地址走机器人配置(`robot config --outgoing-url/--event-url`)。 + +--- + +## 八、操作流程 ### 创建应用全流程 @@ -358,7 +375,4 @@ permission list → permission list --keyword → permission list --scope → pe | `endpoint_not_resolved` | 检查 edition endpoint 注入 | | 多应用命中 | 展示候选,停止写操作 | | `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | - -## 待实现能力 - -- `dws devapp event list/config` — 事件订阅(待后端发布) +| 事件订阅后未生效 | 灰度应用需先 `version publish` 发布版本 | diff --git a/skills/multi/dingtalk-devapp/SKILL.md b/skills/multi/dingtalk-devapp/SKILL.md index b3fc6945..e53e513c 100644 --- a/skills/multi/dingtalk-devapp/SKILL.md +++ b/skills/multi/dingtalk-devapp/SKILL.md @@ -1,6 +1,6 @@ --- name: dingtalk-devapp -description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 +description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/事件订阅/订阅事件/退订事件/eventCode。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 cli_version: ">=1.0.15" metadata: category: product @@ -29,6 +29,7 @@ metadata: - `应用权限/权限点/scopeValue/应用成员/安全配置/IP 白名单` - `创建机器人/智能体机器人/机器人配置/机器人回调地址`(→ `robot`) - `应用版本/版本发布/发布审核/选审批人`(→ `version`) +- `事件订阅/订阅事件/退订事件/eventCode/可订阅事件`(→ `event`) 不要路由到 devapp 的:接口文档→`dingtalk-devdoc`、钉钉文档→`dingtalk-doc`、工作台应用→`workbench app`、审批→`dingtalk-oa`。只说 `应用` 无上下文时先追问。 @@ -59,4 +60,5 @@ metadata: | 成员与安全 | [member-security.md](references/member-security.md) | member list / add / remove / security config | | 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / offline | | 版本发布 | [version.md](references/version.md) | version create / list / get / check-approval / publish / status | +| 事件订阅 | [event.md](references/event.md) | event list / subscribe / unsubscribe | | 操作流程 | [workflows.md](references/workflows.md) | 创建应用全流程 / 权限全流程 / 生命周期管理 | diff --git a/skills/multi/dingtalk-devapp/references/event.md b/skills/multi/dingtalk-devapp/references/event.md new file mode 100644 index 00000000..166dbc61 --- /dev/null +++ b/skills/multi/dingtalk-devapp/references/event.md @@ -0,0 +1,84 @@ +# 事件订阅 + +管理开放平台企业内部应用的事件订阅:列出可订阅事件、按事件码订阅/退订。 + +> 所有命令通过 `--unified-app-id` 定位应用;`corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。订阅/退订是写操作,先 `--dry-run`,确认后再 `--yes`。 + +## 可订阅事件列表 + +```bash +dws devapp event list --unified-app-id --format json +``` + +MCP tool: `list_open_dev_app_events`。返回 `pushType`(当前推送类型)和 `events[]`,每项含: + +| 字段 | 含义 | +|------|------| +| `eventCode` | 事件码,订阅/退订时传入,如 `user_add_org` | +| `eventName` | 事件名称,如「通讯录用户增加」 | +| `subscribed` | 当前是否已订阅 | + +常见事件码示例:`user_add_org`(通讯录用户增加) / `user_modify_org`(用户更改) / `user_leave_org`(用户离职) / `org_dept_create`(部门创建) / `org_dept_modify`(部门修改) / `org_dept_remove`(部门删除)。以 `event list` 实际返回为准。 + +## 订阅事件 + +```bash +dws devapp event subscribe --unified-app-id --event-code user_add_org --dry-run --format json +dws devapp event subscribe --unified-app-id --event-code user_add_org --yes --format json +``` + +MCP tool: `subscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 + +## 退订事件 + +```bash +dws devapp event unsubscribe --unified-app-id --event-code user_add_org --dry-run --format json +dws devapp event unsubscribe --unified-app-id --event-code user_add_org --yes --format json +``` + +MCP tool: `unsubscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 + +## 字段映射 + +| CLI | MCP | 必填 | 说明 | +|-----|-----|------|------| +| `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID(兼容隐藏别名 `--app-id`) | +| `--event-code` | `eventCode` | subscribe/unsubscribe 必填 | 事件码,取自 `event list` | + +## 灰度应用需发布版本生效(重要) + +对**灰度统一应用**,`subscribe`/`unsubscribe` 只会把变更**暂存到版本元数据**,不会立即生效,需后续走版本发布链路: + +```text +event subscribe/unsubscribe 订阅变更写入版本元数据 + → version create 创建新版本 + → version check-approval 预检审批 + → version publish 发布后订阅变更才生效 + → version status 轮询发布/审批状态 +``` + +详见 [version.md](version.md)。 + +## 单步流程 + +```text +1. 查看可订阅事件及当前状态 + dws devapp event list --unified-app-id --format json + → 从返回里挑 eventCode + +2. 订阅 + dws devapp event subscribe --unified-app-id --event-code --dry-run --format json + → 确认后加 --yes + +3. 验证 + dws devapp event list --unified-app-id --format json + → 确认对应事件 subscribed=true(灰度应用需先发布版本) +``` + +## 错误处理 + +| 情况 | 处理 | +|------|------| +| `--event-code` 缺失 | CLI 直接报错,先 `event list` 取事件码 | +| 订阅后 `subscribed` 仍为 false | 灰度应用需先 `version publish` 发布版本 | +| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | From 2c51774150a821fd992ce6e4d776effdf4beff31 Mon Sep 17 00:00:00 2001 From: "shangguanxuan.sgx" Date: Mon, 15 Jun 2026 15:33:30 +0800 Subject: [PATCH 02/47] feat(skills): add self-contained dingtalk-open-platform doc skill DingTalk open-platform documentation skill as an agent execution manual (discover -> auth -> call -> verify -> recover), fully self-contained (no cross-skill references; dws is only a reference tool), with a built-in doc index llm.md of verified open.dingtalk.com/document links. Co-authored-by: Cursor --- skills/dingtalk-open-platform/SKILL.md | 110 ++ skills/dingtalk-open-platform/llm.md | 1743 +++++++++++++++++ .../references/api-catalog.md | 1739 ++++++++++++++++ .../references/api-invocation.md | 57 + .../dingtalk-open-platform/references/auth.md | 71 + .../references/checklist.md | 27 + .../references/discovery.md | 43 + .../references/gotchas.md | 17 + .../references/quick-reference.md | 51 + .../references/reference.md | 61 + .../references/workflow.md | 84 + 11 files changed, 4003 insertions(+) create mode 100644 skills/dingtalk-open-platform/SKILL.md create mode 100644 skills/dingtalk-open-platform/llm.md create mode 100644 skills/dingtalk-open-platform/references/api-catalog.md create mode 100644 skills/dingtalk-open-platform/references/api-invocation.md create mode 100644 skills/dingtalk-open-platform/references/auth.md create mode 100644 skills/dingtalk-open-platform/references/checklist.md create mode 100644 skills/dingtalk-open-platform/references/discovery.md create mode 100644 skills/dingtalk-open-platform/references/gotchas.md create mode 100644 skills/dingtalk-open-platform/references/quick-reference.md create mode 100644 skills/dingtalk-open-platform/references/reference.md create mode 100644 skills/dingtalk-open-platform/references/workflow.md diff --git a/skills/dingtalk-open-platform/SKILL.md b/skills/dingtalk-open-platform/SKILL.md new file mode 100644 index 00000000..46be3c2e --- /dev/null +++ b/skills/dingtalk-open-platform/SKILL.md @@ -0,0 +1,110 @@ +--- +name: dingtalk-open-platform +description: >- + 指导 AI Agent 发现、鉴权、调用、验证并排错钉钉开放平台 OpenAPI / CLI / 应用能力的执行手册。 + Use when 用户要 调钉钉开放平台接口 / OpenAPI、建企业内部或第三方应用、拿 access_token、申请权限 scope、 + 订阅事件、查开发文档或错误码、用 dws 调接口。给出 base URL、Header、应用类型选型、分步工作流与校验清单。 + Distinct from 单纯的应用增删改管理 与 钉钉云文档内容读写。 + Do NOT use for 钉钉客户端 UI 设计、后台人工运营、未开放的内部能力。 + 口语同义词:开放平台、开发者后台、open api、调接口、接口怎么调。 +cli_version: ">=1.0.15" +metadata: + category: developer-platform + api_base: "https://api.dingtalk.com" + legacy_api_base: "https://oapi.dingtalk.com" + doc_base: "https://open.dingtalk.com/document" +--- + +# 钉钉开放平台 Skill(AI Agent 执行手册) + +每一步都回答「你下一步敲什么」。不得凭记忆猜 endpoint / 参数 / scope / ID;先查 [llm.md](llm.md) 文档大目录或官方文档,再执行。 + +## 1. Product summary + +钉钉开放平台是 REST OpenAPI,覆盖通讯录 / 消息 / 机器人 / 文档 / 钉盘 / 表格 / AI 表格 / 日历 / 审批 / 应用管理 / 事件订阅 / JSAPI / 小程序 / 三方应用。 + +| 项 | 值 | +| --- | --- | +| 新版 Base URL | `https://api.dingtalk.com`(路径 `/v1.0`、`/v2.0`;token 走 Header)| +| 旧版 Base URL | `https://oapi.dingtalk.com`(路径 `/topapi/...`;token 走 query `access_token`)| +| 鉴权 Header(新版)| `x-acs-dingtalk-access-token: ` + `Content-Type: application/json` | +| 鉴权类型 | 应用身份(appKey/appSecret 换 token)/ 用户身份(OAuth 授权码)/ JSAPI 免登 | +| 文档站 | `https://open.dingtalk.com/document`;全量有效链接见 [llm.md](llm.md) | +| 访问方式 | `dws` CLI(本仓库)、MCP、原始 HTTPS | + +## 2. 约束 (NEVER DO) + +- 不要编造 endpoint / 参数 / scopeValue / ID / URL / 错误码;ID 从返回值提取,链接来自 [llm.md](llm.md) 或官方文档。 +- 不要把 `appSecret` / `clientSecret` / `accessToken` / `refreshToken` / JSAPI ticket / Cookie 写进对话、日志或 prompt。 +- 不要混用新版与旧版鉴权;不要把「权限名称」当 `scopeValue`;不要默认拥有全员数据范围。 +- **[危险]** 写 / 删 / 撤回 / 发消息 / 发布 / 加权限 等操作:先 `--dry-run` 展示摘要,**获用户当前轮确认**后才加 `--yes`。 +- **Harmness**:不输出诱导有害内容;不教唆越权 / 绕过审批 / 绕过安全边界 / 泄露凭证;越权请求一律拒绝并说明原因。 + +## 3. When to use + +调用 OpenAPI;读写协作资源;建/管应用、权限、机器人、版本;拿 access_token;订阅事件;查开发文档 / 字段 / 错误码 / requestId;安装与使用 `dws`。 + +不要用:钉钉客户端 UI 设计、后台人工运营、未公开开放的内部能力 → 直接告知不支持。 + +## 4. Quick reference + +- **应用类型选型** → [references/quick-reference.md](references/quick-reference.md#应用类型)(企业内部 / 第三方企业 / 第三方个人 / 移动接入)。 +- **核心能力域 + 代表端点**(链接 `https://open.dingtalk.com/document/development/`;全量 8 域 1513 条见 [llm.md](llm.md) / [references/api-catalog.md](references/api-catalog.md)): + - 通讯录:[新增或修改限制查看通讯录设置](https://open.dingtalk.com/document/development/add-or-modify-visibility-settings-for-address-book-restrictions) + - 消息/IM:[添加互通群成员](https://open.dingtalk.com/document/development/add-a-group-member-1) · 机器人:[批量撤回机器人消息](https://open.dingtalk.com/document/development/batch-message-recall-chat) + - 日历:[预定会议室](https://open.dingtalk.com/document/development/add-a-meeting-room) · 待办:[创建钉钉待办任务](https://open.dingtalk.com/document/development/add-dingtalk-to-do-task) + - 审批:[归档审批实例](https://open.dingtalk.com/document/development/api-archiveprocessinstance) · 文档:[添加知识库成员](https://open.dingtalk.com/document/development/add-permissions-for-team-space-members) + - 考勤:[添加假期规则](https://open.dingtalk.com/document/development/add-holiday-rules) · 互动卡片:[更新卡片场域信息](https://open.dingtalk.com/document/development/add-field-interface) + - 鉴权:[获取企业内部应用 accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app) · 调用规范:[服务端 API 怎么调](https://open.dingtalk.com/document/development/how-to-call-apis) +- **必需 Header / 限流 / 分页 / 大小** → [references/quick-reference.md](references/quick-reference.md#header限流分页)。 +- **dws CLI 速查**(可选工具,等价于直接发 HTTP 的一种调用/发现方式;命令均 `--help` 核验): +```bash +dws auth status --format json # 看登录态 +dws auth login --device # 无头环境扫码登录 +dws devdoc article search "<关键词>" --format json # 查开发文档 +dws api GET /v1.0/contact/users/me --format json # 原始调用(自有应用凭证) +dws devapp list --name <应用名> --format json # 开放平台应用 CRUD / 凭证 / 权限 +``` + +## 5. Decision guidance + +| 选择题 | 用哪个 | 依据 | +| --- | --- | --- | +| 应用类型 | 单企业自建→企业内部应用;多企业 ISV→第三方企业应用;面向个人→第三方个人应用;端内免登→移动接入 | [quick-reference.md](references/quick-reference.md#应用类型) | +| 新版 vs 旧版 | 优先新版 `api.dingtalk.com`(Header token);仅旧接口用 `oapi.dingtalk.com`(query token) | [api-invocation.md](references/api-invocation.md) | +| token 类型 | 组织服务端能力→应用 accessToken;个人数据/免登→用户身份;端内→JSAPI | [auth.md](references/auth.md) | +| 轮询 vs 事件订阅 | 一次性结果→同步调用;持续变更→事件订阅(`configure-event-subcription`)| [reference.md](references/reference.md) | +| 访问方式 | 有产品糖命令→`dws `;只有 HTTP 契约→`dws api`;CLI 缺失→HTTPS | [api-invocation.md](references/api-invocation.md) | + +## 6. Workflow + +发现 → 前置 → 调用 → 验证 → 恢复,每步真实命令见 [references/workflow.md](references/workflow.md): + +1. **发现**:[llm.md](llm.md) 定位能力域→接口;运行态 `dws devdoc article search "<关键词>" --format json`。 +2. **建应用 / 拿凭证**:`dws devapp create ...`([危险],先 dry-run)→ `dws devapp credentials get --unified-app-id --format json`(凭证敏感,不回显)。 +3. **申请权限 scope**:`dws devapp permission add --unified-app-id --permissions --dry-run --format json` → 确认 → `--yes`。 +4. **拿 access_token**:`POST https://api.dingtalk.com/v1.0/oauth2/accessToken` body `{"appKey","appSecret"}`;token 必须缓存,禁止每请求重换。 +5. **首次调用**:`dws api {METHOD} {PATH} --data '{...}' --format json`(Header token 由 dws 注入)。 +6. **readback 校验**:成功(2xx+业务字段) / `needs_permission` / `needs_resource`;写接口走 setup→target→verify→cleanup。 +7. **错误排查**:`dws devdoc error diagnose --error-code | --request-id --query "..." --format json`,保留 `requestId`。 +8. **清理**:测试资源用完回收(删除/取消属 [危险],需确认)。 + +## 7. Common gotchas + +- MCP 默认凭证 ≠ raw API 凭证:`dws api` 只认自有应用 `--client-id/--client-secret`,MCP 加密 token 不支持 raw 调用。 +- scope 未开通就调用 → 403/无权限:先 `dws devapp permission add` 申请并发版。 +- 新旧 path / token 混用(Header token 配 `oapi` query 接口)→ 鉴权失败。 +- token 类型用错(用应用 token 调个人数据接口)→ 无权限。 +- devdoc 运行态网关 Forbidden / `PARAM_ERROR - 未找到指定工具` → 记 `needs_gateway_tool_registration`,降级 [llm.md](llm.md)。 +- 多候选默认选第一个 / 写操作未 dry-run / 丢失 requestId / 高频刷新 token → 见 [references/gotchas.md](references/gotchas.md)。 + +## 8. Verification checklist + +上线前逐项核对(详见 [references/checklist.md](references/checklist.md)):已查 llm.md 或官方文档;已解析稳定 ID;已确认 token 类型 + scope + 数据范围;Header 正确;限流 / 分页已处理;写操作已 dry-run + 确认;错误处理覆盖鉴权/权限/限流/参数;能回读则已回读;保留 operation/requestId。 + +## 9. Resources + +- [llm.md](llm.md):开放平台文档大目录(`development/` 全量有效链接,按 8 能力域归类)。 +- [references/api-catalog.md](references/api-catalog.md):同源全量有效链接目录(skill 内离线)。 +- `https://open.dingtalk.com/document`:官方文档站。 +- 细节参考:[auth.md](references/auth.md) · [quick-reference.md](references/quick-reference.md) · [api-invocation.md](references/api-invocation.md) · [workflow.md](references/workflow.md) · [reference.md](references/reference.md) · [gotchas.md](references/gotchas.md) · [checklist.md](references/checklist.md) · [discovery.md](references/discovery.md) diff --git a/skills/dingtalk-open-platform/llm.md b/skills/dingtalk-open-platform/llm.md new file mode 100644 index 00000000..ac144f46 --- /dev/null +++ b/skills/dingtalk-open-platform/llm.md @@ -0,0 +1,1743 @@ +# 钉钉开放平台文档大目录 + +> 钉钉开放平台官方文档大目录,供 AI Agent 发现接口:按能力域组织,每条为官方文档有效链接(`https://open.dingtalk.com/document/development/`,经内容长度校验)。有效 / 无效 / 总数统计见下方区块首行。发现接口后按文档调用(可用 dws CLI 或直接 HTTP)。 + +## 手册 + +- [执行手册 SKILL.md](SKILL.md): 发现 → 鉴权 → 调用 → 验证 → 排错 的分步执行手册。 +- [API 文档目录 api-catalog.md](references/api-catalog.md): 同源全量有效链接目录。 +- 官方文档站:`https://open.dingtalk.com/document` + + + + +> 全量 slug **1520** 条;有效(≥50000B) **1513** 条,无效(空壳) **7** 条;本目录收录 **1513** 条真实文档链接。 + +## 应用开发(642 条) + +二级 Tab:开发指南 / 服务端API / 客户端JSAPI / 事件订阅 / 钉钉CLI / 开发工具 / 平台服务(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 钉钉文档(71 条) + +- [修改知识库文档成员权限](https://open.dingtalk.com/document/development/update-team-space-document-user-permissions): `update-team-space-document-user-permissions` +- [创建工作表](https://open.dingtalk.com/document/development/create-a-worksheet): `create-a-worksheet` +- [创建快捷方式](https://open.dingtalk.com/document/development/api-createshortcut): `api-createshortcut` +- [创建条件格式规则](https://open.dingtalk.com/document/development/create-conditional-formatting-rules): `create-conditional-formatting-rules` +- [创建浮动图片](https://open.dingtalk.com/document/development/api-createfloatimage): `api-createfloatimage` +- [创建知识库文档](https://open.dingtalk.com/document/development/create-team-space-document): `create-team-space-document` +- [创建筛选](https://open.dingtalk.com/document/development/api-createfilter): `api-createfilter` +- [创建筛选视图](https://open.dingtalk.com/document/development/api-createfilterview): `api-createfilterview` +- [删除下拉列表](https://open.dingtalk.com/document/development/delete-drop-down-list): `delete-drop-down-list` +- [删除列](https://open.dingtalk.com/document/development/delete-column): `delete-column` +- [删除块元素](https://open.dingtalk.com/document/development/api-docdeleteblock): `api-docdeleteblock` +- [删除工作表](https://open.dingtalk.com/document/development/delete-classic-workbooks): `delete-classic-workbooks` +- [删除浮动图片](https://open.dingtalk.com/document/development/api-deletefloatimage): `api-deletefloatimage` +- [删除知识库成员](https://open.dingtalk.com/document/development/delete-team-space-user-permissions): `delete-team-space-user-permissions` +- [删除知识库文档](https://open.dingtalk.com/document/development/delete-team-space-documents): `delete-team-space-documents` +- [删除知识库文档成员](https://open.dingtalk.com/document/development/delete-team-space-document-permissions): `delete-team-space-document-permissions` +- [删除筛选](https://open.dingtalk.com/document/development/api-deletefilter): `api-deletefilter` +- [删除筛选条件](https://open.dingtalk.com/document/development/api-clearfiltercriteria): `api-clearfiltercriteria` +- [删除筛选视图](https://open.dingtalk.com/document/development/api-deletefilterview): `api-deletefilterview` +- [删除筛选视图条件](https://open.dingtalk.com/document/development/api-clearfilterviewcriteria): `api-clearfilterviewcriteria` +- [删除行](https://open.dingtalk.com/document/development/delete-row): `delete-row` +- [合并单元格](https://open.dingtalk.com/document/development/merge-cells): `merge-cells` +- [在段落末尾追加文本](https://open.dingtalk.com/document/development/api-docappendtext): `api-docappendtext` +- [在段落末尾追加行内元素](https://open.dingtalk.com/document/development/api-docappendparagraph): `api-docappendparagraph` +- [复制文档](https://open.dingtalk.com/document/development/api-copydoc): `api-copydoc` +- [工作表中追加行](https://open.dingtalk.com/document/development/append-line): `append-line` +- [批量设置列宽](https://open.dingtalk.com/document/development/api-setcolumnswidth): `api-setcolumnswidth` +- [批量设置行高](https://open.dingtalk.com/document/development/api-setrowsheight): `api-setrowsheight` +- [指定列左侧插入若干列](https://open.dingtalk.com/document/development/insert-column-before-column): `insert-column-before-column` +- [指定行上方插入若干行](https://open.dingtalk.com/document/development/insert-rows-before-rows): `insert-rows-before-rows` +- [插入下拉列表](https://open.dingtalk.com/document/development/insert-drop-down-list): `insert-drop-down-list` +- [插入内容](https://open.dingtalk.com/document/development/api-insertcontent): `api-insertcontent` +- [插入块元素](https://open.dingtalk.com/document/development/api-docinsertblocks): `api-docinsertblocks` +- [新建知识库](https://open.dingtalk.com/document/development/create-a-team-space): `create-a-team-space` +- [更新单元格区域](https://open.dingtalk.com/document/development/update-cell-properties): `update-cell-properties` +- [更新块元素](https://open.dingtalk.com/document/development/api-docblocksmodify): `api-docblocksmodify` +- [更新工作表](https://open.dingtalk.com/document/development/update-worksheet): `update-worksheet` +- [更新浮动图片](https://open.dingtalk.com/document/development/api-updatefloatimage): `api-updatefloatimage` +- [更新知识库成员权限](https://open.dingtalk.com/document/development/update-team-space-user-permissions): `update-team-space-user-permissions` +- [更新筛选](https://open.dingtalk.com/document/development/api-updatefilter): `api-updatefilter` +- [更新筛选视图](https://open.dingtalk.com/document/development/api-updatefilterview): `api-updatefilterview` +- [查找工作表中的单元格](https://open.dingtalk.com/document/development/find-the-next-eligible-cell): `find-the-next-eligible-cell` +- [查找所有符合条件的单元格](https://open.dingtalk.com/document/development/find-all-matching-cells): `find-all-matching-cells` +- [查询块元素](https://open.dingtalk.com/document/development/api-docblocksquery): `api-docblocksquery` +- [查询用户有权限的知识库列表](https://open.dingtalk.com/document/development/querying-the-list-of-user-team-spaces): `querying-the-list-of-user-team-spaces` +- [查询知识库下的目录结构](https://open.dingtalk.com/document/development/query-the-directory-tree-in-the-knowledge-base): `query-the-directory-tree-in-the-knowledge-base` +- [查询知识库信息](https://open.dingtalk.com/document/development/query-team-space): `query-team-space` +- [查询知识库节点信息](https://open.dingtalk.com/document/development/query-knowledge-base-node-information): `query-knowledge-base-node-information` +- [根据 dentryUuid 获取 spaceId](https://open.dingtalk.com/document/development/api-getdentryidbyuuid): `api-getdentryidbyuuid` +- [添加知识库成员](https://open.dingtalk.com/document/development/add-permissions-for-team-space-members): `add-permissions-for-team-space-members` +- [添加知识库文档成员](https://open.dingtalk.com/document/development/add-workspace-document-user-permissions): `add-workspace-document-user-permissions` +- [清除单元格区域内所有内容](https://open.dingtalk.com/document/development/clear-all): `clear-all` +- [清除单元格区域内数据](https://open.dingtalk.com/document/development/clear-cell-data): `clear-cell-data` +- [知识库转交所有者](https://open.dingtalk.com/document/development/api-handoveryworkspace): `api-handoveryworkspace` +- [筛选排序](https://open.dingtalk.com/document/development/api-sortfilter): `api-sortfilter` +- [置顶知识库](https://open.dingtalk.com/document/development/api-pinspace): `api-pinspace` +- [获取 dentryUuid 信息](https://open.dingtalk.com/document/development/api-getuuidbydentryid): `api-getuuidbydentryid` +- [获取任务状态](https://open.dingtalk.com/document/development/api-gettaskinfo): `api-gettaskinfo` +- [获取单元格区域](https://open.dingtalk.com/document/development/get-cell-properties): `get-cell-properties` +- [获取工作表](https://open.dingtalk.com/document/development/obtain-worksheet-properties): `obtain-worksheet-properties` +- [获取所有工作表](https://open.dingtalk.com/document/development/obtain-all-worksheets): `obtain-all-worksheets` +- [获取筛选](https://open.dingtalk.com/document/development/api-getfilter): `api-getfilter` +- [获取筛选视图列表](https://open.dingtalk.com/document/development/api-getfilterviews): `api-getfilterviews` +- [获取资源上传信息](https://open.dingtalk.com/document/development/api-getresourceuploadinfo): `api-getresourceuploadinfo` +- [覆写文档(个人授权)](https://open.dingtalk.com/document/development/api-docupdatecontent): `api-docupdatecontent` +- [覆写文档(应用授权)](https://open.dingtalk.com/document/development/api-doc-updatecontent): `api-doc-updatecontent` +- [设置列隐藏或显示](https://open.dingtalk.com/document/development/set-column-visibility): `set-column-visibility` +- [设置筛选条件](https://open.dingtalk.com/document/development/api-setfiltercriteria): `api-setfiltercriteria` +- [设置筛选视图条件](https://open.dingtalk.com/document/development/api-setfilterviewcriteria): `api-setfilterviewcriteria` +- [设置自动行高](https://open.dingtalk.com/document/development/set-row-height-automatically): `set-row-height-automatically` +- [设置行隐藏或显示](https://open.dingtalk.com/document/development/set-row-visibility): `set-row-visibility` + +### 存储(53 条) + +- [修改权限](https://open.dingtalk.com/document/development/modify-permissions-file): `modify-permissions-file` +- [修改权限](https://open.dingtalk.com/document/development/modify-storage-permissions): `modify-storage-permissions` +- [初始化文件分片上传](https://open.dingtalk.com/document/development/initialize-a-multipart-upload-object): `initialize-a-multipart-upload-object` +- [删除回收项](https://open.dingtalk.com/document/development/delete-recycle-item): `delete-recycle-item` +- [删除文件或文件夹](https://open.dingtalk.com/document/development/delete-a-file-or-folder): `delete-a-file-or-folder` +- [删除文件或文件夹的应用属性](https://open.dingtalk.com/document/development/delete-file-app-attribute): `delete-file-app-attribute` +- [删除权限](https://open.dingtalk.com/document/development/delete-permissions-file): `delete-permissions-file` +- [删除权限](https://open.dingtalk.com/document/development/delete-storage-permissions): `delete-storage-permissions` +- [取消订阅文件变更事件](https://open.dingtalk.com/document/development/unsubscribe-from-file-change-events): `unsubscribe-from-file-change-events` +- [复制文件或文件夹](https://open.dingtalk.com/document/development/copy-an-object): `copy-an-object` +- [恢复文件历史版本](https://open.dingtalk.com/document/development/restore-previous-versions-of-files): `restore-previous-versions-of-files` +- [批量删除回收项](https://open.dingtalk.com/document/development/batch-delete-recycle-items): `batch-delete-recycle-items` +- [批量删除文件或文件夹](https://open.dingtalk.com/document/development/delete-files-or-folders-in-bulk): `delete-files-or-folders-in-bulk` +- [批量复制文件或文件夹](https://open.dingtalk.com/document/development/copy-files-or-folders-in-bulk): `copy-files-or-folders-in-bulk` +- [批量移动文件或文件夹](https://open.dingtalk.com/document/development/bulk-move-files-or-folders): `bulk-move-files-or-folders` +- [批量获取文件或文件夹信息](https://open.dingtalk.com/document/development/get-file-or-folder-information-in-bulk): `get-file-or-folder-information-in-bulk` +- [批量获取文件缩略图](https://open.dingtalk.com/document/development/get-file-thumbnails-in-bulk): `get-file-thumbnails-in-bulk` +- [批量还原回收项](https://open.dingtalk.com/document/development/batch-restore-recycled-items): `batch-restore-recycled-items` +- [提交文件](https://open.dingtalk.com/document/development/submit-documents): `submit-documents` +- [提交文件](https://open.dingtalk.com/document/development/submittal-file): `submittal-file` +- [搜索文件](https://open.dingtalk.com/document/development/search-for-files): `search-for-files` +- [搜索知识库](https://open.dingtalk.com/document/development/search-knowledge-base): `search-knowledge-base` +- [更新文件或文件夹的应用属性](https://open.dingtalk.com/document/development/update-file-application-properties): `update-file-application-properties` +- [添加文件夹](https://open.dingtalk.com/document/development/add-folder): `add-folder` +- [添加权限](https://open.dingtalk.com/document/development/add-permissions-file): `add-permissions-file` +- [添加权限](https://open.dingtalk.com/document/development/add-storage-permissions): `add-storage-permissions` +- [添加空间](https://open.dingtalk.com/document/development/add-space): `add-space` +- [清空回收站](https://open.dingtalk.com/document/development/empty-the-recycle-bin): `empty-the-recycle-bin` +- [知识库下载文件](https://open.dingtalk.com/document/development/knowledge-base-download-file): `knowledge-base-download-file` +- [移动文件或文件夹](https://open.dingtalk.com/document/development/move-a-file-or-folder): `move-a-file-or-folder` +- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-storage-related-information): `obtain-enterprise-storage-related-information` +- [获取回收站信息](https://open.dingtalk.com/document/development/obtain-information-about-the-recycle-bin): `obtain-information-about-the-recycle-bin` +- [获取回收项信息](https://open.dingtalk.com/document/development/obtain-recycling-item-information): `obtain-recycling-item-information` +- [获取回收项列表](https://open.dingtalk.com/document/development/gets-the-list-of-recycle-items): `gets-the-list-of-recycle-items` +- [获取存储中异步任务信息](https://open.dingtalk.com/document/development/get-the-asynchronous-task-information-in-storage): `get-the-asynchronous-task-information-in-storage` +- [获取应用信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-current-application): `obtains-the-information-about-the-current-application` +- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-file-upload-informations): `obtain-file-upload-informations` +- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-storage-upload-information): `obtain-storage-upload-information` +- [获取文件下载信息](https://open.dingtalk.com/document/development/obtains-the-download-information-about-a-file): `obtains-the-download-information-about-a-file` +- [获取文件分片上传信息](https://open.dingtalk.com/document/development/obtains-the-information-about-multipart-uploads-of-an-object): `obtains-the-information-about-multipart-uploads-of-an-object` +- [获取文件或文件夹信息](https://open.dingtalk.com/document/development/obtain-file-or-folder-information): `obtain-file-or-folder-information` +- [获取文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-files-or-folders): `get-a-list-of-files-or-folders` +- [获取文件版本列表](https://open.dingtalk.com/document/development/obtains-a-list-of-file-versions): `obtains-a-list-of-file-versions` +- [获取文件预览或编辑信息](https://open.dingtalk.com/document/development/obtains-the-object-preview-or-editing-information): `obtains-the-object-preview-or-editing-information` +- [获取权限列表](https://open.dingtalk.com/document/development/get-permission-list): `get-permission-list` +- [获取权限列表](https://open.dingtalk.com/document/development/get-the-storage-permission-list): `get-the-storage-permission-list` +- [获取权限继承模式](https://open.dingtalk.com/document/development/get-permission-inheritance-mode): `get-permission-inheritance-mode` +- [获取空间下所有文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-all-files-or-folders-under-a): `get-a-list-of-all-files-or-folders-under-a` +- [获取空间信息](https://open.dingtalk.com/document/development/get-space-information): `get-space-information` +- [订阅文件变更事件](https://open.dingtalk.com/document/development/subscribe-to-file-change-events): `subscribe-to-file-change-events` +- [设置权限继承模式](https://open.dingtalk.com/document/development/set-permission-inheritance-mode): `set-permission-inheritance-mode` +- [还原回收项](https://open.dingtalk.com/document/development/restore-recycle-items): `restore-recycle-items` +- [重命名文件或文件夹](https://open.dingtalk.com/document/development/rename-a-file-or-folder): `rename-a-file-or-folder` + +### 项目管理(50 条) + +- [任务迁移至回收站](https://open.dingtalk.com/document/development/archive-tasks): `archive-tasks` +- [创建实际工时](https://open.dingtalk.com/document/development/create-actual-work): `create-actual-work` +- [创建或更新项目概览中自定义字段值](https://open.dingtalk.com/document/development/create-or-update-field-values-project-overview): `create-or-update-field-values-project-overview` +- [创建自由任务](https://open.dingtalk.com/document/development/create-a-free-task): `create-a-free-task` +- [创建计划工时](https://open.dingtalk.com/document/development/create-planned-work): `create-planned-work` +- [创建项目](https://open.dingtalk.com/document/development/create-project): `create-project` +- [创建项目任务](https://open.dingtalk.com/document/development/create-a-project-task): `create-a-project-task` +- [删除任务](https://open.dingtalk.com/document/development/delete-task): `delete-task` +- [删除项目成员](https://open.dingtalk.com/document/development/delete-project-members): `delete-project-members` +- [增加或删除自由任务的参与者](https://open.dingtalk.com/document/development/change-task-participant): `change-task-participant` +- [归档项目](https://open.dingtalk.com/document/development/archiving-project): `archiving-project` +- [恢复项目归档](https://open.dingtalk.com/document/development/cancel-project-archiving): `cancel-project-archiving` +- [批量获取自由任务详情](https://open.dingtalk.com/document/development/obtains-details-about-multiple-free-tasks): `obtains-details-about-multiple-free-tasks` +- [搜索任务工作流状态](https://open.dingtalk.com/document/development/search-task-workflow-status): `search-task-workflow-status` +- [搜索企业项目模板](https://open.dingtalk.com/document/development/search-for-enterprise-custom-templates-by-project-template-name): `search-for-enterprise-custom-templates-by-project-template-name` +- [更新任务优先级](https://open.dingtalk.com/document/development/update-task-priority): `update-task-priority` +- [更新任务参与者](https://open.dingtalk.com/document/development/update-task-participants): `update-task-participants` +- [更新任务备注](https://open.dingtalk.com/document/development/update-task-notes): `update-task-notes` +- [更新任务工作流状态](https://open.dingtalk.com/document/development/update-task-workflow-status): `update-task-workflow-status` +- [更新任务开始时间](https://open.dingtalk.com/document/development/update-task-start-time): `update-task-start-time` +- [更新任务截止时间](https://open.dingtalk.com/document/development/update-task-deadline): `update-task-deadline` +- [更新任务执行者](https://open.dingtalk.com/document/development/update-task-performer): `update-task-performer` +- [更新任务标题](https://open.dingtalk.com/document/development/update-task-content): `update-task-content` +- [更新自由任务备注](https://open.dingtalk.com/document/development/update-free-task-notes): `update-free-task-notes` +- [更新自由任务截止时间](https://open.dingtalk.com/document/development/change-free-task-deadline): `change-free-task-deadline` +- [更新自由任务执行者](https://open.dingtalk.com/document/development/change-free-task-executor): `change-free-task-executor` +- [更新自由任务标题](https://open.dingtalk.com/document/development/change-free-task-title): `change-free-task-title` +- [更新自由任务状态](https://open.dingtalk.com/document/development/change-free-task-status): `change-free-task-status` +- [更新自由任务的优先级](https://open.dingtalk.com/document/development/change-free-task-priority): `change-free-task-priority` +- [更新项目任务的自定义字段值](https://open.dingtalk.com/document/development/update-task-custom-field-value): `update-task-custom-field-value` +- [更新项目所在的分组](https://open.dingtalk.com/document/development/update-project-grouping): `update-project-grouping` +- [查询任务分组](https://open.dingtalk.com/document/development/query-task-grouping): `query-task-grouping` +- [查询任务工作流](https://open.dingtalk.com/document/development/query-task-workflow): `query-task-workflow` +- [查询优先级列表](https://open.dingtalk.com/document/development/query-a-priority-list): `query-a-priority-list` +- [查询员工可见的项目分组](https://open.dingtalk.com/document/development/query-available-project-groups): `query-available-project-groups` +- [查询用户任务信息列表](https://open.dingtalk.com/document/development/querying-user-tasks): `querying-user-tasks` +- [查询项目](https://open.dingtalk.com/document/development/query-enterprise-all-projects): `query-enterprise-all-projects` +- [查询项目中的任务](https://open.dingtalk.com/document/development/query-tasks-in-a-project): `query-tasks-in-a-project` +- [查询项目状态](https://open.dingtalk.com/document/development/query-project-status): `query-project-status` +- [根据userId获取Teambition项目用户ID](https://open.dingtalk.com/document/development/obtain-dingtalk-teambition-user-id-based-on-userid): `obtain-dingtalk-teambition-user-id-based-on-userid` +- [根据项目模板创建项目](https://open.dingtalk.com/document/development/create-a-project-from-a-project-template): `create-a-project-from-a-project-template` +- [添加任务的关联内容](https://open.dingtalk.com/document/development/create-a-linked-object-associated-with-a-task): `create-a-linked-object-associated-with-a-task` +- [添加项目成员](https://open.dingtalk.com/document/development/add-project-members): `add-project-members` +- [获取Teambition项目企业ID](https://open.dingtalk.com/document/development/obtain-the-teambition-enterprise-id): `obtain-the-teambition-enterprise-id` +- [获取任务列表](https://open.dingtalk.com/document/development/get-task-list): `get-task-list` +- [获取任务详情](https://open.dingtalk.com/document/development/get-task-details): `get-task-details` +- [获取用户加入的项目](https://open.dingtalk.com/document/development/get-projects-joined-by-users): `get-projects-joined-by-users` +- [获取自由任务详情](https://open.dingtalk.com/document/development/queries-free-task-details): `queries-free-task-details` +- [获取项目成员](https://open.dingtalk.com/document/development/get-project-members): `get-project-members` +- [项目放入回收站](https://open.dingtalk.com/document/development/items-in-recycle-bin): `items-in-recycle-bin` + +### 通讯录(49 条) + +- [企业账号修改钉钉号](https://open.dingtalk.com/document/development/api-changedingtalkid): `api-changedingtalkid` +- [企业账号转交主管理员(创建者)](https://open.dingtalk.com/document/development/transfer-exclusive-account-to-main-administrator-creator): `transfer-exclusive-account-to-main-administrator-creator` +- [停用企业账号](https://open.dingtalk.com/document/development/disable-an-exclusive-account): `disable-an-exclusive-account` +- [创建上下游组织](https://open.dingtalk.com/document/development/create-a-cooperation-space): `create-a-cooperation-space` +- [删除用户属性可见性设置](https://open.dingtalk.com/document/development/delete-enterprise-employee-attribute-field-visibility-settings): `delete-enterprise-employee-attribute-field-visibility-settings` +- [删除通讯录隐藏设置](https://open.dingtalk.com/document/development/delete-hide-settings): `delete-hide-settings` +- [删除限制查看通讯录设置](https://open.dingtalk.com/document/development/delete-visible-restrictions): `delete-visible-restrictions` +- [启用企业账号](https://open.dingtalk.com/document/development/enable-a-dedicated-account): `enable-a-dedicated-account` +- [异步转译通讯录ID](https://open.dingtalk.com/document/development/asynchronous-address-book-file-content-translation): `asynchronous-address-book-file-content-translation` +- [强制登出企业账号](https://open.dingtalk.com/document/development/force-logout-from-dedicated-account): `force-logout-from-dedicated-account` +- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/apply-for-batch-addition-through-upstream-and-downstream-organizations): `apply-for-batch-addition-through-upstream-and-downstream-organizations` +- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/batch-through-the-application-of-partner-organizations-to-join-contact): `batch-through-the-application-of-partner-organizations-to-join-contact` +- [授权企业账号可加入多组织](https://open.dingtalk.com/document/development/authorize-a-dedicated-account-to-join-multiple-organizations): `authorize-a-dedicated-account-to-join-multiple-organizations` +- [授权其他组织查看本组织的企业账号信息](https://open.dingtalk.com/document/development/api-orgaccountmobilevisibleinotherorg): `api-orgaccountmobilevisibleinotherorg` +- [搜索用户userId](https://open.dingtalk.com/document/development/address-book-search-user-id): `address-book-search-user-id` +- [搜索部门ID](https://open.dingtalk.com/document/development/address-book-search-department-id): `address-book-search-department-id` +- [新增或修改限制查看通讯录设置](https://open.dingtalk.com/document/development/add-or-modify-visibility-settings-for-address-book-restrictions): `add-or-modify-visibility-settings-for-address-book-restrictions` +- [新增或更新通讯录隐藏设置](https://open.dingtalk.com/document/development/update-address-book-hide-settings): `update-address-book-hide-settings` +- [更新伙伴组织在上下游组织内的属性信息](https://open.dingtalk.com/document/development/update-properties-of-branches-in-alibaba-group-1): `update-properties-of-branches-in-alibaba-group-1` +- [更新分支组织在主干组织内的属性信息](https://open.dingtalk.com/document/development/updates-the-property-information-of-a-branch-organization-in-a): `updates-the-property-information-of-a-branch-organization-in-a` +- [查询企业账号拥有的组织](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-organization-that): `you-can-call-this-operation-to-query-the-organization-that` +- [查询企业账号状态](https://open.dingtalk.com/document/development/query-dedicated-account-status-1): `query-dedicated-account-status-1` +- [查询离职记录列表](https://open.dingtalk.com/document/development/query-the-details-of-employees-who-have-left-office): `query-the-details-of-employees-who-have-left-office` +- [根据原dingId查询迁移后的dingId](https://open.dingtalk.com/document/development/query-the-new-dingid-based-on-the-original-dingid): `query-the-new-dingid-based-on-the-original-dingid` +- [根据原unionId查询迁移后的unionId](https://open.dingtalk.com/document/development/the-union-id-that-you-want-to-query-you-can): `the-union-id-that-you-want-to-query-you-can` +- [根据手机号查询用户](https://open.dingtalk.com/document/development/query-users-by-phone-number): `query-users-by-phone-number` +- [根据迁移后的dingId查询原dingId](https://open.dingtalk.com/document/development/query-the-original-dingid-based-on-the-dingid-after-migration): `query-the-original-dingid-based-on-the-dingid-after-migration` +- [根据迁移后的unionId查询原unionId](https://open.dingtalk.com/document/development/query-the-original-union-id-based-on-the-union-id): `query-the-original-union-id-based-on-the-union-id` +- [获取上下游组织的邀请信息](https://open.dingtalk.com/document/development/obtain-the-invitation-information-of-a-cooperation-space): `obtain-the-invitation-information-of-a-cooperation-space` +- [获取上下级组织分支授权的数据](https://open.dingtalk.com/document/development/data-authorized-by-a-branch-of-an-associated-organization): `data-authorized-by-a-branch-of-an-associated-organization` +- [获取企业最新钉钉指数信息](https://open.dingtalk.com/document/development/queries-the-latest-dingtalk-index-information): `queries-the-latest-dingtalk-index-information` +- [获取企业认证信息](https://open.dingtalk.com/document/development/obtain-enterprise-authentication-information): `obtain-enterprise-authentication-information` +- [获取企业邀请信息](https://open.dingtalk.com/document/development/obtain-invitation-information): `obtain-invitation-information` +- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-the-list-of-sub-department-ids): `obtain-the-list-of-sub-department-ids` +- [获取异步转译任务结果](https://open.dingtalk.com/document/development/obtains-the-results-of-an-asynchronous-translation-task): `obtains-the-results-of-an-asynchronous-translation-task` +- [获取用户属性可见性设置](https://open.dingtalk.com/document/development/pull-hidden-property-field-for-enterprise-employees): `pull-hidden-property-field-for-enterprise-employees` +- [获取用户通讯录个人信息](https://open.dingtalk.com/document/development/dingtalk-retrieve-user-information): `dingtalk-retrieve-user-information` +- [获取用户高管模式设置](https://open.dingtalk.com/document/development/get-user-executive-mode-settings): `get-user-executive-mode-settings` +- [获取通讯录隐藏设置](https://open.dingtalk.com/document/development/obtains-the-hide-settings-of-the-address-book): `obtains-the-hide-settings-of-the-address-book` +- [获取部门用户基础信息](https://open.dingtalk.com/document/development/queries-the-simple-information-of-a-department-user): `queries-the-simple-information-of-a-department-user` +- [获取限制查看通讯录设置列表](https://open.dingtalk.com/document/development/gets-a-list-of-address-book-limit-visibility-settings): `gets-a-list-of-address-book-limit-visibility-settings` +- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-an-organization): `disassociate-an-organization` +- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-upstream-and-downstream-organizations): `disassociate-upstream-and-downstream-organizations` +- [设置伙伴组织在上下游组织内的可见范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-branch-in-the-group-1): `set-the-visible-range-of-the-branch-in-the-group-1` +- [设置分支组织在主干组织内的可见范围](https://open.dingtalk.com/document/development/sets-the-visible-range-of-branch-organizations-within-the-group): `sets-the-visible-range-of-branch-organizations-within-the-group` +- [设置用户属性可见性](https://open.dingtalk.com/document/development/add-or-update-the-hidden-settings-of-the-employee-property): `add-or-update-the-hidden-settings-of-the-employee-property` +- [设置部门可见性优先级](https://open.dingtalk.com/document/development/set-address-book-visibility-sub-department-settings-to-take-precedence): `set-address-book-visibility-sub-department-settings-to-take-precedence` +- [设置高管模式](https://open.dingtalk.com/document/development/update-executive-settings): `update-executive-settings` +- [通讯录userId排序](https://open.dingtalk.com/document/development/address-book-userid-sorting): `address-book-userid-sorting` + +### 电子签(41 条) + +- [ISV服务商数据初始化](https://open.dingtalk.com/document/development/offline-isv-service-provider-data-initialization): `offline-isv-service-provider-data-initialization` +- [e签宝数据初始化](https://open.dingtalk.com/document/development/isv-service-provider-data-initialization): `isv-service-provider-data-initialization` +- [创建签署流程](https://open.dingtalk.com/document/development/use-the-api-to-initiate-a-signature-process): `use-the-api-to-initiate-a-signature-process` +- [取消企业授权](https://open.dingtalk.com/document/development/cancel-enterprise-authorization): `cancel-enterprise-authorization` +- [取消企业授权](https://open.dingtalk.com/document/development/e-sign-has-revoked-the-enterprise-authorization): `e-sign-has-revoked-the-enterprise-authorization` +- [套餐转售---分润模式](https://open.dingtalk.com/document/development/package-resale-1-distribution-mode): `package-resale-1-distribution-mode` +- [套餐转售---底价结算模式](https://open.dingtalk.com/document/development/package-resale-2-reserve-price-settlement-mode): `package-resale-2-reserve-price-settlement-mode` +- [套餐转售(分润模式)](https://open.dingtalk.com/document/development/package-resale-profit-distribution-model-1): `package-resale-profit-distribution-model-1` +- [套餐转售(底价结算模式)](https://open.dingtalk.com/document/development/package-resale-base-price-settlement-mode-1): `package-resale-base-price-settlement-mode-1` +- [查询个人是否实名认证](https://open.dingtalk.com/document/development/query-personal-information): `query-personal-information` +- [查询企业是否实名](https://open.dingtalk.com/document/development/check-whether-enterprise-registered-realname-e-sign-treasure): `check-whether-enterprise-registered-realname-e-sign-treasure` +- [查询企业是否实名认证](https://open.dingtalk.com/document/development/query-enterprise-information): `query-enterprise-information` +- [查询套餐余量](https://open.dingtalk.com/document/development/query-package-balance): `query-package-balance` +- [查询用户是否实名](https://open.dingtalk.com/document/development/query-whether-a-user-has-a-real-name): `query-whether-a-user-has-a-real-name` +- [获取个人实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-user-s-real): `obtain-the-address-that-is-redirected-to-the-user-s-real` +- [获取企业e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-status-of-enterprise-e-sign-treasure-micro-application): `obtain-the-status-of-enterprise-e-sign-treasure-micro-application` +- [获取企业实名地址](https://open.dingtalk.com/document/development/obtain-enterprise-real-name-address): `obtain-enterprise-real-name-address` +- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-enterprise-console-address): `get-enterprise-console-address` +- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-the-address-enterprise-console-through-e-sign): `get-the-address-enterprise-console-through-e-sign` +- [获取企业的e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-current-status-of-the-company-s-e-sign-micro-application): `obtain-the-current-status-of-the-company-s-e-sign-micro-application` +- [获取发起签署任务地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-initiating-signing-task): `obtain-the-address-of-the-initiating-signing-task` +- [获取发起签署任务的地址](https://open.dingtalk.com/document/development/obtain-the-address-used-to-initiate-a-signed-task): `obtain-the-address-used-to-initiate-a-signed-task` +- [获取套餐余量](https://open.dingtalk.com/document/development/get-package-margin): `get-package-margin` +- [获取授权的页面地址](https://open.dingtalk.com/document/development/authurl): `authurl` +- [获取授权的页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-authorized-page): `obtain-the-address-of-the-authorized-page` +- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-file-upload-address-1): `obtain-the-file-upload-address-1` +- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-1): `obtain-the-upload-url-of-a-file-1` +- [获取文件详情](https://open.dingtalk.com/document/development/gets-the-file-details): `gets-the-file-details` +- [获取文件详情](https://open.dingtalk.com/document/development/query-file-details): `query-file-details` +- [获取流程任务合同列表](https://open.dingtalk.com/document/development/obtain-the-process-task-contract-list): `obtain-the-process-task-contract-list` +- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtain-the-process-task-print-approval-list-1): `obtain-the-process-task-print-approval-list-1` +- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtains-the-print-approval-list-for-process-tasks): `obtains-the-print-approval-list-for-process-tasks` +- [获取流程任务的所有合同列表](https://open.dingtalk.com/document/development/get-a-list-of-all-contracts-for-the-process-task): `get-a-list-of-all-contracts-for-the-process-task` +- [获取流程任务详情](https://open.dingtalk.com/document/development/obtain-the-task-details-of-the-corresponding-process): `obtain-the-task-details-of-the-corresponding-process` +- [获取流程的签署详情](https://open.dingtalk.com/document/development/get-the-details-of-process-signing): `get-the-details-of-process-signing` +- [获取流程签署详细信息](https://open.dingtalk.com/document/development/get-process-sign-off-details): `get-process-sign-off-details` +- [获取流程详细信息及操作记录](https://open.dingtalk.com/document/development/obtains-the-task-details): `obtains-the-task-details` +- [获取用户实名地址](https://open.dingtalk.com/document/development/obtain-personal-real-name-address): `obtain-personal-real-name-address` +- [获取签署人签署地址](https://open.dingtalk.com/document/development/get-signatory-address): `get-signatory-address` +- [获取签署人签署地址](https://open.dingtalk.com/document/development/obtain-signing-address-of-signatory-1): `obtain-signing-address-of-signatory-1` +- [获取跳转到企业实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-enterprise-s-real): `obtain-the-address-that-is-redirected-to-the-enterprise-s-real` + +### 消息会话(37 条) + +- [人与人会话中机器人发送互动卡片](https://open.dingtalk.com/document/development/send-dingtalk-interactive-cards-to-person-to-person-chat-sessions): `send-dingtalk-interactive-cards-to-person-to-person-chat-sessions` +- [修改钉钉客联互通群名称](https://open.dingtalk.com/document/development/modify-the-group-name): `modify-the-group-name` +- [修改钉钉客联互通群头像](https://open.dingtalk.com/document/development/modify-the-avatar-of-a-communication-group): `modify-the-avatar-of-a-communication-group` +- [关闭互动卡片吊顶](https://open.dingtalk.com/document/development/close-interactive-card-ceiling): `close-interactive-card-ceiling` +- [创建互通群](https://open.dingtalk.com/document/development/create-an-intercommunication-group): `create-an-intercommunication-group` +- [创建并开启互动卡片吊顶](https://open.dingtalk.com/document/development/send-group-helper-message): `send-group-helper-message` +- [创建店铺群](https://open.dingtalk.com/document/development/create-a-store-group): `create-a-store-group` +- [创建钉外两人群](https://open.dingtalk.com/document/development/create-two-people-outside-the-nail): `create-two-people-outside-the-nail` +- [创建钉钉客联两人互通群](https://open.dingtalk.com/document/development/creating-two-groups-of-people): `creating-two-groups-of-people` +- [创建钉钉客联普通互通群](https://open.dingtalk.com/document/development/create-common-group-new-version): `create-common-group-new-version` +- [创建钉钉客联钉外账号](https://open.dingtalk.com/document/development/create-bc-account-association): `create-bc-account-association` +- [发送轻量级互动卡片](https://open.dingtalk.com/document/development/send-lightweight-interactive-cards): `send-lightweight-interactive-cards` +- [发送钉钉互动卡片(高级版)](https://open.dingtalk.com/document/development/send-interactive-dynamic-cards-1): `send-interactive-dynamic-cards-1` +- [在钉钉客联互通群中使用机器人发送消息](https://open.dingtalk.com/document/development/group-robots-send-messages): `group-robots-send-messages` +- [在钉钉客联互通群中使用钉内账号发送消息](https://open.dingtalk.com/document/development/send-b2c-messages): `send-b2c-messages` +- [在钉钉客联互通群中使用钉外账号发送消息](https://open.dingtalk.com/document/development/send-c2b-messages): `send-c2b-messages` +- [批量查询跨钉两人互通群列表](https://open.dingtalk.com/document/development/queries-the-session-information-of-two-population-groups): `queries-the-session-information-of-two-population-groups` +- [批量设置企业群管理员](https://open.dingtalk.com/document/development/batch-setup-group-administrator): `batch-setup-group-administrator` +- [更换钉钉客联互通群群主](https://open.dingtalk.com/document/development/change-group-owner): `change-group-owner` +- [更新机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/update-the-robot-to-send-interactive-cards): `update-the-robot-to-send-interactive-cards` +- [更新群成员的群昵称](https://open.dingtalk.com/document/development/update-group-nicknames): `update-group-nicknames` +- [更新群管理员](https://open.dingtalk.com/document/development/update-group-administrators): `update-group-administrators` +- [更新钉钉互动卡片](https://open.dingtalk.com/document/development/update-dingtalk-interactive-cards): `update-dingtalk-interactive-cards` +- [机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/robots-send-interactive-cards): `robots-send-interactive-cards` +- [查询群内群模板机器人](https://open.dingtalk.com/document/development/search-group-scene-template-robot): `search-group-scene-template-robot` +- [查询群成员](https://open.dingtalk.com/document/development/query-group-members): `query-group-members` +- [查询群禁言状态](https://open.dingtalk.com/document/development/query-group-silence-status): `query-group-silence-status` +- [查询群简要信息](https://open.dingtalk.com/document/development/query-group-information): `query-group-information` +- [查询钉钉客联互通群成员列表](https://open.dingtalk.com/document/development/queries-the-group-member-list): `queries-the-group-member-list` +- [查询钉钉客联钉外账号未读消息数](https://open.dingtalk.com/document/development/querying-the-number-of-unread-messages-of-the-user): `querying-the-number-of-unread-messages-of-the-user` +- [添加钉钉客联互通群成员](https://open.dingtalk.com/document/development/add-a-group-member-1): `add-a-group-member-1` +- [移除钉钉客联互通群成员](https://open.dingtalk.com/document/development/remove-group-members): `remove-group-members` +- [获取群会话的OpenConversationId](https://open.dingtalk.com/document/development/obtain-group-openconversationid): `obtain-group-openconversationid` +- [获取钉钉客联H5页面地址](https://open.dingtalk.com/document/development/get-the-dingtalk-guest-group-session-address): `get-the-dingtalk-guest-group-session-address` +- [解散群聊](https://open.dingtalk.com/document/development/api-dsbandopenscenegroup): `api-dsbandopenscenegroup` +- [解散钉钉客联互通群](https://open.dingtalk.com/document/development/disband-bc-interconnection-group): `disband-bc-interconnection-group` +- [设置群成员禁言状态](https://open.dingtalk.com/document/development/set-group-members-access-control): `set-group-members-access-control` + +### 智能 CRM(34 条) + +- [创建个人或企业客户数据](https://open.dingtalk.com/document/development/add-crm-personal-customers): `add-crm-personal-customers` +- [创建客户群](https://open.dingtalk.com/document/development/create-a-customer-group): `create-a-customer-group` +- [创建客户群组](https://open.dingtalk.com/document/development/crm-create-group): `crm-create-group` +- [删除CRM自定义对象数据](https://open.dingtalk.com/document/development/delete-crm-custom-object-data): `delete-crm-custom-object-data` +- [删除个人或企业客户数据](https://open.dingtalk.com/document/development/delete-crm-personal-customer): `delete-crm-personal-customer` +- [发送服务窗单人消息](https://open.dingtalk.com/document/development/sends-a-single-message-from-the-service-window): `sends-a-single-message-from-the-service-window` +- [批量修改联系人数据](https://open.dingtalk.com/document/development/modify-contact-data-in-batches): `modify-contact-data-in-batches` +- [批量删除跟进记录数据](https://open.dingtalk.com/document/development/batch-delete-follow-up-record-data): `batch-delete-follow-up-record-data` +- [批量发送服务窗消息](https://open.dingtalk.com/document/development/batch-sending-of-service-window-messages): `batch-sending-of-service-window-messages` +- [批量新增个人或企业客户数据](https://open.dingtalk.com/document/development/add-multiple-relationship-data-in-batches): `add-multiple-relationship-data-in-batches` +- [批量新增联系人数据](https://open.dingtalk.com/document/development/add-contact-data-in-batches): `add-contact-data-in-batches` +- [批量新增跟进记录数据](https://open.dingtalk.com/document/development/batch-add-follow-up-record-data): `batch-add-follow-up-record-data` +- [批量更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-multiple-relational-data-tables-at-a-time): `update-multiple-relational-data-tables-at-a-time` +- [批量更新跟进记录数据](https://open.dingtalk.com/document/development/batch-update-follow-up-record-data): `batch-update-follow-up-record-data` +- [批量查询客户群](https://open.dingtalk.com/document/development/query-customer-groups-in-batches): `query-customer-groups-in-batches` +- [批量获取个人或企业客户数据](https://open.dingtalk.com/document/development/acquire-crm-individual-customers-in-batches): `acquire-crm-individual-customers-in-batches` +- [更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-crm-personal-customers): `update-crm-personal-customers` +- [更新客户群组](https://open.dingtalk.com/document/development/crm-update-group): `crm-update-group` +- [查询客户数据](https://open.dingtalk.com/document/development/querying-customer-data): `querying-customer-data` +- [查询客户群列表](https://open.dingtalk.com/document/development/query-the-list-of-customer-groups): `query-the-list-of-customer-groups` +- [查询客户群组列表](https://open.dingtalk.com/document/development/query-groups): `query-groups` +- [查询服务窗粉丝用户基础信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-fans-in-the-service-window): `queries-the-basic-information-of-fans-in-the-service-window` +- [根据指定条件查询个人或企业客户数据](https://open.dingtalk.com/document/development/obtains-crm-individual-customers-in-batches-based-on-specified-query): `obtains-crm-individual-customers-in-batches-based-on-specified-query` +- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/api-getcontacts): `api-getcontacts` +- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/api-getobjectdata): `api-getobjectdata` +- [第三方个人应用发送服务窗单人消息](https://open.dingtalk.com/document/development/a-third-party-personal-application-sends-a-message-to-a-single): `a-third-party-personal-application-sends-a-message-to-a-single` +- [获取个人或企业客户查重字段](https://open.dingtalk.com/document/development/obtain-duplicate-check-fields): `obtain-duplicate-check-fields` +- [获取个人或企业客户的元数据](https://open.dingtalk.com/document/development/obtain-the-metadata-of-individual-enterprise-customers): `obtain-the-metadata-of-individual-enterprise-customers` +- [获取全量个人或企业客户数据](https://open.dingtalk.com/document/development/crm-obtains-all-private-sea-customer-data): `crm-obtains-all-private-sea-customer-data` +- [获取单个客户群组详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-single-customer-group): `queries-the-details-of-a-single-customer-group` +- [获取单个客户群详情](https://open.dingtalk.com/document/development/obtain-a-single-customer-group): `obtain-a-single-customer-group` +- [获取审批中创建与CRM客户关联的TAB表单元数据](https://open.dingtalk.com/document/development/api-getrelatedviewtabmeta): `api-getrelatedviewtabmeta` +- [获取审批里创建的与CRM客户关联的TAB表单数据实例列表](https://open.dingtalk.com/document/development/api-getrelatedviewtabdata): `api-getrelatedviewtabdata` +- [获取客户管理全局信息](https://open.dingtalk.com/document/development/get-customer-management-global-information): `get-customer-management-global-information` + +### 日历(30 条) + +- [修改日程](https://open.dingtalk.com/document/development/modify-event): `modify-event` +- [创建日程](https://open.dingtalk.com/document/development/create-event): `create-event` +- [创建订阅日历](https://open.dingtalk.com/document/development/create-subscription-calendar): `create-subscription-calendar` +- [创建访问控制](https://open.dingtalk.com/document/development/create-schedule-access-control): `create-schedule-access-control` +- [删除日程](https://open.dingtalk.com/document/development/delete-event): `delete-event` +- [删除日程参与者](https://open.dingtalk.com/document/development/delete-schedule-participant): `delete-schedule-participant` +- [删除订阅日历](https://open.dingtalk.com/document/development/delete-subscription-calendar): `delete-subscription-calendar` +- [删除访问控制](https://open.dingtalk.com/document/development/delete-an-access-control-list): `delete-an-access-control-list` +- [取消订阅公共日历](https://open.dingtalk.com/document/development/unsubscribe-from-a-public-calendar): `unsubscribe-from-a-public-calendar` +- [取消预定会议室](https://open.dingtalk.com/document/development/remove-a-meeting-room): `remove-a-meeting-room` +- [更新订阅日历](https://open.dingtalk.com/document/development/update-subscription-calendar): `update-subscription-calendar` +- [查看单个日程的签到详情](https://open.dingtalk.com/document/development/view-the-check-in-details-of-a-single-schedule): `view-the-check-in-details-of-a-single-schedule` +- [查看单个日程的签退详情](https://open.dingtalk.com/document/development/view-the-billing-details-of-a-single-schedule): `view-the-billing-details-of-a-single-schedule` +- [查询单个日程详情](https://open.dingtalk.com/document/development/query-details-about-an-event): `query-details-about-an-event` +- [查询单个订阅日历详情](https://open.dingtalk.com/document/development/query-a-single-subscription-calendar): `query-a-single-subscription-calendar` +- [查询日历](https://open.dingtalk.com/document/development/query-a-calendar): `query-a-calendar` +- [查询日程列表](https://open.dingtalk.com/document/development/query-an-event-list): `query-an-event-list` +- [查询日程视图](https://open.dingtalk.com/document/development/query-schedule-view): `query-schedule-view` +- [添加日程参与者](https://open.dingtalk.com/document/development/add-schedule-participant): `add-schedule-participant` +- [获取会议室忙闲信息](https://open.dingtalk.com/document/development/queries-free-and-busy-meeting-room-information): `queries-free-and-busy-meeting-room-information` +- [获取日程参与者](https://open.dingtalk.com/document/development/get-the-participants-of-a-schedule): `get-the-participants-of-a-schedule` +- [获取用户忙闲信息](https://open.dingtalk.com/document/development/free-schedule): `free-schedule` +- [获取签到链接](https://open.dingtalk.com/document/development/api-getsigninlink): `api-getsigninlink` +- [获取签退链接](https://open.dingtalk.com/document/development/api-getsignoutlink): `api-getsignoutlink` +- [获取访问控制列表](https://open.dingtalk.com/document/development/obtain-the-access-control-list-of-the-calendar): `obtain-the-access-control-list-of-the-calendar` +- [订阅公共日历](https://open.dingtalk.com/document/development/subscribe-to-a-public-calendar): `subscribe-to-a-public-calendar` +- [设置日程响应邀请状态](https://open.dingtalk.com/document/development/configure-response-status): `configure-response-status` +- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-single-schedule-news): `sign-in-single-schedule-news` +- [针对单个日程进行签退](https://open.dingtalk.com/document/development/sign-off-for-a-single-schedule): `sign-off-for-a-single-schedule` +- [预定会议室](https://open.dingtalk.com/document/development/add-a-meeting-room): `add-a-meeting-room` + +### 视频会议(30 条) + +- [停止视频会议云录制](https://open.dingtalk.com/document/development/video-conferencing-stops-cloud-recording): `video-conferencing-stops-cloud-recording` +- [停止视频会议直播推流](https://open.dingtalk.com/document/development/videoconferencing-stops-live-stream-ingest): `videoconferencing-stops-live-stream-ingest` +- [全员静音或全员取消静音](https://open.dingtalk.com/document/development/mute-all-staff-or-unmute-all-staff): `mute-all-staff-or-unmute-all-staff` +- [关闭视频会议](https://open.dingtalk.com/document/development/close-audio-video-conference): `close-audio-video-conference` +- [创建用户专属短链](https://open.dingtalk.com/document/development/api-createcustomshortlink): `api-createcustomshortlink` +- [创建视频会议](https://open.dingtalk.com/document/development/create-a-video-conference): `create-a-video-conference` +- [创建预约会议](https://open.dingtalk.com/document/development/create-appointment-meeting): `create-appointment-meeting` +- [取消预约会议](https://open.dingtalk.com/document/development/cancel-appointment-meeting): `cancel-appointment-meeting` +- [开启视频会议云录制](https://open.dingtalk.com/document/development/video-conference-open-cloud-recording): `video-conference-open-cloud-recording` +- [开启视频会议直播推流](https://open.dingtalk.com/document/development/video-conference-enables-live-stream-ingest): `video-conference-enables-live-stream-ingest` +- [批量查询视频会议信息](https://open.dingtalk.com/document/development/batch-query-of-video-conference-information): `batch-query-of-video-conference-information` +- [指定人员静音或取消静音](https://open.dingtalk.com/document/development/specify-person-to-mute-or-unmute): `specify-person-to-mute-or-unmute` +- [更新预约会议](https://open.dingtalk.com/document/development/update-appointment-meeting): `update-appointment-meeting` +- [更新预约会议设置](https://open.dingtalk.com/document/development/api-updatescheduleconfsettings): `api-updatescheduleconfsettings` +- [查询企业进行中会议列表](https://open.dingtalk.com/document/development/api-queryorgconferencelist): `api-queryorgconferencelist` +- [查询会议录制中的文本信息](https://open.dingtalk.com/document/development/queries-the-text-information-about-cloud-recording): `queries-the-text-information-about-cloud-recording` +- [查询会议录制中的视频信息](https://open.dingtalk.com/document/development/queries-the-playback-information-about-a-recorded-cloud-video): `queries-the-playback-information-about-a-recorded-cloud-video` +- [查询会议录制的详情信息](https://open.dingtalk.com/document/development/query-recording-information): `query-recording-information` +- [查询用户进行中会议列表](https://open.dingtalk.com/document/development/api-queryuserongoingconference): `api-queryuserongoingconference` +- [查询视频会议信息](https://open.dingtalk.com/document/development/querying-video-conference-information): `querying-video-conference-information` +- [查询视频会议成员](https://open.dingtalk.com/document/development/querying-video-conference-members): `querying-video-conference-members` +- [查询预约会议](https://open.dingtalk.com/document/development/query-meeting-reservation): `query-meeting-reservation` +- [查询预约会议历史会议信息](https://open.dingtalk.com/document/development/query-appointment-meeting-history-meeting-information): `query-appointment-meeting-history-meeting-information` +- [查询预约会议设置](https://open.dingtalk.com/document/development/api-queryscheduleconfsettings): `api-queryscheduleconfsettings` +- [根据会议号查询会议信息](https://open.dingtalk.com/document/development/api-queryconferenceinfobyroomcode): `api-queryconferenceinfobyroomcode` +- [设置全员看他](https://open.dingtalk.com/document/development/set-the-whole-staff-to-see-him): `set-the-whole-staff-to-see-him` +- [设置联席主持人](https://open.dingtalk.com/document/development/set-up-co-hosts): `set-up-co-hosts` +- [踢出会议成员](https://open.dingtalk.com/document/development/kick-out-meeting-members): `kick-out-meeting-members` +- [邀请用户入会](https://open.dingtalk.com/document/development/invite-users-to-join): `invite-users-to-join` +- [锁定会议](https://open.dingtalk.com/document/development/api-lockconference): `api-lockconference` + +### 阿里商旅(25 条) + +- [修改成本中心](https://open.dingtalk.com/document/development/modify-basic-cost-center-information): `modify-basic-cost-center-information` +- [关联单号查询相关订单信息列表](https://open.dingtalk.com/document/development/related-order-information): `related-order-information` +- [创建日志](https://open.dingtalk.com/document/development/create-a-log): `create-a-log` +- [同步市内用车申请单](https://open.dingtalk.com/document/development/synchronize-third-party-city-vehicle-approval-form): `synchronize-third-party-city-vehicle-approval-form` +- [回传第三方超标审批结果](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-sync): `dingtalk-oapi-alitrip-btrip-exceedapply-sync` +- [审批市内用车申请单](https://open.dingtalk.com/document/development/approval-of-third-party-city-car-application-form): `approval-of-third-party-city-car-application-form` +- [搜索第三方机票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-flight): `dingtalk-oapi-alitrip-btrip-exceedapply-flight` +- [搜索第三方火车票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-train-get): `dingtalk-oapi-alitrip-btrip-exceedapply-train-get` +- [搜索第三方酒店超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get): `dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get` +- [新建审批单](https://open.dingtalk.com/document/development/user-new-approval-form): `user-new-approval-form` +- [新建成本中心](https://open.dingtalk.com/document/development/new-cost-center): `new-cost-center` +- [查询商旅火车票结算记账数据](https://open.dingtalk.com/document/development/business-travel-train-ticket-settlement-bookkeeping-query-interface): `business-travel-train-ticket-settlement-bookkeeping-query-interface` +- [查询市内用车申请单](https://open.dingtalk.com/document/development/query-the-application-form-for-third-party-vehicles-in-the-city): `query-the-application-form-for-third-party-vehicles-in-the-city` +- [查询成员排班信息](https://open.dingtalk.com/document/development/query-scheduling-for-a-day): `query-scheduling-for-a-day` +- [查询机票结算记账数据](https://open.dingtalk.com/document/development/ticket-settlement-bookkeeping-query-interface): `ticket-settlement-bookkeeping-query-interface` +- [查询用车结算记账记录](https://open.dingtalk.com/document/development/query-interface-for-vehicle-settlement-and-bookkeeping): `query-interface-for-vehicle-settlement-and-bookkeeping` +- [查询酒店结算记账数据](https://open.dingtalk.com/document/development/hotel-settlement-bookkeeping-query-interface): `hotel-settlement-bookkeeping-query-interface` +- [获取企业机票订单数据](https://open.dingtalk.com/document/development/obtains-enterprise-ticket-order-data): `obtains-enterprise-ticket-order-data` +- [获取商旅访问地址](https://open.dingtalk.com/document/development/obtain-business-travel-access-addresses): `obtain-business-travel-access-addresses` +- [获取打卡结果](https://open.dingtalk.com/document/development/open-attendance-clock-in-data): `open-attendance-clock-in-data` +- [获取打卡详情](https://open.dingtalk.com/document/development/attendance-clock-in-record-is-open): `attendance-clock-in-record-is-open` +- [获取日志评论详情](https://open.dingtalk.com/document/development/queries-log-comment-details): `queries-log-comment-details` +- [获取月对账结算数据](https://open.dingtalk.com/document/development/obtain-monthly-reconciliation-settlement-data): `obtain-monthly-reconciliation-settlement-data` +- [获取用车订单数据](https://open.dingtalk.com/document/development/vehicle-order-query-interface): `vehicle-order-query-interface` +- [设置成本中心人员信息](https://open.dingtalk.com/document/development/set-up-cost-center-personnel-information): `set-up-cost-center-personnel-information` + +### Agoal 目标(23 条) + +- [Agoal业务数据查询](https://open.dingtalk.com/document/development/agoal-business-biz-data-query): `agoal-business-biz-data-query` +- [创建业务实体](https://open.dingtalk.com/document/development/api-agoalentitycreate): `api-agoalentitycreate` +- [创建目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskcreate): `api-agoalperftaskcreate` +- [更新业务实体](https://open.dingtalk.com/document/development/api-agoalentityupdate): `api-agoalentityupdate` +- [更新目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskupdate): `api-agoalperftaskupdate` +- [查询企业下个人目标详情](https://open.dingtalk.com/document/development/api-getobjectivedetail): `api-getobjectivedetail` +- [查询企业下单个目标规则详情](https://open.dingtalk.com/document/development/api-getobjectiveruledetail): `api-getobjectiveruledetail` +- [查询企业下指定个人目标的所有进展](https://open.dingtalk.com/document/development/api-agoalobjectiveprogresslist): `api-agoalobjectiveprogresslist` +- [查询企业下的所有考核计划](https://open.dingtalk.com/document/development/api-agoalorgperfplanquery): `api-agoalorgperfplanquery` +- [查询企业下目标规则列表](https://open.dingtalk.com/document/development/api-agoalobjectiverulelist): `api-agoalobjectiverulelist` +- [查询某个考核计划的部门得分](https://open.dingtalk.com/document/development/api-agoalorgperfdocquery): `api-agoalorgperfdocquery` +- [查询组织目标详情](https://open.dingtalk.com/document/development/api-agoalorgobjectivequery): `api-agoalorgobjectivequery` +- [获取 Agoal 组织目标列表](https://open.dingtalk.com/document/development/api-agoalorgobjectivelist): `api-agoalorgobjectivelist` +- [获取Agoal指定目标规则下的周期列表](https://open.dingtalk.com/document/development/api-agoalobjectiveruleperiodlist): `api-agoalobjectiveruleperiodlist` +- [获取Agoal指定组织下的所有目标规则列表](https://open.dingtalk.com/document/development/api-agoalorgobjectiverulelist): `api-agoalorgobjectiverulelist` +- [获取Agoal指定规则周期下负责人的目标列表](https://open.dingtalk.com/document/development/api-agoaluserobjectivelist): `api-agoaluserobjectivelist` +- [获取Agoal指定部门下的计分卡维度和指标id](https://open.dingtalk.com/document/development/api-getdeptscorecardindicator): `api-getdeptscorecardindicator` +- [获取Agoal用户管理员列表](https://open.dingtalk.com/document/development/api-agoaluseradminlist): `api-agoaluseradminlist` +- [获取Agoal目标或关键结果关联的关键行动](https://open.dingtalk.com/document/development/api-agoalobjectivekeyactionlist): `api-agoalobjectivekeyactionlist` +- [获取计分卡指标详情](https://open.dingtalk.com/document/development/api-getindicatordetail): `api-getindicatordetail` +- [通过Agoal系统账号发送消息](https://open.dingtalk.com/document/development/api-agoalsendmessage): `api-agoalsendmessage` +- [通过指标编码批量查询指标列表](https://open.dingtalk.com/document/development/api-agoalindicatorbatchquery): `api-agoalindicatorbatchquery` +- [通过指标编码推送指标时间维度数据](https://open.dingtalk.com/document/development/api-agoalindicatordatapush): `api-agoalindicatordatapush` + +### 教育(23 条) + +- [修改用户成员类型](https://open.dingtalk.com/document/development/api-updatecollegeuseremptype): `api-updatecollegeuseremptype` +- [创建个人账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactuser): `api-addcollegecontactuser` +- [创建组织单元](https://open.dingtalk.com/document/development/api-createcollegecontactdept): `api-createcollegecontactdept` +- [创建自定义校区或部门](https://open.dingtalk.com/document/development/create-a-custom-campus-or-department): `create-a-custom-campus-or-department` +- [创建自定义部门下的班级](https://open.dingtalk.com/document/development/create-classes-in-a-custom-department): `create-classes-in-a-custom-department` +- [创建高校账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactexclusive): `api-addcollegecontactexclusive` +- [删除学生](https://open.dingtalk.com/document/development/delete-student): `delete-student` +- [删除家校部门](https://open.dingtalk.com/document/development/delete-home-school-department): `delete-home-school-department` +- [删除家长关系](https://open.dingtalk.com/document/development/delete-parent-relationship): `delete-parent-relationship` +- [删除老师](https://open.dingtalk.com/document/development/delete-teacher): `delete-teacher` +- [判断用户是否是认证组织的语文老师接口](https://open.dingtalk.com/document/development/api-isyuwencertifiedteacher): `api-isyuwencertifiedteacher` +- [学生调班](https://open.dingtalk.com/document/development/shift-students): `shift-students` +- [更新个人账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactuser): `api-updatecollegecontactuser` +- [更新学生](https://open.dingtalk.com/document/development/api-updatestudent): `api-updatestudent` +- [更新家长](https://open.dingtalk.com/document/development/api-updateguardian): `api-updateguardian` +- [更新班级](https://open.dingtalk.com/document/development/api-updateclass): `api-updateclass` +- [更新组织单元](https://open.dingtalk.com/document/development/api-updatecollegecontactdept): `api-updatecollegecontactdept` +- [更新高校账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactexclusive): `api-updatecollegecontactexclusive` +- [查询用户信息详情](https://open.dingtalk.com/document/development/api-querycollegecontactuserdetail): `api-querycollegecontactuserdetail` +- [获取子组织单元列表](https://open.dingtalk.com/document/development/api-listcollegecontactsubdepts): `api-listcollegecontactsubdepts` +- [获取组织单元支持的部门类型](https://open.dingtalk.com/document/development/api-listcollegecontactdepttypeconfig): `api-listcollegecontactdepttypeconfig` +- [获取组织单元详情](https://open.dingtalk.com/document/development/api-getcollegecontactdeptdetail): `api-getcollegecontactdeptdetail` +- [获取行政组织架构部门详情](https://open.dingtalk.com/document/development/api-getcollegecontactstandardstrudeptdetail): `api-getcollegecontactstandardstrudeptdetail` + +### 智能人事(17 条) + +- [修改已离职员工信息](https://open.dingtalk.com/document/development/modify-resigned-employee-information): `modify-resigned-employee-information` +- [员工加入待离职](https://open.dingtalk.com/document/development/api-empstartdismission): `api-empstartdismission` +- [批量获取员工离职信息](https://open.dingtalk.com/document/development/obtain-resignation-information-of-employees-new-version): `obtain-resignation-information-of-employees-new-version` +- [撤销员工待离职](https://open.dingtalk.com/document/development/api-revoketermination): `api-revoketermination` +- [新增或删除花名册选项类型字段的选项](https://open.dingtalk.com/document/development/intelligent-personnel-roster-field-option-modification): `intelligent-personnel-roster-field-option-modification` +- [智能人事员工调岗](https://open.dingtalk.com/document/development/intelligent-personnel-staff-transfer): `intelligent-personnel-staff-transfer` +- [智能人事员工转正](https://open.dingtalk.com/document/development/intelligent-personnel-staff-to-become-regular): `intelligent-personnel-staff-to-become-regular` +- [更新待离职员工离职信息](https://open.dingtalk.com/document/development/api-updateempdismissioninfo): `api-updateempdismissioninfo` +- [查询花名册中有权限的字段列表](https://open.dingtalk.com/document/development/query-the-list-of-fields-with-permissions-in-the-roster): `query-the-list-of-fields-with-permissions-in-the-roster` +- [添加待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-supports-system-and-custom-fields): `add-employees-to-be-hired-supports-system-and-custom-fields` +- [确认员工离职并删除](https://open.dingtalk.com/document/development/api-hrmprocessterminationandhandover): `api-hrmprocessterminationandhandover` +- [获取企业已有的所有离职原因](https://open.dingtalk.com/document/development/api-getalldismissionreasons): `api-getalldismissionreasons` +- [获取企业职位列表](https://open.dingtalk.com/document/development/obtain-enterprise-position-information): `obtain-enterprise-position-information` +- [获取企业职务列表](https://open.dingtalk.com/document/development/obtain-enterprise-title-information): `obtain-enterprise-title-information` +- [获取企业职级列表](https://open.dingtalk.com/document/development/obtain-enterprise-rank-information): `obtain-enterprise-rank-information` +- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/api-getemployeerosterbyfield): `api-getemployeerosterbyfield` +- [获取离职员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-who-have-left): `obtain-the-list-of-employees-who-have-left` + +### 机器人(16 条) + +- [下载机器人接收消息的文件内容](https://open.dingtalk.com/document/development/download-the-file-content-of-the-robot-receiving-message): `download-the-file-content-of-the-robot-receiving-message` +- [人与人会话中机器人发送普通消息](https://open.dingtalk.com/document/development/the-robot-sends-ordinary-messages-in-a-person-to-person-conversation): `the-robot-sends-ordinary-messages-in-a-person-to-person-conversation` +- [企业机器人撤回内部群消息](https://open.dingtalk.com/document/development/enterprise-chatbot-withdraws-internal-group-messages): `enterprise-chatbot-withdraws-internal-group-messages` +- [发送DING消息](https://open.dingtalk.com/document/development/robot-sends-nail-message): `robot-sends-nail-message` +- [批量发送人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/chatbots-send-one-on-one-chat-messages-in-batches): `chatbots-send-one-on-one-chat-messages-in-batches` +- [批量撤回人与人会话中机器人消息](https://open.dingtalk.com/document/development/batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations): `batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations` +- [批量撤回人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/batch-message-recall-chat): `batch-message-recall-chat` +- [批量查询人与机器人会话机器人消息是否已读](https://open.dingtalk.com/document/development/chatbot-batch-query-the-read-status-of-messages): `chatbot-batch-query-the-read-status-of-messages` +- [撤回已经发送的DING消息](https://open.dingtalk.com/document/development/robot-withdraws-pin-message): `robot-withdraws-pin-message` +- [机器人发送群聊消息](https://open.dingtalk.com/document/development/the-robot-sends-a-group-message): `the-robot-sends-a-group-message` +- [查询人与人会话中机器人消息已读列表](https://open.dingtalk.com/document/development/query-the-read-list-of-robot-messages-in-person-to-person-conversations): `query-the-read-list-of-robot-messages-in-person-to-person-conversations` +- [查询企业机器人群聊消息用户已读状态](https://open.dingtalk.com/document/development/chatbot-queries-the-read-status-of-a-message): `chatbot-queries-the-read-status-of-a-message` +- [查询单聊机器人的快捷入口](https://open.dingtalk.com/document/development/quick-entrance-of-inquiry-single-chat-robot): `quick-entrance-of-inquiry-single-chat-robot` +- [清空单聊机器人快捷入口](https://open.dingtalk.com/document/development/clear-single-chat-robot-quick-entry): `clear-single-chat-robot-quick-entry` +- [获取群内机器人列表](https://open.dingtalk.com/document/development/obtain-the-list-of-robots-in-the-group): `obtain-the-list-of-robots-in-the-group` +- [设置单聊机器人快捷入口](https://open.dingtalk.com/document/development/set-robot-quick-entrance): `set-robot-quick-entrance` + +### 简知 CRM(15 条) + +- [产品信息](https://open.dingtalk.com/document/development/add-or-edit-product-information): `add-or-edit-product-information` +- [入库单](https://open.dingtalk.com/document/development/add-or-edit-a-shipment-record): `add-or-edit-a-shipment-record` +- [出库单](https://open.dingtalk.com/document/development/add-or-edit-an-issue-ticket): `add-or-edit-an-issue-ticket` +- [发货单](https://open.dingtalk.com/document/development/add-or-edit-invoices): `add-or-edit-invoices` +- [合同订单](https://open.dingtalk.com/document/development/add-or-edit-contract-orders): `add-or-edit-contract-orders` +- [客户公共池](https://open.dingtalk.com/document/development/add-or-edit-customer-public-pools): `add-or-edit-customer-public-pools` +- [客户资料](https://open.dingtalk.com/document/development/add-or-edit-customer-profile): `add-or-edit-customer-profile` +- [报价记录](https://open.dingtalk.com/document/development/add-or-edit-quotation-records): `add-or-edit-quotation-records` +- [生产单](https://open.dingtalk.com/document/development/add-or-edit-a-production-order): `add-or-edit-a-production-order` +- [联系人](https://open.dingtalk.com/document/development/add-or-edit-contacts): `add-or-edit-contacts` +- [获取数据列表](https://open.dingtalk.com/document/development/obtain-the-data-list): `obtain-the-data-list` +- [获取数据详情](https://open.dingtalk.com/document/development/queries-data-details): `queries-data-details` +- [采购单](https://open.dingtalk.com/document/development/edit-purchase-order): `edit-purchase-order` +- [销售换货单](https://open.dingtalk.com/document/development/add-or-edit-a-sales-order): `add-or-edit-a-sales-order` +- [销售机会](https://open.dingtalk.com/document/development/add-or-edit-opportunities): `add-or-edit-opportunities` + +### 考勤(15 条) + +- [分页获取加班规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-overtime-rules-by-page): `retrieve-a-list-of-overtime-rules-by-page` +- [分页获取补卡规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-replenishment-rules-by-page): `retrieve-a-list-of-replenishment-rules-by-page` +- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/batch-query-employee-leave-balance-change-record): `batch-query-employee-leave-balance-change-record` +- [批量获取加班规则设置](https://open.dingtalk.com/document/development/batch-retrieve-overtime-rules): `batch-retrieve-overtime-rules` +- [更新假期规则](https://open.dingtalk.com/document/development/update-holiday-rules): `update-holiday-rules` +- [查询指定用户的封账规则](https://open.dingtalk.com/document/development/encapsulate-account-sealing-and-unsealing-rules): `encapsulate-account-sealing-and-unsealing-rules` +- [查询用户某段时间内是否处于封账状态](https://open.dingtalk.com/document/development/checks-whether-a-user-has-blocked-accounts-within-a-specified): `checks-whether-a-user-has-blocked-accounts-within-a-specified` +- [查询用户考勤节假日信息](https://open.dingtalk.com/document/development/obtain-user-attendance-and-holiday-information): `obtain-user-attendance-and-holiday-information` +- [查询考勤写操作权限](https://open.dingtalk.com/document/development/attendance-writing-operation-is-brand-new-query): `attendance-writing-operation-is-brand-new-query` +- [查询考勤机信息](https://open.dingtalk.com/document/development/query-attendance-machine-information): `query-attendance-machine-information` +- [根据设备ID获取员工信息](https://open.dingtalk.com/document/development/obtain-information-about-employees-based-on-device-ids): `obtain-information-about-employees-based-on-device-ids` +- [添加假期规则](https://open.dingtalk.com/document/development/add-holiday-rules): `add-holiday-rules` +- [通知审批通过](https://open.dingtalk.com/document/development/api-processapprovefinish): `api-processapprovefinish` +- [配置考勤排班附加信息](https://open.dingtalk.com/document/development/synchronization-scheduling-information): `synchronization-scheduling-information` +- [预计算时长](https://open.dingtalk.com/document/development/api-calculateduration): `api-calculateduration` + +### 登录授权(11 条) + +- [查询个人授权记录](https://open.dingtalk.com/document/development/query-personal-authorization-records): `query-personal-authorization-records` +- [获取jsapiTicket](https://open.dingtalk.com/document/development/create-a-jsapi-ticket): `create-a-jsapi-ticket` +- [获取企业内部应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app): `obtain-the-access-token-of-an-internal-app` +- [获取企业开通应用后的授权信息](https://open.dingtalk.com/document/development/obtains-the-authorization-information-after-the-enterprise-activates-the-application-1): `obtains-the-authorization-information-after-the-enterprise-activates-the-application-1` +- [获取定制应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-third-party-application-authorization-enterprise): `obtain-the-access-token-of-the-third-party-application-authorization-enterprise` +- [获取应用的 Access Token](https://open.dingtalk.com/document/development/api-gettoken): `api-gettoken` +- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/obtains-the-identity-of-an-application-administrator): `obtains-the-identity-of-an-application-administrator` +- [获取微应用后台免登的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-micro-application-background-without-log-on): `obtain-the-access-token-of-the-micro-application-background-without-log-on` +- [获取用户token](https://open.dingtalk.com/document/development/obtain-user-token): `obtain-user-token` +- [获取第三方企业应用的suiteAccessToken](https://open.dingtalk.com/document/development/obtains-the-suite-acess-token-of-third-party-enterprise-applications): `obtains-the-suite-acess-token-of-third-party-enterprise-applications` +- [获取第三方应用授权企业的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-authorized-enterprise-1): `obtain-the-access-token-of-the-authorized-enterprise-1` + +### 知识库(11 条) + +- [匹配文本中的词条](https://open.dingtalk.com/document/development/enterprise-encyclopedia-match-entries-in-a-text): `enterprise-encyclopedia-match-entries-in-a-text` +- [批量获取知识库](https://open.dingtalk.com/document/development/batch-acquisition-of-knowledge-base): `batch-acquisition-of-knowledge-base` +- [批量获取节点](https://open.dingtalk.com/document/development/obtain-nodes-in-batch): `obtain-nodes-in-batch` +- [新建知识库](https://open.dingtalk.com/document/development/new-knowledge-base): `new-knowledge-base` +- [查询词条详情](https://open.dingtalk.com/document/development/enterprise-encyclopedia-query-entry-details-by-entry-name): `enterprise-encyclopedia-query-entry-details-by-entry-name` +- [获取我的文档知识库信息](https://open.dingtalk.com/document/development/get-my-documents): `get-my-documents` +- [获取知识库](https://open.dingtalk.com/document/development/obtain-the-knowledge-base): `obtain-the-knowledge-base` +- [获取知识库列表](https://open.dingtalk.com/document/development/get-knowledge-base-list): `get-knowledge-base-list` +- [获取节点](https://open.dingtalk.com/document/development/get-knowledge-base-acquisition-node): `get-knowledge-base-acquisition-node` +- [获取节点列表](https://open.dingtalk.com/document/development/get-node-list): `get-node-list` +- [通过链接获取节点](https://open.dingtalk.com/document/development/get-node-by-link): `get-node-by-link` + +### 服务群(8 条) + +- [创建场景服务群](https://open.dingtalk.com/document/development/create-a-scenario-service-group): `create-a-scenario-service-group` +- [升级云客服服务群为钉钉智能服务群](https://open.dingtalk.com/document/development/upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent): `upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent` +- [升级普通群为服务群](https://open.dingtalk.com/document/development/a-dingtalk-group-is-upgraded-to-one-of-the-intelligent): `a-dingtalk-group-is-upgraded-to-one-of-the-intelligent` +- [发送服务群消息](https://open.dingtalk.com/document/development/service-group-message-sending-interface): `service-group-message-sending-interface` +- [更换服务群所在的群分组](https://open.dingtalk.com/document/development/modify-a-service-group): `modify-a-service-group` +- [查询服务群活跃用户](https://open.dingtalk.com/document/development/queries-active-service-users): `queries-active-service-users` +- [添加服务群成员](https://open.dingtalk.com/document/development/add-service-group-members): `add-service-group-members` +- [群发任务](https://open.dingtalk.com/document/development/service-group-sending-task-interface): `service-group-sending-task-interface` + +### Team 空间(7 条) + +- [创建协作空间](https://open.dingtalk.com/document/development/api-createprojectv3): `api-createprojectv3` +- [创建协作空间任务](https://open.dingtalk.com/document/development/api-createtask): `api-createtask` +- [创建自由任务](https://open.dingtalk.com/document/development/api-createorganizationtask): `api-createorganizationtask` +- [更新协作空间](https://open.dingtalk.com/document/development/api-updateprojectv3): `api-updateprojectv3` +- [查询任务详情](https://open.dingtalk.com/document/development/api-queryalltask): `api-queryalltask` +- [获取协作空间列表](https://open.dingtalk.com/document/development/api-searchprojectsv3-1): `api-searchprojectsv3-1` +- [获取用户参与项目](https://open.dingtalk.com/document/development/api-getuserjoinedprojectsv3): `api-getuserjoinedprojectsv3` + +### 智能招聘(7 条) + +- [根据手机号获取候选人信息](https://open.dingtalk.com/document/development/obtain-candidate-information-based-on-mobile-phone-number): `obtain-candidate-information-based-on-mobile-phone-number` +- [添加智能招聘文件到钉盘](https://open.dingtalk.com/document/development/add-nail-disk-file): `add-nail-disk-file` +- [确认完成权益的更新](https://open.dingtalk.com/document/development/confirm-benefits): `confirm-benefits` +- [获取候选人的面试信息](https://open.dingtalk.com/document/development/query-the-interview-list): `query-the-interview-list` +- [获取招聘流程标识](https://open.dingtalk.com/document/development/get-recruitment-process-identity): `get-recruitment-process-identity` +- [获取智能招聘文件上传信息](https://open.dingtalk.com/document/development/obtain-information-about-the-dingtalk-disk-upload-file): `obtain-information-about-the-dingtalk-disk-upload-file` +- [通知完成指定的新手任务](https://open.dingtalk.com/document/development/notify-the-completion-of-the-specified-novice-task): `notify-the-completion-of-the-specified-novice-task` + +### 财务(7 条) + +- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-user-code-instance): `create-a-user-code-instance` +- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/sync-pin-badge-code-verification-result): `sync-pin-badge-code-verification-result` +- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-user-code-instance): `update-user-code-instance` +- [解码钉工牌电子码](https://open.dingtalk.com/document/development/decoding-dingtalk-payment-code): `decoding-dingtalk-payment-code` +- [通知支付结果](https://open.dingtalk.com/document/development/notify-dingtalk-payment-code-payment-result): `notify-dingtalk-payment-code-payment-result` +- [通知退款结果](https://open.dingtalk.com/document/development/dingtalk-payment-code-refund-information-synchronization-operation): `dingtalk-payment-code-refund-information-synchronization-operation` +- [配置企业钉工牌](https://open.dingtalk.com/document/development/set-up-enterprise-payment-code-configuration-interface): `set-up-enterprise-payment-code-configuration-interface` + +### 待办(6 条) + +- [创建钉钉个人待办任务](https://open.dingtalk.com/document/development/api-createpersonaltodotask): `api-createpersonaltodotask` +- [创建钉钉待办任务](https://open.dingtalk.com/document/development/add-dingtalk-to-do-task): `add-dingtalk-to-do-task` +- [删除钉钉待办任务](https://open.dingtalk.com/document/development/delete-dingtalk-to-do-tasks): `delete-dingtalk-to-do-tasks` +- [更新钉钉待办任务](https://open.dingtalk.com/document/development/updates-dingtalk-to-do-tasks): `updates-dingtalk-to-do-tasks` +- [更新钉钉待办执行者状态](https://open.dingtalk.com/document/development/update-dingtalk-to-do-status): `update-dingtalk-to-do-status` +- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/query-the-to-do-list-of-enterprise-users): `query-the-to-do-list-of-enterprise-users` + +### 百科问答(6 条) + +- [分页获取企业词条信息](https://open.dingtalk.com/document/development/entry-search): `entry-search` +- [删除词条](https://open.dingtalk.com/document/development/delete-entry): `delete-entry` +- [审核词条](https://open.dingtalk.com/document/development/review-entries): `review-entries` +- [新增词条](https://open.dingtalk.com/document/development/new-entry): `new-entry` +- [更新词条](https://open.dingtalk.com/document/development/update-entry): `update-entry` +- [根据词条ID查询详情](https://open.dingtalk.com/document/development/query-entry): `query-entry` + +### 直播(6 条) + +- [修改培训课程](https://open.dingtalk.com/document/development/modify-the-basic-information-of-a-live-streaming-course): `modify-the-basic-information-of-a-live-streaming-course` +- [修改直播属性信息](https://open.dingtalk.com/document/development/modify-live-streaming): `modify-live-streaming` +- [创建直播](https://open.dingtalk.com/document/development/create-live-streaming): `create-live-streaming` +- [删除直播](https://open.dingtalk.com/document/development/delete-live-streaming): `delete-live-streaming` +- [查询直播信息](https://open.dingtalk.com/document/development/queries-the-live-streaming-information): `queries-the-live-streaming-information` +- [查询直播观看人员信息](https://open.dingtalk.com/document/development/queries-the-viewing-information-of-viewers): `queries-the-viewing-information-of-viewers` + +### 行业(6 条) + +- [保存人员扩展属性](https://open.dingtalk.com/document/development/personnel-extension-property-error): `personnel-extension-property-error` +- [创建园区项目](https://open.dingtalk.com/document/development/create-a-campus-project): `create-a-campus-project` +- [创建项目组](https://open.dingtalk.com/document/development/create-a-project-group): `create-a-project-group` +- [删除项目组](https://open.dingtalk.com/document/development/delete-the-project-group-team): `delete-the-project-group-team` +- [查询园区项目信息](https://open.dingtalk.com/document/development/query-a-project-in-a-specified-campus): `query-a-project-in-a-specified-campus` +- [查询项目组信息](https://open.dingtalk.com/document/development/query-a-project-group-in-the-specified-park): `query-a-project-group-in-the-specified-park` + +### 居民服务(5 条) + +- [分页查询居民积分流水](https://open.dingtalk.com/document/development/query-the-integral-flow-records-by-page): `query-the-integral-flow-records-by-page` +- [增加或减少居民积分](https://open.dingtalk.com/document/development/increase-or-decrease-resident-points): `increase-or-decrease-resident-points` +- [查询组织维度配置的所有积分规则](https://open.dingtalk.com/document/development/query-all-credit-rules): `query-all-credit-rules` +- [获取用户所在的行业角色信息](https://open.dingtalk.com/document/development/obtain-the-industry-role-information-of-the-user): `obtain-the-industry-role-information-of-the-user` +- [获取行业角色下的用户列表](https://open.dingtalk.com/document/development/obtains-a-list-of-users-under-an-industry-role): `obtains-a-list-of-users-under-an-industry-role` + +### 组织文化(5 条) + +- [创建荣誉勋章模板](https://open.dingtalk.com/document/development/create-medal-of-honor-template): `create-medal-of-honor-template` +- [撤销员工获得的荣誉勋章](https://open.dingtalk.com/document/development/revoke-an-employee-s-medal-of-honor): `revoke-an-employee-s-medal-of-honor` +- [查询员工已获得的组织荣誉](https://open.dingtalk.com/document/development/check-the-honors-that-an-employee-has-received): `check-the-honors-that-an-employee-has-received` +- [查询当前企业下可颁发的荣誉列表](https://open.dingtalk.com/document/development/query-the-list-of-honors-that-can-be-issued-under): `query-the-list-of-honors-that-can-be-issued-under` +- [给员工颁发荣誉](https://open.dingtalk.com/document/development/award-of-honor): `award-of-honor` + +### 钉密(5 条) + +- [小蜜客服机器人消息回复](https://open.dingtalk.com/document/development/xiaomi-customer-service-robot-message-reply): `xiaomi-customer-service-robot-message-reply` +- [推送小蜜机器人单聊O2O消息](https://open.dingtalk.com/document/development/push-xiaomi-customer-service-robot-single-chat-message): `push-xiaomi-customer-service-robot-single-chat-message` +- [智能问答](https://open.dingtalk.com/document/development/alimebot-intelligent-q-a-interface): `alimebot-intelligent-q-a-interface` +- [查询机器人基础指标数据](https://open.dingtalk.com/document/development/query-robot-data-indicators): `query-robot-data-indicators` +- [获取用户登录凭证](https://open.dingtalk.com/document/development/obtains-the-user-login-credential-of-the-third-party-system-of): `obtains-the-user-login-credential-of-the-third-party-system-of` + +### 云盘(4 条) + +- [删除空间](https://open.dingtalk.com/document/development/delete-a-space): `delete-a-space` +- [新建空间](https://open.dingtalk.com/document/development/new-space): `new-space` +- [根据spaceId获取指定空间信息](https://open.dingtalk.com/document/development/retrieves-the-space-list-on-the-management-side): `retrieves-the-space-list-on-the-management-side` +- [获取空间列表](https://open.dingtalk.com/document/development/queries-a-space-list): `queries-a-space-list` + +### 会话文件(4 条) + +- [以应用身份发送文件给指定用户](https://open.dingtalk.com/document/development/sends-a-storage-file-to-a-specified-user): `sends-a-storage-file-to-a-specified-user` +- [发送文件到指定会话](https://open.dingtalk.com/document/development/send-file-to-specified-session): `send-file-to-specified-session` +- [发送文件链接到指定会话](https://open.dingtalk.com/document/development/send-a-file-link-to-the-specified-session): `send-a-file-link-to-the-specified-session` +- [获取群存储空间信息](https://open.dingtalk.com/document/development/obtain-group-storage-space-information): `obtain-group-storage-space-information` + +### 应用市场(4 条) + +- [内购商品订单处理完成](https://open.dingtalk.com/document/development/internal-purchase-order-processing-completed): `internal-purchase-order-processing-completed` +- [查询应用市场订单详情](https://open.dingtalk.com/document/development/check-the-order-details-app-store): `check-the-order-details-app-store` +- [获取个人应用内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-sku-page-address-of-goods-purchased-in-personal): `obtain-the-sku-page-address-of-goods-purchased-in-personal` +- [获取未处理的已支付订单](https://open.dingtalk.com/document/development/obtaining-isv-unfinished-processing-order): `obtaining-isv-unfinished-processing-order` + +### 公告(3 条) + +- [查询公告已读未读人员列表](https://open.dingtalk.com/document/development/query-bulletin-read-unread-persons-list): `query-bulletin-read-unread-persons-list` +- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-get-blackboard): `obtains-the-details-get-blackboard` +- [获取公告钉盘空间信息](https://open.dingtalk.com/document/development/obtain-bulletin-nail-disk-space-information): `obtain-bulletin-nail-disk-space-information` + +### 制造(2 条) + +- [查询计件报工数据](https://open.dingtalk.com/document/development/riqing-monthly-settlement-query-interface-for-piece-rate-reporting): `riqing-monthly-settlement-query-interface-for-piece-rate-reporting` +- [计件报工](https://open.dingtalk.com/document/development/riqing-monthly-settlement-piece-rate-reporting-interface): `riqing-monthly-settlement-piece-rate-reporting-interface` + +### 碳能力(2 条) + +- [写入每日用户碳数据明细信息](https://open.dingtalk.com/document/development/write-in-the-detailed-information-of-daily-user-carbon-data): `write-in-the-detailed-information-of-daily-user-carbon-data` +- [写入每日组织碳数据明细信息](https://open.dingtalk.com/document/development/third-party-applications-write-daily-organizational-carbon-data-details-1): `third-party-applications-write-daily-organizational-carbon-data-details-1` + +### Agoal(1 条) + +- [查询假期余额](https://open.dingtalk.com/document/development/query-holiday-balance): `query-holiday-balance` + +### 会员(1 条) + +- [查询用户钉钉365会员信息](https://open.dingtalk.com/document/development/api-queryvipmemberinfo): `api-queryvipmemberinfo` + +### 硬件(1 条) + +- [获取用户考勤数据](https://open.dingtalk.com/document/development/obtain-the-attendance-update-data): `obtain-the-attendance-update-data` + +### 职业认证(1 条) + +- [检查用户是否完成所有任务](https://open.dingtalk.com/document/development/docking-of-provincial-practical-exercises-for-digital-managers): `docking-of-provincial-practical-exercises-for-digital-managers` + +## 连接平台(66 条) + +二级 Tab:平台介绍 / 开发指南 / 连接器中心 / 连接平台自动化(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 连接平台自动化(60 条) + +- [下载审批附件](https://open.dingtalk.com/document/development/api-premiumgrantprocessinstancefordownloadfile): `api-premiumgrantprocessinstancefordownloadfile` +- [下载审批附件](https://open.dingtalk.com/document/development/download-an-approval-attachment): `download-an-approval-attachment` +- [保存流程中心外部集成审批任务](https://open.dingtalk.com/document/development/api-premiumsaveintegratedtask): `api-premiumsaveintegratedtask` +- [保存流程中心外部集成审批实例](https://open.dingtalk.com/document/development/api-premiumexternalintegrationprocessinstance): `api-premiumexternalintegrationprocessinstance` +- [保存流程中心外部集成审批模板](https://open.dingtalk.com/document/development/api-premiumsaveintegratedprocess): `api-premiumsaveintegratedprocess` +- [创建实例](https://open.dingtalk.com/document/development/create-a-ticket-approval-instance): `create-a-ticket-approval-instance` +- [创建或更新业务分组](https://open.dingtalk.com/document/development/api-premiuminsertorupdatedir): `api-premiuminsertorupdatedir` +- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-orupdate-the-approval-template-new): `create-orupdate-the-approval-template-new` +- [创建或更新审批表单模板](https://open.dingtalk.com/document/development/create-an-approval-form-template): `create-an-approval-form-template` +- [创建或更新数据表单模板](https://open.dingtalk.com/document/development/api-premiumsaveform): `api-premiumsaveform` +- [创建数据表单实例](https://open.dingtalk.com/document/development/api-createdatapremiumsaveforminstance): `api-createdatapremiumsaveforminstance` +- [创建流程中心待处理任务](https://open.dingtalk.com/document/development/create-pending-tasks-in-process-center): `create-pending-tasks-in-process-center` +- [删除业务分组](https://open.dingtalk.com/document/development/api-premiumdeldir): `api-premiumdeldir` +- [删除数据表单实例](https://open.dingtalk.com/document/development/api-premiumdeleteforminstance): `api-premiumdeleteforminstance` +- [删除模板](https://open.dingtalk.com/document/development/self-owned-approval-deletion-template): `self-owned-approval-deletion-template` +- [加签审批任务](https://open.dingtalk.com/document/development/api-premiumappendtask): `api-premiumappendtask` +- [发起审批实例](https://open.dingtalk.com/document/development/create-an-approval-instance): `create-an-approval-instance` +- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/approve-or-reject-the-approval-task): `approve-or-reject-the-approval-task` +- [归档审批实例](https://open.dingtalk.com/document/development/api-archiveprocessinstance): `api-archiveprocessinstance` +- [批量取消流程中心待处理任务](https://open.dingtalk.com/document/development/cancel-multiple-oa-approval-tasks): `cancel-multiple-oa-approval-tasks` +- [批量同意或拒绝审批任务](https://open.dingtalk.com/document/development/api-premiumbatchexecuteprocessinstances): `api-premiumbatchexecuteprocessinstances` +- [批量更新实例状态](https://open.dingtalk.com/document/development/self-owned-batch-update-of-instance-status): `self-owned-batch-update-of-instance-status` +- [批量获取表单模板schema(包含表单和流程配置信息)](https://open.dingtalk.com/document/development/api-premiumqueryschemaandprocessbycodelist): `api-premiumqueryschemaandprocessbycodelist` +- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/api-premiumaddapprovedentryauth): `api-premiumaddapprovedentryauth` +- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/download-the-approval-nail-file): `download-the-approval-nail-file` +- [授权预览审批附件](https://open.dingtalk.com/document/development/api-premiumgetspacewithdownloadauth): `api-premiumgetspacewithdownloadauth` +- [授权预览审批附件](https://open.dingtalk.com/document/development/official-authorized-preview-approval-attachment): `official-authorized-preview-approval-attachment` +- [撤销审批实例](https://open.dingtalk.com/document/development/revoke-an-approval-instance): `revoke-an-approval-instance` +- [更新实例状态](https://open.dingtalk.com/document/development/update-instance-status): `update-instance-status` +- [更新数据表单实例](https://open.dingtalk.com/document/development/api-premiumupdateforminstance): `api-premiumupdateforminstance` +- [更新流程中心任务状态](https://open.dingtalk.com/document/development/update-process-center-task-status): `update-process-center-task-status` +- [更新流程表单审批实例](https://open.dingtalk.com/document/development/api-premiumupdateprocessinstancevariables): `api-premiumupdateprocessinstancevariables` +- [查询审批中心用户已发起实例列表](https://open.dingtalk.com/document/development/api-premiumgetsubmittedinstances): `api-premiumgetsubmittedinstances` +- [查询审批中心用户已处理任务列表](https://open.dingtalk.com/document/development/api-premiumgetdonetasks): `api-premiumgetdonetasks` +- [查询审批中心用户已收到的实例列表](https://open.dingtalk.com/document/development/api-premiumgetnoticedinstances): `api-premiumgetnoticedinstances` +- [查询审批中心用户待处理任务列表](https://open.dingtalk.com/document/development/api-premiumgettodotasks): `api-premiumgettodotasks` +- [查询已设置为条件的表单组件](https://open.dingtalk.com/document/development/query-form-components-that-have-been-set-as-criteria-1): `query-form-components-that-have-been-set-as-criteria-1` +- [查询通过流程中心集成的OA审批任务](https://open.dingtalk.com/document/development/query-oa-approval-tasks-integrated-through-process-center): `query-oa-approval-tasks-integrated-through-process-center` +- [根据processCode分页获取审批流程数据](https://open.dingtalk.com/document/development/api-premiumgetprocessinstances): `api-premiumgetprocessinstances` +- [添加审批评论](https://open.dingtalk.com/document/development/official-approval-adds-approval-comments): `official-approval-adds-approval-comments` +- [清理OA审批数据](https://open.dingtalk.com/document/development/clear-oa-approval-data): `clear-oa-approval-data` +- [管理员批量转交指定员工的待处理任务](https://open.dingtalk.com/document/development/api-premiumredirecttasksbymanager): `api-premiumredirecttasksbymanager` +- [管理员查询指定员工的待处理任务列表](https://open.dingtalk.com/document/development/api-premiumquerytodotasksbymanager): `api-premiumquerytodotasksbymanager` +- [获取单个审批实例详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-single-approval-instance-pop): `obtains-the-details-of-a-single-approval-instance-pop` +- [获取单个数据表单实例详情](https://open.dingtalk.com/document/development/obtain-details-of-a-single-data-form-instance): `obtain-details-of-a-single-data-form-instance` +- [获取审批单流程中的节点信息](https://open.dingtalk.com/document/development/approval-process-prediction): `approval-process-prediction` +- [获取审批实例ID列表](https://open.dingtalk.com/document/development/obtain-an-approval-list-of-instance-ids): `obtain-an-approval-list-of-instance-ids` +- [获取审批表单控件字段内容修改记录](https://open.dingtalk.com/document/development/api-premiumgetfieldmodifiedhistory): `api-premiumgetfieldmodifiedhistory` +- [获取审批表单控件字段操作权限](https://open.dingtalk.com/document/development/api-premiumgetinstfieldsetting): `api-premiumgetinstfieldsetting` +- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/api-premiumgetattachmentspace): `api-premiumgetattachmentspace` +- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/obtains-the-information-about-approval-nail-disk): `obtains-the-information-about-approval-nail-disk` +- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/get-all-manageable-forms-for-the-current-enterprise): `get-all-manageable-forms-for-the-current-enterprise` +- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/obtains-a-list-of-approval-forms-visible-to-the-specified): `obtains-a-list-of-approval-forms-visible-to-the-specified` +- [获取数据表单schema](https://open.dingtalk.com/document/development/api-premiumgetformschema): `api-premiumgetformschema` +- [获取数据表单实例列表](https://open.dingtalk.com/document/development/api-premiumgetforminstances): `api-premiumgetforminstances` +- [获取模板code](https://open.dingtalk.com/document/development/obtain-the-template-code): `obtain-the-template-code` +- [获取用户待审批数量](https://open.dingtalk.com/document/development/queries-the-number-of-requests-to-be-approved-by-users): `queries-the-number-of-requests-to-be-approved-by-users` +- [获取表单 schema](https://open.dingtalk.com/document/development/obtain-the-form-schema): `obtain-the-form-schema` +- [转交OA审批任务](https://open.dingtalk.com/document/development/transfer-the-oa-approval-task): `transfer-the-oa-approval-task` +- [退回审批任务](https://open.dingtalk.com/document/development/api-premiumreverttask): `api-premiumreverttask` + +### 企业互通(6 条) + +- [批量获取关注服务窗用户信息](https://open.dingtalk.com/document/development/obtains-the-follower-information-from-the-service-window): `obtains-the-follower-information-from-the-service-window` +- [获取企业下服务窗列表](https://open.dingtalk.com/document/development/queries-the-list-of-services-under-an-enterprise): `queries-the-list-of-services-under-an-enterprise` +- [获取关注服务窗用户信息](https://open.dingtalk.com/document/development/queries-the-follower-information-of-the-service-window): `queries-the-follower-information-of-the-service-window` +- [获取用户关注服务窗状态](https://open.dingtalk.com/document/development/third-party-enterprise-application-obtains-user-attention-service-window-status): `third-party-enterprise-application-obtains-user-attention-service-window-status` +- [获取用户服务窗关注状态](https://open.dingtalk.com/document/development/obtain-the-attention-status-of-the-user-service-window): `obtain-the-attention-status-of-the-user-service-window` +- [获取组织服务窗账号列表](https://open.dingtalk.com/document/development/the-third-party-enterprise-application-obtains-the-account-list-of-the): `the-third-party-enterprise-application-obtains-the-account-list-of-the` + +## AI PaaS(48 条) + +二级 Tab:平台介绍 / 炼丹炉大模型平台 / AI 助理创建平台 / AI 客服助理(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 组织大脑(41 条) + +- [人员标签数据查询](https://open.dingtalk.com/document/development/api-stafflabelrecordsquery): `api-stafflabelrecordsquery` +- [人才档案基础数据查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofilebasicquery): `api-hrbraintalentprofilebasicquery` +- [人才档案照片查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofileattachmentquery): `api-hrbraintalentprofileattachmentquery` +- [人才池信息查询](https://open.dingtalk.com/document/development/api-hrbrainemppoolquery): `api-hrbrainemppoolquery` +- [人才池在池人员列表](https://open.dingtalk.com/document/development/api-hrbrainemppooluser): `api-hrbrainemppooluser` +- [数据集成专业技能删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelprofskill): `api-hrbraindeletelabelprofskill` +- [数据集成专业技能同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelprofskill): `api-hrbrainimportlabelprofskill` +- [数据集成人员信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteempinfo): `api-hrbraindeleteempinfo` +- [数据集成人员信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportempinfo): `api-hrbrainimportempinfo` +- [数据集成人员标签删除](https://open.dingtalk.com/document/development/api-hrbraindeletetlabelbase): `api-hrbraindeletetlabelbase` +- [数据集成入职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportregist): `api-hrbrainimportregist` +- [数据集成入职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregist): `api-hrbraindeleteregist` +- [数据集成删除自定义模型数据](https://open.dingtalk.com/document/development/api-hrbraindeletecustom): `api-hrbraindeletecustom` +- [数据集成培训学习数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletetraining): `api-hrbraindeletetraining` +- [数据集成培训学习记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttraining): `api-hrbrainimporttraining` +- [数据集成基础标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelbase): `api-hrbrainimportlabelbase` +- [数据集成处分记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletepundetail): `api-hrbraindeletepundetail` +- [数据集成处分记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpundetail): `api-hrbrainimportpundetail` +- [数据集成奖励信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteawardrecords): `api-hrbraindeleteawardrecords` +- [数据集成奖励记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportawarddetail): `api-hrbrainimportawarddetail` +- [数据集成工作经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteworkexp): `api-hrbraindeleteworkexp` +- [数据集成工作经历同步](https://open.dingtalk.com/document/development/api-hrbrainimportworkexp): `api-hrbrainimportworkexp` +- [数据集成异动记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttransfereval): `api-hrbrainimporttransfereval` +- [数据集成教育经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteeduexp): `api-hrbraindeleteeduexp` +- [数据集成教育经历同步](https://open.dingtalk.com/document/development/api-hrbrainimporteduexp): `api-hrbrainimporteduexp` +- [数据集成晋升记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpromeval): `api-hrbrainimportpromeval` +- [数据集成盘点数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelinventory): `api-hrbraindeletelabelinventory` +- [数据集成盘点数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelinventory): `api-hrbrainimportlabelinventory` +- [数据集成离职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportdimission): `api-hrbrainimportdimission` +- [数据集成离职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletedimission): `api-hrbraindeletedimission` +- [数据集成组织架构同步](https://open.dingtalk.com/document/development/api-hrbrainimportdeptinfo): `api-hrbrainimportdeptinfo` +- [数据集成组织架构数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletedeptinfo): `api-hrbraindeletedeptinfo` +- [数据集成绩效记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteperfeval): `api-hrbraindeleteperfeval` +- [数据集成绩效记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportperfeval): `api-hrbrainimportperfeval` +- [数据集成自定义标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelcustom): `api-hrbrainimportlabelcustom` +- [数据集成调岗记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletetransfereval): `api-hrbraindeletetransfereval` +- [数据集成转正数据删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregular): `api-hrbraindeleteregular` +- [数据集成转正记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportregular): `api-hrbrainimportregular` +- [数据集成领域经验删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelindustry): `api-hrbraindeletelabelindustry` +- [数据集成领域经验同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelindustry): `api-hrbrainimportlabelindustry` +- [自定义模型数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportcustom): `api-hrbrainimportcustom` + +### 智能客服(4 条) + +- [分页查询工单](https://open.dingtalk.com/document/development/intelligent-customer-service-paging-query-work-order): `intelligent-customer-service-paging-query-work-order` +- [创建自助单](https://open.dingtalk.com/document/development/create-a-self-service-ticket): `create-a-self-service-ticket` +- [执行工单活动](https://open.dingtalk.com/document/development/intelligent-customer-service-execute-work-order-activities): `intelligent-customer-service-execute-work-order-activities` +- [查询动作记录](https://open.dingtalk.com/document/development/intelligent-customer-service-query-action-records): `intelligent-customer-service-query-action-records` + +### AI PaaS(3 条) + +- [大模型推理服务(多模态模型)](https://open.dingtalk.com/document/development/large-model-reasoning-service-interface-multimodal-model-1): `large-model-reasoning-service-interface-multimodal-model-1` +- [大模型推理服务(文生文模型)](https://open.dingtalk.com/document/development/api-exclusivemodelcompleteservice): `api-exclusivemodelcompleteservice` +- [炼丹炉专属模型服务](https://open.dingtalk.com/document/development/api-liandanluexclusivemodel): `api-liandanluexclusivemodel` + +## 硬件开发(28 条) + +二级 Tab:智能硬件(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 智能会议室(22 条) + +- [创建会议室](https://open.dingtalk.com/document/development/create-meeting-room): `create-meeting-room` +- [创建会议室分组](https://open.dingtalk.com/document/development/create-meeting-room-groups): `create-meeting-room-groups` +- [创建会议室预定黑名单](https://open.dingtalk.com/document/development/api-createbookingblacklist): `api-createbookingblacklist` +- [创建自定义屏幕模板](https://open.dingtalk.com/document/development/api-createdevicecustomtemplate): `api-createdevicecustomtemplate` +- [删除会议室](https://open.dingtalk.com/document/development/delete-a-meeting-room): `delete-a-meeting-room` +- [删除会议室分组](https://open.dingtalk.com/document/development/delete-a-conference-room-group): `delete-a-conference-room-group` +- [删除会议室预定黑名单](https://open.dingtalk.com/document/development/api-deletebookingblacklist): `api-deletebookingblacklist` +- [删除自定义屏幕模板](https://open.dingtalk.com/document/development/api-deletedevicecustomtemplate): `api-deletedevicecustomtemplate` +- [发送Rooms中控API信令](https://open.dingtalk.com/document/development/api-sendcentralcontrol): `api-sendcentralcontrol` +- [更新会议室信息](https://open.dingtalk.com/document/development/update-meeting-room-information): `update-meeting-room-information` +- [更新会议室分组信息](https://open.dingtalk.com/document/development/update-meeting-room-groups): `update-meeting-room-groups` +- [更新自定义屏幕模板](https://open.dingtalk.com/document/development/api-updatedevicecustomtemplate): `api-updatedevicecustomtemplate` +- [查询会议室分组信息](https://open.dingtalk.com/document/development/query-meeting-room-groups): `query-meeting-room-groups` +- [查询会议室分组列表](https://open.dingtalk.com/document/development/query-meeting-rooms-groups): `query-meeting-rooms-groups` +- [查询会议室列表](https://open.dingtalk.com/document/development/check-the-meeting-room-list): `check-the-meeting-room-list` +- [查询会议室详情](https://open.dingtalk.com/document/development/check-meeting-room-details): `check-meeting-room-details` +- [查询自定义屏幕信息](https://open.dingtalk.com/document/development/api-querydevicecustomtemplate): `api-querydevicecustomtemplate` +- [查询自定义屏幕模板列表](https://open.dingtalk.com/document/development/api-querylistdevicecustomscreentemplate): `api-querylistdevicecustomscreentemplate` +- [查询视频会议设备信息](https://open.dingtalk.com/document/development/querying-video-conference-device-information): `querying-video-conference-device-information` +- [查询视频会议设备属性信息](https://open.dingtalk.com/document/development/querying-video-conference-device-attribute-information): `querying-video-conference-device-attribute-information` +- [获取用户发出的日志列表](https://open.dingtalk.com/document/development/query-logs-sent-by-an-employee): `query-logs-sent-by-an-employee` +- [获取用户可见的日志模板](https://open.dingtalk.com/document/development/obtains-the-list-of-visible-log-templates-based-on-the): `obtains-the-list-of-visible-log-templates-based-on-the` + +### 设备管理(5 条) + +- [批量注册与激活设备](https://open.dingtalk.com/document/development/register-and-activate-devices-in-batches): `register-and-activate-devices-in-batches` +- [查询已经注册的设备信息](https://open.dingtalk.com/document/development/query-information-about-a-registered-device): `query-information-about-a-registered-device` +- [注册设备到钉钉](https://open.dingtalk.com/document/development/pin-registration-interface): `pin-registration-interface` +- [获取巡检或保养记录](https://open.dingtalk.com/document/development/obtain-inspection-and-maintenance-records): `obtain-inspection-and-maintenance-records` +- [获取报修记录](https://open.dingtalk.com/document/development/obtain-the-repair-report-record): `obtain-the-repair-report-record` + +### 智能设备(1 条) + +- [变更智能考勤机员工](https://open.dingtalk.com/document/development/change-intelligent-attendance-machine-staff): `change-intelligent-attendance-machine-staff` + +## 互动卡片(9 条) + +二级 Tab:开发指南 / 使用教程 / 卡片模板搭建器 / 互动卡片搭建平台 / 卡片规范设计(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 互动卡片(9 条) + +- [AI卡片流式更新](https://open.dingtalk.com/document/development/api-streamingupdate): `api-streamingupdate` +- [关闭吊顶卡片](https://open.dingtalk.com/document/development/api-closetopcard): `api-closetopcard` +- [创建卡片](https://open.dingtalk.com/document/development/interface-for-creating-a-card-instance): `interface-for-creating-a-card-instance` +- [创建并投放卡片](https://open.dingtalk.com/document/development/create-and-deliver-cards): `create-and-deliver-cards` +- [卡片平台模板复制](https://open.dingtalk.com/document/development/api-copytemplate): `api-copytemplate` +- [投放卡片](https://open.dingtalk.com/document/development/delivery-card-interface): `delivery-card-interface` +- [新增或者更新卡片的场域信息](https://open.dingtalk.com/document/development/add-field-interface): `add-field-interface` +- [更新卡片](https://open.dingtalk.com/document/development/interactive-card-update-interface): `interactive-card-update-interface` +- [注册卡片回调地址](https://open.dingtalk.com/document/development/register-card-callback-address): `register-card-callback-address` + +## 专属版客户端插件(37 条) + +二级 Tab:功能介绍 / 插件开发(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 专属版(37 条) + +- [DING服务](https://open.dingtalk.com/document/development/send-in-application-ding): `send-in-application-ding` +- [专属小红点推送](https://open.dingtalk.com/document/development/push-a-red-dot-to-the-micro-application): `push-a-red-dot-to-the-micro-application` +- [企业内部群禁言或解除禁言](https://open.dingtalk.com/document/development/exclusive-dingtalk-group-ban): `exclusive-dingtalk-group-ban` +- [企业员工专属安全管控功能命中查询](https://open.dingtalk.com/document/development/api-checkcontrolhitstatus): `api-checkcontrolhitstatus` +- [修改伙伴类型可见性](https://open.dingtalk.com/document/development/modify-partner-type-visibility): `modify-partner-type-visibility` +- [修改角色可见性](https://open.dingtalk.com/document/development/modify-role-visibility): `modify-role-visibility` +- [删除可信设备](https://open.dingtalk.com/document/development/delete-trusted-devices): `delete-trusted-devices` +- [发送文件更改的评论](https://open.dingtalk.com/document/development/send-comments-on-file-changes): `send-comments-on-file-changes` +- [发送电话DING](https://open.dingtalk.com/document/development/outgoing-phone-ding): `outgoing-phone-ding` +- [发送邀请函](https://open.dingtalk.com/document/development/send-invitations): `send-invitations` +- [同步存储数据](https://open.dingtalk.com/document/development/api-datasync): `api-datasync` +- [批量新增可信设备](https://open.dingtalk.com/document/development/create-multiple-trusted-devices): `create-multiple-trusted-devices` +- [新增可信设备信息](https://open.dingtalk.com/document/development/add-information-about-a-trusted-device): `add-information-about-a-trusted-device` +- [更新发送文件的检测状态](https://open.dingtalk.com/document/development/update-the-detection-status-of-a-sent-file): `update-the-detection-status-of-a-sent-file` +- [查询人脸录入状态](https://open.dingtalk.com/document/development/query-face-entry-status): `query-face-entry-status` +- [查询企业内部群信息](https://open.dingtalk.com/document/development/obtain-group-info): `obtain-group-info` +- [查询伙伴角色列表](https://open.dingtalk.com/document/development/query-the-list-of-partners): `query-the-list-of-partners` +- [查询公共设备](https://open.dingtalk.com/document/development/query-public-equipment): `query-public-equipment` +- [查询可信设备详细信息](https://open.dingtalk.com/document/development/query-trusted-device-details): `query-trusted-device-details` +- [查询实人认证状态](https://open.dingtalk.com/document/development/queries-the-id-verification-status): `queries-the-id-verification-status` +- [查询群发消息列表](https://open.dingtalk.com/document/development/service-account-query-msgsend-records): `service-account-query-msgsend-records` +- [查询群发消息详情](https://open.dingtalk.com/document/development/service-account-msg-record-detail): `service-account-msg-record-detail` +- [根据userId查询人员的标签信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-user-tag): `you-can-call-this-operation-to-retrieve-the-user-tag` +- [根据会议逻辑ID查询会议基本信息](https://open.dingtalk.com/document/development/query-basic-meeting-information-using-a-logical-id): `query-basic-meeting-information-using-a-logical-id` +- [消息群发](https://open.dingtalk.com/document/development/api-sendmessage): `api-sendmessage` +- [获取专属存储文件路径](https://open.dingtalk.com/document/development/api-getprivatestorefilepath): `api-getprivatestorefilepath` +- [获取人脸对比接口调用记录](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-call-records): `you-can-call-this-operation-to-query-the-call-records` +- [获取企业专属钉钉权益列表](https://open.dingtalk.com/document/development/api-queryexclusivebenefits): `api-queryexclusivebenefits` +- [获取可打标部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-departments-that-can-be-marked): `obtains-a-list-of-departments-that-can-be-marked` +- [获取子标签列表](https://open.dingtalk.com/document/development/obtain-child-tags-from-a-parent-tag): `obtain-child-tags-from-a-parent-tag` +- [获取实人认证接口调用记录](https://open.dingtalk.com/document/development/obtains-the-call-record-of-the-id-authentication-api): `obtains-the-call-record-of-the-id-authentication-api` +- [获取审计协议签署人员信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-persons-who-sign-the-audit-1): `obtains-the-information-about-the-persons-who-sign-the-audit-1` +- [获取文件操作记录](https://open.dingtalk.com/document/development/obtain-file-operation-records): `obtain-file-operation-records` +- [获取群活跃明细列表](https://open.dingtalk.com/document/development/obtains-the-group-activity-details-list): `obtains-the-group-activity-details-list` +- [获取视频会议详情](https://open.dingtalk.com/document/development/get-video-meeting-details): `get-video-meeting-details` +- [获取防截屏操作记录](https://open.dingtalk.com/document/development/obtain-anti-screen-capture-operation-records): `obtain-anti-screen-capture-operation-records` +- [设置部门伙伴类型和伙伴编码](https://open.dingtalk.com/document/development/set-department-partner-type-and-partner-code): `set-department-partner-type-and-partner-code` + +## 数据资产(102 条) + +二级 Tab:平台介绍 / 宜数(智能问数)(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 宜搭(61 条) + +- [保存表单数据](https://open.dingtalk.com/document/development/api-saveformdata-v2): `api-saveformdata-v2` +- [分页获取集成自动化日志列表](https://open.dingtalk.com/document/development/api-pageautoflowlog): `api-pageautoflowlog` +- [删除流程实例](https://open.dingtalk.com/document/development/delete-the-process-instance): `delete-the-process-instance` +- [删除表单数据](https://open.dingtalk.com/document/development/delete-form-data): `delete-form-data` +- [发起宜搭审批流程](https://open.dingtalk.com/document/development/api-startinstance-v2): `api-startinstance-v2` +- [同意或拒绝宜搭审批任务](https://open.dingtalk.com/document/development/execute-approval-tasks): `execute-approval-tasks` +- [应用授权校验](https://open.dingtalk.com/document/development/application-authorization-verification): `application-authorization-verification` +- [批量创建表单实例](https://open.dingtalk.com/document/development/create-multiple-form-instances): `create-multiple-form-instances` +- [批量删除宜搭角色成员](https://open.dingtalk.com/document/development/batch-deleterolemembers): `batch-deleterolemembers` +- [批量删除指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-deletematrixdatabyrowids): `api-deletematrixdatabyrowids` +- [批量删除表单实例](https://open.dingtalk.com/document/development/delete-multiple-form-instances): `delete-multiple-form-instances` +- [批量执行宜搭审批任务](https://open.dingtalk.com/document/development/batch-execution-should-take-the-lead-of-approval-tasks): `batch-execution-should-take-the-lead-of-approval-tasks` +- [批量更新宜搭角色成员](https://open.dingtalk.com/document/development/batch-rolemembers): `batch-rolemembers` +- [批量更新表单实例内的组件值](https://open.dingtalk.com/document/development/batch-update-of-component-values-in-form-instances): `batch-update-of-component-values-in-form-instances` +- [批量查询宜搭表单实例的评论](https://open.dingtalk.com/document/development/batch-query-of-comments-appropriate-for-form-instances): `batch-query-of-comments-appropriate-for-form-instances` +- [批量获取流程实例列表](https://open.dingtalk.com/document/development/queries-multiple-process-instances): `queries-multiple-process-instances` +- [批量获取表单实例数据](https://open.dingtalk.com/document/development/obtain-multiple-form-instance-data): `obtain-multiple-form-instance-data` +- [提交评论](https://open.dingtalk.com/document/development/submit-comment): `submit-comment` +- [新增或更新表单实例](https://open.dingtalk.com/document/development/api-createorupdateformdata-v2): `api-createorupdateformdata-v2` +- [新购授权订单](https://open.dingtalk.com/document/development/new-order-authorization): `new-order-authorization` +- [更新宜搭子表单数据](https://open.dingtalk.com/document/development/api-updatesubtable): `api-updatesubtable` +- [更新指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-saveandupdatematrixdata): `api-saveandupdatematrixdata` +- [更新表单数据](https://open.dingtalk.com/document/development/api-updateformdata-v2): `api-updateformdata-v2` +- [查询宜搭应用列表](https://open.dingtalk.com/document/development/query-the-application-list): `query-the-application-list` +- [查询宜搭表单服务调用执行记录](https://open.dingtalk.com/document/development/the-query-should-be-based-on-the-execution-records-of): `the-query-should-be-based-on-the-execution-records-of` +- [查询抄送我的任务列表(应用维度)](https://open.dingtalk.com/document/development/query-copied-my-task-list-application-dimension): `query-copied-my-task-list-application-dimension` +- [查询流程运行任务(VPC)](https://open.dingtalk.com/document/development/query-process-running-tasks-vpc): `query-process-running-tasks-vpc` +- [查询用户详情](https://open.dingtalk.com/document/development/query-user-details): `query-user-details` +- [查询表单实例数据](https://open.dingtalk.com/document/development/api-searchformdatas-v2): `api-searchformdatas-v2` +- [查询表单数据](https://open.dingtalk.com/document/development/api-getformdatabyid-v2): `api-getformdatabyid-v2` +- [查询表单的变更记录](https://open.dingtalk.com/document/development/change-records-of-query-forms): `change-records-of-query-forms` +- [校验订单](https://open.dingtalk.com/document/development/verification-order): `verification-order` +- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/api-getinstancebyid-v2): `api-getinstancebyid-v2` +- [终止流程实例](https://open.dingtalk.com/document/development/terminate-a-process-instance): `terminate-a-process-instance` +- [获取任务列表(组织维度)](https://open.dingtalk.com/document/development/query-tasks-from-the-organization-dimension): `query-tasks-from-the-organization-dimension` +- [获取发送给用户的通知](https://open.dingtalk.com/document/development/get-notifications-sent-to-users): `get-notifications-sent-to-users` +- [获取员工组件的值](https://open.dingtalk.com/document/development/gets-the-value-of-the-employee-component): `gets-the-value-of-the-employee-component` +- [获取多个表单实例ID](https://open.dingtalk.com/document/development/api-searchformdataidlist-v2): `api-searchformdataidlist-v2` +- [获取子表组件数据](https://open.dingtalk.com/document/development/obtain-child-table-component-data): `obtain-child-table-component-data` +- [获取宜搭附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-free-access-address-of-yixian-accessories): `obtain-the-temporary-free-access-address-of-yixian-accessories` +- [获取实例ID列表](https://open.dingtalk.com/document/development/api-getinstanceidlist-v2): `api-getinstanceidlist-v2` +- [获取审批记录](https://open.dingtalk.com/document/development/queries-an-approval-record): `queries-an-approval-record` +- [获取指定宜搭角色的角色详情](https://open.dingtalk.com/document/development/get-roledetailbyid): `get-roledetailbyid` +- [获取指定应用下的表单列表](https://open.dingtalk.com/document/development/depending-on-the-application-id-to-get-the-form-list): `depending-on-the-application-id-to-get-the-form-list` +- [获取指定权限矩阵的明细数据](https://open.dingtalk.com/document/development/api-getmatrixdetailbyid): `api-getmatrixdetailbyid` +- [获取流程实例](https://open.dingtalk.com/document/development/api-getinstances-v2): `api-getinstances-v2` +- [获取流程设计结构](https://open.dingtalk.com/document/development/api-getprocessdesign): `api-getprocessdesign` +- [获取组件别名列表](https://open.dingtalk.com/document/development/api-getformcomponentaliaslist): `api-getformcomponentaliaslist` +- [获取组织内已完成的审批任务](https://open.dingtalk.com/document/development/obtains-the-completed-approval-tasks-in-an-organization): `obtains-the-completed-approval-tasks-in-an-organization` +- [获取组织内某人提交的任务](https://open.dingtalk.com/document/development/obtains-the-tasks-submitted-by-someone-in-an-organization): `obtains-the-tasks-submitted-by-someone-in-an-organization` +- [获取表单内的组件信息](https://open.dingtalk.com/document/development/get-form-field-information-based-on-form-uuid): `get-form-field-information-based-on-form-uuid` +- [获取表单组件定义列表](https://open.dingtalk.com/document/development/get-a-list-of-form-component-definitions): `get-a-list-of-form-component-definitions` +- [获取集成自动化日志详情](https://open.dingtalk.com/document/development/api-getautoflowlogdetail): `api-getautoflowlogdetail` +- [转交任务](https://open.dingtalk.com/document/development/transfer-tasks): `transfer-tasks` +- [退还商品](https://open.dingtalk.com/document/development/refund-of-goods): `refund-of-goods` +- [通过RowId更新子表单数据](https://open.dingtalk.com/document/development/update-the-subform-data-via-rowid): `update-the-subform-data-via-rowid` +- [通过流程code获取流程定义](https://open.dingtalk.com/document/development/obtain-definition-through-process-code): `obtain-definition-through-process-code` +- [通过表单实例数据批量更新表单实例](https://open.dingtalk.com/document/development/update-multiple-form-instances-with-the-form-instance-data): `update-multiple-form-instances-with-the-form-instance-data` +- [通过高级查询条件获取表单实例数据(不包括子表单组件数据)](https://open.dingtalk.com/document/development/obtain-form-instance-data-using-advanced-query-conditions-excluding-subform): `obtain-form-instance-data-using-advanced-query-conditions-excluding-subform` +- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/api-searchformdatasecondgeneration-v2): `api-searchformdatasecondgeneration-v2` +- [预览审批流程](https://open.dingtalk.com/document/development/api-previewpublishedprocess): `api-previewpublishedprocess` + +### 氚云(20 条) + +- [修改表单业务对象数据](https://open.dingtalk.com/document/development/modify-form-business-object-data): `modify-form-business-object-data` +- [创建流程实例](https://open.dingtalk.com/document/development/create-a-process-instance): `create-a-process-instance` +- [创建表单业务数据](https://open.dingtalk.com/document/development/create-form-business-data): `create-form-business-data` +- [删除业务对象](https://open.dingtalk.com/document/development/delete-a-business-object): `delete-a-business-object` +- [删除流程实例数据](https://open.dingtalk.com/document/development/delete-process-instance-data): `delete-process-instance-data` +- [取消流程实例](https://open.dingtalk.com/document/development/cancel-a-process-instance): `cancel-a-process-instance` +- [批量新增表单业务数据](https://open.dingtalk.com/document/development/batch-add-form-business-data): `batch-add-form-business-data` +- [查询流程实例](https://open.dingtalk.com/document/development/query-flow-instances): `query-flow-instances` +- [查询流程实例节点工作项](https://open.dingtalk.com/document/development/query-flow-instance-node-work-items): `query-flow-instance-node-work-items` +- [查询表单业务数据列表](https://open.dingtalk.com/document/development/querying-form-business-data): `querying-form-business-data` +- [获取业务实例信息](https://open.dingtalk.com/document/development/queries-business-instance-information): `queries-business-instance-information` +- [获取应用列表](https://open.dingtalk.com/document/development/queries-applications): `queries-applications` +- [获取应用功能节点](https://open.dingtalk.com/document/development/queries-the-application-feature-nodes): `queries-the-application-feature-nodes` +- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-2): `obtain-the-upload-url-of-a-file-2` +- [获取用户数据](https://open.dingtalk.com/document/development/obtain-user-data): `obtain-user-data` +- [获取组织数据](https://open.dingtalk.com/document/development/queries-organization-data): `queries-organization-data` +- [获取表单对象结构](https://open.dingtalk.com/document/development/gets-the-form-object-structure): `gets-the-form-object-structure` +- [获取角色数据](https://open.dingtalk.com/document/development/obtain-role-data): `obtain-role-data` +- [获取角色用户数据](https://open.dingtalk.com/document/development/historical-acquisition-of-role-user-data): `historical-acquisition-of-role-user-data` +- [获取附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-attachment-free-address): `obtain-the-temporary-attachment-free-address` + +### 多维表(14 条) + +- [列出多行记录](https://open.dingtalk.com/document/development/api-notable-listrecords): `api-notable-listrecords` +- [创建字段](https://open.dingtalk.com/document/development/api-noatable-createfield): `api-noatable-createfield` +- [创建数据表](https://open.dingtalk.com/document/development/api-createsheet): `api-createsheet` +- [删除多行记录](https://open.dingtalk.com/document/development/api-noatable-deleterecords): `api-noatable-deleterecords` +- [删除字段](https://open.dingtalk.com/document/development/api-noatable-deletefield): `api-noatable-deletefield` +- [删除数据表](https://open.dingtalk.com/document/development/api-noatable-deletesheet): `api-noatable-deletesheet` +- [新增记录](https://open.dingtalk.com/document/development/api-notable-insertrecords): `api-notable-insertrecords` +- [更新多行记录](https://open.dingtalk.com/document/development/api-noatable-updaterecords): `api-noatable-updaterecords` +- [更新字段](https://open.dingtalk.com/document/development/api-noatable-updatefield): `api-noatable-updatefield` +- [更新数据表](https://open.dingtalk.com/document/development/api-noatable-updatesheet): `api-noatable-updatesheet` +- [获取所有字段](https://open.dingtalk.com/document/development/api-noatable-getallfields): `api-noatable-getallfields` +- [获取所有数据表](https://open.dingtalk.com/document/development/api-notable-getallsheets): `api-notable-getallsheets` +- [获取数据表](https://open.dingtalk.com/document/development/api-notable-getsheet): `api-notable-getsheet` +- [获取记录](https://open.dingtalk.com/document/development/api-getrecord): `api-getrecord` + +### 数据中台 AMDP(4 条) + +- [组织变革主数据人员任职数据推送](https://open.dingtalk.com/document/development/api-amdpjobpositiondatapush): `api-amdpjobpositiondatapush` +- [组织变革主数据人员数据推送](https://open.dingtalk.com/document/development/api-amdpemployeedatapush): `api-amdpemployeedatapush` +- [组织变革主数据人员角色数据推送](https://open.dingtalk.com/document/development/api-amdpemproledatapush): `api-amdpemproledatapush` +- [组织变革主数据部门数据推送](https://open.dingtalk.com/document/development/api-amdporganizationdatapush): `api-amdporganizationdatapush` + +### 表单(3 条) + +- [获取单条填表实例详情](https://open.dingtalk.com/document/development/obtains-the-instance-details-of-a-single-fill-table): `obtains-the-instance-details-of-a-single-fill-table` +- [获取填表实例列表](https://open.dingtalk.com/document/development/obtain-the-table-filling-instance-list-data): `obtain-the-table-filling-instance-list-data` +- [获取用户创建的填表模板列表](https://open.dingtalk.com/document/development/new-obtains-the-template-that-a-user-creates): `new-obtains-the-template-that-a-user-creates` + +## 工作台(24 条) + +二级 Tab:平台介绍 / 使用教程(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 微应用(13 条) + +- [创建企业内部应用](https://open.dingtalk.com/document/development/create-an-h5-application-for-your-enterprise): `create-an-h5-application-for-your-enterprise` +- [删除企业内部应用](https://open.dingtalk.com/document/development/delete-an-internal-h5-application): `delete-an-internal-h5-application` +- [发布企业内部小程序版本](https://open.dingtalk.com/document/development/release-internal-applet-version): `release-internal-applet-version` +- [回滚企业内部小程序版本](https://open.dingtalk.com/document/development/rollback-of-enterprise-internal-applet-version): `rollback-of-enterprise-internal-applet-version` +- [更新企业内部应用](https://open.dingtalk.com/document/development/update-internal-h5-applications): `update-internal-h5-applications` +- [更新企业内部应用的可使用范围](https://open.dingtalk.com/document/development/update-the-visible-range-of-micro-applications): `update-the-visible-range-of-micro-applications` +- [查询管理员是否有应用管理权限](https://open.dingtalk.com/document/development/check-whether-the-administrator-has-application-management-permissions): `check-whether-the-administrator-has-application-management-permissions` +- [获取企业内部小程序历史版本列表](https://open.dingtalk.com/document/development/obtain-the-list-of-historical-versions-of-enterprise-internal-applets): `obtain-the-list-of-historical-versions-of-enterprise-internal-applets` +- [获取企业内部小程序的版本列表](https://open.dingtalk.com/document/development/get-the-version-list-of-the-enterprise-internal-applet): `get-the-version-list-of-the-enterprise-internal-applet` +- [获取企业内部应用的可使用范围](https://open.dingtalk.com/document/development/obtains-the-application-visible-range): `obtains-the-application-visible-range` +- [获取企业内部所有应用列表](https://open.dingtalk.com/document/development/get-a-list-of-all-applications-inside-the-enterprise): `get-a-list-of-all-applications-inside-the-enterprise` +- [获取企业所有应用列表](https://open.dingtalk.com/document/development/obtains-a-list-of-all-enterprise-applications): `obtains-a-list-of-all-enterprise-applications` +- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/obtains-the-list-of-enterprise-applications-visible-to-a-user): `obtains-the-list-of-enterprise-applications-visible-to-a-user` + +### 应用角标(8 条) + +- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-badge-user-instance): `create-a-badge-user-instance` +- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-verification-result): `notification-dingtalk-badge-verification-result` +- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-dingtalk-user-instance): `update-dingtalk-user-instance` +- [解码钉工牌电子码](https://open.dingtalk.com/document/development/stack-dingtalk-badge): `stack-dingtalk-badge` +- [通知支付结果](https://open.dingtalk.com/document/development/sync-dingtalk-badge-code-payment-result): `sync-dingtalk-badge-code-payment-result` +- [通知退款结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-code-refund-result): `notification-dingtalk-badge-code-refund-result` +- [配置企业钉工牌](https://open.dingtalk.com/document/development/save-dingtalk-enterprise-instance): `save-dingtalk-enterprise-instance` +- [钉工牌通知消息](https://open.dingtalk.com/document/development/dingtalk-badge-notification-message): `dingtalk-badge-notification-message` + +### 工作台(3 条) + +- [批量添加最近使用应用](https://open.dingtalk.com/document/development/add-recently-used-apps-in-bulk): `add-recently-used-apps-in-bulk` +- [获取工作台插件权限点](https://open.dingtalk.com/document/development/obtain-the-permissions-of-the-workbench-plug-in): `obtain-the-permissions-of-the-workbench-plug-in` +- [获取工作台插件检验的规则信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-obtain-the-information-about): `you-can-call-this-operation-to-obtain-the-information-about` + +## 其它 / 未归类(557 条) + +### 未归类(557 条) + +- [**获取指定用户的所有父部门列表**](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-specified-user): `queries-all-parent-departments-of-a-specified-user` +- [**获取第三方企业应用的suite_access_token**](https://open.dingtalk.com/document/development/obtain-application-suite-ticket): `obtain-application-suite-ticket` +- [AI 助理发消息(回复消息模式)](https://open.dingtalk.com/document/development/ai-assistant-messages-reply-mode): `ai-assistant-messages-reply-mode` +- [AI 助理更新消息(主动发送模式)](https://open.dingtalk.com/document/development/the-ai-assistant-updates-active-message-sending-mode): `the-ai-assistant-updates-active-message-sending-mode` +- [AI 助理结束发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-finish): `api-finish` +- [AI 助理预备发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-prepare): `api-prepare` +- [AI助理发消息(主动发送模式)](https://open.dingtalk.com/document/development/ai-assistant-active-sends-messages-mode): `ai-assistant-active-sends-messages-mode` +- [AI助理响应接口-个人权限](https://open.dingtalk.com/document/development/api-assistantmeresponse): `api-assistantmeresponse` +- [AI助理响应接口-应用权限](https://open.dingtalk.com/document/development/api-assistantresponse): `api-assistantresponse` +- [ASR 一句话语音识别](https://open.dingtalk.com/document/development/asr-short-sentence-recognition): `asr-short-sentence-recognition` +- [OCR文字识别](https://open.dingtalk.com/document/development/structured-image-recognition-api): `structured-image-recognition-api` +- [groupId转换为groupKey](https://open.dingtalk.com/document/development/groupid-to-groupkey): `groupid-to-groupkey` +- [groupKey转换为groupId](https://open.dingtalk.com/document/development/convert-groupkey-to-groupid): `convert-groupkey-to-groupid` +- [上传媒体文件](https://open.dingtalk.com/document/development/upload-media-files): `upload-media-files` +- [上传打卡记录](https://open.dingtalk.com/document/development/upload-punch-records): `upload-punch-records` +- [上传文件块](https://open.dingtalk.com/document/development/upload-file-blocks): `upload-file-blocks` +- [下载审批附件](https://open.dingtalk.com/document/development/grants-the-permission-to-download-the-approval-file): `grants-the-permission-to-download-the-approval-file` +- [事件推送](https://open.dingtalk.com/document/development/dingtalk-iot-push-events): `dingtalk-iot-push-events` +- [企业活跃用户统计列表(部门维度)](https://open.dingtalk.com/document/development/query-the-statistics-of-active-users-in-a-department-of): `query-the-statistics-of-active-users-in-a-department-of` +- [使商品过期](https://open.dingtalk.com/document/development/make-goods-expire): `make-goods-expire` +- [使用服务助手推送消息](https://open.dingtalk.com/document/development/the-message-pushing-interface-of-the-assistant): `the-message-pushing-interface-of-the-assistant` +- [使用模板发送工作通知消息](https://open.dingtalk.com/document/development/work-notification-templating-send-notification-interface): `work-notification-templating-send-notification-interface` +- [保存文件到自定义或审批钉盘空间](https://open.dingtalk.com/document/development/add-file-to-user-s-dingtalk-disk): `add-file-to-user-s-dingtalk-disk` +- [保存日志内容](https://open.dingtalk.com/document/development/save-custom-log-content): `save-custom-log-content` +- [保存表单数据](https://open.dingtalk.com/document/development/save-form-data): `save-form-data` +- [修改发票配置](https://open.dingtalk.com/document/development/modify-invoice-configuration): `modify-invoice-configuration` +- [修改打卡时段设置](https://open.dingtalk.com/document/development/modify-card-settings): `modify-card-settings` +- [修改文件(夹)名](https://open.dingtalk.com/document/development/modify-the-file-and-folder-name): `modify-the-file-and-folder-name` +- [修改日程](https://open.dingtalk.com/document/development/schedule-2-0-modify-schedule): `schedule-2-0-modify-schedule` +- [修改日程参与者](https://open.dingtalk.com/document/development/schedule-2-0-participant-modification): `schedule-2-0-participant-modification` +- [修改权限](https://open.dingtalk.com/document/development/modify-pin-disk-permission-click): `modify-pin-disk-permission-click` +- [修改申请单](https://open.dingtalk.com/document/development/user-modify-approval-form): `user-modify-approval-form` +- [修改直播课程的可观看白名单](https://open.dingtalk.com/document/development/modify-the-whitelist-for-live-streaming-courses): `modify-the-whitelist-for-live-streaming-courses` +- [修改设备昵称](https://open.dingtalk.com/document/development/intelligent-hardware-device-nickname-modification): `intelligent-hardware-device-nickname-modification` +- [修改课程](https://open.dingtalk.com/document/development/modify-course): `modify-course` +- [修改项目](https://open.dingtalk.com/document/development/project-change): `project-change` +- [停用群模板](https://open.dingtalk.com/document/development/disable-a-group-template): `disable-a-group-template` +- [关闭互动卡片实例置顶](https://open.dingtalk.com/document/development/disable-the-sticky-card-setting): `disable-the-sticky-card-setting` +- [列出插件信息](https://open.dingtalk.com/document/development/query-plug-in-information): `query-plug-in-information` +- [创建AI助理](https://open.dingtalk.com/document/development/assistant-management-create-an-ai-assistant): `assistant-management-create-an-ai-assistant` +- [创建AI助理的运行任务](https://open.dingtalk.com/document/development/api-createassistantrun): `api-createassistantrun` +- [创建CRM自定义对象数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-create-custom-crm-object-data): `dingtalk-paas-master-create-custom-crm-object-data` +- [创建SSO企业账号](https://open.dingtalk.com/document/development/create-an-sso-account): `create-an-sso-account` +- [创建专属帐号用户](https://open.dingtalk.com/document/development/create-dedicated-accounts): `create-dedicated-accounts` +- [创建互动卡片实例](https://open.dingtalk.com/document/development/create-an-interactive-card-instance-2): `create-an-interactive-card-instance-2` +- [创建企业内部应用H5微应用](https://open.dingtalk.com/document/development/create-an-h5-microapplication): `create-an-h5-microapplication` +- [创建企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-customer-data): `dingtalk-paas-master-data-create-crm-customer-data` +- [创建公告](https://open.dingtalk.com/document/development/create-an-enterprise-announcement): `create-an-enterprise-announcement` +- [创建培训课程](https://open.dingtalk.com/document/development/create-live-courses): `create-live-courses` +- [创建学段](https://open.dingtalk.com/document/development/create-a-learning-segment): `create-a-learning-segment` +- [创建学科实例](https://open.dingtalk.com/document/development/create-dingtalk-education-subject-instance): `create-dingtalk-education-subject-instance` +- [创建实例](https://open.dingtalk.com/document/development/initiate-an-approval-process-without-a-process): `initiate-an-approval-process-without-a-process` +- [创建年级](https://open.dingtalk.com/document/development/create-grade): `create-grade` +- [创建待办事项](https://open.dingtalk.com/document/development/create-a-to-do-task): `create-a-to-do-task` +- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-or-update-approval-templates): `create-or-update-approval-templates` +- [创建或更新审批模板](https://open.dingtalk.com/document/development/save-approval-template): `save-approval-template` +- [创建日程](https://open.dingtalk.com/document/development/schedule-2-0-creation-interface): `schedule-2-0-creation-interface` +- [创建消息](https://open.dingtalk.com/document/development/api-createassistantmessage): `api-createassistantmessage` +- [创建班次](https://open.dingtalk.com/document/development/create-modify-shifts): `create-modify-shifts` +- [创建班级](https://open.dingtalk.com/document/development/create-a-class): `create-a-class` +- [创建用户](https://open.dingtalk.com/document/development/create-user): `create-user` +- [创建用户](https://open.dingtalk.com/document/development/user-information-creation): `user-information-creation` +- [创建线程](https://open.dingtalk.com/document/development/api-createassistantthread): `api-createassistantthread` +- [创建群](https://open.dingtalk.com/document/development/create-a-scene-group-v2): `create-a-scene-group-v2` +- [创建群](https://open.dingtalk.com/document/development/session-management-creates-groups): `session-management-creates-groups` +- [创建考勤组](https://open.dingtalk.com/document/development/attendance-group-write): `attendance-group-write` +- [创建联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-contact-data): `dingtalk-paas-master-data-create-crm-contact-data` +- [创建角色](https://open.dingtalk.com/document/development/address-book-add-role): `address-book-add-role` +- [创建角色组](https://open.dingtalk.com/document/development/add-a-role-group): `add-a-role-group` +- [创建课程](https://open.dingtalk.com/document/development/create-course): `create-course` +- [创建部门](https://open.dingtalk.com/document/development/address-book-creation-department-established-department): `address-book-creation-department-established-department` +- [创建部门](https://open.dingtalk.com/document/development/create-a-department): `create-a-department` +- [创建部门](https://open.dingtalk.com/document/development/industry-connection-department-creation): `industry-connection-department-creation` +- [创建钉钉自建企业账号](https://open.dingtalk.com/document/development/create-dingtalk-user-created-dedicated-account): `create-dingtalk-user-created-dedicated-account` +- [初始化假期余额](https://open.dingtalk.com/document/development/initialize-holiday-balance): `initialize-holiday-balance` +- [初始化家校架构](https://open.dingtalk.com/document/development/initialize-the-home-school-architecture): `initialize-the-home-school-architecture` +- [删除AI助理](https://open.dingtalk.com/document/development/assistant-management-deletes-ai-assistants): `assistant-management-deletes-ai-assistants` +- [删除AI助理的消息体](https://open.dingtalk.com/document/development/api-deleteassistantmessage): `api-deleteassistantmessage` +- [删除AI助理线程](https://open.dingtalk.com/document/development/api-deleteassistantthread): `api-deleteassistantthread` +- [删除H5微应用](https://open.dingtalk.com/document/development/delete-an-h5-microapplication): `delete-an-h5-microapplication` +- [删除企业客户数据](https://open.dingtalk.com/document/development/delete-crm-customer): `delete-crm-customer` +- [删除假期规则](https://open.dingtalk.com/document/development/api-for-deleting-holiday-types): `api-for-deleting-holiday-types` +- [删除公告](https://open.dingtalk.com/document/development/delete-announcements-based-on-the-announcement-id): `delete-announcements-based-on-the-announcement-id` +- [删除发票信息](https://open.dingtalk.com/document/development/delete-invoice-information): `delete-invoice-information` +- [删除回收站文件(夹)](https://open.dingtalk.com/document/development/delete-recycle-bin-files-folders): `delete-recycle-bin-files-folders` +- [删除图文卡片](https://open.dingtalk.com/document/development/delete-message-card): `delete-message-card` +- [删除培训课程](https://open.dingtalk.com/document/development/delete-live-training-courses): `delete-live-training-courses` +- [删除外部联系人](https://open.dingtalk.com/document/development/delete-external-contact): `delete-external-contact` +- [删除学科实例](https://open.dingtalk.com/document/development/delete-dingtalk-education-disciplines): `delete-dingtalk-education-disciplines` +- [删除序列](https://open.dingtalk.com/document/development/delete-sequence): `delete-sequence` +- [删除成本中心](https://open.dingtalk.com/document/development/delete-cost-center): `delete-cost-center` +- [删除成本中心人员信息](https://open.dingtalk.com/document/development/delete-the-personnel-information-of-the-cost-center): `delete-the-personnel-information-of-the-cost-center` +- [删除文件(夹)](https://open.dingtalk.com/document/development/delete-objects): `delete-objects` +- [删除文章](https://open.dingtalk.com/document/development/delete-article-1): `delete-article-1` +- [删除权限](https://open.dingtalk.com/document/development/delete-the-pin-disk-permission): `delete-the-pin-disk-permission` +- [删除模板](https://open.dingtalk.com/document/development/delete-a-template): `delete-a-template` +- [删除班次](https://open.dingtalk.com/document/development/delete-shift): `delete-shift` +- [删除用户](https://open.dingtalk.com/document/development/delete-a-member): `delete-a-member` +- [删除用户](https://open.dingtalk.com/document/development/delete-a-user): `delete-a-user` +- [删除群成员](https://open.dingtalk.com/document/development/scene-group-delete): `scene-group-delete` +- [删除考勤组](https://open.dingtalk.com/document/development/delete-attendance-group): `delete-attendance-group` +- [删除联系人数据](https://open.dingtalk.com/document/development/delete-crm-contact): `delete-crm-contact` +- [删除角色](https://open.dingtalk.com/document/development/delete-role-information): `delete-role-information` +- [删除设备](https://open.dingtalk.com/document/development/delete-a-device): `delete-a-device` +- [删除课程](https://open.dingtalk.com/document/development/delete-course): `delete-course` +- [删除部门](https://open.dingtalk.com/document/development/address-book-deletion-department): `address-book-deletion-department` +- [删除部门](https://open.dingtalk.com/document/development/delete-a-department): `delete-a-department` +- [删除项目](https://open.dingtalk.com/document/development/delete-a-project): `delete-a-project` +- [剪辑直播课程回放](https://open.dingtalk.com/document/development/clip-live-course-playback): `clip-live-course-playback` +- [加入课程](https://open.dingtalk.com/document/development/join-course): `join-course` +- [助理删除知识](https://open.dingtalk.com/document/development/api-deleteknowledge): `api-deleteknowledge` +- [助理学习知识](https://open.dingtalk.com/document/development/api-learnknowledge): `api-learnknowledge` +- [助理重新学习](https://open.dingtalk.com/document/development/api-relearnknowledge): `api-relearnknowledge` +- [单步文件上传](https://open.dingtalk.com/document/development/single-step-file-upload): `single-step-file-upload` +- [发布商品](https://open.dingtalk.com/document/development/release-products): `release-products` +- [发布文章](https://open.dingtalk.com/document/development/article-publishing-interface-1): `article-publishing-interface-1` +- [发起宜搭审批流程](https://open.dingtalk.com/document/development/initiate-the-approval-process): `initiate-the-approval-process` +- [发起审批实例](https://open.dingtalk.com/document/development/oa-approval-initiates-approval-instances): `oa-approval-initiates-approval-instances` +- [发送工作通知](https://open.dingtalk.com/document/development/asynchronous-sending-of-enterprise-session-messages): `asynchronous-sending-of-enterprise-session-messages` +- [发送普通消息](https://open.dingtalk.com/document/development/send-normal-messages-1): `send-normal-messages-1` +- [发送消息到企业群](https://open.dingtalk.com/document/development/send-group-messages): `send-group-messages` +- [发送群助手消息](https://open.dingtalk.com/document/development/group-template-robot-message): `group-template-robot-message` +- [发送钉盘文件给指定用户](https://open.dingtalk.com/document/development/sends-a-file-to-a-specified-user): `sends-a-file-to-a-specified-user` +- [取消日程](https://open.dingtalk.com/document/development/schedule-2-0-cancel-schedule): `schedule-2-0-cancel-schedule` +- [变配租户信息](https://open.dingtalk.com/document/development/change-tenant-information-1): `change-tenant-information-1` +- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/execute-approval-operation-with-attachment): `execute-approval-operation-with-attachment` +- [启用群模板](https://open.dingtalk.com/document/development/enable-a-group-template): `enable-a-group-template` +- [商旅成本中心转换为外部成本中心](https://open.dingtalk.com/document/development/business-travel-cost-center-converted-to-external-cost-center): `business-travel-cost-center-converted-to-external-cost-center` +- [回放课程](https://open.dingtalk.com/document/development/replay-course): `replay-course` +- [多渠道新购校验](https://open.dingtalk.com/document/development/multi-channel-new-purchase-verification): `multi-channel-new-purchase-verification` +- [学习推荐数据回流](https://open.dingtalk.com/document/development/learn-to-recommend-data-backflow): `learn-to-recommend-data-backflow` +- [家庭Feed同步](https://open.dingtalk.com/document/development/dingtalk-education-family-feed-synchronization): `dingtalk-education-family-feed-synchronization` +- [将助理技能发布到组织技能库](https://open.dingtalk.com/document/development/api-addtoorgskillrepository): `api-addtoorgskillrepository` +- [应用内购商品核销](https://open.dingtalk.com/document/development/application-of-in-house-purchase-verification): `application-of-in-house-purchase-verification` +- [开启互动卡片实例置顶](https://open.dingtalk.com/document/development/enable-the-interactive-card-setting): `enable-the-interactive-card-setting` +- [开启分块上传事务](https://open.dingtalk.com/document/development/enable-upload-transaction): `enable-upload-transaction` +- [开始课程](https://open.dingtalk.com/document/development/start-course): `start-course` +- [执行宜搭的审批任务](https://open.dingtalk.com/document/development/execute-appropriate-approval-tasks): `execute-appropriate-approval-tasks` +- [执行自定义API](https://open.dingtalk.com/document/development/run-custom-api): `run-custom-api` +- [批量修改设备](https://open.dingtalk.com/document/development/batch-modify-devices): `batch-modify-devices` +- [批量删除参与考勤人员](https://open.dingtalk.com/document/development/batch-delete-employees-under-the-attendance-group): `batch-delete-employees-under-the-attendance-group` +- [批量删除员工角色](https://open.dingtalk.com/document/development/delete-the-color-information-of-employee-corners-in-batches): `delete-the-color-information-of-employee-corners-in-batches` +- [批量删除地点](https://open.dingtalk.com/document/development/delete-position-in-batches-under-the-attendance-group): `delete-position-in-batches-under-the-attendance-group` +- [批量发起回调](https://open.dingtalk.com/document/development/initiate-multiple-callbacks): `initiate-multiple-callbacks` +- [批量取消待办](https://open.dingtalk.com/document/development/cancel-multiple-tasks): `cancel-multiple-tasks` +- [批量增加员工角色](https://open.dingtalk.com/document/development/add-role-information-to-employees-in-batches): `add-role-information-to-employees-in-batches` +- [批量新增Wi-Fi信息](https://open.dingtalk.com/document/development/batch-add-wifi-under-attendance-group): `batch-add-wifi-under-attendance-group` +- [批量新增参与考勤人员](https://open.dingtalk.com/document/development/batch-add-employees-under-the-attendance-group): `batch-add-employees-under-the-attendance-group` +- [批量新增地点](https://open.dingtalk.com/document/development/atch-add-position-under-attendance-group): `atch-add-position-under-attendance-group` +- [批量更新假期余额](https://open.dingtalk.com/document/development/bulk-update-holiday-balance): `bulk-update-holiday-balance` +- [批量更新实例状态](https://open.dingtalk.com/document/development/update-the-status-of-multiple-instances-at-a-time): `update-the-status-of-multiple-instances-at-a-time` +- [批量查询Wi-Fi信息](https://open.dingtalk.com/document/development/batch-query-wifi-under-attendance-group): `batch-query-wifi-under-attendance-group` +- [批量查询人员排班信息](https://open.dingtalk.com/document/development/query-batch-scheduling-information): `query-batch-scheduling-information` +- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/query-holiday-consumption-records): `query-holiday-consumption-records` +- [批量查询地点](https://open.dingtalk.com/document/development/batch-query-position-under-attendance-group): `batch-query-position-under-attendance-group` +- [批量查询成员排班概要信息](https://open.dingtalk.com/document/development/query-scheduling-summary-information): `query-scheduling-summary-information` +- [批量注册事件类型](https://open.dingtalk.com/document/development/registration-event-type): `registration-event-type` +- [批量注册设备](https://open.dingtalk.com/document/development/batchregister-devices): `batchregister-devices` +- [批量注册设备](https://open.dingtalk.com/document/development/industry-connection-device-batch-registration): `industry-connection-device-batch-registration` +- [批量移除Wi-Fi信息](https://open.dingtalk.com/document/development/batch-remove-wifi-under-attendance-group): `batch-remove-wifi-under-attendance-group` +- [批量获取企业客户数据](https://open.dingtalk.com/document/development/obtains-customer-data-in-batches-based-on-the-id-list): `obtains-customer-data-in-batches-based-on-the-id-list` +- [批量获取客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-customer-data-search-and-query-interface): `dingtalk-paas-master-data-customer-data-search-and-query-interface` +- [批量获取应用信息](https://open.dingtalk.com/document/development/queries-the-information-about-multiple-applications): `queries-the-information-about-multiple-applications` +- [批量获取服务窗联系人数据](https://open.dingtalk.com/document/development/obtain-contact-data-from-the-service-window): `obtain-contact-data-from-the-service-window` +- [批量获取考勤组摘要](https://open.dingtalk.com/document/development/batch-query-of-simple-information-of-the-attendance-group): `batch-query-of-simple-information-of-the-attendance-group` +- [批量获取考勤组详情](https://open.dingtalk.com/document/development/batch-obtain-attendance-group-details): `batch-obtain-attendance-group-details` +- [批量获取钉钉运动数据](https://open.dingtalk.com/document/development/queries-the-number-of-dingtalk-movement-steps-of-multiple-users): `queries-the-number-of-dingtalk-movement-steps-of-multiple-users` +- [拷贝文件(夹)](https://open.dingtalk.com/document/development/copy-files-folders): `copy-files-folders` +- [按名称搜索班次](https://open.dingtalk.com/document/development/search-shifts-by-rank): `search-shifts-by-rank` +- [按照ID列表批量获取CRM自定义表单数据](https://open.dingtalk.com/document/development/retrieves-custom-crm-forms-from-the-id-list): `retrieves-custom-crm-forms-from-the-id-list` +- [按照ID列表批量获取联系人数据](https://open.dingtalk.com/document/development/retrieves-contact-data-in-batches-based-on-the-id-list): `retrieves-contact-data-in-batches-based-on-the-id-list` +- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/approve-nail-disk-file-authorization): `approve-nail-disk-file-authorization` +- [授权用户访问企业的自定义空间](https://open.dingtalk.com/document/development/authorize-a-user-to-access-a-custom-workspace-of-an): `authorize-a-user-to-access-a-custom-workspace-of-an` +- [授权预览审批附件](https://open.dingtalk.com/document/development/preview-authorization-attachment): `preview-authorization-attachment` +- [排班制考勤组排班](https://open.dingtalk.com/document/development/scheduling-system-attendance-group-scheduling): `scheduling-system-attendance-group-scheduling` +- [提交文件上传事务](https://open.dingtalk.com/document/development/submit-a-file-upload-transaction): `submit-a-file-upload-transaction` +- [搜索考勤组摘要](https://open.dingtalk.com/document/development/attendance-group-search): `attendance-group-search` +- [撤回工作通知消息](https://open.dingtalk.com/document/development/notification-of-work-withdrawal): `notification-of-work-withdrawal` +- [撤销审批实例](https://open.dingtalk.com/document/development/terminate-a-workflow-by-using-an-instance-id): `terminate-a-workflow-by-using-an-instance-id` +- [新增发票配置](https://open.dingtalk.com/document/development/new-invoice-configuration): `new-invoice-configuration` +- [新增图文卡片](https://open.dingtalk.com/document/development/new-message-card-1): `new-message-card-1` +- [新增或更新表单实例](https://open.dingtalk.com/document/development/add-or-update-form-instances): `add-or-update-form-instances` +- [新增文章](https://open.dingtalk.com/document/development/new-article-1): `new-article-1` +- [新增服务号](https://open.dingtalk.com/document/development/added-service-number): `added-service-number` +- [新增群成员](https://open.dingtalk.com/document/development/add-people-to-scene-group): `add-people-to-scene-group` +- [新增钉钉待办任务](https://open.dingtalk.com/document/development/new-to-do-items): `new-to-do-items` +- [新购宜搭产品](https://open.dingtalk.com/document/development/suitable-for-new-purchase): `suitable-for-new-purchase` +- [更新 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-updateassistantscope): `api-updateassistantscope` +- [更新AI助理基础信息](https://open.dingtalk.com/document/development/api-updateassistantbasicinfo): `api-updateassistantbasicinfo` +- [更新企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-microapplication): `set-the-visible-range-of-the-microapplication` +- [更新企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-customer-data): `dingtalk-paas-master-data-update-crm-customer-data` +- [更新企业账号用户信息](https://open.dingtalk.com/document/development/update-dedicated-accounts-information): `update-dedicated-accounts-information` +- [更新假期规则](https://open.dingtalk.com/document/development/holiday-type-update): `holiday-type-update` +- [更新公告](https://open.dingtalk.com/document/development/modify-the-announcement-according-to-the-announcement-id): `modify-the-announcement-according-to-the-announcement-id` +- [更新参与考勤人员](https://open.dingtalk.com/document/development/attendance-group-member-update): `attendance-group-member-update` +- [更新员工花名册](https://open.dingtalk.com/document/development/update-employee-roster): `update-employee-roster` +- [更新员工花名册信息](https://open.dingtalk.com/document/development/intelligent-personnel-update-employee-file-information): `intelligent-personnel-update-employee-file-information` +- [更新图文卡片](https://open.dingtalk.com/document/development/message-card-material-update-interface): `message-card-material-update-interface` +- [更新外部联系人](https://open.dingtalk.com/document/development/update-enterprise-external-contacts): `update-enterprise-external-contacts` +- [更新学科实例](https://open.dingtalk.com/document/development/update-dingtalk-education-instance): `update-dingtalk-education-instance` +- [更新实例状态](https://open.dingtalk.com/document/development/to-do-instance-status): `to-do-instance-status` +- [更新工作通知状态栏](https://open.dingtalk.com/document/development/update-work-notification-status-bar): `update-work-notification-status-bar` +- [更新待办状态](https://open.dingtalk.com/document/development/update-to-do-task-status): `update-to-do-task-status` +- [更新文章](https://open.dingtalk.com/document/development/save-article-details-1): `save-article-details-1` +- [更新服务号](https://open.dingtalk.com/document/development/service-number-update-1): `service-number-update-1` +- [更新流程实例](https://open.dingtalk.com/document/development/update-process-instance-yida): `update-process-instance-yida` +- [更新状态](https://open.dingtalk.com/document/development/update-status): `update-status` +- [更新用户信息](https://open.dingtalk.com/document/development/update-user-details): `update-user-details` +- [更新用户信息](https://open.dingtalk.com/document/development/user-information-update): `user-information-update` +- [更新申请单状态](https://open.dingtalk.com/document/development/update-approval-form): `update-approval-form` +- [更新群](https://open.dingtalk.com/document/development/modify-a-group-session): `modify-a-group-session` +- [更新群](https://open.dingtalk.com/document/development/scene-group-update): `scene-group-update` +- [更新群成员的群昵称](https://open.dingtalk.com/document/development/set-a-group-nickname): `set-a-group-nickname` +- [更新群管理员](https://open.dingtalk.com/document/development/set-chat-admin): `set-chat-admin` +- [更新考勤组](https://open.dingtalk.com/document/development/attendance-group-update-interface): `attendance-group-update-interface` +- [更新联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-contact-data): `dingtalk-paas-master-data-update-crm-contact-data` +- [更新自定义对象数据](https://open.dingtalk.com/document/development/crm-master-data-opens-interface-for-updating-custom-object-data): `crm-master-data-opens-interface-for-updating-custom-object-data` +- [更新表单数据](https://open.dingtalk.com/document/development/update-form-data): `update-form-data` +- [更新角色名称](https://open.dingtalk.com/document/development/update-the-character-name): `update-the-character-name` +- [更新部门](https://open.dingtalk.com/document/development/address-book-update-department): `address-book-update-department` +- [更新部门](https://open.dingtalk.com/document/development/update-a-department): `update-a-department` +- [更新部门扩展信息](https://open.dingtalk.com/document/development/department-update-extension-information): `department-update-extension-information` +- [更新钉钉待办任务](https://open.dingtalk.com/document/development/update-to-do-status): `update-to-do-status` +- [服务号菜单更新](https://open.dingtalk.com/document/development/service-number-menu-update): `service-number-menu-update` +- [服务商获取第三方应用授权企业的access_token](https://open.dingtalk.com/document/development/obtain-isvapp-token): `obtain-isvapp-token` +- [机票城市搜索](https://open.dingtalk.com/document/development/air-ticket-city-search): `air-ticket-city-search` +- [查询 AI 助理基本信息](https://open.dingtalk.com/document/development/api-retrieveassistantbasicinfo): `api-retrieveassistantbasicinfo` +- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/get-the-user-s-to-do-items): `get-the-user-s-to-do-items` +- [查询企业个人待办数量](https://open.dingtalk.com/document/development/query-the-number-of-to-do-tasks-of-the-enterprise): `query-the-number-of-to-do-tasks-of-the-enterprise` +- [查询企业级别](https://open.dingtalk.com/document/development/query-enterprise-level): `query-enterprise-level` +- [查询企业考勤排班详情](https://open.dingtalk.com/document/development/interface-for-daily-full-query-of-attendance-scheduling-information): `interface-for-daily-full-query-of-attendance-scheduling-information` +- [查询企业账号用户详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-dedicated-account): `queries-the-details-of-a-dedicated-account` +- [查询企业通讯录未激活用户列表](https://open.dingtalk.com/document/development/queries-the-list-of-inactive-accounts-in-the-key-account): `queries-the-list-of-inactive-accounts-in-the-key-account` +- [查询假期规则列表](https://open.dingtalk.com/document/development/holiday-type-query): `holiday-type-query` +- [查询历史班次](https://open.dingtalk.com/document/development/query-history-shifts): `query-history-shifts` +- [查询参与考勤人员列表](https://open.dingtalk.com/document/development/batch-query-of-employees-in-the-attendance-group): `batch-query-of-employees-in-the-attendance-group` +- [查询可用发票列表](https://open.dingtalk.com/document/development/query-available-invoices): `query-available-invoices` +- [查询员工智能考勤机列表](https://open.dingtalk.com/document/development/query-the-list-of-employee-intelligent-attendance-machines): `query-the-list-of-employee-intelligent-attendance-machines` +- [查询商品列表](https://open.dingtalk.com/document/development/query-product-lists): `query-product-lists` +- [查询回收站文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-recycle-bin-folder-list): `obtain-the-recycle-bin-folder-list` +- [查询图文卡片列表](https://open.dingtalk.com/document/development/query-message-card-list): `query-message-card-list` +- [查询家庭孩子信息](https://open.dingtalk.com/document/development/query-family-child-information): `query-family-child-information` +- [查询应用信息列表](https://open.dingtalk.com/document/development/queries-application-information): `queries-application-information` +- [查询待办列表](https://open.dingtalk.com/document/development/query-a-user-s-to-do-items): `query-a-user-s-to-do-items` +- [查询成本中心](https://open.dingtalk.com/document/development/query-cost-center): `query-cost-center` +- [查询排班打卡结果](https://open.dingtalk.com/document/development/query-the-results-of-a-batch-of-tasks): `query-the-results-of-a-batch-of-tasks` +- [查询插件信息列表](https://open.dingtalk.com/document/development/query-plug-ins): `query-plug-ins` +- [查询文件(夹)信息](https://open.dingtalk.com/document/development/obtain-file-information): `obtain-file-information` +- [查询文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-file-list): `obtain-the-file-list` +- [查询文档模板](https://open.dingtalk.com/document/development/query-a-document-template): `query-a-document-template` +- [查询文章列表](https://open.dingtalk.com/document/development/query-the-article-list): `query-the-article-list` +- [查询是否启用智能统计报表](https://open.dingtalk.com/document/development/determine-whether-to-enable-attendance-intelligent-report): `determine-whether-to-enable-attendance-intelligent-report` +- [查询服务号列表](https://open.dingtalk.com/document/development/query-service-number-list): `query-service-number-list` +- [查询服务号菜单](https://open.dingtalk.com/document/development/query-service-number-menu-1): `query-service-number-menu-1` +- [查询服务号详情](https://open.dingtalk.com/document/development/inquire-about-service-number-details): `inquire-about-service-number-details` +- [查询激活码](https://open.dingtalk.com/document/development/query-activation-code): `query-activation-code` +- [查询用户是否参与企业步数排行榜](https://open.dingtalk.com/document/development/check-whether-dingtalk-is-enabled): `check-whether-dingtalk-is-enabled` +- [查询用户详情](https://open.dingtalk.com/document/development/queries-user-details): `queries-user-details` +- [查询直播的观看数据](https://open.dingtalk.com/document/development/queries-the-playback-data-of-a-live-stream): `queries-the-playback-data-of-a-live-stream` +- [查询直播课程的可观看白名单](https://open.dingtalk.com/document/development/query-the-whitelist-of-live-courses): `query-the-whitelist-of-live-courses` +- [查询群信息](https://open.dingtalk.com/document/development/obtain-a-group-session): `obtain-a-group-session` +- [查询群信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-a-scenario-group): `queries-the-basic-information-of-a-scenario-group` +- [查询群消息已读人员列表](https://open.dingtalk.com/document/development/queries-the-list-of-people-who-have-read-a-group): `queries-the-list-of-people-who-have-read-a-group` +- [查询表单实例数据](https://open.dingtalk.com/document/development/querying-form-instance-data): `querying-form-instance-data` +- [查询表单数据](https://open.dingtalk.com/document/development/query-form-data): `query-form-data` +- [查询设备列表](https://open.dingtalk.com/document/development/intelligent-hardware-list-query): `intelligent-hardware-list-query` +- [查询设备详情](https://open.dingtalk.com/document/development/intelligent-hardware-device-query): `intelligent-hardware-device-query` +- [查询请假状态](https://open.dingtalk.com/document/development/query-status): `query-status` +- [查询销售用户信息](https://open.dingtalk.com/document/development/query-sales-user-information): `query-sales-user-information` +- [查询项目中文件操作日志](https://open.dingtalk.com/document/development/query-file-operation-logs-of-a-project): `query-file-operation-logs-of-a-project` +- [查询预估价](https://open.dingtalk.com/document/development/query-estimated-price): `query-estimated-price` +- [校验变配](https://open.dingtalk.com/document/development/verify-configuration): `verify-configuration` +- [校验用户是否在当前考勤组](https://open.dingtalk.com/document/development/query-members-by-id): `query-members-by-id` +- [校验订单的升级](https://open.dingtalk.com/document/development/upgrade-the-verification-order): `upgrade-the-verification-order` +- [校验订单的升级状态](https://open.dingtalk.com/document/development/upgrade-status-of-the-verification-order): `upgrade-status-of-the-verification-order` +- [根据ID列表批量获取跟进记录数据](https://open.dingtalk.com/document/development/dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you): `dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you` +- [根据groupKey查询考勤组信息](https://open.dingtalk.com/document/development/queries-attendance-group-information-by-id): `queries-attendance-group-information-by-id` +- [根据sns临时授权码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-user-information-based-on-the-sns-temporary-authorization): `obtain-the-user-information-based-on-the-sns-temporary-authorization` +- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/query-a-user-by-the-union-id): `query-a-user-by-the-union-id` +- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-userids-of): `you-can-call-this-operation-to-retrieve-the-userids-of` +- [根据手机号查询企业账号用户](https://open.dingtalk.com/document/development/obtain-the-userid-of-your-mobile-phone-number): `obtain-the-userid-of-your-mobile-phone-number` +- [根据手机号查询用户](https://open.dingtalk.com/document/development/retrieve-userid-from-mobile-phone-number): `retrieve-userid-from-mobile-phone-number` +- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/dingtalk-the-contact-data-query-api): `dingtalk-the-contact-data-query-api` +- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/retrieve-custom-crm-object-data): `retrieve-custom-crm-object-data` +- [根据指定条件查询跟进记录数据](https://open.dingtalk.com/document/development/query-and-dingtalk-data-of-track-records-in-apsara-stack): `query-and-dingtalk-data-of-track-records-in-apsara-stack` +- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/queries-a-process-instance-based-on-its-id): `queries-a-process-instance-based-on-its-id` +- [根据设备ID查询设备](https://open.dingtalk.com/document/development/the-smart-hardware-can-query-details-based-on-the-device): `the-smart-hardware-can-query-details-based-on-the-device` +- [检索AI助理线程](https://open.dingtalk.com/document/development/api-retrieveassistantthread): `api-retrieveassistantthread` +- [注册互动卡片回调地址](https://open.dingtalk.com/document/development/registration-card-interaction-callback-address-1): `registration-card-interaction-callback-address-1` +- [注册单个设备](https://open.dingtalk.com/document/development/industry-connection-single-device-registration): `industry-connection-single-device-registration` +- [注册设备](https://open.dingtalk.com/document/development/register-devices): `register-devices` +- [注册账号](https://open.dingtalk.com/document/development/register-an-account): `register-an-account` +- [消息撤回](https://open.dingtalk.com/document/development/service-number-message-withdrawal): `service-number-message-withdrawal` +- [消息群发](https://open.dingtalk.com/document/development/interactive-service-window-group-message-sending-interface): `interactive-service-window-group-message-sending-interface` +- [添加企业待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-through-intelligent-personnel): `add-employees-to-be-hired-through-intelligent-personnel` +- [添加假期规则](https://open.dingtalk.com/document/development/holiday-type-added): `holiday-type-added` +- [添加外部联系人](https://open.dingtalk.com/document/development/add-enterprise-external-contacts): `add-enterprise-external-contacts` +- [添加学生](https://open.dingtalk.com/document/development/add-student): `add-student` +- [添加审批评论](https://open.dingtalk.com/document/development/add-an-approval-comment): `add-an-approval-comment` +- [添加家长](https://open.dingtalk.com/document/development/add-parent): `add-parent` +- [添加文件(夹)](https://open.dingtalk.com/document/development/add-file-and-folder): `add-file-and-folder` +- [添加权限](https://open.dingtalk.com/document/development/add-pin-disk-permission): `add-pin-disk-permission` +- [添加老师](https://open.dingtalk.com/document/development/add-teacher): `add-teacher` +- [添加自定义空间权限](https://open.dingtalk.com/document/development/add-custom-workspace-permissions): `add-custom-workspace-permissions` +- [添加课程参与方](https://open.dingtalk.com/document/development/add-course-participants): `add-course-participants` +- [添加项目](https://open.dingtalk.com/document/development/add-a-project): `add-a-project` +- [清理审批数据](https://open.dingtalk.com/document/development/clean-up-workflow-data): `clean-up-workflow-data` +- [清空回收站](https://open.dingtalk.com/document/development/empty-recycle-bin-files-folders): `empty-recycle-bin-files-folders` +- [火车票城市搜索](https://open.dingtalk.com/document/development/train-ticket-city-search): `train-ticket-city-search` +- [移动文件(夹)](https://open.dingtalk.com/document/development/move-file-and-folder): `move-file-and-folder` +- [移除助理组织技能库技能](https://open.dingtalk.com/document/development/api-removefromorgskillrepository): `api-removefromorgskillrepository` +- [移除租户资源](https://open.dingtalk.com/document/development/remove-tenant-resources): `remove-tenant-resources` +- [移除课程参与方](https://open.dingtalk.com/document/development/remove-course-participants): `remove-course-participants` +- [终止阿里云授权](https://open.dingtalk.com/document/development/terminate-authorization-for-alibaba-cloud-services): `terminate-authorization-for-alibaba-cloud-services` +- [绑定设备](https://open.dingtalk.com/document/development/establishing-a-binding-relationship-between-intelligent-hardware-and-cloud): `establishing-a-binding-relationship-between-intelligent-hardware-and-cloud` +- [结束课程](https://open.dingtalk.com/document/development/end-course): `end-course` +- [统计企业活跃用户](https://open.dingtalk.com/document/development/query-for-dau-statistics): `query-for-dau-statistics` +- [续费服务订单](https://open.dingtalk.com/document/development/renewal-service-order): `renewal-service-order` +- [续费租户](https://open.dingtalk.com/document/development/renewal-tenant): `renewal-tenant` +- [自定义机器人发送群消息](https://open.dingtalk.com/document/development/custom-robots-send-group-messages): `custom-robots-send-group-messages` +- [获取 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-retrieveassistantscope): `api-retrieveassistantscope` +- [获取AI助理对话明细列表](https://open.dingtalk.com/document/development/api-loglist): `api-loglist` +- [获取AI助理的消息体](https://open.dingtalk.com/document/development/api-retrieveassistantmessage): `api-retrieveassistantmessage` +- [获取AI助理的消息列表](https://open.dingtalk.com/document/development/api-listassistantmessage): `api-listassistantmessage` +- [获取AI助理的运行任务](https://open.dingtalk.com/document/development/api-retrieveassistantrun): `api-retrieveassistantrun` +- [获取AI助理的运行任务的列表](https://open.dingtalk.com/document/development/api-listassistantrun): `api-listassistantrun` +- [获取jsapi_ticket](https://open.dingtalk.com/document/development/obtain-jsapi-ticket): `obtain-jsapi-ticket` +- [获取个人或部门钉钉运动数据](https://open.dingtalk.com/document/development/queries-individual-or-department-dingtalk-exercise-steps): `queries-individual-or-department-dingtalk-exercise-steps` +- [获取主干组织列表](https://open.dingtalk.com/document/development/obtain-backbone-organization-list): `obtain-backbone-organization-list` +- [获取互动服务窗相关数据](https://open.dingtalk.com/document/development/queries-the-data-about-the-interactive-service-window): `queries-the-data-about-the-interactive-service-window` +- [获取人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-home-school-user-identities): `obtains-a-list-of-home-school-user-identities` +- [获取人员详情](https://open.dingtalk.com/document/development/obtain-the-identity-details-of-home-school-personnel): `obtain-the-identity-details-of-home-school-personnel` +- [获取企业DING使用数据](https://open.dingtalk.com/document/development/enterprise-ding-quantity-statistics): `enterprise-ding-quantity-statistics` +- [获取企业DING使用数据(部门维度)](https://open.dingtalk.com/document/development/query-the-departmental-transmission-status-of-key-clients): `query-the-departmental-transmission-status-of-key-clients` +- [获取企业DING发送统计数据](https://open.dingtalk.com/document/development/obtain-sending-statistics-of-an-enterprise-ding): `obtain-sending-statistics-of-an-enterprise-ding` +- [获取企业DING接收及评论统计数据](https://open.dingtalk.com/document/development/obtain-statistics-on-receiving-and-comments-of-enterprise-ding): `obtain-statistics-on-receiving-and-comments-of-enterprise-ding` +- [获取企业下的自定义空间](https://open.dingtalk.com/document/development/obtain-user-space-under-the-enterprise): `obtain-user-space-under-the-enterprise` +- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-information): `obtain-enterprise-information` +- [获取企业全员圈统计数据](https://open.dingtalk.com/document/development/obtains-the-statistical-data-of-all-employees-of-an-enterprise): `obtains-the-statistical-data-of-all-employees-of-an-enterprise` +- [获取企业公告统计数据](https://open.dingtalk.com/document/development/queries-corporate-announcement-statistics): `queries-corporate-announcement-statistics` +- [获取企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/gets-the-microapplication-visible-range-set-by-the-enterprise): `gets-the-microapplication-visible-range-set-by-the-enterprise` +- [获取企业内部应用的access_token](https://open.dingtalk.com/document/development/obtain-orgapp-token): `obtain-orgapp-token` +- [获取企业单聊统计数据](https://open.dingtalk.com/document/development/queries-the-statistics-on-one-time-enterprise-chats): `queries-the-statistics-on-one-time-enterprise-chats` +- [获取企业发布智能填表数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-of-tables-published-in-an-organization): `queries-the-number-of-tables-published-in-an-organization` +- [获取企业发红包统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-red-packets-issued-by-enterprises): `obtains-the-statistics-on-red-packets-issued-by-enterprises` +- [获取企业各类群组创建情况](https://open.dingtalk.com/document/development/api-for-obtaining-the-creation-status-of-various-groups): `api-for-obtaining-the-creation-status-of-various-groups` +- [获取企业员工类型统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-employee-types): `obtains-statistics-on-employee-types` +- [获取企业商旅酒店订单数据](https://open.dingtalk.com/document/development/enterprises-obtain-order-data-for-business-hotels): `enterprises-obtain-order-data-for-business-hotels` +- [获取企业审批统计数据](https://open.dingtalk.com/document/development/obtains-enterprise-approval-statistics): `obtains-enterprise-approval-statistics` +- [获取企业客户的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-customer-object): `get-metadata-description-of-crm-customer-object` +- [获取企业已经加入的或申请加入中的上下游组织的信息](https://open.dingtalk.com/document/development/obtains-information-about-the-workspaces-that-the-enterprise-has-joined): `obtains-information-about-the-workspaces-that-the-enterprise-has-joined` +- [获取企业应用访问情况](https://open.dingtalk.com/document/development/queries-the-daily-usage-summary-of-microapplications-in-an-enterprise): `queries-the-daily-usage-summary-of-microapplications-in-an-enterprise` +- [获取企业待办统计数据](https://open.dingtalk.com/document/development/obtains-the-to-do-statistics-of-an-enterprise): `obtains-the-to-do-statistics-of-an-enterprise` +- [获取企业所有应用列表](https://open.dingtalk.com/document/development/manager-microapplications-api-permission): `manager-microapplications-api-permission` +- [获取企业授权信息](https://open.dingtalk.com/document/development/obtains-the-basic-information-of-an-enterprise): `obtains-the-basic-information-of-an-enterprise` +- [获取企业接收红包统计数据](https://open.dingtalk.com/document/development/queries-the-red-envelope-receiving-statistics-of-an-enterprise): `queries-the-red-envelope-receiving-statistics-of-an-enterprise` +- [获取企业文档统计数据](https://open.dingtalk.com/document/development/get-enterprise-document-statistics): `get-enterprise-document-statistics` +- [获取企业日志统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-log-statistics): `obtain-enterprise-log-statistics` +- [获取企业日程统计数据](https://open.dingtalk.com/document/development/queries-enterprise-schedule-statistics): `queries-enterprise-schedule-statistics` +- [获取企业某天的所有部门电话会议统计列表](https://open.dingtalk.com/document/development/major-customer-department-dimension-teleconference-statistics): `major-customer-department-dimension-teleconference-statistics` +- [获取企业某天的所有部门视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-list-for-key-accounts-and-departments): `video-conferencing-statistics-list-for-key-accounts-and-departments` +- [获取企业某天的电话会议数据](https://open.dingtalk.com/document/development/major-customer-teleconference-statistics-interface): `major-customer-teleconference-statistics-interface` +- [获取企业某天的视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-query-v2-for-key-accounts): `video-conferencing-statistics-query-v2-for-key-accounts` +- [获取企业火车票订单数据](https://open.dingtalk.com/document/development/obtains-the-enterprise-train-ticket-order-data): `obtains-the-enterprise-train-ticket-order-data` +- [获取企业用户在线统计数据](https://open.dingtalk.com/document/development/retrieve-online-statistics-of-enterprise-users): `retrieve-online-statistics-of-enterprise-users` +- [获取企业用户激活状态统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-user-activation-status): `obtains-statistics-on-user-activation-status` +- [获取企业电话会议明细列表](https://open.dingtalk.com/document/development/major-account-conference-call-details-list): `major-account-conference-call-details-list` +- [获取企业电话会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-teleconference-statistics): `get-enterprise-teleconference-statistics` +- [获取企业签到统计数据](https://open.dingtalk.com/document/development/queries-enterprise-check-in-statistics): `queries-enterprise-check-in-statistics` +- [获取企业群直播统计数据](https://open.dingtalk.com/document/development/obtains-the-live-stream-statistics-for-an-enterprise-group): `obtains-the-live-stream-statistics-for-an-enterprise-group` +- [获取企业群聊统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-group-chat-statistics): `obtain-enterprise-group-chat-statistics` +- [获取企业考勤统计数据](https://open.dingtalk.com/document/development/queries-enterprise-attendance-statistics): `queries-enterprise-attendance-statistics` +- [获取企业聊天数据](https://open.dingtalk.com/document/development/chat-data-statistics-query-for-key-accounts): `chat-data-statistics-query-for-key-accounts` +- [获取企业视频会议明细列表](https://open.dingtalk.com/document/development/video-conference-details-for-key-accounts): `video-conference-details-for-key-accounts` +- [获取企业视频会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-video-conference-statistics): `get-enterprise-video-conference-statistics` +- [获取企业视频直播统计列表(部门维度)](https://open.dingtalk.com/document/development/live-broadcast-summary-statistics-of-key-account-departments): `live-broadcast-summary-statistics-of-key-account-departments` +- [获取企业视频直播统计数据](https://open.dingtalk.com/document/development/query-live-streaming-statistics): `query-live-streaming-statistics` +- [获取企业邮箱统计数据](https://open.dingtalk.com/document/development/queries-enterprise-email-statistics): `queries-enterprise-email-statistics` +- [获取企业部门聊天数据(部门维度)](https://open.dingtalk.com/document/development/dingtalk-chat-information-in-key-accounts): `dingtalk-chat-information-in-key-accounts` +- [获取企业钉盘统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-enterprise-dingtalk-trays): `obtains-the-statistics-on-enterprise-dingtalk-trays` +- [获取企业钉钉运动统计数据](https://open.dingtalk.com/document/development/queries-dingtalk-movement-statistics): `queries-dingtalk-movement-statistics` +- [获取入群二维码链接](https://open.dingtalk.com/document/development/obtain-a-qr-code-link): `obtain-a-qr-code-link` +- [获取公告ID列表](https://open.dingtalk.com/document/development/obtains-the-id-list-of-announcements-that-are-not-deleted): `obtains-the-id-list-of-announcements-that-are-not-deleted` +- [获取公告分类列表](https://open.dingtalk.com/document/development/obtains-the-list-of-categories-not-deleted-for-enterprise-announcements): `obtains-the-list-of-categories-not-deleted-for-enterprise-announcements` +- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-bulletin-that-is-not-deleted): `obtains-the-details-of-a-bulletin-that-is-not-deleted` +- [获取内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-product-sku-details-page): `obtain-the-address-of-the-product-sku-details-page` +- [获取内购订单信息](https://open.dingtalk.com/document/development/obtain-information-about-internal-purchase-orders): `obtain-information-about-internal-purchase-orders` +- [获取分支组织列表](https://open.dingtalk.com/document/development/obtains-the-branch-organization-list): `obtains-the-branch-organization-list` +- [获取助理技能信息](https://open.dingtalk.com/document/development/api-getassistantactioninfo): `api-getassistantactioninfo` +- [获取单个审批实例详情](https://open.dingtalk.com/document/development/get-details-single-approval-instance): `get-details-single-approval-instance` +- [获取参与考勤人员](https://open.dingtalk.com/document/development/batch-query-of-attendance-group-members): `batch-query-of-attendance-group-members` +- [获取参与考勤人员的userid](https://open.dingtalk.com/document/development/query-attendance-group-personnel-information-in-batches): `query-attendance-group-personnel-information-in-batches` +- [获取发布智能填表数量和使用智能填表人数(部门维度)](https://open.dingtalk.com/document/development/obtains-the-number-of-tables-published-by-the-enterprise-from): `obtains-the-number-of-tables-published-by-the-enterprise-from` +- [获取员工人数](https://open.dingtalk.com/document/development/obtain-the-number-of-employees): `obtain-the-number-of-employees` +- [获取员工人数](https://open.dingtalk.com/document/development/user-management-acquires-number-employees): `user-management-acquires-number-employees` +- [获取员工离职信息](https://open.dingtalk.com/document/development/obtain-multiple-employee-demission-information): `obtain-multiple-employee-demission-information` +- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/intelligent-personnel-obtain-employee-roster-information): `intelligent-personnel-obtain-employee-roster-information` +- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/obtain-employee-roster-field-information-in-batches): `obtain-employee-roster-field-information-in-batches` +- [获取图文卡片详情](https://open.dingtalk.com/document/development/get-message-card-details): `get-message-card-details` +- [获取在职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-on-the-job-employees-of-the): `intelligent-personnel-query-the-list-of-on-the-job-employees-of-the` +- [获取培训观看数据](https://open.dingtalk.com/document/development/obtains-the-playback-data-of-a-live-stream): `obtains-the-playback-data-of-a-live-stream` +- [获取培训课程的基本信息](https://open.dingtalk.com/document/development/get-basic-information-about-the-course): `get-basic-information-about-the-course` +- [获取填表实例数据](https://open.dingtalk.com/document/development/obtains-multiple-form-filling-records): `obtains-multiple-form-filling-records` +- [获取外部联系人列表](https://open.dingtalk.com/document/development/obtain-the-external-contact-list): `obtain-the-external-contact-list` +- [获取外部联系人标签列表](https://open.dingtalk.com/document/development/obtains-a-list-of-external-contact-tags): `obtains-a-list-of-external-contact-tags` +- [获取外部联系人详情](https://open.dingtalk.com/document/development/obtains-the-external-contact-details-of-an-enterprise): `obtains-the-external-contact-details-of-an-enterprise` +- [获取多个表单实例ID](https://open.dingtalk.com/document/development/obtain-the-ids-of-multiple-form-instances): `obtain-the-ids-of-multiple-form-instances` +- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-a-sub-department-id-list): `obtain-a-sub-department-id-list` +- [获取学习的知识列表](https://open.dingtalk.com/document/development/api-getknowledgelist): `api-getknowledgelist` +- [获取学段元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-to-query): `dingtalk-the-main-data-of-the-education-ecosystem-to-query` +- [获取学生ID列表](https://open.dingtalk.com/document/development/retrieve-student-based-class): `retrieve-student-based-class` +- [获取学生信息](https://open.dingtalk.com/document/development/obtain-student-information): `obtain-student-information` +- [获取学生监护人详情](https://open.dingtalk.com/document/development/obtain-the-relationship-between-home-and-school-personnel): `obtain-the-relationship-between-home-and-school-personnel` +- [获取学科元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject): `dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject` +- [获取学科实例列表](https://open.dingtalk.com/document/development/get-the-list-of-subject-examples): `get-the-list-of-subject-examples` +- [获取学科实例详情](https://open.dingtalk.com/document/development/query-dingtalk-education-subject-instances): `query-dingtalk-education-subject-instances` +- [获取定制应用的access_token](https://open.dingtalk.com/document/development/obtains-the-enterprise-authorized-credential): `obtains-the-enterprise-authorized-credential` +- [获取实例ID列表](https://open.dingtalk.com/document/development/obtains-a-list-of-instance-ids): `obtains-a-list-of-instance-ids` +- [获取实例详情](https://open.dingtalk.com/document/development/query-collection-form-instance-details): `query-collection-form-instance-details` +- [获取审批实例ID列表](https://open.dingtalk.com/document/development/operation-to-retrieve-a-list-of): `operation-to-retrieve-a-list-of` +- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/query-the-space-of-an-approval-nail): `query-the-space-of-an-approval-nail` +- [获取工作通知消息的发送结果](https://open.dingtalk.com/document/development/gets-the-result-of-sending-messages-asynchronously-to-the-enterprise): `gets-the-result-of-sending-messages-asynchronously-to-the-enterprise` +- [获取工作通知消息的发送进度](https://open.dingtalk.com/document/development/obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session): `obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session` +- [获取已加入或正在申请加入上下游组织的组织和个人信息](https://open.dingtalk.com/document/development/obtains-the-information-about-how-to-join-or-apply-to): `obtains-the-information-about-how-to-join-or-apply-to` +- [获取平台服务资源](https://open.dingtalk.com/document/development/obtain-platform-service-resources): `obtain-platform-service-resources` +- [获取平台资源](https://open.dingtalk.com/document/development/obtain-platform-resources): `obtain-platform-resources` +- [获取年报数据](https://open.dingtalk.com/document/development/obtain-annual-report-data): `obtain-annual-report-data` +- [获取应用下的页面列表](https://open.dingtalk.com/document/development/obtains-the-page-list-under-an-application): `obtains-the-page-list-under-an-application` +- [获取应用未激活的企业列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprises-whose-applications-are-not-activated): `obtains-a-list-of-enterprises-whose-applications-are-not-activated` +- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/exchange-code-for-the-identity-information-of-a-microapplication-administrator): `exchange-code-for-the-identity-information-of-a-microapplication-administrator` +- [获取应用自定义空间使用详情](https://open.dingtalk.com/document/development/queries-the-usage-details-of-a-custom-application-space): `queries-the-usage-details-of-a-custom-application-space` +- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/obtains-the-information-about-all-manageable-templates-of-the-current): `obtains-the-information-about-all-manageable-templates-of-the-current` +- [获取待入职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-employees-to-be-hired): `intelligent-personnel-query-the-list-of-employees-to-be-hired` +- [获取微应用后台免登的access_token](https://open.dingtalk.com/document/development/obtain-the-ssotoken-for-micro-application-background-logon-free): `obtain-the-ssotoken-for-micro-application-background-logon-free` +- [获取报表假期数据](https://open.dingtalk.com/document/development/obtains-the-holiday-data-from-the-smart-attendance-report): `obtains-the-holiday-data-from-the-smart-attendance-report` +- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-a-list-of): `you-can-call-this-operation-to-retrieve-a-list-of` +- [获取指定用户的所有父部门列表](https://open.dingtalk.com/document/development/queries-the-list-of-all-parent-departments-of-a-user): `queries-the-list-of-all-parent-departments-of-a-user` +- [获取指定角色的员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-of-a-role): `obtain-the-list-of-employees-of-a-role` +- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-department): `queries-all-parent-departments-of-a-department` +- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/query-the-list-of-all-parent-departments-of-a-department): `query-the-list-of-all-parent-departments-of-a-department` +- [获取授权应用的基本信息](https://open.dingtalk.com/document/development/obtains-application-information-of-an-enterprise): `obtains-application-information-of-an-enterprise` +- [获取数字化证书](https://open.dingtalk.com/document/development/obtain-digital-certificate): `obtain-digital-certificate` +- [获取数字区县组织信息](https://open.dingtalk.com/document/development/querydigitaldistrictorginfo-api-reference): `querydigitaldistrictorginfo-api-reference` +- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-upload-information): `obtain-upload-information` +- [获取文件下载信息](https://open.dingtalk.com/document/development/obtain-download-file-info): `obtain-download-file-info` +- [获取文章详情](https://open.dingtalk.com/document/development/get-article): `get-article` +- [获取日志接收人员列表](https://open.dingtalk.com/document/development/queries-log-sharing-personnel): `queries-log-sharing-personnel` +- [获取日志相关人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-log-related-personnel-by-type): `obtains-a-list-of-log-related-personnel-by-type` +- [获取日志统计数据](https://open.dingtalk.com/document/development/query-log-statistics): `query-log-statistics` +- [获取服务窗联系人信息](https://open.dingtalk.com/document/development/obtains-the-contact-information-of-the-service-window): `obtains-the-contact-information-of-the-service-window` +- [获取未活跃用户登录明细](https://open.dingtalk.com/document/development/obtains-the-logon-details-of-inactive-users): `obtains-the-logon-details-of-inactive-users` +- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/queries-the-inactive-users-or-active-users-under-an-enterprise): `queries-the-inactive-users-or-active-users-under-an-enterprise` +- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/query-data-of-inactive-users): `query-data-of-inactive-users` +- [获取权限列表](https://open.dingtalk.com/document/development/obtain-a-permission-list): `obtain-a-permission-list` +- [获取模板code](https://open.dingtalk.com/document/development/obtains-the-template-code-based-on-the-template-name): `obtains-the-template-code-based-on-the-template-name` +- [获取模板详情](https://open.dingtalk.com/document/development/query-template-details): `query-template-details` +- [获取流程定义](https://open.dingtalk.com/document/development/obtain-process-definition): `obtain-process-definition` +- [获取流程实例](https://open.dingtalk.com/document/development/obtain-process-instance): `obtain-process-instance` +- [获取流程节点按钮列表](https://open.dingtalk.com/document/development/obtain-a-list-of-process-node-buttons): `obtain-a-list-of-process-node-buttons` +- [获取流程设计的节点信息](https://open.dingtalk.com/document/development/obtain-the-information-about-the-nodes-in-process-design): `obtain-the-information-about-the-nodes-in-process-design` +- [获取班次摘要信息](https://open.dingtalk.com/document/development/enterprise-shift-query-in-batches): `enterprise-shift-query-in-batches` +- [获取班次详情](https://open.dingtalk.com/document/development/shift-query): `shift-query` +- [获取班级内学生的关系列表](https://open.dingtalk.com/document/development/queries-the-list-of-relationships): `queries-the-list-of-relationships` +- [获取班级圈动态列表](https://open.dingtalk.com/document/development/dynamic-list-opening-of-class-circle): `dynamic-list-opening-of-class-circle` +- [获取班级圈话题列表](https://open.dingtalk.com/document/development/obtain-a-topic-list-of-class-circles): `obtain-a-topic-list-of-class-circles` +- [获取用户创建文档数和创建文档人数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-dingtalk-documents-created-per-day-in-an): `queries-the-number-dingtalk-documents-created-per-day-in-an` +- [获取用户创建的AI助理列表](https://open.dingtalk.com/document/development/assistant-acquires-list-assistants-created-by-users): `assistant-acquires-list-assistants-created-by-users` +- [获取用户创建的填表模板](https://open.dingtalk.com/document/development/obtains-the-template-that-a-user-creates): `obtains-the-template-that-a-user-creates` +- [获取用户发送日志的概要信息](https://open.dingtalk.com/document/development/view-log-summary-data): `view-log-summary-data` +- [获取用户可查看的公告](https://open.dingtalk.com/document/development/list-the-user-s-announcement-list): `list-the-user-s-announcement-list` +- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/list-the-microapplications-visible-to-employees): `list-the-microapplications-visible-to-employees` +- [获取用户可见范围的AI助理列表](https://open.dingtalk.com/document/development/api-listvisibleassistant): `api-listvisibleassistant` +- [获取用户基本信息](https://open.dingtalk.com/document/development/queries-basic-user-information): `queries-basic-user-information` +- [获取用户待审批数量](https://open.dingtalk.com/document/development/obtain-the-number-of-tasks-to-be-approved-by-me): `obtain-the-number-of-tasks-to-be-approved-by-me` +- [获取用户授权的持久授权码](https://open.dingtalk.com/document/development/persistent-authorization-code): `persistent-authorization-code` +- [获取用户日志未读数](https://open.dingtalk.com/document/development/querying-the-employee-s-log-is-not-reading): `querying-the-employee-s-log-is-not-reading` +- [获取用户月活跃数据](https://open.dingtalk.com/document/development/retrieves-the-user-s-monthly-active-data): `retrieves-the-user-s-monthly-active-data` +- [获取用户版本分布情况](https://open.dingtalk.com/document/development/queries-the-distribution-of-user-versions): `queries-the-distribution-of-user-versions` +- [获取用户签到记录](https://open.dingtalk.com/document/development/obtain-the-check-in-records-of-multiple-users): `obtain-the-check-in-records-of-multiple-users` +- [获取用户考勤组](https://open.dingtalk.com/document/development/queries-a-user-attendance-group): `queries-a-user-attendance-group` +- [获取申请单列表](https://open.dingtalk.com/document/development/search-enterprise-approval-form-data): `search-enterprise-approval-form-data` +- [获取申请单详情](https://open.dingtalk.com/document/development/obtains-the-detailed-data-of-a-single-request): `obtains-the-detailed-data-of-a-single-request` +- [获取离职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-company-turnover-list): `intelligent-personnel-query-company-turnover-list` +- [获取空间信息](https://open.dingtalk.com/document/development/obtain-space-information): `obtain-space-information` +- [获取第三方个人应用的access_token](https://open.dingtalk.com/document/development/obtain-personal-application): `obtain-personal-application` +- [获取管理员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-administrators): `obtains-a-list-of-administrators` +- [获取管理员列表](https://open.dingtalk.com/document/development/query-the-administrator-list): `query-the-administrator-list` +- [获取管理员的应用管理权限](https://open.dingtalk.com/document/development/obtains-the-administrator-s-microapplication-management-permission): `obtains-the-administrator-s-microapplication-management-permission` +- [获取管理员通讯录权限范围](https://open.dingtalk.com/document/development/query-permissions-of-the-administrator-address-book): `query-permissions-of-the-administrator-address-book` +- [获取考勤报表列值](https://open.dingtalk.com/document/development/queries-the-column-value-of-the-attendance-report): `queries-the-column-value-of-the-attendance-report` +- [获取考勤报表列定义](https://open.dingtalk.com/document/development/queries-the-enterprise-attendance-report-column): `queries-the-enterprise-attendance-report-column` +- [获取考勤组详情](https://open.dingtalk.com/document/development/query-a-single-attendance-group): `query-a-single-attendance-group` +- [获取联系人的元数据](https://open.dingtalk.com/document/development/gets-the-metadata-description-of-a-crm-contact-object): `gets-the-metadata-description-of-a-crm-contact-object` +- [获取自定义对象的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-custom-object): `get-metadata-description-of-crm-custom-object` +- [获取花名册元数据](https://open.dingtalk.com/document/development/intelligent-personnel-roster-metadata-query): `intelligent-personnel-roster-metadata-query` +- [获取花名册字段组详情](https://open.dingtalk.com/document/development/get-roster-field-group-details): `get-roster-field-group-details` +- [获取视频会议详情](https://open.dingtalk.com/document/development/get-details-of-the-video-conference): `get-details-of-the-video-conference` +- [获取视频直播明细列表](https://open.dingtalk.com/document/development/queries-the-details-list-of-apsaravideo-live): `queries-the-details-list-of-apsaravideo-live` +- [获取视频直播观看人员列表](https://open.dingtalk.com/document/development/query-users-of-apsaravideo-live): `query-users-of-apsaravideo-live` +- [获取角色列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprise-roles): `obtains-a-list-of-enterprise-roles` +- [获取角色组列表](https://open.dingtalk.com/document/development/obtains-the-role-group-information): `obtains-the-role-group-information` +- [获取角色详情](https://open.dingtalk.com/document/development/queries-role-details): `queries-role-details` +- [获取课堂明细数据](https://open.dingtalk.com/document/development/obtain-course-detail-data): `obtain-course-detail-data` +- [获取课堂概要数据](https://open.dingtalk.com/document/development/get-course-summary-data): `get-course-summary-data` +- [获取课程列表](https://open.dingtalk.com/document/development/get-course-list): `get-course-list` +- [获取课程参与方列表](https://open.dingtalk.com/document/development/get-a-list-of-course-participants): `get-a-list-of-course-participants` +- [获取课程详情](https://open.dingtalk.com/document/development/get-course-details): `get-course-details` +- [获取跟进记录对象的元数据](https://open.dingtalk.com/document/development/obtains-the-metadata-description-of-the-crm-follow-up-record-object): `obtains-the-metadata-description-of-the-crm-follow-up-record-object` +- [获取通讯录权限范围](https://open.dingtalk.com/document/development/obtain-corpsecret-authorization-scope): `obtain-corpsecret-authorization-scope` +- [获取部门下人员列表](https://open.dingtalk.com/document/development/obtains-the-list-of-people-under-a-department): `obtains-the-list-of-people-under-a-department` +- [获取部门企业账号用户详情](https://open.dingtalk.com/document/development/queries-account-details): `queries-account-details` +- [获取部门列表](https://open.dingtalk.com/document/development/obtain-the-department-list): `obtain-the-department-list` +- [获取部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-industry-departments): `obtains-a-list-of-industry-departments` +- [获取部门列表](https://open.dingtalk.com/document/development/obtains-the-department-node-list): `obtains-the-department-node-list` +- [获取部门列表](https://open.dingtalk.com/document/development/user-management-acquires-the-list-departments): `user-management-acquires-the-list-departments` +- [获取部门扩展信息](https://open.dingtalk.com/document/development/obtain-department-extension-information): `obtain-department-extension-information` +- [获取部门用户userid列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employee-ids-by-department-id): `obtain-the-list-of-employee-ids-by-department-id` +- [获取部门用户userid列表](https://open.dingtalk.com/document/development/query-the-list-of-department-userids): `query-the-list-of-department-userids` +- [获取部门用户基础信息](https://open.dingtalk.com/document/development/obtain-the-basic-information-of-department-users): `obtain-the-basic-information-of-department-users` +- [获取部门用户签到记录](https://open.dingtalk.com/document/development/get-check-in-data): `get-check-in-data` +- [获取部门用户详情](https://open.dingtalk.com/document/development/obtain-department-members-details): `obtain-department-members-details` +- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-department-user-details): `queries-department-user-details` +- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-the-complete-information-of-a-department-user): `queries-the-complete-information-of-a-department-user` +- [获取部门的扩展字段定义](https://open.dingtalk.com/document/development/gets-the-extended-field-definition-of-a-department): `gets-the-extended-field-definition-of-a-department` +- [获取部门详情](https://open.dingtalk.com/document/development/industry-address-book-api-for-obtaining-department-information): `industry-address-book-api-for-obtaining-department-information` +- [获取部门详情](https://open.dingtalk.com/document/development/obtains-queries-department-details): `obtains-queries-department-details` +- [获取部门详情](https://open.dingtalk.com/document/development/queries-department-details): `queries-department-details` +- [获取部门详情](https://open.dingtalk.com/document/development/query-department-details0-v2): `query-department-details0-v2` +- [获取问答明细](https://open.dingtalk.com/document/development/api-getaskdetail): `api-getaskdetail` +- [获得企业创建日志相关信息(组织维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-organization): `obtains-information-about-a-created-enterprise-log-from-the-organization` +- [获得企业创建日志相关信息(部门维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-department): `obtains-information-about-a-created-enterprise-log-from-the-department` +- [获得用户创建文档数和创建文档人数(部门维度)](https://open.dingtalk.com/document/development/obtain-departmental-dimensions-documents-created-people-creating): `obtain-departmental-dimensions-documents-created-people-creating` +- [获得组织维度日程相关信息](https://open.dingtalk.com/document/development/queries-the-number-of-people-who-have-created-an-event): `queries-the-number-of-people-who-have-created-an-event` +- [解绑设备](https://open.dingtalk.com/document/development/unbind-a-smart-hardware-device): `unbind-a-smart-hardware-device` +- [计算请假时长](https://open.dingtalk.com/document/development/calculate-leave-duration): `calculate-leave-duration` +- [设备入会](https://open.dingtalk.com/document/development/equipment-membership): `equipment-membership` +- [设备账号向目标用户发送DING消息](https://open.dingtalk.com/document/development/device-publishing): `device-publishing` +- [设定角色成员管理范围](https://open.dingtalk.com/document/development/update-role-member-management-department-scope): `update-role-member-management-department-scope` +- [设置禁止群成员私聊](https://open.dingtalk.com/document/development/set-private-chat): `set-private-chat` +- [还原回收站文件(夹)](https://open.dingtalk.com/document/development/restore-recycle-bin-files-folder): `restore-recycle-bin-files-folder` +- [通知审批撤销](https://open.dingtalk.com/document/development/notify-the-attendance-to-modify-the-punch-result-when-the): `notify-the-attendance-to-modify-the-punch-result-when-the` +- [通知审批通过](https://open.dingtalk.com/document/development/notice-of-approval): `notice-of-approval` +- [通知换班通过](https://open.dingtalk.com/document/development/shift-change-operation-after-approval): `shift-change-operation-after-approval` +- [通知授权结果](https://open.dingtalk.com/document/development/notify-the-authorization-result): `notify-the-authorization-result` +- [通知补卡通过](https://open.dingtalk.com/document/development/make-up-the-card-after-approval): `make-up-the-card-after-approval` +- [通过免登码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-userid-of-a-user-by-using-the-log-free): `obtain-the-userid-of-a-user-by-using-the-log-free` +- [通过免登码获取用户信息(不推荐)](https://open.dingtalk.com/document/development/get-user-userid-through-login-free-code): `get-user-userid-through-login-free-code` +- [通过调用者unionId获取激活码](https://open.dingtalk.com/document/development/obtain-the-activation-code-by-calling-the-union-id-of): `obtain-the-activation-code-by-calling-the-union-id-of` +- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/query-form-instances-using-advanced-search-conditions): `query-form-instances-using-advanced-search-conditions` +- [邀请其他组织企业账号加入](https://open.dingtalk.com/document/development/invite-other-organization-specific-accounts-to-join): `invite-other-organization-specific-accounts-to-join` +- [配置发票适用人群](https://open.dingtalk.com/document/development/configure-invoice-users): `configure-invoice-users` +- [重新授权未激活应用的企业](https://open.dingtalk.com/document/development/re-authorize-enterprises-whose-applications-are-not-activated): `re-authorize-enterprises-whose-applications-are-not-activated` +- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-for-a-single-schedule): `sign-in-for-a-single-schedule` +- [钉钉文本翻译](https://open.dingtalk.com/document/development/dingtalk-translation): `dingtalk-translation` +- [静态推荐数据同步](https://open.dingtalk.com/document/development/statically-recommended-data-synchronization): `statically-recommended-data-synchronization` +- [预计算时长](https://open.dingtalk.com/document/development/calculate-duration-based-on-attendance-scheduling): `calculate-duration-based-on-attendance-scheduling` +- [验证激活结果](https://open.dingtalk.com/document/development/verify-activation-results): `verify-activation-results` + +> 已知 Gap:7 条 slug 在 development 段为空壳(<50000B),可能属别的 section(如 solution),待补正确 section,本目录暂不收录。 + + + +## OpenAPI Specs + +钉钉对外暂无机器可读 openapi.json:`TODO(gap #7)`,待平台侧提供,不伪造。 diff --git a/skills/dingtalk-open-platform/references/api-catalog.md b/skills/dingtalk-open-platform/references/api-catalog.md new file mode 100644 index 00000000..e8b505c2 --- /dev/null +++ b/skills/dingtalk-open-platform/references/api-catalog.md @@ -0,0 +1,1739 @@ +# 开放平台 API 文档目录(全量有效链接) + +开放平台官方文档的**全量有效链接目录**,供本 skill 在发现阶段离线检索接口文档。 + +- URL 模板:`https://open.dingtalk.com/document/development/`(`` 来自真实爬取页面)。 +- 有效性:经内容长度校验(真实页 ≥50KB,空壳 <50KB)后收录;统计见下方生成区块首行。 +- 归类:按 8 能力域(doc_page_group)+ 接口 namespace / CSV 功能分类;无法归类入「其它 / 未归类」。 +- 与本 skill [llm.md](../llm.md) **同源同步**,由 `scripts/build_llm_md.py` 生成,勿手改区块内容。 +- 用法:按能力域 → 功能子类定位 `- [中文标题](URL): slug`,拿到 URL 后浏览文档,再用 `dws api` / `dws devapp` 调用(见 [api-invocation.md](api-invocation.md))。 + + + + +> 全量 slug **1520** 条;有效(≥50000B) **1513** 条,无效(空壳) **7** 条;本目录收录 **1513** 条真实文档链接。 + +## 应用开发(642 条) + +二级 Tab:开发指南 / 服务端API / 客户端JSAPI / 事件订阅 / 钉钉CLI / 开发工具 / 平台服务(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 钉钉文档(71 条) + +- [修改知识库文档成员权限](https://open.dingtalk.com/document/development/update-team-space-document-user-permissions): `update-team-space-document-user-permissions` +- [创建工作表](https://open.dingtalk.com/document/development/create-a-worksheet): `create-a-worksheet` +- [创建快捷方式](https://open.dingtalk.com/document/development/api-createshortcut): `api-createshortcut` +- [创建条件格式规则](https://open.dingtalk.com/document/development/create-conditional-formatting-rules): `create-conditional-formatting-rules` +- [创建浮动图片](https://open.dingtalk.com/document/development/api-createfloatimage): `api-createfloatimage` +- [创建知识库文档](https://open.dingtalk.com/document/development/create-team-space-document): `create-team-space-document` +- [创建筛选](https://open.dingtalk.com/document/development/api-createfilter): `api-createfilter` +- [创建筛选视图](https://open.dingtalk.com/document/development/api-createfilterview): `api-createfilterview` +- [删除下拉列表](https://open.dingtalk.com/document/development/delete-drop-down-list): `delete-drop-down-list` +- [删除列](https://open.dingtalk.com/document/development/delete-column): `delete-column` +- [删除块元素](https://open.dingtalk.com/document/development/api-docdeleteblock): `api-docdeleteblock` +- [删除工作表](https://open.dingtalk.com/document/development/delete-classic-workbooks): `delete-classic-workbooks` +- [删除浮动图片](https://open.dingtalk.com/document/development/api-deletefloatimage): `api-deletefloatimage` +- [删除知识库成员](https://open.dingtalk.com/document/development/delete-team-space-user-permissions): `delete-team-space-user-permissions` +- [删除知识库文档](https://open.dingtalk.com/document/development/delete-team-space-documents): `delete-team-space-documents` +- [删除知识库文档成员](https://open.dingtalk.com/document/development/delete-team-space-document-permissions): `delete-team-space-document-permissions` +- [删除筛选](https://open.dingtalk.com/document/development/api-deletefilter): `api-deletefilter` +- [删除筛选条件](https://open.dingtalk.com/document/development/api-clearfiltercriteria): `api-clearfiltercriteria` +- [删除筛选视图](https://open.dingtalk.com/document/development/api-deletefilterview): `api-deletefilterview` +- [删除筛选视图条件](https://open.dingtalk.com/document/development/api-clearfilterviewcriteria): `api-clearfilterviewcriteria` +- [删除行](https://open.dingtalk.com/document/development/delete-row): `delete-row` +- [合并单元格](https://open.dingtalk.com/document/development/merge-cells): `merge-cells` +- [在段落末尾追加文本](https://open.dingtalk.com/document/development/api-docappendtext): `api-docappendtext` +- [在段落末尾追加行内元素](https://open.dingtalk.com/document/development/api-docappendparagraph): `api-docappendparagraph` +- [复制文档](https://open.dingtalk.com/document/development/api-copydoc): `api-copydoc` +- [工作表中追加行](https://open.dingtalk.com/document/development/append-line): `append-line` +- [批量设置列宽](https://open.dingtalk.com/document/development/api-setcolumnswidth): `api-setcolumnswidth` +- [批量设置行高](https://open.dingtalk.com/document/development/api-setrowsheight): `api-setrowsheight` +- [指定列左侧插入若干列](https://open.dingtalk.com/document/development/insert-column-before-column): `insert-column-before-column` +- [指定行上方插入若干行](https://open.dingtalk.com/document/development/insert-rows-before-rows): `insert-rows-before-rows` +- [插入下拉列表](https://open.dingtalk.com/document/development/insert-drop-down-list): `insert-drop-down-list` +- [插入内容](https://open.dingtalk.com/document/development/api-insertcontent): `api-insertcontent` +- [插入块元素](https://open.dingtalk.com/document/development/api-docinsertblocks): `api-docinsertblocks` +- [新建知识库](https://open.dingtalk.com/document/development/create-a-team-space): `create-a-team-space` +- [更新单元格区域](https://open.dingtalk.com/document/development/update-cell-properties): `update-cell-properties` +- [更新块元素](https://open.dingtalk.com/document/development/api-docblocksmodify): `api-docblocksmodify` +- [更新工作表](https://open.dingtalk.com/document/development/update-worksheet): `update-worksheet` +- [更新浮动图片](https://open.dingtalk.com/document/development/api-updatefloatimage): `api-updatefloatimage` +- [更新知识库成员权限](https://open.dingtalk.com/document/development/update-team-space-user-permissions): `update-team-space-user-permissions` +- [更新筛选](https://open.dingtalk.com/document/development/api-updatefilter): `api-updatefilter` +- [更新筛选视图](https://open.dingtalk.com/document/development/api-updatefilterview): `api-updatefilterview` +- [查找工作表中的单元格](https://open.dingtalk.com/document/development/find-the-next-eligible-cell): `find-the-next-eligible-cell` +- [查找所有符合条件的单元格](https://open.dingtalk.com/document/development/find-all-matching-cells): `find-all-matching-cells` +- [查询块元素](https://open.dingtalk.com/document/development/api-docblocksquery): `api-docblocksquery` +- [查询用户有权限的知识库列表](https://open.dingtalk.com/document/development/querying-the-list-of-user-team-spaces): `querying-the-list-of-user-team-spaces` +- [查询知识库下的目录结构](https://open.dingtalk.com/document/development/query-the-directory-tree-in-the-knowledge-base): `query-the-directory-tree-in-the-knowledge-base` +- [查询知识库信息](https://open.dingtalk.com/document/development/query-team-space): `query-team-space` +- [查询知识库节点信息](https://open.dingtalk.com/document/development/query-knowledge-base-node-information): `query-knowledge-base-node-information` +- [根据 dentryUuid 获取 spaceId](https://open.dingtalk.com/document/development/api-getdentryidbyuuid): `api-getdentryidbyuuid` +- [添加知识库成员](https://open.dingtalk.com/document/development/add-permissions-for-team-space-members): `add-permissions-for-team-space-members` +- [添加知识库文档成员](https://open.dingtalk.com/document/development/add-workspace-document-user-permissions): `add-workspace-document-user-permissions` +- [清除单元格区域内所有内容](https://open.dingtalk.com/document/development/clear-all): `clear-all` +- [清除单元格区域内数据](https://open.dingtalk.com/document/development/clear-cell-data): `clear-cell-data` +- [知识库转交所有者](https://open.dingtalk.com/document/development/api-handoveryworkspace): `api-handoveryworkspace` +- [筛选排序](https://open.dingtalk.com/document/development/api-sortfilter): `api-sortfilter` +- [置顶知识库](https://open.dingtalk.com/document/development/api-pinspace): `api-pinspace` +- [获取 dentryUuid 信息](https://open.dingtalk.com/document/development/api-getuuidbydentryid): `api-getuuidbydentryid` +- [获取任务状态](https://open.dingtalk.com/document/development/api-gettaskinfo): `api-gettaskinfo` +- [获取单元格区域](https://open.dingtalk.com/document/development/get-cell-properties): `get-cell-properties` +- [获取工作表](https://open.dingtalk.com/document/development/obtain-worksheet-properties): `obtain-worksheet-properties` +- [获取所有工作表](https://open.dingtalk.com/document/development/obtain-all-worksheets): `obtain-all-worksheets` +- [获取筛选](https://open.dingtalk.com/document/development/api-getfilter): `api-getfilter` +- [获取筛选视图列表](https://open.dingtalk.com/document/development/api-getfilterviews): `api-getfilterviews` +- [获取资源上传信息](https://open.dingtalk.com/document/development/api-getresourceuploadinfo): `api-getresourceuploadinfo` +- [覆写文档(个人授权)](https://open.dingtalk.com/document/development/api-docupdatecontent): `api-docupdatecontent` +- [覆写文档(应用授权)](https://open.dingtalk.com/document/development/api-doc-updatecontent): `api-doc-updatecontent` +- [设置列隐藏或显示](https://open.dingtalk.com/document/development/set-column-visibility): `set-column-visibility` +- [设置筛选条件](https://open.dingtalk.com/document/development/api-setfiltercriteria): `api-setfiltercriteria` +- [设置筛选视图条件](https://open.dingtalk.com/document/development/api-setfilterviewcriteria): `api-setfilterviewcriteria` +- [设置自动行高](https://open.dingtalk.com/document/development/set-row-height-automatically): `set-row-height-automatically` +- [设置行隐藏或显示](https://open.dingtalk.com/document/development/set-row-visibility): `set-row-visibility` + +### 存储(53 条) + +- [修改权限](https://open.dingtalk.com/document/development/modify-permissions-file): `modify-permissions-file` +- [修改权限](https://open.dingtalk.com/document/development/modify-storage-permissions): `modify-storage-permissions` +- [初始化文件分片上传](https://open.dingtalk.com/document/development/initialize-a-multipart-upload-object): `initialize-a-multipart-upload-object` +- [删除回收项](https://open.dingtalk.com/document/development/delete-recycle-item): `delete-recycle-item` +- [删除文件或文件夹](https://open.dingtalk.com/document/development/delete-a-file-or-folder): `delete-a-file-or-folder` +- [删除文件或文件夹的应用属性](https://open.dingtalk.com/document/development/delete-file-app-attribute): `delete-file-app-attribute` +- [删除权限](https://open.dingtalk.com/document/development/delete-permissions-file): `delete-permissions-file` +- [删除权限](https://open.dingtalk.com/document/development/delete-storage-permissions): `delete-storage-permissions` +- [取消订阅文件变更事件](https://open.dingtalk.com/document/development/unsubscribe-from-file-change-events): `unsubscribe-from-file-change-events` +- [复制文件或文件夹](https://open.dingtalk.com/document/development/copy-an-object): `copy-an-object` +- [恢复文件历史版本](https://open.dingtalk.com/document/development/restore-previous-versions-of-files): `restore-previous-versions-of-files` +- [批量删除回收项](https://open.dingtalk.com/document/development/batch-delete-recycle-items): `batch-delete-recycle-items` +- [批量删除文件或文件夹](https://open.dingtalk.com/document/development/delete-files-or-folders-in-bulk): `delete-files-or-folders-in-bulk` +- [批量复制文件或文件夹](https://open.dingtalk.com/document/development/copy-files-or-folders-in-bulk): `copy-files-or-folders-in-bulk` +- [批量移动文件或文件夹](https://open.dingtalk.com/document/development/bulk-move-files-or-folders): `bulk-move-files-or-folders` +- [批量获取文件或文件夹信息](https://open.dingtalk.com/document/development/get-file-or-folder-information-in-bulk): `get-file-or-folder-information-in-bulk` +- [批量获取文件缩略图](https://open.dingtalk.com/document/development/get-file-thumbnails-in-bulk): `get-file-thumbnails-in-bulk` +- [批量还原回收项](https://open.dingtalk.com/document/development/batch-restore-recycled-items): `batch-restore-recycled-items` +- [提交文件](https://open.dingtalk.com/document/development/submit-documents): `submit-documents` +- [提交文件](https://open.dingtalk.com/document/development/submittal-file): `submittal-file` +- [搜索文件](https://open.dingtalk.com/document/development/search-for-files): `search-for-files` +- [搜索知识库](https://open.dingtalk.com/document/development/search-knowledge-base): `search-knowledge-base` +- [更新文件或文件夹的应用属性](https://open.dingtalk.com/document/development/update-file-application-properties): `update-file-application-properties` +- [添加文件夹](https://open.dingtalk.com/document/development/add-folder): `add-folder` +- [添加权限](https://open.dingtalk.com/document/development/add-permissions-file): `add-permissions-file` +- [添加权限](https://open.dingtalk.com/document/development/add-storage-permissions): `add-storage-permissions` +- [添加空间](https://open.dingtalk.com/document/development/add-space): `add-space` +- [清空回收站](https://open.dingtalk.com/document/development/empty-the-recycle-bin): `empty-the-recycle-bin` +- [知识库下载文件](https://open.dingtalk.com/document/development/knowledge-base-download-file): `knowledge-base-download-file` +- [移动文件或文件夹](https://open.dingtalk.com/document/development/move-a-file-or-folder): `move-a-file-or-folder` +- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-storage-related-information): `obtain-enterprise-storage-related-information` +- [获取回收站信息](https://open.dingtalk.com/document/development/obtain-information-about-the-recycle-bin): `obtain-information-about-the-recycle-bin` +- [获取回收项信息](https://open.dingtalk.com/document/development/obtain-recycling-item-information): `obtain-recycling-item-information` +- [获取回收项列表](https://open.dingtalk.com/document/development/gets-the-list-of-recycle-items): `gets-the-list-of-recycle-items` +- [获取存储中异步任务信息](https://open.dingtalk.com/document/development/get-the-asynchronous-task-information-in-storage): `get-the-asynchronous-task-information-in-storage` +- [获取应用信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-current-application): `obtains-the-information-about-the-current-application` +- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-file-upload-informations): `obtain-file-upload-informations` +- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-storage-upload-information): `obtain-storage-upload-information` +- [获取文件下载信息](https://open.dingtalk.com/document/development/obtains-the-download-information-about-a-file): `obtains-the-download-information-about-a-file` +- [获取文件分片上传信息](https://open.dingtalk.com/document/development/obtains-the-information-about-multipart-uploads-of-an-object): `obtains-the-information-about-multipart-uploads-of-an-object` +- [获取文件或文件夹信息](https://open.dingtalk.com/document/development/obtain-file-or-folder-information): `obtain-file-or-folder-information` +- [获取文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-files-or-folders): `get-a-list-of-files-or-folders` +- [获取文件版本列表](https://open.dingtalk.com/document/development/obtains-a-list-of-file-versions): `obtains-a-list-of-file-versions` +- [获取文件预览或编辑信息](https://open.dingtalk.com/document/development/obtains-the-object-preview-or-editing-information): `obtains-the-object-preview-or-editing-information` +- [获取权限列表](https://open.dingtalk.com/document/development/get-permission-list): `get-permission-list` +- [获取权限列表](https://open.dingtalk.com/document/development/get-the-storage-permission-list): `get-the-storage-permission-list` +- [获取权限继承模式](https://open.dingtalk.com/document/development/get-permission-inheritance-mode): `get-permission-inheritance-mode` +- [获取空间下所有文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-all-files-or-folders-under-a): `get-a-list-of-all-files-or-folders-under-a` +- [获取空间信息](https://open.dingtalk.com/document/development/get-space-information): `get-space-information` +- [订阅文件变更事件](https://open.dingtalk.com/document/development/subscribe-to-file-change-events): `subscribe-to-file-change-events` +- [设置权限继承模式](https://open.dingtalk.com/document/development/set-permission-inheritance-mode): `set-permission-inheritance-mode` +- [还原回收项](https://open.dingtalk.com/document/development/restore-recycle-items): `restore-recycle-items` +- [重命名文件或文件夹](https://open.dingtalk.com/document/development/rename-a-file-or-folder): `rename-a-file-or-folder` + +### 项目管理(50 条) + +- [任务迁移至回收站](https://open.dingtalk.com/document/development/archive-tasks): `archive-tasks` +- [创建实际工时](https://open.dingtalk.com/document/development/create-actual-work): `create-actual-work` +- [创建或更新项目概览中自定义字段值](https://open.dingtalk.com/document/development/create-or-update-field-values-project-overview): `create-or-update-field-values-project-overview` +- [创建自由任务](https://open.dingtalk.com/document/development/create-a-free-task): `create-a-free-task` +- [创建计划工时](https://open.dingtalk.com/document/development/create-planned-work): `create-planned-work` +- [创建项目](https://open.dingtalk.com/document/development/create-project): `create-project` +- [创建项目任务](https://open.dingtalk.com/document/development/create-a-project-task): `create-a-project-task` +- [删除任务](https://open.dingtalk.com/document/development/delete-task): `delete-task` +- [删除项目成员](https://open.dingtalk.com/document/development/delete-project-members): `delete-project-members` +- [增加或删除自由任务的参与者](https://open.dingtalk.com/document/development/change-task-participant): `change-task-participant` +- [归档项目](https://open.dingtalk.com/document/development/archiving-project): `archiving-project` +- [恢复项目归档](https://open.dingtalk.com/document/development/cancel-project-archiving): `cancel-project-archiving` +- [批量获取自由任务详情](https://open.dingtalk.com/document/development/obtains-details-about-multiple-free-tasks): `obtains-details-about-multiple-free-tasks` +- [搜索任务工作流状态](https://open.dingtalk.com/document/development/search-task-workflow-status): `search-task-workflow-status` +- [搜索企业项目模板](https://open.dingtalk.com/document/development/search-for-enterprise-custom-templates-by-project-template-name): `search-for-enterprise-custom-templates-by-project-template-name` +- [更新任务优先级](https://open.dingtalk.com/document/development/update-task-priority): `update-task-priority` +- [更新任务参与者](https://open.dingtalk.com/document/development/update-task-participants): `update-task-participants` +- [更新任务备注](https://open.dingtalk.com/document/development/update-task-notes): `update-task-notes` +- [更新任务工作流状态](https://open.dingtalk.com/document/development/update-task-workflow-status): `update-task-workflow-status` +- [更新任务开始时间](https://open.dingtalk.com/document/development/update-task-start-time): `update-task-start-time` +- [更新任务截止时间](https://open.dingtalk.com/document/development/update-task-deadline): `update-task-deadline` +- [更新任务执行者](https://open.dingtalk.com/document/development/update-task-performer): `update-task-performer` +- [更新任务标题](https://open.dingtalk.com/document/development/update-task-content): `update-task-content` +- [更新自由任务备注](https://open.dingtalk.com/document/development/update-free-task-notes): `update-free-task-notes` +- [更新自由任务截止时间](https://open.dingtalk.com/document/development/change-free-task-deadline): `change-free-task-deadline` +- [更新自由任务执行者](https://open.dingtalk.com/document/development/change-free-task-executor): `change-free-task-executor` +- [更新自由任务标题](https://open.dingtalk.com/document/development/change-free-task-title): `change-free-task-title` +- [更新自由任务状态](https://open.dingtalk.com/document/development/change-free-task-status): `change-free-task-status` +- [更新自由任务的优先级](https://open.dingtalk.com/document/development/change-free-task-priority): `change-free-task-priority` +- [更新项目任务的自定义字段值](https://open.dingtalk.com/document/development/update-task-custom-field-value): `update-task-custom-field-value` +- [更新项目所在的分组](https://open.dingtalk.com/document/development/update-project-grouping): `update-project-grouping` +- [查询任务分组](https://open.dingtalk.com/document/development/query-task-grouping): `query-task-grouping` +- [查询任务工作流](https://open.dingtalk.com/document/development/query-task-workflow): `query-task-workflow` +- [查询优先级列表](https://open.dingtalk.com/document/development/query-a-priority-list): `query-a-priority-list` +- [查询员工可见的项目分组](https://open.dingtalk.com/document/development/query-available-project-groups): `query-available-project-groups` +- [查询用户任务信息列表](https://open.dingtalk.com/document/development/querying-user-tasks): `querying-user-tasks` +- [查询项目](https://open.dingtalk.com/document/development/query-enterprise-all-projects): `query-enterprise-all-projects` +- [查询项目中的任务](https://open.dingtalk.com/document/development/query-tasks-in-a-project): `query-tasks-in-a-project` +- [查询项目状态](https://open.dingtalk.com/document/development/query-project-status): `query-project-status` +- [根据userId获取Teambition项目用户ID](https://open.dingtalk.com/document/development/obtain-dingtalk-teambition-user-id-based-on-userid): `obtain-dingtalk-teambition-user-id-based-on-userid` +- [根据项目模板创建项目](https://open.dingtalk.com/document/development/create-a-project-from-a-project-template): `create-a-project-from-a-project-template` +- [添加任务的关联内容](https://open.dingtalk.com/document/development/create-a-linked-object-associated-with-a-task): `create-a-linked-object-associated-with-a-task` +- [添加项目成员](https://open.dingtalk.com/document/development/add-project-members): `add-project-members` +- [获取Teambition项目企业ID](https://open.dingtalk.com/document/development/obtain-the-teambition-enterprise-id): `obtain-the-teambition-enterprise-id` +- [获取任务列表](https://open.dingtalk.com/document/development/get-task-list): `get-task-list` +- [获取任务详情](https://open.dingtalk.com/document/development/get-task-details): `get-task-details` +- [获取用户加入的项目](https://open.dingtalk.com/document/development/get-projects-joined-by-users): `get-projects-joined-by-users` +- [获取自由任务详情](https://open.dingtalk.com/document/development/queries-free-task-details): `queries-free-task-details` +- [获取项目成员](https://open.dingtalk.com/document/development/get-project-members): `get-project-members` +- [项目放入回收站](https://open.dingtalk.com/document/development/items-in-recycle-bin): `items-in-recycle-bin` + +### 通讯录(49 条) + +- [企业账号修改钉钉号](https://open.dingtalk.com/document/development/api-changedingtalkid): `api-changedingtalkid` +- [企业账号转交主管理员(创建者)](https://open.dingtalk.com/document/development/transfer-exclusive-account-to-main-administrator-creator): `transfer-exclusive-account-to-main-administrator-creator` +- [停用企业账号](https://open.dingtalk.com/document/development/disable-an-exclusive-account): `disable-an-exclusive-account` +- [创建上下游组织](https://open.dingtalk.com/document/development/create-a-cooperation-space): `create-a-cooperation-space` +- [删除用户属性可见性设置](https://open.dingtalk.com/document/development/delete-enterprise-employee-attribute-field-visibility-settings): `delete-enterprise-employee-attribute-field-visibility-settings` +- [删除通讯录隐藏设置](https://open.dingtalk.com/document/development/delete-hide-settings): `delete-hide-settings` +- [删除限制查看通讯录设置](https://open.dingtalk.com/document/development/delete-visible-restrictions): `delete-visible-restrictions` +- [启用企业账号](https://open.dingtalk.com/document/development/enable-a-dedicated-account): `enable-a-dedicated-account` +- [异步转译通讯录ID](https://open.dingtalk.com/document/development/asynchronous-address-book-file-content-translation): `asynchronous-address-book-file-content-translation` +- [强制登出企业账号](https://open.dingtalk.com/document/development/force-logout-from-dedicated-account): `force-logout-from-dedicated-account` +- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/apply-for-batch-addition-through-upstream-and-downstream-organizations): `apply-for-batch-addition-through-upstream-and-downstream-organizations` +- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/batch-through-the-application-of-partner-organizations-to-join-contact): `batch-through-the-application-of-partner-organizations-to-join-contact` +- [授权企业账号可加入多组织](https://open.dingtalk.com/document/development/authorize-a-dedicated-account-to-join-multiple-organizations): `authorize-a-dedicated-account-to-join-multiple-organizations` +- [授权其他组织查看本组织的企业账号信息](https://open.dingtalk.com/document/development/api-orgaccountmobilevisibleinotherorg): `api-orgaccountmobilevisibleinotherorg` +- [搜索用户userId](https://open.dingtalk.com/document/development/address-book-search-user-id): `address-book-search-user-id` +- [搜索部门ID](https://open.dingtalk.com/document/development/address-book-search-department-id): `address-book-search-department-id` +- [新增或修改限制查看通讯录设置](https://open.dingtalk.com/document/development/add-or-modify-visibility-settings-for-address-book-restrictions): `add-or-modify-visibility-settings-for-address-book-restrictions` +- [新增或更新通讯录隐藏设置](https://open.dingtalk.com/document/development/update-address-book-hide-settings): `update-address-book-hide-settings` +- [更新伙伴组织在上下游组织内的属性信息](https://open.dingtalk.com/document/development/update-properties-of-branches-in-alibaba-group-1): `update-properties-of-branches-in-alibaba-group-1` +- [更新分支组织在主干组织内的属性信息](https://open.dingtalk.com/document/development/updates-the-property-information-of-a-branch-organization-in-a): `updates-the-property-information-of-a-branch-organization-in-a` +- [查询企业账号拥有的组织](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-organization-that): `you-can-call-this-operation-to-query-the-organization-that` +- [查询企业账号状态](https://open.dingtalk.com/document/development/query-dedicated-account-status-1): `query-dedicated-account-status-1` +- [查询离职记录列表](https://open.dingtalk.com/document/development/query-the-details-of-employees-who-have-left-office): `query-the-details-of-employees-who-have-left-office` +- [根据原dingId查询迁移后的dingId](https://open.dingtalk.com/document/development/query-the-new-dingid-based-on-the-original-dingid): `query-the-new-dingid-based-on-the-original-dingid` +- [根据原unionId查询迁移后的unionId](https://open.dingtalk.com/document/development/the-union-id-that-you-want-to-query-you-can): `the-union-id-that-you-want-to-query-you-can` +- [根据手机号查询用户](https://open.dingtalk.com/document/development/query-users-by-phone-number): `query-users-by-phone-number` +- [根据迁移后的dingId查询原dingId](https://open.dingtalk.com/document/development/query-the-original-dingid-based-on-the-dingid-after-migration): `query-the-original-dingid-based-on-the-dingid-after-migration` +- [根据迁移后的unionId查询原unionId](https://open.dingtalk.com/document/development/query-the-original-union-id-based-on-the-union-id): `query-the-original-union-id-based-on-the-union-id` +- [获取上下游组织的邀请信息](https://open.dingtalk.com/document/development/obtain-the-invitation-information-of-a-cooperation-space): `obtain-the-invitation-information-of-a-cooperation-space` +- [获取上下级组织分支授权的数据](https://open.dingtalk.com/document/development/data-authorized-by-a-branch-of-an-associated-organization): `data-authorized-by-a-branch-of-an-associated-organization` +- [获取企业最新钉钉指数信息](https://open.dingtalk.com/document/development/queries-the-latest-dingtalk-index-information): `queries-the-latest-dingtalk-index-information` +- [获取企业认证信息](https://open.dingtalk.com/document/development/obtain-enterprise-authentication-information): `obtain-enterprise-authentication-information` +- [获取企业邀请信息](https://open.dingtalk.com/document/development/obtain-invitation-information): `obtain-invitation-information` +- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-the-list-of-sub-department-ids): `obtain-the-list-of-sub-department-ids` +- [获取异步转译任务结果](https://open.dingtalk.com/document/development/obtains-the-results-of-an-asynchronous-translation-task): `obtains-the-results-of-an-asynchronous-translation-task` +- [获取用户属性可见性设置](https://open.dingtalk.com/document/development/pull-hidden-property-field-for-enterprise-employees): `pull-hidden-property-field-for-enterprise-employees` +- [获取用户通讯录个人信息](https://open.dingtalk.com/document/development/dingtalk-retrieve-user-information): `dingtalk-retrieve-user-information` +- [获取用户高管模式设置](https://open.dingtalk.com/document/development/get-user-executive-mode-settings): `get-user-executive-mode-settings` +- [获取通讯录隐藏设置](https://open.dingtalk.com/document/development/obtains-the-hide-settings-of-the-address-book): `obtains-the-hide-settings-of-the-address-book` +- [获取部门用户基础信息](https://open.dingtalk.com/document/development/queries-the-simple-information-of-a-department-user): `queries-the-simple-information-of-a-department-user` +- [获取限制查看通讯录设置列表](https://open.dingtalk.com/document/development/gets-a-list-of-address-book-limit-visibility-settings): `gets-a-list-of-address-book-limit-visibility-settings` +- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-an-organization): `disassociate-an-organization` +- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-upstream-and-downstream-organizations): `disassociate-upstream-and-downstream-organizations` +- [设置伙伴组织在上下游组织内的可见范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-branch-in-the-group-1): `set-the-visible-range-of-the-branch-in-the-group-1` +- [设置分支组织在主干组织内的可见范围](https://open.dingtalk.com/document/development/sets-the-visible-range-of-branch-organizations-within-the-group): `sets-the-visible-range-of-branch-organizations-within-the-group` +- [设置用户属性可见性](https://open.dingtalk.com/document/development/add-or-update-the-hidden-settings-of-the-employee-property): `add-or-update-the-hidden-settings-of-the-employee-property` +- [设置部门可见性优先级](https://open.dingtalk.com/document/development/set-address-book-visibility-sub-department-settings-to-take-precedence): `set-address-book-visibility-sub-department-settings-to-take-precedence` +- [设置高管模式](https://open.dingtalk.com/document/development/update-executive-settings): `update-executive-settings` +- [通讯录userId排序](https://open.dingtalk.com/document/development/address-book-userid-sorting): `address-book-userid-sorting` + +### 电子签(41 条) + +- [ISV服务商数据初始化](https://open.dingtalk.com/document/development/offline-isv-service-provider-data-initialization): `offline-isv-service-provider-data-initialization` +- [e签宝数据初始化](https://open.dingtalk.com/document/development/isv-service-provider-data-initialization): `isv-service-provider-data-initialization` +- [创建签署流程](https://open.dingtalk.com/document/development/use-the-api-to-initiate-a-signature-process): `use-the-api-to-initiate-a-signature-process` +- [取消企业授权](https://open.dingtalk.com/document/development/cancel-enterprise-authorization): `cancel-enterprise-authorization` +- [取消企业授权](https://open.dingtalk.com/document/development/e-sign-has-revoked-the-enterprise-authorization): `e-sign-has-revoked-the-enterprise-authorization` +- [套餐转售---分润模式](https://open.dingtalk.com/document/development/package-resale-1-distribution-mode): `package-resale-1-distribution-mode` +- [套餐转售---底价结算模式](https://open.dingtalk.com/document/development/package-resale-2-reserve-price-settlement-mode): `package-resale-2-reserve-price-settlement-mode` +- [套餐转售(分润模式)](https://open.dingtalk.com/document/development/package-resale-profit-distribution-model-1): `package-resale-profit-distribution-model-1` +- [套餐转售(底价结算模式)](https://open.dingtalk.com/document/development/package-resale-base-price-settlement-mode-1): `package-resale-base-price-settlement-mode-1` +- [查询个人是否实名认证](https://open.dingtalk.com/document/development/query-personal-information): `query-personal-information` +- [查询企业是否实名](https://open.dingtalk.com/document/development/check-whether-enterprise-registered-realname-e-sign-treasure): `check-whether-enterprise-registered-realname-e-sign-treasure` +- [查询企业是否实名认证](https://open.dingtalk.com/document/development/query-enterprise-information): `query-enterprise-information` +- [查询套餐余量](https://open.dingtalk.com/document/development/query-package-balance): `query-package-balance` +- [查询用户是否实名](https://open.dingtalk.com/document/development/query-whether-a-user-has-a-real-name): `query-whether-a-user-has-a-real-name` +- [获取个人实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-user-s-real): `obtain-the-address-that-is-redirected-to-the-user-s-real` +- [获取企业e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-status-of-enterprise-e-sign-treasure-micro-application): `obtain-the-status-of-enterprise-e-sign-treasure-micro-application` +- [获取企业实名地址](https://open.dingtalk.com/document/development/obtain-enterprise-real-name-address): `obtain-enterprise-real-name-address` +- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-enterprise-console-address): `get-enterprise-console-address` +- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-the-address-enterprise-console-through-e-sign): `get-the-address-enterprise-console-through-e-sign` +- [获取企业的e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-current-status-of-the-company-s-e-sign-micro-application): `obtain-the-current-status-of-the-company-s-e-sign-micro-application` +- [获取发起签署任务地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-initiating-signing-task): `obtain-the-address-of-the-initiating-signing-task` +- [获取发起签署任务的地址](https://open.dingtalk.com/document/development/obtain-the-address-used-to-initiate-a-signed-task): `obtain-the-address-used-to-initiate-a-signed-task` +- [获取套餐余量](https://open.dingtalk.com/document/development/get-package-margin): `get-package-margin` +- [获取授权的页面地址](https://open.dingtalk.com/document/development/authurl): `authurl` +- [获取授权的页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-authorized-page): `obtain-the-address-of-the-authorized-page` +- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-file-upload-address-1): `obtain-the-file-upload-address-1` +- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-1): `obtain-the-upload-url-of-a-file-1` +- [获取文件详情](https://open.dingtalk.com/document/development/gets-the-file-details): `gets-the-file-details` +- [获取文件详情](https://open.dingtalk.com/document/development/query-file-details): `query-file-details` +- [获取流程任务合同列表](https://open.dingtalk.com/document/development/obtain-the-process-task-contract-list): `obtain-the-process-task-contract-list` +- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtain-the-process-task-print-approval-list-1): `obtain-the-process-task-print-approval-list-1` +- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtains-the-print-approval-list-for-process-tasks): `obtains-the-print-approval-list-for-process-tasks` +- [获取流程任务的所有合同列表](https://open.dingtalk.com/document/development/get-a-list-of-all-contracts-for-the-process-task): `get-a-list-of-all-contracts-for-the-process-task` +- [获取流程任务详情](https://open.dingtalk.com/document/development/obtain-the-task-details-of-the-corresponding-process): `obtain-the-task-details-of-the-corresponding-process` +- [获取流程的签署详情](https://open.dingtalk.com/document/development/get-the-details-of-process-signing): `get-the-details-of-process-signing` +- [获取流程签署详细信息](https://open.dingtalk.com/document/development/get-process-sign-off-details): `get-process-sign-off-details` +- [获取流程详细信息及操作记录](https://open.dingtalk.com/document/development/obtains-the-task-details): `obtains-the-task-details` +- [获取用户实名地址](https://open.dingtalk.com/document/development/obtain-personal-real-name-address): `obtain-personal-real-name-address` +- [获取签署人签署地址](https://open.dingtalk.com/document/development/get-signatory-address): `get-signatory-address` +- [获取签署人签署地址](https://open.dingtalk.com/document/development/obtain-signing-address-of-signatory-1): `obtain-signing-address-of-signatory-1` +- [获取跳转到企业实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-enterprise-s-real): `obtain-the-address-that-is-redirected-to-the-enterprise-s-real` + +### 消息会话(37 条) + +- [人与人会话中机器人发送互动卡片](https://open.dingtalk.com/document/development/send-dingtalk-interactive-cards-to-person-to-person-chat-sessions): `send-dingtalk-interactive-cards-to-person-to-person-chat-sessions` +- [修改钉钉客联互通群名称](https://open.dingtalk.com/document/development/modify-the-group-name): `modify-the-group-name` +- [修改钉钉客联互通群头像](https://open.dingtalk.com/document/development/modify-the-avatar-of-a-communication-group): `modify-the-avatar-of-a-communication-group` +- [关闭互动卡片吊顶](https://open.dingtalk.com/document/development/close-interactive-card-ceiling): `close-interactive-card-ceiling` +- [创建互通群](https://open.dingtalk.com/document/development/create-an-intercommunication-group): `create-an-intercommunication-group` +- [创建并开启互动卡片吊顶](https://open.dingtalk.com/document/development/send-group-helper-message): `send-group-helper-message` +- [创建店铺群](https://open.dingtalk.com/document/development/create-a-store-group): `create-a-store-group` +- [创建钉外两人群](https://open.dingtalk.com/document/development/create-two-people-outside-the-nail): `create-two-people-outside-the-nail` +- [创建钉钉客联两人互通群](https://open.dingtalk.com/document/development/creating-two-groups-of-people): `creating-two-groups-of-people` +- [创建钉钉客联普通互通群](https://open.dingtalk.com/document/development/create-common-group-new-version): `create-common-group-new-version` +- [创建钉钉客联钉外账号](https://open.dingtalk.com/document/development/create-bc-account-association): `create-bc-account-association` +- [发送轻量级互动卡片](https://open.dingtalk.com/document/development/send-lightweight-interactive-cards): `send-lightweight-interactive-cards` +- [发送钉钉互动卡片(高级版)](https://open.dingtalk.com/document/development/send-interactive-dynamic-cards-1): `send-interactive-dynamic-cards-1` +- [在钉钉客联互通群中使用机器人发送消息](https://open.dingtalk.com/document/development/group-robots-send-messages): `group-robots-send-messages` +- [在钉钉客联互通群中使用钉内账号发送消息](https://open.dingtalk.com/document/development/send-b2c-messages): `send-b2c-messages` +- [在钉钉客联互通群中使用钉外账号发送消息](https://open.dingtalk.com/document/development/send-c2b-messages): `send-c2b-messages` +- [批量查询跨钉两人互通群列表](https://open.dingtalk.com/document/development/queries-the-session-information-of-two-population-groups): `queries-the-session-information-of-two-population-groups` +- [批量设置企业群管理员](https://open.dingtalk.com/document/development/batch-setup-group-administrator): `batch-setup-group-administrator` +- [更换钉钉客联互通群群主](https://open.dingtalk.com/document/development/change-group-owner): `change-group-owner` +- [更新机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/update-the-robot-to-send-interactive-cards): `update-the-robot-to-send-interactive-cards` +- [更新群成员的群昵称](https://open.dingtalk.com/document/development/update-group-nicknames): `update-group-nicknames` +- [更新群管理员](https://open.dingtalk.com/document/development/update-group-administrators): `update-group-administrators` +- [更新钉钉互动卡片](https://open.dingtalk.com/document/development/update-dingtalk-interactive-cards): `update-dingtalk-interactive-cards` +- [机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/robots-send-interactive-cards): `robots-send-interactive-cards` +- [查询群内群模板机器人](https://open.dingtalk.com/document/development/search-group-scene-template-robot): `search-group-scene-template-robot` +- [查询群成员](https://open.dingtalk.com/document/development/query-group-members): `query-group-members` +- [查询群禁言状态](https://open.dingtalk.com/document/development/query-group-silence-status): `query-group-silence-status` +- [查询群简要信息](https://open.dingtalk.com/document/development/query-group-information): `query-group-information` +- [查询钉钉客联互通群成员列表](https://open.dingtalk.com/document/development/queries-the-group-member-list): `queries-the-group-member-list` +- [查询钉钉客联钉外账号未读消息数](https://open.dingtalk.com/document/development/querying-the-number-of-unread-messages-of-the-user): `querying-the-number-of-unread-messages-of-the-user` +- [添加钉钉客联互通群成员](https://open.dingtalk.com/document/development/add-a-group-member-1): `add-a-group-member-1` +- [移除钉钉客联互通群成员](https://open.dingtalk.com/document/development/remove-group-members): `remove-group-members` +- [获取群会话的OpenConversationId](https://open.dingtalk.com/document/development/obtain-group-openconversationid): `obtain-group-openconversationid` +- [获取钉钉客联H5页面地址](https://open.dingtalk.com/document/development/get-the-dingtalk-guest-group-session-address): `get-the-dingtalk-guest-group-session-address` +- [解散群聊](https://open.dingtalk.com/document/development/api-dsbandopenscenegroup): `api-dsbandopenscenegroup` +- [解散钉钉客联互通群](https://open.dingtalk.com/document/development/disband-bc-interconnection-group): `disband-bc-interconnection-group` +- [设置群成员禁言状态](https://open.dingtalk.com/document/development/set-group-members-access-control): `set-group-members-access-control` + +### 智能 CRM(34 条) + +- [创建个人或企业客户数据](https://open.dingtalk.com/document/development/add-crm-personal-customers): `add-crm-personal-customers` +- [创建客户群](https://open.dingtalk.com/document/development/create-a-customer-group): `create-a-customer-group` +- [创建客户群组](https://open.dingtalk.com/document/development/crm-create-group): `crm-create-group` +- [删除CRM自定义对象数据](https://open.dingtalk.com/document/development/delete-crm-custom-object-data): `delete-crm-custom-object-data` +- [删除个人或企业客户数据](https://open.dingtalk.com/document/development/delete-crm-personal-customer): `delete-crm-personal-customer` +- [发送服务窗单人消息](https://open.dingtalk.com/document/development/sends-a-single-message-from-the-service-window): `sends-a-single-message-from-the-service-window` +- [批量修改联系人数据](https://open.dingtalk.com/document/development/modify-contact-data-in-batches): `modify-contact-data-in-batches` +- [批量删除跟进记录数据](https://open.dingtalk.com/document/development/batch-delete-follow-up-record-data): `batch-delete-follow-up-record-data` +- [批量发送服务窗消息](https://open.dingtalk.com/document/development/batch-sending-of-service-window-messages): `batch-sending-of-service-window-messages` +- [批量新增个人或企业客户数据](https://open.dingtalk.com/document/development/add-multiple-relationship-data-in-batches): `add-multiple-relationship-data-in-batches` +- [批量新增联系人数据](https://open.dingtalk.com/document/development/add-contact-data-in-batches): `add-contact-data-in-batches` +- [批量新增跟进记录数据](https://open.dingtalk.com/document/development/batch-add-follow-up-record-data): `batch-add-follow-up-record-data` +- [批量更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-multiple-relational-data-tables-at-a-time): `update-multiple-relational-data-tables-at-a-time` +- [批量更新跟进记录数据](https://open.dingtalk.com/document/development/batch-update-follow-up-record-data): `batch-update-follow-up-record-data` +- [批量查询客户群](https://open.dingtalk.com/document/development/query-customer-groups-in-batches): `query-customer-groups-in-batches` +- [批量获取个人或企业客户数据](https://open.dingtalk.com/document/development/acquire-crm-individual-customers-in-batches): `acquire-crm-individual-customers-in-batches` +- [更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-crm-personal-customers): `update-crm-personal-customers` +- [更新客户群组](https://open.dingtalk.com/document/development/crm-update-group): `crm-update-group` +- [查询客户数据](https://open.dingtalk.com/document/development/querying-customer-data): `querying-customer-data` +- [查询客户群列表](https://open.dingtalk.com/document/development/query-the-list-of-customer-groups): `query-the-list-of-customer-groups` +- [查询客户群组列表](https://open.dingtalk.com/document/development/query-groups): `query-groups` +- [查询服务窗粉丝用户基础信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-fans-in-the-service-window): `queries-the-basic-information-of-fans-in-the-service-window` +- [根据指定条件查询个人或企业客户数据](https://open.dingtalk.com/document/development/obtains-crm-individual-customers-in-batches-based-on-specified-query): `obtains-crm-individual-customers-in-batches-based-on-specified-query` +- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/api-getcontacts): `api-getcontacts` +- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/api-getobjectdata): `api-getobjectdata` +- [第三方个人应用发送服务窗单人消息](https://open.dingtalk.com/document/development/a-third-party-personal-application-sends-a-message-to-a-single): `a-third-party-personal-application-sends-a-message-to-a-single` +- [获取个人或企业客户查重字段](https://open.dingtalk.com/document/development/obtain-duplicate-check-fields): `obtain-duplicate-check-fields` +- [获取个人或企业客户的元数据](https://open.dingtalk.com/document/development/obtain-the-metadata-of-individual-enterprise-customers): `obtain-the-metadata-of-individual-enterprise-customers` +- [获取全量个人或企业客户数据](https://open.dingtalk.com/document/development/crm-obtains-all-private-sea-customer-data): `crm-obtains-all-private-sea-customer-data` +- [获取单个客户群组详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-single-customer-group): `queries-the-details-of-a-single-customer-group` +- [获取单个客户群详情](https://open.dingtalk.com/document/development/obtain-a-single-customer-group): `obtain-a-single-customer-group` +- [获取审批中创建与CRM客户关联的TAB表单元数据](https://open.dingtalk.com/document/development/api-getrelatedviewtabmeta): `api-getrelatedviewtabmeta` +- [获取审批里创建的与CRM客户关联的TAB表单数据实例列表](https://open.dingtalk.com/document/development/api-getrelatedviewtabdata): `api-getrelatedviewtabdata` +- [获取客户管理全局信息](https://open.dingtalk.com/document/development/get-customer-management-global-information): `get-customer-management-global-information` + +### 日历(30 条) + +- [修改日程](https://open.dingtalk.com/document/development/modify-event): `modify-event` +- [创建日程](https://open.dingtalk.com/document/development/create-event): `create-event` +- [创建订阅日历](https://open.dingtalk.com/document/development/create-subscription-calendar): `create-subscription-calendar` +- [创建访问控制](https://open.dingtalk.com/document/development/create-schedule-access-control): `create-schedule-access-control` +- [删除日程](https://open.dingtalk.com/document/development/delete-event): `delete-event` +- [删除日程参与者](https://open.dingtalk.com/document/development/delete-schedule-participant): `delete-schedule-participant` +- [删除订阅日历](https://open.dingtalk.com/document/development/delete-subscription-calendar): `delete-subscription-calendar` +- [删除访问控制](https://open.dingtalk.com/document/development/delete-an-access-control-list): `delete-an-access-control-list` +- [取消订阅公共日历](https://open.dingtalk.com/document/development/unsubscribe-from-a-public-calendar): `unsubscribe-from-a-public-calendar` +- [取消预定会议室](https://open.dingtalk.com/document/development/remove-a-meeting-room): `remove-a-meeting-room` +- [更新订阅日历](https://open.dingtalk.com/document/development/update-subscription-calendar): `update-subscription-calendar` +- [查看单个日程的签到详情](https://open.dingtalk.com/document/development/view-the-check-in-details-of-a-single-schedule): `view-the-check-in-details-of-a-single-schedule` +- [查看单个日程的签退详情](https://open.dingtalk.com/document/development/view-the-billing-details-of-a-single-schedule): `view-the-billing-details-of-a-single-schedule` +- [查询单个日程详情](https://open.dingtalk.com/document/development/query-details-about-an-event): `query-details-about-an-event` +- [查询单个订阅日历详情](https://open.dingtalk.com/document/development/query-a-single-subscription-calendar): `query-a-single-subscription-calendar` +- [查询日历](https://open.dingtalk.com/document/development/query-a-calendar): `query-a-calendar` +- [查询日程列表](https://open.dingtalk.com/document/development/query-an-event-list): `query-an-event-list` +- [查询日程视图](https://open.dingtalk.com/document/development/query-schedule-view): `query-schedule-view` +- [添加日程参与者](https://open.dingtalk.com/document/development/add-schedule-participant): `add-schedule-participant` +- [获取会议室忙闲信息](https://open.dingtalk.com/document/development/queries-free-and-busy-meeting-room-information): `queries-free-and-busy-meeting-room-information` +- [获取日程参与者](https://open.dingtalk.com/document/development/get-the-participants-of-a-schedule): `get-the-participants-of-a-schedule` +- [获取用户忙闲信息](https://open.dingtalk.com/document/development/free-schedule): `free-schedule` +- [获取签到链接](https://open.dingtalk.com/document/development/api-getsigninlink): `api-getsigninlink` +- [获取签退链接](https://open.dingtalk.com/document/development/api-getsignoutlink): `api-getsignoutlink` +- [获取访问控制列表](https://open.dingtalk.com/document/development/obtain-the-access-control-list-of-the-calendar): `obtain-the-access-control-list-of-the-calendar` +- [订阅公共日历](https://open.dingtalk.com/document/development/subscribe-to-a-public-calendar): `subscribe-to-a-public-calendar` +- [设置日程响应邀请状态](https://open.dingtalk.com/document/development/configure-response-status): `configure-response-status` +- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-single-schedule-news): `sign-in-single-schedule-news` +- [针对单个日程进行签退](https://open.dingtalk.com/document/development/sign-off-for-a-single-schedule): `sign-off-for-a-single-schedule` +- [预定会议室](https://open.dingtalk.com/document/development/add-a-meeting-room): `add-a-meeting-room` + +### 视频会议(30 条) + +- [停止视频会议云录制](https://open.dingtalk.com/document/development/video-conferencing-stops-cloud-recording): `video-conferencing-stops-cloud-recording` +- [停止视频会议直播推流](https://open.dingtalk.com/document/development/videoconferencing-stops-live-stream-ingest): `videoconferencing-stops-live-stream-ingest` +- [全员静音或全员取消静音](https://open.dingtalk.com/document/development/mute-all-staff-or-unmute-all-staff): `mute-all-staff-or-unmute-all-staff` +- [关闭视频会议](https://open.dingtalk.com/document/development/close-audio-video-conference): `close-audio-video-conference` +- [创建用户专属短链](https://open.dingtalk.com/document/development/api-createcustomshortlink): `api-createcustomshortlink` +- [创建视频会议](https://open.dingtalk.com/document/development/create-a-video-conference): `create-a-video-conference` +- [创建预约会议](https://open.dingtalk.com/document/development/create-appointment-meeting): `create-appointment-meeting` +- [取消预约会议](https://open.dingtalk.com/document/development/cancel-appointment-meeting): `cancel-appointment-meeting` +- [开启视频会议云录制](https://open.dingtalk.com/document/development/video-conference-open-cloud-recording): `video-conference-open-cloud-recording` +- [开启视频会议直播推流](https://open.dingtalk.com/document/development/video-conference-enables-live-stream-ingest): `video-conference-enables-live-stream-ingest` +- [批量查询视频会议信息](https://open.dingtalk.com/document/development/batch-query-of-video-conference-information): `batch-query-of-video-conference-information` +- [指定人员静音或取消静音](https://open.dingtalk.com/document/development/specify-person-to-mute-or-unmute): `specify-person-to-mute-or-unmute` +- [更新预约会议](https://open.dingtalk.com/document/development/update-appointment-meeting): `update-appointment-meeting` +- [更新预约会议设置](https://open.dingtalk.com/document/development/api-updatescheduleconfsettings): `api-updatescheduleconfsettings` +- [查询企业进行中会议列表](https://open.dingtalk.com/document/development/api-queryorgconferencelist): `api-queryorgconferencelist` +- [查询会议录制中的文本信息](https://open.dingtalk.com/document/development/queries-the-text-information-about-cloud-recording): `queries-the-text-information-about-cloud-recording` +- [查询会议录制中的视频信息](https://open.dingtalk.com/document/development/queries-the-playback-information-about-a-recorded-cloud-video): `queries-the-playback-information-about-a-recorded-cloud-video` +- [查询会议录制的详情信息](https://open.dingtalk.com/document/development/query-recording-information): `query-recording-information` +- [查询用户进行中会议列表](https://open.dingtalk.com/document/development/api-queryuserongoingconference): `api-queryuserongoingconference` +- [查询视频会议信息](https://open.dingtalk.com/document/development/querying-video-conference-information): `querying-video-conference-information` +- [查询视频会议成员](https://open.dingtalk.com/document/development/querying-video-conference-members): `querying-video-conference-members` +- [查询预约会议](https://open.dingtalk.com/document/development/query-meeting-reservation): `query-meeting-reservation` +- [查询预约会议历史会议信息](https://open.dingtalk.com/document/development/query-appointment-meeting-history-meeting-information): `query-appointment-meeting-history-meeting-information` +- [查询预约会议设置](https://open.dingtalk.com/document/development/api-queryscheduleconfsettings): `api-queryscheduleconfsettings` +- [根据会议号查询会议信息](https://open.dingtalk.com/document/development/api-queryconferenceinfobyroomcode): `api-queryconferenceinfobyroomcode` +- [设置全员看他](https://open.dingtalk.com/document/development/set-the-whole-staff-to-see-him): `set-the-whole-staff-to-see-him` +- [设置联席主持人](https://open.dingtalk.com/document/development/set-up-co-hosts): `set-up-co-hosts` +- [踢出会议成员](https://open.dingtalk.com/document/development/kick-out-meeting-members): `kick-out-meeting-members` +- [邀请用户入会](https://open.dingtalk.com/document/development/invite-users-to-join): `invite-users-to-join` +- [锁定会议](https://open.dingtalk.com/document/development/api-lockconference): `api-lockconference` + +### 阿里商旅(25 条) + +- [修改成本中心](https://open.dingtalk.com/document/development/modify-basic-cost-center-information): `modify-basic-cost-center-information` +- [关联单号查询相关订单信息列表](https://open.dingtalk.com/document/development/related-order-information): `related-order-information` +- [创建日志](https://open.dingtalk.com/document/development/create-a-log): `create-a-log` +- [同步市内用车申请单](https://open.dingtalk.com/document/development/synchronize-third-party-city-vehicle-approval-form): `synchronize-third-party-city-vehicle-approval-form` +- [回传第三方超标审批结果](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-sync): `dingtalk-oapi-alitrip-btrip-exceedapply-sync` +- [审批市内用车申请单](https://open.dingtalk.com/document/development/approval-of-third-party-city-car-application-form): `approval-of-third-party-city-car-application-form` +- [搜索第三方机票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-flight): `dingtalk-oapi-alitrip-btrip-exceedapply-flight` +- [搜索第三方火车票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-train-get): `dingtalk-oapi-alitrip-btrip-exceedapply-train-get` +- [搜索第三方酒店超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get): `dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get` +- [新建审批单](https://open.dingtalk.com/document/development/user-new-approval-form): `user-new-approval-form` +- [新建成本中心](https://open.dingtalk.com/document/development/new-cost-center): `new-cost-center` +- [查询商旅火车票结算记账数据](https://open.dingtalk.com/document/development/business-travel-train-ticket-settlement-bookkeeping-query-interface): `business-travel-train-ticket-settlement-bookkeeping-query-interface` +- [查询市内用车申请单](https://open.dingtalk.com/document/development/query-the-application-form-for-third-party-vehicles-in-the-city): `query-the-application-form-for-third-party-vehicles-in-the-city` +- [查询成员排班信息](https://open.dingtalk.com/document/development/query-scheduling-for-a-day): `query-scheduling-for-a-day` +- [查询机票结算记账数据](https://open.dingtalk.com/document/development/ticket-settlement-bookkeeping-query-interface): `ticket-settlement-bookkeeping-query-interface` +- [查询用车结算记账记录](https://open.dingtalk.com/document/development/query-interface-for-vehicle-settlement-and-bookkeeping): `query-interface-for-vehicle-settlement-and-bookkeeping` +- [查询酒店结算记账数据](https://open.dingtalk.com/document/development/hotel-settlement-bookkeeping-query-interface): `hotel-settlement-bookkeeping-query-interface` +- [获取企业机票订单数据](https://open.dingtalk.com/document/development/obtains-enterprise-ticket-order-data): `obtains-enterprise-ticket-order-data` +- [获取商旅访问地址](https://open.dingtalk.com/document/development/obtain-business-travel-access-addresses): `obtain-business-travel-access-addresses` +- [获取打卡结果](https://open.dingtalk.com/document/development/open-attendance-clock-in-data): `open-attendance-clock-in-data` +- [获取打卡详情](https://open.dingtalk.com/document/development/attendance-clock-in-record-is-open): `attendance-clock-in-record-is-open` +- [获取日志评论详情](https://open.dingtalk.com/document/development/queries-log-comment-details): `queries-log-comment-details` +- [获取月对账结算数据](https://open.dingtalk.com/document/development/obtain-monthly-reconciliation-settlement-data): `obtain-monthly-reconciliation-settlement-data` +- [获取用车订单数据](https://open.dingtalk.com/document/development/vehicle-order-query-interface): `vehicle-order-query-interface` +- [设置成本中心人员信息](https://open.dingtalk.com/document/development/set-up-cost-center-personnel-information): `set-up-cost-center-personnel-information` + +### Agoal 目标(23 条) + +- [Agoal业务数据查询](https://open.dingtalk.com/document/development/agoal-business-biz-data-query): `agoal-business-biz-data-query` +- [创建业务实体](https://open.dingtalk.com/document/development/api-agoalentitycreate): `api-agoalentitycreate` +- [创建目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskcreate): `api-agoalperftaskcreate` +- [更新业务实体](https://open.dingtalk.com/document/development/api-agoalentityupdate): `api-agoalentityupdate` +- [更新目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskupdate): `api-agoalperftaskupdate` +- [查询企业下个人目标详情](https://open.dingtalk.com/document/development/api-getobjectivedetail): `api-getobjectivedetail` +- [查询企业下单个目标规则详情](https://open.dingtalk.com/document/development/api-getobjectiveruledetail): `api-getobjectiveruledetail` +- [查询企业下指定个人目标的所有进展](https://open.dingtalk.com/document/development/api-agoalobjectiveprogresslist): `api-agoalobjectiveprogresslist` +- [查询企业下的所有考核计划](https://open.dingtalk.com/document/development/api-agoalorgperfplanquery): `api-agoalorgperfplanquery` +- [查询企业下目标规则列表](https://open.dingtalk.com/document/development/api-agoalobjectiverulelist): `api-agoalobjectiverulelist` +- [查询某个考核计划的部门得分](https://open.dingtalk.com/document/development/api-agoalorgperfdocquery): `api-agoalorgperfdocquery` +- [查询组织目标详情](https://open.dingtalk.com/document/development/api-agoalorgobjectivequery): `api-agoalorgobjectivequery` +- [获取 Agoal 组织目标列表](https://open.dingtalk.com/document/development/api-agoalorgobjectivelist): `api-agoalorgobjectivelist` +- [获取Agoal指定目标规则下的周期列表](https://open.dingtalk.com/document/development/api-agoalobjectiveruleperiodlist): `api-agoalobjectiveruleperiodlist` +- [获取Agoal指定组织下的所有目标规则列表](https://open.dingtalk.com/document/development/api-agoalorgobjectiverulelist): `api-agoalorgobjectiverulelist` +- [获取Agoal指定规则周期下负责人的目标列表](https://open.dingtalk.com/document/development/api-agoaluserobjectivelist): `api-agoaluserobjectivelist` +- [获取Agoal指定部门下的计分卡维度和指标id](https://open.dingtalk.com/document/development/api-getdeptscorecardindicator): `api-getdeptscorecardindicator` +- [获取Agoal用户管理员列表](https://open.dingtalk.com/document/development/api-agoaluseradminlist): `api-agoaluseradminlist` +- [获取Agoal目标或关键结果关联的关键行动](https://open.dingtalk.com/document/development/api-agoalobjectivekeyactionlist): `api-agoalobjectivekeyactionlist` +- [获取计分卡指标详情](https://open.dingtalk.com/document/development/api-getindicatordetail): `api-getindicatordetail` +- [通过Agoal系统账号发送消息](https://open.dingtalk.com/document/development/api-agoalsendmessage): `api-agoalsendmessage` +- [通过指标编码批量查询指标列表](https://open.dingtalk.com/document/development/api-agoalindicatorbatchquery): `api-agoalindicatorbatchquery` +- [通过指标编码推送指标时间维度数据](https://open.dingtalk.com/document/development/api-agoalindicatordatapush): `api-agoalindicatordatapush` + +### 教育(23 条) + +- [修改用户成员类型](https://open.dingtalk.com/document/development/api-updatecollegeuseremptype): `api-updatecollegeuseremptype` +- [创建个人账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactuser): `api-addcollegecontactuser` +- [创建组织单元](https://open.dingtalk.com/document/development/api-createcollegecontactdept): `api-createcollegecontactdept` +- [创建自定义校区或部门](https://open.dingtalk.com/document/development/create-a-custom-campus-or-department): `create-a-custom-campus-or-department` +- [创建自定义部门下的班级](https://open.dingtalk.com/document/development/create-classes-in-a-custom-department): `create-classes-in-a-custom-department` +- [创建高校账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactexclusive): `api-addcollegecontactexclusive` +- [删除学生](https://open.dingtalk.com/document/development/delete-student): `delete-student` +- [删除家校部门](https://open.dingtalk.com/document/development/delete-home-school-department): `delete-home-school-department` +- [删除家长关系](https://open.dingtalk.com/document/development/delete-parent-relationship): `delete-parent-relationship` +- [删除老师](https://open.dingtalk.com/document/development/delete-teacher): `delete-teacher` +- [判断用户是否是认证组织的语文老师接口](https://open.dingtalk.com/document/development/api-isyuwencertifiedteacher): `api-isyuwencertifiedteacher` +- [学生调班](https://open.dingtalk.com/document/development/shift-students): `shift-students` +- [更新个人账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactuser): `api-updatecollegecontactuser` +- [更新学生](https://open.dingtalk.com/document/development/api-updatestudent): `api-updatestudent` +- [更新家长](https://open.dingtalk.com/document/development/api-updateguardian): `api-updateguardian` +- [更新班级](https://open.dingtalk.com/document/development/api-updateclass): `api-updateclass` +- [更新组织单元](https://open.dingtalk.com/document/development/api-updatecollegecontactdept): `api-updatecollegecontactdept` +- [更新高校账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactexclusive): `api-updatecollegecontactexclusive` +- [查询用户信息详情](https://open.dingtalk.com/document/development/api-querycollegecontactuserdetail): `api-querycollegecontactuserdetail` +- [获取子组织单元列表](https://open.dingtalk.com/document/development/api-listcollegecontactsubdepts): `api-listcollegecontactsubdepts` +- [获取组织单元支持的部门类型](https://open.dingtalk.com/document/development/api-listcollegecontactdepttypeconfig): `api-listcollegecontactdepttypeconfig` +- [获取组织单元详情](https://open.dingtalk.com/document/development/api-getcollegecontactdeptdetail): `api-getcollegecontactdeptdetail` +- [获取行政组织架构部门详情](https://open.dingtalk.com/document/development/api-getcollegecontactstandardstrudeptdetail): `api-getcollegecontactstandardstrudeptdetail` + +### 智能人事(17 条) + +- [修改已离职员工信息](https://open.dingtalk.com/document/development/modify-resigned-employee-information): `modify-resigned-employee-information` +- [员工加入待离职](https://open.dingtalk.com/document/development/api-empstartdismission): `api-empstartdismission` +- [批量获取员工离职信息](https://open.dingtalk.com/document/development/obtain-resignation-information-of-employees-new-version): `obtain-resignation-information-of-employees-new-version` +- [撤销员工待离职](https://open.dingtalk.com/document/development/api-revoketermination): `api-revoketermination` +- [新增或删除花名册选项类型字段的选项](https://open.dingtalk.com/document/development/intelligent-personnel-roster-field-option-modification): `intelligent-personnel-roster-field-option-modification` +- [智能人事员工调岗](https://open.dingtalk.com/document/development/intelligent-personnel-staff-transfer): `intelligent-personnel-staff-transfer` +- [智能人事员工转正](https://open.dingtalk.com/document/development/intelligent-personnel-staff-to-become-regular): `intelligent-personnel-staff-to-become-regular` +- [更新待离职员工离职信息](https://open.dingtalk.com/document/development/api-updateempdismissioninfo): `api-updateempdismissioninfo` +- [查询花名册中有权限的字段列表](https://open.dingtalk.com/document/development/query-the-list-of-fields-with-permissions-in-the-roster): `query-the-list-of-fields-with-permissions-in-the-roster` +- [添加待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-supports-system-and-custom-fields): `add-employees-to-be-hired-supports-system-and-custom-fields` +- [确认员工离职并删除](https://open.dingtalk.com/document/development/api-hrmprocessterminationandhandover): `api-hrmprocessterminationandhandover` +- [获取企业已有的所有离职原因](https://open.dingtalk.com/document/development/api-getalldismissionreasons): `api-getalldismissionreasons` +- [获取企业职位列表](https://open.dingtalk.com/document/development/obtain-enterprise-position-information): `obtain-enterprise-position-information` +- [获取企业职务列表](https://open.dingtalk.com/document/development/obtain-enterprise-title-information): `obtain-enterprise-title-information` +- [获取企业职级列表](https://open.dingtalk.com/document/development/obtain-enterprise-rank-information): `obtain-enterprise-rank-information` +- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/api-getemployeerosterbyfield): `api-getemployeerosterbyfield` +- [获取离职员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-who-have-left): `obtain-the-list-of-employees-who-have-left` + +### 机器人(16 条) + +- [下载机器人接收消息的文件内容](https://open.dingtalk.com/document/development/download-the-file-content-of-the-robot-receiving-message): `download-the-file-content-of-the-robot-receiving-message` +- [人与人会话中机器人发送普通消息](https://open.dingtalk.com/document/development/the-robot-sends-ordinary-messages-in-a-person-to-person-conversation): `the-robot-sends-ordinary-messages-in-a-person-to-person-conversation` +- [企业机器人撤回内部群消息](https://open.dingtalk.com/document/development/enterprise-chatbot-withdraws-internal-group-messages): `enterprise-chatbot-withdraws-internal-group-messages` +- [发送DING消息](https://open.dingtalk.com/document/development/robot-sends-nail-message): `robot-sends-nail-message` +- [批量发送人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/chatbots-send-one-on-one-chat-messages-in-batches): `chatbots-send-one-on-one-chat-messages-in-batches` +- [批量撤回人与人会话中机器人消息](https://open.dingtalk.com/document/development/batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations): `batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations` +- [批量撤回人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/batch-message-recall-chat): `batch-message-recall-chat` +- [批量查询人与机器人会话机器人消息是否已读](https://open.dingtalk.com/document/development/chatbot-batch-query-the-read-status-of-messages): `chatbot-batch-query-the-read-status-of-messages` +- [撤回已经发送的DING消息](https://open.dingtalk.com/document/development/robot-withdraws-pin-message): `robot-withdraws-pin-message` +- [机器人发送群聊消息](https://open.dingtalk.com/document/development/the-robot-sends-a-group-message): `the-robot-sends-a-group-message` +- [查询人与人会话中机器人消息已读列表](https://open.dingtalk.com/document/development/query-the-read-list-of-robot-messages-in-person-to-person-conversations): `query-the-read-list-of-robot-messages-in-person-to-person-conversations` +- [查询企业机器人群聊消息用户已读状态](https://open.dingtalk.com/document/development/chatbot-queries-the-read-status-of-a-message): `chatbot-queries-the-read-status-of-a-message` +- [查询单聊机器人的快捷入口](https://open.dingtalk.com/document/development/quick-entrance-of-inquiry-single-chat-robot): `quick-entrance-of-inquiry-single-chat-robot` +- [清空单聊机器人快捷入口](https://open.dingtalk.com/document/development/clear-single-chat-robot-quick-entry): `clear-single-chat-robot-quick-entry` +- [获取群内机器人列表](https://open.dingtalk.com/document/development/obtain-the-list-of-robots-in-the-group): `obtain-the-list-of-robots-in-the-group` +- [设置单聊机器人快捷入口](https://open.dingtalk.com/document/development/set-robot-quick-entrance): `set-robot-quick-entrance` + +### 简知 CRM(15 条) + +- [产品信息](https://open.dingtalk.com/document/development/add-or-edit-product-information): `add-or-edit-product-information` +- [入库单](https://open.dingtalk.com/document/development/add-or-edit-a-shipment-record): `add-or-edit-a-shipment-record` +- [出库单](https://open.dingtalk.com/document/development/add-or-edit-an-issue-ticket): `add-or-edit-an-issue-ticket` +- [发货单](https://open.dingtalk.com/document/development/add-or-edit-invoices): `add-or-edit-invoices` +- [合同订单](https://open.dingtalk.com/document/development/add-or-edit-contract-orders): `add-or-edit-contract-orders` +- [客户公共池](https://open.dingtalk.com/document/development/add-or-edit-customer-public-pools): `add-or-edit-customer-public-pools` +- [客户资料](https://open.dingtalk.com/document/development/add-or-edit-customer-profile): `add-or-edit-customer-profile` +- [报价记录](https://open.dingtalk.com/document/development/add-or-edit-quotation-records): `add-or-edit-quotation-records` +- [生产单](https://open.dingtalk.com/document/development/add-or-edit-a-production-order): `add-or-edit-a-production-order` +- [联系人](https://open.dingtalk.com/document/development/add-or-edit-contacts): `add-or-edit-contacts` +- [获取数据列表](https://open.dingtalk.com/document/development/obtain-the-data-list): `obtain-the-data-list` +- [获取数据详情](https://open.dingtalk.com/document/development/queries-data-details): `queries-data-details` +- [采购单](https://open.dingtalk.com/document/development/edit-purchase-order): `edit-purchase-order` +- [销售换货单](https://open.dingtalk.com/document/development/add-or-edit-a-sales-order): `add-or-edit-a-sales-order` +- [销售机会](https://open.dingtalk.com/document/development/add-or-edit-opportunities): `add-or-edit-opportunities` + +### 考勤(15 条) + +- [分页获取加班规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-overtime-rules-by-page): `retrieve-a-list-of-overtime-rules-by-page` +- [分页获取补卡规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-replenishment-rules-by-page): `retrieve-a-list-of-replenishment-rules-by-page` +- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/batch-query-employee-leave-balance-change-record): `batch-query-employee-leave-balance-change-record` +- [批量获取加班规则设置](https://open.dingtalk.com/document/development/batch-retrieve-overtime-rules): `batch-retrieve-overtime-rules` +- [更新假期规则](https://open.dingtalk.com/document/development/update-holiday-rules): `update-holiday-rules` +- [查询指定用户的封账规则](https://open.dingtalk.com/document/development/encapsulate-account-sealing-and-unsealing-rules): `encapsulate-account-sealing-and-unsealing-rules` +- [查询用户某段时间内是否处于封账状态](https://open.dingtalk.com/document/development/checks-whether-a-user-has-blocked-accounts-within-a-specified): `checks-whether-a-user-has-blocked-accounts-within-a-specified` +- [查询用户考勤节假日信息](https://open.dingtalk.com/document/development/obtain-user-attendance-and-holiday-information): `obtain-user-attendance-and-holiday-information` +- [查询考勤写操作权限](https://open.dingtalk.com/document/development/attendance-writing-operation-is-brand-new-query): `attendance-writing-operation-is-brand-new-query` +- [查询考勤机信息](https://open.dingtalk.com/document/development/query-attendance-machine-information): `query-attendance-machine-information` +- [根据设备ID获取员工信息](https://open.dingtalk.com/document/development/obtain-information-about-employees-based-on-device-ids): `obtain-information-about-employees-based-on-device-ids` +- [添加假期规则](https://open.dingtalk.com/document/development/add-holiday-rules): `add-holiday-rules` +- [通知审批通过](https://open.dingtalk.com/document/development/api-processapprovefinish): `api-processapprovefinish` +- [配置考勤排班附加信息](https://open.dingtalk.com/document/development/synchronization-scheduling-information): `synchronization-scheduling-information` +- [预计算时长](https://open.dingtalk.com/document/development/api-calculateduration): `api-calculateduration` + +### 登录授权(11 条) + +- [查询个人授权记录](https://open.dingtalk.com/document/development/query-personal-authorization-records): `query-personal-authorization-records` +- [获取jsapiTicket](https://open.dingtalk.com/document/development/create-a-jsapi-ticket): `create-a-jsapi-ticket` +- [获取企业内部应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app): `obtain-the-access-token-of-an-internal-app` +- [获取企业开通应用后的授权信息](https://open.dingtalk.com/document/development/obtains-the-authorization-information-after-the-enterprise-activates-the-application-1): `obtains-the-authorization-information-after-the-enterprise-activates-the-application-1` +- [获取定制应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-third-party-application-authorization-enterprise): `obtain-the-access-token-of-the-third-party-application-authorization-enterprise` +- [获取应用的 Access Token](https://open.dingtalk.com/document/development/api-gettoken): `api-gettoken` +- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/obtains-the-identity-of-an-application-administrator): `obtains-the-identity-of-an-application-administrator` +- [获取微应用后台免登的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-micro-application-background-without-log-on): `obtain-the-access-token-of-the-micro-application-background-without-log-on` +- [获取用户token](https://open.dingtalk.com/document/development/obtain-user-token): `obtain-user-token` +- [获取第三方企业应用的suiteAccessToken](https://open.dingtalk.com/document/development/obtains-the-suite-acess-token-of-third-party-enterprise-applications): `obtains-the-suite-acess-token-of-third-party-enterprise-applications` +- [获取第三方应用授权企业的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-authorized-enterprise-1): `obtain-the-access-token-of-the-authorized-enterprise-1` + +### 知识库(11 条) + +- [匹配文本中的词条](https://open.dingtalk.com/document/development/enterprise-encyclopedia-match-entries-in-a-text): `enterprise-encyclopedia-match-entries-in-a-text` +- [批量获取知识库](https://open.dingtalk.com/document/development/batch-acquisition-of-knowledge-base): `batch-acquisition-of-knowledge-base` +- [批量获取节点](https://open.dingtalk.com/document/development/obtain-nodes-in-batch): `obtain-nodes-in-batch` +- [新建知识库](https://open.dingtalk.com/document/development/new-knowledge-base): `new-knowledge-base` +- [查询词条详情](https://open.dingtalk.com/document/development/enterprise-encyclopedia-query-entry-details-by-entry-name): `enterprise-encyclopedia-query-entry-details-by-entry-name` +- [获取我的文档知识库信息](https://open.dingtalk.com/document/development/get-my-documents): `get-my-documents` +- [获取知识库](https://open.dingtalk.com/document/development/obtain-the-knowledge-base): `obtain-the-knowledge-base` +- [获取知识库列表](https://open.dingtalk.com/document/development/get-knowledge-base-list): `get-knowledge-base-list` +- [获取节点](https://open.dingtalk.com/document/development/get-knowledge-base-acquisition-node): `get-knowledge-base-acquisition-node` +- [获取节点列表](https://open.dingtalk.com/document/development/get-node-list): `get-node-list` +- [通过链接获取节点](https://open.dingtalk.com/document/development/get-node-by-link): `get-node-by-link` + +### 服务群(8 条) + +- [创建场景服务群](https://open.dingtalk.com/document/development/create-a-scenario-service-group): `create-a-scenario-service-group` +- [升级云客服服务群为钉钉智能服务群](https://open.dingtalk.com/document/development/upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent): `upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent` +- [升级普通群为服务群](https://open.dingtalk.com/document/development/a-dingtalk-group-is-upgraded-to-one-of-the-intelligent): `a-dingtalk-group-is-upgraded-to-one-of-the-intelligent` +- [发送服务群消息](https://open.dingtalk.com/document/development/service-group-message-sending-interface): `service-group-message-sending-interface` +- [更换服务群所在的群分组](https://open.dingtalk.com/document/development/modify-a-service-group): `modify-a-service-group` +- [查询服务群活跃用户](https://open.dingtalk.com/document/development/queries-active-service-users): `queries-active-service-users` +- [添加服务群成员](https://open.dingtalk.com/document/development/add-service-group-members): `add-service-group-members` +- [群发任务](https://open.dingtalk.com/document/development/service-group-sending-task-interface): `service-group-sending-task-interface` + +### Team 空间(7 条) + +- [创建协作空间](https://open.dingtalk.com/document/development/api-createprojectv3): `api-createprojectv3` +- [创建协作空间任务](https://open.dingtalk.com/document/development/api-createtask): `api-createtask` +- [创建自由任务](https://open.dingtalk.com/document/development/api-createorganizationtask): `api-createorganizationtask` +- [更新协作空间](https://open.dingtalk.com/document/development/api-updateprojectv3): `api-updateprojectv3` +- [查询任务详情](https://open.dingtalk.com/document/development/api-queryalltask): `api-queryalltask` +- [获取协作空间列表](https://open.dingtalk.com/document/development/api-searchprojectsv3-1): `api-searchprojectsv3-1` +- [获取用户参与项目](https://open.dingtalk.com/document/development/api-getuserjoinedprojectsv3): `api-getuserjoinedprojectsv3` + +### 智能招聘(7 条) + +- [根据手机号获取候选人信息](https://open.dingtalk.com/document/development/obtain-candidate-information-based-on-mobile-phone-number): `obtain-candidate-information-based-on-mobile-phone-number` +- [添加智能招聘文件到钉盘](https://open.dingtalk.com/document/development/add-nail-disk-file): `add-nail-disk-file` +- [确认完成权益的更新](https://open.dingtalk.com/document/development/confirm-benefits): `confirm-benefits` +- [获取候选人的面试信息](https://open.dingtalk.com/document/development/query-the-interview-list): `query-the-interview-list` +- [获取招聘流程标识](https://open.dingtalk.com/document/development/get-recruitment-process-identity): `get-recruitment-process-identity` +- [获取智能招聘文件上传信息](https://open.dingtalk.com/document/development/obtain-information-about-the-dingtalk-disk-upload-file): `obtain-information-about-the-dingtalk-disk-upload-file` +- [通知完成指定的新手任务](https://open.dingtalk.com/document/development/notify-the-completion-of-the-specified-novice-task): `notify-the-completion-of-the-specified-novice-task` + +### 财务(7 条) + +- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-user-code-instance): `create-a-user-code-instance` +- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/sync-pin-badge-code-verification-result): `sync-pin-badge-code-verification-result` +- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-user-code-instance): `update-user-code-instance` +- [解码钉工牌电子码](https://open.dingtalk.com/document/development/decoding-dingtalk-payment-code): `decoding-dingtalk-payment-code` +- [通知支付结果](https://open.dingtalk.com/document/development/notify-dingtalk-payment-code-payment-result): `notify-dingtalk-payment-code-payment-result` +- [通知退款结果](https://open.dingtalk.com/document/development/dingtalk-payment-code-refund-information-synchronization-operation): `dingtalk-payment-code-refund-information-synchronization-operation` +- [配置企业钉工牌](https://open.dingtalk.com/document/development/set-up-enterprise-payment-code-configuration-interface): `set-up-enterprise-payment-code-configuration-interface` + +### 待办(6 条) + +- [创建钉钉个人待办任务](https://open.dingtalk.com/document/development/api-createpersonaltodotask): `api-createpersonaltodotask` +- [创建钉钉待办任务](https://open.dingtalk.com/document/development/add-dingtalk-to-do-task): `add-dingtalk-to-do-task` +- [删除钉钉待办任务](https://open.dingtalk.com/document/development/delete-dingtalk-to-do-tasks): `delete-dingtalk-to-do-tasks` +- [更新钉钉待办任务](https://open.dingtalk.com/document/development/updates-dingtalk-to-do-tasks): `updates-dingtalk-to-do-tasks` +- [更新钉钉待办执行者状态](https://open.dingtalk.com/document/development/update-dingtalk-to-do-status): `update-dingtalk-to-do-status` +- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/query-the-to-do-list-of-enterprise-users): `query-the-to-do-list-of-enterprise-users` + +### 百科问答(6 条) + +- [分页获取企业词条信息](https://open.dingtalk.com/document/development/entry-search): `entry-search` +- [删除词条](https://open.dingtalk.com/document/development/delete-entry): `delete-entry` +- [审核词条](https://open.dingtalk.com/document/development/review-entries): `review-entries` +- [新增词条](https://open.dingtalk.com/document/development/new-entry): `new-entry` +- [更新词条](https://open.dingtalk.com/document/development/update-entry): `update-entry` +- [根据词条ID查询详情](https://open.dingtalk.com/document/development/query-entry): `query-entry` + +### 直播(6 条) + +- [修改培训课程](https://open.dingtalk.com/document/development/modify-the-basic-information-of-a-live-streaming-course): `modify-the-basic-information-of-a-live-streaming-course` +- [修改直播属性信息](https://open.dingtalk.com/document/development/modify-live-streaming): `modify-live-streaming` +- [创建直播](https://open.dingtalk.com/document/development/create-live-streaming): `create-live-streaming` +- [删除直播](https://open.dingtalk.com/document/development/delete-live-streaming): `delete-live-streaming` +- [查询直播信息](https://open.dingtalk.com/document/development/queries-the-live-streaming-information): `queries-the-live-streaming-information` +- [查询直播观看人员信息](https://open.dingtalk.com/document/development/queries-the-viewing-information-of-viewers): `queries-the-viewing-information-of-viewers` + +### 行业(6 条) + +- [保存人员扩展属性](https://open.dingtalk.com/document/development/personnel-extension-property-error): `personnel-extension-property-error` +- [创建园区项目](https://open.dingtalk.com/document/development/create-a-campus-project): `create-a-campus-project` +- [创建项目组](https://open.dingtalk.com/document/development/create-a-project-group): `create-a-project-group` +- [删除项目组](https://open.dingtalk.com/document/development/delete-the-project-group-team): `delete-the-project-group-team` +- [查询园区项目信息](https://open.dingtalk.com/document/development/query-a-project-in-a-specified-campus): `query-a-project-in-a-specified-campus` +- [查询项目组信息](https://open.dingtalk.com/document/development/query-a-project-group-in-the-specified-park): `query-a-project-group-in-the-specified-park` + +### 居民服务(5 条) + +- [分页查询居民积分流水](https://open.dingtalk.com/document/development/query-the-integral-flow-records-by-page): `query-the-integral-flow-records-by-page` +- [增加或减少居民积分](https://open.dingtalk.com/document/development/increase-or-decrease-resident-points): `increase-or-decrease-resident-points` +- [查询组织维度配置的所有积分规则](https://open.dingtalk.com/document/development/query-all-credit-rules): `query-all-credit-rules` +- [获取用户所在的行业角色信息](https://open.dingtalk.com/document/development/obtain-the-industry-role-information-of-the-user): `obtain-the-industry-role-information-of-the-user` +- [获取行业角色下的用户列表](https://open.dingtalk.com/document/development/obtains-a-list-of-users-under-an-industry-role): `obtains-a-list-of-users-under-an-industry-role` + +### 组织文化(5 条) + +- [创建荣誉勋章模板](https://open.dingtalk.com/document/development/create-medal-of-honor-template): `create-medal-of-honor-template` +- [撤销员工获得的荣誉勋章](https://open.dingtalk.com/document/development/revoke-an-employee-s-medal-of-honor): `revoke-an-employee-s-medal-of-honor` +- [查询员工已获得的组织荣誉](https://open.dingtalk.com/document/development/check-the-honors-that-an-employee-has-received): `check-the-honors-that-an-employee-has-received` +- [查询当前企业下可颁发的荣誉列表](https://open.dingtalk.com/document/development/query-the-list-of-honors-that-can-be-issued-under): `query-the-list-of-honors-that-can-be-issued-under` +- [给员工颁发荣誉](https://open.dingtalk.com/document/development/award-of-honor): `award-of-honor` + +### 钉密(5 条) + +- [小蜜客服机器人消息回复](https://open.dingtalk.com/document/development/xiaomi-customer-service-robot-message-reply): `xiaomi-customer-service-robot-message-reply` +- [推送小蜜机器人单聊O2O消息](https://open.dingtalk.com/document/development/push-xiaomi-customer-service-robot-single-chat-message): `push-xiaomi-customer-service-robot-single-chat-message` +- [智能问答](https://open.dingtalk.com/document/development/alimebot-intelligent-q-a-interface): `alimebot-intelligent-q-a-interface` +- [查询机器人基础指标数据](https://open.dingtalk.com/document/development/query-robot-data-indicators): `query-robot-data-indicators` +- [获取用户登录凭证](https://open.dingtalk.com/document/development/obtains-the-user-login-credential-of-the-third-party-system-of): `obtains-the-user-login-credential-of-the-third-party-system-of` + +### 云盘(4 条) + +- [删除空间](https://open.dingtalk.com/document/development/delete-a-space): `delete-a-space` +- [新建空间](https://open.dingtalk.com/document/development/new-space): `new-space` +- [根据spaceId获取指定空间信息](https://open.dingtalk.com/document/development/retrieves-the-space-list-on-the-management-side): `retrieves-the-space-list-on-the-management-side` +- [获取空间列表](https://open.dingtalk.com/document/development/queries-a-space-list): `queries-a-space-list` + +### 会话文件(4 条) + +- [以应用身份发送文件给指定用户](https://open.dingtalk.com/document/development/sends-a-storage-file-to-a-specified-user): `sends-a-storage-file-to-a-specified-user` +- [发送文件到指定会话](https://open.dingtalk.com/document/development/send-file-to-specified-session): `send-file-to-specified-session` +- [发送文件链接到指定会话](https://open.dingtalk.com/document/development/send-a-file-link-to-the-specified-session): `send-a-file-link-to-the-specified-session` +- [获取群存储空间信息](https://open.dingtalk.com/document/development/obtain-group-storage-space-information): `obtain-group-storage-space-information` + +### 应用市场(4 条) + +- [内购商品订单处理完成](https://open.dingtalk.com/document/development/internal-purchase-order-processing-completed): `internal-purchase-order-processing-completed` +- [查询应用市场订单详情](https://open.dingtalk.com/document/development/check-the-order-details-app-store): `check-the-order-details-app-store` +- [获取个人应用内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-sku-page-address-of-goods-purchased-in-personal): `obtain-the-sku-page-address-of-goods-purchased-in-personal` +- [获取未处理的已支付订单](https://open.dingtalk.com/document/development/obtaining-isv-unfinished-processing-order): `obtaining-isv-unfinished-processing-order` + +### 公告(3 条) + +- [查询公告已读未读人员列表](https://open.dingtalk.com/document/development/query-bulletin-read-unread-persons-list): `query-bulletin-read-unread-persons-list` +- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-get-blackboard): `obtains-the-details-get-blackboard` +- [获取公告钉盘空间信息](https://open.dingtalk.com/document/development/obtain-bulletin-nail-disk-space-information): `obtain-bulletin-nail-disk-space-information` + +### 制造(2 条) + +- [查询计件报工数据](https://open.dingtalk.com/document/development/riqing-monthly-settlement-query-interface-for-piece-rate-reporting): `riqing-monthly-settlement-query-interface-for-piece-rate-reporting` +- [计件报工](https://open.dingtalk.com/document/development/riqing-monthly-settlement-piece-rate-reporting-interface): `riqing-monthly-settlement-piece-rate-reporting-interface` + +### 碳能力(2 条) + +- [写入每日用户碳数据明细信息](https://open.dingtalk.com/document/development/write-in-the-detailed-information-of-daily-user-carbon-data): `write-in-the-detailed-information-of-daily-user-carbon-data` +- [写入每日组织碳数据明细信息](https://open.dingtalk.com/document/development/third-party-applications-write-daily-organizational-carbon-data-details-1): `third-party-applications-write-daily-organizational-carbon-data-details-1` + +### Agoal(1 条) + +- [查询假期余额](https://open.dingtalk.com/document/development/query-holiday-balance): `query-holiday-balance` + +### 会员(1 条) + +- [查询用户钉钉365会员信息](https://open.dingtalk.com/document/development/api-queryvipmemberinfo): `api-queryvipmemberinfo` + +### 硬件(1 条) + +- [获取用户考勤数据](https://open.dingtalk.com/document/development/obtain-the-attendance-update-data): `obtain-the-attendance-update-data` + +### 职业认证(1 条) + +- [检查用户是否完成所有任务](https://open.dingtalk.com/document/development/docking-of-provincial-practical-exercises-for-digital-managers): `docking-of-provincial-practical-exercises-for-digital-managers` + +## 连接平台(66 条) + +二级 Tab:平台介绍 / 开发指南 / 连接器中心 / 连接平台自动化(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 连接平台自动化(60 条) + +- [下载审批附件](https://open.dingtalk.com/document/development/api-premiumgrantprocessinstancefordownloadfile): `api-premiumgrantprocessinstancefordownloadfile` +- [下载审批附件](https://open.dingtalk.com/document/development/download-an-approval-attachment): `download-an-approval-attachment` +- [保存流程中心外部集成审批任务](https://open.dingtalk.com/document/development/api-premiumsaveintegratedtask): `api-premiumsaveintegratedtask` +- [保存流程中心外部集成审批实例](https://open.dingtalk.com/document/development/api-premiumexternalintegrationprocessinstance): `api-premiumexternalintegrationprocessinstance` +- [保存流程中心外部集成审批模板](https://open.dingtalk.com/document/development/api-premiumsaveintegratedprocess): `api-premiumsaveintegratedprocess` +- [创建实例](https://open.dingtalk.com/document/development/create-a-ticket-approval-instance): `create-a-ticket-approval-instance` +- [创建或更新业务分组](https://open.dingtalk.com/document/development/api-premiuminsertorupdatedir): `api-premiuminsertorupdatedir` +- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-orupdate-the-approval-template-new): `create-orupdate-the-approval-template-new` +- [创建或更新审批表单模板](https://open.dingtalk.com/document/development/create-an-approval-form-template): `create-an-approval-form-template` +- [创建或更新数据表单模板](https://open.dingtalk.com/document/development/api-premiumsaveform): `api-premiumsaveform` +- [创建数据表单实例](https://open.dingtalk.com/document/development/api-createdatapremiumsaveforminstance): `api-createdatapremiumsaveforminstance` +- [创建流程中心待处理任务](https://open.dingtalk.com/document/development/create-pending-tasks-in-process-center): `create-pending-tasks-in-process-center` +- [删除业务分组](https://open.dingtalk.com/document/development/api-premiumdeldir): `api-premiumdeldir` +- [删除数据表单实例](https://open.dingtalk.com/document/development/api-premiumdeleteforminstance): `api-premiumdeleteforminstance` +- [删除模板](https://open.dingtalk.com/document/development/self-owned-approval-deletion-template): `self-owned-approval-deletion-template` +- [加签审批任务](https://open.dingtalk.com/document/development/api-premiumappendtask): `api-premiumappendtask` +- [发起审批实例](https://open.dingtalk.com/document/development/create-an-approval-instance): `create-an-approval-instance` +- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/approve-or-reject-the-approval-task): `approve-or-reject-the-approval-task` +- [归档审批实例](https://open.dingtalk.com/document/development/api-archiveprocessinstance): `api-archiveprocessinstance` +- [批量取消流程中心待处理任务](https://open.dingtalk.com/document/development/cancel-multiple-oa-approval-tasks): `cancel-multiple-oa-approval-tasks` +- [批量同意或拒绝审批任务](https://open.dingtalk.com/document/development/api-premiumbatchexecuteprocessinstances): `api-premiumbatchexecuteprocessinstances` +- [批量更新实例状态](https://open.dingtalk.com/document/development/self-owned-batch-update-of-instance-status): `self-owned-batch-update-of-instance-status` +- [批量获取表单模板schema(包含表单和流程配置信息)](https://open.dingtalk.com/document/development/api-premiumqueryschemaandprocessbycodelist): `api-premiumqueryschemaandprocessbycodelist` +- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/api-premiumaddapprovedentryauth): `api-premiumaddapprovedentryauth` +- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/download-the-approval-nail-file): `download-the-approval-nail-file` +- [授权预览审批附件](https://open.dingtalk.com/document/development/api-premiumgetspacewithdownloadauth): `api-premiumgetspacewithdownloadauth` +- [授权预览审批附件](https://open.dingtalk.com/document/development/official-authorized-preview-approval-attachment): `official-authorized-preview-approval-attachment` +- [撤销审批实例](https://open.dingtalk.com/document/development/revoke-an-approval-instance): `revoke-an-approval-instance` +- [更新实例状态](https://open.dingtalk.com/document/development/update-instance-status): `update-instance-status` +- [更新数据表单实例](https://open.dingtalk.com/document/development/api-premiumupdateforminstance): `api-premiumupdateforminstance` +- [更新流程中心任务状态](https://open.dingtalk.com/document/development/update-process-center-task-status): `update-process-center-task-status` +- [更新流程表单审批实例](https://open.dingtalk.com/document/development/api-premiumupdateprocessinstancevariables): `api-premiumupdateprocessinstancevariables` +- [查询审批中心用户已发起实例列表](https://open.dingtalk.com/document/development/api-premiumgetsubmittedinstances): `api-premiumgetsubmittedinstances` +- [查询审批中心用户已处理任务列表](https://open.dingtalk.com/document/development/api-premiumgetdonetasks): `api-premiumgetdonetasks` +- [查询审批中心用户已收到的实例列表](https://open.dingtalk.com/document/development/api-premiumgetnoticedinstances): `api-premiumgetnoticedinstances` +- [查询审批中心用户待处理任务列表](https://open.dingtalk.com/document/development/api-premiumgettodotasks): `api-premiumgettodotasks` +- [查询已设置为条件的表单组件](https://open.dingtalk.com/document/development/query-form-components-that-have-been-set-as-criteria-1): `query-form-components-that-have-been-set-as-criteria-1` +- [查询通过流程中心集成的OA审批任务](https://open.dingtalk.com/document/development/query-oa-approval-tasks-integrated-through-process-center): `query-oa-approval-tasks-integrated-through-process-center` +- [根据processCode分页获取审批流程数据](https://open.dingtalk.com/document/development/api-premiumgetprocessinstances): `api-premiumgetprocessinstances` +- [添加审批评论](https://open.dingtalk.com/document/development/official-approval-adds-approval-comments): `official-approval-adds-approval-comments` +- [清理OA审批数据](https://open.dingtalk.com/document/development/clear-oa-approval-data): `clear-oa-approval-data` +- [管理员批量转交指定员工的待处理任务](https://open.dingtalk.com/document/development/api-premiumredirecttasksbymanager): `api-premiumredirecttasksbymanager` +- [管理员查询指定员工的待处理任务列表](https://open.dingtalk.com/document/development/api-premiumquerytodotasksbymanager): `api-premiumquerytodotasksbymanager` +- [获取单个审批实例详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-single-approval-instance-pop): `obtains-the-details-of-a-single-approval-instance-pop` +- [获取单个数据表单实例详情](https://open.dingtalk.com/document/development/obtain-details-of-a-single-data-form-instance): `obtain-details-of-a-single-data-form-instance` +- [获取审批单流程中的节点信息](https://open.dingtalk.com/document/development/approval-process-prediction): `approval-process-prediction` +- [获取审批实例ID列表](https://open.dingtalk.com/document/development/obtain-an-approval-list-of-instance-ids): `obtain-an-approval-list-of-instance-ids` +- [获取审批表单控件字段内容修改记录](https://open.dingtalk.com/document/development/api-premiumgetfieldmodifiedhistory): `api-premiumgetfieldmodifiedhistory` +- [获取审批表单控件字段操作权限](https://open.dingtalk.com/document/development/api-premiumgetinstfieldsetting): `api-premiumgetinstfieldsetting` +- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/api-premiumgetattachmentspace): `api-premiumgetattachmentspace` +- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/obtains-the-information-about-approval-nail-disk): `obtains-the-information-about-approval-nail-disk` +- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/get-all-manageable-forms-for-the-current-enterprise): `get-all-manageable-forms-for-the-current-enterprise` +- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/obtains-a-list-of-approval-forms-visible-to-the-specified): `obtains-a-list-of-approval-forms-visible-to-the-specified` +- [获取数据表单schema](https://open.dingtalk.com/document/development/api-premiumgetformschema): `api-premiumgetformschema` +- [获取数据表单实例列表](https://open.dingtalk.com/document/development/api-premiumgetforminstances): `api-premiumgetforminstances` +- [获取模板code](https://open.dingtalk.com/document/development/obtain-the-template-code): `obtain-the-template-code` +- [获取用户待审批数量](https://open.dingtalk.com/document/development/queries-the-number-of-requests-to-be-approved-by-users): `queries-the-number-of-requests-to-be-approved-by-users` +- [获取表单 schema](https://open.dingtalk.com/document/development/obtain-the-form-schema): `obtain-the-form-schema` +- [转交OA审批任务](https://open.dingtalk.com/document/development/transfer-the-oa-approval-task): `transfer-the-oa-approval-task` +- [退回审批任务](https://open.dingtalk.com/document/development/api-premiumreverttask): `api-premiumreverttask` + +### 企业互通(6 条) + +- [批量获取关注服务窗用户信息](https://open.dingtalk.com/document/development/obtains-the-follower-information-from-the-service-window): `obtains-the-follower-information-from-the-service-window` +- [获取企业下服务窗列表](https://open.dingtalk.com/document/development/queries-the-list-of-services-under-an-enterprise): `queries-the-list-of-services-under-an-enterprise` +- [获取关注服务窗用户信息](https://open.dingtalk.com/document/development/queries-the-follower-information-of-the-service-window): `queries-the-follower-information-of-the-service-window` +- [获取用户关注服务窗状态](https://open.dingtalk.com/document/development/third-party-enterprise-application-obtains-user-attention-service-window-status): `third-party-enterprise-application-obtains-user-attention-service-window-status` +- [获取用户服务窗关注状态](https://open.dingtalk.com/document/development/obtain-the-attention-status-of-the-user-service-window): `obtain-the-attention-status-of-the-user-service-window` +- [获取组织服务窗账号列表](https://open.dingtalk.com/document/development/the-third-party-enterprise-application-obtains-the-account-list-of-the): `the-third-party-enterprise-application-obtains-the-account-list-of-the` + +## AI PaaS(48 条) + +二级 Tab:平台介绍 / 炼丹炉大模型平台 / AI 助理创建平台 / AI 客服助理(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 组织大脑(41 条) + +- [人员标签数据查询](https://open.dingtalk.com/document/development/api-stafflabelrecordsquery): `api-stafflabelrecordsquery` +- [人才档案基础数据查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofilebasicquery): `api-hrbraintalentprofilebasicquery` +- [人才档案照片查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofileattachmentquery): `api-hrbraintalentprofileattachmentquery` +- [人才池信息查询](https://open.dingtalk.com/document/development/api-hrbrainemppoolquery): `api-hrbrainemppoolquery` +- [人才池在池人员列表](https://open.dingtalk.com/document/development/api-hrbrainemppooluser): `api-hrbrainemppooluser` +- [数据集成专业技能删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelprofskill): `api-hrbraindeletelabelprofskill` +- [数据集成专业技能同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelprofskill): `api-hrbrainimportlabelprofskill` +- [数据集成人员信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteempinfo): `api-hrbraindeleteempinfo` +- [数据集成人员信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportempinfo): `api-hrbrainimportempinfo` +- [数据集成人员标签删除](https://open.dingtalk.com/document/development/api-hrbraindeletetlabelbase): `api-hrbraindeletetlabelbase` +- [数据集成入职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportregist): `api-hrbrainimportregist` +- [数据集成入职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregist): `api-hrbraindeleteregist` +- [数据集成删除自定义模型数据](https://open.dingtalk.com/document/development/api-hrbraindeletecustom): `api-hrbraindeletecustom` +- [数据集成培训学习数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletetraining): `api-hrbraindeletetraining` +- [数据集成培训学习记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttraining): `api-hrbrainimporttraining` +- [数据集成基础标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelbase): `api-hrbrainimportlabelbase` +- [数据集成处分记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletepundetail): `api-hrbraindeletepundetail` +- [数据集成处分记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpundetail): `api-hrbrainimportpundetail` +- [数据集成奖励信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteawardrecords): `api-hrbraindeleteawardrecords` +- [数据集成奖励记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportawarddetail): `api-hrbrainimportawarddetail` +- [数据集成工作经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteworkexp): `api-hrbraindeleteworkexp` +- [数据集成工作经历同步](https://open.dingtalk.com/document/development/api-hrbrainimportworkexp): `api-hrbrainimportworkexp` +- [数据集成异动记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttransfereval): `api-hrbrainimporttransfereval` +- [数据集成教育经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteeduexp): `api-hrbraindeleteeduexp` +- [数据集成教育经历同步](https://open.dingtalk.com/document/development/api-hrbrainimporteduexp): `api-hrbrainimporteduexp` +- [数据集成晋升记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpromeval): `api-hrbrainimportpromeval` +- [数据集成盘点数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelinventory): `api-hrbraindeletelabelinventory` +- [数据集成盘点数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelinventory): `api-hrbrainimportlabelinventory` +- [数据集成离职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportdimission): `api-hrbrainimportdimission` +- [数据集成离职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletedimission): `api-hrbraindeletedimission` +- [数据集成组织架构同步](https://open.dingtalk.com/document/development/api-hrbrainimportdeptinfo): `api-hrbrainimportdeptinfo` +- [数据集成组织架构数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletedeptinfo): `api-hrbraindeletedeptinfo` +- [数据集成绩效记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteperfeval): `api-hrbraindeleteperfeval` +- [数据集成绩效记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportperfeval): `api-hrbrainimportperfeval` +- [数据集成自定义标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelcustom): `api-hrbrainimportlabelcustom` +- [数据集成调岗记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletetransfereval): `api-hrbraindeletetransfereval` +- [数据集成转正数据删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregular): `api-hrbraindeleteregular` +- [数据集成转正记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportregular): `api-hrbrainimportregular` +- [数据集成领域经验删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelindustry): `api-hrbraindeletelabelindustry` +- [数据集成领域经验同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelindustry): `api-hrbrainimportlabelindustry` +- [自定义模型数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportcustom): `api-hrbrainimportcustom` + +### 智能客服(4 条) + +- [分页查询工单](https://open.dingtalk.com/document/development/intelligent-customer-service-paging-query-work-order): `intelligent-customer-service-paging-query-work-order` +- [创建自助单](https://open.dingtalk.com/document/development/create-a-self-service-ticket): `create-a-self-service-ticket` +- [执行工单活动](https://open.dingtalk.com/document/development/intelligent-customer-service-execute-work-order-activities): `intelligent-customer-service-execute-work-order-activities` +- [查询动作记录](https://open.dingtalk.com/document/development/intelligent-customer-service-query-action-records): `intelligent-customer-service-query-action-records` + +### AI PaaS(3 条) + +- [大模型推理服务(多模态模型)](https://open.dingtalk.com/document/development/large-model-reasoning-service-interface-multimodal-model-1): `large-model-reasoning-service-interface-multimodal-model-1` +- [大模型推理服务(文生文模型)](https://open.dingtalk.com/document/development/api-exclusivemodelcompleteservice): `api-exclusivemodelcompleteservice` +- [炼丹炉专属模型服务](https://open.dingtalk.com/document/development/api-liandanluexclusivemodel): `api-liandanluexclusivemodel` + +## 硬件开发(28 条) + +二级 Tab:智能硬件(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 智能会议室(22 条) + +- [创建会议室](https://open.dingtalk.com/document/development/create-meeting-room): `create-meeting-room` +- [创建会议室分组](https://open.dingtalk.com/document/development/create-meeting-room-groups): `create-meeting-room-groups` +- [创建会议室预定黑名单](https://open.dingtalk.com/document/development/api-createbookingblacklist): `api-createbookingblacklist` +- [创建自定义屏幕模板](https://open.dingtalk.com/document/development/api-createdevicecustomtemplate): `api-createdevicecustomtemplate` +- [删除会议室](https://open.dingtalk.com/document/development/delete-a-meeting-room): `delete-a-meeting-room` +- [删除会议室分组](https://open.dingtalk.com/document/development/delete-a-conference-room-group): `delete-a-conference-room-group` +- [删除会议室预定黑名单](https://open.dingtalk.com/document/development/api-deletebookingblacklist): `api-deletebookingblacklist` +- [删除自定义屏幕模板](https://open.dingtalk.com/document/development/api-deletedevicecustomtemplate): `api-deletedevicecustomtemplate` +- [发送Rooms中控API信令](https://open.dingtalk.com/document/development/api-sendcentralcontrol): `api-sendcentralcontrol` +- [更新会议室信息](https://open.dingtalk.com/document/development/update-meeting-room-information): `update-meeting-room-information` +- [更新会议室分组信息](https://open.dingtalk.com/document/development/update-meeting-room-groups): `update-meeting-room-groups` +- [更新自定义屏幕模板](https://open.dingtalk.com/document/development/api-updatedevicecustomtemplate): `api-updatedevicecustomtemplate` +- [查询会议室分组信息](https://open.dingtalk.com/document/development/query-meeting-room-groups): `query-meeting-room-groups` +- [查询会议室分组列表](https://open.dingtalk.com/document/development/query-meeting-rooms-groups): `query-meeting-rooms-groups` +- [查询会议室列表](https://open.dingtalk.com/document/development/check-the-meeting-room-list): `check-the-meeting-room-list` +- [查询会议室详情](https://open.dingtalk.com/document/development/check-meeting-room-details): `check-meeting-room-details` +- [查询自定义屏幕信息](https://open.dingtalk.com/document/development/api-querydevicecustomtemplate): `api-querydevicecustomtemplate` +- [查询自定义屏幕模板列表](https://open.dingtalk.com/document/development/api-querylistdevicecustomscreentemplate): `api-querylistdevicecustomscreentemplate` +- [查询视频会议设备信息](https://open.dingtalk.com/document/development/querying-video-conference-device-information): `querying-video-conference-device-information` +- [查询视频会议设备属性信息](https://open.dingtalk.com/document/development/querying-video-conference-device-attribute-information): `querying-video-conference-device-attribute-information` +- [获取用户发出的日志列表](https://open.dingtalk.com/document/development/query-logs-sent-by-an-employee): `query-logs-sent-by-an-employee` +- [获取用户可见的日志模板](https://open.dingtalk.com/document/development/obtains-the-list-of-visible-log-templates-based-on-the): `obtains-the-list-of-visible-log-templates-based-on-the` + +### 设备管理(5 条) + +- [批量注册与激活设备](https://open.dingtalk.com/document/development/register-and-activate-devices-in-batches): `register-and-activate-devices-in-batches` +- [查询已经注册的设备信息](https://open.dingtalk.com/document/development/query-information-about-a-registered-device): `query-information-about-a-registered-device` +- [注册设备到钉钉](https://open.dingtalk.com/document/development/pin-registration-interface): `pin-registration-interface` +- [获取巡检或保养记录](https://open.dingtalk.com/document/development/obtain-inspection-and-maintenance-records): `obtain-inspection-and-maintenance-records` +- [获取报修记录](https://open.dingtalk.com/document/development/obtain-the-repair-report-record): `obtain-the-repair-report-record` + +### 智能设备(1 条) + +- [变更智能考勤机员工](https://open.dingtalk.com/document/development/change-intelligent-attendance-machine-staff): `change-intelligent-attendance-machine-staff` + +## 互动卡片(9 条) + +二级 Tab:开发指南 / 使用教程 / 卡片模板搭建器 / 互动卡片搭建平台 / 卡片规范设计(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 互动卡片(9 条) + +- [AI卡片流式更新](https://open.dingtalk.com/document/development/api-streamingupdate): `api-streamingupdate` +- [关闭吊顶卡片](https://open.dingtalk.com/document/development/api-closetopcard): `api-closetopcard` +- [创建卡片](https://open.dingtalk.com/document/development/interface-for-creating-a-card-instance): `interface-for-creating-a-card-instance` +- [创建并投放卡片](https://open.dingtalk.com/document/development/create-and-deliver-cards): `create-and-deliver-cards` +- [卡片平台模板复制](https://open.dingtalk.com/document/development/api-copytemplate): `api-copytemplate` +- [投放卡片](https://open.dingtalk.com/document/development/delivery-card-interface): `delivery-card-interface` +- [新增或者更新卡片的场域信息](https://open.dingtalk.com/document/development/add-field-interface): `add-field-interface` +- [更新卡片](https://open.dingtalk.com/document/development/interactive-card-update-interface): `interactive-card-update-interface` +- [注册卡片回调地址](https://open.dingtalk.com/document/development/register-card-callback-address): `register-card-callback-address` + +## 专属版客户端插件(37 条) + +二级 Tab:功能介绍 / 插件开发(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 专属版(37 条) + +- [DING服务](https://open.dingtalk.com/document/development/send-in-application-ding): `send-in-application-ding` +- [专属小红点推送](https://open.dingtalk.com/document/development/push-a-red-dot-to-the-micro-application): `push-a-red-dot-to-the-micro-application` +- [企业内部群禁言或解除禁言](https://open.dingtalk.com/document/development/exclusive-dingtalk-group-ban): `exclusive-dingtalk-group-ban` +- [企业员工专属安全管控功能命中查询](https://open.dingtalk.com/document/development/api-checkcontrolhitstatus): `api-checkcontrolhitstatus` +- [修改伙伴类型可见性](https://open.dingtalk.com/document/development/modify-partner-type-visibility): `modify-partner-type-visibility` +- [修改角色可见性](https://open.dingtalk.com/document/development/modify-role-visibility): `modify-role-visibility` +- [删除可信设备](https://open.dingtalk.com/document/development/delete-trusted-devices): `delete-trusted-devices` +- [发送文件更改的评论](https://open.dingtalk.com/document/development/send-comments-on-file-changes): `send-comments-on-file-changes` +- [发送电话DING](https://open.dingtalk.com/document/development/outgoing-phone-ding): `outgoing-phone-ding` +- [发送邀请函](https://open.dingtalk.com/document/development/send-invitations): `send-invitations` +- [同步存储数据](https://open.dingtalk.com/document/development/api-datasync): `api-datasync` +- [批量新增可信设备](https://open.dingtalk.com/document/development/create-multiple-trusted-devices): `create-multiple-trusted-devices` +- [新增可信设备信息](https://open.dingtalk.com/document/development/add-information-about-a-trusted-device): `add-information-about-a-trusted-device` +- [更新发送文件的检测状态](https://open.dingtalk.com/document/development/update-the-detection-status-of-a-sent-file): `update-the-detection-status-of-a-sent-file` +- [查询人脸录入状态](https://open.dingtalk.com/document/development/query-face-entry-status): `query-face-entry-status` +- [查询企业内部群信息](https://open.dingtalk.com/document/development/obtain-group-info): `obtain-group-info` +- [查询伙伴角色列表](https://open.dingtalk.com/document/development/query-the-list-of-partners): `query-the-list-of-partners` +- [查询公共设备](https://open.dingtalk.com/document/development/query-public-equipment): `query-public-equipment` +- [查询可信设备详细信息](https://open.dingtalk.com/document/development/query-trusted-device-details): `query-trusted-device-details` +- [查询实人认证状态](https://open.dingtalk.com/document/development/queries-the-id-verification-status): `queries-the-id-verification-status` +- [查询群发消息列表](https://open.dingtalk.com/document/development/service-account-query-msgsend-records): `service-account-query-msgsend-records` +- [查询群发消息详情](https://open.dingtalk.com/document/development/service-account-msg-record-detail): `service-account-msg-record-detail` +- [根据userId查询人员的标签信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-user-tag): `you-can-call-this-operation-to-retrieve-the-user-tag` +- [根据会议逻辑ID查询会议基本信息](https://open.dingtalk.com/document/development/query-basic-meeting-information-using-a-logical-id): `query-basic-meeting-information-using-a-logical-id` +- [消息群发](https://open.dingtalk.com/document/development/api-sendmessage): `api-sendmessage` +- [获取专属存储文件路径](https://open.dingtalk.com/document/development/api-getprivatestorefilepath): `api-getprivatestorefilepath` +- [获取人脸对比接口调用记录](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-call-records): `you-can-call-this-operation-to-query-the-call-records` +- [获取企业专属钉钉权益列表](https://open.dingtalk.com/document/development/api-queryexclusivebenefits): `api-queryexclusivebenefits` +- [获取可打标部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-departments-that-can-be-marked): `obtains-a-list-of-departments-that-can-be-marked` +- [获取子标签列表](https://open.dingtalk.com/document/development/obtain-child-tags-from-a-parent-tag): `obtain-child-tags-from-a-parent-tag` +- [获取实人认证接口调用记录](https://open.dingtalk.com/document/development/obtains-the-call-record-of-the-id-authentication-api): `obtains-the-call-record-of-the-id-authentication-api` +- [获取审计协议签署人员信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-persons-who-sign-the-audit-1): `obtains-the-information-about-the-persons-who-sign-the-audit-1` +- [获取文件操作记录](https://open.dingtalk.com/document/development/obtain-file-operation-records): `obtain-file-operation-records` +- [获取群活跃明细列表](https://open.dingtalk.com/document/development/obtains-the-group-activity-details-list): `obtains-the-group-activity-details-list` +- [获取视频会议详情](https://open.dingtalk.com/document/development/get-video-meeting-details): `get-video-meeting-details` +- [获取防截屏操作记录](https://open.dingtalk.com/document/development/obtain-anti-screen-capture-operation-records): `obtain-anti-screen-capture-operation-records` +- [设置部门伙伴类型和伙伴编码](https://open.dingtalk.com/document/development/set-department-partner-type-and-partner-code): `set-department-partner-type-and-partner-code` + +## 数据资产(102 条) + +二级 Tab:平台介绍 / 宜数(智能问数)(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 宜搭(61 条) + +- [保存表单数据](https://open.dingtalk.com/document/development/api-saveformdata-v2): `api-saveformdata-v2` +- [分页获取集成自动化日志列表](https://open.dingtalk.com/document/development/api-pageautoflowlog): `api-pageautoflowlog` +- [删除流程实例](https://open.dingtalk.com/document/development/delete-the-process-instance): `delete-the-process-instance` +- [删除表单数据](https://open.dingtalk.com/document/development/delete-form-data): `delete-form-data` +- [发起宜搭审批流程](https://open.dingtalk.com/document/development/api-startinstance-v2): `api-startinstance-v2` +- [同意或拒绝宜搭审批任务](https://open.dingtalk.com/document/development/execute-approval-tasks): `execute-approval-tasks` +- [应用授权校验](https://open.dingtalk.com/document/development/application-authorization-verification): `application-authorization-verification` +- [批量创建表单实例](https://open.dingtalk.com/document/development/create-multiple-form-instances): `create-multiple-form-instances` +- [批量删除宜搭角色成员](https://open.dingtalk.com/document/development/batch-deleterolemembers): `batch-deleterolemembers` +- [批量删除指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-deletematrixdatabyrowids): `api-deletematrixdatabyrowids` +- [批量删除表单实例](https://open.dingtalk.com/document/development/delete-multiple-form-instances): `delete-multiple-form-instances` +- [批量执行宜搭审批任务](https://open.dingtalk.com/document/development/batch-execution-should-take-the-lead-of-approval-tasks): `batch-execution-should-take-the-lead-of-approval-tasks` +- [批量更新宜搭角色成员](https://open.dingtalk.com/document/development/batch-rolemembers): `batch-rolemembers` +- [批量更新表单实例内的组件值](https://open.dingtalk.com/document/development/batch-update-of-component-values-in-form-instances): `batch-update-of-component-values-in-form-instances` +- [批量查询宜搭表单实例的评论](https://open.dingtalk.com/document/development/batch-query-of-comments-appropriate-for-form-instances): `batch-query-of-comments-appropriate-for-form-instances` +- [批量获取流程实例列表](https://open.dingtalk.com/document/development/queries-multiple-process-instances): `queries-multiple-process-instances` +- [批量获取表单实例数据](https://open.dingtalk.com/document/development/obtain-multiple-form-instance-data): `obtain-multiple-form-instance-data` +- [提交评论](https://open.dingtalk.com/document/development/submit-comment): `submit-comment` +- [新增或更新表单实例](https://open.dingtalk.com/document/development/api-createorupdateformdata-v2): `api-createorupdateformdata-v2` +- [新购授权订单](https://open.dingtalk.com/document/development/new-order-authorization): `new-order-authorization` +- [更新宜搭子表单数据](https://open.dingtalk.com/document/development/api-updatesubtable): `api-updatesubtable` +- [更新指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-saveandupdatematrixdata): `api-saveandupdatematrixdata` +- [更新表单数据](https://open.dingtalk.com/document/development/api-updateformdata-v2): `api-updateformdata-v2` +- [查询宜搭应用列表](https://open.dingtalk.com/document/development/query-the-application-list): `query-the-application-list` +- [查询宜搭表单服务调用执行记录](https://open.dingtalk.com/document/development/the-query-should-be-based-on-the-execution-records-of): `the-query-should-be-based-on-the-execution-records-of` +- [查询抄送我的任务列表(应用维度)](https://open.dingtalk.com/document/development/query-copied-my-task-list-application-dimension): `query-copied-my-task-list-application-dimension` +- [查询流程运行任务(VPC)](https://open.dingtalk.com/document/development/query-process-running-tasks-vpc): `query-process-running-tasks-vpc` +- [查询用户详情](https://open.dingtalk.com/document/development/query-user-details): `query-user-details` +- [查询表单实例数据](https://open.dingtalk.com/document/development/api-searchformdatas-v2): `api-searchformdatas-v2` +- [查询表单数据](https://open.dingtalk.com/document/development/api-getformdatabyid-v2): `api-getformdatabyid-v2` +- [查询表单的变更记录](https://open.dingtalk.com/document/development/change-records-of-query-forms): `change-records-of-query-forms` +- [校验订单](https://open.dingtalk.com/document/development/verification-order): `verification-order` +- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/api-getinstancebyid-v2): `api-getinstancebyid-v2` +- [终止流程实例](https://open.dingtalk.com/document/development/terminate-a-process-instance): `terminate-a-process-instance` +- [获取任务列表(组织维度)](https://open.dingtalk.com/document/development/query-tasks-from-the-organization-dimension): `query-tasks-from-the-organization-dimension` +- [获取发送给用户的通知](https://open.dingtalk.com/document/development/get-notifications-sent-to-users): `get-notifications-sent-to-users` +- [获取员工组件的值](https://open.dingtalk.com/document/development/gets-the-value-of-the-employee-component): `gets-the-value-of-the-employee-component` +- [获取多个表单实例ID](https://open.dingtalk.com/document/development/api-searchformdataidlist-v2): `api-searchformdataidlist-v2` +- [获取子表组件数据](https://open.dingtalk.com/document/development/obtain-child-table-component-data): `obtain-child-table-component-data` +- [获取宜搭附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-free-access-address-of-yixian-accessories): `obtain-the-temporary-free-access-address-of-yixian-accessories` +- [获取实例ID列表](https://open.dingtalk.com/document/development/api-getinstanceidlist-v2): `api-getinstanceidlist-v2` +- [获取审批记录](https://open.dingtalk.com/document/development/queries-an-approval-record): `queries-an-approval-record` +- [获取指定宜搭角色的角色详情](https://open.dingtalk.com/document/development/get-roledetailbyid): `get-roledetailbyid` +- [获取指定应用下的表单列表](https://open.dingtalk.com/document/development/depending-on-the-application-id-to-get-the-form-list): `depending-on-the-application-id-to-get-the-form-list` +- [获取指定权限矩阵的明细数据](https://open.dingtalk.com/document/development/api-getmatrixdetailbyid): `api-getmatrixdetailbyid` +- [获取流程实例](https://open.dingtalk.com/document/development/api-getinstances-v2): `api-getinstances-v2` +- [获取流程设计结构](https://open.dingtalk.com/document/development/api-getprocessdesign): `api-getprocessdesign` +- [获取组件别名列表](https://open.dingtalk.com/document/development/api-getformcomponentaliaslist): `api-getformcomponentaliaslist` +- [获取组织内已完成的审批任务](https://open.dingtalk.com/document/development/obtains-the-completed-approval-tasks-in-an-organization): `obtains-the-completed-approval-tasks-in-an-organization` +- [获取组织内某人提交的任务](https://open.dingtalk.com/document/development/obtains-the-tasks-submitted-by-someone-in-an-organization): `obtains-the-tasks-submitted-by-someone-in-an-organization` +- [获取表单内的组件信息](https://open.dingtalk.com/document/development/get-form-field-information-based-on-form-uuid): `get-form-field-information-based-on-form-uuid` +- [获取表单组件定义列表](https://open.dingtalk.com/document/development/get-a-list-of-form-component-definitions): `get-a-list-of-form-component-definitions` +- [获取集成自动化日志详情](https://open.dingtalk.com/document/development/api-getautoflowlogdetail): `api-getautoflowlogdetail` +- [转交任务](https://open.dingtalk.com/document/development/transfer-tasks): `transfer-tasks` +- [退还商品](https://open.dingtalk.com/document/development/refund-of-goods): `refund-of-goods` +- [通过RowId更新子表单数据](https://open.dingtalk.com/document/development/update-the-subform-data-via-rowid): `update-the-subform-data-via-rowid` +- [通过流程code获取流程定义](https://open.dingtalk.com/document/development/obtain-definition-through-process-code): `obtain-definition-through-process-code` +- [通过表单实例数据批量更新表单实例](https://open.dingtalk.com/document/development/update-multiple-form-instances-with-the-form-instance-data): `update-multiple-form-instances-with-the-form-instance-data` +- [通过高级查询条件获取表单实例数据(不包括子表单组件数据)](https://open.dingtalk.com/document/development/obtain-form-instance-data-using-advanced-query-conditions-excluding-subform): `obtain-form-instance-data-using-advanced-query-conditions-excluding-subform` +- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/api-searchformdatasecondgeneration-v2): `api-searchformdatasecondgeneration-v2` +- [预览审批流程](https://open.dingtalk.com/document/development/api-previewpublishedprocess): `api-previewpublishedprocess` + +### 氚云(20 条) + +- [修改表单业务对象数据](https://open.dingtalk.com/document/development/modify-form-business-object-data): `modify-form-business-object-data` +- [创建流程实例](https://open.dingtalk.com/document/development/create-a-process-instance): `create-a-process-instance` +- [创建表单业务数据](https://open.dingtalk.com/document/development/create-form-business-data): `create-form-business-data` +- [删除业务对象](https://open.dingtalk.com/document/development/delete-a-business-object): `delete-a-business-object` +- [删除流程实例数据](https://open.dingtalk.com/document/development/delete-process-instance-data): `delete-process-instance-data` +- [取消流程实例](https://open.dingtalk.com/document/development/cancel-a-process-instance): `cancel-a-process-instance` +- [批量新增表单业务数据](https://open.dingtalk.com/document/development/batch-add-form-business-data): `batch-add-form-business-data` +- [查询流程实例](https://open.dingtalk.com/document/development/query-flow-instances): `query-flow-instances` +- [查询流程实例节点工作项](https://open.dingtalk.com/document/development/query-flow-instance-node-work-items): `query-flow-instance-node-work-items` +- [查询表单业务数据列表](https://open.dingtalk.com/document/development/querying-form-business-data): `querying-form-business-data` +- [获取业务实例信息](https://open.dingtalk.com/document/development/queries-business-instance-information): `queries-business-instance-information` +- [获取应用列表](https://open.dingtalk.com/document/development/queries-applications): `queries-applications` +- [获取应用功能节点](https://open.dingtalk.com/document/development/queries-the-application-feature-nodes): `queries-the-application-feature-nodes` +- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-2): `obtain-the-upload-url-of-a-file-2` +- [获取用户数据](https://open.dingtalk.com/document/development/obtain-user-data): `obtain-user-data` +- [获取组织数据](https://open.dingtalk.com/document/development/queries-organization-data): `queries-organization-data` +- [获取表单对象结构](https://open.dingtalk.com/document/development/gets-the-form-object-structure): `gets-the-form-object-structure` +- [获取角色数据](https://open.dingtalk.com/document/development/obtain-role-data): `obtain-role-data` +- [获取角色用户数据](https://open.dingtalk.com/document/development/historical-acquisition-of-role-user-data): `historical-acquisition-of-role-user-data` +- [获取附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-attachment-free-address): `obtain-the-temporary-attachment-free-address` + +### 多维表(14 条) + +- [列出多行记录](https://open.dingtalk.com/document/development/api-notable-listrecords): `api-notable-listrecords` +- [创建字段](https://open.dingtalk.com/document/development/api-noatable-createfield): `api-noatable-createfield` +- [创建数据表](https://open.dingtalk.com/document/development/api-createsheet): `api-createsheet` +- [删除多行记录](https://open.dingtalk.com/document/development/api-noatable-deleterecords): `api-noatable-deleterecords` +- [删除字段](https://open.dingtalk.com/document/development/api-noatable-deletefield): `api-noatable-deletefield` +- [删除数据表](https://open.dingtalk.com/document/development/api-noatable-deletesheet): `api-noatable-deletesheet` +- [新增记录](https://open.dingtalk.com/document/development/api-notable-insertrecords): `api-notable-insertrecords` +- [更新多行记录](https://open.dingtalk.com/document/development/api-noatable-updaterecords): `api-noatable-updaterecords` +- [更新字段](https://open.dingtalk.com/document/development/api-noatable-updatefield): `api-noatable-updatefield` +- [更新数据表](https://open.dingtalk.com/document/development/api-noatable-updatesheet): `api-noatable-updatesheet` +- [获取所有字段](https://open.dingtalk.com/document/development/api-noatable-getallfields): `api-noatable-getallfields` +- [获取所有数据表](https://open.dingtalk.com/document/development/api-notable-getallsheets): `api-notable-getallsheets` +- [获取数据表](https://open.dingtalk.com/document/development/api-notable-getsheet): `api-notable-getsheet` +- [获取记录](https://open.dingtalk.com/document/development/api-getrecord): `api-getrecord` + +### 数据中台 AMDP(4 条) + +- [组织变革主数据人员任职数据推送](https://open.dingtalk.com/document/development/api-amdpjobpositiondatapush): `api-amdpjobpositiondatapush` +- [组织变革主数据人员数据推送](https://open.dingtalk.com/document/development/api-amdpemployeedatapush): `api-amdpemployeedatapush` +- [组织变革主数据人员角色数据推送](https://open.dingtalk.com/document/development/api-amdpemproledatapush): `api-amdpemproledatapush` +- [组织变革主数据部门数据推送](https://open.dingtalk.com/document/development/api-amdporganizationdatapush): `api-amdporganizationdatapush` + +### 表单(3 条) + +- [获取单条填表实例详情](https://open.dingtalk.com/document/development/obtains-the-instance-details-of-a-single-fill-table): `obtains-the-instance-details-of-a-single-fill-table` +- [获取填表实例列表](https://open.dingtalk.com/document/development/obtain-the-table-filling-instance-list-data): `obtain-the-table-filling-instance-list-data` +- [获取用户创建的填表模板列表](https://open.dingtalk.com/document/development/new-obtains-the-template-that-a-user-creates): `new-obtains-the-template-that-a-user-creates` + +## 工作台(24 条) + +二级 Tab:平台介绍 / 使用教程(公开 URL 待 `doc_url_mapping` 灌入)。 + +### 微应用(13 条) + +- [创建企业内部应用](https://open.dingtalk.com/document/development/create-an-h5-application-for-your-enterprise): `create-an-h5-application-for-your-enterprise` +- [删除企业内部应用](https://open.dingtalk.com/document/development/delete-an-internal-h5-application): `delete-an-internal-h5-application` +- [发布企业内部小程序版本](https://open.dingtalk.com/document/development/release-internal-applet-version): `release-internal-applet-version` +- [回滚企业内部小程序版本](https://open.dingtalk.com/document/development/rollback-of-enterprise-internal-applet-version): `rollback-of-enterprise-internal-applet-version` +- [更新企业内部应用](https://open.dingtalk.com/document/development/update-internal-h5-applications): `update-internal-h5-applications` +- [更新企业内部应用的可使用范围](https://open.dingtalk.com/document/development/update-the-visible-range-of-micro-applications): `update-the-visible-range-of-micro-applications` +- [查询管理员是否有应用管理权限](https://open.dingtalk.com/document/development/check-whether-the-administrator-has-application-management-permissions): `check-whether-the-administrator-has-application-management-permissions` +- [获取企业内部小程序历史版本列表](https://open.dingtalk.com/document/development/obtain-the-list-of-historical-versions-of-enterprise-internal-applets): `obtain-the-list-of-historical-versions-of-enterprise-internal-applets` +- [获取企业内部小程序的版本列表](https://open.dingtalk.com/document/development/get-the-version-list-of-the-enterprise-internal-applet): `get-the-version-list-of-the-enterprise-internal-applet` +- [获取企业内部应用的可使用范围](https://open.dingtalk.com/document/development/obtains-the-application-visible-range): `obtains-the-application-visible-range` +- [获取企业内部所有应用列表](https://open.dingtalk.com/document/development/get-a-list-of-all-applications-inside-the-enterprise): `get-a-list-of-all-applications-inside-the-enterprise` +- [获取企业所有应用列表](https://open.dingtalk.com/document/development/obtains-a-list-of-all-enterprise-applications): `obtains-a-list-of-all-enterprise-applications` +- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/obtains-the-list-of-enterprise-applications-visible-to-a-user): `obtains-the-list-of-enterprise-applications-visible-to-a-user` + +### 应用角标(8 条) + +- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-badge-user-instance): `create-a-badge-user-instance` +- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-verification-result): `notification-dingtalk-badge-verification-result` +- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-dingtalk-user-instance): `update-dingtalk-user-instance` +- [解码钉工牌电子码](https://open.dingtalk.com/document/development/stack-dingtalk-badge): `stack-dingtalk-badge` +- [通知支付结果](https://open.dingtalk.com/document/development/sync-dingtalk-badge-code-payment-result): `sync-dingtalk-badge-code-payment-result` +- [通知退款结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-code-refund-result): `notification-dingtalk-badge-code-refund-result` +- [配置企业钉工牌](https://open.dingtalk.com/document/development/save-dingtalk-enterprise-instance): `save-dingtalk-enterprise-instance` +- [钉工牌通知消息](https://open.dingtalk.com/document/development/dingtalk-badge-notification-message): `dingtalk-badge-notification-message` + +### 工作台(3 条) + +- [批量添加最近使用应用](https://open.dingtalk.com/document/development/add-recently-used-apps-in-bulk): `add-recently-used-apps-in-bulk` +- [获取工作台插件权限点](https://open.dingtalk.com/document/development/obtain-the-permissions-of-the-workbench-plug-in): `obtain-the-permissions-of-the-workbench-plug-in` +- [获取工作台插件检验的规则信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-obtain-the-information-about): `you-can-call-this-operation-to-obtain-the-information-about` + +## 其它 / 未归类(557 条) + +### 未归类(557 条) + +- [**获取指定用户的所有父部门列表**](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-specified-user): `queries-all-parent-departments-of-a-specified-user` +- [**获取第三方企业应用的suite_access_token**](https://open.dingtalk.com/document/development/obtain-application-suite-ticket): `obtain-application-suite-ticket` +- [AI 助理发消息(回复消息模式)](https://open.dingtalk.com/document/development/ai-assistant-messages-reply-mode): `ai-assistant-messages-reply-mode` +- [AI 助理更新消息(主动发送模式)](https://open.dingtalk.com/document/development/the-ai-assistant-updates-active-message-sending-mode): `the-ai-assistant-updates-active-message-sending-mode` +- [AI 助理结束发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-finish): `api-finish` +- [AI 助理预备发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-prepare): `api-prepare` +- [AI助理发消息(主动发送模式)](https://open.dingtalk.com/document/development/ai-assistant-active-sends-messages-mode): `ai-assistant-active-sends-messages-mode` +- [AI助理响应接口-个人权限](https://open.dingtalk.com/document/development/api-assistantmeresponse): `api-assistantmeresponse` +- [AI助理响应接口-应用权限](https://open.dingtalk.com/document/development/api-assistantresponse): `api-assistantresponse` +- [ASR 一句话语音识别](https://open.dingtalk.com/document/development/asr-short-sentence-recognition): `asr-short-sentence-recognition` +- [OCR文字识别](https://open.dingtalk.com/document/development/structured-image-recognition-api): `structured-image-recognition-api` +- [groupId转换为groupKey](https://open.dingtalk.com/document/development/groupid-to-groupkey): `groupid-to-groupkey` +- [groupKey转换为groupId](https://open.dingtalk.com/document/development/convert-groupkey-to-groupid): `convert-groupkey-to-groupid` +- [上传媒体文件](https://open.dingtalk.com/document/development/upload-media-files): `upload-media-files` +- [上传打卡记录](https://open.dingtalk.com/document/development/upload-punch-records): `upload-punch-records` +- [上传文件块](https://open.dingtalk.com/document/development/upload-file-blocks): `upload-file-blocks` +- [下载审批附件](https://open.dingtalk.com/document/development/grants-the-permission-to-download-the-approval-file): `grants-the-permission-to-download-the-approval-file` +- [事件推送](https://open.dingtalk.com/document/development/dingtalk-iot-push-events): `dingtalk-iot-push-events` +- [企业活跃用户统计列表(部门维度)](https://open.dingtalk.com/document/development/query-the-statistics-of-active-users-in-a-department-of): `query-the-statistics-of-active-users-in-a-department-of` +- [使商品过期](https://open.dingtalk.com/document/development/make-goods-expire): `make-goods-expire` +- [使用服务助手推送消息](https://open.dingtalk.com/document/development/the-message-pushing-interface-of-the-assistant): `the-message-pushing-interface-of-the-assistant` +- [使用模板发送工作通知消息](https://open.dingtalk.com/document/development/work-notification-templating-send-notification-interface): `work-notification-templating-send-notification-interface` +- [保存文件到自定义或审批钉盘空间](https://open.dingtalk.com/document/development/add-file-to-user-s-dingtalk-disk): `add-file-to-user-s-dingtalk-disk` +- [保存日志内容](https://open.dingtalk.com/document/development/save-custom-log-content): `save-custom-log-content` +- [保存表单数据](https://open.dingtalk.com/document/development/save-form-data): `save-form-data` +- [修改发票配置](https://open.dingtalk.com/document/development/modify-invoice-configuration): `modify-invoice-configuration` +- [修改打卡时段设置](https://open.dingtalk.com/document/development/modify-card-settings): `modify-card-settings` +- [修改文件(夹)名](https://open.dingtalk.com/document/development/modify-the-file-and-folder-name): `modify-the-file-and-folder-name` +- [修改日程](https://open.dingtalk.com/document/development/schedule-2-0-modify-schedule): `schedule-2-0-modify-schedule` +- [修改日程参与者](https://open.dingtalk.com/document/development/schedule-2-0-participant-modification): `schedule-2-0-participant-modification` +- [修改权限](https://open.dingtalk.com/document/development/modify-pin-disk-permission-click): `modify-pin-disk-permission-click` +- [修改申请单](https://open.dingtalk.com/document/development/user-modify-approval-form): `user-modify-approval-form` +- [修改直播课程的可观看白名单](https://open.dingtalk.com/document/development/modify-the-whitelist-for-live-streaming-courses): `modify-the-whitelist-for-live-streaming-courses` +- [修改设备昵称](https://open.dingtalk.com/document/development/intelligent-hardware-device-nickname-modification): `intelligent-hardware-device-nickname-modification` +- [修改课程](https://open.dingtalk.com/document/development/modify-course): `modify-course` +- [修改项目](https://open.dingtalk.com/document/development/project-change): `project-change` +- [停用群模板](https://open.dingtalk.com/document/development/disable-a-group-template): `disable-a-group-template` +- [关闭互动卡片实例置顶](https://open.dingtalk.com/document/development/disable-the-sticky-card-setting): `disable-the-sticky-card-setting` +- [列出插件信息](https://open.dingtalk.com/document/development/query-plug-in-information): `query-plug-in-information` +- [创建AI助理](https://open.dingtalk.com/document/development/assistant-management-create-an-ai-assistant): `assistant-management-create-an-ai-assistant` +- [创建AI助理的运行任务](https://open.dingtalk.com/document/development/api-createassistantrun): `api-createassistantrun` +- [创建CRM自定义对象数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-create-custom-crm-object-data): `dingtalk-paas-master-create-custom-crm-object-data` +- [创建SSO企业账号](https://open.dingtalk.com/document/development/create-an-sso-account): `create-an-sso-account` +- [创建专属帐号用户](https://open.dingtalk.com/document/development/create-dedicated-accounts): `create-dedicated-accounts` +- [创建互动卡片实例](https://open.dingtalk.com/document/development/create-an-interactive-card-instance-2): `create-an-interactive-card-instance-2` +- [创建企业内部应用H5微应用](https://open.dingtalk.com/document/development/create-an-h5-microapplication): `create-an-h5-microapplication` +- [创建企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-customer-data): `dingtalk-paas-master-data-create-crm-customer-data` +- [创建公告](https://open.dingtalk.com/document/development/create-an-enterprise-announcement): `create-an-enterprise-announcement` +- [创建培训课程](https://open.dingtalk.com/document/development/create-live-courses): `create-live-courses` +- [创建学段](https://open.dingtalk.com/document/development/create-a-learning-segment): `create-a-learning-segment` +- [创建学科实例](https://open.dingtalk.com/document/development/create-dingtalk-education-subject-instance): `create-dingtalk-education-subject-instance` +- [创建实例](https://open.dingtalk.com/document/development/initiate-an-approval-process-without-a-process): `initiate-an-approval-process-without-a-process` +- [创建年级](https://open.dingtalk.com/document/development/create-grade): `create-grade` +- [创建待办事项](https://open.dingtalk.com/document/development/create-a-to-do-task): `create-a-to-do-task` +- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-or-update-approval-templates): `create-or-update-approval-templates` +- [创建或更新审批模板](https://open.dingtalk.com/document/development/save-approval-template): `save-approval-template` +- [创建日程](https://open.dingtalk.com/document/development/schedule-2-0-creation-interface): `schedule-2-0-creation-interface` +- [创建消息](https://open.dingtalk.com/document/development/api-createassistantmessage): `api-createassistantmessage` +- [创建班次](https://open.dingtalk.com/document/development/create-modify-shifts): `create-modify-shifts` +- [创建班级](https://open.dingtalk.com/document/development/create-a-class): `create-a-class` +- [创建用户](https://open.dingtalk.com/document/development/create-user): `create-user` +- [创建用户](https://open.dingtalk.com/document/development/user-information-creation): `user-information-creation` +- [创建线程](https://open.dingtalk.com/document/development/api-createassistantthread): `api-createassistantthread` +- [创建群](https://open.dingtalk.com/document/development/create-a-scene-group-v2): `create-a-scene-group-v2` +- [创建群](https://open.dingtalk.com/document/development/session-management-creates-groups): `session-management-creates-groups` +- [创建考勤组](https://open.dingtalk.com/document/development/attendance-group-write): `attendance-group-write` +- [创建联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-contact-data): `dingtalk-paas-master-data-create-crm-contact-data` +- [创建角色](https://open.dingtalk.com/document/development/address-book-add-role): `address-book-add-role` +- [创建角色组](https://open.dingtalk.com/document/development/add-a-role-group): `add-a-role-group` +- [创建课程](https://open.dingtalk.com/document/development/create-course): `create-course` +- [创建部门](https://open.dingtalk.com/document/development/address-book-creation-department-established-department): `address-book-creation-department-established-department` +- [创建部门](https://open.dingtalk.com/document/development/create-a-department): `create-a-department` +- [创建部门](https://open.dingtalk.com/document/development/industry-connection-department-creation): `industry-connection-department-creation` +- [创建钉钉自建企业账号](https://open.dingtalk.com/document/development/create-dingtalk-user-created-dedicated-account): `create-dingtalk-user-created-dedicated-account` +- [初始化假期余额](https://open.dingtalk.com/document/development/initialize-holiday-balance): `initialize-holiday-balance` +- [初始化家校架构](https://open.dingtalk.com/document/development/initialize-the-home-school-architecture): `initialize-the-home-school-architecture` +- [删除AI助理](https://open.dingtalk.com/document/development/assistant-management-deletes-ai-assistants): `assistant-management-deletes-ai-assistants` +- [删除AI助理的消息体](https://open.dingtalk.com/document/development/api-deleteassistantmessage): `api-deleteassistantmessage` +- [删除AI助理线程](https://open.dingtalk.com/document/development/api-deleteassistantthread): `api-deleteassistantthread` +- [删除H5微应用](https://open.dingtalk.com/document/development/delete-an-h5-microapplication): `delete-an-h5-microapplication` +- [删除企业客户数据](https://open.dingtalk.com/document/development/delete-crm-customer): `delete-crm-customer` +- [删除假期规则](https://open.dingtalk.com/document/development/api-for-deleting-holiday-types): `api-for-deleting-holiday-types` +- [删除公告](https://open.dingtalk.com/document/development/delete-announcements-based-on-the-announcement-id): `delete-announcements-based-on-the-announcement-id` +- [删除发票信息](https://open.dingtalk.com/document/development/delete-invoice-information): `delete-invoice-information` +- [删除回收站文件(夹)](https://open.dingtalk.com/document/development/delete-recycle-bin-files-folders): `delete-recycle-bin-files-folders` +- [删除图文卡片](https://open.dingtalk.com/document/development/delete-message-card): `delete-message-card` +- [删除培训课程](https://open.dingtalk.com/document/development/delete-live-training-courses): `delete-live-training-courses` +- [删除外部联系人](https://open.dingtalk.com/document/development/delete-external-contact): `delete-external-contact` +- [删除学科实例](https://open.dingtalk.com/document/development/delete-dingtalk-education-disciplines): `delete-dingtalk-education-disciplines` +- [删除序列](https://open.dingtalk.com/document/development/delete-sequence): `delete-sequence` +- [删除成本中心](https://open.dingtalk.com/document/development/delete-cost-center): `delete-cost-center` +- [删除成本中心人员信息](https://open.dingtalk.com/document/development/delete-the-personnel-information-of-the-cost-center): `delete-the-personnel-information-of-the-cost-center` +- [删除文件(夹)](https://open.dingtalk.com/document/development/delete-objects): `delete-objects` +- [删除文章](https://open.dingtalk.com/document/development/delete-article-1): `delete-article-1` +- [删除权限](https://open.dingtalk.com/document/development/delete-the-pin-disk-permission): `delete-the-pin-disk-permission` +- [删除模板](https://open.dingtalk.com/document/development/delete-a-template): `delete-a-template` +- [删除班次](https://open.dingtalk.com/document/development/delete-shift): `delete-shift` +- [删除用户](https://open.dingtalk.com/document/development/delete-a-member): `delete-a-member` +- [删除用户](https://open.dingtalk.com/document/development/delete-a-user): `delete-a-user` +- [删除群成员](https://open.dingtalk.com/document/development/scene-group-delete): `scene-group-delete` +- [删除考勤组](https://open.dingtalk.com/document/development/delete-attendance-group): `delete-attendance-group` +- [删除联系人数据](https://open.dingtalk.com/document/development/delete-crm-contact): `delete-crm-contact` +- [删除角色](https://open.dingtalk.com/document/development/delete-role-information): `delete-role-information` +- [删除设备](https://open.dingtalk.com/document/development/delete-a-device): `delete-a-device` +- [删除课程](https://open.dingtalk.com/document/development/delete-course): `delete-course` +- [删除部门](https://open.dingtalk.com/document/development/address-book-deletion-department): `address-book-deletion-department` +- [删除部门](https://open.dingtalk.com/document/development/delete-a-department): `delete-a-department` +- [删除项目](https://open.dingtalk.com/document/development/delete-a-project): `delete-a-project` +- [剪辑直播课程回放](https://open.dingtalk.com/document/development/clip-live-course-playback): `clip-live-course-playback` +- [加入课程](https://open.dingtalk.com/document/development/join-course): `join-course` +- [助理删除知识](https://open.dingtalk.com/document/development/api-deleteknowledge): `api-deleteknowledge` +- [助理学习知识](https://open.dingtalk.com/document/development/api-learnknowledge): `api-learnknowledge` +- [助理重新学习](https://open.dingtalk.com/document/development/api-relearnknowledge): `api-relearnknowledge` +- [单步文件上传](https://open.dingtalk.com/document/development/single-step-file-upload): `single-step-file-upload` +- [发布商品](https://open.dingtalk.com/document/development/release-products): `release-products` +- [发布文章](https://open.dingtalk.com/document/development/article-publishing-interface-1): `article-publishing-interface-1` +- [发起宜搭审批流程](https://open.dingtalk.com/document/development/initiate-the-approval-process): `initiate-the-approval-process` +- [发起审批实例](https://open.dingtalk.com/document/development/oa-approval-initiates-approval-instances): `oa-approval-initiates-approval-instances` +- [发送工作通知](https://open.dingtalk.com/document/development/asynchronous-sending-of-enterprise-session-messages): `asynchronous-sending-of-enterprise-session-messages` +- [发送普通消息](https://open.dingtalk.com/document/development/send-normal-messages-1): `send-normal-messages-1` +- [发送消息到企业群](https://open.dingtalk.com/document/development/send-group-messages): `send-group-messages` +- [发送群助手消息](https://open.dingtalk.com/document/development/group-template-robot-message): `group-template-robot-message` +- [发送钉盘文件给指定用户](https://open.dingtalk.com/document/development/sends-a-file-to-a-specified-user): `sends-a-file-to-a-specified-user` +- [取消日程](https://open.dingtalk.com/document/development/schedule-2-0-cancel-schedule): `schedule-2-0-cancel-schedule` +- [变配租户信息](https://open.dingtalk.com/document/development/change-tenant-information-1): `change-tenant-information-1` +- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/execute-approval-operation-with-attachment): `execute-approval-operation-with-attachment` +- [启用群模板](https://open.dingtalk.com/document/development/enable-a-group-template): `enable-a-group-template` +- [商旅成本中心转换为外部成本中心](https://open.dingtalk.com/document/development/business-travel-cost-center-converted-to-external-cost-center): `business-travel-cost-center-converted-to-external-cost-center` +- [回放课程](https://open.dingtalk.com/document/development/replay-course): `replay-course` +- [多渠道新购校验](https://open.dingtalk.com/document/development/multi-channel-new-purchase-verification): `multi-channel-new-purchase-verification` +- [学习推荐数据回流](https://open.dingtalk.com/document/development/learn-to-recommend-data-backflow): `learn-to-recommend-data-backflow` +- [家庭Feed同步](https://open.dingtalk.com/document/development/dingtalk-education-family-feed-synchronization): `dingtalk-education-family-feed-synchronization` +- [将助理技能发布到组织技能库](https://open.dingtalk.com/document/development/api-addtoorgskillrepository): `api-addtoorgskillrepository` +- [应用内购商品核销](https://open.dingtalk.com/document/development/application-of-in-house-purchase-verification): `application-of-in-house-purchase-verification` +- [开启互动卡片实例置顶](https://open.dingtalk.com/document/development/enable-the-interactive-card-setting): `enable-the-interactive-card-setting` +- [开启分块上传事务](https://open.dingtalk.com/document/development/enable-upload-transaction): `enable-upload-transaction` +- [开始课程](https://open.dingtalk.com/document/development/start-course): `start-course` +- [执行宜搭的审批任务](https://open.dingtalk.com/document/development/execute-appropriate-approval-tasks): `execute-appropriate-approval-tasks` +- [执行自定义API](https://open.dingtalk.com/document/development/run-custom-api): `run-custom-api` +- [批量修改设备](https://open.dingtalk.com/document/development/batch-modify-devices): `batch-modify-devices` +- [批量删除参与考勤人员](https://open.dingtalk.com/document/development/batch-delete-employees-under-the-attendance-group): `batch-delete-employees-under-the-attendance-group` +- [批量删除员工角色](https://open.dingtalk.com/document/development/delete-the-color-information-of-employee-corners-in-batches): `delete-the-color-information-of-employee-corners-in-batches` +- [批量删除地点](https://open.dingtalk.com/document/development/delete-position-in-batches-under-the-attendance-group): `delete-position-in-batches-under-the-attendance-group` +- [批量发起回调](https://open.dingtalk.com/document/development/initiate-multiple-callbacks): `initiate-multiple-callbacks` +- [批量取消待办](https://open.dingtalk.com/document/development/cancel-multiple-tasks): `cancel-multiple-tasks` +- [批量增加员工角色](https://open.dingtalk.com/document/development/add-role-information-to-employees-in-batches): `add-role-information-to-employees-in-batches` +- [批量新增Wi-Fi信息](https://open.dingtalk.com/document/development/batch-add-wifi-under-attendance-group): `batch-add-wifi-under-attendance-group` +- [批量新增参与考勤人员](https://open.dingtalk.com/document/development/batch-add-employees-under-the-attendance-group): `batch-add-employees-under-the-attendance-group` +- [批量新增地点](https://open.dingtalk.com/document/development/atch-add-position-under-attendance-group): `atch-add-position-under-attendance-group` +- [批量更新假期余额](https://open.dingtalk.com/document/development/bulk-update-holiday-balance): `bulk-update-holiday-balance` +- [批量更新实例状态](https://open.dingtalk.com/document/development/update-the-status-of-multiple-instances-at-a-time): `update-the-status-of-multiple-instances-at-a-time` +- [批量查询Wi-Fi信息](https://open.dingtalk.com/document/development/batch-query-wifi-under-attendance-group): `batch-query-wifi-under-attendance-group` +- [批量查询人员排班信息](https://open.dingtalk.com/document/development/query-batch-scheduling-information): `query-batch-scheduling-information` +- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/query-holiday-consumption-records): `query-holiday-consumption-records` +- [批量查询地点](https://open.dingtalk.com/document/development/batch-query-position-under-attendance-group): `batch-query-position-under-attendance-group` +- [批量查询成员排班概要信息](https://open.dingtalk.com/document/development/query-scheduling-summary-information): `query-scheduling-summary-information` +- [批量注册事件类型](https://open.dingtalk.com/document/development/registration-event-type): `registration-event-type` +- [批量注册设备](https://open.dingtalk.com/document/development/batchregister-devices): `batchregister-devices` +- [批量注册设备](https://open.dingtalk.com/document/development/industry-connection-device-batch-registration): `industry-connection-device-batch-registration` +- [批量移除Wi-Fi信息](https://open.dingtalk.com/document/development/batch-remove-wifi-under-attendance-group): `batch-remove-wifi-under-attendance-group` +- [批量获取企业客户数据](https://open.dingtalk.com/document/development/obtains-customer-data-in-batches-based-on-the-id-list): `obtains-customer-data-in-batches-based-on-the-id-list` +- [批量获取客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-customer-data-search-and-query-interface): `dingtalk-paas-master-data-customer-data-search-and-query-interface` +- [批量获取应用信息](https://open.dingtalk.com/document/development/queries-the-information-about-multiple-applications): `queries-the-information-about-multiple-applications` +- [批量获取服务窗联系人数据](https://open.dingtalk.com/document/development/obtain-contact-data-from-the-service-window): `obtain-contact-data-from-the-service-window` +- [批量获取考勤组摘要](https://open.dingtalk.com/document/development/batch-query-of-simple-information-of-the-attendance-group): `batch-query-of-simple-information-of-the-attendance-group` +- [批量获取考勤组详情](https://open.dingtalk.com/document/development/batch-obtain-attendance-group-details): `batch-obtain-attendance-group-details` +- [批量获取钉钉运动数据](https://open.dingtalk.com/document/development/queries-the-number-of-dingtalk-movement-steps-of-multiple-users): `queries-the-number-of-dingtalk-movement-steps-of-multiple-users` +- [拷贝文件(夹)](https://open.dingtalk.com/document/development/copy-files-folders): `copy-files-folders` +- [按名称搜索班次](https://open.dingtalk.com/document/development/search-shifts-by-rank): `search-shifts-by-rank` +- [按照ID列表批量获取CRM自定义表单数据](https://open.dingtalk.com/document/development/retrieves-custom-crm-forms-from-the-id-list): `retrieves-custom-crm-forms-from-the-id-list` +- [按照ID列表批量获取联系人数据](https://open.dingtalk.com/document/development/retrieves-contact-data-in-batches-based-on-the-id-list): `retrieves-contact-data-in-batches-based-on-the-id-list` +- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/approve-nail-disk-file-authorization): `approve-nail-disk-file-authorization` +- [授权用户访问企业的自定义空间](https://open.dingtalk.com/document/development/authorize-a-user-to-access-a-custom-workspace-of-an): `authorize-a-user-to-access-a-custom-workspace-of-an` +- [授权预览审批附件](https://open.dingtalk.com/document/development/preview-authorization-attachment): `preview-authorization-attachment` +- [排班制考勤组排班](https://open.dingtalk.com/document/development/scheduling-system-attendance-group-scheduling): `scheduling-system-attendance-group-scheduling` +- [提交文件上传事务](https://open.dingtalk.com/document/development/submit-a-file-upload-transaction): `submit-a-file-upload-transaction` +- [搜索考勤组摘要](https://open.dingtalk.com/document/development/attendance-group-search): `attendance-group-search` +- [撤回工作通知消息](https://open.dingtalk.com/document/development/notification-of-work-withdrawal): `notification-of-work-withdrawal` +- [撤销审批实例](https://open.dingtalk.com/document/development/terminate-a-workflow-by-using-an-instance-id): `terminate-a-workflow-by-using-an-instance-id` +- [新增发票配置](https://open.dingtalk.com/document/development/new-invoice-configuration): `new-invoice-configuration` +- [新增图文卡片](https://open.dingtalk.com/document/development/new-message-card-1): `new-message-card-1` +- [新增或更新表单实例](https://open.dingtalk.com/document/development/add-or-update-form-instances): `add-or-update-form-instances` +- [新增文章](https://open.dingtalk.com/document/development/new-article-1): `new-article-1` +- [新增服务号](https://open.dingtalk.com/document/development/added-service-number): `added-service-number` +- [新增群成员](https://open.dingtalk.com/document/development/add-people-to-scene-group): `add-people-to-scene-group` +- [新增钉钉待办任务](https://open.dingtalk.com/document/development/new-to-do-items): `new-to-do-items` +- [新购宜搭产品](https://open.dingtalk.com/document/development/suitable-for-new-purchase): `suitable-for-new-purchase` +- [更新 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-updateassistantscope): `api-updateassistantscope` +- [更新AI助理基础信息](https://open.dingtalk.com/document/development/api-updateassistantbasicinfo): `api-updateassistantbasicinfo` +- [更新企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-microapplication): `set-the-visible-range-of-the-microapplication` +- [更新企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-customer-data): `dingtalk-paas-master-data-update-crm-customer-data` +- [更新企业账号用户信息](https://open.dingtalk.com/document/development/update-dedicated-accounts-information): `update-dedicated-accounts-information` +- [更新假期规则](https://open.dingtalk.com/document/development/holiday-type-update): `holiday-type-update` +- [更新公告](https://open.dingtalk.com/document/development/modify-the-announcement-according-to-the-announcement-id): `modify-the-announcement-according-to-the-announcement-id` +- [更新参与考勤人员](https://open.dingtalk.com/document/development/attendance-group-member-update): `attendance-group-member-update` +- [更新员工花名册](https://open.dingtalk.com/document/development/update-employee-roster): `update-employee-roster` +- [更新员工花名册信息](https://open.dingtalk.com/document/development/intelligent-personnel-update-employee-file-information): `intelligent-personnel-update-employee-file-information` +- [更新图文卡片](https://open.dingtalk.com/document/development/message-card-material-update-interface): `message-card-material-update-interface` +- [更新外部联系人](https://open.dingtalk.com/document/development/update-enterprise-external-contacts): `update-enterprise-external-contacts` +- [更新学科实例](https://open.dingtalk.com/document/development/update-dingtalk-education-instance): `update-dingtalk-education-instance` +- [更新实例状态](https://open.dingtalk.com/document/development/to-do-instance-status): `to-do-instance-status` +- [更新工作通知状态栏](https://open.dingtalk.com/document/development/update-work-notification-status-bar): `update-work-notification-status-bar` +- [更新待办状态](https://open.dingtalk.com/document/development/update-to-do-task-status): `update-to-do-task-status` +- [更新文章](https://open.dingtalk.com/document/development/save-article-details-1): `save-article-details-1` +- [更新服务号](https://open.dingtalk.com/document/development/service-number-update-1): `service-number-update-1` +- [更新流程实例](https://open.dingtalk.com/document/development/update-process-instance-yida): `update-process-instance-yida` +- [更新状态](https://open.dingtalk.com/document/development/update-status): `update-status` +- [更新用户信息](https://open.dingtalk.com/document/development/update-user-details): `update-user-details` +- [更新用户信息](https://open.dingtalk.com/document/development/user-information-update): `user-information-update` +- [更新申请单状态](https://open.dingtalk.com/document/development/update-approval-form): `update-approval-form` +- [更新群](https://open.dingtalk.com/document/development/modify-a-group-session): `modify-a-group-session` +- [更新群](https://open.dingtalk.com/document/development/scene-group-update): `scene-group-update` +- [更新群成员的群昵称](https://open.dingtalk.com/document/development/set-a-group-nickname): `set-a-group-nickname` +- [更新群管理员](https://open.dingtalk.com/document/development/set-chat-admin): `set-chat-admin` +- [更新考勤组](https://open.dingtalk.com/document/development/attendance-group-update-interface): `attendance-group-update-interface` +- [更新联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-contact-data): `dingtalk-paas-master-data-update-crm-contact-data` +- [更新自定义对象数据](https://open.dingtalk.com/document/development/crm-master-data-opens-interface-for-updating-custom-object-data): `crm-master-data-opens-interface-for-updating-custom-object-data` +- [更新表单数据](https://open.dingtalk.com/document/development/update-form-data): `update-form-data` +- [更新角色名称](https://open.dingtalk.com/document/development/update-the-character-name): `update-the-character-name` +- [更新部门](https://open.dingtalk.com/document/development/address-book-update-department): `address-book-update-department` +- [更新部门](https://open.dingtalk.com/document/development/update-a-department): `update-a-department` +- [更新部门扩展信息](https://open.dingtalk.com/document/development/department-update-extension-information): `department-update-extension-information` +- [更新钉钉待办任务](https://open.dingtalk.com/document/development/update-to-do-status): `update-to-do-status` +- [服务号菜单更新](https://open.dingtalk.com/document/development/service-number-menu-update): `service-number-menu-update` +- [服务商获取第三方应用授权企业的access_token](https://open.dingtalk.com/document/development/obtain-isvapp-token): `obtain-isvapp-token` +- [机票城市搜索](https://open.dingtalk.com/document/development/air-ticket-city-search): `air-ticket-city-search` +- [查询 AI 助理基本信息](https://open.dingtalk.com/document/development/api-retrieveassistantbasicinfo): `api-retrieveassistantbasicinfo` +- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/get-the-user-s-to-do-items): `get-the-user-s-to-do-items` +- [查询企业个人待办数量](https://open.dingtalk.com/document/development/query-the-number-of-to-do-tasks-of-the-enterprise): `query-the-number-of-to-do-tasks-of-the-enterprise` +- [查询企业级别](https://open.dingtalk.com/document/development/query-enterprise-level): `query-enterprise-level` +- [查询企业考勤排班详情](https://open.dingtalk.com/document/development/interface-for-daily-full-query-of-attendance-scheduling-information): `interface-for-daily-full-query-of-attendance-scheduling-information` +- [查询企业账号用户详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-dedicated-account): `queries-the-details-of-a-dedicated-account` +- [查询企业通讯录未激活用户列表](https://open.dingtalk.com/document/development/queries-the-list-of-inactive-accounts-in-the-key-account): `queries-the-list-of-inactive-accounts-in-the-key-account` +- [查询假期规则列表](https://open.dingtalk.com/document/development/holiday-type-query): `holiday-type-query` +- [查询历史班次](https://open.dingtalk.com/document/development/query-history-shifts): `query-history-shifts` +- [查询参与考勤人员列表](https://open.dingtalk.com/document/development/batch-query-of-employees-in-the-attendance-group): `batch-query-of-employees-in-the-attendance-group` +- [查询可用发票列表](https://open.dingtalk.com/document/development/query-available-invoices): `query-available-invoices` +- [查询员工智能考勤机列表](https://open.dingtalk.com/document/development/query-the-list-of-employee-intelligent-attendance-machines): `query-the-list-of-employee-intelligent-attendance-machines` +- [查询商品列表](https://open.dingtalk.com/document/development/query-product-lists): `query-product-lists` +- [查询回收站文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-recycle-bin-folder-list): `obtain-the-recycle-bin-folder-list` +- [查询图文卡片列表](https://open.dingtalk.com/document/development/query-message-card-list): `query-message-card-list` +- [查询家庭孩子信息](https://open.dingtalk.com/document/development/query-family-child-information): `query-family-child-information` +- [查询应用信息列表](https://open.dingtalk.com/document/development/queries-application-information): `queries-application-information` +- [查询待办列表](https://open.dingtalk.com/document/development/query-a-user-s-to-do-items): `query-a-user-s-to-do-items` +- [查询成本中心](https://open.dingtalk.com/document/development/query-cost-center): `query-cost-center` +- [查询排班打卡结果](https://open.dingtalk.com/document/development/query-the-results-of-a-batch-of-tasks): `query-the-results-of-a-batch-of-tasks` +- [查询插件信息列表](https://open.dingtalk.com/document/development/query-plug-ins): `query-plug-ins` +- [查询文件(夹)信息](https://open.dingtalk.com/document/development/obtain-file-information): `obtain-file-information` +- [查询文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-file-list): `obtain-the-file-list` +- [查询文档模板](https://open.dingtalk.com/document/development/query-a-document-template): `query-a-document-template` +- [查询文章列表](https://open.dingtalk.com/document/development/query-the-article-list): `query-the-article-list` +- [查询是否启用智能统计报表](https://open.dingtalk.com/document/development/determine-whether-to-enable-attendance-intelligent-report): `determine-whether-to-enable-attendance-intelligent-report` +- [查询服务号列表](https://open.dingtalk.com/document/development/query-service-number-list): `query-service-number-list` +- [查询服务号菜单](https://open.dingtalk.com/document/development/query-service-number-menu-1): `query-service-number-menu-1` +- [查询服务号详情](https://open.dingtalk.com/document/development/inquire-about-service-number-details): `inquire-about-service-number-details` +- [查询激活码](https://open.dingtalk.com/document/development/query-activation-code): `query-activation-code` +- [查询用户是否参与企业步数排行榜](https://open.dingtalk.com/document/development/check-whether-dingtalk-is-enabled): `check-whether-dingtalk-is-enabled` +- [查询用户详情](https://open.dingtalk.com/document/development/queries-user-details): `queries-user-details` +- [查询直播的观看数据](https://open.dingtalk.com/document/development/queries-the-playback-data-of-a-live-stream): `queries-the-playback-data-of-a-live-stream` +- [查询直播课程的可观看白名单](https://open.dingtalk.com/document/development/query-the-whitelist-of-live-courses): `query-the-whitelist-of-live-courses` +- [查询群信息](https://open.dingtalk.com/document/development/obtain-a-group-session): `obtain-a-group-session` +- [查询群信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-a-scenario-group): `queries-the-basic-information-of-a-scenario-group` +- [查询群消息已读人员列表](https://open.dingtalk.com/document/development/queries-the-list-of-people-who-have-read-a-group): `queries-the-list-of-people-who-have-read-a-group` +- [查询表单实例数据](https://open.dingtalk.com/document/development/querying-form-instance-data): `querying-form-instance-data` +- [查询表单数据](https://open.dingtalk.com/document/development/query-form-data): `query-form-data` +- [查询设备列表](https://open.dingtalk.com/document/development/intelligent-hardware-list-query): `intelligent-hardware-list-query` +- [查询设备详情](https://open.dingtalk.com/document/development/intelligent-hardware-device-query): `intelligent-hardware-device-query` +- [查询请假状态](https://open.dingtalk.com/document/development/query-status): `query-status` +- [查询销售用户信息](https://open.dingtalk.com/document/development/query-sales-user-information): `query-sales-user-information` +- [查询项目中文件操作日志](https://open.dingtalk.com/document/development/query-file-operation-logs-of-a-project): `query-file-operation-logs-of-a-project` +- [查询预估价](https://open.dingtalk.com/document/development/query-estimated-price): `query-estimated-price` +- [校验变配](https://open.dingtalk.com/document/development/verify-configuration): `verify-configuration` +- [校验用户是否在当前考勤组](https://open.dingtalk.com/document/development/query-members-by-id): `query-members-by-id` +- [校验订单的升级](https://open.dingtalk.com/document/development/upgrade-the-verification-order): `upgrade-the-verification-order` +- [校验订单的升级状态](https://open.dingtalk.com/document/development/upgrade-status-of-the-verification-order): `upgrade-status-of-the-verification-order` +- [根据ID列表批量获取跟进记录数据](https://open.dingtalk.com/document/development/dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you): `dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you` +- [根据groupKey查询考勤组信息](https://open.dingtalk.com/document/development/queries-attendance-group-information-by-id): `queries-attendance-group-information-by-id` +- [根据sns临时授权码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-user-information-based-on-the-sns-temporary-authorization): `obtain-the-user-information-based-on-the-sns-temporary-authorization` +- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/query-a-user-by-the-union-id): `query-a-user-by-the-union-id` +- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-userids-of): `you-can-call-this-operation-to-retrieve-the-userids-of` +- [根据手机号查询企业账号用户](https://open.dingtalk.com/document/development/obtain-the-userid-of-your-mobile-phone-number): `obtain-the-userid-of-your-mobile-phone-number` +- [根据手机号查询用户](https://open.dingtalk.com/document/development/retrieve-userid-from-mobile-phone-number): `retrieve-userid-from-mobile-phone-number` +- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/dingtalk-the-contact-data-query-api): `dingtalk-the-contact-data-query-api` +- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/retrieve-custom-crm-object-data): `retrieve-custom-crm-object-data` +- [根据指定条件查询跟进记录数据](https://open.dingtalk.com/document/development/query-and-dingtalk-data-of-track-records-in-apsara-stack): `query-and-dingtalk-data-of-track-records-in-apsara-stack` +- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/queries-a-process-instance-based-on-its-id): `queries-a-process-instance-based-on-its-id` +- [根据设备ID查询设备](https://open.dingtalk.com/document/development/the-smart-hardware-can-query-details-based-on-the-device): `the-smart-hardware-can-query-details-based-on-the-device` +- [检索AI助理线程](https://open.dingtalk.com/document/development/api-retrieveassistantthread): `api-retrieveassistantthread` +- [注册互动卡片回调地址](https://open.dingtalk.com/document/development/registration-card-interaction-callback-address-1): `registration-card-interaction-callback-address-1` +- [注册单个设备](https://open.dingtalk.com/document/development/industry-connection-single-device-registration): `industry-connection-single-device-registration` +- [注册设备](https://open.dingtalk.com/document/development/register-devices): `register-devices` +- [注册账号](https://open.dingtalk.com/document/development/register-an-account): `register-an-account` +- [消息撤回](https://open.dingtalk.com/document/development/service-number-message-withdrawal): `service-number-message-withdrawal` +- [消息群发](https://open.dingtalk.com/document/development/interactive-service-window-group-message-sending-interface): `interactive-service-window-group-message-sending-interface` +- [添加企业待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-through-intelligent-personnel): `add-employees-to-be-hired-through-intelligent-personnel` +- [添加假期规则](https://open.dingtalk.com/document/development/holiday-type-added): `holiday-type-added` +- [添加外部联系人](https://open.dingtalk.com/document/development/add-enterprise-external-contacts): `add-enterprise-external-contacts` +- [添加学生](https://open.dingtalk.com/document/development/add-student): `add-student` +- [添加审批评论](https://open.dingtalk.com/document/development/add-an-approval-comment): `add-an-approval-comment` +- [添加家长](https://open.dingtalk.com/document/development/add-parent): `add-parent` +- [添加文件(夹)](https://open.dingtalk.com/document/development/add-file-and-folder): `add-file-and-folder` +- [添加权限](https://open.dingtalk.com/document/development/add-pin-disk-permission): `add-pin-disk-permission` +- [添加老师](https://open.dingtalk.com/document/development/add-teacher): `add-teacher` +- [添加自定义空间权限](https://open.dingtalk.com/document/development/add-custom-workspace-permissions): `add-custom-workspace-permissions` +- [添加课程参与方](https://open.dingtalk.com/document/development/add-course-participants): `add-course-participants` +- [添加项目](https://open.dingtalk.com/document/development/add-a-project): `add-a-project` +- [清理审批数据](https://open.dingtalk.com/document/development/clean-up-workflow-data): `clean-up-workflow-data` +- [清空回收站](https://open.dingtalk.com/document/development/empty-recycle-bin-files-folders): `empty-recycle-bin-files-folders` +- [火车票城市搜索](https://open.dingtalk.com/document/development/train-ticket-city-search): `train-ticket-city-search` +- [移动文件(夹)](https://open.dingtalk.com/document/development/move-file-and-folder): `move-file-and-folder` +- [移除助理组织技能库技能](https://open.dingtalk.com/document/development/api-removefromorgskillrepository): `api-removefromorgskillrepository` +- [移除租户资源](https://open.dingtalk.com/document/development/remove-tenant-resources): `remove-tenant-resources` +- [移除课程参与方](https://open.dingtalk.com/document/development/remove-course-participants): `remove-course-participants` +- [终止阿里云授权](https://open.dingtalk.com/document/development/terminate-authorization-for-alibaba-cloud-services): `terminate-authorization-for-alibaba-cloud-services` +- [绑定设备](https://open.dingtalk.com/document/development/establishing-a-binding-relationship-between-intelligent-hardware-and-cloud): `establishing-a-binding-relationship-between-intelligent-hardware-and-cloud` +- [结束课程](https://open.dingtalk.com/document/development/end-course): `end-course` +- [统计企业活跃用户](https://open.dingtalk.com/document/development/query-for-dau-statistics): `query-for-dau-statistics` +- [续费服务订单](https://open.dingtalk.com/document/development/renewal-service-order): `renewal-service-order` +- [续费租户](https://open.dingtalk.com/document/development/renewal-tenant): `renewal-tenant` +- [自定义机器人发送群消息](https://open.dingtalk.com/document/development/custom-robots-send-group-messages): `custom-robots-send-group-messages` +- [获取 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-retrieveassistantscope): `api-retrieveassistantscope` +- [获取AI助理对话明细列表](https://open.dingtalk.com/document/development/api-loglist): `api-loglist` +- [获取AI助理的消息体](https://open.dingtalk.com/document/development/api-retrieveassistantmessage): `api-retrieveassistantmessage` +- [获取AI助理的消息列表](https://open.dingtalk.com/document/development/api-listassistantmessage): `api-listassistantmessage` +- [获取AI助理的运行任务](https://open.dingtalk.com/document/development/api-retrieveassistantrun): `api-retrieveassistantrun` +- [获取AI助理的运行任务的列表](https://open.dingtalk.com/document/development/api-listassistantrun): `api-listassistantrun` +- [获取jsapi_ticket](https://open.dingtalk.com/document/development/obtain-jsapi-ticket): `obtain-jsapi-ticket` +- [获取个人或部门钉钉运动数据](https://open.dingtalk.com/document/development/queries-individual-or-department-dingtalk-exercise-steps): `queries-individual-or-department-dingtalk-exercise-steps` +- [获取主干组织列表](https://open.dingtalk.com/document/development/obtain-backbone-organization-list): `obtain-backbone-organization-list` +- [获取互动服务窗相关数据](https://open.dingtalk.com/document/development/queries-the-data-about-the-interactive-service-window): `queries-the-data-about-the-interactive-service-window` +- [获取人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-home-school-user-identities): `obtains-a-list-of-home-school-user-identities` +- [获取人员详情](https://open.dingtalk.com/document/development/obtain-the-identity-details-of-home-school-personnel): `obtain-the-identity-details-of-home-school-personnel` +- [获取企业DING使用数据](https://open.dingtalk.com/document/development/enterprise-ding-quantity-statistics): `enterprise-ding-quantity-statistics` +- [获取企业DING使用数据(部门维度)](https://open.dingtalk.com/document/development/query-the-departmental-transmission-status-of-key-clients): `query-the-departmental-transmission-status-of-key-clients` +- [获取企业DING发送统计数据](https://open.dingtalk.com/document/development/obtain-sending-statistics-of-an-enterprise-ding): `obtain-sending-statistics-of-an-enterprise-ding` +- [获取企业DING接收及评论统计数据](https://open.dingtalk.com/document/development/obtain-statistics-on-receiving-and-comments-of-enterprise-ding): `obtain-statistics-on-receiving-and-comments-of-enterprise-ding` +- [获取企业下的自定义空间](https://open.dingtalk.com/document/development/obtain-user-space-under-the-enterprise): `obtain-user-space-under-the-enterprise` +- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-information): `obtain-enterprise-information` +- [获取企业全员圈统计数据](https://open.dingtalk.com/document/development/obtains-the-statistical-data-of-all-employees-of-an-enterprise): `obtains-the-statistical-data-of-all-employees-of-an-enterprise` +- [获取企业公告统计数据](https://open.dingtalk.com/document/development/queries-corporate-announcement-statistics): `queries-corporate-announcement-statistics` +- [获取企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/gets-the-microapplication-visible-range-set-by-the-enterprise): `gets-the-microapplication-visible-range-set-by-the-enterprise` +- [获取企业内部应用的access_token](https://open.dingtalk.com/document/development/obtain-orgapp-token): `obtain-orgapp-token` +- [获取企业单聊统计数据](https://open.dingtalk.com/document/development/queries-the-statistics-on-one-time-enterprise-chats): `queries-the-statistics-on-one-time-enterprise-chats` +- [获取企业发布智能填表数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-of-tables-published-in-an-organization): `queries-the-number-of-tables-published-in-an-organization` +- [获取企业发红包统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-red-packets-issued-by-enterprises): `obtains-the-statistics-on-red-packets-issued-by-enterprises` +- [获取企业各类群组创建情况](https://open.dingtalk.com/document/development/api-for-obtaining-the-creation-status-of-various-groups): `api-for-obtaining-the-creation-status-of-various-groups` +- [获取企业员工类型统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-employee-types): `obtains-statistics-on-employee-types` +- [获取企业商旅酒店订单数据](https://open.dingtalk.com/document/development/enterprises-obtain-order-data-for-business-hotels): `enterprises-obtain-order-data-for-business-hotels` +- [获取企业审批统计数据](https://open.dingtalk.com/document/development/obtains-enterprise-approval-statistics): `obtains-enterprise-approval-statistics` +- [获取企业客户的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-customer-object): `get-metadata-description-of-crm-customer-object` +- [获取企业已经加入的或申请加入中的上下游组织的信息](https://open.dingtalk.com/document/development/obtains-information-about-the-workspaces-that-the-enterprise-has-joined): `obtains-information-about-the-workspaces-that-the-enterprise-has-joined` +- [获取企业应用访问情况](https://open.dingtalk.com/document/development/queries-the-daily-usage-summary-of-microapplications-in-an-enterprise): `queries-the-daily-usage-summary-of-microapplications-in-an-enterprise` +- [获取企业待办统计数据](https://open.dingtalk.com/document/development/obtains-the-to-do-statistics-of-an-enterprise): `obtains-the-to-do-statistics-of-an-enterprise` +- [获取企业所有应用列表](https://open.dingtalk.com/document/development/manager-microapplications-api-permission): `manager-microapplications-api-permission` +- [获取企业授权信息](https://open.dingtalk.com/document/development/obtains-the-basic-information-of-an-enterprise): `obtains-the-basic-information-of-an-enterprise` +- [获取企业接收红包统计数据](https://open.dingtalk.com/document/development/queries-the-red-envelope-receiving-statistics-of-an-enterprise): `queries-the-red-envelope-receiving-statistics-of-an-enterprise` +- [获取企业文档统计数据](https://open.dingtalk.com/document/development/get-enterprise-document-statistics): `get-enterprise-document-statistics` +- [获取企业日志统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-log-statistics): `obtain-enterprise-log-statistics` +- [获取企业日程统计数据](https://open.dingtalk.com/document/development/queries-enterprise-schedule-statistics): `queries-enterprise-schedule-statistics` +- [获取企业某天的所有部门电话会议统计列表](https://open.dingtalk.com/document/development/major-customer-department-dimension-teleconference-statistics): `major-customer-department-dimension-teleconference-statistics` +- [获取企业某天的所有部门视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-list-for-key-accounts-and-departments): `video-conferencing-statistics-list-for-key-accounts-and-departments` +- [获取企业某天的电话会议数据](https://open.dingtalk.com/document/development/major-customer-teleconference-statistics-interface): `major-customer-teleconference-statistics-interface` +- [获取企业某天的视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-query-v2-for-key-accounts): `video-conferencing-statistics-query-v2-for-key-accounts` +- [获取企业火车票订单数据](https://open.dingtalk.com/document/development/obtains-the-enterprise-train-ticket-order-data): `obtains-the-enterprise-train-ticket-order-data` +- [获取企业用户在线统计数据](https://open.dingtalk.com/document/development/retrieve-online-statistics-of-enterprise-users): `retrieve-online-statistics-of-enterprise-users` +- [获取企业用户激活状态统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-user-activation-status): `obtains-statistics-on-user-activation-status` +- [获取企业电话会议明细列表](https://open.dingtalk.com/document/development/major-account-conference-call-details-list): `major-account-conference-call-details-list` +- [获取企业电话会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-teleconference-statistics): `get-enterprise-teleconference-statistics` +- [获取企业签到统计数据](https://open.dingtalk.com/document/development/queries-enterprise-check-in-statistics): `queries-enterprise-check-in-statistics` +- [获取企业群直播统计数据](https://open.dingtalk.com/document/development/obtains-the-live-stream-statistics-for-an-enterprise-group): `obtains-the-live-stream-statistics-for-an-enterprise-group` +- [获取企业群聊统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-group-chat-statistics): `obtain-enterprise-group-chat-statistics` +- [获取企业考勤统计数据](https://open.dingtalk.com/document/development/queries-enterprise-attendance-statistics): `queries-enterprise-attendance-statistics` +- [获取企业聊天数据](https://open.dingtalk.com/document/development/chat-data-statistics-query-for-key-accounts): `chat-data-statistics-query-for-key-accounts` +- [获取企业视频会议明细列表](https://open.dingtalk.com/document/development/video-conference-details-for-key-accounts): `video-conference-details-for-key-accounts` +- [获取企业视频会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-video-conference-statistics): `get-enterprise-video-conference-statistics` +- [获取企业视频直播统计列表(部门维度)](https://open.dingtalk.com/document/development/live-broadcast-summary-statistics-of-key-account-departments): `live-broadcast-summary-statistics-of-key-account-departments` +- [获取企业视频直播统计数据](https://open.dingtalk.com/document/development/query-live-streaming-statistics): `query-live-streaming-statistics` +- [获取企业邮箱统计数据](https://open.dingtalk.com/document/development/queries-enterprise-email-statistics): `queries-enterprise-email-statistics` +- [获取企业部门聊天数据(部门维度)](https://open.dingtalk.com/document/development/dingtalk-chat-information-in-key-accounts): `dingtalk-chat-information-in-key-accounts` +- [获取企业钉盘统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-enterprise-dingtalk-trays): `obtains-the-statistics-on-enterprise-dingtalk-trays` +- [获取企业钉钉运动统计数据](https://open.dingtalk.com/document/development/queries-dingtalk-movement-statistics): `queries-dingtalk-movement-statistics` +- [获取入群二维码链接](https://open.dingtalk.com/document/development/obtain-a-qr-code-link): `obtain-a-qr-code-link` +- [获取公告ID列表](https://open.dingtalk.com/document/development/obtains-the-id-list-of-announcements-that-are-not-deleted): `obtains-the-id-list-of-announcements-that-are-not-deleted` +- [获取公告分类列表](https://open.dingtalk.com/document/development/obtains-the-list-of-categories-not-deleted-for-enterprise-announcements): `obtains-the-list-of-categories-not-deleted-for-enterprise-announcements` +- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-bulletin-that-is-not-deleted): `obtains-the-details-of-a-bulletin-that-is-not-deleted` +- [获取内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-product-sku-details-page): `obtain-the-address-of-the-product-sku-details-page` +- [获取内购订单信息](https://open.dingtalk.com/document/development/obtain-information-about-internal-purchase-orders): `obtain-information-about-internal-purchase-orders` +- [获取分支组织列表](https://open.dingtalk.com/document/development/obtains-the-branch-organization-list): `obtains-the-branch-organization-list` +- [获取助理技能信息](https://open.dingtalk.com/document/development/api-getassistantactioninfo): `api-getassistantactioninfo` +- [获取单个审批实例详情](https://open.dingtalk.com/document/development/get-details-single-approval-instance): `get-details-single-approval-instance` +- [获取参与考勤人员](https://open.dingtalk.com/document/development/batch-query-of-attendance-group-members): `batch-query-of-attendance-group-members` +- [获取参与考勤人员的userid](https://open.dingtalk.com/document/development/query-attendance-group-personnel-information-in-batches): `query-attendance-group-personnel-information-in-batches` +- [获取发布智能填表数量和使用智能填表人数(部门维度)](https://open.dingtalk.com/document/development/obtains-the-number-of-tables-published-by-the-enterprise-from): `obtains-the-number-of-tables-published-by-the-enterprise-from` +- [获取员工人数](https://open.dingtalk.com/document/development/obtain-the-number-of-employees): `obtain-the-number-of-employees` +- [获取员工人数](https://open.dingtalk.com/document/development/user-management-acquires-number-employees): `user-management-acquires-number-employees` +- [获取员工离职信息](https://open.dingtalk.com/document/development/obtain-multiple-employee-demission-information): `obtain-multiple-employee-demission-information` +- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/intelligent-personnel-obtain-employee-roster-information): `intelligent-personnel-obtain-employee-roster-information` +- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/obtain-employee-roster-field-information-in-batches): `obtain-employee-roster-field-information-in-batches` +- [获取图文卡片详情](https://open.dingtalk.com/document/development/get-message-card-details): `get-message-card-details` +- [获取在职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-on-the-job-employees-of-the): `intelligent-personnel-query-the-list-of-on-the-job-employees-of-the` +- [获取培训观看数据](https://open.dingtalk.com/document/development/obtains-the-playback-data-of-a-live-stream): `obtains-the-playback-data-of-a-live-stream` +- [获取培训课程的基本信息](https://open.dingtalk.com/document/development/get-basic-information-about-the-course): `get-basic-information-about-the-course` +- [获取填表实例数据](https://open.dingtalk.com/document/development/obtains-multiple-form-filling-records): `obtains-multiple-form-filling-records` +- [获取外部联系人列表](https://open.dingtalk.com/document/development/obtain-the-external-contact-list): `obtain-the-external-contact-list` +- [获取外部联系人标签列表](https://open.dingtalk.com/document/development/obtains-a-list-of-external-contact-tags): `obtains-a-list-of-external-contact-tags` +- [获取外部联系人详情](https://open.dingtalk.com/document/development/obtains-the-external-contact-details-of-an-enterprise): `obtains-the-external-contact-details-of-an-enterprise` +- [获取多个表单实例ID](https://open.dingtalk.com/document/development/obtain-the-ids-of-multiple-form-instances): `obtain-the-ids-of-multiple-form-instances` +- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-a-sub-department-id-list): `obtain-a-sub-department-id-list` +- [获取学习的知识列表](https://open.dingtalk.com/document/development/api-getknowledgelist): `api-getknowledgelist` +- [获取学段元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-to-query): `dingtalk-the-main-data-of-the-education-ecosystem-to-query` +- [获取学生ID列表](https://open.dingtalk.com/document/development/retrieve-student-based-class): `retrieve-student-based-class` +- [获取学生信息](https://open.dingtalk.com/document/development/obtain-student-information): `obtain-student-information` +- [获取学生监护人详情](https://open.dingtalk.com/document/development/obtain-the-relationship-between-home-and-school-personnel): `obtain-the-relationship-between-home-and-school-personnel` +- [获取学科元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject): `dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject` +- [获取学科实例列表](https://open.dingtalk.com/document/development/get-the-list-of-subject-examples): `get-the-list-of-subject-examples` +- [获取学科实例详情](https://open.dingtalk.com/document/development/query-dingtalk-education-subject-instances): `query-dingtalk-education-subject-instances` +- [获取定制应用的access_token](https://open.dingtalk.com/document/development/obtains-the-enterprise-authorized-credential): `obtains-the-enterprise-authorized-credential` +- [获取实例ID列表](https://open.dingtalk.com/document/development/obtains-a-list-of-instance-ids): `obtains-a-list-of-instance-ids` +- [获取实例详情](https://open.dingtalk.com/document/development/query-collection-form-instance-details): `query-collection-form-instance-details` +- [获取审批实例ID列表](https://open.dingtalk.com/document/development/operation-to-retrieve-a-list-of): `operation-to-retrieve-a-list-of` +- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/query-the-space-of-an-approval-nail): `query-the-space-of-an-approval-nail` +- [获取工作通知消息的发送结果](https://open.dingtalk.com/document/development/gets-the-result-of-sending-messages-asynchronously-to-the-enterprise): `gets-the-result-of-sending-messages-asynchronously-to-the-enterprise` +- [获取工作通知消息的发送进度](https://open.dingtalk.com/document/development/obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session): `obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session` +- [获取已加入或正在申请加入上下游组织的组织和个人信息](https://open.dingtalk.com/document/development/obtains-the-information-about-how-to-join-or-apply-to): `obtains-the-information-about-how-to-join-or-apply-to` +- [获取平台服务资源](https://open.dingtalk.com/document/development/obtain-platform-service-resources): `obtain-platform-service-resources` +- [获取平台资源](https://open.dingtalk.com/document/development/obtain-platform-resources): `obtain-platform-resources` +- [获取年报数据](https://open.dingtalk.com/document/development/obtain-annual-report-data): `obtain-annual-report-data` +- [获取应用下的页面列表](https://open.dingtalk.com/document/development/obtains-the-page-list-under-an-application): `obtains-the-page-list-under-an-application` +- [获取应用未激活的企业列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprises-whose-applications-are-not-activated): `obtains-a-list-of-enterprises-whose-applications-are-not-activated` +- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/exchange-code-for-the-identity-information-of-a-microapplication-administrator): `exchange-code-for-the-identity-information-of-a-microapplication-administrator` +- [获取应用自定义空间使用详情](https://open.dingtalk.com/document/development/queries-the-usage-details-of-a-custom-application-space): `queries-the-usage-details-of-a-custom-application-space` +- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/obtains-the-information-about-all-manageable-templates-of-the-current): `obtains-the-information-about-all-manageable-templates-of-the-current` +- [获取待入职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-employees-to-be-hired): `intelligent-personnel-query-the-list-of-employees-to-be-hired` +- [获取微应用后台免登的access_token](https://open.dingtalk.com/document/development/obtain-the-ssotoken-for-micro-application-background-logon-free): `obtain-the-ssotoken-for-micro-application-background-logon-free` +- [获取报表假期数据](https://open.dingtalk.com/document/development/obtains-the-holiday-data-from-the-smart-attendance-report): `obtains-the-holiday-data-from-the-smart-attendance-report` +- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-a-list-of): `you-can-call-this-operation-to-retrieve-a-list-of` +- [获取指定用户的所有父部门列表](https://open.dingtalk.com/document/development/queries-the-list-of-all-parent-departments-of-a-user): `queries-the-list-of-all-parent-departments-of-a-user` +- [获取指定角色的员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-of-a-role): `obtain-the-list-of-employees-of-a-role` +- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-department): `queries-all-parent-departments-of-a-department` +- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/query-the-list-of-all-parent-departments-of-a-department): `query-the-list-of-all-parent-departments-of-a-department` +- [获取授权应用的基本信息](https://open.dingtalk.com/document/development/obtains-application-information-of-an-enterprise): `obtains-application-information-of-an-enterprise` +- [获取数字化证书](https://open.dingtalk.com/document/development/obtain-digital-certificate): `obtain-digital-certificate` +- [获取数字区县组织信息](https://open.dingtalk.com/document/development/querydigitaldistrictorginfo-api-reference): `querydigitaldistrictorginfo-api-reference` +- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-upload-information): `obtain-upload-information` +- [获取文件下载信息](https://open.dingtalk.com/document/development/obtain-download-file-info): `obtain-download-file-info` +- [获取文章详情](https://open.dingtalk.com/document/development/get-article): `get-article` +- [获取日志接收人员列表](https://open.dingtalk.com/document/development/queries-log-sharing-personnel): `queries-log-sharing-personnel` +- [获取日志相关人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-log-related-personnel-by-type): `obtains-a-list-of-log-related-personnel-by-type` +- [获取日志统计数据](https://open.dingtalk.com/document/development/query-log-statistics): `query-log-statistics` +- [获取服务窗联系人信息](https://open.dingtalk.com/document/development/obtains-the-contact-information-of-the-service-window): `obtains-the-contact-information-of-the-service-window` +- [获取未活跃用户登录明细](https://open.dingtalk.com/document/development/obtains-the-logon-details-of-inactive-users): `obtains-the-logon-details-of-inactive-users` +- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/queries-the-inactive-users-or-active-users-under-an-enterprise): `queries-the-inactive-users-or-active-users-under-an-enterprise` +- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/query-data-of-inactive-users): `query-data-of-inactive-users` +- [获取权限列表](https://open.dingtalk.com/document/development/obtain-a-permission-list): `obtain-a-permission-list` +- [获取模板code](https://open.dingtalk.com/document/development/obtains-the-template-code-based-on-the-template-name): `obtains-the-template-code-based-on-the-template-name` +- [获取模板详情](https://open.dingtalk.com/document/development/query-template-details): `query-template-details` +- [获取流程定义](https://open.dingtalk.com/document/development/obtain-process-definition): `obtain-process-definition` +- [获取流程实例](https://open.dingtalk.com/document/development/obtain-process-instance): `obtain-process-instance` +- [获取流程节点按钮列表](https://open.dingtalk.com/document/development/obtain-a-list-of-process-node-buttons): `obtain-a-list-of-process-node-buttons` +- [获取流程设计的节点信息](https://open.dingtalk.com/document/development/obtain-the-information-about-the-nodes-in-process-design): `obtain-the-information-about-the-nodes-in-process-design` +- [获取班次摘要信息](https://open.dingtalk.com/document/development/enterprise-shift-query-in-batches): `enterprise-shift-query-in-batches` +- [获取班次详情](https://open.dingtalk.com/document/development/shift-query): `shift-query` +- [获取班级内学生的关系列表](https://open.dingtalk.com/document/development/queries-the-list-of-relationships): `queries-the-list-of-relationships` +- [获取班级圈动态列表](https://open.dingtalk.com/document/development/dynamic-list-opening-of-class-circle): `dynamic-list-opening-of-class-circle` +- [获取班级圈话题列表](https://open.dingtalk.com/document/development/obtain-a-topic-list-of-class-circles): `obtain-a-topic-list-of-class-circles` +- [获取用户创建文档数和创建文档人数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-dingtalk-documents-created-per-day-in-an): `queries-the-number-dingtalk-documents-created-per-day-in-an` +- [获取用户创建的AI助理列表](https://open.dingtalk.com/document/development/assistant-acquires-list-assistants-created-by-users): `assistant-acquires-list-assistants-created-by-users` +- [获取用户创建的填表模板](https://open.dingtalk.com/document/development/obtains-the-template-that-a-user-creates): `obtains-the-template-that-a-user-creates` +- [获取用户发送日志的概要信息](https://open.dingtalk.com/document/development/view-log-summary-data): `view-log-summary-data` +- [获取用户可查看的公告](https://open.dingtalk.com/document/development/list-the-user-s-announcement-list): `list-the-user-s-announcement-list` +- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/list-the-microapplications-visible-to-employees): `list-the-microapplications-visible-to-employees` +- [获取用户可见范围的AI助理列表](https://open.dingtalk.com/document/development/api-listvisibleassistant): `api-listvisibleassistant` +- [获取用户基本信息](https://open.dingtalk.com/document/development/queries-basic-user-information): `queries-basic-user-information` +- [获取用户待审批数量](https://open.dingtalk.com/document/development/obtain-the-number-of-tasks-to-be-approved-by-me): `obtain-the-number-of-tasks-to-be-approved-by-me` +- [获取用户授权的持久授权码](https://open.dingtalk.com/document/development/persistent-authorization-code): `persistent-authorization-code` +- [获取用户日志未读数](https://open.dingtalk.com/document/development/querying-the-employee-s-log-is-not-reading): `querying-the-employee-s-log-is-not-reading` +- [获取用户月活跃数据](https://open.dingtalk.com/document/development/retrieves-the-user-s-monthly-active-data): `retrieves-the-user-s-monthly-active-data` +- [获取用户版本分布情况](https://open.dingtalk.com/document/development/queries-the-distribution-of-user-versions): `queries-the-distribution-of-user-versions` +- [获取用户签到记录](https://open.dingtalk.com/document/development/obtain-the-check-in-records-of-multiple-users): `obtain-the-check-in-records-of-multiple-users` +- [获取用户考勤组](https://open.dingtalk.com/document/development/queries-a-user-attendance-group): `queries-a-user-attendance-group` +- [获取申请单列表](https://open.dingtalk.com/document/development/search-enterprise-approval-form-data): `search-enterprise-approval-form-data` +- [获取申请单详情](https://open.dingtalk.com/document/development/obtains-the-detailed-data-of-a-single-request): `obtains-the-detailed-data-of-a-single-request` +- [获取离职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-company-turnover-list): `intelligent-personnel-query-company-turnover-list` +- [获取空间信息](https://open.dingtalk.com/document/development/obtain-space-information): `obtain-space-information` +- [获取第三方个人应用的access_token](https://open.dingtalk.com/document/development/obtain-personal-application): `obtain-personal-application` +- [获取管理员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-administrators): `obtains-a-list-of-administrators` +- [获取管理员列表](https://open.dingtalk.com/document/development/query-the-administrator-list): `query-the-administrator-list` +- [获取管理员的应用管理权限](https://open.dingtalk.com/document/development/obtains-the-administrator-s-microapplication-management-permission): `obtains-the-administrator-s-microapplication-management-permission` +- [获取管理员通讯录权限范围](https://open.dingtalk.com/document/development/query-permissions-of-the-administrator-address-book): `query-permissions-of-the-administrator-address-book` +- [获取考勤报表列值](https://open.dingtalk.com/document/development/queries-the-column-value-of-the-attendance-report): `queries-the-column-value-of-the-attendance-report` +- [获取考勤报表列定义](https://open.dingtalk.com/document/development/queries-the-enterprise-attendance-report-column): `queries-the-enterprise-attendance-report-column` +- [获取考勤组详情](https://open.dingtalk.com/document/development/query-a-single-attendance-group): `query-a-single-attendance-group` +- [获取联系人的元数据](https://open.dingtalk.com/document/development/gets-the-metadata-description-of-a-crm-contact-object): `gets-the-metadata-description-of-a-crm-contact-object` +- [获取自定义对象的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-custom-object): `get-metadata-description-of-crm-custom-object` +- [获取花名册元数据](https://open.dingtalk.com/document/development/intelligent-personnel-roster-metadata-query): `intelligent-personnel-roster-metadata-query` +- [获取花名册字段组详情](https://open.dingtalk.com/document/development/get-roster-field-group-details): `get-roster-field-group-details` +- [获取视频会议详情](https://open.dingtalk.com/document/development/get-details-of-the-video-conference): `get-details-of-the-video-conference` +- [获取视频直播明细列表](https://open.dingtalk.com/document/development/queries-the-details-list-of-apsaravideo-live): `queries-the-details-list-of-apsaravideo-live` +- [获取视频直播观看人员列表](https://open.dingtalk.com/document/development/query-users-of-apsaravideo-live): `query-users-of-apsaravideo-live` +- [获取角色列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprise-roles): `obtains-a-list-of-enterprise-roles` +- [获取角色组列表](https://open.dingtalk.com/document/development/obtains-the-role-group-information): `obtains-the-role-group-information` +- [获取角色详情](https://open.dingtalk.com/document/development/queries-role-details): `queries-role-details` +- [获取课堂明细数据](https://open.dingtalk.com/document/development/obtain-course-detail-data): `obtain-course-detail-data` +- [获取课堂概要数据](https://open.dingtalk.com/document/development/get-course-summary-data): `get-course-summary-data` +- [获取课程列表](https://open.dingtalk.com/document/development/get-course-list): `get-course-list` +- [获取课程参与方列表](https://open.dingtalk.com/document/development/get-a-list-of-course-participants): `get-a-list-of-course-participants` +- [获取课程详情](https://open.dingtalk.com/document/development/get-course-details): `get-course-details` +- [获取跟进记录对象的元数据](https://open.dingtalk.com/document/development/obtains-the-metadata-description-of-the-crm-follow-up-record-object): `obtains-the-metadata-description-of-the-crm-follow-up-record-object` +- [获取通讯录权限范围](https://open.dingtalk.com/document/development/obtain-corpsecret-authorization-scope): `obtain-corpsecret-authorization-scope` +- [获取部门下人员列表](https://open.dingtalk.com/document/development/obtains-the-list-of-people-under-a-department): `obtains-the-list-of-people-under-a-department` +- [获取部门企业账号用户详情](https://open.dingtalk.com/document/development/queries-account-details): `queries-account-details` +- [获取部门列表](https://open.dingtalk.com/document/development/obtain-the-department-list): `obtain-the-department-list` +- [获取部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-industry-departments): `obtains-a-list-of-industry-departments` +- [获取部门列表](https://open.dingtalk.com/document/development/obtains-the-department-node-list): `obtains-the-department-node-list` +- [获取部门列表](https://open.dingtalk.com/document/development/user-management-acquires-the-list-departments): `user-management-acquires-the-list-departments` +- [获取部门扩展信息](https://open.dingtalk.com/document/development/obtain-department-extension-information): `obtain-department-extension-information` +- [获取部门用户userid列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employee-ids-by-department-id): `obtain-the-list-of-employee-ids-by-department-id` +- [获取部门用户userid列表](https://open.dingtalk.com/document/development/query-the-list-of-department-userids): `query-the-list-of-department-userids` +- [获取部门用户基础信息](https://open.dingtalk.com/document/development/obtain-the-basic-information-of-department-users): `obtain-the-basic-information-of-department-users` +- [获取部门用户签到记录](https://open.dingtalk.com/document/development/get-check-in-data): `get-check-in-data` +- [获取部门用户详情](https://open.dingtalk.com/document/development/obtain-department-members-details): `obtain-department-members-details` +- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-department-user-details): `queries-department-user-details` +- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-the-complete-information-of-a-department-user): `queries-the-complete-information-of-a-department-user` +- [获取部门的扩展字段定义](https://open.dingtalk.com/document/development/gets-the-extended-field-definition-of-a-department): `gets-the-extended-field-definition-of-a-department` +- [获取部门详情](https://open.dingtalk.com/document/development/industry-address-book-api-for-obtaining-department-information): `industry-address-book-api-for-obtaining-department-information` +- [获取部门详情](https://open.dingtalk.com/document/development/obtains-queries-department-details): `obtains-queries-department-details` +- [获取部门详情](https://open.dingtalk.com/document/development/queries-department-details): `queries-department-details` +- [获取部门详情](https://open.dingtalk.com/document/development/query-department-details0-v2): `query-department-details0-v2` +- [获取问答明细](https://open.dingtalk.com/document/development/api-getaskdetail): `api-getaskdetail` +- [获得企业创建日志相关信息(组织维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-organization): `obtains-information-about-a-created-enterprise-log-from-the-organization` +- [获得企业创建日志相关信息(部门维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-department): `obtains-information-about-a-created-enterprise-log-from-the-department` +- [获得用户创建文档数和创建文档人数(部门维度)](https://open.dingtalk.com/document/development/obtain-departmental-dimensions-documents-created-people-creating): `obtain-departmental-dimensions-documents-created-people-creating` +- [获得组织维度日程相关信息](https://open.dingtalk.com/document/development/queries-the-number-of-people-who-have-created-an-event): `queries-the-number-of-people-who-have-created-an-event` +- [解绑设备](https://open.dingtalk.com/document/development/unbind-a-smart-hardware-device): `unbind-a-smart-hardware-device` +- [计算请假时长](https://open.dingtalk.com/document/development/calculate-leave-duration): `calculate-leave-duration` +- [设备入会](https://open.dingtalk.com/document/development/equipment-membership): `equipment-membership` +- [设备账号向目标用户发送DING消息](https://open.dingtalk.com/document/development/device-publishing): `device-publishing` +- [设定角色成员管理范围](https://open.dingtalk.com/document/development/update-role-member-management-department-scope): `update-role-member-management-department-scope` +- [设置禁止群成员私聊](https://open.dingtalk.com/document/development/set-private-chat): `set-private-chat` +- [还原回收站文件(夹)](https://open.dingtalk.com/document/development/restore-recycle-bin-files-folder): `restore-recycle-bin-files-folder` +- [通知审批撤销](https://open.dingtalk.com/document/development/notify-the-attendance-to-modify-the-punch-result-when-the): `notify-the-attendance-to-modify-the-punch-result-when-the` +- [通知审批通过](https://open.dingtalk.com/document/development/notice-of-approval): `notice-of-approval` +- [通知换班通过](https://open.dingtalk.com/document/development/shift-change-operation-after-approval): `shift-change-operation-after-approval` +- [通知授权结果](https://open.dingtalk.com/document/development/notify-the-authorization-result): `notify-the-authorization-result` +- [通知补卡通过](https://open.dingtalk.com/document/development/make-up-the-card-after-approval): `make-up-the-card-after-approval` +- [通过免登码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-userid-of-a-user-by-using-the-log-free): `obtain-the-userid-of-a-user-by-using-the-log-free` +- [通过免登码获取用户信息(不推荐)](https://open.dingtalk.com/document/development/get-user-userid-through-login-free-code): `get-user-userid-through-login-free-code` +- [通过调用者unionId获取激活码](https://open.dingtalk.com/document/development/obtain-the-activation-code-by-calling-the-union-id-of): `obtain-the-activation-code-by-calling-the-union-id-of` +- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/query-form-instances-using-advanced-search-conditions): `query-form-instances-using-advanced-search-conditions` +- [邀请其他组织企业账号加入](https://open.dingtalk.com/document/development/invite-other-organization-specific-accounts-to-join): `invite-other-organization-specific-accounts-to-join` +- [配置发票适用人群](https://open.dingtalk.com/document/development/configure-invoice-users): `configure-invoice-users` +- [重新授权未激活应用的企业](https://open.dingtalk.com/document/development/re-authorize-enterprises-whose-applications-are-not-activated): `re-authorize-enterprises-whose-applications-are-not-activated` +- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-for-a-single-schedule): `sign-in-for-a-single-schedule` +- [钉钉文本翻译](https://open.dingtalk.com/document/development/dingtalk-translation): `dingtalk-translation` +- [静态推荐数据同步](https://open.dingtalk.com/document/development/statically-recommended-data-synchronization): `statically-recommended-data-synchronization` +- [预计算时长](https://open.dingtalk.com/document/development/calculate-duration-based-on-attendance-scheduling): `calculate-duration-based-on-attendance-scheduling` +- [验证激活结果](https://open.dingtalk.com/document/development/verify-activation-results): `verify-activation-results` + +> 已知 Gap:7 条 slug 在 development 段为空壳(<50000B),可能属别的 section(如 solution),待补正确 section,本目录暂不收录。 + + diff --git a/skills/dingtalk-open-platform/references/api-invocation.md b/skills/dingtalk-open-platform/references/api-invocation.md new file mode 100644 index 00000000..62d8d818 --- /dev/null +++ b/skills/dingtalk-open-platform/references/api-invocation.md @@ -0,0 +1,57 @@ +# API 调用层(Invocation)— HTTP 契约 1:1 映射 + +文档里的 HTTP 主契约(Method + Path + Body)可直接落成 `dws` 命令或原始 HTTPS 请求,无需猜 SDK。`dws` 是可选 CLI,与直接发 HTTP 等价。 + +## 1. 原始直通:dws api + +```bash +# api.dingtalk.com:token 走 Header (x-acs-dingtalk-access-token),路径 /v1.0|/v2.0 +dws api GET /v1.0/contact/users/me --format json +dws api POST /v1.0/contact/users/search \ + --data '{"queryWord":"张三","offset":0,"size":10}' --format json + +# oapi.dingtalk.com:token 走 URL 参数 (access_token),路径 /topapi/... +dws api POST /topapi/v2/user/get --data '{"userid":""}' --format json +``` + +| 域名 | token 传递 | 路径形态 | +| --- | --- | --- | +| `api.dingtalk.com` | Header `x-acs-dingtalk-access-token` | `/v1.0/xxx`、`/v2.0/xxx` | +| `oapi.dingtalk.com` | URL 参数 `access_token` | `/topapi/...` 或完整 URL | + +- raw api 仅限自有应用凭证(见 [auth.md](auth.md))。 + +## 2. 从文档契约到命令(1:1) + +| 文档字段 | 命令位置 | +| --- | --- | +| Method | `dws api {METHOD}` | +| URL Path | `dws api {METHOD} {PATH}` | +| Body (JSON) | `--data '{...}'`(保持嵌套,不要展平)| +| Header token | 由 `dws` 注入,不要手填 | +| 输出解析 | `--format json` + 可选 `--jq` | + +## 3. 产品糖命令优先 + +高频能力可用 `dws ...` 糖命令(自带翻页/轮询/校验),而非手搓 `dws api`,命令树与 flag 以 `dws --help` 为准: + +- 应用管理 → `dws devapp ...`(开放平台应用 CRUD / 凭证 / 权限 / 机器人 / 版本) +- 通讯录 / 日历 / 审批 / 消息等 → `dws contact|calendar|oa|chat ...` + +`dws api` 用于:糖命令未覆盖、或文档明确给 HTTP 契约要 1:1 复现时。 + +## 4. 命令发现(事实源) + +```bash +dws --help # 人读:Usage / Example / Flags +dws schema # 机读:所有产品与工具 +dws schema . # 单工具 JSON Schema +dws schema --jq '.tool.required' # 只看必填字段 +``` + +> 参考文档里的 flag 列表是便于理解,不是契约。冲突时**以 `--help` / `dws schema` 为准**。 + +## 5. 写操作铁律 + +- 写/删/发/审批类先 `--dry-run --format json` 展示摘要 → 用户确认 → 加 `--yes`(详见 [workflow.md](workflow.md))。 +- 单次批量 ≤ 30 条。 diff --git a/skills/dingtalk-open-platform/references/auth.md b/skills/dingtalk-open-platform/references/auth.md new file mode 100644 index 00000000..b24d3b69 --- /dev/null +++ b/skills/dingtalk-open-platform/references/auth.md @@ -0,0 +1,71 @@ +# 鉴权与凭证(Auth & Credentials)— 前置必读 + +调用前先把鉴权与凭证准备好;凭证视为敏感,绝不外泄。 + +## 1. 登录与 token 状态 + +```bash +dws auth status --format json # 查看当前登录态与身份 +dws auth login # 扫码 / Device Flow 登录,自动刷新 token +dws auth logout # 清除认证信息 +dws auth reset # 重置认证(清本地 token,触发重新授权) +``` + +| 子命令 | 用途 | +| --- | --- | +| `status` | 调用前确认是否已登录、token 是否有效 | +| `login` | 未登录 / `token is illegal` / 401 时执行 | +| `export` / `import` | 迁移认证包(跨机器) | + +- token 过期或 `token is illegal`:执行 `dws auth login`,**不要**把 token 贴进对话或日志。 + +## 1b. access_token 端点(HTTP 原始换取) + +```http +# 新版(推荐):token 用于业务请求 Header x-acs-dingtalk-access-token +POST https://api.dingtalk.com/v1.0/oauth2/accessToken +Content-Type: application/json + +{"appKey":"","appSecret":""} +``` +```http +# 旧版:token 用于业务请求 query ?access_token= +GET https://oapi.dingtalk.com/gettoken?appkey=&appsecret= +``` + +- token 必须缓存复用,禁止每请求重换(高频换取会触发限流)。 +- 文档:[获取企业内部应用 accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app) · [获取第三方应用授权企业 accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-authorized-enterprise-1)。 +- 用 dws 时一般无需手动换取,`dws api` 用自有应用凭证登录后自动注入;凭证读取见 §3。 + +## 2. token 类型 + +| 类型 | 主体 | 典型用途 | +| --- | --- | --- | +| `app` / `tenant` | 企业自建应用 | 大多数服务端 OpenAPI | +| `user` | 用户授权 | 读当前用户数据、网页授权 | +| `isv` | 第三方应用 | ISV 套件场景 | + +> 应用类型(企业自建 vs 第三方)查询 API 缺失是高频凭证混淆来源(gap #4),不确定时先 `dws devapp get` 看应用属性。 + +## 3. 应用凭证读取(敏感) + +```bash +dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json +``` + +- 返回含 `clientId` / `clientSecret`(=`appSecret`),**按敏感凭证处理**:仅用于构造 `dws api` 的 `--client-id/--client-secret`,不回显、不写文件。 +- 不能用 `dws devapp get` 代替(`get` 不返回完整 secret)。 +- `unifiedAppId` 来自 `dws devapp list --name X --format json`,多条命中先让用户指定。 + +## 4. raw API 调用的凭证约束 + +- `dws api` 仅限**自有应用凭证**(`--client-id/--client-secret` 登录后)使用。 +- 通过 MCP 默认凭证登录获取的加密 token **不支持** raw API 调用 → 改用对应产品糖命令或先用自有应用登录。 + +## 常见错误 + +| 现象 | 应对 | +| --- | --- | +| `token is illegal` / 401 | `dws auth status` → `dws auth login` | +| raw api 报 token 不支持 | 用 `--client-id/--client-secret` 自有应用凭证 | +| 凭证泄露风险 | 只走 `credentials get`,输出不回显 secret | diff --git a/skills/dingtalk-open-platform/references/checklist.md b/skills/dingtalk-open-platform/references/checklist.md new file mode 100644 index 00000000..bb9d573b --- /dev/null +++ b/skills/dingtalk-open-platform/references/checklist.md @@ -0,0 +1,27 @@ +# Verification Checklist — 调用 / 上线前勾选 + +每次开放平台调用或交付前逐项核对,任一不过则停止并补齐。 + +## 发现 / 契约 +- [ ] 已在 [llm.md](../llm.md) 或官方文档定位到目标接口(method / path / 文档链接)。 +- [ ] 已解析稳定资源 ID(unifiedAppId / userId / unionId / nodeId 等),来自返回值,非编造。 + +## 鉴权 / 权限 +- [ ] 已确认 token 类型(应用 / 用户 / JSAPI)与 base URL(新版 Header / 旧版 query)匹配。 +- [ ] 已确认所需 `scopeValue` 已授权(`dws devapp permission list`);缺则已申请并发版。 +- [ ] 已确认数据范围(data_range),未默认全员。 +- [ ] 凭证未出现在对话 / 日志 / prompt。 + +## 调用 / Header / 限流 +- [ ] 必需 Header 正确(`x-acs-dingtalk-access-token` + `Content-Type`)。 +- [ ] 分页 / 批量大小符合接口约定;限流退避已考虑。 +- [ ] token 已缓存复用,未每请求重换。 + +## 写操作 / 安全 +- [ ] 写 / 删 / 发 / 发布 / 撤回 已先 `--dry-run` 展示摘要并经用户当前轮确认([危险])。 +- [ ] 不可逆操作目标精确(无批量误伤)。 + +## 验证 / 错误处理 +- [ ] readback 三态明确:成功 / needs_permission / needs_resource。 +- [ ] 错误处理覆盖鉴权 / 权限 / 限流 / 参数 / 平台错误;保留 `requestId`。 +- [ ] 能回读则已回读校验;不一致字段已报告。 diff --git a/skills/dingtalk-open-platform/references/discovery.md b/skills/dingtalk-open-platform/references/discovery.md new file mode 100644 index 00000000..851e3e79 --- /dev/null +++ b/skills/dingtalk-open-platform/references/discovery.md @@ -0,0 +1,43 @@ +# 发现层(Discovery)— 找到接口与文档 + +先从文档目录定位能力域与接口,再下钻到单接口官方文档页。 + +## 发现优先级 + +1. **文档目录(首选,离线可用)**:[llm.md](../llm.md) = [api-catalog.md](api-catalog.md) 同源 — 全量**有效**官方文档链接(经内容长度校验,按 8 能力域 + 功能子类归类),URL 模板 `https://open.dingtalk.com/document/development/`。 +2. **运行态搜索**:`dws devdoc article search`(命中即返回标题/摘要/文档链接)。 +3. **官方文档站**:`https://open.dingtalk.com/document`。 + +## devdoc article search + +```bash +dws devdoc article search "MCP" --format json +dws devdoc article search --query "OAuth2 接入" --format json +dws devdoc article search --query "消息卡片" --page 2 --size 5 --format json +``` + +| flag | 说明 | +| --- | --- | +| 位置参数 / `--query` / `--keyword` | 搜索关键词(必填,建议传用户原话里的 API 名/能力名/错误码)| +| `--page` | 页码,从 1 开始,默认 1 | +| `--size` | 每页条数,默认 10 | + +- 只做**搜索**,不做读取;命中条目返回标题、摘要、`doc_link`,由 Agent 引用链接或进一步浏览。 +- devdoc 查的是**开放平台开发者文档**(面向研发),不要拿它查业务数据(那是 doc / aitable / report)。 + +## 运行态风险与降级 + +- 真实调用若返回 `PARAM_ERROR - 未找到指定工具` / Forbidden(pre-mcp-gw):这是**网关/工具注册未闭环**,不是用户参数问题。 +- 处理:标记 `needs_gateway_tool_registration`,降级到 [llm.md](../llm.md) 本地目录或已挂载的逐页 Markdown。 +- `--help` / `--dry-run` 只证明 CLI 命令面与映射存在,**不**证明后端工具已注册。 + +## 上线/注入验收(六步门禁) + +```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 --format json || true +dws schema devdoc.search_open_error_code_rag --format json || true +# real call:未注册则记录 needs_gateway_tool_registration +``` diff --git a/skills/dingtalk-open-platform/references/gotchas.md b/skills/dingtalk-open-platform/references/gotchas.md new file mode 100644 index 00000000..6ca9e754 --- /dev/null +++ b/skills/dingtalk-open-platform/references/gotchas.md @@ -0,0 +1,17 @@ +# Common Gotchas — 钉钉开放平台真实坑 + +钉钉开放平台调用高频踩坑,来自线上失败归因与排错沉淀。格式:错 → 对 → 后果。 + +| 坑 | 错误做法 | 正确做法 | 后果 | +| --- | --- | --- | --- | +| 凭证混用 | 用 MCP 默认(加密)token 调 `dws api` raw 接口 | `dws api` 用自有应用 `--client-id/--client-secret` 登录 | raw 调用被拒 | +| scope 未开通 | 直接调用未授权接口 | 先 `dws devapp permission add` 申请并发版 | 403 / 无权限 | +| 新旧 path 混用 | Header token 配 `oapi` query 接口(或反之)| 新版走 `api.dingtalk.com` Header;旧版走 `oapi` query | 鉴权失败 | +| token 类型用错 | 用应用 token 调个人数据 / 免登接口 | 个人数据用用户身份,端内用 JSAPI | 无权限 | +| 权限名当 scope | `--permissions 机器人发消息` | 只传 `scopeValue`(如 `qyapi_robot_sendmsg`)| 申请失败 | +| 数据范围默认全员 | 假设拥有全员可见范围 | 显式确认 data_range,按授权范围调用 | 越权 / 空数据 | +| 多候选选第一个 | 名称命中多条直接取第一条 | 展示候选,停止写操作,等用户指定 | 操作错对象 | +| 写操作未 dry-run | 直接 `--yes` 执行写/删/发 | 先 `--dry-run` 展示摘要 → 确认 → `--yes` | 不可逆误操作 | +| 丢失 requestId | 报错后不留痕 | 保留 `requestId` 交 `devdoc error diagnose` | 无法链路定位 | +| 高频刷新 token | 每请求重新换 token | token 必须缓存复用 | 触发限流 | +| devdoc 网关未注册 | 把 `PARAM_ERROR - 未找到指定工具` 当用户参数错 | 记 `needs_gateway_tool_registration`,降级 [llm.md](../llm.md) | 误判阻塞原因 | diff --git a/skills/dingtalk-open-platform/references/quick-reference.md b/skills/dingtalk-open-platform/references/quick-reference.md new file mode 100644 index 00000000..faee4388 --- /dev/null +++ b/skills/dingtalk-open-platform/references/quick-reference.md @@ -0,0 +1,51 @@ +# Quick Reference — 应用类型 / Header / 限流 / CLI + +## 应用类型 + +来自各接口「权限」表(企业内部应用 / 第三方企业应用 / 第三方个人应用)与端内能力实测。 + +| 应用类型 | 适用 | 鉴权 / 身份 | 典型场景 | +| --- | --- | --- | --- | +| 企业内部应用 | 单企业自建 | 企业内部应用 `accessToken`(appKey/appSecret 换取) | 服务端调组织数据、机器人、文档 | +| 第三方企业应用 | ISV 多企业 | 授权企业 `accessToken`(suiteTicket / 授权码换取) | 上架应用市场、跨企业服务 | +| 第三方个人应用 | 面向个人用户 | 个人授权(OAuth) | 个人维度能力(部分接口标「暂不支持」)| +| 移动接入应用 | 钉钉端内 H5 / 小程序 / 酷应用 | JSAPI 免登 ticket / 免登 code | 端内免登、容器 JSAPI 能力 | + +> 选型见 [SKILL.md](../SKILL.md) §5 Decision guidance;应用增删改管理用 `dws devapp ...`(以 `dws devapp --help` 为准)。 + +## Header / 限流 / 分页 {#header限流分页} + +### 必需 Header + +| 域名 | token 传递 | 其它 | +| --- | --- | --- | +| `api.dingtalk.com`(新版)| Header `x-acs-dingtalk-access-token: ` | `Content-Type: application/json` | +| `oapi.dingtalk.com`(旧版)| query `?access_token=` | 路径 `/topapi/...` | + +### 限流 + +- 每接口有 `maxQps`(官方文档 / API Explorer 标注,部分接口 `doc_open_api.extension.maxQps` 可查)。 +- 触发限流返回 429 / 限流码:遵守 `Retry-After`,指数退避,单命令重试 ≤ 3 次。 +- 幂等键无平台级统一约定 → 写接口自动重试前优先 readback 确认,避免重复写。 + +### 分页 / 大小 + +- 分页两类:`offset`/`size` 偏移式;`nextToken`/`maxResults` 游标式(以接口文档为准)。 +- 批量大小遵循各接口文档上限;dws 侧批量操作建议单次 ≤ 30 条。 + +## dws CLI 速查(均 `--help` 核验) + +```bash +dws --help # 顶层产品与 utility 命令 +dws auth status --format json # 登录态 / 当前身份 +dws auth login [--device] # 扫码 / 设备流登录 +dws schema [.] # MCP 工具 JSON Schema(必填字段 / flag 别名) +dws devdoc article search "<关键词>" --format json # 查开发文档 +dws devdoc error diagnose --error-code --query "..." --format json # 错误排查 +dws api --data '' --format json # 原始 OpenAPI 直通 +dws devapp list --name <应用名> --format json # 开放平台应用 CRUD / 凭证 / 权限 +dws doc read --node --content-format markdown --format json # 读钉钉文档 +``` + +- `dws api` 仅认自有应用凭证(`--client-id/--client-secret` 登录),MCP 默认加密 token 不支持 raw 调用。 +- 命令事实源以 `dws --help` / `dws schema` 为准;参考文档与之冲突时以 `--help` 为准。 diff --git a/skills/dingtalk-open-platform/references/reference.md b/skills/dingtalk-open-platform/references/reference.md new file mode 100644 index 00000000..fa8c9a93 --- /dev/null +++ b/skills/dingtalk-open-platform/references/reference.md @@ -0,0 +1,61 @@ +# 一等公民:错误码 / 状态码 / 限流 / 版本化 + +把 Status codes / Request limits / Versioning / Webhooks 作为一等公民单列,调用前明确契约边界。 + +## 1. 错误诊断:devdoc error diagnose + +```bash +dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token" --format json +dws devdoc error diagnose --request-id "" --query "接口调用失败" --format json +dws devdoc error diagnose --error-message "token is illegal" --api "获取用户信息" --format json +``` + +| flag | 说明 | +| --- | --- | +| `--query` | 原始排查问题(必填语义)| +| `--error-code` | 错误码 | +| `--error-message` | 错误描述,合并进问题 | +| `--api` | API 名称,合并进问题 | +| `--request-id` | 开放平台 requestId(不是所有接口都返回)| +| `--context` | 额外排查上下文 | + +- 后端返回 `PARAM_ERROR - 未找到指定工具` / `unknown tool` → 记 `needs_gateway_tool_registration`,降级 `devdoc article search` 或本 skill 的 [llm.md](../llm.md) 目录。 + +## 2. 错误码三元组(自恢复) + +每个错误码应能回答:**触发原因 / 排查路径 / next_action**;未知错误码写 `源文档未提供`,不要编造。 + +| 错误码 | 触发原因 | next_action | +| --- | --- | --- | +| `40014` | access_token 无效/过期 | `dws auth login`,自有应用重取凭证 | +| `token is illegal` | 未登录 / token 失效 | `dws auth status` → `dws auth login` | +| `PARAM_ERROR - 未找到指定工具` | 网关工具未注册 | 记 `needs_gateway_tool_registration`,降级本地索引 | +| `ServiceResult.success=false` | 业务校验失败 | 透传 `errorCode/errorMsg`,按总表排查 | + +> 未知错误码写 `源文档未提供`,不要编造原因或 next_action(gap #3:错误码总表数据仍在增量建设)。 + +## 3. 状态码 / HTTP 语义 + +| HTTP | 含义 | 处理 | +| --- | --- | --- | +| 2xx | 成功 | 校验业务成功字段后继续 | +| 401 / 403 | 鉴权 / 权限不足 | `auth login` 或 `devapp permission add` | +| 429 | 限流 | 退避重试(见限流)| +| 5xx | 平台错误 | 附 requestId 回流,不反复变通 | + +## 4. 限流(Request limits) + +- 每接口 `maxQps` 写在 `doc_open_api.extension`(如 `{"maxQps":100}`);批量调用前先看 QPS。 +- 429 / 限流:指数退避重试,单命令重试 ≤ 3 次;超限停止并报告。 +- 幂等键无平台级统一约定(gap #10)→ 写接口自动重试需谨慎,优先 readback 确认而非盲目重发。 + +## 5. 版本化(Versioning) + +- 路径版本:`/v1.0/` 与 `/v2.0/` 并存;旧 `dingtalk.oapi.*` 与 `/topapi/*` 为老接口面。 +- `dws upgrade`:能力缺失 / 新接口未注册时,先升级 dws 再试(新能力先在新版本上线)。 +- 应用版本发布走 `dws devapp version ...`(命令树以 `dws devapp version --help` 为准)。 + +## 6. 事件 / 回调(Webhooks) + +- 事件订阅 `dws devapp event ...` 为**待实现能力**(后端待发布)→ 标 `TODO(待验证)`,不要当可用。 +- 企业回调相关接口见本 skill [llm.md](../llm.md) 的相应能力域。 diff --git a/skills/dingtalk-open-platform/references/workflow.md b/skills/dingtalk-open-platform/references/workflow.md new file mode 100644 index 00000000..36704113 --- /dev/null +++ b/skills/dingtalk-open-platform/references/workflow.md @@ -0,0 +1,84 @@ +# 端到端工作流(发现 → 前置 → 调用 → 验证 → 恢复) + +把运行闭环固化为可复制步骤。所有 dws 命令均 `--help` 核验;dws 仅为可选 CLI,与直接发 HTTP 等价。 + +## 0. 拿 access_token(调用前置) + +```http +# 新版(推荐):token 走业务请求 Header +POST https://api.dingtalk.com/v1.0/oauth2/accessToken +Content-Type: application/json + +{"appKey":"","appSecret":""} +``` +```http +# 旧版:token 走 query access_token +GET https://oapi.dingtalk.com/gettoken?appkey=&appsecret= +``` +- token 必须缓存,禁止每请求重换;日志只记 token hash / operation / path / errcode / requestId。 +- 用 dws 时凭证读取走 `dws devapp credentials get --unified-app-id --format json`(敏感,不回显)。 + +## 1. 只读接口(最常见) + +```bash +# 发现:先查 llm.md 定位文档,运行态可用时: +dws devdoc article search --query "获取用户信息" --format json +# 前置:未登录则 dws auth login(无头环境 dws auth login --device) +dws auth status --format json +# 调用: +dws api GET /v1.0/contact/users/me --format json +# 验证:HTTP 2xx + 业务字段;失败转 §4 +``` + +## 2. 写接口(setup → target → verify → cleanup) + +```bash +dws devapp permission list --unified-app-id ID --status UNAUTHED --format json +dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json +# → 展示摘要 → 用户当前轮确认 → 加 --yes +dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --yes --format json +dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json # verify +# cleanup:测试资源用完回收(取消权限 / 删除测试应用属 [危险],需确认) +``` + +## 3. readback 三态 + +| 态 | 判据 | 下一步 | +| --- | --- | --- | +| 成功 | HTTP 2xx + 业务成功字段 | 继续后续动作 | +| `needs_permission` | 401/403 / 权限未授 | `dws devapp permission add`(→ §2)| +| `needs_resource` | 缺前置 ID / 资源 | 按文档 `resource_requirements` 用 `dws ...` 获取 | + +## 4. 恢复与回流 + +1. 失败先加 `--verbose` 重试**一次**。 +2. 有 requestId → `dws devdoc error diagnose --request-id --query "..." --format json`([reference.md](reference.md))。 +3. 按错误码三元组取 next_action 自恢复;网关未注册记 `needs_gateway_tool_registration`。 +4. 同一命令重试 **≤ 3 次**;仍失败 → 停止,附完整错误与 requestId 报告。 + +## 5. 危险操作确认清单([危险],必须用户确认) + +| 产品域 | 命令 | 影响 | +| --- | --- | --- | +| devapp | `delete` | 删除应用(异步生效,不可逆)| +| devapp | `inactive` | 停用应用 | +| devapp | `permission remove` | 取消已授权限 | +| devapp | `version publish` | 发布版本(含高敏权限需 `--confirm-sensitive`)| +| devapp | `member remove` | 移除应用成员 | +| devapp | `robot offline` | 下线机器人 | +| api | 任意 `POST/PUT/PATCH/DELETE` 写接口 | 按接口 side_effect 评估,先 `--dry-run` | + +确认流程:`Step 1 展示摘要(操作+目标+影响)→ Step 2 用户明确确认 → Step 3 加 --yes 执行`。 + +## 6. 完整示例(创建机器人应用) + +```text +devapp create --dry-run → 确认 → create --yes + → devapp get 确认 unifiedAppId + → credentials get(敏感,不回显) + → permission add(机器人发消息,dry-run → 确认 → --yes) + → robot create(dry-run → 确认 → --yes) + → version create → check-approval → publish(确认)→ status 验证 +``` + +> 应用管理每步 flag 细节以 `dws devapp <子命令> --help` 为准。 From 0a8de62041b6c1a009ad115857339a6ba86de1c1 Mon Sep 17 00:00:00 2001 From: "shangguanxuan.sgx" Date: Mon, 15 Jun 2026 16:47:35 +0800 Subject: [PATCH 03/47] Revert "feat(skills): add self-contained dingtalk-open-platform doc skill" This reverts commit 2c51774150a821fd992ce6e4d776effdf4beff31. --- skills/dingtalk-open-platform/SKILL.md | 110 -- skills/dingtalk-open-platform/llm.md | 1743 ----------------- .../references/api-catalog.md | 1739 ---------------- .../references/api-invocation.md | 57 - .../dingtalk-open-platform/references/auth.md | 71 - .../references/checklist.md | 27 - .../references/discovery.md | 43 - .../references/gotchas.md | 17 - .../references/quick-reference.md | 51 - .../references/reference.md | 61 - .../references/workflow.md | 84 - 11 files changed, 4003 deletions(-) delete mode 100644 skills/dingtalk-open-platform/SKILL.md delete mode 100644 skills/dingtalk-open-platform/llm.md delete mode 100644 skills/dingtalk-open-platform/references/api-catalog.md delete mode 100644 skills/dingtalk-open-platform/references/api-invocation.md delete mode 100644 skills/dingtalk-open-platform/references/auth.md delete mode 100644 skills/dingtalk-open-platform/references/checklist.md delete mode 100644 skills/dingtalk-open-platform/references/discovery.md delete mode 100644 skills/dingtalk-open-platform/references/gotchas.md delete mode 100644 skills/dingtalk-open-platform/references/quick-reference.md delete mode 100644 skills/dingtalk-open-platform/references/reference.md delete mode 100644 skills/dingtalk-open-platform/references/workflow.md diff --git a/skills/dingtalk-open-platform/SKILL.md b/skills/dingtalk-open-platform/SKILL.md deleted file mode 100644 index 46be3c2e..00000000 --- a/skills/dingtalk-open-platform/SKILL.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -name: dingtalk-open-platform -description: >- - 指导 AI Agent 发现、鉴权、调用、验证并排错钉钉开放平台 OpenAPI / CLI / 应用能力的执行手册。 - Use when 用户要 调钉钉开放平台接口 / OpenAPI、建企业内部或第三方应用、拿 access_token、申请权限 scope、 - 订阅事件、查开发文档或错误码、用 dws 调接口。给出 base URL、Header、应用类型选型、分步工作流与校验清单。 - Distinct from 单纯的应用增删改管理 与 钉钉云文档内容读写。 - Do NOT use for 钉钉客户端 UI 设计、后台人工运营、未开放的内部能力。 - 口语同义词:开放平台、开发者后台、open api、调接口、接口怎么调。 -cli_version: ">=1.0.15" -metadata: - category: developer-platform - api_base: "https://api.dingtalk.com" - legacy_api_base: "https://oapi.dingtalk.com" - doc_base: "https://open.dingtalk.com/document" ---- - -# 钉钉开放平台 Skill(AI Agent 执行手册) - -每一步都回答「你下一步敲什么」。不得凭记忆猜 endpoint / 参数 / scope / ID;先查 [llm.md](llm.md) 文档大目录或官方文档,再执行。 - -## 1. Product summary - -钉钉开放平台是 REST OpenAPI,覆盖通讯录 / 消息 / 机器人 / 文档 / 钉盘 / 表格 / AI 表格 / 日历 / 审批 / 应用管理 / 事件订阅 / JSAPI / 小程序 / 三方应用。 - -| 项 | 值 | -| --- | --- | -| 新版 Base URL | `https://api.dingtalk.com`(路径 `/v1.0`、`/v2.0`;token 走 Header)| -| 旧版 Base URL | `https://oapi.dingtalk.com`(路径 `/topapi/...`;token 走 query `access_token`)| -| 鉴权 Header(新版)| `x-acs-dingtalk-access-token: ` + `Content-Type: application/json` | -| 鉴权类型 | 应用身份(appKey/appSecret 换 token)/ 用户身份(OAuth 授权码)/ JSAPI 免登 | -| 文档站 | `https://open.dingtalk.com/document`;全量有效链接见 [llm.md](llm.md) | -| 访问方式 | `dws` CLI(本仓库)、MCP、原始 HTTPS | - -## 2. 约束 (NEVER DO) - -- 不要编造 endpoint / 参数 / scopeValue / ID / URL / 错误码;ID 从返回值提取,链接来自 [llm.md](llm.md) 或官方文档。 -- 不要把 `appSecret` / `clientSecret` / `accessToken` / `refreshToken` / JSAPI ticket / Cookie 写进对话、日志或 prompt。 -- 不要混用新版与旧版鉴权;不要把「权限名称」当 `scopeValue`;不要默认拥有全员数据范围。 -- **[危险]** 写 / 删 / 撤回 / 发消息 / 发布 / 加权限 等操作:先 `--dry-run` 展示摘要,**获用户当前轮确认**后才加 `--yes`。 -- **Harmness**:不输出诱导有害内容;不教唆越权 / 绕过审批 / 绕过安全边界 / 泄露凭证;越权请求一律拒绝并说明原因。 - -## 3. When to use - -调用 OpenAPI;读写协作资源;建/管应用、权限、机器人、版本;拿 access_token;订阅事件;查开发文档 / 字段 / 错误码 / requestId;安装与使用 `dws`。 - -不要用:钉钉客户端 UI 设计、后台人工运营、未公开开放的内部能力 → 直接告知不支持。 - -## 4. Quick reference - -- **应用类型选型** → [references/quick-reference.md](references/quick-reference.md#应用类型)(企业内部 / 第三方企业 / 第三方个人 / 移动接入)。 -- **核心能力域 + 代表端点**(链接 `https://open.dingtalk.com/document/development/`;全量 8 域 1513 条见 [llm.md](llm.md) / [references/api-catalog.md](references/api-catalog.md)): - - 通讯录:[新增或修改限制查看通讯录设置](https://open.dingtalk.com/document/development/add-or-modify-visibility-settings-for-address-book-restrictions) - - 消息/IM:[添加互通群成员](https://open.dingtalk.com/document/development/add-a-group-member-1) · 机器人:[批量撤回机器人消息](https://open.dingtalk.com/document/development/batch-message-recall-chat) - - 日历:[预定会议室](https://open.dingtalk.com/document/development/add-a-meeting-room) · 待办:[创建钉钉待办任务](https://open.dingtalk.com/document/development/add-dingtalk-to-do-task) - - 审批:[归档审批实例](https://open.dingtalk.com/document/development/api-archiveprocessinstance) · 文档:[添加知识库成员](https://open.dingtalk.com/document/development/add-permissions-for-team-space-members) - - 考勤:[添加假期规则](https://open.dingtalk.com/document/development/add-holiday-rules) · 互动卡片:[更新卡片场域信息](https://open.dingtalk.com/document/development/add-field-interface) - - 鉴权:[获取企业内部应用 accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app) · 调用规范:[服务端 API 怎么调](https://open.dingtalk.com/document/development/how-to-call-apis) -- **必需 Header / 限流 / 分页 / 大小** → [references/quick-reference.md](references/quick-reference.md#header限流分页)。 -- **dws CLI 速查**(可选工具,等价于直接发 HTTP 的一种调用/发现方式;命令均 `--help` 核验): -```bash -dws auth status --format json # 看登录态 -dws auth login --device # 无头环境扫码登录 -dws devdoc article search "<关键词>" --format json # 查开发文档 -dws api GET /v1.0/contact/users/me --format json # 原始调用(自有应用凭证) -dws devapp list --name <应用名> --format json # 开放平台应用 CRUD / 凭证 / 权限 -``` - -## 5. Decision guidance - -| 选择题 | 用哪个 | 依据 | -| --- | --- | --- | -| 应用类型 | 单企业自建→企业内部应用;多企业 ISV→第三方企业应用;面向个人→第三方个人应用;端内免登→移动接入 | [quick-reference.md](references/quick-reference.md#应用类型) | -| 新版 vs 旧版 | 优先新版 `api.dingtalk.com`(Header token);仅旧接口用 `oapi.dingtalk.com`(query token) | [api-invocation.md](references/api-invocation.md) | -| token 类型 | 组织服务端能力→应用 accessToken;个人数据/免登→用户身份;端内→JSAPI | [auth.md](references/auth.md) | -| 轮询 vs 事件订阅 | 一次性结果→同步调用;持续变更→事件订阅(`configure-event-subcription`)| [reference.md](references/reference.md) | -| 访问方式 | 有产品糖命令→`dws `;只有 HTTP 契约→`dws api`;CLI 缺失→HTTPS | [api-invocation.md](references/api-invocation.md) | - -## 6. Workflow - -发现 → 前置 → 调用 → 验证 → 恢复,每步真实命令见 [references/workflow.md](references/workflow.md): - -1. **发现**:[llm.md](llm.md) 定位能力域→接口;运行态 `dws devdoc article search "<关键词>" --format json`。 -2. **建应用 / 拿凭证**:`dws devapp create ...`([危险],先 dry-run)→ `dws devapp credentials get --unified-app-id --format json`(凭证敏感,不回显)。 -3. **申请权限 scope**:`dws devapp permission add --unified-app-id --permissions --dry-run --format json` → 确认 → `--yes`。 -4. **拿 access_token**:`POST https://api.dingtalk.com/v1.0/oauth2/accessToken` body `{"appKey","appSecret"}`;token 必须缓存,禁止每请求重换。 -5. **首次调用**:`dws api {METHOD} {PATH} --data '{...}' --format json`(Header token 由 dws 注入)。 -6. **readback 校验**:成功(2xx+业务字段) / `needs_permission` / `needs_resource`;写接口走 setup→target→verify→cleanup。 -7. **错误排查**:`dws devdoc error diagnose --error-code | --request-id --query "..." --format json`,保留 `requestId`。 -8. **清理**:测试资源用完回收(删除/取消属 [危险],需确认)。 - -## 7. Common gotchas - -- MCP 默认凭证 ≠ raw API 凭证:`dws api` 只认自有应用 `--client-id/--client-secret`,MCP 加密 token 不支持 raw 调用。 -- scope 未开通就调用 → 403/无权限:先 `dws devapp permission add` 申请并发版。 -- 新旧 path / token 混用(Header token 配 `oapi` query 接口)→ 鉴权失败。 -- token 类型用错(用应用 token 调个人数据接口)→ 无权限。 -- devdoc 运行态网关 Forbidden / `PARAM_ERROR - 未找到指定工具` → 记 `needs_gateway_tool_registration`,降级 [llm.md](llm.md)。 -- 多候选默认选第一个 / 写操作未 dry-run / 丢失 requestId / 高频刷新 token → 见 [references/gotchas.md](references/gotchas.md)。 - -## 8. Verification checklist - -上线前逐项核对(详见 [references/checklist.md](references/checklist.md)):已查 llm.md 或官方文档;已解析稳定 ID;已确认 token 类型 + scope + 数据范围;Header 正确;限流 / 分页已处理;写操作已 dry-run + 确认;错误处理覆盖鉴权/权限/限流/参数;能回读则已回读;保留 operation/requestId。 - -## 9. Resources - -- [llm.md](llm.md):开放平台文档大目录(`development/` 全量有效链接,按 8 能力域归类)。 -- [references/api-catalog.md](references/api-catalog.md):同源全量有效链接目录(skill 内离线)。 -- `https://open.dingtalk.com/document`:官方文档站。 -- 细节参考:[auth.md](references/auth.md) · [quick-reference.md](references/quick-reference.md) · [api-invocation.md](references/api-invocation.md) · [workflow.md](references/workflow.md) · [reference.md](references/reference.md) · [gotchas.md](references/gotchas.md) · [checklist.md](references/checklist.md) · [discovery.md](references/discovery.md) diff --git a/skills/dingtalk-open-platform/llm.md b/skills/dingtalk-open-platform/llm.md deleted file mode 100644 index ac144f46..00000000 --- a/skills/dingtalk-open-platform/llm.md +++ /dev/null @@ -1,1743 +0,0 @@ -# 钉钉开放平台文档大目录 - -> 钉钉开放平台官方文档大目录,供 AI Agent 发现接口:按能力域组织,每条为官方文档有效链接(`https://open.dingtalk.com/document/development/`,经内容长度校验)。有效 / 无效 / 总数统计见下方区块首行。发现接口后按文档调用(可用 dws CLI 或直接 HTTP)。 - -## 手册 - -- [执行手册 SKILL.md](SKILL.md): 发现 → 鉴权 → 调用 → 验证 → 排错 的分步执行手册。 -- [API 文档目录 api-catalog.md](references/api-catalog.md): 同源全量有效链接目录。 -- 官方文档站:`https://open.dingtalk.com/document` - - - - -> 全量 slug **1520** 条;有效(≥50000B) **1513** 条,无效(空壳) **7** 条;本目录收录 **1513** 条真实文档链接。 - -## 应用开发(642 条) - -二级 Tab:开发指南 / 服务端API / 客户端JSAPI / 事件订阅 / 钉钉CLI / 开发工具 / 平台服务(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 钉钉文档(71 条) - -- [修改知识库文档成员权限](https://open.dingtalk.com/document/development/update-team-space-document-user-permissions): `update-team-space-document-user-permissions` -- [创建工作表](https://open.dingtalk.com/document/development/create-a-worksheet): `create-a-worksheet` -- [创建快捷方式](https://open.dingtalk.com/document/development/api-createshortcut): `api-createshortcut` -- [创建条件格式规则](https://open.dingtalk.com/document/development/create-conditional-formatting-rules): `create-conditional-formatting-rules` -- [创建浮动图片](https://open.dingtalk.com/document/development/api-createfloatimage): `api-createfloatimage` -- [创建知识库文档](https://open.dingtalk.com/document/development/create-team-space-document): `create-team-space-document` -- [创建筛选](https://open.dingtalk.com/document/development/api-createfilter): `api-createfilter` -- [创建筛选视图](https://open.dingtalk.com/document/development/api-createfilterview): `api-createfilterview` -- [删除下拉列表](https://open.dingtalk.com/document/development/delete-drop-down-list): `delete-drop-down-list` -- [删除列](https://open.dingtalk.com/document/development/delete-column): `delete-column` -- [删除块元素](https://open.dingtalk.com/document/development/api-docdeleteblock): `api-docdeleteblock` -- [删除工作表](https://open.dingtalk.com/document/development/delete-classic-workbooks): `delete-classic-workbooks` -- [删除浮动图片](https://open.dingtalk.com/document/development/api-deletefloatimage): `api-deletefloatimage` -- [删除知识库成员](https://open.dingtalk.com/document/development/delete-team-space-user-permissions): `delete-team-space-user-permissions` -- [删除知识库文档](https://open.dingtalk.com/document/development/delete-team-space-documents): `delete-team-space-documents` -- [删除知识库文档成员](https://open.dingtalk.com/document/development/delete-team-space-document-permissions): `delete-team-space-document-permissions` -- [删除筛选](https://open.dingtalk.com/document/development/api-deletefilter): `api-deletefilter` -- [删除筛选条件](https://open.dingtalk.com/document/development/api-clearfiltercriteria): `api-clearfiltercriteria` -- [删除筛选视图](https://open.dingtalk.com/document/development/api-deletefilterview): `api-deletefilterview` -- [删除筛选视图条件](https://open.dingtalk.com/document/development/api-clearfilterviewcriteria): `api-clearfilterviewcriteria` -- [删除行](https://open.dingtalk.com/document/development/delete-row): `delete-row` -- [合并单元格](https://open.dingtalk.com/document/development/merge-cells): `merge-cells` -- [在段落末尾追加文本](https://open.dingtalk.com/document/development/api-docappendtext): `api-docappendtext` -- [在段落末尾追加行内元素](https://open.dingtalk.com/document/development/api-docappendparagraph): `api-docappendparagraph` -- [复制文档](https://open.dingtalk.com/document/development/api-copydoc): `api-copydoc` -- [工作表中追加行](https://open.dingtalk.com/document/development/append-line): `append-line` -- [批量设置列宽](https://open.dingtalk.com/document/development/api-setcolumnswidth): `api-setcolumnswidth` -- [批量设置行高](https://open.dingtalk.com/document/development/api-setrowsheight): `api-setrowsheight` -- [指定列左侧插入若干列](https://open.dingtalk.com/document/development/insert-column-before-column): `insert-column-before-column` -- [指定行上方插入若干行](https://open.dingtalk.com/document/development/insert-rows-before-rows): `insert-rows-before-rows` -- [插入下拉列表](https://open.dingtalk.com/document/development/insert-drop-down-list): `insert-drop-down-list` -- [插入内容](https://open.dingtalk.com/document/development/api-insertcontent): `api-insertcontent` -- [插入块元素](https://open.dingtalk.com/document/development/api-docinsertblocks): `api-docinsertblocks` -- [新建知识库](https://open.dingtalk.com/document/development/create-a-team-space): `create-a-team-space` -- [更新单元格区域](https://open.dingtalk.com/document/development/update-cell-properties): `update-cell-properties` -- [更新块元素](https://open.dingtalk.com/document/development/api-docblocksmodify): `api-docblocksmodify` -- [更新工作表](https://open.dingtalk.com/document/development/update-worksheet): `update-worksheet` -- [更新浮动图片](https://open.dingtalk.com/document/development/api-updatefloatimage): `api-updatefloatimage` -- [更新知识库成员权限](https://open.dingtalk.com/document/development/update-team-space-user-permissions): `update-team-space-user-permissions` -- [更新筛选](https://open.dingtalk.com/document/development/api-updatefilter): `api-updatefilter` -- [更新筛选视图](https://open.dingtalk.com/document/development/api-updatefilterview): `api-updatefilterview` -- [查找工作表中的单元格](https://open.dingtalk.com/document/development/find-the-next-eligible-cell): `find-the-next-eligible-cell` -- [查找所有符合条件的单元格](https://open.dingtalk.com/document/development/find-all-matching-cells): `find-all-matching-cells` -- [查询块元素](https://open.dingtalk.com/document/development/api-docblocksquery): `api-docblocksquery` -- [查询用户有权限的知识库列表](https://open.dingtalk.com/document/development/querying-the-list-of-user-team-spaces): `querying-the-list-of-user-team-spaces` -- [查询知识库下的目录结构](https://open.dingtalk.com/document/development/query-the-directory-tree-in-the-knowledge-base): `query-the-directory-tree-in-the-knowledge-base` -- [查询知识库信息](https://open.dingtalk.com/document/development/query-team-space): `query-team-space` -- [查询知识库节点信息](https://open.dingtalk.com/document/development/query-knowledge-base-node-information): `query-knowledge-base-node-information` -- [根据 dentryUuid 获取 spaceId](https://open.dingtalk.com/document/development/api-getdentryidbyuuid): `api-getdentryidbyuuid` -- [添加知识库成员](https://open.dingtalk.com/document/development/add-permissions-for-team-space-members): `add-permissions-for-team-space-members` -- [添加知识库文档成员](https://open.dingtalk.com/document/development/add-workspace-document-user-permissions): `add-workspace-document-user-permissions` -- [清除单元格区域内所有内容](https://open.dingtalk.com/document/development/clear-all): `clear-all` -- [清除单元格区域内数据](https://open.dingtalk.com/document/development/clear-cell-data): `clear-cell-data` -- [知识库转交所有者](https://open.dingtalk.com/document/development/api-handoveryworkspace): `api-handoveryworkspace` -- [筛选排序](https://open.dingtalk.com/document/development/api-sortfilter): `api-sortfilter` -- [置顶知识库](https://open.dingtalk.com/document/development/api-pinspace): `api-pinspace` -- [获取 dentryUuid 信息](https://open.dingtalk.com/document/development/api-getuuidbydentryid): `api-getuuidbydentryid` -- [获取任务状态](https://open.dingtalk.com/document/development/api-gettaskinfo): `api-gettaskinfo` -- [获取单元格区域](https://open.dingtalk.com/document/development/get-cell-properties): `get-cell-properties` -- [获取工作表](https://open.dingtalk.com/document/development/obtain-worksheet-properties): `obtain-worksheet-properties` -- [获取所有工作表](https://open.dingtalk.com/document/development/obtain-all-worksheets): `obtain-all-worksheets` -- [获取筛选](https://open.dingtalk.com/document/development/api-getfilter): `api-getfilter` -- [获取筛选视图列表](https://open.dingtalk.com/document/development/api-getfilterviews): `api-getfilterviews` -- [获取资源上传信息](https://open.dingtalk.com/document/development/api-getresourceuploadinfo): `api-getresourceuploadinfo` -- [覆写文档(个人授权)](https://open.dingtalk.com/document/development/api-docupdatecontent): `api-docupdatecontent` -- [覆写文档(应用授权)](https://open.dingtalk.com/document/development/api-doc-updatecontent): `api-doc-updatecontent` -- [设置列隐藏或显示](https://open.dingtalk.com/document/development/set-column-visibility): `set-column-visibility` -- [设置筛选条件](https://open.dingtalk.com/document/development/api-setfiltercriteria): `api-setfiltercriteria` -- [设置筛选视图条件](https://open.dingtalk.com/document/development/api-setfilterviewcriteria): `api-setfilterviewcriteria` -- [设置自动行高](https://open.dingtalk.com/document/development/set-row-height-automatically): `set-row-height-automatically` -- [设置行隐藏或显示](https://open.dingtalk.com/document/development/set-row-visibility): `set-row-visibility` - -### 存储(53 条) - -- [修改权限](https://open.dingtalk.com/document/development/modify-permissions-file): `modify-permissions-file` -- [修改权限](https://open.dingtalk.com/document/development/modify-storage-permissions): `modify-storage-permissions` -- [初始化文件分片上传](https://open.dingtalk.com/document/development/initialize-a-multipart-upload-object): `initialize-a-multipart-upload-object` -- [删除回收项](https://open.dingtalk.com/document/development/delete-recycle-item): `delete-recycle-item` -- [删除文件或文件夹](https://open.dingtalk.com/document/development/delete-a-file-or-folder): `delete-a-file-or-folder` -- [删除文件或文件夹的应用属性](https://open.dingtalk.com/document/development/delete-file-app-attribute): `delete-file-app-attribute` -- [删除权限](https://open.dingtalk.com/document/development/delete-permissions-file): `delete-permissions-file` -- [删除权限](https://open.dingtalk.com/document/development/delete-storage-permissions): `delete-storage-permissions` -- [取消订阅文件变更事件](https://open.dingtalk.com/document/development/unsubscribe-from-file-change-events): `unsubscribe-from-file-change-events` -- [复制文件或文件夹](https://open.dingtalk.com/document/development/copy-an-object): `copy-an-object` -- [恢复文件历史版本](https://open.dingtalk.com/document/development/restore-previous-versions-of-files): `restore-previous-versions-of-files` -- [批量删除回收项](https://open.dingtalk.com/document/development/batch-delete-recycle-items): `batch-delete-recycle-items` -- [批量删除文件或文件夹](https://open.dingtalk.com/document/development/delete-files-or-folders-in-bulk): `delete-files-or-folders-in-bulk` -- [批量复制文件或文件夹](https://open.dingtalk.com/document/development/copy-files-or-folders-in-bulk): `copy-files-or-folders-in-bulk` -- [批量移动文件或文件夹](https://open.dingtalk.com/document/development/bulk-move-files-or-folders): `bulk-move-files-or-folders` -- [批量获取文件或文件夹信息](https://open.dingtalk.com/document/development/get-file-or-folder-information-in-bulk): `get-file-or-folder-information-in-bulk` -- [批量获取文件缩略图](https://open.dingtalk.com/document/development/get-file-thumbnails-in-bulk): `get-file-thumbnails-in-bulk` -- [批量还原回收项](https://open.dingtalk.com/document/development/batch-restore-recycled-items): `batch-restore-recycled-items` -- [提交文件](https://open.dingtalk.com/document/development/submit-documents): `submit-documents` -- [提交文件](https://open.dingtalk.com/document/development/submittal-file): `submittal-file` -- [搜索文件](https://open.dingtalk.com/document/development/search-for-files): `search-for-files` -- [搜索知识库](https://open.dingtalk.com/document/development/search-knowledge-base): `search-knowledge-base` -- [更新文件或文件夹的应用属性](https://open.dingtalk.com/document/development/update-file-application-properties): `update-file-application-properties` -- [添加文件夹](https://open.dingtalk.com/document/development/add-folder): `add-folder` -- [添加权限](https://open.dingtalk.com/document/development/add-permissions-file): `add-permissions-file` -- [添加权限](https://open.dingtalk.com/document/development/add-storage-permissions): `add-storage-permissions` -- [添加空间](https://open.dingtalk.com/document/development/add-space): `add-space` -- [清空回收站](https://open.dingtalk.com/document/development/empty-the-recycle-bin): `empty-the-recycle-bin` -- [知识库下载文件](https://open.dingtalk.com/document/development/knowledge-base-download-file): `knowledge-base-download-file` -- [移动文件或文件夹](https://open.dingtalk.com/document/development/move-a-file-or-folder): `move-a-file-or-folder` -- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-storage-related-information): `obtain-enterprise-storage-related-information` -- [获取回收站信息](https://open.dingtalk.com/document/development/obtain-information-about-the-recycle-bin): `obtain-information-about-the-recycle-bin` -- [获取回收项信息](https://open.dingtalk.com/document/development/obtain-recycling-item-information): `obtain-recycling-item-information` -- [获取回收项列表](https://open.dingtalk.com/document/development/gets-the-list-of-recycle-items): `gets-the-list-of-recycle-items` -- [获取存储中异步任务信息](https://open.dingtalk.com/document/development/get-the-asynchronous-task-information-in-storage): `get-the-asynchronous-task-information-in-storage` -- [获取应用信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-current-application): `obtains-the-information-about-the-current-application` -- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-file-upload-informations): `obtain-file-upload-informations` -- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-storage-upload-information): `obtain-storage-upload-information` -- [获取文件下载信息](https://open.dingtalk.com/document/development/obtains-the-download-information-about-a-file): `obtains-the-download-information-about-a-file` -- [获取文件分片上传信息](https://open.dingtalk.com/document/development/obtains-the-information-about-multipart-uploads-of-an-object): `obtains-the-information-about-multipart-uploads-of-an-object` -- [获取文件或文件夹信息](https://open.dingtalk.com/document/development/obtain-file-or-folder-information): `obtain-file-or-folder-information` -- [获取文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-files-or-folders): `get-a-list-of-files-or-folders` -- [获取文件版本列表](https://open.dingtalk.com/document/development/obtains-a-list-of-file-versions): `obtains-a-list-of-file-versions` -- [获取文件预览或编辑信息](https://open.dingtalk.com/document/development/obtains-the-object-preview-or-editing-information): `obtains-the-object-preview-or-editing-information` -- [获取权限列表](https://open.dingtalk.com/document/development/get-permission-list): `get-permission-list` -- [获取权限列表](https://open.dingtalk.com/document/development/get-the-storage-permission-list): `get-the-storage-permission-list` -- [获取权限继承模式](https://open.dingtalk.com/document/development/get-permission-inheritance-mode): `get-permission-inheritance-mode` -- [获取空间下所有文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-all-files-or-folders-under-a): `get-a-list-of-all-files-or-folders-under-a` -- [获取空间信息](https://open.dingtalk.com/document/development/get-space-information): `get-space-information` -- [订阅文件变更事件](https://open.dingtalk.com/document/development/subscribe-to-file-change-events): `subscribe-to-file-change-events` -- [设置权限继承模式](https://open.dingtalk.com/document/development/set-permission-inheritance-mode): `set-permission-inheritance-mode` -- [还原回收项](https://open.dingtalk.com/document/development/restore-recycle-items): `restore-recycle-items` -- [重命名文件或文件夹](https://open.dingtalk.com/document/development/rename-a-file-or-folder): `rename-a-file-or-folder` - -### 项目管理(50 条) - -- [任务迁移至回收站](https://open.dingtalk.com/document/development/archive-tasks): `archive-tasks` -- [创建实际工时](https://open.dingtalk.com/document/development/create-actual-work): `create-actual-work` -- [创建或更新项目概览中自定义字段值](https://open.dingtalk.com/document/development/create-or-update-field-values-project-overview): `create-or-update-field-values-project-overview` -- [创建自由任务](https://open.dingtalk.com/document/development/create-a-free-task): `create-a-free-task` -- [创建计划工时](https://open.dingtalk.com/document/development/create-planned-work): `create-planned-work` -- [创建项目](https://open.dingtalk.com/document/development/create-project): `create-project` -- [创建项目任务](https://open.dingtalk.com/document/development/create-a-project-task): `create-a-project-task` -- [删除任务](https://open.dingtalk.com/document/development/delete-task): `delete-task` -- [删除项目成员](https://open.dingtalk.com/document/development/delete-project-members): `delete-project-members` -- [增加或删除自由任务的参与者](https://open.dingtalk.com/document/development/change-task-participant): `change-task-participant` -- [归档项目](https://open.dingtalk.com/document/development/archiving-project): `archiving-project` -- [恢复项目归档](https://open.dingtalk.com/document/development/cancel-project-archiving): `cancel-project-archiving` -- [批量获取自由任务详情](https://open.dingtalk.com/document/development/obtains-details-about-multiple-free-tasks): `obtains-details-about-multiple-free-tasks` -- [搜索任务工作流状态](https://open.dingtalk.com/document/development/search-task-workflow-status): `search-task-workflow-status` -- [搜索企业项目模板](https://open.dingtalk.com/document/development/search-for-enterprise-custom-templates-by-project-template-name): `search-for-enterprise-custom-templates-by-project-template-name` -- [更新任务优先级](https://open.dingtalk.com/document/development/update-task-priority): `update-task-priority` -- [更新任务参与者](https://open.dingtalk.com/document/development/update-task-participants): `update-task-participants` -- [更新任务备注](https://open.dingtalk.com/document/development/update-task-notes): `update-task-notes` -- [更新任务工作流状态](https://open.dingtalk.com/document/development/update-task-workflow-status): `update-task-workflow-status` -- [更新任务开始时间](https://open.dingtalk.com/document/development/update-task-start-time): `update-task-start-time` -- [更新任务截止时间](https://open.dingtalk.com/document/development/update-task-deadline): `update-task-deadline` -- [更新任务执行者](https://open.dingtalk.com/document/development/update-task-performer): `update-task-performer` -- [更新任务标题](https://open.dingtalk.com/document/development/update-task-content): `update-task-content` -- [更新自由任务备注](https://open.dingtalk.com/document/development/update-free-task-notes): `update-free-task-notes` -- [更新自由任务截止时间](https://open.dingtalk.com/document/development/change-free-task-deadline): `change-free-task-deadline` -- [更新自由任务执行者](https://open.dingtalk.com/document/development/change-free-task-executor): `change-free-task-executor` -- [更新自由任务标题](https://open.dingtalk.com/document/development/change-free-task-title): `change-free-task-title` -- [更新自由任务状态](https://open.dingtalk.com/document/development/change-free-task-status): `change-free-task-status` -- [更新自由任务的优先级](https://open.dingtalk.com/document/development/change-free-task-priority): `change-free-task-priority` -- [更新项目任务的自定义字段值](https://open.dingtalk.com/document/development/update-task-custom-field-value): `update-task-custom-field-value` -- [更新项目所在的分组](https://open.dingtalk.com/document/development/update-project-grouping): `update-project-grouping` -- [查询任务分组](https://open.dingtalk.com/document/development/query-task-grouping): `query-task-grouping` -- [查询任务工作流](https://open.dingtalk.com/document/development/query-task-workflow): `query-task-workflow` -- [查询优先级列表](https://open.dingtalk.com/document/development/query-a-priority-list): `query-a-priority-list` -- [查询员工可见的项目分组](https://open.dingtalk.com/document/development/query-available-project-groups): `query-available-project-groups` -- [查询用户任务信息列表](https://open.dingtalk.com/document/development/querying-user-tasks): `querying-user-tasks` -- [查询项目](https://open.dingtalk.com/document/development/query-enterprise-all-projects): `query-enterprise-all-projects` -- [查询项目中的任务](https://open.dingtalk.com/document/development/query-tasks-in-a-project): `query-tasks-in-a-project` -- [查询项目状态](https://open.dingtalk.com/document/development/query-project-status): `query-project-status` -- [根据userId获取Teambition项目用户ID](https://open.dingtalk.com/document/development/obtain-dingtalk-teambition-user-id-based-on-userid): `obtain-dingtalk-teambition-user-id-based-on-userid` -- [根据项目模板创建项目](https://open.dingtalk.com/document/development/create-a-project-from-a-project-template): `create-a-project-from-a-project-template` -- [添加任务的关联内容](https://open.dingtalk.com/document/development/create-a-linked-object-associated-with-a-task): `create-a-linked-object-associated-with-a-task` -- [添加项目成员](https://open.dingtalk.com/document/development/add-project-members): `add-project-members` -- [获取Teambition项目企业ID](https://open.dingtalk.com/document/development/obtain-the-teambition-enterprise-id): `obtain-the-teambition-enterprise-id` -- [获取任务列表](https://open.dingtalk.com/document/development/get-task-list): `get-task-list` -- [获取任务详情](https://open.dingtalk.com/document/development/get-task-details): `get-task-details` -- [获取用户加入的项目](https://open.dingtalk.com/document/development/get-projects-joined-by-users): `get-projects-joined-by-users` -- [获取自由任务详情](https://open.dingtalk.com/document/development/queries-free-task-details): `queries-free-task-details` -- [获取项目成员](https://open.dingtalk.com/document/development/get-project-members): `get-project-members` -- [项目放入回收站](https://open.dingtalk.com/document/development/items-in-recycle-bin): `items-in-recycle-bin` - -### 通讯录(49 条) - -- [企业账号修改钉钉号](https://open.dingtalk.com/document/development/api-changedingtalkid): `api-changedingtalkid` -- [企业账号转交主管理员(创建者)](https://open.dingtalk.com/document/development/transfer-exclusive-account-to-main-administrator-creator): `transfer-exclusive-account-to-main-administrator-creator` -- [停用企业账号](https://open.dingtalk.com/document/development/disable-an-exclusive-account): `disable-an-exclusive-account` -- [创建上下游组织](https://open.dingtalk.com/document/development/create-a-cooperation-space): `create-a-cooperation-space` -- [删除用户属性可见性设置](https://open.dingtalk.com/document/development/delete-enterprise-employee-attribute-field-visibility-settings): `delete-enterprise-employee-attribute-field-visibility-settings` -- [删除通讯录隐藏设置](https://open.dingtalk.com/document/development/delete-hide-settings): `delete-hide-settings` -- [删除限制查看通讯录设置](https://open.dingtalk.com/document/development/delete-visible-restrictions): `delete-visible-restrictions` -- [启用企业账号](https://open.dingtalk.com/document/development/enable-a-dedicated-account): `enable-a-dedicated-account` -- [异步转译通讯录ID](https://open.dingtalk.com/document/development/asynchronous-address-book-file-content-translation): `asynchronous-address-book-file-content-translation` -- [强制登出企业账号](https://open.dingtalk.com/document/development/force-logout-from-dedicated-account): `force-logout-from-dedicated-account` -- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/apply-for-batch-addition-through-upstream-and-downstream-organizations): `apply-for-batch-addition-through-upstream-and-downstream-organizations` -- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/batch-through-the-application-of-partner-organizations-to-join-contact): `batch-through-the-application-of-partner-organizations-to-join-contact` -- [授权企业账号可加入多组织](https://open.dingtalk.com/document/development/authorize-a-dedicated-account-to-join-multiple-organizations): `authorize-a-dedicated-account-to-join-multiple-organizations` -- [授权其他组织查看本组织的企业账号信息](https://open.dingtalk.com/document/development/api-orgaccountmobilevisibleinotherorg): `api-orgaccountmobilevisibleinotherorg` -- [搜索用户userId](https://open.dingtalk.com/document/development/address-book-search-user-id): `address-book-search-user-id` -- [搜索部门ID](https://open.dingtalk.com/document/development/address-book-search-department-id): `address-book-search-department-id` -- [新增或修改限制查看通讯录设置](https://open.dingtalk.com/document/development/add-or-modify-visibility-settings-for-address-book-restrictions): `add-or-modify-visibility-settings-for-address-book-restrictions` -- [新增或更新通讯录隐藏设置](https://open.dingtalk.com/document/development/update-address-book-hide-settings): `update-address-book-hide-settings` -- [更新伙伴组织在上下游组织内的属性信息](https://open.dingtalk.com/document/development/update-properties-of-branches-in-alibaba-group-1): `update-properties-of-branches-in-alibaba-group-1` -- [更新分支组织在主干组织内的属性信息](https://open.dingtalk.com/document/development/updates-the-property-information-of-a-branch-organization-in-a): `updates-the-property-information-of-a-branch-organization-in-a` -- [查询企业账号拥有的组织](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-organization-that): `you-can-call-this-operation-to-query-the-organization-that` -- [查询企业账号状态](https://open.dingtalk.com/document/development/query-dedicated-account-status-1): `query-dedicated-account-status-1` -- [查询离职记录列表](https://open.dingtalk.com/document/development/query-the-details-of-employees-who-have-left-office): `query-the-details-of-employees-who-have-left-office` -- [根据原dingId查询迁移后的dingId](https://open.dingtalk.com/document/development/query-the-new-dingid-based-on-the-original-dingid): `query-the-new-dingid-based-on-the-original-dingid` -- [根据原unionId查询迁移后的unionId](https://open.dingtalk.com/document/development/the-union-id-that-you-want-to-query-you-can): `the-union-id-that-you-want-to-query-you-can` -- [根据手机号查询用户](https://open.dingtalk.com/document/development/query-users-by-phone-number): `query-users-by-phone-number` -- [根据迁移后的dingId查询原dingId](https://open.dingtalk.com/document/development/query-the-original-dingid-based-on-the-dingid-after-migration): `query-the-original-dingid-based-on-the-dingid-after-migration` -- [根据迁移后的unionId查询原unionId](https://open.dingtalk.com/document/development/query-the-original-union-id-based-on-the-union-id): `query-the-original-union-id-based-on-the-union-id` -- [获取上下游组织的邀请信息](https://open.dingtalk.com/document/development/obtain-the-invitation-information-of-a-cooperation-space): `obtain-the-invitation-information-of-a-cooperation-space` -- [获取上下级组织分支授权的数据](https://open.dingtalk.com/document/development/data-authorized-by-a-branch-of-an-associated-organization): `data-authorized-by-a-branch-of-an-associated-organization` -- [获取企业最新钉钉指数信息](https://open.dingtalk.com/document/development/queries-the-latest-dingtalk-index-information): `queries-the-latest-dingtalk-index-information` -- [获取企业认证信息](https://open.dingtalk.com/document/development/obtain-enterprise-authentication-information): `obtain-enterprise-authentication-information` -- [获取企业邀请信息](https://open.dingtalk.com/document/development/obtain-invitation-information): `obtain-invitation-information` -- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-the-list-of-sub-department-ids): `obtain-the-list-of-sub-department-ids` -- [获取异步转译任务结果](https://open.dingtalk.com/document/development/obtains-the-results-of-an-asynchronous-translation-task): `obtains-the-results-of-an-asynchronous-translation-task` -- [获取用户属性可见性设置](https://open.dingtalk.com/document/development/pull-hidden-property-field-for-enterprise-employees): `pull-hidden-property-field-for-enterprise-employees` -- [获取用户通讯录个人信息](https://open.dingtalk.com/document/development/dingtalk-retrieve-user-information): `dingtalk-retrieve-user-information` -- [获取用户高管模式设置](https://open.dingtalk.com/document/development/get-user-executive-mode-settings): `get-user-executive-mode-settings` -- [获取通讯录隐藏设置](https://open.dingtalk.com/document/development/obtains-the-hide-settings-of-the-address-book): `obtains-the-hide-settings-of-the-address-book` -- [获取部门用户基础信息](https://open.dingtalk.com/document/development/queries-the-simple-information-of-a-department-user): `queries-the-simple-information-of-a-department-user` -- [获取限制查看通讯录设置列表](https://open.dingtalk.com/document/development/gets-a-list-of-address-book-limit-visibility-settings): `gets-a-list-of-address-book-limit-visibility-settings` -- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-an-organization): `disassociate-an-organization` -- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-upstream-and-downstream-organizations): `disassociate-upstream-and-downstream-organizations` -- [设置伙伴组织在上下游组织内的可见范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-branch-in-the-group-1): `set-the-visible-range-of-the-branch-in-the-group-1` -- [设置分支组织在主干组织内的可见范围](https://open.dingtalk.com/document/development/sets-the-visible-range-of-branch-organizations-within-the-group): `sets-the-visible-range-of-branch-organizations-within-the-group` -- [设置用户属性可见性](https://open.dingtalk.com/document/development/add-or-update-the-hidden-settings-of-the-employee-property): `add-or-update-the-hidden-settings-of-the-employee-property` -- [设置部门可见性优先级](https://open.dingtalk.com/document/development/set-address-book-visibility-sub-department-settings-to-take-precedence): `set-address-book-visibility-sub-department-settings-to-take-precedence` -- [设置高管模式](https://open.dingtalk.com/document/development/update-executive-settings): `update-executive-settings` -- [通讯录userId排序](https://open.dingtalk.com/document/development/address-book-userid-sorting): `address-book-userid-sorting` - -### 电子签(41 条) - -- [ISV服务商数据初始化](https://open.dingtalk.com/document/development/offline-isv-service-provider-data-initialization): `offline-isv-service-provider-data-initialization` -- [e签宝数据初始化](https://open.dingtalk.com/document/development/isv-service-provider-data-initialization): `isv-service-provider-data-initialization` -- [创建签署流程](https://open.dingtalk.com/document/development/use-the-api-to-initiate-a-signature-process): `use-the-api-to-initiate-a-signature-process` -- [取消企业授权](https://open.dingtalk.com/document/development/cancel-enterprise-authorization): `cancel-enterprise-authorization` -- [取消企业授权](https://open.dingtalk.com/document/development/e-sign-has-revoked-the-enterprise-authorization): `e-sign-has-revoked-the-enterprise-authorization` -- [套餐转售---分润模式](https://open.dingtalk.com/document/development/package-resale-1-distribution-mode): `package-resale-1-distribution-mode` -- [套餐转售---底价结算模式](https://open.dingtalk.com/document/development/package-resale-2-reserve-price-settlement-mode): `package-resale-2-reserve-price-settlement-mode` -- [套餐转售(分润模式)](https://open.dingtalk.com/document/development/package-resale-profit-distribution-model-1): `package-resale-profit-distribution-model-1` -- [套餐转售(底价结算模式)](https://open.dingtalk.com/document/development/package-resale-base-price-settlement-mode-1): `package-resale-base-price-settlement-mode-1` -- [查询个人是否实名认证](https://open.dingtalk.com/document/development/query-personal-information): `query-personal-information` -- [查询企业是否实名](https://open.dingtalk.com/document/development/check-whether-enterprise-registered-realname-e-sign-treasure): `check-whether-enterprise-registered-realname-e-sign-treasure` -- [查询企业是否实名认证](https://open.dingtalk.com/document/development/query-enterprise-information): `query-enterprise-information` -- [查询套餐余量](https://open.dingtalk.com/document/development/query-package-balance): `query-package-balance` -- [查询用户是否实名](https://open.dingtalk.com/document/development/query-whether-a-user-has-a-real-name): `query-whether-a-user-has-a-real-name` -- [获取个人实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-user-s-real): `obtain-the-address-that-is-redirected-to-the-user-s-real` -- [获取企业e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-status-of-enterprise-e-sign-treasure-micro-application): `obtain-the-status-of-enterprise-e-sign-treasure-micro-application` -- [获取企业实名地址](https://open.dingtalk.com/document/development/obtain-enterprise-real-name-address): `obtain-enterprise-real-name-address` -- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-enterprise-console-address): `get-enterprise-console-address` -- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-the-address-enterprise-console-through-e-sign): `get-the-address-enterprise-console-through-e-sign` -- [获取企业的e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-current-status-of-the-company-s-e-sign-micro-application): `obtain-the-current-status-of-the-company-s-e-sign-micro-application` -- [获取发起签署任务地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-initiating-signing-task): `obtain-the-address-of-the-initiating-signing-task` -- [获取发起签署任务的地址](https://open.dingtalk.com/document/development/obtain-the-address-used-to-initiate-a-signed-task): `obtain-the-address-used-to-initiate-a-signed-task` -- [获取套餐余量](https://open.dingtalk.com/document/development/get-package-margin): `get-package-margin` -- [获取授权的页面地址](https://open.dingtalk.com/document/development/authurl): `authurl` -- [获取授权的页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-authorized-page): `obtain-the-address-of-the-authorized-page` -- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-file-upload-address-1): `obtain-the-file-upload-address-1` -- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-1): `obtain-the-upload-url-of-a-file-1` -- [获取文件详情](https://open.dingtalk.com/document/development/gets-the-file-details): `gets-the-file-details` -- [获取文件详情](https://open.dingtalk.com/document/development/query-file-details): `query-file-details` -- [获取流程任务合同列表](https://open.dingtalk.com/document/development/obtain-the-process-task-contract-list): `obtain-the-process-task-contract-list` -- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtain-the-process-task-print-approval-list-1): `obtain-the-process-task-print-approval-list-1` -- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtains-the-print-approval-list-for-process-tasks): `obtains-the-print-approval-list-for-process-tasks` -- [获取流程任务的所有合同列表](https://open.dingtalk.com/document/development/get-a-list-of-all-contracts-for-the-process-task): `get-a-list-of-all-contracts-for-the-process-task` -- [获取流程任务详情](https://open.dingtalk.com/document/development/obtain-the-task-details-of-the-corresponding-process): `obtain-the-task-details-of-the-corresponding-process` -- [获取流程的签署详情](https://open.dingtalk.com/document/development/get-the-details-of-process-signing): `get-the-details-of-process-signing` -- [获取流程签署详细信息](https://open.dingtalk.com/document/development/get-process-sign-off-details): `get-process-sign-off-details` -- [获取流程详细信息及操作记录](https://open.dingtalk.com/document/development/obtains-the-task-details): `obtains-the-task-details` -- [获取用户实名地址](https://open.dingtalk.com/document/development/obtain-personal-real-name-address): `obtain-personal-real-name-address` -- [获取签署人签署地址](https://open.dingtalk.com/document/development/get-signatory-address): `get-signatory-address` -- [获取签署人签署地址](https://open.dingtalk.com/document/development/obtain-signing-address-of-signatory-1): `obtain-signing-address-of-signatory-1` -- [获取跳转到企业实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-enterprise-s-real): `obtain-the-address-that-is-redirected-to-the-enterprise-s-real` - -### 消息会话(37 条) - -- [人与人会话中机器人发送互动卡片](https://open.dingtalk.com/document/development/send-dingtalk-interactive-cards-to-person-to-person-chat-sessions): `send-dingtalk-interactive-cards-to-person-to-person-chat-sessions` -- [修改钉钉客联互通群名称](https://open.dingtalk.com/document/development/modify-the-group-name): `modify-the-group-name` -- [修改钉钉客联互通群头像](https://open.dingtalk.com/document/development/modify-the-avatar-of-a-communication-group): `modify-the-avatar-of-a-communication-group` -- [关闭互动卡片吊顶](https://open.dingtalk.com/document/development/close-interactive-card-ceiling): `close-interactive-card-ceiling` -- [创建互通群](https://open.dingtalk.com/document/development/create-an-intercommunication-group): `create-an-intercommunication-group` -- [创建并开启互动卡片吊顶](https://open.dingtalk.com/document/development/send-group-helper-message): `send-group-helper-message` -- [创建店铺群](https://open.dingtalk.com/document/development/create-a-store-group): `create-a-store-group` -- [创建钉外两人群](https://open.dingtalk.com/document/development/create-two-people-outside-the-nail): `create-two-people-outside-the-nail` -- [创建钉钉客联两人互通群](https://open.dingtalk.com/document/development/creating-two-groups-of-people): `creating-two-groups-of-people` -- [创建钉钉客联普通互通群](https://open.dingtalk.com/document/development/create-common-group-new-version): `create-common-group-new-version` -- [创建钉钉客联钉外账号](https://open.dingtalk.com/document/development/create-bc-account-association): `create-bc-account-association` -- [发送轻量级互动卡片](https://open.dingtalk.com/document/development/send-lightweight-interactive-cards): `send-lightweight-interactive-cards` -- [发送钉钉互动卡片(高级版)](https://open.dingtalk.com/document/development/send-interactive-dynamic-cards-1): `send-interactive-dynamic-cards-1` -- [在钉钉客联互通群中使用机器人发送消息](https://open.dingtalk.com/document/development/group-robots-send-messages): `group-robots-send-messages` -- [在钉钉客联互通群中使用钉内账号发送消息](https://open.dingtalk.com/document/development/send-b2c-messages): `send-b2c-messages` -- [在钉钉客联互通群中使用钉外账号发送消息](https://open.dingtalk.com/document/development/send-c2b-messages): `send-c2b-messages` -- [批量查询跨钉两人互通群列表](https://open.dingtalk.com/document/development/queries-the-session-information-of-two-population-groups): `queries-the-session-information-of-two-population-groups` -- [批量设置企业群管理员](https://open.dingtalk.com/document/development/batch-setup-group-administrator): `batch-setup-group-administrator` -- [更换钉钉客联互通群群主](https://open.dingtalk.com/document/development/change-group-owner): `change-group-owner` -- [更新机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/update-the-robot-to-send-interactive-cards): `update-the-robot-to-send-interactive-cards` -- [更新群成员的群昵称](https://open.dingtalk.com/document/development/update-group-nicknames): `update-group-nicknames` -- [更新群管理员](https://open.dingtalk.com/document/development/update-group-administrators): `update-group-administrators` -- [更新钉钉互动卡片](https://open.dingtalk.com/document/development/update-dingtalk-interactive-cards): `update-dingtalk-interactive-cards` -- [机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/robots-send-interactive-cards): `robots-send-interactive-cards` -- [查询群内群模板机器人](https://open.dingtalk.com/document/development/search-group-scene-template-robot): `search-group-scene-template-robot` -- [查询群成员](https://open.dingtalk.com/document/development/query-group-members): `query-group-members` -- [查询群禁言状态](https://open.dingtalk.com/document/development/query-group-silence-status): `query-group-silence-status` -- [查询群简要信息](https://open.dingtalk.com/document/development/query-group-information): `query-group-information` -- [查询钉钉客联互通群成员列表](https://open.dingtalk.com/document/development/queries-the-group-member-list): `queries-the-group-member-list` -- [查询钉钉客联钉外账号未读消息数](https://open.dingtalk.com/document/development/querying-the-number-of-unread-messages-of-the-user): `querying-the-number-of-unread-messages-of-the-user` -- [添加钉钉客联互通群成员](https://open.dingtalk.com/document/development/add-a-group-member-1): `add-a-group-member-1` -- [移除钉钉客联互通群成员](https://open.dingtalk.com/document/development/remove-group-members): `remove-group-members` -- [获取群会话的OpenConversationId](https://open.dingtalk.com/document/development/obtain-group-openconversationid): `obtain-group-openconversationid` -- [获取钉钉客联H5页面地址](https://open.dingtalk.com/document/development/get-the-dingtalk-guest-group-session-address): `get-the-dingtalk-guest-group-session-address` -- [解散群聊](https://open.dingtalk.com/document/development/api-dsbandopenscenegroup): `api-dsbandopenscenegroup` -- [解散钉钉客联互通群](https://open.dingtalk.com/document/development/disband-bc-interconnection-group): `disband-bc-interconnection-group` -- [设置群成员禁言状态](https://open.dingtalk.com/document/development/set-group-members-access-control): `set-group-members-access-control` - -### 智能 CRM(34 条) - -- [创建个人或企业客户数据](https://open.dingtalk.com/document/development/add-crm-personal-customers): `add-crm-personal-customers` -- [创建客户群](https://open.dingtalk.com/document/development/create-a-customer-group): `create-a-customer-group` -- [创建客户群组](https://open.dingtalk.com/document/development/crm-create-group): `crm-create-group` -- [删除CRM自定义对象数据](https://open.dingtalk.com/document/development/delete-crm-custom-object-data): `delete-crm-custom-object-data` -- [删除个人或企业客户数据](https://open.dingtalk.com/document/development/delete-crm-personal-customer): `delete-crm-personal-customer` -- [发送服务窗单人消息](https://open.dingtalk.com/document/development/sends-a-single-message-from-the-service-window): `sends-a-single-message-from-the-service-window` -- [批量修改联系人数据](https://open.dingtalk.com/document/development/modify-contact-data-in-batches): `modify-contact-data-in-batches` -- [批量删除跟进记录数据](https://open.dingtalk.com/document/development/batch-delete-follow-up-record-data): `batch-delete-follow-up-record-data` -- [批量发送服务窗消息](https://open.dingtalk.com/document/development/batch-sending-of-service-window-messages): `batch-sending-of-service-window-messages` -- [批量新增个人或企业客户数据](https://open.dingtalk.com/document/development/add-multiple-relationship-data-in-batches): `add-multiple-relationship-data-in-batches` -- [批量新增联系人数据](https://open.dingtalk.com/document/development/add-contact-data-in-batches): `add-contact-data-in-batches` -- [批量新增跟进记录数据](https://open.dingtalk.com/document/development/batch-add-follow-up-record-data): `batch-add-follow-up-record-data` -- [批量更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-multiple-relational-data-tables-at-a-time): `update-multiple-relational-data-tables-at-a-time` -- [批量更新跟进记录数据](https://open.dingtalk.com/document/development/batch-update-follow-up-record-data): `batch-update-follow-up-record-data` -- [批量查询客户群](https://open.dingtalk.com/document/development/query-customer-groups-in-batches): `query-customer-groups-in-batches` -- [批量获取个人或企业客户数据](https://open.dingtalk.com/document/development/acquire-crm-individual-customers-in-batches): `acquire-crm-individual-customers-in-batches` -- [更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-crm-personal-customers): `update-crm-personal-customers` -- [更新客户群组](https://open.dingtalk.com/document/development/crm-update-group): `crm-update-group` -- [查询客户数据](https://open.dingtalk.com/document/development/querying-customer-data): `querying-customer-data` -- [查询客户群列表](https://open.dingtalk.com/document/development/query-the-list-of-customer-groups): `query-the-list-of-customer-groups` -- [查询客户群组列表](https://open.dingtalk.com/document/development/query-groups): `query-groups` -- [查询服务窗粉丝用户基础信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-fans-in-the-service-window): `queries-the-basic-information-of-fans-in-the-service-window` -- [根据指定条件查询个人或企业客户数据](https://open.dingtalk.com/document/development/obtains-crm-individual-customers-in-batches-based-on-specified-query): `obtains-crm-individual-customers-in-batches-based-on-specified-query` -- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/api-getcontacts): `api-getcontacts` -- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/api-getobjectdata): `api-getobjectdata` -- [第三方个人应用发送服务窗单人消息](https://open.dingtalk.com/document/development/a-third-party-personal-application-sends-a-message-to-a-single): `a-third-party-personal-application-sends-a-message-to-a-single` -- [获取个人或企业客户查重字段](https://open.dingtalk.com/document/development/obtain-duplicate-check-fields): `obtain-duplicate-check-fields` -- [获取个人或企业客户的元数据](https://open.dingtalk.com/document/development/obtain-the-metadata-of-individual-enterprise-customers): `obtain-the-metadata-of-individual-enterprise-customers` -- [获取全量个人或企业客户数据](https://open.dingtalk.com/document/development/crm-obtains-all-private-sea-customer-data): `crm-obtains-all-private-sea-customer-data` -- [获取单个客户群组详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-single-customer-group): `queries-the-details-of-a-single-customer-group` -- [获取单个客户群详情](https://open.dingtalk.com/document/development/obtain-a-single-customer-group): `obtain-a-single-customer-group` -- [获取审批中创建与CRM客户关联的TAB表单元数据](https://open.dingtalk.com/document/development/api-getrelatedviewtabmeta): `api-getrelatedviewtabmeta` -- [获取审批里创建的与CRM客户关联的TAB表单数据实例列表](https://open.dingtalk.com/document/development/api-getrelatedviewtabdata): `api-getrelatedviewtabdata` -- [获取客户管理全局信息](https://open.dingtalk.com/document/development/get-customer-management-global-information): `get-customer-management-global-information` - -### 日历(30 条) - -- [修改日程](https://open.dingtalk.com/document/development/modify-event): `modify-event` -- [创建日程](https://open.dingtalk.com/document/development/create-event): `create-event` -- [创建订阅日历](https://open.dingtalk.com/document/development/create-subscription-calendar): `create-subscription-calendar` -- [创建访问控制](https://open.dingtalk.com/document/development/create-schedule-access-control): `create-schedule-access-control` -- [删除日程](https://open.dingtalk.com/document/development/delete-event): `delete-event` -- [删除日程参与者](https://open.dingtalk.com/document/development/delete-schedule-participant): `delete-schedule-participant` -- [删除订阅日历](https://open.dingtalk.com/document/development/delete-subscription-calendar): `delete-subscription-calendar` -- [删除访问控制](https://open.dingtalk.com/document/development/delete-an-access-control-list): `delete-an-access-control-list` -- [取消订阅公共日历](https://open.dingtalk.com/document/development/unsubscribe-from-a-public-calendar): `unsubscribe-from-a-public-calendar` -- [取消预定会议室](https://open.dingtalk.com/document/development/remove-a-meeting-room): `remove-a-meeting-room` -- [更新订阅日历](https://open.dingtalk.com/document/development/update-subscription-calendar): `update-subscription-calendar` -- [查看单个日程的签到详情](https://open.dingtalk.com/document/development/view-the-check-in-details-of-a-single-schedule): `view-the-check-in-details-of-a-single-schedule` -- [查看单个日程的签退详情](https://open.dingtalk.com/document/development/view-the-billing-details-of-a-single-schedule): `view-the-billing-details-of-a-single-schedule` -- [查询单个日程详情](https://open.dingtalk.com/document/development/query-details-about-an-event): `query-details-about-an-event` -- [查询单个订阅日历详情](https://open.dingtalk.com/document/development/query-a-single-subscription-calendar): `query-a-single-subscription-calendar` -- [查询日历](https://open.dingtalk.com/document/development/query-a-calendar): `query-a-calendar` -- [查询日程列表](https://open.dingtalk.com/document/development/query-an-event-list): `query-an-event-list` -- [查询日程视图](https://open.dingtalk.com/document/development/query-schedule-view): `query-schedule-view` -- [添加日程参与者](https://open.dingtalk.com/document/development/add-schedule-participant): `add-schedule-participant` -- [获取会议室忙闲信息](https://open.dingtalk.com/document/development/queries-free-and-busy-meeting-room-information): `queries-free-and-busy-meeting-room-information` -- [获取日程参与者](https://open.dingtalk.com/document/development/get-the-participants-of-a-schedule): `get-the-participants-of-a-schedule` -- [获取用户忙闲信息](https://open.dingtalk.com/document/development/free-schedule): `free-schedule` -- [获取签到链接](https://open.dingtalk.com/document/development/api-getsigninlink): `api-getsigninlink` -- [获取签退链接](https://open.dingtalk.com/document/development/api-getsignoutlink): `api-getsignoutlink` -- [获取访问控制列表](https://open.dingtalk.com/document/development/obtain-the-access-control-list-of-the-calendar): `obtain-the-access-control-list-of-the-calendar` -- [订阅公共日历](https://open.dingtalk.com/document/development/subscribe-to-a-public-calendar): `subscribe-to-a-public-calendar` -- [设置日程响应邀请状态](https://open.dingtalk.com/document/development/configure-response-status): `configure-response-status` -- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-single-schedule-news): `sign-in-single-schedule-news` -- [针对单个日程进行签退](https://open.dingtalk.com/document/development/sign-off-for-a-single-schedule): `sign-off-for-a-single-schedule` -- [预定会议室](https://open.dingtalk.com/document/development/add-a-meeting-room): `add-a-meeting-room` - -### 视频会议(30 条) - -- [停止视频会议云录制](https://open.dingtalk.com/document/development/video-conferencing-stops-cloud-recording): `video-conferencing-stops-cloud-recording` -- [停止视频会议直播推流](https://open.dingtalk.com/document/development/videoconferencing-stops-live-stream-ingest): `videoconferencing-stops-live-stream-ingest` -- [全员静音或全员取消静音](https://open.dingtalk.com/document/development/mute-all-staff-or-unmute-all-staff): `mute-all-staff-or-unmute-all-staff` -- [关闭视频会议](https://open.dingtalk.com/document/development/close-audio-video-conference): `close-audio-video-conference` -- [创建用户专属短链](https://open.dingtalk.com/document/development/api-createcustomshortlink): `api-createcustomshortlink` -- [创建视频会议](https://open.dingtalk.com/document/development/create-a-video-conference): `create-a-video-conference` -- [创建预约会议](https://open.dingtalk.com/document/development/create-appointment-meeting): `create-appointment-meeting` -- [取消预约会议](https://open.dingtalk.com/document/development/cancel-appointment-meeting): `cancel-appointment-meeting` -- [开启视频会议云录制](https://open.dingtalk.com/document/development/video-conference-open-cloud-recording): `video-conference-open-cloud-recording` -- [开启视频会议直播推流](https://open.dingtalk.com/document/development/video-conference-enables-live-stream-ingest): `video-conference-enables-live-stream-ingest` -- [批量查询视频会议信息](https://open.dingtalk.com/document/development/batch-query-of-video-conference-information): `batch-query-of-video-conference-information` -- [指定人员静音或取消静音](https://open.dingtalk.com/document/development/specify-person-to-mute-or-unmute): `specify-person-to-mute-or-unmute` -- [更新预约会议](https://open.dingtalk.com/document/development/update-appointment-meeting): `update-appointment-meeting` -- [更新预约会议设置](https://open.dingtalk.com/document/development/api-updatescheduleconfsettings): `api-updatescheduleconfsettings` -- [查询企业进行中会议列表](https://open.dingtalk.com/document/development/api-queryorgconferencelist): `api-queryorgconferencelist` -- [查询会议录制中的文本信息](https://open.dingtalk.com/document/development/queries-the-text-information-about-cloud-recording): `queries-the-text-information-about-cloud-recording` -- [查询会议录制中的视频信息](https://open.dingtalk.com/document/development/queries-the-playback-information-about-a-recorded-cloud-video): `queries-the-playback-information-about-a-recorded-cloud-video` -- [查询会议录制的详情信息](https://open.dingtalk.com/document/development/query-recording-information): `query-recording-information` -- [查询用户进行中会议列表](https://open.dingtalk.com/document/development/api-queryuserongoingconference): `api-queryuserongoingconference` -- [查询视频会议信息](https://open.dingtalk.com/document/development/querying-video-conference-information): `querying-video-conference-information` -- [查询视频会议成员](https://open.dingtalk.com/document/development/querying-video-conference-members): `querying-video-conference-members` -- [查询预约会议](https://open.dingtalk.com/document/development/query-meeting-reservation): `query-meeting-reservation` -- [查询预约会议历史会议信息](https://open.dingtalk.com/document/development/query-appointment-meeting-history-meeting-information): `query-appointment-meeting-history-meeting-information` -- [查询预约会议设置](https://open.dingtalk.com/document/development/api-queryscheduleconfsettings): `api-queryscheduleconfsettings` -- [根据会议号查询会议信息](https://open.dingtalk.com/document/development/api-queryconferenceinfobyroomcode): `api-queryconferenceinfobyroomcode` -- [设置全员看他](https://open.dingtalk.com/document/development/set-the-whole-staff-to-see-him): `set-the-whole-staff-to-see-him` -- [设置联席主持人](https://open.dingtalk.com/document/development/set-up-co-hosts): `set-up-co-hosts` -- [踢出会议成员](https://open.dingtalk.com/document/development/kick-out-meeting-members): `kick-out-meeting-members` -- [邀请用户入会](https://open.dingtalk.com/document/development/invite-users-to-join): `invite-users-to-join` -- [锁定会议](https://open.dingtalk.com/document/development/api-lockconference): `api-lockconference` - -### 阿里商旅(25 条) - -- [修改成本中心](https://open.dingtalk.com/document/development/modify-basic-cost-center-information): `modify-basic-cost-center-information` -- [关联单号查询相关订单信息列表](https://open.dingtalk.com/document/development/related-order-information): `related-order-information` -- [创建日志](https://open.dingtalk.com/document/development/create-a-log): `create-a-log` -- [同步市内用车申请单](https://open.dingtalk.com/document/development/synchronize-third-party-city-vehicle-approval-form): `synchronize-third-party-city-vehicle-approval-form` -- [回传第三方超标审批结果](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-sync): `dingtalk-oapi-alitrip-btrip-exceedapply-sync` -- [审批市内用车申请单](https://open.dingtalk.com/document/development/approval-of-third-party-city-car-application-form): `approval-of-third-party-city-car-application-form` -- [搜索第三方机票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-flight): `dingtalk-oapi-alitrip-btrip-exceedapply-flight` -- [搜索第三方火车票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-train-get): `dingtalk-oapi-alitrip-btrip-exceedapply-train-get` -- [搜索第三方酒店超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get): `dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get` -- [新建审批单](https://open.dingtalk.com/document/development/user-new-approval-form): `user-new-approval-form` -- [新建成本中心](https://open.dingtalk.com/document/development/new-cost-center): `new-cost-center` -- [查询商旅火车票结算记账数据](https://open.dingtalk.com/document/development/business-travel-train-ticket-settlement-bookkeeping-query-interface): `business-travel-train-ticket-settlement-bookkeeping-query-interface` -- [查询市内用车申请单](https://open.dingtalk.com/document/development/query-the-application-form-for-third-party-vehicles-in-the-city): `query-the-application-form-for-third-party-vehicles-in-the-city` -- [查询成员排班信息](https://open.dingtalk.com/document/development/query-scheduling-for-a-day): `query-scheduling-for-a-day` -- [查询机票结算记账数据](https://open.dingtalk.com/document/development/ticket-settlement-bookkeeping-query-interface): `ticket-settlement-bookkeeping-query-interface` -- [查询用车结算记账记录](https://open.dingtalk.com/document/development/query-interface-for-vehicle-settlement-and-bookkeeping): `query-interface-for-vehicle-settlement-and-bookkeeping` -- [查询酒店结算记账数据](https://open.dingtalk.com/document/development/hotel-settlement-bookkeeping-query-interface): `hotel-settlement-bookkeeping-query-interface` -- [获取企业机票订单数据](https://open.dingtalk.com/document/development/obtains-enterprise-ticket-order-data): `obtains-enterprise-ticket-order-data` -- [获取商旅访问地址](https://open.dingtalk.com/document/development/obtain-business-travel-access-addresses): `obtain-business-travel-access-addresses` -- [获取打卡结果](https://open.dingtalk.com/document/development/open-attendance-clock-in-data): `open-attendance-clock-in-data` -- [获取打卡详情](https://open.dingtalk.com/document/development/attendance-clock-in-record-is-open): `attendance-clock-in-record-is-open` -- [获取日志评论详情](https://open.dingtalk.com/document/development/queries-log-comment-details): `queries-log-comment-details` -- [获取月对账结算数据](https://open.dingtalk.com/document/development/obtain-monthly-reconciliation-settlement-data): `obtain-monthly-reconciliation-settlement-data` -- [获取用车订单数据](https://open.dingtalk.com/document/development/vehicle-order-query-interface): `vehicle-order-query-interface` -- [设置成本中心人员信息](https://open.dingtalk.com/document/development/set-up-cost-center-personnel-information): `set-up-cost-center-personnel-information` - -### Agoal 目标(23 条) - -- [Agoal业务数据查询](https://open.dingtalk.com/document/development/agoal-business-biz-data-query): `agoal-business-biz-data-query` -- [创建业务实体](https://open.dingtalk.com/document/development/api-agoalentitycreate): `api-agoalentitycreate` -- [创建目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskcreate): `api-agoalperftaskcreate` -- [更新业务实体](https://open.dingtalk.com/document/development/api-agoalentityupdate): `api-agoalentityupdate` -- [更新目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskupdate): `api-agoalperftaskupdate` -- [查询企业下个人目标详情](https://open.dingtalk.com/document/development/api-getobjectivedetail): `api-getobjectivedetail` -- [查询企业下单个目标规则详情](https://open.dingtalk.com/document/development/api-getobjectiveruledetail): `api-getobjectiveruledetail` -- [查询企业下指定个人目标的所有进展](https://open.dingtalk.com/document/development/api-agoalobjectiveprogresslist): `api-agoalobjectiveprogresslist` -- [查询企业下的所有考核计划](https://open.dingtalk.com/document/development/api-agoalorgperfplanquery): `api-agoalorgperfplanquery` -- [查询企业下目标规则列表](https://open.dingtalk.com/document/development/api-agoalobjectiverulelist): `api-agoalobjectiverulelist` -- [查询某个考核计划的部门得分](https://open.dingtalk.com/document/development/api-agoalorgperfdocquery): `api-agoalorgperfdocquery` -- [查询组织目标详情](https://open.dingtalk.com/document/development/api-agoalorgobjectivequery): `api-agoalorgobjectivequery` -- [获取 Agoal 组织目标列表](https://open.dingtalk.com/document/development/api-agoalorgobjectivelist): `api-agoalorgobjectivelist` -- [获取Agoal指定目标规则下的周期列表](https://open.dingtalk.com/document/development/api-agoalobjectiveruleperiodlist): `api-agoalobjectiveruleperiodlist` -- [获取Agoal指定组织下的所有目标规则列表](https://open.dingtalk.com/document/development/api-agoalorgobjectiverulelist): `api-agoalorgobjectiverulelist` -- [获取Agoal指定规则周期下负责人的目标列表](https://open.dingtalk.com/document/development/api-agoaluserobjectivelist): `api-agoaluserobjectivelist` -- [获取Agoal指定部门下的计分卡维度和指标id](https://open.dingtalk.com/document/development/api-getdeptscorecardindicator): `api-getdeptscorecardindicator` -- [获取Agoal用户管理员列表](https://open.dingtalk.com/document/development/api-agoaluseradminlist): `api-agoaluseradminlist` -- [获取Agoal目标或关键结果关联的关键行动](https://open.dingtalk.com/document/development/api-agoalobjectivekeyactionlist): `api-agoalobjectivekeyactionlist` -- [获取计分卡指标详情](https://open.dingtalk.com/document/development/api-getindicatordetail): `api-getindicatordetail` -- [通过Agoal系统账号发送消息](https://open.dingtalk.com/document/development/api-agoalsendmessage): `api-agoalsendmessage` -- [通过指标编码批量查询指标列表](https://open.dingtalk.com/document/development/api-agoalindicatorbatchquery): `api-agoalindicatorbatchquery` -- [通过指标编码推送指标时间维度数据](https://open.dingtalk.com/document/development/api-agoalindicatordatapush): `api-agoalindicatordatapush` - -### 教育(23 条) - -- [修改用户成员类型](https://open.dingtalk.com/document/development/api-updatecollegeuseremptype): `api-updatecollegeuseremptype` -- [创建个人账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactuser): `api-addcollegecontactuser` -- [创建组织单元](https://open.dingtalk.com/document/development/api-createcollegecontactdept): `api-createcollegecontactdept` -- [创建自定义校区或部门](https://open.dingtalk.com/document/development/create-a-custom-campus-or-department): `create-a-custom-campus-or-department` -- [创建自定义部门下的班级](https://open.dingtalk.com/document/development/create-classes-in-a-custom-department): `create-classes-in-a-custom-department` -- [创建高校账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactexclusive): `api-addcollegecontactexclusive` -- [删除学生](https://open.dingtalk.com/document/development/delete-student): `delete-student` -- [删除家校部门](https://open.dingtalk.com/document/development/delete-home-school-department): `delete-home-school-department` -- [删除家长关系](https://open.dingtalk.com/document/development/delete-parent-relationship): `delete-parent-relationship` -- [删除老师](https://open.dingtalk.com/document/development/delete-teacher): `delete-teacher` -- [判断用户是否是认证组织的语文老师接口](https://open.dingtalk.com/document/development/api-isyuwencertifiedteacher): `api-isyuwencertifiedteacher` -- [学生调班](https://open.dingtalk.com/document/development/shift-students): `shift-students` -- [更新个人账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactuser): `api-updatecollegecontactuser` -- [更新学生](https://open.dingtalk.com/document/development/api-updatestudent): `api-updatestudent` -- [更新家长](https://open.dingtalk.com/document/development/api-updateguardian): `api-updateguardian` -- [更新班级](https://open.dingtalk.com/document/development/api-updateclass): `api-updateclass` -- [更新组织单元](https://open.dingtalk.com/document/development/api-updatecollegecontactdept): `api-updatecollegecontactdept` -- [更新高校账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactexclusive): `api-updatecollegecontactexclusive` -- [查询用户信息详情](https://open.dingtalk.com/document/development/api-querycollegecontactuserdetail): `api-querycollegecontactuserdetail` -- [获取子组织单元列表](https://open.dingtalk.com/document/development/api-listcollegecontactsubdepts): `api-listcollegecontactsubdepts` -- [获取组织单元支持的部门类型](https://open.dingtalk.com/document/development/api-listcollegecontactdepttypeconfig): `api-listcollegecontactdepttypeconfig` -- [获取组织单元详情](https://open.dingtalk.com/document/development/api-getcollegecontactdeptdetail): `api-getcollegecontactdeptdetail` -- [获取行政组织架构部门详情](https://open.dingtalk.com/document/development/api-getcollegecontactstandardstrudeptdetail): `api-getcollegecontactstandardstrudeptdetail` - -### 智能人事(17 条) - -- [修改已离职员工信息](https://open.dingtalk.com/document/development/modify-resigned-employee-information): `modify-resigned-employee-information` -- [员工加入待离职](https://open.dingtalk.com/document/development/api-empstartdismission): `api-empstartdismission` -- [批量获取员工离职信息](https://open.dingtalk.com/document/development/obtain-resignation-information-of-employees-new-version): `obtain-resignation-information-of-employees-new-version` -- [撤销员工待离职](https://open.dingtalk.com/document/development/api-revoketermination): `api-revoketermination` -- [新增或删除花名册选项类型字段的选项](https://open.dingtalk.com/document/development/intelligent-personnel-roster-field-option-modification): `intelligent-personnel-roster-field-option-modification` -- [智能人事员工调岗](https://open.dingtalk.com/document/development/intelligent-personnel-staff-transfer): `intelligent-personnel-staff-transfer` -- [智能人事员工转正](https://open.dingtalk.com/document/development/intelligent-personnel-staff-to-become-regular): `intelligent-personnel-staff-to-become-regular` -- [更新待离职员工离职信息](https://open.dingtalk.com/document/development/api-updateempdismissioninfo): `api-updateempdismissioninfo` -- [查询花名册中有权限的字段列表](https://open.dingtalk.com/document/development/query-the-list-of-fields-with-permissions-in-the-roster): `query-the-list-of-fields-with-permissions-in-the-roster` -- [添加待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-supports-system-and-custom-fields): `add-employees-to-be-hired-supports-system-and-custom-fields` -- [确认员工离职并删除](https://open.dingtalk.com/document/development/api-hrmprocessterminationandhandover): `api-hrmprocessterminationandhandover` -- [获取企业已有的所有离职原因](https://open.dingtalk.com/document/development/api-getalldismissionreasons): `api-getalldismissionreasons` -- [获取企业职位列表](https://open.dingtalk.com/document/development/obtain-enterprise-position-information): `obtain-enterprise-position-information` -- [获取企业职务列表](https://open.dingtalk.com/document/development/obtain-enterprise-title-information): `obtain-enterprise-title-information` -- [获取企业职级列表](https://open.dingtalk.com/document/development/obtain-enterprise-rank-information): `obtain-enterprise-rank-information` -- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/api-getemployeerosterbyfield): `api-getemployeerosterbyfield` -- [获取离职员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-who-have-left): `obtain-the-list-of-employees-who-have-left` - -### 机器人(16 条) - -- [下载机器人接收消息的文件内容](https://open.dingtalk.com/document/development/download-the-file-content-of-the-robot-receiving-message): `download-the-file-content-of-the-robot-receiving-message` -- [人与人会话中机器人发送普通消息](https://open.dingtalk.com/document/development/the-robot-sends-ordinary-messages-in-a-person-to-person-conversation): `the-robot-sends-ordinary-messages-in-a-person-to-person-conversation` -- [企业机器人撤回内部群消息](https://open.dingtalk.com/document/development/enterprise-chatbot-withdraws-internal-group-messages): `enterprise-chatbot-withdraws-internal-group-messages` -- [发送DING消息](https://open.dingtalk.com/document/development/robot-sends-nail-message): `robot-sends-nail-message` -- [批量发送人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/chatbots-send-one-on-one-chat-messages-in-batches): `chatbots-send-one-on-one-chat-messages-in-batches` -- [批量撤回人与人会话中机器人消息](https://open.dingtalk.com/document/development/batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations): `batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations` -- [批量撤回人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/batch-message-recall-chat): `batch-message-recall-chat` -- [批量查询人与机器人会话机器人消息是否已读](https://open.dingtalk.com/document/development/chatbot-batch-query-the-read-status-of-messages): `chatbot-batch-query-the-read-status-of-messages` -- [撤回已经发送的DING消息](https://open.dingtalk.com/document/development/robot-withdraws-pin-message): `robot-withdraws-pin-message` -- [机器人发送群聊消息](https://open.dingtalk.com/document/development/the-robot-sends-a-group-message): `the-robot-sends-a-group-message` -- [查询人与人会话中机器人消息已读列表](https://open.dingtalk.com/document/development/query-the-read-list-of-robot-messages-in-person-to-person-conversations): `query-the-read-list-of-robot-messages-in-person-to-person-conversations` -- [查询企业机器人群聊消息用户已读状态](https://open.dingtalk.com/document/development/chatbot-queries-the-read-status-of-a-message): `chatbot-queries-the-read-status-of-a-message` -- [查询单聊机器人的快捷入口](https://open.dingtalk.com/document/development/quick-entrance-of-inquiry-single-chat-robot): `quick-entrance-of-inquiry-single-chat-robot` -- [清空单聊机器人快捷入口](https://open.dingtalk.com/document/development/clear-single-chat-robot-quick-entry): `clear-single-chat-robot-quick-entry` -- [获取群内机器人列表](https://open.dingtalk.com/document/development/obtain-the-list-of-robots-in-the-group): `obtain-the-list-of-robots-in-the-group` -- [设置单聊机器人快捷入口](https://open.dingtalk.com/document/development/set-robot-quick-entrance): `set-robot-quick-entrance` - -### 简知 CRM(15 条) - -- [产品信息](https://open.dingtalk.com/document/development/add-or-edit-product-information): `add-or-edit-product-information` -- [入库单](https://open.dingtalk.com/document/development/add-or-edit-a-shipment-record): `add-or-edit-a-shipment-record` -- [出库单](https://open.dingtalk.com/document/development/add-or-edit-an-issue-ticket): `add-or-edit-an-issue-ticket` -- [发货单](https://open.dingtalk.com/document/development/add-or-edit-invoices): `add-or-edit-invoices` -- [合同订单](https://open.dingtalk.com/document/development/add-or-edit-contract-orders): `add-or-edit-contract-orders` -- [客户公共池](https://open.dingtalk.com/document/development/add-or-edit-customer-public-pools): `add-or-edit-customer-public-pools` -- [客户资料](https://open.dingtalk.com/document/development/add-or-edit-customer-profile): `add-or-edit-customer-profile` -- [报价记录](https://open.dingtalk.com/document/development/add-or-edit-quotation-records): `add-or-edit-quotation-records` -- [生产单](https://open.dingtalk.com/document/development/add-or-edit-a-production-order): `add-or-edit-a-production-order` -- [联系人](https://open.dingtalk.com/document/development/add-or-edit-contacts): `add-or-edit-contacts` -- [获取数据列表](https://open.dingtalk.com/document/development/obtain-the-data-list): `obtain-the-data-list` -- [获取数据详情](https://open.dingtalk.com/document/development/queries-data-details): `queries-data-details` -- [采购单](https://open.dingtalk.com/document/development/edit-purchase-order): `edit-purchase-order` -- [销售换货单](https://open.dingtalk.com/document/development/add-or-edit-a-sales-order): `add-or-edit-a-sales-order` -- [销售机会](https://open.dingtalk.com/document/development/add-or-edit-opportunities): `add-or-edit-opportunities` - -### 考勤(15 条) - -- [分页获取加班规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-overtime-rules-by-page): `retrieve-a-list-of-overtime-rules-by-page` -- [分页获取补卡规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-replenishment-rules-by-page): `retrieve-a-list-of-replenishment-rules-by-page` -- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/batch-query-employee-leave-balance-change-record): `batch-query-employee-leave-balance-change-record` -- [批量获取加班规则设置](https://open.dingtalk.com/document/development/batch-retrieve-overtime-rules): `batch-retrieve-overtime-rules` -- [更新假期规则](https://open.dingtalk.com/document/development/update-holiday-rules): `update-holiday-rules` -- [查询指定用户的封账规则](https://open.dingtalk.com/document/development/encapsulate-account-sealing-and-unsealing-rules): `encapsulate-account-sealing-and-unsealing-rules` -- [查询用户某段时间内是否处于封账状态](https://open.dingtalk.com/document/development/checks-whether-a-user-has-blocked-accounts-within-a-specified): `checks-whether-a-user-has-blocked-accounts-within-a-specified` -- [查询用户考勤节假日信息](https://open.dingtalk.com/document/development/obtain-user-attendance-and-holiday-information): `obtain-user-attendance-and-holiday-information` -- [查询考勤写操作权限](https://open.dingtalk.com/document/development/attendance-writing-operation-is-brand-new-query): `attendance-writing-operation-is-brand-new-query` -- [查询考勤机信息](https://open.dingtalk.com/document/development/query-attendance-machine-information): `query-attendance-machine-information` -- [根据设备ID获取员工信息](https://open.dingtalk.com/document/development/obtain-information-about-employees-based-on-device-ids): `obtain-information-about-employees-based-on-device-ids` -- [添加假期规则](https://open.dingtalk.com/document/development/add-holiday-rules): `add-holiday-rules` -- [通知审批通过](https://open.dingtalk.com/document/development/api-processapprovefinish): `api-processapprovefinish` -- [配置考勤排班附加信息](https://open.dingtalk.com/document/development/synchronization-scheduling-information): `synchronization-scheduling-information` -- [预计算时长](https://open.dingtalk.com/document/development/api-calculateduration): `api-calculateduration` - -### 登录授权(11 条) - -- [查询个人授权记录](https://open.dingtalk.com/document/development/query-personal-authorization-records): `query-personal-authorization-records` -- [获取jsapiTicket](https://open.dingtalk.com/document/development/create-a-jsapi-ticket): `create-a-jsapi-ticket` -- [获取企业内部应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app): `obtain-the-access-token-of-an-internal-app` -- [获取企业开通应用后的授权信息](https://open.dingtalk.com/document/development/obtains-the-authorization-information-after-the-enterprise-activates-the-application-1): `obtains-the-authorization-information-after-the-enterprise-activates-the-application-1` -- [获取定制应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-third-party-application-authorization-enterprise): `obtain-the-access-token-of-the-third-party-application-authorization-enterprise` -- [获取应用的 Access Token](https://open.dingtalk.com/document/development/api-gettoken): `api-gettoken` -- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/obtains-the-identity-of-an-application-administrator): `obtains-the-identity-of-an-application-administrator` -- [获取微应用后台免登的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-micro-application-background-without-log-on): `obtain-the-access-token-of-the-micro-application-background-without-log-on` -- [获取用户token](https://open.dingtalk.com/document/development/obtain-user-token): `obtain-user-token` -- [获取第三方企业应用的suiteAccessToken](https://open.dingtalk.com/document/development/obtains-the-suite-acess-token-of-third-party-enterprise-applications): `obtains-the-suite-acess-token-of-third-party-enterprise-applications` -- [获取第三方应用授权企业的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-authorized-enterprise-1): `obtain-the-access-token-of-the-authorized-enterprise-1` - -### 知识库(11 条) - -- [匹配文本中的词条](https://open.dingtalk.com/document/development/enterprise-encyclopedia-match-entries-in-a-text): `enterprise-encyclopedia-match-entries-in-a-text` -- [批量获取知识库](https://open.dingtalk.com/document/development/batch-acquisition-of-knowledge-base): `batch-acquisition-of-knowledge-base` -- [批量获取节点](https://open.dingtalk.com/document/development/obtain-nodes-in-batch): `obtain-nodes-in-batch` -- [新建知识库](https://open.dingtalk.com/document/development/new-knowledge-base): `new-knowledge-base` -- [查询词条详情](https://open.dingtalk.com/document/development/enterprise-encyclopedia-query-entry-details-by-entry-name): `enterprise-encyclopedia-query-entry-details-by-entry-name` -- [获取我的文档知识库信息](https://open.dingtalk.com/document/development/get-my-documents): `get-my-documents` -- [获取知识库](https://open.dingtalk.com/document/development/obtain-the-knowledge-base): `obtain-the-knowledge-base` -- [获取知识库列表](https://open.dingtalk.com/document/development/get-knowledge-base-list): `get-knowledge-base-list` -- [获取节点](https://open.dingtalk.com/document/development/get-knowledge-base-acquisition-node): `get-knowledge-base-acquisition-node` -- [获取节点列表](https://open.dingtalk.com/document/development/get-node-list): `get-node-list` -- [通过链接获取节点](https://open.dingtalk.com/document/development/get-node-by-link): `get-node-by-link` - -### 服务群(8 条) - -- [创建场景服务群](https://open.dingtalk.com/document/development/create-a-scenario-service-group): `create-a-scenario-service-group` -- [升级云客服服务群为钉钉智能服务群](https://open.dingtalk.com/document/development/upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent): `upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent` -- [升级普通群为服务群](https://open.dingtalk.com/document/development/a-dingtalk-group-is-upgraded-to-one-of-the-intelligent): `a-dingtalk-group-is-upgraded-to-one-of-the-intelligent` -- [发送服务群消息](https://open.dingtalk.com/document/development/service-group-message-sending-interface): `service-group-message-sending-interface` -- [更换服务群所在的群分组](https://open.dingtalk.com/document/development/modify-a-service-group): `modify-a-service-group` -- [查询服务群活跃用户](https://open.dingtalk.com/document/development/queries-active-service-users): `queries-active-service-users` -- [添加服务群成员](https://open.dingtalk.com/document/development/add-service-group-members): `add-service-group-members` -- [群发任务](https://open.dingtalk.com/document/development/service-group-sending-task-interface): `service-group-sending-task-interface` - -### Team 空间(7 条) - -- [创建协作空间](https://open.dingtalk.com/document/development/api-createprojectv3): `api-createprojectv3` -- [创建协作空间任务](https://open.dingtalk.com/document/development/api-createtask): `api-createtask` -- [创建自由任务](https://open.dingtalk.com/document/development/api-createorganizationtask): `api-createorganizationtask` -- [更新协作空间](https://open.dingtalk.com/document/development/api-updateprojectv3): `api-updateprojectv3` -- [查询任务详情](https://open.dingtalk.com/document/development/api-queryalltask): `api-queryalltask` -- [获取协作空间列表](https://open.dingtalk.com/document/development/api-searchprojectsv3-1): `api-searchprojectsv3-1` -- [获取用户参与项目](https://open.dingtalk.com/document/development/api-getuserjoinedprojectsv3): `api-getuserjoinedprojectsv3` - -### 智能招聘(7 条) - -- [根据手机号获取候选人信息](https://open.dingtalk.com/document/development/obtain-candidate-information-based-on-mobile-phone-number): `obtain-candidate-information-based-on-mobile-phone-number` -- [添加智能招聘文件到钉盘](https://open.dingtalk.com/document/development/add-nail-disk-file): `add-nail-disk-file` -- [确认完成权益的更新](https://open.dingtalk.com/document/development/confirm-benefits): `confirm-benefits` -- [获取候选人的面试信息](https://open.dingtalk.com/document/development/query-the-interview-list): `query-the-interview-list` -- [获取招聘流程标识](https://open.dingtalk.com/document/development/get-recruitment-process-identity): `get-recruitment-process-identity` -- [获取智能招聘文件上传信息](https://open.dingtalk.com/document/development/obtain-information-about-the-dingtalk-disk-upload-file): `obtain-information-about-the-dingtalk-disk-upload-file` -- [通知完成指定的新手任务](https://open.dingtalk.com/document/development/notify-the-completion-of-the-specified-novice-task): `notify-the-completion-of-the-specified-novice-task` - -### 财务(7 条) - -- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-user-code-instance): `create-a-user-code-instance` -- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/sync-pin-badge-code-verification-result): `sync-pin-badge-code-verification-result` -- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-user-code-instance): `update-user-code-instance` -- [解码钉工牌电子码](https://open.dingtalk.com/document/development/decoding-dingtalk-payment-code): `decoding-dingtalk-payment-code` -- [通知支付结果](https://open.dingtalk.com/document/development/notify-dingtalk-payment-code-payment-result): `notify-dingtalk-payment-code-payment-result` -- [通知退款结果](https://open.dingtalk.com/document/development/dingtalk-payment-code-refund-information-synchronization-operation): `dingtalk-payment-code-refund-information-synchronization-operation` -- [配置企业钉工牌](https://open.dingtalk.com/document/development/set-up-enterprise-payment-code-configuration-interface): `set-up-enterprise-payment-code-configuration-interface` - -### 待办(6 条) - -- [创建钉钉个人待办任务](https://open.dingtalk.com/document/development/api-createpersonaltodotask): `api-createpersonaltodotask` -- [创建钉钉待办任务](https://open.dingtalk.com/document/development/add-dingtalk-to-do-task): `add-dingtalk-to-do-task` -- [删除钉钉待办任务](https://open.dingtalk.com/document/development/delete-dingtalk-to-do-tasks): `delete-dingtalk-to-do-tasks` -- [更新钉钉待办任务](https://open.dingtalk.com/document/development/updates-dingtalk-to-do-tasks): `updates-dingtalk-to-do-tasks` -- [更新钉钉待办执行者状态](https://open.dingtalk.com/document/development/update-dingtalk-to-do-status): `update-dingtalk-to-do-status` -- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/query-the-to-do-list-of-enterprise-users): `query-the-to-do-list-of-enterprise-users` - -### 百科问答(6 条) - -- [分页获取企业词条信息](https://open.dingtalk.com/document/development/entry-search): `entry-search` -- [删除词条](https://open.dingtalk.com/document/development/delete-entry): `delete-entry` -- [审核词条](https://open.dingtalk.com/document/development/review-entries): `review-entries` -- [新增词条](https://open.dingtalk.com/document/development/new-entry): `new-entry` -- [更新词条](https://open.dingtalk.com/document/development/update-entry): `update-entry` -- [根据词条ID查询详情](https://open.dingtalk.com/document/development/query-entry): `query-entry` - -### 直播(6 条) - -- [修改培训课程](https://open.dingtalk.com/document/development/modify-the-basic-information-of-a-live-streaming-course): `modify-the-basic-information-of-a-live-streaming-course` -- [修改直播属性信息](https://open.dingtalk.com/document/development/modify-live-streaming): `modify-live-streaming` -- [创建直播](https://open.dingtalk.com/document/development/create-live-streaming): `create-live-streaming` -- [删除直播](https://open.dingtalk.com/document/development/delete-live-streaming): `delete-live-streaming` -- [查询直播信息](https://open.dingtalk.com/document/development/queries-the-live-streaming-information): `queries-the-live-streaming-information` -- [查询直播观看人员信息](https://open.dingtalk.com/document/development/queries-the-viewing-information-of-viewers): `queries-the-viewing-information-of-viewers` - -### 行业(6 条) - -- [保存人员扩展属性](https://open.dingtalk.com/document/development/personnel-extension-property-error): `personnel-extension-property-error` -- [创建园区项目](https://open.dingtalk.com/document/development/create-a-campus-project): `create-a-campus-project` -- [创建项目组](https://open.dingtalk.com/document/development/create-a-project-group): `create-a-project-group` -- [删除项目组](https://open.dingtalk.com/document/development/delete-the-project-group-team): `delete-the-project-group-team` -- [查询园区项目信息](https://open.dingtalk.com/document/development/query-a-project-in-a-specified-campus): `query-a-project-in-a-specified-campus` -- [查询项目组信息](https://open.dingtalk.com/document/development/query-a-project-group-in-the-specified-park): `query-a-project-group-in-the-specified-park` - -### 居民服务(5 条) - -- [分页查询居民积分流水](https://open.dingtalk.com/document/development/query-the-integral-flow-records-by-page): `query-the-integral-flow-records-by-page` -- [增加或减少居民积分](https://open.dingtalk.com/document/development/increase-or-decrease-resident-points): `increase-or-decrease-resident-points` -- [查询组织维度配置的所有积分规则](https://open.dingtalk.com/document/development/query-all-credit-rules): `query-all-credit-rules` -- [获取用户所在的行业角色信息](https://open.dingtalk.com/document/development/obtain-the-industry-role-information-of-the-user): `obtain-the-industry-role-information-of-the-user` -- [获取行业角色下的用户列表](https://open.dingtalk.com/document/development/obtains-a-list-of-users-under-an-industry-role): `obtains-a-list-of-users-under-an-industry-role` - -### 组织文化(5 条) - -- [创建荣誉勋章模板](https://open.dingtalk.com/document/development/create-medal-of-honor-template): `create-medal-of-honor-template` -- [撤销员工获得的荣誉勋章](https://open.dingtalk.com/document/development/revoke-an-employee-s-medal-of-honor): `revoke-an-employee-s-medal-of-honor` -- [查询员工已获得的组织荣誉](https://open.dingtalk.com/document/development/check-the-honors-that-an-employee-has-received): `check-the-honors-that-an-employee-has-received` -- [查询当前企业下可颁发的荣誉列表](https://open.dingtalk.com/document/development/query-the-list-of-honors-that-can-be-issued-under): `query-the-list-of-honors-that-can-be-issued-under` -- [给员工颁发荣誉](https://open.dingtalk.com/document/development/award-of-honor): `award-of-honor` - -### 钉密(5 条) - -- [小蜜客服机器人消息回复](https://open.dingtalk.com/document/development/xiaomi-customer-service-robot-message-reply): `xiaomi-customer-service-robot-message-reply` -- [推送小蜜机器人单聊O2O消息](https://open.dingtalk.com/document/development/push-xiaomi-customer-service-robot-single-chat-message): `push-xiaomi-customer-service-robot-single-chat-message` -- [智能问答](https://open.dingtalk.com/document/development/alimebot-intelligent-q-a-interface): `alimebot-intelligent-q-a-interface` -- [查询机器人基础指标数据](https://open.dingtalk.com/document/development/query-robot-data-indicators): `query-robot-data-indicators` -- [获取用户登录凭证](https://open.dingtalk.com/document/development/obtains-the-user-login-credential-of-the-third-party-system-of): `obtains-the-user-login-credential-of-the-third-party-system-of` - -### 云盘(4 条) - -- [删除空间](https://open.dingtalk.com/document/development/delete-a-space): `delete-a-space` -- [新建空间](https://open.dingtalk.com/document/development/new-space): `new-space` -- [根据spaceId获取指定空间信息](https://open.dingtalk.com/document/development/retrieves-the-space-list-on-the-management-side): `retrieves-the-space-list-on-the-management-side` -- [获取空间列表](https://open.dingtalk.com/document/development/queries-a-space-list): `queries-a-space-list` - -### 会话文件(4 条) - -- [以应用身份发送文件给指定用户](https://open.dingtalk.com/document/development/sends-a-storage-file-to-a-specified-user): `sends-a-storage-file-to-a-specified-user` -- [发送文件到指定会话](https://open.dingtalk.com/document/development/send-file-to-specified-session): `send-file-to-specified-session` -- [发送文件链接到指定会话](https://open.dingtalk.com/document/development/send-a-file-link-to-the-specified-session): `send-a-file-link-to-the-specified-session` -- [获取群存储空间信息](https://open.dingtalk.com/document/development/obtain-group-storage-space-information): `obtain-group-storage-space-information` - -### 应用市场(4 条) - -- [内购商品订单处理完成](https://open.dingtalk.com/document/development/internal-purchase-order-processing-completed): `internal-purchase-order-processing-completed` -- [查询应用市场订单详情](https://open.dingtalk.com/document/development/check-the-order-details-app-store): `check-the-order-details-app-store` -- [获取个人应用内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-sku-page-address-of-goods-purchased-in-personal): `obtain-the-sku-page-address-of-goods-purchased-in-personal` -- [获取未处理的已支付订单](https://open.dingtalk.com/document/development/obtaining-isv-unfinished-processing-order): `obtaining-isv-unfinished-processing-order` - -### 公告(3 条) - -- [查询公告已读未读人员列表](https://open.dingtalk.com/document/development/query-bulletin-read-unread-persons-list): `query-bulletin-read-unread-persons-list` -- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-get-blackboard): `obtains-the-details-get-blackboard` -- [获取公告钉盘空间信息](https://open.dingtalk.com/document/development/obtain-bulletin-nail-disk-space-information): `obtain-bulletin-nail-disk-space-information` - -### 制造(2 条) - -- [查询计件报工数据](https://open.dingtalk.com/document/development/riqing-monthly-settlement-query-interface-for-piece-rate-reporting): `riqing-monthly-settlement-query-interface-for-piece-rate-reporting` -- [计件报工](https://open.dingtalk.com/document/development/riqing-monthly-settlement-piece-rate-reporting-interface): `riqing-monthly-settlement-piece-rate-reporting-interface` - -### 碳能力(2 条) - -- [写入每日用户碳数据明细信息](https://open.dingtalk.com/document/development/write-in-the-detailed-information-of-daily-user-carbon-data): `write-in-the-detailed-information-of-daily-user-carbon-data` -- [写入每日组织碳数据明细信息](https://open.dingtalk.com/document/development/third-party-applications-write-daily-organizational-carbon-data-details-1): `third-party-applications-write-daily-organizational-carbon-data-details-1` - -### Agoal(1 条) - -- [查询假期余额](https://open.dingtalk.com/document/development/query-holiday-balance): `query-holiday-balance` - -### 会员(1 条) - -- [查询用户钉钉365会员信息](https://open.dingtalk.com/document/development/api-queryvipmemberinfo): `api-queryvipmemberinfo` - -### 硬件(1 条) - -- [获取用户考勤数据](https://open.dingtalk.com/document/development/obtain-the-attendance-update-data): `obtain-the-attendance-update-data` - -### 职业认证(1 条) - -- [检查用户是否完成所有任务](https://open.dingtalk.com/document/development/docking-of-provincial-practical-exercises-for-digital-managers): `docking-of-provincial-practical-exercises-for-digital-managers` - -## 连接平台(66 条) - -二级 Tab:平台介绍 / 开发指南 / 连接器中心 / 连接平台自动化(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 连接平台自动化(60 条) - -- [下载审批附件](https://open.dingtalk.com/document/development/api-premiumgrantprocessinstancefordownloadfile): `api-premiumgrantprocessinstancefordownloadfile` -- [下载审批附件](https://open.dingtalk.com/document/development/download-an-approval-attachment): `download-an-approval-attachment` -- [保存流程中心外部集成审批任务](https://open.dingtalk.com/document/development/api-premiumsaveintegratedtask): `api-premiumsaveintegratedtask` -- [保存流程中心外部集成审批实例](https://open.dingtalk.com/document/development/api-premiumexternalintegrationprocessinstance): `api-premiumexternalintegrationprocessinstance` -- [保存流程中心外部集成审批模板](https://open.dingtalk.com/document/development/api-premiumsaveintegratedprocess): `api-premiumsaveintegratedprocess` -- [创建实例](https://open.dingtalk.com/document/development/create-a-ticket-approval-instance): `create-a-ticket-approval-instance` -- [创建或更新业务分组](https://open.dingtalk.com/document/development/api-premiuminsertorupdatedir): `api-premiuminsertorupdatedir` -- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-orupdate-the-approval-template-new): `create-orupdate-the-approval-template-new` -- [创建或更新审批表单模板](https://open.dingtalk.com/document/development/create-an-approval-form-template): `create-an-approval-form-template` -- [创建或更新数据表单模板](https://open.dingtalk.com/document/development/api-premiumsaveform): `api-premiumsaveform` -- [创建数据表单实例](https://open.dingtalk.com/document/development/api-createdatapremiumsaveforminstance): `api-createdatapremiumsaveforminstance` -- [创建流程中心待处理任务](https://open.dingtalk.com/document/development/create-pending-tasks-in-process-center): `create-pending-tasks-in-process-center` -- [删除业务分组](https://open.dingtalk.com/document/development/api-premiumdeldir): `api-premiumdeldir` -- [删除数据表单实例](https://open.dingtalk.com/document/development/api-premiumdeleteforminstance): `api-premiumdeleteforminstance` -- [删除模板](https://open.dingtalk.com/document/development/self-owned-approval-deletion-template): `self-owned-approval-deletion-template` -- [加签审批任务](https://open.dingtalk.com/document/development/api-premiumappendtask): `api-premiumappendtask` -- [发起审批实例](https://open.dingtalk.com/document/development/create-an-approval-instance): `create-an-approval-instance` -- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/approve-or-reject-the-approval-task): `approve-or-reject-the-approval-task` -- [归档审批实例](https://open.dingtalk.com/document/development/api-archiveprocessinstance): `api-archiveprocessinstance` -- [批量取消流程中心待处理任务](https://open.dingtalk.com/document/development/cancel-multiple-oa-approval-tasks): `cancel-multiple-oa-approval-tasks` -- [批量同意或拒绝审批任务](https://open.dingtalk.com/document/development/api-premiumbatchexecuteprocessinstances): `api-premiumbatchexecuteprocessinstances` -- [批量更新实例状态](https://open.dingtalk.com/document/development/self-owned-batch-update-of-instance-status): `self-owned-batch-update-of-instance-status` -- [批量获取表单模板schema(包含表单和流程配置信息)](https://open.dingtalk.com/document/development/api-premiumqueryschemaandprocessbycodelist): `api-premiumqueryschemaandprocessbycodelist` -- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/api-premiumaddapprovedentryauth): `api-premiumaddapprovedentryauth` -- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/download-the-approval-nail-file): `download-the-approval-nail-file` -- [授权预览审批附件](https://open.dingtalk.com/document/development/api-premiumgetspacewithdownloadauth): `api-premiumgetspacewithdownloadauth` -- [授权预览审批附件](https://open.dingtalk.com/document/development/official-authorized-preview-approval-attachment): `official-authorized-preview-approval-attachment` -- [撤销审批实例](https://open.dingtalk.com/document/development/revoke-an-approval-instance): `revoke-an-approval-instance` -- [更新实例状态](https://open.dingtalk.com/document/development/update-instance-status): `update-instance-status` -- [更新数据表单实例](https://open.dingtalk.com/document/development/api-premiumupdateforminstance): `api-premiumupdateforminstance` -- [更新流程中心任务状态](https://open.dingtalk.com/document/development/update-process-center-task-status): `update-process-center-task-status` -- [更新流程表单审批实例](https://open.dingtalk.com/document/development/api-premiumupdateprocessinstancevariables): `api-premiumupdateprocessinstancevariables` -- [查询审批中心用户已发起实例列表](https://open.dingtalk.com/document/development/api-premiumgetsubmittedinstances): `api-premiumgetsubmittedinstances` -- [查询审批中心用户已处理任务列表](https://open.dingtalk.com/document/development/api-premiumgetdonetasks): `api-premiumgetdonetasks` -- [查询审批中心用户已收到的实例列表](https://open.dingtalk.com/document/development/api-premiumgetnoticedinstances): `api-premiumgetnoticedinstances` -- [查询审批中心用户待处理任务列表](https://open.dingtalk.com/document/development/api-premiumgettodotasks): `api-premiumgettodotasks` -- [查询已设置为条件的表单组件](https://open.dingtalk.com/document/development/query-form-components-that-have-been-set-as-criteria-1): `query-form-components-that-have-been-set-as-criteria-1` -- [查询通过流程中心集成的OA审批任务](https://open.dingtalk.com/document/development/query-oa-approval-tasks-integrated-through-process-center): `query-oa-approval-tasks-integrated-through-process-center` -- [根据processCode分页获取审批流程数据](https://open.dingtalk.com/document/development/api-premiumgetprocessinstances): `api-premiumgetprocessinstances` -- [添加审批评论](https://open.dingtalk.com/document/development/official-approval-adds-approval-comments): `official-approval-adds-approval-comments` -- [清理OA审批数据](https://open.dingtalk.com/document/development/clear-oa-approval-data): `clear-oa-approval-data` -- [管理员批量转交指定员工的待处理任务](https://open.dingtalk.com/document/development/api-premiumredirecttasksbymanager): `api-premiumredirecttasksbymanager` -- [管理员查询指定员工的待处理任务列表](https://open.dingtalk.com/document/development/api-premiumquerytodotasksbymanager): `api-premiumquerytodotasksbymanager` -- [获取单个审批实例详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-single-approval-instance-pop): `obtains-the-details-of-a-single-approval-instance-pop` -- [获取单个数据表单实例详情](https://open.dingtalk.com/document/development/obtain-details-of-a-single-data-form-instance): `obtain-details-of-a-single-data-form-instance` -- [获取审批单流程中的节点信息](https://open.dingtalk.com/document/development/approval-process-prediction): `approval-process-prediction` -- [获取审批实例ID列表](https://open.dingtalk.com/document/development/obtain-an-approval-list-of-instance-ids): `obtain-an-approval-list-of-instance-ids` -- [获取审批表单控件字段内容修改记录](https://open.dingtalk.com/document/development/api-premiumgetfieldmodifiedhistory): `api-premiumgetfieldmodifiedhistory` -- [获取审批表单控件字段操作权限](https://open.dingtalk.com/document/development/api-premiumgetinstfieldsetting): `api-premiumgetinstfieldsetting` -- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/api-premiumgetattachmentspace): `api-premiumgetattachmentspace` -- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/obtains-the-information-about-approval-nail-disk): `obtains-the-information-about-approval-nail-disk` -- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/get-all-manageable-forms-for-the-current-enterprise): `get-all-manageable-forms-for-the-current-enterprise` -- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/obtains-a-list-of-approval-forms-visible-to-the-specified): `obtains-a-list-of-approval-forms-visible-to-the-specified` -- [获取数据表单schema](https://open.dingtalk.com/document/development/api-premiumgetformschema): `api-premiumgetformschema` -- [获取数据表单实例列表](https://open.dingtalk.com/document/development/api-premiumgetforminstances): `api-premiumgetforminstances` -- [获取模板code](https://open.dingtalk.com/document/development/obtain-the-template-code): `obtain-the-template-code` -- [获取用户待审批数量](https://open.dingtalk.com/document/development/queries-the-number-of-requests-to-be-approved-by-users): `queries-the-number-of-requests-to-be-approved-by-users` -- [获取表单 schema](https://open.dingtalk.com/document/development/obtain-the-form-schema): `obtain-the-form-schema` -- [转交OA审批任务](https://open.dingtalk.com/document/development/transfer-the-oa-approval-task): `transfer-the-oa-approval-task` -- [退回审批任务](https://open.dingtalk.com/document/development/api-premiumreverttask): `api-premiumreverttask` - -### 企业互通(6 条) - -- [批量获取关注服务窗用户信息](https://open.dingtalk.com/document/development/obtains-the-follower-information-from-the-service-window): `obtains-the-follower-information-from-the-service-window` -- [获取企业下服务窗列表](https://open.dingtalk.com/document/development/queries-the-list-of-services-under-an-enterprise): `queries-the-list-of-services-under-an-enterprise` -- [获取关注服务窗用户信息](https://open.dingtalk.com/document/development/queries-the-follower-information-of-the-service-window): `queries-the-follower-information-of-the-service-window` -- [获取用户关注服务窗状态](https://open.dingtalk.com/document/development/third-party-enterprise-application-obtains-user-attention-service-window-status): `third-party-enterprise-application-obtains-user-attention-service-window-status` -- [获取用户服务窗关注状态](https://open.dingtalk.com/document/development/obtain-the-attention-status-of-the-user-service-window): `obtain-the-attention-status-of-the-user-service-window` -- [获取组织服务窗账号列表](https://open.dingtalk.com/document/development/the-third-party-enterprise-application-obtains-the-account-list-of-the): `the-third-party-enterprise-application-obtains-the-account-list-of-the` - -## AI PaaS(48 条) - -二级 Tab:平台介绍 / 炼丹炉大模型平台 / AI 助理创建平台 / AI 客服助理(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 组织大脑(41 条) - -- [人员标签数据查询](https://open.dingtalk.com/document/development/api-stafflabelrecordsquery): `api-stafflabelrecordsquery` -- [人才档案基础数据查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofilebasicquery): `api-hrbraintalentprofilebasicquery` -- [人才档案照片查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofileattachmentquery): `api-hrbraintalentprofileattachmentquery` -- [人才池信息查询](https://open.dingtalk.com/document/development/api-hrbrainemppoolquery): `api-hrbrainemppoolquery` -- [人才池在池人员列表](https://open.dingtalk.com/document/development/api-hrbrainemppooluser): `api-hrbrainemppooluser` -- [数据集成专业技能删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelprofskill): `api-hrbraindeletelabelprofskill` -- [数据集成专业技能同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelprofskill): `api-hrbrainimportlabelprofskill` -- [数据集成人员信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteempinfo): `api-hrbraindeleteempinfo` -- [数据集成人员信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportempinfo): `api-hrbrainimportempinfo` -- [数据集成人员标签删除](https://open.dingtalk.com/document/development/api-hrbraindeletetlabelbase): `api-hrbraindeletetlabelbase` -- [数据集成入职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportregist): `api-hrbrainimportregist` -- [数据集成入职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregist): `api-hrbraindeleteregist` -- [数据集成删除自定义模型数据](https://open.dingtalk.com/document/development/api-hrbraindeletecustom): `api-hrbraindeletecustom` -- [数据集成培训学习数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletetraining): `api-hrbraindeletetraining` -- [数据集成培训学习记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttraining): `api-hrbrainimporttraining` -- [数据集成基础标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelbase): `api-hrbrainimportlabelbase` -- [数据集成处分记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletepundetail): `api-hrbraindeletepundetail` -- [数据集成处分记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpundetail): `api-hrbrainimportpundetail` -- [数据集成奖励信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteawardrecords): `api-hrbraindeleteawardrecords` -- [数据集成奖励记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportawarddetail): `api-hrbrainimportawarddetail` -- [数据集成工作经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteworkexp): `api-hrbraindeleteworkexp` -- [数据集成工作经历同步](https://open.dingtalk.com/document/development/api-hrbrainimportworkexp): `api-hrbrainimportworkexp` -- [数据集成异动记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttransfereval): `api-hrbrainimporttransfereval` -- [数据集成教育经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteeduexp): `api-hrbraindeleteeduexp` -- [数据集成教育经历同步](https://open.dingtalk.com/document/development/api-hrbrainimporteduexp): `api-hrbrainimporteduexp` -- [数据集成晋升记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpromeval): `api-hrbrainimportpromeval` -- [数据集成盘点数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelinventory): `api-hrbraindeletelabelinventory` -- [数据集成盘点数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelinventory): `api-hrbrainimportlabelinventory` -- [数据集成离职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportdimission): `api-hrbrainimportdimission` -- [数据集成离职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletedimission): `api-hrbraindeletedimission` -- [数据集成组织架构同步](https://open.dingtalk.com/document/development/api-hrbrainimportdeptinfo): `api-hrbrainimportdeptinfo` -- [数据集成组织架构数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletedeptinfo): `api-hrbraindeletedeptinfo` -- [数据集成绩效记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteperfeval): `api-hrbraindeleteperfeval` -- [数据集成绩效记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportperfeval): `api-hrbrainimportperfeval` -- [数据集成自定义标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelcustom): `api-hrbrainimportlabelcustom` -- [数据集成调岗记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletetransfereval): `api-hrbraindeletetransfereval` -- [数据集成转正数据删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregular): `api-hrbraindeleteregular` -- [数据集成转正记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportregular): `api-hrbrainimportregular` -- [数据集成领域经验删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelindustry): `api-hrbraindeletelabelindustry` -- [数据集成领域经验同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelindustry): `api-hrbrainimportlabelindustry` -- [自定义模型数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportcustom): `api-hrbrainimportcustom` - -### 智能客服(4 条) - -- [分页查询工单](https://open.dingtalk.com/document/development/intelligent-customer-service-paging-query-work-order): `intelligent-customer-service-paging-query-work-order` -- [创建自助单](https://open.dingtalk.com/document/development/create-a-self-service-ticket): `create-a-self-service-ticket` -- [执行工单活动](https://open.dingtalk.com/document/development/intelligent-customer-service-execute-work-order-activities): `intelligent-customer-service-execute-work-order-activities` -- [查询动作记录](https://open.dingtalk.com/document/development/intelligent-customer-service-query-action-records): `intelligent-customer-service-query-action-records` - -### AI PaaS(3 条) - -- [大模型推理服务(多模态模型)](https://open.dingtalk.com/document/development/large-model-reasoning-service-interface-multimodal-model-1): `large-model-reasoning-service-interface-multimodal-model-1` -- [大模型推理服务(文生文模型)](https://open.dingtalk.com/document/development/api-exclusivemodelcompleteservice): `api-exclusivemodelcompleteservice` -- [炼丹炉专属模型服务](https://open.dingtalk.com/document/development/api-liandanluexclusivemodel): `api-liandanluexclusivemodel` - -## 硬件开发(28 条) - -二级 Tab:智能硬件(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 智能会议室(22 条) - -- [创建会议室](https://open.dingtalk.com/document/development/create-meeting-room): `create-meeting-room` -- [创建会议室分组](https://open.dingtalk.com/document/development/create-meeting-room-groups): `create-meeting-room-groups` -- [创建会议室预定黑名单](https://open.dingtalk.com/document/development/api-createbookingblacklist): `api-createbookingblacklist` -- [创建自定义屏幕模板](https://open.dingtalk.com/document/development/api-createdevicecustomtemplate): `api-createdevicecustomtemplate` -- [删除会议室](https://open.dingtalk.com/document/development/delete-a-meeting-room): `delete-a-meeting-room` -- [删除会议室分组](https://open.dingtalk.com/document/development/delete-a-conference-room-group): `delete-a-conference-room-group` -- [删除会议室预定黑名单](https://open.dingtalk.com/document/development/api-deletebookingblacklist): `api-deletebookingblacklist` -- [删除自定义屏幕模板](https://open.dingtalk.com/document/development/api-deletedevicecustomtemplate): `api-deletedevicecustomtemplate` -- [发送Rooms中控API信令](https://open.dingtalk.com/document/development/api-sendcentralcontrol): `api-sendcentralcontrol` -- [更新会议室信息](https://open.dingtalk.com/document/development/update-meeting-room-information): `update-meeting-room-information` -- [更新会议室分组信息](https://open.dingtalk.com/document/development/update-meeting-room-groups): `update-meeting-room-groups` -- [更新自定义屏幕模板](https://open.dingtalk.com/document/development/api-updatedevicecustomtemplate): `api-updatedevicecustomtemplate` -- [查询会议室分组信息](https://open.dingtalk.com/document/development/query-meeting-room-groups): `query-meeting-room-groups` -- [查询会议室分组列表](https://open.dingtalk.com/document/development/query-meeting-rooms-groups): `query-meeting-rooms-groups` -- [查询会议室列表](https://open.dingtalk.com/document/development/check-the-meeting-room-list): `check-the-meeting-room-list` -- [查询会议室详情](https://open.dingtalk.com/document/development/check-meeting-room-details): `check-meeting-room-details` -- [查询自定义屏幕信息](https://open.dingtalk.com/document/development/api-querydevicecustomtemplate): `api-querydevicecustomtemplate` -- [查询自定义屏幕模板列表](https://open.dingtalk.com/document/development/api-querylistdevicecustomscreentemplate): `api-querylistdevicecustomscreentemplate` -- [查询视频会议设备信息](https://open.dingtalk.com/document/development/querying-video-conference-device-information): `querying-video-conference-device-information` -- [查询视频会议设备属性信息](https://open.dingtalk.com/document/development/querying-video-conference-device-attribute-information): `querying-video-conference-device-attribute-information` -- [获取用户发出的日志列表](https://open.dingtalk.com/document/development/query-logs-sent-by-an-employee): `query-logs-sent-by-an-employee` -- [获取用户可见的日志模板](https://open.dingtalk.com/document/development/obtains-the-list-of-visible-log-templates-based-on-the): `obtains-the-list-of-visible-log-templates-based-on-the` - -### 设备管理(5 条) - -- [批量注册与激活设备](https://open.dingtalk.com/document/development/register-and-activate-devices-in-batches): `register-and-activate-devices-in-batches` -- [查询已经注册的设备信息](https://open.dingtalk.com/document/development/query-information-about-a-registered-device): `query-information-about-a-registered-device` -- [注册设备到钉钉](https://open.dingtalk.com/document/development/pin-registration-interface): `pin-registration-interface` -- [获取巡检或保养记录](https://open.dingtalk.com/document/development/obtain-inspection-and-maintenance-records): `obtain-inspection-and-maintenance-records` -- [获取报修记录](https://open.dingtalk.com/document/development/obtain-the-repair-report-record): `obtain-the-repair-report-record` - -### 智能设备(1 条) - -- [变更智能考勤机员工](https://open.dingtalk.com/document/development/change-intelligent-attendance-machine-staff): `change-intelligent-attendance-machine-staff` - -## 互动卡片(9 条) - -二级 Tab:开发指南 / 使用教程 / 卡片模板搭建器 / 互动卡片搭建平台 / 卡片规范设计(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 互动卡片(9 条) - -- [AI卡片流式更新](https://open.dingtalk.com/document/development/api-streamingupdate): `api-streamingupdate` -- [关闭吊顶卡片](https://open.dingtalk.com/document/development/api-closetopcard): `api-closetopcard` -- [创建卡片](https://open.dingtalk.com/document/development/interface-for-creating-a-card-instance): `interface-for-creating-a-card-instance` -- [创建并投放卡片](https://open.dingtalk.com/document/development/create-and-deliver-cards): `create-and-deliver-cards` -- [卡片平台模板复制](https://open.dingtalk.com/document/development/api-copytemplate): `api-copytemplate` -- [投放卡片](https://open.dingtalk.com/document/development/delivery-card-interface): `delivery-card-interface` -- [新增或者更新卡片的场域信息](https://open.dingtalk.com/document/development/add-field-interface): `add-field-interface` -- [更新卡片](https://open.dingtalk.com/document/development/interactive-card-update-interface): `interactive-card-update-interface` -- [注册卡片回调地址](https://open.dingtalk.com/document/development/register-card-callback-address): `register-card-callback-address` - -## 专属版客户端插件(37 条) - -二级 Tab:功能介绍 / 插件开发(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 专属版(37 条) - -- [DING服务](https://open.dingtalk.com/document/development/send-in-application-ding): `send-in-application-ding` -- [专属小红点推送](https://open.dingtalk.com/document/development/push-a-red-dot-to-the-micro-application): `push-a-red-dot-to-the-micro-application` -- [企业内部群禁言或解除禁言](https://open.dingtalk.com/document/development/exclusive-dingtalk-group-ban): `exclusive-dingtalk-group-ban` -- [企业员工专属安全管控功能命中查询](https://open.dingtalk.com/document/development/api-checkcontrolhitstatus): `api-checkcontrolhitstatus` -- [修改伙伴类型可见性](https://open.dingtalk.com/document/development/modify-partner-type-visibility): `modify-partner-type-visibility` -- [修改角色可见性](https://open.dingtalk.com/document/development/modify-role-visibility): `modify-role-visibility` -- [删除可信设备](https://open.dingtalk.com/document/development/delete-trusted-devices): `delete-trusted-devices` -- [发送文件更改的评论](https://open.dingtalk.com/document/development/send-comments-on-file-changes): `send-comments-on-file-changes` -- [发送电话DING](https://open.dingtalk.com/document/development/outgoing-phone-ding): `outgoing-phone-ding` -- [发送邀请函](https://open.dingtalk.com/document/development/send-invitations): `send-invitations` -- [同步存储数据](https://open.dingtalk.com/document/development/api-datasync): `api-datasync` -- [批量新增可信设备](https://open.dingtalk.com/document/development/create-multiple-trusted-devices): `create-multiple-trusted-devices` -- [新增可信设备信息](https://open.dingtalk.com/document/development/add-information-about-a-trusted-device): `add-information-about-a-trusted-device` -- [更新发送文件的检测状态](https://open.dingtalk.com/document/development/update-the-detection-status-of-a-sent-file): `update-the-detection-status-of-a-sent-file` -- [查询人脸录入状态](https://open.dingtalk.com/document/development/query-face-entry-status): `query-face-entry-status` -- [查询企业内部群信息](https://open.dingtalk.com/document/development/obtain-group-info): `obtain-group-info` -- [查询伙伴角色列表](https://open.dingtalk.com/document/development/query-the-list-of-partners): `query-the-list-of-partners` -- [查询公共设备](https://open.dingtalk.com/document/development/query-public-equipment): `query-public-equipment` -- [查询可信设备详细信息](https://open.dingtalk.com/document/development/query-trusted-device-details): `query-trusted-device-details` -- [查询实人认证状态](https://open.dingtalk.com/document/development/queries-the-id-verification-status): `queries-the-id-verification-status` -- [查询群发消息列表](https://open.dingtalk.com/document/development/service-account-query-msgsend-records): `service-account-query-msgsend-records` -- [查询群发消息详情](https://open.dingtalk.com/document/development/service-account-msg-record-detail): `service-account-msg-record-detail` -- [根据userId查询人员的标签信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-user-tag): `you-can-call-this-operation-to-retrieve-the-user-tag` -- [根据会议逻辑ID查询会议基本信息](https://open.dingtalk.com/document/development/query-basic-meeting-information-using-a-logical-id): `query-basic-meeting-information-using-a-logical-id` -- [消息群发](https://open.dingtalk.com/document/development/api-sendmessage): `api-sendmessage` -- [获取专属存储文件路径](https://open.dingtalk.com/document/development/api-getprivatestorefilepath): `api-getprivatestorefilepath` -- [获取人脸对比接口调用记录](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-call-records): `you-can-call-this-operation-to-query-the-call-records` -- [获取企业专属钉钉权益列表](https://open.dingtalk.com/document/development/api-queryexclusivebenefits): `api-queryexclusivebenefits` -- [获取可打标部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-departments-that-can-be-marked): `obtains-a-list-of-departments-that-can-be-marked` -- [获取子标签列表](https://open.dingtalk.com/document/development/obtain-child-tags-from-a-parent-tag): `obtain-child-tags-from-a-parent-tag` -- [获取实人认证接口调用记录](https://open.dingtalk.com/document/development/obtains-the-call-record-of-the-id-authentication-api): `obtains-the-call-record-of-the-id-authentication-api` -- [获取审计协议签署人员信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-persons-who-sign-the-audit-1): `obtains-the-information-about-the-persons-who-sign-the-audit-1` -- [获取文件操作记录](https://open.dingtalk.com/document/development/obtain-file-operation-records): `obtain-file-operation-records` -- [获取群活跃明细列表](https://open.dingtalk.com/document/development/obtains-the-group-activity-details-list): `obtains-the-group-activity-details-list` -- [获取视频会议详情](https://open.dingtalk.com/document/development/get-video-meeting-details): `get-video-meeting-details` -- [获取防截屏操作记录](https://open.dingtalk.com/document/development/obtain-anti-screen-capture-operation-records): `obtain-anti-screen-capture-operation-records` -- [设置部门伙伴类型和伙伴编码](https://open.dingtalk.com/document/development/set-department-partner-type-and-partner-code): `set-department-partner-type-and-partner-code` - -## 数据资产(102 条) - -二级 Tab:平台介绍 / 宜数(智能问数)(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 宜搭(61 条) - -- [保存表单数据](https://open.dingtalk.com/document/development/api-saveformdata-v2): `api-saveformdata-v2` -- [分页获取集成自动化日志列表](https://open.dingtalk.com/document/development/api-pageautoflowlog): `api-pageautoflowlog` -- [删除流程实例](https://open.dingtalk.com/document/development/delete-the-process-instance): `delete-the-process-instance` -- [删除表单数据](https://open.dingtalk.com/document/development/delete-form-data): `delete-form-data` -- [发起宜搭审批流程](https://open.dingtalk.com/document/development/api-startinstance-v2): `api-startinstance-v2` -- [同意或拒绝宜搭审批任务](https://open.dingtalk.com/document/development/execute-approval-tasks): `execute-approval-tasks` -- [应用授权校验](https://open.dingtalk.com/document/development/application-authorization-verification): `application-authorization-verification` -- [批量创建表单实例](https://open.dingtalk.com/document/development/create-multiple-form-instances): `create-multiple-form-instances` -- [批量删除宜搭角色成员](https://open.dingtalk.com/document/development/batch-deleterolemembers): `batch-deleterolemembers` -- [批量删除指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-deletematrixdatabyrowids): `api-deletematrixdatabyrowids` -- [批量删除表单实例](https://open.dingtalk.com/document/development/delete-multiple-form-instances): `delete-multiple-form-instances` -- [批量执行宜搭审批任务](https://open.dingtalk.com/document/development/batch-execution-should-take-the-lead-of-approval-tasks): `batch-execution-should-take-the-lead-of-approval-tasks` -- [批量更新宜搭角色成员](https://open.dingtalk.com/document/development/batch-rolemembers): `batch-rolemembers` -- [批量更新表单实例内的组件值](https://open.dingtalk.com/document/development/batch-update-of-component-values-in-form-instances): `batch-update-of-component-values-in-form-instances` -- [批量查询宜搭表单实例的评论](https://open.dingtalk.com/document/development/batch-query-of-comments-appropriate-for-form-instances): `batch-query-of-comments-appropriate-for-form-instances` -- [批量获取流程实例列表](https://open.dingtalk.com/document/development/queries-multiple-process-instances): `queries-multiple-process-instances` -- [批量获取表单实例数据](https://open.dingtalk.com/document/development/obtain-multiple-form-instance-data): `obtain-multiple-form-instance-data` -- [提交评论](https://open.dingtalk.com/document/development/submit-comment): `submit-comment` -- [新增或更新表单实例](https://open.dingtalk.com/document/development/api-createorupdateformdata-v2): `api-createorupdateformdata-v2` -- [新购授权订单](https://open.dingtalk.com/document/development/new-order-authorization): `new-order-authorization` -- [更新宜搭子表单数据](https://open.dingtalk.com/document/development/api-updatesubtable): `api-updatesubtable` -- [更新指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-saveandupdatematrixdata): `api-saveandupdatematrixdata` -- [更新表单数据](https://open.dingtalk.com/document/development/api-updateformdata-v2): `api-updateformdata-v2` -- [查询宜搭应用列表](https://open.dingtalk.com/document/development/query-the-application-list): `query-the-application-list` -- [查询宜搭表单服务调用执行记录](https://open.dingtalk.com/document/development/the-query-should-be-based-on-the-execution-records-of): `the-query-should-be-based-on-the-execution-records-of` -- [查询抄送我的任务列表(应用维度)](https://open.dingtalk.com/document/development/query-copied-my-task-list-application-dimension): `query-copied-my-task-list-application-dimension` -- [查询流程运行任务(VPC)](https://open.dingtalk.com/document/development/query-process-running-tasks-vpc): `query-process-running-tasks-vpc` -- [查询用户详情](https://open.dingtalk.com/document/development/query-user-details): `query-user-details` -- [查询表单实例数据](https://open.dingtalk.com/document/development/api-searchformdatas-v2): `api-searchformdatas-v2` -- [查询表单数据](https://open.dingtalk.com/document/development/api-getformdatabyid-v2): `api-getformdatabyid-v2` -- [查询表单的变更记录](https://open.dingtalk.com/document/development/change-records-of-query-forms): `change-records-of-query-forms` -- [校验订单](https://open.dingtalk.com/document/development/verification-order): `verification-order` -- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/api-getinstancebyid-v2): `api-getinstancebyid-v2` -- [终止流程实例](https://open.dingtalk.com/document/development/terminate-a-process-instance): `terminate-a-process-instance` -- [获取任务列表(组织维度)](https://open.dingtalk.com/document/development/query-tasks-from-the-organization-dimension): `query-tasks-from-the-organization-dimension` -- [获取发送给用户的通知](https://open.dingtalk.com/document/development/get-notifications-sent-to-users): `get-notifications-sent-to-users` -- [获取员工组件的值](https://open.dingtalk.com/document/development/gets-the-value-of-the-employee-component): `gets-the-value-of-the-employee-component` -- [获取多个表单实例ID](https://open.dingtalk.com/document/development/api-searchformdataidlist-v2): `api-searchformdataidlist-v2` -- [获取子表组件数据](https://open.dingtalk.com/document/development/obtain-child-table-component-data): `obtain-child-table-component-data` -- [获取宜搭附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-free-access-address-of-yixian-accessories): `obtain-the-temporary-free-access-address-of-yixian-accessories` -- [获取实例ID列表](https://open.dingtalk.com/document/development/api-getinstanceidlist-v2): `api-getinstanceidlist-v2` -- [获取审批记录](https://open.dingtalk.com/document/development/queries-an-approval-record): `queries-an-approval-record` -- [获取指定宜搭角色的角色详情](https://open.dingtalk.com/document/development/get-roledetailbyid): `get-roledetailbyid` -- [获取指定应用下的表单列表](https://open.dingtalk.com/document/development/depending-on-the-application-id-to-get-the-form-list): `depending-on-the-application-id-to-get-the-form-list` -- [获取指定权限矩阵的明细数据](https://open.dingtalk.com/document/development/api-getmatrixdetailbyid): `api-getmatrixdetailbyid` -- [获取流程实例](https://open.dingtalk.com/document/development/api-getinstances-v2): `api-getinstances-v2` -- [获取流程设计结构](https://open.dingtalk.com/document/development/api-getprocessdesign): `api-getprocessdesign` -- [获取组件别名列表](https://open.dingtalk.com/document/development/api-getformcomponentaliaslist): `api-getformcomponentaliaslist` -- [获取组织内已完成的审批任务](https://open.dingtalk.com/document/development/obtains-the-completed-approval-tasks-in-an-organization): `obtains-the-completed-approval-tasks-in-an-organization` -- [获取组织内某人提交的任务](https://open.dingtalk.com/document/development/obtains-the-tasks-submitted-by-someone-in-an-organization): `obtains-the-tasks-submitted-by-someone-in-an-organization` -- [获取表单内的组件信息](https://open.dingtalk.com/document/development/get-form-field-information-based-on-form-uuid): `get-form-field-information-based-on-form-uuid` -- [获取表单组件定义列表](https://open.dingtalk.com/document/development/get-a-list-of-form-component-definitions): `get-a-list-of-form-component-definitions` -- [获取集成自动化日志详情](https://open.dingtalk.com/document/development/api-getautoflowlogdetail): `api-getautoflowlogdetail` -- [转交任务](https://open.dingtalk.com/document/development/transfer-tasks): `transfer-tasks` -- [退还商品](https://open.dingtalk.com/document/development/refund-of-goods): `refund-of-goods` -- [通过RowId更新子表单数据](https://open.dingtalk.com/document/development/update-the-subform-data-via-rowid): `update-the-subform-data-via-rowid` -- [通过流程code获取流程定义](https://open.dingtalk.com/document/development/obtain-definition-through-process-code): `obtain-definition-through-process-code` -- [通过表单实例数据批量更新表单实例](https://open.dingtalk.com/document/development/update-multiple-form-instances-with-the-form-instance-data): `update-multiple-form-instances-with-the-form-instance-data` -- [通过高级查询条件获取表单实例数据(不包括子表单组件数据)](https://open.dingtalk.com/document/development/obtain-form-instance-data-using-advanced-query-conditions-excluding-subform): `obtain-form-instance-data-using-advanced-query-conditions-excluding-subform` -- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/api-searchformdatasecondgeneration-v2): `api-searchformdatasecondgeneration-v2` -- [预览审批流程](https://open.dingtalk.com/document/development/api-previewpublishedprocess): `api-previewpublishedprocess` - -### 氚云(20 条) - -- [修改表单业务对象数据](https://open.dingtalk.com/document/development/modify-form-business-object-data): `modify-form-business-object-data` -- [创建流程实例](https://open.dingtalk.com/document/development/create-a-process-instance): `create-a-process-instance` -- [创建表单业务数据](https://open.dingtalk.com/document/development/create-form-business-data): `create-form-business-data` -- [删除业务对象](https://open.dingtalk.com/document/development/delete-a-business-object): `delete-a-business-object` -- [删除流程实例数据](https://open.dingtalk.com/document/development/delete-process-instance-data): `delete-process-instance-data` -- [取消流程实例](https://open.dingtalk.com/document/development/cancel-a-process-instance): `cancel-a-process-instance` -- [批量新增表单业务数据](https://open.dingtalk.com/document/development/batch-add-form-business-data): `batch-add-form-business-data` -- [查询流程实例](https://open.dingtalk.com/document/development/query-flow-instances): `query-flow-instances` -- [查询流程实例节点工作项](https://open.dingtalk.com/document/development/query-flow-instance-node-work-items): `query-flow-instance-node-work-items` -- [查询表单业务数据列表](https://open.dingtalk.com/document/development/querying-form-business-data): `querying-form-business-data` -- [获取业务实例信息](https://open.dingtalk.com/document/development/queries-business-instance-information): `queries-business-instance-information` -- [获取应用列表](https://open.dingtalk.com/document/development/queries-applications): `queries-applications` -- [获取应用功能节点](https://open.dingtalk.com/document/development/queries-the-application-feature-nodes): `queries-the-application-feature-nodes` -- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-2): `obtain-the-upload-url-of-a-file-2` -- [获取用户数据](https://open.dingtalk.com/document/development/obtain-user-data): `obtain-user-data` -- [获取组织数据](https://open.dingtalk.com/document/development/queries-organization-data): `queries-organization-data` -- [获取表单对象结构](https://open.dingtalk.com/document/development/gets-the-form-object-structure): `gets-the-form-object-structure` -- [获取角色数据](https://open.dingtalk.com/document/development/obtain-role-data): `obtain-role-data` -- [获取角色用户数据](https://open.dingtalk.com/document/development/historical-acquisition-of-role-user-data): `historical-acquisition-of-role-user-data` -- [获取附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-attachment-free-address): `obtain-the-temporary-attachment-free-address` - -### 多维表(14 条) - -- [列出多行记录](https://open.dingtalk.com/document/development/api-notable-listrecords): `api-notable-listrecords` -- [创建字段](https://open.dingtalk.com/document/development/api-noatable-createfield): `api-noatable-createfield` -- [创建数据表](https://open.dingtalk.com/document/development/api-createsheet): `api-createsheet` -- [删除多行记录](https://open.dingtalk.com/document/development/api-noatable-deleterecords): `api-noatable-deleterecords` -- [删除字段](https://open.dingtalk.com/document/development/api-noatable-deletefield): `api-noatable-deletefield` -- [删除数据表](https://open.dingtalk.com/document/development/api-noatable-deletesheet): `api-noatable-deletesheet` -- [新增记录](https://open.dingtalk.com/document/development/api-notable-insertrecords): `api-notable-insertrecords` -- [更新多行记录](https://open.dingtalk.com/document/development/api-noatable-updaterecords): `api-noatable-updaterecords` -- [更新字段](https://open.dingtalk.com/document/development/api-noatable-updatefield): `api-noatable-updatefield` -- [更新数据表](https://open.dingtalk.com/document/development/api-noatable-updatesheet): `api-noatable-updatesheet` -- [获取所有字段](https://open.dingtalk.com/document/development/api-noatable-getallfields): `api-noatable-getallfields` -- [获取所有数据表](https://open.dingtalk.com/document/development/api-notable-getallsheets): `api-notable-getallsheets` -- [获取数据表](https://open.dingtalk.com/document/development/api-notable-getsheet): `api-notable-getsheet` -- [获取记录](https://open.dingtalk.com/document/development/api-getrecord): `api-getrecord` - -### 数据中台 AMDP(4 条) - -- [组织变革主数据人员任职数据推送](https://open.dingtalk.com/document/development/api-amdpjobpositiondatapush): `api-amdpjobpositiondatapush` -- [组织变革主数据人员数据推送](https://open.dingtalk.com/document/development/api-amdpemployeedatapush): `api-amdpemployeedatapush` -- [组织变革主数据人员角色数据推送](https://open.dingtalk.com/document/development/api-amdpemproledatapush): `api-amdpemproledatapush` -- [组织变革主数据部门数据推送](https://open.dingtalk.com/document/development/api-amdporganizationdatapush): `api-amdporganizationdatapush` - -### 表单(3 条) - -- [获取单条填表实例详情](https://open.dingtalk.com/document/development/obtains-the-instance-details-of-a-single-fill-table): `obtains-the-instance-details-of-a-single-fill-table` -- [获取填表实例列表](https://open.dingtalk.com/document/development/obtain-the-table-filling-instance-list-data): `obtain-the-table-filling-instance-list-data` -- [获取用户创建的填表模板列表](https://open.dingtalk.com/document/development/new-obtains-the-template-that-a-user-creates): `new-obtains-the-template-that-a-user-creates` - -## 工作台(24 条) - -二级 Tab:平台介绍 / 使用教程(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 微应用(13 条) - -- [创建企业内部应用](https://open.dingtalk.com/document/development/create-an-h5-application-for-your-enterprise): `create-an-h5-application-for-your-enterprise` -- [删除企业内部应用](https://open.dingtalk.com/document/development/delete-an-internal-h5-application): `delete-an-internal-h5-application` -- [发布企业内部小程序版本](https://open.dingtalk.com/document/development/release-internal-applet-version): `release-internal-applet-version` -- [回滚企业内部小程序版本](https://open.dingtalk.com/document/development/rollback-of-enterprise-internal-applet-version): `rollback-of-enterprise-internal-applet-version` -- [更新企业内部应用](https://open.dingtalk.com/document/development/update-internal-h5-applications): `update-internal-h5-applications` -- [更新企业内部应用的可使用范围](https://open.dingtalk.com/document/development/update-the-visible-range-of-micro-applications): `update-the-visible-range-of-micro-applications` -- [查询管理员是否有应用管理权限](https://open.dingtalk.com/document/development/check-whether-the-administrator-has-application-management-permissions): `check-whether-the-administrator-has-application-management-permissions` -- [获取企业内部小程序历史版本列表](https://open.dingtalk.com/document/development/obtain-the-list-of-historical-versions-of-enterprise-internal-applets): `obtain-the-list-of-historical-versions-of-enterprise-internal-applets` -- [获取企业内部小程序的版本列表](https://open.dingtalk.com/document/development/get-the-version-list-of-the-enterprise-internal-applet): `get-the-version-list-of-the-enterprise-internal-applet` -- [获取企业内部应用的可使用范围](https://open.dingtalk.com/document/development/obtains-the-application-visible-range): `obtains-the-application-visible-range` -- [获取企业内部所有应用列表](https://open.dingtalk.com/document/development/get-a-list-of-all-applications-inside-the-enterprise): `get-a-list-of-all-applications-inside-the-enterprise` -- [获取企业所有应用列表](https://open.dingtalk.com/document/development/obtains-a-list-of-all-enterprise-applications): `obtains-a-list-of-all-enterprise-applications` -- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/obtains-the-list-of-enterprise-applications-visible-to-a-user): `obtains-the-list-of-enterprise-applications-visible-to-a-user` - -### 应用角标(8 条) - -- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-badge-user-instance): `create-a-badge-user-instance` -- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-verification-result): `notification-dingtalk-badge-verification-result` -- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-dingtalk-user-instance): `update-dingtalk-user-instance` -- [解码钉工牌电子码](https://open.dingtalk.com/document/development/stack-dingtalk-badge): `stack-dingtalk-badge` -- [通知支付结果](https://open.dingtalk.com/document/development/sync-dingtalk-badge-code-payment-result): `sync-dingtalk-badge-code-payment-result` -- [通知退款结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-code-refund-result): `notification-dingtalk-badge-code-refund-result` -- [配置企业钉工牌](https://open.dingtalk.com/document/development/save-dingtalk-enterprise-instance): `save-dingtalk-enterprise-instance` -- [钉工牌通知消息](https://open.dingtalk.com/document/development/dingtalk-badge-notification-message): `dingtalk-badge-notification-message` - -### 工作台(3 条) - -- [批量添加最近使用应用](https://open.dingtalk.com/document/development/add-recently-used-apps-in-bulk): `add-recently-used-apps-in-bulk` -- [获取工作台插件权限点](https://open.dingtalk.com/document/development/obtain-the-permissions-of-the-workbench-plug-in): `obtain-the-permissions-of-the-workbench-plug-in` -- [获取工作台插件检验的规则信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-obtain-the-information-about): `you-can-call-this-operation-to-obtain-the-information-about` - -## 其它 / 未归类(557 条) - -### 未归类(557 条) - -- [**获取指定用户的所有父部门列表**](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-specified-user): `queries-all-parent-departments-of-a-specified-user` -- [**获取第三方企业应用的suite_access_token**](https://open.dingtalk.com/document/development/obtain-application-suite-ticket): `obtain-application-suite-ticket` -- [AI 助理发消息(回复消息模式)](https://open.dingtalk.com/document/development/ai-assistant-messages-reply-mode): `ai-assistant-messages-reply-mode` -- [AI 助理更新消息(主动发送模式)](https://open.dingtalk.com/document/development/the-ai-assistant-updates-active-message-sending-mode): `the-ai-assistant-updates-active-message-sending-mode` -- [AI 助理结束发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-finish): `api-finish` -- [AI 助理预备发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-prepare): `api-prepare` -- [AI助理发消息(主动发送模式)](https://open.dingtalk.com/document/development/ai-assistant-active-sends-messages-mode): `ai-assistant-active-sends-messages-mode` -- [AI助理响应接口-个人权限](https://open.dingtalk.com/document/development/api-assistantmeresponse): `api-assistantmeresponse` -- [AI助理响应接口-应用权限](https://open.dingtalk.com/document/development/api-assistantresponse): `api-assistantresponse` -- [ASR 一句话语音识别](https://open.dingtalk.com/document/development/asr-short-sentence-recognition): `asr-short-sentence-recognition` -- [OCR文字识别](https://open.dingtalk.com/document/development/structured-image-recognition-api): `structured-image-recognition-api` -- [groupId转换为groupKey](https://open.dingtalk.com/document/development/groupid-to-groupkey): `groupid-to-groupkey` -- [groupKey转换为groupId](https://open.dingtalk.com/document/development/convert-groupkey-to-groupid): `convert-groupkey-to-groupid` -- [上传媒体文件](https://open.dingtalk.com/document/development/upload-media-files): `upload-media-files` -- [上传打卡记录](https://open.dingtalk.com/document/development/upload-punch-records): `upload-punch-records` -- [上传文件块](https://open.dingtalk.com/document/development/upload-file-blocks): `upload-file-blocks` -- [下载审批附件](https://open.dingtalk.com/document/development/grants-the-permission-to-download-the-approval-file): `grants-the-permission-to-download-the-approval-file` -- [事件推送](https://open.dingtalk.com/document/development/dingtalk-iot-push-events): `dingtalk-iot-push-events` -- [企业活跃用户统计列表(部门维度)](https://open.dingtalk.com/document/development/query-the-statistics-of-active-users-in-a-department-of): `query-the-statistics-of-active-users-in-a-department-of` -- [使商品过期](https://open.dingtalk.com/document/development/make-goods-expire): `make-goods-expire` -- [使用服务助手推送消息](https://open.dingtalk.com/document/development/the-message-pushing-interface-of-the-assistant): `the-message-pushing-interface-of-the-assistant` -- [使用模板发送工作通知消息](https://open.dingtalk.com/document/development/work-notification-templating-send-notification-interface): `work-notification-templating-send-notification-interface` -- [保存文件到自定义或审批钉盘空间](https://open.dingtalk.com/document/development/add-file-to-user-s-dingtalk-disk): `add-file-to-user-s-dingtalk-disk` -- [保存日志内容](https://open.dingtalk.com/document/development/save-custom-log-content): `save-custom-log-content` -- [保存表单数据](https://open.dingtalk.com/document/development/save-form-data): `save-form-data` -- [修改发票配置](https://open.dingtalk.com/document/development/modify-invoice-configuration): `modify-invoice-configuration` -- [修改打卡时段设置](https://open.dingtalk.com/document/development/modify-card-settings): `modify-card-settings` -- [修改文件(夹)名](https://open.dingtalk.com/document/development/modify-the-file-and-folder-name): `modify-the-file-and-folder-name` -- [修改日程](https://open.dingtalk.com/document/development/schedule-2-0-modify-schedule): `schedule-2-0-modify-schedule` -- [修改日程参与者](https://open.dingtalk.com/document/development/schedule-2-0-participant-modification): `schedule-2-0-participant-modification` -- [修改权限](https://open.dingtalk.com/document/development/modify-pin-disk-permission-click): `modify-pin-disk-permission-click` -- [修改申请单](https://open.dingtalk.com/document/development/user-modify-approval-form): `user-modify-approval-form` -- [修改直播课程的可观看白名单](https://open.dingtalk.com/document/development/modify-the-whitelist-for-live-streaming-courses): `modify-the-whitelist-for-live-streaming-courses` -- [修改设备昵称](https://open.dingtalk.com/document/development/intelligent-hardware-device-nickname-modification): `intelligent-hardware-device-nickname-modification` -- [修改课程](https://open.dingtalk.com/document/development/modify-course): `modify-course` -- [修改项目](https://open.dingtalk.com/document/development/project-change): `project-change` -- [停用群模板](https://open.dingtalk.com/document/development/disable-a-group-template): `disable-a-group-template` -- [关闭互动卡片实例置顶](https://open.dingtalk.com/document/development/disable-the-sticky-card-setting): `disable-the-sticky-card-setting` -- [列出插件信息](https://open.dingtalk.com/document/development/query-plug-in-information): `query-plug-in-information` -- [创建AI助理](https://open.dingtalk.com/document/development/assistant-management-create-an-ai-assistant): `assistant-management-create-an-ai-assistant` -- [创建AI助理的运行任务](https://open.dingtalk.com/document/development/api-createassistantrun): `api-createassistantrun` -- [创建CRM自定义对象数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-create-custom-crm-object-data): `dingtalk-paas-master-create-custom-crm-object-data` -- [创建SSO企业账号](https://open.dingtalk.com/document/development/create-an-sso-account): `create-an-sso-account` -- [创建专属帐号用户](https://open.dingtalk.com/document/development/create-dedicated-accounts): `create-dedicated-accounts` -- [创建互动卡片实例](https://open.dingtalk.com/document/development/create-an-interactive-card-instance-2): `create-an-interactive-card-instance-2` -- [创建企业内部应用H5微应用](https://open.dingtalk.com/document/development/create-an-h5-microapplication): `create-an-h5-microapplication` -- [创建企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-customer-data): `dingtalk-paas-master-data-create-crm-customer-data` -- [创建公告](https://open.dingtalk.com/document/development/create-an-enterprise-announcement): `create-an-enterprise-announcement` -- [创建培训课程](https://open.dingtalk.com/document/development/create-live-courses): `create-live-courses` -- [创建学段](https://open.dingtalk.com/document/development/create-a-learning-segment): `create-a-learning-segment` -- [创建学科实例](https://open.dingtalk.com/document/development/create-dingtalk-education-subject-instance): `create-dingtalk-education-subject-instance` -- [创建实例](https://open.dingtalk.com/document/development/initiate-an-approval-process-without-a-process): `initiate-an-approval-process-without-a-process` -- [创建年级](https://open.dingtalk.com/document/development/create-grade): `create-grade` -- [创建待办事项](https://open.dingtalk.com/document/development/create-a-to-do-task): `create-a-to-do-task` -- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-or-update-approval-templates): `create-or-update-approval-templates` -- [创建或更新审批模板](https://open.dingtalk.com/document/development/save-approval-template): `save-approval-template` -- [创建日程](https://open.dingtalk.com/document/development/schedule-2-0-creation-interface): `schedule-2-0-creation-interface` -- [创建消息](https://open.dingtalk.com/document/development/api-createassistantmessage): `api-createassistantmessage` -- [创建班次](https://open.dingtalk.com/document/development/create-modify-shifts): `create-modify-shifts` -- [创建班级](https://open.dingtalk.com/document/development/create-a-class): `create-a-class` -- [创建用户](https://open.dingtalk.com/document/development/create-user): `create-user` -- [创建用户](https://open.dingtalk.com/document/development/user-information-creation): `user-information-creation` -- [创建线程](https://open.dingtalk.com/document/development/api-createassistantthread): `api-createassistantthread` -- [创建群](https://open.dingtalk.com/document/development/create-a-scene-group-v2): `create-a-scene-group-v2` -- [创建群](https://open.dingtalk.com/document/development/session-management-creates-groups): `session-management-creates-groups` -- [创建考勤组](https://open.dingtalk.com/document/development/attendance-group-write): `attendance-group-write` -- [创建联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-contact-data): `dingtalk-paas-master-data-create-crm-contact-data` -- [创建角色](https://open.dingtalk.com/document/development/address-book-add-role): `address-book-add-role` -- [创建角色组](https://open.dingtalk.com/document/development/add-a-role-group): `add-a-role-group` -- [创建课程](https://open.dingtalk.com/document/development/create-course): `create-course` -- [创建部门](https://open.dingtalk.com/document/development/address-book-creation-department-established-department): `address-book-creation-department-established-department` -- [创建部门](https://open.dingtalk.com/document/development/create-a-department): `create-a-department` -- [创建部门](https://open.dingtalk.com/document/development/industry-connection-department-creation): `industry-connection-department-creation` -- [创建钉钉自建企业账号](https://open.dingtalk.com/document/development/create-dingtalk-user-created-dedicated-account): `create-dingtalk-user-created-dedicated-account` -- [初始化假期余额](https://open.dingtalk.com/document/development/initialize-holiday-balance): `initialize-holiday-balance` -- [初始化家校架构](https://open.dingtalk.com/document/development/initialize-the-home-school-architecture): `initialize-the-home-school-architecture` -- [删除AI助理](https://open.dingtalk.com/document/development/assistant-management-deletes-ai-assistants): `assistant-management-deletes-ai-assistants` -- [删除AI助理的消息体](https://open.dingtalk.com/document/development/api-deleteassistantmessage): `api-deleteassistantmessage` -- [删除AI助理线程](https://open.dingtalk.com/document/development/api-deleteassistantthread): `api-deleteassistantthread` -- [删除H5微应用](https://open.dingtalk.com/document/development/delete-an-h5-microapplication): `delete-an-h5-microapplication` -- [删除企业客户数据](https://open.dingtalk.com/document/development/delete-crm-customer): `delete-crm-customer` -- [删除假期规则](https://open.dingtalk.com/document/development/api-for-deleting-holiday-types): `api-for-deleting-holiday-types` -- [删除公告](https://open.dingtalk.com/document/development/delete-announcements-based-on-the-announcement-id): `delete-announcements-based-on-the-announcement-id` -- [删除发票信息](https://open.dingtalk.com/document/development/delete-invoice-information): `delete-invoice-information` -- [删除回收站文件(夹)](https://open.dingtalk.com/document/development/delete-recycle-bin-files-folders): `delete-recycle-bin-files-folders` -- [删除图文卡片](https://open.dingtalk.com/document/development/delete-message-card): `delete-message-card` -- [删除培训课程](https://open.dingtalk.com/document/development/delete-live-training-courses): `delete-live-training-courses` -- [删除外部联系人](https://open.dingtalk.com/document/development/delete-external-contact): `delete-external-contact` -- [删除学科实例](https://open.dingtalk.com/document/development/delete-dingtalk-education-disciplines): `delete-dingtalk-education-disciplines` -- [删除序列](https://open.dingtalk.com/document/development/delete-sequence): `delete-sequence` -- [删除成本中心](https://open.dingtalk.com/document/development/delete-cost-center): `delete-cost-center` -- [删除成本中心人员信息](https://open.dingtalk.com/document/development/delete-the-personnel-information-of-the-cost-center): `delete-the-personnel-information-of-the-cost-center` -- [删除文件(夹)](https://open.dingtalk.com/document/development/delete-objects): `delete-objects` -- [删除文章](https://open.dingtalk.com/document/development/delete-article-1): `delete-article-1` -- [删除权限](https://open.dingtalk.com/document/development/delete-the-pin-disk-permission): `delete-the-pin-disk-permission` -- [删除模板](https://open.dingtalk.com/document/development/delete-a-template): `delete-a-template` -- [删除班次](https://open.dingtalk.com/document/development/delete-shift): `delete-shift` -- [删除用户](https://open.dingtalk.com/document/development/delete-a-member): `delete-a-member` -- [删除用户](https://open.dingtalk.com/document/development/delete-a-user): `delete-a-user` -- [删除群成员](https://open.dingtalk.com/document/development/scene-group-delete): `scene-group-delete` -- [删除考勤组](https://open.dingtalk.com/document/development/delete-attendance-group): `delete-attendance-group` -- [删除联系人数据](https://open.dingtalk.com/document/development/delete-crm-contact): `delete-crm-contact` -- [删除角色](https://open.dingtalk.com/document/development/delete-role-information): `delete-role-information` -- [删除设备](https://open.dingtalk.com/document/development/delete-a-device): `delete-a-device` -- [删除课程](https://open.dingtalk.com/document/development/delete-course): `delete-course` -- [删除部门](https://open.dingtalk.com/document/development/address-book-deletion-department): `address-book-deletion-department` -- [删除部门](https://open.dingtalk.com/document/development/delete-a-department): `delete-a-department` -- [删除项目](https://open.dingtalk.com/document/development/delete-a-project): `delete-a-project` -- [剪辑直播课程回放](https://open.dingtalk.com/document/development/clip-live-course-playback): `clip-live-course-playback` -- [加入课程](https://open.dingtalk.com/document/development/join-course): `join-course` -- [助理删除知识](https://open.dingtalk.com/document/development/api-deleteknowledge): `api-deleteknowledge` -- [助理学习知识](https://open.dingtalk.com/document/development/api-learnknowledge): `api-learnknowledge` -- [助理重新学习](https://open.dingtalk.com/document/development/api-relearnknowledge): `api-relearnknowledge` -- [单步文件上传](https://open.dingtalk.com/document/development/single-step-file-upload): `single-step-file-upload` -- [发布商品](https://open.dingtalk.com/document/development/release-products): `release-products` -- [发布文章](https://open.dingtalk.com/document/development/article-publishing-interface-1): `article-publishing-interface-1` -- [发起宜搭审批流程](https://open.dingtalk.com/document/development/initiate-the-approval-process): `initiate-the-approval-process` -- [发起审批实例](https://open.dingtalk.com/document/development/oa-approval-initiates-approval-instances): `oa-approval-initiates-approval-instances` -- [发送工作通知](https://open.dingtalk.com/document/development/asynchronous-sending-of-enterprise-session-messages): `asynchronous-sending-of-enterprise-session-messages` -- [发送普通消息](https://open.dingtalk.com/document/development/send-normal-messages-1): `send-normal-messages-1` -- [发送消息到企业群](https://open.dingtalk.com/document/development/send-group-messages): `send-group-messages` -- [发送群助手消息](https://open.dingtalk.com/document/development/group-template-robot-message): `group-template-robot-message` -- [发送钉盘文件给指定用户](https://open.dingtalk.com/document/development/sends-a-file-to-a-specified-user): `sends-a-file-to-a-specified-user` -- [取消日程](https://open.dingtalk.com/document/development/schedule-2-0-cancel-schedule): `schedule-2-0-cancel-schedule` -- [变配租户信息](https://open.dingtalk.com/document/development/change-tenant-information-1): `change-tenant-information-1` -- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/execute-approval-operation-with-attachment): `execute-approval-operation-with-attachment` -- [启用群模板](https://open.dingtalk.com/document/development/enable-a-group-template): `enable-a-group-template` -- [商旅成本中心转换为外部成本中心](https://open.dingtalk.com/document/development/business-travel-cost-center-converted-to-external-cost-center): `business-travel-cost-center-converted-to-external-cost-center` -- [回放课程](https://open.dingtalk.com/document/development/replay-course): `replay-course` -- [多渠道新购校验](https://open.dingtalk.com/document/development/multi-channel-new-purchase-verification): `multi-channel-new-purchase-verification` -- [学习推荐数据回流](https://open.dingtalk.com/document/development/learn-to-recommend-data-backflow): `learn-to-recommend-data-backflow` -- [家庭Feed同步](https://open.dingtalk.com/document/development/dingtalk-education-family-feed-synchronization): `dingtalk-education-family-feed-synchronization` -- [将助理技能发布到组织技能库](https://open.dingtalk.com/document/development/api-addtoorgskillrepository): `api-addtoorgskillrepository` -- [应用内购商品核销](https://open.dingtalk.com/document/development/application-of-in-house-purchase-verification): `application-of-in-house-purchase-verification` -- [开启互动卡片实例置顶](https://open.dingtalk.com/document/development/enable-the-interactive-card-setting): `enable-the-interactive-card-setting` -- [开启分块上传事务](https://open.dingtalk.com/document/development/enable-upload-transaction): `enable-upload-transaction` -- [开始课程](https://open.dingtalk.com/document/development/start-course): `start-course` -- [执行宜搭的审批任务](https://open.dingtalk.com/document/development/execute-appropriate-approval-tasks): `execute-appropriate-approval-tasks` -- [执行自定义API](https://open.dingtalk.com/document/development/run-custom-api): `run-custom-api` -- [批量修改设备](https://open.dingtalk.com/document/development/batch-modify-devices): `batch-modify-devices` -- [批量删除参与考勤人员](https://open.dingtalk.com/document/development/batch-delete-employees-under-the-attendance-group): `batch-delete-employees-under-the-attendance-group` -- [批量删除员工角色](https://open.dingtalk.com/document/development/delete-the-color-information-of-employee-corners-in-batches): `delete-the-color-information-of-employee-corners-in-batches` -- [批量删除地点](https://open.dingtalk.com/document/development/delete-position-in-batches-under-the-attendance-group): `delete-position-in-batches-under-the-attendance-group` -- [批量发起回调](https://open.dingtalk.com/document/development/initiate-multiple-callbacks): `initiate-multiple-callbacks` -- [批量取消待办](https://open.dingtalk.com/document/development/cancel-multiple-tasks): `cancel-multiple-tasks` -- [批量增加员工角色](https://open.dingtalk.com/document/development/add-role-information-to-employees-in-batches): `add-role-information-to-employees-in-batches` -- [批量新增Wi-Fi信息](https://open.dingtalk.com/document/development/batch-add-wifi-under-attendance-group): `batch-add-wifi-under-attendance-group` -- [批量新增参与考勤人员](https://open.dingtalk.com/document/development/batch-add-employees-under-the-attendance-group): `batch-add-employees-under-the-attendance-group` -- [批量新增地点](https://open.dingtalk.com/document/development/atch-add-position-under-attendance-group): `atch-add-position-under-attendance-group` -- [批量更新假期余额](https://open.dingtalk.com/document/development/bulk-update-holiday-balance): `bulk-update-holiday-balance` -- [批量更新实例状态](https://open.dingtalk.com/document/development/update-the-status-of-multiple-instances-at-a-time): `update-the-status-of-multiple-instances-at-a-time` -- [批量查询Wi-Fi信息](https://open.dingtalk.com/document/development/batch-query-wifi-under-attendance-group): `batch-query-wifi-under-attendance-group` -- [批量查询人员排班信息](https://open.dingtalk.com/document/development/query-batch-scheduling-information): `query-batch-scheduling-information` -- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/query-holiday-consumption-records): `query-holiday-consumption-records` -- [批量查询地点](https://open.dingtalk.com/document/development/batch-query-position-under-attendance-group): `batch-query-position-under-attendance-group` -- [批量查询成员排班概要信息](https://open.dingtalk.com/document/development/query-scheduling-summary-information): `query-scheduling-summary-information` -- [批量注册事件类型](https://open.dingtalk.com/document/development/registration-event-type): `registration-event-type` -- [批量注册设备](https://open.dingtalk.com/document/development/batchregister-devices): `batchregister-devices` -- [批量注册设备](https://open.dingtalk.com/document/development/industry-connection-device-batch-registration): `industry-connection-device-batch-registration` -- [批量移除Wi-Fi信息](https://open.dingtalk.com/document/development/batch-remove-wifi-under-attendance-group): `batch-remove-wifi-under-attendance-group` -- [批量获取企业客户数据](https://open.dingtalk.com/document/development/obtains-customer-data-in-batches-based-on-the-id-list): `obtains-customer-data-in-batches-based-on-the-id-list` -- [批量获取客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-customer-data-search-and-query-interface): `dingtalk-paas-master-data-customer-data-search-and-query-interface` -- [批量获取应用信息](https://open.dingtalk.com/document/development/queries-the-information-about-multiple-applications): `queries-the-information-about-multiple-applications` -- [批量获取服务窗联系人数据](https://open.dingtalk.com/document/development/obtain-contact-data-from-the-service-window): `obtain-contact-data-from-the-service-window` -- [批量获取考勤组摘要](https://open.dingtalk.com/document/development/batch-query-of-simple-information-of-the-attendance-group): `batch-query-of-simple-information-of-the-attendance-group` -- [批量获取考勤组详情](https://open.dingtalk.com/document/development/batch-obtain-attendance-group-details): `batch-obtain-attendance-group-details` -- [批量获取钉钉运动数据](https://open.dingtalk.com/document/development/queries-the-number-of-dingtalk-movement-steps-of-multiple-users): `queries-the-number-of-dingtalk-movement-steps-of-multiple-users` -- [拷贝文件(夹)](https://open.dingtalk.com/document/development/copy-files-folders): `copy-files-folders` -- [按名称搜索班次](https://open.dingtalk.com/document/development/search-shifts-by-rank): `search-shifts-by-rank` -- [按照ID列表批量获取CRM自定义表单数据](https://open.dingtalk.com/document/development/retrieves-custom-crm-forms-from-the-id-list): `retrieves-custom-crm-forms-from-the-id-list` -- [按照ID列表批量获取联系人数据](https://open.dingtalk.com/document/development/retrieves-contact-data-in-batches-based-on-the-id-list): `retrieves-contact-data-in-batches-based-on-the-id-list` -- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/approve-nail-disk-file-authorization): `approve-nail-disk-file-authorization` -- [授权用户访问企业的自定义空间](https://open.dingtalk.com/document/development/authorize-a-user-to-access-a-custom-workspace-of-an): `authorize-a-user-to-access-a-custom-workspace-of-an` -- [授权预览审批附件](https://open.dingtalk.com/document/development/preview-authorization-attachment): `preview-authorization-attachment` -- [排班制考勤组排班](https://open.dingtalk.com/document/development/scheduling-system-attendance-group-scheduling): `scheduling-system-attendance-group-scheduling` -- [提交文件上传事务](https://open.dingtalk.com/document/development/submit-a-file-upload-transaction): `submit-a-file-upload-transaction` -- [搜索考勤组摘要](https://open.dingtalk.com/document/development/attendance-group-search): `attendance-group-search` -- [撤回工作通知消息](https://open.dingtalk.com/document/development/notification-of-work-withdrawal): `notification-of-work-withdrawal` -- [撤销审批实例](https://open.dingtalk.com/document/development/terminate-a-workflow-by-using-an-instance-id): `terminate-a-workflow-by-using-an-instance-id` -- [新增发票配置](https://open.dingtalk.com/document/development/new-invoice-configuration): `new-invoice-configuration` -- [新增图文卡片](https://open.dingtalk.com/document/development/new-message-card-1): `new-message-card-1` -- [新增或更新表单实例](https://open.dingtalk.com/document/development/add-or-update-form-instances): `add-or-update-form-instances` -- [新增文章](https://open.dingtalk.com/document/development/new-article-1): `new-article-1` -- [新增服务号](https://open.dingtalk.com/document/development/added-service-number): `added-service-number` -- [新增群成员](https://open.dingtalk.com/document/development/add-people-to-scene-group): `add-people-to-scene-group` -- [新增钉钉待办任务](https://open.dingtalk.com/document/development/new-to-do-items): `new-to-do-items` -- [新购宜搭产品](https://open.dingtalk.com/document/development/suitable-for-new-purchase): `suitable-for-new-purchase` -- [更新 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-updateassistantscope): `api-updateassistantscope` -- [更新AI助理基础信息](https://open.dingtalk.com/document/development/api-updateassistantbasicinfo): `api-updateassistantbasicinfo` -- [更新企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-microapplication): `set-the-visible-range-of-the-microapplication` -- [更新企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-customer-data): `dingtalk-paas-master-data-update-crm-customer-data` -- [更新企业账号用户信息](https://open.dingtalk.com/document/development/update-dedicated-accounts-information): `update-dedicated-accounts-information` -- [更新假期规则](https://open.dingtalk.com/document/development/holiday-type-update): `holiday-type-update` -- [更新公告](https://open.dingtalk.com/document/development/modify-the-announcement-according-to-the-announcement-id): `modify-the-announcement-according-to-the-announcement-id` -- [更新参与考勤人员](https://open.dingtalk.com/document/development/attendance-group-member-update): `attendance-group-member-update` -- [更新员工花名册](https://open.dingtalk.com/document/development/update-employee-roster): `update-employee-roster` -- [更新员工花名册信息](https://open.dingtalk.com/document/development/intelligent-personnel-update-employee-file-information): `intelligent-personnel-update-employee-file-information` -- [更新图文卡片](https://open.dingtalk.com/document/development/message-card-material-update-interface): `message-card-material-update-interface` -- [更新外部联系人](https://open.dingtalk.com/document/development/update-enterprise-external-contacts): `update-enterprise-external-contacts` -- [更新学科实例](https://open.dingtalk.com/document/development/update-dingtalk-education-instance): `update-dingtalk-education-instance` -- [更新实例状态](https://open.dingtalk.com/document/development/to-do-instance-status): `to-do-instance-status` -- [更新工作通知状态栏](https://open.dingtalk.com/document/development/update-work-notification-status-bar): `update-work-notification-status-bar` -- [更新待办状态](https://open.dingtalk.com/document/development/update-to-do-task-status): `update-to-do-task-status` -- [更新文章](https://open.dingtalk.com/document/development/save-article-details-1): `save-article-details-1` -- [更新服务号](https://open.dingtalk.com/document/development/service-number-update-1): `service-number-update-1` -- [更新流程实例](https://open.dingtalk.com/document/development/update-process-instance-yida): `update-process-instance-yida` -- [更新状态](https://open.dingtalk.com/document/development/update-status): `update-status` -- [更新用户信息](https://open.dingtalk.com/document/development/update-user-details): `update-user-details` -- [更新用户信息](https://open.dingtalk.com/document/development/user-information-update): `user-information-update` -- [更新申请单状态](https://open.dingtalk.com/document/development/update-approval-form): `update-approval-form` -- [更新群](https://open.dingtalk.com/document/development/modify-a-group-session): `modify-a-group-session` -- [更新群](https://open.dingtalk.com/document/development/scene-group-update): `scene-group-update` -- [更新群成员的群昵称](https://open.dingtalk.com/document/development/set-a-group-nickname): `set-a-group-nickname` -- [更新群管理员](https://open.dingtalk.com/document/development/set-chat-admin): `set-chat-admin` -- [更新考勤组](https://open.dingtalk.com/document/development/attendance-group-update-interface): `attendance-group-update-interface` -- [更新联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-contact-data): `dingtalk-paas-master-data-update-crm-contact-data` -- [更新自定义对象数据](https://open.dingtalk.com/document/development/crm-master-data-opens-interface-for-updating-custom-object-data): `crm-master-data-opens-interface-for-updating-custom-object-data` -- [更新表单数据](https://open.dingtalk.com/document/development/update-form-data): `update-form-data` -- [更新角色名称](https://open.dingtalk.com/document/development/update-the-character-name): `update-the-character-name` -- [更新部门](https://open.dingtalk.com/document/development/address-book-update-department): `address-book-update-department` -- [更新部门](https://open.dingtalk.com/document/development/update-a-department): `update-a-department` -- [更新部门扩展信息](https://open.dingtalk.com/document/development/department-update-extension-information): `department-update-extension-information` -- [更新钉钉待办任务](https://open.dingtalk.com/document/development/update-to-do-status): `update-to-do-status` -- [服务号菜单更新](https://open.dingtalk.com/document/development/service-number-menu-update): `service-number-menu-update` -- [服务商获取第三方应用授权企业的access_token](https://open.dingtalk.com/document/development/obtain-isvapp-token): `obtain-isvapp-token` -- [机票城市搜索](https://open.dingtalk.com/document/development/air-ticket-city-search): `air-ticket-city-search` -- [查询 AI 助理基本信息](https://open.dingtalk.com/document/development/api-retrieveassistantbasicinfo): `api-retrieveassistantbasicinfo` -- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/get-the-user-s-to-do-items): `get-the-user-s-to-do-items` -- [查询企业个人待办数量](https://open.dingtalk.com/document/development/query-the-number-of-to-do-tasks-of-the-enterprise): `query-the-number-of-to-do-tasks-of-the-enterprise` -- [查询企业级别](https://open.dingtalk.com/document/development/query-enterprise-level): `query-enterprise-level` -- [查询企业考勤排班详情](https://open.dingtalk.com/document/development/interface-for-daily-full-query-of-attendance-scheduling-information): `interface-for-daily-full-query-of-attendance-scheduling-information` -- [查询企业账号用户详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-dedicated-account): `queries-the-details-of-a-dedicated-account` -- [查询企业通讯录未激活用户列表](https://open.dingtalk.com/document/development/queries-the-list-of-inactive-accounts-in-the-key-account): `queries-the-list-of-inactive-accounts-in-the-key-account` -- [查询假期规则列表](https://open.dingtalk.com/document/development/holiday-type-query): `holiday-type-query` -- [查询历史班次](https://open.dingtalk.com/document/development/query-history-shifts): `query-history-shifts` -- [查询参与考勤人员列表](https://open.dingtalk.com/document/development/batch-query-of-employees-in-the-attendance-group): `batch-query-of-employees-in-the-attendance-group` -- [查询可用发票列表](https://open.dingtalk.com/document/development/query-available-invoices): `query-available-invoices` -- [查询员工智能考勤机列表](https://open.dingtalk.com/document/development/query-the-list-of-employee-intelligent-attendance-machines): `query-the-list-of-employee-intelligent-attendance-machines` -- [查询商品列表](https://open.dingtalk.com/document/development/query-product-lists): `query-product-lists` -- [查询回收站文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-recycle-bin-folder-list): `obtain-the-recycle-bin-folder-list` -- [查询图文卡片列表](https://open.dingtalk.com/document/development/query-message-card-list): `query-message-card-list` -- [查询家庭孩子信息](https://open.dingtalk.com/document/development/query-family-child-information): `query-family-child-information` -- [查询应用信息列表](https://open.dingtalk.com/document/development/queries-application-information): `queries-application-information` -- [查询待办列表](https://open.dingtalk.com/document/development/query-a-user-s-to-do-items): `query-a-user-s-to-do-items` -- [查询成本中心](https://open.dingtalk.com/document/development/query-cost-center): `query-cost-center` -- [查询排班打卡结果](https://open.dingtalk.com/document/development/query-the-results-of-a-batch-of-tasks): `query-the-results-of-a-batch-of-tasks` -- [查询插件信息列表](https://open.dingtalk.com/document/development/query-plug-ins): `query-plug-ins` -- [查询文件(夹)信息](https://open.dingtalk.com/document/development/obtain-file-information): `obtain-file-information` -- [查询文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-file-list): `obtain-the-file-list` -- [查询文档模板](https://open.dingtalk.com/document/development/query-a-document-template): `query-a-document-template` -- [查询文章列表](https://open.dingtalk.com/document/development/query-the-article-list): `query-the-article-list` -- [查询是否启用智能统计报表](https://open.dingtalk.com/document/development/determine-whether-to-enable-attendance-intelligent-report): `determine-whether-to-enable-attendance-intelligent-report` -- [查询服务号列表](https://open.dingtalk.com/document/development/query-service-number-list): `query-service-number-list` -- [查询服务号菜单](https://open.dingtalk.com/document/development/query-service-number-menu-1): `query-service-number-menu-1` -- [查询服务号详情](https://open.dingtalk.com/document/development/inquire-about-service-number-details): `inquire-about-service-number-details` -- [查询激活码](https://open.dingtalk.com/document/development/query-activation-code): `query-activation-code` -- [查询用户是否参与企业步数排行榜](https://open.dingtalk.com/document/development/check-whether-dingtalk-is-enabled): `check-whether-dingtalk-is-enabled` -- [查询用户详情](https://open.dingtalk.com/document/development/queries-user-details): `queries-user-details` -- [查询直播的观看数据](https://open.dingtalk.com/document/development/queries-the-playback-data-of-a-live-stream): `queries-the-playback-data-of-a-live-stream` -- [查询直播课程的可观看白名单](https://open.dingtalk.com/document/development/query-the-whitelist-of-live-courses): `query-the-whitelist-of-live-courses` -- [查询群信息](https://open.dingtalk.com/document/development/obtain-a-group-session): `obtain-a-group-session` -- [查询群信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-a-scenario-group): `queries-the-basic-information-of-a-scenario-group` -- [查询群消息已读人员列表](https://open.dingtalk.com/document/development/queries-the-list-of-people-who-have-read-a-group): `queries-the-list-of-people-who-have-read-a-group` -- [查询表单实例数据](https://open.dingtalk.com/document/development/querying-form-instance-data): `querying-form-instance-data` -- [查询表单数据](https://open.dingtalk.com/document/development/query-form-data): `query-form-data` -- [查询设备列表](https://open.dingtalk.com/document/development/intelligent-hardware-list-query): `intelligent-hardware-list-query` -- [查询设备详情](https://open.dingtalk.com/document/development/intelligent-hardware-device-query): `intelligent-hardware-device-query` -- [查询请假状态](https://open.dingtalk.com/document/development/query-status): `query-status` -- [查询销售用户信息](https://open.dingtalk.com/document/development/query-sales-user-information): `query-sales-user-information` -- [查询项目中文件操作日志](https://open.dingtalk.com/document/development/query-file-operation-logs-of-a-project): `query-file-operation-logs-of-a-project` -- [查询预估价](https://open.dingtalk.com/document/development/query-estimated-price): `query-estimated-price` -- [校验变配](https://open.dingtalk.com/document/development/verify-configuration): `verify-configuration` -- [校验用户是否在当前考勤组](https://open.dingtalk.com/document/development/query-members-by-id): `query-members-by-id` -- [校验订单的升级](https://open.dingtalk.com/document/development/upgrade-the-verification-order): `upgrade-the-verification-order` -- [校验订单的升级状态](https://open.dingtalk.com/document/development/upgrade-status-of-the-verification-order): `upgrade-status-of-the-verification-order` -- [根据ID列表批量获取跟进记录数据](https://open.dingtalk.com/document/development/dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you): `dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you` -- [根据groupKey查询考勤组信息](https://open.dingtalk.com/document/development/queries-attendance-group-information-by-id): `queries-attendance-group-information-by-id` -- [根据sns临时授权码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-user-information-based-on-the-sns-temporary-authorization): `obtain-the-user-information-based-on-the-sns-temporary-authorization` -- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/query-a-user-by-the-union-id): `query-a-user-by-the-union-id` -- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-userids-of): `you-can-call-this-operation-to-retrieve-the-userids-of` -- [根据手机号查询企业账号用户](https://open.dingtalk.com/document/development/obtain-the-userid-of-your-mobile-phone-number): `obtain-the-userid-of-your-mobile-phone-number` -- [根据手机号查询用户](https://open.dingtalk.com/document/development/retrieve-userid-from-mobile-phone-number): `retrieve-userid-from-mobile-phone-number` -- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/dingtalk-the-contact-data-query-api): `dingtalk-the-contact-data-query-api` -- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/retrieve-custom-crm-object-data): `retrieve-custom-crm-object-data` -- [根据指定条件查询跟进记录数据](https://open.dingtalk.com/document/development/query-and-dingtalk-data-of-track-records-in-apsara-stack): `query-and-dingtalk-data-of-track-records-in-apsara-stack` -- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/queries-a-process-instance-based-on-its-id): `queries-a-process-instance-based-on-its-id` -- [根据设备ID查询设备](https://open.dingtalk.com/document/development/the-smart-hardware-can-query-details-based-on-the-device): `the-smart-hardware-can-query-details-based-on-the-device` -- [检索AI助理线程](https://open.dingtalk.com/document/development/api-retrieveassistantthread): `api-retrieveassistantthread` -- [注册互动卡片回调地址](https://open.dingtalk.com/document/development/registration-card-interaction-callback-address-1): `registration-card-interaction-callback-address-1` -- [注册单个设备](https://open.dingtalk.com/document/development/industry-connection-single-device-registration): `industry-connection-single-device-registration` -- [注册设备](https://open.dingtalk.com/document/development/register-devices): `register-devices` -- [注册账号](https://open.dingtalk.com/document/development/register-an-account): `register-an-account` -- [消息撤回](https://open.dingtalk.com/document/development/service-number-message-withdrawal): `service-number-message-withdrawal` -- [消息群发](https://open.dingtalk.com/document/development/interactive-service-window-group-message-sending-interface): `interactive-service-window-group-message-sending-interface` -- [添加企业待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-through-intelligent-personnel): `add-employees-to-be-hired-through-intelligent-personnel` -- [添加假期规则](https://open.dingtalk.com/document/development/holiday-type-added): `holiday-type-added` -- [添加外部联系人](https://open.dingtalk.com/document/development/add-enterprise-external-contacts): `add-enterprise-external-contacts` -- [添加学生](https://open.dingtalk.com/document/development/add-student): `add-student` -- [添加审批评论](https://open.dingtalk.com/document/development/add-an-approval-comment): `add-an-approval-comment` -- [添加家长](https://open.dingtalk.com/document/development/add-parent): `add-parent` -- [添加文件(夹)](https://open.dingtalk.com/document/development/add-file-and-folder): `add-file-and-folder` -- [添加权限](https://open.dingtalk.com/document/development/add-pin-disk-permission): `add-pin-disk-permission` -- [添加老师](https://open.dingtalk.com/document/development/add-teacher): `add-teacher` -- [添加自定义空间权限](https://open.dingtalk.com/document/development/add-custom-workspace-permissions): `add-custom-workspace-permissions` -- [添加课程参与方](https://open.dingtalk.com/document/development/add-course-participants): `add-course-participants` -- [添加项目](https://open.dingtalk.com/document/development/add-a-project): `add-a-project` -- [清理审批数据](https://open.dingtalk.com/document/development/clean-up-workflow-data): `clean-up-workflow-data` -- [清空回收站](https://open.dingtalk.com/document/development/empty-recycle-bin-files-folders): `empty-recycle-bin-files-folders` -- [火车票城市搜索](https://open.dingtalk.com/document/development/train-ticket-city-search): `train-ticket-city-search` -- [移动文件(夹)](https://open.dingtalk.com/document/development/move-file-and-folder): `move-file-and-folder` -- [移除助理组织技能库技能](https://open.dingtalk.com/document/development/api-removefromorgskillrepository): `api-removefromorgskillrepository` -- [移除租户资源](https://open.dingtalk.com/document/development/remove-tenant-resources): `remove-tenant-resources` -- [移除课程参与方](https://open.dingtalk.com/document/development/remove-course-participants): `remove-course-participants` -- [终止阿里云授权](https://open.dingtalk.com/document/development/terminate-authorization-for-alibaba-cloud-services): `terminate-authorization-for-alibaba-cloud-services` -- [绑定设备](https://open.dingtalk.com/document/development/establishing-a-binding-relationship-between-intelligent-hardware-and-cloud): `establishing-a-binding-relationship-between-intelligent-hardware-and-cloud` -- [结束课程](https://open.dingtalk.com/document/development/end-course): `end-course` -- [统计企业活跃用户](https://open.dingtalk.com/document/development/query-for-dau-statistics): `query-for-dau-statistics` -- [续费服务订单](https://open.dingtalk.com/document/development/renewal-service-order): `renewal-service-order` -- [续费租户](https://open.dingtalk.com/document/development/renewal-tenant): `renewal-tenant` -- [自定义机器人发送群消息](https://open.dingtalk.com/document/development/custom-robots-send-group-messages): `custom-robots-send-group-messages` -- [获取 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-retrieveassistantscope): `api-retrieveassistantscope` -- [获取AI助理对话明细列表](https://open.dingtalk.com/document/development/api-loglist): `api-loglist` -- [获取AI助理的消息体](https://open.dingtalk.com/document/development/api-retrieveassistantmessage): `api-retrieveassistantmessage` -- [获取AI助理的消息列表](https://open.dingtalk.com/document/development/api-listassistantmessage): `api-listassistantmessage` -- [获取AI助理的运行任务](https://open.dingtalk.com/document/development/api-retrieveassistantrun): `api-retrieveassistantrun` -- [获取AI助理的运行任务的列表](https://open.dingtalk.com/document/development/api-listassistantrun): `api-listassistantrun` -- [获取jsapi_ticket](https://open.dingtalk.com/document/development/obtain-jsapi-ticket): `obtain-jsapi-ticket` -- [获取个人或部门钉钉运动数据](https://open.dingtalk.com/document/development/queries-individual-or-department-dingtalk-exercise-steps): `queries-individual-or-department-dingtalk-exercise-steps` -- [获取主干组织列表](https://open.dingtalk.com/document/development/obtain-backbone-organization-list): `obtain-backbone-organization-list` -- [获取互动服务窗相关数据](https://open.dingtalk.com/document/development/queries-the-data-about-the-interactive-service-window): `queries-the-data-about-the-interactive-service-window` -- [获取人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-home-school-user-identities): `obtains-a-list-of-home-school-user-identities` -- [获取人员详情](https://open.dingtalk.com/document/development/obtain-the-identity-details-of-home-school-personnel): `obtain-the-identity-details-of-home-school-personnel` -- [获取企业DING使用数据](https://open.dingtalk.com/document/development/enterprise-ding-quantity-statistics): `enterprise-ding-quantity-statistics` -- [获取企业DING使用数据(部门维度)](https://open.dingtalk.com/document/development/query-the-departmental-transmission-status-of-key-clients): `query-the-departmental-transmission-status-of-key-clients` -- [获取企业DING发送统计数据](https://open.dingtalk.com/document/development/obtain-sending-statistics-of-an-enterprise-ding): `obtain-sending-statistics-of-an-enterprise-ding` -- [获取企业DING接收及评论统计数据](https://open.dingtalk.com/document/development/obtain-statistics-on-receiving-and-comments-of-enterprise-ding): `obtain-statistics-on-receiving-and-comments-of-enterprise-ding` -- [获取企业下的自定义空间](https://open.dingtalk.com/document/development/obtain-user-space-under-the-enterprise): `obtain-user-space-under-the-enterprise` -- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-information): `obtain-enterprise-information` -- [获取企业全员圈统计数据](https://open.dingtalk.com/document/development/obtains-the-statistical-data-of-all-employees-of-an-enterprise): `obtains-the-statistical-data-of-all-employees-of-an-enterprise` -- [获取企业公告统计数据](https://open.dingtalk.com/document/development/queries-corporate-announcement-statistics): `queries-corporate-announcement-statistics` -- [获取企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/gets-the-microapplication-visible-range-set-by-the-enterprise): `gets-the-microapplication-visible-range-set-by-the-enterprise` -- [获取企业内部应用的access_token](https://open.dingtalk.com/document/development/obtain-orgapp-token): `obtain-orgapp-token` -- [获取企业单聊统计数据](https://open.dingtalk.com/document/development/queries-the-statistics-on-one-time-enterprise-chats): `queries-the-statistics-on-one-time-enterprise-chats` -- [获取企业发布智能填表数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-of-tables-published-in-an-organization): `queries-the-number-of-tables-published-in-an-organization` -- [获取企业发红包统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-red-packets-issued-by-enterprises): `obtains-the-statistics-on-red-packets-issued-by-enterprises` -- [获取企业各类群组创建情况](https://open.dingtalk.com/document/development/api-for-obtaining-the-creation-status-of-various-groups): `api-for-obtaining-the-creation-status-of-various-groups` -- [获取企业员工类型统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-employee-types): `obtains-statistics-on-employee-types` -- [获取企业商旅酒店订单数据](https://open.dingtalk.com/document/development/enterprises-obtain-order-data-for-business-hotels): `enterprises-obtain-order-data-for-business-hotels` -- [获取企业审批统计数据](https://open.dingtalk.com/document/development/obtains-enterprise-approval-statistics): `obtains-enterprise-approval-statistics` -- [获取企业客户的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-customer-object): `get-metadata-description-of-crm-customer-object` -- [获取企业已经加入的或申请加入中的上下游组织的信息](https://open.dingtalk.com/document/development/obtains-information-about-the-workspaces-that-the-enterprise-has-joined): `obtains-information-about-the-workspaces-that-the-enterprise-has-joined` -- [获取企业应用访问情况](https://open.dingtalk.com/document/development/queries-the-daily-usage-summary-of-microapplications-in-an-enterprise): `queries-the-daily-usage-summary-of-microapplications-in-an-enterprise` -- [获取企业待办统计数据](https://open.dingtalk.com/document/development/obtains-the-to-do-statistics-of-an-enterprise): `obtains-the-to-do-statistics-of-an-enterprise` -- [获取企业所有应用列表](https://open.dingtalk.com/document/development/manager-microapplications-api-permission): `manager-microapplications-api-permission` -- [获取企业授权信息](https://open.dingtalk.com/document/development/obtains-the-basic-information-of-an-enterprise): `obtains-the-basic-information-of-an-enterprise` -- [获取企业接收红包统计数据](https://open.dingtalk.com/document/development/queries-the-red-envelope-receiving-statistics-of-an-enterprise): `queries-the-red-envelope-receiving-statistics-of-an-enterprise` -- [获取企业文档统计数据](https://open.dingtalk.com/document/development/get-enterprise-document-statistics): `get-enterprise-document-statistics` -- [获取企业日志统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-log-statistics): `obtain-enterprise-log-statistics` -- [获取企业日程统计数据](https://open.dingtalk.com/document/development/queries-enterprise-schedule-statistics): `queries-enterprise-schedule-statistics` -- [获取企业某天的所有部门电话会议统计列表](https://open.dingtalk.com/document/development/major-customer-department-dimension-teleconference-statistics): `major-customer-department-dimension-teleconference-statistics` -- [获取企业某天的所有部门视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-list-for-key-accounts-and-departments): `video-conferencing-statistics-list-for-key-accounts-and-departments` -- [获取企业某天的电话会议数据](https://open.dingtalk.com/document/development/major-customer-teleconference-statistics-interface): `major-customer-teleconference-statistics-interface` -- [获取企业某天的视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-query-v2-for-key-accounts): `video-conferencing-statistics-query-v2-for-key-accounts` -- [获取企业火车票订单数据](https://open.dingtalk.com/document/development/obtains-the-enterprise-train-ticket-order-data): `obtains-the-enterprise-train-ticket-order-data` -- [获取企业用户在线统计数据](https://open.dingtalk.com/document/development/retrieve-online-statistics-of-enterprise-users): `retrieve-online-statistics-of-enterprise-users` -- [获取企业用户激活状态统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-user-activation-status): `obtains-statistics-on-user-activation-status` -- [获取企业电话会议明细列表](https://open.dingtalk.com/document/development/major-account-conference-call-details-list): `major-account-conference-call-details-list` -- [获取企业电话会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-teleconference-statistics): `get-enterprise-teleconference-statistics` -- [获取企业签到统计数据](https://open.dingtalk.com/document/development/queries-enterprise-check-in-statistics): `queries-enterprise-check-in-statistics` -- [获取企业群直播统计数据](https://open.dingtalk.com/document/development/obtains-the-live-stream-statistics-for-an-enterprise-group): `obtains-the-live-stream-statistics-for-an-enterprise-group` -- [获取企业群聊统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-group-chat-statistics): `obtain-enterprise-group-chat-statistics` -- [获取企业考勤统计数据](https://open.dingtalk.com/document/development/queries-enterprise-attendance-statistics): `queries-enterprise-attendance-statistics` -- [获取企业聊天数据](https://open.dingtalk.com/document/development/chat-data-statistics-query-for-key-accounts): `chat-data-statistics-query-for-key-accounts` -- [获取企业视频会议明细列表](https://open.dingtalk.com/document/development/video-conference-details-for-key-accounts): `video-conference-details-for-key-accounts` -- [获取企业视频会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-video-conference-statistics): `get-enterprise-video-conference-statistics` -- [获取企业视频直播统计列表(部门维度)](https://open.dingtalk.com/document/development/live-broadcast-summary-statistics-of-key-account-departments): `live-broadcast-summary-statistics-of-key-account-departments` -- [获取企业视频直播统计数据](https://open.dingtalk.com/document/development/query-live-streaming-statistics): `query-live-streaming-statistics` -- [获取企业邮箱统计数据](https://open.dingtalk.com/document/development/queries-enterprise-email-statistics): `queries-enterprise-email-statistics` -- [获取企业部门聊天数据(部门维度)](https://open.dingtalk.com/document/development/dingtalk-chat-information-in-key-accounts): `dingtalk-chat-information-in-key-accounts` -- [获取企业钉盘统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-enterprise-dingtalk-trays): `obtains-the-statistics-on-enterprise-dingtalk-trays` -- [获取企业钉钉运动统计数据](https://open.dingtalk.com/document/development/queries-dingtalk-movement-statistics): `queries-dingtalk-movement-statistics` -- [获取入群二维码链接](https://open.dingtalk.com/document/development/obtain-a-qr-code-link): `obtain-a-qr-code-link` -- [获取公告ID列表](https://open.dingtalk.com/document/development/obtains-the-id-list-of-announcements-that-are-not-deleted): `obtains-the-id-list-of-announcements-that-are-not-deleted` -- [获取公告分类列表](https://open.dingtalk.com/document/development/obtains-the-list-of-categories-not-deleted-for-enterprise-announcements): `obtains-the-list-of-categories-not-deleted-for-enterprise-announcements` -- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-bulletin-that-is-not-deleted): `obtains-the-details-of-a-bulletin-that-is-not-deleted` -- [获取内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-product-sku-details-page): `obtain-the-address-of-the-product-sku-details-page` -- [获取内购订单信息](https://open.dingtalk.com/document/development/obtain-information-about-internal-purchase-orders): `obtain-information-about-internal-purchase-orders` -- [获取分支组织列表](https://open.dingtalk.com/document/development/obtains-the-branch-organization-list): `obtains-the-branch-organization-list` -- [获取助理技能信息](https://open.dingtalk.com/document/development/api-getassistantactioninfo): `api-getassistantactioninfo` -- [获取单个审批实例详情](https://open.dingtalk.com/document/development/get-details-single-approval-instance): `get-details-single-approval-instance` -- [获取参与考勤人员](https://open.dingtalk.com/document/development/batch-query-of-attendance-group-members): `batch-query-of-attendance-group-members` -- [获取参与考勤人员的userid](https://open.dingtalk.com/document/development/query-attendance-group-personnel-information-in-batches): `query-attendance-group-personnel-information-in-batches` -- [获取发布智能填表数量和使用智能填表人数(部门维度)](https://open.dingtalk.com/document/development/obtains-the-number-of-tables-published-by-the-enterprise-from): `obtains-the-number-of-tables-published-by-the-enterprise-from` -- [获取员工人数](https://open.dingtalk.com/document/development/obtain-the-number-of-employees): `obtain-the-number-of-employees` -- [获取员工人数](https://open.dingtalk.com/document/development/user-management-acquires-number-employees): `user-management-acquires-number-employees` -- [获取员工离职信息](https://open.dingtalk.com/document/development/obtain-multiple-employee-demission-information): `obtain-multiple-employee-demission-information` -- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/intelligent-personnel-obtain-employee-roster-information): `intelligent-personnel-obtain-employee-roster-information` -- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/obtain-employee-roster-field-information-in-batches): `obtain-employee-roster-field-information-in-batches` -- [获取图文卡片详情](https://open.dingtalk.com/document/development/get-message-card-details): `get-message-card-details` -- [获取在职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-on-the-job-employees-of-the): `intelligent-personnel-query-the-list-of-on-the-job-employees-of-the` -- [获取培训观看数据](https://open.dingtalk.com/document/development/obtains-the-playback-data-of-a-live-stream): `obtains-the-playback-data-of-a-live-stream` -- [获取培训课程的基本信息](https://open.dingtalk.com/document/development/get-basic-information-about-the-course): `get-basic-information-about-the-course` -- [获取填表实例数据](https://open.dingtalk.com/document/development/obtains-multiple-form-filling-records): `obtains-multiple-form-filling-records` -- [获取外部联系人列表](https://open.dingtalk.com/document/development/obtain-the-external-contact-list): `obtain-the-external-contact-list` -- [获取外部联系人标签列表](https://open.dingtalk.com/document/development/obtains-a-list-of-external-contact-tags): `obtains-a-list-of-external-contact-tags` -- [获取外部联系人详情](https://open.dingtalk.com/document/development/obtains-the-external-contact-details-of-an-enterprise): `obtains-the-external-contact-details-of-an-enterprise` -- [获取多个表单实例ID](https://open.dingtalk.com/document/development/obtain-the-ids-of-multiple-form-instances): `obtain-the-ids-of-multiple-form-instances` -- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-a-sub-department-id-list): `obtain-a-sub-department-id-list` -- [获取学习的知识列表](https://open.dingtalk.com/document/development/api-getknowledgelist): `api-getknowledgelist` -- [获取学段元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-to-query): `dingtalk-the-main-data-of-the-education-ecosystem-to-query` -- [获取学生ID列表](https://open.dingtalk.com/document/development/retrieve-student-based-class): `retrieve-student-based-class` -- [获取学生信息](https://open.dingtalk.com/document/development/obtain-student-information): `obtain-student-information` -- [获取学生监护人详情](https://open.dingtalk.com/document/development/obtain-the-relationship-between-home-and-school-personnel): `obtain-the-relationship-between-home-and-school-personnel` -- [获取学科元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject): `dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject` -- [获取学科实例列表](https://open.dingtalk.com/document/development/get-the-list-of-subject-examples): `get-the-list-of-subject-examples` -- [获取学科实例详情](https://open.dingtalk.com/document/development/query-dingtalk-education-subject-instances): `query-dingtalk-education-subject-instances` -- [获取定制应用的access_token](https://open.dingtalk.com/document/development/obtains-the-enterprise-authorized-credential): `obtains-the-enterprise-authorized-credential` -- [获取实例ID列表](https://open.dingtalk.com/document/development/obtains-a-list-of-instance-ids): `obtains-a-list-of-instance-ids` -- [获取实例详情](https://open.dingtalk.com/document/development/query-collection-form-instance-details): `query-collection-form-instance-details` -- [获取审批实例ID列表](https://open.dingtalk.com/document/development/operation-to-retrieve-a-list-of): `operation-to-retrieve-a-list-of` -- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/query-the-space-of-an-approval-nail): `query-the-space-of-an-approval-nail` -- [获取工作通知消息的发送结果](https://open.dingtalk.com/document/development/gets-the-result-of-sending-messages-asynchronously-to-the-enterprise): `gets-the-result-of-sending-messages-asynchronously-to-the-enterprise` -- [获取工作通知消息的发送进度](https://open.dingtalk.com/document/development/obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session): `obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session` -- [获取已加入或正在申请加入上下游组织的组织和个人信息](https://open.dingtalk.com/document/development/obtains-the-information-about-how-to-join-or-apply-to): `obtains-the-information-about-how-to-join-or-apply-to` -- [获取平台服务资源](https://open.dingtalk.com/document/development/obtain-platform-service-resources): `obtain-platform-service-resources` -- [获取平台资源](https://open.dingtalk.com/document/development/obtain-platform-resources): `obtain-platform-resources` -- [获取年报数据](https://open.dingtalk.com/document/development/obtain-annual-report-data): `obtain-annual-report-data` -- [获取应用下的页面列表](https://open.dingtalk.com/document/development/obtains-the-page-list-under-an-application): `obtains-the-page-list-under-an-application` -- [获取应用未激活的企业列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprises-whose-applications-are-not-activated): `obtains-a-list-of-enterprises-whose-applications-are-not-activated` -- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/exchange-code-for-the-identity-information-of-a-microapplication-administrator): `exchange-code-for-the-identity-information-of-a-microapplication-administrator` -- [获取应用自定义空间使用详情](https://open.dingtalk.com/document/development/queries-the-usage-details-of-a-custom-application-space): `queries-the-usage-details-of-a-custom-application-space` -- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/obtains-the-information-about-all-manageable-templates-of-the-current): `obtains-the-information-about-all-manageable-templates-of-the-current` -- [获取待入职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-employees-to-be-hired): `intelligent-personnel-query-the-list-of-employees-to-be-hired` -- [获取微应用后台免登的access_token](https://open.dingtalk.com/document/development/obtain-the-ssotoken-for-micro-application-background-logon-free): `obtain-the-ssotoken-for-micro-application-background-logon-free` -- [获取报表假期数据](https://open.dingtalk.com/document/development/obtains-the-holiday-data-from-the-smart-attendance-report): `obtains-the-holiday-data-from-the-smart-attendance-report` -- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-a-list-of): `you-can-call-this-operation-to-retrieve-a-list-of` -- [获取指定用户的所有父部门列表](https://open.dingtalk.com/document/development/queries-the-list-of-all-parent-departments-of-a-user): `queries-the-list-of-all-parent-departments-of-a-user` -- [获取指定角色的员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-of-a-role): `obtain-the-list-of-employees-of-a-role` -- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-department): `queries-all-parent-departments-of-a-department` -- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/query-the-list-of-all-parent-departments-of-a-department): `query-the-list-of-all-parent-departments-of-a-department` -- [获取授权应用的基本信息](https://open.dingtalk.com/document/development/obtains-application-information-of-an-enterprise): `obtains-application-information-of-an-enterprise` -- [获取数字化证书](https://open.dingtalk.com/document/development/obtain-digital-certificate): `obtain-digital-certificate` -- [获取数字区县组织信息](https://open.dingtalk.com/document/development/querydigitaldistrictorginfo-api-reference): `querydigitaldistrictorginfo-api-reference` -- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-upload-information): `obtain-upload-information` -- [获取文件下载信息](https://open.dingtalk.com/document/development/obtain-download-file-info): `obtain-download-file-info` -- [获取文章详情](https://open.dingtalk.com/document/development/get-article): `get-article` -- [获取日志接收人员列表](https://open.dingtalk.com/document/development/queries-log-sharing-personnel): `queries-log-sharing-personnel` -- [获取日志相关人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-log-related-personnel-by-type): `obtains-a-list-of-log-related-personnel-by-type` -- [获取日志统计数据](https://open.dingtalk.com/document/development/query-log-statistics): `query-log-statistics` -- [获取服务窗联系人信息](https://open.dingtalk.com/document/development/obtains-the-contact-information-of-the-service-window): `obtains-the-contact-information-of-the-service-window` -- [获取未活跃用户登录明细](https://open.dingtalk.com/document/development/obtains-the-logon-details-of-inactive-users): `obtains-the-logon-details-of-inactive-users` -- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/queries-the-inactive-users-or-active-users-under-an-enterprise): `queries-the-inactive-users-or-active-users-under-an-enterprise` -- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/query-data-of-inactive-users): `query-data-of-inactive-users` -- [获取权限列表](https://open.dingtalk.com/document/development/obtain-a-permission-list): `obtain-a-permission-list` -- [获取模板code](https://open.dingtalk.com/document/development/obtains-the-template-code-based-on-the-template-name): `obtains-the-template-code-based-on-the-template-name` -- [获取模板详情](https://open.dingtalk.com/document/development/query-template-details): `query-template-details` -- [获取流程定义](https://open.dingtalk.com/document/development/obtain-process-definition): `obtain-process-definition` -- [获取流程实例](https://open.dingtalk.com/document/development/obtain-process-instance): `obtain-process-instance` -- [获取流程节点按钮列表](https://open.dingtalk.com/document/development/obtain-a-list-of-process-node-buttons): `obtain-a-list-of-process-node-buttons` -- [获取流程设计的节点信息](https://open.dingtalk.com/document/development/obtain-the-information-about-the-nodes-in-process-design): `obtain-the-information-about-the-nodes-in-process-design` -- [获取班次摘要信息](https://open.dingtalk.com/document/development/enterprise-shift-query-in-batches): `enterprise-shift-query-in-batches` -- [获取班次详情](https://open.dingtalk.com/document/development/shift-query): `shift-query` -- [获取班级内学生的关系列表](https://open.dingtalk.com/document/development/queries-the-list-of-relationships): `queries-the-list-of-relationships` -- [获取班级圈动态列表](https://open.dingtalk.com/document/development/dynamic-list-opening-of-class-circle): `dynamic-list-opening-of-class-circle` -- [获取班级圈话题列表](https://open.dingtalk.com/document/development/obtain-a-topic-list-of-class-circles): `obtain-a-topic-list-of-class-circles` -- [获取用户创建文档数和创建文档人数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-dingtalk-documents-created-per-day-in-an): `queries-the-number-dingtalk-documents-created-per-day-in-an` -- [获取用户创建的AI助理列表](https://open.dingtalk.com/document/development/assistant-acquires-list-assistants-created-by-users): `assistant-acquires-list-assistants-created-by-users` -- [获取用户创建的填表模板](https://open.dingtalk.com/document/development/obtains-the-template-that-a-user-creates): `obtains-the-template-that-a-user-creates` -- [获取用户发送日志的概要信息](https://open.dingtalk.com/document/development/view-log-summary-data): `view-log-summary-data` -- [获取用户可查看的公告](https://open.dingtalk.com/document/development/list-the-user-s-announcement-list): `list-the-user-s-announcement-list` -- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/list-the-microapplications-visible-to-employees): `list-the-microapplications-visible-to-employees` -- [获取用户可见范围的AI助理列表](https://open.dingtalk.com/document/development/api-listvisibleassistant): `api-listvisibleassistant` -- [获取用户基本信息](https://open.dingtalk.com/document/development/queries-basic-user-information): `queries-basic-user-information` -- [获取用户待审批数量](https://open.dingtalk.com/document/development/obtain-the-number-of-tasks-to-be-approved-by-me): `obtain-the-number-of-tasks-to-be-approved-by-me` -- [获取用户授权的持久授权码](https://open.dingtalk.com/document/development/persistent-authorization-code): `persistent-authorization-code` -- [获取用户日志未读数](https://open.dingtalk.com/document/development/querying-the-employee-s-log-is-not-reading): `querying-the-employee-s-log-is-not-reading` -- [获取用户月活跃数据](https://open.dingtalk.com/document/development/retrieves-the-user-s-monthly-active-data): `retrieves-the-user-s-monthly-active-data` -- [获取用户版本分布情况](https://open.dingtalk.com/document/development/queries-the-distribution-of-user-versions): `queries-the-distribution-of-user-versions` -- [获取用户签到记录](https://open.dingtalk.com/document/development/obtain-the-check-in-records-of-multiple-users): `obtain-the-check-in-records-of-multiple-users` -- [获取用户考勤组](https://open.dingtalk.com/document/development/queries-a-user-attendance-group): `queries-a-user-attendance-group` -- [获取申请单列表](https://open.dingtalk.com/document/development/search-enterprise-approval-form-data): `search-enterprise-approval-form-data` -- [获取申请单详情](https://open.dingtalk.com/document/development/obtains-the-detailed-data-of-a-single-request): `obtains-the-detailed-data-of-a-single-request` -- [获取离职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-company-turnover-list): `intelligent-personnel-query-company-turnover-list` -- [获取空间信息](https://open.dingtalk.com/document/development/obtain-space-information): `obtain-space-information` -- [获取第三方个人应用的access_token](https://open.dingtalk.com/document/development/obtain-personal-application): `obtain-personal-application` -- [获取管理员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-administrators): `obtains-a-list-of-administrators` -- [获取管理员列表](https://open.dingtalk.com/document/development/query-the-administrator-list): `query-the-administrator-list` -- [获取管理员的应用管理权限](https://open.dingtalk.com/document/development/obtains-the-administrator-s-microapplication-management-permission): `obtains-the-administrator-s-microapplication-management-permission` -- [获取管理员通讯录权限范围](https://open.dingtalk.com/document/development/query-permissions-of-the-administrator-address-book): `query-permissions-of-the-administrator-address-book` -- [获取考勤报表列值](https://open.dingtalk.com/document/development/queries-the-column-value-of-the-attendance-report): `queries-the-column-value-of-the-attendance-report` -- [获取考勤报表列定义](https://open.dingtalk.com/document/development/queries-the-enterprise-attendance-report-column): `queries-the-enterprise-attendance-report-column` -- [获取考勤组详情](https://open.dingtalk.com/document/development/query-a-single-attendance-group): `query-a-single-attendance-group` -- [获取联系人的元数据](https://open.dingtalk.com/document/development/gets-the-metadata-description-of-a-crm-contact-object): `gets-the-metadata-description-of-a-crm-contact-object` -- [获取自定义对象的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-custom-object): `get-metadata-description-of-crm-custom-object` -- [获取花名册元数据](https://open.dingtalk.com/document/development/intelligent-personnel-roster-metadata-query): `intelligent-personnel-roster-metadata-query` -- [获取花名册字段组详情](https://open.dingtalk.com/document/development/get-roster-field-group-details): `get-roster-field-group-details` -- [获取视频会议详情](https://open.dingtalk.com/document/development/get-details-of-the-video-conference): `get-details-of-the-video-conference` -- [获取视频直播明细列表](https://open.dingtalk.com/document/development/queries-the-details-list-of-apsaravideo-live): `queries-the-details-list-of-apsaravideo-live` -- [获取视频直播观看人员列表](https://open.dingtalk.com/document/development/query-users-of-apsaravideo-live): `query-users-of-apsaravideo-live` -- [获取角色列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprise-roles): `obtains-a-list-of-enterprise-roles` -- [获取角色组列表](https://open.dingtalk.com/document/development/obtains-the-role-group-information): `obtains-the-role-group-information` -- [获取角色详情](https://open.dingtalk.com/document/development/queries-role-details): `queries-role-details` -- [获取课堂明细数据](https://open.dingtalk.com/document/development/obtain-course-detail-data): `obtain-course-detail-data` -- [获取课堂概要数据](https://open.dingtalk.com/document/development/get-course-summary-data): `get-course-summary-data` -- [获取课程列表](https://open.dingtalk.com/document/development/get-course-list): `get-course-list` -- [获取课程参与方列表](https://open.dingtalk.com/document/development/get-a-list-of-course-participants): `get-a-list-of-course-participants` -- [获取课程详情](https://open.dingtalk.com/document/development/get-course-details): `get-course-details` -- [获取跟进记录对象的元数据](https://open.dingtalk.com/document/development/obtains-the-metadata-description-of-the-crm-follow-up-record-object): `obtains-the-metadata-description-of-the-crm-follow-up-record-object` -- [获取通讯录权限范围](https://open.dingtalk.com/document/development/obtain-corpsecret-authorization-scope): `obtain-corpsecret-authorization-scope` -- [获取部门下人员列表](https://open.dingtalk.com/document/development/obtains-the-list-of-people-under-a-department): `obtains-the-list-of-people-under-a-department` -- [获取部门企业账号用户详情](https://open.dingtalk.com/document/development/queries-account-details): `queries-account-details` -- [获取部门列表](https://open.dingtalk.com/document/development/obtain-the-department-list): `obtain-the-department-list` -- [获取部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-industry-departments): `obtains-a-list-of-industry-departments` -- [获取部门列表](https://open.dingtalk.com/document/development/obtains-the-department-node-list): `obtains-the-department-node-list` -- [获取部门列表](https://open.dingtalk.com/document/development/user-management-acquires-the-list-departments): `user-management-acquires-the-list-departments` -- [获取部门扩展信息](https://open.dingtalk.com/document/development/obtain-department-extension-information): `obtain-department-extension-information` -- [获取部门用户userid列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employee-ids-by-department-id): `obtain-the-list-of-employee-ids-by-department-id` -- [获取部门用户userid列表](https://open.dingtalk.com/document/development/query-the-list-of-department-userids): `query-the-list-of-department-userids` -- [获取部门用户基础信息](https://open.dingtalk.com/document/development/obtain-the-basic-information-of-department-users): `obtain-the-basic-information-of-department-users` -- [获取部门用户签到记录](https://open.dingtalk.com/document/development/get-check-in-data): `get-check-in-data` -- [获取部门用户详情](https://open.dingtalk.com/document/development/obtain-department-members-details): `obtain-department-members-details` -- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-department-user-details): `queries-department-user-details` -- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-the-complete-information-of-a-department-user): `queries-the-complete-information-of-a-department-user` -- [获取部门的扩展字段定义](https://open.dingtalk.com/document/development/gets-the-extended-field-definition-of-a-department): `gets-the-extended-field-definition-of-a-department` -- [获取部门详情](https://open.dingtalk.com/document/development/industry-address-book-api-for-obtaining-department-information): `industry-address-book-api-for-obtaining-department-information` -- [获取部门详情](https://open.dingtalk.com/document/development/obtains-queries-department-details): `obtains-queries-department-details` -- [获取部门详情](https://open.dingtalk.com/document/development/queries-department-details): `queries-department-details` -- [获取部门详情](https://open.dingtalk.com/document/development/query-department-details0-v2): `query-department-details0-v2` -- [获取问答明细](https://open.dingtalk.com/document/development/api-getaskdetail): `api-getaskdetail` -- [获得企业创建日志相关信息(组织维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-organization): `obtains-information-about-a-created-enterprise-log-from-the-organization` -- [获得企业创建日志相关信息(部门维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-department): `obtains-information-about-a-created-enterprise-log-from-the-department` -- [获得用户创建文档数和创建文档人数(部门维度)](https://open.dingtalk.com/document/development/obtain-departmental-dimensions-documents-created-people-creating): `obtain-departmental-dimensions-documents-created-people-creating` -- [获得组织维度日程相关信息](https://open.dingtalk.com/document/development/queries-the-number-of-people-who-have-created-an-event): `queries-the-number-of-people-who-have-created-an-event` -- [解绑设备](https://open.dingtalk.com/document/development/unbind-a-smart-hardware-device): `unbind-a-smart-hardware-device` -- [计算请假时长](https://open.dingtalk.com/document/development/calculate-leave-duration): `calculate-leave-duration` -- [设备入会](https://open.dingtalk.com/document/development/equipment-membership): `equipment-membership` -- [设备账号向目标用户发送DING消息](https://open.dingtalk.com/document/development/device-publishing): `device-publishing` -- [设定角色成员管理范围](https://open.dingtalk.com/document/development/update-role-member-management-department-scope): `update-role-member-management-department-scope` -- [设置禁止群成员私聊](https://open.dingtalk.com/document/development/set-private-chat): `set-private-chat` -- [还原回收站文件(夹)](https://open.dingtalk.com/document/development/restore-recycle-bin-files-folder): `restore-recycle-bin-files-folder` -- [通知审批撤销](https://open.dingtalk.com/document/development/notify-the-attendance-to-modify-the-punch-result-when-the): `notify-the-attendance-to-modify-the-punch-result-when-the` -- [通知审批通过](https://open.dingtalk.com/document/development/notice-of-approval): `notice-of-approval` -- [通知换班通过](https://open.dingtalk.com/document/development/shift-change-operation-after-approval): `shift-change-operation-after-approval` -- [通知授权结果](https://open.dingtalk.com/document/development/notify-the-authorization-result): `notify-the-authorization-result` -- [通知补卡通过](https://open.dingtalk.com/document/development/make-up-the-card-after-approval): `make-up-the-card-after-approval` -- [通过免登码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-userid-of-a-user-by-using-the-log-free): `obtain-the-userid-of-a-user-by-using-the-log-free` -- [通过免登码获取用户信息(不推荐)](https://open.dingtalk.com/document/development/get-user-userid-through-login-free-code): `get-user-userid-through-login-free-code` -- [通过调用者unionId获取激活码](https://open.dingtalk.com/document/development/obtain-the-activation-code-by-calling-the-union-id-of): `obtain-the-activation-code-by-calling-the-union-id-of` -- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/query-form-instances-using-advanced-search-conditions): `query-form-instances-using-advanced-search-conditions` -- [邀请其他组织企业账号加入](https://open.dingtalk.com/document/development/invite-other-organization-specific-accounts-to-join): `invite-other-organization-specific-accounts-to-join` -- [配置发票适用人群](https://open.dingtalk.com/document/development/configure-invoice-users): `configure-invoice-users` -- [重新授权未激活应用的企业](https://open.dingtalk.com/document/development/re-authorize-enterprises-whose-applications-are-not-activated): `re-authorize-enterprises-whose-applications-are-not-activated` -- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-for-a-single-schedule): `sign-in-for-a-single-schedule` -- [钉钉文本翻译](https://open.dingtalk.com/document/development/dingtalk-translation): `dingtalk-translation` -- [静态推荐数据同步](https://open.dingtalk.com/document/development/statically-recommended-data-synchronization): `statically-recommended-data-synchronization` -- [预计算时长](https://open.dingtalk.com/document/development/calculate-duration-based-on-attendance-scheduling): `calculate-duration-based-on-attendance-scheduling` -- [验证激活结果](https://open.dingtalk.com/document/development/verify-activation-results): `verify-activation-results` - -> 已知 Gap:7 条 slug 在 development 段为空壳(<50000B),可能属别的 section(如 solution),待补正确 section,本目录暂不收录。 - - - -## OpenAPI Specs - -钉钉对外暂无机器可读 openapi.json:`TODO(gap #7)`,待平台侧提供,不伪造。 diff --git a/skills/dingtalk-open-platform/references/api-catalog.md b/skills/dingtalk-open-platform/references/api-catalog.md deleted file mode 100644 index e8b505c2..00000000 --- a/skills/dingtalk-open-platform/references/api-catalog.md +++ /dev/null @@ -1,1739 +0,0 @@ -# 开放平台 API 文档目录(全量有效链接) - -开放平台官方文档的**全量有效链接目录**,供本 skill 在发现阶段离线检索接口文档。 - -- URL 模板:`https://open.dingtalk.com/document/development/`(`` 来自真实爬取页面)。 -- 有效性:经内容长度校验(真实页 ≥50KB,空壳 <50KB)后收录;统计见下方生成区块首行。 -- 归类:按 8 能力域(doc_page_group)+ 接口 namespace / CSV 功能分类;无法归类入「其它 / 未归类」。 -- 与本 skill [llm.md](../llm.md) **同源同步**,由 `scripts/build_llm_md.py` 生成,勿手改区块内容。 -- 用法:按能力域 → 功能子类定位 `- [中文标题](URL): slug`,拿到 URL 后浏览文档,再用 `dws api` / `dws devapp` 调用(见 [api-invocation.md](api-invocation.md))。 - - - - -> 全量 slug **1520** 条;有效(≥50000B) **1513** 条,无效(空壳) **7** 条;本目录收录 **1513** 条真实文档链接。 - -## 应用开发(642 条) - -二级 Tab:开发指南 / 服务端API / 客户端JSAPI / 事件订阅 / 钉钉CLI / 开发工具 / 平台服务(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 钉钉文档(71 条) - -- [修改知识库文档成员权限](https://open.dingtalk.com/document/development/update-team-space-document-user-permissions): `update-team-space-document-user-permissions` -- [创建工作表](https://open.dingtalk.com/document/development/create-a-worksheet): `create-a-worksheet` -- [创建快捷方式](https://open.dingtalk.com/document/development/api-createshortcut): `api-createshortcut` -- [创建条件格式规则](https://open.dingtalk.com/document/development/create-conditional-formatting-rules): `create-conditional-formatting-rules` -- [创建浮动图片](https://open.dingtalk.com/document/development/api-createfloatimage): `api-createfloatimage` -- [创建知识库文档](https://open.dingtalk.com/document/development/create-team-space-document): `create-team-space-document` -- [创建筛选](https://open.dingtalk.com/document/development/api-createfilter): `api-createfilter` -- [创建筛选视图](https://open.dingtalk.com/document/development/api-createfilterview): `api-createfilterview` -- [删除下拉列表](https://open.dingtalk.com/document/development/delete-drop-down-list): `delete-drop-down-list` -- [删除列](https://open.dingtalk.com/document/development/delete-column): `delete-column` -- [删除块元素](https://open.dingtalk.com/document/development/api-docdeleteblock): `api-docdeleteblock` -- [删除工作表](https://open.dingtalk.com/document/development/delete-classic-workbooks): `delete-classic-workbooks` -- [删除浮动图片](https://open.dingtalk.com/document/development/api-deletefloatimage): `api-deletefloatimage` -- [删除知识库成员](https://open.dingtalk.com/document/development/delete-team-space-user-permissions): `delete-team-space-user-permissions` -- [删除知识库文档](https://open.dingtalk.com/document/development/delete-team-space-documents): `delete-team-space-documents` -- [删除知识库文档成员](https://open.dingtalk.com/document/development/delete-team-space-document-permissions): `delete-team-space-document-permissions` -- [删除筛选](https://open.dingtalk.com/document/development/api-deletefilter): `api-deletefilter` -- [删除筛选条件](https://open.dingtalk.com/document/development/api-clearfiltercriteria): `api-clearfiltercriteria` -- [删除筛选视图](https://open.dingtalk.com/document/development/api-deletefilterview): `api-deletefilterview` -- [删除筛选视图条件](https://open.dingtalk.com/document/development/api-clearfilterviewcriteria): `api-clearfilterviewcriteria` -- [删除行](https://open.dingtalk.com/document/development/delete-row): `delete-row` -- [合并单元格](https://open.dingtalk.com/document/development/merge-cells): `merge-cells` -- [在段落末尾追加文本](https://open.dingtalk.com/document/development/api-docappendtext): `api-docappendtext` -- [在段落末尾追加行内元素](https://open.dingtalk.com/document/development/api-docappendparagraph): `api-docappendparagraph` -- [复制文档](https://open.dingtalk.com/document/development/api-copydoc): `api-copydoc` -- [工作表中追加行](https://open.dingtalk.com/document/development/append-line): `append-line` -- [批量设置列宽](https://open.dingtalk.com/document/development/api-setcolumnswidth): `api-setcolumnswidth` -- [批量设置行高](https://open.dingtalk.com/document/development/api-setrowsheight): `api-setrowsheight` -- [指定列左侧插入若干列](https://open.dingtalk.com/document/development/insert-column-before-column): `insert-column-before-column` -- [指定行上方插入若干行](https://open.dingtalk.com/document/development/insert-rows-before-rows): `insert-rows-before-rows` -- [插入下拉列表](https://open.dingtalk.com/document/development/insert-drop-down-list): `insert-drop-down-list` -- [插入内容](https://open.dingtalk.com/document/development/api-insertcontent): `api-insertcontent` -- [插入块元素](https://open.dingtalk.com/document/development/api-docinsertblocks): `api-docinsertblocks` -- [新建知识库](https://open.dingtalk.com/document/development/create-a-team-space): `create-a-team-space` -- [更新单元格区域](https://open.dingtalk.com/document/development/update-cell-properties): `update-cell-properties` -- [更新块元素](https://open.dingtalk.com/document/development/api-docblocksmodify): `api-docblocksmodify` -- [更新工作表](https://open.dingtalk.com/document/development/update-worksheet): `update-worksheet` -- [更新浮动图片](https://open.dingtalk.com/document/development/api-updatefloatimage): `api-updatefloatimage` -- [更新知识库成员权限](https://open.dingtalk.com/document/development/update-team-space-user-permissions): `update-team-space-user-permissions` -- [更新筛选](https://open.dingtalk.com/document/development/api-updatefilter): `api-updatefilter` -- [更新筛选视图](https://open.dingtalk.com/document/development/api-updatefilterview): `api-updatefilterview` -- [查找工作表中的单元格](https://open.dingtalk.com/document/development/find-the-next-eligible-cell): `find-the-next-eligible-cell` -- [查找所有符合条件的单元格](https://open.dingtalk.com/document/development/find-all-matching-cells): `find-all-matching-cells` -- [查询块元素](https://open.dingtalk.com/document/development/api-docblocksquery): `api-docblocksquery` -- [查询用户有权限的知识库列表](https://open.dingtalk.com/document/development/querying-the-list-of-user-team-spaces): `querying-the-list-of-user-team-spaces` -- [查询知识库下的目录结构](https://open.dingtalk.com/document/development/query-the-directory-tree-in-the-knowledge-base): `query-the-directory-tree-in-the-knowledge-base` -- [查询知识库信息](https://open.dingtalk.com/document/development/query-team-space): `query-team-space` -- [查询知识库节点信息](https://open.dingtalk.com/document/development/query-knowledge-base-node-information): `query-knowledge-base-node-information` -- [根据 dentryUuid 获取 spaceId](https://open.dingtalk.com/document/development/api-getdentryidbyuuid): `api-getdentryidbyuuid` -- [添加知识库成员](https://open.dingtalk.com/document/development/add-permissions-for-team-space-members): `add-permissions-for-team-space-members` -- [添加知识库文档成员](https://open.dingtalk.com/document/development/add-workspace-document-user-permissions): `add-workspace-document-user-permissions` -- [清除单元格区域内所有内容](https://open.dingtalk.com/document/development/clear-all): `clear-all` -- [清除单元格区域内数据](https://open.dingtalk.com/document/development/clear-cell-data): `clear-cell-data` -- [知识库转交所有者](https://open.dingtalk.com/document/development/api-handoveryworkspace): `api-handoveryworkspace` -- [筛选排序](https://open.dingtalk.com/document/development/api-sortfilter): `api-sortfilter` -- [置顶知识库](https://open.dingtalk.com/document/development/api-pinspace): `api-pinspace` -- [获取 dentryUuid 信息](https://open.dingtalk.com/document/development/api-getuuidbydentryid): `api-getuuidbydentryid` -- [获取任务状态](https://open.dingtalk.com/document/development/api-gettaskinfo): `api-gettaskinfo` -- [获取单元格区域](https://open.dingtalk.com/document/development/get-cell-properties): `get-cell-properties` -- [获取工作表](https://open.dingtalk.com/document/development/obtain-worksheet-properties): `obtain-worksheet-properties` -- [获取所有工作表](https://open.dingtalk.com/document/development/obtain-all-worksheets): `obtain-all-worksheets` -- [获取筛选](https://open.dingtalk.com/document/development/api-getfilter): `api-getfilter` -- [获取筛选视图列表](https://open.dingtalk.com/document/development/api-getfilterviews): `api-getfilterviews` -- [获取资源上传信息](https://open.dingtalk.com/document/development/api-getresourceuploadinfo): `api-getresourceuploadinfo` -- [覆写文档(个人授权)](https://open.dingtalk.com/document/development/api-docupdatecontent): `api-docupdatecontent` -- [覆写文档(应用授权)](https://open.dingtalk.com/document/development/api-doc-updatecontent): `api-doc-updatecontent` -- [设置列隐藏或显示](https://open.dingtalk.com/document/development/set-column-visibility): `set-column-visibility` -- [设置筛选条件](https://open.dingtalk.com/document/development/api-setfiltercriteria): `api-setfiltercriteria` -- [设置筛选视图条件](https://open.dingtalk.com/document/development/api-setfilterviewcriteria): `api-setfilterviewcriteria` -- [设置自动行高](https://open.dingtalk.com/document/development/set-row-height-automatically): `set-row-height-automatically` -- [设置行隐藏或显示](https://open.dingtalk.com/document/development/set-row-visibility): `set-row-visibility` - -### 存储(53 条) - -- [修改权限](https://open.dingtalk.com/document/development/modify-permissions-file): `modify-permissions-file` -- [修改权限](https://open.dingtalk.com/document/development/modify-storage-permissions): `modify-storage-permissions` -- [初始化文件分片上传](https://open.dingtalk.com/document/development/initialize-a-multipart-upload-object): `initialize-a-multipart-upload-object` -- [删除回收项](https://open.dingtalk.com/document/development/delete-recycle-item): `delete-recycle-item` -- [删除文件或文件夹](https://open.dingtalk.com/document/development/delete-a-file-or-folder): `delete-a-file-or-folder` -- [删除文件或文件夹的应用属性](https://open.dingtalk.com/document/development/delete-file-app-attribute): `delete-file-app-attribute` -- [删除权限](https://open.dingtalk.com/document/development/delete-permissions-file): `delete-permissions-file` -- [删除权限](https://open.dingtalk.com/document/development/delete-storage-permissions): `delete-storage-permissions` -- [取消订阅文件变更事件](https://open.dingtalk.com/document/development/unsubscribe-from-file-change-events): `unsubscribe-from-file-change-events` -- [复制文件或文件夹](https://open.dingtalk.com/document/development/copy-an-object): `copy-an-object` -- [恢复文件历史版本](https://open.dingtalk.com/document/development/restore-previous-versions-of-files): `restore-previous-versions-of-files` -- [批量删除回收项](https://open.dingtalk.com/document/development/batch-delete-recycle-items): `batch-delete-recycle-items` -- [批量删除文件或文件夹](https://open.dingtalk.com/document/development/delete-files-or-folders-in-bulk): `delete-files-or-folders-in-bulk` -- [批量复制文件或文件夹](https://open.dingtalk.com/document/development/copy-files-or-folders-in-bulk): `copy-files-or-folders-in-bulk` -- [批量移动文件或文件夹](https://open.dingtalk.com/document/development/bulk-move-files-or-folders): `bulk-move-files-or-folders` -- [批量获取文件或文件夹信息](https://open.dingtalk.com/document/development/get-file-or-folder-information-in-bulk): `get-file-or-folder-information-in-bulk` -- [批量获取文件缩略图](https://open.dingtalk.com/document/development/get-file-thumbnails-in-bulk): `get-file-thumbnails-in-bulk` -- [批量还原回收项](https://open.dingtalk.com/document/development/batch-restore-recycled-items): `batch-restore-recycled-items` -- [提交文件](https://open.dingtalk.com/document/development/submit-documents): `submit-documents` -- [提交文件](https://open.dingtalk.com/document/development/submittal-file): `submittal-file` -- [搜索文件](https://open.dingtalk.com/document/development/search-for-files): `search-for-files` -- [搜索知识库](https://open.dingtalk.com/document/development/search-knowledge-base): `search-knowledge-base` -- [更新文件或文件夹的应用属性](https://open.dingtalk.com/document/development/update-file-application-properties): `update-file-application-properties` -- [添加文件夹](https://open.dingtalk.com/document/development/add-folder): `add-folder` -- [添加权限](https://open.dingtalk.com/document/development/add-permissions-file): `add-permissions-file` -- [添加权限](https://open.dingtalk.com/document/development/add-storage-permissions): `add-storage-permissions` -- [添加空间](https://open.dingtalk.com/document/development/add-space): `add-space` -- [清空回收站](https://open.dingtalk.com/document/development/empty-the-recycle-bin): `empty-the-recycle-bin` -- [知识库下载文件](https://open.dingtalk.com/document/development/knowledge-base-download-file): `knowledge-base-download-file` -- [移动文件或文件夹](https://open.dingtalk.com/document/development/move-a-file-or-folder): `move-a-file-or-folder` -- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-storage-related-information): `obtain-enterprise-storage-related-information` -- [获取回收站信息](https://open.dingtalk.com/document/development/obtain-information-about-the-recycle-bin): `obtain-information-about-the-recycle-bin` -- [获取回收项信息](https://open.dingtalk.com/document/development/obtain-recycling-item-information): `obtain-recycling-item-information` -- [获取回收项列表](https://open.dingtalk.com/document/development/gets-the-list-of-recycle-items): `gets-the-list-of-recycle-items` -- [获取存储中异步任务信息](https://open.dingtalk.com/document/development/get-the-asynchronous-task-information-in-storage): `get-the-asynchronous-task-information-in-storage` -- [获取应用信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-current-application): `obtains-the-information-about-the-current-application` -- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-file-upload-informations): `obtain-file-upload-informations` -- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-storage-upload-information): `obtain-storage-upload-information` -- [获取文件下载信息](https://open.dingtalk.com/document/development/obtains-the-download-information-about-a-file): `obtains-the-download-information-about-a-file` -- [获取文件分片上传信息](https://open.dingtalk.com/document/development/obtains-the-information-about-multipart-uploads-of-an-object): `obtains-the-information-about-multipart-uploads-of-an-object` -- [获取文件或文件夹信息](https://open.dingtalk.com/document/development/obtain-file-or-folder-information): `obtain-file-or-folder-information` -- [获取文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-files-or-folders): `get-a-list-of-files-or-folders` -- [获取文件版本列表](https://open.dingtalk.com/document/development/obtains-a-list-of-file-versions): `obtains-a-list-of-file-versions` -- [获取文件预览或编辑信息](https://open.dingtalk.com/document/development/obtains-the-object-preview-or-editing-information): `obtains-the-object-preview-or-editing-information` -- [获取权限列表](https://open.dingtalk.com/document/development/get-permission-list): `get-permission-list` -- [获取权限列表](https://open.dingtalk.com/document/development/get-the-storage-permission-list): `get-the-storage-permission-list` -- [获取权限继承模式](https://open.dingtalk.com/document/development/get-permission-inheritance-mode): `get-permission-inheritance-mode` -- [获取空间下所有文件或文件夹列表](https://open.dingtalk.com/document/development/get-a-list-of-all-files-or-folders-under-a): `get-a-list-of-all-files-or-folders-under-a` -- [获取空间信息](https://open.dingtalk.com/document/development/get-space-information): `get-space-information` -- [订阅文件变更事件](https://open.dingtalk.com/document/development/subscribe-to-file-change-events): `subscribe-to-file-change-events` -- [设置权限继承模式](https://open.dingtalk.com/document/development/set-permission-inheritance-mode): `set-permission-inheritance-mode` -- [还原回收项](https://open.dingtalk.com/document/development/restore-recycle-items): `restore-recycle-items` -- [重命名文件或文件夹](https://open.dingtalk.com/document/development/rename-a-file-or-folder): `rename-a-file-or-folder` - -### 项目管理(50 条) - -- [任务迁移至回收站](https://open.dingtalk.com/document/development/archive-tasks): `archive-tasks` -- [创建实际工时](https://open.dingtalk.com/document/development/create-actual-work): `create-actual-work` -- [创建或更新项目概览中自定义字段值](https://open.dingtalk.com/document/development/create-or-update-field-values-project-overview): `create-or-update-field-values-project-overview` -- [创建自由任务](https://open.dingtalk.com/document/development/create-a-free-task): `create-a-free-task` -- [创建计划工时](https://open.dingtalk.com/document/development/create-planned-work): `create-planned-work` -- [创建项目](https://open.dingtalk.com/document/development/create-project): `create-project` -- [创建项目任务](https://open.dingtalk.com/document/development/create-a-project-task): `create-a-project-task` -- [删除任务](https://open.dingtalk.com/document/development/delete-task): `delete-task` -- [删除项目成员](https://open.dingtalk.com/document/development/delete-project-members): `delete-project-members` -- [增加或删除自由任务的参与者](https://open.dingtalk.com/document/development/change-task-participant): `change-task-participant` -- [归档项目](https://open.dingtalk.com/document/development/archiving-project): `archiving-project` -- [恢复项目归档](https://open.dingtalk.com/document/development/cancel-project-archiving): `cancel-project-archiving` -- [批量获取自由任务详情](https://open.dingtalk.com/document/development/obtains-details-about-multiple-free-tasks): `obtains-details-about-multiple-free-tasks` -- [搜索任务工作流状态](https://open.dingtalk.com/document/development/search-task-workflow-status): `search-task-workflow-status` -- [搜索企业项目模板](https://open.dingtalk.com/document/development/search-for-enterprise-custom-templates-by-project-template-name): `search-for-enterprise-custom-templates-by-project-template-name` -- [更新任务优先级](https://open.dingtalk.com/document/development/update-task-priority): `update-task-priority` -- [更新任务参与者](https://open.dingtalk.com/document/development/update-task-participants): `update-task-participants` -- [更新任务备注](https://open.dingtalk.com/document/development/update-task-notes): `update-task-notes` -- [更新任务工作流状态](https://open.dingtalk.com/document/development/update-task-workflow-status): `update-task-workflow-status` -- [更新任务开始时间](https://open.dingtalk.com/document/development/update-task-start-time): `update-task-start-time` -- [更新任务截止时间](https://open.dingtalk.com/document/development/update-task-deadline): `update-task-deadline` -- [更新任务执行者](https://open.dingtalk.com/document/development/update-task-performer): `update-task-performer` -- [更新任务标题](https://open.dingtalk.com/document/development/update-task-content): `update-task-content` -- [更新自由任务备注](https://open.dingtalk.com/document/development/update-free-task-notes): `update-free-task-notes` -- [更新自由任务截止时间](https://open.dingtalk.com/document/development/change-free-task-deadline): `change-free-task-deadline` -- [更新自由任务执行者](https://open.dingtalk.com/document/development/change-free-task-executor): `change-free-task-executor` -- [更新自由任务标题](https://open.dingtalk.com/document/development/change-free-task-title): `change-free-task-title` -- [更新自由任务状态](https://open.dingtalk.com/document/development/change-free-task-status): `change-free-task-status` -- [更新自由任务的优先级](https://open.dingtalk.com/document/development/change-free-task-priority): `change-free-task-priority` -- [更新项目任务的自定义字段值](https://open.dingtalk.com/document/development/update-task-custom-field-value): `update-task-custom-field-value` -- [更新项目所在的分组](https://open.dingtalk.com/document/development/update-project-grouping): `update-project-grouping` -- [查询任务分组](https://open.dingtalk.com/document/development/query-task-grouping): `query-task-grouping` -- [查询任务工作流](https://open.dingtalk.com/document/development/query-task-workflow): `query-task-workflow` -- [查询优先级列表](https://open.dingtalk.com/document/development/query-a-priority-list): `query-a-priority-list` -- [查询员工可见的项目分组](https://open.dingtalk.com/document/development/query-available-project-groups): `query-available-project-groups` -- [查询用户任务信息列表](https://open.dingtalk.com/document/development/querying-user-tasks): `querying-user-tasks` -- [查询项目](https://open.dingtalk.com/document/development/query-enterprise-all-projects): `query-enterprise-all-projects` -- [查询项目中的任务](https://open.dingtalk.com/document/development/query-tasks-in-a-project): `query-tasks-in-a-project` -- [查询项目状态](https://open.dingtalk.com/document/development/query-project-status): `query-project-status` -- [根据userId获取Teambition项目用户ID](https://open.dingtalk.com/document/development/obtain-dingtalk-teambition-user-id-based-on-userid): `obtain-dingtalk-teambition-user-id-based-on-userid` -- [根据项目模板创建项目](https://open.dingtalk.com/document/development/create-a-project-from-a-project-template): `create-a-project-from-a-project-template` -- [添加任务的关联内容](https://open.dingtalk.com/document/development/create-a-linked-object-associated-with-a-task): `create-a-linked-object-associated-with-a-task` -- [添加项目成员](https://open.dingtalk.com/document/development/add-project-members): `add-project-members` -- [获取Teambition项目企业ID](https://open.dingtalk.com/document/development/obtain-the-teambition-enterprise-id): `obtain-the-teambition-enterprise-id` -- [获取任务列表](https://open.dingtalk.com/document/development/get-task-list): `get-task-list` -- [获取任务详情](https://open.dingtalk.com/document/development/get-task-details): `get-task-details` -- [获取用户加入的项目](https://open.dingtalk.com/document/development/get-projects-joined-by-users): `get-projects-joined-by-users` -- [获取自由任务详情](https://open.dingtalk.com/document/development/queries-free-task-details): `queries-free-task-details` -- [获取项目成员](https://open.dingtalk.com/document/development/get-project-members): `get-project-members` -- [项目放入回收站](https://open.dingtalk.com/document/development/items-in-recycle-bin): `items-in-recycle-bin` - -### 通讯录(49 条) - -- [企业账号修改钉钉号](https://open.dingtalk.com/document/development/api-changedingtalkid): `api-changedingtalkid` -- [企业账号转交主管理员(创建者)](https://open.dingtalk.com/document/development/transfer-exclusive-account-to-main-administrator-creator): `transfer-exclusive-account-to-main-administrator-creator` -- [停用企业账号](https://open.dingtalk.com/document/development/disable-an-exclusive-account): `disable-an-exclusive-account` -- [创建上下游组织](https://open.dingtalk.com/document/development/create-a-cooperation-space): `create-a-cooperation-space` -- [删除用户属性可见性设置](https://open.dingtalk.com/document/development/delete-enterprise-employee-attribute-field-visibility-settings): `delete-enterprise-employee-attribute-field-visibility-settings` -- [删除通讯录隐藏设置](https://open.dingtalk.com/document/development/delete-hide-settings): `delete-hide-settings` -- [删除限制查看通讯录设置](https://open.dingtalk.com/document/development/delete-visible-restrictions): `delete-visible-restrictions` -- [启用企业账号](https://open.dingtalk.com/document/development/enable-a-dedicated-account): `enable-a-dedicated-account` -- [异步转译通讯录ID](https://open.dingtalk.com/document/development/asynchronous-address-book-file-content-translation): `asynchronous-address-book-file-content-translation` -- [强制登出企业账号](https://open.dingtalk.com/document/development/force-logout-from-dedicated-account): `force-logout-from-dedicated-account` -- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/apply-for-batch-addition-through-upstream-and-downstream-organizations): `apply-for-batch-addition-through-upstream-and-downstream-organizations` -- [批量通过伙伴组织的加入申请](https://open.dingtalk.com/document/development/batch-through-the-application-of-partner-organizations-to-join-contact): `batch-through-the-application-of-partner-organizations-to-join-contact` -- [授权企业账号可加入多组织](https://open.dingtalk.com/document/development/authorize-a-dedicated-account-to-join-multiple-organizations): `authorize-a-dedicated-account-to-join-multiple-organizations` -- [授权其他组织查看本组织的企业账号信息](https://open.dingtalk.com/document/development/api-orgaccountmobilevisibleinotherorg): `api-orgaccountmobilevisibleinotherorg` -- [搜索用户userId](https://open.dingtalk.com/document/development/address-book-search-user-id): `address-book-search-user-id` -- [搜索部门ID](https://open.dingtalk.com/document/development/address-book-search-department-id): `address-book-search-department-id` -- [新增或修改限制查看通讯录设置](https://open.dingtalk.com/document/development/add-or-modify-visibility-settings-for-address-book-restrictions): `add-or-modify-visibility-settings-for-address-book-restrictions` -- [新增或更新通讯录隐藏设置](https://open.dingtalk.com/document/development/update-address-book-hide-settings): `update-address-book-hide-settings` -- [更新伙伴组织在上下游组织内的属性信息](https://open.dingtalk.com/document/development/update-properties-of-branches-in-alibaba-group-1): `update-properties-of-branches-in-alibaba-group-1` -- [更新分支组织在主干组织内的属性信息](https://open.dingtalk.com/document/development/updates-the-property-information-of-a-branch-organization-in-a): `updates-the-property-information-of-a-branch-organization-in-a` -- [查询企业账号拥有的组织](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-organization-that): `you-can-call-this-operation-to-query-the-organization-that` -- [查询企业账号状态](https://open.dingtalk.com/document/development/query-dedicated-account-status-1): `query-dedicated-account-status-1` -- [查询离职记录列表](https://open.dingtalk.com/document/development/query-the-details-of-employees-who-have-left-office): `query-the-details-of-employees-who-have-left-office` -- [根据原dingId查询迁移后的dingId](https://open.dingtalk.com/document/development/query-the-new-dingid-based-on-the-original-dingid): `query-the-new-dingid-based-on-the-original-dingid` -- [根据原unionId查询迁移后的unionId](https://open.dingtalk.com/document/development/the-union-id-that-you-want-to-query-you-can): `the-union-id-that-you-want-to-query-you-can` -- [根据手机号查询用户](https://open.dingtalk.com/document/development/query-users-by-phone-number): `query-users-by-phone-number` -- [根据迁移后的dingId查询原dingId](https://open.dingtalk.com/document/development/query-the-original-dingid-based-on-the-dingid-after-migration): `query-the-original-dingid-based-on-the-dingid-after-migration` -- [根据迁移后的unionId查询原unionId](https://open.dingtalk.com/document/development/query-the-original-union-id-based-on-the-union-id): `query-the-original-union-id-based-on-the-union-id` -- [获取上下游组织的邀请信息](https://open.dingtalk.com/document/development/obtain-the-invitation-information-of-a-cooperation-space): `obtain-the-invitation-information-of-a-cooperation-space` -- [获取上下级组织分支授权的数据](https://open.dingtalk.com/document/development/data-authorized-by-a-branch-of-an-associated-organization): `data-authorized-by-a-branch-of-an-associated-organization` -- [获取企业最新钉钉指数信息](https://open.dingtalk.com/document/development/queries-the-latest-dingtalk-index-information): `queries-the-latest-dingtalk-index-information` -- [获取企业认证信息](https://open.dingtalk.com/document/development/obtain-enterprise-authentication-information): `obtain-enterprise-authentication-information` -- [获取企业邀请信息](https://open.dingtalk.com/document/development/obtain-invitation-information): `obtain-invitation-information` -- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-the-list-of-sub-department-ids): `obtain-the-list-of-sub-department-ids` -- [获取异步转译任务结果](https://open.dingtalk.com/document/development/obtains-the-results-of-an-asynchronous-translation-task): `obtains-the-results-of-an-asynchronous-translation-task` -- [获取用户属性可见性设置](https://open.dingtalk.com/document/development/pull-hidden-property-field-for-enterprise-employees): `pull-hidden-property-field-for-enterprise-employees` -- [获取用户通讯录个人信息](https://open.dingtalk.com/document/development/dingtalk-retrieve-user-information): `dingtalk-retrieve-user-information` -- [获取用户高管模式设置](https://open.dingtalk.com/document/development/get-user-executive-mode-settings): `get-user-executive-mode-settings` -- [获取通讯录隐藏设置](https://open.dingtalk.com/document/development/obtains-the-hide-settings-of-the-address-book): `obtains-the-hide-settings-of-the-address-book` -- [获取部门用户基础信息](https://open.dingtalk.com/document/development/queries-the-simple-information-of-a-department-user): `queries-the-simple-information-of-a-department-user` -- [获取限制查看通讯录设置列表](https://open.dingtalk.com/document/development/gets-a-list-of-address-book-limit-visibility-settings): `gets-a-list-of-address-book-limit-visibility-settings` -- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-an-organization): `disassociate-an-organization` -- [解除关联组织](https://open.dingtalk.com/document/development/disassociate-upstream-and-downstream-organizations): `disassociate-upstream-and-downstream-organizations` -- [设置伙伴组织在上下游组织内的可见范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-branch-in-the-group-1): `set-the-visible-range-of-the-branch-in-the-group-1` -- [设置分支组织在主干组织内的可见范围](https://open.dingtalk.com/document/development/sets-the-visible-range-of-branch-organizations-within-the-group): `sets-the-visible-range-of-branch-organizations-within-the-group` -- [设置用户属性可见性](https://open.dingtalk.com/document/development/add-or-update-the-hidden-settings-of-the-employee-property): `add-or-update-the-hidden-settings-of-the-employee-property` -- [设置部门可见性优先级](https://open.dingtalk.com/document/development/set-address-book-visibility-sub-department-settings-to-take-precedence): `set-address-book-visibility-sub-department-settings-to-take-precedence` -- [设置高管模式](https://open.dingtalk.com/document/development/update-executive-settings): `update-executive-settings` -- [通讯录userId排序](https://open.dingtalk.com/document/development/address-book-userid-sorting): `address-book-userid-sorting` - -### 电子签(41 条) - -- [ISV服务商数据初始化](https://open.dingtalk.com/document/development/offline-isv-service-provider-data-initialization): `offline-isv-service-provider-data-initialization` -- [e签宝数据初始化](https://open.dingtalk.com/document/development/isv-service-provider-data-initialization): `isv-service-provider-data-initialization` -- [创建签署流程](https://open.dingtalk.com/document/development/use-the-api-to-initiate-a-signature-process): `use-the-api-to-initiate-a-signature-process` -- [取消企业授权](https://open.dingtalk.com/document/development/cancel-enterprise-authorization): `cancel-enterprise-authorization` -- [取消企业授权](https://open.dingtalk.com/document/development/e-sign-has-revoked-the-enterprise-authorization): `e-sign-has-revoked-the-enterprise-authorization` -- [套餐转售---分润模式](https://open.dingtalk.com/document/development/package-resale-1-distribution-mode): `package-resale-1-distribution-mode` -- [套餐转售---底价结算模式](https://open.dingtalk.com/document/development/package-resale-2-reserve-price-settlement-mode): `package-resale-2-reserve-price-settlement-mode` -- [套餐转售(分润模式)](https://open.dingtalk.com/document/development/package-resale-profit-distribution-model-1): `package-resale-profit-distribution-model-1` -- [套餐转售(底价结算模式)](https://open.dingtalk.com/document/development/package-resale-base-price-settlement-mode-1): `package-resale-base-price-settlement-mode-1` -- [查询个人是否实名认证](https://open.dingtalk.com/document/development/query-personal-information): `query-personal-information` -- [查询企业是否实名](https://open.dingtalk.com/document/development/check-whether-enterprise-registered-realname-e-sign-treasure): `check-whether-enterprise-registered-realname-e-sign-treasure` -- [查询企业是否实名认证](https://open.dingtalk.com/document/development/query-enterprise-information): `query-enterprise-information` -- [查询套餐余量](https://open.dingtalk.com/document/development/query-package-balance): `query-package-balance` -- [查询用户是否实名](https://open.dingtalk.com/document/development/query-whether-a-user-has-a-real-name): `query-whether-a-user-has-a-real-name` -- [获取个人实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-user-s-real): `obtain-the-address-that-is-redirected-to-the-user-s-real` -- [获取企业e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-status-of-enterprise-e-sign-treasure-micro-application): `obtain-the-status-of-enterprise-e-sign-treasure-micro-application` -- [获取企业实名地址](https://open.dingtalk.com/document/development/obtain-enterprise-real-name-address): `obtain-enterprise-real-name-address` -- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-enterprise-console-address): `get-enterprise-console-address` -- [获取企业控制台地址](https://open.dingtalk.com/document/development/get-the-address-enterprise-console-through-e-sign): `get-the-address-enterprise-console-through-e-sign` -- [获取企业的e签宝微应用状态](https://open.dingtalk.com/document/development/obtain-the-current-status-of-the-company-s-e-sign-micro-application): `obtain-the-current-status-of-the-company-s-e-sign-micro-application` -- [获取发起签署任务地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-initiating-signing-task): `obtain-the-address-of-the-initiating-signing-task` -- [获取发起签署任务的地址](https://open.dingtalk.com/document/development/obtain-the-address-used-to-initiate-a-signed-task): `obtain-the-address-used-to-initiate-a-signed-task` -- [获取套餐余量](https://open.dingtalk.com/document/development/get-package-margin): `get-package-margin` -- [获取授权的页面地址](https://open.dingtalk.com/document/development/authurl): `authurl` -- [获取授权的页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-authorized-page): `obtain-the-address-of-the-authorized-page` -- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-file-upload-address-1): `obtain-the-file-upload-address-1` -- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-1): `obtain-the-upload-url-of-a-file-1` -- [获取文件详情](https://open.dingtalk.com/document/development/gets-the-file-details): `gets-the-file-details` -- [获取文件详情](https://open.dingtalk.com/document/development/query-file-details): `query-file-details` -- [获取流程任务合同列表](https://open.dingtalk.com/document/development/obtain-the-process-task-contract-list): `obtain-the-process-task-contract-list` -- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtain-the-process-task-print-approval-list-1): `obtain-the-process-task-print-approval-list-1` -- [获取流程任务用印审批列表](https://open.dingtalk.com/document/development/obtains-the-print-approval-list-for-process-tasks): `obtains-the-print-approval-list-for-process-tasks` -- [获取流程任务的所有合同列表](https://open.dingtalk.com/document/development/get-a-list-of-all-contracts-for-the-process-task): `get-a-list-of-all-contracts-for-the-process-task` -- [获取流程任务详情](https://open.dingtalk.com/document/development/obtain-the-task-details-of-the-corresponding-process): `obtain-the-task-details-of-the-corresponding-process` -- [获取流程的签署详情](https://open.dingtalk.com/document/development/get-the-details-of-process-signing): `get-the-details-of-process-signing` -- [获取流程签署详细信息](https://open.dingtalk.com/document/development/get-process-sign-off-details): `get-process-sign-off-details` -- [获取流程详细信息及操作记录](https://open.dingtalk.com/document/development/obtains-the-task-details): `obtains-the-task-details` -- [获取用户实名地址](https://open.dingtalk.com/document/development/obtain-personal-real-name-address): `obtain-personal-real-name-address` -- [获取签署人签署地址](https://open.dingtalk.com/document/development/get-signatory-address): `get-signatory-address` -- [获取签署人签署地址](https://open.dingtalk.com/document/development/obtain-signing-address-of-signatory-1): `obtain-signing-address-of-signatory-1` -- [获取跳转到企业实名的地址](https://open.dingtalk.com/document/development/obtain-the-address-that-is-redirected-to-the-enterprise-s-real): `obtain-the-address-that-is-redirected-to-the-enterprise-s-real` - -### 消息会话(37 条) - -- [人与人会话中机器人发送互动卡片](https://open.dingtalk.com/document/development/send-dingtalk-interactive-cards-to-person-to-person-chat-sessions): `send-dingtalk-interactive-cards-to-person-to-person-chat-sessions` -- [修改钉钉客联互通群名称](https://open.dingtalk.com/document/development/modify-the-group-name): `modify-the-group-name` -- [修改钉钉客联互通群头像](https://open.dingtalk.com/document/development/modify-the-avatar-of-a-communication-group): `modify-the-avatar-of-a-communication-group` -- [关闭互动卡片吊顶](https://open.dingtalk.com/document/development/close-interactive-card-ceiling): `close-interactive-card-ceiling` -- [创建互通群](https://open.dingtalk.com/document/development/create-an-intercommunication-group): `create-an-intercommunication-group` -- [创建并开启互动卡片吊顶](https://open.dingtalk.com/document/development/send-group-helper-message): `send-group-helper-message` -- [创建店铺群](https://open.dingtalk.com/document/development/create-a-store-group): `create-a-store-group` -- [创建钉外两人群](https://open.dingtalk.com/document/development/create-two-people-outside-the-nail): `create-two-people-outside-the-nail` -- [创建钉钉客联两人互通群](https://open.dingtalk.com/document/development/creating-two-groups-of-people): `creating-two-groups-of-people` -- [创建钉钉客联普通互通群](https://open.dingtalk.com/document/development/create-common-group-new-version): `create-common-group-new-version` -- [创建钉钉客联钉外账号](https://open.dingtalk.com/document/development/create-bc-account-association): `create-bc-account-association` -- [发送轻量级互动卡片](https://open.dingtalk.com/document/development/send-lightweight-interactive-cards): `send-lightweight-interactive-cards` -- [发送钉钉互动卡片(高级版)](https://open.dingtalk.com/document/development/send-interactive-dynamic-cards-1): `send-interactive-dynamic-cards-1` -- [在钉钉客联互通群中使用机器人发送消息](https://open.dingtalk.com/document/development/group-robots-send-messages): `group-robots-send-messages` -- [在钉钉客联互通群中使用钉内账号发送消息](https://open.dingtalk.com/document/development/send-b2c-messages): `send-b2c-messages` -- [在钉钉客联互通群中使用钉外账号发送消息](https://open.dingtalk.com/document/development/send-c2b-messages): `send-c2b-messages` -- [批量查询跨钉两人互通群列表](https://open.dingtalk.com/document/development/queries-the-session-information-of-two-population-groups): `queries-the-session-information-of-two-population-groups` -- [批量设置企业群管理员](https://open.dingtalk.com/document/development/batch-setup-group-administrator): `batch-setup-group-administrator` -- [更换钉钉客联互通群群主](https://open.dingtalk.com/document/development/change-group-owner): `change-group-owner` -- [更新机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/update-the-robot-to-send-interactive-cards): `update-the-robot-to-send-interactive-cards` -- [更新群成员的群昵称](https://open.dingtalk.com/document/development/update-group-nicknames): `update-group-nicknames` -- [更新群管理员](https://open.dingtalk.com/document/development/update-group-administrators): `update-group-administrators` -- [更新钉钉互动卡片](https://open.dingtalk.com/document/development/update-dingtalk-interactive-cards): `update-dingtalk-interactive-cards` -- [机器人发送互动卡片(普通版)](https://open.dingtalk.com/document/development/robots-send-interactive-cards): `robots-send-interactive-cards` -- [查询群内群模板机器人](https://open.dingtalk.com/document/development/search-group-scene-template-robot): `search-group-scene-template-robot` -- [查询群成员](https://open.dingtalk.com/document/development/query-group-members): `query-group-members` -- [查询群禁言状态](https://open.dingtalk.com/document/development/query-group-silence-status): `query-group-silence-status` -- [查询群简要信息](https://open.dingtalk.com/document/development/query-group-information): `query-group-information` -- [查询钉钉客联互通群成员列表](https://open.dingtalk.com/document/development/queries-the-group-member-list): `queries-the-group-member-list` -- [查询钉钉客联钉外账号未读消息数](https://open.dingtalk.com/document/development/querying-the-number-of-unread-messages-of-the-user): `querying-the-number-of-unread-messages-of-the-user` -- [添加钉钉客联互通群成员](https://open.dingtalk.com/document/development/add-a-group-member-1): `add-a-group-member-1` -- [移除钉钉客联互通群成员](https://open.dingtalk.com/document/development/remove-group-members): `remove-group-members` -- [获取群会话的OpenConversationId](https://open.dingtalk.com/document/development/obtain-group-openconversationid): `obtain-group-openconversationid` -- [获取钉钉客联H5页面地址](https://open.dingtalk.com/document/development/get-the-dingtalk-guest-group-session-address): `get-the-dingtalk-guest-group-session-address` -- [解散群聊](https://open.dingtalk.com/document/development/api-dsbandopenscenegroup): `api-dsbandopenscenegroup` -- [解散钉钉客联互通群](https://open.dingtalk.com/document/development/disband-bc-interconnection-group): `disband-bc-interconnection-group` -- [设置群成员禁言状态](https://open.dingtalk.com/document/development/set-group-members-access-control): `set-group-members-access-control` - -### 智能 CRM(34 条) - -- [创建个人或企业客户数据](https://open.dingtalk.com/document/development/add-crm-personal-customers): `add-crm-personal-customers` -- [创建客户群](https://open.dingtalk.com/document/development/create-a-customer-group): `create-a-customer-group` -- [创建客户群组](https://open.dingtalk.com/document/development/crm-create-group): `crm-create-group` -- [删除CRM自定义对象数据](https://open.dingtalk.com/document/development/delete-crm-custom-object-data): `delete-crm-custom-object-data` -- [删除个人或企业客户数据](https://open.dingtalk.com/document/development/delete-crm-personal-customer): `delete-crm-personal-customer` -- [发送服务窗单人消息](https://open.dingtalk.com/document/development/sends-a-single-message-from-the-service-window): `sends-a-single-message-from-the-service-window` -- [批量修改联系人数据](https://open.dingtalk.com/document/development/modify-contact-data-in-batches): `modify-contact-data-in-batches` -- [批量删除跟进记录数据](https://open.dingtalk.com/document/development/batch-delete-follow-up-record-data): `batch-delete-follow-up-record-data` -- [批量发送服务窗消息](https://open.dingtalk.com/document/development/batch-sending-of-service-window-messages): `batch-sending-of-service-window-messages` -- [批量新增个人或企业客户数据](https://open.dingtalk.com/document/development/add-multiple-relationship-data-in-batches): `add-multiple-relationship-data-in-batches` -- [批量新增联系人数据](https://open.dingtalk.com/document/development/add-contact-data-in-batches): `add-contact-data-in-batches` -- [批量新增跟进记录数据](https://open.dingtalk.com/document/development/batch-add-follow-up-record-data): `batch-add-follow-up-record-data` -- [批量更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-multiple-relational-data-tables-at-a-time): `update-multiple-relational-data-tables-at-a-time` -- [批量更新跟进记录数据](https://open.dingtalk.com/document/development/batch-update-follow-up-record-data): `batch-update-follow-up-record-data` -- [批量查询客户群](https://open.dingtalk.com/document/development/query-customer-groups-in-batches): `query-customer-groups-in-batches` -- [批量获取个人或企业客户数据](https://open.dingtalk.com/document/development/acquire-crm-individual-customers-in-batches): `acquire-crm-individual-customers-in-batches` -- [更新个人或企业客户数据](https://open.dingtalk.com/document/development/update-crm-personal-customers): `update-crm-personal-customers` -- [更新客户群组](https://open.dingtalk.com/document/development/crm-update-group): `crm-update-group` -- [查询客户数据](https://open.dingtalk.com/document/development/querying-customer-data): `querying-customer-data` -- [查询客户群列表](https://open.dingtalk.com/document/development/query-the-list-of-customer-groups): `query-the-list-of-customer-groups` -- [查询客户群组列表](https://open.dingtalk.com/document/development/query-groups): `query-groups` -- [查询服务窗粉丝用户基础信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-fans-in-the-service-window): `queries-the-basic-information-of-fans-in-the-service-window` -- [根据指定条件查询个人或企业客户数据](https://open.dingtalk.com/document/development/obtains-crm-individual-customers-in-batches-based-on-specified-query): `obtains-crm-individual-customers-in-batches-based-on-specified-query` -- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/api-getcontacts): `api-getcontacts` -- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/api-getobjectdata): `api-getobjectdata` -- [第三方个人应用发送服务窗单人消息](https://open.dingtalk.com/document/development/a-third-party-personal-application-sends-a-message-to-a-single): `a-third-party-personal-application-sends-a-message-to-a-single` -- [获取个人或企业客户查重字段](https://open.dingtalk.com/document/development/obtain-duplicate-check-fields): `obtain-duplicate-check-fields` -- [获取个人或企业客户的元数据](https://open.dingtalk.com/document/development/obtain-the-metadata-of-individual-enterprise-customers): `obtain-the-metadata-of-individual-enterprise-customers` -- [获取全量个人或企业客户数据](https://open.dingtalk.com/document/development/crm-obtains-all-private-sea-customer-data): `crm-obtains-all-private-sea-customer-data` -- [获取单个客户群组详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-single-customer-group): `queries-the-details-of-a-single-customer-group` -- [获取单个客户群详情](https://open.dingtalk.com/document/development/obtain-a-single-customer-group): `obtain-a-single-customer-group` -- [获取审批中创建与CRM客户关联的TAB表单元数据](https://open.dingtalk.com/document/development/api-getrelatedviewtabmeta): `api-getrelatedviewtabmeta` -- [获取审批里创建的与CRM客户关联的TAB表单数据实例列表](https://open.dingtalk.com/document/development/api-getrelatedviewtabdata): `api-getrelatedviewtabdata` -- [获取客户管理全局信息](https://open.dingtalk.com/document/development/get-customer-management-global-information): `get-customer-management-global-information` - -### 日历(30 条) - -- [修改日程](https://open.dingtalk.com/document/development/modify-event): `modify-event` -- [创建日程](https://open.dingtalk.com/document/development/create-event): `create-event` -- [创建订阅日历](https://open.dingtalk.com/document/development/create-subscription-calendar): `create-subscription-calendar` -- [创建访问控制](https://open.dingtalk.com/document/development/create-schedule-access-control): `create-schedule-access-control` -- [删除日程](https://open.dingtalk.com/document/development/delete-event): `delete-event` -- [删除日程参与者](https://open.dingtalk.com/document/development/delete-schedule-participant): `delete-schedule-participant` -- [删除订阅日历](https://open.dingtalk.com/document/development/delete-subscription-calendar): `delete-subscription-calendar` -- [删除访问控制](https://open.dingtalk.com/document/development/delete-an-access-control-list): `delete-an-access-control-list` -- [取消订阅公共日历](https://open.dingtalk.com/document/development/unsubscribe-from-a-public-calendar): `unsubscribe-from-a-public-calendar` -- [取消预定会议室](https://open.dingtalk.com/document/development/remove-a-meeting-room): `remove-a-meeting-room` -- [更新订阅日历](https://open.dingtalk.com/document/development/update-subscription-calendar): `update-subscription-calendar` -- [查看单个日程的签到详情](https://open.dingtalk.com/document/development/view-the-check-in-details-of-a-single-schedule): `view-the-check-in-details-of-a-single-schedule` -- [查看单个日程的签退详情](https://open.dingtalk.com/document/development/view-the-billing-details-of-a-single-schedule): `view-the-billing-details-of-a-single-schedule` -- [查询单个日程详情](https://open.dingtalk.com/document/development/query-details-about-an-event): `query-details-about-an-event` -- [查询单个订阅日历详情](https://open.dingtalk.com/document/development/query-a-single-subscription-calendar): `query-a-single-subscription-calendar` -- [查询日历](https://open.dingtalk.com/document/development/query-a-calendar): `query-a-calendar` -- [查询日程列表](https://open.dingtalk.com/document/development/query-an-event-list): `query-an-event-list` -- [查询日程视图](https://open.dingtalk.com/document/development/query-schedule-view): `query-schedule-view` -- [添加日程参与者](https://open.dingtalk.com/document/development/add-schedule-participant): `add-schedule-participant` -- [获取会议室忙闲信息](https://open.dingtalk.com/document/development/queries-free-and-busy-meeting-room-information): `queries-free-and-busy-meeting-room-information` -- [获取日程参与者](https://open.dingtalk.com/document/development/get-the-participants-of-a-schedule): `get-the-participants-of-a-schedule` -- [获取用户忙闲信息](https://open.dingtalk.com/document/development/free-schedule): `free-schedule` -- [获取签到链接](https://open.dingtalk.com/document/development/api-getsigninlink): `api-getsigninlink` -- [获取签退链接](https://open.dingtalk.com/document/development/api-getsignoutlink): `api-getsignoutlink` -- [获取访问控制列表](https://open.dingtalk.com/document/development/obtain-the-access-control-list-of-the-calendar): `obtain-the-access-control-list-of-the-calendar` -- [订阅公共日历](https://open.dingtalk.com/document/development/subscribe-to-a-public-calendar): `subscribe-to-a-public-calendar` -- [设置日程响应邀请状态](https://open.dingtalk.com/document/development/configure-response-status): `configure-response-status` -- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-single-schedule-news): `sign-in-single-schedule-news` -- [针对单个日程进行签退](https://open.dingtalk.com/document/development/sign-off-for-a-single-schedule): `sign-off-for-a-single-schedule` -- [预定会议室](https://open.dingtalk.com/document/development/add-a-meeting-room): `add-a-meeting-room` - -### 视频会议(30 条) - -- [停止视频会议云录制](https://open.dingtalk.com/document/development/video-conferencing-stops-cloud-recording): `video-conferencing-stops-cloud-recording` -- [停止视频会议直播推流](https://open.dingtalk.com/document/development/videoconferencing-stops-live-stream-ingest): `videoconferencing-stops-live-stream-ingest` -- [全员静音或全员取消静音](https://open.dingtalk.com/document/development/mute-all-staff-or-unmute-all-staff): `mute-all-staff-or-unmute-all-staff` -- [关闭视频会议](https://open.dingtalk.com/document/development/close-audio-video-conference): `close-audio-video-conference` -- [创建用户专属短链](https://open.dingtalk.com/document/development/api-createcustomshortlink): `api-createcustomshortlink` -- [创建视频会议](https://open.dingtalk.com/document/development/create-a-video-conference): `create-a-video-conference` -- [创建预约会议](https://open.dingtalk.com/document/development/create-appointment-meeting): `create-appointment-meeting` -- [取消预约会议](https://open.dingtalk.com/document/development/cancel-appointment-meeting): `cancel-appointment-meeting` -- [开启视频会议云录制](https://open.dingtalk.com/document/development/video-conference-open-cloud-recording): `video-conference-open-cloud-recording` -- [开启视频会议直播推流](https://open.dingtalk.com/document/development/video-conference-enables-live-stream-ingest): `video-conference-enables-live-stream-ingest` -- [批量查询视频会议信息](https://open.dingtalk.com/document/development/batch-query-of-video-conference-information): `batch-query-of-video-conference-information` -- [指定人员静音或取消静音](https://open.dingtalk.com/document/development/specify-person-to-mute-or-unmute): `specify-person-to-mute-or-unmute` -- [更新预约会议](https://open.dingtalk.com/document/development/update-appointment-meeting): `update-appointment-meeting` -- [更新预约会议设置](https://open.dingtalk.com/document/development/api-updatescheduleconfsettings): `api-updatescheduleconfsettings` -- [查询企业进行中会议列表](https://open.dingtalk.com/document/development/api-queryorgconferencelist): `api-queryorgconferencelist` -- [查询会议录制中的文本信息](https://open.dingtalk.com/document/development/queries-the-text-information-about-cloud-recording): `queries-the-text-information-about-cloud-recording` -- [查询会议录制中的视频信息](https://open.dingtalk.com/document/development/queries-the-playback-information-about-a-recorded-cloud-video): `queries-the-playback-information-about-a-recorded-cloud-video` -- [查询会议录制的详情信息](https://open.dingtalk.com/document/development/query-recording-information): `query-recording-information` -- [查询用户进行中会议列表](https://open.dingtalk.com/document/development/api-queryuserongoingconference): `api-queryuserongoingconference` -- [查询视频会议信息](https://open.dingtalk.com/document/development/querying-video-conference-information): `querying-video-conference-information` -- [查询视频会议成员](https://open.dingtalk.com/document/development/querying-video-conference-members): `querying-video-conference-members` -- [查询预约会议](https://open.dingtalk.com/document/development/query-meeting-reservation): `query-meeting-reservation` -- [查询预约会议历史会议信息](https://open.dingtalk.com/document/development/query-appointment-meeting-history-meeting-information): `query-appointment-meeting-history-meeting-information` -- [查询预约会议设置](https://open.dingtalk.com/document/development/api-queryscheduleconfsettings): `api-queryscheduleconfsettings` -- [根据会议号查询会议信息](https://open.dingtalk.com/document/development/api-queryconferenceinfobyroomcode): `api-queryconferenceinfobyroomcode` -- [设置全员看他](https://open.dingtalk.com/document/development/set-the-whole-staff-to-see-him): `set-the-whole-staff-to-see-him` -- [设置联席主持人](https://open.dingtalk.com/document/development/set-up-co-hosts): `set-up-co-hosts` -- [踢出会议成员](https://open.dingtalk.com/document/development/kick-out-meeting-members): `kick-out-meeting-members` -- [邀请用户入会](https://open.dingtalk.com/document/development/invite-users-to-join): `invite-users-to-join` -- [锁定会议](https://open.dingtalk.com/document/development/api-lockconference): `api-lockconference` - -### 阿里商旅(25 条) - -- [修改成本中心](https://open.dingtalk.com/document/development/modify-basic-cost-center-information): `modify-basic-cost-center-information` -- [关联单号查询相关订单信息列表](https://open.dingtalk.com/document/development/related-order-information): `related-order-information` -- [创建日志](https://open.dingtalk.com/document/development/create-a-log): `create-a-log` -- [同步市内用车申请单](https://open.dingtalk.com/document/development/synchronize-third-party-city-vehicle-approval-form): `synchronize-third-party-city-vehicle-approval-form` -- [回传第三方超标审批结果](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-sync): `dingtalk-oapi-alitrip-btrip-exceedapply-sync` -- [审批市内用车申请单](https://open.dingtalk.com/document/development/approval-of-third-party-city-car-application-form): `approval-of-third-party-city-car-application-form` -- [搜索第三方机票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-flight): `dingtalk-oapi-alitrip-btrip-exceedapply-flight` -- [搜索第三方火车票超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-train-get): `dingtalk-oapi-alitrip-btrip-exceedapply-train-get` -- [搜索第三方酒店超标审批单](https://open.dingtalk.com/document/development/dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get): `dingtalk-oapi-alitrip-btrip-exceedapply-hotel-get` -- [新建审批单](https://open.dingtalk.com/document/development/user-new-approval-form): `user-new-approval-form` -- [新建成本中心](https://open.dingtalk.com/document/development/new-cost-center): `new-cost-center` -- [查询商旅火车票结算记账数据](https://open.dingtalk.com/document/development/business-travel-train-ticket-settlement-bookkeeping-query-interface): `business-travel-train-ticket-settlement-bookkeeping-query-interface` -- [查询市内用车申请单](https://open.dingtalk.com/document/development/query-the-application-form-for-third-party-vehicles-in-the-city): `query-the-application-form-for-third-party-vehicles-in-the-city` -- [查询成员排班信息](https://open.dingtalk.com/document/development/query-scheduling-for-a-day): `query-scheduling-for-a-day` -- [查询机票结算记账数据](https://open.dingtalk.com/document/development/ticket-settlement-bookkeeping-query-interface): `ticket-settlement-bookkeeping-query-interface` -- [查询用车结算记账记录](https://open.dingtalk.com/document/development/query-interface-for-vehicle-settlement-and-bookkeeping): `query-interface-for-vehicle-settlement-and-bookkeeping` -- [查询酒店结算记账数据](https://open.dingtalk.com/document/development/hotel-settlement-bookkeeping-query-interface): `hotel-settlement-bookkeeping-query-interface` -- [获取企业机票订单数据](https://open.dingtalk.com/document/development/obtains-enterprise-ticket-order-data): `obtains-enterprise-ticket-order-data` -- [获取商旅访问地址](https://open.dingtalk.com/document/development/obtain-business-travel-access-addresses): `obtain-business-travel-access-addresses` -- [获取打卡结果](https://open.dingtalk.com/document/development/open-attendance-clock-in-data): `open-attendance-clock-in-data` -- [获取打卡详情](https://open.dingtalk.com/document/development/attendance-clock-in-record-is-open): `attendance-clock-in-record-is-open` -- [获取日志评论详情](https://open.dingtalk.com/document/development/queries-log-comment-details): `queries-log-comment-details` -- [获取月对账结算数据](https://open.dingtalk.com/document/development/obtain-monthly-reconciliation-settlement-data): `obtain-monthly-reconciliation-settlement-data` -- [获取用车订单数据](https://open.dingtalk.com/document/development/vehicle-order-query-interface): `vehicle-order-query-interface` -- [设置成本中心人员信息](https://open.dingtalk.com/document/development/set-up-cost-center-personnel-information): `set-up-cost-center-personnel-information` - -### Agoal 目标(23 条) - -- [Agoal业务数据查询](https://open.dingtalk.com/document/development/agoal-business-biz-data-query): `agoal-business-biz-data-query` -- [创建业务实体](https://open.dingtalk.com/document/development/api-agoalentitycreate): `api-agoalentitycreate` -- [创建目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskcreate): `api-agoalperftaskcreate` -- [更新业务实体](https://open.dingtalk.com/document/development/api-agoalentityupdate): `api-agoalentityupdate` -- [更新目标规则下的考核任务](https://open.dingtalk.com/document/development/api-agoalperftaskupdate): `api-agoalperftaskupdate` -- [查询企业下个人目标详情](https://open.dingtalk.com/document/development/api-getobjectivedetail): `api-getobjectivedetail` -- [查询企业下单个目标规则详情](https://open.dingtalk.com/document/development/api-getobjectiveruledetail): `api-getobjectiveruledetail` -- [查询企业下指定个人目标的所有进展](https://open.dingtalk.com/document/development/api-agoalobjectiveprogresslist): `api-agoalobjectiveprogresslist` -- [查询企业下的所有考核计划](https://open.dingtalk.com/document/development/api-agoalorgperfplanquery): `api-agoalorgperfplanquery` -- [查询企业下目标规则列表](https://open.dingtalk.com/document/development/api-agoalobjectiverulelist): `api-agoalobjectiverulelist` -- [查询某个考核计划的部门得分](https://open.dingtalk.com/document/development/api-agoalorgperfdocquery): `api-agoalorgperfdocquery` -- [查询组织目标详情](https://open.dingtalk.com/document/development/api-agoalorgobjectivequery): `api-agoalorgobjectivequery` -- [获取 Agoal 组织目标列表](https://open.dingtalk.com/document/development/api-agoalorgobjectivelist): `api-agoalorgobjectivelist` -- [获取Agoal指定目标规则下的周期列表](https://open.dingtalk.com/document/development/api-agoalobjectiveruleperiodlist): `api-agoalobjectiveruleperiodlist` -- [获取Agoal指定组织下的所有目标规则列表](https://open.dingtalk.com/document/development/api-agoalorgobjectiverulelist): `api-agoalorgobjectiverulelist` -- [获取Agoal指定规则周期下负责人的目标列表](https://open.dingtalk.com/document/development/api-agoaluserobjectivelist): `api-agoaluserobjectivelist` -- [获取Agoal指定部门下的计分卡维度和指标id](https://open.dingtalk.com/document/development/api-getdeptscorecardindicator): `api-getdeptscorecardindicator` -- [获取Agoal用户管理员列表](https://open.dingtalk.com/document/development/api-agoaluseradminlist): `api-agoaluseradminlist` -- [获取Agoal目标或关键结果关联的关键行动](https://open.dingtalk.com/document/development/api-agoalobjectivekeyactionlist): `api-agoalobjectivekeyactionlist` -- [获取计分卡指标详情](https://open.dingtalk.com/document/development/api-getindicatordetail): `api-getindicatordetail` -- [通过Agoal系统账号发送消息](https://open.dingtalk.com/document/development/api-agoalsendmessage): `api-agoalsendmessage` -- [通过指标编码批量查询指标列表](https://open.dingtalk.com/document/development/api-agoalindicatorbatchquery): `api-agoalindicatorbatchquery` -- [通过指标编码推送指标时间维度数据](https://open.dingtalk.com/document/development/api-agoalindicatordatapush): `api-agoalindicatordatapush` - -### 教育(23 条) - -- [修改用户成员类型](https://open.dingtalk.com/document/development/api-updatecollegeuseremptype): `api-updatecollegeuseremptype` -- [创建个人账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactuser): `api-addcollegecontactuser` -- [创建组织单元](https://open.dingtalk.com/document/development/api-createcollegecontactdept): `api-createcollegecontactdept` -- [创建自定义校区或部门](https://open.dingtalk.com/document/development/create-a-custom-campus-or-department): `create-a-custom-campus-or-department` -- [创建自定义部门下的班级](https://open.dingtalk.com/document/development/create-classes-in-a-custom-department): `create-classes-in-a-custom-department` -- [创建高校账号用户](https://open.dingtalk.com/document/development/api-addcollegecontactexclusive): `api-addcollegecontactexclusive` -- [删除学生](https://open.dingtalk.com/document/development/delete-student): `delete-student` -- [删除家校部门](https://open.dingtalk.com/document/development/delete-home-school-department): `delete-home-school-department` -- [删除家长关系](https://open.dingtalk.com/document/development/delete-parent-relationship): `delete-parent-relationship` -- [删除老师](https://open.dingtalk.com/document/development/delete-teacher): `delete-teacher` -- [判断用户是否是认证组织的语文老师接口](https://open.dingtalk.com/document/development/api-isyuwencertifiedteacher): `api-isyuwencertifiedteacher` -- [学生调班](https://open.dingtalk.com/document/development/shift-students): `shift-students` -- [更新个人账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactuser): `api-updatecollegecontactuser` -- [更新学生](https://open.dingtalk.com/document/development/api-updatestudent): `api-updatestudent` -- [更新家长](https://open.dingtalk.com/document/development/api-updateguardian): `api-updateguardian` -- [更新班级](https://open.dingtalk.com/document/development/api-updateclass): `api-updateclass` -- [更新组织单元](https://open.dingtalk.com/document/development/api-updatecollegecontactdept): `api-updatecollegecontactdept` -- [更新高校账号用户信息](https://open.dingtalk.com/document/development/api-updatecollegecontactexclusive): `api-updatecollegecontactexclusive` -- [查询用户信息详情](https://open.dingtalk.com/document/development/api-querycollegecontactuserdetail): `api-querycollegecontactuserdetail` -- [获取子组织单元列表](https://open.dingtalk.com/document/development/api-listcollegecontactsubdepts): `api-listcollegecontactsubdepts` -- [获取组织单元支持的部门类型](https://open.dingtalk.com/document/development/api-listcollegecontactdepttypeconfig): `api-listcollegecontactdepttypeconfig` -- [获取组织单元详情](https://open.dingtalk.com/document/development/api-getcollegecontactdeptdetail): `api-getcollegecontactdeptdetail` -- [获取行政组织架构部门详情](https://open.dingtalk.com/document/development/api-getcollegecontactstandardstrudeptdetail): `api-getcollegecontactstandardstrudeptdetail` - -### 智能人事(17 条) - -- [修改已离职员工信息](https://open.dingtalk.com/document/development/modify-resigned-employee-information): `modify-resigned-employee-information` -- [员工加入待离职](https://open.dingtalk.com/document/development/api-empstartdismission): `api-empstartdismission` -- [批量获取员工离职信息](https://open.dingtalk.com/document/development/obtain-resignation-information-of-employees-new-version): `obtain-resignation-information-of-employees-new-version` -- [撤销员工待离职](https://open.dingtalk.com/document/development/api-revoketermination): `api-revoketermination` -- [新增或删除花名册选项类型字段的选项](https://open.dingtalk.com/document/development/intelligent-personnel-roster-field-option-modification): `intelligent-personnel-roster-field-option-modification` -- [智能人事员工调岗](https://open.dingtalk.com/document/development/intelligent-personnel-staff-transfer): `intelligent-personnel-staff-transfer` -- [智能人事员工转正](https://open.dingtalk.com/document/development/intelligent-personnel-staff-to-become-regular): `intelligent-personnel-staff-to-become-regular` -- [更新待离职员工离职信息](https://open.dingtalk.com/document/development/api-updateempdismissioninfo): `api-updateempdismissioninfo` -- [查询花名册中有权限的字段列表](https://open.dingtalk.com/document/development/query-the-list-of-fields-with-permissions-in-the-roster): `query-the-list-of-fields-with-permissions-in-the-roster` -- [添加待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-supports-system-and-custom-fields): `add-employees-to-be-hired-supports-system-and-custom-fields` -- [确认员工离职并删除](https://open.dingtalk.com/document/development/api-hrmprocessterminationandhandover): `api-hrmprocessterminationandhandover` -- [获取企业已有的所有离职原因](https://open.dingtalk.com/document/development/api-getalldismissionreasons): `api-getalldismissionreasons` -- [获取企业职位列表](https://open.dingtalk.com/document/development/obtain-enterprise-position-information): `obtain-enterprise-position-information` -- [获取企业职务列表](https://open.dingtalk.com/document/development/obtain-enterprise-title-information): `obtain-enterprise-title-information` -- [获取企业职级列表](https://open.dingtalk.com/document/development/obtain-enterprise-rank-information): `obtain-enterprise-rank-information` -- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/api-getemployeerosterbyfield): `api-getemployeerosterbyfield` -- [获取离职员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-who-have-left): `obtain-the-list-of-employees-who-have-left` - -### 机器人(16 条) - -- [下载机器人接收消息的文件内容](https://open.dingtalk.com/document/development/download-the-file-content-of-the-robot-receiving-message): `download-the-file-content-of-the-robot-receiving-message` -- [人与人会话中机器人发送普通消息](https://open.dingtalk.com/document/development/the-robot-sends-ordinary-messages-in-a-person-to-person-conversation): `the-robot-sends-ordinary-messages-in-a-person-to-person-conversation` -- [企业机器人撤回内部群消息](https://open.dingtalk.com/document/development/enterprise-chatbot-withdraws-internal-group-messages): `enterprise-chatbot-withdraws-internal-group-messages` -- [发送DING消息](https://open.dingtalk.com/document/development/robot-sends-nail-message): `robot-sends-nail-message` -- [批量发送人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/chatbots-send-one-on-one-chat-messages-in-batches): `chatbots-send-one-on-one-chat-messages-in-batches` -- [批量撤回人与人会话中机器人消息](https://open.dingtalk.com/document/development/batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations): `batch-withdrawal-of-single-chat-robot-messages-in-person-to-person-conversations` -- [批量撤回人与机器人会话中机器人消息](https://open.dingtalk.com/document/development/batch-message-recall-chat): `batch-message-recall-chat` -- [批量查询人与机器人会话机器人消息是否已读](https://open.dingtalk.com/document/development/chatbot-batch-query-the-read-status-of-messages): `chatbot-batch-query-the-read-status-of-messages` -- [撤回已经发送的DING消息](https://open.dingtalk.com/document/development/robot-withdraws-pin-message): `robot-withdraws-pin-message` -- [机器人发送群聊消息](https://open.dingtalk.com/document/development/the-robot-sends-a-group-message): `the-robot-sends-a-group-message` -- [查询人与人会话中机器人消息已读列表](https://open.dingtalk.com/document/development/query-the-read-list-of-robot-messages-in-person-to-person-conversations): `query-the-read-list-of-robot-messages-in-person-to-person-conversations` -- [查询企业机器人群聊消息用户已读状态](https://open.dingtalk.com/document/development/chatbot-queries-the-read-status-of-a-message): `chatbot-queries-the-read-status-of-a-message` -- [查询单聊机器人的快捷入口](https://open.dingtalk.com/document/development/quick-entrance-of-inquiry-single-chat-robot): `quick-entrance-of-inquiry-single-chat-robot` -- [清空单聊机器人快捷入口](https://open.dingtalk.com/document/development/clear-single-chat-robot-quick-entry): `clear-single-chat-robot-quick-entry` -- [获取群内机器人列表](https://open.dingtalk.com/document/development/obtain-the-list-of-robots-in-the-group): `obtain-the-list-of-robots-in-the-group` -- [设置单聊机器人快捷入口](https://open.dingtalk.com/document/development/set-robot-quick-entrance): `set-robot-quick-entrance` - -### 简知 CRM(15 条) - -- [产品信息](https://open.dingtalk.com/document/development/add-or-edit-product-information): `add-or-edit-product-information` -- [入库单](https://open.dingtalk.com/document/development/add-or-edit-a-shipment-record): `add-or-edit-a-shipment-record` -- [出库单](https://open.dingtalk.com/document/development/add-or-edit-an-issue-ticket): `add-or-edit-an-issue-ticket` -- [发货单](https://open.dingtalk.com/document/development/add-or-edit-invoices): `add-or-edit-invoices` -- [合同订单](https://open.dingtalk.com/document/development/add-or-edit-contract-orders): `add-or-edit-contract-orders` -- [客户公共池](https://open.dingtalk.com/document/development/add-or-edit-customer-public-pools): `add-or-edit-customer-public-pools` -- [客户资料](https://open.dingtalk.com/document/development/add-or-edit-customer-profile): `add-or-edit-customer-profile` -- [报价记录](https://open.dingtalk.com/document/development/add-or-edit-quotation-records): `add-or-edit-quotation-records` -- [生产单](https://open.dingtalk.com/document/development/add-or-edit-a-production-order): `add-or-edit-a-production-order` -- [联系人](https://open.dingtalk.com/document/development/add-or-edit-contacts): `add-or-edit-contacts` -- [获取数据列表](https://open.dingtalk.com/document/development/obtain-the-data-list): `obtain-the-data-list` -- [获取数据详情](https://open.dingtalk.com/document/development/queries-data-details): `queries-data-details` -- [采购单](https://open.dingtalk.com/document/development/edit-purchase-order): `edit-purchase-order` -- [销售换货单](https://open.dingtalk.com/document/development/add-or-edit-a-sales-order): `add-or-edit-a-sales-order` -- [销售机会](https://open.dingtalk.com/document/development/add-or-edit-opportunities): `add-or-edit-opportunities` - -### 考勤(15 条) - -- [分页获取加班规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-overtime-rules-by-page): `retrieve-a-list-of-overtime-rules-by-page` -- [分页获取补卡规则列表](https://open.dingtalk.com/document/development/retrieve-a-list-of-replenishment-rules-by-page): `retrieve-a-list-of-replenishment-rules-by-page` -- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/batch-query-employee-leave-balance-change-record): `batch-query-employee-leave-balance-change-record` -- [批量获取加班规则设置](https://open.dingtalk.com/document/development/batch-retrieve-overtime-rules): `batch-retrieve-overtime-rules` -- [更新假期规则](https://open.dingtalk.com/document/development/update-holiday-rules): `update-holiday-rules` -- [查询指定用户的封账规则](https://open.dingtalk.com/document/development/encapsulate-account-sealing-and-unsealing-rules): `encapsulate-account-sealing-and-unsealing-rules` -- [查询用户某段时间内是否处于封账状态](https://open.dingtalk.com/document/development/checks-whether-a-user-has-blocked-accounts-within-a-specified): `checks-whether-a-user-has-blocked-accounts-within-a-specified` -- [查询用户考勤节假日信息](https://open.dingtalk.com/document/development/obtain-user-attendance-and-holiday-information): `obtain-user-attendance-and-holiday-information` -- [查询考勤写操作权限](https://open.dingtalk.com/document/development/attendance-writing-operation-is-brand-new-query): `attendance-writing-operation-is-brand-new-query` -- [查询考勤机信息](https://open.dingtalk.com/document/development/query-attendance-machine-information): `query-attendance-machine-information` -- [根据设备ID获取员工信息](https://open.dingtalk.com/document/development/obtain-information-about-employees-based-on-device-ids): `obtain-information-about-employees-based-on-device-ids` -- [添加假期规则](https://open.dingtalk.com/document/development/add-holiday-rules): `add-holiday-rules` -- [通知审批通过](https://open.dingtalk.com/document/development/api-processapprovefinish): `api-processapprovefinish` -- [配置考勤排班附加信息](https://open.dingtalk.com/document/development/synchronization-scheduling-information): `synchronization-scheduling-information` -- [预计算时长](https://open.dingtalk.com/document/development/api-calculateduration): `api-calculateduration` - -### 登录授权(11 条) - -- [查询个人授权记录](https://open.dingtalk.com/document/development/query-personal-authorization-records): `query-personal-authorization-records` -- [获取jsapiTicket](https://open.dingtalk.com/document/development/create-a-jsapi-ticket): `create-a-jsapi-ticket` -- [获取企业内部应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app): `obtain-the-access-token-of-an-internal-app` -- [获取企业开通应用后的授权信息](https://open.dingtalk.com/document/development/obtains-the-authorization-information-after-the-enterprise-activates-the-application-1): `obtains-the-authorization-information-after-the-enterprise-activates-the-application-1` -- [获取定制应用的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-third-party-application-authorization-enterprise): `obtain-the-access-token-of-the-third-party-application-authorization-enterprise` -- [获取应用的 Access Token](https://open.dingtalk.com/document/development/api-gettoken): `api-gettoken` -- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/obtains-the-identity-of-an-application-administrator): `obtains-the-identity-of-an-application-administrator` -- [获取微应用后台免登的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-micro-application-background-without-log-on): `obtain-the-access-token-of-the-micro-application-background-without-log-on` -- [获取用户token](https://open.dingtalk.com/document/development/obtain-user-token): `obtain-user-token` -- [获取第三方企业应用的suiteAccessToken](https://open.dingtalk.com/document/development/obtains-the-suite-acess-token-of-third-party-enterprise-applications): `obtains-the-suite-acess-token-of-third-party-enterprise-applications` -- [获取第三方应用授权企业的accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-authorized-enterprise-1): `obtain-the-access-token-of-the-authorized-enterprise-1` - -### 知识库(11 条) - -- [匹配文本中的词条](https://open.dingtalk.com/document/development/enterprise-encyclopedia-match-entries-in-a-text): `enterprise-encyclopedia-match-entries-in-a-text` -- [批量获取知识库](https://open.dingtalk.com/document/development/batch-acquisition-of-knowledge-base): `batch-acquisition-of-knowledge-base` -- [批量获取节点](https://open.dingtalk.com/document/development/obtain-nodes-in-batch): `obtain-nodes-in-batch` -- [新建知识库](https://open.dingtalk.com/document/development/new-knowledge-base): `new-knowledge-base` -- [查询词条详情](https://open.dingtalk.com/document/development/enterprise-encyclopedia-query-entry-details-by-entry-name): `enterprise-encyclopedia-query-entry-details-by-entry-name` -- [获取我的文档知识库信息](https://open.dingtalk.com/document/development/get-my-documents): `get-my-documents` -- [获取知识库](https://open.dingtalk.com/document/development/obtain-the-knowledge-base): `obtain-the-knowledge-base` -- [获取知识库列表](https://open.dingtalk.com/document/development/get-knowledge-base-list): `get-knowledge-base-list` -- [获取节点](https://open.dingtalk.com/document/development/get-knowledge-base-acquisition-node): `get-knowledge-base-acquisition-node` -- [获取节点列表](https://open.dingtalk.com/document/development/get-node-list): `get-node-list` -- [通过链接获取节点](https://open.dingtalk.com/document/development/get-node-by-link): `get-node-by-link` - -### 服务群(8 条) - -- [创建场景服务群](https://open.dingtalk.com/document/development/create-a-scenario-service-group): `create-a-scenario-service-group` -- [升级云客服服务群为钉钉智能服务群](https://open.dingtalk.com/document/development/upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent): `upgraded-the-cloud-customer-service-group-to-the-dingtalk-intelligent` -- [升级普通群为服务群](https://open.dingtalk.com/document/development/a-dingtalk-group-is-upgraded-to-one-of-the-intelligent): `a-dingtalk-group-is-upgraded-to-one-of-the-intelligent` -- [发送服务群消息](https://open.dingtalk.com/document/development/service-group-message-sending-interface): `service-group-message-sending-interface` -- [更换服务群所在的群分组](https://open.dingtalk.com/document/development/modify-a-service-group): `modify-a-service-group` -- [查询服务群活跃用户](https://open.dingtalk.com/document/development/queries-active-service-users): `queries-active-service-users` -- [添加服务群成员](https://open.dingtalk.com/document/development/add-service-group-members): `add-service-group-members` -- [群发任务](https://open.dingtalk.com/document/development/service-group-sending-task-interface): `service-group-sending-task-interface` - -### Team 空间(7 条) - -- [创建协作空间](https://open.dingtalk.com/document/development/api-createprojectv3): `api-createprojectv3` -- [创建协作空间任务](https://open.dingtalk.com/document/development/api-createtask): `api-createtask` -- [创建自由任务](https://open.dingtalk.com/document/development/api-createorganizationtask): `api-createorganizationtask` -- [更新协作空间](https://open.dingtalk.com/document/development/api-updateprojectv3): `api-updateprojectv3` -- [查询任务详情](https://open.dingtalk.com/document/development/api-queryalltask): `api-queryalltask` -- [获取协作空间列表](https://open.dingtalk.com/document/development/api-searchprojectsv3-1): `api-searchprojectsv3-1` -- [获取用户参与项目](https://open.dingtalk.com/document/development/api-getuserjoinedprojectsv3): `api-getuserjoinedprojectsv3` - -### 智能招聘(7 条) - -- [根据手机号获取候选人信息](https://open.dingtalk.com/document/development/obtain-candidate-information-based-on-mobile-phone-number): `obtain-candidate-information-based-on-mobile-phone-number` -- [添加智能招聘文件到钉盘](https://open.dingtalk.com/document/development/add-nail-disk-file): `add-nail-disk-file` -- [确认完成权益的更新](https://open.dingtalk.com/document/development/confirm-benefits): `confirm-benefits` -- [获取候选人的面试信息](https://open.dingtalk.com/document/development/query-the-interview-list): `query-the-interview-list` -- [获取招聘流程标识](https://open.dingtalk.com/document/development/get-recruitment-process-identity): `get-recruitment-process-identity` -- [获取智能招聘文件上传信息](https://open.dingtalk.com/document/development/obtain-information-about-the-dingtalk-disk-upload-file): `obtain-information-about-the-dingtalk-disk-upload-file` -- [通知完成指定的新手任务](https://open.dingtalk.com/document/development/notify-the-completion-of-the-specified-novice-task): `notify-the-completion-of-the-specified-novice-task` - -### 财务(7 条) - -- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-user-code-instance): `create-a-user-code-instance` -- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/sync-pin-badge-code-verification-result): `sync-pin-badge-code-verification-result` -- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-user-code-instance): `update-user-code-instance` -- [解码钉工牌电子码](https://open.dingtalk.com/document/development/decoding-dingtalk-payment-code): `decoding-dingtalk-payment-code` -- [通知支付结果](https://open.dingtalk.com/document/development/notify-dingtalk-payment-code-payment-result): `notify-dingtalk-payment-code-payment-result` -- [通知退款结果](https://open.dingtalk.com/document/development/dingtalk-payment-code-refund-information-synchronization-operation): `dingtalk-payment-code-refund-information-synchronization-operation` -- [配置企业钉工牌](https://open.dingtalk.com/document/development/set-up-enterprise-payment-code-configuration-interface): `set-up-enterprise-payment-code-configuration-interface` - -### 待办(6 条) - -- [创建钉钉个人待办任务](https://open.dingtalk.com/document/development/api-createpersonaltodotask): `api-createpersonaltodotask` -- [创建钉钉待办任务](https://open.dingtalk.com/document/development/add-dingtalk-to-do-task): `add-dingtalk-to-do-task` -- [删除钉钉待办任务](https://open.dingtalk.com/document/development/delete-dingtalk-to-do-tasks): `delete-dingtalk-to-do-tasks` -- [更新钉钉待办任务](https://open.dingtalk.com/document/development/updates-dingtalk-to-do-tasks): `updates-dingtalk-to-do-tasks` -- [更新钉钉待办执行者状态](https://open.dingtalk.com/document/development/update-dingtalk-to-do-status): `update-dingtalk-to-do-status` -- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/query-the-to-do-list-of-enterprise-users): `query-the-to-do-list-of-enterprise-users` - -### 百科问答(6 条) - -- [分页获取企业词条信息](https://open.dingtalk.com/document/development/entry-search): `entry-search` -- [删除词条](https://open.dingtalk.com/document/development/delete-entry): `delete-entry` -- [审核词条](https://open.dingtalk.com/document/development/review-entries): `review-entries` -- [新增词条](https://open.dingtalk.com/document/development/new-entry): `new-entry` -- [更新词条](https://open.dingtalk.com/document/development/update-entry): `update-entry` -- [根据词条ID查询详情](https://open.dingtalk.com/document/development/query-entry): `query-entry` - -### 直播(6 条) - -- [修改培训课程](https://open.dingtalk.com/document/development/modify-the-basic-information-of-a-live-streaming-course): `modify-the-basic-information-of-a-live-streaming-course` -- [修改直播属性信息](https://open.dingtalk.com/document/development/modify-live-streaming): `modify-live-streaming` -- [创建直播](https://open.dingtalk.com/document/development/create-live-streaming): `create-live-streaming` -- [删除直播](https://open.dingtalk.com/document/development/delete-live-streaming): `delete-live-streaming` -- [查询直播信息](https://open.dingtalk.com/document/development/queries-the-live-streaming-information): `queries-the-live-streaming-information` -- [查询直播观看人员信息](https://open.dingtalk.com/document/development/queries-the-viewing-information-of-viewers): `queries-the-viewing-information-of-viewers` - -### 行业(6 条) - -- [保存人员扩展属性](https://open.dingtalk.com/document/development/personnel-extension-property-error): `personnel-extension-property-error` -- [创建园区项目](https://open.dingtalk.com/document/development/create-a-campus-project): `create-a-campus-project` -- [创建项目组](https://open.dingtalk.com/document/development/create-a-project-group): `create-a-project-group` -- [删除项目组](https://open.dingtalk.com/document/development/delete-the-project-group-team): `delete-the-project-group-team` -- [查询园区项目信息](https://open.dingtalk.com/document/development/query-a-project-in-a-specified-campus): `query-a-project-in-a-specified-campus` -- [查询项目组信息](https://open.dingtalk.com/document/development/query-a-project-group-in-the-specified-park): `query-a-project-group-in-the-specified-park` - -### 居民服务(5 条) - -- [分页查询居民积分流水](https://open.dingtalk.com/document/development/query-the-integral-flow-records-by-page): `query-the-integral-flow-records-by-page` -- [增加或减少居民积分](https://open.dingtalk.com/document/development/increase-or-decrease-resident-points): `increase-or-decrease-resident-points` -- [查询组织维度配置的所有积分规则](https://open.dingtalk.com/document/development/query-all-credit-rules): `query-all-credit-rules` -- [获取用户所在的行业角色信息](https://open.dingtalk.com/document/development/obtain-the-industry-role-information-of-the-user): `obtain-the-industry-role-information-of-the-user` -- [获取行业角色下的用户列表](https://open.dingtalk.com/document/development/obtains-a-list-of-users-under-an-industry-role): `obtains-a-list-of-users-under-an-industry-role` - -### 组织文化(5 条) - -- [创建荣誉勋章模板](https://open.dingtalk.com/document/development/create-medal-of-honor-template): `create-medal-of-honor-template` -- [撤销员工获得的荣誉勋章](https://open.dingtalk.com/document/development/revoke-an-employee-s-medal-of-honor): `revoke-an-employee-s-medal-of-honor` -- [查询员工已获得的组织荣誉](https://open.dingtalk.com/document/development/check-the-honors-that-an-employee-has-received): `check-the-honors-that-an-employee-has-received` -- [查询当前企业下可颁发的荣誉列表](https://open.dingtalk.com/document/development/query-the-list-of-honors-that-can-be-issued-under): `query-the-list-of-honors-that-can-be-issued-under` -- [给员工颁发荣誉](https://open.dingtalk.com/document/development/award-of-honor): `award-of-honor` - -### 钉密(5 条) - -- [小蜜客服机器人消息回复](https://open.dingtalk.com/document/development/xiaomi-customer-service-robot-message-reply): `xiaomi-customer-service-robot-message-reply` -- [推送小蜜机器人单聊O2O消息](https://open.dingtalk.com/document/development/push-xiaomi-customer-service-robot-single-chat-message): `push-xiaomi-customer-service-robot-single-chat-message` -- [智能问答](https://open.dingtalk.com/document/development/alimebot-intelligent-q-a-interface): `alimebot-intelligent-q-a-interface` -- [查询机器人基础指标数据](https://open.dingtalk.com/document/development/query-robot-data-indicators): `query-robot-data-indicators` -- [获取用户登录凭证](https://open.dingtalk.com/document/development/obtains-the-user-login-credential-of-the-third-party-system-of): `obtains-the-user-login-credential-of-the-third-party-system-of` - -### 云盘(4 条) - -- [删除空间](https://open.dingtalk.com/document/development/delete-a-space): `delete-a-space` -- [新建空间](https://open.dingtalk.com/document/development/new-space): `new-space` -- [根据spaceId获取指定空间信息](https://open.dingtalk.com/document/development/retrieves-the-space-list-on-the-management-side): `retrieves-the-space-list-on-the-management-side` -- [获取空间列表](https://open.dingtalk.com/document/development/queries-a-space-list): `queries-a-space-list` - -### 会话文件(4 条) - -- [以应用身份发送文件给指定用户](https://open.dingtalk.com/document/development/sends-a-storage-file-to-a-specified-user): `sends-a-storage-file-to-a-specified-user` -- [发送文件到指定会话](https://open.dingtalk.com/document/development/send-file-to-specified-session): `send-file-to-specified-session` -- [发送文件链接到指定会话](https://open.dingtalk.com/document/development/send-a-file-link-to-the-specified-session): `send-a-file-link-to-the-specified-session` -- [获取群存储空间信息](https://open.dingtalk.com/document/development/obtain-group-storage-space-information): `obtain-group-storage-space-information` - -### 应用市场(4 条) - -- [内购商品订单处理完成](https://open.dingtalk.com/document/development/internal-purchase-order-processing-completed): `internal-purchase-order-processing-completed` -- [查询应用市场订单详情](https://open.dingtalk.com/document/development/check-the-order-details-app-store): `check-the-order-details-app-store` -- [获取个人应用内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-sku-page-address-of-goods-purchased-in-personal): `obtain-the-sku-page-address-of-goods-purchased-in-personal` -- [获取未处理的已支付订单](https://open.dingtalk.com/document/development/obtaining-isv-unfinished-processing-order): `obtaining-isv-unfinished-processing-order` - -### 公告(3 条) - -- [查询公告已读未读人员列表](https://open.dingtalk.com/document/development/query-bulletin-read-unread-persons-list): `query-bulletin-read-unread-persons-list` -- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-get-blackboard): `obtains-the-details-get-blackboard` -- [获取公告钉盘空间信息](https://open.dingtalk.com/document/development/obtain-bulletin-nail-disk-space-information): `obtain-bulletin-nail-disk-space-information` - -### 制造(2 条) - -- [查询计件报工数据](https://open.dingtalk.com/document/development/riqing-monthly-settlement-query-interface-for-piece-rate-reporting): `riqing-monthly-settlement-query-interface-for-piece-rate-reporting` -- [计件报工](https://open.dingtalk.com/document/development/riqing-monthly-settlement-piece-rate-reporting-interface): `riqing-monthly-settlement-piece-rate-reporting-interface` - -### 碳能力(2 条) - -- [写入每日用户碳数据明细信息](https://open.dingtalk.com/document/development/write-in-the-detailed-information-of-daily-user-carbon-data): `write-in-the-detailed-information-of-daily-user-carbon-data` -- [写入每日组织碳数据明细信息](https://open.dingtalk.com/document/development/third-party-applications-write-daily-organizational-carbon-data-details-1): `third-party-applications-write-daily-organizational-carbon-data-details-1` - -### Agoal(1 条) - -- [查询假期余额](https://open.dingtalk.com/document/development/query-holiday-balance): `query-holiday-balance` - -### 会员(1 条) - -- [查询用户钉钉365会员信息](https://open.dingtalk.com/document/development/api-queryvipmemberinfo): `api-queryvipmemberinfo` - -### 硬件(1 条) - -- [获取用户考勤数据](https://open.dingtalk.com/document/development/obtain-the-attendance-update-data): `obtain-the-attendance-update-data` - -### 职业认证(1 条) - -- [检查用户是否完成所有任务](https://open.dingtalk.com/document/development/docking-of-provincial-practical-exercises-for-digital-managers): `docking-of-provincial-practical-exercises-for-digital-managers` - -## 连接平台(66 条) - -二级 Tab:平台介绍 / 开发指南 / 连接器中心 / 连接平台自动化(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 连接平台自动化(60 条) - -- [下载审批附件](https://open.dingtalk.com/document/development/api-premiumgrantprocessinstancefordownloadfile): `api-premiumgrantprocessinstancefordownloadfile` -- [下载审批附件](https://open.dingtalk.com/document/development/download-an-approval-attachment): `download-an-approval-attachment` -- [保存流程中心外部集成审批任务](https://open.dingtalk.com/document/development/api-premiumsaveintegratedtask): `api-premiumsaveintegratedtask` -- [保存流程中心外部集成审批实例](https://open.dingtalk.com/document/development/api-premiumexternalintegrationprocessinstance): `api-premiumexternalintegrationprocessinstance` -- [保存流程中心外部集成审批模板](https://open.dingtalk.com/document/development/api-premiumsaveintegratedprocess): `api-premiumsaveintegratedprocess` -- [创建实例](https://open.dingtalk.com/document/development/create-a-ticket-approval-instance): `create-a-ticket-approval-instance` -- [创建或更新业务分组](https://open.dingtalk.com/document/development/api-premiuminsertorupdatedir): `api-premiuminsertorupdatedir` -- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-orupdate-the-approval-template-new): `create-orupdate-the-approval-template-new` -- [创建或更新审批表单模板](https://open.dingtalk.com/document/development/create-an-approval-form-template): `create-an-approval-form-template` -- [创建或更新数据表单模板](https://open.dingtalk.com/document/development/api-premiumsaveform): `api-premiumsaveform` -- [创建数据表单实例](https://open.dingtalk.com/document/development/api-createdatapremiumsaveforminstance): `api-createdatapremiumsaveforminstance` -- [创建流程中心待处理任务](https://open.dingtalk.com/document/development/create-pending-tasks-in-process-center): `create-pending-tasks-in-process-center` -- [删除业务分组](https://open.dingtalk.com/document/development/api-premiumdeldir): `api-premiumdeldir` -- [删除数据表单实例](https://open.dingtalk.com/document/development/api-premiumdeleteforminstance): `api-premiumdeleteforminstance` -- [删除模板](https://open.dingtalk.com/document/development/self-owned-approval-deletion-template): `self-owned-approval-deletion-template` -- [加签审批任务](https://open.dingtalk.com/document/development/api-premiumappendtask): `api-premiumappendtask` -- [发起审批实例](https://open.dingtalk.com/document/development/create-an-approval-instance): `create-an-approval-instance` -- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/approve-or-reject-the-approval-task): `approve-or-reject-the-approval-task` -- [归档审批实例](https://open.dingtalk.com/document/development/api-archiveprocessinstance): `api-archiveprocessinstance` -- [批量取消流程中心待处理任务](https://open.dingtalk.com/document/development/cancel-multiple-oa-approval-tasks): `cancel-multiple-oa-approval-tasks` -- [批量同意或拒绝审批任务](https://open.dingtalk.com/document/development/api-premiumbatchexecuteprocessinstances): `api-premiumbatchexecuteprocessinstances` -- [批量更新实例状态](https://open.dingtalk.com/document/development/self-owned-batch-update-of-instance-status): `self-owned-batch-update-of-instance-status` -- [批量获取表单模板schema(包含表单和流程配置信息)](https://open.dingtalk.com/document/development/api-premiumqueryschemaandprocessbycodelist): `api-premiumqueryschemaandprocessbycodelist` -- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/api-premiumaddapprovedentryauth): `api-premiumaddapprovedentryauth` -- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/download-the-approval-nail-file): `download-the-approval-nail-file` -- [授权预览审批附件](https://open.dingtalk.com/document/development/api-premiumgetspacewithdownloadauth): `api-premiumgetspacewithdownloadauth` -- [授权预览审批附件](https://open.dingtalk.com/document/development/official-authorized-preview-approval-attachment): `official-authorized-preview-approval-attachment` -- [撤销审批实例](https://open.dingtalk.com/document/development/revoke-an-approval-instance): `revoke-an-approval-instance` -- [更新实例状态](https://open.dingtalk.com/document/development/update-instance-status): `update-instance-status` -- [更新数据表单实例](https://open.dingtalk.com/document/development/api-premiumupdateforminstance): `api-premiumupdateforminstance` -- [更新流程中心任务状态](https://open.dingtalk.com/document/development/update-process-center-task-status): `update-process-center-task-status` -- [更新流程表单审批实例](https://open.dingtalk.com/document/development/api-premiumupdateprocessinstancevariables): `api-premiumupdateprocessinstancevariables` -- [查询审批中心用户已发起实例列表](https://open.dingtalk.com/document/development/api-premiumgetsubmittedinstances): `api-premiumgetsubmittedinstances` -- [查询审批中心用户已处理任务列表](https://open.dingtalk.com/document/development/api-premiumgetdonetasks): `api-premiumgetdonetasks` -- [查询审批中心用户已收到的实例列表](https://open.dingtalk.com/document/development/api-premiumgetnoticedinstances): `api-premiumgetnoticedinstances` -- [查询审批中心用户待处理任务列表](https://open.dingtalk.com/document/development/api-premiumgettodotasks): `api-premiumgettodotasks` -- [查询已设置为条件的表单组件](https://open.dingtalk.com/document/development/query-form-components-that-have-been-set-as-criteria-1): `query-form-components-that-have-been-set-as-criteria-1` -- [查询通过流程中心集成的OA审批任务](https://open.dingtalk.com/document/development/query-oa-approval-tasks-integrated-through-process-center): `query-oa-approval-tasks-integrated-through-process-center` -- [根据processCode分页获取审批流程数据](https://open.dingtalk.com/document/development/api-premiumgetprocessinstances): `api-premiumgetprocessinstances` -- [添加审批评论](https://open.dingtalk.com/document/development/official-approval-adds-approval-comments): `official-approval-adds-approval-comments` -- [清理OA审批数据](https://open.dingtalk.com/document/development/clear-oa-approval-data): `clear-oa-approval-data` -- [管理员批量转交指定员工的待处理任务](https://open.dingtalk.com/document/development/api-premiumredirecttasksbymanager): `api-premiumredirecttasksbymanager` -- [管理员查询指定员工的待处理任务列表](https://open.dingtalk.com/document/development/api-premiumquerytodotasksbymanager): `api-premiumquerytodotasksbymanager` -- [获取单个审批实例详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-single-approval-instance-pop): `obtains-the-details-of-a-single-approval-instance-pop` -- [获取单个数据表单实例详情](https://open.dingtalk.com/document/development/obtain-details-of-a-single-data-form-instance): `obtain-details-of-a-single-data-form-instance` -- [获取审批单流程中的节点信息](https://open.dingtalk.com/document/development/approval-process-prediction): `approval-process-prediction` -- [获取审批实例ID列表](https://open.dingtalk.com/document/development/obtain-an-approval-list-of-instance-ids): `obtain-an-approval-list-of-instance-ids` -- [获取审批表单控件字段内容修改记录](https://open.dingtalk.com/document/development/api-premiumgetfieldmodifiedhistory): `api-premiumgetfieldmodifiedhistory` -- [获取审批表单控件字段操作权限](https://open.dingtalk.com/document/development/api-premiumgetinstfieldsetting): `api-premiumgetinstfieldsetting` -- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/api-premiumgetattachmentspace): `api-premiumgetattachmentspace` -- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/obtains-the-information-about-approval-nail-disk): `obtains-the-information-about-approval-nail-disk` -- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/get-all-manageable-forms-for-the-current-enterprise): `get-all-manageable-forms-for-the-current-enterprise` -- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/obtains-a-list-of-approval-forms-visible-to-the-specified): `obtains-a-list-of-approval-forms-visible-to-the-specified` -- [获取数据表单schema](https://open.dingtalk.com/document/development/api-premiumgetformschema): `api-premiumgetformschema` -- [获取数据表单实例列表](https://open.dingtalk.com/document/development/api-premiumgetforminstances): `api-premiumgetforminstances` -- [获取模板code](https://open.dingtalk.com/document/development/obtain-the-template-code): `obtain-the-template-code` -- [获取用户待审批数量](https://open.dingtalk.com/document/development/queries-the-number-of-requests-to-be-approved-by-users): `queries-the-number-of-requests-to-be-approved-by-users` -- [获取表单 schema](https://open.dingtalk.com/document/development/obtain-the-form-schema): `obtain-the-form-schema` -- [转交OA审批任务](https://open.dingtalk.com/document/development/transfer-the-oa-approval-task): `transfer-the-oa-approval-task` -- [退回审批任务](https://open.dingtalk.com/document/development/api-premiumreverttask): `api-premiumreverttask` - -### 企业互通(6 条) - -- [批量获取关注服务窗用户信息](https://open.dingtalk.com/document/development/obtains-the-follower-information-from-the-service-window): `obtains-the-follower-information-from-the-service-window` -- [获取企业下服务窗列表](https://open.dingtalk.com/document/development/queries-the-list-of-services-under-an-enterprise): `queries-the-list-of-services-under-an-enterprise` -- [获取关注服务窗用户信息](https://open.dingtalk.com/document/development/queries-the-follower-information-of-the-service-window): `queries-the-follower-information-of-the-service-window` -- [获取用户关注服务窗状态](https://open.dingtalk.com/document/development/third-party-enterprise-application-obtains-user-attention-service-window-status): `third-party-enterprise-application-obtains-user-attention-service-window-status` -- [获取用户服务窗关注状态](https://open.dingtalk.com/document/development/obtain-the-attention-status-of-the-user-service-window): `obtain-the-attention-status-of-the-user-service-window` -- [获取组织服务窗账号列表](https://open.dingtalk.com/document/development/the-third-party-enterprise-application-obtains-the-account-list-of-the): `the-third-party-enterprise-application-obtains-the-account-list-of-the` - -## AI PaaS(48 条) - -二级 Tab:平台介绍 / 炼丹炉大模型平台 / AI 助理创建平台 / AI 客服助理(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 组织大脑(41 条) - -- [人员标签数据查询](https://open.dingtalk.com/document/development/api-stafflabelrecordsquery): `api-stafflabelrecordsquery` -- [人才档案基础数据查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofilebasicquery): `api-hrbraintalentprofilebasicquery` -- [人才档案照片查询](https://open.dingtalk.com/document/development/api-hrbraintalentprofileattachmentquery): `api-hrbraintalentprofileattachmentquery` -- [人才池信息查询](https://open.dingtalk.com/document/development/api-hrbrainemppoolquery): `api-hrbrainemppoolquery` -- [人才池在池人员列表](https://open.dingtalk.com/document/development/api-hrbrainemppooluser): `api-hrbrainemppooluser` -- [数据集成专业技能删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelprofskill): `api-hrbraindeletelabelprofskill` -- [数据集成专业技能同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelprofskill): `api-hrbrainimportlabelprofskill` -- [数据集成人员信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteempinfo): `api-hrbraindeleteempinfo` -- [数据集成人员信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportempinfo): `api-hrbrainimportempinfo` -- [数据集成人员标签删除](https://open.dingtalk.com/document/development/api-hrbraindeletetlabelbase): `api-hrbraindeletetlabelbase` -- [数据集成入职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportregist): `api-hrbrainimportregist` -- [数据集成入职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregist): `api-hrbraindeleteregist` -- [数据集成删除自定义模型数据](https://open.dingtalk.com/document/development/api-hrbraindeletecustom): `api-hrbraindeletecustom` -- [数据集成培训学习数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletetraining): `api-hrbraindeletetraining` -- [数据集成培训学习记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttraining): `api-hrbrainimporttraining` -- [数据集成基础标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelbase): `api-hrbrainimportlabelbase` -- [数据集成处分记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletepundetail): `api-hrbraindeletepundetail` -- [数据集成处分记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpundetail): `api-hrbrainimportpundetail` -- [数据集成奖励信息删除](https://open.dingtalk.com/document/development/api-hrbraindeleteawardrecords): `api-hrbraindeleteawardrecords` -- [数据集成奖励记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportawarddetail): `api-hrbrainimportawarddetail` -- [数据集成工作经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteworkexp): `api-hrbraindeleteworkexp` -- [数据集成工作经历同步](https://open.dingtalk.com/document/development/api-hrbrainimportworkexp): `api-hrbrainimportworkexp` -- [数据集成异动记录同步](https://open.dingtalk.com/document/development/api-hrbrainimporttransfereval): `api-hrbrainimporttransfereval` -- [数据集成教育经历删除](https://open.dingtalk.com/document/development/api-hrbraindeleteeduexp): `api-hrbraindeleteeduexp` -- [数据集成教育经历同步](https://open.dingtalk.com/document/development/api-hrbrainimporteduexp): `api-hrbrainimporteduexp` -- [数据集成晋升记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportpromeval): `api-hrbrainimportpromeval` -- [数据集成盘点数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelinventory): `api-hrbraindeletelabelinventory` -- [数据集成盘点数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelinventory): `api-hrbrainimportlabelinventory` -- [数据集成离职信息同步](https://open.dingtalk.com/document/development/api-hrbrainimportdimission): `api-hrbrainimportdimission` -- [数据集成离职记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletedimission): `api-hrbraindeletedimission` -- [数据集成组织架构同步](https://open.dingtalk.com/document/development/api-hrbrainimportdeptinfo): `api-hrbrainimportdeptinfo` -- [数据集成组织架构数据删除](https://open.dingtalk.com/document/development/api-hrbraindeletedeptinfo): `api-hrbraindeletedeptinfo` -- [数据集成绩效记录删除](https://open.dingtalk.com/document/development/api-hrbraindeleteperfeval): `api-hrbraindeleteperfeval` -- [数据集成绩效记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportperfeval): `api-hrbrainimportperfeval` -- [数据集成自定义标签同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelcustom): `api-hrbrainimportlabelcustom` -- [数据集成调岗记录删除](https://open.dingtalk.com/document/development/api-hrbraindeletetransfereval): `api-hrbraindeletetransfereval` -- [数据集成转正数据删除](https://open.dingtalk.com/document/development/api-hrbraindeleteregular): `api-hrbraindeleteregular` -- [数据集成转正记录同步](https://open.dingtalk.com/document/development/api-hrbrainimportregular): `api-hrbrainimportregular` -- [数据集成领域经验删除](https://open.dingtalk.com/document/development/api-hrbraindeletelabelindustry): `api-hrbraindeletelabelindustry` -- [数据集成领域经验同步](https://open.dingtalk.com/document/development/api-hrbrainimportlabelindustry): `api-hrbrainimportlabelindustry` -- [自定义模型数据同步](https://open.dingtalk.com/document/development/api-hrbrainimportcustom): `api-hrbrainimportcustom` - -### 智能客服(4 条) - -- [分页查询工单](https://open.dingtalk.com/document/development/intelligent-customer-service-paging-query-work-order): `intelligent-customer-service-paging-query-work-order` -- [创建自助单](https://open.dingtalk.com/document/development/create-a-self-service-ticket): `create-a-self-service-ticket` -- [执行工单活动](https://open.dingtalk.com/document/development/intelligent-customer-service-execute-work-order-activities): `intelligent-customer-service-execute-work-order-activities` -- [查询动作记录](https://open.dingtalk.com/document/development/intelligent-customer-service-query-action-records): `intelligent-customer-service-query-action-records` - -### AI PaaS(3 条) - -- [大模型推理服务(多模态模型)](https://open.dingtalk.com/document/development/large-model-reasoning-service-interface-multimodal-model-1): `large-model-reasoning-service-interface-multimodal-model-1` -- [大模型推理服务(文生文模型)](https://open.dingtalk.com/document/development/api-exclusivemodelcompleteservice): `api-exclusivemodelcompleteservice` -- [炼丹炉专属模型服务](https://open.dingtalk.com/document/development/api-liandanluexclusivemodel): `api-liandanluexclusivemodel` - -## 硬件开发(28 条) - -二级 Tab:智能硬件(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 智能会议室(22 条) - -- [创建会议室](https://open.dingtalk.com/document/development/create-meeting-room): `create-meeting-room` -- [创建会议室分组](https://open.dingtalk.com/document/development/create-meeting-room-groups): `create-meeting-room-groups` -- [创建会议室预定黑名单](https://open.dingtalk.com/document/development/api-createbookingblacklist): `api-createbookingblacklist` -- [创建自定义屏幕模板](https://open.dingtalk.com/document/development/api-createdevicecustomtemplate): `api-createdevicecustomtemplate` -- [删除会议室](https://open.dingtalk.com/document/development/delete-a-meeting-room): `delete-a-meeting-room` -- [删除会议室分组](https://open.dingtalk.com/document/development/delete-a-conference-room-group): `delete-a-conference-room-group` -- [删除会议室预定黑名单](https://open.dingtalk.com/document/development/api-deletebookingblacklist): `api-deletebookingblacklist` -- [删除自定义屏幕模板](https://open.dingtalk.com/document/development/api-deletedevicecustomtemplate): `api-deletedevicecustomtemplate` -- [发送Rooms中控API信令](https://open.dingtalk.com/document/development/api-sendcentralcontrol): `api-sendcentralcontrol` -- [更新会议室信息](https://open.dingtalk.com/document/development/update-meeting-room-information): `update-meeting-room-information` -- [更新会议室分组信息](https://open.dingtalk.com/document/development/update-meeting-room-groups): `update-meeting-room-groups` -- [更新自定义屏幕模板](https://open.dingtalk.com/document/development/api-updatedevicecustomtemplate): `api-updatedevicecustomtemplate` -- [查询会议室分组信息](https://open.dingtalk.com/document/development/query-meeting-room-groups): `query-meeting-room-groups` -- [查询会议室分组列表](https://open.dingtalk.com/document/development/query-meeting-rooms-groups): `query-meeting-rooms-groups` -- [查询会议室列表](https://open.dingtalk.com/document/development/check-the-meeting-room-list): `check-the-meeting-room-list` -- [查询会议室详情](https://open.dingtalk.com/document/development/check-meeting-room-details): `check-meeting-room-details` -- [查询自定义屏幕信息](https://open.dingtalk.com/document/development/api-querydevicecustomtemplate): `api-querydevicecustomtemplate` -- [查询自定义屏幕模板列表](https://open.dingtalk.com/document/development/api-querylistdevicecustomscreentemplate): `api-querylistdevicecustomscreentemplate` -- [查询视频会议设备信息](https://open.dingtalk.com/document/development/querying-video-conference-device-information): `querying-video-conference-device-information` -- [查询视频会议设备属性信息](https://open.dingtalk.com/document/development/querying-video-conference-device-attribute-information): `querying-video-conference-device-attribute-information` -- [获取用户发出的日志列表](https://open.dingtalk.com/document/development/query-logs-sent-by-an-employee): `query-logs-sent-by-an-employee` -- [获取用户可见的日志模板](https://open.dingtalk.com/document/development/obtains-the-list-of-visible-log-templates-based-on-the): `obtains-the-list-of-visible-log-templates-based-on-the` - -### 设备管理(5 条) - -- [批量注册与激活设备](https://open.dingtalk.com/document/development/register-and-activate-devices-in-batches): `register-and-activate-devices-in-batches` -- [查询已经注册的设备信息](https://open.dingtalk.com/document/development/query-information-about-a-registered-device): `query-information-about-a-registered-device` -- [注册设备到钉钉](https://open.dingtalk.com/document/development/pin-registration-interface): `pin-registration-interface` -- [获取巡检或保养记录](https://open.dingtalk.com/document/development/obtain-inspection-and-maintenance-records): `obtain-inspection-and-maintenance-records` -- [获取报修记录](https://open.dingtalk.com/document/development/obtain-the-repair-report-record): `obtain-the-repair-report-record` - -### 智能设备(1 条) - -- [变更智能考勤机员工](https://open.dingtalk.com/document/development/change-intelligent-attendance-machine-staff): `change-intelligent-attendance-machine-staff` - -## 互动卡片(9 条) - -二级 Tab:开发指南 / 使用教程 / 卡片模板搭建器 / 互动卡片搭建平台 / 卡片规范设计(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 互动卡片(9 条) - -- [AI卡片流式更新](https://open.dingtalk.com/document/development/api-streamingupdate): `api-streamingupdate` -- [关闭吊顶卡片](https://open.dingtalk.com/document/development/api-closetopcard): `api-closetopcard` -- [创建卡片](https://open.dingtalk.com/document/development/interface-for-creating-a-card-instance): `interface-for-creating-a-card-instance` -- [创建并投放卡片](https://open.dingtalk.com/document/development/create-and-deliver-cards): `create-and-deliver-cards` -- [卡片平台模板复制](https://open.dingtalk.com/document/development/api-copytemplate): `api-copytemplate` -- [投放卡片](https://open.dingtalk.com/document/development/delivery-card-interface): `delivery-card-interface` -- [新增或者更新卡片的场域信息](https://open.dingtalk.com/document/development/add-field-interface): `add-field-interface` -- [更新卡片](https://open.dingtalk.com/document/development/interactive-card-update-interface): `interactive-card-update-interface` -- [注册卡片回调地址](https://open.dingtalk.com/document/development/register-card-callback-address): `register-card-callback-address` - -## 专属版客户端插件(37 条) - -二级 Tab:功能介绍 / 插件开发(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 专属版(37 条) - -- [DING服务](https://open.dingtalk.com/document/development/send-in-application-ding): `send-in-application-ding` -- [专属小红点推送](https://open.dingtalk.com/document/development/push-a-red-dot-to-the-micro-application): `push-a-red-dot-to-the-micro-application` -- [企业内部群禁言或解除禁言](https://open.dingtalk.com/document/development/exclusive-dingtalk-group-ban): `exclusive-dingtalk-group-ban` -- [企业员工专属安全管控功能命中查询](https://open.dingtalk.com/document/development/api-checkcontrolhitstatus): `api-checkcontrolhitstatus` -- [修改伙伴类型可见性](https://open.dingtalk.com/document/development/modify-partner-type-visibility): `modify-partner-type-visibility` -- [修改角色可见性](https://open.dingtalk.com/document/development/modify-role-visibility): `modify-role-visibility` -- [删除可信设备](https://open.dingtalk.com/document/development/delete-trusted-devices): `delete-trusted-devices` -- [发送文件更改的评论](https://open.dingtalk.com/document/development/send-comments-on-file-changes): `send-comments-on-file-changes` -- [发送电话DING](https://open.dingtalk.com/document/development/outgoing-phone-ding): `outgoing-phone-ding` -- [发送邀请函](https://open.dingtalk.com/document/development/send-invitations): `send-invitations` -- [同步存储数据](https://open.dingtalk.com/document/development/api-datasync): `api-datasync` -- [批量新增可信设备](https://open.dingtalk.com/document/development/create-multiple-trusted-devices): `create-multiple-trusted-devices` -- [新增可信设备信息](https://open.dingtalk.com/document/development/add-information-about-a-trusted-device): `add-information-about-a-trusted-device` -- [更新发送文件的检测状态](https://open.dingtalk.com/document/development/update-the-detection-status-of-a-sent-file): `update-the-detection-status-of-a-sent-file` -- [查询人脸录入状态](https://open.dingtalk.com/document/development/query-face-entry-status): `query-face-entry-status` -- [查询企业内部群信息](https://open.dingtalk.com/document/development/obtain-group-info): `obtain-group-info` -- [查询伙伴角色列表](https://open.dingtalk.com/document/development/query-the-list-of-partners): `query-the-list-of-partners` -- [查询公共设备](https://open.dingtalk.com/document/development/query-public-equipment): `query-public-equipment` -- [查询可信设备详细信息](https://open.dingtalk.com/document/development/query-trusted-device-details): `query-trusted-device-details` -- [查询实人认证状态](https://open.dingtalk.com/document/development/queries-the-id-verification-status): `queries-the-id-verification-status` -- [查询群发消息列表](https://open.dingtalk.com/document/development/service-account-query-msgsend-records): `service-account-query-msgsend-records` -- [查询群发消息详情](https://open.dingtalk.com/document/development/service-account-msg-record-detail): `service-account-msg-record-detail` -- [根据userId查询人员的标签信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-user-tag): `you-can-call-this-operation-to-retrieve-the-user-tag` -- [根据会议逻辑ID查询会议基本信息](https://open.dingtalk.com/document/development/query-basic-meeting-information-using-a-logical-id): `query-basic-meeting-information-using-a-logical-id` -- [消息群发](https://open.dingtalk.com/document/development/api-sendmessage): `api-sendmessage` -- [获取专属存储文件路径](https://open.dingtalk.com/document/development/api-getprivatestorefilepath): `api-getprivatestorefilepath` -- [获取人脸对比接口调用记录](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-query-the-call-records): `you-can-call-this-operation-to-query-the-call-records` -- [获取企业专属钉钉权益列表](https://open.dingtalk.com/document/development/api-queryexclusivebenefits): `api-queryexclusivebenefits` -- [获取可打标部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-departments-that-can-be-marked): `obtains-a-list-of-departments-that-can-be-marked` -- [获取子标签列表](https://open.dingtalk.com/document/development/obtain-child-tags-from-a-parent-tag): `obtain-child-tags-from-a-parent-tag` -- [获取实人认证接口调用记录](https://open.dingtalk.com/document/development/obtains-the-call-record-of-the-id-authentication-api): `obtains-the-call-record-of-the-id-authentication-api` -- [获取审计协议签署人员信息](https://open.dingtalk.com/document/development/obtains-the-information-about-the-persons-who-sign-the-audit-1): `obtains-the-information-about-the-persons-who-sign-the-audit-1` -- [获取文件操作记录](https://open.dingtalk.com/document/development/obtain-file-operation-records): `obtain-file-operation-records` -- [获取群活跃明细列表](https://open.dingtalk.com/document/development/obtains-the-group-activity-details-list): `obtains-the-group-activity-details-list` -- [获取视频会议详情](https://open.dingtalk.com/document/development/get-video-meeting-details): `get-video-meeting-details` -- [获取防截屏操作记录](https://open.dingtalk.com/document/development/obtain-anti-screen-capture-operation-records): `obtain-anti-screen-capture-operation-records` -- [设置部门伙伴类型和伙伴编码](https://open.dingtalk.com/document/development/set-department-partner-type-and-partner-code): `set-department-partner-type-and-partner-code` - -## 数据资产(102 条) - -二级 Tab:平台介绍 / 宜数(智能问数)(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 宜搭(61 条) - -- [保存表单数据](https://open.dingtalk.com/document/development/api-saveformdata-v2): `api-saveformdata-v2` -- [分页获取集成自动化日志列表](https://open.dingtalk.com/document/development/api-pageautoflowlog): `api-pageautoflowlog` -- [删除流程实例](https://open.dingtalk.com/document/development/delete-the-process-instance): `delete-the-process-instance` -- [删除表单数据](https://open.dingtalk.com/document/development/delete-form-data): `delete-form-data` -- [发起宜搭审批流程](https://open.dingtalk.com/document/development/api-startinstance-v2): `api-startinstance-v2` -- [同意或拒绝宜搭审批任务](https://open.dingtalk.com/document/development/execute-approval-tasks): `execute-approval-tasks` -- [应用授权校验](https://open.dingtalk.com/document/development/application-authorization-verification): `application-authorization-verification` -- [批量创建表单实例](https://open.dingtalk.com/document/development/create-multiple-form-instances): `create-multiple-form-instances` -- [批量删除宜搭角色成员](https://open.dingtalk.com/document/development/batch-deleterolemembers): `batch-deleterolemembers` -- [批量删除指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-deletematrixdatabyrowids): `api-deletematrixdatabyrowids` -- [批量删除表单实例](https://open.dingtalk.com/document/development/delete-multiple-form-instances): `delete-multiple-form-instances` -- [批量执行宜搭审批任务](https://open.dingtalk.com/document/development/batch-execution-should-take-the-lead-of-approval-tasks): `batch-execution-should-take-the-lead-of-approval-tasks` -- [批量更新宜搭角色成员](https://open.dingtalk.com/document/development/batch-rolemembers): `batch-rolemembers` -- [批量更新表单实例内的组件值](https://open.dingtalk.com/document/development/batch-update-of-component-values-in-form-instances): `batch-update-of-component-values-in-form-instances` -- [批量查询宜搭表单实例的评论](https://open.dingtalk.com/document/development/batch-query-of-comments-appropriate-for-form-instances): `batch-query-of-comments-appropriate-for-form-instances` -- [批量获取流程实例列表](https://open.dingtalk.com/document/development/queries-multiple-process-instances): `queries-multiple-process-instances` -- [批量获取表单实例数据](https://open.dingtalk.com/document/development/obtain-multiple-form-instance-data): `obtain-multiple-form-instance-data` -- [提交评论](https://open.dingtalk.com/document/development/submit-comment): `submit-comment` -- [新增或更新表单实例](https://open.dingtalk.com/document/development/api-createorupdateformdata-v2): `api-createorupdateformdata-v2` -- [新购授权订单](https://open.dingtalk.com/document/development/new-order-authorization): `new-order-authorization` -- [更新宜搭子表单数据](https://open.dingtalk.com/document/development/api-updatesubtable): `api-updatesubtable` -- [更新指定矩阵的明细数据](https://open.dingtalk.com/document/development/api-saveandupdatematrixdata): `api-saveandupdatematrixdata` -- [更新表单数据](https://open.dingtalk.com/document/development/api-updateformdata-v2): `api-updateformdata-v2` -- [查询宜搭应用列表](https://open.dingtalk.com/document/development/query-the-application-list): `query-the-application-list` -- [查询宜搭表单服务调用执行记录](https://open.dingtalk.com/document/development/the-query-should-be-based-on-the-execution-records-of): `the-query-should-be-based-on-the-execution-records-of` -- [查询抄送我的任务列表(应用维度)](https://open.dingtalk.com/document/development/query-copied-my-task-list-application-dimension): `query-copied-my-task-list-application-dimension` -- [查询流程运行任务(VPC)](https://open.dingtalk.com/document/development/query-process-running-tasks-vpc): `query-process-running-tasks-vpc` -- [查询用户详情](https://open.dingtalk.com/document/development/query-user-details): `query-user-details` -- [查询表单实例数据](https://open.dingtalk.com/document/development/api-searchformdatas-v2): `api-searchformdatas-v2` -- [查询表单数据](https://open.dingtalk.com/document/development/api-getformdatabyid-v2): `api-getformdatabyid-v2` -- [查询表单的变更记录](https://open.dingtalk.com/document/development/change-records-of-query-forms): `change-records-of-query-forms` -- [校验订单](https://open.dingtalk.com/document/development/verification-order): `verification-order` -- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/api-getinstancebyid-v2): `api-getinstancebyid-v2` -- [终止流程实例](https://open.dingtalk.com/document/development/terminate-a-process-instance): `terminate-a-process-instance` -- [获取任务列表(组织维度)](https://open.dingtalk.com/document/development/query-tasks-from-the-organization-dimension): `query-tasks-from-the-organization-dimension` -- [获取发送给用户的通知](https://open.dingtalk.com/document/development/get-notifications-sent-to-users): `get-notifications-sent-to-users` -- [获取员工组件的值](https://open.dingtalk.com/document/development/gets-the-value-of-the-employee-component): `gets-the-value-of-the-employee-component` -- [获取多个表单实例ID](https://open.dingtalk.com/document/development/api-searchformdataidlist-v2): `api-searchformdataidlist-v2` -- [获取子表组件数据](https://open.dingtalk.com/document/development/obtain-child-table-component-data): `obtain-child-table-component-data` -- [获取宜搭附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-free-access-address-of-yixian-accessories): `obtain-the-temporary-free-access-address-of-yixian-accessories` -- [获取实例ID列表](https://open.dingtalk.com/document/development/api-getinstanceidlist-v2): `api-getinstanceidlist-v2` -- [获取审批记录](https://open.dingtalk.com/document/development/queries-an-approval-record): `queries-an-approval-record` -- [获取指定宜搭角色的角色详情](https://open.dingtalk.com/document/development/get-roledetailbyid): `get-roledetailbyid` -- [获取指定应用下的表单列表](https://open.dingtalk.com/document/development/depending-on-the-application-id-to-get-the-form-list): `depending-on-the-application-id-to-get-the-form-list` -- [获取指定权限矩阵的明细数据](https://open.dingtalk.com/document/development/api-getmatrixdetailbyid): `api-getmatrixdetailbyid` -- [获取流程实例](https://open.dingtalk.com/document/development/api-getinstances-v2): `api-getinstances-v2` -- [获取流程设计结构](https://open.dingtalk.com/document/development/api-getprocessdesign): `api-getprocessdesign` -- [获取组件别名列表](https://open.dingtalk.com/document/development/api-getformcomponentaliaslist): `api-getformcomponentaliaslist` -- [获取组织内已完成的审批任务](https://open.dingtalk.com/document/development/obtains-the-completed-approval-tasks-in-an-organization): `obtains-the-completed-approval-tasks-in-an-organization` -- [获取组织内某人提交的任务](https://open.dingtalk.com/document/development/obtains-the-tasks-submitted-by-someone-in-an-organization): `obtains-the-tasks-submitted-by-someone-in-an-organization` -- [获取表单内的组件信息](https://open.dingtalk.com/document/development/get-form-field-information-based-on-form-uuid): `get-form-field-information-based-on-form-uuid` -- [获取表单组件定义列表](https://open.dingtalk.com/document/development/get-a-list-of-form-component-definitions): `get-a-list-of-form-component-definitions` -- [获取集成自动化日志详情](https://open.dingtalk.com/document/development/api-getautoflowlogdetail): `api-getautoflowlogdetail` -- [转交任务](https://open.dingtalk.com/document/development/transfer-tasks): `transfer-tasks` -- [退还商品](https://open.dingtalk.com/document/development/refund-of-goods): `refund-of-goods` -- [通过RowId更新子表单数据](https://open.dingtalk.com/document/development/update-the-subform-data-via-rowid): `update-the-subform-data-via-rowid` -- [通过流程code获取流程定义](https://open.dingtalk.com/document/development/obtain-definition-through-process-code): `obtain-definition-through-process-code` -- [通过表单实例数据批量更新表单实例](https://open.dingtalk.com/document/development/update-multiple-form-instances-with-the-form-instance-data): `update-multiple-form-instances-with-the-form-instance-data` -- [通过高级查询条件获取表单实例数据(不包括子表单组件数据)](https://open.dingtalk.com/document/development/obtain-form-instance-data-using-advanced-query-conditions-excluding-subform): `obtain-form-instance-data-using-advanced-query-conditions-excluding-subform` -- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/api-searchformdatasecondgeneration-v2): `api-searchformdatasecondgeneration-v2` -- [预览审批流程](https://open.dingtalk.com/document/development/api-previewpublishedprocess): `api-previewpublishedprocess` - -### 氚云(20 条) - -- [修改表单业务对象数据](https://open.dingtalk.com/document/development/modify-form-business-object-data): `modify-form-business-object-data` -- [创建流程实例](https://open.dingtalk.com/document/development/create-a-process-instance): `create-a-process-instance` -- [创建表单业务数据](https://open.dingtalk.com/document/development/create-form-business-data): `create-form-business-data` -- [删除业务对象](https://open.dingtalk.com/document/development/delete-a-business-object): `delete-a-business-object` -- [删除流程实例数据](https://open.dingtalk.com/document/development/delete-process-instance-data): `delete-process-instance-data` -- [取消流程实例](https://open.dingtalk.com/document/development/cancel-a-process-instance): `cancel-a-process-instance` -- [批量新增表单业务数据](https://open.dingtalk.com/document/development/batch-add-form-business-data): `batch-add-form-business-data` -- [查询流程实例](https://open.dingtalk.com/document/development/query-flow-instances): `query-flow-instances` -- [查询流程实例节点工作项](https://open.dingtalk.com/document/development/query-flow-instance-node-work-items): `query-flow-instance-node-work-items` -- [查询表单业务数据列表](https://open.dingtalk.com/document/development/querying-form-business-data): `querying-form-business-data` -- [获取业务实例信息](https://open.dingtalk.com/document/development/queries-business-instance-information): `queries-business-instance-information` -- [获取应用列表](https://open.dingtalk.com/document/development/queries-applications): `queries-applications` -- [获取应用功能节点](https://open.dingtalk.com/document/development/queries-the-application-feature-nodes): `queries-the-application-feature-nodes` -- [获取文件上传地址](https://open.dingtalk.com/document/development/obtain-the-upload-url-of-a-file-2): `obtain-the-upload-url-of-a-file-2` -- [获取用户数据](https://open.dingtalk.com/document/development/obtain-user-data): `obtain-user-data` -- [获取组织数据](https://open.dingtalk.com/document/development/queries-organization-data): `queries-organization-data` -- [获取表单对象结构](https://open.dingtalk.com/document/development/gets-the-form-object-structure): `gets-the-form-object-structure` -- [获取角色数据](https://open.dingtalk.com/document/development/obtain-role-data): `obtain-role-data` -- [获取角色用户数据](https://open.dingtalk.com/document/development/historical-acquisition-of-role-user-data): `historical-acquisition-of-role-user-data` -- [获取附件临时免登地址](https://open.dingtalk.com/document/development/obtain-the-temporary-attachment-free-address): `obtain-the-temporary-attachment-free-address` - -### 多维表(14 条) - -- [列出多行记录](https://open.dingtalk.com/document/development/api-notable-listrecords): `api-notable-listrecords` -- [创建字段](https://open.dingtalk.com/document/development/api-noatable-createfield): `api-noatable-createfield` -- [创建数据表](https://open.dingtalk.com/document/development/api-createsheet): `api-createsheet` -- [删除多行记录](https://open.dingtalk.com/document/development/api-noatable-deleterecords): `api-noatable-deleterecords` -- [删除字段](https://open.dingtalk.com/document/development/api-noatable-deletefield): `api-noatable-deletefield` -- [删除数据表](https://open.dingtalk.com/document/development/api-noatable-deletesheet): `api-noatable-deletesheet` -- [新增记录](https://open.dingtalk.com/document/development/api-notable-insertrecords): `api-notable-insertrecords` -- [更新多行记录](https://open.dingtalk.com/document/development/api-noatable-updaterecords): `api-noatable-updaterecords` -- [更新字段](https://open.dingtalk.com/document/development/api-noatable-updatefield): `api-noatable-updatefield` -- [更新数据表](https://open.dingtalk.com/document/development/api-noatable-updatesheet): `api-noatable-updatesheet` -- [获取所有字段](https://open.dingtalk.com/document/development/api-noatable-getallfields): `api-noatable-getallfields` -- [获取所有数据表](https://open.dingtalk.com/document/development/api-notable-getallsheets): `api-notable-getallsheets` -- [获取数据表](https://open.dingtalk.com/document/development/api-notable-getsheet): `api-notable-getsheet` -- [获取记录](https://open.dingtalk.com/document/development/api-getrecord): `api-getrecord` - -### 数据中台 AMDP(4 条) - -- [组织变革主数据人员任职数据推送](https://open.dingtalk.com/document/development/api-amdpjobpositiondatapush): `api-amdpjobpositiondatapush` -- [组织变革主数据人员数据推送](https://open.dingtalk.com/document/development/api-amdpemployeedatapush): `api-amdpemployeedatapush` -- [组织变革主数据人员角色数据推送](https://open.dingtalk.com/document/development/api-amdpemproledatapush): `api-amdpemproledatapush` -- [组织变革主数据部门数据推送](https://open.dingtalk.com/document/development/api-amdporganizationdatapush): `api-amdporganizationdatapush` - -### 表单(3 条) - -- [获取单条填表实例详情](https://open.dingtalk.com/document/development/obtains-the-instance-details-of-a-single-fill-table): `obtains-the-instance-details-of-a-single-fill-table` -- [获取填表实例列表](https://open.dingtalk.com/document/development/obtain-the-table-filling-instance-list-data): `obtain-the-table-filling-instance-list-data` -- [获取用户创建的填表模板列表](https://open.dingtalk.com/document/development/new-obtains-the-template-that-a-user-creates): `new-obtains-the-template-that-a-user-creates` - -## 工作台(24 条) - -二级 Tab:平台介绍 / 使用教程(公开 URL 待 `doc_url_mapping` 灌入)。 - -### 微应用(13 条) - -- [创建企业内部应用](https://open.dingtalk.com/document/development/create-an-h5-application-for-your-enterprise): `create-an-h5-application-for-your-enterprise` -- [删除企业内部应用](https://open.dingtalk.com/document/development/delete-an-internal-h5-application): `delete-an-internal-h5-application` -- [发布企业内部小程序版本](https://open.dingtalk.com/document/development/release-internal-applet-version): `release-internal-applet-version` -- [回滚企业内部小程序版本](https://open.dingtalk.com/document/development/rollback-of-enterprise-internal-applet-version): `rollback-of-enterprise-internal-applet-version` -- [更新企业内部应用](https://open.dingtalk.com/document/development/update-internal-h5-applications): `update-internal-h5-applications` -- [更新企业内部应用的可使用范围](https://open.dingtalk.com/document/development/update-the-visible-range-of-micro-applications): `update-the-visible-range-of-micro-applications` -- [查询管理员是否有应用管理权限](https://open.dingtalk.com/document/development/check-whether-the-administrator-has-application-management-permissions): `check-whether-the-administrator-has-application-management-permissions` -- [获取企业内部小程序历史版本列表](https://open.dingtalk.com/document/development/obtain-the-list-of-historical-versions-of-enterprise-internal-applets): `obtain-the-list-of-historical-versions-of-enterprise-internal-applets` -- [获取企业内部小程序的版本列表](https://open.dingtalk.com/document/development/get-the-version-list-of-the-enterprise-internal-applet): `get-the-version-list-of-the-enterprise-internal-applet` -- [获取企业内部应用的可使用范围](https://open.dingtalk.com/document/development/obtains-the-application-visible-range): `obtains-the-application-visible-range` -- [获取企业内部所有应用列表](https://open.dingtalk.com/document/development/get-a-list-of-all-applications-inside-the-enterprise): `get-a-list-of-all-applications-inside-the-enterprise` -- [获取企业所有应用列表](https://open.dingtalk.com/document/development/obtains-a-list-of-all-enterprise-applications): `obtains-a-list-of-all-enterprise-applications` -- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/obtains-the-list-of-enterprise-applications-visible-to-a-user): `obtains-the-list-of-enterprise-applications-visible-to-a-user` - -### 应用角标(8 条) - -- [创建钉工牌电子码](https://open.dingtalk.com/document/development/create-a-badge-user-instance): `create-a-badge-user-instance` -- [同步钉工牌码验证结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-verification-result): `notification-dingtalk-badge-verification-result` -- [更新钉工牌电子码](https://open.dingtalk.com/document/development/update-dingtalk-user-instance): `update-dingtalk-user-instance` -- [解码钉工牌电子码](https://open.dingtalk.com/document/development/stack-dingtalk-badge): `stack-dingtalk-badge` -- [通知支付结果](https://open.dingtalk.com/document/development/sync-dingtalk-badge-code-payment-result): `sync-dingtalk-badge-code-payment-result` -- [通知退款结果](https://open.dingtalk.com/document/development/notification-dingtalk-badge-code-refund-result): `notification-dingtalk-badge-code-refund-result` -- [配置企业钉工牌](https://open.dingtalk.com/document/development/save-dingtalk-enterprise-instance): `save-dingtalk-enterprise-instance` -- [钉工牌通知消息](https://open.dingtalk.com/document/development/dingtalk-badge-notification-message): `dingtalk-badge-notification-message` - -### 工作台(3 条) - -- [批量添加最近使用应用](https://open.dingtalk.com/document/development/add-recently-used-apps-in-bulk): `add-recently-used-apps-in-bulk` -- [获取工作台插件权限点](https://open.dingtalk.com/document/development/obtain-the-permissions-of-the-workbench-plug-in): `obtain-the-permissions-of-the-workbench-plug-in` -- [获取工作台插件检验的规则信息](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-obtain-the-information-about): `you-can-call-this-operation-to-obtain-the-information-about` - -## 其它 / 未归类(557 条) - -### 未归类(557 条) - -- [**获取指定用户的所有父部门列表**](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-specified-user): `queries-all-parent-departments-of-a-specified-user` -- [**获取第三方企业应用的suite_access_token**](https://open.dingtalk.com/document/development/obtain-application-suite-ticket): `obtain-application-suite-ticket` -- [AI 助理发消息(回复消息模式)](https://open.dingtalk.com/document/development/ai-assistant-messages-reply-mode): `ai-assistant-messages-reply-mode` -- [AI 助理更新消息(主动发送模式)](https://open.dingtalk.com/document/development/the-ai-assistant-updates-active-message-sending-mode): `the-ai-assistant-updates-active-message-sending-mode` -- [AI 助理结束发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-finish): `api-finish` -- [AI 助理预备发消息(主动发送模式)](https://open.dingtalk.com/document/development/api-prepare): `api-prepare` -- [AI助理发消息(主动发送模式)](https://open.dingtalk.com/document/development/ai-assistant-active-sends-messages-mode): `ai-assistant-active-sends-messages-mode` -- [AI助理响应接口-个人权限](https://open.dingtalk.com/document/development/api-assistantmeresponse): `api-assistantmeresponse` -- [AI助理响应接口-应用权限](https://open.dingtalk.com/document/development/api-assistantresponse): `api-assistantresponse` -- [ASR 一句话语音识别](https://open.dingtalk.com/document/development/asr-short-sentence-recognition): `asr-short-sentence-recognition` -- [OCR文字识别](https://open.dingtalk.com/document/development/structured-image-recognition-api): `structured-image-recognition-api` -- [groupId转换为groupKey](https://open.dingtalk.com/document/development/groupid-to-groupkey): `groupid-to-groupkey` -- [groupKey转换为groupId](https://open.dingtalk.com/document/development/convert-groupkey-to-groupid): `convert-groupkey-to-groupid` -- [上传媒体文件](https://open.dingtalk.com/document/development/upload-media-files): `upload-media-files` -- [上传打卡记录](https://open.dingtalk.com/document/development/upload-punch-records): `upload-punch-records` -- [上传文件块](https://open.dingtalk.com/document/development/upload-file-blocks): `upload-file-blocks` -- [下载审批附件](https://open.dingtalk.com/document/development/grants-the-permission-to-download-the-approval-file): `grants-the-permission-to-download-the-approval-file` -- [事件推送](https://open.dingtalk.com/document/development/dingtalk-iot-push-events): `dingtalk-iot-push-events` -- [企业活跃用户统计列表(部门维度)](https://open.dingtalk.com/document/development/query-the-statistics-of-active-users-in-a-department-of): `query-the-statistics-of-active-users-in-a-department-of` -- [使商品过期](https://open.dingtalk.com/document/development/make-goods-expire): `make-goods-expire` -- [使用服务助手推送消息](https://open.dingtalk.com/document/development/the-message-pushing-interface-of-the-assistant): `the-message-pushing-interface-of-the-assistant` -- [使用模板发送工作通知消息](https://open.dingtalk.com/document/development/work-notification-templating-send-notification-interface): `work-notification-templating-send-notification-interface` -- [保存文件到自定义或审批钉盘空间](https://open.dingtalk.com/document/development/add-file-to-user-s-dingtalk-disk): `add-file-to-user-s-dingtalk-disk` -- [保存日志内容](https://open.dingtalk.com/document/development/save-custom-log-content): `save-custom-log-content` -- [保存表单数据](https://open.dingtalk.com/document/development/save-form-data): `save-form-data` -- [修改发票配置](https://open.dingtalk.com/document/development/modify-invoice-configuration): `modify-invoice-configuration` -- [修改打卡时段设置](https://open.dingtalk.com/document/development/modify-card-settings): `modify-card-settings` -- [修改文件(夹)名](https://open.dingtalk.com/document/development/modify-the-file-and-folder-name): `modify-the-file-and-folder-name` -- [修改日程](https://open.dingtalk.com/document/development/schedule-2-0-modify-schedule): `schedule-2-0-modify-schedule` -- [修改日程参与者](https://open.dingtalk.com/document/development/schedule-2-0-participant-modification): `schedule-2-0-participant-modification` -- [修改权限](https://open.dingtalk.com/document/development/modify-pin-disk-permission-click): `modify-pin-disk-permission-click` -- [修改申请单](https://open.dingtalk.com/document/development/user-modify-approval-form): `user-modify-approval-form` -- [修改直播课程的可观看白名单](https://open.dingtalk.com/document/development/modify-the-whitelist-for-live-streaming-courses): `modify-the-whitelist-for-live-streaming-courses` -- [修改设备昵称](https://open.dingtalk.com/document/development/intelligent-hardware-device-nickname-modification): `intelligent-hardware-device-nickname-modification` -- [修改课程](https://open.dingtalk.com/document/development/modify-course): `modify-course` -- [修改项目](https://open.dingtalk.com/document/development/project-change): `project-change` -- [停用群模板](https://open.dingtalk.com/document/development/disable-a-group-template): `disable-a-group-template` -- [关闭互动卡片实例置顶](https://open.dingtalk.com/document/development/disable-the-sticky-card-setting): `disable-the-sticky-card-setting` -- [列出插件信息](https://open.dingtalk.com/document/development/query-plug-in-information): `query-plug-in-information` -- [创建AI助理](https://open.dingtalk.com/document/development/assistant-management-create-an-ai-assistant): `assistant-management-create-an-ai-assistant` -- [创建AI助理的运行任务](https://open.dingtalk.com/document/development/api-createassistantrun): `api-createassistantrun` -- [创建CRM自定义对象数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-create-custom-crm-object-data): `dingtalk-paas-master-create-custom-crm-object-data` -- [创建SSO企业账号](https://open.dingtalk.com/document/development/create-an-sso-account): `create-an-sso-account` -- [创建专属帐号用户](https://open.dingtalk.com/document/development/create-dedicated-accounts): `create-dedicated-accounts` -- [创建互动卡片实例](https://open.dingtalk.com/document/development/create-an-interactive-card-instance-2): `create-an-interactive-card-instance-2` -- [创建企业内部应用H5微应用](https://open.dingtalk.com/document/development/create-an-h5-microapplication): `create-an-h5-microapplication` -- [创建企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-customer-data): `dingtalk-paas-master-data-create-crm-customer-data` -- [创建公告](https://open.dingtalk.com/document/development/create-an-enterprise-announcement): `create-an-enterprise-announcement` -- [创建培训课程](https://open.dingtalk.com/document/development/create-live-courses): `create-live-courses` -- [创建学段](https://open.dingtalk.com/document/development/create-a-learning-segment): `create-a-learning-segment` -- [创建学科实例](https://open.dingtalk.com/document/development/create-dingtalk-education-subject-instance): `create-dingtalk-education-subject-instance` -- [创建实例](https://open.dingtalk.com/document/development/initiate-an-approval-process-without-a-process): `initiate-an-approval-process-without-a-process` -- [创建年级](https://open.dingtalk.com/document/development/create-grade): `create-grade` -- [创建待办事项](https://open.dingtalk.com/document/development/create-a-to-do-task): `create-a-to-do-task` -- [创建或更新审批模板](https://open.dingtalk.com/document/development/create-or-update-approval-templates): `create-or-update-approval-templates` -- [创建或更新审批模板](https://open.dingtalk.com/document/development/save-approval-template): `save-approval-template` -- [创建日程](https://open.dingtalk.com/document/development/schedule-2-0-creation-interface): `schedule-2-0-creation-interface` -- [创建消息](https://open.dingtalk.com/document/development/api-createassistantmessage): `api-createassistantmessage` -- [创建班次](https://open.dingtalk.com/document/development/create-modify-shifts): `create-modify-shifts` -- [创建班级](https://open.dingtalk.com/document/development/create-a-class): `create-a-class` -- [创建用户](https://open.dingtalk.com/document/development/create-user): `create-user` -- [创建用户](https://open.dingtalk.com/document/development/user-information-creation): `user-information-creation` -- [创建线程](https://open.dingtalk.com/document/development/api-createassistantthread): `api-createassistantthread` -- [创建群](https://open.dingtalk.com/document/development/create-a-scene-group-v2): `create-a-scene-group-v2` -- [创建群](https://open.dingtalk.com/document/development/session-management-creates-groups): `session-management-creates-groups` -- [创建考勤组](https://open.dingtalk.com/document/development/attendance-group-write): `attendance-group-write` -- [创建联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-create-crm-contact-data): `dingtalk-paas-master-data-create-crm-contact-data` -- [创建角色](https://open.dingtalk.com/document/development/address-book-add-role): `address-book-add-role` -- [创建角色组](https://open.dingtalk.com/document/development/add-a-role-group): `add-a-role-group` -- [创建课程](https://open.dingtalk.com/document/development/create-course): `create-course` -- [创建部门](https://open.dingtalk.com/document/development/address-book-creation-department-established-department): `address-book-creation-department-established-department` -- [创建部门](https://open.dingtalk.com/document/development/create-a-department): `create-a-department` -- [创建部门](https://open.dingtalk.com/document/development/industry-connection-department-creation): `industry-connection-department-creation` -- [创建钉钉自建企业账号](https://open.dingtalk.com/document/development/create-dingtalk-user-created-dedicated-account): `create-dingtalk-user-created-dedicated-account` -- [初始化假期余额](https://open.dingtalk.com/document/development/initialize-holiday-balance): `initialize-holiday-balance` -- [初始化家校架构](https://open.dingtalk.com/document/development/initialize-the-home-school-architecture): `initialize-the-home-school-architecture` -- [删除AI助理](https://open.dingtalk.com/document/development/assistant-management-deletes-ai-assistants): `assistant-management-deletes-ai-assistants` -- [删除AI助理的消息体](https://open.dingtalk.com/document/development/api-deleteassistantmessage): `api-deleteassistantmessage` -- [删除AI助理线程](https://open.dingtalk.com/document/development/api-deleteassistantthread): `api-deleteassistantthread` -- [删除H5微应用](https://open.dingtalk.com/document/development/delete-an-h5-microapplication): `delete-an-h5-microapplication` -- [删除企业客户数据](https://open.dingtalk.com/document/development/delete-crm-customer): `delete-crm-customer` -- [删除假期规则](https://open.dingtalk.com/document/development/api-for-deleting-holiday-types): `api-for-deleting-holiday-types` -- [删除公告](https://open.dingtalk.com/document/development/delete-announcements-based-on-the-announcement-id): `delete-announcements-based-on-the-announcement-id` -- [删除发票信息](https://open.dingtalk.com/document/development/delete-invoice-information): `delete-invoice-information` -- [删除回收站文件(夹)](https://open.dingtalk.com/document/development/delete-recycle-bin-files-folders): `delete-recycle-bin-files-folders` -- [删除图文卡片](https://open.dingtalk.com/document/development/delete-message-card): `delete-message-card` -- [删除培训课程](https://open.dingtalk.com/document/development/delete-live-training-courses): `delete-live-training-courses` -- [删除外部联系人](https://open.dingtalk.com/document/development/delete-external-contact): `delete-external-contact` -- [删除学科实例](https://open.dingtalk.com/document/development/delete-dingtalk-education-disciplines): `delete-dingtalk-education-disciplines` -- [删除序列](https://open.dingtalk.com/document/development/delete-sequence): `delete-sequence` -- [删除成本中心](https://open.dingtalk.com/document/development/delete-cost-center): `delete-cost-center` -- [删除成本中心人员信息](https://open.dingtalk.com/document/development/delete-the-personnel-information-of-the-cost-center): `delete-the-personnel-information-of-the-cost-center` -- [删除文件(夹)](https://open.dingtalk.com/document/development/delete-objects): `delete-objects` -- [删除文章](https://open.dingtalk.com/document/development/delete-article-1): `delete-article-1` -- [删除权限](https://open.dingtalk.com/document/development/delete-the-pin-disk-permission): `delete-the-pin-disk-permission` -- [删除模板](https://open.dingtalk.com/document/development/delete-a-template): `delete-a-template` -- [删除班次](https://open.dingtalk.com/document/development/delete-shift): `delete-shift` -- [删除用户](https://open.dingtalk.com/document/development/delete-a-member): `delete-a-member` -- [删除用户](https://open.dingtalk.com/document/development/delete-a-user): `delete-a-user` -- [删除群成员](https://open.dingtalk.com/document/development/scene-group-delete): `scene-group-delete` -- [删除考勤组](https://open.dingtalk.com/document/development/delete-attendance-group): `delete-attendance-group` -- [删除联系人数据](https://open.dingtalk.com/document/development/delete-crm-contact): `delete-crm-contact` -- [删除角色](https://open.dingtalk.com/document/development/delete-role-information): `delete-role-information` -- [删除设备](https://open.dingtalk.com/document/development/delete-a-device): `delete-a-device` -- [删除课程](https://open.dingtalk.com/document/development/delete-course): `delete-course` -- [删除部门](https://open.dingtalk.com/document/development/address-book-deletion-department): `address-book-deletion-department` -- [删除部门](https://open.dingtalk.com/document/development/delete-a-department): `delete-a-department` -- [删除项目](https://open.dingtalk.com/document/development/delete-a-project): `delete-a-project` -- [剪辑直播课程回放](https://open.dingtalk.com/document/development/clip-live-course-playback): `clip-live-course-playback` -- [加入课程](https://open.dingtalk.com/document/development/join-course): `join-course` -- [助理删除知识](https://open.dingtalk.com/document/development/api-deleteknowledge): `api-deleteknowledge` -- [助理学习知识](https://open.dingtalk.com/document/development/api-learnknowledge): `api-learnknowledge` -- [助理重新学习](https://open.dingtalk.com/document/development/api-relearnknowledge): `api-relearnknowledge` -- [单步文件上传](https://open.dingtalk.com/document/development/single-step-file-upload): `single-step-file-upload` -- [发布商品](https://open.dingtalk.com/document/development/release-products): `release-products` -- [发布文章](https://open.dingtalk.com/document/development/article-publishing-interface-1): `article-publishing-interface-1` -- [发起宜搭审批流程](https://open.dingtalk.com/document/development/initiate-the-approval-process): `initiate-the-approval-process` -- [发起审批实例](https://open.dingtalk.com/document/development/oa-approval-initiates-approval-instances): `oa-approval-initiates-approval-instances` -- [发送工作通知](https://open.dingtalk.com/document/development/asynchronous-sending-of-enterprise-session-messages): `asynchronous-sending-of-enterprise-session-messages` -- [发送普通消息](https://open.dingtalk.com/document/development/send-normal-messages-1): `send-normal-messages-1` -- [发送消息到企业群](https://open.dingtalk.com/document/development/send-group-messages): `send-group-messages` -- [发送群助手消息](https://open.dingtalk.com/document/development/group-template-robot-message): `group-template-robot-message` -- [发送钉盘文件给指定用户](https://open.dingtalk.com/document/development/sends-a-file-to-a-specified-user): `sends-a-file-to-a-specified-user` -- [取消日程](https://open.dingtalk.com/document/development/schedule-2-0-cancel-schedule): `schedule-2-0-cancel-schedule` -- [变配租户信息](https://open.dingtalk.com/document/development/change-tenant-information-1): `change-tenant-information-1` -- [同意或拒绝审批任务](https://open.dingtalk.com/document/development/execute-approval-operation-with-attachment): `execute-approval-operation-with-attachment` -- [启用群模板](https://open.dingtalk.com/document/development/enable-a-group-template): `enable-a-group-template` -- [商旅成本中心转换为外部成本中心](https://open.dingtalk.com/document/development/business-travel-cost-center-converted-to-external-cost-center): `business-travel-cost-center-converted-to-external-cost-center` -- [回放课程](https://open.dingtalk.com/document/development/replay-course): `replay-course` -- [多渠道新购校验](https://open.dingtalk.com/document/development/multi-channel-new-purchase-verification): `multi-channel-new-purchase-verification` -- [学习推荐数据回流](https://open.dingtalk.com/document/development/learn-to-recommend-data-backflow): `learn-to-recommend-data-backflow` -- [家庭Feed同步](https://open.dingtalk.com/document/development/dingtalk-education-family-feed-synchronization): `dingtalk-education-family-feed-synchronization` -- [将助理技能发布到组织技能库](https://open.dingtalk.com/document/development/api-addtoorgskillrepository): `api-addtoorgskillrepository` -- [应用内购商品核销](https://open.dingtalk.com/document/development/application-of-in-house-purchase-verification): `application-of-in-house-purchase-verification` -- [开启互动卡片实例置顶](https://open.dingtalk.com/document/development/enable-the-interactive-card-setting): `enable-the-interactive-card-setting` -- [开启分块上传事务](https://open.dingtalk.com/document/development/enable-upload-transaction): `enable-upload-transaction` -- [开始课程](https://open.dingtalk.com/document/development/start-course): `start-course` -- [执行宜搭的审批任务](https://open.dingtalk.com/document/development/execute-appropriate-approval-tasks): `execute-appropriate-approval-tasks` -- [执行自定义API](https://open.dingtalk.com/document/development/run-custom-api): `run-custom-api` -- [批量修改设备](https://open.dingtalk.com/document/development/batch-modify-devices): `batch-modify-devices` -- [批量删除参与考勤人员](https://open.dingtalk.com/document/development/batch-delete-employees-under-the-attendance-group): `batch-delete-employees-under-the-attendance-group` -- [批量删除员工角色](https://open.dingtalk.com/document/development/delete-the-color-information-of-employee-corners-in-batches): `delete-the-color-information-of-employee-corners-in-batches` -- [批量删除地点](https://open.dingtalk.com/document/development/delete-position-in-batches-under-the-attendance-group): `delete-position-in-batches-under-the-attendance-group` -- [批量发起回调](https://open.dingtalk.com/document/development/initiate-multiple-callbacks): `initiate-multiple-callbacks` -- [批量取消待办](https://open.dingtalk.com/document/development/cancel-multiple-tasks): `cancel-multiple-tasks` -- [批量增加员工角色](https://open.dingtalk.com/document/development/add-role-information-to-employees-in-batches): `add-role-information-to-employees-in-batches` -- [批量新增Wi-Fi信息](https://open.dingtalk.com/document/development/batch-add-wifi-under-attendance-group): `batch-add-wifi-under-attendance-group` -- [批量新增参与考勤人员](https://open.dingtalk.com/document/development/batch-add-employees-under-the-attendance-group): `batch-add-employees-under-the-attendance-group` -- [批量新增地点](https://open.dingtalk.com/document/development/atch-add-position-under-attendance-group): `atch-add-position-under-attendance-group` -- [批量更新假期余额](https://open.dingtalk.com/document/development/bulk-update-holiday-balance): `bulk-update-holiday-balance` -- [批量更新实例状态](https://open.dingtalk.com/document/development/update-the-status-of-multiple-instances-at-a-time): `update-the-status-of-multiple-instances-at-a-time` -- [批量查询Wi-Fi信息](https://open.dingtalk.com/document/development/batch-query-wifi-under-attendance-group): `batch-query-wifi-under-attendance-group` -- [批量查询人员排班信息](https://open.dingtalk.com/document/development/query-batch-scheduling-information): `query-batch-scheduling-information` -- [批量查询员工假期余额变更记录](https://open.dingtalk.com/document/development/query-holiday-consumption-records): `query-holiday-consumption-records` -- [批量查询地点](https://open.dingtalk.com/document/development/batch-query-position-under-attendance-group): `batch-query-position-under-attendance-group` -- [批量查询成员排班概要信息](https://open.dingtalk.com/document/development/query-scheduling-summary-information): `query-scheduling-summary-information` -- [批量注册事件类型](https://open.dingtalk.com/document/development/registration-event-type): `registration-event-type` -- [批量注册设备](https://open.dingtalk.com/document/development/batchregister-devices): `batchregister-devices` -- [批量注册设备](https://open.dingtalk.com/document/development/industry-connection-device-batch-registration): `industry-connection-device-batch-registration` -- [批量移除Wi-Fi信息](https://open.dingtalk.com/document/development/batch-remove-wifi-under-attendance-group): `batch-remove-wifi-under-attendance-group` -- [批量获取企业客户数据](https://open.dingtalk.com/document/development/obtains-customer-data-in-batches-based-on-the-id-list): `obtains-customer-data-in-batches-based-on-the-id-list` -- [批量获取客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-customer-data-search-and-query-interface): `dingtalk-paas-master-data-customer-data-search-and-query-interface` -- [批量获取应用信息](https://open.dingtalk.com/document/development/queries-the-information-about-multiple-applications): `queries-the-information-about-multiple-applications` -- [批量获取服务窗联系人数据](https://open.dingtalk.com/document/development/obtain-contact-data-from-the-service-window): `obtain-contact-data-from-the-service-window` -- [批量获取考勤组摘要](https://open.dingtalk.com/document/development/batch-query-of-simple-information-of-the-attendance-group): `batch-query-of-simple-information-of-the-attendance-group` -- [批量获取考勤组详情](https://open.dingtalk.com/document/development/batch-obtain-attendance-group-details): `batch-obtain-attendance-group-details` -- [批量获取钉钉运动数据](https://open.dingtalk.com/document/development/queries-the-number-of-dingtalk-movement-steps-of-multiple-users): `queries-the-number-of-dingtalk-movement-steps-of-multiple-users` -- [拷贝文件(夹)](https://open.dingtalk.com/document/development/copy-files-folders): `copy-files-folders` -- [按名称搜索班次](https://open.dingtalk.com/document/development/search-shifts-by-rank): `search-shifts-by-rank` -- [按照ID列表批量获取CRM自定义表单数据](https://open.dingtalk.com/document/development/retrieves-custom-crm-forms-from-the-id-list): `retrieves-custom-crm-forms-from-the-id-list` -- [按照ID列表批量获取联系人数据](https://open.dingtalk.com/document/development/retrieves-contact-data-in-batches-based-on-the-id-list): `retrieves-contact-data-in-batches-based-on-the-id-list` -- [授权下载审批钉盘文件](https://open.dingtalk.com/document/development/approve-nail-disk-file-authorization): `approve-nail-disk-file-authorization` -- [授权用户访问企业的自定义空间](https://open.dingtalk.com/document/development/authorize-a-user-to-access-a-custom-workspace-of-an): `authorize-a-user-to-access-a-custom-workspace-of-an` -- [授权预览审批附件](https://open.dingtalk.com/document/development/preview-authorization-attachment): `preview-authorization-attachment` -- [排班制考勤组排班](https://open.dingtalk.com/document/development/scheduling-system-attendance-group-scheduling): `scheduling-system-attendance-group-scheduling` -- [提交文件上传事务](https://open.dingtalk.com/document/development/submit-a-file-upload-transaction): `submit-a-file-upload-transaction` -- [搜索考勤组摘要](https://open.dingtalk.com/document/development/attendance-group-search): `attendance-group-search` -- [撤回工作通知消息](https://open.dingtalk.com/document/development/notification-of-work-withdrawal): `notification-of-work-withdrawal` -- [撤销审批实例](https://open.dingtalk.com/document/development/terminate-a-workflow-by-using-an-instance-id): `terminate-a-workflow-by-using-an-instance-id` -- [新增发票配置](https://open.dingtalk.com/document/development/new-invoice-configuration): `new-invoice-configuration` -- [新增图文卡片](https://open.dingtalk.com/document/development/new-message-card-1): `new-message-card-1` -- [新增或更新表单实例](https://open.dingtalk.com/document/development/add-or-update-form-instances): `add-or-update-form-instances` -- [新增文章](https://open.dingtalk.com/document/development/new-article-1): `new-article-1` -- [新增服务号](https://open.dingtalk.com/document/development/added-service-number): `added-service-number` -- [新增群成员](https://open.dingtalk.com/document/development/add-people-to-scene-group): `add-people-to-scene-group` -- [新增钉钉待办任务](https://open.dingtalk.com/document/development/new-to-do-items): `new-to-do-items` -- [新购宜搭产品](https://open.dingtalk.com/document/development/suitable-for-new-purchase): `suitable-for-new-purchase` -- [更新 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-updateassistantscope): `api-updateassistantscope` -- [更新AI助理基础信息](https://open.dingtalk.com/document/development/api-updateassistantbasicinfo): `api-updateassistantbasicinfo` -- [更新企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/set-the-visible-range-of-the-microapplication): `set-the-visible-range-of-the-microapplication` -- [更新企业客户数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-customer-data): `dingtalk-paas-master-data-update-crm-customer-data` -- [更新企业账号用户信息](https://open.dingtalk.com/document/development/update-dedicated-accounts-information): `update-dedicated-accounts-information` -- [更新假期规则](https://open.dingtalk.com/document/development/holiday-type-update): `holiday-type-update` -- [更新公告](https://open.dingtalk.com/document/development/modify-the-announcement-according-to-the-announcement-id): `modify-the-announcement-according-to-the-announcement-id` -- [更新参与考勤人员](https://open.dingtalk.com/document/development/attendance-group-member-update): `attendance-group-member-update` -- [更新员工花名册](https://open.dingtalk.com/document/development/update-employee-roster): `update-employee-roster` -- [更新员工花名册信息](https://open.dingtalk.com/document/development/intelligent-personnel-update-employee-file-information): `intelligent-personnel-update-employee-file-information` -- [更新图文卡片](https://open.dingtalk.com/document/development/message-card-material-update-interface): `message-card-material-update-interface` -- [更新外部联系人](https://open.dingtalk.com/document/development/update-enterprise-external-contacts): `update-enterprise-external-contacts` -- [更新学科实例](https://open.dingtalk.com/document/development/update-dingtalk-education-instance): `update-dingtalk-education-instance` -- [更新实例状态](https://open.dingtalk.com/document/development/to-do-instance-status): `to-do-instance-status` -- [更新工作通知状态栏](https://open.dingtalk.com/document/development/update-work-notification-status-bar): `update-work-notification-status-bar` -- [更新待办状态](https://open.dingtalk.com/document/development/update-to-do-task-status): `update-to-do-task-status` -- [更新文章](https://open.dingtalk.com/document/development/save-article-details-1): `save-article-details-1` -- [更新服务号](https://open.dingtalk.com/document/development/service-number-update-1): `service-number-update-1` -- [更新流程实例](https://open.dingtalk.com/document/development/update-process-instance-yida): `update-process-instance-yida` -- [更新状态](https://open.dingtalk.com/document/development/update-status): `update-status` -- [更新用户信息](https://open.dingtalk.com/document/development/update-user-details): `update-user-details` -- [更新用户信息](https://open.dingtalk.com/document/development/user-information-update): `user-information-update` -- [更新申请单状态](https://open.dingtalk.com/document/development/update-approval-form): `update-approval-form` -- [更新群](https://open.dingtalk.com/document/development/modify-a-group-session): `modify-a-group-session` -- [更新群](https://open.dingtalk.com/document/development/scene-group-update): `scene-group-update` -- [更新群成员的群昵称](https://open.dingtalk.com/document/development/set-a-group-nickname): `set-a-group-nickname` -- [更新群管理员](https://open.dingtalk.com/document/development/set-chat-admin): `set-chat-admin` -- [更新考勤组](https://open.dingtalk.com/document/development/attendance-group-update-interface): `attendance-group-update-interface` -- [更新联系人数据](https://open.dingtalk.com/document/development/dingtalk-paas-master-data-update-crm-contact-data): `dingtalk-paas-master-data-update-crm-contact-data` -- [更新自定义对象数据](https://open.dingtalk.com/document/development/crm-master-data-opens-interface-for-updating-custom-object-data): `crm-master-data-opens-interface-for-updating-custom-object-data` -- [更新表单数据](https://open.dingtalk.com/document/development/update-form-data): `update-form-data` -- [更新角色名称](https://open.dingtalk.com/document/development/update-the-character-name): `update-the-character-name` -- [更新部门](https://open.dingtalk.com/document/development/address-book-update-department): `address-book-update-department` -- [更新部门](https://open.dingtalk.com/document/development/update-a-department): `update-a-department` -- [更新部门扩展信息](https://open.dingtalk.com/document/development/department-update-extension-information): `department-update-extension-information` -- [更新钉钉待办任务](https://open.dingtalk.com/document/development/update-to-do-status): `update-to-do-status` -- [服务号菜单更新](https://open.dingtalk.com/document/development/service-number-menu-update): `service-number-menu-update` -- [服务商获取第三方应用授权企业的access_token](https://open.dingtalk.com/document/development/obtain-isvapp-token): `obtain-isvapp-token` -- [机票城市搜索](https://open.dingtalk.com/document/development/air-ticket-city-search): `air-ticket-city-search` -- [查询 AI 助理基本信息](https://open.dingtalk.com/document/development/api-retrieveassistantbasicinfo): `api-retrieveassistantbasicinfo` -- [查询企业下用户待办列表](https://open.dingtalk.com/document/development/get-the-user-s-to-do-items): `get-the-user-s-to-do-items` -- [查询企业个人待办数量](https://open.dingtalk.com/document/development/query-the-number-of-to-do-tasks-of-the-enterprise): `query-the-number-of-to-do-tasks-of-the-enterprise` -- [查询企业级别](https://open.dingtalk.com/document/development/query-enterprise-level): `query-enterprise-level` -- [查询企业考勤排班详情](https://open.dingtalk.com/document/development/interface-for-daily-full-query-of-attendance-scheduling-information): `interface-for-daily-full-query-of-attendance-scheduling-information` -- [查询企业账号用户详情](https://open.dingtalk.com/document/development/queries-the-details-of-a-dedicated-account): `queries-the-details-of-a-dedicated-account` -- [查询企业通讯录未激活用户列表](https://open.dingtalk.com/document/development/queries-the-list-of-inactive-accounts-in-the-key-account): `queries-the-list-of-inactive-accounts-in-the-key-account` -- [查询假期规则列表](https://open.dingtalk.com/document/development/holiday-type-query): `holiday-type-query` -- [查询历史班次](https://open.dingtalk.com/document/development/query-history-shifts): `query-history-shifts` -- [查询参与考勤人员列表](https://open.dingtalk.com/document/development/batch-query-of-employees-in-the-attendance-group): `batch-query-of-employees-in-the-attendance-group` -- [查询可用发票列表](https://open.dingtalk.com/document/development/query-available-invoices): `query-available-invoices` -- [查询员工智能考勤机列表](https://open.dingtalk.com/document/development/query-the-list-of-employee-intelligent-attendance-machines): `query-the-list-of-employee-intelligent-attendance-machines` -- [查询商品列表](https://open.dingtalk.com/document/development/query-product-lists): `query-product-lists` -- [查询回收站文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-recycle-bin-folder-list): `obtain-the-recycle-bin-folder-list` -- [查询图文卡片列表](https://open.dingtalk.com/document/development/query-message-card-list): `query-message-card-list` -- [查询家庭孩子信息](https://open.dingtalk.com/document/development/query-family-child-information): `query-family-child-information` -- [查询应用信息列表](https://open.dingtalk.com/document/development/queries-application-information): `queries-application-information` -- [查询待办列表](https://open.dingtalk.com/document/development/query-a-user-s-to-do-items): `query-a-user-s-to-do-items` -- [查询成本中心](https://open.dingtalk.com/document/development/query-cost-center): `query-cost-center` -- [查询排班打卡结果](https://open.dingtalk.com/document/development/query-the-results-of-a-batch-of-tasks): `query-the-results-of-a-batch-of-tasks` -- [查询插件信息列表](https://open.dingtalk.com/document/development/query-plug-ins): `query-plug-ins` -- [查询文件(夹)信息](https://open.dingtalk.com/document/development/obtain-file-information): `obtain-file-information` -- [查询文件(夹)列表](https://open.dingtalk.com/document/development/obtain-the-file-list): `obtain-the-file-list` -- [查询文档模板](https://open.dingtalk.com/document/development/query-a-document-template): `query-a-document-template` -- [查询文章列表](https://open.dingtalk.com/document/development/query-the-article-list): `query-the-article-list` -- [查询是否启用智能统计报表](https://open.dingtalk.com/document/development/determine-whether-to-enable-attendance-intelligent-report): `determine-whether-to-enable-attendance-intelligent-report` -- [查询服务号列表](https://open.dingtalk.com/document/development/query-service-number-list): `query-service-number-list` -- [查询服务号菜单](https://open.dingtalk.com/document/development/query-service-number-menu-1): `query-service-number-menu-1` -- [查询服务号详情](https://open.dingtalk.com/document/development/inquire-about-service-number-details): `inquire-about-service-number-details` -- [查询激活码](https://open.dingtalk.com/document/development/query-activation-code): `query-activation-code` -- [查询用户是否参与企业步数排行榜](https://open.dingtalk.com/document/development/check-whether-dingtalk-is-enabled): `check-whether-dingtalk-is-enabled` -- [查询用户详情](https://open.dingtalk.com/document/development/queries-user-details): `queries-user-details` -- [查询直播的观看数据](https://open.dingtalk.com/document/development/queries-the-playback-data-of-a-live-stream): `queries-the-playback-data-of-a-live-stream` -- [查询直播课程的可观看白名单](https://open.dingtalk.com/document/development/query-the-whitelist-of-live-courses): `query-the-whitelist-of-live-courses` -- [查询群信息](https://open.dingtalk.com/document/development/obtain-a-group-session): `obtain-a-group-session` -- [查询群信息](https://open.dingtalk.com/document/development/queries-the-basic-information-of-a-scenario-group): `queries-the-basic-information-of-a-scenario-group` -- [查询群消息已读人员列表](https://open.dingtalk.com/document/development/queries-the-list-of-people-who-have-read-a-group): `queries-the-list-of-people-who-have-read-a-group` -- [查询表单实例数据](https://open.dingtalk.com/document/development/querying-form-instance-data): `querying-form-instance-data` -- [查询表单数据](https://open.dingtalk.com/document/development/query-form-data): `query-form-data` -- [查询设备列表](https://open.dingtalk.com/document/development/intelligent-hardware-list-query): `intelligent-hardware-list-query` -- [查询设备详情](https://open.dingtalk.com/document/development/intelligent-hardware-device-query): `intelligent-hardware-device-query` -- [查询请假状态](https://open.dingtalk.com/document/development/query-status): `query-status` -- [查询销售用户信息](https://open.dingtalk.com/document/development/query-sales-user-information): `query-sales-user-information` -- [查询项目中文件操作日志](https://open.dingtalk.com/document/development/query-file-operation-logs-of-a-project): `query-file-operation-logs-of-a-project` -- [查询预估价](https://open.dingtalk.com/document/development/query-estimated-price): `query-estimated-price` -- [校验变配](https://open.dingtalk.com/document/development/verify-configuration): `verify-configuration` -- [校验用户是否在当前考勤组](https://open.dingtalk.com/document/development/query-members-by-id): `query-members-by-id` -- [校验订单的升级](https://open.dingtalk.com/document/development/upgrade-the-verification-order): `upgrade-the-verification-order` -- [校验订单的升级状态](https://open.dingtalk.com/document/development/upgrade-status-of-the-verification-order): `upgrade-status-of-the-verification-order` -- [根据ID列表批量获取跟进记录数据](https://open.dingtalk.com/document/development/dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you): `dingtalk-the-primary-data-of-apsara-stack-agility-paas-allows-you` -- [根据groupKey查询考勤组信息](https://open.dingtalk.com/document/development/queries-attendance-group-information-by-id): `queries-attendance-group-information-by-id` -- [根据sns临时授权码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-user-information-based-on-the-sns-temporary-authorization): `obtain-the-user-information-based-on-the-sns-temporary-authorization` -- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/query-a-user-by-the-union-id): `query-a-user-by-the-union-id` -- [根据unionid获取用户userid](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-the-userids-of): `you-can-call-this-operation-to-retrieve-the-userids-of` -- [根据手机号查询企业账号用户](https://open.dingtalk.com/document/development/obtain-the-userid-of-your-mobile-phone-number): `obtain-the-userid-of-your-mobile-phone-number` -- [根据手机号查询用户](https://open.dingtalk.com/document/development/retrieve-userid-from-mobile-phone-number): `retrieve-userid-from-mobile-phone-number` -- [根据指定条件查询联系人数据](https://open.dingtalk.com/document/development/dingtalk-the-contact-data-query-api): `dingtalk-the-contact-data-query-api` -- [根据指定条件查询自定义对象数据](https://open.dingtalk.com/document/development/retrieve-custom-crm-object-data): `retrieve-custom-crm-object-data` -- [根据指定条件查询跟进记录数据](https://open.dingtalk.com/document/development/query-and-dingtalk-data-of-track-records-in-apsara-stack): `query-and-dingtalk-data-of-track-records-in-apsara-stack` -- [根据流程实例ID获取流程实例](https://open.dingtalk.com/document/development/queries-a-process-instance-based-on-its-id): `queries-a-process-instance-based-on-its-id` -- [根据设备ID查询设备](https://open.dingtalk.com/document/development/the-smart-hardware-can-query-details-based-on-the-device): `the-smart-hardware-can-query-details-based-on-the-device` -- [检索AI助理线程](https://open.dingtalk.com/document/development/api-retrieveassistantthread): `api-retrieveassistantthread` -- [注册互动卡片回调地址](https://open.dingtalk.com/document/development/registration-card-interaction-callback-address-1): `registration-card-interaction-callback-address-1` -- [注册单个设备](https://open.dingtalk.com/document/development/industry-connection-single-device-registration): `industry-connection-single-device-registration` -- [注册设备](https://open.dingtalk.com/document/development/register-devices): `register-devices` -- [注册账号](https://open.dingtalk.com/document/development/register-an-account): `register-an-account` -- [消息撤回](https://open.dingtalk.com/document/development/service-number-message-withdrawal): `service-number-message-withdrawal` -- [消息群发](https://open.dingtalk.com/document/development/interactive-service-window-group-message-sending-interface): `interactive-service-window-group-message-sending-interface` -- [添加企业待入职员工](https://open.dingtalk.com/document/development/add-employees-to-be-hired-through-intelligent-personnel): `add-employees-to-be-hired-through-intelligent-personnel` -- [添加假期规则](https://open.dingtalk.com/document/development/holiday-type-added): `holiday-type-added` -- [添加外部联系人](https://open.dingtalk.com/document/development/add-enterprise-external-contacts): `add-enterprise-external-contacts` -- [添加学生](https://open.dingtalk.com/document/development/add-student): `add-student` -- [添加审批评论](https://open.dingtalk.com/document/development/add-an-approval-comment): `add-an-approval-comment` -- [添加家长](https://open.dingtalk.com/document/development/add-parent): `add-parent` -- [添加文件(夹)](https://open.dingtalk.com/document/development/add-file-and-folder): `add-file-and-folder` -- [添加权限](https://open.dingtalk.com/document/development/add-pin-disk-permission): `add-pin-disk-permission` -- [添加老师](https://open.dingtalk.com/document/development/add-teacher): `add-teacher` -- [添加自定义空间权限](https://open.dingtalk.com/document/development/add-custom-workspace-permissions): `add-custom-workspace-permissions` -- [添加课程参与方](https://open.dingtalk.com/document/development/add-course-participants): `add-course-participants` -- [添加项目](https://open.dingtalk.com/document/development/add-a-project): `add-a-project` -- [清理审批数据](https://open.dingtalk.com/document/development/clean-up-workflow-data): `clean-up-workflow-data` -- [清空回收站](https://open.dingtalk.com/document/development/empty-recycle-bin-files-folders): `empty-recycle-bin-files-folders` -- [火车票城市搜索](https://open.dingtalk.com/document/development/train-ticket-city-search): `train-ticket-city-search` -- [移动文件(夹)](https://open.dingtalk.com/document/development/move-file-and-folder): `move-file-and-folder` -- [移除助理组织技能库技能](https://open.dingtalk.com/document/development/api-removefromorgskillrepository): `api-removefromorgskillrepository` -- [移除租户资源](https://open.dingtalk.com/document/development/remove-tenant-resources): `remove-tenant-resources` -- [移除课程参与方](https://open.dingtalk.com/document/development/remove-course-participants): `remove-course-participants` -- [终止阿里云授权](https://open.dingtalk.com/document/development/terminate-authorization-for-alibaba-cloud-services): `terminate-authorization-for-alibaba-cloud-services` -- [绑定设备](https://open.dingtalk.com/document/development/establishing-a-binding-relationship-between-intelligent-hardware-and-cloud): `establishing-a-binding-relationship-between-intelligent-hardware-and-cloud` -- [结束课程](https://open.dingtalk.com/document/development/end-course): `end-course` -- [统计企业活跃用户](https://open.dingtalk.com/document/development/query-for-dau-statistics): `query-for-dau-statistics` -- [续费服务订单](https://open.dingtalk.com/document/development/renewal-service-order): `renewal-service-order` -- [续费租户](https://open.dingtalk.com/document/development/renewal-tenant): `renewal-tenant` -- [自定义机器人发送群消息](https://open.dingtalk.com/document/development/custom-robots-send-group-messages): `custom-robots-send-group-messages` -- [获取 AI 助理的使用范围](https://open.dingtalk.com/document/development/api-retrieveassistantscope): `api-retrieveassistantscope` -- [获取AI助理对话明细列表](https://open.dingtalk.com/document/development/api-loglist): `api-loglist` -- [获取AI助理的消息体](https://open.dingtalk.com/document/development/api-retrieveassistantmessage): `api-retrieveassistantmessage` -- [获取AI助理的消息列表](https://open.dingtalk.com/document/development/api-listassistantmessage): `api-listassistantmessage` -- [获取AI助理的运行任务](https://open.dingtalk.com/document/development/api-retrieveassistantrun): `api-retrieveassistantrun` -- [获取AI助理的运行任务的列表](https://open.dingtalk.com/document/development/api-listassistantrun): `api-listassistantrun` -- [获取jsapi_ticket](https://open.dingtalk.com/document/development/obtain-jsapi-ticket): `obtain-jsapi-ticket` -- [获取个人或部门钉钉运动数据](https://open.dingtalk.com/document/development/queries-individual-or-department-dingtalk-exercise-steps): `queries-individual-or-department-dingtalk-exercise-steps` -- [获取主干组织列表](https://open.dingtalk.com/document/development/obtain-backbone-organization-list): `obtain-backbone-organization-list` -- [获取互动服务窗相关数据](https://open.dingtalk.com/document/development/queries-the-data-about-the-interactive-service-window): `queries-the-data-about-the-interactive-service-window` -- [获取人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-home-school-user-identities): `obtains-a-list-of-home-school-user-identities` -- [获取人员详情](https://open.dingtalk.com/document/development/obtain-the-identity-details-of-home-school-personnel): `obtain-the-identity-details-of-home-school-personnel` -- [获取企业DING使用数据](https://open.dingtalk.com/document/development/enterprise-ding-quantity-statistics): `enterprise-ding-quantity-statistics` -- [获取企业DING使用数据(部门维度)](https://open.dingtalk.com/document/development/query-the-departmental-transmission-status-of-key-clients): `query-the-departmental-transmission-status-of-key-clients` -- [获取企业DING发送统计数据](https://open.dingtalk.com/document/development/obtain-sending-statistics-of-an-enterprise-ding): `obtain-sending-statistics-of-an-enterprise-ding` -- [获取企业DING接收及评论统计数据](https://open.dingtalk.com/document/development/obtain-statistics-on-receiving-and-comments-of-enterprise-ding): `obtain-statistics-on-receiving-and-comments-of-enterprise-ding` -- [获取企业下的自定义空间](https://open.dingtalk.com/document/development/obtain-user-space-under-the-enterprise): `obtain-user-space-under-the-enterprise` -- [获取企业信息](https://open.dingtalk.com/document/development/obtain-enterprise-information): `obtain-enterprise-information` -- [获取企业全员圈统计数据](https://open.dingtalk.com/document/development/obtains-the-statistical-data-of-all-employees-of-an-enterprise): `obtains-the-statistical-data-of-all-employees-of-an-enterprise` -- [获取企业公告统计数据](https://open.dingtalk.com/document/development/queries-corporate-announcement-statistics): `queries-corporate-announcement-statistics` -- [获取企业内部应用微应用的可使用范围](https://open.dingtalk.com/document/development/gets-the-microapplication-visible-range-set-by-the-enterprise): `gets-the-microapplication-visible-range-set-by-the-enterprise` -- [获取企业内部应用的access_token](https://open.dingtalk.com/document/development/obtain-orgapp-token): `obtain-orgapp-token` -- [获取企业单聊统计数据](https://open.dingtalk.com/document/development/queries-the-statistics-on-one-time-enterprise-chats): `queries-the-statistics-on-one-time-enterprise-chats` -- [获取企业发布智能填表数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-of-tables-published-in-an-organization): `queries-the-number-of-tables-published-in-an-organization` -- [获取企业发红包统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-red-packets-issued-by-enterprises): `obtains-the-statistics-on-red-packets-issued-by-enterprises` -- [获取企业各类群组创建情况](https://open.dingtalk.com/document/development/api-for-obtaining-the-creation-status-of-various-groups): `api-for-obtaining-the-creation-status-of-various-groups` -- [获取企业员工类型统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-employee-types): `obtains-statistics-on-employee-types` -- [获取企业商旅酒店订单数据](https://open.dingtalk.com/document/development/enterprises-obtain-order-data-for-business-hotels): `enterprises-obtain-order-data-for-business-hotels` -- [获取企业审批统计数据](https://open.dingtalk.com/document/development/obtains-enterprise-approval-statistics): `obtains-enterprise-approval-statistics` -- [获取企业客户的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-customer-object): `get-metadata-description-of-crm-customer-object` -- [获取企业已经加入的或申请加入中的上下游组织的信息](https://open.dingtalk.com/document/development/obtains-information-about-the-workspaces-that-the-enterprise-has-joined): `obtains-information-about-the-workspaces-that-the-enterprise-has-joined` -- [获取企业应用访问情况](https://open.dingtalk.com/document/development/queries-the-daily-usage-summary-of-microapplications-in-an-enterprise): `queries-the-daily-usage-summary-of-microapplications-in-an-enterprise` -- [获取企业待办统计数据](https://open.dingtalk.com/document/development/obtains-the-to-do-statistics-of-an-enterprise): `obtains-the-to-do-statistics-of-an-enterprise` -- [获取企业所有应用列表](https://open.dingtalk.com/document/development/manager-microapplications-api-permission): `manager-microapplications-api-permission` -- [获取企业授权信息](https://open.dingtalk.com/document/development/obtains-the-basic-information-of-an-enterprise): `obtains-the-basic-information-of-an-enterprise` -- [获取企业接收红包统计数据](https://open.dingtalk.com/document/development/queries-the-red-envelope-receiving-statistics-of-an-enterprise): `queries-the-red-envelope-receiving-statistics-of-an-enterprise` -- [获取企业文档统计数据](https://open.dingtalk.com/document/development/get-enterprise-document-statistics): `get-enterprise-document-statistics` -- [获取企业日志统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-log-statistics): `obtain-enterprise-log-statistics` -- [获取企业日程统计数据](https://open.dingtalk.com/document/development/queries-enterprise-schedule-statistics): `queries-enterprise-schedule-statistics` -- [获取企业某天的所有部门电话会议统计列表](https://open.dingtalk.com/document/development/major-customer-department-dimension-teleconference-statistics): `major-customer-department-dimension-teleconference-statistics` -- [获取企业某天的所有部门视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-list-for-key-accounts-and-departments): `video-conferencing-statistics-list-for-key-accounts-and-departments` -- [获取企业某天的电话会议数据](https://open.dingtalk.com/document/development/major-customer-teleconference-statistics-interface): `major-customer-teleconference-statistics-interface` -- [获取企业某天的视频会议统计数据](https://open.dingtalk.com/document/development/video-conferencing-statistics-query-v2-for-key-accounts): `video-conferencing-statistics-query-v2-for-key-accounts` -- [获取企业火车票订单数据](https://open.dingtalk.com/document/development/obtains-the-enterprise-train-ticket-order-data): `obtains-the-enterprise-train-ticket-order-data` -- [获取企业用户在线统计数据](https://open.dingtalk.com/document/development/retrieve-online-statistics-of-enterprise-users): `retrieve-online-statistics-of-enterprise-users` -- [获取企业用户激活状态统计数据](https://open.dingtalk.com/document/development/obtains-statistics-on-user-activation-status): `obtains-statistics-on-user-activation-status` -- [获取企业电话会议明细列表](https://open.dingtalk.com/document/development/major-account-conference-call-details-list): `major-account-conference-call-details-list` -- [获取企业电话会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-teleconference-statistics): `get-enterprise-teleconference-statistics` -- [获取企业签到统计数据](https://open.dingtalk.com/document/development/queries-enterprise-check-in-statistics): `queries-enterprise-check-in-statistics` -- [获取企业群直播统计数据](https://open.dingtalk.com/document/development/obtains-the-live-stream-statistics-for-an-enterprise-group): `obtains-the-live-stream-statistics-for-an-enterprise-group` -- [获取企业群聊统计数据](https://open.dingtalk.com/document/development/obtain-enterprise-group-chat-statistics): `obtain-enterprise-group-chat-statistics` -- [获取企业考勤统计数据](https://open.dingtalk.com/document/development/queries-enterprise-attendance-statistics): `queries-enterprise-attendance-statistics` -- [获取企业聊天数据](https://open.dingtalk.com/document/development/chat-data-statistics-query-for-key-accounts): `chat-data-statistics-query-for-key-accounts` -- [获取企业视频会议明细列表](https://open.dingtalk.com/document/development/video-conference-details-for-key-accounts): `video-conference-details-for-key-accounts` -- [获取企业视频会议统计数据](https://open.dingtalk.com/document/development/get-enterprise-video-conference-statistics): `get-enterprise-video-conference-statistics` -- [获取企业视频直播统计列表(部门维度)](https://open.dingtalk.com/document/development/live-broadcast-summary-statistics-of-key-account-departments): `live-broadcast-summary-statistics-of-key-account-departments` -- [获取企业视频直播统计数据](https://open.dingtalk.com/document/development/query-live-streaming-statistics): `query-live-streaming-statistics` -- [获取企业邮箱统计数据](https://open.dingtalk.com/document/development/queries-enterprise-email-statistics): `queries-enterprise-email-statistics` -- [获取企业部门聊天数据(部门维度)](https://open.dingtalk.com/document/development/dingtalk-chat-information-in-key-accounts): `dingtalk-chat-information-in-key-accounts` -- [获取企业钉盘统计数据](https://open.dingtalk.com/document/development/obtains-the-statistics-on-enterprise-dingtalk-trays): `obtains-the-statistics-on-enterprise-dingtalk-trays` -- [获取企业钉钉运动统计数据](https://open.dingtalk.com/document/development/queries-dingtalk-movement-statistics): `queries-dingtalk-movement-statistics` -- [获取入群二维码链接](https://open.dingtalk.com/document/development/obtain-a-qr-code-link): `obtain-a-qr-code-link` -- [获取公告ID列表](https://open.dingtalk.com/document/development/obtains-the-id-list-of-announcements-that-are-not-deleted): `obtains-the-id-list-of-announcements-that-are-not-deleted` -- [获取公告分类列表](https://open.dingtalk.com/document/development/obtains-the-list-of-categories-not-deleted-for-enterprise-announcements): `obtains-the-list-of-categories-not-deleted-for-enterprise-announcements` -- [获取公告详情](https://open.dingtalk.com/document/development/obtains-the-details-of-a-bulletin-that-is-not-deleted): `obtains-the-details-of-a-bulletin-that-is-not-deleted` -- [获取内购商品SKU页面地址](https://open.dingtalk.com/document/development/obtain-the-address-of-the-product-sku-details-page): `obtain-the-address-of-the-product-sku-details-page` -- [获取内购订单信息](https://open.dingtalk.com/document/development/obtain-information-about-internal-purchase-orders): `obtain-information-about-internal-purchase-orders` -- [获取分支组织列表](https://open.dingtalk.com/document/development/obtains-the-branch-organization-list): `obtains-the-branch-organization-list` -- [获取助理技能信息](https://open.dingtalk.com/document/development/api-getassistantactioninfo): `api-getassistantactioninfo` -- [获取单个审批实例详情](https://open.dingtalk.com/document/development/get-details-single-approval-instance): `get-details-single-approval-instance` -- [获取参与考勤人员](https://open.dingtalk.com/document/development/batch-query-of-attendance-group-members): `batch-query-of-attendance-group-members` -- [获取参与考勤人员的userid](https://open.dingtalk.com/document/development/query-attendance-group-personnel-information-in-batches): `query-attendance-group-personnel-information-in-batches` -- [获取发布智能填表数量和使用智能填表人数(部门维度)](https://open.dingtalk.com/document/development/obtains-the-number-of-tables-published-by-the-enterprise-from): `obtains-the-number-of-tables-published-by-the-enterprise-from` -- [获取员工人数](https://open.dingtalk.com/document/development/obtain-the-number-of-employees): `obtain-the-number-of-employees` -- [获取员工人数](https://open.dingtalk.com/document/development/user-management-acquires-number-employees): `user-management-acquires-number-employees` -- [获取员工离职信息](https://open.dingtalk.com/document/development/obtain-multiple-employee-demission-information): `obtain-multiple-employee-demission-information` -- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/intelligent-personnel-obtain-employee-roster-information): `intelligent-personnel-obtain-employee-roster-information` -- [获取员工花名册字段信息](https://open.dingtalk.com/document/development/obtain-employee-roster-field-information-in-batches): `obtain-employee-roster-field-information-in-batches` -- [获取图文卡片详情](https://open.dingtalk.com/document/development/get-message-card-details): `get-message-card-details` -- [获取在职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-on-the-job-employees-of-the): `intelligent-personnel-query-the-list-of-on-the-job-employees-of-the` -- [获取培训观看数据](https://open.dingtalk.com/document/development/obtains-the-playback-data-of-a-live-stream): `obtains-the-playback-data-of-a-live-stream` -- [获取培训课程的基本信息](https://open.dingtalk.com/document/development/get-basic-information-about-the-course): `get-basic-information-about-the-course` -- [获取填表实例数据](https://open.dingtalk.com/document/development/obtains-multiple-form-filling-records): `obtains-multiple-form-filling-records` -- [获取外部联系人列表](https://open.dingtalk.com/document/development/obtain-the-external-contact-list): `obtain-the-external-contact-list` -- [获取外部联系人标签列表](https://open.dingtalk.com/document/development/obtains-a-list-of-external-contact-tags): `obtains-a-list-of-external-contact-tags` -- [获取外部联系人详情](https://open.dingtalk.com/document/development/obtains-the-external-contact-details-of-an-enterprise): `obtains-the-external-contact-details-of-an-enterprise` -- [获取多个表单实例ID](https://open.dingtalk.com/document/development/obtain-the-ids-of-multiple-form-instances): `obtain-the-ids-of-multiple-form-instances` -- [获取子部门ID列表](https://open.dingtalk.com/document/development/obtain-a-sub-department-id-list): `obtain-a-sub-department-id-list` -- [获取学习的知识列表](https://open.dingtalk.com/document/development/api-getknowledgelist): `api-getknowledgelist` -- [获取学段元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-to-query): `dingtalk-the-main-data-of-the-education-ecosystem-to-query` -- [获取学生ID列表](https://open.dingtalk.com/document/development/retrieve-student-based-class): `retrieve-student-based-class` -- [获取学生信息](https://open.dingtalk.com/document/development/obtain-student-information): `obtain-student-information` -- [获取学生监护人详情](https://open.dingtalk.com/document/development/obtain-the-relationship-between-home-and-school-personnel): `obtain-the-relationship-between-home-and-school-personnel` -- [获取学科元数据列表](https://open.dingtalk.com/document/development/dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject): `dingtalk-the-main-data-of-the-education-ecosystem-query-the-subject` -- [获取学科实例列表](https://open.dingtalk.com/document/development/get-the-list-of-subject-examples): `get-the-list-of-subject-examples` -- [获取学科实例详情](https://open.dingtalk.com/document/development/query-dingtalk-education-subject-instances): `query-dingtalk-education-subject-instances` -- [获取定制应用的access_token](https://open.dingtalk.com/document/development/obtains-the-enterprise-authorized-credential): `obtains-the-enterprise-authorized-credential` -- [获取实例ID列表](https://open.dingtalk.com/document/development/obtains-a-list-of-instance-ids): `obtains-a-list-of-instance-ids` -- [获取实例详情](https://open.dingtalk.com/document/development/query-collection-form-instance-details): `query-collection-form-instance-details` -- [获取审批实例ID列表](https://open.dingtalk.com/document/development/operation-to-retrieve-a-list-of): `operation-to-retrieve-a-list-of` -- [获取审批钉盘空间信息](https://open.dingtalk.com/document/development/query-the-space-of-an-approval-nail): `query-the-space-of-an-approval-nail` -- [获取工作通知消息的发送结果](https://open.dingtalk.com/document/development/gets-the-result-of-sending-messages-asynchronously-to-the-enterprise): `gets-the-result-of-sending-messages-asynchronously-to-the-enterprise` -- [获取工作通知消息的发送进度](https://open.dingtalk.com/document/development/obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session): `obtain-the-sending-progress-of-asynchronous-sending-of-enterprise-session` -- [获取已加入或正在申请加入上下游组织的组织和个人信息](https://open.dingtalk.com/document/development/obtains-the-information-about-how-to-join-or-apply-to): `obtains-the-information-about-how-to-join-or-apply-to` -- [获取平台服务资源](https://open.dingtalk.com/document/development/obtain-platform-service-resources): `obtain-platform-service-resources` -- [获取平台资源](https://open.dingtalk.com/document/development/obtain-platform-resources): `obtain-platform-resources` -- [获取年报数据](https://open.dingtalk.com/document/development/obtain-annual-report-data): `obtain-annual-report-data` -- [获取应用下的页面列表](https://open.dingtalk.com/document/development/obtains-the-page-list-under-an-application): `obtains-the-page-list-under-an-application` -- [获取应用未激活的企业列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprises-whose-applications-are-not-activated): `obtains-a-list-of-enterprises-whose-applications-are-not-activated` -- [获取应用管理后台免登的用户信息](https://open.dingtalk.com/document/development/exchange-code-for-the-identity-information-of-a-microapplication-administrator): `exchange-code-for-the-identity-information-of-a-microapplication-administrator` -- [获取应用自定义空间使用详情](https://open.dingtalk.com/document/development/queries-the-usage-details-of-a-custom-application-space): `queries-the-usage-details-of-a-custom-application-space` -- [获取当前企业所有可管理的表单](https://open.dingtalk.com/document/development/obtains-the-information-about-all-manageable-templates-of-the-current): `obtains-the-information-about-all-manageable-templates-of-the-current` -- [获取待入职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-the-list-of-employees-to-be-hired): `intelligent-personnel-query-the-list-of-employees-to-be-hired` -- [获取微应用后台免登的access_token](https://open.dingtalk.com/document/development/obtain-the-ssotoken-for-micro-application-background-logon-free): `obtain-the-ssotoken-for-micro-application-background-logon-free` -- [获取报表假期数据](https://open.dingtalk.com/document/development/obtains-the-holiday-data-from-the-smart-attendance-report): `obtains-the-holiday-data-from-the-smart-attendance-report` -- [获取指定用户可见的审批表单列表](https://open.dingtalk.com/document/development/you-can-call-this-operation-to-retrieve-a-list-of): `you-can-call-this-operation-to-retrieve-a-list-of` -- [获取指定用户的所有父部门列表](https://open.dingtalk.com/document/development/queries-the-list-of-all-parent-departments-of-a-user): `queries-the-list-of-all-parent-departments-of-a-user` -- [获取指定角色的员工列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employees-of-a-role): `obtain-the-list-of-employees-of-a-role` -- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/queries-all-parent-departments-of-a-department): `queries-all-parent-departments-of-a-department` -- [获取指定部门的所有父部门列表](https://open.dingtalk.com/document/development/query-the-list-of-all-parent-departments-of-a-department): `query-the-list-of-all-parent-departments-of-a-department` -- [获取授权应用的基本信息](https://open.dingtalk.com/document/development/obtains-application-information-of-an-enterprise): `obtains-application-information-of-an-enterprise` -- [获取数字化证书](https://open.dingtalk.com/document/development/obtain-digital-certificate): `obtain-digital-certificate` -- [获取数字区县组织信息](https://open.dingtalk.com/document/development/querydigitaldistrictorginfo-api-reference): `querydigitaldistrictorginfo-api-reference` -- [获取文件上传信息](https://open.dingtalk.com/document/development/obtain-upload-information): `obtain-upload-information` -- [获取文件下载信息](https://open.dingtalk.com/document/development/obtain-download-file-info): `obtain-download-file-info` -- [获取文章详情](https://open.dingtalk.com/document/development/get-article): `get-article` -- [获取日志接收人员列表](https://open.dingtalk.com/document/development/queries-log-sharing-personnel): `queries-log-sharing-personnel` -- [获取日志相关人员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-log-related-personnel-by-type): `obtains-a-list-of-log-related-personnel-by-type` -- [获取日志统计数据](https://open.dingtalk.com/document/development/query-log-statistics): `query-log-statistics` -- [获取服务窗联系人信息](https://open.dingtalk.com/document/development/obtains-the-contact-information-of-the-service-window): `obtains-the-contact-information-of-the-service-window` -- [获取未活跃用户登录明细](https://open.dingtalk.com/document/development/obtains-the-logon-details-of-inactive-users): `obtains-the-logon-details-of-inactive-users` -- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/queries-the-inactive-users-or-active-users-under-an-enterprise): `queries-the-inactive-users-or-active-users-under-an-enterprise` -- [获取未登录钉钉的员工列表](https://open.dingtalk.com/document/development/query-data-of-inactive-users): `query-data-of-inactive-users` -- [获取权限列表](https://open.dingtalk.com/document/development/obtain-a-permission-list): `obtain-a-permission-list` -- [获取模板code](https://open.dingtalk.com/document/development/obtains-the-template-code-based-on-the-template-name): `obtains-the-template-code-based-on-the-template-name` -- [获取模板详情](https://open.dingtalk.com/document/development/query-template-details): `query-template-details` -- [获取流程定义](https://open.dingtalk.com/document/development/obtain-process-definition): `obtain-process-definition` -- [获取流程实例](https://open.dingtalk.com/document/development/obtain-process-instance): `obtain-process-instance` -- [获取流程节点按钮列表](https://open.dingtalk.com/document/development/obtain-a-list-of-process-node-buttons): `obtain-a-list-of-process-node-buttons` -- [获取流程设计的节点信息](https://open.dingtalk.com/document/development/obtain-the-information-about-the-nodes-in-process-design): `obtain-the-information-about-the-nodes-in-process-design` -- [获取班次摘要信息](https://open.dingtalk.com/document/development/enterprise-shift-query-in-batches): `enterprise-shift-query-in-batches` -- [获取班次详情](https://open.dingtalk.com/document/development/shift-query): `shift-query` -- [获取班级内学生的关系列表](https://open.dingtalk.com/document/development/queries-the-list-of-relationships): `queries-the-list-of-relationships` -- [获取班级圈动态列表](https://open.dingtalk.com/document/development/dynamic-list-opening-of-class-circle): `dynamic-list-opening-of-class-circle` -- [获取班级圈话题列表](https://open.dingtalk.com/document/development/obtain-a-topic-list-of-class-circles): `obtain-a-topic-list-of-class-circles` -- [获取用户创建文档数和创建文档人数(组织维度)](https://open.dingtalk.com/document/development/queries-the-number-dingtalk-documents-created-per-day-in-an): `queries-the-number-dingtalk-documents-created-per-day-in-an` -- [获取用户创建的AI助理列表](https://open.dingtalk.com/document/development/assistant-acquires-list-assistants-created-by-users): `assistant-acquires-list-assistants-created-by-users` -- [获取用户创建的填表模板](https://open.dingtalk.com/document/development/obtains-the-template-that-a-user-creates): `obtains-the-template-that-a-user-creates` -- [获取用户发送日志的概要信息](https://open.dingtalk.com/document/development/view-log-summary-data): `view-log-summary-data` -- [获取用户可查看的公告](https://open.dingtalk.com/document/development/list-the-user-s-announcement-list): `list-the-user-s-announcement-list` -- [获取用户可见的企业应用列表](https://open.dingtalk.com/document/development/list-the-microapplications-visible-to-employees): `list-the-microapplications-visible-to-employees` -- [获取用户可见范围的AI助理列表](https://open.dingtalk.com/document/development/api-listvisibleassistant): `api-listvisibleassistant` -- [获取用户基本信息](https://open.dingtalk.com/document/development/queries-basic-user-information): `queries-basic-user-information` -- [获取用户待审批数量](https://open.dingtalk.com/document/development/obtain-the-number-of-tasks-to-be-approved-by-me): `obtain-the-number-of-tasks-to-be-approved-by-me` -- [获取用户授权的持久授权码](https://open.dingtalk.com/document/development/persistent-authorization-code): `persistent-authorization-code` -- [获取用户日志未读数](https://open.dingtalk.com/document/development/querying-the-employee-s-log-is-not-reading): `querying-the-employee-s-log-is-not-reading` -- [获取用户月活跃数据](https://open.dingtalk.com/document/development/retrieves-the-user-s-monthly-active-data): `retrieves-the-user-s-monthly-active-data` -- [获取用户版本分布情况](https://open.dingtalk.com/document/development/queries-the-distribution-of-user-versions): `queries-the-distribution-of-user-versions` -- [获取用户签到记录](https://open.dingtalk.com/document/development/obtain-the-check-in-records-of-multiple-users): `obtain-the-check-in-records-of-multiple-users` -- [获取用户考勤组](https://open.dingtalk.com/document/development/queries-a-user-attendance-group): `queries-a-user-attendance-group` -- [获取申请单列表](https://open.dingtalk.com/document/development/search-enterprise-approval-form-data): `search-enterprise-approval-form-data` -- [获取申请单详情](https://open.dingtalk.com/document/development/obtains-the-detailed-data-of-a-single-request): `obtains-the-detailed-data-of-a-single-request` -- [获取离职员工列表](https://open.dingtalk.com/document/development/intelligent-personnel-query-company-turnover-list): `intelligent-personnel-query-company-turnover-list` -- [获取空间信息](https://open.dingtalk.com/document/development/obtain-space-information): `obtain-space-information` -- [获取第三方个人应用的access_token](https://open.dingtalk.com/document/development/obtain-personal-application): `obtain-personal-application` -- [获取管理员列表](https://open.dingtalk.com/document/development/obtains-a-list-of-administrators): `obtains-a-list-of-administrators` -- [获取管理员列表](https://open.dingtalk.com/document/development/query-the-administrator-list): `query-the-administrator-list` -- [获取管理员的应用管理权限](https://open.dingtalk.com/document/development/obtains-the-administrator-s-microapplication-management-permission): `obtains-the-administrator-s-microapplication-management-permission` -- [获取管理员通讯录权限范围](https://open.dingtalk.com/document/development/query-permissions-of-the-administrator-address-book): `query-permissions-of-the-administrator-address-book` -- [获取考勤报表列值](https://open.dingtalk.com/document/development/queries-the-column-value-of-the-attendance-report): `queries-the-column-value-of-the-attendance-report` -- [获取考勤报表列定义](https://open.dingtalk.com/document/development/queries-the-enterprise-attendance-report-column): `queries-the-enterprise-attendance-report-column` -- [获取考勤组详情](https://open.dingtalk.com/document/development/query-a-single-attendance-group): `query-a-single-attendance-group` -- [获取联系人的元数据](https://open.dingtalk.com/document/development/gets-the-metadata-description-of-a-crm-contact-object): `gets-the-metadata-description-of-a-crm-contact-object` -- [获取自定义对象的元数据](https://open.dingtalk.com/document/development/get-metadata-description-of-crm-custom-object): `get-metadata-description-of-crm-custom-object` -- [获取花名册元数据](https://open.dingtalk.com/document/development/intelligent-personnel-roster-metadata-query): `intelligent-personnel-roster-metadata-query` -- [获取花名册字段组详情](https://open.dingtalk.com/document/development/get-roster-field-group-details): `get-roster-field-group-details` -- [获取视频会议详情](https://open.dingtalk.com/document/development/get-details-of-the-video-conference): `get-details-of-the-video-conference` -- [获取视频直播明细列表](https://open.dingtalk.com/document/development/queries-the-details-list-of-apsaravideo-live): `queries-the-details-list-of-apsaravideo-live` -- [获取视频直播观看人员列表](https://open.dingtalk.com/document/development/query-users-of-apsaravideo-live): `query-users-of-apsaravideo-live` -- [获取角色列表](https://open.dingtalk.com/document/development/obtains-a-list-of-enterprise-roles): `obtains-a-list-of-enterprise-roles` -- [获取角色组列表](https://open.dingtalk.com/document/development/obtains-the-role-group-information): `obtains-the-role-group-information` -- [获取角色详情](https://open.dingtalk.com/document/development/queries-role-details): `queries-role-details` -- [获取课堂明细数据](https://open.dingtalk.com/document/development/obtain-course-detail-data): `obtain-course-detail-data` -- [获取课堂概要数据](https://open.dingtalk.com/document/development/get-course-summary-data): `get-course-summary-data` -- [获取课程列表](https://open.dingtalk.com/document/development/get-course-list): `get-course-list` -- [获取课程参与方列表](https://open.dingtalk.com/document/development/get-a-list-of-course-participants): `get-a-list-of-course-participants` -- [获取课程详情](https://open.dingtalk.com/document/development/get-course-details): `get-course-details` -- [获取跟进记录对象的元数据](https://open.dingtalk.com/document/development/obtains-the-metadata-description-of-the-crm-follow-up-record-object): `obtains-the-metadata-description-of-the-crm-follow-up-record-object` -- [获取通讯录权限范围](https://open.dingtalk.com/document/development/obtain-corpsecret-authorization-scope): `obtain-corpsecret-authorization-scope` -- [获取部门下人员列表](https://open.dingtalk.com/document/development/obtains-the-list-of-people-under-a-department): `obtains-the-list-of-people-under-a-department` -- [获取部门企业账号用户详情](https://open.dingtalk.com/document/development/queries-account-details): `queries-account-details` -- [获取部门列表](https://open.dingtalk.com/document/development/obtain-the-department-list): `obtain-the-department-list` -- [获取部门列表](https://open.dingtalk.com/document/development/obtains-a-list-of-industry-departments): `obtains-a-list-of-industry-departments` -- [获取部门列表](https://open.dingtalk.com/document/development/obtains-the-department-node-list): `obtains-the-department-node-list` -- [获取部门列表](https://open.dingtalk.com/document/development/user-management-acquires-the-list-departments): `user-management-acquires-the-list-departments` -- [获取部门扩展信息](https://open.dingtalk.com/document/development/obtain-department-extension-information): `obtain-department-extension-information` -- [获取部门用户userid列表](https://open.dingtalk.com/document/development/obtain-the-list-of-employee-ids-by-department-id): `obtain-the-list-of-employee-ids-by-department-id` -- [获取部门用户userid列表](https://open.dingtalk.com/document/development/query-the-list-of-department-userids): `query-the-list-of-department-userids` -- [获取部门用户基础信息](https://open.dingtalk.com/document/development/obtain-the-basic-information-of-department-users): `obtain-the-basic-information-of-department-users` -- [获取部门用户签到记录](https://open.dingtalk.com/document/development/get-check-in-data): `get-check-in-data` -- [获取部门用户详情](https://open.dingtalk.com/document/development/obtain-department-members-details): `obtain-department-members-details` -- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-department-user-details): `queries-department-user-details` -- [获取部门用户详情](https://open.dingtalk.com/document/development/queries-the-complete-information-of-a-department-user): `queries-the-complete-information-of-a-department-user` -- [获取部门的扩展字段定义](https://open.dingtalk.com/document/development/gets-the-extended-field-definition-of-a-department): `gets-the-extended-field-definition-of-a-department` -- [获取部门详情](https://open.dingtalk.com/document/development/industry-address-book-api-for-obtaining-department-information): `industry-address-book-api-for-obtaining-department-information` -- [获取部门详情](https://open.dingtalk.com/document/development/obtains-queries-department-details): `obtains-queries-department-details` -- [获取部门详情](https://open.dingtalk.com/document/development/queries-department-details): `queries-department-details` -- [获取部门详情](https://open.dingtalk.com/document/development/query-department-details0-v2): `query-department-details0-v2` -- [获取问答明细](https://open.dingtalk.com/document/development/api-getaskdetail): `api-getaskdetail` -- [获得企业创建日志相关信息(组织维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-organization): `obtains-information-about-a-created-enterprise-log-from-the-organization` -- [获得企业创建日志相关信息(部门维度)](https://open.dingtalk.com/document/development/obtains-information-about-a-created-enterprise-log-from-the-department): `obtains-information-about-a-created-enterprise-log-from-the-department` -- [获得用户创建文档数和创建文档人数(部门维度)](https://open.dingtalk.com/document/development/obtain-departmental-dimensions-documents-created-people-creating): `obtain-departmental-dimensions-documents-created-people-creating` -- [获得组织维度日程相关信息](https://open.dingtalk.com/document/development/queries-the-number-of-people-who-have-created-an-event): `queries-the-number-of-people-who-have-created-an-event` -- [解绑设备](https://open.dingtalk.com/document/development/unbind-a-smart-hardware-device): `unbind-a-smart-hardware-device` -- [计算请假时长](https://open.dingtalk.com/document/development/calculate-leave-duration): `calculate-leave-duration` -- [设备入会](https://open.dingtalk.com/document/development/equipment-membership): `equipment-membership` -- [设备账号向目标用户发送DING消息](https://open.dingtalk.com/document/development/device-publishing): `device-publishing` -- [设定角色成员管理范围](https://open.dingtalk.com/document/development/update-role-member-management-department-scope): `update-role-member-management-department-scope` -- [设置禁止群成员私聊](https://open.dingtalk.com/document/development/set-private-chat): `set-private-chat` -- [还原回收站文件(夹)](https://open.dingtalk.com/document/development/restore-recycle-bin-files-folder): `restore-recycle-bin-files-folder` -- [通知审批撤销](https://open.dingtalk.com/document/development/notify-the-attendance-to-modify-the-punch-result-when-the): `notify-the-attendance-to-modify-the-punch-result-when-the` -- [通知审批通过](https://open.dingtalk.com/document/development/notice-of-approval): `notice-of-approval` -- [通知换班通过](https://open.dingtalk.com/document/development/shift-change-operation-after-approval): `shift-change-operation-after-approval` -- [通知授权结果](https://open.dingtalk.com/document/development/notify-the-authorization-result): `notify-the-authorization-result` -- [通知补卡通过](https://open.dingtalk.com/document/development/make-up-the-card-after-approval): `make-up-the-card-after-approval` -- [通过免登码获取用户信息](https://open.dingtalk.com/document/development/obtain-the-userid-of-a-user-by-using-the-log-free): `obtain-the-userid-of-a-user-by-using-the-log-free` -- [通过免登码获取用户信息(不推荐)](https://open.dingtalk.com/document/development/get-user-userid-through-login-free-code): `get-user-userid-through-login-free-code` -- [通过调用者unionId获取激活码](https://open.dingtalk.com/document/development/obtain-the-activation-code-by-calling-the-union-id-of): `obtain-the-activation-code-by-calling-the-union-id-of` -- [通过高级查询条件获取表单实例数据(包括子表单组件数据)](https://open.dingtalk.com/document/development/query-form-instances-using-advanced-search-conditions): `query-form-instances-using-advanced-search-conditions` -- [邀请其他组织企业账号加入](https://open.dingtalk.com/document/development/invite-other-organization-specific-accounts-to-join): `invite-other-organization-specific-accounts-to-join` -- [配置发票适用人群](https://open.dingtalk.com/document/development/configure-invoice-users): `configure-invoice-users` -- [重新授权未激活应用的企业](https://open.dingtalk.com/document/development/re-authorize-enterprises-whose-applications-are-not-activated): `re-authorize-enterprises-whose-applications-are-not-activated` -- [针对单个日程进行签到](https://open.dingtalk.com/document/development/sign-in-for-a-single-schedule): `sign-in-for-a-single-schedule` -- [钉钉文本翻译](https://open.dingtalk.com/document/development/dingtalk-translation): `dingtalk-translation` -- [静态推荐数据同步](https://open.dingtalk.com/document/development/statically-recommended-data-synchronization): `statically-recommended-data-synchronization` -- [预计算时长](https://open.dingtalk.com/document/development/calculate-duration-based-on-attendance-scheduling): `calculate-duration-based-on-attendance-scheduling` -- [验证激活结果](https://open.dingtalk.com/document/development/verify-activation-results): `verify-activation-results` - -> 已知 Gap:7 条 slug 在 development 段为空壳(<50000B),可能属别的 section(如 solution),待补正确 section,本目录暂不收录。 - - diff --git a/skills/dingtalk-open-platform/references/api-invocation.md b/skills/dingtalk-open-platform/references/api-invocation.md deleted file mode 100644 index 62d8d818..00000000 --- a/skills/dingtalk-open-platform/references/api-invocation.md +++ /dev/null @@ -1,57 +0,0 @@ -# API 调用层(Invocation)— HTTP 契约 1:1 映射 - -文档里的 HTTP 主契约(Method + Path + Body)可直接落成 `dws` 命令或原始 HTTPS 请求,无需猜 SDK。`dws` 是可选 CLI,与直接发 HTTP 等价。 - -## 1. 原始直通:dws api - -```bash -# api.dingtalk.com:token 走 Header (x-acs-dingtalk-access-token),路径 /v1.0|/v2.0 -dws api GET /v1.0/contact/users/me --format json -dws api POST /v1.0/contact/users/search \ - --data '{"queryWord":"张三","offset":0,"size":10}' --format json - -# oapi.dingtalk.com:token 走 URL 参数 (access_token),路径 /topapi/... -dws api POST /topapi/v2/user/get --data '{"userid":""}' --format json -``` - -| 域名 | token 传递 | 路径形态 | -| --- | --- | --- | -| `api.dingtalk.com` | Header `x-acs-dingtalk-access-token` | `/v1.0/xxx`、`/v2.0/xxx` | -| `oapi.dingtalk.com` | URL 参数 `access_token` | `/topapi/...` 或完整 URL | - -- raw api 仅限自有应用凭证(见 [auth.md](auth.md))。 - -## 2. 从文档契约到命令(1:1) - -| 文档字段 | 命令位置 | -| --- | --- | -| Method | `dws api {METHOD}` | -| URL Path | `dws api {METHOD} {PATH}` | -| Body (JSON) | `--data '{...}'`(保持嵌套,不要展平)| -| Header token | 由 `dws` 注入,不要手填 | -| 输出解析 | `--format json` + 可选 `--jq` | - -## 3. 产品糖命令优先 - -高频能力可用 `dws ...` 糖命令(自带翻页/轮询/校验),而非手搓 `dws api`,命令树与 flag 以 `dws --help` 为准: - -- 应用管理 → `dws devapp ...`(开放平台应用 CRUD / 凭证 / 权限 / 机器人 / 版本) -- 通讯录 / 日历 / 审批 / 消息等 → `dws contact|calendar|oa|chat ...` - -`dws api` 用于:糖命令未覆盖、或文档明确给 HTTP 契约要 1:1 复现时。 - -## 4. 命令发现(事实源) - -```bash -dws --help # 人读:Usage / Example / Flags -dws schema # 机读:所有产品与工具 -dws schema . # 单工具 JSON Schema -dws schema --jq '.tool.required' # 只看必填字段 -``` - -> 参考文档里的 flag 列表是便于理解,不是契约。冲突时**以 `--help` / `dws schema` 为准**。 - -## 5. 写操作铁律 - -- 写/删/发/审批类先 `--dry-run --format json` 展示摘要 → 用户确认 → 加 `--yes`(详见 [workflow.md](workflow.md))。 -- 单次批量 ≤ 30 条。 diff --git a/skills/dingtalk-open-platform/references/auth.md b/skills/dingtalk-open-platform/references/auth.md deleted file mode 100644 index b24d3b69..00000000 --- a/skills/dingtalk-open-platform/references/auth.md +++ /dev/null @@ -1,71 +0,0 @@ -# 鉴权与凭证(Auth & Credentials)— 前置必读 - -调用前先把鉴权与凭证准备好;凭证视为敏感,绝不外泄。 - -## 1. 登录与 token 状态 - -```bash -dws auth status --format json # 查看当前登录态与身份 -dws auth login # 扫码 / Device Flow 登录,自动刷新 token -dws auth logout # 清除认证信息 -dws auth reset # 重置认证(清本地 token,触发重新授权) -``` - -| 子命令 | 用途 | -| --- | --- | -| `status` | 调用前确认是否已登录、token 是否有效 | -| `login` | 未登录 / `token is illegal` / 401 时执行 | -| `export` / `import` | 迁移认证包(跨机器) | - -- token 过期或 `token is illegal`:执行 `dws auth login`,**不要**把 token 贴进对话或日志。 - -## 1b. access_token 端点(HTTP 原始换取) - -```http -# 新版(推荐):token 用于业务请求 Header x-acs-dingtalk-access-token -POST https://api.dingtalk.com/v1.0/oauth2/accessToken -Content-Type: application/json - -{"appKey":"","appSecret":""} -``` -```http -# 旧版:token 用于业务请求 query ?access_token= -GET https://oapi.dingtalk.com/gettoken?appkey=&appsecret= -``` - -- token 必须缓存复用,禁止每请求重换(高频换取会触发限流)。 -- 文档:[获取企业内部应用 accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-an-internal-app) · [获取第三方应用授权企业 accessToken](https://open.dingtalk.com/document/development/obtain-the-access-token-of-the-authorized-enterprise-1)。 -- 用 dws 时一般无需手动换取,`dws api` 用自有应用凭证登录后自动注入;凭证读取见 §3。 - -## 2. token 类型 - -| 类型 | 主体 | 典型用途 | -| --- | --- | --- | -| `app` / `tenant` | 企业自建应用 | 大多数服务端 OpenAPI | -| `user` | 用户授权 | 读当前用户数据、网页授权 | -| `isv` | 第三方应用 | ISV 套件场景 | - -> 应用类型(企业自建 vs 第三方)查询 API 缺失是高频凭证混淆来源(gap #4),不确定时先 `dws devapp get` 看应用属性。 - -## 3. 应用凭证读取(敏感) - -```bash -dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -``` - -- 返回含 `clientId` / `clientSecret`(=`appSecret`),**按敏感凭证处理**:仅用于构造 `dws api` 的 `--client-id/--client-secret`,不回显、不写文件。 -- 不能用 `dws devapp get` 代替(`get` 不返回完整 secret)。 -- `unifiedAppId` 来自 `dws devapp list --name X --format json`,多条命中先让用户指定。 - -## 4. raw API 调用的凭证约束 - -- `dws api` 仅限**自有应用凭证**(`--client-id/--client-secret` 登录后)使用。 -- 通过 MCP 默认凭证登录获取的加密 token **不支持** raw API 调用 → 改用对应产品糖命令或先用自有应用登录。 - -## 常见错误 - -| 现象 | 应对 | -| --- | --- | -| `token is illegal` / 401 | `dws auth status` → `dws auth login` | -| raw api 报 token 不支持 | 用 `--client-id/--client-secret` 自有应用凭证 | -| 凭证泄露风险 | 只走 `credentials get`,输出不回显 secret | diff --git a/skills/dingtalk-open-platform/references/checklist.md b/skills/dingtalk-open-platform/references/checklist.md deleted file mode 100644 index bb9d573b..00000000 --- a/skills/dingtalk-open-platform/references/checklist.md +++ /dev/null @@ -1,27 +0,0 @@ -# Verification Checklist — 调用 / 上线前勾选 - -每次开放平台调用或交付前逐项核对,任一不过则停止并补齐。 - -## 发现 / 契约 -- [ ] 已在 [llm.md](../llm.md) 或官方文档定位到目标接口(method / path / 文档链接)。 -- [ ] 已解析稳定资源 ID(unifiedAppId / userId / unionId / nodeId 等),来自返回值,非编造。 - -## 鉴权 / 权限 -- [ ] 已确认 token 类型(应用 / 用户 / JSAPI)与 base URL(新版 Header / 旧版 query)匹配。 -- [ ] 已确认所需 `scopeValue` 已授权(`dws devapp permission list`);缺则已申请并发版。 -- [ ] 已确认数据范围(data_range),未默认全员。 -- [ ] 凭证未出现在对话 / 日志 / prompt。 - -## 调用 / Header / 限流 -- [ ] 必需 Header 正确(`x-acs-dingtalk-access-token` + `Content-Type`)。 -- [ ] 分页 / 批量大小符合接口约定;限流退避已考虑。 -- [ ] token 已缓存复用,未每请求重换。 - -## 写操作 / 安全 -- [ ] 写 / 删 / 发 / 发布 / 撤回 已先 `--dry-run` 展示摘要并经用户当前轮确认([危险])。 -- [ ] 不可逆操作目标精确(无批量误伤)。 - -## 验证 / 错误处理 -- [ ] readback 三态明确:成功 / needs_permission / needs_resource。 -- [ ] 错误处理覆盖鉴权 / 权限 / 限流 / 参数 / 平台错误;保留 `requestId`。 -- [ ] 能回读则已回读校验;不一致字段已报告。 diff --git a/skills/dingtalk-open-platform/references/discovery.md b/skills/dingtalk-open-platform/references/discovery.md deleted file mode 100644 index 851e3e79..00000000 --- a/skills/dingtalk-open-platform/references/discovery.md +++ /dev/null @@ -1,43 +0,0 @@ -# 发现层(Discovery)— 找到接口与文档 - -先从文档目录定位能力域与接口,再下钻到单接口官方文档页。 - -## 发现优先级 - -1. **文档目录(首选,离线可用)**:[llm.md](../llm.md) = [api-catalog.md](api-catalog.md) 同源 — 全量**有效**官方文档链接(经内容长度校验,按 8 能力域 + 功能子类归类),URL 模板 `https://open.dingtalk.com/document/development/`。 -2. **运行态搜索**:`dws devdoc article search`(命中即返回标题/摘要/文档链接)。 -3. **官方文档站**:`https://open.dingtalk.com/document`。 - -## devdoc article search - -```bash -dws devdoc article search "MCP" --format json -dws devdoc article search --query "OAuth2 接入" --format json -dws devdoc article search --query "消息卡片" --page 2 --size 5 --format json -``` - -| flag | 说明 | -| --- | --- | -| 位置参数 / `--query` / `--keyword` | 搜索关键词(必填,建议传用户原话里的 API 名/能力名/错误码)| -| `--page` | 页码,从 1 开始,默认 1 | -| `--size` | 每页条数,默认 10 | - -- 只做**搜索**,不做读取;命中条目返回标题、摘要、`doc_link`,由 Agent 引用链接或进一步浏览。 -- devdoc 查的是**开放平台开发者文档**(面向研发),不要拿它查业务数据(那是 doc / aitable / report)。 - -## 运行态风险与降级 - -- 真实调用若返回 `PARAM_ERROR - 未找到指定工具` / Forbidden(pre-mcp-gw):这是**网关/工具注册未闭环**,不是用户参数问题。 -- 处理:标记 `needs_gateway_tool_registration`,降级到 [llm.md](../llm.md) 本地目录或已挂载的逐页 Markdown。 -- `--help` / `--dry-run` 只证明 CLI 命令面与映射存在,**不**证明后端工具已注册。 - -## 上线/注入验收(六步门禁) - -```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 --format json || true -dws schema devdoc.search_open_error_code_rag --format json || true -# real call:未注册则记录 needs_gateway_tool_registration -``` diff --git a/skills/dingtalk-open-platform/references/gotchas.md b/skills/dingtalk-open-platform/references/gotchas.md deleted file mode 100644 index 6ca9e754..00000000 --- a/skills/dingtalk-open-platform/references/gotchas.md +++ /dev/null @@ -1,17 +0,0 @@ -# Common Gotchas — 钉钉开放平台真实坑 - -钉钉开放平台调用高频踩坑,来自线上失败归因与排错沉淀。格式:错 → 对 → 后果。 - -| 坑 | 错误做法 | 正确做法 | 后果 | -| --- | --- | --- | --- | -| 凭证混用 | 用 MCP 默认(加密)token 调 `dws api` raw 接口 | `dws api` 用自有应用 `--client-id/--client-secret` 登录 | raw 调用被拒 | -| scope 未开通 | 直接调用未授权接口 | 先 `dws devapp permission add` 申请并发版 | 403 / 无权限 | -| 新旧 path 混用 | Header token 配 `oapi` query 接口(或反之)| 新版走 `api.dingtalk.com` Header;旧版走 `oapi` query | 鉴权失败 | -| token 类型用错 | 用应用 token 调个人数据 / 免登接口 | 个人数据用用户身份,端内用 JSAPI | 无权限 | -| 权限名当 scope | `--permissions 机器人发消息` | 只传 `scopeValue`(如 `qyapi_robot_sendmsg`)| 申请失败 | -| 数据范围默认全员 | 假设拥有全员可见范围 | 显式确认 data_range,按授权范围调用 | 越权 / 空数据 | -| 多候选选第一个 | 名称命中多条直接取第一条 | 展示候选,停止写操作,等用户指定 | 操作错对象 | -| 写操作未 dry-run | 直接 `--yes` 执行写/删/发 | 先 `--dry-run` 展示摘要 → 确认 → `--yes` | 不可逆误操作 | -| 丢失 requestId | 报错后不留痕 | 保留 `requestId` 交 `devdoc error diagnose` | 无法链路定位 | -| 高频刷新 token | 每请求重新换 token | token 必须缓存复用 | 触发限流 | -| devdoc 网关未注册 | 把 `PARAM_ERROR - 未找到指定工具` 当用户参数错 | 记 `needs_gateway_tool_registration`,降级 [llm.md](../llm.md) | 误判阻塞原因 | diff --git a/skills/dingtalk-open-platform/references/quick-reference.md b/skills/dingtalk-open-platform/references/quick-reference.md deleted file mode 100644 index faee4388..00000000 --- a/skills/dingtalk-open-platform/references/quick-reference.md +++ /dev/null @@ -1,51 +0,0 @@ -# Quick Reference — 应用类型 / Header / 限流 / CLI - -## 应用类型 - -来自各接口「权限」表(企业内部应用 / 第三方企业应用 / 第三方个人应用)与端内能力实测。 - -| 应用类型 | 适用 | 鉴权 / 身份 | 典型场景 | -| --- | --- | --- | --- | -| 企业内部应用 | 单企业自建 | 企业内部应用 `accessToken`(appKey/appSecret 换取) | 服务端调组织数据、机器人、文档 | -| 第三方企业应用 | ISV 多企业 | 授权企业 `accessToken`(suiteTicket / 授权码换取) | 上架应用市场、跨企业服务 | -| 第三方个人应用 | 面向个人用户 | 个人授权(OAuth) | 个人维度能力(部分接口标「暂不支持」)| -| 移动接入应用 | 钉钉端内 H5 / 小程序 / 酷应用 | JSAPI 免登 ticket / 免登 code | 端内免登、容器 JSAPI 能力 | - -> 选型见 [SKILL.md](../SKILL.md) §5 Decision guidance;应用增删改管理用 `dws devapp ...`(以 `dws devapp --help` 为准)。 - -## Header / 限流 / 分页 {#header限流分页} - -### 必需 Header - -| 域名 | token 传递 | 其它 | -| --- | --- | --- | -| `api.dingtalk.com`(新版)| Header `x-acs-dingtalk-access-token: ` | `Content-Type: application/json` | -| `oapi.dingtalk.com`(旧版)| query `?access_token=` | 路径 `/topapi/...` | - -### 限流 - -- 每接口有 `maxQps`(官方文档 / API Explorer 标注,部分接口 `doc_open_api.extension.maxQps` 可查)。 -- 触发限流返回 429 / 限流码:遵守 `Retry-After`,指数退避,单命令重试 ≤ 3 次。 -- 幂等键无平台级统一约定 → 写接口自动重试前优先 readback 确认,避免重复写。 - -### 分页 / 大小 - -- 分页两类:`offset`/`size` 偏移式;`nextToken`/`maxResults` 游标式(以接口文档为准)。 -- 批量大小遵循各接口文档上限;dws 侧批量操作建议单次 ≤ 30 条。 - -## dws CLI 速查(均 `--help` 核验) - -```bash -dws --help # 顶层产品与 utility 命令 -dws auth status --format json # 登录态 / 当前身份 -dws auth login [--device] # 扫码 / 设备流登录 -dws schema [.] # MCP 工具 JSON Schema(必填字段 / flag 别名) -dws devdoc article search "<关键词>" --format json # 查开发文档 -dws devdoc error diagnose --error-code --query "..." --format json # 错误排查 -dws api --data '' --format json # 原始 OpenAPI 直通 -dws devapp list --name <应用名> --format json # 开放平台应用 CRUD / 凭证 / 权限 -dws doc read --node --content-format markdown --format json # 读钉钉文档 -``` - -- `dws api` 仅认自有应用凭证(`--client-id/--client-secret` 登录),MCP 默认加密 token 不支持 raw 调用。 -- 命令事实源以 `dws --help` / `dws schema` 为准;参考文档与之冲突时以 `--help` 为准。 diff --git a/skills/dingtalk-open-platform/references/reference.md b/skills/dingtalk-open-platform/references/reference.md deleted file mode 100644 index fa8c9a93..00000000 --- a/skills/dingtalk-open-platform/references/reference.md +++ /dev/null @@ -1,61 +0,0 @@ -# 一等公民:错误码 / 状态码 / 限流 / 版本化 - -把 Status codes / Request limits / Versioning / Webhooks 作为一等公民单列,调用前明确契约边界。 - -## 1. 错误诊断:devdoc error diagnose - -```bash -dws devdoc error diagnose --error-code "40014" --query "获取用户信息 access_token" --format json -dws devdoc error diagnose --request-id "" --query "接口调用失败" --format json -dws devdoc error diagnose --error-message "token is illegal" --api "获取用户信息" --format json -``` - -| flag | 说明 | -| --- | --- | -| `--query` | 原始排查问题(必填语义)| -| `--error-code` | 错误码 | -| `--error-message` | 错误描述,合并进问题 | -| `--api` | API 名称,合并进问题 | -| `--request-id` | 开放平台 requestId(不是所有接口都返回)| -| `--context` | 额外排查上下文 | - -- 后端返回 `PARAM_ERROR - 未找到指定工具` / `unknown tool` → 记 `needs_gateway_tool_registration`,降级 `devdoc article search` 或本 skill 的 [llm.md](../llm.md) 目录。 - -## 2. 错误码三元组(自恢复) - -每个错误码应能回答:**触发原因 / 排查路径 / next_action**;未知错误码写 `源文档未提供`,不要编造。 - -| 错误码 | 触发原因 | next_action | -| --- | --- | --- | -| `40014` | access_token 无效/过期 | `dws auth login`,自有应用重取凭证 | -| `token is illegal` | 未登录 / token 失效 | `dws auth status` → `dws auth login` | -| `PARAM_ERROR - 未找到指定工具` | 网关工具未注册 | 记 `needs_gateway_tool_registration`,降级本地索引 | -| `ServiceResult.success=false` | 业务校验失败 | 透传 `errorCode/errorMsg`,按总表排查 | - -> 未知错误码写 `源文档未提供`,不要编造原因或 next_action(gap #3:错误码总表数据仍在增量建设)。 - -## 3. 状态码 / HTTP 语义 - -| HTTP | 含义 | 处理 | -| --- | --- | --- | -| 2xx | 成功 | 校验业务成功字段后继续 | -| 401 / 403 | 鉴权 / 权限不足 | `auth login` 或 `devapp permission add` | -| 429 | 限流 | 退避重试(见限流)| -| 5xx | 平台错误 | 附 requestId 回流,不反复变通 | - -## 4. 限流(Request limits) - -- 每接口 `maxQps` 写在 `doc_open_api.extension`(如 `{"maxQps":100}`);批量调用前先看 QPS。 -- 429 / 限流:指数退避重试,单命令重试 ≤ 3 次;超限停止并报告。 -- 幂等键无平台级统一约定(gap #10)→ 写接口自动重试需谨慎,优先 readback 确认而非盲目重发。 - -## 5. 版本化(Versioning) - -- 路径版本:`/v1.0/` 与 `/v2.0/` 并存;旧 `dingtalk.oapi.*` 与 `/topapi/*` 为老接口面。 -- `dws upgrade`:能力缺失 / 新接口未注册时,先升级 dws 再试(新能力先在新版本上线)。 -- 应用版本发布走 `dws devapp version ...`(命令树以 `dws devapp version --help` 为准)。 - -## 6. 事件 / 回调(Webhooks) - -- 事件订阅 `dws devapp event ...` 为**待实现能力**(后端待发布)→ 标 `TODO(待验证)`,不要当可用。 -- 企业回调相关接口见本 skill [llm.md](../llm.md) 的相应能力域。 diff --git a/skills/dingtalk-open-platform/references/workflow.md b/skills/dingtalk-open-platform/references/workflow.md deleted file mode 100644 index 36704113..00000000 --- a/skills/dingtalk-open-platform/references/workflow.md +++ /dev/null @@ -1,84 +0,0 @@ -# 端到端工作流(发现 → 前置 → 调用 → 验证 → 恢复) - -把运行闭环固化为可复制步骤。所有 dws 命令均 `--help` 核验;dws 仅为可选 CLI,与直接发 HTTP 等价。 - -## 0. 拿 access_token(调用前置) - -```http -# 新版(推荐):token 走业务请求 Header -POST https://api.dingtalk.com/v1.0/oauth2/accessToken -Content-Type: application/json - -{"appKey":"","appSecret":""} -``` -```http -# 旧版:token 走 query access_token -GET https://oapi.dingtalk.com/gettoken?appkey=&appsecret= -``` -- token 必须缓存,禁止每请求重换;日志只记 token hash / operation / path / errcode / requestId。 -- 用 dws 时凭证读取走 `dws devapp credentials get --unified-app-id --format json`(敏感,不回显)。 - -## 1. 只读接口(最常见) - -```bash -# 发现:先查 llm.md 定位文档,运行态可用时: -dws devdoc article search --query "获取用户信息" --format json -# 前置:未登录则 dws auth login(无头环境 dws auth login --device) -dws auth status --format json -# 调用: -dws api GET /v1.0/contact/users/me --format json -# 验证:HTTP 2xx + 业务字段;失败转 §4 -``` - -## 2. 写接口(setup → target → verify → cleanup) - -```bash -dws devapp permission list --unified-app-id ID --status UNAUTHED --format json -dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json -# → 展示摘要 → 用户当前轮确认 → 加 --yes -dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --yes --format json -dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json # verify -# cleanup:测试资源用完回收(取消权限 / 删除测试应用属 [危险],需确认) -``` - -## 3. readback 三态 - -| 态 | 判据 | 下一步 | -| --- | --- | --- | -| 成功 | HTTP 2xx + 业务成功字段 | 继续后续动作 | -| `needs_permission` | 401/403 / 权限未授 | `dws devapp permission add`(→ §2)| -| `needs_resource` | 缺前置 ID / 资源 | 按文档 `resource_requirements` 用 `dws ...` 获取 | - -## 4. 恢复与回流 - -1. 失败先加 `--verbose` 重试**一次**。 -2. 有 requestId → `dws devdoc error diagnose --request-id --query "..." --format json`([reference.md](reference.md))。 -3. 按错误码三元组取 next_action 自恢复;网关未注册记 `needs_gateway_tool_registration`。 -4. 同一命令重试 **≤ 3 次**;仍失败 → 停止,附完整错误与 requestId 报告。 - -## 5. 危险操作确认清单([危险],必须用户确认) - -| 产品域 | 命令 | 影响 | -| --- | --- | --- | -| devapp | `delete` | 删除应用(异步生效,不可逆)| -| devapp | `inactive` | 停用应用 | -| devapp | `permission remove` | 取消已授权限 | -| devapp | `version publish` | 发布版本(含高敏权限需 `--confirm-sensitive`)| -| devapp | `member remove` | 移除应用成员 | -| devapp | `robot offline` | 下线机器人 | -| api | 任意 `POST/PUT/PATCH/DELETE` 写接口 | 按接口 side_effect 评估,先 `--dry-run` | - -确认流程:`Step 1 展示摘要(操作+目标+影响)→ Step 2 用户明确确认 → Step 3 加 --yes 执行`。 - -## 6. 完整示例(创建机器人应用) - -```text -devapp create --dry-run → 确认 → create --yes - → devapp get 确认 unifiedAppId - → credentials get(敏感,不回显) - → permission add(机器人发消息,dry-run → 确认 → --yes) - → robot create(dry-run → 确认 → --yes) - → version create → check-approval → publish(确认)→ status 验证 -``` - -> 应用管理每步 flag 细节以 `dws devapp <子命令> --help` 为准。 From 6480c035fae7d1ea73e3b19cbf170805856ab5cd Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Mon, 15 Jun 2026 20:29:50 +0800 Subject: [PATCH 04/47] Fix review problem --- docs/devapp-agent-install-guide.md | 6 +- docs/devapp-yulan-command-routing.md | 20 ++-- internal/helpers/devapp.go | 36 ++++--- internal/helpers/devapp_test.go | 96 +++++++++++++++++-- skills/dingtalk-devapp/references/event.md | 2 +- skills/dingtalk-devapp/references/robot.md | 10 +- skills/dingtalk-devapp/references/version.md | 11 ++- .../dingtalk-devapp/references/workflows.md | 2 +- skills/mono/references/products/devapp.md | 11 ++- .../multi/dingtalk-devapp/references/event.md | 2 +- .../multi/dingtalk-devapp/references/robot.md | 10 +- .../dingtalk-devapp/references/version.md | 11 ++- .../dingtalk-devapp/references/workflows.md | 2 +- 13 files changed, 147 insertions(+), 72 deletions(-) diff --git a/docs/devapp-agent-install-guide.md b/docs/devapp-agent-install-guide.md index 80d940bb..bac9f19a 100644 --- a/docs/devapp-agent-install-guide.md +++ b/docs/devapp-agent-install-guide.md @@ -104,7 +104,7 @@ DevApp 是开放平台应用管理能力的 CLI 和 Agent Skill 入口。安装 ``` ```text -帮我给 agentId= 的应用配置机器人,先 dry-run 给我确认。 +帮我给 unifiedAppId= 的应用配置机器人,先 dry-run 给我确认。 ``` ```text @@ -192,7 +192,6 @@ dws devapp permission remove --agent-id --scope-value Contact.User.mob ```bash dws devapp robot get --unified-app-id --format json dws devapp robot config --unified-app-id --name "机器人名称" --dry-run --format json -dws devapp robot update --unified-app-id --brief "新的简介" --dry-run --format json dws devapp robot enable --unified-app-id --name "机器人名称" --dry-run --format json dws devapp robot offline --unified-app-id --dry-run --format json ``` @@ -211,6 +210,7 @@ dws devapp security config --unified-app-id --ip-white-list ```bash dws devapp version list --unified-app-id --format json +dws devapp version list --unified-app-id --cursor --format json dws devapp version create --unified-app-id --dry-run --format json dws devapp version publish --unified-app-id --version-id --dry-run --format json dws devapp version status --unified-app-id --version-id --format json @@ -272,7 +272,7 @@ dws devapp robot get --unified-app-id --format json ``` 如果机器人不存在,使用 `robot config` 创建或配置。 -如果机器人已存在,用 `robot update` 修改配置,或用 `robot enable` 重新启用。 +如果机器人已存在,继续用 `robot config` 修改配置,或用 `robot enable` 重新启用。 ## 页面文案建议 diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 3455b635..4f95c984 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -93,12 +93,11 @@ dws devapp robot submit Submit async robot-create task (supports retry by taskId) robot result Query async robot-create task result robot get Get robot config of an existing app - robot config Create robot config on an existing app - robot update Update robot config + robot config Create or update robot config on an existing app robot enable Enable / re-enable robot capability robot offline Offline robot capability version create Save app version - version list List app versions (paged) + version list List app versions (cursor paged) version get Get one version detail version check-approval Precheck publish approval requirements (no publish) version publish Submit publish, possibly creating approval @@ -141,7 +140,7 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Permission remove | `devapp permission remove` | `remove_open_dev_app_permission` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | | Events | `devapp event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | Implemented | Hardcoded helper | | Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | -| Robot config | `devapp robot get/config/update/enable/offline` | `get/create/update_open_dev_app_robot_config`, `enable/offline_open_dev_app_robot` | Implemented | Hardcoded helper | +| Robot config | `devapp robot get/config/enable/offline` | `get/set_open_dev_app_robot_config`, `enable/offline_open_dev_app_robot` | Implemented | Hardcoded helper | | Version | `devapp version create/list/get/check-approval/publish/status` | `create/list_open_dev_app_versions`, `get_open_dev_app_version_detail/status`, `publish_open_dev_app_version` | Implemented | Hardcoded helper | P0 for yulan is app CRUD, permissions, credentials, and web app config. Robot, @@ -1023,13 +1022,13 @@ Implemented version tools (verified against the `op-app` MCP `tools/list`): | CLI | MCP tool | Notes | | --- | --- | --- | | `devapp version create` | `create_open_dev_app_version` | Save a new version from current config (`version`, `description`). | -| `devapp version list` | `list_open_dev_app_versions` | Paged list (`currentPage`, `pageSize`). | +| `devapp version list` | `list_open_dev_app_versions` | Cursor-paged list (`cursor`, `pageSize`; response `items/nextCursor/hasMore`). | | `devapp version get` | `get_open_dev_app_version_detail` | One version detail by `versionId`. | -| `devapp version check-approval` | `publish_open_dev_app_version` (`dryRun=true`) | Precheck approval requirement / approvers; does not publish. | -| `devapp version publish` | `publish_open_dev_app_version` (`dryRun=false`) | Publish; `confirmedSensitive` for high-sensitivity scopes, optional `approverUserId`. | +| `devapp version check-approval` | `publish_open_dev_app_version` (`precheckOnly=true`) | Precheck approval requirement / approvers; does not publish. | +| `devapp version publish` | `publish_open_dev_app_version` (`precheckOnly=false`) | Publish; `confirmedSensitive` for high-sensitivity scopes, optional `approverUserId`. | | `devapp version status` | `get_open_dev_app_version_status` | Poll publish and approval state. | -Note: the MCP `dryRun` field is the server-side approval precheck and maps to +Note: the MCP `precheckOnly` field is the server-side approval precheck and maps to `version check-approval`. It is distinct from the CLI global `--dry-run` (preview without execution). `check-approval` is read-only (no write guard); `publish` uses the standard `--dry-run`/`--yes` write guard. @@ -1042,12 +1041,11 @@ Implemented robot tools: | `devapp robot submit` | `submit_robot_create_task` | Async submit; supports retry via original `taskId`. | | `devapp robot result` | `query_robot_create_result` | Poll async task by `taskId`. | | `devapp robot get` | `get_open_dev_app_robot_config` | Read robot config of an existing app. | -| `devapp robot config` | `create_open_dev_app_robot_config` | Create robot config on an existing app. | -| `devapp robot update` | `update_open_dev_app_robot_config` | Update robot config. | +| `devapp robot config` | `set_open_dev_app_robot_config` | Create or update robot config on an existing app. | | `devapp robot enable` | `enable_open_dev_app_robot` | Enable / re-enable robot capability. | | `devapp robot offline` | `offline_open_dev_app_robot` | Offline robot capability. | -Robot create/submit and config/update/enable/offline are write operations and +Robot create/submit and config/enable/offline are write operations and use the `--dry-run`/`--yes` guard; `get` and `result` are reads. Stitching rule: diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index a4c33f42..27ffeb67 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -34,14 +34,13 @@ const ( devAppSecurityConfigTool = "update_app_security_config" // 机器人能力(op-app MCP 工具,硬编码不走服务发现)。 - devAppRobotCreateTool = "create_dingtalk_robot" - devAppRobotSubmitTool = "submit_robot_create_task" - devAppRobotResultTool = "query_robot_create_result" - devAppRobotConfigGetTool = "get_open_dev_app_robot_config" - devAppRobotConfigCreateTool = "create_open_dev_app_robot_config" - devAppRobotConfigUpdateTool = "update_open_dev_app_robot_config" - devAppRobotEnableTool = "enable_open_dev_app_robot" - devAppRobotOfflineTool = "offline_open_dev_app_robot" + devAppRobotCreateTool = "create_dingtalk_robot" + devAppRobotSubmitTool = "submit_robot_create_task" + devAppRobotResultTool = "query_robot_create_result" + devAppRobotConfigGetTool = "get_open_dev_app_robot_config" + devAppRobotConfigSetTool = "set_open_dev_app_robot_config" + devAppRobotEnableTool = "enable_open_dev_app_robot" + devAppRobotOfflineTool = "offline_open_dev_app_robot" // 版本发布能力(op-app MCP 工具,硬编码不走服务发现)。 devAppVersionCreateTool = "create_open_dev_app_version" @@ -172,8 +171,7 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { newDevAppRobotSubmitCommand(runner), newDevAppRobotResultCommand(runner), newDevAppRobotConfigGetCommand(runner), - newDevAppRobotConfigCommand(runner, "config", "为现有应用创建机器人配置", devAppRobotConfigCreateTool), - newDevAppRobotConfigCommand(runner, "update", "更新现有应用机器人配置", devAppRobotConfigUpdateTool), + newDevAppRobotConfigCommand(runner, "config", "创建或更新现有应用机器人配置", devAppRobotConfigSetTool), newDevAppRobotConfigCommand(runner, "enable", "启用现有应用机器人能力", devAppRobotEnableTool), newDevAppRobotOfflineCommand(runner), newDevAppRobotConnectCommand(runner), @@ -966,8 +964,8 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { func newDevAppVersionListCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "list", - Short: "分页查询应用版本列表", - Example: " dws devapp version list --unified-app-id UNIFIED_APP_ID --page 1 --page-size 20 --format json", + Short: "按 cursor 查询应用版本列表", + Example: " dws devapp version list --unified-app-id UNIFIED_APP_ID --page-size 20 --format json\n dws devapp version list --unified-app-id UNIFIED_APP_ID --cursor NEXT_CURSOR --page-size 20 --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -977,14 +975,14 @@ func newDevAppVersionListCommand(runner executor.Runner) *cobra.Command { } params := map[string]any{ "unifiedAppId": appID, - "currentPage": devAppIntFlag(cmd, "page"), "pageSize": devAppIntFlag(cmd, "page-size"), } + devAppPutString(params, "cursor", devAppStringFlag(cmd, "cursor")) return runDevAppTool(runner, cmd, devAppVersionListTool, params) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().Int("page", 1, "分页页码,从 1 开始") + cmd.Flags().String("cursor", "", "服务端返回的不透明分页游标;首次查询留空") cmd.Flags().Int("page-size", 20, "分页大小") preferLegacyLeaf(cmd) return cmd @@ -1022,8 +1020,8 @@ func newDevAppVersionCheckApprovalCommand(runner executor.Runner) *cobra.Command if err != nil { return err } - // 复用 publish 工具的服务端预检模式:dryRun=true 只返回审批要求,不发布。 - params["dryRun"] = true + // 复用 publish 工具的服务端预检模式:precheckOnly=true 只返回审批要求,不发布。 + params["precheckOnly"] = true return runDevAppTool(runner, cmd, devAppVersionPublishTool, params) }, } @@ -1047,7 +1045,7 @@ func newDevAppVersionPublishCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - params["dryRun"] = false + params["precheckOnly"] = false if cmd.Flags().Changed("confirm-sensitive") { value, _ := cmd.Flags().GetBool("confirm-sensitive") params["confirmedSensitive"] = value @@ -1102,12 +1100,10 @@ func devAppVersionLocator(cmd *cobra.Command) (map[string]any, error) { func addDevAppUnifiedIDFlag(cmd *cobra.Command) { cmd.Flags().String("unified-app-id", "", "统一应用 ID (必填)") - cmd.Flags().String("app-id", "", "--unified-app-id 的兼容别名") - _ = cmd.Flags().MarkHidden("app-id") } func requiredDevAppUnifiedID(cmd *cobra.Command) (string, error) { - appID := devAppFlagOrFallback(cmd, "unified-app-id", "app-id") + appID := devAppStringFlag(cmd, "unified-app-id") if appID == "" { return "", apperrors.NewValidation("--unified-app-id is required") } diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index f027ca4c..7f0567b6 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -119,6 +119,15 @@ func TestDevAppCommandHasAppAliasAndCoreCommands(t *testing.T) { t.Fatalf("missing command %q: %v", name, err) } } + robotCmd, _, err := root.Find([]string{"robot"}) + if err != nil { + t.Fatalf("missing robot command: %v", err) + } + for _, cmd := range robotCmd.Commands() { + if cmd.Name() == "update" { + t.Fatal("robot update command exists, want removed public command") + } + } } func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { @@ -166,7 +175,7 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { { name: "config create with skills and mode", args: []string{"robot", "config", "--unified-app-id", "u-1", "--name", "小助手", "--brief", "审批助手", "--mode", "2", "--skills", "qa,approval", "--add-scope", "--yes"}, - wantTool: "create_open_dev_app_robot_config", + wantTool: "set_open_dev_app_robot_config", wantParams: map[string]any{ "unifiedAppId": "u-1", "name": "小助手", @@ -228,11 +237,11 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { }, { name: "list", - args: []string{"version", "list", "--unified-app-id", "u-1", "--page", "2", "--page-size", "5"}, + args: []string{"version", "list", "--unified-app-id", "u-1", "--cursor", "cursor-1", "--page-size", "5"}, wantTool: "list_open_dev_app_versions", wantParams: map[string]any{ "unifiedAppId": "u-1", - "currentPage": 2, + "cursor": "cursor-1", "pageSize": 5, }, }, @@ -243,19 +252,19 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1"}, }, { - name: "check-approval forces dryRun", + name: "check-approval prechecks only", args: []string{"version", "check-approval", "--unified-app-id", "u-1", "--version-id", "v-1"}, wantTool: "publish_open_dev_app_version", - wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1", "dryRun": true}, + wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1", "precheckOnly": true}, }, { - name: "publish sets dryRun false and sensitive", + name: "publish disables precheck and sets sensitive", args: []string{"version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1", "--confirm-sensitive", "--approver", "user-1", "--yes"}, wantTool: "publish_open_dev_app_version", wantParams: map[string]any{ "unifiedAppId": "u-1", "versionId": "v-1", - "dryRun": false, + "precheckOnly": false, "confirmedSensitive": true, "approverUserId": "user-1", }, @@ -359,6 +368,79 @@ func TestDevAppEventSubscribeRequiresEventCode(t *testing.T) { } } +func TestDevAppScopedCommandsRejectLegacyLocators(t *testing.T) { + cases := []struct { + name string + args []string + }{ + {name: "version create", args: []string{"devapp", "version", "create", "--app-id", "u-1", "--version", "1.0.1", "--yes"}}, + {name: "version list", args: []string{"devapp", "version", "list", "--app-id", "u-1"}}, + {name: "version get", args: []string{"devapp", "version", "get", "--app-id", "u-1", "--version-id", "v-1"}}, + {name: "version check approval", args: []string{"devapp", "version", "check-approval", "--app-id", "u-1", "--version-id", "v-1"}}, + {name: "version publish", args: []string{"devapp", "version", "publish", "--app-id", "u-1", "--version-id", "v-1", "--yes"}}, + {name: "version status", args: []string{"devapp", "version", "status", "--app-id", "u-1", "--version-id", "v-1"}}, + {name: "robot get", args: []string{"devapp", "robot", "get", "--app-id", "u-1"}}, + {name: "robot config", args: []string{"devapp", "robot", "config", "--app-id", "u-1", "--name", "小助手", "--yes"}}, + {name: "robot enable", args: []string{"devapp", "robot", "enable", "--app-id", "u-1", "--name", "小助手", "--yes"}}, + {name: "robot offline", args: []string{"devapp", "robot", "offline", "--app-id", "u-1", "--yes"}}, + {name: "event list", args: []string{"devapp", "event", "list", "--app-id", "u-1"}}, + {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--app-id", "u-1", "--event-code", "user_add_org", "--yes"}}, + {name: "event unsubscribe", args: []string{"devapp", "event", "unsubscribe", "--app-id", "u-1", "--event-code", "user_add_org", "--yes"}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs(tc.args) + + err := root.Execute() + if err == nil { + t.Fatal("Execute() error = nil, want unknown --app-id flag") + } + if !strings.Contains(err.Error(), "unknown flag: --app-id") { + t.Fatalf("error = %q, want unknown --app-id flag", err.Error()) + } + if runner.last.Tool != "" { + t.Fatalf("tool = %q, want no invocation", runner.last.Tool) + } + }) + } +} + +func TestDevAppScopedCommandsRejectAgentAndCustomKeyLocators(t *testing.T) { + for _, flag := range []string{"--agent-id", "--custom-key"} { + for _, args := range [][]string{ + {"devapp", "version", "list", flag, "u-1"}, + {"devapp", "robot", "get", flag, "u-1"}, + {"devapp", "event", "list", flag, "u-1"}, + } { + t.Run(strings.Join(args[1:3], " ")+" "+flag, func(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs(args) + + err := root.Execute() + if err == nil { + t.Fatalf("Execute() error = nil, want unknown %s flag", flag) + } + if !strings.Contains(err.Error(), "unknown flag: "+flag) { + t.Fatalf("error = %q, want unknown %s flag", err.Error(), flag) + } + if runner.last.Tool != "" { + t.Fatalf("tool = %q, want no invocation", runner.last.Tool) + } + }) + } + } +} + func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { cases := []struct { name string diff --git a/skills/dingtalk-devapp/references/event.md b/skills/dingtalk-devapp/references/event.md index 166dbc61..cecdb196 100644 --- a/skills/dingtalk-devapp/references/event.md +++ b/skills/dingtalk-devapp/references/event.md @@ -42,7 +42,7 @@ MCP tool: `unsubscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode` | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| -| `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID(兼容隐藏别名 `--app-id`) | +| `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID | | `--event-code` | `eventCode` | subscribe/unsubscribe 必填 | 事件码,取自 `event list` | ## 灰度应用需发布版本生效(重要) diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md index 11b51755..834d9d98 100644 --- a/skills/dingtalk-devapp/references/robot.md +++ b/skills/dingtalk-devapp/references/robot.md @@ -3,7 +3,7 @@ 为开放平台企业内部应用创建和配置机器人。分两类场景: 1. **新建智能体机器人**:一次性创建一个新的 Agent 应用 + 承载机器人(`create` / `submit` / `result`)。 -2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `update` / `enable` / `offline`),通过 `--unified-app-id` 定位。 +2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `enable` / `offline`),通过 `--unified-app-id` 定位。 > `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 @@ -74,9 +74,9 @@ MCP tool: `get_open_dev_app_robot_config`。返回机器人基础信息、回调 - ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-url`,或用 `robot connect` 接到本地 Agent。 - 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 -### 创建 / 更新 / 启用配置 +### 创建或更新 / 启用配置 -三者字段一致,区别在语义:`config`=首次创建、`update`=修改、`enable`=启用/重新启用。 +`config` 是 upsert:未配置机器人时创建,已存在时更新;`enable` 用于启用/重新启用。 首次 `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为“待生效”;只有 `status=1` 或需要重新上架时才调用 `enable`。 @@ -85,12 +85,10 @@ dws devapp robot config --unified-app-id --name 小助手 --brief --description "处理审批相关问答" --outgoing-url https://example.com/msg \ --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json -dws devapp robot update --unified-app-id --brief "新的简介" --dry-run --format json - dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json ``` -MCP tools: `create_open_dev_app_robot_config` / `update_open_dev_app_robot_config` / `enable_open_dev_app_robot` +MCP tools: `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` | CLI | MCP | 说明 | |-----|-----|------| diff --git a/skills/dingtalk-devapp/references/version.md b/skills/dingtalk-devapp/references/version.md index 0998ec67..bea2ae75 100644 --- a/skills/dingtalk-devapp/references/version.md +++ b/skills/dingtalk-devapp/references/version.md @@ -31,10 +31,11 @@ MCP tool: `create_open_dev_app_version` ## 版本列表 ```bash -dws devapp version list --unified-app-id --page 1 --page-size 20 --format json +dws devapp version list --unified-app-id --page-size 20 --format json +dws devapp version list --unified-app-id --cursor --page-size 20 --format json ``` -MCP tool: `list_open_dev_app_versions`(`--page`→`currentPage`,`--page-size`→`pageSize`) +MCP tool: `list_open_dev_app_versions`(`--cursor`→`cursor`,`--page-size`→`pageSize`)。响应使用 `items` / `nextCursor` / `hasMore`。 新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 @@ -52,7 +53,7 @@ MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能 dws devapp version check-approval --unified-app-id --version-id --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 强制 `dryRun=true`,仅返回审批要求和候选审批人,**不会实际发布**。 +MCP tool: `publish_open_dev_app_version`,CLI 强制 `precheckOnly=true`,仅返回审批要求和候选审批人,**不会实际发布**。 ## 发布 @@ -67,14 +68,14 @@ dws devapp version publish --unified-app-id --version-id --version-id --approver --yes --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 设 `dryRun=false`。 +MCP tool: `publish_open_dev_app_version`,CLI 设 `precheckOnly=false`。 | CLI | MCP | 说明 | |-----|-----|------| | `--confirm-sensitive` | `confirmedSensitive` | 版本含高敏权限时必须确认 | | `--approver` | `approverUserId` | 灰度选人模式指定审批人 userId | -> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`(对应 `dryRun=true`)。二者不同,发布前建议先 `check-approval`。 +> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`(对应 `precheckOnly=true`)。二者不同,发布前建议先 `check-approval`。 发布响应 `result`: diff --git a/skills/dingtalk-devapp/references/workflows.md b/skills/dingtalk-devapp/references/workflows.md index 9347fbee..bed457c6 100644 --- a/skills/dingtalk-devapp/references/workflows.md +++ b/skills/dingtalk-devapp/references/workflows.md @@ -148,7 +148,7 @@ 2. 审批预检:获取审批要求和候选审批人列表 dws devapp version check-approval --unified-app-id --version-id --format json - → 服务端 dryRun 预检,不实际发布 + → 服务端 precheckOnly 预检,不实际发布 → 返回中包含是否需要审批、候选审批人(userId + 姓名) 3. 把候选审批人列表展示给用户,让用户选择 diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index cfde383f..570549c2 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -220,12 +220,11 @@ MCP tools: `create_dingtalk_robot` / `submit_robot_create_task` / `query_robot_c ```bash dws devapp robot get --unified-app-id ID --format json dws devapp robot config --unified-app-id ID --name 小助手 --brief 审批助手 --outgoing-url URL --mode 2 --skills qa,approval --dry-run --format json -dws devapp robot update --unified-app-id ID --brief "新简介" --dry-run --format json dws devapp robot enable --unified-app-id ID --name 小助手 --dry-run --format json dws devapp robot offline --unified-app-id ID --dry-run --format json ``` -MCP tools: `get_open_dev_app_robot_config` / `create_open_dev_app_robot_config` / `update_open_dev_app_robot_config` / `enable_open_dev_app_robot` / `offline_open_dev_app_robot`。 +MCP tools: `get_open_dev_app_robot_config` / `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` / `offline_open_dev_app_robot`。 配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。应用未配机器人时 `get` 返回 `robot info is not exist`。 @@ -278,7 +277,8 @@ dws devapp robot connect --robot-client-id CLIENT_ID --robot-client-secret CLIEN ```bash dws devapp version create --unified-app-id ID --version 1.0.1 --desc "新增机器人能力" --dry-run --format json -dws devapp version list --unified-app-id ID --page 1 --page-size 20 --format json +dws devapp version list --unified-app-id ID --page-size 20 --format json +dws devapp version list --unified-app-id ID --cursor NEXT_CURSOR --page-size 20 --format json dws devapp version get --unified-app-id ID --version-id VERSION_ID --format json dws devapp version check-approval --unified-app-id ID --version-id VERSION_ID --format json dws devapp version publish --unified-app-id ID --version-id VERSION_ID --confirm-sensitive --dry-run --format json @@ -287,8 +287,9 @@ dws devapp version status --unified-app-id ID --version-id VERSION_ID --format j MCP tools: `create_open_dev_app_version` / `list_open_dev_app_versions` / `get_open_dev_app_version_detail` / `publish_open_dev_app_version` / `get_open_dev_app_version_status`。 -- `check-approval` = `publish_open_dev_app_version` 的 `dryRun=true` 预检模式,不实际发布。 -- `publish` 设 `dryRun=false`;含高敏权限需 `--confirm-sensitive`,灰度选人模式用 `--approver USER_ID`。 +- `version list` 使用 `cursor/pageSize`;首次不传 cursor,后续传响应里的 `nextCursor`。 +- `check-approval` = `publish_open_dev_app_version` 的 `precheckOnly=true` 预检模式,不实际发布。 +- `publish` 设 `precheckOnly=false`;含高敏权限需 `--confirm-sensitive`,灰度选人模式用 `--approver USER_ID`。 - 流程:`permission add`(requiredApproval 写入版本变更)→ `version create` → `check-approval` → `publish` → `status`。 - 新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 - **审批人选择**:`check-approval` 返回候选审批人列表(userId+姓名)→ **展示给用户让其选择(不要自行挑选或默认第一个)** → `publish --approver <选中的userId>` 自动向该审批人发起审批 → `version status` 跟踪。机器人等能力需审批通过、应用发布后才可被搜索/加群/路由消息。 diff --git a/skills/multi/dingtalk-devapp/references/event.md b/skills/multi/dingtalk-devapp/references/event.md index 166dbc61..cecdb196 100644 --- a/skills/multi/dingtalk-devapp/references/event.md +++ b/skills/multi/dingtalk-devapp/references/event.md @@ -42,7 +42,7 @@ MCP tool: `unsubscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode` | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| -| `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID(兼容隐藏别名 `--app-id`) | +| `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID | | `--event-code` | `eventCode` | subscribe/unsubscribe 必填 | 事件码,取自 `event list` | ## 灰度应用需发布版本生效(重要) diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md index 11b51755..834d9d98 100644 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ b/skills/multi/dingtalk-devapp/references/robot.md @@ -3,7 +3,7 @@ 为开放平台企业内部应用创建和配置机器人。分两类场景: 1. **新建智能体机器人**:一次性创建一个新的 Agent 应用 + 承载机器人(`create` / `submit` / `result`)。 -2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `update` / `enable` / `offline`),通过 `--unified-app-id` 定位。 +2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `enable` / `offline`),通过 `--unified-app-id` 定位。 > `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 @@ -74,9 +74,9 @@ MCP tool: `get_open_dev_app_robot_config`。返回机器人基础信息、回调 - ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-url`,或用 `robot connect` 接到本地 Agent。 - 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 -### 创建 / 更新 / 启用配置 +### 创建或更新 / 启用配置 -三者字段一致,区别在语义:`config`=首次创建、`update`=修改、`enable`=启用/重新启用。 +`config` 是 upsert:未配置机器人时创建,已存在时更新;`enable` 用于启用/重新启用。 首次 `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为“待生效”;只有 `status=1` 或需要重新上架时才调用 `enable`。 @@ -85,12 +85,10 @@ dws devapp robot config --unified-app-id --name 小助手 --brief --description "处理审批相关问答" --outgoing-url https://example.com/msg \ --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json -dws devapp robot update --unified-app-id --brief "新的简介" --dry-run --format json - dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json ``` -MCP tools: `create_open_dev_app_robot_config` / `update_open_dev_app_robot_config` / `enable_open_dev_app_robot` +MCP tools: `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` | CLI | MCP | 说明 | |-----|-----|------| diff --git a/skills/multi/dingtalk-devapp/references/version.md b/skills/multi/dingtalk-devapp/references/version.md index 0998ec67..bea2ae75 100644 --- a/skills/multi/dingtalk-devapp/references/version.md +++ b/skills/multi/dingtalk-devapp/references/version.md @@ -31,10 +31,11 @@ MCP tool: `create_open_dev_app_version` ## 版本列表 ```bash -dws devapp version list --unified-app-id --page 1 --page-size 20 --format json +dws devapp version list --unified-app-id --page-size 20 --format json +dws devapp version list --unified-app-id --cursor --page-size 20 --format json ``` -MCP tool: `list_open_dev_app_versions`(`--page`→`currentPage`,`--page-size`→`pageSize`) +MCP tool: `list_open_dev_app_versions`(`--cursor`→`cursor`,`--page-size`→`pageSize`)。响应使用 `items` / `nextCursor` / `hasMore`。 新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 @@ -52,7 +53,7 @@ MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能 dws devapp version check-approval --unified-app-id --version-id --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 强制 `dryRun=true`,仅返回审批要求和候选审批人,**不会实际发布**。 +MCP tool: `publish_open_dev_app_version`,CLI 强制 `precheckOnly=true`,仅返回审批要求和候选审批人,**不会实际发布**。 ## 发布 @@ -67,14 +68,14 @@ dws devapp version publish --unified-app-id --version-id --version-id --approver --yes --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 设 `dryRun=false`。 +MCP tool: `publish_open_dev_app_version`,CLI 设 `precheckOnly=false`。 | CLI | MCP | 说明 | |-----|-----|------| | `--confirm-sensitive` | `confirmedSensitive` | 版本含高敏权限时必须确认 | | `--approver` | `approverUserId` | 灰度选人模式指定审批人 userId | -> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`(对应 `dryRun=true`)。二者不同,发布前建议先 `check-approval`。 +> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`(对应 `precheckOnly=true`)。二者不同,发布前建议先 `check-approval`。 发布响应 `result`: diff --git a/skills/multi/dingtalk-devapp/references/workflows.md b/skills/multi/dingtalk-devapp/references/workflows.md index 9347fbee..bed457c6 100644 --- a/skills/multi/dingtalk-devapp/references/workflows.md +++ b/skills/multi/dingtalk-devapp/references/workflows.md @@ -148,7 +148,7 @@ 2. 审批预检:获取审批要求和候选审批人列表 dws devapp version check-approval --unified-app-id --version-id --format json - → 服务端 dryRun 预检,不实际发布 + → 服务端 precheckOnly 预检,不实际发布 → 返回中包含是否需要审批、候选审批人(userId + 姓名) 3. 把候选审批人列表展示给用户,让用户选择 From 1921e5e37eafc22a6d4cdfcaad206fa31798fbd9 Mon Sep 17 00:00:00 2001 From: "shangguanxuan.sgx" Date: Mon, 15 Jun 2026 18:09:51 +0800 Subject: [PATCH 05/47] feat(devdoc): support cursor pagination --- README.md | 2 +- README_zh.md | 2 +- docs/command-index.md | 6 +- internal/helpers/devdoc.go | 197 +++++++++++++++++- internal/helpers/devdoc_test.go | 171 ++++++++++++++- skills/mono/references/products/devdoc.md | 53 ++++- skills/mono/references/products/simple.md | 22 +- .../dingtalk-devdoc/references/devdoc.md | 53 ++++- .../multi/dingtalk-skill/references/skill.md | 24 ++- test/cli_compat/devdoc_test.go | 12 +- test/cli_compat/helpers_test.go | 21 +- test/skill_tests.md | 7 +- test/skill_tests_results.md | 14 +- 13 files changed, 552 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 9b7385c2..8c78237b 100644 --- a/README.md +++ b/README.md @@ -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 | diff --git a/README_zh.md b/README_zh.md index ac4309b1..600699f9 100644 --- a/README_zh.md +++ b/README_zh.md @@ -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 | diff --git a/docs/command-index.md b/docs/command-index.md index dfd964fc..b012b5fb 100644 --- a/docs/command-index.md +++ b/docs/command-index.md @@ -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. | - diff --git a/internal/helpers/devdoc.go b/internal/helpers/devdoc.go index f8495532..540a9e7f 100644 --- a/internal/helpers/devdoc.go +++ b/internal/helpers/devdoc.go @@ -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 { diff --git a/internal/helpers/devdoc_test.go b/internal/helpers/devdoc_test.go index 96c042f7..aaff7088 100644 --- a/internal/helpers/devdoc_test.go +++ b/internal/helpers/devdoc_test.go @@ -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() diff --git a/skills/mono/references/products/devdoc.md b/skills/mono/references/products/devdoc.md index e4116d9b..b7300418 100644 --- a/skills/mono/references/products/devdoc.md +++ b/skills/mono/references/products/devdoc.md @@ -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 "" --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 "" --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 "" --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 "" --query "接口调用失败" --format json + +# 错误排查结果继续翻页 +dws devdoc error diagnose --error-code "40014" --query "access_token" --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 只查**官方开发者文档** diff --git a/skills/mono/references/products/simple.md b/skills/mono/references/products/simple.md index cdaa9e27..05849460 100644 --- a/skills/mono/references/products/simple.md +++ b/skills/mono/references/products/simple.md @@ -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 "" --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 | diff --git a/skills/multi/dingtalk-devdoc/references/devdoc.md b/skills/multi/dingtalk-devdoc/references/devdoc.md index e4116d9b..b7300418 100644 --- a/skills/multi/dingtalk-devdoc/references/devdoc.md +++ b/skills/multi/dingtalk-devdoc/references/devdoc.md @@ -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 "" --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 "" --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 "" --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 "" --query "接口调用失败" --format json + +# 错误排查结果继续翻页 +dws devdoc error diagnose --error-code "40014" --query "access_token" --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 只查**官方开发者文档** diff --git a/skills/multi/dingtalk-skill/references/skill.md b/skills/multi/dingtalk-skill/references/skill.md index d9a416c3..27222b08 100644 --- a/skills/multi/dingtalk-skill/references/skill.md +++ b/skills/multi/dingtalk-skill/references/skill.md @@ -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 "" --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 ` | | `skill install` | 安装成功/失败信息 | 确认目标 Agent 目录已注册 | | `skill publish` | 发布结果(成功或错误信息) | 确认企业技能库已更新 | diff --git a/test/cli_compat/devdoc_test.go b/test/cli_compat/devdoc_test.go index 755db3bf..09c689ae 100644 --- a/test/cli_compat/devdoc_test.go +++ b/test/cli_compat/devdoc_test.go @@ -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() diff --git a/test/cli_compat/helpers_test.go b/test/cli_compat/helpers_test.go index 00df6928..2523b7a5 100644 --- a/test/cli_compat/helpers_test.go +++ b/test/cli_compat/helpers_test.go @@ -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 diff --git a/test/skill_tests.md b/test/skill_tests.md index fe57fb9b..0f39ab26 100644 --- a/test/skill_tests.md +++ b/test/skill_tests.md @@ -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 条) diff --git a/test/skill_tests_results.md b/test/skill_tests_results.md index 8079b9c2..9330788e 100644 --- a/test/skill_tests_results.md +++ b/test/skill_tests_results.md @@ -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 From 0f3c4ccbdd53cc8b04f863500c2da552ee05a44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Tue, 16 Jun 2026 00:22:29 +0800 Subject: [PATCH 06/47] fix: align devapp cli with MCP schema --- docs/devapp-yulan-command-routing.md | 242 ++++++++---------- internal/helpers/devapp.go | 235 +++++++++-------- internal/helpers/devapp_test.go | 80 +++--- skills/dingtalk-devapp/SKILL.md | 6 +- skills/dingtalk-devapp/references/app-crud.md | 32 +-- .../references/credentials-webapp.md | 17 +- .../dingtalk-devapp/references/permissions.md | 16 +- .../dingtalk-devapp/references/workflows.md | 8 +- skills/mono/references/products/devapp.md | 41 ++- skills/multi/dingtalk-devapp/SKILL.md | 6 +- .../dingtalk-devapp/references/app-crud.md | 32 +-- .../references/credentials-webapp.md | 17 +- .../dingtalk-devapp/references/permissions.md | 16 +- .../dingtalk-devapp/references/workflows.md | 8 +- 14 files changed, 354 insertions(+), 402 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 4f95c984..6f0a2f69 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -128,13 +128,13 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Area | CLI command | MCP tool | Backend HSF facade | Current backend status | Open-source DWS status | | --- | --- | --- | --- | --- | --- | -| App query | `devapp list` | `list_open_dev_apps_by_condition` | `OpenInnerAppQueryFacade.listByCondition` | Implemented | Hardcoded helper | -| App detail | `devapp get` | `get_open_dev_app_detail` | `OpenInnerAppQueryFacade.getDetail` | Implemented | Hardcoded helper | -| App create | `devapp create` | `create_inner_app` | `OpenInnerAppManageFacade.create` | Implemented | Hardcoded helper | -| App update | `devapp update` | `update_inner_app` | `OpenInnerAppManageFacade.update` | Implemented | Hardcoded helper | -| App delete | `devapp delete` | `delete_inner_app` | `OpenInnerAppManageFacade.delete` | Implemented | Hardcoded helper | +| App query | `devapp list` | `list_open_dev_app` | `OpenInnerAppQueryFacade.listByCondition` | Implemented | Hardcoded helper | +| App detail | `devapp get` | `get_dev_app` | `OpenInnerAppQueryFacade.getDetail` | Implemented | Hardcoded helper | +| App create | `devapp create` | `create_dev_app` | `OpenInnerAppManageFacade.create` | Implemented | Hardcoded helper | +| App update | `devapp update` | `update_dev_app` | `OpenInnerAppManageFacade.update` | Implemented | Hardcoded helper | +| App delete | `devapp delete` | `delete_dev_app` | `OpenInnerAppManageFacade.delete` | Implemented | Hardcoded helper | | Credentials | `devapp credentials get` | `get_open_dev_app_credentials` | `OpenInnerAppQueryFacade.getCredentials` | Implemented | Hardcoded helper | -| Web app | `devapp webapp get/config` | `get_webapp_config`/`set_webapp_config` | Webapp facade | Implemented | Hardcoded helper; verified `config` then `get` returns `agentId/h5PageType/homepageLink/pcHomepageLink/ompLink`; before config, `get` may only return `agentId`. | +| Web app | `devapp webapp get/config` | `get_webapp_config`/`set_webapp_config` | Webapp facade | Implemented | Hardcoded helper; locator fields are `unifiedAppId/appKey`; config fields are `h5PageType/homepageUrl/pcHomepageUrl/ompUrl`. | | Permission list | `devapp permission list/search/detail` | `list_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | | Permission apply | `devapp permission add` | `apply_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | | Permission remove | `devapp permission remove` | `remove_open_dev_app_permission` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | @@ -179,36 +179,34 @@ MCP output configuration should keep the wrapper fields and configure success as `ServiceResult.success == true`. DWS/Agent normalization can add `ok`, `reason`, `summary`, and `nextAction`, but raw `errorCode/errorMsg` must remain visible. +Naming convention: + +- Public CLI flags use user-facing names: `--name`, `--desc`, `--icon`, `--homepage-url`. +- MCP app create/update currently keep backend request field names: `appName`, `appDesc`, `appIcon`. +- MCP app list/detail use the simplified locator/search fields: `unifiedAppId`, `appKey`, `name`. +- The CLI helper owns the translation, for example `--name` -> `appName` for create/update and + `--name` -> `name` for list/detail. + ### 3.2 P0 Tool Contracts -#### list_open_dev_apps_by_condition +#### list_open_dev_app | Item | Contract | | --- | --- | | CLI | `dws devapp list` | | HSF facade | `OpenInnerAppQueryFacade.listByCondition` | | Request shape | One object with system context and optional list filters. | -| Required public fields | None; default `currentPage=1`, `pageSize=20`. | +| Required public fields | None; default `pageSize=20`. | | Identity source | `corpId/userId` from MCP system context. | Input fields: | Field | Type | CLI flag | Required | Notes | | --- | --- | --- | --- | --- | -| `currentPage` | number | `--page` | no | 1-based page number. | | `pageSize` | number | `--page-size` | no | Default 20; cap by backend. | -| `appName` | string | `--name` | no | App name keyword. | -| `agentId` | number | `--agent-id` | no | Exact DingTalk app agentId. | -| `appId` | number | `--app-id` | no | Compatible app id. | +| `cursor` | string | `--cursor` | no | Cursor from previous response; empty for first page. | +| `name` | string | `--name` | no | App name keyword. | | `appKey` | string | `--app-key` | no | DingTalk appKey/clientId. | -| `customKey` | string | `--custom-key` | no | Custom key. | -| `appGroupId` | number | `--app-group-id` | no | App group id. | -| `creator` | string | `--creator` | no | Creator name keyword. | -| `robotName` | string | `--robot-name` | no | Bot name keyword. | -| `developType` | number/string | `--develop-type` | no | Use backend enum if schema exposes one. | -| `filterCoolApp` | number/boolean | `--filter-cool-app` | no | Only expose when backend semantics are documented. | -| `sortType` | string | `--sort` | no | Example: `gmt_modified`. | -| `sortOrder` | string | `--order` | no | `asc` or `desc`. | Normalized response: @@ -216,15 +214,12 @@ Normalized response: { "ok": true, "operation": "app.list", - "page": {"currentPage": 1, "pageSize": 20, "total": 0}, + "page": {"pageSize": 20, "nextCursor": ""}, "apps": [ { "unifiedAppId": "UNIFIED_APP_ID", - "appName": "DemoApp", - "agentId": 123456, - "appId": 123, + "name": "DemoApp", "appKey": "dingxxx", - "customKey": "custom-key", "creator": "Creator", "robotName": "BotName", "gmtModified": "2026-06-05T00:00:00+08:00" @@ -234,7 +229,7 @@ Normalized response: } ``` -#### get_open_dev_app_detail +#### get_dev_app | Item | Contract | | --- | --- | @@ -249,11 +244,8 @@ Locator fields: | Field | Type | CLI flag | Notes | | --- | --- | --- | --- | | `unifiedAppId` | string | `--unified-app-id` | Preferred. | -| `agentId` | number | `--agent-id` | Exact id. | -| `appId` | number | `--app-id` | Compatible id. | | `appKey` | string | `--app-key` | appKey/clientId. | -| `customKey` | string | `--custom-key` | Custom key. | -| `appName` | string | `--name` | Fuzzy; must resolve uniquely before writes. | +| `name` | string | `--name` | Fuzzy; must resolve uniquely before writes. | Detail response should include identifiers and configuration summary, but not full `clientSecret/appSecret`: @@ -264,11 +256,8 @@ full `clientSecret/appSecret`: "operation": "app.get", "app": { "unifiedAppId": "UNIFIED_APP_ID", - "appName": "DemoApp", - "agentId": 123456, - "appId": 123, + "name": "DemoApp", "appKey": "dingxxx", - "customKey": "custom-key", "developType": "internal", "creator": "Creator", "visibility": "partial_members", @@ -279,7 +268,7 @@ full `clientSecret/appSecret`: } ``` -#### create_inner_app +#### create_dev_app | Item | Contract | | --- | --- | @@ -305,8 +294,7 @@ Response: "operation": "app.create", "app": { "unifiedAppId": "UNIFIED_APP_ID", - "appName": "DemoApp", - "agentId": 123456, + "name": "DemoApp", "appKey": "dingxxx" }, "nextAction": "run devapp get or configure permissions", @@ -314,7 +302,7 @@ Response: } ``` -#### update_inner_app +#### update_dev_app | Item | Contract | | --- | --- | @@ -324,7 +312,7 @@ Response: | Required public fields | One app locator and at least one changed field. | | Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | -Locator fields are the same as `get_open_dev_app_detail`, but the Agent should +Locator fields are the same as `get_dev_app`, but the Agent should prefer `unifiedAppId` after resolving a unique app. Update fields: @@ -340,7 +328,7 @@ Update fields: Response should return the updated app summary plus a `changedFields` array when available. -#### delete_inner_app +#### delete_dev_app | Item | Contract | | --- | --- | @@ -361,21 +349,21 @@ backend fields. | CLI | `dws devapp permission list/search/detail` | | HSF facade | `OpenInnerAppPermissionFacade.list` | | Request shape | One object with system context, app locator, and optional filters. | -| Required public fields | One app locator. | +| Required public fields | `unifiedAppId`. | | Web semantics | Equivalent to grouped `scope/list` for APP and SNS scopes. | Input fields: | Field | Type | CLI flag | Required | Notes | | --- | --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | one locator | Preferred. | -| `agentId` | number | `--agent-id` | one locator | Compatible. | +| `unifiedAppId` | string | `--unified-app-id` | yes | Unified app id. | | `keyword` | string | `--keyword` | no | Maps to Web `key`. | | `apiStatus` | string | `--api-status` | no | Web `apiStatus` filter. | | `authStatus` | string | `--status` | no | `ALL/AUTHED/UNAUTHED`, post-filter. | -| `firstLevelType` | string | `--scope-type` | no | `APP/SNS`; empty returns both. | +| `scopeType` | string | `--scope-type` | no | `APP/SNS`; empty returns both. | | `scopeValue` | string | `--scope` | no | Detail mode and exact post-filter. | -| `limit` | number | `--limit` | no | Agent display cap; no server pagination. | +| `pageSize` | number | `--page-size` | no | Return count cap; default 20. | +| `cursor` | string | `--cursor` | no | Cursor from previous response; empty for first page. | Normalized response should flatten `appScopes` and `snsScopes` into one `permissions[]` list while preserving group metadata: @@ -389,7 +377,7 @@ Normalized response should flatten `appScopes` and `snsScopes` into one { "scopeValue": "qyapi_robot_sendmsg", "scopeName": "企业内机器人发消息权限", - "firstLevelType": "APP", + "scopeType": "APP", "categoryTitle": "机器人", "authed": true, "canEdit": true, @@ -400,7 +388,7 @@ Normalized response should flatten `appScopes` and `snsScopes` into one } ], "summary": {"totalMatched": 1, "returnedCount": 1}, - "truncated": false, + "nextCursor": "", "raw": {} } ``` @@ -412,15 +400,14 @@ Normalized response should flatten `appScopes` and `snsScopes` into one | CLI | `dws devapp permission add` | | HSF facade | `OpenInnerAppPermissionFacade.apply` | | Request shape | One object with system context, app locator, and `scopeValues`. | -| Required public fields | One app locator and non-empty `scopeValues`. | +| Required public fields | `unifiedAppId` and non-empty `scopeValues`. | | Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | Input fields: | Field | Type | CLI flag | Required | Notes | | --- | --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | one locator | Preferred. | -| `agentId` | number | `--agent-id` | one locator | Compatible. | +| `unifiedAppId` | string | `--unified-app-id` | yes | Unified app id. | | `scopeValues` | string[] | `--permissions` | yes | Comma-separated in CLI; must come from list response. | Response must distinguish direct apply, version-staged apply, rejected scopes, and @@ -450,15 +437,14 @@ current app version. Approver selection is handled by the version flow. | CLI | `dws devapp permission remove` | | HSF facade | `OpenInnerAppPermissionFacade.remove` | | Request shape | One object with system context, app locator, and one `scopeValue`. | -| Required public fields | One app locator and one `scopeValue`. | +| Required public fields | `unifiedAppId` and one `scopeValue`. | | Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | Input fields: | Field | Type | CLI flag | Required | Notes | | --- | --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | one locator | Preferred. | -| `agentId` | number | `--agent-id` | one locator | Compatible. | +| `unifiedAppId` | string | `--unified-app-id` | yes | Unified app id. | | `scopeValue` | string | `--permission` | yes | Remove exactly one permission. | Expected structured failures: @@ -498,36 +484,26 @@ Tool override examples: ```json { "toolOverrides": { - "list_open_dev_apps_by_condition": { + "list_open_dev_app": { "cliName": "list", "description": "查询开放平台企业内部应用列表", "flags": { - "currentPage": {"alias": "page", "default": 1}, "pageSize": {"alias": "page-size", "default": 20}, - "appName": {"alias": "name"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, - "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"}, - "creator": {"alias": "creator"}, - "robotName": {"alias": "robot-name"}, - "sortType": {"alias": "sort"}, - "sortOrder": {"alias": "order"} + "cursor": {"alias": "cursor"}, + "name": {"alias": "name"}, + "appKey": {"alias": "app-key"} } }, - "get_open_dev_app_detail": { + "get_dev_app": { "cliName": "get", "description": "查询开放平台企业内部应用详情", "flags": { "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"}, - "appName": {"alias": "name"} + "name": {"alias": "name"} } }, - "create_inner_app": { + "create_dev_app": { "cliName": "create", "description": "创建开放平台企业内部应用", "isSensitive": true, @@ -537,35 +513,26 @@ Tool override examples: "appIcon": {"alias": "icon"} } }, - "update_inner_app": { + "update_dev_app": { "cliName": "update", "description": "修改开放平台企业内部应用基础信息", "isSensitive": true, "flags": { "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"}, - "currentAppName": {"alias": "current-name"}, "appName": {"alias": "name"}, "appDesc": {"alias": "desc"}, - "appIcon": {"alias": "icon"}, - "international": {"alias": "international"}, - "supportHarmony": {"alias": "support-harmony", "type": "bool"} + "appIcon": {"alias": "icon"} } }, - "delete_inner_app": { + "delete_dev_app": { "cliName": "delete", "description": "删除开放平台企业内部应用", "isSensitive": true, "flags": { "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, - "appName": {"alias": "name"}, "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"} + "name": {"alias": "name"} } }, "list_open_dev_app_permissions": { @@ -574,13 +541,13 @@ Tool override examples: "description": "查询开放平台应用权限列表,默认同时返回 APP 应用权限和 SNS 个人权限", "flags": { "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, "keyword": {"alias": "keyword"}, "apiStatus": {"alias": "api-status"}, "authStatus": {"alias": "status", "default": "ALL"}, - "firstLevelType": {"alias": "scope-type"}, + "scopeType": {"alias": "scope-type"}, "scopeValue": {"alias": "scope"}, - "limit": {"alias": "limit", "default": "20"} + "pageSize": {"alias": "page-size", "default": "20"}, + "cursor": {"alias": "cursor"} } }, "apply_open_dev_app_permissions": { @@ -590,7 +557,6 @@ Tool override examples: "isSensitive": true, "flags": { "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, "scopeValues": { "alias": "permissions", "type": "stringSlice", @@ -606,7 +572,6 @@ Tool override examples: "isSensitive": true, "flags": { "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, "scopeValue": { "alias": "permission", "required": true, @@ -639,16 +604,14 @@ Application identity must be resolved before detail and write commands. | Priority | Identifier | Rule | | --- | --- | --- | | 1 | `unifiedAppId` | Preferred unique id for write operations. | -| 2 | `agentId/appId` | Compatible exact identifier. | -| 3 | `appKey/clientId` | Call list/detail and require one result. | -| 4 | `customKey` | Call list/detail and require one result. | -| 5 | `appName/name` | Fuzzy search; write operations require one result. | +| 2 | `appKey/clientId` | Call list/detail and require one result. | +| 3 | `name` | Fuzzy search; write operations require one result. | Agent rule: 1. Never choose the first row when multiple apps match. -2. Show candidates with `appName`, `unifiedAppId`, `agentId`, `appKey/clientId`, `creator`, and modified time. -3. Ask the user to pick `unifiedAppId` or `agentId` for write operations. +2. Show candidates with `name`, `unifiedAppId`, `appKey/clientId`, `creator`, and modified time. +3. Ask the user to pick `unifiedAppId` for write operations when multiple apps match. MCP calls must rely on system context for caller identity: @@ -666,31 +629,26 @@ Open-source DWS and Agent must not pass `orgId` or `uid` as user input. ```bash dws devapp list --page 1 --page-size 20 --format json dws devapp list --name DemoApp --format json -dws devapp list --agent-id AGENT_ID --format json dws devapp list --app-key dingxxx --format json -dws devapp list --creator 张三 --format json -dws devapp list --robot-name 小钉 --format json +dws devapp list --page-size 20 --cursor NEXT_CURSOR --format json ``` MCP payload: ```json { - "currentPage": 1, "pageSize": 20, - "appName": "DemoApp", - "sortType": "gmt_modified", - "sortOrder": "desc" + "name": "DemoApp" } ``` -Pagination is supported for app list through `currentPage/pageSize`, both 1-based. Search filters are optional. A baseline call should pass only pagination and sort fields. +Pagination is cursor based. A baseline call should pass only `pageSize`; use the response `nextCursor` for the next page. ### 6.2 Get ```bash dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --agent-id AGENT_ID --format json +dws devapp get --app-key dingxxx --format json dws devapp get --name DemoApp --format json ``` @@ -702,7 +660,7 @@ MCP payload: } ``` -`get` may show `clientId/appKey/agentId`, but it must not be used to read full `clientSecret/appSecret`. Secret read belongs to `credentials get`. +`get` may show `clientId/appKey`, but it must not be used to read full `clientSecret/appSecret`. Secret read belongs to `credentials get`. ### 6.3 Create @@ -764,7 +722,7 @@ MCP payload: } ``` -Valid locator fields are `unifiedAppId`, `agentId`, `appId`, `appName`, `appKey`, and `customKey`. Name/key locators must uniquely resolve. +Valid public locator fields are `unifiedAppId`, `appKey`, and `name`. Name/key locators must uniquely resolve. ## 7. Credentials @@ -772,7 +730,8 @@ Target command: ```bash dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --agent-id AGENT_ID --format json +dws devapp credentials get --app-key dingxxx --format json +dws devapp credentials get --name DemoApp --format json ``` Current status: @@ -786,7 +745,7 @@ Target fields: | Field | Notes | | --- | --- | -| `unifiedAppId/agentId/appName` | App summary. | +| `unifiedAppId/name` | App summary. | | `clientId/appKey` | Non-secret identifiers. | | `clientSecret/appSecret` | Returned by the dedicated credentials tool; treat as sensitive output. | | `currentSecretStatus` | Current secret state. | @@ -809,10 +768,10 @@ Returned groups: "success": true, "data": { "snsScopes": [ - {"title": "个人权限", "firstLevelType": "SNS", "scopeList": []} + {"title": "个人权限", "scopeType": "SNS", "scopeList": []} ], "appScopes": [ - {"title": "通讯录管理", "firstLevelType": "APP", "scopeList": []} + {"title": "通讯录管理", "scopeType": "APP", "scopeList": []} ] } } @@ -835,7 +794,7 @@ MCP payload for normal list: { "unifiedAppId": "UNIFIED_APP_ID", "authStatus": "ALL", - "limit": 20 + "pageSize": 20 } ``` @@ -846,7 +805,7 @@ MCP payload for search: "unifiedAppId": "UNIFIED_APP_ID", "keyword": "机器人发送消息", "authStatus": "UNAUTHED", - "limit": 10 + "pageSize": 10 } ``` @@ -856,7 +815,7 @@ MCP payload for detail: { "unifiedAppId": "UNIFIED_APP_ID", "scopeValue": "qyapi_robot_sendmsg", - "limit": 1 + "pageSize": 1 } ``` @@ -864,16 +823,16 @@ Input fields: | Field | Type | Required | Notes | | --- | --- | --- | --- | -| `unifiedAppId` | string | one of app id fields | Preferred app locator. | -| `agentId` | number | one of app id fields | Compatible app locator. | +| `unifiedAppId` | string | yes | Unified app id. | | `keyword` | string | no | Maps to Web `key`. | | `apiStatus` | string | no | Maps to Web `apiStatus`. | | `authStatus` | string | no | `ALL/AUTHED/UNAUTHED`, post-filter. | -| `firstLevelType` | string | no | `APP/SNS`; empty returns both. | +| `scopeType` | string | no | `APP/SNS`; empty returns both. | | `scopeValue` | string | no | Detail mode and exact post-filter. | -| `limit` | number | no | Default 20, hard cap 50. | +| `pageSize` | number | no | Default 20, hard cap 50. | +| `cursor` | string | no | Cursor from previous response; empty for first page. | -Permission list has no server pagination. Web also loads the full grouped list. Use `keyword` to reduce server-side results, then use `limit` to control Agent context. +Permission list uses cursor pagination. Use `keyword` to reduce server-side results and `pageSize/cursor` to control Agent context. Default response must flatten APP and SNS scopes into one stable `permissions[]` array: @@ -885,7 +844,7 @@ Default response must flatten APP and SNS scopes into one stable `permissions[]` "scopeValue": "qyapi_robot_sendmsg", "scopeName": "企业内机器人发消息权限", "scopeDesc": "企业内机器人发消息权限", - "firstLevelType": "APP", + "scopeType": "APP", "categoryTitle": "机器人", "authed": true, "canEdit": true, @@ -918,7 +877,7 @@ Default response must flatten APP and SNS scopes into one stable `permissions[]` { "scopeValue": "Contact.User.mobile", "scopeName": "个人手机号信息", - "firstLevelType": "SNS", + "scopeType": "SNS", "authed": false, "canEdit": true, "requiredApproval": true, @@ -933,7 +892,7 @@ Agent selection order: 1. Exact `scopeValue`. 2. Matched `apiList[].name`. 3. Scope name or description. -4. Category title and `firstLevelType`. +4. Category title and `scopeType`. Agent must pass final `scopeValue` to add/remove. Do not pass API name, API uuid, or category title into write tools. @@ -1074,7 +1033,7 @@ Positive `devapp` signals: | Signal | Route | | --- | --- | | `开放平台应用`, `开发者后台应用`, `企业内部应用`, `内部应用` | `devapp` | -| `agentId`, `clientId`, `appKey`, `customKey`, `appSecret`, `clientSecret` | `devapp` | +| `unifiedAppId`, `clientId`, `appKey`, `appSecret`, `clientSecret` | `devapp` | | `应用权限`, `权限点`, `API 权限`, `APP/SNS 权限`, `scopeValue` | `devapp permission ...` | | `事件订阅`, `订阅事件`, `退订事件`, `eventCode`, `可订阅事件` in an app context | `devapp event ...` | | `应用版本`, `版本发布`, `选审批人`, `发布审核` in developer backend context | `devapp version ...` | @@ -1120,11 +1079,11 @@ Intent table: | User intent | Command | Tool | | --- | --- | --- | -| "查应用/搜索应用" | `devapp list` | `list_open_dev_apps_by_condition` | -| "看应用详情/clientId/agentId" | `devapp get` | `get_open_dev_app_detail` | -| "创建应用" | `devapp create` | `create_inner_app` | -| "修改名称/描述/图标" | `devapp update` | `update_inner_app` | -| "删除应用" | `devapp delete` | `delete_inner_app` | +| "查应用/搜索应用" | `devapp list` | `list_open_dev_app` | +| "看应用详情/clientId/appKey" | `devapp get` | `get_dev_app` | +| "创建应用" | `devapp create` | `create_dev_app` | +| "修改名称/描述/图标" | `devapp update` | `update_dev_app` | +| "删除应用" | `devapp delete` | `delete_dev_app` | | "查权限/搜索 API 权限" | `permission list` | `list_open_dev_app_permissions` | | "这个权限覆盖哪些 API" | `permission list --scope` | `list_open_dev_app_permissions` | | "申请权限/开通权限" | `permission add` | `apply_open_dev_app_permissions` | @@ -1217,7 +1176,7 @@ Do not add confirmation booleans to MCP payloads. These belong to the CLI layer: | User request | Agent recipe | | --- | --- | | "查 DemoApp 应用" | `devapp list --name DemoApp`; if one result, optionally `devapp get`; if multiple, show candidates. | -| "这个 agentId 是哪个应用" | `devapp get --agent-id AGENT_ID`; if backend requires list semantics, use `devapp list --agent-id AGENT_ID`. | +| "这个 appKey 是哪个应用" | `devapp get --app-key APP_KEY`; if multiple apps match, ask for `unifiedAppId`. | | "创建企业内部应用" | `devapp create --name ... --type internal --dry-run`; after confirmation, repeat with `--yes`. | | "修改应用描述/图标" | Resolve `unifiedAppId`; `devapp update --unified-app-id ... --desc/icon ... --dry-run`; after confirmation, `--yes`. | | "删除应用" | Resolve unique app; show app summary; `devapp delete ... --dry-run`; after confirmation, `--yes`. | @@ -1237,9 +1196,9 @@ writing: 2. If the user gives an API name, search with `keyword`, then match `apiList[].name` or `apiPreview.name`. 3. If the user gives a human permission name, match `scopeName/scopeDesc`. -4. If multiple scopes remain, show `scopeValue`, `scopeName`, `firstLevelType`, +4. If multiple scopes remain, show `scopeValue`, `scopeName`, `scopeType`, `authed`, `requiredApproval`, and `apiPreview`, then ask for one. -5. For list output, cap the displayed list with `limit`; for detail output, call +5. For list output, cap the displayed list with `pageSize`; for detail output, call with `scopeValue` and return the full API coverage for one scope only. `requiredApproval=true` is not a blocker for permission add. It means the add @@ -1279,8 +1238,7 @@ details: "operation": "permission.add", "app": { "unifiedAppId": "UNIFIED_APP_ID", - "agentId": 123456, - "appName": "DemoApp" + "name": "DemoApp" }, "summary": { "appliedCount": 1, @@ -1383,7 +1341,7 @@ For each P0 tool: 6. Configure tool input JSON Schema with only Agent-facing fields. 7. Map `corpId/userId` from system context to the business request object. 8. Review every recommended mapping manually, especially fields named - `agentId`, `appId`, `appKey`, or `userId`. + `unifiedAppId`, `appKey`, `name`, or `userId`. 9. Keep `ServiceResult` output wrapper and success rule `ServiceResult.success == true`. 10. Remove blank error-text optimization rows; preserve raw @@ -1396,12 +1354,12 @@ P0 debug inputs: | Tool | Baseline debug input | | --- | --- | -| `list_open_dev_apps_by_condition` | `{"currentPage":1,"pageSize":10}` | -| `get_open_dev_app_detail` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | -| `create_inner_app` | `{"appName":"DemoApp","appDesc":"internal app"}` | -| `update_inner_app` | `{"unifiedAppId":"UNIFIED_APP_ID","appDesc":"new desc"}` | -| `delete_inner_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | -| `list_open_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","authStatus":"ALL","limit":20}` | +| `list_open_dev_app` | `{"pageSize":10}` | +| `get_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | +| `create_dev_app` | `{"appName":"DemoApp","appDesc":"internal app"}` | +| `update_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID","appDesc":"new desc"}` | +| `delete_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | +| `list_open_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","authStatus":"ALL","pageSize":20}` | | `apply_open_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValues":["SCOPE_VALUE"]}` | | `remove_open_dev_app_permission` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValue":"SCOPE_VALUE"}` | @@ -1414,8 +1372,8 @@ After publish, verify the service through MCP before using DWS: 1. Run MCP `initialize`. 2. Run `tools/list` and confirm all expected P0 tool keys exist. -3. Call `list_open_dev_apps_by_condition` with only pagination. -4. Call one exact locator scenario, such as `get_open_dev_app_detail`. +3. Call `list_open_dev_app` with only pagination. +4. Call one exact locator scenario, such as `get_dev_app`. 5. Call permission list with `authStatus=ALL` and confirm APP and SNS scopes are both represented. 6. Confirm failures preserve `errorCode/errorMsg`. @@ -1495,8 +1453,8 @@ the following are true: | `dws devapp get --name DemoApp --format json` with multiple matches | Stops and returns candidates. | | `dws devapp create --name T --type internal --dry-run --format json` | Prints payload only; no MCP call. | | `dws devapp create --name T --type internal --format json` | Refuses because write confirmation is missing. | -| `dws devapp create --name T --type internal --yes --format json` | Calls `create_inner_app`. | -| `dws devapp update --unified-app-id X --desc Y --yes --format json` | Calls `update_inner_app`. | +| `dws devapp create --name T --type internal --yes --format json` | Calls `create_dev_app`. | +| `dws devapp update --unified-app-id X --desc Y --yes --format json` | Calls `update_dev_app`. | | `dws devapp delete --unified-app-id X --format json` | Refuses because delete confirmation is missing. | | `dws devapp permission list --unified-app-id X --format json` | Returns flattened APP and SNS permissions. | | `dws devapp permission list --unified-app-id X --keyword "发送消息" --format json` | Returns candidate `permissions[]`; no automatic apply. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 27ffeb67..1fb64fee 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -28,6 +28,15 @@ import ( const ( devAppProduct = "devapp" + devAppListTool = "list_open_dev_app" + devAppGetTool = "get_dev_app" + devAppCreateTool = "create_dev_app" + devAppUpdateTool = "update_dev_app" + devAppDeleteTool = "delete_dev_app" + devAppDisableTool = "disable_dev_app" + devAppEnableTool = "enable_dev_app" + devAppCredentialsGet = "get_open_dev_app_credentials" + devAppMemberListTool = "list_open_dev_app_members" devAppMemberAddTool = "add_open_dev_app_members" devAppMemberRemoveTool = "remove_open_dev_app_members" @@ -217,9 +226,9 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { newDevAppGetCommand(runner), newDevAppCreateCommand(runner), newDevAppUpdateCommand(runner), - newDevAppLifecycleCommand(runner, "delete", "删除开放平台企业内部应用", "delete_inner_app"), - newDevAppLifecycleCommand(runner, "inactive", "停用开放平台企业内部应用", "inactive_inner_app"), - newDevAppLifecycleCommand(runner, "active", "启用开放平台企业内部应用", "active_inner_app"), + newDevAppLifecycleCommand(runner, "delete", "删除开放平台企业内部应用", devAppDeleteTool), + newDevAppLifecycleCommand(runner, "inactive", "停用开放平台企业内部应用", devAppDisableTool), + newDevAppLifecycleCommand(runner, "active", "启用开放平台企业内部应用", devAppEnableTool), credentials, webapp, permission, @@ -236,42 +245,25 @@ func newDevAppListCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "查询开放平台企业内部应用列表", - Example: " dws devapp list --name DemoApp --page 1 --page-size 20 --format json", + Example: " dws devapp list --name DemoApp --page-size 20 --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { params := map[string]any{ - "currentPage": devAppIntFlag(cmd, "page"), - "pageSize": devAppIntFlag(cmd, "page-size"), + "pageSize": devAppIntFlag(cmd, "page-size"), } - devAppPutString(params, "appName", devAppFlagOrFallback(cmd, "name", "keyword")) - devAppPutInt(params, "agentId", devAppIntFlag(cmd, "agent-id")) - devAppPutInt(params, "appId", devAppIntFlag(cmd, "app-id")) + devAppPutString(params, "cursor", devAppStringFlag(cmd, "cursor")) + devAppPutString(params, "name", devAppFlagOrFallback(cmd, "name", "keyword")) devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) - devAppPutString(params, "customKey", devAppStringFlag(cmd, "custom-key")) - devAppPutInt(params, "appGroupId", devAppIntFlag(cmd, "app-group-id")) - devAppPutString(params, "creator", devAppStringFlag(cmd, "creator")) - devAppPutString(params, "robotName", devAppStringFlag(cmd, "robot-name")) - devAppPutInt(params, "developType", devAppIntFlag(cmd, "develop-type")) - devAppPutInt(params, "filterCoolApp", devAppIntFlag(cmd, "filter-cool-app")) - devAppPutString(params, "sortType", devAppStringFlag(cmd, "sort")) - devAppPutString(params, "sortOrder", devAppStringFlag(cmd, "order")) - return runDevAppTool(runner, cmd, "list_open_dev_apps_by_condition", params) + return runDevAppTool(runner, cmd, devAppListTool, params) }, } - cmd.Flags().Int("page", 1, "分页页码,从 1 开始") cmd.Flags().Int("page-size", 20, "分页大小") + cmd.Flags().String("cursor", "", "游标分页 cursor,首次查询为空") cmd.Flags().String("name", "", "应用名称关键词") cmd.Flags().String("keyword", "", "--name 的兼容别名") _ = cmd.Flags().MarkHidden("keyword") - addDevAppLocatorFlagsWithoutName(cmd) - cmd.Flags().Int("app-group-id", 0, "应用分组 ID") - cmd.Flags().String("creator", "", "创建人名称关键词") - cmd.Flags().String("robot-name", "", "机器人名称关键词") - cmd.Flags().Int("develop-type", 0, "开发类型枚举;不确定时不要传") - cmd.Flags().Int("filter-cool-app", 0, "酷应用过滤枚举;不确定时不要传") - cmd.Flags().String("sort", "", "排序字段,如 gmt_modified") - cmd.Flags().String("order", "", "排序方向 asc 或 desc") + cmd.Flags().String("app-key", "", "appKey/clientId") preferLegacyLeaf(cmd) return cmd } @@ -280,18 +272,18 @@ func newDevAppGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "查询开放平台企业内部应用详情", - Example: " dws devapp get --agent-id 123456 --format json", + Example: " dws devapp get --unified-app-id UNIFIED_APP_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppLocatorParams(cmd, true) + params := devAppMcpLocatorParams(cmd, true) if len(params) == 0 { - return devAppLocatorRequired(true) + return devAppMcpLocatorRequired(true) } - return runDevAppTool(runner, cmd, "get_open_dev_app_detail", params) + return runDevAppTool(runner, cmd, devAppGetTool, params) }, } - addDevAppLocatorFlags(cmd) + addDevAppMcpLocatorFlags(cmd, true) preferLegacyLeaf(cmd) return cmd } @@ -318,7 +310,7 @@ func newDevAppCreateCommand(runner executor.Runner) *cobra.Command { params := map[string]any{"appName": name} devAppPutString(params, "appDesc", devAppStringFlag(cmd, "desc")) devAppPutString(params, "appIcon", devAppStringFlag(cmd, "icon")) - return runDevAppTool(runner, cmd, "create_inner_app", params) + return runDevAppTool(runner, cmd, devAppCreateTool, params) }, } cmd.Flags().String("name", "", "应用名称 (必填)") @@ -333,16 +325,16 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "update", Short: "修改开放平台企业内部应用基础信息", - Example: " dws devapp update --agent-id 123456 --name DemoApp2 --dry-run --format json", + Example: " dws devapp update --unified-app-id UNIFIED_APP_ID --name DemoApp2 --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "update"); err != nil { return err } - params := devAppLocatorParams(cmd, false) + params := devAppMcpLocatorParams(cmd, false) if len(params) == 0 { - return devAppLocatorRequired(false) + return devAppMcpLocatorRequired(false) } updates := 0 if v := devAppStringFlag(cmd, "name"); v != "" { @@ -360,10 +352,10 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { if updates == 0 { return apperrors.NewValidation("at least one update field is required: --name, --desc, or --icon") } - return runDevAppTool(runner, cmd, "update_inner_app", params) + return runDevAppTool(runner, cmd, devAppUpdateTool, params) }, } - addDevAppLocatorFlagsWithoutName(cmd) + addDevAppMcpLocatorFlags(cmd, false) cmd.Flags().String("name", "", "新的应用名称") cmd.Flags().String("desc", "", "新的应用描述") cmd.Flags().String("icon", "", "新的应用图标 mediaId") @@ -375,18 +367,18 @@ func newDevAppCredentialsGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "读取开放平台应用凭证", - Example: " dws devapp credentials get --agent-id 123456 --format json", + Example: " dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppLocatorParams(cmd, false) + params := devAppCredentialsLocatorParams(cmd) if len(params) == 0 { - return devAppLocatorRequired(false) + return devAppCredentialsLocatorRequired() } - return runDevAppTool(runner, cmd, "get_open_dev_app_credentials", params) + return runDevAppTool(runner, cmd, devAppCredentialsGet, params) }, } - addDevAppLocatorFlagsWithoutName(cmd) + addDevAppCredentialsLocatorFlags(cmd) preferLegacyLeaf(cmd) return cmd } @@ -401,14 +393,14 @@ func newDevAppLifecycleCommand(runner executor.Runner, use, short, tool string) if err := devAppRequireWriteGuard(cmd, use); err != nil { return err } - params := devAppLocatorParams(cmd, false) + params := devAppMcpLocatorParams(cmd, true) if len(params) == 0 { - return devAppLocatorRequired(false) + return devAppMcpLocatorRequired(true) } return runDevAppTool(runner, cmd, tool, params) }, } - addDevAppLocatorFlagsWithoutName(cmd) + addDevAppMcpLocatorFlags(cmd, true) preferLegacyLeaf(cmd) return cmd } @@ -417,18 +409,18 @@ func newDevAppWebappGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "查询网页应用配置", - Example: " dws devapp webapp get --agent-id 123456 --format json", + Example: " dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppAppLocatorParams(cmd) + params := devAppMcpLocatorParams(cmd, false) if len(params) == 0 { - return devAppAppLocatorRequired() + return devAppMcpLocatorRequired(false) } return runDevAppTool(runner, cmd, "get_webapp_config", params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppMcpLocatorFlags(cmd, false) preferLegacyLeaf(cmd) return cmd } @@ -437,45 +429,51 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "config", Short: "配置网页应用能力", - Example: " dws devapp webapp config --agent-id 123456 --homepage-link https://example.com --dry-run --format json", + Example: " dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "webapp config"); err != nil { return err } - params := devAppAppLocatorParams(cmd) + params := devAppMcpLocatorParams(cmd, true) if len(params) == 0 { - return devAppAppLocatorRequired() + return devAppMcpLocatorRequired(true) } updates := 0 if v := devAppStringFlag(cmd, "h5-page-type"); v != "" { params["h5PageType"] = v updates++ } - if v := devAppStringFlag(cmd, "homepage-link"); v != "" { - params["homepageLink"] = v + if v := devAppFlagOrFallback(cmd, "homepage-url", "homepage-link"); v != "" { + params["homepageUrl"] = v updates++ } - if v := devAppStringFlag(cmd, "pc-homepage-link"); v != "" { - params["pcHomepageLink"] = v + if v := devAppFlagOrFallback(cmd, "pc-homepage-url", "pc-homepage-link"); v != "" { + params["pcHomepageUrl"] = v updates++ } - if v := devAppStringFlag(cmd, "omp-link"); v != "" { - params["ompLink"] = v + if v := devAppFlagOrFallback(cmd, "omp-url", "omp-link"); v != "" { + params["ompUrl"] = v updates++ } if updates == 0 { - return apperrors.NewValidation("at least one webapp field is required: --h5-page-type, --homepage-link, --pc-homepage-link, or --omp-link") + return apperrors.NewValidation("at least one webapp field is required: --h5-page-type, --homepage-url, --pc-homepage-url, or --omp-url") } return runDevAppTool(runner, cmd, "set_webapp_config", params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppMcpLocatorFlags(cmd, true) cmd.Flags().String("h5-page-type", "", "网页应用生效端/页面类型") - cmd.Flags().String("homepage-link", "", "移动端首页地址") - cmd.Flags().String("pc-homepage-link", "", "PC 端首页地址") - cmd.Flags().String("omp-link", "", "管理后台地址") + cmd.Flags().String("homepage-url", "", "移动端首页地址") + cmd.Flags().String("homepage-link", "", "--homepage-url 的兼容别名") + _ = cmd.Flags().MarkHidden("homepage-link") + cmd.Flags().String("pc-homepage-url", "", "PC 端首页地址") + cmd.Flags().String("pc-homepage-link", "", "--pc-homepage-url 的兼容别名") + _ = cmd.Flags().MarkHidden("pc-homepage-link") + cmd.Flags().String("omp-url", "", "管理后台地址") + cmd.Flags().String("omp-link", "", "--omp-url 的兼容别名") + _ = cmd.Flags().MarkHidden("omp-link") preferLegacyLeaf(cmd) return cmd } @@ -485,25 +483,25 @@ func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { Use: "list", Aliases: []string{"search", "detail"}, Short: "查询开放平台应用权限列表", - Example: " dws devapp permission list --agent-id 123456 --keyword 通讯录 --limit 20 --format json", + Example: " dws devapp permission list --unified-app-id UNIFIED_APP_ID --keyword 通讯录 --page-size 20 --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppAppLocatorParams(cmd) + params := devAppPermissionLocatorParams(cmd) if len(params) == 0 { - return devAppAppLocatorRequired() + return devAppPermissionLocatorRequired() } devAppPutString(params, "keyword", devAppStringFlag(cmd, "keyword")) devAppPutString(params, "scopeValue", devAppFlagOrFallback(cmd, "scope", "permission")) devAppPutString(params, "authStatus", strings.ToUpper(devAppStringFlag(cmd, "status"))) - devAppPutString(params, "firstLevelType", strings.ToUpper(devAppStringFlag(cmd, "scope-type"))) + devAppPutString(params, "scopeType", strings.ToUpper(devAppStringFlag(cmd, "scope-type"))) devAppPutString(params, "apiStatus", devAppStringFlag(cmd, "api-status")) - devAppPutInt(params, "limit", devAppIntFlag(cmd, "limit")) - devAppPutInt(params, "offset", devAppIntFlag(cmd, "offset")) + devAppPutString(params, "cursor", devAppStringFlag(cmd, "cursor")) + devAppPutInt(params, "pageSize", devAppIntFlag(cmd, "page-size")) return runDevAppTool(runner, cmd, "list_open_dev_app_permissions", params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppPermissionLocatorFlags(cmd) cmd.Flags().String("keyword", "", "权限名、权限点、接口名关键词") cmd.Flags().String("scope", "", "精确权限点 scopeValue") cmd.Flags().String("permission", "", "--scope 的兼容别名") @@ -511,8 +509,8 @@ func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { cmd.Flags().String("status", "ALL", "权限状态:ALL、AUTHED、UNAUTHED") cmd.Flags().String("scope-type", "", "权限一级类型:APP 或 SNS") cmd.Flags().String("api-status", "", "开发者后台 apiStatus 过滤") - cmd.Flags().Int("limit", 20, "返回数量上限") - cmd.Flags().Int("offset", 0, "返回偏移量") + cmd.Flags().Int("page-size", 20, "返回数量上限") + cmd.Flags().String("cursor", "", "游标分页 cursor,首次查询为空") preferLegacyLeaf(cmd) return cmd } @@ -521,16 +519,16 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "申请开放平台应用权限点", - Example: " dws devapp permission add --agent-id 123456 --permissions Contact.User.mobile --dry-run --format json", + Example: " dws devapp permission add --unified-app-id UNIFIED_APP_ID --permissions Contact.User.mobile --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "permission add"); err != nil { return err } - params := devAppAppLocatorParams(cmd) + params := devAppPermissionLocatorParams(cmd) if len(params) == 0 { - return devAppAppLocatorRequired() + return devAppPermissionLocatorRequired() } scopes := devAppPermissionScopes(cmd) if len(scopes) == 0 { @@ -540,7 +538,7 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { return runDevAppTool(runner, cmd, "apply_open_dev_app_permissions", params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppPermissionLocatorFlags(cmd) cmd.Flags().StringSlice("permissions", nil, "权限点 scopeValue,多个用逗号分隔") cmd.Flags().String("scope", "", "--permissions 的兼容别名,单个权限点 scopeValue") cmd.Flags().String("permission", "", "--permissions 的兼容别名,单个权限点 scopeValue") @@ -554,16 +552,16 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "取消开放平台应用权限点", - Example: " dws devapp permission remove --agent-id 123456 --permission Contact.User.mobile --dry-run --format json", + Example: " dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission Contact.User.mobile --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "permission remove"); err != nil { return err } - params := devAppAppLocatorParams(cmd) + params := devAppPermissionLocatorParams(cmd) if len(params) == 0 { - return devAppAppLocatorRequired() + return devAppPermissionLocatorRequired() } scope := devAppFlagOrFallback(cmd, "permission", "scope") if scope == "" { @@ -573,7 +571,7 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { return runDevAppTool(runner, cmd, "remove_open_dev_app_permission", params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppPermissionLocatorFlags(cmd) cmd.Flags().String("permission", "", "待取消权限点 scopeValue") cmd.Flags().String("scope", "", "--permission 的兼容别名") _ = cmd.Flags().MarkHidden("scope") @@ -1259,50 +1257,61 @@ func splitDevAppList(raw string) []string { return values } -func addDevAppLocatorFlags(cmd *cobra.Command) { - addDevAppLocatorFlagsWithoutName(cmd) - cmd.Flags().String("name", "", "应用名称关键词;写操作前必须唯一命中") -} - -func addDevAppLocatorFlagsWithoutName(cmd *cobra.Command) { - addDevAppAppLocatorFlags(cmd) - cmd.Flags().Int("app-id", 0, "兼容 appId") - cmd.Flags().String("app-key", "", "appKey/clientId") - cmd.Flags().String("custom-key", "", "customKey") -} - -func addDevAppAppLocatorFlags(cmd *cobra.Command) { +func addDevAppMcpLocatorFlags(cmd *cobra.Command, includeName bool) { cmd.Flags().String("unified-app-id", "", "统一应用 ID") - cmd.Flags().Int("agent-id", 0, "应用 agentId") -} - -func devAppLocatorParams(cmd *cobra.Command, includeName bool) map[string]any { - params := devAppAppLocatorParams(cmd) - devAppPutInt(params, "appId", devAppIntFlag(cmd, "app-id")) - devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) - devAppPutString(params, "customKey", devAppStringFlag(cmd, "custom-key")) + cmd.Flags().String("app-key", "", "appKey/clientId") if includeName { - devAppPutString(params, "appName", devAppStringFlag(cmd, "name")) + cmd.Flags().String("name", "", "应用名称关键词;写操作前必须唯一命中") } - return params } -func devAppAppLocatorParams(cmd *cobra.Command) map[string]any { +func devAppMcpLocatorParams(cmd *cobra.Command, includeName bool) map[string]any { params := map[string]any{} devAppPutString(params, "unifiedAppId", devAppStringFlag(cmd, "unified-app-id")) - devAppPutInt(params, "agentId", devAppIntFlag(cmd, "agent-id")) + devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) + if includeName { + devAppPutString(params, "name", devAppStringFlag(cmd, "name")) + } return params } -func devAppLocatorRequired(includeName bool) error { - if includeName { - return apperrors.NewValidation("one app locator is required: --agent-id, --unified-app-id, --app-key, --custom-key, --app-id, or --name") - } - return apperrors.NewValidation("one app locator is required: --agent-id, --unified-app-id, --app-key, --custom-key, or --app-id") +func addDevAppCredentialsLocatorFlags(cmd *cobra.Command) { + cmd.Flags().String("unified-app-id", "", "统一应用 ID") + cmd.Flags().String("app-key", "", "appKey/clientId") + cmd.Flags().String("name", "", "应用名称关键词;必须唯一命中") } -func devAppAppLocatorRequired() error { - return apperrors.NewValidation("one app locator is required: --agent-id or --unified-app-id") +func devAppCredentialsLocatorParams(cmd *cobra.Command) map[string]any { + params := map[string]any{} + devAppPutString(params, "unifiedAppId", devAppStringFlag(cmd, "unified-app-id")) + devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) + devAppPutString(params, "appName", devAppStringFlag(cmd, "name")) + return params +} + +func devAppCredentialsLocatorRequired() error { + return apperrors.NewValidation("one app locator is required: --unified-app-id, --app-key, or --name") +} + +func devAppMcpLocatorRequired(includeName bool) error { + if includeName { + return apperrors.NewValidation("one app locator is required: --unified-app-id, --app-key, or --name") + } + return apperrors.NewValidation("one app locator is required: --unified-app-id or --app-key") +} + +func addDevAppPermissionLocatorFlags(cmd *cobra.Command) { + cmd.Flags().String("unified-app-id", "", "统一应用 ID") +} + +func devAppPermissionLocatorParams(cmd *cobra.Command) map[string]any { + params := map[string]any{} + devAppPutString(params, "unifiedAppId", devAppStringFlag(cmd, "unified-app-id")) + return params +} + +func devAppPermissionLocatorRequired() error { + return apperrors.NewValidation("one app locator is required: --unified-app-id") } func devAppRequireWriteGuard(cmd *cobra.Command, operation string) error { diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 7f0567b6..af49d182 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -484,20 +484,19 @@ func TestDevAppListBuildsListByConditionParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"list", "--name", "Waker", "--page", "2", "--page-size", "5", "--sort", "gmt_modified", "--order", "desc"}) + root.SetArgs([]string{"list", "--name", "Waker", "--app-key", "dingxxx", "--page-size", "5", "--cursor", "next-1"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "list_open_dev_apps_by_condition" { - t.Fatalf("Tool = %q, want list_open_dev_apps_by_condition", got) + if got := runner.last.Tool; got != "list_open_dev_app" { + t.Fatalf("Tool = %q, want list_open_dev_app", got) } want := map[string]any{ - "currentPage": 2, - "pageSize": 5, - "appName": "Waker", - "sortType": "gmt_modified", - "sortOrder": "desc", + "pageSize": 5, + "cursor": "next-1", + "name": "Waker", + "appKey": "dingxxx", } if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) @@ -515,8 +514,8 @@ func TestDevAppGetBuildsDetailParams(t *testing.T) { if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "get_open_dev_app_detail" { - t.Fatalf("Tool = %q, want get_open_dev_app_detail", got) + if got := runner.last.Tool; got != "get_dev_app" { + t.Fatalf("Tool = %q, want get_dev_app", got) } want := map[string]any{"unifiedAppId": "u-1"} if !reflect.DeepEqual(runner.last.Params, want) { @@ -545,8 +544,8 @@ func TestDevAppCreateUsesCurrentInnerToolAndWriteGuard(t *testing.T) { if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "create_inner_app" { - t.Fatalf("Tool = %q, want create_inner_app", got) + if got := runner.last.Tool; got != "create_dev_app" { + t.Fatalf("Tool = %q, want create_dev_app", got) } want := map[string]any{"appName": "Demo", "appDesc": "internal app"} @@ -561,15 +560,15 @@ func TestDevAppUpdateUsesCurrentInnerTool(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "update", "--agent-id", "123", "--desc", "new desc", "--yes"}) + root.SetArgs([]string{"devapp", "update", "--unified-app-id", "u-1", "--desc", "new desc", "--yes"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "update_inner_app" { - t.Fatalf("Tool = %q, want update_inner_app", got) + if got := runner.last.Tool; got != "update_dev_app" { + t.Fatalf("Tool = %q, want update_dev_app", got) } - want := map[string]any{"agentId": 123, "appDesc": "new desc"} + want := map[string]any{"unifiedAppId": "u-1", "appDesc": "new desc"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -583,21 +582,21 @@ func TestDevAppLifecycleBuildsLocatorParams(t *testing.T) { wantParams map[string]any }{ { - name: "delete by agent id", - args: []string{"delete", "--agent-id", "123", "--yes"}, - wantTool: "delete_inner_app", - wantParams: map[string]any{"agentId": 123}, + name: "delete by name", + args: []string{"delete", "--name", "Demo", "--yes"}, + wantTool: "delete_dev_app", + wantParams: map[string]any{"name": "Demo"}, }, { name: "inactive by unified app id", args: []string{"inactive", "--unified-app-id", "u-1", "--yes"}, - wantTool: "inactive_inner_app", + wantTool: "disable_dev_app", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { name: "active by app key", args: []string{"active", "--app-key", "dingxxx", "--yes"}, - wantTool: "active_inner_app", + wantTool: "enable_dev_app", wantParams: map[string]any{"appKey": "dingxxx"}, }, } @@ -639,12 +638,12 @@ func TestDevAppWebappCommandsBuildParams(t *testing.T) { }, { name: "config", - args: []string{"webapp", "config", "--unified-app-id", "u-1", "--homepage-link", "https://example.com", "--pc-homepage-link", "https://pc.example.com", "--yes"}, + args: []string{"webapp", "config", "--unified-app-id", "u-1", "--homepage-url", "https://example.com", "--pc-homepage-url", "https://pc.example.com", "--yes"}, wantTool: "set_webapp_config", wantParams: map[string]any{ - "unifiedAppId": "u-1", - "homepageLink": "https://example.com", - "pcHomepageLink": "https://pc.example.com", + "unifiedAppId": "u-1", + "homepageUrl": "https://example.com", + "pcHomepageUrl": "https://pc.example.com", }, }, } @@ -680,31 +679,32 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { }{ { name: "list", - args: []string{"permission", "list", "--agent-id", "123", "--keyword", "手机号", "--status", "all", "--limit", "5"}, + args: []string{"permission", "list", "--unified-app-id", "u-1", "--keyword", "手机号", "--status", "all", "--page-size", "5", "--cursor", "next-1"}, wantTool: "list_open_dev_app_permissions", wantParams: map[string]any{ - "agentId": 123, - "keyword": "手机号", - "authStatus": "ALL", - "limit": 5, + "unifiedAppId": "u-1", + "keyword": "手机号", + "authStatus": "ALL", + "pageSize": 5, + "cursor": "next-1", }, }, { name: "add", - args: []string{"permission", "add", "--agent-id", "123", "--permissions", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, + args: []string{"permission", "add", "--unified-app-id", "u-1", "--permissions", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, wantTool: "apply_open_dev_app_permissions", wantParams: map[string]any{ - "agentId": 123, - "scopeValues": []string{"Contact.User.mobile", "qyapi_robot_sendmsg"}, + "unifiedAppId": "u-1", + "scopeValues": []string{"Contact.User.mobile", "qyapi_robot_sendmsg"}, }, }, { name: "remove", - args: []string{"permission", "remove", "--agent-id", "123", "--permission", "Contact.User.mobile", "--yes"}, + args: []string{"permission", "remove", "--unified-app-id", "u-1", "--permission", "Contact.User.mobile", "--yes"}, wantTool: "remove_open_dev_app_permission", wantParams: map[string]any{ - "agentId": 123, - "scopeValue": "Contact.User.mobile", + "unifiedAppId": "u-1", + "scopeValue": "Contact.User.mobile", }, }, } @@ -737,7 +737,7 @@ func TestDevAppCredentialsGetBuildsParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "credentials", "get", "--agent-id", "123"}) + root.SetArgs([]string{"devapp", "credentials", "get", "--unified-app-id", "u-1"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -745,7 +745,7 @@ func TestDevAppCredentialsGetBuildsParams(t *testing.T) { if got := runner.last.Tool; got != "get_open_dev_app_credentials" { t.Fatalf("Tool = %q, want get_open_dev_app_credentials", got) } - want := map[string]any{"agentId": 123} + want := map[string]any{"unifiedAppId": "u-1"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -766,7 +766,7 @@ func TestDevAppCredentialsGetKeepsSecretFields(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "credentials", "get", "--agent-id", "123"}) + root.SetArgs([]string{"devapp", "credentials", "get", "--unified-app-id", "u-1"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) diff --git a/skills/dingtalk-devapp/SKILL.md b/skills/dingtalk-devapp/SKILL.md index 664f1cf9..6a13b89c 100644 --- a/skills/dingtalk-devapp/SKILL.md +++ b/skills/dingtalk-devapp/SKILL.md @@ -1,6 +1,6 @@ --- name: dingtalk-devapp -description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/事件订阅/订阅事件/退订事件/eventCode。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 +description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/unifiedAppId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/事件订阅/订阅事件/退订事件/eventCode。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 cli_version: ">=1.0.15" metadata: category: product @@ -19,7 +19,7 @@ metadata: `应用` 是泛词。只有出现以下信号才路由到 devapp: - `开放平台应用/开发者后台应用/企业内部应用/内部应用` -- `agentId/clientId/appKey/appSecret/customKey` +- `unifiedAppId/clientId/appKey/appSecret` - `应用权限/权限点/scopeValue/应用成员/安全配置/IP 白名单` - `创建机器人/智能体机器人/机器人配置/机器人回调地址`(→ `robot`) - `应用版本/版本发布/发布审核/选审批人`(→ `version`) @@ -31,7 +31,7 @@ metadata: 1. 所有命令加 `--format json`。 2. 写操作先 `--dry-run`,确认后才加 `--yes`。 -3. 应用名/appKey/customKey 命中多条时展示候选,不取第一条。 +3. 应用名/appKey 命中多条时展示候选,不取第一条。 4. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名。 5. 主动读取密钥走 `credentials get`;任何 `devapp get`/详情返回里的 `clientSecret/appSecret` 都按敏感凭证脱敏,不向用户展开。 diff --git a/skills/dingtalk-devapp/references/app-crud.md b/skills/dingtalk-devapp/references/app-crud.md index 7898e729..0d773886 100644 --- a/skills/dingtalk-devapp/references/app-crud.md +++ b/skills/dingtalk-devapp/references/app-crud.md @@ -9,31 +9,26 @@ | 优先级 | 标识 | 处理 | |--------|------|------| | 1 | `--unified-app-id` | 直接使用 | -| 2 | `--agent-id` / `--app-id` | 直接使用 | -| 3 | `--app-key` / `--custom-key` | 先查询,唯一命中才继续 | -| 4 | `--name` | 模糊搜索,写操作必须唯一命中 | +| 2 | `--app-key` | appKey/clientId,唯一命中才继续 | +| 3 | `--name` | 应用名称关键词,写操作必须唯一命中 | ## 应用列表 ```bash dws devapp list --format json dws devapp list --name DemoApp --format json -dws devapp list --agent-id 123456 --format json -dws devapp list --creator 张三 --sort gmt_modified --order desc --format json +dws devapp list --app-key dingxxx --format json +dws devapp list --name DemoApp --page-size 20 --cursor NEXT_CURSOR --format json ``` -MCP tool: `list_open_dev_apps_by_condition` +MCP tool: `list_open_dev_app` | CLI | MCP | 说明 | |-----|-----|------| -| `--page` | `currentPage` | 1-based,默认 1 | | `--page-size` | `pageSize` | 默认 20 | -| `--name` / `--keyword` | `appName` | 应用名搜索 | -| `--agent-id` | `agentId` | 精确定位 | +| `--cursor` | `cursor` | 游标,首次为空,用上一页 `nextCursor` 继续 | +| `--name` / `--keyword` | `name` | 应用名搜索 | | `--app-key` | `appKey` | appKey/clientId | -| `--creator` | `creator` | 创建人关键词 | -| `--sort` | `sortType` | 如 `gmt_modified` | -| `--order` | `sortOrder` | `asc` / `desc` | ## 应用状态字段 @@ -52,10 +47,11 @@ MCP tool: `list_open_dev_apps_by_condition` ```bash dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --agent-id 123456 --format json +dws devapp get --app-key dingxxx --format json +dws devapp get --name DemoApp --format json ``` -MCP tool: `get_open_dev_app_detail` +MCP tool: `get_dev_app` 详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 @@ -66,7 +62,7 @@ dws devapp create --name DemoApp --desc "内部应用" --type internal --dry-run dws devapp create --name DemoApp --desc "内部应用" --type internal --yes --format json ``` -MCP tool: `create_inner_app` +MCP tool: `create_dev_app` | CLI | MCP | 必填 | |-----|-----|------| @@ -83,7 +79,7 @@ dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-r dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --yes --format json ``` -MCP tool: `update_inner_app` +MCP tool: `update_dev_app` 至少提供一个更新字段:`--name` / `--desc` / `--icon`。 @@ -97,7 +93,7 @@ dws devapp active --unified-app-id ID --dry-run --format json dws devapp active --unified-app-id ID --yes --format json ``` -MCP tools: `inactive_inner_app` / `active_inner_app` +MCP tools: `disable_dev_app` / `enable_dev_app` 停用保留数据但应用不可用,可通过 `active` 恢复。 @@ -110,7 +106,7 @@ dws devapp delete --unified-app-id ID --dry-run --format json dws devapp delete --unified-app-id ID --yes --format json ``` -MCP tool: `delete_inner_app` +MCP tool: `delete_dev_app` 删除前必须展示应用摘要。删除为异步操作,成功后应用延迟从列表消失。 diff --git a/skills/dingtalk-devapp/references/credentials-webapp.md b/skills/dingtalk-devapp/references/credentials-webapp.md index 0f1dec02..9ce8010f 100644 --- a/skills/dingtalk-devapp/references/credentials-webapp.md +++ b/skills/dingtalk-devapp/references/credentials-webapp.md @@ -4,7 +4,8 @@ ```bash dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --agent-id 123456 --format json +dws devapp credentials get --app-key dingxxx --format json +dws devapp credentials get --name DemoApp --format json ``` MCP tool: `get_open_dev_app_credentials` @@ -28,19 +29,19 @@ MCP tool: `get_open_dev_app_credentials` ## 网页应用查询 ```bash -dws devapp webapp get --agent-id 123456 --format json dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json +dws devapp webapp get --app-key dingxxx --format json ``` MCP tool: `get_webapp_config` -未配置网页应用前可能只返回 `agentId`。 +未配置网页应用前可能只返回空配置。 ## 网页应用配置 ```bash -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --dry-run --format json -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --pc-homepage-link https://example.com/pc --yes --format json +dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --dry-run --format json +dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --pc-homepage-url https://example.com/pc --yes --format json ``` MCP tool: `set_webapp_config` @@ -48,8 +49,8 @@ MCP tool: `set_webapp_config` | CLI | MCP | 说明 | |-----|-----|------| | `--h5-page-type` | `h5PageType` | 网页应用生效端 | -| `--homepage-link` | `homepageLink` | 移动端首页地址 | -| `--pc-homepage-link` | `pcHomepageLink` | PC 端首页地址 | -| `--omp-link` | `ompLink` | 管理后台地址 | +| `--homepage-url` | `homepageUrl` | 移动端首页地址 | +| `--pc-homepage-url` | `pcHomepageUrl` | PC 端首页地址 | +| `--omp-url` | `ompUrl` | 管理后台地址 | 至少提供一个配置字段。`h5PageType` 未显式传入时,不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 diff --git a/skills/dingtalk-devapp/references/permissions.md b/skills/dingtalk-devapp/references/permissions.md index 17cc6d95..3b5a310c 100644 --- a/skills/dingtalk-devapp/references/permissions.md +++ b/skills/dingtalk-devapp/references/permissions.md @@ -16,13 +16,12 @@ MCP tool: `list_open_dev_app_permissions` | CLI | MCP | 说明 | |-----|-----|------| | `--unified-app-id` | `unifiedAppId` | 应用定位 | -| `--agent-id` | `agentId` | 应用定位 | | `--keyword` | `keyword` | 权限名/API 名关键词 | | `--status` | `authStatus` | `ALL` / `AUTHED` / `UNAUTHED` | -| `--scope-type` | `firstLevelType` | `APP` / `SNS`,为空返回两者 | +| `--scope-type` | `scopeType` | `APP` / `SNS`,为空返回两者 | | `--scope` | `scopeValue` | 单权限详情模式 | -| `--limit` | `limit` | 每页返回数量,默认 20,建议不超过 50 | -| `--offset` | `offset` | 翻页偏移量,默认 0 | +| `--page-size` | `pageSize` | 每页返回数量,默认 20,建议不超过 50 | +| `--cursor` | `cursor` | 游标,首次为空,用上一页 `nextCursor` 继续 | **状态判断:** @@ -47,15 +46,14 @@ MCP tool: `list_open_dev_app_permissions` **翻页:** -权限列表支持 `--limit` + `--offset` 分页。一个应用可能有 150+ 个权限点,一次查不完时用 offset 翻页: +权限列表支持 `--page-size` + `--cursor` 分页。一个应用可能有 150+ 个权限点,一次查不完时用返回的 `nextCursor` 继续: ```bash -dws devapp permission list --unified-app-id ID --limit 50 --format json # 第 1 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 50 --format json # 第 2 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 100 --format json # 第 3 页 +dws devapp permission list --unified-app-id ID --page-size 50 --format json +dws devapp permission list --unified-app-id ID --page-size 50 --cursor NEXT_CURSOR --format json ``` -当返回条数 < limit 时表示已到末尾。 +当返回无 `nextCursor` 或返回条数小于 `pageSize` 时表示已到末尾。 **规则:** - `permission search` 和 `permission detail` 是 `list` 的 CLI 别名,不是独立 MCP tool。 diff --git a/skills/dingtalk-devapp/references/workflows.md b/skills/dingtalk-devapp/references/workflows.md index bed457c6..b07be648 100644 --- a/skills/dingtalk-devapp/references/workflows.md +++ b/skills/dingtalk-devapp/references/workflows.md @@ -8,7 +8,7 @@ 1. 创建应用 dws devapp create --name "应用名" --desc "描述" --type internal --dry-run --format json → 确认后加 --yes - → 记录返回的 unifiedAppId 和 agentId + → 记录返回的 unifiedAppId 和 appKey 2. 确认创建成功 dws devapp get --unified-app-id --format json @@ -17,12 +17,12 @@ dws devapp credentials get --unified-app-id --format json 4. 配置网页应用(按需) - dws devapp webapp config --agent-id --homepage-link --dry-run --format json + dws devapp webapp config --unified-app-id --homepage-url --dry-run --format json → 确认后加 --yes 5. 验证网页应用配置 - dws devapp webapp get --agent-id --format json - → 确认返回 homepageLink/pcHomepageLink 等 + dws devapp webapp get --unified-app-id --format json + → 确认返回 homepageUrl/pcHomepageUrl 等 6. 申请权限(按需) dws devapp permission list --unified-app-id --keyword "关键词" --format json diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 570549c2..6f512755 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -8,7 +8,7 @@ 1. 所有命令加 `--format json`。 2. 写操作先 `--dry-run`,确认后才加 `--yes`。 -3. 应用名/appKey/customKey 命中多条时展示候选,不取第一条。 +3. 应用名/appKey 命中多条时展示候选,不取第一条。 4. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名。 5. 主动读取密钥走 `credentials get`;任何 `devapp get`/详情返回里的 `clientSecret/appSecret` 都按敏感凭证脱敏,不向用户展开。 @@ -17,9 +17,8 @@ | 优先级 | 标识 | 处理 | |--------|------|------| | 1 | `--unified-app-id` | 直接使用 | -| 2 | `--agent-id` / `--app-id` | 直接使用 | -| 3 | `--app-key` / `--custom-key` | 先查询,唯一命中才继续 | -| 4 | `--name` | 模糊搜索,写操作必须唯一命中 | +| 2 | `--app-key` | appKey/clientId,唯一命中才继续 | +| 3 | `--name` | 应用名称关键词,写操作必须唯一命中 | --- @@ -30,21 +29,17 @@ ```bash dws devapp list --format json dws devapp list --name DemoApp --format json -dws devapp list --agent-id 123456 --format json +dws devapp list --app-key dingxxx --format json ``` -MCP tool: `list_open_dev_apps_by_condition` +MCP tool: `list_open_dev_app` | CLI | MCP | 说明 | |-----|-----|------| -| `--page` | `currentPage` | 1-based,默认 1 | | `--page-size` | `pageSize` | 默认 20 | -| `--name` / `--keyword` | `appName` | 应用名搜索 | -| `--agent-id` | `agentId` | 精确定位 | +| `--cursor` | `cursor` | 游标,首次为空 | +| `--name` / `--keyword` | `name` | 应用名搜索 | | `--app-key` | `appKey` | appKey/clientId | -| `--creator` | `creator` | 创建人关键词 | -| `--sort` | `sortType` | 如 `gmt_modified` | -| `--order` | `sortOrder` | `asc` / `desc` | 应用状态字段:列表/详情原始字段里如果出现 `status` / `appStatus`,`0=IN_ACTIVE` 已停用、`1=ACTIVE` 已激活、`2=WAIT_ACTIVE` 待激活、`3=EXPIRED` 已过期。执行 `inactive/active` 后必须回读 `get` 或 `list`:看到 `status=0` 才算停用完成,看到 `status=1` 才算启用完成。`versionStatus` 是版本状态,不要和应用启停状态混用。 @@ -54,7 +49,7 @@ MCP tool: `list_open_dev_apps_by_condition` dws devapp get --unified-app-id UNIFIED_APP_ID --format json ``` -MCP tool: `get_open_dev_app_detail` +MCP tool: `get_dev_app` 详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 @@ -65,7 +60,7 @@ dws devapp create --name DemoApp --desc "内部应用" --type internal --dry-run dws devapp create --name DemoApp --desc "内部应用" --type internal --yes --format json ``` -MCP tool: `create_inner_app`。`--type` 只做 CLI 校验,当前仅支持 `internal`。 +MCP tool: `create_dev_app`。`--type` 只做 CLI 校验,当前仅支持 `internal`。 ### 修改 @@ -73,7 +68,7 @@ MCP tool: `create_inner_app`。`--type` 只做 CLI 校验,当前仅支持 `int dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-run --format json ``` -MCP tool: `update_inner_app`。至少一个更新字段:`--name` / `--desc` / `--icon`。 +MCP tool: `update_dev_app`。至少一个更新字段:`--name` / `--desc` / `--icon`。 ### 停用 / 启用 @@ -82,7 +77,7 @@ dws devapp inactive --unified-app-id ID --dry-run --format json dws devapp active --unified-app-id ID --dry-run --format json ``` -MCP tools: `inactive_inner_app` / `active_inner_app` +MCP tools: `disable_dev_app` / `enable_dev_app` ### 删除 @@ -90,7 +85,7 @@ MCP tools: `inactive_inner_app` / `active_inner_app` dws devapp delete --unified-app-id ID --dry-run --format json ``` -MCP tool: `delete_inner_app`。删除前必须展示应用摘要,异步生效。 +MCP tool: `delete_dev_app`。删除前必须展示应用摘要,异步生效。 --- @@ -107,8 +102,8 @@ MCP tool: `get_open_dev_app_credentials`。返回含 `clientSecret/appSecret`, ### 网页应用 ```bash -dws devapp webapp get --agent-id AGENT_ID --format json -dws devapp webapp config --agent-id AGENT_ID --homepage-link https://example.com --dry-run --format json +dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json +dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com --dry-run --format json ``` MCP tools: `get_webapp_config` / `set_webapp_config` @@ -133,10 +128,10 @@ MCP tool: `list_open_dev_app_permissions` |-----|-----|------| | `--keyword` | `keyword` | 关键词搜索 | | `--status` | `authStatus` | `ALL/AUTHED/UNAUTHED` | -| `--scope-type` | `firstLevelType` | `APP/SNS`,空返回两者 | +| `--scope-type` | `scopeType` | `APP/SNS`,空返回两者 | | `--scope` | `scopeValue` | 单权限详情模式 | -| `--limit` | `limit` | 每页数量,默认 20,建议不超过 50 | -| `--offset` | `offset` | 翻页偏移量,默认 0 | +| `--page-size` | `pageSize` | 每页数量,默认 20,建议不超过 50 | +| `--cursor` | `cursor` | 游标,首次为空,用上一页 `nextCursor` 继续 | 权限状态判断: @@ -148,7 +143,7 @@ MCP tool: `list_open_dev_app_permissions` - `status=3` 停止申请,展示 `applyDisabledReason/displayMessage`。 - `authedStatusDesc` 细分展示:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED` 表示已开通、已申请或待发布;`NOT_OPEN`/`NOT_APPLIED` 表示未开通/未申请;`AUDIT_PROCESSING` 表示审批中;`AUDIT_REFUSE` 表示审批未通过。能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 -翻页:`--limit 50 --offset 0`(第1页)、`--offset 50`(第2页)、`--offset 100`(第3页),返回条数 < limit 表示末尾。 +翻页:首次传 `--page-size 50`,后续传 `--cursor NEXT_CURSOR`。无 `nextCursor` 或返回条数小于 `pageSize` 表示末尾。 ### 申请权限 diff --git a/skills/multi/dingtalk-devapp/SKILL.md b/skills/multi/dingtalk-devapp/SKILL.md index e53e513c..478caf07 100644 --- a/skills/multi/dingtalk-devapp/SKILL.md +++ b/skills/multi/dingtalk-devapp/SKILL.md @@ -1,6 +1,6 @@ --- name: dingtalk-devapp -description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/事件订阅/订阅事件/退订事件/eventCode。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 +description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/unifiedAppId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/事件订阅/订阅事件/退订事件/eventCode。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 cli_version: ">=1.0.15" metadata: category: product @@ -25,7 +25,7 @@ metadata: `应用` 是泛词。只有出现以下信号才路由到 devapp: - `开放平台应用/开发者后台应用/企业内部应用/内部应用` -- `agentId/clientId/appKey/appSecret/customKey` +- `unifiedAppId/clientId/appKey/appSecret` - `应用权限/权限点/scopeValue/应用成员/安全配置/IP 白名单` - `创建机器人/智能体机器人/机器人配置/机器人回调地址`(→ `robot`) - `应用版本/版本发布/发布审核/选审批人`(→ `version`) @@ -37,7 +37,7 @@ metadata: 1. 所有命令加 `--format json`。 2. 写操作先 `--dry-run`,确认后才加 `--yes`。 -3. 应用名/appKey/customKey 命中多条时展示候选,不取第一条。 +3. 应用名/appKey 命中多条时展示候选,不取第一条。 4. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名。 5. 主动读取密钥走 `credentials get`;任何 `devapp get`/详情返回里的 `clientSecret/appSecret` 都按敏感凭证脱敏,不向用户展开。 diff --git a/skills/multi/dingtalk-devapp/references/app-crud.md b/skills/multi/dingtalk-devapp/references/app-crud.md index 7898e729..0d773886 100644 --- a/skills/multi/dingtalk-devapp/references/app-crud.md +++ b/skills/multi/dingtalk-devapp/references/app-crud.md @@ -9,31 +9,26 @@ | 优先级 | 标识 | 处理 | |--------|------|------| | 1 | `--unified-app-id` | 直接使用 | -| 2 | `--agent-id` / `--app-id` | 直接使用 | -| 3 | `--app-key` / `--custom-key` | 先查询,唯一命中才继续 | -| 4 | `--name` | 模糊搜索,写操作必须唯一命中 | +| 2 | `--app-key` | appKey/clientId,唯一命中才继续 | +| 3 | `--name` | 应用名称关键词,写操作必须唯一命中 | ## 应用列表 ```bash dws devapp list --format json dws devapp list --name DemoApp --format json -dws devapp list --agent-id 123456 --format json -dws devapp list --creator 张三 --sort gmt_modified --order desc --format json +dws devapp list --app-key dingxxx --format json +dws devapp list --name DemoApp --page-size 20 --cursor NEXT_CURSOR --format json ``` -MCP tool: `list_open_dev_apps_by_condition` +MCP tool: `list_open_dev_app` | CLI | MCP | 说明 | |-----|-----|------| -| `--page` | `currentPage` | 1-based,默认 1 | | `--page-size` | `pageSize` | 默认 20 | -| `--name` / `--keyword` | `appName` | 应用名搜索 | -| `--agent-id` | `agentId` | 精确定位 | +| `--cursor` | `cursor` | 游标,首次为空,用上一页 `nextCursor` 继续 | +| `--name` / `--keyword` | `name` | 应用名搜索 | | `--app-key` | `appKey` | appKey/clientId | -| `--creator` | `creator` | 创建人关键词 | -| `--sort` | `sortType` | 如 `gmt_modified` | -| `--order` | `sortOrder` | `asc` / `desc` | ## 应用状态字段 @@ -52,10 +47,11 @@ MCP tool: `list_open_dev_apps_by_condition` ```bash dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --agent-id 123456 --format json +dws devapp get --app-key dingxxx --format json +dws devapp get --name DemoApp --format json ``` -MCP tool: `get_open_dev_app_detail` +MCP tool: `get_dev_app` 详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 @@ -66,7 +62,7 @@ dws devapp create --name DemoApp --desc "内部应用" --type internal --dry-run dws devapp create --name DemoApp --desc "内部应用" --type internal --yes --format json ``` -MCP tool: `create_inner_app` +MCP tool: `create_dev_app` | CLI | MCP | 必填 | |-----|-----|------| @@ -83,7 +79,7 @@ dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-r dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --yes --format json ``` -MCP tool: `update_inner_app` +MCP tool: `update_dev_app` 至少提供一个更新字段:`--name` / `--desc` / `--icon`。 @@ -97,7 +93,7 @@ dws devapp active --unified-app-id ID --dry-run --format json dws devapp active --unified-app-id ID --yes --format json ``` -MCP tools: `inactive_inner_app` / `active_inner_app` +MCP tools: `disable_dev_app` / `enable_dev_app` 停用保留数据但应用不可用,可通过 `active` 恢复。 @@ -110,7 +106,7 @@ dws devapp delete --unified-app-id ID --dry-run --format json dws devapp delete --unified-app-id ID --yes --format json ``` -MCP tool: `delete_inner_app` +MCP tool: `delete_dev_app` 删除前必须展示应用摘要。删除为异步操作,成功后应用延迟从列表消失。 diff --git a/skills/multi/dingtalk-devapp/references/credentials-webapp.md b/skills/multi/dingtalk-devapp/references/credentials-webapp.md index 0f1dec02..9ce8010f 100644 --- a/skills/multi/dingtalk-devapp/references/credentials-webapp.md +++ b/skills/multi/dingtalk-devapp/references/credentials-webapp.md @@ -4,7 +4,8 @@ ```bash dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --agent-id 123456 --format json +dws devapp credentials get --app-key dingxxx --format json +dws devapp credentials get --name DemoApp --format json ``` MCP tool: `get_open_dev_app_credentials` @@ -28,19 +29,19 @@ MCP tool: `get_open_dev_app_credentials` ## 网页应用查询 ```bash -dws devapp webapp get --agent-id 123456 --format json dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json +dws devapp webapp get --app-key dingxxx --format json ``` MCP tool: `get_webapp_config` -未配置网页应用前可能只返回 `agentId`。 +未配置网页应用前可能只返回空配置。 ## 网页应用配置 ```bash -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --dry-run --format json -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --pc-homepage-link https://example.com/pc --yes --format json +dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --dry-run --format json +dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --pc-homepage-url https://example.com/pc --yes --format json ``` MCP tool: `set_webapp_config` @@ -48,8 +49,8 @@ MCP tool: `set_webapp_config` | CLI | MCP | 说明 | |-----|-----|------| | `--h5-page-type` | `h5PageType` | 网页应用生效端 | -| `--homepage-link` | `homepageLink` | 移动端首页地址 | -| `--pc-homepage-link` | `pcHomepageLink` | PC 端首页地址 | -| `--omp-link` | `ompLink` | 管理后台地址 | +| `--homepage-url` | `homepageUrl` | 移动端首页地址 | +| `--pc-homepage-url` | `pcHomepageUrl` | PC 端首页地址 | +| `--omp-url` | `ompUrl` | 管理后台地址 | 至少提供一个配置字段。`h5PageType` 未显式传入时,不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 diff --git a/skills/multi/dingtalk-devapp/references/permissions.md b/skills/multi/dingtalk-devapp/references/permissions.md index 17cc6d95..3b5a310c 100644 --- a/skills/multi/dingtalk-devapp/references/permissions.md +++ b/skills/multi/dingtalk-devapp/references/permissions.md @@ -16,13 +16,12 @@ MCP tool: `list_open_dev_app_permissions` | CLI | MCP | 说明 | |-----|-----|------| | `--unified-app-id` | `unifiedAppId` | 应用定位 | -| `--agent-id` | `agentId` | 应用定位 | | `--keyword` | `keyword` | 权限名/API 名关键词 | | `--status` | `authStatus` | `ALL` / `AUTHED` / `UNAUTHED` | -| `--scope-type` | `firstLevelType` | `APP` / `SNS`,为空返回两者 | +| `--scope-type` | `scopeType` | `APP` / `SNS`,为空返回两者 | | `--scope` | `scopeValue` | 单权限详情模式 | -| `--limit` | `limit` | 每页返回数量,默认 20,建议不超过 50 | -| `--offset` | `offset` | 翻页偏移量,默认 0 | +| `--page-size` | `pageSize` | 每页返回数量,默认 20,建议不超过 50 | +| `--cursor` | `cursor` | 游标,首次为空,用上一页 `nextCursor` 继续 | **状态判断:** @@ -47,15 +46,14 @@ MCP tool: `list_open_dev_app_permissions` **翻页:** -权限列表支持 `--limit` + `--offset` 分页。一个应用可能有 150+ 个权限点,一次查不完时用 offset 翻页: +权限列表支持 `--page-size` + `--cursor` 分页。一个应用可能有 150+ 个权限点,一次查不完时用返回的 `nextCursor` 继续: ```bash -dws devapp permission list --unified-app-id ID --limit 50 --format json # 第 1 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 50 --format json # 第 2 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 100 --format json # 第 3 页 +dws devapp permission list --unified-app-id ID --page-size 50 --format json +dws devapp permission list --unified-app-id ID --page-size 50 --cursor NEXT_CURSOR --format json ``` -当返回条数 < limit 时表示已到末尾。 +当返回无 `nextCursor` 或返回条数小于 `pageSize` 时表示已到末尾。 **规则:** - `permission search` 和 `permission detail` 是 `list` 的 CLI 别名,不是独立 MCP tool。 diff --git a/skills/multi/dingtalk-devapp/references/workflows.md b/skills/multi/dingtalk-devapp/references/workflows.md index bed457c6..b07be648 100644 --- a/skills/multi/dingtalk-devapp/references/workflows.md +++ b/skills/multi/dingtalk-devapp/references/workflows.md @@ -8,7 +8,7 @@ 1. 创建应用 dws devapp create --name "应用名" --desc "描述" --type internal --dry-run --format json → 确认后加 --yes - → 记录返回的 unifiedAppId 和 agentId + → 记录返回的 unifiedAppId 和 appKey 2. 确认创建成功 dws devapp get --unified-app-id --format json @@ -17,12 +17,12 @@ dws devapp credentials get --unified-app-id --format json 4. 配置网页应用(按需) - dws devapp webapp config --agent-id --homepage-link --dry-run --format json + dws devapp webapp config --unified-app-id --homepage-url --dry-run --format json → 确认后加 --yes 5. 验证网页应用配置 - dws devapp webapp get --agent-id --format json - → 确认返回 homepageLink/pcHomepageLink 等 + dws devapp webapp get --unified-app-id --format json + → 确认返回 homepageUrl/pcHomepageUrl 等 6. 申请权限(按需) dws devapp permission list --unified-app-id --keyword "关键词" --format json From 8aaf0fb6e8f204c4ffd7c42e5fd650d801a0929b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Tue, 16 Jun 2026 08:03:47 +0800 Subject: [PATCH 07/47] fix: unwrap devapp service result responses --- internal/helpers/devapp.go | 17 ++++++++++++++ internal/helpers/devapp_test.go | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 1fb64fee..505949d6 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -1203,9 +1203,26 @@ func runDevAppTool(runner executor.Runner, cmd *cobra.Command, tool string, para if err != nil { return err } + result = normalizeDevAppServiceResult(result) return writeCommandPayload(cmd, result) } +func normalizeDevAppServiceResult(result executor.Result) executor.Result { + content, ok := result.Response["content"].(map[string]any) + if !ok { + return result + } + if success, ok := content["success"].(bool); !ok || !success { + return result + } + value, ok := content["result"] + if !ok || value == nil { + return result + } + result.Response["content"] = value + return result +} + func requiredDevAppID(cmd *cobra.Command) (string, error) { appID, _ := cmd.Flags().GetString("app-id") appID = strings.TrimSpace(appID) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index af49d182..33e110da 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -3,6 +3,7 @@ package helpers import ( "bytes" "context" + "encoding/json" "reflect" "strings" "testing" @@ -779,6 +780,44 @@ func TestDevAppCredentialsGetKeepsSecretFields(t *testing.T) { } } +func TestDevAppUnwrapsSuccessfulServiceResult(t *testing.T) { + runner := &devAppResponseRunner{ + response: map[string]any{ + "content": map[string]any{ + "success": true, + "errorCode": nil, + "errorMsg": nil, + "result": map[string]any{ + "items": []any{ + map[string]any{"versionId": "v-1"}, + }, + "hasMore": false, + }, + }, + }, + } + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"devapp", "version", "list", "--unified-app-id", "u-1"}) + + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + var rendered map[string]any + if err := json.Unmarshal(out.Bytes(), &rendered); err != nil { + t.Fatalf("json.Unmarshal() error = %v\noutput:\n%s", err, out.String()) + } + if _, ok := rendered["success"]; ok { + t.Fatalf("output kept ServiceResult wrapper: %#v", rendered) + } + items, ok := rendered["items"].([]any) + if !ok || len(items) != 1 { + t.Fatalf("items = %#v, want one item", rendered["items"]) + } +} + func TestDevAppMemberCommandsValidateRequiredFlags(t *testing.T) { cases := []struct { name string From 4a19530fd0eefb710dab7d86ac1b6bad68aeda46 Mon Sep 17 00:00:00 2001 From: quanming Date: Tue, 16 Jun 2026 09:57:11 +0800 Subject: [PATCH 08/47] fix: align devapp MCP tool names --- internal/helpers/devapp.go | 12 ++++++------ internal/helpers/devapp_test.go | 18 +++++++++--------- .../references/member-security.md | 12 ++++++------ skills/mono/references/products/devapp.md | 4 ++-- .../references/member-security.md | 12 ++++++------ 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 505949d6..14bd9be5 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -37,10 +37,10 @@ const ( devAppEnableTool = "enable_dev_app" devAppCredentialsGet = "get_open_dev_app_credentials" - devAppMemberListTool = "list_open_dev_app_members" - devAppMemberAddTool = "add_open_dev_app_members" - devAppMemberRemoveTool = "remove_open_dev_app_members" - devAppSecurityConfigTool = "update_app_security_config" + devAppMemberListTool = "list_dev_app_members" + devAppMemberAddTool = "add_dev_app_members" + devAppMemberRemoveTool = "remove_dev_app_members" + devAppSecurityConfigTool = "update_dev_app_security_config" // 机器人能力(op-app MCP 工具,硬编码不走服务发现)。 devAppRobotCreateTool = "create_dingtalk_robot" @@ -652,13 +652,13 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { params := map[string]any{"unifiedAppId": appID} if values := parseDevAppListFlag(cmd, "ip-whitelist"); len(values) > 0 { - params["ipWhiteList"] = values + params["ipWhitelist"] = values } if values := parseDevAppListFlag(cmd, "redirect-url"); len(values) > 0 { params["redirectUrls"] = values } if values := parseDevAppListFlag(cmd, "sso-url"); len(values) > 0 { - params["otherAuthUrls"] = values + params["ssoUrls"] = values } if len(params) == 1 { return apperrors.NewValidation("one of --ip-whitelist, --redirect-url, or --sso-url is required") diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 33e110da..30a993ae 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -46,7 +46,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { name: "list", cmd: "list", args: []string{"--app-id", "app-001"}, - wantTool: "list_open_dev_app_members", + wantTool: "list_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", }, @@ -55,7 +55,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { name: "add multiple users", cmd: "add", args: []string{"--app-id", "app-001", "--users", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, - wantTool: "add_open_dev_app_members", + wantTool: "add_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", "memberUserIds": []string{"userId1", "userId2", "userId3", "userId4"}, @@ -66,7 +66,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { name: "remove trims users", cmd: "remove", args: []string{"--app-id", "app-001", "--users", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, - wantTool: "remove_open_dev_app_members", + wantTool: "remove_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", "memberUserIds": []string{"userId1", "userId2"}, @@ -876,14 +876,14 @@ func TestDevAppSecurityConfigBuildsOnlyProvidedLists(t *testing.T) { if got := runner.last.CanonicalProduct; got != "devapp" { t.Fatalf("CanonicalProduct = %q, want devapp", got) } - if got := runner.last.Tool; got != "update_app_security_config" { - t.Fatalf("Tool = %q, want update_app_security_config", got) + if got := runner.last.Tool; got != "update_dev_app_security_config" { + t.Fatalf("Tool = %q, want update_dev_app_security_config", got) } want := map[string]any{ - "unifiedAppId": "app-001", - "ipWhiteList": []string{"192.0.2.10", "192.0.2.11"}, - "redirectUrls": []string{"https://callback.example.invalid/callback"}, - "otherAuthUrls": []string{"https://sso.example.invalid/sso"}, + "unifiedAppId": "app-001", + "ipWhitelist": []string{"192.0.2.10", "192.0.2.11"}, + "redirectUrls": []string{"https://callback.example.invalid/callback"}, + "ssoUrls": []string{"https://sso.example.invalid/sso"}, } if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) diff --git a/skills/dingtalk-devapp/references/member-security.md b/skills/dingtalk-devapp/references/member-security.md index 8c2b716c..088520bd 100644 --- a/skills/dingtalk-devapp/references/member-security.md +++ b/skills/dingtalk-devapp/references/member-security.md @@ -6,7 +6,7 @@ dws devapp member list --app-id UNIFIED_APP_ID --format json ``` -MCP tool: `list_open_dev_app_members` +MCP tool: `list_dev_app_members` ## 添加成员 @@ -15,7 +15,7 @@ dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-t dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json ``` -MCP tool: `add_open_dev_app_members` +MCP tool: `add_dev_app_members` ## 移除成员 @@ -24,7 +24,7 @@ dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type D dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json ``` -MCP tool: `remove_open_dev_app_members` +MCP tool: `remove_dev_app_members` | CLI | MCP | 说明 | |-----|-----|------| @@ -39,13 +39,13 @@ dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --d dws devapp security config --app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json ``` -MCP tool: `update_app_security_config` +MCP tool: `update_dev_app_security_config` | CLI | MCP | 说明 | |-----|-----|------| | `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | -| `--ip-whitelist` | `ipWhiteList` | 出口 IP 白名单,逗号/分号分隔 | +| `--ip-whitelist` | `ipWhitelist` | 出口 IP 白名单,逗号/分号分隔 | | `--redirect-url` | `redirectUrls` | 登录重定向 URL,逗号/分号分隔 | -| `--sso-url` | `otherAuthUrls` | 端内免登 URL,逗号/分号分隔 | +| `--sso-url` | `ssoUrls` | 端内免登 URL,逗号/分号分隔 | 至少提供一个配置字段。只下发显式提供的字段,未提供的不覆盖。 diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 6f512755..683813e2 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -173,7 +173,7 @@ dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-t dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json ``` -MCP tools: `list_open_dev_app_members` / `add_open_dev_app_members` / `remove_open_dev_app_members` +MCP tools: `list_dev_app_members` / `add_dev_app_members` / `remove_dev_app_members` ### 安全配置 @@ -181,7 +181,7 @@ MCP tools: `list_open_dev_app_members` / `add_open_dev_app_members` / `remove_op dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --redirect-url https://callback.example.invalid/callback --dry-run --format json ``` -MCP tool: `update_app_security_config` +MCP tool: `update_dev_app_security_config` --- diff --git a/skills/multi/dingtalk-devapp/references/member-security.md b/skills/multi/dingtalk-devapp/references/member-security.md index 8c2b716c..088520bd 100644 --- a/skills/multi/dingtalk-devapp/references/member-security.md +++ b/skills/multi/dingtalk-devapp/references/member-security.md @@ -6,7 +6,7 @@ dws devapp member list --app-id UNIFIED_APP_ID --format json ``` -MCP tool: `list_open_dev_app_members` +MCP tool: `list_dev_app_members` ## 添加成员 @@ -15,7 +15,7 @@ dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-t dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json ``` -MCP tool: `add_open_dev_app_members` +MCP tool: `add_dev_app_members` ## 移除成员 @@ -24,7 +24,7 @@ dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type D dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json ``` -MCP tool: `remove_open_dev_app_members` +MCP tool: `remove_dev_app_members` | CLI | MCP | 说明 | |-----|-----|------| @@ -39,13 +39,13 @@ dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --d dws devapp security config --app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json ``` -MCP tool: `update_app_security_config` +MCP tool: `update_dev_app_security_config` | CLI | MCP | 说明 | |-----|-----|------| | `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | -| `--ip-whitelist` | `ipWhiteList` | 出口 IP 白名单,逗号/分号分隔 | +| `--ip-whitelist` | `ipWhitelist` | 出口 IP 白名单,逗号/分号分隔 | | `--redirect-url` | `redirectUrls` | 登录重定向 URL,逗号/分号分隔 | -| `--sso-url` | `otherAuthUrls` | 端内免登 URL,逗号/分号分隔 | +| `--sso-url` | `ssoUrls` | 端内免登 URL,逗号/分号分隔 | 至少提供一个配置字段。只下发显式提供的字段,未提供的不覆盖。 From c9ba0373c2bf1e0bbbb9a40c892e7281f34ed7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Tue, 16 Jun 2026 10:10:43 +0800 Subject: [PATCH 09/47] fix: align devapp cli tool mappings --- docs/devapp-yulan-command-routing.md | 60 +++++++++---------- internal/helpers/devapp.go | 14 ++--- internal/helpers/devapp_connect.go | 8 +-- internal/helpers/devapp_robot_connect_test.go | 6 +- internal/helpers/devapp_test.go | 18 +++--- skills/dingtalk-devapp/references/app-crud.md | 2 +- .../references/credentials-webapp.md | 6 +- .../dingtalk-devapp/references/permissions.md | 6 +- skills/dingtalk-devapp/references/robot.md | 2 +- skills/mono/references/products/devapp.md | 14 ++--- .../dingtalk-devapp/references/app-crud.md | 2 +- .../references/credentials-webapp.md | 6 +- .../dingtalk-devapp/references/permissions.md | 6 +- .../multi/dingtalk-devapp/references/robot.md | 2 +- 14 files changed, 76 insertions(+), 76 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 6f0a2f69..8c9f3177 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -128,16 +128,16 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Area | CLI command | MCP tool | Backend HSF facade | Current backend status | Open-source DWS status | | --- | --- | --- | --- | --- | --- | -| App query | `devapp list` | `list_open_dev_app` | `OpenInnerAppQueryFacade.listByCondition` | Implemented | Hardcoded helper | +| App query | `devapp list` | `list_dev_app` | `OpenInnerAppQueryFacade.listByCondition` | Implemented | Hardcoded helper | | App detail | `devapp get` | `get_dev_app` | `OpenInnerAppQueryFacade.getDetail` | Implemented | Hardcoded helper | | App create | `devapp create` | `create_dev_app` | `OpenInnerAppManageFacade.create` | Implemented | Hardcoded helper | | App update | `devapp update` | `update_dev_app` | `OpenInnerAppManageFacade.update` | Implemented | Hardcoded helper | | App delete | `devapp delete` | `delete_dev_app` | `OpenInnerAppManageFacade.delete` | Implemented | Hardcoded helper | -| Credentials | `devapp credentials get` | `get_open_dev_app_credentials` | `OpenInnerAppQueryFacade.getCredentials` | Implemented | Hardcoded helper | -| Web app | `devapp webapp get/config` | `get_webapp_config`/`set_webapp_config` | Webapp facade | Implemented | Hardcoded helper; locator fields are `unifiedAppId/appKey`; config fields are `h5PageType/homepageUrl/pcHomepageUrl/ompUrl`. | -| Permission list | `devapp permission list/search/detail` | `list_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | -| Permission apply | `devapp permission add` | `apply_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | -| Permission remove | `devapp permission remove` | `remove_open_dev_app_permission` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | +| Credentials | `devapp credentials get` | `get_dev_app_credentials` | `OpenInnerAppQueryFacade.getCredentials` | Implemented | Hardcoded helper | +| Web app | `devapp webapp get/config` | `get_extension_webapp_config`/`set_extension_webapp_config` | Webapp facade | Implemented | Hardcoded helper; locator fields are `unifiedAppId/appKey`; config fields are `h5PageType/homepageUrl/pcHomepageUrl/ompUrl`. | +| Permission list | `devapp permission list/search/detail` | `list_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | +| Permission apply | `devapp permission add` | `apply_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | +| Permission remove | `devapp permission remove` | `remove_dev_app_permissions` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | | Events | `devapp event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | Implemented | Hardcoded helper | | Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | | Robot config | `devapp robot get/config/enable/offline` | `get/set_open_dev_app_robot_config`, `enable/offline_open_dev_app_robot` | Implemented | Hardcoded helper | @@ -189,7 +189,7 @@ Naming convention: ### 3.2 P0 Tool Contracts -#### list_open_dev_app +#### list_dev_app | Item | Contract | | --- | --- | @@ -342,7 +342,7 @@ The Agent must display the resolved app summary before executing delete with `--yes`. The response should include `deleted=true`, the app summary, and raw backend fields. -#### list_open_dev_app_permissions +#### list_dev_app_permissions | Item | Contract | | --- | --- | @@ -393,7 +393,7 @@ Normalized response should flatten `appScopes` and `snsScopes` into one } ``` -#### apply_open_dev_app_permissions +#### apply_dev_app_permissions | Item | Contract | | --- | --- | @@ -430,7 +430,7 @@ skipped scopes: `requiredApproval=true` scopes must be accepted when editable and staged into the current app version. Approver selection is handled by the version flow. -#### remove_open_dev_app_permission +#### remove_dev_app_permissions | Item | Contract | | --- | --- | @@ -484,7 +484,7 @@ Tool override examples: ```json { "toolOverrides": { - "list_open_dev_app": { + "list_dev_app": { "cliName": "list", "description": "查询开放平台企业内部应用列表", "flags": { @@ -535,7 +535,7 @@ Tool override examples: "name": {"alias": "name"} } }, - "list_open_dev_app_permissions": { + "list_dev_app_permissions": { "cliName": "list", "group": "permission", "description": "查询开放平台应用权限列表,默认同时返回 APP 应用权限和 SNS 个人权限", @@ -550,7 +550,7 @@ Tool override examples: "cursor": {"alias": "cursor"} } }, - "apply_open_dev_app_permissions": { + "apply_dev_app_permissions": { "cliName": "add", "group": "permission", "description": "申请开放平台应用权限点;需审核权限写入当前应用版本变更", @@ -565,7 +565,7 @@ Tool override examples: } } }, - "remove_open_dev_app_permission": { + "remove_dev_app_permissions": { "cliName": "remove", "group": "permission", "description": "取消一个已开通的开放平台应用权限点", @@ -583,7 +583,7 @@ Tool override examples: } ``` -`permission search/detail` are UX aliases over `list_open_dev_app_permissions`. The helper exposes one canonical MCP call and keeps alias behavior at the CLI layer: +`permission search/detail` are UX aliases over `list_dev_app_permissions`. The helper exposes one canonical MCP call and keeps alias behavior at the CLI layer: ```bash dws devapp permission list --keyword "机器人发送消息" @@ -594,8 +594,8 @@ If a helper command is added, map aliases without creating new MCP tools: | Helper command | Underlying tool | Forced/derived args | | --- | --- | --- | -| `devapp permission search` | `list_open_dev_app_permissions` | Requires `keyword` or `scopeValue`; returns candidate summaries. | -| `devapp permission detail` | `list_open_dev_app_permissions` | Requires `scopeValue`; returns one scope with full `apiList`. | +| `devapp permission search` | `list_dev_app_permissions` | Requires `keyword` or `scopeValue`; returns candidate summaries. | +| `devapp permission detail` | `list_dev_app_permissions` | Requires `scopeValue`; returns one scope with full `apiList`. | ## 5. App Identity Resolution @@ -737,7 +737,7 @@ dws devapp credentials get --name DemoApp --format json Current status: 1. Backend facade is `OpenInnerAppQueryFacade.getCredentials`. -2. MCP tool is `get_open_dev_app_credentials`. +2. MCP tool is `get_dev_app_credentials`. 3. CLI sends only application locator fields and does not add `showSecret`, `confirmSecret`, or masking fields. 4. The MCP response follows the backend credential contract and may include `clientSecret/appSecret`; callers must treat command output as sensitive. @@ -1079,16 +1079,16 @@ Intent table: | User intent | Command | Tool | | --- | --- | --- | -| "查应用/搜索应用" | `devapp list` | `list_open_dev_app` | +| "查应用/搜索应用" | `devapp list` | `list_dev_app` | | "看应用详情/clientId/appKey" | `devapp get` | `get_dev_app` | | "创建应用" | `devapp create` | `create_dev_app` | | "修改名称/描述/图标" | `devapp update` | `update_dev_app` | | "删除应用" | `devapp delete` | `delete_dev_app` | -| "查权限/搜索 API 权限" | `permission list` | `list_open_dev_app_permissions` | -| "这个权限覆盖哪些 API" | `permission list --scope` | `list_open_dev_app_permissions` | -| "申请权限/开通权限" | `permission add` | `apply_open_dev_app_permissions` | -| "取消权限/移除权限" | `permission remove` | `remove_open_dev_app_permission` | -| "拿 appSecret/clientSecret" | `credentials get` | `get_open_dev_app_credentials` | +| "查权限/搜索 API 权限" | `permission list` | `list_dev_app_permissions` | +| "这个权限覆盖哪些 API" | `permission list --scope` | `list_dev_app_permissions` | +| "申请权限/开通权限" | `permission add` | `apply_dev_app_permissions` | +| "取消权限/移除权限" | `permission remove` | `remove_dev_app_permissions` | +| "拿 appSecret/clientSecret" | `credentials get` | `get_dev_app_credentials` | | "查可订阅事件/订阅/退订事件" | `event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | | "发布/审核/选审批人" | `version ...` | `publish_open_dev_app_version` etc. | @@ -1354,14 +1354,14 @@ P0 debug inputs: | Tool | Baseline debug input | | --- | --- | -| `list_open_dev_app` | `{"pageSize":10}` | +| `list_dev_app` | `{"pageSize":10}` | | `get_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | | `create_dev_app` | `{"appName":"DemoApp","appDesc":"internal app"}` | | `update_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID","appDesc":"new desc"}` | | `delete_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | -| `list_open_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","authStatus":"ALL","pageSize":20}` | -| `apply_open_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValues":["SCOPE_VALUE"]}` | -| `remove_open_dev_app_permission` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValue":"SCOPE_VALUE"}` | +| `list_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","authStatus":"ALL","pageSize":20}` | +| `apply_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValues":["SCOPE_VALUE"]}` | +| `remove_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValue":"SCOPE_VALUE"}` | For destructive debug cases, use a disposable application and permission scope. Do not use production data in long-lived examples. @@ -1372,7 +1372,7 @@ After publish, verify the service through MCP before using DWS: 1. Run MCP `initialize`. 2. Run `tools/list` and confirm all expected P0 tool keys exist. -3. Call `list_open_dev_app` with only pagination. +3. Call `list_dev_app` with only pagination. 4. Call one exact locator scenario, such as `get_dev_app`. 5. Call permission list with `authStatus=ALL` and confirm APP and SNS scopes are both represented. @@ -1460,7 +1460,7 @@ the following are true: | `dws devapp permission list --unified-app-id X --keyword "发送消息" --format json` | Returns candidate `permissions[]`; no automatic apply. | | `dws devapp permission add --unified-app-id X --permissions Contact.User.mobile --yes --format json` | Applies or stages permission; `requiredApproval=true` is allowed. | | `dws devapp permission remove --unified-app-id X --permission qyapi_robot_sendmsg --yes --format json` | Removes one authorized scope or returns a structured reason. | -| `dws devapp credentials get --unified-app-id X --format json` | Calls `get_open_dev_app_credentials`; does not use `devapp get` as a secret fallback. | +| `dws devapp credentials get --unified-app-id X --format json` | Calls `get_dev_app_credentials`; does not use `devapp get` as a secret fallback. | | `查应用 appXYZ 的详情` without OpenDev context | Does not blindly route to `devapp`; uses context such as `workbench app get` or asks. | | `查询开放平台 API 错误码` | Routes to `devdoc`, not `devapp`. | | `创建 MCP 服务并配置 HSF tool` | Uses OpenDev MCP platform workflow, not `dws devapp create`. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 505949d6..0766e6cf 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -28,14 +28,14 @@ import ( const ( devAppProduct = "devapp" - devAppListTool = "list_open_dev_app" + devAppListTool = "list_dev_app" devAppGetTool = "get_dev_app" devAppCreateTool = "create_dev_app" devAppUpdateTool = "update_dev_app" devAppDeleteTool = "delete_dev_app" devAppDisableTool = "disable_dev_app" devAppEnableTool = "enable_dev_app" - devAppCredentialsGet = "get_open_dev_app_credentials" + devAppCredentialsGet = "get_dev_app_credentials" devAppMemberListTool = "list_open_dev_app_members" devAppMemberAddTool = "add_open_dev_app_members" @@ -417,7 +417,7 @@ func newDevAppWebappGetCommand(runner executor.Runner) *cobra.Command { if len(params) == 0 { return devAppMcpLocatorRequired(false) } - return runDevAppTool(runner, cmd, "get_webapp_config", params) + return runDevAppTool(runner, cmd, "get_extension_webapp_config", params) }, } addDevAppMcpLocatorFlags(cmd, false) @@ -460,7 +460,7 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { if updates == 0 { return apperrors.NewValidation("at least one webapp field is required: --h5-page-type, --homepage-url, --pc-homepage-url, or --omp-url") } - return runDevAppTool(runner, cmd, "set_webapp_config", params) + return runDevAppTool(runner, cmd, "set_extension_webapp_config", params) }, } addDevAppMcpLocatorFlags(cmd, true) @@ -498,7 +498,7 @@ func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { devAppPutString(params, "apiStatus", devAppStringFlag(cmd, "api-status")) devAppPutString(params, "cursor", devAppStringFlag(cmd, "cursor")) devAppPutInt(params, "pageSize", devAppIntFlag(cmd, "page-size")) - return runDevAppTool(runner, cmd, "list_open_dev_app_permissions", params) + return runDevAppTool(runner, cmd, "list_dev_app_permissions", params) }, } addDevAppPermissionLocatorFlags(cmd) @@ -535,7 +535,7 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { return apperrors.NewValidation("--permissions is required") } params["scopeValues"] = scopes - return runDevAppTool(runner, cmd, "apply_open_dev_app_permissions", params) + return runDevAppTool(runner, cmd, "apply_dev_app_permissions", params) }, } addDevAppPermissionLocatorFlags(cmd) @@ -568,7 +568,7 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { return apperrors.NewValidation("--permission is required") } params["scopeValue"] = scope - return runDevAppTool(runner, cmd, "remove_open_dev_app_permission", params) + return runDevAppTool(runner, cmd, "remove_dev_app_permissions", params) }, } addDevAppPermissionLocatorFlags(cmd) diff --git a/internal/helpers/devapp_connect.go b/internal/helpers/devapp_connect.go index 6365694a..c5ade78b 100644 --- a/internal/helpers/devapp_connect.go +++ b/internal/helpers/devapp_connect.go @@ -266,7 +266,7 @@ func officialChannelGuidance(channel string) string { // newDevAppRobotConnectCommand implements `dws devapp robot connect`: the linking // half of provisioning. It takes an existing robot's credentials — either // directly via --client-id/--client-secret, or by --unified-app-id (reusing -// devapp's `get_open_dev_app_credentials` to fetch them) — and starts the +// devapp's `get_dev_app_credentials` to fetch them) — and starts the // channel-aware Stream connector in the foreground. It never provisions a robot; // for 建号 run `dws devapp robot create` (or submit/result) first. func newDevAppRobotConnectCommand(runner executor.Runner) *cobra.Command { @@ -459,14 +459,14 @@ func connectAgentOptionsPayload(channel string, opts connectAgentOptions) map[st return map[string]any{"model": model, "workdir": workDir, "memory": memory, "replyStyle": replyStyle} } -// devAppFetchCredentials reuses devapp's get_open_dev_app_credentials tool to +// devAppFetchCredentials reuses devapp's get_dev_app_credentials tool to // resolve a unified app's clientId/clientSecret, so `robot connect // --unified-app-id` need not have the caller paste raw credentials. Note the // open platform only returns clientSecret once (at provisioning); if the tool // omits it the caller is told to fall back to explicit flags. // // TODO(verify): the clientId/clientSecret (and appKey/appSecret fallback) field -// names below are NOT yet confirmed against the real get_open_dev_app_credentials +// names below are NOT yet confirmed against the real get_dev_app_credentials // response — no fixture exists in-repo. Verify against the pre-prod gateway and // pin the exact field names before relying on --unified-app-id auto-fetch. The // path degrades safely today: an unrecognised shape yields empty strings and the @@ -475,7 +475,7 @@ func devAppFetchCredentials(runner executor.Runner, cmd *cobra.Command, unifiedA invocation := executor.NewHelperInvocation( cobracmd.LegacyCommandPath(cmd), devAppProduct, - "get_open_dev_app_credentials", + devAppCredentialsGet, map[string]any{"unifiedAppId": unifiedAppID}, ) res, err := runner.Run(cmd.Context(), invocation) diff --git a/internal/helpers/devapp_robot_connect_test.go b/internal/helpers/devapp_robot_connect_test.go index 90b2b752..d4f30497 100644 --- a/internal/helpers/devapp_robot_connect_test.go +++ b/internal/helpers/devapp_robot_connect_test.go @@ -94,7 +94,7 @@ func TestDevAppRobotConnectValidation(t *testing.T) { } // TestDevAppFetchCredentials checks that --unified-app-id credential resolution -// reuses get_open_dev_app_credentials and extracts clientId/clientSecret from the +// reuses get_dev_app_credentials and extracts clientId/clientSecret from the // MCP envelope. func TestDevAppFetchCredentials(t *testing.T) { runner := &devAppResponseRunner{response: map[string]any{ @@ -119,8 +119,8 @@ func TestDevAppFetchCredentials(t *testing.T) { if id != "ck-123" || secret != "cs-456" { t.Fatalf("got id=%q secret=%q, want ck-123/cs-456", id, secret) } - if runner.last.Tool != "get_open_dev_app_credentials" { - t.Fatalf("Tool = %q, want get_open_dev_app_credentials", runner.last.Tool) + if runner.last.Tool != "get_dev_app_credentials" { + t.Fatalf("Tool = %q, want get_dev_app_credentials", runner.last.Tool) } if runner.last.CanonicalProduct != devAppProduct { t.Fatalf("CanonicalProduct = %q, want %q", runner.last.CanonicalProduct, devAppProduct) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 33e110da..25b75b5b 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -490,8 +490,8 @@ func TestDevAppListBuildsListByConditionParams(t *testing.T) { if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "list_open_dev_app" { - t.Fatalf("Tool = %q, want list_open_dev_app", got) + if got := runner.last.Tool; got != "list_dev_app" { + t.Fatalf("Tool = %q, want list_dev_app", got) } want := map[string]any{ "pageSize": 5, @@ -634,13 +634,13 @@ func TestDevAppWebappCommandsBuildParams(t *testing.T) { { name: "get", args: []string{"webapp", "get", "--unified-app-id", "u-1"}, - wantTool: "get_webapp_config", + wantTool: "get_extension_webapp_config", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { name: "config", args: []string{"webapp", "config", "--unified-app-id", "u-1", "--homepage-url", "https://example.com", "--pc-homepage-url", "https://pc.example.com", "--yes"}, - wantTool: "set_webapp_config", + wantTool: "set_extension_webapp_config", wantParams: map[string]any{ "unifiedAppId": "u-1", "homepageUrl": "https://example.com", @@ -681,7 +681,7 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { { name: "list", args: []string{"permission", "list", "--unified-app-id", "u-1", "--keyword", "手机号", "--status", "all", "--page-size", "5", "--cursor", "next-1"}, - wantTool: "list_open_dev_app_permissions", + wantTool: "list_dev_app_permissions", wantParams: map[string]any{ "unifiedAppId": "u-1", "keyword": "手机号", @@ -693,7 +693,7 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { { name: "add", args: []string{"permission", "add", "--unified-app-id", "u-1", "--permissions", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, - wantTool: "apply_open_dev_app_permissions", + wantTool: "apply_dev_app_permissions", wantParams: map[string]any{ "unifiedAppId": "u-1", "scopeValues": []string{"Contact.User.mobile", "qyapi_robot_sendmsg"}, @@ -702,7 +702,7 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { { name: "remove", args: []string{"permission", "remove", "--unified-app-id", "u-1", "--permission", "Contact.User.mobile", "--yes"}, - wantTool: "remove_open_dev_app_permission", + wantTool: "remove_dev_app_permissions", wantParams: map[string]any{ "unifiedAppId": "u-1", "scopeValue": "Contact.User.mobile", @@ -743,8 +743,8 @@ func TestDevAppCredentialsGetBuildsParams(t *testing.T) { if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "get_open_dev_app_credentials" { - t.Fatalf("Tool = %q, want get_open_dev_app_credentials", got) + if got := runner.last.Tool; got != "get_dev_app_credentials" { + t.Fatalf("Tool = %q, want get_dev_app_credentials", got) } want := map[string]any{"unifiedAppId": "u-1"} if !reflect.DeepEqual(runner.last.Params, want) { diff --git a/skills/dingtalk-devapp/references/app-crud.md b/skills/dingtalk-devapp/references/app-crud.md index 0d773886..dffc302b 100644 --- a/skills/dingtalk-devapp/references/app-crud.md +++ b/skills/dingtalk-devapp/references/app-crud.md @@ -21,7 +21,7 @@ dws devapp list --app-key dingxxx --format json dws devapp list --name DemoApp --page-size 20 --cursor NEXT_CURSOR --format json ``` -MCP tool: `list_open_dev_app` +MCP tool: `list_dev_app` | CLI | MCP | 说明 | |-----|-----|------| diff --git a/skills/dingtalk-devapp/references/credentials-webapp.md b/skills/dingtalk-devapp/references/credentials-webapp.md index 9ce8010f..864a7566 100644 --- a/skills/dingtalk-devapp/references/credentials-webapp.md +++ b/skills/dingtalk-devapp/references/credentials-webapp.md @@ -8,7 +8,7 @@ dws devapp credentials get --app-key dingxxx --format json dws devapp credentials get --name DemoApp --format json ``` -MCP tool: `get_open_dev_app_credentials` +MCP tool: `get_dev_app_credentials` 后端 facade: `OpenInnerAppQueryFacade.getCredentials`。 @@ -33,7 +33,7 @@ dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json dws devapp webapp get --app-key dingxxx --format json ``` -MCP tool: `get_webapp_config` +MCP tool: `get_extension_webapp_config` 未配置网页应用前可能只返回空配置。 @@ -44,7 +44,7 @@ dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https:// dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --pc-homepage-url https://example.com/pc --yes --format json ``` -MCP tool: `set_webapp_config` +MCP tool: `set_extension_webapp_config` | CLI | MCP | 说明 | |-----|-----|------| diff --git a/skills/dingtalk-devapp/references/permissions.md b/skills/dingtalk-devapp/references/permissions.md index 3b5a310c..79ff7b38 100644 --- a/skills/dingtalk-devapp/references/permissions.md +++ b/skills/dingtalk-devapp/references/permissions.md @@ -11,7 +11,7 @@ dws devapp permission list --unified-app-id ID --scope-type SNS --format json dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json ``` -MCP tool: `list_open_dev_app_permissions` +MCP tool: `list_dev_app_permissions` | CLI | MCP | 说明 | |-----|-----|------| @@ -74,7 +74,7 @@ dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg dws devapp permission add --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --yes --format json ``` -MCP tool: `apply_open_dev_app_permissions` +MCP tool: `apply_dev_app_permissions` **规则:** - `--permissions` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 @@ -89,6 +89,6 @@ dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendms dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --yes --format json ``` -MCP tool: `remove_open_dev_app_permission` +MCP tool: `remove_dev_app_permissions` 一次只取消一个权限点。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md index 834d9d98..6b95a22f 100644 --- a/skills/dingtalk-devapp/references/robot.md +++ b/skills/dingtalk-devapp/references/robot.md @@ -148,7 +148,7 @@ Codex 渠道注意: |------|------| | `--channel` | `auto`(默认,运行时信号自动识别) / openclaw / qoder / qoderwork / hermes / workbuddy / claudecode / codebuddy / codex / gemini / opencode | | `--robot-client-id` / `--robot-client-secret` | 现成机器人凭证(clientId=AppKey, clientSecret=AppSecret)。命名带 `robot-` 前缀以避开全局 OAuth `--client-id` flag | -| `--unified-app-id` | 统一应用 ID,内部调 `get_open_dev_app_credentials` 自动取凭证。⚠️ 字段名待预发真机验证;clientSecret 仅建号时返回一次、未必可取,必要时回退手填 | +| `--unified-app-id` | 统一应用 ID,内部调 `get_dev_app_credentials` 自动取凭证。⚠️ 字段名待预发真机验证;clientSecret 仅建号时返回一次、未必可取,必要时回退手填 | | `--agent-memory` | 按会话续聊(默认开):同一群/单聊共享 agent 会话,追问保留上下文。仅 claudecode/codebuddy/workbuddy(CLI 有 `--session-id`/`--resume`);qoder 系/codex/gemini/opencode 无寻址会话,自动保持无状态。`--agent-memory=false` 关闭 | | `--agent-model` | 覆盖本地 agent 模型(如 claudecode 默认锁 haiku 求快,可改 `claude-sonnet-4-6` 换聪明)。env: `DWS_AGENT_MODEL` | | `--agent-workdir` | agent 运行目录:放知识文件(如 CLAUDE.md)可给机器人企业上下文。默认空白临时目录(冷启动快 ~4s vs 大目录 ~29s,慢了会错过钉钉响应窗口)。env: `DWS_AGENT_WORKDIR` | diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 6f512755..5bccf50c 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -32,7 +32,7 @@ dws devapp list --name DemoApp --format json dws devapp list --app-key dingxxx --format json ``` -MCP tool: `list_open_dev_app` +MCP tool: `list_dev_app` | CLI | MCP | 说明 | |-----|-----|------| @@ -97,7 +97,7 @@ MCP tool: `delete_dev_app`。删除前必须展示应用摘要,异步生效。 dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json ``` -MCP tool: `get_open_dev_app_credentials`。返回含 `clientSecret/appSecret`,按敏感凭证处理。不能用 `devapp get` 代替;如果 `devapp get` 偶尔返回密钥,也只用于内部判断并脱敏。 +MCP tool: `get_dev_app_credentials`。返回含 `clientSecret/appSecret`,按敏感凭证处理。不能用 `devapp get` 代替;如果 `devapp get` 偶尔返回密钥,也只用于内部判断并脱敏。 ### 网页应用 @@ -106,7 +106,7 @@ dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com --dry-run --format json ``` -MCP tools: `get_webapp_config` / `set_webapp_config` +MCP tools: `get_extension_webapp_config` / `set_extension_webapp_config` `h5PageType` 未显式传入时,不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 @@ -122,7 +122,7 @@ dws devapp permission list --unified-app-id ID --keyword "机器人" --status UN dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json ``` -MCP tool: `list_open_dev_app_permissions` +MCP tool: `list_dev_app_permissions` | CLI | MCP | 说明 | |-----|-----|------| @@ -151,7 +151,7 @@ MCP tool: `list_open_dev_app_permissions` dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json ``` -MCP tool: `apply_open_dev_app_permissions`。`requiredApproval=true` 允许申请,写入版本变更。 +MCP tool: `apply_dev_app_permissions`。`requiredApproval=true` 允许申请,写入版本变更。 ### 取消权限 @@ -159,7 +159,7 @@ MCP tool: `apply_open_dev_app_permissions`。`requiredApproval=true` 允许申 dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --dry-run --format json ``` -MCP tool: `remove_open_dev_app_permission`。一次只取消一个。 +MCP tool: `remove_dev_app_permissions`。一次只取消一个。 --- @@ -253,7 +253,7 @@ dws devapp robot connect --robot-client-id CLIENT_ID --robot-client-secret CLIEN - **不建号**:connect 只建联。缺凭证先用 `robot create/submit` 建号拿 clientId/clientSecret。 - **凭证用 `--robot-client-id/--robot-client-secret`**(不叫 `--client-id`,那是全局 OAuth 客户端覆盖 flag,会撞名)。 -- **两种来源**:① 直接 `--robot-client-id/--robot-client-secret`;② `--unified-app-id`(内部调 `get_open_dev_app_credentials` 自动取)。⚠️ 来源②**字段名待预发真机验证**,且 clientSecret 一般仅建号时返回一次、该接口未必能返回,取不到时回退手填。 +- **两种来源**:① 直接 `--robot-client-id/--robot-client-secret`;② `--unified-app-id`(内部调 `get_dev_app_credentials` 自动取)。⚠️ 来源②**字段名待预发真机验证**,且 clientSecret 一般仅建号时返回一次、该接口未必能返回,取不到时回退手填。 - **渠道 `--channel`**:`auto`(默认,按运行时信号自动识别当前宿主) | `openclaw` | `qoder` | `qoderwork` | `hermes` | `workbuddy` | `claudecode` | `codebuddy` | `codex` | `gemini` | `opencode`。 - **会话记忆 `--agent-memory`**(默认开):同一群/单聊共享 agent 会话,追问保留上下文。仅 claudecode/codebuddy/workbuddy 支持(CLI 有 `--session-id/--resume`);其余渠道自动无状态。`--agent-memory=false` 关闭。 - **模型覆盖 `--agent-model`**:claudecode 默认锁 haiku 求快,可改 sonnet/opus 换聪明(env `DWS_AGENT_MODEL`)。 diff --git a/skills/multi/dingtalk-devapp/references/app-crud.md b/skills/multi/dingtalk-devapp/references/app-crud.md index 0d773886..dffc302b 100644 --- a/skills/multi/dingtalk-devapp/references/app-crud.md +++ b/skills/multi/dingtalk-devapp/references/app-crud.md @@ -21,7 +21,7 @@ dws devapp list --app-key dingxxx --format json dws devapp list --name DemoApp --page-size 20 --cursor NEXT_CURSOR --format json ``` -MCP tool: `list_open_dev_app` +MCP tool: `list_dev_app` | CLI | MCP | 说明 | |-----|-----|------| diff --git a/skills/multi/dingtalk-devapp/references/credentials-webapp.md b/skills/multi/dingtalk-devapp/references/credentials-webapp.md index 9ce8010f..864a7566 100644 --- a/skills/multi/dingtalk-devapp/references/credentials-webapp.md +++ b/skills/multi/dingtalk-devapp/references/credentials-webapp.md @@ -8,7 +8,7 @@ dws devapp credentials get --app-key dingxxx --format json dws devapp credentials get --name DemoApp --format json ``` -MCP tool: `get_open_dev_app_credentials` +MCP tool: `get_dev_app_credentials` 后端 facade: `OpenInnerAppQueryFacade.getCredentials`。 @@ -33,7 +33,7 @@ dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json dws devapp webapp get --app-key dingxxx --format json ``` -MCP tool: `get_webapp_config` +MCP tool: `get_extension_webapp_config` 未配置网页应用前可能只返回空配置。 @@ -44,7 +44,7 @@ dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https:// dws devapp webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --pc-homepage-url https://example.com/pc --yes --format json ``` -MCP tool: `set_webapp_config` +MCP tool: `set_extension_webapp_config` | CLI | MCP | 说明 | |-----|-----|------| diff --git a/skills/multi/dingtalk-devapp/references/permissions.md b/skills/multi/dingtalk-devapp/references/permissions.md index 3b5a310c..79ff7b38 100644 --- a/skills/multi/dingtalk-devapp/references/permissions.md +++ b/skills/multi/dingtalk-devapp/references/permissions.md @@ -11,7 +11,7 @@ dws devapp permission list --unified-app-id ID --scope-type SNS --format json dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json ``` -MCP tool: `list_open_dev_app_permissions` +MCP tool: `list_dev_app_permissions` | CLI | MCP | 说明 | |-----|-----|------| @@ -74,7 +74,7 @@ dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg dws devapp permission add --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --yes --format json ``` -MCP tool: `apply_open_dev_app_permissions` +MCP tool: `apply_dev_app_permissions` **规则:** - `--permissions` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 @@ -89,6 +89,6 @@ dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendms dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --yes --format json ``` -MCP tool: `remove_open_dev_app_permission` +MCP tool: `remove_dev_app_permissions` 一次只取消一个权限点。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md index 834d9d98..6b95a22f 100644 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ b/skills/multi/dingtalk-devapp/references/robot.md @@ -148,7 +148,7 @@ Codex 渠道注意: |------|------| | `--channel` | `auto`(默认,运行时信号自动识别) / openclaw / qoder / qoderwork / hermes / workbuddy / claudecode / codebuddy / codex / gemini / opencode | | `--robot-client-id` / `--robot-client-secret` | 现成机器人凭证(clientId=AppKey, clientSecret=AppSecret)。命名带 `robot-` 前缀以避开全局 OAuth `--client-id` flag | -| `--unified-app-id` | 统一应用 ID,内部调 `get_open_dev_app_credentials` 自动取凭证。⚠️ 字段名待预发真机验证;clientSecret 仅建号时返回一次、未必可取,必要时回退手填 | +| `--unified-app-id` | 统一应用 ID,内部调 `get_dev_app_credentials` 自动取凭证。⚠️ 字段名待预发真机验证;clientSecret 仅建号时返回一次、未必可取,必要时回退手填 | | `--agent-memory` | 按会话续聊(默认开):同一群/单聊共享 agent 会话,追问保留上下文。仅 claudecode/codebuddy/workbuddy(CLI 有 `--session-id`/`--resume`);qoder 系/codex/gemini/opencode 无寻址会话,自动保持无状态。`--agent-memory=false` 关闭 | | `--agent-model` | 覆盖本地 agent 模型(如 claudecode 默认锁 haiku 求快,可改 `claude-sonnet-4-6` 换聪明)。env: `DWS_AGENT_MODEL` | | `--agent-workdir` | agent 运行目录:放知识文件(如 CLAUDE.md)可给机器人企业上下文。默认空白临时目录(冷启动快 ~4s vs 大目录 ~29s,慢了会错过钉钉响应窗口)。env: `DWS_AGENT_WORKDIR` | From caf672699f36e0f38b663df4d9d063024074732c Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Tue, 16 Jun 2026 11:01:33 +0800 Subject: [PATCH 10/47] tool name rename --- docs/devapp-yulan-command-routing.md | 40 +++++++------- internal/helpers/devapp.go | 36 ++++++------- internal/helpers/devapp_test.go | 52 +++++++++++++------ skills/dingtalk-devapp/SKILL.md | 2 +- skills/dingtalk-devapp/references/event.md | 6 +-- skills/dingtalk-devapp/references/robot.md | 12 ++--- skills/dingtalk-devapp/references/version.md | 12 ++--- skills/mono/references/products/devapp.md | 10 ++-- skills/multi/dingtalk-devapp/SKILL.md | 2 +- .../multi/dingtalk-devapp/references/event.md | 6 +-- .../multi/dingtalk-devapp/references/robot.md | 12 ++--- .../dingtalk-devapp/references/version.md | 12 ++--- 12 files changed, 111 insertions(+), 91 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 6f0a2f69..8f443196 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -95,7 +95,7 @@ dws devapp robot get Get robot config of an existing app robot config Create or update robot config on an existing app robot enable Enable / re-enable robot capability - robot offline Offline robot capability + robot disable Disable robot capability version create Save app version version list List app versions (cursor paged) version get Get one version detail @@ -138,10 +138,10 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Permission list | `devapp permission list/search/detail` | `list_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | | Permission apply | `devapp permission add` | `apply_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | | Permission remove | `devapp permission remove` | `remove_open_dev_app_permission` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | -| Events | `devapp event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | Implemented | Hardcoded helper | +| Events | `devapp event list/subscribe/unsubscribe` | `list_dev_app_events`, `subscribe_dev_app_event`, `unsubscribe_dev_app_event` | Implemented | Hardcoded helper | | Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | -| Robot config | `devapp robot get/config/enable/offline` | `get/set_open_dev_app_robot_config`, `enable/offline_open_dev_app_robot` | Implemented | Hardcoded helper | -| Version | `devapp version create/list/get/check-approval/publish/status` | `create/list_open_dev_app_versions`, `get_open_dev_app_version_detail/status`, `publish_open_dev_app_version` | Implemented | Hardcoded helper | +| Robot config | `devapp robot get/config/enable/disable` | `get_dev_app_robot_config`, `set_extension_robot_config`, `enable_dev_app_robot`, `disable_dev_app_robot` | Implemented | Hardcoded helper | +| Version | `devapp version create/list/get/check-approval/publish/status` | `create_dev_app_version`, `list_dev_app_versions`, `get_dev_app_version_detail`, `publish_dev_app_version`, `get_dev_app_version_status` | Implemented | Hardcoded helper | P0 for yulan is app CRUD, permissions, credentials, and web app config. Robot, version, and event subscription are now all implemented as hardcoded helper @@ -967,9 +967,9 @@ real model is "list subscribable events + subscribe/unsubscribe one event by | CLI | MCP tool | Notes | | --- | --- | --- | -| `devapp event list` | `list_open_dev_app_events` | Args `unifiedAppId`; returns `pushType` + `events[]` (`eventCode`/`eventName`/`subscribed`). Read. | -| `devapp event subscribe` | `subscribe_open_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write (`--dry-run`/`--yes`). | -| `devapp event unsubscribe` | `unsubscribe_open_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write. | +| `devapp event list` | `list_dev_app_events` | Args `unifiedAppId`; returns `pushType` + `events[]` (`eventCode`/`eventName`/`subscribed`). Read. | +| `devapp event subscribe` | `subscribe_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write (`--dry-run`/`--yes`). | +| `devapp event unsubscribe` | `unsubscribe_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write. | Note: for gray unified apps, subscribe/unsubscribe are staged into version metadata and only take effect after `version publish`. The event callback URL @@ -980,12 +980,12 @@ Implemented version tools (verified against the `op-app` MCP `tools/list`): | CLI | MCP tool | Notes | | --- | --- | --- | -| `devapp version create` | `create_open_dev_app_version` | Save a new version from current config (`version`, `description`). | -| `devapp version list` | `list_open_dev_app_versions` | Cursor-paged list (`cursor`, `pageSize`; response `items/nextCursor/hasMore`). | -| `devapp version get` | `get_open_dev_app_version_detail` | One version detail by `versionId`. | -| `devapp version check-approval` | `publish_open_dev_app_version` (`precheckOnly=true`) | Precheck approval requirement / approvers; does not publish. | -| `devapp version publish` | `publish_open_dev_app_version` (`precheckOnly=false`) | Publish; `confirmedSensitive` for high-sensitivity scopes, optional `approverUserId`. | -| `devapp version status` | `get_open_dev_app_version_status` | Poll publish and approval state. | +| `devapp version create` | `create_dev_app_version` | Save a new version from current config (`version`, `description`). | +| `devapp version list` | `list_dev_app_versions` | Cursor-paged list (`cursor`, `pageSize`; response `items/nextCursor/hasMore`). | +| `devapp version get` | `get_dev_app_version_detail` | One version detail by `versionId`. | +| `devapp version check-approval` | `publish_dev_app_version` (`precheckOnly=true`) | Precheck approval requirement / approvers; does not publish. | +| `devapp version publish` | `publish_dev_app_version` (`precheckOnly=false`) | Publish; `confirmedSensitive` for high-sensitivity scopes, optional `approverUserId`. | +| `devapp version status` | `get_dev_app_version_status` | Poll publish and approval state. | Note: the MCP `precheckOnly` field is the server-side approval precheck and maps to `version check-approval`. It is distinct from the CLI global `--dry-run` (preview @@ -999,12 +999,12 @@ Implemented robot tools: | `devapp robot create` | `create_dingtalk_robot` | Synchronously create a new agent app + robot. | | `devapp robot submit` | `submit_robot_create_task` | Async submit; supports retry via original `taskId`. | | `devapp robot result` | `query_robot_create_result` | Poll async task by `taskId`. | -| `devapp robot get` | `get_open_dev_app_robot_config` | Read robot config of an existing app. | -| `devapp robot config` | `set_open_dev_app_robot_config` | Create or update robot config on an existing app. | -| `devapp robot enable` | `enable_open_dev_app_robot` | Enable / re-enable robot capability. | -| `devapp robot offline` | `offline_open_dev_app_robot` | Offline robot capability. | +| `devapp robot get` | `get_dev_app_robot_config` | Read robot config of an existing app. | +| `devapp robot config` | `set_extension_robot_config` | Create or update robot config on an existing app. | +| `devapp robot enable` | `enable_dev_app_robot` | Enable / re-enable robot capability. | +| `devapp robot disable` | `disable_dev_app_robot` | Disable robot capability. | -Robot create/submit and config/enable/offline are write operations and +Robot create/submit and config/enable/disable are write operations and use the `--dry-run`/`--yes` guard; `get` and `result` are reads. Stitching rule: @@ -1089,8 +1089,8 @@ Intent table: | "申请权限/开通权限" | `permission add` | `apply_open_dev_app_permissions` | | "取消权限/移除权限" | `permission remove` | `remove_open_dev_app_permission` | | "拿 appSecret/clientSecret" | `credentials get` | `get_open_dev_app_credentials` | -| "查可订阅事件/订阅/退订事件" | `event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | -| "发布/审核/选审批人" | `version ...` | `publish_open_dev_app_version` etc. | +| "查可订阅事件/订阅/退订事件" | `event list/subscribe/unsubscribe` | `list_dev_app_events`, `subscribe_dev_app_event`, `unsubscribe_dev_app_event` | +| "发布/审核/选审批人" | `version ...` | `publish_dev_app_version` etc. | Yulan invocation examples: diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 505949d6..5bc83382 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -46,22 +46,22 @@ const ( devAppRobotCreateTool = "create_dingtalk_robot" devAppRobotSubmitTool = "submit_robot_create_task" devAppRobotResultTool = "query_robot_create_result" - devAppRobotConfigGetTool = "get_open_dev_app_robot_config" - devAppRobotConfigSetTool = "set_open_dev_app_robot_config" - devAppRobotEnableTool = "enable_open_dev_app_robot" - devAppRobotOfflineTool = "offline_open_dev_app_robot" + devAppRobotConfigGetTool = "get_dev_app_robot_config" + devAppRobotConfigSetTool = "set_extension_robot_config" + devAppRobotEnableTool = "enable_dev_app_robot" + devAppRobotDisableTool = "disable_dev_app_robot" // 版本发布能力(op-app MCP 工具,硬编码不走服务发现)。 - devAppVersionCreateTool = "create_open_dev_app_version" - devAppVersionListTool = "list_open_dev_app_versions" - devAppVersionDetailTool = "get_open_dev_app_version_detail" - devAppVersionPublishTool = "publish_open_dev_app_version" - devAppVersionStatusTool = "get_open_dev_app_version_status" + devAppVersionCreateTool = "create_dev_app_version" + devAppVersionListTool = "list_dev_app_versions" + devAppVersionDetailTool = "get_dev_app_version_detail" + devAppVersionPublishTool = "publish_dev_app_version" + devAppVersionStatusTool = "get_dev_app_version_status" // 事件订阅能力(op-app MCP 工具,硬编码不走服务发现)。 - devAppEventListTool = "list_open_dev_app_events" - devAppEventSubscribeTool = "subscribe_open_dev_app_event" - devAppEventUnsubscribeTool = "unsubscribe_open_dev_app_event" + devAppEventListTool = "list_dev_app_events" + devAppEventSubscribeTool = "subscribe_dev_app_event" + devAppEventUnsubscribeTool = "unsubscribe_dev_app_event" ) func init() { @@ -182,7 +182,7 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { newDevAppRobotConfigGetCommand(runner), newDevAppRobotConfigCommand(runner, "config", "创建或更新现有应用机器人配置", devAppRobotConfigSetTool), newDevAppRobotConfigCommand(runner, "enable", "启用现有应用机器人能力", devAppRobotEnableTool), - newDevAppRobotOfflineCommand(runner), + newDevAppRobotDisableCommand(runner), newDevAppRobotConnectCommand(runner), ) @@ -805,22 +805,22 @@ func newDevAppRobotConfigCommand(runner executor.Runner, use, short, tool string return cmd } -func newDevAppRobotOfflineCommand(runner executor.Runner) *cobra.Command { +func newDevAppRobotDisableCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ - Use: "offline", + Use: "disable", Short: "停用现有应用的机器人能力", - Example: " dws devapp robot offline --unified-app-id UNIFIED_APP_ID --dry-run --format json", + Example: " dws devapp robot disable --unified-app-id UNIFIED_APP_ID --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - if err := devAppRequireWriteGuard(cmd, "robot offline"); err != nil { + if err := devAppRequireWriteGuard(cmd, "robot disable"); err != nil { return err } appID, err := requiredDevAppUnifiedID(cmd) if err != nil { return err } - return runDevAppTool(runner, cmd, devAppRobotOfflineTool, map[string]any{"unifiedAppId": appID}) + return runDevAppTool(runner, cmd, devAppRobotDisableTool, map[string]any{"unifiedAppId": appID}) }, } addDevAppUnifiedIDFlag(cmd) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 33e110da..3fd3bdec 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -170,13 +170,13 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { { name: "get config", args: []string{"robot", "get", "--unified-app-id", "u-1"}, - wantTool: "get_open_dev_app_robot_config", + wantTool: "get_dev_app_robot_config", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { name: "config create with skills and mode", args: []string{"robot", "config", "--unified-app-id", "u-1", "--name", "小助手", "--brief", "审批助手", "--mode", "2", "--skills", "qa,approval", "--add-scope", "--yes"}, - wantTool: "set_open_dev_app_robot_config", + wantTool: "set_extension_robot_config", wantParams: map[string]any{ "unifiedAppId": "u-1", "name": "小助手", @@ -187,9 +187,9 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { }, }, { - name: "offline", - args: []string{"robot", "offline", "--unified-app-id", "u-1", "--yes"}, - wantTool: "offline_open_dev_app_robot", + name: "disable", + args: []string{"robot", "disable", "--unified-app-id", "u-1", "--yes"}, + wantTool: "disable_dev_app_robot", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, } @@ -229,7 +229,7 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { { name: "create", args: []string{"version", "create", "--unified-app-id", "u-1", "--version", "1.0.1", "--desc", "新增机器人", "--yes"}, - wantTool: "create_open_dev_app_version", + wantTool: "create_dev_app_version", wantParams: map[string]any{ "unifiedAppId": "u-1", "version": "1.0.1", @@ -239,7 +239,7 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { { name: "list", args: []string{"version", "list", "--unified-app-id", "u-1", "--cursor", "cursor-1", "--page-size", "5"}, - wantTool: "list_open_dev_app_versions", + wantTool: "list_dev_app_versions", wantParams: map[string]any{ "unifiedAppId": "u-1", "cursor": "cursor-1", @@ -249,19 +249,19 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { { name: "get detail", args: []string{"version", "get", "--unified-app-id", "u-1", "--version-id", "v-1"}, - wantTool: "get_open_dev_app_version_detail", + wantTool: "get_dev_app_version_detail", wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1"}, }, { name: "check-approval prechecks only", args: []string{"version", "check-approval", "--unified-app-id", "u-1", "--version-id", "v-1"}, - wantTool: "publish_open_dev_app_version", + wantTool: "publish_dev_app_version", wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1", "precheckOnly": true}, }, { name: "publish disables precheck and sets sensitive", args: []string{"version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1", "--confirm-sensitive", "--approver", "user-1", "--yes"}, - wantTool: "publish_open_dev_app_version", + wantTool: "publish_dev_app_version", wantParams: map[string]any{ "unifiedAppId": "u-1", "versionId": "v-1", @@ -273,7 +273,7 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { { name: "status", args: []string{"version", "status", "--unified-app-id", "u-1", "--version-id", "v-1"}, - wantTool: "get_open_dev_app_version_status", + wantTool: "get_dev_app_version_status", wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1"}, }, } @@ -310,19 +310,19 @@ func TestDevAppEventCommandsBuildToolParams(t *testing.T) { { name: "list", args: []string{"event", "list", "--unified-app-id", "u-1"}, - wantTool: "list_open_dev_app_events", + wantTool: "list_dev_app_events", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { name: "subscribe", args: []string{"event", "subscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org", "--yes"}, - wantTool: "subscribe_open_dev_app_event", + wantTool: "subscribe_dev_app_event", wantParams: map[string]any{"unifiedAppId": "u-1", "eventCode": "user_add_org"}, }, { name: "unsubscribe", args: []string{"event", "unsubscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org", "--yes"}, - wantTool: "unsubscribe_open_dev_app_event", + wantTool: "unsubscribe_dev_app_event", wantParams: map[string]any{"unifiedAppId": "u-1", "eventCode": "user_add_org"}, }, } @@ -383,7 +383,7 @@ func TestDevAppScopedCommandsRejectLegacyLocators(t *testing.T) { {name: "robot get", args: []string{"devapp", "robot", "get", "--app-id", "u-1"}}, {name: "robot config", args: []string{"devapp", "robot", "config", "--app-id", "u-1", "--name", "小助手", "--yes"}}, {name: "robot enable", args: []string{"devapp", "robot", "enable", "--app-id", "u-1", "--name", "小助手", "--yes"}}, - {name: "robot offline", args: []string{"devapp", "robot", "offline", "--app-id", "u-1", "--yes"}}, + {name: "robot disable", args: []string{"devapp", "robot", "disable", "--app-id", "u-1", "--yes"}}, {name: "event list", args: []string{"devapp", "event", "list", "--app-id", "u-1"}}, {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--app-id", "u-1", "--event-code", "user_add_org", "--yes"}}, {name: "event unsubscribe", args: []string{"devapp", "event", "unsubscribe", "--app-id", "u-1", "--event-code", "user_add_org", "--yes"}}, @@ -442,6 +442,26 @@ func TestDevAppScopedCommandsRejectAgentAndCustomKeyLocators(t *testing.T) { } } +func TestDevAppRobotOfflineCommandRemoved(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"devapp", "robot", "offline", "--unified-app-id", "u-1", "--yes"}) + + err := root.Execute() + if err == nil { + t.Fatal("Execute() error = nil, want unknown command") + } + if !strings.Contains(err.Error(), "unknown command") { + t.Fatalf("error = %q, want unknown command", err.Error()) + } + if runner.last.Tool != "" { + t.Fatalf("tool = %q, want no invocation", runner.last.Tool) + } +} + func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { cases := []struct { name string @@ -449,7 +469,7 @@ func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { }{ {name: "robot create", args: []string{"devapp", "robot", "create", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批"}}, {name: "robot config", args: []string{"devapp", "robot", "config", "--unified-app-id", "u-1", "--name", "小助手"}}, - {name: "robot offline", args: []string{"devapp", "robot", "offline", "--unified-app-id", "u-1"}}, + {name: "robot disable", args: []string{"devapp", "robot", "disable", "--unified-app-id", "u-1"}}, {name: "version create", args: []string{"devapp", "version", "create", "--unified-app-id", "u-1", "--version", "1.0.1"}}, {name: "version publish", args: []string{"devapp", "version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1"}}, {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org"}}, diff --git a/skills/dingtalk-devapp/SKILL.md b/skills/dingtalk-devapp/SKILL.md index 6a13b89c..5f200261 100644 --- a/skills/dingtalk-devapp/SKILL.md +++ b/skills/dingtalk-devapp/SKILL.md @@ -52,7 +52,7 @@ metadata: | 凭证与网页应用 | [credentials-webapp.md](references/credentials-webapp.md) | credentials get / webapp get / webapp config | | 权限管理 | [permissions.md](references/permissions.md) | permission list / permission add / permission remove | | 成员与安全 | [member-security.md](references/member-security.md) | member list / add / remove / security config | -| 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / offline | +| 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / disable | | 版本发布 | [version.md](references/version.md) | version create / list / get / check-approval / publish / status | | 事件订阅 | [event.md](references/event.md) | event list / subscribe / unsubscribe | | 操作流程 | [workflows.md](references/workflows.md) | 创建应用全流程 / 权限全流程 / 生命周期管理 | diff --git a/skills/dingtalk-devapp/references/event.md b/skills/dingtalk-devapp/references/event.md index cecdb196..634f193f 100644 --- a/skills/dingtalk-devapp/references/event.md +++ b/skills/dingtalk-devapp/references/event.md @@ -10,7 +10,7 @@ dws devapp event list --unified-app-id --format json ``` -MCP tool: `list_open_dev_app_events`。返回 `pushType`(当前推送类型)和 `events[]`,每项含: +MCP tool: `list_dev_app_events`。返回 `pushType`(当前推送类型)和 `events[]`,每项含: | 字段 | 含义 | |------|------| @@ -27,7 +27,7 @@ dws devapp event subscribe --unified-app-id --event-code user_add dws devapp event subscribe --unified-app-id --event-code user_add_org --yes --format json ``` -MCP tool: `subscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 +MCP tool: `subscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 ## 退订事件 @@ -36,7 +36,7 @@ dws devapp event unsubscribe --unified-app-id --event-code user_a dws devapp event unsubscribe --unified-app-id --event-code user_add_org --yes --format json ``` -MCP tool: `unsubscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 +MCP tool: `unsubscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 ## 字段映射 diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md index 834d9d98..c386f42f 100644 --- a/skills/dingtalk-devapp/references/robot.md +++ b/skills/dingtalk-devapp/references/robot.md @@ -3,7 +3,7 @@ 为开放平台企业内部应用创建和配置机器人。分两类场景: 1. **新建智能体机器人**:一次性创建一个新的 Agent 应用 + 承载机器人(`create` / `submit` / `result`)。 -2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `enable` / `offline`),通过 `--unified-app-id` 定位。 +2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `enable` / `disable`),通过 `--unified-app-id` 定位。 > `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 @@ -64,7 +64,7 @@ MCP tools: `submit_robot_create_task` / `query_robot_create_result` dws devapp robot get --unified-app-id --format json ``` -MCP tool: `get_open_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 +MCP tool: `get_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 状态判断: @@ -88,7 +88,7 @@ dws devapp robot config --unified-app-id --name 小助手 --brief dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json ``` -MCP tools: `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` +MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` | CLI | MCP | 说明 | |-----|-----|------| @@ -111,11 +111,11 @@ MCP tools: `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` ### 停用 ```bash -dws devapp robot offline --unified-app-id --dry-run --format json -dws devapp robot offline --unified-app-id --yes --format json +dws devapp robot disable --unified-app-id --dry-run --format json +dws devapp robot disable --unified-app-id --yes --format json ``` -MCP tool: `offline_open_dev_app_robot` +MCP tool: `disable_dev_app_robot` ### 建联(把机器人接到本地 agent) diff --git a/skills/dingtalk-devapp/references/version.md b/skills/dingtalk-devapp/references/version.md index bea2ae75..6426998d 100644 --- a/skills/dingtalk-devapp/references/version.md +++ b/skills/dingtalk-devapp/references/version.md @@ -21,7 +21,7 @@ dws devapp version create --unified-app-id --version 1.0.1 --desc dws devapp version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json ``` -MCP tool: `create_open_dev_app_version` +MCP tool: `create_dev_app_version` | CLI | MCP | 说明 | |-----|-----|------| @@ -35,7 +35,7 @@ dws devapp version list --unified-app-id --page-size 20 --format dws devapp version list --unified-app-id --cursor --page-size 20 --format json ``` -MCP tool: `list_open_dev_app_versions`(`--cursor`→`cursor`,`--page-size`→`pageSize`)。响应使用 `items` / `nextCursor` / `hasMore`。 +MCP tool: `list_dev_app_versions`(`--cursor`→`cursor`,`--page-size`→`pageSize`)。响应使用 `items` / `nextCursor` / `hasMore`。 新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 @@ -45,7 +45,7 @@ MCP tool: `list_open_dev_app_versions`(`--cursor`→`cursor`,`--page-size` dws devapp version get --unified-app-id --version-id --format json ``` -MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 +MCP tool: `get_dev_app_version_detail`。返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 ## 预检审批(不发布) @@ -53,7 +53,7 @@ MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能 dws devapp version check-approval --unified-app-id --version-id --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 强制 `precheckOnly=true`,仅返回审批要求和候选审批人,**不会实际发布**。 +MCP tool: `publish_dev_app_version`,CLI 强制 `precheckOnly=true`,仅返回审批要求和候选审批人,**不会实际发布**。 ## 发布 @@ -68,7 +68,7 @@ dws devapp version publish --unified-app-id --version-id --version-id --approver --yes --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 设 `precheckOnly=false`。 +MCP tool: `publish_dev_app_version`,CLI 设 `precheckOnly=false`。 | CLI | MCP | 说明 | |-----|-----|------| @@ -98,7 +98,7 @@ MCP tool: `publish_open_dev_app_version`,CLI 设 `precheckOnly=false`。 dws devapp version status --unified-app-id --version-id --format json ``` -MCP tool: `get_open_dev_app_version_status`。返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 +MCP tool: `get_dev_app_version_status`。返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 版本状态字段:`version create/list/get` 返回 `status`,`version status` 返回 `versionStatus`,二者都对齐版本枚举。 diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 6f512755..73afd380 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -216,10 +216,10 @@ MCP tools: `create_dingtalk_robot` / `submit_robot_create_task` / `query_robot_c dws devapp robot get --unified-app-id ID --format json dws devapp robot config --unified-app-id ID --name 小助手 --brief 审批助手 --outgoing-url URL --mode 2 --skills qa,approval --dry-run --format json dws devapp robot enable --unified-app-id ID --name 小助手 --dry-run --format json -dws devapp robot offline --unified-app-id ID --dry-run --format json +dws devapp robot disable --unified-app-id ID --dry-run --format json ``` -MCP tools: `get_open_dev_app_robot_config` / `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` / `offline_open_dev_app_robot`。 +MCP tools: `get_dev_app_robot_config` / `set_extension_robot_config` / `enable_dev_app_robot` / `disable_dev_app_robot`。 配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。应用未配机器人时 `get` 返回 `robot info is not exist`。 @@ -280,10 +280,10 @@ dws devapp version publish --unified-app-id ID --version-id VERSION_ID --confirm dws devapp version status --unified-app-id ID --version-id VERSION_ID --format json ``` -MCP tools: `create_open_dev_app_version` / `list_open_dev_app_versions` / `get_open_dev_app_version_detail` / `publish_open_dev_app_version` / `get_open_dev_app_version_status`。 +MCP tools: `create_dev_app_version` / `list_dev_app_versions` / `get_dev_app_version_detail` / `publish_dev_app_version` / `get_dev_app_version_status`。 - `version list` 使用 `cursor/pageSize`;首次不传 cursor,后续传响应里的 `nextCursor`。 -- `check-approval` = `publish_open_dev_app_version` 的 `precheckOnly=true` 预检模式,不实际发布。 +- `check-approval` = `publish_dev_app_version` 的 `precheckOnly=true` 预检模式,不实际发布。 - `publish` 设 `precheckOnly=false`;含高敏权限需 `--confirm-sensitive`,灰度选人模式用 `--approver USER_ID`。 - 流程:`permission add`(requiredApproval 写入版本变更)→ `version create` → `check-approval` → `publish` → `status`。 - 新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 @@ -330,7 +330,7 @@ dws devapp event subscribe --unified-app-id ID --event-code user_add_org --dry-r dws devapp event unsubscribe --unified-app-id ID --event-code user_add_org --dry-run --format json ``` -MCP tools: `list_open_dev_app_events` / `subscribe_open_dev_app_event` / `unsubscribe_open_dev_app_event`。 +MCP tools: `list_dev_app_events` / `subscribe_dev_app_event` / `unsubscribe_dev_app_event`。 - `event list` 返回 `pushType` 与 `events[]`(每项 `eventCode`/`eventName`/`subscribed`);订阅/退订只接受 `--event-code`(取自 list 返回),一次一个。 - 写操作走 `--dry-run`/`--yes` 写保护;`list` 是只读。 diff --git a/skills/multi/dingtalk-devapp/SKILL.md b/skills/multi/dingtalk-devapp/SKILL.md index 478caf07..98b36b46 100644 --- a/skills/multi/dingtalk-devapp/SKILL.md +++ b/skills/multi/dingtalk-devapp/SKILL.md @@ -58,7 +58,7 @@ metadata: | 凭证与网页应用 | [credentials-webapp.md](references/credentials-webapp.md) | credentials get / webapp get / webapp config | | 权限管理 | [permissions.md](references/permissions.md) | permission list / permission add / permission remove | | 成员与安全 | [member-security.md](references/member-security.md) | member list / add / remove / security config | -| 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / offline | +| 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / disable | | 版本发布 | [version.md](references/version.md) | version create / list / get / check-approval / publish / status | | 事件订阅 | [event.md](references/event.md) | event list / subscribe / unsubscribe | | 操作流程 | [workflows.md](references/workflows.md) | 创建应用全流程 / 权限全流程 / 生命周期管理 | diff --git a/skills/multi/dingtalk-devapp/references/event.md b/skills/multi/dingtalk-devapp/references/event.md index cecdb196..634f193f 100644 --- a/skills/multi/dingtalk-devapp/references/event.md +++ b/skills/multi/dingtalk-devapp/references/event.md @@ -10,7 +10,7 @@ dws devapp event list --unified-app-id --format json ``` -MCP tool: `list_open_dev_app_events`。返回 `pushType`(当前推送类型)和 `events[]`,每项含: +MCP tool: `list_dev_app_events`。返回 `pushType`(当前推送类型)和 `events[]`,每项含: | 字段 | 含义 | |------|------| @@ -27,7 +27,7 @@ dws devapp event subscribe --unified-app-id --event-code user_add dws devapp event subscribe --unified-app-id --event-code user_add_org --yes --format json ``` -MCP tool: `subscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 +MCP tool: `subscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 ## 退订事件 @@ -36,7 +36,7 @@ dws devapp event unsubscribe --unified-app-id --event-code user_a dws devapp event unsubscribe --unified-app-id --event-code user_add_org --yes --format json ``` -MCP tool: `unsubscribe_open_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 +MCP tool: `unsubscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 ## 字段映射 diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md index 834d9d98..c386f42f 100644 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ b/skills/multi/dingtalk-devapp/references/robot.md @@ -3,7 +3,7 @@ 为开放平台企业内部应用创建和配置机器人。分两类场景: 1. **新建智能体机器人**:一次性创建一个新的 Agent 应用 + 承载机器人(`create` / `submit` / `result`)。 -2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `enable` / `offline`),通过 `--unified-app-id` 定位。 +2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `enable` / `disable`),通过 `--unified-app-id` 定位。 > `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 @@ -64,7 +64,7 @@ MCP tools: `submit_robot_create_task` / `query_robot_create_result` dws devapp robot get --unified-app-id --format json ``` -MCP tool: `get_open_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 +MCP tool: `get_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 状态判断: @@ -88,7 +88,7 @@ dws devapp robot config --unified-app-id --name 小助手 --brief dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json ``` -MCP tools: `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` +MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` | CLI | MCP | 说明 | |-----|-----|------| @@ -111,11 +111,11 @@ MCP tools: `set_open_dev_app_robot_config` / `enable_open_dev_app_robot` ### 停用 ```bash -dws devapp robot offline --unified-app-id --dry-run --format json -dws devapp robot offline --unified-app-id --yes --format json +dws devapp robot disable --unified-app-id --dry-run --format json +dws devapp robot disable --unified-app-id --yes --format json ``` -MCP tool: `offline_open_dev_app_robot` +MCP tool: `disable_dev_app_robot` ### 建联(把机器人接到本地 agent) diff --git a/skills/multi/dingtalk-devapp/references/version.md b/skills/multi/dingtalk-devapp/references/version.md index bea2ae75..6426998d 100644 --- a/skills/multi/dingtalk-devapp/references/version.md +++ b/skills/multi/dingtalk-devapp/references/version.md @@ -21,7 +21,7 @@ dws devapp version create --unified-app-id --version 1.0.1 --desc dws devapp version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json ``` -MCP tool: `create_open_dev_app_version` +MCP tool: `create_dev_app_version` | CLI | MCP | 说明 | |-----|-----|------| @@ -35,7 +35,7 @@ dws devapp version list --unified-app-id --page-size 20 --format dws devapp version list --unified-app-id --cursor --page-size 20 --format json ``` -MCP tool: `list_open_dev_app_versions`(`--cursor`→`cursor`,`--page-size`→`pageSize`)。响应使用 `items` / `nextCursor` / `hasMore`。 +MCP tool: `list_dev_app_versions`(`--cursor`→`cursor`,`--page-size`→`pageSize`)。响应使用 `items` / `nextCursor` / `hasMore`。 新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 @@ -45,7 +45,7 @@ MCP tool: `list_open_dev_app_versions`(`--cursor`→`cursor`,`--page-size` dws devapp version get --unified-app-id --version-id --format json ``` -MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 +MCP tool: `get_dev_app_version_detail`。返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 ## 预检审批(不发布) @@ -53,7 +53,7 @@ MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能 dws devapp version check-approval --unified-app-id --version-id --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 强制 `precheckOnly=true`,仅返回审批要求和候选审批人,**不会实际发布**。 +MCP tool: `publish_dev_app_version`,CLI 强制 `precheckOnly=true`,仅返回审批要求和候选审批人,**不会实际发布**。 ## 发布 @@ -68,7 +68,7 @@ dws devapp version publish --unified-app-id --version-id --version-id --approver --yes --format json ``` -MCP tool: `publish_open_dev_app_version`,CLI 设 `precheckOnly=false`。 +MCP tool: `publish_dev_app_version`,CLI 设 `precheckOnly=false`。 | CLI | MCP | 说明 | |-----|-----|------| @@ -98,7 +98,7 @@ MCP tool: `publish_open_dev_app_version`,CLI 设 `precheckOnly=false`。 dws devapp version status --unified-app-id --version-id --format json ``` -MCP tool: `get_open_dev_app_version_status`。返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 +MCP tool: `get_dev_app_version_status`。返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 版本状态字段:`version create/list/get` 返回 `status`,`version status` 返回 `versionStatus`,二者都对齐版本枚举。 From 41b743de774855c56874402b396db771b364ffd5 Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Tue, 16 Jun 2026 11:02:42 +0800 Subject: [PATCH 11/47] test --- internal/helpers/devapp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 3fd3bdec..4fc29543 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -448,7 +448,7 @@ func TestDevAppRobotOfflineCommandRemoved(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "robot", "offline", "--unified-app-id", "u-1", "--yes"}) + root.SetArgs([]string{"devapp", "robot", "offline"}) err := root.Execute() if err == nil { From ef60d99b26167376890893a5e60df2b83feff5d0 Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Tue, 16 Jun 2026 12:12:19 +0800 Subject: [PATCH 12/47] get_robot_config --- docs/devapp-yulan-command-routing.md | 4 ++-- internal/helpers/devapp.go | 2 +- internal/helpers/devapp_test.go | 2 +- skills/dingtalk-devapp/references/robot.md | 2 +- skills/mono/references/products/devapp.md | 2 +- skills/multi/dingtalk-devapp/references/robot.md | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 76fd51f4..dcb530ff 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -140,7 +140,7 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Permission remove | `devapp permission remove` | `remove_dev_app_permissions` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | | Events | `devapp event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | Implemented | Hardcoded helper | | Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | -| Robot config | `devapp robot get/config/enable/disable` | `get_dev_app_robot_config`, `set_extension_robot_config`, `enable_dev_app_robot`, `disable_dev_app_robot` | Implemented | Hardcoded helper | +| Robot config | `devapp robot get/config/enable/disable` | `get_robot_config`, `set_extension_robot_config`, `enable_dev_app_robot`, `disable_dev_app_robot` | Implemented | Hardcoded helper | | Version | `devapp version create/list/get/check-approval/publish/status` | `create_dev_app_version`, `list_dev_app_versions`, `get_dev_app_version_detail`, `publish_dev_app_version`, `get_dev_app_version_status` | Implemented | Hardcoded helper | P0 for yulan is app CRUD, permissions, credentials, and web app config. Robot, @@ -999,7 +999,7 @@ Implemented robot tools: | `devapp robot create` | `create_dingtalk_robot` | Synchronously create a new agent app + robot. | | `devapp robot submit` | `submit_robot_create_task` | Async submit; supports retry via original `taskId`. | | `devapp robot result` | `query_robot_create_result` | Poll async task by `taskId`. | -| `devapp robot get` | `get_dev_app_robot_config` | Read robot config of an existing app. | +| `devapp robot get` | `get_robot_config` | Read robot config of an existing app. | | `devapp robot config` | `set_extension_robot_config` | Create or update robot config on an existing app. | | `devapp robot enable` | `enable_dev_app_robot` | Enable / re-enable robot capability. | | `devapp robot disable` | `disable_dev_app_robot` | Disable robot capability. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index f6cf9b0b..281218c4 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -46,7 +46,7 @@ const ( devAppRobotCreateTool = "create_dingtalk_robot" devAppRobotSubmitTool = "submit_robot_create_task" devAppRobotResultTool = "query_robot_create_result" - devAppRobotConfigGetTool = "get_dev_app_robot_config" + devAppRobotConfigGetTool = "get_robot_config" devAppRobotConfigSetTool = "set_extension_robot_config" devAppRobotEnableTool = "enable_dev_app_robot" devAppRobotDisableTool = "disable_dev_app_robot" diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index fa1c977f..324cacf4 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -170,7 +170,7 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { { name: "get config", args: []string{"robot", "get", "--unified-app-id", "u-1"}, - wantTool: "get_dev_app_robot_config", + wantTool: "get_robot_config", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md index 0db1886a..9ceaa654 100644 --- a/skills/dingtalk-devapp/references/robot.md +++ b/skills/dingtalk-devapp/references/robot.md @@ -64,7 +64,7 @@ MCP tools: `submit_robot_create_task` / `query_robot_create_result` dws devapp robot get --unified-app-id --format json ``` -MCP tool: `get_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 +MCP tool: `get_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 状态判断: diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index c5c586db..08d57189 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -219,7 +219,7 @@ dws devapp robot enable --unified-app-id ID --name 小助手 --dry-run --format dws devapp robot disable --unified-app-id ID --dry-run --format json ``` -MCP tools: `get_dev_app_robot_config` / `set_extension_robot_config` / `enable_dev_app_robot` / `disable_dev_app_robot`。 +MCP tools: `get_robot_config` / `set_extension_robot_config` / `enable_dev_app_robot` / `disable_dev_app_robot`。 配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。应用未配机器人时 `get` 返回 `robot info is not exist`。 diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md index 0db1886a..9ceaa654 100644 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ b/skills/multi/dingtalk-devapp/references/robot.md @@ -64,7 +64,7 @@ MCP tools: `submit_robot_create_task` / `query_robot_create_result` dws devapp robot get --unified-app-id --format json ``` -MCP tool: `get_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 +MCP tool: `get_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 状态判断: From c2f3653ec4ab5edaf83cfb7cf515d7edc68345a4 Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Tue, 16 Jun 2026 14:40:17 +0800 Subject: [PATCH 13/47] get_extension_robot_config --- docs/devapp-yulan-command-routing.md | 4 ++-- internal/helpers/devapp.go | 2 +- internal/helpers/devapp_test.go | 2 +- skills/dingtalk-devapp/references/robot.md | 2 +- skills/mono/references/products/devapp.md | 2 +- skills/multi/dingtalk-devapp/references/robot.md | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index dcb530ff..040ea582 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -140,7 +140,7 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Permission remove | `devapp permission remove` | `remove_dev_app_permissions` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | | Events | `devapp event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | Implemented | Hardcoded helper | | Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | -| Robot config | `devapp robot get/config/enable/disable` | `get_robot_config`, `set_extension_robot_config`, `enable_dev_app_robot`, `disable_dev_app_robot` | Implemented | Hardcoded helper | +| Robot config | `devapp robot get/config/enable/disable` | `get_extension_robot_config`, `set_extension_robot_config`, `enable_dev_app_robot`, `disable_dev_app_robot` | Implemented | Hardcoded helper | | Version | `devapp version create/list/get/check-approval/publish/status` | `create_dev_app_version`, `list_dev_app_versions`, `get_dev_app_version_detail`, `publish_dev_app_version`, `get_dev_app_version_status` | Implemented | Hardcoded helper | P0 for yulan is app CRUD, permissions, credentials, and web app config. Robot, @@ -999,7 +999,7 @@ Implemented robot tools: | `devapp robot create` | `create_dingtalk_robot` | Synchronously create a new agent app + robot. | | `devapp robot submit` | `submit_robot_create_task` | Async submit; supports retry via original `taskId`. | | `devapp robot result` | `query_robot_create_result` | Poll async task by `taskId`. | -| `devapp robot get` | `get_robot_config` | Read robot config of an existing app. | +| `devapp robot get` | `get_extension_robot_config` | Read robot config of an existing app. | | `devapp robot config` | `set_extension_robot_config` | Create or update robot config on an existing app. | | `devapp robot enable` | `enable_dev_app_robot` | Enable / re-enable robot capability. | | `devapp robot disable` | `disable_dev_app_robot` | Disable robot capability. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 281218c4..aa65f1ba 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -46,7 +46,7 @@ const ( devAppRobotCreateTool = "create_dingtalk_robot" devAppRobotSubmitTool = "submit_robot_create_task" devAppRobotResultTool = "query_robot_create_result" - devAppRobotConfigGetTool = "get_robot_config" + devAppRobotConfigGetTool = "get_extension_robot_config" devAppRobotConfigSetTool = "set_extension_robot_config" devAppRobotEnableTool = "enable_dev_app_robot" devAppRobotDisableTool = "disable_dev_app_robot" diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 324cacf4..9fccd5df 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -170,7 +170,7 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { { name: "get config", args: []string{"robot", "get", "--unified-app-id", "u-1"}, - wantTool: "get_robot_config", + wantTool: "get_extension_robot_config", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md index 9ceaa654..29d201ce 100644 --- a/skills/dingtalk-devapp/references/robot.md +++ b/skills/dingtalk-devapp/references/robot.md @@ -64,7 +64,7 @@ MCP tools: `submit_robot_create_task` / `query_robot_create_result` dws devapp robot get --unified-app-id --format json ``` -MCP tool: `get_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 +MCP tool: `get_extension_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 状态判断: diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 08d57189..94868e9e 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -219,7 +219,7 @@ dws devapp robot enable --unified-app-id ID --name 小助手 --dry-run --format dws devapp robot disable --unified-app-id ID --dry-run --format json ``` -MCP tools: `get_robot_config` / `set_extension_robot_config` / `enable_dev_app_robot` / `disable_dev_app_robot`。 +MCP tools: `get_extension_robot_config` / `set_extension_robot_config` / `enable_dev_app_robot` / `disable_dev_app_robot`。 配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。应用未配机器人时 `get` 返回 `robot info is not exist`。 diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md index 9ceaa654..29d201ce 100644 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ b/skills/multi/dingtalk-devapp/references/robot.md @@ -64,7 +64,7 @@ MCP tools: `submit_robot_create_task` / `query_robot_create_result` dws devapp robot get --unified-app-id --format json ``` -MCP tool: `get_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 +MCP tool: `get_extension_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 状态判断: From eaeb9ac05d26676e915acc7a4044096b261acc25 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:09:06 +0800 Subject: [PATCH 14/47] =?UTF-8?q?feat(dev):=20=E5=BC=80=E6=94=BE=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=20dev=20=E5=91=BD=E4=BB=A4=E9=9B=86=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev 伞形命令(app/connect/doc)、应用全生命周期、权限/成员/安全/机器人/ 版本/事件、cursor 透传分页、pretty 状态标注、connect 建联。MCP 工具名对齐 服务端实际注册名。 Co-Authored-By: Claude Opus 4.8 (1M context) --- internal/helpers/dev.go | 77 +++ internal/helpers/devapp.go | 814 ++++++++++++++++++----------- internal/helpers/devapp_connect.go | 53 +- internal/helpers/devapp_cursor.go | 45 ++ internal/helpers/devapp_pretty.go | 126 +++++ internal/helpers/devdoc.go | 20 +- 6 files changed, 796 insertions(+), 339 deletions(-) create mode 100644 internal/helpers/dev.go create mode 100644 internal/helpers/devapp_cursor.go create mode 100644 internal/helpers/devapp_pretty.go diff --git a/internal/helpers/dev.go b/internal/helpers/dev.go new file mode 100644 index 00000000..10356a66 --- /dev/null +++ b/internal/helpers/dev.go @@ -0,0 +1,77 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package helpers + +import ( + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/executor" + "github.com/spf13/cobra" +) + +func init() { + RegisterPublic(func() Handler { + return devHandler{} + }) +} + +// devHandler mounts the developer umbrella command `dws dev`, grouping the +// three developer-facing pillars under one entry: +// +// dev app application lifecycle (CRUD / credentials / permission / +// member / security / webapp / robot / version) +// dev connect link an existing robot to the local agent for debugging +// (promoted out of the robot subtree: it is a local-dev entry, +// not a robot configuration) +// dev doc open-platform developer doc search (bridges the devdoc +// product; `dws devdoc` keeps working independently) +// +// Future developer capabilities (event subscription, MCP tooling, ...) join +// as new subtrees here instead of new top-level commands. +type devHandler struct{} + +func (devHandler) Name() string { + return "dev" +} + +func (devHandler) Command(runner executor.Runner) *cobra.Command { + root := &cobra.Command{ + Use: "dev", + Short: "开放平台开发者能力", + Long: "钉钉开放平台开发者命令组:应用生命周期管理(app)、机器人本地调试建联(connect)、开发文档搜索(doc)。", + Args: cobra.NoArgs, + TraverseChildren: true, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + + doc := &cobra.Command{ + Use: "doc", + Short: "开放平台文档搜索", + Args: cobra.NoArgs, + TraverseChildren: true, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + doc.AddCommand(newDevdocArticleSearchCommand(runner)) + + root.AddCommand( + newDevAppCommand(runner), + newDevAppRobotConnectCommand(runner), + doc, + ) + return root +} diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 62025e05..00afbd25 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -28,51 +28,65 @@ import ( const ( devAppProduct = "devapp" - devAppMemberListTool = "list_open_dev_app_members" - devAppMemberAddTool = "add_open_dev_app_members" - devAppMemberRemoveTool = "remove_open_dev_app_members" - devAppSecurityConfigTool = "update_app_security_config" + // 工具名 = 服务端 op-app 网关**实际注册**的名字(已用 tools 真实联调逐个核对)。 + // 注意:服务端这批命名本身并不统一——前缀 dev_app 与 extension 混用、list 用复数、 + // permission 用 apply 而 member 用 add、robot 建号流程保留旧名(submit_robot_create_task/ + // query_robot_create_result)。CLI 这里**对齐服务端现状以跑通联调**,不在 CLI 做映射; + // 命名统一是服务端待办,详见 obsidian《dev 命令树 - 服务端 MCP 待改造清单》。 + // 集中声明、调用点不写字面量,避免拼写漂移。 + // + // 应用主体 + 子资源(凭证/网页/权限)。 + devAppListTool = "list_dev_app" + devAppGetTool = "get_dev_app" + devAppCreateTool = "create_dev_app" + devAppUpdateTool = "update_dev_app" + devAppDeleteTool = "delete_dev_app" + devAppEnableTool = "enable_dev_app" + devAppDisableTool = "disable_dev_app" + devAppCredentialsGetTool = "get_dev_app_credentials" + devAppWebappGetTool = "get_extension_webapp_config" + devAppWebappSetTool = "set_extension_webapp_config" + devAppPermissionListTool = "list_dev_app_permissions" + devAppPermissionAddTool = "apply_dev_app_permissions" + devAppPermissionRmTool = "remove_dev_app_permissions" + + devAppMemberListTool = "list_dev_app_members" + devAppMemberAddTool = "add_dev_app_members" + devAppMemberRemoveTool = "remove_dev_app_members" + devAppSecurityConfigTool = "update_dev_app_security_config" // 机器人能力(op-app MCP 工具,硬编码不走服务发现)。 - devAppRobotCreateTool = "create_dingtalk_robot" - devAppRobotSubmitTool = "submit_robot_create_task" - devAppRobotResultTool = "query_robot_create_result" - devAppRobotConfigGetTool = "get_open_dev_app_robot_config" - devAppRobotConfigCreateTool = "create_open_dev_app_robot_config" - devAppRobotConfigUpdateTool = "update_open_dev_app_robot_config" - devAppRobotEnableTool = "enable_open_dev_app_robot" - devAppRobotOfflineTool = "offline_open_dev_app_robot" + devAppRobotSubmitTool = "submit_robot_create_task" + devAppRobotResultTool = "query_robot_create_result" + devAppRobotConfigGetTool = "get_extension_robot_config" + // 上游待合并:create/update 两个 tool 合成一个 upsert(建/改判断在服务端)。 + // 见 docs/upstream-todo.md。上游上线前 CLI 调此名待联调。 + devAppRobotConfigUpsertTool = "set_extension_robot_config" + devAppRobotEnableTool = "enable_dev_app_robot" + devAppRobotOfflineTool = "disable_dev_app_robot" + + // 事件订阅能力(op-app MCP 工具,服务端新增)。 + devAppEventListTool = "list_dev_app_events" + devAppEventSubscribeTool = "subscribe_dev_app_event" + devAppEventUnsubscribeTool = "unsubscribe_dev_app_event" // 版本发布能力(op-app MCP 工具,硬编码不走服务发现)。 - devAppVersionCreateTool = "create_open_dev_app_version" - devAppVersionListTool = "list_open_dev_app_versions" - devAppVersionDetailTool = "get_open_dev_app_version_detail" - devAppVersionPublishTool = "publish_open_dev_app_version" - devAppVersionStatusTool = "get_open_dev_app_version_status" + devAppVersionCreateTool = "create_dev_app_version" + devAppVersionListTool = "list_dev_app_versions" + devAppVersionDetailTool = "get_dev_app_version_detail" + devAppVersionPublishTool = "publish_dev_app_version" + devAppVersionStatusTool = "get_dev_app_version_status" ) -func init() { - RegisterPublic(func() Handler { - return devAppHandler{} - }) -} - -type devAppHandler struct{} - -func (devAppHandler) Name() string { - return "devapp" -} - -func (devAppHandler) Command(runner executor.Runner) *cobra.Command { - return newDevAppCommand(runner) -} - +// newDevAppCommand builds the `app` subtree of `dws dev`. The cobra path is +// `dws dev app ...` while the MCP product id stays "devapp" — the id is a +// backend contract (SupplementServers/StaticServers injection key and the +// pinned op-app endpoint), decoupled from the user-facing command name. func newDevAppCommand(runner executor.Runner) *cobra.Command { root := &cobra.Command{ - Use: "devapp", - Aliases: []string{"app"}, + Use: "app", Short: "开放平台应用", - Long: "管理开放平台开发者应用:查询、详情、创建、更新、启停、删除、权限、网页应用、成员、安全配置、机器人和版本发布。", + Long: "管理开放平台开发者应用:查询、详情、创建、更新、启停、删除、权限、网页应用、成员、安全配置、机器人、版本发布和事件订阅。", Args: cobra.NoArgs, TraverseChildren: true, DisableAutoGenTag: true, @@ -163,15 +177,12 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { }, } robot.AddCommand( - newDevAppRobotCreateCommand(runner), newDevAppRobotSubmitCommand(runner), newDevAppRobotResultCommand(runner), newDevAppRobotConfigGetCommand(runner), - newDevAppRobotConfigCommand(runner, "config", "为现有应用创建机器人配置", devAppRobotConfigCreateTool), - newDevAppRobotConfigCommand(runner, "update", "更新现有应用机器人配置", devAppRobotConfigUpdateTool), - newDevAppRobotConfigCommand(runner, "enable", "启用现有应用机器人能力", devAppRobotEnableTool), + newDevAppRobotConfigCommand(runner), + newDevAppRobotEnableCommand(runner), newDevAppRobotOfflineCommand(runner), - newDevAppRobotConnectCommand(runner), ) version := &cobra.Command{ @@ -193,14 +204,30 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { newDevAppVersionStatusCommand(runner), ) + event := &cobra.Command{ + Use: "event", + Short: "开放平台应用事件订阅", + Args: cobra.NoArgs, + TraverseChildren: true, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + event.AddCommand( + newDevAppEventListCommand(runner), + newDevAppEventSubscribeCommand(runner), + newDevAppEventUnsubscribeCommand(runner), + ) + root.AddCommand( newDevAppListCommand(runner), newDevAppGetCommand(runner), newDevAppCreateCommand(runner), newDevAppUpdateCommand(runner), - newDevAppLifecycleCommand(runner, "delete", "删除开放平台企业内部应用", "delete_inner_app"), - newDevAppLifecycleCommand(runner, "inactive", "停用开放平台企业内部应用", "inactive_inner_app"), - newDevAppLifecycleCommand(runner, "active", "启用开放平台企业内部应用", "active_inner_app"), + newDevAppDeleteCommand(runner), + newDevAppLifecycleCommand(runner, "disable", "停用开放平台企业内部应用", devAppDisableTool), + newDevAppLifecycleCommand(runner, "enable", "启用开放平台企业内部应用", devAppEnableTool), credentials, webapp, permission, @@ -208,27 +235,108 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { security, robot, version, + event, ) return root } +// --------------------------------------------------------------------------- +// 事件订阅能力(服务端新增 list/subscribe/unsubscribe) +// --------------------------------------------------------------------------- + +func newDevAppEventListCommand(runner executor.Runner) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "查询应用已订阅的事件列表", + Example: " dws dev app event list --unified-app-id UNIFIED_APP_ID --page-size 20 --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + params := map[string]any{"unifiedAppId": appID} + devAppApplyCursorParams(cmd, params) + return runDevAppTool(runner, cmd, devAppEventListTool, params) + }, + } + addDevAppUnifiedIDFlag(cmd) + registerDevAppCursorFlags(cmd) + preferLegacyLeaf(cmd) + return cmd +} + +func newDevAppEventSubscribeCommand(runner executor.Runner) *cobra.Command { + cmd := &cobra.Command{ + Use: "subscribe", + Short: "订阅应用事件回调", + Example: " dws dev app event subscribe --unified-app-id UNIFIED_APP_ID --event-types bpms_task_change --callback-url https://example.com/cb --dry-run --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := devAppRequireWriteGuard(cmd, "event subscribe"); err != nil { + return err + } + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + params := map[string]any{"unifiedAppId": appID} + if types := parseDevAppListFlag(cmd, "event-types"); len(types) > 0 { + params["eventTypes"] = types + } + devAppPutString(params, "callbackUrl", devAppStringFlag(cmd, "callback-url")) + return runDevAppTool(runner, cmd, devAppEventSubscribeTool, params) + }, + } + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("event-types", "", "事件类型,多个用逗号或分号分隔") + cmd.Flags().String("callback-url", "", "事件回调地址") + preferLegacyLeaf(cmd) + return cmd +} + +func newDevAppEventUnsubscribeCommand(runner executor.Runner) *cobra.Command { + cmd := &cobra.Command{ + Use: "unsubscribe", + Short: "取消订阅应用事件", + Example: " dws dev app event unsubscribe --unified-app-id UNIFIED_APP_ID --event-types bpms_task_change --dry-run --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := devAppRequireWriteGuard(cmd, "event unsubscribe"); err != nil { + return err + } + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + params := map[string]any{"unifiedAppId": appID} + if types := parseDevAppListFlag(cmd, "event-types"); len(types) > 0 { + params["eventTypes"] = types + } + return runDevAppTool(runner, cmd, devAppEventUnsubscribeTool, params) + }, + } + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("event-types", "", "事件类型,多个用逗号或分号分隔") + preferLegacyLeaf(cmd) + return cmd +} + func newDevAppListCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "查询开放平台企业内部应用列表", - Example: " dws devapp list --name DemoApp --page 1 --page-size 20 --format json", + Example: " dws dev app list --name DemoApp --page-size 20 --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := map[string]any{ - "currentPage": devAppIntFlag(cmd, "page"), - "pageSize": devAppIntFlag(cmd, "page-size"), - } + params := map[string]any{} + devAppApplyCursorParams(cmd, params) devAppPutString(params, "appName", devAppFlagOrFallback(cmd, "name", "keyword")) - devAppPutInt(params, "agentId", devAppIntFlag(cmd, "agent-id")) - devAppPutInt(params, "appId", devAppIntFlag(cmd, "app-id")) devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) - devAppPutString(params, "customKey", devAppStringFlag(cmd, "custom-key")) devAppPutInt(params, "appGroupId", devAppIntFlag(cmd, "app-group-id")) devAppPutString(params, "creator", devAppStringFlag(cmd, "creator")) devAppPutString(params, "robotName", devAppStringFlag(cmd, "robot-name")) @@ -236,15 +344,14 @@ func newDevAppListCommand(runner executor.Runner) *cobra.Command { devAppPutInt(params, "filterCoolApp", devAppIntFlag(cmd, "filter-cool-app")) devAppPutString(params, "sortType", devAppStringFlag(cmd, "sort")) devAppPutString(params, "sortOrder", devAppStringFlag(cmd, "order")) - return runDevAppTool(runner, cmd, "list_open_dev_apps_by_condition", params) + return runDevAppTool(runner, cmd, devAppListTool, params) }, } - cmd.Flags().Int("page", 1, "分页页码,从 1 开始") - cmd.Flags().Int("page-size", 20, "分页大小") + registerDevAppCursorFlags(cmd) cmd.Flags().String("name", "", "应用名称关键词") cmd.Flags().String("keyword", "", "--name 的兼容别名") _ = cmd.Flags().MarkHidden("keyword") - addDevAppLocatorFlagsWithoutName(cmd) + cmd.Flags().String("app-key", "", "按 appKey/clientId 过滤") cmd.Flags().Int("app-group-id", 0, "应用分组 ID") cmd.Flags().String("creator", "", "创建人名称关键词") cmd.Flags().String("robot-name", "", "机器人名称关键词") @@ -260,18 +367,19 @@ func newDevAppGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "查询开放平台企业内部应用详情", - Example: " dws devapp get --agent-id 123456 --format json", + Example: " dws dev app get --unified-app-id UNIFIED_APP_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppLocatorParams(cmd, true) - if len(params) == 0 { - return devAppLocatorRequired(true) + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } - return runDevAppTool(runner, cmd, "get_open_dev_app_detail", params) + params := map[string]any{"unifiedAppId": appID} + return runDevAppTool(runner, cmd, devAppGetTool, params) }, } - addDevAppLocatorFlags(cmd) + addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -280,31 +388,26 @@ func newDevAppCreateCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "创建开放平台企业内部应用", - Example: " dws devapp create --name DemoApp --desc 内部应用 --dry-run --format json", + Example: " dws dev app create --name DemoApp --desc 内部应用 --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "create"); err != nil { return err } - appType := devAppStringFlag(cmd, "type") - if appType != "" && appType != "internal" { - return apperrors.NewValidation("--type currently only supports internal") - } name := devAppStringFlag(cmd, "name") if name == "" { - return apperrors.NewValidation("--name is required") + return apperrors.NewValidation("--name 为必填") } params := map[string]any{"appName": name} - devAppPutString(params, "appDesc", devAppStringFlag(cmd, "desc")) - devAppPutString(params, "appIcon", devAppStringFlag(cmd, "icon")) - return runDevAppTool(runner, cmd, "create_inner_app", params) + devAppPutString(params, "desc", devAppStringFlag(cmd, "desc")) + devAppPutString(params, "icon", devAppStringFlag(cmd, "icon")) + return runDevAppTool(runner, cmd, devAppCreateTool, params) }, } cmd.Flags().String("name", "", "应用名称 (必填)") cmd.Flags().String("desc", "", "应用描述") cmd.Flags().String("icon", "", "应用图标 mediaId") - cmd.Flags().String("type", "internal", "应用类型;当前仅支持 internal") preferLegacyLeaf(cmd) return cmd } @@ -313,37 +416,38 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "update", Short: "修改开放平台企业内部应用基础信息", - Example: " dws devapp update --agent-id 123456 --name DemoApp2 --dry-run --format json", + Example: " dws dev app update --unified-app-id UNIFIED_APP_ID --name DemoApp2 --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "update"); err != nil { return err } - params := devAppLocatorParams(cmd, false) - if len(params) == 0 { - return devAppLocatorRequired(false) + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } + params := map[string]any{"unifiedAppId": appID} updates := 0 if v := devAppStringFlag(cmd, "name"); v != "" { params["appName"] = v updates++ } if v := devAppStringFlag(cmd, "desc"); v != "" { - params["appDesc"] = v + params["desc"] = v updates++ } if v := devAppStringFlag(cmd, "icon"); v != "" { - params["appIcon"] = v + params["icon"] = v updates++ } if updates == 0 { - return apperrors.NewValidation("at least one update field is required: --name, --desc, or --icon") + return apperrors.NewValidation("至少提供一项待更新字段:--name、--desc 或 --icon") } - return runDevAppTool(runner, cmd, "update_inner_app", params) + return runDevAppTool(runner, cmd, devAppUpdateTool, params) }, } - addDevAppLocatorFlagsWithoutName(cmd) + addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("name", "", "新的应用名称") cmd.Flags().String("desc", "", "新的应用描述") cmd.Flags().String("icon", "", "新的应用图标 mediaId") @@ -355,18 +459,19 @@ func newDevAppCredentialsGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "读取开放平台应用凭证", - Example: " dws devapp credentials get --agent-id 123456 --format json", + Example: " dws dev app credentials get --unified-app-id UNIFIED_APP_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppLocatorParams(cmd, false) - if len(params) == 0 { - return devAppLocatorRequired(false) + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } - return runDevAppTool(runner, cmd, "get_open_dev_app_credentials", params) + params := map[string]any{"unifiedAppId": appID} + return runDevAppTool(runner, cmd, devAppCredentialsGetTool, params) }, } - addDevAppLocatorFlagsWithoutName(cmd) + addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -381,34 +486,123 @@ func newDevAppLifecycleCommand(runner executor.Runner, use, short, tool string) if err := devAppRequireWriteGuard(cmd, use); err != nil { return err } - params := devAppLocatorParams(cmd, false) - if len(params) == 0 { - return devAppLocatorRequired(false) + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } + params := map[string]any{"unifiedAppId": appID} return runDevAppTool(runner, cmd, tool, params) }, } - addDevAppLocatorFlagsWithoutName(cmd) + addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } +// newDevAppDeleteCommand is delete with a danger tier: deleting an app is +// irreversible, so beyond the write guard it requires --confirm-name to match +// the located app's real name. This guards against "located the wrong app and +// deleted it" — the agent must first know the name (via `get`/dry-run) before +// it can delete. The match is verified client-side (a `get` then compare), +// standard practice for destructive CLI ops (gh repo delete, gcloud). +func newDevAppDeleteCommand(runner executor.Runner) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete", + Short: "删除开放平台企业内部应用(不可逆,需 --confirm-name 二次确认)", + Example: " dws dev app delete --unified-app-id UNIFIED_APP_ID --confirm-name 应用名 --yes --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := devAppRequireWriteGuard(cmd, "delete"); err != nil { + return err + } + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + params := map[string]any{"unifiedAppId": appID} + // Dry-run previews the delete without requiring confirmation — + // the agent uses it (or `get`) to read the app name first. + if commandDryRun(cmd) { + return runDevAppTool(runner, cmd, devAppDeleteTool, params) + } + // Real execution: require --confirm-name and verify it matches. + confirmName := devAppStringFlag(cmd, "confirm-name") + if confirmName == "" { + return apperrors.NewValidation("删除不可逆,需二次确认:先用 `dev app get` 看应用名,再加 --confirm-name=<应用名>") + } + actualName, err := devAppFetchAppName(runner, cmd, params) + if err != nil { + return err + } + // 读不到应用名时 fail-closed:不可逆删除不能在无法校验 --confirm-name + // 的情况下放行,否则二次确认形同虚设。 + if actualName == "" { + return apperrors.NewValidation("无法读取应用名以校验 --confirm-name,已中止删除;请确认 --unified-app-id 正确,或先用 --dry-run / `dev app get` 预览") + } + if confirmName != actualName { + return apperrors.NewValidation(fmt.Sprintf("名称不匹配:--confirm-name=%q 但定位到的应用名是 %q,已中止删除", confirmName, actualName)) + } + return runDevAppTool(runner, cmd, devAppDeleteTool, params) + }, + } + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("confirm-name", "", "二次确认:必须与被删应用的名称一致(不可逆操作的防误删)") + preferLegacyLeaf(cmd) + return cmd +} + +// devAppFetchAppName resolves the located app's name via get_dev_app +// so delete can verify --confirm-name. Returns "" if the name can't be found; +// the caller treats "" as fail-closed (aborts the irreversible delete) rather +// than silently proceeding. +func devAppFetchAppName(runner executor.Runner, cmd *cobra.Command, locator map[string]any) (string, error) { + inv := executor.NewHelperInvocation( + cobracmd.LegacyCommandPath(cmd), + devAppProduct, + devAppGetTool, + locator, + ) + result, err := runner.Run(cmd.Context(), inv) + if err != nil { + return "", err + } + return devAppExtractString(result.Response, "appName"), nil +} + +// devAppExtractString descends the helper response (content → result) and reads +// a string field. Returns "" if absent. +func devAppExtractString(response map[string]any, key string) string { + node := response + if inner, ok := node["content"].(map[string]any); ok { + node = inner + } + if inner, ok := node["result"].(map[string]any); ok { + node = inner + } + if v, ok := node[key].(string); ok { + return v + } + return "" +} + func newDevAppWebappGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "查询网页应用配置", - Example: " dws devapp webapp get --agent-id 123456 --format json", + Example: " dws dev app webapp get --unified-app-id UNIFIED_APP_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppAppLocatorParams(cmd) - if len(params) == 0 { - return devAppAppLocatorRequired() + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } - return runDevAppTool(runner, cmd, "get_webapp_config", params) + params := map[string]any{"unifiedAppId": appID} + return runDevAppTool(runner, cmd, devAppWebappGetTool, params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -417,45 +611,46 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "config", Short: "配置网页应用能力", - Example: " dws devapp webapp config --agent-id 123456 --homepage-link https://example.com --dry-run --format json", + Example: " dws dev app webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "webapp config"); err != nil { return err } - params := devAppAppLocatorParams(cmd) - if len(params) == 0 { - return devAppAppLocatorRequired() + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } + params := map[string]any{"unifiedAppId": appID} updates := 0 if v := devAppStringFlag(cmd, "h5-page-type"); v != "" { params["h5PageType"] = v updates++ } - if v := devAppStringFlag(cmd, "homepage-link"); v != "" { - params["homepageLink"] = v + if v := devAppStringFlag(cmd, "homepage-url"); v != "" { + params["homepageUrl"] = v updates++ } - if v := devAppStringFlag(cmd, "pc-homepage-link"); v != "" { - params["pcHomepageLink"] = v + if v := devAppStringFlag(cmd, "pc-homepage-url"); v != "" { + params["pcHomepageUrl"] = v updates++ } - if v := devAppStringFlag(cmd, "omp-link"); v != "" { - params["ompLink"] = v + if v := devAppStringFlag(cmd, "omp-url"); v != "" { + params["ompUrl"] = v updates++ } if updates == 0 { - return apperrors.NewValidation("at least one webapp field is required: --h5-page-type, --homepage-link, --pc-homepage-link, or --omp-link") + return apperrors.NewValidation("至少提供一项网页应用配置:--h5-page-type、--homepage-url、--pc-homepage-url 或 --omp-url") } - return runDevAppTool(runner, cmd, "set_webapp_config", params) + return runDevAppTool(runner, cmd, devAppWebappSetTool, params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("h5-page-type", "", "网页应用生效端/页面类型") - cmd.Flags().String("homepage-link", "", "移动端首页地址") - cmd.Flags().String("pc-homepage-link", "", "PC 端首页地址") - cmd.Flags().String("omp-link", "", "管理后台地址") + cmd.Flags().String("homepage-url", "", "移动端首页地址") + cmd.Flags().String("pc-homepage-url", "", "PC 端首页地址") + cmd.Flags().String("omp-url", "", "管理后台地址") preferLegacyLeaf(cmd) return cmd } @@ -463,27 +658,27 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "list", - Aliases: []string{"search", "detail"}, + Aliases: []string{"search"}, Short: "查询开放平台应用权限列表", - Example: " dws devapp permission list --agent-id 123456 --keyword 通讯录 --limit 20 --format json", + Example: " dws dev app permission list --unified-app-id UNIFIED_APP_ID --keyword 通讯录 --page-size 20 --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppAppLocatorParams(cmd) - if len(params) == 0 { - return devAppAppLocatorRequired() + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } + params := map[string]any{"unifiedAppId": appID} devAppPutString(params, "keyword", devAppStringFlag(cmd, "keyword")) devAppPutString(params, "scopeValue", devAppFlagOrFallback(cmd, "scope", "permission")) devAppPutString(params, "authStatus", strings.ToUpper(devAppStringFlag(cmd, "status"))) - devAppPutString(params, "firstLevelType", strings.ToUpper(devAppStringFlag(cmd, "scope-type"))) + devAppPutString(params, "scopeType", strings.ToUpper(devAppStringFlag(cmd, "scope-type"))) devAppPutString(params, "apiStatus", devAppStringFlag(cmd, "api-status")) - devAppPutInt(params, "limit", devAppIntFlag(cmd, "limit")) - devAppPutInt(params, "offset", devAppIntFlag(cmd, "offset")) - return runDevAppTool(runner, cmd, "list_open_dev_app_permissions", params) + devAppApplyCursorParams(cmd, params) + return runDevAppTool(runner, cmd, devAppPermissionListTool, params) }, } - addDevAppAppLocatorFlags(cmd) + addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("keyword", "", "权限名、权限点、接口名关键词") cmd.Flags().String("scope", "", "精确权限点 scopeValue") cmd.Flags().String("permission", "", "--scope 的兼容别名") @@ -491,8 +686,7 @@ func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { cmd.Flags().String("status", "ALL", "权限状态:ALL、AUTHED、UNAUTHED") cmd.Flags().String("scope-type", "", "权限一级类型:APP 或 SNS") cmd.Flags().String("api-status", "", "开发者后台 apiStatus 过滤") - cmd.Flags().Int("limit", 20, "返回数量上限") - cmd.Flags().Int("offset", 0, "返回偏移量") + registerDevAppCursorFlags(cmd) preferLegacyLeaf(cmd) return cmd } @@ -501,27 +695,28 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "申请开放平台应用权限点", - Example: " dws devapp permission add --agent-id 123456 --permissions Contact.User.mobile --dry-run --format json", + Example: " dws dev app permission add --unified-app-id UNIFIED_APP_ID --permissions Contact.User.mobile --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "permission add"); err != nil { return err } - params := devAppAppLocatorParams(cmd) - if len(params) == 0 { - return devAppAppLocatorRequired() + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } + params := map[string]any{"unifiedAppId": appID} scopes := devAppPermissionScopes(cmd) if len(scopes) == 0 { - return apperrors.NewValidation("--permissions is required") + return apperrors.NewValidation("--permissions 为必填") } params["scopeValues"] = scopes - return runDevAppTool(runner, cmd, "apply_open_dev_app_permissions", params) + return runDevAppTool(runner, cmd, devAppPermissionAddTool, params) }, } - addDevAppAppLocatorFlags(cmd) - cmd.Flags().StringSlice("permissions", nil, "权限点 scopeValue,多个用逗号分隔") + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("permissions", "", "权限点 scopeValue,多个用逗号或分号分隔") cmd.Flags().String("scope", "", "--permissions 的兼容别名,单个权限点 scopeValue") cmd.Flags().String("permission", "", "--permissions 的兼容别名,单个权限点 scopeValue") _ = cmd.Flags().MarkHidden("scope") @@ -534,28 +729,66 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "取消开放平台应用权限点", - Example: " dws devapp permission remove --agent-id 123456 --permission Contact.User.mobile --dry-run --format json", + Example: " dws dev app permission remove --unified-app-id UNIFIED_APP_ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { if err := devAppRequireWriteGuard(cmd, "permission remove"); err != nil { return err } - params := devAppAppLocatorParams(cmd) - if len(params) == 0 { - return devAppAppLocatorRequired() + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err } - scope := devAppFlagOrFallback(cmd, "permission", "scope") - if scope == "" { - return apperrors.NewValidation("--permission is required") + locator := map[string]any{"unifiedAppId": appID} + scopes := devAppPermissionScopes(cmd) + if len(scopes) == 0 { + return apperrors.NewValidation("--permissions 为必填") } - params["scopeValue"] = scope - return runDevAppTool(runner, cmd, "remove_open_dev_app_permission", params) + // 上游 MCP tool 一次只接受一个权限点;多条时 CLI 逐条调用并聚合 + // 结果,单条保持原始输出形态不变。 + if len(scopes) == 1 { + params := devAppCloneParams(locator) + params["scopeValue"] = scopes[0] + return runDevAppTool(runner, cmd, devAppPermissionRmTool, params) + } + results := make([]map[string]any, 0, len(scopes)) + failed := 0 + for _, scope := range scopes { + params := devAppCloneParams(locator) + params["scopeValue"] = scope + invocation := executor.NewHelperInvocation( + cobracmd.LegacyCommandPath(cmd), + devAppProduct, + devAppPermissionRmTool, + params, + ) + invocation.DryRun = commandDryRun(cmd) + entry := map[string]any{"scopeValue": scope} + result, err := runner.Run(cmd.Context(), invocation) + if err != nil { + entry["error"] = err.Error() + failed++ + } else { + entry["result"] = result + } + results = append(results, entry) + } + // 逐条 best-effort:把整体成败显式写进 payload(ok / failedCount), + // 让读 JSON 的 agent 一眼看出部分失败,而不是只看 exit code 误判全成功。 + return writeCommandPayload(cmd, map[string]any{ + "results": results, + "ok": failed == 0, + "total": len(results), + "failedCount": failed, + }) }, } - addDevAppAppLocatorFlags(cmd) - cmd.Flags().String("permission", "", "待取消权限点 scopeValue") - cmd.Flags().String("scope", "", "--permission 的兼容别名") + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("permissions", "", "待取消权限点 scopeValue,多个用逗号或分号分隔(多条时逐条调用并聚合结果)") + cmd.Flags().String("permission", "", "--permissions 的兼容别名,单个权限点 scopeValue") + cmd.Flags().String("scope", "", "--permissions 的兼容别名") + _ = cmd.Flags().MarkHidden("permission") _ = cmd.Flags().MarkHidden("scope") preferLegacyLeaf(cmd) return cmd @@ -565,11 +798,11 @@ func newDevAppMemberListCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "查询开放平台应用成员", - Example: " dws devapp member list --app-id ", + Example: " dws dev app member list --unified-app-id ", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - appID, err := requiredDevAppID(cmd) + appID, err := requiredDevAppUnifiedID(cmd) if err != nil { return err } @@ -578,7 +811,7 @@ func newDevAppMemberListCommand(runner executor.Runner) *cobra.Command { }) }, } - cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") + addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -587,7 +820,7 @@ func newDevAppMemberAddCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "添加开放平台应用成员", - Example: " dws devapp member add --app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws dev app member add --unified-app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -603,7 +836,7 @@ func newDevAppMemberRemoveCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "移除开放平台应用成员", - Example: " dws devapp member remove --app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws dev app member remove --unified-app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -619,7 +852,7 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "config", Short: "更新开放平台应用安全配置", - Example: " dws devapp security config --app-id " + + Example: " dws dev app security config --unified-app-id " + "--ip-whitelist 192.0.2.10 --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, @@ -627,31 +860,35 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { if err := devAppRequireWriteGuard(cmd, "security config"); err != nil { return err } - appID, err := requiredDevAppID(cmd) + appID, err := requiredDevAppUnifiedID(cmd) if err != nil { return err } params := map[string]any{"unifiedAppId": appID} + updates := 0 if values := parseDevAppListFlag(cmd, "ip-whitelist"); len(values) > 0 { - params["ipWhiteList"] = values + params["ipWhitelist"] = values + updates++ } if values := parseDevAppListFlag(cmd, "redirect-url"); len(values) > 0 { params["redirectUrls"] = values + updates++ } if values := parseDevAppListFlag(cmd, "sso-url"); len(values) > 0 { - params["otherAuthUrls"] = values + params["ssoUrls"] = values + updates++ } - if len(params) == 1 { - return apperrors.NewValidation("one of --ip-whitelist, --redirect-url, or --sso-url is required") + if updates == 0 { + return apperrors.NewValidation("至少提供一项安全配置:--ip-whitelist、--redirect-url 或 --sso-url") } return runDevAppTool(runner, cmd, devAppSecurityConfigTool, params) }, } - cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") - cmd.Flags().String("ip-whitelist", "", "出口 IP 白名单,多个用逗号或分号分隔") - cmd.Flags().String("redirect-url", "", "登录重定向 URL,多个用逗号或分号分隔") - cmd.Flags().String("sso-url", "", "端内免登地址,多个用逗号或分号分隔") + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("ip-whitelist", "", "出口 IP 白名单,多个用逗号或分号分隔(整组覆盖,非追加)") + cmd.Flags().String("redirect-url", "", "登录重定向 URL,多个用逗号或分号分隔(整组覆盖,非追加)") + cmd.Flags().String("sso-url", "", "端内免登地址,多个用逗号或分号分隔(整组覆盖,非追加)") preferLegacyLeaf(cmd) return cmd } @@ -660,34 +897,11 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { // 机器人能力 // --------------------------------------------------------------------------- -func newDevAppRobotCreateCommand(runner executor.Runner) *cobra.Command { - cmd := &cobra.Command{ - Use: "create", - Short: "新建钉钉智能体机器人(一次性同步创建应用+机器人)", - Example: " dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", - Args: cobra.NoArgs, - DisableAutoGenTag: true, - RunE: func(cmd *cobra.Command, args []string) error { - if err := devAppRequireWriteGuard(cmd, "robot create"); err != nil { - return err - } - params, err := devAppRobotCreateParams(cmd) - if err != nil { - return err - } - return runDevAppTool(runner, cmd, devAppRobotCreateTool, params) - }, - } - registerDevAppRobotCreateFlags(cmd) - preferLegacyLeaf(cmd) - return cmd -} - func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "submit", Short: "异步提交钉钉智能体机器人创建任务(支持失败重试)", - Example: " dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", + Example: " dws dev app robot submit --app-name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -698,10 +912,10 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - // submit_robot_create_task 的 schema 把图标字段标为必填(空值时服务端用默认图标), + // submit_robot_create 的 schema 把图标字段标为必填(空值时服务端用默认图标), // 因此即使用户未提供也补空串占位。 - if _, ok := params["robotMediaId"]; !ok { - params["robotMediaId"] = "" + if _, ok := params["icon"]; !ok { + params["icon"] = "" } if _, ok := params["previewMediaId"]; !ok { params["previewMediaId"] = "" @@ -720,13 +934,13 @@ func newDevAppRobotResultCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "result", Short: "查询机器人异步创建任务结果", - Example: " dws devapp robot result --task-id TASK_ID --format json", + Example: " dws dev app robot result --task-id TASK_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { taskID := devAppStringFlag(cmd, "task-id") if taskID == "" { - return apperrors.NewValidation("--task-id is required") + return apperrors.NewValidation("--task-id 为必填") } return runDevAppTool(runner, cmd, devAppRobotResultTool, map[string]any{"taskId": taskID}) }, @@ -740,7 +954,7 @@ func newDevAppRobotConfigGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "查询现有应用的机器人配置", - Example: " dws devapp robot get --unified-app-id UNIFIED_APP_ID --format json", + Example: " dws dev app robot get --unified-app-id UNIFIED_APP_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -756,15 +970,20 @@ func newDevAppRobotConfigGetCommand(runner executor.Runner) *cobra.Command { return cmd } -func newDevAppRobotConfigCommand(runner executor.Runner, use, short, tool string) *cobra.Command { +// newDevAppRobotConfigCommand is the upsert command for an app's robot config: +// one command for both "首次创建" and "更新" — the create-vs-update decision is +// the upstream tool's job, not the CLI's (see docs/upstream-todo.md, where the +// old create/update tools merge into one `set_dev_app_robot_config`). +// `enable` (pure enable, no config fields) is a separate command. +func newDevAppRobotConfigCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ - Use: use, - Short: short, - Example: " dws devapp robot " + use + " --unified-app-id UNIFIED_APP_ID --name 小助手 --brief 审批助手 --dry-run --format json", + Use: "config", + Short: "创建或更新现有应用的机器人配置(upsert)", + Example: " dws dev app robot config --unified-app-id UNIFIED_APP_ID --name 小助手 --brief 审批助手 --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - if err := devAppRequireWriteGuard(cmd, "robot "+use); err != nil { + if err := devAppRequireWriteGuard(cmd, "robot config"); err != nil { return err } appID, err := requiredDevAppUnifiedID(cmd) @@ -776,9 +995,9 @@ func newDevAppRobotConfigCommand(runner executor.Runner, use, short, tool string return err } if updates == 0 { - return apperrors.NewValidation("at least one robot config field is required, e.g. --name, --brief, --description, --icon, --outgoing-url, --event-url, --mode, --skills") + return apperrors.NewValidation("至少提供一项机器人配置字段,如 --name、--brief、--desc、--icon、--outgoing-url、--event-url、--mode、--skills") } - return runDevAppTool(runner, cmd, tool, params) + return runDevAppTool(runner, cmd, devAppRobotConfigUpsertTool, params) }, } addDevAppUnifiedIDFlag(cmd) @@ -787,15 +1006,40 @@ func newDevAppRobotConfigCommand(runner executor.Runner, use, short, tool string return cmd } -func newDevAppRobotOfflineCommand(runner executor.Runner) *cobra.Command { +// newDevAppRobotEnableCommand enables an app's robot capability. Unlike config, +// it needs no config fields — pure enable, only the app locator. +func newDevAppRobotEnableCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ - Use: "offline", - Short: "停用现有应用的机器人能力", - Example: " dws devapp robot offline --unified-app-id UNIFIED_APP_ID --dry-run --format json", + Use: "enable", + Short: "启用现有应用机器人能力(纯启用,无需配置字段)", + Example: " dws dev app robot enable --unified-app-id UNIFIED_APP_ID --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - if err := devAppRequireWriteGuard(cmd, "robot offline"); err != nil { + if err := devAppRequireWriteGuard(cmd, "robot enable"); err != nil { + return err + } + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + return runDevAppTool(runner, cmd, devAppRobotEnableTool, map[string]any{"unifiedAppId": appID}) + }, + } + addDevAppUnifiedIDFlag(cmd) + preferLegacyLeaf(cmd) + return cmd +} + +func newDevAppRobotOfflineCommand(runner executor.Runner) *cobra.Command { + cmd := &cobra.Command{ + Use: "disable", + Short: "停用现有应用的机器人能力", + Example: " dws dev app robot disable --unified-app-id UNIFIED_APP_ID --dry-run --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := devAppRequireWriteGuard(cmd, "robot disable"); err != nil { return err } appID, err := requiredDevAppUnifiedID(cmd) @@ -821,22 +1065,22 @@ func registerDevAppRobotCreateFlags(cmd *cobra.Command) { func devAppRobotCreateParams(cmd *cobra.Command) (map[string]any, error) { appName := devAppStringFlag(cmd, "app-name") if appName == "" { - return nil, apperrors.NewValidation("--app-name is required") + return nil, apperrors.NewValidation("--app-name 为必填") } robotName := devAppStringFlag(cmd, "robot-name") if robotName == "" { - return nil, apperrors.NewValidation("--robot-name is required") + return nil, apperrors.NewValidation("--robot-name 为必填") } desc := devAppStringFlag(cmd, "desc") if desc == "" { - return nil, apperrors.NewValidation("--desc is required") + return nil, apperrors.NewValidation("--desc 为必填") } params := map[string]any{ "appName": appName, "robotName": robotName, "desc": desc, } - devAppPutString(params, "robotMediaId", devAppStringFlag(cmd, "icon")) + devAppPutString(params, "icon", devAppStringFlag(cmd, "icon")) devAppPutString(params, "previewMediaId", devAppStringFlag(cmd, "preview")) return params, nil } @@ -844,12 +1088,12 @@ func devAppRobotCreateParams(cmd *cobra.Command) (map[string]any, error) { func registerDevAppRobotConfigFlags(cmd *cobra.Command) { cmd.Flags().String("name", "", "机器人名称") cmd.Flags().String("brief", "", "机器人简介") - cmd.Flags().String("description", "", "机器人描述") + cmd.Flags().String("desc", "", "机器人描述") cmd.Flags().String("icon", "", "机器人图标 mediaId") - cmd.Flags().String("outgoing-url", "", "消息回调地址 (outgoingUrl)") - cmd.Flags().String("event-url", "", "事件回调地址 (chatBotEventUrl)") + cmd.Flags().String("outgoing-url", "", "消息回调地址") + cmd.Flags().String("event-url", "", "事件回调地址") cmd.Flags().Int("mode", 0, "机器人模式枚举") - cmd.Flags().StringSlice("skills", nil, "技能列表,多个用逗号分隔") + cmd.Flags().String("skills", "", "技能列表,多个用逗号或分号分隔") cmd.Flags().Bool("add-scope", false, "是否自动添加机器人相关权限") cmd.Flags().Bool("disable-ssl-verify", false, "回调地址是否关闭 SSL 校验") cmd.Flags().String("i18n-name", "", "机器人名称国际化 JSON,如 '{\"en_US\":\"Bot\"}'") @@ -868,10 +1112,10 @@ func devAppRobotConfigParams(cmd *cobra.Command, appID string) (map[string]any, } setString("name", "name") setString("brief", "brief") - setString("description", "description") - setString("iconMediaId", "icon") + setString("desc", "desc") + setString("icon", "icon") setString("outgoingUrl", "outgoing-url") - setString("chatBotEventUrl", "event-url") + setString("eventUrl", "event-url") if cmd.Flags().Changed("mode") { params["mode"] = devAppIntFlag(cmd, "mode") updates++ @@ -883,10 +1127,10 @@ func devAppRobotConfigParams(cmd *cobra.Command, appID string) (map[string]any, } if cmd.Flags().Changed("disable-ssl-verify") { value, _ := cmd.Flags().GetBool("disable-ssl-verify") - params["disableSSLVerify"] = value + params["disableSslVerify"] = value updates++ } - if values, _ := cmd.Flags().GetStringSlice("skills"); len(values) > 0 { + if values := parseDevAppListFlag(cmd, "skills"); len(values) > 0 { params["skillList"] = values updates++ } @@ -901,7 +1145,7 @@ func devAppRobotConfigParams(cmd *cobra.Command, appID string) (map[string]any, } parsed := map[string]any{} if err := json.Unmarshal([]byte(raw), &parsed); err != nil { - return nil, 0, apperrors.NewValidation(fmt.Sprintf("--%s must be valid JSON object: %v", item.flag, err)) + return nil, 0, apperrors.NewValidation(fmt.Sprintf("--%s 必须是合法 JSON 对象:%v", item.flag, err)) } params[item.key] = parsed updates++ @@ -917,7 +1161,7 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "基于当前配置创建应用新版本", - Example: " dws devapp version create --unified-app-id UNIFIED_APP_ID --version 1.0.1 --desc \"新增机器人能力\" --dry-run --format json", + Example: " dws dev app version create --unified-app-id UNIFIED_APP_ID --version 1.0.1 --desc \"新增机器人能力\" --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -928,9 +1172,12 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - params := map[string]any{"unifiedAppId": appID} - devAppPutString(params, "version", devAppStringFlag(cmd, "version")) - devAppPutString(params, "description", devAppStringFlag(cmd, "desc")) + version := devAppStringFlag(cmd, "version") + if version == "" { + return apperrors.NewValidation("--version 为必填") + } + params := map[string]any{"unifiedAppId": appID, "version": version} + devAppPutString(params, "desc", devAppStringFlag(cmd, "desc")) return runDevAppTool(runner, cmd, devAppVersionCreateTool, params) }, } @@ -945,7 +1192,7 @@ func newDevAppVersionListCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "分页查询应用版本列表", - Example: " dws devapp version list --unified-app-id UNIFIED_APP_ID --page 1 --page-size 20 --format json", + Example: " dws dev app version list --unified-app-id UNIFIED_APP_ID --page-size 20 --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -953,17 +1200,13 @@ func newDevAppVersionListCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - params := map[string]any{ - "unifiedAppId": appID, - "currentPage": devAppIntFlag(cmd, "page"), - "pageSize": devAppIntFlag(cmd, "page-size"), - } + params := map[string]any{"unifiedAppId": appID} + devAppApplyCursorParams(cmd, params) return runDevAppTool(runner, cmd, devAppVersionListTool, params) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().Int("page", 1, "分页页码,从 1 开始") - cmd.Flags().Int("page-size", 20, "分页大小") + registerDevAppCursorFlags(cmd) preferLegacyLeaf(cmd) return cmd } @@ -972,7 +1215,7 @@ func newDevAppVersionGetCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "查询指定版本详情", - Example: " dws devapp version get --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --format json", + Example: " dws dev app version get --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -992,7 +1235,7 @@ func newDevAppVersionCheckApprovalCommand(runner executor.Runner) *cobra.Command cmd := &cobra.Command{ Use: "check-approval", Short: "预检版本发布是否需要审批(不实际发布)", - Example: " dws devapp version check-approval --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --format json", + Example: " dws dev app version check-approval --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -1000,8 +1243,10 @@ func newDevAppVersionCheckApprovalCommand(runner executor.Runner) *cobra.Command if err != nil { return err } - // 复用 publish 工具的服务端预检模式:dryRun=true 只返回审批要求,不发布。 - params["dryRun"] = true + // 复用 publish 工具的服务端预检模式:precheckOnly=true 只返回审批 + // 要求,不发布。原上游参数名 dryRun 与 CLI --dry-run 撞名,已改名 + // precheckOnly(见 docs/upstream-todo.md,需上游同步改)。 + params["precheckOnly"] = true return runDevAppTool(runner, cmd, devAppVersionPublishTool, params) }, } @@ -1014,7 +1259,7 @@ func newDevAppVersionPublishCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "publish", Short: "发布指定版本(含高敏权限需 --confirm-sensitive)", - Example: " dws devapp version publish --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --dry-run --format json", + Example: " dws dev app version publish --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -1025,7 +1270,11 @@ func newDevAppVersionPublishCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - params["dryRun"] = false + // precheckOnly=false 表示真发布。命名故意避开 CLI 的 --dry-run: + // 上游接口原参数名是 dryRun,与 CLI 全局 --dry-run 撞名,会让读 + // params 的 agent 误判(publish --dry-run 时回显 dryRun:false)。 + // 见 docs/upstream-todo.md:上游需把入参 dryRun 改名 precheckOnly。 + params["precheckOnly"] = false if cmd.Flags().Changed("confirm-sensitive") { value, _ := cmd.Flags().GetBool("confirm-sensitive") params["confirmedSensitive"] = value @@ -1045,7 +1294,7 @@ func newDevAppVersionStatusCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "status", Short: "查询版本发布/审批状态", - Example: " dws devapp version status --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --format json", + Example: " dws dev app version status --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -1073,27 +1322,28 @@ func devAppVersionLocator(cmd *cobra.Command) (map[string]any, error) { } versionID := devAppStringFlag(cmd, "version-id") if versionID == "" { - return nil, apperrors.NewValidation("--version-id is required") + return nil, apperrors.NewValidation("--version-id 为必填") } return map[string]any{"unifiedAppId": appID, "versionId": versionID}, nil } +// addDevAppUnifiedIDFlag registers the canonical app locator. --unified-app-id +// is the single app identifier across the whole dev app tree (agent-id/app-id/ +// custom-key locators were intentionally removed). func addDevAppUnifiedIDFlag(cmd *cobra.Command) { - cmd.Flags().String("unified-app-id", "", "统一应用 ID (必填)") - cmd.Flags().String("app-id", "", "--unified-app-id 的兼容别名") - _ = cmd.Flags().MarkHidden("app-id") + cmd.Flags().String("unified-app-id", "", "开放平台统一应用 ID(必填)") } func requiredDevAppUnifiedID(cmd *cobra.Command) (string, error) { - appID := devAppFlagOrFallback(cmd, "unified-app-id", "app-id") + appID := devAppStringFlag(cmd, "unified-app-id") if appID == "" { - return "", apperrors.NewValidation("--unified-app-id is required") + return "", apperrors.NewValidation("--unified-app-id 为必填") } return appID, nil } func registerDevAppMemberMutationFlags(cmd *cobra.Command) { - cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") + addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("users", "", "成员 userId 列表,多个用逗号分隔 (必填)") cmd.Flags().String("member-type", "", "成员类型,如 DEVELOPER (必填)") } @@ -1102,7 +1352,7 @@ func runDevAppMemberMutation(runner executor.Runner, cmd *cobra.Command, tool, o if err := devAppRequireWriteGuard(cmd, operation); err != nil { return err } - appID, err := requiredDevAppID(cmd) + appID, err := requiredDevAppUnifiedID(cmd) if err != nil { return err } @@ -1135,26 +1385,20 @@ func runDevAppTool(runner executor.Runner, cmd *cobra.Command, tool string, para if err != nil { return err } - return writeCommandPayload(cmd, result) -} - -func requiredDevAppID(cmd *cobra.Command) (string, error) { - appID, _ := cmd.Flags().GetString("app-id") - appID = strings.TrimSpace(appID) - if appID == "" { - return "", apperrors.NewValidation("--app-id is required") + if devAppPrettyWanted(cmd) { + devAppPrettyAnnotate(tool, result.Response) } - return appID, nil + return writeCommandPayload(cmd, result) } func requiredDevAppUsers(cmd *cobra.Command) ([]string, error) { usersRaw, _ := cmd.Flags().GetString("users") if strings.TrimSpace(usersRaw) == "" { - return nil, apperrors.NewValidation("--users is required") + return nil, apperrors.NewValidation("--users 为必填") } users := splitDevAppList(usersRaw) if len(users) == 0 { - return nil, apperrors.NewValidation("--users must contain at least one userId") + return nil, apperrors.NewValidation("--users 至少包含一个 userId") } return users, nil } @@ -1163,7 +1407,7 @@ func requiredDevAppMemberType(cmd *cobra.Command) (string, error) { memberType, _ := cmd.Flags().GetString("member-type") memberType = strings.TrimSpace(memberType) if memberType == "" { - return "", apperrors.NewValidation("--member-type is required") + return "", apperrors.NewValidation("--member-type 为必填") } return memberType, nil } @@ -1189,57 +1433,15 @@ func splitDevAppList(raw string) []string { return values } -func addDevAppLocatorFlags(cmd *cobra.Command) { - addDevAppLocatorFlagsWithoutName(cmd) - cmd.Flags().String("name", "", "应用名称关键词;写操作前必须唯一命中") -} - -func addDevAppLocatorFlagsWithoutName(cmd *cobra.Command) { - addDevAppAppLocatorFlags(cmd) - cmd.Flags().Int("app-id", 0, "兼容 appId") - cmd.Flags().String("app-key", "", "appKey/clientId") - cmd.Flags().String("custom-key", "", "customKey") -} - -func addDevAppAppLocatorFlags(cmd *cobra.Command) { - cmd.Flags().String("unified-app-id", "", "统一应用 ID") - cmd.Flags().Int("agent-id", 0, "应用 agentId") -} - -func devAppLocatorParams(cmd *cobra.Command, includeName bool) map[string]any { - params := devAppAppLocatorParams(cmd) - devAppPutInt(params, "appId", devAppIntFlag(cmd, "app-id")) - devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) - devAppPutString(params, "customKey", devAppStringFlag(cmd, "custom-key")) - if includeName { - devAppPutString(params, "appName", devAppStringFlag(cmd, "name")) - } - return params -} - -func devAppAppLocatorParams(cmd *cobra.Command) map[string]any { - params := map[string]any{} - devAppPutString(params, "unifiedAppId", devAppStringFlag(cmd, "unified-app-id")) - devAppPutInt(params, "agentId", devAppIntFlag(cmd, "agent-id")) - return params -} - -func devAppLocatorRequired(includeName bool) error { - if includeName { - return apperrors.NewValidation("one app locator is required: --agent-id, --unified-app-id, --app-key, --custom-key, --app-id, or --name") - } - return apperrors.NewValidation("one app locator is required: --agent-id, --unified-app-id, --app-key, --custom-key, or --app-id") -} - -func devAppAppLocatorRequired() error { - return apperrors.NewValidation("one app locator is required: --agent-id or --unified-app-id") -} +// 应用定位统一只用 --unified-app-id(--app-key / --name 定位已下线,列表搜索的 +// --name/--app-key 是过滤参数、不在此列)。所有 app 作用域命令共用 addDevAppUnifiedIDFlag +// + requiredDevAppUnifiedID。 func devAppRequireWriteGuard(cmd *cobra.Command, operation string) error { if commandDryRun(cmd) || devAppYes(cmd) { return nil } - return apperrors.NewValidation(fmt.Sprintf("%s is a write operation; rerun with --dry-run to preview or --yes after confirmation", operation)) + return apperrors.NewValidation(fmt.Sprintf("%s 是写操作;加 --dry-run 预览,或确认后加 --yes 执行", operation)) } func devAppYes(cmd *cobra.Command) bool { @@ -1254,8 +1456,16 @@ func devAppYes(cmd *cobra.Command) bool { return false } +func devAppCloneParams(src map[string]any) map[string]any { + out := make(map[string]any, len(src)+1) + for k, v := range src { + out[k] = v + } + return out +} + func devAppPermissionScopes(cmd *cobra.Command) []string { - values, _ := cmd.Flags().GetStringSlice("permissions") + values := parseDevAppListFlag(cmd, "permissions") values = append(values, devAppFlagOrFallback(cmd, "scope", "permission")) out := make([]string, 0, len(values)) for _, value := range values { diff --git a/internal/helpers/devapp_connect.go b/internal/helpers/devapp_connect.go index bc5a00a8..ad02e982 100644 --- a/internal/helpers/devapp_connect.go +++ b/internal/helpers/devapp_connect.go @@ -26,8 +26,8 @@ import ( ) // devapp_connect wires the channel-aware "建联" (linking) capability into the -// devapp command tree as `dws devapp robot connect`. Provisioning (建号) is NOT -// duplicated here — it reuses devapp's existing `robot create/submit/result`. +// dev command tree as `dws dev connect`. Provisioning (建号) is NOT +// duplicated here — it reuses the existing `dev app robot create/submit/result`. // This file holds only the linking half: detect the current agent channel, then // start a Go-native in-process Stream that forwards @-bot messages to the local // agent CLI. The Stream forwarder internals live in connect_stream.go. @@ -235,23 +235,34 @@ func officialChannelGuidance(channel string) string { return "" } -// newDevAppRobotConnectCommand implements `dws devapp robot connect`: the linking +// connectPreviewEnvelope wraps a connect dry-run preview in an envelope that +// mirrors the app-tree helper_invocation shape (kind + dry_run at a known top +// level), so an agent can parse "is this a dry-run preview" the same way across +// all dev commands. The connect-specific fields (channel/cli/connect/...) sit +// inside, since connect is a linking pre-check, not an MCP tool call. +func connectPreviewEnvelope(fields map[string]any) map[string]any { + fields["kind"] = "connect_preview" + fields["dry_run"] = true + return map[string]any{"invocation": fields} +} + +// newDevAppRobotConnectCommand implements `dws dev connect`: the linking // half of provisioning. It takes an existing robot's credentials — either // directly via --client-id/--client-secret, or by --unified-app-id (reusing -// devapp's `get_open_dev_app_credentials` to fetch them) — and starts the +// devapp 的 `get_dev_app_credentials` to fetch them) — and starts the // channel-aware Stream connector in the foreground. It never provisions a robot; -// for 建号 run `dws devapp robot create` (or submit/result) first. +// for 建号 run `dws dev app robot create` (or submit/result) first. func newDevAppRobotConnectCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "connect", Short: "建联:把现成机器人接到当前本地 agent(起 Stream,不建号)", Long: "用已建好的机器人凭证把它接到当前本地 agent,不做建号。\n" + - "凭证两种来源:① 直接传 --robot-client-id/--robot-client-secret;② 传 --unified-app-id(复用 devapp credentials get 自动取凭证)。\n" + + "凭证两种来源:① 直接传 --robot-client-id/--robot-client-secret;② 传 --unified-app-id(复用 dev app credentials get 自动取凭证)。\n" + "渠道由 --channel 显式指定,或运行时信号自动探测。\n" + - "缺凭证请先用 `dws devapp robot create` 建号拿 clientId/clientSecret。", - Example: " dws devapp robot connect --channel workbuddy --robot-client-id --robot-client-secret \n" + - " dws devapp robot connect --unified-app-id --channel qoderwork\n" + - " dws devapp robot connect --channel claudecode --robot-client-id --robot-client-secret ", + "缺凭证请先用 `dws dev app robot create` 建号拿 clientId/clientSecret。", + Example: " dws dev connect --channel workbuddy --robot-client-id --robot-client-secret \n" + + " dws dev connect --unified-app-id --channel qoderwork\n" + + " dws dev connect --channel claudecode --robot-client-id --robot-client-secret ", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -269,25 +280,24 @@ func newDevAppRobotConnectCommand(runner executor.Runner) *cobra.Command { unifiedAppID := devAppStringFlag(cmd, "unified-app-id") opts := connectAgentOptionsFromCommand(cmd) - // Credential resolution: explicit pair wins; otherwise reuse devapp's + // Credential resolution: explicit pair wins; otherwise reuse dev app's // credentials get against --unified-app-id. resolvedBy := "flag:--robot-client-id/--robot-client-secret" if clientID == "" || clientSecret == "" { if unifiedAppID == "" { - return apperrors.NewValidation("需要 --robot-client-id/--robot-client-secret(用现成机器人凭证),或 --unified-app-id(复用 devapp credentials get 自动取凭证)") + return apperrors.NewValidation("需要 --robot-client-id/--robot-client-secret(用现成机器人凭证),或 --unified-app-id(复用 dev app credentials get 自动取凭证)") } if commandDryRun(cmd) { // Dry-run must not call the credentials tool; just preview routing. - return writeCommandPayload(cmd, map[string]any{ + return writeCommandPayload(cmd, connectPreviewEnvelope(map[string]any{ "channel": channel, "detectedBy": detectedBy, - "dryRun": true, "credentialSource": "unified-app-id (credentials get, skipped in dry-run)", "unifiedAppId": unifiedAppID, "agent": connectAgentOptionsPayload(channel, opts), "cli": connectCliStatus(channel), "connect": buildConnectPlan(channel, "", ""), - }) + })) } id, secret, err := devAppFetchCredentials(runner, cmd, unifiedAppID) if err != nil { @@ -301,16 +311,15 @@ func newDevAppRobotConnectCommand(runner executor.Runner) *cobra.Command { } if commandDryRun(cmd) { - return writeCommandPayload(cmd, map[string]any{ + return writeCommandPayload(cmd, connectPreviewEnvelope(map[string]any{ "channel": channel, "detectedBy": detectedBy, - "dryRun": true, "credentialSource": resolvedBy, "clientId": clientID, "agent": connectAgentOptionsPayload(channel, opts), "cli": connectCliStatus(channel), "connect": buildConnectPlan(channel, clientID, ""), - }) + })) } fmt.Fprintf(cmd.ErrOrStderr(), "[connect] channel=%s(%s)凭证来源=%s\n", channel, detectedBy, resolvedBy) @@ -324,7 +333,7 @@ func newDevAppRobotConnectCommand(runner executor.Runner) *cobra.Command { // 目标机器人凭证,与 OAuth 客户端是两码事,故独立命名避免撞名。 cmd.Flags().String("robot-client-id", "", "现成机器人 clientId(AppKey)") cmd.Flags().String("robot-client-secret", "", "现成机器人 clientSecret(AppSecret)") - cmd.Flags().String("unified-app-id", "", "统一应用 ID:复用 devapp credentials get 自动取凭证(替代手填 robot-client-id/secret)") + cmd.Flags().String("unified-app-id", "", "统一应用 ID:复用 dev app credentials get 自动取凭证(替代手填 robot-client-id/secret)") cmd.Flags().String("agent-model", "", "覆盖本地 agent 模型(如 claude 的 sonnet/opus;默认用渠道内置模型,求快);env: DWS_AGENT_MODEL") cmd.Flags().String("agent-workdir", "", "本地 agent 的运行目录(放知识文件可给机器人上下文;默认空白临时目录,求快);env: DWS_AGENT_WORKDIR") cmd.Flags().Bool("agent-memory", true, "按会话续聊:同一群/单聊共享 agent 会话上下文(claudecode/codebuddy/workbuddy 支持;--agent-memory=false 关闭)") @@ -398,14 +407,14 @@ func connectAgentOptionsPayload(channel string, opts connectAgentOptions) map[st return map[string]any{"model": model, "workdir": workDir, "memory": memory, "replyStyle": replyStyle} } -// devAppFetchCredentials reuses devapp's get_open_dev_app_credentials tool to +// devAppFetchCredentials reuses dev app 的 get_dev_app_credentials tool to // resolve a unified app's clientId/clientSecret, so `robot connect // --unified-app-id` need not have the caller paste raw credentials. Note the // open platform only returns clientSecret once (at provisioning); if the tool // omits it the caller is told to fall back to explicit flags. // // TODO(verify): the clientId/clientSecret (and appKey/appSecret fallback) field -// names below are NOT yet confirmed against the real get_open_dev_app_credentials +// names below are NOT yet confirmed against the real get_dev_app_credentials // response — no fixture exists in-repo. Verify against the pre-prod gateway and // pin the exact field names before relying on --unified-app-id auto-fetch. The // path degrades safely today: an unrecognised shape yields empty strings and the @@ -414,7 +423,7 @@ func devAppFetchCredentials(runner executor.Runner, cmd *cobra.Command, unifiedA invocation := executor.NewHelperInvocation( cobracmd.LegacyCommandPath(cmd), devAppProduct, - "get_open_dev_app_credentials", + "get_dev_app_credentials", map[string]any{"unifiedAppId": unifiedAppID}, ) res, err := runner.Run(cmd.Context(), invocation) diff --git a/internal/helpers/devapp_cursor.go b/internal/helpers/devapp_cursor.go new file mode 100644 index 00000000..6117067a --- /dev/null +++ b/internal/helpers/devapp_cursor.go @@ -0,0 +1,45 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package helpers + +import "github.com/spf13/cobra" + +// devapp_cursor wires cursor pagination as a PASS-THROUGH: the CLI forwards +// --cursor and --page-size straight into the upstream params, and the upstream +// returns nextCursor/hasMore in its response. No synthesis, no offset/page +// translation, no encode/decode in the CLI — the upstream tools own the cursor +// contract (see docs/cursor-pagination-design.md and docs/upstream-todo.md). +// Until the upstream tools accept `cursor`/`pageSize` and return `nextCursor`, +// paging is in the "pending integration" state, same as the precheckOnly +// rename — no anti-corruption layer bridges the gap. + +// registerDevAppCursorFlags adds the two cursor flags every list/search command +// exposes. pageSize defaults to 20. +func registerDevAppCursorFlags(cmd *cobra.Command) { + cmd.Flags().String("cursor", "", "游标令牌:首次查询留空,续翻传上次出参的 nextCursor") + cmd.Flags().Int("page-size", 20, "单页条数,默认 20") +} + +// devAppApplyCursorParams forwards cursor/pageSize into params as-is. cursor is +// only set when non-empty (first page omits it). +func devAppApplyCursorParams(cmd *cobra.Command, params map[string]any) { + if cur := devAppStringFlag(cmd, "cursor"); cur != "" { + params["cursor"] = cur + } + size := devAppIntFlag(cmd, "page-size") + if size < 1 { + size = 20 + } + params["pageSize"] = size +} diff --git a/internal/helpers/devapp_pretty.go b/internal/helpers/devapp_pretty.go new file mode 100644 index 00000000..71f05928 --- /dev/null +++ b/internal/helpers/devapp_pretty.go @@ -0,0 +1,126 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package helpers + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" +) + +// devapp_pretty annotates --format pretty output with human-readable status +// labels. Upstream payloads use a numeric enum for app status and the bare +// INIT/AUDIT/... enum for version status, and the same key `status` means +// different things on different tools — fine for agents reading JSON against +// the skill's enum tables, hostile to humans. JSON output is NEVER touched +// (transparency contract); only the pretty view gets `*Text` companions. + +var devAppStatusLabels = map[int]string{ + 0: "IN_ACTIVE 已停用", + 1: "ACTIVE 已激活", + 2: "WAIT_ACTIVE 待激活", + 3: "EXPIRED 已过期", +} + +var devAppVersionStatusLabels = map[string]string{ + "INIT": "未发布(有待发布变更)", + "AUDIT": "发布审核中", + "RELEASE": "已发布生效", + "GRAY": "灰度中", +} + +// devAppToolStatusKind classifies which enum family a tool's `status`-like +// fields belong to, so app status 1 (ACTIVE) is never labeled with version +// semantics and vice versa. +var devAppToolStatusKind = map[string]string{ + "get_dev_app": "app", + "list_dev_app": "app", + "create_dev_app": "version", // create/update 返回 versionStatus + "update_dev_app": "version", + "create_dev_app_version": "version", + "list_dev_app_version": "version", + "get_dev_app_version": "version", + "get_dev_app_version_status": "version", + "publish_dev_app_version": "version", +} + +func devAppPrettyWanted(cmd *cobra.Command) bool { + format, err := cmd.Flags().GetString("format") + return err == nil && format == "pretty" +} + +// devAppPrettyAnnotate walks the response and adds appStatusText / +// versionStatusText next to recognized enum fields, in place. +func devAppPrettyAnnotate(tool string, payload any) { + kind, ok := devAppToolStatusKind[tool] + if !ok { + return + } + devAppAnnotateNode(payload, kind) +} + +func devAppAnnotateNode(node any, kind string) { + switch v := node.(type) { + case map[string]any: + devAppAnnotateMap(v, kind) + for _, child := range v { + devAppAnnotateNode(child, kind) + } + case []any: + for _, child := range v { + devAppAnnotateNode(child, kind) + } + } +} + +func devAppAnnotateMap(m map[string]any, kind string) { + if kind == "app" { + for _, key := range []string{"status", "appStatus"} { + if n, ok := devAppAsInt(m[key]); ok { + if label, ok := devAppStatusLabels[n]; ok { + m[key+"Text"] = label + } + } + } + return + } + for _, key := range []string{"status", "versionStatus"} { + if s, ok := m[key].(string); ok { + if label, ok := devAppVersionStatusLabels[s]; ok { + m[key+"Text"] = label + } + } + } +} + +func devAppAsInt(v any) (int, bool) { + switch n := v.(type) { + case int: + return n, true + case int64: + return int(n), true + case float64: + return int(n), true + case json.Number: + i, err := n.Int64() + return int(i), err == nil + case string: + var i int + if _, err := fmt.Sscanf(n, "%d", &i); err == nil { + return i, true + } + } + return 0, false +} diff --git a/internal/helpers/devdoc.go b/internal/helpers/devdoc.go index f8495532..11053da7 100644 --- a/internal/helpers/devdoc.go +++ b/internal/helpers/devdoc.go @@ -74,26 +74,16 @@ func newDevdocArticleSearchCommand(runner executor.Runner) *cobra.Command { if keyword == "" { return apperrors.NewValidation("--query is required") } - page, _ := cmd.Flags().GetInt("page") - if page < 1 { - page = 1 - } - size, _ := cmd.Flags().GetInt("size") - if size < 1 { - size = 10 - } - return runDevdocTool(cmd, runner, "search_open_platform_docs", map[string]any{ - "keyword": keyword, - "page": page, - "size": size, - }) + params := map[string]any{"keyword": keyword} + devAppApplyCursorParams(cmd, params) + return runDevdocTool(cmd, runner, "search_open_platform_docs", params) }, } preferLegacyLeaf(cmd) cmd.Flags().String("query", "", "搜索关键词 (必填)") addDevdocHiddenStringFlag(cmd, "keyword", "--query 的悟空兼容别名") - cmd.Flags().Int("page", 1, "分页页码 (从 1 开始,默认 1)") - cmd.Flags().Int("size", 10, "分页大小 (默认 10)") + cmd.Flags().String("cursor", "", "游标令牌:首次查询留空,续翻传上次返回的 nextCursor") + cmd.Flags().Int("page-size", 20, "单页条数,默认 20") return cmd } From 63b6112fa2b40b16777fe5155f0c8030720b9c41 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:09:18 +0800 Subject: [PATCH 15/47] =?UTF-8?q?test(dev):=20dev=20=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E9=9B=86=E6=B5=8B=E8=AF=95=20+=20=E5=BB=BA=E8=81=94/=E7=AB=AF?= =?UTF-8?q?=E7=82=B9=E8=B7=AF=E7=94=B1=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit helpers 单测、connect/card/stream 测试,app runner 与 direct_runtime devapp 端点路由。 Co-Authored-By: Claude Opus 4.8 (1M context) --- internal/app/direct_runtime_test.go | 12 +- internal/app/runner.go | 2 +- .../helpers/connect_agent_options_test.go | 6 +- internal/helpers/connect_card_test.go | 2 +- internal/helpers/connect_stream.go | 2 +- internal/helpers/devapp_pretty_test.go | 51 +++ internal/helpers/devapp_robot_connect_test.go | 14 +- internal/helpers/devapp_test.go | 322 ++++++++++-------- internal/helpers/devdoc_test.go | 10 +- 9 files changed, 261 insertions(+), 160 deletions(-) create mode 100644 internal/helpers/devapp_pretty_test.go diff --git a/internal/app/direct_runtime_test.go b/internal/app/direct_runtime_test.go index 8363143a..fd0b3fe4 100644 --- a/internal/app/direct_runtime_test.go +++ b/internal/app/direct_runtime_test.go @@ -52,14 +52,14 @@ func TestDirectRuntimeEndpoint_DevappEnvOverrideWithoutRegistry(t *testing.T) { withCleanDynamicRegistry(t) t.Setenv("DINGTALK_DEVAPP_MCP_URL", "https://example.test/server/devapp") - assertEndpoint(t, "devapp", "list_open_dev_apps_by_condition", "https://example.test/server/devapp") + assertEndpoint(t, "devapp", "list_dev_app", "https://example.test/server/devapp") } func TestDirectRuntimeEndpoint_DevappEnvOverridePreservesQuery(t *testing.T) { withCleanDynamicRegistry(t) t.Setenv("DINGTALK_DEVAPP_MCP_URL", "https://example.test/server/devapp?key=secret") - assertEndpoint(t, "devapp", "list_open_dev_apps_by_condition", "https://example.test/server/devapp?key=secret") + assertEndpoint(t, "devapp", "list_dev_app", "https://example.test/server/devapp?key=secret") } func TestDirectRuntimeEndpoint_DevappDynamicServerDoesNotOverrideHardcoded(t *testing.T) { @@ -74,7 +74,7 @@ func TestDirectRuntimeEndpoint_DevappDynamicServerDoesNotOverrideHardcoded(t *te }, }) - assertEndpoint(t, "devapp", "list_open_dev_apps_by_condition", devappEndpoint) + assertEndpoint(t, "devapp", "list_dev_app", devappEndpoint) } func TestDirectRuntimeEndpoint_DevappEditionSupplementDoesNotOverrideHardcoded(t *testing.T) { @@ -95,7 +95,7 @@ func TestDirectRuntimeEndpoint_DevappEditionSupplementDoesNotOverrideHardcoded(t }) t.Cleanup(func() { edition.Override(prev) }) - assertEndpoint(t, "devapp", "list_open_dev_apps_by_condition", devappEndpoint) + assertEndpoint(t, "devapp", "list_dev_app", devappEndpoint) } func TestDirectRuntimeEndpoint_DevappEditionStaticDoesNotOverrideHardcoded(t *testing.T) { @@ -116,7 +116,7 @@ func TestDirectRuntimeEndpoint_DevappEditionStaticDoesNotOverrideHardcoded(t *te }) t.Cleanup(func() { edition.Override(prev) }) - assertEndpoint(t, "devapp", "list_open_dev_apps_by_condition", devappEndpoint) + assertEndpoint(t, "devapp", "list_dev_app", devappEndpoint) } func TestDirectRuntimeEndpoint_DevappEnvOverrideWinsOverEditionSupplement(t *testing.T) { @@ -137,7 +137,7 @@ func TestDirectRuntimeEndpoint_DevappEnvOverrideWinsOverEditionSupplement(t *tes }) t.Cleanup(func() { edition.Override(prev) }) - assertEndpoint(t, "devapp", "list_open_dev_apps_by_condition", "https://example.test/server/devapp-env") + assertEndpoint(t, "devapp", "list_dev_app", "https://example.test/server/devapp-env") } func TestDirectRuntimeEndpoint_DefaultPATFallbackWhenRegistryMissing(t *testing.T) { diff --git a/internal/app/runner.go b/internal/app/runner.go index a15901c6..119f0c7c 100644 --- a/internal/app/runner.go +++ b/internal/app/runner.go @@ -262,7 +262,7 @@ func (r *runtimeRunner) handleCatalogMiss(ctx context.Context, invocation execut hint := "产品 envelope 可能未下发到 discovery,或已经被 serverDeps fail-fast 丢弃;可执行 'dws cache refresh' 强制重新 discovery,仍失败请向 Portal 确认 envelope 状态。" actions := []string{"dws cache refresh"} if strings.TrimSpace(invocation.CanonicalProduct) == devappProductID { - hint = "devapp 是 helper-only 产品,命令树不依赖 discovery;真实调用需要内部版通过 SupplementServers/StaticServers 注入 MCP endpoint,或本地调试临时设置 DINGTALK_DEVAPP_MCP_URL。" + hint = "dev app(product id: devapp)是 helper-only 产品,命令树不依赖 discovery;真实调用需要内部版通过 SupplementServers/StaticServers 注入 MCP endpoint,或本地调试临时设置 DINGTALK_DEVAPP_MCP_URL。" actions = []string{ "检查内部版 SupplementServers/StaticServers 是否包含 devapp endpoint", "本地调试可临时设置 DINGTALK_DEVAPP_MCP_URL 后重试", diff --git a/internal/helpers/connect_agent_options_test.go b/internal/helpers/connect_agent_options_test.go index a8ada37a..88162747 100644 --- a/internal/helpers/connect_agent_options_test.go +++ b/internal/helpers/connect_agent_options_test.go @@ -132,7 +132,7 @@ func TestRobotConnectAgentFlagsInDryRun(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "robot", "connect", + root.SetArgs([]string{"dev", "connect", "--channel", "claudecode", "--robot-client-id", "id1", "--robot-client-secret", "sec1", "--agent-model", "claude-sonnet-4-6", "--agent-workdir", "/tmp/kb", @@ -151,7 +151,7 @@ func TestRobotConnectAgentFlagsInDryRun(t *testing.T) { root = newDevAppTestRoot(&captureRunner{}) root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "robot", "connect", + root.SetArgs([]string{"dev", "connect", "--channel", "claudecode", "--robot-client-id", "id1", "--robot-client-secret", "sec1", "--agent-memory=false", "--dry-run"}) @@ -179,7 +179,7 @@ func TestRobotConnectDryRunShowsCliStatus(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "robot", "connect", "--channel", channel, + root.SetArgs([]string{"dev", "connect", "--channel", channel, "--robot-client-id", "a", "--robot-client-secret", "b", "--dry-run"}) if err := root.Execute(); err != nil { t.Fatalf("Execute(%s): %v\n%s", channel, err, out.String()) diff --git a/internal/helpers/connect_card_test.go b/internal/helpers/connect_card_test.go index 03f1f96b..c8671034 100644 --- a/internal/helpers/connect_card_test.go +++ b/internal/helpers/connect_card_test.go @@ -230,7 +230,7 @@ func TestRobotConnectReplyCardFlag(t *testing.T) { var out strings.Builder root.SetOut(&out) root.SetErr(&out) - args := append([]string{"devapp", "robot", "connect", + args := append([]string{"dev", "connect", "--channel", "claudecode", "--robot-client-id", "a", "--robot-client-secret", "b", "--dry-run"}, tc.args...) root.SetArgs(args) diff --git a/internal/helpers/connect_stream.go b/internal/helpers/connect_stream.go index e73cddd5..960322bb 100644 --- a/internal/helpers/connect_stream.go +++ b/internal/helpers/connect_stream.go @@ -66,7 +66,7 @@ type forwarder interface { } // connectAgentOptions carries the user-facing agent tuning exposed on -// `devapp robot connect` (and mirrored env vars). Zero value = defaults: +// `dev connect` (and mirrored env vars). Zero value = defaults: // channel's built-in model, per-conversation memory on (where the CLI supports // it), empty scratch workdir. type connectAgentOptions struct { diff --git a/internal/helpers/devapp_pretty_test.go b/internal/helpers/devapp_pretty_test.go new file mode 100644 index 00000000..c236cc81 --- /dev/null +++ b/internal/helpers/devapp_pretty_test.go @@ -0,0 +1,51 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package helpers + +import "testing" + +func TestDevAppPrettyAnnotateAppStatus(t *testing.T) { + payload := map[string]any{ + "result": map[string]any{ + "items": []any{ + map[string]any{"status": float64(1)}, + map[string]any{"status": float64(0)}, + }, + }, + } + devAppPrettyAnnotate("list_dev_app", payload) + items := payload["result"].(map[string]any)["items"].([]any) + if got := items[0].(map[string]any)["statusText"]; got != "ACTIVE 已激活" { + t.Fatalf("statusText = %v, want ACTIVE 已激活", got) + } + if got := items[1].(map[string]any)["statusText"]; got != "IN_ACTIVE 已停用" { + t.Fatalf("statusText = %v, want IN_ACTIVE 已停用", got) + } +} + +func TestDevAppPrettyAnnotateVersionStatusKindSeparation(t *testing.T) { + // 版本工具的字符串枚举要注解 + version := map[string]any{"status": "INIT", "versionStatus": "RELEASE"} + devAppPrettyAnnotate("get_dev_app_version", version) + if version["statusText"] != "未发布(有待发布变更)" || version["versionStatusText"] != "已发布生效" { + t.Fatalf("version annotations wrong: %#v", version) + } + + // 应用工具的数字枚举不能套用版本语义;未登记的工具不动 + unknown := map[string]any{"status": float64(1)} + devAppPrettyAnnotate("some_unknown_tool", unknown) + if _, exists := unknown["statusText"]; exists { + t.Fatalf("unknown tool should not be annotated: %#v", unknown) + } +} diff --git a/internal/helpers/devapp_robot_connect_test.go b/internal/helpers/devapp_robot_connect_test.go index 90b2b752..d0508625 100644 --- a/internal/helpers/devapp_robot_connect_test.go +++ b/internal/helpers/devapp_robot_connect_test.go @@ -20,12 +20,12 @@ import ( "testing" ) -// TestDevAppRobotConnectRegistered confirms `devapp robot connect` is wired into +// TestDevAppRobotConnectRegistered confirms `dev connect` is wired into // the robot subtree alongside the (reused) provisioning commands. func TestDevAppRobotConnectRegistered(t *testing.T) { root := newDevAppCommand(&captureRunner{}) if _, _, err := root.Find([]string{"robot", "connect"}); err != nil { - t.Fatalf("devapp robot connect not registered: %v", err) + t.Fatalf("dev connect not registered: %v", err) } // Provisioning stays under robot (reused, not duplicated by connect). for _, name := range []string{"create", "submit", "result"} { @@ -72,7 +72,7 @@ func TestDevAppRobotConnectValidation(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp", "robot", "connect"}, tc.args...)) + root.SetArgs(append([]string{"dev", "connect"}, tc.args...)) err := root.Execute() if tc.wantErr != "" { @@ -94,7 +94,7 @@ func TestDevAppRobotConnectValidation(t *testing.T) { } // TestDevAppFetchCredentials checks that --unified-app-id credential resolution -// reuses get_open_dev_app_credentials and extracts clientId/clientSecret from the +// reuses get_dev_app_credentials and extracts clientId/clientSecret from the // MCP envelope. func TestDevAppFetchCredentials(t *testing.T) { runner := &devAppResponseRunner{response: map[string]any{ @@ -106,7 +106,7 @@ func TestDevAppFetchCredentials(t *testing.T) { }, }} root := newDevAppTestRoot(runner) - connectCmd, _, err := root.Find([]string{"devapp", "robot", "connect"}) + connectCmd, _, err := root.Find([]string{"dev", "connect"}) if err != nil { t.Fatalf("find connect: %v", err) } @@ -119,8 +119,8 @@ func TestDevAppFetchCredentials(t *testing.T) { if id != "ck-123" || secret != "cs-456" { t.Fatalf("got id=%q secret=%q, want ck-123/cs-456", id, secret) } - if runner.last.Tool != "get_open_dev_app_credentials" { - t.Fatalf("Tool = %q, want get_open_dev_app_credentials", runner.last.Tool) + if runner.last.Tool != "get_dev_app_credentials" { + t.Fatalf("Tool = %q, want get_dev_app_credentials", runner.last.Tool) } if runner.last.CanonicalProduct != devAppProduct { t.Fatalf("CanonicalProduct = %q, want %q", runner.last.CanonicalProduct, devAppProduct) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index b672d2e3..030481d4 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -18,7 +18,7 @@ func newDevAppTestRoot(runner executor.Runner) *cobra.Command { } root.PersistentFlags().Bool("dry-run", false, "dry run") root.PersistentFlags().Bool("yes", false, "yes") - root.AddCommand(newDevAppCommand(runner)) + root.AddCommand(devHandler{}.Command(runner)) return root } @@ -44,8 +44,8 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "list", cmd: "list", - args: []string{"--app-id", "app-001"}, - wantTool: "list_open_dev_app_members", + args: []string{"--unified-app-id", "app-001"}, + wantTool: "list_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", }, @@ -53,8 +53,8 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "add multiple users", cmd: "add", - args: []string{"--app-id", "app-001", "--users", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, - wantTool: "add_open_dev_app_members", + args: []string{"--unified-app-id", "app-001", "--users", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, + wantTool: "add_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", "memberUserIds": []string{"userId1", "userId2", "userId3", "userId4"}, @@ -64,8 +64,8 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "remove trims users", cmd: "remove", - args: []string{"--app-id", "app-001", "--users", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, - wantTool: "remove_open_dev_app_members", + args: []string{"--unified-app-id", "app-001", "--users", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, + wantTool: "remove_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", "memberUserIds": []string{"userId1", "userId2"}, @@ -81,7 +81,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp", "member", tc.cmd}, tc.args...)) + root.SetArgs(append([]string{"dev", "app", "member", tc.cmd}, tc.args...)) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -100,23 +100,37 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { } } -func TestDevAppCommandHasAppAliasAndCoreCommands(t *testing.T) { - root := newDevAppCommand(&captureRunner{}) - if root.Name() != "devapp" { - t.Fatalf("Name() = %q, want devapp", root.Name()) +func TestDevCommandTree(t *testing.T) { + dev := devHandler{}.Command(&captureRunner{}) + if dev.Name() != "dev" { + t.Fatalf("Name() = %q, want dev", dev.Name()) } - hasAlias := false - for _, alias := range root.Aliases { - if alias == "app" { - hasAlias = true + if len(dev.Aliases) != 0 { + t.Fatalf("Aliases = %v, want none (clean switch from devapp)", dev.Aliases) + } + // 三支柱:app / connect / doc + for _, name := range []string{"app", "connect", "doc"} { + if _, _, err := dev.Find([]string{name}); err != nil { + t.Fatalf("missing subtree %q: %v", name, err) } } - if !hasAlias { - t.Fatalf("Aliases = %v, want app", root.Aliases) + app, _, err := dev.Find([]string{"app"}) + if err != nil { + t.Fatalf("find app: %v", err) } - for _, name := range []string{"list", "get", "create", "update", "delete", "inactive", "active", "credentials", "webapp", "permission", "member", "security", "robot", "version"} { - if _, _, err := root.Find([]string{name}); err != nil { - t.Fatalf("missing command %q: %v", name, err) + for _, name := range []string{"list", "get", "create", "update", "delete", "disable", "enable", "credentials", "webapp", "permission", "member", "security", "robot", "version", "event"} { + if _, _, err := app.Find([]string{name}); err != nil { + t.Fatalf("missing app command %q: %v", name, err) + } + } + // connect 已从 robot 子树提升为 dev connect,robot 下不应再有 + robot, _, err := app.Find([]string{"robot"}) + if err != nil { + t.Fatalf("find robot: %v", err) + } + for _, sub := range robot.Commands() { + if sub.Name() == "connect" { + t.Fatalf("robot subtree still has connect; it moved to dev connect") } } } @@ -128,16 +142,6 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { wantTool string wantParams map[string]any }{ - { - name: "create sync", - args: []string{"robot", "create", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--yes"}, - wantTool: "create_dingtalk_robot", - wantParams: map[string]any{ - "appName": "智能体", - "robotName": "小助手", - "desc": "审批问答", - }, - }, { name: "submit async fills media placeholders", args: []string{"robot", "submit", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--task-id", "t-1", "--yes"}, @@ -146,7 +150,7 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { "appName": "智能体", "robotName": "小助手", "desc": "审批问答", - "robotMediaId": "", + "icon": "", "previewMediaId": "", "taskId": "t-1", }, @@ -160,13 +164,13 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { { name: "get config", args: []string{"robot", "get", "--unified-app-id", "u-1"}, - wantTool: "get_open_dev_app_robot_config", + wantTool: "get_extension_robot_config", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { name: "config create with skills and mode", args: []string{"robot", "config", "--unified-app-id", "u-1", "--name", "小助手", "--brief", "审批助手", "--mode", "2", "--skills", "qa,approval", "--add-scope", "--yes"}, - wantTool: "create_open_dev_app_robot_config", + wantTool: "set_extension_robot_config", wantParams: map[string]any{ "unifiedAppId": "u-1", "name": "小助手", @@ -177,9 +181,9 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { }, }, { - name: "offline", - args: []string{"robot", "offline", "--unified-app-id", "u-1", "--yes"}, - wantTool: "offline_open_dev_app_robot", + name: "robot disable", + args: []string{"robot", "disable", "--unified-app-id", "u-1", "--yes"}, + wantTool: "disable_dev_app_robot", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, } @@ -191,7 +195,7 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp"}, tc.args...)) + root.SetArgs(append([]string{"dev", "app"}, tc.args...)) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -219,43 +223,43 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { { name: "create", args: []string{"version", "create", "--unified-app-id", "u-1", "--version", "1.0.1", "--desc", "新增机器人", "--yes"}, - wantTool: "create_open_dev_app_version", + wantTool: "create_dev_app_version", wantParams: map[string]any{ "unifiedAppId": "u-1", "version": "1.0.1", - "description": "新增机器人", + "desc": "新增机器人", }, }, { name: "list", - args: []string{"version", "list", "--unified-app-id", "u-1", "--page", "2", "--page-size", "5"}, - wantTool: "list_open_dev_app_versions", + args: []string{"version", "list", "--unified-app-id", "u-1", "--cursor", "tok-1", "--page-size", "5"}, + wantTool: "list_dev_app_versions", wantParams: map[string]any{ "unifiedAppId": "u-1", - "currentPage": 2, + "cursor": "tok-1", "pageSize": 5, }, }, { name: "get detail", args: []string{"version", "get", "--unified-app-id", "u-1", "--version-id", "v-1"}, - wantTool: "get_open_dev_app_version_detail", + wantTool: "get_dev_app_version_detail", wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1"}, }, { - name: "check-approval forces dryRun", + name: "check-approval forces precheckOnly", args: []string{"version", "check-approval", "--unified-app-id", "u-1", "--version-id", "v-1"}, - wantTool: "publish_open_dev_app_version", - wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1", "dryRun": true}, + wantTool: "publish_dev_app_version", + wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1", "precheckOnly": true}, }, { - name: "publish sets dryRun false and sensitive", + name: "publish sets precheckOnly false and sensitive", args: []string{"version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1", "--confirm-sensitive", "--approver", "user-1", "--yes"}, - wantTool: "publish_open_dev_app_version", + wantTool: "publish_dev_app_version", wantParams: map[string]any{ "unifiedAppId": "u-1", "versionId": "v-1", - "dryRun": false, + "precheckOnly": false, "confirmedSensitive": true, "approverUserId": "user-1", }, @@ -263,7 +267,7 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { { name: "status", args: []string{"version", "status", "--unified-app-id", "u-1", "--version-id", "v-1"}, - wantTool: "get_open_dev_app_version_status", + wantTool: "get_dev_app_version_status", wantParams: map[string]any{"unifiedAppId": "u-1", "versionId": "v-1"}, }, } @@ -275,7 +279,7 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp"}, tc.args...)) + root.SetArgs(append([]string{"dev", "app"}, tc.args...)) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -295,11 +299,10 @@ func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { name string args []string }{ - {name: "robot create", args: []string{"devapp", "robot", "create", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批"}}, - {name: "robot config", args: []string{"devapp", "robot", "config", "--unified-app-id", "u-1", "--name", "小助手"}}, - {name: "robot offline", args: []string{"devapp", "robot", "offline", "--unified-app-id", "u-1"}}, - {name: "version create", args: []string{"devapp", "version", "create", "--unified-app-id", "u-1", "--version", "1.0.1"}}, - {name: "version publish", args: []string{"devapp", "version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1"}}, + {name: "robot config", args: []string{"dev", "app", "robot", "config", "--unified-app-id", "u-1", "--name", "小助手"}}, + {name: "robot disable", args: []string{"dev", "app", "robot", "disable", "--unified-app-id", "u-1"}}, + {name: "version create", args: []string{"dev", "app", "version", "create", "--unified-app-id", "u-1", "--version", "1.0.1"}}, + {name: "version publish", args: []string{"dev", "app", "version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1"}}, } for _, tc := range cases { @@ -315,7 +318,7 @@ func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { if err == nil { t.Fatal("Execute() error = nil, want write guard") } - if !strings.Contains(err.Error(), "write operation") { + if !strings.Contains(err.Error(), "写操作") { t.Fatalf("error = %q, want write guard", err.Error()) } if runner.last.Tool != "" { @@ -331,20 +334,20 @@ func TestDevAppListBuildsListByConditionParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"list", "--name", "Waker", "--page", "2", "--page-size", "5", "--sort", "gmt_modified", "--order", "desc"}) + root.SetArgs([]string{"list", "--name", "Waker", "--cursor", "tok-2", "--page-size", "5", "--sort", "gmt_modified", "--order", "desc"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "list_open_dev_apps_by_condition" { - t.Fatalf("Tool = %q, want list_open_dev_apps_by_condition", got) + if got := runner.last.Tool; got != "list_dev_app" { + t.Fatalf("Tool = %q, want list_dev_app", got) } want := map[string]any{ - "currentPage": 2, - "pageSize": 5, - "appName": "Waker", - "sortType": "gmt_modified", - "sortOrder": "desc", + "cursor": "tok-2", + "pageSize": 5, + "appName": "Waker", + "sortType": "gmt_modified", + "sortOrder": "desc", } if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) @@ -362,8 +365,8 @@ func TestDevAppGetBuildsDetailParams(t *testing.T) { if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "get_open_dev_app_detail" { - t.Fatalf("Tool = %q, want get_open_dev_app_detail", got) + if got := runner.last.Tool; got != "get_dev_app" { + t.Fatalf("Tool = %q, want get_dev_app", got) } want := map[string]any{"unifiedAppId": "u-1"} if !reflect.DeepEqual(runner.last.Params, want) { @@ -388,15 +391,15 @@ func TestDevAppCreateUsesCurrentInnerToolAndWriteGuard(t *testing.T) { root = newDevAppTestRoot(runner) root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "create", "--name", "Demo", "--desc", "internal app", "--yes"}) + root.SetArgs([]string{"dev", "app", "create", "--name", "Demo", "--desc", "internal app", "--yes"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "create_inner_app" { - t.Fatalf("Tool = %q, want create_inner_app", got) + if got := runner.last.Tool; got != "create_dev_app" { + t.Fatalf("Tool = %q, want create_dev_app", got) } - want := map[string]any{"appName": "Demo", "appDesc": "internal app"} + want := map[string]any{"appName": "Demo", "desc": "internal app"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -408,15 +411,15 @@ func TestDevAppUpdateUsesCurrentInnerTool(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "update", "--agent-id", "123", "--desc", "new desc", "--yes"}) + root.SetArgs([]string{"dev", "app", "update", "--unified-app-id", "u-123", "--desc", "new desc", "--yes"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "update_inner_app" { - t.Fatalf("Tool = %q, want update_inner_app", got) + if got := runner.last.Tool; got != "update_dev_app" { + t.Fatalf("Tool = %q, want update_dev_app", got) } - want := map[string]any{"agentId": 123, "appDesc": "new desc"} + want := map[string]any{"unifiedAppId": "u-123", "desc": "new desc"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -429,23 +432,17 @@ func TestDevAppLifecycleBuildsLocatorParams(t *testing.T) { wantTool string wantParams map[string]any }{ - { - name: "delete by agent id", - args: []string{"delete", "--agent-id", "123", "--yes"}, - wantTool: "delete_inner_app", - wantParams: map[string]any{"agentId": 123}, - }, { name: "inactive by unified app id", - args: []string{"inactive", "--unified-app-id", "u-1", "--yes"}, - wantTool: "inactive_inner_app", + args: []string{"disable", "--unified-app-id", "u-1", "--yes"}, + wantTool: "disable_dev_app", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { - name: "active by app key", - args: []string{"active", "--app-key", "dingxxx", "--yes"}, - wantTool: "active_inner_app", - wantParams: map[string]any{"appKey": "dingxxx"}, + name: "active by unified id", + args: []string{"enable", "--unified-app-id", "u-2", "--yes"}, + wantTool: "enable_dev_app", + wantParams: map[string]any{"unifiedAppId": "u-2"}, }, } @@ -456,7 +453,7 @@ func TestDevAppLifecycleBuildsLocatorParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp"}, tc.args...)) + root.SetArgs(append([]string{"dev", "app"}, tc.args...)) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -471,6 +468,58 @@ func TestDevAppLifecycleBuildsLocatorParams(t *testing.T) { } } +// TestDevAppDeleteConfirmName covers the danger-tier delete: --confirm-name +// must match the located app's real name, and a name that can't be read is +// fail-closed (abort), never a silent proceed. +func TestDevAppDeleteConfirmName(t *testing.T) { + t.Run("matching name proceeds", func(t *testing.T) { + runner := &devAppResponseRunner{response: map[string]any{"appName": "DemoApp"}} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"dev", "app", "delete", "--unified-app-id", "u-123", "--confirm-name", "DemoApp", "--yes"}) + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v", err) + } + if runner.last.Tool != "delete_dev_app" { + t.Fatalf("Tool = %q, want delete_dev_app", runner.last.Tool) + } + }) + + t.Run("mismatched name aborts", func(t *testing.T) { + runner := &devAppResponseRunner{response: map[string]any{"appName": "RealName"}} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"dev", "app", "delete", "--unified-app-id", "u-123", "--confirm-name", "WrongName", "--yes"}) + err := root.Execute() + if err == nil || !strings.Contains(err.Error(), "名称不匹配") { + t.Fatalf("error = %v, want 名称不匹配", err) + } + if runner.last.Tool == "delete_dev_app" { + t.Fatal("delete must not run on name mismatch") + } + }) + + t.Run("unreadable name is fail-closed", func(t *testing.T) { + runner := &devAppResponseRunner{response: map[string]any{}} // no appName + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"dev", "app", "delete", "--unified-app-id", "u-123", "--confirm-name", "DemoApp", "--yes"}) + err := root.Execute() + if err == nil || !strings.Contains(err.Error(), "无法读取应用名") { + t.Fatalf("error = %v, want 无法读取应用名 (fail-closed)", err) + } + if runner.last.Tool == "delete_dev_app" { + t.Fatal("delete must not run when name is unreadable") + } + }) +} + func TestDevAppWebappCommandsBuildParams(t *testing.T) { cases := []struct { name string @@ -481,17 +530,17 @@ func TestDevAppWebappCommandsBuildParams(t *testing.T) { { name: "get", args: []string{"webapp", "get", "--unified-app-id", "u-1"}, - wantTool: "get_webapp_config", + wantTool: "get_extension_webapp_config", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { name: "config", - args: []string{"webapp", "config", "--unified-app-id", "u-1", "--homepage-link", "https://example.com", "--pc-homepage-link", "https://pc.example.com", "--yes"}, - wantTool: "set_webapp_config", + args: []string{"webapp", "config", "--unified-app-id", "u-1", "--homepage-url", "https://example.com", "--pc-homepage-url", "https://pc.example.com", "--yes"}, + wantTool: "set_extension_webapp_config", wantParams: map[string]any{ - "unifiedAppId": "u-1", - "homepageLink": "https://example.com", - "pcHomepageLink": "https://pc.example.com", + "unifiedAppId": "u-1", + "homepageUrl": "https://example.com", + "pcHomepageUrl": "https://pc.example.com", }, }, } @@ -503,7 +552,7 @@ func TestDevAppWebappCommandsBuildParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp"}, tc.args...)) + root.SetArgs(append([]string{"dev", "app"}, tc.args...)) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -527,31 +576,32 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { }{ { name: "list", - args: []string{"permission", "list", "--agent-id", "123", "--keyword", "手机号", "--status", "all", "--limit", "5"}, - wantTool: "list_open_dev_app_permissions", + args: []string{"permission", "list", "--unified-app-id", "u-123", "--keyword", "手机号", "--status", "all", "--cursor", "tok-3", "--page-size", "5"}, + wantTool: "list_dev_app_permissions", wantParams: map[string]any{ - "agentId": 123, - "keyword": "手机号", - "authStatus": "ALL", - "limit": 5, + "unifiedAppId": "u-123", + "keyword": "手机号", + "authStatus": "ALL", + "cursor": "tok-3", + "pageSize": 5, }, }, { name: "add", - args: []string{"permission", "add", "--agent-id", "123", "--permissions", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, - wantTool: "apply_open_dev_app_permissions", + args: []string{"permission", "add", "--unified-app-id", "u-123", "--permissions", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, + wantTool: "apply_dev_app_permissions", wantParams: map[string]any{ - "agentId": 123, - "scopeValues": []string{"Contact.User.mobile", "qyapi_robot_sendmsg"}, + "unifiedAppId": "u-123", + "scopeValues": []string{"Contact.User.mobile", "qyapi_robot_sendmsg"}, }, }, { name: "remove", - args: []string{"permission", "remove", "--agent-id", "123", "--permission", "Contact.User.mobile", "--yes"}, - wantTool: "remove_open_dev_app_permission", + args: []string{"permission", "remove", "--unified-app-id", "u-123", "--permission", "Contact.User.mobile", "--yes"}, + wantTool: "remove_dev_app_permissions", wantParams: map[string]any{ - "agentId": 123, - "scopeValue": "Contact.User.mobile", + "unifiedAppId": "u-123", + "scopeValue": "Contact.User.mobile", }, }, } @@ -563,7 +613,7 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp"}, tc.args...)) + root.SetArgs(append([]string{"dev", "app"}, tc.args...)) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -584,15 +634,15 @@ func TestDevAppCredentialsGetBuildsParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "credentials", "get", "--agent-id", "123"}) + root.SetArgs([]string{"dev", "app", "credentials", "get", "--unified-app-id", "u-123"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) } - if got := runner.last.Tool; got != "get_open_dev_app_credentials" { - t.Fatalf("Tool = %q, want get_open_dev_app_credentials", got) + if got := runner.last.Tool; got != "get_dev_app_credentials" { + t.Fatalf("Tool = %q, want get_dev_app_credentials", got) } - want := map[string]any{"agentId": 123} + want := map[string]any{"unifiedAppId": "u-123"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -613,7 +663,7 @@ func TestDevAppCredentialsGetKeepsSecretFields(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "credentials", "get", "--agent-id", "123"}) + root.SetArgs([]string{"dev", "app", "credentials", "get", "--unified-app-id", "u-123"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -633,10 +683,10 @@ func TestDevAppMemberCommandsValidateRequiredFlags(t *testing.T) { args []string wantErr string }{ - {name: "list requires app", cmd: "list", args: nil, wantErr: "--app-id is required"}, - {name: "add requires users", cmd: "add", args: []string{"--app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users is required"}, - {name: "add rejects empty users", cmd: "add", args: []string{"--app-id", "app-001", "--users", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users must contain at least one userId"}, - {name: "remove requires member type", cmd: "remove", args: []string{"--app-id", "app-001", "--users", "userId1", "--dry-run"}, wantErr: "--member-type is required"}, + {name: "list requires app", cmd: "list", args: nil, wantErr: "--unified-app-id 为必填"}, + {name: "add requires users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users 为必填"}, + {name: "add rejects empty users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--users", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users 至少包含一个 userId"}, + {name: "remove requires member type", cmd: "remove", args: []string{"--unified-app-id", "app-001", "--users", "userId1", "--dry-run"}, wantErr: "--member-type 为必填"}, } for _, tc := range cases { @@ -646,7 +696,7 @@ func TestDevAppMemberCommandsValidateRequiredFlags(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs(append([]string{"devapp", "member", tc.cmd}, tc.args...)) + root.SetArgs(append([]string{"dev", "app", "member", tc.cmd}, tc.args...)) err := root.Execute() if err == nil { @@ -669,8 +719,8 @@ func TestDevAppSecurityConfigBuildsOnlyProvidedLists(t *testing.T) { root.SetOut(&out) root.SetErr(&out) root.SetArgs([]string{ - "devapp", "security", "config", - "--app-id", "app-001", + "dev", "app", "security", "config", + "--unified-app-id", "app-001", "--ip-whitelist", "192.0.2.10,192.0.2.11", "--redirect-url", "https://callback.example.invalid/callback", "--sso-url", "https://sso.example.invalid/sso", @@ -684,14 +734,14 @@ func TestDevAppSecurityConfigBuildsOnlyProvidedLists(t *testing.T) { if got := runner.last.CanonicalProduct; got != "devapp" { t.Fatalf("CanonicalProduct = %q, want devapp", got) } - if got := runner.last.Tool; got != "update_app_security_config" { - t.Fatalf("Tool = %q, want update_app_security_config", got) + if got := runner.last.Tool; got != "update_dev_app_security_config" { + t.Fatalf("Tool = %q, want update_dev_app_security_config", got) } want := map[string]any{ - "unifiedAppId": "app-001", - "ipWhiteList": []string{"192.0.2.10", "192.0.2.11"}, - "redirectUrls": []string{"https://callback.example.invalid/callback"}, - "otherAuthUrls": []string{"https://sso.example.invalid/sso"}, + "unifiedAppId": "app-001", + "ipWhitelist": []string{"192.0.2.10", "192.0.2.11"}, + "redirectUrls": []string{"https://callback.example.invalid/callback"}, + "ssoUrls": []string{"https://sso.example.invalid/sso"}, } if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) @@ -704,7 +754,7 @@ func TestDevAppSecurityConfigOmitsAbsentOptionalLists(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "security", "config", "--app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback", "--dry-run"}) + root.SetArgs([]string{"dev", "app", "security", "config", "--unified-app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback", "--dry-run"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -725,13 +775,13 @@ func TestDevAppSecurityConfigRequiresAtLeastOneConfig(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "security", "config", "--app-id", "app-001", "--dry-run"}) + root.SetArgs([]string{"dev", "app", "security", "config", "--unified-app-id", "app-001", "--dry-run"}) err := root.Execute() if err == nil { t.Fatal("Execute() error = nil, want validation error") } - if !strings.Contains(err.Error(), "one of --ip-whitelist, --redirect-url, or --sso-url is required") { + if !strings.Contains(err.Error(), "至少提供一项安全配置:--ip-whitelist、--redirect-url 或 --sso-url") { t.Fatalf("error = %q", err.Error()) } if runner.last.Tool != "" { @@ -746,15 +796,15 @@ func TestDevAppMemberAndSecurityRequireWriteGuard(t *testing.T) { }{ { name: "member add", - args: []string{"devapp", "member", "add", "--app-id", "app-001", "--users", "userId1", "--member-type", "DEVELOPER"}, + args: []string{"dev", "app", "member", "add", "--unified-app-id", "app-001", "--users", "userId1", "--member-type", "DEVELOPER"}, }, { name: "member remove", - args: []string{"devapp", "member", "remove", "--app-id", "app-001", "--users", "userId1", "--member-type", "DEVELOPER"}, + args: []string{"dev", "app", "member", "remove", "--unified-app-id", "app-001", "--users", "userId1", "--member-type", "DEVELOPER"}, }, { name: "security config", - args: []string{"devapp", "security", "config", "--app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback"}, + args: []string{"dev", "app", "security", "config", "--unified-app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback"}, }, } @@ -771,7 +821,7 @@ func TestDevAppMemberAndSecurityRequireWriteGuard(t *testing.T) { if err == nil { t.Fatal("Execute() error = nil, want write guard") } - if !strings.Contains(err.Error(), "write operation") { + if !strings.Contains(err.Error(), "写操作") { t.Fatalf("error = %q, want write guard", err.Error()) } if runner.last.Tool != "" { diff --git a/internal/helpers/devdoc_test.go b/internal/helpers/devdoc_test.go index 96c042f7..41096199 100644 --- a/internal/helpers/devdoc_test.go +++ b/internal/helpers/devdoc_test.go @@ -38,7 +38,7 @@ func TestDevdocArticleSearchAcceptsWukongKeywordAlias(t *testing.T) { var out, errOut bytes.Buffer cmd.SetOut(&out) cmd.SetErr(&errOut) - cmd.SetArgs([]string{"article", "search", "--keyword", "openConversationId", "--page", "2", "--size", "5"}) + cmd.SetArgs([]string{"article", "search", "--keyword", "openConversationId", "--cursor", "tok-d", "--page-size", "5"}) if err := cmd.Execute(); err != nil { t.Fatalf("Execute() error = %v\nstderr:\n%s", err, errOut.String()) @@ -49,11 +49,11 @@ func TestDevdocArticleSearchAcceptsWukongKeywordAlias(t *testing.T) { if got := runner.last.Params["keyword"]; got != "openConversationId" { t.Fatalf("keyword = %#v, want openConversationId", got) } - if got := runner.last.Params["page"]; got != 2 { - t.Fatalf("page = %#v, want 2", got) + if got := runner.last.Params["cursor"]; got != "tok-d" { + t.Fatalf("cursor = %#v, want tok-d", got) } - if got := runner.last.Params["size"]; got != 5 { - t.Fatalf("size = %#v, want 5", got) + if got := runner.last.Params["pageSize"]; got != 5 { + t.Fatalf("pageSize = %#v, want 5", got) } } From dbcbb1de85cff636f57e0697439a17695705fea5 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:11:09 +0800 Subject: [PATCH 16/47] =?UTF-8?q?docs(dev):=20dingtalk-dev=20skill=20?= =?UTF-8?q?=E6=A6=82=E5=BF=B5=E7=BB=93=E6=9E=84=20+=20=E4=BA=A7=E5=93=81?= =?UTF-8?q?=20reference=20(1/2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SKILL.md 重构为「概念地图 + 核心规则/通用出参约定 + 典型任务 + 产品索引」; reference 改为一命令组一文件;不在 agent 文档暴露内部 MCP tool 名。 Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/multi/dingtalk-dev/SKILL.md | 135 ++++++++++++++++++ skills/multi/dingtalk-dev/references/app.md | 111 ++++++++++++++ .../multi/dingtalk-dev/references/connect.md | 75 ++++++++++ .../dingtalk-dev/references/credentials.md | 23 +++ skills/multi/dingtalk-dev/references/event.md | 60 ++++++++ .../multi/dingtalk-dev/references/member.md | 29 ++++ .../dingtalk-dev/references/permission.md | 91 ++++++++++++ skills/multi/dingtalk-dev/references/robot.md | 108 ++++++++++++++ .../multi/dingtalk-dev/references/security.md | 21 +++ .../multi/dingtalk-dev/references/version.md | 130 +++++++++++++++++ 10 files changed, 783 insertions(+) create mode 100644 skills/multi/dingtalk-dev/SKILL.md create mode 100644 skills/multi/dingtalk-dev/references/app.md create mode 100644 skills/multi/dingtalk-dev/references/connect.md create mode 100644 skills/multi/dingtalk-dev/references/credentials.md create mode 100644 skills/multi/dingtalk-dev/references/event.md create mode 100644 skills/multi/dingtalk-dev/references/member.md create mode 100644 skills/multi/dingtalk-dev/references/permission.md create mode 100644 skills/multi/dingtalk-dev/references/robot.md create mode 100644 skills/multi/dingtalk-dev/references/security.md create mode 100644 skills/multi/dingtalk-dev/references/version.md diff --git a/skills/multi/dingtalk-dev/SKILL.md b/skills/multi/dingtalk-dev/SKILL.md new file mode 100644 index 00000000..5094e751 --- /dev/null +++ b/skills/multi/dingtalk-dev/SKILL.md @@ -0,0 +1,135 @@ +--- +name: dingtalk-dev +description: 钉钉开放平台企业内部应用全生命周期管理。应用是容器:凭证/权限点/成员/安全配置/能力扩展(网页应用/机器人)/版本,需审批的变更要发版本才生效。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/customKey/应用权限/权限点/scopeValue/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/本地调试机器人/把机器人接到本地agent/建联/connect。 +cli_version: "1.0.15+" +metadata: + category: product + stability: experimental + requires: + bins: + - dws +--- + +# 钉钉开放平台应用管理 Skill + +> 🧪 **EXPERIMENTAL · 试验版 / Preview** — dev 为预览能力,仅在 multi 模式提供,接口与命名后续可能调整。问题请提 issue 反馈。 + +> **PREREQUISITE:** Read the root `dws` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH. + + + +> `dws dev app ...` 是内置 helper 命令,不依赖 MCP 服务发现。首次使用前(或命令报错时)用 `dws dev app --help` 验证可用,验证过就不用每次重复;若报 `unknown command`,说明当前 dws 构建不含 dev 命令组(预览能力,`dws upgrade` 装不到),按仓库 `docs/dev-agent-install-guide.md` 用 install-dev.sh 从 `feat/dws-dev` 分支安装。 + +## 概念地图 + +先建立领域模型,再看命令——所有命令都是对这张图上某个节点的操作,用户的模糊意图先映射到节点再选命令。 + +### 应用是什么 + +钉钉开放平台的「企业内部应用」是企业自建的扩展程序。一个应用是一个容器: + +``` +企业内部应用(主键 unifiedAppId) +├── 凭证 appKey/appSecret —— 应用调 OpenAPI 的身份(credentials) +├── 权限 权限点 scopeValue,每个权限点授权一组 OpenAPI(permission) +├── 成员 DEVELOPER 等角色,决定谁能改这个应用(member) +├── 安全配置 IP 白名单 / 登录重定向 / 端内免登 URL(security) +├── 能力扩展 应用对用户「长什么样」,可同时挂多种: +│ ├── 网页应用 钉钉内打开的 H5,配移动端/PC 首页地址(webapp) +│ └── 机器人 群聊/单聊收发消息,走回调 URL 或接本地 agent(robot) +└── 版本 配置改动的生效通道(version) +``` + +映射示例:「想做个钉钉里打开的网页」= 创建应用 → webapp 配置 → 发版本;「做个答疑机器人」= robot submit/result 异步建号(或现有应用 robot config)→ 发版本 →(本地调试用 dev connect)。 + +### ID 体系 + +| 标识 | 是什么 | 用在哪 | +|------|--------|--------| +| `unifiedAppId` | 统一应用 ID,新模型主键 | **唯一全树定位标识**,所有单应用命令都用 `--unified-app-id` | +| `appKey` = `clientId` | 应用身份标识,同一个东西的新旧两个名字,非密钥 | OpenAPI 调用、建联;也可作 `--app-key` 列表过滤(不能定位单应用) | +| `appSecret` = `clientSecret` | 应用密钥,敏感 | 同上,按敏感凭证处理 | +| `agentId` | 旧版微应用 ID,历史遗留 | 仅出现在返回数据里;**不能用于定位**,拿到它先查出 unifiedAppId | +| `robotCode` | 机器人编号 | 加群、机器人发消息、建联 | + +应用定位统一只用 `--unified-app-id`(--app-key/--name 仅作 list 过滤,不能定位单应用)。旧的 `--agent-id`/`--app-id`/`--custom-key` 定位 flag 已移除——agentId 只是返回字段,不再是入参定位手段。appKey 与 clientId 是同一标识的新旧两名,无需追问区别。 + +### 生效模型(最重要) + +**改配置 ≠ 线上生效。** 需审批的变更(如 `requiredApproval=true` 的权限点)先累积在开发态,必须走版本通道才上线: + +``` +配置变更(permission add / robot config / webapp config ...) + → version create → check-approval(预检审批要求+候选审批人) + → publish(需审批时由用户选审批人)→ versionStatus=RELEASE 才生效 +``` + +- 机器人等能力需版本发布后才能被搜索、加群、路由消息。 +- 用户问「为什么没生效 / 机器人搜不到 / 权限加了还报错」→ 先查 `version status`。 +- 两套状态别混:应用 appStatus(0 停用 / 1 激活 / 2 待激活 / 3 过期)是应用开关;版本 versionStatus(INIT / AUDIT / RELEASE / GRAY)是变更走到哪了。 + +### 边界与角色 + +- 本 skill 只管**企业内部应用**。接口文档 → `dingtalk-devdoc`;钉钉云文档 → `dingtalk-doc`;工作台入口的「应用」→ `workbench app`;群里发消息用的机器人 → `dingtalk-chat`;审批流 → `dingtalk-oa`。 +- 角色:开发者(member DEVELOPER)改配置;管理员管启停;审批人批版本发布——审批人必须用户拍板,agent 不代选、不默认取第一个。 + +## 核心规则 + +1. `应用`、`机器人` 是泛词:用户只说这两个词、无开放平台上下文时,先追问确认是不是开发者后台的企业内部应用,不要猜——很可能是工作台应用或群消息机器人(转出口见上方「边界与角色」)。 +2. 所有命令加 `--format json`。 +3. 写操作先 `--dry-run`,确认后才加 `--yes`。例外:`dev connect` 的 dry-run 是建联预检(渠道识别、凭证来源、本地 agent CLI 是否安装),正式 connect 是前台长驻进程——在对话里跑必须后台运行并告诉用户如何停止,或引导用户自己开终端跑。 +4. 写操作返回成功 ≠ 状态已变:启停、机器人配置、版本发布等关键写操作执行后,回读对应查询命令(`get` / `robot get` / `version status`)确认状态,以回读结果为准;接口只返回操作成功但未带状态时,向用户说明以回读为准。 +5. 应用名/appKey 命中多条时展示候选,不取第一条。 +6. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名——权限点才是授权单元,API 名与权限点是多对一。 +7. 主动读取密钥走 `credentials get`;任何返回里的 `clientSecret/appSecret` 都按敏感凭证处理,不写进回答文本。例外:建联流程内部把 secret 作为参数传给 `dev connect` 是必要用途。 + +### 通用出参约定(跨所有命令) + +- **dry-run**:出参里看 `invocation.params`(将下发的参数)确认无误,再把 `--dry-run` 换成 `--yes`;dry-run 时 `response` 只是回显。 +- **游标分页**(list / permission list / version list / event list / doc search):首次不传 `--cursor`,出参带 `nextCursor`(空=到底)原样回传续翻;`hasMore == nextCursor 非空`。cursor 是上游不透明令牌,不要自己解析或构造,也不要跨命令复用。 +- **批量聚合**:`permission remove` 传多个权限点时出参是 `{results:[...], ok, total, failedCount}`,逐条判断成败(`ok=false` 即有失败)。 +- **pretty**:`--format pretty` 会在应用/版本状态字段旁附 `*Text` 可读标签(如 `appStatusText`);JSON 格式不附,以原始字段为准。 +- **失败**:`ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,不编造解释,解读走下方文档 RAG。 + +## 开放平台文档 RAG / 错误码排查 + +- dev 命令执行中,只要用户问开放平台 API、接口参数、字段含义、权限点、回调、SDK、配额、错误码,或命令返回上游 OpenAPI/SDK 错误,必须先用 `dws dev doc search --query "<关键词>" --format json` 做官方文档 RAG。 +- 业务错误(`ServiceResult.success=false`)原样透传 `errorCode/errorMsg`,不要编造解释;需要解读错误含义时走 devdoc RAG。 +- 查询词优先保留原始 API 名、能力名、权限点、完整错误码和 message;首轮形如 `errcode `,无结果再换 `<产品/场景> <错误码>`、`<接口名> 参数`。 +- 本地 CLI 错误(如 `unknown command` / `unknown flag` / 认证 / recovery)仍按 root `dws` / `dws-shared` 的错误处理执行;`devdoc` 用于开放平台业务错误码和接口语义排查。 +- `devdoc` 只查钉钉开放平台开发者文档,不查业务数据;排查结论必须基于命中条目的标题、摘要或链接,不能编造错误原因或不存在的命令。 + +## 典型任务 + +端到端任务都是「定位应用 → 改容器某节点 →(按审批需要)走版本生效 → 回读验证」。常见链路(每步先 `--dry-run` 确认再 `--yes`,细节进对应产品文件): + +- **建钉钉里打开的网页应用**:`app create` → `webapp config` → `version create` → `check-approval` → `publish` →(回读 `version status` 到 `RELEASE`)。 +- **权限从申请到生效**:`permission list`(选 `scopeValue`)→ `permission add` →(`requiredApproval` 的权限)`version create` → `check-approval`(看是否需审批+候选审批人)→ `publish --approver <用户选的>` → `version status`。 +- **做答疑机器人**:`robot submit` → `robot result`(`SUCCESS` 拿 clientId/secret),或现有应用 `robot config` →(发版本)→ 本地调试 `dev connect`。 +- **查"为什么没生效 / 机器人搜不到 / 权限加了还报错"**:先 `version status`——改配置 ≠ 生效,未发到 `RELEASE` 就不生效。 + +> 审批人必须用户拍板,agent 不代选、不默认取第一个。 + +## 产品索引 + +按命令组直达(一命令组一文件): + +| 命令组 | 参考文档 | 覆盖命令 | +|--------|---------|---------| +| 应用 | [app.md](references/app.md) | list / get / create / update / delete / disable / enable | +| 凭证 | [credentials.md](references/credentials.md) | credentials get | +| 网页应用 | [webapp.md](references/webapp.md) | webapp get / config | +| 权限 | [permission.md](references/permission.md) | permission list / add / remove | +| 成员 | [member.md](references/member.md) | member list / add / remove | +| 安全配置 | [security.md](references/security.md) | security config | +| 机器人 | [robot.md](references/robot.md) | robot submit / result / get / config / enable / disable | +| 本地建联 | [connect.md](references/connect.md) | dev connect(渠道预检 / agent 模型工作目录 / 会话记忆 / AI 卡片) | +| 版本发布 | [version.md](references/version.md) | version create / list / get / check-approval / publish / status | +| 事件订阅 | [event.md](references/event.md) | event list / subscribe / unsubscribe | + +## Gotchas + +跨命令组的真实坑,随使用积累追加(单命令组的坑记在对应 reference 的错误处理表里): + +- 新应用 `version list` 返回空 ≠ 无可发布内容:先 `version create`,用返回的 `versionId` 继续 check-approval/publish。 +- `robot info is not exist` 是「应用还没配过机器人」,走 `robot config` 首次创建,不是 `enable`。 diff --git a/skills/multi/dingtalk-dev/references/app.md b/skills/multi/dingtalk-dev/references/app.md new file mode 100644 index 00000000..9241773c --- /dev/null +++ b/skills/multi/dingtalk-dev/references/app.md @@ -0,0 +1,111 @@ +# 应用基础操作 + +> 概念锚点:操作的是「应用」容器本体(见 SKILL.md 概念地图);启停/删除改的是应用 appStatus,不是版本 versionStatus。 + +应用列表查询、详情、创建、修改、生命周期启停和删除。 + +## 应用定位 + +所有单应用命令统一只用 `--unified-app-id`(全树主键,必填)定位: + +| 标识 | 处理 | +|------|------| +| `--unified-app-id` | 直接使用,所有单应用命令都认(唯一定位手段) | + +> 定位只认 `--unified-app-id`。`--app-key`/`--name` 只在 `dev app list` 里作**列表过滤**,不能用于定位单个应用做读写;旧的 `--agent-id`/`--app-id`/`--custom-key` 也已移除。拿到 appKey/agentId 时,先用 `dev app list` 查出 unifiedAppId 再操作。 + +## 应用列表 + +```bash +dws dev app list --format json +dws dev app list --name DemoApp --format json +dws dev app list --app-key dingxxx --format json +dws dev app list --creator 张三 --sort gmt_modified --order desc --format json +``` + +| CLI | 说明 | +|-----|------| +| `--cursor` | 游标令牌:首次留空,续翻传上次出参的 `nextCursor` | +| `--page-size` | 单页条数,默认 20 | +| `--name` / `--keyword` | 应用名搜索 | +| `--app-key` | 按 appKey/clientId 过滤 | +| `--creator` | 创建人关键词 | +| `--sort` | 如 `gmt_modified` | +| `--order` | `asc` / `desc` | + +> 分页用游标:首页不传 `--cursor`,出参带 `nextCursor`(空=到底);续翻把它原样回传。旧 `--page`/`--limit`/`--offset` 仍隐藏兼容(跳页/老脚本)。出参字段见 SKILL.md「通用出参约定」。 + +## 应用状态字段 + +列表/详情统一用 `appStatus`,按应用生命周期枚举判断;不要和版本 `versionStatus` 混用。 + +| appStatus | 枚举 | 含义 | 下一步 | +|-----------|------|------|--------| +| `0` | `IN_ACTIVE` | 已停用,应用不可用 | 需要恢复时走 `enable --dry-run` → 确认 → `--yes` | +| `1` | `ACTIVE` | 已激活,应用可用 | 可继续配置权限、网页应用、机器人或版本 | +| `2` | `WAIT_ACTIVE` | 待激活 | 先回读 `get/list` 确认状态;不要直接按已生效处理 | +| `3` | `EXPIRED` | 已过期 | 停止写操作,提示用户到开发者后台或管理员侧处理 | + +`create/update` 返回的 `versionStatus` 是版本状态,语义见 `version.md`;它不等同于应用启停状态。 + +## 应用详情 + +```bash +dws dev app get --unified-app-id UNIFIED_APP_ID --format json +``` + +详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 + +## 创建应用 + +```bash +dws dev app create --name DemoApp --desc "内部应用" --dry-run --format json +dws dev app create --name DemoApp --desc "内部应用" --yes --format json +``` + +| CLI | 必填 | +|-----|------| +| `--name` | 是 | +| `--desc` | 否 | +| `--icon` | 否 | + +## 修改应用 + +```bash +dws dev app update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-run --format json +dws dev app update --unified-app-id ID --name DemoApp2 --desc "新描述" --yes --format json +``` + +至少提供一个更新字段:`--name` / `--desc` / `--icon`。 + +## 停用 / 启用应用 + +```bash +dws dev app disable --unified-app-id ID --dry-run --format json +dws dev app disable --unified-app-id ID --yes --format json + +dws dev app enable --unified-app-id ID --dry-run --format json +dws dev app enable --unified-app-id ID --yes --format json +``` + +停用保留数据但应用不可用,可通过 `enable` 恢复。 + +执行 `disable/enable` 后必须回读 `get` 或 `list`:看到 `appStatus=0` 才算停用完成,看到 `appStatus=1` 才算启用完成;如果接口只返回操作成功但未带状态,向用户说明需要以回读结果为准。 + +## 删除应用 + +```bash +dws dev app delete --unified-app-id ID --dry-run --format json +dws dev app delete --unified-app-id ID --yes --format json +``` + +删除前必须展示应用摘要。删除为异步操作,成功后应用延迟从列表消失。 + +## 错误处理 + +| 情况 | 处理 | +|------|------| +| `unknown command` | CLI 构建不含 dev 命令组 | +| `endpoint_not_resolved` | 检查 edition endpoint 注入 | +| 多应用命中 | 展示候选,停止写操作 | +| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/multi/dingtalk-dev/references/connect.md b/skills/multi/dingtalk-dev/references/connect.md new file mode 100644 index 00000000..dfa61cc9 --- /dev/null +++ b/skills/multi/dingtalk-dev/references/connect.md @@ -0,0 +1,75 @@ +# 本地建联(把机器人接到本地 agent) + +> 概念锚点:`dws dev connect` 是 dev 顶层命令,把一个**现成机器人**接到当前渠道的本地 agent CLI 做调试/值守——只建联、不建号。缺机器人先走 robot.md 的 `robot submit`/`result` 建号拿 clientId/clientSecret。 + +## 起连接 + +```bash +# 用现成机器人凭证起 Stream,接到当前渠道的本地 agent(前台运行,Ctrl-C 退出) +dws dev connect --channel auto --robot-client-id --robot-client-secret + +# 用统一应用 ID,复用 credentials get 自动取凭证 +dws dev connect --unified-app-id --channel qoderwork + +# 预览建联方案不实际起连接 +dws dev connect --robot-client-id --robot-client-secret --dry-run --format json +``` + +正式 connect 是前台长驻进程:在对话里跑必须后台运行并告诉用户如何停止,或引导用户自己开终端跑。 + +| flag | 说明 | +|------|------| +| `--channel` | `auto`(默认,运行时信号自动识别) / openclaw / qoder / qoderwork / hermes / workbuddy / claudecode / codebuddy / codex / gemini / opencode | +| `--robot-client-id` / `--robot-client-secret` | 现成机器人凭证(clientId=AppKey, clientSecret=AppSecret)。命名带 `robot-` 前缀以避开全局 OAuth `--client-id` flag | +| `--unified-app-id` | 统一应用 ID,内部复用 `credentials get` 自动取凭证。⚠️ 字段名待预发真机验证;clientSecret 仅建号时返回一次、未必可取,必要时回退手填 | +| `--agent-memory` | 按会话续聊(默认开):同一群/单聊共享 agent 会话,追问保留上下文。仅 claudecode/codebuddy/workbuddy(CLI 有 `--session-id`/`--resume`);qoder 系/codex/gemini/opencode 无寻址会话,自动保持无状态。`--agent-memory=false` 关闭 | +| `--agent-model` | 覆盖本地 agent 模型(如 claudecode 默认锁 haiku 求快,可改 `claude-sonnet-4-6` 换聪明)。env: `DWS_AGENT_MODEL` | +| `--agent-workdir` | agent 运行目录:放知识文件(如 CLAUDE.md)可给机器人企业上下文。默认空白临时目录(冷启动快 ~4s vs 大目录 ~29s,慢了会错过钉钉响应窗口)。env: `DWS_AGENT_WORKDIR` | +| `--reply-card` | 富回复(默认开):🤔Thinking/🥳Done 表态永远生效;**卡片需配 `--card-template` 才启用**(同 hermes:没配模板=纯文字回复),失败自动回退文字;env `DWS_REPLY_CARD=0` 全关 | +| `--card-template` | AI 卡片模板 ID。**模板按应用授权**:去开发者后台→你的应用→AI 卡片设置注册/获取模板 ID,可去掉公共模板的第三方角标;默认用公共模板 best-effort。env `DWS_CARD_TEMPLATE` | + +## 建联前的依赖预检(agent 必做) + +渠道背后是本地 agent CLI,用户可能没装。先 `--dry-run` 看出参 `cli` 字段再决定下一步: + +```bash +dws dev connect --channel --robot-client-id x --robot-client-secret y --dry-run --format json +# 输出里的 cli 字段: +# "cli": {"required":"Claude Code","installed":false,"autoInstall":true,"installHint":"npm i -g @anthropic-ai/claude-code"} +``` + +| cli 状态 | agent 应该做什么 | +|----------|----------------| +| `installed: true` | 直接建联 | +| `installed: false, autoInstall: true` | 告知用户缺哪个 CLI,说明启动建联时会自动 `npm` 安装(或先手动执行 installHint 里的命令再连);`DWS_CONNECT_NO_INSTALL=1` 可禁自动安装 | +| `installed: false, autoInstall: false` | **不要直接起连接**——桌面 App 渠道(qoder/qoderwork/workbuddy)需要用户先安装对应 App(installHint 是下载地址),装好后 CLI 随 App 自带;openclaw/hermes 引导用户走官方 onboarding | + +dry-run 出参的完整建联预检结构(channel/detectedBy/credentialSource/agent/cli/connect)见 SKILL.md「通用出参约定」。 + +## Codex 渠道注意 + +```bash +# Codex 默认空白工作目录会触发 "Not inside a trusted directory and --skip-git-repo-check was not specified" +CODEX_BIN="$(command -v codex || echo /Applications/Codex.app/Contents/Resources/codex)" +DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check" \ + dws dev connect --unified-app-id --channel codex --format json +``` + +- `--channel codex` 真实建联时优先用上面的 `DWS_AGENT_CMD` 形式,避免 Codex CLI 在默认临时目录内因非可信 Git 目录拒绝执行。 +- 给 Codex 固定知识/项目上下文:`DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check -C /path/to/repo"`(路径不要含空格)。 +- 设置 `DWS_AGENT_CMD` 后 DWS 不再自动拼接 `--agent-model`/`--agent-workdir`/会话参数,需要的要一起写进 `DWS_AGENT_CMD`。 + +## 机制与环境覆盖 + +- **stream-bridge 渠道**:Go 原生进程内 Stream 转发器,订阅 `TOPIC_ROBOT`,每条 @机器人消息起一个无头 CLI 实例 → stdout 回钉钉,可 7×24 无人值守。 +- **会话记忆**:首条消息 `--session-id ` 建会话,后续 `--resume ` 续聊;会话状态在内存里,连接器重启后从新会话开始;某会话坏了会自愈(下条消息换新会话)。 +- **官方渠道**(openclaw/hermes):dws 不代建机器人,输出官方 onboarding 指引。 +- 环境覆盖:`DWS_AGENT_CMD`(整条命令覆盖,覆盖时不再注入模型/会话参数) / `DWS_AGENT_MODEL` / `DWS_AGENT_WORKDIR` / `DWS_CONNECT_CMD` / `DWS_CONNECT_NO_INSTALL=1` / `DWS_AGENT_TIMEOUT_MS`。 + +## 错误处理 + +| 情况 | 处理 | +|------|------| +| 缺凭证 | 先用 `robot submit`/`robot result` 建号拿 clientId/clientSecret(见 robot.md) | +| Codex agent 返回 `Not inside a trusted directory and --skip-git-repo-check was not specified` | 用 `DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check"` 重启 `connect --channel codex` | +| 桌面 App 渠道 `installed:false, autoInstall:false` | 引导用户先装对应 App(installHint 是下载地址),不要直接起连接 | diff --git a/skills/multi/dingtalk-dev/references/credentials.md b/skills/multi/dingtalk-dev/references/credentials.md new file mode 100644 index 00000000..25297465 --- /dev/null +++ b/skills/multi/dingtalk-dev/references/credentials.md @@ -0,0 +1,23 @@ +# 应用凭证读取 + +> 概念锚点:凭证=应用调 OpenAPI 的身份(appKey=clientId / appSecret=clientSecret);见 SKILL.md 概念地图。 + +## 读取凭证 + +```bash +dws dev app credentials get --unified-app-id UNIFIED_APP_ID --format json +``` + +**规则:** +- CLI 只传应用定位字段,不传 `showSecret`/`confirmSecret`。 +- 返回可能包含 `clientSecret/appSecret`,输出按敏感凭证处理,不写进回答文本。 +- 不能用 `dev app get` 代替;如果 `dev app get` 偶尔返回密钥,也只用于内部判断并脱敏,不向用户展开。 + +关键返回字段: + +| 字段 | 说明 | +|------|------| +| `clientId` / `appKey` | 非密钥标识 | +| `clientSecret` / `appSecret` | 敏感凭证 | +| `currentSecretStatus` | 当前密钥状态 | +| `pendingExpireTask` | 密钥过期任务信息 | diff --git a/skills/multi/dingtalk-dev/references/event.md b/skills/multi/dingtalk-dev/references/event.md new file mode 100644 index 00000000..9158706f --- /dev/null +++ b/skills/multi/dingtalk-dev/references/event.md @@ -0,0 +1,60 @@ +# 事件订阅 + +> 概念锚点:把应用关心的事件推到你的回调地址。所有命令用 `--unified-app-id` 定位;写操作先 `--dry-run`,确认后再 `--yes`。 + +为企业内部应用订阅、查询、取消开放平台事件推送。 + +## 查询已订阅事件 + +```bash +dws dev app event list --unified-app-id ID --format json +dws dev app event list --unified-app-id ID --page-size 50 --format json +dws dev app event list --unified-app-id ID --cursor --page-size 50 --format json +``` + +| CLI | 说明 | +|-----|------| +| `--unified-app-id` | 应用定位 | +| `--page-size` | 单页条数 | +| `--cursor` | 游标令牌:首次留空,续翻传上次返回的游标 | + +出参字段: + +- `events`:已订阅事件列表(每项含事件类型、回调地址等)。 +- `pushType`:推送方式(如 HTTP 回调)。 + +游标分页:返回的游标为空表示已到末尾。 + +## 订阅事件 + +```bash +dws dev app event subscribe --unified-app-id ID --event-types chat_add_member_org --callback-url https://example.com/event --dry-run --format json +dws dev app event subscribe --unified-app-id ID --event-types chat_add_member_org,chat_remove_member_org --callback-url https://example.com/event --yes --format json +``` + +| CLI | 说明 | +|-----|------| +| `--unified-app-id` | 应用定位 | +| `--event-types` | 事件类型,多个逗号分隔 | +| `--callback-url` | 事件回调地址 | + +**规则:** +- 写操作,先 `--dry-run` 预览,确认后 `--yes`。 +- 一次可订阅多个事件类型(逗号分隔),共用同一回调地址。 +- 事件类型取值以开放平台文档为准;不确定走 `dws dev doc search`。 + +## 取消订阅 + +```bash +dws dev app event unsubscribe --unified-app-id ID --event-types chat_add_member_org --dry-run --format json +dws dev app event unsubscribe --unified-app-id ID --event-types chat_add_member_org --yes --format json +``` + +| CLI | 说明 | +|-----|------| +| `--unified-app-id` | 应用定位 | +| `--event-types` | 要取消的事件类型,多个逗号分隔 | + +**规则:** +- 写操作,先 `--dry-run` 预览,确认后 `--yes`。 +- 取消前可先 `event list` 确认当前订阅,避免取消不存在的事件类型。 diff --git a/skills/multi/dingtalk-dev/references/member.md b/skills/multi/dingtalk-dev/references/member.md new file mode 100644 index 00000000..251666c0 --- /dev/null +++ b/skills/multi/dingtalk-dev/references/member.md @@ -0,0 +1,29 @@ +# 成员管理 + +> 概念锚点:成员=谁能改这个应用(DEVELOPER 等角色);见 SKILL.md 概念地图。 + +## 查询成员 + +```bash +dws dev app member list --unified-app-id UNIFIED_APP_ID --format json +``` + +## 添加成员 + +```bash +dws dev app member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json +dws dev app member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json +``` + +## 移除成员 + +```bash +dws dev app member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json +dws dev app member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json +``` + +| CLI | 说明 | +|-----|------| +| `--unified-app-id` | 统一应用 ID(必填) | +| `--users` | userId 列表,逗号/分号分隔(必填) | +| `--member-type` | 成员类型,如 `DEVELOPER`(必填) | diff --git a/skills/multi/dingtalk-dev/references/permission.md b/skills/multi/dingtalk-dev/references/permission.md new file mode 100644 index 00000000..e5c420e3 --- /dev/null +++ b/skills/multi/dingtalk-dev/references/permission.md @@ -0,0 +1,91 @@ +# 权限管理 + +> 概念锚点:权限点 scopeValue 是授权单元,一个权限点授权一组 OpenAPI;requiredApproval=true 的变更走版本通道生效(见 SKILL.md 生效模型)。 + +查询、申请、取消开放平台应用的 APP 应用权限和 SNS 个人权限。 + +## 权限列表 + +```bash +dws dev app permission list --unified-app-id ID --format json +dws dev app permission list --unified-app-id ID --keyword "机器人发消息" --status UNAUTHED --format json +dws dev app permission list --unified-app-id ID --scope-type SNS --format json +dws dev app permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json +``` + +| CLI | 说明 | +|-----|------| +| `--unified-app-id` | 应用定位 | +| `--keyword` | 权限名/API 名关键词 | +| `--status` | `ALL` / `AUTHED` / `UNAUTHED` | +| `--scope-type` | `APP` / `SNS`,为空返回两者 | +| `--scope` | 单权限详情模式 | +| `--cursor` | 游标令牌:首次留空,续翻传上次 `nextCursor` | +| `--page-size` | 单页条数,默认 20,建议不超过 50 | + +**状态判断:** + +`--status` 是查询过滤条件: + +| authStatus | 含义 | +|------------|------| +| `ALL` | 不按授权状态过滤 | +| `AUTHED` | 只看已授权/已开通的权限点 | +| `UNAUTHED` | 只看未授权/未开通的权限点 | + +单个权限项返回的 `status` 是内部操作态: + +| status | 枚举 | 含义 | 下一步 | +|--------|------|------|--------| +| `0` | `STATUS_OBTAINED` | 权限已获得 | 不要重复申请;如需取消,确认 `canRemove=true` 后走 `permission remove` | +| `1` | `STATUS_APPLYING` | 权限申请中 | 不要重复申请;查看 `authedStatusDesc`,通常等待审批或版本发布 | +| `2` | `STATUS_CAN_APPLY` | 权限可以申请 | 可走 `permission add --dry-run` | +| `3` | `STATUS_CAN_NOT_APPLY` | 权限不可以申请 | 停止申请,展示 `applyDisabledReason/displayMessage` | + +`authedStatusDesc` 是给用户看的细分状态:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED` 表示已开通、已申请或待发布;`NOT_OPEN`/`NOT_APPLIED` 表示未开通/未申请;`AUDIT_PROCESSING` 表示审批中;`AUDIT_REFUSE` 表示审批未通过。判断能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 + +**翻页:** + +权限列表用游标分页(与 app/version list 一致)。一个应用可能有 150+ 个权限点,逐页用 `nextCursor` 续翻: + +```bash +dws dev app permission list --unified-app-id ID --page-size 50 --format json +# → 出参 nextCursor=,续翻: +dws dev app permission list --unified-app-id ID --cursor --page-size 50 --format json +``` + +出参 `nextCursor` 为空(或 `hasMore=false`)时表示已到末尾。 + +**规则:** +- `permission search` 和 `permission detail` 是 `list` 的 CLI 别名。 +- 默认同时返回 APP 和 SNS 权限。 +- 列表模式只返回 `apiPreview`;`--scope` 详情模式返回完整 `apiList`。 + +**scopeValue 选择顺序:** + +1. 用户给了 `scopeValue` → 精确匹配 +2. 用户给了 API 名 → `keyword` 搜索,匹配 `apiPreview.name` +3. 用户给了权限名 → 匹配 `scopeName/scopeDesc` +4. 多个候选 → 展示列表让用户选择,不自动取第一条 + +## 申请权限 + +```bash +dws dev app permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json +dws dev app permission add --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --yes --format json +``` + +**规则:** +- `--permissions` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 +- 已开通跳过,不可编辑拒绝。 +- `requiredApproval=true` 允许申请——写入版本变更,审批在版本发布时处理。 +- 不在此处选审批人。 + +## 取消权限 + +```bash +dws dev app permission remove --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json +dws dev app permission remove --unified-app-id ID --permissions qyapi_robot_sendmsg --yes --format json +``` + +`--permissions` 多个逗号分隔;上游一次只取消一个权限点,多条时 CLI 逐条调用并返回 `results` 聚合数组。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 diff --git a/skills/multi/dingtalk-dev/references/robot.md b/skills/multi/dingtalk-dev/references/robot.md new file mode 100644 index 00000000..264da4e4 --- /dev/null +++ b/skills/multi/dingtalk-dev/references/robot.md @@ -0,0 +1,108 @@ +# 机器人能力 + +> 概念锚点:机器人是应用的能力扩展之一;建号/配置在此,接到本地 agent 调试用 `dws dev connect`(见 connect.md)。 + +为开放平台企业内部应用创建和配置机器人。分两类场景: + +1. **新建智能体机器人**:异步创建一个新的 Agent 应用 + 承载机器人(`submit` / `result`)。同步建号(一次性创建应用+机器人)已下线,统一走异步。 +2. **现有应用配置机器人**:在已存在的应用上配置/启用/停用机器人(`get` / `config`(upsert) / `enable` / `disable`),通过 `--unified-app-id` 定位。 + +> `corpId` / `userId` 由系统上下文自动注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 + +## 一、新建智能体机器人(异步建号) + +同步建号已下线,新建智能体一律用异步 `submit` 提交任务 + `result` 轮询。也适合创建耗时较长或需要失败重试的场景。 + +```bash +# 提交任务 +dws dev app robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws dev app robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json +# → 返回 taskId + +# 轮询结果 +dws dev app robot result --task-id --format json +``` + +- `submit` 返回 `taskId / status / expiresIn / interval / retryCount`,提交成功后通常是 `WAITING`。 +- 失败重试:把上次的 `taskId` 通过 `--task-id` 传入 `submit`,避免重复创建。 +- `result` 返回任务状态;只有 `SUCCESS` 时才能使用返回的 `agentId / robotCode / clientId / clientSecret`。 + +异步创建任务状态: + +| status | 含义 | 下一步 | +|--------|------|--------| +| `WAITING` | 任务已提交,仍在创建中 | 按 `interval` 轮询 `robot result` | +| `SUCCESS` | 创建完成 | 保存 `robotCode/clientId/clientSecret`,凭据按敏感信息处理 | +| `APPROVAL_REQUIRED` | 创建编排返回需审批 | 不要重复建号;按返回信息或开发者后台审批后再继续 | +| `FAIL` | 创建失败 | 读取 `errorCode/errorMsg/failReason`;可带原 `taskId` 重新 `submit` | +| `EXPIRED` | `taskId` 不存在或超过有效期 | 重新 `submit`,必要时换新 `taskId` | + +## 二、现有应用的机器人配置 + +### 查询配置 + +```bash +dws dev app robot get --unified-app-id --format json +``` + +返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 + +状态判断: + +- `status=1`:OFFLINE,机器人配置存在但处于停用/下线状态。 +- `status=2`:ONLINE,机器人配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 +- `robot get` 返回 `success=true` 且包含 `robotCode` 时,说明配置已落库,不是异步等待态。 +- ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-url`,或用 `dev connect` 接到本地 Agent(见 connect.md)。 +- 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 + +### 配置(upsert)/ 启用 + +- `config` 是 **upsert**:建或改都用它,配置不存在则建、存在则改,无需区分。需至少一个配置字段。 +- `enable` 是**纯启用**:只开启机器人能力,不带配置字段(只传 `--unified-app-id`)。 +- `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为"待生效";只有 `status=1` 或需要重新上架时才调用 `enable`。 + +```bash +dws dev app robot config --unified-app-id --name 小助手 --brief 审批助手 \ + --desc "处理审批相关问答" --outgoing-url https://example.com/msg \ + --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json + +dws dev app robot enable --unified-app-id --dry-run --format json +``` + +> 旧的独立 `update` 命令已删除(并入 `config`)。 + +| CLI | 说明 | +|-----|------| +| `--name` | 机器人名称 | +| `--brief` | 简介 | +| `--desc` | 描述 | +| `--icon` | 图标 mediaId | +| `--outgoing-url` | 消息回调地址 | +| `--event-url` | 事件回调地址 | +| `--mode` | 机器人模式枚举(整数) | +| `--skills` | 技能列表,逗号/分号分隔 | +| `--add-scope` | 自动添加机器人相关权限 | +| `--disable-ssl-verify` | 回调关闭 SSL 校验 | +| `--i18n-name` | 名称国际化 JSON,如 `'{"en_US":"Bot"}'` | +| `--i18n-brief` | 简介国际化 JSON | +| `--i18n-description` | 描述国际化 JSON | + +至少提供一个配置字段,否则 CLI 报错。 + +### 停用 + +```bash +dws dev app robot disable --unified-app-id --dry-run --format json +dws dev app robot disable --unified-app-id --yes --format json +``` + +## 错误处理 + +| 情况 | 处理 | +|------|------| +| `robot info is not exist` | 应用未配置机器人,先用 `robot config` 创建 | +| 应用名重复 | `app-name` 企业内需唯一,换个名字 | +| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | +| 创建任务 `EXPIRED` | 任务过期,重新 `submit`(可带原 taskId 重试) | + +> 把机器人接到本地 agent 调试/值守(渠道、AI 卡片、会话记忆、依赖预检)见 [connect.md](connect.md)。 diff --git a/skills/multi/dingtalk-dev/references/security.md b/skills/multi/dingtalk-dev/references/security.md new file mode 100644 index 00000000..20884fed --- /dev/null +++ b/skills/multi/dingtalk-dev/references/security.md @@ -0,0 +1,21 @@ +# 安全配置 + +> 概念锚点:安全配置=应用的 IP 白名单 / 登录重定向 / 端内免登 URL;见 SKILL.md 概念地图。 + +## 配置安全设置 + +```bash +dws dev app security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json +dws dev app security config --unified-app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json +``` + +| CLI | 说明 | +|-----|------| +| `--unified-app-id` | 统一应用 ID(必填) | +| `--ip-whitelist` | 出口 IP 白名单,逗号/分号分隔 | +| `--redirect-url` | 登录重定向 URL,逗号/分号分隔 | +| `--sso-url` | 端内免登 URL,逗号/分号分隔 | + +至少提供一个配置字段。 + +覆盖语义:未提供的字段不动;显式提供的列表是**整组覆盖**(传入即全量替换该项,不是追加)——要保留旧值就把旧值一起带上。 diff --git a/skills/multi/dingtalk-dev/references/version.md b/skills/multi/dingtalk-dev/references/version.md new file mode 100644 index 00000000..95c2456a --- /dev/null +++ b/skills/multi/dingtalk-dev/references/version.md @@ -0,0 +1,130 @@ +# 版本发布 + +> 概念锚点:版本是配置变更生效的唯一通道——改配置 ≠ 上线,发布到 RELEASE 才生效(见 SKILL.md 生效模型)。 + +管理开放平台企业内部应用的版本:基于当前配置创建版本、查看版本列表/详情、预检审批、发布、查状态。 + +> `corpId` / `userId` 由系统上下文自动注入,CLI 不传。所有版本通过 `--unified-app-id` 定位,单个版本再加 `--version-id`。 + +## 典型流程 + +```text +permission add(requiredApproval=true 写入版本变更) + → version create 创建版本 + → version check-approval 预检是否需要审批 / 审批人 + → version publish 发布(含高敏权限需 --confirm-sensitive) + → version status 轮询发布/审批状态 +``` + +## 创建版本 + +```bash +dws dev app version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --dry-run --format json +dws dev app version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json +``` + +| CLI | 说明 | +|-----|------| +| `--version` | 版本号,如 1.0.1 | +| `--desc` | 版本描述 | + +## 版本列表 + +```bash +dws dev app version list --unified-app-id --page-size 20 --format json +# 续翻:--cursor <上次出参的 nextCursor> +``` + +游标分页:`--cursor`/`--page-size`,出参带 `nextCursor`(空=到底,续翻原样回传);出参见 SKILL.md「通用出参约定」。 + +新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 + +## 版本详情 + +```bash +dws dev app version get --unified-app-id --version-id --format json +``` + +返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 + +## 预检审批(不发布) + +```bash +dws dev app version check-approval --unified-app-id --version-id --format json +``` + +仅返回审批要求和候选审批人,**不会实际发布**。 + +## 发布 + +```bash +dws dev app version publish --unified-app-id --version-id --dry-run --format json +dws dev app version publish --unified-app-id --version-id --yes --format json + +# 含高敏权限时必须确认 +dws dev app version publish --unified-app-id --version-id --confirm-sensitive --yes --format json + +# 灰度选人模式指定审批人 +dws dev app version publish --unified-app-id --version-id --approver --yes --format json +``` + +`publish` 是真实发布(区别于 `check-approval` 的预检不发布)。 + +| CLI | 说明 | +|-----|------| +| `--confirm-sensitive` | 版本含高敏权限时必须确认 | +| `--approver` | 灰度选人模式指定审批人 userId | + +> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`。二者是两个东西:`check-approval` 是只查审批要求不发布,`--dry-run` 是 CLI 要不要真调上游。发布前建议先 `check-approval`。 + +发布响应 `result`: + +| result | 含义 | 下一步 | +|--------|------|--------| +| `NOT_REQUIRED` | 不需要审批;预检时表示可直接发布,真实发布时表示已直接发布上线 | 真实发布后回读 `version status/get` 验证 `RELEASE` | +| `APPROVAL_REQUIRED` | 需要审批,通常出现在 `check-approval` 或未指定审批人时 | 查看 `approvalMode/approvalCandidates`,让用户选择审批人后再 `publish --approver` | +| `SUBMITTED` | 已提交审批 | 保存 `processId`,轮询 `version status` | + +审批模式 `approvalMode`: + +| approvalMode | 含义 | 下一步 | +|--------------|------|--------| +| `SELECT_APPROVER` | 灰度选人模式,需要在候选审批人中选一个 | 展示候选审批人,不自动选第一个 | +| `ENTERPRISE_SELF_BUILT` | 企业自建审核模式 | 不传 `--approver`,按企业自建审核流程等待 | + +## 版本状态 + +```bash +dws dev app version status --unified-app-id --version-id --format json +``` + +返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 + +版本状态字段:`version create/list/get/status` 统一返回 `versionStatus`,对齐版本枚举。 + +| versionStatus | 含义 | 下一步 | +|---------------|------|--------| +| `INIT` | 版本已创建或有待发布变更,尚未发布 | 可 `check-approval` / `publish` | +| `AUDIT` | 发布审核中 | 不要重复发布;即使没有返回 `processStatus`,也按审核中处理 | +| `RELEASE` | 已发布生效 | 发布完成,可继续验证权限、机器人、网页应用等能力 | +| `GRAY` | 灰度状态 | 按灰度流程处理;不要当全量已发布 | + +审批流程状态字段:`version status` 的 `processStatus` 只在存在审批流程且后端透出流程态时有值。`versionStatus=AUDIT` 但没有 `processStatus/processInstanceId` 时,不要判失败,仍表示审核中。 + +| processStatus | 含义 | 下一步 | +|---------------|------|--------| +| `UNDER_REVIEW` | 审批中 | 等待审批,必要时把 `processInstanceId` 给用户去钉钉客户端查看 | +| `PASS` | 审批通过 | 继续回读版本,确认是否进入 `RELEASE` | +| `FAIL` | 审批拒绝 | 展示 `processComment`,修改后重新创建/发布版本 | +| `WITHDRAW` / `CANCEL` | 审批撤回或取消 | 回到发布前状态,重新 `check-approval` / `publish` | +| `PUBLISH_FAILED` | 审批后发布失败 | 展示错误信息,重新检查版本状态和后端错误 | + +遇到未列出的状态值时,不要猜测语义;原样展示状态,并回读 `version get/status` 或查开放平台文档/后台详情。 + +## 错误处理 + +| 情况 | 处理 | +|------|------| +| `check-approval` 提示需审批 | 按返回选审批人,再 `publish --approver` | +| 发布报高敏权限未确认 | 加 `--confirm-sensitive` 重新发布 | +| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | From 26403388033a6c29f8d066f36844601badc389dd Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:11:21 +0800 Subject: [PATCH 17/47] =?UTF-8?q?docs(dev):=20dingtalk-dev=20skill=20refer?= =?UTF-8?q?ence=20(2/2)=20+=20dev=20=E5=8A=9F=E8=83=BD=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit webapp 产品文档;dev 命令集功能测试用例与报告(测试产物 results.jsonl 不入库)。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../multi/dingtalk-dev/references/webapp.md | 27 +++ test/dev_functional/REPORT.md | 75 +++++++ test/dev_functional/run_cases.py | 206 ++++++++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 skills/multi/dingtalk-dev/references/webapp.md create mode 100644 test/dev_functional/REPORT.md create mode 100644 test/dev_functional/run_cases.py diff --git a/skills/multi/dingtalk-dev/references/webapp.md b/skills/multi/dingtalk-dev/references/webapp.md new file mode 100644 index 00000000..9449f2e3 --- /dev/null +++ b/skills/multi/dingtalk-dev/references/webapp.md @@ -0,0 +1,27 @@ +# 网页应用配置 + +> 概念锚点:网页应用=应用的能力扩展之一,钉钉内打开的 H5;见 SKILL.md 概念地图。 + +## 查询网页应用配置 + +```bash +dws dev app webapp get --unified-app-id UNIFIED_APP_ID --format json +``` + +未配置网页应用前可能只返回 `agentId`。 + +## 配置网页应用 + +```bash +dws dev app webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --dry-run --format json +dws dev app webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --pc-homepage-url https://example.com/pc --yes --format json +``` + +| CLI | 说明 | +|-----|------| +| `--h5-page-type` | 网页应用生效端 | +| `--homepage-url` | 移动端首页地址 | +| `--pc-homepage-url` | PC 端首页地址 | +| `--omp-url` | 管理后台地址 | + +至少提供一个配置字段。`h5PageType` 未显式传入时不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 diff --git a/test/dev_functional/REPORT.md b/test/dev_functional/REPORT.md new file mode 100644 index 00000000..3005106b --- /dev/null +++ b/test/dev_functional/REPORT.md @@ -0,0 +1,75 @@ +# dev 命令树功能测试报告 + +> 日期:2026-06-12 ~ 06-13 | 分支:`feat/dws-dev` | 被测二进制:本地源码构建 +> 结论:**功能层 96/96 PASS;真实数据层 dev doc 全通过,dev app/connect 因预发网关 key 不可得降级(已确认,留内网验收)**。发现并修复缺陷 1 个(`version create` 缺 `--version` 必填校验)。 + +## 范围与方法 + +- 被测对象:本次重命名与参数优化后的全部 dev 命令树——`dws dev app *`(CRUD/凭证/网页应用/权限/成员/安全/机器人/版本,34 条)、`dws dev connect`、`dws dev doc search`,以及旧名移除验证。 +- 方法:CLI 功能层测试。`--dry-run` 验证 flag 解析、CLI→MCP 参数映射、命令路由;错误用例验证必填校验、写守卫(无 `--yes` 拦截)、未知渠道/未知 flag 拒绝;别名用例验证 `--app-id`/`--permission`/`--scope`/`--limit`/`--offset` 隐藏兼容。 +- 执行:`test/dev_functional/run_cases.py`,20 并发(ThreadPool),单用例超时 30s。 +- 边界声明:devapp 是 helper-only 产品,真实 MCP 调用需内部版注入 endpoint(或 `DINGTALK_DEVAPP_MCP_URL`),本报告不含真实上游调用;上游契约(字段语义、错误码)以预发真机验收为准。 + +## 结果总览 + +| 命令组 | 用例 | 通过 | 覆盖要点 | +|--------|------|------|----------| +| 根/旧名 | 4 | 4 | dev 三支柱 help;`devapp`/`app` 旧名确认移除 | +| app list | 5 | 5 | 无参/按名/agentId/分页映射/排序 | +| app get | 3 | 3 | 两种定位 + 缺定位报错 | +| app create | 4 | 4 | 全字段/缺 name/`--type` 确认已删/写守卫 | +| app update | 3 | 3 | 改名/无更新字段报错/缺定位报错 | +| 生命周期 | 5 | 5 | delete/inactive/active + 守卫 + 缺定位 | +| credentials | 3 | 3 | 两种定位 + 缺定位报错 | +| webapp | 5 | 5 | get/单字段/多字段/无字段报错/守卫 | +| permission list | 8 | 8 | 默认分页/`--page` 换算 offset(3,50→100,50)/旧 flag 兼容/keyword/scope 及别名/search 别名/缺定位 | +| permission add | 5 | 5 | 批量/两种别名/缺参/守卫 | +| permission remove | 6 | 6 | 单条原样出参/批量 results 聚合/两种别名/缺参/守卫 | +| member | 8 | 8 | 新主名/`--app-id` 兼容/缺参报 `--unified-app-id`/trim/守卫 | +| security | 6 | 6 | 三字段各自/组合/无字段报错/守卫 | +| robot | 13 | 13 | 同步建/异步 submit+result/get/config/update/enable/offline/缺参/守卫/`robot connect` 确认已移除 | +| version | 8 | 8 | create/list/get/check-approval/publish(approver+confirm-sensitive)/status/缺 version 报错/守卫 | +| dev connect | 5 | 5 | 显式凭证 dry-run/unified-app-id 凭证源/未知渠道拒绝/缺凭证报错/agent 参数全开 | +| dev doc | 4 | 4 | --query/位置参数/分页/缺参 | +| pretty | 1 | 1 | pretty 格式不崩溃 | +| **合计** | **96** | **96** | | + +执行耗时:累计 107.7s(20 并发墙钟约 8s),最慢用例 cred-agent 3.1s。 + +## 缺陷与修复 + +| # | 严重度 | 描述 | 修复 | +|---|--------|------|------| +| 1 | 中 | `dev app version create` 不传 `--version` 通过 CLI 校验,空版本号会直接下发上游 | `devapp.go` 补必填校验,报 `--version is required`;用例 ver-create-missing 回归通过 | + +## 复跑方式 + +```bash +go build -o /tmp/dws-dev ./cmd +python3 test/dev_functional/run_cases.py /tmp/dws-dev test/dev_functional/results.jsonl +``` + +用例与预期内联在 `run_cases.py`(id / 命令 / 期望退出码 / 输出必含子串),新增命令时在 CASES 里加行即可。 + +## 真实数据验证(2026-06-13) + +- 认证:`auth status` 真实校验通过(token 有效,corp ding8196...b85)。 +- `dev doc search` 真实调用 **3/3 PASS**(devdoc 是正常发现的 MCP 服务,不依赖 op-app 网关): + - `--query "errcode 40035"` → 返回真实文档命中(hasMore 翻页字段正常) + - 位置参数 `"机器人回调" --size 3` → 命中「机器人接收消息」等相关文档,size 生效 + - 出参结构与 output-schema.md 描述一致(`result.items[].title/url` + `success`) +- `dev app *` / `dev connect` 真实调用被阻断:devapp 固定指向 `pre-mcp-gw.dingtalk.com/server/op-app`,网关要求带 `?key=` 的完整地址(按安全设计不入库),本机日志/历史/配置均无该 key,裸地址连接被网关断开(EOF,有 recovery 事件记录)。已与负责人确认采用降级方案:真实链路留待内网环境验收,重点验 permission remove 批量真实聚合、connect 真实建联、version publish 审批链路。注入方式:`DINGTALK_DEVAPP_MCP_URL='<含key地址>' /tmp/dws-dev dev app list`。 + +## cursor 分页改造验证(2026-06-15) + +4 个分页命令(app list / permission list / version list / doc search)对外改为游标分页(`--cursor` + `--page-size`,出参注入 `nextCursor`/`hasMore`)。上游仍 page/offset,CLI 合成 cursor 顶替,上游上线真游标后透传。设计规格见 `docs/cursor-pagination-design.md`。 + +- 功能用例扩到 **106 个全 PASS**(新增 10 个 cursor 用例:解码续翻、跨命令 cursor 拒绝、非法 cursor 拒绝、legacy flag 兼容,覆盖 4 个命令)。 +- cursor 单测 7 个全 PASS(编解码往返、跨工具拒绝、非法拒绝、满页合成/到底/上游透传/nil 安全)。 +- 真实数据验证:`dev doc search` 三页连翻(`--page-size 2` → `nextCursor` 续翻),三页首条互不相同、`hasMore` 正确翻转,到底返回空 cursor。 +- dev app/permission/version 的 cursor 解码经 dry-run 验证(cursor→currentPage/offset 换算正确,跨命令 cursor 报「不属于当前命令」);真实游标续翻待内网验收。 + +## 遗留 + +- 真实上游调用(预发 endpoint + 真实凭证)未覆盖,需内部版环境验收:重点验 permission remove 批量逐条调用的真实聚合、connect 真实建联、version publish 审批链路。 +- `dws dev connect` 正式(非 dry-run)模式为前台长驻进程,不适合自动化用例,建联 e2e 需人工或专用 harness。 diff --git a/test/dev_functional/run_cases.py b/test/dev_functional/run_cases.py new file mode 100644 index 00000000..64dacba2 --- /dev/null +++ b/test/dev_functional/run_cases.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""dev 命令树功能测试执行器:并发跑用例,输出 JSONL 结果。 + +只负责机械执行与取数(跑命令、收输出、比对预期子串); +失败归因与报告判断由调用方(agent)完成。 +""" +import concurrent.futures +import json +import subprocess +import sys +import time + +DWS = sys.argv[1] if len(sys.argv) > 1 else "/tmp/dws-dev" +PARALLEL = 20 +TIMEOUT = 30 + + +# 用例格式: (id, args, expect_exit, [output 必须包含的子串]) +# expect_exit: 0=成功, 1=报错(CLI 校验/未知命令等) +U = "--unified-app-id" +CASES = [ + # ---- 根与旧名 ---- + ("root-dev-help", ["dev", "--help"], 0, ["app", "connect", "doc"]), + ("root-dev-app-help", ["dev", "app", "--help"], 0, ["credentials", "permission", "robot", "version"]), + ("root-old-devapp-removed", ["devapp", "list"], 1, []), + ("root-old-app-alias-removed", ["app", "list"], 1, []), + + # ---- app list ---- + ("list-plain", ["dev", "app", "list", "--dry-run", "--format", "json"], 0, ["list_dev_app"]), + ("list-name", ["dev", "app", "list", "--name", "Demo", "--dry-run", "--format", "json"], 0, ['"appName": "Demo"']), + ("list-app-key", ["dev", "app", "list", "--app-key", "dingxxx", "--dry-run", "--format", "json"], 0, ['"appKey": "dingxxx"']), + ("list-agent-id-removed", ["dev", "app", "list", "--agent-id", "123", "--dry-run", "--format", "json"], 1, ["unknown flag"]), + ("list-sort", ["dev", "app", "list", "--creator", "张三", "--sort", "gmt_modified", "--order", "desc", "--dry-run", "--format", "json"], 0, ['"sortType"', '"sortOrder"']), + + # ---- cursor 分页(纯透传:--cursor/--page-size 原样进 params,无合成/换算)---- + ("cursor-list-first", ["dev", "app", "list", "--page-size", "20", "--dry-run", "--format", "json"], 0, ['"pageSize": 20']), + ("cursor-list-passthrough", ["dev", "app", "list", "--cursor", "TOKEN-ABC", "--page-size", "30", "--dry-run", "--format", "json"], 0, ['"cursor": "TOKEN-ABC"', '"pageSize": 30']), + # --page 不是独立 flag,但 cobra 把它当 --page-size 的前缀缩写(唯一前缀),故 --page 2 == --page-size 2 + ("cursor-list-page-abbrev", ["dev", "app", "list", "--page", "2", "--dry-run", "--format", "json"], 0, ['"pageSize": 2']), + ("cursor-perm-passthrough", ["dev", "app", "permission", "list", "--unified-app-id", "u-1", "--cursor", "TOK-P", "--page-size", "50", "--dry-run", "--format", "json"], 0, ['"cursor": "TOK-P"', '"pageSize": 50']), + ("cursor-perm-limit-removed", ["dev", "app", "permission", "list", "--unified-app-id", "u-1", "--limit", "10", "--dry-run", "--format", "json"], 1, ["unknown flag"]), + ("cursor-version-passthrough", ["dev", "app", "version", "list", "--unified-app-id", "u-1", "--cursor", "TOK-V", "--page-size", "20", "--dry-run", "--format", "json"], 0, ['"cursor": "TOK-V"', '"pageSize": 20']), + ("cursor-doc-passthrough", ["dev", "doc", "search", "--query", "x", "--cursor", "TOK-D", "--page-size", "5", "--dry-run", "--format", "json"], 0, ['"cursor": "TOK-D"', '"pageSize": 5']), + ("cursor-doc-size-removed", ["dev", "doc", "search", "--query", "x", "--size", "5", "--dry-run", "--format", "json"], 1, ["unknown flag"]), + + # ---- app get ---- + ("get-unified", ["dev", "app", "get", U, "u-1", "--dry-run", "--format", "json"], 0, ["get_dev_app", '"unifiedAppId": "u-1"']), + ("get-agent-id-removed", ["dev", "app", "get", "--agent-id", "123", "--dry-run", "--format", "json"], 1, ["unknown flag"]), + ("get-no-locator", ["dev", "app", "get", "--dry-run", "--format", "json"], 1, []), + + # ---- app create ---- + ("create-full", ["dev", "app", "create", "--name", "Demo", "--desc", "d", "--icon", "m1", "--dry-run", "--format", "json"], 0, ["create_dev_app", '"appName": "Demo"', '"desc"', '"icon"']), + ("create-no-name", ["dev", "app", "create", "--dry-run", "--format", "json"], 1, ["--name"]), + ("create-type-removed", ["dev", "app", "create", "--name", "X", "--type", "internal", "--dry-run"], 1, ["unknown flag"]), + ("create-write-guard", ["dev", "app", "create", "--name", "Demo", "--format", "json"], 1, []), + + # ---- app update ---- + ("update-name", ["dev", "app", "update", U, "u-1", "--name", "N2", "--dry-run", "--format", "json"], 0, ["update_dev_app", '"appName": "N2"']), + ("update-no-field", ["dev", "app", "update", U, "u-1", "--dry-run", "--format", "json"], 1, ["至少提供一项"]), + ("update-no-locator", ["dev", "app", "update", "--name", "N2", "--dry-run", "--format", "json"], 1, []), + + # ---- app lifecycle ---- + ("delete-dry", ["dev", "app", "delete", U, "u-1", "--dry-run", "--format", "json"], 0, ["delete_dev_app"]), + ("disable-dry", ["dev", "app", "disable", U, "u-1", "--dry-run", "--format", "json"], 0, ["disable_dev_app"]), + ("enable-dry", ["dev", "app", "enable", U, "u-1", "--dry-run", "--format", "json"], 0, ["enable_dev_app"]), + ("delete-write-guard", ["dev", "app", "delete", U, "u-1", "--format", "json"], 1, []), + ("delete-no-confirm-name", ["dev", "app", "delete", U, "u-1", "--yes", "--format", "json"], 1, ["二次确认"]), + ("disable-no-locator", ["dev", "app", "disable", "--dry-run", "--format", "json"], 1, []), + + # ---- credentials ---- + ("cred-unified", ["dev", "app", "credentials", "get", U, "u-1", "--dry-run", "--format", "json"], 0, ["get_dev_app_credentials"]), + ("cred-agent", ["dev", "app", "credentials", "get", "--unified-app-id", "u-1", "--dry-run", "--format", "json"], 0, ['"unifiedAppId": "u-1"']), + ("cred-no-locator", ["dev", "app", "credentials", "get", "--dry-run", "--format", "json"], 1, []), + + # ---- webapp ---- + ("webapp-get", ["dev", "app", "webapp", "get", "--unified-app-id", "u-1", "--dry-run", "--format", "json"], 0, ["get_extension_webapp_config"]), + ("webapp-config", ["dev", "app", "webapp", "config", "--unified-app-id", "u-1", "--homepage-url", "https://x.invalid/m", "--dry-run", "--format", "json"], 0, ["set_extension_webapp_config", '"homepageUrl"']), + ("webapp-config-pc", ["dev", "app", "webapp", "config", U, "u-1", "--pc-homepage-url", "https://x.invalid/pc", "--omp-url", "https://x.invalid/o", "--dry-run", "--format", "json"], 0, ['"pcHomepageUrl"', '"ompUrl"']), + ("webapp-config-no-field", ["dev", "app", "webapp", "config", "--unified-app-id", "u-1", "--dry-run", "--format", "json"], 1, []), + ("webapp-config-guard", ["dev", "app", "webapp", "config", "--unified-app-id", "u-1", "--homepage-url", "https://x.invalid/m", "--format", "json"], 1, []), + + # ---- permission list ---- + ("perm-list", ["dev", "app", "permission", "list", U, "u-1", "--dry-run", "--format", "json"], 0, ["list_dev_app_permissions", '"pageSize": 20', '"authStatus": "ALL"']), + ("perm-list-page-size", ["dev", "app", "permission", "list", U, "u-1", "--page-size", "50", "--dry-run", "--format", "json"], 0, ['"pageSize": 50']), + ("perm-list-keyword", ["dev", "app", "permission", "list", U, "u-1", "--keyword", "机器人", "--status", "unauthed", "--scope-type", "sns", "--dry-run", "--format", "json"], 0, ['"keyword"', '"authStatus": "UNAUTHED"', '"scopeType": "SNS"']), + ("perm-list-scope", ["dev", "app", "permission", "list", U, "u-1", "--scope", "qyapi_robot_sendmsg", "--dry-run", "--format", "json"], 0, ['"scopeValue"']), + ("perm-list-scope-alias", ["dev", "app", "permission", "list", U, "u-1", "--permission", "qyapi_robot_sendmsg", "--dry-run", "--format", "json"], 0, ['"scopeValue"']), + ("perm-list-search-alias", ["dev", "app", "permission", "search", U, "u-1", "--dry-run", "--format", "json"], 0, ["list_dev_app_permissions"]), + ("perm-list-no-locator", ["dev", "app", "permission", "list", "--dry-run", "--format", "json"], 1, []), + + # ---- permission add ---- + ("perm-add-multi", ["dev", "app", "permission", "add", U, "u-1", "--permissions", "A,B", "--dry-run", "--format", "json"], 0, ["apply_dev_app_permissions", '"A"', '"B"']), + ("perm-add-scope-alias", ["dev", "app", "permission", "add", U, "u-1", "--scope", "A", "--dry-run", "--format", "json"], 0, ['"scopeValues"']), + ("perm-add-perm-alias", ["dev", "app", "permission", "add", U, "u-1", "--permission", "A", "--dry-run", "--format", "json"], 0, ['"scopeValues"']), + ("perm-add-missing", ["dev", "app", "permission", "add", U, "u-1", "--dry-run", "--format", "json"], 1, ["--permissions"]), + ("perm-add-guard", ["dev", "app", "permission", "add", U, "u-1", "--permissions", "A", "--format", "json"], 1, []), + + # ---- permission remove ---- + ("perm-rm-single", ["dev", "app", "permission", "remove", U, "u-1", "--permissions", "A", "--dry-run", "--format", "json"], 0, ["remove_dev_app_permissions", '"scopeValue": "A"']), + ("perm-rm-batch", ["dev", "app", "permission", "remove", U, "u-1", "--permissions", "A,B", "--dry-run", "--format", "json"], 0, ['"results"', '"scopeValue": "A"', '"scopeValue": "B"']), + ("perm-rm-old-alias", ["dev", "app", "permission", "remove", U, "u-1", "--permission", "A", "--dry-run", "--format", "json"], 0, ['"scopeValue": "A"']), + ("perm-rm-scope-alias", ["dev", "app", "permission", "remove", U, "u-1", "--scope", "A", "--dry-run", "--format", "json"], 0, ['"scopeValue": "A"']), + ("perm-rm-missing", ["dev", "app", "permission", "remove", U, "u-1", "--dry-run", "--format", "json"], 1, ["--permissions"]), + ("perm-rm-guard", ["dev", "app", "permission", "remove", U, "u-1", "--permissions", "A", "--format", "json"], 1, []), + + # ---- member ---- + ("member-list", ["dev", "app", "member", "list", U, "u-1", "--dry-run", "--format", "json"], 0, ["list_dev_app_members"]), + ("member-list-appid-removed", ["dev", "app", "member", "list", "--app-id", "u-1", "--dry-run", "--format", "json"], 1, ["unknown flag"]), + ("member-list-missing", ["dev", "app", "member", "list", "--dry-run", "--format", "json"], 1, ["--unified-app-id"]), + ("member-add", ["dev", "app", "member", "add", U, "u-1", "--users", "a,b", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 0, ["add_dev_app_members", '"memberUserIds"']), + ("member-add-trim", ["dev", "app", "member", "add", U, "u-1", "--users", " a , b ", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 0, ['"a"', '"b"']), + ("member-add-no-users", ["dev", "app", "member", "add", U, "u-1", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 1, ["--users"]), + ("member-remove", ["dev", "app", "member", "remove", U, "u-1", "--users", "a", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 0, ["remove_dev_app_members"]), + ("member-add-guard", ["dev", "app", "member", "add", U, "u-1", "--users", "a", "--member-type", "DEVELOPER", "--format", "json"], 1, []), + + # ---- security ---- + ("sec-ip", ["dev", "app", "security", "config", U, "u-1", "--ip-whitelist", "192.0.2.1,192.0.2.2", "--dry-run", "--format", "json"], 0, ["update_dev_app_security_config", '"ipWhitelist"']), + ("sec-redirect", ["dev", "app", "security", "config", U, "u-1", "--redirect-url", "https://x.invalid/cb", "--dry-run", "--format", "json"], 0, ['"redirectUrls"']), + ("sec-sso", ["dev", "app", "security", "config", U, "u-1", "--sso-url", "https://x.invalid/sso", "--dry-run", "--format", "json"], 0, ['"ssoUrls"']), + ("sec-combined", ["dev", "app", "security", "config", "--unified-app-id", "u-1", "--ip-whitelist", "192.0.2.1", "--redirect-url", "https://x.invalid/cb", "--dry-run", "--format", "json"], 0, ['"ipWhitelist"', '"redirectUrls"']), + ("sec-no-field", ["dev", "app", "security", "config", U, "u-1", "--dry-run", "--format", "json"], 1, ["至少提供一项安全配置"]), + ("sec-guard", ["dev", "app", "security", "config", U, "u-1", "--ip-whitelist", "192.0.2.1", "--format", "json"], 1, []), + + # ---- robot ---- + ("robot-submit", ["dev", "app", "robot", "submit", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批", "--task-id", "t-1", "--dry-run", "--format", "json"], 0, ["submit_robot_create_task", '"taskId": "t-1"']), + ("robot-result", ["dev", "app", "robot", "result", "--task-id", "t-1", "--format", "json", "--dry-run"], 0, ["query_robot_create_result"]), + ("robot-result-missing", ["dev", "app", "robot", "result", "--format", "json", "--dry-run"], 1, []), + ("robot-get", ["dev", "app", "robot", "get", U, "u-1", "--dry-run", "--format", "json"], 0, ["get_extension_robot_config"]), + ("robot-config-upsert", ["dev", "app", "robot", "config", U, "u-1", "--name", "小助手", "--mode", "2", "--skills", "qa,approval", "--dry-run", "--format", "json"], 0, ["set_extension_robot_config", '"skillList"']), + # update 子命令已并入 config(upsert):robot update 落到 robot 组、报错重定向到 robot --help + ("robot-update-removed", ["dev", "app", "robot", "update", U, "u-1", "--brief", "新简介", "--dry-run", "--format", "json"], 1, ["robot --help"]), + ("robot-enable-pure", ["dev", "app", "robot", "enable", U, "u-1", "--dry-run", "--format", "json"], 0, ["enable_dev_app_robot"]), + ("robot-config-no-field", ["dev", "app", "robot", "config", U, "u-1", "--dry-run", "--format", "json"], 1, []), + ("robot-disable", ["dev", "app", "robot", "disable", U, "u-1", "--dry-run", "--format", "json"], 0, ["disable_dev_app_robot"]), + ("robot-connect-removed", ["dev", "app", "robot", "connect", "--dry-run"], 1, []), + + # ---- version ---- + ("ver-create", ["dev", "app", "version", "create", U, "u-1", "--version", "1.0.1", "--desc", "d", "--dry-run", "--format", "json"], 0, ["create_dev_app_version", '"version": "1.0.1"']), + ("ver-list", ["dev", "app", "version", "list", U, "u-1", "--page-size", "20", "--dry-run", "--format", "json"], 0, ["list_dev_app_versions", '"pageSize": 20']), + ("ver-get", ["dev", "app", "version", "get", U, "u-1", "--version-id", "v-1", "--dry-run", "--format", "json"], 0, ["get_dev_app_version_detail"]), + ("ver-check", ["dev", "app", "version", "check-approval", U, "u-1", "--version-id", "v-1", "--dry-run", "--format", "json"], 0, ["publish_dev_app_version"]), + ("ver-publish", ["dev", "app", "version", "publish", U, "u-1", "--version-id", "v-1", "--approver", "user1", "--confirm-sensitive", "--dry-run", "--format", "json"], 0, ["publish_dev_app_version", '"approverUserId"', '"confirmedSensitive"']), + ("ver-publish-guard", ["dev", "app", "version", "publish", U, "u-1", "--version-id", "v-1", "--format", "json"], 1, []), + ("ver-status", ["dev", "app", "version", "status", U, "u-1", "--version-id", "v-1", "--dry-run", "--format", "json"], 0, ["get_dev_app_version_status"]), + ("event-list", ["dev", "app", "event", "list", U, "u-1", "--page-size", "10", "--dry-run", "--format", "json"], 0, ["list_dev_app_events", '"pageSize": 10']), + ("event-sub", ["dev", "app", "event", "subscribe", U, "u-1", "--event-types", "t1,t2", "--callback-url", "https://x.invalid", "--dry-run", "--format", "json"], 0, ["subscribe_dev_app_event", '"eventTypes"']), + ("event-unsub", ["dev", "app", "event", "unsubscribe", U, "u-1", "--event-types", "t1", "--dry-run", "--format", "json"], 0, ["unsubscribe_dev_app_event"]), + ("event-list-missing", ["dev", "app", "event", "list", "--dry-run", "--format", "json"], 1, []), + ("ver-create-missing", ["dev", "app", "version", "create", U, "u-1", "--dry-run", "--format", "json"], 1, []), + + # ---- dev connect ---- + ("connect-dry-flags", ["dev", "connect", "--channel", "claudecode", "--robot-client-id", "id1", "--robot-client-secret", "s1", "--dry-run", "--format", "json"], 0, ['"channel": "claudecode"', '"cli"', '"connect"']), + ("connect-dry-unified", ["dev", "connect", "--channel", "qoderwork", U, "ua-1", "--dry-run", "--format", "json"], 0, ['"credentialSource"', '"unifiedAppId": "ua-1"']), + ("connect-bad-channel", ["dev", "connect", "--channel", "nope", "--robot-client-id", "i", "--robot-client-secret", "s", "--dry-run"], 1, ["未知渠道"]), + ("connect-no-cred", ["dev", "connect", "--channel", "claudecode", "--dry-run", "--format", "json"], 1, ["--robot-client-id"]), + ("connect-agent-opts", ["dev", "connect", "--channel", "codex", "--robot-client-id", "i", "--robot-client-secret", "s", "--agent-model", "m1", "--agent-workdir", "/tmp", "--agent-memory=false", "--reply-card=false", "--dry-run", "--format", "json"], 0, ['"channel": "codex"']), + + # ---- dev doc ---- + ("doc-search", ["dev", "doc", "search", "--query", "errcode 40035", "--dry-run", "--format", "json"], 0, ["search_open_platform_docs", '"keyword": "errcode 40035"']), + ("doc-search-positional", ["dev", "doc", "search", "回调失败", "--dry-run", "--format", "json"], 0, ['"keyword": "回调失败"']), + ("doc-search-paging", ["dev", "doc", "search", "--query", "x", "--page-size", "5", "--dry-run", "--format", "json"], 0, ['"pageSize": 5']), + ("doc-search-missing", ["dev", "doc", "search", "--dry-run", "--format", "json"], 1, ["--query"]), + + # ---- pretty 出参 ---- + ("pretty-no-crash", ["dev", "app", "get", U, "u-1", "--dry-run", "--format", "pretty"], 0, []), +] + + +def run_case(case): + cid, args, want_exit, want_subs = case + started = time.time() + try: + proc = subprocess.run( + [DWS, *args], capture_output=True, text=True, timeout=TIMEOUT + ) + out = (proc.stdout or "") + (proc.stderr or "") + exit_ok = (proc.returncode == 0) == (want_exit == 0) + missing = [s for s in want_subs if s not in out] + status = "PASS" if exit_ok and not missing else "FAIL" + return { + "id": cid, "status": status, "exit": proc.returncode, + "want_exit": want_exit, "missing": missing, + "ms": int((time.time() - started) * 1000), + "cmd": "dws " + " ".join(args), + "output_head": out[:600], + } + except subprocess.TimeoutExpired: + return {"id": cid, "status": "TIMEOUT", "ms": TIMEOUT * 1000, + "cmd": "dws " + " ".join(args), "output_head": ""} + + +def main(): + results = [] + with concurrent.futures.ThreadPoolExecutor(max_workers=PARALLEL) as pool: + for res in pool.map(run_case, CASES): + results.append(res) + print(f"[{res['status']}] {res['id']}", file=sys.stderr) + with open(sys.argv[2] if len(sys.argv) > 2 else "results.jsonl", "w") as f: + for r in results: + f.write(json.dumps(r, ensure_ascii=False) + "\n") + passed = sum(1 for r in results if r["status"] == "PASS") + print(f"\n{passed}/{len(results)} PASS", file=sys.stderr) + + +if __name__ == "__main__": + main() From f58b426381a01f646c64ed1279cbd4a3d04daf50 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:11:32 +0800 Subject: [PATCH 18/47] =?UTF-8?q?chore(dev):=20=E5=88=A0=E9=99=A4=E6=97=A7?= =?UTF-8?q?=20skills/dingtalk-devapp(root=20=E6=A8=A1=E5=BC=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devapp 旧技能整组下线,已被 multi/dingtalk-dev 替代。 Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/dingtalk-devapp/SKILL.md | 56 ------ skills/dingtalk-devapp/references/app-crud.md | 124 ------------ .../references/credentials-webapp.md | 55 ----- .../references/member-security.md | 51 ----- .../dingtalk-devapp/references/permissions.md | 96 --------- skills/dingtalk-devapp/references/robot.md | 189 ------------------ skills/dingtalk-devapp/references/version.md | 129 ------------ .../dingtalk-devapp/references/workflows.md | 177 ---------------- 8 files changed, 877 deletions(-) delete mode 100644 skills/dingtalk-devapp/SKILL.md delete mode 100644 skills/dingtalk-devapp/references/app-crud.md delete mode 100644 skills/dingtalk-devapp/references/credentials-webapp.md delete mode 100644 skills/dingtalk-devapp/references/member-security.md delete mode 100644 skills/dingtalk-devapp/references/permissions.md delete mode 100644 skills/dingtalk-devapp/references/robot.md delete mode 100644 skills/dingtalk-devapp/references/version.md delete mode 100644 skills/dingtalk-devapp/references/workflows.md diff --git a/skills/dingtalk-devapp/SKILL.md b/skills/dingtalk-devapp/SKILL.md deleted file mode 100644 index 5019efe1..00000000 --- a/skills/dingtalk-devapp/SKILL.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -name: dingtalk-devapp -description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 -cli_version: ">=1.0.15" -metadata: - category: product - stability: experimental - requires: - bins: - - dws ---- - -# 钉钉开放平台应用管理 Skill - -> `dws devapp ...` 是内置 helper 命令,不依赖 MCP 服务发现。执行前用 `dws devapp --help` 验证可用。 - -## 意图消歧 - -`应用` 是泛词。只有出现以下信号才路由到 devapp: - -- `开放平台应用/开发者后台应用/企业内部应用/内部应用` -- `agentId/clientId/appKey/appSecret/customKey` -- `应用权限/权限点/scopeValue/应用成员/安全配置/IP 白名单` -- `创建机器人/智能体机器人/机器人配置/机器人回调地址`(→ `robot`) -- `应用版本/版本发布/发布审核/选审批人`(→ `version`) - -不要路由到 devapp 的:接口文档→`devdoc`、钉钉文档→`doc`、工作台应用→`workbench app`、审批→`oa`。只说 `应用` 无上下文时先追问。 - -## 核心规则 - -1. 所有命令加 `--format json`。 -2. 写操作先 `--dry-run`,确认后才加 `--yes`。 -3. 应用名/appKey/customKey 命中多条时展示候选,不取第一条。 -4. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名。 -5. 主动读取密钥走 `credentials get`;任何 `devapp get`/详情返回里的 `clientSecret/appSecret` 都按敏感凭证脱敏,不向用户展开。 - -## 开放平台文档 RAG / 错误码排查 - -- 任何产品执行中,只要用户问开放平台 API、接口参数、字段含义、权限点、回调、SDK、配额、错误码,或命令返回上游 OpenAPI/SDK 错误,必须先用 `dws devdoc article search --query "<关键词>" --format json` 做官方文档 RAG。 -- 查询词优先保留原始 API 名、能力名、权限点、完整错误码和 message;首轮形如 `errcode `,无结果再换 `<产品/场景> <错误码>`、`<接口名> 参数`。 -- 本地 CLI 错误(如 `unknown command` / `unknown flag` / 认证 / recovery)仍按 root `dws` skill 的错误处理执行;`devdoc` 用于开放平台业务错误码和接口语义排查。 -- `devdoc` 只查钉钉开放平台开发者文档,不查业务数据;排查结论必须基于命中条目的标题、摘要或链接,不能编造错误原因或不存在的命令。 - -## 渐进式参考 - -按使用频率和复杂度分层,按需加载: - -| 层 | 参考文档 | 覆盖命令 | -|----|---------|---------| -| 基础 | [app-crud.md](references/app-crud.md) | list / get / create / update / delete / inactive / active | -| 凭证与网页应用 | [credentials-webapp.md](references/credentials-webapp.md) | credentials get / webapp get / webapp config | -| 权限管理 | [permissions.md](references/permissions.md) | permission list / permission add / permission remove | -| 成员与安全 | [member-security.md](references/member-security.md) | member list / add / remove / security config | -| 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / offline | -| 版本发布 | [version.md](references/version.md) | version create / list / get / check-approval / publish / status | -| 操作流程 | [workflows.md](references/workflows.md) | 创建应用全流程 / 权限全流程 / 生命周期管理 | diff --git a/skills/dingtalk-devapp/references/app-crud.md b/skills/dingtalk-devapp/references/app-crud.md deleted file mode 100644 index 7898e729..00000000 --- a/skills/dingtalk-devapp/references/app-crud.md +++ /dev/null @@ -1,124 +0,0 @@ -# 应用基础操作 - -应用列表查询、详情、创建、修改、生命周期启停和删除。 - -## 应用定位 - -写操作前必须定位到唯一应用: - -| 优先级 | 标识 | 处理 | -|--------|------|------| -| 1 | `--unified-app-id` | 直接使用 | -| 2 | `--agent-id` / `--app-id` | 直接使用 | -| 3 | `--app-key` / `--custom-key` | 先查询,唯一命中才继续 | -| 4 | `--name` | 模糊搜索,写操作必须唯一命中 | - -## 应用列表 - -```bash -dws devapp list --format json -dws devapp list --name DemoApp --format json -dws devapp list --agent-id 123456 --format json -dws devapp list --creator 张三 --sort gmt_modified --order desc --format json -``` - -MCP tool: `list_open_dev_apps_by_condition` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--page` | `currentPage` | 1-based,默认 1 | -| `--page-size` | `pageSize` | 默认 20 | -| `--name` / `--keyword` | `appName` | 应用名搜索 | -| `--agent-id` | `agentId` | 精确定位 | -| `--app-key` | `appKey` | appKey/clientId | -| `--creator` | `creator` | 创建人关键词 | -| `--sort` | `sortType` | 如 `gmt_modified` | -| `--order` | `sortOrder` | `asc` / `desc` | - -## 应用状态字段 - -列表/详情原始字段里如果出现 `status` / `appStatus`,按应用生命周期枚举判断;不要和版本 `versionStatus` 混用。 - -| status | 枚举 | 含义 | 下一步 | -|--------|------|------|--------| -| `0` | `IN_ACTIVE` | 已停用,应用不可用 | 需要恢复时走 `active --dry-run` → 确认 → `--yes` | -| `1` | `ACTIVE` | 已激活,应用可用 | 可继续配置权限、网页应用、机器人或版本 | -| `2` | `WAIT_ACTIVE` | 待激活 | 先回读 `get/list` 确认状态;不要直接按已生效处理 | -| `3` | `EXPIRED` | 已过期 | 停止写操作,提示用户到开发者后台或管理员侧处理 | - -`create/update` 返回的 `versionStatus` 是版本状态,语义见 `version.md`;它不等同于应用启停状态。 - -## 应用详情 - -```bash -dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --agent-id 123456 --format json -``` - -MCP tool: `get_open_dev_app_detail` - -详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 - -## 创建应用 - -```bash -dws devapp create --name DemoApp --desc "内部应用" --type internal --dry-run --format json -dws devapp create --name DemoApp --desc "内部应用" --type internal --yes --format json -``` - -MCP tool: `create_inner_app` - -| CLI | MCP | 必填 | -|-----|-----|------| -| `--name` | `appName` | 是 | -| `--desc` | `appDesc` | 否 | -| `--icon` | `appIcon` | 否 | - -`--type` 只做 CLI 校验(当前仅支持 `internal`),不下发 MCP。 - -## 修改应用 - -```bash -dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-run --format json -dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --yes --format json -``` - -MCP tool: `update_inner_app` - -至少提供一个更新字段:`--name` / `--desc` / `--icon`。 - -## 停用 / 启用应用 - -```bash -dws devapp inactive --unified-app-id ID --dry-run --format json -dws devapp inactive --unified-app-id ID --yes --format json - -dws devapp active --unified-app-id ID --dry-run --format json -dws devapp active --unified-app-id ID --yes --format json -``` - -MCP tools: `inactive_inner_app` / `active_inner_app` - -停用保留数据但应用不可用,可通过 `active` 恢复。 - -执行 `inactive/active` 后必须回读 `get` 或 `list`:看到 `status=0` 才算停用完成,看到 `status=1` 才算启用完成;如果接口只返回操作成功但未带状态,向用户说明需要以回读结果为准。 - -## 删除应用 - -```bash -dws devapp delete --unified-app-id ID --dry-run --format json -dws devapp delete --unified-app-id ID --yes --format json -``` - -MCP tool: `delete_inner_app` - -删除前必须展示应用摘要。删除为异步操作,成功后应用延迟从列表消失。 - -## 错误处理 - -| 情况 | 处理 | -|------|------| -| `unknown command` | CLI 构建不含 devapp helper | -| `endpoint_not_resolved` | 检查 edition endpoint 注入 | -| 多应用命中 | 展示候选,停止写操作 | -| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/dingtalk-devapp/references/credentials-webapp.md b/skills/dingtalk-devapp/references/credentials-webapp.md deleted file mode 100644 index 0f1dec02..00000000 --- a/skills/dingtalk-devapp/references/credentials-webapp.md +++ /dev/null @@ -1,55 +0,0 @@ -# 凭证读取与网页应用配置 - -## 凭证读取 - -```bash -dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --agent-id 123456 --format json -``` - -MCP tool: `get_open_dev_app_credentials` - -后端 facade: `OpenInnerAppQueryFacade.getCredentials`。 - -**规则:** -- CLI 只传应用定位字段,不传 `showSecret`/`confirmSecret`。 -- 返回可能包含 `clientSecret/appSecret`,输出按敏感凭证处理。 -- 不能用 `devapp get` 代替;如果 `devapp get` 偶尔返回密钥,也只用于内部判断并脱敏,不向用户展开。 - -关键返回字段: - -| 字段 | 说明 | -|------|------| -| `clientId` / `appKey` | 非密钥标识 | -| `clientSecret` / `appSecret` | 敏感凭证 | -| `currentSecretStatus` | 当前密钥状态 | -| `pendingExpireTask` | 密钥过期任务信息 | - -## 网页应用查询 - -```bash -dws devapp webapp get --agent-id 123456 --format json -dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json -``` - -MCP tool: `get_webapp_config` - -未配置网页应用前可能只返回 `agentId`。 - -## 网页应用配置 - -```bash -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --dry-run --format json -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --pc-homepage-link https://example.com/pc --yes --format json -``` - -MCP tool: `set_webapp_config` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--h5-page-type` | `h5PageType` | 网页应用生效端 | -| `--homepage-link` | `homepageLink` | 移动端首页地址 | -| `--pc-homepage-link` | `pcHomepageLink` | PC 端首页地址 | -| `--omp-link` | `ompLink` | 管理后台地址 | - -至少提供一个配置字段。`h5PageType` 未显式传入时,不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 diff --git a/skills/dingtalk-devapp/references/member-security.md b/skills/dingtalk-devapp/references/member-security.md deleted file mode 100644 index 8c2b716c..00000000 --- a/skills/dingtalk-devapp/references/member-security.md +++ /dev/null @@ -1,51 +0,0 @@ -# 成员管理与安全配置 - -## 成员查询 - -```bash -dws devapp member list --app-id UNIFIED_APP_ID --format json -``` - -MCP tool: `list_open_dev_app_members` - -## 添加成员 - -```bash -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json -``` - -MCP tool: `add_open_dev_app_members` - -## 移除成员 - -```bash -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json -``` - -MCP tool: `remove_open_dev_app_members` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | -| `--users` | `memberUserIds` | userId 列表,逗号分隔(必填) | -| `--member-type` | `memberType` | 成员类型,如 `DEVELOPER`(必填) | - -## 安全配置 - -```bash -dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json -dws devapp security config --app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json -``` - -MCP tool: `update_app_security_config` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | -| `--ip-whitelist` | `ipWhiteList` | 出口 IP 白名单,逗号/分号分隔 | -| `--redirect-url` | `redirectUrls` | 登录重定向 URL,逗号/分号分隔 | -| `--sso-url` | `otherAuthUrls` | 端内免登 URL,逗号/分号分隔 | - -至少提供一个配置字段。只下发显式提供的字段,未提供的不覆盖。 diff --git a/skills/dingtalk-devapp/references/permissions.md b/skills/dingtalk-devapp/references/permissions.md deleted file mode 100644 index 17cc6d95..00000000 --- a/skills/dingtalk-devapp/references/permissions.md +++ /dev/null @@ -1,96 +0,0 @@ -# 权限管理 - -查询、申请、取消开放平台应用的 APP 应用权限和 SNS 个人权限。 - -## 权限列表 - -```bash -dws devapp permission list --unified-app-id ID --format json -dws devapp permission list --unified-app-id ID --keyword "机器人发消息" --status UNAUTHED --format json -dws devapp permission list --unified-app-id ID --scope-type SNS --format json -dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json -``` - -MCP tool: `list_open_dev_app_permissions` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--unified-app-id` | `unifiedAppId` | 应用定位 | -| `--agent-id` | `agentId` | 应用定位 | -| `--keyword` | `keyword` | 权限名/API 名关键词 | -| `--status` | `authStatus` | `ALL` / `AUTHED` / `UNAUTHED` | -| `--scope-type` | `firstLevelType` | `APP` / `SNS`,为空返回两者 | -| `--scope` | `scopeValue` | 单权限详情模式 | -| `--limit` | `limit` | 每页返回数量,默认 20,建议不超过 50 | -| `--offset` | `offset` | 翻页偏移量,默认 0 | - -**状态判断:** - -`--status` 是查询过滤条件: - -| authStatus | 含义 | -|------------|------| -| `ALL` | 不按授权状态过滤 | -| `AUTHED` | 只看已授权/已开通的权限点 | -| `UNAUTHED` | 只看未授权/未开通的权限点 | - -单个权限项返回的 `status` 是内部操作态: - -| status | 枚举 | 含义 | 下一步 | -|--------|------|------|--------| -| `0` | `STATUS_OBTAINED` | 权限已获得 | 不要重复申请;如需取消,确认 `canRemove=true` 后走 `permission remove` | -| `1` | `STATUS_APPLYING` | 权限申请中 | 不要重复申请;查看 `authedStatusDesc`,通常等待审批或版本发布 | -| `2` | `STATUS_CAN_APPLY` | 权限可以申请 | 可走 `permission add --dry-run` | -| `3` | `STATUS_CAN_NOT_APPLY` | 权限不可以申请 | 停止申请,展示 `applyDisabledReason/displayMessage` | - -`authedStatusDesc` 是给用户看的细分状态:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED` 表示已开通、已申请或待发布;`NOT_OPEN`/`NOT_APPLIED` 表示未开通/未申请;`AUDIT_PROCESSING` 表示审批中;`AUDIT_REFUSE` 表示审批未通过。判断能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 - -**翻页:** - -权限列表支持 `--limit` + `--offset` 分页。一个应用可能有 150+ 个权限点,一次查不完时用 offset 翻页: - -```bash -dws devapp permission list --unified-app-id ID --limit 50 --format json # 第 1 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 50 --format json # 第 2 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 100 --format json # 第 3 页 -``` - -当返回条数 < limit 时表示已到末尾。 - -**规则:** -- `permission search` 和 `permission detail` 是 `list` 的 CLI 别名,不是独立 MCP tool。 -- 默认同时返回 APP 和 SNS 权限。 -- 列表模式只返回 `apiPreview`;`--scope` 详情模式返回完整 `apiList`。 - -**scopeValue 选择顺序:** - -1. 用户给了 `scopeValue` → 精确匹配 -2. 用户给了 API 名 → `keyword` 搜索,匹配 `apiPreview.name` -3. 用户给了权限名 → 匹配 `scopeName/scopeDesc` -4. 多个候选 → 展示列表让用户选择,不自动取第一条 - -## 申请权限 - -```bash -dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json -dws devapp permission add --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --yes --format json -``` - -MCP tool: `apply_open_dev_app_permissions` - -**规则:** -- `--permissions` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 -- 已开通跳过,不可编辑拒绝。 -- `requiredApproval=true` 允许申请——写入版本变更,审批在版本发布时处理。 -- 不在此处选审批人。 - -## 取消权限 - -```bash -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --dry-run --format json -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --yes --format json -``` - -MCP tool: `remove_open_dev_app_permission` - -一次只取消一个权限点。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md deleted file mode 100644 index 11b51755..00000000 --- a/skills/dingtalk-devapp/references/robot.md +++ /dev/null @@ -1,189 +0,0 @@ -# 机器人能力 - -为开放平台企业内部应用创建和配置机器人。分两类场景: - -1. **新建智能体机器人**:一次性创建一个新的 Agent 应用 + 承载机器人(`create` / `submit` / `result`)。 -2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `update` / `enable` / `offline`),通过 `--unified-app-id` 定位。 - -> `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 - -## 一、新建智能体机器人 - -### 同步创建 - -```bash -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json -``` - -MCP tool: `create_dingtalk_robot`。成功返回 `agentId / robotCode / clientId / clientSecret`(凭据按敏感信息处理)。 - -| CLI | MCP | 必填 | 说明 | -|-----|-----|------|------| -| `--app-name` | `appName` | 是 | 智能体应用名称,长度 2-20,企业内唯一 | -| `--robot-name` | `robotName` | 是 | 承载机器人名称,客户端展示 | -| `--desc` | `desc` | 是 | 机器人功能描述,≤200 字 | -| `--icon` | `robotMediaId` | 否 | 图标 mediaId;空则用默认图标 | -| `--preview` | `previewMediaId` | 否 | 预览图 mediaId;空则复用图标 | - -### 异步创建 + 查询结果 - -适合创建耗时较长或需要失败重试的场景。 - -```bash -# 提交任务 -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json -# → 返回 taskId - -# 轮询结果 -dws devapp robot result --task-id --format json -``` - -MCP tools: `submit_robot_create_task` / `query_robot_create_result` - -- `submit` 返回 `taskId / status / expiresIn / interval / retryCount`,提交成功后通常是 `WAITING`。 -- 失败重试:把上次的 `taskId` 通过 `--task-id` 传入 `submit`,避免重复创建。 -- `result` 返回任务状态;只有 `SUCCESS` 时才能使用返回的 `agentId / robotCode / clientId / clientSecret`。 - -异步创建任务状态: - -| status | 含义 | 下一步 | -|--------|------|--------| -| `WAITING` | 任务已提交,仍在创建中 | 按 `interval` 轮询 `robot result` | -| `SUCCESS` | 创建完成 | 保存 `robotCode/clientId/clientSecret`,凭据按敏感信息处理 | -| `APPROVAL_REQUIRED` | 创建编排返回需审批 | 不要重复建号;按返回信息或开发者后台审批后再继续 | -| `FAIL` | 创建失败 | 读取 `errorCode/errorMsg/failReason`;可带原 `taskId` 重新 `submit` | -| `EXPIRED` | `taskId` 不存在或超过有效期 | 重新 `submit`,必要时换新 `taskId` | - -## 二、现有应用的机器人配置 - -### 查询配置 - -```bash -dws devapp robot get --unified-app-id --format json -``` - -MCP tool: `get_open_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 - -状态判断: - -- `status=1`:OFFLINE,机器人配置存在但处于停用/下线状态。 -- `status=2`:ONLINE,机器人配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 -- `robot get` 返回 `success=true` 且包含 `robotCode` 时,说明配置已落库,不是异步等待态。 -- ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-url`,或用 `robot connect` 接到本地 Agent。 -- 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 - -### 创建 / 更新 / 启用配置 - -三者字段一致,区别在语义:`config`=首次创建、`update`=修改、`enable`=启用/重新启用。 - -首次 `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为“待生效”;只有 `status=1` 或需要重新上架时才调用 `enable`。 - -```bash -dws devapp robot config --unified-app-id --name 小助手 --brief 审批助手 \ - --description "处理审批相关问答" --outgoing-url https://example.com/msg \ - --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json - -dws devapp robot update --unified-app-id --brief "新的简介" --dry-run --format json - -dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json -``` - -MCP tools: `create_open_dev_app_robot_config` / `update_open_dev_app_robot_config` / `enable_open_dev_app_robot` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--name` | `name` | 机器人名称 | -| `--brief` | `brief` | 简介 | -| `--description` | `description` | 描述 | -| `--icon` | `iconMediaId` | 图标 mediaId | -| `--outgoing-url` | `outgoingUrl` | 消息回调地址 | -| `--event-url` | `chatBotEventUrl` | 事件回调地址 | -| `--mode` | `mode` | 机器人模式枚举(整数) | -| `--skills` | `skillList` | 技能列表,逗号分隔 | -| `--add-scope` | `isAddScope` | 自动添加机器人相关权限 | -| `--disable-ssl-verify` | `disableSSLVerify` | 回调关闭 SSL 校验 | -| `--i18n-name` | `i18nName` | 名称国际化 JSON,如 `'{"en_US":"Bot"}'` | -| `--i18n-brief` | `i18nBrief` | 简介国际化 JSON | -| `--i18n-description` | `i18nDescription` | 描述国际化 JSON | - -至少提供一个配置字段,否则 CLI 报错。 - -### 停用 - -```bash -dws devapp robot offline --unified-app-id --dry-run --format json -dws devapp robot offline --unified-app-id --yes --format json -``` - -MCP tool: `offline_open_dev_app_robot` - -### 建联(把机器人接到本地 agent) - -```bash -# 用现成机器人凭证起 Stream,接到当前渠道的本地 agent(前台运行,Ctrl-C 退出) -dws devapp robot connect --channel auto --robot-client-id --robot-client-secret - -# 用统一应用 ID,复用 credentials get 自动取凭证 -dws devapp robot connect --unified-app-id --channel qoderwork - -# Codex 渠道推荐显式覆盖 agent 命令;否则默认空白工作目录可能触发 -# "Not inside a trusted directory and --skip-git-repo-check was not specified" -CODEX_BIN="$(command -v codex || echo /Applications/Codex.app/Contents/Resources/codex)" -DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check" \ - dws devapp robot connect --unified-app-id --channel codex --format json - -# 预览建联方案不实际起连接 -dws devapp robot connect --robot-client-id --robot-client-secret --dry-run --format json -``` - -只建联、不建号:缺凭证先用 `robot create` / `robot submit` 建号拿 clientId/clientSecret。 - -Codex 渠道注意: - -- `--channel codex` 真实建联时,优先使用上面的 `DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check"` 形式,避免 Codex CLI 在默认临时目录内因非可信 Git 目录拒绝执行。 -- 如需给 Codex 固定知识/项目上下文,可把可信目录直接写进覆盖命令:`DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check -C /path/to/repo"`。路径不要包含空格。 -- 设置 `DWS_AGENT_CMD` 后,DWS 不再自动拼接 `--agent-model` / `--agent-workdir` / 会话参数;需要的参数要一起写进 `DWS_AGENT_CMD`。 - -| flag | 说明 | -|------|------| -| `--channel` | `auto`(默认,运行时信号自动识别) / openclaw / qoder / qoderwork / hermes / workbuddy / claudecode / codebuddy / codex / gemini / opencode | -| `--robot-client-id` / `--robot-client-secret` | 现成机器人凭证(clientId=AppKey, clientSecret=AppSecret)。命名带 `robot-` 前缀以避开全局 OAuth `--client-id` flag | -| `--unified-app-id` | 统一应用 ID,内部调 `get_open_dev_app_credentials` 自动取凭证。⚠️ 字段名待预发真机验证;clientSecret 仅建号时返回一次、未必可取,必要时回退手填 | -| `--agent-memory` | 按会话续聊(默认开):同一群/单聊共享 agent 会话,追问保留上下文。仅 claudecode/codebuddy/workbuddy(CLI 有 `--session-id`/`--resume`);qoder 系/codex/gemini/opencode 无寻址会话,自动保持无状态。`--agent-memory=false` 关闭 | -| `--agent-model` | 覆盖本地 agent 模型(如 claudecode 默认锁 haiku 求快,可改 `claude-sonnet-4-6` 换聪明)。env: `DWS_AGENT_MODEL` | -| `--agent-workdir` | agent 运行目录:放知识文件(如 CLAUDE.md)可给机器人企业上下文。默认空白临时目录(冷启动快 ~4s vs 大目录 ~29s,慢了会错过钉钉响应窗口)。env: `DWS_AGENT_WORKDIR` | -| `--reply-card` | 富回复(默认开):🤔Thinking/🥳Done 表态永远生效;**卡片需配 `--card-template` 才启用**(同 hermes:没配模板=纯文字回复),失败自动回退文字;env `DWS_REPLY_CARD=0` 全关 | -| `--card-template` | AI 卡片模板 ID。**模板按应用授权**:去开发者后台→你的应用→AI 卡片设置注册/获取模板 ID(同 hermes 的配置方式),可去掉公共模板的第三方角标;默认用公共模板 best-effort。env `DWS_CARD_TEMPLATE` | - -#### 建联前的依赖预检(agent 必做) - -渠道背后是本地 agent CLI,用户可能没装。先 `--dry-run` 看 `cli` 字段再决定下一步: - -```bash -dws devapp robot connect --channel --robot-client-id x --robot-client-secret y --dry-run --format json -# 输出里的 cli 字段: -# "cli": {"required":"Claude Code","installed":false,"autoInstall":true,"installHint":"npm i -g @anthropic-ai/claude-code"} -``` - -| cli 状态 | agent 应该做什么 | -|----------|----------------| -| `installed: true` | 直接建联 | -| `installed: false, autoInstall: true` | 告知用户缺哪个 CLI,说明启动建联时会自动 `npm` 安装(或先手动执行 installHint 里的命令再连);`DWS_CONNECT_NO_INSTALL=1` 可禁自动安装 | -| `installed: false, autoInstall: false` | **不要直接起连接**——桌面 App 渠道(qoder/qoderwork/workbuddy)需要用户先安装对应 App(installHint 是下载地址),装好后 CLI 随 App 自带;openclaw/hermes 引导用户走官方 onboarding | - -- **stream-bridge 渠道**:Go 原生进程内 Stream 转发器,订阅 `TOPIC_ROBOT`,每条 @机器人消息起一个无头 CLI 实例 → stdout 回钉钉,可 7×24 无人值守。 -- **会话记忆**:首条消息 `--session-id ` 建会话,后续 `--resume ` 续聊;会话状态在内存里,连接器重启后从新会话开始;某会话坏了会自愈(下条消息换新会话)。 -- **官方渠道**(openclaw/hermes):dws 不代建机器人,输出官方 onboarding 指引。 -- 环境覆盖:`DWS_AGENT_CMD`(整条命令覆盖,覆盖时不再注入模型/会话参数) / `DWS_AGENT_MODEL` / `DWS_AGENT_WORKDIR` / `DWS_CONNECT_CMD` / `DWS_CONNECT_NO_INSTALL=1` / `DWS_AGENT_TIMEOUT_MS`。 - -## 错误处理 - -| 情况 | 处理 | -|------|------| -| `robot info is not exist` | 应用未配置机器人,先用 `robot config` 创建 | -| 应用名重复 | `app-name` 企业内需唯一,换个名字 | -| 本地 Codex agent 返回 `Not inside a trusted directory and --skip-git-repo-check was not specified` | 用 `DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check"` 重启 `robot connect --channel codex` | -| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | -| 创建任务 `EXPIRED` | 任务过期,重新 `submit`(可带原 taskId 重试) | diff --git a/skills/dingtalk-devapp/references/version.md b/skills/dingtalk-devapp/references/version.md deleted file mode 100644 index 0998ec67..00000000 --- a/skills/dingtalk-devapp/references/version.md +++ /dev/null @@ -1,129 +0,0 @@ -# 版本发布 - -管理开放平台企业内部应用的版本:基于当前配置创建版本、查看版本列表/详情、预检审批、发布、查状态。 - -> `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有版本通过 `--unified-app-id` 定位,单个版本再加 `--version-id`。 - -## 典型流程 - -```text -permission add(requiredApproval=true 写入版本变更) - → version create 创建版本 - → version check-approval 预检是否需要审批 / 审批人 - → version publish 发布(含高敏权限需 --confirm-sensitive) - → version status 轮询发布/审批状态 -``` - -## 创建版本 - -```bash -dws devapp version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --dry-run --format json -dws devapp version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json -``` - -MCP tool: `create_open_dev_app_version` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--version` | `version` | 版本号,如 1.0.1 | -| `--desc` | `description` | 版本描述 | - -## 版本列表 - -```bash -dws devapp version list --unified-app-id --page 1 --page-size 20 --format json -``` - -MCP tool: `list_open_dev_app_versions`(`--page`→`currentPage`,`--page-size`→`pageSize`) - -新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 - -## 版本详情 - -```bash -dws devapp version get --unified-app-id --version-id --format json -``` - -MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 - -## 预检审批(不发布) - -```bash -dws devapp version check-approval --unified-app-id --version-id --format json -``` - -MCP tool: `publish_open_dev_app_version`,CLI 强制 `dryRun=true`,仅返回审批要求和候选审批人,**不会实际发布**。 - -## 发布 - -```bash -dws devapp version publish --unified-app-id --version-id --dry-run --format json -dws devapp version publish --unified-app-id --version-id --yes --format json - -# 含高敏权限时必须确认 -dws devapp version publish --unified-app-id --version-id --confirm-sensitive --yes --format json - -# 灰度选人模式指定审批人 -dws devapp version publish --unified-app-id --version-id --approver --yes --format json -``` - -MCP tool: `publish_open_dev_app_version`,CLI 设 `dryRun=false`。 - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--confirm-sensitive` | `confirmedSensitive` | 版本含高敏权限时必须确认 | -| `--approver` | `approverUserId` | 灰度选人模式指定审批人 userId | - -> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`(对应 `dryRun=true`)。二者不同,发布前建议先 `check-approval`。 - -发布响应 `result`: - -| result | 含义 | 下一步 | -|--------|------|--------| -| `NOT_REQUIRED` | 不需要审批;预检时表示可直接发布,真实发布时表示已直接发布上线 | 真实发布后回读 `version status/get` 验证 `RELEASE` | -| `APPROVAL_REQUIRED` | 需要审批,通常出现在 `check-approval` 或未指定审批人时 | 查看 `approvalMode/approvalCandidates`,让用户选择审批人后再 `publish --approver` | -| `SUBMITTED` | 已提交审批 | 保存 `processId`,轮询 `version status` | - -审批模式 `approvalMode`: - -| approvalMode | 含义 | 下一步 | -|--------------|------|--------| -| `SELECT_APPROVER` | 灰度选人模式,需要在候选审批人中选一个 | 展示候选审批人,不自动选第一个 | -| `ENTERPRISE_SELF_BUILT` | 企业自建审核模式 | 不传 `--approver`,按企业自建审核流程等待 | - -## 版本状态 - -```bash -dws devapp version status --unified-app-id --version-id --format json -``` - -MCP tool: `get_open_dev_app_version_status`。返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 - -版本状态字段:`version create/list/get` 返回 `status`,`version status` 返回 `versionStatus`,二者都对齐版本枚举。 - -| status/versionStatus | 含义 | 下一步 | -|----------------------|------|--------| -| `INIT` | 版本已创建或有待发布变更,尚未发布 | 可 `check-approval` / `publish` | -| `AUDIT` | 发布审核中 | 不要重复发布;即使没有返回 `processStatus`,也按审核中处理 | -| `RELEASE` | 已发布生效 | 发布完成,可继续验证权限、机器人、网页应用等能力 | -| `GRAY` | 灰度状态 | 按灰度流程处理;不要当全量已发布 | - -审批流程状态字段:`version status` 的 `processStatus` 只在存在审批流程且后端透出流程态时有值。`versionStatus=AUDIT` 但没有 `processStatus/processInstanceId` 时,不要判失败,仍表示审核中。 - -| processStatus | 含义 | 下一步 | -|---------------|------|--------| -| `UNDER_REVIEW` | 审批中 | 等待审批,必要时把 `processInstanceId` 给用户去钉钉客户端查看 | -| `PASS` | 审批通过 | 继续回读版本,确认是否进入 `RELEASE` | -| `FAIL` | 审批拒绝 | 展示 `processComment`,修改后重新创建/发布版本 | -| `WITHDRAW` / `CANCEL` | 审批撤回或取消 | 回到发布前状态,重新 `check-approval` / `publish` | -| `PUBLISH_FAILED` | 审批后发布失败 | 展示错误信息,重新检查版本状态和后端错误 | - -遇到未列出的状态值时,不要猜测语义;原样展示状态,并回读 `version get/status` 或查开放平台文档/后台详情。 - -## 错误处理 - -| 情况 | 处理 | -|------|------| -| `check-approval` 提示需审批 | 按返回选审批人,再 `publish --approver` | -| 发布报高敏权限未确认 | 加 `--confirm-sensitive` 重新发布 | -| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/dingtalk-devapp/references/workflows.md b/skills/dingtalk-devapp/references/workflows.md deleted file mode 100644 index 9347fbee..00000000 --- a/skills/dingtalk-devapp/references/workflows.md +++ /dev/null @@ -1,177 +0,0 @@ -# 操作流程 - -端到端操作流程,组合多个命令完成完整业务场景。 - -## 创建应用并完成基础配置 - -```text -1. 创建应用 - dws devapp create --name "应用名" --desc "描述" --type internal --dry-run --format json - → 确认后加 --yes - → 记录返回的 unifiedAppId 和 agentId - -2. 确认创建成功 - dws devapp get --unified-app-id --format json - -3. 读取凭证(获取 appKey/appSecret) - dws devapp credentials get --unified-app-id --format json - -4. 配置网页应用(按需) - dws devapp webapp config --agent-id --homepage-link --dry-run --format json - → 确认后加 --yes - -5. 验证网页应用配置 - dws devapp webapp get --agent-id --format json - → 确认返回 homepageLink/pcHomepageLink 等 - -6. 申请权限(按需) - dws devapp permission list --unified-app-id --keyword "关键词" --format json - → 从返回中选择 scopeValue - dws devapp permission add --unified-app-id --permissions --dry-run --format json - → 确认后加 --yes - -7. 添加开发成员(按需) - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json - → 确认后加 --yes - -8. 验证成员 - dws devapp member list --app-id --format json - -9. 配置安全设置(按需) - dws devapp security config --app-id --ip-whitelist --redirect-url --dry-run --format json - → 确认后加 --yes -``` - -## 权限管理全流程 - -```text -1. 查看所有权限 - dws devapp permission list --unified-app-id --format json - -2. 按关键词搜索未开通权限 - dws devapp permission list --unified-app-id --keyword "机器人" --status UNAUTHED --format json - -3. 查看某个权限覆盖的 API - dws devapp permission list --unified-app-id --scope --format json - -4. 申请权限 - dws devapp permission add --unified-app-id --permissions --dry-run --format json - → 确认后加 --yes - → requiredApproval=true 的权限:写入版本变更,需后续走版本发布审核 - -5. 验证 - dws devapp permission list --unified-app-id --scope --format json - → 确认 authed=true - -6. 取消不需要的权限 - dws devapp permission remove --unified-app-id --permission --dry-run --format json - → 确认后加 --yes -``` - -## 应用生命周期 - -```text -停用(保留数据,暂停服务): - dws devapp get --unified-app-id --format json ← 确认目标 - dws devapp inactive --unified-app-id --dry-run --format json - → 确认后加 --yes - dws devapp get --unified-app-id --format json ← 验证状态 - -启用(恢复已停用应用): - dws devapp active --unified-app-id --dry-run --format json - → 确认后加 --yes - dws devapp get --unified-app-id --format json ← 验证状态 - -删除(不可逆,异步生效): - dws devapp get --unified-app-id --format json ← 展示摘要 - dws devapp delete --unified-app-id --dry-run --format json - → 用户明确确认后加 --yes - dws devapp list --format json ← 验证已消失 -``` - -## 修改应用信息 - -```text -1. 定位应用(若只有名称) - dws devapp list --name "应用名" --format json - → 必须唯一命中 - -2. 修改 - dws devapp update --unified-app-id --name "新名" --desc "新描述" --dry-run --format json - → 确认后加 --yes - -3. 验证 - dws devapp get --unified-app-id --format json -``` - -## 成员管理 - -```text -1. 查看当前成员 - dws devapp member list --app-id --format json - -2. 添加成员 - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json - → 确认后加 --yes - -3. 验证 - dws devapp member list --app-id --format json - -4. 移除成员 - dws devapp member remove --app-id --users --member-type DEVELOPER --dry-run --format json - → 确认后加 --yes - -5. 验证 - dws devapp member list --app-id --format json -``` - -## 安全配置 - -```text -1. 配置 IP 白名单 / 重定向 URL / 免登 URL - dws devapp security config --app-id \ - --ip-whitelist 192.0.2.10,192.0.2.11 \ - --redirect-url https://callback.example.invalid/callback \ - --sso-url https://sso.example.invalid/sso \ - --dry-run --format json - → 确认后加 --yes - -注意:只下发显式提供的字段,未提供的不覆盖。 -``` - -## 版本发布与审批(选审批人) - -```text -1. 创建版本(权限变更等已通过 permission add 写入版本) - dws devapp version create --unified-app-id --version 1.0.1 --desc "变更说明" --yes --format json - → 记录返回的 versionId - -2. 审批预检:获取审批要求和候选审批人列表 - dws devapp version check-approval --unified-app-id --version-id --format json - → 服务端 dryRun 预检,不实际发布 - → 返回中包含是否需要审批、候选审批人(userId + 姓名) - -3. 把候选审批人列表展示给用户,让用户选择 - ⚠️ 必须由用户拍板选哪个审批人,agent 不要自行挑选或默认取第一个 - -4. 用选中的审批人发起发布审批 - dws devapp version publish --unified-app-id --version-id --approver <用户选中的userId> --yes --format json - → 含高敏权限时需追加 --confirm-sensitive - → 审批流程会自动推送给该审批人 - -5. 跟踪审批/发布状态 - dws devapp version status --unified-app-id --version-id --format json - → 审批通过后应用才正式发布;机器人等能力需发布后才可被搜索/加群/路由消息 -``` - -## 通用规则 - -所有操作遵循: - -```text -意图消歧 → 定位唯一应用 → dry-run 预览 → 用户确认 → --yes 执行 → 透传结果 -``` - -- 多应用命中时展示候选,不自动选择 -- `ServiceResult.success=false` 原样透传 `errorCode/errorMsg` -- 待实现的 event 命令遇到时报告功能待上线 From 55f5e0c3d0fbf6e719603dc2232e92c86877e8b2 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:11:32 +0800 Subject: [PATCH 19/47] =?UTF-8?q?chore(dev):=20=E5=88=A0=E9=99=A4=E6=97=A7?= =?UTF-8?q?=20skills/multi/dingtalk-devapp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit multi 模式旧 devapp 技能整组下线,已被 dingtalk-dev 替代。 Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/multi/dingtalk-devapp/SKILL.md | 62 ------ .../dingtalk-devapp/references/app-crud.md | 124 ------------ .../references/credentials-webapp.md | 55 ----- .../references/member-security.md | 51 ----- .../dingtalk-devapp/references/permissions.md | 96 --------- .../multi/dingtalk-devapp/references/robot.md | 189 ------------------ .../dingtalk-devapp/references/version.md | 129 ------------ .../dingtalk-devapp/references/workflows.md | 177 ---------------- 8 files changed, 883 deletions(-) delete mode 100644 skills/multi/dingtalk-devapp/SKILL.md delete mode 100644 skills/multi/dingtalk-devapp/references/app-crud.md delete mode 100644 skills/multi/dingtalk-devapp/references/credentials-webapp.md delete mode 100644 skills/multi/dingtalk-devapp/references/member-security.md delete mode 100644 skills/multi/dingtalk-devapp/references/permissions.md delete mode 100644 skills/multi/dingtalk-devapp/references/robot.md delete mode 100644 skills/multi/dingtalk-devapp/references/version.md delete mode 100644 skills/multi/dingtalk-devapp/references/workflows.md diff --git a/skills/multi/dingtalk-devapp/SKILL.md b/skills/multi/dingtalk-devapp/SKILL.md deleted file mode 100644 index b3fc6945..00000000 --- a/skills/multi/dingtalk-devapp/SKILL.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: dingtalk-devapp -description: 钉钉开放平台应用管理。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/应用权限/权限点/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人。Distinct from dingtalk-devdoc(开放平台文档搜索) and dingtalk-doc(钉钉云文档)。命令前缀:dws devapp(兼容别名 dws app)。 -cli_version: ">=1.0.15" -metadata: - category: product - stability: experimental - requires: - bins: - - dws ---- - -# 钉钉开放平台应用管理 Skill - -> 🧪 **EXPERIMENTAL · 试验版 / Preview** — multi 模式当前未达 stable 标准。接口、命名与跨 skill 引用后续可能调整;生产 / 共享环境请优先使用 mono 模式(`dws skill setup --mode mono`)。问题请提 issue 反馈。 - -> **PREREQUISITE:** Read the root `dws` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH. - - - -> `dws devapp ...` 是内置 helper 命令,不依赖 MCP 服务发现。执行前用 `dws devapp --help` 验证可用。 - -## 意图消歧 - -`应用` 是泛词。只有出现以下信号才路由到 devapp: - -- `开放平台应用/开发者后台应用/企业内部应用/内部应用` -- `agentId/clientId/appKey/appSecret/customKey` -- `应用权限/权限点/scopeValue/应用成员/安全配置/IP 白名单` -- `创建机器人/智能体机器人/机器人配置/机器人回调地址`(→ `robot`) -- `应用版本/版本发布/发布审核/选审批人`(→ `version`) - -不要路由到 devapp 的:接口文档→`dingtalk-devdoc`、钉钉文档→`dingtalk-doc`、工作台应用→`workbench app`、审批→`dingtalk-oa`。只说 `应用` 无上下文时先追问。 - -## 核心规则 - -1. 所有命令加 `--format json`。 -2. 写操作先 `--dry-run`,确认后才加 `--yes`。 -3. 应用名/appKey/customKey 命中多条时展示候选,不取第一条。 -4. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名。 -5. 主动读取密钥走 `credentials get`;任何 `devapp get`/详情返回里的 `clientSecret/appSecret` 都按敏感凭证脱敏,不向用户展开。 - -## 开放平台文档 RAG / 错误码排查 - -- 任何产品执行中,只要用户问开放平台 API、接口参数、字段含义、权限点、回调、SDK、配额、错误码,或命令返回上游 OpenAPI/SDK 错误,必须先用 `dws devdoc article search --query "<关键词>" --format json` 做官方文档 RAG。 -- 查询词优先保留原始 API 名、能力名、权限点、完整错误码和 message;首轮形如 `errcode `,无结果再换 `<产品/场景> <错误码>`、`<接口名> 参数`。 -- 本地 CLI 错误(如 `unknown command` / `unknown flag` / 认证 / recovery)仍按 root `dws` / `dws-shared` 的错误处理执行;`devdoc` 用于开放平台业务错误码和接口语义排查。 -- `devdoc` 只查钉钉开放平台开发者文档,不查业务数据;排查结论必须基于命中条目的标题、摘要或链接,不能编造错误原因或不存在的命令。 - -## 渐进式参考 - -按使用频率和复杂度分层,按需加载: - -| 层 | 参考文档 | 覆盖命令 | -|----|---------|---------| -| 基础 | [app-crud.md](references/app-crud.md) | list / get / create / update / delete / inactive / active | -| 凭证与网页应用 | [credentials-webapp.md](references/credentials-webapp.md) | credentials get / webapp get / webapp config | -| 权限管理 | [permissions.md](references/permissions.md) | permission list / permission add / permission remove | -| 成员与安全 | [member-security.md](references/member-security.md) | member list / add / remove / security config | -| 机器人 | [robot.md](references/robot.md) | robot create / submit / result / get / config / update / enable / offline | -| 版本发布 | [version.md](references/version.md) | version create / list / get / check-approval / publish / status | -| 操作流程 | [workflows.md](references/workflows.md) | 创建应用全流程 / 权限全流程 / 生命周期管理 | diff --git a/skills/multi/dingtalk-devapp/references/app-crud.md b/skills/multi/dingtalk-devapp/references/app-crud.md deleted file mode 100644 index 7898e729..00000000 --- a/skills/multi/dingtalk-devapp/references/app-crud.md +++ /dev/null @@ -1,124 +0,0 @@ -# 应用基础操作 - -应用列表查询、详情、创建、修改、生命周期启停和删除。 - -## 应用定位 - -写操作前必须定位到唯一应用: - -| 优先级 | 标识 | 处理 | -|--------|------|------| -| 1 | `--unified-app-id` | 直接使用 | -| 2 | `--agent-id` / `--app-id` | 直接使用 | -| 3 | `--app-key` / `--custom-key` | 先查询,唯一命中才继续 | -| 4 | `--name` | 模糊搜索,写操作必须唯一命中 | - -## 应用列表 - -```bash -dws devapp list --format json -dws devapp list --name DemoApp --format json -dws devapp list --agent-id 123456 --format json -dws devapp list --creator 张三 --sort gmt_modified --order desc --format json -``` - -MCP tool: `list_open_dev_apps_by_condition` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--page` | `currentPage` | 1-based,默认 1 | -| `--page-size` | `pageSize` | 默认 20 | -| `--name` / `--keyword` | `appName` | 应用名搜索 | -| `--agent-id` | `agentId` | 精确定位 | -| `--app-key` | `appKey` | appKey/clientId | -| `--creator` | `creator` | 创建人关键词 | -| `--sort` | `sortType` | 如 `gmt_modified` | -| `--order` | `sortOrder` | `asc` / `desc` | - -## 应用状态字段 - -列表/详情原始字段里如果出现 `status` / `appStatus`,按应用生命周期枚举判断;不要和版本 `versionStatus` 混用。 - -| status | 枚举 | 含义 | 下一步 | -|--------|------|------|--------| -| `0` | `IN_ACTIVE` | 已停用,应用不可用 | 需要恢复时走 `active --dry-run` → 确认 → `--yes` | -| `1` | `ACTIVE` | 已激活,应用可用 | 可继续配置权限、网页应用、机器人或版本 | -| `2` | `WAIT_ACTIVE` | 待激活 | 先回读 `get/list` 确认状态;不要直接按已生效处理 | -| `3` | `EXPIRED` | 已过期 | 停止写操作,提示用户到开发者后台或管理员侧处理 | - -`create/update` 返回的 `versionStatus` 是版本状态,语义见 `version.md`;它不等同于应用启停状态。 - -## 应用详情 - -```bash -dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --agent-id 123456 --format json -``` - -MCP tool: `get_open_dev_app_detail` - -详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 - -## 创建应用 - -```bash -dws devapp create --name DemoApp --desc "内部应用" --type internal --dry-run --format json -dws devapp create --name DemoApp --desc "内部应用" --type internal --yes --format json -``` - -MCP tool: `create_inner_app` - -| CLI | MCP | 必填 | -|-----|-----|------| -| `--name` | `appName` | 是 | -| `--desc` | `appDesc` | 否 | -| `--icon` | `appIcon` | 否 | - -`--type` 只做 CLI 校验(当前仅支持 `internal`),不下发 MCP。 - -## 修改应用 - -```bash -dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-run --format json -dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --yes --format json -``` - -MCP tool: `update_inner_app` - -至少提供一个更新字段:`--name` / `--desc` / `--icon`。 - -## 停用 / 启用应用 - -```bash -dws devapp inactive --unified-app-id ID --dry-run --format json -dws devapp inactive --unified-app-id ID --yes --format json - -dws devapp active --unified-app-id ID --dry-run --format json -dws devapp active --unified-app-id ID --yes --format json -``` - -MCP tools: `inactive_inner_app` / `active_inner_app` - -停用保留数据但应用不可用,可通过 `active` 恢复。 - -执行 `inactive/active` 后必须回读 `get` 或 `list`:看到 `status=0` 才算停用完成,看到 `status=1` 才算启用完成;如果接口只返回操作成功但未带状态,向用户说明需要以回读结果为准。 - -## 删除应用 - -```bash -dws devapp delete --unified-app-id ID --dry-run --format json -dws devapp delete --unified-app-id ID --yes --format json -``` - -MCP tool: `delete_inner_app` - -删除前必须展示应用摘要。删除为异步操作,成功后应用延迟从列表消失。 - -## 错误处理 - -| 情况 | 处理 | -|------|------| -| `unknown command` | CLI 构建不含 devapp helper | -| `endpoint_not_resolved` | 检查 edition endpoint 注入 | -| 多应用命中 | 展示候选,停止写操作 | -| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/multi/dingtalk-devapp/references/credentials-webapp.md b/skills/multi/dingtalk-devapp/references/credentials-webapp.md deleted file mode 100644 index 0f1dec02..00000000 --- a/skills/multi/dingtalk-devapp/references/credentials-webapp.md +++ /dev/null @@ -1,55 +0,0 @@ -# 凭证读取与网页应用配置 - -## 凭证读取 - -```bash -dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --agent-id 123456 --format json -``` - -MCP tool: `get_open_dev_app_credentials` - -后端 facade: `OpenInnerAppQueryFacade.getCredentials`。 - -**规则:** -- CLI 只传应用定位字段,不传 `showSecret`/`confirmSecret`。 -- 返回可能包含 `clientSecret/appSecret`,输出按敏感凭证处理。 -- 不能用 `devapp get` 代替;如果 `devapp get` 偶尔返回密钥,也只用于内部判断并脱敏,不向用户展开。 - -关键返回字段: - -| 字段 | 说明 | -|------|------| -| `clientId` / `appKey` | 非密钥标识 | -| `clientSecret` / `appSecret` | 敏感凭证 | -| `currentSecretStatus` | 当前密钥状态 | -| `pendingExpireTask` | 密钥过期任务信息 | - -## 网页应用查询 - -```bash -dws devapp webapp get --agent-id 123456 --format json -dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json -``` - -MCP tool: `get_webapp_config` - -未配置网页应用前可能只返回 `agentId`。 - -## 网页应用配置 - -```bash -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --dry-run --format json -dws devapp webapp config --agent-id 123456 --homepage-link https://example.com/mobile --pc-homepage-link https://example.com/pc --yes --format json -``` - -MCP tool: `set_webapp_config` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--h5-page-type` | `h5PageType` | 网页应用生效端 | -| `--homepage-link` | `homepageLink` | 移动端首页地址 | -| `--pc-homepage-link` | `pcHomepageLink` | PC 端首页地址 | -| `--omp-link` | `ompLink` | 管理后台地址 | - -至少提供一个配置字段。`h5PageType` 未显式传入时,不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 diff --git a/skills/multi/dingtalk-devapp/references/member-security.md b/skills/multi/dingtalk-devapp/references/member-security.md deleted file mode 100644 index 8c2b716c..00000000 --- a/skills/multi/dingtalk-devapp/references/member-security.md +++ /dev/null @@ -1,51 +0,0 @@ -# 成员管理与安全配置 - -## 成员查询 - -```bash -dws devapp member list --app-id UNIFIED_APP_ID --format json -``` - -MCP tool: `list_open_dev_app_members` - -## 添加成员 - -```bash -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json -``` - -MCP tool: `add_open_dev_app_members` - -## 移除成员 - -```bash -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json -``` - -MCP tool: `remove_open_dev_app_members` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | -| `--users` | `memberUserIds` | userId 列表,逗号分隔(必填) | -| `--member-type` | `memberType` | 成员类型,如 `DEVELOPER`(必填) | - -## 安全配置 - -```bash -dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json -dws devapp security config --app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json -``` - -MCP tool: `update_app_security_config` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | -| `--ip-whitelist` | `ipWhiteList` | 出口 IP 白名单,逗号/分号分隔 | -| `--redirect-url` | `redirectUrls` | 登录重定向 URL,逗号/分号分隔 | -| `--sso-url` | `otherAuthUrls` | 端内免登 URL,逗号/分号分隔 | - -至少提供一个配置字段。只下发显式提供的字段,未提供的不覆盖。 diff --git a/skills/multi/dingtalk-devapp/references/permissions.md b/skills/multi/dingtalk-devapp/references/permissions.md deleted file mode 100644 index 17cc6d95..00000000 --- a/skills/multi/dingtalk-devapp/references/permissions.md +++ /dev/null @@ -1,96 +0,0 @@ -# 权限管理 - -查询、申请、取消开放平台应用的 APP 应用权限和 SNS 个人权限。 - -## 权限列表 - -```bash -dws devapp permission list --unified-app-id ID --format json -dws devapp permission list --unified-app-id ID --keyword "机器人发消息" --status UNAUTHED --format json -dws devapp permission list --unified-app-id ID --scope-type SNS --format json -dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json -``` - -MCP tool: `list_open_dev_app_permissions` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--unified-app-id` | `unifiedAppId` | 应用定位 | -| `--agent-id` | `agentId` | 应用定位 | -| `--keyword` | `keyword` | 权限名/API 名关键词 | -| `--status` | `authStatus` | `ALL` / `AUTHED` / `UNAUTHED` | -| `--scope-type` | `firstLevelType` | `APP` / `SNS`,为空返回两者 | -| `--scope` | `scopeValue` | 单权限详情模式 | -| `--limit` | `limit` | 每页返回数量,默认 20,建议不超过 50 | -| `--offset` | `offset` | 翻页偏移量,默认 0 | - -**状态判断:** - -`--status` 是查询过滤条件: - -| authStatus | 含义 | -|------------|------| -| `ALL` | 不按授权状态过滤 | -| `AUTHED` | 只看已授权/已开通的权限点 | -| `UNAUTHED` | 只看未授权/未开通的权限点 | - -单个权限项返回的 `status` 是内部操作态: - -| status | 枚举 | 含义 | 下一步 | -|--------|------|------|--------| -| `0` | `STATUS_OBTAINED` | 权限已获得 | 不要重复申请;如需取消,确认 `canRemove=true` 后走 `permission remove` | -| `1` | `STATUS_APPLYING` | 权限申请中 | 不要重复申请;查看 `authedStatusDesc`,通常等待审批或版本发布 | -| `2` | `STATUS_CAN_APPLY` | 权限可以申请 | 可走 `permission add --dry-run` | -| `3` | `STATUS_CAN_NOT_APPLY` | 权限不可以申请 | 停止申请,展示 `applyDisabledReason/displayMessage` | - -`authedStatusDesc` 是给用户看的细分状态:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED` 表示已开通、已申请或待发布;`NOT_OPEN`/`NOT_APPLIED` 表示未开通/未申请;`AUDIT_PROCESSING` 表示审批中;`AUDIT_REFUSE` 表示审批未通过。判断能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 - -**翻页:** - -权限列表支持 `--limit` + `--offset` 分页。一个应用可能有 150+ 个权限点,一次查不完时用 offset 翻页: - -```bash -dws devapp permission list --unified-app-id ID --limit 50 --format json # 第 1 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 50 --format json # 第 2 页 -dws devapp permission list --unified-app-id ID --limit 50 --offset 100 --format json # 第 3 页 -``` - -当返回条数 < limit 时表示已到末尾。 - -**规则:** -- `permission search` 和 `permission detail` 是 `list` 的 CLI 别名,不是独立 MCP tool。 -- 默认同时返回 APP 和 SNS 权限。 -- 列表模式只返回 `apiPreview`;`--scope` 详情模式返回完整 `apiList`。 - -**scopeValue 选择顺序:** - -1. 用户给了 `scopeValue` → 精确匹配 -2. 用户给了 API 名 → `keyword` 搜索,匹配 `apiPreview.name` -3. 用户给了权限名 → 匹配 `scopeName/scopeDesc` -4. 多个候选 → 展示列表让用户选择,不自动取第一条 - -## 申请权限 - -```bash -dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json -dws devapp permission add --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --yes --format json -``` - -MCP tool: `apply_open_dev_app_permissions` - -**规则:** -- `--permissions` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 -- 已开通跳过,不可编辑拒绝。 -- `requiredApproval=true` 允许申请——写入版本变更,审批在版本发布时处理。 -- 不在此处选审批人。 - -## 取消权限 - -```bash -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --dry-run --format json -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --yes --format json -``` - -MCP tool: `remove_open_dev_app_permission` - -一次只取消一个权限点。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md deleted file mode 100644 index 11b51755..00000000 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ /dev/null @@ -1,189 +0,0 @@ -# 机器人能力 - -为开放平台企业内部应用创建和配置机器人。分两类场景: - -1. **新建智能体机器人**:一次性创建一个新的 Agent 应用 + 承载机器人(`create` / `submit` / `result`)。 -2. **现有应用配置机器人**:在已存在的应用上开启/配置/停用机器人(`get` / `config` / `update` / `enable` / `offline`),通过 `--unified-app-id` 定位。 - -> `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 - -## 一、新建智能体机器人 - -### 同步创建 - -```bash -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json -``` - -MCP tool: `create_dingtalk_robot`。成功返回 `agentId / robotCode / clientId / clientSecret`(凭据按敏感信息处理)。 - -| CLI | MCP | 必填 | 说明 | -|-----|-----|------|------| -| `--app-name` | `appName` | 是 | 智能体应用名称,长度 2-20,企业内唯一 | -| `--robot-name` | `robotName` | 是 | 承载机器人名称,客户端展示 | -| `--desc` | `desc` | 是 | 机器人功能描述,≤200 字 | -| `--icon` | `robotMediaId` | 否 | 图标 mediaId;空则用默认图标 | -| `--preview` | `previewMediaId` | 否 | 预览图 mediaId;空则复用图标 | - -### 异步创建 + 查询结果 - -适合创建耗时较长或需要失败重试的场景。 - -```bash -# 提交任务 -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json -# → 返回 taskId - -# 轮询结果 -dws devapp robot result --task-id --format json -``` - -MCP tools: `submit_robot_create_task` / `query_robot_create_result` - -- `submit` 返回 `taskId / status / expiresIn / interval / retryCount`,提交成功后通常是 `WAITING`。 -- 失败重试:把上次的 `taskId` 通过 `--task-id` 传入 `submit`,避免重复创建。 -- `result` 返回任务状态;只有 `SUCCESS` 时才能使用返回的 `agentId / robotCode / clientId / clientSecret`。 - -异步创建任务状态: - -| status | 含义 | 下一步 | -|--------|------|--------| -| `WAITING` | 任务已提交,仍在创建中 | 按 `interval` 轮询 `robot result` | -| `SUCCESS` | 创建完成 | 保存 `robotCode/clientId/clientSecret`,凭据按敏感信息处理 | -| `APPROVAL_REQUIRED` | 创建编排返回需审批 | 不要重复建号;按返回信息或开发者后台审批后再继续 | -| `FAIL` | 创建失败 | 读取 `errorCode/errorMsg/failReason`;可带原 `taskId` 重新 `submit` | -| `EXPIRED` | `taskId` 不存在或超过有效期 | 重新 `submit`,必要时换新 `taskId` | - -## 二、现有应用的机器人配置 - -### 查询配置 - -```bash -dws devapp robot get --unified-app-id --format json -``` - -MCP tool: `get_open_dev_app_robot_config`。返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 - -状态判断: - -- `status=1`:OFFLINE,机器人配置存在但处于停用/下线状态。 -- `status=2`:ONLINE,机器人配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 -- `robot get` 返回 `success=true` 且包含 `robotCode` 时,说明配置已落库,不是异步等待态。 -- ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-url`,或用 `robot connect` 接到本地 Agent。 -- 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 - -### 创建 / 更新 / 启用配置 - -三者字段一致,区别在语义:`config`=首次创建、`update`=修改、`enable`=启用/重新启用。 - -首次 `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为“待生效”;只有 `status=1` 或需要重新上架时才调用 `enable`。 - -```bash -dws devapp robot config --unified-app-id --name 小助手 --brief 审批助手 \ - --description "处理审批相关问答" --outgoing-url https://example.com/msg \ - --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json - -dws devapp robot update --unified-app-id --brief "新的简介" --dry-run --format json - -dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json -``` - -MCP tools: `create_open_dev_app_robot_config` / `update_open_dev_app_robot_config` / `enable_open_dev_app_robot` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--name` | `name` | 机器人名称 | -| `--brief` | `brief` | 简介 | -| `--description` | `description` | 描述 | -| `--icon` | `iconMediaId` | 图标 mediaId | -| `--outgoing-url` | `outgoingUrl` | 消息回调地址 | -| `--event-url` | `chatBotEventUrl` | 事件回调地址 | -| `--mode` | `mode` | 机器人模式枚举(整数) | -| `--skills` | `skillList` | 技能列表,逗号分隔 | -| `--add-scope` | `isAddScope` | 自动添加机器人相关权限 | -| `--disable-ssl-verify` | `disableSSLVerify` | 回调关闭 SSL 校验 | -| `--i18n-name` | `i18nName` | 名称国际化 JSON,如 `'{"en_US":"Bot"}'` | -| `--i18n-brief` | `i18nBrief` | 简介国际化 JSON | -| `--i18n-description` | `i18nDescription` | 描述国际化 JSON | - -至少提供一个配置字段,否则 CLI 报错。 - -### 停用 - -```bash -dws devapp robot offline --unified-app-id --dry-run --format json -dws devapp robot offline --unified-app-id --yes --format json -``` - -MCP tool: `offline_open_dev_app_robot` - -### 建联(把机器人接到本地 agent) - -```bash -# 用现成机器人凭证起 Stream,接到当前渠道的本地 agent(前台运行,Ctrl-C 退出) -dws devapp robot connect --channel auto --robot-client-id --robot-client-secret - -# 用统一应用 ID,复用 credentials get 自动取凭证 -dws devapp robot connect --unified-app-id --channel qoderwork - -# Codex 渠道推荐显式覆盖 agent 命令;否则默认空白工作目录可能触发 -# "Not inside a trusted directory and --skip-git-repo-check was not specified" -CODEX_BIN="$(command -v codex || echo /Applications/Codex.app/Contents/Resources/codex)" -DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check" \ - dws devapp robot connect --unified-app-id --channel codex --format json - -# 预览建联方案不实际起连接 -dws devapp robot connect --robot-client-id --robot-client-secret --dry-run --format json -``` - -只建联、不建号:缺凭证先用 `robot create` / `robot submit` 建号拿 clientId/clientSecret。 - -Codex 渠道注意: - -- `--channel codex` 真实建联时,优先使用上面的 `DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check"` 形式,避免 Codex CLI 在默认临时目录内因非可信 Git 目录拒绝执行。 -- 如需给 Codex 固定知识/项目上下文,可把可信目录直接写进覆盖命令:`DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check -C /path/to/repo"`。路径不要包含空格。 -- 设置 `DWS_AGENT_CMD` 后,DWS 不再自动拼接 `--agent-model` / `--agent-workdir` / 会话参数;需要的参数要一起写进 `DWS_AGENT_CMD`。 - -| flag | 说明 | -|------|------| -| `--channel` | `auto`(默认,运行时信号自动识别) / openclaw / qoder / qoderwork / hermes / workbuddy / claudecode / codebuddy / codex / gemini / opencode | -| `--robot-client-id` / `--robot-client-secret` | 现成机器人凭证(clientId=AppKey, clientSecret=AppSecret)。命名带 `robot-` 前缀以避开全局 OAuth `--client-id` flag | -| `--unified-app-id` | 统一应用 ID,内部调 `get_open_dev_app_credentials` 自动取凭证。⚠️ 字段名待预发真机验证;clientSecret 仅建号时返回一次、未必可取,必要时回退手填 | -| `--agent-memory` | 按会话续聊(默认开):同一群/单聊共享 agent 会话,追问保留上下文。仅 claudecode/codebuddy/workbuddy(CLI 有 `--session-id`/`--resume`);qoder 系/codex/gemini/opencode 无寻址会话,自动保持无状态。`--agent-memory=false` 关闭 | -| `--agent-model` | 覆盖本地 agent 模型(如 claudecode 默认锁 haiku 求快,可改 `claude-sonnet-4-6` 换聪明)。env: `DWS_AGENT_MODEL` | -| `--agent-workdir` | agent 运行目录:放知识文件(如 CLAUDE.md)可给机器人企业上下文。默认空白临时目录(冷启动快 ~4s vs 大目录 ~29s,慢了会错过钉钉响应窗口)。env: `DWS_AGENT_WORKDIR` | -| `--reply-card` | 富回复(默认开):🤔Thinking/🥳Done 表态永远生效;**卡片需配 `--card-template` 才启用**(同 hermes:没配模板=纯文字回复),失败自动回退文字;env `DWS_REPLY_CARD=0` 全关 | -| `--card-template` | AI 卡片模板 ID。**模板按应用授权**:去开发者后台→你的应用→AI 卡片设置注册/获取模板 ID(同 hermes 的配置方式),可去掉公共模板的第三方角标;默认用公共模板 best-effort。env `DWS_CARD_TEMPLATE` | - -#### 建联前的依赖预检(agent 必做) - -渠道背后是本地 agent CLI,用户可能没装。先 `--dry-run` 看 `cli` 字段再决定下一步: - -```bash -dws devapp robot connect --channel --robot-client-id x --robot-client-secret y --dry-run --format json -# 输出里的 cli 字段: -# "cli": {"required":"Claude Code","installed":false,"autoInstall":true,"installHint":"npm i -g @anthropic-ai/claude-code"} -``` - -| cli 状态 | agent 应该做什么 | -|----------|----------------| -| `installed: true` | 直接建联 | -| `installed: false, autoInstall: true` | 告知用户缺哪个 CLI,说明启动建联时会自动 `npm` 安装(或先手动执行 installHint 里的命令再连);`DWS_CONNECT_NO_INSTALL=1` 可禁自动安装 | -| `installed: false, autoInstall: false` | **不要直接起连接**——桌面 App 渠道(qoder/qoderwork/workbuddy)需要用户先安装对应 App(installHint 是下载地址),装好后 CLI 随 App 自带;openclaw/hermes 引导用户走官方 onboarding | - -- **stream-bridge 渠道**:Go 原生进程内 Stream 转发器,订阅 `TOPIC_ROBOT`,每条 @机器人消息起一个无头 CLI 实例 → stdout 回钉钉,可 7×24 无人值守。 -- **会话记忆**:首条消息 `--session-id ` 建会话,后续 `--resume ` 续聊;会话状态在内存里,连接器重启后从新会话开始;某会话坏了会自愈(下条消息换新会话)。 -- **官方渠道**(openclaw/hermes):dws 不代建机器人,输出官方 onboarding 指引。 -- 环境覆盖:`DWS_AGENT_CMD`(整条命令覆盖,覆盖时不再注入模型/会话参数) / `DWS_AGENT_MODEL` / `DWS_AGENT_WORKDIR` / `DWS_CONNECT_CMD` / `DWS_CONNECT_NO_INSTALL=1` / `DWS_AGENT_TIMEOUT_MS`。 - -## 错误处理 - -| 情况 | 处理 | -|------|------| -| `robot info is not exist` | 应用未配置机器人,先用 `robot config` 创建 | -| 应用名重复 | `app-name` 企业内需唯一,换个名字 | -| 本地 Codex agent 返回 `Not inside a trusted directory and --skip-git-repo-check was not specified` | 用 `DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check"` 重启 `robot connect --channel codex` | -| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | -| 创建任务 `EXPIRED` | 任务过期,重新 `submit`(可带原 taskId 重试) | diff --git a/skills/multi/dingtalk-devapp/references/version.md b/skills/multi/dingtalk-devapp/references/version.md deleted file mode 100644 index 0998ec67..00000000 --- a/skills/multi/dingtalk-devapp/references/version.md +++ /dev/null @@ -1,129 +0,0 @@ -# 版本发布 - -管理开放平台企业内部应用的版本:基于当前配置创建版本、查看版本列表/详情、预检审批、发布、查状态。 - -> `corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。所有版本通过 `--unified-app-id` 定位,单个版本再加 `--version-id`。 - -## 典型流程 - -```text -permission add(requiredApproval=true 写入版本变更) - → version create 创建版本 - → version check-approval 预检是否需要审批 / 审批人 - → version publish 发布(含高敏权限需 --confirm-sensitive) - → version status 轮询发布/审批状态 -``` - -## 创建版本 - -```bash -dws devapp version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --dry-run --format json -dws devapp version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json -``` - -MCP tool: `create_open_dev_app_version` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--version` | `version` | 版本号,如 1.0.1 | -| `--desc` | `description` | 版本描述 | - -## 版本列表 - -```bash -dws devapp version list --unified-app-id --page 1 --page-size 20 --format json -``` - -MCP tool: `list_open_dev_app_versions`(`--page`→`currentPage`,`--page-size`→`pageSize`) - -新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 - -## 版本详情 - -```bash -dws devapp version get --unified-app-id --version-id --format json -``` - -MCP tool: `get_open_dev_app_version_detail`。返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 - -## 预检审批(不发布) - -```bash -dws devapp version check-approval --unified-app-id --version-id --format json -``` - -MCP tool: `publish_open_dev_app_version`,CLI 强制 `dryRun=true`,仅返回审批要求和候选审批人,**不会实际发布**。 - -## 发布 - -```bash -dws devapp version publish --unified-app-id --version-id --dry-run --format json -dws devapp version publish --unified-app-id --version-id --yes --format json - -# 含高敏权限时必须确认 -dws devapp version publish --unified-app-id --version-id --confirm-sensitive --yes --format json - -# 灰度选人模式指定审批人 -dws devapp version publish --unified-app-id --version-id --approver --yes --format json -``` - -MCP tool: `publish_open_dev_app_version`,CLI 设 `dryRun=false`。 - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--confirm-sensitive` | `confirmedSensitive` | 版本含高敏权限时必须确认 | -| `--approver` | `approverUserId` | 灰度选人模式指定审批人 userId | - -> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`(对应 `dryRun=true`)。二者不同,发布前建议先 `check-approval`。 - -发布响应 `result`: - -| result | 含义 | 下一步 | -|--------|------|--------| -| `NOT_REQUIRED` | 不需要审批;预检时表示可直接发布,真实发布时表示已直接发布上线 | 真实发布后回读 `version status/get` 验证 `RELEASE` | -| `APPROVAL_REQUIRED` | 需要审批,通常出现在 `check-approval` 或未指定审批人时 | 查看 `approvalMode/approvalCandidates`,让用户选择审批人后再 `publish --approver` | -| `SUBMITTED` | 已提交审批 | 保存 `processId`,轮询 `version status` | - -审批模式 `approvalMode`: - -| approvalMode | 含义 | 下一步 | -|--------------|------|--------| -| `SELECT_APPROVER` | 灰度选人模式,需要在候选审批人中选一个 | 展示候选审批人,不自动选第一个 | -| `ENTERPRISE_SELF_BUILT` | 企业自建审核模式 | 不传 `--approver`,按企业自建审核流程等待 | - -## 版本状态 - -```bash -dws devapp version status --unified-app-id --version-id --format json -``` - -MCP tool: `get_open_dev_app_version_status`。返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 - -版本状态字段:`version create/list/get` 返回 `status`,`version status` 返回 `versionStatus`,二者都对齐版本枚举。 - -| status/versionStatus | 含义 | 下一步 | -|----------------------|------|--------| -| `INIT` | 版本已创建或有待发布变更,尚未发布 | 可 `check-approval` / `publish` | -| `AUDIT` | 发布审核中 | 不要重复发布;即使没有返回 `processStatus`,也按审核中处理 | -| `RELEASE` | 已发布生效 | 发布完成,可继续验证权限、机器人、网页应用等能力 | -| `GRAY` | 灰度状态 | 按灰度流程处理;不要当全量已发布 | - -审批流程状态字段:`version status` 的 `processStatus` 只在存在审批流程且后端透出流程态时有值。`versionStatus=AUDIT` 但没有 `processStatus/processInstanceId` 时,不要判失败,仍表示审核中。 - -| processStatus | 含义 | 下一步 | -|---------------|------|--------| -| `UNDER_REVIEW` | 审批中 | 等待审批,必要时把 `processInstanceId` 给用户去钉钉客户端查看 | -| `PASS` | 审批通过 | 继续回读版本,确认是否进入 `RELEASE` | -| `FAIL` | 审批拒绝 | 展示 `processComment`,修改后重新创建/发布版本 | -| `WITHDRAW` / `CANCEL` | 审批撤回或取消 | 回到发布前状态,重新 `check-approval` / `publish` | -| `PUBLISH_FAILED` | 审批后发布失败 | 展示错误信息,重新检查版本状态和后端错误 | - -遇到未列出的状态值时,不要猜测语义;原样展示状态,并回读 `version get/status` 或查开放平台文档/后台详情。 - -## 错误处理 - -| 情况 | 处理 | -|------|------| -| `check-approval` 提示需审批 | 按返回选审批人,再 `publish --approver` | -| 发布报高敏权限未确认 | 加 `--confirm-sensitive` 重新发布 | -| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/multi/dingtalk-devapp/references/workflows.md b/skills/multi/dingtalk-devapp/references/workflows.md deleted file mode 100644 index 9347fbee..00000000 --- a/skills/multi/dingtalk-devapp/references/workflows.md +++ /dev/null @@ -1,177 +0,0 @@ -# 操作流程 - -端到端操作流程,组合多个命令完成完整业务场景。 - -## 创建应用并完成基础配置 - -```text -1. 创建应用 - dws devapp create --name "应用名" --desc "描述" --type internal --dry-run --format json - → 确认后加 --yes - → 记录返回的 unifiedAppId 和 agentId - -2. 确认创建成功 - dws devapp get --unified-app-id --format json - -3. 读取凭证(获取 appKey/appSecret) - dws devapp credentials get --unified-app-id --format json - -4. 配置网页应用(按需) - dws devapp webapp config --agent-id --homepage-link --dry-run --format json - → 确认后加 --yes - -5. 验证网页应用配置 - dws devapp webapp get --agent-id --format json - → 确认返回 homepageLink/pcHomepageLink 等 - -6. 申请权限(按需) - dws devapp permission list --unified-app-id --keyword "关键词" --format json - → 从返回中选择 scopeValue - dws devapp permission add --unified-app-id --permissions --dry-run --format json - → 确认后加 --yes - -7. 添加开发成员(按需) - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json - → 确认后加 --yes - -8. 验证成员 - dws devapp member list --app-id --format json - -9. 配置安全设置(按需) - dws devapp security config --app-id --ip-whitelist --redirect-url --dry-run --format json - → 确认后加 --yes -``` - -## 权限管理全流程 - -```text -1. 查看所有权限 - dws devapp permission list --unified-app-id --format json - -2. 按关键词搜索未开通权限 - dws devapp permission list --unified-app-id --keyword "机器人" --status UNAUTHED --format json - -3. 查看某个权限覆盖的 API - dws devapp permission list --unified-app-id --scope --format json - -4. 申请权限 - dws devapp permission add --unified-app-id --permissions --dry-run --format json - → 确认后加 --yes - → requiredApproval=true 的权限:写入版本变更,需后续走版本发布审核 - -5. 验证 - dws devapp permission list --unified-app-id --scope --format json - → 确认 authed=true - -6. 取消不需要的权限 - dws devapp permission remove --unified-app-id --permission --dry-run --format json - → 确认后加 --yes -``` - -## 应用生命周期 - -```text -停用(保留数据,暂停服务): - dws devapp get --unified-app-id --format json ← 确认目标 - dws devapp inactive --unified-app-id --dry-run --format json - → 确认后加 --yes - dws devapp get --unified-app-id --format json ← 验证状态 - -启用(恢复已停用应用): - dws devapp active --unified-app-id --dry-run --format json - → 确认后加 --yes - dws devapp get --unified-app-id --format json ← 验证状态 - -删除(不可逆,异步生效): - dws devapp get --unified-app-id --format json ← 展示摘要 - dws devapp delete --unified-app-id --dry-run --format json - → 用户明确确认后加 --yes - dws devapp list --format json ← 验证已消失 -``` - -## 修改应用信息 - -```text -1. 定位应用(若只有名称) - dws devapp list --name "应用名" --format json - → 必须唯一命中 - -2. 修改 - dws devapp update --unified-app-id --name "新名" --desc "新描述" --dry-run --format json - → 确认后加 --yes - -3. 验证 - dws devapp get --unified-app-id --format json -``` - -## 成员管理 - -```text -1. 查看当前成员 - dws devapp member list --app-id --format json - -2. 添加成员 - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json - → 确认后加 --yes - -3. 验证 - dws devapp member list --app-id --format json - -4. 移除成员 - dws devapp member remove --app-id --users --member-type DEVELOPER --dry-run --format json - → 确认后加 --yes - -5. 验证 - dws devapp member list --app-id --format json -``` - -## 安全配置 - -```text -1. 配置 IP 白名单 / 重定向 URL / 免登 URL - dws devapp security config --app-id \ - --ip-whitelist 192.0.2.10,192.0.2.11 \ - --redirect-url https://callback.example.invalid/callback \ - --sso-url https://sso.example.invalid/sso \ - --dry-run --format json - → 确认后加 --yes - -注意:只下发显式提供的字段,未提供的不覆盖。 -``` - -## 版本发布与审批(选审批人) - -```text -1. 创建版本(权限变更等已通过 permission add 写入版本) - dws devapp version create --unified-app-id --version 1.0.1 --desc "变更说明" --yes --format json - → 记录返回的 versionId - -2. 审批预检:获取审批要求和候选审批人列表 - dws devapp version check-approval --unified-app-id --version-id --format json - → 服务端 dryRun 预检,不实际发布 - → 返回中包含是否需要审批、候选审批人(userId + 姓名) - -3. 把候选审批人列表展示给用户,让用户选择 - ⚠️ 必须由用户拍板选哪个审批人,agent 不要自行挑选或默认取第一个 - -4. 用选中的审批人发起发布审批 - dws devapp version publish --unified-app-id --version-id --approver <用户选中的userId> --yes --format json - → 含高敏权限时需追加 --confirm-sensitive - → 审批流程会自动推送给该审批人 - -5. 跟踪审批/发布状态 - dws devapp version status --unified-app-id --version-id --format json - → 审批通过后应用才正式发布;机器人等能力需发布后才可被搜索/加群/路由消息 -``` - -## 通用规则 - -所有操作遵循: - -```text -意图消歧 → 定位唯一应用 → dry-run 预览 → 用户确认 → --yes 执行 → 透传结果 -``` - -- 多应用命中时展示候选,不自动选择 -- `ServiceResult.success=false` 原样透传 `errorCode/errorMsg` -- 待实现的 event 命令遇到时报告功能待上线 From 166f665708a3627eac0944a64519e825cddf4fd5 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:11:50 +0800 Subject: [PATCH 20/47] =?UTF-8?q?chore(dev):=20devapp=E2=86=92dev=20?= =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E6=94=B6=E5=B0=BE(docs/mono/install?= =?UTF-8?q?/README/gitignore)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除旧 devapp docs;mono 技能与 intent-guide 去 devapp;install 脚本改名 install-dev.sh;README 更新;.gitignore 放行 skill credentials.md 文档。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 2 +- README.md | 2 +- README_zh.md | 2 +- docs/devapp-agent-install-guide.md | 303 ---- docs/devapp-yulan-command-routing.md | 1501 ----------------- scripts/{install-devapp.sh => install-dev.sh} | 41 +- skills/mono/SKILL.md | 15 +- skills/mono/references/intent-guide.md | 32 +- skills/mono/references/products/devapp.md | 364 ---- 9 files changed, 42 insertions(+), 2220 deletions(-) delete mode 100644 docs/devapp-agent-install-guide.md delete mode 100644 docs/devapp-yulan-command-routing.md rename scripts/{install-devapp.sh => install-dev.sh} (54%) delete mode 100644 skills/mono/references/products/devapp.md diff --git a/.gitignore b/.gitignore index 58e28be5..1cc7a5db 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ test/cli_compat/testdata/ *.pem *.key credentials* -!**/credentials-webapp.md +!skills/**/credentials.md plans _docs dws.zip diff --git a/README.md b/README.md index 9b7385c2..ce761513 100644 --- a/README.md +++ b/README.md @@ -557,7 +557,7 @@ dws chat message send-by-bot --robot-code BOT_CODE --group GROUP_ID \ - [Command Index](./docs/command-index.md) — every runtime command with description and when-to-use guidance - [Reference](./docs/reference.md) — environment variables, exit codes, output formats, shell completion - [Architecture](./docs/architecture.md) — discovery-driven pipeline, IR, transport layer -- [Open Platform App Command Routing](./docs/devapp-yulan-command-routing.md) — yulan devapp/app command design, MCP overlay, permission flow, and Agent routing +- [Open Platform App Command Routing](./docs/dev-yulan-command-routing.md) — yulan dev app command design, MCP overlay, permission flow, and Agent routing - [Changelog](./CHANGELOG.md) — release history and migration notes ## Contributing diff --git a/README_zh.md b/README_zh.md index ac4309b1..9ed12f8a 100644 --- a/README_zh.md +++ b/README_zh.md @@ -555,7 +555,7 @@ dws chat message send-by-bot --robot-code BOT_CODE --group GROUP_ID \ - [命令索引](./docs/command-index.md) — 全部运行时命令,带描述与使用场景 - [参考手册](./docs/reference.md) — 环境变量、退出码、输出格式、Shell 补全 - [架构设计](./docs/architecture.md) — 发现驱动管道、IR、Transport 层 -- [开放平台应用指令设计](./docs/devapp-yulan-command-routing.md) — yulan devapp/app 应用侧命令、MCP overlay、权限流程与 Agent 路由 +- [开放平台应用指令设计](./docs/dev-yulan-command-routing.md) — yulan dev app 应用侧命令、MCP overlay、权限流程与 Agent 路由 - [更新日志](./CHANGELOG.md) — 版本历史与迁移说明 ## 贡献指南 diff --git a/docs/devapp-agent-install-guide.md b/docs/devapp-agent-install-guide.md deleted file mode 100644 index 80d940bb..00000000 --- a/docs/devapp-agent-install-guide.md +++ /dev/null @@ -1,303 +0,0 @@ -# DevApp 一键安装与 Agent 接入指南 - -面向希望用 Codex、Claude、Cursor 等开发 Agent 管理钉钉开放平台应用的开发者。 - -这份指南参考 Notion Developer Platform 的引导方式:先给出一条可复制的安装命令,再用最短路径完成验证、登录、Agent 调用和排障。 - -## 一键安装 - -当前 DevApp 能力在 `feat/dws-devapp` 预览分支上。要安装这个分支里的最新能力,请使用 DevApp 专用安装脚本: - -```bash -curl -fsSL https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/feat/dws-devapp/scripts/install-devapp.sh | sh -``` - -这个脚本会: - -1. 拉取 `wxianfeng/dingtalk-workspace-cli` 的 `feat/dws-devapp` 分支。 -2. 使用本地源码构建 `dws`。 -3. 安装 `dws` 到默认目录 `~/.local/bin`。 -4. 安装 Agent Skill 到本机已检测到的 Agent 目录。 - -> 预览分支安装需要本机已有 `git`、`go` 和 `make`。Go 版本要求以仓库 `go.mod` 为准。 - -如果 DevApp 能力已经发布到正式 Release,可以改用正式安装命令: - -```bash -curl -fsSL https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/main/scripts/install.sh | sh -``` - -Windows PowerShell 正式安装命令: - -```powershell -irm https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/main/scripts/install.ps1 | iex -``` - -安装脚本支持这些环境变量: - -| 变量 | 说明 | -|---|---| -| `DEVAPP_REPO_URL` | 覆盖源码仓库地址,默认 `https://github.com/wxianfeng/dingtalk-workspace-cli.git` | -| `DEVAPP_BRANCH` | 覆盖安装分支,默认 `feat/dws-devapp` | -| `DEVAPP_SOURCE_DIR` | 使用已有源码目录安装,跳过 clone | -| `DEVAPP_KEEP_SOURCE=1` | 保留临时源码目录,便于调试 | -| `DWS_INSTALL_DIR` | 传给底层 `scripts/install.sh`,覆盖 `dws` 安装目录 | -| `DWS_SKILL_MODE` | 传给底层 `scripts/install.sh`,选择 `mono` 或 `multi` | - -## 安装后验证 - -先确认 `dws` 可执行: - -```bash -dws version -``` - -确认 DevApp 命令存在: - -```bash -dws devapp --help --format json -``` - -如果能看到 `list`、`get`、`create`、`permission`、`robot`、`security`、`version` 等能力,说明 DevApp 已安装成功。 - -确认登录状态: - -```bash -dws auth status -``` - -如果尚未登录: - -```bash -dws auth login -``` - -登录完成后读取应用列表: - -```bash -dws devapp list --format json -``` - -## DevApp 是什么 - -DevApp 是开放平台应用管理能力的 CLI 和 Agent Skill 入口。安装后,开发者和 Agent 可以用统一命令管理企业内部应用,而不需要反复进入开发者后台页面。 - -它让 Agent 可以完成这些工作: - -- 查询、创建、更新、启用、停用、删除开放平台应用。 -- 查询应用凭证,读取 `clientId` / `appKey`,敏感凭证走专用命令。 -- 配置网页应用首页和管理后台地址。 -- 查询、申请、移除权限点。 -- 管理应用成员。 -- 配置安全项,包括 IP 白名单、登录重定向 URL、端内免登地址。 -- 创建、查询、更新、启用、停用机器人。 -- 创建版本、发起发布、查询审批和发布状态。 - -## 给 Agent 使用 - -安装完成后,可以直接让 Agent 操作 DevApp。 - -示例: - -```text -帮我查一下最近创建的开放平台应用。 -``` - -```text -帮我给 agentId= 的应用配置机器人,先 dry-run 给我确认。 -``` - -```text -帮我查询这个应用缺哪些权限点,并申请 Contact.User.mobile。 -``` - -```text -帮我发布这个应用版本,先检查发布前置条件。 -``` - -Agent 写操作必须遵循: - -1. 先查询定位应用。 -2. 先 dry-run 预览。 -3. 明确展示将要修改的应用、字段和值。 -4. 用户确认后加 `--yes` 执行。 -5. 执行后回读验证。 - -## 第一个写操作 - -推荐用机器人配置作为 smoke test。先 dry-run: - -```bash -dws devapp robot config \ - --unified-app-id \ - --name "告警机器人" \ - --brief "告警通知" \ - --description "处理告警通知和事件回调" \ - --dry-run \ - --format json -``` - -确认预览无误后执行: - -```bash -dws devapp robot config \ - --unified-app-id \ - --name "告警机器人" \ - --brief "告警通知" \ - --description "处理告警通知和事件回调" \ - --yes \ - --format json -``` - -回读验证: - -```bash -dws devapp robot get --unified-app-id --format json -``` - -## 常用命令 - -### 应用管理 - -```bash -dws devapp list --format json -dws devapp get --agent-id --format json -dws devapp create --app-name "考勤应用" --dry-run --format json -dws devapp update --agent-id --app-name "新应用名" --dry-run --format json -dws devapp inactive --agent-id --dry-run --format json -dws devapp active --agent-id --dry-run --format json -dws devapp delete --agent-id --dry-run --format json -``` - -### 凭证查询 - -```bash -dws devapp credentials get --agent-id --format json -``` - -凭证输出可能包含敏感字段,不要把完整结果写入文档、日志或长期记忆。 - -### 权限点管理 - -```bash -dws devapp permission list --agent-id --format json -dws devapp permission add --agent-id --scope-values Contact.User.mobile --dry-run --format json -dws devapp permission remove --agent-id --scope-value Contact.User.mobile --dry-run --format json -``` - -权限申请和移除只使用 `scopeValue`,不要传 API 名或权限分组名。 - -### 机器人配置 - -```bash -dws devapp robot get --unified-app-id --format json -dws devapp robot config --unified-app-id --name "机器人名称" --dry-run --format json -dws devapp robot update --unified-app-id --brief "新的简介" --dry-run --format json -dws devapp robot enable --unified-app-id --name "机器人名称" --dry-run --format json -dws devapp robot offline --unified-app-id --dry-run --format json -``` - -### 成员与安全 - -```bash -dws devapp member list --agent-id --format json -dws devapp member add --agent-id --user-ids --dry-run --format json -dws devapp member remove --agent-id --user-ids --dry-run --format json -dws devapp security config --unified-app-id --redirect-urls --dry-run --format json -dws devapp security config --unified-app-id --ip-white-list --dry-run --format json -``` - -### 版本发布 - -```bash -dws devapp version list --unified-app-id --format json -dws devapp version create --unified-app-id --dry-run --format json -dws devapp version publish --unified-app-id --version-id --dry-run --format json -dws devapp version status --unified-app-id --version-id --format json -``` - -## 安全边界 - -DevApp 的目标不是绕过开发者后台权限,而是让 CLI、MCP 和 Web 后台保持一致。 - -默认安全策略: - -- 写操作先 dry-run。 -- 删除、停用、发布必须由用户确认。 -- Agent 不接收用户手动传入的 access token、cookie、`clientSecret`、`appSecret`。 -- 应用定位优先使用 `agentId`、`unifiedAppId`、`appKey`。 -- 对权限点申请、成员变更、安全配置、版本发布记录操作结果,便于审计和回滚。 - -## 排障 - -### `dws devapp` 不存在 - -先确认安装的是预览分支源码,而不是正式 Release: - -```bash -dws version -dws devapp --help --format json -``` - -如果正式 Release 尚未包含 DevApp,请重新执行本文的一键源码安装命令。 - -### `dws devapp list` 失败 - -优先检查登录态: - -```bash -dws auth status -dws auth login -``` - -然后确认当前账号能访问目标企业,并且当前用户在目标企业内。 - -### 页面能操作,但 CLI 或 MCP 提示无权限 - -通常说明 CLI/MCP 后端鉴权和 Web 后台权限没有对齐。 - -先确认当前用户是否满足以下任一条件: - -- 应用 owner。 -- 应用管理员。 -- 应用开发者。 -- 企业管理员或具备开放平台应用管理权限的角色。 - -### 机器人配置失败 - -先查当前机器人状态: - -```bash -dws devapp robot get --unified-app-id --format json -``` - -如果机器人不存在,使用 `robot config` 创建或配置。 -如果机器人已存在,用 `robot update` 修改配置,或用 `robot enable` 重新启用。 - -## 页面文案建议 - -用于产品页顶部: - -```text -Install DevApp in one command. - -Let your coding agents manage DingTalk Open Platform apps from the terminal: -create apps, configure robots, apply permissions, manage security settings, -and publish versions with dry-run safety built in. -``` - -中文版本: - -```text -一行命令接入 DevApp。 - -让 Codex、Claude、Cursor 等开发 Agent 直接管理钉钉开放平台应用: -创建应用、配置机器人、申请权限、管理安全配置、发布版本。 -所有写操作先预览,再确认执行。 -``` - -## 参考 - -- Notion Developer Platform: https://www.notion.com/product/dev -- Notion CLI Help: https://www.notion.com/help/use-notion-from-your-terminal-with-notion-cli -- Notion Developer Platform Blog: https://www.notion.com/blog/introducing-developer-platform diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md deleted file mode 100644 index ced85339..00000000 --- a/docs/devapp-yulan-command-routing.md +++ /dev/null @@ -1,1501 +0,0 @@ -# yulan Open Platform App Command Routing - -> Status: draft, 2026-06-05 -> Branch: `feat/dws-devapp` -> Scope: open-source DWS integration for DingTalk Open Platform enterprise app management. - -This document splits the application-side command set for yulan. It is written for the open-source DWS repository and must not contain personal MCP gateway keys, pre-release tokens, cookies, or production app data. - -## 0. Current Repository Deliverables - -The current branch contains the application-side command contract, Agent -routing assets, and a hardcoded `devapp` helper command tree. `devapp` does not -depend on MCP service discovery or registry overlay publication. Runtime calls -resolve the MCP endpoint from the internal edition's `SupplementServers` or -`StaticServers` hook; local debug may override it with -`DINGTALK_DEVAPP_MCP_URL`. Do not commit the endpoint URL or its `?key=` value. - -| Artifact | Status | Evidence | -| --- | --- | --- | -| Main design document | Done in repo | `docs/devapp-yulan-command-routing.md` covers naming, command tree, P0 RPC contracts, permissions, Agent routing, rollout, and acceptance cases. | -| README discoverability | Done in repo | `README.md` and `README_zh.md` link to this design under Reference/参考文档. | -| Mono skill routing | Done in repo | `skills/mono/SKILL.md` routes Open Platform app intents to `devapp`. | -| Mono product reference | Done in repo | `skills/mono/references/products/devapp.md` summarizes app CRUD, permissions, credentials, events, versions, and troubleshooting. | -| Multi product skill | Done in repo | `skills/multi/dingtalk-devapp/` defines the standalone `dingtalk-devapp` skill and reference file. | -| Agent intent test cases | Done in repo | `test/skill_tests.md` includes 21 devapp cases, devdoc/workbench counterexamples, and routing clarification cases for the overloaded word `应用`; `test/skill_tests_results.md` reports those cases passing. | -| Skill test parser | Done in repo | `test/run_skill_tests.py` now preserves the current `#### dws ...` command section for each case. | -| Static/e2e skill verifier handling | Done in repo | `test/skill_static` and `test/skill_e2e` cover routing docs; runtime smoke uses an externally configured MCP endpoint. | -| Sensitive config guard | Done in repo | `.gitignore` excludes local `docs/mcp/serviceconfig-pre*` files so personal MCP gateway keys are not staged. | -| MCP HSF tool configuration | External dependency | Configure and publish tools on the DingTalk MCP developer platform using sections 3, 4, and 13. | -| DWS runtime command availability | Done in repo | `dws devapp ...` and alias `dws app ...` are hardcoded helper commands. No service discovery is required for this command tree. | -| Full production readiness | Not yet proven | Requires section 13 release exit criteria, including MCP `tools/list`, DWS schema, APP/SNS permission smoke, and write guard smoke. | - -State labels: - -1. `Done in repo` means the open-source branch contains the artifact and it is - covered by local checks. -2. `External dependency` means backend/MCP tool publication must happen outside - this repository. -3. `Not yet proven` means do not claim the application command set is ready for - Agent production usage until runtime smoke evidence exists. - -## 1. Naming - -Use `devapp` as the canonical open-source product id: - -```text -dws devapp ... -``` - -`app` is kept as a compatibility alias by the hardcoded helper. Do not -introduce `opendev` or `apps` as new canonical roots; if they are needed for -migration, make them redirect or hidden aliases. - -Reasoning: - -1. `devapp` is specific enough for Open Platform developer applications. -2. `app` is the PRD/user-facing shorthand and can remain as an alias. -3. The open-source DWS repository is protocol-first; `devapp` uses helper commands so it can run without service discovery. - -Branch context: - -```text -branch: feat/dws-devapp -base: latest upstream/main has been merged into the local design branch -runtime: devapp command availability does not depend on MCP service discovery -``` - -The checked-in open-source helper code owns the `dws devapp` command tree. The -MCP endpoint is owned by the internal edition layer, typically through -`edition.SupplementServers`; `DINGTALK_DEVAPP_MCP_URL` remains a local debug -override. - -## 2. Command Tree - -```text -dws devapp - list Search app list with listByCondition semantics - get Get one app detail - create Create an enterprise internal app - update Update app base information - delete Delete an enterprise internal app - credentials get Controlled credential read, especially appSecret/clientSecret - webapp get Get web app configuration - permission list List APP and SNS permissions - permission search Search permission candidates, alias over list semantics - permission detail Show one permission's full API coverage - permission add Apply permissions into current app version changes - permission remove Remove one authorized permission - event list List event subscription config - event config Configure callback URL, token, aesKey, event types - robot create Create a new agent app + robot synchronously - robot submit Submit async robot-create task (supports retry by taskId) - robot result Query async robot-create task result - robot get Get robot config of an existing app - robot config Create robot config on an existing app - robot update Update robot config - robot enable Enable / re-enable robot capability - robot offline Offline robot capability - version create Save app version - version list List app versions (paged) - version get Get one version detail - version check-approval Precheck publish approval requirements (no publish) - version publish Submit publish, possibly creating approval - version status Poll publish and approval status -``` - -Robot and version commands are hardcoded helper leaves over the `op-app` MCP -server (same pinned endpoint as the rest of `devapp`, no service discovery). -`event` is still spec-only and reports unsupported at runtime. - -Open-source examples should use `--format json` for agent consumption: - -```bash -dws devapp list --name DemoApp --page 1 --page-size 20 --format json -dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp permission list --unified-app-id UNIFIED_APP_ID --format json -``` - -For write operations, DWS should use the existing open-source safety model: - -```bash -dws devapp create --name DemoApp --type internal --desc "internal app" --dry-run --format json -dws devapp create --name DemoApp --type internal --desc "internal app" --yes --format json -``` - -Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confirmDelete`, `confirmPermission`, or `confirmSensitive` to MCP or HSF. Confirmation is only a CLI/Agent guard. - -## 3. Capability Matrix - -| Area | CLI command | MCP tool | Backend HSF facade | Current backend status | Open-source DWS status | -| --- | --- | --- | --- | --- | --- | -| App query | `devapp list` | `list_open_dev_apps_by_condition` | `OpenInnerAppQueryFacade.listByCondition` | Implemented | Hardcoded helper | -| App detail | `devapp get` | `get_open_dev_app_detail` | `OpenInnerAppQueryFacade.getDetail` | Implemented | Hardcoded helper | -| App create | `devapp create` | `create_inner_app` | `OpenInnerAppManageFacade.create` | Implemented | Hardcoded helper | -| App update | `devapp update` | `update_inner_app` | `OpenInnerAppManageFacade.update` | Implemented | Hardcoded helper | -| App delete | `devapp delete` | `delete_inner_app` | `OpenInnerAppManageFacade.delete` | Implemented | Hardcoded helper | -| Credentials | `devapp credentials get` | `get_open_dev_app_credentials` | `OpenInnerAppQueryFacade.getCredentials` | Implemented | Hardcoded helper | -| Web app | `devapp webapp get/config` | `get_webapp_config`/`set_webapp_config` | Webapp facade | Implemented | Hardcoded helper; verified `config` then `get` returns `agentId/h5PageType/homepageLink/pcHomepageLink/ompLink`; before config, `get` may only return `agentId`. | -| Permission list | `devapp permission list/search/detail` | `list_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | -| Permission apply | `devapp permission add` | `apply_open_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | -| Permission remove | `devapp permission remove` | `remove_open_dev_app_permission` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | -| Events | `devapp event list/config` | `list/config_open_dev_app_events` | Event facade pending | Pending | Spec only | -| Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | -| Robot config | `devapp robot get/config/update/enable/offline` | `get/create/update_open_dev_app_robot_config`, `enable/offline_open_dev_app_robot` | Implemented | Hardcoded helper | -| Version | `devapp version create/list/get/check-approval/publish/status` | `create/list_open_dev_app_versions`, `get_open_dev_app_version_detail/status`, `publish_open_dev_app_version` | Implemented | Hardcoded helper | - -P0 for yulan is app CRUD, permissions, credentials, and web app config. Robot -and version are now implemented as hardcoded helper leaves over the same -`op-app` MCP server; events remain a pending backend slice. - -### 3.1 P0 RPC Contract - -All P0 MCP tools should be configured as HSF-backed tools that call one facade -method with one business request object. Do not expose a separate -`BaseOpenRequestVO` to the Agent. The provider should receive caller identity -from MCP system context and merge it into the business request before executing -the facade method. - -Common HSF mapping: - -| Source | Target request field | Public CLI flag | Required | Notes | -| --- | --- | --- | --- | --- | -| MCP system org context | `corpId` | No | Yes | DingTalk corpId. Do not ask the user for internal `orgId`. | -| MCP system user context | `userId` | No | Yes | DingTalk user/staff id. Do not ask the user for internal `uid`. | -| CLI/MCP tool input | business fields | Yes | Per tool | Search, app locator, update fields, permission scopes. | - -Common result wrapper: - -```json -{ - "success": true, - "result": {}, - "errorCode": "", - "errorMsg": "", - "arguments": [] -} -``` - -MCP output configuration should keep the wrapper fields and configure success as -`ServiceResult.success == true`. DWS/Agent normalization can add `ok`, `reason`, -`summary`, and `nextAction`, but raw `errorCode/errorMsg` must remain visible. - -### 3.2 P0 Tool Contracts - -#### list_open_dev_apps_by_condition - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp list` | -| HSF facade | `OpenInnerAppQueryFacade.listByCondition` | -| Request shape | One object with system context and optional list filters. | -| Required public fields | None; default `currentPage=1`, `pageSize=20`. | -| Identity source | `corpId/userId` from MCP system context. | - -Input fields: - -| Field | Type | CLI flag | Required | Notes | -| --- | --- | --- | --- | --- | -| `currentPage` | number | `--page` | no | 1-based page number. | -| `pageSize` | number | `--page-size` | no | Default 20; cap by backend. | -| `appName` | string | `--name` | no | App name keyword. | -| `agentId` | number | `--agent-id` | no | Exact DingTalk app agentId. | -| `appId` | number | `--app-id` | no | Compatible app id. | -| `appKey` | string | `--app-key` | no | DingTalk appKey/clientId. | -| `customKey` | string | `--custom-key` | no | Custom key. | -| `appGroupId` | number | `--app-group-id` | no | App group id. | -| `creator` | string | `--creator` | no | Creator name keyword. | -| `robotName` | string | `--robot-name` | no | Bot name keyword. | -| `developType` | number/string | `--develop-type` | no | Use backend enum if schema exposes one. | -| `filterCoolApp` | number/boolean | `--filter-cool-app` | no | Only expose when backend semantics are documented. | -| `sortType` | string | `--sort` | no | Example: `gmt_modified`. | -| `sortOrder` | string | `--order` | no | `asc` or `desc`. | - -Normalized response: - -```json -{ - "ok": true, - "operation": "app.list", - "page": {"currentPage": 1, "pageSize": 20, "total": 0}, - "apps": [ - { - "unifiedAppId": "UNIFIED_APP_ID", - "appName": "DemoApp", - "agentId": 123456, - "appId": 123, - "appKey": "dingxxx", - "customKey": "custom-key", - "creator": "Creator", - "robotName": "BotName", - "gmtModified": "2026-06-05T00:00:00+08:00" - } - ], - "raw": {} -} -``` - -#### get_open_dev_app_detail - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp get` | -| HSF facade | `OpenInnerAppQueryFacade.getDetail` | -| Request shape | One object with system context and one or more app locator fields. | -| Required public fields | At least one locator. | -| Identity source | `corpId/userId` from MCP system context. | - -Locator fields: - -| Field | Type | CLI flag | Notes | -| --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | Preferred. | -| `agentId` | number | `--agent-id` | Exact id. | -| `appId` | number | `--app-id` | Compatible id. | -| `appKey` | string | `--app-key` | appKey/clientId. | -| `customKey` | string | `--custom-key` | Custom key. | -| `appName` | string | `--name` | Fuzzy; must resolve uniquely before writes. | - -Detail response should include identifiers and configuration summary, but not -full `clientSecret/appSecret`: - -```json -{ - "ok": true, - "operation": "app.get", - "app": { - "unifiedAppId": "UNIFIED_APP_ID", - "appName": "DemoApp", - "agentId": 123456, - "appId": 123, - "appKey": "dingxxx", - "customKey": "custom-key", - "developType": "internal", - "creator": "Creator", - "visibility": "partial_members", - "status": "online", - "gmtModified": "2026-06-05T00:00:00+08:00" - }, - "raw": {} -} -``` - -#### create_inner_app - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp create` | -| HSF facade | `OpenInnerAppManageFacade.create` | -| Request shape | One object with system context and app base fields. | -| Required public fields | `appName`; `--type` is a CLI-only guard and currently must be `internal`. | -| Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | - -Input fields: - -| Field | Type | CLI flag | Required | Notes | -| --- | --- | --- | --- | --- | -| `appName` | string | `--name` | yes | Enterprise internal app name. | -| `appDesc` | string | `--desc` | no | App description. | -| `appIcon` | string | `--icon` | no | Existing media/resource id if backend accepts it. | - -Response: - -```json -{ - "ok": true, - "operation": "app.create", - "app": { - "unifiedAppId": "UNIFIED_APP_ID", - "appName": "DemoApp", - "agentId": 123456, - "appKey": "dingxxx" - }, - "nextAction": "run devapp get or configure permissions", - "raw": {} -} -``` - -#### update_inner_app - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp update` | -| HSF facade | `OpenInnerAppManageFacade.update` | -| Request shape | One object with system context, locator, and changed fields. | -| Required public fields | One app locator and at least one changed field. | -| Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | - -Locator fields are the same as `get_open_dev_app_detail`, but the Agent should -prefer `unifiedAppId` after resolving a unique app. - -Update fields: - -| Field | Type | CLI flag | Notes | -| --- | --- | --- | --- | -| `appName` | string | `--name` | New app name. | -| `appDesc` | string | `--desc` | New app description. | -| `appIcon` | string | `--icon` | New icon resource id if backend accepts it. | -| `international` | object/string | `--international` | Only expose when schema describes the shape. | -| `supportHarmony` | boolean | `--support-harmony` | Harmony support switch if backend exposes it. | - -Response should return the updated app summary plus a `changedFields` array when -available. - -#### delete_inner_app - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp delete` | -| HSF facade | `OpenInnerAppManageFacade.delete` | -| Request shape | One object with system context and one unique app locator. | -| Required public fields | One unique app locator. | -| Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | - -The Agent must display the resolved app summary before executing delete with -`--yes`. The response should include `deleted=true`, the app summary, and raw -backend fields. - -#### list_open_dev_app_permissions - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp permission list/search/detail` | -| HSF facade | `OpenInnerAppPermissionFacade.list` | -| Request shape | One object with system context, app locator, and optional filters. | -| Required public fields | One app locator. | -| Web semantics | Equivalent to grouped `scope/list` for APP and SNS scopes. | - -Input fields: - -| Field | Type | CLI flag | Required | Notes | -| --- | --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | one locator | Preferred. | -| `agentId` | number | `--agent-id` | one locator | Compatible. | -| `keyword` | string | `--keyword` | no | Maps to Web `key`. | -| `apiStatus` | string | `--api-status` | no | Web `apiStatus` filter. | -| `authStatus` | string | `--status` | no | `ALL/AUTHED/UNAUTHED`, post-filter. | -| `firstLevelType` | string | `--scope-type` | no | `APP/SNS`; empty returns both. | -| `scopeValue` | string | `--scope` | no | Detail mode and exact post-filter. | -| `limit` | number | `--limit` | no | Agent display cap; no server pagination. | - -Normalized response should flatten `appScopes` and `snsScopes` into one -`permissions[]` list while preserving group metadata: - -```json -{ - "ok": true, - "operation": "permission.list", - "mode": "list", - "permissions": [ - { - "scopeValue": "qyapi_robot_sendmsg", - "scopeName": "企业内机器人发消息权限", - "firstLevelType": "APP", - "categoryTitle": "机器人", - "authed": true, - "canEdit": true, - "requiredApproval": false, - "allowedActions": ["view", "detail", "remove"], - "apiCount": 4, - "apiPreview": [{"name": "API name"}] - } - ], - "summary": {"totalMatched": 1, "returnedCount": 1}, - "truncated": false, - "raw": {} -} -``` - -#### apply_open_dev_app_permissions - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp permission add` | -| HSF facade | `OpenInnerAppPermissionFacade.apply` | -| Request shape | One object with system context, app locator, and `scopeValues`. | -| Required public fields | One app locator and non-empty `scopeValues`. | -| Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | - -Input fields: - -| Field | Type | CLI flag | Required | Notes | -| --- | --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | one locator | Preferred. | -| `agentId` | number | `--agent-id` | one locator | Compatible. | -| `scopeValues` | string[] | `--permissions` | yes | Comma-separated in CLI; must come from list response. | - -Response must distinguish direct apply, version-staged apply, rejected scopes, and -skipped scopes: - -```json -{ - "ok": true, - "operation": "permission.add", - "appliedScopeValues": ["Contact.User.mobile"], - "directAppliedScopeValues": [], - "versionStagedScopeValues": ["Contact.User.mobile"], - "rejectedScopeValues": [], - "skippedScopeValues": [], - "nextAction": "run devapp version check-approval", - "raw": {} -} -``` - -`requiredApproval=true` scopes must be accepted when editable and staged into the -current app version. Approver selection is handled by the version flow. - -#### remove_open_dev_app_permission - -| Item | Contract | -| --- | --- | -| CLI | `dws devapp permission remove` | -| HSF facade | `OpenInnerAppPermissionFacade.remove` | -| Request shape | One object with system context, app locator, and one `scopeValue`. | -| Required public fields | One app locator and one `scopeValue`. | -| Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | - -Input fields: - -| Field | Type | CLI flag | Required | Notes | -| --- | --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | one locator | Preferred. | -| `agentId` | number | `--agent-id` | one locator | Compatible. | -| `scopeValue` | string | `--permission` | yes | Remove exactly one permission. | - -Expected structured failures: - -| Condition | Normalized reason | -| --- | --- | -| Scope is not currently authorized | `PERMISSION_NOT_AUTHED` | -| Scope cannot be edited | `PERMISSION_NOT_EDITABLE` | -| Scope cannot be found | `PERMISSION_NOT_FOUND` | - -## 4. Hardcoded Helper Mapping - -The open-source branch hardcodes the following command root and tool mapping. -This JSON is kept as a compact mapping reference only; `devapp` runtime command -availability must not depend on MCP registry publication or service discovery. - -```json -{ - "com.dingtalk.dws/helper/cli": { - "id": "devapp", - "command": "devapp", - "description": "开放平台应用管理", - "aliases": ["app"], - "groups": { - "credentials": {"description": "应用凭证"}, - "permission": {"description": "应用权限"}, - "webapp": {"description": "网页应用"}, - "event": {"description": "事件订阅"}, - "version": {"description": "版本发布"} - } - } -} -``` - -Tool override examples: - -```json -{ - "toolOverrides": { - "list_open_dev_apps_by_condition": { - "cliName": "list", - "description": "查询开放平台企业内部应用列表", - "flags": { - "currentPage": {"alias": "page", "default": 1}, - "pageSize": {"alias": "page-size", "default": 20}, - "appName": {"alias": "name"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, - "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"}, - "creator": {"alias": "creator"}, - "robotName": {"alias": "robot-name"}, - "sortType": {"alias": "sort"}, - "sortOrder": {"alias": "order"} - } - }, - "get_open_dev_app_detail": { - "cliName": "get", - "description": "查询开放平台企业内部应用详情", - "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, - "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"}, - "appName": {"alias": "name"} - } - }, - "create_inner_app": { - "cliName": "create", - "description": "创建开放平台企业内部应用", - "isSensitive": true, - "flags": { - "appName": {"alias": "name", "required": true}, - "appDesc": {"alias": "desc"}, - "appIcon": {"alias": "icon"} - } - }, - "update_inner_app": { - "cliName": "update", - "description": "修改开放平台企业内部应用基础信息", - "isSensitive": true, - "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, - "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"}, - "currentAppName": {"alias": "current-name"}, - "appName": {"alias": "name"}, - "appDesc": {"alias": "desc"}, - "appIcon": {"alias": "icon"}, - "international": {"alias": "international"}, - "supportHarmony": {"alias": "support-harmony", "type": "bool"} - } - }, - "delete_inner_app": { - "cliName": "delete", - "description": "删除开放平台企业内部应用", - "isSensitive": true, - "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "appId": {"alias": "app-id"}, - "appName": {"alias": "name"}, - "appKey": {"alias": "app-key"}, - "customKey": {"alias": "custom-key"} - } - }, - "list_open_dev_app_permissions": { - "cliName": "list", - "group": "permission", - "description": "查询开放平台应用权限列表,默认同时返回 APP 应用权限和 SNS 个人权限", - "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "keyword": {"alias": "keyword"}, - "apiStatus": {"alias": "api-status"}, - "authStatus": {"alias": "status", "default": "ALL"}, - "firstLevelType": {"alias": "scope-type"}, - "scopeValue": {"alias": "scope"}, - "limit": {"alias": "limit", "default": "20"} - } - }, - "apply_open_dev_app_permissions": { - "cliName": "add", - "group": "permission", - "description": "申请开放平台应用权限点;需审核权限写入当前应用版本变更", - "isSensitive": true, - "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "scopeValues": { - "alias": "permissions", - "type": "stringSlice", - "required": true, - "description": "待申请权限点 scopeValue,多个用逗号分隔" - } - } - }, - "remove_open_dev_app_permission": { - "cliName": "remove", - "group": "permission", - "description": "取消一个已开通的开放平台应用权限点", - "isSensitive": true, - "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "agentId": {"alias": "agent-id"}, - "scopeValue": { - "alias": "permission", - "required": true, - "description": "待取消权限点 scopeValue" - } - } - } - } -} -``` - -`permission search/detail` are UX aliases over `list_open_dev_app_permissions`. The helper exposes one canonical MCP call and keeps alias behavior at the CLI layer: - -```bash -dws devapp permission list --keyword "机器人发送消息" -dws devapp permission list --scope qyapi_robot_sendmsg -``` - -If a helper command is added, map aliases without creating new MCP tools: - -| Helper command | Underlying tool | Forced/derived args | -| --- | --- | --- | -| `devapp permission search` | `list_open_dev_app_permissions` | Requires `keyword` or `scopeValue`; returns candidate summaries. | -| `devapp permission detail` | `list_open_dev_app_permissions` | Requires `scopeValue`; returns one scope with full `apiList`. | - -## 5. App Identity Resolution - -Application identity must be resolved before detail and write commands. - -| Priority | Identifier | Rule | -| --- | --- | --- | -| 1 | `unifiedAppId` | Preferred unique id for write operations. | -| 2 | `agentId/appId` | Compatible exact identifier. | -| 3 | `appKey/clientId` | Call list/detail and require one result. | -| 4 | `customKey` | Call list/detail and require one result. | -| 5 | `appName/name` | Fuzzy search; write operations require one result. | - -Agent rule: - -1. Never choose the first row when multiple apps match. -2. Show candidates with `appName`, `unifiedAppId`, `agentId`, `appKey/clientId`, `creator`, and modified time. -3. Ask the user to pick `unifiedAppId` or `agentId` for write operations. - -MCP calls must rely on system context for caller identity: - -| System context | HSF request field | Notes | -| --- | --- | --- | -| Caller org | `corpId` | DingTalk corpId, not internal orgId. | -| Caller user | `userId` | DingTalk userId/staffId; backend resolves uid. | - -Open-source DWS and Agent must not pass `orgId` or `uid` as user input. - -## 6. App CRUD - -### 6.1 List - -```bash -dws devapp list --page 1 --page-size 20 --format json -dws devapp list --name DemoApp --format json -dws devapp list --agent-id AGENT_ID --format json -dws devapp list --app-key dingxxx --format json -dws devapp list --creator 张三 --format json -dws devapp list --robot-name 小钉 --format json -``` - -MCP payload: - -```json -{ - "currentPage": 1, - "pageSize": 20, - "appName": "DemoApp", - "sortType": "gmt_modified", - "sortOrder": "desc" -} -``` - -Pagination is supported for app list through `currentPage/pageSize`, both 1-based. Search filters are optional. A baseline call should pass only pagination and sort fields. - -### 6.2 Get - -```bash -dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --agent-id AGENT_ID --format json -dws devapp get --name DemoApp --format json -``` - -MCP payload: - -```json -{ - "unifiedAppId": "UNIFIED_APP_ID" -} -``` - -`get` may show `clientId/appKey/agentId`, but it must not be used to read full `clientSecret/appSecret`. Secret read belongs to `credentials get`. - -### 6.3 Create - -```bash -dws devapp create --name DemoApp --desc "internal app" --type internal --dry-run --format json -dws devapp create --name DemoApp --desc "internal app" --type internal --yes --format json -``` - -Current P0 create supports enterprise internal application basics: - -| Type | Supported now | Notes | -| --- | --- | --- | -| `internal` | Yes | Default enterprise internal app. | -| `h5` | Yes, as internal web capability flavor if backend accepts it | Use only if backend schema exposes it. | -| `robot` | Not as app-create P0 | Bot config is a separate capability. | -| `miniapp` | Not P0 | Needs separate capability/backend confirmation. | -| `isv/connector` | Not P0 | Not yulan app CRUD. | - -MCP payload: - -```json -{ - "appName": "DemoApp", - "appDesc": "internal app" -} -``` - -### 6.4 Update - -```bash -dws devapp update --unified-app-id UNIFIED_APP_ID --name DemoApp2 --desc "new desc" --dry-run --format json -dws devapp update --unified-app-id UNIFIED_APP_ID --name DemoApp2 --desc "new desc" --yes --format json -``` - -At least one update field is required. Supported P0 fields: - -| CLI flag | MCP field | Notes | -| --- | --- | --- | -| `--name` | `appName` | New app name. | -| `--desc` | `appDesc` | New description. | -| `--icon` | `appIcon` | Existing media id or backend-supported resource. | -| `--international` | `international` | JSON string if exposed. | -| `--support-harmony` | `supportHarmony` | Boolean if exposed. | - -### 6.5 Delete - -```bash -dws devapp delete --unified-app-id UNIFIED_APP_ID --dry-run --format json -dws devapp delete --unified-app-id UNIFIED_APP_ID --yes --format json -``` - -Delete is destructive. The CLI/Agent must show the target app summary before calling the tool with `--yes`. - -MCP payload: - -```json -{ - "unifiedAppId": "UNIFIED_APP_ID" -} -``` - -Valid locator fields are `unifiedAppId`, `agentId`, `appId`, `appName`, `appKey`, and `customKey`. Name/key locators must uniquely resolve. - -## 7. Credentials - -Target command: - -```bash -dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --agent-id AGENT_ID --format json -``` - -Current status: - -1. Backend facade is `OpenInnerAppQueryFacade.getCredentials`. -2. MCP tool is `get_open_dev_app_credentials`. -3. CLI sends only application locator fields and does not add `showSecret`, `confirmSecret`, or masking fields. -4. The MCP response follows the backend credential contract and may include `clientSecret/appSecret`; callers must treat command output as sensitive. - -Target fields: - -| Field | Notes | -| --- | --- | -| `unifiedAppId/agentId/appName` | App summary. | -| `clientId/appKey` | Non-secret identifiers. | -| `clientSecret/appSecret` | Returned by the dedicated credentials tool; treat as sensitive output. | -| `currentSecretStatus` | Current secret state. | -| `pendingExpireTask` | Pending secret-expiration task information when present. | - -## 8. Permissions - -### 8.1 Web Semantics - -Permission list must align with the developer console endpoint: - -```text -GET /openapp/unifiedapp/{unifiedAppId}/scope/list?from=inner&key={keyword}&apiStatus={apiStatus} -``` - -Returned groups: - -```json -{ - "success": true, - "data": { - "snsScopes": [ - {"title": "个人权限", "firstLevelType": "SNS", "scopeList": []} - ], - "appScopes": [ - {"title": "通讯录管理", "firstLevelType": "APP", "scopeList": []} - ] - } -} -``` - -Do not split the main list into old `listAuthed/listAvailable` APIs. Use one list operation that returns both authorized and unauthorized scopes. - -### 8.2 List, Search, Detail - -```bash -dws devapp permission list --unified-app-id UNIFIED_APP_ID --format json -dws devapp permission list --unified-app-id UNIFIED_APP_ID --status unauthed --keyword "发送消息" --format json -dws devapp permission list --unified-app-id UNIFIED_APP_ID --scope-type SNS --format json -dws devapp permission list --unified-app-id UNIFIED_APP_ID --scope qyapi_robot_sendmsg --format json -``` - -MCP payload for normal list: - -```json -{ - "unifiedAppId": "UNIFIED_APP_ID", - "authStatus": "ALL", - "limit": 20 -} -``` - -MCP payload for search: - -```json -{ - "unifiedAppId": "UNIFIED_APP_ID", - "keyword": "机器人发送消息", - "authStatus": "UNAUTHED", - "limit": 10 -} -``` - -MCP payload for detail: - -```json -{ - "unifiedAppId": "UNIFIED_APP_ID", - "scopeValue": "qyapi_robot_sendmsg", - "limit": 1 -} -``` - -Input fields: - -| Field | Type | Required | Notes | -| --- | --- | --- | --- | -| `unifiedAppId` | string | one of app id fields | Preferred app locator. | -| `agentId` | number | one of app id fields | Compatible app locator. | -| `keyword` | string | no | Maps to Web `key`. | -| `apiStatus` | string | no | Maps to Web `apiStatus`. | -| `authStatus` | string | no | `ALL/AUTHED/UNAUTHED`, post-filter. | -| `firstLevelType` | string | no | `APP/SNS`; empty returns both. | -| `scopeValue` | string | no | Detail mode and exact post-filter. | -| `limit` | number | no | Default 20, hard cap 50. | - -Permission list has no server pagination. Web also loads the full grouped list. Use `keyword` to reduce server-side results, then use `limit` to control Agent context. - -Default response must flatten APP and SNS scopes into one stable `permissions[]` array: - -```json -{ - "mode": "list", - "permissions": [ - { - "scopeValue": "qyapi_robot_sendmsg", - "scopeName": "企业内机器人发消息权限", - "scopeDesc": "企业内机器人发消息权限", - "firstLevelType": "APP", - "categoryTitle": "机器人", - "authed": true, - "canEdit": true, - "authedStatusDesc": "已开通", - "requiredApproval": false, - "allowedActions": ["view", "detail", "remove"], - "apiCount": 4, - "apiPreview": { - "name": "人与人会话中机器人发送普通消息" - }, - "canApplyDirectly": false, - "canRemove": true - } - ], - "summary": { - "totalMatched": 1, - "returnedCount": 1, - "authedCount": 1, - "unauthedCount": 0, - "requiredApprovalCount": 0 - }, - "truncated": false, - "nextAction": "none" -} -``` - -`requiredApproval=true` must be shown and must still be appliable: - -```json -{ - "scopeValue": "Contact.User.mobile", - "scopeName": "个人手机号信息", - "firstLevelType": "SNS", - "authed": false, - "canEdit": true, - "requiredApproval": true, - "allowedActions": ["view", "detail", "apply"], - "canApplyDirectly": true, - "displayMessage": "该权限可申请;申请后加入当前应用版本变更,发布时进入审核流程。" -} -``` - -Agent selection order: - -1. Exact `scopeValue`. -2. Matched `apiList[].name`. -3. Scope name or description. -4. Category title and `firstLevelType`. - -Agent must pass final `scopeValue` to add/remove. Do not pass API name, API uuid, or category title into write tools. - -### 8.3 Add - -```bash -dws devapp permission add --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --dry-run --format json -dws devapp permission add --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --yes --format json -``` - -MCP payload: - -```json -{ - "unifiedAppId": "UNIFIED_APP_ID", - "scopeValues": ["qyapi_robot_sendmsg"] -} -``` - -Rules: - -1. Already authorized scopes are skipped or reported, not duplicated. -2. Missing scopes are rejected. -3. `canEdit=false` scopes are rejected. -4. `requiredApproval=true` scopes are accepted and staged into current app version changes. -5. Approver selection is not part of permission add; it belongs to version publish. - -Expected response: - -```json -{ - "applied": true, - "appliedScopeValues": ["Contact.User.mobile"], - "directAppliedScopeValues": [], - "versionStagedScopeValues": ["Contact.User.mobile"], - "rejectedScopeValues": [], - "skippedScopeValues": [], - "message": "权限申请成功;权限变更已加入应用版本,请在版本发布时继续审核流程。" -} -``` - -### 8.4 Remove - -```bash -dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --dry-run --format json -dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --yes --format json -``` - -MCP payload: - -```json -{ - "unifiedAppId": "UNIFIED_APP_ID", - "scopeValue": "qyapi_robot_sendmsg" -} -``` - -Rules: - -1. Remove only one scope per call. -2. `authed=false` returns `NOT_AUTHED`. -3. `canEdit=false` returns a no-edit reason. -4. Removing an authorized permission is not the same as withdrawing a pending approval. - -## 9. Event And Version Boundary - -Event and version commands are needed for a full application lifecycle but should not be folded into permission add. - -Target event tools: - -| CLI | MCP tool | Notes | -| --- | --- | --- | -| `devapp event list` | `list_open_dev_app_events` | Query current callback and subscribed event types. | -| `devapp event config` | `configure_open_dev_app_event` | Configure callbackUrl, token, aesKey, eventTypes. | - -Implemented version tools (verified against the `op-app` MCP `tools/list`): - -| CLI | MCP tool | Notes | -| --- | --- | --- | -| `devapp version create` | `create_open_dev_app_version` | Save a new version from current config (`version`, `description`). | -| `devapp version list` | `list_open_dev_app_versions` | Paged list (`currentPage`, `pageSize`). | -| `devapp version get` | `get_open_dev_app_version_detail` | One version detail by `versionId`. | -| `devapp version check-approval` | `publish_open_dev_app_version` (`dryRun=true`) | Precheck approval requirement / approvers; does not publish. | -| `devapp version publish` | `publish_open_dev_app_version` (`dryRun=false`) | Publish; `confirmedSensitive` for high-sensitivity scopes, optional `approverUserId`. | -| `devapp version status` | `get_open_dev_app_version_status` | Poll publish and approval state. | - -Note: the MCP `dryRun` field is the server-side approval precheck and maps to -`version check-approval`. It is distinct from the CLI global `--dry-run` (preview -without execution). `check-approval` is read-only (no write guard); `publish` -uses the standard `--dry-run`/`--yes` write guard. - -Implemented robot tools: - -| CLI | MCP tool | Notes | -| --- | --- | --- | -| `devapp robot create` | `create_dingtalk_robot` | Synchronously create a new agent app + robot. | -| `devapp robot submit` | `submit_robot_create_task` | Async submit; supports retry via original `taskId`. | -| `devapp robot result` | `query_robot_create_result` | Poll async task by `taskId`. | -| `devapp robot get` | `get_open_dev_app_robot_config` | Read robot config of an existing app. | -| `devapp robot config` | `create_open_dev_app_robot_config` | Create robot config on an existing app. | -| `devapp robot update` | `update_open_dev_app_robot_config` | Update robot config. | -| `devapp robot enable` | `enable_open_dev_app_robot` | Enable / re-enable robot capability. | -| `devapp robot offline` | `offline_open_dev_app_robot` | Offline robot capability. | - -Robot create/submit and config/update/enable/offline are write operations and -use the `--dry-run`/`--yes` guard; `get` and `result` are reads. - -Stitching rule: - -```text -permission add requiredApproval=true - -> versionStagedScopeValues returned - -> devapp version check-approval - -> choose approver if required - -> devapp version publish - -> devapp version status -``` - -Approval details may only be visible in the DingTalk app. CLI should return audit id, approval url if backend provides one, and a clear status. - -## 10. Agent Routing - -### 10.1 Application Intent Disambiguation - -`应用` is an overloaded word in DingTalk. A yulan-style Agent must not route every -generic app request to `devapp`. Use `devapp` only when the request is about -DingTalk Open Platform / developer backend enterprise internal applications. - -Positive `devapp` signals: - -| Signal | Route | -| --- | --- | -| `开放平台应用`, `开发者后台应用`, `企业内部应用`, `内部应用` | `devapp` | -| `agentId`, `clientId`, `appKey`, `customKey`, `appSecret`, `clientSecret` | `devapp` | -| `应用权限`, `权限点`, `API 权限`, `APP/SNS 权限`, `scopeValue` | `devapp permission ...` | -| `事件订阅`, `callbackUrl`, `token`, `aesKey` in an app context | `devapp event ...` | -| `应用版本`, `版本发布`, `选审批人`, `发布审核` in developer backend context | `devapp version ...` | -| yulan's current `开放平台应用 CLI 化 / dws app` workstream | `devapp` | - -Non-`devapp` signals: - -| Request shape | Correct route | -| --- | --- | -| `开放平台接口文档`, `API 怎么调用`, `错误码`, `字段说明` | `devdoc` | -| `钉钉文档`, `云文档`, `知识库里的文档`, document read/write/export | `doc` or `wiki` preflight | -| `工作台应用`, `应用 app001`, `钉钉工作台上有哪些应用` | `workbench app ...` | -| `OA 审批单`, `同意/拒绝/撤销审批`, approval task detail | `oa` | -| `MCP 服务`, `connector`, `创建工具`, `HSF 映射`, `上架 MCP` | OpenDev MCP platform workflow, not `dws devapp` | -| `AI 应用`, `智能体`, `机器人对话能力` without developer backend identifiers | Ask or route to the relevant product; do not default to `devapp` | - -If the user says only `应用` and gives no developer-backend identifiers, inspect -the surrounding context. If there is still no context, ask one short clarifying -question: whether they mean an Open Platform enterprise internal app, a workbench -app, a document/app capability, or an MCP connector/tool. - -When the user is already in this yulan workstream, prefer `devapp` for ambiguous -phrases such as `应用查询`, `创建应用`, `应用权限`, or `应用版本` because the active -domain is OpenDev enterprise internal applications. This preference is not a -global DWS rule; it is scoped to this developer-platform application CLI design. - -Agent should use this state machine: - -```text -user intent - -> normalize command - -> collect slots - -> resolve unique app - -> for permission intents, resolve scopeValue - -> dry-run plan for writes - -> require user confirmation or --yes - -> call MCP tool - -> normalize ServiceResult - -> report next action -``` - -Intent table: - -| User intent | Command | Tool | -| --- | --- | --- | -| "查应用/搜索应用" | `devapp list` | `list_open_dev_apps_by_condition` | -| "看应用详情/clientId/agentId" | `devapp get` | `get_open_dev_app_detail` | -| "创建应用" | `devapp create` | `create_inner_app` | -| "修改名称/描述/图标" | `devapp update` | `update_inner_app` | -| "删除应用" | `devapp delete` | `delete_inner_app` | -| "查权限/搜索 API 权限" | `permission list` | `list_open_dev_app_permissions` | -| "这个权限覆盖哪些 API" | `permission list --scope` | `list_open_dev_app_permissions` | -| "申请权限/开通权限" | `permission add` | `apply_open_dev_app_permissions` | -| "取消权限/移除权限" | `permission remove` | `remove_open_dev_app_permission` | -| "拿 appSecret/clientSecret" | `credentials get` | `get_open_dev_app_credentials` | -| "配置事件订阅" | `event config` | Pending | -| "发布/审核/选审批人" | `version ...` | Pending | - -Yulan invocation examples: - -| User wording | Expected first command | Reason | -| --- | --- | --- | -| "查一下开发者后台应用 DemoApp" | `dws devapp list --name DemoApp --format json` | OpenDev context plus app name means listByCondition search. | -| "把应用图标换成 ICON_RESOURCE" | `dws devapp update --unified-app-id ... --icon ICON_RESOURCE --dry-run --format json` | Icon change is app base info update and must be guarded. | -| "已确认删除这个开放平台应用" | `dws devapp delete --unified-app-id ... --yes --format json` | Destructive write only after unique app resolution and confirmation. | -| "手机号权限要申请,虽然需要审核" | `dws devapp permission add --permissions Contact.User.mobile --dry-run/--yes` | `requiredApproval=true` is still appliable; approval moves to version publish. | -| "配置事件订阅回调" | `dws devapp event config ... --dry-run --format json` | Event subscription is app configuration, not permission or docs search. | -| "保存版本并看审核状态" | `version create`, `check-approval`, `publish`, `status` | Version flow owns approver selection and publish audit state. | -| "创建 MCP 服务并配置 HSF tool" | OpenDev MCP platform workflow, not `dws devapp create` | MCP connector/tool is a platform artifact, not an enterprise internal app. | - -ServiceResult handling: - -| Result | Agent behavior | -| --- | --- | -| `success=true` with `result` | Show structured result and next action. | -| `success=false` | Preserve `errorCode/errorMsg`; do not rewrite as a generic failure. | -| `versionStagedScopeValues` non-empty | Tell user to continue version approval/publish. | -| multiple app candidates | Stop and ask for a unique id. | -| permission list `truncated=true` | Ask for a narrower keyword or status filter. | - -## 11. Agent Invocation Runbook - -This section is the operational rulebook for yulan-style Agents. It is meant to -avoid the common failure mode where an Agent knows the right business concept but -calls the wrong DWS product, stale MCP tool, or over-broad permission list. - -### 11.1 Runtime Preflight - -Before running a devapp command in a new environment, the Agent should verify the -hardcoded helper surface. Endpoint configuration is an internal edition concern; -the Agent should not require users to paste or export MCP gateway keys. - -```bash -dws devapp --help -dws app --help -``` - -If the command or endpoint is missing: - -1. If `dws devapp --help` fails, report `DEVAPP_COMMAND_UNAVAILABLE`. -2. If execution reports `endpoint_not_resolved`, report - `DEVAPP_ENDPOINT_NOT_CONFIGURED` and ask the owner to check the internal - edition `SupplementServers/StaticServers` injection. Local developers may - override with `DINGTALK_DEVAPP_MCP_URL`, but Agents should not request it - from end users. -3. Do not reroute the task to `devdoc`, `doc`, or generic `mcp` commands unless - the user is asking for documentation rather than application management. - -If a command flag in this document conflicts with `--help`, use `--help`. The -document defines the target contract; the checked-in helper code defines the -current callable command surface. - -### 11.2 Command Construction - -Use this order when constructing a call: - -1. Identify the product as `devapp` only when the request matches section 10.1 - positive signals or the active yulan OpenDev app workstream. Do not use - `devapp` for every bare `应用`. -2. Resolve application identity before any detail or write operation. -3. Prefer checked-in CLI flags over raw `--json`. -4. Use raw `--json` only when a required MCP field has no CLI flag yet. -5. Always add `--format json` for Agent execution. -6. For writes, call `--dry-run --format json` first, then call the same command - with `--yes --format json` only after user confirmation or an explicit - higher-level instruction. - -Do not add confirmation booleans to MCP payloads. These belong to the CLI layer: - -| Bad MCP field | Correct DWS guard | -| --- | --- | -| `confirmCreate=true` | `dws devapp create ... --yes` | -| `confirmUpdate=true` | `dws devapp update ... --yes` | -| `confirmDelete=true` | `dws devapp delete ... --yes` | -| `confirmPermission=true` | `dws devapp permission add/remove ... --yes` | -| `confirmSecret=true` | Do not send; `credentials get` has no confirm/show-secret payload field. | - -### 11.3 Business Recipes - -| User request | Agent recipe | -| --- | --- | -| "查 DemoApp 应用" | `devapp list --name DemoApp`; if one result, optionally `devapp get`; if multiple, show candidates. | -| "这个 agentId 是哪个应用" | `devapp get --agent-id AGENT_ID`; if backend requires list semantics, use `devapp list --agent-id AGENT_ID`. | -| "创建企业内部应用" | `devapp create --name ... --type internal --dry-run`; after confirmation, repeat with `--yes`. | -| "修改应用描述/图标" | Resolve `unifiedAppId`; `devapp update --unified-app-id ... --desc/icon ... --dry-run`; after confirmation, `--yes`. | -| "删除应用" | Resolve unique app; show app summary; `devapp delete ... --dry-run`; after confirmation, `--yes`. | -| "查询机器人发消息权限" | `devapp permission list --keyword "机器人发消息"`; if candidates are broad, require `scopeValue`. | -| "这个 API 需要哪个权限" | Search by API name; inspect `apiPreview`; if uncertain, use `permission list --scope SCOPE` for detail. | -| "申请通讯录手机号权限" | Resolve app; `permission list --keyword "手机号"`; choose `scopeValue`; `permission add --permissions SCOPE --dry-run`; then `--yes`. | -| "权限已进版本,继续发布" | `version check-approval`; choose approver if needed; `version publish --yes`; poll `version status`. | -| "拿 clientSecret/appSecret" | Use only `credentials get`; never fall back to `devapp get`. | - -### 11.4 Permission Candidate Selection - -Permission list is intentionally one grouped list, not separate -`listAuthed/listAvailable` calls. The Agent must reduce the result set before -writing: - -1. If the user gives a `scopeValue`, use exact match. -2. If the user gives an API name, search with `keyword`, then match - `apiList[].name` or `apiPreview.name`. -3. If the user gives a human permission name, match `scopeName/scopeDesc`. -4. If multiple scopes remain, show `scopeValue`, `scopeName`, `firstLevelType`, - `authed`, `requiredApproval`, and `apiPreview`, then ask for one. -5. For list output, cap the displayed list with `limit`; for detail output, call - with `scopeValue` and return the full API coverage for one scope only. - -`requiredApproval=true` is not a blocker for permission add. It means the add -operation stages the permission into the current application version and the -approval decision happens during version publish. - -### 11.5 Normalized Errors - -DWS may return CLI-layer errors (`category/reason/hint/actions`) or MCP/HSF -business errors (`ServiceResult.success=false`, `errorCode`, `errorMsg`). The -Agent should normalize behavior without hiding raw backend fields. - -| Normalized reason | Typical source | Agent behavior | -| --- | --- | --- | -| `DEVAPP_COMMAND_UNAVAILABLE` | `unknown command` for `dws devapp` | Report that the local CLI build does not contain the devapp helper. Do not try cache refresh as the primary fix. | -| `DEVAPP_ENDPOINT_NOT_CONFIGURED` | `endpoint_not_resolved`, missing internal edition endpoint | Ask the owner to check `SupplementServers/StaticServers`; local debug may use `DINGTALK_DEVAPP_MCP_URL`, but do not ask users for gateway keys. | -| `DEVAPP_TOOL_NOT_PUBLISHED` | Product exists but one tool is missing | Report the missing MCP tool key and avoid substituting a different operation. | -| `AUTH_CONTEXT_MISSING` | Backend cannot resolve `corpId/userId` | Tell the user to re-login or check organization context; do not ask for internal `orgId/uid`. | -| `APP_NOT_FOUND` | Empty list/detail result | Ask for another app identifier; do not create or update implicitly. | -| `APP_AMBIGUOUS` | Multiple apps match name/key | Show candidate ids and stop before write. | -| `PERMISSION_NOT_FOUND` | Scope search has no exact candidate | Ask for another keyword or API name. | -| `PERMISSION_ALREADY_AUTHED` | Add receives an already authorized scope | Report skipped scope and continue only for remaining unauth scopes. | -| `PERMISSION_NOT_AUTHED` | Remove receives an unauthorized scope | Report that there is nothing to remove. | -| `PERMISSION_NOT_EDITABLE` | `canEdit=false` or backend no-edit error | Show the no-edit reason; do not retry as a write. | -| `VERSION_APPROVAL_REQUIRED` | `check-approval` says approver is required | Ask for approver or use returned candidate when the user gave an explicit approver. | -Raw `errorCode/errorMsg` must remain in JSON output or the Agent response so the -backend owner can diagnose actual HSF failures. - -### 11.6 Output Shape For Agent Consumption - -For every command, prefer a stable shape that has a small summary and raw backend -details: - -```json -{ - "ok": true, - "operation": "permission.add", - "app": { - "unifiedAppId": "UNIFIED_APP_ID", - "agentId": 123456, - "appName": "DemoApp" - }, - "summary": { - "appliedCount": 1, - "skippedCount": 0, - "rejectedCount": 0, - "requiresVersionPublish": true - }, - "result": {}, - "raw": { - "success": true, - "errorCode": "", - "errorMsg": "" - }, - "nextAction": "run devapp version check-approval" -} -``` - -For failures: - -```json -{ - "ok": false, - "operation": "app.get", - "reason": "APP_AMBIGUOUS", - "message": "Multiple applications match the provided name.", - "candidates": [], - "raw": { - "success": false, - "errorCode": "BACKEND_ERROR_CODE", - "errorMsg": "backend message" - } -} -``` - -The `raw` object is allowed to include backend fields, but it must not include -tokens, cookies, MCP gateway keys, full app secrets, or unrelated page request -headers. - -## 12. Open-Source DWS Implementation Notes - -The open-source branch uses a hardcoded helper path for `devapp`; do not depend -on service discovery for command visibility. - -1. Add `internal/helpers/devapp.go`, following `internal/helpers/devdoc.go`. -2. Register the root with `RegisterPublic`. -3. Route helper invocations with `executor.NewHelperInvocation`. -4. Keep command aliases such as `app` and `permission search/detail` in the - helper layer, not as separate MCP tools. -5. Add tests for dry-run payloads, `--yes` guard, app resolution, and permission routing. - -Internal edition endpoint injection: - -```go -SupplementServers: func() []edition.ServerInfo { - return []edition.ServerInfo{ - { - ID: "devapp", - Name: "钉钉开放平台应用管理", - Endpoint: loadDevappMCPEndpointFromInternalConfig(), - Prefixes: []string{"devapp", "app"}, - }, - } -} -``` - -The endpoint loader must read from internal config or secret storage. Do not put -the Streamable HTTP URL or `?key=` value in this repository. `StaticServers` is -also valid for an internal build that wants to skip Market discovery entirely. - -Recommended helper root: - -```text -Name(): "devapp" -Aliases: ["app"] if supported by cobra and no conflict -``` - -Do not manually edit generated command indexes unless the generator is part of the change. The runtime command surface should be checked with: - -```bash -dws devapp --help -dws app --help -``` - -## 13. Rollout And Verification - -Use this checklist when moving from backend HSF implementation to usable DWS -commands. The goal is to prove that the same operation works at the MCP layer, -the DWS helper layer, and the Agent recipe layer. - -### 13.1 MCP Tool Configuration Checklist - -For each P0 tool: - -1. Configure on the pre-release MCP developer platform before online release. -2. Use the deterministic tool key from section 3.2. -3. Select HSF and fill interface, version, and method exactly. -4. Generate API input/output from the HSF method. -5. Keep a single business request object. Remove or avoid separate - `BaseOpenRequestVO` public mapping. -6. Configure tool input JSON Schema with only Agent-facing fields. -7. Map `corpId/userId` from system context to the business request object. -8. Review every recommended mapping manually, especially fields named - `agentId`, `appId`, `appKey`, or `userId`. -9. Keep `ServiceResult` output wrapper and success rule - `ServiceResult.success == true`. -10. Remove blank error-text optimization rows; preserve raw - `errorCode/errorMsg`. -11. Save draft and verify the page shows a new draft version. -12. Debug with a baseline call, then one filter/write scenario. -13. Publish only after debug passes. - -P0 debug inputs: - -| Tool | Baseline debug input | -| --- | --- | -| `list_open_dev_apps_by_condition` | `{"currentPage":1,"pageSize":10}` | -| `get_open_dev_app_detail` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | -| `create_inner_app` | `{"appName":"DemoApp","appDesc":"internal app"}` | -| `update_inner_app` | `{"unifiedAppId":"UNIFIED_APP_ID","appDesc":"new desc"}` | -| `delete_inner_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | -| `list_open_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","authStatus":"ALL","limit":20}` | -| `apply_open_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValues":["SCOPE_VALUE"]}` | -| `remove_open_dev_app_permission` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValue":"SCOPE_VALUE"}` | - -For destructive debug cases, use a disposable application and permission scope. -Do not use production data in long-lived examples. - -### 13.2 MCP Direct Smoke - -After publish, verify the service through MCP before using DWS: - -1. Run MCP `initialize`. -2. Run `tools/list` and confirm all expected P0 tool keys exist. -3. Call `list_open_dev_apps_by_condition` with only pagination. -4. Call one exact locator scenario, such as `get_open_dev_app_detail`. -5. Call permission list with `authStatus=ALL` and confirm APP and SNS scopes are - both represented. -6. Confirm failures preserve `errorCode/errorMsg`. - -Do not store the Streamable HTTP URL or `?key=` value in repository files, -README, skills, or issue text. - -### 13.3 DWS Helper Smoke - -After the MCP service is published and the internal edition injects the endpoint -through `SupplementServers` or `StaticServers`, verify the hardcoded helper path: - -```bash -dws devapp --help -dws app --help -dws devapp list --page 1 --page-size 10 --format json -``` - -Expected: - -1. Product id is `devapp`. -2. Command alias `app` exists, but `devapp` is canonical. -3. P0 command leaves appear in help. -4. `--format json` is available through global output flags. -5. Sensitive writes are marked or guarded so Agent uses `--dry-run` then `--yes`. - -If `devapp` is missing, treat it as `DEVAPP_COMMAND_UNAVAILABLE`. If command -execution reports `endpoint_not_resolved`, check the internal edition endpoint -injection. Local debug may use `DINGTALK_DEVAPP_MCP_URL` as a temporary override. -If the endpoint is configured but a backend tool is unavailable, treat it as -`DEVAPP_TOOL_NOT_PUBLISHED`. - -### 13.4 Agent Smoke - -Use these smoke cases to prove the yulan invocation problem is solved for an -Agent. They intentionally cover routing, narrowing, write guard, and backend -error propagation. - -| Scenario | Smoke command | Pass criteria | -| --- | --- | --- | -| Discover product | `dws devapp --help` | Helper command exists and exposes devapp CLI metadata. | -| List baseline | `dws devapp list --page 1 --page-size 10 --format json` | JSON contains page summary and app array. | -| Search by name | `dws devapp list --name DemoApp --format json` | One result opens path to get; multiple results return candidates. | -| Detail by id | `dws devapp get --unified-app-id UNIFIED_APP_ID --format json` | Response includes ids but no full secret. | -| Create guard | `dws devapp create --name DemoApp --type internal --format json` | Refuses without `--yes` or shows dry-run requirement. | -| Create dry-run | `dws devapp create --name DemoApp --type internal --dry-run --format json` | Prints payload and does not call HSF. | -| Permission search | `dws devapp permission list --unified-app-id UNIFIED_APP_ID --keyword "发送消息" --format json` | Returns candidate scopes and no write happens. | -| Permission detail | `dws devapp permission list --unified-app-id UNIFIED_APP_ID --scope SCOPE_VALUE --format json` | Returns full API coverage for one scope. | -| Permission approval | `dws devapp permission add --unified-app-id UNIFIED_APP_ID --permissions SCOPE_VALUE --dry-run --format json` | Shows direct/staged/rejected plan; `requiredApproval=true` is allowed. | -| Backend failure | Invalid app id or scope | JSON preserves `errorCode/errorMsg` and normalized reason. | - -### 13.5 Release Exit Criteria - -The application-side command set is ready for yulan Agent usage only when all of -the following are true: - -1. P0 MCP tools are published and visible through `tools/list`. -2. `dws devapp --help` shows P0 helper leaves and `dws app --help` resolves as - the compatibility alias. -3. App list/detail/create/update/delete smoke cases pass. -4. Permission list/add/remove smoke cases pass for APP and SNS scopes. -5. `requiredApproval=true` permission add stages into version changes instead of - being rejected by the Agent. -6. Credential access is handled only by `credentials get`; missing event/version - tools are reported as unsupported/pending, not silently emulated by other - commands. -7. No repository doc, skill, log, or fixture contains MCP gateway keys, cookies, - access tokens, full app secrets, or production app data. -8. Existing skill setup and generator tests pass. - -## 14. Acceptance Cases - -| Case | Expected behavior | -| --- | --- | -| `dws devapp list --name DemoApp --format json` | Calls listByCondition and returns pagination summary. | -| `dws devapp get --name DemoApp --format json` with multiple matches | Stops and returns candidates. | -| `dws devapp create --name T --type internal --dry-run --format json` | Prints payload only; no MCP call. | -| `dws devapp create --name T --type internal --format json` | Refuses because write confirmation is missing. | -| `dws devapp create --name T --type internal --yes --format json` | Calls `create_inner_app`. | -| `dws devapp update --unified-app-id X --desc Y --yes --format json` | Calls `update_inner_app`. | -| `dws devapp delete --unified-app-id X --format json` | Refuses because delete confirmation is missing. | -| `dws devapp permission list --unified-app-id X --format json` | Returns flattened APP and SNS permissions. | -| `dws devapp permission list --unified-app-id X --keyword "发送消息" --format json` | Returns candidate `permissions[]`; no automatic apply. | -| `dws devapp permission add --unified-app-id X --permissions Contact.User.mobile --yes --format json` | Applies or stages permission; `requiredApproval=true` is allowed. | -| `dws devapp permission remove --unified-app-id X --permission qyapi_robot_sendmsg --yes --format json` | Removes one authorized scope or returns a structured reason. | -| `dws devapp credentials get --unified-app-id X --format json` | Calls `get_open_dev_app_credentials`; does not use `devapp get` as a secret fallback. | -| `查应用 appXYZ 的详情` without OpenDev context | Does not blindly route to `devapp`; uses context such as `workbench app get` or asks. | -| `查询开放平台 API 错误码` | Routes to `devdoc`, not `devapp`. | -| `创建 MCP 服务并配置 HSF tool` | Uses OpenDev MCP platform workflow, not `dws devapp create`. | diff --git a/scripts/install-devapp.sh b/scripts/install-dev.sh similarity index 54% rename from scripts/install-devapp.sh rename to scripts/install-dev.sh index 39bd8f19..02edbd1e 100755 --- a/scripts/install-devapp.sh +++ b/scripts/install-dev.sh @@ -2,26 +2,26 @@ # Copyright 2026 Alibaba Group # Licensed under the Apache License, Version 2.0 # -# One-command installer for the DevApp preview branch. +# One-command installer for the Dev preview branch. # # Usage: -# curl -fsSL https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/feat/dws-devapp/scripts/install-devapp.sh | sh +# curl -fsSL https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/feat/dws-dev/scripts/install-dev.sh | sh # # Environment variables: -# DEVAPP_REPO_URL Git repository URL. Default: https://github.com/wxianfeng/dingtalk-workspace-cli.git -# DEVAPP_BRANCH Branch to install. Default: feat/dws-devapp -# DEVAPP_SOURCE_DIR Existing source checkout to install from. -# DEVAPP_KEEP_SOURCE Set to 1 to keep the temporary source checkout. +# DWS_DEV_REPO_URL Git repository URL. Default: https://github.com/wxianfeng/dingtalk-workspace-cli.git +# DWS_DEV_BRANCH Branch to install. Default: feat/dws-dev +# DWS_DEV_SOURCE_DIR Existing source checkout to install from. +# DWS_DEV_KEEP_SOURCE Set to 1 to keep the temporary source checkout. # # The script clones the requested branch and delegates to scripts/install.sh # from that checkout, which builds and installs dws from local source. set -eu -REPO_URL="${DEVAPP_REPO_URL:-https://github.com/wxianfeng/dingtalk-workspace-cli.git}" -BRANCH="${DEVAPP_BRANCH:-feat/dws-devapp}" -SOURCE_DIR="${DEVAPP_SOURCE_DIR:-}" -KEEP_SOURCE="${DEVAPP_KEEP_SOURCE:-0}" +REPO_URL="${DWS_DEV_REPO_URL:-https://github.com/wxianfeng/dingtalk-workspace-cli.git}" +BRANCH="${DWS_DEV_BRANCH:-feat/dws-dev}" +SOURCE_DIR="${DWS_DEV_SOURCE_DIR:-}" +KEEP_SOURCE="${DWS_DEV_KEEP_SOURCE:-0}" TMPDIR_WORK="" say() { @@ -47,7 +47,7 @@ trap cleanup EXIT INT TERM print_banner() { printf '\n' - say "DevApp preview installer" + say "Dev preview installer" say "Repository: ${REPO_URL}" say "Branch: ${BRANCH}" printf '\n' @@ -55,8 +55,8 @@ print_banner() { resolve_source_dir() { if [ -n "$SOURCE_DIR" ]; then - [ -d "$SOURCE_DIR" ] || err "DEVAPP_SOURCE_DIR does not exist: ${SOURCE_DIR}" - [ -f "$SOURCE_DIR/scripts/install.sh" ] || err "install.sh not found under DEVAPP_SOURCE_DIR: ${SOURCE_DIR}" + [ -d "$SOURCE_DIR" ] || err "DWS_DEV_SOURCE_DIR does not exist: ${SOURCE_DIR}" + [ -f "$SOURCE_DIR/scripts/install.sh" ] || err "install.sh not found under DWS_DEV_SOURCE_DIR: ${SOURCE_DIR}" printf '%s\n' "$SOURCE_DIR" return 0 fi @@ -66,7 +66,7 @@ resolve_source_dir() { TMPDIR_WORK="$(mktemp -d)" checkout="${TMPDIR_WORK}/dingtalk-workspace-cli" - say "Cloning DevApp preview source..." >&2 + say "Cloning Dev preview source..." >&2 git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$checkout" [ -f "$checkout/scripts/install.sh" ] || err "install.sh not found after clone" @@ -84,15 +84,18 @@ main() { source_root="$(resolve_source_dir)" say "" - say "Installing dws from DevApp preview source..." - DWS_VERSION=latest DWS_SKILL_MODE="${DWS_SKILL_MODE:-mono}" sh "$source_root/scripts/install.sh" + say "Installing dws from Dev preview source..." + # the dev skill (dingtalk-dev) ships only in multi mode (developer-facing); mono targets + # general office users and intentionally has no dev routing. + DWS_VERSION=latest DWS_SKILL_MODE="${DWS_SKILL_MODE:-multi}" sh "$source_root/scripts/install.sh" say "" - say "DevApp next steps:" + say "Dev next steps:" say " dws version" say " dws auth login" - say " dws devapp --help --format json" - say " dws devapp list --format json" + say " dws skill setup --mode multi # install per-product skills incl. dingtalk-dev" + say " dws dev --help --format json" + say " dws dev app list --format json" if [ "$KEEP_SOURCE" = "1" ]; then say "" diff --git a/skills/mono/SKILL.md b/skills/mono/SKILL.md index b46c04be..cff58a5c 100644 --- a/skills/mono/SKILL.md +++ b/skills/mono/SKILL.md @@ -1,6 +1,6 @@ --- name: dws -description: 管理钉钉产品能力(AI表格/AI搜问/日历/通讯录/群聊与机器人/待办/审批/考勤/日志/DING消息/开放平台文档/开放平台应用/钉钉文档/钉钉云盘/AI听记/邮箱/在线电子表格/知识库等)。当用户需要操作表格数据、管理日程会议、模糊找人/查谁负责某事项、查询通讯录、管理群聊、机器人发消息、创建待办、提交审批、查看考勤、提交日报周报(钉钉日志模版)、读写钉钉文档、上传下载云盘文件、查询听记纪要、收发邮件、读写在线电子表格(axls)、管理钉钉知识库、管理开放平台企业内部应用、应用成员、安全配置或应用权限时使用。 +description: 管理钉钉产品能力(AI表格/AI搜问/日历/通讯录/群聊与机器人/待办/审批/考勤/日志/DING消息/开放平台文档/钉钉文档/钉钉云盘/AI听记/邮箱/在线电子表格/知识库等)。当用户需要操作表格数据、管理日程会议、模糊找人/查谁负责某事项、查询通讯录、管理群聊、机器人发消息、创建待办、提交审批、查看考勤、提交日报周报(钉钉日志模版)、读写钉钉文档、上传下载云盘文件、查询听记纪要、收发邮件、读写在线电子表格(axls)、管理钉钉知识库时使用。 cli_version: ">=1.0.15" --- @@ -49,7 +49,6 @@ cli_version: ">=1.0.15" | `chat` | 群聊与机器人:搜索群/建群/群成员管理/改群名/消息发送(文本/Markdown/图片/文件)/拉取消息/@我/特别关注/机器人群发/单聊/撤回/转发/引用回复/Webhook/机器人搜索 | [chat.md](./references/products/chat.md) | | `contact` | 通讯录:用户查询(当前用户/搜索/详情/手机号)/花名册档案(学历/家庭/银行卡/合同)/离职员工查询(姓名/时间范围/部门)/部门查询(搜索/详情/子部门/成员)/角色查询(主管/管理员/财务/HR 等 label)/特别关注列表 | [contact.md](./references/products/contact.md) | | `devdoc` | 开放平台文档:搜索开发文档 | [devdoc.md](./references/products/devdoc.md) | -| `devapp` | 开放平台应用:企业内部应用查询/详情/创建/修改/删除、成员管理、安全配置、应用权限 APP/SNS 列表与申请取消、凭证读取、机器人创建与配置、版本创建/发布/审批 | [devapp.md](./references/products/devapp.md) | | `ding` | DING消息:发送/撤回(应用内/短信/电话) | [ding.md](./references/products/ding.md) | | `doc` | 钉钉文档:搜索/浏览/读写/块级编辑/评论/文件创建/复制/移动/重命名/**删除/导出 docx/权限管理/媒体上传下载**;创建/编辑先按渐进式 doc 子文档与 JSONML 工作流决策 | [doc.md](./references/products/doc.md) | | `drive` | 钉钉云盘:文件列表/元数据/文件夹/上传(两步)/下载 | [drive.md](./references/products/drive.md) | @@ -83,13 +82,10 @@ cli_version: ">=1.0.15" ### 消歧规则 -- "应用" 是泛词,不默认等于开放平台应用。只有出现"开放平台应用/开发者后台应用/企业内部应用", - 或伴随 `agentId/clientId/appKey/appSecret/customKey/应用成员/安全配置/IP 白名单/登录重定向/应用权限/权限点/创建机器人/智能体机器人/机器人配置/事件订阅/应用版本/发布审核` - 等开发者后台标识时,才路由到 `devapp`。 -- "工作台应用/app001/appXYZ/钉钉工作台上有哪些应用" → `workbench app`,不要走 `devapp`。 -- "开放平台接口文档/API 怎么调用/错误码/字段说明" → `devdoc`,不要走 `devapp`。 -- "MCP 服务/connector/创建工具/HSF 映射/上架 MCP" → MCP 平台配置流程,不是 `dws devapp create`。 -- 只说"应用"且缺少上下文时,先结合当前对话判断;仍不明确时追问是开放平台企业内部应用、工作台应用、文档能力还是 MCP 工具。 +- "工作台应用/app001/appXYZ/钉钉工作台上有哪些应用" → `workbench app`。 +- "开放平台接口文档/API 怎么调用/错误码/字段说明" → `devdoc`。 +- "MCP 服务/connector/创建工具/HSF 映射/上架 MCP" → MCP 平台配置流程。 +- 只说"应用"且缺少上下文时,先结合当前对话判断;仍不明确时追问是工作台应用、文档能力还是 MCP 工具。 - "知识库空间/成员管理" → `wiki` 产品参考;"知识库里的文档/文件/内容" → #4 文档知识,再切 `doc`。 - "创建表格/在线电子表格/单元格" → `sheet`;"AI 表格/多维表/base/记录/字段/视图" → #6 AI 表格数据。 - "写文档/读文档/总结文档/插入图片附件/块级编辑/JSONML 保真改写" → #4 文档知识,不要停在 `doc create` 或 `doc block --help`;创建/编辑必须继续按 [doc.md](./references/products/doc.md) 加载渐进式子文档和 JSONML workflow。 @@ -104,7 +100,6 @@ cli_version: ">=1.0.15" 用户提到"群聊/建群/群成员/群管理/发消息/发图片消息/发文件消息/发 Markdown 消息/截图发钉钉/转发消息/引用回复/@我/特别关注消息/机器人发消息/Webhook/机器人群发/机器人单聊/通知" → `chat` 用户提到"通讯录/同事/部门/组织架构/子部门/部门多少人/离职员工/离职名单/离职花名册/花名册/员工档案/学历/家庭/银行卡/紧急联系人/合同/角色/主管角色/管理员角色/财务/HR/特别关注/星标联系人" → `contact` 用户提到"开发/API/调用错误 文档" → `devdoc` -用户提到"开放平台应用/开发者后台应用/企业内部应用/应用 agentId/clientId/appKey/appSecret/应用成员/安全配置/IP 白名单/登录重定向/应用权限/权限点/创建机器人/智能体机器人/机器人配置/事件订阅/应用版本发布/审核" → `devapp` 用户提到"DING/紧急消息/电话提醒" → `ding` 用户提到"钉钉文档/云文档/读写文档/知识库里的文档/浏览知识库内容/知识库内搜索文档/块级编辑/文档评论/文档复制移动" → `doc` 用户提到"云盘/文件存储/文件上传下载/文件夹" → `drive` diff --git a/skills/mono/references/intent-guide.md b/skills/mono/references/intent-guide.md index bda871ab..17c4d9e2 100644 --- a/skills/mono/references/intent-guide.md +++ b/skills/mono/references/intent-guide.md @@ -7,13 +7,11 @@ | 用户说... | 真实意图 | 应该用 | 不要用 | 理由 | |-----------|----------|--------|--------|------| | "搜一下 OAuth2 接入文档" | 搜索开发文档 | `devdoc` | — | 搜索开放平台技术文档,不是钉钉内部内容 | -| "查询开放平台 API 错误码/字段说明" | 搜索开放平台接口文档 | `devdoc` | `devapp` | 查 API 用法和错误码,不是管理企业内部应用 | -| "查开放平台应用 DemoApp 的 appKey" | 查询开发者后台企业内部应用 | `devapp` | `devdoc` | 目标是应用实例和凭证/agentId,不是接口文档 | -| "给应用申请 qyapi_robot_sendmsg 权限" | 管理开放平台应用权限 | `devapp permission` | `devdoc` | 权限点申请属于应用配置变更 | -| "查工作台应用 appXYZ 的详情" | 查询钉钉工作台应用 | `workbench app` | `devapp` | `appXYZ/app001` 这类工作台 app id 不是开放平台 `unifiedAppId` | -| "帮我创建一个应用" | 应用类型不明确 | 先追问 | `devapp create` | 缺少开放平台/工作台/AI/MCP 等上下文,不能默认创建企业内部应用 | -| "做一个 AI 应用" | AI 应用/智能体需求不明确 | 先追问或转对应能力 | `devapp create` | 没有开发者后台企业内部应用标识,不能默认走 OpenDev app CRUD | -| "创建 MCP 服务并配置 HSF tool" | MCP 平台工具配置 | OpenDev MCP 平台流程 | `devapp create` | 这是 connector/tool 配置,不是创建开放平台企业内部应用 | +| "查询开放平台 API 错误码/字段说明" | 搜索开放平台接口文档 | `devdoc` | — | 查 API 用法和错误码 | +| "查工作台应用 appXYZ 的详情" | 查询钉钉工作台应用 | `workbench app` | — | `appXYZ/app001` 是工作台 app id | +| "帮我创建一个应用" | 应用类型不明确 | 先追问 | — | 缺少工作台/AI/MCP 等上下文,不能默认 | +| "做一个 AI 应用" | AI 应用/智能体需求不明确 | 先追问或转对应能力 | — | 需求不明确时不默认走任何创建命令 | +| "创建 MCP 服务并配置 HSF tool" | MCP 平台工具配置 | OpenDev MCP 平台流程 | — | 这是 connector/tool 配置 | | "帮我建一个项目跟踪表" | 创建数据表格 | `aitable` | `todo` | 涉及结构化数据/行列操作,不是个人待办 | | "帮我记一下明天要做的事" | 创建个人待办 | `todo` | `aitable` | 个人待办提醒,非数据表 | | "帮我建一个明天下午的日程" | 日历日程 | `calendar` | — | 日历日程管理(可含参与者/会议室)| @@ -47,15 +45,9 @@ --- -### 2. devapp vs devdoc vs workbench — 应用泛词消歧 +### 2. devdoc vs workbench — 应用泛词消歧 -`应用` 是泛词,不能直接等价为开放平台企业内部应用。 - -**用 `devapp` 的场景**: -- "开放平台应用/开发者后台应用/企业内部应用" — 管理开发者后台应用实例 -- "查 appKey/clientId/agentId/appSecret/clientSecret" — 应用身份和凭证 -- "应用权限/权限点/事件订阅/应用版本/发布审核" — 应用配置或发布链路 -- "创建机器人/智能体机器人/机器人配置/机器人回调" — 应用机器人能力(`devapp robot`) +`应用` 是泛词,不能直接等价为某个产品。 **用 `devdoc` 的场景**: - "开放平台 API 怎么调用" — 接口文档 @@ -63,14 +55,14 @@ **用 `workbench app` 的场景**: - "工作台应用/钉钉工作台上的应用" -- "应用 app001/appXYZ 的详情" 且没有开放平台上下文 +- "应用 app001/appXYZ 的详情" **先追问的场景**: -- "创建应用/做个应用/查应用" 但没有开放平台、工作台、文档、AI、MCP 等上下文 -- "AI 应用/智能体" 但没有说明是开发者后台企业内部应用还是其他产品能力 -- "MCP 服务/connector/HSF tool" 属于平台配置流程,不要转成 `devapp create` +- "创建应用/做个应用/查应用" 但没有工作台、文档、AI、MCP 等上下文 +- "AI 应用/智能体" 但没有说明是哪种产品能力 +- "MCP 服务/connector/HSF tool" 属于平台配置流程,不要转成任何创建命令 -**判断关键**:是否在管理一个开发者后台企业内部应用实例?是 → `devapp`。是否在查 API 文档?是 → `devdoc`。是否在查钉钉工作台入口应用?是 → `workbench app`。仍不明确 → 追问,不调用写命令。 +**判断关键**:是否在查 API 文档?是 → `devdoc`。是否在查钉钉工作台入口应用?是 → `workbench app`。仍不明确 → 追问,不调用写命令。 --- diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md deleted file mode 100644 index 726facd9..00000000 --- a/skills/mono/references/products/devapp.md +++ /dev/null @@ -1,364 +0,0 @@ -# 开放平台应用 (devapp) 命令参考 - -管理钉钉开放平台企业内部应用。覆盖应用 CRUD、生命周期启停、凭证读取、权限管理、网页应用配置、成员管理和安全配置。 - -> `dws devapp ...` 是内置 helper 命令,不依赖 MCP 服务发现。`dws app ...` 是兼容别名。执行前用 `dws devapp --help` 验证可用。 - -## 核心规则 - -1. 所有命令加 `--format json`。 -2. 写操作先 `--dry-run`,确认后才加 `--yes`。 -3. 应用名/appKey/customKey 命中多条时展示候选,不取第一条。 -4. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名。 -5. 主动读取密钥走 `credentials get`;任何 `devapp get`/详情返回里的 `clientSecret/appSecret` 都按敏感凭证脱敏,不向用户展开。 - -## 应用定位 - -| 优先级 | 标识 | 处理 | -|--------|------|------| -| 1 | `--unified-app-id` | 直接使用 | -| 2 | `--agent-id` / `--app-id` | 直接使用 | -| 3 | `--app-key` / `--custom-key` | 先查询,唯一命中才继续 | -| 4 | `--name` | 模糊搜索,写操作必须唯一命中 | - ---- - -## 一、应用基础操作 - -### 列表 - -```bash -dws devapp list --format json -dws devapp list --name DemoApp --format json -dws devapp list --agent-id 123456 --format json -``` - -MCP tool: `list_open_dev_apps_by_condition` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--page` | `currentPage` | 1-based,默认 1 | -| `--page-size` | `pageSize` | 默认 20 | -| `--name` / `--keyword` | `appName` | 应用名搜索 | -| `--agent-id` | `agentId` | 精确定位 | -| `--app-key` | `appKey` | appKey/clientId | -| `--creator` | `creator` | 创建人关键词 | -| `--sort` | `sortType` | 如 `gmt_modified` | -| `--order` | `sortOrder` | `asc` / `desc` | - -应用状态字段:列表/详情原始字段里如果出现 `status` / `appStatus`,`0=IN_ACTIVE` 已停用、`1=ACTIVE` 已激活、`2=WAIT_ACTIVE` 待激活、`3=EXPIRED` 已过期。执行 `inactive/active` 后必须回读 `get` 或 `list`:看到 `status=0` 才算停用完成,看到 `status=1` 才算启用完成。`versionStatus` 是版本状态,不要和应用启停状态混用。 - -### 详情 - -```bash -dws devapp get --unified-app-id UNIFIED_APP_ID --format json -``` - -MCP tool: `get_open_dev_app_detail` - -详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 - -### 创建 - -```bash -dws devapp create --name DemoApp --desc "内部应用" --type internal --dry-run --format json -dws devapp create --name DemoApp --desc "内部应用" --type internal --yes --format json -``` - -MCP tool: `create_inner_app`。`--type` 只做 CLI 校验,当前仅支持 `internal`。 - -### 修改 - -```bash -dws devapp update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-run --format json -``` - -MCP tool: `update_inner_app`。至少一个更新字段:`--name` / `--desc` / `--icon`。 - -### 停用 / 启用 - -```bash -dws devapp inactive --unified-app-id ID --dry-run --format json -dws devapp active --unified-app-id ID --dry-run --format json -``` - -MCP tools: `inactive_inner_app` / `active_inner_app` - -### 删除 - -```bash -dws devapp delete --unified-app-id ID --dry-run --format json -``` - -MCP tool: `delete_inner_app`。删除前必须展示应用摘要,异步生效。 - ---- - -## 二、凭证与网页应用 - -### 凭证读取 - -```bash -dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -``` - -MCP tool: `get_open_dev_app_credentials`。返回含 `clientSecret/appSecret`,按敏感凭证处理。不能用 `devapp get` 代替;如果 `devapp get` 偶尔返回密钥,也只用于内部判断并脱敏。 - -### 网页应用 - -```bash -dws devapp webapp get --agent-id AGENT_ID --format json -dws devapp webapp config --agent-id AGENT_ID --homepage-link https://example.com --dry-run --format json -``` - -MCP tools: `get_webapp_config` / `set_webapp_config` - -`h5PageType` 未显式传入时,不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 - ---- - -## 三、权限管理 - -### 权限列表 - -```bash -dws devapp permission list --unified-app-id ID --format json -dws devapp permission list --unified-app-id ID --keyword "机器人" --status UNAUTHED --format json -dws devapp permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json -``` - -MCP tool: `list_open_dev_app_permissions` - -| CLI | MCP | 说明 | -|-----|-----|------| -| `--keyword` | `keyword` | 关键词搜索 | -| `--status` | `authStatus` | `ALL/AUTHED/UNAUTHED` | -| `--scope-type` | `firstLevelType` | `APP/SNS`,空返回两者 | -| `--scope` | `scopeValue` | 单权限详情模式 | -| `--limit` | `limit` | 每页数量,默认 20,建议不超过 50 | -| `--offset` | `offset` | 翻页偏移量,默认 0 | - -权限状态判断: - -- `--status` 是查询过滤条件:`ALL` 不过滤,`AUTHED` 只看已授权/已开通,`UNAUTHED` 只看未授权/未开通。 -- 单个权限项的 `status` 是内部操作态:`0` 已获得,`1` 申请中,`2` 可以申请,`3` 不可以申请。 -- `status=0` 不要重复申请;如需取消,确认 `canRemove=true` 后走 `permission remove`。 -- `status=1` 不要重复申请;查看 `authedStatusDesc`,通常等待审批或版本发布。 -- `status=2` 可走 `permission add --dry-run`。 -- `status=3` 停止申请,展示 `applyDisabledReason/displayMessage`。 -- `authedStatusDesc` 细分展示:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED` 表示已开通、已申请或待发布;`NOT_OPEN`/`NOT_APPLIED` 表示未开通/未申请;`AUDIT_PROCESSING` 表示审批中;`AUDIT_REFUSE` 表示审批未通过。能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 - -翻页:`--limit 50 --offset 0`(第1页)、`--offset 50`(第2页)、`--offset 100`(第3页),返回条数 < limit 表示末尾。 - -### 申请权限 - -```bash -dws devapp permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json -``` - -MCP tool: `apply_open_dev_app_permissions`。`requiredApproval=true` 允许申请,写入版本变更。 - -### 取消权限 - -```bash -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --dry-run --format json -``` - -MCP tool: `remove_open_dev_app_permission`。一次只取消一个。 - ---- - -## 四、成员与安全 - -### 成员管理 - -```bash -dws devapp member list --app-id UNIFIED_APP_ID --format json -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json -``` - -MCP tools: `list_open_dev_app_members` / `add_open_dev_app_members` / `remove_open_dev_app_members` - -### 安全配置 - -```bash -dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --redirect-url https://callback.example.invalid/callback --dry-run --format json -``` - -MCP tool: `update_app_security_config` - ---- - -## 五、机器人 - -### 新建智能体机器人 - -```bash -# 同步创建(返回 agentId/robotCode/clientId/clientSecret) -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json - -# 异步创建 + 查询 -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot result --task-id TASK_ID --format json -``` - -MCP tools: `create_dingtalk_robot` / `submit_robot_create_task` / `query_robot_create_result`。`submit` 失败可带原 `--task-id` 重试。 - -异步创建任务状态: - -| status | 含义 | 下一步 | -|--------|------|--------| -| `WAITING` | 任务已提交,仍在创建中 | 按 `interval` 轮询 `robot result` | -| `SUCCESS` | 创建完成 | 保存 `robotCode/clientId/clientSecret`,凭据按敏感信息处理 | -| `APPROVAL_REQUIRED` | 创建编排返回需审批 | 不要重复建号;按返回信息或开发者后台审批后再继续 | -| `FAIL` | 创建失败 | 读取 `errorCode/errorMsg/failReason`;可带原 `taskId` 重新 `submit` | -| `EXPIRED` | `taskId` 不存在或超过有效期 | 重新 `submit`,必要时换新 `taskId` | - -### 现有应用配置机器人 - -```bash -dws devapp robot get --unified-app-id ID --format json -dws devapp robot config --unified-app-id ID --name 小助手 --brief 审批助手 --outgoing-url URL --mode 2 --skills qa,approval --dry-run --format json -dws devapp robot update --unified-app-id ID --brief "新简介" --dry-run --format json -dws devapp robot enable --unified-app-id ID --name 小助手 --dry-run --format json -dws devapp robot offline --unified-app-id ID --dry-run --format json -``` - -MCP tools: `get_open_dev_app_robot_config` / `create_open_dev_app_robot_config` / `update_open_dev_app_robot_config` / `enable_open_dev_app_robot` / `offline_open_dev_app_robot`。 - -配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。应用未配机器人时 `get` 返回 `robot info is not exist`。 - -状态判断: - -- `robot get` 返回 `success=true` 且包含 `robotCode` 时,说明机器人配置已落库,不是异步等待态。 -- `status=1`:OFFLINE,机器人配置存在但处于停用/下线状态。 -- `status=2`:ONLINE,机器人配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 -- ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-url`,或用 `robot connect` 接到本地 Agent。 -- 首次 `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为“待生效”;只有 `status=1` 或需要重新上架时才调用 `enable`。 -- 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 - -### 建联(把机器人接到本地 agent) - -```bash -# 用现成机器人凭证起 Stream,接到当前渠道的本地 agent(前台运行,Ctrl-C 退出) -dws devapp robot connect --channel auto --robot-client-id CLIENT_ID --robot-client-secret CLIENT_SECRET - -# 用统一应用 ID,复用 credentials get 自动取凭证(省得手填) -dws devapp robot connect --unified-app-id UNIFIED_APP_ID --channel qoderwork - -# Codex 渠道推荐显式覆盖 agent 命令;否则默认临时目录可能触发 -# "Not inside a trusted directory and --skip-git-repo-check was not specified" -CODEX_BIN="$(command -v codex || echo /Applications/Codex.app/Contents/Resources/codex)" -DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check" \ - dws devapp robot connect --unified-app-id UNIFIED_APP_ID --channel codex --format json - -# 预览建联方案不实际起连接 -dws devapp robot connect --robot-client-id CLIENT_ID --robot-client-secret CLIENT_SECRET --dry-run --format json -``` - -- **不建号**:connect 只建联。缺凭证先用 `robot create/submit` 建号拿 clientId/clientSecret。 -- **凭证用 `--robot-client-id/--robot-client-secret`**(不叫 `--client-id`,那是全局 OAuth 客户端覆盖 flag,会撞名)。 -- **两种来源**:① 直接 `--robot-client-id/--robot-client-secret`;② `--unified-app-id`(内部调 `get_open_dev_app_credentials` 自动取)。⚠️ 来源②**字段名待预发真机验证**,且 clientSecret 一般仅建号时返回一次、该接口未必能返回,取不到时回退手填。 -- **渠道 `--channel`**:`auto`(默认,按运行时信号自动识别当前宿主) | `openclaw` | `qoder` | `qoderwork` | `hermes` | `workbuddy` | `claudecode` | `codebuddy` | `codex` | `gemini` | `opencode`。 -- **会话记忆 `--agent-memory`**(默认开):同一群/单聊共享 agent 会话,追问保留上下文。仅 claudecode/codebuddy/workbuddy 支持(CLI 有 `--session-id/--resume`);其余渠道自动无状态。`--agent-memory=false` 关闭。 -- **模型覆盖 `--agent-model`**:claudecode 默认锁 haiku 求快,可改 sonnet/opus 换聪明(env `DWS_AGENT_MODEL`)。 -- **运行目录 `--agent-workdir`**:放知识文件给机器人企业上下文;默认空白临时目录求快(env `DWS_AGENT_WORKDIR`)。 -- **富回复 `--reply-card`**(默认开):Thinking/Done 表态永远生效;**卡片需配 `--card-template` 才启用**(同 hermes 语义:没配模板=纯文字回复+表态),失败自动回退文字(env `DWS_REPLY_CARD=0` 全关)。 -- **卡片模板 `--card-template`**:模板按应用授权,建议在开发者后台为自己应用注册 AI 卡片模板并填其 ID(env `DWS_CARD_TEMPLATE`);默认公共模板 best-effort。 -- **依赖预检(agent 必做)**:建联前先 `--dry-run` 看输出的 `cli` 字段——`installed:false` 且 `autoInstall:true`(npm 系)告知用户会自动安装;`autoInstall:false`(桌面 App/官方渠道)**先引导用户按 `installHint` 安装**,不要直接起连接。 -- **Codex 渠道**:真实建联优先用 `DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check"`,避免 Codex CLI 在默认临时目录内拒绝执行。需要固定上下文时,把可信目录写进覆盖命令:`DWS_AGENT_CMD="$CODEX_BIN exec --skip-git-repo-check -C /path/to/repo"`(路径不要包含空格)。设置 `DWS_AGENT_CMD` 后,DWS 不再自动拼接 `--agent-model/--agent-workdir/会话参数`。 -- **stream-bridge 渠道**(qoder/qoderwork/claudecode/workbuddy/codex/gemini/opencode):Go 原生进程内 Stream 转发器,订阅 `TOPIC_ROBOT`,每条 @机器人消息起一个无头 CLI 实例(如 `claude -p`)→ stdout 回钉钉,可 7×24 无人值守。 -- **官方渠道**(openclaw/hermes):dws 不代建机器人,输出官方 onboarding 指引(连接器自带建号 + AI 卡片回复)。 -- 覆盖项:`DWS_AGENT_CMD`(指定 agent 可执行命令) / `DWS_CONNECT_CMD`(指定外部连接器) / `DWS_CONNECT_NO_INSTALL=1`(关闭缺包自动安装) / `DWS_AGENT_TIMEOUT_MS`。 - ---- - -## 六、版本发布 - -```bash -dws devapp version create --unified-app-id ID --version 1.0.1 --desc "新增机器人能力" --dry-run --format json -dws devapp version list --unified-app-id ID --page 1 --page-size 20 --format json -dws devapp version get --unified-app-id ID --version-id VERSION_ID --format json -dws devapp version check-approval --unified-app-id ID --version-id VERSION_ID --format json -dws devapp version publish --unified-app-id ID --version-id VERSION_ID --confirm-sensitive --dry-run --format json -dws devapp version status --unified-app-id ID --version-id VERSION_ID --format json -``` - -MCP tools: `create_open_dev_app_version` / `list_open_dev_app_versions` / `get_open_dev_app_version_detail` / `publish_open_dev_app_version` / `get_open_dev_app_version_status`。 - -- `check-approval` = `publish_open_dev_app_version` 的 `dryRun=true` 预检模式,不实际发布。 -- `publish` 设 `dryRun=false`;含高敏权限需 `--confirm-sensitive`,灰度选人模式用 `--approver USER_ID`。 -- 流程:`permission add`(requiredApproval 写入版本变更)→ `version create` → `check-approval` → `publish` → `status`。 -- 新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 -- **审批人选择**:`check-approval` 返回候选审批人列表(userId+姓名)→ **展示给用户让其选择(不要自行挑选或默认第一个)** → `publish --approver <选中的userId>` 自动向该审批人发起审批 → `version status` 跟踪。机器人等能力需审批通过、应用发布后才可被搜索/加群/路由消息。 - -发布响应 `result`: - -| result | 含义 | 下一步 | -|--------|------|--------| -| `NOT_REQUIRED` | 不需要审批;预检时表示可直接发布,真实发布时表示已直接发布上线 | 真实发布后回读 `version status/get` 验证 `RELEASE` | -| `APPROVAL_REQUIRED` | 需要审批,通常出现在 `check-approval` 或未指定审批人时 | 查看 `approvalMode/approvalCandidates`,让用户选择审批人后再 `publish --approver` | -| `SUBMITTED` | 已提交审批 | 保存 `processId`,轮询 `version status` | - -审批模式 `approvalMode`:`SELECT_APPROVER` 表示灰度选人模式,需要展示候选审批人并让用户选择;`ENTERPRISE_SELF_BUILT` 表示企业自建审核模式,不传 `--approver`,按企业自建审核流程等待。 - -版本状态字段:`version create/list/get` 返回 `status`,`version status` 返回 `versionStatus`,二者都对齐版本枚举。 - -| status/versionStatus | 含义 | 下一步 | -|----------------------|------|--------| -| `INIT` | 版本已创建或有待发布变更,尚未发布 | 可 `check-approval` / `publish` | -| `AUDIT` | 发布审核中 | 不要重复发布;即使没有返回 `processStatus`,也按审核中处理 | -| `RELEASE` | 已发布生效 | 发布完成,可继续验证权限、机器人、网页应用等能力 | -| `GRAY` | 灰度状态 | 按灰度流程处理;不要当全量已发布 | - -审批流程状态字段:`version status` 的 `processStatus` 只在存在审批流程且后端透出流程态时有值。`versionStatus=AUDIT` 但没有 `processStatus/processInstanceId` 时,不要判失败,仍表示审核中。 - -| processStatus | 含义 | 下一步 | -|---------------|------|--------| -| `UNDER_REVIEW` | 审批中 | 等待审批,必要时把 `processInstanceId` 给用户去钉钉客户端查看 | -| `PASS` | 审批通过 | 继续回读版本,确认是否进入 `RELEASE` | -| `FAIL` | 审批拒绝 | 展示 `processComment`,修改后重新创建/发布版本 | -| `WITHDRAW` / `CANCEL` | 审批撤回或取消 | 回到发布前状态,重新 `check-approval` / `publish` | -| `PUBLISH_FAILED` | 审批后发布失败 | 展示错误信息,重新检查版本状态和后端错误 | - -遇到未列出的状态值时,不要猜测语义;原样展示状态,并回读 `version get/status` 或查开放平台文档/后台详情。 - ---- - -## 七、操作流程 - -### 创建应用全流程 - -```text -create --dry-run → 确认 → create --yes → get 确认 → credentials get → webapp config → permission add → member add -``` - -### 权限管理全流程 - -```text -permission list → permission list --keyword → permission list --scope → permission add --dry-run → 确认 → --yes → permission list 验证 -``` - -### 生命周期 - -```text -停用: get → inactive --dry-run → --yes → get 验证 -启用: active --dry-run → --yes → get 验证 -删除: get 展示 → delete --dry-run → 确认 → --yes → list 验证消失 -``` - ---- - -## 错误处理 - -| 情况 | 处理 | -|------|------| -| `unknown command` | CLI 构建不含 devapp helper | -| `endpoint_not_resolved` | 检查 edition endpoint 注入 | -| 多应用命中 | 展示候选,停止写操作 | -| `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | - -## 待实现能力 - -- `dws devapp event list/config` — 事件订阅(待后端发布) From f7409554239fd7d0de1332a780437899f5a9a3e6 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 16 Jun 2026 17:12:05 +0800 Subject: [PATCH 21/47] =?UTF-8?q?chore(dev):=20skill=20=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=9F=BA=E5=BB=BA=E9=80=82=E9=85=8D=20dev=20=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit run_skill_tests / skill_e2e / skill_static / skill_tests 更新到 dev 技能。 Co-Authored-By: Claude Opus 4.8 (1M context) --- test/run_skill_tests.py | 2 +- test/skill_e2e/skill_e2e_test.go | 4 +- test/skill_static/skill_static_test.go | 4 +- test/skill_tests.md | 134 ++++++++++++------------- 4 files changed, 72 insertions(+), 72 deletions(-) diff --git a/test/run_skill_tests.py b/test/run_skill_tests.py index 57b99629..6b77872a 100644 --- a/test/run_skill_tests.py +++ b/test/run_skill_tests.py @@ -323,7 +323,7 @@ def get_reference_file(product: str) -> str: 'calendar': 'calendar.md', 'chat': 'chat.md', 'contact': 'contact.md', - 'devapp': 'devapp.md', + 'dev': 'skills/multi/dingtalk-dev/SKILL.md', 'ding': 'ding.md', 'doc': 'doc.md', 'drive': 'drive.md', diff --git a/test/skill_e2e/skill_e2e_test.go b/test/skill_e2e/skill_e2e_test.go index 5406d674..9300bbbb 100644 --- a/test/skill_e2e/skill_e2e_test.go +++ b/test/skill_e2e/skill_e2e_test.go @@ -172,8 +172,8 @@ func inAntiRange(root, file string, ln int) bool { } func shouldSkip(cmd string) bool { - if strings.HasPrefix(cmd, "dws devapp ") || cmd == "dws devapp" { - // devapp is a target MCP-discovered command tree. Until the MCP product + if strings.HasPrefix(cmd, "dws dev ") || cmd == "dws dev" { + // dev (product id: devapp) is a target MCP-discovered command tree. Until the MCP product // is published into the open-source registry, validate its Agent routing // through test/skill_tests.md rather than mock cobra dispatch. return true diff --git a/test/skill_static/skill_static_test.go b/test/skill_static/skill_static_test.go index 2d2dd035..9423a7b3 100644 --- a/test/skill_static/skill_static_test.go +++ b/test/skill_static/skill_static_test.go @@ -134,8 +134,8 @@ func readFile(p string) (string, error) { var anglePlaceholderRe = regexp.MustCompile(`<[^>]*>`) func shouldSkip(cmd string) bool { - if strings.HasPrefix(cmd, "dws devapp ") || cmd == "dws devapp" { - // devapp is a target MCP-discovered command tree. Until the MCP product + if strings.HasPrefix(cmd, "dws dev ") || cmd == "dws dev" { + // dev (product id: devapp) is a target MCP-discovered command tree. Until the MCP product // is published into the open-source registry, validate its Agent routing // through test/skill_tests.md rather than static cobra dispatch. return true diff --git a/test/skill_tests.md b/test/skill_tests.md index fe57fb9b..47e514ac 100644 --- a/test/skill_tests.md +++ b/test/skill_tests.md @@ -45,7 +45,7 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 | `chat` | `references/products/chat.md` | 13 | 31 | | `contact` | `references/products/contact.md` | 7 | 14 | | `devdoc` | `references/products/simple.md` | 1 | 7 | -| `devapp` | `references/products/devapp.md` | 18 | 25 | +| `dev` | `skills/multi/dingtalk-dev/SKILL.md` | 19 | 25 | | `ding` | `references/products/ding.md` | 2 | 5 | | `report` | `references/products/report.md` | 6 | 26 | | `routing` | `references/intent-guide.md` | 1 | 3 | @@ -834,161 +834,161 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 --- -### devapp(25 条) +### dev(25 条) -#### `dws devapp list` +#### `dws dev app list` -**devapp_devapp_list_001** +**dev_app_list_001** - Prompt: 搜索开放平台应用,应用名是 DemoApp -- Expected: `dws devapp list --name DemoApp --format json` +- Expected: `dws dev app list --name DemoApp --format json` - Flags: `--name` = `DemoApp` -**devapp_devapp_list_002** +**dev_app_list_002** - Prompt: 分页查询开放平台应用,第 2 页,每页 10 条,按修改时间倒序 -- Expected: `dws devapp list --page 2 --page-size 10 --sort gmt_modified --order desc --format json` +- Expected: `dws dev app list --page 2 --page-size 10 --sort gmt_modified --order desc --format json` - Flags: `--page` = `2`, `--page-size` = `10`, `--sort` = `gmt_modified`, `--order` = `desc` -**devapp_devapp_list_003** +**dev_app_list_003** - Prompt: 在开发者后台搜索企业内部应用 DemoApp -- Expected: `dws devapp list --name DemoApp --format json` +- Expected: `dws dev app list --name DemoApp --format json` - Flags: `--name` = `DemoApp` -#### `dws devapp get` +#### `dws dev app get` -**devapp_devapp_get_001** +**dev_app_get_001** - Prompt: 查看统一应用 UNIFIED_APP_ID 的应用详情 -- Expected: `dws devapp get --unified-app-id UNIFIED_APP_ID --format json` +- Expected: `dws dev app get --unified-app-id UNIFIED_APP_ID --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID` -**devapp_devapp_get_002** +**dev_app_get_002** - Prompt: agentId 为 123456 的开放平台应用是哪个 -- Expected: `dws devapp get --agent-id 123456 --format json` +- Expected: `dws dev app get --agent-id 123456 --format json` - Flags: `--agent-id` = `123456` -**devapp_devapp_get_003** +**dev_app_get_003** - Prompt: 查开放平台应用 DemoApp 的 appKey、clientId 和 agentId -- Expected: `dws devapp get --name DemoApp --format json` +- Expected: `dws dev app get --name DemoApp --format json` - Flags: `--name` = `DemoApp` -#### `dws devapp create` +#### `dws dev app create` -**devapp_devapp_create_001** +**dev_app_create_001** - Prompt: 创建一个企业内部应用,名称 DemoApp,描述是内部应用 -- Expected: `dws devapp create --name DemoApp --desc "内部应用" --type internal --dry-run --format json` +- Expected: `dws dev app create --name DemoApp --desc "内部应用" --type internal --dry-run --format json` - Flags: `--name` = `DemoApp`, `--desc` = `内部应用`, `--type` = `internal` -**devapp_devapp_create_002** +**dev_app_create_002** - Prompt: 已确认创建企业内部应用 DemoApp -- Expected: `dws devapp create --name DemoApp --type internal --yes --format json` +- Expected: `dws dev app create --name DemoApp --type internal --yes --format json` - Flags: `--name` = `DemoApp`, `--type` = `internal` -#### `dws devapp update` +#### `dws dev app update` -**devapp_devapp_update_001** +**dev_app_update_001** - Prompt: 把应用 UNIFIED_APP_ID 的描述改成 新描述 -- Expected: `dws devapp update --unified-app-id UNIFIED_APP_ID --desc "新描述" --dry-run --format json` +- Expected: `dws dev app update --unified-app-id UNIFIED_APP_ID --desc "新描述" --dry-run --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--desc` = `新描述` -#### `dws devapp delete` +#### `dws dev app delete` -**devapp_devapp_delete_001** +**dev_app_delete_001** - Prompt: 删除开放平台应用 UNIFIED_APP_ID,先给我预览 -- Expected: `dws devapp delete --unified-app-id UNIFIED_APP_ID --dry-run --format json` +- Expected: `dws dev app delete --unified-app-id UNIFIED_APP_ID --dry-run --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID` -#### `dws devapp credentials get` +#### `dws dev app credentials get` -**devapp_devapp_credentials_get_001** +**dev_app_credentials_get_001** - Prompt: 查询应用 UNIFIED_APP_ID 的开放平台凭证 -- Expected: `dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json` +- Expected: `dws dev app credentials get --unified-app-id UNIFIED_APP_ID --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID` -**devapp_devapp_credentials_get_002** +**dev_app_credentials_get_002** - Prompt: 查询 AgentId AGENT_ID 对应应用的开放平台凭证 -- Expected: `dws devapp credentials get --agent-id AGENT_ID --format json` +- Expected: `dws dev app credentials get --agent-id AGENT_ID --format json` - Flags: `--agent-id` = `AGENT_ID` -#### `dws devapp member list` +#### `dws dev app member list` -**devapp_devapp_member_list_001** +**dev_app_member_list_001** - Prompt: 查看开放平台应用 UNIFIED_APP_ID 的成员 -- Expected: `dws devapp member list --app-id UNIFIED_APP_ID --format json` +- Expected: `dws dev app member list --app-id UNIFIED_APP_ID --format json` - Flags: `--app-id` = `UNIFIED_APP_ID` -#### `dws devapp member add` +#### `dws dev app member add` -**devapp_devapp_member_add_001** +**dev_app_member_add_001** - Prompt: 给开放平台应用 UNIFIED_APP_ID 添加开发者成员 userId1,userId2,先预览 -- Expected: `dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json` +- Expected: `dws dev app member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json` - Flags: `--app-id` = `UNIFIED_APP_ID`, `--users` = `userId1,userId2`, `--member-type` = `DEVELOPER` -#### `dws devapp member remove` +#### `dws dev app member remove` -**devapp_devapp_member_remove_001** +**dev_app_member_remove_001** - Prompt: 已确认,从开放平台应用 UNIFIED_APP_ID 移除开发者成员 userId1 -- Expected: `dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json` +- Expected: `dws dev app member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json` - Flags: `--app-id` = `UNIFIED_APP_ID`, `--users` = `userId1`, `--member-type` = `DEVELOPER`, `--yes` -#### `dws devapp security config` +#### `dws dev app security config` -**devapp_devapp_security_config_001** +**dev_app_security_config_001** - Prompt: 给开放平台应用 UNIFIED_APP_ID 配置 IP 白名单 192.0.2.10,先预览 -- Expected: `dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` +- Expected: `dws dev app security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` - Flags: `--app-id` = `UNIFIED_APP_ID`, `--ip-whitelist` = `192.0.2.10` -#### `dws devapp permission list` +#### `dws dev app permission list` -**devapp_devapp_permission_list_001** +**dev_app_permission_list_001** - Prompt: 查询应用 UNIFIED_APP_ID 已开通和未开通的全部 APP、SNS 权限 -- Expected: `dws devapp permission list --unified-app-id UNIFIED_APP_ID --status all --format json` +- Expected: `dws dev app permission list --unified-app-id UNIFIED_APP_ID --status all --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--status` = `all` -**devapp_devapp_permission_list_002** +**dev_app_permission_list_002** - Prompt: 搜索应用 UNIFIED_APP_ID 里机器人发送消息相关的未开通权限 -- Expected: `dws devapp permission list --unified-app-id UNIFIED_APP_ID --keyword "机器人发送消息" --status unauthed --format json` +- Expected: `dws dev app permission list --unified-app-id UNIFIED_APP_ID --keyword "机器人发送消息" --status unauthed --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--keyword` = `机器人发送消息`, `--status` = `unauthed` -**devapp_devapp_permission_list_003** +**dev_app_permission_list_003** - Prompt: 看一下权限 qyapi_robot_sendmsg 覆盖哪些 API -- Expected: `dws devapp permission list --unified-app-id UNIFIED_APP_ID --scope qyapi_robot_sendmsg --format json` +- Expected: `dws dev app permission list --unified-app-id UNIFIED_APP_ID --scope qyapi_robot_sendmsg --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--scope` = `qyapi_robot_sendmsg` -**devapp_devapp_permission_list_004** +**dev_app_permission_list_004** - Prompt: 给开放平台应用 UNIFIED_APP_ID 搜索手机号权限,应用权限和个人权限都要展示 -- Expected: `dws devapp permission list --unified-app-id UNIFIED_APP_ID --keyword "手机号" --status all --format json` +- Expected: `dws dev app permission list --unified-app-id UNIFIED_APP_ID --keyword "手机号" --status all --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--keyword` = `手机号`, `--status` = `all` -#### `dws devapp permission add` +#### `dws dev app permission add` -**devapp_devapp_permission_add_001** +**dev_app_permission_add_001** - Prompt: 给应用 UNIFIED_APP_ID 申请 qyapi_robot_sendmsg 权限,先预览 -- Expected: `dws devapp permission add --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --dry-run --format json` +- Expected: `dws dev app permission add --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --dry-run --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--permissions` = `qyapi_robot_sendmsg` -**devapp_devapp_permission_add_002** +**dev_app_permission_add_002** - Prompt: 已确认,给应用 UNIFIED_APP_ID 申请 Contact.User.mobile 权限 -- Expected: `dws devapp permission add --unified-app-id UNIFIED_APP_ID --permissions Contact.User.mobile --yes --format json` +- Expected: `dws dev app permission add --unified-app-id UNIFIED_APP_ID --permissions Contact.User.mobile --yes --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--permissions` = `Contact.User.mobile` -#### `dws devapp permission remove` +#### `dws dev app permission remove` -**devapp_devapp_permission_remove_001** +**dev_app_permission_remove_001** - Prompt: 从应用 UNIFIED_APP_ID 移除 qyapi_robot_sendmsg 权限,先预览 -- Expected: `dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --dry-run --format json` +- Expected: `dws dev app permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --dry-run --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--permission` = `qyapi_robot_sendmsg` -#### `dws devapp event list` +#### `dws dev app event list` -**devapp_devapp_event_list_001** +**dev_app_event_list_001** - Prompt: 查询应用 UNIFIED_APP_ID 的事件订阅配置 -- Expected: `dws devapp event list --unified-app-id UNIFIED_APP_ID --format json` +- Expected: `dws dev app event list --unified-app-id UNIFIED_APP_ID --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID` -#### `dws devapp version publish` +#### `dws dev app version publish` -**devapp_devapp_version_publish_001** +**dev_app_version_publish_001** - Prompt: 已确认,发布应用 UNIFIED_APP_ID 的版本 VERSION_ID,审批人是 USERID -- Expected: `dws devapp version publish --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --approver USERID --yes --format json` +- Expected: `dws dev app version publish --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --approver USERID --yes --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--version-id` = `VERSION_ID`, `--approver` = `USERID` --- From 0197dbc81a79439b122e826cab0211404ea3d8f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Tue, 16 Jun 2026 19:18:27 +0800 Subject: [PATCH 22/47] fix: align devapp DTO field names --- docs/devapp-yulan-command-routing.md | 45 ++++++++-------- internal/helpers/devapp.go | 14 ++--- internal/helpers/devapp_test.go | 54 +++++++++++++------ skills/dingtalk-devapp/references/app-crud.md | 6 +-- .../dingtalk-devapp/references/app-crud.md | 6 +-- 5 files changed, 72 insertions(+), 53 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 040ea582..c7e95d6b 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -182,10 +182,9 @@ MCP output configuration should keep the wrapper fields and configure success as Naming convention: - Public CLI flags use user-facing names: `--name`, `--desc`, `--icon`, `--homepage-url`. -- MCP app create/update currently keep backend request field names: `appName`, `appDesc`, `appIcon`. +- MCP app create/update use the current backend request field names: `name`, `desc`, `icon`. - MCP app list/detail use the simplified locator/search fields: `unifiedAppId`, `appKey`, `name`. -- The CLI helper owns the translation, for example `--name` -> `appName` for create/update and - `--name` -> `name` for list/detail. +- The CLI helper keeps public flags stable, for example `--name` -> `name` for create/update/list/detail. ### 3.2 P0 Tool Contracts @@ -275,16 +274,16 @@ full `clientSecret/appSecret`: | CLI | `dws devapp create` | | HSF facade | `OpenInnerAppManageFacade.create` | | Request shape | One object with system context and app base fields. | -| Required public fields | `appName`; `--type` is a CLI-only guard and currently must be `internal`. | +| Required public fields | `name`; `--type` is a CLI-only guard and currently must be `internal`. | | Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | Input fields: | Field | Type | CLI flag | Required | Notes | | --- | --- | --- | --- | --- | -| `appName` | string | `--name` | yes | Enterprise internal app name. | -| `appDesc` | string | `--desc` | no | App description. | -| `appIcon` | string | `--icon` | no | Existing media/resource id if backend accepts it. | +| `name` | string | `--name` | yes | Enterprise internal app name. | +| `desc` | string | `--desc` | no | App description. | +| `icon` | string | `--icon` | no | Existing media/resource id if backend accepts it. | Response: @@ -319,9 +318,9 @@ Update fields: | Field | Type | CLI flag | Notes | | --- | --- | --- | --- | -| `appName` | string | `--name` | New app name. | -| `appDesc` | string | `--desc` | New app description. | -| `appIcon` | string | `--icon` | New icon resource id if backend accepts it. | +| `name` | string | `--name` | New app name. | +| `desc` | string | `--desc` | New app description. | +| `icon` | string | `--icon` | New icon resource id if backend accepts it. | | `international` | object/string | `--international` | Only expose when schema describes the shape. | | `supportHarmony` | boolean | `--support-harmony` | Harmony support switch if backend exposes it. | @@ -508,9 +507,9 @@ Tool override examples: "description": "创建开放平台企业内部应用", "isSensitive": true, "flags": { - "appName": {"alias": "name", "required": true}, - "appDesc": {"alias": "desc"}, - "appIcon": {"alias": "icon"} + "name": {"alias": "name", "required": true}, + "desc": {"alias": "desc"}, + "icon": {"alias": "icon"} } }, "update_dev_app": { @@ -520,9 +519,9 @@ Tool override examples: "flags": { "unifiedAppId": {"alias": "unified-app-id"}, "appKey": {"alias": "app-key"}, - "appName": {"alias": "name"}, - "appDesc": {"alias": "desc"}, - "appIcon": {"alias": "icon"} + "name": {"alias": "name"}, + "desc": {"alias": "desc"}, + "icon": {"alias": "icon"} } }, "delete_dev_app": { @@ -683,8 +682,8 @@ MCP payload: ```json { - "appName": "DemoApp", - "appDesc": "internal app" + "name": "DemoApp", + "desc": "internal app" } ``` @@ -699,9 +698,9 @@ At least one update field is required. Supported P0 fields: | CLI flag | MCP field | Notes | | --- | --- | --- | -| `--name` | `appName` | New app name. | -| `--desc` | `appDesc` | New description. | -| `--icon` | `appIcon` | Existing media id or backend-supported resource. | +| `--name` | `name` | New app name. | +| `--desc` | `desc` | New description. | +| `--icon` | `icon` | Existing media id or backend-supported resource. | | `--international` | `international` | JSON string if exposed. | | `--support-harmony` | `supportHarmony` | Boolean if exposed. | @@ -1356,8 +1355,8 @@ P0 debug inputs: | --- | --- | | `list_dev_app` | `{"pageSize":10}` | | `get_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | -| `create_dev_app` | `{"appName":"DemoApp","appDesc":"internal app"}` | -| `update_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID","appDesc":"new desc"}` | +| `create_dev_app` | `{"name":"DemoApp","desc":"internal app"}` | +| `update_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID","desc":"new desc"}` | | `delete_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | | `list_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","authStatus":"ALL","pageSize":20}` | | `apply_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValues":["SCOPE_VALUE"]}` | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index aa65f1ba..48b0e7f6 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -307,9 +307,9 @@ func newDevAppCreateCommand(runner executor.Runner) *cobra.Command { if name == "" { return apperrors.NewValidation("--name is required") } - params := map[string]any{"appName": name} - devAppPutString(params, "appDesc", devAppStringFlag(cmd, "desc")) - devAppPutString(params, "appIcon", devAppStringFlag(cmd, "icon")) + params := map[string]any{"name": name} + devAppPutString(params, "desc", devAppStringFlag(cmd, "desc")) + devAppPutString(params, "icon", devAppStringFlag(cmd, "icon")) return runDevAppTool(runner, cmd, devAppCreateTool, params) }, } @@ -338,15 +338,15 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { } updates := 0 if v := devAppStringFlag(cmd, "name"); v != "" { - params["appName"] = v + params["name"] = v updates++ } if v := devAppStringFlag(cmd, "desc"); v != "" { - params["appDesc"] = v + params["desc"] = v updates++ } if v := devAppStringFlag(cmd, "icon"); v != "" { - params["appIcon"] = v + params["icon"] = v updates++ } if updates == 0 { @@ -1302,7 +1302,7 @@ func devAppCredentialsLocatorParams(cmd *cobra.Command) map[string]any { params := map[string]any{} devAppPutString(params, "unifiedAppId", devAppStringFlag(cmd, "unified-app-id")) devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) - devAppPutString(params, "appName", devAppStringFlag(cmd, "name")) + devAppPutString(params, "name", devAppStringFlag(cmd, "name")) return params } diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 9fccd5df..1d0cfa71 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -569,7 +569,7 @@ func TestDevAppCreateUsesCurrentInnerToolAndWriteGuard(t *testing.T) { t.Fatalf("Tool = %q, want create_dev_app", got) } - want := map[string]any{"appName": "Demo", "appDesc": "internal app"} + want := map[string]any{"name": "Demo", "desc": "internal app"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -589,7 +589,7 @@ func TestDevAppUpdateUsesCurrentInnerTool(t *testing.T) { if got := runner.last.Tool; got != "update_dev_app" { t.Fatalf("Tool = %q, want update_dev_app", got) } - want := map[string]any{"unifiedAppId": "u-1", "appDesc": "new desc"} + want := map[string]any{"unifiedAppId": "u-1", "desc": "new desc"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -753,22 +753,42 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { } func TestDevAppCredentialsGetBuildsParams(t *testing.T) { - runner := &captureRunner{} - root := newDevAppTestRoot(runner) - var out bytes.Buffer - root.SetOut(&out) - root.SetErr(&out) - root.SetArgs([]string{"devapp", "credentials", "get", "--unified-app-id", "u-1"}) + cases := []struct { + name string + args []string + wantParams map[string]any + }{ + { + name: "by unified app id", + args: []string{"devapp", "credentials", "get", "--unified-app-id", "u-1"}, + wantParams: map[string]any{"unifiedAppId": "u-1"}, + }, + { + name: "by name", + args: []string{"devapp", "credentials", "get", "--name", "Demo"}, + wantParams: map[string]any{"name": "Demo"}, + }, + } - if err := root.Execute(); err != nil { - t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) - } - if got := runner.last.Tool; got != "get_dev_app_credentials" { - t.Fatalf("Tool = %q, want get_dev_app_credentials", got) - } - want := map[string]any{"unifiedAppId": "u-1"} - if !reflect.DeepEqual(runner.last.Params, want) { - t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs(tc.args) + + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + if got := runner.last.Tool; got != "get_dev_app_credentials" { + t.Fatalf("Tool = %q, want get_dev_app_credentials", got) + } + if !reflect.DeepEqual(runner.last.Params, tc.wantParams) { + t.Fatalf("Params = %#v, want %#v", runner.last.Params, tc.wantParams) + } + }) } } diff --git a/skills/dingtalk-devapp/references/app-crud.md b/skills/dingtalk-devapp/references/app-crud.md index dffc302b..7082fe51 100644 --- a/skills/dingtalk-devapp/references/app-crud.md +++ b/skills/dingtalk-devapp/references/app-crud.md @@ -66,9 +66,9 @@ MCP tool: `create_dev_app` | CLI | MCP | 必填 | |-----|-----|------| -| `--name` | `appName` | 是 | -| `--desc` | `appDesc` | 否 | -| `--icon` | `appIcon` | 否 | +| `--name` | `name` | 是 | +| `--desc` | `desc` | 否 | +| `--icon` | `icon` | 否 | `--type` 只做 CLI 校验(当前仅支持 `internal`),不下发 MCP。 diff --git a/skills/multi/dingtalk-devapp/references/app-crud.md b/skills/multi/dingtalk-devapp/references/app-crud.md index dffc302b..7082fe51 100644 --- a/skills/multi/dingtalk-devapp/references/app-crud.md +++ b/skills/multi/dingtalk-devapp/references/app-crud.md @@ -66,9 +66,9 @@ MCP tool: `create_dev_app` | CLI | MCP | 必填 | |-----|-----|------| -| `--name` | `appName` | 是 | -| `--desc` | `appDesc` | 否 | -| `--icon` | `appIcon` | 否 | +| `--name` | `name` | 是 | +| `--desc` | `desc` | 否 | +| `--icon` | `icon` | 否 | `--type` 只做 CLI 校验(当前仅支持 `internal`),不下发 MCP。 From 410949cdf30de3e961aafd3bbe099cec7bea478c Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Tue, 16 Jun 2026 21:02:38 +0800 Subject: [PATCH 23/47] fix cr problem --- docs/devapp-agent-install-guide.md | 4 +-- internal/helpers/devapp.go | 30 ++++++++++++++++++- internal/helpers/devapp_test.go | 16 ++++++++++ skills/dingtalk-devapp/references/robot.md | 6 ++-- skills/mono/references/products/devapp.md | 4 +-- .../multi/dingtalk-devapp/references/robot.md | 6 ++-- 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/docs/devapp-agent-install-guide.md b/docs/devapp-agent-install-guide.md index bac9f19a..d41a19fd 100644 --- a/docs/devapp-agent-install-guide.md +++ b/docs/devapp-agent-install-guide.md @@ -192,8 +192,8 @@ dws devapp permission remove --agent-id --scope-value Contact.User.mob ```bash dws devapp robot get --unified-app-id --format json dws devapp robot config --unified-app-id --name "机器人名称" --dry-run --format json -dws devapp robot enable --unified-app-id --name "机器人名称" --dry-run --format json -dws devapp robot offline --unified-app-id --dry-run --format json +dws devapp robot enable --unified-app-id --dry-run --format json +dws devapp robot disable --unified-app-id --dry-run --format json ``` ### 成员与安全 diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 48b0e7f6..31f4d597 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -181,7 +181,7 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { newDevAppRobotResultCommand(runner), newDevAppRobotConfigGetCommand(runner), newDevAppRobotConfigCommand(runner, "config", "创建或更新现有应用机器人配置", devAppRobotConfigSetTool), - newDevAppRobotConfigCommand(runner, "enable", "启用现有应用机器人能力", devAppRobotEnableTool), + newDevAppRobotEnableCommand(runner), newDevAppRobotDisableCommand(runner), newDevAppRobotConnectCommand(runner), ) @@ -805,6 +805,34 @@ func newDevAppRobotConfigCommand(runner executor.Runner, use, short, tool string return cmd } +func newDevAppRobotEnableCommand(runner executor.Runner) *cobra.Command { + cmd := &cobra.Command{ + Use: "enable", + Short: "启用现有应用机器人能力", + Example: " dws devapp robot enable --unified-app-id UNIFIED_APP_ID --dry-run --format json", + Args: cobra.NoArgs, + DisableAutoGenTag: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := devAppRequireWriteGuard(cmd, "robot enable"); err != nil { + return err + } + appID, err := requiredDevAppUnifiedID(cmd) + if err != nil { + return err + } + params, _, err := devAppRobotConfigParams(cmd, appID) + if err != nil { + return err + } + return runDevAppTool(runner, cmd, devAppRobotEnableTool, params) + }, + } + addDevAppUnifiedIDFlag(cmd) + registerDevAppRobotConfigFlags(cmd) + preferLegacyLeaf(cmd) + return cmd +} + func newDevAppRobotDisableCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "disable", diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 1d0cfa71..f6c369c3 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -186,6 +186,21 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { "isAddScope": true, }, }, + { + name: "enable without config", + args: []string{"robot", "enable", "--unified-app-id", "u-1", "--yes"}, + wantTool: "enable_dev_app_robot", + wantParams: map[string]any{"unifiedAppId": "u-1"}, + }, + { + name: "enable with config", + args: []string{"robot", "enable", "--unified-app-id", "u-1", "--name", "小助手", "--yes"}, + wantTool: "enable_dev_app_robot", + wantParams: map[string]any{ + "unifiedAppId": "u-1", + "name": "小助手", + }, + }, { name: "disable", args: []string{"robot", "disable", "--unified-app-id", "u-1", "--yes"}, @@ -469,6 +484,7 @@ func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { }{ {name: "robot create", args: []string{"devapp", "robot", "create", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批"}}, {name: "robot config", args: []string{"devapp", "robot", "config", "--unified-app-id", "u-1", "--name", "小助手"}}, + {name: "robot enable", args: []string{"devapp", "robot", "enable", "--unified-app-id", "u-1"}}, {name: "robot disable", args: []string{"devapp", "robot", "disable", "--unified-app-id", "u-1"}}, {name: "version create", args: []string{"devapp", "version", "create", "--unified-app-id", "u-1", "--version", "1.0.1"}}, {name: "version publish", args: []string{"devapp", "version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1"}}, diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md index 29d201ce..d4e0946a 100644 --- a/skills/dingtalk-devapp/references/robot.md +++ b/skills/dingtalk-devapp/references/robot.md @@ -76,7 +76,7 @@ MCP tool: `get_extension_robot_config`。返回机器人基础信息、回调地 ### 创建或更新 / 启用配置 -`config` 是 upsert:未配置机器人时创建,已存在时更新;`enable` 用于启用/重新启用。 +`config` 是 upsert:未配置机器人时创建,已存在时更新;`enable` 用于恢复启用已配置机器人,也可在需要时透传配置字段。 首次 `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为“待生效”;只有 `status=1` 或需要重新上架时才调用 `enable`。 @@ -85,7 +85,7 @@ dws devapp robot config --unified-app-id --name 小助手 --brief --description "处理审批相关问答" --outgoing-url https://example.com/msg \ --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json -dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json +dws devapp robot enable --unified-app-id --dry-run --format json ``` MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` @@ -106,7 +106,7 @@ MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` | `--i18n-brief` | `i18nBrief` | 简介国际化 JSON | | `--i18n-description` | `i18nDescription` | 描述国际化 JSON | -至少提供一个配置字段,否则 CLI 报错。 +`config` 至少提供一个配置字段,否则 CLI 报错。`enable` 只恢复已有机器人时可仅提供 `--unified-app-id`;未配置机器人时先走 `config`。 ### 停用 diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 94868e9e..87a630e0 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -215,13 +215,13 @@ MCP tools: `create_dingtalk_robot` / `submit_robot_create_task` / `query_robot_c ```bash dws devapp robot get --unified-app-id ID --format json dws devapp robot config --unified-app-id ID --name 小助手 --brief 审批助手 --outgoing-url URL --mode 2 --skills qa,approval --dry-run --format json -dws devapp robot enable --unified-app-id ID --name 小助手 --dry-run --format json +dws devapp robot enable --unified-app-id ID --dry-run --format json dws devapp robot disable --unified-app-id ID --dry-run --format json ``` MCP tools: `get_extension_robot_config` / `set_extension_robot_config` / `enable_dev_app_robot` / `disable_dev_app_robot`。 -配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。应用未配机器人时 `get` 返回 `robot info is not exist`。 +配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。`config` 至少提供一个配置字段;`enable` 恢复已有机器人时可仅提供 `--unified-app-id`。应用未配机器人时 `get` 返回 `robot info is not exist`。 状态判断: diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md index 29d201ce..d4e0946a 100644 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ b/skills/multi/dingtalk-devapp/references/robot.md @@ -76,7 +76,7 @@ MCP tool: `get_extension_robot_config`。返回机器人基础信息、回调地 ### 创建或更新 / 启用配置 -`config` 是 upsert:未配置机器人时创建,已存在时更新;`enable` 用于启用/重新启用。 +`config` 是 upsert:未配置机器人时创建,已存在时更新;`enable` 用于恢复启用已配置机器人,也可在需要时透传配置字段。 首次 `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为“待生效”;只有 `status=1` 或需要重新上架时才调用 `enable`。 @@ -85,7 +85,7 @@ dws devapp robot config --unified-app-id --name 小助手 --brief --description "处理审批相关问答" --outgoing-url https://example.com/msg \ --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json -dws devapp robot enable --unified-app-id --name 小助手 --dry-run --format json +dws devapp robot enable --unified-app-id --dry-run --format json ``` MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` @@ -106,7 +106,7 @@ MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` | `--i18n-brief` | `i18nBrief` | 简介国际化 JSON | | `--i18n-description` | `i18nDescription` | 描述国际化 JSON | -至少提供一个配置字段,否则 CLI 报错。 +`config` 至少提供一个配置字段,否则 CLI 报错。`enable` 只恢复已有机器人时可仅提供 `--unified-app-id`;未配置机器人时先走 `config`。 ### 停用 From 844059a77b4e1ef696b9591d8bab091bdeb03b5a Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Wed, 17 Jun 2026 00:43:19 +0800 Subject: [PATCH 24/47] fix cr problem --- internal/helpers/devapp.go | 72 ++++++++++++++++----------------- internal/helpers/devapp_test.go | 59 +++++++++++---------------- 2 files changed, 60 insertions(+), 71 deletions(-) diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 31f4d597..560a60e4 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -682,7 +682,7 @@ func newDevAppRobotCreateCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "新建钉钉智能体机器人(一次性同步创建应用+机器人)", - Example: " dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", + Example: " dws devapp robot create --name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -705,7 +705,7 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "submit", Short: "异步提交钉钉智能体机器人创建任务(支持失败重试)", - Example: " dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", + Example: " dws devapp robot submit --name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -716,14 +716,6 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - // submit_robot_create_task 的 schema 把图标字段标为必填(空值时服务端用默认图标), - // 因此即使用户未提供也补空串占位。 - if _, ok := params["robotMediaId"]; !ok { - params["robotMediaId"] = "" - } - if _, ok := params["previewMediaId"]; !ok { - params["previewMediaId"] = "" - } devAppPutString(params, "taskId", devAppStringFlag(cmd, "task-id")) return runDevAppTool(runner, cmd, devAppRobotSubmitTool, params) }, @@ -794,7 +786,7 @@ func newDevAppRobotConfigCommand(runner executor.Runner, use, short, tool string return err } if updates == 0 { - return apperrors.NewValidation("at least one robot config field is required, e.g. --name, --brief, --description, --icon, --outgoing-url, --event-url, --mode, --skills") + return apperrors.NewValidation("at least one robot config field is required, e.g. --name, --brief, --desc, --icon, --outgoing-url, --event-url, --mode, --skills") } return runDevAppTool(runner, cmd, tool, params) }, @@ -820,15 +812,10 @@ func newDevAppRobotEnableCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - params, _, err := devAppRobotConfigParams(cmd, appID) - if err != nil { - return err - } - return runDevAppTool(runner, cmd, devAppRobotEnableTool, params) + return runDevAppTool(runner, cmd, devAppRobotEnableTool, map[string]any{"unifiedAppId": appID}) }, } addDevAppUnifiedIDFlag(cmd) - registerDevAppRobotConfigFlags(cmd) preferLegacyLeaf(cmd) return cmd } @@ -857,7 +844,7 @@ func newDevAppRobotDisableCommand(runner executor.Runner) *cobra.Command { } func registerDevAppRobotCreateFlags(cmd *cobra.Command) { - cmd.Flags().String("app-name", "", "智能体应用名称,长度 2-20,企业内唯一 (必填)") + cmd.Flags().String("name", "", "智能体应用名称,长度 2-20,企业内唯一 (必填)") cmd.Flags().String("robot-name", "", "承载机器人名称,用于客户端展示 (必填)") cmd.Flags().String("desc", "", "机器人功能描述,不超过 200 字 (必填)") cmd.Flags().String("icon", "", "机器人图标 mediaId;为空时使用默认图标") @@ -865,9 +852,9 @@ func registerDevAppRobotCreateFlags(cmd *cobra.Command) { } func devAppRobotCreateParams(cmd *cobra.Command) (map[string]any, error) { - appName := devAppStringFlag(cmd, "app-name") - if appName == "" { - return nil, apperrors.NewValidation("--app-name is required") + name := devAppStringFlag(cmd, "name") + if name == "" { + return nil, apperrors.NewValidation("--name is required") } robotName := devAppStringFlag(cmd, "robot-name") if robotName == "" { @@ -878,11 +865,11 @@ func devAppRobotCreateParams(cmd *cobra.Command) (map[string]any, error) { return nil, apperrors.NewValidation("--desc is required") } params := map[string]any{ - "appName": appName, + "name": name, "robotName": robotName, "desc": desc, } - devAppPutString(params, "robotMediaId", devAppStringFlag(cmd, "icon")) + devAppPutString(params, "iconMediaId", devAppStringFlag(cmd, "icon")) devAppPutString(params, "previewMediaId", devAppStringFlag(cmd, "preview")) return params, nil } @@ -890,10 +877,10 @@ func devAppRobotCreateParams(cmd *cobra.Command) (map[string]any, error) { func registerDevAppRobotConfigFlags(cmd *cobra.Command) { cmd.Flags().String("name", "", "机器人名称") cmd.Flags().String("brief", "", "机器人简介") - cmd.Flags().String("description", "", "机器人描述") + cmd.Flags().String("desc", "", "机器人描述") cmd.Flags().String("icon", "", "机器人图标 mediaId") cmd.Flags().String("outgoing-url", "", "消息回调地址 (outgoingUrl)") - cmd.Flags().String("event-url", "", "事件回调地址 (chatBotEventUrl)") + cmd.Flags().String("event-url", "", "事件回调地址 (eventCallbackUrl)") cmd.Flags().Int("mode", 0, "机器人模式枚举") cmd.Flags().StringSlice("skills", nil, "技能列表,多个用逗号分隔") cmd.Flags().Bool("add-scope", false, "是否自动添加机器人相关权限") @@ -914,17 +901,17 @@ func devAppRobotConfigParams(cmd *cobra.Command, appID string) (map[string]any, } setString("name", "name") setString("brief", "brief") - setString("description", "description") + setString("desc", "desc") setString("iconMediaId", "icon") setString("outgoingUrl", "outgoing-url") - setString("chatBotEventUrl", "event-url") + setString("eventCallbackUrl", "event-url") if cmd.Flags().Changed("mode") { params["mode"] = devAppIntFlag(cmd, "mode") updates++ } if cmd.Flags().Changed("add-scope") { value, _ := cmd.Flags().GetBool("add-scope") - params["isAddScope"] = value + params["addScope"] = value updates++ } if cmd.Flags().Changed("disable-ssl-verify") { @@ -933,7 +920,7 @@ func devAppRobotConfigParams(cmd *cobra.Command, appID string) (map[string]any, updates++ } if values, _ := cmd.Flags().GetStringSlice("skills"); len(values) > 0 { - params["skillList"] = values + params["skills"] = values updates++ } for _, item := range []struct{ key, flag string }{ @@ -976,7 +963,7 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { } params := map[string]any{"unifiedAppId": appID} devAppPutString(params, "version", devAppStringFlag(cmd, "version")) - devAppPutString(params, "description", devAppStringFlag(cmd, "desc")) + devAppPutString(params, "desc", devAppStringFlag(cmd, "desc")) return runDevAppTool(runner, cmd, devAppVersionCreateTool, params) }, } @@ -1164,7 +1151,7 @@ func newDevAppEventSubscribeCommand(runner executor.Runner, use, short, tool str cmd := &cobra.Command{ Use: use, Short: short, - Example: " dws devapp event " + use + " --unified-app-id UNIFIED_APP_ID --event-code user_add_org --dry-run --format json", + Example: " dws devapp event " + use + " --unified-app-id UNIFIED_APP_ID --event-codes user_add_org,org_dept_modify --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -1175,19 +1162,32 @@ func newDevAppEventSubscribeCommand(runner executor.Runner, use, short, tool str if err != nil { return err } - eventCode := devAppStringFlag(cmd, "event-code") - if eventCode == "" { - return apperrors.NewValidation("--event-code is required") + eventCodes := devAppEventCodes(cmd) + if len(eventCodes) == 0 { + return apperrors.NewValidation("--event-codes is required") } - return runDevAppTool(runner, cmd, tool, map[string]any{"unifiedAppId": appID, "eventCode": eventCode}) + return runDevAppTool(runner, cmd, tool, map[string]any{"unifiedAppId": appID, "eventCodes": eventCodes}) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("event-code", "", "事件码 eventCode,见 event list 返回 (必填)") + cmd.Flags().StringSlice("event-codes", nil, "事件码列表,多个用逗号分隔 (必填)") preferLegacyLeaf(cmd) return cmd } +func devAppEventCodes(cmd *cobra.Command) []string { + values, _ := cmd.Flags().GetStringSlice("event-codes") + out := make([]string, 0, len(values)) + for _, value := range values { + for _, part := range splitDevAppList(value) { + if part != "" { + out = append(out, part) + } + } + } + return out +} + func registerDevAppMemberMutationFlags(cmd *cobra.Command) { cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") cmd.Flags().String("users", "", "成员 userId 列表,多个用逗号分隔 (必填)") diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index f6c369c3..c8b60437 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -140,25 +140,23 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { }{ { name: "create sync", - args: []string{"robot", "create", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--yes"}, + args: []string{"robot", "create", "--name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--yes"}, wantTool: "create_dingtalk_robot", wantParams: map[string]any{ - "appName": "智能体", + "name": "智能体", "robotName": "小助手", "desc": "审批问答", }, }, { - name: "submit async fills media placeholders", - args: []string{"robot", "submit", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--task-id", "t-1", "--yes"}, + name: "submit async leaves optional media absent", + args: []string{"robot", "submit", "--name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--task-id", "t-1", "--yes"}, wantTool: "submit_robot_create_task", wantParams: map[string]any{ - "appName": "智能体", - "robotName": "小助手", - "desc": "审批问答", - "robotMediaId": "", - "previewMediaId": "", - "taskId": "t-1", + "name": "智能体", + "robotName": "小助手", + "desc": "审批问答", + "taskId": "t-1", }, }, { @@ -182,8 +180,8 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { "name": "小助手", "brief": "审批助手", "mode": 2, - "skillList": []string{"qa", "approval"}, - "isAddScope": true, + "skills": []string{"qa", "approval"}, + "addScope": true, }, }, { @@ -192,15 +190,6 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { wantTool: "enable_dev_app_robot", wantParams: map[string]any{"unifiedAppId": "u-1"}, }, - { - name: "enable with config", - args: []string{"robot", "enable", "--unified-app-id", "u-1", "--name", "小助手", "--yes"}, - wantTool: "enable_dev_app_robot", - wantParams: map[string]any{ - "unifiedAppId": "u-1", - "name": "小助手", - }, - }, { name: "disable", args: []string{"robot", "disable", "--unified-app-id", "u-1", "--yes"}, @@ -248,7 +237,7 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { wantParams: map[string]any{ "unifiedAppId": "u-1", "version": "1.0.1", - "description": "新增机器人", + "desc": "新增机器人", }, }, { @@ -330,15 +319,15 @@ func TestDevAppEventCommandsBuildToolParams(t *testing.T) { }, { name: "subscribe", - args: []string{"event", "subscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org", "--yes"}, + args: []string{"event", "subscribe", "--unified-app-id", "u-1", "--event-codes", "user_add_org,org_dept_modify", "--yes"}, wantTool: "subscribe_dev_app_event", - wantParams: map[string]any{"unifiedAppId": "u-1", "eventCode": "user_add_org"}, + wantParams: map[string]any{"unifiedAppId": "u-1", "eventCodes": []string{"user_add_org", "org_dept_modify"}}, }, { name: "unsubscribe", - args: []string{"event", "unsubscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org", "--yes"}, + args: []string{"event", "unsubscribe", "--unified-app-id", "u-1", "--event-codes", "user_add_org", "--yes"}, wantTool: "unsubscribe_dev_app_event", - wantParams: map[string]any{"unifiedAppId": "u-1", "eventCode": "user_add_org"}, + wantParams: map[string]any{"unifiedAppId": "u-1", "eventCodes": []string{"user_add_org"}}, }, } @@ -364,7 +353,7 @@ func TestDevAppEventCommandsBuildToolParams(t *testing.T) { } } -func TestDevAppEventSubscribeRequiresEventCode(t *testing.T) { +func TestDevAppEventSubscribeRequiresEventCodes(t *testing.T) { runner := &captureRunner{} root := newDevAppTestRoot(runner) var out bytes.Buffer @@ -374,10 +363,10 @@ func TestDevAppEventSubscribeRequiresEventCode(t *testing.T) { err := root.Execute() if err == nil { - t.Fatal("Execute() error = nil, want --event-code validation") + t.Fatal("Execute() error = nil, want --event-codes validation") } - if !strings.Contains(err.Error(), "event-code") { - t.Fatalf("error = %q, want event-code validation", err.Error()) + if !strings.Contains(err.Error(), "event-codes") { + t.Fatalf("error = %q, want event-codes validation", err.Error()) } if runner.last.Tool != "" { t.Fatalf("tool = %q, want no invocation", runner.last.Tool) @@ -400,8 +389,8 @@ func TestDevAppScopedCommandsRejectLegacyLocators(t *testing.T) { {name: "robot enable", args: []string{"devapp", "robot", "enable", "--app-id", "u-1", "--name", "小助手", "--yes"}}, {name: "robot disable", args: []string{"devapp", "robot", "disable", "--app-id", "u-1", "--yes"}}, {name: "event list", args: []string{"devapp", "event", "list", "--app-id", "u-1"}}, - {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--app-id", "u-1", "--event-code", "user_add_org", "--yes"}}, - {name: "event unsubscribe", args: []string{"devapp", "event", "unsubscribe", "--app-id", "u-1", "--event-code", "user_add_org", "--yes"}}, + {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--app-id", "u-1", "--event-codes", "user_add_org", "--yes"}}, + {name: "event unsubscribe", args: []string{"devapp", "event", "unsubscribe", "--app-id", "u-1", "--event-codes", "user_add_org", "--yes"}}, } for _, tc := range cases { @@ -482,14 +471,14 @@ func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { name string args []string }{ - {name: "robot create", args: []string{"devapp", "robot", "create", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批"}}, + {name: "robot create", args: []string{"devapp", "robot", "create", "--name", "智能体", "--robot-name", "小助手", "--desc", "审批"}}, {name: "robot config", args: []string{"devapp", "robot", "config", "--unified-app-id", "u-1", "--name", "小助手"}}, {name: "robot enable", args: []string{"devapp", "robot", "enable", "--unified-app-id", "u-1"}}, {name: "robot disable", args: []string{"devapp", "robot", "disable", "--unified-app-id", "u-1"}}, {name: "version create", args: []string{"devapp", "version", "create", "--unified-app-id", "u-1", "--version", "1.0.1"}}, {name: "version publish", args: []string{"devapp", "version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1"}}, - {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org"}}, - {name: "event unsubscribe", args: []string{"devapp", "event", "unsubscribe", "--unified-app-id", "u-1", "--event-code", "user_add_org"}}, + {name: "event subscribe", args: []string{"devapp", "event", "subscribe", "--unified-app-id", "u-1", "--event-codes", "user_add_org"}}, + {name: "event unsubscribe", args: []string{"devapp", "event", "unsubscribe", "--unified-app-id", "u-1", "--event-codes", "user_add_org"}}, } for _, tc := range cases { From 0e6cb46f5844e591e9be330f1806b5579c640feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Wed, 17 Jun 2026 03:29:46 +0800 Subject: [PATCH 25/47] fix: align devapp mcp compatibility --- docs/devapp-yulan-command-routing.md | 75 ++++----- internal/helpers/devapp.go | 148 +++++++++++------- internal/helpers/devapp_test.go | 63 ++++++-- skills/mono/references/products/devapp.md | 15 +- .../dingtalk-devapp/references/app-crud.md | 12 +- .../references/credentials-webapp.md | 3 - .../dingtalk-devapp/references/permissions.md | 6 +- .../dingtalk-devapp/references/workflows.md | 2 +- test/skill_tests.md | 29 +--- 9 files changed, 184 insertions(+), 169 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index c7e95d6b..e6e4964f 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -85,7 +85,7 @@ dws devapp permission search Search permission candidates, alias over list semantics permission detail Show one permission's full API coverage permission add Apply permissions into current app version changes - permission remove Remove one authorized permission + permission remove Remove authorized permissions event list List subscribable events and current subscription state event subscribe Subscribe one event by eventCode event unsubscribe Unsubscribe one event by eventCode @@ -110,7 +110,7 @@ MCP server (same pinned endpoint as the rest of `devapp`, no service discovery). Open-source examples should use `--format json` for agent consumption: ```bash -dws devapp list --name DemoApp --page 1 --page-size 20 --format json +dws devapp list --name DemoApp --page-size 20 --format json dws devapp get --unified-app-id UNIFIED_APP_ID --format json dws devapp permission list --unified-app-id UNIFIED_APP_ID --format json ``` @@ -134,7 +134,7 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | App update | `devapp update` | `update_dev_app` | `OpenInnerAppManageFacade.update` | Implemented | Hardcoded helper | | App delete | `devapp delete` | `delete_dev_app` | `OpenInnerAppManageFacade.delete` | Implemented | Hardcoded helper | | Credentials | `devapp credentials get` | `get_dev_app_credentials` | `OpenInnerAppQueryFacade.getCredentials` | Implemented | Hardcoded helper | -| Web app | `devapp webapp get/config` | `get_extension_webapp_config`/`set_extension_webapp_config` | Webapp facade | Implemented | Hardcoded helper; locator fields are `unifiedAppId/appKey`; config fields are `h5PageType/homepageUrl/pcHomepageUrl/ompUrl`. | +| Web app | `devapp webapp get/config` | `get_extension_webapp_config`/`set_extension_webapp_config` | Webapp facade | Implemented | Hardcoded helper; locator field is `unifiedAppId`; config fields are `h5PageType/homepageUrl/pcHomepageUrl/ompUrl`. | | Permission list | `devapp permission list/search/detail` | `list_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | | Permission apply | `devapp permission add` | `apply_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | | Permission remove | `devapp permission remove` | `remove_dev_app_permissions` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | @@ -205,7 +205,6 @@ Input fields: | `pageSize` | number | `--page-size` | no | Default 20; cap by backend. | | `cursor` | string | `--cursor` | no | Cursor from previous response; empty for first page. | | `name` | string | `--name` | no | App name keyword. | -| `appKey` | string | `--app-key` | no | DingTalk appKey/clientId. | Normalized response: @@ -221,7 +220,7 @@ Normalized response: "appKey": "dingxxx", "creator": "Creator", "robotName": "BotName", - "gmtModified": "2026-06-05T00:00:00+08:00" + "modifiedAt": "2026-06-05 00:00:00" } ], "raw": {} @@ -234,20 +233,18 @@ Normalized response: | --- | --- | | CLI | `dws devapp get` | | HSF facade | `OpenInnerAppQueryFacade.getDetail` | -| Request shape | One object with system context and one or more app locator fields. | -| Required public fields | At least one locator. | +| Request shape | One object with system context and `unifiedAppId`. | +| Required public fields | `unifiedAppId`. | | Identity source | `corpId/userId` from MCP system context. | -Locator fields: +Locator field: | Field | Type | CLI flag | Notes | | --- | --- | --- | --- | -| `unifiedAppId` | string | `--unified-app-id` | Preferred. | -| `appKey` | string | `--app-key` | appKey/clientId. | -| `name` | string | `--name` | Fuzzy; must resolve uniquely before writes. | +| `unifiedAppId` | string | `--unified-app-id` | Required app identity. | -Detail response should include identifiers and configuration summary, but not -full `clientSecret/appSecret`: +Detail response may include identifiers, configuration summary, and credentials. +Treat `clientSecret/appSecret` as sensitive values in all user-visible output: ```json { @@ -435,8 +432,8 @@ current app version. Approver selection is handled by the version flow. | --- | --- | | CLI | `dws devapp permission remove` | | HSF facade | `OpenInnerAppPermissionFacade.remove` | -| Request shape | One object with system context, app locator, and one `scopeValue`. | -| Required public fields | `unifiedAppId` and one `scopeValue`. | +| Request shape | One object with system context, app locator, and `scopeValues`. | +| Required public fields | `unifiedAppId` and non-empty `scopeValues`. | | Write guard | CLI `--dry-run`, then `--yes`; no MCP confirm field. | Input fields: @@ -444,7 +441,7 @@ Input fields: | Field | Type | CLI flag | Required | Notes | | --- | --- | --- | --- | --- | | `unifiedAppId` | string | `--unified-app-id` | yes | Unified app id. | -| `scopeValue` | string | `--permission` | yes | Remove exactly one permission. | +| `scopeValues` | string[] | `--permissions` | yes | Remove one or more permissions. | Expected structured failures: @@ -489,17 +486,14 @@ Tool override examples: "flags": { "pageSize": {"alias": "page-size", "default": 20}, "cursor": {"alias": "cursor"}, - "name": {"alias": "name"}, - "appKey": {"alias": "app-key"} + "name": {"alias": "name"} } }, "get_dev_app": { "cliName": "get", "description": "查询开放平台企业内部应用详情", "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "appKey": {"alias": "app-key"}, - "name": {"alias": "name"} + "unifiedAppId": {"alias": "unified-app-id", "required": true} } }, "create_dev_app": { @@ -517,8 +511,7 @@ Tool override examples: "description": "修改开放平台企业内部应用基础信息", "isSensitive": true, "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "appKey": {"alias": "app-key"}, + "unifiedAppId": {"alias": "unified-app-id", "required": true}, "name": {"alias": "name"}, "desc": {"alias": "desc"}, "icon": {"alias": "icon"} @@ -529,9 +522,7 @@ Tool override examples: "description": "删除开放平台企业内部应用", "isSensitive": true, "flags": { - "unifiedAppId": {"alias": "unified-app-id"}, - "appKey": {"alias": "app-key"}, - "name": {"alias": "name"} + "unifiedAppId": {"alias": "unified-app-id", "required": true} } }, "list_dev_app_permissions": { @@ -567,14 +558,15 @@ Tool override examples: "remove_dev_app_permissions": { "cliName": "remove", "group": "permission", - "description": "取消一个已开通的开放平台应用权限点", + "description": "取消一个或多个已开通的开放平台应用权限点", "isSensitive": true, "flags": { "unifiedAppId": {"alias": "unified-app-id"}, - "scopeValue": { - "alias": "permission", + "scopeValues": { + "alias": "permissions", + "type": "stringSlice", "required": true, - "description": "待取消权限点 scopeValue" + "description": "待取消权限点 scopeValue,多个用逗号分隔" } } } @@ -626,9 +618,8 @@ Open-source DWS and Agent must not pass `orgId` or `uid` as user input. ### 6.1 List ```bash -dws devapp list --page 1 --page-size 20 --format json +dws devapp list --page-size 20 --format json dws devapp list --name DemoApp --format json -dws devapp list --app-key dingxxx --format json dws devapp list --page-size 20 --cursor NEXT_CURSOR --format json ``` @@ -647,8 +638,6 @@ Pagination is cursor based. A baseline call should pass only `pageSize`; use the ```bash dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --app-key dingxxx --format json -dws devapp get --name DemoApp --format json ``` MCP payload: @@ -659,7 +648,7 @@ MCP payload: } ``` -`get` may show `clientId/appKey`, but it must not be used to read full `clientSecret/appSecret`. Secret read belongs to `credentials get`. +`get` may show `clientId/appKey/clientSecret/appSecret`; callers must treat secret fields as sensitive. Active secret reads still prefer `credentials get`. ### 6.3 Create @@ -721,7 +710,7 @@ MCP payload: } ``` -Valid public locator fields are `unifiedAppId`, `appKey`, and `name`. Name/key locators must uniquely resolve. +Valid public locator field is `unifiedAppId`. ## 7. Credentials @@ -729,8 +718,6 @@ Target command: ```bash dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --app-key dingxxx --format json -dws devapp credentials get --name DemoApp --format json ``` Current status: @@ -936,8 +923,8 @@ Expected response: ### 8.4 Remove ```bash -dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --dry-run --format json -dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --yes --format json +dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --dry-run --format json +dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --yes --format json ``` MCP payload: @@ -945,7 +932,7 @@ MCP payload: ```json { "unifiedAppId": "UNIFIED_APP_ID", - "scopeValue": "qyapi_robot_sendmsg" + "scopeValues": ["qyapi_robot_sendmsg"] } ``` @@ -1175,7 +1162,7 @@ Do not add confirmation booleans to MCP payloads. These belong to the CLI layer: | User request | Agent recipe | | --- | --- | | "查 DemoApp 应用" | `devapp list --name DemoApp`; if one result, optionally `devapp get`; if multiple, show candidates. | -| "这个 appKey 是哪个应用" | `devapp get --app-key APP_KEY`; if multiple apps match, ask for `unifiedAppId`. | +| "这个 appKey 是哪个应用" | 当前 CLI 不用 appKey 定位详情;先让用户提供 `unifiedAppId`,或用 `devapp list --name` 缩小候选后再 `devapp get --unified-app-id ...`。 | | "创建企业内部应用" | `devapp create --name ... --type internal --dry-run`; after confirmation, repeat with `--yes`. | | "修改应用描述/图标" | Resolve `unifiedAppId`; `devapp update --unified-app-id ... --desc/icon ... --dry-run`; after confirmation, `--yes`. | | "删除应用" | Resolve unique app; show app summary; `devapp delete ... --dry-run`; after confirmation, `--yes`. | @@ -1360,7 +1347,7 @@ P0 debug inputs: | `delete_dev_app` | `{"unifiedAppId":"UNIFIED_APP_ID"}` | | `list_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","authStatus":"ALL","pageSize":20}` | | `apply_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValues":["SCOPE_VALUE"]}` | -| `remove_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValue":"SCOPE_VALUE"}` | +| `remove_dev_app_permissions` | `{"unifiedAppId":"UNIFIED_APP_ID","scopeValues":["SCOPE_VALUE"]}` | For destructive debug cases, use a disposable application and permission scope. Do not use production data in long-lived examples. @@ -1458,7 +1445,7 @@ the following are true: | `dws devapp permission list --unified-app-id X --format json` | Returns flattened APP and SNS permissions. | | `dws devapp permission list --unified-app-id X --keyword "发送消息" --format json` | Returns candidate `permissions[]`; no automatic apply. | | `dws devapp permission add --unified-app-id X --permissions Contact.User.mobile --yes --format json` | Applies or stages permission; `requiredApproval=true` is allowed. | -| `dws devapp permission remove --unified-app-id X --permission qyapi_robot_sendmsg --yes --format json` | Removes one authorized scope or returns a structured reason. | +| `dws devapp permission remove --unified-app-id X --permissions qyapi_robot_sendmsg --yes --format json` | Removes authorized scopes or returns a structured reason. | | `dws devapp credentials get --unified-app-id X --format json` | Calls `get_dev_app_credentials`; does not use `devapp get` as a secret fallback. | | `查应用 appXYZ 的详情` without OpenDev context | Does not blindly route to `devapp`; uses context such as `workbench app get` or asks. | | `查询开放平台 API 错误码` | Routes to `devdoc`, not `devapp`. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 48b0e7f6..07df6c54 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -254,7 +254,6 @@ func newDevAppListCommand(runner executor.Runner) *cobra.Command { } devAppPutString(params, "cursor", devAppStringFlag(cmd, "cursor")) devAppPutString(params, "name", devAppFlagOrFallback(cmd, "name", "keyword")) - devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) return runDevAppTool(runner, cmd, devAppListTool, params) }, } @@ -263,7 +262,6 @@ func newDevAppListCommand(runner executor.Runner) *cobra.Command { cmd.Flags().String("name", "", "应用名称关键词") cmd.Flags().String("keyword", "", "--name 的兼容别名") _ = cmd.Flags().MarkHidden("keyword") - cmd.Flags().String("app-key", "", "appKey/clientId") preferLegacyLeaf(cmd) return cmd } @@ -276,14 +274,14 @@ func newDevAppGetCommand(runner executor.Runner) *cobra.Command { Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppMcpLocatorParams(cmd, true) + params := devAppUnifiedAppIDParams(cmd) if len(params) == 0 { - return devAppMcpLocatorRequired(true) + return devAppUnifiedAppIDRequired() } return runDevAppTool(runner, cmd, devAppGetTool, params) }, } - addDevAppMcpLocatorFlags(cmd, true) + addDevAppUnifiedAppIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -332,9 +330,9 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { if err := devAppRequireWriteGuard(cmd, "update"); err != nil { return err } - params := devAppMcpLocatorParams(cmd, false) + params := devAppUnifiedAppIDParams(cmd) if len(params) == 0 { - return devAppMcpLocatorRequired(false) + return devAppUnifiedAppIDRequired() } updates := 0 if v := devAppStringFlag(cmd, "name"); v != "" { @@ -355,7 +353,7 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { return runDevAppTool(runner, cmd, devAppUpdateTool, params) }, } - addDevAppMcpLocatorFlags(cmd, false) + addDevAppUnifiedAppIDFlag(cmd) cmd.Flags().String("name", "", "新的应用名称") cmd.Flags().String("desc", "", "新的应用描述") cmd.Flags().String("icon", "", "新的应用图标 mediaId") @@ -371,14 +369,14 @@ func newDevAppCredentialsGetCommand(runner executor.Runner) *cobra.Command { Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppCredentialsLocatorParams(cmd) + params := devAppUnifiedAppIDParams(cmd) if len(params) == 0 { - return devAppCredentialsLocatorRequired() + return devAppUnifiedAppIDRequired() } return runDevAppTool(runner, cmd, devAppCredentialsGet, params) }, } - addDevAppCredentialsLocatorFlags(cmd) + addDevAppUnifiedAppIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -393,14 +391,14 @@ func newDevAppLifecycleCommand(runner executor.Runner, use, short, tool string) if err := devAppRequireWriteGuard(cmd, use); err != nil { return err } - params := devAppMcpLocatorParams(cmd, true) + params := devAppUnifiedAppIDParams(cmd) if len(params) == 0 { - return devAppMcpLocatorRequired(true) + return devAppUnifiedAppIDRequired() } return runDevAppTool(runner, cmd, tool, params) }, } - addDevAppMcpLocatorFlags(cmd, true) + addDevAppUnifiedAppIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -413,14 +411,14 @@ func newDevAppWebappGetCommand(runner executor.Runner) *cobra.Command { Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - params := devAppMcpLocatorParams(cmd, false) + params := devAppUnifiedAppIDParams(cmd) if len(params) == 0 { - return devAppMcpLocatorRequired(false) + return devAppUnifiedAppIDRequired() } return runDevAppTool(runner, cmd, "get_extension_webapp_config", params) }, } - addDevAppMcpLocatorFlags(cmd, false) + addDevAppUnifiedAppIDFlag(cmd) preferLegacyLeaf(cmd) return cmd } @@ -436,9 +434,9 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { if err := devAppRequireWriteGuard(cmd, "webapp config"); err != nil { return err } - params := devAppMcpLocatorParams(cmd, true) + params := devAppUnifiedAppIDParams(cmd) if len(params) == 0 { - return devAppMcpLocatorRequired(true) + return devAppUnifiedAppIDRequired() } updates := 0 if v := devAppStringFlag(cmd, "h5-page-type"); v != "" { @@ -463,7 +461,7 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { return runDevAppTool(runner, cmd, "set_extension_webapp_config", params) }, } - addDevAppMcpLocatorFlags(cmd, true) + addDevAppUnifiedAppIDFlag(cmd) cmd.Flags().String("h5-page-type", "", "网页应用生效端/页面类型") cmd.Flags().String("homepage-url", "", "移动端首页地址") cmd.Flags().String("homepage-link", "", "--homepage-url 的兼容别名") @@ -563,17 +561,19 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { if len(params) == 0 { return devAppPermissionLocatorRequired() } - scope := devAppFlagOrFallback(cmd, "permission", "scope") - if scope == "" { - return apperrors.NewValidation("--permission is required") + scopes := devAppPermissionRemoveScopes(cmd) + if len(scopes) == 0 { + return apperrors.NewValidation("--permissions is required") } - params["scopeValue"] = scope + params["scopeValues"] = scopes return runDevAppTool(runner, cmd, "remove_dev_app_permissions", params) }, } addDevAppPermissionLocatorFlags(cmd) - cmd.Flags().String("permission", "", "待取消权限点 scopeValue") - cmd.Flags().String("scope", "", "--permission 的兼容别名") + cmd.Flags().StringSlice("permissions", nil, "待取消权限点 scopeValue,多个用逗号分隔") + cmd.Flags().String("permission", "", "--permissions 的兼容别名,单个权限点 scopeValue") + cmd.Flags().String("scope", "", "--permissions 的兼容别名,单个权限点 scopeValue") + _ = cmd.Flags().MarkHidden("permission") _ = cmd.Flags().MarkHidden("scope") preferLegacyLeaf(cmd) return cmd @@ -1203,7 +1203,7 @@ func runDevAppTool(runner executor.Runner, cmd *cobra.Command, tool string, para if err != nil { return err } - result = normalizeDevAppServiceResult(result) + result = normalizeDevAppToolResult(tool, normalizeDevAppServiceResult(result)) return writeCommandPayload(cmd, result) } @@ -1223,6 +1223,49 @@ func normalizeDevAppServiceResult(result executor.Result) executor.Result { return result } +func normalizeDevAppToolResult(tool string, result executor.Result) executor.Result { + content, ok := result.Response["content"].(map[string]any) + if !ok { + return result + } + switch tool { + case "remove_dev_app_permissions": + normalizeDevAppScopeValueArray(content, "removedScopeValues") + case devAppDisableTool: + if _, ok := content["disabled"]; !ok { + content["disabled"] = true + } + case devAppEnableTool: + if _, ok := content["enabled"]; !ok { + content["enabled"] = true + } + } + return result +} + +func normalizeDevAppScopeValueArray(content map[string]any, key string) { + values, ok := content[key].([]any) + if !ok { + return + } + out := make([]string, 0, len(values)) + for _, value := range values { + switch typed := value.(type) { + case string: + if typed != "" { + out = append(out, typed) + } + case map[string]any: + if scopeValue, _ := typed["scopeValue"].(string); scopeValue != "" { + out = append(out, scopeValue) + } + } + } + if len(out) == len(values) { + content[key] = out + } +} + func requiredDevAppID(cmd *cobra.Command) (string, error) { appID, _ := cmd.Flags().GetString("app-id") appID = strings.TrimSpace(appID) @@ -1274,47 +1317,18 @@ func splitDevAppList(raw string) []string { return values } -func addDevAppMcpLocatorFlags(cmd *cobra.Command, includeName bool) { +func addDevAppUnifiedAppIDFlag(cmd *cobra.Command) { cmd.Flags().String("unified-app-id", "", "统一应用 ID") - cmd.Flags().String("app-key", "", "appKey/clientId") - if includeName { - cmd.Flags().String("name", "", "应用名称关键词;写操作前必须唯一命中") - } } -func devAppMcpLocatorParams(cmd *cobra.Command, includeName bool) map[string]any { +func devAppUnifiedAppIDParams(cmd *cobra.Command) map[string]any { params := map[string]any{} devAppPutString(params, "unifiedAppId", devAppStringFlag(cmd, "unified-app-id")) - devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) - if includeName { - devAppPutString(params, "name", devAppStringFlag(cmd, "name")) - } return params } -func addDevAppCredentialsLocatorFlags(cmd *cobra.Command) { - cmd.Flags().String("unified-app-id", "", "统一应用 ID") - cmd.Flags().String("app-key", "", "appKey/clientId") - cmd.Flags().String("name", "", "应用名称关键词;必须唯一命中") -} - -func devAppCredentialsLocatorParams(cmd *cobra.Command) map[string]any { - params := map[string]any{} - devAppPutString(params, "unifiedAppId", devAppStringFlag(cmd, "unified-app-id")) - devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) - devAppPutString(params, "name", devAppStringFlag(cmd, "name")) - return params -} - -func devAppCredentialsLocatorRequired() error { - return apperrors.NewValidation("one app locator is required: --unified-app-id, --app-key, or --name") -} - -func devAppMcpLocatorRequired(includeName bool) error { - if includeName { - return apperrors.NewValidation("one app locator is required: --unified-app-id, --app-key, or --name") - } - return apperrors.NewValidation("one app locator is required: --unified-app-id or --app-key") +func devAppUnifiedAppIDRequired() error { + return apperrors.NewValidation("--unified-app-id is required") } func addDevAppPermissionLocatorFlags(cmd *cobra.Command) { @@ -1364,6 +1378,20 @@ func devAppPermissionScopes(cmd *cobra.Command) []string { return out } +func devAppPermissionRemoveScopes(cmd *cobra.Command) []string { + values, _ := cmd.Flags().GetStringSlice("permissions") + values = append(values, devAppFlagOrFallback(cmd, "permission", "scope")) + out := make([]string, 0, len(values)) + for _, value := range values { + for _, part := range splitDevAppList(value) { + if part != "" { + out = append(out, part) + } + } + } + return out +} + func devAppStringFlag(cmd *cobra.Command, name string) string { value, _ := cmd.Flags().GetString(name) return strings.TrimSpace(value) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 1d0cfa71..b64cebec 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -505,7 +505,7 @@ func TestDevAppListBuildsListByConditionParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"list", "--name", "Waker", "--app-key", "dingxxx", "--page-size", "5", "--cursor", "next-1"}) + root.SetArgs([]string{"list", "--name", "Waker", "--page-size", "5", "--cursor", "next-1"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -517,7 +517,6 @@ func TestDevAppListBuildsListByConditionParams(t *testing.T) { "pageSize": 5, "cursor": "next-1", "name": "Waker", - "appKey": "dingxxx", } if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) @@ -603,10 +602,10 @@ func TestDevAppLifecycleBuildsLocatorParams(t *testing.T) { wantParams map[string]any }{ { - name: "delete by name", - args: []string{"delete", "--name", "Demo", "--yes"}, + name: "delete by unified app id", + args: []string{"delete", "--unified-app-id", "u-2", "--yes"}, wantTool: "delete_dev_app", - wantParams: map[string]any{"name": "Demo"}, + wantParams: map[string]any{"unifiedAppId": "u-2"}, }, { name: "inactive by unified app id", @@ -615,10 +614,10 @@ func TestDevAppLifecycleBuildsLocatorParams(t *testing.T) { wantParams: map[string]any{"unifiedAppId": "u-1"}, }, { - name: "active by app key", - args: []string{"active", "--app-key", "dingxxx", "--yes"}, + name: "active by unified app id", + args: []string{"active", "--unified-app-id", "u-3", "--yes"}, wantTool: "enable_dev_app", - wantParams: map[string]any{"appKey": "dingxxx"}, + wantParams: map[string]any{"unifiedAppId": "u-3"}, }, } @@ -721,11 +720,11 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { }, { name: "remove", - args: []string{"permission", "remove", "--unified-app-id", "u-1", "--permission", "Contact.User.mobile", "--yes"}, + args: []string{"permission", "remove", "--unified-app-id", "u-1", "--permissions", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, wantTool: "remove_dev_app_permissions", wantParams: map[string]any{ "unifiedAppId": "u-1", - "scopeValue": "Contact.User.mobile", + "scopeValues": []string{"Contact.User.mobile", "qyapi_robot_sendmsg"}, }, }, } @@ -763,11 +762,6 @@ func TestDevAppCredentialsGetBuildsParams(t *testing.T) { args: []string{"devapp", "credentials", "get", "--unified-app-id", "u-1"}, wantParams: map[string]any{"unifiedAppId": "u-1"}, }, - { - name: "by name", - args: []string{"devapp", "credentials", "get", "--name", "Demo"}, - wantParams: map[string]any{"name": "Demo"}, - }, } for _, tc := range cases { @@ -858,6 +852,45 @@ func TestDevAppUnwrapsSuccessfulServiceResult(t *testing.T) { } } +func TestNormalizeDevAppRemovePermissionScopeValues(t *testing.T) { + result := executor.Result{ + Response: map[string]any{ + "content": map[string]any{ + "removedScopeValues": []any{ + map[string]any{"scopeValue": "qyapi_robot_sendmsg", "scopeName": "机器人发消息"}, + map[string]any{"scopeValue": "Contact.User.mobile", "scopeName": "手机号"}, + }, + }, + }, + } + + normalized := normalizeDevAppToolResult("remove_dev_app_permissions", result) + content := normalized.Response["content"].(map[string]any) + got := content["removedScopeValues"] + want := []string{"qyapi_robot_sendmsg", "Contact.User.mobile"} + if !reflect.DeepEqual(got, want) { + t.Fatalf("removedScopeValues = %#v, want %#v", got, want) + } +} + +func TestNormalizeDevAppLifecycleResult(t *testing.T) { + disable := normalizeDevAppToolResult(devAppDisableTool, executor.Result{ + Response: map[string]any{"content": map[string]any{"deleted": false}}, + }) + disableContent := disable.Response["content"].(map[string]any) + if disabled, _ := disableContent["disabled"].(bool); !disabled { + t.Fatalf("disabled = %#v, want true", disableContent["disabled"]) + } + + enable := normalizeDevAppToolResult(devAppEnableTool, executor.Result{ + Response: map[string]any{"content": map[string]any{"deleted": false}}, + }) + enableContent := enable.Response["content"].(map[string]any) + if enabled, _ := enableContent["enabled"].(bool); !enabled { + t.Fatalf("enabled = %#v, want true", enableContent["enabled"]) + } +} + func TestDevAppMemberCommandsValidateRequiredFlags(t *testing.T) { cases := []struct { name string diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 94868e9e..90b07951 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -8,17 +8,13 @@ 1. 所有命令加 `--format json`。 2. 写操作先 `--dry-run`,确认后才加 `--yes`。 -3. 应用名/appKey 命中多条时展示候选,不取第一条。 +3. 写操作统一通过 `--unified-app-id` 定位应用;不要用 name/appKey 做写操作定位。 4. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名。 5. 主动读取密钥走 `credentials get`;任何 `devapp get`/详情返回里的 `clientSecret/appSecret` 都按敏感凭证脱敏,不向用户展开。 ## 应用定位 -| 优先级 | 标识 | 处理 | -|--------|------|------| -| 1 | `--unified-app-id` | 直接使用 | -| 2 | `--app-key` | appKey/clientId,唯一命中才继续 | -| 3 | `--name` | 应用名称关键词,写操作必须唯一命中 | +除 `list` 支持 `--name` 搜索外,详情、凭证、网页应用、权限和写操作都使用 `--unified-app-id`。 --- @@ -29,7 +25,6 @@ ```bash dws devapp list --format json dws devapp list --name DemoApp --format json -dws devapp list --app-key dingxxx --format json ``` MCP tool: `list_dev_app` @@ -39,7 +34,6 @@ MCP tool: `list_dev_app` | `--page-size` | `pageSize` | 默认 20 | | `--cursor` | `cursor` | 游标,首次为空 | | `--name` / `--keyword` | `name` | 应用名搜索 | -| `--app-key` | `appKey` | appKey/clientId | 应用状态字段:列表/详情原始字段里如果出现 `status` / `appStatus`,`0=IN_ACTIVE` 已停用、`1=ACTIVE` 已激活、`2=WAIT_ACTIVE` 待激活、`3=EXPIRED` 已过期。执行 `inactive/active` 后必须回读 `get` 或 `list`:看到 `status=0` 才算停用完成,看到 `status=1` 才算启用完成。`versionStatus` 是版本状态,不要和应用启停状态混用。 @@ -156,10 +150,11 @@ MCP tool: `apply_dev_app_permissions`。`requiredApproval=true` 允许申请, ### 取消权限 ```bash -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --dry-run --format json +dws devapp permission remove --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json +dws devapp permission remove --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --dry-run --format json ``` -MCP tool: `remove_dev_app_permissions`。一次只取消一个。 +MCP tool: `remove_dev_app_permissions`。`--permissions` 接收一个或多个 `scopeValue`,多个用逗号分隔;旧 `--permission/--scope` 仅作为兼容别名。 --- diff --git a/skills/multi/dingtalk-devapp/references/app-crud.md b/skills/multi/dingtalk-devapp/references/app-crud.md index 7082fe51..28bc8ef1 100644 --- a/skills/multi/dingtalk-devapp/references/app-crud.md +++ b/skills/multi/dingtalk-devapp/references/app-crud.md @@ -4,20 +4,13 @@ ## 应用定位 -写操作前必须定位到唯一应用: - -| 优先级 | 标识 | 处理 | -|--------|------|------| -| 1 | `--unified-app-id` | 直接使用 | -| 2 | `--app-key` | appKey/clientId,唯一命中才继续 | -| 3 | `--name` | 应用名称关键词,写操作必须唯一命中 | +除列表支持 `--name` 搜索外,详情、修改、启停和删除统一使用 `--unified-app-id` 定位应用。 ## 应用列表 ```bash dws devapp list --format json dws devapp list --name DemoApp --format json -dws devapp list --app-key dingxxx --format json dws devapp list --name DemoApp --page-size 20 --cursor NEXT_CURSOR --format json ``` @@ -28,7 +21,6 @@ MCP tool: `list_dev_app` | `--page-size` | `pageSize` | 默认 20 | | `--cursor` | `cursor` | 游标,首次为空,用上一页 `nextCursor` 继续 | | `--name` / `--keyword` | `name` | 应用名搜索 | -| `--app-key` | `appKey` | appKey/clientId | ## 应用状态字段 @@ -47,8 +39,6 @@ MCP tool: `list_dev_app` ```bash dws devapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp get --app-key dingxxx --format json -dws devapp get --name DemoApp --format json ``` MCP tool: `get_dev_app` diff --git a/skills/multi/dingtalk-devapp/references/credentials-webapp.md b/skills/multi/dingtalk-devapp/references/credentials-webapp.md index 864a7566..d0546e83 100644 --- a/skills/multi/dingtalk-devapp/references/credentials-webapp.md +++ b/skills/multi/dingtalk-devapp/references/credentials-webapp.md @@ -4,8 +4,6 @@ ```bash dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json -dws devapp credentials get --app-key dingxxx --format json -dws devapp credentials get --name DemoApp --format json ``` MCP tool: `get_dev_app_credentials` @@ -30,7 +28,6 @@ MCP tool: `get_dev_app_credentials` ```bash dws devapp webapp get --unified-app-id UNIFIED_APP_ID --format json -dws devapp webapp get --app-key dingxxx --format json ``` MCP tool: `get_extension_webapp_config` diff --git a/skills/multi/dingtalk-devapp/references/permissions.md b/skills/multi/dingtalk-devapp/references/permissions.md index 79ff7b38..ceeb2867 100644 --- a/skills/multi/dingtalk-devapp/references/permissions.md +++ b/skills/multi/dingtalk-devapp/references/permissions.md @@ -85,10 +85,10 @@ MCP tool: `apply_dev_app_permissions` ## 取消权限 ```bash -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --dry-run --format json -dws devapp permission remove --unified-app-id ID --permission qyapi_robot_sendmsg --yes --format json +dws devapp permission remove --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json +dws devapp permission remove --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --yes --format json ``` MCP tool: `remove_dev_app_permissions` -一次只取消一个权限点。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 +`--permissions` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。旧 `--permission/--scope` 仅作为单个权限点兼容别名。 diff --git a/skills/multi/dingtalk-devapp/references/workflows.md b/skills/multi/dingtalk-devapp/references/workflows.md index b07be648..7f4adac2 100644 --- a/skills/multi/dingtalk-devapp/references/workflows.md +++ b/skills/multi/dingtalk-devapp/references/workflows.md @@ -64,7 +64,7 @@ → 确认 authed=true 6. 取消不需要的权限 - dws devapp permission remove --unified-app-id --permission --dry-run --format json + dws devapp permission remove --unified-app-id --permissions --dry-run --format json → 确认后加 --yes ``` diff --git a/test/skill_tests.md b/test/skill_tests.md index 0f39ab26..f8aede2a 100644 --- a/test/skill_tests.md +++ b/test/skill_tests.md @@ -45,7 +45,7 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 | `chat` | `references/products/chat.md` | 13 | 31 | | `contact` | `references/products/contact.md` | 7 | 14 | | `devdoc` | `references/products/simple.md` | 1 | 7 | -| `devapp` | `references/products/devapp.md` | 18 | 25 | +| `devapp` | `references/products/devapp.md` | 18 | 22 | | `ding` | `references/products/ding.md` | 2 | 5 | | `report` | `references/products/report.md` | 6 | 26 | | `routing` | `references/intent-guide.md` | 1 | 3 | @@ -834,7 +834,7 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 --- -### devapp(25 条) +### devapp(22 条) #### `dws devapp list` @@ -844,9 +844,9 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 - Flags: `--name` = `DemoApp` **devapp_devapp_list_002** -- Prompt: 分页查询开放平台应用,第 2 页,每页 10 条,按修改时间倒序 -- Expected: `dws devapp list --page 2 --page-size 10 --sort gmt_modified --order desc --format json` -- Flags: `--page` = `2`, `--page-size` = `10`, `--sort` = `gmt_modified`, `--order` = `desc` +- Prompt: 分页查询开放平台应用,使用上一页返回的 cursor,每页 10 条 +- Expected: `dws devapp list --cursor NEXT_CURSOR --page-size 10 --format json` +- Flags: `--cursor` = `NEXT_CURSOR`, `--page-size` = `10` **devapp_devapp_list_003** - Prompt: 在开发者后台搜索企业内部应用 DemoApp @@ -860,16 +860,6 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 - Expected: `dws devapp get --unified-app-id UNIFIED_APP_ID --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID` -**devapp_devapp_get_002** -- Prompt: agentId 为 123456 的开放平台应用是哪个 -- Expected: `dws devapp get --agent-id 123456 --format json` -- Flags: `--agent-id` = `123456` - -**devapp_devapp_get_003** -- Prompt: 查开放平台应用 DemoApp 的 appKey、clientId 和 agentId -- Expected: `dws devapp get --name DemoApp --format json` -- Flags: `--name` = `DemoApp` - #### `dws devapp create` **devapp_devapp_create_001** @@ -903,11 +893,6 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 - Expected: `dws devapp credentials get --unified-app-id UNIFIED_APP_ID --format json` - Flags: `--unified-app-id` = `UNIFIED_APP_ID` -**devapp_devapp_credentials_get_002** -- Prompt: 查询 AgentId AGENT_ID 对应应用的开放平台凭证 -- Expected: `dws devapp credentials get --agent-id AGENT_ID --format json` -- Flags: `--agent-id` = `AGENT_ID` - #### `dws devapp member list` **devapp_devapp_member_list_001** @@ -974,8 +959,8 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 **devapp_devapp_permission_remove_001** - Prompt: 从应用 UNIFIED_APP_ID 移除 qyapi_robot_sendmsg 权限,先预览 -- Expected: `dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --dry-run --format json` -- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--permission` = `qyapi_robot_sendmsg` +- Expected: `dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --dry-run --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--permissions` = `qyapi_robot_sendmsg` #### `dws devapp event list` From 6447bdd45fb1a27bf6233ce4b4523e57bd4a7af2 Mon Sep 17 00:00:00 2001 From: quanming Date: Wed, 17 Jun 2026 09:53:31 +0800 Subject: [PATCH 26/47] fix: align devapp member security app id flag --- internal/helpers/devapp.go | 37 ++++++--- internal/helpers/devapp_test.go | 78 ++++++++++++++++--- .../references/member-security.md | 18 ++--- .../dingtalk-devapp/references/workflows.md | 18 ++--- skills/mono/references/products/devapp.md | 8 +- .../references/member-security.md | 18 ++--- .../dingtalk-devapp/references/workflows.md | 18 ++--- test/skill_tests.md | 16 ++-- test/skill_tests_results.md | 8 +- 9 files changed, 147 insertions(+), 72 deletions(-) diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 13539224..1b33292e 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -583,11 +583,11 @@ func newDevAppMemberListCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "查询开放平台应用成员", - Example: " dws devapp member list --app-id ", + Example: " dws devapp member list --unified-app-id ", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - appID, err := requiredDevAppID(cmd) + appID, err := requiredDevAppUnifiedIDWithLegacyAppID(cmd) if err != nil { return err } @@ -596,7 +596,7 @@ func newDevAppMemberListCommand(runner executor.Runner) *cobra.Command { }) }, } - cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") + addDevAppUnifiedIDFlagWithLegacyAppID(cmd) preferLegacyLeaf(cmd) return cmd } @@ -605,7 +605,7 @@ func newDevAppMemberAddCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "添加开放平台应用成员", - Example: " dws devapp member add --app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws devapp member add --unified-app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -621,7 +621,7 @@ func newDevAppMemberRemoveCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "移除开放平台应用成员", - Example: " dws devapp member remove --app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws devapp member remove --unified-app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -637,7 +637,7 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "config", Short: "更新开放平台应用安全配置", - Example: " dws devapp security config --app-id " + + Example: " dws devapp security config --unified-app-id " + "--ip-whitelist 192.0.2.10 --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, @@ -645,7 +645,7 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { if err := devAppRequireWriteGuard(cmd, "security config"); err != nil { return err } - appID, err := requiredDevAppID(cmd) + appID, err := requiredDevAppUnifiedIDWithLegacyAppID(cmd) if err != nil { return err } @@ -666,7 +666,7 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { return runDevAppTool(runner, cmd, devAppSecurityConfigTool, params) }, } - cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") + addDevAppUnifiedIDFlagWithLegacyAppID(cmd) cmd.Flags().String("ip-whitelist", "", "出口 IP 白名单,多个用逗号或分号分隔") cmd.Flags().String("redirect-url", "", "登录重定向 URL,多个用逗号或分号分隔") cmd.Flags().String("sso-url", "", "端内免登地址,多个用逗号或分号分隔") @@ -1115,6 +1115,12 @@ func addDevAppUnifiedIDFlag(cmd *cobra.Command) { cmd.Flags().String("unified-app-id", "", "统一应用 ID (必填)") } +func addDevAppUnifiedIDFlagWithLegacyAppID(cmd *cobra.Command) { + addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("app-id", "", "--unified-app-id 的兼容别名") + _ = cmd.Flags().MarkHidden("app-id") +} + func requiredDevAppUnifiedID(cmd *cobra.Command) (string, error) { appID := devAppStringFlag(cmd, "unified-app-id") if appID == "" { @@ -1123,6 +1129,17 @@ func requiredDevAppUnifiedID(cmd *cobra.Command) (string, error) { return appID, nil } +func requiredDevAppUnifiedIDWithLegacyAppID(cmd *cobra.Command) (string, error) { + appID := devAppStringFlag(cmd, "unified-app-id") + if appID == "" { + appID = devAppStringFlag(cmd, "app-id") + } + if appID == "" { + return "", apperrors.NewValidation("--unified-app-id is required") + } + return appID, nil +} + // --------------------------------------------------------------------------- // 事件订阅能力 // --------------------------------------------------------------------------- @@ -1189,7 +1206,7 @@ func devAppEventCodes(cmd *cobra.Command) []string { } func registerDevAppMemberMutationFlags(cmd *cobra.Command) { - cmd.Flags().String("app-id", "", "开放平台统一应用 ID (必填)") + addDevAppUnifiedIDFlagWithLegacyAppID(cmd) cmd.Flags().String("users", "", "成员 userId 列表,多个用逗号分隔 (必填)") cmd.Flags().String("member-type", "", "成员类型,如 DEVELOPER (必填)") } @@ -1198,7 +1215,7 @@ func runDevAppMemberMutation(runner executor.Runner, cmd *cobra.Command, tool, o if err := devAppRequireWriteGuard(cmd, operation); err != nil { return err } - appID, err := requiredDevAppID(cmd) + appID, err := requiredDevAppUnifiedIDWithLegacyAppID(cmd) if err != nil { return err } diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 2a5f9e3e..62c12149 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -45,7 +45,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "list", cmd: "list", - args: []string{"--app-id", "app-001"}, + args: []string{"--unified-app-id", "app-001"}, wantTool: "list_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", @@ -54,7 +54,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "add multiple users", cmd: "add", - args: []string{"--app-id", "app-001", "--users", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, + args: []string{"--unified-app-id", "app-001", "--users", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, wantTool: "add_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", @@ -65,7 +65,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "remove trims users", cmd: "remove", - args: []string{"--app-id", "app-001", "--users", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, + args: []string{"--unified-app-id", "app-001", "--users", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, wantTool: "remove_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", @@ -101,6 +101,43 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { } } +func TestDevAppMemberCommandsAcceptLegacyAppIDAlias(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"devapp", "member", "list", "--app-id", "app-001"}) + + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + + want := map[string]any{"unifiedAppId": "app-001"} + if !reflect.DeepEqual(runner.last.Params, want) { + t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) + } +} + +func TestDevAppMemberListHelpUsesUnifiedAppID(t *testing.T) { + root := newDevAppTestRoot(&captureRunner{}) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"devapp", "member", "list", "--help"}) + + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + help := out.String() + if !strings.Contains(help, "--unified-app-id") { + t.Fatalf("help missing --unified-app-id:\n%s", help) + } + if strings.Contains(help, "--app-id") { + t.Fatalf("help should hide legacy --app-id:\n%s", help) + } +} + func TestDevAppCommandHasAppAliasAndCoreCommands(t *testing.T) { root := newDevAppCommand(&captureRunner{}) if root.Name() != "devapp" { @@ -903,10 +940,10 @@ func TestDevAppMemberCommandsValidateRequiredFlags(t *testing.T) { args []string wantErr string }{ - {name: "list requires app", cmd: "list", args: nil, wantErr: "--app-id is required"}, - {name: "add requires users", cmd: "add", args: []string{"--app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users is required"}, - {name: "add rejects empty users", cmd: "add", args: []string{"--app-id", "app-001", "--users", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users must contain at least one userId"}, - {name: "remove requires member type", cmd: "remove", args: []string{"--app-id", "app-001", "--users", "userId1", "--dry-run"}, wantErr: "--member-type is required"}, + {name: "list requires app", cmd: "list", args: nil, wantErr: "--unified-app-id is required"}, + {name: "add requires users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users is required"}, + {name: "add rejects empty users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--users", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users must contain at least one userId"}, + {name: "remove requires member type", cmd: "remove", args: []string{"--unified-app-id", "app-001", "--users", "userId1", "--dry-run"}, wantErr: "--member-type is required"}, } for _, tc := range cases { @@ -940,7 +977,7 @@ func TestDevAppSecurityConfigBuildsOnlyProvidedLists(t *testing.T) { root.SetErr(&out) root.SetArgs([]string{ "devapp", "security", "config", - "--app-id", "app-001", + "--unified-app-id", "app-001", "--ip-whitelist", "192.0.2.10,192.0.2.11", "--redirect-url", "https://callback.example.invalid/callback", "--sso-url", "https://sso.example.invalid/sso", @@ -974,7 +1011,7 @@ func TestDevAppSecurityConfigOmitsAbsentOptionalLists(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "security", "config", "--app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback", "--dry-run"}) + root.SetArgs([]string{"devapp", "security", "config", "--unified-app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback", "--dry-run"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -995,7 +1032,7 @@ func TestDevAppSecurityConfigRequiresAtLeastOneConfig(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"devapp", "security", "config", "--app-id", "app-001", "--dry-run"}) + root.SetArgs([]string{"devapp", "security", "config", "--unified-app-id", "app-001", "--dry-run"}) err := root.Execute() if err == nil { @@ -1009,6 +1046,27 @@ func TestDevAppSecurityConfigRequiresAtLeastOneConfig(t *testing.T) { } } +func TestDevAppSecurityConfigAcceptsLegacyAppIDAlias(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"devapp", "security", "config", "--app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback", "--dry-run"}) + + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + + want := map[string]any{ + "unifiedAppId": "app-001", + "redirectUrls": []string{"https://callback.example.invalid/callback"}, + } + if !reflect.DeepEqual(runner.last.Params, want) { + t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) + } +} + func TestDevAppMemberAndSecurityRequireWriteGuard(t *testing.T) { cases := []struct { name string diff --git a/skills/dingtalk-devapp/references/member-security.md b/skills/dingtalk-devapp/references/member-security.md index 088520bd..75b671bc 100644 --- a/skills/dingtalk-devapp/references/member-security.md +++ b/skills/dingtalk-devapp/references/member-security.md @@ -3,7 +3,7 @@ ## 成员查询 ```bash -dws devapp member list --app-id UNIFIED_APP_ID --format json +dws devapp member list --unified-app-id UNIFIED_APP_ID --format json ``` MCP tool: `list_dev_app_members` @@ -11,8 +11,8 @@ MCP tool: `list_dev_app_members` ## 添加成员 ```bash -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json +dws devapp member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json +dws devapp member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json ``` MCP tool: `add_dev_app_members` @@ -20,30 +20,30 @@ MCP tool: `add_dev_app_members` ## 移除成员 ```bash -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json +dws devapp member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json +dws devapp member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json ``` MCP tool: `remove_dev_app_members` | CLI | MCP | 说明 | |-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | +| `--unified-app-id` | `unifiedAppId` | 统一应用 ID(必填) | | `--users` | `memberUserIds` | userId 列表,逗号分隔(必填) | | `--member-type` | `memberType` | 成员类型,如 `DEVELOPER`(必填) | ## 安全配置 ```bash -dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json -dws devapp security config --app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json +dws devapp security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json +dws devapp security config --unified-app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json ``` MCP tool: `update_dev_app_security_config` | CLI | MCP | 说明 | |-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | +| `--unified-app-id` | `unifiedAppId` | 统一应用 ID(必填) | | `--ip-whitelist` | `ipWhitelist` | 出口 IP 白名单,逗号/分号分隔 | | `--redirect-url` | `redirectUrls` | 登录重定向 URL,逗号/分号分隔 | | `--sso-url` | `ssoUrls` | 端内免登 URL,逗号/分号分隔 | diff --git a/skills/dingtalk-devapp/references/workflows.md b/skills/dingtalk-devapp/references/workflows.md index b07be648..5e876cfc 100644 --- a/skills/dingtalk-devapp/references/workflows.md +++ b/skills/dingtalk-devapp/references/workflows.md @@ -31,14 +31,14 @@ → 确认后加 --yes 7. 添加开发成员(按需) - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json + dws devapp member add --unified-app-id --users --member-type DEVELOPER --dry-run --format json → 确认后加 --yes 8. 验证成员 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json 9. 配置安全设置(按需) - dws devapp security config --app-id --ip-whitelist --redirect-url --dry-run --format json + dws devapp security config --unified-app-id --ip-whitelist --redirect-url --dry-run --format json → 确认后加 --yes ``` @@ -108,28 +108,28 @@ ```text 1. 查看当前成员 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json 2. 添加成员 - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json + dws devapp member add --unified-app-id --users --member-type DEVELOPER --dry-run --format json → 确认后加 --yes 3. 验证 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json 4. 移除成员 - dws devapp member remove --app-id --users --member-type DEVELOPER --dry-run --format json + dws devapp member remove --unified-app-id --users --member-type DEVELOPER --dry-run --format json → 确认后加 --yes 5. 验证 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json ``` ## 安全配置 ```text 1. 配置 IP 白名单 / 重定向 URL / 免登 URL - dws devapp security config --app-id \ + dws devapp security config --unified-app-id \ --ip-whitelist 192.0.2.10,192.0.2.11 \ --redirect-url https://callback.example.invalid/callback \ --sso-url https://sso.example.invalid/sso \ diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 5901e69e..0fe2f482 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -163,9 +163,9 @@ MCP tool: `remove_dev_app_permissions`。`--permissions` 接收一个或多个 ` ### 成员管理 ```bash -dws devapp member list --app-id UNIFIED_APP_ID --format json -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json +dws devapp member list --unified-app-id UNIFIED_APP_ID --format json +dws devapp member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json +dws devapp member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json ``` MCP tools: `list_dev_app_members` / `add_dev_app_members` / `remove_dev_app_members` @@ -173,7 +173,7 @@ MCP tools: `list_dev_app_members` / `add_dev_app_members` / `remove_dev_app_memb ### 安全配置 ```bash -dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --redirect-url https://callback.example.invalid/callback --dry-run --format json +dws devapp security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --redirect-url https://callback.example.invalid/callback --dry-run --format json ``` MCP tool: `update_dev_app_security_config` diff --git a/skills/multi/dingtalk-devapp/references/member-security.md b/skills/multi/dingtalk-devapp/references/member-security.md index 088520bd..75b671bc 100644 --- a/skills/multi/dingtalk-devapp/references/member-security.md +++ b/skills/multi/dingtalk-devapp/references/member-security.md @@ -3,7 +3,7 @@ ## 成员查询 ```bash -dws devapp member list --app-id UNIFIED_APP_ID --format json +dws devapp member list --unified-app-id UNIFIED_APP_ID --format json ``` MCP tool: `list_dev_app_members` @@ -11,8 +11,8 @@ MCP tool: `list_dev_app_members` ## 添加成员 ```bash -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json +dws devapp member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json +dws devapp member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json ``` MCP tool: `add_dev_app_members` @@ -20,30 +20,30 @@ MCP tool: `add_dev_app_members` ## 移除成员 ```bash -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json -dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json +dws devapp member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json +dws devapp member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json ``` MCP tool: `remove_dev_app_members` | CLI | MCP | 说明 | |-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | +| `--unified-app-id` | `unifiedAppId` | 统一应用 ID(必填) | | `--users` | `memberUserIds` | userId 列表,逗号分隔(必填) | | `--member-type` | `memberType` | 成员类型,如 `DEVELOPER`(必填) | ## 安全配置 ```bash -dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json -dws devapp security config --app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json +dws devapp security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json +dws devapp security config --unified-app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json ``` MCP tool: `update_dev_app_security_config` | CLI | MCP | 说明 | |-----|-----|------| -| `--app-id` | `unifiedAppId` | 统一应用 ID(必填) | +| `--unified-app-id` | `unifiedAppId` | 统一应用 ID(必填) | | `--ip-whitelist` | `ipWhitelist` | 出口 IP 白名单,逗号/分号分隔 | | `--redirect-url` | `redirectUrls` | 登录重定向 URL,逗号/分号分隔 | | `--sso-url` | `ssoUrls` | 端内免登 URL,逗号/分号分隔 | diff --git a/skills/multi/dingtalk-devapp/references/workflows.md b/skills/multi/dingtalk-devapp/references/workflows.md index 7f4adac2..909a86e7 100644 --- a/skills/multi/dingtalk-devapp/references/workflows.md +++ b/skills/multi/dingtalk-devapp/references/workflows.md @@ -31,14 +31,14 @@ → 确认后加 --yes 7. 添加开发成员(按需) - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json + dws devapp member add --unified-app-id --users --member-type DEVELOPER --dry-run --format json → 确认后加 --yes 8. 验证成员 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json 9. 配置安全设置(按需) - dws devapp security config --app-id --ip-whitelist --redirect-url --dry-run --format json + dws devapp security config --unified-app-id --ip-whitelist --redirect-url --dry-run --format json → 确认后加 --yes ``` @@ -108,28 +108,28 @@ ```text 1. 查看当前成员 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json 2. 添加成员 - dws devapp member add --app-id --users --member-type DEVELOPER --dry-run --format json + dws devapp member add --unified-app-id --users --member-type DEVELOPER --dry-run --format json → 确认后加 --yes 3. 验证 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json 4. 移除成员 - dws devapp member remove --app-id --users --member-type DEVELOPER --dry-run --format json + dws devapp member remove --unified-app-id --users --member-type DEVELOPER --dry-run --format json → 确认后加 --yes 5. 验证 - dws devapp member list --app-id --format json + dws devapp member list --unified-app-id --format json ``` ## 安全配置 ```text 1. 配置 IP 白名单 / 重定向 URL / 免登 URL - dws devapp security config --app-id \ + dws devapp security config --unified-app-id \ --ip-whitelist 192.0.2.10,192.0.2.11 \ --redirect-url https://callback.example.invalid/callback \ --sso-url https://sso.example.invalid/sso \ diff --git a/test/skill_tests.md b/test/skill_tests.md index f8aede2a..658f1640 100644 --- a/test/skill_tests.md +++ b/test/skill_tests.md @@ -897,29 +897,29 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 **devapp_devapp_member_list_001** - Prompt: 查看开放平台应用 UNIFIED_APP_ID 的成员 -- Expected: `dws devapp member list --app-id UNIFIED_APP_ID --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID` +- Expected: `dws devapp member list --unified-app-id UNIFIED_APP_ID --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID` #### `dws devapp member add` **devapp_devapp_member_add_001** - Prompt: 给开放平台应用 UNIFIED_APP_ID 添加开发者成员 userId1,userId2,先预览 -- Expected: `dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID`, `--users` = `userId1,userId2`, `--member-type` = `DEVELOPER` +- Expected: `dws devapp member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--users` = `userId1,userId2`, `--member-type` = `DEVELOPER` #### `dws devapp member remove` **devapp_devapp_member_remove_001** - Prompt: 已确认,从开放平台应用 UNIFIED_APP_ID 移除开发者成员 userId1 -- Expected: `dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID`, `--users` = `userId1`, `--member-type` = `DEVELOPER`, `--yes` +- Expected: `dws devapp member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--users` = `userId1`, `--member-type` = `DEVELOPER`, `--yes` #### `dws devapp security config` **devapp_devapp_security_config_001** - Prompt: 给开放平台应用 UNIFIED_APP_ID 配置 IP 白名单 192.0.2.10,先预览 -- Expected: `dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID`, `--ip-whitelist` = `192.0.2.10` +- Expected: `dws devapp security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--ip-whitelist` = `192.0.2.10` #### `dws devapp permission list` diff --git a/test/skill_tests_results.md b/test/skill_tests_results.md index 9330788e..c4060328 100644 --- a/test/skill_tests_results.md +++ b/test/skill_tests_results.md @@ -1194,7 +1194,7 @@ **devapp_devapp_member_list_001** ✅ PASS - Prompt: 查看开放平台应用 UNIFIED_APP_ID 的成员 -- Expected: `dws devapp member list --app-id UNIFIED_APP_ID --format json` +- Expected: `dws devapp member list --unified-app-id UNIFIED_APP_ID --format json` - Skill Reference: references/products/devapp.md - Command path: PASS (devapp member list) - Flags: PASS (1 flags validated) @@ -1202,7 +1202,7 @@ **devapp_devapp_member_add_001** ✅ PASS - Prompt: 给开放平台应用 UNIFIED_APP_ID 添加开发者成员 userId1,userId2,先预览 -- Expected: `dws devapp member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json` +- Expected: `dws devapp member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json` - Skill Reference: references/products/devapp.md - Command path: PASS (devapp member add) - Flags: PASS (3 flags validated) @@ -1210,7 +1210,7 @@ **devapp_devapp_member_remove_001** ✅ PASS - Prompt: 已确认,从开放平台应用 UNIFIED_APP_ID 移除开发者成员 userId1 -- Expected: `dws devapp member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json` +- Expected: `dws devapp member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json` - Skill Reference: references/products/devapp.md - Command path: PASS (devapp member remove) - Flags: PASS (4 flags validated) @@ -1218,7 +1218,7 @@ **devapp_devapp_security_config_001** ✅ PASS - Prompt: 给开放平台应用 UNIFIED_APP_ID 配置 IP 白名单 192.0.2.10,先预览 -- Expected: `dws devapp security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` +- Expected: `dws devapp security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` - Skill Reference: references/products/devapp.md - Command path: PASS (devapp security config) - Flags: PASS (2 flags validated) From 591609fdee27e6c40f9394d02ea77db9fa4a6df5 Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Wed, 17 Jun 2026 10:03:04 +0800 Subject: [PATCH 27/47] fix cr problem --- docs/devapp-yulan-command-routing.md | 14 ++++++------- internal/helpers/devapp.go | 8 ++++---- internal/helpers/devapp_test.go | 4 ++-- skills/dingtalk-devapp/references/event.md | 20 +++++++++---------- skills/mono/references/products/devapp.md | 8 ++++---- .../multi/dingtalk-devapp/references/event.md | 20 +++++++++---------- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index e6e4964f..7535e552 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -87,8 +87,8 @@ dws devapp permission add Apply permissions into current app version changes permission remove Remove authorized permissions event list List subscribable events and current subscription state - event subscribe Subscribe one event by eventCode - event unsubscribe Unsubscribe one event by eventCode + event subscribe Subscribe events by eventCodes + event unsubscribe Unsubscribe events by eventCodes robot create Create a new agent app + robot synchronously robot submit Submit async robot-create task (supports retry by taskId) robot result Query async robot-create task result @@ -948,14 +948,14 @@ Rules: Event and version commands are needed for a full application lifecycle but should not be folded into permission add. Implemented event tools (verified against the `op-app` MCP `tools/list`). The -real model is "list subscribable events + subscribe/unsubscribe one event by -`eventCode`", not a single callbackUrl/token/aesKey config call: +real model is "list subscribable events + subscribe/unsubscribe events by +`eventCodes`", not a single callbackUrl/token/aesKey config call: | CLI | MCP tool | Notes | | --- | --- | --- | | `devapp event list` | `list_dev_app_events` | Args `unifiedAppId`; returns `pushType` + `events[]` (`eventCode`/`eventName`/`subscribed`). Read. | -| `devapp event subscribe` | `subscribe_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write (`--dry-run`/`--yes`). | -| `devapp event unsubscribe` | `unsubscribe_dev_app_event` | Args `unifiedAppId` + `eventCode`. Write. | +| `devapp event subscribe` | `subscribe_dev_app_events` | Args `unifiedAppId` + `eventCodes`. Write (`--dry-run`/`--yes`). | +| `devapp event unsubscribe` | `unsubscribe_dev_app_events` | Args `unifiedAppId` + `eventCodes`. Write. | Note: for gray unified apps, subscribe/unsubscribe are staged into version metadata and only take effect after `version publish`. The event callback URL @@ -1086,7 +1086,7 @@ Yulan invocation examples: | "把应用图标换成 ICON_RESOURCE" | `dws devapp update --unified-app-id ... --icon ICON_RESOURCE --dry-run --format json` | Icon change is app base info update and must be guarded. | | "已确认删除这个开放平台应用" | `dws devapp delete --unified-app-id ... --yes --format json` | Destructive write only after unique app resolution and confirmation. | | "手机号权限要申请,虽然需要审核" | `dws devapp permission add --permissions Contact.User.mobile --dry-run/--yes` | `requiredApproval=true` is still appliable; approval moves to version publish. | -| "订阅通讯录用户增加事件" | `dws devapp event list ...` then `dws devapp event subscribe --event-code user_add_org --dry-run --format json` | Pick eventCode from list, then subscribe; gray apps need a version publish to take effect. | +| "订阅通讯录用户增加事件" | `dws devapp event list ...` then `dws devapp event subscribe --event-codes user_add_org --dry-run --format json` | Pick eventCode from list, then subscribe; gray apps need a version publish to take effect. | | "保存版本并看审核状态" | `version create`, `check-approval`, `publish`, `status` | Version flow owns approver selection and publish audit state. | | "创建 MCP 服务并配置 HSF tool" | OpenDev MCP platform workflow, not `dws devapp create` | MCP connector/tool is a platform artifact, not an enterprise internal app. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 13539224..47954a48 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -60,8 +60,8 @@ const ( // 事件订阅能力(op-app MCP 工具,硬编码不走服务发现)。 devAppEventListTool = "list_dev_app_events" - devAppEventSubscribeTool = "subscribe_dev_app_event" - devAppEventUnsubscribeTool = "unsubscribe_dev_app_event" + devAppEventSubscribeTool = "subscribe_dev_app_events" + devAppEventUnsubscribeTool = "unsubscribe_dev_app_events" ) func init() { @@ -217,8 +217,8 @@ func newDevAppCommand(runner executor.Runner) *cobra.Command { } event.AddCommand( newDevAppEventListCommand(runner), - newDevAppEventSubscribeCommand(runner, "subscribe", "订阅单个事件", devAppEventSubscribeTool), - newDevAppEventSubscribeCommand(runner, "unsubscribe", "退订单个事件", devAppEventUnsubscribeTool), + newDevAppEventSubscribeCommand(runner, "subscribe", "批量订阅事件", devAppEventSubscribeTool), + newDevAppEventSubscribeCommand(runner, "unsubscribe", "批量退订事件", devAppEventUnsubscribeTool), ) root.AddCommand( diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 2a5f9e3e..45eb56ec 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -320,13 +320,13 @@ func TestDevAppEventCommandsBuildToolParams(t *testing.T) { { name: "subscribe", args: []string{"event", "subscribe", "--unified-app-id", "u-1", "--event-codes", "user_add_org,org_dept_modify", "--yes"}, - wantTool: "subscribe_dev_app_event", + wantTool: "subscribe_dev_app_events", wantParams: map[string]any{"unifiedAppId": "u-1", "eventCodes": []string{"user_add_org", "org_dept_modify"}}, }, { name: "unsubscribe", args: []string{"event", "unsubscribe", "--unified-app-id", "u-1", "--event-codes", "user_add_org", "--yes"}, - wantTool: "unsubscribe_dev_app_event", + wantTool: "unsubscribe_dev_app_events", wantParams: map[string]any{"unifiedAppId": "u-1", "eventCodes": []string{"user_add_org"}}, }, } diff --git a/skills/dingtalk-devapp/references/event.md b/skills/dingtalk-devapp/references/event.md index 634f193f..67174586 100644 --- a/skills/dingtalk-devapp/references/event.md +++ b/skills/dingtalk-devapp/references/event.md @@ -1,6 +1,6 @@ # 事件订阅 -管理开放平台企业内部应用的事件订阅:列出可订阅事件、按事件码订阅/退订。 +管理开放平台企业内部应用的事件订阅:列出可订阅事件、按事件码列表批量订阅/退订。 > 所有命令通过 `--unified-app-id` 定位应用;`corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。订阅/退订是写操作,先 `--dry-run`,确认后再 `--yes`。 @@ -23,27 +23,27 @@ MCP tool: `list_dev_app_events`。返回 `pushType`(当前推送类型)和 ` ## 订阅事件 ```bash -dws devapp event subscribe --unified-app-id --event-code user_add_org --dry-run --format json -dws devapp event subscribe --unified-app-id --event-code user_add_org --yes --format json +dws devapp event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json +dws devapp event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json ``` -MCP tool: `subscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 +MCP tool: `subscribe_dev_app_events`(入参 `unifiedAppId` + `eventCodes`,一次订阅多个事件码)。 ## 退订事件 ```bash -dws devapp event unsubscribe --unified-app-id --event-code user_add_org --dry-run --format json -dws devapp event unsubscribe --unified-app-id --event-code user_add_org --yes --format json +dws devapp event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json +dws devapp event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json ``` -MCP tool: `unsubscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 +MCP tool: `unsubscribe_dev_app_events`(入参 `unifiedAppId` + `eventCodes`,一次退订多个事件码)。 ## 字段映射 | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| | `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID | -| `--event-code` | `eventCode` | subscribe/unsubscribe 必填 | 事件码,取自 `event list` | +| `--event-codes` | `eventCodes` | subscribe/unsubscribe 必填 | 事件码列表,逗号分隔,取自 `event list` | ## 灰度应用需发布版本生效(重要) @@ -67,7 +67,7 @@ event subscribe/unsubscribe 订阅变更写入版本元数据 → 从返回里挑 eventCode 2. 订阅 - dws devapp event subscribe --unified-app-id --event-code --dry-run --format json + dws devapp event subscribe --unified-app-id --event-codes , --dry-run --format json → 确认后加 --yes 3. 验证 @@ -79,6 +79,6 @@ event subscribe/unsubscribe 订阅变更写入版本元数据 | 情况 | 处理 | |------|------| -| `--event-code` 缺失 | CLI 直接报错,先 `event list` 取事件码 | +| `--event-codes` 缺失 | CLI 直接报错,先 `event list` 取事件码 | | 订阅后 `subscribed` 仍为 false | 灰度应用需先 `version publish` 发布版本 | | `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 5901e69e..068b2a20 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -321,13 +321,13 @@ MCP tools: `create_dev_app_version` / `list_dev_app_versions` / `get_dev_app_ver ```bash dws devapp event list --unified-app-id ID --format json -dws devapp event subscribe --unified-app-id ID --event-code user_add_org --dry-run --format json -dws devapp event unsubscribe --unified-app-id ID --event-code user_add_org --dry-run --format json +dws devapp event subscribe --unified-app-id ID --event-codes user_add_org,org_dept_modify --dry-run --format json +dws devapp event unsubscribe --unified-app-id ID --event-codes user_add_org,org_dept_modify --dry-run --format json ``` -MCP tools: `list_dev_app_events` / `subscribe_dev_app_event` / `unsubscribe_dev_app_event`。 +MCP tools: `list_dev_app_events` / `subscribe_dev_app_events` / `unsubscribe_dev_app_events`。 -- `event list` 返回 `pushType` 与 `events[]`(每项 `eventCode`/`eventName`/`subscribed`);订阅/退订只接受 `--event-code`(取自 list 返回),一次一个。 +- `event list` 返回 `pushType` 与 `events[]`(每项 `eventCode`/`eventName`/`subscribed`);订阅/退订接受 `--event-codes` 逗号分隔列表(取自 list 返回),一次可传多个。 - 写操作走 `--dry-run`/`--yes` 写保护;`list` 是只读。 - **灰度统一应用**:`subscribe`/`unsubscribe` 只把变更暂存到版本元数据,需后续 `version create → publish` 发布版本后才生效。订阅后 `event list` 若仍 `subscribed=false`,先发布版本再回读。 - 回调地址不在此模型内:消息/事件回调地址走机器人配置(`robot config --outgoing-url/--event-url`)。 diff --git a/skills/multi/dingtalk-devapp/references/event.md b/skills/multi/dingtalk-devapp/references/event.md index 634f193f..67174586 100644 --- a/skills/multi/dingtalk-devapp/references/event.md +++ b/skills/multi/dingtalk-devapp/references/event.md @@ -1,6 +1,6 @@ # 事件订阅 -管理开放平台企业内部应用的事件订阅:列出可订阅事件、按事件码订阅/退订。 +管理开放平台企业内部应用的事件订阅:列出可订阅事件、按事件码列表批量订阅/退订。 > 所有命令通过 `--unified-app-id` 定位应用;`corpId` / `userId` 由 MCP 系统上下文注入,CLI 不传。订阅/退订是写操作,先 `--dry-run`,确认后再 `--yes`。 @@ -23,27 +23,27 @@ MCP tool: `list_dev_app_events`。返回 `pushType`(当前推送类型)和 ` ## 订阅事件 ```bash -dws devapp event subscribe --unified-app-id --event-code user_add_org --dry-run --format json -dws devapp event subscribe --unified-app-id --event-code user_add_org --yes --format json +dws devapp event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json +dws devapp event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json ``` -MCP tool: `subscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`,一次订阅一个)。 +MCP tool: `subscribe_dev_app_events`(入参 `unifiedAppId` + `eventCodes`,一次订阅多个事件码)。 ## 退订事件 ```bash -dws devapp event unsubscribe --unified-app-id --event-code user_add_org --dry-run --format json -dws devapp event unsubscribe --unified-app-id --event-code user_add_org --yes --format json +dws devapp event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json +dws devapp event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json ``` -MCP tool: `unsubscribe_dev_app_event`(入参 `unifiedAppId` + `eventCode`)。 +MCP tool: `unsubscribe_dev_app_events`(入参 `unifiedAppId` + `eventCodes`,一次退订多个事件码)。 ## 字段映射 | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| | `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID | -| `--event-code` | `eventCode` | subscribe/unsubscribe 必填 | 事件码,取自 `event list` | +| `--event-codes` | `eventCodes` | subscribe/unsubscribe 必填 | 事件码列表,逗号分隔,取自 `event list` | ## 灰度应用需发布版本生效(重要) @@ -67,7 +67,7 @@ event subscribe/unsubscribe 订阅变更写入版本元数据 → 从返回里挑 eventCode 2. 订阅 - dws devapp event subscribe --unified-app-id --event-code --dry-run --format json + dws devapp event subscribe --unified-app-id --event-codes , --dry-run --format json → 确认后加 --yes 3. 验证 @@ -79,6 +79,6 @@ event subscribe/unsubscribe 订阅变更写入版本元数据 | 情况 | 处理 | |------|------| -| `--event-code` 缺失 | CLI 直接报错,先 `event list` 取事件码 | +| `--event-codes` 缺失 | CLI 直接报错,先 `event list` 取事件码 | | 订阅后 `subscribed` 仍为 false | 灰度应用需先 `version publish` 发布版本 | | `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | From 8984f7b8be24d866e2f6ff854ee39663702cbd55 Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Wed, 17 Jun 2026 12:13:28 +0800 Subject: [PATCH 28/47] fix cr problem --- docs/devapp-agent-install-guide.md | 34 ++++++++--------- docs/devapp-yulan-command-routing.md | 4 +- skills/dingtalk-devapp/references/robot.md | 20 +++++----- skills/dingtalk-devapp/references/version.md | 2 +- skills/mono/references/products/devapp.md | 6 +-- .../multi/dingtalk-devapp/references/robot.md | 20 +++++----- .../dingtalk-devapp/references/version.md | 2 +- test/skill_tests_results.md | 38 ++++--------------- 8 files changed, 51 insertions(+), 75 deletions(-) diff --git a/docs/devapp-agent-install-guide.md b/docs/devapp-agent-install-guide.md index d41a19fd..f75bdf00 100644 --- a/docs/devapp-agent-install-guide.md +++ b/docs/devapp-agent-install-guide.md @@ -132,7 +132,7 @@ dws devapp robot config \ --unified-app-id \ --name "告警机器人" \ --brief "告警通知" \ - --description "处理告警通知和事件回调" \ + --desc "处理告警通知和事件回调" \ --dry-run \ --format json ``` @@ -144,7 +144,7 @@ dws devapp robot config \ --unified-app-id \ --name "告警机器人" \ --brief "告警通知" \ - --description "处理告警通知和事件回调" \ + --desc "处理告警通知和事件回调" \ --yes \ --format json ``` @@ -161,18 +161,18 @@ dws devapp robot get --unified-app-id --format json ```bash dws devapp list --format json -dws devapp get --agent-id --format json -dws devapp create --app-name "考勤应用" --dry-run --format json -dws devapp update --agent-id --app-name "新应用名" --dry-run --format json -dws devapp inactive --agent-id --dry-run --format json -dws devapp active --agent-id --dry-run --format json -dws devapp delete --agent-id --dry-run --format json +dws devapp get --unified-app-id --format json +dws devapp create --name "考勤应用" --dry-run --format json +dws devapp update --unified-app-id --name "新应用名" --dry-run --format json +dws devapp inactive --unified-app-id --dry-run --format json +dws devapp active --unified-app-id --dry-run --format json +dws devapp delete --unified-app-id --dry-run --format json ``` ### 凭证查询 ```bash -dws devapp credentials get --agent-id --format json +dws devapp credentials get --unified-app-id --format json ``` 凭证输出可能包含敏感字段,不要把完整结果写入文档、日志或长期记忆。 @@ -180,9 +180,9 @@ dws devapp credentials get --agent-id --format json ### 权限点管理 ```bash -dws devapp permission list --agent-id --format json -dws devapp permission add --agent-id --scope-values Contact.User.mobile --dry-run --format json -dws devapp permission remove --agent-id --scope-value Contact.User.mobile --dry-run --format json +dws devapp permission list --unified-app-id --format json +dws devapp permission add --unified-app-id --permissions Contact.User.mobile --dry-run --format json +dws devapp permission remove --unified-app-id --permissions Contact.User.mobile --dry-run --format json ``` 权限申请和移除只使用 `scopeValue`,不要传 API 名或权限分组名。 @@ -199,11 +199,11 @@ dws devapp robot disable --unified-app-id --dry-run --format json ### 成员与安全 ```bash -dws devapp member list --agent-id --format json -dws devapp member add --agent-id --user-ids --dry-run --format json -dws devapp member remove --agent-id --user-ids --dry-run --format json -dws devapp security config --unified-app-id --redirect-urls --dry-run --format json -dws devapp security config --unified-app-id --ip-white-list --dry-run --format json +dws devapp member list --unified-app-id --format json +dws devapp member add --unified-app-id --users --dry-run --format json +dws devapp member remove --unified-app-id --users --dry-run --format json +dws devapp security config --unified-app-id --redirect-url --dry-run --format json +dws devapp security config --unified-app-id --ip-whitelist --dry-run --format json ``` ### 版本发布 diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 7535e552..fd1b802c 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -959,14 +959,14 @@ real model is "list subscribable events + subscribe/unsubscribe events by Note: for gray unified apps, subscribe/unsubscribe are staged into version metadata and only take effect after `version publish`. The event callback URL -itself is not part of this model; it lives in robot config (`chatBotEventUrl`, +itself is not part of this model; it lives in robot config (`eventCallbackUrl`, exposed as `robot config --event-url`). Implemented version tools (verified against the `op-app` MCP `tools/list`): | CLI | MCP tool | Notes | | --- | --- | --- | -| `devapp version create` | `create_dev_app_version` | Save a new version from current config (`version`, `description`). | +| `devapp version create` | `create_dev_app_version` | Save a new version from current config (`version`, `desc`). | | `devapp version list` | `list_dev_app_versions` | Cursor-paged list (`cursor`, `pageSize`; response `items/nextCursor/hasMore`). | | `devapp version get` | `get_dev_app_version_detail` | One version detail by `versionId`. | | `devapp version check-approval` | `publish_dev_app_version` (`precheckOnly=true`) | Precheck approval requirement / approvers; does not publish. | diff --git a/skills/dingtalk-devapp/references/robot.md b/skills/dingtalk-devapp/references/robot.md index d4e0946a..8b878784 100644 --- a/skills/dingtalk-devapp/references/robot.md +++ b/skills/dingtalk-devapp/references/robot.md @@ -12,15 +12,15 @@ ### 同步创建 ```bash -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json +dws devapp robot create --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws devapp robot create --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json ``` MCP tool: `create_dingtalk_robot`。成功返回 `agentId / robotCode / clientId / clientSecret`(凭据按敏感信息处理)。 | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| -| `--app-name` | `appName` | 是 | 智能体应用名称,长度 2-20,企业内唯一 | +| `--name` | `name` | 是 | 智能体应用名称,长度 2-20,企业内唯一 | | `--robot-name` | `robotName` | 是 | 承载机器人名称,客户端展示 | | `--desc` | `desc` | 是 | 机器人功能描述,≤200 字 | | `--icon` | `robotMediaId` | 否 | 图标 mediaId;空则用默认图标 | @@ -32,8 +32,8 @@ MCP tool: `create_dingtalk_robot`。成功返回 `agentId / robotCode / clientId ```bash # 提交任务 -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json +dws devapp robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws devapp robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json # → 返回 taskId # 轮询结果 @@ -82,7 +82,7 @@ MCP tool: `get_extension_robot_config`。返回机器人基础信息、回调地 ```bash dws devapp robot config --unified-app-id --name 小助手 --brief 审批助手 \ - --description "处理审批相关问答" --outgoing-url https://example.com/msg \ + --desc "处理审批相关问答" --outgoing-url https://example.com/msg \ --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json dws devapp robot enable --unified-app-id --dry-run --format json @@ -94,13 +94,13 @@ MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` |-----|-----|------| | `--name` | `name` | 机器人名称 | | `--brief` | `brief` | 简介 | -| `--description` | `description` | 描述 | +| `--desc` | `desc` | 描述 | | `--icon` | `iconMediaId` | 图标 mediaId | | `--outgoing-url` | `outgoingUrl` | 消息回调地址 | -| `--event-url` | `chatBotEventUrl` | 事件回调地址 | +| `--event-url` | `eventCallbackUrl` | 事件回调地址 | | `--mode` | `mode` | 机器人模式枚举(整数) | -| `--skills` | `skillList` | 技能列表,逗号分隔 | -| `--add-scope` | `isAddScope` | 自动添加机器人相关权限 | +| `--skills` | `skills` | 技能列表,逗号分隔 | +| `--add-scope` | `addScope` | 自动添加机器人相关权限 | | `--disable-ssl-verify` | `disableSSLVerify` | 回调关闭 SSL 校验 | | `--i18n-name` | `i18nName` | 名称国际化 JSON,如 `'{"en_US":"Bot"}'` | | `--i18n-brief` | `i18nBrief` | 简介国际化 JSON | diff --git a/skills/dingtalk-devapp/references/version.md b/skills/dingtalk-devapp/references/version.md index 6426998d..adee28fa 100644 --- a/skills/dingtalk-devapp/references/version.md +++ b/skills/dingtalk-devapp/references/version.md @@ -26,7 +26,7 @@ MCP tool: `create_dev_app_version` | CLI | MCP | 说明 | |-----|-----|------| | `--version` | `version` | 版本号,如 1.0.1 | -| `--desc` | `description` | 版本描述 | +| `--desc` | `desc` | 版本描述 | ## 版本列表 diff --git a/skills/mono/references/products/devapp.md b/skills/mono/references/products/devapp.md index 5abdfb57..fd64eed8 100644 --- a/skills/mono/references/products/devapp.md +++ b/skills/mono/references/products/devapp.md @@ -186,10 +186,10 @@ MCP tool: `update_dev_app_security_config` ```bash # 同步创建(返回 agentId/robotCode/clientId/clientSecret) -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws devapp robot create --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json # 异步创建 + 查询 -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws devapp robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json dws devapp robot result --task-id TASK_ID --format json ``` @@ -216,7 +216,7 @@ dws devapp robot disable --unified-app-id ID --dry-run --format json MCP tools: `get_extension_robot_config` / `set_extension_robot_config` / `enable_dev_app_robot` / `disable_dev_app_robot`。 -配置字段:`--name/--brief/--description/--icon/--outgoing-url(outgoingUrl)/--event-url(chatBotEventUrl)/--mode/--skills(skillList)/--add-scope/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。`config` 至少提供一个配置字段;`enable` 恢复已有机器人时可仅提供 `--unified-app-id`。应用未配机器人时 `get` 返回 `robot info is not exist`。 +配置字段:`--name/--brief/--desc/--icon/--outgoing-url(outgoingUrl)/--event-url(eventCallbackUrl)/--mode/--skills(skills)/--add-scope(addScope)/--disable-ssl-verify/--i18n-name/--i18n-brief/--i18n-description`。`config` 至少提供一个配置字段;`enable` 恢复已有机器人时可仅提供 `--unified-app-id`。应用未配机器人时 `get` 返回 `robot info is not exist`。 状态判断: diff --git a/skills/multi/dingtalk-devapp/references/robot.md b/skills/multi/dingtalk-devapp/references/robot.md index d4e0946a..8b878784 100644 --- a/skills/multi/dingtalk-devapp/references/robot.md +++ b/skills/multi/dingtalk-devapp/references/robot.md @@ -12,15 +12,15 @@ ### 同步创建 ```bash -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot create --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json +dws devapp robot create --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws devapp robot create --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json ``` MCP tool: `create_dingtalk_robot`。成功返回 `agentId / robotCode / clientId / clientSecret`(凭据按敏感信息处理)。 | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| -| `--app-name` | `appName` | 是 | 智能体应用名称,长度 2-20,企业内唯一 | +| `--name` | `name` | 是 | 智能体应用名称,长度 2-20,企业内唯一 | | `--robot-name` | `robotName` | 是 | 承载机器人名称,客户端展示 | | `--desc` | `desc` | 是 | 机器人功能描述,≤200 字 | | `--icon` | `robotMediaId` | 否 | 图标 mediaId;空则用默认图标 | @@ -32,8 +32,8 @@ MCP tool: `create_dingtalk_robot`。成功返回 `agentId / robotCode / clientId ```bash # 提交任务 -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws devapp robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json +dws devapp robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws devapp robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json # → 返回 taskId # 轮询结果 @@ -82,7 +82,7 @@ MCP tool: `get_extension_robot_config`。返回机器人基础信息、回调地 ```bash dws devapp robot config --unified-app-id --name 小助手 --brief 审批助手 \ - --description "处理审批相关问答" --outgoing-url https://example.com/msg \ + --desc "处理审批相关问答" --outgoing-url https://example.com/msg \ --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json dws devapp robot enable --unified-app-id --dry-run --format json @@ -94,13 +94,13 @@ MCP tools: `set_extension_robot_config` / `enable_dev_app_robot` |-----|-----|------| | `--name` | `name` | 机器人名称 | | `--brief` | `brief` | 简介 | -| `--description` | `description` | 描述 | +| `--desc` | `desc` | 描述 | | `--icon` | `iconMediaId` | 图标 mediaId | | `--outgoing-url` | `outgoingUrl` | 消息回调地址 | -| `--event-url` | `chatBotEventUrl` | 事件回调地址 | +| `--event-url` | `eventCallbackUrl` | 事件回调地址 | | `--mode` | `mode` | 机器人模式枚举(整数) | -| `--skills` | `skillList` | 技能列表,逗号分隔 | -| `--add-scope` | `isAddScope` | 自动添加机器人相关权限 | +| `--skills` | `skills` | 技能列表,逗号分隔 | +| `--add-scope` | `addScope` | 自动添加机器人相关权限 | | `--disable-ssl-verify` | `disableSSLVerify` | 回调关闭 SSL 校验 | | `--i18n-name` | `i18nName` | 名称国际化 JSON,如 `'{"en_US":"Bot"}'` | | `--i18n-brief` | `i18nBrief` | 简介国际化 JSON | diff --git a/skills/multi/dingtalk-devapp/references/version.md b/skills/multi/dingtalk-devapp/references/version.md index 6426998d..adee28fa 100644 --- a/skills/multi/dingtalk-devapp/references/version.md +++ b/skills/multi/dingtalk-devapp/references/version.md @@ -26,7 +26,7 @@ MCP tool: `create_dev_app_version` | CLI | MCP | 说明 | |-----|-----|------| | `--version` | `version` | 版本号,如 1.0.1 | -| `--desc` | `description` | 版本描述 | +| `--desc` | `desc` | 版本描述 | ## 版本列表 diff --git a/test/skill_tests_results.md b/test/skill_tests_results.md index c4060328..e58eb470 100644 --- a/test/skill_tests_results.md +++ b/test/skill_tests_results.md @@ -2,8 +2,8 @@ ## Summary -- **Total Test Cases**: 235 -- **Passed**: 235 +- **Total Test Cases**: 232 +- **Passed**: 232 - **Failed**: 0 - **Pass Rate**: 100.0% @@ -16,7 +16,7 @@ | calendar | 30 | 30 | 0 | 100.0% | | chat | 31 | 31 | 0 | 100.0% | | contact | 14 | 14 | 0 | 100.0% | -| devapp | 25 | 25 | 0 | 100.0% | +| devapp | 22 | 22 | 0 | 100.0% | | devdoc | 8 | 8 | 0 | 100.0% | | ding | 5 | 5 | 0 | 100.0% | | report | 26 | 26 | 0 | 100.0% | @@ -1105,11 +1105,11 @@ **devapp_devapp_list_002** ✅ PASS -- Prompt: 分页查询开放平台应用,第 2 页,每页 10 条,按修改时间倒序 -- Expected: `dws devapp list --page 2 --page-size 10 --sort gmt_modified --order desc --format json` +- Prompt: 分页查询开放平台应用,使用上一页返回的 cursor,每页 10 条 +- Expected: `dws devapp list --cursor NEXT_CURSOR --page-size 10 --format json` - Skill Reference: references/products/devapp.md - Command path: PASS (devapp list) -- Flags: PASS (4 flags validated) +- Flags: PASS (2 flags validated) **devapp_devapp_list_003** ✅ PASS @@ -1127,22 +1127,6 @@ - Command path: PASS (devapp get) - Flags: PASS (1 flags validated) -**devapp_devapp_get_002** ✅ PASS - -- Prompt: agentId 为 123456 的开放平台应用是哪个 -- Expected: `dws devapp get --agent-id 123456 --format json` -- Skill Reference: references/products/devapp.md -- Command path: PASS (devapp get) -- Flags: PASS (1 flags validated) - -**devapp_devapp_get_003** ✅ PASS - -- Prompt: 查开放平台应用 DemoApp 的 appKey、clientId 和 agentId -- Expected: `dws devapp get --name DemoApp --format json` -- Skill Reference: references/products/devapp.md -- Command path: PASS (devapp get) -- Flags: PASS (1 flags validated) - **devapp_devapp_create_001** ✅ PASS - Prompt: 创建一个企业内部应用,名称 DemoApp,描述是内部应用 @@ -1183,14 +1167,6 @@ - Command path: PASS (devapp credentials get) - Flags: PASS (1 flags validated) -**devapp_devapp_credentials_get_002** ✅ PASS - -- Prompt: 查询 AgentId AGENT_ID 对应应用的开放平台凭证 -- Expected: `dws devapp credentials get --agent-id AGENT_ID --format json` -- Skill Reference: references/products/devapp.md -- Command path: PASS (devapp credentials get) -- Flags: PASS (1 flags validated) - **devapp_devapp_member_list_001** ✅ PASS - Prompt: 查看开放平台应用 UNIFIED_APP_ID 的成员 @@ -1274,7 +1250,7 @@ **devapp_devapp_permission_remove_001** ✅ PASS - Prompt: 从应用 UNIFIED_APP_ID 移除 qyapi_robot_sendmsg 权限,先预览 -- Expected: `dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permission qyapi_robot_sendmsg --dry-run --format json` +- Expected: `dws devapp permission remove --unified-app-id UNIFIED_APP_ID --permissions qyapi_robot_sendmsg --dry-run --format json` - Skill Reference: references/products/devapp.md - Command path: PASS (devapp permission remove) - Flags: PASS (2 flags validated) From 6244222adf5c678f6a0c42a180292ba6e5a6d33d Mon Sep 17 00:00:00 2001 From: qinze Date: Wed, 17 Jun 2026 16:43:19 +0800 Subject: [PATCH 29/47] =?UTF-8?q?feat(dev):=20schema=20=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E6=8B=89=E5=8F=96=20MCP=20+=20flag=20=E5=85=A8=E9=9D=A2?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=20MCP=20=E5=8F=82=E6=95=B0=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 dws schema dev.* 实时从 op-app tools/list 拉取参数(gws 扁平格式), description/type/required/default 全部 verbatim 来自服务端,零本地合成 - flag 全部改为 kebab(MCP 参数名),去掉别名,schema 展示与 CLI flag 完全一致, 修掉 schema 显示假参数名的问题 - CLI 同步新 MCP 契约(工具名复数化、参数名 rename) - dingtalk-dev skill 文档同步新 flag 名 Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 4 + docs/dev-agent-test-cases.md | 137 +++++++ internal/app/helper_tool_fetcher.go | 106 ++++++ internal/app/root.go | 2 +- internal/cli/canonical.go | 44 ++- internal/cli/canonical_test.go | 10 +- internal/cli/dev_schema.go | 337 ++++++++++++++++++ internal/cli/dev_schema_test.go | 254 +++++++++++++ internal/helpers/devapp.go | 247 ++++++------- internal/helpers/devapp_test.go | 92 +++-- skills/multi/dingtalk-dev/SKILL.md | 12 +- skills/multi/dingtalk-dev/references/app.md | 10 +- skills/multi/dingtalk-dev/references/event.md | 13 +- .../multi/dingtalk-dev/references/member.md | 10 +- .../dingtalk-dev/references/permission.md | 24 +- skills/multi/dingtalk-dev/references/robot.md | 12 +- .../multi/dingtalk-dev/references/security.md | 6 +- .../multi/dingtalk-dev/references/version.md | 18 +- 18 files changed, 1123 insertions(+), 215 deletions(-) create mode 100644 docs/dev-agent-test-cases.md create mode 100644 internal/app/helper_tool_fetcher.go create mode 100644 internal/cli/dev_schema.go create mode 100644 internal/cli/dev_schema_test.go diff --git a/.gitignore b/.gitignore index 1cc7a5db..ae035cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,7 @@ dws.zip # local/pre-release MCP service configs may contain personal gateway keys /docs/mcp/serviceconfig-pre* + +# 功能测试运行产物 +results.jsonl +test/dev_functional/results.jsonl diff --git a/docs/dev-agent-test-cases.md b/docs/dev-agent-test-cases.md new file mode 100644 index 00000000..68cf3869 --- /dev/null +++ b/docs/dev-agent-test-cases.md @@ -0,0 +1,137 @@ +# dws dev 命令集 · Agent 人肉手工评测集(10 条复合用例) + +> 性质:**人肉手工评测集**——由测评人逐条手工跑、肉眼核对、人工判分,不是自动化脚本。 +> 用途:评测 agent(加载 `dingtalk-dev` 技能后)能否正确处理开放平台 dev 任务。 +> 特点:10 条**复合用例**,每条串多个子任务,一条覆盖一类完整场景;10 条合起来覆盖全部 34 个子命令 + 8 类横切行为。 +> 约定:所有命令应带 `--format json`;写操作应先 `--dry-run` 预览、用户确认后再 `--yes`;应用定位只用 `--unified-app-id`。 + +## 手工评测流程 + +逐条执行,每条三步: + +1. **发起**:在一个干净的 agent 会话里,把该条的「用户说」原样发给 agent(不给额外提示)。 +2. **观察**:看 agent 选了哪些命令、什么 flag、做了哪些判断/追问。 +3. **判分**:对照「通过判据」人工打分。复合用例含多个判据,**全部满足才记 PASS**;部分满足记 PASS\*(半通过)并在备注写清缺哪条。记一行 `用例# | PASS / PASS* / FAIL | 备注(错在哪)`。 + +> 「易错点」是常见扣分项,重点盯。建议每次技能改动后整套重跑,对比上次。 + +## 覆盖矩阵 + +| 用例 | 覆盖的子命令 | 横切行为 | +|------|-------------|---------| +| C1 建应用配齐基础 | app create / get / credentials get / update | dry-run/yes、定位符、密钥脱敏 | +| C2 列表与定位 | app list | cursor 分页、按名定位、多命中候选 | +| C3 生命周期 | app disable / enable / delete | 写后回读、appStatus、pretty 标签、confirm-name 防误删 | +| C4 网页应用到生效 | webapp get / config | 生效模型(改配置≠生效) | +| C5 版本发布全流程 | version create / list / get / check-approval / publish / status | 生效模型、审批人由用户拍板 | +| C6 权限全流程 | permission list / add / remove | 过滤分页、生效模型、批量聚合出参 | +| C7 成员与安全 | member list / add / remove、security config | 整组覆盖语义 | +| C8 机器人与建联 | robot submit / result / get / config / enable / disable、dev connect | 异步轮询、robot info not exist、建联依赖预检、长驻进程 | +| C9 事件与文档排查 | event list / subscribe / unsubscribe、dev doc search | 错误码透传、文档 RAG | +| C10 意图消歧 | (不进 dev,先澄清) | 泛词边界、转其它技能出口 | + +--- + +## 用例 + +### C1. 新建应用并配齐基础 +- **用户说**:「建一个内部应用叫 DemoApp,描述『内部测试』;建好后给我看看它的详情,把它的 AppKey/AppSecret 也取出来;对了名字再改成 DemoApp2。」 +- **覆盖**:`app create` / `get` / `credentials get` / `update`;dry-run/yes、定位符、密钥脱敏。 +- **期望(分步)**: + 1. `app create --name DemoApp --desc 内部测试 --dry-run` → 给用户看 `invocation.params` 确认 → `--yes`,记下返回的 `unifiedAppId`。 + 2. `app get --unified-app-id --format json` 看详情。 + 3. `credentials get --unified-app-id --format json` 取凭证。 + 4. `app update --unified-app-id --name DemoApp2 --dry-run` → `--yes`。 +- **通过判据**:每个写操作先 dry-run 再 yes;全程用 `unifiedAppId` 定位;取凭证走 `credentials get`(不是 app get);`clientSecret/appSecret` 按敏感处理、不明文写进回答。 +- **易错点**:不 dry-run 直接 yes;把 secret 打印给用户;用 `app get` 当取凭证。 + +### C2. 应用列表与按名定位 +- **用户说**:「列出我们企业的开放平台应用,一页 20 条,有下一页继续翻;再帮我找名字叫『早晚会』的那个应用,看它详情。」 +- **覆盖**:`app list`;cursor 分页、按名定位、多命中。 +- **期望(分步)**: + 1. `app list --page-size 20 --format json`;出参有 `nextCursor` 则续翻 `--cursor <上次 nextCursor>` 直到为空。 + 2. `app list --name 早晚会 --format json` 找 `unifiedAppId` → 唯一命中后 `app get --unified-app-id `。 +- **通过判据**:首次不传 `--cursor`,续翻原样回传 `nextCursor`,不自己构造/解析、不跨命令复用;用 list 过滤拿 id 再 get;多条命中时展示候选让用户选、不取第一条。 +- **易错点**:用 `--page/--offset` 翻页;`app get --name xxx`(get 不接受 name 定位)。 + +### C3. 应用生命周期(停用 / 启用 / 删除) +- **用户说**:「先把 DemoApp2 停用,确认停好了告诉我;然后再启用回来;最后这个应用不要了,删掉。」 +- **覆盖**:`app disable` / `enable` / `delete`;写后回读、appStatus、pretty、confirm-name。 +- **期望(分步)**: + 1. `disable --unified-app-id --dry-run` → `--yes` → 回读 `app get`(可 `--format pretty` 看 `appStatusText`),确认 `appStatus=0` 才算停用完成。 + 2. `enable --dry-run` → `--yes` → 回读确认 `appStatus=1`。 + 3. 删除:先 `app get` 展示摘要 → `delete --dry-run` → 真删需 `--confirm-name <应用真实名>`(与定位到的名一致)+ `--yes`。 +- **通过判据**:写成功 ≠ 状态已变,每步回读 appStatus(0停/1激活/2待激活/3过期);删除前展示摘要并让用户确认;confirm-name 匹配才删,读不到应用名时中止(fail-closed)。 +- **易错点**:看到 success 就回报已停/已删不回读;不带 confirm-name 直接删。 + +### C4. 网页应用配置到生效 +- **用户说**:「给这个应用配个钉钉里打开的移动端首页 https://example.com/m,配完要真正能用。」 +- **覆盖**:`webapp config` / `get`;生效模型。 +- **期望(分步)**:`webapp config --unified-app-id --homepage-url https://example.com/m --dry-run` → `--yes` → `webapp get` 回读;明确说明「改配置 ≠ 线上生效,需走版本通道」:`version create → check-approval → publish`(详见 C5)。 +- **通过判据**:先 dry-run 再 yes;配完回读 webapp get;主动点明需发版本才生效,不谎称「已生效」。 +- **易错点**:配完直接说已生效,不提版本通道。 + +### C5. 版本发布全流程(含选审批人) +- **用户说**:「我刚改了配置,发个版本上线;先看下历史版本和这次要发的版本详情;需要审批的话我来选审批人。」 +- **覆盖**:`version create` / `list` / `get` / `check-approval` / `publish` / `status`;生效模型、审批人由用户拍板。 +- **期望(分步)**: + 1. `version create --unified-app-id --version <号> --desc <说明> --yes`,记 `versionId`(新应用 `version list` 空时先 create,不要误判无可发布)。 + 2. `version list` 看历史、`version get --version-id ` 看详情。 + 3. `version check-approval --version-id `(预检,不发布,返回是否需审批 + 候选审批人)。 + 4. 把候选审批人列表给用户选 → `version publish --version-id --approver <用户选的> --yes`(含高敏权限加 `--confirm-sensitive`)。 + 5. `version status --version-id ` 跟踪到 `versionStatus=RELEASE` 才算生效。 +- **通过判据**:check-approval 不实际发布;审批人由用户拍板、agent 不默认取第一个;发布后回读 status 到 RELEASE。 +- **易错点**:跳过 check-approval 直接 publish;agent 自己选审批人;version list 空就说没东西可发。 + +### C6. 权限全流程(查 / 申请 / 批量取消) +- **用户说**:「查下跟『机器人发消息』有关、还没开通的权限;开通其中合适的那个,要真正生效;再把另外两个不需要的权限点 A、B 一起取消掉。」 +- **覆盖**:`permission list` / `add` / `remove`;过滤分页、生效模型、批量聚合。 +- **期望(分步)**: + 1. `permission list --unified-app-id --keyword 机器人发消息 --status UNAUTHED --page-size 50` 找 `scopeValue`(150+ 时用 `nextCursor` 续翻)。 + 2. `permission add --permissions --dry-run` → `--yes`;若 `requiredApproval=true`,走版本通道生效(接 C5)。 + 3. `permission remove --permissions A,B --dry-run` → `--yes`,读出参 `{results, ok, total, failedCount}` 逐条判断。 +- **通过判据**:只传 `scopeValue`(不传 API/分组名);用 keyword+status 过滤、分页不漏;需审批的明确走版本;批量取消读 `ok/failedCount` 报告部分失败,不只看命令成功。 +- **易错点**:把 API 名当权限点;add 后就说开通了;批量 remove 漏报部分失败。 + +### C7. 成员与安全配置 +- **用户说**:「把 userId 张三、李四加成这个应用的开发者,加完看下成员列表,回头把李四移除;另外给应用加一个登录重定向地址 https://b.example.com/cb,别把原来的地址冲掉。」 +- **覆盖**:`member list` / `add` / `remove`、`security config`;整组覆盖。 +- **期望(分步)**: + 1. `member add --unified-app-id --users 张三id,李四id --member-type DEVELOPER --dry-run` → `--yes` → `member list` 回读 → `member remove --users 李四id --member-type DEVELOPER --dry-run` → `--yes`。 + 2. 安全配置:提醒 `--redirect-url` 是**整组覆盖、不是追加**——要保留原地址需把旧+新一起传:`security config --redirect-url <旧1,旧2,新> --dry-run` → `--yes`。 +- **通过判据**:`--users` 逗号分隔、`--member-type` 必填、用 userId 不用姓名;识别整组覆盖语义、避免只传新地址冲掉旧的;未提供的字段(如 ip-whitelist)不动。 +- **易错点**:漏 `--member-type`;security 只传新 redirect-url 把旧的清空。 + +### C8. 机器人建号、配置与本地建联 +- **用户说**:「帮我建一个叫『小助手』的答疑机器人;另外这个现有应用还没机器人,给它也配上并启用;最后把机器人接到我本地的 Claude Code 调试。」 +- **覆盖**:`robot submit` / `result` / `get` / `config` / `enable` / `disable`、`dev connect`;异步轮询、robot info not exist、建联依赖预检、长驻进程、密钥脱敏。 +- **期望(分步)**: + 1. 新建:`robot submit --app-name <名> --robot-name 小助手 --desc <功能> --dry-run` → `--yes`(拿 taskId)→ 按 interval 轮询 `robot result --task-id `,只有 `SUCCESS` 才用返回 `robotCode/clientId/clientSecret`(敏感)。 + 2. 现有应用:`robot get` 若 `robot info is not exist` → `robot config --unified-app-id --name ... --dry-run` → `--yes`(upsert 首次即创建)→ 回读 `robot get` 看 `status=2` → 需要时 `robot enable`(停用 `robot disable`)。 + 3. 建联:`dev connect --channel auto --robot-client-id x --robot-client-secret y --dry-run` 看出参 `cli` 字段做依赖预检;正式 connect 是前台长驻进程,对话里跑要后台运行并告诉用户怎么停,或引导自己开终端。 +- **通过判据**:走异步 submit/result(同步建号已下线),轮询到 SUCCESS 再用凭证;未配置时走 config 不是 enable;config 是 upsert;写后回读 status;建联先 dry-run 预检、处理好长驻/缺凭证(先 submit/result 建号)。 +- **易错点**:找「同步一次建好」的命令;WAITING 就用凭证;robot info not exist 时去 enable;前台直接起 connect 卡住对话。 + +### C9. 事件订阅与上游错误排查 +- **用户说**:「让这个应用订阅『群成员入群』事件,回调发到 https://example.com/event,订阅完看下当前订阅了哪些,再把它取消掉;对了我之前发版本报了个 errcode 62012,这是啥意思?」 +- **覆盖**:`event list` / `subscribe` / `unsubscribe`、`dev doc search`;错误码透传、文档 RAG。 +- **期望(分步)**: + 1. `event subscribe --unified-app-id --event-types chat_add_member_org --callback-url https://example.com/event --dry-run` → `--yes` → `event list` 回读 → `event unsubscribe --event-types chat_add_member_org --dry-run` → `--yes`。事件类型不确定先 `dev doc search` 查。 + 2. 错误码:业务错误 `ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,再 `dev doc search --query "errcode 62012 " --format json` 做官方文档 RAG,结论基于命中条目。 +- **通过判据**:`--event-types` 逗号分隔共用回调,写操作先 dry-run;不编造事件类型/错误含义;先透传原始错误再走 RAG,结论不臆测、不编不存在的命令。 +- **易错点**:编事件类型名;凭空解释错误码。 + +### C10. 意图消歧(泛词边界) +- **用户说**:「帮我建个机器人。」(无任何开放平台上下文) +- **覆盖**:泛词消歧、边界与角色。 +- **期望**:`应用`/`机器人` 是泛词——先追问确认是不是开发者后台的「企业内部应用机器人」,还是工作台应用、或群里发消息的机器人(→ `dingtalk-chat`);确认是开放平台场景后才走 dev 流程(接 C8)。 +- **通过判据**:不直接假设走 dev,先澄清;能正确指向其它技能出口。 +- **易错点**:上来就 `robot submit`,没确认是不是开放平台场景。 + +--- + +## 备注 + +- 10 条合起来覆盖全部 34 个子命令 + 8 类横切行为(见覆盖矩阵)。 +- 评测可分两层:**静态**——无环境,只看 agent 选的命令/flag/判断是否符合「期望/通过判据」;**真机**——有联调环境时核对真实出参。 +- 真机注意:`dev connect` 正式连接是长驻进程;`version publish`/`app delete` 等写操作请用占位应用或停在 dry-run,避免动真实数据。 diff --git a/internal/app/helper_tool_fetcher.go b/internal/app/helper_tool_fetcher.go new file mode 100644 index 00000000..79f0e46e --- /dev/null +++ b/internal/app/helper_tool_fetcher.go @@ -0,0 +1,106 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "context" + "sync" + + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/transport" +) + +// newDevAppToolFetcher returns a cli.HelperToolFetcher that loads the op-app +// (devapp) MCP tools/list LIVE and projects each tool into a cli.HelperToolSchema +// (name, description, inputSchema properties/required). It is injected into the +// schema command so the cli package can render `dws schema dev.*` from real +// server schema without importing app/transport. +// +// The fetch is memoized per process (sync.Once on success) so repeated +// `dws schema dev.*` in one invocation hit the network at most once. A failed +// fetch is not cached, allowing a later retry within the same process. +func newDevAppToolFetcher() cli.HelperToolFetcher { + var ( + once sync.Once + cached map[string]cli.HelperToolSchema + ) + return func(ctx context.Context) (map[string]cli.HelperToolSchema, error) { + if cached != nil { + return cached, nil + } + schemas, err := fetchDevAppToolSchemas(ctx) + if err != nil { + return nil, err + } + once.Do(func() { cached = schemas }) + return cached, nil + } +} + +// fetchDevAppToolSchemas performs the live tools/list call against the pinned +// op-app endpoint and converts the descriptors. Auth and identity headers are +// resolved the same way the runner does for direct-runtime invocations. +func fetchDevAppToolSchemas(ctx context.Context) (map[string]cli.HelperToolSchema, error) { + token := resolveRuntimeAuthToken(ctx, "") + headers := resolveIdentityHeaders() + client := transport.NewClient(nil).WithAuth(token, headers) + + result, err := client.ListTools(ctx, devappEndpoint) + if err != nil { + return nil, err + } + + out := make(map[string]cli.HelperToolSchema, len(result.Tools)) + for _, td := range result.Tools { + out[td.Name] = cli.HelperToolSchema{ + Name: td.Name, + Description: td.Description, + Properties: inputSchemaProperties(td.InputSchema), + Required: inputSchemaRequired(td.InputSchema), + } + } + return out, nil +} + +// inputSchemaProperties pulls the "properties" object out of a deserialized +// MCP inputSchema map. Returns an empty (non-nil) map when absent. +func inputSchemaProperties(schema map[string]any) map[string]any { + if schema == nil { + return map[string]any{} + } + props, _ := schema["properties"].(map[string]any) + if props == nil { + return map[string]any{} + } + return props +} + +// inputSchemaRequired pulls the "required" string list out of a deserialized +// MCP inputSchema map. +func inputSchemaRequired(schema map[string]any) []string { + if schema == nil { + return nil + } + raw, ok := schema["required"].([]any) + if !ok { + return nil + } + out := make([]string, 0, len(raw)) + for _, v := range raw { + if s, ok := v.(string); ok && s != "" { + out = append(out, s) + } + } + return out +} diff --git a/internal/app/root.go b/internal/app/root.go index 86726148..04ecfc43 100644 --- a/internal/app/root.go +++ b/internal/app/root.go @@ -596,7 +596,7 @@ func newVersionCommand() *cobra.Command { } func newSchemaCommand(loader cli.CatalogLoader) *cobra.Command { - return cli.NewSchemaCommand(loader) + return cli.NewSchemaCommand(loader, newDevAppToolFetcher()) } func newGenerateSkillsCommand() *cobra.Command { diff --git a/internal/cli/canonical.go b/internal/cli/canonical.go index 1ddbf4f0..46a1caae 100644 --- a/internal/cli/canonical.go +++ b/internal/cli/canonical.go @@ -103,7 +103,7 @@ func NewMCPCommand(ctx context.Context, loader CatalogLoader, runner executor.Ru return cmd } -func NewSchemaCommand(loader CatalogLoader) *cobra.Command { +func NewSchemaCommand(loader CatalogLoader, helperTools HelperToolFetcher) *cobra.Command { cmd := &cobra.Command{ Use: "schema [path]", Short: "查看 MCP 工具 Schema (产品列表 / 工具参数)", @@ -125,7 +125,13 @@ func NewSchemaCommand(loader CatalogLoader) *cobra.Command { dws schema --cli-path "ding message send" # 同上,显式 flag(脚本友好) dws schema calendar.create_event --jq '.tool.auth' dws schema -f pretty ding.send_ding_message # ANSI 彩色分区展示 - dws schema --jq '.tool.flag_overlay' # 只看 CLI overlay`, + dws schema --jq '.tool.flag_overlay' # 只看 CLI overlay + +helper-only 命令组(如 dev,不走服务发现)也支持查询,schema 从 op-app +MCP 服务端实时拉取,输出对齐 gws 的扁平格式(parameters 内联 required, +键为 CLI flag): + dws schema "dev app robot config" # 实时 MCP 参数 schema(gws-flat) + dws schema "dev app" # 列出该分组下的子命令`, Args: cobra.MaximumNArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -137,6 +143,29 @@ func NewSchemaCommand(loader CatalogLoader) *cobra.Command { } args = []string{cliPath} } + + // Helper-only subtrees (e.g. `dws dev ...`) aren't in the discovery + // catalog; their schema CONTENT is fetched LIVE from the helper's + // pinned MCP server (op-app) and rendered in the gws-flat shape, so + // `dws schema "dev app robot config"` answers without touching + // discovery. Only the `dev` root claims this path; everything else + // falls through to the catalog below. + if len(args) > 0 { + payload, ok, err := renderHelperSchema(cmd.Context(), cmd.Root(), args[0], helperTools) + if err != nil { + return err + } + if ok { + return output.WriteFiltered( + cmd.OutOrStdout(), + output.ResolveFormat(cmd, output.FormatJSON), + payload, + output.ResolveFields(cmd), + output.ResolveJQ(cmd), + ) + } + } + catalog, err := loader.Load(cmd.Context()) if err != nil { var degraded *CatalogDegraded @@ -166,6 +195,17 @@ func NewSchemaCommand(loader CatalogLoader) *cobra.Command { return err } + // Append helper-only subtrees (e.g. `dev`) to the no-arg product + // listing so browsing all products also surfaces helper commands. + if len(args) == 0 { + if helpers := helperProductSummaries(cmd.Root()); len(helpers) > 0 { + if products, ok := payload["products"].([]map[string]any); ok { + payload["products"] = append(products, helpers...) + payload["count"] = len(payload["products"].([]map[string]any)) + } + } + } + return output.WriteFiltered( cmd.OutOrStdout(), output.ResolveFormat(cmd, output.FormatJSON), diff --git a/internal/cli/canonical_test.go b/internal/cli/canonical_test.go index 2bd5e0b6..a52ef5b0 100644 --- a/internal/cli/canonical_test.go +++ b/internal/cli/canonical_test.go @@ -288,7 +288,7 @@ func TestSchemaCommandCLIPathFlag(t *testing.T) { }} t.Run("resolves via --cli-path", func(t *testing.T) { - cmd := NewSchemaCommand(loader) + cmd := NewSchemaCommand(loader, nil) var out bytes.Buffer cmd.SetOut(&out) cmd.SetErr(&bytes.Buffer{}) @@ -310,7 +310,7 @@ func TestSchemaCommandCLIPathFlag(t *testing.T) { }) t.Run("rejects positional + flag collision", func(t *testing.T) { - cmd := NewSchemaCommand(loader) + cmd := NewSchemaCommand(loader, nil) var out bytes.Buffer cmd.SetOut(&out) cmd.SetErr(&bytes.Buffer{}) @@ -1252,7 +1252,7 @@ func TestSchemaCommandOutputsDegradedOnUnauthenticated(t *testing.T) { Reason: DegradedUnauthenticated, Hint: "未登录,无法发现 MCP 服务。请先执行: dws auth login", } - cmd := NewSchemaCommand(errorLoader{err: degradedErr}) + cmd := NewSchemaCommand(errorLoader{err: degradedErr}, nil) var out, errOut bytes.Buffer cmd.SetOut(&out) @@ -1287,7 +1287,7 @@ func TestSchemaCommandOutputsDegradedOnMarketUnreachable(t *testing.T) { Reason: DegradedMarketUnreachable, Hint: "无法连接 MCP 市场 (mcp.dingtalk.com),请检查网络", } - cmd := NewSchemaCommand(errorLoader{err: degradedErr}) + cmd := NewSchemaCommand(errorLoader{err: degradedErr}, nil) var out, errOut bytes.Buffer cmd.SetOut(&out) @@ -1310,7 +1310,7 @@ func TestSchemaCommandPropagatesNonDegradedError(t *testing.T) { t.Parallel() wantErr := errors.New("unexpected failure") - cmd := NewSchemaCommand(errorLoader{err: wantErr}) + cmd := NewSchemaCommand(errorLoader{err: wantErr}, nil) var out bytes.Buffer cmd.SetOut(&out) diff --git a/internal/cli/dev_schema.go b/internal/cli/dev_schema.go new file mode 100644 index 00000000..6f1d924c --- /dev/null +++ b/internal/cli/dev_schema.go @@ -0,0 +1,337 @@ +// 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 cli + +import ( + "context" + "fmt" + "sort" + "strings" + "unicode" + + "github.com/spf13/cobra" +) + +// helperSchemaRoots are top-level command names whose subtrees are helper-only +// (hard-coded cobra commands, not in the discovery catalog). `dws schema` still +// answers for them, but unlike discovery products the schema CONTENT is fetched +// LIVE from the helper's pinned MCP server (op-app) and rendered in the flat +// gws-aligned shape — never synthesized from local cobra flags, never +// hardcoded. The mapping from a leaf command to its MCP tool comes from the +// `mcp-tool` cobra annotation set in internal/helpers/devapp.go. +var helperSchemaRoots = map[string]bool{"dev": true} + +// HelperToolSchema is the live op-app tool schema the renderer needs: the raw +// MCP description plus the inputSchema's properties/required, exactly as the +// server returned them (no local transformation of CONTENT). +type HelperToolSchema struct { + Name string + Description string + Properties map[string]any // MCP param name → property object {type,description,default?,...} + Required []string // MCP param names that are required +} + +// HelperToolFetcher loads the op-app tools/list LIVE and returns toolName→schema. +// The schema command injects this so dev_schema.go can resolve a command's +// MCP tool and render its real schema without the cli package importing app/ +// transport. Implementations should cache per-process so repeated +// `dws schema dev.*` only hit the network once. +type HelperToolFetcher func(ctx context.Context) (map[string]HelperToolSchema, error) + +// renderHelperSchema builds the `dws schema` payload for helper-only command +// subtrees. Returns (payload, true) when the path targets a helper subtree (so +// the caller skips catalog resolution); (nil, false) otherwise so the caller +// falls back to the discovery catalog. +// +// Leaf commands render the gws-flat object {description, path, source, +// parameters{:{type,description,default?,required}}} with all CONTENT +// pulled live from the MCP tool named by the command's `mcp-tool` annotation. +// Group/root paths render a browse listing {path, commands:[...]} from the +// cobra tree (no MCP needed). +func renderHelperSchema(ctx context.Context, root *cobra.Command, rawPath string, fetch HelperToolFetcher) (map[string]any, bool, error) { + if root == nil { + return nil, false, nil + } + tokens := splitSchemaPathTokens(rawPath) + if len(tokens) == 0 || !helperSchemaRoots[tokens[0]] { + return nil, false, nil + } + + target, rest, err := root.Find(tokens) + if err != nil || target == nil { + target = root + rest = tokens[1:] + } + // Find resolves to the deepest matching command and returns trailing tokens + // it couldn't match as (sub)commands. Any non-flag leftover means a typo'd + // or unknown subcommand — surface it with the closest group's children. + if unknown := firstNonFlag(rest); unknown != "" { + return map[string]any{ + "path": rawPath, + "error": "unknown subcommand \"" + unknown + "\" under \"" + helperCommandPath(target) + "\"", + "available": helperSubcommands(target), + }, true, nil + } + + // A runnable leaf → emit its live MCP schema in gws-flat shape. + // A group → browse its subcommands. + if target.Runnable() && !target.HasAvailableSubCommands() { + payload, err := helperLeafSchema(ctx, target, fetch) + return payload, true, err + } + + return map[string]any{ + "path": helperCommandPath(target), + "commands": helperSubcommands(target), + }, true, nil +} + +// helperLeafSchema renders a single leaf command as the gws-flat object, +// fetching its MCP tool schema live. The command must carry an `mcp-tool` +// annotation; commands without one (e.g. `dev connect`, `dev doc search`) are +// not devapp tools and get a clear, non-fatal explanation instead. +func helperLeafSchema(ctx context.Context, cmd *cobra.Command, fetch HelperToolFetcher) (map[string]any, error) { + toolName := "" + if cmd.Annotations != nil { + toolName = strings.TrimSpace(cmd.Annotations["mcp-tool"]) + } + path := helperCommandPath(cmd) + if toolName == "" { + return map[string]any{ + "path": path, + "error": "no MCP tool for this command (not an op-app tool); schema is unavailable", + }, nil + } + if fetch == nil { + return map[string]any{ + "path": path, + "error": "live MCP schema fetcher not configured", + }, nil + } + + tools, err := fetch(ctx) + if err != nil { + return nil, fmt.Errorf("fetch op-app tool schemas: %w", err) + } + tool, ok := tools[toolName] + if !ok { + return map[string]any{ + "path": path, + "error": fmt.Sprintf("MCP tool %q not found in op-app tools/list", toolName), + }, nil + } + + return map[string]any{ + "description": tool.Description, + "path": path, + "source": "mcp:op-app", + "parameters": helperFlatParameters(tool), + }, nil +} + +// helperFlatParameters projects an MCP tool's inputSchema into the gws-flat +// per-parameter object. Keys are kebab-case of the MCP param name (== the CLI +// flag); each value is {type, description, default?, required} with type mapped +// to a JSON-type string, description verbatim from MCP, default only when MCP +// provides one (stringified), and required inline (true iff the param is in the +// tool's required[]). +func helperFlatParameters(tool HelperToolSchema) map[string]any { + required := make(map[string]bool, len(tool.Required)) + for _, r := range tool.Required { + required[r] = true + } + + params := make(map[string]any, len(tool.Properties)) + for name, raw := range tool.Properties { + prop, _ := raw.(map[string]any) + entry := map[string]any{ + "type": mcpJSONType(prop), + "description": mcpString(prop, "description"), + "required": required[name], + } + if def, ok := mcpDefault(prop); ok { + entry["default"] = def + } + params[kebabCase(name)] = entry + } + return params +} + +// mcpJSONType normalizes the MCP property "type" to a JSON-type string. MCP +// reports standard JSON Schema types; pass them through, defaulting to "string" +// when absent/unknown so the contract is always populated. +func mcpJSONType(prop map[string]any) string { + t, _ := prop["type"].(string) + switch t { + case "string", "integer", "number", "boolean", "array", "object": + return t + default: + return "string" + } +} + +// mcpString reads a string field from an MCP property object. +func mcpString(prop map[string]any, key string) string { + if prop == nil { + return "" + } + v, _ := prop[key].(string) + return v +} + +// mcpDefault returns the MCP-provided default, stringified, only when present. +// gws renders default as a string; mirror that. Non-string JSON defaults +// (numbers/bools) are formatted with %v so e.g. 0 → "0", true → "true". +func mcpDefault(prop map[string]any) (string, bool) { + if prop == nil { + return "", false + } + v, ok := prop["default"] + if !ok || v == nil { + return "", false + } + switch tv := v.(type) { + case string: + return tv, true + case float64: + // JSON numbers decode to float64; render integers without a fraction. + if tv == float64(int64(tv)) { + return fmt.Sprintf("%d", int64(tv)), true + } + return fmt.Sprintf("%v", tv), true + default: + return fmt.Sprintf("%v", tv), true + } +} + +// kebabCase converts an MCP camelCase param name to the CLI flag's kebab form, +// matching how flags are registered in internal/helpers/devapp.go: +// +// eventCallbackUrl → event-callback-url +// unifiedAppId → unified-app-id +// disableSSLVerify → disable-ssl-verify +// +// A boundary is inserted before an uppercase letter that follows a lowercase +// letter or digit, and before the final uppercase of a run that starts a new +// lowercase word (so SSLVerify → ssl-verify, not s-s-l-verify). +func kebabCase(name string) string { + runes := []rune(name) + var b strings.Builder + for i, r := range runes { + if unicode.IsUpper(r) { + prevLowerOrDigit := i > 0 && (unicode.IsLower(runes[i-1]) || unicode.IsDigit(runes[i-1])) + nextLower := i+1 < len(runes) && unicode.IsLower(runes[i+1]) + if i > 0 && (prevLowerOrDigit || nextLower) { + b.WriteByte('-') + } + b.WriteRune(unicode.ToLower(r)) + continue + } + b.WriteRune(r) + } + // Collapse any accidental double dashes and trim, just in case the source + // already contained separators. + out := strings.ReplaceAll(b.String(), "_", "-") + for strings.Contains(out, "--") { + out = strings.ReplaceAll(out, "--", "-") + } + return strings.Trim(out, "-") +} + +// helperProductSummaries returns light product entries for every helper-only +// subtree, appended to the no-arg `dws schema` product listing so agents +// browsing all products also see helper commands. Tools are listed by path + +// summary only; drill in with `dws schema ""` for full parameter schema. +func helperProductSummaries(root *cobra.Command) []map[string]any { + if root == nil { + return nil + } + out := []map[string]any{} + for name := range helperSchemaRoots { + top, _, err := root.Find([]string{name}) + if err != nil || top == nil || !top.HasParent() { + continue + } + leaves := []map[string]any{} + walkLeafCommands(top, func(leaf *cobra.Command) { + leaves = append(leaves, map[string]any{ + "cli_name": leaf.Name(), + "cli_path": helperCommandPath(leaf), + "description": strings.TrimSpace(leaf.Short), + }) + }) + out = append(out, map[string]any{ + "id": name, + "name": strings.TrimSpace(top.Short), + "description": "helper-only 命令组(不走服务发现);schema 从 op-app MCP 实时拉取,用 `dws schema \"" + helperCommandPath(top) + " ...\"` 查具体参数", + "helper": true, + "tools": leaves, + }) + } + return out +} + +// walkLeafCommands invokes fn for every runnable leaf under cmd (depth-first). +func walkLeafCommands(cmd *cobra.Command, fn func(*cobra.Command)) { + if cmd.Runnable() && !cmd.HasAvailableSubCommands() { + fn(cmd) + return + } + for _, sub := range cmd.Commands() { + if !sub.IsAvailableCommand() || sub.Name() == "help" { + continue + } + walkLeafCommands(sub, fn) + } +} + +// helperSubcommands lists a group's runnable children for browse mode, sorted +// by name for deterministic output. +func helperSubcommands(cmd *cobra.Command) []map[string]any { + out := []map[string]any{} + for _, sub := range cmd.Commands() { + if !sub.IsAvailableCommand() || sub.Name() == "help" { + continue + } + out = append(out, map[string]any{ + "cli_path": helperCommandPath(sub), + "description": strings.TrimSpace(sub.Short), + }) + } + sort.Slice(out, func(i, j int) bool { + return out[i]["cli_path"].(string) < out[j]["cli_path"].(string) + }) + return out +} + +// helperCommandPath returns the space-joined path from root to cmd, e.g. +// "dev app robot config". +func helperCommandPath(cmd *cobra.Command) string { + parts := []string{} + for c := cmd; c != nil && c.HasParent(); c = c.Parent() { + parts = append([]string{c.Name()}, parts...) + } + return strings.Join(parts, " ") +} + +// firstNonFlag returns the first token that is not a flag (does not start with +// "-"), or "" if there is none. +func firstNonFlag(tokens []string) string { + for _, t := range tokens { + if t != "" && !strings.HasPrefix(t, "-") { + return t + } + } + return "" +} diff --git a/internal/cli/dev_schema_test.go b/internal/cli/dev_schema_test.go new file mode 100644 index 00000000..9e3a3e73 --- /dev/null +++ b/internal/cli/dev_schema_test.go @@ -0,0 +1,254 @@ +// 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 cli + +import ( + "context" + "errors" + "testing" + + "github.com/spf13/cobra" +) + +// buildHelperTestTree mirrors the shape of the real `dws dev` subtree closely +// enough to exercise the live-schema renderer: a group and leaves carrying the +// `mcp-tool` annotation that names the op-app tool to fetch. +func buildHelperTestTree() *cobra.Command { + root := &cobra.Command{Use: "dws"} + + create := &cobra.Command{ + Use: "create", + Short: "创建应用", + Annotations: map[string]string{"mcp-tool": "create_dev_app"}, + Run: func(*cobra.Command, []string) {}, + } + + config := &cobra.Command{ + Use: "config", + Short: "配置机器人", + Annotations: map[string]string{"mcp-tool": "set_extension_robot_config"}, + Run: func(*cobra.Command, []string) {}, + } + + // A leaf without an mcp-tool annotation (e.g. dev connect / dev doc search). + noTool := &cobra.Command{Use: "connect", Short: "无 MCP 工具", Run: func(*cobra.Command, []string) {}} + + robot := &cobra.Command{Use: "robot", Short: "机器人能力"} + robot.AddCommand(config) + + app := &cobra.Command{Use: "app", Short: "应用"} + app.AddCommand(create, robot) + + dev := &cobra.Command{Use: "dev", Short: "开放平台开发者命令"} + dev.AddCommand(app, noTool) + + root.AddCommand(dev) + return root +} + +// fakeFetcher returns a canned op-app tools/list so the renderer is exercised +// without network. It mirrors the MCP shape: properties keyed by camelCase param +// name, required[] listing the camelCase names. +func fakeFetcher(tools map[string]HelperToolSchema) HelperToolFetcher { + return func(context.Context) (map[string]HelperToolSchema, error) { + return tools, nil + } +} + +func robotConfigToolSchema() HelperToolSchema { + return HelperToolSchema{ + Name: "set_extension_robot_config", + Description: "创建或更新现有应用的机器人配置", + Properties: map[string]any{ + "unifiedAppId": map[string]any{"type": "string", "description": "统一应用 ID"}, + "eventCallbackUrl": map[string]any{"type": "string", "description": "事件回调地址"}, + "skills": map[string]any{"type": "array", "description": "技能列表"}, + "mode": map[string]any{"type": "integer", "description": "机器人模式枚举", "default": float64(0)}, + }, + Required: []string{"unifiedAppId"}, + } +} + +func TestRenderHelperSchema_LeafGwsFlat(t *testing.T) { + root := buildHelperTestTree() + fetch := fakeFetcher(map[string]HelperToolSchema{ + "set_extension_robot_config": robotConfigToolSchema(), + }) + + payload, ok, err := renderHelperSchema(context.Background(), root, "dev app robot config", fetch) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !ok { + t.Fatal("expected helper renderer to claim the path") + } + + // Flat top-level: description / path / source / parameters; no wrapper. + if payload["description"] != "创建或更新现有应用的机器人配置" { + t.Fatalf("description = %v", payload["description"]) + } + if payload["path"] != "dev app robot config" { + t.Fatalf("path = %v", payload["path"]) + } + if payload["source"] != "mcp:op-app" { + t.Fatalf("source = %v", payload["source"]) + } + for _, leaked := range []string{"kind", "tool", "product", "helper"} { + if _, present := payload[leaked]; present { + t.Fatalf("gws-flat output must not carry %q wrapper key", leaked) + } + } + + params, _ := payload["parameters"].(map[string]any) + if params == nil { + t.Fatalf("no parameters: %#v", payload) + } + + // Keys are kebab-case of the MCP param name == the CLI flag. + uid, _ := params["unified-app-id"].(map[string]any) + if uid == nil { + t.Fatalf("missing unified-app-id param: %#v", params) + } + if uid["type"] != "string" || uid["required"] != true { + t.Fatalf("unified-app-id = %#v, want string+required", uid) + } + if _, hasDefault := uid["default"]; hasDefault { + t.Fatal("unified-app-id must not carry a default (MCP provides none)") + } + + cb, _ := params["event-callback-url"].(map[string]any) + if cb == nil || cb["required"] != false { + t.Fatalf("event-callback-url = %#v, want required=false", cb) + } + + skills, _ := params["skills"].(map[string]any) + if skills == nil || skills["type"] != "array" { + t.Fatalf("skills = %#v, want array", skills) + } + + mode, _ := params["mode"].(map[string]any) + if mode == nil || mode["type"] != "integer" { + t.Fatalf("mode = %#v, want integer", mode) + } + if mode["default"] != "0" { + t.Fatalf("mode default = %v, want \"0\" (stringified MCP default)", mode["default"]) + } + if mode["required"] != false { + t.Fatalf("mode required = %v, want false", mode["required"]) + } +} + +func TestRenderHelperSchema_Group(t *testing.T) { + root := buildHelperTestTree() + payload, ok, err := renderHelperSchema(context.Background(), root, "dev app", nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !ok { + t.Fatal("expected claim") + } + if payload["path"] != "dev app" { + t.Fatalf("path = %v", payload["path"]) + } + cmds, _ := payload["commands"].([]map[string]any) + if len(cmds) != 2 { // create + robot + t.Fatalf("commands count = %d, want 2", len(cmds)) + } +} + +func TestRenderHelperSchema_NoAnnotation(t *testing.T) { + root := buildHelperTestTree() + payload, ok, err := renderHelperSchema(context.Background(), root, "dev connect", nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !ok { + t.Fatal("expected claim") + } + if payload["error"] == nil { + t.Fatalf("expected a clear no-MCP-tool error, got %#v", payload) + } + if _, present := payload["parameters"]; present { + t.Fatal("no-tool command must not emit parameters") + } +} + +func TestRenderHelperSchema_UnknownSubcommand(t *testing.T) { + root := buildHelperTestTree() + payload, ok, err := renderHelperSchema(context.Background(), root, "dev app nope", nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !ok { + t.Fatal("expected claim") + } + if payload["error"] == nil { + t.Fatalf("expected error for unknown subcommand, got %#v", payload) + } + if avail, _ := payload["available"].([]map[string]any); len(avail) == 0 { + t.Fatal("expected available subcommands listed") + } +} + +func TestRenderHelperSchema_ToolMissingInList(t *testing.T) { + root := buildHelperTestTree() + // Fetcher returns an empty list — the annotated tool isn't present. + payload, ok, err := renderHelperSchema(context.Background(), root, "dev app create", fakeFetcher(map[string]HelperToolSchema{})) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !ok { + t.Fatal("expected claim") + } + if payload["error"] == nil { + t.Fatalf("expected not-found error, got %#v", payload) + } +} + +func TestRenderHelperSchema_FetchError(t *testing.T) { + root := buildHelperTestTree() + failing := func(context.Context) (map[string]HelperToolSchema, error) { + return nil, errors.New("network down") + } + _, ok, err := renderHelperSchema(context.Background(), root, "dev app create", failing) + if !ok { + t.Fatal("expected claim even on fetch error") + } + if err == nil { + t.Fatal("expected the fetch error to surface") + } +} + +func TestRenderHelperSchema_NonHelperPathDeclined(t *testing.T) { + root := buildHelperTestTree() + if _, ok, _ := renderHelperSchema(context.Background(), root, "ding.message.send", nil); ok { + t.Fatal("non-helper path must not be claimed by the helper renderer") + } +} + +func TestKebabCase(t *testing.T) { + cases := map[string]string{ + "eventCallbackUrl": "event-callback-url", + "unifiedAppId": "unified-app-id", + "disableSSLVerify": "disable-ssl-verify", + "mode": "mode", + "skills": "skills", + "i18nName": "i18n-name", + } + for in, want := range cases { + if got := kebabCase(in); got != want { + t.Errorf("kebabCase(%q) = %q, want %q", in, got, want) + } + } +} diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 00afbd25..1a3b8bdd 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -67,8 +67,8 @@ const ( // 事件订阅能力(op-app MCP 工具,服务端新增)。 devAppEventListTool = "list_dev_app_events" - devAppEventSubscribeTool = "subscribe_dev_app_event" - devAppEventUnsubscribeTool = "unsubscribe_dev_app_event" + devAppEventSubscribeTool = "subscribe_dev_app_events" + devAppEventUnsubscribeTool = "unsubscribe_dev_app_events" // 版本发布能力(op-app MCP 工具,硬编码不走服务发现)。 devAppVersionCreateTool = "create_dev_app_version" @@ -264,6 +264,7 @@ func newDevAppEventListCommand(runner executor.Runner) *cobra.Command { addDevAppUnifiedIDFlag(cmd) registerDevAppCursorFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppEventListTool) return cmd } @@ -271,7 +272,7 @@ func newDevAppEventSubscribeCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "subscribe", Short: "订阅应用事件回调", - Example: " dws dev app event subscribe --unified-app-id UNIFIED_APP_ID --event-types bpms_task_change --callback-url https://example.com/cb --dry-run --format json", + Example: " dws dev app event subscribe --unified-app-id UNIFIED_APP_ID --event-codes bpms_task_change --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -283,17 +284,17 @@ func newDevAppEventSubscribeCommand(runner executor.Runner) *cobra.Command { return err } params := map[string]any{"unifiedAppId": appID} - if types := parseDevAppListFlag(cmd, "event-types"); len(types) > 0 { - params["eventTypes"] = types + // 服务端 subscribe/unsubscribe 工具收 eventCodes 数组(事件码列表)。 + if types := parseDevAppListFlag(cmd, "event-codes"); len(types) > 0 { + params["eventCodes"] = types } - devAppPutString(params, "callbackUrl", devAppStringFlag(cmd, "callback-url")) return runDevAppTool(runner, cmd, devAppEventSubscribeTool, params) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("event-types", "", "事件类型,多个用逗号或分号分隔") - cmd.Flags().String("callback-url", "", "事件回调地址") + cmd.Flags().String("event-codes", "", "事件码,多个用逗号或分号分隔") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppEventSubscribeTool) return cmd } @@ -301,7 +302,7 @@ func newDevAppEventUnsubscribeCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "unsubscribe", Short: "取消订阅应用事件", - Example: " dws dev app event unsubscribe --unified-app-id UNIFIED_APP_ID --event-types bpms_task_change --dry-run --format json", + Example: " dws dev app event unsubscribe --unified-app-id UNIFIED_APP_ID --event-codes bpms_task_change --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -313,15 +314,16 @@ func newDevAppEventUnsubscribeCommand(runner executor.Runner) *cobra.Command { return err } params := map[string]any{"unifiedAppId": appID} - if types := parseDevAppListFlag(cmd, "event-types"); len(types) > 0 { - params["eventTypes"] = types + if types := parseDevAppListFlag(cmd, "event-codes"); len(types) > 0 { + params["eventCodes"] = types } return runDevAppTool(runner, cmd, devAppEventUnsubscribeTool, params) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("event-types", "", "事件类型,多个用逗号或分号分隔") + cmd.Flags().String("event-codes", "", "事件码,多个用逗号或分号分隔") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppEventUnsubscribeTool) return cmd } @@ -335,15 +337,15 @@ func newDevAppListCommand(runner executor.Runner) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { params := map[string]any{} devAppApplyCursorParams(cmd, params) - devAppPutString(params, "appName", devAppFlagOrFallback(cmd, "name", "keyword")) + devAppPutString(params, "name", devAppFlagOrFallback(cmd, "name", "keyword")) devAppPutString(params, "appKey", devAppStringFlag(cmd, "app-key")) devAppPutInt(params, "appGroupId", devAppIntFlag(cmd, "app-group-id")) devAppPutString(params, "creator", devAppStringFlag(cmd, "creator")) devAppPutString(params, "robotName", devAppStringFlag(cmd, "robot-name")) devAppPutInt(params, "developType", devAppIntFlag(cmd, "develop-type")) devAppPutInt(params, "filterCoolApp", devAppIntFlag(cmd, "filter-cool-app")) - devAppPutString(params, "sortType", devAppStringFlag(cmd, "sort")) - devAppPutString(params, "sortOrder", devAppStringFlag(cmd, "order")) + devAppPutString(params, "sortType", devAppStringFlag(cmd, "sort-type")) + devAppPutString(params, "sortOrder", devAppStringFlag(cmd, "sort-order")) return runDevAppTool(runner, cmd, devAppListTool, params) }, } @@ -357,9 +359,10 @@ func newDevAppListCommand(runner executor.Runner) *cobra.Command { cmd.Flags().String("robot-name", "", "机器人名称关键词") cmd.Flags().Int("develop-type", 0, "开发类型枚举;不确定时不要传") cmd.Flags().Int("filter-cool-app", 0, "酷应用过滤枚举;不确定时不要传") - cmd.Flags().String("sort", "", "排序字段,如 gmt_modified") - cmd.Flags().String("order", "", "排序方向 asc 或 desc") + cmd.Flags().String("sort-type", "", "排序字段,如 gmt_modified") + cmd.Flags().String("sort-order", "", "排序方向 asc 或 desc") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppListTool) return cmd } @@ -381,6 +384,7 @@ func newDevAppGetCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppGetTool) return cmd } @@ -399,16 +403,17 @@ func newDevAppCreateCommand(runner executor.Runner) *cobra.Command { if name == "" { return apperrors.NewValidation("--name 为必填") } - params := map[string]any{"appName": name} + params := map[string]any{"name": name} devAppPutString(params, "desc", devAppStringFlag(cmd, "desc")) - devAppPutString(params, "icon", devAppStringFlag(cmd, "icon")) + devAppPutString(params, "iconMediaId", devAppStringFlag(cmd, "icon-media-id")) return runDevAppTool(runner, cmd, devAppCreateTool, params) }, } cmd.Flags().String("name", "", "应用名称 (必填)") cmd.Flags().String("desc", "", "应用描述") - cmd.Flags().String("icon", "", "应用图标 mediaId") + cmd.Flags().String("icon-media-id", "", "应用图标 mediaId") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppCreateTool) return cmd } @@ -430,19 +435,19 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { params := map[string]any{"unifiedAppId": appID} updates := 0 if v := devAppStringFlag(cmd, "name"); v != "" { - params["appName"] = v + params["name"] = v updates++ } if v := devAppStringFlag(cmd, "desc"); v != "" { params["desc"] = v updates++ } - if v := devAppStringFlag(cmd, "icon"); v != "" { - params["icon"] = v + if v := devAppStringFlag(cmd, "icon-media-id"); v != "" { + params["iconMediaId"] = v updates++ } if updates == 0 { - return apperrors.NewValidation("至少提供一项待更新字段:--name、--desc 或 --icon") + return apperrors.NewValidation("至少提供一项待更新字段:--name、--desc 或 --icon-media-id") } return runDevAppTool(runner, cmd, devAppUpdateTool, params) }, @@ -450,8 +455,9 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("name", "", "新的应用名称") cmd.Flags().String("desc", "", "新的应用描述") - cmd.Flags().String("icon", "", "新的应用图标 mediaId") + cmd.Flags().String("icon-media-id", "", "新的应用图标 mediaId") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppUpdateTool) return cmd } @@ -473,6 +479,7 @@ func newDevAppCredentialsGetCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppCredentialsGetTool) return cmd } @@ -496,6 +503,7 @@ func newDevAppLifecycleCommand(runner executor.Runner, use, short, tool string) } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, tool) return cmd } @@ -549,6 +557,7 @@ func newDevAppDeleteCommand(runner executor.Runner) *cobra.Command { addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("confirm-name", "", "二次确认:必须与被删应用的名称一致(不可逆操作的防误删)") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppDeleteTool) return cmd } @@ -567,6 +576,11 @@ func devAppFetchAppName(runner executor.Runner, cmd *cobra.Command, locator map[ if err != nil { return "", err } + // get_dev_app 返回的应用名字段是 name(credentials 才用 appName); + // 取 name、appName 兜底,否则 delete 永远读不到名、二次确认必然 fail-closed。 + if name := devAppExtractString(result.Response, "name"); name != "" { + return name, nil + } return devAppExtractString(result.Response, "appName"), nil } @@ -604,6 +618,7 @@ func newDevAppWebappGetCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppWebappGetTool) return cmd } @@ -652,6 +667,7 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { cmd.Flags().String("pc-homepage-url", "", "PC 端首页地址") cmd.Flags().String("omp-url", "", "管理后台地址") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppWebappSetTool) return cmd } @@ -670,8 +686,8 @@ func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { } params := map[string]any{"unifiedAppId": appID} devAppPutString(params, "keyword", devAppStringFlag(cmd, "keyword")) - devAppPutString(params, "scopeValue", devAppFlagOrFallback(cmd, "scope", "permission")) - devAppPutString(params, "authStatus", strings.ToUpper(devAppStringFlag(cmd, "status"))) + devAppPutString(params, "scopeValue", devAppStringFlag(cmd, "scope-value")) + devAppPutString(params, "authStatus", strings.ToUpper(devAppStringFlag(cmd, "auth-status"))) devAppPutString(params, "scopeType", strings.ToUpper(devAppStringFlag(cmd, "scope-type"))) devAppPutString(params, "apiStatus", devAppStringFlag(cmd, "api-status")) devAppApplyCursorParams(cmd, params) @@ -680,14 +696,13 @@ func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("keyword", "", "权限名、权限点、接口名关键词") - cmd.Flags().String("scope", "", "精确权限点 scopeValue") - cmd.Flags().String("permission", "", "--scope 的兼容别名") - _ = cmd.Flags().MarkHidden("permission") - cmd.Flags().String("status", "ALL", "权限状态:ALL、AUTHED、UNAUTHED") + cmd.Flags().String("scope-value", "", "精确权限点 scopeValue") + cmd.Flags().String("auth-status", "ALL", "权限状态:ALL、AUTHED、UNAUTHED") cmd.Flags().String("scope-type", "", "权限一级类型:APP 或 SNS") cmd.Flags().String("api-status", "", "开发者后台 apiStatus 过滤") registerDevAppCursorFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppPermissionListTool) return cmd } @@ -695,7 +710,7 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "申请开放平台应用权限点", - Example: " dws dev app permission add --unified-app-id UNIFIED_APP_ID --permissions Contact.User.mobile --dry-run --format json", + Example: " dws dev app permission add --unified-app-id UNIFIED_APP_ID --scope-values Contact.User.mobile --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -709,19 +724,16 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { params := map[string]any{"unifiedAppId": appID} scopes := devAppPermissionScopes(cmd) if len(scopes) == 0 { - return apperrors.NewValidation("--permissions 为必填") + return apperrors.NewValidation("--scope-values 为必填") } params["scopeValues"] = scopes return runDevAppTool(runner, cmd, devAppPermissionAddTool, params) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("permissions", "", "权限点 scopeValue,多个用逗号或分号分隔") - cmd.Flags().String("scope", "", "--permissions 的兼容别名,单个权限点 scopeValue") - cmd.Flags().String("permission", "", "--permissions 的兼容别名,单个权限点 scopeValue") - _ = cmd.Flags().MarkHidden("scope") - _ = cmd.Flags().MarkHidden("permission") + cmd.Flags().String("scope-values", "", "权限点 scopeValue,多个用逗号或分号分隔") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppPermissionAddTool) return cmd } @@ -729,7 +741,7 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "取消开放平台应用权限点", - Example: " dws dev app permission remove --unified-app-id UNIFIED_APP_ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --dry-run --format json", + Example: " dws dev app permission remove --unified-app-id UNIFIED_APP_ID --scope-values Contact.User.mobile,qyapi_robot_sendmsg --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -740,57 +752,19 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - locator := map[string]any{"unifiedAppId": appID} + params := map[string]any{"unifiedAppId": appID} scopes := devAppPermissionScopes(cmd) if len(scopes) == 0 { - return apperrors.NewValidation("--permissions 为必填") + return apperrors.NewValidation("--scope-values 为必填") } - // 上游 MCP tool 一次只接受一个权限点;多条时 CLI 逐条调用并聚合 - // 结果,单条保持原始输出形态不变。 - if len(scopes) == 1 { - params := devAppCloneParams(locator) - params["scopeValue"] = scopes[0] - return runDevAppTool(runner, cmd, devAppPermissionRmTool, params) - } - results := make([]map[string]any, 0, len(scopes)) - failed := 0 - for _, scope := range scopes { - params := devAppCloneParams(locator) - params["scopeValue"] = scope - invocation := executor.NewHelperInvocation( - cobracmd.LegacyCommandPath(cmd), - devAppProduct, - devAppPermissionRmTool, - params, - ) - invocation.DryRun = commandDryRun(cmd) - entry := map[string]any{"scopeValue": scope} - result, err := runner.Run(cmd.Context(), invocation) - if err != nil { - entry["error"] = err.Error() - failed++ - } else { - entry["result"] = result - } - results = append(results, entry) - } - // 逐条 best-effort:把整体成败显式写进 payload(ok / failedCount), - // 让读 JSON 的 agent 一眼看出部分失败,而不是只看 exit code 误判全成功。 - return writeCommandPayload(cmd, map[string]any{ - "results": results, - "ok": failed == 0, - "total": len(results), - "failedCount": failed, - }) + params["scopeValues"] = scopes + return runDevAppTool(runner, cmd, devAppPermissionRmTool, params) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("permissions", "", "待取消权限点 scopeValue,多个用逗号或分号分隔(多条时逐条调用并聚合结果)") - cmd.Flags().String("permission", "", "--permissions 的兼容别名,单个权限点 scopeValue") - cmd.Flags().String("scope", "", "--permissions 的兼容别名") - _ = cmd.Flags().MarkHidden("permission") - _ = cmd.Flags().MarkHidden("scope") + cmd.Flags().String("scope-values", "", "待取消权限点 scopeValue,多个用逗号或分号分隔") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppPermissionRmTool) return cmd } @@ -813,6 +787,7 @@ func newDevAppMemberListCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppMemberListTool) return cmd } @@ -820,7 +795,7 @@ func newDevAppMemberAddCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "添加开放平台应用成员", - Example: " dws dev app member add --unified-app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws dev app member add --unified-app-id --member-user-ids userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -829,6 +804,7 @@ func newDevAppMemberAddCommand(runner executor.Runner) *cobra.Command { } registerDevAppMemberMutationFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppMemberAddTool) return cmd } @@ -836,7 +812,7 @@ func newDevAppMemberRemoveCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "移除开放平台应用成员", - Example: " dws dev app member remove --unified-app-id --users userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws dev app member remove --unified-app-id --member-user-ids userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -845,6 +821,7 @@ func newDevAppMemberRemoveCommand(runner executor.Runner) *cobra.Command { } registerDevAppMemberMutationFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppMemberRemoveTool) return cmd } @@ -853,7 +830,7 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { Use: "config", Short: "更新开放平台应用安全配置", Example: " dws dev app security config --unified-app-id " + - "--ip-whitelist 192.0.2.10 --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --dry-run", + "--ip-whitelist 192.0.2.10 --redirect-urls https://callback.example.invalid/callback --sso-urls https://sso.example.invalid/sso --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -871,25 +848,26 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { params["ipWhitelist"] = values updates++ } - if values := parseDevAppListFlag(cmd, "redirect-url"); len(values) > 0 { + if values := parseDevAppListFlag(cmd, "redirect-urls"); len(values) > 0 { params["redirectUrls"] = values updates++ } - if values := parseDevAppListFlag(cmd, "sso-url"); len(values) > 0 { + if values := parseDevAppListFlag(cmd, "sso-urls"); len(values) > 0 { params["ssoUrls"] = values updates++ } if updates == 0 { - return apperrors.NewValidation("至少提供一项安全配置:--ip-whitelist、--redirect-url 或 --sso-url") + return apperrors.NewValidation("至少提供一项安全配置:--ip-whitelist、--redirect-urls 或 --sso-urls") } return runDevAppTool(runner, cmd, devAppSecurityConfigTool, params) }, } addDevAppUnifiedIDFlag(cmd) cmd.Flags().String("ip-whitelist", "", "出口 IP 白名单,多个用逗号或分号分隔(整组覆盖,非追加)") - cmd.Flags().String("redirect-url", "", "登录重定向 URL,多个用逗号或分号分隔(整组覆盖,非追加)") - cmd.Flags().String("sso-url", "", "端内免登地址,多个用逗号或分号分隔(整组覆盖,非追加)") + cmd.Flags().String("redirect-urls", "", "登录重定向 URL,多个用逗号或分号分隔(整组覆盖,非追加)") + cmd.Flags().String("sso-urls", "", "端内免登地址,多个用逗号或分号分隔(整组覆盖,非追加)") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppSecurityConfigTool) return cmd } @@ -901,7 +879,7 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "submit", Short: "异步提交钉钉智能体机器人创建任务(支持失败重试)", - Example: " dws dev app robot submit --app-name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", + Example: " dws dev app robot submit --name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -914,8 +892,8 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { } // submit_robot_create 的 schema 把图标字段标为必填(空值时服务端用默认图标), // 因此即使用户未提供也补空串占位。 - if _, ok := params["icon"]; !ok { - params["icon"] = "" + if _, ok := params["iconMediaId"]; !ok { + params["iconMediaId"] = "" } if _, ok := params["previewMediaId"]; !ok { params["previewMediaId"] = "" @@ -927,6 +905,7 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { registerDevAppRobotCreateFlags(cmd) cmd.Flags().String("task-id", "", "失败重试时传入原 taskId;为空时服务端自动生成") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppRobotSubmitTool) return cmd } @@ -947,6 +926,7 @@ func newDevAppRobotResultCommand(runner executor.Runner) *cobra.Command { } cmd.Flags().String("task-id", "", "提交创建任务时返回的 taskId (必填)") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppRobotResultTool) return cmd } @@ -967,6 +947,7 @@ func newDevAppRobotConfigGetCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppRobotConfigGetTool) return cmd } @@ -995,7 +976,7 @@ func newDevAppRobotConfigCommand(runner executor.Runner) *cobra.Command { return err } if updates == 0 { - return apperrors.NewValidation("至少提供一项机器人配置字段,如 --name、--brief、--desc、--icon、--outgoing-url、--event-url、--mode、--skills") + return apperrors.NewValidation("至少提供一项机器人配置字段,如 --name、--brief、--desc、--icon-media-id、--outgoing-url、--event-callback-url、--mode、--skills") } return runDevAppTool(runner, cmd, devAppRobotConfigUpsertTool, params) }, @@ -1003,6 +984,7 @@ func newDevAppRobotConfigCommand(runner executor.Runner) *cobra.Command { addDevAppUnifiedIDFlag(cmd) registerDevAppRobotConfigFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppRobotConfigUpsertTool) return cmd } @@ -1028,6 +1010,7 @@ func newDevAppRobotEnableCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppRobotEnableTool) return cmd } @@ -1051,21 +1034,22 @@ func newDevAppRobotOfflineCommand(runner executor.Runner) *cobra.Command { } addDevAppUnifiedIDFlag(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppRobotOfflineTool) return cmd } func registerDevAppRobotCreateFlags(cmd *cobra.Command) { - cmd.Flags().String("app-name", "", "智能体应用名称,长度 2-20,企业内唯一 (必填)") + cmd.Flags().String("name", "", "智能体应用名称,长度 2-20,企业内唯一 (必填)") cmd.Flags().String("robot-name", "", "承载机器人名称,用于客户端展示 (必填)") cmd.Flags().String("desc", "", "机器人功能描述,不超过 200 字 (必填)") - cmd.Flags().String("icon", "", "机器人图标 mediaId;为空时使用默认图标") - cmd.Flags().String("preview", "", "机器人预览图 mediaId;为空时复用图标") + cmd.Flags().String("icon-media-id", "", "机器人图标 mediaId;为空时使用默认图标") + cmd.Flags().String("preview-media-id", "", "机器人预览图 mediaId;为空时复用图标") } func devAppRobotCreateParams(cmd *cobra.Command) (map[string]any, error) { - appName := devAppStringFlag(cmd, "app-name") - if appName == "" { - return nil, apperrors.NewValidation("--app-name 为必填") + name := devAppStringFlag(cmd, "name") + if name == "" { + return nil, apperrors.NewValidation("--name 为必填") } robotName := devAppStringFlag(cmd, "robot-name") if robotName == "" { @@ -1076,12 +1060,12 @@ func devAppRobotCreateParams(cmd *cobra.Command) (map[string]any, error) { return nil, apperrors.NewValidation("--desc 为必填") } params := map[string]any{ - "appName": appName, + "name": name, "robotName": robotName, "desc": desc, } - devAppPutString(params, "icon", devAppStringFlag(cmd, "icon")) - devAppPutString(params, "previewMediaId", devAppStringFlag(cmd, "preview")) + devAppPutString(params, "iconMediaId", devAppStringFlag(cmd, "icon-media-id")) + devAppPutString(params, "previewMediaId", devAppStringFlag(cmd, "preview-media-id")) return params, nil } @@ -1089,9 +1073,9 @@ func registerDevAppRobotConfigFlags(cmd *cobra.Command) { cmd.Flags().String("name", "", "机器人名称") cmd.Flags().String("brief", "", "机器人简介") cmd.Flags().String("desc", "", "机器人描述") - cmd.Flags().String("icon", "", "机器人图标 mediaId") + cmd.Flags().String("icon-media-id", "", "机器人图标 mediaId") cmd.Flags().String("outgoing-url", "", "消息回调地址") - cmd.Flags().String("event-url", "", "事件回调地址") + cmd.Flags().String("event-callback-url", "", "事件回调地址") cmd.Flags().Int("mode", 0, "机器人模式枚举") cmd.Flags().String("skills", "", "技能列表,多个用逗号或分号分隔") cmd.Flags().Bool("add-scope", false, "是否自动添加机器人相关权限") @@ -1113,25 +1097,25 @@ func devAppRobotConfigParams(cmd *cobra.Command, appID string) (map[string]any, setString("name", "name") setString("brief", "brief") setString("desc", "desc") - setString("icon", "icon") + setString("iconMediaId", "icon-media-id") setString("outgoingUrl", "outgoing-url") - setString("eventUrl", "event-url") + setString("eventCallbackUrl", "event-callback-url") if cmd.Flags().Changed("mode") { params["mode"] = devAppIntFlag(cmd, "mode") updates++ } if cmd.Flags().Changed("add-scope") { value, _ := cmd.Flags().GetBool("add-scope") - params["isAddScope"] = value + params["addScope"] = value updates++ } if cmd.Flags().Changed("disable-ssl-verify") { value, _ := cmd.Flags().GetBool("disable-ssl-verify") - params["disableSslVerify"] = value + params["disableSSLVerify"] = value updates++ } if values := parseDevAppListFlag(cmd, "skills"); len(values) > 0 { - params["skillList"] = values + params["skills"] = values updates++ } for _, item := range []struct{ key, flag string }{ @@ -1185,6 +1169,7 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { cmd.Flags().String("version", "", "版本号,如 1.0.1") cmd.Flags().String("desc", "", "版本描述") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppVersionCreateTool) return cmd } @@ -1208,6 +1193,7 @@ func newDevAppVersionListCommand(runner executor.Runner) *cobra.Command { addDevAppUnifiedIDFlag(cmd) registerDevAppCursorFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppVersionListTool) return cmd } @@ -1228,6 +1214,7 @@ func newDevAppVersionGetCommand(runner executor.Runner) *cobra.Command { } addDevAppVersionLocatorFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppVersionDetailTool) return cmd } @@ -1252,13 +1239,14 @@ func newDevAppVersionCheckApprovalCommand(runner executor.Runner) *cobra.Command } addDevAppVersionLocatorFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppVersionPublishTool) return cmd } func newDevAppVersionPublishCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "publish", - Short: "发布指定版本(含高敏权限需 --confirm-sensitive)", + Short: "发布指定版本(含高敏权限需 --confirmed-sensitive)", Example: " dws dev app version publish --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, @@ -1275,18 +1263,19 @@ func newDevAppVersionPublishCommand(runner executor.Runner) *cobra.Command { // params 的 agent 误判(publish --dry-run 时回显 dryRun:false)。 // 见 docs/upstream-todo.md:上游需把入参 dryRun 改名 precheckOnly。 params["precheckOnly"] = false - if cmd.Flags().Changed("confirm-sensitive") { - value, _ := cmd.Flags().GetBool("confirm-sensitive") + if cmd.Flags().Changed("confirmed-sensitive") { + value, _ := cmd.Flags().GetBool("confirmed-sensitive") params["confirmedSensitive"] = value } - devAppPutString(params, "approverUserId", devAppStringFlag(cmd, "approver")) + devAppPutString(params, "approverUserId", devAppStringFlag(cmd, "approver-user-id")) return runDevAppTool(runner, cmd, devAppVersionPublishTool, params) }, } addDevAppVersionLocatorFlags(cmd) - cmd.Flags().Bool("confirm-sensitive", false, "确认发布包含高敏权限的版本") - cmd.Flags().String("approver", "", "灰度选人模式下指定审批人 userId") + cmd.Flags().Bool("confirmed-sensitive", false, "确认发布包含高敏权限的版本") + cmd.Flags().String("approver-user-id", "", "灰度选人模式下指定审批人 userId") preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppVersionPublishTool) return cmd } @@ -1307,6 +1296,7 @@ func newDevAppVersionStatusCommand(runner executor.Runner) *cobra.Command { } addDevAppVersionLocatorFlags(cmd) preferLegacyLeaf(cmd) + annotateDevAppTool(cmd, devAppVersionStatusTool) return cmd } @@ -1344,7 +1334,7 @@ func requiredDevAppUnifiedID(cmd *cobra.Command) (string, error) { func registerDevAppMemberMutationFlags(cmd *cobra.Command) { addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("users", "", "成员 userId 列表,多个用逗号分隔 (必填)") + cmd.Flags().String("member-user-ids", "", "成员 userId 列表,多个用逗号分隔 (必填)") cmd.Flags().String("member-type", "", "成员类型,如 DEVELOPER (必填)") } @@ -1373,6 +1363,18 @@ func runDevAppMemberMutation(runner executor.Runner, cmd *cobra.Command, tool, o return runDevAppTool(runner, cmd, tool, params) } +// annotateDevAppTool tags a leaf command with the MCP tool name it invokes, so +// `dws schema dev.app...` can resolve command → tool → live op-app schema +// without re-deriving the mapping. The annotation is the single source of truth +// for the schema renderer (see internal/cli/dev_schema.go). +func annotateDevAppTool(cmd *cobra.Command, tool string) *cobra.Command { + if cmd.Annotations == nil { + cmd.Annotations = map[string]string{} + } + cmd.Annotations["mcp-tool"] = tool + return cmd +} + func runDevAppTool(runner executor.Runner, cmd *cobra.Command, tool string, params map[string]any) error { invocation := executor.NewHelperInvocation( cobracmd.LegacyCommandPath(cmd), @@ -1392,13 +1394,13 @@ func runDevAppTool(runner executor.Runner, cmd *cobra.Command, tool string, para } func requiredDevAppUsers(cmd *cobra.Command) ([]string, error) { - usersRaw, _ := cmd.Flags().GetString("users") + usersRaw, _ := cmd.Flags().GetString("member-user-ids") if strings.TrimSpace(usersRaw) == "" { - return nil, apperrors.NewValidation("--users 为必填") + return nil, apperrors.NewValidation("--member-user-ids 为必填") } users := splitDevAppList(usersRaw) if len(users) == 0 { - return nil, apperrors.NewValidation("--users 至少包含一个 userId") + return nil, apperrors.NewValidation("--member-user-ids 至少包含一个 userId") } return users, nil } @@ -1465,8 +1467,7 @@ func devAppCloneParams(src map[string]any) map[string]any { } func devAppPermissionScopes(cmd *cobra.Command) []string { - values := parseDevAppListFlag(cmd, "permissions") - values = append(values, devAppFlagOrFallback(cmd, "scope", "permission")) + values := parseDevAppListFlag(cmd, "scope-values") out := make([]string, 0, len(values)) for _, value := range values { for _, part := range splitDevAppList(value) { diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 030481d4..42437bb7 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -53,7 +53,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "add multiple users", cmd: "add", - args: []string{"--unified-app-id", "app-001", "--users", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, + args: []string{"--unified-app-id", "app-001", "--member-user-ids", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, wantTool: "add_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", @@ -64,7 +64,7 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "remove trims users", cmd: "remove", - args: []string{"--unified-app-id", "app-001", "--users", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, + args: []string{"--unified-app-id", "app-001", "--member-user-ids", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, wantTool: "remove_dev_app_members", wantParams: map[string]any{ "unifiedAppId": "app-001", @@ -144,13 +144,13 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { }{ { name: "submit async fills media placeholders", - args: []string{"robot", "submit", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--task-id", "t-1", "--yes"}, + args: []string{"robot", "submit", "--name", "智能体", "--robot-name", "小助手", "--desc", "审批问答", "--task-id", "t-1", "--yes"}, wantTool: "submit_robot_create_task", wantParams: map[string]any{ - "appName": "智能体", + "name": "智能体", "robotName": "小助手", "desc": "审批问答", - "icon": "", + "iconMediaId": "", "previewMediaId": "", "taskId": "t-1", }, @@ -176,8 +176,8 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { "name": "小助手", "brief": "审批助手", "mode": 2, - "skillList": []string{"qa", "approval"}, - "isAddScope": true, + "skills": []string{"qa", "approval"}, + "addScope": true, }, }, { @@ -254,7 +254,7 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { }, { name: "publish sets precheckOnly false and sensitive", - args: []string{"version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1", "--confirm-sensitive", "--approver", "user-1", "--yes"}, + args: []string{"version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1", "--confirmed-sensitive", "--approver-user-id", "user-1", "--yes"}, wantTool: "publish_dev_app_version", wantParams: map[string]any{ "unifiedAppId": "u-1", @@ -334,7 +334,7 @@ func TestDevAppListBuildsListByConditionParams(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"list", "--name", "Waker", "--cursor", "tok-2", "--page-size", "5", "--sort", "gmt_modified", "--order", "desc"}) + root.SetArgs([]string{"list", "--name", "Waker", "--cursor", "tok-2", "--page-size", "5", "--sort-type", "gmt_modified", "--sort-order", "desc"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -345,7 +345,7 @@ func TestDevAppListBuildsListByConditionParams(t *testing.T) { want := map[string]any{ "cursor": "tok-2", "pageSize": 5, - "appName": "Waker", + "name": "Waker", "sortType": "gmt_modified", "sortOrder": "desc", } @@ -399,7 +399,7 @@ func TestDevAppCreateUsesCurrentInnerToolAndWriteGuard(t *testing.T) { t.Fatalf("Tool = %q, want create_dev_app", got) } - want := map[string]any{"appName": "Demo", "desc": "internal app"} + want := map[string]any{"name": "Demo", "desc": "internal app"} if !reflect.DeepEqual(runner.last.Params, want) { t.Fatalf("Params = %#v, want %#v", runner.last.Params, want) } @@ -473,7 +473,9 @@ func TestDevAppLifecycleBuildsLocatorParams(t *testing.T) { // fail-closed (abort), never a silent proceed. func TestDevAppDeleteConfirmName(t *testing.T) { t.Run("matching name proceeds", func(t *testing.T) { - runner := &devAppResponseRunner{response: map[string]any{"appName": "DemoApp"}} + // get_dev_app 真实返回的应用名字段是 name(不是 appName)—— + // fixture 用 name 才能覆盖真实契约,避免 delete 取名回归。 + runner := &devAppResponseRunner{response: map[string]any{"name": "DemoApp"}} root := newDevAppTestRoot(runner) var out bytes.Buffer root.SetOut(&out) @@ -488,7 +490,7 @@ func TestDevAppDeleteConfirmName(t *testing.T) { }) t.Run("mismatched name aborts", func(t *testing.T) { - runner := &devAppResponseRunner{response: map[string]any{"appName": "RealName"}} + runner := &devAppResponseRunner{response: map[string]any{"name": "RealName"}} root := newDevAppTestRoot(runner) var out bytes.Buffer root.SetOut(&out) @@ -520,6 +522,42 @@ func TestDevAppDeleteConfirmName(t *testing.T) { }) } +// TestDevAppEventSubscribeUsesEventCodes locks the param contract: the server's +// subscribe/unsubscribe tools (plural: subscribe_dev_app_events) read eventCodes +// as an ARRAY — real-device confirmed against the updated MCP schema. +func TestDevAppEventSubscribeUsesEventCodes(t *testing.T) { + for _, tc := range []struct { + name string + args []string + wantTool string + }{ + {"subscribe", []string{"dev", "app", "event", "subscribe", "--unified-app-id", "u-1", "--event-codes", "a,b", "--yes"}, "subscribe_dev_app_events"}, + {"unsubscribe", []string{"dev", "app", "event", "unsubscribe", "--unified-app-id", "u-1", "--event-codes", "a,b", "--yes"}, "unsubscribe_dev_app_events"}, + } { + t.Run(tc.name, func(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs(tc.args) + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + if runner.last.Tool != tc.wantTool { + t.Fatalf("Tool = %q, want %q", runner.last.Tool, tc.wantTool) + } + got, ok := runner.last.Params["eventCodes"].([]string) + if !ok || len(got) != 2 || got[0] != "a" || got[1] != "b" { + t.Fatalf("eventCodes = %#v, want []string{a,b}", runner.last.Params["eventCodes"]) + } + if _, bad := runner.last.Params["eventCode"]; bad { + t.Fatal("must not send singular eventCode") + } + }) + } +} + func TestDevAppWebappCommandsBuildParams(t *testing.T) { cases := []struct { name string @@ -576,7 +614,7 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { }{ { name: "list", - args: []string{"permission", "list", "--unified-app-id", "u-123", "--keyword", "手机号", "--status", "all", "--cursor", "tok-3", "--page-size", "5"}, + args: []string{"permission", "list", "--unified-app-id", "u-123", "--keyword", "手机号", "--auth-status", "all", "--cursor", "tok-3", "--page-size", "5"}, wantTool: "list_dev_app_permissions", wantParams: map[string]any{ "unifiedAppId": "u-123", @@ -588,7 +626,7 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { }, { name: "add", - args: []string{"permission", "add", "--unified-app-id", "u-123", "--permissions", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, + args: []string{"permission", "add", "--unified-app-id", "u-123", "--scope-values", "Contact.User.mobile,qyapi_robot_sendmsg", "--yes"}, wantTool: "apply_dev_app_permissions", wantParams: map[string]any{ "unifiedAppId": "u-123", @@ -597,11 +635,11 @@ func TestDevAppPermissionCommandsBuildParams(t *testing.T) { }, { name: "remove", - args: []string{"permission", "remove", "--unified-app-id", "u-123", "--permission", "Contact.User.mobile", "--yes"}, + args: []string{"permission", "remove", "--unified-app-id", "u-123", "--scope-values", "Contact.User.mobile", "--yes"}, wantTool: "remove_dev_app_permissions", wantParams: map[string]any{ "unifiedAppId": "u-123", - "scopeValue": "Contact.User.mobile", + "scopeValues": []string{"Contact.User.mobile"}, }, }, } @@ -684,9 +722,9 @@ func TestDevAppMemberCommandsValidateRequiredFlags(t *testing.T) { wantErr string }{ {name: "list requires app", cmd: "list", args: nil, wantErr: "--unified-app-id 为必填"}, - {name: "add requires users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users 为必填"}, - {name: "add rejects empty users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--users", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--users 至少包含一个 userId"}, - {name: "remove requires member type", cmd: "remove", args: []string{"--unified-app-id", "app-001", "--users", "userId1", "--dry-run"}, wantErr: "--member-type 为必填"}, + {name: "add requires users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--member-user-ids 为必填"}, + {name: "add rejects empty users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-user-ids", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--member-user-ids 至少包含一个 userId"}, + {name: "remove requires member type", cmd: "remove", args: []string{"--unified-app-id", "app-001", "--member-user-ids", "userId1", "--dry-run"}, wantErr: "--member-type 为必填"}, } for _, tc := range cases { @@ -722,8 +760,8 @@ func TestDevAppSecurityConfigBuildsOnlyProvidedLists(t *testing.T) { "dev", "app", "security", "config", "--unified-app-id", "app-001", "--ip-whitelist", "192.0.2.10,192.0.2.11", - "--redirect-url", "https://callback.example.invalid/callback", - "--sso-url", "https://sso.example.invalid/sso", + "--redirect-urls", "https://callback.example.invalid/callback", + "--sso-urls", "https://sso.example.invalid/sso", "--dry-run", }) @@ -754,7 +792,7 @@ func TestDevAppSecurityConfigOmitsAbsentOptionalLists(t *testing.T) { var out bytes.Buffer root.SetOut(&out) root.SetErr(&out) - root.SetArgs([]string{"dev", "app", "security", "config", "--unified-app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback", "--dry-run"}) + root.SetArgs([]string{"dev", "app", "security", "config", "--unified-app-id", "app-001", "--redirect-urls", "https://callback.example.invalid/callback", "--dry-run"}) if err := root.Execute(); err != nil { t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) @@ -781,7 +819,7 @@ func TestDevAppSecurityConfigRequiresAtLeastOneConfig(t *testing.T) { if err == nil { t.Fatal("Execute() error = nil, want validation error") } - if !strings.Contains(err.Error(), "至少提供一项安全配置:--ip-whitelist、--redirect-url 或 --sso-url") { + if !strings.Contains(err.Error(), "至少提供一项安全配置:--ip-whitelist、--redirect-urls 或 --sso-urls") { t.Fatalf("error = %q", err.Error()) } if runner.last.Tool != "" { @@ -796,15 +834,15 @@ func TestDevAppMemberAndSecurityRequireWriteGuard(t *testing.T) { }{ { name: "member add", - args: []string{"dev", "app", "member", "add", "--unified-app-id", "app-001", "--users", "userId1", "--member-type", "DEVELOPER"}, + args: []string{"dev", "app", "member", "add", "--unified-app-id", "app-001", "--member-user-ids", "userId1", "--member-type", "DEVELOPER"}, }, { name: "member remove", - args: []string{"dev", "app", "member", "remove", "--unified-app-id", "app-001", "--users", "userId1", "--member-type", "DEVELOPER"}, + args: []string{"dev", "app", "member", "remove", "--unified-app-id", "app-001", "--member-user-ids", "userId1", "--member-type", "DEVELOPER"}, }, { name: "security config", - args: []string{"dev", "app", "security", "config", "--unified-app-id", "app-001", "--redirect-url", "https://callback.example.invalid/callback"}, + args: []string{"dev", "app", "security", "config", "--unified-app-id", "app-001", "--redirect-urls", "https://callback.example.invalid/callback"}, }, } diff --git a/skills/multi/dingtalk-dev/SKILL.md b/skills/multi/dingtalk-dev/SKILL.md index 5094e751..3354b03c 100644 --- a/skills/multi/dingtalk-dev/SKILL.md +++ b/skills/multi/dingtalk-dev/SKILL.md @@ -1,7 +1,7 @@ --- name: dingtalk-dev description: 钉钉开放平台企业内部应用全生命周期管理。应用是容器:凭证/权限点/成员/安全配置/能力扩展(网页应用/机器人)/版本,需审批的变更要发版本才生效。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/customKey/应用权限/权限点/scopeValue/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/本地调试机器人/把机器人接到本地agent/建联/connect。 -cli_version: "1.0.15+" +cli_version: "1.0.37+" metadata: category: product stability: experimental @@ -12,14 +12,6 @@ metadata: # 钉钉开放平台应用管理 Skill -> 🧪 **EXPERIMENTAL · 试验版 / Preview** — dev 为预览能力,仅在 multi 模式提供,接口与命名后续可能调整。问题请提 issue 反馈。 - -> **PREREQUISITE:** Read the root `dws` skill first for auth, global flags, product routing, URL preflight, error codes, and safety rules. The `dws` binary must be on PATH. - - - -> `dws dev app ...` 是内置 helper 命令,不依赖 MCP 服务发现。首次使用前(或命令报错时)用 `dws dev app --help` 验证可用,验证过就不用每次重复;若报 `unknown command`,说明当前 dws 构建不含 dev 命令组(预览能力,`dws upgrade` 装不到),按仓库 `docs/dev-agent-install-guide.md` 用 install-dev.sh 从 `feat/dws-dev` 分支安装。 - ## 概念地图 先建立领域模型,再看命令——所有命令都是对这张图上某个节点的操作,用户的模糊意图先映射到节点再选命令。 @@ -104,7 +96,7 @@ metadata: 端到端任务都是「定位应用 → 改容器某节点 →(按审批需要)走版本生效 → 回读验证」。常见链路(每步先 `--dry-run` 确认再 `--yes`,细节进对应产品文件): - **建钉钉里打开的网页应用**:`app create` → `webapp config` → `version create` → `check-approval` → `publish` →(回读 `version status` 到 `RELEASE`)。 -- **权限从申请到生效**:`permission list`(选 `scopeValue`)→ `permission add` →(`requiredApproval` 的权限)`version create` → `check-approval`(看是否需审批+候选审批人)→ `publish --approver <用户选的>` → `version status`。 +- **权限从申请到生效**:`permission list`(选 `scopeValue`)→ `permission add` →(`requiredApproval` 的权限)`version create` → `check-approval`(看是否需审批+候选审批人)→ `publish --approver-user-id <用户选的>` → `version status`。 - **做答疑机器人**:`robot submit` → `robot result`(`SUCCESS` 拿 clientId/secret),或现有应用 `robot config` →(发版本)→ 本地调试 `dev connect`。 - **查"为什么没生效 / 机器人搜不到 / 权限加了还报错"**:先 `version status`——改配置 ≠ 生效,未发到 `RELEASE` 就不生效。 diff --git a/skills/multi/dingtalk-dev/references/app.md b/skills/multi/dingtalk-dev/references/app.md index 9241773c..6f705d76 100644 --- a/skills/multi/dingtalk-dev/references/app.md +++ b/skills/multi/dingtalk-dev/references/app.md @@ -20,7 +20,7 @@ dws dev app list --format json dws dev app list --name DemoApp --format json dws dev app list --app-key dingxxx --format json -dws dev app list --creator 张三 --sort gmt_modified --order desc --format json +dws dev app list --creator 张三 --sort-type gmt_modified --sort-order desc --format json ``` | CLI | 说明 | @@ -30,8 +30,8 @@ dws dev app list --creator 张三 --sort gmt_modified --order desc --format json | `--name` / `--keyword` | 应用名搜索 | | `--app-key` | 按 appKey/clientId 过滤 | | `--creator` | 创建人关键词 | -| `--sort` | 如 `gmt_modified` | -| `--order` | `asc` / `desc` | +| `--sort-type` | 如 `gmt_modified` | +| `--sort-order` | `asc` / `desc` | > 分页用游标:首页不传 `--cursor`,出参带 `nextCursor`(空=到底);续翻把它原样回传。旧 `--page`/`--limit`/`--offset` 仍隐藏兼容(跳页/老脚本)。出参字段见 SKILL.md「通用出参约定」。 @@ -67,7 +67,7 @@ dws dev app create --name DemoApp --desc "内部应用" --yes --format json |-----|------| | `--name` | 是 | | `--desc` | 否 | -| `--icon` | 否 | +| `--icon-media-id` | 否 | ## 修改应用 @@ -76,7 +76,7 @@ dws dev app update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry- dws dev app update --unified-app-id ID --name DemoApp2 --desc "新描述" --yes --format json ``` -至少提供一个更新字段:`--name` / `--desc` / `--icon`。 +至少提供一个更新字段:`--name` / `--desc` / `--icon-media-id`。 ## 停用 / 启用应用 diff --git a/skills/multi/dingtalk-dev/references/event.md b/skills/multi/dingtalk-dev/references/event.md index 9158706f..89d6c1e5 100644 --- a/skills/multi/dingtalk-dev/references/event.md +++ b/skills/multi/dingtalk-dev/references/event.md @@ -28,15 +28,14 @@ dws dev app event list --unified-app-id ID --cursor --page-size 50 --for ## 订阅事件 ```bash -dws dev app event subscribe --unified-app-id ID --event-types chat_add_member_org --callback-url https://example.com/event --dry-run --format json -dws dev app event subscribe --unified-app-id ID --event-types chat_add_member_org,chat_remove_member_org --callback-url https://example.com/event --yes --format json +dws dev app event subscribe --unified-app-id ID --event-codes chat_add_member_org --dry-run --format json +dws dev app event subscribe --unified-app-id ID --event-codes chat_add_member_org,chat_remove_member_org --yes --format json ``` | CLI | 说明 | |-----|------| | `--unified-app-id` | 应用定位 | -| `--event-types` | 事件类型,多个逗号分隔 | -| `--callback-url` | 事件回调地址 | +| `--event-codes` | 事件码,多个逗号分隔(一次可订阅/退订多个)| **规则:** - 写操作,先 `--dry-run` 预览,确认后 `--yes`。 @@ -46,14 +45,14 @@ dws dev app event subscribe --unified-app-id ID --event-types chat_add_member_or ## 取消订阅 ```bash -dws dev app event unsubscribe --unified-app-id ID --event-types chat_add_member_org --dry-run --format json -dws dev app event unsubscribe --unified-app-id ID --event-types chat_add_member_org --yes --format json +dws dev app event unsubscribe --unified-app-id ID --event-codes chat_add_member_org --dry-run --format json +dws dev app event unsubscribe --unified-app-id ID --event-codes chat_add_member_org --yes --format json ``` | CLI | 说明 | |-----|------| | `--unified-app-id` | 应用定位 | -| `--event-types` | 要取消的事件类型,多个逗号分隔 | +| `--event-codes` | 要取消的事件类型,多个逗号分隔 | **规则:** - 写操作,先 `--dry-run` 预览,确认后 `--yes`。 diff --git a/skills/multi/dingtalk-dev/references/member.md b/skills/multi/dingtalk-dev/references/member.md index 251666c0..ba54e352 100644 --- a/skills/multi/dingtalk-dev/references/member.md +++ b/skills/multi/dingtalk-dev/references/member.md @@ -11,19 +11,19 @@ dws dev app member list --unified-app-id UNIFIED_APP_ID --format json ## 添加成员 ```bash -dws dev app member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws dev app member add --unified-app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --yes --format json +dws dev app member add --unified-app-id UNIFIED_APP_ID --member-user-ids userId1,userId2 --member-type DEVELOPER --dry-run --format json +dws dev app member add --unified-app-id UNIFIED_APP_ID --member-user-ids userId1,userId2 --member-type DEVELOPER --yes --format json ``` ## 移除成员 ```bash -dws dev app member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --dry-run --format json -dws dev app member remove --unified-app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json +dws dev app member remove --unified-app-id UNIFIED_APP_ID --member-user-ids userId1 --member-type DEVELOPER --dry-run --format json +dws dev app member remove --unified-app-id UNIFIED_APP_ID --member-user-ids userId1 --member-type DEVELOPER --yes --format json ``` | CLI | 说明 | |-----|------| | `--unified-app-id` | 统一应用 ID(必填) | -| `--users` | userId 列表,逗号/分号分隔(必填) | +| `--member-user-ids` | userId 列表,逗号/分号分隔(必填) | | `--member-type` | 成员类型,如 `DEVELOPER`(必填) | diff --git a/skills/multi/dingtalk-dev/references/permission.md b/skills/multi/dingtalk-dev/references/permission.md index e5c420e3..5c1bd874 100644 --- a/skills/multi/dingtalk-dev/references/permission.md +++ b/skills/multi/dingtalk-dev/references/permission.md @@ -8,24 +8,24 @@ ```bash dws dev app permission list --unified-app-id ID --format json -dws dev app permission list --unified-app-id ID --keyword "机器人发消息" --status UNAUTHED --format json +dws dev app permission list --unified-app-id ID --keyword "机器人发消息" --auth-status UNAUTHED --format json dws dev app permission list --unified-app-id ID --scope-type SNS --format json -dws dev app permission list --unified-app-id ID --scope qyapi_robot_sendmsg --format json +dws dev app permission list --unified-app-id ID --scope-value qyapi_robot_sendmsg --format json ``` | CLI | 说明 | |-----|------| | `--unified-app-id` | 应用定位 | | `--keyword` | 权限名/API 名关键词 | -| `--status` | `ALL` / `AUTHED` / `UNAUTHED` | +| `--auth-status` | `ALL` / `AUTHED` / `UNAUTHED` | | `--scope-type` | `APP` / `SNS`,为空返回两者 | -| `--scope` | 单权限详情模式 | +| `--scope-value` | 单权限详情模式 | | `--cursor` | 游标令牌:首次留空,续翻传上次 `nextCursor` | | `--page-size` | 单页条数,默认 20,建议不超过 50 | **状态判断:** -`--status` 是查询过滤条件: +`--auth-status` 是查询过滤条件: | authStatus | 含义 | |------------|------| @@ -59,7 +59,7 @@ dws dev app permission list --unified-app-id ID --cursor --page-size 50 **规则:** - `permission search` 和 `permission detail` 是 `list` 的 CLI 别名。 - 默认同时返回 APP 和 SNS 权限。 -- 列表模式只返回 `apiPreview`;`--scope` 详情模式返回完整 `apiList`。 +- 列表模式只返回 `apiPreview`;`--scope-value` 详情模式返回完整 `apiList`。 **scopeValue 选择顺序:** @@ -71,12 +71,12 @@ dws dev app permission list --unified-app-id ID --cursor --page-size 50 ## 申请权限 ```bash -dws dev app permission add --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json -dws dev app permission add --unified-app-id ID --permissions Contact.User.mobile,qyapi_robot_sendmsg --yes --format json +dws dev app permission add --unified-app-id ID --scope-values qyapi_robot_sendmsg --dry-run --format json +dws dev app permission add --unified-app-id ID --scope-values Contact.User.mobile,qyapi_robot_sendmsg --yes --format json ``` **规则:** -- `--permissions` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 +- `--scope-values` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 - 已开通跳过,不可编辑拒绝。 - `requiredApproval=true` 允许申请——写入版本变更,审批在版本发布时处理。 - 不在此处选审批人。 @@ -84,8 +84,8 @@ dws dev app permission add --unified-app-id ID --permissions Contact.User.mobile ## 取消权限 ```bash -dws dev app permission remove --unified-app-id ID --permissions qyapi_robot_sendmsg --dry-run --format json -dws dev app permission remove --unified-app-id ID --permissions qyapi_robot_sendmsg --yes --format json +dws dev app permission remove --unified-app-id ID --scope-values qyapi_robot_sendmsg --dry-run --format json +dws dev app permission remove --unified-app-id ID --scope-values qyapi_robot_sendmsg --yes --format json ``` -`--permissions` 多个逗号分隔;上游一次只取消一个权限点,多条时 CLI 逐条调用并返回 `results` 聚合数组。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 +`--scope-values` 多个逗号分隔;上游一次只取消一个权限点,多条时 CLI 逐条调用并返回 `results` 聚合数组。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 diff --git a/skills/multi/dingtalk-dev/references/robot.md b/skills/multi/dingtalk-dev/references/robot.md index 264da4e4..ae9e3192 100644 --- a/skills/multi/dingtalk-dev/references/robot.md +++ b/skills/multi/dingtalk-dev/references/robot.md @@ -15,8 +15,8 @@ ```bash # 提交任务 -dws dev app robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws dev app robot submit --app-name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json +dws dev app robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json +dws dev app robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json # → 返回 taskId # 轮询结果 @@ -52,7 +52,7 @@ dws dev app robot get --unified-app-id --format json - `status=1`:OFFLINE,机器人配置存在但处于停用/下线状态。 - `status=2`:ONLINE,机器人配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 - `robot get` 返回 `success=true` 且包含 `robotCode` 时,说明配置已落库,不是异步等待态。 -- ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-url`,或用 `dev connect` 接到本地 Agent(见 connect.md)。 +- ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-callback-url`,或用 `dev connect` 接到本地 Agent(见 connect.md)。 - 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 ### 配置(upsert)/ 启用 @@ -64,7 +64,7 @@ dws dev app robot get --unified-app-id --format json ```bash dws dev app robot config --unified-app-id --name 小助手 --brief 审批助手 \ --desc "处理审批相关问答" --outgoing-url https://example.com/msg \ - --event-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json + --event-callback-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json dws dev app robot enable --unified-app-id --dry-run --format json ``` @@ -76,9 +76,9 @@ dws dev app robot enable --unified-app-id --dry-run --format json | `--name` | 机器人名称 | | `--brief` | 简介 | | `--desc` | 描述 | -| `--icon` | 图标 mediaId | +| `--icon-media-id` | 图标 mediaId | | `--outgoing-url` | 消息回调地址 | -| `--event-url` | 事件回调地址 | +| `--event-callback-url` | 事件回调地址 | | `--mode` | 机器人模式枚举(整数) | | `--skills` | 技能列表,逗号/分号分隔 | | `--add-scope` | 自动添加机器人相关权限 | diff --git a/skills/multi/dingtalk-dev/references/security.md b/skills/multi/dingtalk-dev/references/security.md index 20884fed..47614b87 100644 --- a/skills/multi/dingtalk-dev/references/security.md +++ b/skills/multi/dingtalk-dev/references/security.md @@ -6,15 +6,15 @@ ```bash dws dev app security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json -dws dev app security config --unified-app-id UNIFIED_APP_ID --redirect-url https://callback.example.invalid/callback --sso-url https://sso.example.invalid/sso --yes --format json +dws dev app security config --unified-app-id UNIFIED_APP_ID --redirect-urls https://callback.example.invalid/callback --sso-urls https://sso.example.invalid/sso --yes --format json ``` | CLI | 说明 | |-----|------| | `--unified-app-id` | 统一应用 ID(必填) | | `--ip-whitelist` | 出口 IP 白名单,逗号/分号分隔 | -| `--redirect-url` | 登录重定向 URL,逗号/分号分隔 | -| `--sso-url` | 端内免登 URL,逗号/分号分隔 | +| `--redirect-urls` | 登录重定向 URL,逗号/分号分隔 | +| `--sso-urls` | 端内免登 URL,逗号/分号分隔 | 至少提供一个配置字段。 diff --git a/skills/multi/dingtalk-dev/references/version.md b/skills/multi/dingtalk-dev/references/version.md index 95c2456a..9dc75508 100644 --- a/skills/multi/dingtalk-dev/references/version.md +++ b/skills/multi/dingtalk-dev/references/version.md @@ -12,7 +12,7 @@ permission add(requiredApproval=true 写入版本变更) → version create 创建版本 → version check-approval 预检是否需要审批 / 审批人 - → version publish 发布(含高敏权限需 --confirm-sensitive) + → version publish 发布(含高敏权限需 --confirmed-sensitive) → version status 轮询发布/审批状态 ``` @@ -62,18 +62,18 @@ dws dev app version publish --unified-app-id --version-id --version-id --yes --format json # 含高敏权限时必须确认 -dws dev app version publish --unified-app-id --version-id --confirm-sensitive --yes --format json +dws dev app version publish --unified-app-id --version-id --confirmed-sensitive --yes --format json # 灰度选人模式指定审批人 -dws dev app version publish --unified-app-id --version-id --approver --yes --format json +dws dev app version publish --unified-app-id --version-id --approver-user-id --yes --format json ``` `publish` 是真实发布(区别于 `check-approval` 的预检不发布)。 | CLI | 说明 | |-----|------| -| `--confirm-sensitive` | 版本含高敏权限时必须确认 | -| `--approver` | 灰度选人模式指定审批人 userId | +| `--confirmed-sensitive` | 版本含高敏权限时必须确认 | +| `--approver-user-id` | 灰度选人模式指定审批人 userId | > 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`。二者是两个东西:`check-approval` 是只查审批要求不发布,`--dry-run` 是 CLI 要不要真调上游。发布前建议先 `check-approval`。 @@ -82,7 +82,7 @@ dws dev app version publish --unified-app-id --version-id --version-id --version-id Date: Wed, 17 Jun 2026 21:56:11 +0800 Subject: [PATCH 30/47] =?UTF-8?q?feat(dev):=20schema=20=E8=A6=86=E7=9B=96?= =?UTF-8?q?=20dev=20doc=20+=20dingtalk-dev=20skill=20=E7=B2=BE=E7=AE=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dws schema 支持 dev.doc.search:fetcher 按 source 取(op-app 钉死 pre/ devdoc 走运行时解析),给 doc search 叶子加 mcp-tool/mcp-source 注解 - dev doc search flag 对齐 MCP 参数名(--keyword/--size 为主,旧名隐藏兼容) - dingtalk-dev SKILL.md:顶部加 MUST DO(每次先 --help/schema 查),核心规则 瘦身、去重、去加粗/箭头符号 - references 精简:删参数表/flag 列表(schema/--help 已实时提供),只留状态机/ 覆盖语义/链路/gotcha;event 补"订阅前需先建联"业务规则;新增 recipes.md 端到端链路;典型任务收成指针 Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/dev-agent-test-cases.md | 2 +- internal/app/helper_tool_fetcher.go | 71 ++++++++---- internal/app/root.go | 2 +- internal/cli/dev_schema.go | 32 +++--- internal/cli/dev_schema_test.go | 4 +- internal/helpers/devapp.go | 1 + internal/helpers/devdoc.go | 41 ++++--- skills/multi/dingtalk-dev/SKILL.md | 73 +++++-------- skills/multi/dingtalk-dev/references/app.md | 102 +++--------------- .../dingtalk-dev/references/credentials.md | 21 +--- skills/multi/dingtalk-dev/references/event.md | 63 ++--------- .../multi/dingtalk-dev/references/member.md | 26 +---- .../dingtalk-dev/references/permission.md | 10 +- .../multi/dingtalk-dev/references/recipes.md | 29 +++++ skills/multi/dingtalk-dev/references/robot.md | 18 +--- .../multi/dingtalk-dev/references/security.md | 18 +--- .../multi/dingtalk-dev/references/version.md | 12 +-- .../multi/dingtalk-dev/references/webapp.md | 25 +---- 18 files changed, 193 insertions(+), 357 deletions(-) create mode 100644 skills/multi/dingtalk-dev/references/recipes.md diff --git a/docs/dev-agent-test-cases.md b/docs/dev-agent-test-cases.md index 68cf3869..5afd852d 100644 --- a/docs/dev-agent-test-cases.md +++ b/docs/dev-agent-test-cases.md @@ -117,7 +117,7 @@ - **覆盖**:`event list` / `subscribe` / `unsubscribe`、`dev doc search`;错误码透传、文档 RAG。 - **期望(分步)**: 1. `event subscribe --unified-app-id --event-types chat_add_member_org --callback-url https://example.com/event --dry-run` → `--yes` → `event list` 回读 → `event unsubscribe --event-types chat_add_member_org --dry-run` → `--yes`。事件类型不确定先 `dev doc search` 查。 - 2. 错误码:业务错误 `ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,再 `dev doc search --query "errcode 62012 " --format json` 做官方文档 RAG,结论基于命中条目。 + 2. 错误码:业务错误 `ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,再 `dev doc search --keyword "errcode 62012 " --format json` 做官方文档 RAG,结论基于命中条目。 - **通过判据**:`--event-types` 逗号分隔共用回调,写操作先 dry-run;不编造事件类型/错误含义;先透传原始错误再走 RAG,结论不臆测、不编不存在的命令。 - **易错点**:编事件类型名;凭空解释错误码。 diff --git a/internal/app/helper_tool_fetcher.go b/internal/app/helper_tool_fetcher.go index 79f0e46e..700b319e 100644 --- a/internal/app/helper_tool_fetcher.go +++ b/internal/app/helper_tool_fetcher.go @@ -15,48 +15,77 @@ package app import ( "context" + "fmt" "sync" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/transport" ) -// newDevAppToolFetcher returns a cli.HelperToolFetcher that loads the op-app -// (devapp) MCP tools/list LIVE and projects each tool into a cli.HelperToolSchema -// (name, description, inputSchema properties/required). It is injected into the -// schema command so the cli package can render `dws schema dev.*` from real -// server schema without importing app/transport. +// newHelperToolFetcher returns a cli.HelperToolFetcher that loads a helper MCP +// server's tools/list LIVE (by source) and projects each tool into a +// cli.HelperToolSchema (name, description, inputSchema properties/required). It +// is injected into the schema command so the cli package can render +// `dws schema dev.*` from real server schema without importing app/transport. // -// The fetch is memoized per process (sync.Once on success) so repeated -// `dws schema dev.*` in one invocation hit the network at most once. A failed +// Sources: "op-app" backs the dev app commands (pinned endpoint); "devdoc" +// backs `dws dev doc search` (endpoint resolved dynamically, see +// helperSourceEndpoint). Results are memoized per source per process so +// repeated `dws schema dev.*` hit the network at most once per source. A failed // fetch is not cached, allowing a later retry within the same process. -func newDevAppToolFetcher() cli.HelperToolFetcher { +func newHelperToolFetcher() cli.HelperToolFetcher { var ( - once sync.Once - cached map[string]cli.HelperToolSchema + mu sync.Mutex + cached = map[string]map[string]cli.HelperToolSchema{} ) - return func(ctx context.Context) (map[string]cli.HelperToolSchema, error) { - if cached != nil { - return cached, nil + return func(ctx context.Context, source string) (map[string]cli.HelperToolSchema, error) { + mu.Lock() + if got, ok := cached[source]; ok { + mu.Unlock() + return got, nil } - schemas, err := fetchDevAppToolSchemas(ctx) + mu.Unlock() + + endpoint, err := helperSourceEndpoint(source) if err != nil { return nil, err } - once.Do(func() { cached = schemas }) - return cached, nil + schemas, err := fetchHelperToolSchemas(ctx, endpoint) + if err != nil { + return nil, err + } + mu.Lock() + cached[source] = schemas + mu.Unlock() + return schemas, nil } } -// fetchDevAppToolSchemas performs the live tools/list call against the pinned -// op-app endpoint and converts the descriptors. Auth and identity headers are -// resolved the same way the runner does for direct-runtime invocations. -func fetchDevAppToolSchemas(ctx context.Context) (map[string]cli.HelperToolSchema, error) { +// helperSourceEndpoint maps a schema source to its MCP endpoint. op-app (dev +// app) is pinned in source (devappEndpoint); other sources (e.g. devdoc) are +// resolved the same way the runner resolves a product endpoint — env override → +// discovery → edition StaticServers/SupplementServers. +func helperSourceEndpoint(source string) (string, error) { + switch source { + case "", "op-app", "devapp": + return devappEndpoint, nil + default: + if endpoint, ok := directRuntimeEndpoint(source, ""); ok { + return endpoint, nil + } + return "", fmt.Errorf("no MCP endpoint resolved for source %q (not injected by edition/discovery)", source) + } +} + +// fetchHelperToolSchemas performs the live tools/list call against endpoint and +// converts the descriptors. Auth and identity headers are resolved the same way +// the runner does for direct-runtime invocations. +func fetchHelperToolSchemas(ctx context.Context, endpoint string) (map[string]cli.HelperToolSchema, error) { token := resolveRuntimeAuthToken(ctx, "") headers := resolveIdentityHeaders() client := transport.NewClient(nil).WithAuth(token, headers) - result, err := client.ListTools(ctx, devappEndpoint) + result, err := client.ListTools(ctx, endpoint) if err != nil { return nil, err } diff --git a/internal/app/root.go b/internal/app/root.go index 04ecfc43..9cfe7ba3 100644 --- a/internal/app/root.go +++ b/internal/app/root.go @@ -596,7 +596,7 @@ func newVersionCommand() *cobra.Command { } func newSchemaCommand(loader cli.CatalogLoader) *cobra.Command { - return cli.NewSchemaCommand(loader, newDevAppToolFetcher()) + return cli.NewSchemaCommand(loader, newHelperToolFetcher()) } func newGenerateSkillsCommand() *cobra.Command { diff --git a/internal/cli/dev_schema.go b/internal/cli/dev_schema.go index 6f1d924c..487cf8d7 100644 --- a/internal/cli/dev_schema.go +++ b/internal/cli/dev_schema.go @@ -42,12 +42,14 @@ type HelperToolSchema struct { Required []string // MCP param names that are required } -// HelperToolFetcher loads the op-app tools/list LIVE and returns toolName→schema. -// The schema command injects this so dev_schema.go can resolve a command's -// MCP tool and render its real schema without the cli package importing app/ -// transport. Implementations should cache per-process so repeated -// `dws schema dev.*` only hit the network once. -type HelperToolFetcher func(ctx context.Context) (map[string]HelperToolSchema, error) +// HelperToolFetcher loads a helper MCP server's tools/list LIVE and returns +// toolName→schema for the given source (e.g. "op-app" for dev app commands, +// "devdoc" for dev doc commands). The schema command injects this so +// dev_schema.go can resolve a command's MCP tool and render its real schema +// without the cli package importing app/transport. Implementations should +// cache per-source per-process so repeated `dws schema dev.*` only hit the +// network once per source. +type HelperToolFetcher func(ctx context.Context, source string) (map[string]HelperToolSchema, error) // renderHelperSchema builds the `dws schema` payload for helper-only command // subtrees. Returns (payload, true) when the path targets a helper subtree (so @@ -102,15 +104,21 @@ func renderHelperSchema(ctx context.Context, root *cobra.Command, rawPath string // annotation; commands without one (e.g. `dev connect`, `dev doc search`) are // not devapp tools and get a clear, non-fatal explanation instead. func helperLeafSchema(ctx context.Context, cmd *cobra.Command, fetch HelperToolFetcher) (map[string]any, error) { - toolName := "" + toolName, source := "", "" if cmd.Annotations != nil { toolName = strings.TrimSpace(cmd.Annotations["mcp-tool"]) + source = strings.TrimSpace(cmd.Annotations["mcp-source"]) + } + // Default source is op-app (dev app commands); dev doc commands annotate + // mcp-source=devdoc to pull from the devdoc MCP server instead. + if source == "" { + source = "op-app" } path := helperCommandPath(cmd) if toolName == "" { return map[string]any{ "path": path, - "error": "no MCP tool for this command (not an op-app tool); schema is unavailable", + "error": "no MCP tool bound to this command; schema is unavailable", }, nil } if fetch == nil { @@ -120,22 +128,22 @@ func helperLeafSchema(ctx context.Context, cmd *cobra.Command, fetch HelperToolF }, nil } - tools, err := fetch(ctx) + tools, err := fetch(ctx, source) if err != nil { - return nil, fmt.Errorf("fetch op-app tool schemas: %w", err) + return nil, fmt.Errorf("fetch %s tool schemas: %w", source, err) } tool, ok := tools[toolName] if !ok { return map[string]any{ "path": path, - "error": fmt.Sprintf("MCP tool %q not found in op-app tools/list", toolName), + "error": fmt.Sprintf("MCP tool %q not found in %s tools/list", toolName, source), }, nil } return map[string]any{ "description": tool.Description, "path": path, - "source": "mcp:op-app", + "source": "mcp:" + source, "parameters": helperFlatParameters(tool), }, nil } diff --git a/internal/cli/dev_schema_test.go b/internal/cli/dev_schema_test.go index 9e3a3e73..603e496b 100644 --- a/internal/cli/dev_schema_test.go +++ b/internal/cli/dev_schema_test.go @@ -61,7 +61,7 @@ func buildHelperTestTree() *cobra.Command { // without network. It mirrors the MCP shape: properties keyed by camelCase param // name, required[] listing the camelCase names. func fakeFetcher(tools map[string]HelperToolSchema) HelperToolFetcher { - return func(context.Context) (map[string]HelperToolSchema, error) { + return func(context.Context, string) (map[string]HelperToolSchema, error) { return tools, nil } } @@ -218,7 +218,7 @@ func TestRenderHelperSchema_ToolMissingInList(t *testing.T) { func TestRenderHelperSchema_FetchError(t *testing.T) { root := buildHelperTestTree() - failing := func(context.Context) (map[string]HelperToolSchema, error) { + failing := func(context.Context, string) (map[string]HelperToolSchema, error) { return nil, errors.New("network down") } _, ok, err := renderHelperSchema(context.Background(), root, "dev app create", failing) diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 42a67c57..d8bfdb26 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -1372,6 +1372,7 @@ func annotateDevAppTool(cmd *cobra.Command, tool string) *cobra.Command { cmd.Annotations = map[string]string{} } cmd.Annotations["mcp-tool"] = tool + cmd.Annotations["mcp-source"] = "op-app" return cmd } diff --git a/internal/helpers/devdoc.go b/internal/helpers/devdoc.go index 84b1ae79..81ff0de6 100644 --- a/internal/helpers/devdoc.go +++ b/internal/helpers/devdoc.go @@ -82,17 +82,21 @@ func (devdocHandler) Command(runner executor.Runner) *cobra.Command { func newDevdocArticleSearchCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ - Use: "search [keyword]", - Short: "搜索开放平台文档", + Use: "search [keyword]", + Short: "搜索开放平台文档", + // Bind the devdoc MCP tool so `dws schema dev.doc.search` can fetch this + // command's real parameters live from the devdoc server (mcp-source), + // the same way dev app commands resolve against op-app. + Annotations: map[string]string{"mcp-tool": devdocArticleSearchTool, "mcp-source": "devdoc"}, Args: cobra.MaximumNArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - keyword := devdocFlagOrFallback(cmd, "query", "keyword") + keyword := devdocFlagOrFallback(cmd, "keyword", "query") if keyword == "" && len(args) > 0 { keyword = strings.TrimSpace(args[0]) } if keyword == "" { - return apperrors.NewValidation("--query is required") + return apperrors.NewValidation("--keyword is required") } page, _ := cmd.Flags().GetInt("page") if page < 1 { @@ -107,30 +111,33 @@ func newDevdocArticleSearchCommand(runner executor.Runner) *cobra.Command { }, } preferLegacyLeaf(cmd) - cmd.Flags().String("query", "", "搜索关键词 (必填)") - addDevdocHiddenStringFlag(cmd, "keyword", "--query 的悟空兼容别名") + // Flag names == MCP param names (keyword/size), so `dws schema dev.doc.search` + // shows exactly what the user types. --query/--page-size kept as hidden + // back-compat aliases for existing scripts and `dws devdoc`. + cmd.Flags().String("keyword", "", "搜索关键词 (必填)") + addDevdocHiddenStringFlag(cmd, "query", "--keyword 的别名") cmd.Flags().Int("page", 1, "分页页码 (从 1 开始,默认 1)") cmd.Flags().String("cursor", "", "分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page") - cmd.Flags().Int("page-size", 20, "单页条数 (默认 20)") - cmd.Flags().Int("size", 0, "--page-size 的兼容别名") - _ = cmd.Flags().MarkHidden("size") + cmd.Flags().Int("size", 20, "单页条数 (默认 20)") + cmd.Flags().Int("page-size", 0, "--size 的别名") + _ = cmd.Flags().MarkHidden("page-size") return cmd } -// devdocPageSize reads the page size, accepting --page-size (primary) with -// --size as a compatibility alias; falls back to 20 when neither is set. +// devdocPageSize reads the page size, accepting --size (primary, == MCP param) +// with --page-size as a compatibility alias; falls back to 20 when neither set. func devdocPageSize(cmd *cobra.Command) int { - if cmd.Flags().Changed("page-size") { - if v, _ := cmd.Flags().GetInt("page-size"); v > 0 { - return v - } - } if cmd.Flags().Changed("size") { if v, _ := cmd.Flags().GetInt("size"); v > 0 { return v } } - if v, _ := cmd.Flags().GetInt("page-size"); v > 0 { + if cmd.Flags().Changed("page-size") { + if v, _ := cmd.Flags().GetInt("page-size"); v > 0 { + return v + } + } + if v, _ := cmd.Flags().GetInt("size"); v > 0 { return v } return 20 diff --git a/skills/multi/dingtalk-dev/SKILL.md b/skills/multi/dingtalk-dev/SKILL.md index 3354b03c..2a824e63 100644 --- a/skills/multi/dingtalk-dev/SKILL.md +++ b/skills/multi/dingtalk-dev/SKILL.md @@ -1,6 +1,6 @@ --- name: dingtalk-dev -description: 钉钉开放平台企业内部应用全生命周期管理。应用是容器:凭证/权限点/成员/安全配置/能力扩展(网页应用/机器人)/版本,需审批的变更要发版本才生效。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/查应用/创建应用/修改应用/删除应用/停用应用/启用应用/应用成员/安全配置/IP白名单/登录重定向/端内免登/agentId/clientId/appKey/appSecret/customKey/应用权限/权限点/scopeValue/创建机器人/智能体机器人/机器人配置/机器人回调/应用版本/版本发布/发布审核/选审批人/本地调试机器人/把机器人接到本地agent/建联/connect。 +description: 钉钉开放平台企业内部应用管理:应用查询/创建/修改/删除/停启、凭证、权限点、成员、安全配置、网页应用、机器人、版本发布。Use when 用户说 开发者后台应用/开放平台应用/企业内部应用/agentId/clientId/appKey/appSecret/customKey/应用权限/权限点/scopeValue/创建机器人/智能体机器人/机器人配置/机器人回调/IP白名单/登录重定向/端内免登/应用版本/版本发布/发布审核/选审批人/把机器人接到本地调试/connect。 cli_version: "1.0.37+" metadata: category: product @@ -11,13 +11,21 @@ metadata: --- # 钉钉开放平台应用管理 Skill +## MUST DO + +每次执行 dev 命令前,先查清楚再敲,别凭记忆或猜: + +1. `--help` 看命令树(一个组下有哪些子命令、flag),例 `dws dev app --help` +2. `dws schema dev..` 看某命令参数(类型 / 必填 / 默认),按它构造、别自己拼——flag 名 = schema 参数名,例 `dws schema dev.app.robot.config`(schema 用点号,命令行用空格) +3. 全部命令带 `--format json` +4. 写操作:`--dry-run` 看 `invocation.params` 确认无误,再换 `--yes`(`dev connect` 例外见 [connect.md](references/connect.md)) +5. 写完回读确认(`get` / `robot get` / `version status`) +6. `clientSecret/appSecret` 不写进回答(脱敏) ## 概念地图 - 先建立领域模型,再看命令——所有命令都是对这张图上某个节点的操作,用户的模糊意图先映射到节点再选命令。 ### 应用是什么 - 钉钉开放平台的「企业内部应用」是企业自建的扩展程序。一个应用是一个容器: ``` @@ -31,76 +39,56 @@ metadata: │ └── 机器人 群聊/单聊收发消息,走回调 URL 或接本地 agent(robot) └── 版本 配置改动的生效通道(version) ``` - -映射示例:「想做个钉钉里打开的网页」= 创建应用 → webapp 配置 → 发版本;「做个答疑机器人」= robot submit/result 异步建号(或现有应用 robot config)→ 发版本 →(本地调试用 dev connect)。 +映射示例:「想做个钉钉里打开的网页」就是 创建应用,再配 webapp,再发版本;「做个答疑机器人」就是 robot submit/result 异步建号(或现有应用 robot config),发版本后本地调试用 dev connect。 ### ID 体系 - | 标识 | 是什么 | 用在哪 | |------|--------|--------| -| `unifiedAppId` | 统一应用 ID,新模型主键 | **唯一全树定位标识**,所有单应用命令都用 `--unified-app-id` | +| `unifiedAppId` | 统一应用 ID,新模型主键 | 唯一全树定位标识,所有单应用命令都用 `--unified-app-id` | | `appKey` = `clientId` | 应用身份标识,同一个东西的新旧两个名字,非密钥 | OpenAPI 调用、建联;也可作 `--app-key` 列表过滤(不能定位单应用) | | `appSecret` = `clientSecret` | 应用密钥,敏感 | 同上,按敏感凭证处理 | -| `agentId` | 旧版微应用 ID,历史遗留 | 仅出现在返回数据里;**不能用于定位**,拿到它先查出 unifiedAppId | +| `agentId` | 旧版微应用 ID,历史遗留 | 仅出现在返回数据里;不能用于定位,拿到它先查出 unifiedAppId | | `robotCode` | 机器人编号 | 加群、机器人发消息、建联 | 应用定位统一只用 `--unified-app-id`(--app-key/--name 仅作 list 过滤,不能定位单应用)。旧的 `--agent-id`/`--app-id`/`--custom-key` 定位 flag 已移除——agentId 只是返回字段,不再是入参定位手段。appKey 与 clientId 是同一标识的新旧两名,无需追问区别。 ### 生效模型(最重要) - -**改配置 ≠ 线上生效。** 需审批的变更(如 `requiredApproval=true` 的权限点)先累积在开发态,必须走版本通道才上线: - +- 改配置不等于线上生效,需审批的变更(如 `requiredApproval=true` 的权限点)先累积在开发态,必须走版本通道才上线: ``` 配置变更(permission add / robot config / webapp config ...) → version create → check-approval(预检审批要求+候选审批人) → publish(需审批时由用户选审批人)→ versionStatus=RELEASE 才生效 ``` - - 机器人等能力需版本发布后才能被搜索、加群、路由消息。 -- 用户问「为什么没生效 / 机器人搜不到 / 权限加了还报错」→ 先查 `version status`。 +- 用户问「为什么没生效 / 机器人搜不到 / 权限加了还报错」时,先查 `version status`。 - 两套状态别混:应用 appStatus(0 停用 / 1 激活 / 2 待激活 / 3 过期)是应用开关;版本 versionStatus(INIT / AUDIT / RELEASE / GRAY)是变更走到哪了。 ### 边界与角色 - -- 本 skill 只管**企业内部应用**。接口文档 → `dingtalk-devdoc`;钉钉云文档 → `dingtalk-doc`;工作台入口的「应用」→ `workbench app`;群里发消息用的机器人 → `dingtalk-chat`;审批流 → `dingtalk-oa`。 -- 角色:开发者(member DEVELOPER)改配置;管理员管启停;审批人批版本发布——审批人必须用户拍板,agent 不代选、不默认取第一个。 +- 本 skill 只管企业内部应用。接口文档用 `dingtalk-devdoc`;钉钉云文档用 `dingtalk-doc`;工作台入口的「应用」用 `workbench app`;群里发消息用的机器人用 `dingtalk-chat`;审批流用 `dingtalk-oa`。 +- 角色:开发者(member DEVELOPER)改配置;管理员管启停;审批人批版本发布。 ## 核心规则 - 1. `应用`、`机器人` 是泛词:用户只说这两个词、无开放平台上下文时,先追问确认是不是开发者后台的企业内部应用,不要猜——很可能是工作台应用或群消息机器人(转出口见上方「边界与角色」)。 -2. 所有命令加 `--format json`。 -3. 写操作先 `--dry-run`,确认后才加 `--yes`。例外:`dev connect` 的 dry-run 是建联预检(渠道识别、凭证来源、本地 agent CLI 是否安装),正式 connect 是前台长驻进程——在对话里跑必须后台运行并告诉用户如何停止,或引导用户自己开终端跑。 -4. 写操作返回成功 ≠ 状态已变:启停、机器人配置、版本发布等关键写操作执行后,回读对应查询命令(`get` / `robot get` / `version status`)确认状态,以回读结果为准;接口只返回操作成功但未带状态时,向用户说明以回读为准。 -5. 应用名/appKey 命中多条时展示候选,不取第一条。 -6. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名——权限点才是授权单元,API 名与权限点是多对一。 -7. 主动读取密钥走 `credentials get`;任何返回里的 `clientSecret/appSecret` 都按敏感凭证处理,不写进回答文本。例外:建联流程内部把 secret 作为参数传给 `dev connect` 是必要用途。 +2. 应用名/appKey 命中多条时展示候选、停止写操作,不取第一条。 +3. 权限申请/取消只接受 `scopeValue`,不传 API 名或分组名——权限点才是授权单元,API 名与权限点是多对一。 +4. 主动读取密钥走 `credentials get`(secret 的脱敏要求见 MUST DO);例外:connect 流程内部把 secret 作为参数传给 `dev connect` 是必要用途。 +5. 审批人必须用户拍板,agent 不代选、不默认取第一个。 ### 通用出参约定(跨所有命令) - -- **dry-run**:出参里看 `invocation.params`(将下发的参数)确认无误,再把 `--dry-run` 换成 `--yes`;dry-run 时 `response` 只是回显。 -- **游标分页**(list / permission list / version list / event list / doc search):首次不传 `--cursor`,出参带 `nextCursor`(空=到底)原样回传续翻;`hasMore == nextCursor 非空`。cursor 是上游不透明令牌,不要自己解析或构造,也不要跨命令复用。 -- **批量聚合**:`permission remove` 传多个权限点时出参是 `{results:[...], ok, total, failedCount}`,逐条判断成败(`ok=false` 即有失败)。 -- **pretty**:`--format pretty` 会在应用/版本状态字段旁附 `*Text` 可读标签(如 `appStatusText`);JSON 格式不附,以原始字段为准。 -- **失败**:`ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,不编造解释,解读走下方文档 RAG。 +- 游标分页(list / permission list / version list / event list / doc search):首次不传 `--cursor`,出参带 `nextCursor`(空=到底)原样回传续翻;`hasMore == nextCursor 非空`。cursor 是上游不透明令牌,不要自己解析或构造,也不要跨命令复用。 +- 批量聚合:`permission remove` 传多个权限点时出参是 `{results:[...], ok, total, failedCount}`,逐条判断成败(`ok=false` 即有失败)。 +- pretty:`--format pretty` 会在应用/版本状态字段旁附 `*Text` 可读标签(如 `appStatusText`);JSON 格式不附,以原始字段为准。 +- 失败:`ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,不编造解释,解读走下方文档 RAG。 ## 开放平台文档 RAG / 错误码排查 - -- dev 命令执行中,只要用户问开放平台 API、接口参数、字段含义、权限点、回调、SDK、配额、错误码,或命令返回上游 OpenAPI/SDK 错误,必须先用 `dws dev doc search --query "<关键词>" --format json` 做官方文档 RAG。 +- dev 命令执行中,只要用户问开放平台 API、接口参数、字段含义、权限点、回调、SDK、配额、错误码,或命令返回上游 OpenAPI/SDK 错误,必须先用 `dws dev doc search --keyword "<关键词>" --format json` 做官方文档 RAG。 - 业务错误(`ServiceResult.success=false`)原样透传 `errorCode/errorMsg`,不要编造解释;需要解读错误含义时走 devdoc RAG。 - 查询词优先保留原始 API 名、能力名、权限点、完整错误码和 message;首轮形如 `errcode `,无结果再换 `<产品/场景> <错误码>`、`<接口名> 参数`。 - 本地 CLI 错误(如 `unknown command` / `unknown flag` / 认证 / recovery)仍按 root `dws` / `dws-shared` 的错误处理执行;`devdoc` 用于开放平台业务错误码和接口语义排查。 - `devdoc` 只查钉钉开放平台开发者文档,不查业务数据;排查结论必须基于命中条目的标题、摘要或链接,不能编造错误原因或不存在的命令。 ## 典型任务 - -端到端任务都是「定位应用 → 改容器某节点 →(按审批需要)走版本生效 → 回读验证」。常见链路(每步先 `--dry-run` 确认再 `--yes`,细节进对应产品文件): - -- **建钉钉里打开的网页应用**:`app create` → `webapp config` → `version create` → `check-approval` → `publish` →(回读 `version status` 到 `RELEASE`)。 -- **权限从申请到生效**:`permission list`(选 `scopeValue`)→ `permission add` →(`requiredApproval` 的权限)`version create` → `check-approval`(看是否需审批+候选审批人)→ `publish --approver-user-id <用户选的>` → `version status`。 -- **做答疑机器人**:`robot submit` → `robot result`(`SUCCESS` 拿 clientId/secret),或现有应用 `robot config` →(发版本)→ 本地调试 `dev connect`。 -- **查"为什么没生效 / 机器人搜不到 / 权限加了还报错"**:先 `version status`——改配置 ≠ 生效,未发到 `RELEASE` 就不生效。 - -> 审批人必须用户拍板,agent 不代选、不默认取第一个。 +端到端任务都是「定位应用,改容器某节点,按审批需要走版本生效,最后回读验证」。完整链路(建网页应用 / 权限到生效 / 建机器人接本地调试 / 排查没生效)见 [recipes.md](references/recipes.md)。 ## 产品索引 @@ -120,8 +108,5 @@ metadata: | 事件订阅 | [event.md](references/event.md) | event list / subscribe / unsubscribe | ## Gotchas - -跨命令组的真实坑,随使用积累追加(单命令组的坑记在对应 reference 的错误处理表里): - -- 新应用 `version list` 返回空 ≠ 无可发布内容:先 `version create`,用返回的 `versionId` 继续 check-approval/publish。 +- 新应用 `version list` 返回空不等于无可发布内容:先 `version create`,用返回的 `versionId` 继续 check-approval/publish。 - `robot info is not exist` 是「应用还没配过机器人」,走 `robot config` 首次创建,不是 `enable`。 diff --git a/skills/multi/dingtalk-dev/references/app.md b/skills/multi/dingtalk-dev/references/app.md index 6f705d76..bf667c7a 100644 --- a/skills/multi/dingtalk-dev/references/app.md +++ b/skills/multi/dingtalk-dev/references/app.md @@ -2,110 +2,34 @@ > 概念锚点:操作的是「应用」容器本体(见 SKILL.md 概念地图);启停/删除改的是应用 appStatus,不是版本 versionStatus。 -应用列表查询、详情、创建、修改、生命周期启停和删除。 +应用列表查询、详情、创建、修改、生命周期启停和删除。参数查 `dws schema dev.app.`(list / get / create / update / delete / disable / enable)。 ## 应用定位 -所有单应用命令统一只用 `--unified-app-id`(全树主键,必填)定位: +所有单应用命令统一只用 `--unified-app-id`(全树主键)定位。`--app-key`/`--name` 只在 `dev app list` 里作列表过滤,不能定位单应用;旧的 `--agent-id`/`--app-id`/`--custom-key` 已移除。拿到 appKey/agentId 时,先用 `dev app list` 查出 unifiedAppId 再操作。 -| 标识 | 处理 | -|------|------| -| `--unified-app-id` | 直接使用,所有单应用命令都认(唯一定位手段) | +## 应用状态 appStatus -> 定位只认 `--unified-app-id`。`--app-key`/`--name` 只在 `dev app list` 里作**列表过滤**,不能用于定位单个应用做读写;旧的 `--agent-id`/`--app-id`/`--custom-key` 也已移除。拿到 appKey/agentId 时,先用 `dev app list` 查出 unifiedAppId 再操作。 - -## 应用列表 - -```bash -dws dev app list --format json -dws dev app list --name DemoApp --format json -dws dev app list --app-key dingxxx --format json -dws dev app list --creator 张三 --sort-type gmt_modified --sort-order desc --format json -``` - -| CLI | 说明 | -|-----|------| -| `--cursor` | 游标令牌:首次留空,续翻传上次出参的 `nextCursor` | -| `--page-size` | 单页条数,默认 20 | -| `--name` / `--keyword` | 应用名搜索 | -| `--app-key` | 按 appKey/clientId 过滤 | -| `--creator` | 创建人关键词 | -| `--sort-type` | 如 `gmt_modified` | -| `--sort-order` | `asc` / `desc` | - -> 分页用游标:首页不传 `--cursor`,出参带 `nextCursor`(空=到底);续翻把它原样回传。旧 `--page`/`--limit`/`--offset` 仍隐藏兼容(跳页/老脚本)。出参字段见 SKILL.md「通用出参约定」。 - -## 应用状态字段 - -列表/详情统一用 `appStatus`,按应用生命周期枚举判断;不要和版本 `versionStatus` 混用。 +列表/详情统一用 `appStatus`,按生命周期判断,不要和版本 `versionStatus` 混。 | appStatus | 枚举 | 含义 | 下一步 | |-----------|------|------|--------| -| `0` | `IN_ACTIVE` | 已停用,应用不可用 | 需要恢复时走 `enable --dry-run` → 确认 → `--yes` | -| `1` | `ACTIVE` | 已激活,应用可用 | 可继续配置权限、网页应用、机器人或版本 | -| `2` | `WAIT_ACTIVE` | 待激活 | 先回读 `get/list` 确认状态;不要直接按已生效处理 | -| `3` | `EXPIRED` | 已过期 | 停止写操作,提示用户到开发者后台或管理员侧处理 | +| `0` | `IN_ACTIVE` | 已停用,应用不可用 | 需恢复走 `enable` | +| `1` | `ACTIVE` | 已激活,应用可用 | 可继续配权限/网页/机器人/版本 | +| `2` | `WAIT_ACTIVE` | 待激活 | 先回读 `get/list` 确认,别按已生效处理 | +| `3` | `EXPIRED` | 已过期 | 停止写操作,提示到开发者后台或管理员处理 | -`create/update` 返回的 `versionStatus` 是版本状态,语义见 `version.md`;它不等同于应用启停状态。 +`create/update` 返回的 `versionStatus` 是版本状态(见 version.md),不等同应用启停状态。 -## 应用详情 +## 要点 -```bash -dws dev app get --unified-app-id UNIFIED_APP_ID --format json -``` - -详情主要用于定位和核验应用。若上游偶尔随详情返回 `clientSecret/appSecret`,必须脱敏处理,不要复制到回答里;主动读取凭证仍走 `credentials get`。 - -## 创建应用 - -```bash -dws dev app create --name DemoApp --desc "内部应用" --dry-run --format json -dws dev app create --name DemoApp --desc "内部应用" --yes --format json -``` - -| CLI | 必填 | -|-----|------| -| `--name` | 是 | -| `--desc` | 否 | -| `--icon-media-id` | 否 | - -## 修改应用 - -```bash -dws dev app update --unified-app-id ID --name DemoApp2 --desc "新描述" --dry-run --format json -dws dev app update --unified-app-id ID --name DemoApp2 --desc "新描述" --yes --format json -``` - -至少提供一个更新字段:`--name` / `--desc` / `--icon-media-id`。 - -## 停用 / 启用应用 - -```bash -dws dev app disable --unified-app-id ID --dry-run --format json -dws dev app disable --unified-app-id ID --yes --format json - -dws dev app enable --unified-app-id ID --dry-run --format json -dws dev app enable --unified-app-id ID --yes --format json -``` - -停用保留数据但应用不可用,可通过 `enable` 恢复。 - -执行 `disable/enable` 后必须回读 `get` 或 `list`:看到 `appStatus=0` 才算停用完成,看到 `appStatus=1` 才算启用完成;如果接口只返回操作成功但未带状态,向用户说明需要以回读结果为准。 - -## 删除应用 - -```bash -dws dev app delete --unified-app-id ID --dry-run --format json -dws dev app delete --unified-app-id ID --yes --format json -``` - -删除前必须展示应用摘要。删除为异步操作,成功后应用延迟从列表消失。 +- `get` 主要用于定位核验;若偶尔返回 `clientSecret/appSecret`,脱敏处理,不复制到回答;主动读凭证走 `credentials get`。 +- `disable/enable` 后必须回读 `get`/`list`:`appStatus=0` 才算停用完成,`appStatus=1` 才算启用完成;接口只返回成功未带状态时,以回读为准。 +- `delete` 前必须展示应用摘要;删除是异步,成功后延迟从列表消失。 ## 错误处理 | 情况 | 处理 | |------|------| -| `unknown command` | CLI 构建不含 dev 命令组 | -| `endpoint_not_resolved` | 检查 edition endpoint 注入 | | 多应用命中 | 展示候选,停止写操作 | | `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | diff --git a/skills/multi/dingtalk-dev/references/credentials.md b/skills/multi/dingtalk-dev/references/credentials.md index 25297465..a1df99e9 100644 --- a/skills/multi/dingtalk-dev/references/credentials.md +++ b/skills/multi/dingtalk-dev/references/credentials.md @@ -2,22 +2,9 @@ > 概念锚点:凭证=应用调 OpenAPI 的身份(appKey=clientId / appSecret=clientSecret);见 SKILL.md 概念地图。 -## 读取凭证 +`dws dev app credentials get --unified-app-id ` 读取应用凭证。参数查 `dws schema dev.app.credentials.get`。 -```bash -dws dev app credentials get --unified-app-id UNIFIED_APP_ID --format json -``` - -**规则:** +规则: - CLI 只传应用定位字段,不传 `showSecret`/`confirmSecret`。 -- 返回可能包含 `clientSecret/appSecret`,输出按敏感凭证处理,不写进回答文本。 -- 不能用 `dev app get` 代替;如果 `dev app get` 偶尔返回密钥,也只用于内部判断并脱敏,不向用户展开。 - -关键返回字段: - -| 字段 | 说明 | -|------|------| -| `clientId` / `appKey` | 非密钥标识 | -| `clientSecret` / `appSecret` | 敏感凭证 | -| `currentSecretStatus` | 当前密钥状态 | -| `pendingExpireTask` | 密钥过期任务信息 | +- 返回可能含 `clientSecret/appSecret`,按敏感凭证处理,不写进回答文本。 +- 不能用 `dev app get` 代替;`dev app get` 偶尔返回密钥也只用于内部判断并脱敏,不向用户展开。 diff --git a/skills/multi/dingtalk-dev/references/event.md b/skills/multi/dingtalk-dev/references/event.md index 89d6c1e5..a8f5a49e 100644 --- a/skills/multi/dingtalk-dev/references/event.md +++ b/skills/multi/dingtalk-dev/references/event.md @@ -1,59 +1,14 @@ # 事件订阅 -> 概念锚点:把应用关心的事件推到你的回调地址。所有命令用 `--unified-app-id` 定位;写操作先 `--dry-run`,确认后再 `--yes`。 +> 概念锚点:把应用关心的事件推到回调地址;见 SKILL.md 概念地图。 -为企业内部应用订阅、查询、取消开放平台事件推送。 +`dws dev app event list/subscribe/unsubscribe`,按 `--unified-app-id` 定位,订阅/退订用 `--event-codes`(逗号分隔,一次多个)。参数查 `dws schema dev.app.event.`。 -## 查询已订阅事件 +前置条件(业务规则):订阅/退订前必须先建立长连(`dev connect`,见 connect.md)。长连未在线时服务端报「长链接未在线」(errorCode -1),这是预期,不是故障——先建联再订阅。 -```bash -dws dev app event list --unified-app-id ID --format json -dws dev app event list --unified-app-id ID --page-size 50 --format json -dws dev app event list --unified-app-id ID --cursor --page-size 50 --format json -``` - -| CLI | 说明 | -|-----|------| -| `--unified-app-id` | 应用定位 | -| `--page-size` | 单页条数 | -| `--cursor` | 游标令牌:首次留空,续翻传上次返回的游标 | - -出参字段: - -- `events`:已订阅事件列表(每项含事件类型、回调地址等)。 -- `pushType`:推送方式(如 HTTP 回调)。 - -游标分页:返回的游标为空表示已到末尾。 - -## 订阅事件 - -```bash -dws dev app event subscribe --unified-app-id ID --event-codes chat_add_member_org --dry-run --format json -dws dev app event subscribe --unified-app-id ID --event-codes chat_add_member_org,chat_remove_member_org --yes --format json -``` - -| CLI | 说明 | -|-----|------| -| `--unified-app-id` | 应用定位 | -| `--event-codes` | 事件码,多个逗号分隔(一次可订阅/退订多个)| - -**规则:** -- 写操作,先 `--dry-run` 预览,确认后 `--yes`。 -- 一次可订阅多个事件类型(逗号分隔),共用同一回调地址。 -- 事件类型取值以开放平台文档为准;不确定走 `dws dev doc search`。 - -## 取消订阅 - -```bash -dws dev app event unsubscribe --unified-app-id ID --event-codes chat_add_member_org --dry-run --format json -dws dev app event unsubscribe --unified-app-id ID --event-codes chat_add_member_org --yes --format json -``` - -| CLI | 说明 | -|-----|------| -| `--unified-app-id` | 应用定位 | -| `--event-codes` | 要取消的事件类型,多个逗号分隔 | - -**规则:** -- 写操作,先 `--dry-run` 预览,确认后 `--yes`。 -- 取消前可先 `event list` 确认当前订阅,避免取消不存在的事件类型。 +规则: +- 写操作先 `--dry-run` 预览,确认后 `--yes`。 +- 一次可订阅多个事件码,共用同一回调。 +- 事件码取值以开放平台文档为准;不确定走 `dws dev doc search`。 +- 退订前先 `event list` 确认当前订阅,避免退不存在的。 +- 返回看 `events`(已订阅列表)和 `pushType`(推送方式)。 diff --git a/skills/multi/dingtalk-dev/references/member.md b/skills/multi/dingtalk-dev/references/member.md index ba54e352..c3113a94 100644 --- a/skills/multi/dingtalk-dev/references/member.md +++ b/skills/multi/dingtalk-dev/references/member.md @@ -2,28 +2,4 @@ > 概念锚点:成员=谁能改这个应用(DEVELOPER 等角色);见 SKILL.md 概念地图。 -## 查询成员 - -```bash -dws dev app member list --unified-app-id UNIFIED_APP_ID --format json -``` - -## 添加成员 - -```bash -dws dev app member add --unified-app-id UNIFIED_APP_ID --member-user-ids userId1,userId2 --member-type DEVELOPER --dry-run --format json -dws dev app member add --unified-app-id UNIFIED_APP_ID --member-user-ids userId1,userId2 --member-type DEVELOPER --yes --format json -``` - -## 移除成员 - -```bash -dws dev app member remove --unified-app-id UNIFIED_APP_ID --member-user-ids userId1 --member-type DEVELOPER --dry-run --format json -dws dev app member remove --unified-app-id UNIFIED_APP_ID --member-user-ids userId1 --member-type DEVELOPER --yes --format json -``` - -| CLI | 说明 | -|-----|------| -| `--unified-app-id` | 统一应用 ID(必填) | -| `--member-user-ids` | userId 列表,逗号/分号分隔(必填) | -| `--member-type` | 成员类型,如 `DEVELOPER`(必填) | +`dws dev app member list/add/remove` 管理应用成员。参数查 `dws schema dev.app.member.`(add/remove 需 `--member-user-ids` 列表 + `--member-type`,如 DEVELOPER)。 diff --git a/skills/multi/dingtalk-dev/references/permission.md b/skills/multi/dingtalk-dev/references/permission.md index 5c1bd874..84eaf9b2 100644 --- a/skills/multi/dingtalk-dev/references/permission.md +++ b/skills/multi/dingtalk-dev/references/permission.md @@ -13,15 +13,7 @@ dws dev app permission list --unified-app-id ID --scope-type SNS --format json dws dev app permission list --unified-app-id ID --scope-value qyapi_robot_sendmsg --format json ``` -| CLI | 说明 | -|-----|------| -| `--unified-app-id` | 应用定位 | -| `--keyword` | 权限名/API 名关键词 | -| `--auth-status` | `ALL` / `AUTHED` / `UNAUTHED` | -| `--scope-type` | `APP` / `SNS`,为空返回两者 | -| `--scope-value` | 单权限详情模式 | -| `--cursor` | 游标令牌:首次留空,续翻传上次 `nextCursor` | -| `--page-size` | 单页条数,默认 20,建议不超过 50 | +参数查 `dws schema dev.app.permission.list`。`--scope-value` 传入即进单权限详情模式;`--scope-type` 取 `APP`/`SNS`,留空返回两者;一个应用可能 150+ 权限点,建议 `--page-size` 不超过 50。 **状态判断:** diff --git a/skills/multi/dingtalk-dev/references/recipes.md b/skills/multi/dingtalk-dev/references/recipes.md new file mode 100644 index 00000000..6d8d18dc --- /dev/null +++ b/skills/multi/dingtalk-dev/references/recipes.md @@ -0,0 +1,29 @@ +# 端到端链路(recipes) + +dev 的端到端任务都是「定位应用,改容器某节点,按审批需要走版本生效,最后回读验证」。每步先 `--dry-run` 确认再 `--yes`,参数查 `dws schema`,细节进对应 reference。 + +## 建一个钉钉里打开的网页应用 + +1. `dev app create --name <名>` 建应用,拿 unifiedAppId +2. `dev app webapp config` 配移动端/PC 首页(见 webapp.md) +3. `dev app version create` 建版本 +4. `dev app version check-approval` 预检是否需审批 +5. `dev app version publish` 发布(需审批时让用户选审批人) +6. 回读 `dev app version status` 到 `RELEASE` 才算生效 + +## 权限从申请到生效 + +1. `dev app permission list` 选 `scopeValue`(选择顺序见 permission.md) +2. `dev app permission add --scope-values <值>` 申请 +3. 若是 `requiredApproval` 的权限,走版本:`version create`,再 `check-approval`,再 `publish --approver-user-id <用户选的>`,最后 `version status` +4. 免审权限直接开通,不必发版本 + +## 做一个答疑机器人并接到本地调试 + +1. 新建:`dev app robot submit` 提交异步建号任务,再 `dev app robot result` 轮询,`SUCCESS` 拿 clientId/clientSecret(见 robot.md);或在现有应用上 `dev app robot config` 配置 +2. 机器人能力需发版本后才能被搜索/加群/路由消息 +3. 本地调试/值守:`dev connect` 把机器人接到本地 agent(见 connect.md);注意订阅事件前要先建联长连(见 event.md) + +## 查「为什么没生效 / 机器人搜不到 / 权限加了还报错」 + +先 `dev app version status`——改配置不等于生效,未发到 `RELEASE` 就不生效。 diff --git a/skills/multi/dingtalk-dev/references/robot.md b/skills/multi/dingtalk-dev/references/robot.md index ae9e3192..c7ecf36d 100644 --- a/skills/multi/dingtalk-dev/references/robot.md +++ b/skills/multi/dingtalk-dev/references/robot.md @@ -71,23 +71,7 @@ dws dev app robot enable --unified-app-id --dry-run --format json > 旧的独立 `update` 命令已删除(并入 `config`)。 -| CLI | 说明 | -|-----|------| -| `--name` | 机器人名称 | -| `--brief` | 简介 | -| `--desc` | 描述 | -| `--icon-media-id` | 图标 mediaId | -| `--outgoing-url` | 消息回调地址 | -| `--event-callback-url` | 事件回调地址 | -| `--mode` | 机器人模式枚举(整数) | -| `--skills` | 技能列表,逗号/分号分隔 | -| `--add-scope` | 自动添加机器人相关权限 | -| `--disable-ssl-verify` | 回调关闭 SSL 校验 | -| `--i18n-name` | 名称国际化 JSON,如 `'{"en_US":"Bot"}'` | -| `--i18n-brief` | 简介国际化 JSON | -| `--i18n-description` | 描述国际化 JSON | - -至少提供一个配置字段,否则 CLI 报错。 +参数查 `dws schema dev.app.robot.config`;至少提供一个配置字段,否则 CLI 报错。国际化字段(`--i18n-name` 等)传 JSON,如 `'{"en_US":"Bot"}'`。 ### 停用 diff --git a/skills/multi/dingtalk-dev/references/security.md b/skills/multi/dingtalk-dev/references/security.md index 47614b87..2335b249 100644 --- a/skills/multi/dingtalk-dev/references/security.md +++ b/skills/multi/dingtalk-dev/references/security.md @@ -2,20 +2,6 @@ > 概念锚点:安全配置=应用的 IP 白名单 / 登录重定向 / 端内免登 URL;见 SKILL.md 概念地图。 -## 配置安全设置 +`dws dev app security config` 配 IP 白名单(`--ip-whitelist`)、登录重定向(`--redirect-urls`)、端内免登(`--sso-urls`)。参数查 `dws schema dev.app.security.config`,至少给一个配置字段。 -```bash -dws dev app security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json -dws dev app security config --unified-app-id UNIFIED_APP_ID --redirect-urls https://callback.example.invalid/callback --sso-urls https://sso.example.invalid/sso --yes --format json -``` - -| CLI | 说明 | -|-----|------| -| `--unified-app-id` | 统一应用 ID(必填) | -| `--ip-whitelist` | 出口 IP 白名单,逗号/分号分隔 | -| `--redirect-urls` | 登录重定向 URL,逗号/分号分隔 | -| `--sso-urls` | 端内免登 URL,逗号/分号分隔 | - -至少提供一个配置字段。 - -覆盖语义:未提供的字段不动;显式提供的列表是**整组覆盖**(传入即全量替换该项,不是追加)——要保留旧值就把旧值一起带上。 +覆盖语义:未提供的字段不动;显式提供的列表是整组覆盖(传入即全量替换该项,不是追加)——要保留旧值就把旧值一起带上。 diff --git a/skills/multi/dingtalk-dev/references/version.md b/skills/multi/dingtalk-dev/references/version.md index 9dc75508..fae7b227 100644 --- a/skills/multi/dingtalk-dev/references/version.md +++ b/skills/multi/dingtalk-dev/references/version.md @@ -23,10 +23,7 @@ dws dev app version create --unified-app-id --version 1.0.1 --des dws dev app version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json ``` -| CLI | 说明 | -|-----|------| -| `--version` | 版本号,如 1.0.1 | -| `--desc` | 版本描述 | +参数查 `dws schema dev.app.version.create`。 ## 版本列表 @@ -68,12 +65,7 @@ dws dev app version publish --unified-app-id --version-id --version-id --approver-user-id --yes --format json ``` -`publish` 是真实发布(区别于 `check-approval` 的预检不发布)。 - -| CLI | 说明 | -|-----|------| -| `--confirmed-sensitive` | 版本含高敏权限时必须确认 | -| `--approver-user-id` | 灰度选人模式指定审批人 userId | +`publish` 是真实发布(区别于 `check-approval` 的预检不发布)。参数查 `dws schema dev.app.version.publish`;含高敏权限要加 `--confirmed-sensitive`,灰度选人模式用 `--approver-user-id` 指定审批人。 > 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`。二者是两个东西:`check-approval` 是只查审批要求不发布,`--dry-run` 是 CLI 要不要真调上游。发布前建议先 `check-approval`。 diff --git a/skills/multi/dingtalk-dev/references/webapp.md b/skills/multi/dingtalk-dev/references/webapp.md index 9449f2e3..198bd9ac 100644 --- a/skills/multi/dingtalk-dev/references/webapp.md +++ b/skills/multi/dingtalk-dev/references/webapp.md @@ -2,26 +2,7 @@ > 概念锚点:网页应用=应用的能力扩展之一,钉钉内打开的 H5;见 SKILL.md 概念地图。 -## 查询网页应用配置 +`dws dev app webapp get` 查配置,`webapp config` 配移动端/PC 首页和管理后台地址。参数查 `dws schema dev.app.webapp.`,config 至少给一个配置字段。 -```bash -dws dev app webapp get --unified-app-id UNIFIED_APP_ID --format json -``` - -未配置网页应用前可能只返回 `agentId`。 - -## 配置网页应用 - -```bash -dws dev app webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --dry-run --format json -dws dev app webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com/mobile --pc-homepage-url https://example.com/pc --yes --format json -``` - -| CLI | 说明 | -|-----|------| -| `--h5-page-type` | 网页应用生效端 | -| `--homepage-url` | 移动端首页地址 | -| `--pc-homepage-url` | PC 端首页地址 | -| `--omp-url` | 管理后台地址 | - -至少提供一个配置字段。`h5PageType` 未显式传入时不要假设固定默认值;配置后以 `webapp get` 回读为准(实跑可能返回 `mobile`)。 +- 未配置网页应用前,`get` 可能只返回 `agentId`。 +- `h5PageType` 未显式传入时不要假设固定默认值;配置后以 `webapp get` 回读为准。 From cc8a726b0af65df5aef4a47ba2937b3cd5688b44 Mon Sep 17 00:00:00 2001 From: qinze Date: Thu, 18 Jun 2026 09:09:04 +0800 Subject: [PATCH 31/47] =?UTF-8?q?docs(dev):=20dingtalk-dev=20references=20?= =?UTF-8?q?=E4=BA=8C=E6=AC=A1=E7=B2=BE=E7=AE=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - permission/robot/version 删 bash 命令示例堆叠(schema/--help 已覆盖), 保留状态机/枚举语义/链路/gotcha - 去掉所有 reference 的"概念锚点"自造词标签,保留指向概念地图的引语 - SKILL.md 生效模型去"(最重要)";connect 去加粗 Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/multi/dingtalk-dev/SKILL.md | 2 +- skills/multi/dingtalk-dev/references/app.md | 2 +- .../multi/dingtalk-dev/references/connect.md | 2 +- .../dingtalk-dev/references/credentials.md | 2 +- skills/multi/dingtalk-dev/references/event.md | 2 +- .../multi/dingtalk-dev/references/member.md | 2 +- .../dingtalk-dev/references/permission.md | 71 +++---------- skills/multi/dingtalk-dev/references/robot.md | 86 ++++------------ .../multi/dingtalk-dev/references/security.md | 2 +- .../multi/dingtalk-dev/references/version.md | 99 +++++-------------- .../multi/dingtalk-dev/references/webapp.md | 2 +- 11 files changed, 68 insertions(+), 204 deletions(-) diff --git a/skills/multi/dingtalk-dev/SKILL.md b/skills/multi/dingtalk-dev/SKILL.md index 2a824e63..527b3109 100644 --- a/skills/multi/dingtalk-dev/SKILL.md +++ b/skills/multi/dingtalk-dev/SKILL.md @@ -52,7 +52,7 @@ metadata: 应用定位统一只用 `--unified-app-id`(--app-key/--name 仅作 list 过滤,不能定位单应用)。旧的 `--agent-id`/`--app-id`/`--custom-key` 定位 flag 已移除——agentId 只是返回字段,不再是入参定位手段。appKey 与 clientId 是同一标识的新旧两名,无需追问区别。 -### 生效模型(最重要) +### 生效模型 - 改配置不等于线上生效,需审批的变更(如 `requiredApproval=true` 的权限点)先累积在开发态,必须走版本通道才上线: ``` 配置变更(permission add / robot config / webapp config ...) diff --git a/skills/multi/dingtalk-dev/references/app.md b/skills/multi/dingtalk-dev/references/app.md index bf667c7a..b5e25c62 100644 --- a/skills/multi/dingtalk-dev/references/app.md +++ b/skills/multi/dingtalk-dev/references/app.md @@ -1,6 +1,6 @@ # 应用基础操作 -> 概念锚点:操作的是「应用」容器本体(见 SKILL.md 概念地图);启停/删除改的是应用 appStatus,不是版本 versionStatus。 +> 操作的是「应用」容器本体(见 SKILL.md 概念地图);启停/删除改的是应用 appStatus,不是版本 versionStatus。 应用列表查询、详情、创建、修改、生命周期启停和删除。参数查 `dws schema dev.app.`(list / get / create / update / delete / disable / enable)。 diff --git a/skills/multi/dingtalk-dev/references/connect.md b/skills/multi/dingtalk-dev/references/connect.md index dfa61cc9..04b51d8f 100644 --- a/skills/multi/dingtalk-dev/references/connect.md +++ b/skills/multi/dingtalk-dev/references/connect.md @@ -1,6 +1,6 @@ # 本地建联(把机器人接到本地 agent) -> 概念锚点:`dws dev connect` 是 dev 顶层命令,把一个**现成机器人**接到当前渠道的本地 agent CLI 做调试/值守——只建联、不建号。缺机器人先走 robot.md 的 `robot submit`/`result` 建号拿 clientId/clientSecret。 +> `dws dev connect` 是 dev 顶层命令,把一个现成机器人接到当前渠道的本地 agent CLI 做调试/值守——只建联、不建号。缺机器人先走 robot.md 的 `robot submit`/`result` 建号拿 clientId/clientSecret。 ## 起连接 diff --git a/skills/multi/dingtalk-dev/references/credentials.md b/skills/multi/dingtalk-dev/references/credentials.md index a1df99e9..b4a740c7 100644 --- a/skills/multi/dingtalk-dev/references/credentials.md +++ b/skills/multi/dingtalk-dev/references/credentials.md @@ -1,6 +1,6 @@ # 应用凭证读取 -> 概念锚点:凭证=应用调 OpenAPI 的身份(appKey=clientId / appSecret=clientSecret);见 SKILL.md 概念地图。 +> 凭证=应用调 OpenAPI 的身份(appKey=clientId / appSecret=clientSecret);见 SKILL.md 概念地图。 `dws dev app credentials get --unified-app-id ` 读取应用凭证。参数查 `dws schema dev.app.credentials.get`。 diff --git a/skills/multi/dingtalk-dev/references/event.md b/skills/multi/dingtalk-dev/references/event.md index a8f5a49e..c020d56a 100644 --- a/skills/multi/dingtalk-dev/references/event.md +++ b/skills/multi/dingtalk-dev/references/event.md @@ -1,6 +1,6 @@ # 事件订阅 -> 概念锚点:把应用关心的事件推到回调地址;见 SKILL.md 概念地图。 +> 把应用关心的事件推到回调地址;见 SKILL.md 概念地图。 `dws dev app event list/subscribe/unsubscribe`,按 `--unified-app-id` 定位,订阅/退订用 `--event-codes`(逗号分隔,一次多个)。参数查 `dws schema dev.app.event.`。 diff --git a/skills/multi/dingtalk-dev/references/member.md b/skills/multi/dingtalk-dev/references/member.md index c3113a94..206a70e2 100644 --- a/skills/multi/dingtalk-dev/references/member.md +++ b/skills/multi/dingtalk-dev/references/member.md @@ -1,5 +1,5 @@ # 成员管理 -> 概念锚点:成员=谁能改这个应用(DEVELOPER 等角色);见 SKILL.md 概念地图。 +> 成员=谁能改这个应用(DEVELOPER 等角色);见 SKILL.md 概念地图。 `dws dev app member list/add/remove` 管理应用成员。参数查 `dws schema dev.app.member.`(add/remove 需 `--member-user-ids` 列表 + `--member-type`,如 DEVELOPER)。 diff --git a/skills/multi/dingtalk-dev/references/permission.md b/skills/multi/dingtalk-dev/references/permission.md index 84eaf9b2..dba7728e 100644 --- a/skills/multi/dingtalk-dev/references/permission.md +++ b/skills/multi/dingtalk-dev/references/permission.md @@ -1,83 +1,44 @@ # 权限管理 -> 概念锚点:权限点 scopeValue 是授权单元,一个权限点授权一组 OpenAPI;requiredApproval=true 的变更走版本通道生效(见 SKILL.md 生效模型)。 +> 权限点 scopeValue 是授权单元,一个权限点授权一组 OpenAPI;requiredApproval=true 的变更走版本通道生效(见 SKILL.md 生效模型)。 -查询、申请、取消开放平台应用的 APP 应用权限和 SNS 个人权限。 +查询、申请、取消开放平台应用的 APP 应用权限和 SNS 个人权限。参数查 `dws schema dev.app.permission.`。 ## 权限列表 -```bash -dws dev app permission list --unified-app-id ID --format json -dws dev app permission list --unified-app-id ID --keyword "机器人发消息" --auth-status UNAUTHED --format json -dws dev app permission list --unified-app-id ID --scope-type SNS --format json -dws dev app permission list --unified-app-id ID --scope-value qyapi_robot_sendmsg --format json -``` - -参数查 `dws schema dev.app.permission.list`。`--scope-value` 传入即进单权限详情模式;`--scope-type` 取 `APP`/`SNS`,留空返回两者;一个应用可能 150+ 权限点,建议 `--page-size` 不超过 50。 - -**状态判断:** +`--scope-value` 传入即进单权限详情模式;`--scope-type` 取 `APP`/`SNS`,留空返回两者;一个应用可能 150+ 权限点,游标分页续翻、`--page-size` 不超过 50。 `--auth-status` 是查询过滤条件: | authStatus | 含义 | |------------|------| | `ALL` | 不按授权状态过滤 | -| `AUTHED` | 只看已授权/已开通的权限点 | -| `UNAUTHED` | 只看未授权/未开通的权限点 | +| `AUTHED` | 只看已授权/已开通 | +| `UNAUTHED` | 只看未授权/未开通 | 单个权限项返回的 `status` 是内部操作态: | status | 枚举 | 含义 | 下一步 | |--------|------|------|--------| | `0` | `STATUS_OBTAINED` | 权限已获得 | 不要重复申请;如需取消,确认 `canRemove=true` 后走 `permission remove` | -| `1` | `STATUS_APPLYING` | 权限申请中 | 不要重复申请;查看 `authedStatusDesc`,通常等待审批或版本发布 | -| `2` | `STATUS_CAN_APPLY` | 权限可以申请 | 可走 `permission add --dry-run` | -| `3` | `STATUS_CAN_NOT_APPLY` | 权限不可以申请 | 停止申请,展示 `applyDisabledReason/displayMessage` | +| `1` | `STATUS_APPLYING` | 权限申请中 | 不要重复申请;看 `authedStatusDesc`,通常等待审批或版本发布 | +| `2` | `STATUS_CAN_APPLY` | 可以申请 | 走 `permission add` | +| `3` | `STATUS_CAN_NOT_APPLY` | 不可申请 | 停止,展示 `applyDisabledReason/displayMessage` | -`authedStatusDesc` 是给用户看的细分状态:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED` 表示已开通、已申请或待发布;`NOT_OPEN`/`NOT_APPLIED` 表示未开通/未申请;`AUDIT_PROCESSING` 表示审批中;`AUDIT_REFUSE` 表示审批未通过。判断能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 +`authedStatusDesc` 是给用户看的细分状态:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED`=已开通/已申请/待发布;`NOT_OPEN`/`NOT_APPLIED`=未开通/未申请;`AUDIT_PROCESSING`=审批中;`AUDIT_REFUSE`=审批未通过。能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 -**翻页:** +list 默认同时返回 APP 和 SNS 权限;列表模式只返回 `apiPreview`,`--scope-value` 详情模式返回完整 `apiList`。`permission search`/`detail` 是 `list` 的别名。 -权限列表用游标分页(与 app/version list 一致)。一个应用可能有 150+ 个权限点,逐页用 `nextCursor` 续翻: - -```bash -dws dev app permission list --unified-app-id ID --page-size 50 --format json -# → 出参 nextCursor=,续翻: -dws dev app permission list --unified-app-id ID --cursor --page-size 50 --format json -``` - -出参 `nextCursor` 为空(或 `hasMore=false`)时表示已到末尾。 - -**规则:** -- `permission search` 和 `permission detail` 是 `list` 的 CLI 别名。 -- 默认同时返回 APP 和 SNS 权限。 -- 列表模式只返回 `apiPreview`;`--scope-value` 详情模式返回完整 `apiList`。 - -**scopeValue 选择顺序:** - -1. 用户给了 `scopeValue` → 精确匹配 -2. 用户给了 API 名 → `keyword` 搜索,匹配 `apiPreview.name` -3. 用户给了权限名 → 匹配 `scopeName/scopeDesc` -4. 多个候选 → 展示列表让用户选择,不自动取第一条 +scopeValue 选择顺序: +1. 用户给了 `scopeValue`,精确匹配 +2. 给了 API 名,用 `keyword` 搜,匹配 `apiPreview.name` +3. 给了权限名,匹配 `scopeName/scopeDesc` +4. 多个候选,展示列表让用户选,不自动取第一条 ## 申请权限 -```bash -dws dev app permission add --unified-app-id ID --scope-values qyapi_robot_sendmsg --dry-run --format json -dws dev app permission add --unified-app-id ID --scope-values Contact.User.mobile,qyapi_robot_sendmsg --yes --format json -``` - -**规则:** -- `--scope-values` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 的返回。 -- 已开通跳过,不可编辑拒绝。 -- `requiredApproval=true` 允许申请——写入版本变更,审批在版本发布时处理。 -- 不在此处选审批人。 +`--scope-values` 传 `scopeValue`,多个逗号分隔,必须来自 `permission list` 返回。已开通跳过、不可编辑拒绝。`requiredApproval=true` 允许申请——写入版本变更,审批在版本发布时处理。不在此处选审批人。 ## 取消权限 -```bash -dws dev app permission remove --unified-app-id ID --scope-values qyapi_robot_sendmsg --dry-run --format json -dws dev app permission remove --unified-app-id ID --scope-values qyapi_robot_sendmsg --yes --format json -``` - `--scope-values` 多个逗号分隔;上游一次只取消一个权限点,多条时 CLI 逐条调用并返回 `results` 聚合数组。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 diff --git a/skills/multi/dingtalk-dev/references/robot.md b/skills/multi/dingtalk-dev/references/robot.md index c7ecf36d..4144a238 100644 --- a/skills/multi/dingtalk-dev/references/robot.md +++ b/skills/multi/dingtalk-dev/references/robot.md @@ -1,92 +1,48 @@ # 机器人能力 -> 概念锚点:机器人是应用的能力扩展之一;建号/配置在此,接到本地 agent 调试用 `dws dev connect`(见 connect.md)。 +> 机器人是应用的能力扩展之一;建号/配置在此,接到本地 agent 调试用 `dws dev connect`(见 connect.md)。 -为开放平台企业内部应用创建和配置机器人。分两类场景: +为开放平台企业内部应用创建和配置机器人。参数查 `dws schema dev.app.robot.`。分两类场景: -1. **新建智能体机器人**:异步创建一个新的 Agent 应用 + 承载机器人(`submit` / `result`)。同步建号(一次性创建应用+机器人)已下线,统一走异步。 -2. **现有应用配置机器人**:在已存在的应用上配置/启用/停用机器人(`get` / `config`(upsert) / `enable` / `disable`),通过 `--unified-app-id` 定位。 +1. 新建智能体机器人:异步创建一个新的 Agent 应用 + 承载机器人(`submit` / `result`)。同步建号已下线,统一走异步。 +2. 现有应用配置机器人:在已存在的应用上配置/启用/停用机器人(`get` / `config`(upsert) / `enable` / `disable`),用 `--unified-app-id` 定位。 > `corpId` / `userId` 由系统上下文自动注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 ## 一、新建智能体机器人(异步建号) -同步建号已下线,新建智能体一律用异步 `submit` 提交任务 + `result` 轮询。也适合创建耗时较长或需要失败重试的场景。 +`submit` 提交任务拿 `taskId`,`result --task-id ` 轮询。`submit` 返回 `taskId/status/expiresIn/interval/retryCount`,提交成功通常是 `WAITING`。失败重试:把上次 `taskId` 通过 `--task-id` 传回 `submit`,避免重复创建。只有 `result` 返回 `SUCCESS` 才能用返回的 `agentId/robotCode/clientId/clientSecret`。 -```bash -# 提交任务 -dws dev app robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --dry-run --format json -dws dev app robot submit --name 我的智能体 --robot-name 小助手 --desc "处理审批问答" --yes --format json -# → 返回 taskId - -# 轮询结果 -dws dev app robot result --task-id --format json -``` - -- `submit` 返回 `taskId / status / expiresIn / interval / retryCount`,提交成功后通常是 `WAITING`。 -- 失败重试:把上次的 `taskId` 通过 `--task-id` 传入 `submit`,避免重复创建。 -- `result` 返回任务状态;只有 `SUCCESS` 时才能使用返回的 `agentId / robotCode / clientId / clientSecret`。 - -异步创建任务状态: +异步任务状态: | status | 含义 | 下一步 | |--------|------|--------| -| `WAITING` | 任务已提交,仍在创建中 | 按 `interval` 轮询 `robot result` | -| `SUCCESS` | 创建完成 | 保存 `robotCode/clientId/clientSecret`,凭据按敏感信息处理 | -| `APPROVAL_REQUIRED` | 创建编排返回需审批 | 不要重复建号;按返回信息或开发者后台审批后再继续 | -| `FAIL` | 创建失败 | 读取 `errorCode/errorMsg/failReason`;可带原 `taskId` 重新 `submit` | -| `EXPIRED` | `taskId` 不存在或超过有效期 | 重新 `submit`,必要时换新 `taskId` | +| `WAITING` | 创建中 | 按 `interval` 轮询 `robot result` | +| `SUCCESS` | 创建完成 | 保存 `robotCode/clientId/clientSecret`,凭据按敏感处理 | +| `APPROVAL_REQUIRED` | 创建编排需审批 | 不要重复建号;按返回信息或后台审批后再继续 | +| `FAIL` | 创建失败 | 读 `errorCode/errorMsg/failReason`;可带原 `taskId` 重新 `submit` | +| `EXPIRED` | `taskId` 不存在或过期 | 重新 `submit` | ## 二、现有应用的机器人配置 -### 查询配置 - -```bash -dws dev app robot get --unified-app-id --format json -``` - -返回机器人基础信息、回调地址、模式、状态和技能列表。应用尚未配置机器人时后端会返回 `robot info is not exist`。 +`robot get` 返回机器人基础信息、回调、模式、状态、技能列表;应用尚未配置机器人时后端返回 `robot info is not exist`。 状态判断: +- `status=1`:OFFLINE,配置存在但停用/下线。 +- `status=2`:ONLINE,配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 +- `robot get` 返回 `success=true` 且含 `robotCode` = 配置已落库,不是异步等待态。 +- ONLINE 只代表能力已开启。要让机器人自动处理消息,还需配 `--outgoing-url`/`--event-callback-url`,或用 `dev connect` 接本地 Agent(见 connect.md)。 +- 未配置机器人时不返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 -- `status=1`:OFFLINE,机器人配置存在但处于停用/下线状态。 -- `status=2`:ONLINE,机器人配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 -- `robot get` 返回 `success=true` 且包含 `robotCode` 时,说明配置已落库,不是异步等待态。 -- ONLINE 只代表开放平台机器人能力已开启。若要让机器人自动处理消息,还需要配置 `--outgoing-url` / `--event-callback-url`,或用 `dev connect` 接到本地 Agent(见 connect.md)。 -- 未配置机器人时不会返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 - -### 配置(upsert)/ 启用 - -- `config` 是 **upsert**:建或改都用它,配置不存在则建、存在则改,无需区分。需至少一个配置字段。 -- `enable` 是**纯启用**:只开启机器人能力,不带配置字段(只传 `--unified-app-id`)。 -- `config` 成功后必须回读 `robot get`:如果返回 `status=2`,不要再误判为"待生效";只有 `status=1` 或需要重新上架时才调用 `enable`。 - -```bash -dws dev app robot config --unified-app-id --name 小助手 --brief 审批助手 \ - --desc "处理审批相关问答" --outgoing-url https://example.com/msg \ - --event-callback-url https://example.com/event --mode 2 --skills qa,approval --dry-run --format json - -dws dev app robot enable --unified-app-id --dry-run --format json -``` - -> 旧的独立 `update` 命令已删除(并入 `config`)。 - -参数查 `dws schema dev.app.robot.config`;至少提供一个配置字段,否则 CLI 报错。国际化字段(`--i18n-name` 等)传 JSON,如 `'{"en_US":"Bot"}'`。 - -### 停用 - -```bash -dws dev app robot disable --unified-app-id --dry-run --format json -dws dev app robot disable --unified-app-id --yes --format json -``` +`config` 是 upsert:建或改都用它,不存在则建、存在则改,至少给一个配置字段。国际化字段(`--i18n-name` 等)传 JSON,如 `'{"en_US":"Bot"}'`。`enable` 是纯启用:只开启能力,不带配置字段(只传 `--unified-app-id`)。旧的独立 `update` 已并入 `config`。`config` 成功后回读 `robot get`:返回 `status=2` 就别再误判"待生效",只有 `status=1` 或要重新上架才调 `enable`。 ## 错误处理 | 情况 | 处理 | |------|------| | `robot info is not exist` | 应用未配置机器人,先用 `robot config` 创建 | -| 应用名重复 | `app-name` 企业内需唯一,换个名字 | +| 应用名重复 | `app-name` 企业内需唯一,换名 | | `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | -| 创建任务 `EXPIRED` | 任务过期,重新 `submit`(可带原 taskId 重试) | +| 创建任务 `EXPIRED` | 任务过期,重新 `submit`(可带原 taskId) | -> 把机器人接到本地 agent 调试/值守(渠道、AI 卡片、会话记忆、依赖预检)见 [connect.md](connect.md)。 +> 把机器人接到本地 agent 调试/值守见 [connect.md](connect.md)。 diff --git a/skills/multi/dingtalk-dev/references/security.md b/skills/multi/dingtalk-dev/references/security.md index 2335b249..a376a933 100644 --- a/skills/multi/dingtalk-dev/references/security.md +++ b/skills/multi/dingtalk-dev/references/security.md @@ -1,6 +1,6 @@ # 安全配置 -> 概念锚点:安全配置=应用的 IP 白名单 / 登录重定向 / 端内免登 URL;见 SKILL.md 概念地图。 +> 安全配置=应用的 IP 白名单 / 登录重定向 / 端内免登 URL;见 SKILL.md 概念地图。 `dws dev app security config` 配 IP 白名单(`--ip-whitelist`)、登录重定向(`--redirect-urls`)、端内免登(`--sso-urls`)。参数查 `dws schema dev.app.security.config`,至少给一个配置字段。 diff --git a/skills/multi/dingtalk-dev/references/version.md b/skills/multi/dingtalk-dev/references/version.md index fae7b227..648c5eae 100644 --- a/skills/multi/dingtalk-dev/references/version.md +++ b/skills/multi/dingtalk-dev/references/version.md @@ -1,117 +1,64 @@ # 版本发布 -> 概念锚点:版本是配置变更生效的唯一通道——改配置 ≠ 上线,发布到 RELEASE 才生效(见 SKILL.md 生效模型)。 +> 版本是配置变更生效的唯一通道——改配置不等于上线,发布到 RELEASE 才生效(见 SKILL.md 生效模型)。 -管理开放平台企业内部应用的版本:基于当前配置创建版本、查看版本列表/详情、预检审批、发布、查状态。 - -> `corpId` / `userId` 由系统上下文自动注入,CLI 不传。所有版本通过 `--unified-app-id` 定位,单个版本再加 `--version-id`。 +管理应用版本:基于当前配置建版本、查列表/详情、预检审批、发布、查状态。参数查 `dws schema dev.app.version.`。版本用 `--unified-app-id` 定位,单个版本再加 `--version-id`;`corpId`/`userId` 系统注入,CLI 不传。 ## 典型流程 ```text permission add(requiredApproval=true 写入版本变更) → version create 创建版本 - → version check-approval 预检是否需要审批 / 审批人 + → version check-approval 预检是否需审批 / 审批人 → version publish 发布(含高敏权限需 --confirmed-sensitive) → version status 轮询发布/审批状态 ``` -## 创建版本 +新应用如果 `version list` 返回空,先 `version create`,用返回的 `versionId` 继续 check-approval/publish;不要因列表空误判无可发布内容。 -```bash -dws dev app version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --dry-run --format json -dws dev app version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json -``` +## check-approval 与 publish -参数查 `dws schema dev.app.version.create`。 +`check-approval` 只查审批要求和候选审批人,不发布。`publish` 是真实发布;含高敏权限要加 `--confirmed-sensitive`,灰度选人模式用 `--approver-user-id` 指定审批人。 -## 版本列表 - -```bash -dws dev app version list --unified-app-id --page-size 20 --format json -# 续翻:--cursor <上次出参的 nextCursor> -``` - -游标分页:`--cursor`/`--page-size`,出参带 `nextCursor`(空=到底,续翻原样回传);出参见 SKILL.md「通用出参约定」。 - -新应用如果 `version list` 返回空,先执行 `version create`,用返回的 `versionId` 继续 `check-approval/publish`;不要因为列表为空误判无可发布内容。 - -## 版本详情 - -```bash -dws dev app version get --unified-app-id --version-id --format json -``` - -返回版本状态、描述、能力列表、权限点、敏感权限、审批要求和脱敏详情。 - -## 预检审批(不发布) - -```bash -dws dev app version check-approval --unified-app-id --version-id --format json -``` - -仅返回审批要求和候选审批人,**不会实际发布**。 - -## 发布 - -```bash -dws dev app version publish --unified-app-id --version-id --dry-run --format json -dws dev app version publish --unified-app-id --version-id --yes --format json - -# 含高敏权限时必须确认 -dws dev app version publish --unified-app-id --version-id --confirmed-sensitive --yes --format json - -# 灰度选人模式指定审批人 -dws dev app version publish --unified-app-id --version-id --approver-user-id --yes --format json -``` - -`publish` 是真实发布(区别于 `check-approval` 的预检不发布)。参数查 `dws schema dev.app.version.publish`;含高敏权限要加 `--confirmed-sensitive`,灰度选人模式用 `--approver-user-id` 指定审批人。 - -> 注意:`--dry-run` 是 CLI 层的"预览不执行"开关;服务端的"审批预检"是 `version check-approval`。二者是两个东西:`check-approval` 是只查审批要求不发布,`--dry-run` 是 CLI 要不要真调上游。发布前建议先 `check-approval`。 +区分两个"预检":`--dry-run` 是 CLI 层的预览不调上游;`check-approval` 是服务端查审批要求不发布。发布前建议先 `check-approval`。 发布响应 `result`: | result | 含义 | 下一步 | |--------|------|--------| -| `NOT_REQUIRED` | 不需要审批;预检时表示可直接发布,真实发布时表示已直接发布上线 | 真实发布后回读 `version status/get` 验证 `RELEASE` | -| `APPROVAL_REQUIRED` | 需要审批,通常出现在 `check-approval` 或未指定审批人时 | 查看 `approvalMode/approvalCandidates`,让用户选择审批人后再 `publish --approver-user-id` | +| `NOT_REQUIRED` | 不需审批;预检=可直接发布,真实发布=已直接上线 | 回读 `version status/get` 验证 `RELEASE` | +| `APPROVAL_REQUIRED` | 需审批,常见于 `check-approval` 或未指定审批人 | 看 `approvalMode/approvalCandidates`,让用户选审批人后再 `publish --approver-user-id` | | `SUBMITTED` | 已提交审批 | 保存 `processId`,轮询 `version status` | 审批模式 `approvalMode`: | approvalMode | 含义 | 下一步 | |--------------|------|--------| -| `SELECT_APPROVER` | 灰度选人模式,需要在候选审批人中选一个 | 展示候选审批人,不自动选第一个 | -| `ENTERPRISE_SELF_BUILT` | 企业自建审核模式 | 不传 `--approver-user-id`,按企业自建审核流程等待 | +| `SELECT_APPROVER` | 灰度选人,需在候选审批人里选一个 | 展示候选,不自动取第一个 | +| `ENTERPRISE_SELF_BUILT` | 企业自建审核 | 不传 `--approver-user-id`,按企业自建流程等待 | ## 版本状态 -```bash -dws dev app version status --unified-app-id --version-id --format json -``` - -返回版本状态、流程实例 ID、审批状态和审批意见。审批详情可能只在钉钉客户端可见。 - -版本状态字段:`version create/list/get/status` 统一返回 `versionStatus`,对齐版本枚举。 +`version create/list/get/status` 统一返回 `versionStatus`: | versionStatus | 含义 | 下一步 | |---------------|------|--------| -| `INIT` | 版本已创建或有待发布变更,尚未发布 | 可 `check-approval` / `publish` | -| `AUDIT` | 发布审核中 | 不要重复发布;即使没有返回 `processStatus`,也按审核中处理 | -| `RELEASE` | 已发布生效 | 发布完成,可继续验证权限、机器人、网页应用等能力 | -| `GRAY` | 灰度状态 | 按灰度流程处理;不要当全量已发布 | +| `INIT` | 已创建或有待发布变更,未发布 | 可 `check-approval`/`publish` | +| `AUDIT` | 发布审核中 | 不要重复发布;即使没返回 `processStatus` 也按审核中处理 | +| `RELEASE` | 已发布生效 | 完成,可验证权限/机器人/网页等能力 | +| `GRAY` | 灰度 | 按灰度流程,不要当全量已发布 | -审批流程状态字段:`version status` 的 `processStatus` 只在存在审批流程且后端透出流程态时有值。`versionStatus=AUDIT` 但没有 `processStatus/processInstanceId` 时,不要判失败,仍表示审核中。 +`version status` 的 `processStatus` 只在存在审批流程且后端透出时有值。`versionStatus=AUDIT` 但没 `processStatus/processInstanceId` 时不要判失败,仍是审核中。 | processStatus | 含义 | 下一步 | |---------------|------|--------| -| `UNDER_REVIEW` | 审批中 | 等待审批,必要时把 `processInstanceId` 给用户去钉钉客户端查看 | -| `PASS` | 审批通过 | 继续回读版本,确认是否进入 `RELEASE` | -| `FAIL` | 审批拒绝 | 展示 `processComment`,修改后重新创建/发布版本 | -| `WITHDRAW` / `CANCEL` | 审批撤回或取消 | 回到发布前状态,重新 `check-approval` / `publish` | -| `PUBLISH_FAILED` | 审批后发布失败 | 展示错误信息,重新检查版本状态和后端错误 | +| `UNDER_REVIEW` | 审批中 | 等待,必要时把 `processInstanceId` 给用户去钉钉客户端看 | +| `PASS` | 审批通过 | 继续回读,确认是否进 `RELEASE` | +| `FAIL` | 审批拒绝 | 展示 `processComment`,改后重新建/发版本 | +| `WITHDRAW` / `CANCEL` | 撤回或取消 | 回到发布前,重新 `check-approval`/`publish` | +| `PUBLISH_FAILED` | 审批后发布失败 | 展示错误,重查版本状态和后端错误 | -遇到未列出的状态值时,不要猜测语义;原样展示状态,并回读 `version get/status` 或查开放平台文档/后台详情。 +遇到未列出的状态值,不要猜语义;原样展示,回读 `version get/status` 或查文档/后台。 ## 错误处理 diff --git a/skills/multi/dingtalk-dev/references/webapp.md b/skills/multi/dingtalk-dev/references/webapp.md index 198bd9ac..d9bf0eb3 100644 --- a/skills/multi/dingtalk-dev/references/webapp.md +++ b/skills/multi/dingtalk-dev/references/webapp.md @@ -1,6 +1,6 @@ # 网页应用配置 -> 概念锚点:网页应用=应用的能力扩展之一,钉钉内打开的 H5;见 SKILL.md 概念地图。 +> 网页应用=应用的能力扩展之一,钉钉内打开的 H5;见 SKILL.md 概念地图。 `dws dev app webapp get` 查配置,`webapp config` 配移动端/PC 首页和管理后台地址。参数查 `dws schema dev.app.webapp.`,config 至少给一个配置字段。 From f29655e7e4faab73d311a35257954ed80ede1131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Thu, 18 Jun 2026 10:32:52 +0800 Subject: [PATCH 32/47] fix(devapp): let server assign app version --- docs/devapp-yulan-command-routing.md | 2 +- internal/helpers/devapp.go | 11 +- internal/helpers/devapp_test.go | 13 +- .../multi/dingtalk-dev/references/version.md | 8 +- test/dev_functional/REPORT.md | 75 ------- test/dev_functional/run_cases.py | 206 ------------------ 6 files changed, 22 insertions(+), 293 deletions(-) delete mode 100644 test/dev_functional/REPORT.md delete mode 100644 test/dev_functional/run_cases.py diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index fd1b802c..90965651 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -966,7 +966,7 @@ Implemented version tools (verified against the `op-app` MCP `tools/list`): | CLI | MCP tool | Notes | | --- | --- | --- | -| `devapp version create` | `create_dev_app_version` | Save a new version from current config (`version`, `desc`). | +| `devapp version create` | `create_dev_app_version` | Save a new version from current config. Omit `version` by default so the server auto-increments from the latest released version; only pass `version` when the user explicitly requests a higher version. | | `devapp version list` | `list_dev_app_versions` | Cursor-paged list (`cursor`, `pageSize`; response `items/nextCursor/hasMore`). | | `devapp version get` | `get_dev_app_version_detail` | One version detail by `versionId`. | | `devapp version check-approval` | `publish_dev_app_version` (`precheckOnly=true`) | Precheck approval requirement / approvers; does not publish. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index d8bfdb26..4778283f 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -1145,7 +1145,7 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "基于当前配置创建应用新版本", - Example: " dws dev app version create --unified-app-id UNIFIED_APP_ID --version 1.0.1 --desc \"新增机器人能力\" --dry-run --format json", + Example: " dws dev app version create --unified-app-id UNIFIED_APP_ID --desc \"新增机器人能力\" --dry-run --format json", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -1156,17 +1156,14 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { if err != nil { return err } - version := devAppStringFlag(cmd, "version") - if version == "" { - return apperrors.NewValidation("--version 为必填") - } - params := map[string]any{"unifiedAppId": appID, "version": version} + params := map[string]any{"unifiedAppId": appID} + devAppPutString(params, "version", devAppStringFlag(cmd, "version")) devAppPutString(params, "desc", devAppStringFlag(cmd, "desc")) return runDevAppTool(runner, cmd, devAppVersionCreateTool, params) }, } addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("version", "", "版本号,如 1.0.1") + cmd.Flags().String("version", "", "高级可选:显式版本号,如 1.0.1;默认不传,由服务端基于最新已发布版本自动递增") cmd.Flags().String("desc", "", "版本描述") preferLegacyLeaf(cmd) annotateDevAppTool(cmd, devAppVersionCreateTool) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index b9f0d7f8..877c4480 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -222,7 +222,16 @@ func TestDevAppVersionCommandsBuildToolParams(t *testing.T) { wantParams map[string]any }{ { - name: "create", + name: "create lets server auto-increment version", + args: []string{"version", "create", "--unified-app-id", "u-1", "--desc", "新增机器人", "--yes"}, + wantTool: "create_dev_app_version", + wantParams: map[string]any{ + "unifiedAppId": "u-1", + "desc": "新增机器人", + }, + }, + { + name: "create with explicit version", args: []string{"version", "create", "--unified-app-id", "u-1", "--version", "1.0.1", "--desc", "新增机器人", "--yes"}, wantTool: "create_dev_app_version", wantParams: map[string]any{ @@ -302,7 +311,7 @@ func TestDevAppRobotAndVersionWritesRequireGuard(t *testing.T) { }{ {name: "robot config", args: []string{"dev", "app", "robot", "config", "--unified-app-id", "u-1", "--name", "小助手"}}, {name: "robot disable", args: []string{"dev", "app", "robot", "disable", "--unified-app-id", "u-1"}}, - {name: "version create", args: []string{"dev", "app", "version", "create", "--unified-app-id", "u-1", "--version", "1.0.1"}}, + {name: "version create", args: []string{"dev", "app", "version", "create", "--unified-app-id", "u-1"}}, {name: "version publish", args: []string{"dev", "app", "version", "publish", "--unified-app-id", "u-1", "--version-id", "v-1"}}, } diff --git a/skills/multi/dingtalk-dev/references/version.md b/skills/multi/dingtalk-dev/references/version.md index fae7b227..afb175bf 100644 --- a/skills/multi/dingtalk-dev/references/version.md +++ b/skills/multi/dingtalk-dev/references/version.md @@ -19,12 +19,16 @@ permission add(requiredApproval=true 写入版本变更) ## 创建版本 ```bash -dws dev app version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --dry-run --format json -dws dev app version create --unified-app-id --version 1.0.1 --desc "新增机器人能力" --yes --format json +dws dev app version create --unified-app-id --desc "新增机器人能力" --dry-run --format json +dws dev app version create --unified-app-id --desc "新增机器人能力" --yes --format json ``` 参数查 `dws schema dev.app.version.create`。 +默认创建版本时不要随机生成 `--version`,不传时服务端会基于最新已发布版本自动递增。只有用户明确指定版本号时才填写 `--version`,并且目标版本必须大于最新 `RELEASE` 版本,否则服务端会返回 `62018`(应用版本号需要高于上个版本号)。 + +真实创建成功后,后续 `get` / `check-approval` / `publish` 必须使用 `create` 返回的 `versionId`。不要通过 `list` 猜测最新版本;如果创建结果没有返回 `versionId`,应停止并报错。 + ## 版本列表 ```bash diff --git a/test/dev_functional/REPORT.md b/test/dev_functional/REPORT.md deleted file mode 100644 index 3005106b..00000000 --- a/test/dev_functional/REPORT.md +++ /dev/null @@ -1,75 +0,0 @@ -# dev 命令树功能测试报告 - -> 日期:2026-06-12 ~ 06-13 | 分支:`feat/dws-dev` | 被测二进制:本地源码构建 -> 结论:**功能层 96/96 PASS;真实数据层 dev doc 全通过,dev app/connect 因预发网关 key 不可得降级(已确认,留内网验收)**。发现并修复缺陷 1 个(`version create` 缺 `--version` 必填校验)。 - -## 范围与方法 - -- 被测对象:本次重命名与参数优化后的全部 dev 命令树——`dws dev app *`(CRUD/凭证/网页应用/权限/成员/安全/机器人/版本,34 条)、`dws dev connect`、`dws dev doc search`,以及旧名移除验证。 -- 方法:CLI 功能层测试。`--dry-run` 验证 flag 解析、CLI→MCP 参数映射、命令路由;错误用例验证必填校验、写守卫(无 `--yes` 拦截)、未知渠道/未知 flag 拒绝;别名用例验证 `--app-id`/`--permission`/`--scope`/`--limit`/`--offset` 隐藏兼容。 -- 执行:`test/dev_functional/run_cases.py`,20 并发(ThreadPool),单用例超时 30s。 -- 边界声明:devapp 是 helper-only 产品,真实 MCP 调用需内部版注入 endpoint(或 `DINGTALK_DEVAPP_MCP_URL`),本报告不含真实上游调用;上游契约(字段语义、错误码)以预发真机验收为准。 - -## 结果总览 - -| 命令组 | 用例 | 通过 | 覆盖要点 | -|--------|------|------|----------| -| 根/旧名 | 4 | 4 | dev 三支柱 help;`devapp`/`app` 旧名确认移除 | -| app list | 5 | 5 | 无参/按名/agentId/分页映射/排序 | -| app get | 3 | 3 | 两种定位 + 缺定位报错 | -| app create | 4 | 4 | 全字段/缺 name/`--type` 确认已删/写守卫 | -| app update | 3 | 3 | 改名/无更新字段报错/缺定位报错 | -| 生命周期 | 5 | 5 | delete/inactive/active + 守卫 + 缺定位 | -| credentials | 3 | 3 | 两种定位 + 缺定位报错 | -| webapp | 5 | 5 | get/单字段/多字段/无字段报错/守卫 | -| permission list | 8 | 8 | 默认分页/`--page` 换算 offset(3,50→100,50)/旧 flag 兼容/keyword/scope 及别名/search 别名/缺定位 | -| permission add | 5 | 5 | 批量/两种别名/缺参/守卫 | -| permission remove | 6 | 6 | 单条原样出参/批量 results 聚合/两种别名/缺参/守卫 | -| member | 8 | 8 | 新主名/`--app-id` 兼容/缺参报 `--unified-app-id`/trim/守卫 | -| security | 6 | 6 | 三字段各自/组合/无字段报错/守卫 | -| robot | 13 | 13 | 同步建/异步 submit+result/get/config/update/enable/offline/缺参/守卫/`robot connect` 确认已移除 | -| version | 8 | 8 | create/list/get/check-approval/publish(approver+confirm-sensitive)/status/缺 version 报错/守卫 | -| dev connect | 5 | 5 | 显式凭证 dry-run/unified-app-id 凭证源/未知渠道拒绝/缺凭证报错/agent 参数全开 | -| dev doc | 4 | 4 | --query/位置参数/分页/缺参 | -| pretty | 1 | 1 | pretty 格式不崩溃 | -| **合计** | **96** | **96** | | - -执行耗时:累计 107.7s(20 并发墙钟约 8s),最慢用例 cred-agent 3.1s。 - -## 缺陷与修复 - -| # | 严重度 | 描述 | 修复 | -|---|--------|------|------| -| 1 | 中 | `dev app version create` 不传 `--version` 通过 CLI 校验,空版本号会直接下发上游 | `devapp.go` 补必填校验,报 `--version is required`;用例 ver-create-missing 回归通过 | - -## 复跑方式 - -```bash -go build -o /tmp/dws-dev ./cmd -python3 test/dev_functional/run_cases.py /tmp/dws-dev test/dev_functional/results.jsonl -``` - -用例与预期内联在 `run_cases.py`(id / 命令 / 期望退出码 / 输出必含子串),新增命令时在 CASES 里加行即可。 - -## 真实数据验证(2026-06-13) - -- 认证:`auth status` 真实校验通过(token 有效,corp ding8196...b85)。 -- `dev doc search` 真实调用 **3/3 PASS**(devdoc 是正常发现的 MCP 服务,不依赖 op-app 网关): - - `--query "errcode 40035"` → 返回真实文档命中(hasMore 翻页字段正常) - - 位置参数 `"机器人回调" --size 3` → 命中「机器人接收消息」等相关文档,size 生效 - - 出参结构与 output-schema.md 描述一致(`result.items[].title/url` + `success`) -- `dev app *` / `dev connect` 真实调用被阻断:devapp 固定指向 `pre-mcp-gw.dingtalk.com/server/op-app`,网关要求带 `?key=` 的完整地址(按安全设计不入库),本机日志/历史/配置均无该 key,裸地址连接被网关断开(EOF,有 recovery 事件记录)。已与负责人确认采用降级方案:真实链路留待内网环境验收,重点验 permission remove 批量真实聚合、connect 真实建联、version publish 审批链路。注入方式:`DINGTALK_DEVAPP_MCP_URL='<含key地址>' /tmp/dws-dev dev app list`。 - -## cursor 分页改造验证(2026-06-15) - -4 个分页命令(app list / permission list / version list / doc search)对外改为游标分页(`--cursor` + `--page-size`,出参注入 `nextCursor`/`hasMore`)。上游仍 page/offset,CLI 合成 cursor 顶替,上游上线真游标后透传。设计规格见 `docs/cursor-pagination-design.md`。 - -- 功能用例扩到 **106 个全 PASS**(新增 10 个 cursor 用例:解码续翻、跨命令 cursor 拒绝、非法 cursor 拒绝、legacy flag 兼容,覆盖 4 个命令)。 -- cursor 单测 7 个全 PASS(编解码往返、跨工具拒绝、非法拒绝、满页合成/到底/上游透传/nil 安全)。 -- 真实数据验证:`dev doc search` 三页连翻(`--page-size 2` → `nextCursor` 续翻),三页首条互不相同、`hasMore` 正确翻转,到底返回空 cursor。 -- dev app/permission/version 的 cursor 解码经 dry-run 验证(cursor→currentPage/offset 换算正确,跨命令 cursor 报「不属于当前命令」);真实游标续翻待内网验收。 - -## 遗留 - -- 真实上游调用(预发 endpoint + 真实凭证)未覆盖,需内部版环境验收:重点验 permission remove 批量逐条调用的真实聚合、connect 真实建联、version publish 审批链路。 -- `dws dev connect` 正式(非 dry-run)模式为前台长驻进程,不适合自动化用例,建联 e2e 需人工或专用 harness。 diff --git a/test/dev_functional/run_cases.py b/test/dev_functional/run_cases.py deleted file mode 100644 index 64dacba2..00000000 --- a/test/dev_functional/run_cases.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env python3 -"""dev 命令树功能测试执行器:并发跑用例,输出 JSONL 结果。 - -只负责机械执行与取数(跑命令、收输出、比对预期子串); -失败归因与报告判断由调用方(agent)完成。 -""" -import concurrent.futures -import json -import subprocess -import sys -import time - -DWS = sys.argv[1] if len(sys.argv) > 1 else "/tmp/dws-dev" -PARALLEL = 20 -TIMEOUT = 30 - - -# 用例格式: (id, args, expect_exit, [output 必须包含的子串]) -# expect_exit: 0=成功, 1=报错(CLI 校验/未知命令等) -U = "--unified-app-id" -CASES = [ - # ---- 根与旧名 ---- - ("root-dev-help", ["dev", "--help"], 0, ["app", "connect", "doc"]), - ("root-dev-app-help", ["dev", "app", "--help"], 0, ["credentials", "permission", "robot", "version"]), - ("root-old-devapp-removed", ["devapp", "list"], 1, []), - ("root-old-app-alias-removed", ["app", "list"], 1, []), - - # ---- app list ---- - ("list-plain", ["dev", "app", "list", "--dry-run", "--format", "json"], 0, ["list_dev_app"]), - ("list-name", ["dev", "app", "list", "--name", "Demo", "--dry-run", "--format", "json"], 0, ['"appName": "Demo"']), - ("list-app-key", ["dev", "app", "list", "--app-key", "dingxxx", "--dry-run", "--format", "json"], 0, ['"appKey": "dingxxx"']), - ("list-agent-id-removed", ["dev", "app", "list", "--agent-id", "123", "--dry-run", "--format", "json"], 1, ["unknown flag"]), - ("list-sort", ["dev", "app", "list", "--creator", "张三", "--sort", "gmt_modified", "--order", "desc", "--dry-run", "--format", "json"], 0, ['"sortType"', '"sortOrder"']), - - # ---- cursor 分页(纯透传:--cursor/--page-size 原样进 params,无合成/换算)---- - ("cursor-list-first", ["dev", "app", "list", "--page-size", "20", "--dry-run", "--format", "json"], 0, ['"pageSize": 20']), - ("cursor-list-passthrough", ["dev", "app", "list", "--cursor", "TOKEN-ABC", "--page-size", "30", "--dry-run", "--format", "json"], 0, ['"cursor": "TOKEN-ABC"', '"pageSize": 30']), - # --page 不是独立 flag,但 cobra 把它当 --page-size 的前缀缩写(唯一前缀),故 --page 2 == --page-size 2 - ("cursor-list-page-abbrev", ["dev", "app", "list", "--page", "2", "--dry-run", "--format", "json"], 0, ['"pageSize": 2']), - ("cursor-perm-passthrough", ["dev", "app", "permission", "list", "--unified-app-id", "u-1", "--cursor", "TOK-P", "--page-size", "50", "--dry-run", "--format", "json"], 0, ['"cursor": "TOK-P"', '"pageSize": 50']), - ("cursor-perm-limit-removed", ["dev", "app", "permission", "list", "--unified-app-id", "u-1", "--limit", "10", "--dry-run", "--format", "json"], 1, ["unknown flag"]), - ("cursor-version-passthrough", ["dev", "app", "version", "list", "--unified-app-id", "u-1", "--cursor", "TOK-V", "--page-size", "20", "--dry-run", "--format", "json"], 0, ['"cursor": "TOK-V"', '"pageSize": 20']), - ("cursor-doc-passthrough", ["dev", "doc", "search", "--query", "x", "--cursor", "TOK-D", "--page-size", "5", "--dry-run", "--format", "json"], 0, ['"cursor": "TOK-D"', '"pageSize": 5']), - ("cursor-doc-size-removed", ["dev", "doc", "search", "--query", "x", "--size", "5", "--dry-run", "--format", "json"], 1, ["unknown flag"]), - - # ---- app get ---- - ("get-unified", ["dev", "app", "get", U, "u-1", "--dry-run", "--format", "json"], 0, ["get_dev_app", '"unifiedAppId": "u-1"']), - ("get-agent-id-removed", ["dev", "app", "get", "--agent-id", "123", "--dry-run", "--format", "json"], 1, ["unknown flag"]), - ("get-no-locator", ["dev", "app", "get", "--dry-run", "--format", "json"], 1, []), - - # ---- app create ---- - ("create-full", ["dev", "app", "create", "--name", "Demo", "--desc", "d", "--icon", "m1", "--dry-run", "--format", "json"], 0, ["create_dev_app", '"appName": "Demo"', '"desc"', '"icon"']), - ("create-no-name", ["dev", "app", "create", "--dry-run", "--format", "json"], 1, ["--name"]), - ("create-type-removed", ["dev", "app", "create", "--name", "X", "--type", "internal", "--dry-run"], 1, ["unknown flag"]), - ("create-write-guard", ["dev", "app", "create", "--name", "Demo", "--format", "json"], 1, []), - - # ---- app update ---- - ("update-name", ["dev", "app", "update", U, "u-1", "--name", "N2", "--dry-run", "--format", "json"], 0, ["update_dev_app", '"appName": "N2"']), - ("update-no-field", ["dev", "app", "update", U, "u-1", "--dry-run", "--format", "json"], 1, ["至少提供一项"]), - ("update-no-locator", ["dev", "app", "update", "--name", "N2", "--dry-run", "--format", "json"], 1, []), - - # ---- app lifecycle ---- - ("delete-dry", ["dev", "app", "delete", U, "u-1", "--dry-run", "--format", "json"], 0, ["delete_dev_app"]), - ("disable-dry", ["dev", "app", "disable", U, "u-1", "--dry-run", "--format", "json"], 0, ["disable_dev_app"]), - ("enable-dry", ["dev", "app", "enable", U, "u-1", "--dry-run", "--format", "json"], 0, ["enable_dev_app"]), - ("delete-write-guard", ["dev", "app", "delete", U, "u-1", "--format", "json"], 1, []), - ("delete-no-confirm-name", ["dev", "app", "delete", U, "u-1", "--yes", "--format", "json"], 1, ["二次确认"]), - ("disable-no-locator", ["dev", "app", "disable", "--dry-run", "--format", "json"], 1, []), - - # ---- credentials ---- - ("cred-unified", ["dev", "app", "credentials", "get", U, "u-1", "--dry-run", "--format", "json"], 0, ["get_dev_app_credentials"]), - ("cred-agent", ["dev", "app", "credentials", "get", "--unified-app-id", "u-1", "--dry-run", "--format", "json"], 0, ['"unifiedAppId": "u-1"']), - ("cred-no-locator", ["dev", "app", "credentials", "get", "--dry-run", "--format", "json"], 1, []), - - # ---- webapp ---- - ("webapp-get", ["dev", "app", "webapp", "get", "--unified-app-id", "u-1", "--dry-run", "--format", "json"], 0, ["get_extension_webapp_config"]), - ("webapp-config", ["dev", "app", "webapp", "config", "--unified-app-id", "u-1", "--homepage-url", "https://x.invalid/m", "--dry-run", "--format", "json"], 0, ["set_extension_webapp_config", '"homepageUrl"']), - ("webapp-config-pc", ["dev", "app", "webapp", "config", U, "u-1", "--pc-homepage-url", "https://x.invalid/pc", "--omp-url", "https://x.invalid/o", "--dry-run", "--format", "json"], 0, ['"pcHomepageUrl"', '"ompUrl"']), - ("webapp-config-no-field", ["dev", "app", "webapp", "config", "--unified-app-id", "u-1", "--dry-run", "--format", "json"], 1, []), - ("webapp-config-guard", ["dev", "app", "webapp", "config", "--unified-app-id", "u-1", "--homepage-url", "https://x.invalid/m", "--format", "json"], 1, []), - - # ---- permission list ---- - ("perm-list", ["dev", "app", "permission", "list", U, "u-1", "--dry-run", "--format", "json"], 0, ["list_dev_app_permissions", '"pageSize": 20', '"authStatus": "ALL"']), - ("perm-list-page-size", ["dev", "app", "permission", "list", U, "u-1", "--page-size", "50", "--dry-run", "--format", "json"], 0, ['"pageSize": 50']), - ("perm-list-keyword", ["dev", "app", "permission", "list", U, "u-1", "--keyword", "机器人", "--status", "unauthed", "--scope-type", "sns", "--dry-run", "--format", "json"], 0, ['"keyword"', '"authStatus": "UNAUTHED"', '"scopeType": "SNS"']), - ("perm-list-scope", ["dev", "app", "permission", "list", U, "u-1", "--scope", "qyapi_robot_sendmsg", "--dry-run", "--format", "json"], 0, ['"scopeValue"']), - ("perm-list-scope-alias", ["dev", "app", "permission", "list", U, "u-1", "--permission", "qyapi_robot_sendmsg", "--dry-run", "--format", "json"], 0, ['"scopeValue"']), - ("perm-list-search-alias", ["dev", "app", "permission", "search", U, "u-1", "--dry-run", "--format", "json"], 0, ["list_dev_app_permissions"]), - ("perm-list-no-locator", ["dev", "app", "permission", "list", "--dry-run", "--format", "json"], 1, []), - - # ---- permission add ---- - ("perm-add-multi", ["dev", "app", "permission", "add", U, "u-1", "--permissions", "A,B", "--dry-run", "--format", "json"], 0, ["apply_dev_app_permissions", '"A"', '"B"']), - ("perm-add-scope-alias", ["dev", "app", "permission", "add", U, "u-1", "--scope", "A", "--dry-run", "--format", "json"], 0, ['"scopeValues"']), - ("perm-add-perm-alias", ["dev", "app", "permission", "add", U, "u-1", "--permission", "A", "--dry-run", "--format", "json"], 0, ['"scopeValues"']), - ("perm-add-missing", ["dev", "app", "permission", "add", U, "u-1", "--dry-run", "--format", "json"], 1, ["--permissions"]), - ("perm-add-guard", ["dev", "app", "permission", "add", U, "u-1", "--permissions", "A", "--format", "json"], 1, []), - - # ---- permission remove ---- - ("perm-rm-single", ["dev", "app", "permission", "remove", U, "u-1", "--permissions", "A", "--dry-run", "--format", "json"], 0, ["remove_dev_app_permissions", '"scopeValue": "A"']), - ("perm-rm-batch", ["dev", "app", "permission", "remove", U, "u-1", "--permissions", "A,B", "--dry-run", "--format", "json"], 0, ['"results"', '"scopeValue": "A"', '"scopeValue": "B"']), - ("perm-rm-old-alias", ["dev", "app", "permission", "remove", U, "u-1", "--permission", "A", "--dry-run", "--format", "json"], 0, ['"scopeValue": "A"']), - ("perm-rm-scope-alias", ["dev", "app", "permission", "remove", U, "u-1", "--scope", "A", "--dry-run", "--format", "json"], 0, ['"scopeValue": "A"']), - ("perm-rm-missing", ["dev", "app", "permission", "remove", U, "u-1", "--dry-run", "--format", "json"], 1, ["--permissions"]), - ("perm-rm-guard", ["dev", "app", "permission", "remove", U, "u-1", "--permissions", "A", "--format", "json"], 1, []), - - # ---- member ---- - ("member-list", ["dev", "app", "member", "list", U, "u-1", "--dry-run", "--format", "json"], 0, ["list_dev_app_members"]), - ("member-list-appid-removed", ["dev", "app", "member", "list", "--app-id", "u-1", "--dry-run", "--format", "json"], 1, ["unknown flag"]), - ("member-list-missing", ["dev", "app", "member", "list", "--dry-run", "--format", "json"], 1, ["--unified-app-id"]), - ("member-add", ["dev", "app", "member", "add", U, "u-1", "--users", "a,b", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 0, ["add_dev_app_members", '"memberUserIds"']), - ("member-add-trim", ["dev", "app", "member", "add", U, "u-1", "--users", " a , b ", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 0, ['"a"', '"b"']), - ("member-add-no-users", ["dev", "app", "member", "add", U, "u-1", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 1, ["--users"]), - ("member-remove", ["dev", "app", "member", "remove", U, "u-1", "--users", "a", "--member-type", "DEVELOPER", "--dry-run", "--format", "json"], 0, ["remove_dev_app_members"]), - ("member-add-guard", ["dev", "app", "member", "add", U, "u-1", "--users", "a", "--member-type", "DEVELOPER", "--format", "json"], 1, []), - - # ---- security ---- - ("sec-ip", ["dev", "app", "security", "config", U, "u-1", "--ip-whitelist", "192.0.2.1,192.0.2.2", "--dry-run", "--format", "json"], 0, ["update_dev_app_security_config", '"ipWhitelist"']), - ("sec-redirect", ["dev", "app", "security", "config", U, "u-1", "--redirect-url", "https://x.invalid/cb", "--dry-run", "--format", "json"], 0, ['"redirectUrls"']), - ("sec-sso", ["dev", "app", "security", "config", U, "u-1", "--sso-url", "https://x.invalid/sso", "--dry-run", "--format", "json"], 0, ['"ssoUrls"']), - ("sec-combined", ["dev", "app", "security", "config", "--unified-app-id", "u-1", "--ip-whitelist", "192.0.2.1", "--redirect-url", "https://x.invalid/cb", "--dry-run", "--format", "json"], 0, ['"ipWhitelist"', '"redirectUrls"']), - ("sec-no-field", ["dev", "app", "security", "config", U, "u-1", "--dry-run", "--format", "json"], 1, ["至少提供一项安全配置"]), - ("sec-guard", ["dev", "app", "security", "config", U, "u-1", "--ip-whitelist", "192.0.2.1", "--format", "json"], 1, []), - - # ---- robot ---- - ("robot-submit", ["dev", "app", "robot", "submit", "--app-name", "智能体", "--robot-name", "小助手", "--desc", "审批", "--task-id", "t-1", "--dry-run", "--format", "json"], 0, ["submit_robot_create_task", '"taskId": "t-1"']), - ("robot-result", ["dev", "app", "robot", "result", "--task-id", "t-1", "--format", "json", "--dry-run"], 0, ["query_robot_create_result"]), - ("robot-result-missing", ["dev", "app", "robot", "result", "--format", "json", "--dry-run"], 1, []), - ("robot-get", ["dev", "app", "robot", "get", U, "u-1", "--dry-run", "--format", "json"], 0, ["get_extension_robot_config"]), - ("robot-config-upsert", ["dev", "app", "robot", "config", U, "u-1", "--name", "小助手", "--mode", "2", "--skills", "qa,approval", "--dry-run", "--format", "json"], 0, ["set_extension_robot_config", '"skillList"']), - # update 子命令已并入 config(upsert):robot update 落到 robot 组、报错重定向到 robot --help - ("robot-update-removed", ["dev", "app", "robot", "update", U, "u-1", "--brief", "新简介", "--dry-run", "--format", "json"], 1, ["robot --help"]), - ("robot-enable-pure", ["dev", "app", "robot", "enable", U, "u-1", "--dry-run", "--format", "json"], 0, ["enable_dev_app_robot"]), - ("robot-config-no-field", ["dev", "app", "robot", "config", U, "u-1", "--dry-run", "--format", "json"], 1, []), - ("robot-disable", ["dev", "app", "robot", "disable", U, "u-1", "--dry-run", "--format", "json"], 0, ["disable_dev_app_robot"]), - ("robot-connect-removed", ["dev", "app", "robot", "connect", "--dry-run"], 1, []), - - # ---- version ---- - ("ver-create", ["dev", "app", "version", "create", U, "u-1", "--version", "1.0.1", "--desc", "d", "--dry-run", "--format", "json"], 0, ["create_dev_app_version", '"version": "1.0.1"']), - ("ver-list", ["dev", "app", "version", "list", U, "u-1", "--page-size", "20", "--dry-run", "--format", "json"], 0, ["list_dev_app_versions", '"pageSize": 20']), - ("ver-get", ["dev", "app", "version", "get", U, "u-1", "--version-id", "v-1", "--dry-run", "--format", "json"], 0, ["get_dev_app_version_detail"]), - ("ver-check", ["dev", "app", "version", "check-approval", U, "u-1", "--version-id", "v-1", "--dry-run", "--format", "json"], 0, ["publish_dev_app_version"]), - ("ver-publish", ["dev", "app", "version", "publish", U, "u-1", "--version-id", "v-1", "--approver", "user1", "--confirm-sensitive", "--dry-run", "--format", "json"], 0, ["publish_dev_app_version", '"approverUserId"', '"confirmedSensitive"']), - ("ver-publish-guard", ["dev", "app", "version", "publish", U, "u-1", "--version-id", "v-1", "--format", "json"], 1, []), - ("ver-status", ["dev", "app", "version", "status", U, "u-1", "--version-id", "v-1", "--dry-run", "--format", "json"], 0, ["get_dev_app_version_status"]), - ("event-list", ["dev", "app", "event", "list", U, "u-1", "--page-size", "10", "--dry-run", "--format", "json"], 0, ["list_dev_app_events", '"pageSize": 10']), - ("event-sub", ["dev", "app", "event", "subscribe", U, "u-1", "--event-types", "t1,t2", "--callback-url", "https://x.invalid", "--dry-run", "--format", "json"], 0, ["subscribe_dev_app_event", '"eventTypes"']), - ("event-unsub", ["dev", "app", "event", "unsubscribe", U, "u-1", "--event-types", "t1", "--dry-run", "--format", "json"], 0, ["unsubscribe_dev_app_event"]), - ("event-list-missing", ["dev", "app", "event", "list", "--dry-run", "--format", "json"], 1, []), - ("ver-create-missing", ["dev", "app", "version", "create", U, "u-1", "--dry-run", "--format", "json"], 1, []), - - # ---- dev connect ---- - ("connect-dry-flags", ["dev", "connect", "--channel", "claudecode", "--robot-client-id", "id1", "--robot-client-secret", "s1", "--dry-run", "--format", "json"], 0, ['"channel": "claudecode"', '"cli"', '"connect"']), - ("connect-dry-unified", ["dev", "connect", "--channel", "qoderwork", U, "ua-1", "--dry-run", "--format", "json"], 0, ['"credentialSource"', '"unifiedAppId": "ua-1"']), - ("connect-bad-channel", ["dev", "connect", "--channel", "nope", "--robot-client-id", "i", "--robot-client-secret", "s", "--dry-run"], 1, ["未知渠道"]), - ("connect-no-cred", ["dev", "connect", "--channel", "claudecode", "--dry-run", "--format", "json"], 1, ["--robot-client-id"]), - ("connect-agent-opts", ["dev", "connect", "--channel", "codex", "--robot-client-id", "i", "--robot-client-secret", "s", "--agent-model", "m1", "--agent-workdir", "/tmp", "--agent-memory=false", "--reply-card=false", "--dry-run", "--format", "json"], 0, ['"channel": "codex"']), - - # ---- dev doc ---- - ("doc-search", ["dev", "doc", "search", "--query", "errcode 40035", "--dry-run", "--format", "json"], 0, ["search_open_platform_docs", '"keyword": "errcode 40035"']), - ("doc-search-positional", ["dev", "doc", "search", "回调失败", "--dry-run", "--format", "json"], 0, ['"keyword": "回调失败"']), - ("doc-search-paging", ["dev", "doc", "search", "--query", "x", "--page-size", "5", "--dry-run", "--format", "json"], 0, ['"pageSize": 5']), - ("doc-search-missing", ["dev", "doc", "search", "--dry-run", "--format", "json"], 1, ["--query"]), - - # ---- pretty 出参 ---- - ("pretty-no-crash", ["dev", "app", "get", U, "u-1", "--dry-run", "--format", "pretty"], 0, []), -] - - -def run_case(case): - cid, args, want_exit, want_subs = case - started = time.time() - try: - proc = subprocess.run( - [DWS, *args], capture_output=True, text=True, timeout=TIMEOUT - ) - out = (proc.stdout or "") + (proc.stderr or "") - exit_ok = (proc.returncode == 0) == (want_exit == 0) - missing = [s for s in want_subs if s not in out] - status = "PASS" if exit_ok and not missing else "FAIL" - return { - "id": cid, "status": status, "exit": proc.returncode, - "want_exit": want_exit, "missing": missing, - "ms": int((time.time() - started) * 1000), - "cmd": "dws " + " ".join(args), - "output_head": out[:600], - } - except subprocess.TimeoutExpired: - return {"id": cid, "status": "TIMEOUT", "ms": TIMEOUT * 1000, - "cmd": "dws " + " ".join(args), "output_head": ""} - - -def main(): - results = [] - with concurrent.futures.ThreadPoolExecutor(max_workers=PARALLEL) as pool: - for res in pool.map(run_case, CASES): - results.append(res) - print(f"[{res['status']}] {res['id']}", file=sys.stderr) - with open(sys.argv[2] if len(sys.argv) > 2 else "results.jsonl", "w") as f: - for r in results: - f.write(json.dumps(r, ensure_ascii=False) + "\n") - passed = sum(1 for r in results if r["status"] == "PASS") - print(f"\n{passed}/{len(results)} PASS", file=sys.stderr) - - -if __name__ == "__main__": - main() From 493ca36e0850a3f03961b08727fab5929c1bef30 Mon Sep 17 00:00:00 2001 From: "shangguanxuan.sgx" Date: Wed, 10 Jun 2026 09:38:45 +0800 Subject: [PATCH 33/47] fix(pat): carry agentCode in batch auth args --- internal/app/runner.go | 5 +- internal/app/runner_test.go | 12 ++ internal/auth/channel.go | 39 +++- internal/pat/chmod.go | 177 ++++++++++++---- internal/pat/chmod_test.go | 271 +++++++++++++++++++++--- internal/pat/pat.go | 5 +- test/unit/pat_host_owned_signal_test.go | 36 +++- 7 files changed, 463 insertions(+), 82 deletions(-) diff --git a/internal/app/runner.go b/internal/app/runner.go index 119f0c7c..e9b68daa 100644 --- a/internal/app/runner.go +++ b/internal/app/runner.go @@ -111,7 +111,7 @@ func logHostOwnedPATDecisionOnce() { hostOwnedPATDecisionOnce.Do(func() { slog.Debug("runtime.host_owned_pat", "hostOwned", authpkg.HostOwnsPATFlow(), - "agentCodeEnvPresent", os.Getenv(authpkg.AgentCodeEnv) != "", + "agentCodeEnvPresent", authpkg.AgentCodeEnvPresent(), ) }) } @@ -696,9 +696,10 @@ func resolveIdentityHeaders() map[string]string { if sessionID == "" { sessionID = os.Getenv(envRewindSessionID) } + agentCode, _ := authpkg.AgentCodeFromEnv() envHeaders := map[string]string{ "x-dingtalk-agent": os.Getenv(envDingtalkAgent), - "x-dingtalk-dws-agent-code": strings.TrimSpace(os.Getenv(authpkg.AgentCodeEnv)), + "x-dingtalk-dws-agent-code": agentCode, "x-dingtalk-trace-id": os.Getenv(envDingtalkTraceID), "x-dingtalk-session-id": sessionID, "x-dingtalk-message-id": os.Getenv(envDingtalkMessageID), diff --git a/internal/app/runner_test.go b/internal/app/runner_test.go index fd3fd673..0803ff79 100644 --- a/internal/app/runner_test.go +++ b/internal/app/runner_test.go @@ -321,6 +321,7 @@ func TestRuntimeRunnerInjectsAuthTokenFromFlag(t *testing.T) { func TestResolveIdentityHeadersForwardsAgentCode(t *testing.T) { setupRuntimeCommandTest(t) t.Setenv(authpkg.AgentCodeEnv, " cursor ") + t.Setenv(authpkg.AgentCodeEnvCompat, "") headers := resolveIdentityHeaders() if got := headers["x-dingtalk-dws-agent-code"]; got != "cursor" { @@ -328,6 +329,17 @@ func TestResolveIdentityHeadersForwardsAgentCode(t *testing.T) { } } +func TestResolveIdentityHeadersForwardsCompatAgentCode(t *testing.T) { + setupRuntimeCommandTest(t) + t.Setenv(authpkg.AgentCodeEnv, "") + t.Setenv(authpkg.AgentCodeEnvCompat, " compat ") + + headers := resolveIdentityHeaders() + if got := headers["x-dingtalk-dws-agent-code"]; got != "compat" { + t.Fatalf("x-dingtalk-dws-agent-code = %q, want compat", got) + } +} + func TestResolveIdentityHeadersSessionEnvPriority(t *testing.T) { setupRuntimeCommandTest(t) t.Setenv(envDingtalkSessionID, "ding-session") diff --git a/internal/auth/channel.go b/internal/auth/channel.go index e9c1f49d..38c3753c 100644 --- a/internal/auth/channel.go +++ b/internal/auth/channel.go @@ -19,19 +19,40 @@ import ( ) const ( - // AgentCodeEnv is the sole per-spawn environment variable the host injects - // to declare "this process is driven by a third-party Agent host, render - // authorization UI yourselves". + // AgentCodeEnv is the primary per-spawn environment variable the host + // injects to declare "this process is driven by a third-party Agent host, + // render authorization UI yourselves". AgentCodeEnv = "DINGTALK_DWS_AGENTCODE" + + // AgentCodeEnvCompat is a compatibility alias for hosts that shipped the + // reversed prefix before AgentCodeEnv became the public spelling. + AgentCodeEnvCompat = "DWS_DINGTALK_AGENTCODE" ) +// AgentCodeFromEnv returns the effective host agent code and the env name that +// supplied it. The primary public spelling wins over the compatibility alias. +func AgentCodeFromEnv() (string, string) { + if value := strings.TrimSpace(os.Getenv(AgentCodeEnv)); value != "" { + return value, AgentCodeEnv + } + if value := strings.TrimSpace(os.Getenv(AgentCodeEnvCompat)); value != "" { + return value, AgentCodeEnvCompat + } + return "", "" +} + +func AgentCodeEnvPresent() bool { + value, _ := AgentCodeFromEnv() + return value != "" +} + // HostOwnsPATFlow reports whether the current process is running under a // third-party Agent host that will render the PAT authorization card -// itself. The sole trigger is AgentCodeEnv (DINGTALK_DWS_AGENTCODE) being -// non-empty. The CLI deliberately does not consult any other signal -// (DINGTALK_AGENT / DWS_CHANNEL / the wire claw-type header) for this -// decision so that server-side routing tags and the host-owned UI contract -// remain independent concerns. +// itself. The trigger is the effective agent-code env (DINGTALK_DWS_AGENTCODE +// or DWS_DINGTALK_AGENTCODE) being non-empty. The CLI deliberately does not +// consult any other signal (DINGTALK_AGENT / DWS_CHANNEL / the wire claw-type +// header) for this decision so that server-side routing tags and the host-owned +// UI contract remain independent concerns. func HostOwnsPATFlow() bool { - return strings.TrimSpace(os.Getenv(AgentCodeEnv)) != "" + return AgentCodeEnvPresent() } diff --git a/internal/pat/chmod.go b/internal/pat/chmod.go index c5cafdcb..2c14d9ab 100644 --- a/internal/pat/chmod.go +++ b/internal/pat/chmod.go @@ -25,6 +25,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" + authpkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/auth" apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition" ) @@ -58,23 +59,25 @@ func resolveSessionIDFromEnv() string { return "" } -// agentCodeEnv is the canonical (and only) environment variable name -// used as a per-shell fallback for the --agentCode flag on `dws pat *` -// commands. +// agentCodeEnv is the canonical environment variable name used as a +// per-shell fallback for the --agentCode flag on `dws pat *` commands. // // Why: agent hosts may set their business agent code once when spawning // a long-lived shell / sub-process. Exposing DINGTALK_DWS_AGENTCODE lets // the host export the code once and let the CLI resolve it on every pat // subcommand. The flag always wins when both are set so scripted one-offs -// remain deterministic. When neither flag nor env is set, the request is -// sent without agentCode and lippi-pat-core applies its default agentCode. +// remain deterministic. When neither flag nor env is set, `pat chmod` fails +// locally instead of letting the server reject the request later. Batch PAT +// tools receive the resolved agentCode in arguments, while the CLI also keeps +// exporting it through env for older gateway paths. // -// Namespace note: DWS_AGENTCODE / DINGTALK_AGENTCODE / REWIND_AGENTCODE -// are explicitly NOT consumed. The legacy DWS_AGENTCODE alias was -// hard-removed once the public integration surface landed on -// DINGTALK_DWS_AGENTCODE; hosts must migrate rather than rely on a -// silent fallback. -const agentCodeEnv = "DINGTALK_DWS_AGENTCODE" +// Namespace note: DWS_DINGTALK_AGENTCODE is kept as a compatibility alias for +// hosts that shipped the reversed prefix early. DWS_AGENTCODE / +// DINGTALK_AGENTCODE / REWIND_AGENTCODE are explicitly NOT consumed. +const ( + agentCodeEnv = authpkg.AgentCodeEnv + agentCodeEnvCompat = authpkg.AgentCodeEnvCompat +) // agentCodePattern is the validation regex for any --agentCode value // resolved from either the flag or the agent-code env var. It matches @@ -84,16 +87,12 @@ const agentCodeEnv = "DINGTALK_DWS_AGENTCODE" // argument. var agentCodePattern = regexp.MustCompile(`^[A-Za-z0-9_-]{1,64}$`) -// resolveAgentCodeFromEnv returns the fallback agent code from the -// canonical DINGTALK_DWS_AGENTCODE env var. The second return value -// reports the env name that was consumed (for error attribution); it -// is "" when the env is unset or blank. No legacy aliases are honored. +// resolveAgentCodeFromEnv returns the fallback agent code from the canonical +// DINGTALK_DWS_AGENTCODE env var, then the DWS_DINGTALK_AGENTCODE compatibility +// alias. The second return value reports the env name that was consumed (for +// error attribution); it is "" when both env vars are unset or blank. func resolveAgentCodeFromEnv() (string, string) { - primary := strings.TrimSpace(os.Getenv(agentCodeEnv)) - if primary != "" { - return primary, agentCodeEnv - } - return "", "" + return authpkg.AgentCodeFromEnv() } // validateAgentCode rejects agent codes that would be ambiguous or unsafe @@ -116,7 +115,8 @@ func validateAgentCode(code string) error { // // 1. explicit --agentCode flag value (highest priority; wins over env) // 2. DINGTALK_DWS_AGENTCODE env var (per-shell primary fallback) -// 3. empty ("") when required=false; typed error when required=true. +// 3. DWS_DINGTALK_AGENTCODE env var (compatibility fallback) +// 4. empty ("") when required=false; typed error when required=true. // // Any non-empty resolved value is validated via validateAgentCode, so // callers never have to re-validate. @@ -128,17 +128,30 @@ func resolveAgentCode(flagVal string, required bool) (string, error) { } if code == "" { if required { - return "", fmt.Errorf( - "flag --agentCode is required (or set env %s)\n hint: dws pat chmod ... --agentCode \n hint: export %s=", - agentCodeEnv, agentCodeEnv) + return "", apperrors.NewValidation( + fmt.Sprintf("flag --agentCode is required (or set env %s or %s)", agentCodeEnv, agentCodeEnvCompat), + apperrors.WithReason("missing_agent_code"), + apperrors.WithHint(fmt.Sprintf("dws pat chmod ... --agentCode \n or: export %s=", agentCodeEnv)), + apperrors.WithActions( + "dws pat chmod ... --agentCode ", + fmt.Sprintf("export %s=", agentCodeEnv), + fmt.Sprintf("export %s=", agentCodeEnvCompat), + ), + ) } return "", nil } if err := validateAgentCode(code); err != nil { if envSource != "" { - return "", fmt.Errorf("%s env: %w", envSource, err) + return "", apperrors.NewValidation( + fmt.Sprintf("%s env: %v", envSource, err), + apperrors.WithReason("invalid_agent_code"), + ) } - return "", err + return "", apperrors.NewValidation( + err.Error(), + apperrors.WithReason("invalid_agent_code"), + ) } return code, nil } @@ -159,8 +172,25 @@ const ( patBatchUnsupportedCode = "PAT_BATCH_AUTH_UNSUPPORTED" patBatchUnsupportedCodeLower = "pat_batch_auth_unsupported" + patForgedIdentityCode = "PAT_FORGED_IDENTITY_FIELD" + patForgedIdentityCodeLower = "pat_forged_identity_field" ) +var patBatchMetadataContractCodes = map[string]bool{ + "pat_batch_auth_metadata_required": true, + "pat_batch_scope_not_declared": true, + "pat_batch_product_not_declared": true, +} + +var patBatchIdentityArgumentKeys = map[string]bool{ + "agentCode": true, + "sessionId": true, + "orgId": true, + "uid": true, + "source": true, + "caller": true, +} + var validGrantTypes = map[string]bool{ "once": true, "session": true, @@ -204,7 +234,7 @@ grantType 规则: dws pat chmod --recommend --grant-type session --session-id session-xxx`, RunE: func(cmd *cobra.Command, args []string) error { flagVal, _ := cmd.Flags().GetString("agentCode") - agentCode, err := resolveAgentCode(flagVal, false) + agentCode, err := resolveAgentCode(flagVal, true) if err != nil { return err } @@ -239,8 +269,6 @@ grantType 规则: fmt.Printf("%-16s%s\n", "Tool:", patBatchGrantToolName) if agentCode != "" { fmt.Printf("%-16s%s\n", "AgentCode:", agentCode) - } else { - fmt.Printf("%-16s%s\n", "AgentCode:", "(server default)") } fmt.Printf("%-16s%v\n", "Scope:", scopes) fmt.Printf("%-16s%s\n", "GrantType:", grantType) @@ -315,7 +343,7 @@ grantType 规则: } chmodCmd.Flags().String("agentCode", "", - "Agent 唯一标识(可选;不填则由服务端写入默认 AgentCode;env DINGTALK_DWS_AGENTCODE 可注入,flag 优先)") + "Agent 唯一标识(必填;也可通过 env DINGTALK_DWS_AGENTCODE/DWS_DINGTALK_AGENTCODE 注入,flag 优先;会进入 batch 参数并同步注入兼容 env)") chmodCmd.Flags().String("grant-type", "session", "授权策略: once|session|permanent") chmodCmd.Flags().String("session-id", "", "会话标识(session 模式下必填)") chmodCmd.Flags().StringArrayVar(&productFlags, "product", nil, "产品编码,可重复;与 --products 等价") @@ -388,13 +416,11 @@ func callPATBatchGrantWithLegacyFallback( if c == nil { return nil, fmt.Errorf("internal error: tool runtime not initialized") } - result, err := withPATContextEnv(agentCode, sessionID, func() (*edition.ToolResult, error) { - return c.CallTool(ctx, "pat", patBatchGrantToolName, batchArgs) - }) - if err == nil && !isPATBatchUnsupportedResult(result) { + result, err := callPATBatchToolWithIdentityFallback(ctx, c, agentCode, sessionID, patBatchGrantToolName, batchArgs) + if err == nil && !isPATBatchFallbackResult(result) { return result, nil } - if err != nil && !isPATBatchUnsupportedError(err) && !isToolNotRegisteredError(err) { + if err != nil && !isPATBatchFallbackError(err) && !isToolNotRegisteredError(err) { return nil, err } return withPATContextEnv(agentCode, sessionID, func() (*edition.ToolResult, error) { @@ -414,8 +440,19 @@ func callPATBatchPlan(ctx context.Context, c edition.ToolCaller, agentCode, sess if c == nil { return nil, fmt.Errorf("internal error: tool runtime not initialized") } + return callPATBatchToolWithIdentityFallback(ctx, c, agentCode, sessionID, patBatchPlanToolName, args) +} + +func callPATBatchToolWithIdentityFallback(ctx context.Context, c edition.ToolCaller, agentCode, sessionID, toolName string, args map[string]any) (*edition.ToolResult, error) { + result, err := withPATContextEnv(agentCode, sessionID, func() (*edition.ToolResult, error) { + return c.CallTool(ctx, "pat", toolName, args) + }) + if !shouldRetryPATBatchWithoutIdentityArgs(result, err, args) { + return result, err + } + compatArgs := cloneWithoutPATIdentityArgs(args) return withPATContextEnv(agentCode, sessionID, func() (*edition.ToolResult, error) { - return c.CallTool(ctx, "pat", patBatchPlanToolName, args) + return c.CallTool(ctx, "pat", toolName, compatArgs) }) } @@ -485,6 +522,25 @@ func firstToolResultText(result *edition.ToolResult) string { } func isPATBatchUnsupportedResult(result *edition.ToolResult) bool { + return patBatchResultHasCode(result, func(code string) bool { + return strings.EqualFold(code, patBatchUnsupportedCode) + }) +} + +func isPATBatchFallbackResult(result *edition.ToolResult) bool { + return patBatchResultHasCode(result, func(code string) bool { + normalized := strings.ToLower(strings.TrimSpace(code)) + return strings.EqualFold(code, patBatchUnsupportedCode) || patBatchMetadataContractCodes[normalized] + }) +} + +func isPATForgedIdentityResult(result *edition.ToolResult) bool { + return patBatchResultHasCode(result, func(code string) bool { + return strings.EqualFold(code, patForgedIdentityCode) + }) +} + +func patBatchResultHasCode(result *edition.ToolResult, matches func(string) bool) bool { text := firstToolResultText(result) if text == "" { return false @@ -494,7 +550,7 @@ func isPATBatchUnsupportedResult(result *edition.ToolResult) bool { return false } for _, key := range []string{"code", "errorCode", "error_code"} { - if code, ok := body[key].(string); ok && strings.EqualFold(strings.TrimSpace(code), patBatchUnsupportedCode) { + if code, ok := body[key].(string); ok && matches(strings.TrimSpace(code)) { return true } } @@ -505,6 +561,53 @@ func isPATBatchUnsupportedError(err error) bool { return err != nil && strings.Contains(normalizedPATErrorText(err), patBatchUnsupportedCodeLower) } +func isPATBatchFallbackError(err error) bool { + if isPATBatchUnsupportedError(err) { + return true + } + text := normalizedPATErrorText(err) + for code := range patBatchMetadataContractCodes { + if strings.Contains(text, code) { + return true + } + } + return false +} + +func isPATForgedIdentityError(err error) bool { + return err != nil && strings.Contains(normalizedPATErrorText(err), patForgedIdentityCodeLower) +} + +func shouldRetryPATBatchWithoutIdentityArgs(result *edition.ToolResult, err error, args map[string]any) bool { + if !hasPATIdentityArgs(args) { + return false + } + if err != nil { + return isPATForgedIdentityError(err) + } + return isPATForgedIdentityResult(result) +} + +func hasPATIdentityArgs(args map[string]any) bool { + for key := range args { + if patBatchIdentityArgumentKeys[key] { + return true + } + } + return false +} + +func cloneWithoutPATIdentityArgs(args map[string]any) map[string]any { + out := make(map[string]any, len(args)) + for key, value := range args { + if patBatchIdentityArgumentKeys[key] { + continue + } + out[key] = value + } + return out +} + // callPATToolWithLegacyFallback invokes the canonical PAT grant tool first, // then silently retries the legacy Chinese alias when the server has not // registered the canonical tool yet. The retry intentionally emits no stderr diff --git a/internal/pat/chmod_test.go b/internal/pat/chmod_test.go index 57443096..d7441cd4 100644 --- a/internal/pat/chmod_test.go +++ b/internal/pat/chmod_test.go @@ -68,8 +68,11 @@ func (f *fakeToolCaller) Format() string { return "json" } func (f *fakeToolCaller) DryRun() bool { return f.dryRun } type recordedToolCall struct { - tool string - args map[string]any + tool string + args map[string]any + agentEnv string + sessionEnv string + dingSessionEnv string } type fallbackToolCaller struct { @@ -198,6 +201,7 @@ func (f *fallbackPATContractErrorToolCaller) DryRun() bool { return false } type sequenceToolCaller struct { calls []recordedToolCall responses []string + errs []error dryRun bool } @@ -207,6 +211,12 @@ func (s *sequenceToolCaller) CallTool(_ context.Context, _ string, toolName stri copied[k] = v } s.calls = append(s.calls, recordedToolCall{tool: toolName, args: copied}) + s.calls[len(s.calls)-1].agentEnv = os.Getenv(agentCodeEnv) + s.calls[len(s.calls)-1].sessionEnv = os.Getenv(sessionIDEnvDWS) + s.calls[len(s.calls)-1].dingSessionEnv = os.Getenv(sessionIDEnvDingtalk) + if len(s.errs) >= len(s.calls) && s.errs[len(s.calls)-1] != nil { + return nil, s.errs[len(s.calls)-1] + } response := `{"success":true,"data":{}}` if len(s.responses) >= len(s.calls) { response = s.responses[len(s.calls)-1] @@ -305,8 +315,11 @@ func TestChmod_productsFlagPlansThenGrantsSelectedScopes(t *testing.T) { if got := fake.calls[0].args["recommend"]; got != false { t.Fatalf("recommend = %#v, want false", got) } + if got := fake.calls[0].agentEnv; got != "qoderwork" { + t.Fatalf("plan agent env = %q, want qoderwork", got) + } if got := fake.calls[0].args["agentCode"]; got != "qoderwork" { - t.Fatalf("plan agentCode = %#v, want qoderwork", got) + t.Fatalf("batch plan agentCode = %#v, want qoderwork", got) } if fake.calls[1].tool != patBatchGrantToolName { t.Fatalf("second tool = %q, want %q", fake.calls[1].tool, patBatchGrantToolName) @@ -314,12 +327,15 @@ func TestChmod_productsFlagPlansThenGrantsSelectedScopes(t *testing.T) { if got := fake.calls[1].args["scopes"]; !stringSliceArgEqual(got, []string{"calendar.event:read", "aitable.record:read"}) { t.Fatalf("grant scopes = %#v, want selected scopes", got) } + if got := fake.calls[1].agentEnv; got != "qoderwork" { + t.Fatalf("grant agent env = %q, want qoderwork", got) + } if got := fake.calls[1].args["agentCode"]; got != "qoderwork" { - t.Fatalf("grant agentCode = %#v, want qoderwork", got) + t.Fatalf("batch grant agentCode = %#v, want qoderwork", got) } } -func TestChmod_productsSessionModePassesSessionIDToPlanAndGrant(t *testing.T) { +func TestChmod_productsSessionModePassesIdentityArgsAndCompatEnv(t *testing.T) { t.Setenv(agentCodeEnv, "qoderwork") fake := &sequenceToolCaller{responses: []string{ `{"success":true,"data":{"selectedScopes":["calendar.event:read"]}}`, @@ -339,20 +355,32 @@ func TestChmod_productsSessionModePassesSessionIDToPlanAndGrant(t *testing.T) { if got := fake.calls[0].args["grantType"]; got != "session" { t.Fatalf("plan grantType = %#v, want session", got) } + if got := fake.calls[0].args["agentCode"]; got != "qoderwork" { + t.Fatalf("plan agentCode = %#v, want qoderwork", got) + } if got := fake.calls[0].args["sessionId"]; got != "session-123" { t.Fatalf("plan sessionId = %#v, want session-123", got) } - if got := fake.calls[0].args["agentCode"]; got != "qoderwork" { - t.Fatalf("plan agentCode = %#v, want qoderwork", got) + if got := fake.calls[0].agentEnv; got != "qoderwork" { + t.Fatalf("plan agent env = %q, want qoderwork", got) + } + if fake.calls[0].dingSessionEnv != "session-123" { + t.Fatalf("plan %s env = %q, want session-123", sessionIDEnvDingtalk, fake.calls[0].dingSessionEnv) } if got := fake.calls[1].args["grantType"]; got != "session" { t.Fatalf("grant grantType = %#v, want session", got) } + if got := fake.calls[1].args["agentCode"]; got != "qoderwork" { + t.Fatalf("grant agentCode = %#v, want qoderwork", got) + } if got := fake.calls[1].args["sessionId"]; got != "session-123" { t.Fatalf("grant sessionId = %#v, want session-123", got) } - if got := fake.calls[1].args["agentCode"]; got != "qoderwork" { - t.Fatalf("grant agentCode = %#v, want qoderwork", got) + if got := fake.calls[1].agentEnv; got != "qoderwork" { + t.Fatalf("grant agent env = %q, want qoderwork", got) + } + if fake.calls[1].dingSessionEnv != "session-123" { + t.Fatalf("grant %s env = %q, want session-123", sessionIDEnvDingtalk, fake.calls[1].dingSessionEnv) } } @@ -378,11 +406,100 @@ func TestChmod_productsDryRunUsesSessionIDFromEnv(t *testing.T) { if fake.calls[0].tool != patBatchPlanToolName { t.Fatalf("plan tool = %q, want %q", fake.calls[0].tool, patBatchPlanToolName) } + if got := fake.calls[0].args["agentCode"]; got != "qoderwork" { + t.Fatalf("plan agentCode = %#v, want qoderwork", got) + } if got := fake.calls[0].args["sessionId"]; got != "env-session-123" { t.Fatalf("plan sessionId = %#v, want env-session-123", got) } + if got := fake.calls[0].agentEnv; got != "qoderwork" { + t.Fatalf("plan agent env = %q, want qoderwork", got) + } + if fake.calls[0].dingSessionEnv != "env-session-123" { + t.Fatalf("plan %s env = %q, want env-session-123", sessionIDEnvDingtalk, fake.calls[0].dingSessionEnv) + } +} + +func TestChmod_batchPlanRetriesWithoutIdentityArgsForCompat(t *testing.T) { + t.Setenv(agentCodeEnv, "") + t.Setenv(agentCodeEnvCompat, "qoderwork") + fake := &sequenceToolCaller{ + errs: []error{ + apperrors.NewAPI("PAT batch identity field 'agentCode' must be derived by gateway.", + apperrors.WithReason("business_error"), + apperrors.WithServerDiag(apperrors.ServerDiagnostics{ + ServerErrorCode: patForgedIdentityCode, + }), + ), + nil, + }, + responses: []string{ + "", + `{"success":true,"data":{"allGranted":true,"selectedScopes":[]}}`, + }, + } + cmd := newChmodCommand(fake) + _ = cmd.Flags().Set("grant-type", "once") + _ = cmd.Flags().Set("products", "calendar") + + if err := cmd.RunE(cmd, nil); err != nil { + t.Fatalf("chmod RunE error = %v", err) + } + if len(fake.calls) != 2 { + t.Fatalf("CallTool count = %d, want 2", len(fake.calls)) + } + if fake.calls[0].tool != patBatchPlanToolName || fake.calls[1].tool != patBatchPlanToolName { + t.Fatalf("tools = %q, %q; want repeated %q", fake.calls[0].tool, fake.calls[1].tool, patBatchPlanToolName) + } if got := fake.calls[0].args["agentCode"]; got != "qoderwork" { - t.Fatalf("plan agentCode = %#v, want qoderwork", got) + t.Fatalf("first plan agentCode = %#v, want qoderwork", got) + } + if _, ok := fake.calls[1].args["agentCode"]; ok { + t.Fatalf("compat retry must omit agentCode arg: %#v", fake.calls[1].args) + } + if got := fake.calls[1].agentEnv; got != "qoderwork" { + t.Fatalf("compat retry %s = %q, want qoderwork", agentCodeEnv, got) + } +} + +func TestChmod_batchGrantRetriesWithoutIdentityArgsForCompat(t *testing.T) { + t.Setenv(agentCodeEnv, "") + t.Setenv(agentCodeEnvCompat, "qoderwork") + fake := &sequenceToolCaller{ + errs: []error{ + apperrors.NewAPI("PAT batch identity field 'agentCode' must be derived by gateway.", + apperrors.WithReason("business_error"), + apperrors.WithServerDiag(apperrors.ServerDiagnostics{ + ServerErrorCode: patForgedIdentityCode, + }), + ), + nil, + }, + responses: []string{ + "", + `{"success":true,"data":{"grantedScopes":["calendar.event:read"]}}`, + }, + } + cmd := newChmodCommand(fake) + _ = cmd.Flags().Set("grant-type", "once") + + if err := cmd.RunE(cmd, []string{"calendar.event:read"}); err != nil { + t.Fatalf("chmod RunE error = %v", err) + } + if len(fake.calls) != 2 { + t.Fatalf("CallTool count = %d, want 2", len(fake.calls)) + } + if fake.calls[0].tool != patBatchGrantToolName || fake.calls[1].tool != patBatchGrantToolName { + t.Fatalf("tools = %q, %q; want repeated %q", fake.calls[0].tool, fake.calls[1].tool, patBatchGrantToolName) + } + if got := fake.calls[0].args["agentCode"]; got != "qoderwork" { + t.Fatalf("first grant agentCode = %#v, want qoderwork", got) + } + if _, ok := fake.calls[1].args["agentCode"]; ok { + t.Fatalf("compat retry must omit agentCode arg: %#v", fake.calls[1].args) + } + if got := fake.calls[1].agentEnv; got != "qoderwork" { + t.Fatalf("compat retry %s = %q, want qoderwork", agentCodeEnv, got) } } @@ -407,6 +524,7 @@ func TestResolveSessionIDFromEnvMatchesHeaderPriority(t *testing.T) { } func TestChmod_sessionModeUsesDingtalkSessionEnv(t *testing.T) { + t.Setenv(agentCodeEnv, "qoderwork") t.Setenv(sessionIDEnvDingtalk, "ding-session-123") fake := &fakeToolCaller{resultOK: true} @@ -416,6 +534,9 @@ func TestChmod_sessionModeUsesDingtalkSessionEnv(t *testing.T) { t.Fatalf("chmod RunE error = %v", err) } + if got := fake.gotArgs["agentCode"]; got != "qoderwork" { + t.Fatalf("agentCode arg = %#v, want qoderwork", got) + } if got := fake.gotArgs["sessionId"]; got != "ding-session-123" { t.Fatalf("sessionId arg = %#v, want ding-session-123", got) } @@ -428,6 +549,7 @@ func TestChmod_sessionModeUsesDingtalkSessionEnv(t *testing.T) { } func TestChmod_explicitSessionIDOverridesStaleDingtalkSessionEnv(t *testing.T) { + t.Setenv(agentCodeEnv, "qoderwork") t.Setenv(sessionIDEnvDingtalk, "stale-session") fake := &fakeToolCaller{resultOK: true} @@ -438,6 +560,9 @@ func TestChmod_explicitSessionIDOverridesStaleDingtalkSessionEnv(t *testing.T) { t.Fatalf("chmod RunE error = %v", err) } + if got := fake.gotArgs["agentCode"]; got != "qoderwork" { + t.Fatalf("agentCode arg = %#v, want qoderwork", got) + } if got := fake.gotArgs["sessionId"]; got != "flag-session" { t.Fatalf("sessionId arg = %#v, want flag-session", got) } @@ -523,7 +648,7 @@ func TestChmod_explicitScopesDryRunShowsBatchGrantTool(t *testing.T) { // TestChmod_agentCode_env_fallback verifies that when --agentCode is // omitted but DINGTALK_DWS_AGENTCODE is exported, the resolver picks -// the env value up and forwards it verbatim in the MCP argv. +// the env value up for both batch arguments and gateway-compatible env. func TestChmod_agentCode_env_fallback(t *testing.T) { t.Setenv(agentCodeEnv, "qoderwork") @@ -543,7 +668,7 @@ func TestChmod_agentCode_env_fallback(t *testing.T) { t.Fatalf("agent env = %q, want %q", got, "qoderwork") } if got := fake.gotArgs["agentCode"]; got != "qoderwork" { - t.Fatalf("batch argv agentCode = %#v, want qoderwork", got) + t.Fatalf("batch agentCode = %#v, want qoderwork", got) } if got := fake.gotArgs["scopes"]; !stringSliceArgEqual(got, []string{"aitable.record:read"}) { t.Fatalf("scopes in argv = %#v, want %#v", got, []string{"aitable.record:read"}) @@ -553,8 +678,9 @@ func TestChmod_agentCode_env_fallback(t *testing.T) { } } -func TestChmod_withoutAgentCodeUsesServerDefault(t *testing.T) { +func TestChmod_agentCode_compatEnvFallback(t *testing.T) { t.Setenv(agentCodeEnv, "") + t.Setenv(agentCodeEnvCompat, "compatwork") fake := &fakeToolCaller{resultOK: true} cmd := buildChmod(t, fake) @@ -566,14 +692,31 @@ func TestChmod_withoutAgentCodeUsesServerDefault(t *testing.T) { if fake.gotTool != patBatchGrantToolName { t.Fatalf("gotTool = %q, want %q", fake.gotTool, patBatchGrantToolName) } - if got := fake.gotAgentEnv; got != "" { - t.Fatalf("agent env = %q, want empty so server default agentCode is used", got) + if got := fake.gotArgs["agentCode"]; got != "compatwork" { + t.Fatalf("batch agentCode = %#v, want compatwork", got) } - if _, ok := fake.gotArgs["agentCode"]; ok { - t.Fatalf("batch argv must omit agentCode when caller leaves it unset: %#v", fake.gotArgs) + if got := fake.gotAgentEnv; got != "compatwork" { + t.Fatalf("%s during CallTool = %q, want compatwork", agentCodeEnv, got) } - if got := fake.gotArgs["scopes"]; !stringSliceArgEqual(got, []string{"aitable.record:read"}) { - t.Fatalf("scopes in argv = %#v, want %#v", got, []string{"aitable.record:read"}) +} + +func TestChmod_withoutAgentCodeFailsBeforeMCP(t *testing.T) { + t.Setenv(agentCodeEnv, "") + t.Setenv(agentCodeEnvCompat, "") + + fake := &fakeToolCaller{resultOK: true} + cmd := buildChmod(t, fake) + _ = cmd.Flags().Set("grant-type", "once") + + err := cmd.RunE(cmd, []string{"aitable.record:read"}) + if err == nil { + t.Fatal("chmod RunE error = nil, want missing agentCode error") + } + if !strings.Contains(err.Error(), "flag --agentCode is required") { + t.Fatalf("error = %q, want missing agentCode hint", err.Error()) + } + if fake.callN != 0 { + t.Fatalf("CallTool was invoked %d times; missing agentCode must fail before MCP", fake.callN) } } @@ -786,6 +929,40 @@ func TestCallPATToolWithLegacyFallback_patContractErrorDoesNotRetryLegacyAlias(t } } +func TestChmod_batchMetadataScopeErrorFallsBackToPATGrant(t *testing.T) { + fake := &sequenceToolCaller{ + responses: []string{ + `{"success":false,"errorCode":"PAT_BATCH_SCOPE_NOT_DECLARED","data":{"scopes":["mail:send"]}}`, + `{"success":true,"data":{"authRequestId":"req-ok"}}`, + }, + } + cmd := newChmodCommand(fake) + _ = cmd.Flags().Set("agentCode", "qoderwork") + _ = cmd.Flags().Set("grant-type", "once") + + if err := cmd.RunE(cmd, []string{"mail:send"}); err != nil { + t.Fatalf("chmod RunE error = %v", err) + } + if len(fake.calls) != 2 { + t.Fatalf("CallTool call count = %d, want 2", len(fake.calls)) + } + if fake.calls[0].tool != patBatchGrantToolName { + t.Fatalf("first tool = %q, want %q", fake.calls[0].tool, patBatchGrantToolName) + } + if fake.calls[1].tool != patGrantToolName { + t.Fatalf("fallback tool = %q, want %q", fake.calls[1].tool, patGrantToolName) + } + if got := fake.calls[0].args["agentCode"]; got != "qoderwork" { + t.Fatalf("batch agentCode = %#v, want qoderwork", got) + } + if got := fake.calls[1].args["agentCode"]; got != "qoderwork" { + t.Fatalf("fallback agentCode = %#v, want qoderwork", got) + } + if got := fake.calls[1].args["scopes"]; !stringSliceArgEqual(got, []string{"mail:send"}) { + t.Fatalf("fallback scopes = %#v, want mail:send", got) + } +} + func TestIsToolNotRegisteredError_ChineseGatewayMessage(t *testing.T) { err := errors.New("pat chmod failed: business error: PARAM_ERROR - 未找到指定工具") if !isToolNotRegisteredError(err) { @@ -813,6 +990,13 @@ func TestIsPATBatchUnsupportedResultCaseInsensitive(t *testing.T) { } } +func TestIsPATBatchFallbackResultIncludesMetadataContractErrors(t *testing.T) { + result := &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: `{"success":false,"errorCode":"PAT_BATCH_SCOPE_NOT_DECLARED"}`}}} + if !isPATBatchFallbackResult(result) { + t.Fatal("isPATBatchFallbackResult() = false, want true") + } +} + func TestIsPATBatchUnsupportedErrorUsesNormalizedDiagnostics(t *testing.T) { err := apperrors.NewAPI("business error: success=false", apperrors.WithReason("business_error"), @@ -825,6 +1009,18 @@ func TestIsPATBatchUnsupportedErrorUsesNormalizedDiagnostics(t *testing.T) { } } +func TestIsPATBatchFallbackErrorIncludesMetadataContractDiagnostics(t *testing.T) { + err := apperrors.NewAPI("business error: success=false", + apperrors.WithReason("business_error"), + apperrors.WithServerDiag(apperrors.ServerDiagnostics{ + ServerErrorCode: "PAT_BATCH_PRODUCT_NOT_DECLARED", + }), + ) + if !isPATBatchFallbackError(err) { + t.Fatal("isPATBatchFallbackError() = false, want true") + } +} + func TestHandleToolResult_emptyResultReturnsError(t *testing.T) { err := handleToolResult(nil, nil, &edition.ToolResult{}) if err == nil { @@ -978,34 +1174,35 @@ func TestChmod_agentCode_flag_wins_over_env(t *testing.T) { t.Fatalf("agent env = %q, want %q (flag must win over env)", got, "flagval") } if got := fake.gotArgs["agentCode"]; got != "flagval" { - t.Fatalf("batch argv agentCode = %#v, want flagval", got) + t.Fatalf("batch agentCode = %#v, want flagval", got) } } // TestChmod_agentCode_legacy_env_not_recognized is a reverse-guard: after // the SSOT hard-removal of the DWS_AGENTCODE alias, exporting only the -// legacy env MUST NOT be consumed. The command is still allowed to run, -// omits agentCode, and lets lippi-pat-core write its default agentCode. +// legacy env MUST NOT satisfy the required agentCode contract. func TestChmod_agentCode_legacy_env_not_recognized(t *testing.T) { t.Setenv(agentCodeEnv, "") + t.Setenv(agentCodeEnvCompat, "") t.Setenv("DWS_AGENTCODE", "legacyval") fake := &fakeToolCaller{resultOK: true} cmd := buildChmod(t, fake) _ = cmd.Flags().Set("grant-type", "once") - if err := cmd.RunE(cmd, []string{"aitable.record:read"}); err != nil { - t.Fatalf("chmod RunE error = %v", err) + err := cmd.RunE(cmd, []string{"aitable.record:read"}) + if err == nil { + t.Fatal("chmod RunE error = nil, want missing agentCode error") } - if fake.callN != 1 { - t.Fatalf("CallTool was invoked %d times, want 1", fake.callN) + if !strings.Contains(err.Error(), "flag --agentCode is required") { + t.Fatalf("error = %q, want missing agentCode hint", err.Error()) + } + if fake.callN != 0 { + t.Fatalf("CallTool was invoked %d times; legacy env must not satisfy agentCode", fake.callN) } if got := fake.gotAgentEnv; got != "" { t.Fatalf("agent env = %q, want empty; legacy DWS_AGENTCODE must not be consumed", got) } - if _, ok := fake.gotArgs["agentCode"]; ok { - t.Fatalf("batch argv must omit agentCode when only legacy env is set: %#v", fake.gotArgs) - } } // --------------------------------------------------------------------------- @@ -1046,8 +1243,21 @@ func TestResolveAgentCodeFromEnv(t *testing.T) { code, src, "qoderwork", agentCodeEnv) } - // Empty primary → ("", ""). + t.Setenv(agentCodeEnvCompat, "compatwork") + if code, src := resolveAgentCodeFromEnv(); code != "qoderwork" || src != agentCodeEnv { + t.Errorf("resolveAgentCodeFromEnv() = (%q, %q), want primary (%q, %q)", + code, src, "qoderwork", agentCodeEnv) + } + t.Setenv(agentCodeEnv, "") + if code, src := resolveAgentCodeFromEnv(); code != "compatwork" || src != agentCodeEnvCompat { + t.Errorf("resolveAgentCodeFromEnv() = (%q, %q), want compat (%q, %q)", + code, src, "compatwork", agentCodeEnvCompat) + } + + // Empty primary + empty compat → ("", ""). + t.Setenv(agentCodeEnv, "") + t.Setenv(agentCodeEnvCompat, "") if code, src := resolveAgentCodeFromEnv(); code != "" || src != "" { t.Errorf("resolveAgentCodeFromEnv() = (%q, %q), want empty", code, src) } @@ -1055,6 +1265,7 @@ func TestResolveAgentCodeFromEnv(t *testing.T) { // Reverse-guard: legacy DWS_AGENTCODE MUST NOT be picked up when the // canonical env is unset — it was hard-removed as a legacy alias. t.Setenv(agentCodeEnv, "") + t.Setenv(agentCodeEnvCompat, "") t.Setenv("DWS_AGENTCODE", "legacy") if code, src := resolveAgentCodeFromEnv(); code != "" || src != "" { t.Errorf("resolveAgentCodeFromEnv() = (%q, %q), want empty — legacy DWS_AGENTCODE must be ignored", diff --git a/internal/pat/pat.go b/internal/pat/pat.go index 3d17b11c..ab55a162 100644 --- a/internal/pat/pat.go +++ b/internal/pat/pat.go @@ -37,7 +37,10 @@ func RegisterCommands(root *cobra.Command, c edition.ToolCaller) { pat chmod 默认输出轻量授权摘要;显式 --format json / --verbose 时, 才返回服务端完整 JSON(含逐 scope 明细),便于机器校验。 浏览器是否打开由本地 PAT 策略单独决定,与 json / non-json 独立。 - 生效时会优先按 DINGTALK_DWS_AGENTCODE 读取 agent 策略,再回退到默认策略。 + pat chmod 必须传 --agentCode,或设置 DINGTALK_DWS_AGENTCODE / + DWS_DINGTALK_AGENTCODE;CLI 会把 agentCode 放入 batch 请求参数, + 并同步注入 gateway 兼容身份头。 + 浏览器策略生效时会优先按 DINGTALK_DWS_AGENTCODE 读取 agent 策略,再回退到默认策略。 写入 agent 策略需显式传 --agentCode;不传则写入全局默认策略。 Host-owned PAT 开关: diff --git a/test/unit/pat_host_owned_signal_test.go b/test/unit/pat_host_owned_signal_test.go index 77de05d3..9123ad0b 100644 --- a/test/unit/pat_host_owned_signal_test.go +++ b/test/unit/pat_host_owned_signal_test.go @@ -21,9 +21,10 @@ import ( // TestHostOwnsPATFlow_OnlySignal is the wire-level guard for the // "custom authorization card" contract: the CLI switches to host-owned -// PAT mode iff the host injects DINGTALK_DWS_AGENTCODE. DINGTALK_AGENT / -// claw-type is purely a server-side routing tag and must NOT influence -// the decision, in either direction. +// PAT mode iff the host injects DINGTALK_DWS_AGENTCODE or its compatibility +// alias DWS_DINGTALK_AGENTCODE. DINGTALK_AGENT / claw-type is purely a +// server-side routing tag and must NOT influence the decision, in either +// direction. // // Regression guard: several earlier drafts conflated the two signals, // causing third-party Agent hosts that only set DINGTALK_DWS_AGENTCODE @@ -33,6 +34,7 @@ func TestHostOwnsPATFlow_OnlySignal(t *testing.T) { cases := []struct { name string agentCode string + compat string agentEnv string want bool }{ @@ -48,6 +50,20 @@ func TestHostOwnsPATFlow_OnlySignal(t *testing.T) { agentEnv: "", want: true, }, + { + name: "compat agent code only → host-owned", + agentCode: "", + compat: "agt-compat", + agentEnv: "", + want: true, + }, + { + name: "primary wins when both env names are set", + agentCode: "agt-primary", + compat: "agt-compat", + agentEnv: "", + want: true, + }, { name: "agent code + DINGTALK_AGENT=default → host-owned", agentCode: "agt-cursor", @@ -84,6 +100,7 @@ func TestHostOwnsPATFlow_OnlySignal(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Setenv(authpkg.AgentCodeEnv, tc.agentCode) + t.Setenv(authpkg.AgentCodeEnvCompat, tc.compat) // DINGTALK_AGENT is set purely to demonstrate that it does NOT // influence the host-owned decision. The literal env name is // used here because the auth package no longer exports a @@ -97,6 +114,19 @@ func TestHostOwnsPATFlow_OnlySignal(t *testing.T) { got, tc.want, tc.agentCode, tc.agentEnv, ) } + if tc.want { + gotCode, gotSource := authpkg.AgentCodeFromEnv() + wantCode := tc.agentCode + wantSource := authpkg.AgentCodeEnv + if wantCode == "" { + wantCode = tc.compat + wantSource = authpkg.AgentCodeEnvCompat + } + if gotCode != wantCode || gotSource != wantSource { + t.Fatalf("AgentCodeFromEnv() = (%q, %q), want (%q, %q)", + gotCode, gotSource, wantCode, wantSource) + } + } }) } } From 6067906c5524cdd2acdabb19239484ecc22f85cc Mon Sep 17 00:00:00 2001 From: "shangguanxuan.sgx" Date: Thu, 18 Jun 2026 03:44:22 +0800 Subject: [PATCH 34/47] fix(devdoc): fallback empty rag article search --- internal/app/legacy.go | 11 +- internal/app/root.go | 2 +- internal/cli/loader.go | 2 +- internal/discovery/service.go | 2 +- internal/helpers/devdoc.go | 77 +++++++++++--- internal/helpers/devdoc_test.go | 171 ++++++++++++++++++++++++++++++- internal/market/registry.go | 74 ++++++++++++- internal/market/registry_test.go | 68 ++++++++++++ pkg/config/constants.go | 6 +- test/cli_compat/devdoc_test.go | 14 ++- 10 files changed, 393 insertions(+), 34 deletions(-) diff --git a/internal/app/legacy.go b/internal/app/legacy.go index acd6e753..6202de17 100644 --- a/internal/app/legacy.go +++ b/internal/app/legacy.go @@ -241,7 +241,7 @@ func loadDynamicCommands(ctx context.Context, runner executor.Runner) []*cobra.C // no-op: fall through to FallbackServers check below } } else { - servers = market.NormalizeServers(resp, "market") + servers = market.NormalizeServersForBaseURL(resp, "market", registryDiscoveryBaseURL()) if discoveryTraceEnabled() { slog.Info("loadDynamicCommands: sync discovery fetch ok", "partition", partition, @@ -499,6 +499,13 @@ func fetchRegistryServers(ctx context.Context, httpClient *http.Client) (market. return client.FetchServers(ctx, config.DefaultFetchServersLimit) } +func registryDiscoveryBaseURL() string { + if editionURL := strings.TrimSpace(edition.Get().DiscoveryURL); editionURL != "" { + return editionURL + } + return DiscoveryBaseURL() +} + // asyncRevalidateRegistry refreshes the registry cache in the background. // Uses a short timeout derived from the parent context and silently ignores // errors — the next CLI invocation will pick up the refreshed cache or retry. @@ -511,7 +518,7 @@ func asyncRevalidateRegistry(parent context.Context, store *cache.Store, partiti slog.Debug("asyncRevalidateRegistry: fetch failed", "error", err) return } - servers := market.NormalizeServers(resp, "market") + servers := market.NormalizeServersForBaseURL(resp, "market", registryDiscoveryBaseURL()) if saveErr := store.SaveRegistry(partition, cache.RegistrySnapshot{Servers: servers}); saveErr != nil { slog.Debug("asyncRevalidateRegistry: save failed", "error", saveErr) } diff --git a/internal/app/root.go b/internal/app/root.go index 9cfe7ba3..18024a50 100644 --- a/internal/app/root.go +++ b/internal/app/root.go @@ -474,7 +474,7 @@ func newCacheCommand() *cobra.Command { if err != nil { return apperrors.NewDiscovery(fmt.Sprintf("cache refresh: fetch server list failed: %v", err)) } - servers := market.NormalizeServers(resp, "live_market") + servers := market.NormalizeServersForBaseURL(resp, "live_market", registryDiscoveryBaseURL()) _ = store.SaveRegistry(service.CachePartition(), cache.RegistrySnapshot{Servers: servers}) selected := selectServersForProduct(servers, product) diff --git a/internal/cli/loader.go b/internal/cli/loader.go index b67edce9..19dd85bd 100644 --- a/internal/cli/loader.go +++ b/internal/cli/loader.go @@ -279,7 +279,7 @@ func (l EnvironmentLoader) Load(ctx context.Context) (ir.Catalog, error) { // accepted subset. See plan fix-wukong-discovery-missing-servers Phase 4.3. logDiscoveryWarnings(response.Metadata.Warnings) - servers := market.NormalizeServers(response, "live_market") + servers := market.NormalizeServersForBaseURL(response, "live_market", baseURL) _ = store.SaveRegistry(partition, cache.RegistrySnapshot{Servers: servers}) changedKeys := cache.ChangedServerKeysByUpdatedAt(cached.Registry.Servers, servers) diff --git a/internal/discovery/service.go b/internal/discovery/service.go index 39f722c2..ab5c7887 100644 --- a/internal/discovery/service.go +++ b/internal/discovery/service.go @@ -94,7 +94,7 @@ func (s *Service) DiscoverServers(ctx context.Context) ([]market.ServerDescripto response, err := s.MarketClient.FetchServers(ctx, 200) if err == nil { - servers := market.NormalizeServers(response, "live_market") + servers := market.NormalizeServersForBaseURL(response, "live_market", s.MarketClient.BaseURL) _ = s.Cache.SaveRegistry(partition, cache.RegistrySnapshot{Servers: servers}) return servers, nil } diff --git a/internal/helpers/devdoc.go b/internal/helpers/devdoc.go index 81ff0de6..b90bfe65 100644 --- a/internal/helpers/devdoc.go +++ b/internal/helpers/devdoc.go @@ -82,7 +82,7 @@ func (devdocHandler) Command(runner executor.Runner) *cobra.Command { func newDevdocArticleSearchCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ - Use: "search [keyword]", + Use: "search [query]", Short: "搜索开放平台文档", // Bind the devdoc MCP tool so `dws schema dev.doc.search` can fetch this // command's real parameters live from the devdoc server (mcp-source), @@ -91,11 +91,11 @@ func newDevdocArticleSearchCommand(runner executor.Runner) *cobra.Command { Args: cobra.MaximumNArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - keyword := devdocFlagOrFallback(cmd, "keyword", "query") - if keyword == "" && len(args) > 0 { - keyword = strings.TrimSpace(args[0]) + query := devdocFlagOrFallback(cmd, "keyword", "query") + if query == "" && len(args) > 0 { + query = strings.TrimSpace(args[0]) } - if keyword == "" { + if query == "" { return apperrors.NewValidation("--keyword is required") } page, _ := cmd.Flags().GetInt("page") @@ -105,17 +105,16 @@ func newDevdocArticleSearchCommand(runner executor.Runner) *cobra.Command { size := devdocPageSize(cmd) cursor := devdocFlagOrFallback(cmd, "cursor") return runDevdocToolWithFallback(cmd, runner, devdocArticleSearchTool, - devdocSearchParams(keyword, page, size, cursor), + devdocSearchParams(query, page, size, cursor), devdocArticleSearchLegacyTool, - devdocLegacySearchParams(keyword, page, size, cursor)) + devdocLegacySearchParams(query, page, size, cursor)) }, } preferLegacyLeaf(cmd) - // Flag names == MCP param names (keyword/size), so `dws schema dev.doc.search` - // shows exactly what the user types. --query/--page-size kept as hidden - // back-compat aliases for existing scripts and `dws devdoc`. + // The published devdoc RAG action accepts keyword. Keep --query as a hidden + // compatibility alias for older scripts. cmd.Flags().String("keyword", "", "搜索关键词 (必填)") - addDevdocHiddenStringFlag(cmd, "query", "--keyword 的别名") + addDevdocHiddenStringFlag(cmd, "query", "--keyword 的兼容别名") cmd.Flags().Int("page", 1, "分页页码 (从 1 开始,默认 1)") cmd.Flags().String("cursor", "", "分页游标,翻页传上次返回的 nextCursor;传入后不再使用 --page") cmd.Flags().Int("size", 20, "单页条数 (默认 20)") @@ -215,10 +214,10 @@ func newDevdocErrorDiagnoseCommand(runner executor.Runner) *cobra.Command { return cmd } -func devdocSearchParams(keyword string, page int, size int, cursor string) map[string]any { +func devdocSearchParams(query string, page int, size int, cursor string) map[string]any { params := map[string]any{ - "keyword": keyword, - "pageSize": size, + "keyword": query, + "size": size, } if cursor != "" { params["cursor"] = cursor @@ -275,8 +274,12 @@ func runDevdocToolWithFallbacks(cmd *cobra.Command, runner executor.Runner, tool return writeCommandPayload(cmd, invocation) } result, err := runner.Run(cmd.Context(), invocation) + currentTool := tool for _, fallback := range fallbacks { - if err == nil || !isDevdocToolNotFoundError(err) { + if err == nil && !shouldFallbackDevdocResult(currentTool, result) { + break + } + if err != nil && !isDevdocToolNotFoundError(err) { break } if fallback.tool == "" { @@ -293,6 +296,7 @@ func runDevdocToolWithFallbacks(cmd *cobra.Command, runner executor.Runner, tool fallbackParams, ) result, err = runner.Run(cmd.Context(), fallbackInvocation) + currentTool = fallback.tool } if err != nil { return err @@ -300,6 +304,48 @@ func runDevdocToolWithFallbacks(cmd *cobra.Command, runner executor.Runner, tool return writeCommandPayload(cmd, result) } +func shouldFallbackDevdocResult(tool string, result executor.Result) bool { + if tool != devdocArticleSearchTool || !result.Invocation.Implemented { + return false + } + content, ok := result.Response["content"].(map[string]any) + if !ok { + return false + } + return isEmptyDevdocArticleRAGContent(content) +} + +func isEmptyDevdocArticleRAGContent(content map[string]any) bool { + if hasDevdocArticleRAGPayload(content) { + return false + } + for _, key := range []string{"materials", "references", "ragContext", "result", "success"} { + if _, ok := content[key]; ok { + return true + } + } + return false +} + +func hasDevdocArticleRAGPayload(content map[string]any) bool { + if hasNonEmptyList(content["materials"]) || hasNonEmptyList(content["references"]) || hasNonEmptyString(content["ragContext"]) { + return true + } + result, _ := content["result"].(map[string]any) + return hasNonEmptyList(result["items"]) || hasNonEmptyList(result["materials"]) || + hasNonEmptyList(result["references"]) || hasNonEmptyString(result["ragContext"]) +} + +func hasNonEmptyList(value any) bool { + items, ok := value.([]any) + return ok && len(items) > 0 +} + +func hasNonEmptyString(value any) bool { + text, ok := value.(string) + return ok && strings.TrimSpace(text) != "" +} + func isDevdocToolNotFoundError(err error) bool { if err == nil { return false @@ -308,7 +354,6 @@ func isDevdocToolNotFoundError(err error) bool { 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, "未找到指定工具") } diff --git a/internal/helpers/devdoc_test.go b/internal/helpers/devdoc_test.go index 2470819b..8fa9b478 100644 --- a/internal/helpers/devdoc_test.go +++ b/internal/helpers/devdoc_test.go @@ -24,9 +24,10 @@ import ( ) type devdocCommandRunner struct { - last executor.Invocation - calls []executor.Invocation - errors map[string]error + last executor.Invocation + calls []executor.Invocation + errors map[string]error + responses map[string]map[string]any } func (r *devdocCommandRunner) Run(_ context.Context, invocation executor.Invocation) (executor.Result, error) { @@ -37,6 +38,15 @@ func (r *devdocCommandRunner) Run(_ context.Context, invocation executor.Invocat return executor.Result{}, err } } + if r.responses != nil { + if content, ok := r.responses[invocation.Tool]; ok { + invocation.Implemented = true + return executor.Result{ + Invocation: invocation, + Response: map[string]any{"content": content}, + }, nil + } + } return executor.Result{Invocation: invocation}, nil } @@ -59,11 +69,20 @@ func TestDevdocArticleSearchAcceptsWukongKeywordAlias(t *testing.T) { if got := runner.last.Params["keyword"]; got != "openConversationId" { t.Fatalf("keyword = %#v, want openConversationId", got) } + if _, ok := runner.last.Params["query"]; ok { + t.Fatalf("query must not be sent to article RAG search: %#v", runner.last.Params) + } if got := runner.last.Params["cursor"]; got != "tok-d" { t.Fatalf("cursor = %#v, want tok-d", got) } - if got := runner.last.Params["pageSize"]; got != 5 { - t.Fatalf("pageSize = %#v, want 5", got) + if got := runner.last.Params["size"]; got != 5 { + t.Fatalf("size = %#v, want 5", got) + } + if _, ok := runner.last.Params["pageSize"]; ok { + t.Fatalf("pageSize must not be sent to RAG search: %#v", runner.last.Params) + } + if _, ok := runner.last.Params["CliRagSearchReqVO"]; ok { + t.Fatalf("CliRagSearchReqVO wrapper must not be sent to RAG search: %#v", runner.last.Params) } } @@ -168,6 +187,12 @@ func TestDevdocErrorDiagnoseFallsBackToArticleSearchTools(t *testing.T) { if got := runner.calls[1].Params["cursor"]; got != "2" { t.Fatalf("RAG fallback cursor = %#v, want 2", got) } + if got := runner.calls[1].Params["keyword"]; got != "40014 获取用户信息 req-1" { + t.Fatalf("RAG fallback keyword = %#v, want merged diagnostic query", got) + } + if _, ok := runner.calls[1].Params["query"]; ok { + t.Fatalf("RAG fallback query must not be sent: %#v", runner.calls[1].Params) + } 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) } @@ -222,6 +247,139 @@ func TestDevdocArticleSearchFallsBackOnStructuredToolMetadataError(t *testing.T) } } +func TestDevdocArticleSearchDoesNotFallbackOnGenericToolMetadataError(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"}) + + if err := cmd.Execute(); err == nil { + t.Fatalf("Execute() error = nil, want metadata PARAM_ERROR") + } + if len(runner.calls) != 1 { + t.Fatalf("calls = %d, want 1", len(runner.calls)) + } + if got := runner.calls[0].Tool; got != devdocArticleSearchTool { + t.Fatalf("first tool = %q, want %s", got, devdocArticleSearchTool) + } +} + +func TestDevdocArticleSearchFallsBackOnEmptyRAGContent(t *testing.T) { + t.Parallel() + + runner := &devdocCommandRunner{ + responses: map[string]map[string]any{ + devdocArticleSearchTool: { + "materials": []any{}, + "references": []any{}, + "ragContext": nil, + }, + devdocArticleSearchLegacyTool: { + "success": true, + "result": map[string]any{ + "items": []any{map[string]any{"title": "OAuth2.0鉴权 - 开放平台"}}, + }, + }, + }, + } + cmd := devdocHandler{}.Command(runner) + var out, errOut bytes.Buffer + cmd.SetOut(&out) + cmd.SetErr(&errOut) + cmd.SetArgs([]string{"article", "search", "--keyword", "OAuth2", "--size", "2"}) + + 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) + } +} + +func TestDevdocArticleSearchFallsBackOnPaginationOnlyRAGContent(t *testing.T) { + t.Parallel() + + runner := &devdocCommandRunner{ + responses: map[string]map[string]any{ + devdocArticleSearchTool: { + "success": true, + "result": map[string]any{"nextCursor": "2"}, + }, + devdocArticleSearchLegacyTool: { + "success": true, + "result": map[string]any{ + "items": []any{map[string]any{"title": "获取用户token - 开放平台"}}, + }, + }, + }, + } + cmd := devdocHandler{}.Command(runner) + var out, errOut bytes.Buffer + cmd.SetOut(&out) + cmd.SetErr(&errOut) + cmd.SetArgs([]string{"article", "search", "--query", "OAuth2", "--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[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 TestDevdocArticleSearchKeepsNonEmptyRAGContent(t *testing.T) { + t.Parallel() + + runner := &devdocCommandRunner{ + responses: map[string]map[string]any{ + devdocArticleSearchTool: { + "materials": []any{map[string]any{"title": "OAuth2.0鉴权 - 开放平台"}}, + }, + }, + } + cmd := devdocHandler{}.Command(runner) + var out, errOut bytes.Buffer + cmd.SetOut(&out) + cmd.SetErr(&errOut) + cmd.SetArgs([]string{"article", "search", "--keyword", "OAuth2"}) + + if err := cmd.Execute(); err != nil { + t.Fatalf("Execute() error = %v\nstderr:\n%s", err, errOut.String()) + } + if len(runner.calls) != 1 { + t.Fatalf("calls = %d, want 1", len(runner.calls)) + } + if got := runner.calls[0].Tool; got != devdocArticleSearchTool { + t.Fatalf("first tool = %q, want %s", got, devdocArticleSearchTool) + } +} + func TestDevdocArticleSearchAcceptsPositionalKeyword(t *testing.T) { t.Parallel() @@ -238,4 +396,7 @@ func TestDevdocArticleSearchAcceptsPositionalKeyword(t *testing.T) { if got := runner.last.Params["keyword"]; got != "MCP" { t.Fatalf("keyword = %#v, want MCP", got) } + if _, ok := runner.last.Params["query"]; ok { + t.Fatalf("query must not be sent to article RAG search: %#v", runner.last.Params) + } } diff --git a/internal/market/registry.go b/internal/market/registry.go index 78bd0950..50f0c308 100644 --- a/internal/market/registry.go +++ b/internal/market/registry.go @@ -668,7 +668,12 @@ func isPrivateHost(host string) bool { } func NormalizeServers(response ListResponse, source string) []ServerDescriptor { + return NormalizeServersForBaseURL(response, source, "") +} + +func NormalizeServersForBaseURL(response ListResponse, source string, discoveryBaseURL string) []ServerDescriptor { bestByEndpoint := make(map[string]ServerDescriptor) + forcePreGateway := isPreMCPBaseURL(discoveryBaseURL) for _, envelope := range response.Servers { meta := envelope.Meta.Registry @@ -681,7 +686,7 @@ func NormalizeServers(response ListResponse, source string) []ServerDescriptor { continue } - endpoint := NormalizeEndpoint(remoteURL) + endpoint := normalizeEndpoint(remoteURL, forcePreGateway) descriptor := ServerDescriptor{ Key: ServerKey(endpoint), DisplayName: strings.TrimSpace(envelope.Server.Name), @@ -822,10 +827,15 @@ func hasDeprecatedMarker(displayName string) bool { } func NormalizeEndpoint(raw string) string { + return normalizeEndpoint(raw, false) +} + +func normalizeEndpoint(raw string, forcePreGateway bool) string { parsed, err := url.Parse(raw) if err != nil { return strings.TrimSpace(raw) } + normalizeGatewayHost(parsed, forcePreGateway) parsed.Fragment = "" parsed.Path = strings.TrimRight(parsed.Path, "/") values := parsed.Query() @@ -846,6 +856,68 @@ func NormalizeEndpoint(raw string) string { return parsed.String() } +func normalizeGatewayHost(parsed *url.URL, forcePreGateway bool) { + if parsed == nil { + return + } + host := parsed.Hostname() + nextHost, ok := normalizedGatewayHost(host, forcePreGateway) + if !ok { + return + } + if port := parsed.Port(); port != "" { + parsed.Host = net.JoinHostPort(nextHost, port) + } else { + parsed.Host = nextHost + } +} + +func normalizedGatewayHost(host string, forcePreGateway bool) (string, bool) { + host = strings.TrimSpace(strings.ToLower(host)) + if host == "" { + return "", false + } + if forcePreGateway { + switch { + case host == "mcp.dingtalk.com" || host == "mcp-gw.dingtalk.com" || + host == "pre-mcp.dingtalk.com" || host == "pre-mcp-gw.dingtalk.com": + return "pre-mcp-gw.dingtalk.com", true + case strings.HasPrefix(host, "pre-mcp-gw."): + return host, true + case strings.HasPrefix(host, "pre-mcp."): + return strings.Replace(host, "pre-mcp.", "pre-mcp-gw.", 1), true + case strings.HasPrefix(host, "mcp-gw."): + return strings.Replace(host, "mcp-gw.", "pre-mcp-gw.", 1), true + case strings.HasPrefix(host, "mcp."): + return strings.Replace(host, "mcp.", "pre-mcp-gw.", 1), true + default: + return "", false + } + } + switch { + case host == "mcp.dingtalk.com": + return "mcp-gw.dingtalk.com", true + case strings.HasPrefix(host, "pre-mcp."): + return strings.Replace(host, "pre-mcp.", "pre-mcp-gw.", 1), true + case strings.HasPrefix(host, "mcp."): + return strings.Replace(host, "mcp.", "mcp-gw.", 1), true + default: + return "", false + } +} + +func isPreMCPBaseURL(raw string) bool { + parsed, err := url.Parse(strings.TrimSpace(raw)) + if err != nil { + return false + } + host := strings.ToLower(parsed.Hostname()) + return host == "pre-mcp.dingtalk.com" || + host == "pre-mcp-gw.dingtalk.com" || + strings.HasPrefix(host, "pre-mcp.") || + strings.HasPrefix(host, "pre-mcp-gw.") +} + func ServerKey(endpoint string) string { sum := sha256.Sum256([]byte(endpoint)) return hex.EncodeToString(sum[:8]) diff --git a/internal/market/registry_test.go b/internal/market/registry_test.go index 5b030104..577f4c47 100644 --- a/internal/market/registry_test.go +++ b/internal/market/registry_test.go @@ -101,6 +101,74 @@ func TestNormalizeServersDeduplicatesSameNameAcrossEndpoints(t *testing.T) { } } +func TestNormalizeServersConvertsPreMCPRemoteToGatewayEndpoint(t *testing.T) { + t.Parallel() + + response := ListResponse{ + Servers: []ServerEnvelope{ + { + Server: RegistryServer{ + Name: "钉钉开放平台文档搜索", + Remotes: []RegistryRemote{ + {Type: "streamable-http", URL: "https://pre-mcp.dingtalk.com/server/devdoc?key=b&key=a#fragment"}, + }, + }, + Meta: EnvelopeMeta{ + Registry: RegistryMetadata{Status: "active", UpdatedAt: "2026-06-16T00:00:00Z"}, + CLI: CLIOverlay{ID: "devdoc", Command: "devdoc"}, + }, + }, + }, + } + + servers := NormalizeServers(response, "live_market") + if len(servers) != 1 { + t.Fatalf("NormalizeServers() len = %d, want 1", len(servers)) + } + want := "https://pre-mcp-gw.dingtalk.com/server/devdoc?key=a&key=b" + if servers[0].Endpoint != want { + t.Fatalf("NormalizeServers() endpoint = %q, want %q", servers[0].Endpoint, want) + } +} + +func TestNormalizeServersForBaseURLForcesPreGatewayEndpoint(t *testing.T) { + t.Parallel() + + response := ListResponse{ + Servers: []ServerEnvelope{ + { + Server: RegistryServer{ + Name: "钉钉文档", + Remotes: []RegistryRemote{ + {Type: "streamable-http", URL: "https://mcp-gw.dingtalk.com/server/doc?key=b&key=a#fragment"}, + }, + }, + Meta: EnvelopeMeta{ + Registry: RegistryMetadata{Status: "active", UpdatedAt: "2026-06-16T00:00:00Z"}, + CLI: CLIOverlay{ID: "doc", Command: "doc"}, + }, + }, + }, + } + + prodServers := NormalizeServers(response, "live_market") + if len(prodServers) != 1 { + t.Fatalf("NormalizeServers() len = %d, want 1", len(prodServers)) + } + if want := "https://mcp-gw.dingtalk.com/server/doc?key=a&key=b"; prodServers[0].Endpoint != want { + t.Fatalf("NormalizeServers() endpoint = %q, want %q", prodServers[0].Endpoint, want) + } + + preServers := NormalizeServersForBaseURL(response, "live_market", "https://pre-mcp.dingtalk.com") + if len(preServers) != 1 { + t.Fatalf("NormalizeServersForBaseURL() len = %d, want 1", len(preServers)) + } + want := "https://pre-mcp-gw.dingtalk.com/server/doc?key=a&key=b" + if preServers[0].Endpoint != want { + t.Fatalf("NormalizeServersForBaseURL() endpoint = %q, want %q", preServers[0].Endpoint, want) + } +} + // TestNormalizeServersPreservesDistinctCLIIDsOnSharedEndpoint guards the // bot-root / bot-message / bot-group split (issue: chat bot subtree vanished // from the CLI after NormalizeServers collapsed three envelopes that share a diff --git a/pkg/config/constants.go b/pkg/config/constants.go index c986e45a..0848ab37 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -161,9 +161,9 @@ const ( // Shared across auth, errors, and device-flow packages. const ( - // DefaultMCPBaseURL is the DingTalk MCP base URL. + // DefaultMCPBaseURL is the DingTalk MCP base URL for the pre-release build. // Override at runtime via ~/.dws/mcp_url file. - DefaultMCPBaseURL = "https://mcp.dingtalk.com" + DefaultMCPBaseURL = "https://pre-mcp.dingtalk.com" // DefaultTerminalBaseURL is the DingTalk developer platform base URL. // Override at runtime via ~/.dws/terminal_url file. @@ -189,7 +189,7 @@ func DefaultConfigDir() string { // GetMCPBaseURL returns the MCP base URL with priority: // 1. ~/.dws/mcp_url file content (for custom environment) -// 2. Default value (https://mcp.dingtalk.com) +// 2. Default value (https://pre-mcp.dingtalk.com) func GetMCPBaseURL() string { mcpURLPath := filepath.Join(DefaultConfigDir(), "mcp_url") if data, err := os.ReadFile(mcpURLPath); err == nil { diff --git a/test/cli_compat/devdoc_test.go b/test/cli_compat/devdoc_test.go index 09c689ae..2b189ec9 100644 --- a/test/cli_compat/devdoc_test.go +++ b/test/cli_compat/devdoc_test.go @@ -8,7 +8,7 @@ func TestDevdocArticleSearch_should_call_correct_tool(t *testing.T) { cap := setupTestDeps(t, "devdoc") root := buildRoot() err := execCmd(t, root, []string{"devdoc", "article", "search"}, map[string]string{ - "keyword": "MCP", "page": "1", "size": "10", + "query": "MCP", "page": "1", "size": "10", }) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -20,16 +20,22 @@ func TestDevdocArticleSearch_should_pass_keyword(t *testing.T) { cap := setupTestDeps(t, "devdoc") root := buildRoot() _ = execCmd(t, root, []string{"devdoc", "article", "search"}, map[string]string{ - "keyword": "openConversationId", "page": "1", "size": "10", + "query": "openConversationId", "page": "1", "size": "10", }) assertToolArg(t, cap, "keyword", "openConversationId") + assertArgNotPresent(t, cap, "query") + assertToolArg(t, cap, "size", float64(10)) + assertArgNotPresent(t, cap, "pageSize") + if _, ok := cap.last().Args["CliRagSearchReqVO"]; ok { + t.Fatalf("CliRagSearchReqVO wrapper must not be sent for RAG search: %v", cap.last().Args) + } } 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", + "query": "Webhook", "cursor": "3", "size": "10", }) assertToolArg(t, cap, "cursor", "3") assertArgNotPresent(t, cap, "page") @@ -39,7 +45,7 @@ func TestDevdocArticleSearch_should_not_call_when_dry_run(t *testing.T) { cap := setupTestDepsWithDryRun(t, "devdoc") root := buildRoot() _ = execCmd(t, root, []string{"devdoc", "article", "search"}, map[string]string{ - "keyword": "MCP", "page": "1", "size": "10", + "query": "MCP", "page": "1", "size": "10", }) assertCallCount(t, cap, 0) } From f2b2de89f4cc7db99ef7616a82fbeb366c4b19dd Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Thu, 18 Jun 2026 18:34:12 +0800 Subject: [PATCH 35/47] fix cr problem --- docs/devapp-yulan-command-routing.md | 16 ++--- internal/app/runner.go | 36 ++++++++++- internal/app/runner_test.go | 14 +++++ internal/helpers/devapp.go | 20 ++++-- internal/helpers/devapp_pretty.go | 16 ++--- internal/helpers/devapp_pretty_test.go | 9 ++- internal/helpers/devapp_test.go | 26 ++++++++ internal/transport/diagnostics.go | 61 ++++++++++++++++--- internal/transport/diagnostics_test.go | 20 ++++++ skills/multi/dingtalk-dev/SKILL.md | 2 +- skills/multi/dingtalk-dev/references/event.md | 7 ++- skills/multi/dingtalk-dev/references/robot.md | 17 +++--- .../multi/dingtalk-dev/references/version.md | 14 +++-- .../multi/dingtalk-devapp/references/event.md | 27 ++++---- 14 files changed, 219 insertions(+), 66 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 90965651..110ede95 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -138,7 +138,7 @@ Do not send confirmation fields such as `confirmCreate`, `confirmUpdate`, `confi | Permission list | `devapp permission list/search/detail` | `list_dev_app_permissions` | `OpenInnerAppPermissionFacade.list` | Implemented | Hardcoded helper | | Permission apply | `devapp permission add` | `apply_dev_app_permissions` | `OpenInnerAppPermissionFacade.apply` | Implemented | Hardcoded helper | | Permission remove | `devapp permission remove` | `remove_dev_app_permissions` | `OpenInnerAppPermissionFacade.remove` | Implemented | Hardcoded helper | -| Events | `devapp event list/subscribe/unsubscribe` | `list_open_dev_app_events`, `subscribe/unsubscribe_open_dev_app_event` | Implemented | Hardcoded helper | +| Events | `devapp event list/subscribe/unsubscribe` | `list_dev_app_events`, `subscribe_dev_app_events`, `unsubscribe_dev_app_events` | Implemented | Hardcoded helper | | Robot create | `devapp robot create/submit/result` | `create_dingtalk_robot`/`submit_robot_create_task`/`query_robot_create_result` | Implemented | Hardcoded helper | | Robot config | `devapp robot get/config/enable/disable` | `get_extension_robot_config`, `set_extension_robot_config`, `enable_dev_app_robot`, `disable_dev_app_robot` | Implemented | Hardcoded helper | | Version | `devapp version create/list/get/check-approval/publish/status` | `create_dev_app_version`, `list_dev_app_versions`, `get_dev_app_version_detail`, `publish_dev_app_version`, `get_dev_app_version_status` | Implemented | Hardcoded helper | @@ -953,9 +953,9 @@ real model is "list subscribable events + subscribe/unsubscribe events by | CLI | MCP tool | Notes | | --- | --- | --- | -| `devapp event list` | `list_dev_app_events` | Args `unifiedAppId`; returns `pushType` + `events[]` (`eventCode`/`eventName`/`subscribed`). Read. | -| `devapp event subscribe` | `subscribe_dev_app_events` | Args `unifiedAppId` + `eventCodes`. Write (`--dry-run`/`--yes`). | -| `devapp event unsubscribe` | `unsubscribe_dev_app_events` | Args `unifiedAppId` + `eventCodes`. Write. | +| `dev app event list` | `list_dev_app_events` | Args `unifiedAppId/cursor/pageSize`; returns `pushType=STREAM` + `events[]` (`eventCode`/`eventName`/`subscribed`) + `hasMore/nextCursor/pageSize`. Read. | +| `dev app event subscribe` | `subscribe_dev_app_events` | Args `unifiedAppId` + required array `eventCodes`. Returns `success/operation/eventCodes/needsPublish/versionRequiredAction`; failures include `errorCode/errorMsg/reason/retryable/action` such as `STREAM_NOT_CONNECTED`. Write (`--dry-run`/`--yes`). | +| `dev app event unsubscribe` | `unsubscribe_dev_app_events` | Args `unifiedAppId` + required array `eventCodes`. Returns `success/operation/eventCodes/needsPublish/versionRequiredAction`; failures include `errorCode/errorMsg/reason/retryable/action`. Write. | Note: for gray unified apps, subscribe/unsubscribe are staged into version metadata and only take effect after `version publish`. The event callback URL @@ -969,8 +969,8 @@ Implemented version tools (verified against the `op-app` MCP `tools/list`): | `devapp version create` | `create_dev_app_version` | Save a new version from current config. Omit `version` by default so the server auto-increments from the latest released version; only pass `version` when the user explicitly requests a higher version. | | `devapp version list` | `list_dev_app_versions` | Cursor-paged list (`cursor`, `pageSize`; response `items/nextCursor/hasMore`). | | `devapp version get` | `get_dev_app_version_detail` | One version detail by `versionId`. | -| `devapp version check-approval` | `publish_dev_app_version` (`precheckOnly=true`) | Precheck approval requirement / approvers; does not publish. | -| `devapp version publish` | `publish_dev_app_version` (`precheckOnly=false`) | Publish; `confirmedSensitive` for high-sensitivity scopes, optional `approverUserId`. | +| `devapp version check-approval` | `publish_dev_app_version` (`precheckOnly=true`) | Precheck approval requirement / approvers via `requiresApproval/publishable/approvalMode/approvalCandidates`; does not publish. | +| `devapp version publish` | `publish_dev_app_version` (`precheckOnly=false`) | Publish or submit approval; inspect `published/approvalSubmitted/processId/versionStatus`. | | `devapp version status` | `get_dev_app_version_status` | Poll publish and approval state. | Note: the MCP `precheckOnly` field is the server-side approval precheck and maps to @@ -1086,7 +1086,7 @@ Yulan invocation examples: | "把应用图标换成 ICON_RESOURCE" | `dws devapp update --unified-app-id ... --icon ICON_RESOURCE --dry-run --format json` | Icon change is app base info update and must be guarded. | | "已确认删除这个开放平台应用" | `dws devapp delete --unified-app-id ... --yes --format json` | Destructive write only after unique app resolution and confirmation. | | "手机号权限要申请,虽然需要审核" | `dws devapp permission add --permissions Contact.User.mobile --dry-run/--yes` | `requiredApproval=true` is still appliable; approval moves to version publish. | -| "订阅通讯录用户增加事件" | `dws devapp event list ...` then `dws devapp event subscribe --event-codes user_add_org --dry-run --format json` | Pick eventCode from list, then subscribe; gray apps need a version publish to take effect. | +| "订阅通讯录用户增加事件" | `dws dev app event list ...` then `dws dev app event subscribe --event-codes user_add_org --dry-run --format json` | Pick eventCode from list, then subscribe; gray apps need a version publish to take effect. | | "保存版本并看审核状态" | `version create`, `check-approval`, `publish`, `status` | Version flow owns approver selection and publish audit state. | | "创建 MCP 服务并配置 HSF tool" | OpenDev MCP platform workflow, not `dws devapp create` | MCP connector/tool is a platform artifact, not an enterprise internal app. | @@ -1209,7 +1209,7 @@ Agent should normalize behavior without hiding raw backend fields. | `PERMISSION_ALREADY_AUTHED` | Add receives an already authorized scope | Report skipped scope and continue only for remaining unauth scopes. | | `PERMISSION_NOT_AUTHED` | Remove receives an unauthorized scope | Report that there is nothing to remove. | | `PERMISSION_NOT_EDITABLE` | `canEdit=false` or backend no-edit error | Show the no-edit reason; do not retry as a write. | -| `VERSION_APPROVAL_REQUIRED` | `check-approval` says approver is required | Ask for approver or use returned candidate when the user gave an explicit approver. | +| `requiresApproval=true` | `check-approval` says approval is required | Use `approvalMode/approvalCandidates`; ask for approver only in `SELECT_APPROVER` mode. | Raw `errorCode/errorMsg` must remain in JSON output or the Agent response so the backend owner can diagnose actual HSF failures. diff --git a/internal/app/runner.go b/internal/app/runner.go index e9b68daa..e13894d9 100644 --- a/internal/app/runner.go +++ b/internal/app/runner.go @@ -726,13 +726,23 @@ func resolveIdentityHeaders() map[string]string { // errors (success=false + errorCode/errorMsg) that are not flagged at the MCP // protocol level. Returns the error message, or "" if the response is OK. func detectBusinessError(content map[string]any) string { + return detectBusinessErrorAtDepth(content, 0) +} + +func detectBusinessErrorAtDepth(content map[string]any, depth int) string { + if content == nil || depth > 8 { + return "" + } success, ok := content["success"] if !ok { - return "" + return detectNestedBusinessError(content, depth) } b, ok := success.(bool) if !ok || b { - return "" + return detectNestedBusinessError(content, depth) + } + if nested := detectNestedBusinessError(content, depth); nested != "" { + return nested } if msg, ok := content["errorMsg"].(string); ok && strings.TrimSpace(msg) != "" { return strings.TrimSpace(msg) @@ -743,6 +753,28 @@ func detectBusinessError(content map[string]any) string { return "business error: success=false" } +func detectNestedBusinessError(content map[string]any, depth int) string { + for _, key := range []string{"content", "result", "data"} { + switch child := content[key].(type) { + case map[string]any: + if msg := detectBusinessErrorAtDepth(child, depth+1); msg != "" { + return msg + } + case []any: + for _, item := range child { + childMap, ok := item.(map[string]any) + if !ok { + continue + } + if msg := detectBusinessErrorAtDepth(childMap, depth+1); msg != "" { + return msg + } + } + } + } + return "" +} + // extractMCPErrorMessage builds an error message from a ToolCallResult with // isError=true. It extracts text from content blocks when available. func extractMCPErrorMessage(result transport.ToolCallResult) string { diff --git a/internal/app/runner_test.go b/internal/app/runner_test.go index 0803ff79..3aeafb4b 100644 --- a/internal/app/runner_test.go +++ b/internal/app/runner_test.go @@ -896,6 +896,20 @@ func jsonRPCToolName(req map[string]any) string { return name } +func TestDetectBusinessErrorNestedServiceResult(t *testing.T) { + content := map[string]any{ + "success": false, + "result": map[string]any{ + "success": false, + "errorCode": "ROBOT_NOT_FOUND", + "errorMsg": "robot info is not exist", + }, + } + if got := detectBusinessError(content); got != "robot info is not exist" { + t.Fatalf("detectBusinessError() = %q, want nested errorMsg", got) + } +} + func writeJSONRPCToolResult(t *testing.T, w http.ResponseWriter, req map[string]any, content map[string]any, isError bool) { t.Helper() _ = json.NewEncoder(w).Encode(map[string]any{ diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 4778283f..3617f5f8 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -285,9 +285,11 @@ func newDevAppEventSubscribeCommand(runner executor.Runner) *cobra.Command { } params := map[string]any{"unifiedAppId": appID} // 服务端 subscribe/unsubscribe 工具收 eventCodes 数组(事件码列表)。 - if types := parseDevAppListFlag(cmd, "event-codes"); len(types) > 0 { - params["eventCodes"] = types + eventCodes, err := requiredDevAppEventCodes(cmd) + if err != nil { + return err } + params["eventCodes"] = eventCodes return runDevAppTool(runner, cmd, devAppEventSubscribeTool, params) }, } @@ -314,9 +316,11 @@ func newDevAppEventUnsubscribeCommand(runner executor.Runner) *cobra.Command { return err } params := map[string]any{"unifiedAppId": appID} - if types := parseDevAppListFlag(cmd, "event-codes"); len(types) > 0 { - params["eventCodes"] = types + eventCodes, err := requiredDevAppEventCodes(cmd) + if err != nil { + return err } + params["eventCodes"] = eventCodes return runDevAppTool(runner, cmd, devAppEventUnsubscribeTool, params) }, } @@ -1489,6 +1493,14 @@ func parseDevAppListFlag(cmd *cobra.Command, name string) []string { return splitDevAppList(raw) } +func requiredDevAppEventCodes(cmd *cobra.Command) ([]string, error) { + eventCodes := parseDevAppListFlag(cmd, "event-codes") + if len(eventCodes) == 0 { + return nil, apperrors.NewValidation("--event-codes 为必填") + } + return eventCodes, nil +} + func splitDevAppList(raw string) []string { raw = strings.TrimSpace(raw) if raw == "" { diff --git a/internal/helpers/devapp_pretty.go b/internal/helpers/devapp_pretty.go index 71f05928..37854818 100644 --- a/internal/helpers/devapp_pretty.go +++ b/internal/helpers/devapp_pretty.go @@ -22,9 +22,7 @@ import ( // devapp_pretty annotates --format pretty output with human-readable status // labels. Upstream payloads use a numeric enum for app status and the bare -// INIT/AUDIT/... enum for version status, and the same key `status` means -// different things on different tools — fine for agents reading JSON against -// the skill's enum tables, hostile to humans. JSON output is NEVER touched +// INIT/AUDIT/... enum for versionStatus. JSON output is NEVER touched // (transparency contract); only the pretty view gets `*Text` companions. var devAppStatusLabels = map[int]string{ @@ -50,8 +48,8 @@ var devAppToolStatusKind = map[string]string{ "create_dev_app": "version", // create/update 返回 versionStatus "update_dev_app": "version", "create_dev_app_version": "version", - "list_dev_app_version": "version", - "get_dev_app_version": "version", + "list_dev_app_versions": "version", + "get_dev_app_version_detail": "version", "get_dev_app_version_status": "version", "publish_dev_app_version": "version", } @@ -96,11 +94,9 @@ func devAppAnnotateMap(m map[string]any, kind string) { } return } - for _, key := range []string{"status", "versionStatus"} { - if s, ok := m[key].(string); ok { - if label, ok := devAppVersionStatusLabels[s]; ok { - m[key+"Text"] = label - } + if s, ok := m["versionStatus"].(string); ok { + if label, ok := devAppVersionStatusLabels[s]; ok { + m["versionStatusText"] = label } } } diff --git a/internal/helpers/devapp_pretty_test.go b/internal/helpers/devapp_pretty_test.go index c236cc81..85be0606 100644 --- a/internal/helpers/devapp_pretty_test.go +++ b/internal/helpers/devapp_pretty_test.go @@ -35,12 +35,15 @@ func TestDevAppPrettyAnnotateAppStatus(t *testing.T) { } func TestDevAppPrettyAnnotateVersionStatusKindSeparation(t *testing.T) { - // 版本工具的字符串枚举要注解 + // 版本工具只注解 versionStatus;旧 status 字段不再参与版本契约。 version := map[string]any{"status": "INIT", "versionStatus": "RELEASE"} - devAppPrettyAnnotate("get_dev_app_version", version) - if version["statusText"] != "未发布(有待发布变更)" || version["versionStatusText"] != "已发布生效" { + devAppPrettyAnnotate("get_dev_app_version_detail", version) + if version["versionStatusText"] != "已发布生效" { t.Fatalf("version annotations wrong: %#v", version) } + if _, exists := version["statusText"]; exists { + t.Fatalf("version status should not be annotated: %#v", version) + } // 应用工具的数字枚举不能套用版本语义;未登记的工具不动 unknown := map[string]any{"status": float64(1)} diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 877c4480..0a8d988e 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -568,6 +568,32 @@ func TestDevAppEventSubscribeUsesEventCodes(t *testing.T) { } } +func TestDevAppEventSubscribeRequiresEventCodes(t *testing.T) { + for _, tc := range []struct { + name string + args []string + }{ + {"subscribe", []string{"dev", "app", "event", "subscribe", "--unified-app-id", "u-1", "--yes"}}, + {"unsubscribe", []string{"dev", "app", "event", "unsubscribe", "--unified-app-id", "u-1", "--yes"}}, + } { + t.Run(tc.name, func(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs(tc.args) + err := root.Execute() + if err == nil || !strings.Contains(err.Error(), "--event-codes 为必填") { + t.Fatalf("Execute() error = %v, want --event-codes 为必填", err) + } + if runner.last.Tool != "" { + t.Fatalf("runner should not be called, got tool %q", runner.last.Tool) + } + }) + } +} + func TestDevAppWebappCommandsBuildParams(t *testing.T) { cases := []struct { name string diff --git a/internal/transport/diagnostics.go b/internal/transport/diagnostics.go index cd62a465..a50f5a07 100644 --- a/internal/transport/diagnostics.go +++ b/internal/transport/diagnostics.go @@ -16,6 +16,7 @@ package transport import ( "encoding/json" "net/http" + "strings" apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors" ) @@ -38,16 +39,11 @@ func ExtractServerDiagnostics(data json.RawMessage) apperrors.ServerDiagnostics if len(data) == 0 { return apperrors.ServerDiagnostics{} } - var fields serverDiagFields - if json.Unmarshal(data, &fields) != nil { + var content map[string]any + if json.Unmarshal(data, &content) != nil { return apperrors.ServerDiagnostics{} } - return apperrors.ServerDiagnostics{ - TraceID: coalesceStr(fields.TraceID, fields.TraceIDCamel), - ServerErrorCode: coalesceStr(fields.Code, fields.ErrorCode), - TechnicalDetail: fields.TechnicalDetail, - ServerRetryable: fields.Retryable, - } + return ExtractServerDiagnosticsFromMap(content) } // ExtractServerDiagnosticsFromMap parses server diagnostic fields from a @@ -58,7 +54,7 @@ func ExtractServerDiagnosticsFromMap(content map[string]any) apperrors.ServerDia } diag := apperrors.ServerDiagnostics{ TraceID: stringFromMap(content, "trace_id", "traceId"), - ServerErrorCode: stringFromMap(content, "code", "errorCode"), + ServerErrorCode: serverErrorCodeFromMap(content, 0), TechnicalDetail: stringFromMap(content, "technical_detail"), } if v, ok := content["retryable"].(bool); ok { @@ -102,3 +98,50 @@ func stringFromMap(m map[string]any, keys ...string) string { } return "" } + +func serverErrorCodeFromMap(content map[string]any, depth int) string { + if content == nil || depth > 8 { + return "" + } + direct := stringFromMap(content, "errorCode", "code", "server_error_code") + if isWrapperServerCode(direct) { + if nested := nestedServerErrorCode(content, depth); nested != "" { + return nested + } + } + if direct != "" { + return direct + } + return nestedServerErrorCode(content, depth) +} + +func nestedServerErrorCode(content map[string]any, depth int) string { + for _, key := range []string{"content", "result", "data"} { + switch child := content[key].(type) { + case map[string]any: + if code := serverErrorCodeFromMap(child, depth+1); code != "" { + return code + } + case []any: + for _, item := range child { + childMap, ok := item.(map[string]any) + if !ok { + continue + } + if code := serverErrorCodeFromMap(childMap, depth+1); code != "" { + return code + } + } + } + } + return "" +} + +func isWrapperServerCode(code string) bool { + switch strings.ToUpper(strings.TrimSpace(code)) { + case "ERROR", "BUSINESS_ERROR", "-1": + return true + default: + return false + } +} diff --git a/internal/transport/diagnostics_test.go b/internal/transport/diagnostics_test.go index 4d018454..2a25dd5f 100644 --- a/internal/transport/diagnostics_test.go +++ b/internal/transport/diagnostics_test.go @@ -76,6 +76,26 @@ func TestExtractServerDiagnosticsFromMap(t *testing.T) { } } +func TestExtractServerDiagnosticsFromMapNestedBusinessCode(t *testing.T) { + t.Parallel() + content := map[string]any{ + "trace_id": "trace-002", + "code": "BUSINESS_ERROR", + "result": map[string]any{ + "success": false, + "errorCode": "ROBOT_NOT_FOUND", + "errorMsg": "robot info is not exist", + }, + } + diag := ExtractServerDiagnosticsFromMap(content) + if diag.TraceID != "trace-002" { + t.Fatalf("TraceID = %q, want trace-002", diag.TraceID) + } + if diag.ServerErrorCode != "ROBOT_NOT_FOUND" { + t.Fatalf("ServerErrorCode = %q, want ROBOT_NOT_FOUND", diag.ServerErrorCode) + } +} + func TestExtractServerDiagnosticsFromMap_Empty(t *testing.T) { t.Parallel() diag := ExtractServerDiagnosticsFromMap(nil) diff --git a/skills/multi/dingtalk-dev/SKILL.md b/skills/multi/dingtalk-dev/SKILL.md index 527b3109..b98a84ab 100644 --- a/skills/multi/dingtalk-dev/SKILL.md +++ b/skills/multi/dingtalk-dev/SKILL.md @@ -109,4 +109,4 @@ metadata: ## Gotchas - 新应用 `version list` 返回空不等于无可发布内容:先 `version create`,用返回的 `versionId` 继续 check-approval/publish。 -- `robot info is not exist` 是「应用还没配过机器人」,走 `robot config` 首次创建,不是 `enable`。 +- `robotStatus=UNCONFIGURED` 是「应用还没配过机器人」,走 `robot config` 首次创建,不是 `enable`。 diff --git a/skills/multi/dingtalk-dev/references/event.md b/skills/multi/dingtalk-dev/references/event.md index c020d56a..08b14d4c 100644 --- a/skills/multi/dingtalk-dev/references/event.md +++ b/skills/multi/dingtalk-dev/references/event.md @@ -4,11 +4,12 @@ `dws dev app event list/subscribe/unsubscribe`,按 `--unified-app-id` 定位,订阅/退订用 `--event-codes`(逗号分隔,一次多个)。参数查 `dws schema dev.app.event.`。 -前置条件(业务规则):订阅/退订前必须先建立长连(`dev connect`,见 connect.md)。长连未在线时服务端报「长链接未在线」(errorCode -1),这是预期,不是故障——先建联再订阅。 - 规则: - 写操作先 `--dry-run` 预览,确认后 `--yes`。 - 一次可订阅多个事件码,共用同一回调。 - 事件码取值以开放平台文档为准;不确定走 `dws dev doc search`。 - 退订前先 `event list` 确认当前订阅,避免退不存在的。 -- 返回看 `events`(已订阅列表)和 `pushType`(推送方式)。 +- `event list` 支持 `--cursor/--page-size`,返回 `events/hasMore/nextCursor/pageSize`;翻页继续传 `nextCursor`。 +- 返回看 `events[].subscribed` 和 `pushType=STREAM`。 +- `subscribe/unsubscribe` 的 `--event-codes` 必填,返回 `success/operation/unifiedAppId/eventCodes/needsPublish/versionRequiredAction`;失败时补 `errorCode/errorMsg/reason/retryable/action`。 +- 如果服务端返回 `errorCode=STREAM_NOT_CONNECTED`、`reason=STREAM_NOT_CONNECTED`、`retryable=false`、`action=run connect`,先执行 `dev connect` 建联,再重试订阅。 diff --git a/skills/multi/dingtalk-dev/references/robot.md b/skills/multi/dingtalk-dev/references/robot.md index 4144a238..1aa9a877 100644 --- a/skills/multi/dingtalk-dev/references/robot.md +++ b/skills/multi/dingtalk-dev/references/robot.md @@ -11,13 +11,13 @@ ## 一、新建智能体机器人(异步建号) -`submit` 提交任务拿 `taskId`,`result --task-id ` 轮询。`submit` 返回 `taskId/status/expiresIn/interval/retryCount`,提交成功通常是 `WAITING`。失败重试:把上次 `taskId` 通过 `--task-id` 传回 `submit`,避免重复创建。只有 `result` 返回 `SUCCESS` 才能用返回的 `agentId/robotCode/clientId/clientSecret`。 +`submit` 提交任务拿 `taskId`,`result --task-id ` 轮询。`submit` 返回 `taskId/status/expiresInSeconds/intervalSeconds/retryCount/bindsUnifiedApp`,提交成功通常是 `WAITING`,且 `bindsUnifiedApp=false` 表示异步建号任务不挂到现有应用。失败重试:把上次 `taskId` 通过 `--task-id` 传回 `submit`,避免重复创建。只有 `result` 返回 `SUCCESS` 才能用返回的 `agentId/robotCode/clientId/clientSecret`。 异步任务状态: | status | 含义 | 下一步 | |--------|------|--------| -| `WAITING` | 创建中 | 按 `interval` 轮询 `robot result` | +| `WAITING` | 创建中 | 按 `intervalSeconds` 轮询 `robot result` | | `SUCCESS` | 创建完成 | 保存 `robotCode/clientId/clientSecret`,凭据按敏感处理 | | `APPROVAL_REQUIRED` | 创建编排需审批 | 不要重复建号;按返回信息或后台审批后再继续 | | `FAIL` | 创建失败 | 读 `errorCode/errorMsg/failReason`;可带原 `taskId` 重新 `submit` | @@ -25,22 +25,23 @@ ## 二、现有应用的机器人配置 -`robot get` 返回机器人基础信息、回调、模式、状态、技能列表;应用尚未配置机器人时后端返回 `robot info is not exist`。 +`robot get` 返回机器人基础信息、回调、模式、状态、技能列表;应用尚未配置机器人时返回空态 `robotStatus=UNCONFIGURED`,不是业务错误。 状态判断: -- `status=1`:OFFLINE,配置存在但停用/下线。 -- `status=2`:ONLINE,配置已生效;`robotCode` 可用于加群、机器人身份发消息或后续建联。 +- `robotStatus=UNCONFIGURED`:应用未配置机器人,走 `robot config`。 +- `robotStatus=OFFLINE`:配置存在但停用/下线,可走 `robot enable`。 +- `robotStatus=ONLINE`:配置已启用;`robotCode` 可用于加群、机器人身份发消息或后续建联。 +- `mode` 是字符串枚举:`HTTPS` / `STREAM` / `AISKILL`。 - `robot get` 返回 `success=true` 且含 `robotCode` = 配置已落库,不是异步等待态。 - ONLINE 只代表能力已开启。要让机器人自动处理消息,还需配 `--outgoing-url`/`--event-callback-url`,或用 `dev connect` 接本地 Agent(见 connect.md)。 -- 未配置机器人时不返回 `status`,而是业务错误 `robot info is not exist`;这时走 `robot config`,不是 `enable`。 -`config` 是 upsert:建或改都用它,不存在则建、存在则改,至少给一个配置字段。国际化字段(`--i18n-name` 等)传 JSON,如 `'{"en_US":"Bot"}'`。`enable` 是纯启用:只开启能力,不带配置字段(只传 `--unified-app-id`)。旧的独立 `update` 已并入 `config`。`config` 成功后回读 `robot get`:返回 `status=2` 就别再误判"待生效",只有 `status=1` 或要重新上架才调 `enable`。 +`config` 是 upsert:建或改都用它,不存在则建、存在则改,至少给一个配置字段。国际化字段(`--i18n-name` 等)传 JSON,如 `'{"en_US":"Bot"}'`。`enable` 是纯启用:只开启能力,不带配置字段(只传 `--unified-app-id`)。旧的独立 `update` 已并入 `config`。`config/enable/disable` 成功统一返回 `success/operation/unifiedAppId/robotCode/robotStatus/configured`;回读 `robot get` 看到 `robotStatus=ONLINE` 就别再误判"待生效"。 ## 错误处理 | 情况 | 处理 | |------|------| -| `robot info is not exist` | 应用未配置机器人,先用 `robot config` 创建 | +| `robotStatus=UNCONFIGURED` | 应用未配置机器人,先用 `robot config` 创建 | | 应用名重复 | `app-name` 企业内需唯一,换名 | | `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | | 创建任务 `EXPIRED` | 任务过期,重新 `submit`(可带原 taskId) | diff --git a/skills/multi/dingtalk-dev/references/version.md b/skills/multi/dingtalk-dev/references/version.md index 932e57d7..af1c23b2 100644 --- a/skills/multi/dingtalk-dev/references/version.md +++ b/skills/multi/dingtalk-dev/references/version.md @@ -28,13 +28,15 @@ permission add(requiredApproval=true 写入版本变更) 区分两个"预检":`--dry-run` 是 CLI 层的预览不调上游;`check-approval` 是服务端查审批要求不发布。发布前建议先 `check-approval`。 -发布响应 `result`: +发布/预检不返回动作枚举 `result`,看结构化字段判断下一步: -| result | 含义 | 下一步 | -|--------|------|--------| -| `NOT_REQUIRED` | 不需审批;预检=可直接发布,真实发布=已直接上线 | 回读 `version status/get` 验证 `RELEASE` | -| `APPROVAL_REQUIRED` | 需审批,常见于 `check-approval` 或未指定审批人 | 看 `approvalMode/approvalCandidates`,让用户选审批人后再 `publish --approver-user-id` | -| `SUBMITTED` | 已提交审批 | 保存 `processId`,轮询 `version status` | +| 字段 | 含义 | 下一步 | +|------|------|--------| +| `requiresApproval=false` + `publishable=true` | 不需审批,`check-approval` 通过 | 可以执行 `version publish` | +| `requiresApproval=true` + `approvalMode=SELECT_APPROVER` | 需从候选人里选审批人 | 展示 `approvalCandidates`,让用户选后再 `publish --approver-user-id` | +| `requiresApproval=true` + `approvalMode=ENTERPRISE_SELF_BUILT` | 企业自建审核 | 不传 `--approver-user-id`,直接 `publish` 提交审批 | +| `published=true` | 本次 `publish` 已直接发布 | 回读 `version status/get` 验证 `versionStatus=RELEASE` | +| `approvalSubmitted=true` | 本次 `publish` 已提交审批 | 保存 `processId`,轮询 `version status` | 审批模式 `approvalMode`: diff --git a/skills/multi/dingtalk-devapp/references/event.md b/skills/multi/dingtalk-devapp/references/event.md index 67174586..107a80b9 100644 --- a/skills/multi/dingtalk-devapp/references/event.md +++ b/skills/multi/dingtalk-devapp/references/event.md @@ -7,10 +7,10 @@ ## 可订阅事件列表 ```bash -dws devapp event list --unified-app-id --format json +dws dev app event list --unified-app-id --page-size 20 --format json ``` -MCP tool: `list_dev_app_events`。返回 `pushType`(当前推送类型)和 `events[]`,每项含: +MCP tool: `list_dev_app_events`。返回 `pushType=STREAM`、`events[]`、`hasMore`、`nextCursor`、`pageSize`;`hasMore=true` 时下一页继续传 `--cursor `。`events[]` 每项含: | 字段 | 含义 | |------|------| @@ -23,26 +23,28 @@ MCP tool: `list_dev_app_events`。返回 `pushType`(当前推送类型)和 ` ## 订阅事件 ```bash -dws devapp event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json -dws devapp event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json +dws dev app event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json +dws dev app event subscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json ``` -MCP tool: `subscribe_dev_app_events`(入参 `unifiedAppId` + `eventCodes`,一次订阅多个事件码)。 +MCP tool: `subscribe_dev_app_events`(入参 `unifiedAppId` + 必填数组 `eventCodes`,一次订阅多个事件码)。成功返回 `success/operation=SUBSCRIBE/unifiedAppId/eventCodes/needsPublish/versionRequiredAction`;失败时补 `errorCode/errorMsg/reason/retryable/action`。 ## 退订事件 ```bash -dws devapp event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json -dws devapp event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json +dws dev app event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --dry-run --format json +dws dev app event unsubscribe --unified-app-id --event-codes user_add_org,org_dept_modify --yes --format json ``` -MCP tool: `unsubscribe_dev_app_events`(入参 `unifiedAppId` + `eventCodes`,一次退订多个事件码)。 +MCP tool: `unsubscribe_dev_app_events`(入参 `unifiedAppId` + 必填数组 `eventCodes`,一次退订多个事件码)。成功返回 `success/operation=UNSUBSCRIBE/unifiedAppId/eventCodes/needsPublish/versionRequiredAction`;失败时补 `errorCode/errorMsg/reason/retryable/action`。 ## 字段映射 | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| | `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID | +| `--cursor` | `cursor` | list 可选 | 服务端返回的下一页游标 | +| `--page-size` | `pageSize` | list 可选 | 单页数量 | | `--event-codes` | `eventCodes` | subscribe/unsubscribe 必填 | 事件码列表,逗号分隔,取自 `event list` | ## 灰度应用需发布版本生效(重要) @@ -57,21 +59,21 @@ event subscribe/unsubscribe 订阅变更写入版本元数据 → version status 轮询发布/审批状态 ``` -详见 [version.md](version.md)。 +详见版本发布文档。 ## 单步流程 ```text 1. 查看可订阅事件及当前状态 - dws devapp event list --unified-app-id --format json + dws dev app event list --unified-app-id --format json → 从返回里挑 eventCode 2. 订阅 - dws devapp event subscribe --unified-app-id --event-codes , --dry-run --format json + dws dev app event subscribe --unified-app-id --event-codes , --dry-run --format json → 确认后加 --yes 3. 验证 - dws devapp event list --unified-app-id --format json + dws dev app event list --unified-app-id --format json → 确认对应事件 subscribed=true(灰度应用需先发布版本) ``` @@ -81,4 +83,5 @@ event subscribe/unsubscribe 订阅变更写入版本元数据 |------|------| | `--event-codes` 缺失 | CLI 直接报错,先 `event list` 取事件码 | | 订阅后 `subscribed` 仍为 false | 灰度应用需先 `version publish` 发布版本 | +| `errorCode=STREAM_NOT_CONNECTED` / `reason=STREAM_NOT_CONNECTED` | 先执行 `dev connect` 建联,再重试订阅 | | `ServiceResult.success=false` | 透传 `errorCode/errorMsg` | From f69d9ed281f9160e95dd002f550739448074022d Mon Sep 17 00:00:00 2001 From: quanming Date: Thu, 18 Jun 2026 19:34:01 +0800 Subject: [PATCH 36/47] fix: align devapp member user ids --- docs/dev-agent-test-cases.md | 8 ++-- internal/helpers/devapp.go | 23 +++++++----- internal/helpers/devapp_test.go | 37 ++++++++++++------- .../multi/dingtalk-dev/references/member.md | 2 +- test/skill_tests.md | 16 ++++---- 5 files changed, 51 insertions(+), 35 deletions(-) diff --git a/docs/dev-agent-test-cases.md b/docs/dev-agent-test-cases.md index 5afd852d..3986b362 100644 --- a/docs/dev-agent-test-cases.md +++ b/docs/dev-agent-test-cases.md @@ -97,10 +97,10 @@ - **用户说**:「把 userId 张三、李四加成这个应用的开发者,加完看下成员列表,回头把李四移除;另外给应用加一个登录重定向地址 https://b.example.com/cb,别把原来的地址冲掉。」 - **覆盖**:`member list` / `add` / `remove`、`security config`;整组覆盖。 - **期望(分步)**: - 1. `member add --unified-app-id --users 张三id,李四id --member-type DEVELOPER --dry-run` → `--yes` → `member list` 回读 → `member remove --users 李四id --member-type DEVELOPER --dry-run` → `--yes`。 - 2. 安全配置:提醒 `--redirect-url` 是**整组覆盖、不是追加**——要保留原地址需把旧+新一起传:`security config --redirect-url <旧1,旧2,新> --dry-run` → `--yes`。 -- **通过判据**:`--users` 逗号分隔、`--member-type` 必填、用 userId 不用姓名;识别整组覆盖语义、避免只传新地址冲掉旧的;未提供的字段(如 ip-whitelist)不动。 -- **易错点**:漏 `--member-type`;security 只传新 redirect-url 把旧的清空。 + 1. `member add --unified-app-id --user-ids 张三id,李四id --member-type DEVELOPER --dry-run` → `--yes` → `member list` 回读 → `member remove --user-ids 李四id --member-type DEVELOPER --dry-run` → `--yes`。 + 2. 安全配置:提醒 `--redirect-urls` 是**整组覆盖、不是追加**——要保留原地址需把旧+新一起传:`security config --redirect-urls <旧1,旧2,新> --dry-run` → `--yes`。 +- **通过判据**:`--user-ids` 逗号分隔、`--member-type` 必填、用 userId 不用姓名;识别整组覆盖语义、避免只传新地址冲掉旧的;未提供的字段(如 ip-whitelist)不动。 +- **易错点**:漏 `--member-type`;security 只传新 redirect-urls 把旧的清空。 ### C8. 机器人建号、配置与本地建联 - **用户说**:「帮我建一个叫『小助手』的答疑机器人;另外这个现有应用还没机器人,给它也配上并启用;最后把机器人接到我本地的 Claude Code 调试。」 diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 3617f5f8..db23e030 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -799,7 +799,7 @@ func newDevAppMemberAddCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "add", Short: "添加开放平台应用成员", - Example: " dws dev app member add --unified-app-id --member-user-ids userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws dev app member add --unified-app-id --user-ids userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -816,7 +816,7 @@ func newDevAppMemberRemoveCommand(runner executor.Runner) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "移除开放平台应用成员", - Example: " dws dev app member remove --unified-app-id --member-user-ids userId1,userId2 --member-type DEVELOPER --dry-run", + Example: " dws dev app member remove --unified-app-id --user-ids userId1,userId2 --member-type DEVELOPER --dry-run", Args: cobra.NoArgs, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { @@ -1335,7 +1335,9 @@ func requiredDevAppUnifiedID(cmd *cobra.Command) (string, error) { func registerDevAppMemberMutationFlags(cmd *cobra.Command) { addDevAppUnifiedIDFlag(cmd) - cmd.Flags().String("member-user-ids", "", "成员 userId 列表,多个用逗号分隔 (必填)") + cmd.Flags().String("user-ids", "", "成员 userId 列表,多个用逗号分隔 (必填)") + cmd.Flags().String("member-user-ids", "", "成员 userId 列表,多个用逗号分隔 (兼容旧参数)") + _ = cmd.Flags().MarkHidden("member-user-ids") cmd.Flags().String("member-type", "", "成员类型,如 DEVELOPER (必填)") } @@ -1357,9 +1359,9 @@ func runDevAppMemberMutation(runner executor.Runner, cmd *cobra.Command, tool, o } params := map[string]any{ - "unifiedAppId": appID, - "memberUserIds": users, - "memberType": memberType, + "unifiedAppId": appID, + "userIds": users, + "memberType": memberType, } return runDevAppTool(runner, cmd, tool, params) } @@ -1468,13 +1470,16 @@ func normalizeDevAppScopeValueArray(content map[string]any, key string) { } func requiredDevAppUsers(cmd *cobra.Command) ([]string, error) { - usersRaw, _ := cmd.Flags().GetString("member-user-ids") + usersRaw, _ := cmd.Flags().GetString("user-ids") if strings.TrimSpace(usersRaw) == "" { - return nil, apperrors.NewValidation("--member-user-ids 为必填") + usersRaw, _ = cmd.Flags().GetString("member-user-ids") + } + if strings.TrimSpace(usersRaw) == "" { + return nil, apperrors.NewValidation("--user-ids 为必填") } users := splitDevAppList(usersRaw) if len(users) == 0 { - return nil, apperrors.NewValidation("--member-user-ids 至少包含一个 userId") + return nil, apperrors.NewValidation("--user-ids 至少包含一个 userId") } return users, nil } diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 0a8d988e..ca4e6d00 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -54,23 +54,34 @@ func TestDevAppMemberCommandsBuildToolParams(t *testing.T) { { name: "add multiple users", cmd: "add", - args: []string{"--unified-app-id", "app-001", "--member-user-ids", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, + args: []string{"--unified-app-id", "app-001", "--user-ids", "userId1,userId2,userId3,userId4", "--member-type", "DEVELOPER", "--yes"}, wantTool: "add_dev_app_members", wantParams: map[string]any{ - "unifiedAppId": "app-001", - "memberUserIds": []string{"userId1", "userId2", "userId3", "userId4"}, - "memberType": "DEVELOPER", + "unifiedAppId": "app-001", + "userIds": []string{"userId1", "userId2", "userId3", "userId4"}, + "memberType": "DEVELOPER", }, }, { name: "remove trims users", cmd: "remove", - args: []string{"--unified-app-id", "app-001", "--member-user-ids", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, + args: []string{"--unified-app-id", "app-001", "--user-ids", " userId1 , userId2 ", "--member-type", "DEVELOPER", "--yes"}, wantTool: "remove_dev_app_members", wantParams: map[string]any{ - "unifiedAppId": "app-001", - "memberUserIds": []string{"userId1", "userId2"}, - "memberType": "DEVELOPER", + "unifiedAppId": "app-001", + "userIds": []string{"userId1", "userId2"}, + "memberType": "DEVELOPER", + }, + }, + { + name: "add accepts legacy member user ids flag", + cmd: "add", + args: []string{"--unified-app-id", "app-001", "--member-user-ids", "userId1,userId2", "--member-type", "DEVELOPER", "--yes"}, + wantTool: "add_dev_app_members", + wantParams: map[string]any{ + "unifiedAppId": "app-001", + "userIds": []string{"userId1", "userId2"}, + "memberType": "DEVELOPER", }, }, } @@ -758,9 +769,9 @@ func TestDevAppMemberCommandsValidateRequiredFlags(t *testing.T) { wantErr string }{ {name: "list requires app", cmd: "list", args: nil, wantErr: "--unified-app-id 为必填"}, - {name: "add requires users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--member-user-ids 为必填"}, - {name: "add rejects empty users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-user-ids", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--member-user-ids 至少包含一个 userId"}, - {name: "remove requires member type", cmd: "remove", args: []string{"--unified-app-id", "app-001", "--member-user-ids", "userId1", "--dry-run"}, wantErr: "--member-type 为必填"}, + {name: "add requires users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--user-ids 为必填"}, + {name: "add rejects empty users", cmd: "add", args: []string{"--unified-app-id", "app-001", "--user-ids", " , ", "--member-type", "DEVELOPER", "--dry-run"}, wantErr: "--user-ids 至少包含一个 userId"}, + {name: "remove requires member type", cmd: "remove", args: []string{"--unified-app-id", "app-001", "--user-ids", "userId1", "--dry-run"}, wantErr: "--member-type 为必填"}, } for _, tc := range cases { @@ -870,11 +881,11 @@ func TestDevAppMemberAndSecurityRequireWriteGuard(t *testing.T) { }{ { name: "member add", - args: []string{"dev", "app", "member", "add", "--unified-app-id", "app-001", "--member-user-ids", "userId1", "--member-type", "DEVELOPER"}, + args: []string{"dev", "app", "member", "add", "--unified-app-id", "app-001", "--user-ids", "userId1", "--member-type", "DEVELOPER"}, }, { name: "member remove", - args: []string{"dev", "app", "member", "remove", "--unified-app-id", "app-001", "--member-user-ids", "userId1", "--member-type", "DEVELOPER"}, + args: []string{"dev", "app", "member", "remove", "--unified-app-id", "app-001", "--user-ids", "userId1", "--member-type", "DEVELOPER"}, }, { name: "security config", diff --git a/skills/multi/dingtalk-dev/references/member.md b/skills/multi/dingtalk-dev/references/member.md index 206a70e2..87a29fd8 100644 --- a/skills/multi/dingtalk-dev/references/member.md +++ b/skills/multi/dingtalk-dev/references/member.md @@ -2,4 +2,4 @@ > 成员=谁能改这个应用(DEVELOPER 等角色);见 SKILL.md 概念地图。 -`dws dev app member list/add/remove` 管理应用成员。参数查 `dws schema dev.app.member.`(add/remove 需 `--member-user-ids` 列表 + `--member-type`,如 DEVELOPER)。 +`dws dev app member list/add/remove` 管理应用成员。参数查 `dws schema dev.app.member.`(add/remove 需 `--user-ids` 列表 + `--member-type`,如 DEVELOPER;remove 也必须传 memberType,因为同一用户可能有多个成员身份)。 diff --git a/test/skill_tests.md b/test/skill_tests.md index 47e514ac..8f4861ea 100644 --- a/test/skill_tests.md +++ b/test/skill_tests.md @@ -912,29 +912,29 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 **dev_app_member_list_001** - Prompt: 查看开放平台应用 UNIFIED_APP_ID 的成员 -- Expected: `dws dev app member list --app-id UNIFIED_APP_ID --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID` +- Expected: `dws dev app member list --unified-app-id UNIFIED_APP_ID --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID` #### `dws dev app member add` **dev_app_member_add_001** - Prompt: 给开放平台应用 UNIFIED_APP_ID 添加开发者成员 userId1,userId2,先预览 -- Expected: `dws dev app member add --app-id UNIFIED_APP_ID --users userId1,userId2 --member-type DEVELOPER --dry-run --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID`, `--users` = `userId1,userId2`, `--member-type` = `DEVELOPER` +- Expected: `dws dev app member add --unified-app-id UNIFIED_APP_ID --user-ids userId1,userId2 --member-type DEVELOPER --dry-run --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--user-ids` = `userId1,userId2`, `--member-type` = `DEVELOPER` #### `dws dev app member remove` **dev_app_member_remove_001** - Prompt: 已确认,从开放平台应用 UNIFIED_APP_ID 移除开发者成员 userId1 -- Expected: `dws dev app member remove --app-id UNIFIED_APP_ID --users userId1 --member-type DEVELOPER --yes --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID`, `--users` = `userId1`, `--member-type` = `DEVELOPER`, `--yes` +- Expected: `dws dev app member remove --unified-app-id UNIFIED_APP_ID --user-ids userId1 --member-type DEVELOPER --yes --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--user-ids` = `userId1`, `--member-type` = `DEVELOPER`, `--yes` #### `dws dev app security config` **dev_app_security_config_001** - Prompt: 给开放平台应用 UNIFIED_APP_ID 配置 IP 白名单 192.0.2.10,先预览 -- Expected: `dws dev app security config --app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` -- Flags: `--app-id` = `UNIFIED_APP_ID`, `--ip-whitelist` = `192.0.2.10` +- Expected: `dws dev app security config --unified-app-id UNIFIED_APP_ID --ip-whitelist 192.0.2.10 --dry-run --format json` +- Flags: `--unified-app-id` = `UNIFIED_APP_ID`, `--ip-whitelist` = `192.0.2.10` #### `dws dev app permission list` From 787a40ffcb8ffcc3a3d318ba1fda5b78279a3ae9 Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Thu, 18 Jun 2026 22:54:24 +0800 Subject: [PATCH 37/47] fix cr problem --- docs/dev-agent-test-cases.md | 14 +++++++------- internal/cli/dev_schema_test.go | 10 +++++----- internal/helpers/devapp.go | 10 ++++++++-- internal/helpers/devapp_test.go | 4 ++-- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/dev-agent-test-cases.md b/docs/dev-agent-test-cases.md index 3986b362..e41112f2 100644 --- a/docs/dev-agent-test-cases.md +++ b/docs/dev-agent-test-cases.md @@ -106,20 +106,20 @@ - **用户说**:「帮我建一个叫『小助手』的答疑机器人;另外这个现有应用还没机器人,给它也配上并启用;最后把机器人接到我本地的 Claude Code 调试。」 - **覆盖**:`robot submit` / `result` / `get` / `config` / `enable` / `disable`、`dev connect`;异步轮询、robot info not exist、建联依赖预检、长驻进程、密钥脱敏。 - **期望(分步)**: - 1. 新建:`robot submit --app-name <名> --robot-name 小助手 --desc <功能> --dry-run` → `--yes`(拿 taskId)→ 按 interval 轮询 `robot result --task-id `,只有 `SUCCESS` 才用返回 `robotCode/clientId/clientSecret`(敏感)。 - 2. 现有应用:`robot get` 若 `robot info is not exist` → `robot config --unified-app-id --name ... --dry-run` → `--yes`(upsert 首次即创建)→ 回读 `robot get` 看 `status=2` → 需要时 `robot enable`(停用 `robot disable`)。 + 1. 新建:`robot submit --name <应用名> --robot-name 小助手 --desc <功能> --dry-run` → `--yes`(拿 taskId)→ 按 `intervalSeconds` 轮询 `robot result --task-id `,只有 `SUCCESS` 才用返回 `robotCode/clientId/clientSecret`(敏感)。 + 2. 现有应用:`robot get` 若 `robotStatus=UNCONFIGURED` → `robot config --unified-app-id --name ... --mode STREAM --dry-run` → `--yes`(upsert 首次即创建)→ 回读 `robot get` 看 `robotStatus=ONLINE` → 需要时 `robot enable`(停用 `robot disable`)。 3. 建联:`dev connect --channel auto --robot-client-id x --robot-client-secret y --dry-run` 看出参 `cli` 字段做依赖预检;正式 connect 是前台长驻进程,对话里跑要后台运行并告诉用户怎么停,或引导自己开终端。 -- **通过判据**:走异步 submit/result(同步建号已下线),轮询到 SUCCESS 再用凭证;未配置时走 config 不是 enable;config 是 upsert;写后回读 status;建联先 dry-run 预检、处理好长驻/缺凭证(先 submit/result 建号)。 +- **通过判据**:走异步 submit/result(同步建号已下线),轮询到 SUCCESS 再用凭证;未配置时走 config 不是 enable;config 是 upsert;写后回读 `robotStatus`;建联先 dry-run 预检、处理好长驻/缺凭证(先 submit/result 建号)。 - **易错点**:找「同步一次建好」的命令;WAITING 就用凭证;robot info not exist 时去 enable;前台直接起 connect 卡住对话。 ### C9. 事件订阅与上游错误排查 -- **用户说**:「让这个应用订阅『群成员入群』事件,回调发到 https://example.com/event,订阅完看下当前订阅了哪些,再把它取消掉;对了我之前发版本报了个 errcode 62012,这是啥意思?」 +- **用户说**:「让这个应用订阅『群成员入群』事件,订阅完看下当前订阅了哪些,再把它取消掉;对了我之前发版本报了个 errcode 62012,这是啥意思?」 - **覆盖**:`event list` / `subscribe` / `unsubscribe`、`dev doc search`;错误码透传、文档 RAG。 - **期望(分步)**: - 1. `event subscribe --unified-app-id --event-types chat_add_member_org --callback-url https://example.com/event --dry-run` → `--yes` → `event list` 回读 → `event unsubscribe --event-types chat_add_member_org --dry-run` → `--yes`。事件类型不确定先 `dev doc search` 查。 + 1. `event list --unified-app-id --page-size 20 --format json` 取 `eventCode` → `event subscribe --unified-app-id --event-codes chat_add_member_org --dry-run` → `--yes` → `event list` 回读 → `event unsubscribe --unified-app-id --event-codes chat_add_member_org --dry-run` → `--yes`。事件码不确定先 `event list` 翻页查。 2. 错误码:业务错误 `ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,再 `dev doc search --keyword "errcode 62012 " --format json` 做官方文档 RAG,结论基于命中条目。 -- **通过判据**:`--event-types` 逗号分隔共用回调,写操作先 dry-run;不编造事件类型/错误含义;先透传原始错误再走 RAG,结论不臆测、不编不存在的命令。 -- **易错点**:编事件类型名;凭空解释错误码。 +- **通过判据**:`--event-codes` 逗号分隔,写操作先 dry-run;`event list` 使用 `hasMore/nextCursor` 翻页;不编造事件码/错误含义;先透传原始错误再走 RAG,结论不臆测、不编不存在的命令。 +- **易错点**:编事件码;把事件回调地址塞进事件订阅命令;凭空解释错误码。 ### C10. 意图消歧(泛词边界) - **用户说**:「帮我建个机器人。」(无任何开放平台上下文) diff --git a/internal/cli/dev_schema_test.go b/internal/cli/dev_schema_test.go index 603e496b..4a745e87 100644 --- a/internal/cli/dev_schema_test.go +++ b/internal/cli/dev_schema_test.go @@ -74,7 +74,7 @@ func robotConfigToolSchema() HelperToolSchema { "unifiedAppId": map[string]any{"type": "string", "description": "统一应用 ID"}, "eventCallbackUrl": map[string]any{"type": "string", "description": "事件回调地址"}, "skills": map[string]any{"type": "array", "description": "技能列表"}, - "mode": map[string]any{"type": "integer", "description": "机器人模式枚举", "default": float64(0)}, + "mode": map[string]any{"type": "string", "description": "机器人模式", "enum": []any{"HTTPS", "STREAM", "AISKILL"}}, }, Required: []string{"unifiedAppId"}, } @@ -138,11 +138,11 @@ func TestRenderHelperSchema_LeafGwsFlat(t *testing.T) { } mode, _ := params["mode"].(map[string]any) - if mode == nil || mode["type"] != "integer" { - t.Fatalf("mode = %#v, want integer", mode) + if mode == nil || mode["type"] != "string" { + t.Fatalf("mode = %#v, want string", mode) } - if mode["default"] != "0" { - t.Fatalf("mode default = %v, want \"0\" (stringified MCP default)", mode["default"]) + if _, hasDefault := mode["default"]; hasDefault { + t.Fatalf("mode default = %v, want none", mode["default"]) } if mode["required"] != false { t.Fatalf("mode required = %v, want false", mode["required"]) diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index db23e030..625eb52d 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -1080,7 +1080,7 @@ func registerDevAppRobotConfigFlags(cmd *cobra.Command) { cmd.Flags().String("icon-media-id", "", "机器人图标 mediaId") cmd.Flags().String("outgoing-url", "", "消息回调地址") cmd.Flags().String("event-callback-url", "", "事件回调地址") - cmd.Flags().Int("mode", 0, "机器人模式枚举") + cmd.Flags().String("mode", "", "机器人模式:HTTPS / STREAM / AISKILL") cmd.Flags().String("skills", "", "技能列表,多个用逗号或分号分隔") cmd.Flags().Bool("add-scope", false, "是否自动添加机器人相关权限") cmd.Flags().Bool("disable-ssl-verify", false, "回调地址是否关闭 SSL 校验") @@ -1105,7 +1105,13 @@ func devAppRobotConfigParams(cmd *cobra.Command, appID string) (map[string]any, setString("outgoingUrl", "outgoing-url") setString("eventCallbackUrl", "event-callback-url") if cmd.Flags().Changed("mode") { - params["mode"] = devAppIntFlag(cmd, "mode") + mode := strings.ToUpper(strings.TrimSpace(devAppStringFlag(cmd, "mode"))) + switch mode { + case "HTTPS", "STREAM", "AISKILL": + params["mode"] = mode + default: + return nil, 0, apperrors.NewValidation("--mode 仅支持 HTTPS、STREAM、AISKILL") + } updates++ } if cmd.Flags().Changed("add-scope") { diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index ca4e6d00..22fad5a6 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -181,13 +181,13 @@ func TestDevAppRobotCommandsBuildToolParams(t *testing.T) { }, { name: "config create with skills and mode", - args: []string{"robot", "config", "--unified-app-id", "u-1", "--name", "小助手", "--brief", "审批助手", "--mode", "2", "--skills", "qa,approval", "--add-scope", "--yes"}, + args: []string{"robot", "config", "--unified-app-id", "u-1", "--name", "小助手", "--brief", "审批助手", "--mode", "STREAM", "--skills", "qa,approval", "--add-scope", "--yes"}, wantTool: "set_extension_robot_config", wantParams: map[string]any{ "unifiedAppId": "u-1", "name": "小助手", "brief": "审批助手", - "mode": 2, + "mode": "STREAM", "skills": []string{"qa", "approval"}, "addScope": true, }, From e193ba97f4f1a55b06ba4779901269c6f22beff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Mon, 22 Jun 2026 16:08:51 +0800 Subject: [PATCH 38/47] feat: add devapp install script --- docs/devapp-agent-install-guide.md | 1 + scripts/README.md | 2 +- scripts/install-devapp.sh | 146 +++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100755 scripts/install-devapp.sh diff --git a/docs/devapp-agent-install-guide.md b/docs/devapp-agent-install-guide.md index f75bdf00..497f7099 100644 --- a/docs/devapp-agent-install-guide.md +++ b/docs/devapp-agent-install-guide.md @@ -41,6 +41,7 @@ irm https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/ma | `DEVAPP_BRANCH` | 覆盖安装分支,默认 `feat/dws-devapp` | | `DEVAPP_SOURCE_DIR` | 使用已有源码目录安装,跳过 clone | | `DEVAPP_KEEP_SOURCE=1` | 保留临时源码目录,便于调试 | +| `DEVAPP_SKIP_SKILL_SETUP=1` | 跳过自动执行 `dws skill setup --mode multi` | | `DWS_INSTALL_DIR` | 传给底层 `scripts/install.sh`,覆盖 `dws` 安装目录 | | `DWS_SKILL_MODE` | 传给底层 `scripts/install.sh`,选择 `mono` 或 `multi` | diff --git a/scripts/README.md b/scripts/README.md index 5513489a..28069f23 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -11,7 +11,7 @@ These repo-local entrypoints are the supported shell entrypoints for building, t Script groups: -- Root installers: `./scripts/install.sh`, `./scripts/install.ps1`, `./scripts/install-skills.sh` +- Root installers: `./scripts/install.sh`, `./scripts/install.ps1`, `./scripts/install-skills.sh`, `./scripts/install-devapp.sh` - Dev helpers: `./scripts/dev/build.sh`, `./scripts/dev/lint.sh`, `./scripts/dev/ci-local.sh`, `./scripts/dev/run-mock-e2e.sh`, `./scripts/dev/coverage.sh` - Policy checks: `./scripts/policy/check-generated-drift.sh`, `./scripts/policy/check-command-surface.sh`, `./scripts/policy/check-open-source-assets.sh` - Release helpers: `./scripts/release/post-goreleaser.sh`, `./scripts/release/verify-package-managers.sh`, `./scripts/release/publish-homebrew-formula.sh` diff --git a/scripts/install-devapp.sh b/scripts/install-devapp.sh new file mode 100755 index 00000000..e87f3088 --- /dev/null +++ b/scripts/install-devapp.sh @@ -0,0 +1,146 @@ +#!/bin/sh +# Copyright 2026 Alibaba Group +# Licensed under the Apache License, Version 2.0 +# +# One-command installer for the DevApp preview branch. +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/feat/dws-devapp/scripts/install-devapp.sh | sh +# +# Environment variables: +# DEVAPP_REPO_URL Git repository URL. Default: https://github.com/wxianfeng/dingtalk-workspace-cli.git +# DEVAPP_BRANCH Branch to install. Default: feat/dws-devapp +# DEVAPP_SOURCE_DIR Existing source checkout to install from. +# DEVAPP_KEEP_SOURCE Set to 1 to keep the temporary source checkout. +# DEVAPP_SKIP_SKILL_SETUP Set to 1 to skip automatic multi-skill setup. +# +# Pass-through variables handled by scripts/install.sh: +# DWS_INSTALL_DIR Binary install directory. Default: ~/.local/bin +# DWS_INSTALL_NAME Installed binary name. Default: dws +# DWS_SKILL_MODE mono | multi. Default here: multi +# DWS_NO_SKILLS Set to 1 to skip skills. +# DWS_SKILLS_ONLY Set to 1 to install only skills. + +set -eu + +REPO_URL="${DEVAPP_REPO_URL:-https://github.com/wxianfeng/dingtalk-workspace-cli.git}" +BRANCH="${DEVAPP_BRANCH:-feat/dws-devapp}" +SOURCE_DIR="${DEVAPP_SOURCE_DIR:-}" +KEEP_SOURCE="${DEVAPP_KEEP_SOURCE:-0}" +SKIP_SKILL_SETUP="${DEVAPP_SKIP_SKILL_SETUP:-0}" +TMPDIR_WORK="" + +say() { + printf ' %s\n' "$@" +} + +err() { + printf ' ERROR: %s\n' "$@" >&2 + exit 1 +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || err "Missing required command: $1" +} + +cleanup() { + if [ -n "$TMPDIR_WORK" ] && [ "$KEEP_SOURCE" != "1" ]; then + rm -rf "$TMPDIR_WORK" + fi +} + +trap cleanup EXIT INT TERM + +print_banner() { + printf '\n' + say "DevApp preview installer" + say "Repository: ${REPO_URL}" + say "Branch: ${BRANCH}" + printf '\n' +} + +resolve_source_dir() { + if [ -n "$SOURCE_DIR" ]; then + [ -d "$SOURCE_DIR" ] || err "DEVAPP_SOURCE_DIR does not exist: ${SOURCE_DIR}" + [ -f "$SOURCE_DIR/scripts/install.sh" ] || err "install.sh not found under DEVAPP_SOURCE_DIR: ${SOURCE_DIR}" + printf '%s\n' "$SOURCE_DIR" + return 0 + fi + + need_cmd git + + TMPDIR_WORK="$(mktemp -d)" + checkout="${TMPDIR_WORK}/dingtalk-workspace-cli" + + say "Cloning DevApp preview source..." >&2 + git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$checkout" + + [ -f "$checkout/scripts/install.sh" ] || err "install.sh not found after clone" + printf '%s\n' "$checkout" +} + +installed_binary() { + install_dir="${DWS_INSTALL_DIR:-$HOME/.local/bin}" + install_name="${DWS_INSTALL_NAME:-dws}" + candidate="${install_dir}/${install_name}" + if [ -x "$candidate" ]; then + printf '%s\n' "$candidate" + return 0 + fi + if command -v "$install_name" >/dev/null 2>&1; then + command -v "$install_name" + return 0 + fi + return 1 +} + +setup_multi_skills() { + mode="$(printf '%s' "${DWS_SKILL_MODE:-multi}" | tr '[:upper:]' '[:lower:]')" + if [ "$mode" != "multi" ] || [ "${DWS_NO_SKILLS:-0}" = "1" ] || [ "$SKIP_SKILL_SETUP" = "1" ]; then + return 0 + fi + + bin_path="$(installed_binary || true)" + if [ -z "$bin_path" ]; then + say "" + say "Could not find installed dws binary; skip automatic skill setup." + say "Run later:" + say " dws skill setup --mode multi --source ${source_root} --yes" + return 0 + fi + + say "" + say "Installing DevApp agent skills from source..." + "$bin_path" skill setup --mode multi --source "$source_root" --yes +} + +main() { + print_banner + + if [ "${DWS_SKILLS_ONLY:-0}" != "1" ]; then + need_cmd go + need_cmd make + fi + + source_root="$(resolve_source_dir)" + + say "" + say "Installing dws from DevApp preview source..." + DWS_VERSION=latest DWS_SKILL_MODE="${DWS_SKILL_MODE:-multi}" sh "$source_root/scripts/install.sh" + + setup_multi_skills + + say "" + say "DevApp next steps:" + say " dws version" + say " dws auth login" + say " dws devapp --help --format json" + say " dws devapp list --format json" + + if [ "$KEEP_SOURCE" = "1" ]; then + say "" + say "Source checkout kept at: ${source_root}" + fi +} + +main From b65be61599346146ccae6f17191ffdb713c692b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E6=BE=9C?= Date: Mon, 22 Jun 2026 16:18:58 +0800 Subject: [PATCH 39/47] fix: limit devapp installer skills --- docs/devapp-agent-install-guide.md | 5 +- scripts/install-devapp.sh | 125 ++++++++++++++++++++++------- 2 files changed, 97 insertions(+), 33 deletions(-) diff --git a/docs/devapp-agent-install-guide.md b/docs/devapp-agent-install-guide.md index 497f7099..48d392d4 100644 --- a/docs/devapp-agent-install-guide.md +++ b/docs/devapp-agent-install-guide.md @@ -17,7 +17,7 @@ curl -fsSL https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/fe 1. 拉取 `wxianfeng/dingtalk-workspace-cli` 的 `feat/dws-devapp` 分支。 2. 使用本地源码构建 `dws`。 3. 安装 `dws` 到默认目录 `~/.local/bin`。 -4. 安装 Agent Skill 到本机已检测到的 Agent 目录。 +4. 安装 Agent Skill 到本机已检测到的 Agent 目录,只安装通用 `dws` 和 DevApp 专用 `dws-devapp` 两个 skill。 > 预览分支安装需要本机已有 `git`、`go` 和 `make`。Go 版本要求以仓库 `go.mod` 为准。 @@ -41,7 +41,8 @@ irm https://raw.githubusercontent.com/DingTalk-Real-AI/dingtalk-workspace-cli/ma | `DEVAPP_BRANCH` | 覆盖安装分支,默认 `feat/dws-devapp` | | `DEVAPP_SOURCE_DIR` | 使用已有源码目录安装,跳过 clone | | `DEVAPP_KEEP_SOURCE=1` | 保留临时源码目录,便于调试 | -| `DEVAPP_SKIP_SKILL_SETUP=1` | 跳过自动执行 `dws skill setup --mode multi` | +| `DEVAPP_SKIP_SKILL_SETUP=1` | 跳过自动安装 `dws` 与 `dws-devapp` skill | +| `DEVAPP_SKILL_NAME` | 覆盖 DevApp skill 安装名称,默认 `dws-devapp` | | `DWS_INSTALL_DIR` | 传给底层 `scripts/install.sh`,覆盖 `dws` 安装目录 | | `DWS_SKILL_MODE` | 传给底层 `scripts/install.sh`,选择 `mono` 或 `multi` | diff --git a/scripts/install-devapp.sh b/scripts/install-devapp.sh index e87f3088..74c3f9a7 100755 --- a/scripts/install-devapp.sh +++ b/scripts/install-devapp.sh @@ -12,7 +12,8 @@ # DEVAPP_BRANCH Branch to install. Default: feat/dws-devapp # DEVAPP_SOURCE_DIR Existing source checkout to install from. # DEVAPP_KEEP_SOURCE Set to 1 to keep the temporary source checkout. -# DEVAPP_SKIP_SKILL_SETUP Set to 1 to skip automatic multi-skill setup. +# DEVAPP_SKIP_SKILL_SETUP Set to 1 to skip automatic DevApp skill install. +# DEVAPP_SKILL_NAME Installed DevApp skill directory/name. Default: dws-devapp # # Pass-through variables handled by scripts/install.sh: # DWS_INSTALL_DIR Binary install directory. Default: ~/.local/bin @@ -28,6 +29,7 @@ BRANCH="${DEVAPP_BRANCH:-feat/dws-devapp}" SOURCE_DIR="${DEVAPP_SOURCE_DIR:-}" KEEP_SOURCE="${DEVAPP_KEEP_SOURCE:-0}" SKIP_SKILL_SETUP="${DEVAPP_SKIP_SKILL_SETUP:-0}" +DEVAPP_SKILL_NAME="${DEVAPP_SKILL_NAME:-dws-devapp}" TMPDIR_WORK="" say() { @@ -79,39 +81,95 @@ resolve_source_dir() { printf '%s\n' "$checkout" } -installed_binary() { - install_dir="${DWS_INSTALL_DIR:-$HOME/.local/bin}" - install_name="${DWS_INSTALL_NAME:-dws}" - candidate="${install_dir}/${install_name}" - if [ -x "$candidate" ]; then - printf '%s\n' "$candidate" - return 0 - fi - if command -v "$install_name" >/dev/null 2>&1; then - command -v "$install_name" - return 0 - fi - return 1 +copy_skill() { + src="$1" + dest="$2" + + rm -rf "$dest" + mkdir -p "$dest" + cp -R "$src/"* "$dest/" 2>/dev/null || cp -r "$src/"* "$dest/" } -setup_multi_skills() { - mode="$(printf '%s' "${DWS_SKILL_MODE:-multi}" | tr '[:upper:]' '[:lower:]')" - if [ "$mode" != "multi" ] || [ "${DWS_NO_SKILLS:-0}" = "1" ] || [ "$SKIP_SKILL_SETUP" = "1" ]; then +copy_devapp_skill() { + src="$1" + dest="$2" + + copy_skill "$src" "$dest" + if [ -f "$dest/SKILL.md" ]; then + tmp_skill="${dest}/SKILL.md.tmp" + sed "s/^name: .*/name: ${DEVAPP_SKILL_NAME}/" "$dest/SKILL.md" > "$tmp_skill" + mv "$tmp_skill" "$dest/SKILL.md" + fi +} + +install_skill_to_agent_homes() { + src="$1" + skill_name="$2" + kind="$3" + installed=0 + idx=0 + + for agent_dir in \ + ".agents/skills" \ + ".claude/skills" \ + ".cursor/skills" \ + ".qoder/skills" \ + ".qoderwork/skills" \ + ".gemini/skills" \ + ".codex/skills" \ + ".github/skills" \ + ".windsurf/skills" \ + ".augment/skills" \ + ".cline/skills" \ + ".amp/skills" \ + ".kiro/skills" \ + ".trae/skills" \ + ".openclaw/skills" \ + ".hermes/skills" + do + base_dir="$HOME/$agent_dir" + parent_gate="$(dirname "$base_dir")" + if [ "$idx" -gt 0 ] && [ ! -e "$parent_gate" ]; then + idx=$((idx + 1)) + continue + fi + + dest="$base_dir/$skill_name" + if [ "$kind" = "devapp" ]; then + copy_devapp_skill "$src" "$dest" + else + copy_skill "$src" "$dest" + fi + say "✅ Skill installed: ~/${agent_dir}/${skill_name}" + installed=$((installed + 1)) + idx=$((idx + 1)) + done + + if [ "$installed" -eq 0 ]; then + dest="$HOME/.agents/skills/$skill_name" + if [ "$kind" = "devapp" ]; then + copy_devapp_skill "$src" "$dest" + else + copy_skill "$src" "$dest" + fi + say "✅ Skill installed: ~/.agents/skills/${skill_name}" + fi +} + +install_devapp_skills() { + if [ "${DWS_NO_SKILLS:-0}" = "1" ] || [ "$SKIP_SKILL_SETUP" = "1" ]; then return 0 fi - bin_path="$(installed_binary || true)" - if [ -z "$bin_path" ]; then - say "" - say "Could not find installed dws binary; skip automatic skill setup." - say "Run later:" - say " dws skill setup --mode multi --source ${source_root} --yes" - return 0 - fi + dws_skill_src="$source_root/skills/mono" + devapp_skill_src="$source_root/skills/multi/dingtalk-dev" + [ -f "$dws_skill_src/SKILL.md" ] || err "dws skill source not found: ${dws_skill_src}" + [ -f "$devapp_skill_src/SKILL.md" ] || err "DevApp skill source not found: ${devapp_skill_src}" say "" - say "Installing DevApp agent skills from source..." - "$bin_path" skill setup --mode multi --source "$source_root" --yes + say "Installing agent skills: dws + ${DEVAPP_SKILL_NAME}" + install_skill_to_agent_homes "$dws_skill_src" "dws" "dws" + install_skill_to_agent_homes "$devapp_skill_src" "$DEVAPP_SKILL_NAME" "devapp" } main() { @@ -124,11 +182,16 @@ main() { source_root="$(resolve_source_dir)" - say "" - say "Installing dws from DevApp preview source..." - DWS_VERSION=latest DWS_SKILL_MODE="${DWS_SKILL_MODE:-multi}" sh "$source_root/scripts/install.sh" + if [ "${DWS_SKILLS_ONLY:-0}" = "1" ]; then + say "" + say "Skipping binary install because DWS_SKILLS_ONLY=1." + else + say "" + say "Installing dws from DevApp preview source..." + DWS_VERSION=latest DWS_NO_SKILLS=1 sh "$source_root/scripts/install.sh" + fi - setup_multi_skills + install_devapp_skills say "" say "DevApp next steps:" From 5d5884a0d77633c8549d59dfec3d5fc9a623f02d Mon Sep 17 00:00:00 2001 From: qinze Date: Mon, 22 Jun 2026 18:06:27 +0800 Subject: [PATCH 40/47] =?UTF-8?q?fix(dev):=20=E6=A0=A1=E5=87=86=20dingtalk?= =?UTF-8?q?-dev=20skill=20=E4=B8=8E=E7=9C=9F=E6=9C=BA=E4=B8=80=E8=87=B4=20?= =?UTF-8?q?+=20=E6=B8=85=E7=90=86=E9=87=8D=E5=A4=8D=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - skill 订正(以真机/schema 为准): app appStatus 字符串/permission 状态字段与 apiPreview/credentials 去掉不存在的 showSecret/webapp 结构化空态/connect 去过时 警告并补 flag/robot 去掉错误的 success 字段; version 用远端结构化审批字段 - 删除重复的 install-dev.sh, 保留 install-devapp.sh 并去掉会误拦用户的 go/make 检查 Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/install-dev.sh | 106 ------------------ scripts/install-devapp.sh | 5 - skills/multi/dingtalk-dev/SKILL.md | 12 +- skills/multi/dingtalk-dev/references/app.md | 17 +-- .../multi/dingtalk-dev/references/connect.md | 6 +- .../dingtalk-dev/references/credentials.md | 8 +- .../dingtalk-dev/references/permission.md | 20 ++-- skills/multi/dingtalk-dev/references/robot.md | 6 +- .../multi/dingtalk-dev/references/webapp.md | 2 +- 9 files changed, 34 insertions(+), 148 deletions(-) delete mode 100755 scripts/install-dev.sh diff --git a/scripts/install-dev.sh b/scripts/install-dev.sh deleted file mode 100755 index 02edbd1e..00000000 --- a/scripts/install-dev.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/bin/sh -# Copyright 2026 Alibaba Group -# Licensed under the Apache License, Version 2.0 -# -# One-command installer for the Dev preview branch. -# -# Usage: -# curl -fsSL https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/feat/dws-dev/scripts/install-dev.sh | sh -# -# Environment variables: -# DWS_DEV_REPO_URL Git repository URL. Default: https://github.com/wxianfeng/dingtalk-workspace-cli.git -# DWS_DEV_BRANCH Branch to install. Default: feat/dws-dev -# DWS_DEV_SOURCE_DIR Existing source checkout to install from. -# DWS_DEV_KEEP_SOURCE Set to 1 to keep the temporary source checkout. -# -# The script clones the requested branch and delegates to scripts/install.sh -# from that checkout, which builds and installs dws from local source. - -set -eu - -REPO_URL="${DWS_DEV_REPO_URL:-https://github.com/wxianfeng/dingtalk-workspace-cli.git}" -BRANCH="${DWS_DEV_BRANCH:-feat/dws-dev}" -SOURCE_DIR="${DWS_DEV_SOURCE_DIR:-}" -KEEP_SOURCE="${DWS_DEV_KEEP_SOURCE:-0}" -TMPDIR_WORK="" - -say() { - printf ' %s\n' "$@" -} - -err() { - printf ' ERROR: %s\n' "$@" >&2 - exit 1 -} - -need_cmd() { - command -v "$1" >/dev/null 2>&1 || err "Missing required command: $1" -} - -cleanup() { - if [ -n "$TMPDIR_WORK" ] && [ "$KEEP_SOURCE" != "1" ]; then - rm -rf "$TMPDIR_WORK" - fi -} - -trap cleanup EXIT INT TERM - -print_banner() { - printf '\n' - say "Dev preview installer" - say "Repository: ${REPO_URL}" - say "Branch: ${BRANCH}" - printf '\n' -} - -resolve_source_dir() { - if [ -n "$SOURCE_DIR" ]; then - [ -d "$SOURCE_DIR" ] || err "DWS_DEV_SOURCE_DIR does not exist: ${SOURCE_DIR}" - [ -f "$SOURCE_DIR/scripts/install.sh" ] || err "install.sh not found under DWS_DEV_SOURCE_DIR: ${SOURCE_DIR}" - printf '%s\n' "$SOURCE_DIR" - return 0 - fi - - need_cmd git - - TMPDIR_WORK="$(mktemp -d)" - checkout="${TMPDIR_WORK}/dingtalk-workspace-cli" - - say "Cloning Dev preview source..." >&2 - git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$checkout" - - [ -f "$checkout/scripts/install.sh" ] || err "install.sh not found after clone" - printf '%s\n' "$checkout" -} - -main() { - print_banner - - if [ "${DWS_SKILLS_ONLY:-0}" != "1" ]; then - need_cmd go - need_cmd make - fi - - source_root="$(resolve_source_dir)" - - say "" - say "Installing dws from Dev preview source..." - # the dev skill (dingtalk-dev) ships only in multi mode (developer-facing); mono targets - # general office users and intentionally has no dev routing. - DWS_VERSION=latest DWS_SKILL_MODE="${DWS_SKILL_MODE:-multi}" sh "$source_root/scripts/install.sh" - - say "" - say "Dev next steps:" - say " dws version" - say " dws auth login" - say " dws skill setup --mode multi # install per-product skills incl. dingtalk-dev" - say " dws dev --help --format json" - say " dws dev app list --format json" - - if [ "$KEEP_SOURCE" = "1" ]; then - say "" - say "Source checkout kept at: ${source_root}" - fi -} - -main diff --git a/scripts/install-devapp.sh b/scripts/install-devapp.sh index 74c3f9a7..5db972a2 100755 --- a/scripts/install-devapp.sh +++ b/scripts/install-devapp.sh @@ -175,11 +175,6 @@ install_devapp_skills() { main() { print_banner - if [ "${DWS_SKILLS_ONLY:-0}" != "1" ]; then - need_cmd go - need_cmd make - fi - source_root="$(resolve_source_dir)" if [ "${DWS_SKILLS_ONLY:-0}" = "1" ]; then diff --git a/skills/multi/dingtalk-dev/SKILL.md b/skills/multi/dingtalk-dev/SKILL.md index b98a84ab..1b65b337 100644 --- a/skills/multi/dingtalk-dev/SKILL.md +++ b/skills/multi/dingtalk-dev/SKILL.md @@ -44,13 +44,13 @@ metadata: ### ID 体系 | 标识 | 是什么 | 用在哪 | |------|--------|--------| -| `unifiedAppId` | 统一应用 ID,新模型主键 | 唯一全树定位标识,所有单应用命令都用 `--unified-app-id` | -| `appKey` = `clientId` | 应用身份标识,同一个东西的新旧两个名字,非密钥 | OpenAPI 调用、建联;也可作 `--app-key` 列表过滤(不能定位单应用) | +| `unifiedAppId` | 统一应用 ID,全树主键 | 唯一全树定位标识,所有单应用命令都用 `--unified-app-id` | +| `appKey` = `clientId` | 应用身份标识,同一个标识的两个名字,非密钥 | OpenAPI 调用、建联;也可作 `--app-key` 列表过滤(不能定位单应用) | | `appSecret` = `clientSecret` | 应用密钥,敏感 | 同上,按敏感凭证处理 | -| `agentId` | 旧版微应用 ID,历史遗留 | 仅出现在返回数据里;不能用于定位,拿到它先查出 unifiedAppId | +| `agentId` | 应用 ID,仅出现在返回数据里 | 不能用于定位,拿到它先查出 unifiedAppId | | `robotCode` | 机器人编号 | 加群、机器人发消息、建联 | -应用定位统一只用 `--unified-app-id`(--app-key/--name 仅作 list 过滤,不能定位单应用)。旧的 `--agent-id`/`--app-id`/`--custom-key` 定位 flag 已移除——agentId 只是返回字段,不再是入参定位手段。appKey 与 clientId 是同一标识的新旧两名,无需追问区别。 +应用定位统一只用 `--unified-app-id`(--app-key/--name 仅作 list 过滤,不能定位单应用)。agentId 只是返回字段,不能用于定位。appKey 与 clientId 是同一标识的两个名字,无需追问区别。 ### 生效模型 - 改配置不等于线上生效,需审批的变更(如 `requiredApproval=true` 的权限点)先累积在开发态,必须走版本通道才上线: @@ -61,7 +61,7 @@ metadata: ``` - 机器人等能力需版本发布后才能被搜索、加群、路由消息。 - 用户问「为什么没生效 / 机器人搜不到 / 权限加了还报错」时,先查 `version status`。 -- 两套状态别混:应用 appStatus(0 停用 / 1 激活 / 2 待激活 / 3 过期)是应用开关;版本 versionStatus(INIT / AUDIT / RELEASE / GRAY)是变更走到哪了。 +- 两套状态别混:应用 appStatus(字符串,取值如 normal / published)是应用开关;版本 versionStatus(INIT / AUDIT / RELEASE / GRAY)是变更走到哪了。app list 不回 appStatus(恒 null),看应用状态以 app get 为准。 ### 边界与角色 - 本 skill 只管企业内部应用。接口文档用 `dingtalk-devdoc`;钉钉云文档用 `dingtalk-doc`;工作台入口的「应用」用 `workbench app`;群里发消息用的机器人用 `dingtalk-chat`;审批流用 `dingtalk-oa`。 @@ -76,7 +76,7 @@ metadata: ### 通用出参约定(跨所有命令) - 游标分页(list / permission list / version list / event list / doc search):首次不传 `--cursor`,出参带 `nextCursor`(空=到底)原样回传续翻;`hasMore == nextCursor 非空`。cursor 是上游不透明令牌,不要自己解析或构造,也不要跨命令复用。 -- 批量聚合:`permission remove` 传多个权限点时出参是 `{results:[...], ok, total, failedCount}`,逐条判断成败(`ok=false` 即有失败)。 +- 批量聚合:`permission remove` 出参是 `{removed, removedScopeValues, rejectedScopeValues, success, message}`,逐条看 `removedScopeValues`/`rejectedScopeValues` 判断每个权限点成败。 - pretty:`--format pretty` 会在应用/版本状态字段旁附 `*Text` 可读标签(如 `appStatusText`);JSON 格式不附,以原始字段为准。 - 失败:`ServiceResult.success=false` 原样透传 `errorCode/errorMsg`,不编造解释,解读走下方文档 RAG。 diff --git a/skills/multi/dingtalk-dev/references/app.md b/skills/multi/dingtalk-dev/references/app.md index b5e25c62..e398b5cd 100644 --- a/skills/multi/dingtalk-dev/references/app.md +++ b/skills/multi/dingtalk-dev/references/app.md @@ -6,25 +6,18 @@ ## 应用定位 -所有单应用命令统一只用 `--unified-app-id`(全树主键)定位。`--app-key`/`--name` 只在 `dev app list` 里作列表过滤,不能定位单应用;旧的 `--agent-id`/`--app-id`/`--custom-key` 已移除。拿到 appKey/agentId 时,先用 `dev app list` 查出 unifiedAppId 再操作。 +所有单应用命令统一只用 `--unified-app-id`(全树主键)定位。`--app-key`/`--name` 只在 `dev app list` 里作列表过滤,不能定位单应用。拿到 appKey/agentId 时,先用 `dev app list` 查出 unifiedAppId 再操作。 ## 应用状态 appStatus -列表/详情统一用 `appStatus`,按生命周期判断,不要和版本 `versionStatus` 混。 +`app get` 的 `appStatus` 是字符串,取值如 `normal`、`published`。`app list` 不回这个字段(恒 `null`),看应用状态以 `app get` 为准。应用状态 `appStatus` 和版本状态 `versionStatus` 是两套,别混。遇到没见过的 `appStatus` 值原样展示。 -| appStatus | 枚举 | 含义 | 下一步 | -|-----------|------|------|--------| -| `0` | `IN_ACTIVE` | 已停用,应用不可用 | 需恢复走 `enable` | -| `1` | `ACTIVE` | 已激活,应用可用 | 可继续配权限/网页/机器人/版本 | -| `2` | `WAIT_ACTIVE` | 待激活 | 先回读 `get/list` 确认,别按已生效处理 | -| `3` | `EXPIRED` | 已过期 | 停止写操作,提示到开发者后台或管理员处理 | - -`create/update` 返回的 `versionStatus` 是版本状态(见 version.md),不等同应用启停状态。 +`app create`、`app update` 不返回状态字段;版本状态由 `version create` 返回的 `status`(值如 INIT)表达,见 version.md。 ## 要点 -- `get` 主要用于定位核验;若偶尔返回 `clientSecret/appSecret`,脱敏处理,不复制到回答;主动读凭证走 `credentials get`。 -- `disable/enable` 后必须回读 `get`/`list`:`appStatus=0` 才算停用完成,`appStatus=1` 才算启用完成;接口只返回成功未带状态时,以回读为准。 +- `get` 主要用于定位核验;若返回里带 `appSecret`,脱敏处理,不复制到回答;主动读凭证走 `credentials get`。 +- `disable/enable` 成功返回 `{disabled:true}` / `{enabled:true}` + `message`,不回 `appStatus`;以这个布尔判操作成败。要确认最终生效态再 `get` 看 `appStatus` 字符串值。 - `delete` 前必须展示应用摘要;删除是异步,成功后延迟从列表消失。 ## 错误处理 diff --git a/skills/multi/dingtalk-dev/references/connect.md b/skills/multi/dingtalk-dev/references/connect.md index 04b51d8f..14272c90 100644 --- a/skills/multi/dingtalk-dev/references/connect.md +++ b/skills/multi/dingtalk-dev/references/connect.md @@ -21,12 +21,16 @@ dws dev connect --robot-client-id --robot-client-secret ` 读取应用凭证。参数查 `dws schema dev.app.credentials.get`。 +返回字段:`clientId`/`appKey`(同值)、`clientSecret`/`appSecret`(同值)、`currentSecretStatus`、`hasPendingExpireTask`、`unifiedAppId` 等。 + 规则: -- CLI 只传应用定位字段,不传 `showSecret`/`confirmSecret`。 -- 返回可能含 `clientSecret/appSecret`,按敏感凭证处理,不写进回答文本。 -- 不能用 `dev app get` 代替;`dev app get` 偶尔返回密钥也只用于内部判断并脱敏,不向用户展开。 +- 该命令只需 `--unified-app-id`。 +- 返回里 `clientSecret/appSecret` 是明文密钥,按敏感凭证处理,不写进回答文本。 +- 不能用 `dev app get` 代替;`dev app get` 也会带密钥,同样只用于内部判断并脱敏,不向用户展开。 diff --git a/skills/multi/dingtalk-dev/references/permission.md b/skills/multi/dingtalk-dev/references/permission.md index dba7728e..1cc4a845 100644 --- a/skills/multi/dingtalk-dev/references/permission.md +++ b/skills/multi/dingtalk-dev/references/permission.md @@ -16,18 +16,16 @@ | `AUTHED` | 只看已授权/已开通 | | `UNAUTHED` | 只看未授权/未开通 | -单个权限项返回的 `status` 是内部操作态: +单个权限项的状态看这几个字段: -| status | 枚举 | 含义 | 下一步 | -|--------|------|------|--------| -| `0` | `STATUS_OBTAINED` | 权限已获得 | 不要重复申请;如需取消,确认 `canRemove=true` 后走 `permission remove` | -| `1` | `STATUS_APPLYING` | 权限申请中 | 不要重复申请;看 `authedStatusDesc`,通常等待审批或版本发布 | -| `2` | `STATUS_CAN_APPLY` | 可以申请 | 走 `permission add` | -| `3` | `STATUS_CAN_NOT_APPLY` | 不可申请 | 停止,展示 `applyDisabledReason/displayMessage` | +- `authed`(布尔):是否已授权/已开通。true=已开通,不要重复申请。 +- `allowedActions`(数组):本权限点当前允许的动作,如 `["view","detail","apply"]`。含 `apply` 才能申请,含 `remove` 才能取消。 +- `authedStatusDesc`(中文文案):状态的中文说明,如"已开通"/"未开通",直接展示给用户。 +- `apiStatus`:权限点本身的开放状态,如 `FULLY_OPEN`。 +- `requiredApproval`(布尔):申请是否需审批。true 的变更走版本通道,审批在版本发布时处理。 +- `displayMessage`(中文文案):服务端给的提示语,能否申请的原因看它。 -`authedStatusDesc` 是给用户看的细分状态:`OPENED`/`APPLIED`/`TO_BE_PUBLISHED`=已开通/已申请/待发布;`NOT_OPEN`/`NOT_APPLIED`=未开通/未申请;`AUDIT_PROCESSING`=审批中;`AUDIT_REFUSE`=审批未通过。能否操作仍以 `status`、`canEdit`、`canApplyDirectly`、`allowedActions` 为准。 - -list 默认同时返回 APP 和 SNS 权限;列表模式只返回 `apiPreview`,`--scope-value` 详情模式返回完整 `apiList`。`permission search`/`detail` 是 `list` 的别名。 +list 默认同时返回 APP 和 SNS 权限;列表模式和 `--scope-value` 详情模式,权限项里的 API 信息字段都叫 `apiPreview`。`permission search` 是 `list` 的别名。 scopeValue 选择顺序: 1. 用户给了 `scopeValue`,精确匹配 @@ -41,4 +39,4 @@ scopeValue 选择顺序: ## 取消权限 -`--scope-values` 多个逗号分隔;上游一次只取消一个权限点,多条时 CLI 逐条调用并返回 `results` 聚合数组。未开通返回 `NOT_AUTHED`;不可编辑返回 no-edit 原因。 +`--scope-values` 多个逗号分隔。返回:`removed`(布尔,整体成败)、`removedScopeValues`(成功取消的)、`rejectedScopeValues`(被拒的)、`message`。逐条看 `removedScopeValues`/`rejectedScopeValues` 判断每个权限点的结果。 diff --git a/skills/multi/dingtalk-dev/references/robot.md b/skills/multi/dingtalk-dev/references/robot.md index 1aa9a877..399c9fd9 100644 --- a/skills/multi/dingtalk-dev/references/robot.md +++ b/skills/multi/dingtalk-dev/references/robot.md @@ -4,7 +4,7 @@ 为开放平台企业内部应用创建和配置机器人。参数查 `dws schema dev.app.robot.`。分两类场景: -1. 新建智能体机器人:异步创建一个新的 Agent 应用 + 承载机器人(`submit` / `result`)。同步建号已下线,统一走异步。 +1. 新建智能体机器人:异步创建一个新的 Agent 应用 + 承载机器人(`submit` / `result`)。 2. 现有应用配置机器人:在已存在的应用上配置/启用/停用机器人(`get` / `config`(upsert) / `enable` / `disable`),用 `--unified-app-id` 定位。 > `corpId` / `userId` 由系统上下文自动注入,CLI 不传。所有写操作先 `--dry-run`,确认后再 `--yes`。 @@ -32,10 +32,10 @@ - `robotStatus=OFFLINE`:配置存在但停用/下线,可走 `robot enable`。 - `robotStatus=ONLINE`:配置已启用;`robotCode` 可用于加群、机器人身份发消息或后续建联。 - `mode` 是字符串枚举:`HTTPS` / `STREAM` / `AISKILL`。 -- `robot get` 返回 `success=true` 且含 `robotCode` = 配置已落库,不是异步等待态。 +- `robot get` 正常返回是平铺字段(`configured`/`mode`/`robotStatus`/`robotCode`/`name`/`brief`/`desc`),没有 `success` 字段;拿到这组字段就是配置已落库,不是异步等待态。 - ONLINE 只代表能力已开启。要让机器人自动处理消息,还需配 `--outgoing-url`/`--event-callback-url`,或用 `dev connect` 接本地 Agent(见 connect.md)。 -`config` 是 upsert:建或改都用它,不存在则建、存在则改,至少给一个配置字段。国际化字段(`--i18n-name` 等)传 JSON,如 `'{"en_US":"Bot"}'`。`enable` 是纯启用:只开启能力,不带配置字段(只传 `--unified-app-id`)。旧的独立 `update` 已并入 `config`。`config/enable/disable` 成功统一返回 `success/operation/unifiedAppId/robotCode/robotStatus/configured`;回读 `robot get` 看到 `robotStatus=ONLINE` 就别再误判"待生效"。 +`config` 是 upsert:建或改都用它,不存在则建、存在则改,至少给一个配置字段。国际化字段(`--i18n-name` 等)传 JSON,如 `'{"en_US":"Bot"}'`。`enable` 是纯启用:只开启能力,不带配置字段(只传 `--unified-app-id`)。`config/enable/disable` 成功统一返回 `success/operation/unifiedAppId/robotCode/robotStatus/configured`;回读 `robot get` 看到 `robotStatus=ONLINE` 就别再误判"待生效"。 ## 错误处理 diff --git a/skills/multi/dingtalk-dev/references/webapp.md b/skills/multi/dingtalk-dev/references/webapp.md index d9bf0eb3..147aad80 100644 --- a/skills/multi/dingtalk-dev/references/webapp.md +++ b/skills/multi/dingtalk-dev/references/webapp.md @@ -4,5 +4,5 @@ `dws dev app webapp get` 查配置,`webapp config` 配移动端/PC 首页和管理后台地址。参数查 `dws schema dev.app.webapp.`,config 至少给一个配置字段。 -- 未配置网页应用前,`get` 可能只返回 `agentId`。 +- 未配置网页应用前,`get` 返回空对象 `{}`。拿到 `{}` 就是还没配过,走 `webapp config` 首次配置。 - `h5PageType` 未显式传入时不要假设固定默认值;配置后以 `webapp get` 回读为准。 From 1cda263e0e9c6e5461b63375f7a40ae93335eb13 Mon Sep 17 00:00:00 2001 From: qinze Date: Mon, 22 Jun 2026 18:24:56 +0800 Subject: [PATCH 41/47] =?UTF-8?q?feat(install):=20install-devapp.sh=20?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=87=AA=E5=8C=85=E5=90=AB=E4=B8=8B=E4=BA=8C?= =?UTF-8?q?=E8=BF=9B=E5=88=B6=E5=AE=89=E8=A3=85(cli+skill)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 从 fork 的 dev release 下预编译二进制 + dws-skills.zip, 只需 curl+tar, 不再 clone 源码编译(去掉 go/make/git 依赖) - 装 dingtalk-dev skill(不改名), 命令名对齐 dws dev - 版本默认取 fork 最新 release(含 prerelease) Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/install-devapp.sh | 271 ++++++++++++++------------------------ 1 file changed, 99 insertions(+), 172 deletions(-) diff --git a/scripts/install-devapp.sh b/scripts/install-devapp.sh index 5db972a2..913e3550 100755 --- a/scripts/install-devapp.sh +++ b/scripts/install-devapp.sh @@ -2,203 +2,130 @@ # Copyright 2026 Alibaba Group # Licensed under the Apache License, Version 2.0 # -# One-command installer for the DevApp preview branch. +# One-command installer for the dws Dev preview — pre-built binary, no build tools. +# Downloads the dev binary + dingtalk-dev skill from the fork's GitHub Releases. +# Requires only curl + tar (no go / make / git). # # Usage: # curl -fsSL https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/feat/dws-devapp/scripts/install-devapp.sh | sh # -# Environment variables: -# DEVAPP_REPO_URL Git repository URL. Default: https://github.com/wxianfeng/dingtalk-workspace-cli.git -# DEVAPP_BRANCH Branch to install. Default: feat/dws-devapp -# DEVAPP_SOURCE_DIR Existing source checkout to install from. -# DEVAPP_KEEP_SOURCE Set to 1 to keep the temporary source checkout. -# DEVAPP_SKIP_SKILL_SETUP Set to 1 to skip automatic DevApp skill install. -# DEVAPP_SKILL_NAME Installed DevApp skill directory/name. Default: dws-devapp -# -# Pass-through variables handled by scripts/install.sh: -# DWS_INSTALL_DIR Binary install directory. Default: ~/.local/bin -# DWS_INSTALL_NAME Installed binary name. Default: dws -# DWS_SKILL_MODE mono | multi. Default here: multi -# DWS_NO_SKILLS Set to 1 to skip skills. -# DWS_SKILLS_ONLY Set to 1 to install only skills. - +# Env (all optional): +# DEVAPP_REPO fork holding dev releases (default: wxianfeng/dingtalk-workspace-cli) +# DEVAPP_VERSION pin a dev release tag (default: latest release on the fork) +# DWS_INSTALL_DIR binary dir (default: ~/.local/bin) +# DWS_NO_SKILLS set 1 to skip the dev skill set -eu -REPO_URL="${DEVAPP_REPO_URL:-https://github.com/wxianfeng/dingtalk-workspace-cli.git}" -BRANCH="${DEVAPP_BRANCH:-feat/dws-devapp}" -SOURCE_DIR="${DEVAPP_SOURCE_DIR:-}" -KEEP_SOURCE="${DEVAPP_KEEP_SOURCE:-0}" -SKIP_SKILL_SETUP="${DEVAPP_SKIP_SKILL_SETUP:-0}" -DEVAPP_SKILL_NAME="${DEVAPP_SKILL_NAME:-dws-devapp}" -TMPDIR_WORK="" +DEVAPP_REPO="${DEVAPP_REPO:-wxianfeng/dingtalk-workspace-cli}" +DEVAPP_VERSION="${DEVAPP_VERSION:-}" +INSTALL_DIR="${DWS_INSTALL_DIR:-$HOME/.local/bin}" +NO_SKILLS="${DWS_NO_SKILLS:-0}" +SKILL_NAME="dingtalk-dev" -say() { - printf ' %s\n' "$@" +say() { printf ' %s\n' "$@"; } +err() { printf ' ❌ %s\n' "$@" >&2; exit 1; } +need_cmd() { command -v "$1" >/dev/null 2>&1 || err "Missing required command: $1"; } + +need_cmd curl +need_cmd tar + +detect_os() { + case "$(uname -s)" in + Linux*) echo linux ;; + Darwin*) echo darwin ;; + *) err "Unsupported OS: $(uname -s)" ;; + esac } -err() { - printf ' ERROR: %s\n' "$@" >&2 - exit 1 +detect_arch() { + case "$(uname -m)" in + x86_64|amd64) echo amd64 ;; + arm64|aarch64) echo arm64 ;; + *) err "Unsupported architecture: $(uname -m)" ;; + esac } -need_cmd() { - command -v "$1" >/dev/null 2>&1 || err "Missing required command: $1" +# GitHub's /releases/latest excludes prereleases, so read the releases list +# (newest first) and take the top tag — the dev preview is published as a prerelease. +resolve_version() { + [ -n "$DEVAPP_VERSION" ] && return 0 + DEVAPP_VERSION="$(curl -fsSL "https://api.github.com/repos/${DEVAPP_REPO}/releases?per_page=1" 2>/dev/null \ + | grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')" + [ -n "$DEVAPP_VERSION" ] || err "No release found on ${DEVAPP_REPO}. Push a dev tag (e.g. v1.0.39-dev.1) to trigger CI, or set DEVAPP_VERSION." } -cleanup() { - if [ -n "$TMPDIR_WORK" ] && [ "$KEEP_SOURCE" != "1" ]; then - rm -rf "$TMPDIR_WORK" - fi -} - -trap cleanup EXIT INT TERM - -print_banner() { - printf '\n' - say "DevApp preview installer" - say "Repository: ${REPO_URL}" - say "Branch: ${BRANCH}" - printf '\n' -} - -resolve_source_dir() { - if [ -n "$SOURCE_DIR" ]; then - [ -d "$SOURCE_DIR" ] || err "DEVAPP_SOURCE_DIR does not exist: ${SOURCE_DIR}" - [ -f "$SOURCE_DIR/scripts/install.sh" ] || err "install.sh not found under DEVAPP_SOURCE_DIR: ${SOURCE_DIR}" - printf '%s\n' "$SOURCE_DIR" - return 0 - fi - - need_cmd git - - TMPDIR_WORK="$(mktemp -d)" - checkout="${TMPDIR_WORK}/dingtalk-workspace-cli" - - say "Cloning DevApp preview source..." >&2 - git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$checkout" - - [ -f "$checkout/scripts/install.sh" ] || err "install.sh not found after clone" - printf '%s\n' "$checkout" -} - -copy_skill() { - src="$1" - dest="$2" - - rm -rf "$dest" - mkdir -p "$dest" - cp -R "$src/"* "$dest/" 2>/dev/null || cp -r "$src/"* "$dest/" -} - -copy_devapp_skill() { - src="$1" - dest="$2" - - copy_skill "$src" "$dest" - if [ -f "$dest/SKILL.md" ]; then - tmp_skill="${dest}/SKILL.md.tmp" - sed "s/^name: .*/name: ${DEVAPP_SKILL_NAME}/" "$dest/SKILL.md" > "$tmp_skill" - mv "$tmp_skill" "$dest/SKILL.md" - fi -} - -install_skill_to_agent_homes() { - src="$1" - skill_name="$2" - kind="$3" - installed=0 - idx=0 - - for agent_dir in \ - ".agents/skills" \ - ".claude/skills" \ - ".cursor/skills" \ - ".qoder/skills" \ - ".qoderwork/skills" \ - ".gemini/skills" \ - ".codex/skills" \ - ".github/skills" \ - ".windsurf/skills" \ - ".augment/skills" \ - ".cline/skills" \ - ".amp/skills" \ - ".kiro/skills" \ - ".trae/skills" \ - ".openclaw/skills" \ - ".hermes/skills" - do - base_dir="$HOME/$agent_dir" - parent_gate="$(dirname "$base_dir")" - if [ "$idx" -gt 0 ] && [ ! -e "$parent_gate" ]; then - idx=$((idx + 1)) - continue - fi - - dest="$base_dir/$skill_name" - if [ "$kind" = "devapp" ]; then - copy_devapp_skill "$src" "$dest" - else - copy_skill "$src" "$dest" - fi - say "✅ Skill installed: ~/${agent_dir}/${skill_name}" - installed=$((installed + 1)) - idx=$((idx + 1)) +install_skill() { + bundle="$1" # extracted skills bundle dir + src="" + for c in "$bundle/multi/$SKILL_NAME" "$bundle/skills/multi/$SKILL_NAME" "$bundle/$SKILL_NAME"; do + [ -f "$c/SKILL.md" ] && src="$c" && break done + [ -n "$src" ] || { say " (dingtalk-dev not found in skills bundle; skipped)"; return 0; } - if [ "$installed" -eq 0 ]; then - dest="$HOME/.agents/skills/$skill_name" - if [ "$kind" = "devapp" ]; then - copy_devapp_skill "$src" "$dest" - else - copy_skill "$src" "$dest" - fi - say "✅ Skill installed: ~/.agents/skills/${skill_name}" - fi -} + # cache so `dws skill setup --mode multi` can find a source later + cache="$HOME/.dws/skills/multi/$SKILL_NAME" + rm -rf "$cache"; mkdir -p "$cache"; cp -R "$src/." "$cache/" -install_devapp_skills() { - if [ "${DWS_NO_SKILLS:-0}" = "1" ] || [ "$SKIP_SKILL_SETUP" = "1" ]; then - return 0 - fi - - dws_skill_src="$source_root/skills/mono" - devapp_skill_src="$source_root/skills/multi/dingtalk-dev" - [ -f "$dws_skill_src/SKILL.md" ] || err "dws skill source not found: ${dws_skill_src}" - [ -f "$devapp_skill_src/SKILL.md" ] || err "DevApp skill source not found: ${devapp_skill_src}" - - say "" - say "Installing agent skills: dws + ${DEVAPP_SKILL_NAME}" - install_skill_to_agent_homes "$dws_skill_src" "dws" "dws" - install_skill_to_agent_homes "$devapp_skill_src" "$DEVAPP_SKILL_NAME" "devapp" + installed=0; idx=0 + for agent_dir in \ + .agents/skills .claude/skills .cursor/skills .qoder/skills .qoderwork/skills \ + .gemini/skills .codex/skills .github/skills .windsurf/skills .augment/skills \ + .cline/skills .amp/skills .kiro/skills .trae/skills .openclaw/skills .hermes/skills + do + base="$HOME/$agent_dir" + if [ "$idx" -gt 0 ] && [ ! -e "$(dirname "$base")" ]; then idx=$((idx + 1)); continue; fi + dest="$base/$SKILL_NAME"; rm -rf "$dest"; mkdir -p "$dest"; cp -R "$src/." "$dest/" + installed=$((installed + 1)); idx=$((idx + 1)) + done + say "✅ Skill dingtalk-dev → ${installed} agent dir(s)" } main() { - print_banner + resolve_version + os="$(detect_os)"; arch="$(detect_arch)" + tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT INT TERM - source_root="$(resolve_source_dir)" + printf '\n' + say "dws Dev preview installer (pre-built binary)" + say "Repo: ${DEVAPP_REPO}" + say "Version: ${DEVAPP_VERSION}" + say "Target: ${os}/${arch}" + printf '\n' - if [ "${DWS_SKILLS_ONLY:-0}" = "1" ]; then - say "" - say "Skipping binary install because DWS_SKILLS_ONLY=1." - else - say "" - say "Installing dws from DevApp preview source..." - DWS_VERSION=latest DWS_NO_SKILLS=1 sh "$source_root/scripts/install.sh" + # 1) binary (already ad-hoc signed by CI; copy does not break the signature) + asset="dws-${os}-${arch}.tar.gz" + say "⬇ Downloading ${asset} ..." + curl -fsSL "https://github.com/${DEVAPP_REPO}/releases/download/${DEVAPP_VERSION}/${asset}" -o "$tmp/$asset" \ + || err "Binary download failed — does release ${DEVAPP_VERSION} have ${asset}?" + tar -xzf "$tmp/$asset" -C "$tmp" + [ -f "$tmp/dws" ] || err "dws binary not found inside ${asset}" + mkdir -p "$INSTALL_DIR" + cp "$tmp/dws" "$INSTALL_DIR/dws"; chmod +x "$INSTALL_DIR/dws" + say "✅ Binary → ${INSTALL_DIR}/dws" + + # 2) dev skill from the release's skills bundle + if [ "$NO_SKILLS" != "1" ]; then + if curl -fsSL "https://github.com/${DEVAPP_REPO}/releases/download/${DEVAPP_VERSION}/dws-skills.zip" -o "$tmp/skills.zip" 2>/dev/null; then + mkdir -p "$tmp/sk" + if command -v unzip >/dev/null 2>&1; then unzip -q "$tmp/skills.zip" -d "$tmp/sk"; else tar -xf "$tmp/skills.zip" -C "$tmp/sk"; fi + say "" + install_skill "$tmp/sk" + else + say " (no dws-skills.zip in release ${DEVAPP_VERSION}; skill skipped)" + fi fi - install_devapp_skills - - say "" - say "DevApp next steps:" + printf '\n' + say "🎉 Done. Next steps:" say " dws version" say " dws auth login" - say " dws devapp --help --format json" - say " dws devapp list --format json" - - if [ "$KEEP_SOURCE" = "1" ]; then - say "" - say "Source checkout kept at: ${source_root}" - fi + say " dws dev --help --format json" + say " dws dev app list --format json" + printf '\n' + case ":$PATH:" in + *":$INSTALL_DIR:"*) ;; + *) say "Note: ${INSTALL_DIR} is not on \$PATH — add it so 'dws' is found." ;; + esac } main From 9d38a3be54fcebb3900a6d342e507e22f4f73105 Mon Sep 17 00:00:00 2001 From: qinze Date: Mon, 22 Jun 2026 18:31:24 +0800 Subject: [PATCH 42/47] =?UTF-8?q?fix(release):=20goreleaser=20owner=20?= =?UTF-8?q?=E7=94=A8=20GITHUB=5FREPOSITORY=5FOWNER?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 写死 DingTalk-Real-AI 导致 fork CI 想往官方仓库发 release 被 403 拒绝。 改用 CI 自带的 GITHUB_REPOSITORY_OWNER, fork CI 发到 fork、官方 CI 发到官方。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .goreleaser.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index cc654873..b0fd8a76 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -64,7 +64,8 @@ changelog: release: github: - owner: DingTalk-Real-AI + # 用当前运行 CI 的仓库 owner: fork CI 发到 fork, 官方 CI 发到官方, 两边都对 + owner: "{{ .Env.GITHUB_REPOSITORY_OWNER }}" name: dingtalk-workspace-cli draft: false prerelease: auto From 3427b65da661284bdcd993fbc035f1e6b040542f Mon Sep 17 00:00:00 2001 From: qinze Date: Mon, 22 Jun 2026 18:46:40 +0800 Subject: [PATCH 43/47] =?UTF-8?q?feat(install):=20install-devapp.sh=20skil?= =?UTF-8?q?l=20=E7=9B=AE=E5=BD=95=E5=A2=9E=E5=8A=A0=20opencode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit opencode 全局 skill 目录是 ~/.config/opencode/skills(在 .config 下, 与其它 agent 的 ~/.xxx 不同),加入安装列表; gate 检查 ~/.config/opencode 存在才装。 Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/install-devapp.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/install-devapp.sh b/scripts/install-devapp.sh index 913e3550..6a398b40 100755 --- a/scripts/install-devapp.sh +++ b/scripts/install-devapp.sh @@ -70,7 +70,8 @@ install_skill() { for agent_dir in \ .agents/skills .claude/skills .cursor/skills .qoder/skills .qoderwork/skills \ .gemini/skills .codex/skills .github/skills .windsurf/skills .augment/skills \ - .cline/skills .amp/skills .kiro/skills .trae/skills .openclaw/skills .hermes/skills + .cline/skills .amp/skills .kiro/skills .trae/skills .openclaw/skills .hermes/skills \ + .config/opencode/skills do base="$HOME/$agent_dir" if [ "$idx" -gt 0 ] && [ ! -e "$(dirname "$base")" ]; then idx=$((idx + 1)); continue; fi From 58dced0c8a6a24a96d2d563451045a19cd956a6a Mon Sep 17 00:00:00 2001 From: qinze Date: Mon, 22 Jun 2026 18:54:49 +0800 Subject: [PATCH 44/47] =?UTF-8?q?feat(install):=20dev=20=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20Windows(sh=20=E5=8A=A0=20Windows=20?= =?UTF-8?q?=E5=88=86=E6=94=AF=20+=20=E6=96=B0=E5=A2=9E=20ps1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - install-devapp.sh: detect_os 加 MINGW/MSYS/CYGWIN, 按平台选 .zip/.tar.gz 和 dws/dws.exe, 覆盖 mac/linux + Git Bash/WSL 下的 Windows - 新增 install-devapp.ps1: 原生 Windows 用 irm|iex 一键装, 自包含下 dev release 的 dws.exe + dingtalk-dev skill(含 opencode 目录) Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/install-devapp.ps1 | 139 +++++++++++++++++++++++++++++++++++++ scripts/install-devapp.sh | 22 ++++-- 2 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 scripts/install-devapp.ps1 diff --git a/scripts/install-devapp.ps1 b/scripts/install-devapp.ps1 new file mode 100644 index 00000000..e2bee7a8 --- /dev/null +++ b/scripts/install-devapp.ps1 @@ -0,0 +1,139 @@ +# Copyright 2026 Alibaba Group +# Licensed under the Apache License, Version 2.0 +# +# One-command installer for the dws Dev preview on native Windows (PowerShell). +# Downloads the dev binary (dws.exe) + dingtalk-dev skill from the fork's GitHub Releases. +# +# Usage: +# irm https://raw.githubusercontent.com/wxianfeng/dingtalk-workspace-cli/feat/dws-devapp/scripts/install-devapp.ps1 | iex +# +# Env (all optional): +# DEVAPP_REPO fork holding dev releases (default: wxianfeng/dingtalk-workspace-cli) +# DEVAPP_VERSION pin a dev release tag (default: latest release on the fork) +# DWS_ARCH architecture override (amd64 or arm64) +# DWS_INSTALL_DIR binary dir (default: ~/.local/bin) +# DWS_NO_SKILLS set 1 to skip the dev skill + +$ErrorActionPreference = "Stop" + +$Repo = if ($env:DEVAPP_REPO) { $env:DEVAPP_REPO } else { "wxianfeng/dingtalk-workspace-cli" } +$Version = $env:DEVAPP_VERSION +$InstallDir = if ($env:DWS_INSTALL_DIR) { $env:DWS_INSTALL_DIR } else { Join-Path $HOME ".local\bin" } +$NoSkills = $env:DWS_NO_SKILLS -eq "1" +$SkillName = "dingtalk-dev" + +function Say($m) { Write-Host " $m" } +function Die($m) { Write-Host " X $m" -ForegroundColor Red; exit 1 } + +function Get-Arch { + if ($env:DWS_ARCH -eq "amd64" -or $env:DWS_ARCH -eq "arm64") { return $env:DWS_ARCH } + try { + switch ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) { + "X64" { return "amd64" } + "Arm64" { return "arm64" } + } + } catch {} + switch ($env:PROCESSOR_ARCHITECTURE) { + "AMD64" { return "amd64" } + "ARM64" { return "arm64" } + default { Die "Could not detect architecture. Set DWS_ARCH to amd64 or arm64." } + } +} + +# GitHub's /releases/latest excludes prereleases; read the releases list (newest +# first) and take the top tag — the dev preview is published as a prerelease. +if (-not $Version) { + try { + $rel = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases?per_page=1" ` + -Headers @{ "User-Agent" = "dws-devapp-installer" } -UseBasicParsing + $Version = $rel[0].tag_name + } catch {} + if (-not $Version) { Die "No release found on $Repo. Push a dev tag (e.g. v1.0.39-dev.1) to trigger CI, or set DEVAPP_VERSION." } +} + +$arch = Get-Arch +$tmp = Join-Path $env:TEMP ("dws-dev-" + [System.Guid]::NewGuid().ToString()) +New-Item -ItemType Directory -Path $tmp -Force | Out-Null + +Write-Host "" +Say "dws Dev preview installer (Windows, pre-built binary)" +Say "Repo: $Repo" +Say "Version: $Version" +Say "Target: windows/$arch" +Write-Host "" + +# 1) binary +$asset = "dws-windows-$arch.zip" +$zip = Join-Path $tmp $asset +Say "Downloading $asset ..." +try { + Invoke-WebRequest -Uri "https://github.com/$Repo/releases/download/$Version/$asset" ` + -OutFile $zip -UseBasicParsing +} catch { Die "Binary download failed - does release $Version have $asset?" } + +Expand-Archive -Path $zip -DestinationPath $tmp -Force +$exe = Get-ChildItem -Path $tmp -Recurse -Filter "dws.exe" | Select-Object -First 1 +if (-not $exe) { Die "dws.exe not found inside $asset" } +if (-not (Test-Path $InstallDir)) { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null } +Copy-Item -Path $exe.FullName -Destination (Join-Path $InstallDir "dws.exe") -Force +Say "Binary -> $InstallDir\dws.exe" + +# 2) dev skill from the release's skills bundle +if (-not $NoSkills) { + try { + $skzip = Join-Path $tmp "dws-skills.zip" + Invoke-WebRequest -Uri "https://github.com/$Repo/releases/download/$Version/dws-skills.zip" ` + -OutFile $skzip -UseBasicParsing + $skdir = Join-Path $tmp "sk" + Expand-Archive -Path $skzip -DestinationPath $skdir -Force + + $src = $null + foreach ($c in @("multi\$SkillName", "skills\multi\$SkillName", "$SkillName")) { + $p = Join-Path $skdir $c + if (Test-Path (Join-Path $p "SKILL.md")) { $src = $p; break } + } + if ($src) { + # cache so `dws skill setup --mode multi` can find a source later + $cache = Join-Path $HOME ".dws\skills\multi\$SkillName" + if (Test-Path $cache) { Remove-Item -Recurse -Force $cache } + New-Item -ItemType Directory -Path $cache -Force | Out-Null + Copy-Item -Path "$src\*" -Destination $cache -Recurse -Force + + $agentDirs = @( + ".agents\skills", ".claude\skills", ".cursor\skills", ".qoder\skills", ".qoderwork\skills", + ".gemini\skills", ".codex\skills", ".github\skills", ".windsurf\skills", ".augment\skills", + ".cline\skills", ".amp\skills", ".kiro\skills", ".trae\skills", ".openclaw\skills", + ".hermes\skills", ".config\opencode\skills" + ) + $installed = 0; $idx = 0 + foreach ($d in $agentDirs) { + $base = Join-Path $HOME $d + $parent = Split-Path $base -Parent + if ($idx -gt 0 -and -not (Test-Path $parent)) { $idx++; continue } + $dest = Join-Path $base $SkillName + if (Test-Path $dest) { Remove-Item -Recurse -Force $dest } + New-Item -ItemType Directory -Path $dest -Force | Out-Null + Copy-Item -Path "$src\*" -Destination $dest -Recurse -Force + $installed++; $idx++ + } + Say "Skill dingtalk-dev -> $installed agent dir(s)" + } else { + Say "(dingtalk-dev not found in skills bundle; skipped)" + } + } catch { + Say "(skill install skipped: $($_.Exception.Message))" + } +} + +Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue + +Write-Host "" +Say "Done. Next steps:" +Say " dws version" +Say " dws auth login" +Say " dws dev --help --format json" +Write-Host "" +if (($env:Path -split ';') -notcontains $InstallDir) { + Say "Note: $InstallDir is not on PATH. Add it (new terminal after):" + Say " [Environment]::SetEnvironmentVariable('Path', `"`$env:Path;$InstallDir`", 'User')" +} diff --git a/scripts/install-devapp.sh b/scripts/install-devapp.sh index 6a398b40..d63fd8cb 100755 --- a/scripts/install-devapp.sh +++ b/scripts/install-devapp.sh @@ -27,13 +27,13 @@ err() { printf ' ❌ %s\n' "$@" >&2; exit 1; } need_cmd() { command -v "$1" >/dev/null 2>&1 || err "Missing required command: $1"; } need_cmd curl -need_cmd tar detect_os() { case "$(uname -s)" in Linux*) echo linux ;; Darwin*) echo darwin ;; - *) err "Unsupported OS: $(uname -s)" ;; + MINGW*|MSYS*|CYGWIN*) echo windows ;; # Git Bash / MSYS2 / Cygwin on Windows + *) err "Unsupported OS: $(uname -s). On native Windows use install-devapp.ps1." ;; esac } @@ -94,15 +94,23 @@ main() { printf '\n' # 1) binary (already ad-hoc signed by CI; copy does not break the signature) - asset="dws-${os}-${arch}.tar.gz" + if [ "$os" = "windows" ]; then + asset="dws-windows-${arch}.zip"; binname="dws.exe" + else + asset="dws-${os}-${arch}.tar.gz"; binname="dws" + fi say "⬇ Downloading ${asset} ..." curl -fsSL "https://github.com/${DEVAPP_REPO}/releases/download/${DEVAPP_VERSION}/${asset}" -o "$tmp/$asset" \ || err "Binary download failed — does release ${DEVAPP_VERSION} have ${asset}?" - tar -xzf "$tmp/$asset" -C "$tmp" - [ -f "$tmp/dws" ] || err "dws binary not found inside ${asset}" + if [ "$os" = "windows" ]; then + need_cmd unzip; unzip -q "$tmp/$asset" -d "$tmp" + else + need_cmd tar; tar -xzf "$tmp/$asset" -C "$tmp" + fi + [ -f "$tmp/$binname" ] || err "${binname} not found inside ${asset}" mkdir -p "$INSTALL_DIR" - cp "$tmp/dws" "$INSTALL_DIR/dws"; chmod +x "$INSTALL_DIR/dws" - say "✅ Binary → ${INSTALL_DIR}/dws" + cp "$tmp/$binname" "$INSTALL_DIR/$binname"; chmod +x "$INSTALL_DIR/$binname" 2>/dev/null || true + say "✅ Binary → ${INSTALL_DIR}/${binname}" # 2) dev skill from the release's skills bundle if [ "$NO_SKILLS" != "1" ]; then From 922745f3183a92ba32fc2739032e35b13b1d233d Mon Sep 17 00:00:00 2001 From: wxianfeng Date: Mon, 22 Jun 2026 20:45:14 +0800 Subject: [PATCH 45/47] event search --- docs/devapp-yulan-command-routing.md | 2 +- internal/helpers/devapp.go | 2 ++ internal/helpers/devapp_test.go | 18 ++++++++++++++++++ skills/multi/dingtalk-dev/references/event.md | 2 +- .../multi/dingtalk-devapp/references/event.md | 4 +++- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/devapp-yulan-command-routing.md b/docs/devapp-yulan-command-routing.md index 110ede95..583f7739 100644 --- a/docs/devapp-yulan-command-routing.md +++ b/docs/devapp-yulan-command-routing.md @@ -953,7 +953,7 @@ real model is "list subscribable events + subscribe/unsubscribe events by | CLI | MCP tool | Notes | | --- | --- | --- | -| `dev app event list` | `list_dev_app_events` | Args `unifiedAppId/cursor/pageSize`; returns `pushType=STREAM` + `events[]` (`eventCode`/`eventName`/`subscribed`) + `hasMore/nextCursor/pageSize`. Read. | +| `dev app event list` | `list_dev_app_events` | Args `unifiedAppId/keyword/cursor/pageSize`; `keyword` searches `eventCode` or `eventName`; returns `pushType=STREAM` + `events[]` (`eventCode`/`eventName`/`subscribed`) + `hasMore/nextCursor/pageSize`. Read. | | `dev app event subscribe` | `subscribe_dev_app_events` | Args `unifiedAppId` + required array `eventCodes`. Returns `success/operation/eventCodes/needsPublish/versionRequiredAction`; failures include `errorCode/errorMsg/reason/retryable/action` such as `STREAM_NOT_CONNECTED`. Write (`--dry-run`/`--yes`). | | `dev app event unsubscribe` | `unsubscribe_dev_app_events` | Args `unifiedAppId` + required array `eventCodes`. Returns `success/operation/eventCodes/needsPublish/versionRequiredAction`; failures include `errorCode/errorMsg/reason/retryable/action`. Write. | diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 625eb52d..28e94892 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -257,11 +257,13 @@ func newDevAppEventListCommand(runner executor.Runner) *cobra.Command { return err } params := map[string]any{"unifiedAppId": appID} + devAppPutString(params, "keyword", devAppStringFlag(cmd, "keyword")) devAppApplyCursorParams(cmd, params) return runDevAppTool(runner, cmd, devAppEventListTool, params) }, } addDevAppUnifiedIDFlag(cmd) + cmd.Flags().String("keyword", "", "事件搜索关键词,支持按事件码或事件名称模糊匹配") registerDevAppCursorFlags(cmd) preferLegacyLeaf(cmd) annotateDevAppTool(cmd, devAppEventListTool) diff --git a/internal/helpers/devapp_test.go b/internal/helpers/devapp_test.go index 22fad5a6..ce1e3b61 100644 --- a/internal/helpers/devapp_test.go +++ b/internal/helpers/devapp_test.go @@ -546,6 +546,24 @@ func TestDevAppDeleteConfirmName(t *testing.T) { // TestDevAppEventSubscribeUsesEventCodes locks the param contract: the server's // subscribe/unsubscribe tools (plural: subscribe_dev_app_events) read eventCodes // as an ARRAY — real-device confirmed against the updated MCP schema. +func TestDevAppEventListForwardsKeyword(t *testing.T) { + runner := &captureRunner{} + root := newDevAppTestRoot(runner) + var out bytes.Buffer + root.SetOut(&out) + root.SetErr(&out) + root.SetArgs([]string{"dev", "app", "event", "list", "--unified-app-id", "u-1", "--keyword", "通讯录"}) + if err := root.Execute(); err != nil { + t.Fatalf("Execute() error = %v\noutput:\n%s", err, out.String()) + } + if runner.last.Tool != devAppEventListTool { + t.Fatalf("Tool = %q, want %q", runner.last.Tool, devAppEventListTool) + } + if got := runner.last.Params["keyword"]; got != "通讯录" { + t.Fatalf("keyword = %#v, want 通讯录", got) + } +} + func TestDevAppEventSubscribeUsesEventCodes(t *testing.T) { for _, tc := range []struct { name string diff --git a/skills/multi/dingtalk-dev/references/event.md b/skills/multi/dingtalk-dev/references/event.md index 08b14d4c..c27d03a6 100644 --- a/skills/multi/dingtalk-dev/references/event.md +++ b/skills/multi/dingtalk-dev/references/event.md @@ -9,7 +9,7 @@ - 一次可订阅多个事件码,共用同一回调。 - 事件码取值以开放平台文档为准;不确定走 `dws dev doc search`。 - 退订前先 `event list` 确认当前订阅,避免退不存在的。 -- `event list` 支持 `--cursor/--page-size`,返回 `events/hasMore/nextCursor/pageSize`;翻页继续传 `nextCursor`。 +- `event list` 支持 `--keyword/--cursor/--page-size`;`--keyword` 按事件码或事件名称搜索;返回 `events/hasMore/nextCursor/pageSize`;翻页继续传 `nextCursor`。 - 返回看 `events[].subscribed` 和 `pushType=STREAM`。 - `subscribe/unsubscribe` 的 `--event-codes` 必填,返回 `success/operation/unifiedAppId/eventCodes/needsPublish/versionRequiredAction`;失败时补 `errorCode/errorMsg/reason/retryable/action`。 - 如果服务端返回 `errorCode=STREAM_NOT_CONNECTED`、`reason=STREAM_NOT_CONNECTED`、`retryable=false`、`action=run connect`,先执行 `dev connect` 建联,再重试订阅。 diff --git a/skills/multi/dingtalk-devapp/references/event.md b/skills/multi/dingtalk-devapp/references/event.md index 107a80b9..55a78a48 100644 --- a/skills/multi/dingtalk-devapp/references/event.md +++ b/skills/multi/dingtalk-devapp/references/event.md @@ -8,9 +8,10 @@ ```bash dws dev app event list --unified-app-id --page-size 20 --format json +dws dev app event list --unified-app-id --keyword 通讯录 --page-size 20 --format json ``` -MCP tool: `list_dev_app_events`。返回 `pushType=STREAM`、`events[]`、`hasMore`、`nextCursor`、`pageSize`;`hasMore=true` 时下一页继续传 `--cursor `。`events[]` 每项含: +MCP tool: `list_dev_app_events`。可选 `--keyword` 按事件码或事件名称搜索。返回 `pushType=STREAM`、`events[]`、`hasMore`、`nextCursor`、`pageSize`;`hasMore=true` 时下一页继续传 `--cursor `。`events[]` 每项含: | 字段 | 含义 | |------|------| @@ -43,6 +44,7 @@ MCP tool: `unsubscribe_dev_app_events`(入参 `unifiedAppId` + 必填数组 `e | CLI | MCP | 必填 | 说明 | |-----|-----|------|------| | `--unified-app-id` | `unifiedAppId` | 是 | 统一应用 ID | +| `--keyword` | `keyword` | list 可选 | 事件搜索关键词,匹配事件码或事件名称 | | `--cursor` | `cursor` | list 可选 | 服务端返回的下一页游标 | | `--page-size` | `pageSize` | list 可选 | 单页数量 | | `--event-codes` | `eventCodes` | subscribe/unsubscribe 必填 | 事件码列表,逗号分隔,取自 `event list` | From 567cf163f10fbd76e07921e4f484ac1e95900f97 Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 23 Jun 2026 09:18:37 +0800 Subject: [PATCH 46/47] =?UTF-8?q?docs(dev):=20event.md=20=E6=BE=84?= =?UTF-8?q?=E6=B8=85=20connect/event=20=E5=9C=A8=20Stream=20=E4=B8=8A?= =?UTF-8?q?=E7=9A=84=E5=B7=AE=E5=BC=82=20+=20Stream=20=E5=BB=BA=E7=AB=8B?= =?UTF-8?q?=E6=8C=87=E5=90=91=E5=AE=98=E6=96=B9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 加「connect 与事件订阅的关系」: connect 收机器人消息、event 只是配置订阅, dws 当前不消费事件; 两者唯一关联是先 connect 让长连在线 subscribe 才成功 - 「Stream 长连怎么建」改为命令视角 + 指向官方 SDK 文档(事件 RegisterAllEventHandler vs 机器人 RegisterChatBotCallbackRouter 别混), 不固化易过时的 SDK 代码 Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/multi/dingtalk-dev/references/event.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/skills/multi/dingtalk-dev/references/event.md b/skills/multi/dingtalk-dev/references/event.md index 08b14d4c..a87d820e 100644 --- a/skills/multi/dingtalk-dev/references/event.md +++ b/skills/multi/dingtalk-dev/references/event.md @@ -10,6 +10,21 @@ - 事件码取值以开放平台文档为准;不确定走 `dws dev doc search`。 - 退订前先 `event list` 确认当前订阅,避免退不存在的。 - `event list` 支持 `--cursor/--page-size`,返回 `events/hasMore/nextCursor/pageSize`;翻页继续传 `nextCursor`。 -- 返回看 `events[].subscribed` 和 `pushType=STREAM`。 +- 返回看 `events[].subscribed` 和 `pushType=STREAM`(事件走 Stream 长连推送;connect 与事件订阅的关系见下方「Stream 长连」)。 - `subscribe/unsubscribe` 的 `--event-codes` 必填,返回 `success/operation/unifiedAppId/eventCodes/needsPublish/versionRequiredAction`;失败时补 `errorCode/errorMsg/reason/retryable/action`。 - 如果服务端返回 `errorCode=STREAM_NOT_CONNECTED`、`reason=STREAM_NOT_CONNECTED`、`retryable=false`、`action=run connect`,先执行 `dev connect` 建联,再重试订阅。 + +## Stream 长连:connect 与事件订阅的关系 + +钉钉一个应用就一条 Stream 长连(WebSocket),上面同时承载机器人消息、事件、卡片回调等多种 topic。connect 和 event 在这条长连上分工不同: + +- `dev connect`:用应用凭证把这条长连建起来并保活,但只注册了机器人消息处理(收 @机器人 → 转发本地 agent),**不消费事件**。 +- `event subscribe/unsubscribe`:只是**配置**操作(配应用订阅哪些事件码),自己不建长连、不收事件;服务端要求应用的 Stream 长连已在线,否则返回 `STREAM_NOT_CONNECTED`。 +- 两者唯一关联:先 `dev connect` 把长连建在线,`event subscribe` 才能成功——connect 负责「让长连在线」,subscribe 负责「配置订阅」。 +- 当前限制:connect 的长连不处理事件,dws 也没有「收/消费事件」的运行时命令,event 只到「配置订阅」为止。订阅成功后真正的事件推送 dws 暂不消费——要消费得用注册了事件 handler 的 SDK 自接(事件结构走 `dev doc search` 查)。 + +## Stream 长连怎么建的 + +`dev connect` 内部用 dingtalk-stream-sdk-go,凭 `clientId/clientSecret` 建一条 WebSocket 长连并保活——底层网关握手、ticket、加解密都由 SDK 封装,agent 跑 `dev connect` 即可,不用碰这些。 + +dws 当前只消费机器人消息、不消费事件。要自己写事件消费程序补这个 gap 时,SDK 用法以官方文档为准(走 `dev doc search`,或看 github.com/open-dingtalk/dingtalk-stream-sdk-go)——注意事件用 `RegisterAllEventHandler`、connect 用的机器人是 `RegisterChatBotCallbackRouter`,两套别混;版本/接口会变,不在这里固化。 From 0e59fedbbbdac2cc5a10e3c44db86988ade9b4bb Mon Sep 17 00:00:00 2001 From: qinze Date: Tue, 23 Jun 2026 09:36:42 +0800 Subject: [PATCH 47/47] =?UTF-8?q?ci:=20dev=20=E5=88=86=E6=94=AF=20push=20?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=87=BA=E9=A2=84=E8=A7=88=20release(?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=80=9A=E8=BF=87=E6=89=8D=E5=8F=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 auto-dev-release.yml: push feat/dws-devapp → 跑 build+test → 通过则自增 dev 版本号、打 tag、自包含发 release(不靠 tag 触发 release.yml, 绕开 GITHUB_TOKEN 不触发的限制); 不含 npm 步骤天然绿; concurrency 串行防撞号 - release.yml: npm publish 加 owner 条件, fork 跳过避免红叉 Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/auto-dev-release.yml | 103 +++++++++++++++++++++++++ .github/workflows/release.yml | 2 + 2 files changed, 105 insertions(+) create mode 100644 .github/workflows/auto-dev-release.yml diff --git a/.github/workflows/auto-dev-release.yml b/.github/workflows/auto-dev-release.yml new file mode 100644 index 00000000..9cc2426f --- /dev/null +++ b/.github/workflows/auto-dev-release.yml @@ -0,0 +1,103 @@ +name: Auto Dev Release + +# 推到 dev 分支 → 跑测试 → 通过才自动出一个 dev 预览 release。 +# install-devapp.sh / .ps1 取 fork 最新 release,所以发完安装链接即最新。 +# +# 设计要点: +# - 自包含:测试 + 算版本 + 打 tag + 发 release 全在一个 job,不依赖 tag 触发 +# release.yml(GITHUB_TOKEN 推的 tag 本来也不会触发别的 workflow)。 +# - 质量门:测试这一步失败,后面发布步骤就不会跑。 +# - 不含 npm publish,所以自动发布天然是绿的。 +on: + push: + branches: + - feat/dws-devapp + +# 串行:两次 push 不会同时算版本号撞车 +concurrency: + group: auto-dev-release + cancel-in-progress: false + +permissions: + contents: write + +jobs: + test-and-release: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install archive tooling + run: sudo apt-get update && sudo apt-get install -y zip unzip + + # ---- 质量门:测试不过就不发 ---- + - name: Build + run: make build + + - name: Test + run: go test -race -count=1 -timeout=5m ./cmd/... ./internal/... + + # ---- 算下一个 dev 版本号并打 tag ---- + - name: Compute next dev version + id: ver + run: | + set -eu + LATEST=$(git tag -l 'v*-dev.*' --sort=-v:refname | head -1 || true) + if [ -z "$LATEST" ]; then + BASE="v1.0.39"; N=0 + else + BASE=$(printf '%s' "$LATEST" | sed -E 's/-dev\.[0-9]+$//') + N=$(printf '%s' "$LATEST" | sed -E 's/.*-dev\.([0-9]+)$/\1/') + fi + NEW="${BASE}-dev.$((N + 1))" + echo "version=$NEW" >> "$GITHUB_OUTPUT" + echo "next dev release: $NEW (prev: ${LATEST:-none})" + + - name: Create and push tag + run: | + set -eu + V="${{ steps.ver.outputs.version }}" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag "$V" + git push origin "$V" + + # ---- 自己发 release(不靠 tag 触发 release.yml)---- + - name: Install rcodesign (ad-hoc sign darwin binaries from Linux) + run: | + set -eu + RCS_VERSION="0.27.0" + curl -fsSL -o /tmp/rcodesign.tar.gz \ + "https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F${RCS_VERSION}/apple-codesign-${RCS_VERSION}-x86_64-unknown-linux-musl.tar.gz" + mkdir -p /tmp/rcodesign + tar -xzf /tmp/rcodesign.tar.gz -C /tmp/rcodesign --strip-components=1 + sudo install -m 0755 /tmp/rcodesign/rcodesign /usr/local/bin/rcodesign + rcodesign --version + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Post-release packaging + run: ./scripts/release/post-goreleaser.sh + env: + DWS_PACKAGE_VERSION: ${{ steps.ver.outputs.version }} + + - name: Upload dws-skills.zip to release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload "${{ steps.ver.outputs.version }}" dist/dws-skills.zip --clobber diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3fdee978..df68a5bb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,6 +65,8 @@ jobs: registry-url: "https://registry.npmjs.org" - name: Publish to npm + # 只有官方仓库发 npm;fork(dev 预览)没有 NPM_TOKEN,跳过以免红叉 + if: ${{ github.repository_owner == 'DingTalk-Real-AI' }} working-directory: dist/npm/dingtalk-workspace-cli run: npm publish --access public env: